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  
#include <cstddef>
41  
#include <cstddef>
42  

42  

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

44  

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

46  

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

50  

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

54  

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

62  

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

70  

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

74  

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

76  

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

80  

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

87  

88  
    /// Return the option value.
88  
    /// Return the option value.
89  
    bool value() const noexcept { return value_ != 0; }
89  
    bool value() const noexcept { return value_ != 0; }
90  

90  

91  
    /// Return the option value.
91  
    /// Return the option value.
92  
    explicit operator bool() const noexcept { return value_ != 0; }
92  
    explicit operator bool() const noexcept { return value_ != 0; }
93  

93  

94  
    /// Return the negated option value.
94  
    /// Return the negated option value.
95  
    bool operator!() const noexcept { return value_ == 0; }
95  
    bool operator!() const noexcept { return value_ == 0; }
96  

96  

97  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
97  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
98  
    static constexpr int level() noexcept { return Level; }
98  
    static constexpr int level() noexcept { return Level; }
99  

99  

100  
    /// Return the option name for `setsockopt`/`getsockopt`.
100  
    /// Return the option name for `setsockopt`/`getsockopt`.
101  
    static constexpr int name() noexcept { return Name; }
101  
    static constexpr int name() noexcept { return Name; }
102  

102  

103  
    /// Return a pointer to the underlying storage.
103  
    /// Return a pointer to the underlying storage.
104  
    void* data() noexcept { return &value_; }
104  
    void* data() noexcept { return &value_; }
105  

105  

106  
    /// Return a pointer to the underlying storage.
106  
    /// Return a pointer to the underlying storage.
107  
    void const* data() const noexcept { return &value_; }
107  
    void const* data() const noexcept { return &value_; }
108  

108  

109  
    /// Return the size of the underlying storage.
109  
    /// Return the size of the underlying storage.
110  
    std::size_t size() const noexcept { return sizeof( value_ ); }
110  
    std::size_t size() const noexcept { return sizeof( value_ ); }
111  

111  

112  
    /** Normalize after `getsockopt` returns fewer bytes than expected.
112  
    /** Normalize after `getsockopt` returns fewer bytes than expected.
113  

113  

114  
        Windows Vista+ may write only 1 byte for boolean options.
114  
        Windows Vista+ may write only 1 byte for boolean options.
115  

115  

116  
        @param s The number of bytes actually written by `getsockopt`.
116  
        @param s The number of bytes actually written by `getsockopt`.
117  
    */
117  
    */
118  
    void resize( std::size_t s ) noexcept
118  
    void resize( std::size_t s ) noexcept
119  
    {
119  
    {
120  
        if ( s == sizeof( char ) )
120  
        if ( s == sizeof( char ) )
121  
            value_ = *reinterpret_cast<unsigned char*>( &value_ ) ? 1 : 0;
121  
            value_ = *reinterpret_cast<unsigned char*>( &value_ ) ? 1 : 0;
122  
    }
122  
    }
123  
};
123  
};
124  

124  

