include/boost/corosio/native/native_socket_option.hpp

98.9% Lines (173 / 175) 100.0% Functions (115 / 115)
native_socket_option.hpp
f(x) Functions (115)
Function Calls Lines Blocks
boost::corosio::native_socket_option::boolean<1, 15>::boolean() :84 8x 100.0% 100.0% boost::corosio::native_socket_option::boolean<1, 2>::boolean() :84 1222x 100.0% 100.0% boost::corosio::native_socket_option::boolean<1, 6>::boolean() :84 20x 100.0% 100.0% boost::corosio::native_socket_option::boolean<1, 9>::boolean() :84 40x 100.0% 100.0% boost::corosio::native_socket_option::boolean<41, 26>::boolean() :84 68x 100.0% 100.0% boost::corosio::native_socket_option::boolean<6, 1>::boolean() :84 136x 100.0% 100.0% boost::corosio::native_socket_option::boolean<1, 15>::boolean(bool) :90 2x 100.0% 75.0% boost::corosio::native_socket_option::boolean<1, 2>::boolean(bool) :90 12x 100.0% 75.0% boost::corosio::native_socket_option::boolean<1, 6>::boolean(bool) :90 2x 100.0% 75.0% boost::corosio::native_socket_option::boolean<6, 1>::boolean(bool) :90 7x 100.0% 100.0% boost::corosio::native_socket_option::boolean<1, 15>::value() const :100 2x 100.0% 100.0% boost::corosio::native_socket_option::boolean<1, 6>::value() const :100 2x 100.0% 100.0% boost::corosio::native_socket_option::boolean<6, 1>::value() const :100 7x 100.0% 100.0% boost::corosio::native_socket_option::boolean<1, 15>::level(boost::corosio::family) const :118 8x 100.0% 100.0% boost::corosio::native_socket_option::boolean<1, 2>::level(boost::corosio::family) const :118 623x 100.0% 100.0% boost::corosio::native_socket_option::boolean<1, 6>::level(boost::corosio::family) const :118 14x 100.0% 100.0% boost::corosio::native_socket_option::boolean<1, 9>::level(boost::corosio::family) const :118 20x 100.0% 100.0% boost::corosio::native_socket_option::boolean<41, 26>::level(boost::corosio::family) const :118 34x 100.0% 100.0% boost::corosio::native_socket_option::boolean<6, 1>::level(boost::corosio::family) const :118 78x 100.0% 100.0% boost::corosio::native_socket_option::boolean<1, 15>::name(boost::corosio::family) const :124 8x 100.0% 100.0% boost::corosio::native_socket_option::boolean<1, 2>::name(boost::corosio::family) const :124 623x 100.0% 100.0% boost::corosio::native_socket_option::boolean<1, 6>::name(boost::corosio::family) const :124 14x 100.0% 100.0% boost::corosio::native_socket_option::boolean<1, 9>::name(boost::corosio::family) const :124 20x 100.0% 100.0% boost::corosio::native_socket_option::boolean<41, 26>::name(boost::corosio::family) const :124 34x 100.0% 100.0% boost::corosio::native_socket_option::boolean<6, 1>::name(boost::corosio::family) const :124 78x 100.0% 100.0% boost::corosio::native_socket_option::boolean<1, 15>::data(boost::corosio::family) :130 2x 100.0% 100.0% boost::corosio::native_socket_option::boolean<1, 6>::data(boost::corosio::family) :130 2x 100.0% 100.0% boost::corosio::native_socket_option::boolean<6, 1>::data(boost::corosio::family) :130 6x 100.0% 100.0% boost::corosio::native_socket_option::boolean<1, 15>::data(boost::corosio::family) const :136 2x 100.0% 100.0% boost::corosio::native_socket_option::boolean<1, 2>::data(boost::corosio::family) const :136 12x 100.0% 100.0% boost::corosio::native_socket_option::boolean<1, 6>::data(boost::corosio::family) const :136 2x 100.0% 100.0% boost::corosio::native_socket_option::boolean<6, 1>::data(boost::corosio::family) const :136 6x 100.0% 100.0% boost::corosio::native_socket_option::boolean<1, 15>::size(boost::corosio::family) const :142 4x 100.0% 100.0% boost::corosio::native_socket_option::boolean<1, 2>::size(boost::corosio::family) const :142 12x 100.0% 100.0% boost::corosio::native_socket_option::boolean<1, 6>::size(boost::corosio::family) const :142 4x 100.0% 100.0% boost::corosio::native_socket_option::boolean<6, 1>::size(boost::corosio::family) const :142 10x 100.0% 100.0% boost::corosio::native_socket_option::boolean<1, 15>::resize(boost::corosio::family, unsigned long) :153 2x 75.0% 33.0% boost::corosio::native_socket_option::boolean<1, 6>::resize(boost::corosio::family, unsigned long) :153 2x 75.0% 33.0% boost::corosio::native_socket_option::boolean<6, 1>::resize(boost::corosio::family, unsigned long) :153 5x 100.0% 83.0% boost::corosio::native_socket_option::integer<1, 7>::integer() :183 148x 100.0% 100.0% boost::corosio::native_socket_option::integer<1, 8>::integer() :183 114x 100.0% 100.0% boost::corosio::native_socket_option::integer<1, 7>::integer(int) :189 2x 100.0% 100.0% boost::corosio::native_socket_option::integer<1, 8>::integer(int) :189 3x 100.0% 100.0% boost::corosio::native_socket_option::integer<1, 7>::value() const :199 2x 100.0% 100.0% boost::corosio::native_socket_option::integer<1, 8>::value() const :199 3x 100.0% 100.0% boost::corosio::native_socket_option::integer<1, 7>::level(boost::corosio::family) const :205 78x 100.0% 100.0% boost::corosio::native_socket_option::integer<1, 8>::level(boost::corosio::family) const :205 61x 100.0% 100.0% boost::corosio::native_socket_option::integer<1, 7>::name(boost::corosio::family) const :211 78x 100.0% 100.0% boost::corosio::native_socket_option::integer<1, 8>::name(boost::corosio::family) const :211 61x 100.0% 100.0% boost::corosio::native_socket_option::integer<1, 7>::data(boost::corosio::family) :217 2x 100.0% 100.0% boost::corosio::native_socket_option::integer<1, 8>::data(boost::corosio::family) :217 2x 100.0% 100.0% boost::corosio::native_socket_option::integer<1, 7>::data(boost::corosio::family) const :223 2x 100.0% 100.0% boost::corosio::native_socket_option::integer<1, 8>::data(boost::corosio::family) const :223 2x 100.0% 100.0% boost::corosio::native_socket_option::integer<1, 7>::size(boost::corosio::family) const :229 4x 100.0% 100.0% boost::corosio::native_socket_option::integer<1, 8>::size(boost::corosio::family) const :229 4x 100.0% 100.0% boost::corosio::native_socket_option::integer<1, 7>::resize(boost::corosio::family, unsigned long) :238 2x 60.0% 67.0% boost::corosio::native_socket_option::integer<1, 8>::resize(boost::corosio::family, unsigned long) :238 3x 100.0% 100.0% boost::corosio::native_socket_option::linger::linger() :388 645x 100.0% 100.0% boost::corosio::native_socket_option::linger::linger(bool, int) :395 203x 100.0% 100.0% boost::corosio::native_socket_option::linger::enabled() const :402 22x 100.0% 100.0% boost::corosio::native_socket_option::linger::enabled(bool) :408 4x 100.0% 75.0% boost::corosio::native_socket_option::linger::timeout() const :414 20x 100.0% 100.0% boost::corosio::native_socket_option::linger::timeout(int) :420 4x 100.0% 100.0% boost::corosio::native_socket_option::linger::level(boost::corosio::family) const :426 217x 100.0% 100.0% boost::corosio::native_socket_option::linger::name(boost::corosio::family) const :432 217x 100.0% 100.0% boost::corosio::native_socket_option::linger::data(boost::corosio::family) :438 241x 100.0% 100.0% boost::corosio::native_socket_option::linger::data(boost::corosio::family) const :444 2x 100.0% 100.0% boost::corosio::native_socket_option::linger::size(boost::corosio::family) const :450 456x 100.0% 100.0% boost::corosio::native_socket_option::multicast_loop::multicast_loop() :504 68x 100.0% 100.0% boost::corosio::native_socket_option::multicast_loop::multicast_loop(bool) :510 6x 100.0% 71.0% boost::corosio::native_socket_option::multicast_loop::value() const :523 2x 100.0% 67.0% boost::corosio::native_socket_option::multicast_loop::level(boost::corosio::family) const :529 40x 100.0% 100.0% boost::corosio::native_socket_option::multicast_loop::name(boost::corosio::family) const :535 40x 100.0% 100.0% boost::corosio::native_socket_option::multicast_loop::data(boost::corosio::family) :541 2x 100.0% 80.0% boost::corosio::native_socket_option::multicast_loop::data(boost::corosio::family) const :548 4x 100.0% 100.0% boost::corosio::native_socket_option::multicast_loop::size(boost::corosio::family) const :555 10x 100.0% 100.0% boost::corosio::native_socket_option::multicast_loop::resize(boost::corosio::family, unsigned long) :568 2x 80.0% 50.0% boost::corosio::native_socket_option::multicast_hops::multicast_hops() :590 32x 100.0% 100.0% boost::corosio::native_socket_option::multicast_hops::multicast_hops(int) :599 6x 100.0% 100.0% boost::corosio::native_socket_option::multicast_hops::value() const :621 2x 100.0% 100.0% boost::corosio::native_socket_option::multicast_hops::level(boost::corosio::family) const :627 22x 100.0% 100.0% boost::corosio::native_socket_option::multicast_hops::name(boost::corosio::family) const :633 22x 100.0% 100.0% boost::corosio::native_socket_option::multicast_hops::data(boost::corosio::family) :639 2x 100.0% 80.0% boost::corosio::native_socket_option::multicast_hops::data(boost::corosio::family) const :646 4x 100.0% 100.0% boost::corosio::native_socket_option::multicast_hops::size(boost::corosio::family) const :653 6x 100.0% 100.0% boost::corosio::native_socket_option::multicast_hops::resize(boost::corosio::family, unsigned long) :662 2x 80.0% 75.0% boost::corosio::native_socket_option::membership_request<0, 35, 41, 20>::assign_v4(boost::corosio::ipv4_address, boost::corosio::ipv4_address) :690 52x 100.0% 100.0% boost::corosio::native_socket_option::membership_request<0, 36, 41, 21>::assign_v4(boost::corosio::ipv4_address, boost::corosio::ipv4_address) :690 36x 100.0% 100.0% boost::corosio::native_socket_option::membership_request<0, 35, 41, 20>::assign_v6(boost::corosio::ipv6_address const&, unsigned int) :699 52x 100.0% 100.0% boost::corosio::native_socket_option::membership_request<0, 36, 41, 21>::assign_v6(boost::corosio::ipv6_address const&, unsigned int) :699 38x 100.0% 86.0% boost::corosio::native_socket_option::membership_request<0, 35, 41, 20>::membership_request(boost::corosio::ip_address const&) :719 12x 100.0% 100.0% boost::corosio::native_socket_option::membership_request<0, 36, 41, 21>::membership_request(boost::corosio::ip_address const&) :719 4x 83.3% 70.0% boost::corosio::native_socket_option::membership_request<0, 35, 41, 20>::membership_request(boost::corosio::ipv4_address, boost::corosio::ipv4_address) :732 48x 100.0% 100.0% boost::corosio::native_socket_option::membership_request<0, 36, 41, 21>::membership_request(boost::corosio::ipv4_address, boost::corosio::ipv4_address) :732 32x 100.0% 100.0% boost::corosio::native_socket_option::membership_request<0, 35, 41, 20>::membership_request(boost::corosio::ipv6_address const&, unsigned int) :744 44x 100.0% 100.0% boost::corosio::native_socket_option::membership_request<0, 36, 41, 21>::membership_request(boost::corosio::ipv6_address const&, unsigned int) :744 38x 100.0% 100.0% boost::corosio::native_socket_option::membership_request<0, 35, 41, 20>::level(boost::corosio::family) const :751 46x 100.0% 100.0% boost::corosio::native_socket_option::membership_request<0, 36, 41, 21>::level(boost::corosio::family) const :751 32x 100.0% 100.0% boost::corosio::native_socket_option::membership_request<0, 35, 41, 20>::name(boost::corosio::family) const :757 34x 100.0% 100.0% boost::corosio::native_socket_option::membership_request<0, 36, 41, 21>::name(boost::corosio::family) const :757 32x 100.0% 100.0% boost::corosio::native_socket_option::membership_request<0, 35, 41, 20>::data(boost::corosio::family) const :763 32x 100.0% 100.0% boost::corosio::native_socket_option::membership_request<0, 36, 41, 21>::data(boost::corosio::family) const :763 24x 100.0% 100.0% boost::corosio::native_socket_option::membership_request<0, 35, 41, 20>::size(boost::corosio::family) const :770 56x 100.0% 100.0% boost::corosio::native_socket_option::membership_request<0, 36, 41, 21>::size(boost::corosio::family) const :770 48x 100.0% 100.0% boost::corosio::native_socket_option::multicast_interface::multicast_interface() :808 22x 100.0% 100.0% boost::corosio::native_socket_option::multicast_interface::multicast_interface(boost::corosio::ipv4_address) :814 8x 100.0% 100.0% boost::corosio::native_socket_option::multicast_interface::multicast_interface(unsigned int) :824 4x 100.0% 100.0% boost::corosio::native_socket_option::multicast_interface::address() const :830 2x 100.0% 100.0% boost::corosio::native_socket_option::multicast_interface::if_index() const :838 4x 100.0% 100.0% boost::corosio::native_socket_option::multicast_interface::level(boost::corosio::family) const :844 14x 100.0% 100.0% boost::corosio::native_socket_option::multicast_interface::name(boost::corosio::family) const :850 14x 100.0% 100.0% boost::corosio::native_socket_option::multicast_interface::data(boost::corosio::family) :856 10x 100.0% 100.0% boost::corosio::native_socket_option::multicast_interface::data(boost::corosio::family) const :863 4x 100.0% 100.0% boost::corosio::native_socket_option::multicast_interface::size(boost::corosio::family) const :870 24x 100.0% 100.0% boost::corosio::native_socket_option::multicast_interface::resize(boost::corosio::family, unsigned long) :876 2x 100.0% 100.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Steve Gerbino
3 // Copyright (c) 2026 Michael Vandeberg
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/cppalliance/corosio
9 //
10
11 /** @file native_socket_option.hpp
12
13 Inline socket option types using platform-specific constants.
14 All methods are `constexpr` or trivially inlined, giving zero
15 overhead compared to hand-written `setsockopt` calls.
16
17 This header includes platform socket headers
18 (`<sys/socket.h>`, `<netinet/tcp.h>`, etc.).
19 For a version that avoids platform includes, use
20 `<boost/corosio/socket_option.hpp>`
21 (`boost::corosio::socket_option`).
22
23 Both variants satisfy the same option-type interface and work
24 interchangeably with `tcp_socket::set_option` /
25 `tcp_socket::get_option` and the corresponding acceptor methods.
26
27 @see boost::corosio::socket_option
28 */
29
30 #ifndef BOOST_COROSIO_NATIVE_NATIVE_SOCKET_OPTION_HPP
31 #define BOOST_COROSIO_NATIVE_NATIVE_SOCKET_OPTION_HPP
32
33 #ifdef _WIN32
34 #include <winsock2.h>
35 #include <ws2tcpip.h>
36 #else
37 #include <netinet/in.h>
38 #include <netinet/tcp.h>
39 #include <sys/socket.h>
40 #endif
41
42 // Some older systems define only the legacy names
43 #ifndef IPV6_JOIN_GROUP
44 #define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP
45 #endif
46 #ifndef IPV6_LEAVE_GROUP
47 #define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP
48 #endif
49
50 #include <boost/corosio/detail/except.hpp>
51 #include <boost/corosio/family.hpp>
52 #include <boost/corosio/ip_address.hpp>
53 #include <boost/corosio/ipv4_address.hpp>
54 #include <boost/corosio/ipv6_address.hpp>
55
56 #include <cstddef>
57 #include <cstring>
58
59 namespace boost::corosio::native_socket_option {
60
61 /** A socket option with a boolean value.
62
63 Models socket options whose underlying representation is an `int`
64 where 0 means disabled and non-zero means enabled. The option's
65 protocol level and name are encoded as template parameters.
66
67 This is the native (inline) variant that includes platform
68 headers. For a type-erased version that avoids platform
69 includes, use `boost::corosio::socket_option` instead.
70
71 @par Example
72 @par !example boolean
73
74 @tparam Level The protocol level (e.g. `SOL_SOCKET`, `IPPROTO_TCP`).
75 @tparam Name The option name (e.g. `TCP_NODELAY`, `SO_KEEPALIVE`).
76 */
77 template<int Level, int Name>
78 class boolean
79 {
80 int value_ = 0;
81
82 public:
83 /// Construct with default value (disabled).
84 1494x boolean() = default;
85
86 /** Construct with an explicit value.
87
88 @param v `true` to enable the option, `false` to disable.
89 */
90 23x explicit boolean(bool v) noexcept : value_(v ? 1 : 0) {}
91
92 /// Assign a new value.
93 boolean& operator=(bool v) noexcept
94 {
95 value_ = v ? 1 : 0;
96 return *this;
97 }
98
99 /// Return the option value.
100 11x bool value() const noexcept
101 {
102 11x return value_ != 0;
103 }
104
105 /// Return the option value.
106 explicit operator bool() const noexcept
107 {
108 return value_ != 0;
109 }
110
111 /// Return the negated option value.
112 bool operator!() const noexcept
113 {
114 return value_ == 0;
115 }
116
117 /// Return the protocol level for `setsockopt`/`getsockopt`.
118 777x constexpr int level(family) const noexcept
119 {
120 777x return Level;
121 }
122
123 /// Return the option name for `setsockopt`/`getsockopt`.
124 777x constexpr int name(family) const noexcept
125 {
126 777x return Name;
127 }
128
129 /// Return a pointer to the underlying storage.
130 10x void* data(family) noexcept
131 {
132 10x return &value_;
133 }
134
135 /// Return a pointer to the underlying storage.
136 22x void const* data(family) const noexcept
137 {
138 22x return &value_;
139 }
140
141 /// Return the size of the underlying storage.
142 30x std::size_t size(family) const noexcept
143 {
144 30x return sizeof(value_);
145 }
146
147 /** Normalize after `getsockopt` returns fewer bytes than expected.
148
149 Windows Vista+ may write only 1 byte for boolean options.
150
151 @param s The number of bytes actually written by `getsockopt`.
152 */
153 9x void resize(family, std::size_t s) noexcept
154 {
155 9x if (s == sizeof(char))
156 1x value_ = *reinterpret_cast<unsigned char*>(&value_) ? 1 : 0;
157 9x }
158 };
159
160 /** A socket option with an integer value.
161
162 Models socket options whose underlying representation is a
163 plain `int`. The option's protocol level and name are encoded
164 as template parameters.
165
166 This is the native (inline) variant that includes platform
167 headers. For a type-erased version that avoids platform
168 includes, use `boost::corosio::socket_option` instead.
169
170 @par Example
171 @par !example integer
172
173 @tparam Level The protocol level (e.g. `SOL_SOCKET`).
174 @tparam Name The option name (e.g. `SO_RCVBUF`).
175 */
176 template<int Level, int Name>
177 class integer
178 {
179 int value_ = 0;
180
181 public:
182 /// Construct with default value (zero).
183 262x integer() = default;
184
185 /** Construct with an explicit value.
186
187 @param v The option value.
188 */
189 5x explicit integer(int v) noexcept : value_(v) {}
190
191 /// Assign a new value.
192 integer& operator=(int v) noexcept
193 {
194 value_ = v;
195 return *this;
196 }
197
198 /// Return the option value.
199 5x int value() const noexcept
200 {
201 5x return value_;
202 }
203
204 /// Return the protocol level for `setsockopt`/`getsockopt`.
205 139x constexpr int level(family) const noexcept
206 {
207 139x return Level;
208 }
209
210 /// Return the option name for `setsockopt`/`getsockopt`.
211 139x constexpr int name(family) const noexcept
212 {
213 139x return Name;
214 }
215
216 /// Return a pointer to the underlying storage.
217 4x void* data(family) noexcept
218 {
219 4x return &value_;
220 }
221
222 /// Return a pointer to the underlying storage.
223 4x void const* data(family) const noexcept
224 {
225 4x return &value_;
226 }
227
228 /// Return the size of the underlying storage.
229 8x std::size_t size(family) const noexcept
230 {
231 8x return sizeof(value_);
232 }
233
234 /** Normalize after `getsockopt` returns fewer bytes than expected.
235
236 @param s The number of bytes actually written by `getsockopt`.
237 */
238 5x void resize(family, std::size_t s) noexcept
239 {
240 5x if (s == sizeof(char))
241 1x value_ =
242 1x static_cast<int>(*reinterpret_cast<unsigned char*>(&value_));
243 5x }
244 };
245
246 /** A boolean socket option with single-byte storage.
247
248 Some BSD-derived kernels (macOS, FreeBSD) require certain IPv4 multicast
249 options (`IP_MULTICAST_LOOP`) to be set with a one-byte value and return
250 `EINVAL` for the four-byte form that Linux accepts. This template
251 provides `unsigned char` storage so the option works on every platform.
252
253 @tparam Level The protocol level.
254 @tparam Name The option name.
255 */
256 template<int Level, int Name>
257 class byte_boolean
258 {
259 unsigned char value_ = 0;
260
261 public:
262 byte_boolean() = default;
263
264 explicit byte_boolean(bool v) noexcept : value_(v ? 1 : 0) {}
265
266 byte_boolean& operator=(bool v) noexcept
267 {
268 value_ = v ? 1 : 0;
269 return *this;
270 }
271
272 bool value() const noexcept
273 {
274 return value_ != 0;
275 }
276 explicit operator bool() const noexcept
277 {
278 return value_ != 0;
279 }
280 bool operator!() const noexcept
281 {
282 return value_ == 0;
283 }
284
285 constexpr int level(family) const noexcept
286 {
287 return Level;
288 }
289 constexpr int name(family) const noexcept
290 {
291 return Name;
292 }
293
294 void* data(family) noexcept
295 {
296 return &value_;
297 }
298 void const* data(family) const noexcept
299 {
300 return &value_;
301 }
302 std::size_t size(family) const noexcept
303 {
304 return sizeof(value_);
305 }
306
307 void resize(family, std::size_t) noexcept {}
308 };
309
310 /** An integer socket option with single-byte storage.
311
312 Same rationale as `byte_boolean`: BSD-derived kernels require
313 `IP_MULTICAST_TTL` to be set with a one-byte value. Linux accepts
314 one byte too, so single-byte storage is portable. Values are
315 truncated to the 0–255 range.
316
317 @tparam Level The protocol level.
318 @tparam Name The option name.
319 */
320 template<int Level, int Name>
321 class byte_integer
322 {
323 unsigned char value_ = 0;
324
325 public:
326 byte_integer() = default;
327
328 explicit byte_integer(int v) noexcept
329 : value_(static_cast<unsigned char>(v))
330 {
331 }
332
333 byte_integer& operator=(int v) noexcept
334 {
335 value_ = static_cast<unsigned char>(v);
336 return *this;
337 }
338
339 int value() const noexcept
340 {
341 return value_;
342 }
343
344 constexpr int level(family) const noexcept
345 {
346 return Level;
347 }
348 constexpr int name(family) const noexcept
349 {
350 return Name;
351 }
352
353 void* data(family) noexcept
354 {
355 return &value_;
356 }
357 void const* data(family) const noexcept
358 {
359 return &value_;
360 }
361 std::size_t size(family) const noexcept
362 {
363 return sizeof(value_);
364 }
365
366 void resize(family, std::size_t) noexcept {}
367 };
368
369 /** The SO_LINGER socket option (native variant).
370
371 Controls behavior when closing a socket with unsent data.
372 When enabled, `close()` blocks until pending data is sent
373 or the timeout expires.
374
375 This variant stores the platform's `struct linger` directly,
376 avoiding the opaque-storage indirection of the type-erased
377 version.
378
379 @par Example
380 @par !example linger
381 */
382 class linger
383 {
384 struct ::linger value_{};
385
386 public:
387 /// Construct with default values (disabled, zero timeout).
388 645x linger() = default;
389
390 /** Construct with explicit values.
391
392 @param enabled `true` to enable linger behavior on close.
393 @param timeout The linger timeout in seconds.
394 */
395 203x linger(bool enabled, int timeout) noexcept
396 203x {
397 203x value_.l_onoff = enabled ? 1 : 0;
398 203x value_.l_linger = static_cast<decltype(value_.l_linger)>(timeout);
399 203x }
400
401 /// Return whether linger is enabled.
402 22x bool enabled() const noexcept
403 {
404 22x return value_.l_onoff != 0;
405 }
406
407 /// Set whether linger is enabled.
408 4x void enabled(bool v) noexcept
409 {
410 4x value_.l_onoff = v ? 1 : 0;
411 4x }
412
413 /// Return the linger timeout in seconds.
414 20x int timeout() const noexcept
415 {
416 20x return static_cast<int>(value_.l_linger);
417 }
418
419 /// Set the linger timeout in seconds.
420 4x void timeout(int v) noexcept
421 {
422 4x value_.l_linger = static_cast<decltype(value_.l_linger)>(v);
423 4x }
424
425 /// Return the protocol level for `setsockopt`/`getsockopt`.
426 217x constexpr int level(family) const noexcept
427 {
428 217x return SOL_SOCKET;
429 }
430
431 /// Return the option name for `setsockopt`/`getsockopt`.
432 217x constexpr int name(family) const noexcept
433 {
434 217x return SO_LINGER;
435 }
436
437 /// Return a pointer to the underlying storage.
438 241x void* data(family) noexcept
439 {
440 241x return &value_;
441 }
442
443 /// Return a pointer to the underlying storage.
444 2x void const* data(family) const noexcept
445 {
446 2x return &value_;
447 }
448
449 /// Return the size of the underlying storage.
450 456x std::size_t size(family) const noexcept
451 {
452 456x return sizeof(value_);
453 }
454
455 /** Normalize after `getsockopt`.
456
457 No-op — `struct linger` is always returned at full size.
458
459 @param s The number of bytes actually written by `getsockopt`.
460 */
461 void resize(family, std::size_t) noexcept {}
462 };
463
464 /// Disable Nagle's algorithm (TCP_NODELAY).
465 using no_delay = boolean<IPPROTO_TCP, TCP_NODELAY>;
466
467 /// Enable periodic keepalive probes (SO_KEEPALIVE).
468 using keep_alive = boolean<SOL_SOCKET, SO_KEEPALIVE>;
469
470 /// Restrict an IPv6 socket to IPv6 only (IPV6_V6ONLY).
471 using v6_only = boolean<IPPROTO_IPV6, IPV6_V6ONLY>;
472
473 /// Allow local address reuse (SO_REUSEADDR).
474 using reuse_address = boolean<SOL_SOCKET, SO_REUSEADDR>;
475
476 /// Allow sending to broadcast addresses (SO_BROADCAST).
477 using broadcast = boolean<SOL_SOCKET, SO_BROADCAST>;
478
479 /// Set the receive buffer size (SO_RCVBUF).
480 using receive_buffer_size = integer<SOL_SOCKET, SO_RCVBUF>;
481
482 /// Set the send buffer size (SO_SNDBUF).
483 using send_buffer_size = integer<SOL_SOCKET, SO_SNDBUF>;
484
485 #ifdef SO_REUSEPORT
486 /// Allow multiple sockets to bind to the same port (SO_REUSEPORT).
487 using reuse_port = boolean<SOL_SOCKET, SO_REUSEPORT>;
488 #endif
489
490 /** Enable loopback of outgoing multicast (IP_MULTICAST_LOOP /
491 IPV6_MULTICAST_LOOP).
492
493 The socket's family selects the wire rendering: a single byte
494 at `IPPROTO_IP` for IPv4 (BSD-derived kernels reject the
495 four-byte form), an `int` at `IPPROTO_IPV6` for IPv6.
496 */
497 class multicast_loop
498 {
499 unsigned char byte_ = 0; // IPv4 rendering
500 int int_ = 0; // IPv6 rendering
501
502 public:
503 /// Construct with default value (disabled).
504 68x multicast_loop() = default;
505
506 /** Construct with an explicit value.
507
508 @param v `true` to enable loopback, `false` to disable.
509 */
510 6x explicit multicast_loop(bool v) noexcept : byte_(v ? 1 : 0), int_(v ? 1 : 0)
511 {
512 6x }
513
514 /// Assign a new value.
515 multicast_loop& operator=(bool v) noexcept
516 {
517 byte_ = v ? 1 : 0;
518 int_ = v ? 1 : 0;
519 return *this;
520 }
521
522 /// Return the option value.
523 2x bool value() const noexcept
524 {
525 2x return byte_ != 0 || int_ != 0;
526 }
527
528 /// Return the protocol level for `setsockopt`/`getsockopt`.
529 40x constexpr int level(family f) const noexcept
530 {
531 40x return f == family::v6 ? IPPROTO_IPV6 : IPPROTO_IP;
532 }
533
534 /// Return the option name for `setsockopt`/`getsockopt`.
535 40x constexpr int name(family f) const noexcept
536 {
537 40x return f == family::v6 ? IPV6_MULTICAST_LOOP : IP_MULTICAST_LOOP;
538 }
539
540 /// Return a pointer to the rendering for `f`.
541 2x void* data(family f) noexcept
542 {
543 2x return f == family::v6 ? static_cast<void*>(&int_)
544 2x : static_cast<void*>(&byte_);
545 }
546
547 /// Return a pointer to the rendering for `f`.
548 4x void const* data(family f) const noexcept
549 {
550 4x return f == family::v6 ? static_cast<void const*>(&int_)
551 4x : static_cast<void const*>(&byte_);
552 }
553
554 /// Return the size of the rendering for `f`.
555 10x std::size_t size(family f) const noexcept
556 {
557 10x return f == family::v6 ? sizeof(int_) : sizeof(byte_);
558 }
559
560 /** Synchronize both renderings after `getsockopt`.
561
562 Only the rendering the socket's family selected was
563 written; fold it into the other so `value()` answers
564 from either.
565
566 @param f The family `getsockopt` was performed for.
567 */
568 2x void resize(family f, std::size_t) noexcept
569 {
570 2x if (f == family::v6)
571 ✗ byte_ = int_ ? 1 : 0;
572 else
573 2x int_ = byte_ ? 1 : 0;
574 2x }
575 };
576
577 /** Set the multicast TTL / hop limit (IP_MULTICAST_TTL /
578 IPV6_MULTICAST_HOPS).
579
580 The socket's family selects the wire rendering: a single byte
581 at `IPPROTO_IP` for IPv4, an `int` at `IPPROTO_IPV6` for IPv6.
582 */
583 class multicast_hops
584 {
585 unsigned char byte_ = 0; // IPv4 rendering
586 int int_ = 0; // IPv6 rendering
587
588 public:
589 /// Construct with default value (zero).
590 32x multicast_hops() = default;
591
592 /** Construct with an explicit value.
593
594 @param v The hop count, 0 to 255 — the range the IPv4 wire
595 rendering can carry.
596
597 @throws std::logic_error if `v` is outside [0, 255].
598 */
599 6x explicit multicast_hops(int v)
600 6x {
601 6x if (v < 0 || v > 255)
602 2x detail::throw_logic_error("multicast hops value out of range");
603 4x byte_ = static_cast<unsigned char>(v);
604 4x int_ = v;
605 4x }
606
607 /** Assign a new value.
608
609 @throws std::logic_error if `v` is outside [0, 255].
610 */
611 multicast_hops& operator=(int v)
612 {
613 if (v < 0 || v > 255)
614 detail::throw_logic_error("multicast hops value out of range");
615 byte_ = static_cast<unsigned char>(v);
616 int_ = v;
617 return *this;
618 }
619
620 /// Return the option value.
621 2x int value() const noexcept
622 {
623 2x return int_;
624 }
625
626 /// Return the protocol level for `setsockopt`/`getsockopt`.
627 22x constexpr int level(family f) const noexcept
628 {
629 22x return f == family::v6 ? IPPROTO_IPV6 : IPPROTO_IP;
630 }
631
632 /// Return the option name for `setsockopt`/`getsockopt`.
633 22x constexpr int name(family f) const noexcept
634 {
635 22x return f == family::v6 ? IPV6_MULTICAST_HOPS : IP_MULTICAST_TTL;
636 }
637
638 /// Return a pointer to the rendering for `f`.
639 2x void* data(family f) noexcept
640 {
641 2x return f == family::v6 ? static_cast<void*>(&int_)
642 2x : static_cast<void*>(&byte_);
643 }
644
645 /// Return a pointer to the rendering for `f`.
646 4x void const* data(family f) const noexcept
647 {
648 4x return f == family::v6 ? static_cast<void const*>(&int_)
649 4x : static_cast<void const*>(&byte_);
650 }
651
652 /// Return the size of the rendering for `f`.
653 6x std::size_t size(family f) const noexcept
654 {
655 6x return f == family::v6 ? sizeof(int_) : sizeof(byte_);
656 }
657
658 /** Synchronize both renderings after `getsockopt`.
659
660 @param f The family `getsockopt` was performed for.
661 */
662 2x void resize(family f, std::size_t) noexcept
663 {
664 2x if (f == family::v6)
665 ✗ byte_ = static_cast<unsigned char>(int_);
666 else
667 2x int_ = byte_;
668 2x }
669 };
670
671 /** A multicast membership request.
672
673 The group's family — not the socket's — selects the wire
674 struct and protocol level: a v4 group renders as an `ip_mreq`
675 at the IPv4 level even when applied to a dual-stack v6 socket,
676 which is the level such a join actually targets.
677
678 @tparam Level4 The IPv4 protocol level.
679 @tparam Name4 The IPv4 option name.
680 @tparam Level6 The IPv6 protocol level.
681 @tparam Name6 The IPv6 option name.
682 */
683 template<int Level4, int Name4, int Level6, int Name6>
684 class membership_request
685 {
686 struct ip_mreq v4_{};
687 struct ipv6_mreq v6_{};
688 family group_family_ = family::v4;
689
690 88x void assign_v4(ipv4_address group, ipv4_address iface) noexcept
691 {
692 88x auto g = group.to_bytes();
693 88x std::memcpy(&v4_.imr_multiaddr, g.data(), 4);
694 88x auto i = iface.to_bytes();
695 88x std::memcpy(&v4_.imr_interface, i.data(), 4);
696 88x group_family_ = family::v4;
697 88x }
698
699 90x void assign_v6(ipv6_address const& group, unsigned int if_index) noexcept
700 {
701 90x auto g = group.to_bytes();
702 90x std::memcpy(&v6_.ipv6mr_multiaddr, g.data(), 16);
703 // The group's zone is the natural default interface
704 90x v6_.ipv6mr_interface = if_index ? if_index : group.scope_id();
705 90x group_family_ = family::v6;
706 90x }
707
708 public:
709 /// Construct with default values.
710 membership_request() = default;
711
712 /** Construct from a group address.
713
714 The group's family selects the wire representation; the
715 interface defaults to any (v4) or the group's zone (v6).
716
717 @param group The multicast group address.
718 */
719 16x explicit membership_request(ip_address const& group) noexcept
720 16x {
721 16x if (group.is_v4())
722 8x assign_v4(group.to_v4(), ipv4_address());
723 else
724 8x assign_v6(group.to_v6(), 0);
725 16x }
726
727 /** Construct from an IPv4 group and interface address.
728
729 @param group The multicast group address.
730 @param iface The local interface to use (default: any).
731 */
732 80x membership_request(
733 ipv4_address group, ipv4_address iface = ipv4_address()) noexcept
734 80x {
735 80x assign_v4(group, iface);
736 80x }
737
738 /** Construct from an IPv6 group and interface index.
739
740 @param group The multicast group address.
741 @param if_index The interface index; 0 uses the group's
742 zone, and a zone of 0 lets the kernel choose.
743 */
744 82x membership_request(
745 ipv6_address const& group, unsigned int if_index = 0) noexcept
746 82x {
747 82x assign_v6(group, if_index);
748 82x }
749
750 /// Return the protocol level for the group's family.
751 78x int level(family) const noexcept
752 {
753 78x return group_family_ == family::v4 ? Level4 : Level6;
754 }
755
756 /// Return the option name for the group's family.
757 66x int name(family) const noexcept
758 {
759 66x return group_family_ == family::v4 ? Name4 : Name6;
760 }
761
762 /// Return a pointer to the wire struct for the group's family.
763 56x void const* data(family) const noexcept
764 {
765 56x return group_family_ == family::v4 ? static_cast<void const*>(&v4_)
766 56x : static_cast<void const*>(&v6_);
767 }
768
769 /// Return the size of the wire struct for the group's family.
770 104x std::size_t size(family) const noexcept
771 {
772 104x return group_family_ == family::v4 ? sizeof(v4_) : sizeof(v6_);
773 }
774
775 /// No-op resize.
776 void resize(family, std::size_t) noexcept {}
777 };
778
779 /// Join a multicast group (IP_ADD_MEMBERSHIP / IPV6_JOIN_GROUP).
780 using join_group = membership_request<
781 IPPROTO_IP,
782 IP_ADD_MEMBERSHIP,
783 IPPROTO_IPV6,
784 IPV6_JOIN_GROUP>;
785
786 /// Leave a multicast group (IP_DROP_MEMBERSHIP / IPV6_LEAVE_GROUP).
787 using leave_group = membership_request<
788 IPPROTO_IP,
789 IP_DROP_MEMBERSHIP,
790 IPPROTO_IPV6,
791 IPV6_LEAVE_GROUP>;
792
793 /** Set the outgoing multicast interface (IP_MULTICAST_IF /
794 IPV6_MULTICAST_IF).
795
796 The two families name interfaces differently on the wire — IPv4
797 by interface address, IPv6 by interface index — so the option
798 stores both renderings and the socket's family selects one; the
799 other stays at its default (any address, kernel-chosen index).
800 */
801 class multicast_interface
802 {
803 struct in_addr v4_{};
804 unsigned int if_index_ = 0;
805
806 public:
807 /// Construct with default values (any address, kernel-chosen index).
808 22x multicast_interface() = default;
809
810 /** Construct with an IPv4 interface address.
811
812 @param iface The local interface address.
813 */
814 8x explicit multicast_interface(ipv4_address iface) noexcept
815 8x {
816 8x auto b = iface.to_bytes();
817 8x std::memcpy(&v4_, b.data(), 4);
818 8x }
819
820 /** Construct with an IPv6 interface index.
821
822 @param if_index The interface index (0 = kernel chooses).
823 */
824 4x explicit multicast_interface(unsigned int if_index) noexcept
825 4x : if_index_(if_index)
826 {
827 4x }
828
829 /// Return the IPv4 rendering as an address.
830 2x ipv4_address address() const noexcept
831 {
832 ipv4_address::bytes_type b;
833 2x std::memcpy(b.data(), &v4_, 4);
834 2x return ipv4_address(b);
835 }
836
837 /// Return the IPv6 rendering as an interface index.
838 4x unsigned int if_index() const noexcept
839 {
840 4x return if_index_;
841 }
842
843 /// Return the protocol level for `setsockopt`/`getsockopt`.
844 14x constexpr int level(family f) const noexcept
845 {
846 14x return f == family::v6 ? IPPROTO_IPV6 : IPPROTO_IP;
847 }
848
849 /// Return the option name for `setsockopt`/`getsockopt`.
850 14x constexpr int name(family f) const noexcept
851 {
852 14x return f == family::v6 ? IPV6_MULTICAST_IF : IP_MULTICAST_IF;
853 }
854
855 /// Return a pointer to the rendering for `f`.
856 10x void* data(family f) noexcept
857 {
858 10x return f == family::v6 ? static_cast<void*>(&if_index_)
859 10x : static_cast<void*>(&v4_);
860 }
861
862 /// Return a pointer to the rendering for `f`.
863 4x void const* data(family f) const noexcept
864 {
865 4x return f == family::v6 ? static_cast<void const*>(&if_index_)
866 4x : static_cast<void const*>(&v4_);
867 }
868
869 /// Return the size of the rendering for `f`.
870 24x std::size_t size(family f) const noexcept
871 {
872 24x return f == family::v6 ? sizeof(if_index_) : sizeof(v4_);
873 }
874
875 /// No-op resize.
876 2x void resize(family, std::size_t) noexcept {}
877 };
878
879 } // namespace boost::corosio::native_socket_option
880
881 #endif // BOOST_COROSIO_NATIVE_NATIVE_SOCKET_OPTION_HPP
882