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