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

98.0% Lines (100 / 102) 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&) :53 5405x 100.0% 100.0% boost::corosio::detail::to_sockaddr_in6(boost::corosio::endpoint const&) :71 56x 100.0% 100.0% boost::corosio::detail::from_sockaddr_in(sockaddr_in const&) :89 9841x 100.0% 100.0% boost::corosio::detail::from_sockaddr_in6(sockaddr_in6 const&) :102 87x 100.0% 100.0% boost::corosio::detail::to_v4_mapped_sockaddr_in6(boost::corosio::endpoint const&) :120 2x 100.0% 100.0% boost::corosio::detail::to_sockaddr(boost::corosio::endpoint const&, sockaddr_storage&) :141 5441x 100.0% 100.0% boost::corosio::detail::to_sockaddr(boost::corosio::endpoint const&, int, sockaddr_storage&) :168 4837x 100.0% 100.0% boost::corosio::detail::from_sockaddr(sockaddr_storage const&) :193 9906x 87.5% 88.0% boost::corosio::detail::to_family(int) :217 1217x 100.0% 100.0% boost::corosio::detail::native_family(boost::corosio::family) :228 5552x 100.0% 100.0% boost::corosio::detail::socket_family(int) :250 6253x 100.0% 100.0% boost::corosio::detail::to_sockaddr(boost::corosio::local_endpoint const&, sockaddr_storage&) :295 274x 100.0% 100.0% boost::corosio::detail::to_sockaddr(boost::corosio::local_endpoint const&, int, sockaddr_storage&) :324 199x 100.0% 100.0% boost::corosio::detail::from_sockaddr_local(sockaddr_storage const&, unsigned int) :340 890x 94.4% 94.0% boost::corosio::detail::from_sockaddr_as(sockaddr_storage const&, unsigned int, boost::corosio::endpoint const&) :387 9905x 100.0% 100.0% boost::corosio::detail::from_sockaddr_as(sockaddr_storage const&, unsigned int, boost::corosio::local_endpoint const&) :402 890x 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
39 #ifndef AF_UNIX
40 #define AF_UNIX 1
41 #endif
42
43 namespace boost::corosio::detail {
44
45 /** Convert IPv4 endpoint to sockaddr_in.
46
47 @param ep The endpoint to convert. Must be IPv4 (is_v4() == true);
48 a wrong-family endpoint terminates (throwing conversion inside
49 a noexcept function).
50 @return A sockaddr_in structure with fields in network byte order.
51 */
52 inline sockaddr_in
53 5405x to_sockaddr_in(endpoint const& ep) noexcept
54 {
55 5405x sockaddr_in sa{};
56 5405x sa.sin_family = AF_INET;
57 5405x sa.sin_port = htons(ep.port());
58 5405x auto bytes = ep.address().to_v4().to_bytes();
59 5405x std::memcpy(&sa.sin_addr, bytes.data(), 4);
60 5405x return sa;
61 }
62
63 /** Convert IPv6 endpoint to sockaddr_in6.
64
65 @param ep The endpoint to convert. Must be IPv6 (is_v6() == true);
66 a wrong-family endpoint terminates (throwing conversion inside
67 a noexcept function).
68 @return A sockaddr_in6 structure with fields in network byte order.
69 */
70 inline sockaddr_in6
71 56x to_sockaddr_in6(endpoint const& ep) noexcept
72 {
73 56x sockaddr_in6 sa{};
74 56x sa.sin6_family = AF_INET6;
75 56x sa.sin6_port = htons(ep.port());
76 56x auto addr = ep.address().to_v6();
77 56x auto bytes = addr.to_bytes();
78 56x std::memcpy(&sa.sin6_addr, bytes.data(), 16);
79 56x sa.sin6_scope_id = addr.scope_id();
80 56x return sa;
81 }
82
83 /** Create endpoint from sockaddr_in.
84
85 @param sa The sockaddr_in structure with fields in network byte order.
86 @return An endpoint with address and port extracted from sa.
87 */
88 inline endpoint
89 9841x from_sockaddr_in(sockaddr_in const& sa) noexcept
90 {
91 ipv4_address::bytes_type bytes;
92 9841x std::memcpy(bytes.data(), &sa.sin_addr, 4);
93 9841x return endpoint(ipv4_address(bytes), ntohs(sa.sin_port));
94 }
95
96 /** Create endpoint from sockaddr_in6.
97
98 @param sa The sockaddr_in6 structure with fields in network byte order.
99 @return An endpoint with address and port extracted from sa.
100 */
101 inline endpoint
102 87x from_sockaddr_in6(sockaddr_in6 const& sa) noexcept
103 {
104 ipv6_address::bytes_type bytes;
105 87x std::memcpy(bytes.data(), &sa.sin6_addr, 16);
106 87x return endpoint(ipv6_address(bytes, sa.sin6_scope_id), ntohs(sa.sin6_port));
107 }
108
109 /** Convert an IPv4 endpoint to an IPv4-mapped IPv6 sockaddr_in6.
110
111 Produces a `sockaddr_in6` with the `::ffff:` prefix, suitable
112 for passing an IPv4 destination to a dual-stack IPv6 socket.
113
114 @param ep The endpoint to convert. Must be IPv4 (is_v4() == true);
115 a wrong-family endpoint terminates (throwing conversion inside
116 a noexcept function).
117 @return A sockaddr_in6 with the IPv4-mapped address.
118 */
119 inline sockaddr_in6
120 2x to_v4_mapped_sockaddr_in6(endpoint const& ep) noexcept
121 {
122 2x sockaddr_in6 sa{};
123 2x sa.sin6_family = AF_INET6;
124 2x sa.sin6_port = htons(ep.port());
125 // The mapping constructor supplies the ::ffff:0:0/96 prefix
126 2x auto bytes = ipv6_address(ep.address().to_v4()).to_bytes();
127 2x std::memcpy(&sa.sin6_addr, bytes.data(), 16);
128 2x return sa;
129 }
130
131 /** Convert endpoint to sockaddr_storage.
132
133 Dispatches to @ref to_sockaddr_in or @ref to_sockaddr_in6
134 based on the endpoint's address family.
135
136 @param ep The endpoint to convert.
137 @param storage Output parameter filled with the sockaddr.
138 @return The length of the filled sockaddr structure.
139 */
140 inline socklen_t
141 5441x to_sockaddr(endpoint const& ep, sockaddr_storage& storage) noexcept
142 {
143 5441x std::memset(&storage, 0, sizeof(storage));
144 5441x if (ep.is_v4())
145 {
146 5390x auto sa = to_sockaddr_in(ep);
147 5390x std::memcpy(&storage, &sa, sizeof(sa));
148 5390x return sizeof(sa);
149 }
150 51x auto sa6 = to_sockaddr_in6(ep);
151 51x std::memcpy(&storage, &sa6, sizeof(sa6));
152 51x return sizeof(sa6);
153 }
154
155 /** Convert endpoint to sockaddr_storage for a specific socket family.
156
157 When the socket is AF_INET6 and the endpoint is IPv4, the address
158 is converted to an IPv4-mapped IPv6 address (`::ffff:x.x.x.x`) so
159 dual-stack sockets can connect to IPv4 destinations.
160
161 @param ep The endpoint to convert.
162 @param socket_family The address family of the socket (AF_INET or
163 AF_INET6).
164 @param storage Output parameter filled with the sockaddr.
165 @return The length of the filled sockaddr structure.
166 */
167 inline socklen_t
168 4837x to_sockaddr(
169 endpoint const& ep, int socket_family, sockaddr_storage& storage) noexcept
170 {
171 // IPv4 endpoint on IPv6 socket: use IPv4-mapped address.
172 // The reverse mismatch (v4-mapped IPv6 endpoint on an AF_INET
173 // socket) is deliberately not bridged; the kernel rejects it.
174 4837x if (ep.is_v4() && socket_family == AF_INET6)
175 {
176 2x std::memset(&storage, 0, sizeof(storage));
177 2x auto sa6 = to_v4_mapped_sockaddr_in6(ep);
178 2x std::memcpy(&storage, &sa6, sizeof(sa6));
179 2x return sizeof(sa6);
180 }
181 4835x return to_sockaddr(ep, storage);
182 }
183
184 /** Create endpoint from sockaddr_storage.
185
186 Dispatches on `ss_family` to reconstruct the appropriate
187 IPv4 or IPv6 endpoint.
188
189 @param storage The sockaddr_storage with fields in network byte order.
190 @return An endpoint with address and port extracted from storage.
191 */
192 inline endpoint
193 9906x from_sockaddr(sockaddr_storage const& storage) noexcept
194 {
195 9906x if (storage.ss_family == AF_INET)
196 {
197 sockaddr_in sa;
198 9823x std::memcpy(&sa, &storage, sizeof(sa));
199 9823x return from_sockaddr_in(sa);
200 }
201 83x if (storage.ss_family == AF_INET6)
202 {
203 sockaddr_in6 sa6;
204 83x std::memcpy(&sa6, &storage, sizeof(sa6));
205 83x return from_sockaddr_in6(sa6);
206 }
207 ✗ return endpoint{};
208 }
209
210 /** Convert a native address family to the portable one.
211
212 @param af The native family (`AF_INET`, `AF_INET6`, or anything
213 else, which maps to v4 as the inert value).
214 @return The portable family.
215 */
216 inline corosio::family
217 1217x to_family(int af) noexcept
218 {
219 1217x return af == AF_INET6 ? corosio::family::v6 : corosio::family::v4;
220 }
221
222 /** Convert the portable address family to the native one.
223
224 @param f The portable family.
225 @return `AF_INET` or `AF_INET6`.
226 */
227 inline int
228 5552x native_family(corosio::family f) noexcept
229 {
230 5552x return f == corosio::family::v6 ? AF_INET6 : AF_INET;
231 }
232
233 /** Return the native address family for an endpoint.
234
235 @param ep The endpoint to query.
236 @return `AF_INET` for IPv4, `AF_INET6` for IPv6.
237 */
238 inline int
239 endpoint_family(endpoint const& ep) noexcept
240 {
241 return ep.is_v6() ? AF_INET6 : AF_INET;
242 }
243
244 /** Return the address family of a socket descriptor.
245
246 @param fd The socket file descriptor.
247 @return AF_INET, AF_INET6, or AF_UNSPEC on failure.
248 */
249 inline int
250 6253x socket_family(
251 #if BOOST_COROSIO_POSIX
252 int fd
253 #else
254 std::uintptr_t fd
255 #endif
256 ) noexcept
257 {
258 6253x sockaddr_storage storage{};
259 6253x socklen_t len = sizeof(storage);
260 6253x if (getsockname(
261 #if BOOST_COROSIO_POSIX
262 fd,
263 #else
264 static_cast<SOCKET>(fd),
265 #endif
266 6253x reinterpret_cast<sockaddr*>(&storage), &len) != 0)
267 2x return AF_UNSPEC;
268 6251x return storage.ss_family;
269 }
270
271 //----------------------------------------------------------
272 // local_endpoint (AF_UNIX) conversions
273 //----------------------------------------------------------
274
275 // Platform-agnostic sockaddr_un alias. POSIX uses the real
276 // sockaddr_un from <sys/un.h>; Windows uses a private struct
277 // matching the layout (same approach as Boost.Asio).
278 #if BOOST_COROSIO_POSIX
279 using un_sa_t = sockaddr_un;
280 #else
281 struct un_sa_t
282 {
283 u_short sun_family;
284 char sun_path[108];
285 };
286 #endif
287
288 /** Convert a local_endpoint to sockaddr_storage.
289
290 @param ep The local endpoint to convert.
291 @param storage Output parameter filled with the sockaddr_un.
292 @return The length of the filled sockaddr structure.
293 */
294 inline socklen_t
295 274x to_sockaddr(local_endpoint const& ep, sockaddr_storage& storage) noexcept
296 {
297 274x std::memset(&storage, 0, sizeof(storage));
298 274x un_sa_t sa{};
299 274x sa.sun_family = AF_UNIX;
300 274x auto path = ep.path();
301 548x auto copy_len = (std::min)(
302 274x path.size(),
303 274x (std::min)(local_endpoint::max_path_length, sizeof(sa.sun_path)));
304 274x if (copy_len > 0)
305 274x std::memcpy(sa.sun_path, path.data(), copy_len);
306 274x std::memcpy(&storage, &sa, sizeof(sa));
307
308 274x if (ep.is_abstract())
309 6x return static_cast<socklen_t>(offsetof(un_sa_t, sun_path) + copy_len);
310 268x return static_cast<socklen_t>(sizeof(sa));
311 }
312
313 /** Convert a local_endpoint to sockaddr_storage (family-aware overload).
314
315 The socket_family parameter is ignored for Unix sockets since
316 there is no dual-stack mapping.
317
318 @param ep The local endpoint to convert.
319 @param socket_family Ignored.
320 @param storage Output parameter filled with the sockaddr_un.
321 @return The length of the filled sockaddr structure.
322 */
323 inline socklen_t
324 199x to_sockaddr(
325 local_endpoint const& ep,
326 int /*socket_family*/,
327 sockaddr_storage& storage) noexcept
328 {
329 199x return to_sockaddr(ep, storage);
330 }
331
332 /** Create a local_endpoint from sockaddr_storage.
333
334 @param storage The sockaddr_storage (must have ss_family == AF_UNIX).
335 @param len The address length returned by the kernel.
336 @return A local_endpoint with the path extracted from the
337 sockaddr_un, or an empty endpoint if the family is not AF_UNIX.
338 */
339 inline local_endpoint
340 890x from_sockaddr_local(sockaddr_storage const& storage, socklen_t len) noexcept
341 {
342 890x if (storage.ss_family != AF_UNIX)
343 1x return local_endpoint{};
344
345 889x un_sa_t sa{};
346 889x std::memcpy(
347 889x &sa, &storage, (std::min)(static_cast<std::size_t>(len), sizeof(sa)));
348
349 889x auto path_offset = offsetof(un_sa_t, sun_path);
350 889x if (static_cast<std::size_t>(len) <= path_offset)
351 661x return local_endpoint{};
352
353 // Clamp to the buffer: a foreign len may overstate the payload,
354 // and sun_path is the struct's last member.
355 456x auto path_len = (std::min)(static_cast<std::size_t>(len) - path_offset,
356 228x sizeof(sa.sun_path));
357
358 // Non-abstract paths may be null-terminated by the kernel
359 228x if (path_len > 0 && sa.sun_path[0] != '\0')
360 {
361 auto* end =
362 222x static_cast<char const*>(std::memchr(sa.sun_path, '\0', path_len));
363 222x if (end)
364 222x path_len = static_cast<std::size_t>(end - sa.sun_path);
365 }
366
367 // A foreign sun_path may exceed corosio's cap; the length
368 // pre-check keeps the throwing constructor unreachable.
369 228x if (path_len > local_endpoint::max_path_length)
370 ✗ return local_endpoint{};
371 228x return 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 9905x from_sockaddr_as(
388 sockaddr_storage const& storage,
389 socklen_t /*len*/,
390 endpoint const&) noexcept
391 {
392 9905x 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 890x from_sockaddr_as(
403 sockaddr_storage const& storage,
404 socklen_t len,
405 local_endpoint const&) noexcept
406 {
407 890x return from_sockaddr_local(storage, len);
408 }
409
410 } // namespace boost::corosio::detail
411
412 #endif
413