]> git.proxmox.com Git - rustc.git/blob - vendor/rustix-0.37.22/src/backend/libc/net/read_sockaddr.rs
New upstream version 1.73.0+dfsg1
[rustc.git] / vendor / rustix-0.37.22 / src / backend / libc / net / read_sockaddr.rs
1 use super::super::c;
2 #[cfg(unix)]
3 use super::addr::SocketAddrUnix;
4 use super::ext::{in6_addr_s6_addr, in_addr_s_addr, sockaddr_in6_sin6_scope_id};
5 #[cfg(not(windows))]
6 use crate::ffi::CStr;
7 use crate::io;
8 use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddrAny, SocketAddrV4, SocketAddrV6};
9 use core::mem::size_of;
10
11 // This must match the header of `sockaddr`.
12 #[repr(C)]
13 struct sockaddr_header {
14 #[cfg(any(bsd, target_os = "haiku"))]
15 sa_len: u8,
16 #[cfg(any(bsd, target_os = "haiku"))]
17 ss_family: u8,
18 #[cfg(not(any(bsd, target_os = "haiku")))]
19 ss_family: u16,
20 }
21
22 #[inline]
23 unsafe fn read_ss_family(storage: *const c::sockaddr_storage) -> u16 {
24 // Assert that we know the layout of `sockaddr`.
25 let _ = c::sockaddr {
26 #[cfg(any(bsd, target_os = "haiku"))]
27 sa_len: 0_u8,
28 #[cfg(any(bsd, target_os = "haiku"))]
29 sa_family: 0_u8,
30 #[cfg(not(any(bsd, target_os = "haiku")))]
31 sa_family: 0_u16,
32 #[cfg(not(target_os = "haiku"))]
33 sa_data: [0; 14],
34 #[cfg(target_os = "haiku")]
35 sa_data: [0; 30],
36 };
37
38 (*storage.cast::<sockaddr_header>()).ss_family.into()
39 }
40
41 /// Set the `ss_family` field of a socket address to `AF_UNSPEC`, so that we
42 /// can test for `AF_UNSPEC` to test whether it was stored to.
43 pub(crate) unsafe fn initialize_family_to_unspec(storage: *mut c::sockaddr_storage) {
44 (*storage.cast::<sockaddr_header>()).ss_family = c::AF_UNSPEC as _;
45 }
46
47 pub(crate) unsafe fn read_sockaddr(
48 storage: *const c::sockaddr_storage,
49 len: usize,
50 ) -> io::Result<SocketAddrAny> {
51 #[cfg(unix)]
52 let offsetof_sun_path = super::addr::offsetof_sun_path();
53
54 if len < size_of::<c::sa_family_t>() {
55 return Err(io::Errno::INVAL);
56 }
57 match read_ss_family(storage).into() {
58 c::AF_INET => {
59 if len < size_of::<c::sockaddr_in>() {
60 return Err(io::Errno::INVAL);
61 }
62 let decode = &*storage.cast::<c::sockaddr_in>();
63 Ok(SocketAddrAny::V4(SocketAddrV4::new(
64 Ipv4Addr::from(u32::from_be(in_addr_s_addr(decode.sin_addr))),
65 u16::from_be(decode.sin_port),
66 )))
67 }
68 c::AF_INET6 => {
69 if len < size_of::<c::sockaddr_in6>() {
70 return Err(io::Errno::INVAL);
71 }
72 let decode = &*storage.cast::<c::sockaddr_in6>();
73 #[cfg(not(windows))]
74 let s6_addr = decode.sin6_addr.s6_addr;
75 #[cfg(windows)]
76 let s6_addr = decode.sin6_addr.u.Byte;
77 #[cfg(not(windows))]
78 let sin6_scope_id = decode.sin6_scope_id;
79 #[cfg(windows)]
80 let sin6_scope_id = decode.Anonymous.sin6_scope_id;
81 Ok(SocketAddrAny::V6(SocketAddrV6::new(
82 Ipv6Addr::from(s6_addr),
83 u16::from_be(decode.sin6_port),
84 u32::from_be(decode.sin6_flowinfo),
85 sin6_scope_id,
86 )))
87 }
88 #[cfg(unix)]
89 c::AF_UNIX => {
90 if len < offsetof_sun_path {
91 return Err(io::Errno::INVAL);
92 }
93 if len == offsetof_sun_path {
94 SocketAddrUnix::new(&[][..]).map(SocketAddrAny::Unix)
95 } else {
96 let decode = &*storage.cast::<c::sockaddr_un>();
97
98 // On Linux check for Linux's [abstract namespace].
99 //
100 // [abstract namespace]: https://man7.org/linux/man-pages/man7/unix.7.html
101 #[cfg(linux_kernel)]
102 if decode.sun_path[0] == 0 {
103 return SocketAddrUnix::new_abstract_name(core::mem::transmute::<
104 &[c::c_char],
105 &[u8],
106 >(
107 &decode.sun_path[1..len - offsetof_sun_path],
108 ))
109 .map(SocketAddrAny::Unix);
110 }
111
112 // Otherwise we expect a NUL-terminated filesystem path.
113
114 // Trim off unused bytes from the end of `path_bytes`.
115 let path_bytes = if cfg!(target_os = "freebsd") {
116 // FreeBSD sometimes sets the length to longer than the length
117 // of the NUL-terminated string. Find the NUL and truncate the
118 // string accordingly.
119 &decode.sun_path[..decode
120 .sun_path
121 .iter()
122 .position(|b| *b == 0)
123 .ok_or(io::Errno::INVAL)?]
124 } else {
125 // Otherwise, use the provided length.
126 let provided_len = len - 1 - offsetof_sun_path;
127 if decode.sun_path[provided_len] != 0 {
128 return Err(io::Errno::INVAL);
129 }
130 debug_assert_eq!(
131 CStr::from_ptr(decode.sun_path.as_ptr()).to_bytes().len(),
132 provided_len
133 );
134 &decode.sun_path[..provided_len]
135 };
136
137 SocketAddrUnix::new(core::mem::transmute::<&[c::c_char], &[u8]>(path_bytes))
138 .map(SocketAddrAny::Unix)
139 }
140 }
141 _ => Err(io::Errno::INVAL),
142 }
143 }
144
145 pub(crate) unsafe fn maybe_read_sockaddr_os(
146 storage: *const c::sockaddr_storage,
147 len: usize,
148 ) -> Option<SocketAddrAny> {
149 if len == 0 {
150 return None;
151 }
152
153 assert!(len >= size_of::<c::sa_family_t>());
154 let family = read_ss_family(storage).into();
155 if family == c::AF_UNSPEC {
156 None
157 } else {
158 Some(inner_read_sockaddr_os(family, storage, len))
159 }
160 }
161
162 pub(crate) unsafe fn read_sockaddr_os(
163 storage: *const c::sockaddr_storage,
164 len: usize,
165 ) -> SocketAddrAny {
166 assert!(len >= size_of::<c::sa_family_t>());
167 let family = read_ss_family(storage).into();
168 inner_read_sockaddr_os(family, storage, len)
169 }
170
171 unsafe fn inner_read_sockaddr_os(
172 family: c::c_int,
173 storage: *const c::sockaddr_storage,
174 len: usize,
175 ) -> SocketAddrAny {
176 #[cfg(unix)]
177 let offsetof_sun_path = super::addr::offsetof_sun_path();
178
179 assert!(len >= size_of::<c::sa_family_t>());
180 match family {
181 c::AF_INET => {
182 assert!(len >= size_of::<c::sockaddr_in>());
183 let decode = &*storage.cast::<c::sockaddr_in>();
184 SocketAddrAny::V4(SocketAddrV4::new(
185 Ipv4Addr::from(u32::from_be(in_addr_s_addr(decode.sin_addr))),
186 u16::from_be(decode.sin_port),
187 ))
188 }
189 c::AF_INET6 => {
190 assert!(len >= size_of::<c::sockaddr_in6>());
191 let decode = &*storage.cast::<c::sockaddr_in6>();
192 SocketAddrAny::V6(SocketAddrV6::new(
193 Ipv6Addr::from(in6_addr_s6_addr(decode.sin6_addr)),
194 u16::from_be(decode.sin6_port),
195 u32::from_be(decode.sin6_flowinfo),
196 sockaddr_in6_sin6_scope_id(decode),
197 ))
198 }
199 #[cfg(unix)]
200 c::AF_UNIX => {
201 assert!(len >= offsetof_sun_path);
202 if len == offsetof_sun_path {
203 SocketAddrAny::Unix(SocketAddrUnix::new(&[][..]).unwrap())
204 } else {
205 let decode = &*storage.cast::<c::sockaddr_un>();
206
207 // On Linux check for Linux's [abstract namespace].
208 //
209 // [abstract namespace]: https://man7.org/linux/man-pages/man7/unix.7.html
210 #[cfg(linux_kernel)]
211 if decode.sun_path[0] == 0 {
212 return SocketAddrAny::Unix(
213 SocketAddrUnix::new_abstract_name(core::mem::transmute::<
214 &[c::c_char],
215 &[u8],
216 >(
217 &decode.sun_path[1..len - offsetof_sun_path],
218 ))
219 .unwrap(),
220 );
221 }
222
223 // Otherwise we expect a NUL-terminated filesystem path.
224 assert_eq!(decode.sun_path[len - 1 - offsetof_sun_path], 0);
225 let path_bytes = &decode.sun_path[..len - 1 - offsetof_sun_path];
226
227 // FreeBSD sometimes sets the length to longer than the length
228 // of the NUL-terminated string. Find the NUL and truncate the
229 // string accordingly.
230 #[cfg(target_os = "freebsd")]
231 let path_bytes = &path_bytes[..path_bytes.iter().position(|b| *b == 0).unwrap()];
232
233 SocketAddrAny::Unix(
234 SocketAddrUnix::new(core::mem::transmute::<&[c::c_char], &[u8]>(path_bytes))
235 .unwrap(),
236 )
237 }
238 }
239 other => unimplemented!("{:?}", other),
240 }
241 }