LCOV - code coverage report
Current view: top level - corosio/native - native_socket_option.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 98.9 % 175 173 2
Test Date: 2026-09-25 21:36:35 Functions: 100.0 % 115 115

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2026 Steve Gerbino
       3                 : // Copyright (c) 2026 Michael Vandeberg
       4                 : //
       5                 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       6                 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       7                 : //
       8                 : // Official repository: https://github.com/cppalliance/corosio
       9                 : //
      10                 : 
      11                 : /** @file native_socket_option.hpp
      12                 : 
      13                 :     Inline socket option types using platform-specific constants.
      14                 :     All methods are `constexpr` or trivially inlined, giving zero
      15                 :     overhead compared to hand-written `setsockopt` calls.
      16                 : 
      17                 :     This header includes platform socket headers
      18                 :     (`<sys/socket.h>`, `<netinet/tcp.h>`, etc.).
      19                 :     For a version that avoids platform includes, use
      20                 :     `<boost/corosio/socket_option.hpp>`
      21                 :     (`boost::corosio::socket_option`).
      22                 : 
      23                 :     Both variants satisfy the same option-type interface and work
      24                 :     interchangeably with `tcp_socket::set_option` /
      25                 :     `tcp_socket::get_option` and the corresponding acceptor methods.
      26                 : 
      27                 :     @see boost::corosio::socket_option
      28                 : */
      29                 : 
      30                 : #ifndef BOOST_COROSIO_NATIVE_NATIVE_SOCKET_OPTION_HPP
      31                 : #define BOOST_COROSIO_NATIVE_NATIVE_SOCKET_OPTION_HPP
      32                 : 
      33                 : #ifdef _WIN32
      34                 : #include <winsock2.h>
      35                 : #include <ws2tcpip.h>
      36                 : #else
      37                 : #include <netinet/in.h>
      38                 : #include <netinet/tcp.h>
      39                 : #include <sys/socket.h>
      40                 : #endif
      41                 : 
      42                 : // Some older systems define only the legacy names
      43                 : #ifndef IPV6_JOIN_GROUP
      44                 : #define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP
      45                 : #endif
      46                 : #ifndef IPV6_LEAVE_GROUP
      47                 : #define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP
      48                 : #endif
      49                 : 
      50                 : #include <boost/corosio/detail/except.hpp>
      51                 : #include <boost/corosio/family.hpp>
      52                 : #include <boost/corosio/ip_address.hpp>
      53                 : #include <boost/corosio/ipv4_address.hpp>
      54                 : #include <boost/corosio/ipv6_address.hpp>
      55                 : 
      56                 : #include <cstddef>
      57                 : #include <cstring>
      58                 : 
      59                 : namespace boost::corosio::native_socket_option {
      60                 : 
      61                 : /** A socket option with a boolean value.
      62                 : 
      63                 :     Models socket options whose underlying representation is an `int`
      64                 :     where 0 means disabled and non-zero means enabled. The option's
      65                 :     protocol level and name are encoded as template parameters.
      66                 : 
      67                 :     This is the native (inline) variant that includes platform
      68                 :     headers. For a type-erased version that avoids platform
      69                 :     includes, use `boost::corosio::socket_option` instead.
      70                 : 
      71                 :     @par Example
      72                 :     @par !example boolean
      73                 : 
      74                 :     @tparam Level The protocol level (e.g. `SOL_SOCKET`, `IPPROTO_TCP`).
      75                 :     @tparam Name The option name (e.g. `TCP_NODELAY`, `SO_KEEPALIVE`).
      76                 : */
      77                 : template<int Level, int Name>
      78                 : class boolean
      79                 : {
      80                 :     int value_ = 0;
      81                 : 
      82                 : public:
      83                 :     /// Construct with default value (disabled).
      84 HIT        1494 :     boolean() = default;
      85                 : 
      86                 :     /** Construct with an explicit value.
      87                 : 
      88                 :         @param v `true` to enable the option, `false` to disable.
      89                 :     */
      90              23 :     explicit boolean(bool v) noexcept : value_(v ? 1 : 0) {}
      91                 : 
      92                 :     /// Assign a new value.
      93                 :     boolean& operator=(bool v) noexcept
      94                 :     {
      95                 :         value_ = v ? 1 : 0;
      96                 :         return *this;
      97                 :     }
      98                 : 
      99                 :     /// Return the option value.
     100              11 :     bool value() const noexcept
     101                 :     {
     102              11 :         return value_ != 0;
     103                 :     }
     104                 : 
     105                 :     /// Return the option value.
     106                 :     explicit operator bool() const noexcept
     107                 :     {
     108                 :         return value_ != 0;
     109                 :     }
     110                 : 
     111                 :     /// Return the negated option value.
     112                 :     bool operator!() const noexcept
     113                 :     {
     114                 :         return value_ == 0;
     115                 :     }
     116                 : 
     117                 :     /// Return the protocol level for `setsockopt`/`getsockopt`.
     118             777 :     constexpr int level(family) const noexcept
     119                 :     {
     120             777 :         return Level;
     121                 :     }
     122                 : 
     123                 :     /// Return the option name for `setsockopt`/`getsockopt`.
     124             777 :     constexpr int name(family) const noexcept
     125                 :     {
     126             777 :         return Name;
     127                 :     }
     128                 : 
     129                 :     /// Return a pointer to the underlying storage.
     130              10 :     void* data(family) noexcept
     131                 :     {
     132              10 :         return &value_;
     133                 :     }
     134                 : 
     135                 :     /// Return a pointer to the underlying storage.
     136              22 :     void const* data(family) const noexcept
     137                 :     {
     138              22 :         return &value_;
     139                 :     }
     140                 : 
     141                 :     /// Return the size of the underlying storage.
     142              30 :     std::size_t size(family) const noexcept
     143                 :     {
     144              30 :         return sizeof(value_);
     145                 :     }
     146                 : 
     147                 :     /** Normalize after `getsockopt` returns fewer bytes than expected.
     148                 : 
     149                 :         Windows Vista+ may write only 1 byte for boolean options.
     150                 : 
     151                 :         @param s The number of bytes actually written by `getsockopt`.
     152                 :     */
     153               9 :     void resize(family, std::size_t s) noexcept
     154                 :     {
     155               9 :         if (s == sizeof(char))
     156               1 :             value_ = *reinterpret_cast<unsigned char*>(&value_) ? 1 : 0;
     157               9 :     }
     158                 : };
     159                 : 
     160                 : /** A socket option with an integer value.
     161                 : 
     162                 :     Models socket options whose underlying representation is a
     163                 :     plain `int`. The option's protocol level and name are encoded
     164                 :     as template parameters.
     165                 : 
     166                 :     This is the native (inline) variant that includes platform
     167                 :     headers. For a type-erased version that avoids platform
     168                 :     includes, use `boost::corosio::socket_option` instead.
     169                 : 
     170                 :     @par Example
     171                 :     @par !example integer
     172                 : 
     173                 :     @tparam Level The protocol level (e.g. `SOL_SOCKET`).
     174                 :     @tparam Name The option name (e.g. `SO_RCVBUF`).
     175                 : */
     176                 : template<int Level, int Name>
     177                 : class integer
     178                 : {
     179                 :     int value_ = 0;
     180                 : 
     181                 : public:
     182                 :     /// Construct with default value (zero).
     183             262 :     integer() = default;
     184                 : 
     185                 :     /** Construct with an explicit value.
     186                 : 
     187                 :         @param v The option value.
     188                 :     */
     189               5 :     explicit integer(int v) noexcept : value_(v) {}
     190                 : 
     191                 :     /// Assign a new value.
     192                 :     integer& operator=(int v) noexcept
     193                 :     {
     194                 :         value_ = v;
     195                 :         return *this;
     196                 :     }
     197                 : 
     198                 :     /// Return the option value.
     199               5 :     int value() const noexcept
     200                 :     {
     201               5 :         return value_;
     202                 :     }
     203                 : 
     204                 :     /// Return the protocol level for `setsockopt`/`getsockopt`.
     205             139 :     constexpr int level(family) const noexcept
     206                 :     {
     207             139 :         return Level;
     208                 :     }
     209                 : 
     210                 :     /// Return the option name for `setsockopt`/`getsockopt`.
     211             139 :     constexpr int name(family) const noexcept
     212                 :     {
     213             139 :         return Name;
     214                 :     }
     215                 : 
     216                 :     /// Return a pointer to the underlying storage.
     217               4 :     void* data(family) noexcept
     218                 :     {
     219               4 :         return &value_;
     220                 :     }
     221                 : 
     222                 :     /// Return a pointer to the underlying storage.
     223               4 :     void const* data(family) const noexcept
     224                 :     {
     225               4 :         return &value_;
     226                 :     }
     227                 : 
     228                 :     /// Return the size of the underlying storage.
     229               8 :     std::size_t size(family) const noexcept
     230                 :     {
     231               8 :         return sizeof(value_);
     232                 :     }
     233                 : 
     234                 :     /** Normalize after `getsockopt` returns fewer bytes than expected.
     235                 : 
     236                 :         @param s The number of bytes actually written by `getsockopt`.
     237                 :     */
     238               5 :     void resize(family, std::size_t s) noexcept
     239                 :     {
     240               5 :         if (s == sizeof(char))
     241               1 :             value_ =
     242               1 :                 static_cast<int>(*reinterpret_cast<unsigned char*>(&value_));
     243               5 :     }
     244                 : };
     245                 : 
     246                 : /** A boolean socket option with single-byte storage.
     247                 : 
     248                 :     Some BSD-derived kernels (macOS, FreeBSD) require certain IPv4 multicast
     249                 :     options (`IP_MULTICAST_LOOP`) to be set with a one-byte value and return
     250                 :     `EINVAL` for the four-byte form that Linux accepts. This template
     251                 :     provides `unsigned char` storage so the option works on every platform.
     252                 : 
     253                 :     @tparam Level The protocol level.
     254                 :     @tparam Name The option name.
     255                 : */
     256                 : template<int Level, int Name>
     257                 : class byte_boolean
     258                 : {
     259                 :     unsigned char value_ = 0;
     260                 : 
     261                 : public:
     262                 :     byte_boolean() = default;
     263                 : 
     264                 :     explicit byte_boolean(bool v) noexcept : value_(v ? 1 : 0) {}
     265                 : 
     266                 :     byte_boolean& operator=(bool v) noexcept
     267                 :     {
     268                 :         value_ = v ? 1 : 0;
     269                 :         return *this;
     270                 :     }
     271                 : 
     272                 :     bool value() const noexcept
     273                 :     {
     274                 :         return value_ != 0;
     275                 :     }
     276                 :     explicit operator bool() const noexcept
     277                 :     {
     278                 :         return value_ != 0;
     279                 :     }
     280                 :     bool operator!() const noexcept
     281                 :     {
     282                 :         return value_ == 0;
     283                 :     }
     284                 : 
     285                 :     constexpr int level(family) const noexcept
     286                 :     {
     287                 :         return Level;
     288                 :     }
     289                 :     constexpr int name(family) const noexcept
     290                 :     {
     291                 :         return Name;
     292                 :     }
     293                 : 
     294                 :     void* data(family) noexcept
     295                 :     {
     296                 :         return &value_;
     297                 :     }
     298                 :     void const* data(family) const noexcept
     299                 :     {
     300                 :         return &value_;
     301                 :     }
     302                 :     std::size_t size(family) const noexcept
     303                 :     {
     304                 :         return sizeof(value_);
     305                 :     }
     306                 : 
     307                 :     void resize(family, std::size_t) noexcept {}
     308                 : };
     309                 : 
     310                 : /** An integer socket option with single-byte storage.
     311                 : 
     312                 :     Same rationale as `byte_boolean`: BSD-derived kernels require
     313                 :     `IP_MULTICAST_TTL` to be set with a one-byte value. Linux accepts
     314                 :     one byte too, so single-byte storage is portable. Values are
     315                 :     truncated to the 0–255 range.
     316                 : 
     317                 :     @tparam Level The protocol level.
     318                 :     @tparam Name The option name.
     319                 : */
     320                 : template<int Level, int Name>
     321                 : class byte_integer
     322                 : {
     323                 :     unsigned char value_ = 0;
     324                 : 
     325                 : public:
     326                 :     byte_integer() = default;
     327                 : 
     328                 :     explicit byte_integer(int v) noexcept
     329                 :         : value_(static_cast<unsigned char>(v))
     330                 :     {
     331                 :     }
     332                 : 
     333                 :     byte_integer& operator=(int v) noexcept
     334                 :     {
     335                 :         value_ = static_cast<unsigned char>(v);
     336                 :         return *this;
     337                 :     }
     338                 : 
     339                 :     int value() const noexcept
     340                 :     {
     341                 :         return value_;
     342                 :     }
     343                 : 
     344                 :     constexpr int level(family) const noexcept
     345                 :     {
     346                 :         return Level;
     347                 :     }
     348                 :     constexpr int name(family) const noexcept
     349                 :     {
     350                 :         return Name;
     351                 :     }
     352                 : 
     353                 :     void* data(family) noexcept
     354                 :     {
     355                 :         return &value_;
     356                 :     }
     357                 :     void const* data(family) const noexcept
     358                 :     {
     359                 :         return &value_;
     360                 :     }
     361                 :     std::size_t size(family) const noexcept
     362                 :     {
     363                 :         return sizeof(value_);
     364                 :     }
     365                 : 
     366                 :     void resize(family, std::size_t) noexcept {}
     367                 : };
     368                 : 
     369                 : /** The SO_LINGER socket option (native variant).
     370                 : 
     371                 :     Controls behavior when closing a socket with unsent data.
     372                 :     When enabled, `close()` blocks until pending data is sent
     373                 :     or the timeout expires.
     374                 : 
     375                 :     This variant stores the platform's `struct linger` directly,
     376                 :     avoiding the opaque-storage indirection of the type-erased
     377                 :     version.
     378                 : 
     379                 :     @par Example
     380                 :     @par !example linger
     381                 : */
     382                 : class linger
     383                 : {
     384                 :     struct ::linger value_{};
     385                 : 
     386                 : public:
     387                 :     /// Construct with default values (disabled, zero timeout).
     388             645 :     linger() = default;
     389                 : 
     390                 :     /** Construct with explicit values.
     391                 : 
     392                 :         @param enabled `true` to enable linger behavior on close.
     393                 :         @param timeout The linger timeout in seconds.
     394                 :     */
     395             203 :     linger(bool enabled, int timeout) noexcept
     396             203 :     {
     397             203 :         value_.l_onoff  = enabled ? 1 : 0;
     398             203 :         value_.l_linger = static_cast<decltype(value_.l_linger)>(timeout);
     399             203 :     }
     400                 : 
     401                 :     /// Return whether linger is enabled.
     402              22 :     bool enabled() const noexcept
     403                 :     {
     404              22 :         return value_.l_onoff != 0;
     405                 :     }
     406                 : 
     407                 :     /// Set whether linger is enabled.
     408               4 :     void enabled(bool v) noexcept
     409                 :     {
     410               4 :         value_.l_onoff = v ? 1 : 0;
     411               4 :     }
     412                 : 
     413                 :     /// Return the linger timeout in seconds.
     414              20 :     int timeout() const noexcept
     415                 :     {
     416              20 :         return static_cast<int>(value_.l_linger);
     417                 :     }
     418                 : 
     419                 :     /// Set the linger timeout in seconds.
     420               4 :     void timeout(int v) noexcept
     421                 :     {
     422               4 :         value_.l_linger = static_cast<decltype(value_.l_linger)>(v);
     423               4 :     }
     424                 : 
     425                 :     /// Return the protocol level for `setsockopt`/`getsockopt`.
     426             217 :     constexpr int level(family) const noexcept
     427                 :     {
     428             217 :         return SOL_SOCKET;
     429                 :     }
     430                 : 
     431                 :     /// Return the option name for `setsockopt`/`getsockopt`.
     432             217 :     constexpr int name(family) const noexcept
     433                 :     {
     434             217 :         return SO_LINGER;
     435                 :     }
     436                 : 
     437                 :     /// Return a pointer to the underlying storage.
     438             241 :     void* data(family) noexcept
     439                 :     {
     440             241 :         return &value_;
     441                 :     }
     442                 : 
     443                 :     /// Return a pointer to the underlying storage.
     444               2 :     void const* data(family) const noexcept
     445                 :     {
     446               2 :         return &value_;
     447                 :     }
     448                 : 
     449                 :     /// Return the size of the underlying storage.
     450             456 :     std::size_t size(family) const noexcept
     451                 :     {
     452             456 :         return sizeof(value_);
     453                 :     }
     454                 : 
     455                 :     /** Normalize after `getsockopt`.
     456                 : 
     457                 :         No-op — `struct linger` is always returned at full size.
     458                 : 
     459                 :         @param s The number of bytes actually written by `getsockopt`.
     460                 :     */
     461                 :     void resize(family, std::size_t) noexcept {}
     462                 : };
     463                 : 
     464                 : /// Disable Nagle's algorithm (TCP_NODELAY).
     465                 : using no_delay = boolean<IPPROTO_TCP, TCP_NODELAY>;
     466                 : 
     467                 : /// Enable periodic keepalive probes (SO_KEEPALIVE).
     468                 : using keep_alive = boolean<SOL_SOCKET, SO_KEEPALIVE>;
     469                 : 
     470                 : /// Restrict an IPv6 socket to IPv6 only (IPV6_V6ONLY).
     471                 : using v6_only = boolean<IPPROTO_IPV6, IPV6_V6ONLY>;
     472                 : 
     473                 : /// Allow local address reuse (SO_REUSEADDR).
     474                 : using reuse_address = boolean<SOL_SOCKET, SO_REUSEADDR>;
     475                 : 
     476                 : /// Allow sending to broadcast addresses (SO_BROADCAST).
     477                 : using broadcast = boolean<SOL_SOCKET, SO_BROADCAST>;
     478                 : 
     479                 : /// Set the receive buffer size (SO_RCVBUF).
     480                 : using receive_buffer_size = integer<SOL_SOCKET, SO_RCVBUF>;
     481                 : 
     482                 : /// Set the send buffer size (SO_SNDBUF).
     483                 : using send_buffer_size = integer<SOL_SOCKET, SO_SNDBUF>;
     484                 : 
     485                 : #ifdef SO_REUSEPORT
     486                 : /// Allow multiple sockets to bind to the same port (SO_REUSEPORT).
     487                 : using reuse_port = boolean<SOL_SOCKET, SO_REUSEPORT>;
     488                 : #endif
     489                 : 
     490                 : /** Enable loopback of outgoing multicast (IP_MULTICAST_LOOP /
     491                 :     IPV6_MULTICAST_LOOP).
     492                 : 
     493                 :     The socket's family selects the wire rendering: a single byte
     494                 :     at `IPPROTO_IP` for IPv4 (BSD-derived kernels reject the
     495                 :     four-byte form), an `int` at `IPPROTO_IPV6` for IPv6.
     496                 : */
     497                 : class multicast_loop
     498                 : {
     499                 :     unsigned char byte_ = 0; // IPv4 rendering
     500                 :     int int_            = 0; // IPv6 rendering
     501                 : 
     502                 : public:
     503                 :     /// Construct with default value (disabled).
     504              68 :     multicast_loop() = default;
     505                 : 
     506                 :     /** Construct with an explicit value.
     507                 : 
     508                 :         @param v `true` to enable loopback, `false` to disable.
     509                 :     */
     510               6 :     explicit multicast_loop(bool v) noexcept : byte_(v ? 1 : 0), int_(v ? 1 : 0)
     511                 :     {
     512               6 :     }
     513                 : 
     514                 :     /// Assign a new value.
     515                 :     multicast_loop& operator=(bool v) noexcept
     516                 :     {
     517                 :         byte_ = v ? 1 : 0;
     518                 :         int_  = v ? 1 : 0;
     519                 :         return *this;
     520                 :     }
     521                 : 
     522                 :     /// Return the option value.
     523               2 :     bool value() const noexcept
     524                 :     {
     525               2 :         return byte_ != 0 || int_ != 0;
     526                 :     }
     527                 : 
     528                 :     /// Return the protocol level for `setsockopt`/`getsockopt`.
     529              40 :     constexpr int level(family f) const noexcept
     530                 :     {
     531              40 :         return f == family::v6 ? IPPROTO_IPV6 : IPPROTO_IP;
     532                 :     }
     533                 : 
     534                 :     /// Return the option name for `setsockopt`/`getsockopt`.
     535              40 :     constexpr int name(family f) const noexcept
     536                 :     {
     537              40 :         return f == family::v6 ? IPV6_MULTICAST_LOOP : IP_MULTICAST_LOOP;
     538                 :     }
     539                 : 
     540                 :     /// Return a pointer to the rendering for `f`.
     541               2 :     void* data(family f) noexcept
     542                 :     {
     543               2 :         return f == family::v6 ? static_cast<void*>(&int_)
     544               2 :                                : static_cast<void*>(&byte_);
     545                 :     }
     546                 : 
     547                 :     /// Return a pointer to the rendering for `f`.
     548               4 :     void const* data(family f) const noexcept
     549                 :     {
     550               4 :         return f == family::v6 ? static_cast<void const*>(&int_)
     551               4 :                                : static_cast<void const*>(&byte_);
     552                 :     }
     553                 : 
     554                 :     /// Return the size of the rendering for `f`.
     555              10 :     std::size_t size(family f) const noexcept
     556                 :     {
     557              10 :         return f == family::v6 ? sizeof(int_) : sizeof(byte_);
     558                 :     }
     559                 : 
     560                 :     /** Synchronize both renderings after `getsockopt`.
     561                 : 
     562                 :         Only the rendering the socket's family selected was
     563                 :         written; fold it into the other so `value()` answers
     564                 :         from either.
     565                 : 
     566                 :         @param f The family `getsockopt` was performed for.
     567                 :     */
     568               2 :     void resize(family f, std::size_t) noexcept
     569                 :     {
     570               2 :         if (f == family::v6)
     571 MIS           0 :             byte_ = int_ ? 1 : 0;
     572                 :         else
     573 HIT           2 :             int_ = byte_ ? 1 : 0;
     574               2 :     }
     575                 : };
     576                 : 
     577                 : /** Set the multicast TTL / hop limit (IP_MULTICAST_TTL /
     578                 :     IPV6_MULTICAST_HOPS).
     579                 : 
     580                 :     The socket's family selects the wire rendering: a single byte
     581                 :     at `IPPROTO_IP` for IPv4, an `int` at `IPPROTO_IPV6` for IPv6.
     582                 : */
     583                 : class multicast_hops
     584                 : {
     585                 :     unsigned char byte_ = 0; // IPv4 rendering
     586                 :     int int_            = 0; // IPv6 rendering
     587                 : 
     588                 : public:
     589                 :     /// Construct with default value (zero).
     590              32 :     multicast_hops() = default;
     591                 : 
     592                 :     /** Construct with an explicit value.
     593                 : 
     594                 :         @param v The hop count, 0 to 255 — the range the IPv4 wire
     595                 :         rendering can carry.
     596                 : 
     597                 :         @throws std::logic_error if `v` is outside [0, 255].
     598                 :     */
     599               6 :     explicit multicast_hops(int v)
     600               6 :     {
     601               6 :         if (v < 0 || v > 255)
     602               2 :             detail::throw_logic_error("multicast hops value out of range");
     603               4 :         byte_ = static_cast<unsigned char>(v);
     604               4 :         int_  = v;
     605               4 :     }
     606                 : 
     607                 :     /** Assign a new value.
     608                 : 
     609                 :         @throws std::logic_error if `v` is outside [0, 255].
     610                 :     */
     611                 :     multicast_hops& operator=(int v)
     612                 :     {
     613                 :         if (v < 0 || v > 255)
     614                 :             detail::throw_logic_error("multicast hops value out of range");
     615                 :         byte_ = static_cast<unsigned char>(v);
     616                 :         int_  = v;
     617                 :         return *this;
     618                 :     }
     619                 : 
     620                 :     /// Return the option value.
     621               2 :     int value() const noexcept
     622                 :     {
     623               2 :         return int_;
     624                 :     }
     625                 : 
     626                 :     /// Return the protocol level for `setsockopt`/`getsockopt`.
     627              22 :     constexpr int level(family f) const noexcept
     628                 :     {
     629              22 :         return f == family::v6 ? IPPROTO_IPV6 : IPPROTO_IP;
     630                 :     }
     631                 : 
     632                 :     /// Return the option name for `setsockopt`/`getsockopt`.
     633              22 :     constexpr int name(family f) const noexcept
     634                 :     {
     635              22 :         return f == family::v6 ? IPV6_MULTICAST_HOPS : IP_MULTICAST_TTL;
     636                 :     }
     637                 : 
     638                 :     /// Return a pointer to the rendering for `f`.
     639               2 :     void* data(family f) noexcept
     640                 :     {
     641               2 :         return f == family::v6 ? static_cast<void*>(&int_)
     642               2 :                                : static_cast<void*>(&byte_);
     643                 :     }
     644                 : 
     645                 :     /// Return a pointer to the rendering for `f`.
     646               4 :     void const* data(family f) const noexcept
     647                 :     {
     648               4 :         return f == family::v6 ? static_cast<void const*>(&int_)
     649               4 :                                : static_cast<void const*>(&byte_);
     650                 :     }
     651                 : 
     652                 :     /// Return the size of the rendering for `f`.
     653               6 :     std::size_t size(family f) const noexcept
     654                 :     {
     655               6 :         return f == family::v6 ? sizeof(int_) : sizeof(byte_);
     656                 :     }
     657                 : 
     658                 :     /** Synchronize both renderings after `getsockopt`.
     659                 : 
     660                 :         @param f The family `getsockopt` was performed for.
     661                 :     */
     662               2 :     void resize(family f, std::size_t) noexcept
     663                 :     {
     664               2 :         if (f == family::v6)
     665 MIS           0 :             byte_ = static_cast<unsigned char>(int_);
     666                 :         else
     667 HIT           2 :             int_ = byte_;
     668               2 :     }
     669                 : };
     670                 : 
     671                 : /** A multicast membership request.
     672                 : 
     673                 :     The group's family — not the socket's — selects the wire
     674                 :     struct and protocol level: a v4 group renders as an `ip_mreq`
     675                 :     at the IPv4 level even when applied to a dual-stack v6 socket,
     676                 :     which is the level such a join actually targets.
     677                 : 
     678                 :     @tparam Level4 The IPv4 protocol level.
     679                 :     @tparam Name4 The IPv4 option name.
     680                 :     @tparam Level6 The IPv6 protocol level.
     681                 :     @tparam Name6 The IPv6 option name.
     682                 : */
     683                 : template<int Level4, int Name4, int Level6, int Name6>
     684                 : class membership_request
     685                 : {
     686                 :     struct ip_mreq v4_{};
     687                 :     struct ipv6_mreq v6_{};
     688                 :     family group_family_ = family::v4;
     689                 : 
     690              88 :     void assign_v4(ipv4_address group, ipv4_address iface) noexcept
     691                 :     {
     692              88 :         auto g = group.to_bytes();
     693              88 :         std::memcpy(&v4_.imr_multiaddr, g.data(), 4);
     694              88 :         auto i = iface.to_bytes();
     695              88 :         std::memcpy(&v4_.imr_interface, i.data(), 4);
     696              88 :         group_family_ = family::v4;
     697              88 :     }
     698                 : 
     699              90 :     void assign_v6(ipv6_address const& group, unsigned int if_index) noexcept
     700                 :     {
     701              90 :         auto g = group.to_bytes();
     702              90 :         std::memcpy(&v6_.ipv6mr_multiaddr, g.data(), 16);
     703                 :         // The group's zone is the natural default interface
     704              90 :         v6_.ipv6mr_interface = if_index ? if_index : group.scope_id();
     705              90 :         group_family_        = family::v6;
     706              90 :     }
     707                 : 
     708                 : public:
     709                 :     /// Construct with default values.
     710                 :     membership_request() = default;
     711                 : 
     712                 :     /** Construct from a group address.
     713                 : 
     714                 :         The group's family selects the wire representation; the
     715                 :         interface defaults to any (v4) or the group's zone (v6).
     716                 : 
     717                 :         @param group The multicast group address.
     718                 :     */
     719              16 :     explicit membership_request(ip_address const& group) noexcept
     720              16 :     {
     721              16 :         if (group.is_v4())
     722               8 :             assign_v4(group.to_v4(), ipv4_address());
     723                 :         else
     724               8 :             assign_v6(group.to_v6(), 0);
     725              16 :     }
     726                 : 
     727                 :     /** Construct from an IPv4 group and interface address.
     728                 : 
     729                 :         @param group The multicast group address.
     730                 :         @param iface The local interface to use (default: any).
     731                 :     */
     732              80 :     membership_request(
     733                 :         ipv4_address group, ipv4_address iface = ipv4_address()) noexcept
     734              80 :     {
     735              80 :         assign_v4(group, iface);
     736              80 :     }
     737                 : 
     738                 :     /** Construct from an IPv6 group and interface index.
     739                 : 
     740                 :         @param group The multicast group address.
     741                 :         @param if_index The interface index; 0 uses the group's
     742                 :         zone, and a zone of 0 lets the kernel choose.
     743                 :     */
     744              82 :     membership_request(
     745                 :         ipv6_address const& group, unsigned int if_index = 0) noexcept
     746              82 :     {
     747              82 :         assign_v6(group, if_index);
     748              82 :     }
     749                 : 
     750                 :     /// Return the protocol level for the group's family.
     751              78 :     int level(family) const noexcept
     752                 :     {
     753              78 :         return group_family_ == family::v4 ? Level4 : Level6;
     754                 :     }
     755                 : 
     756                 :     /// Return the option name for the group's family.
     757              66 :     int name(family) const noexcept
     758                 :     {
     759              66 :         return group_family_ == family::v4 ? Name4 : Name6;
     760                 :     }
     761                 : 
     762                 :     /// Return a pointer to the wire struct for the group's family.
     763              56 :     void const* data(family) const noexcept
     764                 :     {
     765              56 :         return group_family_ == family::v4 ? static_cast<void const*>(&v4_)
     766              56 :                                            : static_cast<void const*>(&v6_);
     767                 :     }
     768                 : 
     769                 :     /// Return the size of the wire struct for the group's family.
     770             104 :     std::size_t size(family) const noexcept
     771                 :     {
     772             104 :         return group_family_ == family::v4 ? sizeof(v4_) : sizeof(v6_);
     773                 :     }
     774                 : 
     775                 :     /// No-op resize.
     776                 :     void resize(family, std::size_t) noexcept {}
     777                 : };
     778                 : 
     779                 : /// Join a multicast group (IP_ADD_MEMBERSHIP / IPV6_JOIN_GROUP).
     780                 : using join_group = membership_request<
     781                 :     IPPROTO_IP,
     782                 :     IP_ADD_MEMBERSHIP,
     783                 :     IPPROTO_IPV6,
     784                 :     IPV6_JOIN_GROUP>;
     785                 : 
     786                 : /// Leave a multicast group (IP_DROP_MEMBERSHIP / IPV6_LEAVE_GROUP).
     787                 : using leave_group = membership_request<
     788                 :     IPPROTO_IP,
     789                 :     IP_DROP_MEMBERSHIP,
     790                 :     IPPROTO_IPV6,
     791                 :     IPV6_LEAVE_GROUP>;
     792                 : 
     793                 : /** Set the outgoing multicast interface (IP_MULTICAST_IF /
     794                 :     IPV6_MULTICAST_IF).
     795                 : 
     796                 :     The two families name interfaces differently on the wire — IPv4
     797                 :     by interface address, IPv6 by interface index — so the option
     798                 :     stores both renderings and the socket's family selects one; the
     799                 :     other stays at its default (any address, kernel-chosen index).
     800                 : */
     801                 : class multicast_interface
     802                 : {
     803                 :     struct in_addr v4_{};
     804                 :     unsigned int if_index_ = 0;
     805                 : 
     806                 : public:
     807                 :     /// Construct with default values (any address, kernel-chosen index).
     808              22 :     multicast_interface() = default;
     809                 : 
     810                 :     /** Construct with an IPv4 interface address.
     811                 : 
     812                 :         @param iface The local interface address.
     813                 :     */
     814               8 :     explicit multicast_interface(ipv4_address iface) noexcept
     815               8 :     {
     816               8 :         auto b = iface.to_bytes();
     817               8 :         std::memcpy(&v4_, b.data(), 4);
     818               8 :     }
     819                 : 
     820                 :     /** Construct with an IPv6 interface index.
     821                 : 
     822                 :         @param if_index The interface index (0 = kernel chooses).
     823                 :     */
     824               4 :     explicit multicast_interface(unsigned int if_index) noexcept
     825               4 :         : if_index_(if_index)
     826                 :     {
     827               4 :     }
     828                 : 
     829                 :     /// Return the IPv4 rendering as an address.
     830               2 :     ipv4_address address() const noexcept
     831                 :     {
     832                 :         ipv4_address::bytes_type b;
     833               2 :         std::memcpy(b.data(), &v4_, 4);
     834               2 :         return ipv4_address(b);
     835                 :     }
     836                 : 
     837                 :     /// Return the IPv6 rendering as an interface index.
     838               4 :     unsigned int if_index() const noexcept
     839                 :     {
     840               4 :         return if_index_;
     841                 :     }
     842                 : 
     843                 :     /// Return the protocol level for `setsockopt`/`getsockopt`.
     844              14 :     constexpr int level(family f) const noexcept
     845                 :     {
     846              14 :         return f == family::v6 ? IPPROTO_IPV6 : IPPROTO_IP;
     847                 :     }
     848                 : 
     849                 :     /// Return the option name for `setsockopt`/`getsockopt`.
     850              14 :     constexpr int name(family f) const noexcept
     851                 :     {
     852              14 :         return f == family::v6 ? IPV6_MULTICAST_IF : IP_MULTICAST_IF;
     853                 :     }
     854                 : 
     855                 :     /// Return a pointer to the rendering for `f`.
     856              10 :     void* data(family f) noexcept
     857                 :     {
     858              10 :         return f == family::v6 ? static_cast<void*>(&if_index_)
     859              10 :                                : static_cast<void*>(&v4_);
     860                 :     }
     861                 : 
     862                 :     /// Return a pointer to the rendering for `f`.
     863               4 :     void const* data(family f) const noexcept
     864                 :     {
     865               4 :         return f == family::v6 ? static_cast<void const*>(&if_index_)
     866               4 :                                : static_cast<void const*>(&v4_);
     867                 :     }
     868                 : 
     869                 :     /// Return the size of the rendering for `f`.
     870              24 :     std::size_t size(family f) const noexcept
     871                 :     {
     872              24 :         return f == family::v6 ? sizeof(if_index_) : sizeof(v4_);
     873                 :     }
     874                 : 
     875                 :     /// No-op resize.
     876               2 :     void resize(family, std::size_t) noexcept {}
     877                 : };
     878                 : 
     879                 : } // namespace boost::corosio::native_socket_option
     880                 : 
     881                 : #endif // BOOST_COROSIO_NATIVE_NATIVE_SOCKET_OPTION_HPP
        

Generated by: LCOV version 2.3