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