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