From b2dcd7819a491d6a1f92c59712c03d3f8ae131b1 Mon Sep 17 00:00:00 2001 From: Andrew Golovashevich Date: Sun, 1 Mar 2026 17:37:58 +0300 Subject: [PATCH] Windows eventloop draft --- src/io/abstract/address.rs | 4 +- src/io/abstract/context.rs | 0 src/io/abstract/mod.rs | 1 + src/io/allocator.rs | 5 +- src/io/mod.rs | 8 ++- src/io/servers_context.rs | 7 +++ src/io/windows/address.rs | 74 ++++++++++++++++++++++--- src/io/windows/eventloop.rs | 104 ++++++++++++++++++++++++++++++++++++ src/io/windows/mod.rs | 4 ++ src/io/windows/task.rs | 46 ++++++++++++++++ 10 files changed, 242 insertions(+), 11 deletions(-) create mode 100644 src/io/abstract/context.rs create mode 100644 src/io/servers_context.rs create mode 100644 src/io/windows/eventloop.rs create mode 100644 src/io/windows/task.rs diff --git a/src/io/abstract/address.rs b/src/io/abstract/address.rs index d96110a..56076a1 100644 --- a/src/io/abstract/address.rs +++ b/src/io/abstract/address.rs @@ -1,4 +1,6 @@ -pub trait Address: Sized { +use std::hash::Hash; + +pub trait Address: Sized + PartialEq + Hash { fn parse(raw: &str) -> Result; fn to_string(self) -> String; diff --git a/src/io/abstract/context.rs b/src/io/abstract/context.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/io/abstract/mod.rs b/src/io/abstract/mod.rs index a64e967..c5f2726 100644 --- a/src/io/abstract/mod.rs +++ b/src/io/abstract/mod.rs @@ -1,3 +1,4 @@ mod address; +mod context; pub use address::Address; \ No newline at end of file diff --git a/src/io/allocator.rs b/src/io/allocator.rs index 7c51b1b..a415044 100644 --- a/src/io/allocator.rs +++ b/src/io/allocator.rs @@ -3,7 +3,7 @@ use std::mem::uninitialized; use std::pin::Pin; use std::ptr; -pub struct Allocator { +pub(super) struct Allocator { map: HashMap>>, } @@ -24,4 +24,7 @@ impl Allocator { .unwrap_or_else(|| unreachable!()) .as_mut(); } + + pub unsafe fn free(&mut self, p: Pin<&mut T>) { + } } diff --git a/src/io/mod.rs b/src/io/mod.rs index 15b2dac..5bc18fc 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -1,3 +1,7 @@ -mod windows; +pub mod r#abstract; mod allocator; -pub mod r#abstract; \ No newline at end of file +mod windows; +mod servers_context; + +pub type Address = windows::WindowsAddressKnown; +use allocator::Allocator; \ No newline at end of file diff --git a/src/io/servers_context.rs b/src/io/servers_context.rs new file mode 100644 index 0000000..b48f73d --- /dev/null +++ b/src/io/servers_context.rs @@ -0,0 +1,7 @@ +use super::Address; + +pub trait ServersContext { + fn iter_servers(&self) -> impl Iterator; + + fn report_ping(&mut self, addr: Address, ping_ms: u64); +} \ No newline at end of file diff --git a/src/io/windows/address.rs b/src/io/windows/address.rs index 4d7e962..e591b0f 100644 --- a/src/io/windows/address.rs +++ b/src/io/windows/address.rs @@ -1,19 +1,24 @@ use crate::io::r#abstract::Address; use crate::io::windows::errors::{format_windows_err_code_result, throw_from_windows_err_code}; use std::ffi::OsString; +use std::hash::{Hash, Hasher}; use std::mem::size_of; use std::mem::zeroed; use std::os::windows::ffi::OsStringExt; -use std::ptr::{copy_nonoverlapping, null, null_mut}; use std::ptr; +use std::ptr::{copy_nonoverlapping, null, null_mut}; +use windows::Win32::Networking::WinSock::{ + ADDRINFOW, AF_INET, AF_INET6, AF_UNSPEC, FreeAddrInfoW, GetAddrInfoW, SOCKADDR_IN, + SOCKADDR_IN6, SOCKADDR_STORAGE, WSAAddressToStringW, WSAGetLastError, +}; use windows::core::{PCWSTR, PWSTR}; -use windows::Win32::Networking::WinSock::{FreeAddrInfoW, GetAddrInfoW, WSAAddressToStringW, WSAGetLastError, ADDRINFOW, AF_INET, AF_INET6, SOCKADDR_IN, SOCKADDR_IN6, SOCKADDR_STORAGE}; -pub struct WindowsAddress { - native: SOCKADDR_STORAGE, +#[derive(Clone, Copy)] +pub struct WindowsAddressAny { + pub(super) native: SOCKADDR_STORAGE, } -impl Address for WindowsAddress { +impl Address for WindowsAddressAny { fn parse(raw: &str) -> Result { let encoded = raw.encode_utf16().collect::>(); unsafe { @@ -58,7 +63,7 @@ impl Address for WindowsAddress { } } -impl PartialEq for WindowsAddress { +impl PartialEq for WindowsAddressAny { fn eq(&self, other: &Self) -> bool { match self.native.ss_family { AF_INET => { @@ -75,8 +80,63 @@ impl PartialEq for WindowsAddress { let self_6 = *ptr::from_ref(&self.native).cast::(); let other_6 = *ptr::from_ref(&other.native).cast::(); return self_6.sin6_addr.u.Word == other_6.sin6_addr.u.Word; - } + }, _ => return false, } } } + +impl Hash for WindowsAddressAny { + fn hash(&self, state: &mut H) { + match self.native.ss_family { + AF_INET => { + state.write_u16(self.native.ss_family.0); + unsafe { + let self_4 = *ptr::from_ref(&self.native).cast::(); + state.write_u32(self_4.sin_addr.S_un.S_addr); + } + } + AF_INET6 => { + state.write_u16(self.native.ss_family.0); + unsafe { + let self_6 = *ptr::from_ref(&self.native).cast::(); + state.write(&self_6.sin6_addr.u.Byte); + } + } + _ => state.write_u16(AF_UNSPEC.0), + } + } +} + +#[derive(Clone, Copy)] +pub struct WindowsAddressKnown { + pub(super) any: WindowsAddressAny, +} + +impl Address for WindowsAddressKnown { + fn parse(raw: &str) -> Result { + let any = WindowsAddressAny::parse(raw)?; + match any.native.ss_family { + AF_INET | AF_INET6 => return Result::Ok(Self { any }), + _ => return Result::Err("Address resolved to unsupported AF".to_owned()), + } + } + + fn to_string(self) -> String { + return self.any.to_string(); + } +} + +impl PartialEq for WindowsAddressKnown { + fn eq(&self, other: &Self) -> bool { + return self.any == other.any; + } +} + +impl Eq for WindowsAddressKnown {} + +impl Hash for WindowsAddressKnown { + fn hash(&self, state: &mut H) { + self.any.hash(state) + } +} diff --git a/src/io/windows/eventloop.rs b/src/io/windows/eventloop.rs new file mode 100644 index 0000000..96235b3 --- /dev/null +++ b/src/io/windows/eventloop.rs @@ -0,0 +1,104 @@ +use crate::io::allocator::Allocator; +use crate::io::servers_context::ServersContext; +use crate::io::windows::task::IoTask; +use crate::io::windows::WindowsAddressKnown; +use std::pin::Pin; +use std::ptr::NonNull; +use windows::Win32::Networking::WinSock::{ + WSARecvFrom, WSASendTo, WSASocketW, AF_UNSPEC, IPPROTO_ICMP, SOCKADDR, + SOCKADDR_STORAGE, SOCK_RAW, WSA_FLAG_NO_HANDLE_INHERIT, WSA_FLAG_OVERLAPPED, +}; +use windows::Win32::System::IO::OVERLAPPED; + +pub unsafe fn run_eventloop(ctx: *mut Ctx) { + let mut heap = Allocator::>>::new(); + + let socket = WSASocketW( + AF_UNSPEC.0 as i32, + SOCK_RAW.0, + IPPROTO_ICMP.0, + None, + 0, + WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT, + ) + .unwrap(); + + let mut recv_task = NonNull::from_mut(&mut heap).as_mut().alloc(); + let mut in_addr_size = 0i32; + let mut in_flags = 0u32; + + loop { + for mut address in (*ctx).iter_servers() { + let mut data = NonNull::from_mut(&mut heap).as_mut().alloc(); + data.init(CallbackData { + ctx: NonNull::new_unchecked(ctx), + allocator: NonNull::from_mut(&mut heap), + address, + }); + + let overlapped = data.get_overlapped(); + let address = + NonNull::from_ref(&data.get_extra().address.any.native).cast::(); + WSASendTo( + socket, + data.as_ref().get_buffer_descriptors(), + None, + 0, + Some(address.as_ptr()), + size_of::() as i32, + Some(overlapped), + Some(sending_done::), + ); + + let overlapped = recv_task.get_overlapped(); + let address = + NonNull::from_mut(&mut recv_task.get_extra().address.any.native).cast::(); + WSARecvFrom( + socket, + data.as_ref().get_buffer_descriptors(), + None, + &mut in_flags, + Some(address.as_ptr()), + Some(&mut in_addr_size), + Some(overlapped), + Some(receiving_done::), + ); + } + } +} + +struct CallbackData { + ctx: NonNull, + allocator: NonNull>>, + address: WindowsAddressKnown, +} + +unsafe extern "system" fn sending_done( + dwerror: u32, + cbtransferred: u32, + lpoverlapped: *mut OVERLAPPED, + dwflags: u32, +) { + let mut task = Pin::new_unchecked( + lpoverlapped + .cast::>>() + .as_mut() + .unwrap(), + ); + task.get_extra().allocator.as_mut().free(task); +} + +unsafe extern "system" fn receiving_done( + dwerror: u32, + cbtransferred: u32, + lpoverlapped: *mut OVERLAPPED, + dwflags: u32, +) { + let mut task = Pin::new_unchecked( + lpoverlapped + .cast::>>() + .as_mut() + .unwrap(), + ); + task.get_extra().allocator.as_mut().free(task); +} diff --git a/src/io/windows/mod.rs b/src/io/windows/mod.rs index 1250081..14cb5cd 100644 --- a/src/io/windows/mod.rs +++ b/src/io/windows/mod.rs @@ -1,2 +1,6 @@ mod address; mod errors; +mod eventloop; +mod task; + +pub use address::{WindowsAddressAny, WindowsAddressKnown}; \ No newline at end of file diff --git a/src/io/windows/task.rs b/src/io/windows/task.rs new file mode 100644 index 0000000..1d8d40e --- /dev/null +++ b/src/io/windows/task.rs @@ -0,0 +1,46 @@ +use std::marker::PhantomPinned; +use std::mem::zeroed; +use std::pin::Pin; +use std::ptr::NonNull; +use windows::Win32::Networking::WinSock::WSABUF; +use windows::Win32::System::IO::OVERLAPPED; +use windows::core::PSTR; + +const _BUFF_LEN: usize = 100; + +#[repr(C)] +pub struct IoTask { + sys: OVERLAPPED, + __pinned: PhantomPinned, + buff_d: [WSABUF; 1], + buff: [u8; _BUFF_LEN], + extra: E, +} + +impl IoTask { + pub unsafe fn init(self: &mut Pin<&mut Self>, extra: E) { + let self_unwrapped = Pin::into_inner_unchecked(NonNull::from_mut(self).read()); + self_unwrapped.sys = zeroed(); + self_unwrapped.buff_d[0].len = self_unwrapped.buff.len() as u32; + self_unwrapped.buff_d[0].buf = PSTR(self_unwrapped.buff.as_mut_ptr()); + self_unwrapped.extra = extra + } + + pub const BUFFER_LEN: usize = _BUFF_LEN; + + pub fn get_buffer<'s>(self: &'s mut Pin<&mut Self>) -> &'s mut [u8; _BUFF_LEN] { + return &mut unsafe { Pin::into_inner_unchecked(NonNull::from_mut(self).read()) }.buff; + } + + pub fn get_buffer_descriptors<'s>(self: &'s Pin<&Self>) -> &'s [WSABUF] { + return &unsafe { Pin::into_inner_unchecked(NonNull::from_ref(self).read()) }.buff_d; + } + + pub fn get_extra<'s>(self: &'s mut Pin<&mut Self>) -> &'s mut E { + return &mut unsafe { Pin::into_inner_unchecked(NonNull::from_mut(self).read()) }.extra; + } + + pub fn get_overlapped(self: &mut Pin<&mut Self>) -> *mut OVERLAPPED { + return &mut unsafe { Pin::into_inner_unchecked(NonNull::from_mut(self).read()) }.sys; + } +}