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 | |
8 | use crate::backend::c; |
9 | #[cfg (target_os = "linux" )] |
10 | use crate::backend::net::write_sockaddr::encode_sockaddr_xdp; |
11 | use crate::backend::net::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6}; |
12 | |
13 | use crate::io::{self, IoSlice, IoSliceMut}; |
14 | #[cfg (target_os = "linux" )] |
15 | use crate::net::xdp::SocketAddrXdp; |
16 | use crate::net::{RecvAncillaryBuffer, SendAncillaryBuffer, SocketAddrV4, SocketAddrV6}; |
17 | use crate::utils::as_ptr; |
18 | |
19 | use core::mem::{size_of, MaybeUninit}; |
20 | use core::ptr::null_mut; |
21 | |
22 | fn msg_iov_len(len: usize) -> c::size_t { |
23 | // This cast cannot overflow. |
24 | len as c::size_t |
25 | } |
26 | |
27 | pub(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. |
33 | pub(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. |
65 | pub(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. |
82 | pub(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. |
102 | pub(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. |
122 | pub(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" )] |
141 | pub(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. |
161 | pub(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 | |