diff --git a/proxychains-bin/src/main.rs b/proxychains-bin/src/main.rs index 84fe810..d2d195c 100644 --- a/proxychains-bin/src/main.rs +++ b/proxychains-bin/src/main.rs @@ -286,6 +286,21 @@ fn build_probe_report(results: Vec, timeout: Duration, group: String) let total = results.len(); let ok = results.iter().filter(|r| r.ok).count(); let fail = total.saturating_sub(ok); + let success_rate = if total == 0 { + 0.0 + } else { + (ok as f64) * 100.0 / (total as f64) + }; + let best_latency_ms = results + .iter() + .filter(|r| r.ok) + .map(|r| r.latency_ms) + .min(); + let worst_latency_ms = results + .iter() + .filter(|r| r.ok) + .map(|r| r.latency_ms) + .max(); let mut stats = ProbeFailureStats::default(); for r in &results { if r.ok { @@ -310,7 +325,14 @@ fn build_probe_report(results: Vec, timeout: Duration, group: String) generated_at_iso8601: format_unix_ts_iso8601(generated_at), timeout_ms: timeout.as_millis(), selected_group: group, - summary: ProbeSummary { total, ok, fail }, + summary: ProbeSummary { + total, + ok, + fail, + success_rate, + best_latency_ms, + worst_latency_ms, + }, failure_stats: stats, nodes: results, } @@ -346,9 +368,15 @@ fn print_probe_report(report: &ProbeReport) { } } println!( - "Probe summary: total={}, ok={}, fail={}", - report.summary.total, report.summary.ok, report.summary.fail + "Probe summary: total={}, ok={}, fail={}, success_rate={:.2}%", + report.summary.total, report.summary.ok, report.summary.fail, report.summary.success_rate ); + if let Some(best) = report.summary.best_latency_ms { + println!("Latency: best={} ms", best); + } + if let Some(worst) = report.summary.worst_latency_ms { + println!("Latency: worst={} ms", worst); + } println!( "Failure stats: timeout={}, refused={}, reset={}, net_unreach={}, addr_unavail={}, not_connected={}, other={}", report.failure_stats.timeout, @@ -378,6 +406,9 @@ struct ProbeSummary { total: usize, ok: usize, fail: usize, + success_rate: f64, + best_latency_ms: Option, + worst_latency_ms: Option, } #[derive(Debug, Serialize, Default)] diff --git a/proxychains/src/hook/hooks_windows.rs b/proxychains/src/hook/hooks_windows.rs index 1fa283e..bef5581 100644 --- a/proxychains/src/hook/hooks_windows.rs +++ b/proxychains/src/hook/hooks_windows.rs @@ -403,6 +403,20 @@ pub unsafe extern "system" fn hook_connect_impl( SOCKET_ERROR } +/// Windows WSAConnect hook implementation. +#[cfg(windows)] +pub unsafe extern "system" fn hook_wsa_connect_impl( + sock: usize, + name: *const c_void, + namelen: i32, + _caller_data: *const c_void, + _callee_data: *const c_void, + _sqos: *const c_void, + _gqos: *const c_void, +) -> i32 { + hook_connect_impl(sock, name, namelen) +} + /// Windows getaddrinfo hook implementation. #[cfg(windows)] pub unsafe extern "system" fn hook_getaddrinfo_impl( diff --git a/proxychains/src/hook/interpose_windows.rs b/proxychains/src/hook/interpose_windows.rs index 4248dc6..123bc0c 100644 --- a/proxychains/src/hook/interpose_windows.rs +++ b/proxychains/src/hook/interpose_windows.rs @@ -15,10 +15,19 @@ use crate::error::{Error, Result}; use super::hooks_windows::{ hook_connect_impl, hook_freeaddrinfo_impl, hook_getaddrinfo_impl, hook_gethostbyname_impl, - hook_getnameinfo_impl, + hook_getnameinfo_impl, hook_wsa_connect_impl, }; type ConnectFn = unsafe extern "system" fn(usize, *const c_void, i32) -> i32; +type WsaConnectFn = unsafe extern "system" fn( + usize, + *const c_void, + i32, + *const c_void, + *const c_void, + *const c_void, + *const c_void, +) -> i32; type GetAddrInfoFn = unsafe extern "system" fn(*const i8, *const i8, *const c_void, *mut *mut c_void) -> i32; type FreeAddrInfoFn = unsafe extern "system" fn(*mut c_void); @@ -27,6 +36,7 @@ type GetNameInfoFn = unsafe extern "system" fn(*const c_void, i32, *mut i8, u32, *mut i8, u32, i32) -> i32; static ORIGINAL_CONNECT: OnceLock = OnceLock::new(); +static ORIGINAL_WSA_CONNECT: OnceLock = OnceLock::new(); static ORIGINAL_GETADDRINFO: OnceLock = OnceLock::new(); static ORIGINAL_FREEADDRINFO: OnceLock = OnceLock::new(); static ORIGINAL_GETHOSTBYNAME: OnceLock = OnceLock::new(); @@ -56,6 +66,8 @@ impl OriginalFunctions { unsafe { let connect_fn: ConnectFn = install_api_hook("connect", hook_connect_impl as *const () as *mut c_void)?; + let wsa_connect_fn: WsaConnectFn = + install_api_hook("WSAConnect", hook_wsa_connect_impl as *const () as *mut c_void)?; let getaddrinfo_fn: GetAddrInfoFn = install_api_hook( "getaddrinfo", hook_getaddrinfo_impl as *const () as *mut c_void, @@ -74,6 +86,7 @@ impl OriginalFunctions { )?; let _ = ORIGINAL_CONNECT.set(connect_fn); + let _ = ORIGINAL_WSA_CONNECT.set(wsa_connect_fn); let _ = ORIGINAL_GETADDRINFO.set(getaddrinfo_fn); let _ = ORIGINAL_FREEADDRINFO.set(freeaddrinfo_fn); let _ = ORIGINAL_GETHOSTBYNAME.set(gethostbyname_fn); @@ -115,6 +128,32 @@ pub unsafe fn original_connect(sock: usize, addr: *const c_void, len: i32) -> i3 } } +/// Call the original WSAConnect function. +pub unsafe fn original_wsa_connect( + sock: usize, + name: *const c_void, + namelen: i32, + caller_data: *const c_void, + callee_data: *const c_void, + sqos: *const c_void, + gqos: *const c_void, +) -> i32 { + if let Some(f) = ORIGINAL_WSA_CONNECT.get() { + f( + sock, + name, + namelen, + caller_data, + callee_data, + sqos, + gqos, + ) + } else { + WSASetLastError(WSAECONNREFUSED.0); + -1 + } +} + /// Call the original getaddrinfo function. pub unsafe fn original_getaddrinfo( node: *const i8,