about summary refs log tree commit diff
path: root/library/std/src
diff options
context:
space:
mode:
Diffstat (limited to 'library/std/src')
-rw-r--r--library/std/src/os/unix/net/addr.rs84
-rw-r--r--library/std/src/os/unix/net/datagram.rs94
-rw-r--r--library/std/src/os/unix/net/listener.rs34
-rw-r--r--library/std/src/os/unix/net/stream.rs31
-rw-r--r--library/std/src/os/unix/net/tests.rs125
5 files changed, 363 insertions, 5 deletions
diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs
index 62bfde8bfd4..5c985a28017 100644
--- a/library/std/src/os/unix/net/addr.rs
+++ b/library/std/src/os/unix/net/addr.rs
@@ -2,7 +2,7 @@ use crate::ffi::OsStr;
 use crate::os::unix::ffi::OsStrExt;
 use crate::path::Path;
 use crate::sys::cvt;
-use crate::{ascii, fmt, io, iter, mem};
+use crate::{ascii, fmt, io, iter, mem, ptr};
 
 // FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here?
 #[cfg(not(unix))]
@@ -92,8 +92,8 @@ impl<'a> fmt::Display for AsciiEscaped<'a> {
 #[derive(Clone)]
 #[stable(feature = "unix_socket", since = "1.10.0")]
 pub struct SocketAddr {
-    addr: libc::sockaddr_un,
-    len: libc::socklen_t,
+    pub(super) addr: libc::sockaddr_un,
+    pub(super) len: libc::socklen_t,
 }
 
 impl SocketAddr {
@@ -196,6 +196,30 @@ impl SocketAddr {
         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(())
+    /// }
+    /// ```
+    #[cfg(any(doc, target_os = "android", target_os = "linux",))]
+    #[unstable(feature = "unix_socket_abstract", issue = "42048")]
+    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) };
@@ -212,6 +236,60 @@ impl SocketAddr {
             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(())
+    /// }
+    /// ```
+    #[cfg(any(doc, target_os = "android", target_os = "linux",))]
+    #[unstable(feature = "unix_socket_abstract", issue = "42048")]
+    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::Error::new_const(
+                    io::ErrorKind::InvalidInput,
+                    &"namespace must be shorter than SUN_LEN",
+                ));
+            }
+
+            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)
+        }
+    }
 }
 
 #[stable(feature = "unix_socket", since = "1.10.0")]
diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs
index f11eec18cc5..bae88af6d1f 100644
--- a/library/std/src/os/unix/net/datagram.rs
+++ b/library/std/src/os/unix/net/datagram.rs
@@ -112,6 +112,35 @@ impl UnixDatagram {
         }
     }
 
+    /// Creates a Unix datagram socket bound to an address.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// #![feature(unix_socket_abstract)]
+    /// use std::os::unix::net::{UnixDatagram, SocketAddr};
+    ///
+    /// fn main() -> std::io::Result<()> {
+    ///     let addr = SocketAddr::from_abstract_namespace(b"hidden")?; // Linux only
+    ///     let sock = match UnixDatagram::bind_addr(&addr) {
+    ///         Ok(sock) => sock,
+    ///         Err(err) => {
+    ///             println!("Couldn't bind: {:?}", err);
+    ///             return Err(err);
+    ///         }
+    ///     };
+    ///     Ok(())
+    /// }
+    /// ```
+    #[unstable(feature = "unix_socket_abstract", issue = "42048")]
+    pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result<UnixDatagram> {
+        unsafe {
+            let socket = UnixDatagram::unbound()?;
+            cvt(libc::bind(*socket.0.as_inner(), &socket_addr.addr as *const _ as *const _, socket_addr.len as _))?;
+            Ok(socket)
+        }
+    }
+
     /// Creates a Unix Datagram socket which is not bound to any address.
     ///
     /// # Examples
@@ -156,7 +185,7 @@ impl UnixDatagram {
         Ok((UnixDatagram(i1), UnixDatagram(i2)))
     }
 
-    /// Connects the socket to the specified address.
+    /// Connects the socket to the specified path address.
     ///
     /// The [`send`] method may be used to send data to the specified address.
     /// [`recv`] and [`recv_from`] will only receive data from that address.
@@ -192,6 +221,35 @@ impl UnixDatagram {
         Ok(())
     }
 
+    /// Connects the socket to an address.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// #![feature(unix_socket_abstract)]
+    /// use std::os::unix::net::{UnixDatagram, SocketAddr};
+    ///
+    /// fn main() -> std::io::Result<()> {
+    ///     let addr = SocketAddr::from_abstract_namespace(b"hidden")?; // Linux only
+    ///     let sock = UnixDatagram::unbound()?;
+    ///     match sock.connect_addr(&addr) {
+    ///         Ok(sock) => sock,
+    ///         Err(e) => {
+    ///             println!("Couldn't connect: {:?}", e);
+    ///             return Err(e)
+    ///         }
+    ///     };
+    ///     Ok(())
+    /// }
+    /// ```
+    #[unstable(feature = "unix_socket_abstract", issue = "42048")]
+    pub fn connect_addr(&self, socket_addr: &SocketAddr) -> io::Result<()> {
+        unsafe {
+            cvt(libc::connect(*self.0.as_inner(), &socket_addr.addr as *const _ as *const _, socket_addr.len))?;
+        }
+        Ok(())
+    }
+
     /// Creates a new independently owned handle to the underlying socket.
     ///
     /// The returned `UnixDatagram` is a reference to the same socket that this
