1 | use crate::ffi::OsStr; |
2 | #[cfg (any(doc, target_os = "android" , target_os = "linux" ))] |
3 | use crate::os::net::linux_ext; |
4 | use crate::os::unix::ffi::OsStrExt; |
5 | use crate::path::Path; |
6 | use crate::sealed::Sealed; |
7 | use crate::sys::cvt; |
8 | use crate::{fmt, io, mem, ptr}; |
9 | |
10 | // FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here? |
11 | #[cfg (not(unix))] |
12 | #[allow (non_camel_case_types)] |
13 | mod libc { |
14 | pub use core::ffi::c_int; |
15 | pub type socklen_t = u32; |
16 | pub struct sockaddr; |
17 | #[derive (Clone)] |
18 | pub struct sockaddr_un { |
19 | pub sun_path: [u8; 1], |
20 | } |
21 | } |
22 | |
23 | const SUN_PATH_OFFSET: usize = mem::offset_of!(libc::sockaddr_un, sun_path); |
24 | |
25 | pub(super) fn sockaddr_un(path: &Path) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> { |
26 | // SAFETY: All zeros is a valid representation for `sockaddr_un`. |
27 | let mut addr: libc::sockaddr_un = unsafe { mem::zeroed() }; |
28 | addr.sun_family = libc::AF_UNIX as libc::sa_family_t; |
29 | |
30 | let bytes = path.as_os_str().as_bytes(); |
31 | |
32 | if bytes.contains(&0) { |
33 | return Err(io::const_error!( |
34 | io::ErrorKind::InvalidInput, |
35 | "paths must not contain interior null bytes" , |
36 | )); |
37 | } |
38 | |
39 | if bytes.len() >= addr.sun_path.len() { |
40 | return Err(io::const_error!( |
41 | io::ErrorKind::InvalidInput, |
42 | "path must be shorter than SUN_LEN" , |
43 | )); |
44 | } |
45 | // SAFETY: `bytes` and `addr.sun_path` are not overlapping and |
46 | // both point to valid memory. |
47 | // NOTE: We zeroed the memory above, so the path is already null |
48 | // terminated. |
49 | unsafe { |
50 | ptr::copy_nonoverlapping(bytes.as_ptr(), addr.sun_path.as_mut_ptr().cast(), bytes.len()) |
51 | }; |
52 | |
53 | let mut len = SUN_PATH_OFFSET + bytes.len(); |
54 | match bytes.get(0) { |
55 | Some(&0) | None => {} |
56 | Some(_) => len += 1, |
57 | } |
58 | Ok((addr, len as libc::socklen_t)) |
59 | } |
60 | |
61 | enum AddressKind<'a> { |
62 | Unnamed, |
63 | Pathname(&'a Path), |
64 | Abstract(&'a [u8]), |
65 | } |
66 | |
67 | /// An address associated with a Unix socket. |
68 | /// |
69 | /// # Examples |
70 | /// |
71 | /// ``` |
72 | /// use std::os::unix::net::UnixListener; |
73 | /// |
74 | /// let socket = match UnixListener::bind("/tmp/sock" ) { |
75 | /// Ok(sock) => sock, |
76 | /// Err(e) => { |
77 | /// println!("Couldn't bind: {e:?}" ); |
78 | /// return |
79 | /// } |
80 | /// }; |
81 | /// let addr = socket.local_addr().expect("Couldn't get local address" ); |
82 | /// ``` |
83 | #[derive (Clone)] |
84 | #[stable (feature = "unix_socket" , since = "1.10.0" )] |
85 | pub struct SocketAddr { |
86 | pub(super) addr: libc::sockaddr_un, |
87 | pub(super) len: libc::socklen_t, |
88 | } |
89 | |
90 | impl SocketAddr { |
91 | pub(super) fn new<F>(f: F) -> io::Result<SocketAddr> |
92 | where |
93 | F: FnOnce(*mut libc::sockaddr, *mut libc::socklen_t) -> libc::c_int, |
94 | { |
95 | unsafe { |
96 | let mut addr: libc::sockaddr_un = mem::zeroed(); |
97 | let mut len = size_of::<libc::sockaddr_un>() as libc::socklen_t; |
98 | cvt(f((&raw mut addr) as *mut _, &mut len))?; |
99 | SocketAddr::from_parts(addr, len) |
100 | } |
101 | } |
102 | |
103 | pub(super) fn from_parts( |
104 | addr: libc::sockaddr_un, |
105 | mut len: libc::socklen_t, |
106 | ) -> io::Result<SocketAddr> { |
107 | if cfg!(target_os = "openbsd" ) { |
108 | // on OpenBSD, getsockname(2) returns the actual size of the socket address, |
109 | // and not the len of the content. Figure out the length for ourselves. |
110 | // https://marc.info/?l=openbsd-bugs&m=170105481926736&w=2 |
111 | let sun_path: &[u8] = |
112 | unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&addr.sun_path) }; |
113 | len = core::slice::memchr::memchr(0, sun_path) |
114 | .map_or(len, |new_len| (new_len + SUN_PATH_OFFSET) as libc::socklen_t); |
115 | } |
116 | |
117 | if len == 0 { |
118 | // When there is a datagram from unnamed unix socket |
119 | // linux returns zero bytes of address |
120 | len = SUN_PATH_OFFSET as libc::socklen_t; // i.e., zero-length address |
121 | } else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t { |
122 | return Err(io::const_error!( |
123 | io::ErrorKind::InvalidInput, |
124 | "file descriptor did not correspond to a Unix socket" , |
125 | )); |
126 | } |
127 | |
128 | Ok(SocketAddr { addr, len }) |
129 | } |
130 | |
131 | /// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path. |
132 | /// |
133 | /// # Errors |
134 | /// |
135 | /// Returns an error if the path is longer than `SUN_LEN` or if it contains |
136 | /// NULL bytes. |
137 | /// |
138 | /// # Examples |
139 | /// |
140 | /// ``` |
141 | /// use std::os::unix::net::SocketAddr; |
142 | /// use std::path::Path; |
143 | /// |
144 | /// # fn main() -> std::io::Result<()> { |
145 | /// let address = SocketAddr::from_pathname("/path/to/socket" )?; |
146 | /// assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket" ))); |
147 | /// # Ok(()) |
148 | /// # } |
149 | /// ``` |
150 | /// |
151 | /// Creating a `SocketAddr` with a NULL byte results in an error. |
152 | /// |
153 | /// ``` |
154 | /// use std::os::unix::net::SocketAddr; |
155 | /// |
156 | /// assert!(SocketAddr::from_pathname("/path/with/ \0/bytes" ).is_err()); |
157 | /// ``` |
158 | #[stable (feature = "unix_socket_creation" , since = "1.61.0" )] |
159 | pub fn from_pathname<P>(path: P) -> io::Result<SocketAddr> |
160 | where |
161 | P: AsRef<Path>, |
162 | { |
163 | sockaddr_un(path.as_ref()).map(|(addr, len)| SocketAddr { addr, len }) |
164 | } |
165 | |
166 | /// Returns `true` if the address is unnamed. |
167 | /// |
168 | /// # Examples |
169 | /// |
170 | /// A named address: |
171 | /// |
172 | /// ```no_run |
173 | /// use std::os::unix::net::UnixListener; |
174 | /// |
175 | /// fn main() -> std::io::Result<()> { |
176 | /// let socket = UnixListener::bind("/tmp/sock" )?; |
177 | /// let addr = socket.local_addr().expect("Couldn't get local address" ); |
178 | /// assert_eq!(addr.is_unnamed(), false); |
179 | /// Ok(()) |
180 | /// } |
181 | /// ``` |
182 | /// |
183 | /// An unnamed address: |
184 | /// |
185 | /// ``` |
186 | /// use std::os::unix::net::UnixDatagram; |
187 | /// |
188 | /// fn main() -> std::io::Result<()> { |
189 | /// let socket = UnixDatagram::unbound()?; |
190 | /// let addr = socket.local_addr().expect("Couldn't get local address" ); |
191 | /// assert_eq!(addr.is_unnamed(), true); |
192 | /// Ok(()) |
193 | /// } |
194 | /// ``` |
195 | #[must_use ] |
196 | #[stable (feature = "unix_socket" , since = "1.10.0" )] |
197 | pub fn is_unnamed(&self) -> bool { |
198 | matches!(self.address(), AddressKind::Unnamed) |
199 | } |
200 | |
201 | /// Returns the contents of this address if it is a `pathname` address. |
202 | /// |
203 | /// # Examples |
204 | /// |
205 | /// With a pathname: |
206 | /// |
207 | /// ```no_run |
208 | /// use std::os::unix::net::UnixListener; |
209 | /// use std::path::Path; |
210 | /// |
211 | /// fn main() -> std::io::Result<()> { |
212 | /// let socket = UnixListener::bind("/tmp/sock" )?; |
213 | /// let addr = socket.local_addr().expect("Couldn't get local address" ); |
214 | /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock" ))); |
215 | /// Ok(()) |
216 | /// } |
217 | /// ``` |
218 | /// |
219 | /// Without a pathname: |
220 | /// |
221 | /// ``` |
222 | /// use std::os::unix::net::UnixDatagram; |
223 | /// |
224 | /// fn main() -> std::io::Result<()> { |
225 | /// let socket = UnixDatagram::unbound()?; |
226 | /// let addr = socket.local_addr().expect("Couldn't get local address" ); |
227 | /// assert_eq!(addr.as_pathname(), None); |
228 | /// Ok(()) |
229 | /// } |
230 | /// ``` |
231 | #[stable (feature = "unix_socket" , since = "1.10.0" )] |
232 | #[must_use ] |
233 | pub fn as_pathname(&self) -> Option<&Path> { |
234 | if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None } |
235 | } |
236 | |
237 | fn address(&self) -> AddressKind<'_> { |
238 | let len = self.len as usize - SUN_PATH_OFFSET; |
239 | let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) }; |
240 | |
241 | // macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses |
242 | if len == 0 |
243 | || (cfg!(not(any(target_os = "linux" , target_os = "android" ))) |
244 | && self.addr.sun_path[0] == 0) |
245 | { |
246 | AddressKind::Unnamed |
247 | } else if self.addr.sun_path[0] == 0 { |
248 | AddressKind::Abstract(&path[1..len]) |
249 | } else { |
250 | AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref()) |
251 | } |
252 | } |
253 | } |
254 | |
255 | #[stable (feature = "unix_socket_abstract" , since = "1.70.0" )] |
256 | impl Sealed for SocketAddr {} |
257 | |
258 | #[doc (cfg(any(target_os = "android" , target_os = "linux" )))] |
259 | #[cfg (any(doc, target_os = "android" , target_os = "linux" ))] |
260 | #[stable (feature = "unix_socket_abstract" , since = "1.70.0" )] |
261 | impl linux_ext::addr::SocketAddrExt for SocketAddr { |
262 | fn as_abstract_name(&self) -> Option<&[u8]> { |
263 | if let AddressKind::Abstract(name) = self.address() { Some(name) } else { None } |
264 | } |
265 | |
266 | fn from_abstract_name<N>(name: N) -> crate::io::Result<Self> |
267 | where |
268 | N: AsRef<[u8]>, |
269 | { |
270 | let name = name.as_ref(); |
271 | unsafe { |
272 | let mut addr: libc::sockaddr_un = mem::zeroed(); |
273 | addr.sun_family = libc::AF_UNIX as libc::sa_family_t; |
274 | |
275 | if name.len() + 1 > addr.sun_path.len() { |
276 | return Err(io::const_error!( |
277 | io::ErrorKind::InvalidInput, |
278 | "abstract socket name must be shorter than SUN_LEN" , |
279 | )); |
280 | } |
281 | |
282 | crate::ptr::copy_nonoverlapping( |
283 | name.as_ptr(), |
284 | addr.sun_path.as_mut_ptr().add(1) as *mut u8, |
285 | name.len(), |
286 | ); |
287 | let len = (SUN_PATH_OFFSET + 1 + name.len()) as libc::socklen_t; |
288 | SocketAddr::from_parts(addr, len) |
289 | } |
290 | } |
291 | } |
292 | |
293 | #[stable (feature = "unix_socket" , since = "1.10.0" )] |
294 | impl fmt::Debug for SocketAddr { |
295 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
296 | match self.address() { |
297 | AddressKind::Unnamed => write!(fmt, "(unnamed)" ), |
298 | AddressKind::Abstract(name: &[u8]) => write!(fmt, " \"{}\" (abstract)" , name.escape_ascii()), |
299 | AddressKind::Pathname(path: &Path) => write!(fmt, " {path:?} (pathname)" ), |
300 | } |
301 | } |
302 | } |
303 | |