diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs index e5fcb93b88308..72999dc85d315 100644 --- a/library/std/src/os/unix/net/addr.rs +++ b/library/std/src/os/unix/net/addr.rs @@ -6,72 +6,291 @@ use crate::os::net::linux_ext; use crate::os::unix::ffi::OsStrExt; use crate::path::Path; use crate::sys::cvt; -use crate::{fmt, io, mem, ptr}; +use crate::{fmt, io, mem}; // FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here? #[cfg(not(unix))] #[allow(non_camel_case_types)] mod libc { pub use core::ffi::c_int; + pub type sa_family_t = u8; pub type socklen_t = u32; pub struct sockaddr; #[derive(Clone)] pub struct sockaddr_un { + pub sun_family: sa_family_t, pub sun_path: [u8; 1], } } +enum AddressKind<'a> { + Unnamed, + Pathname(&'a Path), + Abstract(&'a ByteStr), +} + +/// The max socket address size in bytes +const SOCK_MAX_SIZE: usize = cfg_select! { + any(target_vendor = "apple", target_os = "dragonfly") => { + // Apple and DragonFly utilize `SOCK_MAXADDRLEN` to define + // the maximum size that the `sockaddr_un` struct could be. + // SOCK_MAXADDRLEN = 255 on these platforms, and it's based on + // sizeof(sa_len) (a u8) + sizeof(sun_family) (a u8) + 253 bytes + // (excluding nul). We add +1 for nul byte just in case. + // See https://github.com/rust-lang/rust/issues/160684 + libc::SOCK_MAXADDRLEN as usize + 1 + } + any(target_os = "netbsd") => { + // NetBSD uses `UCHAR_MAX` (essentially a `u8::MAX`) + 1 as defined by `sockaddr_big` here: + // https://github.com/IIJ-NetBSD/netbsd-src/blob/cc2b5d89fa44/sys/sys/socket.h#L272-L287 + libc::c_uchar::MAX as usize + 1 + } + _ => { + // All other platforms max socket address size is based on + // the size of their sockaddr_un struct + size_of::() + } +}; + +// A check to see if it's safe to downcast SOCK_MAX_SIZE to libc::socklen_t +// (a u32) +const _: () = assert!(SOCK_MAX_SIZE <= libc::socklen_t::MAX as usize); + +/// Offset to `libc::sockaddr_un.sun_family` in bytes +const SUN_FAMILY_OFFSET: usize = mem::offset_of!(libc::sockaddr_un, sun_family); +/// Offset to `libc::sockaddr_un.sun_path` in bytes const SUN_PATH_OFFSET: usize = mem::offset_of!(libc::sockaddr_un, sun_path); +/// This represents the maximum number of bytes + 1 allowed to be +/// stored in the socket path (e.g. 254 for NetBSD because it allows 253 +/// valid characters for its path, 104 for FreeBSD because it allows up to +/// 103 characters for its path) +pub(crate) const SUN_PATH_MAX_LEN: usize = SOCK_MAX_SIZE - SUN_PATH_OFFSET; +/// Platform-dependent error message when user provides a longer socket address path +/// than what the OS allows for. +const LEN_EXCEEDED_MSG: &'static str = + cfg_select! { + any(target_os = "macos", target_os = "dragonfly") => { + "path must be shorter than SOCK_MAXADDRLEN - 1" + } + target_os = "netbsd" => "path must be shorter than UCHAR_MAX - 1", + _ => "path must be shorter than SUN_LEN", + }; -#[rustdoc_skip_body] -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; +/// An internal helper struct that provides a `len` field describing the actual filled size of the socket +/// address and a `buf` field that contains an internal platform-agnostic struct of `libc::sockaddr_un`. +/// +/// On BSD platforms, `libc::sockaddr_un` is defined as: +/// struct sockaddr_un { +/// sun_len: u8, +/// sun_family: sa_family_t, +/// sun_path: [c_char; 104], +/// } +/// +/// On Linux and other platforms, `libc::sockaddr_un` is defined as: +/// struct sockaddr_un { +/// sun_family: sa_family_t, +/// sun_path: [c_char; 108], +/// } +/// +/// Note: +/// * `sa_family_t` is a u8 on BSD platforms and a u16 on Linux/other platforms. +/// * The fix-sized array value for `sun_path` field could be different across +/// other platforms (for Linux it's 108, but this may be a different on other platforms). +/// +/// Although `sockaddr_un.sun_path` is restricted to 104 characters on BSD platforms, +/// DragonFlyBSD/NetBSD/Apple actually allow `sun_path` to hold up to 253 non-nul bytes. +/// Therefore, this `SockaddrUn` struct aims to hold a buffer that contains the maximum +/// socket address size for each platform. +#[derive(Clone)] +#[repr(C)] +pub(super) struct SockaddrBuf { + /// Size of the socket address, `sun_family` and `sun_path` + /// fields from `libc::sockaddr_un` included + len: libc::socklen_t, + /// Stack allocated buffer that contains full size of what `libc::sockaddr_un` + /// could be (as `sun_path` field defined for `sockaddr_un` does not represent + /// the maximum path length of a Unix Domain socket name) + buf: [u8; SOCK_MAX_SIZE], + /// Make it sound to cast the struct to/from a `sockaddr_un` + align: [libc::sockaddr_un; 0], +} - let bytes = path.as_os_str().as_bytes(); +impl SockaddrBuf { + /* API for use in Rust */ - if bytes.contains(&0) { - return Err(io::const_error!( - io::ErrorKind::InvalidInput, - "paths must not contain interior null bytes", - )); + /// This returns an empty Unix Domain socket address with a + /// length value containing the current size of the `sockaddr_un` + pub(super) fn default() -> SockaddrBuf { + let mut sockaddr_un: [u8; SOCK_MAX_SIZE] = [0; SOCK_MAX_SIZE]; + let sun_family = (libc::AF_UNIX as libc::sa_family_t).to_ne_bytes(); + sockaddr_un[SUN_FAMILY_OFFSET..SUN_FAMILY_OFFSET + size_of::()] + .copy_from_slice(&sun_family); + + // Size of the socket address is not 0 since we have an initialized + // `sun_family`/`sun_len` + let len = SUN_PATH_OFFSET as libc::socklen_t; + + SockaddrBuf { len, buf: sockaddr_un, align: [] } } - if bytes.len() >= addr.sun_path.len() { - return Err(io::const_error!( - io::ErrorKind::InvalidInput, - "path must be shorter than SUN_LEN", - )); + /// Extracts the `sun_path` value from the socket address buffer. + /// + /// On QNX, NTO, DragonFlyBSD, NetBSD, OpenBSD, FreeBSD, and Apple family + /// the nul byte is not part of `SockaddrBuf.len` + /// + /// On all other platforms (e.g. Linux), the nul byte is a part of `SockaddrBuf.len` + fn path(&self) -> &[u8] { + &self.buf[SUN_PATH_OFFSET..self.len as usize] } - // 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 + bytes.len(); - match bytes.get(0) { - Some(&0) | None => {} - Some(_) => { - // on QNX7.1 and QNX8 the `len` value returned by the SUN_LEN - // macro in its libc does not include the null byte in the count so - // don't add it here to match what a C program passes to bind(2) and - // similar functions - if cfg!(not(any(target_os = "qnx", target_env = "nto71"))) { - len += 1 + /// Sets the socket address path for `sockaddr_un` in `SocketaddrBuf`. + fn set_path(&mut self, bytes: &[u8]) -> io::Result<()> { + if core::slice::memchr::memchr(0, bytes).is_some() { + return Err(io::const_error!( + io::ErrorKind::InvalidInput, + "paths must not contain interior null bytes", + )); + } + + if bytes.len() >= SUN_PATH_MAX_LEN { + return Err(io::const_error!(io::ErrorKind::InvalidInput, LEN_EXCEEDED_MSG)); + } + + self.buf[SUN_PATH_OFFSET..SUN_PATH_OFFSET + bytes.len()].copy_from_slice(bytes); + self.buf[SUN_PATH_OFFSET + bytes.len()] = 0; + + let mut len = SUN_PATH_OFFSET + bytes.len(); + #[cfg(any( + target_os = "dragonfly", + target_vendor = "apple", + target_os = "netbsd", + target_os = "openbsd", + target_os = "freebsd" + ))] + { + const _: () = assert!(SUN_PATH_MAX_LEN as usize <= u8::MAX as usize); + // For these platforms, `sockaddr_un` has a `sun_len` (u8) field that should be initialized with a value + // (nul byte excluding) + self.buf[mem::offset_of!(libc::sockaddr_un, sun_len)] = len as u8; + } + + match bytes.get(0) { + Some(&0) | None => {} + Some(_) => { + // on QNX7.1 and QNX8 the `len` value returned by the SUN_LEN + // macro in its libc does not include the null byte in the count so + // don't add it here to match what a C program passes to bind(2) and + // similar functions + // For BSD-based platforms, they do not count the nul terminating byte + // in the address length + if cfg!(not(any( + target_os = "qnx", + target_env = "nto71", + target_os = "dragonfly", + target_vendor = "apple", + target_os = "netbsd", + target_os = "openbsd", + target_os = "freebsd" + ))) { + len += 1 + } } } + + // Even though len here is a `usize` and `libc::socklen_t` is a `u32` + // our len value should be limited to whatever value a `u32` can hold + self.len = len as libc::socklen_t; + + Ok(()) } - Ok((addr, len as libc::socklen_t)) -} -enum AddressKind<'a> { - Unnamed, - Pathname(&'a Path), - Abstract(&'a ByteStr), + /// Extracts the `sun_path` value from the abstract socket address buffer (excludes initial + /// nul byte). + fn abstract_path(&self) -> &[u8] { + &self.buf[SUN_PATH_OFFSET + 1..self.len as usize] + } + + #[cfg(any(target_os = "android", target_os = "linux", target_os = "cygwin"))] + /// Sets the abstract socket address path for `sockaddr_un` in `SocketaddrBuf`. + /// Only used on Android, Linux, and Cygwin. + fn set_abstract_path(&mut self, bytes: &[u8]) -> io::Result<()> { + // Abstract socket address paths are not nul terminated! + // (Hence the > instead of >=) + // See Linux manpage: https://man7.org/linux/man-pages/man7/unix.7.html + // "The socket's address in this namespace is given by the additional + // bytes in sun_path that are covered by the specified length of the + // address structure. (Null bytes in the name have no special + // significance.)" + if bytes.len() + 1 > SUN_PATH_MAX_LEN { + return Err(io::const_error!( + io::ErrorKind::InvalidInput, + "path must be shorter than SUN_LEN - 1", + )); + } + + // +1 because the first byte in the sun_path for abstract sockets is a nul byte + self.buf[SUN_PATH_OFFSET + 1..SUN_PATH_OFFSET + 1 + bytes.len()].copy_from_slice(bytes); + self.len = (SUN_PATH_OFFSET + 1 + bytes.len()) as libc::socklen_t; + + Ok(()) + } + + /// Extracts the `sun_family` value from the socket address buffer. + fn sun_family(&self) -> libc::sa_family_t { + let sun_family_array = self.buf[SUN_FAMILY_OFFSET..SUN_FAMILY_OFFSET + size_of::()] + .try_into() + .expect("Slice should have exactly the same number of bytes extracted as the size of libc::sa_family_t"); + libc::sa_family_t::from_ne_bytes(sun_family_array) + } + + /* API for use in sockets in abstract namespace + interop with C */ + + /// Types passed to libc calls that read a socket address. + pub(super) fn as_libc_input(&self) -> (*const libc::sockaddr, libc::socklen_t) { + (self.buf.as_ptr().cast(), self.len) + } + + /// Types passed to libc calls that write a socket address. Length is first set to the + /// max allowed. + /// + /// Note that using this API _must_ be followed by a call to `update_from_libc`. + // Note that returning two raw pointers from a single `&mut self` function call rather than + // two `&mut self -> *mut T` function calls is required to pass Miri with stacked borrows. + pub(super) fn as_max_libc_output(&mut self) -> (*mut libc::sockaddr, *mut libc::socklen_t) { + // Even though len here is a `usize` and `libc::socklen_t` is a `u32` + // our len value should be limited to whatever value a `u32` can hold + self.len = SOCK_MAX_SIZE as libc::socklen_t; + + (self.buf.as_mut_ptr().cast(), &mut self.len) + } + + /// Some platforms encode things differently. Call this after a `libc` call to adjust length + /// and validate as needed. + pub(super) fn update_from_libc(&mut self) -> io::Result<()> { + if cfg!(target_os = "openbsd") { + // on OpenBSD, getsockname(2) doesn't re-adjust the len field of the socket address, + // so it doesn't reflect the true len of the content. Figure out the length for ourselves. + // https://marc.info/?l=openbsd-bugs&m=170105481926736&w=2 + let sun_path = self.path(); + + if let Some(new_len) = core::slice::memchr::memchr(0, sun_path) { + self.len = (new_len + SUN_PATH_OFFSET) as libc::socklen_t; + } + } + + if self.len == 0 { + // When there is a datagram from unnamed unix socket + // linux returns zero bytes of address + self.len = SUN_PATH_OFFSET as libc::socklen_t; // i.e., zero-length address + } else if self.sun_family() != libc::AF_UNIX as libc::sa_family_t { + return Err(io::const_error!( + io::ErrorKind::InvalidInput, + "file descriptor did not correspond to a Unix socket", + )); + } + + Ok(()) + } } /// An address associated with a Unix socket. @@ -89,57 +308,23 @@ enum AddressKind<'a> { /// return /// } /// }; -/// let addr = socket.local_addr().expect("`UnixListener::local_addr` should not fail"); +/// let addr = socket.local_addr().expect("Couldn't get local address"); /// ``` #[derive(Clone)] #[stable(feature = "unix_socket", since = "1.10.0")] pub struct SocketAddr { - pub(super) addr: libc::sockaddr_un, - pub(super) len: libc::socklen_t, + pub(super) sock: SockaddrBuf, } impl SocketAddr { pub(super) fn new(f: F) -> io::Result where - F: FnOnce(*mut libc::sockaddr, *mut libc::socklen_t) -> libc::c_int, + F: FnOnce(&mut SockaddrBuf) -> libc::c_int, { - unsafe { - let mut addr: libc::sockaddr_un = mem::zeroed(); - let mut len = size_of::() as libc::socklen_t; - cvt(f((&raw mut addr) as *mut _, &mut len))?; - SocketAddr::from_parts(addr, len) - } - } - - #[rustdoc_skip_body] - pub(super) fn from_parts( - addr: libc::sockaddr_un, - mut len: libc::socklen_t, - ) -> io::Result { - if cfg!(target_os = "openbsd") { - // on OpenBSD, getsockname(2) returns the actual size of the socket address, - // and not the len of the content. Figure out the length for ourselves. - // https://marc.info/?l=openbsd-bugs&m=170105481926736&w=2 - let sun_path: &[u8] = - unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&addr.sun_path) }; - len = core::slice::memchr::memchr(0, sun_path) - .map_or(len, |new_len| (new_len + SUN_PATH_OFFSET) as libc::socklen_t); - } - - len = len.min(size_of::() as libc::socklen_t); - - if len == 0 { - // When there is a datagram from unnamed unix socket - // linux returns zero bytes of address - len = SUN_PATH_OFFSET 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_error!( - io::ErrorKind::InvalidInput, - "file descriptor did not correspond to a Unix socket", - )); - } - - Ok(SocketAddr { addr, len }) + let mut sock = SockaddrBuf::default(); + cvt(f(&mut sock))?; + sock.update_from_libc()?; + Ok(SocketAddr { sock }) } /// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path. @@ -176,7 +361,15 @@ impl SocketAddr { where P: AsRef, { - sockaddr_un(path.as_ref()).map(|(addr, len)| SocketAddr { addr, len }) + SocketAddr::from_path(path.as_ref()) + } + + /// Constructs a Unix Domain socket address from a given `Path` + pub(super) fn from_path(path: &Path) -> io::Result { + let bytes = path.as_os_str().as_bytes(); + let mut sock = SockaddrBuf::default(); + sock.set_path(bytes)?; + Ok(SocketAddr { sock }) } /// Returns `true` if the address is unnamed. @@ -256,17 +449,17 @@ impl SocketAddr { #[rustdoc_skip_body] fn address(&self) -> AddressKind<'_> { - let len = self.len as usize - SUN_PATH_OFFSET; - let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) }; + let len = self.sock.len as usize - SUN_PATH_OFFSET; + let path = self.sock.path(); // macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses if len == 0 || (cfg!(not(any(target_os = "linux", target_os = "android", target_os = "cygwin"))) - && self.addr.sun_path[0] == 0) + && path[0] == 0) { AddressKind::Unnamed - } else if self.addr.sun_path[0] == 0 { - AddressKind::Abstract(ByteStr::from_bytes(&path[1..len])) + } else if path[0] == 0 { + AddressKind::Abstract(ByteStr::from_bytes(self.sock.abstract_path())) } else { // linux adds a trailing NUL and counts it in the length, freebsd, netbsd // and qnx do not, and a caller may bind(2) without one either. unix(7) @@ -291,25 +484,9 @@ impl linux_ext::addr::SocketAddrExt for SocketAddr { N: AsRef<[u8]>, { let name = name.as_ref(); - unsafe { - let mut addr: libc::sockaddr_un = mem::zeroed(); - addr.sun_family = libc::AF_UNIX as libc::sa_family_t; - - if name.len() + 1 > addr.sun_path.len() { - return Err(io::const_error!( - io::ErrorKind::InvalidInput, - "abstract socket name must be shorter than SUN_LEN", - )); - } - - crate::ptr::copy_nonoverlapping( - name.as_ptr(), - addr.sun_path.as_mut_ptr().add(1) as *mut u8, - name.len(), - ); - let len = (SUN_PATH_OFFSET + 1 + name.len()) as libc::socklen_t; - SocketAddr::from_parts(addr, len) - } + let mut sock = SockaddrBuf::default(); + sock.set_abstract_path(name)?; + Ok(SocketAddr { sock }) } } diff --git a/library/std/src/os/unix/net/ancillary.rs b/library/std/src/os/unix/net/ancillary.rs index acd564eb9c157..77f86816ad384 100644 --- a/library/std/src/os/unix/net/ancillary.rs +++ b/library/std/src/os/unix/net/ancillary.rs @@ -1,6 +1,6 @@ // FIXME: This is currently disabled on *BSD. -use super::{SocketAddr, sockaddr_un}; +use super::{SockaddrBuf, SocketAddr}; use crate::io::{self, IoSlice, IoSliceMut}; use crate::macros::rustdoc_skip_body; use crate::marker::PhantomData; @@ -38,10 +38,11 @@ pub(super) fn recv_vectored_with_ancillary_from( ancillary: &mut SocketAncillary<'_>, ) -> io::Result<(usize, bool, io::Result)> { unsafe { - let mut msg_name: libc::sockaddr_un = zeroed(); + let mut sock = SockaddrBuf::default(); + let (addr, len) = sock.as_max_libc_output(); let mut msg: libc::msghdr = zeroed(); - msg.msg_name = (&raw mut msg_name) as *mut _; - msg.msg_namelen = size_of::() as libc::socklen_t; + msg.msg_name = addr.cast(); + msg.msg_namelen = len.read(); msg.msg_iov = bufs.as_mut_ptr().cast(); msg.msg_iovlen = bufs.len() as _; msg.msg_controllen = ancillary.buffer.len() as _; @@ -56,9 +57,11 @@ pub(super) fn recv_vectored_with_ancillary_from( ancillary.truncated = msg.msg_flags & libc::MSG_CTRUNC == libc::MSG_CTRUNC; let truncated = msg.msg_flags & libc::MSG_TRUNC == libc::MSG_TRUNC; - let addr = SocketAddr::from_parts(msg_name, msg.msg_namelen); - Ok((count, truncated, addr)) + *len = msg.msg_namelen; + sock.update_from_libc()?; + + Ok((count, truncated, Ok(SocketAddr { sock }))) } } @@ -70,12 +73,12 @@ pub(super) fn send_vectored_with_ancillary_to( ancillary: &mut SocketAncillary<'_>, ) -> io::Result { unsafe { - let (mut msg_name, msg_namelen) = - if let Some(path) = path { sockaddr_un(path)? } else { (zeroed(), 0) }; + let sockaddr = if let Some(path) = path { SocketAddr::from_path(path)? } else { zeroed() }; + let (addr, len) = sockaddr.sock.as_libc_input(); let mut msg: libc::msghdr = zeroed(); - msg.msg_name = (&raw mut msg_name) as *mut _; - msg.msg_namelen = msg_namelen; + msg.msg_name = addr.cast_mut().cast(); + msg.msg_namelen = len; msg.msg_iov = bufs.as_ptr() as *mut _; msg.msg_iovlen = bufs.len() as _; msg.msg_controllen = ancillary.length as _; diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index 901263a7aa123..b822c78b1f331 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -14,7 +14,7 @@ ))] use libc::MSG_NOSIGNAL; -use super::{SocketAddr, sockaddr_un}; +use super::SocketAddr; #[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "cygwin"))] use super::{SocketAncillary, recv_vectored_with_ancillary_from, send_vectored_with_ancillary_to}; #[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "cygwin"))] @@ -101,9 +101,10 @@ impl UnixDatagram { pub fn bind>(path: P) -> io::Result { unsafe { let socket = UnixDatagram::unbound()?; - let (addr, len) = sockaddr_un(path.as_ref())?; + let sockaddr = SocketAddr::from_path(path.as_ref())?; + let (addr, len) = sockaddr.sock.as_libc_input(); - cvt(libc::bind(socket.as_raw_fd(), (&raw const addr) as *const _, len as _))?; + cvt(libc::bind(socket.as_raw_fd(), addr, len))?; Ok(socket) } @@ -134,11 +135,8 @@ impl UnixDatagram { pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result { unsafe { let socket = UnixDatagram::unbound()?; - cvt(libc::bind( - socket.as_raw_fd(), - (&raw const socket_addr.addr) as *const _, - socket_addr.len as _, - ))?; + let (addr, len) = socket_addr.sock.as_libc_input(); + cvt(libc::bind(socket.as_raw_fd(), addr, len))?; Ok(socket) } } @@ -219,9 +217,10 @@ impl UnixDatagram { #[stable(feature = "unix_socket", since = "1.10.0")] pub fn connect>(&self, path: P) -> io::Result<()> { unsafe { - let (addr, len) = sockaddr_un(path.as_ref())?; + let sockaddr = SocketAddr::from_path(path.as_ref())?; + let (addr, len) = sockaddr.sock.as_libc_input(); - cvt(libc::connect(self.as_raw_fd(), (&raw const addr) as *const _, len))?; + cvt(libc::connect(self.as_raw_fd(), addr, len))?; } Ok(()) } @@ -252,11 +251,8 @@ impl UnixDatagram { #[stable(feature = "unix_socket_abstract", since = "1.70.0")] pub fn connect_addr(&self, socket_addr: &SocketAddr) -> io::Result<()> { unsafe { - cvt(libc::connect( - self.as_raw_fd(), - (&raw const socket_addr.addr) as *const _, - socket_addr.len, - ))?; + let (addr, len) = socket_addr.sock.as_libc_input(); + cvt(libc::connect(self.as_raw_fd(), addr, len))?; } Ok(()) } @@ -301,7 +297,10 @@ impl UnixDatagram { #[stable(feature = "unix_socket", since = "1.10.0")] #[rustdoc_skip_body] pub fn local_addr(&self) -> io::Result { - SocketAddr::new(|addr, len| unsafe { libc::getsockname(self.as_raw_fd(), addr, len) }) + SocketAddr::new(|sock| unsafe { + let (addr, len) = sock.as_max_libc_output(); + libc::getsockname(self.as_raw_fd(), addr, len) + }) } /// Returns the address of this socket's peer. @@ -327,7 +326,10 @@ impl UnixDatagram { #[stable(feature = "unix_socket", since = "1.10.0")] #[rustdoc_skip_body] pub fn peer_addr(&self) -> io::Result { - SocketAddr::new(|addr, len| unsafe { libc::getpeername(self.as_raw_fd(), addr, len) }) + SocketAddr::new(|sockaddr| unsafe { + let (addr, len) = sockaddr.as_max_libc_output(); + libc::getpeername(self.as_raw_fd(), addr, len) + }) } fn recv_from_flags( @@ -336,7 +338,9 @@ impl UnixDatagram { flags: core::ffi::c_int, ) -> io::Result<(usize, SocketAddr)> { let mut count = 0; - let addr = SocketAddr::new(|addr, len| unsafe { + let addr = SocketAddr::new(|sockaddr| unsafe { + let (addr, len) = sockaddr.as_max_libc_output(); + count = libc::recvfrom( self.as_raw_fd(), buf.as_mut_ptr() as *mut _, @@ -534,14 +538,15 @@ impl UnixDatagram { #[stable(feature = "unix_socket", since = "1.10.0")] pub fn send_to>(&self, buf: &[u8], path: P) -> io::Result { unsafe { - let (addr, len) = sockaddr_un(path.as_ref())?; + let sockaddr = SocketAddr::from_path(path.as_ref())?; + let (addr, len) = sockaddr.sock.as_libc_input(); let count = cvt(libc::sendto( self.as_raw_fd(), buf.as_ptr() as *const _, buf.len(), MSG_NOSIGNAL, - (&raw const addr) as *const _, + addr, len, ))?; Ok(count as usize) @@ -572,13 +577,14 @@ impl UnixDatagram { #[stable(feature = "unix_socket_abstract", since = "1.70.0")] pub fn send_to_addr(&self, buf: &[u8], socket_addr: &SocketAddr) -> io::Result { unsafe { + let (addr, len) = socket_addr.sock.as_libc_input(); let count = cvt(libc::sendto( self.as_raw_fd(), buf.as_ptr() as *const _, buf.len(), MSG_NOSIGNAL, - (&raw const socket_addr.addr) as *const _, - socket_addr.len, + addr, + len, ))?; Ok(count as usize) } diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs index cc0bcdf9e614c..e8e2387ccf282 100644 --- a/library/std/src/os/unix/net/listener.rs +++ b/library/std/src/os/unix/net/listener.rs @@ -1,4 +1,4 @@ -use super::{SocketAddr, UnixStream, sockaddr_un}; +use super::{SockaddrBuf, SocketAddr, UnixStream}; use crate::macros::rustdoc_skip_body; use crate::os::unix::io::{ AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd, unsafe_impl_fd_asrefs, @@ -6,7 +6,7 @@ use crate::os::unix::io::{ use crate::path::Path; use crate::sys::net::Socket; use crate::sys::{AsInner, FromInner, IntoInner, cvt}; -use crate::{fmt, io, mem}; +use crate::{fmt, io}; /// A structure representing a Unix domain socket server. /// @@ -76,7 +76,6 @@ impl UnixListener { pub fn bind>(path: P) -> io::Result { unsafe { let inner = Socket::new(libc::AF_UNIX, libc::SOCK_STREAM)?; - let (addr, len) = sockaddr_un(path.as_ref())?; #[cfg(any( target_os = "windows", target_os = "redox", @@ -106,8 +105,10 @@ impl UnixListener { target_vendor = "apple", )))] const backlog: libc::c_int = libc::SOMAXCONN; + let sockaddr = SocketAddr::from_path(path.as_ref())?; + let (addr, len) = sockaddr.sock.as_libc_input(); - cvt(libc::bind(inner.as_inner().as_raw_fd(), (&raw const addr) as *const _, len as _))?; + cvt(libc::bind(inner.as_inner().as_raw_fd(), addr, len))?; cvt(libc::listen(inner.as_inner().as_raw_fd(), backlog))?; Ok(UnixListener(inner)) @@ -141,15 +142,12 @@ impl UnixListener { pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result { unsafe { let inner = Socket::new(libc::AF_UNIX, libc::SOCK_STREAM)?; + let (addr, len) = socket_addr.sock.as_libc_input(); #[cfg(target_os = "linux")] const backlog: core::ffi::c_int = -1; #[cfg(not(target_os = "linux"))] const backlog: core::ffi::c_int = 128; - cvt(libc::bind( - inner.as_raw_fd(), - (&raw const socket_addr.addr) as *const _, - socket_addr.len as _, - ))?; + cvt(libc::bind(inner.as_raw_fd(), addr, len))?; cvt(libc::listen(inner.as_raw_fd(), backlog))?; Ok(UnixListener(inner)) } @@ -181,11 +179,10 @@ impl UnixListener { /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> { - let mut storage: libc::sockaddr_un = unsafe { mem::zeroed() }; - let mut len = size_of_val(&storage) as libc::socklen_t; - let sock = self.0.accept((&raw mut storage) as *mut _, &mut len)?; - let addr = SocketAddr::from_parts(storage, len)?; - Ok((UnixStream(sock), addr)) + let mut sockaddr_buf = SockaddrBuf::default(); + let (addr, len) = sockaddr_buf.as_max_libc_output(); + let sock = self.0.accept(addr, len)?; + Ok((UnixStream(sock), SocketAddr { sock: sockaddr_buf })) } /// Creates a new independently owned handle to the underlying socket. @@ -228,7 +225,10 @@ impl UnixListener { #[stable(feature = "unix_socket", since = "1.10.0")] #[rustdoc_skip_body] pub fn local_addr(&self) -> io::Result { - SocketAddr::new(|addr, len| unsafe { libc::getsockname(self.as_raw_fd(), addr, len) }) + SocketAddr::new(|sockaddr| unsafe { + let (addr, len) = sockaddr.as_max_libc_output(); + libc::getsockname(self.as_raw_fd(), addr, len) + }) } /// Moves the socket into or out of nonblocking mode. diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index 3ad76f77aa475..4a60c4f99081e 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -21,7 +21,7 @@ cfg_select! { } } -use super::{SocketAddr, sockaddr_un}; +use super::SocketAddr; #[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "cygwin"))] use super::{SocketAncillary, recv_vectored_with_ancillary_from, send_vectored_with_ancillary_to}; #[cfg(any( @@ -119,9 +119,10 @@ impl UnixStream { pub fn connect>(path: P) -> io::Result { unsafe { let inner = Socket::new(libc::AF_UNIX, libc::SOCK_STREAM)?; - let (addr, len) = sockaddr_un(path.as_ref())?; + let sockaddr = SocketAddr::from_path(path.as_ref())?; + let (addr, len) = sockaddr.sock.as_libc_input(); - cvt(libc::connect(inner.as_raw_fd(), (&raw const addr) as *const _, len))?; + cvt(libc::connect(inner.as_raw_fd(), addr, len))?; Ok(UnixStream(inner)) } } @@ -154,11 +155,8 @@ impl UnixStream { pub fn connect_addr(socket_addr: &SocketAddr) -> io::Result { unsafe { let inner = Socket::new(libc::AF_UNIX, libc::SOCK_STREAM)?; - cvt(libc::connect( - inner.as_raw_fd(), - (&raw const socket_addr.addr) as *const _, - socket_addr.len, - ))?; + let (addr, len) = socket_addr.sock.as_libc_input(); + cvt(libc::connect(inner.as_raw_fd(), addr, len))?; Ok(UnixStream(inner)) } } @@ -228,7 +226,10 @@ impl UnixStream { #[stable(feature = "unix_socket", since = "1.10.0")] #[rustdoc_skip_body] pub fn local_addr(&self) -> io::Result { - SocketAddr::new(|addr, len| unsafe { libc::getsockname(self.as_raw_fd(), addr, len) }) + SocketAddr::new(|sockaddr| unsafe { + let (addr, len) = sockaddr.as_max_libc_output(); + libc::getsockname(self.as_raw_fd(), addr, len) + }) } /// Returns the socket address of the remote half of this connection. @@ -248,7 +249,10 @@ impl UnixStream { #[stable(feature = "unix_socket", since = "1.10.0")] #[rustdoc_skip_body] pub fn peer_addr(&self) -> io::Result { - SocketAddr::new(|addr, len| unsafe { libc::getpeername(self.as_raw_fd(), addr, len) }) + SocketAddr::new(|sockaddr| unsafe { + let (addr, len) = sockaddr.as_max_libc_output(); + libc::getpeername(self.as_raw_fd(), addr, len) + }) } /// Gets the peer credentials for this Unix domain socket. diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index 4014767461389..3e44f39c0d131 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -29,48 +29,6 @@ fn sock_addr_from_pathname() { assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket"))); } -// the trailing NUL is not counted in the reported length on freebsd, netbsd -// and qnx, and a caller may bind(2) without one anywhere -#[test] -fn sock_addr_without_trailing_nul() { - const PATH: &[u8] = b"/path/to/socket"; - - // SAFETY: all zeros is a valid representation for `sockaddr_un`. - let mut addr: libc::sockaddr_un = unsafe { crate::mem::zeroed() }; - addr.sun_family = libc::AF_UNIX as libc::sa_family_t; - for (dst, &src) in addr.sun_path.iter_mut().zip(PATH) { - *dst = src as _; - } - let offset = crate::mem::offset_of!(libc::sockaddr_un, sun_path); - - // length excluding the NUL, as reported by freebsd, netbsd and qnx - let address = or_panic!(SocketAddr::from_parts(addr, (offset + PATH.len()) as _)); - assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket"))); - - // length including the NUL, as reported by linux - let address = or_panic!(SocketAddr::from_parts(addr, (offset + PATH.len() + 1) as _)); - assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket"))); -} - -#[test] -#[cfg(any(target_os = "android", target_os = "linux"))] -fn sock_addr_pathname_fills_sun_path() { - use crate::ffi::OsStr; - use crate::os::unix::ffi::OsStrExt; - - let mut addr: libc::sockaddr_un = unsafe { crate::mem::zeroed() }; - addr.sun_family = libc::AF_UNIX as libc::sa_family_t; - let mut path = vec![b'a'; addr.sun_path.len()]; - path[0] = b'/'; - for (dst, &src) in addr.sun_path.iter_mut().zip(&path) { - *dst = src as _; - } - let offset = crate::mem::offset_of!(libc::sockaddr_un, sun_path); - - let address = or_panic!(SocketAddr::from_parts(addr, (offset + path.len() + 1) as _)); - assert_eq!(address.as_pathname(), Some(Path::new(OsStr::from_bytes(&path)))); -} - #[test] #[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets #[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")] @@ -204,10 +162,14 @@ fn iter() { #[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")] fn long_path() { let dir = tmpdir(); - let socket_path = dir.path().join( - "asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfa\ - sasdfasdfasdasdfasdfasdfadfasdfasdfasdfasdfasdf", + // +1 for separator byte on join + let dir_len = dir.path().as_os_str().len() + 1; + let sock = format!( + "sock{}", + vec!['a'; SUN_PATH_MAX_LEN.saturating_sub(dir_len)].into_iter().collect::() ); + let socket_path = dir.path().join(sock); + match UnixStream::connect(&socket_path) { Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {} Err(e) => panic!("unexpected error {e}"), @@ -868,3 +830,34 @@ fn test_send_vectored_with_ancillary_unix_datagram() { unreachable!("must be ScmRights"); } } + +#[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets +#[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")] +fn test_unix_datagram_max_path() { + let dir = tmpdir(); + // +1 for separator byte on join + let dir_len = dir.path().as_os_str().len() + 1; + // SUN_PATH_MAX_LEN differs based on platform (e.g. for macos this should be 254, for + // linux this should be 108). We test whether a socket can bind to the maximally allowed + // path size, which is SUN_PATH_MAX_LEN - 1 + let sock = format!( + "sock{}", + vec!['a'; SUN_PATH_MAX_LEN.saturating_sub(5 + dir_len)].into_iter().collect::() + ); + let sock2 = format!( + "sock{}", + vec!['b'; SUN_PATH_MAX_LEN.saturating_sub(5 + dir_len)].into_iter().collect::() + ); + let path1 = dir.path().join(sock); + let path2 = dir.path().join(sock2); + + let sock1 = or_panic!(UnixDatagram::bind(&path1)); + let sock2 = or_panic!(UnixDatagram::bind(&path2)); + + let msg = b"hello world"; + or_panic!(sock1.send_to(msg, &path2)); + let mut buf = [0; 11]; + or_panic!(sock2.recv_from(&mut buf)); + assert_eq!(msg, &buf[..]); +}