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