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 libc::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 = (&addr.sun_path as *const libc::c_char).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(&mut addr as *mut _ 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 len == 0 { |
111 | // When there is a datagram from unnamed unix socket |
112 | // linux returns zero bytes of address |
113 | len = sun_path_offset(&addr) as libc::socklen_t; // i.e., zero-length address |
114 | } else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t { |
115 | return Err(io::const_io_error!( |
116 | io::ErrorKind::InvalidInput, |
117 | "file descriptor did not correspond to a Unix socket" , |
118 | )); |
119 | } |
120 | |
121 | Ok(SocketAddr { addr, len }) |
122 | } |
123 | |
124 | /// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path. |
125 | /// |
126 | /// # Errors |
127 | /// |
128 | /// Returns an error if the path is longer than `SUN_LEN` or if it contains |
129 | /// NULL bytes. |
130 | /// |
131 | /// # Examples |
132 | /// |
133 | /// ``` |
134 | /// use std::os::unix::net::SocketAddr; |
135 | /// use std::path::Path; |
136 | /// |
137 | /// # fn main() -> std::io::Result<()> { |
138 | /// let address = SocketAddr::from_pathname("/path/to/socket" )?; |
139 | /// assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket" ))); |
140 | /// # Ok(()) |
141 | /// # } |
142 | /// ``` |
143 | /// |
144 | /// Creating a `SocketAddr` with a NULL byte results in an error. |
145 | /// |
146 | /// ``` |
147 | /// use std::os::unix::net::SocketAddr; |
148 | /// |
149 | /// assert!(SocketAddr::from_pathname("/path/with/ \0/bytes" ).is_err()); |
150 | /// ``` |
151 | #[stable (feature = "unix_socket_creation" , since = "1.61.0" )] |
152 | pub fn from_pathname<P>(path: P) -> io::Result<SocketAddr> |
153 | where |
154 | P: AsRef<Path>, |
155 | { |
156 | sockaddr_un(path.as_ref()).map(|(addr, len)| SocketAddr { addr, len }) |
157 | } |
158 | |
159 | /// Returns `true` if the address is unnamed. |
160 | /// |
161 | /// # Examples |
162 | /// |
163 | /// A named address: |
164 | /// |
165 | /// ```no_run |
166 | /// use std::os::unix::net::UnixListener; |
167 | /// |
168 | /// fn main() -> std::io::Result<()> { |
169 | /// let socket = UnixListener::bind("/tmp/sock" )?; |
170 | /// let addr = socket.local_addr().expect("Couldn't get local address" ); |
171 | /// assert_eq!(addr.is_unnamed(), false); |
172 | /// Ok(()) |
173 | /// } |
174 | /// ``` |
175 | /// |
176 | /// An unnamed address: |
177 | /// |
178 | /// ``` |
179 | /// use std::os::unix::net::UnixDatagram; |
180 | /// |
181 | /// fn main() -> std::io::Result<()> { |
182 | /// let socket = UnixDatagram::unbound()?; |
183 | /// let addr = socket.local_addr().expect("Couldn't get local address" ); |
184 | /// assert_eq!(addr.is_unnamed(), true); |
185 | /// Ok(()) |
186 | /// } |
187 | /// ``` |
188 | #[must_use ] |
189 | #[stable (feature = "unix_socket" , since = "1.10.0" )] |
190 | pub fn is_unnamed(&self) -> bool { |
191 | matches!(self.address(), AddressKind::Unnamed) |
192 | } |
193 | |
194 | /// Returns the contents of this address if it is a `pathname` address. |
195 | /// |
196 | /// # Examples |
197 | /// |
198 | /// With a pathname: |
199 | /// |
200 | /// ```no_run |
201 | /// use std::os::unix::net::UnixListener; |
202 | /// use std::path::Path; |
203 | /// |
204 | /// fn main() -> std::io::Result<()> { |
205 | /// let socket = UnixListener::bind("/tmp/sock" )?; |
206 | /// let addr = socket.local_addr().expect("Couldn't get local address" ); |
207 | /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock" ))); |
208 | /// Ok(()) |
209 | /// } |
210 | /// ``` |
211 | /// |
212 | /// Without a pathname: |
213 | /// |
214 | /// ``` |
215 | /// use std::os::unix::net::UnixDatagram; |
216 | /// |
217 | /// fn main() -> std::io::Result<()> { |
218 | /// let socket = UnixDatagram::unbound()?; |
219 | /// let addr = socket.local_addr().expect("Couldn't get local address" ); |
220 | /// assert_eq!(addr.as_pathname(), None); |
221 | /// Ok(()) |
222 | /// } |
223 | /// ``` |
224 | #[stable (feature = "unix_socket" , since = "1.10.0" )] |
225 | #[must_use ] |
226 | pub fn as_pathname(&self) -> Option<&Path> { |
227 | if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None } |
228 | } |
229 | |
230 | fn address(&self) -> AddressKind<'_> { |
231 | let len = self.len as usize - sun_path_offset(&self.addr); |
232 | let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) }; |
233 | |
234 | // macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses |
235 | if len == 0 |
236 | || (cfg!(not(any(target_os = "linux" , target_os = "android" ))) |
237 | && self.addr.sun_path[0] == 0) |
238 | { |
239 | AddressKind::Unnamed |
240 | } else if self.addr.sun_path[0] == 0 { |
241 | AddressKind::Abstract(&path[1..len]) |
242 | } else { |
243 | AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref()) |
244 | } |
245 | } |
246 | } |
247 | |
248 | #[stable (feature = "unix_socket_abstract" , since = "1.70.0" )] |
249 | impl Sealed for SocketAddr {} |
250 | |
251 | #[doc (cfg(any(target_os = "android" , target_os = "linux" )))] |
252 | #[cfg (any(doc, target_os = "android" , target_os = "linux" ))] |
253 | #[stable (feature = "unix_socket_abstract" , since = "1.70.0" )] |
254 | impl linux_ext::addr::SocketAddrExt for SocketAddr { |
255 | fn as_abstract_name(&self) -> Option<&[u8]> { |
256 | if let AddressKind::Abstract(name) = self.address() { Some(name) } else { None } |
257 | } |
258 | |
259 | fn from_abstract_name<N>(name: N) -> crate::io::Result<Self> |
260 | where |
261 | N: AsRef<[u8]>, |
262 | { |
263 | let name = name.as_ref(); |
264 | unsafe { |
265 | let mut addr: libc::sockaddr_un = mem::zeroed(); |
266 | addr.sun_family = libc::AF_UNIX as libc::sa_family_t; |
267 | |
268 | if name.len() + 1 > addr.sun_path.len() { |
269 | return Err(io::const_io_error!( |
270 | io::ErrorKind::InvalidInput, |
271 | "abstract socket name must be shorter than SUN_LEN" , |
272 | )); |
273 | } |
274 | |
275 | crate::ptr::copy_nonoverlapping( |
276 | name.as_ptr(), |
277 | addr.sun_path.as_mut_ptr().add(1) as *mut u8, |
278 | name.len(), |
279 | ); |
280 | let len = (sun_path_offset(&addr) + 1 + name.len()) as libc::socklen_t; |
281 | SocketAddr::from_parts(addr, len) |
282 | } |
283 | } |
284 | } |
285 | |
286 | #[stable (feature = "unix_socket" , since = "1.10.0" )] |
287 | impl fmt::Debug for SocketAddr { |
288 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
289 | match self.address() { |
290 | AddressKind::Unnamed => write!(fmt, "(unnamed)" ), |
291 | AddressKind::Abstract(name: &[u8]) => write!(fmt, " \"{}\" (abstract)" , name.escape_ascii()), |
292 | AddressKind::Pathname(path: &Path) => write!(fmt, " {path:?} (pathname)" ), |
293 | } |
294 | } |
295 | } |
296 | |