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  

14  

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

16  

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

18  

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

22  

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

26  

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

30  

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

33  

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

35  

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

37  

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

44  

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

48  

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

50  

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

54  

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

61  

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

64  

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

67  

68  
    /// Return the negated option value.
68  
    /// Return the negated option value.
69  
    bool operator!() const noexcept { return value_ == 0; }
69  
    bool operator!() const noexcept { return value_ == 0; }
70  

70  

71  
    /// Return a pointer to the underlying storage.
71  
    /// Return a pointer to the underlying storage.
72  
    void* data() noexcept { return &value_; }
72  
    void* data() noexcept { return &value_; }
73  

73  

74  
    /// Return a pointer to the underlying storage.
74  
    /// Return a pointer to the underlying storage.
75  
    void const* data() const noexcept { return &value_; }
75  
    void const* data() const noexcept { return &value_; }
76  

76  

77  
    /// Return the size of the underlying storage.
77  
    /// Return the size of the underlying storage.
78  
    std::size_t size() const noexcept { return sizeof( value_ ); }
78  
    std::size_t size() const noexcept { return sizeof( value_ ); }
79  

79  

80  
    /** Normalize after `getsockopt` returns fewer bytes than expected.
80  
    /** Normalize after `getsockopt` returns fewer bytes than expected.
81  

81  

82  
        Windows Vista+ may write only 1 byte for boolean options.
82  
        Windows Vista+ may write only 1 byte for boolean options.
83  

83  

84  
        @param s The number of bytes actually written by `getsockopt`.
84  
        @param s The number of bytes actually written by `getsockopt`.
85  
    */
85  
    */
86  
    void resize( std::size_t s ) noexcept
86  
    void resize( std::size_t s ) noexcept
87  
    {
87  
    {
88  
        if ( s == sizeof( char ) )
88  
        if ( s == sizeof( char ) )
89  
            value_ = *reinterpret_cast<unsigned char*>( &value_ ) ? 1 : 0;
89  
            value_ = *reinterpret_cast<unsigned char*>( &value_ ) ? 1 : 0;
90  
    }
90  
    }
91  
};
91  
};
92  

92  

93  
/** Base class for concrete integer socket options.
93  
/** Base class for concrete integer socket options.
94  

94  

95  
    Stores an integer suitable for `setsockopt`/`getsockopt`.
95  
    Stores an integer suitable for `setsockopt`/`getsockopt`.
96  
    Derived types provide `level()` and `name()` for the specific option.
96  
    Derived types provide `level()` and `name()` for the specific option.
97  
*/
97  
*/
98  
class integer_option
98  
class integer_option
99  
{
99  
{
100  
    int value_ = 0;
100  
    int value_ = 0;
101  

101  

102  
public:
102  
public:
103  
    /// Construct with default value (zero).
103  
    /// Construct with default value (zero).
104  
    integer_option() = default;
104  
    integer_option() = default;
105  

105  

106  
    /** Construct with an explicit value.
106  
    /** Construct with an explicit value.
107  

107  

108  
        @param v The option value.
108  
        @param v The option value.
109  
    */
109  
    */
110  
    explicit integer_option( int v ) noexcept : value_( v ) {}
110  
    explicit integer_option( int v ) noexcept : value_( v ) {}
111  

111  

112  
    /// Assign a new value.
112  
    /// Assign a new value.
113  
    integer_option& operator=( int v ) noexcept
113  
    integer_option& operator=( int v ) noexcept
114  
    {
114  
    {
115  
        value_ = v;
115  
        value_ = v;
116  
        return *this;
116  
        return *this;
117  
    }
117  
    }
118  

118  

119  
    /// Return the option value.
119  
    /// Return the option value.
120  
    int value() const noexcept { return value_; }
120  
    int value() const noexcept { return value_; }
121  

121  

122  
    /// Return a pointer to the underlying storage.
122  
    /// Return a pointer to the underlying storage.
123  
    void* data() noexcept { return &value_; }
123  
    void* data() noexcept { return &value_; }
124  

124  

125  
    /// Return a pointer to the underlying storage.
125  
    /// Return a pointer to the underlying storage.
126  
    void const* data() const noexcept { return &value_; }
126  
    void const* data() const noexcept { return &value_; }
127  

127  

128  
    /// Return the size of the underlying storage.
128  
    /// Return the size of the underlying storage.
129  
    std::size_t size() const noexcept { return sizeof( value_ ); }
129  
    std::size_t size() const noexcept { return sizeof( value_ ); }
130  

130  

131  
    /** Normalize after `getsockopt` returns fewer bytes than expected.
131  
    /** Normalize after `getsockopt` returns fewer bytes than expected.
132  

132  

133  
        @param s The number of bytes actually written by `getsockopt`.
133  
        @param s The number of bytes actually written by `getsockopt`.
134  
    */
134  
    */
135  
    void resize( std::size_t s ) noexcept
135  
    void resize( std::size_t s ) noexcept
136  
    {
136  
    {
137  
        if ( s == sizeof( char ) )
137  
        if ( s == sizeof( char ) )
138  
            value_ = static_cast<int>(
138  
            value_ = static_cast<int>(
139  
                *reinterpret_cast<unsigned char*>( &value_ ) );
139  
                *reinterpret_cast<unsigned char*>( &value_ ) );
140  
    }
140  
    }
141  
};
141  
};
142  

