Skip to content

Commit 20b6d40

Browse files
committed
Windows: Add support for Unix sockets
Newer versions of Windows support AF_UNIX stream sockets. This change adds Windows support for the `SockAddr::unix` function and the `Domain::UNIX` constant. Since Unix sockets are now available on all tier1 platforms, this also removes `all` feature requirement from the `SockAddr::unix` function.
1 parent 1c67209 commit 20b6d40

File tree

6 files changed

+133
-74
lines changed

6 files changed

+133
-74
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ add new tests.
3333

3434
All types and methods that are available on all tier 1 platforms are defined in
3535
the first level of the source, i.e. `src/*.rs` files. Additional API that is
36-
platform specific, e.g. `Domain::UNIX`, is defined in `src/sys/*.rs` and only
36+
platform specific, e.g. `Domain::VSOCK`, is defined in `src/sys/*.rs` and only
3737
for the platforms that support it. For API that is not available on all tier 1
3838
platforms the `all` feature is used, to indicate to the user that they're using
3939
API that might is not available on all platforms.

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ impl Domain {
151151
/// Domain for IPv6 communication, corresponding to `AF_INET6`.
152152
pub const IPV6: Domain = Domain(sys::AF_INET6);
153153

154+
/// Domain for Unix socket communication, corresponding to `AF_UNIX`.
155+
pub const UNIX: Domain = Domain(sys::AF_UNIX);
156+
154157
/// Returns the correct domain for `address`.
155158
pub const fn for_address(address: SocketAddr) -> Domain {
156159
match address {

src/sockaddr.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::mem::{self, size_of, MaybeUninit};
22
use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
3+
use std::path::Path;
34
use std::{fmt, io};
45

56
use crate::sys::{
@@ -208,6 +209,18 @@ impl SockAddr {
208209
_ => None,
209210
}
210211
}
212+
213+
/// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path.
214+
///
215+
/// # Failure
216+
///
217+
/// Returns an error if the path is longer than `SUN_LEN`.
218+
pub fn unix<P>(path: P) -> io::Result<SockAddr>
219+
where
220+
P: AsRef<Path>,
221+
{
222+
crate::sys::unix_sockaddr(path.as_ref())
223+
}
211224
}
212225

213226
impl From<SocketAddr> for SockAddr {

src/sys/unix.rs

Lines changed: 48 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ use std::num::NonZeroU32;
2525
)
2626
))]
2727
use std::num::NonZeroUsize;
28-
#[cfg(feature = "all")]
2928
use std::os::unix::ffi::OsStrExt;
3029
#[cfg(all(
3130
feature = "all",
@@ -40,7 +39,6 @@ use std::os::unix::io::RawFd;
4039
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
4140
#[cfg(feature = "all")]
4241
use std::os::unix::net::{UnixDatagram, UnixListener, UnixStream};
43-
#[cfg(feature = "all")]
4442
use std::path::Path;
4543
#[cfg(not(all(target_os = "redox", not(feature = "all"))))]
4644
use std::ptr;
@@ -58,7 +56,7 @@ use crate::{Domain, Protocol, SockAddr, TcpKeepalive, Type};
5856
pub(crate) use libc::c_int;
5957

6058
// Used in `Domain`.
61-
pub(crate) use libc::{AF_INET, AF_INET6};
59+
pub(crate) use libc::{AF_INET, AF_INET6, AF_UNIX};
6260
// Used in `Type`.
6361
#[cfg(all(feature = "all", not(target_os = "redox")))]
6462
pub(crate) use libc::SOCK_RAW;
@@ -204,10 +202,6 @@ type IovLen = c_int;
204202

205203
/// Unix only API.
206204
impl Domain {
207-
/// Domain for Unix socket communication, corresponding to `AF_UNIX`.
208-
#[cfg_attr(docsrs, doc(cfg(unix)))]
209-
pub const UNIX: Domain = Domain(libc::AF_UNIX);
210-
211205
/// Domain for low-level packet interface, corresponding to `AF_PACKET`.
212206
#[cfg(all(
213207
feature = "all",
@@ -436,70 +430,57 @@ impl<'a> MaybeUninitSlice<'a> {
436430
}
437431
}
438432

