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;
7use crate::backend::conv::{msg_control_len, msg_iov_len};
8#[cfg(target_os = "linux")]
9use crate::backend::net::write_sockaddr::encode_sockaddr_xdp;
10use crate::backend::net::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6};
11
12use crate::io::{self, IoSlice, IoSliceMut};
13#[cfg(target_os = "linux")]
14use crate::net::xdp::SocketAddrXdp;
15use crate::net::{RecvAncillaryBuffer, SendAncillaryBuffer, SocketAddrV4, SocketAddrV6};
16use crate::utils::as_ptr;
17
18use core::mem::{size_of, zeroed, MaybeUninit};
19
20/// Create a message header intended to receive a datagram.
21pub(crate) fn with_recv_msghdr<R>(
22    name: &mut MaybeUninit<c::sockaddr_storage>,
23    iov: &mut [IoSliceMut<'_>],
24    control: &mut RecvAncillaryBuffer<'_>,
25    f: impl FnOnce(&mut c::msghdr) -> io::Result<R>,
26) -> io::Result<R> {
27    control.clear();
28
29    let namelen = size_of::<c::sockaddr_storage>() as c::socklen_t;
30    let mut msghdr = {
31        let mut h = zero_msghdr();
32        h.msg_name = name.as_mut_ptr().cast();
33        h.msg_namelen = namelen;
34        h.msg_iov = iov.as_mut_ptr().cast();
35        h.msg_iovlen = msg_iov_len(iov.len());
36        h.msg_control = control.as_control_ptr().cast();
37        h.msg_controllen = msg_control_len(control.control_len());
38        h
39    };
40
41    let res = f(&mut msghdr);
42
43    // Reset the control length.
44    if res.is_ok() {
45        unsafe {
46            control.set_control_len(msghdr.msg_controllen.try_into().unwrap_or(usize::MAX));
47        }
48    }
49
50    res
51}
52
53/// Create a message header intended to send without an address.
54pub(crate) fn with_noaddr_msghdr<R>(
55    iov: &[IoSlice<'_>],
56    control: &mut SendAncillaryBuffer<'_, '_, '_>,
57    f: impl FnOnce(c::msghdr) -> R,
58) -> R {
59    f({
60        let mut h = zero_msghdr();
61        h.msg_iov = iov.as_ptr() as _;
62        h.msg_iovlen = msg_iov_len(iov.len());
63        h.msg_control = control.as_control_ptr().cast();
64        h.msg_controllen = msg_control_len(control.control_len());
65        h
66    })
67}
68
69/// Create a message header intended to send with an IPv4 address.
70pub(crate) fn with_v4_msghdr<R>(
71    addr: &SocketAddrV4,
72    iov: &[IoSlice<'_>],
73    control: &mut SendAncillaryBuffer<'_, '_, '_>,
74    f: impl FnOnce(c::msghdr) -> R,
75) -> R {
76    let encoded = encode_sockaddr_v4(addr);
77
78    f({
79        let mut h = zero_msghdr();
80        h.msg_name = as_ptr(&encoded) as _;
81        h.msg_namelen = size_of::<SocketAddrV4>() as _;
82        h.msg_iov = iov.as_ptr() as _;
83        h.msg_iovlen = msg_iov_len(iov.len());
84        h.msg_control = control.as_control_ptr().cast();
85        h.msg_controllen = msg_control_len(control.control_len());
86        h
87    })
88}
89
90/// Create a message header intended to send with an IPv6 address.
91pub(crate) fn with_v6_msghdr<R>(
92    addr: &SocketAddrV6,
93    iov: &[IoSlice<'_>],
94    control: &mut SendAncillaryBuffer<'_, '_, '_>,
95    f: impl FnOnce(c::msghdr) -> R,
96) -> R {
97    let encoded = encode_sockaddr_v6(addr);
98
99    f({
100        let mut h = zero_msghdr();
101        h.msg_name = as_ptr(&encoded) as _;
102        h.msg_namelen = size_of::<SocketAddrV6>() as _;
103        h.msg_iov = iov.as_ptr() as _;
104        h.msg_iovlen = msg_iov_len(iov.len());
105        h.msg_control = control.as_control_ptr().cast();
106        h.msg_controllen = msg_control_len(control.control_len());
107        h
108    })
109}
110
111/// Create a message header intended to send with a Unix address.
112#[cfg(all(unix, not(target_os = "redox")))]
113pub(crate) fn with_unix_msghdr<R>(
114    addr: &crate::net::SocketAddrUnix,
115    iov: &[IoSlice<'_>],
116    control: &mut SendAncillaryBuffer<'_, '_, '_>,
117    f: impl FnOnce(c::msghdr) -> R,
118) -> R {
119    f({
120        let mut h = zero_msghdr();
121        h.msg_name = as_ptr(&addr.unix) as _;
122        h.msg_namelen = addr.addr_len();
123        h.msg_iov = iov.as_ptr() as _;
124        h.msg_iovlen = msg_iov_len(iov.len());
125        h.msg_control = control.as_control_ptr().cast();
126        h.msg_controllen = msg_control_len(control.control_len());
127        h
128    })
129}
130
131/// Create a message header intended to send with an IPv6 address.
132#[cfg(target_os = "linux")]
133pub(crate) fn with_xdp_msghdr<R>(
134    addr: &SocketAddrXdp,
135    iov: &[IoSlice<'_>],
136    control: &mut SendAncillaryBuffer<'_, '_, '_>,
137    f: impl FnOnce(c::msghdr) -> R,
138) -> R {
139    let encoded = encode_sockaddr_xdp(addr);
140
141    f({
142        let mut h = zero_msghdr();
143        h.msg_name = as_ptr(&encoded) as _;
144        h.msg_namelen = size_of::<SocketAddrXdp>() as _;
145        h.msg_iov = iov.as_ptr() as _;
146        h.msg_iovlen = msg_iov_len(iov.len());
147        h.msg_control = control.as_control_ptr().cast();
148        h.msg_controllen = msg_control_len(control.control_len());
149        h
150    })
151}
152
153/// Create a zero-initialized message header struct value.
154#[cfg(all(unix, not(target_os = "redox")))]
155pub(crate) fn zero_msghdr() -> c::msghdr {
156    // SAFETY: We can't initialize all the fields by value because on some
157    // platforms the `msghdr` struct in the libc crate contains private padding
158    // fields. But it is still a C type that's meant to be zero-initializable.
159    unsafe { zeroed() }
160}