From e2ff79aeb3578de7d5b439437bc5058edbf9cdfa Mon Sep 17 00:00:00 2001 From: YISH Date: Thu, 26 Sep 2024 19:21:54 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Ignore=20UdpSocket's=20WSAECONNR?= =?UTF-8?q?ESET=20error=20on=20Windows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 1 + src/server/mod.rs | 135 +++++++++++----------------------------------- src/server/net.rs | 130 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+), 103 deletions(-) create mode 100644 src/server/net.rs diff --git a/Cargo.toml b/Cargo.toml index 6647c966..bd63c03b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -196,6 +196,7 @@ self_update = { version = "0.40.0", default-features = false, features=["archive windows = { version = "0.57", features = [ "Win32_System_Console", "Win32_Foundation", + "Win32_Networking", "Win32_Networking_WinSock", "Win32_System_IO", # set UDP_CONNRESET off ] } windows-service = "0.7.0" self_update = { version = "0.40.0", default-features = false, features=["archive-zip", "compression-zip-deflate", "rustls"], optional = true } diff --git a/src/server/mod.rs b/src/server/mod.rs index 6aa26d94..4b987470 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -2,6 +2,7 @@ mod https; #[cfg(feature = "legacy_dns_server")] mod legacy; +mod net; #[cfg(not(feature = "legacy_dns_server"))] mod protocol; #[cfg(feature = "dns-over-quic")] @@ -49,18 +50,28 @@ pub async fn serve( certificate_key_file: Option<&Path>, ) -> Result { use crate::rustls::load_certificate_and_key; - use net::{bind_to, tcp, udp}; + use net::{bind_to, setup_tcp_socket, setup_udp_socket}; use std::time::Duration; let dns_handle = handle.with_new_opt(listener_config.server_opts().clone()); let token = match listener_config { ListenerConfig::Udp(listener) => { - let udp_socket = bind_to(udp, listener.sock_addr(), listener.device(), "UDP"); + let udp_socket = bind_to( + setup_udp_socket, + listener.sock_addr(), + listener.device(), + "UDP", + ); udp::serve(udp_socket, dns_handle) } ListenerConfig::Tcp(listener) => { - let tcp_listener = bind_to(tcp, listener.sock_addr(), listener.device(), "TCP"); + let tcp_listener = bind_to( + setup_tcp_socket, + listener.sock_addr(), + listener.device(), + "TCP", + ); tcp::serve(tcp_listener, dns_handle, Duration::from_secs(idle_time)) } #[cfg(feature = "dns-over-tls")] @@ -75,7 +86,12 @@ pub async fn serve( LISTENER_TYPE, )?; - let tls_listener = bind_to(tcp, listener.sock_addr(), listener.device(), LISTENER_TYPE); + let tls_listener = bind_to( + setup_tcp_socket, + listener.sock_addr(), + listener.device(), + LISTENER_TYPE, + ); tls::serve( tls_listener, @@ -96,8 +112,12 @@ pub async fn serve( LISTENER_TYPE, )?; - let https_listener = - bind_to(tcp, listener.sock_addr(), listener.device(), LISTENER_TYPE); + let https_listener = bind_to( + setup_tcp_socket, + listener.sock_addr(), + listener.device(), + LISTENER_TYPE, + ); let app = app.clone(); https::serve( @@ -121,7 +141,12 @@ pub async fn serve( LISTENER_TYPE, )?; - let quic_listener = bind_to(udp, listener.sock_addr(), listener.device(), "QUIC"); + let quic_listener = bind_to( + setup_udp_socket, + listener.sock_addr(), + listener.device(), + "QUIC", + ); quic::serve( quic_listener, @@ -270,99 +295,3 @@ fn sanitize_src_address(src: SocketAddr) -> Result<(), String> { IpAddr::V6(v6) => verify_v6(v6), } } - -mod net { - use crate::log; - use std::{io, net::SocketAddr}; - use tokio::net::{TcpListener, UdpSocket}; - - pub fn bind_to( - func: impl Fn(SocketAddr, Option<&str>, &str) -> io::Result, - sock_addr: SocketAddr, - bind_device: Option<&str>, - bind_type: &str, - ) -> T { - func(sock_addr, bind_device, bind_type).unwrap_or_else(|err| { - panic!("cound not bind to {bind_type}: {sock_addr}, {err}"); - }) - } - - pub fn tcp( - sock_addr: SocketAddr, - bind_device: Option<&str>, - bind_type: &str, - ) -> io::Result { - let device_note = bind_device - .map(|device| format!("@{device}")) - .unwrap_or_default(); - - log::debug!("binding {} to {:?}{}", bind_type, sock_addr, device_note); - let tcp_listener = std::net::TcpListener::bind(sock_addr)?; - - { - let sock_ref = socket2::SockRef::from(&tcp_listener); - sock_ref.set_nonblocking(true)?; - sock_ref.set_reuse_address(true)?; - - #[cfg(target_os = "macos")] - sock_ref.set_reuse_port(true)?; - - #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] - if let Some(device) = bind_device { - sock_ref.bind_device(Some(device.as_bytes()))?; - } - } - - let tcp_listener = TcpListener::from_std(tcp_listener)?; - - log::info!( - "listening for {} on {:?}{}", - bind_type, - tcp_listener - .local_addr() - .expect("could not lookup local address"), - device_note - ); - - Ok(tcp_listener) - } - - pub fn udp( - sock_addr: SocketAddr, - bind_device: Option<&str>, - bind_type: &str, - ) -> io::Result { - let device_note = bind_device - .map(|device| format!("@{device}")) - .unwrap_or_default(); - - log::debug!("binding {} to {:?}{}", bind_type, sock_addr, device_note); - let udp_socket = std::net::UdpSocket::bind(sock_addr)?; - - { - let sock_ref = socket2::SockRef::from(&udp_socket); - sock_ref.set_nonblocking(true)?; - sock_ref.set_reuse_address(true)?; - - #[cfg(target_os = "macos")] - sock_ref.set_reuse_port(true)?; - - #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] - if let Some(device) = bind_device { - sock_ref.bind_device(Some(device.as_bytes()))?; - } - } - - let udp_socket = UdpSocket::from_std(udp_socket)?; - - log::info!( - "listening for {} on {:?}{}", - bind_type, - udp_socket - .local_addr() - .expect("could not lookup local address"), - device_note - ); - Ok(udp_socket) - } -} diff --git a/src/server/net.rs b/src/server/net.rs new file mode 100644 index 00000000..ecaa5712 --- /dev/null +++ b/src/server/net.rs @@ -0,0 +1,130 @@ +use crate::log; +use std::{io, net::SocketAddr}; +use tokio::net::{TcpListener, UdpSocket}; + +pub fn bind_to( + func: impl Fn(SocketAddr, Option<&str>, &str) -> io::Result, + sock_addr: SocketAddr, + bind_device: Option<&str>, + bind_type: &str, +) -> T { + func(sock_addr, bind_device, bind_type).unwrap_or_else(|err| { + panic!("cound not bind to {bind_type}: {sock_addr}, {err}"); + }) +} + +pub fn setup_tcp_socket( + sock_addr: SocketAddr, + bind_device: Option<&str>, + bind_type: &str, +) -> io::Result { + let device_note = bind_device + .map(|device| format!("@{device}")) + .unwrap_or_default(); + + log::debug!("binding {} to {:?}{}", bind_type, sock_addr, device_note); + let tcp_listener = std::net::TcpListener::bind(sock_addr)?; + + { + let sock_ref = socket2::SockRef::from(&tcp_listener); + sock_ref.set_nonblocking(true)?; + sock_ref.set_reuse_address(true)?; + + #[cfg(target_os = "macos")] + sock_ref.set_reuse_port(true)?; + + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + if let Some(device) = bind_device { + sock_ref.bind_device(Some(device.as_bytes()))?; + } + } + + let tcp_listener = TcpListener::from_std(tcp_listener)?; + + log::info!( + "listening for {} on {:?}{}", + bind_type, + tcp_listener + .local_addr() + .expect("could not lookup local address"), + device_note + ); + + Ok(tcp_listener) +} + +pub fn setup_udp_socket( + sock_addr: SocketAddr, + bind_device: Option<&str>, + bind_type: &str, +) -> io::Result { + let device_note = bind_device + .map(|device| format!("@{device}")) + .unwrap_or_default(); + + log::debug!("binding {} to {:?}{}", bind_type, sock_addr, device_note); + let udp_socket = std::net::UdpSocket::bind(sock_addr)?; + + { + let sock_ref = socket2::SockRef::from(&udp_socket); + sock_ref.set_nonblocking(true)?; + sock_ref.set_reuse_address(true)?; + + #[cfg(target_os = "macos")] + sock_ref.set_reuse_port(true)?; + + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + if let Some(device) = bind_device { + sock_ref.bind_device(Some(device.as_bytes()))?; + } + } + + // set UDP_CONNRESET off to ignore UdpSocket's WSAECONNRESET error + #[cfg(target_os = "windows")] + { + // https://github.com/mokeyish/smartdns-rs/issues/391 + // https://github.com/shadowsocks/shadowsocks-rust/blob/3b47fa67fac6c2bded73616a284f26c6159cbe9a/src/relay/sys/windows/mod.rs#L17 + use std::ffi::c_void; + use std::{mem, os::windows::io::AsRawSocket, ptr}; + use windows::Win32::Foundation::FALSE; + use windows::Win32::Networking::WinSock::{ + WSAGetLastError, WSAIoctl, SIO_UDP_CONNRESET, SOCKET, SOCKET_ERROR, + }; + + let handle = SOCKET(udp_socket.as_raw_socket() as usize); + let mut bytes_returned: u32 = 0; + let enable = FALSE; + unsafe { + let ret = WSAIoctl( + handle, + SIO_UDP_CONNRESET, + Some(&enable as *const _ as *const c_void), + mem::size_of_val(&enable) as u32, + Some(ptr::null_mut()), + 0, + &mut bytes_returned, + Some(ptr::null_mut()), + None, + ); + + if ret == SOCKET_ERROR { + // ignore the error here, just warn and continue + let err_code = WSAGetLastError(); + log::warn!("WSAIoctl failed with error code {:?}", err_code); + // return Err(td::io::Error::from_raw_os_error(err_code.0)); + } + }; + } + + let udp_socket = UdpSocket::from_std(udp_socket)?; + + log::info!( + "listening for {} on {:?}{}", + bind_type, + udp_socket + .local_addr() + .expect("could not lookup local address"), + device_note + ); + Ok(udp_socket) +}