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: 96.9 % 97 94 3
Test Date: 2026-04-13 22:45:57 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                 : #include <cstddef> // offsetof
      36                 : 
      37                 : #ifndef AF_UNIX
      38                 : #define AF_UNIX 1
      39                 : #endif
      40                 : 
      41                 : namespace boost::corosio::detail {
      42                 : 
      43                 : /** Convert IPv4 endpoint to sockaddr_in.
      44                 : 
      45                 :     @param ep The endpoint to convert. Must be IPv4 (is_v4() == true).
      46                 :     @return A sockaddr_in structure with fields in network byte order.
      47                 : */
      48                 : inline sockaddr_in
      49 HIT        8323 : to_sockaddr_in(endpoint const& ep) noexcept
      50                 : {
      51            8323 :     sockaddr_in sa{};
      52            8323 :     sa.sin_family = AF_INET;
      53            8323 :     sa.sin_port   = htons(ep.port());
      54            8323 :     auto bytes    = ep.v4_address().to_bytes();
      55            8323 :     std::memcpy(&sa.sin_addr, bytes.data(), 4);
      56            8323 :     return sa;
      57                 : }
      58                 : 
      59                 : /** Convert IPv6 endpoint to sockaddr_in6.
      60                 : 
      61                 :     @param ep The endpoint to convert. Must be IPv6 (is_v6() == true).
      62                 :     @return A sockaddr_in6 structure with fields in network byte order.
      63                 : */
      64                 : inline sockaddr_in6
      65              36 : to_sockaddr_in6(endpoint const& ep) noexcept
      66                 : {
      67              36 :     sockaddr_in6 sa{};
      68              36 :     sa.sin6_family = AF_INET6;
      69              36 :     sa.sin6_port   = htons(ep.port());
      70              36 :     auto bytes     = ep.v6_address().to_bytes();
      71              36 :     std::memcpy(&sa.sin6_addr, bytes.data(), 16);
      72              36 :     return sa;
      73                 : }
      74                 : 
      75                 : /** Create endpoint from sockaddr_in.
      76                 : 
      77                 :     @param sa The sockaddr_in structure with fields in network byte order.
      78                 :     @return An endpoint with address and port extracted from sa.
      79                 : */
      80                 : inline endpoint
      81           16415 : from_sockaddr_in(sockaddr_in const& sa) noexcept
      82                 : {
      83                 :     ipv4_address::bytes_type bytes;
      84           16415 :     std::memcpy(bytes.data(), &sa.sin_addr, 4);
      85           16415 :     return endpoint(ipv4_address(bytes), ntohs(sa.sin_port));
      86                 : }
      87                 : 
      88                 : /** Create endpoint from sockaddr_in6.
      89                 : 
      90                 :     @param sa The sockaddr_in6 structure with fields in network byte order.
      91                 :     @return An endpoint with address and port extracted from sa.
      92                 : */
      93                 : inline endpoint
      94              52 : from_sockaddr_in6(sockaddr_in6 const& sa) noexcept
      95                 : {
      96                 :     ipv6_address::bytes_type bytes;
      97              52 :     std::memcpy(bytes.data(), &sa.sin6_addr, 16);
      98              52 :     return endpoint(ipv6_address(bytes), ntohs(sa.sin6_port));
      99                 : }
     100                 : 
     101                 : /** Convert an IPv4 endpoint to an IPv4-mapped IPv6 sockaddr_in6.
     102                 : 
     103                 :     Produces a `sockaddr_in6` with the `::ffff:` prefix, suitable
     104                 :     for passing an IPv4 destination to a dual-stack IPv6 socket.
     105                 : 
     106                 :     @param ep The endpoint to convert. Must be IPv4 (is_v4() == true).
     107                 :     @return A sockaddr_in6 with the IPv4-mapped address.
     108                 : */
     109                 : inline sockaddr_in6
     110               2 : to_v4_mapped_sockaddr_in6(endpoint const& ep) noexcept
     111                 : {
     112               2 :     sockaddr_in6 sa{};
     113               2 :     sa.sin6_family = AF_INET6;
     114               2 :     sa.sin6_port   = htons(ep.port());
     115                 :     // ::ffff:0:0/96 prefix
     116               2 :     sa.sin6_addr.s6_addr[10] = 0xff;
     117               2 :     sa.sin6_addr.s6_addr[11] = 0xff;
     118               2 :     auto bytes               = ep.v4_address().to_bytes();
     119               2 :     std::memcpy(&sa.sin6_addr.s6_addr[12], bytes.data(), 4);
     120               2 :     return sa;
     121                 : }
     122                 : 
     123                 : /** Convert endpoint to sockaddr_storage.
     124                 : 
     125                 :     Dispatches to @ref to_sockaddr_in or @ref to_sockaddr_in6
     126                 :     based on the endpoint's address family.
     127                 : 
     128                 :     @param ep The endpoint to convert.
     129                 :     @param storage Output parameter filled with the sockaddr.
     130                 :     @return The length of the filled sockaddr structure.
     131                 : */
     132                 : inline socklen_t
     133            8349 : to_sockaddr(endpoint const& ep, sockaddr_storage& storage) noexcept
     134                 : {
     135            8349 :     std::memset(&storage, 0, sizeof(storage));
     136            8349 :     if (ep.is_v4())
     137                 :     {
     138            8315 :         auto sa = to_sockaddr_in(ep);
     139            8315 :         std::memcpy(&storage, &sa, sizeof(sa));
     140            8315 :         return sizeof(sa);
     141                 :     }
     142              34 :     auto sa6 = to_sockaddr_in6(ep);
     143              34 :     std::memcpy(&storage, &sa6, sizeof(sa6));
     144              34 :     return sizeof(sa6);
     145                 : }
     146                 : 
     147                 : /** Convert endpoint to sockaddr_storage for a specific socket family.
     148                 : 
     149                 :     When the socket is AF_INET6 and the endpoint is IPv4, the address
     150                 :     is converted to an IPv4-mapped IPv6 address (`::ffff:x.x.x.x`) so
     151                 :     dual-stack sockets can connect to IPv4 destinations.
     152                 : 
     153                 :     @param ep The endpoint to convert.
     154                 :     @param socket_family The address family of the socket (AF_INET or
     155                 :         AF_INET6).
     156                 :     @param storage Output parameter filled with the sockaddr.
     157                 :     @return The length of the filled sockaddr structure.
     158                 : */
     159                 : inline socklen_t
     160            8209 : to_sockaddr(
     161                 :     endpoint const& ep, int socket_family, sockaddr_storage& storage) noexcept
     162                 : {
     163                 :     // IPv4 endpoint on IPv6 socket: use IPv4-mapped address
     164            8209 :     if (ep.is_v4() && socket_family == AF_INET6)
     165                 :     {
     166               2 :         std::memset(&storage, 0, sizeof(storage));
     167               2 :         auto sa6 = to_v4_mapped_sockaddr_in6(ep);
     168               2 :         std::memcpy(&storage, &sa6, sizeof(sa6));
     169               2 :         return sizeof(sa6);
     170                 :     }
     171            8207 :     return to_sockaddr(ep, storage);
     172                 : }
     173                 : 
     174                 : /** Create endpoint from sockaddr_storage.
     175                 : 
     176                 :     Dispatches on `ss_family` to reconstruct the appropriate
     177                 :     IPv4 or IPv6 endpoint.
     178                 : 
     179                 :     @param storage The sockaddr_storage with fields in network byte order.
     180                 :     @return An endpoint with address and port extracted from storage.
     181                 : */
     182                 : inline endpoint
     183           16454 : from_sockaddr(sockaddr_storage const& storage) noexcept
     184                 : {
     185           16454 :     if (storage.ss_family == AF_INET)
     186                 :     {
     187                 :         sockaddr_in sa;
     188           16404 :         std::memcpy(&sa, &storage, sizeof(sa));
     189           16404 :         return from_sockaddr_in(sa);
     190                 :     }
     191              50 :     if (storage.ss_family == AF_INET6)
     192                 :     {
     193                 :         sockaddr_in6 sa6;
     194              50 :         std::memcpy(&sa6, &storage, sizeof(sa6));
     195              50 :         return from_sockaddr_in6(sa6);
     196                 :     }
     197 MIS           0 :     return endpoint{};
     198                 : }
     199                 : 
     200                 : /** Return the native address family for an endpoint.
     201                 : 
     202                 :     @param ep The endpoint to query.
     203                 :     @return `AF_INET` for IPv4, `AF_INET6` for IPv6.
     204                 : */
     205                 : inline int
     206                 : endpoint_family(endpoint const& ep) noexcept
     207                 : {
     208                 :     return ep.is_v6() ? AF_INET6 : AF_INET;
     209                 : }
     210                 : 
     211                 : /** Return the address family of a socket descriptor.
     212                 : 
     213                 :     @param fd The socket file descriptor.
     214                 :     @return AF_INET, AF_INET6, or AF_UNSPEC on failure.
     215                 : */
     216                 : inline int
     217 HIT        8235 : socket_family(
     218                 : #if BOOST_COROSIO_POSIX
     219                 :     int fd
     220                 : #else
     221                 :     std::uintptr_t fd
     222                 : #endif
     223                 :     ) noexcept
     224                 : {
     225            8235 :     sockaddr_storage storage{};
     226            8235 :     socklen_t len = sizeof(storage);
     227            8235 :     if (getsockname(
     228                 : #if BOOST_COROSIO_POSIX
     229                 :             fd,
     230                 : #else
     231                 :             static_cast<SOCKET>(fd),
     232                 : #endif
     233            8235 :             reinterpret_cast<sockaddr*>(&storage), &len) != 0)
     234 MIS           0 :         return AF_UNSPEC;
     235 HIT        8235 :     return storage.ss_family;
     236                 : }
     237                 : 
     238                 : //----------------------------------------------------------
     239                 : // local_endpoint (AF_UNIX) conversions
     240                 : //----------------------------------------------------------
     241                 : 
     242                 : // Platform-agnostic sockaddr_un alias.  POSIX uses the real
     243                 : // sockaddr_un from <sys/un.h>; Windows uses a private struct
     244                 : // matching the layout (same approach as Boost.Asio).
     245                 : #if BOOST_COROSIO_POSIX
     246                 : using un_sa_t = sockaddr_un;
     247                 : #else
     248                 : struct un_sa_t { u_short sun_family; char sun_path[108]; };
     249                 : #endif
     250                 : 
     251                 : /** Convert a local_endpoint to sockaddr_storage.
     252                 : 
     253                 :     @param ep The local endpoint to convert.
     254                 :     @param storage Output parameter filled with the sockaddr_un.
     255                 :     @return The length of the filled sockaddr structure.
     256                 : */
     257                 : inline socklen_t
     258              38 : to_sockaddr(local_endpoint const& ep, sockaddr_storage& storage) noexcept
     259                 : {
     260              38 :     std::memset(&storage, 0, sizeof(storage));
     261              38 :     un_sa_t sa{};
     262              38 :     sa.sun_family = AF_UNIX;
     263              38 :     auto path     = ep.path();
     264              38 :     auto copy_len = (std::min)(path.size(), sizeof(sa.sun_path));
     265              38 :     if (copy_len > 0)
     266              38 :         std::memcpy(sa.sun_path, path.data(), copy_len);
     267              38 :     std::memcpy(&storage, &sa, sizeof(sa));
     268                 : 
     269              38 :     if (ep.is_abstract())
     270                 :         return static_cast<socklen_t>(
     271               6 :             offsetof(un_sa_t, sun_path) + copy_len);
     272              32 :     return static_cast<socklen_t>(sizeof(sa));
     273                 : }
     274                 : 
     275                 : /** Convert a local_endpoint to sockaddr_storage (family-aware overload).
     276                 : 
     277                 :     The socket_family parameter is ignored for Unix sockets since
     278                 :     there is no dual-stack mapping.
     279                 : 
     280                 :     @param ep The local endpoint to convert.
     281                 :     @param socket_family Ignored.
     282                 :     @param storage Output parameter filled with the sockaddr_un.
     283                 :     @return The length of the filled sockaddr structure.
     284                 : */
     285                 : inline socklen_t
     286              26 : to_sockaddr(
     287                 :     local_endpoint const& ep,
     288                 :     int /*socket_family*/,
     289                 :     sockaddr_storage& storage) noexcept
     290                 : {
     291              26 :     return to_sockaddr(ep, storage);
     292                 : }
     293                 : 
     294                 : /** Create a local_endpoint from sockaddr_storage.
     295                 : 
     296                 :     @param storage The sockaddr_storage (must have ss_family == AF_UNIX).
     297                 :     @param len The address length returned by the kernel.
     298                 :     @return A local_endpoint with the path extracted from the
     299                 :         sockaddr_un, or an empty endpoint if the family is not AF_UNIX.
     300                 : */
     301                 : inline local_endpoint
     302              44 : from_sockaddr_local(
     303                 :     sockaddr_storage const& storage, socklen_t len) noexcept
     304                 : {
     305              44 :     if (storage.ss_family != AF_UNIX)
     306 MIS           0 :         return local_endpoint{};
     307                 : 
     308 HIT          44 :     un_sa_t sa{};
     309              44 :     std::memcpy(
     310                 :         &sa, &storage,
     311              44 :         (std::min)(static_cast<std::size_t>(len), sizeof(sa)));
     312                 : 
     313              44 :     auto path_offset = offsetof(un_sa_t, sun_path);
     314              44 :     if (static_cast<std::size_t>(len) <= path_offset)
     315               8 :         return local_endpoint{};
     316                 : 
     317              36 :     auto path_len = static_cast<std::size_t>(len) - path_offset;
     318                 : 
     319                 :     // Non-abstract paths may be null-terminated by the kernel
     320              36 :     if (path_len > 0 && sa.sun_path[0] != '\0')
     321                 :     {
     322                 :         auto* end = static_cast<char const*>(
     323              26 :             std::memchr(sa.sun_path, '\0', path_len));
     324              26 :         if (end)
     325              26 :             path_len = static_cast<std::size_t>(end - sa.sun_path);
     326                 :     }
     327                 : 
     328              36 :     std::error_code ec;
     329              36 :     local_endpoint ep(std::string_view(sa.sun_path, path_len), ec);
     330              36 :     if (ec)
     331               4 :         return local_endpoint{};
     332              32 :     return ep;
     333                 : }
     334                 : 
     335                 : //----------------------------------------------------------
     336                 : // Tag-dispatch helpers for templatized reactor code.
     337                 : // Overload resolution selects the correct conversion based
     338                 : // on the Endpoint type.
     339                 : //----------------------------------------------------------
     340                 : 
     341                 : /** Convert sockaddr_storage to an IP endpoint (tag overload).
     342                 : 
     343                 :     @param storage The sockaddr_storage with fields in network byte order.
     344                 :     @param len The address length returned by the kernel.
     345                 :     @return An endpoint with address and port extracted from storage.
     346                 : */
     347                 : inline endpoint
     348           16454 : from_sockaddr_as(
     349                 :     sockaddr_storage const& storage, socklen_t /*len*/, endpoint const&) noexcept
     350                 : {
     351           16454 :     return from_sockaddr(storage);
     352                 : }
     353                 : 
     354                 : /** Convert sockaddr_storage to a local_endpoint (tag overload).
     355                 : 
     356                 :     @param storage The sockaddr_storage.
     357                 :     @param len The address length returned by the kernel.
     358                 :     @return A local_endpoint with path extracted from storage.
     359                 : */
     360                 : inline local_endpoint
     361              44 : from_sockaddr_as(
     362                 :     sockaddr_storage const& storage,
     363                 :     socklen_t len,
     364                 :     local_endpoint const&) noexcept
     365                 : {
     366              44 :     return from_sockaddr_local(storage, len);
     367                 : }
     368                 : 
     369                 : } // namespace boost::corosio::detail
     370                 : 
     371                 : #endif
        

Generated by: LCOV version 2.3