TLA Line data 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 <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 HIT 5418 : to_sockaddr_in(endpoint const& ep) noexcept
51 : {
52 5418 : sockaddr_in sa{};
53 5418 : sa.sin_family = AF_INET;
54 5418 : sa.sin_port = htons(ep.port());
55 5418 : auto bytes = ep.v4_address().to_bytes();
56 5418 : std::memcpy(&sa.sin_addr, bytes.data(), 4);
57 5418 : 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 52 : to_sockaddr_in6(endpoint const& ep) noexcept
67 : {
68 52 : sockaddr_in6 sa{};
69 52 : sa.sin6_family = AF_INET6;
70 52 : sa.sin6_port = htons(ep.port());
71 52 : auto bytes = ep.v6_address().to_bytes();
72 52 : std::memcpy(&sa.sin6_addr, bytes.data(), 16);
73 52 : 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 9854 : from_sockaddr_in(sockaddr_in const& sa) noexcept
83 : {
84 : ipv4_address::bytes_type bytes;
85 9854 : std::memcpy(bytes.data(), &sa.sin_addr, 4);
86 9854 : 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 84 : from_sockaddr_in6(sockaddr_in6 const& sa) noexcept
96 : {
97 : ipv6_address::bytes_type bytes;
98 84 : std::memcpy(bytes.data(), &sa.sin6_addr, 16);
99 84 : 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 2 : to_v4_mapped_sockaddr_in6(endpoint const& ep) noexcept
112 : {
113 2 : sockaddr_in6 sa{};
114 2 : sa.sin6_family = AF_INET6;
115 2 : sa.sin6_port = htons(ep.port());
116 : // ::ffff:0:0/96 prefix
117 2 : sa.sin6_addr.s6_addr[10] = 0xff;
118 2 : sa.sin6_addr.s6_addr[11] = 0xff;
119 2 : auto bytes = ep.v4_address().to_bytes();
120 2 : std::memcpy(&sa.sin6_addr.s6_addr[12], bytes.data(), 4);
121 2 : 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 5452 : to_sockaddr(endpoint const& ep, sockaddr_storage& storage) noexcept
135 : {
136 5452 : std::memset(&storage, 0, sizeof(storage));
137 5452 : if (ep.is_v4())
138 : {
139 5402 : auto sa = to_sockaddr_in(ep);
140 5402 : std::memcpy(&storage, &sa, sizeof(sa));
141 5402 : return sizeof(sa);
142 : }
143 50 : auto sa6 = to_sockaddr_in6(ep);
144 50 : std::memcpy(&storage, &sa6, sizeof(sa6));
145 50 : return sizeof(sa6);
146 : }
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 4857 : 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 4857 : if (ep.is_v4() && socket_family == AF_INET6)
166 : {
167 2 : std::memset(&storage, 0, sizeof(storage));
168 2 : auto sa6 = to_v4_mapped_sockaddr_in6(ep);
169 2 : std::memcpy(&storage, &sa6, sizeof(sa6));
170 2 : return sizeof(sa6);
171 : }
172 4855 : return to_sockaddr(ep, storage);
173 : }
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 9919 : from_sockaddr(sockaddr_storage const& storage) noexcept
185 : {
186 9919 : if (storage.ss_family == AF_INET)
187 : {
188 : sockaddr_in sa;
189 9837 : std::memcpy(&sa, &storage, sizeof(sa));
190 9837 : return from_sockaddr_in(sa);
191 : }
192 82 : if (storage.ss_family == AF_INET6)
193 : {
194 : sockaddr_in6 sa6;
195 82 : std::memcpy(&sa6, &storage, sizeof(sa6));
196 82 : return from_sockaddr_in6(sa6);
197 : }
198 MIS 0 : return endpoint{};
199 : }
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 HIT 5066 : socket_family(
219 : #if BOOST_COROSIO_POSIX
220 : int fd
221 : #else
222 : std::uintptr_t fd
223 : #endif
224 : ) noexcept
225 : {
226 5066 : sockaddr_storage storage{};
227 5066 : socklen_t len = sizeof(storage);
228 5066 : if (getsockname(
229 : #if BOOST_COROSIO_POSIX
230 : fd,
231 : #else
232 : static_cast<SOCKET>(fd),
233 : #endif
234 5066 : reinterpret_cast<sockaddr*>(&storage), &len) != 0)
235 2 : return AF_UNSPEC;
236 5064 : return storage.ss_family;
237 : }
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 284 : to_sockaddr(local_endpoint const& ep, sockaddr_storage& storage) noexcept
264 : {
265 284 : std::memset(&storage, 0, sizeof(storage));
266 284 : un_sa_t sa{};
267 284 : sa.sun_family = AF_UNIX;
268 284 : auto path = ep.path();
269 284 : auto copy_len = (std::min)(path.size(), sizeof(sa.sun_path));
270 284 : if (copy_len > 0)
271 284 : std::memcpy(sa.sun_path, path.data(), copy_len);
272 284 : std::memcpy(&storage, &sa, sizeof(sa));
273 :
274 284 : if (ep.is_abstract())
275 6 : return static_cast<socklen_t>(offsetof(un_sa_t, sun_path) + copy_len);
276 278 : return static_cast<socklen_t>(sizeof(sa));
277 : }
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 209 : to_sockaddr(
291 : local_endpoint const& ep,
292 : int /*socket_family*/,
293 : sockaddr_storage& storage) noexcept
294 : {
295 209 : 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 894 : from_sockaddr_local(sockaddr_storage const& storage, socklen_t len) noexcept
307 : {
308 894 : if (storage.ss_family != AF_UNIX)
309 1 : return local_endpoint{};
310 :
311 893 : un_sa_t sa{};
312 893 : std::memcpy(
313 893 : &sa, &storage, (std::min)(static_cast<std::size_t>(len), sizeof(sa)));
314 :
315 893 : auto path_offset = offsetof(un_sa_t, sun_path);
316 893 : if (static_cast<std::size_t>(len) <= path_offset)
317 663 : 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 460 : auto path_len = (std::min)(static_cast<std::size_t>(len) - path_offset,
322 230 : sizeof(sa.sun_path));
323 :
324 : // Non-abstract paths may be null-terminated by the kernel
325 230 : if (path_len > 0 && sa.sun_path[0] != '\0')
326 : {
327 : auto* end =
328 224 : static_cast<char const*>(std::memchr(sa.sun_path, '\0', path_len));
329 224 : if (end)
330 224 : path_len = static_cast<std::size_t>(end - sa.sun_path);
331 : }
332 :
333 : // A foreign sun_path may exceed corosio's cap; the length
334 : // pre-check keeps the throwing constructor unreachable.
335 230 : if (path_len > local_endpoint::max_path_length)
336 MIS 0 : return local_endpoint{};
337 HIT 230 : return local_endpoint(std::string_view(sa.sun_path, path_len));
338 : }
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 9919 : from_sockaddr_as(
354 : sockaddr_storage const& storage,
355 : socklen_t /*len*/,
356 : endpoint const&) noexcept
357 : {
358 9919 : 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 894 : from_sockaddr_as(
369 : sockaddr_storage const& storage,
370 : socklen_t len,
371 : local_endpoint const&) noexcept
372 : {
373 894 : return from_sockaddr_local(storage, len);
374 : }
375 :
376 : } // namespace boost::corosio::detail
377 :
378 : #endif
|