142  

143  
/** Disable Nagle's algorithm (TCP_NODELAY).
143  
/** Disable Nagle's algorithm (TCP_NODELAY).
144  

144  

145  
    @par Example
145  
    @par Example
146  
    @code
146  
    @code
147  
    sock.set_option( socket_option::no_delay( true ) );
147  
    sock.set_option( socket_option::no_delay( true ) );
148  
    auto nd = sock.get_option<socket_option::no_delay>();
148  
    auto nd = sock.get_option<socket_option::no_delay>();
149  
    if ( nd.value() )
149  
    if ( nd.value() )
150  
        // Nagle's algorithm is disabled
150  
        // Nagle's algorithm is disabled
151  
    @endcode
151  
    @endcode
152  
*/
152  
*/
153  
class BOOST_COROSIO_DECL no_delay : public boolean_option
153  
class BOOST_COROSIO_DECL no_delay : public boolean_option
154  
{
154  
{
155  
public:
155  
public:
156  
    using boolean_option::boolean_option;
156  
    using boolean_option::boolean_option;
157  
    using boolean_option::operator=;
157  
    using boolean_option::operator=;
158  

158  

159  
    /// Return the protocol level.
159  
    /// Return the protocol level.
160  
    static int level() noexcept;
160  
    static int level() noexcept;
161  

161  

162  
    /// Return the option name.
162  
    /// Return the option name.
163  
    static int name() noexcept;
163  
    static int name() noexcept;
164  
};
164  
};
165  

165  

166  
/** Enable periodic keepalive probes (SO_KEEPALIVE).
166  
/** Enable periodic keepalive probes (SO_KEEPALIVE).
167  

167  

168  
    @par Example
168  
    @par Example
169  
    @code
169  
    @code
170  
    sock.set_option( socket_option::keep_alive( true ) );
170  
    sock.set_option( socket_option::keep_alive( true ) );
171  
    @endcode
171  
    @endcode
172  
*/
172  
*/
173  
class BOOST_COROSIO_DECL keep_alive : public boolean_option
173  
class BOOST_COROSIO_DECL keep_alive : public boolean_option
174  
{
174  
{
175  
public:
175  
public:
176  
    using boolean_option::boolean_option;
176  
    using boolean_option::boolean_option;
177  
    using boolean_option::operator=;
177  
    using boolean_option::operator=;
178  

178  

179  
    /// Return the protocol level.
179  
    /// Return the protocol level.
180  
    static int level() noexcept;
180  
    static int level() noexcept;
181  

181  

182  
    /// Return the option name.
182  
    /// Return the option name.
183  
    static int name() noexcept;
183  
    static int name() noexcept;
184  
};
184  
};
185  

185  

