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: sockaddr_in = 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: sockaddr_in6 = 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: sockaddr_xdp = 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}
172