Skip to content

Commit 1689fcd

Browse files
committed
feat(windows): add Unix domain socket support
This commit introduces initial, unstable support for Unix domain sockets (UDS) on Windows, behind the `windows_unix_domain_sockets` feature gate Added types: - `std::os::windows::net::SocketAddr`: represents a UDS address with support for pathname addresses (abstract and unnamed are parsed but not yet fully supported). - `std::os::windows::net::UnixListener`: server-side UDS listener. - `std::os::windows::net::UnixStream`: client/server stream for UDS. Key features: - Binding and connecting using filesystem paths. - Basic I/O via `Read`/`Write`. - Address querying (`local_addr`, `peer_addr`). - Non-blocking mode, timeouts, and socket duplication. - Includes basic test coverage for smoke, echo, path length, and bind reuse.
1 parent 35a31ba commit 1689fcd

File tree

7 files changed

+1035
-0
lines changed

7 files changed

+1035
-0
lines changed

‎library/std/src/os/windows/mod.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
pub mod ffi;
3030
pub mod fs;
3131
pub mod io;
32+
#[unstable(feature = "windows_unix_domain_sockets", issue = "150487")]
33+
pub mod net;
3234
pub mod process;
3335
pub mod raw;
3436
pub mod thread;
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
#![unstable(feature = "windows_unix_domain_sockets", issue = "150487")]
2+
use crate::bstr::ByteStr;
3+
use crate::ffi::OsStr;
4+
use crate::path::Path;
5+
#[cfg(not(doc))]
6+
use crate::sys::c::{AF_UNIX, SOCKADDR, SOCKADDR_UN};
7+
use crate::sys::cvt_nz;
8+
use crate::{fmt, io, mem, ptr};
9+
10+
#[cfg(not(doc))]
11+
pub fn sockaddr_un(path: &Path) -> io::Result<(SOCKADDR_UN, usize)> {
12+
// SAFETY: All zeros is a valid representation for `sockaddr_un`.
13+
let mut addr: SOCKADDR_UN = unsafe { mem::zeroed() };
14+
addr.sun_family = AF_UNIX;
15+
16+
// path to UTF-8 bytes
17+
let bytes = path
18+
.to_str()
19+
.ok_or(io::const_error!(io::ErrorKind::InvalidInput, "path must be valid UTF-8"))?
20+
.as_bytes();
21+
if bytes.len() >= addr.sun_path.len() {
22+
return Err(io::const_error!(io::ErrorKind::InvalidInput, "path too long"));
23+
}
24+
// SAFETY: `bytes` and `addr.sun_path` are not overlapping and
25+
// both point to valid memory.
26+
// NOTE: We zeroed the memory above, so the path is already null
27+
// terminated.
28+
unsafe {
29+
ptr::copy_nonoverlapping(bytes.as_ptr(), addr.sun_path.as_mut_ptr().cast(), bytes.len())
30+
};
31+
32+
let len = SUN_PATH_OFFSET + bytes.len() + 1;
33+
Ok((addr, len))
34+
}
35+
#[cfg(not(doc))]
36+
const SUN_PATH_OFFSET: usize = mem::offset_of!(SOCKADDR_UN, sun_path);
37+
pub struct SocketAddr {
38+
#[cfg(not(doc))]
39+
pub(super) addr: SOCKADDR_UN,
40+
pub(super) len: u32, // Use u32 here as same as libc::socklen_t
41+
}
42+
impl fmt::Debug for SocketAddr {
43+
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
44+
match self.address() {
45+
AddressKind::Unnamed => write!(fmt, "(unnamed)"),
46+
AddressKind::Abstract(name) => write!(fmt, "{name:?} (abstract)"),
47+
AddressKind::Pathname(path) => write!(fmt, "{path:?} (pathname)"),
48+
}
49+
}
50+
}
51+
52+
impl SocketAddr {
53+
#[cfg(not(doc))]
54+
pub(super) fn new<F>(f: F) -> io::Result<SocketAddr>
55+
where
56+
F: FnOnce(*mut SOCKADDR, *mut i32) -> i32,
57+
{
58+
unsafe {
59+
let mut addr: SOCKADDR_UN = mem::zeroed();
60+
let mut len = mem::size_of::<SOCKADDR_UN>() as i32;
61+
cvt_nz(f(&raw mut addr as *mut _, &mut len))?;
62+
SocketAddr::from_parts(addr, len)
63+
}
64+
}
65+
#[cfg(not(doc))]
66+
pub(super) fn from_parts(addr: SOCKADDR_UN, len: i32) -> io::Result<SocketAddr> {
67+
if addr.sun_family != AF_UNIX {
68+
Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid address family"))
69+
} else if len < SUN_PATH_OFFSET as _ || len > mem::size_of::<SOCKADDR_UN>() as _ {
70+
Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid address length"))
71+
} else {
72+
Ok(SocketAddr { addr, len: len as _ })
73+
}
74+
}
75+
76+
/// Returns the contents of this address if it is a `pathname` address.
77+
///
78+
/// # Examples
79+
///
80+
/// With a pathname:
81+
///
82+
/// ```no_run
83+
/// #![feature(windows_unix_domain_sockets)]
84+
/// use std::os::windows::net::UnixListener;
85+
/// use std::path::Path;
86+
///
87+
/// fn main() -> std::io::Result<()> {
88+
/// let socket = UnixListener::bind("/tmp/sock")?;
89+
/// let addr = socket.local_addr().expect("Couldn't get local address");
90+
/// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock")));
91+
/// Ok(())
92+
/// }
93+
/// ```
94+
pub fn as_pathname(&self) -> Option<&Path> {
95+
if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None }
96+
}
97+
98+
/// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path.
99+
///
100+
/// # Errors
101+
///
102+
/// Returns an error if the path is longer than `SUN_LEN` or if it contains
103+
/// NULL bytes.
104+
///
105+
/// # Examples
106+
///
107+
/// ```no_run
108+
/// #![feature(windows_unix_domain_sockets)]
109+
/// use std::os::windows::net::SocketAddr;
110+
/// use std::path::Path;
111+
///
112+
/// # fn main() -> std::io::Result<()> {
113+
/// let address = SocketAddr::from_pathname("/path/to/socket")?;
114+
/// assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket")));
115+
/// # Ok(())
116+
/// # }
117+
/// ```
118+
///
119+
/// Creating a `SocketAddr` with a NULL byte results in an error.
120+
///
121+
/// ```no_run
122+
/// #![feature(windows_unix_domain_sockets)]
123+
/// use std::os::windows::net::SocketAddr;
124+
///
125+
/// assert!(SocketAddr::from_pathname("/path/with/\0/bytes").is_err());
126+
/// ```
127+
pub fn from_pathname<P>(path: P) -> io::Result<SocketAddr>
128+
where
129+
P: AsRef<Path>,
130+
{
131+
sockaddr_un(path.as_ref()).map(|(addr, len)| SocketAddr { addr, len: len as _ })
132+
}
133+
fn address(&self) -> AddressKind<'_> {
134+
let len = self.len as usize - SUN_PATH_OFFSET;
135+
let path = unsafe { mem::transmute::<&[i8], &[u8]>(&self.addr.sun_path) };
136+
137+
if len == 0 {
138+
AddressKind::Unnamed
139+
} else if self.addr.sun_path[0] == 0 {
140+
AddressKind::Abstract(ByteStr::from_bytes(&path[1..len]))
141+
} else {
142+
AddressKind::Pathname(unsafe {
143+
OsStr::from_encoded_bytes_unchecked(&path[..len - 1]).as_ref()
144+
})
145+
}
146+
}
147+
148+
/// Returns `true` if the address is unnamed.
149+
///
150+
/// # Examples
151+
///
152+
/// A named address:
153+
///
154+
/// ```no_run
155+
/// #![feature(windows_unix_domain_sockets)]
156+
/// use std::os::windows::net::UnixListener;
157+
///
158+
/// fn main() -> std::io::Result<()> {
159+
/// let socket = UnixListener::bind("/tmp/sock")?;
160+
/// let addr = socket.local_addr().expect("Couldn't get local address");
161+
/// assert_eq!(addr.is_unnamed(), false);
162+
/// Ok(())
163+
/// }
164+
/// ```
165+
pub fn is_unnamed(&self) -> bool {
166+
matches!(self.address(), AddressKind::Unnamed)
167+
}
168+
}
169+
enum AddressKind<'a> {
170+
Unnamed,
171+
Pathname(&'a Path),
172+
Abstract(&'a ByteStr),
173+
}

0 commit comments

Comments
 (0)