1 +
//
 
2 +
// Copyright (c) 2026 Steve Gerbino
 
3 +
//
 
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)
 
6 +
//
 
7 +
// Official repository: https://github.com/cppalliance/corosio
 
8 +
//
 
9 +

 
10 +
/** @file native_socket_option.hpp
 
11 +

 
12 +
    Inline socket option types using platform-specific constants.
 
13 +
    All methods are `constexpr` or trivially inlined, giving zero
 
14 +
    overhead compared to hand-written `setsockopt` calls.
 
15 +

 
16 +
    This header includes platform socket headers
 
17 +
    (`<sys/socket.h>`, `<netinet/tcp.h>`, etc.).
 
18 +
    For a version that avoids platform includes, use
 
19 +
    `<boost/corosio/socket_option.hpp>`
 
20 +
    (`boost::corosio::socket_option`).
 
21 +

 
22 +
    Both variants satisfy the same option-type interface and work
 
23 +
    interchangeably with `tcp_socket::set_option` /
 
24 +
    `tcp_socket::get_option` and the corresponding acceptor methods.
 
25 +

 
26 +
    @see boost::corosio::socket_option
 
27 +
*/
 
28 +

 
29 +
#ifndef BOOST_COROSIO_NATIVE_NATIVE_SOCKET_OPTION_HPP
 
30 +
#define BOOST_COROSIO_NATIVE_NATIVE_SOCKET_OPTION_HPP
 
31 +

 
32 +
#ifdef _WIN32
 
33 +
#include <winsock2.h>
 
34 +
#include <ws2tcpip.h>
 
35 +
#else
 
36 +
#include <netinet/in.h>
 
37 +
#include <netinet/tcp.h>
 
38 +
#include <sys/socket.h>
 
39 +
#endif
 
40 +

 
41 +
#include <cstddef>
 
42 +

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 
303 +
#endif // BOOST_COROSIO_NATIVE_NATIVE_SOCKET_OPTION_HPP