1  
//
1  
//
2  
// Copyright (c) 2026 Steve Gerbino
2  
// Copyright (c) 2026 Steve Gerbino
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/corosio
7  
// Official repository: https://github.com/cppalliance/corosio
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_COROSIO_SOCKET_OPTION_HPP
10  
#ifndef BOOST_COROSIO_SOCKET_OPTION_HPP
11  
#define BOOST_COROSIO_SOCKET_OPTION_HPP
11  
#define BOOST_COROSIO_SOCKET_OPTION_HPP
12  

12  

13  
#include <boost/corosio/detail/config.hpp>
13  
#include <boost/corosio/detail/config.hpp>
14  
#include <boost/corosio/ipv4_address.hpp>
14  
#include <boost/corosio/ipv4_address.hpp>
15  
#include <boost/corosio/ipv6_address.hpp>
15  
#include <boost/corosio/ipv6_address.hpp>
16  

16  

17  
#include <cstddef>
17  
#include <cstddef>
18  

18  

19  
/** @file socket_option.hpp
19  
/** @file socket_option.hpp
20  

20  

21  
    Type-erased socket option types that avoid platform-specific
21  
    Type-erased socket option types that avoid platform-specific
22  
    headers. The protocol level and option name for each type are
22  
    headers. The protocol level and option name for each type are
23  
    resolved at link time via the compiled library.
23  
    resolved at link time via the compiled library.
24  

24  

25  
    For an inline (zero-overhead) alternative that includes platform
25  
    For an inline (zero-overhead) alternative that includes platform
26  
    headers, use `<boost/corosio/native/native_socket_option.hpp>`
26  
    headers, use `<boost/corosio/native/native_socket_option.hpp>`
27  
    (`boost::corosio::native_socket_option`).
27  
    (`boost::corosio::native_socket_option`).
28  

28  

29  
    Both variants satisfy the same option-type interface and work
29  
    Both variants satisfy the same option-type interface and work
30  
    interchangeably with `tcp_socket::set_option` /
30  
    interchangeably with `tcp_socket::set_option` /
31  
    `tcp_socket::get_option` and the corresponding acceptor methods.
31  
    `tcp_socket::get_option` and the corresponding acceptor methods.
32  

32  

33  
    @see native_socket_option
33  
    @see native_socket_option
34  
*/
34  
*/
35  

35  

