use std::io;
use std::mem::MaybeUninit;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use std::os::windows::io::{BorrowedHandle, FromRawSocket, OwnedHandle, OwnedSocket};
use crate::fs::Allocate;
use crate::io::raw::Raw;
use crate::io::source::Kind;
use crate::syscore::windows::{
AF_INET, AF_INET6, BY_HANDLE_FILE_INFORMATION, DWORD, DeviceIoControl, ERROR_BROKEN_PIPE,
ERROR_HANDLE_EOF, FILE_ACCESS_INFORMATION, FILE_ALLOCATION_INFO_CLASS, FILE_APPEND_DATA,
FILE_BEGIN, FILE_CURRENT, FILE_END_OF_FILE_INFO_CLASS, FILE_MODE_INFORMATION,
FILE_NO_INTERMEDIATE_BUFFERING, FILE_STANDARD_INFO, FILE_STANDARD_INFO_CLASS,
FILE_SYNCHRONOUS_IO_ALERT, FILE_SYNCHRONOUS_IO_NONALERT, FILE_TYPE_DISK, FILE_WRITE_DATA,
FILE_ZERO_DATA_INFORMATION, FSCTL_SET_SPARSE, FSCTL_SET_ZERO_DATA, GetFileInformationByHandle,
GetFileInformationByHandleEx, GetFileSizeEx, GetFileType, HANDLE, IN_ADDR, INVALID_SOCKET,
IO_STATUS_BLOCK, IPPROTO_TCP, MSG_PEEK, NtQueryInformationFile, OVERLAPPED, ReadFile, SD_BOTH,
SD_RECEIVE, SD_SEND, SIO_UDP_CONNRESET, SO_ACCEPTCONN, SO_MAX_MSG_SIZE, SO_TYPE,
SO_UPDATE_ACCEPT_CONTEXT, SO_UPDATE_CONNECT_CONTEXT, SOCK_DGRAM, SOCK_RAW, SOCK_RDM,
SOCK_STREAM, SOCKADDR, SOCKADDR_IN, SOCKADDR_IN6, SOCKADDR_STORAGE, SOCKET, SOCKET_ERROR,
SOL_SOCKET, STATUS_SUCCESS, SetFileInformationByHandle, SetFilePointerEx, ULONG,
WSA_FLAG_NO_HANDLE_INHERIT, WSA_FLAG_OVERLAPPED, WSAEMSGSIZE, WSAIoctl, WSASocketW, WriteFile,
bind, getsockname, getsockopt, last_error, recv, recvfrom, send, sendto, setsockopt, wsa_error,
};
fn socket_of(raw: Raw) -> SOCKET {
raw as SOCKET
}
fn handle_of(raw: Raw) -> HANDLE {
raw as usize as HANDLE
}
fn check(ret: i32) -> io::Result<()> {
if ret == SOCKET_ERROR {
Err(wsa_error())
} else {
Ok(())
}
}
fn int_option(raw: Raw, name: i32) -> io::Result<i32> {
let mut value: i32 = 0;
let mut len = size_of::<i32>() as i32;
check(unsafe {
getsockopt(
socket_of(raw),
SOL_SOCKET,
name,
core::ptr::from_mut(&mut value).cast(),
&mut len,
)
})?;
Ok(value)
}
pub(crate) fn kind_of_socket(raw: Raw) -> io::Result<Kind> {
Ok(match int_option(raw, SO_TYPE)? {
SOCK_DGRAM | SOCK_RAW | SOCK_RDM => Kind::Udp,
_ if int_option(raw, SO_ACCEPTCONN)? != 0 => Kind::TcpListener,
_ => Kind::Tcp,
})
}
pub(crate) fn kind_of(raw: Raw) -> io::Result<Kind> {
match kind_of_socket(raw) {
Err(err) if err.raw_os_error() == Some(crate::syscore::windows::WSAENOTSOCK) => {
Ok(match unsafe { GetFileType(handle_of(raw)) } {
FILE_TYPE_DISK => Kind::File,
_ => Kind::Bytes,
})
}
other => other,
}
}
pub(crate) fn synchronous(raw: Raw) -> io::Result<bool> {
let mode = file_information(raw, FILE_MODE_INFORMATION)?;
Ok(mode & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT) != 0)
}
pub(crate) struct FileMode {
pub(crate) append: bool,
pub(crate) direct: bool,
}
pub(crate) fn file_mode(raw: Raw) -> io::Result<FileMode> {
let access = file_information(raw, FILE_ACCESS_INFORMATION)?;
let mode = file_information(raw, FILE_MODE_INFORMATION)?;
Ok(FileMode {
append: access & FILE_APPEND_DATA != 0 && access & FILE_WRITE_DATA == 0,
direct: mode & FILE_NO_INTERMEDIATE_BUFFERING != 0,
})
}
fn file_information(raw: Raw, class: u32) -> io::Result<ULONG> {
let mut status = IO_STATUS_BLOCK {
Status: 0,
Information: 0,
};
let mut value: ULONG = 0;
let got = unsafe {
NtQueryInformationFile(
handle_of(raw),
&mut status,
core::ptr::from_mut(&mut value).cast(),
size_of::<ULONG>() as ULONG,
class,
)
};
if got != STATUS_SUCCESS {
return Err(io::Error::from_raw_os_error(
crate::syscore::windows::status_error(got).unwrap_or(0),
));
}
Ok(value)
}
pub(crate) fn shutdown(raw: Raw, how: std::net::Shutdown) -> io::Result<()> {
let how = match how {
std::net::Shutdown::Read => SD_RECEIVE,
std::net::Shutdown::Write => SD_SEND,
std::net::Shutdown::Both => SD_BOTH,
};
check(unsafe { crate::syscore::windows::shutdown(socket_of(raw), how) })
}
pub(crate) fn recv_ready(raw: Raw, buf: &mut [u8], peek: bool) -> io::Result<usize> {
let len = i32::try_from(buf.len()).unwrap_or(i32::MAX);
let flags = if peek { MSG_PEEK } else { 0 };
let n = unsafe { recv(socket_of(raw), buf.as_mut_ptr(), len, flags) };
if n == SOCKET_ERROR {
let err = wsa_error();
if err.raw_os_error() == Some(crate::syscore::windows::WSAESHUTDOWN) {
return Ok(0);
}
return Err(err);
}
Ok(n as usize)
}
pub(crate) fn recv_from_ready(
raw: Raw,
buf: &mut [u8],
peek: bool,
) -> io::Result<(usize, SocketAddr)> {
let len = i32::try_from(buf.len()).unwrap_or(i32::MAX);
let flags = if peek { MSG_PEEK } else { 0 };
let mut from = SOCKADDR_STORAGE::zeroed();
let mut from_len = size_of::<SOCKADDR_STORAGE>() as i32;
let n = unsafe {
recvfrom(
socket_of(raw),
buf.as_mut_ptr(),
len,
flags,
core::ptr::from_mut(&mut from).cast(),
&mut from_len,
)
};
let n = if n == SOCKET_ERROR {
let err = wsa_error();
if err.raw_os_error() != Some(WSAEMSGSIZE) {
return Err(err);
}
buf.len()
} else {
n as usize
};
let from = unsafe { socket_addr(core::ptr::from_ref(&from).cast(), from_len) }?;
Ok((n, from))
}
pub(crate) fn send_datagram_now(
raw: Raw,
bytes: &[u8],
to: Option<&SocketAddr>,
) -> io::Result<usize> {
let len = i32::try_from(bytes.len()).unwrap_or(i32::MAX);
let n = match to {
None => unsafe { send(socket_of(raw), bytes.as_ptr(), len, 0) },
Some(addr) => {
let (storage, to_len) = raw_addr(addr);
unsafe {
sendto(
socket_of(raw),
bytes.as_ptr(),
len,
0,
core::ptr::from_ref(&storage).cast(),
to_len,
)
}
}
};
if n == SOCKET_ERROR {
return Err(wsa_error());
}
Ok(n as usize)
}
pub(crate) fn adopt_accepted(listener: Raw, conn: &OwnedSocket) -> io::Result<()> {
use std::os::windows::io::AsRawSocket;
let listener = socket_of(listener);
check(unsafe {
setsockopt(
conn.as_raw_socket() as SOCKET,
SOL_SOCKET,
SO_UPDATE_ACCEPT_CONTEXT,
core::ptr::from_ref(&listener).cast(),
size_of::<SOCKET>() as i32,
)
})
}
pub(crate) fn adopt_connected(raw: Raw) -> io::Result<()> {
check(unsafe {
setsockopt(
socket_of(raw),
SOL_SOCKET,
SO_UPDATE_CONNECT_CONTEXT,
core::ptr::null(),
0,
)
})
}
pub(crate) fn tcp_socket(family: u16) -> io::Result<OwnedSocket> {
socket(family, SOCK_STREAM, IPPROTO_TCP)
}
pub(crate) fn udp_socket(family: u16) -> io::Result<OwnedSocket> {
socket(family, SOCK_DGRAM, crate::syscore::windows::IPPROTO_UDP)
}
fn socket(family: u16, ty: i32, protocol: i32) -> io::Result<OwnedSocket> {
crate::syscore::windows::winsock::start()?;
let socket = unsafe {
WSASocketW(
i32::from(family),
ty,
protocol,
core::ptr::null_mut(),
0,
WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT,
)
};
if socket == INVALID_SOCKET {
return Err(wsa_error());
}
Ok(unsafe { OwnedSocket::from_raw_socket(socket as u64) })
}
pub(crate) fn family(raw: Raw) -> io::Result<u16> {
let mut storage = SOCKADDR_STORAGE::zeroed();
let mut len = size_of::<SOCKADDR_STORAGE>() as i32;
check(unsafe {
getsockname(
socket_of(raw),
core::ptr::from_mut(&mut storage).cast(),
&mut len,
)
})?;
Ok(storage.ss_family)
}
pub(crate) fn bind_any(raw: Raw, family: u16) -> io::Result<()> {
let any = if family == AF_INET6 {
SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0)
} else {
SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0)
};
let (storage, len) = raw_addr(&any);
match check(unsafe { bind(socket_of(raw), core::ptr::from_ref(&storage).cast(), len) }) {
Err(err) if err.raw_os_error() == Some(crate::syscore::windows::WSAEINVAL) => Ok(()),
other => other,
}
}
pub(crate) fn datagram_too_long(raw: Raw, len: usize) -> Option<io::Error> {
match int_option(raw, SO_MAX_MSG_SIZE) {
Ok(max) => usize::try_from(max)
.is_ok_and(|max| len > max)
.then(|| io::Error::from_raw_os_error(crate::syscore::windows::WSAEMSGSIZE)),
Err(err) => Some(err),
}
}
pub(crate) fn udp_connreset_off(raw: Raw) -> io::Result<()> {
let socket = core::mem::ManuallyDrop::new(unsafe {
std::net::UdpSocket::from_raw_socket(socket_of(raw) as _)
});
if socket.peer_addr().is_ok() {
return Ok(());
}
let off: u32 = 0;
let mut bytes: DWORD = 0;
check(unsafe {
WSAIoctl(
socket_of(raw),
SIO_UDP_CONNRESET,
core::ptr::from_ref(&off).cast(),
size_of::<u32>() as DWORD,
core::ptr::null_mut(),
0,
&mut bytes,
core::ptr::null_mut(),
core::ptr::null(),
)
})
}
pub(crate) fn bind_inet(raw: Raw, addr: &SocketAddr) -> io::Result<()> {
let (storage, len) = raw_addr(addr);
check(unsafe { bind(socket_of(raw), core::ptr::from_ref(&storage).cast(), len) })
}
pub(crate) fn listen(raw: Raw, backlog: u32) -> io::Result<()> {
let backlog = i32::try_from(backlog).unwrap_or(i32::MAX);
check(unsafe { crate::syscore::windows::listen(socket_of(raw), backlog) })
}
pub(crate) fn raw_addr(addr: &SocketAddr) -> (SOCKADDR_STORAGE, i32) {
let mut storage = SOCKADDR_STORAGE::zeroed();
let len = match addr {
SocketAddr::V4(v4) => {
let raw = SOCKADDR_IN {
sin_family: AF_INET,
sin_port: v4.port().to_be(),
sin_addr: IN_ADDR {
s_addr: v4.ip().octets(),
},
sin_zero: [0; 8],
};
unsafe { core::ptr::write(core::ptr::from_mut(&mut storage).cast(), raw) };
size_of::<SOCKADDR_IN>()
}
SocketAddr::V6(v6) => {
let raw = SOCKADDR_IN6 {
sin6_family: AF_INET6,
sin6_port: v6.port().to_be(),
sin6_flowinfo: v6.flowinfo(),
sin6_addr: v6.ip().octets(),
sin6_scope_id: v6.scope_id(),
};
unsafe { core::ptr::write(core::ptr::from_mut(&mut storage).cast(), raw) };
size_of::<SOCKADDR_IN6>()
}
};
(storage, len as i32)
}
pub(crate) unsafe fn socket_addr(addr: *const SOCKADDR, len: i32) -> io::Result<SocketAddr> {
let len = usize::try_from(len).unwrap_or(0);
let family = if len >= 2 {
unsafe { (*addr).sa_family }
} else {
0
};
match family {
AF_INET if len >= size_of::<SOCKADDR_IN>() => {
let raw = unsafe { core::ptr::read_unaligned(addr.cast::<SOCKADDR_IN>()) };
Ok(SocketAddr::V4(SocketAddrV4::new(
Ipv4Addr::from(raw.sin_addr.s_addr),
u16::from_be(raw.sin_port),
)))
}
AF_INET6 if len >= size_of::<SOCKADDR_IN6>() => {
let raw = unsafe { core::ptr::read_unaligned(addr.cast::<SOCKADDR_IN6>()) };
Ok(SocketAddr::V6(SocketAddrV6::new(
Ipv6Addr::from(raw.sin6_addr),
u16::from_be(raw.sin6_port),
raw.sin6_flowinfo,
raw.sin6_scope_id,
)))
}
_ => Err(io::Error::new(
io::ErrorKind::InvalidData,
"the kernel gave an address of a family nuclei does not read",
)),
}
}
fn at(offset: u64) -> OVERLAPPED {
let mut ov = OVERLAPPED::zeroed();
ov.Offset = offset as u32;
ov.OffsetHigh = (offset >> 32) as u32;
ov
}
pub(super) fn read_at(raw: Raw, buf: *mut u8, len: usize, offset: u64) -> io::Result<usize> {
let mut ov = at(offset);
let mut read: DWORD = 0;
let len = u32::try_from(len).unwrap_or(u32::MAX);
if unsafe { ReadFile(handle_of(raw), buf.cast(), len, &mut read, &mut ov) } == 0 {
let err = last_error();
return match err.raw_os_error() {
Some(ERROR_HANDLE_EOF | ERROR_BROKEN_PIPE) => Ok(0),
_ => Err(err),
};
}
Ok(read as usize)
}
pub(crate) fn pread(raw: Raw, buf: &mut [u8], offset: u64) -> io::Result<usize> {
read_at(raw, buf.as_mut_ptr(), buf.len(), offset)
}
pub(crate) fn pread_uninit(
raw: Raw,
buf: &mut [MaybeUninit<u8>],
offset: u64,
) -> io::Result<usize> {
read_at(raw, buf.as_mut_ptr().cast(), buf.len(), offset)
}
pub(crate) fn pwrite_all(raw: Raw, buf: &[u8], offset: u64) -> io::Result<()> {
write_all_at(raw, buf, Some(offset))
}
pub(crate) fn append_all(raw: Raw, buf: &[u8]) -> io::Result<u64> {
write_all_at(raw, buf, None)?;
file_size(raw)
}
pub(super) fn write_all_at(raw: Raw, mut buf: &[u8], mut offset: Option<u64>) -> io::Result<()> {
while !buf.is_empty() {
let mut ov = at(offset.unwrap_or(u64::MAX));
let mut written: DWORD = 0;
let len = u32::try_from(buf.len()).unwrap_or(u32::MAX);
if unsafe {
WriteFile(
handle_of(raw),
buf.as_ptr().cast(),
len,
&mut written,
&mut ov,
)
} == 0
{
return Err(last_error());
}
if written == 0 {
return Err(io::ErrorKind::WriteZero.into());
}
buf = &buf[written as usize..];
offset = offset.map(|at| at + u64::from(written));
}
Ok(())
}
pub(crate) fn seek(raw: Raw, offset: Option<u64>) -> io::Result<u64> {
let (distance, method) = match offset {
Some(offset) => (
i64::try_from(offset).map_err(|_| io::ErrorKind::InvalidInput)?,
FILE_BEGIN,
),
None => (0, FILE_CURRENT),
};
let mut now: i64 = 0;
if unsafe { SetFilePointerEx(handle_of(raw), distance, &mut now, method) } == 0 {
return Err(last_error());
}
Ok(now as u64)
}
pub(crate) fn file_size(raw: Raw) -> io::Result<u64> {
let mut size: i64 = 0;
if unsafe { GetFileSizeEx(handle_of(raw), &mut size) } == 0 {
return Err(last_error());
}
Ok(size as u64)
}
pub(crate) fn duplicate_file(raw: Raw) -> io::Result<OwnedHandle> {
unsafe { BorrowedHandle::borrow_raw(handle_of(raw).cast()) }.try_clone_to_owned()
}
pub(crate) fn reopen_overlapped(raw: Raw) -> io::Result<OwnedHandle> {
use crate::syscore::windows::{
FILE_FLAG_OVERLAPPED, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE, GENERIC_READ,
INVALID_HANDLE_VALUE, ReOpenFile,
};
use std::os::windows::io::FromRawHandle;
let handle = unsafe {
ReOpenFile(
handle_of(raw),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_FLAG_OVERLAPPED,
)
};
if handle == INVALID_HANDLE_VALUE {
return Err(io::Error::last_os_error());
}
Ok(unsafe { OwnedHandle::from_raw_handle(handle) })
}
pub(crate) fn transmit_file_available() -> bool {
crate::syscore::windows::winsock::ext().is_ok_and(|ext| ext.transmit_file.is_some())
}
fn set_size(raw: Raw, class: i32, value: i64) -> io::Result<()> {
if unsafe {
SetFileInformationByHandle(
handle_of(raw),
class,
core::ptr::from_ref(&value).cast(),
size_of::<i64>() as DWORD,
)
} == 0
{
return Err(last_error());
}
Ok(())
}
fn control<T>(raw: Raw, code: DWORD, input: Option<&T>) -> io::Result<()> {
let mut returned: DWORD = 0;
let (ptr, len) = match input {
Some(input) => (core::ptr::from_ref(input).cast(), size_of::<T>() as DWORD),
None => (core::ptr::null(), 0),
};
if unsafe {
DeviceIoControl(
handle_of(raw),
code,
ptr,
len,
core::ptr::null_mut(),
0,
&mut returned,
core::ptr::null_mut(),
)
} == 0
{
return Err(last_error());
}
Ok(())
}
fn zero(raw: Raw, from: i64, to: i64) -> io::Result<()> {
let range = FILE_ZERO_DATA_INFORMATION {
FileOffset: from,
BeyondFinalZero: to,
};
control(raw, FSCTL_SET_ZERO_DATA, Some(&range))
}
pub(crate) fn allocate(raw: Raw, offset: u64, len: u64, mode: Allocate) -> io::Result<()> {
let past = || {
io::Error::new(
io::ErrorKind::InvalidInput,
"the range ends past the largest offset a file can have",
)
};
let end = i64::try_from(offset.saturating_add(len)).map_err(|_| past())?;
let from = i64::try_from(offset).map_err(|_| past())?;
let mut standard = FILE_STANDARD_INFO {
AllocationSize: 0,
EndOfFile: 0,
NumberOfLinks: 0,
DeletePending: 0,
Directory: 0,
};
if unsafe {
GetFileInformationByHandleEx(
handle_of(raw),
FILE_STANDARD_INFO_CLASS,
core::ptr::from_mut(&mut standard).cast(),
size_of::<FILE_STANDARD_INFO>() as DWORD,
)
} == 0
{
return Err(last_error());
}
match mode {
Allocate::Extend | Allocate::KeepSize => {
if end > standard.AllocationSize {
set_size(raw, FILE_ALLOCATION_INFO_CLASS, end)?;
}
if mode == Allocate::Extend && end > standard.EndOfFile {
set_size(raw, FILE_END_OF_FILE_INFO_CLASS, end)?;
}
Ok(())
}
Allocate::PunchHole => {
control::<()>(raw, FSCTL_SET_SPARSE, None)?;
let to = end.min(standard.EndOfFile);
if from < to {
zero(raw, from, to)
} else {
Ok(())
}
}
Allocate::ZeroRange => {
if end > standard.EndOfFile {
set_size(raw, FILE_END_OF_FILE_INFO_CLASS, end)?;
}
zero(raw, from, end)
}
}
}
fn identity(raw: Raw) -> io::Result<(u32, u64)> {
let mut info = BY_HANDLE_FILE_INFORMATION {
dwFileAttributes: 0,
ftCreationTime: [0; 2],
ftLastAccessTime: [0; 2],
ftLastWriteTime: [0; 2],
dwVolumeSerialNumber: 0,
nFileSizeHigh: 0,
nFileSizeLow: 0,
nNumberOfLinks: 0,
nFileIndexHigh: 0,
nFileIndexLow: 0,
};
if unsafe { GetFileInformationByHandle(handle_of(raw), &mut info) } == 0 {
return Err(last_error());
}
Ok((
info.dwVolumeSerialNumber,
(u64::from(info.nFileIndexHigh) << 32) | u64::from(info.nFileIndexLow),
))
}
pub(crate) fn same_file(a: Raw, b: Raw) -> io::Result<bool> {
Ok(identity(a)? == identity(b)?)
}
pub(crate) const COPIES_IN_KERNEL: bool = false;
pub(crate) fn copy_file_range(_: Raw, _: u64, _: Raw, _: u64, _: usize) -> io::Result<usize> {
Err(io::ErrorKind::Unsupported.into())
}
pub(crate) fn kernel_copy_unavailable(err: &io::Error) -> bool {
err.kind() == io::ErrorKind::Unsupported
}