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