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