diff --git a/Cargo.lock b/Cargo.lock index c6f0395f821f..21881600b76e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,6 +30,27 @@ version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dabe5a181f83789739c194cbe5a897dde195078fac08568d09221fd6137a7ba8" +[[package]] +name = "async-stream" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3670df70cbc01729f901f94c887814b3c68db038aad1329a418bae178bc5295c" +dependencies = [ + "async-stream-impl", + "futures-core", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3548b8efc9f8e8a5a0a2808c5bd8451a9031b9e5b879a79590304ae928b0a70" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-trait" version = "0.1.42" @@ -146,9 +167,9 @@ checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" [[package]] name = "bytes" -version = "0.6.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0dcbc35f504eb6fc275a6d20e4ebcda18cf50d40ba6fabff8c711fa16cb3b16" +checksum = "ad1f8e949d755f9d79112b5bb46938e0ef9d3804a0b16dfab13aafcaa5f0fa72" [[package]] name = "cc" @@ -272,9 +293,9 @@ dependencies = [ [[package]] name = "dtoa" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b" +checksum = "88d7ed2934d741c6b37e33e3832298e8850b53fd2d2bea03873375596c7cea4e" [[package]] name = "enum-as-inner" @@ -473,9 +494,10 @@ dependencies = [ [[package]] name = "h2" version = "0.3.0" -source = "git+https://github.com/hyperium/h2#dc3079ab89ca9fa7b79e014f5b2a835f30f4916b" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b67e66362108efccd8ac053abafc8b7a8d86a37e6e48fc4f6f7485eb5e9e6a5" dependencies = [ - "bytes 0.6.0", + "bytes 1.0.0", "fnv", "futures-core", "futures-sink", @@ -483,7 +505,7 @@ dependencies = [ "http", "indexmap", "slab", - "tokio", + "tokio 1.0.1", "tokio-util", "tracing", "tracing-futures", @@ -538,9 +560,10 @@ dependencies = [ [[package]] name = "http-body" version = "0.4.0" -source = "git+https://github.com/hyperium/http-body#5e434739e747c0b6611ec41020740b17f735d25a" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2861bd27ee074e5ee891e8b539837a9430012e249d7f0ca2d795650f579c1994" dependencies = [ - "bytes 0.6.0", + "bytes 1.0.0", "http", ] @@ -564,10 +587,11 @@ checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a" [[package]] name = "hyper" -version = "0.14.0-dev" -source = "git+https://github.com/hyperium/hyper.git#3b3077da1f891b09de18320d9f6ccf94f136943d" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b980c7bc75203b968f06374cbde00bf1818e02e156b8e5b6ccf440fb53b6d0" dependencies = [ - "bytes 0.6.0", + "bytes 1.0.0", "futures-channel", "futures-core", "futures-util", @@ -579,7 +603,7 @@ dependencies = [ "itoa", "pin-project 1.0.2", "socket2", - "tokio", + "tokio 1.0.1", "tower-service", "tracing", "want", @@ -644,9 +668,9 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] name = "jemalloc-sys" @@ -921,9 +945,9 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "openssl" -version = "0.10.31" +version = "0.10.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d008f51b1acffa0d3450a68606e6a51c123012edaacb0f4e1426bd978869187" +checksum = "038d43985d1ddca7a9900630d8cd031b56e4794eecc2e9ea39dd17aa04399a70" dependencies = [ "bitflags", "cfg-if 1.0.0", @@ -941,9 +965,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "openssl-sys" -version = "0.9.59" +version = "0.9.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de52d8eabd217311538a39bba130d7dea1f1e118010fee7a033d966845e7d5fe" +checksum = "921fc71883267538946025deffb622905ecad223c28efbfdef9bb59a0175f3e6" dependencies = [ "autocfg", "cc", @@ -1075,6 +1099,12 @@ dependencies = [ "syn", ] +[[package]] +name = "pin-project-lite" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" + [[package]] name = "pin-project-lite" version = "0.2.0" @@ -1448,7 +1478,7 @@ dependencies = [ "base64", "bloomfilter", "byte_string", - "bytes 0.6.0", + "bytes 1.0.0", "cfg-if 1.0.0", "env_logger", "futures", @@ -1465,7 +1495,7 @@ dependencies = [ "socket2", "spin 0.7.0", "thiserror", - "tokio", + "tokio 1.0.1", "trust-dns-resolver", "url", "winapi", @@ -1500,7 +1530,7 @@ dependencies = [ "qrcode", "shadowsocks-service", "tcmalloc", - "tokio", + "tokio 1.0.1", ] [[package]] @@ -1510,7 +1540,7 @@ dependencies = [ "async-trait", "byte_string", "byteorder", - "bytes 0.6.0", + "bytes 1.0.0", "cfg-if 1.0.0", "env_logger", "futures", @@ -1539,11 +1569,10 @@ dependencies = [ "strum", "strum_macros", "thiserror", - "tokio", + "tokio 1.0.1", "tokio-native-tls", "tokio-rustls", "tower", - "trust-dns-proto", "trust-dns-resolver", "webpki-roots", ] @@ -1624,9 +1653,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.55" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a571a711dddd09019ccc628e1b17fe87c59b09d513c06c026877aa708334f37a" +checksum = "a9802ddde94170d186eeee5005b798d9c159fa970403f1be19976d0cfb939b72" dependencies = [ "proc-macro2", "quote", @@ -1693,18 +1722,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e9ae34b84616eedaaf1e9dd6026dbe00dcafa92aa0c8077cb69df1fcfe5e53e" +checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba20f23e85b10754cd195504aebf6a27e2e6cbe28c17778a0c930724628dd56" +checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1" dependencies = [ "proc-macro2", "quote", @@ -1759,31 +1788,41 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "0.3.6" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "720ba21c25078711bf456d607987d95bce90f7c3bea5abe1db587862e7a1e87c" +checksum = "099837d3464c16a808060bb3f02263b412f6fafcb5d01c533d309985fbeebe48" +dependencies = [ + "bytes 0.5.6", + "fnv", + "pin-project-lite 0.1.11", + "slab", +] + +[[package]] +name = "tokio" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d258221f566b6c803c7b4714abadc080172b272090cdc5e244a6d4dd13c3a6bd" dependencies = [ "autocfg", - "bytes 0.6.0", - "futures-core", + "bytes 1.0.0", "libc", "memchr", "mio", "num_cpus", "once_cell", "parking_lot", - "pin-project-lite", + "pin-project-lite 0.2.0", "signal-hook-registry", - "slab", "tokio-macros", "winapi", ] [[package]] name = "tokio-macros" -version = "0.3.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46dfffa59fc3c8aad216ed61bdc2c263d2b9d87a9c8ac9de0c11a813e51b6db7" +checksum = "42517d2975ca3114b22a16192634e8241dc5cc1f130be194645970cc1c371494" dependencies = [ "proc-macro2", "quote", @@ -1792,55 +1831,153 @@ dependencies = [ [[package]] name = "tokio-native-tls" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501c8252b73bd01379aaae1521523c2629ff1bc6ea46c29e0baff515cee60f1b" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" dependencies = [ "native-tls", - "tokio", + "tokio 1.0.1", ] [[package]] name = "tokio-rustls" -version = "0.21.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "609ada6f5bf21315925c6e43d78dc51fba5c5968a995f95345b4781cc06f37eb" +checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" dependencies = [ "rustls", - "tokio", + "tokio 1.0.1", "webpki", ] +[[package]] +name = "tokio-stream" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f3be913b74b13210c8fe04b17ab833f5a124f45b93d0f99f59fff621f64392a" +dependencies = [ + "async-stream", + "futures-core", + "pin-project-lite 0.2.0", + "tokio 1.0.1", +] + [[package]] name = "tokio-util" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3137de2b078e95274b696cc522e87f22c9a753fe3ef3344116ffb94f104f10a3" +checksum = "36135b7e7da911f5f8b9331209f7fab4cc13498f3fff52f72a710c78187e3148" dependencies = [ - "bytes 0.6.0", + "bytes 1.0.0", "futures-core", "futures-sink", "log", - "pin-project-lite", - "tokio", + "pin-project-lite 0.2.0", + "tokio 1.0.1", + "tokio-stream", ] [[package]] name = "tower" -version = "0.4.0" -source = "git+https://github.com/tower-rs/tower.git#3a8d31c60f927a7c7073851062ef0ec11f76c677" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3169017c090b7a28fce80abaad0ab4f5566423677c9331bb320af7e49cfe62" dependencies = [ "futures-core", - "pin-project 1.0.2", + "tower-buffer", + "tower-discover", + "tower-layer", + "tower-limit", + "tower-load-shed", + "tower-retry", + "tower-service", + "tower-timeout", + "tower-util", +] + +[[package]] +name = "tower-buffer" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4887dc2a65d464c8b9b66e0e4d51c2fd6cf5b3373afc72805b0a60bce00446a" +dependencies = [ + "futures-core", + "pin-project 0.4.27", + "tokio 0.2.24", "tower-layer", "tower-service", "tracing", ] +[[package]] +name = "tower-discover" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f6b5000c3c54d269cc695dff28136bb33d08cbf1df2c48129e143ab65bf3c2a" +dependencies = [ + "futures-core", + "pin-project 0.4.27", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.0" -source = "git+https://github.com/tower-rs/tower.git#3a8d31c60f927a7c7073851062ef0ec11f76c677" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a35d656f2638b288b33495d1053ea74c40dc05ec0b92084dd71ca5566c4ed1dc" + +[[package]] +name = "tower-limit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c3040c5dbed68abffaa0d4517ac1a454cd741044f33ab0eefab6b8d1361404" +dependencies = [ + "futures-core", + "pin-project 0.4.27", + "tokio 0.2.24", + "tower-layer", + "tower-load", + "tower-service", +] + +[[package]] +name = "tower-load" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cc79fc3afd07492b7966d7efa7c6c50f8ed58d768a6075dd7ae6591c5d2017b" +dependencies = [ + "futures-core", + "log", + "pin-project 0.4.27", + "tokio 0.2.24", + "tower-discover", + "tower-service", +] + +[[package]] +name = "tower-load-shed" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f021e23900173dc315feb4b6922510dae3e79c689b74c089112066c11f0ae4e" +dependencies = [ + "futures-core", + "pin-project 0.4.27", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-retry" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6727956aaa2f8957d4d9232b308fe8e4e65d99db30f42b225646e86c9b6a952" +dependencies = [ + "futures-core", + "pin-project 0.4.27", + "tokio 0.2.24", + "tower-layer", + "tower-service", +] [[package]] name = "tower-service" @@ -1848,6 +1985,30 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" +[[package]] +name = "tower-timeout" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "127b8924b357be938823eaaec0608c482d40add25609481027b96198b2e4b31e" +dependencies = [ + "pin-project 0.4.27", + "tokio 0.2.24", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-util" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1093c19826d33807c72511e68f73b4a0469a3f22c2bd5f7d5212178b4b89674" +dependencies = [ + "futures-core", + "futures-util", + "pin-project 0.4.27", + "tower-service", +] + [[package]] name = "tracing" version = "0.1.22" @@ -1856,7 +2017,7 @@ checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3" dependencies = [ "cfg-if 1.0.0", "log", - "pin-project-lite", + "pin-project-lite 0.2.0", "tracing-attributes", "tracing-core", ] @@ -1900,7 +2061,7 @@ checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" [[package]] name = "trust-dns-proto" version = "0.20.0-alpha.3" -source = "git+https://github.com/bluejekyll/trust-dns.git?branch=main#0662ab66acf5ce40a20df72ed82766236245c0ef" +source = "git+https://github.com/bluejekyll/trust-dns.git?branch=main#540906be77b03dc59494aef453b2dbcc875a2a0a" dependencies = [ "async-trait", "cfg-if 1.0.0", @@ -1913,18 +2074,18 @@ dependencies = [ "ipnet", "lazy_static", "log", - "rand 0.7.3", + "rand 0.8.0", "serde", "smallvec", "thiserror", - "tokio", + "tokio 1.0.1", "url", ] [[package]] name = "trust-dns-resolver" version = "0.20.0-alpha.3" -source = "git+https://github.com/bluejekyll/trust-dns.git?branch=main#0662ab66acf5ce40a20df72ed82766236245c0ef" +source = "git+https://github.com/bluejekyll/trust-dns.git?branch=main#540906be77b03dc59494aef453b2dbcc875a2a0a" dependencies = [ "cfg-if 1.0.0", "futures-util", @@ -1938,7 +2099,7 @@ dependencies = [ "serde", "smallvec", "thiserror", - "tokio", + "tokio 1.0.1", "tokio-rustls", "trust-dns-proto", "trust-dns-rustls", @@ -1948,14 +2109,14 @@ dependencies = [ [[package]] name = "trust-dns-rustls" version = "0.20.0-alpha.3" -source = "git+https://github.com/bluejekyll/trust-dns.git?branch=main#0662ab66acf5ce40a20df72ed82766236245c0ef" +source = "git+https://github.com/bluejekyll/trust-dns.git?branch=main#540906be77b03dc59494aef453b2dbcc875a2a0a" dependencies = [ "futures-channel", "futures-io", "futures-util", "log", "rustls", - "tokio", + "tokio 1.0.1", "tokio-rustls", "trust-dns-proto", "webpki", diff --git a/Cargo.toml b/Cargo.toml index c261b8215698..80f5df128e20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,6 @@ default = [ "server", "manager", "utility", - "local-dns", "local-http", "local-http-rustls", "local-tunnel", @@ -112,7 +111,7 @@ cfg-if = "1" qrcode = { version = "0.12", default-features = false } futures = "0.3" -tokio = { version = "0.3.1", features = ["full"] } +tokio = { version = "1.0", features = ["rt", "rt-multi-thread", "signal"] } mimalloc = { version = "0.1", optional = true } tcmalloc = { version = "0.3", optional = true } @@ -127,3 +126,4 @@ daemonize = "0.4" byteorder = "1.3" env_logger = "0.8" byte_string = "1.0" +tokio = { version = "1.0", features = ["net", "time", "macros", "io-util"]} diff --git a/README.md b/README.md index b89ed85ac6d9..44f08fe47476 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![License](https://img.shields.io/github/license/zonyitoo/shadowsocks-rust.svg)](https://github.com/zonyitoo/shadowsocks-rust) [![crates.io](https://img.shields.io/crates/v/shadowsocks-rust.svg)](https://crates.io/crates/shadowsocks-rust) [![Release](https://img.shields.io/github/release/shadowsocks/shadowsocks-rust.svg)](https://github.com/shadowsocks/shadowsocks-rust/releases) +[![Dependabot Status](https://api.dependabot.com/badges/status?host=github&repo=shadowsocks%2fshadowsocks-rust)](https://dependabot.com) This is a port of [shadowsocks](https://github.com/shadowsocks/shadowsocks). diff --git a/bin/common/monitor/windows.rs b/bin/common/monitor/windows.rs index 14bd7289ab6b..f9e716c8e7a2 100644 --- a/bin/common/monitor/windows.rs +++ b/bin/common/monitor/windows.rs @@ -1,21 +1,10 @@ -use futures::{ - future::{self, Either, FutureExt}, - StreamExt, -}; use log::info; use std::io; -use tokio::signal::{ctrl_c, windows::ctrl_break}; +use tokio::signal::ctrl_c; pub async fn create_signal_monitor() -> io::Result<()> { - let cc = ctrl_c(); - let cb = ctrl_break()?; - - let signal_name = match future::select(cc.boxed(), cb.into_future().boxed()).await { - Either::Left(..) => "CTRL-C", - Either::Right(..) => "CTRL-BREAK", - }; - - info!("received {}, exiting", signal_name); + ctrl_c().await; + info!("received CTRL-C, exiting"); Ok(()) } diff --git a/bin/common/validator/mod.rs b/bin/common/validator/mod.rs index dfeaed96c0c2..224f56fa2e35 100644 --- a/bin/common/validator/mod.rs +++ b/bin/common/validator/mod.rs @@ -4,8 +4,6 @@ use std::net::SocketAddr; -#[cfg(feature = "local-dns")] -use shadowsocks_service::config::NameServerAddr; use shadowsocks_service::shadowsocks::{relay::socks5::Address, ManagerAddr, ServerAddr, ServerConfig}; macro_rules! validate_type { @@ -31,12 +29,6 @@ validate_type!( ManagerAddr, "should be either ip:port, domain:port or /path/to/unix.sock" ); -#[cfg(feature = "local-dns")] -validate_type!( - validate_name_server_addr, - NameServerAddr, - "should be either ip:port, domain:port or /path/to/unix.sock" -); validate_type!(validate_u64, u64, "should be unsigned integer"); validate_type!(validate_u32, u32, "should be unsigned integer"); diff --git a/bin/sslocal.rs b/bin/sslocal.rs index 82ba80d7b2cb..143ad1351531 100644 --- a/bin/sslocal.rs +++ b/bin/sslocal.rs @@ -70,6 +70,11 @@ fn main() { (@arg UDP_MAX_ASSOCIATIONS: --("udp-max-associations") +takes_value {validator::validate_u64} "Maximum associations to be kept simultaneously for UDP relay") (@arg UDP_BIND_ADDR: --("udp-bind-addr") +takes_value {validator::validate_server_addr} "UDP relay's bind address, default is the same as local-addr") + + (@arg INBOUND_SEND_BUFFER_SIZE: --("inbound-send-buffer-size") +takes_value {validator::validate_u32} "Set inbound sockets' SO_SNDBUF option") + (@arg INBOUND_RECV_BUFFER_SIZE: --("inbound-redv-buffer-size") +takes_value {validator::validate_u32} "Set inbound sockets' SO_RCVBUF option") + (@arg OUTBOUND_SEND_BUFFER_SIZE: --("outbound-send-buffer-size") +takes_value {validator::validate_u32} "Set outbound sockets' SO_SNDBUF option") + (@arg OUTBOUND_RECV_BUFFER_SIZE: --("outbound-redv-buffer-size") +takes_value {validator::validate_u32} "Set outbound sockets' SO_RCVBUF option") ); // FIXME: -6 is not a identifier, so we cannot build it with clap_app! @@ -137,7 +142,7 @@ fn main() { #[cfg(feature = "local-dns")] { app = clap_app!(@app (app) - (@arg LOCAL_DNS_ADDR: --("local-dns-addr") +takes_value required_if("PROTOCOL", "dns") {validator::validate_name_server_addr} "Specify the address of local DNS server, send queries directly") + (@arg LOCAL_DNS_ADDR: --("local-dns-addr") +takes_value required_if("PROTOCOL", "dns") {validator::validate_socket_addr} "Specify the address of local DNS server, send queries directly") (@arg REMOTE_DNS_ADDR: --("remote-dns-addr") +takes_value required_if("PROTOCOL", "dns") {validator::validate_address} "Specify the address of remote DNS server, send queries through shadowsocks' tunnel") (@arg DNS_LOCAL_ADDR: --("dns-addr") +takes_value requires_all(&["REMOTE_DNS_ADDR"]) {validator::validate_server_addr} "DNS address, listen to this address if specified") ); @@ -235,10 +240,10 @@ fn main() { #[cfg(feature = "local-dns")] { - use shadowsocks_service::config::NameServerAddr; + use std::net::SocketAddr; if let Some(local_dns_addr) = matches.value_of("LOCAL_DNS_ADDR") { - let addr = local_dns_addr.parse::().expect("local dns address"); + let addr = local_dns_addr.parse::().expect("local dns address"); config.local_dns_addr = Some(addr); } @@ -258,12 +263,6 @@ fn main() { // A socket `protect_path` in CWD // Same as shadowsocks-libev's android.c config.outbound_vpn_protect_path = Some(From::from("protect_path")); - - // Set default config.local_dns_addr - #[cfg(feature = "local-dns")] - if config.local_dns_addr.is_none() { - config.local_dns_addr = Some(From::from("local_dns_path")); - } } if let Some(local_addr) = matches.value_of("LOCAL_ADDR") { @@ -341,6 +340,19 @@ fn main() { config.udp_bind_addr = Some(udp_bind_addr.parse::().expect("udp-bind-addr")); } + if let Some(bs) = matches.value_of("INBOUND_SEND_BUFFER_SIZE") { + config.inbound_send_buffer_size = Some(bs.parse::().expect("inbound-send-buffer-size")); + } + if let Some(bs) = matches.value_of("INBOUND_RECV_BUFFER_SIZE") { + config.inbound_recv_buffer_size = Some(bs.parse::().expect("inbound-recv-buffer-size")); + } + if let Some(bs) = matches.value_of("OUTBOUND_SEND_BUFFER_SIZE") { + config.outbound_send_buffer_size = Some(bs.parse::().expect("outbound-send-buffer-size")); + } + if let Some(bs) = matches.value_of("OUTBOUND_RECV_BUFFER_SIZE") { + config.outbound_recv_buffer_size = Some(bs.parse::().expect("outbound-recv-buffer-size")); + } + // DONE READING options if config.local_addr.is_none() { diff --git a/bin/ssmanager.rs b/bin/ssmanager.rs index 2953d4ce433f..2f1b7e445769 100644 --- a/bin/ssmanager.rs +++ b/bin/ssmanager.rs @@ -61,6 +61,11 @@ fn main() { (@arg NOFILE: -n --nofile +takes_value "Set RLIMIT_NOFILE with both soft and hard limit (only for *nix systems)") (@arg ACL: --acl +takes_value "Path to ACL (Access Control List)") + + (@arg INBOUND_SEND_BUFFER_SIZE: --("inbound-send-buffer-size") +takes_value {validator::validate_u32} "Set inbound sockets' SO_SNDBUF option") + (@arg INBOUND_RECV_BUFFER_SIZE: --("inbound-redv-buffer-size") +takes_value {validator::validate_u32} "Set inbound sockets' SO_RCVBUF option") + (@arg OUTBOUND_SEND_BUFFER_SIZE: --("outbound-send-buffer-size") +takes_value {validator::validate_u32} "Set outbound sockets' SO_SNDBUF option") + (@arg OUTBOUND_RECV_BUFFER_SIZE: --("outbound-redv-buffer-size") +takes_value {validator::validate_u32} "Set outbound sockets' SO_RCVBUF option") ); #[cfg(feature = "logging")] @@ -191,6 +196,19 @@ fn main() { config.ipv6_first = true; } + if let Some(bs) = matches.value_of("INBOUND_SEND_BUFFER_SIZE") { + config.inbound_send_buffer_size = Some(bs.parse::().expect("inbound-send-buffer-size")); + } + if let Some(bs) = matches.value_of("INBOUND_RECV_BUFFER_SIZE") { + config.inbound_recv_buffer_size = Some(bs.parse::().expect("inbound-recv-buffer-size")); + } + if let Some(bs) = matches.value_of("OUTBOUND_SEND_BUFFER_SIZE") { + config.outbound_send_buffer_size = Some(bs.parse::().expect("outbound-send-buffer-size")); + } + if let Some(bs) = matches.value_of("OUTBOUND_RECV_BUFFER_SIZE") { + config.outbound_recv_buffer_size = Some(bs.parse::().expect("outbound-recv-buffer-size")); + } + // DONE reading options if config.manager.is_none() { diff --git a/bin/ssserver.rs b/bin/ssserver.rs index 69812428ec62..3ac307d4f6ba 100644 --- a/bin/ssserver.rs +++ b/bin/ssserver.rs @@ -66,6 +66,11 @@ fn main() { (@arg UDP_TIMEOUT: --("udp-timeout") +takes_value {validator::validate_u64} "Timeout seconds for UDP relay") (@arg UDP_MAX_ASSOCIATIONS: --("udp-max-associations") +takes_value {validator::validate_u64} "Maximum associations to be kept simultaneously for UDP relay") + + (@arg INBOUND_SEND_BUFFER_SIZE: --("inbound-send-buffer-size") +takes_value {validator::validate_u32} "Set inbound sockets' SO_SNDBUF option") + (@arg INBOUND_RECV_BUFFER_SIZE: --("inbound-redv-buffer-size") +takes_value {validator::validate_u32} "Set inbound sockets' SO_RCVBUF option") + (@arg OUTBOUND_SEND_BUFFER_SIZE: --("outbound-send-buffer-size") +takes_value {validator::validate_u32} "Set outbound sockets' SO_SNDBUF option") + (@arg OUTBOUND_RECV_BUFFER_SIZE: --("outbound-redv-buffer-size") +takes_value {validator::validate_u32} "Set outbound sockets' SO_RCVBUF option") ); #[cfg(feature = "logging")] @@ -217,6 +222,19 @@ fn main() { config.udp_max_associations = Some(udp_max_assoc.parse::().expect("udp-max-associations")); } + if let Some(bs) = matches.value_of("INBOUND_SEND_BUFFER_SIZE") { + config.inbound_send_buffer_size = Some(bs.parse::().expect("inbound-send-buffer-size")); + } + if let Some(bs) = matches.value_of("INBOUND_RECV_BUFFER_SIZE") { + config.inbound_recv_buffer_size = Some(bs.parse::().expect("inbound-recv-buffer-size")); + } + if let Some(bs) = matches.value_of("OUTBOUND_SEND_BUFFER_SIZE") { + config.outbound_send_buffer_size = Some(bs.parse::().expect("outbound-send-buffer-size")); + } + if let Some(bs) = matches.value_of("OUTBOUND_RECV_BUFFER_SIZE") { + config.outbound_recv_buffer_size = Some(bs.parse::().expect("outbound-recv-buffer-size")); + } + // DONE READING options if config.server.is_empty() { diff --git a/configs/log4rs.yaml b/configs/log4rs.yaml new file mode 100644 index 000000000000..3c43c3bd828d --- /dev/null +++ b/configs/log4rs.yaml @@ -0,0 +1,25 @@ +refresh_rate: 30 seconds +appenders: + stdout: + kind: console + encoder: + pattern: "{d} {h({l}):<5} {m}{n}" + file: + kind: rolling_file + path: shadowsocks.log + encoder: + kind: pattern + pattern: "{d} {h({l}):<5} {m}{n}" + policy: + trigger: + kind: size + limit: 10 mb + roller: + kind: fixed_window + pattern: shadowsocks.{}.log + count: 5 +root: + level: debug + appenders: + - stdout + - file diff --git a/crates/shadowsocks-service/Cargo.toml b/crates/shadowsocks-service/Cargo.toml index cf07c8152660..fc707cba000f 100644 --- a/crates/shadowsocks-service/Cargo.toml +++ b/crates/shadowsocks-service/Cargo.toml @@ -19,7 +19,6 @@ full = [ "server", "manager", "trust-dns", - "local-dns", "local-http", "local-http-rustls", "local-redir", @@ -42,7 +41,7 @@ dns-over-tls = ["trust-dns", "trust-dns-resolver/dns-over-tls", "trust-dns-resol dns-over-https = ["trust-dns", "trust-dns-resolver/dns-over-https"] # Enable DNS-relay -local-dns = ["local", "trust-dns-proto", "rand"] +local-dns = ["local", "trust-dns", "rand"] # Backward compatibility, DO NOT USE local-dns-relay = ["local-dns"] # Enable client flow statistic report @@ -72,16 +71,16 @@ thiserror = "1.0" spin = "0.7" lru_time_cache = "0.11" -bytes = "0.6" +bytes = "1.0" byte_string = "1.0" byteorder = "1.3" rand = { version = "0.8", optional = true } futures = "0.3" -tokio = { version = "0.3.1", features = ["full"] } -tokio-native-tls = { version = "0.2", optional = true } +tokio = { version = "1.0", features = ["full"] } +tokio-native-tls = { version = "0.3", optional = true } native-tls = { version = "0.2", optional = true } -tokio-rustls = { version = "0.21", optional = true } +tokio-rustls = { version = "0.22", optional = true } webpki-roots = { version = "0.21", optional = true } rustls-native-certs = { version = "0.5", optional = true } async-trait = "0.1" @@ -91,11 +90,10 @@ socket2 = "0.3" libc = "0.2" http = { version = "0.2", optional = true } -hyper = { git = "https://github.com/hyperium/hyper.git", optional = true, features = ["full"] } -tower = { git = "https://github.com/tower-rs/tower.git", optional = true } +hyper = { version = "0.14", optional = true, features = ["full"] } +tower = { version = "0.3", optional = true } trust-dns-resolver = { git = "https://github.com/bluejekyll/trust-dns.git", branch = "main", optional = true, features = ["serde-config"] } -trust-dns-proto = { git = "https://github.com/bluejekyll/trust-dns.git", branch = "main", optional = true } ipnet = "2.3" iprange = "0.6" diff --git a/crates/shadowsocks-service/src/config.rs b/crates/shadowsocks-service/src/config.rs index cbc1ddb56c82..f2b3c2ff5030 100644 --- a/crates/shadowsocks-service/src/config.rs +++ b/crates/shadowsocks-service/src/config.rs @@ -70,8 +70,6 @@ use shadowsocks::{ use trust_dns_resolver::config::{NameServerConfig, Protocol, ResolverConfig}; use crate::acl::AccessControl; -#[cfg(feature = "local-dns")] -pub use crate::local::dns::config::NameServerAddr; #[cfg(feature = "trust-dns")] #[derive(Serialize, Deserialize, Debug)] @@ -548,9 +546,11 @@ pub struct Config { pub server: Vec, /// Local server's bind address, or ShadowSocks server's outbound address pub local_addr: Option, + /// Destination address for tunnel #[cfg(feature = "local-tunnel")] pub forward: Option
, + /// DNS configuration, uses system-wide DNS configuration by default /// /// Value could be a `IpAddr`, uses UDP DNS protocol with port `53`. For example: `8.8.8.8` @@ -562,22 +562,59 @@ pub struct Config { /// - `quad9`, `quad9_tls` #[cfg(feature = "trust-dns")] pub dns: Option, - /// Server mode, `tcp_only`, `tcp_and_udp`, and `udp_only` - pub mode: Mode, + /// Uses IPv6 addresses first + /// + /// Set to `true` if you want to query IPv6 addresses before IPv4 + pub ipv6_first: bool, + + /// Internal DNS's bind address + #[cfg(feature = "local-dns")] + pub dns_bind_addr: Option, + /// Local DNS's address + /// + /// Sending DNS query directly to this address + #[cfg(feature = "local-dns")] + pub local_dns_addr: Option, + /// Remote DNS's address + /// + /// Sending DNS query through proxy to this address + #[cfg(feature = "local-dns")] + pub remote_dns_addr: Option
, + /// Set `TCP_NODELAY` socket option pub no_delay: bool, + /// `RLIMIT_NOFILE` option for *nix systems + pub nofile: Option, + /// Set `SO_MARK` socket option for outbound sockets #[cfg(any(target_os = "linux", target_os = "android"))] pub outbound_fwmark: Option, /// Set `SO_BINDTODEVICE` socket option for outbound sockets #[cfg(any(target_os = "linux", target_os = "android"))] pub outbound_bind_interface: Option, + /// Path to protect callback unix address, only for Android + #[cfg(target_os = "android")] + pub outbound_vpn_protect_path: Option, + + /// Set `SO_SNDBUF` for inbound sockets + pub inbound_send_buffer_size: Option, + /// Set `SO_RCVBUF` for inbound sockets + pub inbound_recv_buffer_size: Option, + /// Set `SO_SNDBUF` for outbound sockets + pub outbound_send_buffer_size: Option, + /// Set `SO_RCVBUF` for outbound sockets + pub outbound_recv_buffer_size: Option, + /// Manager's configuration pub manager: Option, + + /// Server mode, `tcp_only`, `tcp_and_udp`, and `udp_only` + pub mode: Mode, /// Config is for Client or Server pub config_type: ConfigType, /// Protocol for local server pub local_protocol: ProtocolType, + /// Timeout for UDP Associations, default is 5 minutes pub udp_timeout: Option, /// Maximum number of UDP Associations, default is unconfigured @@ -586,10 +623,10 @@ pub struct Config { /// /// Resolving Android's issue: [shadowsocks/shadowsocks-android#2571](https://github.com/shadowsocks/shadowsocks-android/issues/2571) pub udp_bind_addr: Option, - /// `RLIMIT_NOFILE` option for *nix systems - pub nofile: Option, + /// ACL configuration pub acl: Option, + /// TCP Transparent Proxy type #[cfg(feature = "local-redir")] pub tcp_redir: RedirType, @@ -597,28 +634,9 @@ pub struct Config { #[cfg(feature = "local-redir")] pub udp_redir: RedirType, /// Flow statistic report Unix socket path (only for Android) + #[cfg(feature = "local-flow-stat")] pub stat_path: Option, - /// Path to protect callback unix address, only for Android - #[cfg(target_os = "android")] - pub outbound_vpn_protect_path: Option, - /// Internal DNS's bind address - #[cfg(feature = "local-dns")] - pub dns_bind_addr: Option, - /// Local DNS's address - /// - /// Sending DNS query directly to this address - #[cfg(feature = "local-dns")] - pub local_dns_addr: Option, - /// Remote DNS's address - /// - /// Sending DNS query through proxy to this address - #[cfg(feature = "local-dns")] - pub remote_dns_addr: Option
, - /// Uses IPv6 addresses first - /// - /// Set to `true` if you want to query IPv6 addresses before IPv4 - pub ipv6_first: bool, } /// Configuration parsing error kind @@ -686,39 +704,55 @@ impl Config { Config { server: Vec::new(), local_addr: None, + #[cfg(feature = "local-tunnel")] forward: None, + #[cfg(feature = "trust-dns")] dns: None, - mode: Mode::TcpOnly, + ipv6_first: false, + + #[cfg(feature = "local-dns")] + dns_bind_addr: None, + #[cfg(feature = "local-dns")] + local_dns_addr: None, + #[cfg(feature = "local-dns")] + remote_dns_addr: None, + no_delay: false, + nofile: None, + #[cfg(any(target_os = "linux", target_os = "android"))] outbound_fwmark: None, #[cfg(any(target_os = "linux", target_os = "android"))] outbound_bind_interface: None, + #[cfg(target_os = "android")] + outbound_vpn_protect_path: None, + + inbound_send_buffer_size: None, + inbound_recv_buffer_size: None, + outbound_send_buffer_size: None, + outbound_recv_buffer_size: None, + manager: None, + + mode: Mode::TcpOnly, config_type, local_protocol: ProtocolType::default(), + udp_timeout: None, udp_max_associations: None, udp_bind_addr: None, - nofile: None, + acl: None, + #[cfg(feature = "local-redir")] tcp_redir: RedirType::tcp_default(), #[cfg(feature = "local-redir")] udp_redir: RedirType::udp_default(), + #[cfg(feature = "local-flow-stat")] stat_path: None, - #[cfg(target_os = "android")] - outbound_vpn_protect_path: None, - #[cfg(feature = "local-dns")] - dns_bind_addr: None, - #[cfg(feature = "local-dns")] - local_dns_addr: None, - #[cfg(feature = "local-dns")] - remote_dns_addr: None, - ipv6_first: false, } } diff --git a/crates/shadowsocks-service/src/lib.rs b/crates/shadowsocks-service/src/lib.rs index 66b12298cb17..c2bc533b5700 100644 --- a/crates/shadowsocks-service/src/lib.rs +++ b/crates/shadowsocks-service/src/lib.rs @@ -71,3 +71,15 @@ mod sys; /// Default UDP association's expire duration const DEFAULT_UDP_EXPIRY_DURATION: Duration = Duration::from_secs(5 * 60); + +#[cfg(feature = "trust-dns")] +fn hint_support_default_system_resolver() -> bool { + // Nearly all *nix system have /etc/resolv.conf, except Android. + // macOS have to use system provided resolver. + cfg!(all( + unix, + not(target_os = "android"), + not(target_os = "macos"), + not(target_os = "ios") + )) +} diff --git a/crates/shadowsocks-service/src/local/context.rs b/crates/shadowsocks-service/src/local/context.rs index c7b386643630..0300976e896a 100644 --- a/crates/shadowsocks-service/src/local/context.rs +++ b/crates/shadowsocks-service/src/local/context.rs @@ -10,7 +10,7 @@ use shadowsocks::{ config::ServerType, context::{Context, SharedContext}, dns_resolver::DnsResolver, - net::ConnectOpts, + net::{AcceptOpts, ConnectOpts}, relay::Address, }; #[cfg(feature = "local-dns")] @@ -22,6 +22,7 @@ use crate::{acl::AccessControl, net::FlowStat}; pub struct ServiceContext { context: SharedContext, connect_opts: ConnectOpts, + accept_opts: AcceptOpts, // Access Control acl: Option, @@ -40,6 +41,7 @@ impl ServiceContext { ServiceContext { context: Context::new_shared(ServerType::Local), connect_opts: ConnectOpts::default(), + accept_opts: AcceptOpts::default(), acl: None, flow_stat: Arc::new(FlowStat::new()), #[cfg(feature = "local-dns")] @@ -67,6 +69,16 @@ impl ServiceContext { &self.connect_opts } + /// Set `AcceptOpts` + pub fn set_accept_opts(&mut self, accept_opts: AcceptOpts) { + self.accept_opts = accept_opts; + } + + /// Get `AcceptOpts` cloned + pub fn accept_opts(&self) -> AcceptOpts { + self.accept_opts.clone() + } + /// Set Access Control List pub fn set_acl(&mut self, acl: AccessControl) { self.acl = Some(acl); diff --git a/crates/shadowsocks-service/src/local/dns/client_cache.rs b/crates/shadowsocks-service/src/local/dns/client_cache.rs index cf6e2fd0f2c9..efc096971555 100644 --- a/crates/shadowsocks-service/src/local/dns/client_cache.rs +++ b/crates/shadowsocks-service/src/local/dns/client_cache.rs @@ -13,7 +13,7 @@ use std::{ use log::trace; use shadowsocks::{config::ServerConfig, net::ConnectOpts, relay::socks5::Address}; use tokio::sync::Mutex; -use trust_dns_proto::{error::ProtoError, op::Message}; +use trust_dns_resolver::proto::{error::ProtoError, op::Message}; use crate::local::context::ServiceContext; @@ -121,6 +121,7 @@ impl DnsClientCache { } #[cfg(unix)] + #[allow(dead_code)] pub async fn lookup_unix_stream>(&self, ns: &P, msg: Message) -> Result { let mut last_err = None; diff --git a/crates/shadowsocks-service/src/local/dns/config.rs b/crates/shadowsocks-service/src/local/dns/config.rs deleted file mode 100644 index 1219e113b779..000000000000 --- a/crates/shadowsocks-service/src/local/dns/config.rs +++ /dev/null @@ -1,67 +0,0 @@ -//! DNS relay configuration - -#[cfg(unix)] -use std::path::PathBuf; -use std::{ - fmt::{self, Display}, - net::SocketAddr, - str::FromStr, -}; - -/// Parse `NameServerAddr` error -#[derive(Debug)] -pub struct NameServerAddrError; - -/// Address for Manager server -#[derive(Debug, Clone)] -pub enum NameServerAddr { - /// IP address - SocketAddr(SocketAddr), - /// Unix socket path - #[cfg(unix)] - UnixSocketAddr(PathBuf), -} - -impl FromStr for NameServerAddr { - type Err = NameServerAddrError; - - fn from_str(s: &str) -> Result { - match s.parse::() { - Ok(socket_addr) => Ok(NameServerAddr::SocketAddr(socket_addr)), - #[cfg(unix)] - Err(..) => Ok(NameServerAddr::UnixSocketAddr(PathBuf::from(s))), - #[cfg(not(unix))] - Err(..) => Err(NameServerAddrError), - } - } -} - -impl Display for NameServerAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - NameServerAddr::SocketAddr(ref saddr) => Display::fmt(saddr, f), - #[cfg(unix)] - NameServerAddr::UnixSocketAddr(ref path) => Display::fmt(&path.display(), f), - } - } -} - -impl From for NameServerAddr { - fn from(addr: SocketAddr) -> NameServerAddr { - NameServerAddr::SocketAddr(addr) - } -} - -#[cfg(unix)] -impl From for NameServerAddr { - fn from(p: PathBuf) -> NameServerAddr { - NameServerAddr::UnixSocketAddr(p) - } -} - -#[cfg(unix)] -impl From<&str> for NameServerAddr { - fn from(p: &str) -> NameServerAddr { - NameServerAddr::UnixSocketAddr(PathBuf::from(p)) - } -} diff --git a/crates/shadowsocks-service/src/local/dns/dns_resolver.rs b/crates/shadowsocks-service/src/local/dns/dns_resolver.rs deleted file mode 100644 index 97f25386e4a3..000000000000 --- a/crates/shadowsocks-service/src/local/dns/dns_resolver.rs +++ /dev/null @@ -1,220 +0,0 @@ -//! Replacement of service's DNS resolver - -use std::{ - io::{self, ErrorKind}, - net::SocketAddr, -}; - -use async_trait::async_trait; -use futures::future; -use log::{debug, trace}; -use shadowsocks::{dns_resolver::DnsResolve, net::ConnectOpts}; -use trust_dns_proto::{ - op::{Message, Query}, - rr::{DNSClass, Name, RData, RecordType}, -}; - -use crate::config::Mode; - -use super::{client_cache::DnsClientCache, config::NameServerAddr}; - -pub struct DnsResolver { - ns: NameServerAddr, - client_cache: DnsClientCache, - mode: Mode, - ipv6_first: bool, - connect_opts: ConnectOpts, - attempts: usize, -} - -impl DnsResolver { - pub fn new(ns: NameServerAddr) -> DnsResolver { - DnsResolver { - ns, - client_cache: DnsClientCache::new(5), - mode: Mode::UdpOnly, - ipv6_first: false, - connect_opts: ConnectOpts::default(), - attempts: 2, - } - } - - pub fn set_mode(&mut self, mode: Mode) { - self.mode = mode; - } - - pub fn set_ipv6_first(&mut self, ipv6_first: bool) { - self.ipv6_first = ipv6_first; - } - - pub fn set_connect_opts(&mut self, connect_opts: ConnectOpts) { - self.connect_opts = connect_opts; - } - - async fn lookup(&self, msg: Message) -> io::Result { - let mut last_err = io::Error::new(ErrorKind::InvalidData, "resolve empty"); - - for _ in 0..self.attempts { - match self.lookup_inner(msg.clone()).await { - Ok(m) => return Ok(m), - Err(err) => last_err = err, - } - } - - Err(last_err) - } - - async fn lookup_inner(&self, msg: Message) -> io::Result { - match self.ns { - NameServerAddr::SocketAddr(ns) => { - let mut last_err = io::Error::new(ErrorKind::InvalidData, "resolve empty"); - - // Query UDP then TCP - - if self.mode.enable_udp() { - match self - .client_cache - .lookup_udp_local(ns, msg.clone(), &self.connect_opts) - .await - { - Ok(msg) => return Ok(msg), - Err(err) => { - last_err = err.into(); - } - } - } - - if self.mode.enable_tcp() { - match self.client_cache.lookup_tcp_local(ns, msg, &self.connect_opts).await { - Ok(msg) => return Ok(msg), - Err(err) => { - last_err = err.into(); - } - } - } - - Err(last_err) - } - - #[cfg(unix)] - NameServerAddr::UnixSocketAddr(ref path) => self - .client_cache - .lookup_unix_stream(path, msg) - .await - .map_err(From::from), - } - } -} - -#[async_trait] -impl DnsResolve for DnsResolver { - async fn resolve(&self, host: &str, port: u16) -> io::Result> { - let mut name = Name::from_utf8(host)?; - name.set_fqdn(true); - - let mut queryv4 = Query::new(); - queryv4.set_query_class(DNSClass::IN); - queryv4.set_name(name); - - let mut queryv6 = queryv4.clone(); - queryv4.set_query_type(RecordType::A); - queryv6.set_query_type(RecordType::AAAA); - - let mut msgv4 = Message::new(); - msgv4.set_recursion_desired(true); - msgv4.add_query(queryv4); - - let mut msgv6 = Message::new(); - msgv6.set_recursion_desired(true); - msgv6.add_query(queryv6); - - let (res_v4, res_v6) = future::join(self.lookup(msgv4), self.lookup(msgv6)).await; - - if res_v4.is_err() && res_v6.is_err() { - return if self.ipv6_first { - Err(res_v6.unwrap_err()) - } else { - Err(res_v4.unwrap_err()) - }; - } - - let mut vaddr = Vec::new(); - - if self.ipv6_first { - match res_v6 { - Ok(res) => { - for record in res.answers() { - match *record.rdata() { - RData::A(addr) => vaddr.push(SocketAddr::new(addr.into(), port)), - RData::AAAA(addr) => vaddr.push(SocketAddr::new(addr.into(), port)), - ref rdata => { - trace!("skipped rdata {:?}", rdata); - } - } - } - } - Err(err) => { - debug!("failed to resolve AAAA records, error: {}", err); - } - } - - match res_v4 { - Ok(res) => { - for record in res.answers() { - match *record.rdata() { - RData::A(addr) => vaddr.push(SocketAddr::new(addr.into(), port)), - RData::AAAA(addr) => vaddr.push(SocketAddr::new(addr.into(), port)), - ref rdata => { - trace!("skipped rdata {:?}", rdata); - } - } - } - } - Err(err) => { - debug!("failed to resolve A records, error: {}", err); - } - } - } else { - match res_v4 { - Ok(res) => { - for record in res.answers() { - match *record.rdata() { - RData::A(addr) => vaddr.push(SocketAddr::new(addr.into(), port)), - RData::AAAA(addr) => vaddr.push(SocketAddr::new(addr.into(), port)), - ref rdata => { - trace!("skipped rdata {:?}", rdata); - } - } - } - } - Err(err) => { - debug!("failed to resolve A records, error: {}", err); - } - } - - match res_v6 { - Ok(res) => { - for record in res.answers() { - match *record.rdata() { - RData::A(addr) => vaddr.push(SocketAddr::new(addr.into(), port)), - RData::AAAA(addr) => vaddr.push(SocketAddr::new(addr.into(), port)), - ref rdata => { - trace!("skipped rdata {:?}", rdata); - } - } - } - } - Err(err) => { - debug!("failed to resolve AAAA records, error: {}", err); - } - } - } - - if vaddr.is_empty() { - let err = io::Error::new(ErrorKind::InvalidData, "resolve empty"); - return Err(err); - } - - Ok(vaddr) - } -} diff --git a/crates/shadowsocks-service/src/local/dns/mod.rs b/crates/shadowsocks-service/src/local/dns/mod.rs index 57a0675dc6b8..77752387f4e0 100644 --- a/crates/shadowsocks-service/src/local/dns/mod.rs +++ b/crates/shadowsocks-service/src/local/dns/mod.rs @@ -3,7 +3,5 @@ pub use self::server::Dns; mod client_cache; -pub mod config; -pub mod dns_resolver; pub mod server; mod upstream; diff --git a/crates/shadowsocks-service/src/local/dns/server.rs b/crates/shadowsocks-service/src/local/dns/server.rs index 3e7df2de53a8..e1f460b2aa50 100644 --- a/crates/shadowsocks-service/src/local/dns/server.rs +++ b/crates/shadowsocks-service/src/local/dns/server.rs @@ -17,15 +17,15 @@ use log::{debug, error, info, trace, warn}; use rand::{thread_rng, Rng}; use shadowsocks::{ lookup_then, - net::UdpSocket as ShadowUdpSocket, + net::{TcpListener, UdpSocket as ShadowUdpSocket}, relay::{udprelay::MAXIMUM_UDP_PAYLOAD_SIZE, Address}, }; use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, - net::{TcpListener, TcpStream, UdpSocket}, + net::{TcpStream, UdpSocket}, time, }; -use trust_dns_proto::{ +use trust_dns_resolver::proto::{ op::{header::MessageType, response_code::ResponseCode, Message, OpCode, Query}, rr::{DNSClass, Name, RData, RecordType}, }; @@ -36,32 +36,30 @@ use crate::{ local::{context::ServiceContext, loadbalancing::PingBalancer}, }; -use super::{client_cache::DnsClientCache, config::NameServerAddr}; +use super::client_cache::DnsClientCache; /// DNS Relay server pub struct Dns { context: Arc, mode: Mode, - local_addr: Arc, + local_addr: SocketAddr, remote_addr: Arc
, - nodelay: bool, } impl Dns { /// Create a new DNS Relay server - pub fn new(local_addr: NameServerAddr, remote_addr: Address) -> Dns { + pub fn new(local_addr: SocketAddr, remote_addr: Address) -> Dns { let context = ServiceContext::new(); Dns::with_context(Arc::new(context), local_addr, remote_addr) } /// Create with an existed `context` - pub fn with_context(context: Arc, local_addr: NameServerAddr, remote_addr: Address) -> Dns { + pub fn with_context(context: Arc, local_addr: SocketAddr, remote_addr: Address) -> Dns { Dns { context, mode: Mode::UdpOnly, - local_addr: Arc::new(local_addr), + local_addr: local_addr, remote_addr: Arc::new(remote_addr), - nodelay: false, } } @@ -70,11 +68,6 @@ impl Dns { self.mode = mode; } - /// Set `TCP_NODELAY` - pub fn set_nodelay(&mut self, nodelay: bool) { - self.nodelay = nodelay; - } - /// Run server pub async fn run(self, bind_addr: &ClientConfig, balancer: PingBalancer) -> io::Result<()> { let client = Arc::new(DnsClient::new(self.context.clone(), balancer, self.mode)); @@ -92,10 +85,12 @@ impl Dns { async fn run_tcp_server(&self, bind_addr: &ClientConfig, client: Arc) -> io::Result<()> { let listener = match *bind_addr { - ClientConfig::SocketAddr(ref saddr) => TcpListener::bind(saddr).await?, + ClientConfig::SocketAddr(ref saddr) => { + TcpListener::bind_with_opts(saddr, self.context.accept_opts()).await? + } ClientConfig::DomainName(ref dname, port) => { lookup_then!(self.context.context_ref(), dname, port, |addr| { - TcpListener::bind(addr).await + TcpListener::bind_with_opts(&addr, self.context.accept_opts()).await })? .1 } @@ -118,15 +113,11 @@ impl Dns { } }; - if self.nodelay { - let _ = stream.set_nodelay(true); - } - tokio::spawn(Dns::handle_tcp_stream( client.clone(), stream, peer_addr, - self.local_addr.clone(), + self.local_addr, self.remote_addr.clone(), )); } @@ -136,7 +127,7 @@ impl Dns { client: Arc, mut stream: TcpStream, peer_addr: SocketAddr, - local_addr: Arc, + local_addr: SocketAddr, remote_addr: Arc
, ) -> io::Result<()> { let mut length_buf = [0u8; 2]; @@ -177,7 +168,7 @@ impl Dns { } }; - let respond_message = match client.resolve(message, &local_addr, &remote_addr).await { + let respond_message = match client.resolve(message, local_addr, &remote_addr).await { Ok(m) => m, Err(err) => { error!("dns tcp {} lookup error: {}", peer_addr, err); @@ -246,7 +237,7 @@ impl Dns { listener.clone(), peer_addr, message, - self.local_addr.clone(), + self.local_addr, self.remote_addr.clone(), )); } @@ -257,10 +248,10 @@ impl Dns { listener: Arc, peer_addr: SocketAddr, message: Message, - local_addr: Arc, + local_addr: SocketAddr, remote_addr: Arc
, ) -> io::Result<()> { - let respond_message = match client.resolve(message, &local_addr, &remote_addr).await { + let respond_message = match client.resolve(message, local_addr, &remote_addr).await { Ok(m) => m, Err(err) => { error!("dns udp {} lookup failed, error: {}", peer_addr, err); @@ -451,12 +442,7 @@ impl DnsClient { } } - async fn resolve( - &self, - request: Message, - local_addr: &NameServerAddr, - remote_addr: &Address, - ) -> io::Result { + async fn resolve(&self, request: Message, local_addr: SocketAddr, remote_addr: &Address) -> io::Result { let mut message = Message::new(); message.set_id(request.id()); message.set_recursion_desired(true); @@ -490,7 +476,7 @@ impl DnsClient { async fn acl_lookup( &self, query: &Query, - local_addr: &NameServerAddr, + local_addr: SocketAddr, remote_addr: &Address, ) -> (io::Result, bool) { // Start querying name servers @@ -671,7 +657,7 @@ impl DnsClient { } } - async fn lookup_local(&self, query: &Query, local_addr: &NameServerAddr) -> io::Result { + async fn lookup_local(&self, query: &Query, local_addr: SocketAddr) -> io::Result { let mut last_err = io::Error::new(ErrorKind::InvalidData, "resolve empty"); for _ in 0..self.attempts { @@ -686,53 +672,42 @@ impl DnsClient { Err(last_err) } - async fn lookup_local_inner(&self, query: &Query, local_addr: &NameServerAddr) -> io::Result { + async fn lookup_local_inner(&self, query: &Query, local_addr: SocketAddr) -> io::Result { let mut message = Message::new(); message.set_id(thread_rng().gen()); message.set_recursion_desired(true); message.add_query(query.clone()); - match *local_addr { - NameServerAddr::SocketAddr(ns) => { - let mut last_err = io::Error::new(ErrorKind::InvalidData, "resolve empty"); - - // Query UDP then TCP + let mut last_err = io::Error::new(ErrorKind::InvalidData, "resolve empty"); - if self.mode.enable_udp() { - match self - .client_cache - .lookup_udp_local(ns, message.clone(), self.context.connect_opts_ref()) - .await - { - Ok(msg) => return Ok(msg), - Err(err) => { - last_err = err.into(); - } - } - } + // Query UDP then TCP - if self.mode.enable_tcp() { - match self - .client_cache - .lookup_tcp_local(ns, message, self.context.connect_opts_ref()) - .await - { - Ok(msg) => return Ok(msg), - Err(err) => { - last_err = err.into(); - } - } + if self.mode.enable_udp() { + match self + .client_cache + .lookup_udp_local(local_addr, message.clone(), self.context.connect_opts_ref()) + .await + { + Ok(msg) => return Ok(msg), + Err(err) => { + last_err = err.into(); } - - Err(last_err) } + } - #[cfg(unix)] - NameServerAddr::UnixSocketAddr(ref path) => self + if self.mode.enable_tcp() { + match self .client_cache - .lookup_unix_stream(path, message) + .lookup_tcp_local(local_addr, message, self.context.connect_opts_ref()) .await - .map_err(From::from), + { + Ok(msg) => return Ok(msg), + Err(err) => { + last_err = err.into(); + } + } } + + Err(last_err) } } diff --git a/crates/shadowsocks-service/src/local/dns/upstream.rs b/crates/shadowsocks-service/src/local/dns/upstream.rs index 6b6c88d6d0b8..9b745ec876bf 100644 --- a/crates/shadowsocks-service/src/local/dns/upstream.rs +++ b/crates/shadowsocks-service/src/local/dns/upstream.rs @@ -21,7 +21,7 @@ use tokio::{ net::{TcpStream, UdpSocket}, time, }; -use trust_dns_proto::{ +use trust_dns_resolver::proto::{ error::{ProtoError, ProtoErrorKind}, op::Message, }; @@ -37,6 +37,7 @@ pub enum DnsClient { socket: UdpSocket, }, #[cfg(unix)] + #[allow(dead_code)] UnixStream { stream: UnixStream, }, diff --git a/crates/shadowsocks-service/src/local/loadbalancing/ping_balancer.rs b/crates/shadowsocks-service/src/local/loadbalancing/ping_balancer.rs index 468b301d62ac..835caac0b561 100644 --- a/crates/shadowsocks-service/src/local/loadbalancing/ping_balancer.rs +++ b/crates/shadowsocks-service/src/local/loadbalancing/ping_balancer.rs @@ -14,7 +14,7 @@ use std::{ use byte_string::ByteStr; use futures::future::{self, AbortHandle}; -use log::{debug, info, trace}; +use log::{debug, log, trace, Level}; use shadowsocks::relay::{ socks5::Address, tcprelay::proxy_stream::ProxyClientStream, @@ -71,7 +71,7 @@ impl PingBalancerBuilder { pub async fn build(self) -> (PingBalancer, impl Future) { assert!(!self.servers.is_empty(), "build PingBalancer without any servers"); - let balancer = PingBalancerInner { + let balancer_context = PingBalancerContext { servers: self.servers, best_tcp_idx: AtomicUsize::new(0), best_udp_idx: AtomicUsize::new(0), @@ -79,25 +79,29 @@ impl PingBalancerBuilder { mode: self.mode, }; - balancer.init_score().await; + balancer_context.init_score().await; - let shared = Arc::new(balancer); - let inner = shared.clone(); + let shared_context = Arc::new(balancer_context); - let (checker, abortable) = future::abortable(async move { shared.checker_task().await }); + let (checker, abortable) = { + let shared_context = shared_context.clone(); + future::abortable(async move { shared_context.checker_task().await }) + }; let checker = async move { let _ = checker.await; }; let balancer = PingBalancer { - inner, - abortable: Arc::new(abortable), + inner: Arc::new(PingBalancerInner { + context: shared_context, + abortable, + }), }; (balancer, checker) } } -struct PingBalancerInner { +struct PingBalancerContext { servers: Vec>, best_tcp_idx: AtomicUsize, best_udp_idx: AtomicUsize, @@ -105,7 +109,7 @@ struct PingBalancerInner { mode: Mode, } -impl PingBalancerInner { +impl PingBalancerContext { fn best_tcp_server(&self) -> Arc { self.servers[self.best_tcp_idx.load(Ordering::Relaxed)].clone() } @@ -115,7 +119,7 @@ impl PingBalancerInner { } } -impl PingBalancerInner { +impl PingBalancerContext { async fn init_score(&self) { assert!(!self.servers.is_empty(), "check PingBalancer without any servers"); @@ -141,7 +145,10 @@ impl PingBalancerInner { /// Check each servers' score and update the best server's index async fn check_once(&self, print_switch: bool) { - let mut vfut = Vec::with_capacity(self.servers.len()); + let mut vfut = match self.mode { + Mode::TcpAndUdp => Vec::with_capacity(self.servers.len() * 2), + Mode::TcpOnly | Mode::UdpOnly => Vec::with_capacity(self.servers.len()), + }; for server in self.servers.iter() { if self.mode.enable_tcp() { @@ -179,12 +186,18 @@ impl PingBalancerInner { } self.best_tcp_idx.store(best_idx, Ordering::Release); - if print_switch && best_idx != old_best_idx { - info!( + if best_idx != old_best_idx { + log!( + if print_switch { Level::Info } else { Level::Debug }, "switched best TCP server from {} to {}", self.servers[old_best_idx].server_config().addr(), self.servers[best_idx].server_config().addr() ); + } else { + debug!( + "kept best TCP server {}", + self.servers[old_best_idx].server_config().addr() + ); } } @@ -202,12 +215,18 @@ impl PingBalancerInner { } self.best_udp_idx.store(best_idx, Ordering::Release); - if print_switch && best_idx != old_best_idx { - info!( + if best_idx != old_best_idx { + log!( + if print_switch { Level::Info } else { Level::Debug }, "switched best UDP server from {} to {}", self.servers[old_best_idx].server_config().addr(), self.servers[best_idx].server_config().addr() ); + } else { + debug!( + "kept best UDP server {}", + self.servers[old_best_idx].server_config().addr() + ); } } } @@ -220,47 +239,52 @@ impl PingBalancerInner { } } -/// Balancer with active probing -#[derive(Clone)] -pub struct PingBalancer { - inner: Arc, - abortable: Arc, +struct PingBalancerInner { + context: Arc, + abortable: AbortHandle, } -impl Drop for PingBalancer { +impl Drop for PingBalancerInner { fn drop(&mut self) { self.abortable.abort(); + trace!("ping balancer stopped"); } } +/// Balancer with active probing +#[derive(Clone)] +pub struct PingBalancer { + inner: Arc, +} + impl PingBalancer { /// Get service context pub fn context(&self) -> Arc { - self.inner.context.clone() + self.inner.context.context.clone() } /// Get reference of the service context pub fn context_ref(&self) -> &ServiceContext { - self.inner.context.as_ref() + self.inner.context.context.as_ref() } /// Pick the best TCP server pub fn best_tcp_server(&self) -> Arc { - self.inner.best_tcp_server() + self.inner.context.best_tcp_server() } /// Pick the best UDP server pub fn best_udp_server(&self) -> Arc { - self.inner.best_udp_server() + self.inner.context.best_udp_server() } } impl Debug for PingBalancer { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("PingBalancer") - .field("servers", &self.inner.servers) - .field("best_tcp_idx", &self.inner.best_tcp_idx.load(Ordering::Relaxed)) - .field("best_udp_idx", &self.inner.best_udp_idx.load(Ordering::Relaxed)) + .field("servers", &self.inner.context.servers) + .field("best_tcp_idx", &self.inner.context.best_tcp_idx.load(Ordering::Relaxed)) + .field("best_udp_idx", &self.inner.context.best_udp_idx.load(Ordering::Relaxed)) .finish() } } diff --git a/crates/shadowsocks-service/src/local/loadbalancing/server_stat.rs b/crates/shadowsocks-service/src/local/loadbalancing/server_stat.rs index 88b33ab8cdc6..bdd7d5465837 100644 --- a/crates/shadowsocks-service/src/local/loadbalancing/server_stat.rs +++ b/crates/shadowsocks-service/src/local/loadbalancing/server_stat.rs @@ -76,8 +76,8 @@ impl ServerStat { let score = (nrtt * SCORE_RTT_WEIGHT + self.fail_rate * SCORE_FAIL_WEIGHT + nstdev * SCORE_STDEV_WEIGHT) / (SCORE_RTT_WEIGHT + SCORE_FAIL_WEIGHT + SCORE_STDEV_WEIGHT); - // Times 1000 converts to u64, for 0.001 precision - (score * 1000.0) as u64 + // Times 10000 converts to u64, for 0.0001 precision + (score * 10000.0) as u64 } pub fn push_score(&mut self, score: Score) -> u64 { diff --git a/crates/shadowsocks-service/src/local/mod.rs b/crates/shadowsocks-service/src/local/mod.rs index 7dca8e5b6f85..262de83f477e 100644 --- a/crates/shadowsocks-service/src/local/mod.rs +++ b/crates/shadowsocks-service/src/local/mod.rs @@ -9,7 +9,7 @@ use log::{error, trace, warn}; #[cfg(any(feature = "local-dns", feature = "trust-dns"))] use shadowsocks::dns_resolver::DnsResolver; use shadowsocks::{ - net::ConnectOpts, + net::{AcceptOpts, ConnectOpts}, plugin::{Plugin, PluginMode}, }; @@ -17,8 +17,6 @@ use crate::config::{Config, ConfigType, ProtocolType}; #[cfg(feature = "local-flow-stat")] use crate::net::FlowStat; -#[cfg(feature = "local-dns")] -use self::dns::dns_resolver::DnsResolver as LocalDnsResolver; use self::{ context::ServiceContext, loadbalancing::{PingBalancerBuilder, ServerIdent}, @@ -54,7 +52,8 @@ pub async fn run(mut config: Config) -> io::Result<()> { } let mut context = ServiceContext::new(); - context.set_connect_opts(ConnectOpts { + + let mut connect_opts = ConnectOpts { #[cfg(any(target_os = "linux", target_os = "android"))] fwmark: config.outbound_fwmark, @@ -65,32 +64,68 @@ pub async fn run(mut config: Config) -> io::Result<()> { bind_interface: config.outbound_bind_interface, ..Default::default() - }); - - #[cfg(feature = "local-dns")] - if let Some(ref ns) = config.local_dns_addr { - use crate::config::Mode; - - trace!("initializing direct DNS resolver for {}", ns); - - let mut resolver = LocalDnsResolver::new(ns.clone()); - resolver.set_mode(Mode::TcpAndUdp); - resolver.set_ipv6_first(config.ipv6_first); - resolver.set_connect_opts(context.connect_opts_ref().clone()); - context.set_dns_resolver(Arc::new(DnsResolver::custom_resolver(resolver))); - } - - #[cfg(feature = "trust-dns")] - if matches!(context.dns_resolver(), DnsResolver::System) { - match DnsResolver::trust_dns_resolver(config.dns, config.ipv6_first).await { + }; + connect_opts.tcp.send_buffer_size = config.outbound_send_buffer_size; + connect_opts.tcp.recv_buffer_size = config.outbound_recv_buffer_size; + context.set_connect_opts(connect_opts); + + let mut accept_opts = AcceptOpts::default(); + accept_opts.tcp.send_buffer_size = config.inbound_send_buffer_size; + accept_opts.tcp.recv_buffer_size = config.inbound_recv_buffer_size; + accept_opts.tcp.nodelay = config.no_delay; + + #[cfg(all(feature = "local-dns", feature = "trust-dns"))] + if let Some(socket_addr) = config.local_dns_addr { + use trust_dns_resolver::config::{NameServerConfig, Protocol, ResolverConfig}; + + trace!("initializing direct DNS resolver for {}", socket_addr); + + let mut resolver_config = ResolverConfig::new(); + + resolver_config.add_name_server(NameServerConfig { + socket_addr, + protocol: Protocol::Udp, + tls_dns_name: None, + trust_nx_responses: false, + #[cfg(feature = "dns-over-tls")] + tls_config: None, + }); + resolver_config.add_name_server(NameServerConfig { + socket_addr, + protocol: Protocol::Tcp, + tls_dns_name: None, + trust_nx_responses: false, + #[cfg(feature = "dns-over-tls")] + tls_config: None, + }); + + match DnsResolver::trust_dns_resolver(Some(resolver_config), config.ipv6_first).await { Ok(r) => { context.set_dns_resolver(Arc::new(r)); } Err(err) => { - warn!( - "initialize DNS resolver failed, fallback to system resolver, error: {}", - err + error!( + "initialize DNS resolver failed, nameserver: {}, error: {}", + socket_addr, err ); + return Err(err); + } + } + } + + #[cfg(feature = "trust-dns")] + if context.dns_resolver().is_system_resolver() { + if config.dns.is_some() || crate::hint_support_default_system_resolver() { + match DnsResolver::trust_dns_resolver(config.dns, config.ipv6_first).await { + Ok(r) => { + context.set_dns_resolver(Arc::new(r)); + } + Err(err) => { + warn!( + "initialize DNS resolver failed, fallback to system resolver, error: {}", + err + ); + } } } } @@ -190,9 +225,6 @@ pub async fn run(mut config: Config) -> io::Result<()> { let mut server = Dns::with_context(context.clone(), local_addr, remote_addr); server.set_mode(config.mode); - if config.no_delay { - server.set_nodelay(true); - } vfut.push(server.run(bind_addr, balancer.clone()).boxed()); } diff --git a/crates/shadowsocks-service/src/local/redir/tcprelay/mod.rs b/crates/shadowsocks-service/src/local/redir/tcprelay/mod.rs index d8c8a11b8aee..1b7431cb643d 100644 --- a/crates/shadowsocks-service/src/local/redir/tcprelay/mod.rs +++ b/crates/shadowsocks-service/src/local/redir/tcprelay/mod.rs @@ -3,7 +3,7 @@ use std::{io, net::SocketAddr, sync::Arc, time::Duration}; use log::{debug, error, info, trace}; -use shadowsocks::{lookup_then, relay::socks5::Address}; +use shadowsocks::{lookup_then, net::TcpListener as ShadowTcpListener, relay::socks5::Address}; use tokio::{ net::{TcpListener, TcpStream}, time, @@ -110,6 +110,8 @@ pub async fn run_tcp_redir( } }; + let listener = ShadowTcpListener::from_listener(listener, context.accept_opts()); + let actual_local_addr = listener.local_addr().expect("determine port bound to"); info!("shadowsocks TCP redirect listening on {}", actual_local_addr); diff --git a/crates/shadowsocks-service/src/local/redir/udprelay/mod.rs b/crates/shadowsocks-service/src/local/redir/udprelay/mod.rs index d7d225a3f70c..79c0d4749aa2 100644 --- a/crates/shadowsocks-service/src/local/redir/udprelay/mod.rs +++ b/crates/shadowsocks-service/src/local/redir/udprelay/mod.rs @@ -172,11 +172,14 @@ impl UdpRedir { struct UdpAssociation { assoc: Arc, + sender: mpsc::Sender<(SocketAddr, Bytes)>, } impl Drop for UdpAssociation { fn drop(&mut self) { - self.assoc.abortables.lock().abort_all(); + self.assoc.bypassed_ipv4_socket.lock().abort(); + self.assoc.bypassed_ipv6_socket.lock().abort(); + self.assoc.proxied_socket.lock().abort(); } } @@ -188,13 +191,12 @@ impl UdpAssociation { assoc_map: Arc>>, balancer: PingBalancer, ) -> UdpAssociation { - UdpAssociation { - assoc: UdpAssociationContext::new(context, redir_ty, peer_addr, assoc_map, balancer), - } + let (assoc, sender) = UdpAssociationContext::new(context, redir_ty, peer_addr, assoc_map, balancer); + UdpAssociation { assoc, sender } } fn try_send(&self, data: (SocketAddr, Bytes)) -> io::Result<()> { - if let Err(..) = self.assoc.sender.try_send(data) { + if let Err(..) = self.sender.try_send(data) { let err = io::Error::new(ErrorKind::Other, "udp relay channel full"); return Err(err); } @@ -202,34 +204,69 @@ impl UdpAssociation { } } -struct UdpAssociationTaskHandle { - abortables: Vec, - finished: bool, +enum UdpAssociationBypassState { + Empty, + Connected { + socket: Arc, + abortable: AbortHandle, + }, + Aborted, } -impl UdpAssociationTaskHandle { - fn new() -> UdpAssociationTaskHandle { - UdpAssociationTaskHandle { - abortables: Vec::new(), - finished: false, +impl Drop for UdpAssociationBypassState { + fn drop(&mut self) { + if let UdpAssociationBypassState::Connected { ref abortable, .. } = *self { + abortable.abort(); } } +} - fn push_abortable(&mut self, abortable: AbortHandle) { - if self.finished { - // Association is already finished. Kill it immediately. - abortable.abort(); - } else { - self.abortables.push(abortable); - } +impl UdpAssociationBypassState { + fn empty() -> UdpAssociationBypassState { + UdpAssociationBypassState::Empty + } + + fn set_connected(&mut self, socket: Arc, abortable: AbortHandle) { + *self = UdpAssociationBypassState::Connected { socket, abortable }; } - fn abort_all(&mut self) { - self.finished = true; - for abortable in &self.abortables { + fn abort(&mut self) { + *self = UdpAssociationBypassState::Aborted; + } +} + +enum UdpAssociationProxyState { + Empty, + Connected { + socket: Arc, + abortable: AbortHandle, + }, + Aborted, +} + +impl Drop for UdpAssociationProxyState { + fn drop(&mut self) { + if let UdpAssociationProxyState::Connected { ref abortable, .. } = *self { abortable.abort(); } - self.abortables.clear(); + } +} + +impl UdpAssociationProxyState { + fn empty() -> UdpAssociationProxyState { + UdpAssociationProxyState::Empty + } + + fn reset(&mut self) { + *self = UdpAssociationProxyState::Empty; + } + + fn set_connected(&mut self, socket: Arc, abortable: AbortHandle) { + *self = UdpAssociationProxyState::Connected { socket, abortable }; + } + + fn abort(&mut self) { + *self = UdpAssociationProxyState::Aborted; } } @@ -237,13 +274,11 @@ struct UdpAssociationContext { context: Arc, redir_ty: RedirType, peer_addr: SocketAddr, - sender: mpsc::Sender<(SocketAddr, Bytes)>, - bypassed_ipv4_socket: SpinMutex>>, - bypassed_ipv6_socket: SpinMutex>>, - proxied_socket: SpinMutex>>, + bypassed_ipv4_socket: SpinMutex, + bypassed_ipv6_socket: SpinMutex, + proxied_socket: SpinMutex, assoc_map: Arc>>, balancer: PingBalancer, - abortables: SpinMutex, } impl Drop for UdpAssociationContext { @@ -259,7 +294,7 @@ impl UdpAssociationContext { peer_addr: SocketAddr, assoc_map: Arc>>, balancer: PingBalancer, - ) -> Arc { + ) -> (Arc, mpsc::Sender<(SocketAddr, Bytes)>) { // Pending packets 1024 should be good enough for a server. // If there are plenty of packets stuck in the channel, dropping exccess packets is a good way to protect the server from // being OOM. @@ -269,23 +304,20 @@ impl UdpAssociationContext { context, redir_ty, peer_addr, - sender, - bypassed_ipv4_socket: SpinMutex::new(None), - bypassed_ipv6_socket: SpinMutex::new(None), - proxied_socket: SpinMutex::new(None), + bypassed_ipv4_socket: SpinMutex::new(UdpAssociationBypassState::empty()), + bypassed_ipv6_socket: SpinMutex::new(UdpAssociationBypassState::empty()), + proxied_socket: SpinMutex::new(UdpAssociationProxyState::empty()), assoc_map, balancer, - abortables: SpinMutex::new(UdpAssociationTaskHandle::new()), }); - let (l2r_task, l2r_abortable) = { + let l2r_task = { let assoc = assoc.clone(); - future::abortable(assoc.copy_l2r(receiver)) + assoc.copy_l2r(receiver) }; tokio::spawn(l2r_task); - assoc.abortables.lock().push_abortable(l2r_abortable); - assoc + (assoc, sender) } async fn copy_l2r(self: Arc, mut receiver: mpsc::Receiver<(SocketAddr, Bytes)>) { @@ -333,31 +365,46 @@ impl UdpAssociationContext { } async fn copy_bypassed_ipv4_l2r(self: Arc, target_addr: SocketAddr, data: &[u8]) -> io::Result<()> { - let mut bypassed_socket = self.bypassed_ipv4_socket.lock(); - - if bypassed_socket.is_none() { - // Initialize bypass task - - let socket = ShadowUdpSocket::bind_with_opts(&target_addr, self.context.connect_opts_ref()).await?; - let socket: Arc = Arc::new(socket.into()); - - let (r2l_fut, r2l_abortable) = { - let assoc = self.clone(); - future::abortable(assoc.copy_bypassed_r2l(socket.clone())) - }; + let socket = { + let mut handle = self.bypassed_ipv4_socket.lock(); + + match *handle { + UdpAssociationBypassState::Empty => { + // Create a new connection to proxy server + + let socket = + ShadowUdpSocket::connect_any_with_opts(&target_addr, self.context.connect_opts_ref()).await?; + let socket: Arc = Arc::new(socket.into()); + + let (r2l_fut, r2l_abortable) = { + let assoc = self.clone(); + future::abortable(assoc.copy_bypassed_r2l(socket.clone())) + }; + + // CLIENT <- REMOTE + tokio::spawn(r2l_fut); + debug!( + "created udp association for {} (bypassed) with {:?}", + self.peer_addr, + self.context.connect_opts_ref() + ); - // CLIENT <- REMOTE - tokio::spawn(r2l_fut); - debug!( - "created udp association for {} (bypassed) with {:?}", - self.peer_addr, - self.context.connect_opts_ref() - ); - *bypassed_socket = Some(socket); - self.abortables.lock().push_abortable(r2l_abortable); - } + handle.set_connected(socket.clone(), r2l_abortable); + socket + } + UdpAssociationBypassState::Connected { ref socket, .. } => socket.clone(), + UdpAssociationBypassState::Aborted => { + debug!( + "udp association for {} (bypassed) have been aborted, dropped packet {} bytes to {}", + self.peer_addr, + data.len(), + target_addr + ); + return Ok(()); + } + } + }; - let socket = bypassed_socket.as_ref().unwrap(); let n = socket.send_to(data, target_addr).await?; if n != data.len() { warn!( @@ -373,32 +420,46 @@ impl UdpAssociationContext { } async fn copy_bypassed_ipv6_l2r(self: Arc, target_addr: SocketAddr, data: &[u8]) -> io::Result<()> { - let mut bypassed_socket = self.bypassed_ipv6_socket.lock(); - - if bypassed_socket.is_none() { - // Initialize bypass task - - let socket = ShadowUdpSocket::bind_with_opts(&target_addr, self.context.connect_opts_ref()).await?; - let socket: Arc = Arc::new(socket.into()); - - let (r2l_fut, r2l_abortable) = { - let assoc = self.clone(); - future::abortable(assoc.copy_bypassed_r2l(socket.clone())) - }; - - // CLIENT <- REMOTE - tokio::spawn(r2l_fut); + let socket = { + let mut handle = self.bypassed_ipv6_socket.lock(); + + match *handle { + UdpAssociationBypassState::Empty => { + // Create a new connection to proxy server + + let socket = + ShadowUdpSocket::connect_any_with_opts(&target_addr, self.context.connect_opts_ref()).await?; + let socket: Arc = Arc::new(socket.into()); + + let (r2l_fut, r2l_abortable) = { + let assoc = self.clone(); + future::abortable(assoc.copy_bypassed_r2l(socket.clone())) + }; + + // CLIENT <- REMOTE + tokio::spawn(r2l_fut); + debug!( + "created udp association for {} (bypassed) with {:?}", + self.peer_addr, + self.context.connect_opts_ref() + ); - debug!( - "created udp association for {} (bypassed) with {:?}", - self.peer_addr, - self.context.connect_opts_ref() - ); - *bypassed_socket = Some(socket); - self.abortables.lock().push_abortable(r2l_abortable); - } + handle.set_connected(socket.clone(), r2l_abortable); + socket + } + UdpAssociationBypassState::Connected { ref socket, .. } => socket.clone(), + UdpAssociationBypassState::Aborted => { + debug!( + "udp association for {} (bypassed) have been aborted, dropped packet {} bytes to {}", + self.peer_addr, + data.len(), + target_addr + ); + return Ok(()); + } + } + }; - let socket = bypassed_socket.as_ref().unwrap(); let n = socket.send_to(data, target_addr).await?; if n != data.len() { warn!( @@ -414,42 +475,82 @@ impl UdpAssociationContext { } async fn copy_proxied_l2r(self: Arc, target_addr: SocketAddr, data: &[u8]) -> io::Result<()> { - let mut proxied_socket = self.proxied_socket.lock(); - - if proxied_socket.is_none() { - // Initialize proxied socket - - let server = self.balancer.best_udp_server(); - let svr_cfg = server.server_config(); - - let socket = - ProxySocket::connect_with_opts(self.context.context(), svr_cfg, self.context.connect_opts_ref()) - .await?; - let socket = MonProxySocket::from_socket(socket, self.context.flow_stat()); - let socket = Arc::new(socket); - - let (r2l_fut, r2l_abortable) = { - let assoc = self.clone(); - future::abortable(assoc.copy_proxied_r2l(socket.clone())) + let mut last_err = io::Error::new(ErrorKind::Other, "udp relay sendto failed after retry"); + + for tried in 0..3 { + let socket = { + let mut handle = self.proxied_socket.lock(); + + match *handle { + UdpAssociationProxyState::Empty => { + // Create a new connection to proxy server + + let server = self.balancer.best_udp_server(); + let svr_cfg = server.server_config(); + + let socket = ProxySocket::connect_with_opts( + self.context.context(), + svr_cfg, + self.context.connect_opts_ref(), + ) + .await?; + let socket = MonProxySocket::from_socket(socket, self.context.flow_stat()); + let socket = Arc::new(socket); + + let (r2l_fut, r2l_abortable) = { + let assoc = self.clone(); + future::abortable(assoc.copy_proxied_r2l(socket.clone())) + }; + + // CLIENT <- REMOTE + tokio::spawn(r2l_fut); + + debug!( + "created udp association for {} <-> {} (proxied) with {:?}", + self.peer_addr, + svr_cfg.addr(), + self.context.connect_opts_ref() + ); + + handle.set_connected(socket.clone(), r2l_abortable); + socket + } + UdpAssociationProxyState::Connected { ref socket, .. } => socket.clone(), + UdpAssociationProxyState::Aborted => { + debug!( + "udp association for {} (proxied) have been aborted, dropped packet {} bytes to {}", + self.peer_addr, + data.len(), + target_addr + ); + return Ok(()); + } + } }; - // CLIENT <- REMOTE - tokio::spawn(r2l_fut); + let target_addr = Address::from(target_addr); + match socket.send(&target_addr, data).await { + Ok(..) => return Ok(()), + Err(err) => { + debug!( + "{} -> {} (proxied) sending {} bytes failed, tried: {}, error: {}", + self.peer_addr, + target_addr, + data.len(), + tried + 1, + err + ); + last_err = err; - debug!( - "created udp association for {} <-> {} (proxied) with {:?}", - self.peer_addr, - svr_cfg.addr(), - self.context.connect_opts_ref() - ); - *proxied_socket = Some(socket); - self.abortables.lock().push_abortable(r2l_abortable); - } + // Reset for reconnecting + self.proxied_socket.lock().reset(); - let socket = proxied_socket.as_ref().unwrap(); - socket.send(&Address::from(target_addr), data).await?; + tokio::task::yield_now().await; + } + } + } - Ok(()) + Err(last_err) } async fn copy_proxied_r2l(self: Arc, outbound: Arc) -> io::Result<()> { @@ -462,6 +563,12 @@ impl UdpAssociationContext { n } Err(err) => { + // Socket that connected to remote server returns an error, it should be ECONNREFUSED in most cases. + // That indicates that the association on the server side have been dropped. + // + // There is no point to keep this socket. Drop it immediately. + self.proxied_socket.lock().reset(); + error!( "udp failed to receive from proxied outbound socket, peer_addr: {}, error: {}", self.peer_addr, err diff --git a/crates/shadowsocks-service/src/local/socks/server/mod.rs b/crates/shadowsocks-service/src/local/socks/server/mod.rs index 47915de145d8..e410d4d057a0 100644 --- a/crates/shadowsocks-service/src/local/socks/server/mod.rs +++ b/crates/shadowsocks-service/src/local/socks/server/mod.rs @@ -4,7 +4,7 @@ use std::{io, net::SocketAddr, sync::Arc, time::Duration}; use futures::{future, FutureExt}; use log::{error, info}; -use shadowsocks::lookup_then; +use shadowsocks::{lookup_then, net::TcpListener as ShadowTcpListener}; use tokio::{ net::{TcpListener, TcpStream}, time, @@ -112,6 +112,7 @@ impl Socks { .1 } }; + let listener = ShadowTcpListener::from_listener(listener, self.context.accept_opts()); info!("shadowsocks socks TCP listening on {}", listener.local_addr()?); diff --git a/crates/shadowsocks-service/src/local/socks/server/socks5/udprelay.rs b/crates/shadowsocks-service/src/local/socks/server/socks5/udprelay.rs index cd97b55440e4..fb800c083627 100644 --- a/crates/shadowsocks-service/src/local/socks/server/socks5/udprelay.rs +++ b/crates/shadowsocks-service/src/local/socks/server/socks5/udprelay.rs @@ -177,11 +177,14 @@ impl Socks5UdpServer { struct UdpAssociation { assoc: Arc, + sender: mpsc::Sender<(Address, Bytes)>, } impl Drop for UdpAssociation { fn drop(&mut self) { - self.assoc.abortables.lock().abort_all(); + self.assoc.bypassed_ipv4_socket.lock().abort(); + self.assoc.bypassed_ipv6_socket.lock().abort(); + self.assoc.proxied_socket.lock().abort(); } } @@ -193,13 +196,12 @@ impl UdpAssociation { assoc_map: Arc>>, balancer: PingBalancer, ) -> UdpAssociation { - UdpAssociation { - assoc: UdpAssociationContext::new(context, inbound, peer_addr, assoc_map, balancer), - } + let (assoc, sender) = UdpAssociationContext::new(context, inbound, peer_addr, assoc_map, balancer); + UdpAssociation { assoc, sender } } fn try_send(&self, data: (Address, Bytes)) -> io::Result<()> { - if let Err(..) = self.assoc.sender.try_send(data) { + if let Err(..) = self.sender.try_send(data) { let err = io::Error::new(ErrorKind::Other, "udp relay channel full"); return Err(err); } @@ -207,34 +209,69 @@ impl UdpAssociation { } } -struct UdpAssociationTaskHandle { - abortables: Vec, - finished: bool, +enum UdpAssociationBypassState { + Empty, + Connected { + socket: Arc, + abortable: AbortHandle, + }, + Aborted, } -impl UdpAssociationTaskHandle { - fn new() -> UdpAssociationTaskHandle { - UdpAssociationTaskHandle { - abortables: Vec::new(), - finished: false, +impl Drop for UdpAssociationBypassState { + fn drop(&mut self) { + if let UdpAssociationBypassState::Connected { ref abortable, .. } = *self { + abortable.abort(); } } +} - fn push_abortable(&mut self, abortable: AbortHandle) { - if self.finished { - // Association is already finished. Kill it immediately. - abortable.abort(); - } else { - self.abortables.push(abortable); - } +impl UdpAssociationBypassState { + fn empty() -> UdpAssociationBypassState { + UdpAssociationBypassState::Empty } - fn abort_all(&mut self) { - self.finished = true; - for abortable in &self.abortables { + fn set_connected(&mut self, socket: Arc, abortable: AbortHandle) { + *self = UdpAssociationBypassState::Connected { socket, abortable }; + } + + fn abort(&mut self) { + *self = UdpAssociationBypassState::Aborted; + } +} + +enum UdpAssociationProxyState { + Empty, + Connected { + socket: Arc, + abortable: AbortHandle, + }, + Aborted, +} + +impl Drop for UdpAssociationProxyState { + fn drop(&mut self) { + if let UdpAssociationProxyState::Connected { ref abortable, .. } = *self { abortable.abort(); } - self.abortables.clear(); + } +} + +impl UdpAssociationProxyState { + fn empty() -> UdpAssociationProxyState { + UdpAssociationProxyState::Empty + } + + fn reset(&mut self) { + *self = UdpAssociationProxyState::Empty; + } + + fn set_connected(&mut self, socket: Arc, abortable: AbortHandle) { + *self = UdpAssociationProxyState::Connected { socket, abortable }; + } + + fn abort(&mut self) { + *self = UdpAssociationProxyState::Aborted; } } @@ -242,13 +279,11 @@ struct UdpAssociationContext { context: Arc, inbound: Arc, peer_addr: SocketAddr, - sender: mpsc::Sender<(Address, Bytes)>, - bypassed_ipv4_socket: SpinMutex>>, - bypassed_ipv6_socket: SpinMutex>>, - proxied_socket: SpinMutex>>, + bypassed_ipv4_socket: SpinMutex, + bypassed_ipv6_socket: SpinMutex, + proxied_socket: SpinMutex, assoc_map: Arc>>, balancer: PingBalancer, - abortables: SpinMutex, } impl Drop for UdpAssociationContext { @@ -264,7 +299,7 @@ impl UdpAssociationContext { peer_addr: SocketAddr, assoc_map: Arc>>, balancer: PingBalancer, - ) -> Arc { + ) -> (Arc, mpsc::Sender<(Address, Bytes)>) { // Pending packets 1024 should be good enough for a server. // If there are plenty of packets stuck in the channel, dropping exccess packets is a good way to protect the server from // being OOM. @@ -274,23 +309,20 @@ impl UdpAssociationContext { context, inbound, peer_addr, - sender, - bypassed_ipv4_socket: SpinMutex::new(None), - bypassed_ipv6_socket: SpinMutex::new(None), - proxied_socket: SpinMutex::new(None), + bypassed_ipv4_socket: SpinMutex::new(UdpAssociationBypassState::empty()), + bypassed_ipv6_socket: SpinMutex::new(UdpAssociationBypassState::empty()), + proxied_socket: SpinMutex::new(UdpAssociationProxyState::empty()), assoc_map, balancer, - abortables: SpinMutex::new(UdpAssociationTaskHandle::new()), }); - let (l2r_task, l2r_abortable) = { + let l2r_task = { let assoc = assoc.clone(); - future::abortable(assoc.copy_l2r(receiver)) + assoc.copy_l2r(receiver) }; tokio::spawn(l2r_task); - assoc.abortables.lock().push_abortable(l2r_abortable); - assoc + (assoc, sender) } async fn copy_l2r(self: Arc, mut receiver: mpsc::Receiver<(Address, Bytes)>) { @@ -349,31 +381,46 @@ impl UdpAssociationContext { } async fn copy_bypassed_ipv4_l2r(self: Arc, target_addr: SocketAddr, data: &[u8]) -> io::Result<()> { - let mut bypassed_socket = self.bypassed_ipv4_socket.lock(); - - if bypassed_socket.is_none() { - // Initialize bypass task - - let socket = ShadowUdpSocket::bind_with_opts(&target_addr, self.context.connect_opts_ref()).await?; - let socket: Arc = Arc::new(socket.into()); - - let (r2l_fut, r2l_abortable) = { - let assoc = self.clone(); - future::abortable(assoc.copy_bypassed_r2l(socket.clone())) - }; + let socket = { + let mut handle = self.bypassed_ipv4_socket.lock(); + + match *handle { + UdpAssociationBypassState::Empty => { + // Create a new connection to proxy server + + let socket = + ShadowUdpSocket::connect_any_with_opts(&target_addr, self.context.connect_opts_ref()).await?; + let socket: Arc = Arc::new(socket.into()); + + let (r2l_fut, r2l_abortable) = { + let assoc = self.clone(); + future::abortable(assoc.copy_bypassed_r2l(socket.clone())) + }; + + // CLIENT <- REMOTE + tokio::spawn(r2l_fut); + debug!( + "created udp association for {} (bypassed) with {:?}", + self.peer_addr, + self.context.connect_opts_ref() + ); - // CLIENT <- REMOTE - tokio::spawn(r2l_fut); - debug!( - "created udp association for {} (bypassed) with {:?}", - self.peer_addr, - self.context.connect_opts_ref() - ); - *bypassed_socket = Some(socket); - self.abortables.lock().push_abortable(r2l_abortable); - } + handle.set_connected(socket.clone(), r2l_abortable); + socket + } + UdpAssociationBypassState::Connected { ref socket, .. } => socket.clone(), + UdpAssociationBypassState::Aborted => { + debug!( + "udp association for {} (bypassed) have been aborted, dropped packet {} bytes to {}", + self.peer_addr, + data.len(), + target_addr + ); + return Ok(()); + } + } + }; - let socket = bypassed_socket.as_ref().unwrap(); let n = socket.send_to(data, target_addr).await?; if n != data.len() { warn!( @@ -389,32 +436,46 @@ impl UdpAssociationContext { } async fn copy_bypassed_ipv6_l2r(self: Arc, target_addr: SocketAddr, data: &[u8]) -> io::Result<()> { - let mut bypassed_socket = self.bypassed_ipv6_socket.lock(); - - if bypassed_socket.is_none() { - // Initialize bypass task - - let socket = ShadowUdpSocket::bind_with_opts(&target_addr, self.context.connect_opts_ref()).await?; - let socket: Arc = Arc::new(socket.into()); - - let (r2l_fut, r2l_abortable) = { - let assoc = self.clone(); - future::abortable(assoc.copy_bypassed_r2l(socket.clone())) - }; - - // CLIENT <- REMOTE - tokio::spawn(r2l_fut); + let socket = { + let mut handle = self.bypassed_ipv6_socket.lock(); + + match *handle { + UdpAssociationBypassState::Empty => { + // Create a new connection to proxy server + + let socket = + ShadowUdpSocket::connect_any_with_opts(&target_addr, self.context.connect_opts_ref()).await?; + let socket: Arc = Arc::new(socket.into()); + + let (r2l_fut, r2l_abortable) = { + let assoc = self.clone(); + future::abortable(assoc.copy_bypassed_r2l(socket.clone())) + }; + + // CLIENT <- REMOTE + tokio::spawn(r2l_fut); + debug!( + "created udp association for {} (bypassed) with {:?}", + self.peer_addr, + self.context.connect_opts_ref() + ); - debug!( - "created udp association for {} (bypassed) with {:?}", - self.peer_addr, - self.context.connect_opts_ref() - ); - *bypassed_socket = Some(socket); - self.abortables.lock().push_abortable(r2l_abortable); - } + handle.set_connected(socket.clone(), r2l_abortable); + socket + } + UdpAssociationBypassState::Connected { ref socket, .. } => socket.clone(), + UdpAssociationBypassState::Aborted => { + debug!( + "udp association for {} (bypassed) have been aborted, dropped packet {} bytes to {}", + self.peer_addr, + data.len(), + target_addr + ); + return Ok(()); + } + } + }; - let socket = bypassed_socket.as_ref().unwrap(); let n = socket.send_to(data, target_addr).await?; if n != data.len() { warn!( @@ -430,42 +491,81 @@ impl UdpAssociationContext { } async fn copy_proxied_l2r(self: Arc, target_addr: &Address, data: &[u8]) -> io::Result<()> { - let mut proxied_socket = self.proxied_socket.lock(); - - if proxied_socket.is_none() { - // Initialize proxied socket - - let server = self.balancer.best_udp_server(); - let svr_cfg = server.server_config(); - - let socket = - ProxySocket::connect_with_opts(self.context.context(), svr_cfg, self.context.connect_opts_ref()) - .await?; - let socket = MonProxySocket::from_socket(socket, self.context.flow_stat()); - let socket = Arc::new(socket); - - let (r2l_fut, r2l_abortable) = { - let assoc = self.clone(); - future::abortable(assoc.copy_proxied_r2l(socket.clone())) + let mut last_err = io::Error::new(ErrorKind::Other, "udp relay sendto failed after retry"); + + for tried in 0..3 { + let socket = { + let mut handle = self.proxied_socket.lock(); + + match *handle { + UdpAssociationProxyState::Empty => { + // Create a new connection to proxy server + + let server = self.balancer.best_udp_server(); + let svr_cfg = server.server_config(); + + let socket = ProxySocket::connect_with_opts( + self.context.context(), + svr_cfg, + self.context.connect_opts_ref(), + ) + .await?; + let socket = MonProxySocket::from_socket(socket, self.context.flow_stat()); + let socket = Arc::new(socket); + + let (r2l_fut, r2l_abortable) = { + let assoc = self.clone(); + future::abortable(assoc.copy_proxied_r2l(socket.clone())) + }; + + // CLIENT <- REMOTE + tokio::spawn(r2l_fut); + + debug!( + "created udp association for {} <-> {} (proxied) with {:?}", + self.peer_addr, + svr_cfg.addr(), + self.context.connect_opts_ref() + ); + + handle.set_connected(socket.clone(), r2l_abortable); + socket + } + UdpAssociationProxyState::Connected { ref socket, .. } => socket.clone(), + UdpAssociationProxyState::Aborted => { + debug!( + "udp association for {} (proxied) have been aborted, dropped packet {} bytes to {}", + self.peer_addr, + data.len(), + target_addr + ); + return Ok(()); + } + } }; - // CLIENT <- REMOTE - tokio::spawn(r2l_fut); + match socket.send(target_addr, data).await { + Ok(..) => return Ok(()), + Err(err) => { + debug!( + "{} -> {} (proxied) sending {} bytes failed, tried: {}, error: {}", + self.peer_addr, + target_addr, + data.len(), + tried + 1, + err + ); + last_err = err; + + // Reset for reconnecting + self.proxied_socket.lock().reset(); - debug!( - "created udp association for {} <-> {} (proxied) with {:?}", - self.peer_addr, - svr_cfg.addr(), - self.context.connect_opts_ref() - ); - *proxied_socket = Some(socket); - self.abortables.lock().push_abortable(r2l_abortable); + tokio::task::yield_now().await; + } + } } - let socket = proxied_socket.as_ref().unwrap(); - socket.send(target_addr, data).await?; - - Ok(()) + Err(last_err) } async fn copy_proxied_r2l(self: Arc, outbound: Arc) -> io::Result<()> { @@ -479,6 +579,12 @@ impl UdpAssociationContext { n } Err(err) => { + // Socket that connected to remote server returns an error, it should be ECONNREFUSED in most cases. + // That indicates that the association on the server side have been dropped. + // + // There is no point to keep this socket. Drop it immediately. + self.proxied_socket.lock().reset(); + error!( "udp failed to receive from proxied outbound socket, peer_addr: {}, error: {}", self.peer_addr, err diff --git a/crates/shadowsocks-service/src/local/tunnel/tcprelay.rs b/crates/shadowsocks-service/src/local/tunnel/tcprelay.rs index 1208d3ac4a3b..3aa435962d7b 100644 --- a/crates/shadowsocks-service/src/local/tunnel/tcprelay.rs +++ b/crates/shadowsocks-service/src/local/tunnel/tcprelay.rs @@ -3,7 +3,7 @@ use std::{io, net::SocketAddr, sync::Arc, time::Duration}; use log::{error, info, trace}; -use shadowsocks::{lookup_then, relay::socks5::Address}; +use shadowsocks::{lookup_then, net::TcpListener as ShadowTcpListener, relay::socks5::Address}; use tokio::{ net::{TcpListener, TcpStream}, time, @@ -35,6 +35,7 @@ pub async fn run_tcp_tunnel( .1 } }; + let listener = ShadowTcpListener::from_listener(listener, context.accept_opts()); info!("shadowsocks TCP tunnel listening on {}", listener.local_addr()?); diff --git a/crates/shadowsocks-service/src/local/tunnel/udprelay.rs b/crates/shadowsocks-service/src/local/tunnel/udprelay.rs index 56922d26f247..28220a0977b5 100644 --- a/crates/shadowsocks-service/src/local/tunnel/udprelay.rs +++ b/crates/shadowsocks-service/src/local/tunnel/udprelay.rs @@ -15,6 +15,7 @@ use shadowsocks::{ udprelay::{ProxySocket, MAXIMUM_UDP_PAYLOAD_SIZE}, }, }; +use spin::Mutex as SpinMutex; use tokio::{ net::UdpSocket, sync::{mpsc, Mutex}, @@ -125,112 +126,263 @@ impl UdpTunnel { data: &[u8], ) -> io::Result<()> { let mut assoc_map = self.assoc_map.lock().await; - let assoc = match assoc_map.entry(peer_addr) { - Entry::Occupied(occ) => occ.into_mut(), + match assoc_map.entry(peer_addr) { + Entry::Occupied(occ) => { + let assoc = occ.into_mut(); + assoc.try_send(Bytes::copy_from_slice(data)) + } Entry::Vacant(vac) => { - let server = balancer.best_udp_server(); - let svr_cfg = server.server_config(); - - let socket = - ProxySocket::connect_with_opts(self.context.context(), svr_cfg, self.context.connect_opts_ref()) - .await?; - let socket = MonProxySocket::from_socket(socket, self.context.flow_stat()); - let socket = Arc::new(socket); - - // Pending packets 1024 should be good enough for a server. - // If there are plenty of packets stuck in the channel, dropping exccess packets is a good way to protect the server from - // being OOM. - let (sender, receiver) = mpsc::channel(1024); - - let (r2l_fut, r2l_abortable) = future::abortable(UdpAssociation::copy_r2l( + let assoc = vac.insert(UdpAssociation::new( + self.context.clone(), listener.clone(), peer_addr, - socket.clone(), forward_addr.clone(), self.assoc_map.clone(), + balancer.clone(), )); + trace!("created udp association for {}", peer_addr); + assoc.try_send(Bytes::copy_from_slice(data)) + } + } + } +} - // CLIENT <- REMOTE - tokio::spawn(r2l_fut); - - // CLIENT -> REMOTE - let l2r_fut = UdpAssociation::copy_l2r(socket, peer_addr, forward_addr.clone(), receiver); - tokio::spawn(l2r_fut); +struct UdpAssociation { + sender: mpsc::Sender, + assoc: Arc, +} - debug!( - "established udp tunnel {} <-> {} with {:?}", - peer_addr, - forward_addr, - self.context.connect_opts_ref() - ); +impl Drop for UdpAssociation { + fn drop(&mut self) { + self.assoc.proxied_socket.lock().abort(); + } +} - vac.insert(UdpAssociation { - sender, - peer_addr, - r2l_abortable, - }) - } - }; +impl UdpAssociation { + fn new( + context: Arc, + inbound: Arc, + peer_addr: SocketAddr, + forward_addr: Address, + assoc_map: Arc>>, + balancer: PingBalancer, + ) -> UdpAssociation { + let (assoc, sender) = + UdpAssociationContext::new(context, inbound, peer_addr, forward_addr, assoc_map, balancer); + UdpAssociation { sender, assoc } + } - if let Err(..) = assoc.sender.try_send(Bytes::copy_from_slice(data)) { + fn try_send(&self, data: Bytes) -> io::Result<()> { + if let Err(..) = self.sender.try_send(data) { let err = io::Error::new(ErrorKind::Other, "udp relay channel full"); return Err(err); } - Ok(()) } } -struct UdpAssociation { - sender: mpsc::Sender, +enum UdpAssociationState { + Empty, + Connected { + socket: Arc, + abortable: AbortHandle, + }, + Aborted, +} + +impl Drop for UdpAssociationState { + fn drop(&mut self) { + if let UdpAssociationState::Connected { ref abortable, .. } = *self { + abortable.abort(); + } + } +} + +impl UdpAssociationState { + fn empty() -> UdpAssociationState { + UdpAssociationState::Empty + } + + fn reset(&mut self) { + *self = UdpAssociationState::Empty; + } + + fn set_connected(&mut self, socket: Arc, abortable: AbortHandle) { + *self = UdpAssociationState::Connected { socket, abortable }; + } + + fn abort(&mut self) { + *self = UdpAssociationState::Aborted; + } +} + +struct UdpAssociationContext { + context: Arc, + inbound: Arc, peer_addr: SocketAddr, - r2l_abortable: AbortHandle, + forward_addr: Address, + proxied_socket: SpinMutex, + assoc_map: Arc>>, + balancer: PingBalancer, } -impl Drop for UdpAssociation { +impl Drop for UdpAssociationContext { fn drop(&mut self) { - self.r2l_abortable.abort(); trace!("udp tunnel for {} is closed", self.peer_addr); } } -impl UdpAssociation { - async fn copy_l2r( - outbound: Arc, +impl UdpAssociationContext { + fn new( + context: Arc, + inbound: Arc, peer_addr: SocketAddr, - target_addr: Address, - mut receiver: mpsc::Receiver, - ) { + forward_addr: Address, + assoc_map: Arc>>, + balancer: PingBalancer, + ) -> (Arc, mpsc::Sender) { + // Pending packets 1024 should be good enough for a server. + // If there are plenty of packets stuck in the channel, dropping exccess packets is a good way to protect the server from + // being OOM. + let (sender, receiver) = mpsc::channel(1024); + + let assoc = Arc::new(UdpAssociationContext { + context, + inbound, + peer_addr, + forward_addr, + proxied_socket: SpinMutex::new(UdpAssociationState::empty()), + assoc_map, + balancer, + }); + + let l2r_task = { + let assoc = assoc.clone(); + assoc.copy_l2r(receiver) + }; + tokio::spawn(l2r_task); + + (assoc, sender) + } + + async fn copy_l2r(self: Arc, mut receiver: mpsc::Receiver) { while let Some(data) = receiver.recv().await { - if let Err(err) = outbound.send(&target_addr, &data).await { - error!("udp failed to send to {} outbound socket, error: {}", target_addr, err); + if let Err(err) = self.clone().copy_proxied_l2r(&data).await { + error!( + "udp failed to send to {} outbound socket, error: {}", + self.forward_addr, err + ); } else { - trace!("udp relay {} -> {} with {} bytes", peer_addr, target_addr, data.len()); + trace!( + "udp relay {} -> {} with {} bytes", + self.peer_addr, + self.forward_addr, + data.len() + ); } } } - async fn copy_r2l( - inbound: Arc, - peer_addr: SocketAddr, - outbound: Arc, - target_addr: Address, - assoc_map: Arc>>, - ) -> io::Result<()> { + async fn copy_proxied_l2r(self: Arc, data: &[u8]) -> io::Result<()> { + let mut last_err = io::Error::new(ErrorKind::Other, "udp relay sendto failed after retry"); + let target_addr = &self.forward_addr; + + for tried in 0..3 { + let socket = { + let mut handle = self.proxied_socket.lock(); + + match *handle { + UdpAssociationState::Empty => { + // Create a new connection to proxy server + + let server = self.balancer.best_udp_server(); + let svr_cfg = server.server_config(); + + let socket = ProxySocket::connect_with_opts( + self.context.context(), + svr_cfg, + self.context.connect_opts_ref(), + ) + .await?; + let socket = MonProxySocket::from_socket(socket, self.context.flow_stat()); + let socket = Arc::new(socket); + + let (r2l_fut, r2l_abortable) = { + let assoc = self.clone(); + future::abortable(assoc.copy_proxied_r2l(socket.clone())) + }; + + // CLIENT <- REMOTE + tokio::spawn(r2l_fut); + + debug!( + "created udp association for {} <-> {} (proxied) with {:?}", + self.peer_addr, + svr_cfg.addr(), + self.context.connect_opts_ref() + ); + + handle.set_connected(socket.clone(), r2l_abortable); + socket + } + UdpAssociationState::Connected { ref socket, .. } => socket.clone(), + UdpAssociationState::Aborted => { + debug!( + "udp association for {} (proxied) have been aborted, dropped packet {} bytes to {}", + self.peer_addr, + data.len(), + target_addr + ); + return Ok(()); + } + } + }; + + match socket.send(target_addr, data).await { + Ok(..) => return Ok(()), + Err(err) => { + debug!( + "{} -> {} (proxied) sending {} bytes failed, tried: {}, error: {}", + self.peer_addr, + target_addr, + data.len(), + tried + 1, + err + ); + last_err = err; + + // Reset for reconnecting + self.proxied_socket.lock().reset(); + + tokio::task::yield_now().await; + } + } + } + + Err(last_err) + } + + async fn copy_proxied_r2l(self: Arc, outbound: Arc) -> io::Result<()> { let mut buffer = [0u8; MAXIMUM_UDP_PAYLOAD_SIZE]; loop { let (n, _) = match outbound.recv(&mut buffer).await { Ok(n) => { // Keep association alive in map - let _ = assoc_map.lock().await.get(&peer_addr); + let _ = self.assoc_map.lock().await.get(&self.peer_addr); n } Err(err) => { + // Socket that connected to remote server returns an error, it should be ECONNREFUSED in most cases. + // That indicates that the association on the server side have been dropped. + // + // There is no point to keep this socket. Drop it immediately. + self.proxied_socket.lock().reset(); + error!( "udp failed to receive from {} outbound socket, error: {}", - target_addr, err + self.forward_addr, err ); - time::sleep(Duration::from_secs(0)).await; + time::sleep(Duration::from_secs(1)).await; continue; } }; @@ -238,14 +390,19 @@ impl UdpAssociation { let data = &buffer[..n]; // Send back to client - if let Err(err) = inbound.send_to(data, peer_addr).await { + if let Err(err) = self.inbound.send_to(data, self.peer_addr).await { warn!( "udp failed to send back to client {}, from target {}, error: {}", - peer_addr, target_addr, err + self.peer_addr, self.forward_addr, err ); } - trace!("udp relay {} <- {} with {} bytes", peer_addr, target_addr, data.len()); + trace!( + "udp relay {} <- {} with {} bytes", + self.peer_addr, + self.forward_addr, + data.len() + ); } } } diff --git a/crates/shadowsocks-service/src/manager/mod.rs b/crates/shadowsocks-service/src/manager/mod.rs index 8577b7391882..f4df64b1aec5 100644 --- a/crates/shadowsocks-service/src/manager/mod.rs +++ b/crates/shadowsocks-service/src/manager/mod.rs @@ -7,7 +7,10 @@ use std::io::{self, ErrorKind}; use std::sync::Arc; use log::{trace, warn}; -use shadowsocks::{config::ServerAddr, net::ConnectOpts}; +use shadowsocks::{ + config::ServerAddr, + net::{AcceptOpts, ConnectOpts}, +}; use crate::config::{Config, ConfigType}; @@ -34,14 +37,14 @@ pub async fn run(config: Config) -> io::Result<()> { manager.set_nodelay(config.no_delay); #[cfg(feature = "trust-dns")] - { + if config.dns.is_some() || crate::hint_support_default_system_resolver() { use shadowsocks::dns_resolver::DnsResolver; let resolver = Arc::new(DnsResolver::trust_dns_resolver(config.dns, config.ipv6_first).await?); manager.set_dns_resolver(resolver); } - let connect_opts = ConnectOpts { + let mut connect_opts = ConnectOpts { #[cfg(any(target_os = "linux", target_os = "android"))] fwmark: config.outbound_fwmark, @@ -64,7 +67,18 @@ pub async fn run(config: Config) -> io::Result<()> { ..Default::default() }; + + connect_opts.tcp.send_buffer_size = config.outbound_send_buffer_size; + connect_opts.tcp.recv_buffer_size = config.outbound_recv_buffer_size; + connect_opts.tcp.nodelay = config.no_delay; + + let mut accept_opts = AcceptOpts::default(); + accept_opts.tcp.send_buffer_size = config.inbound_send_buffer_size; + accept_opts.tcp.recv_buffer_size = config.inbound_recv_buffer_size; + accept_opts.tcp.nodelay = config.no_delay; + manager.set_connect_opts(connect_opts); + manager.set_accept_opts(accept_opts); if let Some(c) = config.udp_max_associations { manager.set_udp_capacity(c); diff --git a/crates/shadowsocks-service/src/manager/server.rs b/crates/shadowsocks-service/src/manager/server.rs index d57f210ad4be..fd71baef303a 100644 --- a/crates/shadowsocks-service/src/manager/server.rs +++ b/crates/shadowsocks-service/src/manager/server.rs @@ -21,7 +21,7 @@ use shadowsocks::{ RemoveResponse, StatRequest, }, - net::ConnectOpts, + net::{AcceptOpts, ConnectOpts}, plugin::PluginConfig, ManagerListener, ServerAddr, @@ -54,6 +54,7 @@ pub struct Manager { svr_cfg: ManagerConfig, mode: Mode, connect_opts: ConnectOpts, + accept_opts: AcceptOpts, udp_expiry_duration: Option, udp_capacity: Option, nodelay: bool, @@ -74,6 +75,7 @@ impl Manager { svr_cfg, mode: Mode::TcpOnly, connect_opts: ConnectOpts::default(), + accept_opts: AcceptOpts::default(), udp_expiry_duration: None, udp_capacity: None, nodelay: false, @@ -86,6 +88,11 @@ impl Manager { self.connect_opts = opts; } + /// Set `AcceptOpts` + pub fn set_accept_opts(&mut self, opts: AcceptOpts) { + self.accept_opts = opts; + } + /// Set UDP association's expiry duration pub fn set_udp_expiry_duration(&mut self, d: Duration) { self.udp_expiry_duration = Some(d); @@ -174,6 +181,7 @@ impl Manager { let mut server = Server::new(svr_cfg.clone()); server.set_connect_opts(self.connect_opts.clone()); + server.set_accept_opts(self.accept_opts.clone()); server.set_dns_resolver(self.context.dns_resolver().clone()); if let Some(d) = self.udp_expiry_duration { @@ -184,10 +192,6 @@ impl Manager { server.set_udp_capacity(c); } - if self.nodelay { - server.set_nodelay(true); - } - server.set_mode(mode.unwrap_or(self.mode)); if let Some(ref acl) = self.acl { diff --git a/crates/shadowsocks-service/src/server/mod.rs b/crates/shadowsocks-service/src/server/mod.rs index 4d51f6b8ca73..3539bfe978f2 100644 --- a/crates/shadowsocks-service/src/server/mod.rs +++ b/crates/shadowsocks-service/src/server/mod.rs @@ -9,7 +9,10 @@ use futures::{future, FutureExt}; use log::{trace, warn}; #[cfg(feature = "trust-dns")] use shadowsocks::dns_resolver::DnsResolver; -use shadowsocks::{config::ServerAddr, net::ConnectOpts}; +use shadowsocks::{ + config::ServerAddr, + net::{AcceptOpts, ConnectOpts}, +}; use crate::config::{Config, ConfigType}; @@ -37,7 +40,7 @@ pub async fn run(config: Config) -> io::Result<()> { let mut servers = Vec::new(); - let connect_opts = ConnectOpts { + let mut connect_opts = ConnectOpts { #[cfg(any(target_os = "linux", target_os = "android"))] fwmark: config.outbound_fwmark, @@ -61,8 +64,23 @@ pub async fn run(config: Config) -> io::Result<()> { ..Default::default() }; + connect_opts.tcp.send_buffer_size = config.outbound_send_buffer_size; + connect_opts.tcp.recv_buffer_size = config.outbound_recv_buffer_size; + connect_opts.tcp.nodelay = config.no_delay; + + let mut accept_opts = AcceptOpts::default(); + accept_opts.tcp.send_buffer_size = config.inbound_send_buffer_size; + accept_opts.tcp.recv_buffer_size = config.inbound_recv_buffer_size; + accept_opts.tcp.nodelay = config.no_delay; + #[cfg(feature = "trust-dns")] - let resolver = Arc::new(DnsResolver::trust_dns_resolver(config.dns, config.ipv6_first).await?); + let resolver = if config.dns.is_some() || crate::hint_support_default_system_resolver() { + Some(Arc::new( + DnsResolver::trust_dns_resolver(config.dns, config.ipv6_first).await?, + )) + } else { + None + }; let acl = config.acl.map(Arc::new); @@ -70,9 +88,13 @@ pub async fn run(config: Config) -> io::Result<()> { let mut server = Server::new(svr_cfg); #[cfg(feature = "trust-dns")] - server.set_dns_resolver(resolver.clone()); + if let Some(ref r) = resolver { + server.set_dns_resolver(r.clone()); + } server.set_connect_opts(connect_opts.clone()); + server.set_accept_opts(accept_opts.clone()); + if let Some(c) = config.udp_max_associations { server.set_udp_capacity(c); } @@ -83,9 +105,6 @@ pub async fn run(config: Config) -> io::Result<()> { if let Some(ref m) = config.manager { server.set_manager_addr(m.addr.clone()); } - if config.no_delay { - server.set_nodelay(true); - } if let Some(ref acl) = acl { server.set_acl(acl.clone()); diff --git a/crates/shadowsocks-service/src/server/server.rs b/crates/shadowsocks-service/src/server/server.rs index 7a81a49272e6..3a4f2f6218e8 100644 --- a/crates/shadowsocks-service/src/server/server.rs +++ b/crates/shadowsocks-service/src/server/server.rs @@ -12,7 +12,7 @@ use log::{error, trace}; use shadowsocks::{ config::{ManagerAddr, ServerConfig}, dns_resolver::DnsResolver, - net::ConnectOpts, + net::{AcceptOpts, ConnectOpts}, plugin::{Plugin, PluginMode}, ManagerClient, }; @@ -30,7 +30,7 @@ pub struct Server { udp_expiry_duration: Option, udp_capacity: Option, manager_addr: Option, - nodelay: bool, + accept_opts: AcceptOpts, } impl Server { @@ -48,7 +48,7 @@ impl Server { udp_expiry_duration: None, udp_capacity: None, manager_addr: None, - nodelay: false, + accept_opts: AcceptOpts::default(), } } @@ -93,11 +93,6 @@ impl Server { &self.svr_cfg } - /// Set `TCP_NODELAY` - pub fn set_nodelay(&mut self, nodelay: bool) { - self.nodelay = nodelay; - } - /// Set customized DNS resolver pub fn set_dns_resolver(&mut self, resolver: Arc) { let context = Arc::get_mut(&mut self.context).expect("cannot set DNS resolver on a shared context"); @@ -110,6 +105,11 @@ impl Server { context.set_acl(acl); } + /// Set `AcceptOpts` for accepting new connections + pub fn set_accept_opts(&mut self, opts: AcceptOpts) { + self.accept_opts = opts; + } + /// Start serving pub async fn run(mut self) -> io::Result<()> { let mut vfut = Vec::new(); @@ -156,7 +156,7 @@ impl Server { } async fn run_tcp_server(&self) -> io::Result<()> { - let server = TcpServer::new(self.context.clone(), self.nodelay); + let server = TcpServer::new(self.context.clone(), self.accept_opts.clone()); server.run(&self.svr_cfg).await } diff --git a/crates/shadowsocks-service/src/server/tcprelay.rs b/crates/shadowsocks-service/src/server/tcprelay.rs index 722ae532e856..e4a51d2449cc 100644 --- a/crates/shadowsocks-service/src/server/tcprelay.rs +++ b/crates/shadowsocks-service/src/server/tcprelay.rs @@ -6,7 +6,7 @@ use futures::future::{self, Either}; use log::{debug, error, info, trace, warn}; use shadowsocks::{ crypto::v1::CipherKind, - net::TcpStream as OutboundTcpStream, + net::{AcceptOpts, TcpStream as OutboundTcpStream}, relay::{ socks5::Address, tcprelay::{ @@ -25,16 +25,16 @@ use super::context::ServiceContext; pub struct TcpServer { context: Arc, - nodelay: bool, + accept_opts: AcceptOpts, } impl TcpServer { - pub fn new(context: Arc, nodelay: bool) -> TcpServer { - TcpServer { context, nodelay } + pub fn new(context: Arc, accept_opts: AcceptOpts) -> TcpServer { + TcpServer { context, accept_opts } } pub async fn run(self, svr_cfg: &ServerConfig) -> io::Result<()> { - let listener = ProxyListener::bind(self.context.context(), svr_cfg).await?; + let listener = ProxyListener::bind_with_opts(self.context.context(), svr_cfg, self.accept_opts).await?; info!( "shadowsocks tcp server listening on {}, inbound address {}", @@ -43,26 +43,20 @@ impl TcpServer { ); loop { - let (local_stream, peer_addr) = match listener - .accept_map(|s| MonProxyStream::from_stream(s, self.context.flow_stat())) - .await - { - Ok(s) => s, - Err(err) => { - error!("tcp server accept failed with error: {}", err); - time::sleep(Duration::from_secs(1)).await; - continue; - } - }; - - if self.nodelay { - let stream = local_stream.get_ref().get_ref(); - stream.set_nodelay(true)?; - } + let flow_stat = self.context.flow_stat(); + + let (local_stream, peer_addr) = + match listener.accept_map(|s| MonProxyStream::from_stream(s, flow_stat)).await { + Ok(s) => s, + Err(err) => { + error!("tcp server accept failed with error: {}", err); + time::sleep(Duration::from_secs(1)).await; + continue; + } + }; let client = TcpServerClient { context: self.context.clone(), - nodelay: self.nodelay, method: svr_cfg.method(), peer_addr, stream: local_stream, @@ -79,7 +73,6 @@ impl TcpServer { struct TcpServerClient { context: Arc, - nodelay: bool, method: CipherKind, peer_addr: SocketAddr, stream: ProxyServerStream>, @@ -123,10 +116,6 @@ impl TcpServerClient { ) .await?; - if self.nodelay { - remote_stream.set_nodelay(true)?; - } - let (mut lr, mut lw) = self.stream.into_split(); let (mut rr, mut rw) = remote_stream.split(); diff --git a/crates/shadowsocks-service/src/server/udprelay.rs b/crates/shadowsocks-service/src/server/udprelay.rs index 82a8e19e573d..1ca2044d3935 100644 --- a/crates/shadowsocks-service/src/server/udprelay.rs +++ b/crates/shadowsocks-service/src/server/udprelay.rs @@ -133,11 +133,13 @@ impl UdpServer { struct UdpAssociation { assoc: Arc, + sender: mpsc::Sender<(Address, Bytes)>, } impl Drop for UdpAssociation { fn drop(&mut self) { - self.assoc.abortables.lock().abort_all(); + self.assoc.outbound_ipv4_socket.lock().abort(); + self.assoc.outbound_ipv6_socket.lock().abort(); } } @@ -148,13 +150,12 @@ impl UdpAssociation { peer_addr: SocketAddr, assoc_map: Arc>>, ) -> UdpAssociation { - UdpAssociation { - assoc: UdpAssociationContext::new(context, inbound, peer_addr, assoc_map), - } + let (assoc, sender) = UdpAssociationContext::new(context, inbound, peer_addr, assoc_map); + UdpAssociation { assoc, sender } } fn try_send(&self, data: (Address, Bytes)) -> io::Result<()> { - if let Err(..) = self.assoc.sender.try_send(data) { + if let Err(..) = self.sender.try_send(data) { let err = io::Error::new(ErrorKind::Other, "udp relay channel full"); return Err(err); } @@ -162,34 +163,34 @@ impl UdpAssociation { } } -struct UdpAssociationTaskHandle { - abortables: Vec, - finished: bool, +enum UdpAssociationState { + Empty, + Connected { + socket: Arc, + abortable: AbortHandle, + }, + Aborted, } -impl UdpAssociationTaskHandle { - fn new() -> UdpAssociationTaskHandle { - UdpAssociationTaskHandle { - abortables: Vec::new(), - finished: false, +impl Drop for UdpAssociationState { + fn drop(&mut self) { + if let UdpAssociationState::Connected { ref abortable, .. } = *self { + abortable.abort(); } } +} - fn push_abortable(&mut self, abortable: AbortHandle) { - if self.finished { - // Association is already finished. Kill it immediately. - abortable.abort(); - } else { - self.abortables.push(abortable); - } +impl UdpAssociationState { + fn empty() -> UdpAssociationState { + UdpAssociationState::Empty } - fn abort_all(&mut self) { - self.finished = true; - for abortable in &self.abortables { - abortable.abort(); - } - self.abortables.clear(); + fn set_connected(&mut self, socket: Arc, abortable: AbortHandle) { + *self = UdpAssociationState::Connected { socket, abortable }; + } + + fn abort(&mut self) { + *self = UdpAssociationState::Aborted; } } @@ -197,11 +198,9 @@ struct UdpAssociationContext { context: Arc, inbound: Arc, peer_addr: SocketAddr, - sender: mpsc::Sender<(Address, Bytes)>, - outbound_ipv4_socket: SpinMutex>>, - outbound_ipv6_socket: SpinMutex>>, + outbound_ipv4_socket: SpinMutex, + outbound_ipv6_socket: SpinMutex, assoc_map: Arc>>, - abortables: SpinMutex, target_cache: Mutex>, } @@ -217,7 +216,7 @@ impl UdpAssociationContext { inbound: Arc, peer_addr: SocketAddr, assoc_map: Arc>>, - ) -> Arc { + ) -> (Arc, mpsc::Sender<(Address, Bytes)>) { // Pending packets 1024 should be good enough for a server. // If there are plenty of packets stuck in the channel, dropping exccess packets is a good way to protect the server from // being OOM. @@ -227,11 +226,9 @@ impl UdpAssociationContext { context, inbound, peer_addr, - sender, - outbound_ipv4_socket: SpinMutex::new(None), - outbound_ipv6_socket: SpinMutex::new(None), + outbound_ipv4_socket: SpinMutex::new(UdpAssociationState::empty()), + outbound_ipv6_socket: SpinMutex::new(UdpAssociationState::empty()), assoc_map, - abortables: SpinMutex::new(UdpAssociationTaskHandle::new()), // Cache for remembering the original Address of target, // when recv_from a SocketAddr, we have to know whch Address that client was originally requested. // @@ -239,14 +236,13 @@ impl UdpAssociationContext { target_cache: Mutex::new(LruCache::with_capacity(64)), }); - let (l2r_task, l2r_abortable) = { + let l2r_task = { let assoc = assoc.clone(); - future::abortable(assoc.copy_l2r(receiver)) + assoc.copy_l2r(receiver) }; tokio::spawn(l2r_task); - assoc.abortables.lock().push_abortable(l2r_abortable); - assoc + (assoc, sender) } async fn copy_l2r(self: Arc, mut receiver: mpsc::Receiver<(Address, Bytes)>) { @@ -295,32 +291,47 @@ impl UdpAssociationContext { } async fn copy_ipv4_l2r_dispatch(self: Arc, target_addr: SocketAddr, data: &[u8]) -> io::Result<()> { - let mut outbound = self.outbound_ipv4_socket.lock(); - - if outbound.is_none() { - // Initialize bypass task - - let socket = OutboundUdpSocket::bind_with_opts(&target_addr, self.context.connect_opts_ref()).await?; - let socket: Arc = Arc::new(socket.into()); - - let (r2l_fut, r2l_abortable) = { - let assoc = self.clone(); - future::abortable(assoc.copy_r2l(socket.clone())) - }; + let outbound = { + let mut handle = self.outbound_ipv4_socket.lock(); + + match *handle { + UdpAssociationState::Empty => { + // Create a new connection to proxy server + + let socket = + OutboundUdpSocket::connect_any_with_opts(&target_addr, self.context.connect_opts_ref()).await?; + let socket: Arc = Arc::new(socket.into()); + + let (r2l_fut, r2l_abortable) = { + let assoc = self.clone(); + future::abortable(assoc.copy_r2l(socket.clone())) + }; + + // CLIENT <- REMOTE + tokio::spawn(r2l_fut); + debug!( + "created udp association for {} with {:?}", + self.peer_addr, + self.context.connect_opts_ref() + ); - // CLIENT <- REMOTE - tokio::spawn(r2l_fut); - debug!( - "created udp association for {} with {:?}", - self.peer_addr, - self.context.connect_opts_ref() - ); - *outbound = Some(socket); - self.abortables.lock().push_abortable(r2l_abortable); - } + handle.set_connected(socket.clone(), r2l_abortable); + socket + } + UdpAssociationState::Connected { ref socket, .. } => socket.clone(), + UdpAssociationState::Aborted => { + debug!( + "udp association for {} (bypassed) have been aborted, dropped packet {} bytes to {}", + self.peer_addr, + data.len(), + target_addr + ); + return Ok(()); + } + } + }; - let socket = outbound.as_ref().unwrap(); - let n = socket.send_to(data, target_addr).await?; + let n = outbound.send_to(data, target_addr).await?; if n != data.len() { warn!( "{} -> {} sent {} bytes != expected {} bytes", @@ -335,32 +346,47 @@ impl UdpAssociationContext { } async fn copy_ipv6_l2r_dispatch(self: Arc, target_addr: SocketAddr, data: &[u8]) -> io::Result<()> { - let mut outbound = self.outbound_ipv6_socket.lock(); - - if outbound.is_none() { - // Initialize bypass task - - let socket = OutboundUdpSocket::bind_with_opts(&target_addr, self.context.connect_opts_ref()).await?; - let socket: Arc = Arc::new(socket.into()); - - let (r2l_fut, r2l_abortable) = { - let assoc = self.clone(); - future::abortable(assoc.copy_r2l(socket.clone())) - }; + let outbound = { + let mut handle = self.outbound_ipv6_socket.lock(); + + match *handle { + UdpAssociationState::Empty => { + // Create a new connection to proxy server + + let socket = + OutboundUdpSocket::connect_any_with_opts(&target_addr, self.context.connect_opts_ref()).await?; + let socket: Arc = Arc::new(socket.into()); + + let (r2l_fut, r2l_abortable) = { + let assoc = self.clone(); + future::abortable(assoc.copy_r2l(socket.clone())) + }; + + // CLIENT <- REMOTE + tokio::spawn(r2l_fut); + debug!( + "created udp association for {} with {:?}", + self.peer_addr, + self.context.connect_opts_ref() + ); - // CLIENT <- REMOTE - tokio::spawn(r2l_fut); - debug!( - "created udp association for {} with {:?}", - self.peer_addr, - self.context.connect_opts_ref() - ); - *outbound = Some(socket); - self.abortables.lock().push_abortable(r2l_abortable); - } + handle.set_connected(socket.clone(), r2l_abortable); + socket + } + UdpAssociationState::Connected { ref socket, .. } => socket.clone(), + UdpAssociationState::Aborted => { + debug!( + "udp association for {} (bypassed) have been aborted, dropped packet {} bytes to {}", + self.peer_addr, + data.len(), + target_addr + ); + return Ok(()); + } + } + }; - let socket = outbound.as_ref().unwrap(); - let n = socket.send_to(data, target_addr).await?; + let n = outbound.send_to(data, target_addr).await?; if n != data.len() { warn!( "{} -> {} sent {} bytes != expected {} bytes", diff --git a/crates/shadowsocks/Cargo.toml b/crates/shadowsocks/Cargo.toml index e57f85f0ae70..8668444999ce 100644 --- a/crates/shadowsocks/Cargo.toml +++ b/crates/shadowsocks/Cargo.toml @@ -25,7 +25,7 @@ trust-dns = ["trust-dns-resolver"] log = "0.4" libc = "0.2" -bytes = "0.6" +bytes = "1.0" cfg-if = "1" byte_string = "1.0" base64 = "0.13" @@ -45,7 +45,7 @@ async-trait = "0.1" mio = "0.7" socket2 = "0.3" -tokio = { version = "0.3.1", features = ["io-util", "macros", "net", "parking_lot", "process", "rt"] } +tokio = { version = "1.0", features = ["io-util", "macros", "net", "parking_lot", "process", "rt"] } trust-dns-resolver = { git = "https://github.com/bluejekyll/trust-dns.git", branch = "main", optional = true } diff --git a/crates/shadowsocks/src/dns_resolver/resolver.rs b/crates/shadowsocks/src/dns_resolver/resolver.rs index 0fa9f60e84b2..f1e220fe040a 100644 --- a/crates/shadowsocks/src/dns_resolver/resolver.rs +++ b/crates/shadowsocks/src/dns_resolver/resolver.rs @@ -4,11 +4,11 @@ use std::{ fmt::{self, Debug}, io::{self, Error, ErrorKind}, net::SocketAddr, - sync::atomic::{AtomicBool, Ordering}, + time::Instant, }; use async_trait::async_trait; -use log::{trace, warn}; +use log::{log_enabled, trace, Level}; use tokio::net::lookup_host; #[cfg(feature = "trust-dns")] use trust_dns_resolver::{config::ResolverConfig, TokioAsyncResolver}; @@ -33,7 +33,7 @@ pub enum DnsResolver { impl Default for DnsResolver { fn default() -> DnsResolver { - DnsResolver::System + DnsResolver::system_resolver() } } @@ -105,57 +105,122 @@ impl DnsResolver { /// Resolve address into `SocketAddr`s pub async fn resolve<'a>(&self, addr: &'a str, port: u16) -> io::Result + 'a> { - match *self { - DnsResolver::System => { - static TOKIO_USED: AtomicBool = AtomicBool::new(false); - if !TOKIO_USED.swap(true, Ordering::Relaxed) { - warn!("Tokio resolver is used. Performance might deteriorate."); - } + struct ResolverLogger<'x, 'y> { + resolver: &'x DnsResolver, + addr: &'y str, + port: u16, + start_time: Option, + } - trace!("DNS resolving {}:{} with tokio", addr, port); + impl<'x, 'y> ResolverLogger<'x, 'y> { + fn new(resolver: &'x DnsResolver, addr: &'y str, port: u16) -> ResolverLogger<'x, 'y> { + let start_time = if log_enabled!(Level::Trace) { + Some(Instant::now()) + } else { + None + }; + + ResolverLogger { + resolver, + addr, + port, + start_time, + } + } + } - match lookup_host((addr, port)).await { - Ok(v) => Ok(EitherResolved::Tokio(v)), - Err(err) => { - let err = Error::new( - ErrorKind::Other, - format!("dns resolve {}:{} error: {}", addr, port, err), - ); - Err(err) + impl<'x, 'y> Drop for ResolverLogger<'x, 'y> { + fn drop(&mut self) { + match self.start_time { + Some(start_time) => { + let end_time = Instant::now(); + let elapsed = end_time - start_time; + + match *self.resolver { + DnsResolver::System => { + trace!( + "DNS resolved {}:{} with tokio {}s", + self.addr, + self.port, + elapsed.as_secs_f32() + ); + } + #[cfg(feature = "trust-dns")] + DnsResolver::TrustDns(..) => { + trace!( + "DNS resolved {}:{} with trust-dns {}s", + self.addr, + self.port, + elapsed.as_secs_f32() + ); + } + DnsResolver::Custom(..) => { + trace!( + "DNS resolved {}:{} with customized {}s", + self.addr, + self.port, + elapsed.as_secs_f32() + ); + } + } } + None => match *self.resolver { + DnsResolver::System => { + trace!("DNS resolved {}:{} with tokio", self.addr, self.port); + } + #[cfg(feature = "trust-dns")] + DnsResolver::TrustDns(..) => { + trace!("DNS resolved {}:{} with trust-dns", self.addr, self.port); + } + DnsResolver::Custom(..) => { + trace!("DNS resolved {}:{} with customized", self.addr, self.port); + } + }, } } + } + + let _log_guard = ResolverLogger::new(self, addr, port); + + match *self { + DnsResolver::System => match lookup_host((addr, port)).await { + Ok(v) => Ok(EitherResolved::Tokio(v)), + Err(err) => { + let err = Error::new( + ErrorKind::Other, + format!("dns resolve {}:{} error: {}", addr, port, err), + ); + Err(err) + } + }, #[cfg(feature = "trust-dns")] - DnsResolver::TrustDns(ref resolver) => { - trace!("DNS resolving {}:{} with trust-dns", addr, port); - - match resolver.lookup_ip(addr).await { - Ok(lookup_result) => Ok(EitherResolved::TrustDns( - lookup_result.into_iter().map(move |ip| SocketAddr::new(ip, port)), - )), - Err(err) => { - let err = Error::new( - ErrorKind::Other, - format!("dns resolve {}:{} error: {}", addr, port, err), - ); - Err(err) - } + DnsResolver::TrustDns(ref resolver) => match resolver.lookup_ip(addr).await { + Ok(lookup_result) => Ok(EitherResolved::TrustDns( + lookup_result.into_iter().map(move |ip| SocketAddr::new(ip, port)), + )), + Err(err) => { + let err = Error::new( + ErrorKind::Other, + format!("dns resolve {}:{} error: {}", addr, port, err), + ); + Err(err) } - } - DnsResolver::Custom(ref resolver) => { - trace!("DNS resolving {}:{} with customized", addr, port); - - match resolver.resolve(addr, port).await { - Ok(v) => Ok(EitherResolved::Custom(v.into_iter())), - Err(err) => { - let err = Error::new( - ErrorKind::Other, - format!("dns resolve {}:{} error: {}", addr, port, err), - ); - Err(err) - } + }, + DnsResolver::Custom(ref resolver) => match resolver.resolve(addr, port).await { + Ok(v) => Ok(EitherResolved::Custom(v.into_iter())), + Err(err) => { + let err = Error::new( + ErrorKind::Other, + format!("dns resolve {}:{} error: {}", addr, port, err), + ); + Err(err) } - } + }, } } + + /// Check if currently using system resolver + pub fn is_system_resolver(&self) -> bool { + matches!(*self, DnsResolver::System) + } } diff --git a/crates/shadowsocks/src/dns_resolver/trust_dns_resolver.rs b/crates/shadowsocks/src/dns_resolver/trust_dns_resolver.rs index 6acdcfb7a597..9d4e692691ea 100644 --- a/crates/shadowsocks/src/dns_resolver/trust_dns_resolver.rs +++ b/crates/shadowsocks/src/dns_resolver/trust_dns_resolver.rs @@ -1,15 +1,15 @@ //! Asynchronous DNS resolver -use std::io; - +use cfg_if::cfg_if; use log::{error, trace}; use trust_dns_resolver::{ config::{LookupIpStrategy, ResolverConfig, ResolverOpts}, + error::ResolveResult, TokioAsyncResolver, }; /// Create a `trust-dns` asynchronous DNS resolver -pub async fn create_resolver(dns: Option, ipv6_first: bool) -> io::Result { +pub async fn create_resolver(dns: Option, ipv6_first: bool) -> ResolveResult { let mut resolver_opts = ResolverOpts::default(); if ipv6_first { @@ -28,48 +28,44 @@ pub async fn create_resolver(dns: Option, ipv6_first: bool) -> i } // To make this independent, if targeting macOS, BSD, Linux, or Windows, we can use the system's configuration - #[cfg(any(unix, windows))] + // Android doesn't have /etc/resolv.conf. None => { - use trust_dns_resolver::{name_server::TokioHandle, system_conf::read_system_conf}; + cfg_if! { + if #[cfg(any(all(unix, not(target_os = "android")), windows))] { + use trust_dns_resolver::{name_server::TokioHandle, system_conf::read_system_conf}; - // use the system resolver configuration - let (config, mut opts) = match read_system_conf() { - Ok(o) => o, - Err(err) => { - error!("failed to initialize DNS resolver with system-config, error: {}", err); + // use the system resolver configuration + let (config, mut opts) = match read_system_conf() { + Ok(o) => o, + Err(err) => { + error!("failed to initialize DNS resolver with system-config, error: {}", err); - // From::from is required because on error type is different on Windows - #[allow(clippy::useless_conversion)] - return Err(From::from(err)); - } - }; + // From::from is required because on error type is different on Windows + #[allow(clippy::useless_conversion)] + return Err(From::from(err)); + } + }; - // NOTE: timeout will be set by config (for example, /etc/resolv.conf on UNIX-like system) - // - // Only ip_strategy should be changed - if ipv6_first { - opts.ip_strategy = LookupIpStrategy::Ipv6thenIpv4; - } + // NOTE: timeout will be set by config (for example, /etc/resolv.conf on UNIX-like system) + // + // Only ip_strategy should be changed + if ipv6_first { + opts.ip_strategy = LookupIpStrategy::Ipv6thenIpv4; + } - trace!( - "initializing DNS resolver with system-config {:?} opts {:?}", - config, - opts - ); + trace!( + "initializing DNS resolver with system-config {:?} opts {:?}", + config, + opts + ); - TokioAsyncResolver::new(config, opts, TokioHandle) - } + TokioAsyncResolver::new(config, opts, TokioHandle) + } else { + use trust_dns_resolver::error::ResolveError; - #[cfg(not(any(unix, windows)))] - None => { - // Get a new resolver with the google nameservers as the upstream recursive resolvers - trace!( - "initializing DNS resolver with google-config {:?} opts {:?}", - ResolverConfig::google(), - resolver_opts - ); - TokioAsyncResolver::tokio(ResolverConfig::google(), resolver_opts) + Err(ResolveError::from("current platform doesn't support trust-dns resolver with system configured".to_owned())) + } + } } } - .map_err(From::from) } diff --git a/crates/shadowsocks/src/manager/datagram.rs b/crates/shadowsocks/src/manager/datagram.rs index 656679bb8142..8dfa8c5265ac 100644 --- a/crates/shadowsocks/src/manager/datagram.rs +++ b/crates/shadowsocks/src/manager/datagram.rs @@ -11,8 +11,7 @@ use tokio::net::{unix::SocketAddr as UnixSocketAddr, UnixDatagram}; use crate::{ config::ManagerAddr, context::Context, - net::ConnectOpts, - relay::sys::{create_outbound_udp_socket, create_udp_socket}, + net::{ConnectOpts, UdpSocket as ShadowUdpSocket}, }; /// Address accepted from Manager @@ -57,11 +56,13 @@ impl ManagerDatagram { /// Create a `ManagerDatagram` binding to requested `bind_addr` pub async fn bind(context: &Context, bind_addr: &ManagerAddr) -> io::Result { match *bind_addr { - ManagerAddr::SocketAddr(ref saddr) => Ok(ManagerDatagram::UdpDatagram(create_udp_socket(saddr).await?)), + ManagerAddr::SocketAddr(ref saddr) => { + Ok(ManagerDatagram::UdpDatagram(ShadowUdpSocket::bind(saddr).await?.into())) + } ManagerAddr::DomainName(ref dname, port) => { - let (_, socket) = lookup_then!(context, dname, port, |saddr| { create_udp_socket(&saddr).await })?; + let (_, socket) = lookup_then!(context, dname, port, |saddr| { ShadowUdpSocket::bind(&saddr).await })?; - Ok(ManagerDatagram::UdpDatagram(socket)) + Ok(ManagerDatagram::UdpDatagram(socket.into())) } #[cfg(unix)] ManagerAddr::UnixSocketAddr(ref path) => { @@ -100,10 +101,8 @@ impl ManagerDatagram { } async fn connect_socket_addr(sa: SocketAddr, connect_opts: &ConnectOpts) -> io::Result { - let socket = create_outbound_udp_socket(&sa, connect_opts).await?; - socket.connect(sa).await?; - - Ok(ManagerDatagram::UdpDatagram(socket)) + let socket = ShadowUdpSocket::connect_with_opts(&sa, connect_opts).await?; + Ok(ManagerDatagram::UdpDatagram(socket.into())) } /// Receives data from the socket. diff --git a/crates/shadowsocks/src/net/mod.rs b/crates/shadowsocks/src/net/mod.rs index 4cd7cc28e1cf..e135a72ddbbf 100644 --- a/crates/shadowsocks/src/net/mod.rs +++ b/crates/shadowsocks/src/net/mod.rs @@ -1,7 +1,40 @@ //! Network wrappers for shadowsocks' specific requirements -pub use self::{connect_opt::ConnectOpts, tcp::TcpStream, udp::UdpSocket}; +use std::net::SocketAddr; -mod connect_opt; +pub use self::{ + option::{AcceptOpts, ConnectOpts}, + tcp::{TcpListener, TcpStream}, + udp::UdpSocket, +}; + +mod option; pub mod tcp; pub mod udp; + +/// Address family `AF_INET`, `AF_INET6` +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum AddrFamily { + /// `AF_INET` + Ipv4, + /// `AF_INET6` + Ipv6, +} + +impl From<&SocketAddr> for AddrFamily { + fn from(addr: &SocketAddr) -> AddrFamily { + match *addr { + SocketAddr::V4(..) => AddrFamily::Ipv4, + SocketAddr::V6(..) => AddrFamily::Ipv6, + } + } +} + +impl From for AddrFamily { + fn from(addr: SocketAddr) -> AddrFamily { + match addr { + SocketAddr::V4(..) => AddrFamily::Ipv4, + SocketAddr::V6(..) => AddrFamily::Ipv6, + } + } +} diff --git a/crates/shadowsocks/src/net/connect_opt.rs b/crates/shadowsocks/src/net/option.rs similarity index 63% rename from crates/shadowsocks/src/net/connect_opt.rs rename to crates/shadowsocks/src/net/option.rs index fca1cb97f3f9..7e0dbe2f4844 100644 --- a/crates/shadowsocks/src/net/connect_opt.rs +++ b/crates/shadowsocks/src/net/option.rs @@ -4,6 +4,29 @@ use std::ffi::OsString; use std::net::IpAddr; +/// Options for connecting to TCP remote server +#[derive(Debug, Clone)] +pub struct TcpSocketOpts { + /// TCP socket's `SO_SNDBUF` + pub send_buffer_size: Option, + + /// TCP socket's `SO_RCVBUF` + pub recv_buffer_size: Option, + + /// `TCP_NODELAY` + pub nodelay: bool, +} + +impl Default for TcpSocketOpts { + fn default() -> TcpSocketOpts { + TcpSocketOpts { + send_buffer_size: None, + recv_buffer_size: None, + nodelay: false, + } + } +} + /// Options for connecting to remote server #[derive(Debug, Clone)] pub struct ConnectOpts { @@ -25,6 +48,9 @@ pub struct ConnectOpts { /// Outbound socket binds to interface #[cfg(any(target_os = "linux", target_os = "android"))] pub bind_interface: Option, + + /// TCP options + pub tcp: TcpSocketOpts, } impl Default for ConnectOpts { @@ -37,6 +63,22 @@ impl Default for ConnectOpts { bind_local_addr: None, #[cfg(any(target_os = "linux", target_os = "android"))] bind_interface: None, + tcp: TcpSocketOpts::default(), + } + } +} + +/// Inbound connection options +#[derive(Clone, Debug)] +pub struct AcceptOpts { + /// TCP options + pub tcp: TcpSocketOpts, +} + +impl Default for AcceptOpts { + fn default() -> AcceptOpts { + AcceptOpts { + tcp: TcpSocketOpts::default(), } } } diff --git a/crates/shadowsocks/src/net/tcp.rs b/crates/shadowsocks/src/net/tcp.rs index 4bd58347828f..5763867ebabf 100644 --- a/crates/shadowsocks/src/net/tcp.rs +++ b/crates/shadowsocks/src/net/tcp.rs @@ -8,8 +8,13 @@ use std::{ task::{self, Poll}, }; +use futures::{future, ready}; use pin_project::pin_project; -use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; +use socket2::Socket; +use tokio::{ + io::{AsyncRead, AsyncWrite, ReadBuf}, + net::{TcpListener as TokioTcpListener, TcpStream as TokioTcpStream}, +}; use crate::{ context::Context, @@ -17,11 +22,11 @@ use crate::{ ServerAddr, }; -use super::connect_opt::ConnectOpts; +use super::{AcceptOpts, ConnectOpts}; /// TcpStream for outbound connections #[pin_project] -pub struct TcpStream(#[pin] tokio::net::TcpStream); +pub struct TcpStream(#[pin] TokioTcpStream); impl TcpStream { /// Connects to address @@ -69,7 +74,7 @@ impl TcpStream { } impl Deref for TcpStream { - type Target = tokio::net::TcpStream; + type Target = TokioTcpStream; fn deref(&self) -> &Self::Target { &self.0 @@ -102,14 +107,107 @@ impl AsyncWrite for TcpStream { } } -impl From for TcpStream { - fn from(s: tokio::net::TcpStream) -> TcpStream { +impl From for TcpStream { + fn from(s: TokioTcpStream) -> TcpStream { TcpStream(s) } } -impl Into for TcpStream { - fn into(self) -> tokio::net::TcpStream { +impl Into for TcpStream { + fn into(self) -> TokioTcpStream { self.0 } } + +/// `TcpListener` for accepting inbound connections +pub struct TcpListener { + inner: TokioTcpListener, + accept_opts: AcceptOpts, +} + +impl TcpListener { + /// Creates a new TcpListener, which will be bound to the specified address. + pub async fn bind_with_opts(addr: &SocketAddr, accept_opts: AcceptOpts) -> io::Result { + let inner = TokioTcpListener::bind(addr).await?; + Ok(TcpListener { inner, accept_opts }) + } + + /// Create a `TcpListener` from tokio's `TcpListener` + pub fn from_listener(listener: TokioTcpListener, accept_opts: AcceptOpts) -> TcpListener { + TcpListener { + inner: listener, + accept_opts, + } + } + + /// Polls to accept a new incoming connection to this listener. + pub fn poll_accept(&self, cx: &mut task::Context<'_>) -> Poll> { + let (stream, peer_addr) = ready!(self.inner.poll_accept(cx))?; + setsockopt_with_opt(&stream, &self.accept_opts)?; + Poll::Ready(Ok((stream, peer_addr))) + } + + /// Accept a new incoming connection to this listener + pub async fn accept(&self) -> io::Result<(TokioTcpStream, SocketAddr)> { + future::poll_fn(|cx| self.poll_accept(cx)).await + } +} + +impl Deref for TcpListener { + type Target = TokioTcpListener; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for TcpListener { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +#[cfg(unix)] +use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd}; +#[cfg(windows)] +use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket}; + +#[cfg(unix)] +fn setsockopt_with_opt(f: &F, opts: &AcceptOpts) -> io::Result<()> { + let socket = unsafe { Socket::from_raw_fd(f.as_raw_fd()) }; + + if let Some(buf_size) = opts.tcp.send_buffer_size { + socket.set_send_buffer_size(buf_size as usize)?; + } + + if let Some(buf_size) = opts.tcp.recv_buffer_size { + socket.set_recv_buffer_size(buf_size as usize)?; + } + + if opts.tcp.nodelay { + socket.set_nodelay(true)?; + } + + let _ = socket.into_raw_fd(); + Ok(()) +} + +#[cfg(windows)] +fn setsockopt_with_opt(f: &F, opts: &AcceptOpts) -> io::Result<()> { + let socket = unsafe { Socket::from_raw_socket(f.as_raw_socket()) }; + + if let Some(buf_size) = opts.tcp.send_buffer_size { + socket.set_send_buffer_size(buf_size as usize)?; + } + + if let Some(buf_size) = opts.tcp.recv_buffer_size { + socket.set_recv_buffer_size(buf_size as usize)?; + } + + if opts.tcp.nodelay { + socket.set_nodelay(true)?; + } + + let _ = socket.into_raw_socket(); + Ok(()) +} diff --git a/crates/shadowsocks/src/net/udp.rs b/crates/shadowsocks/src/net/udp.rs index ae19cecb79b1..362e7a8bb9d1 100644 --- a/crates/shadowsocks/src/net/udp.rs +++ b/crates/shadowsocks/src/net/udp.rs @@ -17,7 +17,7 @@ use crate::{ ServerAddr, }; -use super::connect_opt::ConnectOpts; +use super::{AddrFamily, ConnectOpts}; /// Wrappers for outbound `UdpSocket` #[pin_project] @@ -32,13 +32,13 @@ impl UdpSocket { ) -> io::Result { let socket = match *addr { ServerAddr::SocketAddr(ref remote_addr) => { - let socket = create_outbound_udp_socket(remote_addr, opts).await?; + let socket = create_outbound_udp_socket(From::from(remote_addr), opts).await?; socket.connect(remote_addr).await?; socket } ServerAddr::DomainName(ref dname, port) => { lookup_then!(&context, dname, port, |remote_addr| { - let s = create_outbound_udp_socket(&remote_addr, opts).await?; + let s = create_outbound_udp_socket(From::from(&remote_addr), opts).await?; s.connect(remote_addr).await.map(|_| s) })? .1 @@ -56,13 +56,13 @@ impl UdpSocket { ) -> io::Result { let socket = match *addr { Address::SocketAddress(ref remote_addr) => { - let socket = create_outbound_udp_socket(remote_addr, opts).await?; + let socket = create_outbound_udp_socket(From::from(remote_addr), opts).await?; socket.connect(remote_addr).await?; socket } Address::DomainNameAddress(ref dname, port) => { lookup_then!(&context, dname, port, |remote_addr| { - let s = create_outbound_udp_socket(&remote_addr, opts).await?; + let s = create_outbound_udp_socket(From::from(&remote_addr), opts).await?; s.connect(remote_addr).await.map(|_| s) })? .1 @@ -74,7 +74,7 @@ impl UdpSocket { /// Connects to shadowsocks server pub async fn connect_with_opts(addr: &SocketAddr, opts: &ConnectOpts) -> io::Result { - let socket = create_outbound_udp_socket(addr, opts).await?; + let socket = create_outbound_udp_socket(From::from(addr), opts).await?; socket.connect(addr).await?; Ok(UdpSocket(socket)) } @@ -86,8 +86,8 @@ impl UdpSocket { } /// Binds to a specific address with opts - pub async fn bind_with_opts(addr: &SocketAddr, opts: &ConnectOpts) -> io::Result { - create_outbound_udp_socket(addr, opts).await.map(UdpSocket) + pub async fn connect_any_with_opts>(af: AF, opts: &ConnectOpts) -> io::Result { + create_outbound_udp_socket(af.into(), opts).await.map(UdpSocket) } } diff --git a/crates/shadowsocks/src/relay/sys/unix/mod.rs b/crates/shadowsocks/src/relay/sys/unix/mod.rs index e52f89df0119..3d874417a5e8 100644 --- a/crates/shadowsocks/src/relay/sys/unix/mod.rs +++ b/crates/shadowsocks/src/relay/sys/unix/mod.rs @@ -11,7 +11,7 @@ use std::{os::unix::io::RawFd, path::Path}; use cfg_if::cfg_if; use tokio::net::{TcpSocket, TcpStream, UdpSocket}; -use crate::net::ConnectOpts; +use crate::net::{AddrFamily, ConnectOpts}; /// Convert `sockaddr_storage` to `SocketAddr` #[allow(dead_code)] @@ -125,27 +125,43 @@ pub async fn tcp_stream_connect(saddr: &SocketAddr, config: &ConnectOpts) -> io: } } + // Set `SO_SNDBUF` + if let Some(buf_size) = config.tcp.send_buffer_size { + socket.set_send_buffer_size(buf_size)?; + } + + // Set `SO_RCVBUF` + if let Some(buf_size) = config.tcp.recv_buffer_size { + socket.set_recv_buffer_size(buf_size)?; + } + // it's important that the socket is protected before connecting - socket.connect(*saddr).await + let stream = socket.connect(*saddr).await?; + + if config.tcp.nodelay { + stream.set_nodelay(true)?; + } + + Ok(stream) } /// Create a `UdpSocket` for connecting to `addr` #[inline(always)] #[allow(unused_variables)] -pub async fn create_outbound_udp_socket(addr: &SocketAddr, config: &ConnectOpts) -> io::Result { - let bind_addr = match (addr.ip(), config.bind_local_addr) { - (IpAddr::V4(..), Some(IpAddr::V4(ip))) => SocketAddr::new(ip.into(), 0), - (IpAddr::V6(..), Some(IpAddr::V6(ip))) => SocketAddr::new(ip.into(), 0), - (IpAddr::V4(..), ..) => SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), - (IpAddr::V6(..), ..) => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0), +pub async fn create_outbound_udp_socket(af: AddrFamily, config: &ConnectOpts) -> io::Result { + let bind_addr = match (af, config.bind_local_addr) { + (AddrFamily::Ipv4, Some(IpAddr::V4(ip))) => SocketAddr::new(ip.into(), 0), + (AddrFamily::Ipv6, Some(IpAddr::V6(ip))) => SocketAddr::new(ip.into(), 0), + (AddrFamily::Ipv4, ..) => SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), + (AddrFamily::Ipv6, ..) => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0), }; let socket = UdpSocket::bind(bind_addr).await?; - // Any traffic to localhost should be protected + // Any traffic except localhost should be protected // This is a workaround for VPNService #[cfg(target_os = "android")] - if !addr.ip().is_loopback() { + { if let Some(ref path) = config.vpn_protect_path { protect(path, socket.as_raw_fd()).await?; } diff --git a/crates/shadowsocks/src/relay/sys/unix/uds.rs b/crates/shadowsocks/src/relay/sys/unix/uds.rs index 669a6143ca00..460e8b5c1f5f 100644 --- a/crates/shadowsocks/src/relay/sys/unix/uds.rs +++ b/crates/shadowsocks/src/relay/sys/unix/uds.rs @@ -34,17 +34,21 @@ impl UnixStream { } fn poll_send_with_fd(&self, cx: &mut Context, buf: &[u8], fds: &[RawFd]) -> Poll> { - let mut ready = ready!(self.io.poll_write_ready(cx))?; - - let fd = self.io.get_ref().as_raw_fd(); - ready.with_poll(|| match send_with_fd(fd, buf, fds) { - // self.io.poll_write_ready indicates that writable event have been received by tokio, - // so it is not a common case that sendto returns EAGAIN. - // - // Just for double check. If EAGAIN actually returns, clear the readness state. - Err(ref err) if err.kind() == ErrorKind::WouldBlock => Poll::Pending, - x => Poll::Ready(x), - }) + loop { + let mut ready = ready!(self.io.poll_write_ready(cx))?; + + let fd = self.io.get_ref().as_raw_fd(); + match send_with_fd(fd, buf, fds) { + // self.io.poll_write_ready indicates that writable event have been received by tokio, + // so it is not a common case that sendto returns EAGAIN. + // + // Just for double check. If EAGAIN actually returns, clear the readness state. + Err(ref err) if err.kind() == ErrorKind::WouldBlock => { + ready.clear_ready(); + } + x => return Poll::Ready(x), + } + } } /// Send data with file descriptors @@ -96,36 +100,38 @@ impl UnixStream { // notifications and tasks hanging. pub(crate) fn poll_read_priv(&self, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>) -> Poll> { - let mut read_guard = ready!(self.io.poll_read_ready(cx))?; + loop { + let mut read_guard = ready!(self.io.poll_read_ready(cx))?; - let b = unsafe { &mut *(buf.unfilled_mut() as *mut [std::mem::MaybeUninit] as *mut [u8]) }; - match self.io.get_ref().read(b) { - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { - read_guard.clear_ready(); - Poll::Pending - } - Ok(n) => { - // Safety: We trust `UnixStream::read` to have filled up `n` bytes - // in the buffer. - unsafe { - buf.assume_init(n); + let b = unsafe { &mut *(buf.unfilled_mut() as *mut [std::mem::MaybeUninit] as *mut [u8]) }; + match self.io.get_ref().read(b) { + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { + read_guard.clear_ready(); + } + Ok(n) => { + // Safety: We trust `UnixStream::read` to have filled up `n` bytes + // in the buffer. + unsafe { + buf.assume_init(n); + } + buf.advance(n); + return Poll::Ready(Ok(())); } - buf.advance(n); - return Poll::Ready(Ok(())); + Err(e) => return Poll::Ready(Err(e)), } - Err(e) => return Poll::Ready(Err(e)), } } pub(crate) fn poll_write_priv(&self, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { - let mut write_guard = ready!(self.io.poll_write_ready(cx))?; + loop { + let mut write_guard = ready!(self.io.poll_write_ready(cx))?; - match self.io.get_ref().write(buf) { - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { - write_guard.clear_ready(); - Poll::Pending + match self.io.get_ref().write(buf) { + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { + write_guard.clear_ready(); + } + x => return Poll::Ready(x), } - x => Poll::Ready(x), } } } diff --git a/crates/shadowsocks/src/relay/sys/windows/mod.rs b/crates/shadowsocks/src/relay/sys/windows/mod.rs index 9f73c0a2cf8b..fec471c8a75d 100644 --- a/crates/shadowsocks/src/relay/sys/windows/mod.rs +++ b/crates/shadowsocks/src/relay/sys/windows/mod.rs @@ -15,7 +15,7 @@ use winapi::{ }, }; -use crate::net::ConnectOpts; +use crate::net::{AddrFamily, ConnectOpts}; /// Create a `UdpSocket` binded to `addr` /// @@ -65,7 +65,7 @@ pub async fn create_udp_socket(addr: &SocketAddr) -> io::Result { /// create a new TCP stream #[inline(always)] pub async fn tcp_stream_connect(saddr: &SocketAddr, opts: &ConnectOpts) -> io::Result { - if let Some(ip) = opts.bind_local_addr { + let stream = if let Some(ip) = opts.bind_local_addr { let socket = match *saddr { SocketAddr::V4(..) => TcpSocket::new_v4()?, SocketAddr::V6(..) => TcpSocket::new_v6()?, @@ -83,20 +83,26 @@ pub async fn tcp_stream_connect(saddr: &SocketAddr, opts: &ConnectOpts) -> io::R } // it's important that the socket is binded before connecting - socket.connect(*saddr).await + socket.connect(*saddr).await? } else { - TcpStream::connect(saddr).await + TcpStream::connect(saddr).await? + }; + + if opts.tcp.nodelay { + stream.set_nodelay(true)?; } + + Ok(stream) } /// Create a `UdpSocket` for connecting to `addr` #[inline(always)] -pub async fn create_outbound_udp_socket(addr: &SocketAddr, opts: &ConnectOpts) -> io::Result { - let bind_addr = match (addr.ip(), opts.bind_local_addr) { - (IpAddr::V4(..), Some(IpAddr::V4(ip))) => SocketAddr::new(ip.into(), 0), - (IpAddr::V6(..), Some(IpAddr::V6(ip))) => SocketAddr::new(ip.into(), 0), - (IpAddr::V4(..), ..) => SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), - (IpAddr::V6(..), ..) => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0), +pub async fn create_outbound_udp_socket(af: AddrFamily, opts: &ConnectOpts) -> io::Result { + let bind_addr = match (af, opts.bind_local_addr) { + (AddrFamily::Ipv4, Some(IpAddr::V4(ip))) => SocketAddr::new(ip.into(), 0), + (AddrFamily::Ipv6, Some(IpAddr::V6(ip))) => SocketAddr::new(ip.into(), 0), + (AddrFamily::Ipv4, ..) => SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), + (AddrFamily::Ipv6, ..) => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0), }; create_udp_socket(&bind_addr).await } diff --git a/crates/shadowsocks/src/relay/tcprelay/aead.rs b/crates/shadowsocks/src/relay/tcprelay/aead.rs index 0fb983272412..1296fbc58413 100644 --- a/crates/shadowsocks/src/relay/tcprelay/aead.rs +++ b/crates/shadowsocks/src/relay/tcprelay/aead.rs @@ -233,7 +233,7 @@ impl DecryptedReader { while self.buffer.len() < size { let remaining = size - self.buffer.len(); - let buffer = &mut self.buffer.bytes_mut()[..remaining]; + let buffer = &mut self.buffer.chunk_mut()[..remaining]; let mut read_buf = ReadBuf::uninit(unsafe { slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut _, remaining) }); @@ -330,7 +330,7 @@ impl EncryptedWriter { let length_size = 2 + self.cipher.tag_len(); self.buffer.reserve(length_size); - let mbuf = &mut self.buffer.bytes_mut()[..length_size]; + let mbuf = &mut self.buffer.chunk_mut()[..length_size]; let mbuf = unsafe { slice::from_raw_parts_mut(mbuf.as_mut_ptr(), mbuf.len()) }; self.buffer.put_u16(buf.len() as u16); @@ -341,7 +341,7 @@ impl EncryptedWriter { let data_size = buf.len() + self.cipher.tag_len(); self.buffer.reserve(data_size); - let mbuf = &mut self.buffer.bytes_mut()[..data_size]; + let mbuf = &mut self.buffer.chunk_mut()[..data_size]; let mbuf = unsafe { slice::from_raw_parts_mut(mbuf.as_mut_ptr(), mbuf.len()) }; self.buffer.put_slice(buf); diff --git a/crates/shadowsocks/src/relay/tcprelay/proxy_listener.rs b/crates/shadowsocks/src/relay/tcprelay/proxy_listener.rs index a42d255bc8fc..e898b086af29 100644 --- a/crates/shadowsocks/src/relay/tcprelay/proxy_listener.rs +++ b/crates/shadowsocks/src/relay/tcprelay/proxy_listener.rs @@ -2,15 +2,17 @@ use std::{io, net::SocketAddr}; +use lazy_static::lazy_static; use tokio::{ io::{AsyncRead, AsyncWrite}, - net::{TcpListener, TcpStream}, + net::TcpStream, }; use crate::{ config::{ServerAddr, ServerConfig}, context::SharedContext, crypto::v1::CipherKind, + net::{AcceptOpts, TcpListener}, relay::tcprelay::proxy_stream::server::ProxyServerStream, }; @@ -25,10 +27,25 @@ pub struct ProxyListener { impl ProxyListener { /// Create a `ProxyListener` binding to a specific address pub async fn bind(context: SharedContext, svr_cfg: &ServerConfig) -> io::Result { + lazy_static! { + static ref DEFAULT_ACCEPT_OPTS: AcceptOpts = AcceptOpts::default(); + }; + ProxyListener::bind_with_opts(context, svr_cfg, DEFAULT_ACCEPT_OPTS.clone()).await + } + + /// Create a `ProxyListener` binding to a specific address with opts + pub async fn bind_with_opts( + context: SharedContext, + svr_cfg: &ServerConfig, + accept_opts: AcceptOpts, + ) -> io::Result { let listener = match svr_cfg.external_addr() { - ServerAddr::SocketAddr(sa) => TcpListener::bind(sa).await?, + ServerAddr::SocketAddr(sa) => TcpListener::bind_with_opts(sa, accept_opts).await?, ServerAddr::DomainName(domain, port) => { - lookup_then!(&context, &domain, *port, |addr| { TcpListener::bind(addr).await })?.1 + lookup_then!(&context, &domain, *port, |addr| { + TcpListener::bind_with_opts(&addr, accept_opts.clone()).await + })? + .1 } }; Ok(ProxyListener::from_listener(context, listener, svr_cfg)) diff --git a/crates/shadowsocks/src/relay/tcprelay/stream.rs b/crates/shadowsocks/src/relay/tcprelay/stream.rs index c6e3f70c39c6..b1510a8ba5c1 100644 --- a/crates/shadowsocks/src/relay/tcprelay/stream.rs +++ b/crates/shadowsocks/src/relay/tcprelay/stream.rs @@ -138,7 +138,7 @@ impl DecryptedReader { while self.buffer.len() < size { let remaining = size - self.buffer.len(); - let buffer = &mut self.buffer.bytes_mut()[..remaining]; + let buffer = &mut self.buffer.chunk_mut()[..remaining]; let mut read_buf = ReadBuf::uninit(unsafe { slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut _, remaining) }); diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 000000000000..dfc45182c156 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly-2020-12-17" diff --git a/tests/dns.rs b/tests/dns.rs index 808ad0d96de1..3ade0393991c 100644 --- a/tests/dns.rs +++ b/tests/dns.rs @@ -4,8 +4,8 @@ use std::time::Duration; use byteorder::{BigEndian, ByteOrder}; use tokio::{ + io::{AsyncReadExt, AsyncWriteExt}, net::{TcpStream, UdpSocket}, - prelude::*, time, }; diff --git a/tests/http.rs b/tests/http.rs index 66492d05ab09..34353fe9852c 100644 --- a/tests/http.rs +++ b/tests/http.rs @@ -2,7 +2,11 @@ use std::time::Duration; -use tokio::{net::TcpStream, prelude::*, time}; +use tokio::{ + io::{AsyncReadExt, AsyncWriteExt}, + net::TcpStream, + time, +}; use shadowsocks_service::{ config::{Config, ConfigType, ProtocolType}, diff --git a/tests/socks4.rs b/tests/socks4.rs index fbe671cc89a2..16f06a5334a1 100644 --- a/tests/socks4.rs +++ b/tests/socks4.rs @@ -6,7 +6,7 @@ use std::{ }; use tokio::{ - prelude::*, + io::{AsyncReadExt, AsyncWriteExt}, time::{self, Duration}, }; diff --git a/tests/socks5.rs b/tests/socks5.rs index 0185def60118..6155f0b4b032 100644 --- a/tests/socks5.rs +++ b/tests/socks5.rs @@ -6,7 +6,7 @@ use std::{ }; use tokio::{ - prelude::*, + io::{AsyncReadExt, AsyncWriteExt}, time::{self, Duration}, }; diff --git a/tests/tunnel.rs b/tests/tunnel.rs index a1a18fa33040..87bf935b8c8c 100644 --- a/tests/tunnel.rs +++ b/tests/tunnel.rs @@ -5,8 +5,8 @@ use std::str; use byte_string::ByteStr; use tokio::{ self, + io::{AsyncReadExt, AsyncWriteExt}, net::{TcpStream, UdpSocket}, - prelude::*, time::{self, Duration}, };