1 -
//
 
2 -
// Copyright (c) 2026 Vinnie Falco (vinnie.falco@gmail.com)
 
3 -
//
 
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)
 
6 -
//
 
7 -
// Official repository: https://github.com/cppalliance/corosio
 
8 -
//
 
9 -

 
10 -
#ifndef BOOST_COROSIO_DETAIL_ENDPOINT_CONVERT_HPP
 
11 -
#define BOOST_COROSIO_DETAIL_ENDPOINT_CONVERT_HPP
 
12 -

 
13 -
#include <boost/corosio/endpoint.hpp>
 
14 -
#include <boost/corosio/detail/platform.hpp>
 
15 -

 
16 -
#include <cstring>
 
17 -

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

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

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

 
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.
 
39 -
*/
 
40 -
inline sockaddr_in
 
41 -
to_sockaddr_in(endpoint const& ep) noexcept
 
42 -
{
 
43 -
    sockaddr_in sa{};
 
44 -
    sa.sin_family = AF_INET;
 
45 -
    sa.sin_port   = htons(ep.port());
 
46 -
    auto bytes    = ep.v4_address().to_bytes();
 
47 -
    std::memcpy(&sa.sin_addr, bytes.data(), 4);
 
48 -
    return sa;
 
49 -
}
 
50 -

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

 
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.
 
55 -
*/
 
56 -
inline sockaddr_in6
 
57 -
to_sockaddr_in6(endpoint const& ep) noexcept
 
58 -
{
 
59 -
    sockaddr_in6 sa{};
 
60 -
    sa.sin6_family = AF_INET6;
 
61 -
    sa.sin6_port   = htons(ep.port());
 
62 -
    auto bytes     = ep.v6_address().to_bytes();
 
63 -
    std::memcpy(&sa.sin6_addr, bytes.data(), 16);
 
64 -
    return sa;
 
65 -
}
 
66 -

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

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

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

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

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

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

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

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

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

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

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

 
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
 
143 -
    dual-stack sockets can connect to IPv4 destinations.
 
144 -

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

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

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

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

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

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

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

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

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

 
234 -
#endif