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

15  

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

17  

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

32  

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

34  

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

36  

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

50  

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

52  

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

66  

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

68  

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

79  

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

81  

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

92  

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

94  

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

97  

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

114  

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

116  

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

119  

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

138  

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

140  

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

144  

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

165  

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

167  

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

170  

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

191  

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

193  

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

202  

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

204  

205  
    @param fd The socket file descriptor.
205  
    @param fd The socket file descriptor.
206  
    @return AF_INET, AF_INET6, or AF_UNSPEC on failure.
206  
    @return AF_INET, AF_INET6, or AF_UNSPEC on failure.
207  
*/
207  
*/
208  
inline int
208  
inline int
209  
socket_family(
209  
socket_family(
210  
#if BOOST_COROSIO_POSIX
210  
#if BOOST_COROSIO_POSIX
211  
    int fd
211  
    int fd
212  
#else
212  
#else
213  
    std::uintptr_t fd
213  
    std::uintptr_t fd
214  
#endif
214  
#endif
215  
    ) noexcept
215  
    ) noexcept
216  
{
216  
{
217  
    sockaddr_storage storage{};
217  
    sockaddr_storage storage{};
218  
    socklen_t len = sizeof(storage);
218  
    socklen_t len = sizeof(storage);
219  
    if (getsockname(
219  
    if (getsockname(
220  
#if BOOST_COROSIO_POSIX
220  
#if BOOST_COROSIO_POSIX
221  
            fd,
221  
            fd,
222  
#else
222  
#else
223  
            static_cast<SOCKET>(fd),
223  
            static_cast<SOCKET>(fd),
224  
#endif
224  
#endif
225  
            reinterpret_cast<sockaddr*>(&storage), &len) != 0)
225  
            reinterpret_cast<sockaddr*>(&storage), &len) != 0)
226  
        return AF_UNSPEC;
226  
        return AF_UNSPEC;
227  
    return storage.ss_family;
227  
    return storage.ss_family;
228  
}
228  
}
229  

229  

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

231  

232  
#endif
232  
#endif