rustix/backend/libc/net/
msghdr.rs

1//! Utilities for dealing with message headers.
2//!
3//! These take closures rather than returning a `c::msghdr` directly because
4//! the message headers may reference stack-local data.
5
6use crate::backend::c;
7
8use crate::io::{self, IoSlice, IoSliceMut};
9use crate::net::addr::SocketAddrArg;
10use crate::net::{RecvAncillaryBuffer, SendAncillaryBuffer, SocketAddrBuf};
11
12use core::mem::zeroed;
13
14/// Convert the value to the `msg_iovlen` field of a `msghdr` struct.
15#[cfg(all(
16    not(any(windows, target_os = "espidf", target_os = "wasi")),
17    any(
18        target_os = "android",
19        target_os = "redox",
20        all(
21            target_os = "linux",
22            not(target_env = "musl"),
23            not(all(target_env = "uclibc", any(target_arch = "arm", target_arch = "mips")))
24        )
25    )
26))]
27#[inline]
28fn msg_iov_len(len: usize) -> c::size_t {
29    len
30}
31
32/// Convert the value to the `msg_iovlen` field of a `msghdr` struct.
33#[cfg(all(
34    not(any(windows, target_os = "espidf", target_os = "vita", target_os = "wasi")),
35    not(any(
36        target_os = "android",
37        target_os = "redox",
38        all(
39            target_os = "linux",
40            not(target_env = "musl"),
41            not(all(target_env = "uclibc", any(target_arch = "arm", target_arch = "mips")))
42        )
43    ))
44))]
45#[inline]
46fn msg_iov_len(len: usize) -> c::c_int {
47    len.try_into().unwrap_or(c::c_int::MAX)
48}
49
50/// Convert the value to a `socklen_t`.
51#[cfg(any(
52    bsd,
53    solarish,
54    target_env = "musl",
55    target_os = "aix",
56    target_os = "cygwin",
57    target_os = "emscripten",
58    target_os = "fuchsia",
59    target_os = "haiku",
60    target_os = "hurd",
61    target_os = "nto",
62))]
63#[inline]
64fn msg_control_len(len: usize) -> c::socklen_t {
65    len.try_into().unwrap_or(c::socklen_t::MAX)
66}
67
68/// Convert the value to a `size_t`.
69#[cfg(not(any(
70    bsd,
71    solarish,
72    windows,
73    target_env = "musl",
74    target_os = "aix",
75    target_os = "cygwin",
76    target_os = "emscripten",
77    target_os = "espidf",
78    target_os = "fuchsia",
79    target_os = "haiku",
80    target_os = "hurd",
81    target_os = "nto",
82    target_os = "vita",
83    target_os = "wasi",
84)))]
85#[inline]
86fn msg_control_len(len: usize) -> c::size_t {
87    len
88}
89
90/// Create a message header intended to receive a datagram.
91///
92/// # Safety
93///
94/// If `f` dereferences the pointers in the `msghdr`, it must do so only within
95/// the bounds indicated by the associated lengths in the `msghdr`.
96///
97/// And, if `f` returns `Ok`, it must have updated the `msg_controllen` field
98/// of the `msghdr` to indicate how many bytes it initialized.
99pub(crate) unsafe fn with_recv_msghdr<R>(
100    name: &mut SocketAddrBuf,
101    iov: &mut [IoSliceMut<'_>],
102    control: &mut RecvAncillaryBuffer<'_>,
103    f: impl FnOnce(&mut c::msghdr) -> io::Result<R>,
104) -> io::Result<R> {
105    control.clear();
106
107    let mut msghdr = zero_msghdr();
108    msghdr.msg_name = name.storage.as_mut_ptr().cast();
109    msghdr.msg_namelen = name.len;
110    msghdr.msg_iov = iov.as_mut_ptr().cast();
111    msghdr.msg_iovlen = msg_iov_len(iov.len());
112    msghdr.msg_control = control.as_control_ptr().cast();
113    msghdr.msg_controllen = msg_control_len(control.control_len());
114
115    let res = f(&mut msghdr);
116
117    // Reset the control length.
118    if res.is_ok() {
119        // SAFETY: `f` returned `Ok`, so our safety condition requires `f` to
120        // have initialized `msg_controllen` bytes.
121        control.set_control_len(msghdr.msg_controllen as usize);
122    }
123
124    name.len = msghdr.msg_namelen;
125
126    res
127}
128
129/// Create a message header intended to send without an address.
130///
131/// The returned `msghdr` will contain raw pointers to the memory
132/// referenced by `iov` and `control`.
133pub(crate) fn noaddr_msghdr(
134    iov: &[IoSlice<'_>],
135    control: &mut SendAncillaryBuffer<'_, '_, '_>,
136) -> c::msghdr {
137    let mut h = zero_msghdr();
138    h.msg_iov = iov.as_ptr() as _;
139    h.msg_iovlen = msg_iov_len(iov.len());
140    h.msg_control = control.as_control_ptr().cast();
141    h.msg_controllen = msg_control_len(control.control_len());
142    h
143}
144
145/// Create a message header intended to send with the specified address.
146///
147/// This creates a `c::msghdr` and calls a function `f` on it. The `msghdr`'s
148/// raw pointers may point to temporaries, so this function should avoid
149/// storing the pointers anywhere that would outlive the function call.
150///
151/// # Safety
152///
153/// If `f` dereferences the pointers in the `msghdr`, it must do so only within
154/// the bounds indicated by the associated lengths in the `msghdr`.
155pub(crate) unsafe fn with_msghdr<R>(
156    addr: &impl SocketAddrArg,
157    iov: &[IoSlice<'_>],
158    control: &mut SendAncillaryBuffer<'_, '_, '_>,
159    f: impl FnOnce(&c::msghdr) -> R,
160) -> R {
161    addr.with_sockaddr(|addr_ptr, addr_len| {
162        let mut h = zero_msghdr();
163        h.msg_name = addr_ptr as *mut _;
164        h.msg_namelen = bitcast!(addr_len);
165        h.msg_iov = iov.as_ptr() as _;
166        h.msg_iovlen = msg_iov_len(iov.len());
167        h.msg_control = control.as_control_ptr().cast();
168        h.msg_controllen = msg_control_len(control.control_len());
169        // Pass a reference to the `c::msghdr` instead of passing it by value
170        // because it may contain pointers to temporary objects that won't
171        // live beyond the call to `with_sockaddr`.
172        f(&h)
173    })
174}
175
176/// Create a zero-initialized message header struct value.
177#[cfg(unix)]
178pub(crate) fn zero_msghdr() -> c::msghdr {
179    // SAFETY: We can't initialize all the fields by value because on some
180    // platforms the `msghdr` struct in the libc crate contains private padding
181    // fields. But it is still a C type that's meant to be zero-initializable.
182    unsafe { zeroed() }
183}