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