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