125  
/** A socket option with an integer value.
125  
/** A socket option with an integer value.
126  

126  

127  
    Models socket options whose underlying representation is a
127  
    Models socket options whose underlying representation is a
128  
    plain `int`. The option's protocol level and name are encoded
128  
    plain `int`. The option's protocol level and name are encoded
129  
    as template parameters.
129  
    as template parameters.
130  

130  

131  
    This is the native (inline) variant that includes platform
131  
    This is the native (inline) variant that includes platform
132  
    headers. For a type-erased version that avoids platform
132  
    headers. For a type-erased version that avoids platform
133  
    includes, use `boost::corosio::socket_option` instead.
133  
    includes, use `boost::corosio::socket_option` instead.
134  

134  

135  
    @par Example
135  
    @par Example
136  
    @code
136  
    @code
137  
    sock.set_option( native_socket_option::receive_buffer_size( 65536 ) );
137  
    sock.set_option( native_socket_option::receive_buffer_size( 65536 ) );
138  
    auto opt = sock.get_option<native_socket_option::receive_buffer_size>();
138  
    auto opt = sock.get_option<native_socket_option::receive_buffer_size>();
139  
    int sz = opt.value();
139  
    int sz = opt.value();
140  
    @endcode
140  
    @endcode
141  

141  

142  
    @tparam Level The protocol level (e.g. `SOL_SOCKET`).
142  
    @tparam Level The protocol level (e.g. `SOL_SOCKET`).
143  
    @tparam Name The option name (e.g. `SO_RCVBUF`).
143  
    @tparam Name The option name (e.g. `SO_RCVBUF`).
144  
*/
144  
*/
145  
template<int Level, int Name>
145  
template<int Level, int Name>
146  
class integer
146  
class integer
147  
{
147  
{
148  
    int value_ = 0;
148  
    int value_ = 0;
149  

149  

150  
public:
150  
public:
151  
    /// Construct with default value (zero).
151  
    /// Construct with default value (zero).
152  
    integer() = default;
152  
    integer() = default;
153  

153  

154  
    /** Construct with an explicit value.
154  
    /** Construct with an explicit value.
155  

155  

156  
        @param v The option value.
156  
        @param v The option value.
157  
    */
157  
    */
158  
    explicit integer( int v ) noexcept : value_( v ) {}
158  
    explicit integer( int v ) noexcept : value_( v ) {}
159  

159  

160  
    /// Assign a new value.
160  
    /// Assign a new value.
161  
    integer& operator=( int v ) noexcept
161  
    integer& operator=( int v ) noexcept
162  
    {
162  
    {
163  
        value_ = v;
163  
        value_ = v;
164  
        return *this;
164  
        return *this;
165  
    }
165  
    }
166  

166  

167  
    /// Return the option value.
167  
    /// Return the option value.
168  
    int value() const noexcept { return value_; }
168  
    int value() const noexcept { return value_; }
169  

169  

170  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
170  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
171  
    static constexpr int level() noexcept { return Level; }
171  
    static constexpr int level() noexcept { return Level; }
172  

172  

173  
    /// Return the option name for `setsockopt`/`getsockopt`.
173  
    /// Return the option name for `setsockopt`/`getsockopt`.
174  
    static constexpr int name() noexcept { return Name; }
174  
    static constexpr int name() noexcept { return Name; }
175  

175  

176  
    /// Return a pointer to the underlying storage.
176  
    /// Return a pointer to the underlying storage.
177  
    void* data() noexcept { return &value_; }
177  
    void* data() noexcept { return &value_; }
178  

178  

179  
    /// Return a pointer to the underlying storage.
179  
    /// Return a pointer to the underlying storage.
180  
    void const* data() const noexcept { return &value_; }
180  
    void const* data() const noexcept { return &value_; }
181  

181  

182  
    /// Return the size of the underlying storage.
182  
    /// Return the size of the underlying storage.
183  
    std::size_t size() const noexcept { return sizeof( value_ ); }
183  
    std::size_t size() const noexcept { return sizeof( value_ ); }
184  

184  

185  
    /** Normalize after `getsockopt` returns fewer bytes than expected.
185  
    /** Normalize after `getsockopt` returns fewer bytes than expected.
186  

186  

187  
        @param s The number of bytes actually written by `getsockopt`.
187  
        @param s The number of bytes actually written by `getsockopt`.
188  
    */
188  
    */
189  
    void resize( std::size_t s ) noexcept
189  
    void resize( std::size_t s ) noexcept
190  
    {
190  
    {
191  
        if ( s == sizeof( char ) )
191  
        if ( s == sizeof( char ) )
192  
            value_ = static_cast<int>(
192  
            value_ = static_cast<int>(
193  
                *reinterpret_cast<unsigned char*>( &value_ ) );
193  
                *reinterpret_cast<unsigned char*>( &value_ ) );
194  
    }
194  
    }
195  
};
195  
};
196  

196  

