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
 
42 +
#ifndef IPV6_JOIN_GROUP
 
43 +
#define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP
 
44 +
#endif
 
45 +
#ifndef IPV6_LEAVE_GROUP
 
46 +
#define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP
 
47 +
#endif
 
48 +

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

41  
#include <cstddef>
52  
#include <cstddef>
 
53 +
#include <cstring>
42  

54  

43  
namespace boost::corosio::native_socket_option {
55  
namespace boost::corosio::native_socket_option {
44  

56  

45  
/** A socket option with a boolean value.
57  
/** A socket option with a boolean value.
46  

58  

47  
    Models socket options whose underlying representation is an `int`
59  
    Models socket options whose underlying representation is an `int`
48  
    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
49  
    protocol level and name are encoded as template parameters.
61  
    protocol level and name are encoded as template parameters.
50  

62  

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

66  

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

74  

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

82  

71  
public:
83  
public:
72  
    /// Construct with default value (disabled).
84  
    /// Construct with default value (disabled).
73  
    boolean() = default;
85  
    boolean() = default;
74  

86  

75  
    /** Construct with an explicit value.
87  
    /** Construct with an explicit value.
76  

88  

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

92  

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

99  

88  
    /// Return the option value.
100  
    /// Return the option value.
89  
    bool value() const noexcept
101  
    bool value() const noexcept
90  
    {
102  
    {
91  
        return value_ != 0;
103  
        return value_ != 0;
92  
    }
104  
    }
93  

105  

94  
    /// Return the option value.
106  
    /// Return the option value.
95  
    explicit operator bool() const noexcept
107  
    explicit operator bool() const noexcept
96  
    {
108  
    {
97  
        return value_ != 0;
109  
        return value_ != 0;
98  
    }
110  
    }
99  

111  

100  
    /// Return the negated option value.
112  
    /// Return the negated option value.
101  
    bool operator!() const noexcept
113  
    bool operator!() const noexcept
102  
    {
114  
    {
103  
        return value_ == 0;
115  
        return value_ == 0;
104  
    }
116  
    }
105  

117  

106  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
118  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
107  
    static constexpr int level() noexcept
119  
    static constexpr int level() noexcept
108  
    {
120  
    {
109  
        return Level;
121  
        return Level;
110  
    }
122  
    }
111  

123  

112  
    /// Return the option name for `setsockopt`/`getsockopt`.
124  
    /// Return the option name for `setsockopt`/`getsockopt`.
113  
    static constexpr int name() noexcept
125  
    static constexpr int name() noexcept
114  
    {
126  
    {
115  
        return Name;
127  
        return Name;
116  
    }
128  
    }
117  

129  

118  
    /// Return a pointer to the underlying storage.
130  
    /// Return a pointer to the underlying storage.
119  
    void* data() noexcept
131  
    void* data() noexcept
120  
    {
132  
    {
121  
        return &value_;
133  
        return &value_;
122  
    }
134  
    }
123  

135  

124  
    /// Return a pointer to the underlying storage.
136  
    /// Return a pointer to the underlying storage.
125  
    void const* data() const noexcept
137  
    void const* data() const noexcept
126  
    {
138  
    {
127  
        return &value_;
139  
        return &value_;
128  
    }
140  
    }
129  

141  

130  
    /// Return the size of the underlying storage.
142  
    /// Return the size of the underlying storage.
131  
    std::size_t size() const noexcept
143  
    std::size_t size() const noexcept
132  
    {
144  
    {
133  
        return sizeof(value_);
145  
        return sizeof(value_);
134  
    }
146  
    }
135  

147  

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

149  

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

151  

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

160  

149  
/** A socket option with an integer value.
161  
/** A socket option with an integer value.
150  

162  

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

166  

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

170  

159  
    @par Example
171  
    @par Example
160  
    @code
172  
    @code
161  
    sock.set_option( native_socket_option::receive_buffer_size( 65536 ) );
173  
    sock.set_option( native_socket_option::receive_buffer_size( 65536 ) );
162  
    auto opt = sock.get_option<native_socket_option::receive_buffer_size>();
174  
    auto opt = sock.get_option<native_socket_option::receive_buffer_size>();
163  
    int sz = opt.value();
175  
    int sz = opt.value();
164  
    @endcode
176  
    @endcode
165  

177  

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

185  

174  
public:
186  
public:
175  
    /// Construct with default value (zero).
187  
    /// Construct with default value (zero).
176  
    integer() = default;
188  
    integer() = default;
177  

189  

178  
    /** Construct with an explicit value.
190  
    /** Construct with an explicit value.
179  

191  

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

195  

184  
    /// Assign a new value.
196  
    /// Assign a new value.
185  
    integer& operator=(int v) noexcept
197  
    integer& operator=(int v) noexcept
186  
    {
198  
    {
187  
        value_ = v;
199  
        value_ = v;
188  
        return *this;
200  
        return *this;
189  
    }
201  
    }
190  

202  

191  
    /// Return the option value.
203  
    /// Return the option value.
192  
    int value() const noexcept
204  
    int value() const noexcept
193  
    {
205  
    {
194  
        return value_;
206  
        return value_;
195  
    }
207  
    }
196  

208  

197  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
209  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
198  
    static constexpr int level() noexcept
210  
    static constexpr int level() noexcept
199  
    {
211  
    {
200  
        return Level;
212  
        return Level;
201  
    }
213  
    }
202  

214  

203  
    /// Return the option name for `setsockopt`/`getsockopt`.
215  
    /// Return the option name for `setsockopt`/`getsockopt`.
204  
    static constexpr int name() noexcept
216  
    static constexpr int name() noexcept
205  
    {
217  
    {
206  
        return Name;
218  
        return Name;
207  
    }
219  
    }
208  

220  

209  
    /// Return a pointer to the underlying storage.
221  
    /// Return a pointer to the underlying storage.
210  
    void* data() noexcept
222  
    void* data() noexcept
211  
    {
223  
    {
212  
        return &value_;
224  
        return &value_;
213  
    }
225  
    }
214  

226  

215  
    /// Return a pointer to the underlying storage.
227  
    /// Return a pointer to the underlying storage.
216  
    void const* data() const noexcept
228  
    void const* data() const noexcept
217  
    {
229  
    {
218  
        return &value_;
230  
        return &value_;
219  
    }
231  
    }
220  

232  

221  
    /// Return the size of the underlying storage.
233  
    /// Return the size of the underlying storage.
222  
    std::size_t size() const noexcept
234  
    std::size_t size() const noexcept
223  
    {
235  
    {
224  
        return sizeof(value_);
236  
        return sizeof(value_);
225  
    }
237  
    }
226  

238  

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

240  

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

250  

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

252  

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

256  

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

260  

249  
    @par Example
261  
    @par Example
250  
    @code
262  
    @code
251  
    sock.set_option( native_socket_option::linger( true, 5 ) );
263  
    sock.set_option( native_socket_option::linger( true, 5 ) );
252  
    auto opt = sock.get_option<native_socket_option::linger>();
264  
    auto opt = sock.get_option<native_socket_option::linger>();
253  
    if ( opt.enabled() )
265  
    if ( opt.enabled() )
254  
        std::cout << "linger timeout: " << opt.timeout() << "s\n";
266  
        std::cout << "linger timeout: " << opt.timeout() << "s\n";
255  
    @endcode
267  
    @endcode
256  
*/
268  
*/
257  
class linger
269  
class linger
258  
{
270  
{
259  
    struct ::linger value_{};
271  
    struct ::linger value_{};
260  

272  

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

276  

265  
    /** Construct with explicit values.
277  
    /** Construct with explicit values.
266  

278  

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

287  

276  
    /// Return whether linger is enabled.
288  
    /// Return whether linger is enabled.
277  
    bool enabled() const noexcept
289  
    bool enabled() const noexcept
278  
    {
290  
    {
279  
        return value_.l_onoff != 0;
291  
        return value_.l_onoff != 0;
280  
    }
292  
    }
281  

293  

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

299  

288  
    /// Return the linger timeout in seconds.
300  
    /// Return the linger timeout in seconds.
289  
    int timeout() const noexcept
301  
    int timeout() const noexcept
290  
    {
302  
    {
291  
        return static_cast<int>(value_.l_linger);
303  
        return static_cast<int>(value_.l_linger);
292  
    }
304  
    }
293  

305  

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

311  

300  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
312  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
301  
    static constexpr int level() noexcept
313  
    static constexpr int level() noexcept
302  
    {
314  
    {
303  
        return SOL_SOCKET;
315  
        return SOL_SOCKET;
304  
    }
316  
    }
305  

317  

306  
    /// Return the option name for `setsockopt`/`getsockopt`.
318  
    /// Return the option name for `setsockopt`/`getsockopt`.
307  
    static constexpr int name() noexcept
319  
    static constexpr int name() noexcept
308  
    {
320  
    {
309  
        return SO_LINGER;
321  
        return SO_LINGER;
310  
    }
322  
    }
311  

323  

312  
    /// Return a pointer to the underlying storage.
324  
    /// Return a pointer to the underlying storage.
313  
    void* data() noexcept
325  
    void* data() noexcept
314  
    {
326  
    {
315  
        return &value_;
327  
        return &value_;
316  
    }
328  
    }
317  

329  

318  
    /// Return a pointer to the underlying storage.
330  
    /// Return a pointer to the underlying storage.
319  
    void const* data() const noexcept
331  
    void const* data() const noexcept
320  
    {
332  
    {
321  
        return &value_;
333  
        return &value_;
322  
    }
334  
    }
323  

335  

324  
    /// Return the size of the underlying storage.
336  
    /// Return the size of the underlying storage.
325  
    std::size_t size() const noexcept
337  
    std::size_t size() const noexcept
326  
    {
338  
    {
327  
        return sizeof(value_);
339  
        return sizeof(value_);
328  
    }
340  
    }
329  

341  

330  
    /** Normalize after `getsockopt`.
342  
    /** Normalize after `getsockopt`.
331  

343  

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

345  

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

350  

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

353  

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

356  

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

359  

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

362  

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

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

368  

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

371  

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

706  

362  
} // namespace boost::corosio::native_socket_option
707  
} // namespace boost::corosio::native_socket_option
363  

708  

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