186  
/** Restrict an IPv6 socket to IPv6 only (IPV6_V6ONLY).
186  
/** Restrict an IPv6 socket to IPv6 only (IPV6_V6ONLY).
187  

187  

188  
    When enabled, the socket only accepts IPv6 connections.
188  
    When enabled, the socket only accepts IPv6 connections.
189  
    When disabled, the socket accepts both IPv4 and IPv6
189  
    When disabled, the socket accepts both IPv4 and IPv6
190  
    connections (dual-stack mode).
190  
    connections (dual-stack mode).
191  

191  

192  
    @par Example
192  
    @par Example
193  
    @code
193  
    @code
194  
    sock.set_option( socket_option::v6_only( true ) );
194  
    sock.set_option( socket_option::v6_only( true ) );
195  
    @endcode
195  
    @endcode
196  
*/
196  
*/
197  
class BOOST_COROSIO_DECL v6_only : public boolean_option
197  
class BOOST_COROSIO_DECL v6_only : public boolean_option
198  
{
198  
{
199  
public:
199  
public:
200  
    using boolean_option::boolean_option;
200  
    using boolean_option::boolean_option;
201  
    using boolean_option::operator=;
201  
    using boolean_option::operator=;
202  

202  

203  
    /// Return the protocol level.
203  
    /// Return the protocol level.
204  
    static int level() noexcept;
204  
    static int level() noexcept;
205  

205  

206  
    /// Return the option name.
206  
    /// Return the option name.
207  
    static int name() noexcept;
207  
    static int name() noexcept;
208  
};
208  
};
209  

209  

210  
/** Allow local address reuse (SO_REUSEADDR).
210  
/** Allow local address reuse (SO_REUSEADDR).
211  

211  

212  
    @par Example
212  
    @par Example
213  
    @code
213  
    @code
214  
    acc.set_option( socket_option::reuse_address( true ) );
214  
    acc.set_option( socket_option::reuse_address( true ) );
215  
    @endcode
215  
    @endcode
216  
*/
216  
*/
217  
class BOOST_COROSIO_DECL reuse_address : public boolean_option
217  
class BOOST_COROSIO_DECL reuse_address : public boolean_option
218  
{
218  
{
219  
public:
219  
public:
220  
    using boolean_option::boolean_option;
220  
    using boolean_option::boolean_option;
221  
    using boolean_option::operator=;
221  
    using boolean_option::operator=;
222  

222  

223  
    /// Return the protocol level.
223  
    /// Return the protocol level.
224  
    static int level() noexcept;
224  
    static int level() noexcept;
225  

225  

226  
    /// Return the option name.
226  
    /// Return the option name.
227  
    static int name() noexcept;
227  
    static int name() noexcept;
228  
};
228  
};
229  

229  

230  
/** Allow multiple sockets to bind to the same port (SO_REUSEPORT).
230  
/** Allow multiple sockets to bind to the same port (SO_REUSEPORT).
231  

231  

232  
    Not available on all platforms. On unsupported platforms,
232  
    Not available on all platforms. On unsupported platforms,
233  
    `set_option` will return an error.
233  
    `set_option` will return an error.
234  

234  

235  
    @par Example
235  
    @par Example
236  
    @code
236  
    @code
237  
    acc.open( tcp::v6() );
237  
    acc.open( tcp::v6() );
238  
    acc.set_option( socket_option::reuse_port( true ) );
238  
    acc.set_option( socket_option::reuse_port( true ) );
239  
    acc.bind( endpoint( ipv6_address::any(), 8080 ) );
239  
    acc.bind( endpoint( ipv6_address::any(), 8080 ) );
240  
    acc.listen();
240  
    acc.listen();
241  
    @endcode
241  
    @endcode
242  
*/
242  
*/
243  
class BOOST_COROSIO_DECL reuse_port : public boolean_option
243  
class BOOST_COROSIO_DECL reuse_port : public boolean_option
244  
{
244  
{
245  
public:
245  
public:
246  
    using boolean_option::boolean_option;
246  
    using boolean_option::boolean_option;
247  
    using boolean_option::operator=;
247  
    using boolean_option::operator=;
248  

248  

249  
    /// Return the protocol level.
249  
    /// Return the protocol level.
250  
    static int level() noexcept;
250  
    static int level() noexcept;
251  

251  

252  
    /// Return the option name.
252  
    /// Return the option name.
253  
    static int name() noexcept;
253  
    static int name() noexcept;
254  
};
254  
};
255  

