TLA Line data Source code
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 : // Some older systems define only the legacy names
42 : #ifndef IPV6_JOIN_GROUP
43 : #define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP
44 : #endif
45 : #ifndef IPV6_LEAVE_GROUP
46 : #define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP
47 : #endif
48 :
49 : #include <boost/corosio/ipv4_address.hpp>
50 : #include <boost/corosio/ipv6_address.hpp>
51 :
52 : #include <cstddef>
53 : #include <cstring>
54 :
55 : namespace boost::corosio::native_socket_option {
56 :
57 : /** A socket option with a boolean value.
58 :
59 : Models socket options whose underlying representation is an `int`
60 : where 0 means disabled and non-zero means enabled. The option's
61 : protocol level and name are encoded as template parameters.
62 :
63 : This is the native (inline) variant that includes platform
64 : headers. For a type-erased version that avoids platform
65 : includes, use `boost::corosio::socket_option` instead.
66 :
67 : @par Example
68 : @code
69 : sock.set_option( native_socket_option::no_delay( true ) );
70 : auto nd = sock.get_option<native_socket_option::no_delay>();
71 : if ( nd.value() )
72 : // Nagle's algorithm is disabled
73 : @endcode
74 :
75 : @tparam Level The protocol level (e.g. `SOL_SOCKET`, `IPPROTO_TCP`).
76 : @tparam Name The option name (e.g. `TCP_NODELAY`, `SO_KEEPALIVE`).
77 : */
78 : template<int Level, int Name>
79 : class boolean
80 : {
81 : int value_ = 0;
82 :
83 : public:
84 : /// Construct with default value (disabled).
85 : boolean() = default;
86 :
87 : /** Construct with an explicit value.
88 :
89 : @param v `true` to enable the option, `false` to disable.
90 : */
91 : explicit boolean(bool v) noexcept : value_(v ? 1 : 0) {}
92 :
93 : /// Assign a new value.
94 : boolean& operator=(bool v) noexcept
95 : {
96 : value_ = v ? 1 : 0;
97 : return *this;
98 : }
99 :
100 : /// Return the option value.
101 : bool value() const noexcept
102 : {
103 : return value_ != 0;
104 : }
105 :
106 : /// Return the option value.
107 : explicit operator bool() const noexcept
108 : {
109 : return value_ != 0;
110 : }
111 :
112 : /// Return the negated option value.
113 : bool operator!() const noexcept
114 : {
115 : return value_ == 0;
116 : }
117 :
118 : /// Return the protocol level for `setsockopt`/`getsockopt`.
119 HIT 227 : static constexpr int level() noexcept
120 : {
121 227 : return Level;
122 : }
123 :
124 : /// Return the option name for `setsockopt`/`getsockopt`.
125 227 : static constexpr int name() noexcept
126 : {
127 227 : return Name;
128 : }
129 :
130 : /// Return a pointer to the underlying storage.
131 : void* data() noexcept
132 : {
133 : return &value_;
134 : }
135 :
136 : /// Return a pointer to the underlying storage.
137 : void const* data() const noexcept
138 : {
139 : return &value_;
140 : }
141 :
142 : /// Return the size of the underlying storage.
143 : std::size_t size() const noexcept
144 : {
145 : return sizeof(value_);
146 : }
147 :
148 : /** Normalize after `getsockopt` returns fewer bytes than expected.
149 :
150 : Windows Vista+ may write only 1 byte for boolean options.
151 :
152 : @param s The number of bytes actually written by `getsockopt`.
153 : */
154 : void resize(std::size_t s) noexcept
155 : {
156 : if (s == sizeof(char))
157 : value_ = *reinterpret_cast<unsigned char*>(&value_) ? 1 : 0;
158 : }
159 : };
160 :
161 : /** A socket option with an integer value.
162 :
163 : Models socket options whose underlying representation is a
164 : plain `int`. The option's protocol level and name are encoded
165 : as template parameters.
166 :
167 : This is the native (inline) variant that includes platform
168 : headers. For a type-erased version that avoids platform
169 : includes, use `boost::corosio::socket_option` instead.
170 :
171 : @par Example
172 : @code
173 : sock.set_option( native_socket_option::receive_buffer_size( 65536 ) );
174 : auto opt = sock.get_option<native_socket_option::receive_buffer_size>();
175 : int sz = opt.value();
176 : @endcode
177 :
178 : @tparam Level The protocol level (e.g. `SOL_SOCKET`).
179 : @tparam Name The option name (e.g. `SO_RCVBUF`).
180 : */
181 : template<int Level, int Name>
182 : class integer
183 : {
184 : int value_ = 0;
185 :
186 : public:
187 : /// Construct with default value (zero).
188 : integer() = default;
189 :
190 : /** Construct with an explicit value.
191 :
192 : @param v The option value.
193 : */
194 : explicit integer(int v) noexcept : value_(v) {}
195 :
196 : /// Assign a new value.
197 : integer& operator=(int v) noexcept
198 : {
199 : value_ = v;
200 : return *this;
201 : }
202 :
203 : /// Return the option value.
204 : int value() const noexcept
205 : {
206 : return value_;
207 : }
208 :
209 : /// Return the protocol level for `setsockopt`/`getsockopt`.
210 36 : static constexpr int level() noexcept
211 : {
212 36 : return Level;
213 : }
214 :
215 : /// Return the option name for `setsockopt`/`getsockopt`.
216 36 : static constexpr int name() noexcept
217 : {
218 36 : return Name;
219 : }
220 :
221 : /// Return a pointer to the underlying storage.
222 : void* data() noexcept
223 : {
224 : return &value_;
225 : }
226 :
227 : /// Return a pointer to the underlying storage.
228 : void const* data() const noexcept
229 : {
230 : return &value_;
231 : }
232 :
233 : /// Return the size of the underlying storage.
234 : std::size_t size() const noexcept
235 : {
236 : return sizeof(value_);
237 : }
238 :
239 : /** Normalize after `getsockopt` returns fewer bytes than expected.
240 :
241 : @param s The number of bytes actually written by `getsockopt`.
242 : */
243 : void resize(std::size_t s) noexcept
244 : {
245 : if (s == sizeof(char))
246 : value_ =
247 : static_cast<int>(*reinterpret_cast<unsigned char*>(&value_));
248 : }
249 : };
250 :
251 : /** The SO_LINGER socket option (native variant).
252 :
253 : Controls behavior when closing a socket with unsent data.
254 : When enabled, `close()` blocks until pending data is sent
255 : or the timeout expires.
256 :
257 : This variant stores the platform's `struct linger` directly,
258 : avoiding the opaque-storage indirection of the type-erased
259 : version.
260 :
261 : @par Example
262 : @code
263 : sock.set_option( native_socket_option::linger( true, 5 ) );
264 : auto opt = sock.get_option<native_socket_option::linger>();
265 : if ( opt.enabled() )
266 : std::cout << "linger timeout: " << opt.timeout() << "s\n";
267 : @endcode
268 : */
269 : class linger
270 : {
271 : struct ::linger value_{};
272 :
273 : public:
274 : /// Construct with default values (disabled, zero timeout).
275 28 : linger() = default;
276 :
277 : /** Construct with explicit values.
278 :
279 : @param enabled `true` to enable linger behavior on close.
280 : @param timeout The linger timeout in seconds.
281 : */
282 16 : linger(bool enabled, int timeout) noexcept
283 16 : {
284 16 : value_.l_onoff = enabled ? 1 : 0;
285 16 : value_.l_linger = static_cast<decltype(value_.l_linger)>(timeout);
286 16 : }
287 :
288 : /// Return whether linger is enabled.
289 14 : bool enabled() const noexcept
290 : {
291 14 : return value_.l_onoff != 0;
292 : }
293 :
294 : /// Set whether linger is enabled.
295 2 : void enabled(bool v) noexcept
296 : {
297 2 : value_.l_onoff = v ? 1 : 0;
298 2 : }
299 :
300 : /// Return the linger timeout in seconds.
301 12 : int timeout() const noexcept
302 : {
303 12 : return static_cast<int>(value_.l_linger);
304 : }
305 :
306 : /// Set the linger timeout in seconds.
307 2 : void timeout(int v) noexcept
308 : {
309 2 : value_.l_linger = static_cast<decltype(value_.l_linger)>(v);
310 2 : }
311 :
312 : /// Return the protocol level for `setsockopt`/`getsockopt`.
313 28 : static constexpr int level() noexcept
314 : {
315 28 : return SOL_SOCKET;
316 : }
317 :
318 : /// Return the option name for `setsockopt`/`getsockopt`.
319 28 : static constexpr int name() noexcept
320 : {
321 28 : return SO_LINGER;
322 : }
323 :
324 : /// Return a pointer to the underlying storage.
325 50 : void* data() noexcept
326 : {
327 50 : return &value_;
328 : }
329 :
330 : /// Return a pointer to the underlying storage.
331 : void const* data() const noexcept
332 : {
333 : return &value_;
334 : }
335 :
336 : /// Return the size of the underlying storage.
337 78 : std::size_t size() const noexcept
338 : {
339 78 : return sizeof(value_);
340 : }
341 :
342 : /** Normalize after `getsockopt`.
343 :
344 : No-op — `struct linger` is always returned at full size.
345 :
346 : @param s The number of bytes actually written by `getsockopt`.
347 : */
348 : void resize(std::size_t) noexcept {}
349 : };
350 :
351 : /// Disable Nagle's algorithm (TCP_NODELAY).
352 : using no_delay = boolean<IPPROTO_TCP, TCP_NODELAY>;
353 :
354 : /// Enable periodic keepalive probes (SO_KEEPALIVE).
355 : using keep_alive = boolean<SOL_SOCKET, SO_KEEPALIVE>;
356 :
357 : /// Restrict an IPv6 socket to IPv6 only (IPV6_V6ONLY).
358 : using v6_only = boolean<IPPROTO_IPV6, IPV6_V6ONLY>;
359 :
360 : /// Allow local address reuse (SO_REUSEADDR).
361 : using reuse_address = boolean<SOL_SOCKET, SO_REUSEADDR>;
362 :
363 : /// Allow sending to broadcast addresses (SO_BROADCAST).
364 : using broadcast = boolean<SOL_SOCKET, SO_BROADCAST>;
365 :
366 : /// Set the receive buffer size (SO_RCVBUF).
367 : using receive_buffer_size = integer<SOL_SOCKET, SO_RCVBUF>;
368 :
369 : /// Set the send buffer size (SO_SNDBUF).
370 : using send_buffer_size = integer<SOL_SOCKET, SO_SNDBUF>;
371 :
372 : #ifdef SO_REUSEPORT
373 : /// Allow multiple sockets to bind to the same port (SO_REUSEPORT).
374 : using reuse_port = boolean<SOL_SOCKET, SO_REUSEPORT>;
375 : #endif
376 :
377 : /// Enable loopback of outgoing multicast on IPv4 (IP_MULTICAST_LOOP).
378 : using multicast_loop_v4 = boolean<IPPROTO_IP, IP_MULTICAST_LOOP>;
379 :
380 : /// Enable loopback of outgoing multicast on IPv6 (IPV6_MULTICAST_LOOP).
381 : using multicast_loop_v6 = boolean<IPPROTO_IPV6, IPV6_MULTICAST_LOOP>;
382 :
383 : /// Set the multicast TTL for IPv4 (IP_MULTICAST_TTL).
384 : using multicast_hops_v4 = integer<IPPROTO_IP, IP_MULTICAST_TTL>;
385 :
386 : /// Set the multicast hop limit for IPv6 (IPV6_MULTICAST_HOPS).
387 : using multicast_hops_v6 = integer<IPPROTO_IPV6, IPV6_MULTICAST_HOPS>;
388 :
389 : /// Set the outgoing interface for IPv6 multicast (IPV6_MULTICAST_IF).
390 : using multicast_interface_v6 = integer<IPPROTO_IPV6, IPV6_MULTICAST_IF>;
391 :
392 : /** Join an IPv4 multicast group (IP_ADD_MEMBERSHIP).
393 :
394 : @par Example
395 : @code
396 : sock.set_option( native_socket_option::join_group_v4(
397 : ipv4_address( "239.255.0.1" ) ) );
398 : @endcode
399 : */
400 : class join_group_v4
401 : {
402 : struct ip_mreq value_{};
403 :
404 : public:
405 : /// Construct with default values.
406 2 : join_group_v4() = default;
407 :
408 : /** Construct with a group and optional interface address.
409 :
410 : @param group The multicast group address to join.
411 : @param iface The local interface to use (default: any).
412 : */
413 2 : join_group_v4(
414 : ipv4_address group, ipv4_address iface = ipv4_address()) noexcept
415 2 : {
416 2 : auto gb = group.to_bytes();
417 2 : auto ib = iface.to_bytes();
418 2 : std::memcpy(&value_.imr_multiaddr, gb.data(), 4);
419 2 : std::memcpy(&value_.imr_interface, ib.data(), 4);
420 2 : }
421 :
422 : /// Return the protocol level for `setsockopt`/`getsockopt`.
423 2 : static constexpr int level() noexcept
424 : {
425 2 : return IPPROTO_IP;
426 : }
427 :
428 : /// Return the option name for `setsockopt`/`getsockopt`.
429 2 : static constexpr int name() noexcept
430 : {
431 2 : return IP_ADD_MEMBERSHIP;
432 : }
433 :
434 : /// Return a pointer to the underlying storage.
435 2 : void* data() noexcept
436 : {
437 2 : return &value_;
438 : }
439 :
440 : /// Return a pointer to the underlying storage.
441 : void const* data() const noexcept
442 : {
443 : return &value_;
444 : }
445 :
446 : /// Return the size of the underlying storage.
447 4 : std::size_t size() const noexcept
448 : {
449 4 : return sizeof(value_);
450 : }
451 :
452 : /// No-op resize.
453 : void resize(std::size_t) noexcept {}
454 : };
455 :
456 : /** Leave an IPv4 multicast group (IP_DROP_MEMBERSHIP).
457 :
458 : @par Example
459 : @code
460 : sock.set_option( native_socket_option::leave_group_v4(
461 : ipv4_address( "239.255.0.1" ) ) );
462 : @endcode
463 : */
464 : class leave_group_v4
465 : {
466 : struct ip_mreq value_{};
467 :
468 : public:
469 : /// Construct with default values.
470 MIS 0 : leave_group_v4() = default;
471 :
472 : /** Construct with a group and optional interface address.
473 :
474 : @param group The multicast group address to leave.
475 : @param iface The local interface (default: any).
476 : */
477 0 : leave_group_v4(
478 : ipv4_address group, ipv4_address iface = ipv4_address()) noexcept
479 0 : {
480 0 : auto gb = group.to_bytes();
481 0 : auto ib = iface.to_bytes();
482 0 : std::memcpy(&value_.imr_multiaddr, gb.data(), 4);
483 0 : std::memcpy(&value_.imr_interface, ib.data(), 4);
484 0 : }
485 :
486 : /// Return the protocol level for `setsockopt`/`getsockopt`.
487 0 : static constexpr int level() noexcept
488 : {
489 0 : return IPPROTO_IP;
490 : }
491 :
492 : /// Return the option name for `setsockopt`/`getsockopt`.
493 0 : static constexpr int name() noexcept
494 : {
495 0 : return IP_DROP_MEMBERSHIP;
496 : }
497 :
498 : /// Return a pointer to the underlying storage.
499 0 : void* data() noexcept
500 : {
501 0 : return &value_;
502 : }
503 :
504 : /// Return a pointer to the underlying storage.
505 : void const* data() const noexcept
506 : {
507 : return &value_;
508 : }
509 :
510 : /// Return the size of the underlying storage.
511 0 : std::size_t size() const noexcept
512 : {
513 0 : return sizeof(value_);
514 : }
515 :
516 : /// No-op resize.
517 : void resize(std::size_t) noexcept {}
518 : };
519 :
520 : /** Join an IPv6 multicast group (IPV6_JOIN_GROUP).
521 :
522 : @par Example
523 : @code
524 : sock.set_option( native_socket_option::join_group_v6(
525 : ipv6_address( "ff02::1" ), 0 ) );
526 : @endcode
527 : */
528 : class join_group_v6
529 : {
530 : struct ipv6_mreq value_{};
531 :
532 : public:
533 : /// Construct with default values.
534 0 : join_group_v6() = default;
535 :
536 : /** Construct with a group and optional interface index.
537 :
538 : @param group The multicast group address to join.
539 : @param if_index The interface index (0 = kernel chooses).
540 : */
541 0 : join_group_v6(ipv6_address group, unsigned int if_index = 0) noexcept
542 0 : {
543 0 : auto gb = group.to_bytes();
544 0 : std::memcpy(&value_.ipv6mr_multiaddr, gb.data(), 16);
545 0 : value_.ipv6mr_interface = if_index;
546 0 : }
547 :
548 : /// Return the protocol level for `setsockopt`/`getsockopt`.
549 0 : static constexpr int level() noexcept
550 : {
551 0 : return IPPROTO_IPV6;
552 : }
553 :
554 : /// Return the option name for `setsockopt`/`getsockopt`.
555 0 : static constexpr int name() noexcept
556 : {
557 0 : return IPV6_JOIN_GROUP;
558 : }
559 :
560 : /// Return a pointer to the underlying storage.
561 0 : void* data() noexcept
562 : {
563 0 : return &value_;
564 : }
565 :
566 : /// Return a pointer to the underlying storage.
567 : void const* data() const noexcept
568 : {
569 : return &value_;
570 : }
571 :
572 : /// Return the size of the underlying storage.
573 0 : std::size_t size() const noexcept
574 : {
575 0 : return sizeof(value_);
576 : }
577 :
578 : /// No-op resize.
579 : void resize(std::size_t) noexcept {}
580 : };
581 :
582 : /** Leave an IPv6 multicast group (IPV6_LEAVE_GROUP).
583 :
584 : @par Example
585 : @code
586 : sock.set_option( native_socket_option::leave_group_v6(
587 : ipv6_address( "ff02::1" ), 0 ) );
588 : @endcode
589 : */
590 : class leave_group_v6
591 : {
592 : struct ipv6_mreq value_{};
593 :
594 : public:
595 : /// Construct with default values.
596 0 : leave_group_v6() = default;
597 :
598 : /** Construct with a group and optional interface index.
599 :
600 : @param group The multicast group address to leave.
601 : @param if_index The interface index (0 = kernel chooses).
602 : */
603 0 : leave_group_v6(ipv6_address group, unsigned int if_index = 0) noexcept
604 0 : {
605 0 : auto gb = group.to_bytes();
606 0 : std::memcpy(&value_.ipv6mr_multiaddr, gb.data(), 16);
607 0 : value_.ipv6mr_interface = if_index;
608 0 : }
609 :
610 : /// Return the protocol level for `setsockopt`/`getsockopt`.
611 0 : static constexpr int level() noexcept
612 : {
613 0 : return IPPROTO_IPV6;
614 : }
615 :
616 : /// Return the option name for `setsockopt`/`getsockopt`.
617 0 : static constexpr int name() noexcept
618 : {
619 0 : return IPV6_LEAVE_GROUP;
620 : }
621 :
622 : /// Return a pointer to the underlying storage.
623 0 : void* data() noexcept
624 : {
625 0 : return &value_;
626 : }
627 :
628 : /// Return a pointer to the underlying storage.
629 : void const* data() const noexcept
630 : {
631 : return &value_;
632 : }
633 :
634 : /// Return the size of the underlying storage.
635 0 : std::size_t size() const noexcept
636 : {
637 0 : return sizeof(value_);
638 : }
639 :
640 : /// No-op resize.
641 : void resize(std::size_t) noexcept {}
642 : };
643 :
644 : /** Set the outgoing interface for IPv4 multicast (IP_MULTICAST_IF).
645 :
646 : Unlike the integer-based `multicast_interface_v6`, this option
647 : takes an `ipv4_address` identifying the local interface.
648 :
649 : @par Example
650 : @code
651 : sock.set_option( native_socket_option::multicast_interface_v4(
652 : ipv4_address( "192.168.1.1" ) ) );
653 : @endcode
654 : */
655 : class multicast_interface_v4
656 : {
657 : struct in_addr value_{};
658 :
659 : public:
660 : /// Construct with default values (INADDR_ANY).
661 0 : multicast_interface_v4() = default;
662 :
663 : /** Construct with an interface address.
664 :
665 : @param iface The local interface address.
666 : */
667 0 : explicit multicast_interface_v4(ipv4_address iface) noexcept
668 0 : {
669 0 : auto b = iface.to_bytes();
670 0 : std::memcpy(&value_, b.data(), 4);
671 0 : }
672 :
673 : /// Return the protocol level for `setsockopt`/`getsockopt`.
674 0 : static constexpr int level() noexcept
675 : {
676 0 : return IPPROTO_IP;
677 : }
678 :
679 : /// Return the option name for `setsockopt`/`getsockopt`.
680 0 : static constexpr int name() noexcept
681 : {
682 0 : return IP_MULTICAST_IF;
683 : }
684 :
685 : /// Return a pointer to the underlying storage.
686 0 : void* data() noexcept
687 : {
688 0 : return &value_;
689 : }
690 :
691 : /// Return a pointer to the underlying storage.
692 : void const* data() const noexcept
693 : {
694 : return &value_;
695 : }
696 :
697 : /// Return the size of the underlying storage.
698 0 : std::size_t size() const noexcept
699 : {
700 0 : return sizeof(value_);
701 : }
702 :
703 : /// No-op resize.
704 : void resize(std::size_t) noexcept {}
705 : };
706 :
707 : } // namespace boost::corosio::native_socket_option
708 :
709 : #endif // BOOST_COROSIO_NATIVE_NATIVE_SOCKET_OPTION_HPP
|