From da211e975776fbced36a51c5eccaf364cb09bdb9 Mon Sep 17 00:00:00 2001 From: AliReza Beigy Date: Thu, 22 Jan 2026 21:45:26 +0330 Subject: [PATCH 01/11] Add Windows support (socket types, OpenSSL linking, IPv6 fallback) --- crates/slipstream-client/src/dns/path.rs | 2 +- crates/slipstream-client/src/dns/poll.rs | 6 +- crates/slipstream-client/src/dns/resolver.rs | 6 +- crates/slipstream-client/src/dns/response.rs | 2 +- crates/slipstream-client/src/runtime.rs | 4 +- crates/slipstream-client/src/runtime/path.rs | 2 +- crates/slipstream-client/src/runtime/setup.rs | 18 ++++- crates/slipstream-ffi/Cargo.toml | 1 + crates/slipstream-ffi/build.rs | 19 ++++- crates/slipstream-ffi/src/lib.rs | 3 +- crates/slipstream-ffi/src/picoquic.rs | 5 ++ crates/slipstream-ffi/src/runtime.rs | 81 +++++++++++++++++++ crates/slipstream-server/src/server.rs | 4 +- crates/slipstream-server/src/udp_fallback.rs | 16 +++- 14 files changed, 147 insertions(+), 22 deletions(-) diff --git a/crates/slipstream-client/src/dns/path.rs b/crates/slipstream-client/src/dns/path.rs index 5042aa91..9f7f9428 100644 --- a/crates/slipstream-client/src/dns/path.rs +++ b/crates/slipstream-client/src/dns/path.rs @@ -51,7 +51,7 @@ pub(crate) fn add_paths( return Ok(()); } - let mut local_storage: libc::sockaddr_storage = unsafe { std::mem::zeroed() }; + let mut local_storage: slipstream_ffi::SockaddrStorage = unsafe { std::mem::zeroed() }; let ret = unsafe { picoquic_get_path_addr(cnx, 0, 1, &mut local_storage) }; if ret != 0 { return Ok(()); diff --git a/crates/slipstream-client/src/dns/poll.rs b/crates/slipstream-client/src/dns/poll.rs index 83e10cd4..63481457 100644 --- a/crates/slipstream-client/src/dns/poll.rs +++ b/crates/slipstream-client/src/dns/poll.rs @@ -35,7 +35,7 @@ pub(crate) async fn send_poll_queries( cnx: *mut picoquic_cnx_t, udp: &TokioUdpSocket, config: &ClientConfig<'_>, - local_addr_storage: &mut libc::sockaddr_storage, + local_addr_storage: &mut slipstream_ffi::SockaddrStorage, dns_id: &mut u16, resolver: &mut ResolverState, remaining: &mut usize, @@ -54,8 +54,8 @@ pub(crate) async fn send_poll_queries( } let mut send_length: libc::size_t = 0; - let mut addr_to: libc::sockaddr_storage = unsafe { std::mem::zeroed() }; - let mut addr_from: libc::sockaddr_storage = unsafe { std::mem::zeroed() }; + let mut addr_to: slipstream_ffi::SockaddrStorage = unsafe { std::mem::zeroed() }; + let mut addr_from: slipstream_ffi::SockaddrStorage = unsafe { std::mem::zeroed() }; let mut if_index: libc::c_int = 0; let ret = unsafe { picoquic_prepare_packet_ex( diff --git a/crates/slipstream-client/src/dns/resolver.rs b/crates/slipstream-client/src/dns/resolver.rs index f5c45dce..e0960449 100644 --- a/crates/slipstream-client/src/dns/resolver.rs +++ b/crates/slipstream-client/src/dns/resolver.rs @@ -10,8 +10,8 @@ use super::debug::DebugMetrics; pub(crate) struct ResolverState { pub(crate) addr: SocketAddr, - pub(crate) storage: libc::sockaddr_storage, - pub(crate) local_addr_storage: Option, + pub(crate) storage: slipstream_ffi::SockaddrStorage, + pub(crate) local_addr_storage: Option, pub(crate) mode: ResolverMode, pub(crate) added: bool, pub(crate) path_id: libc::c_int, @@ -93,7 +93,7 @@ pub(crate) fn reset_resolver_path(resolver: &mut ResolverState) { } pub(crate) fn sockaddr_storage_to_socket_addr( - storage: &libc::sockaddr_storage, + storage: &slipstream_ffi::SockaddrStorage, ) -> Result { slipstream_ffi::sockaddr_storage_to_socket_addr(storage).map_err(ClientError::new) } diff --git a/crates/slipstream-client/src/dns/response.rs b/crates/slipstream-client/src/dns/response.rs index a0f7152a..6e3e826c 100644 --- a/crates/slipstream-client/src/dns/response.rs +++ b/crates/slipstream-client/src/dns/response.rs @@ -14,7 +14,7 @@ const MAX_POLL_BURST: usize = PICOQUIC_PACKET_LOOP_RECV_MAX; pub(crate) struct DnsResponseContext<'a> { pub(crate) quic: *mut picoquic_quic_t, - pub(crate) local_addr_storage: &'a libc::sockaddr_storage, + pub(crate) local_addr_storage: &'a slipstream_ffi::SockaddrStorage, pub(crate) resolvers: &'a mut [ResolverState], } diff --git a/crates/slipstream-client/src/runtime.rs b/crates/slipstream-client/src/runtime.rs index d7145542..96a3d963 100644 --- a/crates/slipstream-client/src/runtime.rs +++ b/crates/slipstream-client/src/runtime.rs @@ -355,8 +355,8 @@ pub async fn run_client(config: &ClientConfig<'_>) -> Result { for _ in 0..packet_loop_send_max { let current_time = unsafe { picoquic_current_time() }; let mut send_length: libc::size_t = 0; - let mut addr_to: libc::sockaddr_storage = unsafe { std::mem::zeroed() }; - let mut addr_from: libc::sockaddr_storage = unsafe { std::mem::zeroed() }; + let mut addr_to: slipstream_ffi::SockaddrStorage = unsafe { std::mem::zeroed() }; + let mut addr_from: slipstream_ffi::SockaddrStorage = unsafe { std::mem::zeroed() }; let mut if_index: libc::c_int = 0; let mut log_cid = picoquic_connection_id_t { id: [0; PICOQUIC_CONNECTION_ID_MAX_SIZE], diff --git a/crates/slipstream-client/src/runtime/path.rs b/crates/slipstream-client/src/runtime/path.rs index 966a4e75..4e01731c 100644 --- a/crates/slipstream-client/src/runtime/path.rs +++ b/crates/slipstream-client/src/runtime/path.rs @@ -86,7 +86,7 @@ pub(crate) fn drain_path_events( } fn path_peer_addr(cnx: *mut picoquic_cnx_t, unique_path_id: u64) -> Option { - let mut storage: libc::sockaddr_storage = unsafe { std::mem::zeroed() }; + let mut storage: slipstream_ffi::SockaddrStorage = unsafe { std::mem::zeroed() }; let ret = unsafe { picoquic_get_path_addr(cnx, unique_path_id, 2, &mut storage) }; if ret != 0 { return None; diff --git a/crates/slipstream-client/src/runtime/setup.rs b/crates/slipstream-client/src/runtime/setup.rs index 90850ebe..f8524ba1 100644 --- a/crates/slipstream-client/src/runtime/setup.rs +++ b/crates/slipstream-client/src/runtime/setup.rs @@ -1,7 +1,9 @@ use crate::error::ClientError; use slipstream_core::net::{bind_first_resolved, bind_tcp_listener_addr, bind_udp_socket_addr}; -use std::net::{Ipv6Addr, SocketAddr, SocketAddrV6}; -use tokio::net::{TcpListener as TokioTcpListener, UdpSocket as TokioUdpSocket}; +use socket2::{Domain, Protocol, SockAddr, Socket, Type}; +use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; +use tokio::net::{lookup_host, TcpListener as TokioTcpListener, UdpSocket as TokioUdpSocket}; +use tracing::warn; pub(crate) fn compute_mtu(domain_len: usize) -> Result { if domain_len >= 240 { @@ -19,8 +21,16 @@ pub(crate) fn compute_mtu(domain_len: usize) -> Result { } pub(crate) async fn bind_udp_socket() -> Result { - let bind_addr = SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0)); - bind_udp_socket_addr(bind_addr, "UDP socket").map_err(map_io) + // Try IPv6 dual-stack first (works on most systems), fall back to IPv4 + let bind_addr_v6 = SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0)); + match bind_udp_socket_addr(bind_addr_v6, "UDP socket") { + Ok(socket) => Ok(socket), + Err(_) => { + // Fall back to IPv4 if IPv6 is not available (common on Windows) + let bind_addr_v4 = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)); + bind_udp_socket_addr(bind_addr_v4, "UDP socket").map_err(map_io) + } + } } pub(crate) async fn bind_tcp_listener( diff --git a/crates/slipstream-ffi/Cargo.toml b/crates/slipstream-ffi/Cargo.toml index b0bea19b..5b4a9364 100644 --- a/crates/slipstream-ffi/Cargo.toml +++ b/crates/slipstream-ffi/Cargo.toml @@ -9,6 +9,7 @@ readme = "../../README.md" [dependencies] libc = "0.2" +winapi = { version = "0.3", features = ["winsock2", "ws2def", "ws2ipdef"] } openssl-sys = { version = "0.9", optional = true, features = ["vendored"] } slipstream-core = { path = "../slipstream-core" } diff --git a/crates/slipstream-ffi/build.rs b/crates/slipstream-ffi/build.rs index 6fdbad4e..c6f03d45 100644 --- a/crates/slipstream-ffi/build.rs +++ b/crates/slipstream-ffi/build.rs @@ -85,6 +85,7 @@ fn main() -> Result<(), Box> { let openssl_paths = resolve_openssl_paths(); let target = env::var("TARGET").unwrap_or_default(); + let is_windows = target.contains("windows") || target.contains("pc-windows"); let auto_build = env_flag("PICOQUIC_AUTO_BUILD", true); let explicit_picoquic_include = env::var_os("PICOQUIC_INCLUDE_DIR").is_some(); let explicit_picoquic_lib = env::var_os("PICOQUIC_LIB_DIR").is_some(); @@ -226,13 +227,25 @@ fn main() -> Result<(), Box> { println!("cargo:rustc-link-lib=static=ssl"); println!("cargo:rustc-link-lib=static=crypto"); } else { - println!("cargo:rustc-link-lib=dylib=ssl"); - println!("cargo:rustc-link-lib=dylib=crypto"); + if is_windows { + if let Ok(openssl_lib_dir) = env::var("OPENSSL_LIB_DIR") { + println!("cargo:rustc-link-search=native={}", openssl_lib_dir); + } + println!("cargo:rustc-link-lib=dylib=libssl"); + println!("cargo:rustc-link-lib=dylib=libcrypto"); + } else { + println!("cargo:rustc-link-lib=dylib=ssl"); + println!("cargo:rustc-link-lib=dylib=crypto"); + } } } if !target.contains("android") { - println!("cargo:rustc-link-lib=dylib=pthread"); + if is_windows { + println!("cargo:rustc-link-lib=dylib=ws2_32"); + } else { + println!("cargo:rustc-link-lib=dylib=pthread"); + } } else { maybe_link_android_builtins(&target, &cc); } diff --git a/crates/slipstream-ffi/src/lib.rs b/crates/slipstream-ffi/src/lib.rs index a54df7b5..f58102b0 100644 --- a/crates/slipstream-ffi/src/lib.rs +++ b/crates/slipstream-ffi/src/lib.rs @@ -39,5 +39,6 @@ pub struct ClientConfig<'a> { pub use runtime::{ abort_stream_bidi, configure_quic, configure_quic_with_custom, sockaddr_storage_to_socket_addr, socket_addr_to_storage, take_crypto_errors, take_stateless_packet_for_cid, - write_stream_or_reset, QuicGuard, SLIPSTREAM_FILE_CANCEL_ERROR, SLIPSTREAM_INTERNAL_ERROR, + write_stream_or_reset, QuicGuard, SockaddrStorage, SLIPSTREAM_FILE_CANCEL_ERROR, + SLIPSTREAM_INTERNAL_ERROR, }; diff --git a/crates/slipstream-ffi/src/picoquic.rs b/crates/slipstream-ffi/src/picoquic.rs index 698c3e63..e12d9dd3 100644 --- a/crates/slipstream-ffi/src/picoquic.rs +++ b/crates/slipstream-ffi/src/picoquic.rs @@ -1,6 +1,11 @@ #![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)] +#[cfg(not(windows))] use libc::{c_char, c_int, c_uint, c_void, size_t, sockaddr, sockaddr_storage}; +#[cfg(windows)] +use libc::{c_char, c_int, c_uint, c_void, size_t, sockaddr}; +#[cfg(windows)] +use winapi::shared::ws2def::SOCKADDR_STORAGE as sockaddr_storage; pub const PICOQUIC_CONNECTION_ID_MAX_SIZE: usize = 20; pub const PICOQUIC_MAX_PACKET_SIZE: usize = 1536; diff --git a/crates/slipstream-ffi/src/runtime.rs b/crates/slipstream-ffi/src/runtime.rs index 7c572e8f..9ecb0c18 100644 --- a/crates/slipstream-ffi/src/runtime.rs +++ b/crates/slipstream-ffi/src/runtime.rs @@ -8,7 +8,14 @@ use crate::picoquic::{ picoquic_set_preemptive_repeat_policy, picoquic_set_stream_data_consumption_mode, picoquic_stop_sending, slipstream_take_stateless_packet_for_cid, PICOQUIC_MAX_PACKET_SIZE, }; +#[cfg(not(windows))] use libc::{c_char, c_int, c_ulong, size_t, sockaddr_storage}; +#[cfg(windows)] +use winapi::shared::ws2def::{SOCKADDR_STORAGE as sockaddr_storage, SOCKADDR_IN, AF_INET, AF_INET6}; +#[cfg(windows)] +use winapi::shared::ws2ipdef::SOCKADDR_IN6_LH; +#[cfg(windows)] +use libc::{c_char, c_int, c_ulong, size_t}; use slipstream_core::tcp::stream_write_buffer_bytes; use std::ffi::CStr; use std::io::Write; @@ -124,6 +131,13 @@ pub fn take_crypto_errors() -> Vec { errors } +// Re-export sockaddr_storage for use by other crates +#[cfg(not(windows))] +pub use libc::sockaddr_storage as SockaddrStorage; +#[cfg(windows)] +pub use winapi::shared::ws2def::SOCKADDR_STORAGE as SockaddrStorage; + +#[cfg(not(windows))] pub fn socket_addr_to_storage(addr: SocketAddr) -> sockaddr_storage { match addr { SocketAddr::V4(addr) => { @@ -180,6 +194,7 @@ pub fn socket_addr_to_storage(addr: SocketAddr) -> sockaddr_storage { } } +#[cfg(not(windows))] pub fn sockaddr_storage_to_socket_addr(storage: &sockaddr_storage) -> Result { match storage.ss_family as libc::c_int { libc::AF_INET => { @@ -262,3 +277,69 @@ pub unsafe fn abort_stream_bidi(cnx: *mut picoquic_cnx_t, stream_id: u64, app_er let _ = picoquic_stop_sending(cnx, stream_id, app_error); let _ = picoquic_reset_stream(cnx, stream_id, app_error); } + +#[cfg(windows)] +pub fn socket_addr_to_storage(addr: SocketAddr) -> sockaddr_storage { + match addr { + SocketAddr::V4(addr) => { + let mut storage: sockaddr_storage = unsafe { std::mem::zeroed() }; + unsafe { + let sockaddr_ptr = &mut storage as *mut _ as *mut SOCKADDR_IN; + (*sockaddr_ptr).sin_family = AF_INET as u16; + (*sockaddr_ptr).sin_port = addr.port().to_be(); + *(*sockaddr_ptr).sin_addr.S_un.S_addr_mut() = u32::from_ne_bytes(addr.ip().octets()); + } + storage + } + SocketAddr::V6(addr) => { + let mut storage: sockaddr_storage = unsafe { std::mem::zeroed() }; + unsafe { + let sockaddr_ptr = &mut storage as *mut _ as *mut SOCKADDR_IN6_LH; + (*sockaddr_ptr).sin6_family = AF_INET6 as u16; + (*sockaddr_ptr).sin6_port = addr.port().to_be(); + (*sockaddr_ptr).sin6_flowinfo = addr.flowinfo(); + // Copy IPv6 address bytes directly using raw pointer + let addr_bytes = addr.ip().octets(); + let dest_ptr = &mut (*sockaddr_ptr).sin6_addr as *mut _ as *mut u8; + std::ptr::copy_nonoverlapping(addr_bytes.as_ptr(), dest_ptr, 16); + // Set scope_id via the union + let scope_ptr = &mut (*sockaddr_ptr).u as *mut _ as *mut u32; + *scope_ptr = addr.scope_id(); + } + storage + } + } +} + +#[cfg(windows)] +pub fn sockaddr_storage_to_socket_addr(storage: &sockaddr_storage) -> Result { + let family = storage.ss_family as i32; + match family { + AF_INET => { + let addr_in: &SOCKADDR_IN = + unsafe { &*(storage as *const _ as *const SOCKADDR_IN) }; + let ip = Ipv4Addr::from(unsafe { addr_in.sin_addr.S_un.S_addr().to_ne_bytes() }); + let port = u16::from_be(addr_in.sin_port); + Ok(SocketAddr::V4(SocketAddrV4::new(ip, port))) + } + AF_INET6 => { + let addr_in6: &SOCKADDR_IN6_LH = + unsafe { &*(storage as *const _ as *const SOCKADDR_IN6_LH) }; + // Read IPv6 address bytes directly using raw pointer + let src_ptr = &addr_in6.sin6_addr as *const _ as *const u8; + let mut ip_bytes: [u8; 16] = [0; 16]; + unsafe { std::ptr::copy_nonoverlapping(src_ptr, ip_bytes.as_mut_ptr(), 16) }; + let ip = Ipv6Addr::from(ip_bytes); + let port = u16::from_be(addr_in6.sin6_port); + // Read scope_id via the union + let scope_id = unsafe { *(&addr_in6.u as *const _ as *const u32) }; + Ok(SocketAddr::V6(SocketAddrV6::new( + ip, + port, + addr_in6.sin6_flowinfo, + scope_id, + ))) + } + _ => Err("Unsupported sockaddr family".to_string()), + } +} diff --git a/crates/slipstream-server/src/server.rs b/crates/slipstream-server/src/server.rs index d6e1718a..cceb3228 100644 --- a/crates/slipstream-server/src/server.rs +++ b/crates/slipstream-server/src/server.rs @@ -379,8 +379,8 @@ pub async fn run_server(config: &ServerConfig) -> Result { for slot in slots.iter_mut() { let mut send_length = 0usize; - let mut addr_to: libc::sockaddr_storage = unsafe { std::mem::zeroed() }; - let mut addr_from: libc::sockaddr_storage = unsafe { std::mem::zeroed() }; + let mut addr_to: slipstream_ffi::SockaddrStorage = unsafe { std::mem::zeroed() }; + let mut addr_from: slipstream_ffi::SockaddrStorage = unsafe { std::mem::zeroed() }; let mut if_index: libc::c_int = 0; if slot.payload_override.is_none() && slot.rcode.is_none() && !slot.cnx.is_null() { diff --git a/crates/slipstream-server/src/udp_fallback.rs b/crates/slipstream-server/src/udp_fallback.rs index 085d90b5..d62f752e 100644 --- a/crates/slipstream-server/src/udp_fallback.rs +++ b/crates/slipstream-server/src/udp_fallback.rs @@ -29,7 +29,7 @@ pub(crate) struct PacketContext<'a> { pub(crate) domains: &'a [&'a str], pub(crate) quic: *mut picoquic_quic_t, pub(crate) current_time: u64, - pub(crate) local_addr_storage: &'a libc::sockaddr_storage, + pub(crate) local_addr_storage: &'a slipstream_ffi::SockaddrStorage, } /// Tracks per-peer routing for UDP fallback based on DNS decoding outcomes. @@ -56,6 +56,7 @@ mod session; pub(crate) use decode::handle_packet; +#[cfg(not(windows))] fn dummy_sockaddr_storage() -> libc::sockaddr_storage { socket_addr_to_storage(SocketAddr::new( IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)), @@ -63,5 +64,18 @@ fn dummy_sockaddr_storage() -> libc::sockaddr_storage { )) } +#[cfg(windows)] +fn dummy_sockaddr_storage() -> slipstream_ffi::SockaddrStorage { + use std::net::{Ipv6Addr, SocketAddrV6}; + slipstream_ffi::socket_addr_to_storage( + std::net::SocketAddr::V6(SocketAddrV6::new( + Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), + 12345, + 0, + 0, + )) + ) +} + #[cfg(test)] mod tests; From 3c3886dcd136bc8f420d78cc8d8224134843068f Mon Sep 17 00:00:00 2001 From: AliReza Beigy Date: Thu, 22 Jan 2026 22:13:13 +0330 Subject: [PATCH 02/11] Fix Windows IPv4 byte order: use network byte order (from_be_bytes) --- crates/slipstream-ffi/src/runtime.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/slipstream-ffi/src/runtime.rs b/crates/slipstream-ffi/src/runtime.rs index 9ecb0c18..632c5ce1 100644 --- a/crates/slipstream-ffi/src/runtime.rs +++ b/crates/slipstream-ffi/src/runtime.rs @@ -287,7 +287,7 @@ pub fn socket_addr_to_storage(addr: SocketAddr) -> sockaddr_storage { let sockaddr_ptr = &mut storage as *mut _ as *mut SOCKADDR_IN; (*sockaddr_ptr).sin_family = AF_INET as u16; (*sockaddr_ptr).sin_port = addr.port().to_be(); - *(*sockaddr_ptr).sin_addr.S_un.S_addr_mut() = u32::from_ne_bytes(addr.ip().octets()); + *(*sockaddr_ptr).sin_addr.S_un.S_addr_mut() = u32::from_be_bytes(addr.ip().octets()); } storage } @@ -318,7 +318,7 @@ pub fn sockaddr_storage_to_socket_addr(storage: &sockaddr_storage) -> Result { let addr_in: &SOCKADDR_IN = unsafe { &*(storage as *const _ as *const SOCKADDR_IN) }; - let ip = Ipv4Addr::from(unsafe { addr_in.sin_addr.S_un.S_addr().to_ne_bytes() }); + let ip = Ipv4Addr::from(addr_in.sin_addr.S_un.S_addr()); let port = u16::from_be(addr_in.sin_port); Ok(SocketAddr::V4(SocketAddrV4::new(ip, port))) } From 2c0214b394737bab8c7c1e77b13285cbf3c72dfd Mon Sep 17 00:00:00 2001 From: AliReza Beigy Date: Sat, 24 Jan 2026 00:04:23 +0330 Subject: [PATCH 03/11] refactor: Update build process and linking for windows --- Cargo.lock | 24 ++++ crates/slipstream-ffi/Cargo.toml | 3 + crates/slipstream-ffi/build.rs | 1 + crates/slipstream-ffi/build/cc.rs | 156 +++++++++++++++++++----- crates/slipstream-ffi/build/picoquic.rs | 8 +- crates/slipstream-ffi/src/runtime.rs | 2 +- scripts/build_picoquic.sh | 62 +++++++++- 7 files changed, 220 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88e99e9e..0625bdaf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -453,9 +453,11 @@ dependencies = [ name = "slipstream-ffi" version = "0.1.0" dependencies = [ + "cc", "libc", "openssl-sys", "slipstream-core", + "winapi", ] [[package]] @@ -664,6 +666,28 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-link" version = "0.2.1" diff --git a/crates/slipstream-ffi/Cargo.toml b/crates/slipstream-ffi/Cargo.toml index 5b4a9364..3b1564d9 100644 --- a/crates/slipstream-ffi/Cargo.toml +++ b/crates/slipstream-ffi/Cargo.toml @@ -7,6 +7,9 @@ license = "Apache-2.0" repository = "https://github.com/Mygod/slipstream-rust" readme = "../../README.md" +[build-dependencies] +cc = "1.0" + [dependencies] libc = "0.2" winapi = { version = "0.3", features = ["winsock2", "ws2def", "ws2ipdef"] } diff --git a/crates/slipstream-ffi/build.rs b/crates/slipstream-ffi/build.rs index c6f03d45..6baa6058 100644 --- a/crates/slipstream-ffi/build.rs +++ b/crates/slipstream-ffi/build.rs @@ -243,6 +243,7 @@ fn main() -> Result<(), Box> { if !target.contains("android") { if is_windows { println!("cargo:rustc-link-lib=dylib=ws2_32"); + println!("cargo:rustc-link-lib=dylib=bcrypt"); } else { println!("cargo:rustc-link-lib=dylib=pthread"); } diff --git a/crates/slipstream-ffi/build/cc.rs b/crates/slipstream-ffi/build/cc.rs index 8202ce40..2b0c3e6a 100644 --- a/crates/slipstream-ffi/build/cc.rs +++ b/crates/slipstream-ffi/build/cc.rs @@ -4,12 +4,40 @@ use std::process::Command; pub(crate) fn resolve_cc(target: &str) -> String { if target.contains("android") { - env::var("RUST_ANDROID_GRADLE_CC") + return env::var("RUST_ANDROID_GRADLE_CC") .or_else(|_| env::var("CC")) - .unwrap_or_else(|_| "cc".to_string()) - } else { - env::var("CC").unwrap_or_else(|_| "cc".to_string()) + .unwrap_or_else(|_| "cc".to_string()); + } + + if let Ok(cc) = env::var("CC") { + return cc; + } + + if target.contains("windows") || target.contains("pc-windows") { + let gcc_candidates = [ + "C:\\Strawberry\\c\\bin\\gcc.exe", + "C:\\mingw64\\bin\\gcc.exe", + "C:\\msys64\\mingw64\\bin\\gcc.exe", + ]; + + for candidate in &gcc_candidates { + if Path::new(candidate).exists() { + return candidate.to_string(); + } + } + } + + let builder = cc::Build::new(); + let compiler = builder.get_compiler(); + let path = compiler.path(); + let path_str = path.to_string_lossy().to_string(); + + // Verify the compiler actually exists + if !path.exists() { + panic!("Compiler not found at: {}. Please install a C compiler (MinGW-w64, MSVC Build Tools, or Strawberry Perl).", path_str); } + + path_str } pub(crate) fn resolve_ar(target: &str, cc: &str) -> String { @@ -21,6 +49,21 @@ pub(crate) fn resolve_ar(target: &str, cc: &str) -> String { if let Ok(ar) = env::var("AR") { return ar; } + if target.contains("windows") || target.contains("pc-windows") { + if cc.contains("gcc") || cc.contains("mingw") { + if let Some(gcc_dir) = Path::new(cc).parent() { + let ar_path = gcc_dir.join("ar.exe"); + if ar_path.exists() { + return ar_path.to_string_lossy().to_string(); + } + } + return "ar".to_string(); + } + if target.contains("msvc") { + return "lib.exe".to_string(); + } + } + // For non-Windows targets, look for llvm-ar or ar in the compiler directory if let Some(dir) = Path::new(cc).parent() { let candidate = dir.join("llvm-ar"); if candidate.exists() { @@ -39,14 +82,34 @@ pub(crate) fn create_archive( archive: &Path, objects: &[PathBuf], ) -> Result<(), Box> { - let mut command = Command::new(ar); - command.arg("crus").arg(archive); - for obj in objects { - command.arg(obj); - } - let status = command.status()?; - if !status.success() { - return Err("Failed to create static archive for slipstream objects.".into()); + let target = env::var("TARGET").unwrap_or_default(); + let is_windows = target.contains("windows") || target.contains("pc-windows"); + + if is_windows && ar.contains("lib.exe") && ar != "ar" { + // MSVC: use lib.exe + let mut lib_cmd = Command::new("lib.exe"); + lib_cmd + .arg("/OUT:") + .arg(archive) + .arg("/NOLOGO"); + for obj in objects { + lib_cmd.arg(obj); + } + let status = lib_cmd.status()?; + if !status.success() { + return Err("Failed to create static archive for slipstream objects.".into()); + } + } else { + // GCC/Clang/MinGW: use ar + let mut command = Command::new(ar); + command.arg("crus").arg(archive); + for obj in objects { + command.arg(obj); + } + let status = command.status()?; + if !status.success() { + return Err("Failed to create static archive for slipstream objects.".into()); + } } Ok(()) } @@ -57,15 +120,30 @@ pub(crate) fn compile_cc( output: &Path, picoquic_include_dir: &Path, ) -> Result<(), Box> { - let status = Command::new(cc) - .arg("-c") - .arg("-fPIC") - .arg(source) - .arg("-o") - .arg(output) - .arg("-I") - .arg(picoquic_include_dir) - .status()?; + let target = env::var("TARGET").unwrap_or_default(); + let is_windows = target.contains("windows") || target.contains("pc-windows"); + let is_gcc = cc.contains("gcc") || cc.contains("mingw"); + + let mut cmd = Command::new(cc); + + if is_windows && !is_gcc { + // MSVC: cl.exe /c /Fo:output source.c /I include_dir + cmd.arg("/c") + .arg(format!("/Fo:{}", output.display())) + .arg(source) + .arg(format!("/I{}", picoquic_include_dir.display())); + } else { + // GCC/Clang: cc -c -fPIC -o output source.c -I include_dir + cmd.arg("-c") + .arg("-fPIC") + .arg("-o") + .arg(output) + .arg(source) + .arg("-I") + .arg(picoquic_include_dir); + } + + let status = cmd.status()?; if !status.success() { return Err(format!("Failed to compile {}.", source.display()).into()); } @@ -78,17 +156,33 @@ pub(crate) fn compile_cc_with_includes( output: &Path, include_dirs: &[&Path], ) -> Result<(), Box> { - let mut command = Command::new(cc); - command - .arg("-c") - .arg("-fPIC") - .arg(source) - .arg("-o") - .arg(output); - for dir in include_dirs { - command.arg("-I").arg(dir); + let target = env::var("TARGET").unwrap_or_default(); + let is_windows = target.contains("windows") || target.contains("pc-windows"); + let is_gcc = cc.contains("gcc") || cc.contains("mingw"); + + let mut cmd = Command::new(cc); + + if is_windows && !is_gcc { + // MSVC: cl.exe /c /Fo:output source.c /I include_dir1 /I include_dir2 + cmd.arg("/c") + .arg(format!("/Fo:{}", output.display())) + .arg(source); + for dir in include_dirs { + cmd.arg(format!("/I{}", dir.display())); + } + } else { + // GCC/Clang: cc -c -fPIC -o output source.c -I include_dir1 -I include_dir2 + cmd.arg("-c") + .arg("-fPIC") + .arg("-o") + .arg(output) + .arg(source); + for dir in include_dirs { + cmd.arg("-I").arg(dir); + } } - let status = command.status()?; + + let status = cmd.status()?; if !status.success() { return Err(format!("Failed to compile {}.", source.display()).into()); } diff --git a/crates/slipstream-ffi/build/picoquic.rs b/crates/slipstream-ffi/build/picoquic.rs index 8af5ab12..d7f6f7c1 100644 --- a/crates/slipstream-ffi/build/picoquic.rs +++ b/crates/slipstream-ffi/build/picoquic.rs @@ -28,7 +28,13 @@ pub(crate) fn build_picoquic( .map(PathBuf::from) .unwrap_or_else(|| root.join(".picoquic-build")); - let mut command = Command::new(script); + let mut command = if cfg!(windows) { + let mut cmd = Command::new("bash"); + cmd.arg(&script); + cmd + } else { + Command::new(script) + }; command .env("PICOQUIC_DIR", picoquic_dir) .env("PICOQUIC_BUILD_DIR", build_dir) diff --git a/crates/slipstream-ffi/src/runtime.rs b/crates/slipstream-ffi/src/runtime.rs index 632c5ce1..55fc4aa1 100644 --- a/crates/slipstream-ffi/src/runtime.rs +++ b/crates/slipstream-ffi/src/runtime.rs @@ -318,7 +318,7 @@ pub fn sockaddr_storage_to_socket_addr(storage: &sockaddr_storage) -> Result { let addr_in: &SOCKADDR_IN = unsafe { &*(storage as *const _ as *const SOCKADDR_IN) }; - let ip = Ipv4Addr::from(addr_in.sin_addr.S_un.S_addr()); + let ip = Ipv4Addr::from(unsafe { *addr_in.sin_addr.S_un.S_addr() }); let port = u16::from_be(addr_in.sin_port); Ok(SocketAddr::V4(SocketAddrV4::new(ip, port))) } diff --git a/scripts/build_picoquic.sh b/scripts/build_picoquic.sh index d8bd79d4..66e85d8f 100755 --- a/scripts/build_picoquic.sh +++ b/scripts/build_picoquic.sh @@ -12,6 +12,17 @@ if [[ ! -d "${PICOQUIC_DIR}" ]]; then exit 1 fi +IS_WINDOWS=0 +case "${OSTYPE:-}" in + msys*|cygwin*) IS_WINDOWS=1 ;; +esac +if [[ "$IS_WINDOWS" == "0" ]]; then + UNAME_S=$(uname -s 2>/dev/null || echo "") + case "$UNAME_S" in + MSYS*|MINGW*|CYGWIN*) IS_WINDOWS=1 ;; + esac +fi + CMAKE_ARGS=( "-DCMAKE_BUILD_TYPE=${BUILD_TYPE}" "-DPICOQUIC_FETCH_PTLS=${FETCH_PTLS}" @@ -20,6 +31,22 @@ CMAKE_ARGS=( ) BUILD_TARGET=() + +if [[ "$IS_WINDOWS" == "1" ]]; then + CMAKE_ARGS+=("-DBUILD_TESTING=OFF") + CMAKE_ARGS+=("-Dpicoquic_BUILD_TESTS=OFF") + + if [[ -d "/c/Program Files/Microsoft Visual Studio/2022" ]] || [[ -d "C:/Program Files/Microsoft Visual Studio/2022" ]]; then + CMAKE_ARGS+=("-G" "Visual Studio 17 2022" "-A" "x64") + echo "Using Visual Studio 2022 generator" >&2 + elif [[ -d "/c/Program Files (x86)/Microsoft Visual Studio/2019" ]] || [[ -d "C:/Program Files (x86)/Microsoft Visual Studio/2019" ]]; then + CMAKE_ARGS+=("-G" "Visual Studio 16 2019" "-A" "x64") + echo "Using Visual Studio 2019 generator" >&2 + fi + + BUILD_TARGET=(--target picoquic-core picotls-core picotls-fusion picotls-minicrypto picotls-openssl) +fi + if [[ -n "${CARGO_FEATURE_PICOQUIC_MINIMAL_BUILD:-}" ]]; then case "${CARGO_FEATURE_PICOQUIC_MINIMAL_BUILD,,}" in 1|true|yes|on) @@ -82,8 +109,37 @@ if [[ -n "${OPENSSL_USE_STATIC_LIBS:-}" ]]; then fi cmake -S "${PICOQUIC_DIR}" -B "${BUILD_DIR}" "${CMAKE_ARGS[@]}" -if [[ ${#BUILD_TARGET[@]} -gt 0 ]]; then - cmake --build "${BUILD_DIR}" "${BUILD_TARGET[@]}" + +if [[ "$IS_WINDOWS" == "1" ]]; then + if [[ ${#BUILD_TARGET[@]} -gt 0 ]]; then + cmake --build "${BUILD_DIR}" --config "${BUILD_TYPE}" "${BUILD_TARGET[@]}" + else + cmake --build "${BUILD_DIR}" --config "${BUILD_TYPE}" + fi else - cmake --build "${BUILD_DIR}" + if [[ ${#BUILD_TARGET[@]} -gt 0 ]]; then + cmake --build "${BUILD_DIR}" "${BUILD_TARGET[@]}" + else + cmake --build "${BUILD_DIR}" + fi +fi + +if [[ "$IS_WINDOWS" == "1" ]]; then + for BUILD_CONFIG in Debug Release; do + RELEASE_DIR="${BUILD_DIR}/${BUILD_CONFIG}" + PTLS_RELEASE="${BUILD_DIR}/_deps/picotls-build/${BUILD_CONFIG}" + + [[ -d "$RELEASE_DIR" ]] || continue + + for lib in picoquic-core picotls-core picotls-fusion picotls-minicrypto picotls-openssl; do + src_dir="$RELEASE_DIR" + [[ "$lib" != "picoquic-core" ]] && src_dir="$PTLS_RELEASE" + + if [[ -f "$src_dir/${lib}.lib" ]]; then + cp "$src_dir/${lib}.lib" "${BUILD_DIR}/lib${lib}.a" 2>/dev/null || true + underscored=$(echo "$lib" | tr '-' '_') + cp "$src_dir/${lib}.lib" "${BUILD_DIR}/lib${underscored}.a" 2>/dev/null || true + fi + done + done fi From 5d93540cd387189172261f5147ce42d8843e58fb Mon Sep 17 00:00:00 2001 From: AliReza Beigy Date: Sat, 24 Jan 2026 00:23:45 +0330 Subject: [PATCH 04/11] refactor: apply copilot feedbacks --- crates/slipstream-client/src/runtime/setup.rs | 6 +- crates/slipstream-ffi/Cargo.toml | 4 +- crates/slipstream-ffi/build/cc.rs | 59 ++++++++----------- crates/slipstream-ffi/src/picoquic.rs | 4 +- crates/slipstream-ffi/src/runtime.rs | 31 +++++++--- crates/slipstream-server/src/udp_fallback.rs | 14 ++--- 6 files changed, 64 insertions(+), 54 deletions(-) diff --git a/crates/slipstream-client/src/runtime/setup.rs b/crates/slipstream-client/src/runtime/setup.rs index f8524ba1..84b1ff90 100644 --- a/crates/slipstream-client/src/runtime/setup.rs +++ b/crates/slipstream-client/src/runtime/setup.rs @@ -25,8 +25,12 @@ pub(crate) async fn bind_udp_socket() -> Result { let bind_addr_v6 = SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0)); match bind_udp_socket_addr(bind_addr_v6, "UDP socket") { Ok(socket) => Ok(socket), - Err(_) => { + Err(err) => { // Fall back to IPv4 if IPv6 is not available (common on Windows) + warn!( + "Failed to bind UDP socket on IPv6 {}: {}. Falling back to IPv4", + bind_addr_v6, err + ); let bind_addr_v4 = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)); bind_udp_socket_addr(bind_addr_v4, "UDP socket").map_err(map_io) } diff --git a/crates/slipstream-ffi/Cargo.toml b/crates/slipstream-ffi/Cargo.toml index 3b1564d9..990dd9a8 100644 --- a/crates/slipstream-ffi/Cargo.toml +++ b/crates/slipstream-ffi/Cargo.toml @@ -12,10 +12,12 @@ cc = "1.0" [dependencies] libc = "0.2" -winapi = { version = "0.3", features = ["winsock2", "ws2def", "ws2ipdef"] } openssl-sys = { version = "0.9", optional = true, features = ["vendored"] } slipstream-core = { path = "../slipstream-core" } +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3", features = ["winsock2", "ws2def", "ws2ipdef"] } + [features] default = [] openssl-vendored = ["dep:openssl-sys", "openssl-sys/vendored", "openssl-static"] diff --git a/crates/slipstream-ffi/build/cc.rs b/crates/slipstream-ffi/build/cc.rs index 2b0c3e6a..3413f9af 100644 --- a/crates/slipstream-ffi/build/cc.rs +++ b/crates/slipstream-ffi/build/cc.rs @@ -8,35 +8,35 @@ pub(crate) fn resolve_cc(target: &str) -> String { .or_else(|_| env::var("CC")) .unwrap_or_else(|_| "cc".to_string()); } - + if let Ok(cc) = env::var("CC") { return cc; } - + if target.contains("windows") || target.contains("pc-windows") { let gcc_candidates = [ "C:\\Strawberry\\c\\bin\\gcc.exe", "C:\\mingw64\\bin\\gcc.exe", "C:\\msys64\\mingw64\\bin\\gcc.exe", ]; - + for candidate in &gcc_candidates { if Path::new(candidate).exists() { return candidate.to_string(); } } } - + let builder = cc::Build::new(); let compiler = builder.get_compiler(); let path = compiler.path(); let path_str = path.to_string_lossy().to_string(); - + // Verify the compiler actually exists if !path.exists() { panic!("Compiler not found at: {}. Please install a C compiler (MinGW-w64, MSVC Build Tools, or Strawberry Perl).", path_str); } - + path_str } @@ -84,14 +84,11 @@ pub(crate) fn create_archive( ) -> Result<(), Box> { let target = env::var("TARGET").unwrap_or_default(); let is_windows = target.contains("windows") || target.contains("pc-windows"); - + if is_windows && ar.contains("lib.exe") && ar != "ar" { // MSVC: use lib.exe let mut lib_cmd = Command::new("lib.exe"); - lib_cmd - .arg("/OUT:") - .arg(archive) - .arg("/NOLOGO"); + lib_cmd.arg("/OUT:").arg(archive).arg("/NOLOGO"); for obj in objects { lib_cmd.arg(obj); } @@ -123,26 +120,26 @@ pub(crate) fn compile_cc( let target = env::var("TARGET").unwrap_or_default(); let is_windows = target.contains("windows") || target.contains("pc-windows"); let is_gcc = cc.contains("gcc") || cc.contains("mingw"); - + let mut cmd = Command::new(cc); - + if is_windows && !is_gcc { // MSVC: cl.exe /c /Fo:output source.c /I include_dir cmd.arg("/c") - .arg(format!("/Fo:{}", output.display())) - .arg(source) - .arg(format!("/I{}", picoquic_include_dir.display())); + .arg(format!("/Fo:{}", output.display())) + .arg(source) + .arg(format!("/I{}", picoquic_include_dir.display())); } else { // GCC/Clang: cc -c -fPIC -o output source.c -I include_dir cmd.arg("-c") - .arg("-fPIC") - .arg("-o") - .arg(output) - .arg(source) - .arg("-I") - .arg(picoquic_include_dir); + .arg("-fPIC") + .arg("-o") + .arg(output) + .arg(source) + .arg("-I") + .arg(picoquic_include_dir); } - + let status = cmd.status()?; if !status.success() { return Err(format!("Failed to compile {}.", source.display()).into()); @@ -159,29 +156,25 @@ pub(crate) fn compile_cc_with_includes( let target = env::var("TARGET").unwrap_or_default(); let is_windows = target.contains("windows") || target.contains("pc-windows"); let is_gcc = cc.contains("gcc") || cc.contains("mingw"); - + let mut cmd = Command::new(cc); - + if is_windows && !is_gcc { // MSVC: cl.exe /c /Fo:output source.c /I include_dir1 /I include_dir2 cmd.arg("/c") - .arg(format!("/Fo:{}", output.display())) - .arg(source); + .arg(format!("/Fo:{}", output.display())) + .arg(source); for dir in include_dirs { cmd.arg(format!("/I{}", dir.display())); } } else { // GCC/Clang: cc -c -fPIC -o output source.c -I include_dir1 -I include_dir2 - cmd.arg("-c") - .arg("-fPIC") - .arg("-o") - .arg(output) - .arg(source); + cmd.arg("-c").arg("-fPIC").arg("-o").arg(output).arg(source); for dir in include_dirs { cmd.arg("-I").arg(dir); } } - + let status = cmd.status()?; if !status.success() { return Err(format!("Failed to compile {}.", source.display()).into()); diff --git a/crates/slipstream-ffi/src/picoquic.rs b/crates/slipstream-ffi/src/picoquic.rs index e12d9dd3..d6eca0a6 100644 --- a/crates/slipstream-ffi/src/picoquic.rs +++ b/crates/slipstream-ffi/src/picoquic.rs @@ -1,9 +1,9 @@ #![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)] -#[cfg(not(windows))] -use libc::{c_char, c_int, c_uint, c_void, size_t, sockaddr, sockaddr_storage}; #[cfg(windows)] use libc::{c_char, c_int, c_uint, c_void, size_t, sockaddr}; +#[cfg(not(windows))] +use libc::{c_char, c_int, c_uint, c_void, size_t, sockaddr, sockaddr_storage}; #[cfg(windows)] use winapi::shared::ws2def::SOCKADDR_STORAGE as sockaddr_storage; diff --git a/crates/slipstream-ffi/src/runtime.rs b/crates/slipstream-ffi/src/runtime.rs index 55fc4aa1..e07235d0 100644 --- a/crates/slipstream-ffi/src/runtime.rs +++ b/crates/slipstream-ffi/src/runtime.rs @@ -8,18 +8,20 @@ use crate::picoquic::{ picoquic_set_preemptive_repeat_policy, picoquic_set_stream_data_consumption_mode, picoquic_stop_sending, slipstream_take_stateless_packet_for_cid, PICOQUIC_MAX_PACKET_SIZE, }; -#[cfg(not(windows))] -use libc::{c_char, c_int, c_ulong, size_t, sockaddr_storage}; -#[cfg(windows)] -use winapi::shared::ws2def::{SOCKADDR_STORAGE as sockaddr_storage, SOCKADDR_IN, AF_INET, AF_INET6}; -#[cfg(windows)] -use winapi::shared::ws2ipdef::SOCKADDR_IN6_LH; #[cfg(windows)] use libc::{c_char, c_int, c_ulong, size_t}; +#[cfg(not(windows))] +use libc::{c_char, c_int, c_ulong, size_t, sockaddr_storage}; use slipstream_core::tcp::stream_write_buffer_bytes; use std::ffi::CStr; use std::io::Write; use std::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6, TcpStream}; +#[cfg(windows)] +use winapi::shared::ws2def::{ + AF_INET, AF_INET6, SOCKADDR_IN, SOCKADDR_STORAGE as sockaddr_storage, +}; +#[cfg(windows)] +use winapi::shared::ws2ipdef::SOCKADDR_IN6_LH; pub const SLIPSTREAM_INTERNAL_ERROR: u64 = 0x101; pub const SLIPSTREAM_FILE_CANCEL_ERROR: u64 = 0x105; @@ -282,26 +284,33 @@ pub unsafe fn abort_stream_bidi(cnx: *mut picoquic_cnx_t, stream_id: u64, app_er pub fn socket_addr_to_storage(addr: SocketAddr) -> sockaddr_storage { match addr { SocketAddr::V4(addr) => { + // SAFETY: sockaddr_storage is plain-old-data; zeroing is valid. let mut storage: sockaddr_storage = unsafe { std::mem::zeroed() }; unsafe { + // SAFETY: storage is properly aligned and large enough for SOCKADDR_IN. let sockaddr_ptr = &mut storage as *mut _ as *mut SOCKADDR_IN; (*sockaddr_ptr).sin_family = AF_INET as u16; (*sockaddr_ptr).sin_port = addr.port().to_be(); - *(*sockaddr_ptr).sin_addr.S_un.S_addr_mut() = u32::from_be_bytes(addr.ip().octets()); + *(*sockaddr_ptr).sin_addr.S_un.S_addr_mut() = + u32::from_be_bytes(addr.ip().octets()); } storage } SocketAddr::V6(addr) => { + // SAFETY: sockaddr_storage is plain-old-data; zeroing is valid. let mut storage: sockaddr_storage = unsafe { std::mem::zeroed() }; unsafe { + // SAFETY: storage is properly aligned and large enough for SOCKADDR_IN6_LH. let sockaddr_ptr = &mut storage as *mut _ as *mut SOCKADDR_IN6_LH; (*sockaddr_ptr).sin6_family = AF_INET6 as u16; (*sockaddr_ptr).sin6_port = addr.port().to_be(); (*sockaddr_ptr).sin6_flowinfo = addr.flowinfo(); + // SAFETY: sin6_addr is a 16-byte array; addr_bytes is 16 bytes. // Copy IPv6 address bytes directly using raw pointer let addr_bytes = addr.ip().octets(); let dest_ptr = &mut (*sockaddr_ptr).sin6_addr as *mut _ as *mut u8; std::ptr::copy_nonoverlapping(addr_bytes.as_ptr(), dest_ptr, 16); + // SAFETY: u union field contains u32 for scope_id on Windows. // Set scope_id via the union let scope_ptr = &mut (*sockaddr_ptr).u as *mut _ as *mut u32; *scope_ptr = addr.scope_id(); @@ -316,21 +325,25 @@ pub fn sockaddr_storage_to_socket_addr(storage: &sockaddr_storage) -> Result { - let addr_in: &SOCKADDR_IN = - unsafe { &*(storage as *const _ as *const SOCKADDR_IN) }; + // SAFETY: ss_family identifies an IPv4 sockaddr layout. + let addr_in: &SOCKADDR_IN = unsafe { &*(storage as *const _ as *const SOCKADDR_IN) }; + // SAFETY: S_un union contains S_addr (u32) for IPv4 addresses on Windows. let ip = Ipv4Addr::from(unsafe { *addr_in.sin_addr.S_un.S_addr() }); let port = u16::from_be(addr_in.sin_port); Ok(SocketAddr::V4(SocketAddrV4::new(ip, port))) } AF_INET6 => { + // SAFETY: ss_family identifies an IPv6 sockaddr layout. let addr_in6: &SOCKADDR_IN6_LH = unsafe { &*(storage as *const _ as *const SOCKADDR_IN6_LH) }; + // SAFETY: sin6_addr is a 16-byte array; ip_bytes is 16 bytes. // Read IPv6 address bytes directly using raw pointer let src_ptr = &addr_in6.sin6_addr as *const _ as *const u8; let mut ip_bytes: [u8; 16] = [0; 16]; unsafe { std::ptr::copy_nonoverlapping(src_ptr, ip_bytes.as_mut_ptr(), 16) }; let ip = Ipv6Addr::from(ip_bytes); let port = u16::from_be(addr_in6.sin6_port); + // SAFETY: u union field contains u32 for scope_id on Windows. // Read scope_id via the union let scope_id = unsafe { *(&addr_in6.u as *const _ as *const u32) }; Ok(SocketAddr::V6(SocketAddrV6::new( diff --git a/crates/slipstream-server/src/udp_fallback.rs b/crates/slipstream-server/src/udp_fallback.rs index d62f752e..22b79f52 100644 --- a/crates/slipstream-server/src/udp_fallback.rs +++ b/crates/slipstream-server/src/udp_fallback.rs @@ -67,14 +67,12 @@ fn dummy_sockaddr_storage() -> libc::sockaddr_storage { #[cfg(windows)] fn dummy_sockaddr_storage() -> slipstream_ffi::SockaddrStorage { use std::net::{Ipv6Addr, SocketAddrV6}; - slipstream_ffi::socket_addr_to_storage( - std::net::SocketAddr::V6(SocketAddrV6::new( - Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), - 12345, - 0, - 0, - )) - ) + slipstream_ffi::socket_addr_to_storage(std::net::SocketAddr::V6(SocketAddrV6::new( + Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), + 12345, + 0, + 0, + ))) } #[cfg(test)] From 26c400338733c431f36e5455ea48a25ecc9a9f68 Mon Sep 17 00:00:00 2001 From: AliReza Beigy Date: Sat, 24 Jan 2026 15:39:11 +0330 Subject: [PATCH 05/11] refactor: Enhance UDP error handling and improve Windows compatibility --- crates/slipstream-core/src/net.rs | 26 ++++++++++++++++---- crates/slipstream-ffi/build.rs | 18 ++++++-------- crates/slipstream-server/src/config.rs | 4 +-- crates/slipstream-server/src/udp_fallback.rs | 5 +++- 4 files changed, 35 insertions(+), 18 deletions(-) diff --git a/crates/slipstream-core/src/net.rs b/crates/slipstream-core/src/net.rs index 71648774..e4bb692e 100644 --- a/crates/slipstream-core/src/net.rs +++ b/crates/slipstream-core/src/net.rs @@ -5,16 +5,32 @@ use tokio::net::{lookup_host, TcpListener as TokioTcpListener, UdpSocket as Toki pub fn is_transient_udp_error(err: &Error) -> bool { match err.kind() { - ErrorKind::WouldBlock | ErrorKind::TimedOut | ErrorKind::Interrupted => { + ErrorKind::WouldBlock + | ErrorKind::TimedOut + | ErrorKind::Interrupted + | ErrorKind::ConnectionReset => { return true; } _ => {} } - matches!( - err.raw_os_error(), - Some(code) if code == libc::ENETUNREACH || code == libc::EHOSTUNREACH - ) + #[cfg(not(windows))] + { + matches!( + err.raw_os_error(), + Some(code) if code == libc::ENETUNREACH || code == libc::EHOSTUNREACH + ) + } + #[cfg(windows)] + { + // Windows uses WinSock error codes: WSAENETUNREACH = 10051, WSAEHOSTUNREACH = 10065 + const WSAENETUNREACH: i32 = 10051; + const WSAEHOSTUNREACH: i32 = 10065; + matches!( + err.raw_os_error(), + Some(code) if code == WSAENETUNREACH || code == WSAEHOSTUNREACH + ) + } } pub async fn bind_first_resolved( diff --git a/crates/slipstream-ffi/build.rs b/crates/slipstream-ffi/build.rs index 6baa6058..69262078 100644 --- a/crates/slipstream-ffi/build.rs +++ b/crates/slipstream-ffi/build.rs @@ -226,17 +226,15 @@ fn main() -> Result<(), Box> { if cfg!(feature = "openssl-static") { println!("cargo:rustc-link-lib=static=ssl"); println!("cargo:rustc-link-lib=static=crypto"); - } else { - if is_windows { - if let Ok(openssl_lib_dir) = env::var("OPENSSL_LIB_DIR") { - println!("cargo:rustc-link-search=native={}", openssl_lib_dir); - } - println!("cargo:rustc-link-lib=dylib=libssl"); - println!("cargo:rustc-link-lib=dylib=libcrypto"); - } else { - println!("cargo:rustc-link-lib=dylib=ssl"); - println!("cargo:rustc-link-lib=dylib=crypto"); + } else if is_windows { + if let Ok(openssl_lib_dir) = env::var("OPENSSL_LIB_DIR") { + println!("cargo:rustc-link-search=native={}", openssl_lib_dir); } + println!("cargo:rustc-link-lib=dylib=libssl"); + println!("cargo:rustc-link-lib=dylib=libcrypto"); + } else { + println!("cargo:rustc-link-lib=dylib=ssl"); + println!("cargo:rustc-link-lib=dylib=crypto"); } } diff --git a/crates/slipstream-server/src/config.rs b/crates/slipstream-server/src/config.rs index e3678500..e298e2a4 100644 --- a/crates/slipstream-server/src/config.rs +++ b/crates/slipstream-server/src/config.rs @@ -267,12 +267,12 @@ fn write_pem_file(path: &Path, bytes: &[u8], mode: u32) -> io::Result<()> { Ok(()) } -fn open_new_with_mode(path: &Path, mode: u32) -> io::Result { +fn open_new_with_mode(path: &Path, _mode: u32) -> io::Result { let mut options = OpenOptions::new(); options.write(true).create_new(true); #[cfg(unix)] { - options.mode(mode); + options.mode(_mode); } options.open(path) } diff --git a/crates/slipstream-server/src/udp_fallback.rs b/crates/slipstream-server/src/udp_fallback.rs index 22b79f52..aac12a00 100644 --- a/crates/slipstream-server/src/udp_fallback.rs +++ b/crates/slipstream-server/src/udp_fallback.rs @@ -1,4 +1,7 @@ -use slipstream_ffi::picoquic::picoquic_quic_t; +use slipstream_ffi::picoquic::{ + picoquic_cnx_t, picoquic_incoming_packet_ex, picoquic_quic_t, slipstream_disable_ack_delay, +}; +#[cfg(not(windows))] use slipstream_ffi::socket_addr_to_storage; use std::collections::HashMap; use std::net::{IpAddr, Ipv6Addr, SocketAddr}; From e6f6cff129749c53aca816f2e16e2e9c6c3a68fb Mon Sep 17 00:00:00 2001 From: Mygod Date: Tue, 27 Jan 2026 14:53:33 -0800 Subject: [PATCH 06/11] Add CI --- .github/workflows/ci.yml | 42 ++++++++++++++++++++++++++++++++++++---- vendor/picoquic | 2 +- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a1328881..41c442d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -125,6 +125,10 @@ jobs: runner: ubuntu-latest target: x86_64-unknown-linux-gnu artifact_name: slipstream-linux-x86_64 + - name: windows-x86_64 + runner: windows-latest + target: x86_64-pc-windows-msvc + artifact_name: slipstream-windows-x86_64 steps: - name: Check out slipstream-rust uses: actions/checkout@v4 @@ -144,6 +148,32 @@ jobs: echo "OPENSSL_ROOT_DIR=$(brew --prefix openssl@3)" >> "$GITHUB_ENV" echo "PKG_CONFIG_PATH=$(brew --prefix openssl@3)/lib/pkgconfig" >> "$GITHUB_ENV" + - name: Install build dependencies (Windows) + if: runner.os == 'Windows' + shell: pwsh + env: + VCPKG_ROOT: C:\vcpkg + VCPKG_INSTALLATION_ROOT: C:\vcpkg + VCPKG_DEFAULT_TRIPLET: x64-windows + VCPKG_BINARY_SOURCES: clear;x-gha,readwrite + run: | + $vcpkgRoot = $env:VCPKG_INSTALLATION_ROOT + if (!(Test-Path "$vcpkgRoot\vcpkg.exe")) { + Write-Error "vcpkg.exe not found at $vcpkgRoot. Expected GitHub runner preinstall." + } + & "$vcpkgRoot\vcpkg.exe" install openssl:x64-windows + $installed = Join-Path $vcpkgRoot "installed\x64-windows" + "OPENSSL_ROOT_DIR=$installed" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + "OPENSSL_INCLUDE_DIR=$installed\include" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + "OPENSSL_LIB_DIR=$installed\lib" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + "OPENSSL_DIR=$installed" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + $gitBin = "C:\Program Files\Git\bin" + $gitUsrBin = "C:\Program Files\Git\usr\bin" + if (!(Test-Path $gitBin)) { + Write-Error "Git Bash not found at $gitBin." + } + "PATH=$gitBin;$gitUsrBin;$env:PATH" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + - name: Set up Rust uses: dtolnay/rust-toolchain@stable with: @@ -162,13 +192,17 @@ jobs: dist_dir="dist" base_name="${{ matrix.artifact_name }}" bin_dir="target/${{ matrix.target }}/release" + bin_suffix="" + if [[ "${RUNNER_OS}" == "Windows" ]]; then + bin_suffix=".exe" + fi mkdir -p "${dist_dir}" - cp "${bin_dir}/slipstream-client" "${dist_dir}/" - cp "${bin_dir}/slipstream-server" "${dist_dir}/" + cp "${bin_dir}/slipstream-client${bin_suffix}" "${dist_dir}/" + cp "${bin_dir}/slipstream-server${bin_suffix}" "${dist_dir}/" if command -v sha256sum >/dev/null 2>&1; then - (cd "${dist_dir}" && sha256sum slipstream-client slipstream-server > "${base_name}.sha256") + (cd "${dist_dir}" && sha256sum "slipstream-client${bin_suffix}" "slipstream-server${bin_suffix}" > "${base_name}.sha256") else - (cd "${dist_dir}" && shasum -a 256 slipstream-client slipstream-server > "${base_name}.sha256") + (cd "${dist_dir}" && shasum -a 256 "slipstream-client${bin_suffix}" "slipstream-server${bin_suffix}" > "${base_name}.sha256") fi - name: Upload binaries diff --git a/vendor/picoquic b/vendor/picoquic index 4bd356c0..58212f3a 160000 --- a/vendor/picoquic +++ b/vendor/picoquic @@ -1 +1 @@ -Subproject commit 4bd356c004a50ec46ee8a933ebd024da5d659f75 +Subproject commit 58212f3acd175e66b7721f1b8e109cf35b1cea2f From 33633ea5f5f6c37101a52dcee706de1e6b56ca34 Mon Sep 17 00:00:00 2001 From: AliReza Beigy Date: Wed, 28 Jan 2026 21:40:33 +0330 Subject: [PATCH 07/11] refactor: Update CI workflow for Windows --- .github/workflows/ci.yml | 85 +++++++++++++++++++------ crates/slipstream-ffi/build/picoquic.rs | 13 +++- 2 files changed, 76 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41c442d1..854c4665 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -151,28 +151,59 @@ jobs: - name: Install build dependencies (Windows) if: runner.os == 'Windows' shell: pwsh - env: - VCPKG_ROOT: C:\vcpkg - VCPKG_INSTALLATION_ROOT: C:\vcpkg - VCPKG_DEFAULT_TRIPLET: x64-windows - VCPKG_BINARY_SOURCES: clear;x-gha,readwrite run: | - $vcpkgRoot = $env:VCPKG_INSTALLATION_ROOT - if (!(Test-Path "$vcpkgRoot\vcpkg.exe")) { - Write-Error "vcpkg.exe not found at $vcpkgRoot. Expected GitHub runner preinstall." + choco install cmake pkgconfiglite strawberryperl -y + + # Find and configure OpenSSL + $opensslPaths = @( + "C:\Program Files\OpenSSL", + "C:\Program Files\OpenSSL-Win64", + "C:\OpenSSL-Win64", + "C:\OpenSSL" + ) + + foreach ($path in $opensslPaths) { + if (Test-Path $path) { + $libDir = Get-ChildItem -Path "$path\lib" -Filter "libcrypto*.lib" -Recurse -ErrorAction SilentlyContinue | + Select-Object -First 1 | ForEach-Object { $_.DirectoryName } + + if ($libDir) { + echo "OPENSSL_DIR=$path" >> $env:GITHUB_ENV + echo "OPENSSL_ROOT_DIR=$path" >> $env:GITHUB_ENV + echo "OPENSSL_LIB_DIR=$libDir" >> $env:GITHUB_ENV + echo "OPENSSL_INCLUDE_DIR=$path\include" >> $env:GITHUB_ENV + Write-Host "OpenSSL configured at $path (libs in $libDir)" + break + } + } } - & "$vcpkgRoot\vcpkg.exe" install openssl:x64-windows - $installed = Join-Path $vcpkgRoot "installed\x64-windows" - "OPENSSL_ROOT_DIR=$installed" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - "OPENSSL_INCLUDE_DIR=$installed\include" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - "OPENSSL_LIB_DIR=$installed\lib" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - "OPENSSL_DIR=$installed" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - $gitBin = "C:\Program Files\Git\bin" - $gitUsrBin = "C:\Program Files\Git\usr\bin" - if (!(Test-Path $gitBin)) { - Write-Error "Git Bash not found at $gitBin." + + # Setup Perl for OpenSSL vendored build + $perlPaths = @( + "C:\Strawberry\perl\bin", + "C:\Program Files\Strawberry\perl\bin", + "${env:ProgramFiles}\Strawberry\perl\bin" + ) + + foreach ($path in $perlPaths) { + $exe = Join-Path $path "perl.exe" + if (Test-Path $exe) { + $env:Path = "$path;$env:Path" + echo "PATH=$path`;$env:Path" >> $env:GITHUB_ENV + echo "PERL=$exe" >> $env:GITHUB_ENV + Write-Host "Found Strawberry Perl at $path" + + # Install Locale::Maketext::Simple if needed + $moduleCheck = & $exe -e "use Locale::Maketext::Simple; print 'OK'" 2>&1 + if ($LASTEXITCODE -ne 0) { + $cpanmExe = Join-Path $path "cpanm.bat" + if (Test-Path $cpanmExe) { + & $cpanmExe --notest Locale::Maketext::Simple 2>&1 | Out-String | Write-Host + } + } + break + } } - "PATH=$gitBin;$gitUsrBin;$env:PATH" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - name: Set up Rust uses: dtolnay/rust-toolchain@stable @@ -180,10 +211,22 @@ jobs: targets: ${{ matrix.target }} - name: Build slipstream-client - run: cargo build -p slipstream-client --release --target ${{ matrix.target }} + shell: bash + run: | + if [[ "${{ runner.os }}" == "Windows" ]]; then + cargo build -p slipstream-client --release --target ${{ matrix.target }} --features openssl-vendored + else + cargo build -p slipstream-client --release --target ${{ matrix.target }} + fi - name: Build slipstream-server - run: cargo build -p slipstream-server --release --target ${{ matrix.target }} + shell: bash + run: | + if [[ "${{ runner.os }}" == "Windows" ]]; then + cargo build -p slipstream-server --release --target ${{ matrix.target }} --features openssl-vendored + else + cargo build -p slipstream-server --release --target ${{ matrix.target }} + fi - name: Package binaries shell: bash diff --git a/crates/slipstream-ffi/build/picoquic.rs b/crates/slipstream-ffi/build/picoquic.rs index d7f6f7c1..3c569926 100644 --- a/crates/slipstream-ffi/build/picoquic.rs +++ b/crates/slipstream-ffi/build/picoquic.rs @@ -29,7 +29,18 @@ pub(crate) fn build_picoquic( .unwrap_or_else(|| root.join(".picoquic-build")); let mut command = if cfg!(windows) { - let mut cmd = Command::new("bash"); + // On Windows, find Git Bash explicitly to avoid WSL bash.exe from System32 + let git_bash_paths = [ + PathBuf::from(r"C:\Program Files\Git\bin\bash.exe"), + PathBuf::from(r"C:\Program Files\Git\usr\bin\bash.exe"), + ]; + + let bash_exe = git_bash_paths + .iter() + .find(|path| path.exists()) + .ok_or("Git Bash not found. Please install Git for Windows.")?; + + let mut cmd = Command::new(bash_exe); cmd.arg(&script); cmd } else { From 8610a9af38267fe5b6f58cb1d9bf67a63c4231d1 Mon Sep 17 00:00:00 2001 From: AliReza Beigy Date: Wed, 28 Jan 2026 23:26:26 +0330 Subject: [PATCH 08/11] ci: remove openssl-vendored feature for Windows builds --- .github/workflows/ci.yml | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 854c4665..9361edb0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -213,20 +213,12 @@ jobs: - name: Build slipstream-client shell: bash run: | - if [[ "${{ runner.os }}" == "Windows" ]]; then - cargo build -p slipstream-client --release --target ${{ matrix.target }} --features openssl-vendored - else - cargo build -p slipstream-client --release --target ${{ matrix.target }} - fi + cargo build -p slipstream-client --release --target ${{ matrix.target }} - name: Build slipstream-server shell: bash run: | - if [[ "${{ runner.os }}" == "Windows" ]]; then - cargo build -p slipstream-server --release --target ${{ matrix.target }} --features openssl-vendored - else - cargo build -p slipstream-server --release --target ${{ matrix.target }} - fi + cargo build -p slipstream-server --release --target ${{ matrix.target }} - name: Package binaries shell: bash From fc38d665603e5e24bb0140c8094722a80fd5eb4f Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 28 Jan 2026 15:44:59 -0800 Subject: [PATCH 09/11] Test --- vendor/picoquic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/picoquic b/vendor/picoquic index 58212f3a..4bd356c0 160000 --- a/vendor/picoquic +++ b/vendor/picoquic @@ -1 +1 @@ -Subproject commit 58212f3acd175e66b7721f1b8e109cf35b1cea2f +Subproject commit 4bd356c004a50ec46ee8a933ebd024da5d659f75 From 0d5bcbeb3121750b7e885ae1e0a46f7fb4a100c3 Mon Sep 17 00:00:00 2001 From: AliReza Beigy Date: Thu, 29 Jan 2026 19:39:14 +0330 Subject: [PATCH 10/11] refactor: Update MSVC and GCC/Clang command arguments for Windows compatibility --- crates/slipstream-ffi/build/cc.rs | 41 +++++++++++++++++++++---------- vendor/picoquic | 2 +- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/crates/slipstream-ffi/build/cc.rs b/crates/slipstream-ffi/build/cc.rs index 3413f9af..c999c3a9 100644 --- a/crates/slipstream-ffi/build/cc.rs +++ b/crates/slipstream-ffi/build/cc.rs @@ -124,20 +124,25 @@ pub(crate) fn compile_cc( let mut cmd = Command::new(cc); if is_windows && !is_gcc { - // MSVC: cl.exe /c /Fo:output source.c /I include_dir + // MSVC: cl.exe /c /Fo:output source.c /I include_dir /D_WINDOWS [/D_WINDOWS64] cmd.arg("/c") .arg(format!("/Fo:{}", output.display())) .arg(source) - .arg(format!("/I{}", picoquic_include_dir.display())); + .arg("/D_WINDOWS"); + if target.contains("x86_64") { + cmd.arg("/D_WINDOWS64"); + } + cmd.arg(format!("/I{}", picoquic_include_dir.display())); } else { - // GCC/Clang: cc -c -fPIC -o output source.c -I include_dir - cmd.arg("-c") - .arg("-fPIC") - .arg("-o") - .arg(output) - .arg(source) - .arg("-I") - .arg(picoquic_include_dir); + // GCC/Clang: cc -c -fPIC -o output source.c -I include_dir [-D_WINDOWS [-D_WINDOWS64]] + cmd.arg("-c").arg("-fPIC").arg("-o").arg(output).arg(source); + if is_windows { + cmd.arg("-D_WINDOWS"); + if target.contains("x86_64") { + cmd.arg("-D_WINDOWS64"); + } + } + cmd.arg("-I").arg(picoquic_include_dir); } let status = cmd.status()?; @@ -160,16 +165,26 @@ pub(crate) fn compile_cc_with_includes( let mut cmd = Command::new(cc); if is_windows && !is_gcc { - // MSVC: cl.exe /c /Fo:output source.c /I include_dir1 /I include_dir2 + // MSVC: cl.exe /c /Fo:output source.c /D_WINDOWS [/D_WINDOWS64] /I dir1 /I dir2 cmd.arg("/c") .arg(format!("/Fo:{}", output.display())) - .arg(source); + .arg(source) + .arg("/D_WINDOWS"); + if target.contains("x86_64") { + cmd.arg("/D_WINDOWS64"); + } for dir in include_dirs { cmd.arg(format!("/I{}", dir.display())); } } else { - // GCC/Clang: cc -c -fPIC -o output source.c -I include_dir1 -I include_dir2 + // GCC/Clang: cc -c -fPIC -o output source.c [-D_WINDOWS [-D_WINDOWS64]] -I dir1 -I dir2 cmd.arg("-c").arg("-fPIC").arg("-o").arg(output).arg(source); + if is_windows { + cmd.arg("-D_WINDOWS"); + if target.contains("x86_64") { + cmd.arg("-D_WINDOWS64"); + } + } for dir in include_dirs { cmd.arg("-I").arg(dir); } diff --git a/vendor/picoquic b/vendor/picoquic index 4bd356c0..3a0b3cc7 160000 --- a/vendor/picoquic +++ b/vendor/picoquic @@ -1 +1 @@ -Subproject commit 4bd356c004a50ec46ee8a933ebd024da5d659f75 +Subproject commit 3a0b3cc7fd55f2441ee9a25598300ca4af3e7f4a From 5e546a5217c8431d1c882021b85616ff90d289c1 Mon Sep 17 00:00:00 2001 From: AliReza Beigy Date: Sat, 14 Mar 2026 14:27:19 +0330 Subject: [PATCH 11/11] fix: use SockaddrStorage from slipstream_ffi refactor: clean up after rebase with upstream --- crates/slipstream-client/src/runtime/setup.rs | 3 +-- crates/slipstream-server/src/udp_fallback.rs | 5 +---- crates/slipstream-server/src/udp_fallback/decode.rs | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/crates/slipstream-client/src/runtime/setup.rs b/crates/slipstream-client/src/runtime/setup.rs index 84b1ff90..f3fc0608 100644 --- a/crates/slipstream-client/src/runtime/setup.rs +++ b/crates/slipstream-client/src/runtime/setup.rs @@ -1,8 +1,7 @@ use crate::error::ClientError; use slipstream_core::net::{bind_first_resolved, bind_tcp_listener_addr, bind_udp_socket_addr}; -use socket2::{Domain, Protocol, SockAddr, Socket, Type}; use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; -use tokio::net::{lookup_host, TcpListener as TokioTcpListener, UdpSocket as TokioUdpSocket}; +use tokio::net::{TcpListener as TokioTcpListener, UdpSocket as TokioUdpSocket}; use tracing::warn; pub(crate) fn compute_mtu(domain_len: usize) -> Result { diff --git a/crates/slipstream-server/src/udp_fallback.rs b/crates/slipstream-server/src/udp_fallback.rs index aac12a00..22b79f52 100644 --- a/crates/slipstream-server/src/udp_fallback.rs +++ b/crates/slipstream-server/src/udp_fallback.rs @@ -1,7 +1,4 @@ -use slipstream_ffi::picoquic::{ - picoquic_cnx_t, picoquic_incoming_packet_ex, picoquic_quic_t, slipstream_disable_ack_delay, -}; -#[cfg(not(windows))] +use slipstream_ffi::picoquic::picoquic_quic_t; use slipstream_ffi::socket_addr_to_storage; use std::collections::HashMap; use std::net::{IpAddr, Ipv6Addr, SocketAddr}; diff --git a/crates/slipstream-server/src/udp_fallback/decode.rs b/crates/slipstream-server/src/udp_fallback/decode.rs index e396d46b..43931bbb 100644 --- a/crates/slipstream-server/src/udp_fallback/decode.rs +++ b/crates/slipstream-server/src/udp_fallback/decode.rs @@ -62,7 +62,7 @@ fn decode_slot( domains: &[&str], quic: *mut picoquic_quic_t, current_time: u64, - local_addr_storage: &libc::sockaddr_storage, + local_addr_storage: &slipstream_ffi::SockaddrStorage, ) -> Result { match decode_query_with_domains(packet, domains) { Ok(query) => {