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

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

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

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

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

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

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

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

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

 
232 +
#endif