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  
/** @file native_socket_option.hpp
10  
/** @file native_socket_option.hpp
11  

11  

12  
    Inline socket option types using platform-specific constants.
12  
    Inline socket option types using platform-specific constants.
13  
    All methods are `constexpr` or trivially inlined, giving zero
13  
    All methods are `constexpr` or trivially inlined, giving zero
14  
    overhead compared to hand-written `setsockopt` calls.
14  
    overhead compared to hand-written `setsockopt` calls.
15  

15  

16  
    This header includes platform socket headers
16  
    This header includes platform socket headers
17  
    (`<sys/socket.h>`, `<netinet/tcp.h>`, etc.).
17  
    (`<sys/socket.h>`, `<netinet/tcp.h>`, etc.).
18  
    For a version that avoids platform includes, use
18  
    For a version that avoids platform includes, use
19  
    `<boost/corosio/socket_option.hpp>`
19  
    `<boost/corosio/socket_option.hpp>`
20  
    (`boost::corosio::socket_option`).
20  
    (`boost::corosio::socket_option`).
21  

21  

22  
    Both variants satisfy the same option-type interface and work
22  
    Both variants satisfy the same option-type interface and work
23  
    interchangeably with `tcp_socket::set_option` /
23  
    interchangeably with `tcp_socket::set_option` /
24  
    `tcp_socket::get_option` and the corresponding acceptor methods.
24  
    `tcp_socket::get_option` and the corresponding acceptor methods.
25  

25  

26  
    @see boost::corosio::socket_option
26  
    @see boost::corosio::socket_option
27  
*/
27  
*/
28  

28  

29  
#ifndef BOOST_COROSIO_NATIVE_NATIVE_SOCKET_OPTION_HPP
29  
#ifndef BOOST_COROSIO_NATIVE_NATIVE_SOCKET_OPTION_HPP
30  
#define BOOST_COROSIO_NATIVE_NATIVE_SOCKET_OPTION_HPP
30  
#define BOOST_COROSIO_NATIVE_NATIVE_SOCKET_OPTION_HPP
31  

31  

32  
#ifdef _WIN32
32  
#ifdef _WIN32
33  
#include <winsock2.h>
33  
#include <winsock2.h>
34  
#include <ws2tcpip.h>
34  
#include <ws2tcpip.h>
35  
#else
35  
#else
36  
#include <netinet/in.h>
36  
#include <netinet/in.h>
37  
#include <netinet/tcp.h>
37  
#include <netinet/tcp.h>
38  
#include <sys/socket.h>
38  
#include <sys/socket.h>
39  
#endif
39  
#endif
40  

40  

41  
// Some older systems define only the legacy names
41  
// Some older systems define only the legacy names
42  
#ifndef IPV6_JOIN_GROUP
42  
#ifndef IPV6_JOIN_GROUP
43  
#define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP
43  
#define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP
44  
#endif
44  
#endif
45  
#ifndef IPV6_LEAVE_GROUP
45  
#ifndef IPV6_LEAVE_GROUP
46  
#define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP
46  
#define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP
47  
#endif
47  
#endif
48  

48  

49  
#include <boost/corosio/ipv4_address.hpp>
49  
#include <boost/corosio/ipv4_address.hpp>
50  
#include <boost/corosio/ipv6_address.hpp>
50  
#include <boost/corosio/ipv6_address.hpp>
51  

51  

52  
#include <cstddef>
52  
#include <cstddef>
53  
#include <cstring>
53  
#include <cstring>
54  

54  