36  
namespace boost::corosio::socket_option {
36  
namespace boost::corosio::socket_option {
37  

37  

38  
/** Base class for concrete boolean socket options.
38  
/** Base class for concrete boolean socket options.
39  

39  

40  
    Stores a boolean as an `int` suitable for `setsockopt`/`getsockopt`.
40  
    Stores a boolean as an `int` suitable for `setsockopt`/`getsockopt`.
41  
    Derived types provide `level()` and `name()` for the specific option.
41  
    Derived types provide `level()` and `name()` for the specific option.
42  
*/
42  
*/
43  
class boolean_option
43  
class boolean_option
44  
{
44  
{
45  
    int value_ = 0;
45  
    int value_ = 0;
46  

46  

47  
public:
47  
public:
48  
    /// Construct with default value (disabled).
48  
    /// Construct with default value (disabled).
49  
    boolean_option() = default;
49  
    boolean_option() = default;
50  

50  

51  
    /** Construct with an explicit value.
51  
    /** Construct with an explicit value.
52  

52  

53  
        @param v `true` to enable the option, `false` to disable.
53  
        @param v `true` to enable the option, `false` to disable.
54  
    */
54  
    */
55  
    explicit boolean_option(bool v) noexcept : value_(v ? 1 : 0) {}
55  
    explicit boolean_option(bool v) noexcept : value_(v ? 1 : 0) {}
56  

56  

57  
    /// Assign a new value.
57  
    /// Assign a new value.
58  
    boolean_option& operator=(bool v) noexcept
58  
    boolean_option& operator=(bool v) noexcept
59  
    {
59  
    {
60  
        value_ = v ? 1 : 0;
60  
        value_ = v ? 1 : 0;
61  
        return *this;
61  
        return *this;
62  
    }
62  
    }
63  

63  

64  
    /// Return the option value.
64  
    /// Return the option value.
65  
    bool value() const noexcept
65  
    bool value() const noexcept
66  
    {
66  
    {
67  
        return value_ != 0;
67  
        return value_ != 0;
68  
    }
68  
    }
69  

69  

70  
    /// Return the option value.
70  
    /// Return the option value.
71  
    explicit operator bool() const noexcept
71  
    explicit operator bool() const noexcept
72  
    {
72  
    {
73  
        return value_ != 0;
73  
        return value_ != 0;
74  
    }
74  
    }
75  

75  

76  
    /// Return the negated option value.
76  
    /// Return the negated option value.
77  
    bool operator!() const noexcept
77  
    bool operator!() const noexcept
78  
    {
78  
    {
79  
        return value_ == 0;
79  
        return value_ == 0;
80  
    }
80  
    }
81  

81  

82  
    /// Return a pointer to the underlying storage.
82  
    /// Return a pointer to the underlying storage.
83  
    void* data() noexcept
83  
    void* data() noexcept
84  
    {
84  
    {
85  
        return &value_;
85  
        return &value_;
86  
    }
86  
    }
87  

87  

88  
    /// Return a pointer to the underlying storage.
88  
    /// Return a pointer to the underlying storage.
89  
    void const* data() const noexcept
89  
    void const* data() const noexcept
90  
    {
90  
    {
91  
        return &value_;
91  
        return &value_;
92  
    }
92  
    }
93  

93  

94  
    /// Return the size of the underlying storage.
94  
    /// Return the size of the underlying storage.
95  
    std::size_t size() const noexcept
95  
    std::size_t size() const noexcept
96  
    {
96  
    {
97  
        return sizeof(value_);
97  
        return sizeof(value_);
98  
    }
98  
    }
99  

99  

100  
    /** Normalize after `getsockopt` returns fewer bytes than expected.
100  
    /** Normalize after `getsockopt` returns fewer bytes than expected.
101  

101  

102  
        Windows Vista+ may write only 1 byte for boolean options.
102  
        Windows Vista+ may write only 1 byte for boolean options.
103  

103  

104  
        @param s The number of bytes actually written by `getsockopt`.
104  
        @param s The number of bytes actually written by `getsockopt`.
105  
    */
105  
    */
106  
    void resize(std::size_t s) noexcept
106  
    void resize(std::size_t s) noexcept
107  
    {
107  
    {
108  
        if (s == sizeof(char))
108  
        if (s == sizeof(char))
109  
            value_ = *reinterpret_cast<unsigned char*>(&value_) ? 1 : 0;
109  
            value_ = *reinterpret_cast<unsigned char*>(&value_) ? 1 : 0;
110  
    }
110  
    }
111  
};
111  
};
112  

112  

113  
/** Base class for concrete integer socket options.
113  
/** Base class for concrete integer socket options.
114  

114  

115  
    Stores an integer suitable for `setsockopt`/`getsockopt`.
115  
    Stores an integer suitable for `setsockopt`/`getsockopt`.
116  
    Derived types provide `level()` and `name()` for the specific option.
116  
    Derived types provide `level()` and `name()` for the specific option.
117  
*/
117  
*/
118  
class integer_option
118  
class integer_option
119  
{
119  
{
120  
    int value_ = 0;
120  
    int value_ = 0;
121  

121  

122  
public:
122  
public:
123  
    /// Construct with default value (zero).
123  
    /// Construct with default value (zero).
124  
    integer_option() = default;
124  
    integer_option() = default;
125  

125  

126  
    /** Construct with an explicit value.
126  
    /** Construct with an explicit value.
127  

127  

128  
        @param v The option value.
128  
        @param v The option value.
129  
    */
129  
    */
130  
    explicit integer_option(int v) noexcept : value_(v) {}
130  
    explicit integer_option(int v) noexcept : value_(v) {}
131  

131  

132  
    /// Assign a new value.
132  
    /// Assign a new value.
133  
    integer_option& operator=(int v) noexcept
133  
    integer_option& operator=(int v) noexcept
134  
    {
134  
    {
135  
        value_ = v;
135  
        value_ = v;
136  
        return *this;
136  
        return *this;
137  
    }
137  
    }
138  

138  

139  
    /// Return the option value.
139  
    /// Return the option value.
140  
    int value() const noexcept
140  
    int value() const noexcept
141  
    {
141  
    {
142  
        return value_;
142  
        return value_;
143  
    }
143  
    }
144  

144  

145  
    /// Return a pointer to the underlying storage.
145  
    /// Return a pointer to the underlying storage.
146  
    void* data() noexcept
146  
    void* data() noexcept
147  
    {
147  
    {
148  
        return &value_;
148  
        return &value_;
149  
    }
149  
    }
150  

150  

151  
    /// Return a pointer to the underlying storage.
151  
    /// Return a pointer to the underlying storage.
152  
    void const* data() const noexcept
152  
    void const* data() const noexcept
153  
    {
153  
    {
154  
        return &value_;
154  
        return &value_;
155  
    }
155  
    }
156  

156  

157  
    /// Return the size of the underlying storage.
157  
    /// Return the size of the underlying storage.
158  
    std::size_t size() const noexcept
158  
    std::size_t size() const noexcept
159  
    {
159  
    {
160  
        return sizeof(value_);
160  
        return sizeof(value_);
161  
    }
161  
    }
162  

162  

163  
    /** Normalize after `getsockopt` returns fewer bytes than expected.
163  
    /** Normalize after `getsockopt` returns fewer bytes than expected.
164  

164  

165  
        @param s The number of bytes actually written by `getsockopt`.
165  
        @param s The number of bytes actually written by `getsockopt`.
166  
    */
166  
    */
167  
    void resize(std::size_t s) noexcept
167  
    void resize(std::size_t s) noexcept
168  
    {
168  
    {
169  
        if (s == sizeof(char))
169  
        if (s == sizeof(char))
170  
            value_ =
170  
            value_ =
171  
                static_cast<int>(*reinterpret_cast<unsigned char*>(&value_));
171  
                static_cast<int>(*reinterpret_cast<unsigned char*>(&value_));
172  
    }
172  
    }
173  
};
173  
};
174  

174  

175  
/** Disable Nagle's algorithm (TCP_NODELAY).
175  
/** Disable Nagle's algorithm (TCP_NODELAY).
176  

176  

177  
    @par Example
177  
    @par Example
178  
    @code
178  
    @code
179  
    sock.set_option( socket_option::no_delay( true ) );
179  
    sock.set_option( socket_option::no_delay( true ) );
180  
    auto nd = sock.get_option<socket_option::no_delay>();
180  
    auto nd = sock.get_option<socket_option::no_delay>();
181  
    if ( nd.value() )
181  
    if ( nd.value() )
182  
        // Nagle's algorithm is disabled
182  
        // Nagle's algorithm is disabled
183  
    @endcode
183  
    @endcode
184  
*/
184  
*/
185  
class BOOST_COROSIO_DECL no_delay : public boolean_option
185  
class BOOST_COROSIO_DECL no_delay : public boolean_option
186  
{
186  
{
187  
public:
187  
public:
188  
    using boolean_option::boolean_option;
188  
    using boolean_option::boolean_option;
189  
    using boolean_option::operator=;
189  
    using boolean_option::operator=;
190  

190  

191  
    /// Return the protocol level.
191  
    /// Return the protocol level.
192  
    static int level() noexcept;
192  
    static int level() noexcept;
193  

193  

194  
    /// Return the option name.
194  
    /// Return the option name.
195  
    static int name() noexcept;
195  
    static int name() noexcept;
196  
};
196  
};
197  

197  

198  
/** Enable periodic keepalive probes (SO_KEEPALIVE).
198  
/** Enable periodic keepalive probes (SO_KEEPALIVE).
199  

199  

200  
    @par Example
200  
    @par Example
201  
    @code
201  
    @code
202  
    sock.set_option( socket_option::keep_alive( true ) );
202  
    sock.set_option( socket_option::keep_alive( true ) );
203  
    @endcode
203  
    @endcode
204  
*/
204  
*/
205  
class BOOST_COROSIO_DECL keep_alive : public boolean_option
205  
class BOOST_COROSIO_DECL keep_alive : public boolean_option
206  
{
206  
{
207  
public:
207  
public:
208  
    using boolean_option::boolean_option;
208  
    using boolean_option::boolean_option;
209  
    using boolean_option::operator=;
209  
    using boolean_option::operator=;
210  

210  

211  
    /// Return the protocol level.
211  
    /// Return the protocol level.
212  
    static int level() noexcept;
212  
    static int level() noexcept;
213  

213  

214  
    /// Return the option name.
214  
    /// Return the option name.
215  
    static int name() noexcept;
215  
    static int name() noexcept;
216  
};
216  
};
217  

217  

218  
/** Restrict an IPv6 socket to IPv6 only (IPV6_V6ONLY).
218  
/** Restrict an IPv6 socket to IPv6 only (IPV6_V6ONLY).
219  

219  

220  
    When enabled, the socket only accepts IPv6 connections.
220  
    When enabled, the socket only accepts IPv6 connections.
221  
    When disabled, the socket accepts both IPv4 and IPv6
221  
    When disabled, the socket accepts both IPv4 and IPv6
222  
    connections (dual-stack mode).
222  
    connections (dual-stack mode).
223  

223  

224  
    @par Example
224  
    @par Example
225  
    @code
225  
    @code
226  
    sock.set_option( socket_option::v6_only( true ) );
226  
    sock.set_option( socket_option::v6_only( true ) );
227  
    @endcode
227  
    @endcode
228  
*/
228  
*/
229  
class BOOST_COROSIO_DECL v6_only : public boolean_option
229  
class BOOST_COROSIO_DECL v6_only : public boolean_option
230  
{
230  
{
231  
public:
231  
public:
232  
    using boolean_option::boolean_option;
232  
    using boolean_option::boolean_option;
233  
    using boolean_option::operator=;
233  
    using boolean_option::operator=;
234  

234  

235  
    /// Return the protocol level.
235  
    /// Return the protocol level.
236  
    static int level() noexcept;
236  
    static int level() noexcept;
237  

237  

238  
    /// Return the option name.
238  
    /// Return the option name.
239  
    static int name() noexcept;
239  
    static int name() noexcept;
240  
};
240  
};
241  

241  

242  
/** Allow local address reuse (SO_REUSEADDR).
242  
/** Allow local address reuse (SO_REUSEADDR).
243  

243  

244  
    @par Example
244  
    @par Example
245  
    @code
245  
    @code
246  
    acc.set_option( socket_option::reuse_address( true ) );
246  
    acc.set_option( socket_option::reuse_address( true ) );
247  
    @endcode
247  
    @endcode
248  
*/
248  
*/
249  
class BOOST_COROSIO_DECL reuse_address : public boolean_option
249  
class BOOST_COROSIO_DECL reuse_address : public boolean_option
250  
{
250  
{
251  
public:
251  
public:
252  
    using boolean_option::boolean_option;
252  
    using boolean_option::boolean_option;
253  
    using boolean_option::operator=;
253  
    using boolean_option::operator=;
254  

254  

255  
    /// Return the protocol level.
255  
    /// Return the protocol level.
256  
    static int level() noexcept;
256  
    static int level() noexcept;
257  

257  

258  
    /// Return the option name.
258  
    /// Return the option name.
259  
    static int name() noexcept;
259  
    static int name() noexcept;
260  
};
260  
};
261  

261  

262  
/** Allow sending to broadcast addresses (SO_BROADCAST).
262  
/** Allow sending to broadcast addresses (SO_BROADCAST).
263  

263  

264  
    Required for UDP sockets that send to broadcast addresses
264  
    Required for UDP sockets that send to broadcast addresses
265  
    such as 255.255.255.255. Without this option, `send_to`
265  
    such as 255.255.255.255. Without this option, `send_to`
266  
    returns an error.
266  
    returns an error.
267  

267  

268  
    @par Example
268  
    @par Example
269  
    @code
269  
    @code
270  
    udp_socket sock( ioc );
270  
    udp_socket sock( ioc );
271  
    sock.open();
271  
    sock.open();
272  
    sock.set_option( socket_option::broadcast( true ) );
272  
    sock.set_option( socket_option::broadcast( true ) );
273  
    @endcode
273  
    @endcode
274  
*/
274  
*/
275  
class BOOST_COROSIO_DECL broadcast : public boolean_option
275  
class BOOST_COROSIO_DECL broadcast : public boolean_option
276  
{
276  
{
277  
public:
277  
public:
278  
    using boolean_option::boolean_option;
278  
    using boolean_option::boolean_option;
279  
    using boolean_option::operator=;
279  
    using boolean_option::operator=;
280  

280  

281  
    /// Return the protocol level.
281  
    /// Return the protocol level.
282  
    static int level() noexcept;
282  
    static int level() noexcept;
283  

283  

284  
    /// Return the option name.
284  
    /// Return the option name.
285  
    static int name() noexcept;
285  
    static int name() noexcept;
286  
};
286  
};
287  

287  

288  
/** Allow multiple sockets to bind to the same port (SO_REUSEPORT).
288  
/** Allow multiple sockets to bind to the same port (SO_REUSEPORT).
289  

289  

290  
    Not available on all platforms. On unsupported platforms,
290  
    Not available on all platforms. On unsupported platforms,
291  
    `set_option` will return an error.
291  
    `set_option` will return an error.
292  

292  

293  
    @par Example
293  
    @par Example
294  
    @code
294  
    @code
295  
    acc.open( tcp::v6() );
295  
    acc.open( tcp::v6() );
296  
    acc.set_option( socket_option::reuse_port( true ) );
296  
    acc.set_option( socket_option::reuse_port( true ) );
297  
    acc.bind( endpoint( ipv6_address::any(), 8080 ) );
297  
    acc.bind( endpoint( ipv6_address::any(), 8080 ) );
298  
    acc.listen();
298  
    acc.listen();
299  
    @endcode
299  
    @endcode
300  
*/
300  
*/
301  
class BOOST_COROSIO_DECL reuse_port : public boolean_option
301  
class BOOST_COROSIO_DECL reuse_port : public boolean_option
302  
{
302  
{
303  
public:
303  
public:
304  
    using boolean_option::boolean_option;
304  
    using boolean_option::boolean_option;
305  
    using boolean_option::operator=;
305  
    using boolean_option::operator=;
306  

306  

307  
    /// Return the protocol level.
307  
    /// Return the protocol level.
308  
    static int level() noexcept;
308  
    static int level() noexcept;
309  

309  

310  
    /// Return the option name.
310  
    /// Return the option name.
311  
    static int name() noexcept;
311  
    static int name() noexcept;
312  
};
312  
};
313  

313  

314  
/** Set the receive buffer size (SO_RCVBUF).
314  
/** Set the receive buffer size (SO_RCVBUF).
315  

315  

316  
    @par Example
316  
    @par Example
317  
    @code
317  
    @code
318  
    sock.set_option( socket_option::receive_buffer_size( 65536 ) );
318  
    sock.set_option( socket_option::receive_buffer_size( 65536 ) );
319  
    auto opt = sock.get_option<socket_option::receive_buffer_size>();
319  
    auto opt = sock.get_option<socket_option::receive_buffer_size>();
320  
    int sz = opt.value();
320  
    int sz = opt.value();
321  
    @endcode
321  
    @endcode
322  
*/
322  
*/
323  
class BOOST_COROSIO_DECL receive_buffer_size : public integer_option
323  
class BOOST_COROSIO_DECL receive_buffer_size : public integer_option
324  
{
324  
{
325  
public:
325  
public:
326  
    using integer_option::integer_option;
326  
    using integer_option::integer_option;
327  
    using integer_option::operator=;
327  
    using integer_option::operator=;
328  

328  

329  
    /// Return the protocol level.
329  
    /// Return the protocol level.
330  
    static int level() noexcept;
330  
    static int level() noexcept;
331  

331  

332  
    /// Return the option name.
332  
    /// Return the option name.
333  
    static int name() noexcept;
333  
    static int name() noexcept;
334  
};
334  
};
335  

335  

336  
/** Set the send buffer size (SO_SNDBUF).
336  
/** Set the send buffer size (SO_SNDBUF).
337  

337  

338  
    @par Example
338  
    @par Example
339  
    @code
339  
    @code
340  
    sock.set_option( socket_option::send_buffer_size( 65536 ) );
340  
    sock.set_option( socket_option::send_buffer_size( 65536 ) );
341  
    @endcode
341  
    @endcode
342  
*/
342  
*/
343  
class BOOST_COROSIO_DECL send_buffer_size : public integer_option
343  
class BOOST_COROSIO_DECL send_buffer_size : public integer_option
344  
{
344  
{
345  
public:
345  
public:
346  
    using integer_option::integer_option;
346  
    using integer_option::integer_option;
347  
    using integer_option::operator=;
347  
    using integer_option::operator=;
348  

348  

349  
    /// Return the protocol level.
349  
    /// Return the protocol level.
350  
    static int level() noexcept;
350  
    static int level() noexcept;
351  

351  

352  
    /// Return the option name.
352  
    /// Return the option name.
353  
    static int name() noexcept;
353  
    static int name() noexcept;
354  
};
354  
};
355  

355  

356  
/** The SO_LINGER socket option.
356  
/** The SO_LINGER socket option.
357  

357  

358  
    Controls behavior when closing a socket with unsent data.
358  
    Controls behavior when closing a socket with unsent data.
359  
    When enabled, `close()` blocks until pending data is sent
359  
    When enabled, `close()` blocks until pending data is sent
360  
    or the timeout expires.
360  
    or the timeout expires.
361  

361  

362  
    @par Example
362  
    @par Example
363  
    @code
363  
    @code
364  
    sock.set_option( socket_option::linger( true, 5 ) );
364  
    sock.set_option( socket_option::linger( true, 5 ) );
365  
    auto opt = sock.get_option<socket_option::linger>();
365  
    auto opt = sock.get_option<socket_option::linger>();
366  
    if ( opt.enabled() )
366  
    if ( opt.enabled() )
367  
        std::cout << "linger timeout: " << opt.timeout() << "s\n";
367  
        std::cout << "linger timeout: " << opt.timeout() << "s\n";
368  
    @endcode
368  
    @endcode
369  
*/
369  
*/
370  
class BOOST_COROSIO_DECL linger
370  
class BOOST_COROSIO_DECL linger
371  
{
371  
{
372  
    // Opaque storage for the platform's struct linger.
372  
    // Opaque storage for the platform's struct linger.
373  
    // POSIX: { int, int } = 8 bytes.
373  
    // POSIX: { int, int } = 8 bytes.
374  
    // Windows: { u_short, u_short } = 4 bytes.
374  
    // Windows: { u_short, u_short } = 4 bytes.
375  
    static constexpr std::size_t max_storage_ = 8;
375  
    static constexpr std::size_t max_storage_ = 8;
376  
    alignas(4) unsigned char storage_[max_storage_]{};
376  
    alignas(4) unsigned char storage_[max_storage_]{};
377  

377  

378  
public:
378  
public:
379  
    /// Construct with default values (disabled, zero timeout).
379  
    /// Construct with default values (disabled, zero timeout).
380  
    linger() noexcept = default;
380  
    linger() noexcept = default;
381  

381  

382  
    /** Construct with explicit values.
382  
    /** Construct with explicit values.
383  

383  

384  
        @param enabled `true` to enable linger behavior on close.
384  
        @param enabled `true` to enable linger behavior on close.
385  
        @param timeout The linger timeout in seconds.
385  
        @param timeout The linger timeout in seconds.
386  
    */
386  
    */
387  
    linger(bool enabled, int timeout) noexcept;
387  
    linger(bool enabled, int timeout) noexcept;
388  

388  

389  
    /// Return whether linger is enabled.
389  
    /// Return whether linger is enabled.
390  
    bool enabled() const noexcept;
390  
    bool enabled() const noexcept;
391  

391  

392  
    /// Set whether linger is enabled.
392  
    /// Set whether linger is enabled.
393  
    void enabled(bool v) noexcept;
393  
    void enabled(bool v) noexcept;
394  

394  

395  
    /// Return the linger timeout in seconds.
395  
    /// Return the linger timeout in seconds.
396  
    int timeout() const noexcept;
396  
    int timeout() const noexcept;
397  

397  

398  
    /// Set the linger timeout in seconds.
398  
    /// Set the linger timeout in seconds.
399  
    void timeout(int v) noexcept;
399  
    void timeout(int v) noexcept;
400  

400  

401  
    /// Return the protocol level.
401  
    /// Return the protocol level.
402  
    static int level() noexcept;
402  
    static int level() noexcept;
403  

403  

404  
    /// Return the option name.
404  
    /// Return the option name.
405  
    static int name() noexcept;
405  
    static int name() noexcept;
406  

406  

407  
    /// Return a pointer to the underlying storage.
407  
    /// Return a pointer to the underlying storage.
408  
    void* data() noexcept
408  
    void* data() noexcept
409  
    {
409  
    {
410  
        return storage_;
410  
        return storage_;
411  
    }
411  
    }
412  

412  

413  
    /// Return a pointer to the underlying storage.
413  
    /// Return a pointer to the underlying storage.
414  
    void const* data() const noexcept
414  
    void const* data() const noexcept
415  
    {
415  
    {
416  
        return storage_;
416  
        return storage_;
417  
    }
417  
    }
418  

418  

419  
    /// Return the size of the underlying storage.
419  
    /// Return the size of the underlying storage.
420  
    std::size_t size() const noexcept;
420  
    std::size_t size() const noexcept;
421  

421  

422  
    /** Normalize after `getsockopt`.
422  
    /** Normalize after `getsockopt`.
423  

423  

424  
        No-op — `struct linger` is always returned at full size.
424  
        No-op — `struct linger` is always returned at full size.
425  

425  

426  
        @param s The number of bytes actually written by `getsockopt`.
426  
        @param s The number of bytes actually written by `getsockopt`.
427  
    */
427  
    */
428  
    void resize(std::size_t) noexcept {}
428  
    void resize(std::size_t) noexcept {}
429  
};
429  
};
430  

430  

431  
/** Enable loopback of outgoing multicast on IPv4 (IP_MULTICAST_LOOP).
431  
/** Enable loopback of outgoing multicast on IPv4 (IP_MULTICAST_LOOP).
432  

432  

433  
    @par Example
433  
    @par Example
434  
    @code
434  
    @code
435  
    sock.set_option( socket_option::multicast_loop_v4( true ) );
435  
    sock.set_option( socket_option::multicast_loop_v4( true ) );
436  
    @endcode
436  
    @endcode
437  
*/
437  
*/
438  
class BOOST_COROSIO_DECL multicast_loop_v4 : public boolean_option
438  
class BOOST_COROSIO_DECL multicast_loop_v4 : public boolean_option
439  
{
439  
{
440  
public:
440  
public:
441  
    using boolean_option::boolean_option;
441  
    using boolean_option::boolean_option;
442  
    using boolean_option::operator=;
442  
    using boolean_option::operator=;
443  

443  

444  
    /// Return the protocol level.
444  
    /// Return the protocol level.
445  
    static int level() noexcept;
445  
    static int level() noexcept;
446  

446  

447  
    /// Return the option name.
447  
    /// Return the option name.
448  
    static int name() noexcept;
448  
    static int name() noexcept;
449  
};
449  
};
450  

450  

451  
/** Enable loopback of outgoing multicast on IPv6 (IPV6_MULTICAST_LOOP).
451  
/** Enable loopback of outgoing multicast on IPv6 (IPV6_MULTICAST_LOOP).
452  

452  

453  
    @par Example
453  
    @par Example
454  
    @code
454  
    @code
455  
    sock.set_option( socket_option::multicast_loop_v6( true ) );
455  
    sock.set_option( socket_option::multicast_loop_v6( true ) );
456  
    @endcode
456  
    @endcode
457  
*/
457  
*/
458  
class BOOST_COROSIO_DECL multicast_loop_v6 : public boolean_option
458  
class BOOST_COROSIO_DECL multicast_loop_v6 : public boolean_option
459  
{
459  
{
460  
public:
460  
public:
461  
    using boolean_option::boolean_option;
461  
    using boolean_option::boolean_option;
462  
    using boolean_option::operator=;
462  
    using boolean_option::operator=;
463  

463  

464  
    /// Return the protocol level.
464  
    /// Return the protocol level.
465  
    static int level() noexcept;
465  
    static int level() noexcept;
466  

466  

467  
    /// Return the option name.
467  
    /// Return the option name.
468  
    static int name() noexcept;
468  
    static int name() noexcept;
469  
};
469  
};
470  

470  

471  
/** Set the multicast TTL for IPv4 (IP_MULTICAST_TTL).
471  
/** Set the multicast TTL for IPv4 (IP_MULTICAST_TTL).
472  

472  

473  
    @par Example
473  
    @par Example
474  
    @code
474  
    @code
475  
    sock.set_option( socket_option::multicast_hops_v4( 4 ) );
475  
    sock.set_option( socket_option::multicast_hops_v4( 4 ) );
476  
    @endcode
476  
    @endcode
477  
*/
477  
*/
478  
class BOOST_COROSIO_DECL multicast_hops_v4 : public integer_option
478  
class BOOST_COROSIO_DECL multicast_hops_v4 : public integer_option
479  
{
479  
{
480  
public:
480  
public:
481  
    using integer_option::integer_option;
481  
    using integer_option::integer_option;
482  
    using integer_option::operator=;
482  
    using integer_option::operator=;
483  

483  

484  
    /// Return the protocol level.
484  
    /// Return the protocol level.
485  
    static int level() noexcept;
485  
    static int level() noexcept;
486  

486  

487  
    /// Return the option name.
487  
    /// Return the option name.
488  
    static int name() noexcept;
488  
    static int name() noexcept;
489  
};
489  
};
490  

490  

491  
/** Set the multicast hop limit for IPv6 (IPV6_MULTICAST_HOPS).
491  
/** Set the multicast hop limit for IPv6 (IPV6_MULTICAST_HOPS).
492  

492  

493  
    @par Example
493  
    @par Example
494  
    @code
494  
    @code
495  
    sock.set_option( socket_option::multicast_hops_v6( 4 ) );
495  
    sock.set_option( socket_option::multicast_hops_v6( 4 ) );
496  
    @endcode
496  
    @endcode
497  
*/
497  
*/
498  
class BOOST_COROSIO_DECL multicast_hops_v6 : public integer_option
498  
class BOOST_COROSIO_DECL multicast_hops_v6 : public integer_option
499  
{
499  
{
500  
public:
500  
public:
501  
    using integer_option::integer_option;
501  
    using integer_option::integer_option;
502  
    using integer_option::operator=;
502  
    using integer_option::operator=;
503  

503  

504  
    /// Return the protocol level.
504  
    /// Return the protocol level.
505  
    static int level() noexcept;
505  
    static int level() noexcept;
506  

506  

507  
    /// Return the option name.
507  
    /// Return the option name.
508  
    static int name() noexcept;
508  
    static int name() noexcept;
509  
};
509  
};
510  

510  

511  
/** Set the outgoing interface for IPv6 multicast (IPV6_MULTICAST_IF).
511  
/** Set the outgoing interface for IPv6 multicast (IPV6_MULTICAST_IF).
512  

512  

513  
    @par Example
513  
    @par Example
514  
    @code
514  
    @code
515  
    sock.set_option( socket_option::multicast_interface_v6( 1 ) );
515  
    sock.set_option( socket_option::multicast_interface_v6( 1 ) );
516  
    @endcode
516  
    @endcode
517  
*/
517  
*/
518  
class BOOST_COROSIO_DECL multicast_interface_v6 : public integer_option
518  
class BOOST_COROSIO_DECL multicast_interface_v6 : public integer_option
519  
{
519  
{
520  
public:
520  
public:
521  
    using integer_option::integer_option;
521  
    using integer_option::integer_option;
522  
    using integer_option::operator=;
522  
    using integer_option::operator=;
523  

523  

524  
    /// Return the protocol level.
524  
    /// Return the protocol level.
525  
    static int level() noexcept;
525  
    static int level() noexcept;
526  

526  

527  
    /// Return the option name.
527  
    /// Return the option name.
528  
    static int name() noexcept;
528  
    static int name() noexcept;
529  
};
529  
};
530  

530  

531  
/** Join an IPv4 multicast group (IP_ADD_MEMBERSHIP).
531  
/** Join an IPv4 multicast group (IP_ADD_MEMBERSHIP).
532  

532  

533  
    @par Example
533  
    @par Example
534  
    @code
534  
    @code
535  
    sock.set_option( socket_option::join_group_v4(
535  
    sock.set_option( socket_option::join_group_v4(
536  
        ipv4_address( "239.255.0.1" ) ) );
536  
        ipv4_address( "239.255.0.1" ) ) );
537  
    @endcode
537  
    @endcode
538  
*/
538  
*/
539  
class BOOST_COROSIO_DECL join_group_v4
539  
class BOOST_COROSIO_DECL join_group_v4
540  
{
540  
{
541  
    static constexpr std::size_t max_storage_ = 8;
541  
    static constexpr std::size_t max_storage_ = 8;
542  
    alignas(4) unsigned char storage_[max_storage_]{};
542  
    alignas(4) unsigned char storage_[max_storage_]{};
543  

543  

544  
public:
544  
public:
545  
    /// Construct with default values.
545  
    /// Construct with default values.
546  
    join_group_v4() noexcept = default;
546  
    join_group_v4() noexcept = default;
547  

547  

548  
    /** Construct with a group and optional interface address.
548  
    /** Construct with a group and optional interface address.
549  

549  

550  
        @param group The multicast group address to join.
550  
        @param group The multicast group address to join.
551  
        @param iface The local interface to use (default: any).
551  
        @param iface The local interface to use (default: any).
552  
    */
552  
    */
553  
    join_group_v4(
553  
    join_group_v4(
554  
        ipv4_address group, ipv4_address iface = ipv4_address()) noexcept;
554  
        ipv4_address group, ipv4_address iface = ipv4_address()) noexcept;
555  

555  

556  
    /// Return the protocol level.
556  
    /// Return the protocol level.
557  
    static int level() noexcept;
557  
    static int level() noexcept;
558  

558  

559  
    /// Return the option name.
559  
    /// Return the option name.
560  
    static int name() noexcept;
560  
    static int name() noexcept;
561  

561  

562  
    /// Return a pointer to the underlying storage.
562  
    /// Return a pointer to the underlying storage.
563  
    void* data() noexcept
563  
    void* data() noexcept
564  
    {
564  
    {
565  
        return storage_;
565  
        return storage_;
566  
    }
566  
    }
567  

567  

568  
    /// Return a pointer to the underlying storage.
568  
    /// Return a pointer to the underlying storage.
569  
    void const* data() const noexcept
569  
    void const* data() const noexcept
570  
    {
570  
    {
571  
        return storage_;
571  
        return storage_;
572  
    }
572  
    }
573  

573  

574  
    /// Return the size of the underlying storage.
574  
    /// Return the size of the underlying storage.
575  
    std::size_t size() const noexcept;
575  
    std::size_t size() const noexcept;
576  

576  

577  
    /// No-op resize.
577  
    /// No-op resize.
578  
    void resize(std::size_t) noexcept {}
578  
    void resize(std::size_t) noexcept {}
579  
};
579  
};
580  

580  

581  
/** Leave an IPv4 multicast group (IP_DROP_MEMBERSHIP).
581  
/** Leave an IPv4 multicast group (IP_DROP_MEMBERSHIP).
582  

582  

583  
    @par Example
583  
    @par Example
584  
    @code
584  
    @code
585  
    sock.set_option( socket_option::leave_group_v4(
585  
    sock.set_option( socket_option::leave_group_v4(
586  
        ipv4_address( "239.255.0.1" ) ) );
586  
        ipv4_address( "239.255.0.1" ) ) );
587  
    @endcode
587  
    @endcode
588  
*/
588  
*/
589  
class BOOST_COROSIO_DECL leave_group_v4
589  
class BOOST_COROSIO_DECL leave_group_v4
590  
{
590  
{
591  
    static constexpr std::size_t max_storage_ = 8;
591  
    static constexpr std::size_t max_storage_ = 8;
592  
    alignas(4) unsigned char storage_[max_storage_]{};
592  
    alignas(4) unsigned char storage_[max_storage_]{};
593  

593  

594  
public:
594  
public:
595  
    /// Construct with default values.
595  
    /// Construct with default values.
596  
    leave_group_v4() noexcept = default;
596  
    leave_group_v4() noexcept = default;
597  

597  

598  
    /** Construct with a group and optional interface address.
598  
    /** Construct with a group and optional interface address.
599  

599  

600  
        @param group The multicast group address to leave.
600  
        @param group The multicast group address to leave.
601  
        @param iface The local interface (default: any).
601  
        @param iface The local interface (default: any).
602  
    */
602  
    */
603  
    leave_group_v4(
603  
    leave_group_v4(
604  
        ipv4_address group, ipv4_address iface = ipv4_address()) noexcept;
604  
        ipv4_address group, ipv4_address iface = ipv4_address()) noexcept;
605  

605  

606  
    /// Return the protocol level.
606  
    /// Return the protocol level.
607  
    static int level() noexcept;
607  
    static int level() noexcept;
608  

608  

609  
    /// Return the option name.
609  
    /// Return the option name.
610  
    static int name() noexcept;
610  
    static int name() noexcept;
611  

611  

612  
    /// Return a pointer to the underlying storage.
612  
    /// Return a pointer to the underlying storage.
613  
    void* data() noexcept
613  
    void* data() noexcept
614  
    {
614  
    {
615  
        return storage_;
615  
        return storage_;
616  
    }
616  
    }
617  

617  

618  
    /// Return a pointer to the underlying storage.
618  
    /// Return a pointer to the underlying storage.
619  
    void const* data() const noexcept
619  
    void const* data() const noexcept
620  
    {
620  
    {
621  
        return storage_;
621  
        return storage_;
622  
    }
622  
    }
623  

623  

624  
    /// Return the size of the underlying storage.
624  
    /// Return the size of the underlying storage.
625  
    std::size_t size() const noexcept;
625  
    std::size_t size() const noexcept;
626  

626  

627  
    /// No-op resize.
627  
    /// No-op resize.
628  
    void resize(std::size_t) noexcept {}
628  
    void resize(std::size_t) noexcept {}
629  
};
629  
};
630  

630  

631  
/** Join an IPv6 multicast group (IPV6_JOIN_GROUP).
631  
/** Join an IPv6 multicast group (IPV6_JOIN_GROUP).
632  

632  

633  
    @par Example
633  
    @par Example
634  
    @code
634  
    @code
635  
    sock.set_option( socket_option::join_group_v6(
635  
    sock.set_option( socket_option::join_group_v6(
636  
        ipv6_address( "ff02::1" ), 0 ) );
636  
        ipv6_address( "ff02::1" ), 0 ) );
637  
    @endcode
637  
    @endcode
638  
*/
638  
*/
639  
class BOOST_COROSIO_DECL join_group_v6
639  
class BOOST_COROSIO_DECL join_group_v6
640  
{
640  
{
641  
    static constexpr std::size_t max_storage_ = 20;
641  
    static constexpr std::size_t max_storage_ = 20;
642  
    alignas(4) unsigned char storage_[max_storage_]{};
642  
    alignas(4) unsigned char storage_[max_storage_]{};
643  

643  

644  
public:
644  
public:
645  
    /// Construct with default values.
645  
    /// Construct with default values.
646  
    join_group_v6() noexcept = default;
646  
    join_group_v6() noexcept = default;
647  

647  

648  
    /** Construct with a group and optional interface index.
648  
    /** Construct with a group and optional interface index.
649  

649  

650  
        @param group The multicast group address to join.
650  
        @param group The multicast group address to join.
651  
        @param if_index The interface index (0 = kernel chooses).
651  
        @param if_index The interface index (0 = kernel chooses).
652  
    */
652  
    */
653  
    join_group_v6(ipv6_address group, unsigned int if_index = 0) noexcept;
653  
    join_group_v6(ipv6_address group, unsigned int if_index = 0) noexcept;
654  

654  

655  
    /// Return the protocol level.
655  
    /// Return the protocol level.
656  
    static int level() noexcept;
656  
    static int level() noexcept;
657  

657  

658  
    /// Return the option name.
658  
    /// Return the option name.
659  
    static int name() noexcept;
659  
    static int name() noexcept;
660  

660  

661  
    /// Return a pointer to the underlying storage.
661  
    /// Return a pointer to the underlying storage.
662  
    void* data() noexcept
662  
    void* data() noexcept
663  
    {
663  
    {
664  
        return storage_;
664  
        return storage_;
665  
    }
665  
    }
666  

666  

667  
    /// Return a pointer to the underlying storage.
667  
    /// Return a pointer to the underlying storage.
668  
    void const* data() const noexcept
668  
    void const* data() const noexcept
669  
    {
669  
    {
670  
        return storage_;
670  
        return storage_;
671  
    }
671  
    }
672  

672  

673  
    /// Return the size of the underlying storage.
673  
    /// Return the size of the underlying storage.
674  
    std::size_t size() const noexcept;
674  
    std::size_t size() const noexcept;
675  

675  

676  
    /// No-op resize.
676  
    /// No-op resize.
677  
    void resize(std::size_t) noexcept {}
677  
    void resize(std::size_t) noexcept {}
678  
};
678  
};
679  

679  

680  
/** Leave an IPv6 multicast group (IPV6_LEAVE_GROUP).
680  
/** Leave an IPv6 multicast group (IPV6_LEAVE_GROUP).
681  

681  

682  
    @par Example
682  
    @par Example
683  
    @code
683  
    @code
684  
    sock.set_option( socket_option::leave_group_v6(
684  
    sock.set_option( socket_option::leave_group_v6(
685  
        ipv6_address( "ff02::1" ), 0 ) );
685  
        ipv6_address( "ff02::1" ), 0 ) );
686  
    @endcode
686  
    @endcode
687  
*/
687  
*/
688  
class BOOST_COROSIO_DECL leave_group_v6
688  
class BOOST_COROSIO_DECL leave_group_v6
689  
{
689  
{
690  
    static constexpr std::size_t max_storage_ = 20;
690  
    static constexpr std::size_t max_storage_ = 20;
691  
    alignas(4) unsigned char storage_[max_storage_]{};
691  
    alignas(4) unsigned char storage_[max_storage_]{};
692  

692  

693  
public:
693  
public:
694  
    /// Construct with default values.
694  
    /// Construct with default values.
695  
    leave_group_v6() noexcept = default;
695  
    leave_group_v6() noexcept = default;
696  

696  

697  
    /** Construct with a group and optional interface index.
697  
    /** Construct with a group and optional interface index.
698  

698  

699  
        @param group The multicast group address to leave.
699  
        @param group The multicast group address to leave.
700  
        @param if_index The interface index (0 = kernel chooses).
700  
        @param if_index The interface index (0 = kernel chooses).
701  
    */
701  
    */
702  
    leave_group_v6(ipv6_address group, unsigned int if_index = 0) noexcept;
702  
    leave_group_v6(ipv6_address group, unsigned int if_index = 0) noexcept;
703  

703  

704  
    /// Return the protocol level.
704  
    /// Return the protocol level.
705  
    static int level() noexcept;
705  
    static int level() noexcept;
706  

706  

707  
    /// Return the option name.
707  
    /// Return the option name.
708  
    static int name() noexcept;
708  
    static int name() noexcept;
709  

709  

710  
    /// Return a pointer to the underlying storage.
710  
    /// Return a pointer to the underlying storage.
711  
    void* data() noexcept
711  
    void* data() noexcept
712  
    {
712  
    {
713  
        return storage_;
713  
        return storage_;
714  
    }
714  
    }
715  

715  

716  
    /// Return a pointer to the underlying storage.
716  
    /// Return a pointer to the underlying storage.
717  
    void const* data() const noexcept
717  
    void const* data() const noexcept
718  
    {
718  
    {
719  
        return storage_;
719  
        return storage_;
720  
    }
720  
    }
721  

721  

722  
    /// Return the size of the underlying storage.
722  
    /// Return the size of the underlying storage.
723  
    std::size_t size() const noexcept;
723  
    std::size_t size() const noexcept;
724  

724  

725  
    /// No-op resize.
725  
    /// No-op resize.
726  
    void resize(std::size_t) noexcept {}
726  
    void resize(std::size_t) noexcept {}
727  
};
727  
};
728  

728  

729  
/** Set the outgoing interface for IPv4 multicast (IP_MULTICAST_IF).
729  
/** Set the outgoing interface for IPv4 multicast (IP_MULTICAST_IF).
730  

730  

731  
    Unlike the integer-based `multicast_interface_v6`, this option
731  
    Unlike the integer-based `multicast_interface_v6`, this option
732  
    takes an `ipv4_address` identifying the local interface.
732  
    takes an `ipv4_address` identifying the local interface.
733  

733  

734  
    @par Example
734  
    @par Example
735  
    @code
735  
    @code
736  
    sock.set_option( socket_option::multicast_interface_v4(
736  
    sock.set_option( socket_option::multicast_interface_v4(
737  
        ipv4_address( "192.168.1.1" ) ) );
737  
        ipv4_address( "192.168.1.1" ) ) );
738  
    @endcode
738  
    @endcode
739  
*/
739  
*/
740  
class BOOST_COROSIO_DECL multicast_interface_v4
740  
class BOOST_COROSIO_DECL multicast_interface_v4
741  
{
741  
{
742  
    static constexpr std::size_t max_storage_ = 4;
742  
    static constexpr std::size_t max_storage_ = 4;
743  
    alignas(4) unsigned char storage_[max_storage_]{};
743  
    alignas(4) unsigned char storage_[max_storage_]{};
744  

744  

745  
public:
745  
public:
746  
    /// Construct with default values (INADDR_ANY).
746  
    /// Construct with default values (INADDR_ANY).
747  
    multicast_interface_v4() noexcept = default;
747  
    multicast_interface_v4() noexcept = default;
748  

748  

749  
    /** Construct with an interface address.
749  
    /** Construct with an interface address.
750  

750  

751  
        @param iface The local interface address.
751  
        @param iface The local interface address.
752  
    */
752  
    */
753  
    explicit multicast_interface_v4(ipv4_address iface) noexcept;
753  
    explicit multicast_interface_v4(ipv4_address iface) noexcept;
754  

754  

755  
    /// Return the protocol level.
755  
    /// Return the protocol level.
756  
    static int level() noexcept;
756  
    static int level() noexcept;
757  

757  

758  
    /// Return the option name.
758  
    /// Return the option name.
759  
    static int name() noexcept;
759  
    static int name() noexcept;
760  

760  

761  
    /// Return a pointer to the underlying storage.
761  
    /// Return a pointer to the underlying storage.
762  
    void* data() noexcept
762  
    void* data() noexcept
763  
    {
763  
    {
764  
        return storage_;
764  
        return storage_;
765  
    }
765  
    }
766  

766  

767  
    /// Return a pointer to the underlying storage.
767  
    /// Return a pointer to the underlying storage.
768  
    void const* data() const noexcept
768  
    void const* data() const noexcept
769  
    {
769  
    {
770  
        return storage_;
770  
        return storage_;
771  
    }
771  
    }
772  

772  

773  
    /// Return the size of the underlying storage.
773  
    /// Return the size of the underlying storage.
774  
    std::size_t size() const noexcept;
774  
    std::size_t size() const noexcept;
775  

775  

776  
    /// No-op resize.
776  
    /// No-op resize.
777  
    void resize(std::size_t) noexcept {}
777  
    void resize(std::size_t) noexcept {}
778  
};
778  
};
779  

779  

780  
} // namespace boost::corosio::socket_option
780  
} // namespace boost::corosio::socket_option
781  

781  

782  
#endif // BOOST_COROSIO_SOCKET_OPTION_HPP
782  
#endif // BOOST_COROSIO_SOCKET_OPTION_HPP