197  
/** The SO_LINGER socket option (native variant).
197  
/** The SO_LINGER socket option (native variant).
198  

198  

199  
    Controls behavior when closing a socket with unsent data.
199  
    Controls behavior when closing a socket with unsent data.
200  
    When enabled, `close()` blocks until pending data is sent
200  
    When enabled, `close()` blocks until pending data is sent
201  
    or the timeout expires.
201  
    or the timeout expires.
202  

202  

203  
    This variant stores the platform's `struct linger` directly,
203  
    This variant stores the platform's `struct linger` directly,
204  
    avoiding the opaque-storage indirection of the type-erased
204  
    avoiding the opaque-storage indirection of the type-erased
205  
    version.
205  
    version.
206  

206  

207  
    @par Example
207  
    @par Example
208  
    @code
208  
    @code
209  
    sock.set_option( native_socket_option::linger( true, 5 ) );
209  
    sock.set_option( native_socket_option::linger( true, 5 ) );
210  
    auto opt = sock.get_option<native_socket_option::linger>();
210  
    auto opt = sock.get_option<native_socket_option::linger>();
211  
    if ( opt.enabled() )
211  
    if ( opt.enabled() )
212  
        std::cout << "linger timeout: " << opt.timeout() << "s\n";
212  
        std::cout << "linger timeout: " << opt.timeout() << "s\n";
213  
    @endcode
213  
    @endcode
214  
*/
214  
*/
215  
class linger
215  
class linger
216  
{
216  
{
217  
    struct ::linger value_{};
217  
    struct ::linger value_{};
218  

218  

219  
public:
219  
public:
220  
    /// Construct with default values (disabled, zero timeout).
220  
    /// Construct with default values (disabled, zero timeout).
221  
    linger() = default;
221  
    linger() = default;
222  

222  

223  
    /** Construct with explicit values.
223  
    /** Construct with explicit values.
224  

224  

225  
        @param enabled `true` to enable linger behavior on close.
225  
        @param enabled `true` to enable linger behavior on close.
226  
        @param timeout The linger timeout in seconds.
226  
        @param timeout The linger timeout in seconds.
227  
    */
227  
    */
228  
    linger( bool enabled, int timeout ) noexcept
228  
    linger( bool enabled, int timeout ) noexcept
229  
    {
229  
    {
230  
        value_.l_onoff = enabled ? 1 : 0;
230  
        value_.l_onoff = enabled ? 1 : 0;
231  
        value_.l_linger =
231  
        value_.l_linger =
232  
            static_cast<decltype( value_.l_linger )>( timeout );
232  
            static_cast<decltype( value_.l_linger )>( timeout );
233  
    }
233  
    }
234  

234  

235  
    /// Return whether linger is enabled.
235  
    /// Return whether linger is enabled.
236  
    bool enabled() const noexcept { return value_.l_onoff != 0; }
236  
    bool enabled() const noexcept { return value_.l_onoff != 0; }
237  

237  

238  
    /// Set whether linger is enabled.
238  
    /// Set whether linger is enabled.
239  
    void enabled( bool v ) noexcept { value_.l_onoff = v ? 1 : 0; }
239  
    void enabled( bool v ) noexcept { value_.l_onoff = v ? 1 : 0; }
240  

240  

241  
    /// Return the linger timeout in seconds.
241  
    /// Return the linger timeout in seconds.
242  
    int timeout() const noexcept
242  
    int timeout() const noexcept
243  
    {
243  
    {
244  
        return static_cast<int>( value_.l_linger );
244  
        return static_cast<int>( value_.l_linger );
245  
    }
245  
    }
246  

246  

247  
    /// Set the linger timeout in seconds.
247  
    /// Set the linger timeout in seconds.
248  
    void timeout( int v ) noexcept
248  
    void timeout( int v ) noexcept
249  
    {
249  
    {
250  
        value_.l_linger =
250  
        value_.l_linger =
251  
            static_cast<decltype( value_.l_linger )>( v );
251  
            static_cast<decltype( value_.l_linger )>( v );
252  
    }
252  
    }
253  

253  

254  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
254  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
255  
    static constexpr int level() noexcept { return SOL_SOCKET; }
255  
    static constexpr int level() noexcept { return SOL_SOCKET; }
256  

256  

257  
    /// Return the option name for `setsockopt`/`getsockopt`.
257  
    /// Return the option name for `setsockopt`/`getsockopt`.
258  
    static constexpr int name() noexcept { return SO_LINGER; }
258  
    static constexpr int name() noexcept { return SO_LINGER; }
259  

259  

260  
    /// Return a pointer to the underlying storage.
260  
    /// Return a pointer to the underlying storage.
261  
    void* data() noexcept { return &value_; }
261  
    void* data() noexcept { return &value_; }
262  

262  

263  
    /// Return a pointer to the underlying storage.
263  
    /// Return a pointer to the underlying storage.
264  
    void const* data() const noexcept { return &value_; }
264  
    void const* data() const noexcept { return &value_; }
265  

265  

266  
    /// Return the size of the underlying storage.
266  
    /// Return the size of the underlying storage.
267  
    std::size_t size() const noexcept { return sizeof( value_ ); }
267  
    std::size_t size() const noexcept { return sizeof( value_ ); }
268  

268  

269  
    /** Normalize after `getsockopt`.
269  
    /** Normalize after `getsockopt`.
270  

270  

271  
        No-op — `struct linger` is always returned at full size.
271  
        No-op — `struct linger` is always returned at full size.
272  

272  

273  
        @param s The number of bytes actually written by `getsockopt`.
273  
        @param s The number of bytes actually written by `getsockopt`.
274  
    */
274  
    */
275  
    void resize( std::size_t ) noexcept {}
275  
    void resize( std::size_t ) noexcept {}
276  
};
276  
};
277  

