Skip to content

Commit edcaf88

Browse files
authored
Rollup merge of rust-lang#85379 - mdaverde:uds-abstract, r=joshtriplett,GuillaumeGomez
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: rust-lang#85410 Further context: rust-lang#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](rust-lang@c4db068) [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!
2 parents da0388d + b0b0267 commit edcaf88

File tree

5 files changed

+381
-4
lines changed

5 files changed

+381
-4
lines changed

library/std/src/os/unix/net/addr.rs

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ impl<'a> fmt::Display for AsciiEscaped<'a> {
9292
#[derive(Clone)]
9393
#[stable(feature = "unix_socket", since = "1.10.0")]
9494
pub struct SocketAddr {
95-
addr: libc::sockaddr_un,
96-
len: libc::socklen_t,
95+
pub(super) addr: libc::sockaddr_un,
96+
pub(super) len: libc::socklen_t,
9797
}
9898

9999
impl SocketAddr {
@@ -196,6 +196,30 @@ impl SocketAddr {
196196
if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None }
197197
}
198198

199+
/// Returns the contents of this address if it is an abstract namespace
200+
/// without the leading null byte.
201+
///
202+
/// # Examples
203+
///
204+
/// ```no_run
205+
/// #![feature(unix_socket_abstract)]
206+
/// use std::os::unix::net::{UnixListener, SocketAddr};
207+
///
208+
/// fn main() -> std::io::Result<()> {
209+
/// let namespace = b"hidden";
210+
/// let namespace_addr = SocketAddr::from_abstract_namespace(&namespace[..])?;
211+
/// let socket = UnixListener::bind_addr(&namespace_addr)?;
212+
/// let local_addr = socket.local_addr().expect("Couldn't get local address");
213+
/// assert_eq!(local_addr.as_abstract_namespace(), Some(&namespace[..]));
214+
/// Ok(())
215+
/// }
216+
/// ```
217+
#[cfg(any(doc, target_os = "android", target_os = "linux",))]
218+
#[unstable(feature = "unix_socket_abstract", issue = "85410")]
219+
pub fn as_abstract_namespace(&self) -> Option<&[u8]> {
220+
if let AddressKind::Abstract(name) = self.address() { Some(name) } else { None }
221+
}
222+
199223
fn address(&self) -> AddressKind<'_> {
200224
let len = self.len as usize - sun_path_offset(&self.addr);
201225
let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) };
@@ -212,6 +236,64 @@ impl SocketAddr {
212236
AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref())
213237
}
214238
}
239+
240+
/// Creates an abstract domain socket address from a namespace
241+
///
242+
/// An abstract address does not create a file unlike traditional path-based
243+
/// Unix sockets. The advantage of this is that the address will disappear when
244+
/// the socket bound to it is closed, so no filesystem clean up is required.
245+
///
246+
/// The leading null byte for the abstract namespace is automatically added.
247+
///
248+
/// This is a Linux-specific extension. See more at [`unix(7)`].
249+
///
250+
/// [`unix(7)`]: https://man7.org/linux/man-pages/man7/unix.7.html
251+
///
252+
/// # Errors
253+
///
254+
/// This will return an error if the given namespace is too long
255+
///
256+
/// # Examples
257+
///
258+
/// ```no_run
259+
/// #![feature(unix_socket_abstract)]
260+
/// use std::os::unix::net::{UnixListener, SocketAddr};
261+
///
262+
/// fn main() -> std::io::Result<()> {
263+
/// let addr = SocketAddr::from_abstract_namespace(b"hidden")?;
264+
/// let listener = match UnixListener::bind_addr(&addr) {
265+
/// Ok(sock) => sock,
266+
/// Err(err) => {
267+
/// println!("Couldn't bind: {:?}", err);
268+
/// return Err(err);
269+
/// }
270+
/// };
271+
/// Ok(())
272+
/// }
273+
/// ```
274+
#[cfg(any(doc, target_os = "android", target_os = "linux",))]
275+
#[unstable(feature = "unix_socket_abstract", issue = "85410")]
276+
pub fn from_abstract_namespace(namespace: &[u8]) -> io::Result<SocketAddr> {
277+
unsafe {
278+
let mut addr: libc::sockaddr_un = mem::zeroed();
279+
addr.sun_family = libc::AF_UNIX as libc::sa_family_t;
280+
281+
if namespace.len() + 1 > addr.sun_path.len() {
282+
return Err(io::Error::new_const(
283+
io::ErrorKind::InvalidInput,
284+
&"namespace must be shorter than SUN_LEN",
285+
));
286+
}
287+
288+
crate::ptr::copy_nonoverlapping(
289+
namespace.as_ptr(),
290+
addr.sun_path.as_mut_ptr().offset(1) as *mut u8,
291+
namespace.len(),
292+
);
293+
let len = (sun_path_offset(&addr) + 1 + namespace.len()) as libc::socklen_t;
294+
SocketAddr::from_parts(addr, len)
295+
}
296+
}
215297
}
216298

