├── .github └── workflows │ ├── build.yml │ └── publish.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── README.zh-CN.md ├── build.rs ├── config ├── cert.pem ├── config.json └── key.pem ├── example ├── grpc_trojan │ ├── client.json │ └── server.json ├── quic_trojan │ ├── client.json │ └── server.json └── tcp_trojan │ ├── client.json │ └── server.json ├── proto └── transport.proto ├── src ├── config │ ├── base.rs │ ├── mod.rs │ ├── parser.rs │ └── tls.rs ├── lib.rs ├── main.rs ├── protocol │ ├── common │ │ ├── addr.rs │ │ ├── atype.rs │ │ ├── command.rs │ │ ├── mod.rs │ │ ├── request.rs │ │ └── stream.rs │ ├── direct │ │ └── outbound.rs │ ├── mod.rs │ ├── socks5 │ │ ├── base.rs │ │ ├── mod.rs │ │ └── parser.rs │ └── trojan │ │ ├── base.rs │ │ ├── mod.rs │ │ ├── packet.rs │ │ └── parser.rs ├── proxy │ ├── base.rs │ ├── grpc │ │ ├── acceptor.rs │ │ ├── handler.rs │ │ ├── mod.rs │ │ └── server.rs │ ├── mod.rs │ ├── quic │ │ ├── mod.rs │ │ └── server.rs │ └── tcp │ │ ├── acceptor.rs │ │ ├── handler.rs │ │ ├── mod.rs │ │ └── server.rs └── transport │ ├── grpc_stream.rs │ └── mod.rs └── tests ├── proxy └── acceptor_test.rs └── tests.rs /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | types: [opened, synchronize, reopened] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Setup protoc 20 | uses: arduino/setup-protoc@v1.1.2 21 | - name: Build 22 | run: cargo build --verbose 23 | - name: Run tests 24 | run: cargo test --verbose 25 | 26 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build-windows: 12 | name: Build for Windows x86_64 13 | runs-on: windows-2019 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v2 17 | 18 | - name: Setup protoc 19 | uses: arduino/setup-protoc@v1.1.2 20 | 21 | - name: get-cmake 22 | uses: lukka/get-cmake@v3.23.0 23 | 24 | - name: Visual Studio shell 25 | uses: egor-tensin/vs-shell@v2 26 | 27 | - name: Rust Toolchain 28 | uses: actions-rs/toolchain@v1 29 | with: 30 | profile: minimal 31 | toolchain: stable 32 | 33 | - name: Build 34 | run: cargo build --release --locked 35 | 36 | - name: Upload 37 | uses: actions/upload-artifact@v2 38 | with: 39 | name: trojan_rust_win_x86_64 40 | path: target/release/trojan-rust.exe 41 | 42 | build: 43 | name: Build for ${{ matrix.os }} 44 | runs-on: ${{ matrix.os }} 45 | strategy: 46 | matrix: 47 | name: [ 48 | linux, 49 | macos 50 | ] 51 | 52 | include: 53 | - name: linux 54 | os: ubuntu-latest 55 | artifact_name: target/release/trojan-rust 56 | asset_name: trojan-rust-linux 57 | 58 | - name: macos 59 | os: macos-latest 60 | artifact_name: target/release/trojan-rust 61 | asset_name: trojan-rust-macos 62 | 63 | steps: 64 | - name: Checkout 65 | uses: actions/checkout@v2 66 | 67 | - name: Setup protoc 68 | uses: arduino/setup-protoc@v1.1.2 69 | 70 | - name: Rust Toolchain 71 | uses: actions-rs/toolchain@v1 72 | with: 73 | profile: minimal 74 | toolchain: stable 75 | 76 | - name: Build 77 | run: cargo build --release --locked 78 | 79 | - name: Upload 80 | uses: actions/upload-artifact@v2 81 | with: 82 | name: ${{ matrix.asset_name }} 83 | path: ${{ matrix.artifact_name }} 84 | 85 | publish: 86 | runs-on: ubuntu-latest 87 | needs: [build, build-windows] 88 | steps: 89 | - name: Download Windows Artifact 90 | uses: actions/download-artifact@v2 91 | with: 92 | name: trojan_rust_win_x86_64 93 | path: ~/windows/ 94 | 95 | - name: Download Ubuntu Artifact 96 | uses: actions/download-artifact@v2 97 | with: 98 | name: trojan-rust-linux 99 | path: ~/linux/ 100 | 101 | - name: Download MacOS Artifact 102 | uses: actions/download-artifact@v2 103 | with: 104 | name: trojan-rust-macos 105 | path: ~/macos/ 106 | 107 | - name: Rename 108 | run: | 109 | ls 110 | ls ~/linux/ 111 | ls ~/macos/ 112 | ls ~/windows/ 113 | 114 | mv ~/macos/trojan-rust ./trojan_rust_macos_x86_64 115 | mv ~/linux/trojan-rust ./trojan_rust_linux_x86_64 116 | mv ~/windows/trojan-rust.exe ./trojan_rust_win_x86_64.exe 117 | 118 | - name: Release 119 | uses: softprops/action-gh-release@v0.1.7 120 | with: 121 | files: | 122 | trojan_rust_win_x86_64.exe 123 | trojan_rust_linux_x86_64 124 | trojan_rust_macos_x86_64 125 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .vscode 3 | .idea 4 | config -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ahash" 7 | version = "0.7.6" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" 10 | dependencies = [ 11 | "getrandom", 12 | "once_cell", 13 | "version_check", 14 | ] 15 | 16 | [[package]] 17 | name = "aho-corasick" 18 | version = "0.7.18" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 21 | dependencies = [ 22 | "memchr", 23 | ] 24 | 25 | [[package]] 26 | name = "anyhow" 27 | version = "1.0.52" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3" 30 | 31 | [[package]] 32 | name = "async-stream" 33 | version = "0.3.2" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "171374e7e3b2504e0e5236e3b59260560f9fe94bfe9ac39ba5e4e929c5590625" 36 | dependencies = [ 37 | "async-stream-impl", 38 | "futures-core", 39 | ] 40 | 41 | [[package]] 42 | name = "async-stream-impl" 43 | version = "0.3.2" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "648ed8c8d2ce5409ccd57453d9d1b214b342a0d69376a6feda1fd6cae3299308" 46 | dependencies = [ 47 | "proc-macro2", 48 | "quote", 49 | "syn", 50 | ] 51 | 52 | [[package]] 53 | name = "async-trait" 54 | version = "0.1.56" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" 57 | dependencies = [ 58 | "proc-macro2", 59 | "quote", 60 | "syn", 61 | ] 62 | 63 | [[package]] 64 | name = "autocfg" 65 | version = "1.1.0" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 68 | 69 | [[package]] 70 | name = "axum" 71 | version = "0.5.4" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "f4af7447fc1214c1f3a1ace861d0216a6c8bb13965b64bbad9650f375b67689a" 74 | dependencies = [ 75 | "async-trait", 76 | "axum-core", 77 | "bitflags", 78 | "bytes", 79 | "futures-util", 80 | "http", 81 | "http-body", 82 | "hyper", 83 | "itoa 1.0.1", 84 | "matchit", 85 | "memchr", 86 | "mime", 87 | "percent-encoding", 88 | "pin-project-lite", 89 | "serde", 90 | "sync_wrapper", 91 | "tokio", 92 | "tower", 93 | "tower-http", 94 | "tower-layer", 95 | "tower-service", 96 | ] 97 | 98 | [[package]] 99 | name = "axum-core" 100 | version = "0.2.3" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "3bdc19781b16e32f8a7200368a336fa4509d4b72ef15dd4e41df5290855ee1e6" 103 | dependencies = [ 104 | "async-trait", 105 | "bytes", 106 | "futures-util", 107 | "http", 108 | "http-body", 109 | "mime", 110 | ] 111 | 112 | [[package]] 113 | name = "base64" 114 | version = "0.13.0" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 117 | 118 | [[package]] 119 | name = "bitflags" 120 | version = "1.3.2" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 123 | 124 | [[package]] 125 | name = "block-buffer" 126 | version = "0.10.0" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95" 129 | dependencies = [ 130 | "generic-array", 131 | ] 132 | 133 | [[package]] 134 | name = "bumpalo" 135 | version = "3.6.1" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" 138 | 139 | [[package]] 140 | name = "byteorder" 141 | version = "1.4.3" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 144 | 145 | [[package]] 146 | name = "bytes" 147 | version = "1.4.0" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" 150 | 151 | [[package]] 152 | name = "cc" 153 | version = "1.0.67" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" 156 | 157 | [[package]] 158 | name = "cfg-if" 159 | version = "1.0.0" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 162 | 163 | [[package]] 164 | name = "clap" 165 | version = "4.1.4" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" 168 | dependencies = [ 169 | "bitflags", 170 | "clap_lex", 171 | "is-terminal", 172 | "strsim", 173 | "termcolor", 174 | ] 175 | 176 | [[package]] 177 | name = "clap_lex" 178 | version = "0.3.1" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" 181 | dependencies = [ 182 | "os_str_bytes", 183 | ] 184 | 185 | [[package]] 186 | name = "constant_time_eq" 187 | version = "0.2.4" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279" 190 | 191 | [[package]] 192 | name = "core-foundation" 193 | version = "0.9.2" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" 196 | dependencies = [ 197 | "core-foundation-sys", 198 | "libc", 199 | ] 200 | 201 | [[package]] 202 | name = "core-foundation-sys" 203 | version = "0.8.3" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" 206 | 207 | [[package]] 208 | name = "cpufeatures" 209 | version = "0.2.1" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" 212 | dependencies = [ 213 | "libc", 214 | ] 215 | 216 | [[package]] 217 | name = "crypto-common" 218 | version = "0.1.3" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" 221 | dependencies = [ 222 | "generic-array", 223 | "typenum", 224 | ] 225 | 226 | [[package]] 227 | name = "difflib" 228 | version = "0.4.0" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" 231 | 232 | [[package]] 233 | name = "digest" 234 | version = "0.10.3" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" 237 | dependencies = [ 238 | "block-buffer", 239 | "crypto-common", 240 | ] 241 | 242 | [[package]] 243 | name = "downcast" 244 | version = "0.11.0" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" 247 | 248 | [[package]] 249 | name = "either" 250 | version = "1.6.1" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 253 | 254 | [[package]] 255 | name = "env_logger" 256 | version = "0.10.0" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" 259 | dependencies = [ 260 | "humantime", 261 | "is-terminal", 262 | "log", 263 | "regex", 264 | "termcolor", 265 | ] 266 | 267 | [[package]] 268 | name = "errno" 269 | version = "0.2.8" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" 272 | dependencies = [ 273 | "errno-dragonfly", 274 | "libc", 275 | "winapi", 276 | ] 277 | 278 | [[package]] 279 | name = "errno-dragonfly" 280 | version = "0.1.2" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" 283 | dependencies = [ 284 | "cc", 285 | "libc", 286 | ] 287 | 288 | [[package]] 289 | name = "fixedbitset" 290 | version = "0.4.1" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "279fb028e20b3c4c320317955b77c5e0c9701f05a1d309905d6fc702cdc5053e" 293 | 294 | [[package]] 295 | name = "float-cmp" 296 | version = "0.9.0" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" 299 | dependencies = [ 300 | "num-traits", 301 | ] 302 | 303 | [[package]] 304 | name = "fnv" 305 | version = "1.0.7" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 308 | 309 | [[package]] 310 | name = "fragile" 311 | version = "1.0.0" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "69a039c3498dc930fe810151a34ba0c1c70b02b8625035592e74432f678591f2" 314 | 315 | [[package]] 316 | name = "futures" 317 | version = "0.3.21" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" 320 | dependencies = [ 321 | "futures-channel", 322 | "futures-core", 323 | "futures-executor", 324 | "futures-io", 325 | "futures-sink", 326 | "futures-task", 327 | "futures-util", 328 | ] 329 | 330 | [[package]] 331 | name = "futures-channel" 332 | version = "0.3.21" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" 335 | dependencies = [ 336 | "futures-core", 337 | "futures-sink", 338 | ] 339 | 340 | [[package]] 341 | name = "futures-core" 342 | version = "0.3.21" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" 345 | 346 | [[package]] 347 | name = "futures-executor" 348 | version = "0.3.21" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" 351 | dependencies = [ 352 | "futures-core", 353 | "futures-task", 354 | "futures-util", 355 | "num_cpus", 356 | ] 357 | 358 | [[package]] 359 | name = "futures-io" 360 | version = "0.3.21" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" 363 | 364 | [[package]] 365 | name = "futures-macro" 366 | version = "0.3.21" 367 | source = "registry+https://github.com/rust-lang/crates.io-index" 368 | checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" 369 | dependencies = [ 370 | "proc-macro2", 371 | "quote", 372 | "syn", 373 | ] 374 | 375 | [[package]] 376 | name = "futures-sink" 377 | version = "0.3.21" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" 380 | 381 | [[package]] 382 | name = "futures-task" 383 | version = "0.3.21" 384 | source = "registry+https://github.com/rust-lang/crates.io-index" 385 | checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" 386 | 387 | [[package]] 388 | name = "futures-util" 389 | version = "0.3.21" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" 392 | dependencies = [ 393 | "futures-channel", 394 | "futures-core", 395 | "futures-io", 396 | "futures-macro", 397 | "futures-sink", 398 | "futures-task", 399 | "memchr", 400 | "pin-project-lite", 401 | "pin-utils", 402 | "slab", 403 | ] 404 | 405 | [[package]] 406 | name = "generic-array" 407 | version = "0.14.4" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" 410 | dependencies = [ 411 | "typenum", 412 | "version_check", 413 | ] 414 | 415 | [[package]] 416 | name = "getrandom" 417 | version = "0.2.3" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" 420 | dependencies = [ 421 | "cfg-if", 422 | "libc", 423 | "wasi 0.10.2+wasi-snapshot-preview1", 424 | ] 425 | 426 | [[package]] 427 | name = "h2" 428 | version = "0.3.9" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "8f072413d126e57991455e0a922b31e4c8ba7c2ffbebf6b78b4f8521397d65cd" 431 | dependencies = [ 432 | "bytes", 433 | "fnv", 434 | "futures-core", 435 | "futures-sink", 436 | "futures-util", 437 | "http", 438 | "indexmap", 439 | "slab", 440 | "tokio", 441 | "tokio-util 0.6.9", 442 | "tracing", 443 | ] 444 | 445 | [[package]] 446 | name = "hashbrown" 447 | version = "0.11.2" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" 450 | 451 | [[package]] 452 | name = "hashbrown" 453 | version = "0.12.1" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" 456 | dependencies = [ 457 | "ahash", 458 | ] 459 | 460 | [[package]] 461 | name = "heck" 462 | version = "0.4.0" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 465 | 466 | [[package]] 467 | name = "hermit-abi" 468 | version = "0.1.18" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" 471 | dependencies = [ 472 | "libc", 473 | ] 474 | 475 | [[package]] 476 | name = "hermit-abi" 477 | version = "0.3.1" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" 480 | 481 | [[package]] 482 | name = "http" 483 | version = "0.2.6" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" 486 | dependencies = [ 487 | "bytes", 488 | "fnv", 489 | "itoa 1.0.1", 490 | ] 491 | 492 | [[package]] 493 | name = "http-body" 494 | version = "0.4.4" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" 497 | dependencies = [ 498 | "bytes", 499 | "http", 500 | "pin-project-lite", 501 | ] 502 | 503 | [[package]] 504 | name = "http-range-header" 505 | version = "0.3.0" 506 | source = "registry+https://github.com/rust-lang/crates.io-index" 507 | checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" 508 | 509 | [[package]] 510 | name = "httparse" 511 | version = "1.5.1" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" 514 | 515 | [[package]] 516 | name = "httpdate" 517 | version = "1.0.2" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" 520 | 521 | [[package]] 522 | name = "humantime" 523 | version = "2.1.0" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 526 | 527 | [[package]] 528 | name = "hyper" 529 | version = "0.14.16" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "b7ec3e62bdc98a2f0393a5048e4c30ef659440ea6e0e572965103e72bd836f55" 532 | dependencies = [ 533 | "bytes", 534 | "futures-channel", 535 | "futures-core", 536 | "futures-util", 537 | "h2", 538 | "http", 539 | "http-body", 540 | "httparse", 541 | "httpdate", 542 | "itoa 0.4.7", 543 | "pin-project-lite", 544 | "socket2", 545 | "tokio", 546 | "tower-service", 547 | "tracing", 548 | "want", 549 | ] 550 | 551 | [[package]] 552 | name = "hyper-timeout" 553 | version = "0.4.1" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" 556 | dependencies = [ 557 | "hyper", 558 | "pin-project-lite", 559 | "tokio", 560 | "tokio-io-timeout", 561 | ] 562 | 563 | [[package]] 564 | name = "indexmap" 565 | version = "1.7.0" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" 568 | dependencies = [ 569 | "autocfg", 570 | "hashbrown 0.11.2", 571 | ] 572 | 573 | [[package]] 574 | name = "io-lifetimes" 575 | version = "1.0.5" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" 578 | dependencies = [ 579 | "libc", 580 | "windows-sys 0.45.0", 581 | ] 582 | 583 | [[package]] 584 | name = "is-terminal" 585 | version = "0.4.3" 586 | source = "registry+https://github.com/rust-lang/crates.io-index" 587 | checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" 588 | dependencies = [ 589 | "hermit-abi 0.3.1", 590 | "io-lifetimes", 591 | "rustix", 592 | "windows-sys 0.45.0", 593 | ] 594 | 595 | [[package]] 596 | name = "itertools" 597 | version = "0.10.3" 598 | source = "registry+https://github.com/rust-lang/crates.io-index" 599 | checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" 600 | dependencies = [ 601 | "either", 602 | ] 603 | 604 | [[package]] 605 | name = "itoa" 606 | version = "0.4.7" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" 609 | 610 | [[package]] 611 | name = "itoa" 612 | version = "1.0.1" 613 | source = "registry+https://github.com/rust-lang/crates.io-index" 614 | checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" 615 | 616 | [[package]] 617 | name = "js-sys" 618 | version = "0.3.51" 619 | source = "registry+https://github.com/rust-lang/crates.io-index" 620 | checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062" 621 | dependencies = [ 622 | "wasm-bindgen", 623 | ] 624 | 625 | [[package]] 626 | name = "lazy_static" 627 | version = "1.4.0" 628 | source = "registry+https://github.com/rust-lang/crates.io-index" 629 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 630 | 631 | [[package]] 632 | name = "libc" 633 | version = "0.2.139" 634 | source = "registry+https://github.com/rust-lang/crates.io-index" 635 | checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" 636 | 637 | [[package]] 638 | name = "linux-raw-sys" 639 | version = "0.1.4" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" 642 | 643 | [[package]] 644 | name = "lock_api" 645 | version = "0.4.7" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" 648 | dependencies = [ 649 | "autocfg", 650 | "scopeguard", 651 | ] 652 | 653 | [[package]] 654 | name = "log" 655 | version = "0.4.14" 656 | source = "registry+https://github.com/rust-lang/crates.io-index" 657 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 658 | dependencies = [ 659 | "cfg-if", 660 | ] 661 | 662 | [[package]] 663 | name = "matchit" 664 | version = "0.5.0" 665 | source = "registry+https://github.com/rust-lang/crates.io-index" 666 | checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" 667 | 668 | [[package]] 669 | name = "memchr" 670 | version = "2.5.0" 671 | source = "registry+https://github.com/rust-lang/crates.io-index" 672 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 673 | 674 | [[package]] 675 | name = "mime" 676 | version = "0.3.16" 677 | source = "registry+https://github.com/rust-lang/crates.io-index" 678 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 679 | 680 | [[package]] 681 | name = "mio" 682 | version = "0.8.5" 683 | source = "registry+https://github.com/rust-lang/crates.io-index" 684 | checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" 685 | dependencies = [ 686 | "libc", 687 | "log", 688 | "wasi 0.11.0+wasi-snapshot-preview1", 689 | "windows-sys 0.42.0", 690 | ] 691 | 692 | [[package]] 693 | name = "mockall" 694 | version = "0.11.1" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "5641e476bbaf592a3939a7485fa079f427b4db21407d5ebfd5bba4e07a1f6f4c" 697 | dependencies = [ 698 | "cfg-if", 699 | "downcast", 700 | "fragile", 701 | "lazy_static", 702 | "mockall_derive", 703 | "predicates", 704 | "predicates-tree", 705 | ] 706 | 707 | [[package]] 708 | name = "mockall_derive" 709 | version = "0.11.1" 710 | source = "registry+https://github.com/rust-lang/crates.io-index" 711 | checksum = "262d56735932ee0240d515656e5a7667af3af2a5b0af4da558c4cff2b2aeb0c7" 712 | dependencies = [ 713 | "cfg-if", 714 | "proc-macro2", 715 | "quote", 716 | "syn", 717 | ] 718 | 719 | [[package]] 720 | name = "multimap" 721 | version = "0.8.3" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" 724 | 725 | [[package]] 726 | name = "normalize-line-endings" 727 | version = "0.3.0" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" 730 | 731 | [[package]] 732 | name = "num-traits" 733 | version = "0.2.14" 734 | source = "registry+https://github.com/rust-lang/crates.io-index" 735 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 736 | dependencies = [ 737 | "autocfg", 738 | ] 739 | 740 | [[package]] 741 | name = "num_cpus" 742 | version = "1.13.0" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 745 | dependencies = [ 746 | "hermit-abi 0.1.18", 747 | "libc", 748 | ] 749 | 750 | [[package]] 751 | name = "once_cell" 752 | version = "1.17.0" 753 | source = "registry+https://github.com/rust-lang/crates.io-index" 754 | checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" 755 | 756 | [[package]] 757 | name = "openssl-probe" 758 | version = "0.1.4" 759 | source = "registry+https://github.com/rust-lang/crates.io-index" 760 | checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" 761 | 762 | [[package]] 763 | name = "os_str_bytes" 764 | version = "6.0.0" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" 767 | 768 | [[package]] 769 | name = "parking_lot" 770 | version = "0.12.0" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" 773 | dependencies = [ 774 | "lock_api", 775 | "parking_lot_core", 776 | ] 777 | 778 | [[package]] 779 | name = "parking_lot_core" 780 | version = "0.9.3" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" 783 | dependencies = [ 784 | "cfg-if", 785 | "libc", 786 | "redox_syscall", 787 | "smallvec", 788 | "windows-sys 0.36.1", 789 | ] 790 | 791 | [[package]] 792 | name = "percent-encoding" 793 | version = "2.1.0" 794 | source = "registry+https://github.com/rust-lang/crates.io-index" 795 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 796 | 797 | [[package]] 798 | name = "petgraph" 799 | version = "0.6.0" 800 | source = "registry+https://github.com/rust-lang/crates.io-index" 801 | checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" 802 | dependencies = [ 803 | "fixedbitset", 804 | "indexmap", 805 | ] 806 | 807 | [[package]] 808 | name = "pin-project" 809 | version = "1.0.10" 810 | source = "registry+https://github.com/rust-lang/crates.io-index" 811 | checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" 812 | dependencies = [ 813 | "pin-project-internal", 814 | ] 815 | 816 | [[package]] 817 | name = "pin-project-internal" 818 | version = "1.0.10" 819 | source = "registry+https://github.com/rust-lang/crates.io-index" 820 | checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" 821 | dependencies = [ 822 | "proc-macro2", 823 | "quote", 824 | "syn", 825 | ] 826 | 827 | [[package]] 828 | name = "pin-project-lite" 829 | version = "0.2.9" 830 | source = "registry+https://github.com/rust-lang/crates.io-index" 831 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 832 | 833 | [[package]] 834 | name = "pin-utils" 835 | version = "0.1.0" 836 | source = "registry+https://github.com/rust-lang/crates.io-index" 837 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 838 | 839 | [[package]] 840 | name = "ppv-lite86" 841 | version = "0.2.16" 842 | source = "registry+https://github.com/rust-lang/crates.io-index" 843 | checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" 844 | 845 | [[package]] 846 | name = "predicates" 847 | version = "2.1.0" 848 | source = "registry+https://github.com/rust-lang/crates.io-index" 849 | checksum = "95e5a7689e456ab905c22c2b48225bb921aba7c8dfa58440d68ba13f6222a715" 850 | dependencies = [ 851 | "difflib", 852 | "float-cmp", 853 | "itertools", 854 | "normalize-line-endings", 855 | "predicates-core", 856 | "regex", 857 | ] 858 | 859 | [[package]] 860 | name = "predicates-core" 861 | version = "1.0.2" 862 | source = "registry+https://github.com/rust-lang/crates.io-index" 863 | checksum = "57e35a3326b75e49aa85f5dc6ec15b41108cf5aee58eabb1f274dd18b73c2451" 864 | 865 | [[package]] 866 | name = "predicates-tree" 867 | version = "1.0.3" 868 | source = "registry+https://github.com/rust-lang/crates.io-index" 869 | checksum = "d7dd0fd014130206c9352efbdc92be592751b2b9274dff685348341082c6ea3d" 870 | dependencies = [ 871 | "predicates-core", 872 | "treeline", 873 | ] 874 | 875 | [[package]] 876 | name = "prettyplease" 877 | version = "0.1.10" 878 | source = "registry+https://github.com/rust-lang/crates.io-index" 879 | checksum = "d9e07e3a46d0771a8a06b5f4441527802830b43e679ba12f44960f48dd4c6803" 880 | dependencies = [ 881 | "proc-macro2", 882 | "syn", 883 | ] 884 | 885 | [[package]] 886 | name = "proc-macro2" 887 | version = "1.0.40" 888 | source = "registry+https://github.com/rust-lang/crates.io-index" 889 | checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" 890 | dependencies = [ 891 | "unicode-ident", 892 | ] 893 | 894 | [[package]] 895 | name = "prost" 896 | version = "0.11.0" 897 | source = "registry+https://github.com/rust-lang/crates.io-index" 898 | checksum = "399c3c31cdec40583bb68f0b18403400d01ec4289c383aa047560439952c4dd7" 899 | dependencies = [ 900 | "bytes", 901 | "prost-derive", 902 | ] 903 | 904 | [[package]] 905 | name = "prost-build" 906 | version = "0.11.0" 907 | source = "registry+https://github.com/rust-lang/crates.io-index" 908 | checksum = "d49d928704208aba2cb1fb022ce1a319bdedcb03caf51ddf82734fa903407762" 909 | dependencies = [ 910 | "bytes", 911 | "heck", 912 | "itertools", 913 | "lazy_static", 914 | "log", 915 | "multimap", 916 | "petgraph", 917 | "prost", 918 | "prost-types", 919 | "regex", 920 | "tempfile", 921 | "which", 922 | ] 923 | 924 | [[package]] 925 | name = "prost-derive" 926 | version = "0.11.0" 927 | source = "registry+https://github.com/rust-lang/crates.io-index" 928 | checksum = "7345d5f0e08c0536d7ac7229952590239e77abf0a0100a1b1d890add6ea96364" 929 | dependencies = [ 930 | "anyhow", 931 | "itertools", 932 | "proc-macro2", 933 | "quote", 934 | "syn", 935 | ] 936 | 937 | [[package]] 938 | name = "prost-types" 939 | version = "0.11.0" 940 | source = "registry+https://github.com/rust-lang/crates.io-index" 941 | checksum = "d30bc806a29b347314be074ff0608ef8e547286e8ea68b061a2fe55689edc01f" 942 | dependencies = [ 943 | "bytes", 944 | "prost", 945 | ] 946 | 947 | [[package]] 948 | name = "quinn" 949 | version = "0.9.3" 950 | source = "registry+https://github.com/rust-lang/crates.io-index" 951 | checksum = "445cbfe2382fa023c4f2f3c7e1c95c03dcc1df2bf23cebcb2b13e1402c4394d1" 952 | dependencies = [ 953 | "bytes", 954 | "pin-project-lite", 955 | "quinn-proto", 956 | "quinn-udp", 957 | "rustc-hash", 958 | "rustls", 959 | "thiserror", 960 | "tokio", 961 | "tracing", 962 | "webpki", 963 | ] 964 | 965 | [[package]] 966 | name = "quinn-proto" 967 | version = "0.9.2" 968 | source = "registry+https://github.com/rust-lang/crates.io-index" 969 | checksum = "72ef4ced82a24bb281af338b9e8f94429b6eca01b4e66d899f40031f074e74c9" 970 | dependencies = [ 971 | "bytes", 972 | "rand", 973 | "ring", 974 | "rustc-hash", 975 | "rustls", 976 | "rustls-native-certs", 977 | "slab", 978 | "thiserror", 979 | "tinyvec", 980 | "tracing", 981 | "webpki", 982 | ] 983 | 984 | [[package]] 985 | name = "quinn-udp" 986 | version = "0.3.2" 987 | source = "registry+https://github.com/rust-lang/crates.io-index" 988 | checksum = "641538578b21f5e5c8ea733b736895576d0fe329bb883b937db6f4d163dbaaf4" 989 | dependencies = [ 990 | "libc", 991 | "quinn-proto", 992 | "socket2", 993 | "tracing", 994 | "windows-sys 0.42.0", 995 | ] 996 | 997 | [[package]] 998 | name = "quote" 999 | version = "1.0.9" 1000 | source = "registry+https://github.com/rust-lang/crates.io-index" 1001 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 1002 | dependencies = [ 1003 | "proc-macro2", 1004 | ] 1005 | 1006 | [[package]] 1007 | name = "rand" 1008 | version = "0.8.4" 1009 | source = "registry+https://github.com/rust-lang/crates.io-index" 1010 | checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" 1011 | dependencies = [ 1012 | "libc", 1013 | "rand_chacha", 1014 | "rand_core", 1015 | "rand_hc", 1016 | ] 1017 | 1018 | [[package]] 1019 | name = "rand_chacha" 1020 | version = "0.3.1" 1021 | source = "registry+https://github.com/rust-lang/crates.io-index" 1022 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1023 | dependencies = [ 1024 | "ppv-lite86", 1025 | "rand_core", 1026 | ] 1027 | 1028 | [[package]] 1029 | name = "rand_core" 1030 | version = "0.6.3" 1031 | source = "registry+https://github.com/rust-lang/crates.io-index" 1032 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 1033 | dependencies = [ 1034 | "getrandom", 1035 | ] 1036 | 1037 | [[package]] 1038 | name = "rand_hc" 1039 | version = "0.3.1" 1040 | source = "registry+https://github.com/rust-lang/crates.io-index" 1041 | checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" 1042 | dependencies = [ 1043 | "rand_core", 1044 | ] 1045 | 1046 | [[package]] 1047 | name = "redox_syscall" 1048 | version = "0.2.13" 1049 | source = "registry+https://github.com/rust-lang/crates.io-index" 1050 | checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" 1051 | dependencies = [ 1052 | "bitflags", 1053 | ] 1054 | 1055 | [[package]] 1056 | name = "regex" 1057 | version = "1.5.5" 1058 | source = "registry+https://github.com/rust-lang/crates.io-index" 1059 | checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" 1060 | dependencies = [ 1061 | "aho-corasick", 1062 | "memchr", 1063 | "regex-syntax", 1064 | ] 1065 | 1066 | [[package]] 1067 | name = "regex-syntax" 1068 | version = "0.6.25" 1069 | source = "registry+https://github.com/rust-lang/crates.io-index" 1070 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 1071 | 1072 | [[package]] 1073 | name = "remove_dir_all" 1074 | version = "0.5.3" 1075 | source = "registry+https://github.com/rust-lang/crates.io-index" 1076 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 1077 | dependencies = [ 1078 | "winapi", 1079 | ] 1080 | 1081 | [[package]] 1082 | name = "ring" 1083 | version = "0.16.20" 1084 | source = "registry+https://github.com/rust-lang/crates.io-index" 1085 | checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" 1086 | dependencies = [ 1087 | "cc", 1088 | "libc", 1089 | "once_cell", 1090 | "spin", 1091 | "untrusted", 1092 | "web-sys", 1093 | "winapi", 1094 | ] 1095 | 1096 | [[package]] 1097 | name = "rustc-hash" 1098 | version = "1.1.0" 1099 | source = "registry+https://github.com/rust-lang/crates.io-index" 1100 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 1101 | 1102 | [[package]] 1103 | name = "rustix" 1104 | version = "0.36.8" 1105 | source = "registry+https://github.com/rust-lang/crates.io-index" 1106 | checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" 1107 | dependencies = [ 1108 | "bitflags", 1109 | "errno", 1110 | "io-lifetimes", 1111 | "libc", 1112 | "linux-raw-sys", 1113 | "windows-sys 0.45.0", 1114 | ] 1115 | 1116 | [[package]] 1117 | name = "rustls" 1118 | version = "0.20.6" 1119 | source = "registry+https://github.com/rust-lang/crates.io-index" 1120 | checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" 1121 | dependencies = [ 1122 | "log", 1123 | "ring", 1124 | "sct", 1125 | "webpki", 1126 | ] 1127 | 1128 | [[package]] 1129 | name = "rustls-native-certs" 1130 | version = "0.6.2" 1131 | source = "registry+https://github.com/rust-lang/crates.io-index" 1132 | checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" 1133 | dependencies = [ 1134 | "openssl-probe", 1135 | "rustls-pemfile", 1136 | "schannel", 1137 | "security-framework", 1138 | ] 1139 | 1140 | [[package]] 1141 | name = "rustls-pemfile" 1142 | version = "1.0.0" 1143 | source = "registry+https://github.com/rust-lang/crates.io-index" 1144 | checksum = "e7522c9de787ff061458fe9a829dc790a3f5b22dc571694fc5883f448b94d9a9" 1145 | dependencies = [ 1146 | "base64", 1147 | ] 1148 | 1149 | [[package]] 1150 | name = "ryu" 1151 | version = "1.0.5" 1152 | source = "registry+https://github.com/rust-lang/crates.io-index" 1153 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 1154 | 1155 | [[package]] 1156 | name = "schannel" 1157 | version = "0.1.19" 1158 | source = "registry+https://github.com/rust-lang/crates.io-index" 1159 | checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" 1160 | dependencies = [ 1161 | "lazy_static", 1162 | "winapi", 1163 | ] 1164 | 1165 | [[package]] 1166 | name = "scopeguard" 1167 | version = "1.1.0" 1168 | source = "registry+https://github.com/rust-lang/crates.io-index" 1169 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1170 | 1171 | [[package]] 1172 | name = "sct" 1173 | version = "0.7.0" 1174 | source = "registry+https://github.com/rust-lang/crates.io-index" 1175 | checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" 1176 | dependencies = [ 1177 | "ring", 1178 | "untrusted", 1179 | ] 1180 | 1181 | [[package]] 1182 | name = "security-framework" 1183 | version = "2.3.1" 1184 | source = "registry+https://github.com/rust-lang/crates.io-index" 1185 | checksum = "23a2ac85147a3a11d77ecf1bc7166ec0b92febfa4461c37944e180f319ece467" 1186 | dependencies = [ 1187 | "bitflags", 1188 | "core-foundation", 1189 | "core-foundation-sys", 1190 | "libc", 1191 | "security-framework-sys", 1192 | ] 1193 | 1194 | [[package]] 1195 | name = "security-framework-sys" 1196 | version = "2.4.2" 1197 | source = "registry+https://github.com/rust-lang/crates.io-index" 1198 | checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" 1199 | dependencies = [ 1200 | "core-foundation-sys", 1201 | "libc", 1202 | ] 1203 | 1204 | [[package]] 1205 | name = "serde" 1206 | version = "1.0.125" 1207 | source = "registry+https://github.com/rust-lang/crates.io-index" 1208 | checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" 1209 | dependencies = [ 1210 | "serde_derive", 1211 | ] 1212 | 1213 | [[package]] 1214 | name = "serde_derive" 1215 | version = "1.0.125" 1216 | source = "registry+https://github.com/rust-lang/crates.io-index" 1217 | checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" 1218 | dependencies = [ 1219 | "proc-macro2", 1220 | "quote", 1221 | "syn", 1222 | ] 1223 | 1224 | [[package]] 1225 | name = "serde_json" 1226 | version = "1.0.64" 1227 | source = "registry+https://github.com/rust-lang/crates.io-index" 1228 | checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" 1229 | dependencies = [ 1230 | "itoa 0.4.7", 1231 | "ryu", 1232 | "serde", 1233 | ] 1234 | 1235 | [[package]] 1236 | name = "sha2" 1237 | version = "0.10.2" 1238 | source = "registry+https://github.com/rust-lang/crates.io-index" 1239 | checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" 1240 | dependencies = [ 1241 | "cfg-if", 1242 | "cpufeatures", 1243 | "digest", 1244 | ] 1245 | 1246 | [[package]] 1247 | name = "signal-hook-registry" 1248 | version = "1.3.0" 1249 | source = "registry+https://github.com/rust-lang/crates.io-index" 1250 | checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" 1251 | dependencies = [ 1252 | "libc", 1253 | ] 1254 | 1255 | [[package]] 1256 | name = "slab" 1257 | version = "0.4.5" 1258 | source = "registry+https://github.com/rust-lang/crates.io-index" 1259 | checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" 1260 | 1261 | [[package]] 1262 | name = "smallvec" 1263 | version = "1.6.1" 1264 | source = "registry+https://github.com/rust-lang/crates.io-index" 1265 | checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" 1266 | 1267 | [[package]] 1268 | name = "socket2" 1269 | version = "0.4.4" 1270 | source = "registry+https://github.com/rust-lang/crates.io-index" 1271 | checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" 1272 | dependencies = [ 1273 | "libc", 1274 | "winapi", 1275 | ] 1276 | 1277 | [[package]] 1278 | name = "spin" 1279 | version = "0.5.2" 1280 | source = "registry+https://github.com/rust-lang/crates.io-index" 1281 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 1282 | 1283 | [[package]] 1284 | name = "strsim" 1285 | version = "0.10.0" 1286 | source = "registry+https://github.com/rust-lang/crates.io-index" 1287 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 1288 | 1289 | [[package]] 1290 | name = "syn" 1291 | version = "1.0.98" 1292 | source = "registry+https://github.com/rust-lang/crates.io-index" 1293 | checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" 1294 | dependencies = [ 1295 | "proc-macro2", 1296 | "quote", 1297 | "unicode-ident", 1298 | ] 1299 | 1300 | [[package]] 1301 | name = "sync_wrapper" 1302 | version = "0.1.1" 1303 | source = "registry+https://github.com/rust-lang/crates.io-index" 1304 | checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8" 1305 | 1306 | [[package]] 1307 | name = "tempfile" 1308 | version = "3.2.0" 1309 | source = "registry+https://github.com/rust-lang/crates.io-index" 1310 | checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" 1311 | dependencies = [ 1312 | "cfg-if", 1313 | "libc", 1314 | "rand", 1315 | "redox_syscall", 1316 | "remove_dir_all", 1317 | "winapi", 1318 | ] 1319 | 1320 | [[package]] 1321 | name = "termcolor" 1322 | version = "1.1.2" 1323 | source = "registry+https://github.com/rust-lang/crates.io-index" 1324 | checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" 1325 | dependencies = [ 1326 | "winapi-util", 1327 | ] 1328 | 1329 | [[package]] 1330 | name = "thiserror" 1331 | version = "1.0.31" 1332 | source = "registry+https://github.com/rust-lang/crates.io-index" 1333 | checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" 1334 | dependencies = [ 1335 | "thiserror-impl", 1336 | ] 1337 | 1338 | [[package]] 1339 | name = "thiserror-impl" 1340 | version = "1.0.31" 1341 | source = "registry+https://github.com/rust-lang/crates.io-index" 1342 | checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" 1343 | dependencies = [ 1344 | "proc-macro2", 1345 | "quote", 1346 | "syn", 1347 | ] 1348 | 1349 | [[package]] 1350 | name = "tinyvec" 1351 | version = "1.6.0" 1352 | source = "registry+https://github.com/rust-lang/crates.io-index" 1353 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 1354 | dependencies = [ 1355 | "tinyvec_macros", 1356 | ] 1357 | 1358 | [[package]] 1359 | name = "tinyvec_macros" 1360 | version = "0.1.0" 1361 | source = "registry+https://github.com/rust-lang/crates.io-index" 1362 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 1363 | 1364 | [[package]] 1365 | name = "tokio" 1366 | version = "1.25.0" 1367 | source = "registry+https://github.com/rust-lang/crates.io-index" 1368 | checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" 1369 | dependencies = [ 1370 | "autocfg", 1371 | "bytes", 1372 | "libc", 1373 | "memchr", 1374 | "mio", 1375 | "num_cpus", 1376 | "parking_lot", 1377 | "pin-project-lite", 1378 | "signal-hook-registry", 1379 | "socket2", 1380 | "tokio-macros", 1381 | "windows-sys 0.42.0", 1382 | ] 1383 | 1384 | [[package]] 1385 | name = "tokio-io-timeout" 1386 | version = "1.2.0" 1387 | source = "registry+https://github.com/rust-lang/crates.io-index" 1388 | checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" 1389 | dependencies = [ 1390 | "pin-project-lite", 1391 | "tokio", 1392 | ] 1393 | 1394 | [[package]] 1395 | name = "tokio-macros" 1396 | version = "1.7.0" 1397 | source = "registry+https://github.com/rust-lang/crates.io-index" 1398 | checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" 1399 | dependencies = [ 1400 | "proc-macro2", 1401 | "quote", 1402 | "syn", 1403 | ] 1404 | 1405 | [[package]] 1406 | name = "tokio-rustls" 1407 | version = "0.23.4" 1408 | source = "registry+https://github.com/rust-lang/crates.io-index" 1409 | checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" 1410 | dependencies = [ 1411 | "rustls", 1412 | "tokio", 1413 | "webpki", 1414 | ] 1415 | 1416 | [[package]] 1417 | name = "tokio-stream" 1418 | version = "0.1.9" 1419 | source = "registry+https://github.com/rust-lang/crates.io-index" 1420 | checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9" 1421 | dependencies = [ 1422 | "futures-core", 1423 | "pin-project-lite", 1424 | "tokio", 1425 | ] 1426 | 1427 | [[package]] 1428 | name = "tokio-util" 1429 | version = "0.6.9" 1430 | source = "registry+https://github.com/rust-lang/crates.io-index" 1431 | checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" 1432 | dependencies = [ 1433 | "bytes", 1434 | "futures-core", 1435 | "futures-sink", 1436 | "log", 1437 | "pin-project-lite", 1438 | "tokio", 1439 | ] 1440 | 1441 | [[package]] 1442 | name = "tokio-util" 1443 | version = "0.7.3" 1444 | source = "registry+https://github.com/rust-lang/crates.io-index" 1445 | checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" 1446 | dependencies = [ 1447 | "bytes", 1448 | "futures-core", 1449 | "futures-io", 1450 | "futures-sink", 1451 | "futures-util", 1452 | "hashbrown 0.12.1", 1453 | "pin-project-lite", 1454 | "slab", 1455 | "tokio", 1456 | "tracing", 1457 | ] 1458 | 1459 | [[package]] 1460 | name = "tonic" 1461 | version = "0.8.0" 1462 | source = "registry+https://github.com/rust-lang/crates.io-index" 1463 | checksum = "498f271adc46acce75d66f639e4d35b31b2394c295c82496727dafa16d465dd2" 1464 | dependencies = [ 1465 | "async-stream", 1466 | "async-trait", 1467 | "axum", 1468 | "base64", 1469 | "bytes", 1470 | "futures-core", 1471 | "futures-util", 1472 | "h2", 1473 | "http", 1474 | "http-body", 1475 | "hyper", 1476 | "hyper-timeout", 1477 | "percent-encoding", 1478 | "pin-project", 1479 | "prost", 1480 | "prost-derive", 1481 | "rustls-native-certs", 1482 | "rustls-pemfile", 1483 | "tokio", 1484 | "tokio-rustls", 1485 | "tokio-stream", 1486 | "tokio-util 0.7.3", 1487 | "tower", 1488 | "tower-layer", 1489 | "tower-service", 1490 | "tracing", 1491 | "tracing-futures", 1492 | "webpki-roots", 1493 | ] 1494 | 1495 | [[package]] 1496 | name = "tonic-build" 1497 | version = "0.8.0" 1498 | source = "registry+https://github.com/rust-lang/crates.io-index" 1499 | checksum = "2fbcd2800e34e743b9ae795867d5f77b535d3a3be69fd731e39145719752df8c" 1500 | dependencies = [ 1501 | "prettyplease", 1502 | "proc-macro2", 1503 | "prost-build", 1504 | "quote", 1505 | "syn", 1506 | ] 1507 | 1508 | [[package]] 1509 | name = "tower" 1510 | version = "0.4.12" 1511 | source = "registry+https://github.com/rust-lang/crates.io-index" 1512 | checksum = "9a89fd63ad6adf737582df5db40d286574513c69a11dac5214dc3b5603d6713e" 1513 | dependencies = [ 1514 | "futures-core", 1515 | "futures-util", 1516 | "indexmap", 1517 | "pin-project", 1518 | "pin-project-lite", 1519 | "rand", 1520 | "slab", 1521 | "tokio", 1522 | "tokio-util 0.7.3", 1523 | "tower-layer", 1524 | "tower-service", 1525 | "tracing", 1526 | ] 1527 | 1528 | [[package]] 1529 | name = "tower-http" 1530 | version = "0.3.2" 1531 | source = "registry+https://github.com/rust-lang/crates.io-index" 1532 | checksum = "e980386f06883cf4d0578d6c9178c81f68b45d77d00f2c2c1bc034b3439c2c56" 1533 | dependencies = [ 1534 | "bitflags", 1535 | "bytes", 1536 | "futures-core", 1537 | "futures-util", 1538 | "http", 1539 | "http-body", 1540 | "http-range-header", 1541 | "pin-project-lite", 1542 | "tower", 1543 | "tower-layer", 1544 | "tower-service", 1545 | ] 1546 | 1547 | [[package]] 1548 | name = "tower-layer" 1549 | version = "0.3.1" 1550 | source = "registry+https://github.com/rust-lang/crates.io-index" 1551 | checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" 1552 | 1553 | [[package]] 1554 | name = "tower-service" 1555 | version = "0.3.1" 1556 | source = "registry+https://github.com/rust-lang/crates.io-index" 1557 | checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" 1558 | 1559 | [[package]] 1560 | name = "tracing" 1561 | version = "0.1.29" 1562 | source = "registry+https://github.com/rust-lang/crates.io-index" 1563 | checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" 1564 | dependencies = [ 1565 | "cfg-if", 1566 | "log", 1567 | "pin-project-lite", 1568 | "tracing-attributes", 1569 | "tracing-core", 1570 | ] 1571 | 1572 | [[package]] 1573 | name = "tracing-attributes" 1574 | version = "0.1.18" 1575 | source = "registry+https://github.com/rust-lang/crates.io-index" 1576 | checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" 1577 | dependencies = [ 1578 | "proc-macro2", 1579 | "quote", 1580 | "syn", 1581 | ] 1582 | 1583 | [[package]] 1584 | name = "tracing-core" 1585 | version = "0.1.21" 1586 | source = "registry+https://github.com/rust-lang/crates.io-index" 1587 | checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" 1588 | dependencies = [ 1589 | "lazy_static", 1590 | ] 1591 | 1592 | [[package]] 1593 | name = "tracing-futures" 1594 | version = "0.2.5" 1595 | source = "registry+https://github.com/rust-lang/crates.io-index" 1596 | checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" 1597 | dependencies = [ 1598 | "pin-project", 1599 | "tracing", 1600 | ] 1601 | 1602 | [[package]] 1603 | name = "treeline" 1604 | version = "0.1.0" 1605 | source = "registry+https://github.com/rust-lang/crates.io-index" 1606 | checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" 1607 | 1608 | [[package]] 1609 | name = "trojan-rust" 1610 | version = "0.7.2" 1611 | dependencies = [ 1612 | "async-trait", 1613 | "byteorder", 1614 | "bytes", 1615 | "clap", 1616 | "constant_time_eq", 1617 | "env_logger", 1618 | "futures", 1619 | "itertools", 1620 | "lazy_static", 1621 | "log", 1622 | "mockall", 1623 | "once_cell", 1624 | "prost", 1625 | "prost-build", 1626 | "quinn", 1627 | "rustls", 1628 | "rustls-pemfile", 1629 | "serde", 1630 | "serde_json", 1631 | "sha2", 1632 | "tokio", 1633 | "tokio-rustls", 1634 | "tokio-stream", 1635 | "tokio-util 0.7.3", 1636 | "tonic", 1637 | "tonic-build", 1638 | "uninit", 1639 | "webpki-roots", 1640 | ] 1641 | 1642 | [[package]] 1643 | name = "try-lock" 1644 | version = "0.2.3" 1645 | source = "registry+https://github.com/rust-lang/crates.io-index" 1646 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 1647 | 1648 | [[package]] 1649 | name = "typenum" 1650 | version = "1.15.0" 1651 | source = "registry+https://github.com/rust-lang/crates.io-index" 1652 | checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" 1653 | 1654 | [[package]] 1655 | name = "unicode-ident" 1656 | version = "1.0.1" 1657 | source = "registry+https://github.com/rust-lang/crates.io-index" 1658 | checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" 1659 | 1660 | [[package]] 1661 | name = "uninit" 1662 | version = "0.5.0" 1663 | source = "registry+https://github.com/rust-lang/crates.io-index" 1664 | checksum = "bdb5fa53d0101e0cd980b0d679db2c76f75c94b883985f57cfe7f50c72dd0536" 1665 | 1666 | [[package]] 1667 | name = "untrusted" 1668 | version = "0.7.1" 1669 | source = "registry+https://github.com/rust-lang/crates.io-index" 1670 | checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" 1671 | 1672 | [[package]] 1673 | name = "version_check" 1674 | version = "0.9.3" 1675 | source = "registry+https://github.com/rust-lang/crates.io-index" 1676 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" 1677 | 1678 | [[package]] 1679 | name = "want" 1680 | version = "0.3.0" 1681 | source = "registry+https://github.com/rust-lang/crates.io-index" 1682 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 1683 | dependencies = [ 1684 | "log", 1685 | "try-lock", 1686 | ] 1687 | 1688 | [[package]] 1689 | name = "wasi" 1690 | version = "0.10.2+wasi-snapshot-preview1" 1691 | source = "registry+https://github.com/rust-lang/crates.io-index" 1692 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 1693 | 1694 | [[package]] 1695 | name = "wasi" 1696 | version = "0.11.0+wasi-snapshot-preview1" 1697 | source = "registry+https://github.com/rust-lang/crates.io-index" 1698 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1699 | 1700 | [[package]] 1701 | name = "wasm-bindgen" 1702 | version = "0.2.74" 1703 | source = "registry+https://github.com/rust-lang/crates.io-index" 1704 | checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" 1705 | dependencies = [ 1706 | "cfg-if", 1707 | "wasm-bindgen-macro", 1708 | ] 1709 | 1710 | [[package]] 1711 | name = "wasm-bindgen-backend" 1712 | version = "0.2.74" 1713 | source = "registry+https://github.com/rust-lang/crates.io-index" 1714 | checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" 1715 | dependencies = [ 1716 | "bumpalo", 1717 | "lazy_static", 1718 | "log", 1719 | "proc-macro2", 1720 | "quote", 1721 | "syn", 1722 | "wasm-bindgen-shared", 1723 | ] 1724 | 1725 | [[package]] 1726 | name = "wasm-bindgen-macro" 1727 | version = "0.2.74" 1728 | source = "registry+https://github.com/rust-lang/crates.io-index" 1729 | checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" 1730 | dependencies = [ 1731 | "quote", 1732 | "wasm-bindgen-macro-support", 1733 | ] 1734 | 1735 | [[package]] 1736 | name = "wasm-bindgen-macro-support" 1737 | version = "0.2.74" 1738 | source = "registry+https://github.com/rust-lang/crates.io-index" 1739 | checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" 1740 | dependencies = [ 1741 | "proc-macro2", 1742 | "quote", 1743 | "syn", 1744 | "wasm-bindgen-backend", 1745 | "wasm-bindgen-shared", 1746 | ] 1747 | 1748 | [[package]] 1749 | name = "wasm-bindgen-shared" 1750 | version = "0.2.74" 1751 | source = "registry+https://github.com/rust-lang/crates.io-index" 1752 | checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" 1753 | 1754 | [[package]] 1755 | name = "web-sys" 1756 | version = "0.3.51" 1757 | source = "registry+https://github.com/rust-lang/crates.io-index" 1758 | checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" 1759 | dependencies = [ 1760 | "js-sys", 1761 | "wasm-bindgen", 1762 | ] 1763 | 1764 | [[package]] 1765 | name = "webpki" 1766 | version = "0.22.0" 1767 | source = "registry+https://github.com/rust-lang/crates.io-index" 1768 | checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" 1769 | dependencies = [ 1770 | "ring", 1771 | "untrusted", 1772 | ] 1773 | 1774 | [[package]] 1775 | name = "webpki-roots" 1776 | version = "0.22.4" 1777 | source = "registry+https://github.com/rust-lang/crates.io-index" 1778 | checksum = "f1c760f0d366a6c24a02ed7816e23e691f5d92291f94d15e836006fd11b04daf" 1779 | dependencies = [ 1780 | "webpki", 1781 | ] 1782 | 1783 | [[package]] 1784 | name = "which" 1785 | version = "4.2.2" 1786 | source = "registry+https://github.com/rust-lang/crates.io-index" 1787 | checksum = "ea187a8ef279bc014ec368c27a920da2024d2a711109bfbe3440585d5cf27ad9" 1788 | dependencies = [ 1789 | "either", 1790 | "lazy_static", 1791 | "libc", 1792 | ] 1793 | 1794 | [[package]] 1795 | name = "winapi" 1796 | version = "0.3.9" 1797 | source = "registry+https://github.com/rust-lang/crates.io-index" 1798 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1799 | dependencies = [ 1800 | "winapi-i686-pc-windows-gnu", 1801 | "winapi-x86_64-pc-windows-gnu", 1802 | ] 1803 | 1804 | [[package]] 1805 | name = "winapi-i686-pc-windows-gnu" 1806 | version = "0.4.0" 1807 | source = "registry+https://github.com/rust-lang/crates.io-index" 1808 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1809 | 1810 | [[package]] 1811 | name = "winapi-util" 1812 | version = "0.1.5" 1813 | source = "registry+https://github.com/rust-lang/crates.io-index" 1814 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1815 | dependencies = [ 1816 | "winapi", 1817 | ] 1818 | 1819 | [[package]] 1820 | name = "winapi-x86_64-pc-windows-gnu" 1821 | version = "0.4.0" 1822 | source = "registry+https://github.com/rust-lang/crates.io-index" 1823 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1824 | 1825 | [[package]] 1826 | name = "windows-sys" 1827 | version = "0.36.1" 1828 | source = "registry+https://github.com/rust-lang/crates.io-index" 1829 | checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" 1830 | dependencies = [ 1831 | "windows_aarch64_msvc 0.36.1", 1832 | "windows_i686_gnu 0.36.1", 1833 | "windows_i686_msvc 0.36.1", 1834 | "windows_x86_64_gnu 0.36.1", 1835 | "windows_x86_64_msvc 0.36.1", 1836 | ] 1837 | 1838 | [[package]] 1839 | name = "windows-sys" 1840 | version = "0.42.0" 1841 | source = "registry+https://github.com/rust-lang/crates.io-index" 1842 | checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" 1843 | dependencies = [ 1844 | "windows_aarch64_gnullvm", 1845 | "windows_aarch64_msvc 0.42.1", 1846 | "windows_i686_gnu 0.42.1", 1847 | "windows_i686_msvc 0.42.1", 1848 | "windows_x86_64_gnu 0.42.1", 1849 | "windows_x86_64_gnullvm", 1850 | "windows_x86_64_msvc 0.42.1", 1851 | ] 1852 | 1853 | [[package]] 1854 | name = "windows-sys" 1855 | version = "0.45.0" 1856 | source = "registry+https://github.com/rust-lang/crates.io-index" 1857 | checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" 1858 | dependencies = [ 1859 | "windows-targets", 1860 | ] 1861 | 1862 | [[package]] 1863 | name = "windows-targets" 1864 | version = "0.42.1" 1865 | source = "registry+https://github.com/rust-lang/crates.io-index" 1866 | checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" 1867 | dependencies = [ 1868 | "windows_aarch64_gnullvm", 1869 | "windows_aarch64_msvc 0.42.1", 1870 | "windows_i686_gnu 0.42.1", 1871 | "windows_i686_msvc 0.42.1", 1872 | "windows_x86_64_gnu 0.42.1", 1873 | "windows_x86_64_gnullvm", 1874 | "windows_x86_64_msvc 0.42.1", 1875 | ] 1876 | 1877 | [[package]] 1878 | name = "windows_aarch64_gnullvm" 1879 | version = "0.42.1" 1880 | source = "registry+https://github.com/rust-lang/crates.io-index" 1881 | checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" 1882 | 1883 | [[package]] 1884 | name = "windows_aarch64_msvc" 1885 | version = "0.36.1" 1886 | source = "registry+https://github.com/rust-lang/crates.io-index" 1887 | checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" 1888 | 1889 | [[package]] 1890 | name = "windows_aarch64_msvc" 1891 | version = "0.42.1" 1892 | source = "registry+https://github.com/rust-lang/crates.io-index" 1893 | checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" 1894 | 1895 | [[package]] 1896 | name = "windows_i686_gnu" 1897 | version = "0.36.1" 1898 | source = "registry+https://github.com/rust-lang/crates.io-index" 1899 | checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" 1900 | 1901 | [[package]] 1902 | name = "windows_i686_gnu" 1903 | version = "0.42.1" 1904 | source = "registry+https://github.com/rust-lang/crates.io-index" 1905 | checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" 1906 | 1907 | [[package]] 1908 | name = "windows_i686_msvc" 1909 | version = "0.36.1" 1910 | source = "registry+https://github.com/rust-lang/crates.io-index" 1911 | checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" 1912 | 1913 | [[package]] 1914 | name = "windows_i686_msvc" 1915 | version = "0.42.1" 1916 | source = "registry+https://github.com/rust-lang/crates.io-index" 1917 | checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" 1918 | 1919 | [[package]] 1920 | name = "windows_x86_64_gnu" 1921 | version = "0.36.1" 1922 | source = "registry+https://github.com/rust-lang/crates.io-index" 1923 | checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" 1924 | 1925 | [[package]] 1926 | name = "windows_x86_64_gnu" 1927 | version = "0.42.1" 1928 | source = "registry+https://github.com/rust-lang/crates.io-index" 1929 | checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" 1930 | 1931 | [[package]] 1932 | name = "windows_x86_64_gnullvm" 1933 | version = "0.42.1" 1934 | source = "registry+https://github.com/rust-lang/crates.io-index" 1935 | checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" 1936 | 1937 | [[package]] 1938 | name = "windows_x86_64_msvc" 1939 | version = "0.36.1" 1940 | source = "registry+https://github.com/rust-lang/crates.io-index" 1941 | checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" 1942 | 1943 | [[package]] 1944 | name = "windows_x86_64_msvc" 1945 | version = "0.42.1" 1946 | source = "registry+https://github.com/rust-lang/crates.io-index" 1947 | checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" 1948 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "trojan-rust" 3 | version = "0.7.2" 4 | authors = ["cty123"] 5 | edition = "2021" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | async-trait = "0.1" 11 | byteorder = "1.4" 12 | bytes = "1.4" 13 | clap = "4.1" 14 | env_logger = "0.10" 15 | futures = { version = "0.3", features = ["thread-pool"] } 16 | itertools = "0.10" 17 | log = "0.4" 18 | rustls = { version = "0.20", features = ["dangerous_configuration"] } 19 | serde = { version = "1.0", features = ["derive"] } 20 | serde_json = { version = "1.0" } 21 | sha2 = { version = "0.10" } 22 | tokio = { version = "1.25", features = ["full"] } 23 | tokio-util = { version = "0.7", features = ["full"] } 24 | tokio-stream = { version = "0.1" } 25 | tokio-rustls = "0.23" 26 | tonic = { version = "0.8", features = [ 27 | "transport", 28 | "codegen", 29 | "tls-roots", 30 | "tls-webpki-roots", 31 | "tls", 32 | "prost", 33 | ] } 34 | prost = "0.11" 35 | uninit = "0.5" 36 | webpki-roots = "0.22" 37 | rustls-pemfile = "1.0" 38 | mockall = "0.11" 39 | lazy_static = "1.4" 40 | quinn = "0.9" 41 | prost-build = "0.11" 42 | once_cell = "1.17" 43 | constant_time_eq = "0.2" 44 | 45 | [build-dependencies] 46 | tonic-build = { version = "0.8" } 47 | 48 | [lib] 49 | name = "trojan_rust" 50 | path = "src/lib.rs" 51 | 52 | [[bin]] 53 | name = "trojan-rust" 54 | path = "src/main.rs" 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 cty123 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # trojan-rust 2 | 3 | ![Build](https://github.com/cty123/TrojanRust/actions/workflows/build.yml/badge.svg) ![Stage](https://img.shields.io/badge/beta-blue.svg) 4 | 5 | [中文zh-CN](https://github.com/cty123/TrojanRust/blob/main/README.zh-CN.md) 6 | 7 | Trojan-rust is a rust implementation for [Trojan protocol](https://trojan-gfw.github.io/trojan/protocol.html) that is targeted to circumvent [GFW](https://en.wikipedia.org/wiki/Great_Firewall). This implementation focus on performance and stability above everything else. 8 | 9 | # Why trojan-rust 10 | 11 | * Depends on [tokio-rs](https://github.com/tokio-rs/tokio) to achieve high performance async io. Tokio io provides better async IO performance by using lightweight threads that is somewhat similar to the runtime environment of Golang. 12 | 13 | * Uses [rustls](https://github.com/ctz/rustls) to handle TLS protocol. rustls is an implemention written in native rust, and is considered to be more secure compared and performant compared to Openssl implementation. 14 | 15 | * Performance focused. This implementation only aims at a few mainstream proxy protocols like [Trojan protocol](https://trojan-gfw.github.io/trojan/protocol.html), so that we have more capacity to improve the performance and bugfixes rather than keep adding useless features. 16 | 17 | * Easy to use/configure. Make this project beginner friendly, minimize the amount of configurations one needs to write. 18 | 19 | # How to compile 20 | 21 | Currently there is no existing binary file that you can just download and use, and it is recommanded to compile and build yourself. To do so, first you need to set up the Rust environment, by installing through here https://www.rust-lang.org/. Once you have rust installed, you can simply go to command line and run, 22 | 23 | cargo build --release 24 | 25 | and it should generate a binary program under ./target/release/trojan-rust. 26 | 27 | Alternatively, you can also run it directly through, 28 | 29 | cargo run --release 30 | 31 | To enable logs, on MacOs or Linux, run, 32 | 33 | RUST_LOG=info cargo run --release 34 | 35 | On windows powershell, run, 36 | 37 | $Env:RUST_LOG = "info" 38 | cargo run --release 39 | 40 | # Examples 41 | 42 | 43 | 44 | ## Create Certificate 45 | Quick short script for your convenience, 46 | 47 | openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes 48 | 49 | ## Sample for Trojan 50 | ### Server config 51 | ```json 52 | { 53 | "inbound": { 54 | "protocol": "TROJAN", 55 | "address": "0.0.0.0", 56 | "secret": "123123", 57 | "port": 8081, 58 | "mode": "TCP", 59 | "tls": { 60 | "cert_path": "./cert.pem", 61 | "key_path": "./key.pem" 62 | } 63 | }, 64 | "outbound": { 65 | "protocol": "DIRECT" 66 | } 67 | } 68 | ``` 69 | 70 | ### Client config 71 | ```json 72 | { 73 | "inbound": { 74 | "protocol": "SOCKS", 75 | "address": "0.0.0.0", 76 | "port": 8081 77 | }, 78 | "outbound": { 79 | "protocol": "TROJAN", 80 | "address": "0.0.0.0", 81 | "port": 8082, 82 | "secret": "123123", 83 | "mode": "TCP", 84 | "tls": { 85 | "host_name": "example.com", 86 | "allow_insecure": true 87 | } 88 | } 89 | } 90 | ``` 91 | ### For using GRPC as transport layer 92 | Just add GRPC to transport under inbound or outbound 93 | ```json 94 | "inbound": { 95 | "protocol": "TROJAN", 96 | "address": "0.0.0.0", 97 | "secret": "123123", 98 | "port": 8081, 99 | "tls": { 100 | "cert_path": "./cert.pem", 101 | "key_path": "./key.pem" 102 | }, 103 | "mode": "GRPC" 104 | }, 105 | "outbound": { 106 | "protocol": "DIRECT" 107 | } 108 | ``` 109 | 110 | ## Run the program 111 | 112 | ```bash 113 | trojan-rust -h 114 | 115 | Trojan Rust 0.0.1 116 | Anonymous 117 | Trojan Rust is a rust implementation of the trojan protocol to circumvent GFW 118 | 119 | USAGE: 120 | trojan-rust [OPTIONS] 121 | 122 | FLAGS: 123 | -h, --help Prints help information 124 | -V, --version Prints version information 125 | 126 | OPTIONS: 127 | -c, --config Sets the config file, readers ./config/config.json by default 128 | ``` 129 | 130 | Run trojan-rust with specified config file 131 | 132 | trojan-rust --config ./config.json 133 | 134 | 135 | # Roadmap 136 | 137 | ## Beta stage 0.0.1 - 1.0.0(For developers) 138 | - [x] Build up the framework for this project and support basic server side SOCKS5 protocol. 139 | 140 | - [x] Support server side Trojan protocol for handling Trojan traffic. 141 | 142 | - [x] Implement UDP over TCP for Trojan protocol on server side. 143 | 144 | - [x] Implement client side Trojan protocol so that trojan-rust and be used as a Trojan client. - Work in progress. 145 | - [x] Implement client side Trojan protocol with TCP 146 | - [x] Implement client side Trojan protocol with TLS 147 | - [ ] -[Delayed After Beta] Implement client side Trojan protocol with UDP over TCP. 148 | 149 | - [x] Performance profiling and bottleneck resolving. Will also include benchmarks versus other implementations. (Benchmark report coming up soon) 150 | 151 | ## Official release 0.4.0 and above(For general users) 152 | - [ ] Improve client mode performance. 153 | 154 | - [x] Implement gRPC for transporting data 155 | 156 | - [ ] +[Delayed After Beta] Implement client side Trojan protocol with UDP over TCP. 157 | 158 | - [ ] Build the package into kernel module release 159 | 160 | - [ ] Support other protocols, gRPC, websocket etc. 161 | -------------------------------------------------------------------------------- /README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # trojan-rust 2 | 3 | ![Build](https://github.com/cty123/TrojanRust/actions/workflows/build.yml/badge.svg) ![publish](https://github.com/cty123/TrojanRust/actions/workflows/publish.yml/badge.svg) ![Version](https://img.shields.io/github/v/release/cty123/TrojanRust) ![Stage](https://img.shields.io/badge/beta-blue.svg) 4 | 5 | Trojan-rust 是一个[Trojan协议](https://trojan-gfw.github.io/trojan/protocol.html)的Rust实现之一用于绕过[GFW](https://en.wikipedia.org/wiki/Great_Firewall)实现翻墙。这个项目专注于提升性能以及稳定性高于一的同时会尽量保证简洁易用。 6 | 7 | # trojan-rust的优势 8 | 9 | * 使用 [tokio-rs](https://github.com/tokio-rs/tokio) 来实现高性能的异步IO,吃满机器的IO资源。相比直接使用系统线程,Tokio 使用的轻量化线程通过减少系统线程的context switch来提升IO吞吐量,该项机制类似于Golang对于goroutine的处理。 10 | 11 | * 使用 [rustls](https://github.com/ctz/rustls) 来处理TLS协议的通信,相比使用CVE频出Openssl来说更为安全,因为Rust编译器的特性能根除内存泄漏,并且也不需要GC。 12 | 13 | * 项目的初衷就是为了能够最大化单机性能,所以按照计划该项目只会支持几个主流常用的翻墙协议,比如说[Trojan协议](https://trojan-gfw.github.io/trojan/protocol.html)而不是一味的添加新功能。这样能确保开发者能把更多的时间花在性能优化以及bug修复上面。另外会定期对项目做performance profiling以及benchmarking来了解现有代码的瓶颈。 14 | 15 | * 易用性。另外一个项目初衷就是高易用性,项目最小化配置文件的代码量来做到对新人友好,又因为项目本身会比较简洁,所以这点应该不难做到。 16 | 17 | # 编译此项目 18 | 19 | 目前项目还在非常早期阶段,目前的release里面只有Windows,Macos,Linux的64位版本的build。其他平台之后等到CI完全配置好之后会有的。另外由于Rust的缘故,编译的过程其实非常的傻瓜,所以一直会建议下载源代码自行编译。只需要安装Rust的SDK,https://www.rust-lang.org/, 20 | 21 | git clone https://github.com/cty123/TrojanRust.git 22 | cd ./TrojanRust 23 | cargo build --release 24 | 25 | 然后cargo就会自动编译所有不需要其他任何操作,编译好的文件会在 ./target/release/trojan-rust 目录下。 26 | 27 | 此外你也可以直接通过cargo运行, 28 | 29 | cargo run --release 30 | 31 | 如果需要显示log则要在环境变量里把RUST_LOG=info设置上,在Macos和Linux下可以这么做, 32 | 33 | RUST_LOG=info cargo run --release 34 | 35 | 在Windows Powershell下可以这么做, 36 | 37 | $Env:RUST_LOG = "info" 38 | cargo run --release 39 | 40 | 至于编译上的各种优化选项还没有进行过测试,所以暂时没有什么推荐的。 41 | # Examples 42 | 43 | ## Create Certificate 44 | 45 | 要用开启TLS的话需要先生成自签证书,或者用现有注册过的,可以用以下命令来生成, 46 | 47 | openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes 48 | 49 | ### Trojan 服务端配置样板 50 | ```json 51 | { 52 | "inbound": { 53 | "protocol": "TROJAN", 54 | "address": "0.0.0.0", 55 | "secret": "123123", 56 | "port": 8081, 57 | "tls": { 58 | "cert_path": "./cert.pem", 59 | "key_path": "./key.pem" 60 | } 61 | }, 62 | "outbound": { 63 | "protocol": "DIRECT" 64 | } 65 | } 66 | ``` 67 | ### Trojan 客户端配置样板 68 | ```json 69 | { 70 | "inbound": { 71 | "protocol": "SOCKS", 72 | "address": "0.0.0.0", 73 | "port": 8081 74 | }, 75 | "outbound": { 76 | "protocol": "TROJAN", 77 | "address": "0.0.0.0", 78 | "port": 8082, 79 | "secret": "123123", 80 | "tls": { 81 | "host_name": "example.com", 82 | "allow_insecure": true 83 | } 84 | } 85 | } 86 | ``` 87 | 88 | ## 运行程序 89 | 90 | ``` 91 | trojan-rust -h 92 | 93 | Trojan Rust 0.0.1 94 | Anonymous 95 | Trojan Rust is a rust implementation of the trojan protocol to circumvent GFW 96 | 97 | USAGE: 98 | trojan-rust [OPTIONS] 99 | 100 | FLAGS: 101 | -h, --help Prints help information 102 | -V, --version Prints version information 103 | 104 | OPTIONS: 105 | -c, --config Sets the config file, readers ./config/config.json by default 106 | ``` 107 | 108 | Run trojan-rust with specified config file 109 | 110 | trojan-rust --config ./config.json 111 | 112 | 113 | # 项目规划 114 | 115 | ## Beta stage 0.0.1 - 0.4.0 116 | - [x] 搭建项目框架,先有个能用socks5服务端。 117 | 118 | - [x] 支持服务端Trojan协议,目前只支持TCP,UDP over TCP已经有实现,但是性能堪忧,所以暂时不放出(其实源码里面有,但是全部注释掉了),在优化之中。 119 | 120 | - [x] 支持客户端Trojan协议,这样该项目就是端到端可用了,既可以当服务端也可以当客户端。 121 | 122 | - [x] 性能调优,已经测试完,之后会放出详细的性能测试报告 123 | 124 | ## Official release 0.4.0 以上 125 | - [ ] 改善Trojan客户端的性能(根据性能测试得知,此项目的Trojan客户端是最关键的性能拖油瓶) 126 | 127 | - [ ] 实现gRPC来传输数据,降低延迟,提升用户体验 128 | 129 | - [ ] Build the package into kernel module release 130 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | fn main() -> Result<(), Box> { 2 | tonic_build::compile_protos("proto/transport.proto")?; 3 | Ok(()) 4 | } 5 | -------------------------------------------------------------------------------- /config/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFazCCA1OgAwIBAgIULvUEC/jjZt06A3eJUEI3AHvxVU4wDQYJKoZIhvcNAQEL 3 | BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMTA3MjUxNjE3MTlaFw0yMjA3 5 | MjUxNjE3MTlaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw 6 | HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIiMA0GCSqGSIb3DQEB 7 | AQUAA4ICDwAwggIKAoICAQDR5P/2o1VbRkUikL1irvnFE326KDH0JwNk6ziNUBez 8 | Iz3sONgZ90XWGEPob4abvTcfPCv5rLqG8wDzrKkdPtP5gc2szLwtuXGQtimn6LaD 9 | 5MaJe6ri87eGJtiq78Z6TvKnU1rhWHbVmRtZtj8G/amkSIM2fqgz0OzWgJ5Qq2r9 10 | HsZXUKijPWBl5IAy3owdxZ6ejoRg/qilO/Nf7WG9N6a3NyGaLQ05hGRbr31abU84 11 | RsMLcq1yvKMxkIJpdrJIuGlXdUtpx+YYbjGsATrOVRWXVwOy9q3sGTuu7iUvHMrV 12 | +VbX4rhxlUcwmANtiE1aUSjwqelYX5shXDya4Ol0kw3pTxZEpXgh5P4b4zTegDTU 13 | 1fVgdebH4Z6ixVTULOJpn1R4uyLp0M9Y00p585Wl3ziUFUawjLXJH1/zamK62+nz 14 | kYCZeoUD9+DJHuRHPVBNwMLk5QVyIRNq73QAylTR+bbcdF8Q7u5/AZb6Z4QMHkh8 15 | ZAwLZg/XOPnM+9GQK+WhQqpH5rYPk5QpTEPJfxrGgqezqhSpkRB/c6Qh+EnjKpf0 16 | FIOoBXWLqGOOf6+sdj0q+QRXHNw9vrfhztNehhJ1L6AmphQXuW5wuixfpobZE1Sy 17 | bc9P8eq8WCL6w6fEAT4gVAJPebJPcC/7JQpaETtweTKSH3B4ohADJ6rtqEgp2/gu 18 | 5wIDAQABo1MwUTAdBgNVHQ4EFgQUPrR7XoA3BI8mh/CX5rGV1eLoSBQwHwYDVR0j 19 | BBgwFoAUPrR7XoA3BI8mh/CX5rGV1eLoSBQwDwYDVR0TAQH/BAUwAwEB/zANBgkq 20 | hkiG9w0BAQsFAAOCAgEAIIpAriAiG04mPsxRiT4oizS359PuvfGK24HueOR2x3it 21 | b3QzVtd9scWkmSox7OF8H/sugka44kDN4aECTAU0wGBVUha7lfBg292GNVClKpvk 22 | rv27VDA8EKMIApgEEiI8HvDBYKECkk1eH73foDSeFLWVYcZvD/CIk7Mn3Avq5BLF 23 | K+GmHBpld7whkzzsiD/dIHSenpY44yashIZ0lzjHZsyvI6y0xHHcmMEPpxuG80vL 24 | VDL3pYQSBjcyLeh9Pn1npUQ0HiCbukd+aPxlVr0EXqrvFFmK8gK7NJ4r+4U1F6p1 25 | LEUy3wMd486/kjfCeI5qo7QFtS/Zn6VhURRKtuyE+i+DK9MLO8PCeWTLln0zTABs 26 | sNhtkCrfdGK49KDGdq/YPSRt4cFbhkgZxnG68ax9TDUZw0J/E3oBaHu3xT67lG8v 27 | Z1c8easyzeD1kmZrUowt2VjH8giEbBkFPqkENSLvPXUJtEGuckhAZYeQ3qa+Bclp 28 | lM6Kj1DM2Jz0F3G/HssBeMRalNfwSJiQBStmQh70q6+KAUP1izh16/Qf7Np1ZCXh 29 | 5WyzOwZwsh88xgQ93lRpPVF7X3oxyrno0MA6eKfEKOkrb9/dnwfHn/fENZ9f7YCV 30 | A/tLn9WY96nUEzvrFLSvLegsqGYd59j5FP4bGB+G0oXFQHVjnUBvAR8CF59qfLk= 31 | -----END CERTIFICATE----- 32 | -------------------------------------------------------------------------------- /config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "inbound": { 3 | "mode": "QUIC", 4 | "protocol": "TROJAN", 5 | "address": "0.0.0.0", 6 | "port": 8081, 7 | "secret": "123123", 8 | "tls": { 9 | "cert_path": "/home/ctydw/Projects/TrojanRust/config/cert.pem", 10 | "key_path": "/home/ctydw/Projects/TrojanRust/config/key.pem" 11 | } 12 | }, 13 | "outbound": { 14 | "mode": "DIRECT", 15 | "protocol": "DIRECT" 16 | } 17 | } -------------------------------------------------------------------------------- /config/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDR5P/2o1VbRkUi 3 | kL1irvnFE326KDH0JwNk6ziNUBezIz3sONgZ90XWGEPob4abvTcfPCv5rLqG8wDz 4 | rKkdPtP5gc2szLwtuXGQtimn6LaD5MaJe6ri87eGJtiq78Z6TvKnU1rhWHbVmRtZ 5 | tj8G/amkSIM2fqgz0OzWgJ5Qq2r9HsZXUKijPWBl5IAy3owdxZ6ejoRg/qilO/Nf 6 | 7WG9N6a3NyGaLQ05hGRbr31abU84RsMLcq1yvKMxkIJpdrJIuGlXdUtpx+YYbjGs 7 | ATrOVRWXVwOy9q3sGTuu7iUvHMrV+VbX4rhxlUcwmANtiE1aUSjwqelYX5shXDya 8 | 4Ol0kw3pTxZEpXgh5P4b4zTegDTU1fVgdebH4Z6ixVTULOJpn1R4uyLp0M9Y00p5 9 | 85Wl3ziUFUawjLXJH1/zamK62+nzkYCZeoUD9+DJHuRHPVBNwMLk5QVyIRNq73QA 10 | ylTR+bbcdF8Q7u5/AZb6Z4QMHkh8ZAwLZg/XOPnM+9GQK+WhQqpH5rYPk5QpTEPJ 11 | fxrGgqezqhSpkRB/c6Qh+EnjKpf0FIOoBXWLqGOOf6+sdj0q+QRXHNw9vrfhztNe 12 | hhJ1L6AmphQXuW5wuixfpobZE1Sybc9P8eq8WCL6w6fEAT4gVAJPebJPcC/7JQpa 13 | ETtweTKSH3B4ohADJ6rtqEgp2/gu5wIDAQABAoICAQCoNvo4hQBEbVW89PkC3K6K 14 | FiveBJa8UIP03lrtTfIgfC8FYS036ieLBNlvr+nWaNeDberkZ1z6CEiMAaVrLc4e 15 | mbvnMBHkJ0nvqz5JNkUIAVJoHrd+ucosdneQzydnIFxyY2hjT021s8XqqRdsF+Fc 16 | K00VYH6XQGp/Ioc+qZLOF1rn4Vm1sdvO7Ukc+6SZu6Z69kb7oTwxaJGNIKcDSVWQ 17 | YlvIvzBpF0SDAmrZDN+/f16Tjy2Y1buIXoNV2G3bwAeUswlkVnN+wtmGO1oQwLnU 18 | F0V8qRwMKtDdSjh8wHuC7xh9XlNpnDR4qmuc3e74YjEIZ3P+gmjGXLCpg3U2GyC6 19 | lXfgLuARPL/9Mcq49dpN5T6RxZMBJgGcCrB+uORI/fJlrPZCctBib7TxtchoedBl 20 | 3Vdx3roa6ueCEN2obSDs9a05JHRiuWWH2/sAU1heWhkiGGfNAFidk6kLf/KFl3C6 21 | krl7oSTTknsX60GsS/DCMLEf8YHcCUGnuaf0r/BTJtqwl8nlKNPpjlGZBEVkLG/r 22 | UnS1X1T8AfYM+O9Mw1efaXneINcmFiSOq5IyxitRuDWRFCJ/0SQnAwrEOVoG+zBY 23 | 0BPYAQLbIK1KLm7CM4vFZzNyYvP5hLgPXGodJMuHo8Yew7HHeCCBzJ/ejQDbAJcF 24 | cqDEsdxT8YFTWpO8s6IfOQKCAQEA9zhEjQvGk7J1uNeOHMqHrPskBvrwO4KDHwYa 25 | qwETTl3uGLCkX/movhfIMSU0n63ZEEpOlSGpSgtjacYnYMaI0JLQaofR3u7X+iZ+ 26 | KzxGLyCLvP1KLiYyrLK4DuMaFoekESzo5wYMZjcm35b6pDEWSaojq2CWvOVuPcxf 27 | aSa5qLLASx/xVQbQfwY+6Ujx2sZBzEkMUhvkoCcVuihQxHWpEF78BCs3Cf8X6+3R 28 | I74159Pqg0CNF/g8NTz+WNhhPTOSTkgTL3BpwtVl4qUUpP6ZVII6ZSrUXI/52Sda 29 | YB9IJZTwVDWzNlznPKUKvESeg7AxoO2xh39YpVNlSzgLP2R3JQKCAQEA2VleiUug 30 | ezYWJyDn+7fqxkuLg54aQLYQyEWuZwEwj4oFxVwLJs2yseHmZpFNT4tHK1kZzk4j 31 | t8gfIw7T0JBvP9usnnl/cyNjVJS0lVGL5u7CBOA00NMiWvxcm8+9t4zjyUrgN+fN 32 | AmiaPpaPk1L5SZ7IruyoT62tv8qGUMaPt2qulU+k+W2NMloXiKcgffab7rpJrheK 33 | VSSWw29quJTjXTIckWF1UnxSliarhUX8KUP9/VuWMl23c+H+z9z1HNYpRwMq0hDN 34 | KENzbEVj0OUMqQ6mSG2hPL9NsJh0H6hM6dr2UO+yFiMFuoDVNAwz3esyu79XV+8p 35 | ClnjNOZ6qoXGGwKCAQEAoGUJVNmHBNyMNFjmAxgD3t8yCLVWl8WdfMthEgGonPek 36 | OrCgdQRtzqdvYzNYaSFJ65/KTEeCpl+tKDs/4THuIiSew/9K0vDzf4Dp6vM4NetP 37 | vlY3gqmVxyZM9JuzmVp3wAz7Lqhl8wtlVTuHcm/GnhSc/9uy8OuKEt4+KOLPIfyX 38 | diZLVXcOXtuWhZSmFeL7XacnLz6Znq69EgKGJHCSN+TobWv8UagHxTKOtjn6j2aW 39 | iIGhCSQtK/1eqBX7QNHs4o0wpV6xv47kg5/HPSVSJdo4ONsI3OUsnQ9DO4oQLRHs 40 | uzX8CBFOlbIevPdoQNhX2qlcvSSKRvlMhvjdk0uDYQKCAQBK7Z1ClZjIgRfX+2ce 41 | BwRfwfFhuWJxhpJ4iFUcW3OjOEDPDfEmOmltG2RzXYoabvhlAD1+Or/jfpEFY/ov 42 | f2tqx09V/qqqRbvbj7xFcaxaO1sVlTl/+Bly5mcCq/ZKTb7FSRPNEhNBzk/GXd83 43 | ObaI471pWFqZxVUNtkLhm+I1qAhxnthPyK2Rey97w6nW4upUvVHjO2hL++Yhj8pp 44 | waI0Ia9piwCIJCXQb5pXwo2wqCjqa+V2jYpN384ZWKIDg+0M9xGPA43GYKJs4+sK 45 | O7xGl69JbsnUCFs/Ev+NXlbNk9ewhUGOkhpKdlmJrN9AlPGTed4hiWhLkjtapE4L 46 | dTybAoIBAQCWMupKt2ZUqEd50mJRKxhv4m+BxOL/i689E1SYG80W4Y3beFa9QG6u 47 | dS0v86+mWS3ntFKaQ7jLKoo4XvgOJCJnsl0La4k0IJbKgJ5pQDO+XwkiWvkbV8BW 48 | oDEJBE/JHqeJd2KUprRiVAEDQz+BV5uHcz3Fwx8AhQCmaHLy2CcZOizrfQumKy0p 49 | GGDwqyrnRYUpRyQcLd/yMOct25YKqrm+2o8yfd3FwPvPJH8HdGAi9MdgoxYqz84r 50 | 9smWLAbOc+jfDBvlKGzePLTvLi6uA2fGziNJ0GszHxaQPGzRwBLFxLwuZ8gT+nsi 51 | Hls3deDQ2FA2CR8UJsblq+L92OpqY3qk 52 | -----END PRIVATE KEY----- 53 | -------------------------------------------------------------------------------- /example/grpc_trojan/client.json: -------------------------------------------------------------------------------- 1 | { 2 | "inbound": { 3 | "mode": "TCP", 4 | "protocol": "SOCKS", 5 | "address": "0.0.0.0", 6 | "port": 8080 7 | }, 8 | "outbound": { 9 | "mode": "GRPC", 10 | "protocol": "TROJAN", 11 | "address": "0.0.0.0", 12 | "port": 8081, 13 | "secret": "123123" 14 | }, 15 | "tls": { 16 | "host_name": "example.com" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /example/grpc_trojan/server.json: -------------------------------------------------------------------------------- 1 | { 2 | "inbound": { 3 | "mode": "GRPC", 4 | "protocol": "TROJAN", 5 | "address": "0.0.0.0", 6 | "port": 8081, 7 | "secret": "123123", 8 | "tls": { 9 | "cert_path": "/home/cty123/Projects/TrojanRust/config/cert.pem", 10 | "key_path": "/home/cty123/Projects/TrojanRust/config/key.pem" 11 | } 12 | }, 13 | "outbound": { 14 | "mode": "DIRECT", 15 | "protocol": "DIRECT" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /example/quic_trojan/client.json: -------------------------------------------------------------------------------- 1 | { 2 | "inbound": { 3 | "mode": "TCP", 4 | "protocol": "SOCKS", 5 | "address": "0.0.0.0", 6 | "port": 8080 7 | }, 8 | "outbound": { 9 | "mode": "QUIC", 10 | "protocol": "TROJAN", 11 | "address": "0.0.0.0", 12 | "port": 8081, 13 | "secret": "123123", 14 | "tls": { 15 | "allow_insecure": false, 16 | "host_name": "example.com" 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /example/quic_trojan/server.json: -------------------------------------------------------------------------------- 1 | { 2 | "inbound": { 3 | "mode": "QUIC", 4 | "protocol": "TROJAN", 5 | "address": "0.0.0.0", 6 | "port": 8081, 7 | "secret": "123123", 8 | "tls": { 9 | "cert_path": "/home/ctydw/Projects/TrojanRust/config/cert.pem", 10 | "key_path": "/home/ctydw/Projects/TrojanRust/config/key.pem" 11 | } 12 | }, 13 | "outbound": { 14 | "mode": "DIRECT", 15 | "protocol": "DIRECT" 16 | } 17 | } -------------------------------------------------------------------------------- /example/tcp_trojan/client.json: -------------------------------------------------------------------------------- 1 | { 2 | "inbound": { 3 | "mode": "TCP", 4 | "protocol": "SOCKS", 5 | "address": "0.0.0.0", 6 | "port": 8080 7 | }, 8 | "outbound": { 9 | "mode": "TCP", 10 | "protocol": "TROJAN", 11 | "address": "0.0.0.0", 12 | "port": 8081, 13 | "secret": "123123" 14 | }, 15 | "tls": { 16 | "host_name": "example.com" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /example/tcp_trojan/server.json: -------------------------------------------------------------------------------- 1 | { 2 | "inbound": { 3 | "mode": "TCP", 4 | "protocol": "TROJAN", 5 | "address": "0.0.0.0", 6 | "port": 8081, 7 | "secret": "123123", 8 | "tls": { 9 | "cert_path": "/home/cty123/Projects/TrojanRust/config/cert.pem", 10 | "key_path": "/home/cty123/Projects/TrojanRust/config/key.pem" 11 | } 12 | }, 13 | "outbound": { 14 | "mode": "DIRECT", 15 | "protocol": "DIRECT" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /proto/transport.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package trojan_rust.transport.grpc; 4 | 5 | service GRPCService { 6 | rpc Tun (stream Hunk) returns (stream Hunk); 7 | rpc TunMulti (stream MultiHunk) returns (stream MultiHunk); 8 | } 9 | 10 | message Hunk { 11 | bytes data = 1; 12 | } 13 | 14 | message MultiHunk { 15 | repeated bytes data = 1; 16 | } 17 | -------------------------------------------------------------------------------- /src/config/base.rs: -------------------------------------------------------------------------------- 1 | use crate::proxy::base::SupportedProtocols; 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive(Serialize, Deserialize)] 6 | pub struct Config { 7 | pub inbound: InboundConfig, 8 | pub outbound: OutboundConfig, 9 | } 10 | 11 | /// Inbound traffic supports the following 3 modes: 12 | /// 13 | /// TCP - Raw TCP byte stream traffic 14 | /// GRPC - GRPC packet stream that contains payload data in the body for proxy purposes 15 | /// QUIC - Application level byte stream that is built on top of QUIC protocol 16 | /// 17 | /// TCP and QUIC are both byte streams from the abstractions of the low level implementation. GRPC on the other hand is 18 | /// packet stream. 19 | #[derive(Serialize, Deserialize, Clone, Debug)] 20 | pub enum InboundMode { 21 | TCP, 22 | GRPC, 23 | QUIC, 24 | } 25 | 26 | /// Outbound traffic supports 4 types of proxy modes: 27 | /// 28 | /// DIRECT: Directly send the data in the proxy request to the requested destination, either via raw TCP or UDP 29 | /// TCP: Forward the proxy traffic to a remote proxy server via raw TCP stream and have it take care of the traffic handling 30 | /// GRPC: Forward the proxy traffic to a remote proxy server via GRPC packet stream 31 | /// QUIC: Forward the proxy traffic to a remote proxy server via QUIC stream 32 | #[derive(Serialize, Deserialize, Clone)] 33 | pub enum OutboundMode { 34 | DIRECT, 35 | TCP, 36 | GRPC, 37 | QUIC, 38 | } 39 | 40 | #[derive(Serialize, Deserialize, Clone)] 41 | pub struct InboundConfig { 42 | pub mode: InboundMode, 43 | pub protocol: SupportedProtocols, 44 | pub address: String, 45 | pub port: u16, 46 | pub secret: Option, 47 | pub tls: Option, 48 | } 49 | 50 | #[derive(Serialize, Deserialize, Clone)] 51 | pub struct OutboundConfig { 52 | pub mode: OutboundMode, 53 | pub protocol: SupportedProtocols, 54 | pub address: Option, 55 | pub port: Option, 56 | pub secret: Option, 57 | pub tls: Option, 58 | } 59 | 60 | #[derive(Serialize, Deserialize, Clone)] 61 | pub struct InboundTlsConfig { 62 | pub cert_path: String, 63 | pub key_path: String, 64 | } 65 | 66 | #[derive(Serialize, Deserialize, Clone)] 67 | pub struct OutboundTlsConfig { 68 | pub host_name: String, 69 | pub allow_insecure: bool, 70 | } 71 | -------------------------------------------------------------------------------- /src/config/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod base; 2 | pub mod parser; 3 | pub mod tls; 4 | -------------------------------------------------------------------------------- /src/config/parser.rs: -------------------------------------------------------------------------------- 1 | use crate::config::base::Config; 2 | 3 | use std::fs::File; 4 | use std::io::{BufReader, Error, ErrorKind, Result}; 5 | 6 | /// Read and parse the json file located at path, will attempt to deserialize and throw error if the 7 | /// format is invalid. 8 | pub fn read_config(path: &'static str) -> Result { 9 | let reader = match File::open(path) { 10 | Ok(file) => BufReader::new(file), 11 | Err(e) => return Err(Error::new(ErrorKind::InvalidData, e)), 12 | }; 13 | 14 | return match serde_json::from_reader(reader) { 15 | Ok(config) => Ok(config), 16 | Err(e) => return Err(Error::new(ErrorKind::InvalidData, e)), 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /src/config/tls.rs: -------------------------------------------------------------------------------- 1 | use log::error; 2 | 3 | use std::fs::File; 4 | use std::io::{BufReader, ErrorKind}; 5 | use std::sync::Arc; 6 | use std::time::SystemTime; 7 | 8 | use rustls::client::{ServerCertVerified, ServerCertVerifier, ServerName}; 9 | use rustls::Error; 10 | use rustls::RootCertStore; 11 | use rustls::{Certificate, ClientConfig, PrivateKey, ServerConfig}; 12 | use rustls_pemfile::{read_one, Item}; 13 | 14 | use crate::config::base::{InboundTlsConfig, OutboundTlsConfig}; 15 | 16 | /// Stub Certificate verifier that skips certificate verification. It is used when the user 17 | /// explicitly allows insecure TLS connection in configuration file, by setting 18 | /// 19 | /// ```json 20 | /// { 21 | /// ..., 22 | /// outbound: { 23 | /// ..., 24 | /// tls: { 25 | /// ..., 26 | /// allow_insecure: true 27 | /// } 28 | /// } 29 | /// } 30 | /// ``` 31 | /// 32 | /// The option is not recommended for production level services, but could be handy in testing stages. 33 | pub struct NoCertificateVerification {} 34 | 35 | impl ServerCertVerifier for NoCertificateVerification { 36 | fn verify_server_cert( 37 | &self, 38 | _end_entity: &Certificate, 39 | _intermediates: &[Certificate], 40 | _server_name: &ServerName, 41 | _scts: &mut dyn Iterator, 42 | _ocsp_response: &[u8], 43 | _now: SystemTime, 44 | ) -> Result { 45 | Ok(ServerCertVerified::assertion()) 46 | } 47 | } 48 | 49 | /// Create ClientConfig for rustls based on the configurations in the config.json file. The function 50 | /// will read the tls configuration under outbound, 51 | /// 52 | /// ```json 53 | /// { 54 | /// outbound: { 55 | /// tls: { 56 | /// # Configurations here 57 | /// } 58 | /// } 59 | /// } 60 | /// ``` 61 | pub fn make_client_config(config: &OutboundTlsConfig) -> ClientConfig { 62 | if config.allow_insecure { 63 | let mut config = ClientConfig::builder() 64 | .with_safe_defaults() 65 | .with_root_certificates(RootCertStore::empty()) 66 | .with_no_client_auth(); 67 | 68 | config 69 | .dangerous() 70 | .set_certificate_verifier(Arc::new(NoCertificateVerification {})); 71 | 72 | config 73 | } else { 74 | let mut root_store = RootCertStore::empty(); 75 | root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| { 76 | rustls::OwnedTrustAnchor::from_subject_spki_name_constraints( 77 | ta.subject, 78 | ta.spki, 79 | ta.name_constraints, 80 | ) 81 | })); 82 | 83 | let config = ClientConfig::builder() 84 | .with_safe_defaults() 85 | .with_root_certificates(root_store) 86 | .with_no_client_auth(); 87 | 88 | config 89 | } 90 | } 91 | 92 | /// Create ServerConfig for rustls based on the configurations in the config.json file. The function 93 | /// will read the tls configuration under inbound, 94 | /// 95 | /// ```json 96 | /// { 97 | /// inbound: { 98 | /// tls: { 99 | /// # Configurations here 100 | /// } 101 | /// } 102 | /// } 103 | /// ``` 104 | pub fn make_server_config(config: &InboundTlsConfig) -> Option { 105 | let certificates = match load_certs(&config.cert_path) { 106 | Ok(certs) => certs, 107 | Err(_) => return None, 108 | }; 109 | 110 | let key = match load_private_key(&config.key_path) { 111 | Ok(key) => key, 112 | Err(_) => return None, 113 | }; 114 | 115 | let cfg = ServerConfig::builder() 116 | .with_safe_defaults() 117 | .with_no_client_auth() 118 | .with_single_cert(certificates, key) 119 | .expect("bad certificate/key"); 120 | 121 | Some(cfg) 122 | } 123 | 124 | fn load_certs(path: &str) -> std::io::Result> { 125 | let mut reader = match File::open(path) { 126 | Ok(file) => BufReader::new(file), 127 | Err(e) => { 128 | error!("Failed to load tls certificate file, {}", e); 129 | return Err(e); 130 | } 131 | }; 132 | 133 | return match rustls_pemfile::certs(&mut reader) { 134 | Ok(certs) => Ok(certs.into_iter().map(|bytes| Certificate(bytes)).collect()), 135 | Err(_) => Err(std::io::Error::new( 136 | ErrorKind::InvalidData, 137 | "failed to load tls certificate", 138 | )), 139 | }; 140 | } 141 | 142 | fn load_private_key(path: &str) -> std::io::Result { 143 | let mut reader = match File::open(path) { 144 | Ok(file) => BufReader::new(file), 145 | Err(e) => return Err(e), 146 | }; 147 | 148 | return match read_one(&mut reader) { 149 | Ok(opt) => match opt { 150 | Some(item) => match item { 151 | Item::RSAKey(key) => Ok(rustls::PrivateKey(key)), 152 | Item::PKCS8Key(key) => Ok(rustls::PrivateKey(key)), 153 | Item::ECKey(key) => Ok(rustls::PrivateKey(key)), 154 | _ => Err(std::io::Error::new( 155 | ErrorKind::InvalidInput, 156 | "Found cert in ssl key file", 157 | )), 158 | }, 159 | None => Err(std::io::Error::new( 160 | ErrorKind::InvalidInput, 161 | "Failed to find any private key in file", 162 | )), 163 | }, 164 | Err(e) => Err(e), 165 | }; 166 | } 167 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod config; 2 | pub mod protocol; 3 | pub mod proxy; 4 | pub mod transport; 5 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::Arg; 2 | use clap::{ArgMatches, Command}; 3 | use lazy_static::lazy_static; 4 | use log::info; 5 | use std::io::Result; 6 | use trojan_rust::config::base::{InboundConfig, InboundMode, OutboundConfig}; 7 | use trojan_rust::config::parser::read_config; 8 | use trojan_rust::proxy::grpc; 9 | use trojan_rust::proxy::quic; 10 | use trojan_rust::proxy::tcp; 11 | 12 | lazy_static! { 13 | static ref ARGS: ArgMatches = Command::new("Trojan Rust") 14 | .version("0.7.2") 15 | .author("cty123") 16 | .about("Trojan Rust is a rust implementation of the trojan protocol to circumvent GFW") 17 | .arg( 18 | Arg::new("config") 19 | .short('c') 20 | .long("config") 21 | .value_name("FILE") 22 | .help("Sets the config file, read ./config/config.json by default") 23 | .required(false) 24 | ) 25 | .get_matches(); 26 | static ref CONFIG_PATH: String = ARGS 27 | .get_one::("config") 28 | .unwrap_or(&"./config/config.json".to_string()) 29 | .to_string(); 30 | static ref CONFIG: (InboundConfig, OutboundConfig) = { 31 | let config = read_config(&CONFIG_PATH).expect("Error parsing the config file"); 32 | (config.inbound, config.outbound) 33 | }; 34 | } 35 | 36 | #[tokio::main] 37 | async fn main() -> Result<()> { 38 | env_logger::init(); 39 | 40 | info!( 41 | "Reading trojan configuration file from {}", 42 | CONFIG_PATH.to_string() 43 | ); 44 | 45 | info!( 46 | "Starting {:?} server to accept inbound traffic", 47 | CONFIG.0.mode 48 | ); 49 | 50 | // TODO: Support more types of server, like UDP 51 | match CONFIG.0.mode { 52 | InboundMode::TCP => { 53 | tcp::server::start(&CONFIG.0, &CONFIG.1).await?; 54 | } 55 | InboundMode::GRPC => { 56 | grpc::server::start(&CONFIG.0, &CONFIG.1).await?; 57 | } 58 | InboundMode::QUIC => { 59 | quic::server::start(&CONFIG.0, &CONFIG.1).await?; 60 | } 61 | } 62 | 63 | Ok(()) 64 | } 65 | -------------------------------------------------------------------------------- /src/protocol/common/addr.rs: -------------------------------------------------------------------------------- 1 | use bytes::Bytes; 2 | use std::fmt::{self}; 3 | use std::io::{Error, ErrorKind}; 4 | use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; 5 | 6 | pub const IPV4_SIZE: usize = 4; 7 | pub const IPV6_SIZE: usize = 16; 8 | 9 | /// DomainName is a vector of bytes whose length can go up to 255. This is not the most efficient way of 10 | /// storing DomainName, should consider using stack memory to avoid calling malloc and free repeatedly. 11 | /// This may be altered after we perform a thourough benchmark to determine the tradeoffs between slice and Vec. 12 | pub struct DomainName { 13 | inner: Bytes, 14 | } 15 | 16 | /// Wrap around std::net::IpAddr to create a parent enum for all kinds of IpAddresses used in Trojan. 17 | /// Apart from the basic IPv4 and IPv6 types provided by standard library, DomainName is commonly used 18 | /// for proxy protocols, so we need to extend that. 19 | pub enum IpAddress { 20 | IpAddr(IpAddr), 21 | Domain(DomainName), 22 | } 23 | 24 | /// Wrapper class that contains the destination ip and port of the proxy request. 25 | /// The struct is capable of converting to SocketAddr class that can be used to establish an outbound connection. 26 | pub struct IpAddrPort { 27 | pub ip: IpAddress, 28 | pub port: u16, 29 | } 30 | 31 | /// Expose constructor for IpAddrPort for the easy of initialization 32 | impl IpAddrPort { 33 | #[inline] 34 | pub fn new(ip: IpAddress, port: u16) -> Self { 35 | Self { ip, port } 36 | } 37 | } 38 | 39 | /// IpAddrPort is essentially SocketAddr, except we allow DomainName which needs to be resolved by DNS query. 40 | /// TODO: Come up with a better way of resolving DNS names by adding builtin DNS cache in memory or make pluggable modules. 41 | impl Into> for IpAddrPort { 42 | fn into(self) -> std::io::Result { 43 | match self.ip { 44 | IpAddress::IpAddr(addr) => Ok(SocketAddr::new(addr, self.port)), 45 | IpAddress::Domain(domain) => { 46 | let name = match std::str::from_utf8(&domain.inner) { 47 | Ok(name) => name, 48 | Err(_) => { 49 | return Err(Error::new( 50 | ErrorKind::InvalidData, 51 | "request domain name contains non-utf8 character", 52 | )) 53 | } 54 | }; 55 | 56 | // to_socket_addrs function implicitly runs a DNS query to resolve the DomainName 57 | let addrs = match (name, self.port).to_socket_addrs() { 58 | Ok(a) => a, 59 | Err(_) => { 60 | return Err(Error::new( 61 | ErrorKind::AddrNotAvailable, 62 | "Failed to resolve DNS name", 63 | )); 64 | } 65 | }; 66 | 67 | return match addrs.into_iter().nth(0) { 68 | Some(n) => Ok(n), 69 | None => Err(Error::new( 70 | ErrorKind::AddrNotAvailable, 71 | "Failed to resolve DNS name", 72 | )), 73 | }; 74 | } 75 | } 76 | } 77 | } 78 | 79 | impl IpAddress { 80 | #[inline] 81 | pub fn len(&self) -> usize { 82 | match self { 83 | IpAddress::IpAddr(IpAddr::V4(_)) => IPV4_SIZE, 84 | IpAddress::IpAddr(IpAddr::V6(_)) => IPV6_SIZE, 85 | IpAddress::Domain(domain) => domain.inner.len(), 86 | } 87 | } 88 | 89 | #[inline] 90 | pub fn from_u32(addr: u32) -> IpAddress { 91 | IpAddress::IpAddr(IpAddr::V4(Ipv4Addr::from(addr))) 92 | } 93 | 94 | #[inline] 95 | pub fn from_u128(addr: u128) -> IpAddress { 96 | IpAddress::IpAddr(IpAddr::V6(Ipv6Addr::from(addr))) 97 | } 98 | 99 | #[inline] 100 | pub fn from_bytes(addr: Bytes) -> IpAddress { 101 | IpAddress::Domain(DomainName { inner: addr }) 102 | } 103 | } 104 | 105 | impl DomainName { 106 | pub fn as_bytes(&self) -> &[u8] { 107 | &self.inner 108 | } 109 | } 110 | 111 | impl fmt::Display for DomainName { 112 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 113 | write!(fmt, "{}", String::from_utf8_lossy(&self.inner)) 114 | } 115 | } 116 | 117 | impl fmt::Display for IpAddress { 118 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 119 | match self { 120 | IpAddress::IpAddr(IpAddr::V4(ip)) => ip.fmt(fmt), 121 | IpAddress::IpAddr(IpAddr::V6(ip)) => ip.fmt(fmt), 122 | IpAddress::Domain(domain) => domain.fmt(fmt), 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/protocol/common/atype.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self}; 2 | use std::io::{Error, ErrorKind, Result}; 3 | 4 | const ATYPE_IPV4: u8 = 1; 5 | const ATYPE_DOMAIN_NAME: u8 = 3; 6 | const ATYPE_IPV6: u8 = 4; 7 | 8 | #[repr(u8)] 9 | #[derive(Copy, Clone)] 10 | pub enum Atype { 11 | IPv4 = 1, 12 | IPv6 = 4, 13 | DomainName = 3, 14 | } 15 | 16 | impl From for Atype { 17 | fn from(atype: u8) -> Self { 18 | match atype { 19 | ATYPE_IPV4 => Atype::IPv4, 20 | ATYPE_IPV6 => Atype::IPv6, 21 | ATYPE_DOMAIN_NAME => Atype::DomainName, 22 | _ => Atype::IPv4, 23 | } 24 | } 25 | } 26 | 27 | impl Atype { 28 | #[inline] 29 | pub fn from(atype: u8) -> Result { 30 | match atype { 31 | ATYPE_IPV4 => Ok(Atype::IPv4), 32 | ATYPE_IPV6 => Ok(Atype::IPv6), 33 | ATYPE_DOMAIN_NAME => Ok(Atype::DomainName), 34 | _ => Err(Error::new( 35 | ErrorKind::Unsupported, 36 | "Unsupported address type", 37 | )), 38 | } 39 | } 40 | 41 | #[inline] 42 | pub fn to_byte(&self) -> u8 { 43 | match self { 44 | Atype::IPv4 => ATYPE_IPV4, 45 | Atype::IPv6 => ATYPE_IPV6, 46 | Atype::DomainName => ATYPE_DOMAIN_NAME, 47 | } 48 | } 49 | } 50 | 51 | impl fmt::Display for Atype { 52 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 53 | match self { 54 | Atype::IPv4 => write!(fmt, "IPv4"), 55 | Atype::IPv6 => write!(fmt, "IPv6"), 56 | Atype::DomainName => write!(fmt, "DomainName"), 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/protocol/common/command.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self}; 2 | use std::io::{Error, ErrorKind, Result}; 3 | 4 | const CONNECT: u8 = 1; 5 | const BIND: u8 = 2; 6 | const UDP: u8 = 3; 7 | 8 | #[repr(u8)] 9 | #[derive(Copy, Clone)] 10 | pub enum Command { 11 | Connect = 1, 12 | Bind = 2, 13 | Udp = 3, 14 | } 15 | 16 | impl Command { 17 | #[inline] 18 | pub fn from(command: u8) -> Result { 19 | return match command { 20 | CONNECT => Ok(Command::Connect), 21 | BIND => Ok(Command::Bind), 22 | UDP => Ok(Command::Udp), 23 | _ => Err(Error::new( 24 | ErrorKind::Unsupported, 25 | "Unsupported command request", 26 | )), 27 | }; 28 | } 29 | } 30 | 31 | impl fmt::Display for Command { 32 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 33 | match self { 34 | Command::Connect => write!(fmt, "Connect"), 35 | Command::Bind => write!(fmt, "Bind"), 36 | Command::Udp => write!(fmt, "Udp"), 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/protocol/common/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod addr; 2 | pub mod atype; 3 | pub mod command; 4 | pub mod stream; 5 | pub mod request; 6 | -------------------------------------------------------------------------------- /src/protocol/common/request.rs: -------------------------------------------------------------------------------- 1 | use crate::protocol::common::addr::IpAddrPort; 2 | use crate::protocol::common::atype::Atype; 3 | use crate::protocol::common::command::Command; 4 | use crate::{protocol::common::addr::IpAddress, proxy::base::SupportedProtocols}; 5 | 6 | use serde::{Deserialize, Serialize}; 7 | 8 | #[derive(Debug, Copy, Clone, Serialize, Deserialize)] 9 | pub enum TransportProtocol { 10 | TCP, 11 | UDP, 12 | } 13 | 14 | pub struct InboundRequest { 15 | pub atype: Atype, 16 | pub addr_port: IpAddrPort, 17 | pub command: Command, 18 | pub transport_protocol: TransportProtocol, 19 | pub proxy_protocol: SupportedProtocols, 20 | } 21 | 22 | impl InboundRequest { 23 | #[inline] 24 | pub fn new( 25 | atype: Atype, 26 | addr: IpAddress, 27 | command: Command, 28 | port: u16, 29 | transport_protocol: TransportProtocol, 30 | proxy_protocol: SupportedProtocols, 31 | ) -> Self { 32 | Self { 33 | atype, 34 | addr_port: IpAddrPort::new(addr, port), 35 | command, 36 | transport_protocol, 37 | proxy_protocol, 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/protocol/common/stream.rs: -------------------------------------------------------------------------------- 1 | use std::pin::Pin; 2 | use std::task::{Context, Poll}; 3 | use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; 4 | 5 | pub enum StandardTcpStream { 6 | Plain(T), 7 | RustlsServer(tokio_rustls::server::TlsStream), 8 | RustlsClient(tokio_rustls::client::TlsStream), 9 | } 10 | 11 | impl AsyncRead for StandardTcpStream { 12 | #[inline] 13 | fn poll_read( 14 | self: Pin<&mut Self>, 15 | cx: &mut Context<'_>, 16 | buf: &mut ReadBuf<'_>, 17 | ) -> Poll> { 18 | match self.get_mut() { 19 | StandardTcpStream::Plain(ref mut s) => Pin::new(s).poll_read(cx, buf), 20 | StandardTcpStream::RustlsServer(s) => Pin::new(s).poll_read(cx, buf), 21 | StandardTcpStream::RustlsClient(s) => Pin::new(s).poll_read(cx, buf), 22 | } 23 | } 24 | } 25 | 26 | impl AsyncWrite for StandardTcpStream { 27 | #[inline] 28 | fn poll_write( 29 | self: Pin<&mut Self>, 30 | cx: &mut Context<'_>, 31 | buf: &[u8], 32 | ) -> Poll> { 33 | match self.get_mut() { 34 | StandardTcpStream::Plain(ref mut s) => Pin::new(s).poll_write(cx, buf), 35 | StandardTcpStream::RustlsServer(ref mut s) => Pin::new(s).poll_write(cx, buf), 36 | StandardTcpStream::RustlsClient(ref mut s) => Pin::new(s).poll_write(cx, buf), 37 | } 38 | } 39 | 40 | #[inline] 41 | fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 42 | match self.get_mut() { 43 | StandardTcpStream::Plain(ref mut s) => Pin::new(s).poll_flush(cx), 44 | StandardTcpStream::RustlsServer(ref mut s) => Pin::new(s).poll_flush(cx), 45 | StandardTcpStream::RustlsClient(ref mut s) => Pin::new(s).poll_flush(cx), 46 | } 47 | } 48 | 49 | #[inline] 50 | fn poll_shutdown( 51 | self: Pin<&mut Self>, 52 | cx: &mut Context<'_>, 53 | ) -> Poll> { 54 | match self.get_mut() { 55 | StandardTcpStream::Plain(ref mut s) => Pin::new(s).poll_shutdown(cx), 56 | StandardTcpStream::RustlsServer(ref mut s) => Pin::new(s).poll_shutdown(cx), 57 | StandardTcpStream::RustlsClient(ref mut s) => Pin::new(s).poll_shutdown(cx), 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/protocol/direct/outbound.rs: -------------------------------------------------------------------------------- 1 | use std::io::Result; 2 | use std::pin::Pin; 3 | use std::task::{Context, Poll}; 4 | 5 | use tokio::io::{AsyncRead, AsyncWrite, BufReader, ReadBuf}; 6 | 7 | pub struct DirectOutboundStream 8 | where 9 | IO: AsyncRead + AsyncWrite + Unpin, 10 | { 11 | stream: BufReader, 12 | } 13 | 14 | impl OutboundStream for DirectOutboundStream where 15 | IO: AsyncRead + AsyncWrite + Unpin + Send + Sync + 'static 16 | { 17 | } 18 | 19 | impl DirectOutboundStream 20 | where 21 | IO: AsyncRead + AsyncWrite + Unpin + Send + Sync + 'static, 22 | { 23 | pub fn new(stream: IO) -> Box { 24 | return Box::new(DirectOutboundStream { 25 | stream: BufReader::with_capacity(512, stream), 26 | }); 27 | } 28 | } 29 | 30 | impl AsyncRead for DirectOutboundStream 31 | where 32 | IO: AsyncRead + AsyncWrite + Unpin, 33 | { 34 | #[inline] 35 | fn poll_read( 36 | mut self: Pin<&mut Self>, 37 | cx: &mut Context<'_>, 38 | buf: &mut ReadBuf<'_>, 39 | ) -> Poll> { 40 | return Pin::new(&mut self.stream).poll_read(cx, buf); 41 | } 42 | } 43 | 44 | impl AsyncWrite for DirectOutboundStream 45 | where 46 | IO: AsyncRead + AsyncWrite + Unpin, 47 | { 48 | #[inline] 49 | fn poll_write( 50 | mut self: Pin<&mut Self>, 51 | cx: &mut Context<'_>, 52 | buf: &[u8], 53 | ) -> Poll> { 54 | return Pin::new(&mut self.stream).poll_write(cx, buf); 55 | } 56 | 57 | #[inline] 58 | fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 59 | return Pin::new(&mut self.stream).poll_flush(cx); 60 | } 61 | 62 | #[inline] 63 | fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 64 | return Pin::new(&mut self.stream).poll_shutdown(cx); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/protocol/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod socks5; 2 | pub mod common; 3 | pub mod trojan; -------------------------------------------------------------------------------- /src/protocol/socks5/base.rs: -------------------------------------------------------------------------------- 1 | use crate::protocol::common::addr::IpAddress; 2 | use crate::protocol::common::atype::Atype; 3 | use crate::protocol::common::command::Command; 4 | use crate::protocol::common::request::{InboundRequest, TransportProtocol}; 5 | 6 | /// Only implement SOCKS5 protocol 7 | pub const VERSION: u8 = 5; 8 | 9 | /// SOCKS proxy request body 10 | #[allow(dead_code)] 11 | pub struct Request { 12 | version: u8, 13 | command: Command, 14 | rsv: u8, 15 | atype: Atype, 16 | addr: IpAddress, 17 | port: u16, 18 | } 19 | 20 | #[allow(dead_code)] 21 | pub struct ClientHello { 22 | version: u8, 23 | method_size: u8, 24 | // Assume for now that the number of methods is always 1 25 | methods: u8, 26 | } 27 | 28 | #[allow(dead_code)] 29 | pub struct ServerHello { 30 | version: u8, 31 | method: u8, 32 | } 33 | 34 | #[allow(dead_code)] 35 | pub struct RequestAck { 36 | version: u8, 37 | rep: u8, 38 | rsv: u8, 39 | atype: u8, 40 | addr: IpAddress, 41 | port: u16, 42 | } 43 | 44 | impl ServerHello { 45 | #[inline] 46 | pub fn new(method: u8) -> ServerHello { 47 | return ServerHello { 48 | version: VERSION, 49 | method, 50 | }; 51 | } 52 | 53 | #[inline] 54 | pub fn bytes(self) -> [u8; 2] { 55 | return [self.version, self.method] 56 | } 57 | } 58 | 59 | impl RequestAck { 60 | pub fn new(rep: u8, rsv: u8, atype: u8, addr: IpAddress, port: u16) -> RequestAck { 61 | return RequestAck { 62 | version: VERSION, 63 | rep, 64 | rsv, 65 | atype, 66 | addr, 67 | port, 68 | }; 69 | } 70 | } 71 | 72 | impl Request { 73 | pub fn new( 74 | version: u8, 75 | command: Command, 76 | rsv: u8, 77 | atype: Atype, 78 | port: u16, 79 | addr: IpAddress, 80 | ) -> Request { 81 | return Request { 82 | version, 83 | command, 84 | rsv, 85 | atype, 86 | port, 87 | addr, 88 | }; 89 | } 90 | 91 | #[inline] 92 | pub fn dump_request(&self) -> String { 93 | return format!( 94 | "[{} => {}:{}]", 95 | self.command.to_string(), 96 | self.addr, 97 | self.port 98 | ); 99 | } 100 | 101 | #[inline] 102 | pub fn into_request(self) -> InboundRequest { 103 | return match self.command { 104 | Command::Udp => InboundRequest::new( 105 | self.atype, 106 | self.addr, 107 | self.command, 108 | self.port, 109 | TransportProtocol::UDP, 110 | crate::proxy::base::SupportedProtocols::SOCKS, 111 | ), 112 | _ => InboundRequest::new( 113 | self.atype, 114 | self.addr, 115 | self.command, 116 | self.port, 117 | TransportProtocol::TCP, 118 | crate::proxy::base::SupportedProtocols::SOCKS, 119 | ), 120 | }; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/protocol/socks5/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod base; 2 | pub mod parser; 3 | 4 | use self::base::{ServerHello, VERSION}; 5 | 6 | use crate::protocol::common::request::InboundRequest; 7 | use crate::protocol::common::stream::StandardTcpStream; 8 | 9 | use std::io::Result; 10 | use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; 11 | 12 | pub async fn accept( 13 | mut stream: StandardTcpStream, 14 | port: u16, 15 | ) -> Result<(InboundRequest, StandardTcpStream)> { 16 | // Initialize the handshake process to establish socks connection 17 | init_ack(&mut stream).await?; 18 | 19 | // Read socks5 request and convert it to universal request 20 | let request = parser::parse(&mut stream).await?.into_request(); 21 | 22 | // Write back the request port 23 | write_request_ack(&mut stream, port).await?; 24 | 25 | Ok((request, stream)) 26 | } 27 | 28 | async fn init_ack(stream: &mut T) -> Result<()> { 29 | let mut buf = vec![0u8; 32]; 30 | 31 | // Receive the client hello message 32 | stream.read(&mut buf).await?; 33 | 34 | // TODO: Validate client hello message 35 | // Reply with server hello message 36 | let server_hello = ServerHello::new(0); 37 | stream.write_all(&server_hello.bytes()).await?; 38 | stream.flush().await?; 39 | 40 | Ok(()) 41 | } 42 | 43 | async fn write_request_ack( 44 | mut stream: T, 45 | port: u16, 46 | ) -> Result<()> { 47 | // TODO: Have a better way to write back request ACK 48 | stream.write_all(&[VERSION, 0, 0, 1, 127, 0, 0, 1]).await?; 49 | stream.write_u16(port).await?; 50 | stream.flush().await?; 51 | 52 | Ok(()) 53 | } 54 | -------------------------------------------------------------------------------- /src/protocol/socks5/parser.rs: -------------------------------------------------------------------------------- 1 | use crate::protocol::common::addr::IpAddress; 2 | use crate::protocol::common::atype::Atype; 3 | use crate::protocol::common::command::Command; 4 | use crate::protocol::socks5::base::{Request, VERSION}; 5 | 6 | use bytes::Bytes; 7 | use std::io::Result; 8 | use std::io::{Error, ErrorKind}; 9 | use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite}; 10 | 11 | pub async fn parse(mut stream: T) -> Result { 12 | // Read version number 13 | let version = match stream.read_u8().await? { 14 | VERSION => VERSION, 15 | _ => { 16 | return Err(Error::new( 17 | ErrorKind::Unsupported, 18 | "Unsupported version number", 19 | )) 20 | } 21 | }; 22 | 23 | // Read command byte 24 | let command = match Command::from(stream.read_u8().await?) { 25 | Ok(command) => command, 26 | Err(e) => return Err(e), 27 | }; 28 | 29 | // Don't do anything about rsv 30 | let rsv = stream.read_u8().await?; 31 | 32 | // Read address type 33 | let atype = match Atype::from(stream.read_u8().await?) { 34 | Ok(atype) => atype, 35 | Err(e) => return Err(e), 36 | }; 37 | 38 | // Get address size and address object 39 | let addr = match atype { 40 | Atype::IPv4 => IpAddress::from_u32(stream.read_u32().await?), 41 | Atype::IPv6 => IpAddress::from_u128(stream.read_u128().await?), 42 | Atype::DomainName => { 43 | // Read address size 44 | let size = stream.read_u8().await? as usize; 45 | let mut buf = vec![0u8; size]; 46 | 47 | // Read address data 48 | stream.read_exact(&mut buf).await?; 49 | IpAddress::from_bytes(Bytes::from(buf)) 50 | } 51 | }; 52 | 53 | // Read port number 54 | let port = stream.read_u16().await?; 55 | 56 | Ok(Request::new(version, command, rsv, atype, port, addr)) 57 | } 58 | -------------------------------------------------------------------------------- /src/protocol/trojan/base.rs: -------------------------------------------------------------------------------- 1 | use crate::protocol::common::addr::IpAddress; 2 | use crate::protocol::common::atype::Atype; 3 | use crate::protocol::common::command::Command; 4 | use crate::protocol::common::request::{InboundRequest, TransportProtocol}; 5 | use crate::proxy::base::SupportedProtocols; 6 | 7 | use std::fmt; 8 | 9 | /// Trojan hex payload is always 56 bytes 10 | pub const HEX_SIZE: usize = 56; 11 | 12 | /// Trojan protocol uses the 0x0D0A as deliminate for packet header and payload 13 | pub const CRLF: u16 = 0x0D0A; 14 | 15 | pub struct Request { 16 | command: Command, 17 | atype: Atype, 18 | addr: IpAddress, 19 | port: u16, 20 | proxy_protocol: SupportedProtocols, 21 | } 22 | 23 | impl Request { 24 | pub fn new( 25 | command: Command, 26 | atype: Atype, 27 | addr: IpAddress, 28 | port: u16, 29 | proxy_protocol: SupportedProtocols, 30 | ) -> Request { 31 | return Request { 32 | command, 33 | atype, 34 | addr, 35 | port, 36 | proxy_protocol, 37 | }; 38 | } 39 | 40 | #[inline] 41 | pub fn into_request(self) -> InboundRequest { 42 | return match self.command { 43 | Command::Udp => InboundRequest::new( 44 | self.atype, 45 | self.addr, 46 | self.command, 47 | self.port, 48 | TransportProtocol::UDP, 49 | self.proxy_protocol, 50 | ), 51 | _ => InboundRequest::new( 52 | self.atype, 53 | self.addr, 54 | self.command, 55 | self.port, 56 | TransportProtocol::TCP, 57 | self.proxy_protocol, 58 | ), 59 | }; 60 | } 61 | } 62 | 63 | impl fmt::Display for Request { 64 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 65 | write!( 66 | fmt, 67 | "{} {}:{}", 68 | self.command.to_string(), 69 | self.addr.to_string(), 70 | self.port 71 | ) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/protocol/trojan/mod.rs: -------------------------------------------------------------------------------- 1 | mod base; 2 | mod parser; 3 | 4 | pub mod packet; 5 | 6 | pub use self::base::CRLF; 7 | pub use self::base::HEX_SIZE; 8 | pub use self::parser::parse_and_authenticate; 9 | 10 | use crate::protocol::common::addr::IpAddress; 11 | use crate::protocol::common::{request::InboundRequest, stream::StandardTcpStream}; 12 | 13 | use std::io::Result; 14 | use std::net::IpAddr; 15 | use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt}; 16 | 17 | /// Helper function to accept an abstract TCP stream to Trojan connection 18 | pub async fn accept( 19 | mut stream: StandardTcpStream, 20 | secret: &[u8], 21 | ) -> Result<(InboundRequest, StandardTcpStream)> { 22 | // Read trojan request header and generate request header 23 | let request = parse_and_authenticate(&mut stream, secret).await?; 24 | 25 | Ok((request.into_request(), stream)) 26 | } 27 | 28 | /// Helper function to establish Trojan connection to remote server 29 | pub async fn handshake( 30 | stream: &mut T, 31 | request: &InboundRequest, 32 | secret: &[u8], 33 | ) -> Result<()> { 34 | // Write request header 35 | stream.write_all(secret).await?; 36 | stream.write_u16(CRLF).await?; 37 | stream.write_u8(request.command as u8).await?; 38 | stream.write_u8(request.atype as u8).await?; 39 | match &request.addr_port.ip { 40 | IpAddress::IpAddr(IpAddr::V4(ipv4)) => { 41 | stream.write_all(&ipv4.octets()).await?; 42 | } 43 | IpAddress::IpAddr(IpAddr::V6(ipv6)) => { 44 | stream.write_all(&ipv6.octets()).await?; 45 | } 46 | IpAddress::Domain(domain) => { 47 | stream.write_u8(domain.as_bytes().len() as u8).await?; 48 | stream.write_all(&domain.as_bytes()).await?; 49 | } 50 | } 51 | stream.write_u16(request.addr_port.port).await?; 52 | stream.write_u16(CRLF).await?; 53 | stream.flush().await?; 54 | 55 | Ok(()) 56 | } 57 | -------------------------------------------------------------------------------- /src/protocol/trojan/packet.rs: -------------------------------------------------------------------------------- 1 | use crate::protocol::common::addr::{IpAddrPort, IpAddress}; 2 | use crate::protocol::common::atype::Atype; 3 | use crate::protocol::common::request::InboundRequest; 4 | use crate::protocol::trojan::base::CRLF; 5 | use crate::protocol::trojan::parser::parse_udp; 6 | use crate::transport::grpc_stream::GrpcDataReaderStream; 7 | use crate::transport::grpc_transport::Hunk; 8 | 9 | use log::debug; 10 | use std::io::{self, Cursor, Error, ErrorKind}; 11 | use std::net::{IpAddr, SocketAddr}; 12 | use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; 13 | use tokio::net::UdpSocket; 14 | use tokio::sync::mpsc::Sender; 15 | use tonic::Streaming; 16 | 17 | /// Define the size of the buffer used to transport the data back and forth 18 | const BUF_SIZE: usize = 4096; 19 | 20 | /// According the official documentation for Trojan protocol, the UDP data will be segmented into Trojan UDP packets, 21 | /// which allows the outbound handler to also forward them as real UDP packets to the desired destinations. 22 | /// Link: https://trojan-gfw.github.io/trojan/protocol.html 23 | pub struct TrojanUdpPacketHeader { 24 | pub atype: Atype, 25 | pub dest: SocketAddr, 26 | pub payload_size: usize, 27 | } 28 | 29 | pub async fn copy_client_reader_to_udp_socket( 30 | mut client_reader: R, 31 | server_writer: &UdpSocket, 32 | ) -> io::Result<()> { 33 | let mut read_buf = vec![0u8; BUF_SIZE]; 34 | 35 | loop { 36 | let header = parse_udp(&mut client_reader).await?; 37 | 38 | assert!( 39 | header.payload_size <= BUF_SIZE, 40 | "Payload size exceeds read buffer size" 41 | ); 42 | 43 | let size = client_reader 44 | .read_exact(&mut read_buf[..header.payload_size]) 45 | .await?; 46 | 47 | if size == 0 { 48 | return Ok(()); 49 | } 50 | 51 | assert!( 52 | size == header.payload_size, 53 | "Failed to read the entire trojan udp packet, expect: {} bytes, read: {} bytes", 54 | header.payload_size, 55 | size 56 | ); 57 | 58 | server_writer 59 | .send_to(&read_buf[..header.payload_size], header.dest) 60 | .await?; 61 | } 62 | } 63 | 64 | pub async fn copy_udp_socket_to_client_writer( 65 | server_reader: &UdpSocket, 66 | mut client_writer: W, 67 | addr: IpAddrPort, 68 | ) -> io::Result<()> { 69 | let mut read_buf = vec![0u8; BUF_SIZE]; 70 | let (addr, port) = (addr.ip, addr.port); 71 | 72 | loop { 73 | let (size, _dest) = server_reader.recv_from(&mut read_buf).await?; 74 | 75 | if size == 0 { 76 | return Ok(()); 77 | } 78 | 79 | match addr { 80 | IpAddress::IpAddr(IpAddr::V4(addr)) => { 81 | client_writer.write_u8(Atype::IPv4 as u8).await?; 82 | client_writer.write_all(&addr.octets()).await?; 83 | } 84 | IpAddress::IpAddr(IpAddr::V6(addr)) => { 85 | client_writer.write_u8(Atype::IPv6 as u8).await?; 86 | client_writer.write_all(&addr.octets()).await?; 87 | } 88 | IpAddress::Domain(ref domain) => { 89 | client_writer.write_u8(Atype::DomainName as u8).await?; 90 | client_writer 91 | .write_u8(domain.as_bytes().len() as u8) 92 | .await?; 93 | client_writer.write_all(domain.as_bytes()).await?; 94 | } 95 | } 96 | 97 | client_writer.write_u16(port).await?; 98 | client_writer.write_u16(size as u16).await?; 99 | client_writer.write_u16(CRLF).await?; 100 | client_writer.write_all(&read_buf[..size]).await?; 101 | client_writer.flush().await?; 102 | 103 | debug!("Write {} bytes back to the client", size); 104 | } 105 | } 106 | 107 | pub async fn copy_client_reader_to_udp_server_writer< 108 | R: AsyncRead + Unpin, 109 | W: AsyncWrite + Unpin, 110 | >( 111 | mut client_reader: R, 112 | mut server_writer: W, 113 | request: InboundRequest, 114 | ) -> io::Result<()> { 115 | let mut read_buf = vec![0u8; BUF_SIZE]; 116 | 117 | loop { 118 | let size = client_reader.read(&mut read_buf).await?; 119 | 120 | server_writer.write_u8(request.atype as u8).await?; 121 | 122 | match request.addr_port.ip { 123 | IpAddress::IpAddr(IpAddr::V4(addr)) => { 124 | server_writer.write_all(&addr.octets()).await?; 125 | } 126 | IpAddress::IpAddr(IpAddr::V6(addr)) => { 127 | server_writer.write_all(&addr.octets()).await?; 128 | } 129 | IpAddress::Domain(ref domain) => { 130 | server_writer 131 | .write_u8(domain.as_bytes().len() as u8) 132 | .await?; 133 | server_writer.write_all(domain.as_bytes()).await?; 134 | } 135 | } 136 | 137 | server_writer.write_u16(request.addr_port.port).await?; 138 | server_writer.write_u16(size as u16).await?; 139 | server_writer.write_u16(CRLF).await?; 140 | server_writer.write_all(&read_buf[..size]).await?; 141 | server_writer.flush().await?; 142 | } 143 | } 144 | 145 | pub async fn copy_udp_server_reader_to_client_writer< 146 | R: AsyncRead + Unpin, 147 | W: AsyncWrite + Unpin, 148 | >( 149 | mut server_reader: R, 150 | mut client_writer: W, 151 | ) -> io::Result<()> { 152 | let mut read_buf = vec![0u8; BUF_SIZE]; 153 | 154 | loop { 155 | let header = parse_udp(&mut server_reader).await?; 156 | 157 | server_reader 158 | .read_exact(&mut read_buf[..header.payload_size]) 159 | .await?; 160 | 161 | client_writer 162 | .write_all(&read_buf[..header.payload_size]) 163 | .await?; 164 | } 165 | } 166 | 167 | pub async fn copy_client_reader_to_server_grpc_writer( 168 | mut client_reader: R, 169 | server_writer: Sender, 170 | request: InboundRequest, 171 | ) -> io::Result<()> { 172 | loop { 173 | let mut read_buf = vec![0u8; BUF_SIZE]; 174 | 175 | let n = client_reader.read(&mut read_buf).await?; 176 | 177 | read_buf.truncate(n); 178 | 179 | let mut cursor = Cursor::new(vec![0u8; 512]); 180 | 181 | cursor.write_u8(request.atype as u8).await?; 182 | 183 | match request.addr_port.ip { 184 | IpAddress::IpAddr(IpAddr::V4(addr)) => { 185 | cursor.write_all(&addr.octets()).await?; 186 | } 187 | IpAddress::IpAddr(IpAddr::V6(addr)) => { 188 | cursor.write_all(&addr.octets()).await?; 189 | } 190 | IpAddress::Domain(ref domain) => { 191 | cursor.write_u8(domain.as_bytes().len() as u8).await?; 192 | cursor.write_all(domain.as_bytes()).await?; 193 | } 194 | } 195 | 196 | cursor.write_u16(request.addr_port.port).await?; 197 | cursor.write_u16(n as u16).await?; 198 | cursor.write_u16(CRLF).await?; 199 | 200 | let (pos, mut header) = (cursor.position(), cursor.into_inner()); 201 | header.truncate(pos as usize); 202 | 203 | if let Err(_) = server_writer.send(Hunk { data: header }).await { 204 | return Err(Error::new( 205 | ErrorKind::ConnectionReset, 206 | "Failed to send GRPC packet", 207 | )); 208 | } 209 | 210 | if let Err(_) = server_writer.send(Hunk { data: read_buf }).await { 211 | return Err(Error::new( 212 | ErrorKind::ConnectionReset, 213 | "Failed to send GRPC packet", 214 | )); 215 | } 216 | } 217 | } 218 | 219 | pub async fn copy_server_grpc_reader_to_client_writer( 220 | server_reader: Streaming, 221 | mut client_writer: W, 222 | ) -> io::Result<()> { 223 | let mut server_reader = GrpcDataReaderStream::from_reader(server_reader); 224 | let mut read_buf = vec![0u8; BUF_SIZE]; 225 | 226 | loop { 227 | let header = parse_udp(&mut server_reader).await?; 228 | 229 | server_reader 230 | .read_exact(&mut read_buf[..header.payload_size]) 231 | .await?; 232 | 233 | client_writer 234 | .write_all(&read_buf[..header.payload_size]) 235 | .await?; 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /src/protocol/trojan/parser.rs: -------------------------------------------------------------------------------- 1 | use crate::protocol::common::addr::{IpAddrPort, IpAddress, IPV4_SIZE, IPV6_SIZE}; 2 | use crate::protocol::common::atype::Atype; 3 | use crate::protocol::common::command::Command; 4 | use crate::protocol::trojan::base::{Request, HEX_SIZE}; 5 | use crate::protocol::trojan::packet::TrojanUdpPacketHeader; 6 | 7 | use bytes::{Bytes, BytesMut}; 8 | use constant_time_eq::constant_time_eq; 9 | use std::io::{Error, ErrorKind, Result}; 10 | use tokio::io::{AsyncRead, AsyncReadExt}; 11 | 12 | pub async fn parse_and_authenticate( 13 | stream: &mut T, 14 | hex_key: &[u8], 15 | ) -> Result { 16 | // Read hex value for authentication. 17 | let mut hex = BytesMut::with_capacity(HEX_SIZE); 18 | let n = stream.read_buf(&mut hex).await?; 19 | if n != HEX_SIZE { 20 | return Err(Error::new( 21 | ErrorKind::InvalidData, 22 | "Malformed Trojan header", 23 | )); 24 | } 25 | 26 | // Authentication needs to happen here, otherwise the following read operations are not safe. 27 | if !constant_time_eq(hex_key, &hex) { 28 | return Err(Error::new( 29 | ErrorKind::InvalidData, 30 | "Trojan password mismatch", 31 | )); 32 | } 33 | 34 | // Read CLRF 35 | stream.read_u16().await?; 36 | 37 | // Extract command 38 | let command = match Command::from(stream.read_u8().await?) { 39 | Ok(command) => command, 40 | Err(e) => return Err(e), 41 | }; 42 | 43 | // Read address type 44 | let atype = match Atype::from(stream.read_u8().await?) { 45 | Ok(atype) => atype, 46 | Err(e) => return Err(e), 47 | }; 48 | 49 | // Get address size and address object 50 | let (_, addr) = match atype { 51 | Atype::IPv4 => (IPV4_SIZE, IpAddress::from_u32(stream.read_u32().await?)), 52 | Atype::IPv6 => (IPV6_SIZE, IpAddress::from_u128(stream.read_u128().await?)), 53 | Atype::DomainName => { 54 | // Read domain name size 55 | let size = stream.read_u8().await? as usize; 56 | 57 | // Read domain name context 58 | let mut buf = vec![0u8; size]; 59 | stream.read_exact(&mut buf).await?; 60 | (size, IpAddress::from_bytes(Bytes::from(buf))) 61 | } 62 | }; 63 | 64 | // Read port number 65 | let port = stream.read_u16().await?; 66 | 67 | // Read CLRF 68 | stream.read_u16().await?; 69 | 70 | Ok(Request::new( 71 | command, 72 | atype, 73 | addr, 74 | port, 75 | crate::proxy::base::SupportedProtocols::TROJAN, 76 | )) 77 | } 78 | 79 | pub async fn parse_udp(reader: &mut T) -> Result { 80 | // Read address type 81 | let atype = Atype::from(reader.read_u8().await?)?; 82 | 83 | // Read the address type 84 | let addr = match atype { 85 | Atype::IPv4 => IpAddress::from_u32(reader.read_u32().await?), 86 | Atype::IPv6 => IpAddress::from_u128(reader.read_u128().await?), 87 | Atype::DomainName => { 88 | // Get payload size 89 | let size = reader.read_u8().await? as usize; 90 | let mut buf = vec![0u8; size]; 91 | 92 | // Read data into buffer 93 | reader.read_exact(&mut buf).await?; 94 | IpAddress::from_bytes(Bytes::from(buf)) 95 | } 96 | }; 97 | 98 | // Read port, payload length and CRLF 99 | let port = reader.read_u16().await?; 100 | let length = reader.read_u16().await?; 101 | reader.read_u16().await?; 102 | 103 | // Resolve DNS name if the requested address is DNS name. 104 | let dest = match IpAddrPort::new(addr, port).into() { 105 | Ok(d) => d, 106 | Err(e) => return Err(e), 107 | }; 108 | 109 | Ok(TrojanUdpPacketHeader { 110 | atype, 111 | dest, 112 | payload_size: length as usize, 113 | }) 114 | } 115 | -------------------------------------------------------------------------------- /src/proxy/base.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Clone, Copy, Debug, Serialize, Deserialize)] 4 | pub enum SupportedProtocols { 5 | SOCKS, 6 | TROJAN, 7 | DIRECT, 8 | } 9 | -------------------------------------------------------------------------------- /src/proxy/grpc/acceptor.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | config::base::InboundConfig, 3 | protocol::{common::request::InboundRequest, trojan}, 4 | proxy::base::SupportedProtocols, 5 | transport::{grpc_stream::GrpcDataReaderStream, grpc_transport::Hunk}, 6 | }; 7 | 8 | use once_cell::sync::OnceCell; 9 | use sha2::{Digest, Sha224}; 10 | use std::io::{self, Error, ErrorKind}; 11 | use tonic::{Request, Streaming}; 12 | 13 | /// Static lifetime GRPC acceptor 14 | static GRPC_ACCEPTOR: OnceCell = OnceCell::new(); 15 | 16 | /// Acceptor handles incomming connection by escalating them to application level data stream based on 17 | /// the configuration. It is also responsible for escalating TCP connection to TLS connection if the user 18 | /// enabled TLS. 19 | pub struct GrpcAcceptor { 20 | protocol: SupportedProtocols, 21 | secret: Vec, 22 | } 23 | 24 | /// GrpcAcceptor should implment 2 types of GRPC transport protocol, Hunk and MultiHunk. 25 | impl GrpcAcceptor { 26 | pub fn new(inbound_config: &InboundConfig) -> &'static GrpcAcceptor { 27 | let secret = match inbound_config.protocol { 28 | SupportedProtocols::TROJAN if inbound_config.secret.is_some() => { 29 | let secret = inbound_config.secret.as_ref().unwrap(); 30 | Sha224::digest(secret.as_bytes()) 31 | .iter() 32 | .map(|x| format!("{:02x}", x)) 33 | .collect::() 34 | .as_bytes() 35 | .to_vec() 36 | } 37 | _ => Vec::new(), 38 | }; 39 | 40 | GRPC_ACCEPTOR.get_or_init(|| Self { 41 | protocol: inbound_config.protocol, 42 | secret, 43 | }) 44 | } 45 | 46 | /// Handler function for proxying GRPC traffic with Hunk message payload. 47 | pub async fn accept_hunk( 48 | &self, 49 | request: Request>, 50 | ) -> io::Result<(InboundRequest, GrpcDataReaderStream)> { 51 | // Convert request into inbound reader stream 52 | let mut inbound_reader = GrpcDataReaderStream::from_reader(request.into_inner()); 53 | 54 | // Based on the protocol, decide how to proceed with the inbound stream 55 | let request = match self.protocol { 56 | SupportedProtocols::TROJAN => { 57 | // Read trojan request from the inbound stream 58 | let trojan_request = trojan::parse_and_authenticate(&mut inbound_reader, &self.secret).await?; 59 | 60 | trojan_request.into_request() 61 | } 62 | // TODO: Support more protocols than just Trojan 63 | _ => return Err(Error::new(ErrorKind::Unsupported, "Unsupported protocol")), 64 | }; 65 | 66 | Ok((request, inbound_reader)) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/proxy/grpc/handler.rs: -------------------------------------------------------------------------------- 1 | use crate::protocol::common::addr::IpAddress; 2 | use crate::protocol::trojan::{self, CRLF}; 3 | use crate::{ 4 | protocol::common::request::InboundRequest, 5 | proxy::base::SupportedProtocols, 6 | transport::{grpc_stream::GrpcDataReaderStream, grpc_transport::Hunk}, 7 | }; 8 | 9 | use bytes::BufMut; 10 | use once_cell::sync::OnceCell; 11 | use std::io::{self, Error, ErrorKind}; 12 | use std::net::{IpAddr, SocketAddr}; 13 | use tokio::io::{AsyncRead, AsyncReadExt}; 14 | use tokio::net::{TcpStream, UdpSocket}; 15 | use tokio::sync::mpsc::Sender; 16 | use tonic::Status; 17 | 18 | const BUFFER_SIZE: usize = 4096; 19 | 20 | /// Static life time TCP server outbound traffic handler to avoid ARC 21 | /// The handler is initialized through init() function 22 | static GRPC_HANDLER: OnceCell = OnceCell::new(); 23 | 24 | /// GrpcHandler is responsible for handling outbound traffic for GRPC inbound streams 25 | pub struct GrpcHandler { 26 | protocol: SupportedProtocols, 27 | } 28 | 29 | impl GrpcHandler { 30 | pub fn new() -> &'static GrpcHandler { 31 | GRPC_HANDLER.get_or_init(|| Self { 32 | protocol: SupportedProtocols::TROJAN, 33 | }) 34 | } 35 | 36 | pub async fn handle_hunk( 37 | &self, 38 | mut client_reader: GrpcDataReaderStream, 39 | client_writer: Sender>, 40 | request: InboundRequest, 41 | ) -> io::Result<()> { 42 | match self.protocol { 43 | SupportedProtocols::TROJAN => { 44 | return match request.command { 45 | crate::protocol::common::command::Command::Connect => { 46 | let ip_port: SocketAddr = match request.addr_port.into() { 47 | Ok(sa) => sa, 48 | Err(e) => return Err(e), 49 | }; 50 | 51 | // Establish connection to remote server as specified by proxy request 52 | let (mut server_reader, mut server_writer) = 53 | match TcpStream::connect(ip_port).await { 54 | Ok(stream) => tokio::io::split(stream), 55 | Err(e) => return Err(e), 56 | }; 57 | 58 | tokio::select!( 59 | _ = tokio::io::copy(&mut client_reader, &mut server_writer) => (), 60 | _ = copy_server_reader_to_client_grpc_writer(&mut server_reader, client_writer) => (), 61 | ); 62 | 63 | Ok(()) 64 | } 65 | crate::protocol::common::command::Command::Udp => { 66 | // Establish UDP connection to remote host 67 | let socket = UdpSocket::bind("0.0.0.0:0").await?; 68 | 69 | tokio::select!( 70 | _ = trojan::packet::copy_client_reader_to_udp_socket(client_reader, &socket) => (), 71 | _ = copy_udp_socket_to_client_grpc_writer(&socket, client_writer, request) => () 72 | ); 73 | 74 | Ok(()) 75 | } 76 | // Trojan only supports 2 types of commands unlike SOCKS 77 | // * CONNECT X'01' 78 | // * UDP ASSOCIATE X'03' 79 | crate::protocol::common::command::Command::Bind => Err(Error::new( 80 | ErrorKind::Unsupported, 81 | "Bind command is not supported in Trojan", 82 | )), 83 | }; 84 | } 85 | _ => { 86 | return Err(Error::new( 87 | ErrorKind::Unsupported, 88 | "GrpcHandler only supports Trojan for now", 89 | )) 90 | } 91 | } 92 | } 93 | } 94 | 95 | async fn copy_server_reader_to_client_grpc_writer( 96 | mut reader: R, 97 | writer: Sender>, 98 | ) -> io::Result<()> { 99 | loop { 100 | let mut buf = Vec::with_capacity(BUFFER_SIZE); 101 | 102 | match reader.read_buf(&mut buf).await { 103 | Ok(_) => (), 104 | Err(_) => { 105 | return Err(io::Error::new( 106 | io::ErrorKind::BrokenPipe, 107 | "Failed to read data from remote server", 108 | )) 109 | } 110 | } 111 | 112 | match writer.send(Ok(Hunk { data: buf })).await { 113 | Ok(_) => (), 114 | Err(_) => { 115 | return Err(io::Error::new( 116 | io::ErrorKind::BrokenPipe, 117 | "Failed to send data to client", 118 | )) 119 | } 120 | } 121 | } 122 | } 123 | 124 | async fn copy_udp_socket_to_client_grpc_writer( 125 | udp_socket: &UdpSocket, 126 | client_sender: Sender>, 127 | request: InboundRequest, 128 | ) -> io::Result<()> { 129 | let mut udp_buffer = vec![0u8; BUFFER_SIZE]; 130 | 131 | loop { 132 | let n = match udp_socket.recv(&mut udp_buffer).await { 133 | Ok(n) => n, 134 | Err(_) => { 135 | return Err(io::Error::new( 136 | io::ErrorKind::BrokenPipe, 137 | "Failed to read data from remote server", 138 | )) 139 | } 140 | }; 141 | 142 | let mut buf = Vec::with_capacity(BUFFER_SIZE); 143 | 144 | // Write address type to remote 145 | buf.put_u8(request.atype as u8); 146 | 147 | // Write back the address of the trojan request 148 | match request.addr_port.ip { 149 | IpAddress::IpAddr(IpAddr::V4(addr)) => { 150 | buf.put_slice(&addr.octets()); 151 | } 152 | IpAddress::IpAddr(IpAddr::V6(addr)) => { 153 | buf.put_slice(&addr.octets()); 154 | } 155 | IpAddress::Domain(ref domain) => { 156 | buf.put_u8(domain.as_bytes().len() as u8); 157 | buf.put_slice(domain.as_bytes()); 158 | } 159 | } 160 | 161 | // Write port, payload size, CRLF, and the payload data into the stream 162 | buf.put_u16(request.addr_port.port); 163 | buf.put_u16(n as u16); 164 | buf.put_u16(CRLF); 165 | buf.put_slice(&udp_buffer[..n]); 166 | 167 | match client_sender.send(Ok(Hunk { data: buf })).await { 168 | Ok(_) => (), 169 | Err(_) => { 170 | return Err(io::Error::new( 171 | io::ErrorKind::BrokenPipe, 172 | "Failed to send data to client", 173 | )) 174 | } 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/proxy/grpc/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod handler; 2 | pub mod acceptor; 3 | pub mod server; 4 | -------------------------------------------------------------------------------- /src/proxy/grpc/server.rs: -------------------------------------------------------------------------------- 1 | use crate::config::base::{InboundConfig, OutboundConfig}; 2 | use crate::transport::grpc_transport::grpc_service_server::GrpcService; 3 | use crate::transport::grpc_transport::grpc_service_server::GrpcServiceServer; 4 | use crate::transport::grpc_transport::{Hunk, MultiHunk}; 5 | 6 | use futures::Stream; 7 | use log::{info, warn}; 8 | use std::io::{self, Error, ErrorKind}; 9 | use std::net::ToSocketAddrs; 10 | use std::pin::Pin; 11 | use tokio::sync::mpsc; 12 | use tonic::transport::{Identity, Server, ServerTlsConfig}; 13 | use tonic::{Request, Response, Status, Streaming}; 14 | 15 | use super::acceptor::GrpcAcceptor; 16 | use super::handler::GrpcHandler; 17 | 18 | // TODO: Need more discretion in detemining the value for channel size, or make it configurable 19 | const CHANNEL_SIZE: usize = 16; 20 | 21 | /// Start running the GRPC server 22 | pub async fn start( 23 | inbound_config: &'static InboundConfig, 24 | _outbound_config: &'static OutboundConfig, 25 | ) -> io::Result<()> { 26 | // Extract the address that the server should listen on 27 | let address = match (inbound_config.address.as_ref(), inbound_config.port) 28 | .to_socket_addrs() 29 | .unwrap() 30 | .next() 31 | { 32 | Some(addr) => addr, 33 | None => { 34 | return Err(Error::new( 35 | ErrorKind::AddrNotAvailable, 36 | "incorrect address in configuration", 37 | )) 38 | } 39 | }; 40 | 41 | let tls_config = match &inbound_config.tls { 42 | Some(cfg) => { 43 | let cert = tokio::fs::read(cfg.cert_path.clone()).await?; 44 | let key = tokio::fs::read(cfg.key_path.clone()).await?; 45 | Some(ServerTlsConfig::new().identity(Identity::from_pem(cert, key))) 46 | } 47 | None => None, 48 | }; 49 | 50 | // Initialize and start the GRPC server to serve GRPC requests 51 | let mut server = match tls_config { 52 | Some(cfg) => Server::builder() 53 | .tls_config(cfg) 54 | .expect("Failed to build GRPC server"), 55 | None => Server::builder(), 56 | }; 57 | 58 | return match server 59 | .add_service(GrpcServiceServer::new(GrpcProxyService::new( 60 | GrpcAcceptor::new(&inbound_config), 61 | GrpcHandler::new(), 62 | ))) 63 | .serve(address) 64 | .await 65 | { 66 | Ok(_) => Ok(()), 67 | Err(e) => Err(Error::new( 68 | ErrorKind::Interrupted, 69 | format!("Failed to start grpc server: {}", e), 70 | )), 71 | }; 72 | } 73 | 74 | pub struct GrpcProxyService { 75 | acceptor: &'static GrpcAcceptor, 76 | handler: &'static GrpcHandler, 77 | } 78 | 79 | impl GrpcProxyService { 80 | pub fn new(acceptor: &'static GrpcAcceptor, handler: &'static GrpcHandler) -> Self { 81 | Self { acceptor, handler } 82 | } 83 | } 84 | 85 | #[tonic::async_trait] 86 | impl GrpcService for GrpcProxyService { 87 | type TunStream = Pin> + Send>>; 88 | type TunMultiStream = Pin> + Send>>; 89 | 90 | async fn tun( 91 | &self, 92 | request: Request>, 93 | ) -> Result, Status> { 94 | info!("Received GRPC request"); 95 | 96 | let (acceptor, handler) = (self.acceptor, self.handler); 97 | let (tx, rx) = mpsc::channel(CHANNEL_SIZE); 98 | 99 | tokio::spawn(async move { 100 | let (request, client_reader) = match acceptor.accept_hunk(request).await { 101 | Ok((req, reader)) => (req, reader), 102 | Err(e) => { 103 | warn!("Failed to accept the inbound traffic: {}", e); 104 | return; 105 | } 106 | }; 107 | 108 | match handler.handle_hunk(client_reader, tx, request).await { 109 | Ok(_) => return, 110 | Err(e) => { 111 | warn!("Failed to handle inbound traffic: {}", e); 112 | return; 113 | } 114 | }; 115 | }); 116 | 117 | Ok(Response::new(Box::pin( 118 | tokio_stream::wrappers::ReceiverStream::new(rx), 119 | ))) 120 | } 121 | 122 | async fn tun_multi( 123 | &self, 124 | _request: tonic::Request>, 125 | ) -> Result, Status> { 126 | todo!() 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/proxy/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod base; 2 | pub mod grpc; 3 | pub mod tcp; 4 | pub mod quic; 5 | -------------------------------------------------------------------------------- /src/proxy/quic/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod server; 2 | -------------------------------------------------------------------------------- /src/proxy/quic/server.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | config::base::InboundConfig, 3 | config::{base::OutboundConfig, tls::make_server_config}, 4 | protocol::{ 5 | common::command::Command, 6 | trojan::{self, parse_and_authenticate}, 7 | }, 8 | proxy::base::SupportedProtocols, 9 | }; 10 | 11 | use log::{info, warn}; 12 | use once_cell::sync::OnceCell; 13 | use quinn::{self, Endpoint}; 14 | use sha2::{Digest, Sha224}; 15 | use std::{io::Result, net::SocketAddr, sync::Arc}; 16 | use std::{ 17 | io::{Error, ErrorKind}, 18 | net::ToSocketAddrs, 19 | }; 20 | use tokio::net::{TcpStream, UdpSocket}; 21 | 22 | // Static placeholder for trojan secret 23 | static TROJAN_SECRET: OnceCell> = OnceCell::new(); 24 | 25 | pub const ALPN_QUIC_HTTP: &[&[u8]] = &[b"hq-29", b"h2", b"h3"]; 26 | 27 | /// Start running QUIC server 28 | pub async fn start( 29 | inbound_config: &'static InboundConfig, 30 | _outboud_config: &'static OutboundConfig, 31 | ) -> Result<()> { 32 | // Extract listening address of the inbound traffic 33 | let address = (inbound_config.address.clone(), inbound_config.port) 34 | .to_socket_addrs() 35 | .unwrap() 36 | .next() 37 | .unwrap(); 38 | 39 | // Compute trojan secret 40 | let hex = match inbound_config.protocol { 41 | SupportedProtocols::TROJAN if inbound_config.secret.is_some() => { 42 | let secret = inbound_config.secret.as_ref().unwrap(); 43 | Sha224::digest(secret.as_bytes()) 44 | .iter() 45 | .map(|x| format!("{:02x}", x)) 46 | .collect::() 47 | .as_bytes() 48 | .to_vec() 49 | } 50 | _ => Vec::new() 51 | }; 52 | _ = TROJAN_SECRET.set(hex); 53 | 54 | // Build config for accepting QUIC connection 55 | let mut tls_config = match &inbound_config.tls { 56 | Some(tls) => match make_server_config(&tls) { 57 | Some(cfg) => cfg, 58 | None => { 59 | return Err(Error::new( 60 | ErrorKind::InvalidInput, 61 | "Failed to build TLS configuration for QUIC", 62 | )) 63 | } 64 | }, 65 | None => { 66 | return Err(Error::new( 67 | ErrorKind::Unsupported, 68 | "QUIC protocol must have TLS configuration", 69 | )) 70 | } 71 | }; 72 | tls_config.alpn_protocols = ALPN_QUIC_HTTP.iter().map(|&x| x.into()).collect(); 73 | 74 | // Build server tls configuration 75 | let mut config = quinn::ServerConfig::with_crypto(Arc::new(tls_config)); 76 | let mut transport_config = quinn::TransportConfig::default(); 77 | transport_config 78 | .congestion_controller_factory(Arc::new(quinn::congestion::BbrConfig::default())); 79 | config.transport = Arc::new(transport_config); 80 | 81 | // Create QUIC server socket 82 | let endpoint = Endpoint::server(config, address)?; 83 | 84 | // Start accept loop to handle incomming QUIC connections 85 | while let Some(conn) = endpoint.accept().await { 86 | info!("Accepted new QUIC connection"); 87 | 88 | tokio::spawn(async move { 89 | // Establish QUIC connection with handshake 90 | let connection = match conn.await { 91 | Ok(c) => c, 92 | Err(e) => { 93 | warn!("Failed to complete QUIC handshake after accepting the initial connection, {}", e); 94 | return; 95 | } 96 | }; 97 | 98 | loop { 99 | let (mut client_writer, mut client_reader) = match connection.accept_bi().await { 100 | Ok((tx, rx)) => (tx, rx), 101 | Err(_) => return, 102 | }; 103 | 104 | tokio::spawn(async move { 105 | // Read proxy request from the client stream 106 | let request = 107 | parse_and_authenticate(&mut client_reader, TROJAN_SECRET.get().unwrap()) 108 | .await 109 | .unwrap() 110 | .into_request(); 111 | 112 | info!( 113 | "Trojan request parsed: ({} {})", 114 | request.addr_port.ip.to_string(), 115 | request.addr_port.port 116 | ); 117 | 118 | // Dispatch connection based on trojan command 119 | match request.command { 120 | Command::Udp => { 121 | let udp_socket = match UdpSocket::bind("0.0.0.0:0").await { 122 | Ok(s) => Arc::from(s), 123 | Err(e) => { 124 | warn!("Failed to create a local UDP socket: {}", e); 125 | return; 126 | } 127 | }; 128 | 129 | tokio::select!( 130 | _ = trojan::packet::copy_client_reader_to_udp_socket(&mut client_reader, &udp_socket) => (), 131 | _ = trojan::packet::copy_udp_socket_to_client_writer(&udp_socket, &mut client_writer, request.addr_port) => () 132 | ); 133 | } 134 | _ => { 135 | // Connect to remote server 136 | let addr: SocketAddr = match request.addr_port.into() { 137 | Ok(addr) => addr, 138 | Err(e) => { 139 | warn!("Failed to resolve target dns name: {}", e); 140 | return; 141 | } 142 | }; 143 | let outbound_connection = TcpStream::connect(addr).await.unwrap(); 144 | 145 | info!("Established connection"); 146 | 147 | // Transport data between client and remote server 148 | let (mut server_reader, mut server_writer) = 149 | tokio::io::split(outbound_connection); 150 | 151 | tokio::select!( 152 | _ = tokio::io::copy(&mut client_reader, &mut server_writer) => (), 153 | _ = tokio::io::copy(&mut server_reader, &mut client_writer) => () 154 | ); 155 | } 156 | }; 157 | 158 | // Try to gracefully shutdown, nothing we can do if it fails. 159 | _ = client_writer.finish().await; 160 | }); 161 | } 162 | }); 163 | } 164 | 165 | Ok(()) 166 | } 167 | -------------------------------------------------------------------------------- /src/proxy/tcp/acceptor.rs: -------------------------------------------------------------------------------- 1 | use crate::config::base::InboundConfig; 2 | use crate::config::tls::make_server_config; 3 | use crate::protocol::common::request::InboundRequest; 4 | use crate::protocol::common::stream::StandardTcpStream; 5 | use crate::protocol::socks5; 6 | use crate::protocol::trojan; 7 | use crate::proxy::base::SupportedProtocols; 8 | 9 | use once_cell::sync::OnceCell; 10 | use sha2::{Digest, Sha224}; 11 | use std::io::{Error, ErrorKind, Result}; 12 | use std::sync::Arc; 13 | use tokio::io::{AsyncRead, AsyncWrite}; 14 | use tokio_rustls::TlsAcceptor; 15 | 16 | /// Static cell for storing the TCP acceptor with static lifetime to avoid use of Arc. 17 | static TCP_ACCEPTOR: OnceCell = OnceCell::new(); 18 | 19 | /// Acceptor handles incomming connection by escalating them to application level data stream based on 20 | /// the configuration. It is also responsible for escalating TCP connection to TLS connection if the user 21 | /// enabled TLS. 22 | pub struct TcpAcceptor { 23 | tls_acceptor: Option, 24 | port: u16, 25 | protocol: SupportedProtocols, 26 | secret: Vec, 27 | } 28 | 29 | impl TcpAcceptor { 30 | /// Instantiate a new acceptor based on InboundConfig passed by the user. It will generate the secret based on 31 | /// secret in the config file and the selected protocol and instantiate TLS acceptor is it is enabled. 32 | pub fn init(inbound: &InboundConfig) -> &'static Self { 33 | let secret = match inbound.protocol { 34 | SupportedProtocols::TROJAN if inbound.secret.is_some() => { 35 | let secret = inbound.secret.as_ref().unwrap(); 36 | Sha224::digest(secret.as_bytes()) 37 | .iter() 38 | .map(|x| format!("{:02x}", x)) 39 | .collect::() 40 | .as_bytes() 41 | .to_vec() 42 | } 43 | _ => Vec::new(), 44 | }; 45 | 46 | let tls_acceptor = match &inbound.tls { 47 | Some(tls) => match make_server_config(&tls) { 48 | Some(cfg) => Some(TlsAcceptor::from(Arc::new(cfg))), 49 | None => None, 50 | }, 51 | None => None, 52 | }; 53 | 54 | TCP_ACCEPTOR.get_or_init(|| Self { 55 | tls_acceptor, 56 | port: inbound.port, 57 | protocol: inbound.protocol, 58 | secret, 59 | }) 60 | } 61 | 62 | /// Takes an inbound TCP stream, escalate to TLS if possible and then escalate to application level data stream 63 | /// to be ready to read user's request and process them. 64 | pub async fn accept( 65 | &self, 66 | inbound_stream: T, 67 | ) -> Result<(InboundRequest, StandardTcpStream)> { 68 | let stream = if let Some(tls_acceptor) = self.tls_acceptor.as_ref() { 69 | StandardTcpStream::RustlsServer(tls_acceptor.accept(inbound_stream).await?) 70 | } else { 71 | StandardTcpStream::Plain(inbound_stream) 72 | }; 73 | 74 | match self.protocol { 75 | // Handle SOCK5 protocol 76 | SupportedProtocols::SOCKS => Ok(socks5::accept(stream, self.port).await?), 77 | // Handle Trojan protocol 78 | SupportedProtocols::TROJAN => Ok(trojan::accept(stream, &self.secret).await?), 79 | // Shutdown the connection if the protocol is currently unsupported 80 | _ => Err(Error::new( 81 | ErrorKind::ConnectionReset, 82 | "Failed to accept inbound stream, unsupported protocol", 83 | )), 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/proxy/tcp/handler.rs: -------------------------------------------------------------------------------- 1 | use crate::config::base::{OutboundConfig, OutboundMode}; 2 | use crate::config::tls::{make_client_config, NoCertificateVerification}; 3 | use crate::protocol::common::request::{InboundRequest, TransportProtocol}; 4 | use crate::protocol::common::stream::StandardTcpStream; 5 | use crate::protocol::trojan::{self, handshake, HEX_SIZE}; 6 | use crate::proxy::base::SupportedProtocols; 7 | use crate::transport::grpc_transport::grpc_service_client::GrpcServiceClient; 8 | use crate::transport::grpc_transport::Hunk; 9 | 10 | use futures::Stream; 11 | use log::{info, warn}; 12 | use once_cell::sync::OnceCell; 13 | use quinn::Endpoint; 14 | use rustls::{ClientConfig, ServerName}; 15 | use sha2::{Digest, Sha224}; 16 | use std::io::{self, Cursor, Error, ErrorKind}; 17 | use std::net::{Ipv6Addr, SocketAddr, SocketAddrV6}; 18 | use std::sync::Arc; 19 | use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, BufReader, BufWriter}; 20 | use tokio::net::{TcpStream, UdpSocket}; 21 | use tokio::sync::mpsc::{self, Sender}; 22 | use tokio_rustls::TlsConnector; 23 | use tokio_stream::wrappers::ReceiverStream; 24 | use tokio_stream::StreamExt; 25 | use tonic::Status; 26 | 27 | /// A list of ALPN that the client should support. 28 | pub const ALPN_QUIC_HTTP: &[&[u8]] = &[b"hq-29", b"h2", b"h3"]; 29 | 30 | /// Static life time TCP server outbound traffic handler to avoid ARC 31 | /// The handler is initialized through init() function 32 | static TCP_HANDLER: OnceCell = OnceCell::new(); 33 | 34 | /// Handler is responsible for taking user's request and process them and send back the result. 35 | /// It may need to dial to remote using TCP, UDP and TLS, in which it will be responsible for 36 | /// establishing a tranport level connection and escalate it to application data stream. 37 | pub struct TcpHandler { 38 | mode: OutboundMode, 39 | protocol: SupportedProtocols, 40 | destination: Option, 41 | tls: Option<(Arc, ServerName)>, 42 | secret: Vec, 43 | } 44 | 45 | impl TcpHandler { 46 | /// Instantiate a new Handler instance based on OutboundConfig passed by the user. It will evaluate the 47 | /// TLS option particularly to be able to later determine whether it should escalate the connection to 48 | /// TLS first or not. 49 | pub fn init(outbound: &OutboundConfig) -> &'static TcpHandler { 50 | // Get outbound TLS configuration and host dns name if TLS is enabled 51 | let tls = match &outbound.tls { 52 | Some(cfg) => { 53 | let client_config = make_client_config(&cfg); 54 | Some(( 55 | Arc::new(client_config), 56 | ServerName::try_from(cfg.host_name.as_ref()) 57 | .expect("Failed to parse host name"), 58 | )) 59 | } 60 | None => None, 61 | }; 62 | 63 | // Attempt to extract destination address and port from OutboundConfig. 64 | let destination = match (outbound.address.clone(), outbound.port) { 65 | (Some(addr), Some(port)) => match format!("{}:{}", addr, port).parse() { 66 | Ok(s) => Some(s), 67 | Err(e) => panic!("Failed to parse destination address: {}", e), 68 | }, 69 | (Some(_), None) => { 70 | panic!("Missing port while address is present") 71 | } 72 | (None, Some(_)) => { 73 | panic!("Missing address while address is present") 74 | } 75 | // No destination address and port specified, will use the address and port in each request 76 | (None, None) => None, 77 | }; 78 | 79 | // Extract the plaintext of the secret and process it 80 | let secret = match outbound.protocol { 81 | SupportedProtocols::TROJAN if outbound.secret.is_some() => { 82 | let secret = outbound.secret.clone().unwrap(); 83 | Sha224::digest(secret.as_bytes()) 84 | .iter() 85 | .map(|x| format!("{:02x}", x)) 86 | .collect::() 87 | .as_bytes() 88 | .to_vec() 89 | } 90 | // Configure secret if need to add other protocols 91 | _ => Vec::new(), 92 | }; 93 | 94 | TCP_HANDLER.get_or_init(|| Self { 95 | mode: outbound.mode.clone(), 96 | protocol: outbound.protocol, 97 | destination, 98 | tls, 99 | secret, 100 | }) 101 | } 102 | 103 | /// Given an abstract inbound stream, it will read the request to standard request format and then process it. 104 | /// After taking the request, the handler will then establish the outbound connection based on the user configuration, 105 | /// and transport data back and forth until one side terminate the connection. 106 | #[inline] 107 | pub async fn dispatch( 108 | &self, 109 | inbound_stream: StandardTcpStream, 110 | request: InboundRequest, 111 | ) -> io::Result<()> { 112 | match self.mode { 113 | OutboundMode::DIRECT => self.handle_direct_stream(request, inbound_stream).await?, 114 | OutboundMode::TCP => self.handle_tcp_stream(request, inbound_stream).await?, 115 | OutboundMode::QUIC => self.handle_quic_stream(request, inbound_stream).await?, 116 | OutboundMode::GRPC => self.handle_grpc_stream(request, inbound_stream).await?, 117 | } 118 | 119 | Ok(()) 120 | } 121 | 122 | /// Handle inbound TCP stream with direct outbound proxy strategy. Based on the inbound request, the handler 123 | /// will need to determine the way the input data is encrypted from the proxy request body and decrypt it to 124 | /// get the actual payload. Finally, it forwards the payload directly either with TCP or UDP flow. 125 | async fn handle_direct_stream( 126 | &self, 127 | request: InboundRequest, 128 | inbound_stream: StandardTcpStream, 129 | ) -> io::Result<()> { 130 | let (proxy_protocol, transport_protocol) = 131 | (request.proxy_protocol, request.transport_protocol); 132 | 133 | // Based on the protocol in the request body, decrypt the payload respectively 134 | match proxy_protocol { 135 | SupportedProtocols::TROJAN => { 136 | match transport_protocol { 137 | TransportProtocol::TCP => { 138 | // Extract the destination port and address from the proxy request 139 | let addr: SocketAddr = match request.addr_port.into() { 140 | Ok(addr) => addr, 141 | Err(e) => return Err(e), 142 | }; 143 | 144 | // Connect to remote server from the proxy request 145 | let outbound_stream = match TcpStream::connect(addr).await { 146 | Ok(stream) => stream, 147 | Err(e) => { 148 | return Err(Error::new( 149 | ErrorKind::ConnectionRefused, 150 | format!("failed to connect to tcp {}: {}", addr, e), 151 | )) 152 | } 153 | }; 154 | 155 | let (mut client_reader, mut client_writer) = 156 | tokio::io::split(inbound_stream); 157 | let (mut server_reader, mut server_writer) = 158 | tokio::io::split(outbound_stream); 159 | 160 | // Obtain reader and writer for inbound and outbound streams 161 | tokio::select!( 162 | _ = tokio::io::copy(&mut client_reader, &mut server_writer) => (), 163 | _ = tokio::io::copy(&mut server_reader, &mut client_writer) => () 164 | ); 165 | } 166 | TransportProtocol::UDP => { 167 | // Establish UDP connection to remote host 168 | let socket = UdpSocket::bind("0.0.0.0:0").await?; 169 | 170 | let (client_reader, client_writer) = tokio::io::split(inbound_stream); 171 | 172 | tokio::select!( 173 | _ = trojan::packet::copy_client_reader_to_udp_socket(BufReader::new(client_reader), &socket) => (), 174 | _ = trojan::packet::copy_udp_socket_to_client_writer(&socket, BufWriter::new(client_writer), request.addr_port) => () 175 | ); 176 | } 177 | }; 178 | } 179 | // Handler currently doesn't support SOCKS protocol. 180 | // Also not sure if we should support SOCKS protocol for the scope of this project. 181 | SupportedProtocols::SOCKS => { 182 | return Err(Error::new( 183 | ErrorKind::Unsupported, 184 | "Proxy request can't have socks as proxy protocol", 185 | )) 186 | } 187 | SupportedProtocols::DIRECT => { 188 | return Err(Error::new( 189 | ErrorKind::Unsupported, 190 | "Proxy request can't have direct as proxy protocol", 191 | )) 192 | } 193 | }; 194 | 195 | info!("Connection finished"); 196 | 197 | Ok(()) 198 | } 199 | 200 | /// #Experimental functionality 201 | /// QUIC support is currently experimental. 202 | async fn handle_quic_stream( 203 | &self, 204 | request: InboundRequest, 205 | inbound_stream: StandardTcpStream, 206 | ) -> io::Result<()> { 207 | // Dial remote proxy server 208 | let _roots = rustls::RootCertStore::empty(); 209 | let mut client_crypto = rustls::ClientConfig::builder() 210 | .with_safe_defaults() 211 | .with_custom_certificate_verifier(Arc::new(NoCertificateVerification {})) 212 | .with_no_client_auth(); 213 | client_crypto.alpn_protocols = ALPN_QUIC_HTTP.iter().map(|&x| x.into()).collect(); 214 | 215 | // Create client 216 | let mut endpoint = match Endpoint::client(SocketAddr::V6(SocketAddrV6::new( 217 | Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), 218 | 0, 219 | 0, 220 | 0, 221 | ))) { 222 | Ok(e) => e, 223 | Err(e) => { 224 | warn!("Failed to create local QUIC client: {}", e); 225 | return Err(e); 226 | } 227 | }; 228 | endpoint.set_default_client_config(quinn::ClientConfig::new(Arc::new(client_crypto))); 229 | 230 | // Establish connection with remote proxy server using QUIC protocol 231 | let connection = match endpoint.connect("127.0.0.1:8081".parse().unwrap(), "example.com") { 232 | Ok(c) => c, 233 | Err(e) => return Err(Error::new(ErrorKind::ConnectionAborted, e)), 234 | }; 235 | 236 | let connection = match connection.await { 237 | Ok(c) => c, 238 | Err(e) => return Err(Error::new(ErrorKind::ConnectionAborted, e)), 239 | }; 240 | 241 | let (mut server_writer, mut server_reader) = match connection.open_bi().await { 242 | Ok((tx, rx)) => (tx, rx), 243 | Err(e) => return Err(Error::new(ErrorKind::ConnectionAborted, e)), 244 | }; 245 | let (mut client_reader, mut client_writer) = tokio::io::split(inbound_stream); 246 | 247 | handshake(&mut server_writer, &request, &self.secret).await?; 248 | 249 | tokio::select!( 250 | _ = tokio::io::copy(&mut client_reader, &mut server_writer) => (), 251 | _ = tokio::io::copy(&mut server_reader, &mut client_writer) => (), 252 | ); 253 | 254 | Ok(()) 255 | } 256 | 257 | /// Handle inbound TCP stream with TCP outbound proxy strategy. This function is used when the program serves as 258 | /// the client end of proxy chain, such that it read the plaintext data from the inbound stream and will encrypt 259 | /// the it with the selected proxy and forward the proxy request to remote server. 260 | async fn handle_tcp_stream( 261 | &self, 262 | request: InboundRequest, 263 | inbound_stream: StandardTcpStream, 264 | ) -> io::Result<()> { 265 | // Establish the initial connection with remote server 266 | let connection = match self.destination { 267 | Some(dest) => TcpStream::connect(dest).await?, 268 | None => { 269 | return Err(Error::new( 270 | ErrorKind::NotConnected, 271 | "missing address of the remote server", 272 | )) 273 | } 274 | }; 275 | 276 | // Escalate the connection to TLS connection if tls config is present 277 | let mut outbound_stream = match &self.tls { 278 | Some((client_config, domain)) => { 279 | let connector = TlsConnector::from(Arc::clone(client_config)); 280 | StandardTcpStream::RustlsClient( 281 | connector.connect(domain.clone(), connection).await?, 282 | ) 283 | } 284 | None => StandardTcpStream::Plain(connection), 285 | }; 286 | 287 | // Handshake to form the proxy stream 288 | match self.protocol { 289 | SupportedProtocols::TROJAN => { 290 | // Check Trojan secret match 291 | if self.secret.len() != HEX_SIZE { 292 | return Err(Error::new( 293 | ErrorKind::InvalidInput, 294 | format!("Hex in trojan protocol is not {} bytes", HEX_SIZE), 295 | )); 296 | } 297 | 298 | // Start handshake to establish proxy stream 299 | handshake(&mut outbound_stream, &request, &self.secret).await?; 300 | 301 | match request.transport_protocol { 302 | TransportProtocol::TCP => { 303 | let (mut client_reader, mut client_writer) = 304 | tokio::io::split(inbound_stream); 305 | let (mut server_reader, mut server_writer) = 306 | tokio::io::split(outbound_stream); 307 | 308 | // Obtain reader and writer for inbound and outbound streams 309 | tokio::select!( 310 | _ = tokio::io::copy(&mut client_reader, &mut server_writer) => (), 311 | _ = tokio::io::copy(&mut server_reader, &mut client_writer) => () 312 | ); 313 | } 314 | TransportProtocol::UDP => { 315 | let (client_reader, client_writer) = tokio::io::split(inbound_stream); 316 | let (server_reader, server_writer) = tokio::io::split(outbound_stream); 317 | 318 | tokio::select!( 319 | _ = trojan::packet::copy_client_reader_to_udp_server_writer(client_reader, BufWriter::new(server_writer), request) => (), 320 | _ = trojan::packet::copy_udp_server_reader_to_client_writer(BufReader::new(server_reader), client_writer) => (), 321 | ); 322 | } 323 | } 324 | } 325 | SupportedProtocols::SOCKS => { 326 | return Err(Error::new(ErrorKind::Unsupported, "Unsupported protocol")) 327 | } 328 | SupportedProtocols::DIRECT => { 329 | return Err(Error::new(ErrorKind::Unsupported, "Unsupported protocol")); 330 | } 331 | }; 332 | 333 | info!("Connection finished"); 334 | Ok(()) 335 | } 336 | 337 | async fn handle_grpc_stream( 338 | &self, 339 | request: InboundRequest, 340 | inbound_stream: StandardTcpStream, 341 | ) -> io::Result<()> { 342 | // Remote GrpcService can not be None, otherwise we have no idea how to handle the proxy request 343 | if self.destination == None { 344 | return Err(Error::new( 345 | ErrorKind::Unsupported, 346 | "Destination can not be null", 347 | )); 348 | } 349 | 350 | // Safety: We have checked previous for self.destination equals None condition, so that the unwrap will always work. 351 | let endpoint = match self.tls { 352 | None => format!("http://{}", self.destination.unwrap()), 353 | Some(_) => format!("https://{}", self.destination.unwrap()), 354 | }; 355 | 356 | // Establish GRPC connection with remote server 357 | let mut connection = match GrpcServiceClient::connect(endpoint).await { 358 | Ok(c) => c, 359 | Err(_) => { 360 | return Err(Error::new( 361 | ErrorKind::ConnectionRefused, 362 | "Failed to connect to remote GRPC server", 363 | )) 364 | } 365 | }; 366 | 367 | let (tx, rx) = mpsc::channel(16); 368 | 369 | // Write request to cursor buffer and send to the receiver stream 370 | let mut cursor = Cursor::new(vec![0u8; 512]); 371 | handshake(&mut cursor, &request, &self.secret).await?; 372 | let (pos, mut data) = (cursor.position() as usize, cursor.into_inner()); 373 | data.truncate(pos); 374 | 375 | if let Err(_) = tx.send(Hunk { data }).await { 376 | return Err(Error::new( 377 | ErrorKind::ConnectionRefused, 378 | "Failed to send trojan request", 379 | )); 380 | } 381 | 382 | // Connect to remote server 383 | let server_reader = match connection.tun(ReceiverStream::from(rx)).await { 384 | Ok(c) => c.into_inner(), 385 | Err(_) => { 386 | return Err(Error::new( 387 | ErrorKind::ConnectionRefused, 388 | "failed to write request data", 389 | )) 390 | } 391 | }; 392 | 393 | let (client_reader, client_writer) = tokio::io::split(inbound_stream); 394 | 395 | // Dispatch the request based on the proxy command 396 | match request.command { 397 | crate::protocol::common::command::Command::Connect => { 398 | tokio::select!( 399 | _ = copy_client_reader_to_server_grpc_writer(client_reader, tx) => (), 400 | _ = copy_server_grpc_reader_to_client_writer(server_reader, client_writer) => () 401 | ); 402 | } 403 | crate::protocol::common::command::Command::Udp => { 404 | tokio::select!( 405 | _ = trojan::packet::copy_client_reader_to_server_grpc_writer(client_reader, tx, request) => (), 406 | _ = trojan::packet::copy_server_grpc_reader_to_client_writer(server_reader, client_writer) => (), 407 | ); 408 | } 409 | crate::protocol::common::command::Command::Bind => { 410 | return Err(Error::new( 411 | ErrorKind::Unsupported, 412 | "Bind command is not supported in Trojan protocol", 413 | )) 414 | } 415 | } 416 | 417 | Ok(()) 418 | } 419 | } 420 | 421 | async fn copy_client_reader_to_server_grpc_writer( 422 | mut client_reader: R, 423 | server_writer: Sender, 424 | ) -> io::Result<()> { 425 | loop { 426 | let mut read_buf = vec![0u8; 4096]; 427 | 428 | let n = client_reader.read(&mut read_buf).await?; 429 | 430 | read_buf.truncate(n); 431 | 432 | if let Err(e) = server_writer.send(Hunk { data: read_buf }).await { 433 | return Err(Error::new( 434 | ErrorKind::ConnectionReset, 435 | format!("Failed to send back server data, {}", e), 436 | )); 437 | } 438 | } 439 | } 440 | 441 | async fn copy_server_grpc_reader_to_client_writer< 442 | R: Stream> + Unpin, 443 | W: AsyncWrite + Unpin, 444 | >( 445 | mut server_reader: R, 446 | mut client_writer: W, 447 | ) -> io::Result<()> { 448 | loop { 449 | let hunk = match server_reader.next().await { 450 | Some(data) => match data { 451 | Ok(h) => h, 452 | Err(_) => { 453 | return Err(Error::new( 454 | ErrorKind::ConnectionReset, 455 | "Received error from GRPC server", 456 | )) 457 | } 458 | }, 459 | None => return Ok(()), 460 | }; 461 | 462 | client_writer.write_all(&hunk.data).await?; 463 | } 464 | } 465 | -------------------------------------------------------------------------------- /src/proxy/tcp/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod acceptor; 2 | pub mod handler; 3 | pub mod server; 4 | -------------------------------------------------------------------------------- /src/proxy/tcp/server.rs: -------------------------------------------------------------------------------- 1 | use crate::config::base::{InboundConfig, OutboundConfig}; 2 | use crate::proxy::tcp::acceptor::TcpAcceptor; 3 | use crate::proxy::tcp::handler::TcpHandler; 4 | 5 | use log::{info, warn}; 6 | use std::io::Result; 7 | use std::net::{SocketAddr, ToSocketAddrs}; 8 | use tokio::net::TcpListener; 9 | 10 | /// Start running raw TCP server 11 | pub async fn start( 12 | inbound_config: &'static InboundConfig, 13 | outbound_config: &'static OutboundConfig, 14 | ) -> Result<()> { 15 | // Extract the inbound config address 16 | let addresses: Vec = (inbound_config.address.clone(), inbound_config.port) 17 | .to_socket_addrs() 18 | .unwrap() 19 | .collect(); 20 | 21 | // Start the TCP server listener socket 22 | let listener = TcpListener::bind(&addresses[..]).await?; 23 | 24 | // Create TCP server acceptor and handler 25 | let (acceptor, handler) = ( 26 | TcpAcceptor::init(&inbound_config), 27 | TcpHandler::init(&outbound_config), 28 | ); 29 | 30 | // Enter server listener socket accept loop 31 | loop { 32 | let (socket, addr) = listener.accept().await?; 33 | 34 | info!("Received new connection from {}", addr); 35 | 36 | tokio::spawn(async move { 37 | let (request, inbound_stream) = match acceptor.accept(socket).await { 38 | Ok(stream) => stream, 39 | Err(e) => { 40 | warn!("Failed to accept inbound connection from {}: {}", addr, e); 41 | return; 42 | } 43 | }; 44 | 45 | match handler.dispatch(inbound_stream, request).await { 46 | Ok(_) => { 47 | info!("Connection from {} has finished", addr); 48 | } 49 | Err(e) => { 50 | warn!("Failed to handle the inbound stream: {}", e); 51 | } 52 | } 53 | }); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/transport/grpc_stream.rs: -------------------------------------------------------------------------------- 1 | use crate::transport::grpc_transport::Hunk; 2 | 3 | use bytes::{Buf, BufMut, BytesMut}; 4 | use futures::ready; 5 | use futures::Stream; 6 | use std::io; 7 | use std::pin::Pin; 8 | use std::task::Poll; 9 | use tokio::io::ReadBuf; 10 | use tokio::io::AsyncRead; 11 | use tonic::Status; 12 | use tonic::{self, Streaming}; 13 | 14 | pub struct GrpcDataReaderStream { 15 | reader: Streaming, 16 | buf: BytesMut, 17 | } 18 | 19 | impl GrpcDataReaderStream { 20 | #[inline] 21 | pub fn from_reader(reader: Streaming) -> Self { 22 | Self { 23 | reader, 24 | buf: BytesMut::new(), 25 | } 26 | } 27 | } 28 | 29 | impl AsyncRead for GrpcDataReaderStream { 30 | fn poll_read( 31 | mut self: Pin<&mut Self>, 32 | cx: &mut std::task::Context<'_>, 33 | buf: &mut tokio::io::ReadBuf<'_>, 34 | ) -> Poll> { 35 | // Used to indicate if we have new data available 36 | let mut has_new_data = false; 37 | 38 | // Check if the internal buffer has any data left 39 | if self.buf.has_remaining() { 40 | has_new_data = true; 41 | 42 | // Check if read buffer has enough space left 43 | if self.buf.remaining() <= buf.remaining() { 44 | // Dump the entire buffer into read buffer 45 | buf.put_slice(&self.buf); 46 | 47 | // Empty internal buffer 48 | self.buf.clear(); 49 | } else { 50 | // Fill read buffer as much as we can 51 | let read_len = buf.remaining(); 52 | buf.put_slice(&self.buf[..read_len]); 53 | 54 | // Advance internal buffer 55 | self.buf.advance(read_len); 56 | 57 | // Return as we have depleted read buffer 58 | return Poll::Ready(Ok(())); 59 | } 60 | } 61 | 62 | let data = match Pin::new(&mut self.reader).poll_next(cx) { 63 | Poll::Ready(d) => d, 64 | Poll::Pending if has_new_data => return Poll::Ready(Ok(())), 65 | Poll::Pending => return Poll::Pending, 66 | }; 67 | 68 | let packet = match data { 69 | None => { 70 | return Poll::Ready(Err(io::Error::new( 71 | io::ErrorKind::BrokenPipe, 72 | "Failed to read", 73 | ))) 74 | } 75 | Some(packet) => match packet { 76 | Ok(p) => p, 77 | Err(_) => { 78 | return Poll::Ready(Err(io::Error::new( 79 | io::ErrorKind::BrokenPipe, 80 | "Failed to read", 81 | ))) 82 | } 83 | }, 84 | }; 85 | 86 | // Check if the buffer is able to fit the packet 87 | if buf.remaining() >= packet.data.len() { 88 | // Write the entire packet to buffer if the buffer is large enough to fit 89 | buf.put_slice(&packet.data); 90 | } else { 91 | // Fill the read buffer as much as possible 92 | let rem = buf.remaining(); 93 | buf.put_slice(&packet.data[..rem]); 94 | 95 | // Move the rest of the packet to internal buffer 96 | self.buf.put_slice(&packet.data[rem..]); 97 | } 98 | 99 | return Poll::Ready(Ok(())); 100 | } 101 | } 102 | 103 | pub struct GrpcHunkRequestStream { 104 | inner: T, 105 | } 106 | 107 | impl GrpcHunkRequestStream { 108 | pub fn new(inner: T) -> Self { 109 | Self { inner } 110 | } 111 | } 112 | 113 | pub struct GrpcHunkResponseStream { 114 | inner: T, 115 | } 116 | 117 | impl GrpcHunkResponseStream { 118 | pub fn new(inner: T) -> Self { 119 | Self { inner } 120 | } 121 | } 122 | 123 | impl Stream for GrpcHunkRequestStream 124 | where 125 | T: AsyncRead + Unpin, 126 | { 127 | type Item = Hunk; 128 | 129 | fn poll_next( 130 | mut self: Pin<&mut Self>, 131 | cx: &mut std::task::Context<'_>, 132 | ) -> Poll> { 133 | let mut buf = vec![0; 4096]; 134 | 135 | let mut read_buf = ReadBuf::new(&mut buf); 136 | 137 | match ready!(Pin::new(&mut self.inner).poll_read(cx, &mut read_buf)) { 138 | Ok(_) => (), 139 | Err(_) => return Poll::Ready(None), 140 | } 141 | 142 | let size = read_buf.filled().len(); 143 | 144 | buf.truncate(size); 145 | 146 | return Poll::Ready(Some(Hunk { data: buf })); 147 | } 148 | } 149 | 150 | impl Stream for GrpcHunkResponseStream 151 | where 152 | T: AsyncRead + Unpin, 153 | { 154 | type Item = Result; 155 | 156 | fn poll_next( 157 | mut self: Pin<&mut Self>, 158 | cx: &mut std::task::Context<'_>, 159 | ) -> Poll> { 160 | let mut buf = vec![0; 4096]; 161 | 162 | let mut read_buf = ReadBuf::new(&mut buf); 163 | 164 | match ready!(Pin::new(&mut self.inner).poll_read(cx, &mut read_buf)) { 165 | Ok(_) => (), 166 | Err(_) => return Poll::Ready(Some(Err(Status::aborted("Failed to poll the data")))), 167 | } 168 | 169 | let size = read_buf.filled().len(); 170 | 171 | buf.truncate(size); 172 | 173 | return Poll::Ready(Some(Ok(Hunk { data: buf }))); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/transport/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod grpc_stream; 2 | 3 | pub mod grpc_transport { 4 | tonic::include_proto!("trojan_rust.transport.grpc"); 5 | } -------------------------------------------------------------------------------- /tests/proxy/acceptor_test.rs: -------------------------------------------------------------------------------- 1 | use std::mem::MaybeUninit; 2 | 3 | use bytes::BytesMut; 4 | use futures::TryFutureExt; 5 | use tokio::io::{AsyncReadExt, AsyncWriteExt, DuplexStream, ReadBuf}; 6 | use trojan_rust::config::base::InboundConfig; 7 | use trojan_rust::proxy::base::SupportedProtocols; 8 | 9 | #[test] 10 | fn test_acceptor_initialization() { 11 | // let inbound_config = InboundConfig { 12 | // address: "1.2.3.4".to_string(), 13 | // port: 123, 14 | // protocol: SupportedProtocols::TROJAN, 15 | // secret: Some("123123".to_string()), 16 | // tls: None, 17 | // }; 18 | // let acceptor = Acceptor::new(&inbound_config); 19 | // assert_eq!(1, 1); 20 | } 21 | 22 | #[tokio::test] 23 | async fn test_buffer() { 24 | // let mut buf: Vec = Vec::with_capacity(2); 25 | 26 | // let (mut server, mut client) = tokio::io::duplex(512); 27 | 28 | // server.write(&[2u8; 16]).await.unwrap(); 29 | 30 | // client.read_exact(&mut buf).await.unwrap(); 31 | 32 | // print!("{:?}", buf); 33 | use tokio::sync::oneshot; 34 | 35 | let (tx, rx) = oneshot::channel::(); 36 | 37 | tx.send(1); 38 | // tx.send(2); 39 | 40 | } 41 | -------------------------------------------------------------------------------- /tests/tests.rs: -------------------------------------------------------------------------------- 1 | extern crate trojan_rust; 2 | 3 | mod proxy { 4 | mod acceptor_test; 5 | } --------------------------------------------------------------------------------