]> git.lizzy.rs Git - rust.git/blob - library/std/src/os/unix/net/addr.rs
Merge commit '4bdfb0741dbcecd5279a2635c3280726db0604b5' into clippyup
[rust.git] / library / std / src / os / unix / net / addr.rs
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 = (addr as *const libc::sockaddr_un).addr();
24     let path = (&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 #[unstable(feature = "unix_socket_abstract", issue = "85410")]
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 #[unstable(feature = "unix_socket_abstract", issue = "85410")]
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) => write!(fmt, "\"{}\" (abstract)", name.escape_ascii()),
292             AddressKind::Pathname(path) => write!(fmt, "{path:?} (pathname)"),
293         }
294     }
295 }