277  

278  
/// Disable Nagle's algorithm (TCP_NODELAY).
278  
/// Disable Nagle's algorithm (TCP_NODELAY).
279  
using no_delay = boolean<IPPROTO_TCP, TCP_NODELAY>;
279  
using no_delay = boolean<IPPROTO_TCP, TCP_NODELAY>;
280  

280  

281  
/// Enable periodic keepalive probes (SO_KEEPALIVE).
281  
/// Enable periodic keepalive probes (SO_KEEPALIVE).
282  
using keep_alive = boolean<SOL_SOCKET, SO_KEEPALIVE>;
282  
using keep_alive = boolean<SOL_SOCKET, SO_KEEPALIVE>;
283  

283  

284  
/// Restrict an IPv6 socket to IPv6 only (IPV6_V6ONLY).
284  
/// Restrict an IPv6 socket to IPv6 only (IPV6_V6ONLY).
285  
using v6_only = boolean<IPPROTO_IPV6, IPV6_V6ONLY>;
285  
using v6_only = boolean<IPPROTO_IPV6, IPV6_V6ONLY>;
286  

286  

287  
/// Allow local address reuse (SO_REUSEADDR).
287  
/// Allow local address reuse (SO_REUSEADDR).
288  
using reuse_address = boolean<SOL_SOCKET, SO_REUSEADDR>;
288  
using reuse_address = boolean<SOL_SOCKET, SO_REUSEADDR>;
289  

289  

290  
/// Set the receive buffer size (SO_RCVBUF).
290  
/// Set the receive buffer size (SO_RCVBUF).
291  
using receive_buffer_size = integer<SOL_SOCKET, SO_RCVBUF>;
291  
using receive_buffer_size = integer<SOL_SOCKET, SO_RCVBUF>;
292  

292  

293  
/// Set the send buffer size (SO_SNDBUF).
293  
/// Set the send buffer size (SO_SNDBUF).
294  
using send_buffer_size = integer<SOL_SOCKET, SO_SNDBUF>;
294  
using send_buffer_size = integer<SOL_SOCKET, SO_SNDBUF>;
295  

295  

296  
#ifdef SO_REUSEPORT
296  
#ifdef SO_REUSEPORT
297  
/// Allow multiple sockets to bind to the same port (SO_REUSEPORT).
297  
/// Allow multiple sockets to bind to the same port (SO_REUSEPORT).
298  
using reuse_port = boolean<SOL_SOCKET, SO_REUSEPORT>;
298  
using reuse_port = boolean<SOL_SOCKET, SO_REUSEPORT>;
299  
#endif
299  
#endif
300  

300  

301  
} // namespace boost::corosio::native_socket_option
301  
} // namespace boost::corosio::native_socket_option
302  

302  

303  
#endif // BOOST_COROSIO_NATIVE_NATIVE_SOCKET_OPTION_HPP
303  
#endif // BOOST_COROSIO_NATIVE_NATIVE_SOCKET_OPTION_HPP