feat(core): tighten windows proxy enforcement and dns/ipv6 handling
This commit is contained in:
1 parent
c452b0ea42
commit
04a548ca23
6 files changed
+389
-14
No files matched your search
@@ -238,8 +238,14 @@ impl Config {
|
||||
pub fn should_bypass_ip(&self, ip: &IpAddr) -> bool {
|
||||
match ip {
|
||||
IpAddr::V4(v4) => self.should_bypass(v4),
|
||||
// For now, IPv6 bypass only loopback.
|
||||
IpAddr::V6(v6) => v6.is_loopback(),
|
||||
// Mirror localhost/local semantics for IPv6 to avoid unintended proxying
|
||||
// of link-local/unspecified traffic.
|
||||
IpAddr::V6(v6) => {
|
||||
v6.is_loopback()
|
||||
|| v6.is_unspecified()
|
||||
|| v6.is_unique_local()
|
||||
|| v6.is_unicast_link_local()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,3 +280,25 @@ impl Config {
|
||||
!self.proxies.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::net::Ipv6Addr;
|
||||
|
||||
#[test]
|
||||
fn test_ipv6_bypass_local_scopes() {
|
||||
let cfg = Config::default();
|
||||
assert!(cfg.should_bypass_ip(&IpAddr::V6(Ipv6Addr::LOCALHOST)));
|
||||
assert!(cfg.should_bypass_ip(&IpAddr::V6(Ipv6Addr::UNSPECIFIED)));
|
||||
assert!(cfg.should_bypass_ip(&IpAddr::V6(Ipv6Addr::new(
|
||||
0xfe80, 0, 0, 0, 1, 2, 3, 4
|
||||
))));
|
||||
assert!(cfg.should_bypass_ip(&IpAddr::V6(Ipv6Addr::new(
|
||||
0xfc00, 0, 0, 0, 1, 2, 3, 4
|
||||
))));
|
||||
assert!(!cfg.should_bypass_ip(&IpAddr::V6(Ipv6Addr::new(
|
||||
0x2606, 0x4700, 0, 0, 0, 0, 0, 0x1111
|
||||
))));
|
||||
}
|
||||
}
|
||||
@@ -407,7 +407,7 @@ pub unsafe fn hook_getaddrinfo(
|
||||
// Get the hostname
|
||||
let hostname = match CStr::from_ptr(node).to_str() {
|
||||
Ok(s) => s.to_string(),
|
||||
Err(_) => return original_getaddrinfo(node, service, hints, res),
|
||||
Err(_) => return libc::EAI_NONAME,
|
||||
};
|
||||
|
||||
debug!("getaddrinfo hook for: {}", hostname);
|
||||
@@ -475,7 +475,7 @@ pub unsafe fn hook_gethostbyname(name: *const c_char) -> *mut libc::hostent {
|
||||
// Get the hostname
|
||||
let hostname = match CStr::from_ptr(name).to_str() {
|
||||
Ok(s) => s.to_string(),
|
||||
Err(_) => return original_gethostbyname(name),
|
||||
Err(_) => return std::ptr::null_mut(),
|
||||
};
|
||||
|
||||
debug!("gethostbyname hook for: {}", hostname);
|
||||
@@ -504,7 +504,7 @@ pub unsafe fn hook_gethostbyname(name: *const c_char) -> *mut libc::hostent {
|
||||
|
||||
let name_cstr = match CString::new(hostname) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return original_gethostbyname(name),
|
||||
Err(_) => return std::ptr::null_mut(),
|
||||
};
|
||||
|
||||
GETHOSTBYNAME_STORE.with(|slot| {
|
||||
|
||||
@@ -16,10 +16,12 @@ use parking_lot::Mutex;
|
||||
use rand::seq::SliceRandom;
|
||||
use tracing::{debug, error, info, warn};
|
||||
use windows::Win32::Networking::WinSock::{
|
||||
ADDRINFOA, AF_INET, AF_INET6, IN_ADDR, IN_ADDR_0, IPPROTO_TCP, SOCKADDR, SOCKADDR_IN,
|
||||
SOCK_STREAM, SOCKET_ERROR, WSAEALREADY, WSAECONNREFUSED, WSAEFAULT, WSAEINPROGRESS, WSAEINVAL,
|
||||
WSAEWOULDBLOCK, WSAGetLastError, WSAHOST_NOT_FOUND, WSASetLastError,
|
||||
ADDRINFOA, ADDRINFOW, AF_INET, AF_INET6, IN_ADDR, IN_ADDR_0, IPPROTO_TCP, SOCKADDR,
|
||||
SOCKADDR_IN, SOCK_STREAM, SOCKET_ERROR, WSAEALREADY, WSAECONNREFUSED, WSAEFAULT,
|
||||
WSAEINPROGRESS, WSAEINVAL, WSAEOPNOTSUPP, WSAEWOULDBLOCK, WSAGetLastError, WSAHOST_NOT_FOUND,
|
||||
WSASetLastError,
|
||||
};
|
||||
use windows::core::GUID;
|
||||
|
||||
use crate::config::{ChainType, Config, ProxyData, ProxyState};
|
||||
use crate::dns::{is_fake_ip, DnsResolver};
|
||||
@@ -30,7 +32,7 @@ use crate::ConfigParser;
|
||||
|
||||
use super::interpose_windows::{
|
||||
init_original_functions, original_connect, original_freeaddrinfo, original_getaddrinfo,
|
||||
original_gethostbyname, original_getnameinfo,
|
||||
original_getaddrinfow, original_gethostbyname, original_getnameinfo, original_wsa_ioctl,
|
||||
};
|
||||
use super::reload::config_reload_interval;
|
||||
|
||||
@@ -66,6 +68,7 @@ static CUSTOM_ADDRINFO_ALLOCATIONS: OnceLock<Mutex<HashMap<usize, CustomAddrinfo
|
||||
struct CustomAddrinfoAllocation {
|
||||
sockaddr_ptr: usize,
|
||||
family: i32,
|
||||
is_wide: bool,
|
||||
}
|
||||
|
||||
fn custom_alloc_map() -> &'static Mutex<HashMap<usize, CustomAddrinfoAllocation>> {
|
||||
@@ -307,6 +310,35 @@ fn parse_service_port(service: *const i8) -> u16 {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_service_port_wide(service: *const u16) -> u16 {
|
||||
if service.is_null() {
|
||||
return 0;
|
||||
}
|
||||
unsafe {
|
||||
let mut len = 0usize;
|
||||
while *service.add(len) != 0 {
|
||||
len += 1;
|
||||
}
|
||||
let slice = std::slice::from_raw_parts(service, len);
|
||||
let s = String::from_utf16_lossy(slice);
|
||||
s.parse::<u16>().unwrap_or(0)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_wide_string(ptr: *const u16) -> std::result::Result<String, ()> {
|
||||
if ptr.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
unsafe {
|
||||
let mut len = 0usize;
|
||||
while *ptr.add(len) != 0 {
|
||||
len += 1;
|
||||
}
|
||||
let slice = std::slice::from_raw_parts(ptr, len);
|
||||
String::from_utf16(slice).map_err(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Windows connect hook implementation.
|
||||
#[cfg(windows)]
|
||||
pub unsafe extern "system" fn hook_connect_impl(
|
||||
@@ -423,6 +455,104 @@ pub unsafe extern "system" fn hook_wsa_connect_impl(
|
||||
hook_connect_impl(sock, name, namelen)
|
||||
}
|
||||
|
||||
const SIO_GET_EXTENSION_FUNCTION_POINTER: u32 = 0xC800_0006;
|
||||
const WSAID_CONNECTEX: GUID = GUID::from_u128(0x25a207b9_ddf3_4660_8ee9_76e58c74063e);
|
||||
|
||||
/// ConnectEx replacement used when applications query extension function pointers via WSAIoctl.
|
||||
///
|
||||
/// This implementation is intentionally synchronous:
|
||||
/// - `lpOverlapped != NULL` is rejected (`WSAEOPNOTSUPP`) to avoid silent bypass.
|
||||
/// - `dwSendDataLength > 0` is rejected (`WSAEOPNOTSUPP`) because initial-send semantics are
|
||||
/// not yet mirrored.
|
||||
#[cfg(windows)]
|
||||
pub unsafe extern "system" fn hook_connect_ex_impl(
|
||||
sock: usize,
|
||||
name: *const c_void,
|
||||
namelen: i32,
|
||||
send_buf: *const c_void,
|
||||
send_len: u32,
|
||||
bytes_sent: *mut u32,
|
||||
overlapped: *mut c_void,
|
||||
) -> i32 {
|
||||
if !overlapped.is_null() || (!send_buf.is_null() && send_len > 0) {
|
||||
WSASetLastError(WSAEOPNOTSUPP.0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if !bytes_sent.is_null() {
|
||||
*bytes_sent = 0;
|
||||
}
|
||||
|
||||
let ret = hook_connect_impl(sock, name, namelen);
|
||||
if ret == SOCKET_ERROR {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
/// WSAIoctl hook implementation.
|
||||
///
|
||||
/// Intercepts `SIO_GET_EXTENSION_FUNCTION_POINTER` for `WSAID_CONNECTEX` and returns our own
|
||||
/// ConnectEx function pointer so ConnectEx-based clients still go through proxy enforcement.
|
||||
#[cfg(windows)]
|
||||
pub unsafe extern "system" fn hook_wsa_ioctl_impl(
|
||||
sock: usize,
|
||||
io_control_code: u32,
|
||||
in_buffer: *mut c_void,
|
||||
in_buffer_len: u32,
|
||||
out_buffer: *mut c_void,
|
||||
out_buffer_len: u32,
|
||||
bytes_returned: *mut u32,
|
||||
overlapped: *mut c_void,
|
||||
completion_routine: *mut c_void,
|
||||
) -> i32 {
|
||||
if io_control_code != SIO_GET_EXTENSION_FUNCTION_POINTER
|
||||
|| in_buffer_len < std::mem::size_of::<GUID>() as u32
|
||||
|| out_buffer_len < std::mem::size_of::<*const c_void>() as u32
|
||||
|| in_buffer.is_null()
|
||||
|| out_buffer.is_null()
|
||||
{
|
||||
return original_wsa_ioctl(
|
||||
sock,
|
||||
io_control_code,
|
||||
in_buffer,
|
||||
in_buffer_len,
|
||||
out_buffer,
|
||||
out_buffer_len,
|
||||
bytes_returned,
|
||||
overlapped,
|
||||
completion_routine,
|
||||
);
|
||||
}
|
||||
|
||||
let requested = *(in_buffer as *const GUID);
|
||||
if requested != WSAID_CONNECTEX {
|
||||
return original_wsa_ioctl(
|
||||
sock,
|
||||
io_control_code,
|
||||
in_buffer,
|
||||
in_buffer_len,
|
||||
out_buffer,
|
||||
out_buffer_len,
|
||||
bytes_returned,
|
||||
overlapped,
|
||||
completion_routine,
|
||||
);
|
||||
}
|
||||
|
||||
let replacement = hook_connect_ex_impl as *const c_void;
|
||||
std::ptr::copy_nonoverlapping(
|
||||
&replacement as *const *const c_void as *const u8,
|
||||
out_buffer as *mut u8,
|
||||
std::mem::size_of::<*const c_void>(),
|
||||
);
|
||||
if !bytes_returned.is_null() {
|
||||
*bytes_returned = std::mem::size_of::<*const c_void>() as u32;
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
/// Windows getaddrinfo hook implementation.
|
||||
#[cfg(windows)]
|
||||
pub unsafe extern "system" fn hook_getaddrinfo_impl(
|
||||
@@ -443,7 +573,7 @@ pub unsafe extern "system" fn hook_getaddrinfo_impl(
|
||||
let dns_resolver = DnsResolver::new(config.proxy_dns, config.remote_dns_subnet);
|
||||
|
||||
if pnode.is_null() || ppresult.is_null() {
|
||||
return original_getaddrinfo(pnode, pservice, phints, ppresult);
|
||||
return WSAEINVAL.0;
|
||||
}
|
||||
|
||||
let requested_family = if phints.is_null() {
|
||||
@@ -461,7 +591,7 @@ pub unsafe extern "system" fn hook_getaddrinfo_impl(
|
||||
|
||||
let hostname = match CStr::from_ptr(pnode).to_str() {
|
||||
Ok(s) => s,
|
||||
Err(_) => return original_getaddrinfo(pnode, pservice, phints, ppresult),
|
||||
Err(_) => return WSAEINVAL.0,
|
||||
};
|
||||
|
||||
if hostname.parse::<IpAddr>().is_ok() {
|
||||
@@ -518,6 +648,7 @@ pub unsafe extern "system" fn hook_getaddrinfo_impl(
|
||||
CustomAddrinfoAllocation {
|
||||
sockaddr_ptr: sockaddr_ptr as usize,
|
||||
family: requested_family,
|
||||
is_wide: false,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -525,6 +656,109 @@ pub unsafe extern "system" fn hook_getaddrinfo_impl(
|
||||
0
|
||||
}
|
||||
|
||||
/// Windows GetAddrInfoW hook implementation.
|
||||
#[cfg(windows)]
|
||||
pub unsafe extern "system" fn hook_getaddrinfow_impl(
|
||||
pnode: *const u16,
|
||||
pservice: *const u16,
|
||||
phints: *const c_void,
|
||||
ppresult: *mut *mut c_void,
|
||||
) -> i32 {
|
||||
let state = match get_hook_state() {
|
||||
Some(s) => s,
|
||||
None => return original_getaddrinfow(pnode, pservice, phints, ppresult),
|
||||
};
|
||||
maybe_reload_config(state);
|
||||
let config = state.config.lock().clone();
|
||||
if !config.proxy_dns {
|
||||
return original_getaddrinfow(pnode, pservice, phints, ppresult);
|
||||
}
|
||||
let dns_resolver = DnsResolver::new(config.proxy_dns, config.remote_dns_subnet);
|
||||
|
||||
if pnode.is_null() || ppresult.is_null() {
|
||||
return WSAEINVAL.0;
|
||||
}
|
||||
|
||||
let requested_family = if phints.is_null() {
|
||||
AF_INET.0 as i32
|
||||
} else {
|
||||
let hints = &*(phints as *const ADDRINFOW);
|
||||
if hints.ai_family == 0 || hints.ai_family == AF_INET.0 as i32 {
|
||||
AF_INET.0 as i32
|
||||
} else if hints.ai_family == AF_INET6.0 as i32 {
|
||||
AF_INET6.0 as i32
|
||||
} else {
|
||||
return WSAHOST_NOT_FOUND.0;
|
||||
}
|
||||
};
|
||||
|
||||
let hostname = match parse_wide_string(pnode) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return WSAEINVAL.0,
|
||||
};
|
||||
|
||||
if hostname.parse::<IpAddr>().is_ok() {
|
||||
return original_getaddrinfow(pnode, pservice, phints, ppresult);
|
||||
}
|
||||
|
||||
if crate::dns::lookup_in_hosts(&hostname).is_some() {
|
||||
return original_getaddrinfow(pnode, pservice, phints, ppresult);
|
||||
}
|
||||
|
||||
let fake_ip = match dns_resolver.resolve(&hostname) {
|
||||
Ok(ip) => ip,
|
||||
Err(e) => {
|
||||
error!("Failed to resolve {} in hook_getaddrinfow_impl: {}", hostname, e);
|
||||
return WSAHOST_NOT_FOUND.0;
|
||||
}
|
||||
};
|
||||
|
||||
let service_port = parse_service_port_wide(pservice);
|
||||
let (sockaddr_ptr, addrlen) = if requested_family == AF_INET6.0 as i32 {
|
||||
let sockaddr_raw = Box::new(make_sockaddr_in6_mapped_bytes(fake_ip, service_port));
|
||||
(
|
||||
Box::into_raw(sockaddr_raw) as *mut SOCKADDR,
|
||||
mem::size_of::<[u8; 28]>(),
|
||||
)
|
||||
} else {
|
||||
let sockaddr_box = Box::new(make_sockaddr_in(fake_ip, service_port));
|
||||
(
|
||||
Box::into_raw(sockaddr_box) as *mut SOCKADDR,
|
||||
mem::size_of::<SOCKADDR_IN>(),
|
||||
)
|
||||
};
|
||||
|
||||
let mut ai = ADDRINFOW::default();
|
||||
ai.ai_family = requested_family;
|
||||
ai.ai_socktype = if phints.is_null() {
|
||||
SOCK_STREAM.0
|
||||
} else {
|
||||
(*(phints as *const ADDRINFOW)).ai_socktype
|
||||
};
|
||||
ai.ai_protocol = if phints.is_null() {
|
||||
IPPROTO_TCP.0
|
||||
} else {
|
||||
(*(phints as *const ADDRINFOW)).ai_protocol
|
||||
};
|
||||
ai.ai_addrlen = addrlen;
|
||||
ai.ai_addr = sockaddr_ptr;
|
||||
ai.ai_next = std::ptr::null_mut();
|
||||
|
||||
let ai_ptr = Box::into_raw(Box::new(ai));
|
||||
*ppresult = ai_ptr as *mut c_void;
|
||||
custom_alloc_map().lock().insert(
|
||||
ai_ptr as usize,
|
||||
CustomAddrinfoAllocation {
|
||||
sockaddr_ptr: sockaddr_ptr as usize,
|
||||
family: requested_family,
|
||||
is_wide: true,
|
||||
},
|
||||
);
|
||||
|
||||
debug!("Assigned fake IP {} for {} (GetAddrInfoW)", fake_ip, hostname);
|
||||
0
|
||||
}
|
||||
|
||||
/// Windows freeaddrinfo hook implementation.
|
||||
#[cfg(windows)]
|
||||
pub unsafe extern "system" fn hook_freeaddrinfo_impl(pres: *mut c_void) {
|
||||
@@ -538,7 +772,11 @@ pub unsafe extern "system" fn hook_freeaddrinfo_impl(pres: *mut c_void) {
|
||||
} else {
|
||||
let _ = Box::from_raw(alloc.sockaddr_ptr as *mut SOCKADDR_IN);
|
||||
}
|
||||
let _ = Box::from_raw(pres as *mut ADDRINFOA);
|
||||
if alloc.is_wide {
|
||||
let _ = Box::from_raw(pres as *mut ADDRINFOW);
|
||||
} else {
|
||||
let _ = Box::from_raw(pres as *mut ADDRINFOA);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ use windows::Win32::Networking::WinSock::{WSAECONNREFUSED, WSAHOST_NOT_FOUND, WS
|
||||
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_wsa_connect_impl,
|
||||
hook_connect_impl, hook_freeaddrinfo_impl, hook_getaddrinfo_impl, hook_getaddrinfow_impl,
|
||||
hook_gethostbyname_impl, hook_getnameinfo_impl, hook_wsa_connect_impl, hook_wsa_ioctl_impl,
|
||||
};
|
||||
|
||||
type ConnectFn = unsafe extern "system" fn(usize, *const c_void, i32) -> i32;
|
||||
@@ -30,17 +30,32 @@ type WsaConnectFn = unsafe extern "system" fn(
|
||||
) -> i32;
|
||||
type GetAddrInfoFn =
|
||||
unsafe extern "system" fn(*const i8, *const i8, *const c_void, *mut *mut c_void) -> i32;
|
||||
type GetAddrInfoWFn =
|
||||
unsafe extern "system" fn(*const u16, *const u16, *const c_void, *mut *mut c_void) -> i32;
|
||||
type FreeAddrInfoFn = unsafe extern "system" fn(*mut c_void);
|
||||
type GetHostByNameFn = unsafe extern "system" fn(*const i8) -> *mut c_void;
|
||||
type GetNameInfoFn =
|
||||
unsafe extern "system" fn(*const c_void, i32, *mut i8, u32, *mut i8, u32, i32) -> i32;
|
||||
type WsaIoctlFn = unsafe extern "system" fn(
|
||||
usize,
|
||||
u32,
|
||||
*mut c_void,
|
||||
u32,
|
||||
*mut c_void,
|
||||
u32,
|
||||
*mut u32,
|
||||
*mut c_void,
|
||||
*mut c_void,
|
||||
) -> i32;
|
||||
|
||||
static ORIGINAL_CONNECT: OnceLock<ConnectFn> = OnceLock::new();
|
||||
static ORIGINAL_WSA_CONNECT: OnceLock<WsaConnectFn> = OnceLock::new();
|
||||
static ORIGINAL_GETADDRINFO: OnceLock<GetAddrInfoFn> = OnceLock::new();
|
||||
static ORIGINAL_GETADDRINFOW: OnceLock<GetAddrInfoWFn> = OnceLock::new();
|
||||
static ORIGINAL_FREEADDRINFO: OnceLock<FreeAddrInfoFn> = OnceLock::new();
|
||||
static ORIGINAL_GETHOSTBYNAME: OnceLock<GetHostByNameFn> = OnceLock::new();
|
||||
static ORIGINAL_GETNAMEINFO: OnceLock<GetNameInfoFn> = OnceLock::new();
|
||||
static ORIGINAL_WSA_IOCTL: OnceLock<WsaIoctlFn> = OnceLock::new();
|
||||
|
||||
static HOOKS_READY: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
@@ -68,10 +83,16 @@ impl OriginalFunctions {
|
||||
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 wsa_ioctl_fn: WsaIoctlFn =
|
||||
install_api_hook("WSAIoctl", hook_wsa_ioctl_impl as *const () as *mut c_void)?;
|
||||
let getaddrinfo_fn: GetAddrInfoFn = install_api_hook(
|
||||
"getaddrinfo",
|
||||
hook_getaddrinfo_impl as *const () as *mut c_void,
|
||||
)?;
|
||||
let getaddrinfow_fn: GetAddrInfoWFn = install_api_hook(
|
||||
"GetAddrInfoW",
|
||||
hook_getaddrinfow_impl as *const () as *mut c_void,
|
||||
)?;
|
||||
let freeaddrinfo_fn: FreeAddrInfoFn = install_api_hook(
|
||||
"freeaddrinfo",
|
||||
hook_freeaddrinfo_impl as *const () as *mut c_void,
|
||||
@@ -87,7 +108,9 @@ impl OriginalFunctions {
|
||||
|
||||
let _ = ORIGINAL_CONNECT.set(connect_fn);
|
||||
let _ = ORIGINAL_WSA_CONNECT.set(wsa_connect_fn);
|
||||
let _ = ORIGINAL_WSA_IOCTL.set(wsa_ioctl_fn);
|
||||
let _ = ORIGINAL_GETADDRINFO.set(getaddrinfo_fn);
|
||||
let _ = ORIGINAL_GETADDRINFOW.set(getaddrinfow_fn);
|
||||
let _ = ORIGINAL_FREEADDRINFO.set(freeaddrinfo_fn);
|
||||
let _ = ORIGINAL_GETHOSTBYNAME.set(gethostbyname_fn);
|
||||
let _ = ORIGINAL_GETNAMEINFO.set(getnameinfo_fn);
|
||||
@@ -154,6 +177,36 @@ pub unsafe fn original_wsa_connect(
|
||||
}
|
||||
}
|
||||
|
||||
/// Call the original WSAIoctl function.
|
||||
pub unsafe fn original_wsa_ioctl(
|
||||
sock: usize,
|
||||
io_control_code: u32,
|
||||
in_buffer: *mut c_void,
|
||||
in_buffer_len: u32,
|
||||
out_buffer: *mut c_void,
|
||||
out_buffer_len: u32,
|
||||
bytes_returned: *mut u32,
|
||||
overlapped: *mut c_void,
|
||||
completion_routine: *mut c_void,
|
||||
) -> i32 {
|
||||
if let Some(f) = ORIGINAL_WSA_IOCTL.get() {
|
||||
f(
|
||||
sock,
|
||||
io_control_code,
|
||||
in_buffer,
|
||||
in_buffer_len,
|
||||
out_buffer,
|
||||
out_buffer_len,
|
||||
bytes_returned,
|
||||
overlapped,
|
||||
completion_routine,
|
||||
)
|
||||
} else {
|
||||
WSASetLastError(WSAECONNREFUSED.0);
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
/// Call the original getaddrinfo function.
|
||||
pub unsafe fn original_getaddrinfo(
|
||||
node: *const i8,
|
||||
@@ -169,6 +222,21 @@ pub unsafe fn original_getaddrinfo(
|
||||
}
|
||||
}
|
||||
|
||||
/// Call the original GetAddrInfoW function.
|
||||
pub unsafe fn original_getaddrinfow(
|
||||
node: *const u16,
|
||||
service: *const u16,
|
||||
hints: *const c_void,
|
||||
res: *mut *mut c_void,
|
||||
) -> i32 {
|
||||
if let Some(f) = ORIGINAL_GETADDRINFOW.get() {
|
||||
f(node, service, hints, res)
|
||||
} else {
|
||||
WSASetLastError(WSAHOST_NOT_FOUND.0);
|
||||
WSAHOST_NOT_FOUND.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Call the original freeaddrinfo function.
|
||||
pub unsafe fn original_freeaddrinfo(res: *mut c_void) {
|
||||
if let Some(f) = ORIGINAL_FREEADDRINFO.get() {
|
||||
|
||||
@@ -152,6 +152,10 @@ pub fn get_ip_from_sockaddr(addr: *const libc::sockaddr) -> Option<Ipv4Addr> {
|
||||
let addr_in = addr as *const libc::sockaddr_in;
|
||||
let ip_bytes = (*addr_in).sin_addr.s_addr.to_ne_bytes();
|
||||
Some(Ipv4Addr::from(ip_bytes))
|
||||
} else if sa_family == libc::AF_INET6 as libc::sa_family_t {
|
||||
let addr_in6 = addr as *const libc::sockaddr_in6;
|
||||
let v6 = Ipv6Addr::from((*addr_in6).sin6_addr.s6_addr);
|
||||
v6.to_ipv4_mapped()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -201,4 +205,22 @@ mod tests {
|
||||
assert!(is_localhost(&Ipv4Addr::new(127, 0, 0, 1)));
|
||||
assert!(!is_localhost(&Ipv4Addr::new(192, 168, 1, 1)));
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
#[test]
|
||||
fn test_get_ip_from_sockaddr_ipv6_mapped() {
|
||||
let sockaddr = libc::sockaddr_in6 {
|
||||
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
|
||||
sin6_len: std::mem::size_of::<libc::sockaddr_in6>() as u8,
|
||||
sin6_family: libc::AF_INET6 as libc::sa_family_t,
|
||||
sin6_port: 0,
|
||||
sin6_flowinfo: 0,
|
||||
sin6_addr: libc::in6_addr {
|
||||
s6_addr: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 5, 6, 7, 8],
|
||||
},
|
||||
sin6_scope_id: 0,
|
||||
};
|
||||
let ptr = &sockaddr as *const libc::sockaddr_in6 as *const libc::sockaddr;
|
||||
assert_eq!(get_ip_from_sockaddr(ptr), Some(Ipv4Addr::new(5, 6, 7, 8)));
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,13 @@ pub fn get_ip_from_sockaddr(addr: *const core::ffi::c_void) -> Option<Ipv4Addr>
|
||||
*ip_ptr.add(3),
|
||||
];
|
||||
Some(Ipv4Addr::from(ip_bytes))
|
||||
} else if sa_family == 23 {
|
||||
// Handle IPv4-mapped IPv6 (::ffff:a.b.c.d) so reverse fake-DNS lookup works.
|
||||
let ip_ptr = (addr as *const u8).add(8);
|
||||
let mut octets = [0u8; 16];
|
||||
std::ptr::copy_nonoverlapping(ip_ptr, octets.as_mut_ptr(), 16);
|
||||
let v6 = Ipv6Addr::from(octets);
|
||||
v6.to_ipv4_mapped()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -174,4 +181,16 @@ mod tests {
|
||||
)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_ip_from_sockaddr_ipv6_mapped() {
|
||||
let mut raw = [0u8; 28];
|
||||
raw[0] = 23; // AF_INET6
|
||||
raw[1] = 0;
|
||||
raw[8..24].copy_from_slice(&[
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 1, 2, 3, 4,
|
||||
]);
|
||||
let addr_ptr = raw.as_ptr() as *const core::ffi::c_void;
|
||||
assert_eq!(get_ip_from_sockaddr(addr_ptr), Some(Ipv4Addr::new(1, 2, 3, 4)));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user