socket2_plus/
lib.rs

1// Copyright 2015 The Rust Project Developers.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9#![cfg_attr(
10    not(any(
11        target_os = "freebsd",
12        target_os = "fuchsia",
13        target_os = "hurd",
14        target_os = "redox",
15        target_os = "vita",
16    )),
17    doc = r#"
18This library is a superset of [`socket2`](https://crates.io/crates/socket2) crate and it aims
19to provide some features with safe APIs currently missing from `socket2`. As the result, this
20library can be used as a drop-in replacement of `socket2`.
21
22The following are the additional features or APIs:
23
24- [`socket::Socket::recv_from_initialized`]
25
26- [`socket::Socket::recvmsg_initialized`]
27
28- [`socket::Socket::set_pktinfo_v4`]
29
30- [`socket::Socket::set_recv_pktinfo_v6`]
31
32- [`MsgHdrInit`]: support `recvmsg` message header operations.
33
34- [`CMsgHdr`]: support Control Messages used in `recvmsg_initialized`.
35
36"#
37)]
38
39//! ## The following summary is inherited from `socket2`.
40//!
41//! Utilities for creating and using sockets.
42//!
43//! The goal of this crate is to create and use a socket using advanced
44//! configuration options (those that are not available in the types in the
45//! standard library) without using any unsafe code.
46//!
47//! This crate provides as direct as possible access to the system's
48//! functionality for sockets, this means little effort to provide
49//! cross-platform utilities. It is up to the user to know how to use sockets
50//! when using this crate. *If you don't know how to create a socket using
51//! libc/system calls then this crate is not for you*. Most, if not all,
52//! functions directly relate to the equivalent system call with no error
53//! handling applied, so no handling errors such as [`EINTR`]. As a result using
54//! this crate can be a little wordy, but it should give you maximal flexibility
55//! over configuration of sockets.
56//!
57//! [`EINTR`]: std::io::ErrorKind::Interrupted
58//!
59//! # Examples
60//!
61//! ```no_run
62//! # fn main() -> std::io::Result<()> {
63//! use std::net::{SocketAddr, TcpListener};
64//! use socket2_plus::{Socket, Domain, Type};
65//!
66//! // Create a TCP listener bound to two addresses.
67//! let socket = Socket::new(Domain::IPV6, Type::STREAM, None)?;
68//!
69//! socket.set_only_v6(false)?;
70//! let address: SocketAddr = "[::1]:12345".parse().unwrap();
71//! socket.bind(&address.into())?;
72//! socket.listen(128)?;
73//!
74//! let listener: TcpListener = socket.into();
75//! // ...
76//! # drop(listener);
77//! # Ok(()) }
78//! ```
79//!
80//! ## Features
81//!
82//! This crate has a single feature `all`, which enables all functions even ones
83//! that are not available on all OSs.
84
85#![deny(missing_docs, missing_debug_implementations, rust_2018_idioms)]
86// Show required OS/features on docs.rs.
87#![cfg_attr(docsrs, feature(doc_cfg))]
88// Disallow warnings when running tests.
89#![cfg_attr(test, deny(warnings))]
90// Disallow warnings in examples.
91#![doc(test(attr(deny(warnings))))]
92
93use std::fmt;
94#[cfg(not(target_os = "redox"))]
95use std::io::IoSlice;
96#[cfg(not(any(
97    target_os = "freebsd",
98    target_os = "fuchsia",
99    target_os = "hurd",
100    target_os = "redox",
101    target_os = "vita",
102)))]
103use std::io::IoSliceMut;
104#[cfg(not(target_os = "redox"))]
105use std::marker::PhantomData;
106#[cfg(not(target_os = "redox"))]
107use std::mem;
108use std::mem::MaybeUninit;
109use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
110use std::ops::{Deref, DerefMut};
111#[cfg(not(any(
112    target_os = "freebsd",
113    target_os = "fuchsia",
114    target_os = "hurd",
115    target_os = "redox",
116    target_os = "vita",
117)))]
118use std::ptr;
119use std::time::Duration;
120
121/// Macro to implement `fmt::Debug` for a type, printing the constant names
122/// rather than a number.
123///
124/// Note this is used in the `sys` module and thus must be defined before
125/// defining the modules.
126macro_rules! impl_debug {
127    (
128        // Type name for which to implement `fmt::Debug`.
129        $type: path,
130        $(
131            $(#[$target: meta])*
132            // The flag(s) to check.
133            // Need to specific the libc crate because Windows doesn't use
134            // `libc` but `windows_sys`.
135            $libc: ident :: $flag: ident
136        ),+ $(,)*
137    ) => {
138        impl std::fmt::Debug for $type {
139            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140                let string = match self.0 {
141                    $(
142                        $(#[$target])*
143                        $libc :: $flag => stringify!($flag),
144                    )+
145                    n => return write!(f, "{n}"),
146                };
147                f.write_str(string)
148            }
149        }
150    };
151}
152
153/// Macro to convert from one network type to another.
154macro_rules! from {
155    ($from: ty, $for: ty) => {
156        impl From<$from> for $for {
157            fn from(socket: $from) -> $for {
158                #[cfg(unix)]
159                unsafe {
160                    <$for>::from_raw_fd(socket.into_raw_fd())
161                }
162                #[cfg(windows)]
163                unsafe {
164                    <$for>::from_raw_socket(socket.into_raw_socket())
165                }
166            }
167        }
168    };
169}
170
171/// Link to online documentation for (almost) all supported OSs.
172#[rustfmt::skip]
173macro_rules! man_links {
174    // Links to all OSs.
175    ($syscall: tt ( $section: tt ) ) => {
176        concat!(
177            man_links!(__ intro),
178            man_links!(__ unix $syscall($section)),
179            man_links!(__ windows $syscall($section)),
180        )
181    };
182    // Links to Unix-like OSs.
183    (unix: $syscall: tt ( $section: tt ) ) => {
184        concat!(
185            man_links!(__ intro),
186            man_links!(__ unix $syscall($section)),
187        )
188    };
189    // Links to Windows only.
190    (windows: $syscall: tt ( $section: tt ) ) => {
191        concat!(
192            man_links!(__ intro),
193            man_links!(__ windows $syscall($section)),
194        )
195    };
196    // Internals.
197    (__ intro) => {
198        "\n\nAdditional documentation can be found in manual of the OS:\n\n"
199    };
200    // List for Unix-like OSs.
201    (__ unix $syscall: tt ( $section: tt ) ) => {
202        concat!(
203            " * DragonFly BSD: <https://man.dragonflybsd.org/?command=", stringify!($syscall), "&section=", stringify!($section), ">\n",
204            " * FreeBSD: <https://www.freebsd.org/cgi/man.cgi?query=", stringify!($syscall), "&sektion=", stringify!($section), ">\n",
205            " * Linux: <https://man7.org/linux/man-pages/man", stringify!($section), "/", stringify!($syscall), ".", stringify!($section), ".html>\n",
206            " * macOS: <https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/", stringify!($syscall), ".", stringify!($section), ".html> (archived, actually for iOS)\n",
207            " * NetBSD: <https://man.netbsd.org/", stringify!($syscall), ".", stringify!($section), ">\n",
208            " * OpenBSD: <https://man.openbsd.org/", stringify!($syscall), ".", stringify!($section), ">\n",
209            " * iOS: <https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/", stringify!($syscall), ".", stringify!($section), ".html> (archived)\n",
210            " * illumos: <https://illumos.org/man/3SOCKET/", stringify!($syscall), ">\n",
211        )
212    };
213    // List for Window (so just Windows).
214    (__ windows $syscall: tt ( $section: tt ) ) => {
215        concat!(
216            " * Windows: <https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-", stringify!($syscall), ">\n",
217        )
218    };
219}
220
221mod sockaddr;
222mod socket;
223mod sockref;
224
225#[cfg_attr(unix, path = "sys/unix.rs")]
226#[cfg_attr(windows, path = "sys/windows.rs")]
227mod sys;
228
229#[cfg(not(any(windows, unix)))]
230compile_error!("Socket2 doesn't support the compile target");
231
232use sys::c_int;
233
234pub use sockaddr::SockAddr;
235pub use socket::Socket;
236pub use sockref::SockRef;
237
238#[cfg(not(any(
239    target_os = "haiku",
240    target_os = "illumos",
241    target_os = "netbsd",
242    target_os = "redox",
243    target_os = "solaris",
244)))]
245pub use socket::InterfaceIndexOrAddress;
246
247/// Specification of the communication domain for a socket.
248///
249/// This is a newtype wrapper around an integer which provides a nicer API in
250/// addition to an injection point for documentation. Convenience constants such
251/// as [`Domain::IPV4`], [`Domain::IPV6`], etc, are provided to avoid reaching
252/// into libc for various constants.
253///
254/// This type is freely interconvertible with C's `int` type, however, if a raw
255/// value needs to be provided.
256#[derive(Copy, Clone, Eq, PartialEq)]
257pub struct Domain(c_int);
258
259impl Domain {
260    /// Domain for IPv4 communication, corresponding to `AF_INET`.
261    pub const IPV4: Domain = Domain(sys::AF_INET);
262
263    /// Domain for IPv6 communication, corresponding to `AF_INET6`.
264    pub const IPV6: Domain = Domain(sys::AF_INET6);
265
266    /// Domain for Unix socket communication, corresponding to `AF_UNIX`.
267    pub const UNIX: Domain = Domain(sys::AF_UNIX);
268
269    /// Returns the correct domain for `address`.
270    pub const fn for_address(address: SocketAddr) -> Domain {
271        match address {
272            SocketAddr::V4(_) => Domain::IPV4,
273            SocketAddr::V6(_) => Domain::IPV6,
274        }
275    }
276}
277
278impl From<c_int> for Domain {
279    fn from(d: c_int) -> Domain {
280        Domain(d)
281    }
282}
283
284impl From<Domain> for c_int {
285    fn from(d: Domain) -> c_int {
286        d.0
287    }
288}
289
290/// Specification of communication semantics on a socket.
291///
292/// This is a newtype wrapper around an integer which provides a nicer API in
293/// addition to an injection point for documentation. Convenience constants such
294/// as [`Type::STREAM`], [`Type::DGRAM`], etc, are provided to avoid reaching
295/// into libc for various constants.
296///
297/// This type is freely interconvertible with C's `int` type, however, if a raw
298/// value needs to be provided.
299#[derive(Copy, Clone, Eq, PartialEq)]
300pub struct Type(c_int);
301
302impl Type {
303    /// Type corresponding to `SOCK_STREAM`.
304    ///
305    /// Used for protocols such as TCP.
306    pub const STREAM: Type = Type(sys::SOCK_STREAM);
307
308    /// Type corresponding to `SOCK_DGRAM`.
309    ///
310    /// Used for protocols such as UDP.
311    pub const DGRAM: Type = Type(sys::SOCK_DGRAM);
312
313    /// Type corresponding to `SOCK_DCCP`.
314    ///
315    /// Used for the DCCP protocol.
316    #[cfg(all(feature = "all", target_os = "linux"))]
317    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
318    pub const DCCP: Type = Type(sys::SOCK_DCCP);
319
320    /// Type corresponding to `SOCK_SEQPACKET`.
321    #[cfg(all(feature = "all", not(target_os = "espidf")))]
322    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", not(target_os = "espidf")))))]
323    pub const SEQPACKET: Type = Type(sys::SOCK_SEQPACKET);
324
325    /// Type corresponding to `SOCK_RAW`.
326    #[cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))]
327    #[cfg_attr(
328        docsrs,
329        doc(cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf")))))
330    )]
331    pub const RAW: Type = Type(sys::SOCK_RAW);
332}
333
334impl From<c_int> for Type {
335    fn from(t: c_int) -> Type {
336        Type(t)
337    }
338}
339
340impl From<Type> for c_int {
341    fn from(t: Type) -> c_int {
342        t.0
343    }
344}
345
346/// Protocol specification used for creating sockets via `Socket::new`.
347///
348/// This is a newtype wrapper around an integer which provides a nicer API in
349/// addition to an injection point for documentation.
350///
351/// This type is freely interconvertible with C's `int` type, however, if a raw
352/// value needs to be provided.
353#[derive(Copy, Clone, Eq, PartialEq)]
354pub struct Protocol(c_int);
355
356impl Protocol {
357    /// Protocol corresponding to `ICMPv4`.
358    pub const ICMPV4: Protocol = Protocol(sys::IPPROTO_ICMP);
359
360    /// Protocol corresponding to `ICMPv6`.
361    pub const ICMPV6: Protocol = Protocol(sys::IPPROTO_ICMPV6);
362
363    /// Protocol corresponding to `TCP`.
364    pub const TCP: Protocol = Protocol(sys::IPPROTO_TCP);
365
366    /// Protocol corresponding to `UDP`.
367    pub const UDP: Protocol = Protocol(sys::IPPROTO_UDP);
368
369    #[cfg(target_os = "linux")]
370    /// Protocol corresponding to `MPTCP`.
371    pub const MPTCP: Protocol = Protocol(sys::IPPROTO_MPTCP);
372
373    /// Protocol corresponding to `DCCP`.
374    #[cfg(all(feature = "all", target_os = "linux"))]
375    #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
376    pub const DCCP: Protocol = Protocol(sys::IPPROTO_DCCP);
377
378    /// Protocol corresponding to `SCTP`.
379    #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
380    pub const SCTP: Protocol = Protocol(sys::IPPROTO_SCTP);
381
382    /// Protocol corresponding to `UDPLITE`.
383    #[cfg(all(
384        feature = "all",
385        any(
386            target_os = "android",
387            target_os = "freebsd",
388            target_os = "fuchsia",
389            target_os = "linux",
390        )
391    ))]
392    pub const UDPLITE: Protocol = Protocol(sys::IPPROTO_UDPLITE);
393
394    /// Protocol corresponding to `DIVERT`.
395    #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "openbsd")))]
396    pub const DIVERT: Protocol = Protocol(sys::IPPROTO_DIVERT);
397}
398
399impl From<c_int> for Protocol {
400    fn from(p: c_int) -> Protocol {
401        Protocol(p)
402    }
403}
404
405impl From<Protocol> for c_int {
406    fn from(p: Protocol) -> c_int {
407        p.0
408    }
409}
410
411/// Flags for incoming messages.
412///
413/// Flags provide additional information about incoming messages.
414#[cfg(not(target_os = "redox"))]
415#[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))]
416#[derive(Copy, Clone, Eq, PartialEq)]
417pub struct RecvFlags(c_int);
418
419#[cfg(not(target_os = "redox"))]
420impl RecvFlags {
421    /// Check if the message contains a truncated datagram.
422    ///
423    /// This flag is only used for datagram-based sockets,
424    /// not for stream sockets.
425    ///
426    /// On Unix this corresponds to the `MSG_TRUNC` flag.
427    /// On Windows this corresponds to the `WSAEMSGSIZE` error code.
428    #[cfg(not(target_os = "espidf"))]
429    pub const fn is_truncated(self) -> bool {
430        self.0 & sys::MSG_TRUNC != 0
431    }
432}
433
434/// A version of [`IoSliceMut`] that allows the buffer to be uninitialised.
435///
436/// [`IoSliceMut`]: std::io::IoSliceMut
437#[repr(transparent)]
438pub struct MaybeUninitSlice<'a>(sys::MaybeUninitSlice<'a>);
439
440impl<'a> fmt::Debug for MaybeUninitSlice<'a> {
441    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
442        fmt::Debug::fmt(self.0.as_slice(), fmt)
443    }
444}
445
446impl<'a> MaybeUninitSlice<'a> {
447    /// Creates a new `MaybeUninitSlice` wrapping a byte slice.
448    ///
449    /// # Panics
450    ///
451    /// Panics on Windows if the slice is larger than 4GB.
452    pub fn new(buf: &'a mut [MaybeUninit<u8>]) -> MaybeUninitSlice<'a> {
453        MaybeUninitSlice(sys::MaybeUninitSlice::new(buf))
454    }
455}
456
457impl<'a> Deref for MaybeUninitSlice<'a> {
458    type Target = [MaybeUninit<u8>];
459
460    fn deref(&self) -> &[MaybeUninit<u8>] {
461        self.0.as_slice()
462    }
463}
464
465impl<'a> DerefMut for MaybeUninitSlice<'a> {
466    fn deref_mut(&mut self) -> &mut [MaybeUninit<u8>] {
467        self.0.as_mut_slice()
468    }
469}
470
471/// Configures a socket's TCP keepalive parameters.
472///
473/// See [`Socket::set_tcp_keepalive`].
474#[derive(Debug, Clone)]
475pub struct TcpKeepalive {
476    #[cfg_attr(
477        any(target_os = "openbsd", target_os = "haiku", target_os = "vita"),
478        allow(dead_code)
479    )]
480    time: Option<Duration>,
481    #[cfg(not(any(
482        target_os = "openbsd",
483        target_os = "redox",
484        target_os = "solaris",
485        target_os = "nto",
486        target_os = "espidf",
487        target_os = "vita",
488        target_os = "haiku",
489    )))]
490    interval: Option<Duration>,
491    #[cfg(not(any(
492        target_os = "openbsd",
493        target_os = "redox",
494        target_os = "solaris",
495        target_os = "windows",
496        target_os = "nto",
497        target_os = "espidf",
498        target_os = "vita",
499        target_os = "haiku",
500    )))]
501    retries: Option<u32>,
502}
503
504impl TcpKeepalive {
505    /// Returns a new, empty set of TCP keepalive parameters.
506    pub const fn new() -> TcpKeepalive {
507        TcpKeepalive {
508            time: None,
509            #[cfg(not(any(
510                target_os = "openbsd",
511                target_os = "redox",
512                target_os = "solaris",
513                target_os = "nto",
514                target_os = "espidf",
515                target_os = "vita",
516                target_os = "haiku",
517            )))]
518            interval: None,
519            #[cfg(not(any(
520                target_os = "openbsd",
521                target_os = "redox",
522                target_os = "solaris",
523                target_os = "windows",
524                target_os = "nto",
525                target_os = "espidf",
526                target_os = "vita",
527                target_os = "haiku",
528            )))]
529            retries: None,
530        }
531    }
532
533    /// Set the amount of time after which TCP keepalive probes will be sent on
534    /// idle connections.
535    ///
536    /// This will set `TCP_KEEPALIVE` on macOS and iOS, and
537    /// `TCP_KEEPIDLE` on all other Unix operating systems, except
538    /// OpenBSD and Haiku which don't support any way to set this
539    /// option. On Windows, this sets the value of the `tcp_keepalive`
540    /// struct's `keepalivetime` field.
541    ///
542    /// Some platforms specify this value in seconds, so sub-second
543    /// specifications may be omitted.
544    pub const fn with_time(self, time: Duration) -> Self {
545        Self {
546            time: Some(time),
547            ..self
548        }
549    }
550
551    /// Set the value of the `TCP_KEEPINTVL` option. On Windows, this sets the
552    /// value of the `tcp_keepalive` struct's `keepaliveinterval` field.
553    ///
554    /// Sets the time interval between TCP keepalive probes.
555    ///
556    /// Some platforms specify this value in seconds, so sub-second
557    /// specifications may be omitted.
558    #[cfg(any(
559        target_os = "android",
560        target_os = "dragonfly",
561        target_os = "freebsd",
562        target_os = "fuchsia",
563        target_os = "illumos",
564        target_os = "ios",
565        target_os = "linux",
566        target_os = "macos",
567        target_os = "netbsd",
568        target_os = "tvos",
569        target_os = "watchos",
570        target_os = "windows",
571    ))]
572    #[cfg_attr(
573        docsrs,
574        doc(cfg(any(
575            target_os = "android",
576            target_os = "dragonfly",
577            target_os = "freebsd",
578            target_os = "fuchsia",
579            target_os = "illumos",
580            target_os = "ios",
581            target_os = "linux",
582            target_os = "macos",
583            target_os = "netbsd",
584            target_os = "tvos",
585            target_os = "watchos",
586            target_os = "windows",
587        )))
588    )]
589    pub const fn with_interval(self, interval: Duration) -> Self {
590        Self {
591            interval: Some(interval),
592            ..self
593        }
594    }
595
596    /// Set the value of the `TCP_KEEPCNT` option.
597    ///
598    /// Set the maximum number of TCP keepalive probes that will be sent before
599    /// dropping a connection, if TCP keepalive is enabled on this socket.
600    #[cfg(all(
601        feature = "all",
602        any(
603            target_os = "android",
604            target_os = "dragonfly",
605            target_os = "freebsd",
606            target_os = "fuchsia",
607            target_os = "illumos",
608            target_os = "ios",
609            target_os = "linux",
610            target_os = "macos",
611            target_os = "netbsd",
612            target_os = "tvos",
613            target_os = "watchos",
614        )
615    ))]
616    #[cfg_attr(
617        docsrs,
618        doc(cfg(all(
619            feature = "all",
620            any(
621                target_os = "android",
622                target_os = "dragonfly",
623                target_os = "freebsd",
624                target_os = "fuchsia",
625                target_os = "illumos",
626                target_os = "ios",
627                target_os = "linux",
628                target_os = "macos",
629                target_os = "netbsd",
630                target_os = "tvos",
631                target_os = "watchos",
632            )
633        )))
634    )]
635    pub const fn with_retries(self, retries: u32) -> Self {
636        Self {
637            retries: Some(retries),
638            ..self
639        }
640    }
641}
642
643impl Default for TcpKeepalive {
644    fn default() -> Self {
645        Self::new()
646    }
647}
648
649/// Configuration of a `sendmsg(2)` system call.
650///
651/// This wraps `msghdr` on Unix and `WSAMSG` on Windows. Also see [`MsgHdrMut`]
652/// for the variant used by `recvmsg(2)`.
653#[cfg(not(target_os = "redox"))]
654pub struct MsgHdr<'addr, 'bufs, 'control> {
655    inner: sys::msghdr,
656    #[allow(clippy::type_complexity)]
657    _lifetimes: PhantomData<(&'addr SockAddr, &'bufs IoSlice<'bufs>, &'control [u8])>,
658}
659
660#[cfg(not(target_os = "redox"))]
661impl<'addr, 'bufs, 'control> MsgHdr<'addr, 'bufs, 'control> {
662    /// Create a new `MsgHdr` with all empty/zero fields.
663    #[allow(clippy::new_without_default)]
664    pub fn new() -> MsgHdr<'addr, 'bufs, 'control> {
665        // SAFETY: all zero is valid for `msghdr` and `WSAMSG`.
666        MsgHdr {
667            inner: unsafe { mem::zeroed() },
668            _lifetimes: PhantomData,
669        }
670    }
671
672    /// Set the address (name) of the message.
673    ///
674    /// Corresponds to setting `msg_name` and `msg_namelen` on Unix and `name`
675    /// and `namelen` on Windows.
676    pub fn with_addr(mut self, addr: &'addr SockAddr) -> Self {
677        sys::set_msghdr_name(&mut self.inner, addr);
678        self
679    }
680
681    /// Set the buffer(s) of the message.
682    ///
683    /// Corresponds to setting `msg_iov` and `msg_iovlen` on Unix and `lpBuffers`
684    /// and `dwBufferCount` on Windows.
685    pub fn with_buffers(mut self, bufs: &'bufs [IoSlice<'_>]) -> Self {
686        let ptr = bufs.as_ptr() as *mut _;
687        sys::set_msghdr_iov(&mut self.inner, ptr, bufs.len());
688        self
689    }
690
691    /// Set the control buffer of the message.
692    ///
693    /// Corresponds to setting `msg_control` and `msg_controllen` on Unix and
694    /// `Control` on Windows.
695    pub fn with_control(mut self, buf: &'control [u8]) -> Self {
696        let ptr = buf.as_ptr() as *mut _;
697        sys::set_msghdr_control(&mut self.inner, ptr, buf.len());
698        self
699    }
700
701    /// Set the flags of the message.
702    ///
703    /// Corresponds to setting `msg_flags` on Unix and `dwFlags` on Windows.
704    pub fn with_flags(mut self, flags: sys::c_int) -> Self {
705        sys::set_msghdr_flags(&mut self.inner, flags);
706        self
707    }
708}
709
710#[cfg(not(target_os = "redox"))]
711impl<'name, 'bufs, 'control> fmt::Debug for MsgHdr<'name, 'bufs, 'control> {
712    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
713        "MsgHdr".fmt(fmt)
714    }
715}
716
717/// Configuration of a `recvmsg(2)` system call.
718///
719/// This wraps `msghdr` on Unix and `WSAMSG` on Windows. Also see [`MsgHdr`] for
720/// the variant used by `sendmsg(2)`.
721#[cfg(not(target_os = "redox"))]
722pub struct MsgHdrMut<'addr, 'bufs, 'control> {
723    inner: sys::msghdr,
724    #[allow(clippy::type_complexity)]
725    _lifetimes: PhantomData<(
726        &'addr mut SockAddr,
727        &'bufs mut MaybeUninitSlice<'bufs>,
728        &'control mut [u8],
729    )>,
730}
731
732#[cfg(not(target_os = "redox"))]
733impl<'addr, 'bufs, 'control> MsgHdrMut<'addr, 'bufs, 'control> {
734    /// Create a new `MsgHdrMut` with all empty/zero fields.
735    #[allow(clippy::new_without_default)]
736    pub fn new() -> MsgHdrMut<'addr, 'bufs, 'control> {
737        // SAFETY: all zero is valid for `msghdr` and `WSAMSG`.
738        MsgHdrMut {
739            inner: unsafe { mem::zeroed() },
740            _lifetimes: PhantomData,
741        }
742    }
743
744    /// Set the mutable address (name) of the message.
745    ///
746    /// Corresponds to setting `msg_name` and `msg_namelen` on Unix and `name`
747    /// and `namelen` on Windows.
748    #[allow(clippy::needless_pass_by_ref_mut)]
749    pub fn with_addr(mut self, addr: &'addr mut SockAddr) -> Self {
750        sys::set_msghdr_name(&mut self.inner, addr);
751        self
752    }
753
754    /// Set the mutable buffer(s) of the message.
755    ///
756    /// Corresponds to setting `msg_iov` and `msg_iovlen` on Unix and `lpBuffers`
757    /// and `dwBufferCount` on Windows.
758    pub fn with_buffers(mut self, bufs: &'bufs mut [MaybeUninitSlice<'_>]) -> Self {
759        sys::set_msghdr_iov(&mut self.inner, bufs.as_mut_ptr().cast(), bufs.len());
760        self
761    }
762
763    /// Set the mutable control buffer of the message.
764    ///
765    /// Corresponds to setting `msg_control` and `msg_controllen` on Unix and
766    /// `Control` on Windows.
767    pub fn with_control(mut self, buf: &'control mut [MaybeUninit<u8>]) -> Self {
768        sys::set_msghdr_control(&mut self.inner, buf.as_mut_ptr().cast(), buf.len());
769        self
770    }
771
772    /// Returns the flags of the message.
773    pub fn flags(&self) -> RecvFlags {
774        sys::msghdr_flags(&self.inner)
775    }
776
777    /// Gets the length of the control buffer.
778    ///
779    /// Can be used to determine how much, if any, of the control buffer was filled by `recvmsg`.
780    ///
781    /// Corresponds to `msg_controllen` on Unix and `Control.len` on Windows.
782    pub fn control_len(&self) -> usize {
783        sys::msghdr_control_len(&self.inner)
784    }
785}
786
787#[cfg(not(target_os = "redox"))]
788impl<'name, 'bufs, 'control> fmt::Debug for MsgHdrMut<'name, 'bufs, 'control> {
789    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
790        "MsgHdrMut".fmt(fmt)
791    }
792}
793
794/// Configuration of a `recvmsg(2)` system call with initialized buffers.
795///
796/// This wraps `msghdr` on Unix and `WSAMSG` on Windows and supports
797/// fully initialized buffers.
798///
799/// For the control messages, please use [MsgHdrInit::cmsg_hdr_vec] to retrieve.
800#[cfg(not(any(
801    target_os = "freebsd",
802    target_os = "fuchsia",
803    target_os = "hurd",
804    target_os = "redox",
805    target_os = "vita",
806)))]
807pub struct MsgHdrInit<'addr, 'bufs, 'control> {
808    inner: sys::msghdr,
809
810    /// This is a workaround for a subtle problem:
811    ///
812    /// `sys::msghdr` does not store the full `SockAddr`, namely only `sockaddr_storage`,
813    /// not the `len`. Any syscall that operates on `MsgHdrInit` does not update
814    /// `addr.len` in the struct passed in via `with_addr`. Hence we store a reference
815    /// here to access both the storage and the len.
816    src: Option<&'addr mut SockAddr>,
817
818    _lifetimes: PhantomData<(&'bufs [IoSliceMut<'bufs>], &'control [u8])>,
819}
820
821#[cfg(not(any(
822    target_os = "freebsd",
823    target_os = "fuchsia",
824    target_os = "hurd",
825    target_os = "redox",
826    target_os = "vita",
827)))]
828impl<'addr, 'bufs, 'control> MsgHdrInit<'addr, 'bufs, 'control> {
829    /// Create a new `MsgHdrInit` with all empty/zero fields.
830    #[allow(clippy::new_without_default)]
831    pub fn new() -> MsgHdrInit<'addr, 'bufs, 'control> {
832        // SAFETY: all zero is valid for `msghdr` and `WSAMSG`.
833        MsgHdrInit {
834            inner: unsafe { mem::zeroed() },
835            src: None,
836            _lifetimes: PhantomData,
837        }
838    }
839
840    /// Set the mutable address buffer to store the source address.
841    ///
842    /// Corresponds to setting `msg_name` and `msg_namelen` on Unix and `name`
843    /// and `namelen` on Windows.
844    #[allow(clippy::needless_pass_by_ref_mut)]
845    pub fn with_addr(mut self, addr: &'addr mut SockAddr) -> Self {
846        sys::set_msghdr_name(&mut self.inner, addr);
847        self.src = Some(addr);
848        self
849    }
850
851    /// Set the mutable array of buffers for receiving the message.
852    ///
853    /// Corresponds to setting `msg_iov` and `msg_iovlen` on Unix and `lpBuffers`
854    /// and `dwBufferCount` on Windows.
855    pub fn with_buffers(mut self, buf_list: &'bufs mut [IoSliceMut<'_>]) -> Self {
856        sys::set_msghdr_iov(
857            &mut self.inner,
858            buf_list.as_mut_ptr().cast(),
859            buf_list.len(),
860        );
861        self
862    }
863
864    /// Set the mutable control buffer of the message.
865    ///
866    /// Corresponds to setting `msg_control` and `msg_controllen` on Unix and
867    /// `Control` on Windows.
868    pub fn with_control(mut self, buf: &'control mut [u8]) -> Self {
869        sys::set_msghdr_control(&mut self.inner, buf.as_mut_ptr().cast(), buf.len());
870        self
871    }
872
873    /// Returns the list of control message headers in the message.
874    ///
875    /// This decodes the control messages inside the ancillary data buffer.
876    pub fn cmsg_hdr_vec(&self) -> Vec<CMsgHdr<'_>> {
877        let mut cmsg_vec = Vec::new();
878
879        let mut cmsg = self.inner.cmsg_first_hdr();
880        if !cmsg.is_null() {
881            let cmsg_hdr = unsafe { CMsgHdr { inner: &*cmsg } };
882            cmsg_vec.push(cmsg_hdr);
883
884            cmsg = self.inner.cmsg_next_hdr(unsafe { &*cmsg });
885            while !cmsg.is_null() {
886                let cmsg_hdr = unsafe { CMsgHdr { inner: &*cmsg } };
887                cmsg_vec.push(cmsg_hdr);
888            }
889        }
890
891        cmsg_vec
892    }
893
894    /// Returns the optional source address in the msg.
895    ///
896    /// This is a convenient method to access the address passed in [`Self::with_addr`]
897    pub fn get_addr(&self) -> Option<&SockAddr> {
898        self.src.as_deref()
899    }
900}
901
902#[cfg(not(any(
903    target_os = "freebsd",
904    target_os = "fuchsia",
905    target_os = "hurd",
906    target_os = "redox",
907    target_os = "vita",
908)))]
909impl fmt::Debug for MsgHdrInit<'_, '_, '_> {
910    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
911        "MsgHdrInit".fmt(fmt)
912    }
913}
914
915/// Common operations supported on `msghdr`
916#[cfg(not(any(
917    target_os = "freebsd",
918    target_os = "fuchsia",
919    target_os = "hurd",
920    target_os = "redox",
921    target_os = "vita",
922)))]
923pub(crate) trait MsgHdrOps {
924    fn cmsg_first_hdr(&self) -> *mut sys::cmsghdr;
925
926    fn cmsg_next_hdr(&self, cmsg: &sys::cmsghdr) -> *mut sys::cmsghdr;
927}
928
929/// Reference of a control message header in the control buffer in `MsgHdrInit`
930#[cfg(not(any(
931    target_os = "freebsd",
932    target_os = "fuchsia",
933    target_os = "hurd",
934    target_os = "redox",
935    target_os = "vita",
936)))]
937pub struct CMsgHdr<'a> {
938    inner: &'a sys::cmsghdr,
939}
940
941#[cfg(not(any(
942    target_os = "freebsd",
943    target_os = "fuchsia",
944    target_os = "hurd",
945    target_os = "redox",
946    target_os = "vita",
947)))]
948impl CMsgHdr<'_> {
949    /// Get the cmsg level
950    pub fn get_level(&self) -> CMsgLevel {
951        self.inner.cmsg_level
952    }
953
954    /// Get the cmsg type
955    pub fn get_type(&self) -> CMsgType {
956        self.inner.cmsg_type
957    }
958
959    /// Decode this header as IN_PKTINFO
960    pub fn as_pktinfo_v4(&self) -> Option<PktInfoV4> {
961        if self.inner.cmsg_level != sys::IPPROTO_IP {
962            return None;
963        }
964
965        if self.inner.cmsg_type != sys::IP_PKTINFO {
966            return None;
967        }
968
969        let data_ptr = self.inner.cmsg_data();
970        let pktinfo = unsafe { ptr::read_unaligned(data_ptr as *const sys::InPktInfo) };
971
972        #[cfg(not(windows))]
973        let addr_dst = Ipv4Addr::from(u32::from_be(pktinfo.ipi_addr.s_addr));
974
975        #[cfg(windows)]
976        let addr_dst = Ipv4Addr::from(u32::from_be(unsafe { pktinfo.ipi_addr.S_un.S_addr }));
977
978        Some(PktInfoV4 {
979            if_index: pktinfo.ipi_ifindex as _,
980            addr_dst,
981        })
982    }
983
984    /// Decode this header as IN6_PKTINFO
985    pub fn as_recvpktinfo_v6(&self) -> Option<PktInfoV6> {
986        if self.inner.cmsg_level != sys::IPPROTO_IPV6 {
987            return None;
988        }
989
990        if self.inner.cmsg_type != sys::IPV6_PKTINFO {
991            return None;
992        }
993
994        let data_ptr = self.inner.cmsg_data();
995        let pktinfo = unsafe { ptr::read_unaligned(data_ptr as *const sys::In6PktInfo) };
996
997        #[cfg(windows)]
998        let addr_dst = Ipv6Addr::from(unsafe { pktinfo.ipi6_addr.u.Byte });
999
1000        #[cfg(not(windows))]
1001        let addr_dst = Ipv6Addr::from(pktinfo.ipi6_addr.s6_addr);
1002
1003        Some(PktInfoV6 {
1004            if_index: pktinfo.ipi6_ifindex as _,
1005            addr_dst,
1006        })
1007    }
1008}
1009
1010#[cfg(not(any(
1011    target_os = "freebsd",
1012    target_os = "fuchsia",
1013    target_os = "hurd",
1014    target_os = "redox",
1015    target_os = "vita",
1016)))]
1017pub(crate) trait CMsgHdrOps {
1018    /// Returns a pointer to the data portion of a cmsghdr.
1019    fn cmsg_data(&self) -> *mut u8;
1020}
1021
1022/// Given a payload of `data_len`, returns the number of bytes a control message occupies.
1023/// i.e. it includes the header, the data and the alignments.
1024#[cfg(not(any(
1025    target_os = "freebsd",
1026    target_os = "fuchsia",
1027    target_os = "hurd",
1028    target_os = "redox",
1029    target_os = "vita",
1030)))]
1031pub const fn cmsg_space(data_len: usize) -> usize {
1032    sys::_cmsg_space(data_len)
1033}
1034
1035#[cfg(not(any(
1036    target_os = "freebsd",
1037    target_os = "fuchsia",
1038    target_os = "hurd",
1039    target_os = "redox",
1040    target_os = "vita",
1041)))]
1042impl fmt::Debug for CMsgHdr<'_> {
1043    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1044        write!(
1045            f,
1046            "(len: {} level: {} type: {})",
1047            self.inner.cmsg_len, self.inner.cmsg_level, self.inner.cmsg_type
1048        )
1049    }
1050}
1051
1052#[cfg(not(any(
1053    target_os = "freebsd",
1054    target_os = "fuchsia",
1055    target_os = "hurd",
1056    target_os = "redox",
1057    target_os = "vita",
1058)))]
1059const IN_PKTINFO_SIZE: usize = mem::size_of::<sys::InPktInfo>();
1060#[cfg(not(any(
1061    target_os = "freebsd",
1062    target_os = "fuchsia",
1063    target_os = "hurd",
1064    target_os = "redox",
1065    target_os = "vita",
1066)))]
1067const IN6_PKTINFO_SIZE: usize = mem::size_of::<sys::In6PktInfo>();
1068
1069/// Represents IN_PKTINFO structure.
1070#[derive(Debug)]
1071pub struct PktInfoV4 {
1072    /// Interface index
1073    pub if_index: u64,
1074
1075    /// Header destination address
1076    pub addr_dst: Ipv4Addr,
1077}
1078
1079impl PktInfoV4 {
1080    /// The size in bytes for IPv4 pktinfo
1081    #[cfg(not(any(
1082        target_os = "freebsd",
1083        target_os = "fuchsia",
1084        target_os = "hurd",
1085        target_os = "redox",
1086        target_os = "vita",
1087    )))]
1088    pub const fn size() -> usize {
1089        IN_PKTINFO_SIZE
1090    }
1091}
1092
1093/// Represents IN6_PKTINFO structure.
1094#[derive(Debug)]
1095pub struct PktInfoV6 {
1096    /// Interface index
1097    pub if_index: u64,
1098
1099    /// Header destination address
1100    pub addr_dst: Ipv6Addr,
1101}
1102
1103impl PktInfoV6 {
1104    /// The size in bytes for IPv6 pktinfo
1105    #[cfg(not(any(
1106        target_os = "freebsd",
1107        target_os = "fuchsia",
1108        target_os = "hurd",
1109        target_os = "redox",
1110        target_os = "vita",
1111    )))]
1112    pub const fn size() -> usize {
1113        IN6_PKTINFO_SIZE
1114    }
1115}
1116
1117/// Represents available protocols
1118pub type CMsgLevel = i32;
1119
1120/// constant for cmsg_level of IPPROTO_IP
1121#[cfg(not(any(
1122    target_os = "freebsd",
1123    target_os = "fuchsia",
1124    target_os = "hurd",
1125    target_os = "redox",
1126    target_os = "vita",
1127)))]
1128pub const CMSG_LEVEL_IPPROTO_IP: CMsgLevel = sys::IPPROTO_IP;
1129
1130/// constant for cmsg_level of IPPROTO_IPV6
1131#[cfg(not(any(
1132    target_os = "freebsd",
1133    target_os = "fuchsia",
1134    target_os = "hurd",
1135    target_os = "redox",
1136    target_os = "vita",
1137)))]
1138pub const CMSG_LEVEL_IPPROTO_IPV6: CMsgLevel = sys::IPPROTO_IPV6;
1139
1140/// Represents available types of control messages.
1141pub type CMsgType = i32;
1142
1143/// constant for cmsghdr type
1144#[cfg(not(any(
1145    target_os = "freebsd",
1146    target_os = "fuchsia",
1147    target_os = "hurd",
1148    target_os = "redox",
1149    target_os = "vita",
1150)))]
1151pub const CMSG_TYPE_IP_PKTINFO: CMsgType = sys::IP_PKTINFO;
1152
1153/// constant for cmsghdr type in IPv6
1154#[cfg(not(any(
1155    target_os = "freebsd",
1156    target_os = "fuchsia",
1157    target_os = "hurd",
1158    target_os = "redox",
1159    target_os = "vita",
1160)))]
1161pub const CMSG_TYPE_IPV6_PKTINFO: CMsgType = sys::IPV6_PKTINFO;