fix: disable SO_REUSEADDR & enable SO_EXCLUSIVEADDRUSE on Windows (#2128)

This commit is contained in:
Luna Yao
2026-04-24 18:37:34 +02:00
committed by GitHub
parent 4688ad74ad
commit b4666be696
5 changed files with 122 additions and 41 deletions
+6 -10
View File
@@ -274,11 +274,15 @@ windivert = { git = "https://github.com/EasyTier/windivert-rust.git", rev = "adc
] }
[target.'cfg(windows)'.dependencies]
windows = { version = "0.52.0", features = [
windows = { version = "0.62.2", features = [
"Win32_Foundation",
"Win32_NetworkManagement_IpHelper",
"Win32_NetworkManagement_Ndis",
"Win32_NetworkManagement_WindowsFirewall",
"Win32_System_Com",
"Win32_Networking",
"Win32_System_Com",
"Win32_System_Diagnostics",
"Win32_System_Diagnostics_Debug",
"Win32_System_Ole",
"Win32_System_Variant",
"Win32_Networking_WinSock",
@@ -287,14 +291,6 @@ windows = { version = "0.52.0", features = [
encoding = "0.2"
winreg = "0.52"
windows-service = "0.7.0"
windows-sys = { version = "0.52", features = [
"Win32_NetworkManagement_IpHelper",
"Win32_NetworkManagement_Ndis",
"Win32_Networking_WinSock",
"Win32_Foundation",
"Win32_System_Diagnostics",
"Win32_System_Diagnostics_Debug",
] }
winapi = { version = "0.3.9", features = ["impl-default"] }
[target.'cfg(not(windows))'.dependencies]
+19 -16
View File
@@ -4,15 +4,16 @@ use anyhow::Context;
use network_interface::NetworkInterfaceConfig;
use windows::{
Win32::{
Foundation::{BOOL, FALSE},
Foundation::FALSE,
NetworkManagement::WindowsFirewall::{
INetFwPolicy2, INetFwRule, NET_FW_ACTION_ALLOW, NET_FW_PROFILE2_DOMAIN,
NET_FW_PROFILE2_PRIVATE, NET_FW_PROFILE2_PUBLIC, NET_FW_RULE_DIR_IN,
NET_FW_RULE_DIR_OUT,
},
Networking::WinSock::{
IP_UNICAST_IF, IPPROTO_IP, IPPROTO_IPV6, IPV6_UNICAST_IF, SIO_UDP_CONNRESET, SOCKET,
SOCKET_ERROR, WSAGetLastError, WSAIoctl, htonl, setsockopt,
IP_UNICAST_IF, IPPROTO_IP, IPPROTO_IPV6, IPV6_UNICAST_IF, SIO_UDP_CONNRESET,
SO_EXCLUSIVEADDRUSE, SOCKET, SOCKET_ERROR, SOL_SOCKET, WSAGetLastError, WSAIoctl,
htonl, setsockopt,
},
System::Com::{
CLSCTX_ALL, COINIT_MULTITHREADED, CoCreateInstance, CoInitializeEx, CoUninitialize,
@@ -20,7 +21,7 @@ use windows::{
System::Ole::{SafeArrayCreateVector, SafeArrayPutElement},
System::Variant::{VARENUM, VARIANT, VT_ARRAY, VT_BSTR, VT_VARIANT},
},
core::BSTR,
core::{BOOL, BSTR},
};
pub fn disable_connection_reset<S: AsRawSocket>(socket: &S) -> io::Result<()> {
@@ -88,13 +89,7 @@ pub fn find_interface_index(iface_name: &str) -> io::Result<u32> {
))
}
pub fn set_ip_unicast_if<S: AsRawSocket>(
socket: &S,
addr: &SocketAddr,
iface: &str,
) -> io::Result<()> {
let handle = SOCKET(socket.as_raw_socket() as usize);
pub fn set_ip_unicast_if(socket: SOCKET, addr: &SocketAddr, iface: &str) -> io::Result<()> {
let if_index = find_interface_index(iface)?;
unsafe {
@@ -103,12 +98,12 @@ pub fn set_ip_unicast_if<S: AsRawSocket>(
SocketAddr::V4(..) => {
let if_index = htonl(if_index);
let if_index_bytes = if_index.to_ne_bytes();
setsockopt(handle, IPPROTO_IP.0, IP_UNICAST_IF, Some(&if_index_bytes))
setsockopt(socket, IPPROTO_IP.0, IP_UNICAST_IF, Some(&if_index_bytes))
}
SocketAddr::V6(..) => {
let if_index_bytes = if_index.to_ne_bytes();
setsockopt(
handle,
socket,
IPPROTO_IPV6.0,
IPV6_UNICAST_IF,
Some(&if_index_bytes),
@@ -141,8 +136,16 @@ pub fn setup_socket_for_win<S: AsRawSocket>(
disable_connection_reset(socket)?;
}
let socket = SOCKET(socket.as_raw_socket() as usize);
let optval = 1_i32.to_ne_bytes();
unsafe {
if setsockopt(socket, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, Some(&optval)) == SOCKET_ERROR {
return Err(io::Error::last_os_error());
}
}
if let Some(iface) = bind_dev {
set_ip_unicast_if(socket, bind_addr, iface.as_str())?;
set_ip_unicast_if(socket, bind_addr, &iface)?;
}
Ok(())
@@ -152,7 +155,7 @@ struct ComInitializer;
impl ComInitializer {
fn new() -> windows::core::Result<Self> {
unsafe { CoInitializeEx(None, COINIT_MULTITHREADED)? };
unsafe { CoInitializeEx(None, COINIT_MULTITHREADED).ok()? };
Ok(Self)
}
}
@@ -354,7 +357,7 @@ fn add_protocol_firewall_rules(
(*interface_variant.Anonymous.Anonymous).vt = VARENUM(VT_ARRAY.0 | VT_VARIANT.0);
(*interface_variant.Anonymous.Anonymous).Anonymous.parray = interface_array;
rule.SetInterfaces(interface_variant)?;
rule.SetInterfaces(&interface_variant)?;
// Get rule collection and add new rule
let rules = policy.Rules()?;
+9 -8
View File
@@ -6,15 +6,16 @@ use cidr::{Ipv4Inet, Ipv6Inet};
use std::{
io,
net::{Ipv4Addr, Ipv6Addr},
ptr::null_mut,
};
use windows_sys::Win32::{
use windows::Win32::NetworkManagement::IpHelper::INTERNAL_IF_OPER_STATUS;
use windows::Win32::{
Foundation::NO_ERROR,
NetworkManagement::IpHelper::{GetIfEntry, MIB_IFROW, SetIfEntry},
System::Diagnostics::Debug::{
FORMAT_MESSAGE_FROM_SYSTEM, FORMAT_MESSAGE_IGNORE_INSERTS, FormatMessageW,
},
};
use windows::core::PWSTR;
use winreg::{
RegKey,
enums::{HKEY_LOCAL_MACHINE, KEY_READ, KEY_WRITE},
@@ -32,12 +33,12 @@ fn format_win_error(error: u32) -> String {
unsafe {
FormatMessageW(
flags,
null_mut(),
None,
error,
0,
buffer.as_mut_ptr(),
PWSTR(buffer.as_mut_ptr()),
size,
null_mut(),
None,
);
}
let str_end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len());
@@ -100,7 +101,7 @@ impl WindowsIfConfiger {
dwPhysAddrLen: 0,
bPhysAddr: [0; 8],
dwAdminStatus: if up { 1 } else { 2 }, // 1 = up, 2 = down
dwOperStatus: 0,
dwOperStatus: INTERNAL_IF_OPER_STATUS(0),
dwLastChange: 0,
dwInOctets: 0,
dwInUcastPkts: 0,
@@ -118,8 +119,8 @@ impl WindowsIfConfiger {
bDescr: [0; 256],
};
if GetIfEntry(&mut if_row) == NO_ERROR {
if SetIfEntry(&if_row) == NO_ERROR {
if GetIfEntry(&mut if_row) == NO_ERROR.0 {
if SetIfEntry(&if_row) == NO_ERROR.0 {
Ok(())
} else {
Err(anyhow::anyhow!("Failed to set interface status").into())
+1 -1
View File
@@ -423,7 +423,7 @@ fn setup_socket2_ext(
}
socket2_socket.set_nonblocking(true)?;
socket2_socket.set_reuse_address(true)?;
socket2_socket.set_reuse_address(!cfg!(target_os = "windows"))?;
if let Err(e) = socket2_socket.bind(&socket2::SockAddr::from(*bind_addr)) {
if bind_addr.is_ipv4() {
return Err(e.into());