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

Generated by: LCOV version 2.3