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

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