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

Generated by: LCOV version 2.3