55  
namespace boost::corosio::native_socket_option {
55  
namespace boost::corosio::native_socket_option {
56  

56  

57  
/** A socket option with a boolean value.
57  
/** A socket option with a boolean value.
58  

58  

59  
    Models socket options whose underlying representation is an `int`
59  
    Models socket options whose underlying representation is an `int`
60  
    where 0 means disabled and non-zero means enabled. The option's
60  
    where 0 means disabled and non-zero means enabled. The option's
61  
    protocol level and name are encoded as template parameters.
61  
    protocol level and name are encoded as template parameters.
62  

62  

63  
    This is the native (inline) variant that includes platform
63  
    This is the native (inline) variant that includes platform
64  
    headers. For a type-erased version that avoids platform
64  
    headers. For a type-erased version that avoids platform
65  
    includes, use `boost::corosio::socket_option` instead.
65  
    includes, use `boost::corosio::socket_option` instead.
66  

66  

67  
    @par Example
67  
    @par Example
68  
    @code
68  
    @code
69  
    sock.set_option( native_socket_option::no_delay( true ) );
69  
    sock.set_option( native_socket_option::no_delay( true ) );
70  
    auto nd = sock.get_option<native_socket_option::no_delay>();
70  
    auto nd = sock.get_option<native_socket_option::no_delay>();
71  
    if ( nd.value() )
71  
    if ( nd.value() )
72  
        // Nagle's algorithm is disabled
72  
        // Nagle's algorithm is disabled
73  
    @endcode
73  
    @endcode
74  

74  

75  
    @tparam Level The protocol level (e.g. `SOL_SOCKET`, `IPPROTO_TCP`).
75  
    @tparam Level The protocol level (e.g. `SOL_SOCKET`, `IPPROTO_TCP`).
76  
    @tparam Name The option name (e.g. `TCP_NODELAY`, `SO_KEEPALIVE`).
76  
    @tparam Name The option name (e.g. `TCP_NODELAY`, `SO_KEEPALIVE`).
77  
*/
77  
*/
78  
template<int Level, int Name>
78  
template<int Level, int Name>
79  
class boolean
79  
class boolean
80  
{
80  
{
81  
    int value_ = 0;
81  
    int value_ = 0;
82  

82  

83  
public:
83  
public:
84  
    /// Construct with default value (disabled).
84  
    /// Construct with default value (disabled).
85  
    boolean() = default;
85  
    boolean() = default;
86  

86  

87  
    /** Construct with an explicit value.
87  
    /** Construct with an explicit value.
88  

88  

89  
        @param v `true` to enable the option, `false` to disable.
89  
        @param v `true` to enable the option, `false` to disable.
90  
    */
90  
    */
91  
    explicit boolean(bool v) noexcept : value_(v ? 1 : 0) {}
91  
    explicit boolean(bool v) noexcept : value_(v ? 1 : 0) {}
92  

92  

93  
    /// Assign a new value.
93  
    /// Assign a new value.
94  
    boolean& operator=(bool v) noexcept
94  
    boolean& operator=(bool v) noexcept
95  
    {
95  
    {
96  
        value_ = v ? 1 : 0;
96  
        value_ = v ? 1 : 0;
97  
        return *this;
97  
        return *this;
98  
    }
98  
    }
99  

99  

100  
    /// Return the option value.
100  
    /// Return the option value.
101  
    bool value() const noexcept
101  
    bool value() const noexcept
102  
    {
102  
    {
103  
        return value_ != 0;
103  
        return value_ != 0;
104  
    }
104  
    }
105  

105  

106  
    /// Return the option value.
106  
    /// Return the option value.
107  
    explicit operator bool() const noexcept
107  
    explicit operator bool() const noexcept
108  
    {
108  
    {
109  
        return value_ != 0;
109  
        return value_ != 0;
110  
    }
110  
    }
111  

111  

112  
    /// Return the negated option value.
112  
    /// Return the negated option value.
113  
    bool operator!() const noexcept
113  
    bool operator!() const noexcept
114  
    {
114  
    {
115  
        return value_ == 0;
115  
        return value_ == 0;
116  
    }
116  
    }
117  

117  

118  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
118  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
119  
    static constexpr int level() noexcept
119  
    static constexpr int level() noexcept
120  
    {
120  
    {
121  
        return Level;
121  
        return Level;
122  
    }
122  
    }
123  

123  

124  
    /// Return the option name for `setsockopt`/`getsockopt`.
124  
    /// Return the option name for `setsockopt`/`getsockopt`.
125  
    static constexpr int name() noexcept
125  
    static constexpr int name() noexcept
126  
    {
126  
    {
127  
        return Name;
127  
        return Name;
128  
    }
128  
    }
129  

129  

130  
    /// Return a pointer to the underlying storage.
130  
    /// Return a pointer to the underlying storage.
131  
    void* data() noexcept
131  
    void* data() noexcept
132  
    {
132  
    {
133  
        return &value_;
133  
        return &value_;
134  
    }
134  
    }
135  

135  

136  
    /// Return a pointer to the underlying storage.
136  
    /// Return a pointer to the underlying storage.
137  
    void const* data() const noexcept
137  
    void const* data() const noexcept
138  
    {
138  
    {
139  
        return &value_;
139  
        return &value_;
140  
    }
140  
    }
141  

141  

142  
    /// Return the size of the underlying storage.
142  
    /// Return the size of the underlying storage.
143  
    std::size_t size() const noexcept
143  
    std::size_t size() const noexcept
144  
    {
144  
    {
145  
        return sizeof(value_);
145  
        return sizeof(value_);
146  
    }
146  
    }
147  

147  

148  
    /** Normalize after `getsockopt` returns fewer bytes than expected.
148  
    /** Normalize after `getsockopt` returns fewer bytes than expected.
149  

149  

150  
        Windows Vista+ may write only 1 byte for boolean options.
150  
        Windows Vista+ may write only 1 byte for boolean options.
151  

151  

152  
        @param s The number of bytes actually written by `getsockopt`.
152  
        @param s The number of bytes actually written by `getsockopt`.
153  
    */
153  
    */
154  
    void resize(std::size_t s) noexcept
154  
    void resize(std::size_t s) noexcept
155  
    {
155  
    {
156  
        if (s == sizeof(char))
156  
        if (s == sizeof(char))
157  
            value_ = *reinterpret_cast<unsigned char*>(&value_) ? 1 : 0;
157  
            value_ = *reinterpret_cast<unsigned char*>(&value_) ? 1 : 0;
158  
    }
158  
    }
159  
};
159  
};
160  

160  

161  
/** A socket option with an integer value.
161  
/** A socket option with an integer value.
162  

162  

163  
    Models socket options whose underlying representation is a
163  
    Models socket options whose underlying representation is a
164  
    plain `int`. The option's protocol level and name are encoded
164  
    plain `int`. The option's protocol level and name are encoded
165  
    as template parameters.
165  
    as template parameters.
166  

166  

167  
    This is the native (inline) variant that includes platform
167  
    This is the native (inline) variant that includes platform
168  
    headers. For a type-erased version that avoids platform
168  
    headers. For a type-erased version that avoids platform
169  
    includes, use `boost::corosio::socket_option` instead.
169  
    includes, use `boost::corosio::socket_option` instead.
170  

170  

171  
    @par Example
171  
    @par Example
172  
    @code
172  
    @code
173  
    sock.set_option( native_socket_option::receive_buffer_size( 65536 ) );
173  
    sock.set_option( native_socket_option::receive_buffer_size( 65536 ) );
174  
    auto opt = sock.get_option<native_socket_option::receive_buffer_size>();
174  
    auto opt = sock.get_option<native_socket_option::receive_buffer_size>();
175  
    int sz = opt.value();
175  
    int sz = opt.value();
176  
    @endcode
176  
    @endcode
177  

177  

178  
    @tparam Level The protocol level (e.g. `SOL_SOCKET`).
178  
    @tparam Level The protocol level (e.g. `SOL_SOCKET`).
179  
    @tparam Name The option name (e.g. `SO_RCVBUF`).
179  
    @tparam Name The option name (e.g. `SO_RCVBUF`).
180  
*/
180  
*/
181  
template<int Level, int Name>
181  
template<int Level, int Name>
182  
class integer
182  
class integer
183  
{
183  
{
184  
    int value_ = 0;
184  
    int value_ = 0;
185  

185  

186  
public:
186  
public:
187  
    /// Construct with default value (zero).
187  
    /// Construct with default value (zero).
188  
    integer() = default;
188  
    integer() = default;
189  

189  

190  
    /** Construct with an explicit value.
190  
    /** Construct with an explicit value.
191  

191  

192  
        @param v The option value.
192  
        @param v The option value.
193  
    */
193  
    */
194  
    explicit integer(int v) noexcept : value_(v) {}
194  
    explicit integer(int v) noexcept : value_(v) {}
195  

195  

196  
    /// Assign a new value.
196  
    /// Assign a new value.
197  
    integer& operator=(int v) noexcept
197  
    integer& operator=(int v) noexcept
198  
    {
198  
    {
199  
        value_ = v;
199  
        value_ = v;
200  
        return *this;
200  
        return *this;
201  
    }
201  
    }
202  

202  

203  
    /// Return the option value.
203  
    /// Return the option value.
204  
    int value() const noexcept
204  
    int value() const noexcept
205  
    {
205  
    {
206  
        return value_;
206  
        return value_;
207  
    }
207  
    }
208  

208  

209  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
209  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
210  
    static constexpr int level() noexcept
210  
    static constexpr int level() noexcept
211  
    {
211  
    {
212  
        return Level;
212  
        return Level;
213  
    }
213  
    }
214  

214  

215  
    /// Return the option name for `setsockopt`/`getsockopt`.
215  
    /// Return the option name for `setsockopt`/`getsockopt`.
216  
    static constexpr int name() noexcept
216  
    static constexpr int name() noexcept
217  
    {
217  
    {
218  
        return Name;
218  
        return Name;
219  
    }
219  
    }
220  

220  

221  
    /// Return a pointer to the underlying storage.
221  
    /// Return a pointer to the underlying storage.
222  
    void* data() noexcept
222  
    void* data() noexcept
223  
    {
223  
    {
224  
        return &value_;
224  
        return &value_;
225  
    }
225  
    }
226  

226  

227  
    /// Return a pointer to the underlying storage.
227  
    /// Return a pointer to the underlying storage.
228  
    void const* data() const noexcept
228  
    void const* data() const noexcept
229  
    {
229  
    {
230  
        return &value_;
230  
        return &value_;
231  
    }
231  
    }
232  

232  

233  
    /// Return the size of the underlying storage.
233  
    /// Return the size of the underlying storage.
234  
    std::size_t size() const noexcept
234  
    std::size_t size() const noexcept
235  
    {
235  
    {
236  
        return sizeof(value_);
236  
        return sizeof(value_);
237  
    }
237  
    }
238  

238  

239  
    /** Normalize after `getsockopt` returns fewer bytes than expected.
239  
    /** Normalize after `getsockopt` returns fewer bytes than expected.
240  

240  

241  
        @param s The number of bytes actually written by `getsockopt`.
241  
        @param s The number of bytes actually written by `getsockopt`.
242  
    */
242  
    */
243  
    void resize(std::size_t s) noexcept
243  
    void resize(std::size_t s) noexcept
244  
    {
244  
    {
245  
        if (s == sizeof(char))
245  
        if (s == sizeof(char))
246  
            value_ =
246  
            value_ =
247  
                static_cast<int>(*reinterpret_cast<unsigned char*>(&value_));
247  
                static_cast<int>(*reinterpret_cast<unsigned char*>(&value_));
248  
    }
248  
    }
249  
};
249  
};
250  

250  

251  
/** The SO_LINGER socket option (native variant).
251  
/** The SO_LINGER socket option (native variant).
252  

252  

253  
    Controls behavior when closing a socket with unsent data.
253  
    Controls behavior when closing a socket with unsent data.
254  
    When enabled, `close()` blocks until pending data is sent
254  
    When enabled, `close()` blocks until pending data is sent
255  
    or the timeout expires.
255  
    or the timeout expires.
256  

256  

257  
    This variant stores the platform's `struct linger` directly,
257  
    This variant stores the platform's `struct linger` directly,
258  
    avoiding the opaque-storage indirection of the type-erased
258  
    avoiding the opaque-storage indirection of the type-erased
259  
    version.
259  
    version.
260  

260  

261  
    @par Example
261  
    @par Example
262  
    @code
262  
    @code
263  
    sock.set_option( native_socket_option::linger( true, 5 ) );
263  
    sock.set_option( native_socket_option::linger( true, 5 ) );
264  
    auto opt = sock.get_option<native_socket_option::linger>();
264  
    auto opt = sock.get_option<native_socket_option::linger>();
265  
    if ( opt.enabled() )
265  
    if ( opt.enabled() )
266  
        std::cout << "linger timeout: " << opt.timeout() << "s\n";
266  
        std::cout << "linger timeout: " << opt.timeout() << "s\n";
267  
    @endcode
267  
    @endcode
268  
*/
268  
*/
269  
class linger
269  
class linger
270  
{
270  
{
271  
    struct ::linger value_{};
271  
    struct ::linger value_{};
272  

272  

273  
public:
273  
public:
274  
    /// Construct with default values (disabled, zero timeout).
274  
    /// Construct with default values (disabled, zero timeout).
275  
    linger() = default;
275  
    linger() = default;
276  

276  

277  
    /** Construct with explicit values.
277  
    /** Construct with explicit values.
278  

278  

279  
        @param enabled `true` to enable linger behavior on close.
279  
        @param enabled `true` to enable linger behavior on close.
280  
        @param timeout The linger timeout in seconds.
280  
        @param timeout The linger timeout in seconds.
281  
    */
281  
    */
282  
    linger(bool enabled, int timeout) noexcept
282  
    linger(bool enabled, int timeout) noexcept
283  
    {
283  
    {
284  
        value_.l_onoff  = enabled ? 1 : 0;
284  
        value_.l_onoff  = enabled ? 1 : 0;
285  
        value_.l_linger = static_cast<decltype(value_.l_linger)>(timeout);
285  
        value_.l_linger = static_cast<decltype(value_.l_linger)>(timeout);
286  
    }
286  
    }
287  

287  

288  
    /// Return whether linger is enabled.
288  
    /// Return whether linger is enabled.
289  
    bool enabled() const noexcept
289  
    bool enabled() const noexcept
290  
    {
290  
    {
291  
        return value_.l_onoff != 0;
291  
        return value_.l_onoff != 0;
292  
    }
292  
    }
293  

293  

294  
    /// Set whether linger is enabled.
294  
    /// Set whether linger is enabled.
295  
    void enabled(bool v) noexcept
295  
    void enabled(bool v) noexcept
296  
    {
296  
    {
297  
        value_.l_onoff = v ? 1 : 0;
297  
        value_.l_onoff = v ? 1 : 0;
298  
    }
298  
    }
299  

299  

300  
    /// Return the linger timeout in seconds.
300  
    /// Return the linger timeout in seconds.
301  
    int timeout() const noexcept
301  
    int timeout() const noexcept
302  
    {
302  
    {
303  
        return static_cast<int>(value_.l_linger);
303  
        return static_cast<int>(value_.l_linger);
304  
    }
304  
    }
305  

305  

306  
    /// Set the linger timeout in seconds.
306  
    /// Set the linger timeout in seconds.
307  
    void timeout(int v) noexcept
307  
    void timeout(int v) noexcept
308  
    {
308  
    {
309  
        value_.l_linger = static_cast<decltype(value_.l_linger)>(v);
309  
        value_.l_linger = static_cast<decltype(value_.l_linger)>(v);
310  
    }
310  
    }
311  

311  

312  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
312  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
313  
    static constexpr int level() noexcept
313  
    static constexpr int level() noexcept
314  
    {
314  
    {
315  
        return SOL_SOCKET;
315  
        return SOL_SOCKET;
316  
    }
316  
    }
317  

317  

318  
    /// Return the option name for `setsockopt`/`getsockopt`.
318  
    /// Return the option name for `setsockopt`/`getsockopt`.
319  
    static constexpr int name() noexcept
319  
    static constexpr int name() noexcept
320  
    {
320  
    {
321  
        return SO_LINGER;
321  
        return SO_LINGER;
322  
    }
322  
    }
323  

323  

324  
    /// Return a pointer to the underlying storage.
324  
    /// Return a pointer to the underlying storage.
325  
    void* data() noexcept
325  
    void* data() noexcept
326  
    {
326  
    {
327  
        return &value_;
327  
        return &value_;
328  
    }
328  
    }
329  

329  

330  
    /// Return a pointer to the underlying storage.
330  
    /// Return a pointer to the underlying storage.
331  
    void const* data() const noexcept
331  
    void const* data() const noexcept
332  
    {
332  
    {
333  
        return &value_;
333  
        return &value_;
334  
    }
334  
    }
335  

335  

336  
    /// Return the size of the underlying storage.
336  
    /// Return the size of the underlying storage.
337  
    std::size_t size() const noexcept
337  
    std::size_t size() const noexcept
338  
    {
338  
    {
339  
        return sizeof(value_);
339  
        return sizeof(value_);
340  
    }
340  
    }
341  

341  

342  
    /** Normalize after `getsockopt`.
342  
    /** Normalize after `getsockopt`.
343  

343  

344  
        No-op — `struct linger` is always returned at full size.
344  
        No-op — `struct linger` is always returned at full size.
345  

345  

346  
        @param s The number of bytes actually written by `getsockopt`.
346  
        @param s The number of bytes actually written by `getsockopt`.
347  
    */
347  
    */
348  
    void resize(std::size_t) noexcept {}
348  
    void resize(std::size_t) noexcept {}
349  
};
349  
};
350  

350  

351  
/// Disable Nagle's algorithm (TCP_NODELAY).
351  
/// Disable Nagle's algorithm (TCP_NODELAY).
352  
using no_delay = boolean<IPPROTO_TCP, TCP_NODELAY>;
352  
using no_delay = boolean<IPPROTO_TCP, TCP_NODELAY>;
353  

353  

354  
/// Enable periodic keepalive probes (SO_KEEPALIVE).
354  
/// Enable periodic keepalive probes (SO_KEEPALIVE).
355  
using keep_alive = boolean<SOL_SOCKET, SO_KEEPALIVE>;
355  
using keep_alive = boolean<SOL_SOCKET, SO_KEEPALIVE>;
356  

356  

357  
/// Restrict an IPv6 socket to IPv6 only (IPV6_V6ONLY).
357  
/// Restrict an IPv6 socket to IPv6 only (IPV6_V6ONLY).
358  
using v6_only = boolean<IPPROTO_IPV6, IPV6_V6ONLY>;
358  
using v6_only = boolean<IPPROTO_IPV6, IPV6_V6ONLY>;
359  

359  

360  
/// Allow local address reuse (SO_REUSEADDR).
360  
/// Allow local address reuse (SO_REUSEADDR).
361  
using reuse_address = boolean<SOL_SOCKET, SO_REUSEADDR>;
361  
using reuse_address = boolean<SOL_SOCKET, SO_REUSEADDR>;
362  

362  

363  
/// Allow sending to broadcast addresses (SO_BROADCAST).
363  
/// Allow sending to broadcast addresses (SO_BROADCAST).
364  
using broadcast = boolean<SOL_SOCKET, SO_BROADCAST>;
364  
using broadcast = boolean<SOL_SOCKET, SO_BROADCAST>;
365  

365  

366  
/// Set the receive buffer size (SO_RCVBUF).
366  
/// Set the receive buffer size (SO_RCVBUF).
367  
using receive_buffer_size = integer<SOL_SOCKET, SO_RCVBUF>;
367  
using receive_buffer_size = integer<SOL_SOCKET, SO_RCVBUF>;
368  

368  

369  
/// Set the send buffer size (SO_SNDBUF).
369  
/// Set the send buffer size (SO_SNDBUF).
370  
using send_buffer_size = integer<SOL_SOCKET, SO_SNDBUF>;
370  
using send_buffer_size = integer<SOL_SOCKET, SO_SNDBUF>;
371  

371  

372  
#ifdef SO_REUSEPORT
372  
#ifdef SO_REUSEPORT
373  
/// Allow multiple sockets to bind to the same port (SO_REUSEPORT).
373  
/// Allow multiple sockets to bind to the same port (SO_REUSEPORT).
374  
using reuse_port = boolean<SOL_SOCKET, SO_REUSEPORT>;
374  
using reuse_port = boolean<SOL_SOCKET, SO_REUSEPORT>;
375  
#endif
375  
#endif
376  

376  

377  
/// Enable loopback of outgoing multicast on IPv4 (IP_MULTICAST_LOOP).
377  
/// Enable loopback of outgoing multicast on IPv4 (IP_MULTICAST_LOOP).
378  
using multicast_loop_v4 = boolean<IPPROTO_IP, IP_MULTICAST_LOOP>;
378  
using multicast_loop_v4 = boolean<IPPROTO_IP, IP_MULTICAST_LOOP>;
379  

379  

380  
/// Enable loopback of outgoing multicast on IPv6 (IPV6_MULTICAST_LOOP).
380  
/// Enable loopback of outgoing multicast on IPv6 (IPV6_MULTICAST_LOOP).
381  
using multicast_loop_v6 = boolean<IPPROTO_IPV6, IPV6_MULTICAST_LOOP>;
381  
using multicast_loop_v6 = boolean<IPPROTO_IPV6, IPV6_MULTICAST_LOOP>;
382  

382  

383  
/// Set the multicast TTL for IPv4 (IP_MULTICAST_TTL).
383  
/// Set the multicast TTL for IPv4 (IP_MULTICAST_TTL).
384  
using multicast_hops_v4 = integer<IPPROTO_IP, IP_MULTICAST_TTL>;
384  
using multicast_hops_v4 = integer<IPPROTO_IP, IP_MULTICAST_TTL>;
385  

385  

386  
/// Set the multicast hop limit for IPv6 (IPV6_MULTICAST_HOPS).
386  
/// Set the multicast hop limit for IPv6 (IPV6_MULTICAST_HOPS).
387  
using multicast_hops_v6 = integer<IPPROTO_IPV6, IPV6_MULTICAST_HOPS>;
387  
using multicast_hops_v6 = integer<IPPROTO_IPV6, IPV6_MULTICAST_HOPS>;
388  

388  

389  
/// Set the outgoing interface for IPv6 multicast (IPV6_MULTICAST_IF).
389  
/// Set the outgoing interface for IPv6 multicast (IPV6_MULTICAST_IF).
390  
using multicast_interface_v6 = integer<IPPROTO_IPV6, IPV6_MULTICAST_IF>;
390  
using multicast_interface_v6 = integer<IPPROTO_IPV6, IPV6_MULTICAST_IF>;
391  

391  

392  
/** Join an IPv4 multicast group (IP_ADD_MEMBERSHIP).
392  
/** Join an IPv4 multicast group (IP_ADD_MEMBERSHIP).
393  

393  

394  
    @par Example
394  
    @par Example
395  
    @code
395  
    @code
396  
    sock.set_option( native_socket_option::join_group_v4(
396  
    sock.set_option( native_socket_option::join_group_v4(
397  
        ipv4_address( "239.255.0.1" ) ) );
397  
        ipv4_address( "239.255.0.1" ) ) );
398  
    @endcode
398  
    @endcode
399  
*/
399  
*/
400  
class join_group_v4
400  
class join_group_v4
401  
{
401  
{
402  
    struct ip_mreq value_{};
402  
    struct ip_mreq value_{};
403  

403  

404  
public:
404  
public:
405  
    /// Construct with default values.
405  
    /// Construct with default values.
406  
    join_group_v4() = default;
406  
    join_group_v4() = default;
407  

407  

408  
    /** Construct with a group and optional interface address.
408  
    /** Construct with a group and optional interface address.
409  

409  

410  
        @param group The multicast group address to join.
410  
        @param group The multicast group address to join.
411  
        @param iface The local interface to use (default: any).
411  
        @param iface The local interface to use (default: any).
412  
    */
412  
    */
413  
    join_group_v4(
413  
    join_group_v4(
414  
        ipv4_address group, ipv4_address iface = ipv4_address()) noexcept
414  
        ipv4_address group, ipv4_address iface = ipv4_address()) noexcept
415  
    {
415  
    {
416  
        auto gb = group.to_bytes();
416  
        auto gb = group.to_bytes();
417  
        auto ib = iface.to_bytes();
417  
        auto ib = iface.to_bytes();
418  
        std::memcpy(&value_.imr_multiaddr, gb.data(), 4);
418  
        std::memcpy(&value_.imr_multiaddr, gb.data(), 4);
419  
        std::memcpy(&value_.imr_interface, ib.data(), 4);
419  
        std::memcpy(&value_.imr_interface, ib.data(), 4);
420  
    }
420  
    }
421  

421  

422  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
422  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
423  
    static constexpr int level() noexcept
423  
    static constexpr int level() noexcept
424  
    {
424  
    {
425  
        return IPPROTO_IP;
425  
        return IPPROTO_IP;
426  
    }
426  
    }
427  

427  

428  
    /// Return the option name for `setsockopt`/`getsockopt`.
428  
    /// Return the option name for `setsockopt`/`getsockopt`.
429  
    static constexpr int name() noexcept
429  
    static constexpr int name() noexcept
430  
    {
430  
    {
431  
        return IP_ADD_MEMBERSHIP;
431  
        return IP_ADD_MEMBERSHIP;
432  
    }
432  
    }
433  

433  

434  
    /// Return a pointer to the underlying storage.
434  
    /// Return a pointer to the underlying storage.
435  
    void* data() noexcept
435  
    void* data() noexcept
436  
    {
436  
    {
437  
        return &value_;
437  
        return &value_;
438  
    }
438  
    }
439  

439  

440  
    /// Return a pointer to the underlying storage.
440  
    /// Return a pointer to the underlying storage.
441  
    void const* data() const noexcept
441  
    void const* data() const noexcept
442  
    {
442  
    {
443  
        return &value_;
443  
        return &value_;
444  
    }
444  
    }
445  

445  

446  
    /// Return the size of the underlying storage.
446  
    /// Return the size of the underlying storage.
447  
    std::size_t size() const noexcept
447  
    std::size_t size() const noexcept
448  
    {
448  
    {
449  
        return sizeof(value_);
449  
        return sizeof(value_);
450  
    }
450  
    }
451  

451  

452  
    /// No-op resize.
452  
    /// No-op resize.
453  
    void resize(std::size_t) noexcept {}
453  
    void resize(std::size_t) noexcept {}
454  
};
454  
};
455  

455  

456  
/** Leave an IPv4 multicast group (IP_DROP_MEMBERSHIP).
456  
/** Leave an IPv4 multicast group (IP_DROP_MEMBERSHIP).
457  

457  

458  
    @par Example
458  
    @par Example
459  
    @code
459  
    @code
460  
    sock.set_option( native_socket_option::leave_group_v4(
460  
    sock.set_option( native_socket_option::leave_group_v4(
461  
        ipv4_address( "239.255.0.1" ) ) );
461  
        ipv4_address( "239.255.0.1" ) ) );
462  
    @endcode
462  
    @endcode
463  
*/
463  
*/
464  
class leave_group_v4
464  
class leave_group_v4
465  
{
465  
{
466  
    struct ip_mreq value_{};
466  
    struct ip_mreq value_{};
467  

467  

468  
public:
468  
public:
469  
    /// Construct with default values.
469  
    /// Construct with default values.
470  
    leave_group_v4() = default;
470  
    leave_group_v4() = default;
471  

471  

472  
    /** Construct with a group and optional interface address.
472  
    /** Construct with a group and optional interface address.
473  

473  

474  
        @param group The multicast group address to leave.
474  
        @param group The multicast group address to leave.
475  
        @param iface The local interface (default: any).
475  
        @param iface The local interface (default: any).
476  
    */
476  
    */
477  
    leave_group_v4(
477  
    leave_group_v4(
478  
        ipv4_address group, ipv4_address iface = ipv4_address()) noexcept
478  
        ipv4_address group, ipv4_address iface = ipv4_address()) noexcept
479  
    {
479  
    {
480  
        auto gb = group.to_bytes();
480  
        auto gb = group.to_bytes();
481  
        auto ib = iface.to_bytes();
481  
        auto ib = iface.to_bytes();
482  
        std::memcpy(&value_.imr_multiaddr, gb.data(), 4);
482  
        std::memcpy(&value_.imr_multiaddr, gb.data(), 4);
483  
        std::memcpy(&value_.imr_interface, ib.data(), 4);
483  
        std::memcpy(&value_.imr_interface, ib.data(), 4);
484  
    }
484  
    }
485  

485  

486  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
486  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
487  
    static constexpr int level() noexcept
487  
    static constexpr int level() noexcept
488  
    {
488  
    {
489  
        return IPPROTO_IP;
489  
        return IPPROTO_IP;
490  
    }
490  
    }
491  

491  

492  
    /// Return the option name for `setsockopt`/`getsockopt`.
492  
    /// Return the option name for `setsockopt`/`getsockopt`.
493  
    static constexpr int name() noexcept
493  
    static constexpr int name() noexcept
494  
    {
494  
    {
495  
        return IP_DROP_MEMBERSHIP;
495  
        return IP_DROP_MEMBERSHIP;
496  
    }
496  
    }
497  

497  

498  
    /// Return a pointer to the underlying storage.
498  
    /// Return a pointer to the underlying storage.
499  
    void* data() noexcept
499  
    void* data() noexcept
500  
    {
500  
    {
501  
        return &value_;
501  
        return &value_;
502  
    }
502  
    }
503  

503  

504  
    /// Return a pointer to the underlying storage.
504  
    /// Return a pointer to the underlying storage.
505  
    void const* data() const noexcept
505  
    void const* data() const noexcept
506  
    {
506  
    {
507  
        return &value_;
507  
        return &value_;
508  
    }
508  
    }
509  

509  

510  
    /// Return the size of the underlying storage.
510  
    /// Return the size of the underlying storage.
511  
    std::size_t size() const noexcept
511  
    std::size_t size() const noexcept
512  
    {
512  
    {
513  
        return sizeof(value_);
513  
        return sizeof(value_);
514  
    }
514  
    }
515  

515  

516  
    /// No-op resize.
516  
    /// No-op resize.
517  
    void resize(std::size_t) noexcept {}
517  
    void resize(std::size_t) noexcept {}
518  
};
518  
};
519  

519  

520  
/** Join an IPv6 multicast group (IPV6_JOIN_GROUP).
520  
/** Join an IPv6 multicast group (IPV6_JOIN_GROUP).
521  

521  

522  
    @par Example
522  
    @par Example
523  
    @code
523  
    @code
524  
    sock.set_option( native_socket_option::join_group_v6(
524  
    sock.set_option( native_socket_option::join_group_v6(
525  
        ipv6_address( "ff02::1" ), 0 ) );
525  
        ipv6_address( "ff02::1" ), 0 ) );
526  
    @endcode
526  
    @endcode
527  
*/
527  
*/
528  
class join_group_v6
528  
class join_group_v6
529  
{
529  
{
530  
    struct ipv6_mreq value_{};
530  
    struct ipv6_mreq value_{};
531  

531  

532  
public:
532  
public:
533  
    /// Construct with default values.
533  
    /// Construct with default values.
534  
    join_group_v6() = default;
534  
    join_group_v6() = default;
535  

535  

536  
    /** Construct with a group and optional interface index.
536  
    /** Construct with a group and optional interface index.
537  

537  

538  
        @param group The multicast group address to join.
538  
        @param group The multicast group address to join.
539  
        @param if_index The interface index (0 = kernel chooses).
539  
        @param if_index The interface index (0 = kernel chooses).
540  
    */
540  
    */
541  
    join_group_v6(ipv6_address group, unsigned int if_index = 0) noexcept
541  
    join_group_v6(ipv6_address group, unsigned int if_index = 0) noexcept
542  
    {
542  
    {
543  
        auto gb = group.to_bytes();
543  
        auto gb = group.to_bytes();
544  
        std::memcpy(&value_.ipv6mr_multiaddr, gb.data(), 16);
544  
        std::memcpy(&value_.ipv6mr_multiaddr, gb.data(), 16);
545  
        value_.ipv6mr_interface = if_index;
545  
        value_.ipv6mr_interface = if_index;
546  
    }
546  
    }
547  

547  

548  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
548  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
549  
    static constexpr int level() noexcept
549  
    static constexpr int level() noexcept
550  
    {
550  
    {
551  
        return IPPROTO_IPV6;
551  
        return IPPROTO_IPV6;
552  
    }
552  
    }
553  

553  

554  
    /// Return the option name for `setsockopt`/`getsockopt`.
554  
    /// Return the option name for `setsockopt`/`getsockopt`.
555  
    static constexpr int name() noexcept
555  
    static constexpr int name() noexcept
556  
    {
556  
    {
557  
        return IPV6_JOIN_GROUP;
557  
        return IPV6_JOIN_GROUP;
558  
    }
558  
    }
559  

559  

560  
    /// Return a pointer to the underlying storage.
560  
    /// Return a pointer to the underlying storage.
561  
    void* data() noexcept
561  
    void* data() noexcept
562  
    {
562  
    {
563  
        return &value_;
563  
        return &value_;
564  
    }
564  
    }
565  

565  

566  
    /// Return a pointer to the underlying storage.
566  
    /// Return a pointer to the underlying storage.
567  
    void const* data() const noexcept
567  
    void const* data() const noexcept
568  
    {
568  
    {
569  
        return &value_;
569  
        return &value_;
570  
    }
570  
    }
571  

571  

572  
    /// Return the size of the underlying storage.
572  
    /// Return the size of the underlying storage.
573  
    std::size_t size() const noexcept
573  
    std::size_t size() const noexcept
574  
    {
574  
    {
575  
        return sizeof(value_);
575  
        return sizeof(value_);
576  
    }
576  
    }
577  

577  

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

581  

582  
/** Leave an IPv6 multicast group (IPV6_LEAVE_GROUP).
582  
/** Leave an IPv6 multicast group (IPV6_LEAVE_GROUP).
583  

583  

584  
    @par Example
584  
    @par Example
585  
    @code
585  
    @code
586  
    sock.set_option( native_socket_option::leave_group_v6(
586  
    sock.set_option( native_socket_option::leave_group_v6(
587  
        ipv6_address( "ff02::1" ), 0 ) );
587  
        ipv6_address( "ff02::1" ), 0 ) );
588  
    @endcode
588  
    @endcode
589  
*/
589  
*/
590  
class leave_group_v6
590  
class leave_group_v6
591  
{
591  
{
592  
    struct ipv6_mreq value_{};
592  
    struct ipv6_mreq value_{};
593  

593  

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

597  

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

599  

600  
        @param group The multicast group address to leave.
600  
        @param group The multicast group address to leave.
601  
        @param if_index The interface index (0 = kernel chooses).
601  
        @param if_index The interface index (0 = kernel chooses).
602  
    */
602  
    */
603  
    leave_group_v6(ipv6_address group, unsigned int if_index = 0) noexcept
603  
    leave_group_v6(ipv6_address group, unsigned int if_index = 0) noexcept
604  
    {
604  
    {
605  
        auto gb = group.to_bytes();
605  
        auto gb = group.to_bytes();
606  
        std::memcpy(&value_.ipv6mr_multiaddr, gb.data(), 16);
606  
        std::memcpy(&value_.ipv6mr_multiaddr, gb.data(), 16);
607  
        value_.ipv6mr_interface = if_index;
607  
        value_.ipv6mr_interface = if_index;
608  
    }
608  
    }
609  

609  

610  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
610  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
611  
    static constexpr int level() noexcept
611  
    static constexpr int level() noexcept
612  
    {
612  
    {
613  
        return IPPROTO_IPV6;
613  
        return IPPROTO_IPV6;
614  
    }
614  
    }
615  

615  

616  
    /// Return the option name for `setsockopt`/`getsockopt`.
616  
    /// Return the option name for `setsockopt`/`getsockopt`.
617  
    static constexpr int name() noexcept
617  
    static constexpr int name() noexcept
618  
    {
618  
    {
619  
        return IPV6_LEAVE_GROUP;
619  
        return IPV6_LEAVE_GROUP;
620  
    }
620  
    }
621  

621  

622  
    /// Return a pointer to the underlying storage.
622  
    /// Return a pointer to the underlying storage.
623  
    void* data() noexcept
623  
    void* data() noexcept
624  
    {
624  
    {
625  
        return &value_;
625  
        return &value_;
626  
    }
626  
    }
627  

627  

628  
    /// Return a pointer to the underlying storage.
628  
    /// Return a pointer to the underlying storage.
629  
    void const* data() const noexcept
629  
    void const* data() const noexcept
630  
    {
630  
    {
631  
        return &value_;
631  
        return &value_;
632  
    }
632  
    }
633  

633  

634  
    /// Return the size of the underlying storage.
634  
    /// Return the size of the underlying storage.
635  
    std::size_t size() const noexcept
635  
    std::size_t size() const noexcept
636  
    {
636  
    {
637  
        return sizeof(value_);
637  
        return sizeof(value_);
638  
    }
638  
    }
639  

639  

640  
    /// No-op resize.
640  
    /// No-op resize.
641  
    void resize(std::size_t) noexcept {}
641  
    void resize(std::size_t) noexcept {}
642  
};
642  
};
643  

643  

644  
/** Set the outgoing interface for IPv4 multicast (IP_MULTICAST_IF).
644  
/** Set the outgoing interface for IPv4 multicast (IP_MULTICAST_IF).
645  

645  

646  
    Unlike the integer-based `multicast_interface_v6`, this option
646  
    Unlike the integer-based `multicast_interface_v6`, this option
647  
    takes an `ipv4_address` identifying the local interface.
647  
    takes an `ipv4_address` identifying the local interface.
648  

648  

649  
    @par Example
649  
    @par Example
650  
    @code
650  
    @code
651  
    sock.set_option( native_socket_option::multicast_interface_v4(
651  
    sock.set_option( native_socket_option::multicast_interface_v4(
652  
        ipv4_address( "192.168.1.1" ) ) );
652  
        ipv4_address( "192.168.1.1" ) ) );
653  
    @endcode
653  
    @endcode
654  
*/
654  
*/
655  
class multicast_interface_v4
655  
class multicast_interface_v4
656  
{
656  
{
657  
    struct in_addr value_{};
657  
    struct in_addr value_{};
658  

658  

659  
public:
659  
public:
660  
    /// Construct with default values (INADDR_ANY).
660  
    /// Construct with default values (INADDR_ANY).
661  
    multicast_interface_v4() = default;
661  
    multicast_interface_v4() = default;
662  

662  

663  
    /** Construct with an interface address.
663  
    /** Construct with an interface address.
664  

664  

665  
        @param iface The local interface address.
665  
        @param iface The local interface address.
666  
    */
666  
    */
667  
    explicit multicast_interface_v4(ipv4_address iface) noexcept
667  
    explicit multicast_interface_v4(ipv4_address iface) noexcept
668  
    {
668  
    {
669  
        auto b = iface.to_bytes();
669  
        auto b = iface.to_bytes();
670  
        std::memcpy(&value_, b.data(), 4);
670  
        std::memcpy(&value_, b.data(), 4);
671  
    }
671  
    }
672  

672  

673  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
673  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
674  
    static constexpr int level() noexcept
674  
    static constexpr int level() noexcept
675  
    {
675  
    {
676  
        return IPPROTO_IP;
676  
        return IPPROTO_IP;
677  
    }
677  
    }
678  

678  

679  
    /// Return the option name for `setsockopt`/`getsockopt`.
679  
    /// Return the option name for `setsockopt`/`getsockopt`.
680  
    static constexpr int name() noexcept
680  
    static constexpr int name() noexcept
681  
    {
681  
    {
682  
        return IP_MULTICAST_IF;
682  
        return IP_MULTICAST_IF;
683  
    }
683  
    }
684  

684  

685  
    /// Return a pointer to the underlying storage.
685  
    /// Return a pointer to the underlying storage.
686  
    void* data() noexcept
686  
    void* data() noexcept
687  
    {
687  
    {
688  
        return &value_;
688  
        return &value_;
689  
    }
689  
    }
690  

690  

691  
    /// Return a pointer to the underlying storage.
691  
    /// Return a pointer to the underlying storage.
692  
    void const* data() const noexcept
692  
    void const* data() const noexcept
693  
    {
693  
    {
694  
        return &value_;
694  
        return &value_;
695  
    }
695  
    }
696  

696  

697  
    /// Return the size of the underlying storage.
697  
    /// Return the size of the underlying storage.
698  
    std::size_t size() const noexcept
698  
    std::size_t size() const noexcept
699  
    {
699  
    {
700  
        return sizeof(value_);
700  
        return sizeof(value_);
701  
    }
701  
    }
702  

702  

703  
    /// No-op resize.
703  
    /// No-op resize.
704  
    void resize(std::size_t) noexcept {}
704  
    void resize(std::size_t) noexcept {}
705  
};
705  
};
706  

706  

707  
} // namespace boost::corosio::native_socket_option
707  
} // namespace boost::corosio::native_socket_option
708  

708  

709  
#endif // BOOST_COROSIO_NATIVE_NATIVE_SOCKET_OPTION_HPP
709  
#endif // BOOST_COROSIO_NATIVE_NATIVE_SOCKET_OPTION_HPP