From 5dd3c920e49122e6a62368b0d9b919839550baae Mon Sep 17 00:00:00 2001 From: EAimTY Date: Fri, 26 May 2023 21:01:56 +0900 Subject: [PATCH] fix error on setting dual-stack socket --- tuic-client/src/connection.rs | 16 ++++- tuic-client/src/main.rs | 2 + tuic-client/src/socks5.rs | 121 +++++++++++++++++++++++----------- tuic-server/src/main.rs | 6 +- tuic-server/src/server.rs | 72 +++++++++++++------- 5 files changed, 150 insertions(+), 67 deletions(-) diff --git a/tuic-client/src/connection.rs b/tuic-client/src/connection.rs index 84cc4da..14e758f 100644 --- a/tuic-client/src/connection.rs +++ b/tuic-client/src/connection.rs @@ -88,13 +88,20 @@ impl Endpoint { config.transport_config(Arc::new(tp_cfg)); - let socket = UdpSocket::bind(SocketAddr::from(([0, 0, 0, 0], 0)))?; + // Try to create an IPv4 socket as the placeholder first, if it fails, try IPv6. + let socket = UdpSocket::bind(SocketAddr::from((Ipv4Addr::UNSPECIFIED, 0))) + .or_else(|err| { + UdpSocket::bind(SocketAddr::from((Ipv6Addr::UNSPECIFIED, 0))).map_err(|_| err) + }) + .map_err(|err| Error::Socket("failed to create endpoint UDP socket", err))?; + let mut ep = QuinnEndpoint::new( EndpointConfig::default(), None, socket, Arc::new(TokioRuntime), )?; + ep.set_default_client_config(config); let ep = Self { @@ -139,7 +146,12 @@ impl Endpoint { SocketAddr::from((Ipv6Addr::UNSPECIFIED, 0)) }; - ep.rebind(UdpSocket::bind(bind_addr)?)?; + ep.rebind( + UdpSocket::bind(bind_addr).map_err(|err| { + Error::Socket("failed to create endpoint UDP socket", err) + })?, + ) + .map_err(|err| Error::Socket("failed to rebind endpoint UDP socket", err))?; } let conn = ep.connect(addr, server_name)?; diff --git a/tuic-client/src/main.rs b/tuic-client/src/main.rs index b6017ee..af84d87 100644 --- a/tuic-client/src/main.rs +++ b/tuic-client/src/main.rs @@ -68,6 +68,8 @@ pub enum Error { LoadNativeCerts(IoError), #[error(transparent)] Rustls(#[from] RustlsError), + #[error("{0}: {1}")] + Socket(&'static str, IoError), #[error("timeout establishing connection")] Timeout, #[error("cannot resolve the server name")] diff --git a/tuic-client/src/socks5.rs b/tuic-client/src/socks5.rs index 128ab78..414ffd2 100644 --- a/tuic-client/src/socks5.rs +++ b/tuic-client/src/socks5.rs @@ -3,7 +3,7 @@ use bytes::Bytes; use once_cell::sync::OnceCell; use parking_lot::Mutex; use quinn::VarInt; -use socket2::Socket; +use socket2::{Domain, Protocol, SockAddr, Socket, Type}; use socks5_proto::{Address, Reply}; use socks5_server::{ auth::{NoAuth, Password}, @@ -30,7 +30,6 @@ static SERVER: OnceCell = OnceCell::new(); pub struct Server { inner: Socks5Server, - addr: SocketAddr, dual_stack: Option, max_pkt_size: usize, next_assoc_id: AtomicU16, @@ -40,22 +39,38 @@ pub struct Server { impl Server { pub async fn set_config(cfg: Local) -> Result<(), Error> { let socket = { - let socket = Socket::from(TcpListener::bind(&cfg.server).await?.into_std()?); + let domain = match cfg.server { + SocketAddr::V4(_) => Domain::IPV4, + SocketAddr::V6(_) => Domain::IPV6, + }; + + let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP)) + .map_err(|err| Error::Socket("failed to create socks5 server socket", err))?; if let Some(dual_stack) = cfg.dual_stack { - if cfg.server.is_ipv4() && dual_stack { - return Err(Error::from(IoError::new( - ErrorKind::Unsupported, - "IPv4 socket cannot be dual stack", - ))); - } - - socket.set_only_v6(!dual_stack)?; + socket.set_only_v6(!dual_stack).map_err(|err| { + Error::Socket("socks5 server dual-stack socket setting error", err) + })?; } - socket.set_reuse_address(true)?; + socket.set_reuse_address(true).map_err(|err| { + Error::Socket("failed to set socks5 server socket to reuse_address", err) + })?; - TcpListener::from_std(StdTcpListener::from(socket))? + socket.set_nonblocking(true).map_err(|err| { + Error::Socket("failed setting socks5 server socket as non-blocking", err) + })?; + + socket + .bind(&SockAddr::from(cfg.server)) + .map_err(|err| Error::Socket("failed to bind socks5 server socket", err))?; + + socket + .listen(i32::MAX) + .map_err(|err| Error::Socket("failed to listen on socks5 server socket", err))?; + + TcpListener::from_std(StdTcpListener::from(socket)) + .map_err(|err| Error::Socket("failed to create socks5 server socket", err))? }; let auth: Arc = match (cfg.username, cfg.password) { @@ -68,7 +83,6 @@ impl Server { let server = Self { inner: Socks5Server::new(socket, auth), - addr: cfg.server, dual_stack: cfg.dual_stack, max_pkt_size: cfg.max_packet_size, next_assoc_id: AtomicU16::new(0), @@ -84,11 +98,10 @@ impl Server { } pub async fn start() { - let server = SERVER.get().unwrap(); - log::warn!("[socks5] server started, listening on {}", server.addr); + log::warn!("[socks5] server started, listening on {}", Self::addr()); loop { - match server.inner.accept().await { + match SERVER.get().unwrap().inner.accept().await { Ok((conn, addr)) => { log::debug!("[socks5] [{addr}] connection established"); tokio::spawn(async move { @@ -118,33 +131,55 @@ impl Server { assoc: Associate, _addr: Address, ) -> Result<(), Error> { - async fn get_assoc_socket() -> Result, IoError> { - let socket = Socket::from( - UdpSocket::bind(SERVER.get().unwrap().addr) - .await? - .into_std()?, - ); + async fn get_assoc_socket() -> Result, Error> { + let domain = match Server::addr() { + SocketAddr::V4(_) => Domain::IPV4, + SocketAddr::V6(_) => Domain::IPV6, + }; - if let Some(dual_stack) = SERVER.get().unwrap().dual_stack { - // We already checked that the server address is IPv6 - socket.set_only_v6(!dual_stack)?; + let socket = Socket::new(domain, Type::DGRAM, Some(Protocol::UDP)).map_err(|err| { + Error::Socket("failed to create socks5 server UDP associate socket", err) + })?; + + if let Some(dual_stack) = Server::dual_stack() { + socket.set_only_v6(!dual_stack).map_err(|err| { + Error::Socket( + "socks5 server UDP associate dual-stack socket setting error", + err, + ) + })?; } - let socket = AssociatedUdpSocket::from(( - UdpSocket::from_std(StdUdpSocket::from(socket))?, - SERVER.get().unwrap().max_pkt_size, - )); + socket.set_nonblocking(true).map_err(|err| { + Error::Socket( + "failed setting socks5 server UDP associate socket as non-blocking", + err, + ) + })?; - Ok(Arc::new(socket)) + socket + .bind(&SockAddr::from(Server::addr())) + .map_err(|err| { + Error::Socket("failed to bind socks5 server UDP associate socket", err) + })?; + + let socket = UdpSocket::from_std(StdUdpSocket::from(socket)).map_err(|err| { + Error::Socket("failed to create socks5 server UDP associate socket", err) + })?; + + Ok(Arc::new(AssociatedUdpSocket::from(( + socket, + Server::max_pkt_size(), + )))) } - match get_assoc_socket() - .await - .and_then(|socket| socket.local_addr().map(|addr| (socket, addr))) - { - Ok((assoc_socket, assoc_addr)) => { + match get_assoc_socket().await { + Ok(assoc_socket) => { let assoc = assoc - .reply(Reply::Succeeded, Address::SocketAddress(assoc_addr)) + .reply( + Reply::Succeeded, + Address::SocketAddress(assoc_socket.local_addr().unwrap()), + ) .await?; Self::send_pkt(assoc, assoc_socket).await } @@ -308,4 +343,16 @@ impl Server { Err(err) => log::error!("[socks5] [send] {err}"), } } + + fn addr() -> SocketAddr { + SERVER.get().unwrap().inner.local_addr().unwrap() + } + + fn dual_stack() -> Option { + SERVER.get().unwrap().dual_stack + } + + fn max_pkt_size() -> usize { + SERVER.get().unwrap().max_pkt_size + } } diff --git a/tuic-server/src/main.rs b/tuic-server/src/main.rs index 3a5e6c0..51bf977 100644 --- a/tuic-server/src/main.rs +++ b/tuic-server/src/main.rs @@ -64,8 +64,8 @@ pub enum Error { AuthFailed(Uuid), #[error("received packet from unexpected source")] UnexpectedPacketSource, - #[error("create UDP session socket failed: {0}")] - CreateUdpSessionSocket(IoError), - #[error("{0} resolved to {1} but IPv6 UDP relay disabled")] + #[error("{0}: {1}")] + Socket(&'static str, IoError), + #[error("{0} resolved to {1} but IPv6 UDP relaying is disabled")] UdpRelayIpv6Disabled(Address, SocketAddr), } diff --git a/tuic-server/src/server.rs b/tuic-server/src/server.rs index 184b254..8eba5f8 100644 --- a/tuic-server/src/server.rs +++ b/tuic-server/src/server.rs @@ -13,7 +13,7 @@ use quinn::{ }; use register_count::{Counter, Register}; use rustls::{version, ServerConfig as RustlsServerConfig}; -use socket2::Socket; +use socket2::{Domain, Protocol, SockAddr, Socket, Type}; use std::{ collections::{hash_map::Entry, HashMap}, future::Future, @@ -95,23 +95,32 @@ impl Server { config.transport_config(Arc::new(tp_cfg)); - let socket = Socket::from(StdUdpSocket::bind(cfg.server)?); + let socket = { + let domain = match cfg.server { + SocketAddr::V4(_) => Domain::IPV4, + SocketAddr::V6(_) => Domain::IPV6, + }; - if let Some(dual_stack) = cfg.dual_stack { - if cfg.server.is_ipv4() && dual_stack { - return Err(Error::from(IoError::new( - ErrorKind::Unsupported, - "IPv4 socket cannot be dual stack", - ))); + let socket = Socket::new(domain, Type::DGRAM, Some(Protocol::UDP)) + .map_err(|err| Error::Socket("failed to create endpoint UDP socket", err))?; + + if let Some(dual_stack) = cfg.dual_stack { + socket.set_only_v6(!dual_stack).map_err(|err| { + Error::Socket("endpoint dual-stack socket setting error", err) + })?; } - socket.set_only_v6(!dual_stack)?; - } + socket + .bind(&SockAddr::from(cfg.server)) + .map_err(|err| Error::Socket("failed to bind endpoint UDP socket", err))?; + + StdUdpSocket::from(socket) + }; let ep = Endpoint::new( EndpointConfig::default(), Some(config), - StdUdpSocket::from(socket), + socket, Arc::new(TokioRuntime), )?; @@ -492,11 +501,8 @@ impl Connection { (session.socket_v4.clone(), session.socket_v6.clone()) } Entry::Vacant(entry) => { - let session = entry.insert( - UdpSession::new(assoc_id, self.clone(), self.udp_relay_ipv6) - .await - .map_err(Error::CreateUdpSessionSocket)?, - ); + let session = entry + .insert(UdpSession::new(assoc_id, self.clone(), self.udp_relay_ipv6).await?); (session.socket_v4.clone(), session.socket_v6.clone()) } @@ -592,17 +598,33 @@ struct UdpSession { } impl UdpSession { - async fn new(assoc_id: u16, conn: Connection, udp_relay_ipv6: bool) -> Result { - let socket_v4 = - Arc::new(UdpSocket::bind(SocketAddr::from((Ipv4Addr::UNSPECIFIED, 0))).await?); + async fn new(assoc_id: u16, conn: Connection, udp_relay_ipv6: bool) -> Result { + let socket_v4 = Arc::new( + UdpSocket::bind(SocketAddr::from((Ipv4Addr::UNSPECIFIED, 0))) + .await + .map_err(|err| Error::Socket("failed to create UDP associate IPv4 socket", err))?, + ); let socket_v6 = if udp_relay_ipv6 { - let socket = Socket::from( - UdpSocket::bind(SocketAddr::from((Ipv6Addr::UNSPECIFIED, 0))) - .await? - .into_std()?, - ); + let socket = Socket::new(Domain::IPV6, Type::DGRAM, Some(Protocol::UDP)) + .map_err(|err| Error::Socket("failed to create UDP associate IPv6 socket", err))?; - socket.set_only_v6(true)?; + socket.set_nonblocking(true).map_err(|err| { + Error::Socket( + "failed setting UDP associate IPv6 socket as non-blocking", + err, + ) + })?; + + socket.set_only_v6(true).map_err(|err| { + Error::Socket("failed setting UDP associate IPv6 socket as IPv6-only", err) + })?; + + socket + .bind(&SockAddr::from(SocketAddr::from(( + Ipv6Addr::UNSPECIFIED, + 0, + )))) + .map_err(|err| Error::Socket("failed to bind UDP associate IPv6 socket", err))?; Some(Arc::new(UdpSocket::from_std(StdUdpSocket::from(socket))?)) } else {