439-
/// Unix only API.
440-
impl SockAddr {
441-
/// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path.
442-
///
443-
/// # Failure
444-
///
445-
/// Returns an error if the path is longer than `SUN_LEN`.
446-
#[cfg(feature = "all")]
447-
#[cfg_attr(docsrs, doc(cfg(all(unix, feature = "all"))))]
448-
#[allow(unused_unsafe)] // TODO: replace with `unsafe_op_in_unsafe_fn` once stable.
449-
pub fn unix<P>(path: P) -> io::Result<SockAddr>
450-
where
451-
P: AsRef<Path>,
452-
{
453-
unsafe {
454-
SockAddr::init(|storage, len| {
455-
// Safety: `SockAddr::init` zeros the address, which is a valid
456-
// representation.
457-
let storage: &mut libc::sockaddr_un = unsafe { &mut *storage.cast() };
458-
let len: &mut socklen_t = unsafe { &mut *len };
459-
460-
let bytes = path.as_ref().as_os_str().as_bytes();
461-
let too_long = match bytes.first() {
462-
None => false,
463-
// linux abstract namespaces aren't null-terminated
464-
Some(&0) => bytes.len() > storage.sun_path.len(),
465-
Some(_) => bytes.len() >= storage.sun_path.len(),
466-
};
467-
if too_long {
468-
return Err(io::Error::new(
469-
io::ErrorKind::InvalidInput,
470-
"path must be shorter than SUN_LEN",
471-
));
472-
}
433+
#[allow(unused_unsafe)] // TODO: replace with `unsafe_op_in_unsafe_fn` once stable.
434+
pub(crate) fn unix_sockaddr(path: &Path) -> io::Result<SockAddr> {
435+
unsafe {
436+
SockAddr::init(|storage, len| {
437+
// Safety: `SockAddr::init` zeros the address, which is a valid
438+
// representation.
439+
let storage: &mut libc::sockaddr_un = unsafe { &mut *storage.cast() };
440+
let len: &mut socklen_t = unsafe { &mut *len };
441+
442+
let bytes = path.as_os_str().as_bytes();
443+
let too_long = match bytes.first() {
444+
None => false,
445+
// linux abstract namespaces aren't null-terminated
446+
Some(&0) => bytes.len() > storage.sun_path.len(),
447+
Some(_) => bytes.len() >= storage.sun_path.len(),
448+
};
449+
if too_long {
450+
return Err(io::Error::new(
451+
io::ErrorKind::InvalidInput,
452+
"path must be shorter than SUN_LEN",
453+
));
454+
}
473455

474-
storage.sun_family = libc::AF_UNIX as sa_family_t;
475-
// Safety: `bytes` and `addr.sun_path` are not overlapping and
476-
// both point to valid memory.
477-
// `SockAddr::init` zeroes the memory, so the path is already
478-
// null terminated.
479-
unsafe {
480-
ptr::copy_nonoverlapping(
481-
bytes.as_ptr(),
482-
storage.sun_path.as_mut_ptr() as *mut u8,
483-
bytes.len(),
484-
)
456+
storage.sun_family = libc::AF_UNIX as sa_family_t;
457+
// Safety: `bytes` and `addr.sun_path` are not overlapping and
458+
// both point to valid memory.
459+
// `SockAddr::init` zeroes the memory, so the path is already
460+
// null terminated.
461+
unsafe {
462+
ptr::copy_nonoverlapping(
463+
bytes.as_ptr(),
464+
storage.sun_path.as_mut_ptr() as *mut u8,
465+
bytes.len(),
466+
)
467+
};
468+
469+
let base = storage as *const _ as usize;
470+
let path = &storage.sun_path as *const _ as usize;
471+
let sun_path_offset = path - base;
472+
let length = sun_path_offset
473+
+ bytes.len()
474+
+ match bytes.first() {
475+
Some(&0) | None => 0,
476+
Some(_) => 1,
485477
};
478+
*len = length as socklen_t;
486479

487-
let base = storage as *const _ as usize;
488-
let path = &storage.sun_path as *const _ as usize;
489-
let sun_path_offset = path - base;
490-
let length = sun_path_offset
491-
+ bytes.len()
492-
+ match bytes.first() {
493-
Some(&0) | None => 0,
494-
Some(_) => 1,
495-
};
496-
*len = length as socklen_t;
497-
498-
Ok(())
499-
})
500-
}
501-
.map(|(_, addr)| addr)
480+
Ok(())
481+
})
502482
}
483+
.map(|(_, addr)| addr)
503484
}
504485

505486
impl SockAddr {

src/sys/windows.rs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use std::marker::PhantomData;
1212
use std::mem::{self, size_of, MaybeUninit};
1313
use std::net::{self, Ipv4Addr, Ipv6Addr, Shutdown};
1414
use std::os::windows::prelude::*;
15+
use std::path::Path;
1516
use std::sync::Once;
1617
use std::time::{Duration, Instant};
1718
use std::{ptr, slice};
@@ -44,7 +45,7 @@ pub(crate) use winapi::ctypes::c_int;
4445
pub(crate) const MSG_TRUNC: c_int = 0x01;
4546

4647
// Used in `Domain`.
47-
pub(crate) use winapi::shared::ws2def::{AF_INET, AF_INET6};
48+
pub(crate) use winapi::shared::ws2def::{AF_INET, AF_INET6, AF_UNIX};
4849
// Used in `Type`.
4950
pub(crate) use winapi::shared::ws2def::{SOCK_DGRAM, SOCK_STREAM};
5051
#[cfg(feature = "all")]
@@ -735,6 +736,52 @@ pub(crate) fn from_in6_addr(addr: in6_addr) -> Ipv6Addr {
735736
Ipv6Addr::from(*unsafe { addr.u.Byte() })
736737
}
737738

739+
/// This type is not yet in winapi.
740+
#[repr(C)]
741+
#[allow(non_camel_case_types)]
742+
struct sockaddr_un {
743+
pub sun_family: sa_family_t,
744+
pub sun_path: [u8; 108],
745+
}
746+
747+
#[allow(unused_unsafe)] // TODO: replace with `unsafe_op_in_unsafe_fn` once stable.
748+
pub(crate) fn unix_sockaddr(path: &Path) -> io::Result<SockAddr> {
749+
unsafe {
750+
SockAddr::init(|storage, len| {
751+
// Safety: `SockAddr::init` zeros the address, which is a valid
752+
// representation.
753+
let storage: &mut crate::sys::sockaddr_un = unsafe { &mut *storage.cast() };
754+
let len: &mut socklen_t = unsafe { &mut *len };
755+
756+
let bytes = path
757+
.to_str()
758+
.ok_or_else(|| {
759+
io::Error::new(io::ErrorKind::InvalidInput, "path must be valid UTF-8")
760+
})?
761+
.as_bytes();
762+
763+
if bytes.len() > storage.sun_path.len() {
764+
return Err(io::Error::new(
765+
io::ErrorKind::InvalidInput,
766+
"path must be shorter than SUN_LEN",
767+
));
768+
}
769+
770+
storage.sun_family = crate::sys::AF_UNIX as sa_family_t;
771+
storage.sun_path[..bytes.len()].copy_from_slice(bytes);
772+
773+
let base = storage as *const _ as usize;
774+
let path = &storage.sun_path as *const _ as usize;
775+
let sun_path_offset = path - base;
776+
let length = sun_path_offset + bytes.len();
777+
*len = length as socklen_t;
778+
779+
Ok(())
780+
})
781+
}
782+
.map(|(_, addr)| addr)
783+
}
784+
738785
/// Windows only API.
739786
impl crate::Socket {
740787
/// Sets `HANDLE_FLAG_INHERIT` using `SetHandleInformation`.

tests/socket.rs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ use std::fs::File;
1111
use std::io;
1212
#[cfg(not(target_os = "redox"))]
1313
use std::io::IoSlice;
14-
#[cfg(all(unix, feature = "all"))]
1514
use std::io::Read;
1615
use std::io::Write;
1716
use std::mem::{self, MaybeUninit};
@@ -35,7 +34,6 @@ use std::os::windows::io::AsRawSocket;
3534
use std::str;
3635
use std::thread;
3736
use std::time::Duration;
38-
#[cfg(all(unix, feature = "all"))]
3937
use std::{env, fs};
4038

4139
#[cfg(windows)]
@@ -65,7 +63,6 @@ fn domain_fmt_debug() {
6563
let tests = &[
6664
(Domain::IPV4, "AF_INET"),
6765
(Domain::IPV6, "AF_INET6"),
68-
#[cfg(unix)]
6966
(Domain::UNIX, "AF_UNIX"),
7067
#[cfg(all(feature = "all", any(target_os = "fuchsia", target_os = "linux")))]
7168
(Domain::PACKET, "AF_PACKET"),
@@ -133,7 +130,6 @@ fn from_invalid_raw_fd_should_panic() {
133130
}
134131

135132
#[test]
136-
#[cfg(all(unix, feature = "all"))]
137133
fn socket_address_unix() {
138134
let string = "/tmp/socket";
139135
let addr = SockAddr::unix(string).unwrap();
@@ -432,9 +428,28 @@ fn pair() {
432428
assert_eq!(&buf[..n], DATA);
433429
}
434430

431+
fn unix_sockets_supported() -> bool {
432+
#[cfg(windows)]
433+
{
434+
// Only some versions of Windows support Unix sockets.
435+
match Socket::new(Domain::UNIX, Type::STREAM, None) {
436+
Ok(_) => {}
437+
Err(err)
438+
if err.raw_os_error() == Some(winapi::um::winsock2::WSAEAFNOSUPPORT as i32) =>
439+
{
440+
return false;
441+
}
442+
Err(err) => panic!("socket error: {}", err),
443+
}
444+
}
445+
true
446+
}
447+
435448
#[test]
436-
#[cfg(all(feature = "all", unix))]
437449
fn unix() {
450+
if !unix_sockets_supported() {
451+
return;
452+
}
438453
let mut path = env::temp_dir();
439454
path.push("socket2");
440455
let _ = fs::remove_dir_all(&path);

0 commit comments

Comments
 (0)