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
89  
    bool value() const noexcept
90  
    {
90  
    {
91  
        return value_ != 0;
91  
        return value_ != 0;
92  
    }
92  
    }
93  

93  

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

99  

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

105  

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

111  

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

117  

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

123  

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

129  

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

135  

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

137  

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

139  

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

148  

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

150  

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

154  

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

158  

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

165  

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

173  

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

177  

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

179  

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

183  

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

190  

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

196  

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

202  

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

208  

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

214  

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

220  

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

226  

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

228  

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

238  

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

240  

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

244  

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

248  

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

260  

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

264  

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

266  

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

275  

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

281  

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

287  

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

293  

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

299  

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

305  

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

311  

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

317  

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

323  

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

329  

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

331  

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

333  

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

338  

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

341  

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

344  

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

347  

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

350  

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

353  

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

356  

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

361  

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

363  

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