217299
#[stable(feature = "unix_socket", since = "1.10.0")]

library/std/src/os/unix/net/datagram.rs

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,39 @@ impl UnixDatagram {
112112
}
113113
}
114114

115+
/// Creates a Unix datagram socket bound to an address.
116+
///
117+
/// # Examples
118+
///
119+
/// ```no_run
120+
/// #![feature(unix_socket_abstract)]
121+
/// use std::os::unix::net::{UnixDatagram, SocketAddr};
122+
///
123+
/// fn main() -> std::io::Result<()> {
124+
/// let addr = SocketAddr::from_abstract_namespace(b"hidden")?; // Linux only
125+
/// let sock = match UnixDatagram::bind_addr(&addr) {
126+
/// Ok(sock) => sock,
127+
/// Err(err) => {
128+
/// println!("Couldn't bind: {:?}", err);
129+
/// return Err(err);
130+
/// }
131+
/// };
132+
/// Ok(())
133+
/// }
134+
/// ```
135+
#[unstable(feature = "unix_socket_abstract", issue = "85410")]
136+
pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result<UnixDatagram> {
137+
unsafe {
138+
let socket = UnixDatagram::unbound()?;
139+
cvt(libc::bind(
140+
*socket.0.as_inner(),
141+
&socket_addr.addr as *const _ as *const _,
142+
socket_addr.len as _,
143+
))?;
144+
Ok(socket)
145+
}
146+
}
147+
115148
/// Creates a Unix Datagram socket which is not bound to any address.
116149
///
117150
/// # Examples
@@ -156,7 +189,7 @@ impl UnixDatagram {
156189
Ok((UnixDatagram(i1), UnixDatagram(i2)))
157190
}
158191

159-
/// Connects the socket to the specified address.
192+
/// Connects the socket to the specified path address.
160193
///
161194
/// The [`send`] method may be used to send data to the specified address.
162195
/// [`recv`] and [`recv_from`] will only receive data from that address.
@@ -192,6 +225,39 @@ impl UnixDatagram {
192225
Ok(())
193226
}
194227

228+
/// Connects the socket to an address.
229+
///
230+
/// # Examples
231+
///
232+
/// ```no_run
233+
/// #![feature(unix_socket_abstract)]
234+
/// use std::os::unix::net::{UnixDatagram, SocketAddr};
235+
///
236+
/// fn main() -> std::io::Result<()> {
237+
/// let addr = SocketAddr::from_abstract_namespace(b"hidden")?; // Linux only
238+
/// let sock = UnixDatagram::unbound()?;
239+
/// match sock.connect_addr(&addr) {
240+
/// Ok(sock) => sock,
241+
/// Err(e) => {
242+
/// println!("Couldn't connect: {:?}", e);
243+
/// return Err(e)
244+
/// }
245+
/// };
246+
/// Ok(())
247+
/// }
248+
/// ```
249+
#[unstable(feature = "unix_socket_abstract", issue = "85410")]
250+
pub fn connect_addr(&self, socket_addr: &SocketAddr) -> io::Result<()> {
251+
unsafe {
252+
cvt(libc::connect(
253+
*self.0.as_inner(),
254+
&socket_addr.addr as *const _ as *const _,
255+
socket_addr.len,
256+
))?;
257+
}
258+
Ok(())
259+
}
260+
195261
/// Creates a new independently owned handle to the underlying socket.
196262
///
197263
/// The returned `UnixDatagram` is a reference to the same socket that this
@@ -473,6 +539,40 @@ impl UnixDatagram {
473539
}
474540
}
475541