@@ -473,6 +531,40 @@ impl UnixDatagram {
         }
     }
 
+    /// Sends data on the socket to the specified [SocketAddr].
+    ///
+    /// On success, returns the number of bytes written.
+    ///
+    /// [SocketAddr]: crate::os::unix::net::SocketAddr
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// #![feature(unix_socket_abstract)]
+    /// use std::os::unix::net::{UnixDatagram, SocketAddr};
+    ///
+    /// fn main() -> std::io::Result<()> {
+    ///     let addr = SocketAddr::from_abstract_namespace(b"hidden")?;
+    ///     let sock = UnixDatagram::unbound()?;
+    ///     sock.send_to_addr(b"bacon egg and cheese", &addr).expect("send_to_addr function failed");
+    ///     Ok(())
+    /// }
+    /// ```
+    #[unstable(feature = "unix_socket_abstract", issue = "42048")]
+    pub fn send_to_addr(&self, buf: &[u8], socket_addr: &SocketAddr) -> io::Result<usize> {
+        unsafe {
+            let count = cvt(libc::sendto(
+                *self.0.as_inner(),
+                buf.as_ptr() as *const _,
+                buf.len(),
+                MSG_NOSIGNAL,
+                &socket_addr.addr as *const _ as *const _,
+                socket_addr.len,
+            ))?;
+            Ok(count as usize)
+        }
+    }
+
     /// Sends data on the socket to the socket's peer.
     ///
     /// The peer address may be set by the `connect` method, and this method
diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs
index f08bd252e46..3077632764e 100644
--- a/library/std/src/os/unix/net/listener.rs
+++ b/library/std/src/os/unix/net/listener.rs
@@ -81,6 +81,40 @@ impl UnixListener {
         }
     }
 
+    /// Creates a new `UnixListener` bound to the specified [`socket address`].
+    ///
+    /// [`socket address`]: crate::os::unix::net::SocketAddr
+    ///
+    /// # 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"namespace")?; // Linux only
+    ///     let listener = match UnixListener::bind_addr(&addr) {
+    ///         Ok(sock) => sock,
+    ///         Err(err) => {
+    ///             println!("Couldn't bind: {:?}", err);
+    ///             return Err(err);
+    ///         }
+    ///     };
+    ///     Ok(())
+    /// }
+    /// ```
+    #[unstable(feature = "unix_socket_abstract", issue = "42048")]
+    pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result<UnixListener> {
+        unsafe {
+            let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?;
+            cvt(libc::bind(*inner.as_inner(), &socket_addr.addr as *const _ as *const _, socket_addr.len as _))?;
+            cvt(libc::listen(*inner.as_inner(), 128))?;
+
+            Ok(UnixListener(inner))
+        }
+    }
+
+
     /// Accepts a new incoming connection to this listener.
     ///
     /// This function will block the calling thread until a new Unix connection
diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs
index 4119de3c03c..00a713f0732 100644
--- a/library/std/src/os/unix/net/stream.rs
+++ b/library/std/src/os/unix/net/stream.rs
@@ -106,6 +106,37 @@ impl UnixStream {
         }
     }
 
+    /// Connects to the socket specified by [`address`].
+    ///
+    /// [`address`]: crate::os::unix::net::SocketAddr
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// #![feature(unix_socket_abstract)]
+    /// use std::os::unix::net::{UnixStream, SocketAddr};
+    ///
+    /// fn main() -> std::io::Result<()> {
+    ///     let addr = SocketAddr::from_abstract_namespace(b"hidden")?; // Linux only
+    ///     match UnixStream::connect_addr(&addr) {
+    ///         Ok(sock) => sock,
+    ///         Err(e) => {
+    ///             println!("Couldn't connect: {:?}", e);
+    ///             return Err(e)
+    ///         }
+    ///     };
+    ///     Ok(())
+    /// }
+    /// ````
+    #[unstable(feature = "unix_socket_abstract", issue = "42048")]
+    pub fn connect_addr(socket_addr: &SocketAddr) -> io::Result<UnixStream> {
+        unsafe {
+            let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?;
+            cvt(libc::connect(*inner.as_inner(), &socket_addr.addr as *const _ as *const _, socket_addr.len))?;
+            Ok(UnixStream(inner))
+        }
+    }
+
     /// Creates an unnamed pair of connected sockets.
     ///
     /// Returns two `UnixStream`s which are connected to each other.
diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs
index bd9b6dd727b..b13ce0e252a 100644
--- a/library/std/src/os/unix/net/tests.rs
+++ b/library/std/src/os/unix/net/tests.rs
@@ -388,10 +388,133 @@ fn test_unix_datagram_timeout_zero_duration() {
 }
 
 #[test]
