Skip to content

Commit 6cc0a76

Browse files
committed
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](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 c102653 + 15b1198 commit 6cc0a76

File tree

5 files changed

+416
-4
lines changed

5 files changed

+416
-4
lines changed

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

+86-2
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 {
@@ -198,6 +198,31 @@ impl SocketAddr {
198198
if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None }
199199
}
200200

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

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

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

+107-1
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,41 @@ 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};
122+
///
123+
/// fn main() -> std::io::Result<()> {
124+
/// let sock1 = UnixDatagram::bind("path/to/socket")?;
125+
/// let addr = sock1.local_addr()?;
126+
///
127+
/// let sock2 = match UnixDatagram::bind_addr(&addr) {
128+
/// Ok(sock) => sock,
129+
/// Err(err) => {
130+
/// println!("Couldn't bind: {:?}", err);
131+
/// return Err(err);
132+
/// }
133+
/// };
134+
/// Ok(())
135+
/// }
136+
/// ```
137+
#[unstable(feature = "unix_socket_abstract", issue = "85410")]
138+
pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result<UnixDatagram> {
139+
unsafe {
140+
let socket = UnixDatagram::unbound()?;
141+
cvt(libc::bind(
142+
socket.as_raw_fd(),
143+
&socket_addr.addr as *const _ as *const _,
144+
socket_addr.len as _,
145+
))?;
146+
Ok(socket)
147+
}
148+
}
149+
115150
/// Creates a Unix Datagram socket which is not bound to any address.
116151
///
117152
/// # Examples
@@ -156,7 +191,7 @@ impl UnixDatagram {
156191
Ok((UnixDatagram(i1), UnixDatagram(i2)))
157192
}
158193

159-
/// Connects the socket to the specified address.
194+
/// Connects the socket to the specified path address.
160195
///
161196
/// The [`send`] method may be used to send data to the specified address.
162197
/// [`recv`] and [`recv_from`] will only receive data from that address.
@@ -192,6 +227,41 @@ impl UnixDatagram {
192227
Ok(())
193228
}
194229

230+
/// Connects the socket to an address.
231+
///
232+
/// # Examples
233+
///
234+
/// ```no_run
235+
/// #![feature(unix_socket_abstract)]
236+
/// use std::os::unix::net::{UnixDatagram};
237+
///
238+
/// fn main() -> std::io::Result<()> {
239+
/// let bound = UnixDatagram::bind("/path/to/socket")?;
240+
/// let addr = bound.local_addr()?;
241+
///
242+
/// let sock = UnixDatagram::unbound()?;
243+
/// match sock.connect_addr(&addr) {
244+
/// Ok(sock) => sock,
245+
/// Err(e) => {
246+
/// println!("Couldn't connect: {:?}", e);
247+
/// return Err(e)
248+
/// }
249+
/// };
250+
/// Ok(())
251+
/// }
252+
/// ```
253+
#[unstable(feature = "unix_socket_abstract", issue = "85410")]
254+
pub fn connect_addr(&self, socket_addr: &SocketAddr) -> io::Result<()> {
255+
unsafe {
256+
cvt(libc::connect(
257+
self.as_raw_fd(),
258+
&socket_addr.addr as *const _ as *const _,
259+
socket_addr.len,
260+
))?;
261+
}
262+
Ok(())
263+
}
264+
195265
/// Creates a new independently owned handle to the underlying socket.
196266
///
197267
/// The returned `UnixDatagram` is a reference to the same socket that this
@@ -473,6 +543,42 @@ impl UnixDatagram {
473543
}
474544
}
475545

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

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

+38
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,44 @@ 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};
93+
///
94+
/// fn main() -> std::io::Result<()> {
95+
/// let listener1 = UnixListener::bind("path/to/socket")?;
96+
/// let addr = listener1.local_addr()?;
97+
///
98+
/// let listener2 = match UnixListener::bind_addr(&addr) {
99+
/// Ok(sock) => sock,
100+
/// Err(err) => {
101+
/// println!("Couldn't bind: {:?}", err);
102+
/// return Err(err);
103+
/// }
104+
/// };
105+
/// Ok(())
106+
/// }
107+
/// ```
108+
#[unstable(feature = "unix_socket_abstract", issue = "85410")]
109+
pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result<UnixListener> {
110+
unsafe {
111+
let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?;
112+
cvt(libc::bind(
113+
inner.as_raw_fd(),
114+
&socket_addr.addr as *const _ as *const _,
115+
socket_addr.len as _,
116+
))?;
117+
cvt(libc::listen(inner.as_raw_fd(), 128))?;
118+
Ok(UnixListener(inner))
119+
}
120+
}
121+
84122
/// Accepts a new incoming connection to this listener.
85123
///
86124
/// This function will block the calling thread until a new Unix connection

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

+37
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,43 @@ impl UnixStream {
106106
}
107107
}
108108

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

0 commit comments

Comments
 (0)