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