TLA Line data 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 HIT 1494 : boolean() = default;
85 :
86 : /** Construct with an explicit value.
87 :
88 : @param v `true` to enable the option, `false` to disable.
89 : */
90 23 : 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 11 : bool value() const noexcept
101 : {
102 11 : 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 777 : constexpr int level(family) const noexcept
119 : {
120 777 : return Level;
121 : }
122 :
123 : /// Return the option name for `setsockopt`/`getsockopt`.
124 777 : constexpr int name(family) const noexcept
125 : {
126 777 : return Name;
127 : }
128 :
129 : /// Return a pointer to the underlying storage.
130 10 : void* data(family) noexcept
131 : {
132 10 : return &value_;
133 : }
134 :
135 : /// Return a pointer to the underlying storage.
136 22 : void const* data(family) const noexcept
137 : {
138 22 : return &value_;
139 : }
140 :
141 : /// Return the size of the underlying storage.
142 30 : std::size_t size(family) const noexcept
143 : {
144 30 : 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 9 : void resize(family, std::size_t s) noexcept
154 : {
155 9 : if (s == sizeof(char))
156 1 : value_ = *reinterpret_cast<unsigned char*>(&value_) ? 1 : 0;
157 9 : }
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 262 : integer() = default;
184 :
185 : /** Construct with an explicit value.
186 :
187 : @param v The option value.
188 : */
189 5 : 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 5 : int value() const noexcept
200 : {
201 5 : return value_;
202 : }
203 :
204 : /// Return the protocol level for `setsockopt`/`getsockopt`.
205 139 : constexpr int level(family) const noexcept
206 : {
207 139 : return Level;
208 : }
209 :
210 : /// Return the option name for `setsockopt`/`getsockopt`.
211 139 : constexpr int name(family) const noexcept
212 : {
213 139 : return Name;
214 : }
215 :
216 : /// Return a pointer to the underlying storage.
217 4 : void* data(family) noexcept
218 : {
219 4 : return &value_;
220 : }
221 :
222 : /// Return a pointer to the underlying storage.
223 4 : void const* data(family) const noexcept
224 : {
225 4 : return &value_;
226 : }
227 :
228 : /// Return the size of the underlying storage.
229 8 : std::size_t size(family) const noexcept
230 : {
231 8 : 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 5 : void resize(family, std::size_t s) noexcept
239 : {
240 5 : if (s == sizeof(char))
241 1 : value_ =
242 1 : static_cast<int>(*reinterpret_cast<unsigned char*>(&value_));
243 5 : }
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 645 : 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 203 : linger(bool enabled, int timeout) noexcept
396 203 : {
397 203 : value_.l_onoff = enabled ? 1 : 0;
398 203 : value_.l_linger = static_cast<decltype(value_.l_linger)>(timeout);
399 203 : }
400 :
401 : /// Return whether linger is enabled.
402 22 : bool enabled() const noexcept
403 : {
404 22 : return value_.l_onoff != 0;
405 : }
406 :
407 : /// Set whether linger is enabled.
408 4 : void enabled(bool v) noexcept
409 : {
410 4 : value_.l_onoff = v ? 1 : 0;
411 4 : }
412 :
413 : /// Return the linger timeout in seconds.
414 20 : int timeout() const noexcept
415 : {
416 20 : return static_cast<int>(value_.l_linger);
417 : }
418 :
419 : /// Set the linger timeout in seconds.
420 4 : void timeout(int v) noexcept
421 : {
422 4 : value_.l_linger = static_cast<decltype(value_.l_linger)>(v);
423 4 : }
424 :
425 : /// Return the protocol level for `setsockopt`/`getsockopt`.
426 217 : constexpr int level(family) const noexcept
427 : {
428 217 : return SOL_SOCKET;
429 : }
430 :
431 : /// Return the option name for `setsockopt`/`getsockopt`.
432 217 : constexpr int name(family) const noexcept
433 : {
434 217 : return SO_LINGER;
435 : }
436 :
437 : /// Return a pointer to the underlying storage.
438 241 : void* data(family) noexcept
439 : {
440 241 : return &value_;
441 : }
442 :
443 : /// Return a pointer to the underlying storage.
444 2 : void const* data(family) const noexcept
445 : {
446 2 : return &value_;
447 : }
448 :
449 : /// Return the size of the underlying storage.
450 456 : std::size_t size(family) const noexcept
451 : {
452 456 : 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 68 : multicast_loop() = default;
505 :
506 : /** Construct with an explicit value.
507 :
508 : @param v `true` to enable loopback, `false` to disable.
509 : */
510 6 : explicit multicast_loop(bool v) noexcept : byte_(v ? 1 : 0), int_(v ? 1 : 0)
511 : {
512 6 : }
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 2 : bool value() const noexcept
524 : {
525 2 : return byte_ != 0 || int_ != 0;
526 : }
527 :
528 : /// Return the protocol level for `setsockopt`/`getsockopt`.
529 40 : constexpr int level(family f) const noexcept
530 : {
531 40 : return f == family::v6 ? IPPROTO_IPV6 : IPPROTO_IP;
532 : }
533 :
534 : /// Return the option name for `setsockopt`/`getsockopt`.
535 40 : constexpr int name(family f) const noexcept
536 : {
537 40 : return f == family::v6 ? IPV6_MULTICAST_LOOP : IP_MULTICAST_LOOP;
538 : }
539 :
540 : /// Return a pointer to the rendering for `f`.
541 2 : void* data(family f) noexcept
542 : {
543 2 : return f == family::v6 ? static_cast<void*>(&int_)
544 2 : : static_cast<void*>(&byte_);
545 : }
546 :
547 : /// Return a pointer to the rendering for `f`.
548 4 : void const* data(family f) const noexcept
549 : {
550 4 : return f == family::v6 ? static_cast<void const*>(&int_)
551 4 : : static_cast<void const*>(&byte_);
552 : }
553 :
554 : /// Return the size of the rendering for `f`.
555 10 : std::size_t size(family f) const noexcept
556 : {
557 10 : 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 2 : void resize(family f, std::size_t) noexcept
569 : {
570 2 : if (f == family::v6)
571 MIS 0 : byte_ = int_ ? 1 : 0;
572 : else
573 HIT 2 : int_ = byte_ ? 1 : 0;
574 2 : }
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 32 : 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 6 : explicit multicast_hops(int v)
600 6 : {
601 6 : if (v < 0 || v > 255)
602 2 : detail::throw_logic_error("multicast hops value out of range");
603 4 : byte_ = static_cast<unsigned char>(v);
604 4 : int_ = v;
605 4 : }
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 2 : int value() const noexcept
622 : {
623 2 : return int_;
624 : }
625 :
626 : /// Return the protocol level for `setsockopt`/`getsockopt`.
627 22 : constexpr int level(family f) const noexcept
628 : {
629 22 : return f == family::v6 ? IPPROTO_IPV6 : IPPROTO_IP;
630 : }
631 :
632 : /// Return the option name for `setsockopt`/`getsockopt`.
633 22 : constexpr int name(family f) const noexcept
634 : {
635 22 : return f == family::v6 ? IPV6_MULTICAST_HOPS : IP_MULTICAST_TTL;
636 : }
637 :
638 : /// Return a pointer to the rendering for `f`.
639 2 : void* data(family f) noexcept
640 : {
641 2 : return f == family::v6 ? static_cast<void*>(&int_)
642 2 : : static_cast<void*>(&byte_);
643 : }
644 :
645 : /// Return a pointer to the rendering for `f`.
646 4 : void const* data(family f) const noexcept
647 : {
648 4 : return f == family::v6 ? static_cast<void const*>(&int_)
649 4 : : static_cast<void const*>(&byte_);
650 : }
651 :
652 : /// Return the size of the rendering for `f`.
653 6 : std::size_t size(family f) const noexcept
654 : {
655 6 : 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 2 : void resize(family f, std::size_t) noexcept
663 : {
664 2 : if (f == family::v6)
665 MIS 0 : byte_ = static_cast<unsigned char>(int_);
666 : else
667 HIT 2 : int_ = byte_;
668 2 : }
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 88 : void assign_v4(ipv4_address group, ipv4_address iface) noexcept
691 : {
692 88 : auto g = group.to_bytes();
693 88 : std::memcpy(&v4_.imr_multiaddr, g.data(), 4);
694 88 : auto i = iface.to_bytes();
695 88 : std::memcpy(&v4_.imr_interface, i.data(), 4);
696 88 : group_family_ = family::v4;
697 88 : }
698 :
699 90 : void assign_v6(ipv6_address const& group, unsigned int if_index) noexcept
700 : {
701 90 : auto g = group.to_bytes();
702 90 : std::memcpy(&v6_.ipv6mr_multiaddr, g.data(), 16);
703 : // The group's zone is the natural default interface
704 90 : v6_.ipv6mr_interface = if_index ? if_index : group.scope_id();
705 90 : group_family_ = family::v6;
706 90 : }
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 16 : explicit membership_request(ip_address const& group) noexcept
720 16 : {
721 16 : if (group.is_v4())
722 8 : assign_v4(group.to_v4(), ipv4_address());
723 : else
724 8 : assign_v6(group.to_v6(), 0);
725 16 : }
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 80 : membership_request(
733 : ipv4_address group, ipv4_address iface = ipv4_address()) noexcept
734 80 : {
735 80 : assign_v4(group, iface);
736 80 : }
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 82 : membership_request(
745 : ipv6_address const& group, unsigned int if_index = 0) noexcept
746 82 : {
747 82 : assign_v6(group, if_index);
748 82 : }
749 :
750 : /// Return the protocol level for the group's family.
751 78 : int level(family) const noexcept
752 : {
753 78 : return group_family_ == family::v4 ? Level4 : Level6;
754 : }
755 :
756 : /// Return the option name for the group's family.
757 66 : int name(family) const noexcept
758 : {
759 66 : return group_family_ == family::v4 ? Name4 : Name6;
760 : }
761 :
762 : /// Return a pointer to the wire struct for the group's family.
763 56 : void const* data(family) const noexcept
764 : {
765 56 : return group_family_ == family::v4 ? static_cast<void const*>(&v4_)
766 56 : : static_cast<void const*>(&v6_);
767 : }
768 :
769 : /// Return the size of the wire struct for the group's family.
770 104 : std::size_t size(family) const noexcept
771 : {
772 104 : 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 22 : multicast_interface() = default;
809 :
810 : /** Construct with an IPv4 interface address.
811 :
812 : @param iface The local interface address.
813 : */
814 8 : explicit multicast_interface(ipv4_address iface) noexcept
815 8 : {
816 8 : auto b = iface.to_bytes();
817 8 : std::memcpy(&v4_, b.data(), 4);
818 8 : }
819 :
820 : /** Construct with an IPv6 interface index.
821 :
822 : @param if_index The interface index (0 = kernel chooses).
823 : */
824 4 : explicit multicast_interface(unsigned int if_index) noexcept
825 4 : : if_index_(if_index)
826 : {
827 4 : }
828 :
829 : /// Return the IPv4 rendering as an address.
830 2 : ipv4_address address() const noexcept
831 : {
832 : ipv4_address::bytes_type b;
833 2 : std::memcpy(b.data(), &v4_, 4);
834 2 : return ipv4_address(b);
835 : }
836 :
837 : /// Return the IPv6 rendering as an interface index.
838 4 : unsigned int if_index() const noexcept
839 : {
840 4 : return if_index_;
841 : }
842 :
843 : /// Return the protocol level for `setsockopt`/`getsockopt`.
844 14 : constexpr int level(family f) const noexcept
845 : {
846 14 : return f == family::v6 ? IPPROTO_IPV6 : IPPROTO_IP;
847 : }
848 :
849 : /// Return the option name for `setsockopt`/`getsockopt`.
850 14 : constexpr int name(family f) const noexcept
851 : {
852 14 : return f == family::v6 ? IPV6_MULTICAST_IF : IP_MULTICAST_IF;
853 : }
854 :
855 : /// Return a pointer to the rendering for `f`.
856 10 : void* data(family f) noexcept
857 : {
858 10 : return f == family::v6 ? static_cast<void*>(&if_index_)
859 10 : : static_cast<void*>(&v4_);
860 : }
861 :
862 : /// Return a pointer to the rendering for `f`.
863 4 : void const* data(family f) const noexcept
864 : {
865 4 : return f == family::v6 ? static_cast<void const*>(&if_index_)
866 4 : : static_cast<void const*>(&v4_);
867 : }
868 :
869 : /// Return the size of the rendering for `f`.
870 24 : std::size_t size(family f) const noexcept
871 : {
872 24 : return f == family::v6 ? sizeof(if_index_) : sizeof(v4_);
873 : }
874 :
875 : /// No-op resize.
876 2 : 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
|