LCOV - code coverage report
Current view: top level - corosio/native/detail - endpoint_convert.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 95.9 % 97 93 4
Test Date: 2026-04-10 22:36:12 Functions: 100.0 % 14 14

           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_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/local_endpoint.hpp>
      15                 : #include <boost/corosio/detail/platform.hpp>
      16                 : 
      17                 : #include <cstring>
      18                 : 
      19                 : #if BOOST_COROSIO_POSIX
      20                 : #include <sys/socket.h>
      21                 : #include <sys/un.h>
      22                 : #include <netinet/in.h>
      23                 : #include <arpa/inet.h>
      24                 : #else
      25                 : #ifndef WIN32_LEAN_AND_MEAN
      26                 : #define WIN32_LEAN_AND_MEAN
      27                 : #endif
      28                 : #ifndef NOMINMAX
      29                 : #define NOMINMAX
      30                 : #endif
      31                 : #include <WinSock2.h>
      32                 : #include <Ws2tcpip.h>
      33                 : #endif
      34                 : 
      35                 : namespace boost::corosio::detail {
      36                 : 
      37                 : /** Convert IPv4 endpoint to sockaddr_in.
      38                 : 
      39                 :     @param ep The endpoint to convert. Must be IPv4 (is_v4() == true).
      40                 :     @return A sockaddr_in structure with fields in network byte order.
      41                 : */
      42                 : inline sockaddr_in
      43 HIT        8201 : to_sockaddr_in(endpoint const& ep) noexcept
      44                 : {
      45            8201 :     sockaddr_in sa{};
      46            8201 :     sa.sin_family = AF_INET;
      47            8201 :     sa.sin_port   = htons(ep.port());
      48            8201 :     auto bytes    = ep.v4_address().to_bytes();
      49            8201 :     std::memcpy(&sa.sin_addr, bytes.data(), 4);
      50            8201 :     return sa;
      51                 : }
      52                 : 
      53                 : /** Convert IPv6 endpoint to sockaddr_in6.
      54                 : 
      55                 :     @param ep The endpoint to convert. Must be IPv6 (is_v6() == true).
      56                 :     @return A sockaddr_in6 structure with fields in network byte order.
      57                 : */
      58                 : inline sockaddr_in6
      59              36 : to_sockaddr_in6(endpoint const& ep) noexcept
      60                 : {
      61              36 :     sockaddr_in6 sa{};
      62              36 :     sa.sin6_family = AF_INET6;
      63              36 :     sa.sin6_port   = htons(ep.port());
      64              36 :     auto bytes     = ep.v6_address().to_bytes();
      65              36 :     std::memcpy(&sa.sin6_addr, bytes.data(), 16);
      66              36 :     return sa;
      67                 : }
      68                 : 
      69                 : /** Create endpoint from sockaddr_in.
      70                 : 
      71                 :     @param sa The sockaddr_in structure with fields in network byte order.
      72                 :     @return An endpoint with address and port extracted from sa.
      73                 : */
      74                 : inline endpoint
      75           16167 : from_sockaddr_in(sockaddr_in const& sa) noexcept
      76                 : {
      77                 :     ipv4_address::bytes_type bytes;
      78           16167 :     std::memcpy(bytes.data(), &sa.sin_addr, 4);
      79           16167 :     return endpoint(ipv4_address(bytes), ntohs(sa.sin_port));
      80                 : }
      81                 : 
      82                 : /** Create endpoint from sockaddr_in6.
      83                 : 
      84                 :     @param sa The sockaddr_in6 structure with fields in network byte order.
      85                 :     @return An endpoint with address and port extracted from sa.
      86                 : */
      87                 : inline endpoint
      88              52 : from_sockaddr_in6(sockaddr_in6 const& sa) noexcept
      89                 : {
      90                 :     ipv6_address::bytes_type bytes;
      91              52 :     std::memcpy(bytes.data(), &sa.sin6_addr, 16);
      92              52 :     return endpoint(ipv6_address(bytes), ntohs(sa.sin6_port));
      93                 : }
      94                 : 
      95                 : /** Convert an IPv4 endpoint to an IPv4-mapped IPv6 sockaddr_in6.
      96                 : 
      97                 :     Produces a `sockaddr_in6` with the `::ffff:` prefix, suitable
      98                 :     for passing an IPv4 destination to a dual-stack IPv6 socket.
      99                 : 
     100                 :     @param ep The endpoint to convert. Must be IPv4 (is_v4() == true).
     101                 :     @return A sockaddr_in6 with the IPv4-mapped address.
     102                 : */
     103                 : inline sockaddr_in6
     104               2 : to_v4_mapped_sockaddr_in6(endpoint const& ep) noexcept
     105                 : {
     106               2 :     sockaddr_in6 sa{};
     107               2 :     sa.sin6_family = AF_INET6;
     108               2 :     sa.sin6_port   = htons(ep.port());
     109                 :     // ::ffff:0:0/96 prefix
     110               2 :     sa.sin6_addr.s6_addr[10] = 0xff;
     111               2 :     sa.sin6_addr.s6_addr[11] = 0xff;
     112               2 :     auto bytes               = ep.v4_address().to_bytes();
     113               2 :     std::memcpy(&sa.sin6_addr.s6_addr[12], bytes.data(), 4);
     114               2 :     return sa;
     115                 : }
     116                 : 
     117                 : /** Convert endpoint to sockaddr_storage.
     118                 : 
     119                 :     Dispatches to @ref to_sockaddr_in or @ref to_sockaddr_in6
     120                 :     based on the endpoint's address family.
     121                 : 
     122                 :     @param ep The endpoint to convert.
     123                 :     @param storage Output parameter filled with the sockaddr.
     124                 :     @return The length of the filled sockaddr structure.
     125                 : */
     126                 : inline socklen_t
     127            8227 : to_sockaddr(endpoint const& ep, sockaddr_storage& storage) noexcept
     128                 : {
     129            8227 :     std::memset(&storage, 0, sizeof(storage));
     130            8227 :     if (ep.is_v4())
     131                 :     {
     132            8193 :         auto sa = to_sockaddr_in(ep);
     133            8193 :         std::memcpy(&storage, &sa, sizeof(sa));
     134            8193 :         return sizeof(sa);
     135                 :     }
     136              34 :     auto sa6 = to_sockaddr_in6(ep);
     137              34 :     std::memcpy(&storage, &sa6, sizeof(sa6));
     138              34 :     return sizeof(sa6);
     139                 : }
     140                 : 
     141                 : /** Convert endpoint to sockaddr_storage for a specific socket family.
     142                 : 
     143                 :     When the socket is AF_INET6 and the endpoint is IPv4, the address
     144                 :     is converted to an IPv4-mapped IPv6 address (`::ffff:x.x.x.x`) so
     145                 :     dual-stack sockets can connect to IPv4 destinations.
     146                 : 
     147                 :     @param ep The endpoint to convert.
     148                 :     @param socket_family The address family of the socket (AF_INET or
     149                 :         AF_INET6).
     150                 :     @param storage Output parameter filled with the sockaddr.
     151                 :     @return The length of the filled sockaddr structure.
     152                 : */
     153                 : inline socklen_t
     154            8085 : to_sockaddr(
     155                 :     endpoint const& ep, int socket_family, sockaddr_storage& storage) noexcept
     156                 : {
     157                 :     // IPv4 endpoint on IPv6 socket: use IPv4-mapped address
     158            8085 :     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            8083 :     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           16206 : from_sockaddr(sockaddr_storage const& storage) noexcept
     178                 : {
     179           16206 :     if (storage.ss_family == AF_INET)
     180                 :     {
     181                 :         sockaddr_in sa;
     182           16156 :         std::memcpy(&sa, &storage, sizeof(sa));
     183           16156 :         return from_sockaddr_in(sa);
     184                 :     }
     185              50 :     if (storage.ss_family == AF_INET6)
     186                 :     {
     187                 :         sockaddr_in6 sa6;
     188              50 :         std::memcpy(&sa6, &storage, sizeof(sa6));
     189              50 :         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        8111 : socket_family(
     212                 : #if BOOST_COROSIO_POSIX
     213                 :     int fd
     214                 : #else
     215                 :     std::uintptr_t fd
     216                 : #endif
     217                 :     ) noexcept
     218                 : {
     219            8111 :     sockaddr_storage storage{};
     220            8111 :     socklen_t len = sizeof(storage);
     221            8111 :     if (getsockname(
     222                 : #if BOOST_COROSIO_POSIX
     223                 :             fd,
     224                 : #else
     225                 :             static_cast<SOCKET>(fd),
     226                 : #endif
     227            8111 :             reinterpret_cast<sockaddr*>(&storage), &len) != 0)
     228 MIS           0 :         return AF_UNSPEC;
     229 HIT        8111 :     return storage.ss_family;
     230                 : }
     231                 : 
     232                 : //----------------------------------------------------------
     233                 : // local_endpoint (AF_UNIX) conversions
     234                 : //----------------------------------------------------------
     235                 : 
     236                 : #if BOOST_COROSIO_POSIX
     237                 : 
     238                 : /** Convert a local_endpoint to sockaddr_storage.
     239                 : 
     240                 :     @param ep The local endpoint to convert.
     241                 :     @param storage Output parameter filled with the sockaddr_un.
     242                 :     @return The length of the filled sockaddr structure.
     243                 : */
     244                 : inline socklen_t
     245              38 : to_sockaddr(local_endpoint const& ep, sockaddr_storage& storage) noexcept
     246                 : {
     247              38 :     std::memset(&storage, 0, sizeof(storage));
     248              38 :     sockaddr_un sa{};
     249              38 :     sa.sun_family = AF_UNIX;
     250              38 :     auto path     = ep.path();
     251              38 :     auto copy_len = (std::min)(path.size(), sizeof(sa.sun_path));
     252              38 :     if (copy_len > 0)
     253              38 :         std::memcpy(sa.sun_path, path.data(), copy_len);
     254              38 :     std::memcpy(&storage, &sa, sizeof(sa));
     255                 : 
     256              38 :     if (ep.is_abstract())
     257                 :         return static_cast<socklen_t>(
     258               6 :             offsetof(sockaddr_un, sun_path) + path.size());
     259              32 :     return static_cast<socklen_t>(sizeof(sa));
     260                 : }
     261                 : 
     262                 : /** Convert a local_endpoint to sockaddr_storage (family-aware overload).
     263                 : 
     264                 :     The socket_family parameter is ignored for Unix sockets since
     265                 :     there is no dual-stack mapping.
     266                 : 
     267                 :     @param ep The local endpoint to convert.
     268                 :     @param socket_family Ignored.
     269                 :     @param storage Output parameter filled with the sockaddr_un.
     270                 :     @return The length of the filled sockaddr structure.
     271                 : */
     272                 : inline socklen_t
     273              26 : to_sockaddr(
     274                 :     local_endpoint const& ep,
     275                 :     int /*socket_family*/,
     276                 :     sockaddr_storage& storage) noexcept
     277                 : {
     278              26 :     return to_sockaddr(ep, storage);
     279                 : }
     280                 : 
     281                 : /** Create a local_endpoint from sockaddr_storage.
     282                 : 
     283                 :     @param storage The sockaddr_storage (must have ss_family == AF_UNIX).
     284                 :     @param len The address length returned by the kernel.
     285                 :     @return A local_endpoint with the path extracted from the
     286                 :         sockaddr_un, or an empty endpoint if the family is not AF_UNIX.
     287                 : */
     288                 : inline local_endpoint
     289             108 : from_sockaddr_local(
     290                 :     sockaddr_storage const& storage, socklen_t len) noexcept
     291                 : {
     292             108 :     if (storage.ss_family != AF_UNIX)
     293 MIS           0 :         return local_endpoint{};
     294                 : 
     295 HIT         108 :     sockaddr_un sa{};
     296                 :     auto bytes_copied =
     297             108 :         (std::min)(static_cast<std::size_t>(len), sizeof(sa));
     298             108 :     std::memcpy(&sa, &storage, bytes_copied);
     299                 : 
     300                 :     // Derive path_len from bytes_copied (NOT len) so memchr and the
     301                 :     // string_view below can never read past sa.sun_path. The kernel
     302                 :     // can return an addrlen larger than sizeof(sockaddr_un) (e.g.,
     303                 :     // sizeof(sockaddr_storage) from a misbehaving caller); without
     304                 :     // clamping, the OOB read could expose adjacent stack bytes.
     305             108 :     auto path_offset = offsetof(sockaddr_un, sun_path);
     306             108 :     if (bytes_copied <= path_offset)
     307              76 :         return local_endpoint{};
     308                 : 
     309              32 :     auto path_len = bytes_copied - path_offset;
     310                 : 
     311                 :     // Non-abstract paths may be null-terminated by the kernel
     312              32 :     if (path_len > 0 && sa.sun_path[0] != '\0')
     313                 :     {
     314                 :         auto* end = static_cast<char const*>(
     315              26 :             std::memchr(sa.sun_path, '\0', path_len));
     316              26 :         if (end)
     317              26 :             path_len = static_cast<std::size_t>(end - sa.sun_path);
     318                 :     }
     319                 : 
     320              32 :     std::error_code ec;
     321              32 :     local_endpoint ep(std::string_view(sa.sun_path, path_len), ec);
     322              32 :     if (ec)
     323 MIS           0 :         return local_endpoint{};
     324 HIT          32 :     return ep;
     325                 : }
     326                 : 
     327                 : #endif // BOOST_COROSIO_POSIX
     328                 : 
     329                 : //----------------------------------------------------------
     330                 : // Tag-dispatch helpers for templatized reactor code.
     331                 : // Overload resolution selects the correct conversion based
     332                 : // on the Endpoint type.
     333                 : //----------------------------------------------------------
     334                 : 
     335                 : /** Convert sockaddr_storage to an IP endpoint (tag overload).
     336                 : 
     337                 :     @param storage The sockaddr_storage with fields in network byte order.
     338                 :     @param len The address length returned by the kernel.
     339                 :     @return An endpoint with address and port extracted from storage.
     340                 : */
     341                 : inline endpoint
     342           16206 : from_sockaddr_as(
     343                 :     sockaddr_storage const& storage, socklen_t /*len*/, endpoint const&) noexcept
     344                 : {
     345           16206 :     return from_sockaddr(storage);
     346                 : }
     347                 : 
     348                 : #if BOOST_COROSIO_POSIX
     349                 : 
     350                 : /** Convert sockaddr_storage to a local_endpoint (tag overload).
     351                 : 
     352                 :     @param storage The sockaddr_storage.
     353                 :     @param len The address length returned by the kernel.
     354                 :     @return A local_endpoint with path extracted from storage.
     355                 : */
     356                 : inline local_endpoint
     357             108 : from_sockaddr_as(
     358                 :     sockaddr_storage const& storage,
     359                 :     socklen_t len,
     360                 :     local_endpoint const&) noexcept
     361                 : {
     362             108 :     return from_sockaddr_local(storage, len);
     363                 : }
     364                 : 
     365                 : #endif // BOOST_COROSIO_POSIX
     366                 : 
     367                 : } // namespace boost::corosio::detail
     368                 : 
     369                 : #endif
        

Generated by: LCOV version 2.3