mirror of https://github.com/rust-lang/rust.git
Auto merge of #85379 - mdaverde:uds-abstract, r=joshtriplett
Add abstract namespace support for Unix domain sockets
Hello! The other day I wanted to mess around with UDS in Rust and found that abstract namespaces ([unix(7)](https://man7.org/linux/man-pages/man7/unix.7.html)) on Linux still needed development. I took the approach of adding `_addr` specific public functions to reduce conflicts.
Feature name: `unix_socket_abstract`
Tracking issue: #85410
Further context: #42048
## Non-platform specific additions
`UnixListener::bind_addr(&SocketAddr) -> Result<UnixListener>`
`UnixStream::connect_addr(&SocketAddr) -> Result<()>`
`UnixDatagram::bind_addr(&SocketAddr) -> Result<UnixDatagram>`
`UnixDatagram::connect_addr(&SocketAddr) -> Result<()>`
`UnixDatagram::send_to_addr(&self, &[u8], &SocketAddr) -> Result<usize>`
## Platform-specific (Linux) additions
`SocketAddr::from_abstract_namespace(&[u8]) -> SocketAddr`
`SockerAddr::as_abstract_namespace() -> Option<&[u8]>`
## Example
```rust
#![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(())
}
```
## Further Details
The main inspiration for the implementation came from the [nix-rust](https://github.com/nix-rust/nix/blob/master/src/sys/socket/addr.rs#L558) crate but there are also other [historical](c4db0685b1
) [attempts](https://github.com/tormol/uds/blob/master/src/addr.rs#L324) with similar approaches.
A comment I did have was with this change, we now allow a `SocketAddr` to be constructed explicitly rather than just used almost as a handle for the return of `peer_addr` and `local_addr`. We could consider adding other explicit constructors (e.g. `SocketAddr::from_pathname`, `SockerAddr::from_unnamed`).
Cheers!
This commit is contained in:
commit
6cc0a764e0
|
@ -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 {
|
||||
|
@ -198,6 +198,31 @@ 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(())
|
||||
/// }
|
||||
/// ```
|
||||
#[doc(cfg(any(target_os = "android", target_os = "linux")))]
|
||||
#[cfg(any(doc, target_os = "android", target_os = "linux",))]
|
||||
#[unstable(feature = "unix_socket_abstract", issue = "85410")]
|
||||
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) };
|
||||
|
@ -214,6 +239,65 @@ 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(())
|
||||
/// }
|
||||
/// ```
|
||||
#[doc(cfg(any(target_os = "android", target_os = "linux")))]
|
||||
#[cfg(any(doc, target_os = "android", target_os = "linux",))]
|
||||
#[unstable(feature = "unix_socket_abstract", issue = "85410")]
|
||||
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",
|
||||
));
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "unix_socket", since = "1.10.0")]
|
||||
|
|
|
@ -112,6 +112,41 @@ impl UnixDatagram {
|
|||
}
|
||||
}
|
||||
|
||||
/// Creates a Unix datagram socket bound to an address.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// #![feature(unix_socket_abstract)]
|
||||
/// use std::os::unix::net::{UnixDatagram};
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let sock1 = UnixDatagram::bind("path/to/socket")?;
|
||||
/// let addr = sock1.local_addr()?;
|
||||
///
|
||||
/// let sock2 = 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 = "85410")]
|
||||
pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result<UnixDatagram> {
|
||||
unsafe {
|
||||
let socket = UnixDatagram::unbound()?;
|
||||
cvt(libc::bind(
|
||||
socket.as_raw_fd(),
|
||||
&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 +191,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 +227,41 @@ impl UnixDatagram {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Connects the socket to an address.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// #![feature(unix_socket_abstract)]
|
||||
/// use std::os::unix::net::{UnixDatagram};
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let bound = UnixDatagram::bind("/path/to/socket")?;
|
||||
/// let addr = bound.local_addr()?;
|
||||
///
|
||||
/// 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 = "85410")]
|
||||
pub fn connect_addr(&self, socket_addr: &SocketAddr) -> io::Result<()> {
|
||||
unsafe {
|
||||
cvt(libc::connect(
|
||||
self.as_raw_fd(),
|
||||
&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 +543,42 @@ 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};
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let bound = UnixDatagram::bind("/path/to/socket")?;
|
||||
/// let addr = bound.local_addr()?;
|
||||
///
|
||||
/// 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 = "85410")]
|
||||
pub fn send_to_addr(&self, buf: &[u8], socket_addr: &SocketAddr) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let count = cvt(libc::sendto(
|
||||
self.as_raw_fd(),
|
||||
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
|
||||
|
|
|
@ -81,6 +81,44 @@ 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};
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let listener1 = UnixListener::bind("path/to/socket")?;
|
||||
/// let addr = listener1.local_addr()?;
|
||||
///
|
||||
/// let listener2 = 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 = "85410")]
|
||||
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_raw_fd(),
|
||||
&socket_addr.addr as *const _ as *const _,
|
||||
socket_addr.len as _,
|
||||
))?;
|
||||
cvt(libc::listen(inner.as_raw_fd(), 128))?;
|
||||
Ok(UnixListener(inner))
|
||||
}
|
||||
}
|
||||
|
||||
/// Accepts a new incoming connection to this listener.
|
||||
///
|
||||
/// This function will block the calling thread until a new Unix connection
|
||||
|
|
|
@ -106,6 +106,43 @@ 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::{UnixListener, UnixStream};
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let listener = UnixListener::bind("/path/to/the/socket")?;
|
||||
/// let addr = listener.local_addr()?;
|
||||
///
|
||||
/// let sock = 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 = "85410")]
|
||||
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_raw_fd(),
|
||||
&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.
|
||||
|
|
|
@ -303,6 +303,30 @@ fn test_unnamed_unix_datagram() {
|
|||
assert_eq!(msg, &buf[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unix_datagram_connect_to_recv_addr() {
|
||||
let dir = tmpdir();
|
||||
let path1 = dir.path().join("sock1");
|
||||
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";
|
||||
let sock1_addr = or_panic!(sock1.local_addr());
|
||||
or_panic!(sock2.send_to_addr(msg, &sock1_addr));
|
||||
let mut buf = [0; 11];
|
||||
let (_, addr) = or_panic!(sock1.recv_from(&mut buf));
|
||||
|
||||
let new_msg = b"hello back";
|
||||
let mut new_buf = [0; 10];
|
||||
or_panic!(sock2.connect_addr(&addr));
|
||||
or_panic!(sock2.send(new_msg)); // set by connect_addr
|
||||
let usize = or_panic!(sock2.recv(&mut new_buf));
|
||||
assert_eq!(usize, 10);
|
||||
assert_eq!(new_msg, &new_buf[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_connect_unix_datagram() {
|
||||
let dir = tmpdir();
|
||||
|
@ -388,10 +412,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();
|
||||
|
|
Loading…
Reference in New Issue