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