| #![cfg(windows)] |
| #![cfg(not(miri))] // no socket support in Miri |
| #![feature(windows_unix_domain_sockets)] |
| // Now only test windows_unix_domain_sockets feature |
| // in the future, will test both unix and windows uds |
| use std::io::{Read, Write}; |
| use std::os::windows::net::{UnixListener, UnixStream}; |
| use std::{mem, thread}; |
| |
| macro_rules! skip_nonapplicable_oses { |
| () => { |
| // UDS have been available under Windows since Insider Preview Build |
| // 17063. "Redstone 4" (RS4, version 1803, build number 17134) is |
| // therefore the first official release to include it. |
| if !is_windows_10_v1803_or_greater() { |
| println!("Not running this test on too-old Windows."); |
| return; |
| } |
| }; |
| } |
| |
| #[test] |
| fn win_uds_smoke_bind_connect() { |
| skip_nonapplicable_oses!(); |
| |
| let tmp = std::env::temp_dir(); |
| let sock_path = tmp.join("rust-test-uds-smoke.sock"); |
| let _ = std::fs::remove_file(&sock_path); |
| let listener = UnixListener::bind(&sock_path).expect("bind failed"); |
| let sock_path_clone = sock_path.clone(); |
| let tx = thread::spawn(move || { |
| let mut stream = UnixStream::connect(&sock_path_clone).expect("connect failed"); |
| stream.write_all(b"hello").expect("write failed"); |
| }); |
| |
| let (mut stream, _) = listener.accept().expect("accept failed"); |
| let mut buf = [0; 5]; |
| stream.read_exact(&mut buf).expect("read failed"); |
| assert_eq!(&buf, b"hello"); |
| |
| tx.join().unwrap(); |
| |
| drop(listener); |
| let _ = std::fs::remove_file(&sock_path); |
| } |
| |
| #[test] |
| fn win_uds_echo() { |
| skip_nonapplicable_oses!(); |
| |
| let tmp = std::env::temp_dir(); |
| let sock_path = tmp.join("rust-test-uds-echo.sock"); |
| let _ = std::fs::remove_file(&sock_path); |
| |
| let listener = UnixListener::bind(&sock_path).expect("bind failed"); |
| let srv = thread::spawn(move || { |
| let (mut stream, _) = listener.accept().expect("accept failed"); |
| let mut buf = [0u8; 128]; |
| loop { |
| let n = match stream.read(&mut buf) { |
| Ok(0) => break, |
| Ok(n) => n, |
| Err(e) => panic!("read error: {}", e), |
| }; |
| stream.write_all(&buf[..n]).expect("write_all failed"); |
| } |
| }); |
| |
| let sock_path_clone = sock_path.clone(); |
| let cli = thread::spawn(move || { |
| let mut stream = UnixStream::connect(&sock_path_clone).expect("connect failed"); |
| let req = b"hello windows uds"; |
| stream.write_all(req).expect("write failed"); |
| let mut resp = vec![0u8; req.len()]; |
| stream.read_exact(&mut resp).expect("read failed"); |
| assert_eq!(resp, req); |
| }); |
| |
| cli.join().unwrap(); |
| srv.join().unwrap(); |
| |
| let _ = std::fs::remove_file(&sock_path); |
| } |
| |
| #[test] |
| fn win_uds_path_too_long() { |
| skip_nonapplicable_oses!(); |
| |
| let tmp = std::env::temp_dir(); |
| let long_path = tmp.join("a".repeat(200)); |
| let result = UnixListener::bind(&long_path); |
| assert!(result.is_err()); |
| let _ = std::fs::remove_file(&long_path); |
| } |
| |
| #[test] |
| fn win_uds_existing_bind() { |
| skip_nonapplicable_oses!(); |
| |
| let tmp = std::env::temp_dir(); |
| let sock_path = tmp.join("rust-test-uds-existing.sock"); |
| let _ = std::fs::remove_file(&sock_path); |
| let listener = UnixListener::bind(&sock_path).expect("bind failed"); |
| let result = UnixListener::bind(&sock_path); |
| assert!(result.is_err()); |
| drop(listener); |
| let _ = std::fs::remove_file(&sock_path); |
| } |
| |
| /// Returns true if we are currently running on Windows 10 v1803 (RS4) or greater. |
| fn is_windows_10_v1803_or_greater() -> bool { |
| is_windows_version_greater_or_equal(NTDDI_WIN10_RS4) |
| } |
| |
| /// Returns true if we are currently running on the given version of Windows |
| /// 10 (or newer). |
| fn is_windows_version_greater_or_equal(min_version: u32) -> bool { |
| is_windows_version_or_greater(HIBYTE(OSVER(min_version)), LOBYTE(OSVER(min_version)), 0, 0) |
| } |
| |
| /// Checks if we are running a version of Windows newer than the specified one. |
| fn is_windows_version_or_greater( |
| major: u8, |
| minor: u8, |
| service_pack: u8, |
| build_number: u32, |
| ) -> bool { |
| let mut osvi = OSVERSIONINFOEXW { |
| dwOSVersionInfoSize: mem::size_of::<OSVERSIONINFOEXW>() as _, |
| dwMajorVersion: u32::from(major), |
| dwMinorVersion: u32::from(minor), |
| wServicePackMajor: u16::from(service_pack), |
| dwBuildNumber: build_number, |
| ..OSVERSIONINFOEXW::default() |
| }; |
| |
| // SAFETY: this function is always safe to call. |
| let condmask = unsafe { |
| VerSetConditionMask( |
| VerSetConditionMask( |
| VerSetConditionMask( |
| VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL as _), |
| VER_MINORVERSION, |
| VER_GREATER_EQUAL as _, |
| ), |
| VER_SERVICEPACKMAJOR, |
| VER_GREATER_EQUAL as _, |
| ), |
| VER_BUILDNUMBER, |
| VER_GREATER_EQUAL as _, |
| ) |
| }; |
| |
| // SAFETY: osvi needs to point to a memory region valid for at least |
| // dwOSVersionInfoSize bytes, which is the case here. |
| (unsafe { |
| RtlVerifyVersionInfo( |
| &raw mut osvi, |
| VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, |
| condmask, |
| ) |
| }) == STATUS_SUCCESS |
| } |
| |
| #[expect(non_snake_case)] |
| const fn HIBYTE(x: u16) -> u8 { |
| ((x >> 8) & 0xFF) as u8 |
| } |
| |
| #[expect(non_snake_case)] |
| const fn LOBYTE(x: u16) -> u8 { |
| (x & 0xFF) as u8 |
| } |
| |
| #[expect(non_snake_case)] |
| const fn OSVER(x: u32) -> u16 { |
| ((x & OSVERSION_MASK) >> 16) as u16 |
| } |
| |
| // Inlined bindings because outside of `std` here. |
| |
| type NTSTATUS = i32; |
| const STATUS_SUCCESS: NTSTATUS = 0; |
| |
| #[expect(non_camel_case_types)] |
| type VER_FLAGS = u32; |
| const VER_BUILDNUMBER: VER_FLAGS = 4u32; |
| const VER_GREATER_EQUAL: VER_FLAGS = 3u32; |
| const VER_MAJORVERSION: VER_FLAGS = 2u32; |
| const VER_MINORVERSION: VER_FLAGS = 1u32; |
| const VER_SERVICEPACKMAJOR: VER_FLAGS = 32u32; |
| |
| const OSVERSION_MASK: u32 = 4294901760u32; |
| const NTDDI_WIN10_RS4: u32 = 167772165u32; |
| |
| #[expect(non_snake_case)] |
| #[repr(C)] |
| #[derive(Clone, Copy)] |
| struct OSVERSIONINFOEXW { |
| pub dwOSVersionInfoSize: u32, |
| pub dwMajorVersion: u32, |
| pub dwMinorVersion: u32, |
| pub dwBuildNumber: u32, |
| pub dwPlatformId: u32, |
| pub szCSDVersion: [u16; 128], |
| pub wServicePackMajor: u16, |
| pub wServicePackMinor: u16, |
| pub wSuiteMask: u16, |
| pub wProductType: u8, |
| pub wReserved: u8, |
| } |
| |
| impl Default for OSVERSIONINFOEXW { |
| fn default() -> Self { |
| unsafe { core::mem::zeroed() } |
| } |
| } |
| |
| windows_link::link!("ntdll.dll" "system" fn RtlVerifyVersionInfo(versioninfo : *const OSVERSIONINFOEXW, typemask : u32, conditionmask : u64) -> NTSTATUS); |
| windows_link::link!("kernel32.dll" "system" fn VerSetConditionMask(conditionmask : u64, typemask : VER_FLAGS, condition : u8) -> u64); |