]> git.proxmox.com Git - rustc.git/blame - library/std/src/os/unix/net/addr.rs
New upstream version 1.61.0+dfsg1
[rustc.git] / library / std / src / os / unix / net / addr.rs
CommitLineData
fc512014
XL
1use crate::ffi::OsStr;
2use crate::os::unix::ffi::OsStrExt;
3use crate::path::Path;
4use crate::sys::cvt;
5099ac24 5use crate::{ascii, fmt, io, mem, ptr};
fc512014
XL
6
7// FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here?
8#[cfg(not(unix))]
9#[allow(non_camel_case_types)]
10mod libc {
11 pub use libc::c_int;
12 pub type socklen_t = u32;
13 pub struct sockaddr;
14 #[derive(Clone)]
15 pub struct sockaddr_un;
16}
17
18fn sun_path_offset(addr: &libc::sockaddr_un) -> usize {
19 // Work with an actual instance of the type since using a null pointer is UB
ee023bcb
FG
20 let base = (addr as *const libc::sockaddr_un).addr();
21 let path = (&addr.sun_path as *const libc::c_char).addr();
fc512014
XL
22 path - base
23}
24
5099ac24
FG
25pub(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() };
fc512014
XL
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) {
5099ac24 33 return Err(io::const_io_error!(
fc512014 34 io::ErrorKind::InvalidInput,
5099ac24 35 "paths must not contain interior null bytes",
fc512014
XL
36 ));
37 }
38
39 if bytes.len() >= addr.sun_path.len() {
5099ac24 40 return Err(io::const_io_error!(
fc512014 41 io::ErrorKind::InvalidInput,
5099ac24 42 "path must be shorter than SUN_LEN",
fc512014
XL
43 ));
44 }
5099ac24
FG
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 };
fc512014
XL
52
53 let mut len = sun_path_offset(&addr) + 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
61enum AddressKind<'a> {
62 Unnamed,
63 Pathname(&'a Path),
64 Abstract(&'a [u8]),
65}
66
67struct AsciiEscaped<'a>(&'a [u8]);
68
69impl<'a> fmt::Display for AsciiEscaped<'a> {
70 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
71 write!(fmt, "\"")?;
72 for byte in self.0.iter().cloned().flat_map(ascii::escape_default) {
73 write!(fmt, "{}", byte as char)?;
74 }
75 write!(fmt, "\"")
76 }
77}
78
79/// An address associated with a Unix socket.
80///
81/// # Examples
82///
83/// ```
84/// use std::os::unix::net::UnixListener;
85///
86/// let socket = match UnixListener::bind("/tmp/sock") {
87/// Ok(sock) => sock,
88/// Err(e) => {
ee023bcb 89/// println!("Couldn't bind: {e:?}");
fc512014
XL
90/// return
91/// }
92/// };
93/// let addr = socket.local_addr().expect("Couldn't get local address");
94/// ```
95#[derive(Clone)]
96#[stable(feature = "unix_socket", since = "1.10.0")]
97pub struct SocketAddr {
c295e0f8
XL
98 pub(super) addr: libc::sockaddr_un,
99 pub(super) len: libc::socklen_t,
fc512014
XL
100}
101
102impl SocketAddr {
103 pub(super) fn new<F>(f: F) -> io::Result<SocketAddr>
104 where
105 F: FnOnce(*mut libc::sockaddr, *mut libc::socklen_t) -> libc::c_int,
106 {
107 unsafe {
108 let mut addr: libc::sockaddr_un = mem::zeroed();
109 let mut len = mem::size_of::<libc::sockaddr_un>() as libc::socklen_t;
110 cvt(f(&mut addr as *mut _ as *mut _, &mut len))?;
111 SocketAddr::from_parts(addr, len)
112 }
113 }
114
115 pub(super) fn from_parts(
116 addr: libc::sockaddr_un,
117 mut len: libc::socklen_t,
118 ) -> io::Result<SocketAddr> {
119 if len == 0 {
120 // When there is a datagram from unnamed unix socket
121 // linux returns zero bytes of address
122 len = sun_path_offset(&addr) as libc::socklen_t; // i.e., zero-length address
123 } else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t {
5099ac24 124 return Err(io::const_io_error!(
fc512014 125 io::ErrorKind::InvalidInput,
5099ac24 126 "file descriptor did not correspond to a Unix socket",
fc512014
XL
127 ));
128 }
129
130 Ok(SocketAddr { addr, len })
131 }
132
5099ac24
FG
133 /// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path.
134 ///
135 /// # Errors
136 ///
137 /// Returns an error if the path is longer than `SUN_LEN` or if it contains
138 /// NULL bytes.
139 ///
140 /// # Examples
141 ///
142 /// ```
5099ac24
FG
143 /// use std::os::unix::net::SocketAddr;
144 /// use std::path::Path;
145 ///
146 /// # fn main() -> std::io::Result<()> {
ee023bcb 147 /// let address = SocketAddr::from_pathname("/path/to/socket")?;
5099ac24
FG
148 /// assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket")));
149 /// # Ok(())
150 /// # }
151 /// ```
152 ///
153 /// Creating a `SocketAddr` with a NULL byte results in an error.
154 ///
155 /// ```
5099ac24
FG
156 /// use std::os::unix::net::SocketAddr;
157 ///
ee023bcb 158 /// assert!(SocketAddr::from_pathname("/path/with/\0/bytes").is_err());
5099ac24 159 /// ```
ee023bcb
FG
160 #[stable(feature = "unix_socket_creation", since = "1.61.0")]
161 pub fn from_pathname<P>(path: P) -> io::Result<SocketAddr>
5099ac24
FG
162 where
163 P: AsRef<Path>,
164 {
165 sockaddr_un(path.as_ref()).map(|(addr, len)| SocketAddr { addr, len })
166 }
167
fc512014
XL
168 /// Returns `true` if the address is unnamed.
169 ///
170 /// # Examples
171 ///
172 /// A named address:
173 ///
174 /// ```no_run
175 /// use std::os::unix::net::UnixListener;
176 ///
177 /// fn main() -> std::io::Result<()> {
178 /// let socket = UnixListener::bind("/tmp/sock")?;
179 /// let addr = socket.local_addr().expect("Couldn't get local address");
180 /// assert_eq!(addr.is_unnamed(), false);
181 /// Ok(())
182 /// }
183 /// ```
184 ///
185 /// An unnamed address:
186 ///
187 /// ```
188 /// use std::os::unix::net::UnixDatagram;
189 ///
190 /// fn main() -> std::io::Result<()> {
191 /// let socket = UnixDatagram::unbound()?;
192 /// let addr = socket.local_addr().expect("Couldn't get local address");
193 /// assert_eq!(addr.is_unnamed(), true);
194 /// Ok(())
195 /// }
196 /// ```
c295e0f8 197 #[must_use]
fc512014
XL
198 #[stable(feature = "unix_socket", since = "1.10.0")]
199 pub fn is_unnamed(&self) -> bool {
3c0e092e 200 matches!(self.address(), AddressKind::Unnamed)
fc512014
XL
201 }
202
203 /// Returns the contents of this address if it is a `pathname` address.
204 ///
205 /// # Examples
206 ///
207 /// With a pathname:
208 ///
209 /// ```no_run
210 /// use std::os::unix::net::UnixListener;
211 /// use std::path::Path;
212 ///
213 /// fn main() -> std::io::Result<()> {
214 /// let socket = UnixListener::bind("/tmp/sock")?;
215 /// let addr = socket.local_addr().expect("Couldn't get local address");
216 /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock")));
217 /// Ok(())
218 /// }
219 /// ```
220 ///
221 /// Without a pathname:
222 ///
223 /// ```
224 /// use std::os::unix::net::UnixDatagram;
225 ///
226 /// fn main() -> std::io::Result<()> {
227 /// let socket = UnixDatagram::unbound()?;
228 /// let addr = socket.local_addr().expect("Couldn't get local address");
229 /// assert_eq!(addr.as_pathname(), None);
230 /// Ok(())
231 /// }
232 /// ```
233 #[stable(feature = "unix_socket", since = "1.10.0")]
c295e0f8 234 #[must_use]
fc512014
XL
235 pub fn as_pathname(&self) -> Option<&Path> {
236 if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None }
237 }
238
c295e0f8
XL
239 /// Returns the contents of this address if it is an abstract namespace
240 /// without the leading null byte.
241 ///
242 /// # Examples
243 ///
244 /// ```no_run
245 /// #![feature(unix_socket_abstract)]
246 /// use std::os::unix::net::{UnixListener, SocketAddr};
247 ///
248 /// fn main() -> std::io::Result<()> {
249 /// let namespace = b"hidden";
250 /// let namespace_addr = SocketAddr::from_abstract_namespace(&namespace[..])?;
251 /// let socket = UnixListener::bind_addr(&namespace_addr)?;
252 /// let local_addr = socket.local_addr().expect("Couldn't get local address");
253 /// assert_eq!(local_addr.as_abstract_namespace(), Some(&namespace[..]));
254 /// Ok(())
255 /// }
256 /// ```
257 #[doc(cfg(any(target_os = "android", target_os = "linux")))]
258 #[cfg(any(doc, target_os = "android", target_os = "linux",))]
259 #[unstable(feature = "unix_socket_abstract", issue = "85410")]
260 pub fn as_abstract_namespace(&self) -> Option<&[u8]> {
261 if let AddressKind::Abstract(name) = self.address() { Some(name) } else { None }
262 }
263
fc512014
XL
264 fn address(&self) -> AddressKind<'_> {
265 let len = self.len as usize - sun_path_offset(&self.addr);
266 let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) };
267
268 // macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses
269 if len == 0
270 || (cfg!(not(any(target_os = "linux", target_os = "android")))
271 && self.addr.sun_path[0] == 0)
272 {
273 AddressKind::Unnamed
274 } else if self.addr.sun_path[0] == 0 {
275 AddressKind::Abstract(&path[1..len])
276 } else {
277 AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref())
278 }
279 }
c295e0f8
XL
280
281 /// Creates an abstract domain socket address from a namespace
282 ///
283 /// An abstract address does not create a file unlike traditional path-based
284 /// Unix sockets. The advantage of this is that the address will disappear when
285 /// the socket bound to it is closed, so no filesystem clean up is required.
286 ///
287 /// The leading null byte for the abstract namespace is automatically added.
288 ///
289 /// This is a Linux-specific extension. See more at [`unix(7)`].
290 ///
291 /// [`unix(7)`]: https://man7.org/linux/man-pages/man7/unix.7.html
292 ///
293 /// # Errors
294 ///
295 /// This will return an error if the given namespace is too long
296 ///
297 /// # Examples
298 ///
299 /// ```no_run
300 /// #![feature(unix_socket_abstract)]
301 /// use std::os::unix::net::{UnixListener, SocketAddr};
302 ///
303 /// fn main() -> std::io::Result<()> {
304 /// let addr = SocketAddr::from_abstract_namespace(b"hidden")?;
305 /// let listener = match UnixListener::bind_addr(&addr) {
306 /// Ok(sock) => sock,
307 /// Err(err) => {
ee023bcb 308 /// println!("Couldn't bind: {err:?}");
c295e0f8
XL
309 /// return Err(err);
310 /// }
311 /// };
312 /// Ok(())
313 /// }
314 /// ```
315 #[doc(cfg(any(target_os = "android", target_os = "linux")))]
316 #[cfg(any(doc, target_os = "android", target_os = "linux",))]
317 #[unstable(feature = "unix_socket_abstract", issue = "85410")]
318 pub fn from_abstract_namespace(namespace: &[u8]) -> io::Result<SocketAddr> {
319 unsafe {
320 let mut addr: libc::sockaddr_un = mem::zeroed();
321 addr.sun_family = libc::AF_UNIX as libc::sa_family_t;
322
323 if namespace.len() + 1 > addr.sun_path.len() {
5099ac24 324 return Err(io::const_io_error!(
c295e0f8 325 io::ErrorKind::InvalidInput,
5099ac24 326 "namespace must be shorter than SUN_LEN",
c295e0f8
XL
327 ));
328 }
329
330 crate::ptr::copy_nonoverlapping(
331 namespace.as_ptr(),
332 addr.sun_path.as_mut_ptr().offset(1) as *mut u8,
333 namespace.len(),
334 );
335 let len = (sun_path_offset(&addr) + 1 + namespace.len()) as libc::socklen_t;
336 SocketAddr::from_parts(addr, len)
337 }
338 }
fc512014
XL
339}
340
341#[stable(feature = "unix_socket", since = "1.10.0")]
342impl fmt::Debug for SocketAddr {
343 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
344 match self.address() {
345 AddressKind::Unnamed => write!(fmt, "(unnamed)"),
346 AddressKind::Abstract(name) => write!(fmt, "{} (abstract)", AsciiEscaped(name)),
ee023bcb 347 AddressKind::Pathname(path) => write!(fmt, "{path:?} (pathname)"),
fc512014
XL
348 }
349 }
350}