98.00% Lines (98/100) 100.00% Functions (16/16)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2026 Vinnie Falco (vinnie.falco@gmail.com)
3   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 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) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/cppalliance/corosio 7   // Official repository: https://github.com/cppalliance/corosio
8   // 8   //
9   9  
10   #ifndef BOOST_COROSIO_NATIVE_DETAIL_ENDPOINT_CONVERT_HPP 10   #ifndef BOOST_COROSIO_NATIVE_DETAIL_ENDPOINT_CONVERT_HPP
11   #define BOOST_COROSIO_NATIVE_DETAIL_ENDPOINT_CONVERT_HPP 11   #define BOOST_COROSIO_NATIVE_DETAIL_ENDPOINT_CONVERT_HPP
12   12  
13   #include <boost/corosio/endpoint.hpp> 13   #include <boost/corosio/endpoint.hpp>
14   #include <boost/corosio/family.hpp> 14   #include <boost/corosio/family.hpp>
15   #include <boost/corosio/local_endpoint.hpp> 15   #include <boost/corosio/local_endpoint.hpp>
16   #include <boost/corosio/detail/platform.hpp> 16   #include <boost/corosio/detail/platform.hpp>
17   17  
18   #include <algorithm> 18   #include <algorithm>
19   #include <cstring> 19   #include <cstring>
20   20  
21   #if BOOST_COROSIO_POSIX 21   #if BOOST_COROSIO_POSIX
22   #include <sys/socket.h> 22   #include <sys/socket.h>
23   #include <sys/un.h> 23   #include <sys/un.h>
24   #include <netinet/in.h> 24   #include <netinet/in.h>
25   #include <arpa/inet.h> 25   #include <arpa/inet.h>
26   #else 26   #else
27   #ifndef WIN32_LEAN_AND_MEAN 27   #ifndef WIN32_LEAN_AND_MEAN
28   #define WIN32_LEAN_AND_MEAN 28   #define WIN32_LEAN_AND_MEAN
29   #endif 29   #endif
30   #ifndef NOMINMAX 30   #ifndef NOMINMAX
31   #define NOMINMAX 31   #define NOMINMAX
32   #endif 32   #endif
33   #include <winsock2.h> 33   #include <winsock2.h>
34   #include <ws2tcpip.h> 34   #include <ws2tcpip.h>
35   #endif 35   #endif
36   36  
37   #include <cstddef> // offsetof 37   #include <cstddef> // offsetof
38   38  
39   #ifndef AF_UNIX 39   #ifndef AF_UNIX
40   #define AF_UNIX 1 40   #define AF_UNIX 1
41   #endif 41   #endif
42   42  
43   namespace boost::corosio::detail { 43   namespace boost::corosio::detail {
44   44  
45   /** Convert IPv4 endpoint to sockaddr_in. 45   /** Convert IPv4 endpoint to sockaddr_in.
46   46  
47   @param ep The endpoint to convert. Must be IPv4 (is_v4() == true); 47   @param ep The endpoint to convert. Must be IPv4 (is_v4() == true);
48   a wrong-family endpoint terminates (throwing conversion inside 48   a wrong-family endpoint terminates (throwing conversion inside
49   a noexcept function). 49   a noexcept function).
50   @return A sockaddr_in structure with fields in network byte order. 50   @return A sockaddr_in structure with fields in network byte order.
51   */ 51   */
52   inline sockaddr_in 52   inline sockaddr_in
HITCBC 53   5386 to_sockaddr_in(endpoint const& ep) noexcept 53   5400 to_sockaddr_in(endpoint const& ep) noexcept
54   { 54   {
HITCBC 55   5386 sockaddr_in sa{}; 55   5400 sockaddr_in sa{};
HITCBC 56   5386 sa.sin_family = AF_INET; 56   5400 sa.sin_family = AF_INET;
HITCBC 57   5386 sa.sin_port = htons(ep.port()); 57   5400 sa.sin_port = htons(ep.port());
HITCBC 58   5386 auto bytes = ep.address().to_v4().to_bytes(); 58   5400 auto bytes = ep.address().to_v4().to_bytes();
HITCBC 59   5386 std::memcpy(&sa.sin_addr, bytes.data(), 4); 59   5400 std::memcpy(&sa.sin_addr, bytes.data(), 4);
HITCBC 60   5386 return sa; 60   5400 return sa;
61   } 61   }
62   62  
63   /** Convert IPv6 endpoint to sockaddr_in6. 63   /** Convert IPv6 endpoint to sockaddr_in6.
64   64  
65   @param ep The endpoint to convert. Must be IPv6 (is_v6() == true); 65   @param ep The endpoint to convert. Must be IPv6 (is_v6() == true);
66   a wrong-family endpoint terminates (throwing conversion inside 66   a wrong-family endpoint terminates (throwing conversion inside
67   a noexcept function). 67   a noexcept function).
68   @return A sockaddr_in6 structure with fields in network byte order. 68   @return A sockaddr_in6 structure with fields in network byte order.
69   */ 69   */
70   inline sockaddr_in6 70   inline sockaddr_in6
HITCBC 71   56 to_sockaddr_in6(endpoint const& ep) noexcept 71   56 to_sockaddr_in6(endpoint const& ep) noexcept
72   { 72   {
HITCBC 73   56 sockaddr_in6 sa{}; 73   56 sockaddr_in6 sa{};
HITCBC 74   56 sa.sin6_family = AF_INET6; 74   56 sa.sin6_family = AF_INET6;
HITCBC 75   56 sa.sin6_port = htons(ep.port()); 75   56 sa.sin6_port = htons(ep.port());
HITCBC 76   56 auto addr = ep.address().to_v6(); 76   56 auto addr = ep.address().to_v6();
HITCBC 77   56 auto bytes = addr.to_bytes(); 77   56 auto bytes = addr.to_bytes();
HITCBC 78   56 std::memcpy(&sa.sin6_addr, bytes.data(), 16); 78   56 std::memcpy(&sa.sin6_addr, bytes.data(), 16);
HITCBC 79   56 sa.sin6_scope_id = addr.scope_id(); 79   56 sa.sin6_scope_id = addr.scope_id();
HITCBC 80   56 return sa; 80   56 return sa;
81   } 81   }
82   82  
83   /** Create endpoint from sockaddr_in. 83   /** Create endpoint from sockaddr_in.
84   84  
85   @param sa The sockaddr_in structure with fields in network byte order. 85   @param sa The sockaddr_in structure with fields in network byte order.
86   @return An endpoint with address and port extracted from sa. 86   @return An endpoint with address and port extracted from sa.
87   */ 87   */
88   inline endpoint 88   inline endpoint
HITCBC 89   9795 from_sockaddr_in(sockaddr_in const& sa) noexcept 89   9825 from_sockaddr_in(sockaddr_in const& sa) noexcept
90   { 90   {
91   ipv4_address::bytes_type bytes; 91   ipv4_address::bytes_type bytes;
HITCBC 92   9795 std::memcpy(bytes.data(), &sa.sin_addr, 4); 92   9825 std::memcpy(bytes.data(), &sa.sin_addr, 4);
HITCBC 93   9795 return endpoint(ipv4_address(bytes), ntohs(sa.sin_port)); 93   9825 return endpoint(ipv4_address(bytes), ntohs(sa.sin_port));
94   } 94   }
95   95  
96   /** Create endpoint from sockaddr_in6. 96   /** Create endpoint from sockaddr_in6.
97   97  
98   @param sa The sockaddr_in6 structure with fields in network byte order. 98   @param sa The sockaddr_in6 structure with fields in network byte order.
99   @return An endpoint with address and port extracted from sa. 99   @return An endpoint with address and port extracted from sa.
100   */ 100   */
101   inline endpoint 101   inline endpoint
HITCBC 102   87 from_sockaddr_in6(sockaddr_in6 const& sa) noexcept 102   87 from_sockaddr_in6(sockaddr_in6 const& sa) noexcept
103   { 103   {
104   ipv6_address::bytes_type bytes; 104   ipv6_address::bytes_type bytes;
HITCBC 105   87 std::memcpy(bytes.data(), &sa.sin6_addr, 16); 105   87 std::memcpy(bytes.data(), &sa.sin6_addr, 16);
HITCBC 106   87 return endpoint(ipv6_address(bytes, sa.sin6_scope_id), ntohs(sa.sin6_port)); 106   87 return endpoint(ipv6_address(bytes, sa.sin6_scope_id), ntohs(sa.sin6_port));
107   } 107   }
108   108  
109   /** Convert an IPv4 endpoint to an IPv4-mapped IPv6 sockaddr_in6. 109   /** Convert an IPv4 endpoint to an IPv4-mapped IPv6 sockaddr_in6.
110   110  
111   Produces a `sockaddr_in6` with the `::ffff:` prefix, suitable 111   Produces a `sockaddr_in6` with the `::ffff:` prefix, suitable
112   for passing an IPv4 destination to a dual-stack IPv6 socket. 112   for passing an IPv4 destination to a dual-stack IPv6 socket.
113   113  
114   @param ep The endpoint to convert. Must be IPv4 (is_v4() == true); 114   @param ep The endpoint to convert. Must be IPv4 (is_v4() == true);
115   a wrong-family endpoint terminates (throwing conversion inside 115   a wrong-family endpoint terminates (throwing conversion inside
116   a noexcept function). 116   a noexcept function).
117   @return A sockaddr_in6 with the IPv4-mapped address. 117   @return A sockaddr_in6 with the IPv4-mapped address.
118   */ 118   */
119   inline sockaddr_in6 119   inline sockaddr_in6
HITCBC 120   2 to_v4_mapped_sockaddr_in6(endpoint const& ep) noexcept 120   2 to_v4_mapped_sockaddr_in6(endpoint const& ep) noexcept
121   { 121   {
HITCBC 122   2 sockaddr_in6 sa{}; 122   2 sockaddr_in6 sa{};
HITCBC 123   2 sa.sin6_family = AF_INET6; 123   2 sa.sin6_family = AF_INET6;
HITCBC 124   2 sa.sin6_port = htons(ep.port()); 124   2 sa.sin6_port = htons(ep.port());
125   // The mapping constructor supplies the ::ffff:0:0/96 prefix 125   // The mapping constructor supplies the ::ffff:0:0/96 prefix
HITCBC 126   2 auto bytes = ipv6_address(ep.address().to_v4()).to_bytes(); 126   2 auto bytes = ipv6_address(ep.address().to_v4()).to_bytes();
HITCBC 127   2 std::memcpy(&sa.sin6_addr, bytes.data(), 16); 127   2 std::memcpy(&sa.sin6_addr, bytes.data(), 16);
HITCBC 128   2 return sa; 128   2 return sa;
129   } 129   }
130   130  
131   /** Convert endpoint to sockaddr_storage. 131   /** Convert endpoint to sockaddr_storage.
132   132  
133   Dispatches to @ref to_sockaddr_in or @ref to_sockaddr_in6 133   Dispatches to @ref to_sockaddr_in or @ref to_sockaddr_in6
134   based on the endpoint's address family. 134   based on the endpoint's address family.
135   135  
136   @param ep The endpoint to convert. 136   @param ep The endpoint to convert.
137   @param storage Output parameter filled with the sockaddr. 137   @param storage Output parameter filled with the sockaddr.
138   @return The length of the filled sockaddr structure. 138   @return The length of the filled sockaddr structure.
139   */ 139   */
140   inline socklen_t 140   inline socklen_t
HITCBC 141   5422 to_sockaddr(endpoint const& ep, sockaddr_storage& storage) noexcept 141   5436 to_sockaddr(endpoint const& ep, sockaddr_storage& storage) noexcept
142   { 142   {
HITCBC 143   5422 std::memset(&storage, 0, sizeof(storage)); 143   5436 std::memset(&storage, 0, sizeof(storage));
HITCBC 144   5422 if (ep.is_v4()) 144   5436 if (ep.is_v4())
145   { 145   {
HITCBC 146   5371 auto sa = to_sockaddr_in(ep); 146   5385 auto sa = to_sockaddr_in(ep);
HITCBC 147   5371 std::memcpy(&storage, &sa, sizeof(sa)); 147   5385 std::memcpy(&storage, &sa, sizeof(sa));
HITCBC 148   5371 return sizeof(sa); 148   5385 return sizeof(sa);
149   } 149   }
HITCBC 150   51 auto sa6 = to_sockaddr_in6(ep); 150   51 auto sa6 = to_sockaddr_in6(ep);
HITCBC 151   51 std::memcpy(&storage, &sa6, sizeof(sa6)); 151   51 std::memcpy(&storage, &sa6, sizeof(sa6));
HITCBC 152   51 return sizeof(sa6); 152   51 return sizeof(sa6);
153   } 153   }
154   154  
155   /** Convert endpoint to sockaddr_storage for a specific socket family. 155   /** Convert endpoint to sockaddr_storage for a specific socket family.
156   156  
157   When the socket is AF_INET6 and the endpoint is IPv4, the address 157   When the socket is AF_INET6 and the endpoint is IPv4, the address
158   is converted to an IPv4-mapped IPv6 address (`::ffff:x.x.x.x`) so 158   is converted to an IPv4-mapped IPv6 address (`::ffff:x.x.x.x`) so
159   dual-stack sockets can connect to IPv4 destinations. 159   dual-stack sockets can connect to IPv4 destinations.
160   160  
161   @param ep The endpoint to convert. 161   @param ep The endpoint to convert.
162   @param socket_family The address family of the socket (AF_INET or 162   @param socket_family The address family of the socket (AF_INET or
163   AF_INET6). 163   AF_INET6).
164   @param storage Output parameter filled with the sockaddr. 164   @param storage Output parameter filled with the sockaddr.
165   @return The length of the filled sockaddr structure. 165   @return The length of the filled sockaddr structure.
166   */ 166   */
167   inline socklen_t 167   inline socklen_t
HITCBC 168   4814 to_sockaddr( 168   4828 to_sockaddr(
169   endpoint const& ep, int socket_family, sockaddr_storage& storage) noexcept 169   endpoint const& ep, int socket_family, sockaddr_storage& storage) noexcept
170   { 170   {
171   // IPv4 endpoint on IPv6 socket: use IPv4-mapped address. 171   // IPv4 endpoint on IPv6 socket: use IPv4-mapped address.
172   // The reverse mismatch (v4-mapped IPv6 endpoint on an AF_INET 172   // The reverse mismatch (v4-mapped IPv6 endpoint on an AF_INET
173   // socket) is deliberately not bridged; the kernel rejects it. 173   // socket) is deliberately not bridged; the kernel rejects it.
HITCBC 174   4814 if (ep.is_v4() && socket_family == AF_INET6) 174   4828 if (ep.is_v4() && socket_family == AF_INET6)
175   { 175   {
HITCBC 176   2 std::memset(&storage, 0, sizeof(storage)); 176   2 std::memset(&storage, 0, sizeof(storage));
HITCBC 177   2 auto sa6 = to_v4_mapped_sockaddr_in6(ep); 177   2 auto sa6 = to_v4_mapped_sockaddr_in6(ep);
HITCBC 178   2 std::memcpy(&storage, &sa6, sizeof(sa6)); 178   2 std::memcpy(&storage, &sa6, sizeof(sa6));
HITCBC 179   2 return sizeof(sa6); 179   2 return sizeof(sa6);
180   } 180   }
HITCBC 181   4812 return to_sockaddr(ep, storage); 181   4826 return to_sockaddr(ep, storage);
182   } 182   }
183   183  
184   /** Create endpoint from sockaddr_storage. 184   /** Create endpoint from sockaddr_storage.
185   185  
186   Dispatches on `ss_family` to reconstruct the appropriate 186   Dispatches on `ss_family` to reconstruct the appropriate
187   IPv4 or IPv6 endpoint. 187   IPv4 or IPv6 endpoint.
188   188  
189   @param storage The sockaddr_storage with fields in network byte order. 189   @param storage The sockaddr_storage with fields in network byte order.
190   @return An endpoint with address and port extracted from storage. 190   @return An endpoint with address and port extracted from storage.
191   */ 191   */
192   inline endpoint 192   inline endpoint
HITCBC 193   9860 from_sockaddr(sockaddr_storage const& storage) noexcept 193   9890 from_sockaddr(sockaddr_storage const& storage) noexcept
194   { 194   {
HITCBC 195   9860 if (storage.ss_family == AF_INET) 195   9890 if (storage.ss_family == AF_INET)
196   { 196   {
197   sockaddr_in sa; 197   sockaddr_in sa;
HITCBC 198   9777 std::memcpy(&sa, &storage, sizeof(sa)); 198   9807 std::memcpy(&sa, &storage, sizeof(sa));
HITCBC 199   9777 return from_sockaddr_in(sa); 199   9807 return from_sockaddr_in(sa);
200   } 200   }
HITCBC 201   83 if (storage.ss_family == AF_INET6) 201   83 if (storage.ss_family == AF_INET6)
202   { 202   {
203   sockaddr_in6 sa6; 203   sockaddr_in6 sa6;
HITCBC 204   83 std::memcpy(&sa6, &storage, sizeof(sa6)); 204   83 std::memcpy(&sa6, &storage, sizeof(sa6));
HITCBC 205   83 return from_sockaddr_in6(sa6); 205   83 return from_sockaddr_in6(sa6);
206   } 206   }
MISUBC 207   ✗ return endpoint{}; 207   ✗ return endpoint{};
208   } 208   }
209   209  
210   /** Convert a native address family to the portable one. 210   /** Convert a native address family to the portable one.
211   211  
212   @param af The native family (`AF_INET`, `AF_INET6`, or anything 212   @param af The native family (`AF_INET`, `AF_INET6`, or anything
213   else, which maps to v4 as the inert value). 213   else, which maps to v4 as the inert value).
214   @return The portable family. 214   @return The portable family.
215   */ 215   */
216   inline corosio::family 216   inline corosio::family
HITCBC 217   1221 to_family(int af) noexcept 217   1221 to_family(int af) noexcept
218   { 218   {
HITCBC 219   1221 return af == AF_INET6 ? corosio::family::v6 : corosio::family::v4; 219   1221 return af == AF_INET6 ? corosio::family::v6 : corosio::family::v4;
220   } 220   }
221   221  
222   /** Convert the portable address family to the native one. 222   /** Convert the portable address family to the native one.
223   223  
224   @param f The portable family. 224   @param f The portable family.
225   @return `AF_INET` or `AF_INET6`. 225   @return `AF_INET` or `AF_INET6`.
226   */ 226   */
227   inline int 227   inline int
HITCBC 228   5537 native_family(corosio::family f) noexcept 228   5551 native_family(corosio::family f) noexcept
229   { 229   {
HITCBC 230   5537 return f == corosio::family::v6 ? AF_INET6 : AF_INET; 230   5551 return f == corosio::family::v6 ? AF_INET6 : AF_INET;
231   } 231   }
232   232  
233   /** Return the native address family for an endpoint. 233   /** Return the native address family for an endpoint.
234   234  
235   @param ep The endpoint to query. 235   @param ep The endpoint to query.
236   @return `AF_INET` for IPv4, `AF_INET6` for IPv6. 236   @return `AF_INET` for IPv4, `AF_INET6` for IPv6.
237   */ 237   */
238   inline int 238   inline int
239   endpoint_family(endpoint const& ep) noexcept 239   endpoint_family(endpoint const& ep) noexcept
240   { 240   {
241   return ep.is_v6() ? AF_INET6 : AF_INET; 241   return ep.is_v6() ? AF_INET6 : AF_INET;
242   } 242   }
243   243  
244   /** Return the address family of a socket descriptor. 244   /** Return the address family of a socket descriptor.
245   245  
246   @param fd The socket file descriptor. 246   @param fd The socket file descriptor.
247   @return AF_INET, AF_INET6, or AF_UNSPEC on failure. 247   @return AF_INET, AF_INET6, or AF_UNSPEC on failure.
248   */ 248   */
249   inline int 249   inline int
HITCBC 250   6234 socket_family( 250   6248 socket_family(
251   #if BOOST_COROSIO_POSIX 251   #if BOOST_COROSIO_POSIX
252   int fd 252   int fd
253   #else 253   #else
254   std::uintptr_t fd 254   std::uintptr_t fd
255   #endif 255   #endif
256   ) noexcept 256   ) noexcept
257   { 257   {
HITCBC 258   6234 sockaddr_storage storage{}; 258   6248 sockaddr_storage storage{};
HITCBC 259   6234 socklen_t len = sizeof(storage); 259   6248 socklen_t len = sizeof(storage);
HITCBC 260   6234 if (getsockname( 260   6248 if (getsockname(
261   #if BOOST_COROSIO_POSIX 261   #if BOOST_COROSIO_POSIX
262   fd, 262   fd,
263   #else 263   #else
264   static_cast<SOCKET>(fd), 264   static_cast<SOCKET>(fd),
265   #endif 265   #endif
HITCBC 266   6234 reinterpret_cast<sockaddr*>(&storage), &len) != 0) 266   6248 reinterpret_cast<sockaddr*>(&storage), &len) != 0)
HITCBC 267   2 return AF_UNSPEC; 267   2 return AF_UNSPEC;
HITCBC 268   6232 return storage.ss_family; 268   6246 return storage.ss_family;
269   } 269   }
270   270  
271   //---------------------------------------------------------- 271   //----------------------------------------------------------
272   // local_endpoint (AF_UNIX) conversions 272   // local_endpoint (AF_UNIX) conversions
273   //---------------------------------------------------------- 273   //----------------------------------------------------------
274   274  
275   // Platform-agnostic sockaddr_un alias. POSIX uses the real 275   // Platform-agnostic sockaddr_un alias. POSIX uses the real
276   // sockaddr_un from <sys/un.h>; Windows uses a private struct 276   // sockaddr_un from <sys/un.h>; Windows uses a private struct
277   // matching the layout (same approach as Boost.Asio). 277   // matching the layout (same approach as Boost.Asio).
278   #if BOOST_COROSIO_POSIX 278   #if BOOST_COROSIO_POSIX
279   using un_sa_t = sockaddr_un; 279   using un_sa_t = sockaddr_un;
280   #else 280   #else
281   struct un_sa_t 281   struct un_sa_t
282   { 282   {
283   u_short sun_family; 283   u_short sun_family;
284   char sun_path[108]; 284   char sun_path[108];
285   }; 285   };
286   #endif 286   #endif
287   287  
288   /** Convert a local_endpoint to sockaddr_storage. 288   /** Convert a local_endpoint to sockaddr_storage.
289   289  
290   @param ep The local endpoint to convert. 290   @param ep The local endpoint to convert.
291   @param storage Output parameter filled with the sockaddr_un. 291   @param storage Output parameter filled with the sockaddr_un.
292   @return The length of the filled sockaddr structure. 292   @return The length of the filled sockaddr structure.
293   */ 293   */
294   inline socklen_t 294   inline socklen_t
HITCBC 295   274 to_sockaddr(local_endpoint const& ep, sockaddr_storage& storage) noexcept 295   274 to_sockaddr(local_endpoint const& ep, sockaddr_storage& storage) noexcept
296   { 296   {
HITCBC 297   274 std::memset(&storage, 0, sizeof(storage)); 297   274 std::memset(&storage, 0, sizeof(storage));
HITCBC 298   274 un_sa_t sa{}; 298   274 un_sa_t sa{};
HITCBC 299   274 sa.sun_family = AF_UNIX; 299   274 sa.sun_family = AF_UNIX;
HITCBC 300   274 auto path = ep.path(); 300   274 auto path = ep.path();
HITCBC 301   274 auto copy_len = (std::min)(path.size(), sizeof(sa.sun_path)); 301   274 auto copy_len = (std::min)(path.size(), sizeof(sa.sun_path));
HITCBC 302   274 if (copy_len > 0) 302   274 if (copy_len > 0)
HITCBC 303   274 std::memcpy(sa.sun_path, path.data(), copy_len); 303   274 std::memcpy(sa.sun_path, path.data(), copy_len);
HITCBC 304   274 std::memcpy(&storage, &sa, sizeof(sa)); 304   274 std::memcpy(&storage, &sa, sizeof(sa));
305   305  
HITCBC 306   274 if (ep.is_abstract()) 306   274 if (ep.is_abstract())
HITCBC 307   6 return static_cast<socklen_t>(offsetof(un_sa_t, sun_path) + copy_len); 307   6 return static_cast<socklen_t>(offsetof(un_sa_t, sun_path) + copy_len);
HITCBC 308   268 return static_cast<socklen_t>(sizeof(sa)); 308   268 return static_cast<socklen_t>(sizeof(sa));
309   } 309   }
310   310  
311   /** Convert a local_endpoint to sockaddr_storage (family-aware overload). 311   /** Convert a local_endpoint to sockaddr_storage (family-aware overload).
312   312  
313   The socket_family parameter is ignored for Unix sockets since 313   The socket_family parameter is ignored for Unix sockets since
314   there is no dual-stack mapping. 314   there is no dual-stack mapping.
315   315  
316   @param ep The local endpoint to convert. 316   @param ep The local endpoint to convert.
317   @param socket_family Ignored. 317   @param socket_family Ignored.
318   @param storage Output parameter filled with the sockaddr_un. 318   @param storage Output parameter filled with the sockaddr_un.
319   @return The length of the filled sockaddr structure. 319   @return The length of the filled sockaddr structure.
320   */ 320   */
321   inline socklen_t 321   inline socklen_t
HITCBC 322   199 to_sockaddr( 322   199 to_sockaddr(
323   local_endpoint const& ep, 323   local_endpoint const& ep,
324   int /*socket_family*/, 324   int /*socket_family*/,
325   sockaddr_storage& storage) noexcept 325   sockaddr_storage& storage) noexcept
326   { 326   {
HITCBC 327   199 return to_sockaddr(ep, storage); 327   199 return to_sockaddr(ep, storage);
328   } 328   }
329   329  
330   /** Create a local_endpoint from sockaddr_storage. 330   /** Create a local_endpoint from sockaddr_storage.
331   331  
332   @param storage The sockaddr_storage (must have ss_family == AF_UNIX). 332   @param storage The sockaddr_storage (must have ss_family == AF_UNIX).
333   @param len The address length returned by the kernel. 333   @param len The address length returned by the kernel.
334   @return A local_endpoint with the path extracted from the 334   @return A local_endpoint with the path extracted from the
335   sockaddr_un, or an empty endpoint if the family is not AF_UNIX. 335   sockaddr_un, or an empty endpoint if the family is not AF_UNIX.
336   */ 336   */
337   inline local_endpoint 337   inline local_endpoint
HITCBC 338   890 from_sockaddr_local(sockaddr_storage const& storage, socklen_t len) noexcept 338   890 from_sockaddr_local(sockaddr_storage const& storage, socklen_t len) noexcept
339   { 339   {
HITCBC 340   890 if (storage.ss_family != AF_UNIX) 340   890 if (storage.ss_family != AF_UNIX)
HITCBC 341   1 return local_endpoint{}; 341   1 return local_endpoint{};
342   342  
HITCBC 343   889 un_sa_t sa{}; 343   889 un_sa_t sa{};
HITCBC 344   889 std::memcpy( 344   889 std::memcpy(
HITCBC 345   889 &sa, &storage, (std::min)(static_cast<std::size_t>(len), sizeof(sa))); 345   889 &sa, &storage, (std::min)(static_cast<std::size_t>(len), sizeof(sa)));
346   346  
HITCBC 347   889 auto path_offset = offsetof(un_sa_t, sun_path); 347   889 auto path_offset = offsetof(un_sa_t, sun_path);
HITCBC 348   889 if (static_cast<std::size_t>(len) <= path_offset) 348   889 if (static_cast<std::size_t>(len) <= path_offset)
HITCBC 349   661 return local_endpoint{}; 349   661 return local_endpoint{};
350   350  
351   // Clamp to the buffer: a foreign len may overstate the payload, 351   // Clamp to the buffer: a foreign len may overstate the payload,
352   // and sun_path is the struct's last member. 352   // and sun_path is the struct's last member.
HITCBC 353   456 auto path_len = (std::min)(static_cast<std::size_t>(len) - path_offset, 353   456 auto path_len = (std::min)(static_cast<std::size_t>(len) - path_offset,
HITCBC 354   228 sizeof(sa.sun_path)); 354   228 sizeof(sa.sun_path));
355   355  
356   // Non-abstract paths may be null-terminated by the kernel 356   // Non-abstract paths may be null-terminated by the kernel
HITCBC 357   228 if (path_len > 0 && sa.sun_path[0] != '\0') 357   228 if (path_len > 0 && sa.sun_path[0] != '\0')
358   { 358   {
359   auto* end = 359   auto* end =
HITCBC 360   222 static_cast<char const*>(std::memchr(sa.sun_path, '\0', path_len)); 360   222 static_cast<char const*>(std::memchr(sa.sun_path, '\0', path_len));
HITCBC 361   222 if (end) 361   222 if (end)
HITCBC 362   222 path_len = static_cast<std::size_t>(end - sa.sun_path); 362   222 path_len = static_cast<std::size_t>(end - sa.sun_path);
363   } 363   }
364   364  
365   // A foreign sun_path may exceed corosio's cap; the length 365   // A foreign sun_path may exceed corosio's cap; the length
366   // pre-check keeps the throwing constructor unreachable. 366   // pre-check keeps the throwing constructor unreachable.
HITCBC 367   228 if (path_len > local_endpoint::max_path_length) 367   228 if (path_len > local_endpoint::max_path_length)
MISUBC 368   ✗ return local_endpoint{}; 368   ✗ return local_endpoint{};
HITCBC 369   228 return local_endpoint(std::string_view(sa.sun_path, path_len)); 369   228 return local_endpoint(std::string_view(sa.sun_path, path_len));
370   } 370   }
371   371  
372   //---------------------------------------------------------- 372   //----------------------------------------------------------
373   // Tag-dispatch helpers for templatized reactor code. 373   // Tag-dispatch helpers for templatized reactor code.
374   // Overload resolution selects the correct conversion based 374   // Overload resolution selects the correct conversion based
375   // on the Endpoint type. 375   // on the Endpoint type.
376   //---------------------------------------------------------- 376   //----------------------------------------------------------
377   377  
378   /** Convert sockaddr_storage to an IP endpoint (tag overload). 378   /** Convert sockaddr_storage to an IP endpoint (tag overload).
379   379  
380   @param storage The sockaddr_storage with fields in network byte order. 380   @param storage The sockaddr_storage with fields in network byte order.
381   @param len The address length returned by the kernel. 381   @param len The address length returned by the kernel.
382   @return An endpoint with address and port extracted from storage. 382   @return An endpoint with address and port extracted from storage.
383   */ 383   */
384   inline endpoint 384   inline endpoint
HITCBC 385   9859 from_sockaddr_as( 385   9889 from_sockaddr_as(
386   sockaddr_storage const& storage, 386   sockaddr_storage const& storage,
387   socklen_t /*len*/, 387   socklen_t /*len*/,
388   endpoint const&) noexcept 388   endpoint const&) noexcept
389   { 389   {
HITCBC 390   9859 return from_sockaddr(storage); 390   9889 return from_sockaddr(storage);
391   } 391   }
392   392  
393   /** Convert sockaddr_storage to a local_endpoint (tag overload). 393   /** Convert sockaddr_storage to a local_endpoint (tag overload).
394   394  
395   @param storage The sockaddr_storage. 395   @param storage The sockaddr_storage.
396   @param len The address length returned by the kernel. 396   @param len The address length returned by the kernel.
397   @return A local_endpoint with path extracted from storage. 397   @return A local_endpoint with path extracted from storage.
398   */ 398   */
399   inline local_endpoint 399   inline local_endpoint
HITCBC 400   890 from_sockaddr_as( 400   890 from_sockaddr_as(
401   sockaddr_storage const& storage, 401   sockaddr_storage const& storage,
402   socklen_t len, 402   socklen_t len,
403   local_endpoint const&) noexcept 403   local_endpoint const&) noexcept
404   { 404   {
HITCBC 405   890 return from_sockaddr_local(storage, len); 405   890 return from_sockaddr_local(storage, len);
406   } 406   }
407   407  
408   } // namespace boost::corosio::detail 408   } // namespace boost::corosio::detail
409   409  
410   #endif 410   #endif