255  

256  
/** Set the receive buffer size (SO_RCVBUF).
256  
/** Set the receive buffer size (SO_RCVBUF).
257  

257  

258  
    @par Example
258  
    @par Example
259  
    @code
259  
    @code
260  
    sock.set_option( socket_option::receive_buffer_size( 65536 ) );
260  
    sock.set_option( socket_option::receive_buffer_size( 65536 ) );
261  
    auto opt = sock.get_option<socket_option::receive_buffer_size>();
261  
    auto opt = sock.get_option<socket_option::receive_buffer_size>();
262  
    int sz = opt.value();
262  
    int sz = opt.value();
263  
    @endcode
263  
    @endcode
264  
*/
264  
*/
265  
class BOOST_COROSIO_DECL receive_buffer_size : public integer_option
265  
class BOOST_COROSIO_DECL receive_buffer_size : public integer_option
266  
{
266  
{
267  
public:
267  
public:
268  
    using integer_option::integer_option;
268  
    using integer_option::integer_option;
269  
    using integer_option::operator=;
269  
    using integer_option::operator=;
270  

270  

271  
    /// Return the protocol level.
271  
    /// Return the protocol level.
272  
    static int level() noexcept;
272  
    static int level() noexcept;
273  

273  

274  
    /// Return the option name.
274  
    /// Return the option name.
275  
    static int name() noexcept;
275  
    static int name() noexcept;
276  
};
276  
};
277  

277  

278  
/** Set the send buffer size (SO_SNDBUF).
278  
/** Set the send buffer size (SO_SNDBUF).
279  

279  

280  
    @par Example
280  
    @par Example
281  
    @code
281  
    @code
282  
    sock.set_option( socket_option::send_buffer_size( 65536 ) );
282  
    sock.set_option( socket_option::send_buffer_size( 65536 ) );
283  
    @endcode
283  
    @endcode
284  
*/
284  
*/
285  
class BOOST_COROSIO_DECL send_buffer_size : public integer_option
285  
class BOOST_COROSIO_DECL send_buffer_size : public integer_option
286  
{
286  
{
287  
public:
287  
public:
288  
    using integer_option::integer_option;
288  
    using integer_option::integer_option;
289  
    using integer_option::operator=;
289  
    using integer_option::operator=;
290  

290  

291  
    /// Return the protocol level.
291  
    /// Return the protocol level.
292  
    static int level() noexcept;
292  
    static int level() noexcept;
293  

293  

294  
    /// Return the option name.
294  
    /// Return the option name.
295  
    static int name() noexcept;
295  
    static int name() noexcept;
296  
};
296  
};
297  

297  

