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