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

16  

16  
#include <cstring>
17  
#include <cstring>
17  

18  

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

34  

33  
namespace boost::corosio::detail {
35  
namespace boost::corosio::detail {
34  

36  

35  
/** Convert IPv4 endpoint to sockaddr_in.
37  
/** Convert IPv4 endpoint to sockaddr_in.
36  

38  

37  
    @param ep The endpoint to convert. Must be IPv4 (is_v4() == true).
39  
    @param ep The endpoint to convert. Must be IPv4 (is_v4() == true).
38  
    @return A sockaddr_in structure with fields in network byte order.
40  
    @return A sockaddr_in structure with fields in network byte order.
39  
*/
41  
*/
40  
inline sockaddr_in
42  
inline sockaddr_in
41  
to_sockaddr_in(endpoint const& ep) noexcept
43  
to_sockaddr_in(endpoint const& ep) noexcept
42  
{
44  
{
43  
    sockaddr_in sa{};
45  
    sockaddr_in sa{};
44  
    sa.sin_family = AF_INET;
46  
    sa.sin_family = AF_INET;
45  
    sa.sin_port   = htons(ep.port());
47  
    sa.sin_port   = htons(ep.port());
46  
    auto bytes    = ep.v4_address().to_bytes();
48  
    auto bytes    = ep.v4_address().to_bytes();
47  
    std::memcpy(&sa.sin_addr, bytes.data(), 4);
49  
    std::memcpy(&sa.sin_addr, bytes.data(), 4);
48  
    return sa;
50  
    return sa;
49  
}
51  
}
50  

52  

51  
/** Convert IPv6 endpoint to sockaddr_in6.
53  
/** Convert IPv6 endpoint to sockaddr_in6.
52  

54  

53  
    @param ep The endpoint to convert. Must be IPv6 (is_v6() == true).
55  
    @param ep The endpoint to convert. Must be IPv6 (is_v6() == true).
54  
    @return A sockaddr_in6 structure with fields in network byte order.
56  
    @return A sockaddr_in6 structure with fields in network byte order.
55  
*/
57  
*/
56  
inline sockaddr_in6
58  
inline sockaddr_in6
57  
to_sockaddr_in6(endpoint const& ep) noexcept
59  
to_sockaddr_in6(endpoint const& ep) noexcept
58  
{
60  
{
59  
    sockaddr_in6 sa{};
61  
    sockaddr_in6 sa{};
60  
    sa.sin6_family = AF_INET6;
62  
    sa.sin6_family = AF_INET6;
61  
    sa.sin6_port   = htons(ep.port());
63  
    sa.sin6_port   = htons(ep.port());
62  
    auto bytes     = ep.v6_address().to_bytes();
64  
    auto bytes     = ep.v6_address().to_bytes();
63  
    std::memcpy(&sa.sin6_addr, bytes.data(), 16);
65  
    std::memcpy(&sa.sin6_addr, bytes.data(), 16);
64  
    return sa;
66  
    return sa;
65  
}
67  
}
66  

68  

67  
/** Create endpoint from sockaddr_in.
69  
/** Create endpoint from sockaddr_in.
68  

70  

69  
    @param sa The sockaddr_in structure with fields in network byte order.
71  
    @param sa The sockaddr_in structure with fields in network byte order.
70  
    @return An endpoint with address and port extracted from sa.
72  
    @return An endpoint with address and port extracted from sa.
71  
*/
73  
*/
72  
inline endpoint
74  
inline endpoint
73  
from_sockaddr_in(sockaddr_in const& sa) noexcept
75  
from_sockaddr_in(sockaddr_in const& sa) noexcept
74  
{
76  
{
75  
    ipv4_address::bytes_type bytes;
77  
    ipv4_address::bytes_type bytes;
76  
    std::memcpy(bytes.data(), &sa.sin_addr, 4);
78  
    std::memcpy(bytes.data(), &sa.sin_addr, 4);
77  
    return endpoint(ipv4_address(bytes), ntohs(sa.sin_port));
79  
    return endpoint(ipv4_address(bytes), ntohs(sa.sin_port));
78  
}
80  
}
79  

81  

80  
/** Create endpoint from sockaddr_in6.
82  
/** Create endpoint from sockaddr_in6.
81  

83  

82  
    @param sa The sockaddr_in6 structure with fields in network byte order.
84  
    @param sa The sockaddr_in6 structure with fields in network byte order.
83  
    @return An endpoint with address and port extracted from sa.
85  
    @return An endpoint with address and port extracted from sa.
84  
*/
86  
*/
85  
inline endpoint
87  
inline endpoint
86  
from_sockaddr_in6(sockaddr_in6 const& sa) noexcept
88  
from_sockaddr_in6(sockaddr_in6 const& sa) noexcept
87  
{
89  
{
88  
    ipv6_address::bytes_type bytes;
90  
    ipv6_address::bytes_type bytes;
89  
    std::memcpy(bytes.data(), &sa.sin6_addr, 16);
91  
    std::memcpy(bytes.data(), &sa.sin6_addr, 16);
90  
    return endpoint(ipv6_address(bytes), ntohs(sa.sin6_port));
92  
    return endpoint(ipv6_address(bytes), ntohs(sa.sin6_port));
91  
}
93  
}
92  

94  

93  
/** Convert an IPv4 endpoint to an IPv4-mapped IPv6 sockaddr_in6.
95  
/** Convert an IPv4 endpoint to an IPv4-mapped IPv6 sockaddr_in6.
94  

96  

95  
    Produces a `sockaddr_in6` with the `::ffff:` prefix, suitable
97  
    Produces a `sockaddr_in6` with the `::ffff:` prefix, suitable
96  
    for passing an IPv4 destination to a dual-stack IPv6 socket.
98  
    for passing an IPv4 destination to a dual-stack IPv6 socket.
97  

99  

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

116  

115  
/** Convert endpoint to sockaddr_storage.
117  
/** Convert endpoint to sockaddr_storage.
116  

118  

117  
    Dispatches to @ref to_sockaddr_in or @ref to_sockaddr_in6
119  
    Dispatches to @ref to_sockaddr_in or @ref to_sockaddr_in6
118  
    based on the endpoint's address family.
120  
    based on the endpoint's address family.
119  

121  

120  
    @param ep The endpoint to convert.
122  
    @param ep The endpoint to convert.
121  
    @param storage Output parameter filled with the sockaddr.
123  
    @param storage Output parameter filled with the sockaddr.
122  
    @return The length of the filled sockaddr structure.
124  
    @return The length of the filled sockaddr structure.
123  
*/
125  
*/
124  
inline socklen_t
126  
inline socklen_t
125  
to_sockaddr(endpoint const& ep, sockaddr_storage& storage) noexcept
127  
to_sockaddr(endpoint const& ep, sockaddr_storage& storage) noexcept
126  
{
128  
{
127  
    std::memset(&storage, 0, sizeof(storage));
129  
    std::memset(&storage, 0, sizeof(storage));
128  
    if (ep.is_v4())
130  
    if (ep.is_v4())
129  
    {
131  
    {
130  
        auto sa = to_sockaddr_in(ep);
132  
        auto sa = to_sockaddr_in(ep);
131  
        std::memcpy(&storage, &sa, sizeof(sa));
133  
        std::memcpy(&storage, &sa, sizeof(sa));
132  
        return sizeof(sa);
134  
        return sizeof(sa);
133  
    }
135  
    }
134  
    auto sa6 = to_sockaddr_in6(ep);
136  
    auto sa6 = to_sockaddr_in6(ep);
135  
    std::memcpy(&storage, &sa6, sizeof(sa6));
137  
    std::memcpy(&storage, &sa6, sizeof(sa6));
136  
    return sizeof(sa6);
138  
    return sizeof(sa6);
137  
}
139  
}
138  

140  

139  
/** Convert endpoint to sockaddr_storage for a specific socket family.
141  
/** Convert endpoint to sockaddr_storage for a specific socket family.
140  

142  

141  
    When the socket is AF_INET6 and the endpoint is IPv4, the address
143  
    When the socket is AF_INET6 and the endpoint is IPv4, the address
142  
    is converted to an IPv4-mapped IPv6 address (`::ffff:x.x.x.x`) so
144  
    is converted to an IPv4-mapped IPv6 address (`::ffff:x.x.x.x`) so
143  
    dual-stack sockets can connect to IPv4 destinations.
145  
    dual-stack sockets can connect to IPv4 destinations.
144  

146  

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

167  

166  
/** Create endpoint from sockaddr_storage.
168  
/** Create endpoint from sockaddr_storage.
167  

169  

168  
    Dispatches on `ss_family` to reconstruct the appropriate
170  
    Dispatches on `ss_family` to reconstruct the appropriate
169  
    IPv4 or IPv6 endpoint.
171  
    IPv4 or IPv6 endpoint.
170  

172  

171  
    @param storage The sockaddr_storage with fields in network byte order.
173  
    @param storage The sockaddr_storage with fields in network byte order.
172  
    @return An endpoint with address and port extracted from storage.
174  
    @return An endpoint with address and port extracted from storage.
173  
*/
175  
*/
174  
inline endpoint
176  
inline endpoint
175  
from_sockaddr(sockaddr_storage const& storage) noexcept
177  
from_sockaddr(sockaddr_storage const& storage) noexcept
176  
{
178  
{
177  
    if (storage.ss_family == AF_INET)
179  
    if (storage.ss_family == AF_INET)
178  
    {
180  
    {
179  
        sockaddr_in sa;
181  
        sockaddr_in sa;
180  
        std::memcpy(&sa, &storage, sizeof(sa));
182  
        std::memcpy(&sa, &storage, sizeof(sa));
181  
        return from_sockaddr_in(sa);
183  
        return from_sockaddr_in(sa);
182  
    }
184  
    }
183  
    if (storage.ss_family == AF_INET6)
185  
    if (storage.ss_family == AF_INET6)
184  
    {
186  
    {
185  
        sockaddr_in6 sa6;
187  
        sockaddr_in6 sa6;
186  
        std::memcpy(&sa6, &storage, sizeof(sa6));
188  
        std::memcpy(&sa6, &storage, sizeof(sa6));
187  
        return from_sockaddr_in6(sa6);
189  
        return from_sockaddr_in6(sa6);
188  
    }
190  
    }
189  
    return endpoint{};
191  
    return endpoint{};
190  
}
192  
}
191  

193  

192  
/** Return the native address family for an endpoint.
194  
/** Return the native address family for an endpoint.
193  

195  

194  
    @param ep The endpoint to query.
196  
    @param ep The endpoint to query.
195  
    @return `AF_INET` for IPv4, `AF_INET6` for IPv6.
197  
    @return `AF_INET` for IPv4, `AF_INET6` for IPv6.
196  
*/
198  
*/
197  
inline int
199  
inline int
198  
endpoint_family(endpoint const& ep) noexcept
200  
endpoint_family(endpoint const& ep) noexcept
199  
{
201  
{
200  
    return ep.is_v6() ? AF_INET6 : AF_INET;
202  
    return ep.is_v6() ? AF_INET6 : AF_INET;
201  
}
203  
}
202  

204  

203  
/** Return the address family of a socket descriptor.
205  
/** Return the address family of a socket descriptor.
204  

206  

205  
    @param fd The socket file descriptor.
207  
    @param fd The socket file descriptor.
206  
    @return AF_INET, AF_INET6, or AF_UNSPEC on failure.
208  
    @return AF_INET, AF_INET6, or AF_UNSPEC on failure.
207  
*/
209  
*/
208  
inline int
210  
inline int
209  
socket_family(
211  
socket_family(
210  
#if BOOST_COROSIO_POSIX
212  
#if BOOST_COROSIO_POSIX
211  
    int fd
213  
    int fd
212  
#else
214  
#else
213  
    std::uintptr_t fd
215  
    std::uintptr_t fd
214  
#endif
216  
#endif
215  
    ) noexcept
217  
    ) noexcept
216  
{
218  
{
217  
    sockaddr_storage storage{};
219  
    sockaddr_storage storage{};
218  
    socklen_t len = sizeof(storage);
220  
    socklen_t len = sizeof(storage);
219  
    if (getsockname(
221  
    if (getsockname(
220  
#if BOOST_COROSIO_POSIX
222  
#if BOOST_COROSIO_POSIX
221  
            fd,
223  
            fd,
222  
#else
224  
#else
223  
            static_cast<SOCKET>(fd),
225  
            static_cast<SOCKET>(fd),
224  
#endif
226  
#endif
225  
            reinterpret_cast<sockaddr*>(&storage), &len) != 0)
227  
            reinterpret_cast<sockaddr*>(&storage), &len) != 0)
226  
        return AF_UNSPEC;
228  
        return AF_UNSPEC;
227  
    return storage.ss_family;
229  
    return storage.ss_family;
228  
}
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 +
to_sockaddr(local_endpoint const& ep, sockaddr_storage& storage) noexcept
 
246 +
{
 
247 +
    std::memset(&storage, 0, sizeof(storage));
 
248 +
    sockaddr_un sa{};
 
249 +
    sa.sun_family = AF_UNIX;
 
250 +
    auto path     = ep.path();
 
251 +
    auto copy_len = (std::min)(path.size(), sizeof(sa.sun_path));
 
252 +
    if (copy_len > 0)
 
253 +
        std::memcpy(sa.sun_path, path.data(), copy_len);
 
254 +
    std::memcpy(&storage, &sa, sizeof(sa));
 
255 +

 
256 +
    if (ep.is_abstract())
 
257 +
        return static_cast<socklen_t>(
 
258 +
            offsetof(sockaddr_un, sun_path) + path.size());
 
259 +
    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 +
to_sockaddr(
 
274 +
    local_endpoint const& ep,
 
275 +
    int /*socket_family*/,
 
276 +
    sockaddr_storage& storage) noexcept
 
277 +
{
 
278 +
    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 +
from_sockaddr_local(
 
290 +
    sockaddr_storage const& storage, socklen_t len) noexcept
 
291 +
{
 
292 +
    if (storage.ss_family != AF_UNIX)
 
293 +
        return local_endpoint{};
 
294 +

 
295 +
    sockaddr_un sa{};
 
296 +
    std::memcpy(
 
297 +
        &sa, &storage,
 
298 +
        (std::min)(static_cast<std::size_t>(len), sizeof(sa)));
 
299 +

 
300 +
    auto path_offset = offsetof(sockaddr_un, sun_path);
 
301 +
    if (static_cast<std::size_t>(len) <= path_offset)
 
302 +
        return local_endpoint{};
 
303 +

 
304 +
    auto path_len = static_cast<std::size_t>(len) - path_offset;
 
305 +

 
306 +
    // Non-abstract paths may be null-terminated by the kernel
 
307 +
    if (path_len > 0 && sa.sun_path[0] != '\0')
 
308 +
    {
 
309 +
        auto* end = static_cast<char const*>(
 
310 +
            std::memchr(sa.sun_path, '\0', path_len));
 
311 +
        if (end)
 
312 +
            path_len = static_cast<std::size_t>(end - sa.sun_path);
 
313 +
    }
 
314 +

 
315 +
    std::error_code ec;
 
316 +
    local_endpoint ep(std::string_view(sa.sun_path, path_len), ec);
 
317 +
    if (ec)
 
318 +
        return local_endpoint{};
 
319 +
    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 +
from_sockaddr_as(
 
338 +
    sockaddr_storage const& storage, socklen_t /*len*/, endpoint const&) noexcept
 
339 +
{
 
340 +
    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 +
from_sockaddr_as(
 
353 +
    sockaddr_storage const& storage,
 
354 +
    socklen_t len,
 
355 +
    local_endpoint const&) noexcept
 
356 +
{
 
357 +
    return from_sockaddr_local(storage, len);
 
358 +
}
 
359 +

 
360 +
#endif // BOOST_COROSIO_POSIX
229  

361  

230  
} // namespace boost::corosio::detail
362  
} // namespace boost::corosio::detail
231  

363  

232  
#endif
364  
#endif