Compare commits

...

12 Commits
v0.5.0 ... main

Author SHA1 Message Date
sunmy2019 0ee0157b4b
docs: update docs for rustls (#337)
* Fix typos and update docs for rustls

* update description

* Remove placeholders in doc
2024-03-01 14:46:29 +08:00
Yujia Qiao 3ab540f19d
chore: update bug_report.md 2024-02-18 22:55:59 +08:00
sunmy2019 4ac53a5a39
feat: optional rustls support (#330)
* initial implementation of rustls support

* Refactor create_self_signed_cert.sh script

* resolve lint errors

* Fix handling of Option in tls.rs

* Update cargo-hack check command and feature dependencies

* fix missing point

* Add conditional check to skip test if client or server is not enabled

* clean up things

* fix for windows CI

* try fixing Windows CI

* Update src/main.rs

* Update src/transport/websocket.rs

* add missing messages

* split the tls mod

Co-authored-by: Ning Sun <n@sunng.info>
2024-02-18 17:17:17 +08:00
dependabot[bot] 7251759bda
chore(deps): bump h2 from 0.3.21 to 0.3.24 (#334)
Bumps [h2](https://github.com/hyperium/h2) from 0.3.21 to 0.3.24.
- [Release notes](https://github.com/hyperium/h2/releases)
- [Changelog](https://github.com/hyperium/h2/blob/v0.3.24/CHANGELOG.md)
- [Commits](https://github.com/hyperium/h2/compare/v0.3.21...v0.3.24)

---
updated-dependencies:
- dependency-name: h2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-18 02:55:27 +00:00
dependabot[bot] ee7561c38d
chore(deps): bump snow from 0.9.3 to 0.9.6 (#333)
Bumps [snow](https://github.com/mcginty/snow) from 0.9.3 to 0.9.6.
- [Release notes](https://github.com/mcginty/snow/releases)
- [Commits](https://github.com/mcginty/snow/compare/v0.9.3...v0.9.6)

---
updated-dependencies:
- dependency-name: snow
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-18 10:50:47 +08:00
Vincent Young e4766e7d90
ci: support apple aarch64 (#294) 2024-02-18 02:25:45 +00:00
Ryan Dearing 63221028c9
fix: flush DataChannelCmd::StartForward* commands (#316)
Without flushing this may sit in a kernel buffer and we won't
know if the channel is still alive. This is particularly problematic
for the TCP connection pool.
2024-02-14 11:30:52 +08:00
sunmy2019 915bf4d21d
chore: update dependencies in Cargo.lock (#329) 2024-02-14 11:29:49 +08:00
Thomas Fournier 62114cde4c
fix: typo (#323) 2024-02-13 23:08:06 +08:00
zhfish 65b27f076c
chore: vendor openssl for musl (#301)
* Update Cargo.toml

add openssl's features for musl

* Update release.yml

* Update Cargo.toml

musl

* 更新 Cargo.toml

---------

Co-authored-by: Yujia Qiao <rapiz3142@gmail.com>
2023-11-05 13:39:16 +08:00
blueskea e08b2a9e92
docs: build with openssl (#303) 2023-11-05 13:38:39 +08:00
Yujia Qiao 84c04ab9df
fix: clippy (#302) 2023-11-03 11:37:43 +00:00
22 changed files with 657 additions and 139 deletions

View File

@ -28,3 +28,4 @@ If you encountered a panic, please re-run with `RUST_BACKTRACE=1` to provide the
- OS: <!-- Please fill in distribution if you're using linux-->
- `rathole --version` output:
- CPU architecture:
- rustc version:

View File

@ -21,6 +21,10 @@ jobs:
target: x86_64-unknown-linux-gnu
exe: rathole
cross: false
- os: ubuntu-latest
target: x86_64-unknown-linux-musl
exe: rathole
cross: false
- os: ubuntu-latest
target: aarch64-unknown-linux-musl
exe: rathole
@ -66,6 +70,11 @@ jobs:
target: x86_64-apple-darwin
exe: rathole
cross: false
- os: macos-latest
target: aarch64-apple-darwin
exe: rathole
cross: false
- os: windows-latest
target: x86_64-pc-windows-msvc
@ -92,7 +101,7 @@ jobs:
if: matrix.cross == false
run: rustup target add ${{ matrix.target }}
- name: Run tests
if: matrix.cross == false
if: matrix.cross == false && matrix.target != 'aarch64-apple-darwin'
run: cargo test --release --target ${{ matrix.target }} --verbose
- name: Build release
if: matrix.cross == false

View File

@ -32,7 +32,9 @@ jobs:
- name: Setup cargo-hack
run: cargo install cargo-hack
- name: Check all features
run: cargo hack check --feature-powerset --no-dev-deps
run: >
cargo hack check --feature-powerset --no-dev-deps
--mutually-exclusive-features default,native-tls,websocket-native-tls,rustls,websocket-rustls
build:
name: Build for ${{ matrix.target }}
@ -62,8 +64,10 @@ jobs:
- uses: Swatinem/rust-cache@v1
- name: Build
run: cargo build
- name: Run tests
- name: Run tests with native-tls
run: cargo test --verbose
- name: Run tests with rustls
run: cargo test --verbose --no-default-features --features server,client,rustls,noise,websocket-rustls,hot-reload
- uses: actions/upload-artifact@v2
with:
name: rathole-${{ matrix.target }}

280
Cargo.lock generated
View File

@ -19,30 +19,30 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aead"
version = "0.4.3"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877"
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
dependencies = [
"crypto-common",
"generic-array",
]
[[package]]
name = "aes"
version = "0.7.5"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
"opaque-debug",
]
[[package]]
name = "aes-gcm"
version = "0.9.2"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc3be92e19a7ef47457b8e6f90707e12b6ac5d20c6f3866584fa3be0787d839f"
checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
dependencies = [
"aead",
"aes",
@ -243,6 +243,15 @@ dependencies = [
"generic-array",
]
[[package]]
name = "block-padding"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
dependencies = [
"generic-array",
]
[[package]]
name = "byteorder"
version = "1.4.3"
@ -258,6 +267,15 @@ dependencies = [
"serde",
]
[[package]]
name = "cbc"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
dependencies = [
"cipher",
]
[[package]]
name = "cc"
version = "1.0.83"
@ -276,21 +294,20 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chacha20"
version = "0.8.2"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6"
checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
"zeroize",
]
[[package]]
name = "chacha20poly1305"
version = "0.9.1"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5"
checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
dependencies = [
"aead",
"chacha20",
@ -301,11 +318,13 @@ dependencies = [
[[package]]
name = "cipher"
version = "0.3.0"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"generic-array",
"crypto-common",
"inout",
"zeroize",
]
[[package]]
@ -318,7 +337,7 @@ dependencies = [
"bitflags 1.3.2",
"clap_derive",
"clap_lex",
"indexmap",
"indexmap 1.9.3",
"once_cell",
"strsim",
"termcolor",
@ -444,23 +463,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"rand_core",
"typenum",
]
[[package]]
name = "ctr"
version = "0.7.0"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a232f92a03f37dd7d7dd2adc67166c77e9cd88de5b019b9a9eecfaeaf7bfd481"
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
dependencies = [
"cipher",
]
[[package]]
name = "curve25519-dalek"
version = "4.1.1"
version = "4.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c"
checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348"
dependencies = [
"cfg-if",
"cpufeatures",
@ -495,6 +515,15 @@ version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946"
[[package]]
name = "des"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e"
dependencies = [
"cipher",
]
[[package]]
name = "digest"
version = "0.10.7"
@ -532,6 +561,12 @@ dependencies = [
"syn 2.0.37",
]
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.3.3"
@ -744,9 +779,9 @@ dependencies = [
[[package]]
name = "ghash"
version = "0.4.4"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99"
checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40"
dependencies = [
"opaque-debug",
"polyval",
@ -773,9 +808,9 @@ dependencies = [
[[package]]
name = "h2"
version = "0.3.21"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833"
checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9"
dependencies = [
"bytes",
"fnv",
@ -783,7 +818,7 @@ dependencies = [
"futures-sink",
"futures-util",
"http",
"indexmap",
"indexmap 2.2.3",
"slab",
"tokio",
"tokio-util",
@ -796,6 +831,12 @@ version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "hdrhistogram"
version = "7.5.2"
@ -836,6 +877,15 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest",
]
[[package]]
name = "http"
version = "0.2.9"
@ -929,7 +979,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown",
"hashbrown 0.12.3",
]
[[package]]
name = "indexmap"
version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177"
dependencies = [
"equivalent",
"hashbrown 0.14.3",
]
[[package]]
@ -952,6 +1012,16 @@ dependencies = [
"libc",
]
[[package]]
name = "inout"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
dependencies = [
"block-padding",
"generic-array",
]
[[package]]
name = "instant"
version = "0.1.12"
@ -1245,6 +1315,15 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-src"
version = "300.2.3+3.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cff92b6f71555b61bb9315f7c64da3ca43d87531622120fea0195fc761b4843"
dependencies = [
"cc",
]
[[package]]
name = "openssl-sys"
version = "0.9.93"
@ -1253,6 +1332,7 @@ checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d"
dependencies = [
"cc",
"libc",
"openssl-src",
"pkg-config",
"vcpkg",
]
@ -1269,6 +1349,23 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "p12"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4873306de53fe82e7e484df31e1e947d61514b6ea2ed6cd7b45d63006fd9224"
dependencies = [
"cbc",
"cipher",
"des",
"getrandom",
"hmac",
"lazy_static",
"rc2",
"sha1",
"yasna",
]
[[package]]
name = "parking_lot"
version = "0.12.1"
@ -1344,9 +1441,9 @@ checksum = "4503fa043bf02cee09a9582e9554b4c6403b2ef55e4612e96561d294419429f8"
[[package]]
name = "poly1305"
version = "0.7.2"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede"
checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
dependencies = [
"cpufeatures",
"opaque-debug",
@ -1355,9 +1452,9 @@ dependencies = [
[[package]]
name = "polyval"
version = "0.5.3"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1"
checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb"
dependencies = [
"cfg-if",
"cpufeatures",
@ -1496,13 +1593,18 @@ dependencies = [
"hex",
"lazy_static",
"notify",
"openssl",
"p12",
"rand",
"rustls-native-certs",
"rustls-pemfile",
"serde",
"sha2",
"snowstorm",
"socket2 0.4.9",
"tokio",
"tokio-native-tls",
"tokio-rustls",
"tokio-tungstenite",
"tokio-util",
"toml",
@ -1512,6 +1614,15 @@ dependencies = [
"vergen",
]
[[package]]
name = "rc2"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62c64daa8e9438b84aaae55010a93f396f8e60e3911590fcba770d04643fc1dd"
dependencies = [
"cipher",
]
[[package]]
name = "redox_syscall"
version = "0.3.5"
@ -1565,6 +1676,20 @@ version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
[[package]]
name = "ring"
version = "0.17.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74"
dependencies = [
"cc",
"getrandom",
"libc",
"spin",
"untrusted",
"windows-sys 0.48.0",
]
[[package]]
name = "rustc-demangle"
version = "0.1.23"
@ -1593,6 +1718,60 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "rustls"
version = "0.22.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41"
dependencies = [
"log",
"ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-native-certs"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792"
dependencies = [
"openssl-probe",
"rustls-pemfile",
"rustls-pki-types",
"schannel",
"security-framework",
]
[[package]]
name = "rustls-pemfile"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35e4980fa29e4c4b212ffb3db068a564cbf560e51d3944b7c88bd8bf5bec64f4"
dependencies = [
"base64 0.21.4",
"rustls-pki-types",
]
[[package]]
name = "rustls-pki-types"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "048a63e5b3ac996d78d402940b5fa47973d2d080c6c6fffa1d0f19c4445310b7"
[[package]]
name = "rustls-webpki"
version = "0.102.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]]
name = "rustversion"
version = "1.0.14"
@ -1746,9 +1925,9 @@ checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
[[package]]
name = "snow"
version = "0.9.3"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c9d1425eb528a21de2755c75af4c9b5d57f50a0d4c3b7f1828a4cd03f8ba155"
checksum = "850948bee068e713b8ab860fe1adc4d109676ab4c3b621fd8147f06b261f2f85"
dependencies = [
"aes-gcm",
"blake2",
@ -1794,6 +1973,12 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "strsim"
version = "0.10.0"
@ -1986,6 +2171,17 @@ dependencies = [
"tokio",
]
[[package]]
name = "tokio-rustls"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f"
dependencies = [
"rustls",
"rustls-pki-types",
"tokio",
]
[[package]]
name = "tokio-stream"
version = "0.1.14"
@ -2068,7 +2264,7 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
dependencies = [
"futures-core",
"futures-util",
"indexmap",
"indexmap 1.9.3",
"pin-project",
"pin-project-lite",
"rand",
@ -2209,14 +2405,20 @@ dependencies = [
[[package]]
name = "universal-hash"
version = "0.4.0"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402"
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
dependencies = [
"generic-array",
"crypto-common",
"subtle",
]
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "url"
version = "2.4.1"
@ -2457,6 +2659,12 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "yasna"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd"
[[package]]
name = "zeroize"
version = "1.6.0"

View File

@ -11,18 +11,48 @@ build = "build.rs"
include = ["src/**/*", "LICENSE", "README.md", "build.rs"]
[features]
default = ["server", "client", "tls", "noise", "websocket", "hot-reload"]
default = [
"server",
"client",
"native-tls",
"noise",
"websocket-native-tls",
"hot-reload",
]
# Run as a server
server = []
# Run as a client
client = []
# TLS support
tls = ["tokio-native-tls"]
native-tls = ["tokio-native-tls"]
rustls = [
"tokio-rustls",
"rustls-pemfile",
"rustls-native-certs",
"p12",
]
# Noise support
noise = ["snowstorm", "base64"]
# Websocket support
websocket = ["tokio-tungstenite", "tokio-util", "futures-core", "futures-sink", "tls"]
websocket-native-tls = [
"tokio-tungstenite",
"tokio-util",
"futures-core",
"futures-sink",
"native-tls",
]
websocket-rustls = [
"tokio-tungstenite",
"tokio-util",
"futures-core",
"futures-sink",
"rustls",
]
# Configuration hot-reload support
hot-reload = ["notify"]
@ -67,24 +97,42 @@ hex = "0.4"
rand = "0.8"
backoff = { version = "0.4", features = ["tokio"] }
tracing = "0.1"
tracing-subscriber = { version="0.3", features=["env-filter"] }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
socket2 = { version = "0.4", features = ["all"] }
fdlimit = "0.2"
tokio-native-tls = { version = "0.3", optional = true }
async-trait = "0.1"
snowstorm = { version = "0.4", optional = true, features = ["stream"], default-features = false }
snowstorm = { version = "0.4", optional = true, features = [
"stream",
], default-features = false }
base64 = { version = "0.13", optional = true }
notify = { version = "5.0.0-pre.13", optional = true }
console-subscriber = { version = "0.1", optional = true, features = ["parking_lot"] }
console-subscriber = { version = "0.1", optional = true, features = [
"parking_lot",
] }
atty = "0.2"
async-http-proxy = { version = "1.2", features = ["runtime-tokio", "basic-auth"] }
async-http-proxy = { version = "1.2", features = [
"runtime-tokio",
"basic-auth",
] }
async-socks5 = "0.5"
url = { version = "2.2", features = ["serde"] }
tokio-tungstenite = { version="0.20.1", optional = true}
tokio-util = { version="0.7.9", optional = true, features = ["io"] }
futures-core = { version="0.3.28", optional = true }
futures-sink = { version="0.3.28", optional = true }
tokio-tungstenite = { version = "0.20.1", optional = true }
tokio-util = { version = "0.7.9", optional = true, features = ["io"] }
futures-core = { version = "0.3.28", optional = true }
futures-sink = { version = "0.3.28", optional = true }
tokio-native-tls = { version = "0.3", optional = true }
tokio-rustls = { version = "0.25", optional = true }
rustls-native-certs = { version = "0.7", optional = true }
rustls-pemfile = { version = "2.0", optional = true }
p12 = { version = "0.6.3", optional = true }
[target.'cfg(target_env = "musl")'.dependencies]
openssl = { version = "0.10", features = ["vendored"] }
[build-dependencies]
vergen = { version = "7.4.2", default-features = false, features = ["build", "git", "cargo"] }
vergen = { version = "7.4.2", default-features = false, features = [
"build",
"git",
"cargo",
] }
anyhow = "1.0"

View File

@ -55,7 +55,7 @@ Create `server.toml` with the following content and accommodate it to your needs
bind_addr = "0.0.0.0:2333" # `2333` specifies the port that rathole listens for clients
[server.services.my_nas_ssh]
token = "use_a_secret_that_only_you_know" # Token that is used to authenticate the client for the service. Change to a arbitrary value.
token = "use_a_secret_that_only_you_know" # Token that is used to authenticate the client for the service. Change to an arbitrary value.
bind_addr = "0.0.0.0:5202" # `5202` specifies the port that exposes `my_nas_ssh` to the Internet
```

View File

@ -1,30 +1,54 @@
# Build Guide
This is for those who want to build `rathole` themselves, possibly because the need of latest features or the minimal binary size.
## Build
To use default build settings, run:
```
```sh
cargo build --release
```
## Customize the build
You may need to pre-install [openssl](https://docs.rs/openssl/latest/openssl/index.html) dependencies in Unix-like systems.
## Customize the Build
`rathole` comes with lots of *crate features* that determine whether a certain feature will be compiled or not. Supported features can be checked out in `[features]` of [Cargo.toml](../Cargo.toml).
For example, to build `rathole` with the `client` and `noise` feature:
```
```sh
cargo build --release --no-default-features --features client,noise
```
## Rustls Support
`rathole` provides optional `rustls` support. It's an almost drop-in replacement of `native-tls` support. (See [Transport](transport.md) for more information.)
To enable this, disable the default features and enable `rustls` feature. And for websocket feature, enable `websocket-rustls` feature as well.
You can also use command line option for this. For example, to replace all default features with `rustls`:
```sh
cargo build --release --no-default-features --features server,client,rustls,noise,websocket-rustls,hot-reload
```
Feature `rustls` and `websocket-rustls` cannot be enabled with `native-tls` and `websocket-native-tls` at the same time, as they are mutually exclusive. Enabling both will result in a compile error.
(Note that default features contains `native-tls` and `websocket-native-tls`.)
## Minimalize the binary
1. Build with the `minimal` profile
The `release` build profile optimize for the program running time, not the binary size.
The `release` build profile optimize for the program running time, not the binary size.
However, the `minimal` profile enables lots of optimization for the binary size to produce a much smaller binary.
For example, to build `rathole` with `client` feature with the `minimal` profile:
```
```sh
cargo build --profile minimal --no-default-features --features client
```
@ -33,7 +57,8 @@ cargo build --profile minimal --no-default-features --features client
The binary that step 1 produces can be even smaller, by using `strip` and `upx` to remove the symbols and compress the binary.
Like:
```
```sh
strip rathole
upx --best --lzma rathole
```

View File

@ -30,5 +30,5 @@ When `rathole` starts in the client mode, it creates connections to `server.comm
When a control channel starts, the server challenge the client by a nonce, the client is required to authenticate as the service it wants to represent. Then the forwarding of that service is set up.
When the server accepts a connection on a service's `bind_port`, it sends a control command to the client via the corresponding contorl channel. Then the client connects to the server to create a data channel. In this way, a forwarding is set up. The server also creates a few data channels in advance to improve the latency.
When the server accepts a connection on a service's `bind_port`, it sends a control command to the client via the corresponding control channel. Then the client connects to the server to create a data channel. In this way, a forwarding is set up. The server also creates a few data channels in advance to improve the latency.

View File

@ -3,21 +3,27 @@
By default, `rathole` forwards traffic as it is. Different options can be enabled to secure the traffic.
## TLS
Checkout the [example](../examples/tls)
### Client
Normally, a self-signed certificate is used. In this case, the client needs to trust the CA. `trusted_root` is the path to the root CA's certificate PEM file.
`hostname` is the hostname that the client used to validate aginst the certificate that the server presents. Note that it does not have to be the same with the `remote_addr` in `[client]`.
```
```toml
[client.transport.tls]
trusted_root = "example/tls/rootCA.crt"
hostname = "localhost"
```
### Server
PKCS#12 archives are needed to run the server.
It can be created using openssl like:
```
```sh
openssl pkcs12 -export -out identity.pfx -inkey server.key -in server.crt -certfile ca_chain_certs.crt
```
@ -29,8 +35,22 @@ Aruguments are:
Creating self-signed certificate with one's own CA is a non-trival task. However, a script is provided under tls example folder for reference.
### Rustls Support
`rathole` provides optional `rustls` support. [Build Guide](build-guide.md) demostrated this.
One difference is that, the crate we use for loading PKCS#12 archives can only handle limited types of PBE algorithms. We only support PKCS#12 archives that they (crate `p12`) support. So we need to specify the legacy format (openssl 1.x format) when creating the PKCS#12 archive.
In short, the command used with openssl 3 to create the PKCS#12 archive with `rustls` support is:
```sh
openssl pkcs12 -export -out identity.pfx -inkey server.key -in server.crt -certfile ca_chain_certs.crt -legacy
```
## Noise Protocol
### Quickstart for the Noise Protocl
In one word, the [Noise Protocol](http://noiseprotocol.org/noise.html) is a lightweigt, easy to configure and drop-in replacement of TLS. No need to create a self-sign certificate to secure the connection.
`rathole` comes with a reasonable default configuration for noise protocol. You can a glimpse of the minimal [example](../examples/noise_nk) for how it will look like.
@ -38,12 +58,14 @@ In one word, the [Noise Protocol](http://noiseprotocol.org/noise.html) is a ligh
The default noise protocol that `rathole` uses, which is `Noise_NK_25519_ChaChaPoly_BLAKE2s`, providing the authentication of the server, just like TLS with properly configured certificates. So MITM is no more a problem.
To use it, a X25519 keypair is needed.
#### Generate a Keypair
1. Run `rathole --genkey`, which will generate a keypair using the default X25519 algorithm.
It emits:
```
```sh
$ rathole --genkey
Private Key:
cQ/vwIqNPJZmuM/OikglzBo/+jlYGrOt9i0k5h5vn1Q=
@ -51,11 +73,13 @@ cQ/vwIqNPJZmuM/OikglzBo/+jlYGrOt9i0k5h5vn1Q=
Public Key:
GQYTKSbWLBUSZiGfdWPSgek9yoOuaiwGD/GIX8Z1kkE=
```
(WARNING: Don't use the keypair from the Internet, including this one)
2. The server should keep the private key to identify itself. And the client should keep the public key, which is used to verify whether the peer is the authentic server.
So relevant snippets of configuration are:
```toml
# Client Side Configuration
[client.transport]
@ -73,9 +97,11 @@ local_private_key = "cQ/vwIqNPJZmuM/OikglzBo/+jlYGrOt9i0k5h5vn1Q="
Then `rathole` will run under the protection of the Noise Protocol.
## Specifying the Pattern of Noise Protocol
The default configuration of Noise Protocol that comes with `rathole` satifies most use cases, which is described above. But there're other patterns that can be useful.
### No Authentication
This configuration provides encryption of the traffic but provides no authentication, which means it's vulnerable to MITM attack, but is resistent to the sniffing and replay attack. If MITM attack is not one of the concerns, this is more convenient to use.
```toml
@ -107,6 +133,7 @@ remote_public_key = "server-pub-key-here"
### Other Patterns
To find out which pattern to use, refer to:
- [7.5. Interactive handshake patterns (fundamental)](https://noiseprotocol.org/noise.html#interactive-handshake-patterns-fundamental)
- [8. Protocol names and modifiers](https://noiseprotocol.org/noise.html#protocol-names-and-modifiers)

View File

@ -56,7 +56,8 @@ openssl x509 -req \
-sha256 -extfile cert.conf
# create pkcs12
openssl pkcs12 -export -out identity.pfx -inkey server.key -in server.crt -certfile rootCA.crt -passout pass:1234
openssl pkcs12 -export -out identity.pfx -inkey server.key -in server.crt -certfile rootCA.crt \
-passout pass:1234 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES
# clean up
rm server.csr csr.conf cert.conf
rm server.csr csr.conf cert.conf

Binary file not shown.

View File

@ -1,20 +1,20 @@
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----

View File

@ -8,8 +8,9 @@ use crate::protocol::{
};
use crate::transport::{AddrMaybeCached, SocketOpts, TcpTransport, Transport};
use anyhow::{anyhow, bail, Context, Result};
use backoff::backoff::Backoff;
use backoff::future::retry_notify;
use backoff::ExponentialBackoff;
use backoff::{backoff::Backoff, future::retry_notify};
use bytes::{Bytes, BytesMut};
use std::collections::HashMap;
use std::net::SocketAddr;
@ -22,9 +23,9 @@ use tracing::{debug, error, info, instrument, trace, warn, Instrument, Span};
#[cfg(feature = "noise")]
use crate::transport::NoiseTransport;
#[cfg(feature = "tls")]
#[cfg(any(feature = "native-tls", feature = "rustls"))]
use crate::transport::TlsTransport;
#[cfg(feature = "websocket")]
#[cfg(any(feature = "websocket-native-tls", feature = "websocket-rustls"))]
use crate::transport::WebsocketTransport;
use crate::constants::{run_control_chan_backoff, UDP_BUFFER_SIZE, UDP_SENDQ_SIZE, UDP_TIMEOUT};
@ -47,13 +48,13 @@ pub async fn run_client(
client.run(shutdown_rx, update_rx).await
}
TransportType::Tls => {
#[cfg(feature = "tls")]
#[cfg(any(feature = "native-tls", feature = "rustls"))]
{
let mut client = Client::<TlsTransport>::from(config).await?;
client.run(shutdown_rx, update_rx).await
}
#[cfg(not(feature = "tls"))]
crate::helper::feature_not_compile("tls")
#[cfg(not(any(feature = "native-tls", feature = "rustls")))]
crate::helper::feature_neither_compile("native-tls", "rustls")
}
TransportType::Noise => {
#[cfg(feature = "noise")]
@ -65,13 +66,13 @@ pub async fn run_client(
crate::helper::feature_not_compile("noise")
}
TransportType::Websocket => {
#[cfg(feature = "websocket")]
#[cfg(any(feature = "websocket-native-tls", feature = "websocket-rustls"))]
{
let mut client = Client::<WebsocketTransport>::from(config).await?;
client.run(shutdown_rx, update_rx).await
}
#[cfg(not(feature = "websocket"))]
crate::helper::feature_not_compile("websocket")
#[cfg(not(any(feature = "websocket-native-tls", feature = "websocket-rustls")))]
crate::helper::feature_neither_compile("websocket-native-tls", "websocket-rustls")
}
}
}

View File

@ -1,8 +1,9 @@
use anyhow::{anyhow, Result};
use anyhow::{anyhow, Context, Result};
use async_http_proxy::{http_connect_tokio, http_connect_tokio_with_basic_auth};
use backoff::{backoff::Backoff, Notify};
use socket2::{SockRef, TcpKeepalive};
use std::{future::Future, net::SocketAddr, time::Duration};
use tokio::io::{AsyncWrite, AsyncWriteExt};
use tokio::{
net::{lookup_host, TcpStream, ToSocketAddrs, UdpSocket},
sync::broadcast,
@ -42,6 +43,14 @@ pub fn feature_not_compile(feature: &str) -> ! {
)
}
#[allow(dead_code)]
pub fn feature_neither_compile(feature1: &str, feature2: &str) -> ! {
panic!(
"Neither of the feature '{}' or '{}' is compiled in this binary. Please re-compile rathole",
feature1, feature2
)
}
pub async fn to_socket_addr<A: ToSocketAddrs>(addr: A) -> Result<SocketAddr> {
lookup_host(addr)
.await?
@ -144,3 +153,14 @@ where
}
}
}
pub async fn write_and_flush<T>(conn: &mut T, data: &[u8]) -> Result<()>
where
T: AsyncWrite + Unpin,
{
conn.write_all(data)
.await
.with_context(|| "Failed to write data")?;
conn.flush().await.with_context(|| "Failed to flush data")?;
Ok(())
}

View File

@ -83,7 +83,7 @@ pub async fn run(args: Cli, shutdown_rx: broadcast::Receiver<bool>) -> Result<()
if let Some((i, _)) = last_instance {
info!("General configuration change detected. Restarting...");
shutdown_tx.send(true)?;
i.await?;
i.await??;
}
debug!("{:?}", config);
@ -119,8 +119,8 @@ async fn run_instance(
args: Cli,
shutdown_rx: broadcast::Receiver<bool>,
service_update: mpsc::Receiver<ConfigChange>,
) {
let ret: Result<()> = match determine_run_mode(&config, &args) {
) -> Result<()> {
match determine_run_mode(&config, &args) {
RunMode::Undetermine => panic!("Cannot determine running as a server or a client"),
RunMode::Client => {
#[cfg(not(feature = "client"))]
@ -134,8 +134,7 @@ async fn run_instance(
#[cfg(feature = "server")]
run_server(config, shutdown_rx, service_update).await
}
};
ret.unwrap();
}
}
#[derive(PartialEq, Eq, Debug)]

View File

@ -112,8 +112,7 @@ impl UdpTraffic {
}
pub async fn read<T: AsyncRead + Unpin>(reader: &mut T, hdr_len: u8) -> Result<UdpTraffic> {
let mut buf = Vec::new();
buf.resize(hdr_len as usize, 0);
let mut buf = vec![0; hdr_len as usize];
reader
.read_exact(&mut buf)
.await

View File

@ -1,7 +1,7 @@
use crate::config::{Config, ServerConfig, ServerServiceConfig, ServiceType, TransportType};
use crate::config_watcher::{ConfigChange, ServerServiceChange};
use crate::constants::{listen_backoff, UDP_BUFFER_SIZE};
use crate::helper::retry_notify_with_deadline;
use crate::helper::{retry_notify_with_deadline, write_and_flush};
use crate::multi_map::MultiMap;
use crate::protocol::Hello::{ControlChannelHello, DataChannelHello};
use crate::protocol::{
@ -25,9 +25,9 @@ use tracing::{debug, error, info, info_span, instrument, warn, Instrument, Span}
#[cfg(feature = "noise")]
use crate::transport::NoiseTransport;
#[cfg(feature = "tls")]
#[cfg(any(feature = "native-tls", feature = "rustls"))]
use crate::transport::TlsTransport;
#[cfg(feature = "websocket")]
#[cfg(any(feature = "websocket-native-tls", feature = "websocket-rustls"))]
use crate::transport::WebsocketTransport;
type ServiceDigest = protocol::Digest; // SHA256 of a service name
@ -57,13 +57,13 @@ pub async fn run_server(
server.run(shutdown_rx, update_rx).await?;
}
TransportType::Tls => {
#[cfg(feature = "tls")]
#[cfg(any(feature = "native-tls", feature = "rustls"))]
{
let mut server = Server::<TlsTransport>::from(config).await?;
server.run(shutdown_rx, update_rx).await?;
}
#[cfg(not(feature = "tls"))]
crate::helper::feature_not_compile("tls")
#[cfg(not(any(feature = "native-tls", feature = "rustls")))]
crate::helper::feature_neither_compile("native-tls", "rustls")
}
TransportType::Noise => {
#[cfg(feature = "noise")]
@ -75,13 +75,13 @@ pub async fn run_server(
crate::helper::feature_not_compile("noise")
}
TransportType::Websocket => {
#[cfg(feature = "websocket")]
#[cfg(any(feature = "websocket-native-tls", feature = "websocket-rustls"))]
{
let mut server = Server::<WebsocketTransport>::from(config).await?;
server.run(shutdown_rx, update_rx).await?;
}
#[cfg(not(feature = "websocket"))]
crate::helper::feature_not_compile("websocket")
#[cfg(not(any(feature = "websocket-native-tls", feature = "websocket-rustls")))]
crate::helper::feature_neither_compile("websocket-native-tls", "websocket-rustls")
}
}
@ -498,14 +498,9 @@ struct ControlChannel<T: Transport> {
impl<T: Transport> ControlChannel<T> {
async fn write_and_flush(&mut self, data: &[u8]) -> Result<()> {
self.conn
.write_all(data)
write_and_flush(&mut self.conn, data)
.await
.with_context(|| "Failed to write control cmds")?;
self.conn
.flush()
.await
.with_context(|| "Failed to flush control cmds")?;
Ok(())
}
// Run a control channel
@ -640,7 +635,7 @@ async fn run_tcp_connection_pool<T: Transport>(
'pool: while let Some(mut visitor) = visitor_rx.recv().await {
loop {
if let Some(mut ch) = data_ch_rx.recv().await {
if ch.write_all(&cmd).await.is_ok() {
if write_and_flush(&mut ch, &cmd).await.is_ok() {
tokio::spawn(async move {
let _ = copy_bidirectional(&mut ch, &mut visitor).await;
});
@ -690,7 +685,7 @@ async fn run_udp_connection_pool<T: Transport>(
.recv()
.await
.ok_or_else(|| anyhow!("No available data channels"))?;
conn.write_all(&cmd).await?;
write_and_flush(&mut conn, &cmd).await?;
let mut buf = [0u8; UDP_BUFFER_SIZE];
loop {

View File

@ -69,19 +69,30 @@ pub trait Transport: Debug + Send + Sync {
mod tcp;
pub use tcp::TcpTransport;
#[cfg(feature = "tls")]
mod tls;
#[cfg(feature = "tls")]
pub use tls::TlsTransport;
#[cfg(all(feature = "native-tls", feature = "rustls"))]
compile_error!("Only one of `native-tls` and `rustls` can be enabled");
#[cfg(feature = "native-tls")]
mod native_tls;
#[cfg(feature = "native-tls")]
use native_tls as tls;
#[cfg(feature = "rustls")]
mod rustls;
#[cfg(feature = "rustls")]
use rustls as tls;
#[cfg(any(feature = "native-tls", feature = "rustls"))]
pub(crate) use tls::TlsTransport;
#[cfg(feature = "noise")]
mod noise;
#[cfg(feature = "noise")]
pub use noise::NoiseTransport;
#[cfg(feature = "websocket")]
#[cfg(any(feature = "websocket-native-tls", feature = "websocket-rustls"))]
mod websocket;
#[cfg(feature = "websocket")]
#[cfg(any(feature = "websocket-native-tls", feature = "websocket-rustls"))]
pub use websocket::WebsocketTransport;
#[derive(Debug, Clone, Copy)]

View File

@ -1,14 +1,14 @@
use std::net::SocketAddr;
use super::{AddrMaybeCached, SocketOpts, TcpTransport, Transport};
use crate::config::{TlsConfig, TransportConfig};
use crate::helper::host_port_pair;
use crate::transport::{AddrMaybeCached, SocketOpts, TcpTransport, Transport};
use anyhow::{anyhow, Context, Result};
use async_trait::async_trait;
use std::fs;
use std::net::SocketAddr;
use tokio::net::{TcpListener, TcpStream, ToSocketAddrs};
use tokio_native_tls::native_tls::{self, Certificate, Identity};
use tokio_native_tls::{TlsAcceptor, TlsConnector, TlsStream};
pub(crate) use tokio_native_tls::TlsStream;
use tokio_native_tls::{TlsAcceptor, TlsConnector};
#[derive(Debug)]
pub struct TlsTransport {
@ -109,3 +109,8 @@ impl Transport for TlsTransport {
.await?)
}
}
#[cfg(feature = "websocket-native-tls")]
pub(crate) fn get_tcpstream(s: &TlsStream<TcpStream>) -> &TcpStream {
s.get_ref().get_ref().get_ref()
}

156
src/transport/rustls.rs Normal file
View File

@ -0,0 +1,156 @@
use crate::config::{TlsConfig, TransportConfig};
use crate::helper::host_port_pair;
use crate::transport::{AddrMaybeCached, SocketOpts, TcpTransport, Transport};
use std::fmt::Debug;
use std::fs;
use std::net::SocketAddr;
use std::sync::Arc;
use tokio::net::{TcpListener, TcpStream, ToSocketAddrs};
use tokio_rustls::rustls::pki_types::{CertificateDer, PrivatePkcs8KeyDer, ServerName};
use anyhow::{anyhow, Context, Result};
use async_trait::async_trait;
use p12::PFX;
use tokio_rustls::rustls::{ClientConfig, RootCertStore, ServerConfig};
pub(crate) use tokio_rustls::TlsStream;
use tokio_rustls::{TlsAcceptor, TlsConnector};
pub struct TlsTransport {
tcp: TcpTransport,
config: TlsConfig,
connector: Option<TlsConnector>,
tls_acceptor: Option<TlsAcceptor>,
}
// workaround for TlsConnector and TlsAcceptor not implementing Debug
impl Debug for TlsTransport {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TlsTransport")
.field("tcp", &self.tcp)
.field("config", &self.config)
.finish()
}
}
fn load_server_config(config: &TlsConfig) -> Result<Option<ServerConfig>> {
if let Some(pkcs12_path) = config.pkcs12.as_ref() {
let buf = fs::read(pkcs12_path)?;
let pfx = PFX::parse(buf.as_slice())?;
let pass = config.pkcs12_password.as_ref().unwrap();
let certs = pfx.cert_bags(pass)?;
let keys = pfx.key_bags(pass)?;
let chain: Vec<CertificateDer> = certs.into_iter().map(CertificateDer::from).collect();
let key = PrivatePkcs8KeyDer::from(keys.into_iter().next().unwrap());
Ok(Some(
ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(chain, key.into())?,
))
} else {
Ok(None)
}
}
fn load_client_config(config: &TlsConfig) -> Result<Option<ClientConfig>> {
let cert = if let Some(path) = config.trusted_root.as_ref() {
rustls_pemfile::certs(&mut std::io::BufReader::new(fs::File::open(path).unwrap()))
.map(|cert| cert.unwrap())
.next()
.with_context(|| "Failed to read certificate")?
} else {
// read from native
match rustls_native_certs::load_native_certs() {
Ok(certs) => certs.into_iter().next().unwrap(),
Err(e) => {
eprintln!("Failed to load native certs: {}", e);
return Ok(None);
}
}
};
let mut root_certs = RootCertStore::empty();
root_certs.add(cert).unwrap();
Ok(Some(
ClientConfig::builder()
.with_root_certificates(root_certs)
.with_no_client_auth(),
))
}
#[async_trait]
impl Transport for TlsTransport {
type Acceptor = TcpListener;
type RawStream = TcpStream;
type Stream = TlsStream<TcpStream>;
fn new(config: &TransportConfig) -> Result<Self> {
let tcp = TcpTransport::new(config)?;
let config = config
.tls
.as_ref()
.ok_or_else(|| anyhow!("Missing tls config"))?;
let connector = load_client_config(config)
.unwrap()
.map(|c| Arc::new(c).into());
let tls_acceptor = load_server_config(config)
.unwrap()
.map(|c| Arc::new(c).into());
Ok(TlsTransport {
tcp,
config: config.clone(),
connector,
tls_acceptor,
})
}
fn hint(conn: &Self::Stream, opt: SocketOpts) {
opt.apply(conn.get_ref().0);
}
async fn bind<A: ToSocketAddrs + Send + Sync>(&self, addr: A) -> Result<Self::Acceptor> {
let l = TcpListener::bind(addr)
.await
.with_context(|| "Failed to create tcp listener")?;
Ok(l)
}
async fn accept(&self, a: &Self::Acceptor) -> Result<(Self::RawStream, SocketAddr)> {
self.tcp
.accept(a)
.await
.with_context(|| "Failed to accept TCP connection")
}
async fn handshake(&self, conn: Self::RawStream) -> Result<Self::Stream> {
let conn = self.tls_acceptor.as_ref().unwrap().accept(conn).await?;
Ok(tokio_rustls::TlsStream::Server(conn))
}
async fn connect(&self, addr: &AddrMaybeCached) -> Result<Self::Stream> {
let conn = self.tcp.connect(addr).await?;
let connector = self.connector.as_ref().unwrap();
let host_name = self
.config
.hostname
.as_deref()
.unwrap_or(host_port_pair(&addr.addr)?.0);
Ok(tokio_rustls::TlsStream::Client(
connector
.connect(ServerName::try_from(host_name)?.to_owned(), conn)
.await?,
))
}
}
pub(crate) fn get_tcpstream(s: &TlsStream<TcpStream>) -> &TcpStream {
&s.get_ref().0
}

View File

@ -13,10 +13,14 @@ use futures_core::stream::Stream;
use futures_sink::Sink;
use tokio::io::{AsyncBufRead, AsyncRead, AsyncWrite, ReadBuf};
use tokio::net::{TcpListener, TcpStream, ToSocketAddrs};
use tokio_native_tls::TlsStream;
use tokio_tungstenite::tungstenite::protocol::WebSocketConfig;
use tokio_tungstenite::{accept_async_with_config, client_async_with_config};
use tokio_tungstenite::{tungstenite::protocol::Message, WebSocketStream};
#[cfg(any(feature = "native-tls", feature = "rustls"))]
use super::tls::get_tcpstream;
#[cfg(any(feature = "native-tls", feature = "rustls"))]
use super::tls::TlsStream;
use tokio_tungstenite::tungstenite::protocol::{Message, WebSocketConfig};
use tokio_tungstenite::{accept_async_with_config, client_async_with_config, WebSocketStream};
use tokio_util::io::StreamReader;
use url::Url;
@ -30,7 +34,7 @@ impl TransportStream {
fn get_tcpstream(&self) -> &TcpStream {
match self {
TransportStream::Insecure(s) => s,
TransportStream::Secure(s) => s.get_ref().get_ref().get_ref(),
TransportStream::Secure(s) => get_tcpstream(s),
}
}
}

View File

@ -1,4 +1,4 @@
use anyhow::Result;
use anyhow::{Ok, Result};
use common::{run_rathole_client, PING, PONG};
use rand::Rng;
use std::time::Duration;
@ -57,17 +57,17 @@ async fn tcp() -> Result<()> {
test("tests/for_tcp/tcp_transport.toml", Type::Tcp).await?;
// FIXME: Self-signed certificate on Mac requires mannual interference. Disable CI for now
#[cfg(not(target_os = "macos"))]
#[cfg(feature="tls")]
#[cfg(any(feature = "native-tls", feature = "rustls"))]
test("tests/for_tcp/tls_transport.toml", Type::Tcp).await?;
#[cfg(feature="noise")]
#[cfg(feature = "noise")]
test("tests/for_tcp/noise_transport.toml", Type::Tcp).await?;
#[cfg(feature="websocket")]
#[cfg(any(feature = "websocket-native-tls", feature = "websocket-rustls"))]
test("tests/for_tcp/websocket_transport.toml", Type::Tcp).await?;
#[cfg(not(target_os = "macos"))]
#[cfg(feature="websocket")]
#[cfg(any(feature = "websocket-native-tls", feature = "websocket-rustls"))]
test("tests/for_tcp/websocket_tls_transport.toml", Type::Tcp).await?;
Ok(())
@ -94,17 +94,17 @@ async fn udp() -> Result<()> {
test("tests/for_udp/tcp_transport.toml", Type::Udp).await?;
// See above
#[cfg(not(target_os = "macos"))]
#[cfg(feature="tls")]
#[cfg(any(feature = "native-tls", feature = "rustls"))]
test("tests/for_udp/tls_transport.toml", Type::Udp).await?;
#[cfg(feature="noise")]
#[cfg(feature = "noise")]
test("tests/for_udp/noise_transport.toml", Type::Udp).await?;
#[cfg(feature="websocket")]
#[cfg(any(feature = "websocket-native-tls", feature = "websocket-rustls"))]
test("tests/for_udp/websocket_transport.toml", Type::Udp).await?;
#[cfg(not(target_os = "macos"))]
#[cfg(feature="websocket")]
#[cfg(any(feature = "websocket-native-tls", feature = "websocket-rustls"))]
test("tests/for_udp/websocket_tls_transport.toml", Type::Udp).await?;
Ok(())
@ -112,6 +112,11 @@ async fn udp() -> Result<()> {
#[instrument]
async fn test(config_path: &'static str, t: Type) -> Result<()> {
if cfg!(not(all(feature = "client", feature = "server"))) {
// Skip the test if the client or the server is not enabled
return Ok(());
}
let (client_shutdown_tx, client_shutdown_rx) = broadcast::channel(1);
let (server_shutdown_tx, server_shutdown_rx) = broadcast::channel(1);