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>
 
15 +
#include <boost/corosio/ipv6_address.hpp>
14  

16  

15  
#include <cstddef>
17  
#include <cstddef>
16  

18  

17  
/** @file socket_option.hpp
19  
/** @file socket_option.hpp
18  

20  

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

24  

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

28  

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

32  

31  
    @see native_socket_option
33  
    @see native_socket_option
32  
*/
34  
*/
33  

35  

34  
namespace boost::corosio::socket_option {
36  
namespace boost::corosio::socket_option {
35  

37  

36  
/** Base class for concrete boolean socket options.
38  
/** Base class for concrete boolean socket options.
37  

39  

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

46  

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

50  

49  
    /** Construct with an explicit value.
51  
    /** Construct with an explicit value.
50  

52  

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

56  

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

63  

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

69  

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

75  

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

81  

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

87  

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

93  

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

99  

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

101  

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

103  

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

112  

111  
/** Base class for concrete integer socket options.
113  
/** Base class for concrete integer socket options.
112  

114  

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

121  

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

125  

124  
    /** Construct with an explicit value.
126  
    /** Construct with an explicit value.
125  

127  

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

131  

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

138  

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

144  

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

150  

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

156  

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

162  

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

164  

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

174  

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

176  

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

190  

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

193  

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

197  

196  
/** Enable periodic keepalive probes (SO_KEEPALIVE).
198  
/** Enable periodic keepalive probes (SO_KEEPALIVE).
197  

199  

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

210  

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

213  

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

217  

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

219  

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

223  

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

234  

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

237  

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

241  

240  
/** Allow local address reuse (SO_REUSEADDR).
242  
/** Allow local address reuse (SO_REUSEADDR).
241  

243  

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

254  

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

257  

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

261  

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

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

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

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

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

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

289  

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

292  

265  
    @par Example
293  
    @par Example
266  
    @code
294  
    @code
267  
    acc.open( tcp::v6() );
295  
    acc.open( tcp::v6() );
268  
    acc.set_option( socket_option::reuse_port( true ) );
296  
    acc.set_option( socket_option::reuse_port( true ) );
269  
    acc.bind( endpoint( ipv6_address::any(), 8080 ) );
297  
    acc.bind( endpoint( ipv6_address::any(), 8080 ) );
270  
    acc.listen();
298  
    acc.listen();
271  
    @endcode
299  
    @endcode
272  
*/
300  
*/
273  
class BOOST_COROSIO_DECL reuse_port : public boolean_option
301  
class BOOST_COROSIO_DECL reuse_port : public boolean_option
274  
{
302  
{
275  
public:
303  
public:
276  
    using boolean_option::boolean_option;
304  
    using boolean_option::boolean_option;
277  
    using boolean_option::operator=;
305  
    using boolean_option::operator=;
278  

306  

279  
    /// Return the protocol level.
307  
    /// Return the protocol level.
280  
    static int level() noexcept;
308  
    static int level() noexcept;
281  

309  

282  
    /// Return the option name.
310  
    /// Return the option name.
283  
    static int name() noexcept;
311  
    static int name() noexcept;
284  
};
312  
};
285  

313  

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

315  

288  
    @par Example
316  
    @par Example
289  
    @code
317  
    @code
290  
    sock.set_option( socket_option::receive_buffer_size( 65536 ) );
318  
    sock.set_option( socket_option::receive_buffer_size( 65536 ) );
291  
    auto opt = sock.get_option<socket_option::receive_buffer_size>();
319  
    auto opt = sock.get_option<socket_option::receive_buffer_size>();
292  
    int sz = opt.value();
320  
    int sz = opt.value();
293  
    @endcode
321  
    @endcode
294  
*/
322  
*/
295  
class BOOST_COROSIO_DECL receive_buffer_size : public integer_option
323  
class BOOST_COROSIO_DECL receive_buffer_size : public integer_option
296  
{
324  
{
297  
public:
325  
public:
298  
    using integer_option::integer_option;
326  
    using integer_option::integer_option;
299  
    using integer_option::operator=;
327  
    using integer_option::operator=;
300  

328  

301  
    /// Return the protocol level.
329  
    /// Return the protocol level.
302  
    static int level() noexcept;
330  
    static int level() noexcept;
303  

331  

304  
    /// Return the option name.
332  
    /// Return the option name.
305  
    static int name() noexcept;
333  
    static int name() noexcept;
306  
};
334  
};
307  

335  

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

337  

310  
    @par Example
338  
    @par Example
311  
    @code
339  
    @code
312  
    sock.set_option( socket_option::send_buffer_size( 65536 ) );
340  
    sock.set_option( socket_option::send_buffer_size( 65536 ) );
313  
    @endcode
341  
    @endcode
314  
*/
342  
*/
315  
class BOOST_COROSIO_DECL send_buffer_size : public integer_option
343  
class BOOST_COROSIO_DECL send_buffer_size : public integer_option
316  
{
344  
{
317  
public:
345  
public:
318  
    using integer_option::integer_option;
346  
    using integer_option::integer_option;
319  
    using integer_option::operator=;
347  
    using integer_option::operator=;
320  

348  

321  
    /// Return the protocol level.
349  
    /// Return the protocol level.
322  
    static int level() noexcept;
350  
    static int level() noexcept;
323  

351  

324  
    /// Return the option name.
352  
    /// Return the option name.
325  
    static int name() noexcept;
353  
    static int name() noexcept;
326  
};
354  
};
327  

355  

328  
/** The SO_LINGER socket option.
356  
/** The SO_LINGER socket option.
329  

357  

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

361  

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

377  

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

381  

354  
    /** Construct with explicit values.
382  
    /** Construct with explicit values.
355  

383  

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

388  

361  
    /// Return whether linger is enabled.
389  
    /// Return whether linger is enabled.
362  
    bool enabled() const noexcept;
390  
    bool enabled() const noexcept;
363  

391  

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

394  

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

397  

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

400  

373  
    /// Return the protocol level.
401  
    /// Return the protocol level.
374  
    static int level() noexcept;
402  
    static int level() noexcept;
375  

403  

376  
    /// Return the option name.
404  
    /// Return the option name.
377  
    static int name() noexcept;
405  
    static int name() noexcept;
378  

406  

379  
    /// Return a pointer to the underlying storage.
407  
    /// Return a pointer to the underlying storage.
380  
    void* data() noexcept
408  
    void* data() noexcept
381  
    {
409  
    {
382  
        return storage_;
410  
        return storage_;
383  
    }
411  
    }
384  

412  

385  
    /// Return a pointer to the underlying storage.
413  
    /// Return a pointer to the underlying storage.
386  
    void const* data() const noexcept
414  
    void const* data() const noexcept
387  
    {
415  
    {
388  
        return storage_;
416  
        return storage_;
389  
    }
417  
    }
390  

418  

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

421  

394  
    /** Normalize after `getsockopt`.
422  
    /** Normalize after `getsockopt`.
395  

423  

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

425  

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 
776 +
    /// No-op resize.
400  
    void resize(std::size_t) noexcept {}
777  
    void resize(std::size_t) noexcept {}
401  
};
778  
};
402  

779  

403  
} // namespace boost::corosio::socket_option
780  
} // namespace boost::corosio::socket_option
404  

781  

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