From 52e828537e71b7fddedece5994e32012e51bf1e2 Mon Sep 17 00:00:00 2001 From: sankhwang Date: Mon, 22 Feb 2021 12:36:20 +0800 Subject: [PATCH 1/3] should update interest after process_send immediately --- relay-rust/src/relay/tcp_connection.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/relay-rust/src/relay/tcp_connection.rs b/relay-rust/src/relay/tcp_connection.rs index 4935f306..9018801e 100644 --- a/relay-rust/src/relay/tcp_connection.rs +++ b/relay-rust/src/relay/tcp_connection.rs @@ -228,6 +228,9 @@ impl TcpConnection { } else { self.process_send(selector)?; } + if !self.closed { + self.update_interests(selector); + } } if !self.closed && ready.is_readable() { self.process_receive(selector)?; From 80918c838749c893c0a3b23c2457c8bfe03ba179 Mon Sep 17 00:00:00 2001 From: sankhwang Date: Mon, 22 Feb 2021 13:38:26 +0800 Subject: [PATCH 2/3] if read stream throw wouldblock error , should update interest after process_send immediately --- relay-rust/src/relay/tcp_connection.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/relay-rust/src/relay/tcp_connection.rs b/relay-rust/src/relay/tcp_connection.rs index 9018801e..616d52b4 100644 --- a/relay-rust/src/relay/tcp_connection.rs +++ b/relay-rust/src/relay/tcp_connection.rs @@ -227,13 +227,19 @@ impl TcpConnection { self.process_connect(selector); } else { self.process_send(selector)?; - } - if !self.closed { - self.update_interests(selector); - } + } } if !self.closed && ready.is_readable() { - self.process_receive(selector)?; + match self.process_receive(selector) { + Ok(_) => (), + Err(err) => { + if err.kind() == io::ErrorKind::WouldBlock && ready.is_writable() { + cx_debug!(target: TAG, self.id, "already write, update interests here"); + self.update_interests(selector); + } + return Err(err); + } + } } if !self.closed { self.update_interests(selector); From 8e6567dc8c44d607086e929ce361b74014d76e5e Mon Sep 17 00:00:00 2001 From: sankhwang Date: Wed, 5 Jan 2022 11:59:21 +0000 Subject: [PATCH 3/3] add socks5 support --- relay-rust/Cargo.lock | 560 ++++++++++++++++++------ relay-rust/Cargo.toml | 17 + relay-rust/proxy.toml.example | 19 + relay-rust/src/cli_args.rs | 17 + relay-rust/src/lib.rs | 5 +- relay-rust/src/logger.rs | 2 +- relay-rust/src/main.rs | 54 ++- relay-rust/src/relay/mod.rs | 9 + relay-rust/src/relay/proxy_config.rs | 90 ++++ relay-rust/src/relay/relay.rs | 13 +- relay-rust/src/relay/router.rs | 9 + relay-rust/src/relay/selector.rs | 7 +- relay-rust/src/relay/socks5_protocol.rs | 311 +++++++++++++ relay-rust/src/relay/tcp_connection.rs | 322 +++++++++++++- relay-rust/src/relay/udp_connection.rs | 3 +- 15 files changed, 1275 insertions(+), 163 deletions(-) create mode 100644 relay-rust/proxy.toml.example create mode 100644 relay-rust/src/relay/proxy_config.rs create mode 100644 relay-rust/src/relay/socks5_protocol.rs diff --git a/relay-rust/Cargo.lock b/relay-rust/Cargo.lock index c78116e1..3aeaba80 100644 --- a/relay-rust/Cargo.lock +++ b/relay-rust/Cargo.lock @@ -2,336 +2,634 @@ # It is not intended for manual editing. [[package]] name = "autocfg" -version = "0.1.7" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "byteorder" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" [[package]] -name = "c2-chacha" -version = "0.2.3" +name = "bytes" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" [[package]] name = "cc" -version = "1.0.47" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.9" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" dependencies = [ - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "num-integer", + "num-traits", + "time", + "winapi 0.3.9", ] [[package]] name = "ctrlc" -version = "3.1.3" +version = "3.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c15b8ec3b5755a188c141c1f6a98e76de31b936209bf066b647979e2a84764a9" dependencies = [ - "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "nix", + "winapi 0.3.9", ] [[package]] name = "fuchsia-zircon" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "fuchsia-zircon-sys", ] [[package]] name = "fuchsia-zircon-sys" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "getrandom" -version = "0.1.13" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", - "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] name = "gnirehtet" version = "2.5.0" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", - "ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "bytes", + "chrono", + "ctrlc", + "ipnet", + "iprange", + "lazy_static", + "libc", + "log", + "mio 0.6.23", + "once_cell", + "pin-project", + "rand", + "serde", + "slab", + "thiserror", + "tokio", + "toml", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "instant" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +dependencies = [ + "cfg-if 1.0.0", ] [[package]] name = "iovec" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "ipnet" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" + +[[package]] +name = "iprange" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f97116c07c1d65dacd68ec91d8df530f5c1f7fcfc56ec4d91b4d7d854868c080" dependencies = [ - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "ipnet", ] [[package]] name = "kernel32-sys" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8", + "winapi-build", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" -version = "0.2.65" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c" + +[[package]] +name = "lock_api" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" +dependencies = [ + "scopeguard", +] [[package]] name = "log" -version = "0.4.8" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", ] +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + [[package]] name = "mio" -version = "0.6.19" +version = "0.6.23" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" dependencies = [ - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow 0.2.2", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "mio" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5dede4e2065b3842b8b0af444119f3aa331cc7cc2dd20388bfb0f5d5a38823a" +dependencies = [ + "libc", + "log", + "miow 0.3.6", + "ntapi", + "winapi 0.3.9", ] [[package]] name = "miow" -version = "0.2.1" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "miow" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2", + "winapi 0.3.9", ] [[package]] name = "net2" -version = "0.2.33" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10", + "libc", + "winapi 0.3.9", ] [[package]] name = "nix" -version = "0.14.1" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "cc", + "cfg-if 1.0.0", + "libc", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi 0.3.9", ] [[package]] name = "num-integer" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "num-traits", ] [[package]] name = "num-traits" -version = "0.2.9" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad167a2f54e832b82dbe003a046280dceffe5227b5f79e08e363a29638cfddd" + +[[package]] +name = "parking_lot" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi 0.3.9", ] +[[package]] +name = "pin-project" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96fa8ebb90271c4477f144354485b8068bd8f6b78b428b01ba892ca26caf0b63" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827" + [[package]] name = "ppv-lite86" -version = "0.2.6" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] [[package]] name = "rand" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", ] [[package]] name = "rand_chacha" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ - "c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ppv-lite86", + "rand_core", ] [[package]] name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", ] [[package]] name = "rand_hc" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core", ] [[package]] name = "redox_syscall" -version = "0.1.56" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" +dependencies = [ + "bitflags", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.123" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "signal-hook-registry" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" +dependencies = [ + "libc", +] [[package]] name = "slab" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" + +[[package]] +name = "socket2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "syn" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "thiserror" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "time" -version = "0.1.42" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi 0.3.9", +] + +[[package]] +name = "tokio" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8190d04c665ea9e6b6a0dc45523ade572c088d2e6566244c1122671dbf4ae3a" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio 0.7.9", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "tokio-macros", + "winapi 0.3.9", +] + +[[package]] +name = "tokio-macros" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57" dependencies = [ - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "void" -version = "1.0.2" +name = "toml" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "wasi" -version = "0.7.0" +version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" [[package]] name = "winapi" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" [[package]] name = "winapi" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-build" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "ws2_32-sys" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" -"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" -"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" -"checksum cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)" = "aa87058dce70a3ff5621797f1506cb837edd02ac4c0ae642b4542dce802908b8" -"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -"checksum chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e8493056968583b0193c1bb04d6f7684586f3726992d6c573261941a895dbd68" -"checksum ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7dfd2d8b4c82121dfdff120f818e09fc4380b0b7e17a742081a89b94853e87f" -"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407" -"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" -"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8" -"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" -"checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23" -"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" -"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" -"checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" -"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" -"checksum num-traits 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "443c53b3c3531dfcbfa499d8893944db78474ad7a1d87fa2d94d1a2231693ac6" -"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" -"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" -"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" -"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" -"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" -"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" -"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" -"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" -"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" + "winapi 0.2.8", + "winapi-build", +] diff --git a/relay-rust/Cargo.toml b/relay-rust/Cargo.toml index 13a22a85..2a2695ec 100644 --- a/relay-rust/Cargo.toml +++ b/relay-rust/Cargo.toml @@ -16,6 +16,23 @@ chrono = "0.4" # for formatting timestamp in logs byteorder = "1.3" # for reading/writing binary rand = "0.7" # for random TCP sequence number ctrlc = { version = "3.0", features = ["termination"] } # for handling Ctrl+C +tokio = { version = "1.2", features = ["io-util", "macros", "net", "parking_lot", "process", "rt"] } +thiserror = "1.0" +bytes = "1.0" +pin-project = "1.0" +toml = "0.5" +serde = { version = "1.0", features = ["derive"] } +lazy_static = "1.4" +iprange = "0.6" +ipnet = "2.3" +once_cell = "1.4" + +[target.'cfg(unix)'.dependencies] +libc = "0.2" + +[target.'cfg(windows)'.dependencies] +winapi = "0.2" +ws2_32-sys = "0.2" [profile.release] lto = true # link-time optimization diff --git a/relay-rust/proxy.toml.example b/relay-rust/proxy.toml.example new file mode 100644 index 00000000..4e9d54f8 --- /dev/null +++ b/relay-rust/proxy.toml.example @@ -0,0 +1,19 @@ +tcp_read_time_out = 15000 +tcp_connect_time_out = 15000 + +lan_host = [ + "10.0.0.0/8", + "172.16.0.0/16", + "192.168.1.0/24" +] + +[special_proxy_config] +proxy = { proxy_addr = "119.9.77.88:1080", proxy_type = "socks5", username = "gnirehtet", password = "gnirehtet" } +hosts = [ + "8.8.8.8", + "119.29.29.29" +] + +[default_proxy_config] +proxy = { proxy_addr = "127.0.0.1:10080", proxy_type = "socks5", username = "", password = "" } +hosts = [] diff --git a/relay-rust/src/cli_args.rs b/relay-rust/src/cli_args.rs index 438427fb..198ca269 100644 --- a/relay-rust/src/cli_args.rs +++ b/relay-rust/src/cli_args.rs @@ -19,6 +19,7 @@ pub const PARAM_SERIAL: u8 = 1; pub const PARAM_DNS_SERVERS: u8 = 1 << 1; pub const PARAM_ROUTES: u8 = 1 << 2; pub const PARAM_PORT: u8 = 1 << 3; +pub const PARAM_CONF: u8 = 1 << 4; pub const DEFAULT_PORT: u16 = 31416; @@ -27,6 +28,7 @@ pub struct CommandLineArguments { dns_servers: Option, routes: Option, port: u16, + conf: String } impl CommandLineArguments { @@ -36,6 +38,7 @@ impl CommandLineArguments { let mut dns_servers = None; let mut routes = None; let mut port = 0; + let mut conf = String::from(""); let mut iter = args.into_iter(); while let Some(arg) = iter.next() { @@ -70,6 +73,15 @@ impl CommandLineArguments { } else { return Err(String::from("Missing -p parameter")); } + } else if (accepted_parameters & PARAM_CONF) != 0 && "-c" == arg { + if !conf.is_empty() { + return Err(String::from("Conf already set")); + } + if let Some(value) = iter.next() { + conf = value.into(); + } else { + return Err(String::from("Missing -c parameter")); + } } else if (accepted_parameters & PARAM_SERIAL) != 0 && serial.is_none() { serial = Some(arg); } else { @@ -84,6 +96,7 @@ impl CommandLineArguments { dns_servers, routes, port, + conf }) } @@ -102,6 +115,10 @@ impl CommandLineArguments { pub fn port(&self) -> u16 { self.port } + + pub fn conf(&self) -> &str { + &self.conf + } } #[cfg(test)] diff --git a/relay-rust/src/lib.rs b/relay-rust/src/lib.rs index e50ca4e7..54a95291 100644 --- a/relay-rust/src/lib.rs +++ b/relay-rust/src/lib.rs @@ -14,12 +14,13 @@ * limitations under the License. */ +#![allow(dead_code)] mod relay; pub use crate::relay::byte_buffer; use crate::relay::Relay; use std::io; -pub fn relay(port: u16) -> io::Result<()> { - Relay::new(port).run() +pub fn relay(port: u16, conf: String) -> io::Result<()> { + Relay::new(port, conf).run() } diff --git a/relay-rust/src/logger.rs b/relay-rust/src/logger.rs index 44cbbac1..4bfb7569 100644 --- a/relay-rust/src/logger.rs +++ b/relay-rust/src/logger.rs @@ -19,7 +19,7 @@ use log::*; use std::io::{self, Write}; static LOGGER: SimpleLogger = SimpleLogger; -const THRESHOLD: LevelFilter = LevelFilter::Info; +const THRESHOLD: LevelFilter = LevelFilter::Debug; pub struct SimpleLogger; diff --git a/relay-rust/src/main.rs b/relay-rust/src/main.rs index cb80e9c8..ef3830e8 100644 --- a/relay-rust/src/main.rs +++ b/relay-rust/src/main.rs @@ -155,6 +155,7 @@ impl Command for RunCommand { | cli_args::PARAM_DNS_SERVERS | cli_args::PARAM_ROUTES | cli_args::PARAM_PORT + | cli_args::PARAM_CONF } fn description(&self) -> &'static str { @@ -171,6 +172,7 @@ impl Command for RunCommand { args.dns_servers(), args.routes(), args.port(), + args.conf(), ) } } @@ -181,7 +183,7 @@ impl Command for AutorunCommand { } fn accepted_parameters(&self) -> u8 { - cli_args::PARAM_DNS_SERVERS | cli_args::PARAM_ROUTES | cli_args::PARAM_PORT + cli_args::PARAM_DNS_SERVERS | cli_args::PARAM_ROUTES | cli_args::PARAM_PORT | cli_args::PARAM_CONF } fn description(&self) -> &'static str { @@ -191,7 +193,7 @@ impl Command for AutorunCommand { } fn execute(&self, args: &CommandLineArguments) -> Result<(), CommandExecutionError> { - cmd_autorun(args.dns_servers(), args.routes(), args.port()) + cmd_autorun(args.dns_servers(), args.routes(), args.port(), args.conf()) } } @@ -205,6 +207,7 @@ impl Command for StartCommand { | cli_args::PARAM_DNS_SERVERS | cli_args::PARAM_ROUTES | cli_args::PARAM_PORT + | cli_args::PARAM_CONF } fn description(&self) -> &'static str { @@ -228,6 +231,7 @@ impl Command for StartCommand { args.dns_servers(), args.routes(), args.port(), + args.conf() ) } } @@ -238,7 +242,7 @@ impl Command for AutostartCommand { } fn accepted_parameters(&self) -> u8 { - cli_args::PARAM_DNS_SERVERS | cli_args::PARAM_ROUTES | cli_args::PARAM_PORT + cli_args::PARAM_DNS_SERVERS | cli_args::PARAM_ROUTES | cli_args::PARAM_PORT | cli_args::PARAM_CONF } fn description(&self) -> &'static str { @@ -249,7 +253,7 @@ impl Command for AutostartCommand { } fn execute(&self, args: &CommandLineArguments) -> Result<(), CommandExecutionError> { - cmd_autostart(args.dns_servers(), args.routes(), args.port()) + cmd_autostart(args.dns_servers(), args.routes(), args.port(), args.conf()) } } @@ -283,6 +287,7 @@ impl Command for RestartCommand { | cli_args::PARAM_DNS_SERVERS | cli_args::PARAM_ROUTES | cli_args::PARAM_PORT + | cli_args::PARAM_CONF } fn description(&self) -> &'static str { @@ -296,6 +301,7 @@ impl Command for RestartCommand { args.dns_servers(), args.routes(), args.port(), + args.conf() )?; Ok(()) } @@ -328,7 +334,7 @@ impl Command for RelayCommand { } fn accepted_parameters(&self) -> u8 { - cli_args::PARAM_NONE | cli_args::PARAM_PORT + cli_args::PARAM_NONE | cli_args::PARAM_PORT | cli_args::PARAM_CONF } fn description(&self) -> &'static str { @@ -336,7 +342,7 @@ impl Command for RelayCommand { } fn execute(&self, args: &CommandLineArguments) -> Result<(), CommandExecutionError> { - cmd_relay(args.port())?; + cmd_relay(args.port(), args.conf().to_string())?; Ok(()) } } @@ -362,9 +368,10 @@ fn cmd_run( dns_servers: Option<&str>, routes: Option<&str>, port: u16, + conf: &str, ) -> Result<(), CommandExecutionError> { // start in parallel so that the relay server is ready when the client connects - async_start(serial, dns_servers, routes, port); + async_start(serial, dns_servers, routes, port, conf); let ctrlc_serial = serial.map(String::from); ctrlc::set_handler(move || { @@ -379,27 +386,30 @@ fn cmd_run( }) .expect("Error setting Ctrl-C handler"); - cmd_relay(port) + cmd_relay(port, conf.to_string()) } fn cmd_autorun( dns_servers: Option<&str>, routes: Option<&str>, port: u16, + conf: &str ) -> Result<(), CommandExecutionError> { { let autostart_dns_servers = dns_servers.map(String::from); let autostart_routes = routes.map(String::from); + let autostart_conf = String::from(conf); thread::spawn(move || { let dns_servers = autostart_dns_servers.as_ref().map(String::as_ref); let routes = autostart_routes.as_ref().map(String::as_ref); - if let Err(err) = cmd_autostart(dns_servers, routes, port) { + let conf = &autostart_conf; + if let Err(err) = cmd_autostart(dns_servers, routes, port, conf) { error!(target: TAG, "Cannot auto start clients: {}", err); } }); } - cmd_relay(port) + cmd_relay(port, conf.to_string()) } fn cmd_start( @@ -407,6 +417,7 @@ fn cmd_start( dns_servers: Option<&str>, routes: Option<&str>, port: u16, + conf: &str ) -> Result<(), CommandExecutionError> { if must_install_client(serial)? { cmd_install(serial)?; @@ -433,6 +444,8 @@ fn cmd_start( if let Some(routes) = routes { adb_args.append(&mut vec!["--esa", "routes", routes]); } + adb_args.append(&mut vec!["--esa", "conf", conf]); + exec_adb(serial, adb_args) } @@ -440,13 +453,16 @@ fn cmd_autostart( dns_servers: Option<&str>, routes: Option<&str>, port: u16, + conf: &str ) -> Result<(), CommandExecutionError> { let start_dns_servers = dns_servers.map(String::from); let start_routes = routes.map(String::from); + let start_conf = String::from(conf); let mut adb_monitor = AdbMonitor::new(Box::new(move |serial: &str| { let dns_servers = start_dns_servers.as_ref().map(String::as_ref); let routes = start_routes.as_ref().map(String::as_ref); - async_start(Some(serial), dns_servers, routes, port) + let conf = &start_conf; + async_start(Some(serial), dns_servers, routes, port, conf) })); adb_monitor.monitor(); Ok(()) @@ -479,21 +495,23 @@ fn cmd_tunnel(serial: Option<&str>, port: u16) -> Result<(), CommandExecutionErr ) } -fn cmd_relay(port: u16) -> Result<(), CommandExecutionError> { - info!(target: TAG, "Starting relay server on port {}...", port); - relaylib::relay(port)?; +fn cmd_relay(port: u16, conf: String) -> Result<(), CommandExecutionError> { + info!(target: TAG, "Starting relay server on port {} with conf file {}...", port, conf); + relaylib::relay(port, conf)?; Ok(()) } -fn async_start(serial: Option<&str>, dns_servers: Option<&str>, routes: Option<&str>, port: u16) { +fn async_start(serial: Option<&str>, dns_servers: Option<&str>, routes: Option<&str>, port: u16, conf: &str) { let start_serial = serial.map(String::from); let start_dns_servers = dns_servers.map(String::from); let start_routes = routes.map(String::from); + let start_conf = String::from(conf); thread::spawn(move || { let serial = start_serial.as_ref().map(String::as_ref); let dns_servers = start_dns_servers.as_ref().map(String::as_ref); let routes = start_routes.as_ref().map(String::as_ref); - if let Err(err) = cmd_start(serial, dns_servers, routes, port) { + let conf = &start_conf; + if let Err(err) = cmd_start(serial, dns_servers, routes, port, conf) { error!(target: TAG, "Cannot start client: {}", err); } }); @@ -605,6 +623,9 @@ fn append_command_usage(msg: &mut String, command: &dyn Command) { if (accepted_parameters & cli_args::PARAM_ROUTES) != 0 { msg.push_str(" [-r ROUTE[,ROUTE2,...]]"); } + if (accepted_parameters & cli_args::PARAM_CONF) != 0 { + msg.push_str(" -c path_to_proxy.toml"); + } msg.push('\n'); for desc_line in command.description().split('\n') { msg.push_str(" "); @@ -623,6 +644,7 @@ fn main() { logger::init().unwrap(); let mut args = env::args(); // args.nth(1) will consume the two first arguments (the binary name and the command name) + if let Some(command_name) = args.nth(1) { let command = COMMANDS .iter() diff --git a/relay-rust/src/relay/mod.rs b/relay-rust/src/relay/mod.rs index 3a94b12c..9d2adb27 100644 --- a/relay-rust/src/relay/mod.rs +++ b/relay-rust/src/relay/mod.rs @@ -15,8 +15,15 @@ */ pub use self::relay::Relay; +pub use self::socks5_protocol::{Socks5State, Authentication}; +pub use self::proxy_config::GNIREHTET_PROXY_CONFIG; +pub use self::proxy_config::CONF_PATH; + pub mod byte_buffer; +mod socks5_protocol; +mod proxy_config; + mod binary; mod client; mod close_listener; @@ -32,6 +39,7 @@ mod ipv4_packet_buffer; mod net; mod packet_source; mod packetizer; + #[allow(clippy::module_inception)] // relay.rs is in relay/ mod relay; mod router; @@ -43,3 +51,4 @@ mod transport_header; mod tunnel_server; mod udp_connection; mod udp_header; + diff --git a/relay-rust/src/relay/proxy_config.rs b/relay-rust/src/relay/proxy_config.rs new file mode 100644 index 00000000..f4fa1653 --- /dev/null +++ b/relay-rust/src/relay/proxy_config.rs @@ -0,0 +1,90 @@ +extern crate iprange; +extern crate ipnet; + +use serde::Deserialize; + +use iprange::IpRange; +use ipnet::Ipv4Net; + +use std::fs; +use std::net::{SocketAddrV4, Ipv4Addr}; + +use lazy_static::lazy_static; +use once_cell::sync::OnceCell; + +pub static CONF_PATH: OnceCell = OnceCell::new(); + +#[derive(Debug, Deserialize, Clone)] +pub struct ProxyConfig { + pub proxy_addr: SocketAddrV4, + pub proxy_type: String, + pub username: String, + pub password: String, +} + +#[derive(Debug, Deserialize, Clone)] +pub struct ProxyHostConfig { + proxy: ProxyConfig, + hosts: Vec +} + +#[derive(Debug, Deserialize, Clone)] +pub struct GnirehtetProxyConfig { + tcp_read_time_out: u32, + tcp_connect_time_out: u32, + #[serde(skip_deserializing)] + lan_host_iprange: IpRange, + lan_host: Vec, + special_proxy_config: ProxyHostConfig, + default_proxy_config: ProxyHostConfig +} + +lazy_static! { + pub static ref GNIREHTET_PROXY_CONFIG: GnirehtetProxyConfig = { + let path: &str = CONF_PATH.get().unwrap(); + let toml_str = fs::read_to_string(path); + match toml_str { + Ok(val) => { + let mut conf: GnirehtetProxyConfig = toml::from_str(&val).unwrap(); + let lan_host_range: IpRange = conf.lan_host.iter().map(|s| s.parse().unwrap()).collect(); + conf.lan_host_iprange = lan_host_range; + println!("Gnirehtet Proxy Config: \n{:#?}", conf); + conf + }, + Err(_) => { + if !path.is_empty() { + panic!("invalid conf file, please check {}", path); + } else { + panic!("invalid conf file, -c args is none"); + } + } + } + }; +} + +pub fn get_proxy_for_addr(addr: SocketAddrV4) -> Option { + // in lan_host, don't use proxy + if GNIREHTET_PROXY_CONFIG.lan_host_iprange.contains(addr.ip()) { + return None + } + + // in special proxy.hosts, return the specify proxy + for (_, host) in GNIREHTET_PROXY_CONFIG.special_proxy_config.hosts.iter().enumerate() { + if host == addr.ip() { + return Some(ProxyConfig { + proxy_addr: GNIREHTET_PROXY_CONFIG.special_proxy_config.proxy.proxy_addr, + proxy_type: GNIREHTET_PROXY_CONFIG.special_proxy_config.proxy.proxy_type.clone(), + username: GNIREHTET_PROXY_CONFIG.special_proxy_config.proxy.username.clone(), + password: GNIREHTET_PROXY_CONFIG.special_proxy_config.proxy.password.clone() + }) + } + } + + // not in lan_host neither proxies.hosts, use default proxy + Some(ProxyConfig { + proxy_addr: GNIREHTET_PROXY_CONFIG.default_proxy_config.proxy.proxy_addr, + proxy_type: GNIREHTET_PROXY_CONFIG.default_proxy_config.proxy.proxy_type.clone(), + username: GNIREHTET_PROXY_CONFIG.default_proxy_config.proxy.username.clone(), + password: GNIREHTET_PROXY_CONFIG.default_proxy_config.proxy.password.clone() + }) +} \ No newline at end of file diff --git a/relay-rust/src/relay/relay.rs b/relay-rust/src/relay/relay.rs index 31cf3a12..4f5e6761 100644 --- a/relay-rust/src/relay/relay.rs +++ b/relay-rust/src/relay/relay.rs @@ -26,20 +26,29 @@ use std::time::Duration; use super::selector::Selector; use super::tunnel_server::TunnelServer; use super::udp_connection::IDLE_TIMEOUT_SECONDS; +use super::CONF_PATH; + +use super::proxy_config::get_proxy_for_addr; const TAG: &str = "Relay"; const CLEANING_INTERVAL_SECONDS: i64 = 60; pub struct Relay { port: u16, + conf: String } impl Relay { - pub fn new(port: u16) -> Self { - Self { port } + pub fn new(port: u16, conf: String) -> Self { + Self { port, conf } } pub fn run(&self) -> io::Result<()> { + + // make lazy_static not so lazy. + let _ = CONF_PATH.set(self.conf.clone()); + let _ = get_proxy_for_addr("127.0.0.1:1080".parse().unwrap()); + let mut selector = Selector::create().unwrap(); let tunnel_server = TunnelServer::create(self.port, &mut selector)?; info!(target: TAG, "Relay server started"); diff --git a/relay-rust/src/relay/router.rs b/relay-rust/src/relay/router.rs index 88a8d9e6..7de7da4b 100644 --- a/relay-rust/src/relay/router.rs +++ b/relay-rust/src/relay/router.rs @@ -55,6 +55,15 @@ impl Router { client_channel: &mut ClientChannel, ipv4_packet: &Ipv4Packet, ) { + + let (ipv4_header_data, _transport_header_data) = ipv4_packet.headers_data(); + + //TODO: add proxy check + if ipv4_header_data.protocol() == Protocol::Udp { + debug!(target: TAG, "proxy mode : Dropping udp packet"); + return; + } + if ipv4_packet.is_valid() { match self.connection(selector, ipv4_packet) { Ok(index) => { diff --git a/relay-rust/src/relay/selector.rs b/relay-rust/src/relay/selector.rs index 606fa924..75cc5dd2 100644 --- a/relay-rust/src/relay/selector.rs +++ b/relay-rust/src/relay/selector.rs @@ -14,7 +14,6 @@ * limitations under the License. */ -use log::*; use mio::{Event, Evented, Events, Poll, PollOpt, Ready, Token}; use slab::Slab; use std::io; @@ -40,7 +39,7 @@ pub struct Selector { poll: Poll, handlers: Slab>, // tokens to be removed after all the current poll events are executed - tokens_to_remove: Vec, + tokens_to_remove: Vec } impl Selector { @@ -48,7 +47,7 @@ impl Selector { Ok(Self { poll: Poll::new()?, handlers: Slab::with_capacity(1024), - tokens_to_remove: Vec::new(), + tokens_to_remove: Vec::new() }) } @@ -109,7 +108,7 @@ impl Selector { pub fn run_handlers(&mut self, events: &Events) { for event in events { - debug!(target: TAG, "event={:?}", event); + //debug!(target: TAG, "event={:?}", event); let handler = self .handlers .get_mut(event.token().0) diff --git a/relay-rust/src/relay/socks5_protocol.rs b/relay-rust/src/relay/socks5_protocol.rs new file mode 100644 index 00000000..5ab87c5d --- /dev/null +++ b/relay-rust/src/relay/socks5_protocol.rs @@ -0,0 +1,311 @@ +//! Socks5 protocol definition (RFC1928) +//! +//! Implements [SOCKS Protocol Version 5](https://www.ietf.org/rfc/rfc1928.txt) proxy protocol + +use std::{ + convert::From, + fmt::{self, Debug}, + io::{self, ErrorKind, Read}, + u8, +}; +use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr}; +use mio::net::TcpStream; +use byteorder::{ReadBytesExt, BigEndian}; + +pub use self::consts::{ + SOCKS5_AUTH_METHOD_GSSAPI, + SOCKS5_AUTH_METHOD_NONE, + SOCKS5_AUTH_METHOD_NOT_ACCEPTABLE, + SOCKS5_AUTH_METHOD_PASSWORD, +}; + +pub const MAX_ADDR_LEN: usize = 260; + +#[rustfmt::skip] +pub mod consts { + pub const SOCKS5_VERSION: u8 = 0x05; + + pub const SOCKS5_AUTH_METHOD_NONE: u8 = 0x00; + pub const SOCKS5_AUTH_METHOD_GSSAPI: u8 = 0x01; + pub const SOCKS5_AUTH_METHOD_PASSWORD: u8 = 0x02; + pub const SOCKS5_AUTH_METHOD_NOT_ACCEPTABLE: u8 = 0xff; + + pub const SOCKS5_CMD_TCP_CONNECT: u8 = 0x01; + pub const SOCKS5_CMD_TCP_BIND: u8 = 0x02; + pub const SOCKS5_CMD_UDP_ASSOCIATE: u8 = 0x03; + + pub const SOCKS5_ADDR_TYPE_IPV4: u8 = 0x01; + pub const SOCKS5_ADDR_TYPE_DOMAIN_NAME: u8 = 0x03; + pub const SOCKS5_ADDR_TYPE_IPV6: u8 = 0x04; + + pub const SOCKS5_REPLY_SUCCEEDED: u8 = 0x00; + pub const SOCKS5_REPLY_GENERAL_FAILURE: u8 = 0x01; + pub const SOCKS5_REPLY_CONNECTION_NOT_ALLOWED: u8 = 0x02; + pub const SOCKS5_REPLY_NETWORK_UNREACHABLE: u8 = 0x03; + pub const SOCKS5_REPLY_HOST_UNREACHABLE: u8 = 0x04; + pub const SOCKS5_REPLY_CONNECTION_REFUSED: u8 = 0x05; + pub const SOCKS5_REPLY_TTL_EXPIRED: u8 = 0x06; + pub const SOCKS5_REPLY_COMMAND_NOT_SUPPORTED: u8 = 0x07; + pub const SOCKS5_REPLY_ADDRESS_TYPE_NOT_SUPPORTED: u8 = 0x08; +} + +/// SOCKS5 command +#[derive(Clone, Debug, Copy)] +pub enum Command { + /// CONNECT command (TCP tunnel) + TcpConnect, + /// BIND command (Not supported) + TcpBind, + /// UDP ASSOCIATE command + UdpAssociate, +} + +#[allow(dead_code)] +impl Command { + #[inline] + #[rustfmt::skip] + fn as_u8(self) -> u8 { + match self { + Command::TcpConnect => consts::SOCKS5_CMD_TCP_CONNECT, + Command::TcpBind => consts::SOCKS5_CMD_TCP_BIND, + Command::UdpAssociate => consts::SOCKS5_CMD_UDP_ASSOCIATE, + } + } + + #[inline] + #[rustfmt::skip] + fn from_u8(code: u8) -> Option { + match code { + consts::SOCKS5_CMD_TCP_CONNECT => Some(Command::TcpConnect), + consts::SOCKS5_CMD_TCP_BIND => Some(Command::TcpBind), + consts::SOCKS5_CMD_UDP_ASSOCIATE => Some(Command::UdpAssociate), + _ => None, + } + } +} + +/// SOCKS5 reply code +#[derive(Clone, Debug, Copy)] +pub enum Reply { + Succeeded, + GeneralFailure, + ConnectionNotAllowed, + NetworkUnreachable, + HostUnreachable, + ConnectionRefused, + TtlExpired, + CommandNotSupported, + AddressTypeNotSupported, + + OtherReply(u8), +} + +#[allow(dead_code)] +impl Reply { + #[inline] + #[rustfmt::skip] + fn as_u8(self) -> u8 { + match self { + Reply::Succeeded => consts::SOCKS5_REPLY_SUCCEEDED, + Reply::GeneralFailure => consts::SOCKS5_REPLY_GENERAL_FAILURE, + Reply::ConnectionNotAllowed => consts::SOCKS5_REPLY_CONNECTION_NOT_ALLOWED, + Reply::NetworkUnreachable => consts::SOCKS5_REPLY_NETWORK_UNREACHABLE, + Reply::HostUnreachable => consts::SOCKS5_REPLY_HOST_UNREACHABLE, + Reply::ConnectionRefused => consts::SOCKS5_REPLY_CONNECTION_REFUSED, + Reply::TtlExpired => consts::SOCKS5_REPLY_TTL_EXPIRED, + Reply::CommandNotSupported => consts::SOCKS5_REPLY_COMMAND_NOT_SUPPORTED, + Reply::AddressTypeNotSupported => consts::SOCKS5_REPLY_ADDRESS_TYPE_NOT_SUPPORTED, + Reply::OtherReply(c) => c, + } + } + + #[inline] + #[rustfmt::skip] + fn from_u8(code: u8) -> Reply { + match code { + consts::SOCKS5_REPLY_SUCCEEDED => Reply::Succeeded, + consts::SOCKS5_REPLY_GENERAL_FAILURE => Reply::GeneralFailure, + consts::SOCKS5_REPLY_CONNECTION_NOT_ALLOWED => Reply::ConnectionNotAllowed, + consts::SOCKS5_REPLY_NETWORK_UNREACHABLE => Reply::NetworkUnreachable, + consts::SOCKS5_REPLY_HOST_UNREACHABLE => Reply::HostUnreachable, + consts::SOCKS5_REPLY_CONNECTION_REFUSED => Reply::ConnectionRefused, + consts::SOCKS5_REPLY_TTL_EXPIRED => Reply::TtlExpired, + consts::SOCKS5_REPLY_COMMAND_NOT_SUPPORTED => Reply::CommandNotSupported, + consts::SOCKS5_REPLY_ADDRESS_TYPE_NOT_SUPPORTED => Reply::AddressTypeNotSupported, + _ => Reply::OtherReply(code), + } + } +} + +impl fmt::Display for Reply { + #[rustfmt::skip] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Reply::Succeeded => write!(f, "Succeeded"), + Reply::AddressTypeNotSupported => write!(f, "Address type not supported"), + Reply::CommandNotSupported => write!(f, "Command not supported"), + Reply::ConnectionNotAllowed => write!(f, "Connection not allowed"), + Reply::ConnectionRefused => write!(f, "Connection refused"), + Reply::GeneralFailure => write!(f, "General failure"), + Reply::HostUnreachable => write!(f, "Host unreachable"), + Reply::NetworkUnreachable => write!(f, "Network unreachable"), + Reply::OtherReply(u) => write!(f, "Other reply ({})", u), + Reply::TtlExpired => write!(f, "TTL expired"), + } + } +} + +/// SOCKS5 protocol error +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("{0}")] + IoError(#[from] io::Error), + #[error("address type {0:#x} not supported")] + AddressTypeNotSupported(u8), + #[error("address domain name must be UTF-8 encoding")] + AddressDomainInvalidEncoding, + #[error("unsupported socks version {0:#x}")] + UnsupportedSocksVersion(u8), + #[error("unsupported command {0:#x}")] + UnsupportedCommand(u8), + #[error("{0}")] + Reply(Reply), +} + +impl From for io::Error { + fn from(err: Error) -> io::Error { + match err { + Error::IoError(err) => err, + e => io::Error::new(ErrorKind::Other, e), + } + } +} + +impl Error { + /// Convert to `Reply` for responding + pub fn as_reply(&self) -> Reply { + match *self { + Error::IoError(ref err) => match err.kind() { + ErrorKind::ConnectionRefused => Reply::ConnectionRefused, + _ => Reply::GeneralFailure, + }, + Error::AddressTypeNotSupported(..) => Reply::AddressTypeNotSupported, + Error::AddressDomainInvalidEncoding => Reply::GeneralFailure, + Error::UnsupportedSocksVersion(..) => Reply::GeneralFailure, + Error::UnsupportedCommand(..) => Reply::CommandNotSupported, + Error::Reply(r) => r, + } + } +} + +/// SOCKS5 address type +#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum Address { + /// Socket address (IP Address) + SocketAddress(SocketAddr), + /// Domain name address + DomainNameAddress(String, u16), +} + +#[derive(Debug, PartialEq, Eq)] +pub enum Socks5State { + NoProxy, + Socks5HostNotConnected, // first we send auth request + Socks5AuthSend, // socks5 reply auth method + Socks5AuthUsernamePasswordSend, // send username/password to socks5 + Socks5AuthUsernamePasswordDone, // socks5 username/password authorize done + Socks5AuthDone, // socks5 authorize done, send cmd connect with target addr/port + TargetAddrSend, // socks reply cmd connect status + RemoteConnected // socks5 reply X'00', succeeded +} + +/// Authentication methods + +#[derive(Debug)] +pub enum Authentication<'a> { + Password { username: &'a str, password: &'a str }, + None +} + +impl<'a> Authentication<'a> { + pub fn id(&self) -> u8 { + match *self { + Authentication::Password { .. } => 2, + Authentication::None => 0 + } + } + + pub fn is_no_auth(&self) -> bool { + if let Authentication::None = *self { + true + } else { + false + } + } +} + + +fn read_addr(socket: &mut R) -> io::Result { + match socket.read_u8()? { + 1 => { // socks5 ipv4 + let ip = Ipv4Addr::from(socket.read_u32::()?); + let port = socket.read_u16::()?; + Ok(SocketAddrV4::new(ip, port)) + } + _ => Err(io::Error::new(io::ErrorKind::Other, "unsupported address type")), + } +} + +// must be called when socks5_state = TargetAddrSend and selector is readable +pub fn socks5_read_response(socket: &mut TcpStream) -> io::Result { + if socket.read_u8()? != 5 { + return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid response version")); + } + + match socket.read_u8()? { + 0 => {} + 1 => return Err(io::Error::new(io::ErrorKind::Other, "general SOCKS server failure")), + 2 => return Err(io::Error::new(io::ErrorKind::Other, "connection not allowed by ruleset")), + 3 => return Err(io::Error::new(io::ErrorKind::Other, "network unreachable")), + 4 => return Err(io::Error::new(io::ErrorKind::Other, "host unreachable")), + 5 => return Err(io::Error::new(io::ErrorKind::Other, "connection refused")), + 6 => return Err(io::Error::new(io::ErrorKind::Other, "TTL expired")), + 7 => return Err(io::Error::new(io::ErrorKind::Other, "command not supported")), + 8 => return Err(io::Error::new(io::ErrorKind::Other, "address kind not supported")), + _ => return Err(io::Error::new(io::ErrorKind::Other, "unknown error")), + } + + if socket.read_u8()? != 0 { + return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid reserved byte")); + } + + read_addr(socket) +} + +pub fn socks5_read_auth_method_response(socket : &mut TcpStream) -> io::Result { + let mut buf = [0; 2]; + socket.read_exact(&mut buf)?; + + let response_version = buf[0]; + let selected_method = buf[1]; + + if response_version != 5 { + return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid auth response version")); + } else { + Ok(selected_method) + } +} + +pub fn socks5_read_username_password_auth_response(socket : &mut TcpStream) -> io::Result { + let mut buf = [0; 2]; + socket.read_exact(&mut buf)?; + + let response_version = buf[0]; + let authenticate_status = buf[1]; + + if response_version != 1 { + return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid auth u/p response version")); + } else { + Ok(authenticate_status) + } +} \ No newline at end of file diff --git a/relay-rust/src/relay/tcp_connection.rs b/relay-rust/src/relay/tcp_connection.rs index 616d52b4..8da24ac1 100644 --- a/relay-rust/src/relay/tcp_connection.rs +++ b/relay-rust/src/relay/tcp_connection.rs @@ -22,8 +22,17 @@ use std::cell::RefCell; use std::cmp; use std::io; use std::num::Wrapping; +use std::net::{SocketAddrV4}; use std::rc::{Rc, Weak}; +use byteorder::{WriteBytesExt}; + +use super::proxy_config::ProxyConfig; +use super::proxy_config::get_proxy_for_addr; +use super::socks5_protocol::{Socks5State, Authentication}; +use super::socks5_protocol; +use super::socks5_protocol::MAX_ADDR_LEN; + use super::binary; use super::client::{Client, ClientChannel}; use super::connection::{Connection, ConnectionId}; @@ -55,6 +64,7 @@ pub struct TcpConnection { packet_for_client_length: Option, closed: bool, tcb: Tcb, + socks5_state : Socks5State, } // Transport Control Block @@ -140,7 +150,20 @@ impl TcpConnection { transport_header: TransportHeader, ) -> io::Result>> { cx_info!(target: TAG, id, "Open"); - let stream = Self::create_stream(&id)?; + + // determine if we should use proxy for destination ip + let stream : TcpStream; + let proxy_init_state : Socks5State; + match get_proxy_for_addr(id.rewritten_destination().into()) { + None => { + stream = Self::create_stream(&id)?; + proxy_init_state = Socks5State::NoProxy; + } + Some(cnf) => { + stream = Self::create_proxy_stream(&cnf)?; + proxy_init_state = Socks5State::Socks5HostNotConnected; + }, + } let tcp_header = Self::tcp_header_of_transport(transport_header); @@ -176,6 +199,7 @@ impl TcpConnection { packet_for_client_length: None, closed: false, tcb: Tcb::new(), + socks5_state : proxy_init_state, })); { @@ -198,7 +222,11 @@ impl TcpConnection { fn create_stream(id: &ConnectionId) -> io::Result { TcpStream::connect(&id.rewritten_destination().into()) } - + + fn create_proxy_stream(proxy_config: &ProxyConfig ) -> io::Result { + TcpStream::connect(&proxy_config.proxy_addr.into()) + } + fn remove_from_router(&self) { // route is embedded in router which is embedded in client: the client necessarily exists let client_rc = self.client.upgrade().expect("Expected client not found"); @@ -216,31 +244,285 @@ impl TcpConnection { Err(_) => panic!("Unexpected unhandled error"), } } + + fn socks5_update_interests(&mut self, selector: &mut Selector, new_interests: Ready) { + assert!(!self.closed); + + cx_debug!(target: TAG, self.id, "socks5_update_interests: {:?}", new_interests); + if self.interests != new_interests { + // interests must be changed + self.interests = new_interests; + selector + .reregister(&self.stream, self.token, new_interests, PollOpt::level()) + .expect("Cannot register on poll"); + } + } + + + fn handle_socks5_state(&mut self, selector: &mut Selector, ready: Ready) -> io::Result<()> { + let proxy_config: ProxyConfig; + + match get_proxy_for_addr(self.id.rewritten_destination().into()) { + None => panic!("don't set proxy parameters"), + Some(c) => proxy_config = c, + }; + + match self.socks5_state { + // Gnirehtet socks5 client -> socks5 server, request to authenticate + Socks5State::Socks5HostNotConnected => { + let auth : Authentication = match proxy_config.username.len() { + 0 => Authentication::None, + _ => Authentication::Password { username: &* (proxy_config.username), password: &* proxy_config.password }, + }; + + let packet_len = if auth.is_no_auth() { 3 } else { 4 }; + let packet = [ + socks5_protocol::consts::SOCKS5_VERSION, // protocol version + if auth.is_no_auth() { 1 } else { 2 }, // method count + 0, // no auth (always offered) + auth.id(), // method + ]; + + self.client_to_network.read_from(&packet[..packet_len]); + match self.client_to_network.write_to(&mut self.stream) { + //match self.stream.write_all(packet[..packet_len]) { + Ok(w) => { + cx_debug!(target: TAG, self.id, "Write to socks5 for auth request {}, packet payload length: {}", auth.id(), w); + self.socks5_update_interests(selector, Ready::readable()); // change interest to write after read + self.socks5_state = Socks5State::Socks5AuthSend; + } + Err(err) => { + if err.kind() == io::ErrorKind::WouldBlock { + // rethrow + return Err(err); + } + cx_error!(target: TAG, self.id, "Cannot write socks5 for auth request: [{:?}] {}", err.kind(), err ); + // error or hup + self.close(selector); + } + } + }, + // socks5 server <- Gnirehtet socks5 client, no auth or username/password authenticate + Socks5State::Socks5AuthSend => { + if ready.is_readable() { + match socks5_protocol::socks5_read_auth_method_response(&mut self.stream) { + Ok(selected_method) =>{ + cx_debug!(target: TAG, self.id, "SOCKS5 LOG auth method = {}", selected_method); + + self.socks5_update_interests(selector, Ready::writable()); // change interest to write after read + + match selected_method { + 0 => { + // if no auth need, goto cmd connect + self.socks5_state = Socks5State::Socks5AuthDone; + }, + 2 => { + self.socks5_state = Socks5State::Socks5AuthUsernamePasswordSend; + } + _ => cx_error!(target: TAG, self.id, "SOCKS5 ERR unsupported auth method {}", selected_method) + } + } + Err(err) => { + if err.kind() == io::ErrorKind::WouldBlock { + // rethrow + return Err(err); + } + cx_error!(target: TAG, self.id, "SOCKS5 ERR Cannot read socks5 for auth response: [{:?}] {}", err.kind(), err); + self.send_empty_packet_to_client(selector, tcp_header::FLAG_RST); + self.close(selector); + } + } + } + }, + // Gnirehtet socks5 client -> socks5 server, username/password authenticate + Socks5State::Socks5AuthUsernamePasswordSend => { + if ready.is_writable() { + let mut packet = [0; MAX_ADDR_LEN]; + let username = proxy_config.username.as_bytes(); + let password = proxy_config.password.as_bytes(); + + packet[0] = 1; // protocol version + + packet[1] = 0; // ulen + let mut u: &mut [u8] = &mut packet[2..]; + let mut ulen: usize = 0; + for byte in username { // copy_from_slice + let _ = u.write_u8(*byte); + ulen += 1; + } + packet[1] = ulen as u8; // ulen + + packet[2+ulen] = 0; // plen + let mut p: &mut [u8] = &mut packet[2+ulen+1..]; + let mut plen: usize = 0; + for byte in password { // copy_from_slice + let _ = p.write_u8(*byte); + plen += 1; + } + packet[2+ulen] = plen as u8; // plen + + self.client_to_network.read_from(&packet[..3+ulen+plen]); + match self.client_to_network.write_to(&mut self.stream) { + //match self.stream.write_all(packet[..packet_len]) { + Ok(w) => { + cx_debug!(target: TAG, self.id, "packets {:X?}", &packet[..3+ulen+plen]); + cx_debug!(target: TAG, self.id, "Write to socks5 for username/password authenticate, packet payload length: {}", w); + self.socks5_update_interests(selector, Ready::readable()); // change interest to write after read + self.socks5_state = Socks5State::Socks5AuthUsernamePasswordDone; + } + Err(err) => { + if err.kind() == io::ErrorKind::WouldBlock { + // rethrow + return Err(err); + } + cx_error!(target: TAG, self.id, "Cannot write socks5 for username/password authenticate: [{:?}] {}", err.kind(), err ); + // error or hup + self.close(selector); + } + } + } + } + // socks5 server <- Gnirehtet socks5 client, check authenticate result, expect 5 1 + Socks5State::Socks5AuthUsernamePasswordDone => { + if ready.is_readable() { + match socks5_protocol::socks5_read_username_password_auth_response(&mut self.stream) { + Ok(authenticate_status) =>{ + cx_debug!(target: TAG, self.id, "SOCKS5 LOG authenticate status = {}", authenticate_status); + + self.socks5_update_interests(selector, Ready::writable()); // change interest to write after read + + match authenticate_status { + 0 => { + // if no auth need, goto cmd connect + self.socks5_update_interests(selector, Ready::writable()); // change interest to write after read + self.socks5_state = Socks5State::Socks5AuthDone; + }, + _ => { + cx_error!(target: TAG, self.id, "SOCKS5 ERR authenticate failed {}", authenticate_status); + self.send_empty_packet_to_client(selector, tcp_header::FLAG_RST); + self.close(selector); + } + } + } + Err(err) => { + if err.kind() == io::ErrorKind::WouldBlock { + // rethrow + return Err(err); + } + cx_error!(target: TAG, self.id, "SOCKS5 ERR Cannot read socks5 for auth response: [{:?}] {}", err.kind(), err); + self.send_empty_packet_to_client(selector, tcp_header::FLAG_RST); + self.close(selector); + } + } + } + } + // Gnirehtet socks5 client -> socks5 server: cmd connect + Socks5State::Socks5AuthDone => { + if ready.is_writable() { + + let mut packet = [0; MAX_ADDR_LEN + 3]; + packet[0] = socks5_protocol::consts::SOCKS5_VERSION; // protocol version + packet[1] = socks5_protocol::consts::SOCKS5_CMD_TCP_CONNECT; // command + packet[2] = 0; // reserved + packet[3] = 1; // ATYP address type of IP V4 + + let target_addr: SocketAddrV4 = self.id.rewritten_destination().into(); + let to_addr : [u8; 4] = target_addr.ip().octets(); + packet[4] = to_addr[0]; + packet[5] = to_addr[1]; + packet[6] = to_addr[2]; + packet[7] = to_addr[3]; + + let to_port : [u8; 2] = target_addr.port().to_be_bytes(); + packet[8] = to_port[0]; + packet[9] = to_port[1]; + + self.client_to_network.read_from(&packet[..10]); + + match self.client_to_network.write_to(&mut self.stream) { + Ok(w) => { + cx_debug!(target: TAG, self.id, "Write to socks5 for cmd connect, packet payload length:{}", w); + self.socks5_update_interests(selector, Ready::readable()); // change interest to read after write + self.socks5_state = Socks5State::TargetAddrSend; + } + Err(err) => { + if err.kind() == io::ErrorKind::WouldBlock { + // rethrow + return Err(err); + } + cx_error!(target: TAG, self.id, "Cannot write socks5 for cmd connect: [{:?}] {}", err.kind(), err); + // error or hup + self.close(selector); + } + } + } + }, + // check cmd connect result, if ok, all done. + Socks5State::TargetAddrSend => { + if ready.is_readable() { + match socks5_protocol::socks5_read_response(&mut self.stream) { + Ok(addr) => { + // if no auth need, goto cmd connect + cx_error!(target: TAG, self.id, "Read socks5 for cmd connect, OK, response: {}", addr); + + self.socks5_update_interests(selector, Ready::writable()); // change interest to write after read + self.socks5_state = Socks5State::RemoteConnected; + } + Err(err) => { + if err.kind() == io::ErrorKind::WouldBlock { + // rethrow + return Err(err); + } + cx_error!(target: TAG, self.id, "Cannot read socks5 for auth response: [{:?}] {}", err.kind(), err); + self.send_empty_packet_to_client(selector, tcp_header::FLAG_RST); + self.close(selector); + } + } + } + }, + _ => { + cx_debug!(target: TAG, self.id, "unknown socks5_state"); + } + } + + Ok(()) + } + // return Err(err) with err.kind() == io::ErrorKind::WouldBlock on spurious event fn process(&mut self, selector: &mut Selector, event: Event) -> io::Result<()> { if !self.closed { let ready = event.readiness(); if ready.is_readable() || ready.is_writable() { + // if use proxy and proxy not ready to use, prepare socks5 connection first. + if self.socks5_state != Socks5State::NoProxy && self.socks5_state != Socks5State::RemoteConnected { + // should connect proxy first + let _ = self.handle_socks5_state(selector, ready); + + return Ok(()) + } + if ready.is_writable() { if self.tcb.state == TcpState::SynSent { // writable is first triggered when the stream is connected self.process_connect(selector); } else { self.process_send(selector)?; - } + } } + if !self.closed && ready.is_readable() { match self.process_receive(selector) { Ok(_) => (), Err(err) => { if err.kind() == io::ErrorKind::WouldBlock && ready.is_writable() { - cx_debug!(target: TAG, self.id, "already write, update interests here"); - self.update_interests(selector); + cx_debug!(target: TAG, self.id, "already write, update interests here"); + self.update_interests(selector); } return Err(err); } } } + if !self.closed { self.update_interests(selector); } @@ -283,6 +565,7 @@ impl TcpConnection { self.send_empty_packet_to_client(selector, tcp_header::FLAG_ACK); } } else { + cx_debug!(target: TAG, self.id, "State = {:?}, process_send, close selector", self.tcb.state); self.close(selector); } } @@ -593,6 +876,7 @@ impl TcpConnection { ) { let tcp_header = Self::tcp_header_of_packet(ipv4_packet); let their_sequence_number = tcp_header.sequence_number(); + cx_debug!(target: TAG, self.id, "State = {:?}, handle_duplicate_syn", self.tcb.state); if self.tcb.state == TcpState::SynSent { // the connection is not established yet, we can accept this packet as if it were the // first SYN @@ -658,11 +942,11 @@ impl TcpConnection { } fn handle_fin_ack(&mut self, selector: &mut Selector) { + cx_debug!(target: TAG, self.id, "State = {:?}", self.tcb.state); if self.tcb.state == TcpState::LastAck || self.tcb.state == TcpState::Closing { self.close(selector); } else if self.tcb.state == TcpState::FinWait1 { self.tcb.state = TcpState::FinWait2; - cx_debug!(target: TAG, self.id, "State = {:?}", self.tcb.state); } else if self.tcb.state != TcpState::FinWait2 { cx_warn!( target: TAG, @@ -847,3 +1131,29 @@ impl PacketSource for TcpConnection { self.update_interests(selector); } } + +/* +impl AsyncRead for mio::net::TcpStream { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut task::Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + self.project().stream.poll_read(cx, buf) + } +} + +impl AsyncWrite for mio::net::TcpStream { + fn poll_write(self: Pin<&mut Self>, cx: &mut task::Context<'_>, buf: &[u8]) -> Poll> { + self.project().stream.poll_write(cx, buf) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll> { + self.project().stream.poll_flush(cx) + } + + fn poll_shutdown(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll> { + self.project().stream.poll_shutdown(cx) + } +} +*/ \ No newline at end of file diff --git a/relay-rust/src/relay/udp_connection.rs b/relay-rust/src/relay/udp_connection.rs index 936d578b..18a61b7f 100644 --- a/relay-rust/src/relay/udp_connection.rs +++ b/relay-rust/src/relay/udp_connection.rs @@ -60,6 +60,7 @@ impl UdpConnection { ) -> io::Result>> { cx_info!(target: TAG, id, "Open"); let socket = Self::create_socket(&id)?; + //let socket = Self::create_proxy_socket(&id)?; let packetizer = Packetizer::new(&ipv4_header, &transport_header); let interests = Ready::readable(); let rc = Rc::new(RefCell::new(Self { @@ -94,7 +95,7 @@ impl UdpConnection { udp_socket.connect(id.rewritten_destination().into())?; Ok(udp_socket) } - + fn remove_from_router(&self) { // route is embedded in router which is embedded in client: the client necessarily exists let client_rc = self.client.upgrade().expect("Expected client not found");