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
24class AddrException : public std::runtime_error {
25 public:
26 using std::runtime_error::runtime_error;
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() { ::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 Domain-specific native socket address structure pointer.
58 /// @warning No type checking is performed, user must ensure that only the
59 /// correct domain is accessed.
60 template <typename T>
61 T* As() {
62 static_assert(sizeof(T) <= sizeof(data_), "Invalid socket address type");
63 return reinterpret_cast<T*>(&data_);
64 }
65
66 /// @brief Domain-specific native socket address structure pointer.
67 /// @warning No type checking is performed, user must ensure that only the
68 /// correct domain is accessed.
69 template <typename T>
70 const T* As() const {
71 static_assert(sizeof(T) <= sizeof(data_), "Invalid socket address type");
72 return reinterpret_cast<const T*>(&data_);
73 }
74
75 /// Native socket address structure pointer.
76 struct sockaddr* Data() { return As<struct sockaddr>(); }
77
78 /// Native socket address structure pointer.
79 const struct sockaddr* Data() const { return As<struct sockaddr>(); }
80
81 /// Maximum supported native socket address structure size.
82 socklen_t Size() const { return Addrlen(Domain()); }
83
84 /// Maximum supported native socket address structure size.
85 socklen_t Capacity() const { return sizeof(data_); }
86
87 /// Protocol family.
88 sa_family_t Family() const { return Data()->sa_family; }
89
90 /// Communication domain.
91 AddrDomain Domain() const { return static_cast<AddrDomain>(Family()); }
92
93 /// Whether the stored socket address family expects a port.
94 bool HasPort() const;
95
96 /// Returns the stored port number if available, otherwise throws.
97 int Port() const;
98
99 /// Sets a port for address families that allow for one, otherwise throws.
100 void SetPort(int port);
101
102 /// @brief Human-readable address representation.
103 /// @note Does not include port number.
104 std::string PrimaryAddressString() const;
105
106 /// Domain-specific native socket address structure size.
107 static constexpr socklen_t Addrlen(AddrDomain domain) {
108 const auto res = AddrlenImpl(domain);
109
110 if (res == 0) {
111 throw AddrException(fmt::format("Unexpected address family {}",
112 static_cast<int>(domain)));
113 }
114
115 return res;
116 }
117
118 private:
119 static constexpr socklen_t AddrlenImpl(AddrDomain domain) noexcept {
120 switch (domain) {
122 return sizeof(struct sockaddr);
124 return sizeof(struct sockaddr_in);
126 return sizeof(struct sockaddr_in6);
128 return sizeof(struct sockaddr_un);
129 }
130
131 return 0;
132 }
133
134 union Storage {
135 struct sockaddr sa_any;
136 struct sockaddr_in sa_inet;
137 struct sockaddr_in6 sa_inet6;
138 struct sockaddr_un sa_unix;
139 } data_;
140};
141
142/// Outputs human-readable address representation, including port number.
143logging::LogHelper& operator<<(logging::LogHelper&, const Sockaddr&);
144
145} // namespace engine::io
146
147USERVER_NAMESPACE_END
148
149/// Socket address fmt formatter.
150template <>
151struct fmt::formatter<USERVER_NAMESPACE::engine::io::Sockaddr> {
152 static constexpr auto parse(format_parse_context&);
153
154 template <typename FormatContext>
155 auto format(const USERVER_NAMESPACE::engine::io::Sockaddr& sa,
156 FormatContext& ctx) USERVER_FMT_CONST;
157};
158
159inline constexpr auto fmt::formatter<
160 USERVER_NAMESPACE::engine::io::Sockaddr>::parse(format_parse_context& ctx) {
161 const auto* it = ctx.begin();
162 if (it != ctx.end() && *it != '}') {
163 throw format_error("invalid Sockaddr format");
164 }
165 return it;
166}
167
168template <typename FormatContext>
169inline auto fmt::formatter<USERVER_NAMESPACE::engine::io::Sockaddr>::format(
170 const USERVER_NAMESPACE::engine::io::Sockaddr& sa,
171 FormatContext& ctx) USERVER_FMT_CONST {
172 switch (sa.Domain()) {
173 case USERVER_NAMESPACE::engine::io::AddrDomain::kInet:
174 return fmt::format_to(ctx.out(), "{}:{}", sa.PrimaryAddressString(),
175 sa.Port());
176
177 case USERVER_NAMESPACE::engine::io::AddrDomain::kInet6:
178 return fmt::format_to(ctx.out(), "[{}]:{}", sa.PrimaryAddressString(),
179 sa.Port());
180
181 default:
182 return fmt::format_to(ctx.out(), "{}", sa.PrimaryAddressString());
183 }
184}