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
 
90 +
    {
 
91 +
        return value_ != 0;
 
92 +
    }
90  

93  

91  
    /// Return the option value.
94  
    /// Return the option value.
92 -
    explicit operator bool() const noexcept { return value_ != 0; }
95 +
    explicit operator bool() const noexcept
 
96 +
    {
 
97 +
        return value_ != 0;
 
98 +
    }
93  

99  

94  
    /// Return the negated option value.
100  
    /// Return the negated option value.
95 -
    bool operator!() const noexcept { return value_ == 0; }
101 +
    bool operator!() const noexcept
 
102 +
    {
 
103 +
        return value_ == 0;
 
104 +
    }
96  

105  

97  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
106  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
98 -
    static constexpr int level() noexcept { return Level; }
107 +
    static constexpr int level() noexcept
 
108 +
    {
 
109 +
        return Level;
 
110 +
    }
99  

111  

100  
    /// Return the option name for `setsockopt`/`getsockopt`.
112  
    /// Return the option name for `setsockopt`/`getsockopt`.
101 -
    static constexpr int name() noexcept { return Name; }
113 +
    static constexpr int name() noexcept
 
114 +
    {
 
115 +
        return Name;
 
116 +
    }
102  

117  

103  
    /// Return a pointer to the underlying storage.
118  
    /// Return a pointer to the underlying storage.
104 -
    void* data() noexcept { return &value_; }
119 +
    void* data() noexcept
 
120 +
    {
 
121 +
        return &value_;
 
122 +
    }
105  

123  

106  
    /// Return a pointer to the underlying storage.
124  
    /// Return a pointer to the underlying storage.
107 -
    void const* data() const noexcept { return &value_; }
125 +
    void const* data() const noexcept
 
126 +
    {
 
127 +
        return &value_;
 
128 +
    }
108  

129  

109  
    /// Return the size of the underlying storage.
130  
    /// Return the size of the underlying storage.
110 -
    std::size_t size() const noexcept { return sizeof( value_ ); }
131 +
    std::size_t size() const noexcept
 
132 +
    {
 
133 +
        return sizeof(value_);
 
134 +
    }
111  

135  

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

137  

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

139  

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

148  

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

150  

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

154  

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

158  

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

165  

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

173  

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

177  

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

179  

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

183  

160  
    /// Assign a new value.
184  
    /// Assign a new value.
161 -
    integer& operator=( int v ) noexcept
185 +
    integer& operator=(int v) noexcept
162  
    {
186  
    {
163  
        value_ = v;
187  
        value_ = v;
164  
        return *this;
188  
        return *this;
165  
    }
189  
    }
166  

190  

167  
    /// Return the option value.
191  
    /// Return the option value.
168 -
    int value() const noexcept { return value_; }
192 +
    int value() const noexcept
 
193 +
    {
 
194 +
        return value_;
 
195 +
    }
169  

196  

170  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
197  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
171 -
    static constexpr int level() noexcept { return Level; }
198 +
    static constexpr int level() noexcept
 
199 +
    {
 
200 +
        return Level;
 
201 +
    }
172  

202  

173  
    /// Return the option name for `setsockopt`/`getsockopt`.
203  
    /// Return the option name for `setsockopt`/`getsockopt`.
174 -
    static constexpr int name() noexcept { return Name; }
204 +
    static constexpr int name() noexcept
 
205 +
    {
 
206 +
        return Name;
 
207 +
    }
175  

208  

176  
    /// Return a pointer to the underlying storage.
209  
    /// Return a pointer to the underlying storage.
177 -
    void* data() noexcept { return &value_; }
210 +
    void* data() noexcept
 
211 +
    {
 
212 +
        return &value_;
 
213 +
    }
178  

214  

179  
    /// Return a pointer to the underlying storage.
215  
    /// Return a pointer to the underlying storage.
180 -
    void const* data() const noexcept { return &value_; }
216 +
    void const* data() const noexcept
 
217 +
    {
 
218 +
        return &value_;
 
219 +
    }
181  

220  

182  
    /// Return the size of the underlying storage.
221  
    /// Return the size of the underlying storage.
183 -
    std::size_t size() const noexcept { return sizeof( value_ ); }
222 +
    std::size_t size() const noexcept
 
223 +
    {
 
224 +
        return sizeof(value_);
 
225 +
    }
184  

226  

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

228  

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

238  

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

240  

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

244  

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

248  

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

260  

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

264  

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

266  

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

275  

235  
    /// Return whether linger is enabled.
276  
    /// Return whether linger is enabled.
236 -
    bool enabled() const noexcept { return value_.l_onoff != 0; }
277 +
    bool enabled() const noexcept
 
278 +
    {
 
279 +
        return value_.l_onoff != 0;
 
280 +
    }
237  

281  

238  
    /// Set whether linger is enabled.
282  
    /// Set whether linger is enabled.
239 -
    void enabled( bool v ) noexcept { value_.l_onoff = v ? 1 : 0; }
283 +
    void enabled(bool v) noexcept
 
284 +
    {
 
285 +
        value_.l_onoff = v ? 1 : 0;
 
286 +
    }
240  

287  

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

293  

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

299  

254  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
300  
    /// Return the protocol level for `setsockopt`/`getsockopt`.
255 -
    static constexpr int level() noexcept { return SOL_SOCKET; }
301 +
    static constexpr int level() noexcept
 
302 +
    {
 
303 +
        return SOL_SOCKET;
 
304 +
    }
256  

305  

257  
    /// Return the option name for `setsockopt`/`getsockopt`.
306  
    /// Return the option name for `setsockopt`/`getsockopt`.
258 -
    static constexpr int name() noexcept { return SO_LINGER; }
307 +
    static constexpr int name() noexcept
 
308 +
    {
 
309 +
        return SO_LINGER;
 
310 +
    }
259  

311  

260  
    /// Return a pointer to the underlying storage.
312  
    /// Return a pointer to the underlying storage.
261 -
    void* data() noexcept { return &value_; }
313 +
    void* data() noexcept
 
314 +
    {
 
315 +
        return &value_;
 
316 +
    }
262  

317  

263  
    /// Return a pointer to the underlying storage.
318  
    /// Return a pointer to the underlying storage.
264 -
    void const* data() const noexcept { return &value_; }
319 +
    void const* data() const noexcept
 
320 +
    {
 
321 +
        return &value_;
 
322 +
    }
265  

323  

266  
    /// Return the size of the underlying storage.
324  
    /// Return the size of the underlying storage.
267 -
    std::size_t size() const noexcept { return sizeof( value_ ); }
325 +
    std::size_t size() const noexcept
 
326 +
    {
 
327 +
        return sizeof(value_);
 
328 +
    }
268  

329  

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

331  

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

333  

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

338  

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

341  

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

344  

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

347  

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

350  

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

353  

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

356  

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

361  

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

363  

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