userver: userver/engine/io/sockaddr.hpp Source File
Loading...
Searching...
No Matches
sockaddr.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/engine/io/sockaddr.hpp
4/// @brief @copybrief engine::io::Sockaddr
5
6#include <netinet/in.h>
7#include <sys/socket.h>
8#include <sys/types.h>
9#include <sys/un.h>
10
11#include <cstring>
12#include <string>
13
14#include <fmt/format.h>
15#include <userver/utils/fmt_compat.hpp>
16
17#include <userver/logging/log_helper_fwd.hpp>
18
19USERVER_NAMESPACE_BEGIN
20
21namespace engine::io {
22
23/// Socket address-related exceptions
25 public:
27};
28
29/// Communication domain
30enum class AddrDomain {
31 kUnspecified = AF_UNSPEC, ///< Unspecified
32 kInet = AF_INET, ///< IPv4
33 kInet6 = AF_INET6, ///< IPv6
34 kUnix = AF_UNIX, ///< Unix socket
35};
36
37static_assert(
38 AF_UNSPEC == 0,
39 "Your socket subsystem looks broken, please contact support chat.");
40
41/// Native socket address wrapper
42class Sockaddr final {
43 public:
44 /// Constructs an unspecified native socket address.
45 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
46 Sockaddr() noexcept { ::memset(&data_, 0, sizeof(data_)); }
47
48 /// @brief Wraps a native socket address structure.
49 /// @warning sa_family must contain a correct address family.
50 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
51 explicit Sockaddr(const void* data) {
52 const auto* sockaddr = reinterpret_cast<const struct sockaddr*>(data);
53 const auto domain = static_cast<AddrDomain>(sockaddr->sa_family);
54 ::memcpy(&data_, data, Sockaddr::Addrlen(domain));
55 }
56
57 /// @brief Creates address of a Unix socket located at the specified path.
58 static Sockaddr MakeUnixSocketAddress(std::string_view path);
59
60 /// @brief Creates the IPv6 loopback address `[::1]:0` that also handles IPv4
61 /// connections.
62 ///
63 /// A program needs to support only this API type to support IPv4 and IPv6.
64 static Sockaddr MakeLoopbackAddress() noexcept;
65
66 /// @brief Creates the IPv4 only loopback address `127.0.0.1:0`.
67 ///
68 /// Prefer a more generic MakeLoopbackAddress() function if not sure.
69 static Sockaddr MakeIPv4LoopbackAddress() noexcept;
70
71 /// @brief Domain-specific native socket address structure pointer.
72 /// @warning No type checking is performed, user must ensure that only the
73 /// correct domain is accessed.
74 template <typename T>
75 T* As() {
76 static_assert(sizeof(T) <= sizeof(data_), "Invalid socket address type");
77 return reinterpret_cast<T*>(&data_);
78 }
79
80 /// @brief Domain-specific native socket address structure pointer.
81 /// @warning No type checking is performed, user must ensure that only the
82 /// correct domain is accessed.
83 template <typename T>
84 const T* As() const {
85 static_assert(sizeof(T) <= sizeof(data_), "Invalid socket address type");
86 return reinterpret_cast<const T*>(&data_);
87 }
88
89 /// Native socket address structure pointer.
90 struct sockaddr* Data() { return As<struct sockaddr>(); }
91
92 /// Native socket address structure pointer.
93 const struct sockaddr* Data() const { return As<struct sockaddr>(); }
94
95 /// Maximum supported native socket address structure size.
96 socklen_t Size() const { return Addrlen(Domain()); }
97
98 /// Maximum supported native socket address structure size.
99 socklen_t Capacity() const { return sizeof(data_); }
100
101 /// Protocol family.
102 sa_family_t Family() const { return Data()->sa_family; }
103
104 /// Communication domain.
105 AddrDomain Domain() const { return static_cast<AddrDomain>(Family()); }
106
107 /// Whether the stored socket address family expects a port.
108 bool HasPort() const;
109
110 /// Returns the stored port number if available, otherwise throws.
111 std::uint16_t Port() const;
112
113 /// Sets a port for address families that allow for one, otherwise throws.
114 void SetPort(std::uint16_t port);
115
116 /// @brief Human-readable address representation.
117 /// @note Does not include port number.
118 std::string PrimaryAddressString() const;
119
120 /// Domain-specific native socket address structure size.
121 static constexpr socklen_t Addrlen(AddrDomain domain) {
122 const auto res = AddrlenImpl(domain);
123
124 if (res == 0) {
125 throw AddrException(fmt::format("Unexpected address family {}",
126 static_cast<int>(domain)));
127 }
128
129 return res;
130 }
131
132 private:
133 static constexpr socklen_t AddrlenImpl(AddrDomain domain) noexcept {
134 switch (domain) {
136 return sizeof(struct sockaddr);
138 return sizeof(struct sockaddr_in);
140 return sizeof(struct sockaddr_in6);
142 return sizeof(struct sockaddr_un);
143 }
144
145 return 0;
146 }
147
148 union Storage {
149 struct sockaddr sa_any;
150 struct sockaddr_in sa_inet;
151 struct sockaddr_in6 sa_inet6;
152 struct sockaddr_un sa_unix;
153 } data_;
154};
155
156/// Outputs human-readable address representation, including port number.
157logging::LogHelper& operator<<(logging::LogHelper&, const Sockaddr&);
158
159} // namespace engine::io
160
161USERVER_NAMESPACE_END
162
163/// Socket address fmt formatter.
164template <>
165struct fmt::formatter<USERVER_NAMESPACE::engine::io::Sockaddr> {
166 static constexpr auto parse(format_parse_context&);
167
168 template <typename FormatContext>
169 auto format(const USERVER_NAMESPACE::engine::io::Sockaddr& sa,
170 FormatContext& ctx) USERVER_FMT_CONST;
171};
172
173inline constexpr auto fmt::formatter<
174 USERVER_NAMESPACE::engine::io::Sockaddr>::parse(format_parse_context& ctx) {
175 const auto* it = ctx.begin();
176 if (it != ctx.end() && *it != '}') {
177 throw format_error("invalid Sockaddr format");
178 }
179 return it;
180}
181
182template <typename FormatContext>
183inline auto fmt::formatter<USERVER_NAMESPACE::engine::io::Sockaddr>::format(
184 const USERVER_NAMESPACE::engine::io::Sockaddr& sa,
185 FormatContext& ctx) USERVER_FMT_CONST {
186 switch (sa.Domain()) {
187 case USERVER_NAMESPACE::engine::io::AddrDomain::kInet:
188 return fmt::format_to(ctx.out(), "{}:{}", sa.PrimaryAddressString(),
189 sa.Port());
190
191 case USERVER_NAMESPACE::engine::io::AddrDomain::kInet6:
192 return fmt::format_to(ctx.out(), "[{}]:{}", sa.PrimaryAddressString(),
193 sa.Port());
194
195 default:
196 return fmt::format_to(ctx.out(), "{}", sa.PrimaryAddressString());
197 }
198}