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