TLA Line data 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_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 HIT 7814 : to_sockaddr_in(endpoint const& ep) noexcept
42 : {
43 7814 : sockaddr_in sa{};
44 7814 : sa.sin_family = AF_INET;
45 7814 : sa.sin_port = htons(ep.port());
46 7814 : auto bytes = ep.v4_address().to_bytes();
47 7814 : std::memcpy(&sa.sin_addr, bytes.data(), 4);
48 7814 : 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 18551 : from_sockaddr_in(sockaddr_in const& sa) noexcept
74 : {
75 : ipv4_address::bytes_type bytes;
76 18551 : std::memcpy(bytes.data(), &sa.sin_addr, 4);
77 18551 : 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 7828 : to_sockaddr( endpoint const& ep, sockaddr_storage& storage ) noexcept
126 : {
127 7828 : std::memset( &storage, 0, sizeof( storage ) );
128 7828 : if( ep.is_v4() )
129 : {
130 7806 : auto sa = to_sockaddr_in( ep );
131 7806 : std::memcpy( &storage, &sa, sizeof( sa ) );
132 7806 : 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 7692 : 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 7692 : if (ep.is_v4() && socket_family == AF_INET6)
159 : {
160 2 : std::memset(&storage, 0, sizeof(storage));
161 2 : auto sa6 = to_v4_mapped_sockaddr_in6(ep);
162 2 : std::memcpy(&storage, &sa6, sizeof(sa6));
163 2 : return sizeof(sa6);
164 : }
165 7690 : 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 18579 : from_sockaddr( sockaddr_storage const& storage ) noexcept
178 : {
179 18579 : if( storage.ss_family == AF_INET )
180 : {
181 : sockaddr_in sa;
182 18540 : std::memcpy( &sa, &storage, sizeof( sa ) );
183 18540 : return from_sockaddr_in( sa );
184 : }
185 39 : if( storage.ss_family == AF_INET6 )
186 : {
187 : sockaddr_in6 sa6;
188 39 : std::memcpy( &sa6, &storage, sizeof( sa6 ) );
189 39 : return from_sockaddr_in6( sa6 );
190 : }
191 MIS 0 : 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 HIT 7692 : socket_family(
212 : #if BOOST_COROSIO_POSIX
213 : int fd
214 : #else
215 : std::uintptr_t fd
216 : #endif
217 : ) noexcept
218 : {
219 7692 : sockaddr_storage storage{};
220 7692 : socklen_t len = sizeof(storage);
221 7692 : if (getsockname(
222 : #if BOOST_COROSIO_POSIX
223 : fd,
224 : #else
225 : static_cast<SOCKET>(fd),
226 : #endif
227 7692 : reinterpret_cast<sockaddr*>(&storage), &len) != 0)
228 MIS 0 : return AF_UNSPEC;
229 HIT 7692 : return storage.ss_family;
230 : }
231 :
232 : } // namespace boost::corosio::detail
233 :
234 : #endif
|