-fn abstract_namespace_not_allowed() {
+fn abstract_namespace_not_allowed_connect() {
     assert!(UnixStream::connect("\0asdf").is_err());
 }
 
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+fn test_abstract_stream_connect() {
+    let msg1 = b"hello";
+    let msg2 = b"world";
+
+    let socket_addr = or_panic!(SocketAddr::from_abstract_namespace(b"namespace"));
+    let listener = or_panic!(UnixListener::bind_addr(&socket_addr));
+
+    let thread = thread::spawn(move || {
+        let mut stream = or_panic!(listener.accept()).0;
+        let mut buf = [0; 5];
+        or_panic!(stream.read(&mut buf));
+        assert_eq!(&msg1[..], &buf[..]);
+        or_panic!(stream.write_all(msg2));
+    });
+
+    let mut stream = or_panic!(UnixStream::connect_addr(&socket_addr));
+
+    let peer = or_panic!(stream.peer_addr());
+    assert_eq!(peer.as_abstract_namespace().unwrap(), b"namespace");
+
+    or_panic!(stream.write_all(msg1));
+    let mut buf = vec![];
+    or_panic!(stream.read_to_end(&mut buf));
+    assert_eq!(&msg2[..], &buf[..]);
+    drop(stream);
+
+    thread.join().unwrap();
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+fn test_abstract_stream_iter() {
+    let addr = or_panic!(SocketAddr::from_abstract_namespace(b"hidden"));
+    let listener = or_panic!(UnixListener::bind_addr(&addr));
+
+    let thread = thread::spawn(move || {
+        for stream in listener.incoming().take(2) {
+            let mut stream = or_panic!(stream);
+            let mut buf = [0];
+            or_panic!(stream.read(&mut buf));
+        }
+    });
+
+    for _ in 0..2 {
+        let mut stream = or_panic!(UnixStream::connect_addr(&addr));
+        or_panic!(stream.write_all(&[0]));
+    }
+
+    thread.join().unwrap();
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+fn test_abstract_datagram_bind_send_to_addr() {
+    let addr1 = or_panic!(SocketAddr::from_abstract_namespace(b"ns1"));
+    let sock1 = or_panic!(UnixDatagram::bind_addr(&addr1));
+
+    let local = or_panic!(sock1.local_addr());
+    assert_eq!(local.as_abstract_namespace().unwrap(), b"ns1");
+
+    let addr2 = or_panic!(SocketAddr::from_abstract_namespace(b"ns2"));
+    let sock2 = or_panic!(UnixDatagram::bind_addr(&addr2));
+
+    let msg = b"hello world";
+    or_panic!(sock1.send_to_addr(msg, &addr2));
+    let mut buf = [0; 11];
+    let (len, addr) = or_panic!(sock2.recv_from(&mut buf));
+    assert_eq!(msg, &buf[..]);
+    assert_eq!(len, 11);
+    assert_eq!(addr.as_abstract_namespace().unwrap(), b"ns1");
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+fn test_abstract_datagram_connect_addr() {
+    let addr1 = or_panic!(SocketAddr::from_abstract_namespace(b"ns3"));
+    let bsock1 = or_panic!(UnixDatagram::bind_addr(&addr1));
+
+    let sock = or_panic!(UnixDatagram::unbound());
+    or_panic!(sock.connect_addr(&addr1));
+
+    let msg = b"hello world";
+    or_panic!(sock.send(msg));
+    let mut buf = [0; 11];
+    let (len, addr) = or_panic!(bsock1.recv_from(&mut buf));
+    assert_eq!(len, 11);
+    assert_eq!(addr.is_unnamed(), true);
+    assert_eq!(msg, &buf[..]);
+
+    let addr2 = or_panic!(SocketAddr::from_abstract_namespace(b"ns4"));
+    let bsock2 = or_panic!(UnixDatagram::bind_addr(&addr2));
+
+    or_panic!(sock.connect_addr(&addr2));
+    or_panic!(sock.send(msg));
+    or_panic!(bsock2.recv_from(&mut buf));
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+fn test_abstract_namespace_too_long() {
+    match SocketAddr::from_abstract_namespace(
+        b"abcdefghijklmnopqrstuvwxyzabcdefghijklmn\
+        opqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi\
+        jklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz",
+    ) {
+        Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {}
+        Err(e) => panic!("unexpected error {}", e),
+        Ok(_) => panic!("unexpected success"),
+    }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+fn test_abstract_namespace_no_pathname_and_not_unnamed() {
+    let namespace = b"local";
+    let addr = or_panic!(SocketAddr::from_abstract_namespace(&namespace[..]));
+    assert_eq!(addr.as_pathname(), None);
+    assert_eq!(addr.as_abstract_namespace(), Some(&namespace[..]));
+    assert_eq!(addr.is_unnamed(), false);
+}
+
 #[test]
 fn test_unix_stream_peek() {
     let (txdone, rxdone) = crate::sync::mpsc::channel();