mirror of https://github.com/ospab/ostp.git
fix(server): generate correct public IP for client configs instead of 0.0.0.0
This commit is contained in:
parent
f62a342688
commit
36a325997c
|
|
@ -111,6 +111,30 @@ version = "1.0.102"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayvec"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-channel"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2"
|
||||||
|
dependencies = [
|
||||||
|
"concurrent-queue",
|
||||||
|
"event-listener-strategy",
|
||||||
|
"futures-core",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-task"
|
||||||
|
version = "4.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atomic-waker"
|
name = "atomic-waker"
|
||||||
version = "1.1.2"
|
version = "1.1.2"
|
||||||
|
|
@ -211,6 +235,19 @@ dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blocking"
|
||||||
|
version = "1.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21"
|
||||||
|
dependencies = [
|
||||||
|
"async-channel",
|
||||||
|
"async-task",
|
||||||
|
"futures-io",
|
||||||
|
"futures-lite",
|
||||||
|
"piper",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.20.2"
|
version = "3.20.2"
|
||||||
|
|
@ -229,26 +266,6 @@ version = "1.11.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
|
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "c2rust-bitfields"
|
|
||||||
version = "0.18.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b43c3f07ab0ef604fa6f595aa46ec2f8a22172c975e186f6f5bf9829a3b72c41"
|
|
||||||
dependencies = [
|
|
||||||
"c2rust-bitfields-derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "c2rust-bitfields-derive"
|
|
||||||
version = "0.18.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d3cbc102e2597c9744c8bd8c15915d554300601c91a079430d309816b0912545"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.109",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.62"
|
version = "1.2.62"
|
||||||
|
|
@ -356,7 +373,7 @@ dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.117",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -381,6 +398,15 @@ dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "concurrent-queue"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation-sys"
|
name = "core-foundation-sys"
|
||||||
version = "0.8.7"
|
version = "0.8.7"
|
||||||
|
|
@ -396,6 +422,12 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
|
|
@ -439,7 +471,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.117",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -471,7 +503,7 @@ dependencies = [
|
||||||
"proc-macro-error2",
|
"proc-macro-error2",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.117",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -511,7 +543,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.117",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -527,9 +559,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "etherparse"
|
||||||
|
version = "0.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8d8a704b617484e9d867a0423cd45f7577f008c4068e2e33378f8d3860a6d73"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "event-listener"
|
||||||
|
version = "5.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab"
|
||||||
|
dependencies = [
|
||||||
|
"concurrent-queue",
|
||||||
|
"parking",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "event-listener-strategy"
|
||||||
|
version = "0.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
|
||||||
|
dependencies = [
|
||||||
|
"event-listener",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastrand"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fiat-crypto"
|
name = "fiat-crypto"
|
||||||
version = "0.2.9"
|
version = "0.2.9"
|
||||||
|
|
@ -557,6 +625,21 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures"
|
||||||
|
version = "0.3.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-executor",
|
||||||
|
"futures-io",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.32"
|
version = "0.3.32"
|
||||||
|
|
@ -564,6 +647,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
|
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -572,6 +656,33 @@ version = "0.3.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
|
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-executor"
|
||||||
|
version = "0.3.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-io"
|
||||||
|
version = "0.3.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-lite"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-macro"
|
name = "futures-macro"
|
||||||
version = "0.3.32"
|
version = "0.3.32"
|
||||||
|
|
@ -580,7 +691,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.117",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -601,9 +712,13 @@ version = "0.3.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
|
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
"futures-macro",
|
"futures-macro",
|
||||||
|
"futures-sink",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
|
"memchr",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
@ -834,7 +949,7 @@ dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"log",
|
"log",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"windows-core 0.62.2",
|
"windows-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -976,12 +1091,6 @@ dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ioctl-sys"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8bd11f3a29434026f5ff98c730b668ba74b1033637b8817940b54d040696133c"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipnet"
|
name = "ipnet"
|
||||||
version = "2.12.0"
|
version = "2.12.0"
|
||||||
|
|
@ -1041,7 +1150,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264"
|
checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.117",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1076,15 +1185,15 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.185"
|
version = "0.2.186"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f"
|
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
version = "0.8.9"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
|
checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"windows-link",
|
"windows-link",
|
||||||
|
|
@ -1096,6 +1205,15 @@ version = "0.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
|
checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||||
|
dependencies = [
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.29"
|
version = "0.4.29"
|
||||||
|
|
@ -1162,6 +1280,34 @@ dependencies = [
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "netstack-smoltcp"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "398691cef792b89eb5d29e6ea6b3999def706b908d355e29815ba8101cf5c4c8"
|
||||||
|
dependencies = [
|
||||||
|
"etherparse",
|
||||||
|
"futures",
|
||||||
|
"rand 0.8.5",
|
||||||
|
"smoltcp",
|
||||||
|
"spin",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.31.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.11.1",
|
||||||
|
"cfg-if",
|
||||||
|
"cfg_aliases",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-ansi-term"
|
name = "nu-ansi-term"
|
||||||
version = "0.50.3"
|
version = "0.50.3"
|
||||||
|
|
@ -1232,9 +1378,12 @@ dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"bytes",
|
"bytes",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"futures",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hmac",
|
"hmac",
|
||||||
"json_comments",
|
"json_comments",
|
||||||
|
"libc",
|
||||||
|
"netstack-smoltcp",
|
||||||
"ostp-core",
|
"ostp-core",
|
||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
|
|
@ -1243,14 +1392,12 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
"smoltcp",
|
|
||||||
"socket2",
|
"socket2",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tun",
|
"tun",
|
||||||
"webpki-roots 0.26.11",
|
"webpki-roots 0.26.11",
|
||||||
"wintun 0.4.0",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1282,6 +1429,8 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1328,6 +1477,12 @@ dependencies = [
|
||||||
"winres",
|
"winres",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking"
|
||||||
|
version = "2.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pem"
|
name = "pem"
|
||||||
version = "3.0.6"
|
version = "3.0.6"
|
||||||
|
|
@ -1350,6 +1505,17 @@ version = "0.2.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "piper"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1"
|
||||||
|
dependencies = [
|
||||||
|
"atomic-waker",
|
||||||
|
"fastrand",
|
||||||
|
"futures-io",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "poly1305"
|
name = "poly1305"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
|
|
@ -1410,7 +1576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"syn 2.0.117",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1432,7 +1598,7 @@ dependencies = [
|
||||||
"proc-macro-error-attr2",
|
"proc-macro-error-attr2",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.117",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1681,7 +1847,7 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rust-embed-utils",
|
"rust-embed-utils",
|
||||||
"syn 2.0.117",
|
"syn",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -1767,6 +1933,12 @@ dependencies = [
|
||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "1.0.28"
|
version = "1.0.28"
|
||||||
|
|
@ -1800,7 +1972,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.117",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1889,15 +2061,16 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smoltcp"
|
name = "smoltcp"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a1a996951e50b5971a2c8c0fa05a381480d70a933064245c4a223ddc87ccc97"
|
checksum = "dad095989c1533c1c266d9b1e8d70a1329dd3723c3edac6d03bbd67e7bf6f4bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"defmt 0.3.100",
|
"defmt 0.3.100",
|
||||||
"heapless",
|
"heapless",
|
||||||
|
"log",
|
||||||
"managed",
|
"managed",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -1927,6 +2100,15 @@ dependencies = [
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spin"
|
||||||
|
version = "0.9.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stable_deref_trait"
|
name = "stable_deref_trait"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
|
|
@ -1945,17 +2127,6 @@ version = "2.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "1.0.109"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.117"
|
version = "2.0.117"
|
||||||
|
|
@ -1984,7 +2155,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.117",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2013,7 +2184,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.117",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2024,7 +2195,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.117",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2082,9 +2253,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.52.0"
|
version = "1.52.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a91135f59b1cbf38c91e73cf3386fca9bb77915c45ce2771460c9d92f0f3d776"
|
checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
|
|
@ -2104,7 +2275,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.117",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2205,7 +2376,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.117",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2255,20 +2426,23 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tun"
|
name = "tun"
|
||||||
version = "0.6.1"
|
version = "0.8.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0adb9992bbd5ca76f3847ed579ad4ee8defb2ec2eea918cceef17ccc66fa4fd4"
|
checksum = "bb51dd4da3b9e08057abbaed588d4244bccb0a4122788aff2775b9a4b9aec489"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"cfg-if",
|
||||||
|
"futures",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"ioctl-sys",
|
"ipnet",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"thiserror 1.0.69",
|
"nix",
|
||||||
|
"thiserror 2.0.18",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"wintun 0.3.2",
|
"windows-sys 0.61.2",
|
||||||
|
"wintun-bindings",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2444,7 +2618,7 @@ dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.117",
|
"syn",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -2535,45 +2709,7 @@ version = "0.1.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.52.0",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows"
|
|
||||||
version = "0.51.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9"
|
|
||||||
dependencies = [
|
|
||||||
"windows-core 0.51.1",
|
|
||||||
"windows-targets 0.48.5",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows"
|
|
||||||
version = "0.52.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
|
|
||||||
dependencies = [
|
|
||||||
"windows-core 0.52.0",
|
|
||||||
"windows-targets 0.52.6",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-core"
|
|
||||||
version = "0.51.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
|
|
||||||
dependencies = [
|
|
||||||
"windows-targets 0.48.5",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-core"
|
|
||||||
version = "0.52.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
|
||||||
dependencies = [
|
|
||||||
"windows-targets 0.52.6",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2597,7 +2733,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.117",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2608,7 +2744,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.117",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2677,21 +2813,6 @@ dependencies = [
|
||||||
"windows_x86_64_msvc 0.42.2",
|
"windows_x86_64_msvc 0.42.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-targets"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
|
||||||
dependencies = [
|
|
||||||
"windows_aarch64_gnullvm 0.48.5",
|
|
||||||
"windows_aarch64_msvc 0.48.5",
|
|
||||||
"windows_i686_gnu 0.48.5",
|
|
||||||
"windows_i686_msvc 0.48.5",
|
|
||||||
"windows_x86_64_gnu 0.48.5",
|
|
||||||
"windows_x86_64_gnullvm 0.48.5",
|
|
||||||
"windows_x86_64_msvc 0.48.5",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
|
|
@ -2714,12 +2835,6 @@ version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_gnullvm"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
|
|
@ -2732,12 +2847,6 @@ version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_msvc"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
|
|
@ -2750,12 +2859,6 @@ version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_gnu"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
|
|
@ -2774,12 +2877,6 @@ version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_msvc"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
|
|
@ -2792,12 +2889,6 @@ version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnu"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
|
|
@ -2810,12 +2901,6 @@ version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnullvm"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
|
|
@ -2828,12 +2913,6 @@ version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_msvc"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
|
|
@ -2850,29 +2929,17 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wintun"
|
name = "wintun-bindings"
|
||||||
version = "0.3.2"
|
version = "0.7.38"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "29b83b0eca06dd125dbcd48a45327c708a6da8aada3d95a3f06db0ce4b17e0d4"
|
checksum = "4d94caf41ce60c37c9215c68f124835c401d2ec4f9b3b6431b491c79b3cdd335"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"c2rust-bitfields",
|
"blocking",
|
||||||
|
"futures",
|
||||||
"libloading",
|
"libloading",
|
||||||
"log",
|
"log",
|
||||||
"thiserror 1.0.69",
|
"thiserror 2.0.18",
|
||||||
"windows 0.51.1",
|
"windows-sys 0.61.2",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wintun"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1b3c8c8876c686f8a2d6376999ac1c9a24c74d2968551c9394f7e89127783685"
|
|
||||||
dependencies = [
|
|
||||||
"c2rust-bitfields",
|
|
||||||
"libloading",
|
|
||||||
"log",
|
|
||||||
"thiserror 1.0.69",
|
|
||||||
"windows 0.52.0",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2911,7 +2978,7 @@ dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"prettyplease",
|
"prettyplease",
|
||||||
"syn 2.0.117",
|
"syn",
|
||||||
"wasm-metadata",
|
"wasm-metadata",
|
||||||
"wit-bindgen-core",
|
"wit-bindgen-core",
|
||||||
"wit-component",
|
"wit-component",
|
||||||
|
|
@ -2927,7 +2994,7 @@ dependencies = [
|
||||||
"prettyplease",
|
"prettyplease",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.117",
|
"syn",
|
||||||
"wit-bindgen-core",
|
"wit-bindgen-core",
|
||||||
"wit-bindgen-rust",
|
"wit-bindgen-rust",
|
||||||
]
|
]
|
||||||
|
|
@ -3003,7 +3070,7 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.117",
|
"syn",
|
||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -3024,7 +3091,7 @@ checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.117",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -3044,7 +3111,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.117",
|
"syn",
|
||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -3084,7 +3151,7 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.117",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,7 @@ sha2 = "0.10.8"
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
webpki-roots = "0.26"
|
webpki-roots = "0.26"
|
||||||
rustls-pki-types = "1.7"
|
rustls-pki-types = "1.7"
|
||||||
smoltcp = { version = "0.11", default-features = false, features = ["std", "medium-ip", "proto-ipv4", "socket-tcp", "socket-udp"] }
|
tun = { version = "0.8.9", features = ["async"] }
|
||||||
|
netstack-smoltcp = "0.2.2"
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
futures = "0.3.32"
|
||||||
wintun = "0.4"
|
libc = "0.2.186"
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
|
||||||
tun = { version = "0.6", features = ["async"] }
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,12 @@ pub struct ClientConfig {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub multiplex: MultiplexConfig,
|
pub multiplex: MultiplexConfig,
|
||||||
pub dns_server: Option<String>,
|
pub dns_server: Option<String>,
|
||||||
|
#[serde(default = "default_tun_stack")]
|
||||||
|
pub tun_stack: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_tun_stack() -> String { "system".to_string() }
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||||
pub struct ExclusionConfig {
|
pub struct ExclusionConfig {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|
@ -142,6 +146,7 @@ impl Default for ClientConfig {
|
||||||
exclusions: ExclusionConfig::default(),
|
exclusions: ExclusionConfig::default(),
|
||||||
multiplex: MultiplexConfig::default(),
|
multiplex: MultiplexConfig::default(),
|
||||||
dns_server: None,
|
dns_server: None,
|
||||||
|
tun_stack: "system".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -184,6 +189,7 @@ struct RawTransportSection {
|
||||||
struct RawTunSection {
|
struct RawTunSection {
|
||||||
enable: Option<bool>,
|
enable: Option<bool>,
|
||||||
dns: Option<String>,
|
dns: Option<String>,
|
||||||
|
stack: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
|
@ -275,6 +281,7 @@ impl ClientConfig {
|
||||||
sessions: mux.sessions.unwrap_or(1),
|
sessions: mux.sessions.unwrap_or(1),
|
||||||
},
|
},
|
||||||
dns_server: raw.tun.as_ref().and_then(|t| t.dns.clone()),
|
dns_server: raw.tun.as_ref().and_then(|t| t.dns.clone()),
|
||||||
|
tun_stack: raw.tun.as_ref().and_then(|t| t.stack.clone()).unwrap_or_else(|| "system".to_string()),
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -198,6 +198,7 @@ pub async fn run_client_core(
|
||||||
let (ui_tx, mut ui_rx) = mpsc::channel(512);
|
let (ui_tx, mut ui_rx) = mpsc::channel(512);
|
||||||
let (cmd_tx, cmd_rx) = mpsc::channel(128);
|
let (cmd_tx, cmd_rx) = mpsc::channel(128);
|
||||||
let (shutdown_tx, shutdown_rx) = watch::channel(false);
|
let (shutdown_tx, shutdown_rx) = watch::channel(false);
|
||||||
|
let proxy_shutdown_rx = shutdown_tx.subscribe();
|
||||||
|
|
||||||
|
|
||||||
// Auto-connect on startup
|
// Auto-connect on startup
|
||||||
|
|
@ -247,29 +248,26 @@ pub async fn run_client_core(
|
||||||
});
|
});
|
||||||
|
|
||||||
let config_clone = config.clone();
|
let config_clone = config.clone();
|
||||||
let (mut proxy_task, mut wintun_task) = if config.mode == "proxy" {
|
let mut proxy_task = tokio::spawn(async move {
|
||||||
let proxy_shutdown_rx = shutdown_tx.subscribe();
|
tunnel::run_local_proxy(
|
||||||
let t = tokio::spawn(async move {
|
config.local_proxy,
|
||||||
tunnel::run_local_proxy(
|
config.ostp,
|
||||||
config.local_proxy,
|
config.exclusions,
|
||||||
config.ostp,
|
config.debug,
|
||||||
config.exclusions,
|
proxy_shutdown_rx,
|
||||||
config.debug,
|
proxy_events_tx,
|
||||||
proxy_shutdown_rx,
|
client_msgs_rx,
|
||||||
proxy_events_tx,
|
)
|
||||||
client_msgs_rx,
|
.await
|
||||||
)
|
});
|
||||||
.await
|
|
||||||
});
|
let wintun_shutdown_rx = shutdown_tx.subscribe();
|
||||||
(Some(t), None)
|
let mut wintun_task = if config_clone.mode == "tun" {
|
||||||
} else if config.mode == "tun" {
|
Some(tokio::spawn(async move {
|
||||||
let wintun_shutdown_rx = shutdown_tx.subscribe();
|
tunnel::run_tun_tunnel(config_clone, wintun_shutdown_rx).await
|
||||||
let t = tokio::spawn(async move {
|
}))
|
||||||
tunnel::run_tun_tunnel(config_clone, proxy_events_tx, client_msgs_rx, wintun_shutdown_rx).await
|
|
||||||
});
|
|
||||||
(None, Some(t))
|
|
||||||
} else {
|
} else {
|
||||||
(None, None)
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
// Wait for either external shutdown OR any task to fail
|
// Wait for either external shutdown OR any task to fail
|
||||||
|
|
@ -282,9 +280,7 @@ pub async fn run_client_core(
|
||||||
let _ = shutdown_tx.send(true);
|
let _ = shutdown_tx.send(true);
|
||||||
res.map_err(|e| anyhow::anyhow!("Bridge task panicked: {}", e))??;
|
res.map_err(|e| anyhow::anyhow!("Bridge task panicked: {}", e))??;
|
||||||
}
|
}
|
||||||
res = async {
|
res = &mut proxy_task => {
|
||||||
if let Some(ref mut t) = proxy_task { t.await } else { std::future::pending().await }
|
|
||||||
} => {
|
|
||||||
let _ = shutdown_tx.send(true);
|
let _ = shutdown_tx.send(true);
|
||||||
res.map_err(|e| anyhow::anyhow!("Proxy task panicked: {}", e))??;
|
res.map_err(|e| anyhow::anyhow!("Proxy task panicked: {}", e))??;
|
||||||
}
|
}
|
||||||
|
|
@ -298,9 +294,7 @@ pub async fn run_client_core(
|
||||||
|
|
||||||
// Final cleanup: wait for tasks to finish
|
// Final cleanup: wait for tasks to finish
|
||||||
let _ = bridge_task.await;
|
let _ = bridge_task.await;
|
||||||
if let Some(task) = proxy_task {
|
let _ = proxy_task.await;
|
||||||
let _ = task.await;
|
|
||||||
}
|
|
||||||
if let Some(task) = wintun_task {
|
if let Some(task) = wintun_task {
|
||||||
let _ = task.await;
|
let _ = task.await;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,27 @@
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use tokio::sync::{mpsc, watch};
|
use tokio::sync::watch;
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
use tracing::info;
|
|
||||||
|
|
||||||
use crate::tunnel::{ProxyEvent, ProxyToClientMsg};
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
use crate::tunnel::tun_device::create_tun_device;
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
use crate::tunnel::smoltcp_stack::run_smoltcp_stack;
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use std::net::ToSocketAddrs;
|
use std::net::ToSocketAddrs;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use std::process::Command;
|
use std::process::{Command, Stdio, Child};
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
use std::io::{BufRead, BufReader};
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
struct LinuxRouteGuard {
|
struct LinuxRouteGuard {
|
||||||
server_ip_str: String,
|
server_ip_str: String,
|
||||||
default_gw: String,
|
default_gw: String,
|
||||||
default_if: String,
|
default_if: String,
|
||||||
|
child: Option<Child>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
impl Drop for LinuxRouteGuard {
|
impl Drop for LinuxRouteGuard {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
if let Some(mut child) = self.child.take() {
|
||||||
|
let _ = child.kill();
|
||||||
|
}
|
||||||
let cleanup_script = format!(
|
let cleanup_script = format!(
|
||||||
"ip route del 0.0.0.0/1 dev ostp_tun || true; \
|
"ip route del 0.0.0.0/1 dev ostp_tun || true; \
|
||||||
ip route del 128.0.0.0/1 dev ostp_tun || true; \
|
ip route del 128.0.0.0/1 dev ostp_tun || true; \
|
||||||
|
|
@ -41,13 +39,39 @@ impl Drop for LinuxRouteGuard {
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub async fn run_linux_tunnel(
|
pub async fn run_linux_tunnel(
|
||||||
config: crate::config::ClientConfig,
|
config: crate::config::ClientConfig,
|
||||||
proxy_events_tx: mpsc::Sender<ProxyEvent>,
|
|
||||||
client_msgs_rx: mpsc::UnboundedReceiver<(u16, ProxyToClientMsg)>,
|
|
||||||
mut shutdown: watch::Receiver<bool>,
|
mut shutdown: watch::Receiver<bool>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
info!("Initializing built-in Linux TUN tunnel...");
|
let debug = config.debug;
|
||||||
|
if debug {
|
||||||
|
println!("[ostp] Initializing TUN tunnel...");
|
||||||
|
}
|
||||||
|
|
||||||
// 1. Pre-flight system checks
|
let exe = std::env::current_exe()?;
|
||||||
|
let dir = exe.parent().ok_or_else(|| anyhow!("failed to get binary directory"))?;
|
||||||
|
|
||||||
|
let mut tun2socks_exe = dir.join("tun2socks");
|
||||||
|
if !tun2socks_exe.exists() {
|
||||||
|
// Try system PATH via standard command check
|
||||||
|
let in_path = Command::new("which")
|
||||||
|
.arg("tun2socks")
|
||||||
|
.output()
|
||||||
|
.map(|o| o.status.success())
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
if in_path {
|
||||||
|
tun2socks_exe = std::path::PathBuf::from("tun2socks");
|
||||||
|
} else {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"CRITICAL: 'tun2socks' binary is missing!\n\
|
||||||
|
OSTP requires tun2socks for TUN mode on Linux. Please download the appropriate binary for your architecture from: \n\
|
||||||
|
https://github.com/xjasonlyu/tun2socks/releases \n\
|
||||||
|
and place it in the same directory as the ostp executable ({}), or install it globally in your PATH.",
|
||||||
|
dir.display()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.5. Pre-flight system checks
|
||||||
let is_root = Command::new("id")
|
let is_root = Command::new("id")
|
||||||
.arg("-u")
|
.arg("-u")
|
||||||
.output()
|
.output()
|
||||||
|
|
@ -76,7 +100,10 @@ pub async fn run_linux_tunnel(
|
||||||
.ok_or_else(|| anyhow!("Could not resolve host IP for routing exclusion"))?;
|
.ok_or_else(|| anyhow!("Could not resolve host IP for routing exclusion"))?;
|
||||||
|
|
||||||
let server_ip_str = server_ip.to_string();
|
let server_ip_str = server_ip.to_string();
|
||||||
info!("Resolved server IP: {}", server_ip_str);
|
|
||||||
|
if debug {
|
||||||
|
println!("[ostp] Resolved server IP: {}", server_ip_str);
|
||||||
|
}
|
||||||
|
|
||||||
// 3. Detect current default gateway and interface
|
// 3. Detect current default gateway and interface
|
||||||
let route_output = Command::new("sh")
|
let route_output = Command::new("sh")
|
||||||
|
|
@ -87,6 +114,7 @@ pub async fn run_linux_tunnel(
|
||||||
let route_str = String::from_utf8_lossy(&route_output.stdout);
|
let route_str = String::from_utf8_lossy(&route_output.stdout);
|
||||||
let parts: Vec<&str> = route_str.split_whitespace().collect();
|
let parts: Vec<&str> = route_str.split_whitespace().collect();
|
||||||
|
|
||||||
|
// Expected: "default via 192.168.1.1 dev eth0 ..."
|
||||||
let mut default_gw = String::new();
|
let mut default_gw = String::new();
|
||||||
let mut default_if = String::new();
|
let mut default_if = String::new();
|
||||||
|
|
||||||
|
|
@ -103,62 +131,95 @@ pub async fn run_linux_tunnel(
|
||||||
return Err(anyhow!("Failed to discover active default gateway or network interface on Linux system."));
|
return Err(anyhow!("Failed to discover active default gateway or network interface on Linux system."));
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Default route: gateway={} interface={}", default_gw, default_if);
|
if debug {
|
||||||
|
println!("[ostp] Default route: gateway={} interface={}", default_gw, default_if);
|
||||||
|
}
|
||||||
|
|
||||||
// Create the TunDevice inside the client process (creates the interface and sets up IP/MTU/Status)
|
// 4. Setup commands (Using standard /1 routing trick for fail-proof overriding)
|
||||||
let tun_dev = create_tun_device("ostp_tun", config.ostp.mtu)?;
|
|
||||||
|
|
||||||
let mut _guard = LinuxRouteGuard {
|
|
||||||
server_ip_str: server_ip_str.clone(),
|
|
||||||
default_gw: default_gw.clone(),
|
|
||||||
default_if: default_if.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// 4. Setup routing rules
|
|
||||||
let setup_script = format!(
|
let setup_script = format!(
|
||||||
"ip route add {} via {} dev {}; \
|
"ip tuntap add name ostp_tun mode tun || true; \
|
||||||
|
ip link set dev ostp_tun mtu {}; \
|
||||||
|
ip addr add 10.1.0.2/24 dev ostp_tun || true; \
|
||||||
|
ip link set dev ostp_tun up; \
|
||||||
|
ip route add {} via {} dev {}; \
|
||||||
ip route add 1.1.1.1 via {} dev {}; \
|
ip route add 1.1.1.1 via {} dev {}; \
|
||||||
ip route add 0.0.0.0/1 dev ostp_tun; \
|
ip route add 0.0.0.0/1 dev ostp_tun; \
|
||||||
ip route add 128.0.0.0/1 dev ostp_tun",
|
ip route add 128.0.0.0/1 dev ostp_tun",
|
||||||
server_ip_str, default_gw, default_if,
|
config.ostp.mtu, server_ip_str, default_gw, default_if,
|
||||||
default_gw, default_if
|
default_gw, default_if
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
println!("[ostp] Executing Linux network config: {}", setup_script);
|
||||||
|
}
|
||||||
|
|
||||||
let out = Command::new("sh")
|
let out = Command::new("sh")
|
||||||
.args(["-c", &setup_script])
|
.args(["-c", &setup_script])
|
||||||
.output()?;
|
.output()?;
|
||||||
|
|
||||||
if !out.status.success() {
|
if !out.status.success() && debug {
|
||||||
tracing::warn!("Warning: Setup routing returned: {}", String::from_utf8_lossy(&out.stderr));
|
println!("[ostp] Warning: Setup routing returned: {}", String::from_utf8_lossy(&out.stderr));
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("TUN tunnel active. Direct in-process packets handling started.");
|
// 5. Prepare and launch tun2socks
|
||||||
|
// Using HTTP Proxy natively avoids any UDP Associate requests,
|
||||||
|
// providing clean TCP proxying with maximum reliability.
|
||||||
|
let proxy_url = format!("http://{}", config.local_proxy.bind_addr);
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
println!("[ostp] Spawning {} -device ostp_tun -proxy {}", tun2socks_exe.display(), proxy_url);
|
||||||
|
}
|
||||||
|
|
||||||
// Run the smoltcp stack loop in the background
|
let mut child = Command::new(&tun2socks_exe)
|
||||||
let stack_shutdown_rx = shutdown.clone();
|
.args([
|
||||||
let stack_handle = tokio::spawn(async move {
|
"-device", "ostp_tun",
|
||||||
if let Err(e) = run_smoltcp_stack(
|
"-proxy", &proxy_url,
|
||||||
tun_dev.packet_rx,
|
])
|
||||||
tun_dev.packet_tx,
|
.stdout(if debug { Stdio::piped() } else { Stdio::null() })
|
||||||
config.ostp.mtu,
|
.stderr(if debug { Stdio::piped() } else { Stdio::null() })
|
||||||
proxy_events_tx,
|
.spawn()
|
||||||
client_msgs_rx,
|
.map_err(|e| anyhow!("Failed to spawn tun2socks process: {}", e))?;
|
||||||
stack_shutdown_rx,
|
|
||||||
).await {
|
|
||||||
tracing::error!("smoltcp stack loop failed: {:?}", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 5. Wait for shutdown signal
|
let mut _guard = LinuxRouteGuard {
|
||||||
|
server_ip_str: server_ip_str.clone(),
|
||||||
|
default_gw: default_gw.clone(),
|
||||||
|
default_if: default_if.clone(),
|
||||||
|
child: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("[ostp] TUN tunnel active. All traffic is routed through OSTP.");
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
let stdout = child.stdout.take().unwrap();
|
||||||
|
let stderr = child.stderr.take().unwrap();
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let reader = BufReader::new(stdout);
|
||||||
|
for line in reader.lines().map_while(Result::ok) {
|
||||||
|
println!("[tun2socks] {}", line);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let reader = BufReader::new(stderr);
|
||||||
|
for line in reader.lines().map_while(Result::ok) {
|
||||||
|
tracing::warn!("tun2socks: {}", line);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_guard.child = Some(child);
|
||||||
|
|
||||||
|
// 6. Wait for shutdown signal
|
||||||
let _ = shutdown.changed().await;
|
let _ = shutdown.changed().await;
|
||||||
|
|
||||||
info!("Deactivating TUN tunnel...");
|
println!("[ostp] Deactivating TUN tunnel...");
|
||||||
|
|
||||||
|
// Drop guard runs cleanup automatically
|
||||||
drop(_guard);
|
drop(_guard);
|
||||||
|
|
||||||
// Terminate smoltcp stack
|
println!("[ostp] TUN tunnel stopped.");
|
||||||
let _ = stack_handle.await;
|
|
||||||
|
|
||||||
info!("TUN tunnel stopped.");
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -166,8 +227,6 @@ pub async fn run_linux_tunnel(
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub async fn run_linux_tunnel(
|
pub async fn run_linux_tunnel(
|
||||||
_config: crate::config::ClientConfig,
|
_config: crate::config::ClientConfig,
|
||||||
_proxy_events_tx: mpsc::Sender<ProxyEvent>,
|
|
||||||
_client_msgs_rx: mpsc::UnboundedReceiver<(u16, ProxyToClientMsg)>,
|
|
||||||
_shutdown: watch::Receiver<bool>,
|
_shutdown: watch::Receiver<bool>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
Err(anyhow!("Linux tunnel driver executed on a non-Linux host!"))
|
Err(anyhow!("Linux tunnel driver executed on a non-Linux host!"))
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,30 @@
|
||||||
mod proxy;
|
mod proxy;
|
||||||
mod wintun_handler;
|
mod wintun_handler;
|
||||||
mod linux_handler;
|
mod linux_handler;
|
||||||
mod tun_device;
|
pub mod native_handler;
|
||||||
mod smoltcp_stack;
|
|
||||||
|
|
||||||
pub async fn run_tun_tunnel(
|
pub async fn run_tun_tunnel(
|
||||||
config: crate::config::ClientConfig,
|
config: crate::config::ClientConfig,
|
||||||
proxy_events_tx: mpsc::Sender<ProxyEvent>,
|
|
||||||
client_msgs_rx: mpsc::UnboundedReceiver<(u16, ProxyToClientMsg)>,
|
|
||||||
shutdown: watch::Receiver<bool>,
|
shutdown: watch::Receiver<bool>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
|
if config.tun_stack == "ostp" {
|
||||||
|
return native_handler::run_native_tunnel(config, shutdown).await;
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
{
|
{
|
||||||
wintun_handler::run_wintun_tunnel(config, proxy_events_tx, client_msgs_rx, shutdown).await
|
wintun_handler::run_wintun_tunnel(config, shutdown).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
linux_handler::run_linux_tunnel(config, proxy_events_tx, client_msgs_rx, shutdown).await
|
linux_handler::run_linux_tunnel(config, shutdown).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "windows", target_os = "linux")))]
|
#[cfg(not(any(target_os = "windows", target_os = "linux")))]
|
||||||
{
|
{
|
||||||
let _ = shutdown;
|
let _ = shutdown;
|
||||||
let _ = config;
|
let _ = config;
|
||||||
let _ = proxy_events_tx;
|
|
||||||
let _ = client_msgs_rx;
|
|
||||||
anyhow::bail!("Operating system unsupported, text an issue at github.");
|
anyhow::bail!("Operating system unsupported, text an issue at github.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -72,7 +71,4 @@ pub async fn run_local_proxy(
|
||||||
run_local_socks5_proxy(cfg, ostp, exclusions, debug, shutdown, proxy_events_tx, client_msgs_rx).await
|
run_local_socks5_proxy(cfg, ostp, exclusions, debug, shutdown, proxy_events_tx, client_msgs_rx).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use tun_device::create_tun_device_from_fd;
|
|
||||||
pub use smoltcp_stack::run_smoltcp_stack;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use crate::tunnel::{ProxyEvent, ProxyToClientMsg};
|
||||||
|
|
||||||
pub async fn run_local_socks5_proxy(
|
pub async fn run_local_socks5_proxy(
|
||||||
cfg: LocalProxyConfig,
|
cfg: LocalProxyConfig,
|
||||||
_ostp: OstpConfig,
|
ostp: OstpConfig,
|
||||||
exclusions: ExclusionConfig,
|
exclusions: ExclusionConfig,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
mut shutdown: watch::Receiver<bool>,
|
mut shutdown: watch::Receiver<bool>,
|
||||||
|
|
@ -29,6 +29,7 @@ pub async fn run_local_socks5_proxy(
|
||||||
|
|
||||||
let matcher = ExclusionMatcher::new(&exclusions);
|
let matcher = ExclusionMatcher::new(&exclusions);
|
||||||
let (connect_tx, mut connect_rx) = mpsc::channel(128);
|
let (connect_tx, mut connect_rx) = mpsc::channel(128);
|
||||||
|
let max_chunk = ostp.mtu.saturating_sub(150).max(512);
|
||||||
|
|
||||||
let mut next_stream_id: u16 = 1;
|
let mut next_stream_id: u16 = 1;
|
||||||
let mut active_streams: HashMap<u16, mpsc::UnboundedSender<ProxyToClientMsg>> = HashMap::new();
|
let mut active_streams: HashMap<u16, mpsc::UnboundedSender<ProxyToClientMsg>> = HashMap::new();
|
||||||
|
|
@ -66,6 +67,7 @@ pub async fn run_local_socks5_proxy(
|
||||||
connect_timeout,
|
connect_timeout,
|
||||||
debug,
|
debug,
|
||||||
matcher_clone,
|
matcher_clone,
|
||||||
|
max_chunk,
|
||||||
).await {
|
).await {
|
||||||
let msg = err.to_string();
|
let msg = err.to_string();
|
||||||
// Suppress routine disconnects and unsupported SOCKS5 command attempts (like UDP) from spam logs
|
// Suppress routine disconnects and unsupported SOCKS5 command attempts (like UDP) from spam logs
|
||||||
|
|
@ -147,6 +149,7 @@ async fn handle_proxy_client(
|
||||||
connect_timeout: Duration,
|
connect_timeout: Duration,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
matcher: ExclusionMatcher,
|
matcher: ExclusionMatcher,
|
||||||
|
max_chunk: usize,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let _guard = StreamGuard { stream_id, close_tx: close_tx.clone() };
|
let _guard = StreamGuard { stream_id, close_tx: close_tx.clone() };
|
||||||
|
|
||||||
|
|
@ -340,7 +343,7 @@ async fn handle_proxy_client(
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Bidirectional raw data forwarding ─────────────────────────────
|
// ── Bidirectional raw data forwarding ─────────────────────────────
|
||||||
let mut tcp_buf = vec![0_u8; 1024];
|
let mut tcp_buf = vec![0_u8; 65536];
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
read_res = client.read(&mut tcp_buf) => {
|
read_res = client.read(&mut tcp_buf) => {
|
||||||
|
|
@ -353,10 +356,15 @@ async fn handle_proxy_client(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Ok(n) => {
|
Ok(n) => {
|
||||||
let _ = event_tx.send(ProxyEvent::Data {
|
let mut offset = 0;
|
||||||
stream_id,
|
while offset < n {
|
||||||
payload: bytes::Bytes::copy_from_slice(&tcp_buf[..n]),
|
let end = (offset + max_chunk).min(n);
|
||||||
}).await;
|
let _ = event_tx.send(ProxyEvent::Data {
|
||||||
|
stream_id,
|
||||||
|
payload: bytes::Bytes::copy_from_slice(&tcp_buf[offset..end]),
|
||||||
|
}).await;
|
||||||
|
offset = end;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let _ = event_tx.send(ProxyEvent::Close { stream_id }).await;
|
let _ = event_tx.send(ProxyEvent::Close { stream_id }).await;
|
||||||
|
|
|
||||||
|
|
@ -1,272 +0,0 @@
|
||||||
use std::collections::{HashMap, VecDeque};
|
|
||||||
use std::time::Instant;
|
|
||||||
use anyhow::Result;
|
|
||||||
use tokio::sync::mpsc;
|
|
||||||
use tokio::sync::watch;
|
|
||||||
use bytes::Bytes;
|
|
||||||
use tracing::{info, error, debug};
|
|
||||||
|
|
||||||
use smoltcp::iface::{Config, Interface, SocketSet, SocketHandle};
|
|
||||||
use smoltcp::phy::{Device, DeviceCapabilities, Medium, RxToken, TxToken};
|
|
||||||
use smoltcp::socket::tcp::{Socket as TcpSocket, State as TcpState};
|
|
||||||
use smoltcp::wire::{IpAddress, IpCidr, Ipv4Packet, IpProtocol, TcpPacket};
|
|
||||||
|
|
||||||
use crate::tunnel::{ProxyEvent, ProxyToClientMsg};
|
|
||||||
|
|
||||||
// Custom smoltcp device that bridges to tokio channels
|
|
||||||
struct ChannelDevice {
|
|
||||||
rx_queue: VecDeque<Vec<u8>>,
|
|
||||||
tx_sender: mpsc::Sender<Vec<u8>>,
|
|
||||||
capabilities: DeviceCapabilities,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ChannelDevice {
|
|
||||||
fn new(tx_sender: mpsc::Sender<Vec<u8>>, mtu: usize) -> Self {
|
|
||||||
let mut caps = DeviceCapabilities::default();
|
|
||||||
caps.medium = Medium::Ip;
|
|
||||||
caps.max_transmission_unit = mtu;
|
|
||||||
Self {
|
|
||||||
rx_queue: VecDeque::new(),
|
|
||||||
tx_sender,
|
|
||||||
capabilities: caps,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ChannelRxToken(Vec<u8>);
|
|
||||||
|
|
||||||
impl RxToken for ChannelRxToken {
|
|
||||||
fn consume<R, F>(mut self, f: F) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut [u8]) -> R,
|
|
||||||
{
|
|
||||||
f(&mut self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ChannelTxToken(mpsc::Sender<Vec<u8>>);
|
|
||||||
|
|
||||||
impl TxToken for ChannelTxToken {
|
|
||||||
fn consume<R, F>(self, len: usize, f: F) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut [u8]) -> R,
|
|
||||||
{
|
|
||||||
let mut buffer = vec![0; len];
|
|
||||||
let result = f(&mut buffer);
|
|
||||||
let _ = self.0.try_send(buffer);
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Device for ChannelDevice {
|
|
||||||
type RxToken<'a> = ChannelRxToken where Self: 'a;
|
|
||||||
type TxToken<'a> = ChannelTxToken where Self: 'a;
|
|
||||||
|
|
||||||
fn receive(&mut self, _timestamp: smoltcp::time::Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
|
|
||||||
self.rx_queue.pop_front().map(|packet| {
|
|
||||||
(
|
|
||||||
ChannelRxToken(packet),
|
|
||||||
ChannelTxToken(self.tx_sender.clone()),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option<Self::TxToken<'_>> {
|
|
||||||
Some(ChannelTxToken(self.tx_sender.clone()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn capabilities(&self) -> DeviceCapabilities {
|
|
||||||
self.capabilities.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct VirtualStream {
|
|
||||||
_stream_id: u16,
|
|
||||||
socket_handle: SocketHandle,
|
|
||||||
last_activity: Instant,
|
|
||||||
target: String,
|
|
||||||
established: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn run_smoltcp_stack(
|
|
||||||
mut tun_rx: mpsc::Receiver<Vec<u8>>,
|
|
||||||
tun_tx: mpsc::Sender<Vec<u8>>,
|
|
||||||
mtu: usize,
|
|
||||||
proxy_events_tx: mpsc::Sender<ProxyEvent>,
|
|
||||||
mut client_msgs_rx: mpsc::UnboundedReceiver<(u16, ProxyToClientMsg)>,
|
|
||||||
mut shutdown: watch::Receiver<bool>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let mut device = ChannelDevice::new(tun_tx, mtu);
|
|
||||||
|
|
||||||
let config = Config::new(smoltcp::wire::HardwareAddress::Ip);
|
|
||||||
|
|
||||||
let mut interface = Interface::new(config, &mut device, smoltcp::time::Instant::now());
|
|
||||||
interface.set_any_ip(true); // Required to intercept all packets
|
|
||||||
interface.update_ip_addrs(|addrs| {
|
|
||||||
let _ = addrs.push(IpCidr::new(IpAddress::v4(10, 1, 0, 2), 24));
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut sockets = SocketSet::new(vec![]);
|
|
||||||
let mut stream_id_counter: u16 = 1;
|
|
||||||
let mut streams: HashMap<u16, VirtualStream> = HashMap::new();
|
|
||||||
let mut handle_to_stream_id: HashMap<SocketHandle, u16> = HashMap::new();
|
|
||||||
|
|
||||||
// Map to route incoming data from client_msgs_rx to target sockets
|
|
||||||
let mut pending_client_msgs: VecDeque<(u16, ProxyToClientMsg)> = VecDeque::new();
|
|
||||||
|
|
||||||
let mut ticker = tokio::time::interval(std::time::Duration::from_millis(5));
|
|
||||||
|
|
||||||
info!("smoltcp virtual TCP/IP stack runner active.");
|
|
||||||
|
|
||||||
loop {
|
|
||||||
tokio::select! {
|
|
||||||
_ = shutdown.changed() => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
_ = ticker.tick() => {
|
|
||||||
// Periodical stack poll
|
|
||||||
}
|
|
||||||
pkt_opt = tun_rx.recv() => {
|
|
||||||
if let Some(pkt) = pkt_opt {
|
|
||||||
// Check if it's a new TCP connection (SYN) and dynamically spawn a listener
|
|
||||||
if let Ok(ipv4_packet) = Ipv4Packet::new_checked(&pkt) {
|
|
||||||
if ipv4_packet.next_header() == IpProtocol::Tcp {
|
|
||||||
if let Ok(tcp_packet) = TcpPacket::new_checked(ipv4_packet.payload()) {
|
|
||||||
if tcp_packet.syn() && !tcp_packet.ack() {
|
|
||||||
let dst_ip = ipv4_packet.dst_addr();
|
|
||||||
let dst_port = tcp_packet.dst_port();
|
|
||||||
|
|
||||||
// Allocate a stream_id
|
|
||||||
let stream_id = stream_id_counter;
|
|
||||||
stream_id_counter = stream_id_counter.wrapping_add(1);
|
|
||||||
if stream_id_counter == 0 {
|
|
||||||
stream_id_counter = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let target = format!("{}:{}", dst_ip, dst_port);
|
|
||||||
debug!("Intercepted TCP SYN to {}, creating virtual socket. Stream ID: {}", target, stream_id);
|
|
||||||
|
|
||||||
// Create socket inside smoltcp
|
|
||||||
let mut tcp_socket = TcpSocket::new(
|
|
||||||
smoltcp::socket::tcp::SocketBuffer::new(vec![0; 65535]),
|
|
||||||
smoltcp::socket::tcp::SocketBuffer::new(vec![0; 65535]),
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Err(e) = tcp_socket.listen((dst_ip, dst_port)) {
|
|
||||||
error!("Failed to set TCP socket to listen: {:?}", e);
|
|
||||||
} else {
|
|
||||||
let handle = sockets.add(tcp_socket);
|
|
||||||
streams.insert(stream_id, VirtualStream {
|
|
||||||
_stream_id: stream_id,
|
|
||||||
socket_handle: handle,
|
|
||||||
last_activity: Instant::now(),
|
|
||||||
target: target.clone(),
|
|
||||||
established: false,
|
|
||||||
});
|
|
||||||
handle_to_stream_id.insert(handle, stream_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
device.rx_queue.push_back(pkt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
msg_opt = client_msgs_rx.recv() => {
|
|
||||||
if let Some((stream_id, msg)) = msg_opt {
|
|
||||||
pending_client_msgs.push_back((stream_id, msg));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process pending client messages (responses from OSTP bridge)
|
|
||||||
let mut unhandled = VecDeque::new();
|
|
||||||
while let Some((stream_id, msg)) = pending_client_msgs.pop_front() {
|
|
||||||
if let Some(stream) = streams.get_mut(&stream_id) {
|
|
||||||
let socket = sockets.get_mut::<TcpSocket>(stream.socket_handle);
|
|
||||||
match msg {
|
|
||||||
ProxyToClientMsg::ConnectOk => {
|
|
||||||
stream.established = true;
|
|
||||||
debug!("Stream ID {} connected successfully via OSTP.", stream_id);
|
|
||||||
}
|
|
||||||
ProxyToClientMsg::Data(data) => {
|
|
||||||
if socket.can_send() {
|
|
||||||
let _ = socket.send_slice(&data);
|
|
||||||
stream.last_activity = Instant::now();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ProxyToClientMsg::Close | ProxyToClientMsg::Error(_) => {
|
|
||||||
socket.close();
|
|
||||||
// Socket clean-up will occur below
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unhandled.push_back((stream_id, msg));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pending_client_msgs = unhandled;
|
|
||||||
|
|
||||||
// Poll the virtual interface
|
|
||||||
let timestamp = smoltcp::time::Instant::now();
|
|
||||||
let _ = interface.poll(timestamp, &mut device, &mut sockets);
|
|
||||||
|
|
||||||
// Process data transfer from virtual sockets -> OSTP client bridge
|
|
||||||
let mut closed_streams = Vec::new();
|
|
||||||
for (&stream_id, stream) in streams.iter_mut() {
|
|
||||||
let socket = sockets.get_mut::<TcpSocket>(stream.socket_handle);
|
|
||||||
|
|
||||||
// 1. Handshake detection & initiation
|
|
||||||
if socket.is_active() && !stream.established {
|
|
||||||
if socket.state() == TcpState::Established {
|
|
||||||
// Send Connect request to the bridge
|
|
||||||
if proxy_events_tx.try_send(ProxyEvent::NewStream {
|
|
||||||
stream_id,
|
|
||||||
target: stream.target.clone(),
|
|
||||||
}).is_ok() {
|
|
||||||
stream.established = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Read inbound data from client OS applications
|
|
||||||
if socket.may_recv() {
|
|
||||||
let mut buf = vec![0; 4096];
|
|
||||||
if let Ok(n) = socket.recv_slice(&mut buf) {
|
|
||||||
if n > 0 {
|
|
||||||
stream.last_activity = Instant::now();
|
|
||||||
let _ = proxy_events_tx.try_send(ProxyEvent::Data {
|
|
||||||
stream_id,
|
|
||||||
payload: Bytes::from(buf[..n].to_vec()),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Connection termination detection
|
|
||||||
let mut should_close = false;
|
|
||||||
if !socket.is_active() || socket.state() == TcpState::Closed || socket.state() == TcpState::TimeWait {
|
|
||||||
should_close = true;
|
|
||||||
} else if stream.last_activity.elapsed() > std::time::Duration::from_secs(120) {
|
|
||||||
// Timeout inactive streams
|
|
||||||
should_close = true;
|
|
||||||
socket.abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
if should_close {
|
|
||||||
closed_streams.push(stream_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up closed streams
|
|
||||||
for stream_id in closed_streams {
|
|
||||||
if let Some(stream) = streams.remove(&stream_id) {
|
|
||||||
debug!("Cleaning up virtual socket for stream ID: {}", stream_id);
|
|
||||||
handle_to_stream_id.remove(&stream.socket_handle);
|
|
||||||
sockets.remove(stream.socket_handle);
|
|
||||||
let _ = proxy_events_tx.try_send(ProxyEvent::Close { stream_id });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
@ -1,216 +0,0 @@
|
||||||
use std::sync::Arc;
|
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
use tokio::sync::mpsc;
|
|
||||||
use tracing::{info, error, debug};
|
|
||||||
|
|
||||||
pub struct TunDevice {
|
|
||||||
pub packet_rx: mpsc::Receiver<Vec<u8>>,
|
|
||||||
pub packet_tx: mpsc::Sender<Vec<u8>>,
|
|
||||||
_shutdown_tx: mpsc::Sender<()>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
pub fn create_tun_device(tun_name: &str, mtu: usize) -> Result<TunDevice> {
|
|
||||||
let exe = std::env::current_exe()?;
|
|
||||||
let dir = exe.parent().ok_or_else(|| anyhow!("failed to get binary directory"))?;
|
|
||||||
let wintun_dll = dir.join("wintun.dll");
|
|
||||||
|
|
||||||
if !wintun_dll.exists() {
|
|
||||||
return Err(anyhow!(
|
|
||||||
"CRITICAL: 'wintun.dll' is missing at {}!\n\
|
|
||||||
Please make sure wintun.dll is present in the binary directory.",
|
|
||||||
dir.display()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
info!("Loading wintun.dll from: {:?}", wintun_dll);
|
|
||||||
let wintun = unsafe { wintun::load_from_path(wintun_dll)? };
|
|
||||||
|
|
||||||
// Open or create adapter
|
|
||||||
let adapter = match wintun::Adapter::open(&wintun, tun_name) {
|
|
||||||
Ok(a) => a,
|
|
||||||
Err(_) => {
|
|
||||||
info!("TUN adapter '{}' not found, creating a new one...", tun_name);
|
|
||||||
wintun::Adapter::create(&wintun, "Wintun", tun_name, None)?
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let session = Arc::new(adapter.start_session(wintun::MAX_RING_CAPACITY)?);
|
|
||||||
|
|
||||||
let (packet_tx_in, packet_rx) = mpsc::channel::<Vec<u8>>(100000);
|
|
||||||
let (packet_tx, mut packet_rx_out) = mpsc::channel::<Vec<u8>>(100000);
|
|
||||||
let (shutdown_tx, _shutdown_rx) = mpsc::channel::<()>(1);
|
|
||||||
|
|
||||||
// Spawning blocking read loop in a dedicated thread
|
|
||||||
let session_read = session.clone();
|
|
||||||
std::thread::spawn(move || {
|
|
||||||
loop {
|
|
||||||
match session_read.receive_blocking() {
|
|
||||||
Ok(packet) => {
|
|
||||||
let bytes: &[u8] = packet.bytes();
|
|
||||||
if packet_tx_in.blocking_send(bytes.to_vec()).is_err() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
error!("Wintun receive packet error: {:?}", e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Spawning blocking write loop in a dedicated thread
|
|
||||||
let session_write = session.clone();
|
|
||||||
std::thread::spawn(move || {
|
|
||||||
while let Some(pkt) = packet_rx_out.blocking_recv() {
|
|
||||||
if pkt.len() > mtu {
|
|
||||||
debug!("Dropped packet exceeding MTU: {} > {}", pkt.len(), mtu);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
match session_write.allocate_send_packet(pkt.len() as u16) {
|
|
||||||
Ok(mut send_packet) => {
|
|
||||||
send_packet.bytes_mut().copy_from_slice(&pkt);
|
|
||||||
session_write.send_packet(send_packet);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
error!("Wintun allocate send packet error: {:?}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(TunDevice {
|
|
||||||
packet_rx,
|
|
||||||
packet_tx,
|
|
||||||
_shutdown_tx: shutdown_tx,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
pub fn create_tun_device(tun_name: &str, mtu: usize) -> Result<TunDevice> {
|
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
|
||||||
|
|
||||||
let mut config = tun::Configuration::default();
|
|
||||||
config
|
|
||||||
.name(tun_name)
|
|
||||||
.address("10.1.0.2")
|
|
||||||
.netmask("255.255.255.0")
|
|
||||||
.mtu(mtu as i32)
|
|
||||||
.up();
|
|
||||||
|
|
||||||
let device = tun::create_as_async(&config)?;
|
|
||||||
let (mut reader, mut writer) = tokio::io::split(device);
|
|
||||||
|
|
||||||
let (packet_tx_in, packet_rx) = mpsc::channel::<Vec<u8>>(100000);
|
|
||||||
let (packet_tx, mut packet_rx_out) = mpsc::channel::<Vec<u8>>(100000);
|
|
||||||
let (shutdown_tx, mut shutdown_rx) = mpsc::channel::<()>(1);
|
|
||||||
|
|
||||||
// Read loop
|
|
||||||
tokio::spawn(async move {
|
|
||||||
let mut buf = vec![0_u8; 65535];
|
|
||||||
loop {
|
|
||||||
match reader.read(&mut buf).await {
|
|
||||||
Ok(0) => break,
|
|
||||||
Ok(n) => {
|
|
||||||
if packet_tx_in.send(buf[..n].to_vec()).await.is_err() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
error!("TUN read error: {:?}", e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Write loop
|
|
||||||
tokio::spawn(async move {
|
|
||||||
loop {
|
|
||||||
tokio::select! {
|
|
||||||
_ = shutdown_rx.recv() => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pkt_opt = packet_rx_out.recv() => {
|
|
||||||
if let Some(pkt) = pkt_opt {
|
|
||||||
if let Err(e) = writer.write_all(&pkt).await {
|
|
||||||
error!("TUN write error: {:?}", e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(TunDevice {
|
|
||||||
packet_rx,
|
|
||||||
packet_tx,
|
|
||||||
_shutdown_tx: shutdown_tx,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "windows", target_os = "linux")))]
|
|
||||||
pub fn create_tun_device(_tun_name: &str, _mtu: usize) -> Result<TunDevice> {
|
|
||||||
Err(anyhow!("Unsupported operating system for TUN device"))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
pub fn create_tun_device_from_fd(fd: i32, mtu: usize) -> Result<TunDevice> {
|
|
||||||
use std::os::unix::io::FromRawFd;
|
|
||||||
use std::io::{Read, Write};
|
|
||||||
|
|
||||||
let file = unsafe { std::fs::File::from_raw_fd(fd) };
|
|
||||||
let mut file_read = file.try_clone()?;
|
|
||||||
let mut file_write = file;
|
|
||||||
|
|
||||||
let (packet_tx_in, packet_rx) = mpsc::channel::<Vec<u8>>(100000);
|
|
||||||
let (packet_tx, mut packet_rx_out) = mpsc::channel::<Vec<u8>>(100000);
|
|
||||||
let (shutdown_tx, _shutdown_rx) = mpsc::channel::<()>(1);
|
|
||||||
|
|
||||||
// Read loop thread
|
|
||||||
std::thread::spawn(move || {
|
|
||||||
let mut buf = vec![0_u8; 65535];
|
|
||||||
loop {
|
|
||||||
match file_read.read(&mut buf) {
|
|
||||||
Ok(0) => break,
|
|
||||||
Ok(n) => {
|
|
||||||
if packet_tx_in.blocking_send(buf[..n].to_vec()).is_err() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
error!("TUN fd read error: {:?}", e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Write loop thread
|
|
||||||
std::thread::spawn(move || {
|
|
||||||
while let Some(pkt) = packet_rx_out.blocking_recv() {
|
|
||||||
if pkt.len() > mtu {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if let Err(e) = file_write.write_all(&pkt) {
|
|
||||||
error!("TUN fd write error: {:?}", e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(TunDevice {
|
|
||||||
packet_rx,
|
|
||||||
packet_tx,
|
|
||||||
_shutdown_tx: shutdown_tx,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(unix))]
|
|
||||||
pub fn create_tun_device_from_fd(_fd: i32, _mtu: usize) -> Result<TunDevice> {
|
|
||||||
Err(anyhow!("Raw fd TUN device is not supported on this operating system"))
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +1,29 @@
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use tokio::sync::{mpsc, watch};
|
use tokio::sync::watch;
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
use tracing::info;
|
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
use std::os::windows::process::CommandExt;
|
|
||||||
|
|
||||||
use crate::tunnel::{ProxyEvent, ProxyToClientMsg};
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
use crate::tunnel::tun_device::create_tun_device;
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
use crate::tunnel::smoltcp_stack::run_smoltcp_stack;
|
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub async fn run_wintun_tunnel(
|
pub async fn run_wintun_tunnel(
|
||||||
config: crate::config::ClientConfig,
|
config: crate::config::ClientConfig,
|
||||||
proxy_events_tx: mpsc::Sender<ProxyEvent>,
|
|
||||||
client_msgs_rx: mpsc::UnboundedReceiver<(u16, ProxyToClientMsg)>,
|
|
||||||
mut shutdown: watch::Receiver<bool>,
|
mut shutdown: watch::Receiver<bool>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
use std::net::ToSocketAddrs;
|
use std::net::ToSocketAddrs;
|
||||||
use std::process::Command;
|
use std::process::{Command, Stdio, Child};
|
||||||
|
use std::os::windows::process::CommandExt;
|
||||||
|
|
||||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||||
const TUN_NAME: &str = "ostp_tun";
|
const TUN_NAME: &str = "ostp_tun";
|
||||||
|
|
||||||
struct WintunGuard {
|
struct WintunGuard {
|
||||||
server_ip_str: String,
|
server_ip_str: String,
|
||||||
|
child: Option<Child>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for WintunGuard {
|
impl Drop for WintunGuard {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
if let Some(mut child) = self.child.take() {
|
||||||
|
let _ = child.kill();
|
||||||
|
let _ = child.wait();
|
||||||
|
}
|
||||||
let cleanup_script = format!(
|
let cleanup_script = format!(
|
||||||
"$remote_ip = '{}'\n\
|
"$remote_ip = '{}'\n\
|
||||||
Remove-NetRoute -DestinationPrefix \"$remote_ip/32\" -Confirm:$false -ErrorAction SilentlyContinue\n\
|
Remove-NetRoute -DestinationPrefix \"$remote_ip/32\" -Confirm:$false -ErrorAction SilentlyContinue\n\
|
||||||
|
|
@ -46,9 +39,27 @@ pub async fn run_wintun_tunnel(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Initializing built-in Wintun TUN tunnel...");
|
let debug = config.debug;
|
||||||
|
|
||||||
|
tracing::info!("Initializing TUN tunnel...");
|
||||||
|
|
||||||
|
let exe = std::env::current_exe()?;
|
||||||
|
let dir = exe.parent().ok_or_else(|| anyhow!("failed to get binary directory"))?;
|
||||||
|
let tun2socks_exe = dir.join("tun2socks.exe");
|
||||||
|
|
||||||
|
if !tun2socks_exe.exists() {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"CRITICAL: 'tun2socks.exe' binary is missing!\n\
|
||||||
|
OSTP requires tun2socks for TUN mode on Windows. Please download the appropriate binary from: \n\
|
||||||
|
https://github.com/xjasonlyu/tun2socks/releases \n\
|
||||||
|
and place it in the same directory as the ostp executable ({}).",
|
||||||
|
dir.display()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
// 1. Delete stale TUN adapter if it exists from a previous run.
|
// 1. Delete stale TUN adapter if it exists from a previous run.
|
||||||
|
// This prevents wintun from creating "ostp_tun 2", "ostp_tun 3", etc.
|
||||||
|
tracing::info!("Cleaning up stale TUN adapter...");
|
||||||
let _ = Command::new("powershell")
|
let _ = Command::new("powershell")
|
||||||
.creation_flags(CREATE_NO_WINDOW)
|
.creation_flags(CREATE_NO_WINDOW)
|
||||||
.args(["-NoProfile", "-Command", &format!(
|
.args(["-NoProfile", "-Command", &format!(
|
||||||
|
|
@ -57,6 +68,7 @@ pub async fn run_wintun_tunnel(
|
||||||
netsh interface set interface \"{TUN_NAME}\" admin=disable 2>$null"
|
netsh interface set interface \"{TUN_NAME}\" admin=disable 2>$null"
|
||||||
)])
|
)])
|
||||||
.output();
|
.output();
|
||||||
|
// Brief pause to let the driver release the adapter
|
||||||
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
|
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
|
||||||
|
|
||||||
// 2. Resolve Server IP for routing table exclusion
|
// 2. Resolve Server IP for routing table exclusion
|
||||||
|
|
@ -67,7 +79,7 @@ pub async fn run_wintun_tunnel(
|
||||||
.ok_or_else(|| anyhow!("Could not resolve host IP for routing exclusion"))?;
|
.ok_or_else(|| anyhow!("Could not resolve host IP for routing exclusion"))?;
|
||||||
|
|
||||||
let server_ip_str = server_ip.to_string();
|
let server_ip_str = server_ip.to_string();
|
||||||
info!("Resolved server IP: {}", server_ip_str);
|
tracing::info!("Resolved server IP: {}", server_ip_str);
|
||||||
|
|
||||||
// 3. Prepare routing and firewall setup script
|
// 3. Prepare routing and firewall setup script
|
||||||
let current_exe = std::env::current_exe()?.to_string_lossy().into_owned();
|
let current_exe = std::env::current_exe()?.to_string_lossy().into_owned();
|
||||||
|
|
@ -75,33 +87,42 @@ pub async fn run_wintun_tunnel(
|
||||||
let setup_script = format!(
|
let setup_script = format!(
|
||||||
"$remote_ip = '{}'\n\
|
"$remote_ip = '{}'\n\
|
||||||
$exe_path = '{}'\n\
|
$exe_path = '{}'\n\
|
||||||
$route = Find-NetRoute -RemoteIPAddress $remote_ip -ErrorAction SilentlyContinue | Select-Object -First 1\n\
|
$route = Get-NetRoute -DestinationPrefix '0.0.0.0/0' | Where-Object {{ $_.InterfaceAlias -notmatch 'tun' -and $_.InterfaceAlias -notmatch 'wintun' }} | Sort-Object RouteMetric | Select-Object -First 1\n\
|
||||||
if (-not $route) {{\n\
|
if ($route) {{\n\
|
||||||
$route = Get-NetRoute -DestinationPrefix '0.0.0.0/0' | Where-Object {{ $_.InterfaceAlias -notmatch 'tun' -and $_.InterfaceAlias -notmatch 'wintun' }} | Sort-Object RouteMetric | Select-Object -First 1\n\
|
$gw = $route.NextHop\n\
|
||||||
|
$ifIndex = $route.InterfaceIndex\n\
|
||||||
|
New-NetRoute -DestinationPrefix \"$remote_ip/32\" -NextHop $gw -InterfaceIndex $ifIndex -RouteMetric 1 -ErrorAction SilentlyContinue\n\
|
||||||
}}\n\
|
}}\n\
|
||||||
$gw = $route.NextHop\n\
|
|
||||||
$ifIndex = $route.InterfaceIndex\n\
|
|
||||||
New-NetRoute -DestinationPrefix \"$remote_ip/32\" -NextHop $gw -InterfaceIndex $ifIndex -RouteMetric 1 -ErrorAction SilentlyContinue\n\
|
|
||||||
$dns_ips = Get-DnsClientServerAddress -InterfaceIndex $ifIndex | Select-Object -ExpandProperty ServerAddresses\n\
|
|
||||||
foreach ($dns in $dns_ips) {{\n\
|
|
||||||
if ($dns -match '^\\d+\\.\\d+\\.\\d+\\.\\d+$') {{\n\
|
|
||||||
New-NetRoute -DestinationPrefix \"$dns/32\" -NextHop $gw -InterfaceIndex $ifIndex -RouteMetric 1 -ErrorAction SilentlyContinue\n\
|
|
||||||
}}\n\
|
|
||||||
}}\n\
|
|
||||||
New-NetRoute -DestinationPrefix \"1.1.1.1/32\" -NextHop $gw -InterfaceIndex $ifIndex -RouteMetric 1 -ErrorAction SilentlyContinue\n\
|
|
||||||
New-NetFirewallRule -DisplayName 'OSTP Tunnel In' -Direction Inbound -Program $exe_path -Action Allow -Enabled True -ErrorAction SilentlyContinue\n\
|
New-NetFirewallRule -DisplayName 'OSTP Tunnel In' -Direction Inbound -Program $exe_path -Action Allow -Enabled True -ErrorAction SilentlyContinue\n\
|
||||||
New-NetFirewallRule -DisplayName 'OSTP Tunnel Out' -Direction Outbound -Program $exe_path -Action Allow -Enabled True -ErrorAction SilentlyContinue\n",
|
New-NetFirewallRule -DisplayName 'OSTP Tunnel Out' -Direction Outbound -Program $exe_path -Action Allow -Enabled True -ErrorAction SilentlyContinue\n",
|
||||||
server_ip_str, current_exe
|
server_ip_str, current_exe
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create the TunDevice inside the client process
|
// 4. Launch tun2socks + route setup IN PARALLEL to save ~3 seconds
|
||||||
let tun_dev = create_tun_device(TUN_NAME, config.ostp.mtu)?;
|
let proxy_url = format!("http://{}", config.local_proxy.bind_addr);
|
||||||
|
tracing::info!("Starting tun2socks (proxy={})", proxy_url);
|
||||||
|
|
||||||
|
// Spawn tun2socks immediately — it creates the adapter on its own
|
||||||
|
let mut child = Command::new(&tun2socks_exe)
|
||||||
|
.creation_flags(CREATE_NO_WINDOW)
|
||||||
|
.args([
|
||||||
|
"-device", TUN_NAME,
|
||||||
|
"-proxy", &proxy_url,
|
||||||
|
"-loglevel", if debug { "debug" } else { "error" }
|
||||||
|
])
|
||||||
|
.current_dir(dir)
|
||||||
|
.stdout(if debug { Stdio::piped() } else { Stdio::null() })
|
||||||
|
.stderr(if debug { Stdio::piped() } else { Stdio::null() })
|
||||||
|
.spawn()
|
||||||
|
.map_err(|e| anyhow!("Failed to launch tun2socks.exe: {}", e))?;
|
||||||
|
|
||||||
let mut _guard = WintunGuard {
|
let mut _guard = WintunGuard {
|
||||||
server_ip_str: server_ip_str.clone(),
|
server_ip_str: server_ip_str.clone(),
|
||||||
|
child: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Run route setup in parallel
|
// Run route setup in parallel while tun2socks creates the adapter.
|
||||||
|
// Also poll for the adapter to appear (typically <1s).
|
||||||
let route_handle = {
|
let route_handle = {
|
||||||
let script = setup_script.clone();
|
let script = setup_script.clone();
|
||||||
tokio::task::spawn_blocking(move || {
|
tokio::task::spawn_blocking(move || {
|
||||||
|
|
@ -112,7 +133,7 @@ pub async fn run_wintun_tunnel(
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
// 5. Wait for TUN adapter to appear
|
// 5. Wait for TUN adapter to appear (poll with timeout instead of fixed 2s sleep)
|
||||||
let adapter_deadline = tokio::time::Instant::now() + tokio::time::Duration::from_secs(8);
|
let adapter_deadline = tokio::time::Instant::now() + tokio::time::Duration::from_secs(8);
|
||||||
let mut adapter_ready = false;
|
let mut adapter_ready = false;
|
||||||
while tokio::time::Instant::now() < adapter_deadline {
|
while tokio::time::Instant::now() < adapter_deadline {
|
||||||
|
|
@ -124,6 +145,9 @@ pub async fn run_wintun_tunnel(
|
||||||
.output();
|
.output();
|
||||||
if let Ok(out) = check {
|
if let Ok(out) = check {
|
||||||
let status = String::from_utf8_lossy(&out.stdout).trim().to_string();
|
let status = String::from_utf8_lossy(&out.stdout).trim().to_string();
|
||||||
|
if debug {
|
||||||
|
tracing::info!("Adapter status: '{}'", status);
|
||||||
|
}
|
||||||
if status == "Up" || status == "Disconnected" || !status.is_empty() {
|
if status == "Up" || status == "Disconnected" || !status.is_empty() {
|
||||||
adapter_ready = true;
|
adapter_ready = true;
|
||||||
break;
|
break;
|
||||||
|
|
@ -135,21 +159,22 @@ pub async fn run_wintun_tunnel(
|
||||||
tracing::warn!("WARNING: TUN adapter did not appear within timeout. Proceeding anyway.");
|
tracing::warn!("WARNING: TUN adapter did not appear within timeout. Proceeding anyway.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for route setup to finish
|
// Wait for route setup to finish (should already be done by now)
|
||||||
let _ = route_handle.await;
|
let _ = route_handle.await;
|
||||||
|
|
||||||
// 6. Configure the adapter
|
// 6. Configure the adapter (IP, metric, MTU, DNS)
|
||||||
info!("Applying network configuration...");
|
tracing::info!("Applying network configuration...");
|
||||||
let mut net_setup = format!(
|
let mut net_setup = format!(
|
||||||
"netsh interface ipv4 set address name=\"{TUN_NAME}\" static 10.1.0.2 255.255.255.0 10.1.0.1\n\
|
"netsh interface ipv4 set address name=\"{TUN_NAME}\" static 10.1.0.2 255.255.255.0 10.1.0.1\n\
|
||||||
netsh interface ipv4 set subinterface \"{TUN_NAME}\" mtu={} store=persistent\n\
|
netsh interface ipv4 set subinterface \"{TUN_NAME}\" mtu={} store=persistent\n\
|
||||||
netsh interface ipv4 set interface name=\"{TUN_NAME}\" metric=5\n",
|
netsh interface ipv4 set interface name=\"{TUN_NAME}\" metric=1\n\
|
||||||
|
New-NetRoute -DestinationPrefix '0.0.0.0/0' -InterfaceAlias '{TUN_NAME}' -NextHop '10.1.0.1' -RouteMetric 1 -ErrorAction SilentlyContinue\n",
|
||||||
config.ostp.mtu
|
config.ostp.mtu
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(ref dns) = config.dns_server {
|
if let Some(ref dns) = config.dns_server {
|
||||||
if !dns.is_empty() {
|
if !dns.is_empty() {
|
||||||
info!("DNS server: {}", dns);
|
tracing::info!("DNS server: {}", dns);
|
||||||
net_setup.push_str(&format!(
|
net_setup.push_str(&format!(
|
||||||
"netsh interface ipv4 set dnsservers name=\"{TUN_NAME}\" static {} primary\n", dns
|
"netsh interface ipv4 set dnsservers name=\"{TUN_NAME}\" static {} primary\n", dns
|
||||||
));
|
));
|
||||||
|
|
@ -161,33 +186,41 @@ pub async fn run_wintun_tunnel(
|
||||||
.args(["-NoProfile", "-Command", &net_setup])
|
.args(["-NoProfile", "-Command", &net_setup])
|
||||||
.output()?;
|
.output()?;
|
||||||
|
|
||||||
info!("TUN tunnel active. Direct in-process packets handling started.");
|
tracing::info!("TUN tunnel active. All traffic is routed through OSTP.");
|
||||||
|
|
||||||
// Run the smoltcp stack loop in the background
|
// 7. Spawn debug log readers for tun2socks output
|
||||||
let stack_shutdown_rx = shutdown.clone();
|
let mut stdout = child.stdout.take();
|
||||||
let stack_handle = tokio::spawn(async move {
|
let mut stderr = child.stderr.take();
|
||||||
if let Err(e) = run_smoltcp_stack(
|
_guard.child = Some(child);
|
||||||
tun_dev.packet_rx,
|
|
||||||
tun_dev.packet_tx,
|
if debug {
|
||||||
config.ostp.mtu,
|
std::thread::spawn(move || {
|
||||||
proxy_events_tx,
|
use std::io::{BufRead, BufReader};
|
||||||
client_msgs_rx,
|
if let Some(out) = stdout.take() {
|
||||||
stack_shutdown_rx,
|
let reader = BufReader::new(out);
|
||||||
).await {
|
for line in reader.lines().map_while(Result::ok) {
|
||||||
tracing::error!("smoltcp stack loop failed: {:?}", e);
|
tracing::debug!("tun2socks: {}", line);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
use std::io::{BufRead, BufReader};
|
||||||
|
if let Some(err) = stderr.take() {
|
||||||
|
let reader = BufReader::new(err);
|
||||||
|
for line in reader.lines().map_while(Result::ok) {
|
||||||
|
tracing::warn!("tun2socks: {}", line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 8. Wait for shutdown signal
|
// 8. Wait for shutdown signal
|
||||||
let _ = shutdown.changed().await;
|
let _ = shutdown.changed().await;
|
||||||
|
|
||||||
info!("Deactivating TUN tunnel...");
|
tracing::info!("Deactivating TUN tunnel...");
|
||||||
drop(_guard);
|
drop(_guard);
|
||||||
|
tracing::info!("TUN tunnel stopped.");
|
||||||
|
|
||||||
// Terminate smoltcp stack
|
|
||||||
let _ = stack_handle.await;
|
|
||||||
|
|
||||||
info!("TUN tunnel stopped.");
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -195,8 +228,6 @@ pub async fn run_wintun_tunnel(
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub async fn run_wintun_tunnel(
|
pub async fn run_wintun_tunnel(
|
||||||
_config: crate::config::ClientConfig,
|
_config: crate::config::ClientConfig,
|
||||||
_proxy_events_tx: mpsc::Sender<ProxyEvent>,
|
|
||||||
_client_msgs_rx: mpsc::UnboundedReceiver<(u16, ProxyToClientMsg)>,
|
|
||||||
_shutdown: watch::Receiver<bool>,
|
_shutdown: watch::Receiver<bool>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
Err(anyhow!("Wintun driver executed on a non-Windows host!"))
|
Err(anyhow!("Wintun driver executed on a non-Windows host!"))
|
||||||
|
|
|
||||||
|
|
@ -165,16 +165,8 @@ impl CongestionController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Phase::ProbeBandwidth => {
|
Phase::ProbeBandwidth => {
|
||||||
// BBR-style: target cwnd = BDP * gain
|
// TCP Reno Additive Increase: increase cwnd by ~1 MTU per RTT
|
||||||
let bdp = self.bandwidth_delay_product();
|
self.cwnd = self.cwnd.saturating_add(bytes * self.mtu / self.cwnd.max(1));
|
||||||
// Apply gain of 1.25 during probe bandwidth
|
|
||||||
let target = (bdp * 5 / 4).max(MIN_CWND_PACKETS * self.mtu);
|
|
||||||
// Smooth transition
|
|
||||||
if self.cwnd < target {
|
|
||||||
self.cwnd = self.cwnd.saturating_add(bytes * self.mtu / self.cwnd.max(1));
|
|
||||||
} else {
|
|
||||||
self.cwnd = target;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Phase::ProbeRtt => {
|
Phase::ProbeRtt => {
|
||||||
// Drain down to 4 packets to measure true min RTT
|
// Drain down to 4 packets to measure true min RTT
|
||||||
|
|
@ -184,20 +176,21 @@ impl CongestionController {
|
||||||
// ProbeRTT complete, return to ProbeBandwidth
|
// ProbeRTT complete, return to ProbeBandwidth
|
||||||
self.phase = Phase::ProbeBandwidth;
|
self.phase = Phase::ProbeBandwidth;
|
||||||
self.probe_rtt_timer = None;
|
self.probe_rtt_timer = None;
|
||||||
let bdp = self.bandwidth_delay_product();
|
self.cwnd = (MIN_CWND_PACKETS * self.mtu * 4).max(self.cwnd);
|
||||||
self.cwnd = bdp.max(MIN_CWND_PACKETS * self.mtu);
|
|
||||||
tracing::debug!(cwnd = self.cwnd, min_rtt = ?self.min_rtt, "congestion: probe RTT complete");
|
tracing::debug!(cwnd = self.cwnd, min_rtt = ?self.min_rtt, "congestion: probe RTT complete");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
// Periodically enter ProbeRTT to refresh min_rtt
|
// Periodically enter ProbeRTT to refresh min_rtt
|
||||||
if now.duration_since(self.min_rtt_stamp) >= MIN_RTT_EXPIRY && self.phase != Phase::ProbeRtt {
|
if now.duration_since(self.min_rtt_stamp) >= MIN_RTT_EXPIRY && self.phase != Phase::ProbeRtt {
|
||||||
self.phase = Phase::ProbeRtt;
|
self.phase = Phase::ProbeRtt;
|
||||||
self.probe_rtt_timer = Some(now);
|
self.probe_rtt_timer = Some(now);
|
||||||
tracing::debug!("congestion: entering probe RTT phase");
|
tracing::debug!("congestion: entering probe RTT phase");
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
self.update_pacing_rate();
|
self.update_pacing_rate();
|
||||||
self.last_ack_time = now;
|
self.last_ack_time = now;
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@ pub struct ProtocolMachine {
|
||||||
/// Key-derived handshake padding range
|
/// Key-derived handshake padding range
|
||||||
handshake_pad_min: usize,
|
handshake_pad_min: usize,
|
||||||
handshake_pad_max: usize,
|
handshake_pad_max: usize,
|
||||||
|
mtu: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -145,6 +146,7 @@ impl ProtocolMachine {
|
||||||
cc: CongestionController::new(config.mtu as u64),
|
cc: CongestionController::new(config.mtu as u64),
|
||||||
handshake_pad_min: config.handshake_pad_min.max(8),
|
handshake_pad_min: config.handshake_pad_min.max(8),
|
||||||
handshake_pad_max: config.handshake_pad_max.max(config.handshake_pad_min + 16),
|
handshake_pad_max: config.handshake_pad_max.max(config.handshake_pad_min + 16),
|
||||||
|
mtu: config.mtu,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ struct TunConfig {
|
||||||
wintun_path: Option<String>,
|
wintun_path: Option<String>,
|
||||||
ipv4_address: Option<String>,
|
ipv4_address: Option<String>,
|
||||||
dns: Option<String>,
|
dns: Option<String>,
|
||||||
|
stack: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
|
@ -179,6 +180,7 @@ fn map_to_client_config(raw: &ClientConfigRaw, mode: &str) -> ostp_client::confi
|
||||||
sessions: raw.mux.as_ref().and_then(|m| m.sessions).unwrap_or(1),
|
sessions: raw.mux.as_ref().and_then(|m| m.sessions).unwrap_or(1),
|
||||||
},
|
},
|
||||||
dns_server: raw.tun.as_ref().and_then(|t| t.dns.clone()),
|
dns_server: raw.tun.as_ref().and_then(|t| t.dns.clone()),
|
||||||
|
tun_stack: raw.tun.as_ref().and_then(|t| t.stack.clone()).unwrap_or_else(|| "system".to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
<div class="ambient" aria-hidden="true">
|
<div class="ambient" aria-hidden="true">
|
||||||
<div class="blob blob-1"></div>
|
<div class="blob blob-1"></div>
|
||||||
<div class="blob blob-2"></div>
|
<div class="blob blob-2"></div>
|
||||||
<div class="blob blob-3"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ── HOME SCREEN ──────────────────────────────────────────── -->
|
<!-- ── HOME SCREEN ──────────────────────────────────────────── -->
|
||||||
|
|
@ -29,9 +28,6 @@
|
||||||
<span class="brand-name">OSTP</span>
|
<span class="brand-name">OSTP</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="topbar-right">
|
<div class="topbar-right">
|
||||||
<button id="btn-lang" class="pill-btn" title="Language">
|
|
||||||
<span id="lang-label">EN</span>
|
|
||||||
</button>
|
|
||||||
<button id="btn-go-settings" class="icon-btn" aria-label="Settings">
|
<button id="btn-go-settings" class="icon-btn" aria-label="Settings">
|
||||||
<!-- Gear icon -->
|
<!-- Gear icon -->
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
|
@ -49,7 +45,6 @@
|
||||||
<div class="orbit-wrap" id="orbit-wrap">
|
<div class="orbit-wrap" id="orbit-wrap">
|
||||||
<div class="orbit orbit-1"></div>
|
<div class="orbit orbit-1"></div>
|
||||||
<div class="orbit orbit-2"></div>
|
<div class="orbit orbit-2"></div>
|
||||||
<div class="orbit orbit-3"></div>
|
|
||||||
|
|
||||||
<!-- Power button -->
|
<!-- Power button -->
|
||||||
<button id="btn-connect" class="power-btn" aria-label="Connect">
|
<button id="btn-connect" class="power-btn" aria-label="Connect">
|
||||||
|
|
@ -68,15 +63,30 @@
|
||||||
<div id="uptime-text" class="status-sub" data-i18n="hint_tap">Tap to protect your traffic</div>
|
<div id="uptime-text" class="status-sub" data-i18n="hint_tap">Tap to protect your traffic</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Server badge (shown when connected) -->
|
<!-- Connection info (shown when connected) -->
|
||||||
<div id="server-badge" class="server-badge hidden">
|
<div id="connection-info" class="connection-info hidden">
|
||||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
<div class="server-badge">
|
||||||
<rect x="2" y="2" width="20" height="8" rx="2"/>
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<rect x="2" y="14" width="20" height="8" rx="2"/>
|
<rect x="2" y="2" width="20" height="8" rx="2"/>
|
||||||
<line x1="6" y1="6" x2="6.01" y2="6"/>
|
<rect x="2" y="14" width="20" height="8" rx="2"/>
|
||||||
<line x1="6" y1="18" x2="6.01" y2="18"/>
|
<line x1="6" y1="6" x2="6.01" y2="6"/>
|
||||||
</svg>
|
<line x1="6" y1="18" x2="6.01" y2="18"/>
|
||||||
<span id="server-badge-text">—</span>
|
</svg>
|
||||||
|
<span id="server-badge-text">—</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ping-test-box">
|
||||||
|
<div class="ping-test-left">
|
||||||
|
<span class="ping-test-title">CONNECTION TEST</span>
|
||||||
|
<span id="ping-text-value" class="ping-test-value">Target Ping: -- ms</span>
|
||||||
|
</div>
|
||||||
|
<button id="btn-test-ping" class="ping-btn">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M22 12h-4l-3 9L9 3l-3 9H2"/>
|
||||||
|
</svg>
|
||||||
|
<span>Test Ping</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
@ -108,35 +118,6 @@
|
||||||
<span id="metric-up" class="metric-value">0 B</span>
|
<span id="metric-up" class="metric-value">0 B</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="metric-sep"></div>
|
|
||||||
|
|
||||||
<div class="metric">
|
|
||||||
<div class="metric-icon ping-icon" id="ping-icon-block">
|
|
||||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<path d="M22 12h-4l-3 9L9 3l-3 9H2"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div class="metric-body">
|
|
||||||
<span class="metric-label">Ping</span>
|
|
||||||
<span id="metric-ping" class="metric-value">— ms</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="metric-sep"></div>
|
|
||||||
|
|
||||||
<div class="metric">
|
|
||||||
<div class="metric-icon mode-icon">
|
|
||||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<rect x="2" y="3" width="20" height="14" rx="2"/>
|
|
||||||
<path d="M8 21h8M12 17v4"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div class="metric-body">
|
|
||||||
<span class="metric-label">Mode</span>
|
|
||||||
<span id="metric-mode" class="metric-value">—</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -243,6 +224,14 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="field-group" id="group-tun-stack">
|
||||||
|
<label class="field-label" for="in-tun-stack" data-i18n="label_tun_stack">TUN Stack</label>
|
||||||
|
<select id="in-tun-stack" class="field-input">
|
||||||
|
<option value="system">System (tun2socks)</option>
|
||||||
|
<option value="ostp">OSTP (Native)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="toggle-row">
|
<div class="toggle-row">
|
||||||
<div class="toggle-text">
|
<div class="toggle-text">
|
||||||
<span class="toggle-name" data-i18n="label_mux">Multiplexing (Mux)</span>
|
<span class="toggle-name" data-i18n="label_mux">Multiplexing (Mux)</span>
|
||||||
|
|
@ -291,8 +280,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Save -->
|
|
||||||
<button id="btn-save-config" class="save-btn" data-i18n="save_btn">Save & Apply</button>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -24,21 +24,18 @@ const orbitWrap = $('orbit-wrap');
|
||||||
const brandDot = $('brand-dot');
|
const brandDot = $('brand-dot');
|
||||||
const statusLabel = $('status-text');
|
const statusLabel = $('status-text');
|
||||||
const statusSub = $('uptime-text');
|
const statusSub = $('uptime-text');
|
||||||
const serverBadge = $('server-badge');
|
const connInfo = $('connection-info');
|
||||||
const serverBadgeTxt = $('server-badge-text');
|
const serverBadgeTxt = $('server-badge-text');
|
||||||
const metricDown = $('metric-down');
|
const metricDown = $('metric-down');
|
||||||
const metricUp = $('metric-up');
|
const metricUp = $('metric-up');
|
||||||
const metricMode = $('metric-mode');
|
const pingValueTxt = $('ping-text-value');
|
||||||
const metricPing = $('metric-ping');
|
const btnTestPing = $('btn-test-ping');
|
||||||
const pingIconBlock = $('ping-icon-block');
|
|
||||||
const toast = $('toast');
|
const toast = $('toast');
|
||||||
|
|
||||||
const btnGoSettings = $('btn-go-settings');
|
const btnGoSettings = $('btn-go-settings');
|
||||||
const btnBack = $('btn-back');
|
const btnBack = $('btn-back');
|
||||||
const btnLang = $('btn-lang');
|
|
||||||
const btnImport = $('btn-import-url');
|
const btnImport = $('btn-import-url');
|
||||||
const btnPeekKey = $('btn-peek-key');
|
const btnPeekKey = $('btn-peek-key');
|
||||||
const btnSave = $('btn-save-config');
|
|
||||||
const importInput = $('in-import-url');
|
const importInput = $('in-import-url');
|
||||||
const inServer = $('in-server');
|
const inServer = $('in-server');
|
||||||
const inKey = $('in-key');
|
const inKey = $('in-key');
|
||||||
|
|
@ -50,6 +47,8 @@ const inPbk = $('in-pbk');
|
||||||
const inSid = $('in-sid');
|
const inSid = $('in-sid');
|
||||||
const inMtu = $('in-mtu');
|
const inMtu = $('in-mtu');
|
||||||
const inTun = $('in-tun-mode');
|
const inTun = $('in-tun-mode');
|
||||||
|
const inTunStack = $('in-tun-stack');
|
||||||
|
const groupTunStack = $('group-tun-stack');
|
||||||
const inMux = $('in-mux-mode');
|
const inMux = $('in-mux-mode');
|
||||||
const inMuxSessions = $('in-mux-sessions');
|
const inMuxSessions = $('in-mux-sessions');
|
||||||
const inDebug = $('in-debug');
|
const inDebug = $('in-debug');
|
||||||
|
|
@ -105,10 +104,11 @@ function setState(next) {
|
||||||
statusLabel.textContent = t('status_disconnected');
|
statusLabel.textContent = t('status_disconnected');
|
||||||
statusSub.textContent = t('hint_tap');
|
statusSub.textContent = t('hint_tap');
|
||||||
statusLabel.classList.add('');
|
statusLabel.classList.add('');
|
||||||
serverBadge.classList.add('hidden');
|
connInfo.classList.add('hidden');
|
||||||
metricDown.textContent = '0 B';
|
metricDown.textContent = '0 B';
|
||||||
metricUp.textContent = '0 B';
|
metricUp.textContent = '0 B';
|
||||||
metricMode.textContent = '—';
|
pingValueTxt.textContent = 'Target Ping: -- ms';
|
||||||
|
pingValueTxt.className = 'ping-test-value';
|
||||||
clearInterval(pollTimer);
|
clearInterval(pollTimer);
|
||||||
clearInterval(uptimeTimer);
|
clearInterval(uptimeTimer);
|
||||||
pollTimer = uptimeTimer = null;
|
pollTimer = uptimeTimer = null;
|
||||||
|
|
@ -121,7 +121,7 @@ function setState(next) {
|
||||||
statusLabel.classList.add('is-connecting');
|
statusLabel.classList.add('is-connecting');
|
||||||
statusLabel.textContent = t('status_connecting');
|
statusLabel.textContent = t('status_connecting');
|
||||||
statusSub.textContent = t('hint_connecting');
|
statusSub.textContent = t('hint_connecting');
|
||||||
serverBadge.classList.add('hidden');
|
connInfo.classList.add('hidden');
|
||||||
clearInterval(uptimeTimer);
|
clearInterval(uptimeTimer);
|
||||||
uptimeSecs = 0;
|
uptimeSecs = 0;
|
||||||
|
|
||||||
|
|
@ -132,10 +132,10 @@ function setState(next) {
|
||||||
statusLabel.classList.add('is-connected');
|
statusLabel.classList.add('is-connected');
|
||||||
statusLabel.textContent = t('status_connected');
|
statusLabel.textContent = t('status_connected');
|
||||||
|
|
||||||
// Show server badge
|
// Show connection info
|
||||||
if (serverAddr) {
|
if (serverAddr) {
|
||||||
serverBadgeTxt.textContent = serverAddr;
|
serverBadgeTxt.textContent = serverAddr;
|
||||||
serverBadge.classList.remove('hidden');
|
connInfo.classList.remove('hidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start uptime counter
|
// Start uptime counter
|
||||||
|
|
@ -161,29 +161,6 @@ async function poll() {
|
||||||
if (metrics) {
|
if (metrics) {
|
||||||
metricDown.textContent = fmtBytes(metrics.bytes_recv);
|
metricDown.textContent = fmtBytes(metrics.bytes_recv);
|
||||||
metricUp.textContent = fmtBytes(metrics.bytes_sent);
|
metricUp.textContent = fmtBytes(metrics.bytes_sent);
|
||||||
|
|
||||||
const isTun = rawConfig?.tun?.enable;
|
|
||||||
metricMode.textContent = isTun ? 'TUN' : 'SOCKS5';
|
|
||||||
|
|
||||||
const rtt = metrics.rtt_ms || 0;
|
|
||||||
if (rtt > 0) {
|
|
||||||
metricPing.textContent = rtt + ' ms';
|
|
||||||
// Color code: green < 80ms, yellow < 200ms, red >= 200ms
|
|
||||||
if (rtt < 80) {
|
|
||||||
metricPing.style.color = 'var(--ping-good, #4ade80)';
|
|
||||||
pingIconBlock.style.color = 'var(--ping-good, #4ade80)';
|
|
||||||
} else if (rtt < 200) {
|
|
||||||
metricPing.style.color = 'var(--ping-mid, #facc15)';
|
|
||||||
pingIconBlock.style.color = 'var(--ping-mid, #facc15)';
|
|
||||||
} else {
|
|
||||||
metricPing.style.color = 'var(--ping-bad, #f87171)';
|
|
||||||
pingIconBlock.style.color = 'var(--ping-bad, #f87171)';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
metricPing.textContent = '— ms';
|
|
||||||
metricPing.style.color = '';
|
|
||||||
pingIconBlock.style.color = '';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
setState('disconnected');
|
setState('disconnected');
|
||||||
|
|
@ -199,15 +176,10 @@ function startPolling() {
|
||||||
// ── Connect / Disconnect ─────────────────────────────────────────────────────
|
// ── Connect / Disconnect ─────────────────────────────────────────────────────
|
||||||
async function handleToggle() {
|
async function handleToggle() {
|
||||||
if (appState === 'disconnected') {
|
if (appState === 'disconnected') {
|
||||||
// Read server address for badge before connecting
|
|
||||||
try {
|
try {
|
||||||
const raw = await invoke('get_config');
|
const raw = await invoke('get_config');
|
||||||
const cfg = JSON.parse(raw);
|
const cfg = JSON.parse(raw);
|
||||||
serverAddr = cfg.server || '';
|
serverAddr = cfg.server || '';
|
||||||
|
|
||||||
// Determine mode label
|
|
||||||
const isTun = cfg.tun?.enable;
|
|
||||||
metricMode.textContent = isTun ? 'TUN' : 'SOCKS5';
|
|
||||||
} catch { serverAddr = ''; }
|
} catch { serverAddr = ''; }
|
||||||
|
|
||||||
setState('connecting');
|
setState('connecting');
|
||||||
|
|
@ -260,8 +232,11 @@ async function loadConfigIntoForm() {
|
||||||
inSid.value = c.reality?.sid || '';
|
inSid.value = c.reality?.sid || '';
|
||||||
inMtu.value = c.mtu || '';
|
inMtu.value = c.mtu || '';
|
||||||
inTun.checked = !!c.tun?.enable;
|
inTun.checked = !!c.tun?.enable;
|
||||||
|
inTunStack.value = c.tun?.stack || 'system';
|
||||||
inMux.checked = !!c.mux?.enabled;
|
inMux.checked = !!c.mux?.enabled;
|
||||||
inMuxSessions.value = c.mux?.sessions || '';
|
inMuxSessions.value = c.mux?.sessions || '';
|
||||||
|
|
||||||
|
groupTunStack.style.display = inTun.checked ? 'block' : 'none';
|
||||||
inDns.value = c.tun?.dns || '';
|
inDns.value = c.tun?.dns || '';
|
||||||
inDebug.checked = !!c.debug;
|
inDebug.checked = !!c.debug;
|
||||||
|
|
||||||
|
|
@ -275,14 +250,20 @@ async function loadConfigIntoForm() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Config — save ─────────────────────────────────────────────────────────────
|
// ── Config — save ─────────────────────────────────────────────────────────────
|
||||||
async function handleSave() {
|
let autoSaveTimer = null;
|
||||||
|
function scheduleAutoSave() {
|
||||||
|
clearTimeout(autoSaveTimer);
|
||||||
|
autoSaveTimer = setTimeout(() => handleSave(true), 600);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSave(silent = false) {
|
||||||
if (!rawConfig) rawConfig = { mode: 'client', log_level: 'info' };
|
if (!rawConfig) rawConfig = { mode: 'client', log_level: 'info' };
|
||||||
|
|
||||||
const server = inServer.value.trim();
|
const server = inServer.value.trim();
|
||||||
const key = inKey.value.trim();
|
const key = inKey.value.trim();
|
||||||
|
|
||||||
if (!server) { showToast(t('err_server_req') || 'Server address required', 'error'); return; }
|
if (!server) { if (!silent) showToast(t('err_server_req') || 'Server address required', 'error'); return; }
|
||||||
if (!key) { showToast(t('err_key_req') || 'Access key required', 'error'); return; }
|
if (!key) { if (!silent) showToast(t('err_key_req') || 'Access key required', 'error'); return; }
|
||||||
|
|
||||||
rawConfig.mode = 'client';
|
rawConfig.mode = 'client';
|
||||||
rawConfig.server = server;
|
rawConfig.server = server;
|
||||||
|
|
@ -324,6 +305,7 @@ async function handleSave() {
|
||||||
}
|
}
|
||||||
rawConfig.tun.enable = inTun.checked;
|
rawConfig.tun.enable = inTun.checked;
|
||||||
rawConfig.tun.dns = inDns.value.trim() || null;
|
rawConfig.tun.dns = inDns.value.trim() || null;
|
||||||
|
rawConfig.tun.stack = inTunStack.value;
|
||||||
|
|
||||||
rawConfig.exclude = {
|
rawConfig.exclude = {
|
||||||
domains: splitLines(inDomains.value),
|
domains: splitLines(inDomains.value),
|
||||||
|
|
@ -333,14 +315,11 @@ async function handleSave() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const ok = await invoke('save_config', { jsonContent: JSON.stringify(rawConfig, null, 2) });
|
const ok = await invoke('save_config', { jsonContent: JSON.stringify(rawConfig, null, 2) });
|
||||||
if (ok) {
|
if (!ok && !silent) {
|
||||||
showToast(t('toast_saved'), 'ok');
|
|
||||||
setTimeout(() => showScreen('home'), 700);
|
|
||||||
} else {
|
|
||||||
showToast(t('toast_error'), 'error');
|
showToast(t('toast_error'), 'error');
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(String(err), 'error');
|
if (!silent) showToast(String(err), 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -366,6 +345,7 @@ function handleImport() {
|
||||||
|
|
||||||
importInput.value = '';
|
importInput.value = '';
|
||||||
showToast(t('toast_imported'), 'ok');
|
showToast(t('toast_imported'), 'ok');
|
||||||
|
handleSave(false);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, 'error');
|
showToast(err.message, 'error');
|
||||||
}
|
}
|
||||||
|
|
@ -390,19 +370,35 @@ window.addEventListener('DOMContentLoaded', async () => {
|
||||||
btnConnect.addEventListener('click', handleToggle);
|
btnConnect.addEventListener('click', handleToggle);
|
||||||
btnGoSettings.addEventListener('click', () => showScreen('settings'));
|
btnGoSettings.addEventListener('click', () => showScreen('settings'));
|
||||||
btnBack.addEventListener('click', () => showScreen('home'));
|
btnBack.addEventListener('click', () => showScreen('home'));
|
||||||
btnSave.addEventListener('click', handleSave);
|
|
||||||
btnImport.addEventListener('click', handleImport);
|
btnImport.addEventListener('click', handleImport);
|
||||||
btnPeekKey.addEventListener('click', togglePeek);
|
btnPeekKey.addEventListener('click', togglePeek);
|
||||||
|
inTun.addEventListener('change', () => { groupTunStack.style.display = inTun.checked ? 'block' : 'none'; });
|
||||||
importInput.addEventListener('keydown', e => { if (e.key === 'Enter') handleImport(); });
|
importInput.addEventListener('keydown', e => { if (e.key === 'Enter') handleImport(); });
|
||||||
|
|
||||||
btnLang.addEventListener('click', () => {
|
// Auto-save wiring
|
||||||
toggleLang();
|
const formInputs = document.querySelectorAll('#settings-screen input:not(#in-import-url), #settings-screen textarea, #settings-screen select');
|
||||||
// Refresh dynamic text without losing state
|
formInputs.forEach(el => {
|
||||||
const cur = appState;
|
el.addEventListener('input', scheduleAutoSave);
|
||||||
appState = '';
|
el.addEventListener('change', scheduleAutoSave);
|
||||||
setState(cur);
|
});
|
||||||
document.getElementById('lang-label').textContent =
|
|
||||||
localStorage.getItem('ostp_lang') === 'ru' ? 'RU' : 'EN';
|
btnTestPing.addEventListener('click', async () => {
|
||||||
|
pingValueTxt.textContent = 'Testing...';
|
||||||
|
pingValueTxt.className = 'ping-test-value';
|
||||||
|
try {
|
||||||
|
const metrics = await invoke('get_metrics');
|
||||||
|
if (metrics && metrics.rtt_ms > 0) {
|
||||||
|
const rtt = metrics.rtt_ms;
|
||||||
|
pingValueTxt.textContent = `Target Ping: ${rtt} ms`;
|
||||||
|
if (rtt < 80) pingValueTxt.classList.add('good');
|
||||||
|
else if (rtt < 200) pingValueTxt.classList.add('warn');
|
||||||
|
else pingValueTxt.classList.add('bad');
|
||||||
|
} else {
|
||||||
|
pingValueTxt.textContent = 'Target Ping: -- ms';
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
pingValueTxt.textContent = 'Target Ping: Error';
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Restore status on app open
|
// Restore status on app open
|
||||||
|
|
|
||||||
|
|
@ -84,35 +84,26 @@ input, textarea { font-family: inherit; }
|
||||||
.blob {
|
.blob {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
filter: blur(80px);
|
filter: blur(100px);
|
||||||
will-change: transform;
|
will-change: transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
.blob-1 {
|
.blob-1 {
|
||||||
width: 360px; height: 360px;
|
width: 400px; height: 400px;
|
||||||
background: var(--c-accent);
|
background: var(--c-accent);
|
||||||
opacity: 0.055;
|
opacity: 0.15;
|
||||||
top: -140px; right: -80px;
|
top: -150px; right: -100px;
|
||||||
animation: blob-drift 28s infinite alternate ease-in-out;
|
animation: blob-drift 28s infinite alternate ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.blob-2 {
|
.blob-2 {
|
||||||
width: 280px; height: 280px;
|
width: 350px; height: 350px;
|
||||||
background: var(--c-green);
|
background: var(--c-green);
|
||||||
opacity: 0.04;
|
opacity: 0.10;
|
||||||
bottom: -100px; left: -60px;
|
bottom: -100px; left: -100px;
|
||||||
animation: blob-drift 22s infinite alternate-reverse ease-in-out;
|
animation: blob-drift 22s infinite alternate-reverse ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.blob-3 {
|
|
||||||
width: 200px; height: 200px;
|
|
||||||
background: var(--c-accent-2);
|
|
||||||
opacity: 0.035;
|
|
||||||
top: 50%; left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
animation: blob-drift 35s infinite alternate ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes blob-drift {
|
@keyframes blob-drift {
|
||||||
from { transform: translate(0, 0); }
|
from { transform: translate(0, 0); }
|
||||||
to { transform: translate(30px, 20px); }
|
to { transform: translate(30px, 20px); }
|
||||||
|
|
@ -379,38 +370,100 @@ input, textarea { font-family: inherit; }
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Server badge ─────────────────────────────────────────────────────────── */
|
/* ── Connection info ──────────────────────────────────────────────────────── */
|
||||||
|
.connection-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
width: 100%;
|
||||||
|
transition: all var(--t-med);
|
||||||
|
}
|
||||||
|
|
||||||
|
.connection-info.hidden {
|
||||||
|
opacity: 0; pointer-events: none; transform: translateY(4px);
|
||||||
|
}
|
||||||
|
|
||||||
.server-badge {
|
.server-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 12px 20px;
|
||||||
|
border-radius: 30px;
|
||||||
|
background: rgba(255,255,255,0.08);
|
||||||
|
border: 1px solid rgba(255,255,255,0.15);
|
||||||
|
font-size: 0.95rem;
|
||||||
|
color: rgba(255,255,255,0.7);
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
font-weight: 600;
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ping-test-box {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
width: calc(100% - 64px);
|
||||||
|
max-width: 400px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: rgba(255,255,255,0.03);
|
||||||
|
border-radius: 20px;
|
||||||
|
border: 1px solid rgba(255,255,255,0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ping-test-left {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ping-test-title {
|
||||||
|
font-size: 0.62rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: rgba(255,255,255,0.38);
|
||||||
|
letter-spacing: 0.8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ping-test-value {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: rgba(255,255,255,0.7);
|
||||||
|
transition: color var(--t-fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ping-test-value.good { color: var(--c-green); }
|
||||||
|
.ping-test-value.warn { color: var(--c-amber); }
|
||||||
|
.ping-test-value.bad { color: var(--c-red); }
|
||||||
|
|
||||||
|
.ping-btn {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
padding: 5px 12px;
|
background: var(--c-accent-dim);
|
||||||
border-radius: var(--r-full);
|
border: none;
|
||||||
background: var(--c-card);
|
padding: 8px 12px;
|
||||||
border: 1px solid var(--c-card-border);
|
border-radius: 12px;
|
||||||
font-size: 0.7rem;
|
color: var(--c-accent);
|
||||||
color: var(--c-txt-2);
|
font-size: 0.8rem;
|
||||||
font-family: 'JetBrains Mono', monospace;
|
font-weight: 700;
|
||||||
backdrop-filter: blur(8px);
|
transition: all var(--t-fast);
|
||||||
transition: all var(--t-med);
|
|
||||||
max-width: 200px;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.server-badge.hidden { opacity: 0; pointer-events: none; transform: translateY(4px); }
|
.ping-btn:hover {
|
||||||
|
background: rgba(108,114,255,0.2);
|
||||||
|
transform: scale(1.03);
|
||||||
|
}
|
||||||
|
.ping-btn:active { transform: scale(0.96); }
|
||||||
|
|
||||||
/* ── Metrics bar ──────────────────────────────────────────────────────────── */
|
/* ── Metrics bar ──────────────────────────────────────────────────────────── */
|
||||||
.metrics-bar {
|
.metrics-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0;
|
gap: 0;
|
||||||
padding: 12px 4px 16px;
|
padding: 24px 20px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
background: rgba(255,255,255,0.02);
|
background: rgba(255,255,255,0.04);
|
||||||
border-top: 1px solid var(--c-card-border);
|
border-top: 1px solid rgba(255,255,255,0.08);
|
||||||
border-radius: var(--r-lg) var(--r-lg) 0 0;
|
|
||||||
margin: 0 -18px;
|
margin: 0 -18px;
|
||||||
padding-inline: 18px;
|
padding-inline: 18px;
|
||||||
}
|
}
|
||||||
|
|
@ -419,27 +472,27 @@ input, textarea { font-family: inherit; }
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
justify-content: center;
|
||||||
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.metric-sep {
|
.metric-sep {
|
||||||
width: 1px;
|
width: 1px;
|
||||||
height: 28px;
|
height: 40px;
|
||||||
background: var(--c-card-border);
|
background: rgba(255,255,255,0.15);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
margin: 0 4px;
|
margin: 0 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.metric-icon {
|
.metric-icon {
|
||||||
width: 26px; height: 26px;
|
width: 36px; height: 36px;
|
||||||
border-radius: var(--r-xs);
|
border-radius: 10px;
|
||||||
display: flex; align-items: center; justify-content: center;
|
display: flex; align-items: center; justify-content: center;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.down-icon { background: var(--c-green-dim); color: var(--c-green); }
|
.down-icon { background: var(--c-green-dim); color: var(--c-green); }
|
||||||
.up-icon { background: var(--c-accent-dim); color: var(--c-accent); }
|
.up-icon { background: var(--c-accent-dim); color: var(--c-accent); }
|
||||||
.ping-icon { background: rgba(245,158,11,0.10); color: var(--c-amber); }
|
|
||||||
|
|
||||||
.metric-body {
|
.metric-body {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -449,16 +502,16 @@ input, textarea { font-family: inherit; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.metric-label {
|
.metric-label {
|
||||||
font-size: 0.58rem;
|
font-size: 0.75rem;
|
||||||
color: var(--c-txt-3);
|
color: rgba(255,255,255,0.54);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.6px;
|
letter-spacing: 0.8px;
|
||||||
font-weight: 600;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.metric-value {
|
.metric-value {
|
||||||
font-size: 0.76rem;
|
font-size: 1rem;
|
||||||
font-weight: 600;
|
font-weight: 700;
|
||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
color: var(--c-txt-1);
|
color: var(--c-txt-1);
|
||||||
font-family: 'JetBrains Mono', monospace;
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
|
@ -694,29 +747,7 @@ input, textarea { font-family: inherit; }
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Save button ──────────────────────────────────────────────────────────── */
|
|
||||||
.save-btn {
|
|
||||||
width: 100%;
|
|
||||||
height: 44px;
|
|
||||||
border-radius: var(--r-md);
|
|
||||||
border: 1px solid var(--c-accent);
|
|
||||||
background: var(--c-accent-dim);
|
|
||||||
color: var(--c-accent);
|
|
||||||
font-size: 0.82rem;
|
|
||||||
font-weight: 600;
|
|
||||||
letter-spacing: 0.3px;
|
|
||||||
transition: all var(--t-fast);
|
|
||||||
flex-shrink: 0;
|
|
||||||
backdrop-filter: blur(8px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.save-btn:hover {
|
|
||||||
background: var(--c-accent);
|
|
||||||
color: #fff;
|
|
||||||
box-shadow: 0 4px 20px var(--c-accent-glow);
|
|
||||||
}
|
|
||||||
|
|
||||||
.save-btn:active { transform: scale(0.98); }
|
|
||||||
|
|
||||||
/* ── Toast ────────────────────────────────────────────────────────────────── */
|
/* ── Toast ────────────────────────────────────────────────────────────────── */
|
||||||
.toast {
|
.toast {
|
||||||
|
|
|
||||||
|
|
@ -18,3 +18,5 @@ serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
portable-atomic = { workspace = true }
|
portable-atomic = { workspace = true }
|
||||||
|
tracing-subscriber = "0.3.23"
|
||||||
|
tracing.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ class OstpClientSdk private constructor(private val context: Context) {
|
||||||
|
|
||||||
// ── Native JNI bindings ───────────────────────────────────────────────────
|
// ── Native JNI bindings ───────────────────────────────────────────────────
|
||||||
|
|
||||||
private external fun nativeStartClient(configJson: String, fd: Int): Boolean
|
private external fun nativeStartClient(configJson: String): Boolean
|
||||||
private external fun nativeStopClient(): Boolean
|
private external fun nativeStopClient(): Boolean
|
||||||
private external fun nativeGetMetrics(): String
|
private external fun nativeGetMetrics(): String
|
||||||
private external fun nativeGetLogs(): String
|
private external fun nativeGetLogs(): String
|
||||||
|
|
@ -165,7 +165,7 @@ class OstpClientSdk private constructor(private val context: Context) {
|
||||||
*
|
*
|
||||||
* @return `true` if the native layer accepted the start command.
|
* @return `true` if the native layer accepted the start command.
|
||||||
*/
|
*/
|
||||||
fun start(config: Config, fd: Int = -1): Boolean {
|
fun start(config: Config): Boolean {
|
||||||
if (started.getAndSet(true)) {
|
if (started.getAndSet(true)) {
|
||||||
emitLog("SDK already started; call stop() first to change config")
|
emitLog("SDK already started; call stop() first to change config")
|
||||||
return false
|
return false
|
||||||
|
|
@ -175,7 +175,7 @@ class OstpClientSdk private constructor(private val context: Context) {
|
||||||
_state.value = TunnelState.Connecting
|
_state.value = TunnelState.Connecting
|
||||||
|
|
||||||
val json = config.toNativeJson()
|
val json = config.toNativeJson()
|
||||||
val ok = nativeStartClient(json, fd)
|
val ok = nativeStartClient(json)
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
_state.value = TunnelState.Failed("Native layer rejected config")
|
_state.value = TunnelState.Failed("Native layer rejected config")
|
||||||
started.set(false)
|
started.set(false)
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,49 @@ use tokio::runtime::Runtime;
|
||||||
use tokio::sync::{mpsc, watch};
|
use tokio::sync::{mpsc, watch};
|
||||||
use ostp_client::bridge::{Bridge, BridgeMetrics};
|
use ostp_client::bridge::{Bridge, BridgeMetrics};
|
||||||
use ostp_client::config::ClientConfig;
|
use ostp_client::config::ClientConfig;
|
||||||
|
use ostp_client::tunnel;
|
||||||
use ostp_client::app::{BridgeCommand, UiEvent};
|
use ostp_client::app::{BridgeCommand, UiEvent};
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
struct JniLogWriter;
|
||||||
|
|
||||||
|
impl Write for JniLogWriter {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||||
|
let s = String::from_utf8_lossy(buf).trim().to_string();
|
||||||
|
if !s.is_empty() {
|
||||||
|
add_log(s);
|
||||||
|
}
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
fn flush(&mut self) -> std::io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> tracing_subscriber::fmt::MakeWriter<'a> for JniLogWriter {
|
||||||
|
type Writer = JniLogWriter;
|
||||||
|
fn make_writer(&'a self) -> Self::Writer {
|
||||||
|
JniLogWriter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static TRACING_INIT: std::sync::Once = std::sync::Once::new();
|
||||||
|
|
||||||
|
fn init_tracing() {
|
||||||
|
TRACING_INIT.call_once(|| {
|
||||||
|
let subscriber = tracing_subscriber::fmt()
|
||||||
|
.with_writer(JniLogWriter)
|
||||||
|
.with_ansi(false)
|
||||||
|
.finish();
|
||||||
|
let _ = tracing::subscriber::set_global_default(subscriber);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
struct SdkState {
|
struct SdkState {
|
||||||
runtime: Option<Runtime>,
|
runtime: Option<Runtime>,
|
||||||
shutdown_tx: Option<watch::Sender<bool>>,
|
shutdown_tx: Option<watch::Sender<bool>>,
|
||||||
metrics: Option<Arc<BridgeMetrics>>,
|
metrics: Option<Arc<BridgeMetrics>>,
|
||||||
|
tun_child: Option<std::process::Child>,
|
||||||
cmd_tx: Option<mpsc::Sender<BridgeCommand>>,
|
cmd_tx: Option<mpsc::Sender<BridgeCommand>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -22,6 +59,7 @@ lazy_static! {
|
||||||
runtime: None,
|
runtime: None,
|
||||||
shutdown_tx: None,
|
shutdown_tx: None,
|
||||||
metrics: None,
|
metrics: None,
|
||||||
|
tun_child: None,
|
||||||
cmd_tx: None,
|
cmd_tx: None,
|
||||||
});
|
});
|
||||||
static ref LOGS: Mutex<VecDeque<String>> = Mutex::new(VecDeque::new());
|
static ref LOGS: Mutex<VecDeque<String>> = Mutex::new(VecDeque::new());
|
||||||
|
|
@ -39,11 +77,13 @@ fn add_log(text: String) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "system" fn Java_net_ostp_client_OstpClientSdk_nativeStartClient(
|
pub extern "system" fn Java_net_ostp_client_OstpClientSdk_startClient(
|
||||||
mut env: JNIEnv,
|
mut env: JNIEnv,
|
||||||
_class: JClass,
|
_class: JClass,
|
||||||
config_json: JString,
|
config_json: JString,
|
||||||
fd: jni::sys::jint,
|
fd: jni::sys::jint,
|
||||||
|
t2s_bin_path: JString,
|
||||||
|
local_proxy: JString,
|
||||||
) -> jboolean {
|
) -> jboolean {
|
||||||
let mut state = match STATE.lock() {
|
let mut state = match STATE.lock() {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
|
|
@ -55,6 +95,8 @@ pub extern "system" fn Java_net_ostp_client_OstpClientSdk_nativeStartClient(
|
||||||
return jni::sys::JNI_TRUE;
|
return jni::sys::JNI_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init_tracing();
|
||||||
|
|
||||||
if let Ok(jvm) = env.get_java_vm() {
|
if let Ok(jvm) = env.get_java_vm() {
|
||||||
if let Ok(mut guard) = JVM.lock() {
|
if let Ok(mut guard) = JVM.lock() {
|
||||||
*guard = Some(jvm);
|
*guard = Some(jvm);
|
||||||
|
|
@ -100,6 +142,16 @@ pub extern "system" fn Java_net_ostp_client_OstpClientSdk_nativeStartClient(
|
||||||
Err(_) => return jni::sys::JNI_FALSE,
|
Err(_) => return jni::sys::JNI_FALSE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let t2s_path: String = match env.get_string(&t2s_bin_path) {
|
||||||
|
Ok(s) => s.into(),
|
||||||
|
Err(_) => return jni::sys::JNI_FALSE,
|
||||||
|
};
|
||||||
|
|
||||||
|
let proxy_addr: String = match env.get_string(&local_proxy) {
|
||||||
|
Ok(s) => s.into(),
|
||||||
|
Err(_) => return jni::sys::JNI_FALSE,
|
||||||
|
};
|
||||||
|
|
||||||
// Parse config from JSON
|
// Parse config from JSON
|
||||||
let config: ClientConfig = match serde_json::from_str(&config_str) {
|
let config: ClientConfig = match serde_json::from_str(&config_str) {
|
||||||
Ok(cfg) => cfg,
|
Ok(cfg) => cfg,
|
||||||
|
|
@ -109,6 +161,8 @@ pub extern "system" fn Java_net_ostp_client_OstpClientSdk_nativeStartClient(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let debug = config.debug;
|
||||||
|
|
||||||
// Create tokio runtime
|
// Create tokio runtime
|
||||||
let rt = match Runtime::new() {
|
let rt = match Runtime::new() {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
|
|
@ -139,6 +193,7 @@ pub extern "system" fn Java_net_ostp_client_OstpClientSdk_nativeStartClient(
|
||||||
let (ui_tx, mut ui_rx) = mpsc::channel(512);
|
let (ui_tx, mut ui_rx) = mpsc::channel(512);
|
||||||
let (cmd_tx, cmd_rx) = mpsc::channel(128);
|
let (cmd_tx, cmd_rx) = mpsc::channel(128);
|
||||||
let (shutdown_tx, shutdown_rx) = watch::channel(false);
|
let (shutdown_tx, shutdown_rx) = watch::channel(false);
|
||||||
|
let proxy_shutdown_rx = shutdown_tx.subscribe();
|
||||||
|
|
||||||
let metrics_clone = Arc::clone(&metrics);
|
let metrics_clone = Arc::clone(&metrics);
|
||||||
|
|
||||||
|
|
@ -147,51 +202,19 @@ pub extern "system" fn Java_net_ostp_client_OstpClientSdk_nativeStartClient(
|
||||||
bridge.run(ui_tx, cmd_rx, shutdown_rx, proxy_events_rx, client_msgs_tx).await
|
bridge.run(ui_tx, cmd_rx, shutdown_rx, proxy_events_rx, client_msgs_tx).await
|
||||||
});
|
});
|
||||||
|
|
||||||
if config.mode == "tun" {
|
let config_proxy = config.clone();
|
||||||
if fd < 0 {
|
rt.spawn(async move {
|
||||||
add_log("Error: TUN mode requested but invalid file descriptor provided".to_string());
|
tunnel::run_local_proxy(
|
||||||
return jni::sys::JNI_FALSE;
|
config_proxy.local_proxy,
|
||||||
}
|
config_proxy.ostp,
|
||||||
|
config_proxy.exclusions,
|
||||||
let tun_dev = match ostp_client::tunnel::create_tun_device_from_fd(fd, config.ostp.mtu) {
|
config_proxy.debug,
|
||||||
Ok(d) => d,
|
proxy_shutdown_rx,
|
||||||
Err(e) => {
|
proxy_events_tx,
|
||||||
add_log(format!("Failed to wrap TUN fd: {:?}", e));
|
client_msgs_rx,
|
||||||
return jni::sys::JNI_FALSE;
|
)
|
||||||
}
|
.await
|
||||||
};
|
});
|
||||||
|
|
||||||
let stack_shutdown_rx = shutdown_tx.subscribe();
|
|
||||||
let proxy_events_tx_clone = proxy_events_tx.clone();
|
|
||||||
let mtu = config.ostp.mtu;
|
|
||||||
rt.spawn(async move {
|
|
||||||
if let Err(e) = ostp_client::tunnel::run_smoltcp_stack(
|
|
||||||
tun_dev.packet_rx,
|
|
||||||
tun_dev.packet_tx,
|
|
||||||
mtu,
|
|
||||||
proxy_events_tx_clone,
|
|
||||||
client_msgs_rx,
|
|
||||||
stack_shutdown_rx,
|
|
||||||
).await {
|
|
||||||
add_log(format!("smoltcp stack loop failed: {:?}", e));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
let config_proxy = config.clone();
|
|
||||||
let proxy_shutdown_rx = shutdown_tx.subscribe();
|
|
||||||
rt.spawn(async move {
|
|
||||||
let _ = ostp_client::tunnel::run_local_proxy(
|
|
||||||
config_proxy.local_proxy,
|
|
||||||
config_proxy.ostp,
|
|
||||||
config_proxy.exclusions,
|
|
||||||
config_proxy.debug,
|
|
||||||
proxy_shutdown_rx,
|
|
||||||
proxy_events_tx,
|
|
||||||
client_msgs_rx,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start logs receiver task
|
// Start logs receiver task
|
||||||
rt.spawn(async move {
|
rt.spawn(async move {
|
||||||
|
|
@ -211,6 +234,90 @@ pub extern "system" fn Java_net_ostp_client_OstpClientSdk_nativeStartClient(
|
||||||
let _ = cmd_tx_clone.send(BridgeCommand::ToggleTunnel).await;
|
let _ = cmd_tx_clone.send(BridgeCommand::ToggleTunnel).await;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if config.tun_stack == "system" {
|
||||||
|
// Spawn tun2socks
|
||||||
|
let fd_str = format!("fd://{}", fd);
|
||||||
|
let proxy_str = format!("socks5://{}", proxy_addr);
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
add_log(format!("Spawning tun2socks: {} -device {} -proxy {}", t2s_path, fd_str, proxy_str));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cmd = std::process::Command::new(&t2s_path);
|
||||||
|
cmd.arg("-device")
|
||||||
|
.arg(&fd_str)
|
||||||
|
.arg("-proxy")
|
||||||
|
.arg(&proxy_str);
|
||||||
|
|
||||||
|
if config.ostp.mtu > 0 {
|
||||||
|
cmd.arg("-mtu").arg(config.ostp.mtu.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.stdout(std::process::Stdio::piped())
|
||||||
|
.stderr(std::process::Stdio::piped());
|
||||||
|
|
||||||
|
let mut child = match cmd.spawn() {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(e) => {
|
||||||
|
add_log(format!("Failed to spawn tun2socks from Rust: {e}"));
|
||||||
|
return jni::sys::JNI_FALSE;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let stdout = match child.stdout.take() {
|
||||||
|
Some(s) => s,
|
||||||
|
None => {
|
||||||
|
add_log("Failed to capture tun2socks stdout".to_string());
|
||||||
|
return jni::sys::JNI_FALSE;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let stderr = match child.stderr.take() {
|
||||||
|
Some(s) => s,
|
||||||
|
None => {
|
||||||
|
add_log("Failed to capture tun2socks stderr".to_string());
|
||||||
|
return jni::sys::JNI_FALSE;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Read stdout
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
use std::io::{BufRead, BufReader};
|
||||||
|
let reader = BufReader::new(stdout);
|
||||||
|
for line in reader.lines() {
|
||||||
|
if let Ok(l) = line {
|
||||||
|
if debug {
|
||||||
|
add_log(format!("tun2socks: {}", l));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Read stderr & wait
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
use std::io::{BufRead, BufReader};
|
||||||
|
let reader = BufReader::new(stderr);
|
||||||
|
for line in reader.lines() {
|
||||||
|
if let Ok(l) = line {
|
||||||
|
if debug {
|
||||||
|
add_log(format!("tun2socks ERROR: {}", l));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
state.tun_child = Some(child);
|
||||||
|
} else {
|
||||||
|
if debug {
|
||||||
|
add_log("Using OSTP native TUN stack. Bypassing tun2socks.".to_string());
|
||||||
|
}
|
||||||
|
let shutdown_rx_clone = shutdown_tx.subscribe();
|
||||||
|
let config_clone = config.clone();
|
||||||
|
rt.spawn(async move {
|
||||||
|
if let Err(e) = tunnel::native_handler::run_native_tunnel_from_fd(config_clone, shutdown_rx_clone, fd).await {
|
||||||
|
add_log(format!("Native TUN exited with error: {}", e));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
state.runtime = Some(rt);
|
state.runtime = Some(rt);
|
||||||
state.shutdown_tx = Some(shutdown_tx);
|
state.shutdown_tx = Some(shutdown_tx);
|
||||||
state.metrics = Some(metrics_clone);
|
state.metrics = Some(metrics_clone);
|
||||||
|
|
@ -221,31 +328,42 @@ pub extern "system" fn Java_net_ostp_client_OstpClientSdk_nativeStartClient(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "system" fn Java_net_ostp_client_OstpClientSdk_nativeStopClient(
|
pub extern "system" fn Java_net_ostp_client_OstpClientSdk_stopClient(
|
||||||
_env: JNIEnv,
|
_env: JNIEnv,
|
||||||
_class: JClass,
|
_class: JClass,
|
||||||
) -> jboolean {
|
) -> jboolean {
|
||||||
let mut state = match STATE.lock() {
|
let (tun_child, shutdown_tx, runtime) = {
|
||||||
Ok(s) => s,
|
let mut state = match STATE.lock() {
|
||||||
Err(_) => return jni::sys::JNI_FALSE,
|
Ok(s) => s,
|
||||||
|
Err(_) => return jni::sys::JNI_FALSE,
|
||||||
|
};
|
||||||
|
let c = state.tun_child.take();
|
||||||
|
let s = state.shutdown_tx.take();
|
||||||
|
let r = state.runtime.take();
|
||||||
|
state.cmd_tx = None;
|
||||||
|
state.metrics = None;
|
||||||
|
(c, s, r)
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(shutdown_tx) = state.shutdown_tx.take() {
|
if let Some(mut child) = tun_child {
|
||||||
let _ = shutdown_tx.send(true);
|
let _ = child.kill();
|
||||||
|
add_log("Killed tun2socks process".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(rt) = state.runtime.take() {
|
if let Some(s) = shutdown_tx {
|
||||||
|
let _ = s.send(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(rt) = runtime {
|
||||||
rt.shutdown_timeout(std::time::Duration::from_secs(3));
|
rt.shutdown_timeout(std::time::Duration::from_secs(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
state.cmd_tx = None;
|
|
||||||
state.metrics = None;
|
|
||||||
add_log("OSTP SDK: Client successfully stopped".to_string());
|
add_log("OSTP SDK: Client successfully stopped".to_string());
|
||||||
jni::sys::JNI_TRUE
|
jni::sys::JNI_TRUE
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "system" fn Java_net_ostp_client_OstpClientSdk_nativeGetMetrics(
|
pub extern "system" fn Java_net_ostp_client_OstpClientSdk_getMetrics(
|
||||||
env: JNIEnv,
|
env: JNIEnv,
|
||||||
_class: JClass,
|
_class: JClass,
|
||||||
) -> jstring {
|
) -> jstring {
|
||||||
|
|
@ -266,7 +384,7 @@ pub extern "system" fn Java_net_ostp_client_OstpClientSdk_nativeGetMetrics(
|
||||||
r#"{{"bytes_sent": {}, "bytes_recv": {}, "connection_state": {}, "rtt_ms": {}}}"#,
|
r#"{{"bytes_sent": {}, "bytes_recv": {}, "connection_state": {}, "rtt_ms": {}}}"#,
|
||||||
sent, recv, conn_state, rtt
|
sent, recv, conn_state, rtt
|
||||||
);
|
);
|
||||||
match env.new_string(json) {
|
match env.new_string(json.replace('\0', "")) {
|
||||||
Ok(s) => s.into_raw(),
|
Ok(s) => s.into_raw(),
|
||||||
Err(_) => std::ptr::null_mut(),
|
Err(_) => std::ptr::null_mut(),
|
||||||
}
|
}
|
||||||
|
|
@ -279,7 +397,7 @@ pub extern "system" fn Java_net_ostp_client_OstpClientSdk_nativeGetMetrics(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "system" fn Java_net_ostp_client_OstpClientSdk_nativeGetLogs(
|
pub extern "system" fn Java_net_ostp_client_OstpClientSdk_getLogs(
|
||||||
env: JNIEnv,
|
env: JNIEnv,
|
||||||
_class: JClass,
|
_class: JClass,
|
||||||
) -> jstring {
|
) -> jstring {
|
||||||
|
|
@ -293,7 +411,7 @@ pub extern "system" fn Java_net_ostp_client_OstpClientSdk_nativeGetLogs(
|
||||||
Err(_) => "[]".to_string(),
|
Err(_) => "[]".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
match env.new_string(json) {
|
match env.new_string(json.replace('\0', "")) {
|
||||||
Ok(s) => s.into_raw(),
|
Ok(s) => s.into_raw(),
|
||||||
Err(_) => std::ptr::null_mut(),
|
Err(_) => std::ptr::null_mut(),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,10 @@ use portable_atomic::AtomicU64;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
|
body::Body,
|
||||||
extract::{Path, State},
|
extract::{Path, State},
|
||||||
http::{header, StatusCode, Uri},
|
http::{header, Request, StatusCode, Uri},
|
||||||
response::IntoResponse,
|
response::{IntoResponse, Response},
|
||||||
routing::{get, post, put},
|
routing::{get, post, put},
|
||||||
Json, Router,
|
Json, Router,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ pub(crate) struct RemoteState {
|
||||||
|
|
||||||
pub async fn run_server(
|
pub async fn run_server(
|
||||||
bind_addrs: Vec<String>,
|
bind_addrs: Vec<String>,
|
||||||
|
server_public_ip: Option<String>,
|
||||||
access_keys: Vec<(String, crate::api::UserMeta)>,
|
access_keys: Vec<(String, crate::api::UserMeta)>,
|
||||||
outbound: Option<OutboundConfig>,
|
outbound: Option<OutboundConfig>,
|
||||||
api_config: Option<ApiConfig>,
|
api_config: Option<ApiConfig>,
|
||||||
|
|
@ -248,7 +249,7 @@ pub async fn run_server(
|
||||||
let primary = bind_addrs.first().cloned().unwrap_or_else(|| "0.0.0.0:50000".to_string());
|
let primary = bind_addrs.first().cloned().unwrap_or_else(|| "0.0.0.0:50000".to_string());
|
||||||
let parts: Vec<&str> = primary.rsplitn(2, ':').collect();
|
let parts: Vec<&str> = primary.rsplitn(2, ':').collect();
|
||||||
let server_port: u16 = parts.first().and_then(|p| p.parse().ok()).unwrap_or(50000);
|
let server_port: u16 = parts.first().and_then(|p| p.parse().ok()).unwrap_or(50000);
|
||||||
let server_host = parts.get(1).unwrap_or(&"0.0.0.0").to_string();
|
let server_host = server_public_ip.unwrap_or_else(|| parts.get(1).unwrap_or(&"0.0.0.0").to_string());
|
||||||
let rq = reality_query.clone().unwrap_or_default();
|
let rq = reality_query.clone().unwrap_or_default();
|
||||||
let config_path_api = config_path.clone();
|
let config_path_api = config_path.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
|
|
|
||||||
|
|
@ -894,8 +894,9 @@ async fn run_app() -> Result<()> {
|
||||||
limit_bytes: uc.limit(),
|
limit_bytes: uc.limit(),
|
||||||
})
|
})
|
||||||
}).collect::<Vec<_>>();
|
}).collect::<Vec<_>>();
|
||||||
|
let host = get_or_ask_public_ip(&args.config);
|
||||||
// Pass all listen addresses for multi-listener support
|
// Pass all listen addresses for multi-listener support
|
||||||
ostp_server::run_server(listen_addrs, access_keys_meta, outbound, api_config, fallback_config, debug, rq, rc, Some(args.config)).await?;
|
ostp_server::run_server(listen_addrs, Some(host), access_keys_meta, outbound, api_config, fallback_config, debug, rq, rc, Some(args.config)).await?;
|
||||||
}
|
}
|
||||||
AppMode::Client(client_cfg) => {
|
AppMode::Client(client_cfg) => {
|
||||||
run_client_directly(client_cfg).await?;
|
run_client_directly(client_cfg).await?;
|
||||||
|
|
@ -1008,6 +1009,7 @@ async fn run_client_directly(client_cfg: ClientConfig) -> Result<()> {
|
||||||
let reality_cfg = client_cfg.reality.as_ref();
|
let reality_cfg = client_cfg.reality.as_ref();
|
||||||
let client_conf = ostp_client::config::ClientConfig {
|
let client_conf = ostp_client::config::ClientConfig {
|
||||||
mode: if is_tun_enabled { "tun".to_string() } else { "proxy".to_string() },
|
mode: if is_tun_enabled { "tun".to_string() } else { "proxy".to_string() },
|
||||||
|
tun_stack: "native".to_string(),
|
||||||
debug: client_cfg.debug.unwrap_or(false),
|
debug: client_cfg.debug.unwrap_or(false),
|
||||||
ostp: ostp_client::config::OstpConfig {
|
ostp: ostp_client::config::OstpConfig {
|
||||||
server_addr: client_cfg.server.clone(),
|
server_addr: client_cfg.server.clone(),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue