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/local_endpoint.hpp>
14  
#include <boost/corosio/local_endpoint.hpp>
15  
#include <boost/corosio/detail/platform.hpp>
15  
#include <boost/corosio/detail/platform.hpp>
16  

16  

17  
#include <algorithm>
17  
#include <algorithm>
18  
#include <cstring>
18  
#include <cstring>
19  

19  

20  
#if BOOST_COROSIO_POSIX
20  
#if BOOST_COROSIO_POSIX
21  
#include <sys/socket.h>
21  
#include <sys/socket.h>
22  
#include <sys/un.h>
22  
#include <sys/un.h>
23  
#include <netinet/in.h>
23  
#include <netinet/in.h>
24  
#include <arpa/inet.h>
24  
#include <arpa/inet.h>
25  
#else
25  
#else
26  
#ifndef WIN32_LEAN_AND_MEAN
26  
#ifndef WIN32_LEAN_AND_MEAN
27  
#define WIN32_LEAN_AND_MEAN
27  
#define WIN32_LEAN_AND_MEAN
28  
#endif
28  
#endif
29  
#ifndef NOMINMAX
29  
#ifndef NOMINMAX
30  
#define NOMINMAX
30  
#define NOMINMAX
31  
#endif
31  
#endif
32  
#include <WinSock2.h>
32  
#include <WinSock2.h>
33  
#include <Ws2tcpip.h>
33  
#include <Ws2tcpip.h>
34  
#endif
34  
#endif
35  

35  

36  
#include <cstddef> // offsetof
36  
#include <cstddef> // offsetof
37  

37  

38  
#ifndef AF_UNIX
38  
#ifndef AF_UNIX
39  
#define AF_UNIX 1
39  
#define AF_UNIX 1
40  
#endif
40  
#endif
41  

41  

42  
namespace boost::corosio::detail {
42  
namespace boost::corosio::detail {
43  

43  

44  
/** Convert IPv4 endpoint to sockaddr_in.
44  
/** Convert IPv4 endpoint to sockaddr_in.
45  

45  

46  
    @param ep The endpoint to convert. Must be IPv4 (is_v4() == true).
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.
47  
    @return A sockaddr_in structure with fields in network byte order.
48  
*/
48  
*/
49  
inline sockaddr_in
49  
inline sockaddr_in
50  
to_sockaddr_in(endpoint const& ep) noexcept
50  
to_sockaddr_in(endpoint const& ep) noexcept
51  
{
51  
{
52  
    sockaddr_in sa{};
52  
    sockaddr_in sa{};
53  
    sa.sin_family = AF_INET;
53  
    sa.sin_family = AF_INET;
54  
    sa.sin_port   = htons(ep.port());
54  
    sa.sin_port   = htons(ep.port());
55  
    auto bytes    = ep.v4_address().to_bytes();
55  
    auto bytes    = ep.v4_address().to_bytes();
56  
    std::memcpy(&sa.sin_addr, bytes.data(), 4);
56  
    std::memcpy(&sa.sin_addr, bytes.data(), 4);
57  
    return sa;
57  
    return sa;
58  
}
58  
}
59  

59  

60  
/** Convert IPv6 endpoint to sockaddr_in6.
60  
/** Convert IPv6 endpoint to sockaddr_in6.
61  

61  

62  
    @param ep The endpoint to convert. Must be IPv6 (is_v6() == true).
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.
63  
    @return A sockaddr_in6 structure with fields in network byte order.
64  
*/
64  
*/
65  
inline sockaddr_in6
65  
inline sockaddr_in6
66  
to_sockaddr_in6(endpoint const& ep) noexcept
66  
to_sockaddr_in6(endpoint const& ep) noexcept
67  
{
67  
{
68  
    sockaddr_in6 sa{};
68  
    sockaddr_in6 sa{};
69  
    sa.sin6_family = AF_INET6;
69  
    sa.sin6_family = AF_INET6;
70  
    sa.sin6_port   = htons(ep.port());
70  
    sa.sin6_port   = htons(ep.port());
71  
    auto bytes     = ep.v6_address().to_bytes();
71  
    auto bytes     = ep.v6_address().to_bytes();
72  
    std::memcpy(&sa.sin6_addr, bytes.data(), 16);
72  
    std::memcpy(&sa.sin6_addr, bytes.data(), 16);
73  
    return sa;
73  
    return sa;
74  
}
74  
}
75  

75  

76  
/** Create endpoint from sockaddr_in.
76  
/** Create endpoint from sockaddr_in.
77  

77  

78  
    @param sa The sockaddr_in structure with fields in network byte order.
78  
    @param sa The sockaddr_in structure with fields in network byte order.
79  
    @return An endpoint with address and port extracted from sa.
79  
    @return An endpoint with address and port extracted from sa.
80  
*/
80  
*/
81  
inline endpoint
81  
inline endpoint
82  
from_sockaddr_in(sockaddr_in const& sa) noexcept
82  
from_sockaddr_in(sockaddr_in const& sa) noexcept
83  
{
83  
{
84  
    ipv4_address::bytes_type bytes;
84  
    ipv4_address::bytes_type bytes;
85  
    std::memcpy(bytes.data(), &sa.sin_addr, 4);
85  
    std::memcpy(bytes.data(), &sa.sin_addr, 4);
86  
    return endpoint(ipv4_address(bytes), ntohs(sa.sin_port));
86  
    return endpoint(ipv4_address(bytes), ntohs(sa.sin_port));
87  
}
87  
}
88  

88  

89  
/** Create endpoint from sockaddr_in6.
89  
/** Create endpoint from sockaddr_in6.
90  

90  

91  
    @param sa The sockaddr_in6 structure with fields in network byte order.
91  
    @param sa The sockaddr_in6 structure with fields in network byte order.
92  
    @return An endpoint with address and port extracted from sa.
92  
    @return An endpoint with address and port extracted from sa.
93  
*/
93  
*/
94  
inline endpoint
94  
inline endpoint
95  
from_sockaddr_in6(sockaddr_in6 const& sa) noexcept
95  
from_sockaddr_in6(sockaddr_in6 const& sa) noexcept
96  
{
96  
{
97  
    ipv6_address::bytes_type bytes;
97  
    ipv6_address::bytes_type bytes;
98  
    std::memcpy(bytes.data(), &sa.sin6_addr, 16);
98  
    std::memcpy(bytes.data(), &sa.sin6_addr, 16);
99  
    return endpoint(ipv6_address(bytes), ntohs(sa.sin6_port));
99  
    return endpoint(ipv6_address(bytes), ntohs(sa.sin6_port));
100  
}
100  
}
101  

101  

102  
/** Convert an IPv4 endpoint to an IPv4-mapped IPv6 sockaddr_in6.
102  
/** Convert an IPv4 endpoint to an IPv4-mapped IPv6 sockaddr_in6.
103  

103  

104  
    Produces a `sockaddr_in6` with the `::ffff:` prefix, suitable
104  
    Produces a `sockaddr_in6` with the `::ffff:` prefix, suitable
105  
    for passing an IPv4 destination to a dual-stack IPv6 socket.
105  
    for passing an IPv4 destination to a dual-stack IPv6 socket.
106  

106  

107  
    @param ep The endpoint to convert. Must be IPv4 (is_v4() == true).
107  
    @param ep The endpoint to convert. Must be IPv4 (is_v4() == true).
108  
    @return A sockaddr_in6 with the IPv4-mapped address.
108  
    @return A sockaddr_in6 with the IPv4-mapped address.
109  
*/
109  
*/
110  
inline sockaddr_in6
110  
inline sockaddr_in6
111  
to_v4_mapped_sockaddr_in6(endpoint const& ep) noexcept
111  
to_v4_mapped_sockaddr_in6(endpoint const& ep) noexcept
112  
{
112  
{
113  
    sockaddr_in6 sa{};
113  
    sockaddr_in6 sa{};
114  
    sa.sin6_family = AF_INET6;
114  
    sa.sin6_family = AF_INET6;
115  
    sa.sin6_port   = htons(ep.port());
115  
    sa.sin6_port   = htons(ep.port());
116  
    // ::ffff:0:0/96 prefix
116  
    // ::ffff:0:0/96 prefix
117  
    sa.sin6_addr.s6_addr[10] = 0xff;
117  
    sa.sin6_addr.s6_addr[10] = 0xff;
118  
    sa.sin6_addr.s6_addr[11] = 0xff;
118  
    sa.sin6_addr.s6_addr[11] = 0xff;
119  
    auto bytes               = ep.v4_address().to_bytes();
119  
    auto bytes               = ep.v4_address().to_bytes();
120  
    std::memcpy(&sa.sin6_addr.s6_addr[12], bytes.data(), 4);
120  
    std::memcpy(&sa.sin6_addr.s6_addr[12], bytes.data(), 4);
121  
    return sa;
121  
    return sa;
122  
}
122  
}
123  

123  

124  
/** Convert endpoint to sockaddr_storage.
124  
/** Convert endpoint to sockaddr_storage.
125  

125  

126  
    Dispatches to @ref to_sockaddr_in or @ref to_sockaddr_in6
126  
    Dispatches to @ref to_sockaddr_in or @ref to_sockaddr_in6
127  
    based on the endpoint's address family.
127  
    based on the endpoint's address family.
128  

128  

129  
    @param ep The endpoint to convert.
129  
    @param ep The endpoint to convert.
130  
    @param storage Output parameter filled with the sockaddr.
130  
    @param storage Output parameter filled with the sockaddr.
131  
    @return The length of the filled sockaddr structure.
131  
    @return The length of the filled sockaddr structure.
132  
*/
132  
*/
133  
inline socklen_t
133  
inline socklen_t
134  
to_sockaddr(endpoint const& ep, sockaddr_storage& storage) noexcept
134  
to_sockaddr(endpoint const& ep, sockaddr_storage& storage) noexcept
135  
{
135  
{
136  
    std::memset(&storage, 0, sizeof(storage));
136  
    std::memset(&storage, 0, sizeof(storage));
137  
    if (ep.is_v4())
137  
    if (ep.is_v4())
138  
    {
138  
    {
139  
        auto sa = to_sockaddr_in(ep);
139  
        auto sa = to_sockaddr_in(ep);
140  
        std::memcpy(&storage, &sa, sizeof(sa));
140  
        std::memcpy(&storage, &sa, sizeof(sa));
141  
        return sizeof(sa);
141  
        return sizeof(sa);
142  
    }
142  
    }
143  
    auto sa6 = to_sockaddr_in6(ep);
143  
    auto sa6 = to_sockaddr_in6(ep);
144  
    std::memcpy(&storage, &sa6, sizeof(sa6));
144  
    std::memcpy(&storage, &sa6, sizeof(sa6));
145  
    return sizeof(sa6);
145  
    return sizeof(sa6);
146  
}
146  
}
147  

147  

148  
/** Convert endpoint to sockaddr_storage for a specific socket family.
148  
/** Convert endpoint to sockaddr_storage for a specific socket family.
149  

149  

150  
    When the socket is AF_INET6 and the endpoint is IPv4, the address
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
151  
    is converted to an IPv4-mapped IPv6 address (`::ffff:x.x.x.x`) so
152  
    dual-stack sockets can connect to IPv4 destinations.
152  
    dual-stack sockets can connect to IPv4 destinations.
153  

153  

154  
    @param ep The endpoint to convert.
154  
    @param ep The endpoint to convert.
155  
    @param socket_family The address family of the socket (AF_INET or
155  
    @param socket_family The address family of the socket (AF_INET or
156  
        AF_INET6).
156  
        AF_INET6).
157  
    @param storage Output parameter filled with the sockaddr.
157  
    @param storage Output parameter filled with the sockaddr.
158  
    @return The length of the filled sockaddr structure.
158  
    @return The length of the filled sockaddr structure.
159  
*/
159  
*/
160  
inline socklen_t
160  
inline socklen_t
161  
to_sockaddr(
161  
to_sockaddr(
162  
    endpoint const& ep, int socket_family, sockaddr_storage& storage) noexcept
162  
    endpoint const& ep, int socket_family, sockaddr_storage& storage) noexcept
163  
{
163  
{
164  
    // IPv4 endpoint on IPv6 socket: use IPv4-mapped address
164  
    // IPv4 endpoint on IPv6 socket: use IPv4-mapped address
165  
    if (ep.is_v4() && socket_family == AF_INET6)
165  
    if (ep.is_v4() && socket_family == AF_INET6)
166  
    {
166  
    {
167  
        std::memset(&storage, 0, sizeof(storage));
167  
        std::memset(&storage, 0, sizeof(storage));
168  
        auto sa6 = to_v4_mapped_sockaddr_in6(ep);
168  
        auto sa6 = to_v4_mapped_sockaddr_in6(ep);
169  
        std::memcpy(&storage, &sa6, sizeof(sa6));
169  
        std::memcpy(&storage, &sa6, sizeof(sa6));
170  
        return sizeof(sa6);
170  
        return sizeof(sa6);
171  
    }
171  
    }
172  
    return to_sockaddr(ep, storage);
172  
    return to_sockaddr(ep, storage);
173  
}
173  
}
174  

174  

175  
/** Create endpoint from sockaddr_storage.
175  
/** Create endpoint from sockaddr_storage.
176  

176  

177  
    Dispatches on `ss_family` to reconstruct the appropriate
177  
    Dispatches on `ss_family` to reconstruct the appropriate
178  
    IPv4 or IPv6 endpoint.
178  
    IPv4 or IPv6 endpoint.
179  

179  

180  
    @param storage The sockaddr_storage with fields in network byte order.
180  
    @param storage The sockaddr_storage with fields in network byte order.
181  
    @return An endpoint with address and port extracted from storage.
181  
    @return An endpoint with address and port extracted from storage.
182  
*/
182  
*/
183  
inline endpoint
183  
inline endpoint
184  
from_sockaddr(sockaddr_storage const& storage) noexcept
184  
from_sockaddr(sockaddr_storage const& storage) noexcept
185  
{
185  
{
186  
    if (storage.ss_family == AF_INET)
186  
    if (storage.ss_family == AF_INET)
187  
    {
187  
    {
188  
        sockaddr_in sa;
188  
        sockaddr_in sa;
189  
        std::memcpy(&sa, &storage, sizeof(sa));
189  
        std::memcpy(&sa, &storage, sizeof(sa));
190  
        return from_sockaddr_in(sa);
190  
        return from_sockaddr_in(sa);
191  
    }
191  
    }
192  
    if (storage.ss_family == AF_INET6)
192  
    if (storage.ss_family == AF_INET6)
193  
    {
193  
    {
194  
        sockaddr_in6 sa6;
194  
        sockaddr_in6 sa6;
195  
        std::memcpy(&sa6, &storage, sizeof(sa6));
195  
        std::memcpy(&sa6, &storage, sizeof(sa6));
196  
        return from_sockaddr_in6(sa6);
196  
        return from_sockaddr_in6(sa6);
197  
    }
197  
    }
198  
    return endpoint{};
198  
    return endpoint{};
199  
}
199  
}
200  

200  

201  
/** Return the native address family for an endpoint.
201  
/** Return the native address family for an endpoint.
202  

202  

203  
    @param ep The endpoint to query.
203  
    @param ep The endpoint to query.
204  
    @return `AF_INET` for IPv4, `AF_INET6` for IPv6.
204  
    @return `AF_INET` for IPv4, `AF_INET6` for IPv6.
205  
*/
205  
*/
206  
inline int
206  
inline int
207  
endpoint_family(endpoint const& ep) noexcept
207  
endpoint_family(endpoint const& ep) noexcept
208  
{
208  
{
209  
    return ep.is_v6() ? AF_INET6 : AF_INET;
209  
    return ep.is_v6() ? AF_INET6 : AF_INET;
210  
}
210  
}
211  

211  

212  
/** Return the address family of a socket descriptor.
212  
/** Return the address family of a socket descriptor.
213  

213  

214  
    @param fd The socket file descriptor.
214  
    @param fd The socket file descriptor.
215  
    @return AF_INET, AF_INET6, or AF_UNSPEC on failure.
215  
    @return AF_INET, AF_INET6, or AF_UNSPEC on failure.
216  
*/
216  
*/
217  
inline int
217  
inline int
218  
socket_family(
218  
socket_family(
219  
#if BOOST_COROSIO_POSIX
219  
#if BOOST_COROSIO_POSIX
220  
    int fd
220  
    int fd
221  
#else
221  
#else
222  
    std::uintptr_t fd
222  
    std::uintptr_t fd
223  
#endif
223  
#endif
224  
    ) noexcept
224  
    ) noexcept
225  
{
225  
{
226  
    sockaddr_storage storage{};
226  
    sockaddr_storage storage{};
227  
    socklen_t len = sizeof(storage);
227  
    socklen_t len = sizeof(storage);
228  
    if (getsockname(
228  
    if (getsockname(
229  
#if BOOST_COROSIO_POSIX
229  
#if BOOST_COROSIO_POSIX
230  
            fd,
230  
            fd,
231  
#else
231  
#else
232  
            static_cast<SOCKET>(fd),
232  
            static_cast<SOCKET>(fd),
233  
#endif
233  
#endif
234  
            reinterpret_cast<sockaddr*>(&storage), &len) != 0)
234  
            reinterpret_cast<sockaddr*>(&storage), &len) != 0)
235  
        return AF_UNSPEC;
235  
        return AF_UNSPEC;
236  
    return storage.ss_family;
236  
    return storage.ss_family;
237  
}
237  
}
238  

238  

239  
//----------------------------------------------------------
239  
//----------------------------------------------------------
240  
// local_endpoint (AF_UNIX) conversions
240  
// local_endpoint (AF_UNIX) conversions
241  
//----------------------------------------------------------
241  
//----------------------------------------------------------
242  

242  

243  
// Platform-agnostic sockaddr_un alias.  POSIX uses the real
243  
// Platform-agnostic sockaddr_un alias.  POSIX uses the real
244  
// sockaddr_un from <sys/un.h>; Windows uses a private struct
244  
// sockaddr_un from <sys/un.h>; Windows uses a private struct
245  
// matching the layout (same approach as Boost.Asio).
245  
// matching the layout (same approach as Boost.Asio).
246  
#if BOOST_COROSIO_POSIX
246  
#if BOOST_COROSIO_POSIX
247  
using un_sa_t = sockaddr_un;
247  
using un_sa_t = sockaddr_un;
248  
#else
248  
#else
249  
struct un_sa_t { u_short sun_family; char sun_path[108]; };
249  
struct un_sa_t { u_short sun_family; char sun_path[108]; };
250  
#endif
250  
#endif
251  

251  

252  
/** Convert a local_endpoint to sockaddr_storage.
252  
/** Convert a local_endpoint to sockaddr_storage.
253  

253  

254  
    @param ep The local endpoint to convert.
254  
    @param ep The local endpoint to convert.
255  
    @param storage Output parameter filled with the sockaddr_un.
255  
    @param storage Output parameter filled with the sockaddr_un.
256  
    @return The length of the filled sockaddr structure.
256  
    @return The length of the filled sockaddr structure.
257  
*/
257  
*/
258  
inline socklen_t
258  
inline socklen_t
259  
to_sockaddr(local_endpoint const& ep, sockaddr_storage& storage) noexcept
259  
to_sockaddr(local_endpoint const& ep, sockaddr_storage& storage) noexcept
260  
{
260  
{
261  
    std::memset(&storage, 0, sizeof(storage));
261  
    std::memset(&storage, 0, sizeof(storage));
262  
    un_sa_t sa{};
262  
    un_sa_t sa{};
263  
    sa.sun_family = AF_UNIX;
263  
    sa.sun_family = AF_UNIX;
264  
    auto path     = ep.path();
264  
    auto path     = ep.path();
265  
    auto copy_len = (std::min)(path.size(), sizeof(sa.sun_path));
265  
    auto copy_len = (std::min)(path.size(), sizeof(sa.sun_path));
266  
    if (copy_len > 0)
266  
    if (copy_len > 0)
267  
        std::memcpy(sa.sun_path, path.data(), copy_len);
267  
        std::memcpy(sa.sun_path, path.data(), copy_len);
268  
    std::memcpy(&storage, &sa, sizeof(sa));
268  
    std::memcpy(&storage, &sa, sizeof(sa));
269  

269  

270  
    if (ep.is_abstract())
270  
    if (ep.is_abstract())
271  
        return static_cast<socklen_t>(
271  
        return static_cast<socklen_t>(
272  
            offsetof(un_sa_t, sun_path) + copy_len);
272  
            offsetof(un_sa_t, sun_path) + copy_len);
273  
    return static_cast<socklen_t>(sizeof(sa));
273  
    return static_cast<socklen_t>(sizeof(sa));
274  
}
274  
}
275  

275  

276  
/** Convert a local_endpoint to sockaddr_storage (family-aware overload).
276  
/** Convert a local_endpoint to sockaddr_storage (family-aware overload).
277  

277  

278  
    The socket_family parameter is ignored for Unix sockets since
278  
    The socket_family parameter is ignored for Unix sockets since
279  
    there is no dual-stack mapping.
279  
    there is no dual-stack mapping.
280  

280  

281  
    @param ep The local endpoint to convert.
281  
    @param ep The local endpoint to convert.
282  
    @param socket_family Ignored.
282  
    @param socket_family Ignored.
283  
    @param storage Output parameter filled with the sockaddr_un.
283  
    @param storage Output parameter filled with the sockaddr_un.
284  
    @return The length of the filled sockaddr structure.
284  
    @return The length of the filled sockaddr structure.
285  
*/
285  
*/
286  
inline socklen_t
286  
inline socklen_t
287  
to_sockaddr(
287  
to_sockaddr(
288  
    local_endpoint const& ep,
288  
    local_endpoint const& ep,
289  
    int /*socket_family*/,
289  
    int /*socket_family*/,
290  
    sockaddr_storage& storage) noexcept
290  
    sockaddr_storage& storage) noexcept
291  
{
291  
{
292  
    return to_sockaddr(ep, storage);
292  
    return to_sockaddr(ep, storage);
293  
}
293  
}
294  

294  

295  
/** Create a local_endpoint from sockaddr_storage.
295  
/** Create a local_endpoint from sockaddr_storage.
296  

296  

297  
    @param storage The sockaddr_storage (must have ss_family == AF_UNIX).
297  
    @param storage The sockaddr_storage (must have ss_family == AF_UNIX).
298  
    @param len The address length returned by the kernel.
298  
    @param len The address length returned by the kernel.
299  
    @return A local_endpoint with the path extracted from the
299  
    @return A local_endpoint with the path extracted from the
300  
        sockaddr_un, or an empty endpoint if the family is not AF_UNIX.
300  
        sockaddr_un, or an empty endpoint if the family is not AF_UNIX.
301  
*/
301  
*/
302  
inline local_endpoint
302  
inline local_endpoint
303  
from_sockaddr_local(
303  
from_sockaddr_local(
304  
    sockaddr_storage const& storage, socklen_t len) noexcept
304  
    sockaddr_storage const& storage, socklen_t len) noexcept
305  
{
305  
{
306  
    if (storage.ss_family != AF_UNIX)
306  
    if (storage.ss_family != AF_UNIX)
307  
        return local_endpoint{};
307  
        return local_endpoint{};
308  

308  

309  
    un_sa_t sa{};
309  
    un_sa_t sa{};
310  
    std::memcpy(
310  
    std::memcpy(
311  
        &sa, &storage,
311  
        &sa, &storage,
312  
        (std::min)(static_cast<std::size_t>(len), sizeof(sa)));
312  
        (std::min)(static_cast<std::size_t>(len), sizeof(sa)));
313  

313  

314  
    auto path_offset = offsetof(un_sa_t, sun_path);
314  
    auto path_offset = offsetof(un_sa_t, sun_path);
315  
    if (static_cast<std::size_t>(len) <= path_offset)
315  
    if (static_cast<std::size_t>(len) <= path_offset)
316  
        return local_endpoint{};
316  
        return local_endpoint{};
317  

317  

318  
    auto path_len = static_cast<std::size_t>(len) - path_offset;
318  
    auto path_len = static_cast<std::size_t>(len) - path_offset;
319  

319  

320  
    // Non-abstract paths may be null-terminated by the kernel
320  
    // Non-abstract paths may be null-terminated by the kernel
321  
    if (path_len > 0 && sa.sun_path[0] != '\0')
321  
    if (path_len > 0 && sa.sun_path[0] != '\0')
322  
    {
322  
    {
323  
        auto* end = static_cast<char const*>(
323  
        auto* end = static_cast<char const*>(
324  
            std::memchr(sa.sun_path, '\0', path_len));
324  
            std::memchr(sa.sun_path, '\0', path_len));
325  
        if (end)
325  
        if (end)
326  
            path_len = static_cast<std::size_t>(end - sa.sun_path);
326  
            path_len = static_cast<std::size_t>(end - sa.sun_path);
327  
    }
327  
    }
328  

328  

329  
    std::error_code ec;
329  
    std::error_code ec;
330  
    local_endpoint ep(std::string_view(sa.sun_path, path_len), ec);
330  
    local_endpoint ep(std::string_view(sa.sun_path, path_len), ec);
331  
    if (ec)
331  
    if (ec)
332  
        return local_endpoint{};
332  
        return local_endpoint{};
333  
    return ep;
333  
    return ep;
334  
}
334  
}
335  

335  

336  
//----------------------------------------------------------
336  
//----------------------------------------------------------
337  
// Tag-dispatch helpers for templatized reactor code.
337  
// Tag-dispatch helpers for templatized reactor code.
338  
// Overload resolution selects the correct conversion based
338  
// Overload resolution selects the correct conversion based
339  
// on the Endpoint type.
339  
// on the Endpoint type.
340  
//----------------------------------------------------------
340  
//----------------------------------------------------------
341  

341  

342  
/** Convert sockaddr_storage to an IP endpoint (tag overload).
342  
/** Convert sockaddr_storage to an IP endpoint (tag overload).
343  

343  

344  
    @param storage The sockaddr_storage with fields in network byte order.
344  
    @param storage The sockaddr_storage with fields in network byte order.
345  
    @param len The address length returned by the kernel.
345  
    @param len The address length returned by the kernel.
346  
    @return An endpoint with address and port extracted from storage.
346  
    @return An endpoint with address and port extracted from storage.
347  
*/
347  
*/
348  
inline endpoint
348  
inline endpoint
349  
from_sockaddr_as(
349  
from_sockaddr_as(
350  
    sockaddr_storage const& storage, socklen_t /*len*/, endpoint const&) noexcept
350  
    sockaddr_storage const& storage, socklen_t /*len*/, endpoint const&) noexcept
351  
{
351  
{
352  
    return from_sockaddr(storage);
352  
    return from_sockaddr(storage);
353  
}
353  
}
354  

354  

355  
/** Convert sockaddr_storage to a local_endpoint (tag overload).
355  
/** Convert sockaddr_storage to a local_endpoint (tag overload).
356  

356  

357  
    @param storage The sockaddr_storage.
357  
    @param storage The sockaddr_storage.
358  
    @param len The address length returned by the kernel.
358  
    @param len The address length returned by the kernel.
359  
    @return A local_endpoint with path extracted from storage.
359  
    @return A local_endpoint with path extracted from storage.
360  
*/
360  
*/
361  
inline local_endpoint
361  
inline local_endpoint
362  
from_sockaddr_as(
362  
from_sockaddr_as(
363  
    sockaddr_storage const& storage,
363  
    sockaddr_storage const& storage,
364  
    socklen_t len,
364  
    socklen_t len,
365  
    local_endpoint const&) noexcept
365  
    local_endpoint const&) noexcept
366  
{
366  
{
367  
    return from_sockaddr_local(storage, len);
367  
    return from_sockaddr_local(storage, len);
368  
}
368  
}
369  

369  

370  
} // namespace boost::corosio::detail
370  
} // namespace boost::corosio::detail
371  

371  

372  
#endif
372  
#endif