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 : 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 HIT 8728 : to_sockaddr_in(endpoint const& ep) noexcept
44 : {
45 8728 : sockaddr_in sa{};
46 8728 : sa.sin_family = AF_INET;
47 8728 : sa.sin_port = htons(ep.port());
48 8728 : auto bytes = ep.v4_address().to_bytes();
49 8728 : std::memcpy(&sa.sin_addr, bytes.data(), 4);
50 8728 : 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 36 : to_sockaddr_in6(endpoint const& ep) noexcept
60 : {
61 36 : sockaddr_in6 sa{};
62 36 : sa.sin6_family = AF_INET6;
63 36 : sa.sin6_port = htons(ep.port());
64 36 : auto bytes = ep.v6_address().to_bytes();
65 36 : std::memcpy(&sa.sin6_addr, bytes.data(), 16);
66 36 : 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 17225 : from_sockaddr_in(sockaddr_in const& sa) noexcept
76 : {
77 : ipv4_address::bytes_type bytes;
78 17225 : std::memcpy(bytes.data(), &sa.sin_addr, 4);
79 17225 : 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 52 : from_sockaddr_in6(sockaddr_in6 const& sa) noexcept
89 : {
90 : ipv6_address::bytes_type bytes;
91 52 : std::memcpy(bytes.data(), &sa.sin6_addr, 16);
92 52 : 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 2 : to_v4_mapped_sockaddr_in6(endpoint const& ep) noexcept
105 : {
106 2 : sockaddr_in6 sa{};
107 2 : sa.sin6_family = AF_INET6;
108 2 : sa.sin6_port = htons(ep.port());
109 : // ::ffff:0:0/96 prefix
110 2 : sa.sin6_addr.s6_addr[10] = 0xff;
111 2 : sa.sin6_addr.s6_addr[11] = 0xff;
112 2 : auto bytes = ep.v4_address().to_bytes();
113 2 : std::memcpy(&sa.sin6_addr.s6_addr[12], bytes.data(), 4);
114 2 : 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 8754 : to_sockaddr(endpoint const& ep, sockaddr_storage& storage) noexcept
128 : {
129 8754 : std::memset(&storage, 0, sizeof(storage));
130 8754 : if (ep.is_v4())
131 : {
132 8720 : auto sa = to_sockaddr_in(ep);
133 8720 : std::memcpy(&storage, &sa, sizeof(sa));
134 8720 : return sizeof(sa);
135 : }
136 34 : auto sa6 = to_sockaddr_in6(ep);
137 34 : std::memcpy(&storage, &sa6, sizeof(sa6));
138 34 : 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 8612 : 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 8612 : if (ep.is_v4() && socket_family == AF_INET6)
159 : {
160 2 : std::memset(&storage, 0, sizeof(storage));
161 2 : auto sa6 = to_v4_mapped_sockaddr_in6(ep);
162 2 : std::memcpy(&storage, &sa6, sizeof(sa6));
163 2 : return sizeof(sa6);
164 : }
165 8610 : 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 17264 : from_sockaddr(sockaddr_storage const& storage) noexcept
178 : {
179 17264 : if (storage.ss_family == AF_INET)
180 : {
181 : sockaddr_in sa;
182 17214 : std::memcpy(&sa, &storage, sizeof(sa));
183 17214 : return from_sockaddr_in(sa);
184 : }
185 50 : if (storage.ss_family == AF_INET6)
186 : {
187 : sockaddr_in6 sa6;
188 50 : std::memcpy(&sa6, &storage, sizeof(sa6));
189 50 : return from_sockaddr_in6(sa6);
190 : }
191 MIS 0 : 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 HIT 8638 : socket_family(
212 : #if BOOST_COROSIO_POSIX
213 : int fd
214 : #else
215 : std::uintptr_t fd
216 : #endif
217 : ) noexcept
218 : {
219 8638 : sockaddr_storage storage{};
220 8638 : socklen_t len = sizeof(storage);
221 8638 : if (getsockname(
222 : #if BOOST_COROSIO_POSIX
223 : fd,
224 : #else
225 : static_cast<SOCKET>(fd),
226 : #endif
227 8638 : reinterpret_cast<sockaddr*>(&storage), &len) != 0)
228 MIS 0 : return AF_UNSPEC;
229 HIT 8638 : 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 38 : to_sockaddr(local_endpoint const& ep, sockaddr_storage& storage) noexcept
246 : {
247 38 : std::memset(&storage, 0, sizeof(storage));
248 38 : sockaddr_un sa{};
249 38 : sa.sun_family = AF_UNIX;
250 38 : auto path = ep.path();
251 38 : auto copy_len = (std::min)(path.size(), sizeof(sa.sun_path));
252 38 : if (copy_len > 0)
253 38 : std::memcpy(sa.sun_path, path.data(), copy_len);
254 38 : std::memcpy(&storage, &sa, sizeof(sa));
255 :
256 38 : if (ep.is_abstract())
257 : return static_cast<socklen_t>(
258 6 : offsetof(sockaddr_un, sun_path) + path.size());
259 32 : 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 26 : to_sockaddr(
274 : local_endpoint const& ep,
275 : int /*socket_family*/,
276 : sockaddr_storage& storage) noexcept
277 : {
278 26 : 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 44 : from_sockaddr_local(
290 : sockaddr_storage const& storage, socklen_t len) noexcept
291 : {
292 44 : if (storage.ss_family != AF_UNIX)
293 MIS 0 : return local_endpoint{};
294 :
295 HIT 44 : sockaddr_un sa{};
296 44 : std::memcpy(
297 : &sa, &storage,
298 44 : (std::min)(static_cast<std::size_t>(len), sizeof(sa)));
299 :
300 44 : auto path_offset = offsetof(sockaddr_un, sun_path);
301 44 : if (static_cast<std::size_t>(len) <= path_offset)
302 8 : return local_endpoint{};
303 :
304 36 : auto path_len = static_cast<std::size_t>(len) - path_offset;
305 :
306 : // Non-abstract paths may be null-terminated by the kernel
307 36 : if (path_len > 0 && sa.sun_path[0] != '\0')
308 : {
309 : auto* end = static_cast<char const*>(
310 26 : std::memchr(sa.sun_path, '\0', path_len));
311 26 : if (end)
312 26 : path_len = static_cast<std::size_t>(end - sa.sun_path);
313 : }
314 :
315 36 : std::error_code ec;
316 36 : local_endpoint ep(std::string_view(sa.sun_path, path_len), ec);
317 36 : if (ec)
318 4 : return local_endpoint{};
319 32 : return ep;
320 : }
321 :
322 : #endif // BOOST_COROSIO_POSIX
323 :
324 : //----------------------------------------------------------
325 : // Tag-dispatch helpers for templatized reactor code.
326 : // Overload resolution selects the correct conversion based
327 : // on the Endpoint type.
328 : //----------------------------------------------------------
329 :
330 : /** Convert sockaddr_storage to an IP endpoint (tag overload).
331 :
332 : @param storage The sockaddr_storage with fields in network byte order.
333 : @param len The address length returned by the kernel.
334 : @return An endpoint with address and port extracted from storage.
335 : */
336 : inline endpoint
337 17264 : from_sockaddr_as(
338 : sockaddr_storage const& storage, socklen_t /*len*/, endpoint const&) noexcept
339 : {
340 17264 : return from_sockaddr(storage);
341 : }
342 :
343 : #if BOOST_COROSIO_POSIX
344 :
345 : /** Convert sockaddr_storage to a local_endpoint (tag overload).
346 :
347 : @param storage The sockaddr_storage.
348 : @param len The address length returned by the kernel.
349 : @return A local_endpoint with path extracted from storage.
350 : */
351 : inline local_endpoint
352 44 : from_sockaddr_as(
353 : sockaddr_storage const& storage,
354 : socklen_t len,
355 : local_endpoint const&) noexcept
356 : {
357 44 : return from_sockaddr_local(storage, len);
358 : }
359 :
360 : #endif // BOOST_COROSIO_POSIX
361 :
362 : } // namespace boost::corosio::detail
363 :
364 : #endif
|