Skip to main content

socket2/
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#![allow(clippy::needless_lifetimes)]
10
11//! Utilities for creating and using sockets.
12//!
13//! The goal of this crate is to create and use a socket using advanced
14//! configuration options (those that are not available in the types in the
15//! standard library) without using any unsafe code.
16//!
17//! This crate provides as direct as possible access to the system's
18//! functionality for sockets, this means little effort to provide
19//! cross-platform utilities. It is up to the user to know how to use sockets
20//! when using this crate. *If you don't know how to create a socket using
21//! libc/system calls then this crate is not for you*. Most, if not all,
22//! functions directly relate to the equivalent system call with no error
23//! handling applied, so no handling errors such as [`EINTR`]. As a result using
24//! this crate can be a little wordy, but it should give you maximal flexibility
25//! over configuration of sockets.
26//!
27//! [`EINTR`]: std::io::ErrorKind::Interrupted
28//!
29//! # Examples
30//!
31//! ```no_run
32//! # fn main() -> std::io::Result<()> {
33//! use std::net::{SocketAddr, TcpListener};
34//! use socket2::{Socket, Domain, Type};
35//!
36//! // Create a TCP listener bound to two addresses.
37//! let socket = Socket::new(Domain::IPV6, Type::STREAM, None)?;
38//!
39//! socket.set_only_v6(false)?;
40//! let address: SocketAddr = "[::1]:12345".parse().unwrap();
41//! socket.bind(&address.into())?;
42//! socket.listen(128)?;
43//!
44//! let listener: TcpListener = socket.into();
45//! // ...
46//! # drop(listener);
47//! # Ok(()) }
48//! ```
49//!
50//! ## Features
51//!
52//! This crate has a single feature `all`, which enables all functions even ones
53//! that are not available on all OSs.
54
55#![deny(missing_docs, missing_debug_implementations, rust_2018_idioms)]
56// Automatically generate required OS/features for docs.rs.
57#![cfg_attr(docsrs, feature(doc_cfg))]
58// Disallow warnings when running tests.
59#![cfg_attr(test, deny(warnings))]
60// Disallow warnings in examples.
61#![doc(test(attr(deny(warnings))))]
62
63use std::fmt;
64#[cfg(not(any(target_os = "redox", target_os = "wasi", target_os = "horizon")))]
65use std::io::IoSlice;
66#[cfg(not(any(target_os = "redox", target_os = "wasi", target_os = "horizon")))]
67use std::marker::PhantomData;
68#[cfg(not(any(target_os = "redox", target_os = "wasi", target_os = "horizon")))]
69use std::mem;
70use std::mem::MaybeUninit;
71use std::net::SocketAddr;
72use std::ops::{Deref, DerefMut};
73use std::time::Duration;
74
75/// Macro to implement `fmt::Debug` for a type, printing the constant names
76/// rather than a number.
77///
78/// Note this is used in the `sys` module and thus must be defined before
79/// defining the modules.
80macro_rules! impl_debug {
81    (
82        // Type name for which to implement `fmt::Debug`.
83        $type: path,
84        $(
85            $(#[$target: meta])*
86            // The flag(s) to check.
87            // Need to specific the libc crate because Windows doesn't use
88            // `libc` but `windows_sys`.
89            $libc: ident :: $flag: ident
90        ),+ $(,)*
91    ) => {
92        impl std::fmt::Debug for $type {
93            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94                let string = match self.0 {
95                    $(
96                        $(#[$target])*
97                        $libc :: $flag => stringify!($flag),
98                    )+
99                    n => return write!(f, "{n}"),
100                };
101                f.write_str(string)
102            }
103        }
104    };
105}
106
107/// Macro to convert from one network type to another.
108macro_rules! from {
109    ($from: ty, $for: ty) => {
110        impl From<$from> for $for {
111            fn from(socket: $from) -> $for {
112                #[cfg(any(unix, all(target_os = "wasi", not(target_env = "p1"))))]
113                unsafe {
114                    <$for>::from_raw_fd(socket.into_raw_fd())
115                }
116                #[cfg(windows)]
117                unsafe {
118                    <$for>::from_raw_socket(socket.into_raw_socket())
119                }
120            }
121        }
122    };
123}
124
125/// Link to online documentation for (almost) all supported OSs.
126#[rustfmt::skip]
127macro_rules! man_links {
128    // Links to all OSs.
129    ($syscall: tt ( $section: tt ) ) => {
130        concat!(
131            man_links!(__ intro),
132            man_links!(__ unix $syscall($section)),
133            man_links!(__ windows $syscall($section)),
134        )
135    };
136    // Links to Unix-like OSs.
137    (unix: $syscall: tt ( $section: tt ) ) => {
138        concat!(
139            man_links!(__ intro),
140            man_links!(__ unix $syscall($section)),
141        )
142    };
143    // Links to Windows only.
144    (windows: $syscall: tt ( $section: tt ) ) => {
145        concat!(
146            man_links!(__ intro),
147            man_links!(__ windows $syscall($section)),
148        )
149    };
150    // Internals.
151    (__ intro) => {
152        "\n\nAdditional documentation can be found in manual of the OS:\n\n"
153    };
154    // List for Unix-like OSs.
155    (__ unix $syscall: tt ( $section: tt ) ) => {
156        concat!(
157            " * DragonFly BSD: <https://man.dragonflybsd.org/?command=", stringify!($syscall), "&section=", stringify!($section), ">\n",
158            " * FreeBSD: <https://www.freebsd.org/cgi/man.cgi?query=", stringify!($syscall), "&sektion=", stringify!($section), ">\n",
159            " * Linux: <https://man7.org/linux/man-pages/man", stringify!($section), "/", stringify!($syscall), ".", stringify!($section), ".html>\n",
160            " * macOS: <https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/", stringify!($syscall), ".", stringify!($section), ".html> (archived, actually for iOS)\n",
161            " * NetBSD: <https://man.netbsd.org/", stringify!($syscall), ".", stringify!($section), ">\n",
162            " * OpenBSD: <https://man.openbsd.org/", stringify!($syscall), ".", stringify!($section), ">\n",
163            " * iOS: <https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/", stringify!($syscall), ".", stringify!($section), ".html> (archived)\n",
164            " * illumos: <https://illumos.org/man/3SOCKET/", stringify!($syscall), ">\n",
165        )
166    };
167    // List for Window (so just Windows).
168    (__ windows $syscall: tt ( $section: tt ) ) => {
169        concat!(
170            " * Windows: <https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-", stringify!($syscall), ">\n",
171        )
172    };
173}
174
175mod sockaddr;
176mod socket;
177mod sockref;
178
179#[cfg_attr(
180    any(unix, all(target_os = "wasi", not(target_env = "p1"))),
181    path = "sys/unix.rs"
182)]
183#[cfg_attr(windows, path = "sys/windows.rs")]
184mod sys;
185
186#[cfg(not(any(windows, unix, all(target_os = "wasi", not(target_env = "p1")))))]
187compile_error!("Socket2 doesn't support the compile target");
188
189use sys::c_int;
190
191pub use sockaddr::{sa_family_t, socklen_t, SockAddr, SockAddrStorage};
192#[cfg(not(any(
193    target_os = "haiku",
194    target_os = "illumos",
195    target_os = "netbsd",
196    target_os = "redox",
197    target_os = "solaris",
198    target_os = "wasi",
199)))]
200pub use socket::InterfaceIndexOrAddress;
201pub use socket::Socket;
202pub use sockref::SockRef;
203#[cfg(all(feature = "all", target_os = "linux"))]
204pub use sys::CcidEndpoints;
205#[cfg(all(feature = "all", any(target_os = "linux", target_os = "android")))]
206pub use sys::SockFilter;
207
208/// Specification of the communication domain for a socket.
209///
210/// This is a newtype wrapper around an integer which provides a nicer API in
211/// addition to an injection point for documentation. Convenience constants such
212/// as [`Domain::IPV4`], [`Domain::IPV6`], etc, are provided to avoid reaching
213/// into libc for various constants.
214///
215/// This type is freely interconvertible with C's `int` type, however, if a raw
216/// value needs to be provided.
217#[derive(Copy, Clone, Eq, PartialEq)]
218pub struct Domain(c_int);
219
220impl Domain {
221    /// Domain for IPv4 communication, corresponding to `AF_INET`.
222    pub const IPV4: Domain = Domain(sys::AF_INET);
223
224    /// Domain for IPv6 communication, corresponding to `AF_INET6`.
225    pub const IPV6: Domain = Domain(sys::AF_INET6);
226
227    /// Domain for Unix socket communication, corresponding to `AF_UNIX`.
228    #[cfg(not(target_os = "wasi"))]
229    pub const UNIX: Domain = Domain(sys::AF_UNIX);
230
231    /// Returns the correct domain for `address`.
232    pub const fn for_address(address: SocketAddr) -> Domain {
233        match address {
234            SocketAddr::V4(_) => Domain::IPV4,
235            SocketAddr::V6(_) => Domain::IPV6,
236        }
237    }
238}
239
240impl From<c_int> for Domain {
241    fn from(d: c_int) -> Domain {
242        Domain(d)
243    }
244}
245
246impl From<Domain> for c_int {
247    fn from(d: Domain) -> c_int {
248        d.0
249    }
250}
251
252/// Specification of communication semantics on a socket.
253///
254/// This is a newtype wrapper around an integer which provides a nicer API in
255/// addition to an injection point for documentation. Convenience constants such
256/// as [`Type::STREAM`], [`Type::DGRAM`], etc, are provided to avoid reaching
257/// into libc for various constants.
258///
259/// This type is freely interconvertible with C's `int` type, however, if a raw
260/// value needs to be provided.
261#[derive(Copy, Clone, Eq, PartialEq)]
262pub struct Type(c_int);
263
264impl Type {
265    /// Type corresponding to `SOCK_STREAM`.
266    ///
267    /// Used for protocols such as TCP.
268    pub const STREAM: Type = Type(sys::SOCK_STREAM);
269
270    /// Type corresponding to `SOCK_DGRAM`.
271    ///
272    /// Used for protocols such as UDP.
273    pub const DGRAM: Type = Type(sys::SOCK_DGRAM);
274
275    /// Type corresponding to `SOCK_DCCP`.
276    ///
277    /// Used for the DCCP protocol.
278    #[cfg(all(feature = "all", target_os = "linux"))]
279    pub const DCCP: Type = Type(sys::SOCK_DCCP);
280
281    /// Type corresponding to `SOCK_SEQPACKET`.
282    #[cfg(all(
283        feature = "all",
284        not(any(target_os = "espidf", target_os = "wasi", target_os = "horizon"))
285    ))]
286    pub const SEQPACKET: Type = Type(sys::SOCK_SEQPACKET);
287
288    /// Type corresponding to `SOCK_RAW`.
289    #[cfg(all(
290        feature = "all",
291        not(any(
292            target_os = "redox",
293            target_os = "espidf",
294            target_os = "wasi",
295            target_os = "horizon"
296        ))
297    ))]
298    pub const RAW: Type = Type(sys::SOCK_RAW);
299}
300
301impl From<c_int> for Type {
302    fn from(t: c_int) -> Type {
303        Type(t)
304    }
305}
306
307impl From<Type> for c_int {
308    fn from(t: Type) -> c_int {
309        t.0
310    }
311}
312
313/// Protocol specification used for creating sockets via `Socket::new`.
314///
315/// This is a newtype wrapper around an integer which provides a nicer API in
316/// addition to an injection point for documentation.
317///
318/// This type is freely interconvertible with C's `int` type, however, if a raw
319/// value needs to be provided.
320#[derive(Copy, Clone, Eq, PartialEq)]
321pub struct Protocol(c_int);
322
323impl Protocol {
324    /// Protocol corresponding to `ICMPv4`.
325    #[cfg(not(target_os = "wasi"))]
326    pub const ICMPV4: Protocol = Protocol(sys::IPPROTO_ICMP);
327
328    /// Protocol corresponding to `ICMPv6`.
329    #[cfg(not(target_os = "wasi"))]
330    pub const ICMPV6: Protocol = Protocol(sys::IPPROTO_ICMPV6);
331
332    /// Protocol corresponding to `TCP`.
333    pub const TCP: Protocol = Protocol(sys::IPPROTO_TCP);
334
335    /// Protocol corresponding to `UDP`.
336    pub const UDP: Protocol = Protocol(sys::IPPROTO_UDP);
337
338    #[cfg(target_os = "linux")]
339    /// Protocol corresponding to `MPTCP`.
340    pub const MPTCP: Protocol = Protocol(sys::IPPROTO_MPTCP);
341
342    /// Protocol corresponding to `DCCP`.
343    #[cfg(all(feature = "all", target_os = "linux"))]
344    pub const DCCP: Protocol = Protocol(sys::IPPROTO_DCCP);
345
346    /// Protocol corresponding to `SCTP`.
347    #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
348    pub const SCTP: Protocol = Protocol(sys::IPPROTO_SCTP);
349
350    /// Protocol corresponding to `UDPLITE`.
351    #[cfg(all(
352        feature = "all",
353        any(
354            target_os = "android",
355            target_os = "freebsd",
356            target_os = "fuchsia",
357            target_os = "linux",
358        )
359    ))]
360    pub const UDPLITE: Protocol = Protocol(sys::IPPROTO_UDPLITE);
361
362    /// Protocol corresponding to `DIVERT`.
363    #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "openbsd")))]
364    pub const DIVERT: Protocol = Protocol(sys::IPPROTO_DIVERT);
365}
366
367impl From<c_int> for Protocol {
368    fn from(p: c_int) -> Protocol {
369        Protocol(p)
370    }
371}
372
373impl From<Protocol> for c_int {
374    fn from(p: Protocol) -> c_int {
375        p.0
376    }
377}
378
379/// Flags for incoming messages.
380///
381/// Flags provide additional information about incoming messages.
382#[cfg(not(any(target_os = "redox", target_os = "wasi", target_os = "horizon")))]
383#[derive(Copy, Clone, Eq, PartialEq)]
384pub struct RecvFlags(c_int);
385
386#[cfg(not(any(target_os = "redox", target_os = "wasi", target_os = "horizon")))]
387impl RecvFlags {
388    /// Check if the message contains a truncated datagram.
389    ///
390    /// This flag is only used for datagram-based sockets,
391    /// not for stream sockets.
392    ///
393    /// On Unix this corresponds to the `MSG_TRUNC` flag.
394    /// On Windows this corresponds to the `WSAEMSGSIZE` error code.
395    #[cfg(not(target_os = "espidf"))]
396    pub const fn is_truncated(self) -> bool {
397        self.0 & sys::MSG_TRUNC != 0
398    }
399}
400
401/// A version of [`IoSliceMut`] that allows the buffer to be uninitialised.
402///
403/// [`IoSliceMut`]: std::io::IoSliceMut
404#[repr(transparent)]
405pub struct MaybeUninitSlice<'a>(sys::MaybeUninitSlice<'a>);
406
407impl<'a> fmt::Debug for MaybeUninitSlice<'a> {
408    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
409        fmt::Debug::fmt(self.0.as_slice(), fmt)
410    }
411}
412
413impl<'a> MaybeUninitSlice<'a> {
414    /// Creates a new `MaybeUninitSlice` wrapping a byte slice.
415    ///
416    /// # Panics
417    ///
418    /// Panics on Windows if the slice is larger than 4GB.
419    pub fn new(buf: &'a mut [MaybeUninit<u8>]) -> MaybeUninitSlice<'a> {
420        MaybeUninitSlice(sys::MaybeUninitSlice::new(buf))
421    }
422}
423
424impl<'a> Deref for MaybeUninitSlice<'a> {
425    type Target = [MaybeUninit<u8>];
426
427    fn deref(&self) -> &[MaybeUninit<u8>] {
428        self.0.as_slice()
429    }
430}
431
432impl<'a> DerefMut for MaybeUninitSlice<'a> {
433    fn deref_mut(&mut self) -> &mut [MaybeUninit<u8>] {
434        self.0.as_mut_slice()
435    }
436}
437
438/// Configures a socket's TCP keepalive parameters.
439///
440/// See [`Socket::set_tcp_keepalive`].
441#[derive(Debug, Clone)]
442pub struct TcpKeepalive {
443    #[cfg_attr(
444        any(target_os = "openbsd", target_os = "haiku", target_os = "vita"),
445        allow(dead_code)
446    )]
447    time: Option<Duration>,
448    #[cfg(not(any(
449        target_os = "openbsd",
450        target_os = "redox",
451        target_os = "solaris",
452        target_os = "nto",
453        target_os = "espidf",
454        target_os = "vita",
455        target_os = "haiku",
456        target_os = "horizon"
457    )))]
458    interval: Option<Duration>,
459    #[cfg(not(any(
460        target_os = "openbsd",
461        target_os = "redox",
462        target_os = "solaris",
463        target_os = "nto",
464        target_os = "espidf",
465        target_os = "vita",
466        target_os = "haiku",
467        target_os = "horizon"
468    )))]
469    retries: Option<u32>,
470}
471
472impl TcpKeepalive {
473    /// Returns a new, empty set of TCP keepalive parameters.
474    #[allow(clippy::new_without_default)]
475    pub const fn new() -> TcpKeepalive {
476        TcpKeepalive {
477            time: None,
478            #[cfg(not(any(
479                target_os = "openbsd",
480                target_os = "redox",
481                target_os = "solaris",
482                target_os = "nto",
483                target_os = "espidf",
484                target_os = "vita",
485                target_os = "haiku",
486                target_os = "horizon"
487            )))]
488            interval: None,
489            #[cfg(not(any(
490                target_os = "openbsd",
491                target_os = "redox",
492                target_os = "solaris",
493                target_os = "nto",
494                target_os = "espidf",
495                target_os = "vita",
496                target_os = "haiku",
497                target_os = "horizon"
498            )))]
499            retries: None,
500        }
501    }
502
503    /// Set the amount of time after which TCP keepalive probes will be sent on
504    /// idle connections.
505    ///
506    /// This will set `TCP_KEEPALIVE` on macOS and iOS, and
507    /// `TCP_KEEPIDLE` on all other Unix operating systems, except
508    /// OpenBSD and Haiku which don't support any way to set this
509    /// option. On Windows, this sets the value of the `tcp_keepalive`
510    /// struct's `keepalivetime` field.
511    ///
512    /// Some platforms specify this value in seconds, so sub-second
513    /// specifications may be omitted.
514    pub const fn with_time(self, time: Duration) -> Self {
515        Self {
516            time: Some(time),
517            ..self
518        }
519    }
520
521    /// Set the value of the `TCP_KEEPINTVL` option. On Windows, this sets the
522    /// value of the `tcp_keepalive` struct's `keepaliveinterval` field.
523    ///
524    /// Sets the time interval between TCP keepalive probes.
525    ///
526    /// Some platforms specify this value in seconds, so sub-second
527    /// specifications may be omitted.
528    #[cfg(any(
529        target_os = "android",
530        target_os = "dragonfly",
531        target_os = "emscripten",
532        target_os = "freebsd",
533        target_os = "fuchsia",
534        target_os = "illumos",
535        target_os = "ios",
536        target_os = "visionos",
537        target_os = "linux",
538        target_os = "macos",
539        target_os = "netbsd",
540        target_os = "tvos",
541        target_os = "watchos",
542        target_os = "windows",
543        target_os = "cygwin",
544        target_os = "nuttx",
545        all(target_os = "wasi", not(target_env = "p1")),
546    ))]
547    pub const fn with_interval(self, interval: Duration) -> Self {
548        Self {
549            interval: Some(interval),
550            ..self
551        }
552    }
553
554    /// Set the value of the `TCP_KEEPCNT` option.
555    ///
556    /// Set the maximum number of TCP keepalive probes that will be sent before
557    /// dropping a connection, if TCP keepalive is enabled on this socket.
558    #[cfg(all(
559        feature = "all",
560        any(
561            target_os = "android",
562            target_os = "dragonfly",
563            target_os = "emscripten",
564            target_os = "freebsd",
565            target_os = "fuchsia",
566            target_os = "illumos",
567            target_os = "ios",
568            target_os = "visionos",
569            target_os = "linux",
570            target_os = "macos",
571            target_os = "netbsd",
572            target_os = "tvos",
573            target_os = "watchos",
574            target_os = "cygwin",
575            target_os = "windows",
576            target_os = "nuttx",
577            all(target_os = "wasi", not(target_env = "p1")),
578        )
579    ))]
580    pub const fn with_retries(self, retries: u32) -> Self {
581        Self {
582            retries: Some(retries),
583            ..self
584        }
585    }
586}
587
588/// Configuration of a `sendmsg(2)` system call.
589///
590/// This wraps `msghdr` on Unix and `WSAMSG` on Windows. Also see [`MsgHdrMut`]
591/// for the variant used by `recvmsg(2)`.
592#[cfg(not(any(target_os = "redox", target_os = "wasi", target_os = "horizon")))]
593#[repr(transparent)]
594pub struct MsgHdr<'addr, 'bufs, 'control> {
595    inner: sys::msghdr,
596    #[allow(clippy::type_complexity)]
597    _lifetimes: PhantomData<(&'addr SockAddr, &'bufs IoSlice<'bufs>, &'control [u8])>,
598}
599
600#[cfg(not(any(target_os = "redox", target_os = "wasi", target_os = "horizon")))]
601impl<'addr, 'bufs, 'control> MsgHdr<'addr, 'bufs, 'control> {
602    /// Create a new `MsgHdr` with all empty/zero fields.
603    #[allow(clippy::new_without_default)]
604    pub fn new() -> MsgHdr<'addr, 'bufs, 'control> {
605        // SAFETY: all zero is valid for `msghdr` and `WSAMSG`.
606        MsgHdr {
607            inner: unsafe { mem::zeroed() },
608            _lifetimes: PhantomData,
609        }
610    }
611
612    /// Set the address (name) of the message.
613    ///
614    /// Corresponds to setting `msg_name` and `msg_namelen` on Unix and `name`
615    /// and `namelen` on Windows.
616    pub fn with_addr(mut self, addr: &'addr SockAddr) -> Self {
617        sys::set_msghdr_name(&mut self.inner, addr);
618        self
619    }
620
621    /// Set the buffer(s) of the message.
622    ///
623    /// Corresponds to setting `msg_iov` and `msg_iovlen` on Unix and `lpBuffers`
624    /// and `dwBufferCount` on Windows.
625    pub fn with_buffers(mut self, bufs: &'bufs [IoSlice<'_>]) -> Self {
626        let ptr = bufs.as_ptr() as *mut _;
627        sys::set_msghdr_iov(&mut self.inner, ptr, bufs.len());
628        self
629    }
630
631    /// Set the control buffer of the message.
632    ///
633    /// Corresponds to setting `msg_control` and `msg_controllen` on Unix and
634    /// `Control` on Windows.
635    pub fn with_control(mut self, buf: &'control [u8]) -> Self {
636        let ptr = buf.as_ptr() as *mut _;
637        sys::set_msghdr_control(&mut self.inner, ptr, buf.len());
638        self
639    }
640
641    /// Set the flags of the message.
642    ///
643    /// Corresponds to setting `msg_flags` on Unix and `dwFlags` on Windows.
644    pub fn with_flags(mut self, flags: sys::c_int) -> Self {
645        sys::set_msghdr_flags(&mut self.inner, flags);
646        self
647    }
648}
649
650#[cfg(not(any(target_os = "redox", target_os = "wasi", target_os = "horizon")))]
651impl<'name, 'bufs, 'control> fmt::Debug for MsgHdr<'name, 'bufs, 'control> {
652    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
653        "MsgHdr".fmt(fmt)
654    }
655}
656
657#[cfg(not(any(target_os = "redox", target_os = "wasi", target_os = "horizon")))]
658unsafe impl Send for MsgHdr<'_, '_, '_> {}
659
660/// Configuration of a `recvmsg(2)` system call.
661///
662/// This wraps `msghdr` on Unix and `WSAMSG` on Windows. Also see [`MsgHdr`] for
663/// the variant used by `sendmsg(2)`.
664#[cfg(not(any(target_os = "redox", target_os = "wasi", target_os = "horizon")))]
665#[repr(transparent)]
666pub struct MsgHdrMut<'addr, 'bufs, 'control> {
667    inner: sys::msghdr,
668    #[allow(clippy::type_complexity)]
669    _lifetimes: PhantomData<(
670        &'addr mut SockAddr,
671        &'bufs mut MaybeUninitSlice<'bufs>,
672        &'control mut [u8],
673    )>,
674}
675
676#[cfg(not(any(target_os = "redox", target_os = "wasi", target_os = "horizon")))]
677impl<'addr, 'bufs, 'control> MsgHdrMut<'addr, 'bufs, 'control> {
678    /// Create a new `MsgHdrMut` with all empty/zero fields.
679    #[allow(clippy::new_without_default)]
680    pub fn new() -> MsgHdrMut<'addr, 'bufs, 'control> {
681        // SAFETY: all zero is valid for `msghdr` and `WSAMSG`.
682        MsgHdrMut {
683            inner: unsafe { mem::zeroed() },
684            _lifetimes: PhantomData,
685        }
686    }
687
688    /// Set the mutable address (name) of the message.
689    ///
690    /// Corresponds to setting `msg_name` and `msg_namelen` on Unix and `name`
691    /// and `namelen` on Windows.
692    #[allow(clippy::needless_pass_by_ref_mut)]
693    pub fn with_addr(mut self, addr: &'addr mut SockAddr) -> Self {
694        sys::set_msghdr_name(&mut self.inner, addr);
695        self
696    }
697
698    /// Set the mutable buffer(s) of the message.
699    ///
700    /// Corresponds to setting `msg_iov` and `msg_iovlen` on Unix and `lpBuffers`
701    /// and `dwBufferCount` on Windows.
702    pub fn with_buffers(mut self, bufs: &'bufs mut [MaybeUninitSlice<'_>]) -> Self {
703        sys::set_msghdr_iov(&mut self.inner, bufs.as_mut_ptr().cast(), bufs.len());
704        self
705    }
706
707    /// Set the mutable control buffer of the message.
708    ///
709    /// Corresponds to setting `msg_control` and `msg_controllen` on Unix and
710    /// `Control` on Windows.
711    pub fn with_control(mut self, buf: &'control mut [MaybeUninit<u8>]) -> Self {
712        sys::set_msghdr_control(&mut self.inner, buf.as_mut_ptr().cast(), buf.len());
713        self
714    }
715
716    /// Returns the flags of the message.
717    pub fn flags(&self) -> RecvFlags {
718        sys::msghdr_flags(&self.inner)
719    }
720
721    /// Gets the length of the control buffer.
722    ///
723    /// Can be used to determine how much, if any, of the control buffer was filled by `recvmsg`.
724    ///
725    /// Corresponds to `msg_controllen` on Unix and `Control.len` on Windows.
726    pub fn control_len(&self) -> usize {
727        sys::msghdr_control_len(&self.inner)
728    }
729}
730
731#[cfg(not(any(target_os = "redox", target_os = "wasi", target_os = "horizon")))]
732impl<'name, 'bufs, 'control> fmt::Debug for MsgHdrMut<'name, 'bufs, 'control> {
733    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
734        "MsgHdrMut".fmt(fmt)
735    }
736}
737
738#[cfg(not(any(target_os = "redox", target_os = "wasi", target_os = "horizon")))]
739unsafe impl Send for MsgHdrMut<'_, '_, '_> {}