diff --git a/tuic-client/Cargo.toml b/tuic-client/Cargo.toml index 3af8623..1a614f1 100644 --- a/tuic-client/Cargo.toml +++ b/tuic-client/Cargo.toml @@ -12,6 +12,7 @@ quinn = { version = "0.9.3", default-features = false, features = ["futures-io", register-count = { version = "0.1.0", default-features = false, features = ["std"] } serde = { version = "1.0.152", default-features = false, features = ["derive", "std"] } serde_json = { version = "1.0.91", default-features = false, features = ["std"] } +socket2 = { version = "0.4.7", default-features = false } socks5-proto = { version = "0.3.3", default-features = false } socks5-server = { version = "0.8.3", default-features = false } thiserror = { version = "1.0.38", default-features = false } diff --git a/tuic-client/src/error.rs b/tuic-client/src/error.rs index c250262..74fa0c5 100644 --- a/tuic-client/src/error.rs +++ b/tuic-client/src/error.rs @@ -15,4 +15,6 @@ pub enum Error { Model(#[from] ModelError), #[error("timeout")] Timeout, + #[error("invalid authentication")] + InvalidAuth, } diff --git a/tuic-client/src/main.rs b/tuic-client/src/main.rs index e1d83e5..cc06f30 100644 --- a/tuic-client/src/main.rs +++ b/tuic-client/src/main.rs @@ -34,7 +34,7 @@ async fn main() { } } - match Server::set_config(cfg.local).await { + match Server::set_config(cfg.local) { Ok(()) => {} Err(err) => { eprintln!("{err}"); diff --git a/tuic-client/src/socks5.rs b/tuic-client/src/socks5.rs index 00af2b5..bc82251 100644 --- a/tuic-client/src/socks5.rs +++ b/tuic-client/src/socks5.rs @@ -2,16 +2,17 @@ use crate::{config::Local, connection::Connection as TuicConnection, error::Erro use bytes::Bytes; use once_cell::sync::{Lazy, OnceCell}; use parking_lot::Mutex; +use socket2::{Domain, Protocol, SockAddr, Socket, Type}; use socks5_proto::{Address, Reply}; use socks5_server::{ - auth::NoAuth, + auth::{NoAuth, Password}, connection::{associate, bind, connect}, - Associate, AssociatedUdpSocket, Bind, Connect, Connection, Server as Socks5Server, + Associate, AssociatedUdpSocket, Auth, Bind, Connect, Connection, Server as Socks5Server, }; use std::{ collections::HashMap, io::{Error as IoError, ErrorKind}, - net::SocketAddr, + net::{IpAddr, SocketAddr, TcpListener as StdTcpListener, UdpSocket as StdUdpSocket}, sync::{ atomic::{AtomicU16, Ordering}, Arc, @@ -19,7 +20,7 @@ use std::{ }; use tokio::{ io::{self, AsyncWriteExt}, - net::UdpSocket, + net::{TcpListener, UdpSocket}, }; use tokio_util::compat::FuturesAsyncReadCompatExt; use tuic::Address as TuicAddress; @@ -31,16 +32,41 @@ static UDP_SESSIONS: Lazy>>> = pub struct Server { inner: Socks5Server, + addr: SocketAddr, dual_stack: Option, max_packet_size: usize, } impl Server { - pub async fn set_config(cfg: Local) -> Result<(), Error> { - let server = Socks5Server::bind(cfg.server, Arc::new(NoAuth)).await?; + pub fn set_config(cfg: Local) -> Result<(), Error> { + let socket = { + let domain = match cfg.server.ip() { + IpAddr::V4(_) => Domain::IPV4, + IpAddr::V6(_) => Domain::IPV6, + }; + + let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))?; + + if let Some(dual_stack) = cfg.dual_stack { + socket.set_only_v6(!dual_stack)?; + } + + socket.set_reuse_address(true)?; + socket.bind(&SockAddr::from(cfg.server))?; + TcpListener::from_std(StdTcpListener::from(socket))? + }; + + let auth: Arc = match (cfg.username, cfg.password) { + (Some(username), Some(password)) => { + Arc::new(Password::new(username.into_bytes(), password.into_bytes())) + } + (None, None) => Arc::new(NoAuth), + _ => return Err(Error::InvalidAuth), + }; let server = Self { - inner: server, + inner: Socks5Server::new(socket, auth), + addr: cfg.server, dual_stack: cfg.dual_stack, max_packet_size: cfg.max_packet_size, }; @@ -86,15 +112,31 @@ impl Server { assoc: Associate, _addr: Address, ) -> Result<(), Error> { - let assoc_socket = UdpSocket::bind(SocketAddr::from((assoc.local_addr()?.ip(), 0))) - .await - .and_then(|socket| { - socket - .local_addr() - .map(|addr| (Arc::new(AssociatedUdpSocket::from((socket, 1500))), addr)) - }); + async fn get_assoc_socket() -> Result<(Arc, SocketAddr), IoError> { + let domain = match SERVER.get().unwrap().addr.ip() { + IpAddr::V4(_) => Domain::IPV4, + IpAddr::V6(_) => Domain::IPV6, + }; - match assoc_socket { + let socket = Socket::new(domain, Type::DGRAM, Some(Protocol::UDP))?; + + if let Some(dual_stack) = SERVER.get().unwrap().dual_stack { + socket.set_only_v6(!dual_stack)?; + } + + socket.set_reuse_address(true)?; + socket.bind(&SockAddr::from(SERVER.get().unwrap().addr))?; + + let socket = AssociatedUdpSocket::from(( + UdpSocket::from_std(StdUdpSocket::from(socket))?, + SERVER.get().unwrap().max_packet_size, + )); + + let addr = socket.local_addr()?; + Ok((Arc::new(socket), addr)) + } + + match get_assoc_socket().await { Ok((assoc_socket, assoc_addr)) => { let assoc = assoc .reply(Reply::Succeeded, Address::SocketAddress(assoc_addr))