| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License.. |
| |
| use crate::ffi::OsStr; |
| use crate::os::unix::ffi::OsStrExt; |
| use crate::path::Path; |
| use crate::sys::cvt; |
| use crate::{ascii, fmt, io, mem, ptr}; |
| use sgx_libc as libc; |
| |
| fn sun_path_offset(addr: &libc::sockaddr_un) -> usize { |
| // Work with an actual instance of the type since using a null pointer is UB |
| let base = addr as *const _ as usize; |
| let path = &addr.sun_path as *const _ as usize; |
| path - base |
| } |
| |
| pub(super) fn sockaddr_un(path: &Path) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> { |
| // SAFETY: All zeros is a valid representation for `sockaddr_un`. |
| let mut addr: libc::sockaddr_un = unsafe { mem::zeroed() }; |
| addr.sun_family = libc::AF_UNIX as libc::sa_family_t; |
| |
| let bytes = path.as_os_str().as_bytes(); |
| |
| if bytes.contains(&0) { |
| return Err(io::const_io_error!( |
| io::ErrorKind::InvalidInput, |
| "paths must not contain interior null bytes", |
| )); |
| } |
| |
| if bytes.len() >= addr.sun_path.len() { |
| return Err(io::const_io_error!( |
| io::ErrorKind::InvalidInput, |
| "path must be shorter than SUN_LEN", |
| )); |
| } |
| // SAFETY: `bytes` and `addr.sun_path` are not overlapping and |
| // both point to valid memory. |
| // NOTE: We zeroed the memory above, so the path is already null |
| // terminated. |
| unsafe { |
| ptr::copy_nonoverlapping(bytes.as_ptr(), addr.sun_path.as_mut_ptr().cast(), bytes.len()) |
| }; |
| |
| let mut len = sun_path_offset(&addr) + bytes.len(); |
| match bytes.get(0) { |
| Some(&0) | None => {} |
| Some(_) => len += 1, |
| } |
| Ok((addr, len as libc::socklen_t)) |
| } |
| |
| enum AddressKind<'a> { |
| Unnamed, |
| Pathname(&'a Path), |
| Abstract(&'a [u8]), |
| } |
| |
| struct AsciiEscaped<'a>(&'a [u8]); |
| |
| impl<'a> fmt::Display for AsciiEscaped<'a> { |
| fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
| write!(fmt, "\"")?; |
| for byte in self.0.iter().cloned().flat_map(ascii::escape_default) { |
| write!(fmt, "{}", byte as char)?; |
| } |
| write!(fmt, "\"") |
| } |
| } |
| |
| /// An address associated with a Unix socket. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// use std::os::unix::net::UnixListener; |
| /// |
| /// let socket = match UnixListener::bind("/tmp/sock") { |
| /// Ok(sock) => sock, |
| /// Err(e) => { |
| /// println!("Couldn't bind: {:?}", e); |
| /// return |
| /// } |
| /// }; |
| /// let addr = socket.local_addr().expect("Couldn't get local address"); |
| /// ``` |
| #[derive(Clone)] |
| pub struct SocketAddr { |
| pub(super) addr: libc::sockaddr_un, |
| pub(super) len: libc::socklen_t, |
| } |
| |
| impl SocketAddr { |
| pub(super) fn new<F>(f: F) -> io::Result<SocketAddr> |
| where |
| F: FnOnce(*mut libc::sockaddr, *mut libc::socklen_t) -> libc::c_int, |
| { |
| unsafe { |
| let mut addr: libc::sockaddr_un = mem::zeroed(); |
| let mut len = mem::size_of::<libc::sockaddr_un>() as libc::socklen_t; |
| cvt(f(&mut addr as *mut _ as *mut _, &mut len))?; |
| SocketAddr::from_parts(addr, len) |
| } |
| } |
| |
| pub(super) fn from_parts( |
| addr: libc::sockaddr_un, |
| mut len: libc::socklen_t, |
| ) -> io::Result<SocketAddr> { |
| if len == 0 { |
| // When there is a datagram from unnamed unix socket |
| // linux returns zero bytes of address |
| len = sun_path_offset(&addr) as libc::socklen_t; // i.e., zero-length address |
| } else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t { |
| return Err(io::const_io_error!( |
| io::ErrorKind::InvalidInput, |
| "file descriptor did not correspond to a Unix socket", |
| )); |
| } |
| |
| Ok(SocketAddr { addr, len }) |
| } |
| |
| /// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path. |
| /// |
| /// # Errors |
| /// |
| /// Returns an error if the path is longer than `SUN_LEN` or if it contains |
| /// NULL bytes. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// #![feature(unix_socket_creation)] |
| /// use std::os::unix::net::SocketAddr; |
| /// use std::path::Path; |
| /// |
| /// # fn main() -> std::io::Result<()> { |
| /// let address = SocketAddr::from_path("/path/to/socket")?; |
| /// assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket"))); |
| /// # Ok(()) |
| /// # } |
| /// ``` |
| /// |
| /// Creating a `SocketAddr` with a NULL byte results in an error. |
| /// |
| /// ``` |
| /// #![feature(unix_socket_creation)] |
| /// use std::os::unix::net::SocketAddr; |
| /// |
| /// assert!(SocketAddr::from_path("/path/with/\0/bytes").is_err()); |
| /// ``` |
| pub fn from_path<P>(path: P) -> io::Result<SocketAddr> |
| where |
| P: AsRef<Path>, |
| { |
| sockaddr_un(path.as_ref()).map(|(addr, len)| SocketAddr { addr, len }) |
| } |
| |
| /// Returns `true` if the address is unnamed. |
| /// |
| /// # Examples |
| /// |
| /// A named address: |
| /// |
| /// ```no_run |
| /// use std::os::unix::net::UnixListener; |
| /// |
| /// fn main() -> std::io::Result<()> { |
| /// let socket = UnixListener::bind("/tmp/sock")?; |
| /// let addr = socket.local_addr().expect("Couldn't get local address"); |
| /// assert_eq!(addr.is_unnamed(), false); |
| /// Ok(()) |
| /// } |
| /// ``` |
| /// |
| /// An unnamed address: |
| /// |
| /// ``` |
| /// use std::os::unix::net::UnixDatagram; |
| /// |
| /// fn main() -> std::io::Result<()> { |
| /// let socket = UnixDatagram::unbound()?; |
| /// let addr = socket.local_addr().expect("Couldn't get local address"); |
| /// assert_eq!(addr.is_unnamed(), true); |
| /// Ok(()) |
| /// } |
| /// ``` |
| #[must_use] |
| pub fn is_unnamed(&self) -> bool { |
| matches!(self.address(), AddressKind::Unnamed) |
| } |
| |
| /// Returns the contents of this address if it is a `pathname` address. |
| /// |
| /// # Examples |
| /// |
| /// With a pathname: |
| /// |
| /// ```no_run |
| /// use std::os::unix::net::UnixListener; |
| /// use std::path::Path; |
| /// |
| /// fn main() -> std::io::Result<()> { |
| /// let socket = UnixListener::bind("/tmp/sock")?; |
| /// let addr = socket.local_addr().expect("Couldn't get local address"); |
| /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock"))); |
| /// Ok(()) |
| /// } |
| /// ``` |
| /// |
| /// Without a pathname: |
| /// |
| /// ``` |
| /// use std::os::unix::net::UnixDatagram; |
| /// |
| /// fn main() -> std::io::Result<()> { |
| /// let socket = UnixDatagram::unbound()?; |
| /// let addr = socket.local_addr().expect("Couldn't get local address"); |
| /// assert_eq!(addr.as_pathname(), None); |
| /// Ok(()) |
| /// } |
| /// ``` |
| #[must_use] |
| pub fn as_pathname(&self) -> Option<&Path> { |
| if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None } |
| } |
| |
| /// Returns the contents of this address if it is an abstract namespace |
| /// without the leading null byte. |
| /// |
| /// # Examples |
| /// |
| /// ```no_run |
| /// #![feature(unix_socket_abstract)] |
| /// use std::os::unix::net::{UnixListener, SocketAddr}; |
| /// |
| /// fn main() -> std::io::Result<()> { |
| /// let namespace = b"hidden"; |
| /// let namespace_addr = SocketAddr::from_abstract_namespace(&namespace[..])?; |
| /// let socket = UnixListener::bind_addr(&namespace_addr)?; |
| /// let local_addr = socket.local_addr().expect("Couldn't get local address"); |
| /// assert_eq!(local_addr.as_abstract_namespace(), Some(&namespace[..])); |
| /// Ok(()) |
| /// } |
| /// ``` |
| pub fn as_abstract_namespace(&self) -> Option<&[u8]> { |
| if let AddressKind::Abstract(name) = self.address() { Some(name) } else { None } |
| } |
| |
| fn address(&self) -> AddressKind<'_> { |
| let len = self.len as usize - sun_path_offset(&self.addr); |
| let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) }; |
| |
| if len == 0 { |
| AddressKind::Unnamed |
| } else if self.addr.sun_path[0] == 0 { |
| AddressKind::Abstract(&path[1..len]) |
| } else { |
| AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref()) |
| } |
| } |
| |
| /// Creates an abstract domain socket address from a namespace |
| /// |
| /// An abstract address does not create a file unlike traditional path-based |
| /// Unix sockets. The advantage of this is that the address will disappear when |
| /// the socket bound to it is closed, so no filesystem clean up is required. |
| /// |
| /// The leading null byte for the abstract namespace is automatically added. |
| /// |
| /// This is a Linux-specific extension. See more at [`unix(7)`]. |
| /// |
| /// [`unix(7)`]: https://man7.org/linux/man-pages/man7/unix.7.html |
| /// |
| /// # Errors |
| /// |
| /// This will return an error if the given namespace is too long |
| /// |
| /// # Examples |
| /// |
| /// ```no_run |
| /// #![feature(unix_socket_abstract)] |
| /// use std::os::unix::net::{UnixListener, SocketAddr}; |
| /// |
| /// fn main() -> std::io::Result<()> { |
| /// let addr = SocketAddr::from_abstract_namespace(b"hidden")?; |
| /// let listener = match UnixListener::bind_addr(&addr) { |
| /// Ok(sock) => sock, |
| /// Err(err) => { |
| /// println!("Couldn't bind: {:?}", err); |
| /// return Err(err); |
| /// } |
| /// }; |
| /// Ok(()) |
| /// } |
| /// ``` |
| pub fn from_abstract_namespace(namespace: &[u8]) -> io::Result<SocketAddr> { |
| unsafe { |
| let mut addr: libc::sockaddr_un = mem::zeroed(); |
| addr.sun_family = libc::AF_UNIX as libc::sa_family_t; |
| |
| if namespace.len() + 1 > addr.sun_path.len() { |
| return Err(io::const_io_error!( |
| io::ErrorKind::InvalidInput, |
| "namespace must be shorter than SUN_LEN", |
| )); |
| } |
| |
| crate::ptr::copy_nonoverlapping( |
| namespace.as_ptr(), |
| addr.sun_path.as_mut_ptr().offset(1) as *mut u8, |
| namespace.len(), |
| ); |
| let len = (sun_path_offset(&addr) + 1 + namespace.len()) as libc::socklen_t; |
| SocketAddr::from_parts(addr, len) |
| } |
| } |
| } |
| |
| impl fmt::Debug for SocketAddr { |
| fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
| match self.address() { |
| AddressKind::Unnamed => write!(fmt, "(unnamed)"), |
| AddressKind::Abstract(name) => write!(fmt, "{} (abstract)", AsciiEscaped(name)), |
| AddressKind::Pathname(path) => write!(fmt, "{:?} (pathname)", path), |
| } |
| } |
| } |