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

95.2% Lines (100/105) 100.0% List of functions (14/14) 62.0% Branches (31/50)
endpoint_convert.hpp
f(x) Functions (14)
Function Calls Lines Branches Blocks
boost::corosio::detail::to_sockaddr_in(boost::corosio::endpoint const&) :50 8266x 100.0% 100.0% boost::corosio::detail::to_sockaddr_in6(boost::corosio::endpoint const&) :66 52x 100.0% 100.0% boost::corosio::detail::from_sockaddr_in(sockaddr_in const&) :82 14565x 100.0% 50.0% 75.0% boost::corosio::detail::from_sockaddr_in6(sockaddr_in6 const&) :95 91x 100.0% 50.0% 75.0% boost::corosio::detail::to_v4_mapped_sockaddr_in6(boost::corosio::endpoint const&) :111 2x 100.0% 50.0% 66.0% boost::corosio::detail::to_sockaddr(boost::corosio::endpoint const&, sockaddr_storage&) :134 8303x 100.0% 100.0% 100.0% boost::corosio::detail::to_sockaddr(boost::corosio::endpoint const&, int, sockaddr_storage&) :161 6716x 100.0% 100.0% 100.0% boost::corosio::detail::from_sockaddr(sockaddr_storage const&) :184 14630x 88.9% 75.0% 83.0% boost::corosio::detail::socket_family(int) :218 6851x 100.0% 75.0% 83.0% boost::corosio::detail::to_sockaddr(boost::corosio::local_endpoint const&, sockaddr_storage&) :263 207x 92.3% 50.0% 75.0% boost::corosio::detail::to_sockaddr(boost::corosio::local_endpoint const&, int, sockaddr_storage&) :290 135x 100.0% 100.0% boost::corosio::detail::from_sockaddr_local(sockaddr_storage const&, unsigned int) :306 775x 85.7% 54.5% 77.0% boost::corosio::detail::from_sockaddr_as(sockaddr_storage const&, unsigned int, boost::corosio::endpoint const&) :353 14630x 100.0% 100.0% boost::corosio::detail::from_sockaddr_as(sockaddr_storage const&, unsigned int, boost::corosio::local_endpoint const&) :368 775x 100.0% 100.0%
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Vinnie Falco ([email protected])
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/local_endpoint.hpp>
15 #include <boost/corosio/detail/platform.hpp>
16
17 #include <algorithm>
18 #include <cstring>
19
20 #if BOOST_COROSIO_POSIX
21 #include <sys/socket.h>
22 #include <sys/un.h>
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
25 #else
26 #ifndef WIN32_LEAN_AND_MEAN
27 #define WIN32_LEAN_AND_MEAN
28 #endif
29 #ifndef NOMINMAX
30 #define NOMINMAX
31 #endif
32 #include <WinSock2.h>
33 #include <Ws2tcpip.h>
34 #endif
35
36 #include <cstddef> // offsetof
37
38 #ifndef AF_UNIX
39 #define AF_UNIX 1
40 #endif
41
42 namespace boost::corosio::detail {
43
44 /** Convert IPv4 endpoint to sockaddr_in.
45
46 @param ep The endpoint to convert. Must be IPv4 (is_v4() == true).
47 @return A sockaddr_in structure with fields in network byte order.
48 */
49 inline sockaddr_in
50 8266x to_sockaddr_in(endpoint const& ep) noexcept
51 {
52 8266x sockaddr_in sa{};
53 8266x sa.sin_family = AF_INET;
54 8266x sa.sin_port = htons(ep.port());
55 8266x auto bytes = ep.v4_address().to_bytes();
56 8266x std::memcpy(&sa.sin_addr, bytes.data(), 4);
57 8266x return sa;
58 }
59
60 /** Convert IPv6 endpoint to sockaddr_in6.
61
62 @param ep The endpoint to convert. Must be IPv6 (is_v6() == true).
63 @return A sockaddr_in6 structure with fields in network byte order.
64 */
65 inline sockaddr_in6
66 52x to_sockaddr_in6(endpoint const& ep) noexcept
67 {
68 52x sockaddr_in6 sa{};
69 52x sa.sin6_family = AF_INET6;
70 52x sa.sin6_port = htons(ep.port());
71 52x auto bytes = ep.v6_address().to_bytes();
72 52x std::memcpy(&sa.sin6_addr, bytes.data(), 16);
73 52x return sa;
74 }
75
76 /** Create endpoint from sockaddr_in.
77
78 @param sa The sockaddr_in structure with fields in network byte order.
79 @return An endpoint with address and port extracted from sa.
80 */
81 inline endpoint
82 14565x from_sockaddr_in(sockaddr_in const& sa) noexcept
83 {
84 ipv4_address::bytes_type bytes;
85 14565x std::memcpy(bytes.data(), &sa.sin_addr, 4);
86
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 14565 times.
✓ Branch 2 taken 257 times.
✗ Branch 3 not taken.
14565x return endpoint(ipv4_address(bytes), ntohs(sa.sin_port));
87 }
88
89 /** Create endpoint from sockaddr_in6.
90
91 @param sa The sockaddr_in6 structure with fields in network byte order.
92 @return An endpoint with address and port extracted from sa.
93 */
94 inline endpoint
95 91x from_sockaddr_in6(sockaddr_in6 const& sa) noexcept
96 {
97 ipv6_address::bytes_type bytes;
98 91x std::memcpy(bytes.data(), &sa.sin6_addr, 16);
99
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 91 times.
91x return endpoint(ipv6_address(bytes), ntohs(sa.sin6_port));
100 }
101
102 /** Convert an IPv4 endpoint to an IPv4-mapped IPv6 sockaddr_in6.
103
104 Produces a `sockaddr_in6` with the `::ffff:` prefix, suitable
105 for passing an IPv4 destination to a dual-stack IPv6 socket.
106
107 @param ep The endpoint to convert. Must be IPv4 (is_v4() == true).
108 @return A sockaddr_in6 with the IPv4-mapped address.
109 */
110 inline sockaddr_in6
111 2x to_v4_mapped_sockaddr_in6(endpoint const& ep) noexcept
112 {
113 2x sockaddr_in6 sa{};
114 2x sa.sin6_family = AF_INET6;
115
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2x sa.sin6_port = htons(ep.port());
116 // ::ffff:0:0/96 prefix
117 2x sa.sin6_addr.s6_addr[10] = 0xff;
118 2x sa.sin6_addr.s6_addr[11] = 0xff;
119 2x auto bytes = ep.v4_address().to_bytes();
120 2x std::memcpy(&sa.sin6_addr.s6_addr[12], bytes.data(), 4);
121 2x return sa;
122 }
123
124 /** Convert endpoint to sockaddr_storage.
125
126 Dispatches to @ref to_sockaddr_in or @ref to_sockaddr_in6
127 based on the endpoint's address family.
128
129 @param ep The endpoint to convert.
130 @param storage Output parameter filled with the sockaddr.
131 @return The length of the filled sockaddr structure.
132 */
133 inline socklen_t
134 8303x to_sockaddr(endpoint const& ep, sockaddr_storage& storage) noexcept
135 {
136 8303x std::memset(&storage, 0, sizeof(storage));
137
2/2
✓ Branch 0 taken 8253 times.
✓ Branch 1 taken 50 times.
8303x if (ep.is_v4())
138 {
139 8253x auto sa = to_sockaddr_in(ep);
140 8253x std::memcpy(&storage, &sa, sizeof(sa));
141 8253x return sizeof(sa);
142 }
143 50x auto sa6 = to_sockaddr_in6(ep);
144 50x std::memcpy(&storage, &sa6, sizeof(sa6));
145 50x return sizeof(sa6);
146 8303x }
147
148 /** Convert endpoint to sockaddr_storage for a specific socket family.
149
150 When the socket is AF_INET6 and the endpoint is IPv4, the address
151 is converted to an IPv4-mapped IPv6 address (`::ffff:x.x.x.x`) so
152 dual-stack sockets can connect to IPv4 destinations.
153
154 @param ep The endpoint to convert.
155 @param socket_family The address family of the socket (AF_INET or
156 AF_INET6).
157 @param storage Output parameter filled with the sockaddr.
158 @return The length of the filled sockaddr structure.
159 */
160 inline socklen_t
161 6716x to_sockaddr(
162 endpoint const& ep, int socket_family, sockaddr_storage& storage) noexcept
163 {
164 // IPv4 endpoint on IPv6 socket: use IPv4-mapped address
165
4/4
✓ Branch 0 taken 6688 times.
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 6686 times.
✓ Branch 3 taken 2 times.
6716x if (ep.is_v4() && socket_family == AF_INET6)
166 {
167 2x std::memset(&storage, 0, sizeof(storage));
168 2x auto sa6 = to_v4_mapped_sockaddr_in6(ep);
169 2x std::memcpy(&storage, &sa6, sizeof(sa6));
170 2x return sizeof(sa6);
171 }
172 6714x return to_sockaddr(ep, storage);
173 6716x }
174
175 /** Create endpoint from sockaddr_storage.
176
177 Dispatches on `ss_family` to reconstruct the appropriate
178 IPv4 or IPv6 endpoint.
179
180 @param storage The sockaddr_storage with fields in network byte order.
181 @return An endpoint with address and port extracted from storage.
182 */
183 inline endpoint
184 14630x from_sockaddr(sockaddr_storage const& storage) noexcept
185 {
186
2/2
✓ Branch 0 taken 14548 times.
✓ Branch 1 taken 82 times.
14630x if (storage.ss_family == AF_INET)
187 {
188 sockaddr_in sa;
189 14548x std::memcpy(&sa, &storage, sizeof(sa));
190 14548x return from_sockaddr_in(sa);
191 }
192
1/2
✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
82x if (storage.ss_family == AF_INET6)
193 {
194 sockaddr_in6 sa6;
195 82x std::memcpy(&sa6, &storage, sizeof(sa6));
196 82x return from_sockaddr_in6(sa6);
197 }
198 return endpoint{};
199 14630x }
200
201 /** Return the native address family for an endpoint.
202
203 @param ep The endpoint to query.
204 @return `AF_INET` for IPv4, `AF_INET6` for IPv6.
205 */
206 inline int
207 endpoint_family(endpoint const& ep) noexcept
208 {
209 return ep.is_v6() ? AF_INET6 : AF_INET;
210 }
211
212 /** Return the address family of a socket descriptor.
213
214 @param fd The socket file descriptor.
215 @return AF_INET, AF_INET6, or AF_UNSPEC on failure.
216 */
217 inline int
218 6851x socket_family(
219 #if BOOST_COROSIO_POSIX
220 int fd
221 #else
222 std::uintptr_t fd
223 #endif
224 ) noexcept
225 {
226 6851x sockaddr_storage storage{};
227 6851x socklen_t len = sizeof(storage);
228
3/4
✓ Branch 0 taken 6851 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 6849 times.
6851x if (getsockname(
229 #if BOOST_COROSIO_POSIX
230 6851x fd,
231 #else
232 static_cast<SOCKET>(fd),
233 #endif
234 6851x reinterpret_cast<sockaddr*>(&storage), &len) != 0)
235 2x return AF_UNSPEC;
236 6849x return storage.ss_family;
237 6851x }
238
239 //----------------------------------------------------------
240 // local_endpoint (AF_UNIX) conversions
241 //----------------------------------------------------------
242
243 // Platform-agnostic sockaddr_un alias. POSIX uses the real
244 // sockaddr_un from <sys/un.h>; Windows uses a private struct
245 // matching the layout (same approach as Boost.Asio).
246 #if BOOST_COROSIO_POSIX
247 using un_sa_t = sockaddr_un;
248 #else
249 struct un_sa_t
250 {
251 u_short sun_family;
252 char sun_path[108];
253 };
254 #endif
255
256 /** Convert a local_endpoint to sockaddr_storage.
257
258 @param ep The local endpoint to convert.
259 @param storage Output parameter filled with the sockaddr_un.
260 @return The length of the filled sockaddr structure.
261 */
262 inline socklen_t
263 207x to_sockaddr(local_endpoint const& ep, sockaddr_storage& storage) noexcept
264 {
265 207x std::memset(&storage, 0, sizeof(storage));
266 207x un_sa_t sa{};
267 207x sa.sun_family = AF_UNIX;
268 207x auto path = ep.path();
269
1/2
✓ Branch 0 taken 207 times.
✗ Branch 1 not taken.
207x auto copy_len = (std::min)(path.size(), sizeof(sa.sun_path));
270
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 207 times.
207x if (copy_len > 0)
271 207x std::memcpy(sa.sun_path, path.data(), copy_len);
272 207x std::memcpy(&storage, &sa, sizeof(sa));
273
274
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 207 times.
207x if (ep.is_abstract())
275 return static_cast<socklen_t>(offsetof(un_sa_t, sun_path) + copy_len);
276 207x return static_cast<socklen_t>(sizeof(sa));
277 207x }
278
279 /** Convert a local_endpoint to sockaddr_storage (family-aware overload).
280
281 The socket_family parameter is ignored for Unix sockets since
282 there is no dual-stack mapping.
283
284 @param ep The local endpoint to convert.
285 @param socket_family Ignored.
286 @param storage Output parameter filled with the sockaddr_un.
287 @return The length of the filled sockaddr structure.
288 */
289 inline socklen_t
290 135x to_sockaddr(
291 local_endpoint const& ep,
292 int /*socket_family*/,
293 sockaddr_storage& storage) noexcept
294 {
295 135x return to_sockaddr(ep, storage);
296 }
297
298 /** Create a local_endpoint from sockaddr_storage.
299
300 @param storage The sockaddr_storage (must have ss_family == AF_UNIX).
301 @param len The address length returned by the kernel.
302 @return A local_endpoint with the path extracted from the
303 sockaddr_un, or an empty endpoint if the family is not AF_UNIX.
304 */
305 inline local_endpoint
306 775x from_sockaddr_local(sockaddr_storage const& storage, socklen_t len) noexcept
307 {
308
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 775 times.
775x if (storage.ss_family != AF_UNIX)
309 return local_endpoint{};
310
311 775x un_sa_t sa{};
312 775x std::memcpy(
313
1/2
✓ Branch 0 taken 775 times.
✗ Branch 1 not taken.
775x &sa, &storage, (std::min)(static_cast<std::size_t>(len), sizeof(sa)));
314
315 775x auto path_offset = offsetof(un_sa_t, sun_path);
316
1/2
✓ Branch 0 taken 775 times.
✗ Branch 1 not taken.
775x if (static_cast<std::size_t>(len) <= path_offset)
317 return local_endpoint{};
318
319 // Clamp to the buffer: a foreign len may overstate the payload,
320 // and sun_path is the struct's last member.
321
2/4
✓ Branch 0 taken 775 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 775 times.
✗ Branch 3 not taken.
1550x auto path_len = (std::min)(static_cast<std::size_t>(len) - path_offset,
322 775x sizeof(sa.sun_path));
323
324 // Non-abstract paths may be null-terminated by the kernel
325
3/4
✓ Branch 0 taken 775 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 156 times.
✓ Branch 3 taken 619 times.
775x if (path_len > 0 && sa.sun_path[0] != '\0')
326 {
327 156x auto* end =
328
1/2
✓ Branch 0 taken 156 times.
✗ Branch 1 not taken.
156x static_cast<char const*>(std::memchr(sa.sun_path, '\0', path_len));
329
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 156 times.
156x if (end)
330 156x path_len = static_cast<std::size_t>(end - sa.sun_path);
331 156x }
332
333 // A foreign sun_path may exceed corosio's cap; the length
334 // pre-check keeps the throwing constructor unreachable.
335
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 775 times.
775x if (path_len > local_endpoint::max_path_length)
336 return local_endpoint{};
337
1/2
✓ Branch 0 taken 775 times.
✗ Branch 1 not taken.
775x return local_endpoint(std::string_view(sa.sun_path, path_len));
338 775x }
339
340 //----------------------------------------------------------
341 // Tag-dispatch helpers for templatized reactor code.
342 // Overload resolution selects the correct conversion based
343 // on the Endpoint type.
344 //----------------------------------------------------------
345
346 /** Convert sockaddr_storage to an IP endpoint (tag overload).
347
348 @param storage The sockaddr_storage with fields in network byte order.
349 @param len The address length returned by the kernel.
350 @return An endpoint with address and port extracted from storage.
351 */
352 inline endpoint
353 14630x from_sockaddr_as(
354 sockaddr_storage const& storage,
355 socklen_t /*len*/,
356 endpoint const&) noexcept
357 {
358 14630x return from_sockaddr(storage);
359 }
360
361 /** Convert sockaddr_storage to a local_endpoint (tag overload).
362
363 @param storage The sockaddr_storage.
364 @param len The address length returned by the kernel.
365 @return A local_endpoint with path extracted from storage.
366 */
367 inline local_endpoint
368 775x from_sockaddr_as(
369 sockaddr_storage const& storage,
370 socklen_t len,
371 local_endpoint const&) noexcept
372 {
373 775x return from_sockaddr_local(storage, len);
374 }
375
376 } // namespace boost::corosio::detail
377
378 #endif
379