blob: 8efe3aff07b544dd4f6dc49d0676491ff246c833 [file] [log] [blame]
// 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),
}
}
}