95.95% Lines (71/74) 100.00% Functions (36/36)
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   #ifndef BOOST_COROSIO_SOCKET_OPTION_HPP 10   #ifndef BOOST_COROSIO_SOCKET_OPTION_HPP
11   #define BOOST_COROSIO_SOCKET_OPTION_HPP 11   #define BOOST_COROSIO_SOCKET_OPTION_HPP
12   12  
13   #include <boost/corosio/detail/config.hpp> 13   #include <boost/corosio/detail/config.hpp>
14   #include <boost/corosio/ipv4_address.hpp> 14   #include <boost/corosio/ipv4_address.hpp>
15   #include <boost/corosio/ipv6_address.hpp> 15   #include <boost/corosio/ipv6_address.hpp>
16   16  
17   #include <cstddef> 17   #include <cstddef>
18   18  
19   /** @file socket_option.hpp 19   /** @file socket_option.hpp
20   20  
21   Type-erased socket option types that avoid platform-specific 21   Type-erased socket option types that avoid platform-specific
22   headers. The protocol level and option name for each type are 22   headers. The protocol level and option name for each type are
23   resolved at link time via the compiled library. 23   resolved at link time via the compiled library.
24   24  
25   For an inline (zero-overhead) alternative that includes platform 25   For an inline (zero-overhead) alternative that includes platform
26   headers, use `<boost/corosio/native/native_socket_option.hpp>` 26   headers, use `<boost/corosio/native/native_socket_option.hpp>`
27   (`boost::corosio::native_socket_option`). 27   (`boost::corosio::native_socket_option`).
28   28  
29   Both variants satisfy the same option-type interface and work 29   Both variants satisfy the same option-type interface and work
30   interchangeably with `tcp_socket::set_option` / 30   interchangeably with `tcp_socket::set_option` /
31   `tcp_socket::get_option` and the corresponding acceptor methods. 31   `tcp_socket::get_option` and the corresponding acceptor methods.
32   32  
33   @see native_socket_option 33   @see native_socket_option
34   */ 34   */
35   35  
36   namespace boost::corosio::socket_option { 36   namespace boost::corosio::socket_option {
37   37  
38   /** Base class for concrete boolean socket options. 38   /** Base class for concrete boolean socket options.
39   39  
40   Stores a boolean as an `int` suitable for `setsockopt`/`getsockopt`. 40   Stores a boolean as an `int` suitable for `setsockopt`/`getsockopt`.
41   Derived types provide `level()` and `name()` for the specific option. 41   Derived types provide `level()` and `name()` for the specific option.
42   */ 42   */
43   class boolean_option 43   class boolean_option
44   { 44   {
45   int value_ = 0; 45   int value_ = 0;
46   46  
47   public: 47   public:
48   /// Construct with default value (disabled). 48   /// Construct with default value (disabled).
49   boolean_option() = default; 49   boolean_option() = default;
50   50  
51   /** Construct with an explicit value. 51   /** Construct with an explicit value.
52   52  
53   @param v `true` to enable the option, `false` to disable. 53   @param v `true` to enable the option, `false` to disable.
54   */ 54   */
HITCBC 55   215 explicit boolean_option(bool v) noexcept : value_(v ? 1 : 0) {} 55   261 explicit boolean_option(bool v) noexcept : value_(v ? 1 : 0) {}
56   56  
57   /// Assign a new value. 57   /// Assign a new value.
HITCBC 58   4 boolean_option& operator=(bool v) noexcept 58   4 boolean_option& operator=(bool v) noexcept
59   { 59   {
HITCBC 60   4 value_ = v ? 1 : 0; 60   4 value_ = v ? 1 : 0;
HITCBC 61   4 return *this; 61   4 return *this;
62   } 62   }
63   63  
64   /// Return the option value. 64   /// Return the option value.
HITCBC 65   44 bool value() const noexcept 65   42 bool value() const noexcept
66   { 66   {
HITCBC 67   44 return value_ != 0; 67   42 return value_ != 0;
68   } 68   }
69   69  
70   /// Return the option value. 70   /// Return the option value.
HITCBC 71   4 explicit operator bool() const noexcept 71   4 explicit operator bool() const noexcept
72   { 72   {
HITCBC 73   4 return value_ != 0; 73   4 return value_ != 0;
74   } 74   }
75   75  
76   /// Return the negated option value. 76   /// Return the negated option value.
HITCBC 77   4 bool operator!() const noexcept 77   4 bool operator!() const noexcept
78   { 78   {
HITCBC 79   4 return value_ == 0; 79   4 return value_ == 0;
80   } 80   }
81   81  
82   /// Return a pointer to the underlying storage. 82   /// Return a pointer to the underlying storage.
HITCBC 83   46 void* data() noexcept 83   44 void* data() noexcept
84   { 84   {
HITCBC 85   46 return &value_; 85   44 return &value_;
86   } 86   }
87   87  
88   /// Return a pointer to the underlying storage. 88   /// Return a pointer to the underlying storage.
HITCBC 89   217 void const* data() const noexcept 89   261 void const* data() const noexcept
90   { 90   {
HITCBC 91   217 return &value_; 91   261 return &value_;
92   } 92   }
93   93  
94   /// Return the size of the underlying storage. 94   /// Return the size of the underlying storage.
HITCBC 95   263 std::size_t size() const noexcept 95   305 std::size_t size() const noexcept
96   { 96   {
HITCBC 97   263 return sizeof(value_); 97   305 return sizeof(value_);
98   } 98   }
99   99  
100   /** Normalize after `getsockopt` returns fewer bytes than expected. 100   /** Normalize after `getsockopt` returns fewer bytes than expected.
101   101  
102   Windows Vista+ may write only 1 byte for boolean options. 102   Windows Vista+ may write only 1 byte for boolean options.
103   103  
104   @param s The number of bytes actually written by `getsockopt`. 104   @param s The number of bytes actually written by `getsockopt`.
105   */ 105   */
HITCBC 106   46 void resize(std::size_t s) noexcept 106   44 void resize(std::size_t s) noexcept
107   { 107   {
HITCBC 108   46 if (s == sizeof(char)) 108   44 if (s == sizeof(char))
MISUBC 109   value_ = *reinterpret_cast<unsigned char*>(&value_) ? 1 : 0; 109   value_ = *reinterpret_cast<unsigned char*>(&value_) ? 1 : 0;
HITCBC 110   46 } 110   44 }
111   }; 111   };
112   112  
113   /** Base class for concrete integer socket options. 113   /** Base class for concrete integer socket options.
114   114  
115   Stores an integer suitable for `setsockopt`/`getsockopt`. 115   Stores an integer suitable for `setsockopt`/`getsockopt`.
116   Derived types provide `level()` and `name()` for the specific option. 116   Derived types provide `level()` and `name()` for the specific option.
117   */ 117   */
118   class integer_option 118   class integer_option
119   { 119   {
120   int value_ = 0; 120   int value_ = 0;
121   121  
122   public: 122   public:
123   /// Construct with default value (zero). 123   /// Construct with default value (zero).
124   integer_option() = default; 124   integer_option() = default;
125   125  
126   /** Construct with an explicit value. 126   /** Construct with an explicit value.
127   127  
128   @param v The option value. 128   @param v The option value.
129   */ 129   */
HITCBC 130   14 explicit integer_option(int v) noexcept : value_(v) {} 130   24 explicit integer_option(int v) noexcept : value_(v) {}
131   131  
132   /// Assign a new value. 132   /// Assign a new value.
HITCBC 133   2 integer_option& operator=(int v) noexcept 133   2 integer_option& operator=(int v) noexcept
134   { 134   {
HITCBC 135   2 value_ = v; 135   2 value_ = v;
HITCBC 136   2 return *this; 136   2 return *this;
137   } 137   }
138   138  
139   /// Return the option value. 139   /// Return the option value.
HITCBC 140   24 int value() const noexcept 140   30 int value() const noexcept
141   { 141   {
HITCBC 142   24 return value_; 142   30 return value_;
143   } 143   }
144   144  
145   /// Return a pointer to the underlying storage. 145   /// Return a pointer to the underlying storage.
HITCBC 146   22 void* data() noexcept 146   28 void* data() noexcept
147   { 147   {
HITCBC 148   22 return &value_; 148   28 return &value_;
149   } 149   }
150   150  
151   /// Return a pointer to the underlying storage. 151   /// Return a pointer to the underlying storage.
HITCBC 152   14 void const* data() const noexcept 152   24 void const* data() const noexcept
153   { 153   {
HITCBC 154   14 return &value_; 154   24 return &value_;
155   } 155   }
156   156  
157   /// Return the size of the underlying storage. 157   /// Return the size of the underlying storage.
HITCBC 158   36 std::size_t size() const noexcept 158   52 std::size_t size() const noexcept
159   { 159   {
HITCBC 160   36 return sizeof(value_); 160   52 return sizeof(value_);
161   } 161   }
162   162  
163   /** Normalize after `getsockopt` returns fewer bytes than expected. 163   /** Normalize after `getsockopt` returns fewer bytes than expected.
164   164  
165   @param s The number of bytes actually written by `getsockopt`. 165   @param s The number of bytes actually written by `getsockopt`.
166   */ 166   */
HITCBC 167   22 void resize(std::size_t s) noexcept 167   28 void resize(std::size_t s) noexcept
168   { 168   {
HITCBC 169   22 if (s == sizeof(char)) 169   28 if (s == sizeof(char))
MISUBC 170   value_ = 170   value_ =
MISUBC 171   static_cast<int>(*reinterpret_cast<unsigned char*>(&value_)); 171   static_cast<int>(*reinterpret_cast<unsigned char*>(&value_));
HITCBC 172   22 } 172   28 }
173   }; 173   };
174   174  
  175 + /** Base class for concrete boolean socket options with single-byte storage.
  176 +
  177 + Some BSD-derived kernels (macOS, FreeBSD) require certain IPv4 multicast
  178 + options (`IP_MULTICAST_LOOP`) to be set with a one-byte value and return
  179 + `EINVAL` for the four-byte form that Linux accepts. This base provides
  180 + `unsigned char` storage so the same options work on every platform.
  181 + */
  182 + class byte_boolean_option
  183 + {
  184 + unsigned char value_ = 0;
  185 +
  186 + public:
  187 + /// Construct with default value (disabled).
  188 + byte_boolean_option() = default;
  189 +
  190 + /** Construct with an explicit value.
  191 +
  192 + @param v `true` to enable the option, `false` to disable.
  193 + */
HITGNC   194 + 6 explicit byte_boolean_option(bool v) noexcept : value_(v ? 1 : 0) {}
  195 +
  196 + /// Assign a new value.
  197 + byte_boolean_option& operator=(bool v) noexcept
  198 + {
  199 + value_ = v ? 1 : 0;
  200 + return *this;
  201 + }
  202 +
  203 + /// Return the option value.
HITGNC   204 + 4 bool value() const noexcept
  205 + {
HITGNC   206 + 4 return value_ != 0;
  207 + }
  208 +
  209 + /// Return the option value.
  210 + explicit operator bool() const noexcept
  211 + {
  212 + return value_ != 0;
  213 + }
  214 +
  215 + /// Return the negated option value.
  216 + bool operator!() const noexcept
  217 + {
  218 + return value_ == 0;
  219 + }
  220 +
  221 + /// Return a pointer to the underlying storage.
HITGNC   222 + 4 void* data() noexcept
  223 + {
HITGNC   224 + 4 return &value_;
  225 + }
  226 +
  227 + /// Return a pointer to the underlying storage.
HITGNC   228 + 6 void const* data() const noexcept
  229 + {
HITGNC   230 + 6 return &value_;
  231 + }
  232 +
  233 + /// Return the size of the underlying storage.
HITGNC   234 + 10 std::size_t size() const noexcept
  235 + {
HITGNC   236 + 10 return sizeof(value_);
  237 + }
  238 +
  239 + /// Storage is already one byte; no normalization needed.
HITGNC   240 + 4 void resize(std::size_t) noexcept {}
  241 + };
  242 +
  243 + /** Base class for concrete integer socket options with single-byte storage.
  244 +
  245 + Same rationale as `byte_boolean_option`: BSD-derived kernels require
  246 + `IP_MULTICAST_TTL` to be set with a one-byte value. Linux accepts
  247 + one-byte too, so single-byte storage is portable.
  248 + */
  249 + class byte_integer_option
  250 + {
  251 + unsigned char value_ = 0;
  252 +
  253 + public:
  254 + /// Construct with default value (zero).
  255 + byte_integer_option() = default;
  256 +
  257 + /** Construct with an explicit value.
  258 +
  259 + @param v The option value; truncated to one byte.
  260 + */
HITGNC   261 + 2 explicit byte_integer_option(int v) noexcept
HITGNC   262 + 2 : value_(static_cast<unsigned char>(v))
HITGNC   263 + 2 {}
  264 +
  265 + /// Assign a new value; truncated to one byte.
  266 + byte_integer_option& operator=(int v) noexcept
  267 + {
  268 + value_ = static_cast<unsigned char>(v);
  269 + return *this;
  270 + }
  271 +
  272 + /// Return the option value.
HITGNC   273 + 2 int value() const noexcept
  274 + {
HITGNC   275 + 2 return value_;
  276 + }
  277 +
  278 + /// Return a pointer to the underlying storage.
HITGNC   279 + 2 void* data() noexcept
  280 + {
HITGNC   281 + 2 return &value_;
  282 + }
  283 +
  284 + /// Return a pointer to the underlying storage.
HITGNC   285 + 2 void const* data() const noexcept
  286 + {
HITGNC   287 + 2 return &value_;
  288 + }
  289 +
  290 + /// Return the size of the underlying storage.
HITGNC   291 + 4 std::size_t size() const noexcept
  292 + {
HITGNC   293 + 4 return sizeof(value_);
  294 + }
  295 +
  296 + /// Storage is already one byte; no normalization needed.
HITGNC   297 + 2 void resize(std::size_t) noexcept {}
  298 + };
  299 +
175   /** Disable Nagle's algorithm (TCP_NODELAY). 300   /** Disable Nagle's algorithm (TCP_NODELAY).
176   301  
177   @par Example 302   @par Example
178   @code 303   @code
179   sock.set_option( socket_option::no_delay( true ) ); 304   sock.set_option( socket_option::no_delay( true ) );
180   auto nd = sock.get_option<socket_option::no_delay>(); 305   auto nd = sock.get_option<socket_option::no_delay>();
181   if ( nd.value() ) 306   if ( nd.value() )
182   // Nagle's algorithm is disabled 307   // Nagle's algorithm is disabled
183   @endcode 308   @endcode
184   */ 309   */
185   class BOOST_COROSIO_DECL no_delay : public boolean_option 310   class BOOST_COROSIO_DECL no_delay : public boolean_option
186   { 311   {
187   public: 312   public:
188   using boolean_option::boolean_option; 313   using boolean_option::boolean_option;
189   using boolean_option::operator=; 314   using boolean_option::operator=;
190   315  
191   /// Return the protocol level. 316   /// Return the protocol level.
192   static int level() noexcept; 317   static int level() noexcept;
193   318  
194   /// Return the option name. 319   /// Return the option name.
195   static int name() noexcept; 320   static int name() noexcept;
196   }; 321   };
197   322  
198   /** Enable periodic keepalive probes (SO_KEEPALIVE). 323   /** Enable periodic keepalive probes (SO_KEEPALIVE).
199   324  
200   @par Example 325   @par Example
201   @code 326   @code
202   sock.set_option( socket_option::keep_alive( true ) ); 327   sock.set_option( socket_option::keep_alive( true ) );
203   @endcode 328   @endcode
204   */ 329   */
205   class BOOST_COROSIO_DECL keep_alive : public boolean_option 330   class BOOST_COROSIO_DECL keep_alive : public boolean_option
206   { 331   {
207   public: 332   public:
208   using boolean_option::boolean_option; 333   using boolean_option::boolean_option;
209   using boolean_option::operator=; 334   using boolean_option::operator=;
210   335  
211   /// Return the protocol level. 336   /// Return the protocol level.
212   static int level() noexcept; 337   static int level() noexcept;
213   338  
214   /// Return the option name. 339   /// Return the option name.
215   static int name() noexcept; 340   static int name() noexcept;
216   }; 341   };
217   342  
218   /** Restrict an IPv6 socket to IPv6 only (IPV6_V6ONLY). 343   /** Restrict an IPv6 socket to IPv6 only (IPV6_V6ONLY).
219   344  
220   When enabled, the socket only accepts IPv6 connections. 345   When enabled, the socket only accepts IPv6 connections.
221   When disabled, the socket accepts both IPv4 and IPv6 346   When disabled, the socket accepts both IPv4 and IPv6
222   connections (dual-stack mode). 347   connections (dual-stack mode).
223   348  
224   @par Example 349   @par Example
225   @code 350   @code
226   sock.set_option( socket_option::v6_only( true ) ); 351   sock.set_option( socket_option::v6_only( true ) );
227   @endcode 352   @endcode
228   */ 353   */
229   class BOOST_COROSIO_DECL v6_only : public boolean_option 354   class BOOST_COROSIO_DECL v6_only : public boolean_option
230   { 355   {
231   public: 356   public:
232   using boolean_option::boolean_option; 357   using boolean_option::boolean_option;
233   using boolean_option::operator=; 358   using boolean_option::operator=;
234   359  
235   /// Return the protocol level. 360   /// Return the protocol level.
236   static int level() noexcept; 361   static int level() noexcept;
237   362  
238   /// Return the option name. 363   /// Return the option name.
239   static int name() noexcept; 364   static int name() noexcept;
240   }; 365   };
241   366  
242   /** Allow local address reuse (SO_REUSEADDR). 367   /** Allow local address reuse (SO_REUSEADDR).
243   368  
244   @par Example 369   @par Example
245   @code 370   @code
246   acc.set_option( socket_option::reuse_address( true ) ); 371   acc.set_option( socket_option::reuse_address( true ) );
247   @endcode 372   @endcode
248   */ 373   */
249   class BOOST_COROSIO_DECL reuse_address : public boolean_option 374   class BOOST_COROSIO_DECL reuse_address : public boolean_option
250   { 375   {
251   public: 376   public:
252   using boolean_option::boolean_option; 377   using boolean_option::boolean_option;
253   using boolean_option::operator=; 378   using boolean_option::operator=;
254   379  
255   /// Return the protocol level. 380   /// Return the protocol level.
256   static int level() noexcept; 381   static int level() noexcept;
257   382  
258   /// Return the option name. 383   /// Return the option name.
259   static int name() noexcept; 384   static int name() noexcept;
260   }; 385   };
261   386  
262   /** Allow sending to broadcast addresses (SO_BROADCAST). 387   /** Allow sending to broadcast addresses (SO_BROADCAST).
263   388  
264   Required for UDP sockets that send to broadcast addresses 389   Required for UDP sockets that send to broadcast addresses
265   such as 255.255.255.255. Without this option, `send_to` 390   such as 255.255.255.255. Without this option, `send_to`
266   returns an error. 391   returns an error.
267   392  
268   @par Example 393   @par Example
269   @code 394   @code
270   udp_socket sock( ioc ); 395   udp_socket sock( ioc );
271   sock.open(); 396   sock.open();
272   sock.set_option( socket_option::broadcast( true ) ); 397   sock.set_option( socket_option::broadcast( true ) );
273   @endcode 398   @endcode
274   */ 399   */
275   class BOOST_COROSIO_DECL broadcast : public boolean_option 400   class BOOST_COROSIO_DECL broadcast : public boolean_option
276   { 401   {
277   public: 402   public:
278   using boolean_option::boolean_option; 403   using boolean_option::boolean_option;
279   using boolean_option::operator=; 404   using boolean_option::operator=;
280   405  
281   /// Return the protocol level. 406   /// Return the protocol level.
282   static int level() noexcept; 407   static int level() noexcept;
283   408  
284   /// Return the option name. 409   /// Return the option name.
285   static int name() noexcept; 410   static int name() noexcept;
286   }; 411   };
287   412  
288   /** Allow multiple sockets to bind to the same port (SO_REUSEPORT). 413   /** Allow multiple sockets to bind to the same port (SO_REUSEPORT).
289   414  
290   Not available on all platforms. On unsupported platforms, 415   Not available on all platforms. On unsupported platforms,
291   `set_option` will return an error. 416   `set_option` will return an error.
292   417  
293   @par Example 418   @par Example
294   @code 419   @code
295   acc.open( tcp::v6() ); 420   acc.open( tcp::v6() );
296   acc.set_option( socket_option::reuse_port( true ) ); 421   acc.set_option( socket_option::reuse_port( true ) );
297   acc.bind( endpoint( ipv6_address::any(), 8080 ) ); 422   acc.bind( endpoint( ipv6_address::any(), 8080 ) );
298   acc.listen(); 423   acc.listen();
299   @endcode 424   @endcode
300   */ 425   */
301   class BOOST_COROSIO_DECL reuse_port : public boolean_option 426   class BOOST_COROSIO_DECL reuse_port : public boolean_option
302   { 427   {
303   public: 428   public:
304   using boolean_option::boolean_option; 429   using boolean_option::boolean_option;
305   using boolean_option::operator=; 430   using boolean_option::operator=;
306   431  
307   /// Return the protocol level. 432   /// Return the protocol level.
308   static int level() noexcept; 433   static int level() noexcept;
309   434  
310   /// Return the option name. 435   /// Return the option name.
311   static int name() noexcept; 436   static int name() noexcept;
312   }; 437   };
313   438  
314   /** Set the receive buffer size (SO_RCVBUF). 439   /** Set the receive buffer size (SO_RCVBUF).
315   440  
316   @par Example 441   @par Example
317   @code 442   @code
318   sock.set_option( socket_option::receive_buffer_size( 65536 ) ); 443   sock.set_option( socket_option::receive_buffer_size( 65536 ) );
319   auto opt = sock.get_option<socket_option::receive_buffer_size>(); 444   auto opt = sock.get_option<socket_option::receive_buffer_size>();
320   int sz = opt.value(); 445   int sz = opt.value();
321   @endcode 446   @endcode
322   */ 447   */
323   class BOOST_COROSIO_DECL receive_buffer_size : public integer_option 448   class BOOST_COROSIO_DECL receive_buffer_size : public integer_option
324   { 449   {
325   public: 450   public:
326   using integer_option::integer_option; 451   using integer_option::integer_option;
327   using integer_option::operator=; 452   using integer_option::operator=;
328   453  
329   /// Return the protocol level. 454   /// Return the protocol level.
330   static int level() noexcept; 455   static int level() noexcept;
331   456  
332   /// Return the option name. 457   /// Return the option name.
333   static int name() noexcept; 458   static int name() noexcept;
334   }; 459   };
335   460  
336   /** Set the send buffer size (SO_SNDBUF). 461   /** Set the send buffer size (SO_SNDBUF).
337   462  
338   @par Example 463   @par Example
339   @code 464   @code
340   sock.set_option( socket_option::send_buffer_size( 65536 ) ); 465   sock.set_option( socket_option::send_buffer_size( 65536 ) );
341   @endcode 466   @endcode
342   */ 467   */
343   class BOOST_COROSIO_DECL send_buffer_size : public integer_option 468   class BOOST_COROSIO_DECL send_buffer_size : public integer_option
344   { 469   {
345   public: 470   public:
346   using integer_option::integer_option; 471   using integer_option::integer_option;
347   using integer_option::operator=; 472   using integer_option::operator=;
348   473  
349   /// Return the protocol level. 474   /// Return the protocol level.
350   static int level() noexcept; 475   static int level() noexcept;
351   476  
352   /// Return the option name. 477   /// Return the option name.
353   static int name() noexcept; 478   static int name() noexcept;
354   }; 479   };
355   480  
356   /** The SO_LINGER socket option. 481   /** The SO_LINGER socket option.
357   482  
358   Controls behavior when closing a socket with unsent data. 483   Controls behavior when closing a socket with unsent data.
359   When enabled, `close()` blocks until pending data is sent 484   When enabled, `close()` blocks until pending data is sent
360   or the timeout expires. 485   or the timeout expires.
361   486  
362   @par Example 487   @par Example
363   @code 488   @code
364   sock.set_option( socket_option::linger( true, 5 ) ); 489   sock.set_option( socket_option::linger( true, 5 ) );
365   auto opt = sock.get_option<socket_option::linger>(); 490   auto opt = sock.get_option<socket_option::linger>();
366   if ( opt.enabled() ) 491   if ( opt.enabled() )
367   std::cout << "linger timeout: " << opt.timeout() << "s\n"; 492   std::cout << "linger timeout: " << opt.timeout() << "s\n";
368   @endcode 493   @endcode
369   */ 494   */
370   class BOOST_COROSIO_DECL linger 495   class BOOST_COROSIO_DECL linger
371   { 496   {
372   // Opaque storage for the platform's struct linger. 497   // Opaque storage for the platform's struct linger.
373   // POSIX: { int, int } = 8 bytes. 498   // POSIX: { int, int } = 8 bytes.
374   // Windows: { u_short, u_short } = 4 bytes. 499   // Windows: { u_short, u_short } = 4 bytes.
375   static constexpr std::size_t max_storage_ = 8; 500   static constexpr std::size_t max_storage_ = 8;
376   alignas(4) unsigned char storage_[max_storage_]{}; 501   alignas(4) unsigned char storage_[max_storage_]{};
377   502  
378   public: 503   public:
379   /// Construct with default values (disabled, zero timeout). 504   /// Construct with default values (disabled, zero timeout).
380   linger() noexcept = default; 505   linger() noexcept = default;
381   506  
382   /** Construct with explicit values. 507   /** Construct with explicit values.
383   508  
384   @param enabled `true` to enable linger behavior on close. 509   @param enabled `true` to enable linger behavior on close.
385   @param timeout The linger timeout in seconds. 510   @param timeout The linger timeout in seconds.
386   */ 511   */
387   linger(bool enabled, int timeout) noexcept; 512   linger(bool enabled, int timeout) noexcept;
388   513  
389   /// Return whether linger is enabled. 514   /// Return whether linger is enabled.
390   bool enabled() const noexcept; 515   bool enabled() const noexcept;
391   516  
392   /// Set whether linger is enabled. 517   /// Set whether linger is enabled.
393   void enabled(bool v) noexcept; 518   void enabled(bool v) noexcept;
394   519  
395   /// Return the linger timeout in seconds. 520   /// Return the linger timeout in seconds.
396   int timeout() const noexcept; 521   int timeout() const noexcept;
397   522  
398   /// Set the linger timeout in seconds. 523   /// Set the linger timeout in seconds.
399   void timeout(int v) noexcept; 524   void timeout(int v) noexcept;
400   525  
401   /// Return the protocol level. 526   /// Return the protocol level.
402   static int level() noexcept; 527   static int level() noexcept;
403   528  
404   /// Return the option name. 529   /// Return the option name.
405   static int name() noexcept; 530   static int name() noexcept;
406   531  
407   /// Return a pointer to the underlying storage. 532   /// Return a pointer to the underlying storage.
HITCBC 408   10 void* data() noexcept 533   10 void* data() noexcept
409   { 534   {
HITCBC 410   10 return storage_; 535   10 return storage_;
411   } 536   }
412   537  
413   /// Return a pointer to the underlying storage. 538   /// Return a pointer to the underlying storage.
HITCBC 414   30 void const* data() const noexcept 539   34 void const* data() const noexcept
415   { 540   {
HITCBC 416   30 return storage_; 541   34 return storage_;
417   } 542   }
418   543  
419   /// Return the size of the underlying storage. 544   /// Return the size of the underlying storage.
420   std::size_t size() const noexcept; 545   std::size_t size() const noexcept;
421   546  
422   /** Normalize after `getsockopt`. 547   /** Normalize after `getsockopt`.
423   548  
424   No-op — `struct linger` is always returned at full size. 549   No-op — `struct linger` is always returned at full size.
425   550  
426   @param s The number of bytes actually written by `getsockopt`. 551   @param s The number of bytes actually written by `getsockopt`.
427   */ 552   */
HITCBC 428   10 void resize(std::size_t) noexcept {} 553   10 void resize(std::size_t) noexcept {}
429   }; 554   };
430   555  
431   /** Enable loopback of outgoing multicast on IPv4 (IP_MULTICAST_LOOP). 556   /** Enable loopback of outgoing multicast on IPv4 (IP_MULTICAST_LOOP).
432   557  
  558 + Uses single-byte storage because BSD-derived kernels (macOS, FreeBSD)
  559 + reject the four-byte form with `EINVAL`. Linux accepts either size.
  560 +
433   @par Example 561   @par Example
434   @code 562   @code
435   sock.set_option( socket_option::multicast_loop_v4( true ) ); 563   sock.set_option( socket_option::multicast_loop_v4( true ) );
436   @endcode 564   @endcode
437   */ 565   */
438 - class BOOST_COROSIO_DECL multicast_loop_v4 : public boolean_option 566 + class BOOST_COROSIO_DECL multicast_loop_v4 : public byte_boolean_option
439   { 567   {
440   public: 568   public:
441 - using boolean_option::boolean_option; 569 + using byte_boolean_option::byte_boolean_option;
442 - using boolean_option::operator=; 570 + using byte_boolean_option::operator=;
443   571  
444   /// Return the protocol level. 572   /// Return the protocol level.
445   static int level() noexcept; 573   static int level() noexcept;
446   574  
447   /// Return the option name. 575   /// Return the option name.
448   static int name() noexcept; 576   static int name() noexcept;
449   }; 577   };
450   578  
451   /** Enable loopback of outgoing multicast on IPv6 (IPV6_MULTICAST_LOOP). 579   /** Enable loopback of outgoing multicast on IPv6 (IPV6_MULTICAST_LOOP).
452   580  
453   @par Example 581   @par Example
454   @code 582   @code
455   sock.set_option( socket_option::multicast_loop_v6( true ) ); 583   sock.set_option( socket_option::multicast_loop_v6( true ) );
456   @endcode 584   @endcode
457   */ 585   */
458   class BOOST_COROSIO_DECL multicast_loop_v6 : public boolean_option 586   class BOOST_COROSIO_DECL multicast_loop_v6 : public boolean_option
459   { 587   {
460   public: 588   public:
461   using boolean_option::boolean_option; 589   using boolean_option::boolean_option;
462   using boolean_option::operator=; 590   using boolean_option::operator=;
463   591  
464   /// Return the protocol level. 592   /// Return the protocol level.
465   static int level() noexcept; 593   static int level() noexcept;
466   594  
467   /// Return the option name. 595   /// Return the option name.
468   static int name() noexcept; 596   static int name() noexcept;
469   }; 597   };
470   598  
471   /** Set the multicast TTL for IPv4 (IP_MULTICAST_TTL). 599   /** Set the multicast TTL for IPv4 (IP_MULTICAST_TTL).
472   600  
  601 + Uses single-byte storage because BSD-derived kernels (macOS, FreeBSD)
  602 + reject the four-byte form with `EINVAL`. Linux accepts either size.
  603 + Values are truncated to the 0–255 range.
  604 +
473   @par Example 605   @par Example
474   @code 606   @code
475   sock.set_option( socket_option::multicast_hops_v4( 4 ) ); 607   sock.set_option( socket_option::multicast_hops_v4( 4 ) );
476   @endcode 608   @endcode
477   */ 609   */
478 - class BOOST_COROSIO_DECL multicast_hops_v4 : public integer_option 610 + class BOOST_COROSIO_DECL multicast_hops_v4 : public byte_integer_option
479   { 611   {
480   public: 612   public:
481 - using integer_option::integer_option; 613 + using byte_integer_option::byte_integer_option;
482 - using integer_option::operator=; 614 + using byte_integer_option::operator=;
483   615  
484   /// Return the protocol level. 616   /// Return the protocol level.
485   static int level() noexcept; 617   static int level() noexcept;
486   618  
487   /// Return the option name. 619   /// Return the option name.
488   static int name() noexcept; 620   static int name() noexcept;
489   }; 621   };
490   622  
491   /** Set the multicast hop limit for IPv6 (IPV6_MULTICAST_HOPS). 623   /** Set the multicast hop limit for IPv6 (IPV6_MULTICAST_HOPS).
492   624  
493   @par Example 625   @par Example
494   @code 626   @code
495   sock.set_option( socket_option::multicast_hops_v6( 4 ) ); 627   sock.set_option( socket_option::multicast_hops_v6( 4 ) );
496   @endcode 628   @endcode
497   */ 629   */
498   class BOOST_COROSIO_DECL multicast_hops_v6 : public integer_option 630   class BOOST_COROSIO_DECL multicast_hops_v6 : public integer_option
499   { 631   {
500   public: 632   public:
501   using integer_option::integer_option; 633   using integer_option::integer_option;
502   using integer_option::operator=; 634   using integer_option::operator=;
503   635  
504   /// Return the protocol level. 636   /// Return the protocol level.
505   static int level() noexcept; 637   static int level() noexcept;
506   638  
507   /// Return the option name. 639   /// Return the option name.
508   static int name() noexcept; 640   static int name() noexcept;
509   }; 641   };
510   642  
511   /** Set the outgoing interface for IPv6 multicast (IPV6_MULTICAST_IF). 643   /** Set the outgoing interface for IPv6 multicast (IPV6_MULTICAST_IF).
512   644  
513   @par Example 645   @par Example
514   @code 646   @code
515   sock.set_option( socket_option::multicast_interface_v6( 1 ) ); 647   sock.set_option( socket_option::multicast_interface_v6( 1 ) );
516   @endcode 648   @endcode
517   */ 649   */
518   class BOOST_COROSIO_DECL multicast_interface_v6 : public integer_option 650   class BOOST_COROSIO_DECL multicast_interface_v6 : public integer_option
519   { 651   {
520   public: 652   public:
521   using integer_option::integer_option; 653   using integer_option::integer_option;
522   using integer_option::operator=; 654   using integer_option::operator=;
523   655  
524   /// Return the protocol level. 656   /// Return the protocol level.
525   static int level() noexcept; 657   static int level() noexcept;
526   658  
527   /// Return the option name. 659   /// Return the option name.
528   static int name() noexcept; 660   static int name() noexcept;
529   }; 661   };
530   662  
531   /** Join an IPv4 multicast group (IP_ADD_MEMBERSHIP). 663   /** Join an IPv4 multicast group (IP_ADD_MEMBERSHIP).
532   664  
533   @par Example 665   @par Example
534   @code 666   @code
535   sock.set_option( socket_option::join_group_v4( 667   sock.set_option( socket_option::join_group_v4(
536   ipv4_address( "239.255.0.1" ) ) ); 668   ipv4_address( "239.255.0.1" ) ) );
537   @endcode 669   @endcode
538   */ 670   */
539   class BOOST_COROSIO_DECL join_group_v4 671   class BOOST_COROSIO_DECL join_group_v4
540   { 672   {
541   static constexpr std::size_t max_storage_ = 8; 673   static constexpr std::size_t max_storage_ = 8;
542   alignas(4) unsigned char storage_[max_storage_]{}; 674   alignas(4) unsigned char storage_[max_storage_]{};
543   675  
544   public: 676   public:
545   /// Construct with default values. 677   /// Construct with default values.
546   join_group_v4() noexcept = default; 678   join_group_v4() noexcept = default;
547   679  
548   /** Construct with a group and optional interface address. 680   /** Construct with a group and optional interface address.
549   681  
550   @param group The multicast group address to join. 682   @param group The multicast group address to join.
551   @param iface The local interface to use (default: any). 683   @param iface The local interface to use (default: any).
552   */ 684   */
553   join_group_v4( 685   join_group_v4(
554   ipv4_address group, ipv4_address iface = ipv4_address()) noexcept; 686   ipv4_address group, ipv4_address iface = ipv4_address()) noexcept;
555   687  
556   /// Return the protocol level. 688   /// Return the protocol level.
557   static int level() noexcept; 689   static int level() noexcept;
558   690  
559   /// Return the option name. 691   /// Return the option name.
560   static int name() noexcept; 692   static int name() noexcept;
561   693  
562   /// Return a pointer to the underlying storage. 694   /// Return a pointer to the underlying storage.
563   void* data() noexcept 695   void* data() noexcept
564   { 696   {
565   return storage_; 697   return storage_;
566   } 698   }
567   699  
568   /// Return a pointer to the underlying storage. 700   /// Return a pointer to the underlying storage.
HITCBC 569   2 void const* data() const noexcept 701   4 void const* data() const noexcept
570   { 702   {
HITCBC 571   2 return storage_; 703   4 return storage_;
572   } 704   }
573   705  
574   /// Return the size of the underlying storage. 706   /// Return the size of the underlying storage.
575   std::size_t size() const noexcept; 707   std::size_t size() const noexcept;
576   708  
577   /// No-op resize. 709   /// No-op resize.
578   void resize(std::size_t) noexcept {} 710   void resize(std::size_t) noexcept {}
579   }; 711   };
580   712  
581   /** Leave an IPv4 multicast group (IP_DROP_MEMBERSHIP). 713   /** Leave an IPv4 multicast group (IP_DROP_MEMBERSHIP).
582   714  
583   @par Example 715   @par Example
584   @code 716   @code
585   sock.set_option( socket_option::leave_group_v4( 717   sock.set_option( socket_option::leave_group_v4(
586   ipv4_address( "239.255.0.1" ) ) ); 718   ipv4_address( "239.255.0.1" ) ) );
587   @endcode 719   @endcode
588   */ 720   */
589   class BOOST_COROSIO_DECL leave_group_v4 721   class BOOST_COROSIO_DECL leave_group_v4
590   { 722   {
591   static constexpr std::size_t max_storage_ = 8; 723   static constexpr std::size_t max_storage_ = 8;
592   alignas(4) unsigned char storage_[max_storage_]{}; 724   alignas(4) unsigned char storage_[max_storage_]{};
593   725  
594   public: 726   public:
595   /// Construct with default values. 727   /// Construct with default values.
596   leave_group_v4() noexcept = default; 728   leave_group_v4() noexcept = default;
597   729  
598   /** Construct with a group and optional interface address. 730   /** Construct with a group and optional interface address.
599   731  
600   @param group The multicast group address to leave. 732   @param group The multicast group address to leave.
601   @param iface The local interface (default: any). 733   @param iface The local interface (default: any).
602   */ 734   */
603   leave_group_v4( 735   leave_group_v4(
604   ipv4_address group, ipv4_address iface = ipv4_address()) noexcept; 736   ipv4_address group, ipv4_address iface = ipv4_address()) noexcept;
605   737  
606   /// Return the protocol level. 738   /// Return the protocol level.
607   static int level() noexcept; 739   static int level() noexcept;
608   740  
609   /// Return the option name. 741   /// Return the option name.
610   static int name() noexcept; 742   static int name() noexcept;
611   743  
612   /// Return a pointer to the underlying storage. 744   /// Return a pointer to the underlying storage.
613   void* data() noexcept 745   void* data() noexcept
614   { 746   {
615   return storage_; 747   return storage_;
616   } 748   }
617   749  
618   /// Return a pointer to the underlying storage. 750   /// Return a pointer to the underlying storage.
HITGIC 619   void const* data() const noexcept 751   2 void const* data() const noexcept
620   { 752   {
HITGIC 621   return storage_; 753   2 return storage_;
622   } 754   }
623   755  
624   /// Return the size of the underlying storage. 756   /// Return the size of the underlying storage.
625   std::size_t size() const noexcept; 757   std::size_t size() const noexcept;
626   758  
627   /// No-op resize. 759   /// No-op resize.
628   void resize(std::size_t) noexcept {} 760   void resize(std::size_t) noexcept {}
629   }; 761   };
630   762  
631   /** Join an IPv6 multicast group (IPV6_JOIN_GROUP). 763   /** Join an IPv6 multicast group (IPV6_JOIN_GROUP).
632   764  
633   @par Example 765   @par Example
634   @code 766   @code
635   sock.set_option( socket_option::join_group_v6( 767   sock.set_option( socket_option::join_group_v6(
636   ipv6_address( "ff02::1" ), 0 ) ); 768   ipv6_address( "ff02::1" ), 0 ) );
637   @endcode 769   @endcode
638   */ 770   */
639   class BOOST_COROSIO_DECL join_group_v6 771   class BOOST_COROSIO_DECL join_group_v6
640   { 772   {
641   static constexpr std::size_t max_storage_ = 20; 773   static constexpr std::size_t max_storage_ = 20;
642   alignas(4) unsigned char storage_[max_storage_]{}; 774   alignas(4) unsigned char storage_[max_storage_]{};
643   775  
644   public: 776   public:
645   /// Construct with default values. 777   /// Construct with default values.
646   join_group_v6() noexcept = default; 778   join_group_v6() noexcept = default;
647   779  
648   /** Construct with a group and optional interface index. 780   /** Construct with a group and optional interface index.
649   781  
650   @param group The multicast group address to join. 782   @param group The multicast group address to join.
651   @param if_index The interface index (0 = kernel chooses). 783   @param if_index The interface index (0 = kernel chooses).
652   */ 784   */
653   join_group_v6(ipv6_address group, unsigned int if_index = 0) noexcept; 785   join_group_v6(ipv6_address group, unsigned int if_index = 0) noexcept;
654   786  
655   /// Return the protocol level. 787   /// Return the protocol level.
656   static int level() noexcept; 788   static int level() noexcept;
657   789  
658   /// Return the option name. 790   /// Return the option name.
659   static int name() noexcept; 791   static int name() noexcept;
660   792  
661   /// Return a pointer to the underlying storage. 793   /// Return a pointer to the underlying storage.
662   void* data() noexcept 794   void* data() noexcept
663   { 795   {
664   return storage_; 796   return storage_;
665   } 797   }
666   798  
667   /// Return a pointer to the underlying storage. 799   /// Return a pointer to the underlying storage.
HITGIC 668   void const* data() const noexcept 800   2 void const* data() const noexcept
669   { 801   {
HITGIC 670   return storage_; 802   2 return storage_;
671   } 803   }
672   804  
673   /// Return the size of the underlying storage. 805   /// Return the size of the underlying storage.
674   std::size_t size() const noexcept; 806   std::size_t size() const noexcept;
675   807  
676   /// No-op resize. 808   /// No-op resize.
677   void resize(std::size_t) noexcept {} 809   void resize(std::size_t) noexcept {}
678   }; 810   };
679   811  
680   /** Leave an IPv6 multicast group (IPV6_LEAVE_GROUP). 812   /** Leave an IPv6 multicast group (IPV6_LEAVE_GROUP).
681   813  
682   @par Example 814   @par Example
683   @code 815   @code
684   sock.set_option( socket_option::leave_group_v6( 816   sock.set_option( socket_option::leave_group_v6(
685   ipv6_address( "ff02::1" ), 0 ) ); 817   ipv6_address( "ff02::1" ), 0 ) );
686   @endcode 818   @endcode
687   */ 819   */
688   class BOOST_COROSIO_DECL leave_group_v6 820   class BOOST_COROSIO_DECL leave_group_v6
689   { 821   {
690   static constexpr std::size_t max_storage_ = 20; 822   static constexpr std::size_t max_storage_ = 20;
691   alignas(4) unsigned char storage_[max_storage_]{}; 823   alignas(4) unsigned char storage_[max_storage_]{};
692   824  
693   public: 825   public:
694   /// Construct with default values. 826   /// Construct with default values.
695   leave_group_v6() noexcept = default; 827   leave_group_v6() noexcept = default;
696   828  
697   /** Construct with a group and optional interface index. 829   /** Construct with a group and optional interface index.
698   830  
699   @param group The multicast group address to leave. 831   @param group The multicast group address to leave.
700   @param if_index The interface index (0 = kernel chooses). 832   @param if_index The interface index (0 = kernel chooses).
701   */ 833   */
702   leave_group_v6(ipv6_address group, unsigned int if_index = 0) noexcept; 834   leave_group_v6(ipv6_address group, unsigned int if_index = 0) noexcept;
703   835  
704   /// Return the protocol level. 836   /// Return the protocol level.
705   static int level() noexcept; 837   static int level() noexcept;
706   838  
707   /// Return the option name. 839   /// Return the option name.
708   static int name() noexcept; 840   static int name() noexcept;
709   841  
710   /// Return a pointer to the underlying storage. 842   /// Return a pointer to the underlying storage.
711   void* data() noexcept 843   void* data() noexcept
712   { 844   {
713   return storage_; 845   return storage_;
714   } 846   }
715   847  
716   /// Return a pointer to the underlying storage. 848   /// Return a pointer to the underlying storage.
HITGIC 717   void const* data() const noexcept 849   2 void const* data() const noexcept
718   { 850   {
HITGIC 719   return storage_; 851   2 return storage_;
720   } 852   }
721   853  
722   /// Return the size of the underlying storage. 854   /// Return the size of the underlying storage.
723   std::size_t size() const noexcept; 855   std::size_t size() const noexcept;
724   856  
725   /// No-op resize. 857   /// No-op resize.
726   void resize(std::size_t) noexcept {} 858   void resize(std::size_t) noexcept {}
727   }; 859   };
728   860  
729   /** Set the outgoing interface for IPv4 multicast (IP_MULTICAST_IF). 861   /** Set the outgoing interface for IPv4 multicast (IP_MULTICAST_IF).
730   862  
731   Unlike the integer-based `multicast_interface_v6`, this option 863   Unlike the integer-based `multicast_interface_v6`, this option
732   takes an `ipv4_address` identifying the local interface. 864   takes an `ipv4_address` identifying the local interface.
733   865  
734   @par Example 866   @par Example
735   @code 867   @code
736   sock.set_option( socket_option::multicast_interface_v4( 868   sock.set_option( socket_option::multicast_interface_v4(
737   ipv4_address( "192.168.1.1" ) ) ); 869   ipv4_address( "192.168.1.1" ) ) );
738   @endcode 870   @endcode
739   */ 871   */
740   class BOOST_COROSIO_DECL multicast_interface_v4 872   class BOOST_COROSIO_DECL multicast_interface_v4
741   { 873   {
742   static constexpr std::size_t max_storage_ = 4; 874   static constexpr std::size_t max_storage_ = 4;
743   alignas(4) unsigned char storage_[max_storage_]{}; 875   alignas(4) unsigned char storage_[max_storage_]{};
744   876  
745   public: 877   public:
746   /// Construct with default values (INADDR_ANY). 878   /// Construct with default values (INADDR_ANY).
747   multicast_interface_v4() noexcept = default; 879   multicast_interface_v4() noexcept = default;
748   880  
749   /** Construct with an interface address. 881   /** Construct with an interface address.
750   882  
751   @param iface The local interface address. 883   @param iface The local interface address.
752   */ 884   */
753   explicit multicast_interface_v4(ipv4_address iface) noexcept; 885   explicit multicast_interface_v4(ipv4_address iface) noexcept;
754   886  
755   /// Return the protocol level. 887   /// Return the protocol level.
756   static int level() noexcept; 888   static int level() noexcept;
757   889  
758   /// Return the option name. 890   /// Return the option name.
759   static int name() noexcept; 891   static int name() noexcept;
760   892  
761   /// Return a pointer to the underlying storage. 893   /// Return a pointer to the underlying storage.
762   void* data() noexcept 894   void* data() noexcept
763   { 895   {
764   return storage_; 896   return storage_;
765   } 897   }
766   898  
767   /// Return a pointer to the underlying storage. 899   /// Return a pointer to the underlying storage.
HITGIC 768   void const* data() const noexcept 900   2 void const* data() const noexcept
769   { 901   {
HITGIC 770   return storage_; 902   2 return storage_;
771   } 903   }
772   904  
773   /// Return the size of the underlying storage. 905   /// Return the size of the underlying storage.
774   std::size_t size() const noexcept; 906   std::size_t size() const noexcept;
775   907  
776   /// No-op resize. 908   /// No-op resize.
777   void resize(std::size_t) noexcept {} 909   void resize(std::size_t) noexcept {}
778   }; 910   };
779   911  
780   } // namespace boost::corosio::socket_option 912   } // namespace boost::corosio::socket_option
781   913  
782   #endif // BOOST_COROSIO_SOCKET_OPTION_HPP 914   #endif // BOOST_COROSIO_SOCKET_OPTION_HPP