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