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: 99.0 % 101 100 1
Test Date: 2026-10-28 12:45:41 Functions: 100.0 % 16 16

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

Generated by: LCOV version 2.3