userver: userver/engine/io/sockaddr.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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}