298  
/** The SO_LINGER socket option.
298  
/** The SO_LINGER socket option.
299  

299  

300  
    Controls behavior when closing a socket with unsent data.
300  
    Controls behavior when closing a socket with unsent data.
301  
    When enabled, `close()` blocks until pending data is sent
301  
    When enabled, `close()` blocks until pending data is sent
302  
    or the timeout expires.
302  
    or the timeout expires.
303  

303  

304  
    @par Example
304  
    @par Example
305  
    @code
305  
    @code
306  
    sock.set_option( socket_option::linger( true, 5 ) );
306  
    sock.set_option( socket_option::linger( true, 5 ) );
307  
    auto opt = sock.get_option<socket_option::linger>();
307  
    auto opt = sock.get_option<socket_option::linger>();
308  
    if ( opt.enabled() )
308  
    if ( opt.enabled() )
309  
        std::cout << "linger timeout: " << opt.timeout() << "s\n";
309  
        std::cout << "linger timeout: " << opt.timeout() << "s\n";
310  
    @endcode
310  
    @endcode
311  
*/
311  
*/
312  
class BOOST_COROSIO_DECL linger
312  
class BOOST_COROSIO_DECL linger
313  
{
313  
{
314  
    // Opaque storage for the platform's struct linger.
314  
    // Opaque storage for the platform's struct linger.
315  
    // POSIX: { int, int } = 8 bytes.
315  
    // POSIX: { int, int } = 8 bytes.
316  
    // Windows: { u_short, u_short } = 4 bytes.
316  
    // Windows: { u_short, u_short } = 4 bytes.
317  
    static constexpr std::size_t max_storage_ = 8;
317  
    static constexpr std::size_t max_storage_ = 8;
318  
    alignas( 4 ) unsigned char storage_[max_storage_]{};
318  
    alignas( 4 ) unsigned char storage_[max_storage_]{};
319  

319  

320  
public:
320  
public:
321  
    /// Construct with default values (disabled, zero timeout).
321  
    /// Construct with default values (disabled, zero timeout).
322  
    linger() noexcept = default;
322  
    linger() noexcept = default;
323  

323  

324  
    /** Construct with explicit values.
324  
    /** Construct with explicit values.
325  

325  

326  
        @param enabled `true` to enable linger behavior on close.
326  
        @param enabled `true` to enable linger behavior on close.
327  
        @param timeout The linger timeout in seconds.
327  
        @param timeout The linger timeout in seconds.
328  
    */
328  
    */
329  
    linger( bool enabled, int timeout ) noexcept;
329  
    linger( bool enabled, int timeout ) noexcept;
330  

330  

331  
    /// Return whether linger is enabled.
331  
    /// Return whether linger is enabled.
332  
    bool enabled() const noexcept;
332  
    bool enabled() const noexcept;
333  

333  

334  
    /// Set whether linger is enabled.
334  
    /// Set whether linger is enabled.
335  
    void enabled( bool v ) noexcept;
335  
    void enabled( bool v ) noexcept;
336  

336  

337  
    /// Return the linger timeout in seconds.
337  
    /// Return the linger timeout in seconds.
338  
    int timeout() const noexcept;
338  
    int timeout() const noexcept;
339  

339  

340  
    /// Set the linger timeout in seconds.
340  
    /// Set the linger timeout in seconds.
341  
    void timeout( int v ) noexcept;
341  
    void timeout( int v ) noexcept;
342  

342  

343  
    /// Return the protocol level.
343  
    /// Return the protocol level.
344  
    static int level() noexcept;
344  
    static int level() noexcept;
345  

345  

346  
    /// Return the option name.
346  
    /// Return the option name.
347  
    static int name() noexcept;
347  
    static int name() noexcept;
348  

348  

349  
    /// Return a pointer to the underlying storage.
349  
    /// Return a pointer to the underlying storage.
350  
    void* data() noexcept { return storage_; }
350  
    void* data() noexcept { return storage_; }
351  

351  

352  
    /// Return a pointer to the underlying storage.
352  
    /// Return a pointer to the underlying storage.
353  
    void const* data() const noexcept { return storage_; }
353  
    void const* data() const noexcept { return storage_; }
354  

354  

355  
    /// Return the size of the underlying storage.
355  
    /// Return the size of the underlying storage.
356  
    std::size_t size() const noexcept;
356  
    std::size_t size() const noexcept;
357  

357  

358  
    /** Normalize after `getsockopt`.
358  
    /** Normalize after `getsockopt`.
359  

359  

360  
        No-op — `struct linger` is always returned at full size.
360  
        No-op — `struct linger` is always returned at full size.
361  

361  

362  
        @param s The number of bytes actually written by `getsockopt`.
362  
        @param s The number of bytes actually written by `getsockopt`.
363  
    */
363  
    */
364  
    void resize( std::size_t ) noexcept {}
364  
    void resize( std::size_t ) noexcept {}
365  
};
365  
};
366  

366  

367  
} // namespace boost::corosio::socket_option
367  
} // namespace boost::corosio::socket_option
368  

368  

369  
#endif // BOOST_COROSIO_SOCKET_OPTION_HPP
369  
#endif // BOOST_COROSIO_SOCKET_OPTION_HPP