542+
/// Sends data on the socket to the specified [SocketAddr].
543+
///
544+
/// On success, returns the number of bytes written.
545+
///
546+
/// [SocketAddr]: crate::os::unix::net::SocketAddr
547+
///
548+
/// # Examples
549+
///
550+
/// ```no_run
551+
/// #![feature(unix_socket_abstract)]
552+
/// use std::os::unix::net::{UnixDatagram, SocketAddr};
553+
///
554+
/// fn main() -> std::io::Result<()> {
555+
/// let addr = SocketAddr::from_abstract_namespace(b"hidden")?;
556+
/// let sock = UnixDatagram::unbound()?;
557+
/// sock.send_to_addr(b"bacon egg and cheese", &addr).expect("send_to_addr function failed");
558+
/// Ok(())
559+
/// }
560+
/// ```
561+
#[unstable(feature = "unix_socket_abstract", issue = "85410")]
562+
pub fn send_to_addr(&self, buf: &[u8], socket_addr: &SocketAddr) -> io::Result<usize> {
563+
unsafe {
564+
let count = cvt(libc::sendto(
565+
*self.0.as_inner(),
566+
buf.as_ptr() as *const _,
567+
buf.len(),
568+
MSG_NOSIGNAL,
569+
&socket_addr.addr as *const _ as *const _,
570+
socket_addr.len,
571+
))?;
572+
Ok(count as usize)
573+
}
574+
}
575+
476576
/// Sends data on the socket to the socket's peer.
477577
///
478578
/// The peer address may be set by the `connect` method, and this method

library/std/src/os/unix/net/listener.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,43 @@ impl UnixListener {
8181
}
8282
}
8383

84+
/// Creates a new `UnixListener` bound to the specified [`socket address`].
85+
///
86+
/// [`socket address`]: crate::os::unix::net::SocketAddr
87+
///
88+
/// # Examples
89+
///
90+
/// ```no_run
91+
/// #![feature(unix_socket_abstract)]
92+
/// use std::os::unix::net::{UnixListener, SocketAddr};
93+
///
94+
/// fn main() -> std::io::Result<()> {
95+
/// let addr = SocketAddr::from_abstract_namespace(b"namespace")?; // Linux only
96+
/// let listener = match UnixListener::bind_addr(&addr) {
97+
/// Ok(sock) => sock,
98+
/// Err(err) => {
99+
/// println!("Couldn't bind: {:?}", err);
100+
/// return Err(err);
101+
/// }
102+
/// };
103+
/// Ok(())
104+
/// }
105+
/// ```
106+
#[unstable(feature = "unix_socket_abstract", issue = "85410")]
107+
pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result<UnixListener> {
108+
unsafe {
109+
let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?;
110+
cvt(libc::bind(
111+
*inner.as_inner(),
112+
&socket_addr.addr as *const _ as *const _,
113+
socket_addr.len as _,
114+
))?;
115+
cvt(libc::listen(*inner.as_inner(), 128))?;
116+
117+
Ok(UnixListener(inner))
118+
}
119+
}
120+
84121
/// Accepts a new incoming connection to this listener.
85122
///
86123
/// This function will block the calling thread until a new Unix connection

library/std/src/os/unix/net/stream.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,41 @@ impl UnixStream {
104104
}
105105
}
106106

107+
/// Connects to the socket specified by [`address`].
108+
///
109+
/// [`address`]: crate::os::unix::net::SocketAddr
110+
///
111+
/// # Examples
112+
///
113+
/// ```no_run
114+
/// #![feature(unix_socket_abstract)]
115+
/// use std::os::unix::net::{UnixStream, SocketAddr};
116+
///
117+
/// fn main() -> std::io::Result<()> {
118+
/// let addr = SocketAddr::from_abstract_namespace(b"hidden")?; // Linux only
119+
/// match UnixStream::connect_addr(&addr) {
120+
/// Ok(sock) => sock,
121+
/// Err(e) => {
122+
/// println!("Couldn't connect: {:?}", e);
123+
/// return Err(e)
124+
/// }
125+
/// };
126+
/// Ok(())
127+
/// }
128+
/// ````
129+
#[unstable(feature = "unix_socket_abstract", issue = "85410")]
130+
pub fn connect_addr(socket_addr: &SocketAddr) -> io::Result<UnixStream> {
131+
unsafe {
132+
let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?;
133+
cvt(libc::connect(
134+
*inner.as_inner(),
135+
&socket_addr.addr as *const _ as *const _,
136+
socket_addr.len,
137+
))?;
138+
Ok(UnixStream(inner))
139+
}
140+
}
141+
107142
/// Creates an unnamed pair of connected sockets.
108143
///
109144
/// Returns two `UnixStream`s which are connected to each other.

0 commit comments

Comments
 (0)