include/boost/corosio/native/detail/endpoint_convert.hpp

96.7% Lines (58/60) 100.0% Functions (9/9)
include/boost/corosio/native/detail/endpoint_convert.hpp
Line TLA Hits Source Code
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 8355 to_sockaddr_in(endpoint const& ep) noexcept
42 {
43 8355 sockaddr_in sa{};
44 8355 sa.sin_family = AF_INET;
45 8355 sa.sin_port = htons(ep.port());
46 8355 auto bytes = ep.v4_address().to_bytes();
47 8355 std::memcpy(&sa.sin_addr, bytes.data(), 4);
48 8355 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 24 to_sockaddr_in6(endpoint const& ep) noexcept
58 {
59 24 sockaddr_in6 sa{};
60 24 sa.sin6_family = AF_INET6;
61 24 sa.sin6_port = htons(ep.port());
62 24 auto bytes = ep.v6_address().to_bytes();
63 24 std::memcpy(&sa.sin6_addr, bytes.data(), 16);
64 24 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 20057 from_sockaddr_in(sockaddr_in const& sa) noexcept
74 {
75 ipv4_address::bytes_type bytes;
76 20057 std::memcpy(bytes.data(), &sa.sin_addr, 4);
77 20057 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 41 from_sockaddr_in6(sockaddr_in6 const& sa) noexcept
87 {
88 ipv6_address::bytes_type bytes;
89 41 std::memcpy(bytes.data(), &sa.sin6_addr, 16);
90 41 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 2 to_v4_mapped_sockaddr_in6(endpoint const& ep) noexcept
103 {
104 2 sockaddr_in6 sa{};
105 2 sa.sin6_family = AF_INET6;
106 2 sa.sin6_port = htons(ep.port());
107 // ::ffff:0:0/96 prefix
108 2 sa.sin6_addr.s6_addr[10] = 0xff;
109 2 sa.sin6_addr.s6_addr[11] = 0xff;
110 2 auto bytes = ep.v4_address().to_bytes();
111 2 std::memcpy(&sa.sin6_addr.s6_addr[12], bytes.data(), 4);
112 2 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 8369 to_sockaddr(endpoint const& ep, sockaddr_storage& storage) noexcept
126 {
127 8369 std::memset(&storage, 0, sizeof(storage));
128 8369 if (ep.is_v4())
129 {
130 8347 auto sa = to_sockaddr_in(ep);
131 8347 std::memcpy(&storage, &sa, sizeof(sa));
132 8347 return sizeof(sa);
133 }
134 22 auto sa6 = to_sockaddr_in6(ep);
135 22 std::memcpy(&storage, &sa6, sizeof(sa6));
136 22 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 8233 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 8233 if (ep.is_v4() && socket_family == AF_INET6)
157 {
158 2 std::memset(&storage, 0, sizeof(storage));
159 2 auto sa6 = to_v4_mapped_sockaddr_in6(ep);
160 2 std::memcpy(&storage, &sa6, sizeof(sa6));
161 2 return sizeof(sa6);
162 }
163 8231 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 20085 from_sockaddr(sockaddr_storage const& storage) noexcept
176 {
177 20085 if (storage.ss_family == AF_INET)
178 {
179 sockaddr_in sa;
180 20046 std::memcpy(&sa, &storage, sizeof(sa));
181 20046 return from_sockaddr_in(sa);
182 }
183 39 if (storage.ss_family == AF_INET6)
184 {
185 sockaddr_in6 sa6;
186 39 std::memcpy(&sa6, &storage, sizeof(sa6));
187 39 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 8233 socket_family(
210 #if BOOST_COROSIO_POSIX
211 int fd
212 #else
213 std::uintptr_t fd
214 #endif
215 ) noexcept
216 {
217 8233 sockaddr_storage storage{};
218 8233 socklen_t len = sizeof(storage);
219 8233 if (getsockname(
220 #if BOOST_COROSIO_POSIX
221 fd,
222 #else
223 static_cast<SOCKET>(fd),
224 #endif
225 8233 reinterpret_cast<sockaddr*>(&storage), &len) != 0)
226 return AF_UNSPEC;
227 8233 return storage.ss_family;
228 }
229
230 } // namespace boost::corosio::detail
231
232 #endif
233