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