include/boost/corosio/native/detail/endpoint_convert.hpp

99.0% Lines (100 / 101) 100.0% Functions (16 / 16)
endpoint_convert.hpp
f(x) Functions (16)
Function Calls Lines Blocks
boost::corosio::detail::to_sockaddr_in(boost::corosio::endpoint const&) :54 10792x 100.0% 100.0% boost::corosio::detail::to_sockaddr_in6(boost::corosio::endpoint const&) :72 56x 100.0% 100.0% boost::corosio::detail::from_sockaddr_in(sockaddr_in const&) :90 27155x 100.0% 100.0% boost::corosio::detail::from_sockaddr_in6(sockaddr_in6 const&) :103 87x 100.0% 100.0% boost::corosio::detail::to_v4_mapped_sockaddr_in6(boost::corosio::endpoint const&) :121 2x 100.0% 100.0% boost::corosio::detail::to_sockaddr(boost::corosio::endpoint const&, sockaddr_storage&) :142 10829x 100.0% 100.0% boost::corosio::detail::to_sockaddr(boost::corosio::endpoint const&, int, sockaddr_storage&) :169 8566x 100.0% 100.0% boost::corosio::detail::from_sockaddr(sockaddr_storage const&) :194 27215x 87.5% 88.0% boost::corosio::detail::to_family(int) :218 8759x 100.0% 100.0% boost::corosio::detail::native_family(boost::corosio::family) :229 10987x 100.0% 100.0% boost::corosio::detail::socket_family(int) :251 17526x 100.0% 100.0% boost::corosio::detail::to_sockaddr(boost::corosio::local_endpoint const&, sockaddr_storage&) :296 290x 100.0% 100.0% boost::corosio::detail::to_sockaddr(boost::corosio::local_endpoint const&, int, sockaddr_storage&) :325 201x 100.0% 100.0% boost::corosio::detail::from_sockaddr_local(sockaddr_storage const&, unsigned int) :341 946x 100.0% 100.0% boost::corosio::detail::from_sockaddr_as(sockaddr_storage const&, unsigned int, boost::corosio::endpoint const&) :387 27214x 100.0% 100.0% boost::corosio::detail::from_sockaddr_as(sockaddr_storage const&, unsigned int, boost::corosio::local_endpoint const&) :402 946x 100.0% 100.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Vinnie Falco (vinnie.falco@gmail.com)
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/cppalliance/corosio
8 //
9
10 #ifndef BOOST_COROSIO_NATIVE_DETAIL_ENDPOINT_CONVERT_HPP
11 #define BOOST_COROSIO_NATIVE_DETAIL_ENDPOINT_CONVERT_HPP
12
13 #include <boost/corosio/endpoint.hpp>
14 #include <boost/corosio/family.hpp>
15 #include <boost/corosio/local_endpoint.hpp>
16 #include <boost/corosio/detail/platform.hpp>
17
18 #include <algorithm>
19 #include <cstring>
20
21 #if BOOST_COROSIO_POSIX
22 #include <sys/socket.h>
23 #include <sys/un.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26 #else
27 #ifndef WIN32_LEAN_AND_MEAN
28 #define WIN32_LEAN_AND_MEAN
29 #endif
30 #ifndef NOMINMAX
31 #define NOMINMAX
32 #endif
33 #include <winsock2.h>
34 #include <ws2tcpip.h>
35 #endif
36
37 #include <cstddef> // offsetof
38 #include <tuple>
39
40 #ifndef AF_UNIX
41 #define AF_UNIX 1
42 #endif
43
44 namespace boost::corosio::detail {
45
46 /** Convert IPv4 endpoint to sockaddr_in.
47
48 @param ep The endpoint to convert. Must be IPv4 (is_v4() == true);
49 a wrong-family endpoint terminates (throwing conversion inside
50 a noexcept function).
51 @return A sockaddr_in structure with fields in network byte order.
52 */
53 inline sockaddr_in
54 10792x to_sockaddr_in(endpoint const& ep) noexcept
55 {
56 10792x sockaddr_in sa{};
57 10792x sa.sin_family = AF_INET;
58 10792x sa.sin_port = htons(ep.port());
59 10792x auto bytes = ep.address().to_v4().to_bytes();
60 10792x std::memcpy(&sa.sin_addr, bytes.data(), 4);
61 10792x return sa;
62 }
63
64 /** Convert IPv6 endpoint to sockaddr_in6.
65
66 @param ep The endpoint to convert. Must be IPv6 (is_v6() == true);
67 a wrong-family endpoint terminates (throwing conversion inside
68 a noexcept function).
69 @return A sockaddr_in6 structure with fields in network byte order.
70 */
71 inline sockaddr_in6
72 56x to_sockaddr_in6(endpoint const& ep) noexcept
73 {
74 56x sockaddr_in6 sa{};
75 56x sa.sin6_family = AF_INET6;
76 56x sa.sin6_port = htons(ep.port());
77 56x auto addr = ep.address().to_v6();
78 56x auto bytes = addr.to_bytes();
79 56x std::memcpy(&sa.sin6_addr, bytes.data(), 16);
80 56x sa.sin6_scope_id = addr.scope_id();
81 56x return sa;
82 }
83
84 /** Create endpoint from sockaddr_in.
85
86 @param sa The sockaddr_in structure with fields in network byte order.
87 @return An endpoint with address and port extracted from sa.
88 */
89 inline endpoint
90 27155x from_sockaddr_in(sockaddr_in const& sa) noexcept
91 {
92 ipv4_address::bytes_type bytes;
93 27155x std::memcpy(bytes.data(), &sa.sin_addr, 4);
94 27155x return endpoint(ipv4_address(bytes), ntohs(sa.sin_port));
95 }
96
97 /** Create endpoint from sockaddr_in6.
98
99 @param sa The sockaddr_in6 structure with fields in network byte order.
100 @return An endpoint with address and port extracted from sa.
101 */
102 inline endpoint
103 87x from_sockaddr_in6(sockaddr_in6 const& sa) noexcept
104 {
105 ipv6_address::bytes_type bytes;
106 87x std::memcpy(bytes.data(), &sa.sin6_addr, 16);
107 87x return endpoint(ipv6_address(bytes, sa.sin6_scope_id), ntohs(sa.sin6_port));
108 }
109
110 /** Convert an IPv4 endpoint to an IPv4-mapped IPv6 sockaddr_in6.
111
112 Produces a `sockaddr_in6` with the `::ffff:` prefix, suitable
113 for passing an IPv4 destination to a dual-stack IPv6 socket.
114
115 @param ep The endpoint to convert. Must be IPv4 (is_v4() == true);
116 a wrong-family endpoint terminates (throwing conversion inside
117 a noexcept function).
118 @return A sockaddr_in6 with the IPv4-mapped address.
119 */
120 inline sockaddr_in6
121 2x to_v4_mapped_sockaddr_in6(endpoint const& ep) noexcept
122 {
123 2x sockaddr_in6 sa{};
124 2x sa.sin6_family = AF_INET6;
125 2x sa.sin6_port = htons(ep.port());
126 // The mapping constructor supplies the ::ffff:0:0/96 prefix
127 2x auto bytes = ipv6_address(ep.address().to_v4()).to_bytes();
128 2x std::memcpy(&sa.sin6_addr, bytes.data(), 16);
129 2x return sa;
130 }
131
132 /** Convert endpoint to sockaddr_storage.
133
134 Dispatches to @ref to_sockaddr_in or @ref to_sockaddr_in6
135 based on the endpoint's address family.
136
137 @param ep The endpoint to convert.
138 @param storage Output parameter filled with the sockaddr.
139 @return The length of the filled sockaddr structure.
140 */
141 inline socklen_t
142 10829x to_sockaddr(endpoint const& ep, sockaddr_storage& storage) noexcept
143 {
144 10829x std::memset(&storage, 0, sizeof(storage));
145 10829x if (ep.is_v4())
146 {
147 10778x auto sa = to_sockaddr_in(ep);
148 10778x std::memcpy(&storage, &sa, sizeof(sa));
149 10778x return sizeof(sa);
150 }
151 51x auto sa6 = to_sockaddr_in6(ep);
152 51x std::memcpy(&storage, &sa6, sizeof(sa6));
153 51x return sizeof(sa6);
154 }
155
156 /** Convert endpoint to sockaddr_storage for a specific socket family.
157
158 When the socket is AF_INET6 and the endpoint is IPv4, the address
159 is converted to an IPv4-mapped IPv6 address (`::ffff:x.x.x.x`) so
160 dual-stack sockets can connect to IPv4 destinations.
161
162 @param ep The endpoint to convert.
163 @param socket_family The address family of the socket (AF_INET or
164 AF_INET6).
165 @param storage Output parameter filled with the sockaddr.
166 @return The length of the filled sockaddr structure.
167 */
168 inline socklen_t
169 8566x to_sockaddr(
170 endpoint const& ep, int socket_family, sockaddr_storage& storage) noexcept
171 {
172 // IPv4 endpoint on IPv6 socket: use IPv4-mapped address.
173 // The reverse mismatch (v4-mapped IPv6 endpoint on an AF_INET
174 // socket) is deliberately not bridged; the kernel rejects it.
175 8566x if (ep.is_v4() && socket_family == AF_INET6)
176 {
177 2x std::memset(&storage, 0, sizeof(storage));
178 2x auto sa6 = to_v4_mapped_sockaddr_in6(ep);
179 2x std::memcpy(&storage, &sa6, sizeof(sa6));
180 2x return sizeof(sa6);
181 }
182 8564x return to_sockaddr(ep, storage);
183 }
184
185 /** Create endpoint from sockaddr_storage.
186
187 Dispatches on `ss_family` to reconstruct the appropriate
188 IPv4 or IPv6 endpoint.
189
190 @param storage The sockaddr_storage with fields in network byte order.
191 @return An endpoint with address and port extracted from storage.
192 */
193 inline endpoint
194 27215x from_sockaddr(sockaddr_storage const& storage) noexcept
195 {
196 27215x if (storage.ss_family == AF_INET)
197 {
198 sockaddr_in sa;
199 27132x std::memcpy(&sa, &storage, sizeof(sa));
200 27132x return from_sockaddr_in(sa);
201 }
202 83x if (storage.ss_family == AF_INET6)
203 {
204 sockaddr_in6 sa6;
205 83x std::memcpy(&sa6, &storage, sizeof(sa6));
206 83x return from_sockaddr_in6(sa6);
207 }
208 ✗ return endpoint{};
209 }
210
211 /** Convert a native address family to the portable one.
212
213 @param af The native family (`AF_INET`, `AF_INET6`, or anything
214 else, which maps to v4 as the inert value).
215 @return The portable family.
216 */
217 inline corosio::family
218 8759x to_family(int af) noexcept
219 {
220 8759x return af == AF_INET6 ? corosio::family::v6 : corosio::family::v4;
221 }
222
223 /** Convert the portable address family to the native one.
224
225 @param f The portable family.
226 @return `AF_INET` or `AF_INET6`.
227 */
228 inline int
229 10987x native_family(corosio::family f) noexcept
230 {
231 10987x return f == corosio::family::v6 ? AF_INET6 : AF_INET;
232 }
233
234 /** Return the native address family for an endpoint.
235
236 @param ep The endpoint to query.
237 @return `AF_INET` for IPv4, `AF_INET6` for IPv6.
238 */
239 inline int
240 endpoint_family(endpoint const& ep) noexcept
241 {
242 return ep.is_v6() ? AF_INET6 : AF_INET;
243 }
244
245 /** Return the address family of a socket descriptor.
246
247 @param fd The socket file descriptor.
248 @return AF_INET, AF_INET6, or AF_UNSPEC on failure.
249 */
250 inline int
251 17526x socket_family(
252 #if BOOST_COROSIO_POSIX
253 int fd
254 #else
255 std::uintptr_t fd
256 #endif
257 ) noexcept
258 {
259 17526x sockaddr_storage storage{};
260 17526x socklen_t len = sizeof(storage);
261 17526x if (getsockname(
262 #if BOOST_COROSIO_POSIX
263 fd,
264 #else
265 static_cast<SOCKET>(fd),
266 #endif
267 17526x reinterpret_cast<sockaddr*>(&storage), &len) != 0)
268 2x return AF_UNSPEC;
269 17524x return storage.ss_family;
270 }
271
272 //----------------------------------------------------------
273 // local_endpoint (AF_UNIX) conversions
274 //----------------------------------------------------------
275
276 // Platform-agnostic sockaddr_un alias. POSIX uses the real
277 // sockaddr_un from <sys/un.h>; Windows uses a private struct
278 // matching the layout (same approach as Boost.Asio).
279 #if BOOST_COROSIO_POSIX
280 using un_sa_t = sockaddr_un;
281 #else
282 struct un_sa_t
283 {
284 u_short sun_family;
285 char sun_path[108];
286 };
287 #endif
288
289 /** Convert a local_endpoint to sockaddr_storage.
290
291 @param ep The local endpoint to convert.
292 @param storage Output parameter filled with the sockaddr_un.
293 @return The length of the filled sockaddr structure.
294 */
295 inline socklen_t
296 290x to_sockaddr(local_endpoint const& ep, sockaddr_storage& storage) noexcept
297 {
298 290x std::memset(&storage, 0, sizeof(storage));
299 290x un_sa_t sa{};
300 290x sa.sun_family = AF_UNIX;
301 290x auto path = ep.path();
302 580x auto copy_len = (std::min)(
303 290x path.size(),
304 290x (std::min)(local_endpoint::max_path_length, sizeof(sa.sun_path)));
305 290x if (copy_len > 0)
306 290x std::memcpy(sa.sun_path, path.data(), copy_len);
307 290x std::memcpy(&storage, &sa, sizeof(sa));
308
309 290x if (ep.is_abstract())
310 6x return static_cast<socklen_t>(offsetof(un_sa_t, sun_path) + copy_len);
311 284x return static_cast<socklen_t>(sizeof(sa));
312 }
313
314 /** Convert a local_endpoint to sockaddr_storage (family-aware overload).
315
316 The socket_family parameter is ignored for Unix sockets since
317 there is no dual-stack mapping.
318
319 @param ep The local endpoint to convert.
320 @param socket_family Ignored.
321 @param storage Output parameter filled with the sockaddr_un.
322 @return The length of the filled sockaddr structure.
323 */
324 inline socklen_t
325 201x to_sockaddr(
326 local_endpoint const& ep,
327 int /*socket_family*/,
328 sockaddr_storage& storage) noexcept
329 {
330 201x return to_sockaddr(ep, storage);
331 }
332
333 /** Create a local_endpoint from sockaddr_storage.
334
335 @param storage The sockaddr_storage (must have ss_family == AF_UNIX).
336 @param len The address length returned by the kernel.
337 @return A local_endpoint with the path extracted from the
338 sockaddr_un, or an empty endpoint if the family is not AF_UNIX.
339 */
340 inline local_endpoint
341 946x from_sockaddr_local(sockaddr_storage const& storage, socklen_t len) noexcept
342 {
343 946x if (storage.ss_family != AF_UNIX)
344 1x return local_endpoint{};
345
346 945x un_sa_t sa{};
347 945x std::memcpy(
348 945x &sa, &storage, (std::min)(static_cast<std::size_t>(len), sizeof(sa)));
349
350 945x auto path_offset = offsetof(un_sa_t, sun_path);
351 945x if (static_cast<std::size_t>(len) <= path_offset)
352 707x return local_endpoint{};
353
354 // Clamp to the buffer: a foreign len may overstate the payload,
355 // and sun_path is the struct's last member.
356 476x auto path_len = (std::min)(static_cast<std::size_t>(len) - path_offset,
357 238x sizeof(sa.sun_path));
358
359 // Non-abstract paths may be null-terminated by the kernel
360 238x if (path_len > 0 && sa.sun_path[0] != '\0')
361 {
362 auto* end =
363 232x static_cast<char const*>(std::memchr(sa.sun_path, '\0', path_len));
364 232x if (end)
365 232x path_len = static_cast<std::size_t>(end - sa.sun_path);
366 }
367
368 // A foreign sun_path may exceed corosio's cap; the factory then
369 // yields the empty endpoint, which is what we return.
370 238x return std::get<1>(
371 476x make_local_endpoint(std::string_view(sa.sun_path, path_len)));
372 }
373
374 //----------------------------------------------------------
375 // Tag-dispatch helpers for templatized reactor code.
376 // Overload resolution selects the correct conversion based
377 // on the Endpoint type.
378 //----------------------------------------------------------
379
380 /** Convert sockaddr_storage to an IP endpoint (tag overload).
381
382 @param storage The sockaddr_storage with fields in network byte order.
383 @param len The address length returned by the kernel.
384 @return An endpoint with address and port extracted from storage.
385 */
386 inline endpoint
387 27214x from_sockaddr_as(
388 sockaddr_storage const& storage,
389 socklen_t /*len*/,
390 endpoint const&) noexcept
391 {
392 27214x return from_sockaddr(storage);
393 }
394
395 /** Convert sockaddr_storage to a local_endpoint (tag overload).
396
397 @param storage The sockaddr_storage.
398 @param len The address length returned by the kernel.
399 @return A local_endpoint with path extracted from storage.
400 */
401 inline local_endpoint
402 946x from_sockaddr_as(
403 sockaddr_storage const& storage,
404 socklen_t len,
405 local_endpoint const&) noexcept
406 {
407 946x return from_sockaddr_local(storage, len);
408 }
409
410 } // namespace boost::corosio::detail
411
412 #endif
413