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