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