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-09 23:21:11 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        8728 : to_sockaddr_in(endpoint const& ep) noexcept
      44                 : {
      45            8728 :     sockaddr_in sa{};
      46            8728 :     sa.sin_family = AF_INET;
      47            8728 :     sa.sin_port   = htons(ep.port());
      48            8728 :     auto bytes    = ep.v4_address().to_bytes();
      49            8728 :     std::memcpy(&sa.sin_addr, bytes.data(), 4);
      50            8728 :     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           17225 : from_sockaddr_in(sockaddr_in const& sa) noexcept
      76                 : {
      77                 :     ipv4_address::bytes_type bytes;
      78           17225 :     std::memcpy(bytes.data(), &sa.sin_addr, 4);
      79           17225 :     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            8754 : to_sockaddr(endpoint const& ep, sockaddr_storage& storage) noexcept
     128                 : {
     129            8754 :     std::memset(&storage, 0, sizeof(storage));
     130            8754 :     if (ep.is_v4())
     131                 :     {
     132            8720 :         auto sa = to_sockaddr_in(ep);
     133            8720 :         std::memcpy(&storage, &sa, sizeof(sa));
     134            8720 :         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            8612 : 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            8612 :     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            8610 :     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           17264 : from_sockaddr(sockaddr_storage const& storage) noexcept
     178                 : {
     179           17264 :     if (storage.ss_family == AF_INET)
     180                 :     {
     181                 :         sockaddr_in sa;
     182           17214 :         std::memcpy(&sa, &storage, sizeof(sa));
     183           17214 :         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        8638 : socket_family(
     212                 : #if BOOST_COROSIO_POSIX
     213                 :     int fd
     214                 : #else
     215                 :     std::uintptr_t fd
     216                 : #endif
     217                 :     ) noexcept
     218                 : {
     219            8638 :     sockaddr_storage storage{};
     220            8638 :     socklen_t len = sizeof(storage);
     221            8638 :     if (getsockname(
     222                 : #if BOOST_COROSIO_POSIX
     223                 :             fd,
     224                 : #else
     225                 :             static_cast<SOCKET>(fd),
     226                 : #endif
     227            8638 :             reinterpret_cast<sockaddr*>(&storage), &len) != 0)
     228 MIS           0 :         return AF_UNSPEC;
     229 HIT        8638 :     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              44 : from_sockaddr_local(
     290                 :     sockaddr_storage const& storage, socklen_t len) noexcept
     291                 : {
     292              44 :     if (storage.ss_family != AF_UNIX)
     293 MIS           0 :         return local_endpoint{};
     294                 : 
     295 HIT          44 :     sockaddr_un sa{};
     296              44 :     std::memcpy(
     297                 :         &sa, &storage,
     298              44 :         (std::min)(static_cast<std::size_t>(len), sizeof(sa)));
     299                 : 
     300              44 :     auto path_offset = offsetof(sockaddr_un, sun_path);
     301              44 :     if (static_cast<std::size_t>(len) <= path_offset)
     302               8 :         return local_endpoint{};
     303                 : 
     304              36 :     auto path_len = static_cast<std::size_t>(len) - path_offset;
     305                 : 
     306                 :     // Non-abstract paths may be null-terminated by the kernel
     307              36 :     if (path_len > 0 && sa.sun_path[0] != '\0')
     308                 :     {
     309                 :         auto* end = static_cast<char const*>(
     310              26 :             std::memchr(sa.sun_path, '\0', path_len));
     311              26 :         if (end)
     312              26 :             path_len = static_cast<std::size_t>(end - sa.sun_path);
     313                 :     }
     314                 : 
     315              36 :     std::error_code ec;
     316              36 :     local_endpoint ep(std::string_view(sa.sun_path, path_len), ec);
     317              36 :     if (ec)
     318               4 :         return local_endpoint{};
     319              32 :     return ep;
     320                 : }
     321                 : 
     322                 : #endif // BOOST_COROSIO_POSIX
     323                 : 
     324                 : //----------------------------------------------------------
     325                 : // Tag-dispatch helpers for templatized reactor code.
     326                 : // Overload resolution selects the correct conversion based
     327                 : // on the Endpoint type.
     328                 : //----------------------------------------------------------
     329                 : 
     330                 : /** Convert sockaddr_storage to an IP endpoint (tag overload).
     331                 : 
     332                 :     @param storage The sockaddr_storage with fields in network byte order.
     333                 :     @param len The address length returned by the kernel.
     334                 :     @return An endpoint with address and port extracted from storage.
     335                 : */
     336                 : inline endpoint
     337           17264 : from_sockaddr_as(
     338                 :     sockaddr_storage const& storage, socklen_t /*len*/, endpoint const&) noexcept
     339                 : {
     340           17264 :     return from_sockaddr(storage);
     341                 : }
     342                 : 
     343                 : #if BOOST_COROSIO_POSIX
     344                 : 
     345                 : /** Convert sockaddr_storage to a local_endpoint (tag overload).
     346                 : 
     347                 :     @param storage The sockaddr_storage.
     348                 :     @param len The address length returned by the kernel.
     349                 :     @return A local_endpoint with path extracted from storage.
     350                 : */
     351                 : inline local_endpoint
     352              44 : from_sockaddr_as(
     353                 :     sockaddr_storage const& storage,
     354                 :     socklen_t len,
     355                 :     local_endpoint const&) noexcept
     356                 : {
     357              44 :     return from_sockaddr_local(storage, len);
     358                 : }
     359                 : 
     360                 : #endif // BOOST_COROSIO_POSIX
     361                 : 
     362                 : } // namespace boost::corosio::detail
     363                 : 
     364                 : #endif
        

Generated by: LCOV version 2.3