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 -
/** Convert an IPv4 endpoint to an IPv4-mapped IPv6 sockaddr_in6.
 
93 -

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

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

 
114 -
/** Convert endpoint to sockaddr_storage.
 
115 -

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

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

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

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

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

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

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

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

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

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

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

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

 
231  

92  

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

94  

234  
#endif
95  
#endif