├── .gitignore ├── src ├── util │ ├── mod.rs │ └── stream_util.rs ├── udp │ ├── mod.rs │ ├── udp_server.rs │ └── udp_tunnel.rs ├── tcp │ ├── mod.rs │ ├── tcp_tunnel.rs │ └── tcp_server.rs ├── pem_util.rs ├── tunnel_info_bridge.rs ├── bin │ ├── rstunc.rs │ └── rstund.rs ├── tunnel_message.rs ├── server.rs ├── lib.rs └── client.rs ├── .cargo └── config.toml ├── .vscode └── launch.json ├── gen_cert_and_key.sh ├── localhost.crt.pem ├── Cargo.toml ├── localhost.key.pem ├── README.md └── .github └── workflows └── main.yml /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /src/util/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod stream_util; 2 | -------------------------------------------------------------------------------- /src/udp/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod udp_server; 2 | pub mod udp_tunnel; 3 | 4 | use byte_pool::Block; 5 | use std::net::SocketAddr; 6 | use tokio::sync::mpsc::{Receiver, Sender}; 7 | 8 | pub enum UdpMessage { 9 | Packet(UdpPacket), 10 | Quit, 11 | } 12 | 13 | pub type UdpSender = Sender; 14 | pub type UdpReceiver = Receiver; 15 | 16 | pub struct UdpPacket { 17 | pub payload: Block<'static, Vec>, 18 | pub local_addr: SocketAddr, 19 | pub peer_addr: Option, 20 | } 21 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | # works with NDK 28.0.13004108 2 | [target.aarch64-linux-android] 3 | ar = "/Users/neevek/Library/Android/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android-ar" 4 | linker = "/Users/neevek/Library/Android/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android35-clang" 5 | 6 | [target.armv7-linux-androideabi] 7 | ar = "/Users/neevek/Library/Android/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-ar" 8 | linker = "/Users/neevek/Library/Android/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64/bin/armv7a-linux-androideabi35-clang" 9 | -------------------------------------------------------------------------------- /src/tcp/mod.rs: -------------------------------------------------------------------------------- 1 | use std::net::SocketAddr; 2 | use tokio::io::{AsyncRead, AsyncWrite}; 3 | use tokio::net::TcpStream; 4 | use tokio::sync::mpsc::{Receiver, Sender}; 5 | 6 | pub mod tcp_server; 7 | pub mod tcp_tunnel; 8 | 9 | pub trait AsyncStream: AsyncRead + AsyncWrite + Unpin + Send + 'static { 10 | fn peer_addr(&self) -> std::io::Result; 11 | } 12 | 13 | impl AsyncStream for TcpStream { 14 | fn peer_addr(&self) -> std::io::Result { 15 | TcpStream::peer_addr(self) 16 | } 17 | } 18 | 19 | pub struct StreamRequest { 20 | pub stream: S, 21 | pub dst_addr: Option, 22 | } 23 | 24 | pub enum StreamMessage { 25 | Request(StreamRequest), 26 | Quit, 27 | } 28 | 29 | pub type StreamSender = Sender>; 30 | pub type StreamReceiver = Receiver>; 31 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Debug rstunc", 6 | "type": "codelldb", 7 | "request": "launch", 8 | "program": "${workspaceFolder}/target/debug/rstunc", 9 | "args": [ 10 | "-mOUT", "-a127.0.0.1:9701", "-p1234", "-t0.0.0.0:9500^ANY", "-lD" 11 | ], 12 | "cwd": "${workspaceFolder}", 13 | "sourceLanguages": ["rust"], 14 | "stopOnEntry": false, 15 | "console": "integratedTerminal" 16 | }, 17 | { 18 | "name": "Debug rstund", 19 | "type": "codelldb", 20 | "request": "launch", 21 | "program": "${workspaceFolder}/target/debug/rstund", 22 | "args": [ 23 | "-a127.0.0.1:9701", "-p1234", "-lD" 24 | ], 25 | "cwd": "${workspaceFolder}", 26 | "sourceLanguages": ["rust"], 27 | "stopOnEntry": false, 28 | "console": "integratedTerminal" 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /gen_cert_and_key.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | domain_or_ip=$1 4 | if [[ -z $domain_or_ip ]]; then 5 | echo "usage: $0 " 6 | exit 1 7 | fi 8 | 9 | openssl_cnf= 10 | platform=$(uname 2> /dev/null) 11 | 12 | if [[ "$platform" = "Darwin" ]]; then 13 | openssl_cnf="/System/Library/OpenSSL/openssl.cnf" 14 | elif [[ "$platform" = "Linux" ]]; then 15 | openssl_cnf="/etc/ssl/openssl.cnf" 16 | else 17 | echo "not supported!" 18 | exit 1 19 | fi 20 | 21 | if [[ $domain_or_ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ || $domain_or_ip =~ : ]]; then 22 | san_type="IP" 23 | else 24 | san_type="DNS" 25 | fi 26 | 27 | openssl req \ 28 | -newkey rsa:2048 \ 29 | -x509 \ 30 | -nodes \ 31 | -keyout $domain_or_ip.key.pem \ 32 | -new \ 33 | -out $domain_or_ip.crt.pem \ 34 | -subj /CN=$domain_or_ip \ 35 | -reqexts san \ 36 | -extensions san \ 37 | -config <(cat $openssl_cnf \ 38 | <(printf "[san]\nsubjectAltName=$san_type:$domain_or_ip")) \ 39 | -sha256 \ 40 | -days 3650 41 | -------------------------------------------------------------------------------- /localhost.crt.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC7TCCAdWgAwIBAgIUYPwMIryUb2HDSMECaNPOvjJa4xkwDQYJKoZIhvcNAQEL 3 | BQAwFDESMBAGA1UEAxMJbG9jYWxob3N0MB4XDTIzMTAxMzEzNTg1M1oXDTMzMTAx 4 | MDEzNTg1M1owFDESMBAGA1UEAxMJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF 5 | AAOCAQ8AMIIBCgKCAQEAo62orpJ8jDcl7j3Xa4PbZw0cW9wjEj3w0q2uG+jdUqdl 6 | 2qrAyaHp8hq00N0yDLgiNhcGEv5kw1TQ1H4Z3v9cz4kHMWnc5BFH4dtCkK5TZrWg 7 | k5eHrRkebtFtzTIEle7ObVbnwjrcPm06+Ys7kwoZSJKoAvgmprsJGL0OI7S1sxUw 8 | zMvhDdxfdR4XkTR1pcAeSYzfpkFvXU6tItWnSFuNCKVx1LlMl1BaZ+t8KB2yRSn4 9 | YRfT5sQPhbVyzNEoEgpdQRaE9sAKKb1/+ezXMKT9BrE1uMUIJtbNE/wd41hgFxUi 10 | 97AcXizKTf/Ey84pGae8Juzils0QaZabRkktjZ0QfQIDAQABozcwNTAUBgNVHREE 11 | DTALgglsb2NhbGhvc3QwHQYDVR0OBBYEFFVgOh7RgV4czbC53EgO3Iu1J1H7MA0G 12 | CSqGSIb3DQEBCwUAA4IBAQAmWYntJkwVG4XKjbdMMdCfTjw/7nvvfDrRqpZhZKpk 13 | NXh/UrNVQGgC2zoivfaVSb9cJi+CZ0M9X8WActAXtqo9j0PqX2y6XLIPAbulHWbv 14 | +KDQHuqYnUMz7YouvgCQDqqdVTW9IREdQgtHsFWajPL5JHd8rqS7KPINwRboSxO5 15 | BYTEq17egIigR3Yr+Fr20mrz5/w3JC4WLTZnr5bQ06HaXYqWuSOwGBQvfS5w3Y/t 16 | XlmSqLz9Q8YbO8bbpybErBUd/dv0qo8BXYUhSWZyFzgqqqp0ciIPzPs1y5OCbIz1 17 | yq8KmcY2W6ILipKklCqs2V0esasj9zwVQ2W95oBWv0Y7 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /src/pem_util.rs: -------------------------------------------------------------------------------- 1 | use anyhow::bail; 2 | use anyhow::Context; 3 | use anyhow::Result; 4 | use rustls::pki_types::CertificateDer; 5 | use rustls::pki_types::PrivateKeyDer; 6 | use rustls_pemfile::Item; 7 | use std::fs; 8 | use std::path::PathBuf; 9 | use std::{fs::File, io::BufReader}; 10 | 11 | pub fn load_certificates_from_pem(path: &str) -> Result>> { 12 | let file_path = PathBuf::from(path); 13 | let cert_buf = fs::read(file_path).context("reading cert failed")?; 14 | let cert_buf = &mut cert_buf.as_ref(); 15 | let certs = rustls_pemfile::certs(cert_buf); 16 | Ok(certs.filter_map(Result::ok).collect()) 17 | } 18 | 19 | pub fn load_private_key_from_pem(path: &str) -> Result> { 20 | let file = File::open(path)?; 21 | let mut reader = BufReader::new(&file); 22 | 23 | let key = match rustls_pemfile::read_one(&mut reader).context("failed to read private key")? { 24 | Some(Item::Pkcs1Key(key)) => PrivateKeyDer::Pkcs1(key), 25 | Some(Item::Pkcs8Key(key)) => PrivateKeyDer::Pkcs8(key), 26 | Some(Item::Sec1Key(key)) => PrivateKeyDer::Sec1(key), 27 | _ => bail!("unexpected private key"), 28 | }; 29 | 30 | Ok(key) 31 | } 32 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rstun" 3 | version = "0.7.4" 4 | edition = "2021" 5 | 6 | [lib] 7 | crate-type = ["lib"] 8 | 9 | [dependencies] 10 | rustls = { version = "0.23", default-features = false, features = ["ring"] } 11 | clap = { version = "4.5", features = ["derive"] } 12 | rcgen = "0.14" 13 | tokio = { version = "1.47", features = ["full"] } 14 | pretty_env_logger = "0.5.0" 15 | ring = "0.17" 16 | log = "0.4" 17 | chrono = "0.4" 18 | anyhow = "1.0" 19 | # quinn = "0.11.8" 20 | quinn = { git = "https://github.com/neevek/quinn", branch = "fix-rebind" } # based on 0.11.8 21 | quinn-proto = "0.11.12" 22 | futures-util = "0.3" 23 | bincode = { version = "2", features = ["serde"] } 24 | pin-utils = "0.1.0" 25 | enum-as-inner = "0.6" 26 | num_cpus = "1.17" 27 | rs-utilities = "0.4.4" 28 | # rs-utilities = { path = "../rs-utilities" } 29 | serde = { version = "1.0", features = ["derive"] } 30 | serde_json = "1.0" 31 | rustls-platform-verifier = "0.6.0" 32 | byte-pool = { git = "https://github.com/neevek/byte-pool" } 33 | x509-parser = "0.17" 34 | lazy_static = "1.5" 35 | rustls-pemfile = "2.2" 36 | bytes = "1" 37 | backon = "1.5" 38 | dashmap = "6" 39 | ctrlc = "3.4" 40 | 41 | [dev-dependencies] 42 | jni = "0.21" 43 | android_logger = "0.15" 44 | 45 | [target.aarch64-linux-android.dependencies] 46 | jni = "0.21" 47 | android_logger = "0.15" 48 | 49 | [target.armv7-linux-androideabi.dependencies] 50 | jni = "0.21" 51 | android_logger = "0.15" 52 | 53 | [profile.release] 54 | opt-level = "z" 55 | strip = true 56 | lto = "fat" 57 | panic = "abort" 58 | -------------------------------------------------------------------------------- /src/tunnel_info_bridge.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | use std::sync::{Arc, Mutex}; 3 | 4 | #[derive(Serialize, Default, Clone)] 5 | pub(crate) struct TunnelTraffic { 6 | pub rx_bytes: u64, 7 | pub tx_bytes: u64, 8 | pub tx_dgrams: u64, 9 | pub rx_dgrams: u64, 10 | } 11 | 12 | #[derive(Serialize)] 13 | pub(crate) enum TunnelInfoType { 14 | TunnelState, 15 | TunnelLog, 16 | TunnelTraffic, 17 | } 18 | 19 | #[derive(Serialize)] 20 | pub(crate) struct TunnelInfo 21 | where 22 | T: ?Sized + Serialize, 23 | { 24 | pub info_type: TunnelInfoType, 25 | pub data: Box, 26 | } 27 | 28 | impl TunnelInfo 29 | where 30 | T: ?Sized + Serialize, 31 | { 32 | pub(crate) fn new(info_type: TunnelInfoType, data: Box) -> Self { 33 | Self { info_type, data } 34 | } 35 | } 36 | 37 | #[derive(Clone)] 38 | pub(crate) struct TunnelInfoBridge { 39 | listener: Option>>, 40 | } 41 | 42 | impl TunnelInfoBridge { 43 | pub(crate) fn new() -> Self { 44 | TunnelInfoBridge { listener: None } 45 | } 46 | 47 | pub(crate) fn set_listener(&mut self, listener: impl FnMut(&str) + 'static + Send + Sync) { 48 | self.listener = Some(Arc::new(Mutex::new(listener))); 49 | } 50 | 51 | pub(crate) fn has_listener(&self) -> bool { 52 | self.listener.is_some() 53 | } 54 | 55 | pub(crate) fn post_tunnel_info(&self, data: TunnelInfo) 56 | where 57 | T: ?Sized + Serialize, 58 | { 59 | if let Some(ref listener) = self.listener { 60 | if let Ok(json) = serde_json::to_string(&data) { 61 | listener.lock().unwrap()(json.as_str()); 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /localhost.key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCjraiuknyMNyXu 3 | Pddrg9tnDRxb3CMSPfDSra4b6N1Sp2XaqsDJoenyGrTQ3TIMuCI2FwYS/mTDVNDU 4 | fhne/1zPiQcxadzkEUfh20KQrlNmtaCTl4etGR5u0W3NMgSV7s5tVufCOtw+bTr5 5 | izuTChlIkqgC+CamuwkYvQ4jtLWzFTDMy+EN3F91HheRNHWlwB5JjN+mQW9dTq0i 6 | 1adIW40IpXHUuUyXUFpn63woHbJFKfhhF9PmxA+FtXLM0SgSCl1BFoT2wAopvX/5 7 | 7NcwpP0GsTW4xQgm1s0T/B3jWGAXFSL3sBxeLMpN/8TLzikZp7wm7OKWzRBplptG 8 | SS2NnRB9AgMBAAECggEAA2AX6+2yatPSUOzujdWMxz3CeXR7NquXVUjmn8W4FrXE 9 | CHPgAOh5YhhB5VLCbve3IOVtpyOe4VZU7iThlLMwb0k0oES+HOfsUxCJ+WDW8HXL 10 | Z2/HCP9NHNztxj8DUDI6CJAzvFIpa5ImFrJT2q7pIZGArHsAlQyjXeK8MWlPG+/J 11 | +u13xNDs0pnnXa3ks6l5Wv107z34b7aa6TLNz0BsfXnMvxAkgrgAfWm4kgJCfgF/ 12 | P8xgrE9CZg/QyMTGFnyVw38ofYgAdMCpQNyz2GeF+jm3HEx15KCfsNogVKXNjXTR 13 | PkWIi8CUkRldjpztMCZuxFgBYQ4uqEDte6LNGOa9MQKBgQDCDwPw9JZsVeJjmc2I 14 | wndNFsbRzHGDtlwJ2I01ofQi1RQshG2OwygLb2b1iJmaJgfL7OrfSh8oM4j4cFGW 15 | /pWfrkwOfgK8GoguydOCBAC+xSvTyM/4OpdTJnTxx4KYxK/JYAzJfqqHTysTXD0z 16 | Scnuqg/O+k4c6yKGzprOh/YgyQKBgQDX7DLT6otws7HoIrrcTZwohwGLxuNlmnAz 17 | AHapI6kY9arM3nnZ45e9/IzdDKdTdEeJHlg+Qq7PA7awPDD8JKkRR/wwEQiyNsZQ 18 | /nq/5E7upxuffvSNZXkYGPCMqR3QjeXtgwiaUyAVBv0awcb7euOYfiNPE9F/ZGRo 19 | dn2lcplgFQKBgB4ConJl8MlKMtuCUoW+xSJXzmFtg3SCBBPFuHi91fp5B0inJiY4 20 | yf2SudJo0JBFJ1mDBwOG+/CEn78D56o5LrxmAP8Qv62FUOAjeCEYK2FVLqTu3jKe 21 | JP7H6LfnSawEZsb2oqOaghawyJGS5ygCVkchZ5ZzoRbZyhoc41XjMs35AoGBAJ89 22 | vjqVtqK2k9Vcj6zbu/gToStB0gDjxi2HAPw6pYIX5BBVX043UHi4IfcAVwLwNbXF 23 | YFUCfsODvJ76tTGvo9Rv32hfl6c/SEEBfOu6aBAPxAp76cXB+W2xLu695pQholnp 24 | ElYmSfnX/qBWGvbqqaGUHVw7hHzMQFTeVknHW6AFAoGALqfMktqZCIg51s+ZUSA3 25 | lJ2b9h5x/Xf2KztaGZxAa99uRZhVbK7f9yGVgAFABFXLtN71UdfGy0vf3nIZY+s4 26 | 7Rkutd1YJA0y94D4Yi7i44duAOuPdlQJur9pX80VPJZOeDKYVqkuceoGDYk5RS7M 27 | W0BfQBQKqgmZdy5vuVYw97U= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /src/bin/rstunc.rs: -------------------------------------------------------------------------------- 1 | use clap::builder::PossibleValuesParser; 2 | use clap::builder::TypedValueParser as _; 3 | use clap::Parser; 4 | use log::error; 5 | use rstun::*; 6 | 7 | fn main() { 8 | let args = RstuncArgs::parse(); 9 | let log_filter = format!("rstun={},rs_utilities={}", args.loglevel, args.loglevel); 10 | rs_utilities::LogHelper::init_logger("rstunc", log_filter.as_str()); 11 | 12 | let config = ClientConfig::create( 13 | &args.server_addr, 14 | &args.password, 15 | &args.cert, 16 | &args.cipher, 17 | &args.tcp_mappings, 18 | &args.udp_mappings, 19 | &args.dot, 20 | &args.dns, 21 | args.workers, 22 | args.wait_before_retry_ms, 23 | args.quic_timeout_ms, 24 | args.tcp_timeout_ms, 25 | args.udp_timeout_ms, 26 | args.hop_interval_ms, 27 | ) 28 | .map_err(|e| { 29 | error!("{e}"); 30 | }); 31 | 32 | if let Ok(config) = config { 33 | let mut client = Client::new(config); 34 | 35 | #[cfg(target_os = "android")] 36 | { 37 | use log::info; 38 | client.set_enable_on_info_report(true); 39 | client.set_on_info_listener(|s| { 40 | info!("{}", s); 41 | }); 42 | } 43 | 44 | client.start_tunneling(); 45 | } 46 | } 47 | 48 | #[derive(Parser, Debug)] 49 | #[command(author, version, about, long_about = None)] 50 | struct RstuncArgs { 51 | /// Server address ([:port]) of rstund. Default port is 3515. 52 | #[arg(short = 'a', long)] 53 | server_addr: String, 54 | 55 | /// Password for server authentication (must match server's --password) 56 | #[arg(short = 'p', long, required = true)] 57 | password: String, 58 | 59 | /// Comma-separated list of TCP tunnel mappings. Each mapping is in the form MODE^[ip:]port^[ip:]port, e.g. OUT^8080^0.0.0.0:9090 60 | /// MODE is either OUT or IN. Use OUT^8000^ANY to use the server's default upstream for OUT mode. 61 | #[arg(short = 't', long, verbatim_doc_comment, default_value = "")] 62 | tcp_mappings: String, 63 | 64 | /// Comma-separated list of UDP tunnel mappings. Each mapping is in the form MODE^[ip:]port^[ip:]port, e.g. OUT^8080^0.0.0.0:9090 65 | /// MODE is either OUT or IN. Use OUT^8000^ANY to use the server's default upstream for OUT mode. 66 | #[arg(short = 'u', long, verbatim_doc_comment, default_value = "")] 67 | udp_mappings: String, 68 | 69 | /// Path to the certificate file (only needed for self-signed certificates) 70 | #[arg(short = 'c', long, default_value = "")] 71 | cert: String, 72 | 73 | /// Preferred cipher suite 74 | #[arg(short = 'e', long, default_value_t = String::from(SUPPORTED_CIPHER_SUITE_STRS[0]), 75 | value_parser = PossibleValuesParser::new(SUPPORTED_CIPHER_SUITE_STRS).map(|v| v.to_string()))] 76 | cipher: String, 77 | 78 | /// Number of async worker threads [uses all logical CPUs if 0] 79 | #[arg(short = 'w', long, default_value_t = 0)] 80 | workers: usize, 81 | 82 | /// Wait time in milliseconds before retrying connection 83 | #[arg(short = 'r', long, default_value_t = 5000)] 84 | wait_before_retry_ms: u64, 85 | 86 | /// QUIC idle timeout in milliseconds 87 | #[arg(long, default_value_t = 30000)] 88 | quic_timeout_ms: u64, 89 | 90 | /// TCP idle timeout in milliseconds 91 | #[arg(long, default_value_t = 30000)] 92 | tcp_timeout_ms: u64, 93 | 94 | /// UDP idle timeout in milliseconds 95 | #[arg(long, default_value_t = 5000)] 96 | udp_timeout_ms: u64, 97 | 98 | #[arg(long, default_value_t = 0)] 99 | hop_interval_ms: u64, 100 | 101 | /// Comma-separated DoT servers (domains) for DNS resolution, e.g. "dns.google,one.one.one.one". Takes precedence over --dns if set. 102 | #[arg(long, verbatim_doc_comment, default_value = "")] 103 | dot: String, 104 | 105 | /// Comma-separated DNS servers (IPs) for DNS resolution, e.g. "1.1.1.1,8.8.8.8" 106 | #[arg(long, verbatim_doc_comment, default_value = "")] 107 | dns: String, 108 | 109 | /// Log level 110 | #[arg(short = 'l', long, default_value_t = String::from("I"), 111 | value_parser = PossibleValuesParser::new(["T", "D", "I", "W", "E"]).map(|v| match v.as_str() { 112 | "T" => "trace", 113 | "D" => "debug", 114 | "I" => "info", 115 | "W" => "warn", 116 | "E" => "error", 117 | _ => "info", 118 | }.to_string()))] 119 | loglevel: String, 120 | } 121 | -------------------------------------------------------------------------------- /src/tcp/tcp_tunnel.rs: -------------------------------------------------------------------------------- 1 | use crate::tcp::StreamMessage; 2 | use crate::tcp::{AsyncStream, StreamReceiver, StreamRequest}; 3 | use crate::util::stream_util::StreamUtil; 4 | use log::{debug, error, info}; 5 | use std::borrow::BorrowMut; 6 | use std::net::SocketAddr; 7 | use std::time::Duration; 8 | use tokio::net::TcpStream; 9 | 10 | pub struct TcpTunnel; 11 | 12 | impl TcpTunnel { 13 | pub async fn start_serving( 14 | tunnel_out: bool, 15 | conn: &quinn::Connection, 16 | stream_receiver: &mut StreamReceiver, 17 | pending_request: &mut Option>, 18 | stream_timeout_ms: u64, 19 | ) { 20 | loop { 21 | let request = match pending_request.take() { 22 | Some(request) => request, 23 | None => match stream_receiver.borrow_mut().recv().await { 24 | Some(StreamMessage::Request(request)) => request, 25 | _ => break, 26 | }, 27 | }; 28 | 29 | match conn.open_bi().await { 30 | Ok((mut quic_send, quic_recv)) => { 31 | if let Err(e) = 32 | StreamUtil::write_socket_addr(&mut quic_send, &request.dst_addr, false) 33 | .await 34 | { 35 | error!("failed to send dst addr: {e}"); 36 | *pending_request = Some(request); 37 | continue; 38 | } 39 | StreamUtil::start_flowing( 40 | if tunnel_out { "OUT" } else { "IN" }, 41 | request.stream, 42 | (quic_send, quic_recv), 43 | stream_timeout_ms, 44 | ) 45 | } 46 | Err(e) => { 47 | error!("failed to open_bi, will retry: {e}"); 48 | *pending_request = Some(request); 49 | break; 50 | } 51 | } 52 | } 53 | // the tcp server will be reused when tunnel reconnects 54 | } 55 | 56 | pub async fn start_accepting( 57 | conn: &quinn::Connection, 58 | upstream_addr: Option, 59 | stream_timeout_ms: u64, 60 | ) { 61 | let remote_addr = &conn.remote_address(); 62 | info!("start tcp streaming, {remote_addr} ↔ {upstream_addr:?}"); 63 | 64 | loop { 65 | match conn.accept_bi().await { 66 | Err(quinn::ConnectionError::TimedOut) => { 67 | info!("connection timeout: {remote_addr}"); 68 | break; 69 | } 70 | Err(quinn::ConnectionError::ApplicationClosed { .. }) => { 71 | debug!("connection closed: {remote_addr}"); 72 | break; 73 | } 74 | Err(e) => { 75 | error!("failed to open accept_bi: {remote_addr}, err: {e}"); 76 | break; 77 | } 78 | Ok((quic_send, mut quic_recv)) => tokio::spawn(async move { 79 | let dst_addr = match upstream_addr { 80 | Some(dst_addr) => dst_addr, 81 | None => { 82 | match StreamUtil::read_socket_addr(&mut quic_recv, stream_timeout_ms) 83 | .await 84 | { 85 | Ok(dst_addr) => dst_addr, 86 | Err(e) => { 87 | log::error!("failed to read dst address: {e}"); 88 | return; 89 | } 90 | } 91 | } 92 | }; 93 | 94 | match tokio::time::timeout( 95 | Duration::from_secs(5), 96 | TcpStream::connect(&dst_addr), 97 | ) 98 | .await 99 | { 100 | Ok(Ok(request)) => StreamUtil::start_flowing( 101 | "OUT", 102 | request, 103 | (quic_send, quic_recv), 104 | stream_timeout_ms, 105 | ), 106 | Ok(Err(e)) => error!("failed to connect to {dst_addr}, err: {e}"), 107 | Err(_) => error!("timeout connecting to {dst_addr}"), 108 | } 109 | }), 110 | }; 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/tcp/tcp_server.rs: -------------------------------------------------------------------------------- 1 | use crate::tcp::{StreamMessage, StreamReceiver, StreamRequest, StreamSender}; 2 | use anyhow::Result; 3 | use log::{debug, error, info}; 4 | use std::net::SocketAddr; 5 | use std::sync::{Arc, Mutex}; 6 | use std::time::Duration; 7 | use tokio::net::{TcpListener, TcpStream}; 8 | use tokio::sync::mpsc::channel; 9 | use tokio::sync::mpsc::error::SendTimeoutError; 10 | 11 | #[derive(Debug, Clone)] 12 | pub struct TcpServer { 13 | state: Arc>, 14 | } 15 | 16 | #[derive(Debug)] 17 | struct State { 18 | addr: SocketAddr, 19 | tcp_sender: StreamSender, 20 | tcp_receiver: Option>, 21 | active: bool, 22 | terminated: bool, 23 | } 24 | 25 | impl TcpServer { 26 | pub async fn bind_and_start(addr: SocketAddr) -> Result { 27 | let tcp_listener = TcpListener::bind(addr).await?; 28 | let addr = tcp_listener.local_addr().unwrap(); 29 | 30 | let (tcp_sender, tcp_receiver) = channel(4); 31 | let state = Arc::new(Mutex::new(State { 32 | addr, 33 | tcp_sender: tcp_sender.clone(), 34 | tcp_receiver: Some(tcp_receiver), 35 | active: false, 36 | terminated: false, 37 | })); 38 | let state_clone = state.clone(); 39 | 40 | tokio::spawn(async move { 41 | loop { 42 | match tcp_listener.accept().await { 43 | Ok((stream, addr)) => { 44 | { 45 | let (terminated, active) = { 46 | let state = state.lock().unwrap(); 47 | (state.terminated, state.active) 48 | }; 49 | 50 | if terminated { 51 | tcp_sender.send(StreamMessage::Quit).await.ok(); 52 | break; 53 | } 54 | 55 | if !active { 56 | // unless being explicitly requested, always drop the connections because we are not 57 | // sure whether the receiver is ready to aceept connections 58 | debug!("drop connection: {addr}"); 59 | continue; 60 | } 61 | } 62 | 63 | match tcp_sender 64 | .send_timeout( 65 | StreamMessage::Request(StreamRequest { 66 | stream, 67 | dst_addr: None, 68 | }), 69 | Duration::from_millis(3000), 70 | ) 71 | .await 72 | { 73 | Ok(_) => { 74 | // succeeded 75 | } 76 | Err(SendTimeoutError::Timeout(_)) => { 77 | debug!("timedout sending the request, drop the stream"); 78 | } 79 | Err(e) => { 80 | info!("channel is closed, will quit tcp server, err: {e}"); 81 | break; 82 | } 83 | } 84 | } 85 | 86 | Err(e) => { 87 | error!("tcp server failed, err: {e}"); 88 | } 89 | } 90 | } 91 | info!("tcp server quit: {addr}"); 92 | }); 93 | 94 | Ok(Self { state: state_clone }) 95 | } 96 | 97 | pub async fn shutdown(&mut self) -> Result<()> { 98 | let addr = { 99 | let mut state = self.state.lock().unwrap(); 100 | state.terminated = true; 101 | state.addr 102 | }; 103 | // initiate a new connection to wake up the accept() loop 104 | TcpStream::connect(addr).await?; 105 | Ok(()) 106 | } 107 | 108 | pub fn addr(&self) -> SocketAddr { 109 | self.state.lock().unwrap().addr 110 | } 111 | 112 | pub fn take_receiver(&mut self) -> StreamReceiver { 113 | let mut state = self.state.lock().unwrap(); 114 | state.active = true; 115 | state.tcp_receiver.take().unwrap() 116 | } 117 | 118 | pub fn put_receiver(&mut self, tcp_receiver: StreamReceiver) { 119 | let mut state = self.state.lock().unwrap(); 120 | state.active = false; 121 | state.tcp_receiver = Some(tcp_receiver); 122 | } 123 | 124 | pub fn clone_sender(&self) -> StreamSender { 125 | self.state.lock().unwrap().tcp_sender.clone() 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/bin/rstund.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use clap::builder::PossibleValuesParser; 3 | use clap::builder::TypedValueParser as _; 4 | use clap::Parser; 5 | use log::error; 6 | use log::info; 7 | use rs_utilities::log_and_bail; 8 | use rstun::*; 9 | use std::net::SocketAddr; 10 | 11 | fn main() { 12 | let args = RstundArgs::parse(); 13 | let log_filter = format!("rstun={},rs_utilities={}", args.loglevel, args.loglevel); 14 | rs_utilities::LogHelper::init_logger("rstund", log_filter.as_str()); 15 | 16 | let workers = if args.workers > 0 { 17 | args.workers 18 | } else { 19 | num_cpus::get() 20 | }; 21 | 22 | info!("will use {} workers", workers); 23 | 24 | tokio::runtime::Builder::new_multi_thread() 25 | .enable_all() 26 | .worker_threads(workers) 27 | .build() 28 | .unwrap() 29 | .block_on(async { 30 | run(args) 31 | .await 32 | .map_err(|e| { 33 | error!("{e}"); 34 | }) 35 | .ok(); 36 | }) 37 | } 38 | 39 | async fn run(mut args: RstundArgs) -> Result<()> { 40 | if args.addr.is_empty() { 41 | args.addr = "0.0.0.0:0".to_string(); 42 | } 43 | 44 | if !args.addr.contains(':') { 45 | args.addr = format!("127.0.0.1:{}", args.addr); 46 | } 47 | 48 | let config = ServerConfig { 49 | addr: args.addr, 50 | password: args.password, 51 | cert_path: args.cert, 52 | key_path: args.key, 53 | default_tcp_upstream: parse_upstreams("tcp", &args.tcp_upstream)?, 54 | default_udp_upstream: parse_upstreams("udp", &args.udp_upstream)?, 55 | quic_timeout_ms: args.quic_timeout_ms, 56 | tcp_timeout_ms: args.tcp_timeout_ms, 57 | udp_timeout_ms: args.udp_timeout_ms, 58 | dashboard_server: "".to_string(), 59 | dashboard_server_credential: "".to_string(), 60 | }; 61 | 62 | let mut server = Server::new(config); 63 | server.bind()?; 64 | server.serve().await?; 65 | Ok(()) 66 | } 67 | 68 | fn parse_upstreams(upstream_type: &str, upstreams_str: &str) -> Result> { 69 | if upstreams_str.is_empty() { 70 | return Ok(None); 71 | } 72 | 73 | let mut upstream = upstreams_str.to_string(); 74 | if upstream.starts_with("0.0.0.0:") { 75 | upstream = upstream.replace("0.0.0.0:", "127.0.0.1:"); 76 | } 77 | 78 | if !upstream.contains(':') { 79 | upstream = format!("127.0.0.1:{upstreams_str}"); 80 | } 81 | 82 | if let Ok(addr) = upstream.parse() { 83 | Ok(Some(addr)) 84 | } else { 85 | log_and_bail!("invalid {upstream_type} upstream address: {upstreams_str}"); 86 | } 87 | } 88 | 89 | #[derive(Parser, Debug)] 90 | #[command(author, version, about, long_about = None)] 91 | struct RstundArgs { 92 | /// Address ([ip:]port) to listen on. If only a port is given, binds to 127.0.0.1:PORT. 93 | #[arg( 94 | short = 'a', 95 | long, 96 | required = true, 97 | default_value = "", 98 | verbatim_doc_comment 99 | )] 100 | addr: String, 101 | 102 | /// Default TCP upstream for OUT tunnels ([ip:]port). Used if client does not specify an upstream. 103 | #[arg( 104 | short = 't', 105 | long, 106 | required = false, 107 | default_value = "", 108 | verbatim_doc_comment 109 | )] 110 | tcp_upstream: String, 111 | 112 | /// Default UDP upstream for OUT tunnels ([ip:]port). Used if client does not specify an upstream. 113 | #[arg( 114 | short = 'u', 115 | long, 116 | required = false, 117 | default_value = "", 118 | verbatim_doc_comment 119 | )] 120 | udp_upstream: String, 121 | 122 | /// Server password (required, must match client --password) 123 | #[arg(short = 'p', long, required = true)] 124 | password: String, 125 | 126 | /// Path to certificate file (optional). If empty, a self-signed certificate for "localhost" is generated (testing only). 127 | #[arg(short = 'c', long, default_value = "", verbatim_doc_comment)] 128 | cert: String, 129 | 130 | /// Path to key file (optional, only needed if --cert is set) 131 | #[arg(short = 'k', long, default_value = "")] 132 | key: String, 133 | 134 | /// Number of async worker threads [uses all logical CPUs if 0] 135 | #[arg(short = 'w', long, default_value_t = 0)] 136 | workers: usize, 137 | 138 | /// QUIC idle timeout in milliseconds 139 | #[arg(long, default_value_t = 40000)] 140 | quic_timeout_ms: u64, 141 | 142 | /// TCP idle timeout in milliseconds 143 | #[arg(long, default_value_t = 30000)] 144 | tcp_timeout_ms: u64, 145 | 146 | /// UDP idle timeout in milliseconds 147 | #[arg(long, default_value_t = 5000)] 148 | udp_timeout_ms: u64, 149 | 150 | /// Log level 151 | #[arg(short = 'l', long, default_value_t = String::from("I"), 152 | value_parser = PossibleValuesParser::new(["T", "D", "I", "W", "E"]).map(|v| match v.as_str() { 153 | "T" => "trace", 154 | "D" => "debug", 155 | "I" => "info", 156 | "W" => "warn", 157 | "E" => "error", 158 | _ => "info", 159 | }.to_string()))] 160 | loglevel: String, 161 | } 162 | -------------------------------------------------------------------------------- /src/udp/udp_server.rs: -------------------------------------------------------------------------------- 1 | use crate::BUFFER_POOL; 2 | use crate::UDP_PACKET_SIZE; 3 | use anyhow::Result; 4 | use log::debug; 5 | use log::error; 6 | use log::info; 7 | use std::net::SocketAddr; 8 | use std::sync::Arc; 9 | use std::sync::Mutex; 10 | use std::time::Duration; 11 | use tokio::net::UdpSocket; 12 | use tokio::sync::mpsc::channel; 13 | 14 | pub use crate::udp::{UdpMessage, UdpPacket, UdpReceiver, UdpSender}; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct UdpServer(Arc>); 18 | 19 | #[derive(Debug)] 20 | struct State { 21 | addr: SocketAddr, 22 | active: bool, 23 | in_udp_sender: UdpSender, 24 | udp_receiver: Option, 25 | } 26 | 27 | impl UdpServer { 28 | pub async fn bind_and_start(addr: SocketAddr) -> Result { 29 | let udp_socket = UdpSocket::bind(addr).await?; 30 | let addr = udp_socket.local_addr().unwrap(); 31 | 32 | let (in_udp_sender, mut in_udp_receiver) = channel::(4); 33 | let (out_udp_sender, out_udp_receiver) = channel::(4); 34 | 35 | let state = Arc::new(Mutex::new(State { 36 | addr, 37 | active: false, 38 | in_udp_sender, 39 | udp_receiver: Some(out_udp_receiver), 40 | })); 41 | let state_clone = state.clone(); 42 | 43 | tokio::spawn(async move { 44 | loop { 45 | let mut payload = BUFFER_POOL.alloc_and_fill(UDP_PACKET_SIZE); 46 | tokio::select! { 47 | result = udp_socket.recv_from(&mut payload) => { 48 | match result { 49 | Ok((size, local_addr)) => { 50 | let active = { 51 | state.clone().lock().unwrap().active 52 | }; 53 | if !active { 54 | debug!("drop the packet ({size}) from addr: {local_addr}"); 55 | continue; 56 | } 57 | 58 | unsafe { payload.set_len(size); } 59 | let msg = UdpMessage::Packet(UdpPacket{payload, local_addr, peer_addr: None}); 60 | match tokio::time::timeout( 61 | Duration::from_millis(300), 62 | out_udp_sender.send(msg)).await { 63 | Ok(Ok(_)) => { 64 | // succeeded 65 | } 66 | Err(_) => { 67 | // timeout 68 | } 69 | Ok(Err(e)) => { 70 | error!("receiving end of the channel is closed, will quit. err: {e}"); 71 | break; 72 | } 73 | } 74 | } 75 | Err(e) => { 76 | error!("failed to read from local udp socket, err: {e}"); 77 | } 78 | } 79 | } 80 | 81 | result = in_udp_receiver.recv() => { 82 | match result { 83 | Some(UdpMessage::Packet(p)) => { 84 | match udp_socket.send_to(&p.payload, p.local_addr).await { 85 | Ok(_) => { 86 | // succeeded 87 | } 88 | Err(e) => { 89 | error!("failed to send packet to local, err: {e}"); 90 | } 91 | } 92 | } 93 | Some(UdpMessage::Quit) => { 94 | info!("udp server is requested to quit"); 95 | break; 96 | } 97 | None => { 98 | // all senders quit 99 | info!("udp server quit"); 100 | break; 101 | } 102 | } 103 | } 104 | } 105 | } 106 | 107 | info!("udp server quit: {addr}"); 108 | }); 109 | 110 | Ok(Self(state_clone)) 111 | } 112 | 113 | pub fn addr(&self) -> SocketAddr { 114 | self.0.lock().unwrap().addr 115 | } 116 | 117 | pub async fn shutdown(&mut self) -> Result<()> { 118 | let udp_sender = self.0.lock().unwrap().in_udp_sender.clone(); 119 | udp_sender.send(UdpMessage::Quit).await?; 120 | Ok(()) 121 | } 122 | 123 | pub fn set_active(&mut self, active: bool) { 124 | self.0.lock().unwrap().active = active 125 | } 126 | 127 | pub fn take_receiver(&mut self) -> UdpReceiver { 128 | let mut state = self.0.lock().unwrap(); 129 | state.active = true; 130 | state.udp_receiver.take().unwrap() 131 | } 132 | 133 | pub fn put_receiver(&mut self, udp_receiver: UdpReceiver) { 134 | let mut state = self.0.lock().unwrap(); 135 | state.active = false; 136 | state.udp_receiver = Some(udp_receiver); 137 | } 138 | 139 | pub fn clone_sender(&self) -> UdpSender { 140 | self.0.lock().unwrap().in_udp_sender.clone() 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/tunnel_message.rs: -------------------------------------------------------------------------------- 1 | use crate::{Tunnel, TunnelMode}; 2 | use anyhow::Result; 3 | use anyhow::{bail, Context}; 4 | use bincode::config::{self, Configuration}; 5 | use enum_as_inner::EnumAsInner; 6 | use quinn::{RecvStream, SendStream}; 7 | use serde::{Deserialize, Serialize}; 8 | use std::fmt::Display; 9 | use std::net::SocketAddr; 10 | use std::time::Duration; 11 | use tokio::io::{AsyncReadExt, AsyncWriteExt}; 12 | 13 | #[derive(EnumAsInner, Serialize, Deserialize, Debug, Clone)] 14 | pub enum TunnelMessage { 15 | ReqLogin(LoginInfo), 16 | ReqUdpStart(UdpPeerAddr), 17 | RespFailure(String), 18 | RespSuccess, 19 | } 20 | 21 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 22 | pub(crate) struct LoginInfo { 23 | pub password: String, 24 | pub tunnel: Tunnel, 25 | } 26 | 27 | impl LoginInfo { 28 | pub fn format_with_remote_addr(&self, remote_addr: &SocketAddr) -> String { 29 | match &self.tunnel { 30 | Tunnel::ChannelBased(upstream_type) => { 31 | format!("{upstream_type}_ChannelBased → {remote_addr}") 32 | } 33 | Tunnel::NetworkBased(cfg) => { 34 | let upstream = &cfg.upstream; 35 | let upstream_str = if let Some(upstream) = upstream.upstream_addr { 36 | if upstream.ip().is_loopback() { 37 | format!("{}:{}", remote_addr.ip(), upstream.port()) 38 | } else { 39 | format!("{upstream}") 40 | } 41 | } else { 42 | String::from("PeerDefault") 43 | }; 44 | 45 | match cfg.mode { 46 | TunnelMode::Out => { 47 | format!( 48 | "{}_OUT → {} → {remote_addr} → {upstream_str}", 49 | upstream.upstream_type, 50 | cfg.local_server_addr.unwrap() 51 | ) 52 | } 53 | TunnelMode::In => { 54 | format!( 55 | "{}_IN ← {} ← {remote_addr} ← {upstream_str}", 56 | upstream.upstream_type, 57 | cfg.local_server_addr.unwrap() 58 | ) 59 | } 60 | } 61 | } 62 | } 63 | } 64 | } 65 | 66 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 67 | pub struct UdpPeerAddr(pub Option); 68 | 69 | impl Display for LoginInfo { 70 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 71 | match &self.tunnel { 72 | Tunnel::ChannelBased(upstream_type) => { 73 | f.write_str(format!("{upstream_type}_ChannelBased").as_str()) 74 | } 75 | Tunnel::NetworkBased(cfg) => { 76 | f.write_str(format!("{}_{}", cfg.upstream.upstream_type, cfg.mode).as_str()) 77 | } 78 | } 79 | } 80 | } 81 | 82 | impl Display for TunnelMessage { 83 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 84 | match self { 85 | Self::ReqLogin(login_info) => f.write_str(login_info.to_string().as_str()), 86 | Self::ReqUdpStart(udp_peer_addr) => { 87 | f.write_str(format!("udp_start:{udp_peer_addr:?}").as_str()) 88 | } 89 | Self::RespFailure(msg) => f.write_str(format!("fail:{msg}").as_str()), 90 | Self::RespSuccess => f.write_str("succeeded"), 91 | } 92 | } 93 | } 94 | 95 | impl TunnelMessage { 96 | pub async fn recv(quic_recv: &mut RecvStream) -> Result { 97 | let msg_len = quic_recv.read_u32().await? as usize; 98 | let mut msg = vec![0; msg_len]; 99 | quic_recv 100 | .read_exact(&mut msg) 101 | .await 102 | .context("read message failed")?; 103 | 104 | let tun_msg = bincode::serde::decode_from_slice::( 105 | &msg, 106 | config::standard(), 107 | ) 108 | .context("deserialize message failed")?; 109 | Ok(tun_msg.0) 110 | } 111 | 112 | pub async fn send(quic_send: &mut SendStream, msg: &TunnelMessage) -> Result<()> { 113 | let msg = bincode::serde::encode_to_vec(msg, config::standard()) 114 | .context("serialize message failed")?; 115 | quic_send.write_u32(msg.len() as u32).await?; 116 | quic_send.write_all(&msg).await?; 117 | Ok(()) 118 | } 119 | 120 | pub async fn send_failure(quic_send: &mut SendStream, msg: String) -> Result<()> { 121 | let msg = TunnelMessage::RespFailure(msg); 122 | Self::send(quic_send, &msg).await?; 123 | quic_send.flush().await?; 124 | tokio::time::sleep(Duration::from_millis(200)).await; 125 | Ok(()) 126 | } 127 | 128 | pub async fn recv_raw(quic_recv: &mut RecvStream, data: &mut [u8]) -> Result { 129 | let msg_len = quic_recv.read_u16().await? as usize; 130 | if msg_len > data.len() { 131 | bail!("message too large: {msg_len}"); 132 | } 133 | quic_recv 134 | .read_exact(&mut data[..msg_len]) 135 | .await 136 | .context("read message failed")?; 137 | Ok(msg_len as u16) 138 | } 139 | 140 | pub async fn send_raw(quic_send: &mut SendStream, data: &[u8]) -> Result<()> { 141 | quic_send.write_u16(data.len() as u16).await?; 142 | quic_send.write_all(data).await?; 143 | Ok(()) 144 | } 145 | 146 | pub fn handle_message(msg: &TunnelMessage) -> Result<()> { 147 | match msg { 148 | TunnelMessage::RespSuccess => Ok(()), 149 | TunnelMessage::RespFailure(msg) => bail!(format!("received failure, err: {msg}")), 150 | _ => bail!("unexpected message type"), 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rstun 2 | 3 | A high-performance TCP/UDP tunnel over QUIC, written in Rust. 4 | 5 | rstun leverages the [Quinn](https://github.com/quinn-rs/quinn) library for [QUIC](https://quicwg.org/) transport, providing efficient, low-latency, and secure bidirectional communication. All traffic is protected by QUIC's integrated TLS layer. 6 | 7 | --- 8 | 9 | ## Features 10 | 11 | - **Multiple TCP and UDP tunnels**: Support for running multiple tunnels (TCP and/or UDP) simultaneously in a single client or server instance. 12 | - **Bidirectional tunneling**: Both inbound (IN) and outbound (OUT) modes for flexible deployment. 13 | - **Modern encryption**: Security via QUIC's TLS 1.3 layer, with configurable cipher suites. 14 | - **Automatic or custom certificates**: Use your own certificate/key or let rstun generate a self-signed certificate for testing. 15 | - **Connection migration**: Optional periodic migration of QUIC connection to new random local UDP ports to avoid throttling during long data transfers. 16 | - **Traffic statistics**: Real-time tunnel traffic reporting. 17 | 18 | --- 19 | 20 | ## Operating Modes 21 | 22 | ### Inbound Tunneling (IN Mode) 23 | Expose a local service (e.g., web server) to the public internet securely through the QUIC tunnel. Useful for making services behind NAT/firewall accessible externally. 24 | 25 | ### Outbound Tunneling (OUT Mode) 26 | Tunnel local outbound traffic through the server, which then forwards it to the specified destination. Commonly used to encrypt and route traffic from a local network to external servers. 27 | 28 | --- 29 | 30 | ## Components 31 | 32 | - **rstunc** (client): Establishes and manages tunnels to the server. 33 | - **rstund** (server): Accepts incoming connections and forwards TCP/UDP traffic according to configuration. 34 | 35 | --- 36 | 37 | ## Example Usage 38 | 39 | ### Start the server 40 | 41 | ```sh 42 | rstund \ 43 | --addr 0.0.0.0:6060 \ 44 | --tcp-upstream 8800 \ 45 | --udp-upstream 8.8.8.8:53 \ 46 | --password 123456 \ 47 | --cert path/to/cert.der \ 48 | --key path/to/key.der 49 | ``` 50 | 51 | - `--addr` — IP:port to listen on. 52 | - `--tcp-upstream` — Default TCP upstream for OUT tunnels (if client does not specify one). 53 | - `--udp-upstream` — Default UDP upstream for OUT tunnels (if client does not specify one). 54 | - `--password` — Required for client authentication. 55 | - `--cert`/`--key` — Certificate and private key for the server. If omitted, a self-signed certificate for `localhost` is generated (for testing only). 56 | 57 | ### Start the client (multiple tunnels example) 58 | 59 | ```sh 60 | rstunc \ 61 | --server-addr 1.2.3.4:6060 \ 62 | --password 123456 \ 63 | --cert path/to/cert.der \ 64 | --tcp-mappings "OUT^0.0.0.0:9900^8800,IN^127.0.0.1:8080^9000" \ 65 | --udp-mappings "OUT^0.0.0.0:9900^8.8.8.8:53" \ 66 | --hop-interval-ms 30000 \ 67 | --loglevel D 68 | ``` 69 | 70 | - `--tcp-mappings` and `--udp-mappings` now accept **comma-separated lists** of mappings, each in the form `MODE^[ip:]port^[ip:]port` (e.g., `OUT^8000^ANY`). 71 | - `MODE` is either `OUT` or `IN`. 72 | - `ANY` as the destination means the server's default upstream is used. 73 | - `--hop-interval-ms` — Optional parameter to enable connection migration by periodically changing local UDP ports at the specified interval(ms). 74 | 75 | #### Simple test 76 | 77 | ```sh 78 | # Start server with auto-generated self-signed certificate 79 | rstund -a 9000 -p 1234 80 | 81 | # Start client with both TCP and UDP tunnels and connection migration every 30 seconds 82 | rstunc \ 83 | --server-addr 127.0.0.1:9000 \ 84 | --password 1234 \ 85 | --tcp-mappings "OUT^0.0.0.0:9900^8800" \ 86 | --udp-mappings "IN^127.0.0.1:8080^9000" \ 87 | --hop-interval-ms 30000 88 | ``` 89 | 90 | --- 91 | 92 | ## Command-Line Options 93 | 94 | ### rstund (server) 95 | 96 | ``` 97 | Usage: rstund [OPTIONS] --addr --password 98 | 99 | Options: 100 | -a, --addr Address ([ip:]port) to listen on 101 | -t, --tcp-upstream Default TCP upstream for OUT tunnels ([ip:]port) 102 | -u, --udp-upstream Default UDP upstream for OUT tunnels ([ip:]port) 103 | -p, --password Server password (required) 104 | -c, --cert Path to certificate file (optional) 105 | -k, --key Path to key file (optional) 106 | -w, --workers Number of async worker threads [default: 0] 107 | --quic-timeout-ms QUIC idle timeout (ms) [default: 40000] 108 | --tcp-timeout-ms TCP idle timeout (ms) [default: 30000] 109 | --udp-timeout-ms UDP idle timeout (ms) [default: 30000] 110 | -l, --loglevel Log level [default: I] [T, D, I, W, E] 111 | -h, --help Print help 112 | -V, --version Print version 113 | ``` 114 | 115 | ### rstunc (client) 116 | 117 | ``` 118 | Usage: rstunc [OPTIONS] --server-addr --password 119 | 120 | Options: 121 | -a, --server-addr Server address ([:port]) 122 | -p, --password Password for server authentication 123 | -t, --tcp-mappings Comma-separated list of TCP tunnel mappings (MODE^[ip:]port^[ip:]port) 124 | -u, --udp-mappings Comma-separated list of UDP tunnel mappings (MODE^[ip:]port^[ip:]port) 125 | -c, --cert Path to certificate file (optional) 126 | -e, --cipher Cipher suite [default: chacha20-poly1305] [chacha20-poly1305, aes-256-gcm, aes-128-gcm] 127 | -w, --workers Number of async worker threads [default: 0] 128 | -r, --wait-before-retry-ms Wait before retry (ms) [default: 5000] 129 | --quic-timeout-ms QUIC idle timeout (ms) [default: 30000] 130 | --tcp-timeout-ms TCP idle timeout (ms) [default: 30000] 131 | --udp-timeout-ms UDP idle timeout (ms) [default: 5000] 132 | --hop-interval-ms Interval in millseconds for connection migration to new random local UDP port (optional,default:0 means disabled) 133 | --dot Comma-separated DoT servers for DNS resolution 134 | --dns Comma-separated DNS servers for resolution 135 | -l, --loglevel Log level [default: I] [T, D, I, W, E] 136 | -h, --help Print help 137 | -V, --version Print version 138 | ``` 139 | 140 | --- 141 | 142 | ## Connection Migration 143 | 144 | The client supports optional connection migration via the `--hop-interval-ms` parameter. When specified, the QUIC connection will periodically migrate to a new random local UDP port at the given interval (in millseconds). This feature helps avoid UDP throttling that may occur during long data transfers while maintaining the upper-layer QUIC connection without interruption. 145 | 146 | **Benefits:** 147 | - Prevents UDP port-based rate limiting during extended data transfers 148 | - Maintains seamless connectivity without breaking existing tunnels 149 | - Helps bypass certain network restrictions that may throttle long-lived UDP flows 150 | 151 | **Usage:** 152 | - If `--hop-interval-ms` is not specified, connection migration is disabled 153 | - Recommended intervals range from 60 to 600 seconds depending on network conditions 154 | - Shorter intervals provide more frequent migration but may cause brief latency spikes 155 | 156 | --- 157 | 158 | ## Notes 159 | 160 | - **Multiple tunnels**: You can specify multiple TCP and/or UDP tunnels in a single client or server instance using the new `--tcp-mappings` and `--udp-mappings` options. 161 | - **Mapping format**: Each mapping is `MODE^[ip:]port^[ip:]port`, where `MODE` is `OUT` or `IN`. 162 | - **Self-signed certificates**: If no certificate is provided, a self-signed certificate for `localhost` is generated (for testing only). 163 | - **Security**: For production, always use a valid certificate and connect via domain name. 164 | - **Connection migration**: Use `--hop-interval-ms` to enable periodic port migration for improved performance in environments with UDP throttling. 165 | 166 | --- 167 | 168 | ## License 169 | 170 | This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, you can obtain one at [http://mozilla.org/MPL/2.0/](http://mozilla.org/MPL/2.0/). 171 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v[0-9]+.[0-9]+.[0-9]+' 7 | 8 | jobs: 9 | linux-x86_64: 10 | name: Linux x86_64 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: actions-rs/toolchain@v1 15 | with: 16 | toolchain: stable 17 | target: x86_64-unknown-linux-gnu 18 | override: true 19 | - run: cargo build --all-features --release && mkdir -p rstun-linux-x86_64 20 | && mv target/release/rstunc ./rstun-linux-x86_64/ 21 | && mv target/release/rstund ./rstun-linux-x86_64/ 22 | && tar zcf rstun-linux-x86_64.tar.gz ./rstun-linux-x86_64/* 23 | - name: Release 24 | uses: softprops/action-gh-release@v1 25 | if: startsWith(github.ref, 'refs/tags/') 26 | with: 27 | files: | 28 | rstun-linux-x86_64.tar.gz 29 | env: 30 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 31 | 32 | linux-musl-x86_64: 33 | name: Linux musl x86_64 34 | runs-on: ubuntu-latest 35 | steps: 36 | - uses: actions/checkout@v2 37 | - uses: actions-rs/toolchain@v1 38 | with: 39 | toolchain: stable 40 | target: x86_64-unknown-linux-musl 41 | override: true 42 | - run: sudo apt-get -y install musl-tools && rustup target add x86_64-unknown-linux-musl && cargo build --all-features --release --target x86_64-unknown-linux-musl 43 | && mkdir -p rstun-linux-musl-x86_64 44 | && mv target/x86_64-unknown-linux-musl/release/rstunc ./rstun-linux-musl-x86_64/ 45 | && mv target/x86_64-unknown-linux-musl/release/rstund ./rstun-linux-musl-x86_64/ 46 | && tar zcf rstun-linux-musl-x86_64.tar.gz ./rstun-linux-musl-x86_64/* 47 | - name: Release 48 | uses: softprops/action-gh-release@v1 49 | if: startsWith(github.ref, 'refs/tags/') 50 | with: 51 | files: | 52 | rstun-linux-musl-x86_64.tar.gz 53 | env: 54 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 55 | 56 | windows-x86_64: 57 | name: Windows x86_64 58 | runs-on: windows-latest 59 | defaults: 60 | run: 61 | shell: bash 62 | steps: 63 | - uses: actions/checkout@v2 64 | - uses: actions-rs/toolchain@v1 65 | with: 66 | toolchain: stable 67 | target: x86_64-pc-windows-msvc 68 | override: true 69 | - run: cargo build --all-features --release && mkdir -p rstun-windows-x86_64 70 | && mv target/release/rstunc.exe ./rstun-windows-x86_64/ 71 | && mv target/release/rstund.exe ./rstun-windows-x86_64/ 72 | && 7z a rstun-windows-x86_64.zip ./rstun-windows-x86_64/* 73 | - name: Release 74 | uses: softprops/action-gh-release@v1 75 | if: startsWith(github.ref, 'refs/tags/') 76 | with: 77 | files: | 78 | rstun-windows-x86_64.zip 79 | env: 80 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 81 | 82 | darwin-x86_64: 83 | name: Darwin x86_64 84 | runs-on: macos-latest 85 | steps: 86 | - uses: actions/checkout@v2 87 | - uses: actions-rs/toolchain@v1 88 | with: 89 | toolchain: stable 90 | target: x86_64-apple-darwin 91 | override: true 92 | - run: rustup target add x86_64-apple-darwin && cargo build --all-features --release --target x86_64-apple-darwin 93 | && mkdir -p rstun-darwin-x86_64 94 | && mv target/x86_64-apple-darwin/release/rstunc ./rstun-darwin-x86_64/ 95 | && mv target/x86_64-apple-darwin/release/rstund ./rstun-darwin-x86_64/ 96 | && zip rstun-darwin-x86_64.zip ./rstun-darwin-x86_64/* 97 | - name: Release 98 | uses: softprops/action-gh-release@v1 99 | if: startsWith(github.ref, 'refs/tags/') 100 | with: 101 | files: | 102 | rstun-darwin-x86_64.zip 103 | env: 104 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 105 | 106 | darwin-aarch64: 107 | name: Darwin Aarch64 108 | runs-on: macos-latest 109 | steps: 110 | - uses: actions/checkout@v2 111 | - uses: actions-rs/toolchain@v1 112 | with: 113 | toolchain: stable 114 | target: aarch64-apple-darwin 115 | override: true 116 | - run: rustup target add aarch64-apple-darwin && cargo build --all-features --release --target aarch64-apple-darwin 117 | && mkdir -p rstun-darwin-aarch64 118 | && mv target/aarch64-apple-darwin/release/rstunc ./rstun-darwin-aarch64/ 119 | && mv target/aarch64-apple-darwin/release/rstund ./rstun-darwin-aarch64/ 120 | && zip rstun-darwin-aarch64.zip ./rstun-darwin-aarch64/* 121 | - name: Release 122 | uses: softprops/action-gh-release@v1 123 | if: startsWith(github.ref, 'refs/tags/') 124 | with: 125 | files: | 126 | rstun-darwin-aarch64.zip 127 | env: 128 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 129 | 130 | linux-armv7: 131 | name: Linux ARMv7 132 | runs-on: ubuntu-latest 133 | steps: 134 | - uses: actions/checkout@v2 135 | - uses: actions-rs/toolchain@v1 136 | with: 137 | toolchain: stable 138 | target: armv7-unknown-linux-gnueabihf 139 | override: true 140 | - run: rustup target add armv7-unknown-linux-gnueabihf && cargo install cross --git https://github.com/cross-rs/cross 141 | && cross build --all-features --release --target armv7-unknown-linux-gnueabihf 142 | && mkdir -p rstun-linux-armv7 143 | && mv target/armv7-unknown-linux-gnueabihf/release/rstunc ./rstun-linux-armv7/ 144 | && mv target/armv7-unknown-linux-gnueabihf/release/rstund ./rstun-linux-armv7/ 145 | && tar zcf rstun-linux-armv7.tar.gz ./rstun-linux-armv7/* 146 | - name: Release 147 | uses: softprops/action-gh-release@v1 148 | if: startsWith(github.ref, 'refs/tags/') 149 | with: 150 | files: | 151 | rstun-linux-armv7.tar.gz 152 | env: 153 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 154 | 155 | linux-aarch64: 156 | name: Linux Aarch64 157 | runs-on: ubuntu-latest 158 | steps: 159 | - uses: actions/checkout@v2 160 | - uses: actions-rs/toolchain@v1 161 | with: 162 | toolchain: stable 163 | target: aarch64-unknown-linux-gnu 164 | override: true 165 | - run: rustup target add aarch64-unknown-linux-gnu && cargo install cross --git https://github.com/cross-rs/cross 166 | && cross build --all-features --release --target aarch64-unknown-linux-gnu 167 | && mkdir -p rstun-linux-aarch64 168 | && mv target/aarch64-unknown-linux-gnu/release/rstunc ./rstun-linux-aarch64/ 169 | && mv target/aarch64-unknown-linux-gnu/release/rstund ./rstun-linux-aarch64/ 170 | && tar zcf rstun-linux-aarch64.tar.gz ./rstun-linux-aarch64/* 171 | - name: Release 172 | uses: softprops/action-gh-release@v1 173 | if: startsWith(github.ref, 'refs/tags/') 174 | with: 175 | files: | 176 | rstun-linux-aarch64.tar.gz 177 | env: 178 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 179 | 180 | linux-musl-aarch64: 181 | name: Linux musl Aarch64 182 | runs-on: ubuntu-latest 183 | steps: 184 | - uses: actions/checkout@v2 185 | - uses: actions-rs/toolchain@v1 186 | with: 187 | toolchain: stable 188 | target: aarch64-unknown-linux-musl 189 | override: true 190 | - run: sudo apt-get -y install musl-tools && rustup target add aarch64-unknown-linux-musl 191 | - run: cargo install cross --git https://github.com/cross-rs/cross 192 | - run: cross build --all-features --release --target aarch64-unknown-linux-musl 193 | - run: mkdir -p rstun-linux-musl-aarch64 194 | - run: mv target/aarch64-unknown-linux-musl/release/rstunc ./rstun-linux-musl-aarch64/ 195 | - run: mv target/aarch64-unknown-linux-musl/release/rstund ./rstun-linux-musl-aarch64/ 196 | - run: tar zcf rstun-linux-musl-aarch64.tar.gz ./rstun-linux-musl-aarch64/* 197 | - name: Release 198 | uses: softprops/action-gh-release@v1 199 | if: startsWith(github.ref, 'refs/tags/') 200 | with: 201 | files: | 202 | rstun-linux-musl-aarch64.tar.gz 203 | env: 204 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 205 | 206 | 207 | -------------------------------------------------------------------------------- /src/util/stream_util.rs: -------------------------------------------------------------------------------- 1 | use crate::tcp::AsyncStream; 2 | use crate::BUFFER_POOL; 3 | use anyhow::Result; 4 | use log::debug; 5 | use quinn::{RecvStream, SendStream}; 6 | use std::fmt::Display; 7 | use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr}; 8 | use std::time::Duration; 9 | use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadHalf, WriteHalf}; 10 | use tokio::sync::oneshot; 11 | use tokio::time::error::Elapsed; 12 | 13 | #[derive(Debug, PartialEq, Eq)] 14 | pub enum TransferError { 15 | InternalError, 16 | InvalidIPAddress, 17 | InvalidIPFamily, 18 | TimeoutError, 19 | } 20 | 21 | impl Display for TransferError { 22 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 23 | match self { 24 | Self::InternalError => write!(f, "InternalError"), 25 | Self::InvalidIPAddress => write!(f, "InvalidIPAddress"), 26 | Self::InvalidIPFamily => write!(f, "InvalidIPFamily"), 27 | Self::TimeoutError => write!(f, "TimeoutError"), 28 | } 29 | } 30 | } 31 | 32 | pub struct StreamUtil {} 33 | 34 | impl StreamUtil { 35 | pub fn start_flowing( 36 | tag: &'static str, 37 | stream: S, 38 | quic_stream: (SendStream, RecvStream), 39 | stream_timeout_ms: u64, 40 | ) { 41 | let peer_addr = match stream.peer_addr() { 42 | Ok(addr) => addr, 43 | Err(e) => { 44 | log::error!("failed to obtain peer address:{e}"); 45 | return; 46 | } 47 | }; 48 | 49 | let (mut stream_read, mut stream_write) = tokio::io::split(stream); 50 | let (mut quic_send, mut quic_recv) = quic_stream; 51 | let index = quic_send.id().index(); 52 | 53 | debug!("[{tag}] START {index:<3} → {peer_addr:<20}"); 54 | 55 | let (quic_to_stream_tx, quic_to_stream_rx) = oneshot::channel::<()>(); 56 | let (stream_to_quic_tx, stream_to_quic_rx) = oneshot::channel::<()>(); 57 | const BUFFER_SIZE: usize = 8192; 58 | 59 | tokio::spawn(async move { 60 | let mut transfer_bytes = 0u64; 61 | let mut buffer = BUFFER_POOL.alloc_and_fill(BUFFER_SIZE); 62 | loop { 63 | let result = Self::quic_to_stream( 64 | &mut quic_recv, 65 | &mut stream_write, 66 | &mut buffer, 67 | &mut transfer_bytes, 68 | stream_timeout_ms, 69 | ) 70 | .await; 71 | 72 | match result { 73 | Err(TransferError::TimeoutError) => { 74 | let _ = quic_to_stream_tx.send(()); 75 | match stream_to_quic_rx.await { 76 | _ => { 77 | // either the sender is dropped or the task times out 78 | break; 79 | } 80 | } 81 | } 82 | Ok(0) | Err(_) => { 83 | let _ = quic_to_stream_tx.send(()); 84 | break; 85 | } 86 | _ => { 87 | // ok, continue 88 | } 89 | } 90 | } 91 | 92 | debug!("[{tag}] END {index:<5}→ {peer_addr}, {transfer_bytes} bytes"); 93 | }); 94 | 95 | tokio::spawn(async move { 96 | let mut transfer_bytes = 0u64; 97 | let mut buffer = BUFFER_POOL.alloc_and_fill(BUFFER_SIZE); 98 | loop { 99 | let result = Self::stream_to_quic( 100 | &mut stream_read, 101 | &mut quic_send, 102 | &mut buffer, 103 | &mut transfer_bytes, 104 | stream_timeout_ms, 105 | ) 106 | .await; 107 | 108 | match result { 109 | Err(TransferError::TimeoutError) => { 110 | let _ = stream_to_quic_tx.send(()); 111 | match quic_to_stream_rx.await { 112 | _ => { 113 | // either the sender is dropped or the task times out 114 | break; 115 | } 116 | } 117 | } 118 | Ok(0) | Err(_) => { 119 | let _ = stream_to_quic_tx.send(()); 120 | break; 121 | } 122 | _ => { 123 | // ok, continue 124 | } 125 | } 126 | } 127 | 128 | debug!("[{tag}] END {index:<4}← {peer_addr}, {transfer_bytes} bytes"); 129 | Ok::<(), anyhow::Error>(()) 130 | }); 131 | } 132 | 133 | async fn stream_to_quic( 134 | stream_read: &mut ReadHalf, 135 | quic_send: &mut SendStream, 136 | buffer: &mut [u8], 137 | transfer_bytes: &mut u64, 138 | stream_timeout_ms: u64, 139 | ) -> Result { 140 | let len_read = tokio::time::timeout( 141 | Duration::from_millis(stream_timeout_ms), 142 | stream_read.read(buffer), 143 | ) 144 | .await 145 | .map_err(|_: Elapsed| TransferError::TimeoutError)? 146 | .map_err(|_| TransferError::InternalError)?; 147 | if len_read > 0 { 148 | *transfer_bytes += len_read as u64; 149 | quic_send 150 | .write_all(&buffer[..len_read]) 151 | .await 152 | .map_err(|_| TransferError::InternalError)?; 153 | Ok(len_read) 154 | } else { 155 | quic_send 156 | .finish() 157 | .map_err(|_| TransferError::InternalError)?; 158 | Ok(0) 159 | } 160 | } 161 | 162 | async fn quic_to_stream( 163 | quic_recv: &mut RecvStream, 164 | stream_write: &mut WriteHalf, 165 | buffer: &mut [u8], 166 | transfer_bytes: &mut u64, 167 | stream_timeout_ms: u64, 168 | ) -> Result { 169 | let result = tokio::time::timeout( 170 | Duration::from_millis(stream_timeout_ms), 171 | quic_recv.read(buffer), 172 | ) 173 | .await 174 | .map_err(|_: Elapsed| TransferError::TimeoutError)? 175 | .map_err(|_| TransferError::InternalError)?; 176 | if let Some(len_read) = result { 177 | *transfer_bytes += len_read as u64; 178 | stream_write 179 | .write_all(&buffer[..len_read]) 180 | .await 181 | .map_err(|_| TransferError::InternalError)?; 182 | Ok(len_read) 183 | } else { 184 | stream_write 185 | .shutdown() 186 | .await 187 | .map_err(|_| TransferError::InternalError)?; 188 | Ok(0) 189 | } 190 | } 191 | 192 | pub async fn write_socket_addr( 193 | quic_send: &mut SendStream, 194 | addr: &Option, 195 | mark_none: bool, 196 | ) -> Result<()> { 197 | match addr { 198 | Some(SocketAddr::V4(v4)) => { 199 | let mut buf = [0u8; 1 + 4 + 2]; 200 | buf[0] = 4; 201 | buf[1..5].copy_from_slice(&v4.ip().octets()); 202 | buf[5..7].copy_from_slice(&v4.port().to_be_bytes()); 203 | quic_send.write_all(&buf[..7]).await?; 204 | } 205 | Some(SocketAddr::V6(v6)) => { 206 | let mut buf = [0u8; 1 + 16 + 2]; 207 | buf[0] = 6; 208 | buf[1..17].copy_from_slice(&v6.ip().octets()); 209 | buf[17..19].copy_from_slice(&v6.port().to_be_bytes()); 210 | quic_send.write_all(&buf[..19]).await?; 211 | } 212 | None => { 213 | if mark_none { 214 | quic_send.write_u8(0).await?; 215 | } 216 | } 217 | }; 218 | Ok(()) 219 | } 220 | 221 | pub async fn read_socket_addr( 222 | quic_recv: &mut RecvStream, 223 | stream_timeout_ms: u64, 224 | ) -> Result { 225 | let mut buf = [0u8; 19]; 226 | tokio::time::timeout( 227 | Duration::from_millis(stream_timeout_ms), 228 | quic_recv.read_exact(&mut buf[..7]), 229 | ) 230 | .await 231 | .map_err(|_: Elapsed| TransferError::TimeoutError)? 232 | .map_err(|_| TransferError::InternalError)?; 233 | 234 | match buf[0] { 235 | 4 => { 236 | let ip = Ipv4Addr::from( 237 | <[u8; 4]>::try_from(&buf[1..5]).map_err(|_| TransferError::InvalidIPAddress)?, 238 | ); 239 | let port = u16::from_be_bytes(buf[5..7].try_into().unwrap()); 240 | Ok(SocketAddr::new(ip.into(), port)) 241 | } 242 | 6 => { 243 | tokio::time::timeout( 244 | Duration::from_millis(stream_timeout_ms), 245 | quic_recv.read_exact(&mut buf[7..]), 246 | ) 247 | .await 248 | .map_err(|_: Elapsed| TransferError::TimeoutError)? 249 | .map_err(|_| TransferError::InternalError)?; 250 | 251 | let ip = Ipv6Addr::from( 252 | <[u8; 16]>::try_from(&buf[1..17]) 253 | .map_err(|_| TransferError::InvalidIPAddress)?, 254 | ); 255 | let port = u16::from_be_bytes(buf[17..19].try_into().unwrap()); 256 | Ok(SocketAddr::new(ip.into(), port)) 257 | } 258 | _ => { 259 | log::error!("invalid address family"); 260 | Err(TransferError::InvalidIPFamily) 261 | } 262 | } 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /src/udp/udp_tunnel.rs: -------------------------------------------------------------------------------- 1 | use crate::tunnel_message::{TunnelMessage, UdpPeerAddr}; 2 | use crate::udp::{UdpMessage, UdpPacket}; 3 | use crate::BUFFER_POOL; 4 | use crate::UDP_PACKET_SIZE; 5 | use anyhow::{Context, Result}; 6 | use dashmap::DashMap; 7 | use log::{debug, error, info, warn}; 8 | use quinn::{Connection, RecvStream, SendStream}; 9 | use rs_utilities::log_and_bail; 10 | use std::{ 11 | net::{IpAddr, Ipv4Addr, SocketAddr}, 12 | sync::Arc, 13 | time::Duration, 14 | }; 15 | use tokio::sync::mpsc::{Receiver, Sender}; 16 | use tokio::sync::oneshot; 17 | use tokio::{net::UdpSocket, sync::Mutex}; 18 | 19 | type TSafe = Arc>; 20 | 21 | pub struct UdpTunnel; 22 | 23 | impl UdpTunnel { 24 | pub async fn start_serving( 25 | conn: &quinn::Connection, 26 | udp_sender: &Sender, 27 | udp_receiver: &mut Receiver, 28 | udp_timeout_ms: u64, 29 | ) { 30 | debug!("start serving udp via: {}", conn.remote_address()); 31 | let stream_map = Arc::new(DashMap::new()); 32 | while let Some(UdpMessage::Packet(packet)) = udp_receiver.recv().await { 33 | let quic_send = match UdpTunnel::open_stream( 34 | conn.clone(), 35 | udp_sender.clone(), 36 | packet.local_addr, 37 | stream_map.clone(), 38 | udp_timeout_ms, 39 | ) 40 | .await 41 | { 42 | Ok(quic_send) => quic_send, 43 | Err(e) => { 44 | error!("{e}"); 45 | if conn.close_reason().is_some() { 46 | debug!("connection is closed, will quit"); 47 | break; 48 | } 49 | continue; 50 | } 51 | }; 52 | 53 | // send the packet using an async task 54 | tokio::spawn(async move { 55 | let mut quic_send = quic_send.lock().await; 56 | let payload_len = packet.payload.len(); 57 | 58 | TunnelMessage::send( 59 | &mut quic_send, 60 | &TunnelMessage::ReqUdpStart(UdpPeerAddr(packet.peer_addr)), 61 | ) 62 | .await 63 | .ok(); 64 | 65 | TunnelMessage::send_raw(&mut quic_send, &packet.payload) 66 | .await 67 | .inspect_err(|e| { 68 | warn!( 69 | "failed to send datagram({payload_len}) through the tunnel, err: {e}" 70 | ); 71 | }) 72 | .ok(); 73 | }); 74 | } 75 | 76 | info!("udp server quit"); 77 | } 78 | 79 | async fn open_stream( 80 | conn: Connection, 81 | udp_sender: Sender, 82 | local_addr: SocketAddr, 83 | stream_map: Arc>>, 84 | udp_timeout_ms: u64, 85 | ) -> Result> { 86 | if let Some(s) = stream_map.get(&local_addr) { 87 | return Ok((*s).clone()); 88 | } 89 | 90 | let (quic_send, mut quic_recv) = 91 | conn.open_bi().await.context("open_bi failed for udp out")?; 92 | 93 | let quic_send = Arc::new(Mutex::new(quic_send)); 94 | stream_map.insert(local_addr, quic_send.clone()); 95 | 96 | let stream_map = stream_map.clone(); 97 | tokio::spawn(async move { 98 | debug!( 99 | "start udp stream: {local_addr}, streams: {}", 100 | stream_map.len() 101 | ); 102 | loop { 103 | let mut payload = BUFFER_POOL.alloc_and_fill(UDP_PACKET_SIZE); 104 | match tokio::time::timeout( 105 | Duration::from_millis(udp_timeout_ms), 106 | TunnelMessage::recv_raw(&mut quic_recv, &mut payload), 107 | ) 108 | .await 109 | { 110 | Ok(Ok(packet_len)) => { 111 | unsafe { 112 | payload.set_len(packet_len as usize); 113 | } 114 | let packet = UdpPacket { 115 | payload, 116 | local_addr, 117 | peer_addr: None, 118 | }; 119 | let _ = udp_sender.send(UdpMessage::Packet(packet)).await; 120 | } 121 | Ok(Err(_)) => { 122 | // warn!("failed to read for udp, err: {e}"); 123 | break; 124 | } 125 | Err(_) => { 126 | // Timeout occurred 127 | break; 128 | } 129 | } 130 | } 131 | 132 | stream_map.remove(&local_addr); 133 | debug!( 134 | "dropped udp stream: {local_addr}, streams: {}", 135 | stream_map.len() 136 | ); 137 | }); 138 | 139 | Ok(quic_send) 140 | } 141 | 142 | pub async fn start_accepting( 143 | conn: &quinn::Connection, 144 | upstream_addr: Option, 145 | udp_timeout_ms: u64, 146 | ) { 147 | let remote_addr = &conn.remote_address(); 148 | info!("start udp stream, {remote_addr} ↔ {upstream_addr:?}"); 149 | 150 | loop { 151 | match conn.accept_bi().await { 152 | Err(quinn::ConnectionError::TimedOut) => { 153 | info!("connection timeout: {remote_addr}"); 154 | break; 155 | } 156 | Err(quinn::ConnectionError::ApplicationClosed { .. }) => { 157 | debug!("connection closed: {remote_addr}"); 158 | break; 159 | } 160 | Err(e) => { 161 | error!("failed to accept_bi: {remote_addr}, err: {e}"); 162 | break; 163 | } 164 | Ok((quic_send, quic_recv)) => tokio::spawn(async move { 165 | Self::process(quic_send, quic_recv, upstream_addr, udp_timeout_ms).await 166 | }), 167 | }; 168 | } 169 | 170 | info!("connection for udp out is dropped"); 171 | } 172 | 173 | async fn process( 174 | quic_send: SendStream, 175 | mut quic_recv: RecvStream, 176 | upstream_addr: Option, 177 | udp_timeout_ms: u64, 178 | ) -> Result<()> { 179 | let quic_send = Arc::new(Mutex::new(quic_send)); 180 | let mut udp_socket = None; 181 | if let Some(upstream_addr) = upstream_addr { 182 | // pre-create the udp-socket if upstream is specified 183 | udp_socket = Self::create_peer_socket_and_exchange_data( 184 | upstream_addr, 185 | quic_send.clone(), 186 | udp_timeout_ms, 187 | ) 188 | .await?; 189 | } 190 | 191 | let mut buf = BUFFER_POOL.alloc_and_fill(UDP_PACKET_SIZE); 192 | loop { 193 | match tokio::time::timeout(Duration::from_millis(udp_timeout_ms), async { 194 | let peer_addr = match TunnelMessage::recv(&mut quic_recv).await? { 195 | TunnelMessage::ReqUdpStart(UdpPeerAddr(peer_addr)) => peer_addr, 196 | msg => { 197 | log_and_bail!("unexpected tunnel message: {msg}"); 198 | } 199 | }; 200 | 201 | let packet_len = TunnelMessage::recv_raw(&mut quic_recv, &mut buf).await?; 202 | Ok((peer_addr, packet_len)) 203 | }) 204 | .await 205 | { 206 | Ok(Ok((peer_addr, packet_len))) => { 207 | match peer_addr { 208 | Some(peer_addr) => { 209 | if let Some(upstream_addr) = upstream_addr { 210 | warn!("upstream_addr {upstream_addr:?} is specified for the connection, peer_addr {peer_addr} is ignored"); 211 | } else if udp_socket.as_ref().and_then(|sock| sock.0.peer_addr().ok()) 212 | != Some(peer_addr) 213 | { 214 | if let Some(udp_socket) = udp_socket { 215 | // shutdown the old socket 216 | udp_socket.1.send(()).ok(); 217 | } 218 | udp_socket = Self::create_peer_socket_and_exchange_data( 219 | peer_addr, 220 | quic_send.clone(), 221 | udp_timeout_ms, 222 | ) 223 | .await?; 224 | } 225 | } 226 | None => { 227 | if udp_socket.is_none() { 228 | log_and_bail!("no valid upstream_addr to connect"); 229 | } 230 | } 231 | }; 232 | 233 | udp_socket 234 | .as_ref() 235 | .unwrap() 236 | .0 237 | .send(&buf[..packet_len as usize]) 238 | .await 239 | .context("failed to send datagram through udp_socket")?; 240 | } 241 | Ok(Err(e)) => { 242 | warn!("failed to read from udp packet from tunnel, err: {e}"); 243 | break; 244 | } 245 | Err(_) => { 246 | // timeout on receiving datagrams from upstream 247 | break; 248 | } 249 | } 250 | } 251 | 252 | Ok::<(), anyhow::Error>(()) 253 | } 254 | 255 | async fn create_peer_socket_and_exchange_data( 256 | addr: SocketAddr, 257 | quic_send: Arc>, 258 | udp_timeout_ms: u64, 259 | ) -> Result, oneshot::Sender<()>)>> { 260 | let local_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0); 261 | match UdpSocket::bind(local_addr).await { 262 | Ok(udp_socket) => { 263 | if let Err(e) = udp_socket.connect(addr).await { 264 | log_and_bail!("failed to connect to upstream: {addr}, err: {e}"); 265 | }; 266 | 267 | let (shutdown_tx, shutdown_rx) = oneshot::channel::<()>(); 268 | let udp_socket = Arc::new(udp_socket); 269 | 270 | Self::udp_to_quic( 271 | udp_socket.clone(), 272 | quic_send.clone(), 273 | udp_timeout_ms, 274 | shutdown_rx, 275 | ); 276 | 277 | Ok(Some((udp_socket, shutdown_tx))) 278 | } 279 | Err(e) => { 280 | log_and_bail!("failed to bind to localhost, err: {e}"); 281 | } 282 | } 283 | } 284 | 285 | fn udp_to_quic( 286 | udp_socket: Arc, 287 | quic_send: Arc>, 288 | udp_timeout_ms: u64, 289 | mut shutdown_rx: oneshot::Receiver<()>, 290 | ) { 291 | tokio::spawn(async move { 292 | debug!("start udp stream → {:?}", udp_socket.peer_addr()); 293 | let mut buf = BUFFER_POOL.alloc_and_fill(UDP_PACKET_SIZE); 294 | loop { 295 | tokio::select! { 296 | biased; 297 | 298 | _ = &mut shutdown_rx => { 299 | break; 300 | } 301 | 302 | result = tokio::time::timeout( 303 | Duration::from_millis(udp_timeout_ms), 304 | udp_socket.recv(&mut buf) 305 | ) => { 306 | match result { 307 | Ok(Ok(len)) => { 308 | let mut quic_send = quic_send.lock().await; 309 | TunnelMessage::send_raw(&mut quic_send, &buf[..len]) 310 | .await 311 | .ok(); 312 | } 313 | Ok(Err(e)) => { 314 | warn!("failed to receive datagrams from upstream, err: {e:?}"); 315 | break; 316 | } 317 | Err(_) => { 318 | // timeout on receiving datagrams from upstream 319 | break; 320 | } 321 | } 322 | } 323 | } 324 | } 325 | debug!("dropped udp stream → {:?}", udp_socket.peer_addr()); 326 | }); 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /src/server.rs: -------------------------------------------------------------------------------- 1 | use crate::tcp::tcp_tunnel::TcpTunnel; 2 | use crate::tcp::{StreamMessage, StreamSender}; 3 | use crate::tunnel_message::TunnelMessage; 4 | use crate::udp::udp_server::{UdpMessage, UdpSender}; 5 | use crate::udp::{udp_server::UdpServer, udp_tunnel::UdpTunnel}; 6 | use crate::{ 7 | pem_util, ServerConfig, TcpServer, TcpTunnelInInfo, TcpTunnelOutInfo, Tunnel, TunnelConfig, 8 | TunnelMode, TunnelType, UdpTunnelInInfo, UdpTunnelOutInfo, UpstreamType, 9 | SUPPORTED_CIPHER_SUITES, 10 | }; 11 | use anyhow::{Context, Result}; 12 | use log::{debug, error, info, warn}; 13 | use quinn::crypto::rustls::QuicServerConfig; 14 | use quinn::IdleTimeout; 15 | use quinn::VarInt; 16 | use quinn::{congestion, Connection, Endpoint, SendStream, TransportConfig}; 17 | use rs_utilities::log_and_bail; 18 | use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer}; 19 | use std::net::SocketAddr; 20 | use std::sync::{Arc, Mutex, Once}; 21 | use tokio::net::TcpStream; 22 | use tokio::time::Duration; 23 | 24 | #[derive(Debug, Clone)] 25 | struct ConnectedTcpInSession { 26 | conn: Connection, 27 | sender: StreamSender, 28 | } 29 | 30 | #[derive(Debug, Clone)] 31 | struct ConnectedUdpInSession { 32 | conn: Connection, 33 | sender: UdpSender, 34 | } 35 | 36 | #[derive(Debug)] 37 | struct State { 38 | config: ServerConfig, 39 | endpoint: Option, 40 | tcp_sessions: Vec, 41 | udp_sessions: Vec, 42 | } 43 | 44 | impl State { 45 | pub fn new(config: ServerConfig) -> Self { 46 | State { 47 | config, 48 | endpoint: None, 49 | tcp_sessions: Vec::new(), 50 | udp_sessions: Vec::new(), 51 | } 52 | } 53 | } 54 | 55 | #[derive(Debug)] 56 | pub struct Server { 57 | inner_state: Arc>, 58 | } 59 | 60 | macro_rules! inner_state { 61 | ($self:ident, $field:ident) => { 62 | (*$self.inner_state.lock().unwrap()).$field 63 | }; 64 | } 65 | 66 | impl Server { 67 | pub fn new(config: ServerConfig) -> Self { 68 | Server { 69 | inner_state: Arc::new(Mutex::new(State::new(config))), 70 | } 71 | } 72 | 73 | pub fn bind(&mut self) -> Result { 74 | let mut state = self.inner_state.lock().unwrap(); 75 | let config = state.config.clone(); 76 | let addr: SocketAddr = config 77 | .addr 78 | .parse() 79 | .context(format!("invalid address: {}", config.addr))?; 80 | 81 | let quinn_server_cfg = Self::load_quinn_server_config(&config)?; 82 | let endpoint = quinn::Endpoint::server(quinn_server_cfg, addr).inspect_err(|e| { 83 | error!("failed to bind tunnel server on address: {addr}, err: {e}"); 84 | })?; 85 | 86 | info!( 87 | "tunnel server is bound on address: {}, idle_timeout: {}", 88 | endpoint.local_addr()?, 89 | config.quic_timeout_ms 90 | ); 91 | 92 | let ep = endpoint.clone(); 93 | tokio::spawn(async move { 94 | loop { 95 | tokio::time::sleep(Duration::from_secs(3600 * 24)).await; 96 | match Self::load_quinn_server_config(&config) { 97 | Ok(quinn_server_cfg) => { 98 | info!("updated quinn server config!"); 99 | ep.set_server_config(Some(quinn_server_cfg)); 100 | } 101 | Err(e) => { 102 | error!("failed to load quinn server config:{e}"); 103 | } 104 | } 105 | } 106 | }); 107 | 108 | state.endpoint = Some(endpoint); 109 | Ok(addr) 110 | } 111 | 112 | fn load_quinn_server_config(config: &ServerConfig) -> Result { 113 | let (certs, key) = 114 | Self::read_certs_and_key(config.cert_path.as_str(), config.key_path.as_str()) 115 | .context("failed to read certificate or key")?; 116 | 117 | let default_provider = rustls::crypto::ring::default_provider(); 118 | let provider = rustls::crypto::CryptoProvider { 119 | cipher_suites: SUPPORTED_CIPHER_SUITES.into(), 120 | ..default_provider 121 | }; 122 | 123 | let tls_server_cfg = rustls::ServerConfig::builder_with_provider(provider.into()) 124 | .with_protocol_versions(&[&rustls::version::TLS13]) 125 | .unwrap() 126 | .with_no_client_auth() 127 | .with_single_cert(certs, key) 128 | .unwrap(); 129 | 130 | let mut transport_cfg = TransportConfig::default(); 131 | transport_cfg.stream_receive_window(VarInt::from_u32(1024 * 1024)); 132 | transport_cfg.receive_window(VarInt::from_u32(1024 * 1024 * 2)); 133 | transport_cfg.send_window(1024 * 1024 * 2); 134 | transport_cfg.congestion_controller_factory(Arc::new(congestion::BbrConfig::default())); 135 | if config.quic_timeout_ms > 0 { 136 | let timeout = IdleTimeout::from(VarInt::from_u32(config.quic_timeout_ms as u32)); 137 | transport_cfg.max_idle_timeout(Some(timeout)); 138 | transport_cfg 139 | .keep_alive_interval(Some(Duration::from_millis(config.quic_timeout_ms * 2 / 3))); 140 | } 141 | transport_cfg.max_concurrent_bidi_streams(VarInt::from_u32(1024)); 142 | 143 | let quic_server_cfg = Arc::new(QuicServerConfig::try_from(tls_server_cfg)?); 144 | let mut quinn_server_cfg = quinn::ServerConfig::with_crypto(quic_server_cfg); 145 | quinn_server_cfg.transport = Arc::new(transport_cfg); 146 | Ok(quinn_server_cfg) 147 | } 148 | 149 | pub async fn serve(&self) -> Result<()> { 150 | let state = self.inner_state.clone(); 151 | tokio::spawn(async move { 152 | let mut interval = tokio::time::interval(Duration::from_secs(2)); 153 | loop { 154 | interval.tick().await; 155 | Self::clear_expired_sessions(state.clone()); 156 | } 157 | }); 158 | 159 | let endpoint = inner_state!(self, endpoint).take().context("failed")?; 160 | while let Some(client_conn) = endpoint.accept().await { 161 | let state = self.inner_state.clone(); 162 | let config = inner_state!(self, config).clone(); 163 | tokio::spawn(async move { 164 | let client_conn = client_conn.await?; 165 | let tun_type = Self::authenticate_connection(&config, client_conn).await?; 166 | 167 | match tun_type { 168 | TunnelType::TcpOut(info) => { 169 | TcpTunnel::start_accepting( 170 | &info.conn, 171 | Some(info.upstream_addr), 172 | config.tcp_timeout_ms, 173 | ) 174 | .await; 175 | } 176 | 177 | TunnelType::UdpOut(info) => { 178 | UdpTunnel::start_accepting( 179 | &info.conn, 180 | Some(info.upstream_addr), 181 | config.udp_timeout_ms, 182 | ) 183 | .await 184 | } 185 | 186 | TunnelType::TcpIn(mut info) => { 187 | state 188 | .lock() 189 | .unwrap() 190 | .tcp_sessions 191 | .push(ConnectedTcpInSession { 192 | conn: info.conn.clone(), 193 | sender: info.tcp_server.clone_sender(), 194 | }); 195 | 196 | let mut tcp_receiver = info.tcp_server.take_receiver(); 197 | 198 | TcpTunnel::start_serving( 199 | false, 200 | &info.conn, 201 | &mut tcp_receiver, 202 | &mut None, 203 | config.tcp_timeout_ms, 204 | ) 205 | .await; 206 | 207 | info.tcp_server.shutdown().await.ok(); 208 | } 209 | 210 | TunnelType::UdpIn(mut info) => { 211 | state 212 | .lock() 213 | .unwrap() 214 | .udp_sessions 215 | .push(ConnectedUdpInSession { 216 | conn: info.conn.clone(), 217 | sender: info.udp_server.clone_sender(), 218 | }); 219 | 220 | let mut udp_receiver = info.udp_server.take_receiver(); 221 | let udp_sender = info.udp_server.clone_sender(); 222 | 223 | UdpTunnel::start_serving( 224 | &info.conn, 225 | &udp_sender, 226 | &mut udp_receiver, 227 | config.udp_timeout_ms, 228 | ) 229 | .await; 230 | 231 | info.udp_server.shutdown().await.ok(); 232 | } 233 | TunnelType::DynamicUpstreamTcpOut(conn) => { 234 | TcpTunnel::start_accepting(&conn, None, config.tcp_timeout_ms).await; 235 | } 236 | TunnelType::DynamicUpstreamUdpOut(conn) => { 237 | UdpTunnel::start_accepting(&conn, None, config.udp_timeout_ms).await 238 | } 239 | } 240 | 241 | Ok::<(), anyhow::Error>(()) 242 | }); 243 | } 244 | info!("quit!"); 245 | 246 | Ok(()) 247 | } 248 | 249 | async fn authenticate_connection( 250 | config: &ServerConfig, 251 | conn: quinn::Connection, 252 | ) -> Result { 253 | let remote_addr = &conn.remote_address(); 254 | 255 | info!("authenticating connection, addr:{remote_addr}"); 256 | let (mut quic_send, mut quic_recv) = conn 257 | .accept_bi() 258 | .await 259 | .context(format!("login request not received in time: {remote_addr}"))?; 260 | 261 | info!("received bi_stream request: {remote_addr}"); 262 | match TunnelMessage::recv(&mut quic_recv).await? { 263 | TunnelMessage::ReqLogin(login_info) => { 264 | info!("received ReqLogin request: {remote_addr}"); 265 | 266 | Self::check_password(config.password.as_str(), login_info.password.as_str())?; 267 | 268 | let tunnel_type = match login_info.tunnel { 269 | Tunnel::NetworkBased(tunnel_config) => { 270 | Self::derive_tunnel_type(conn, &mut quic_send, &tunnel_config, config) 271 | .await? 272 | } 273 | Tunnel::ChannelBased(upstream_type) => match upstream_type { 274 | UpstreamType::Tcp => TunnelType::DynamicUpstreamTcpOut(conn), 275 | UpstreamType::Udp => TunnelType::DynamicUpstreamUdpOut(conn), 276 | }, 277 | }; 278 | 279 | TunnelMessage::send(&mut quic_send, &TunnelMessage::RespSuccess).await?; 280 | info!("connection authenticated! addr: {remote_addr}"); 281 | Ok(tunnel_type) 282 | } 283 | 284 | _ => { 285 | log_and_bail!("received unepxected message"); 286 | } 287 | } 288 | } 289 | 290 | async fn derive_tunnel_type( 291 | conn: quinn::Connection, 292 | quic_send: &mut SendStream, 293 | tunnel_config: &TunnelConfig, 294 | config: &ServerConfig, 295 | ) -> Result { 296 | let upstream_addr = match tunnel_config.upstream.upstream_type { 297 | UpstreamType::Tcp => { 298 | Self::obtain_upstream_addr(tunnel_config, &config.default_tcp_upstream)? 299 | } 300 | UpstreamType::Udp => { 301 | Self::obtain_upstream_addr(tunnel_config, &config.default_udp_upstream)? 302 | } 303 | }; 304 | let tunnel_type = match tunnel_config.mode { 305 | TunnelMode::Out => match tunnel_config.upstream.upstream_type { 306 | UpstreamType::Tcp => TunnelType::TcpOut(TcpTunnelOutInfo { 307 | conn, 308 | upstream_addr, 309 | }), 310 | 311 | UpstreamType::Udp => TunnelType::UdpOut(UdpTunnelOutInfo { 312 | conn, 313 | upstream_addr, 314 | }), 315 | }, 316 | 317 | TunnelMode::In => match tunnel_config.upstream.upstream_type { 318 | UpstreamType::Tcp => { 319 | let tcp_server = match TcpServer::bind_and_start(upstream_addr).await { 320 | Ok(tcp_server) => tcp_server, 321 | Err(e) => { 322 | TunnelMessage::send_failure( 323 | quic_send, 324 | format!("udp server failed to bind at: {upstream_addr}"), 325 | ) 326 | .await?; 327 | log_and_bail!("tcp_IN login rejected: {e}"); 328 | } 329 | }; 330 | 331 | TunnelMessage::send(quic_send, &TunnelMessage::RespSuccess).await?; 332 | TunnelType::TcpIn(TcpTunnelInInfo { conn, tcp_server }) 333 | } 334 | 335 | UpstreamType::Udp => { 336 | let udp_server = match UdpServer::bind_and_start(upstream_addr).await { 337 | Ok(udp_server) => udp_server, 338 | Err(e) => { 339 | TunnelMessage::send_failure( 340 | quic_send, 341 | format!("udp server failed to bind at: {upstream_addr}"), 342 | ) 343 | .await?; 344 | log_and_bail!("udp_IN login rejected: {e}"); 345 | } 346 | }; 347 | 348 | TunnelMessage::send(quic_send, &TunnelMessage::RespSuccess).await?; 349 | TunnelType::UdpIn(UdpTunnelInInfo { conn, udp_server }) 350 | } 351 | }, 352 | }; 353 | 354 | Ok(tunnel_type) 355 | } 356 | 357 | fn obtain_upstream_addr( 358 | tunnel_config: &TunnelConfig, 359 | default_upstream: &Option, 360 | ) -> Result { 361 | Ok(match tunnel_config.upstream.upstream_addr { 362 | None => { 363 | if tunnel_config.mode == TunnelMode::In { 364 | log_and_bail!("explicit port is required to start inbound tunneling"); 365 | } 366 | 367 | if default_upstream.is_none() { 368 | log_and_bail!( 369 | "explicit {} upstream address must be specified when logging in because there's no default upstream specified for the server", 370 | tunnel_config.upstream.upstream_type 371 | ); 372 | } 373 | 374 | default_upstream.unwrap() 375 | } 376 | 377 | Some(addr) => { 378 | if tunnel_config.mode == TunnelMode::In 379 | && !addr.ip().is_unspecified() 380 | && !addr.ip().is_loopback() 381 | { 382 | log_and_bail!( 383 | "only loopback or unspecified IP is allowed for inbound tunelling: {addr}, or simply specify a port without the IP part" 384 | ); 385 | } 386 | 387 | addr 388 | } 389 | }) 390 | } 391 | 392 | fn clear_expired_sessions(state: Arc>) { 393 | let mut state = state.lock().unwrap(); 394 | state.udp_sessions.retain(|sess| { 395 | if sess.conn.close_reason().is_some() { 396 | let sess = sess.clone(); 397 | tokio::spawn(async move { 398 | sess.sender.send(UdpMessage::Quit).await.ok(); 399 | debug!("dropped udp session: {}", sess.conn.remote_address()); 400 | }); 401 | false 402 | } else { 403 | true 404 | } 405 | }); 406 | 407 | state.tcp_sessions.retain(|sess| { 408 | if sess.conn.close_reason().is_some() { 409 | let sess = sess.clone(); 410 | tokio::spawn(async move { 411 | sess.sender.send(StreamMessage::Quit).await.ok(); 412 | debug!("dropped tcp session: {}", sess.conn.remote_address()); 413 | }); 414 | false 415 | } else { 416 | true 417 | } 418 | }); 419 | } 420 | 421 | fn read_certs_and_key( 422 | cert_path: &str, 423 | key_path: &str, 424 | ) -> Result<(Vec>, PrivateKeyDer<'static>)> { 425 | let (certs, key) = if cert_path.is_empty() { 426 | static ONCE: Once = Once::new(); 427 | ONCE.call_once(|| { 428 | info!("will use auto-generated self-signed certificate."); 429 | warn!("============================= WARNING =============================="); 430 | warn!("No valid certificate path is provided, a self-signed certificate"); 431 | warn!("for the domain \"localhost\" is generated."); 432 | warn!("============== Be cautious, this is for TEST only!!! ==============="); 433 | }); 434 | 435 | let cert = rcgen::generate_simple_self_signed(vec!["localhost".into()])?; 436 | let key = PrivatePkcs8KeyDer::from(cert.signing_key.serialize_der()); 437 | let cert = CertificateDer::from(cert.cert); 438 | (vec![cert], PrivateKeyDer::Pkcs8(key)) 439 | } else { 440 | let certs = pem_util::load_certificates_from_pem(cert_path) 441 | .context(format!("failed to read cert file: {cert_path}"))?; 442 | let key = pem_util::load_private_key_from_pem(key_path) 443 | .context(format!("failed to read key file: {key_path}"))?; 444 | (certs, key) 445 | }; 446 | 447 | Ok((certs, key)) 448 | } 449 | 450 | fn check_password(password1: &str, password2: &str) -> Result<()> { 451 | if password1 != password2 { 452 | log_and_bail!("passwords don't match!"); 453 | } 454 | Ok(()) 455 | } 456 | } 457 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod client; 2 | mod pem_util; 3 | mod server; 4 | mod tcp; 5 | mod tunnel_info_bridge; 6 | mod tunnel_message; 7 | mod udp; 8 | mod util; 9 | 10 | use anyhow::{Context, Result}; 11 | use byte_pool::BytePool; 12 | pub use client::Client; 13 | pub use client::ClientState; 14 | use lazy_static::lazy_static; 15 | use log::warn; 16 | use rs_utilities::log_and_bail; 17 | use rustls::crypto::ring::cipher_suite; 18 | use serde::Deserialize; 19 | use serde::Serialize; 20 | pub use server::Server; 21 | use std::fmt::Display; 22 | use std::net::IpAddr; 23 | use std::net::Ipv4Addr; 24 | use std::net::Ipv6Addr; 25 | use std::{net::SocketAddr, ops::Deref}; 26 | pub use tcp::tcp_server::TcpServer; 27 | pub use tcp::{AsyncStream, StreamMessage, StreamReceiver, StreamRequest, StreamSender}; 28 | use tunnel_message::LoginInfo; 29 | use udp::udp_server::UdpServer; 30 | pub use udp::{UdpMessage, UdpPacket, UdpReceiver, UdpSender}; 31 | 32 | extern crate bincode; 33 | extern crate pretty_env_logger; 34 | 35 | pub const TUNNEL_MODE_IN: &str = "IN"; 36 | pub const TUNNEL_MODE_OUT: &str = "OUT"; 37 | pub const UDP_PACKET_SIZE: usize = 1500; 38 | 39 | lazy_static! { 40 | static ref BUFFER_POOL: BytePool::> = BytePool::>::new(); 41 | } 42 | 43 | pub const SUPPORTED_CIPHER_SUITE_STRS: &[&str] = &[ 44 | "chacha20-poly1305", 45 | "aes-256-gcm", 46 | "aes-128-gcm", 47 | // the following ciphers don't work at the moement, will look into it later 48 | // "ecdhe-ecdsa-aes256-gcm", 49 | // "ecdhe-ecdsa-aes128-gcm", 50 | // "ecdhe-ecdsa-chacha20-poly1305", 51 | // "ecdhe-rsa-aes256-gcm", 52 | // "ecdhe-rsa-aes128-gcm", 53 | // "ecdhe-rsa-chacha20-poly1305", 54 | ]; 55 | 56 | pub static SUPPORTED_CIPHER_SUITES: &[rustls::SupportedCipherSuite] = &[ 57 | cipher_suite::TLS13_CHACHA20_POLY1305_SHA256, 58 | cipher_suite::TLS13_AES_256_GCM_SHA384, 59 | cipher_suite::TLS13_AES_128_GCM_SHA256, 60 | ]; 61 | 62 | pub(crate) struct SelectedCipherSuite(rustls::SupportedCipherSuite); 63 | 64 | impl std::str::FromStr for SelectedCipherSuite { 65 | type Err = (); 66 | 67 | fn from_str(s: &str) -> Result { 68 | match s { 69 | "chacha20-poly1305" => Ok(SelectedCipherSuite( 70 | cipher_suite::TLS13_CHACHA20_POLY1305_SHA256, 71 | )), 72 | "aes-256-gcm" => Ok(SelectedCipherSuite(cipher_suite::TLS13_AES_256_GCM_SHA384)), 73 | "aes-128-gcm" => Ok(SelectedCipherSuite(cipher_suite::TLS13_AES_128_GCM_SHA256)), 74 | // "ecdhe-ecdsa-aes256-gcm" => Ok(SelectedCipherSuite( 75 | // rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 76 | // )), 77 | // "ecdhe-ecdsa-aes128-gcm" => Ok(SelectedCipherSuite( 78 | // rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 79 | // )), 80 | // "ecdhe-ecdsa-chacha20-poly1305" => Ok(SelectedCipherSuite( 81 | // rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 82 | // )), 83 | // "ecdhe-rsa-aes256-gcm" => Ok(SelectedCipherSuite( 84 | // rustls::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 85 | // )), 86 | // "ecdhe-rsa-aes128-gcm" => Ok(SelectedCipherSuite( 87 | // rustls::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 88 | // )), 89 | // "ecdhe-rsa-chacha20-poly1305" => Ok(SelectedCipherSuite( 90 | // rustls::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 91 | // )), 92 | _ => Ok(SelectedCipherSuite( 93 | cipher_suite::TLS13_CHACHA20_POLY1305_SHA256, 94 | )), 95 | } 96 | } 97 | } 98 | 99 | impl Deref for SelectedCipherSuite { 100 | type Target = rustls::SupportedCipherSuite; 101 | fn deref(&self) -> &Self::Target { 102 | &self.0 103 | } 104 | } 105 | 106 | #[derive(Debug)] 107 | pub struct TcpTunnelOutInfo { 108 | conn: quinn::Connection, 109 | upstream_addr: SocketAddr, 110 | } 111 | 112 | #[derive(Debug)] 113 | pub struct TcpTunnelInInfo { 114 | conn: quinn::Connection, 115 | tcp_server: TcpServer, 116 | } 117 | 118 | #[derive(Debug)] 119 | pub struct UdpTunnelOutInfo { 120 | conn: quinn::Connection, 121 | upstream_addr: SocketAddr, 122 | } 123 | 124 | #[derive(Debug)] 125 | pub struct UdpTunnelInInfo { 126 | conn: quinn::Connection, 127 | udp_server: UdpServer, 128 | } 129 | 130 | #[derive(Debug)] 131 | pub enum TunnelType { 132 | TcpOut(TcpTunnelOutInfo), 133 | TcpIn(TcpTunnelInInfo), 134 | UdpOut(UdpTunnelOutInfo), 135 | UdpIn(UdpTunnelInInfo), 136 | DynamicUpstreamTcpOut(quinn::Connection), 137 | DynamicUpstreamUdpOut(quinn::Connection), 138 | } 139 | 140 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 141 | pub enum TunnelMode { 142 | In, 143 | Out, 144 | } 145 | 146 | impl Display for TunnelMode { 147 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 148 | match self { 149 | Self::In => write!(f, "IN"), 150 | Self::Out => write!(f, "OUT"), 151 | } 152 | } 153 | } 154 | 155 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 156 | pub enum UpstreamType { 157 | Tcp, 158 | Udp, 159 | } 160 | 161 | impl Display for UpstreamType { 162 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 163 | match self { 164 | Self::Tcp => write!(f, "TCP"), 165 | Self::Udp => write!(f, "UDP"), 166 | } 167 | } 168 | } 169 | 170 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 171 | pub struct Upstream { 172 | pub upstream_addr: Option, 173 | pub upstream_type: UpstreamType, 174 | } 175 | 176 | impl Display for Upstream { 177 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 178 | match self.upstream_addr { 179 | Some(addr) => write!(f, "{}", addr), 180 | None => write!(f, "PeerDefault"), 181 | } 182 | } 183 | } 184 | 185 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 186 | pub struct TunnelConfig { 187 | pub mode: TunnelMode, 188 | pub local_server_addr: Option, 189 | pub upstream: Upstream, 190 | } 191 | 192 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 193 | pub(crate) enum Tunnel { 194 | NetworkBased(TunnelConfig), 195 | ChannelBased(UpstreamType), 196 | } 197 | 198 | #[derive(Debug, Default, Clone)] 199 | pub struct ClientConfig { 200 | pub cert_path: String, 201 | pub cipher: String, 202 | pub server_addr: String, 203 | pub password: String, 204 | pub wait_before_retry_ms: u64, 205 | pub quic_timeout_ms: u64, 206 | pub tcp_timeout_ms: u64, 207 | pub udp_timeout_ms: u64, 208 | pub hop_interval_ms: u64, 209 | pub tunnels: Vec, 210 | pub dot_servers: Vec, 211 | pub dns_servers: Vec, 212 | pub workers: usize, 213 | } 214 | 215 | #[derive(Debug, Clone)] 216 | pub struct ServerConfig { 217 | pub addr: String, 218 | pub password: String, 219 | pub cert_path: String, 220 | pub key_path: String, 221 | pub quic_timeout_ms: u64, 222 | pub tcp_timeout_ms: u64, 223 | pub udp_timeout_ms: u64, 224 | 225 | /// for TunnelOut only 226 | pub default_tcp_upstream: Option, 227 | pub default_udp_upstream: Option, 228 | 229 | /// 0.0.0.0:3515 230 | pub dashboard_server: String, 231 | /// user:password 232 | pub dashboard_server_credential: String, 233 | } 234 | 235 | impl ClientConfig { 236 | #[allow(clippy::too_many_arguments)] 237 | pub fn create( 238 | server_addr: &str, 239 | password: &str, 240 | cert: &str, 241 | cipher: &str, 242 | tcp_addr_mappings: &str, 243 | udp_addr_mappings: &str, 244 | dot: &str, 245 | dns: &str, 246 | workers: usize, 247 | wait_before_retry_ms: u64, 248 | mut quic_timeout_ms: u64, 249 | mut tcp_timeout_ms: u64, 250 | mut udp_timeout_ms: u64, 251 | mut hop_interval_ms: u64, 252 | ) -> Result { 253 | if tcp_addr_mappings.is_empty() && udp_addr_mappings.is_empty() { 254 | log_and_bail!("must specify either --tcp-mappings or --udp-mappings, or both"); 255 | } 256 | 257 | if quic_timeout_ms == 0 { 258 | quic_timeout_ms = 30000; 259 | } 260 | if tcp_timeout_ms == 0 { 261 | tcp_timeout_ms = 30000; 262 | } 263 | if udp_timeout_ms == 0 { 264 | udp_timeout_ms = 5000; 265 | } 266 | if hop_interval_ms != 0 && hop_interval_ms < 5000 { 267 | warn!("Endpoint migration interval: {hop_interval_ms} ms is too low and has been forcibly set to 5000 ms to prevent potential network failures due to excessive port or NAT resource exhaustion." 268 | ); 269 | hop_interval_ms = 5000; 270 | } 271 | 272 | let mut config = ClientConfig { 273 | cert_path: cert.to_string(), 274 | cipher: cipher.to_string(), 275 | server_addr: if !server_addr.contains(':') { 276 | format!("127.0.0.1:{server_addr}") 277 | } else { 278 | server_addr.to_string() 279 | }, 280 | password: password.to_string(), 281 | workers: if workers > 0 { 282 | workers 283 | } else { 284 | num_cpus::get() 285 | }, 286 | wait_before_retry_ms, 287 | quic_timeout_ms, 288 | tcp_timeout_ms, 289 | udp_timeout_ms, 290 | hop_interval_ms, 291 | dot_servers: dot.split(',').map(|s| s.to_string()).collect(), 292 | dns_servers: dns.split(',').map(|s| s.to_string()).collect(), 293 | ..ClientConfig::default() 294 | }; 295 | 296 | parse_addr_mappings(tcp_addr_mappings, UpstreamType::Tcp, &mut config.tunnels)?; 297 | parse_addr_mappings(udp_addr_mappings, UpstreamType::Udp, &mut config.tunnels)?; 298 | 299 | Ok(config) 300 | } 301 | } 302 | 303 | fn parse_addr_mappings( 304 | mappings: &str, 305 | upstream_type: UpstreamType, 306 | v: &mut Vec, 307 | ) -> Result<()> { 308 | if mappings.is_empty() { 309 | return Ok(()); 310 | } 311 | 312 | for mapping in mappings.split(',') { 313 | let parts: Vec<&str> = mapping.split('^').collect(); 314 | if parts.len() != 3 { 315 | log_and_bail!("Invalid mapping format, expected TYPE^SRC^DEST"); 316 | } 317 | 318 | let tunnel_mode = parts[0]; 319 | if tunnel_mode != "OUT" && tunnel_mode != "IN" { 320 | log_and_bail!("Invalid tunnel type, expected OUT or IN"); 321 | } 322 | 323 | let parse_addr = |addr: &str| -> Result> { 324 | if addr == "ANY" { 325 | return Ok(None); 326 | } 327 | 328 | // Handle port-only case 329 | let port = addr.parse::(); 330 | if let Ok(port) = port { 331 | return Ok(Some(SocketAddr::new( 332 | IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 333 | port, 334 | ))); 335 | } 336 | 337 | // Parse full SocketAddr 338 | Ok(Some(addr.parse().with_context(|| { 339 | format!("Invalid address format '{addr}', expected IP:PORT or PORT") 340 | })?)) 341 | }; 342 | 343 | let local_server_addr = parse_addr(parts[1])?; 344 | if local_server_addr.is_none() { 345 | log_and_bail!("'ANY' is not allowed as local_server_addr"); 346 | } 347 | let upstream_addr = parse_addr(parts[2])?; 348 | 349 | v.push(TunnelConfig { 350 | mode: if tunnel_mode == "IN" { 351 | TunnelMode::In 352 | } else { 353 | TunnelMode::Out 354 | }, 355 | upstream: Upstream { 356 | upstream_addr, 357 | upstream_type: upstream_type.clone(), 358 | }, 359 | local_server_addr, 360 | }); 361 | } 362 | 363 | Ok(()) 364 | } 365 | 366 | pub fn socket_addr_with_unspecified_ip_port(ipv6: bool) -> SocketAddr { 367 | if ipv6 { 368 | SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 0) 369 | } else { 370 | SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0) 371 | } 372 | } 373 | 374 | #[cfg(target_os = "android")] 375 | #[allow(non_snake_case)] 376 | pub mod android { 377 | extern crate jni; 378 | 379 | use jni::sys::{jlong, jstring}; 380 | use log::{error, info}; 381 | 382 | use self::jni::objects::{JClass, JObject, JString}; 383 | use self::jni::sys::{jboolean, jint, JNI_TRUE, JNI_VERSION_1_6}; 384 | use self::jni::{JNIEnv, JavaVM}; 385 | use super::*; 386 | use std::os::raw::c_void; 387 | use std::sync::{Arc, Mutex}; 388 | use std::thread; 389 | 390 | #[no_mangle] 391 | pub extern "system" fn JNI_OnLoad(_vm: JavaVM, _: *mut c_void) -> jint { 392 | JNI_VERSION_1_6 393 | } 394 | 395 | #[no_mangle] 396 | pub unsafe extern "C" fn Java_net_neevek_omnip_RsTunc_initCertificateVerifier( 397 | mut env: JNIEnv, 398 | _: JClass, 399 | context: JObject, 400 | ) { 401 | if let Err(e) = rustls_platform_verifier::android::init_hosted( 402 | &mut env, 403 | JObject::try_from(context).unwrap(), 404 | ) { 405 | error!("failed to init rustls_platform_verifier: {}", e); 406 | } else { 407 | info!("initializing rustls_platform_verifier succeeded!"); 408 | } 409 | } 410 | 411 | #[no_mangle] 412 | pub unsafe extern "C" fn Java_net_neevek_omnip_RsTunc_nativeInitLogger( 413 | mut env: JNIEnv, 414 | _: JClass, 415 | jlogLevel: JString, 416 | ) -> jboolean { 417 | let log_level = match convert_jstring(&mut env, jlogLevel).as_str() { 418 | "T" => "trace", 419 | "D" => "debug", 420 | "I" => "info", 421 | "W" => "warn", 422 | "E" => "error", 423 | _ => "info", 424 | }; 425 | let log_filter = format!("rstun={},rs_utilities={}", log_level, log_level); 426 | rs_utilities::LogHelper::init_logger("rstunc", log_filter.as_str()); 427 | return JNI_TRUE; 428 | } 429 | 430 | #[no_mangle] 431 | pub unsafe extern "C" fn Java_net_neevek_omnip_RsTunc_nativeCreate( 432 | mut env: JNIEnv, 433 | _: JClass, 434 | jserverAddr: JString, 435 | jtcpMappings: JString, 436 | judpMappings: JString, 437 | jdotServer: JString, 438 | jdnsServer: JString, 439 | jpassword: JString, 440 | jcertFilePath: JString, 441 | jcipher: JString, 442 | jworkers: jint, 443 | jwaitBeforeRetryMs: jint, 444 | jquicTimeoutMs: jint, 445 | jhopTimeoutMs: jint, 446 | ) -> jlong { 447 | let server_addr = convert_jstring(&mut env, jserverAddr); 448 | let tcp_mappings = convert_jstring(&mut env, jtcpMappings); 449 | let udp_mappings = convert_jstring(&mut env, judpMappings); 450 | let dot_server = convert_jstring(&mut env, jdotServer); 451 | let dns_server = convert_jstring(&mut env, jdnsServer); 452 | let password = convert_jstring(&mut env, jpassword); 453 | let cert_file_path = convert_jstring(&mut env, jcertFilePath); 454 | let cipher = convert_jstring(&mut env, jcipher); 455 | 456 | match ClientConfig::create( 457 | &server_addr, 458 | &password, 459 | &cert_file_path, 460 | &cipher, 461 | &tcp_mappings, 462 | &udp_mappings, 463 | &dot_server, 464 | &dns_server, 465 | jworkers as usize, 466 | jwaitBeforeRetryMs as u64, 467 | jquicTimeoutMs as u64, 468 | 0, // tcp_timeout_ms - use default 469 | 0, // udp_timeout_ms - use default 470 | jhopTimeoutMs as u64, 471 | ) { 472 | Ok(client_config) => { 473 | let client = Client::new(client_config); 474 | let client = Box::into_raw(Box::new(client)); 475 | client as jlong 476 | } 477 | Err(e) => { 478 | error!("failed to create client: {}", e); 479 | 0 480 | } 481 | } 482 | } 483 | 484 | #[no_mangle] 485 | pub unsafe extern "C" fn Java_net_neevek_omnip_RsTunc_nativeStop( 486 | _env: JNIEnv, 487 | _: JClass, 488 | client_ptr: jlong, 489 | ) { 490 | if client_ptr != 0 { 491 | let _boxed_client = Box::from_raw(client_ptr as *mut Client); 492 | } 493 | } 494 | 495 | #[no_mangle] 496 | pub unsafe extern "C" fn Java_net_neevek_omnip_RsTunc_nativeStartTunnelling( 497 | _env: JNIEnv, 498 | _: JClass, 499 | client_ptr: jlong, 500 | ) { 501 | if client_ptr == 0 { 502 | return; 503 | } 504 | 505 | thread::spawn(move || { 506 | let mut client = (&mut *(client_ptr as *mut Arc>)) 507 | .lock() 508 | .unwrap(); 509 | client.start_tunneling(); 510 | }); 511 | } 512 | 513 | #[no_mangle] 514 | pub unsafe extern "C" fn Java_net_neevek_omnip_RsTunc_nativeGetState( 515 | env: JNIEnv, 516 | _: JClass, 517 | client_ptr: jlong, 518 | ) -> jstring { 519 | if client_ptr == 0 { 520 | return env.new_string("").unwrap().into_raw(); 521 | } 522 | 523 | let client = (&mut *(client_ptr as *mut Arc>)) 524 | .lock() 525 | .unwrap(); 526 | env.new_string(client.get_state().to_string()) 527 | .unwrap() 528 | .into_raw() 529 | } 530 | 531 | #[no_mangle] 532 | pub unsafe extern "C" fn Java_net_neevek_omnip_RsTunc_nativeSetEnableOnInfoReport( 533 | env: JNIEnv, 534 | jobj: JClass, 535 | client_ptr: jlong, 536 | enable: jboolean, 537 | ) { 538 | if client_ptr == 0 { 539 | return; 540 | } 541 | 542 | let client = (&mut *(client_ptr as *mut Arc>)) 543 | .lock() 544 | .unwrap(); 545 | let bool_enable = enable == 1; 546 | if bool_enable && !client.has_on_info_listener() { 547 | let jvm = env.get_java_vm().unwrap(); 548 | let jobj_global_ref = env.new_global_ref(jobj).unwrap(); 549 | client.set_on_info_listener(move |data: &str| { 550 | let mut env = jvm.attach_current_thread().unwrap(); 551 | if let Ok(s) = env.new_string(data) { 552 | env.call_method( 553 | &jobj_global_ref, 554 | "onInfo", 555 | "(Ljava/lang/String;)V", 556 | &[(&s).into()], 557 | ) 558 | .unwrap(); 559 | } 560 | }); 561 | } 562 | 563 | client.set_enable_on_info_report(bool_enable); 564 | } 565 | 566 | fn convert_jstring(env: &mut JNIEnv, jstr: JString) -> String { 567 | if !jstr.is_null() { 568 | env.get_string(&jstr).unwrap().to_string_lossy().to_string() 569 | } else { 570 | String::from("") 571 | } 572 | } 573 | } 574 | -------------------------------------------------------------------------------- /src/client.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | pem_util, socket_addr_with_unspecified_ip_port, 3 | tcp::{tcp_tunnel::TcpTunnel, AsyncStream, StreamReceiver, StreamRequest}, 4 | tunnel_info_bridge::{TunnelInfo, TunnelInfoBridge, TunnelInfoType, TunnelTraffic}, 5 | tunnel_message::TunnelMessage, 6 | udp::{udp_server::UdpServer, udp_tunnel::UdpTunnel, UdpReceiver, UdpSender}, 7 | ClientConfig, LoginInfo, SelectedCipherSuite, TcpServer, Tunnel, TunnelConfig, TunnelMode, 8 | UpstreamType, 9 | }; 10 | use anyhow::{bail, Context, Result}; 11 | use backon::ExponentialBuilder; 12 | use backon::Retryable; 13 | use log::{debug, error, info, warn}; 14 | use quinn::{congestion, crypto::rustls::QuicClientConfig, Connection, Endpoint, TransportConfig}; 15 | use quinn::{IdleTimeout, VarInt}; 16 | use rs_utilities::dns::{self, DNSQueryOrdering, DNSResolverConfig, DNSResolverLookupIpStrategy}; 17 | use rs_utilities::log_and_bail; 18 | use rustls::{ 19 | client::danger::ServerCertVerified, 20 | crypto::{ring::cipher_suite, CryptoProvider}, 21 | RootCertStore, SupportedCipherSuite, 22 | }; 23 | use rustls_platform_verifier::{self, BuilderVerifierExt}; 24 | use serde::Serialize; 25 | use std::collections::HashMap; 26 | use std::{ 27 | fmt::Display, 28 | net::{IpAddr, SocketAddr}, 29 | str::FromStr, 30 | sync::{Arc, Mutex, Once}, 31 | time::Duration, 32 | }; 33 | use tokio::net::TcpStream; 34 | 35 | const TIME_FORMAT: &str = "%Y-%m-%d %H:%M:%S.%3f"; 36 | const DEFAULT_SERVER_PORT: u16 = 3515; 37 | const POST_TRAFFIC_DATA_INTERVAL_SECS: u64 = 30; 38 | static INIT: Once = Once::new(); 39 | 40 | #[derive(Clone, Serialize, PartialEq)] 41 | pub enum ClientState { 42 | Idle = 0, 43 | Connecting, 44 | Connected, 45 | LoggingIn, 46 | Tunneling, 47 | Stopping, 48 | Terminated, 49 | } 50 | 51 | impl Display for ClientState { 52 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 53 | match self { 54 | ClientState::Idle => write!(f, "Idle"), 55 | ClientState::Connecting => write!(f, "Connecting"), 56 | ClientState::Connected => write!(f, "Connected"), 57 | ClientState::LoggingIn => write!(f, "LoggingIn"), 58 | ClientState::Tunneling => write!(f, "Tunneling"), 59 | ClientState::Stopping => write!(f, "Stopping"), 60 | ClientState::Terminated => write!(f, "Terminated"), 61 | } 62 | } 63 | } 64 | 65 | struct State { 66 | tcp_servers: HashMap, 67 | udp_servers: HashMap, 68 | endpoint: Option, 69 | connections: HashMap, 70 | client_state: ClientState, 71 | total_traffic_data: TunnelTraffic, 72 | tunnel_info_bridge: TunnelInfoBridge, 73 | on_info_report_enabled: bool, 74 | } 75 | 76 | impl State { 77 | fn new() -> Self { 78 | Self { 79 | tcp_servers: HashMap::new(), 80 | udp_servers: HashMap::new(), 81 | endpoint: None, 82 | connections: HashMap::new(), 83 | client_state: ClientState::Idle, 84 | total_traffic_data: TunnelTraffic::default(), 85 | tunnel_info_bridge: TunnelInfoBridge::new(), 86 | on_info_report_enabled: false, 87 | } 88 | } 89 | 90 | fn post_tunnel_info(&self, server_info: TunnelInfo) 91 | where 92 | T: ?Sized + Serialize, 93 | { 94 | if self.on_info_report_enabled { 95 | self.tunnel_info_bridge.post_tunnel_info(server_info); 96 | } 97 | } 98 | } 99 | 100 | struct LoginConfig { 101 | local_addr: SocketAddr, 102 | remote_addr: SocketAddr, 103 | quinn_client_cfg: quinn::ClientConfig, 104 | domain: String, 105 | } 106 | 107 | #[derive(Clone)] 108 | pub struct Client { 109 | config: ClientConfig, 110 | inner_state: Arc>, 111 | } 112 | 113 | macro_rules! inner_state { 114 | ($self:ident, $field:ident) => { 115 | (*$self.inner_state.lock().unwrap()).$field 116 | }; 117 | } 118 | 119 | impl Client { 120 | pub fn new(config: ClientConfig) -> Self { 121 | INIT.call_once(|| { 122 | rustls::crypto::ring::default_provider() 123 | .install_default() 124 | .unwrap(); 125 | }); 126 | 127 | Client { 128 | config, 129 | inner_state: Arc::new(Mutex::new(State::new())), 130 | } 131 | } 132 | 133 | pub fn start_tunneling(&mut self) { 134 | let (tx, rx) = std::sync::mpsc::channel(); 135 | ctrlc::set_handler(move || tx.send(()).expect("Could not send signal on channel.")) 136 | .expect("Error setting Ctrl-C handler"); 137 | 138 | tokio::runtime::Builder::new_multi_thread() 139 | .enable_all() 140 | .worker_threads(self.config.workers) 141 | .build() 142 | .unwrap() 143 | .block_on(async { 144 | self.connect_and_serve_async(); 145 | rx.recv().expect("Could not receive from channel."); 146 | self.stop_async().await; 147 | }); 148 | } 149 | 150 | pub fn connect_and_serve_async(&mut self) { 151 | for (index, tunnel_config) in self.config.tunnels.iter().cloned().enumerate() { 152 | let mut this = self.clone(); 153 | tokio::spawn(async move { 154 | this.connect_and_serve::( 155 | index, 156 | Tunnel::NetworkBased(tunnel_config), 157 | None, 158 | None, 159 | ) 160 | .await; 161 | }); 162 | } 163 | 164 | self.report_traffic_data_in_background(); 165 | if self.config.hop_interval_ms > 0 { 166 | self.start_migration_task(); 167 | } 168 | } 169 | 170 | pub fn connect_and_serve_tcp_async( 171 | &mut self, 172 | stream_receiver: StreamReceiver, 173 | ) { 174 | let mut this = self.clone(); 175 | tokio::spawn(async move { 176 | this.connect_and_serve::( 177 | 0, 178 | Tunnel::ChannelBased(UpstreamType::Tcp), 179 | Some(stream_receiver), 180 | None, 181 | ) 182 | .await; 183 | }); 184 | } 185 | 186 | pub fn connect_and_serve_udp_async(&mut self, ch: (UdpSender, UdpReceiver)) { 187 | let mut this = self.clone(); 188 | tokio::spawn(async move { 189 | this.connect_and_serve::( 190 | 0, 191 | Tunnel::ChannelBased(UpstreamType::Udp), 192 | None, 193 | Some(ch), 194 | ) 195 | .await; 196 | }); 197 | } 198 | 199 | fn start_migration_task(&self) { 200 | let state = self.inner_state.clone(); 201 | let hop_interval = self.config.hop_interval_ms; 202 | 203 | tokio::spawn(async move { 204 | let mut interval = tokio::time::interval(Duration::from_millis(hop_interval)); 205 | interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip); 206 | interval.tick().await; 207 | 208 | loop { 209 | interval.tick().await; 210 | 211 | let endpoint = { state.lock().unwrap().endpoint.clone() }; 212 | if let Some(endpoint) = endpoint { 213 | Self::migrate_endpoint(&endpoint).await.ok(); 214 | } 215 | } 216 | }); 217 | } 218 | 219 | async fn migrate_endpoint(endpoint: &Endpoint) -> Result<()> { 220 | let current_addr = endpoint.local_addr()?; 221 | let new_addr = socket_addr_with_unspecified_ip_port(current_addr.is_ipv6()); 222 | let socket = std::net::UdpSocket::bind(new_addr)?; 223 | debug!( 224 | "endpoint will migrated from {} to {}", 225 | current_addr, 226 | socket.local_addr()? 227 | ); 228 | endpoint.rebind(socket)?; 229 | Ok(()) 230 | } 231 | 232 | pub async fn start_tcp_server(&self, addr: SocketAddr) -> Result { 233 | let bind_tcp_server = || async { TcpServer::bind_and_start(addr).await }; 234 | let tcp_server = bind_tcp_server 235 | .retry( 236 | ExponentialBuilder::default() 237 | .with_max_delay(Duration::from_secs(10)) 238 | .with_max_times(10), 239 | ) 240 | .sleep(tokio::time::sleep) 241 | .notify(|err: &anyhow::Error, dur: Duration| { 242 | warn!("will start tcp server ({addr}) after {dur:?}, err: {err:?}"); 243 | }) 244 | .await?; 245 | 246 | inner_state!(self, tcp_servers).insert(addr, tcp_server.clone()); 247 | 248 | Ok(tcp_server) 249 | } 250 | 251 | pub async fn start_udp_server(&self, addr: SocketAddr) -> Result { 252 | // create a local udp server for 'OUT' tunnel 253 | let bind_udp_server = || async { UdpServer::bind_and_start(addr).await }; 254 | let udp_server = bind_udp_server 255 | .retry( 256 | ExponentialBuilder::default() 257 | .with_max_delay(Duration::from_secs(10)) 258 | .with_max_times(10), 259 | ) 260 | .sleep(tokio::time::sleep) 261 | .notify(|err: &anyhow::Error, dur: Duration| { 262 | warn!("will start udp server ({addr}) after {dur:?}, err: {err:?}"); 263 | }) 264 | .await?; 265 | 266 | inner_state!(self, udp_servers).insert(addr, udp_server.clone()); 267 | Ok(udp_server) 268 | } 269 | 270 | pub fn get_config(&self) -> ClientConfig { 271 | self.config.clone() 272 | } 273 | 274 | #[allow(clippy::unnecessary_to_owned)] 275 | pub fn stop(&self) { 276 | self.set_and_post_tunnel_state(ClientState::Stopping); 277 | 278 | if let Ok(mut state) = self.inner_state.lock() { 279 | for mut s in state.tcp_servers.values().cloned() { 280 | tokio::spawn(async move { 281 | s.shutdown().await.ok(); 282 | }); 283 | } 284 | for mut s in state.udp_servers.values().cloned() { 285 | tokio::spawn(async move { 286 | s.shutdown().await.ok(); 287 | }); 288 | } 289 | 290 | for c in state.connections.values().cloned() { 291 | tokio::spawn(async move { 292 | c.close(VarInt::from_u32(1), b""); 293 | }); 294 | } 295 | 296 | state.tcp_servers.clear(); 297 | state.udp_servers.clear(); 298 | state.connections.clear(); 299 | } 300 | 301 | std::thread::sleep(Duration::from_secs(3)); 302 | } 303 | 304 | #[allow(clippy::unnecessary_to_owned)] 305 | pub async fn stop_async(&self) { 306 | self.set_and_post_tunnel_state(ClientState::Stopping); 307 | 308 | let mut tasks = tokio::task::JoinSet::new(); 309 | if let Ok(mut state) = self.inner_state.lock() { 310 | for mut s in state.tcp_servers.values().cloned() { 311 | tasks.spawn(async move { 312 | s.shutdown().await.ok(); 313 | }); 314 | } 315 | for mut s in state.udp_servers.values().cloned() { 316 | tasks.spawn(async move { 317 | s.shutdown().await.ok(); 318 | }); 319 | } 320 | 321 | for c in state.connections.values().cloned() { 322 | tasks.spawn(async move { 323 | c.close(VarInt::from_u32(1), b""); 324 | }); 325 | } 326 | 327 | state.tcp_servers.clear(); 328 | state.udp_servers.clear(); 329 | state.connections.clear(); 330 | } 331 | 332 | while tasks.join_next().await.is_some() {} 333 | } 334 | 335 | async fn connect_and_serve( 336 | &mut self, 337 | index: usize, 338 | tunnel: Tunnel, 339 | mut stream_receiver: Option>, 340 | mut ch: Option<(UdpSender, UdpReceiver)>, 341 | ) { 342 | let login_info = LoginInfo { 343 | password: self.config.password.clone(), 344 | tunnel: tunnel.clone(), 345 | }; 346 | 347 | let mut pending_network_based_stream = None; 348 | let mut pending_channel_based_stream = None; 349 | loop { 350 | let connect = || async { 351 | let login_cfg = self.prepare_login_config().await?; 352 | let endpoint = { self.inner_state.lock().unwrap().endpoint.clone() }; 353 | let endpoint = if let Some(endpoint) = endpoint { 354 | Self::migrate_endpoint(&endpoint).await?; 355 | endpoint 356 | } else { 357 | let mut endpoint = quinn::Endpoint::client(login_cfg.local_addr)?; 358 | endpoint.set_default_client_config(login_cfg.quinn_client_cfg); 359 | inner_state!(self, endpoint) = Some(endpoint.clone()); 360 | endpoint 361 | }; 362 | 363 | let conn = self 364 | .login( 365 | index, 366 | &endpoint, 367 | &login_info, 368 | &login_cfg.remote_addr, 369 | login_cfg.domain.as_str(), 370 | ) 371 | .await?; 372 | 373 | Ok(conn) 374 | }; 375 | let result = connect 376 | .retry( 377 | ExponentialBuilder::default() 378 | .with_max_delay(Duration::from_secs(10)) 379 | .with_max_times(usize::MAX), 380 | ) 381 | .when(|_| !self.should_quit()) 382 | .sleep(tokio::time::sleep) 383 | .notify(|err: &anyhow::Error, dur: Duration| { 384 | warn!("will retry after {dur:?}, err: {err:?}"); 385 | }) 386 | .await; 387 | 388 | if self.should_quit() { 389 | break; 390 | } 391 | 392 | match result { 393 | Ok(conn) => match &tunnel { 394 | Tunnel::NetworkBased(tunnel_config) => { 395 | let local_server_addr = tunnel_config.local_server_addr.unwrap(); 396 | inner_state!(self, connections).insert(local_server_addr, conn.clone()); 397 | 398 | self.handle_network_based_tunnel( 399 | index, 400 | conn.clone(), 401 | tunnel_config, 402 | &mut pending_network_based_stream, 403 | ) 404 | .await; 405 | 406 | inner_state!(self, connections).remove(&local_server_addr); 407 | } 408 | Tunnel::ChannelBased(upstream_type) => match upstream_type { 409 | UpstreamType::Tcp => { 410 | self.post_tunnel_log( 411 | format!( 412 | "{index}:STREAM_OUT start serving via {}", 413 | conn.remote_address() 414 | ) 415 | .as_str(), 416 | ); 417 | self.set_and_post_tunnel_state(ClientState::Tunneling); 418 | 419 | let stream_receiver = stream_receiver.as_mut().unwrap(); 420 | TcpTunnel::start_serving( 421 | true, 422 | &conn, 423 | stream_receiver, 424 | &mut pending_channel_based_stream, 425 | self.config.tcp_timeout_ms, 426 | ) 427 | .await; 428 | } 429 | 430 | UpstreamType::Udp => { 431 | self.post_tunnel_log( 432 | format!( 433 | "{index}:UDP_OUT start serving via {}", 434 | conn.remote_address() 435 | ) 436 | .as_str(), 437 | ); 438 | self.set_and_post_tunnel_state(ClientState::Tunneling); 439 | 440 | let ch = ch.as_mut().unwrap(); 441 | UdpTunnel::start_serving( 442 | &conn, 443 | &ch.0, 444 | &mut ch.1, 445 | self.config.udp_timeout_ms, 446 | ) 447 | .await; 448 | } 449 | }, 450 | }, 451 | 452 | Err(e) => { 453 | error!("{e}"); 454 | info!( 455 | "[{login_info}] quit after having retried for {} times", 456 | usize::MAX 457 | ); 458 | break; 459 | } 460 | }; 461 | 462 | if self.should_quit() { 463 | break; 464 | } 465 | } 466 | self.post_tunnel_log(format!("[{login_info}] quit").as_str()); 467 | } 468 | 469 | async fn handle_network_based_tunnel( 470 | &mut self, 471 | index: usize, 472 | conn: Connection, 473 | tunnel_config: &TunnelConfig, 474 | pending_request: &mut Option>, 475 | ) { 476 | let upstream_type = &tunnel_config.upstream.upstream_type; 477 | let local_server_addr = tunnel_config.local_server_addr.unwrap(); 478 | 479 | if tunnel_config.mode == TunnelMode::Out { 480 | match upstream_type { 481 | UpstreamType::Tcp => { 482 | self.serve_outbound_tcp( 483 | index, 484 | conn.clone(), 485 | local_server_addr, 486 | pending_request, 487 | ) 488 | .await 489 | .ok(); 490 | } 491 | UpstreamType::Udp => { 492 | self.serve_outbound_udp(index, conn.clone(), local_server_addr) 493 | .await 494 | .ok(); 495 | } 496 | } 497 | } else { 498 | match upstream_type { 499 | UpstreamType::Tcp => { 500 | self.serve_inbound_tcp(index, conn.clone(), local_server_addr) 501 | .await 502 | .ok(); 503 | } 504 | UpstreamType::Udp => { 505 | self.serve_inbound_udp(index, conn.clone(), local_server_addr) 506 | .await 507 | .ok(); 508 | } 509 | } 510 | } 511 | 512 | let stats = conn.stats(); 513 | let data = &mut inner_state!(self, total_traffic_data); 514 | data.rx_bytes += stats.udp_rx.bytes; 515 | data.tx_bytes += stats.udp_tx.bytes; 516 | data.rx_dgrams += stats.udp_rx.datagrams; 517 | data.tx_dgrams += stats.udp_tx.datagrams; 518 | } 519 | 520 | async fn prepare_login_config(&self) -> Result { 521 | let mut transport_cfg = TransportConfig::default(); 522 | transport_cfg.stream_receive_window(quinn::VarInt::from_u32(1024 * 1024)); 523 | transport_cfg.receive_window(quinn::VarInt::from_u32(1024 * 1024 * 2)); 524 | transport_cfg.send_window(1024 * 1024 * 2); 525 | transport_cfg.congestion_controller_factory(Arc::new(congestion::BbrConfig::default())); 526 | transport_cfg.max_concurrent_bidi_streams(VarInt::from_u32(1024)); 527 | 528 | if self.config.quic_timeout_ms > 0 { 529 | let timeout = IdleTimeout::from(VarInt::from_u32(self.config.quic_timeout_ms as u32)); 530 | transport_cfg.max_idle_timeout(Some(timeout)); 531 | transport_cfg.keep_alive_interval(Some(Duration::from_millis( 532 | self.config.quic_timeout_ms * 2 / 3, 533 | ))); 534 | } 535 | 536 | let (tls_client_cfg, domain) = self.parse_client_config_and_domain()?; 537 | let quic_client_cfg = Arc::new(QuicClientConfig::try_from(tls_client_cfg)?); 538 | let mut client_cfg = quinn::ClientConfig::new(quic_client_cfg); 539 | client_cfg.transport_config(Arc::new(transport_cfg)); 540 | 541 | let remote_addr = self.parse_server_addr().await?; 542 | let local_addr = socket_addr_with_unspecified_ip_port(remote_addr.is_ipv6()); 543 | Ok(LoginConfig { 544 | local_addr, 545 | remote_addr, 546 | quinn_client_cfg: client_cfg, 547 | domain, 548 | }) 549 | } 550 | 551 | async fn login( 552 | &self, 553 | index: usize, 554 | endpoint: &Endpoint, 555 | login_info: &LoginInfo, 556 | remote_addr: &SocketAddr, 557 | domain: &str, 558 | ) -> Result { 559 | self.set_and_post_tunnel_state(ClientState::Connecting); 560 | self.post_tunnel_log( 561 | format!( 562 | "{index}:{} connecting, idle_timeout:{}, retry_timeout:{}, cipher:{}, threads:{}", 563 | login_info.format_with_remote_addr(remote_addr), 564 | self.config.quic_timeout_ms, 565 | self.config.wait_before_retry_ms, 566 | self.config.cipher, 567 | self.config.workers, 568 | ) 569 | .as_str(), 570 | ); 571 | 572 | let conn = endpoint.connect(*remote_addr, domain)?.await?; 573 | let (mut quic_send, mut quic_recv) = conn 574 | .open_bi() 575 | .await 576 | .context("open bidirectional connection failed")?; 577 | 578 | self.set_and_post_tunnel_state(ClientState::Connected); 579 | 580 | self.post_tunnel_log( 581 | format!( 582 | "{index}:{} logging in...", 583 | login_info.format_with_remote_addr(remote_addr) 584 | ) 585 | .as_str(), 586 | ); 587 | 588 | let login_msg = TunnelMessage::ReqLogin(login_info.clone()); 589 | TunnelMessage::send(&mut quic_send, &login_msg).await?; 590 | 591 | let resp = TunnelMessage::recv(&mut quic_recv).await?; 592 | if let TunnelMessage::RespFailure(msg) = resp { 593 | bail!( 594 | "{index}:{} failed to login: {msg}", 595 | login_info.format_with_remote_addr(remote_addr) 596 | ); 597 | } 598 | if !resp.is_resp_success() { 599 | bail!( 600 | "{index}:{} unexpected response, failed to login", 601 | login_info.format_with_remote_addr(remote_addr) 602 | ); 603 | } 604 | TunnelMessage::handle_message(&resp)?; 605 | self.post_tunnel_log( 606 | format!( 607 | "{index}:{} login succeeded!", 608 | login_info.format_with_remote_addr(remote_addr) 609 | ) 610 | .as_str(), 611 | ); 612 | Ok(conn) 613 | } 614 | 615 | async fn serve_outbound_tcp( 616 | &mut self, 617 | index: usize, 618 | conn: Connection, 619 | local_server_addr: SocketAddr, 620 | pending_request: &mut Option>, 621 | ) -> Result<()> { 622 | let tcp_server = { 623 | inner_state!(self, tcp_servers) 624 | .get(&local_server_addr) 625 | .cloned() 626 | }; 627 | 628 | let mut tcp_server = match tcp_server { 629 | Some(server) => server.clone(), 630 | None => self.start_tcp_server(local_server_addr).await?, 631 | }; 632 | 633 | self.post_tunnel_log( 634 | format!( 635 | "{index}:TCP_OUT start serving from {} via {}", 636 | tcp_server.addr(), 637 | conn.remote_address() 638 | ) 639 | .as_str(), 640 | ); 641 | self.set_and_post_tunnel_state(ClientState::Tunneling); 642 | 643 | let mut tcp_receiver = tcp_server.take_receiver(); 644 | 645 | TcpTunnel::start_serving( 646 | true, 647 | &conn, 648 | &mut tcp_receiver, 649 | pending_request, 650 | self.config.tcp_timeout_ms, 651 | ) 652 | .await; 653 | 654 | tcp_server.put_receiver(tcp_receiver); 655 | 656 | Ok(()) 657 | } 658 | 659 | async fn serve_outbound_udp( 660 | &mut self, 661 | index: usize, 662 | conn: Connection, 663 | local_server_addr: SocketAddr, 664 | ) -> Result<()> { 665 | let udp_server = { 666 | inner_state!(self, udp_servers) 667 | .get(&local_server_addr) 668 | .cloned() 669 | }; 670 | 671 | let mut udp_server = match udp_server { 672 | Some(server) => server.clone(), 673 | None => self.start_udp_server(local_server_addr).await?, 674 | }; 675 | 676 | self.post_tunnel_log( 677 | format!( 678 | "{index}:UDP_OUT start serving from {} via {}", 679 | udp_server.addr(), 680 | conn.remote_address() 681 | ) 682 | .as_str(), 683 | ); 684 | 685 | self.set_and_post_tunnel_state(ClientState::Tunneling); 686 | 687 | let mut udp_receiver = udp_server.take_receiver(); 688 | let udp_sender = udp_server.clone_sender(); 689 | 690 | UdpTunnel::start_serving( 691 | &conn, 692 | &udp_sender, 693 | &mut udp_receiver, 694 | self.config.udp_timeout_ms, 695 | ) 696 | .await; 697 | 698 | udp_server.put_receiver(udp_receiver); 699 | 700 | Ok(()) 701 | } 702 | 703 | async fn serve_inbound_tcp( 704 | &mut self, 705 | index: usize, 706 | conn: Connection, 707 | local_server_addr: SocketAddr, 708 | ) -> Result<()> { 709 | self.post_tunnel_log( 710 | format!( 711 | "{index}:TCP_IN start serving via: {}", 712 | conn.remote_address() 713 | ) 714 | .as_str(), 715 | ); 716 | 717 | self.set_and_post_tunnel_state(ClientState::Tunneling); 718 | TcpTunnel::start_accepting(&conn, Some(local_server_addr), self.config.tcp_timeout_ms) 719 | .await; 720 | 721 | Ok(()) 722 | } 723 | 724 | async fn serve_inbound_udp( 725 | &mut self, 726 | index: usize, 727 | conn: Connection, 728 | local_server_addr: SocketAddr, 729 | ) -> Result<()> { 730 | self.post_tunnel_log( 731 | format!( 732 | "{index}:UDP_IN start serving via: {}", 733 | conn.remote_address() 734 | ) 735 | .as_str(), 736 | ); 737 | 738 | self.set_and_post_tunnel_state(ClientState::Tunneling); 739 | UdpTunnel::start_accepting(&conn, Some(local_server_addr), self.config.udp_timeout_ms) 740 | .await; 741 | 742 | Ok(()) 743 | } 744 | 745 | fn should_quit(&self) -> bool { 746 | let state = self.get_state(); 747 | state == ClientState::Stopping || state == ClientState::Terminated 748 | } 749 | 750 | fn report_traffic_data_in_background(&self) { 751 | let state = self.inner_state.clone(); 752 | tokio::spawn(async move { 753 | let mut interval = 754 | tokio::time::interval(Duration::from_secs(POST_TRAFFIC_DATA_INTERVAL_SECS)); 755 | interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip); 756 | 757 | loop { 758 | interval.tick().await; 759 | 760 | let mut rx_bytes = 0; 761 | let mut tx_bytes = 0; 762 | let mut rx_dgrams = 0; 763 | let mut tx_dgrams = 0; 764 | 765 | { 766 | let connections = &state.lock().unwrap().connections; 767 | for conn in connections.values() { 768 | let stats = conn.stats(); 769 | rx_bytes += stats.udp_rx.bytes; 770 | tx_bytes += stats.udp_tx.bytes; 771 | rx_dgrams += stats.udp_rx.datagrams; 772 | tx_dgrams += stats.udp_tx.datagrams; 773 | } 774 | } 775 | 776 | { 777 | let total_traffic_data = &&state.lock().unwrap().total_traffic_data; 778 | rx_bytes += total_traffic_data.rx_bytes; 779 | tx_bytes += total_traffic_data.tx_bytes; 780 | rx_dgrams += total_traffic_data.rx_dgrams; 781 | tx_dgrams += total_traffic_data.tx_dgrams; 782 | } 783 | 784 | let state = state.lock().unwrap(); 785 | let client_state = state.client_state.clone(); 786 | let data = TunnelTraffic { 787 | rx_bytes, 788 | tx_bytes, 789 | rx_dgrams, 790 | tx_dgrams, 791 | }; 792 | 793 | info!("traffic log, rx_bytes:{rx_bytes}, tx_bytes:{tx_bytes}, rx_dgrams:{rx_dgrams}, tx_dgrams:{tx_dgrams}"); 794 | state.post_tunnel_info(TunnelInfo::new( 795 | TunnelInfoType::TunnelTraffic, 796 | Box::new(data), 797 | )); 798 | 799 | if client_state == ClientState::Stopping || client_state == ClientState::Terminated 800 | { 801 | break; 802 | } 803 | } 804 | }); 805 | } 806 | 807 | fn get_crypto_provider(&self, cipher: &SupportedCipherSuite) -> Arc { 808 | let default_provider = rustls::crypto::ring::default_provider(); 809 | let mut cipher_suites = vec![*cipher]; 810 | // Quinn assumes that the cipher suites contain this one 811 | cipher_suites.push(cipher_suite::TLS13_AES_128_GCM_SHA256); 812 | Arc::new(rustls::crypto::CryptoProvider { 813 | cipher_suites, 814 | ..default_provider 815 | }) 816 | } 817 | 818 | fn create_client_config_builder( 819 | &self, 820 | cipher: &SupportedCipherSuite, 821 | ) -> std::result::Result< 822 | rustls::ConfigBuilder, 823 | rustls::Error, 824 | > { 825 | let cfg_builder = 826 | rustls::ClientConfig::builder_with_provider(self.get_crypto_provider(cipher)) 827 | .with_protocol_versions(&[&rustls::version::TLS13]) 828 | .unwrap(); 829 | Ok(cfg_builder) 830 | } 831 | 832 | fn parse_client_config_and_domain(&self) -> Result<(rustls::ClientConfig, String)> { 833 | let cipher = *SelectedCipherSuite::from_str(&self.config.cipher).map_err(|_| { 834 | rustls::Error::General(format!("invalid cipher: {}", self.config.cipher)) 835 | })?; 836 | 837 | if self.config.cert_path.is_empty() { 838 | if !Self::is_ip_addr(&self.config.server_addr) { 839 | let domain = match self.config.server_addr.rfind(':') { 840 | Some(colon_index) => self.config.server_addr[0..colon_index].to_string(), 841 | None => self.config.server_addr.to_string(), 842 | }; 843 | 844 | let client_config = self 845 | .create_client_config_builder(&cipher)? 846 | .with_platform_verifier()? 847 | .with_no_client_auth(); 848 | 849 | return Ok((client_config, domain)); 850 | } 851 | 852 | let client_config = self 853 | .create_client_config_builder(&cipher)? 854 | .dangerous() 855 | .with_custom_certificate_verifier(Arc::new(InsecureCertVerifier::new( 856 | self.get_crypto_provider(&cipher), 857 | ))) 858 | .with_no_client_auth(); 859 | 860 | static ONCE: Once = Once::new(); 861 | ONCE.call_once(|| { 862 | warn!( 863 | "No certificate is provided for verification, domain \"localhost\" is assumed" 864 | ); 865 | }); 866 | return Ok((client_config, "localhost".to_string())); 867 | } 868 | 869 | // when client config provides a certificate 870 | let certs = pem_util::load_certificates_from_pem(self.config.cert_path.as_str()) 871 | .context("failed to read from cert file")?; 872 | if certs.is_empty() { 873 | log_and_bail!( 874 | "No certificates found in provided file: {}", 875 | self.config.cert_path 876 | ); 877 | } 878 | let mut roots = RootCertStore::empty(); 879 | // save all certificates in the certificate chain to the trust list 880 | for cert in &certs { 881 | roots.add(cert.clone()).context(format!( 882 | "failed to add certificate from file: {}", 883 | self.config.cert_path 884 | ))?; 885 | } 886 | 887 | // for self-signed certificates, generating IP-based TLS certificates is not difficult 888 | let domain_or_ip = match self.config.server_addr.rfind(':') { 889 | Some(colon_index) => self.config.server_addr[0..colon_index].to_string(), 890 | None => self.config.server_addr.to_string(), 891 | }; 892 | 893 | Ok(( 894 | self.create_client_config_builder(&cipher)? 895 | .with_root_certificates(roots) 896 | .with_no_client_auth(), 897 | domain_or_ip, 898 | )) 899 | } 900 | 901 | pub fn get_state(&self) -> ClientState { 902 | inner_state!(self, client_state).clone() 903 | } 904 | 905 | fn is_ip_addr(addr: &str) -> bool { 906 | addr.parse::().is_ok() 907 | } 908 | 909 | async fn parse_server_addr(&self) -> Result { 910 | let addr = self.config.server_addr.as_str(); 911 | let sock_addr: Result = addr.parse().context("error will be ignored"); 912 | 913 | if sock_addr.is_ok() { 914 | return sock_addr; 915 | } 916 | 917 | let mut domain = addr; 918 | let mut port = DEFAULT_SERVER_PORT; 919 | let pos = addr.rfind(':'); 920 | if let Some(pos) = pos { 921 | port = addr[(pos + 1)..] 922 | .parse() 923 | .with_context(|| format!("invalid address: {}", addr))?; 924 | domain = &addr[..pos]; 925 | } 926 | 927 | for dot in &self.config.dot_servers { 928 | if let Ok(ip) = Self::lookup_server_ip(domain, dot, vec![]).await { 929 | return Ok(SocketAddr::new(ip, port)); 930 | } 931 | } 932 | 933 | if let Ok(ip) = Self::lookup_server_ip(domain, "", self.config.dns_servers.clone()).await { 934 | return Ok(SocketAddr::new(ip, port)); 935 | } 936 | 937 | if let Ok(ip) = Self::lookup_server_ip(domain, "", vec![]).await { 938 | return Ok(SocketAddr::new(ip, port)); 939 | } 940 | 941 | bail!("failed to resolve domain: {domain}"); 942 | } 943 | 944 | async fn lookup_server_ip( 945 | domain: &str, 946 | dot_server: &str, 947 | name_servers: Vec, 948 | ) -> Result { 949 | let dns_config = DNSResolverConfig { 950 | strategy: DNSResolverLookupIpStrategy::Ipv6thenIpv4, 951 | num_conccurent_reqs: 3, 952 | ordering: DNSQueryOrdering::QueryStatistics, 953 | }; 954 | 955 | let resolver = if !dot_server.is_empty() { 956 | dns::resolver2(dot_server, vec![], dns_config) 957 | } else if !name_servers.is_empty() { 958 | dns::resolver2("", name_servers, dns_config) 959 | } else { 960 | dns::resolver2("", vec![], dns_config) 961 | }; 962 | 963 | let ip = resolver.await.lookup_first(domain).await?; 964 | info!("resolved {domain} to {ip}"); 965 | Ok(ip) 966 | } 967 | 968 | fn post_tunnel_log(&self, msg: &str) { 969 | info!("{msg}"); 970 | let state = self.inner_state.lock().unwrap(); 971 | state.post_tunnel_info(TunnelInfo::new( 972 | TunnelInfoType::TunnelLog, 973 | Box::new(format!( 974 | "{} {msg}", 975 | chrono::Local::now().format(TIME_FORMAT) 976 | )), 977 | )); 978 | } 979 | 980 | fn set_and_post_tunnel_state(&self, client_state: ClientState) { 981 | let mut state = self.inner_state.lock().unwrap(); 982 | state.client_state = client_state.clone(); 983 | state.post_tunnel_info(TunnelInfo::new( 984 | TunnelInfoType::TunnelState, 985 | Box::new(client_state), 986 | )); 987 | } 988 | 989 | pub fn set_on_info_listener(&self, callback: impl FnMut(&str) + 'static + Send + Sync) { 990 | inner_state!(self, tunnel_info_bridge).set_listener(callback); 991 | } 992 | 993 | pub fn has_on_info_listener(&self) -> bool { 994 | inner_state!(self, tunnel_info_bridge).has_listener() 995 | } 996 | 997 | pub fn set_enable_on_info_report(&self, enable: bool) { 998 | info!("set_enable_on_info_report, enable:{enable}"); 999 | inner_state!(self, on_info_report_enabled) = enable; 1000 | } 1001 | } 1002 | 1003 | #[derive(Debug)] 1004 | struct InsecureCertVerifier(Arc); 1005 | 1006 | impl InsecureCertVerifier { 1007 | pub fn new(crypto: Arc) -> Self { 1008 | Self(crypto) 1009 | } 1010 | } 1011 | 1012 | impl rustls::client::danger::ServerCertVerifier for InsecureCertVerifier { 1013 | fn verify_tls12_signature( 1014 | &self, 1015 | message: &[u8], 1016 | cert: &rustls::pki_types::CertificateDer<'_>, 1017 | dss: &rustls::DigitallySignedStruct, 1018 | ) -> std::prelude::v1::Result 1019 | { 1020 | rustls::crypto::verify_tls12_signature( 1021 | message, 1022 | cert, 1023 | dss, 1024 | &self.0.signature_verification_algorithms, 1025 | ) 1026 | } 1027 | 1028 | fn verify_tls13_signature( 1029 | &self, 1030 | message: &[u8], 1031 | cert: &rustls::pki_types::CertificateDer<'_>, 1032 | dss: &rustls::DigitallySignedStruct, 1033 | ) -> std::prelude::v1::Result 1034 | { 1035 | rustls::crypto::verify_tls12_signature( 1036 | message, 1037 | cert, 1038 | dss, 1039 | &self.0.signature_verification_algorithms, 1040 | ) 1041 | } 1042 | 1043 | fn supported_verify_schemes(&self) -> Vec { 1044 | self.0.signature_verification_algorithms.supported_schemes() 1045 | } 1046 | 1047 | fn verify_server_cert( 1048 | &self, 1049 | _end_entity: &rustls::pki_types::CertificateDer<'_>, 1050 | _intermediates: &[rustls::pki_types::CertificateDer<'_>], 1051 | _server_name: &rustls::pki_types::ServerName<'_>, 1052 | _ocsp_response: &[u8], 1053 | _now: rustls::pki_types::UnixTime, 1054 | ) -> std::prelude::v1::Result { 1055 | static ONCE: Once = Once::new(); 1056 | ONCE.call_once(|| { 1057 | warn!("======================================= WARNING ======================================"); 1058 | warn!("Connecting to a server without verifying its certificate is DANGEROUS!!!"); 1059 | warn!("Provide the self-signed certificate for verification or connect with a domain name"); 1060 | warn!("======================= Be cautious, this is for TEST only!!! ========================"); 1061 | }); 1062 | Ok(ServerCertVerified::assertion()) 1063 | } 1064 | } 1065 | --------------------------------------------------------------------------------