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_DETAIL_ENDPOINT_CONVERT_HPP
10  
#ifndef BOOST_COROSIO_DETAIL_ENDPOINT_CONVERT_HPP
11  
#define BOOST_COROSIO_DETAIL_ENDPOINT_CONVERT_HPP
11  
#define BOOST_COROSIO_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,
153  
    endpoint const& ep,
154  
    int socket_family,
154  
    int socket_family,
155  
    sockaddr_storage& storage) noexcept
155  
    sockaddr_storage& storage) noexcept
156  
{
156  
{
157  
    // IPv4 endpoint on IPv6 socket: use IPv4-mapped address
157  
    // IPv4 endpoint on IPv6 socket: use IPv4-mapped address
158  
    if (ep.is_v4() && socket_family == AF_INET6)
158  
    if (ep.is_v4() && socket_family == AF_INET6)
159  
    {
159  
    {
160  
        std::memset(&storage, 0, sizeof(storage));
160  
        std::memset(&storage, 0, sizeof(storage));
161  
        auto sa6 = to_v4_mapped_sockaddr_in6(ep);
161  
        auto sa6 = to_v4_mapped_sockaddr_in6(ep);
162  
        std::memcpy(&storage, &sa6, sizeof(sa6));
162  
        std::memcpy(&storage, &sa6, sizeof(sa6));
163  
        return sizeof(sa6);
163  
        return sizeof(sa6);
164  
    }
164  
    }
165  
    return to_sockaddr(ep, storage);
165  
    return to_sockaddr(ep, storage);
166  
}
166  
}
167  

167  

168  
/** Create endpoint from sockaddr_storage.
168  
/** Create endpoint from sockaddr_storage.
169  

169  

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

172  

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

193  

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

195  

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

204  

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

206  

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

231  

232  
} // namespace boost::corosio::detail
232  
} // namespace boost::corosio::detail
233  

233  

234  
#endif
234  
#endif