diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index c7ae3246..a6cf3d79 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -44,40 +44,40 @@ jobs: FEATURES: default - TARGET: x86_64-unknown-linux-musl # test in an alpine container on a mac OS: ubuntu-latest - FEATURES: ring-cipher,openssl-vendored + FEATURES: ring-cipher,wss - TARGET: aarch64-unknown-linux-musl # tested on aws t4g.nano in alpine container OS: ubuntu-latest - FEATURES: ring-cipher,openssl-vendored + FEATURES: ring-cipher,wss - TARGET: armv7-unknown-linux-musleabihf # raspberry pi 2-3-4, not tested OS: ubuntu-latest - FEATURES: openssl-vendored + FEATURES: ring-cipher,wss - TARGET: armv7-unknown-linux-musleabi # raspberry pi 2-3-4, not tested OS: ubuntu-latest - FEATURES: openssl-vendored + FEATURES: ring-cipher,wss - TARGET: arm-unknown-linux-musleabihf # raspberry pi 0-1, not tested OS: ubuntu-latest - FEATURES: ring-cipher,openssl-vendored + FEATURES: ring-cipher,wss - TARGET: arm-unknown-linux-musleabi # raspberry pi 0-1, not tested OS: ubuntu-latest - FEATURES: ring-cipher,openssl-vendored + FEATURES: ring-cipher,wss - TARGET: x86_64-apple-darwin # tested on a mac, is not properly signed so there are security warnings OS: macos-latest - FEATURES: ring-cipher,openssl-vendored + FEATURES: ring-cipher,wss - TARGET: aarch64-apple-darwin # tested on a mac, is not properly signed so there are security warnings OS: macos-latest - FEATURES: ring-cipher,openssl-vendored + FEATURES: ring-cipher,wss - TARGET: i686-pc-windows-msvc # tested on a windows machine OS: windows-2019 - FEATURES: ring-cipher,openssl-vendored + FEATURES: ring-cipher,wss - TARGET: x86_64-pc-windows-msvc # tested on a windows machine OS: windows-latest - FEATURES: ring-cipher,openssl-vendored + FEATURES: ring-cipher,wss - TARGET: mipsel-unknown-linux-musl # openwrt OS: ubuntu-latest - FEATURES: openssl-vendored,ring-cipher + FEATURES: ring-cipher,wss - TARGET: mips-unknown-linux-musl # openwrt OS: ubuntu-latest - FEATURES: openssl-vendored + FEATURES: ring-cipher,wss # needs: test runs-on: ${{ matrix.OS }} env: @@ -108,7 +108,7 @@ jobs: rustup set auto-self-update disable if [[ $OS =~ ^ubuntu.*$ ]]; then - sudo apt-get update && sudo apt-get install -qq crossbuild-essential-arm64 crossbuild-essential-armhf musl-tools + sudo apt-get update && sudo apt-get install clang llvm libc6-dev-i386 crossbuild-essential-arm64 crossbuild-essential-armhf musl-tools libboost-all-dev libc6-dev -y # curl -s musl.cc | grep mipsel case $TARGET in mipsel-unknown-linux-musl) @@ -193,7 +193,9 @@ jobs: EOF - name: Install rust target run: rustup target add $TARGET - - name: Run build + - name: Run build vn-link-cli + run: cargo build --package vn-link-cli --release --verbose --target $TARGET --features $FEATURES + - name: Run build vnt-cli run: cargo build --package vnt-cli --release --verbose --target $TARGET --features $FEATURES - name: List target run: find ./target @@ -202,17 +204,22 @@ jobs: mkdir -p ./artifacts # windows is the only OS using a different convention for executable file name if [[ $OS =~ ^windows.*$ ]]; then - EXEC=$NAME.exe + EXEC_VNT_CLI=vnt-cli.exe + EXEC_VN_LINK_CLI=vn-link-cli.exe else - EXEC=$NAME + EXEC_VNT_CLI=vnt-cli + EXEC_VN_LINK_CLI=vn-link-cli fi if [[ $GITHUB_REF_TYPE =~ ^tag$ ]]; then TAG=$GITHUB_REF_NAME else TAG=$GITHUB_SHA fi - mv ./target/$TARGET/release/$EXEC ./artifacts/$EXEC - tar -czf ./artifacts/$NAME-$TARGET-$TAG.tar.gz -C ./artifacts $EXEC + mv ./target/$TARGET/release/$EXEC_VNT_CLI ./artifacts/$EXEC_VNT_CLI + mv ./target/$TARGET/release/$EXEC_VN_LINK_CLI ./artifacts/$EXEC_VN_LINK_CLI + mv ./README ./artifacts/README.txt + cd ./artifacts + tar -czf vnt-$TARGET-$TAG.tar.gz * - name: Archive artifact uses: actions/upload-artifact@v2 with: diff --git a/.gitignore b/.gitignore index f5c9a395..a4867ea2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ target/* vnt/src/proto/* -vnt-cli/src/generated_serial_number.rs \ No newline at end of file +common/src/generated_serial_number.rs + +# RustRover +.idea \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index c8a796b7..66928473 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,24 +67,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" -[[package]] -name = "android_log-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ecc8056bf6ab9892dcd53216c83d1597487d7dacac16c8df6b877d127df9937" - -[[package]] -name = "android_logger" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c494134f746c14dc653a35a4ea5aca24ac368529da5370ecf41fe0341c35772f" -dependencies = [ - "android_log-sys", - "env_logger", - "log", - "once_cell", -] - [[package]] name = "android_system_properties" version = "0.1.5" @@ -106,6 +88,18 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +[[package]] +name = "attohttpc" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb8867f378f33f78a811a8eb9bf108ad99430d7aad43315dd9319c827ef6247" +dependencies = [ + "http 0.2.12", + "log", + "url", + "wildmatch", +] + [[package]] name = "autocfg" version = "1.2.0" @@ -127,12 +121,41 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags 2.5.0", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.60", + "which", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -192,19 +215,23 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.94" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" dependencies = [ "jobserver", "libc", + "once_cell", ] [[package]] -name = "cesu8" -version = "1.1.0" +name = "cexpr" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] [[package]] name = "cfg-if" @@ -268,18 +295,33 @@ dependencies = [ ] [[package]] -name = "combine" -version = "4.6.7" +name = "clang-sys" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ - "bytes", - "memchr", + "glob", + "libc", + "libloading", ] [[package]] name = "common" -version = "1.2.10" +version = "1.2.11" +dependencies = [ + "anyhow", + "chrono", + "console", + "gethostname", + "getopts", + "log", + "log4rs", + "rand", + "serde", + "serde_yaml", + "uuid", + "vnt", +] [[package]] name = "console" @@ -300,6 +342,16 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -315,6 +367,38 @@ dependencies = [ "libc", ] +[[package]] +name = "crossbeam" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + [[package]] name = "crossbeam-epoch" version = "0.9.18" @@ -359,6 +443,12 @@ dependencies = [ "cipher", ] +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + [[package]] name = "der" version = "0.7.9" @@ -423,28 +513,12 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" -[[package]] -name = "embed-manifest" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cd446c890d6bed1d8b53acef5f240069ebef91d6fae7c5f52efe61fe8b5eae" - [[package]] name = "encode_unicode" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" -[[package]] -name = "env_logger" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" -dependencies = [ - "log", - "regex", -] - [[package]] name = "equivalent" version = "1.0.1" @@ -473,6 +547,59 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -483,6 +610,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", +] + [[package]] name = "getopts" version = "0.2.21" @@ -521,6 +658,12 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "hashbrown" version = "0.12.3" @@ -548,6 +691,34 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" + [[package]] name = "humantime" version = "2.1.0" @@ -577,6 +748,29 @@ dependencies = [ "cc", ] +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "igd" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556b5a75cd4adb7c4ea21c64af1c48cefb2ce7d43dc4352c720a1fe47c21f355" +dependencies = [ + "attohttpc", + "log", + "rand", + "url", + "xmltree", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -614,32 +808,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bd11f3a29434026f5ff98c730b668ba74b1033637b8817940b54d040696133c" [[package]] -name = "itoa" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" - -[[package]] -name = "jni" -version = "0.21.1" +name = "itertools" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys", - "log", - "thiserror", - "walkdir", - "windows-sys 0.45.0", + "either", ] [[package]] -name = "jni-sys" -version = "0.3.0" +name = "itoa" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" @@ -668,6 +849,12 @@ dependencies = [ "spin 0.5.2", ] +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.153" @@ -764,6 +951,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "lwip-rs" +version = "0.1.0" +source = "git+https://github.com/lbl8603/lwip-rs#3133f0c3bde55333a27641182ae3550e7acc2a39" +dependencies = [ + "bindgen", + "cc", + "crossbeam", + "lazy_static", + "log", + "parking_lot", + "tokio", +] + [[package]] name = "lz4_flex" version = "0.11.3" @@ -776,6 +977,12 @@ version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.2" @@ -797,6 +1004,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-bigint" version = "0.4.4" @@ -886,6 +1103,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + [[package]] name = "openssl-src" version = "300.1.3+3.1.2" @@ -915,17 +1138,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "os_info" -version = "3.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" -dependencies = [ - "log", - "serde", - "windows-sys 0.52.0", -] - [[package]] name = "packet" version = "0.1.0" @@ -965,12 +1177,24 @@ dependencies = [ "base64ct", ] +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "pin-project-lite" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "pkcs1" version = "0.7.5" @@ -1027,6 +1251,16 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn 2.0.60", +] + [[package]] name = "proc-macro2" version = "1.0.81" @@ -1038,9 +1272,9 @@ dependencies = [ [[package]] name = "protobuf" -version = "3.4.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58678a64de2fced2bdec6bca052a6716a0efe692d6e3f53d1bda6a1def64cfc0" +checksum = "b55bad9126f378a853655831eb7363b7b01b81d19f8cb1218861086ca4a1a61e" dependencies = [ "once_cell", "protobuf-support", @@ -1049,9 +1283,9 @@ dependencies = [ [[package]] name = "protobuf-codegen" -version = "3.4.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32777b0b3f6538d9d2e012b3fad85c7e4b9244b5958d04a6415f4333782b7a77" +checksum = "0dd418ac3c91caa4032d37cb80ff0d44e2ebe637b2fb243b6234bf89cdac4901" dependencies = [ "anyhow", "once_cell", @@ -1064,9 +1298,9 @@ dependencies = [ [[package]] name = "protobuf-parse" -version = "3.4.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96cb37955261126624a25b5e6bda40ae34cf3989d52a783087ca6091b29b5642" +checksum = "9d39b14605eaa1f6a340aec7f320b34064feb26c93aec35d6a9a2272a8ddfa49" dependencies = [ "anyhow", "indexmap 1.9.3", @@ -1080,9 +1314,9 @@ dependencies = [ [[package]] name = "protobuf-support" -version = "3.4.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1ed294a835b0f30810e13616b1cd34943c6d1e84a8f3b0dcfe466d256c3e7e7" +checksum = "a5d4d7b8601c814cfb36bcebb79f0e61e45e1e93640cf778837833bbed05c372" dependencies = [ "thiserror", ] @@ -1267,6 +1501,12 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustix" version = "0.38.32" @@ -1280,6 +1520,60 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls" +version = "0.23.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a88d6d420651b496bdd98684116959239430022a115c1240e6c3993be0b15fba" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +dependencies = [ + "base64", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + +[[package]] +name = "rustls-webpki" +version = "0.102.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "ryu" version = "1.0.17" @@ -1287,12 +1581,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] -name = "same-file" -version = "1.0.6" +name = "schannel" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "winapi-util", + "windows-sys 0.52.0", ] [[package]] @@ -1301,6 +1595,29 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +dependencies = [ + "bitflags 2.5.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.198" @@ -1355,6 +1672,17 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.8" @@ -1366,6 +1694,12 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook" version = "0.3.17" @@ -1395,6 +1729,15 @@ dependencies = [ "rand_core", ] +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "smallvec" version = "1.13.2" @@ -1533,6 +1876,21 @@ dependencies = [ "winapi", ] +[[package]] +name = "tinyvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.37.0" @@ -1563,6 +1921,34 @@ dependencies = [ "syn 2.0.60", ] +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" +dependencies = [ + "futures-util", + "log", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite", + "webpki-roots", +] + [[package]] name = "tun" version = "0.1.0" @@ -1576,6 +1962,26 @@ dependencies = [ "winapi", ] +[[package]] +name = "tungstenite" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.1.0", + "httparse", + "log", + "rand", + "rustls", + "rustls-pki-types", + "sha1", + "thiserror", + "utf-8", +] + [[package]] name = "typemap-ors" version = "1.0.0" @@ -1591,17 +1997,32 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "universal-hash" @@ -1634,6 +2055,23 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "uuid" version = "1.8.0" @@ -1655,9 +2093,32 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vn-link" +version = "1.2.11" +dependencies = [ + "anyhow", + "crossbeam-utils", + "log", + "lwip-rs", + "parking_lot", + "tokio", + "vnt", +] + +[[package]] +name = "vn-link-cli" +version = "1.2.11" +dependencies = [ + "common", + "log", + "tokio", + "vn-link", +] + [[package]] name = "vnt" -version = "1.2.10" +version = "1.2.11" dependencies = [ "aes", "aes-gcm", @@ -1672,6 +2133,9 @@ dependencies = [ "crossbeam-utils", "dns-parser", "ecb", + "fnv", + "futures-util", + "igd", "libc", "libloading", "libsm", @@ -1687,62 +2151,33 @@ dependencies = [ "rand", "ring", "rsa", + "rustls", "sha2", "socket2", "spki", "stun-format", "thiserror", "tokio", + "tokio-tungstenite", "tun", "zstd", ] [[package]] name = "vnt-cli" -version = "1.2.10" +version = "1.2.11" dependencies = [ "anyhow", "chrono", "common", - "console", - "embed-manifest", - "getopts", "log", - "log4rs", - "os_info", "rand", - "serde", - "serde_yaml", "signal-hook", "sudo", - "uuid", "vnt", "winapi", ] -[[package]] -name = "vnt-jni" -version = "1.2.10" -dependencies = [ - "android_logger", - "common", - "jni", - "log", - "parking_lot", - "spki", - "vnt", -] - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1803,6 +2238,15 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "webpki-roots" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "which" version = "4.4.2" @@ -1821,6 +2265,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" +[[package]] +name = "wildmatch" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f44b95f62d34113cf558c93511ac93027e03e9c29a60dd0fd70e6e025c7270a" + [[package]] name = "winapi" version = "0.3.9" @@ -1837,15 +2287,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -1861,15 +2302,6 @@ dependencies = [ "windows-targets 0.52.5", ] -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -1888,21 +2320,6 @@ dependencies = [ "windows-targets 0.52.5", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-targets" version = "0.48.5" @@ -1934,12 +2351,6 @@ dependencies = [ "windows_x86_64_msvc 0.52.5", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -1952,12 +2363,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -1970,12 +2375,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -1994,12 +2393,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -2012,12 +2405,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -2030,12 +2417,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -2048,12 +2429,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -2066,6 +2441,21 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +[[package]] +name = "xml-rs" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" + +[[package]] +name = "xmltree" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7d8a75eaf6557bb84a65ace8609883db44a29951042ada9b393151532e41fcb" +dependencies = [ + "xml-rs", +] + [[package]] name = "yasna" version = "0.5.2" diff --git a/Cargo.toml b/Cargo.toml index 0fad732d..876febaf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,11 @@ [workspace] -members = ["vnt","common","vnt-cli","vnt-jni"] +members = ["vnt", "common", "vnt-cli", "vn-link", "vn-link-cli"] [profile.release] opt-level = 'z' debug = 0 debug-assertions = false -strip= "debuginfo" +strip = "debuginfo" lto = true panic = 'abort' incremental = false diff --git a/README b/README new file mode 100644 index 00000000..85677f24 --- /dev/null +++ b/README @@ -0,0 +1,14 @@ +一、程序说明 + 1. vnt-cli vnt的命令行程序 + 2. vn-link-cli 功能和vnt-cli基本一致,但是不依赖tun、不改变本地路由、不需要管理员/root权限 + +二、使用说明 + 使用-k参数构建虚拟网络 + + +1. Program Description + a. vnt-cli: Command-line program for VNT. + b. vn-link-cli: Functions similarly to vnt-cli, but does not depend on TUN, does not change local routing, and does not require administrator/root permissions. + +2. Instructions for Use + Use the -k parameter to create a virtual network. \ No newline at end of file diff --git a/common/Cargo.toml b/common/Cargo.toml index 16e39e1e..80fa20b1 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -1,8 +1,45 @@ [package] name = "common" -version = "1.2.10" +version = "1.2.11" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +vnt = { path = "../vnt", package = "vnt", default-features = false } +anyhow = "1.0.82" +console = "0.15.2" +log = "0.4.17" +log4rs = { version = "1.3.0", optional = true } +serde = { version = "1.0", features = ["derive"] } +serde_yaml = "0.9.32" +getopts = "0.2.21" +gethostname = "0.4.3" +uuid = { version = "1.8.0", features = ["v4"] } + +[features] +default = [] +openssl = ["vnt/openssl"] +openssl-vendored = ["vnt/openssl-vendored"] +ring-cipher = ["vnt/ring-cipher"] +aes_cbc = ["vnt/aes_cbc"] +aes_ecb = ["vnt/aes_ecb"] +sm4_cbc = ["vnt/sm4_cbc"] +aes_gcm = ["vnt/aes_gcm"] +chacha20_poly1305 = ["vnt/chacha20_poly1305"] +server_encrypt = ["vnt/server_encrypt"] +ip_proxy = ["vnt/ip_proxy"] +port_mapping = ["vnt/port_mapping"] +lz4 = ["vnt/lz4_compress"] +zstd = ["vnt/zstd_compress"] +upnp = ["vnt/upnp"] +ws = ["vnt/ws"] +wss = ["vnt/wss"] +command = [] +file_config = [] +log = ["log4rs"] +integrated_tun = ["vnt/integrated_tun"] + +[build-dependencies] +rand = "0.8.5" +chrono = "0.4.23" \ No newline at end of file diff --git a/vnt-cli/build.rs b/common/build.rs similarity index 100% rename from vnt-cli/build.rs rename to common/build.rs diff --git a/vnt-cli/src/callback.rs b/common/src/callback.rs similarity index 86% rename from vnt-cli/src/callback.rs rename to common/src/callback.rs index 7ac67c7f..a1801a3d 100644 --- a/vnt-cli/src/callback.rs +++ b/common/src/callback.rs @@ -1,9 +1,7 @@ use std::process; use console::style; - -use vnt::handle::callback::{ConnectInfo, ErrorType}; -use vnt::{DeviceInfo, ErrorInfo, HandshakeInfo, RegisterInfo, VntCallback}; +use vnt::{ConnectInfo, ErrorInfo, ErrorType, HandshakeInfo, RegisterInfo, VntCallback}; #[derive(Clone)] pub struct VntHandler {} @@ -12,7 +10,8 @@ impl VntCallback for VntHandler { fn success(&self) { println!(" {} ", style("====== Connect Successfully ======").green()) } - fn create_tun(&self, info: DeviceInfo) { + #[cfg(feature = "vnt-model")] + fn create_tun(&self, info: vnt::DeviceInfo) { println!("create_tun {}", info) } diff --git a/common/src/cli.rs b/common/src/cli.rs new file mode 100644 index 00000000..f5c5c8b2 --- /dev/null +++ b/common/src/cli.rs @@ -0,0 +1,482 @@ +use crate::args_parse::{ips_parse, out_ips_parse}; +#[cfg(feature = "command")] +use crate::command; +use crate::{config, generated_serial_number}; +use anyhow::anyhow; +use console::style; +use getopts::Options; +use std::io; +use std::net::Ipv4Addr; +use std::path::PathBuf; +use std::str::FromStr; +use vnt::channel::punch::PunchModel; +use vnt::channel::UseChannelType; +use vnt::cipher::CipherModel; +use vnt::compression::Compressor; +use vnt::core::Config; + +pub fn app_home() -> io::Result { + let root_path = match std::env::current_exe() { + Ok(path) => { + if let Some(v) = path.as_path().parent() { + v.to_path_buf() + } else { + log::warn!("current_exe parent none:{:?}", path); + PathBuf::new() + } + } + Err(e) => { + log::warn!("current_exe err:{:?}", e); + PathBuf::new() + } + }; + let path = root_path.join("env"); + if !path.exists() { + std::fs::create_dir_all(&path)?; + } + Ok(path) +} + +pub fn parse_args_config() -> anyhow::Result, bool)>> { + #[cfg(feature = "log")] + let _ = log4rs::init_file("log4rs.yaml", Default::default()); + let args: Vec = std::env::args().collect(); + let program = args[0].clone(); + let mut opts = Options::new(); + opts.optopt("k", "", "组网标识", ""); + opts.optopt("n", "", "设备名称", ""); + opts.optopt("d", "", "设备标识", ""); + opts.optflag("c", "", "关闭交互式命令"); + opts.optopt("s", "", "注册和中继服务器地址", ""); + opts.optmulti("e", "", "stun服务器", ""); + opts.optflag("a", "", "使用tap模式"); + opts.optopt("", "nic", "虚拟网卡名称,windows下使用tap则必填", ""); + opts.optmulti("i", "", "配置点对网(IP代理)入站时使用", ""); + opts.optmulti("o", "", "配置点对网出站时使用", ""); + opts.optopt("w", "", "客户端加密", ""); + opts.optflag("W", "", "服务端加密"); + opts.optopt("u", "", "自定义mtu(默认为1430)", ""); + opts.optopt("", "ip", "指定虚拟ip", ""); + opts.optflag("", "relay", "仅使用服务器转发"); + opts.optopt("", "par", "任务并行度(必须为正整数)", ""); + opts.optopt("", "model", "加密模式", ""); + opts.optflag("", "finger", "指纹校验"); + opts.optopt("", "punch", "取值ipv4/ipv6", ""); + opts.optopt("", "ports", "监听的端口", ""); + opts.optflag("", "cmd", "开启窗口输入"); + opts.optflag("", "no-proxy", "关闭内置代理"); + opts.optflag("", "first-latency", "优先延迟"); + opts.optopt("", "use-channel", "使用通道 relay/p2p", ""); + opts.optopt("", "packet-loss", "丢包率", ""); + opts.optopt("", "packet-delay", "延迟", ""); + opts.optmulti("", "dns", "dns", ""); + opts.optmulti("", "mapping", "mapping", ""); + opts.optmulti("", "vnt-mapping", "vnt-mapping", ""); + opts.optopt("f", "", "配置文件", ""); + opts.optopt("", "compressor", "压缩算法", ""); + opts.optflag("", "disable-stats", "关闭流量统计"); + //"后台运行时,查看其他设备列表" + opts.optflag("", "add", "后台运行时,添加地址"); + opts.optflag("", "list", "后台运行时,查看其他设备列表"); + opts.optflag("", "all", "后台运行时,查看其他设备完整信息"); + opts.optflag("", "info", "后台运行时,查看当前设备信息"); + opts.optflag("", "route", "后台运行时,查看数据转发路径"); + opts.optflag("", "chart_a", "后台运行时,查看流量统计"); + opts.optopt("", "chart_b", "后台运行时,查看流量统计", ""); + opts.optflag("", "stop", "停止后台运行"); + opts.optflag("h", "help", "帮助"); + let matches = match opts.parse(&args[1..]) { + Ok(m) => m, + Err(f) => { + print_usage(&program, opts); + return Err(anyhow::anyhow!("{}", f.to_string())); + } + }; + if matches.opt_present("h") || args.len() == 1 { + print_usage(&program, opts); + return Ok(None); + } + + #[cfg(feature = "command")] + if matches.opt_present("list") { + command::command(command::CommandEnum::List); + return Ok(None); + } else if matches.opt_present("info") { + command::command(command::CommandEnum::Info); + return Ok(None); + } else if matches.opt_present("stop") { + command::command(command::CommandEnum::Stop); + return Ok(None); + } else if matches.opt_present("route") { + command::command(command::CommandEnum::Route); + return Ok(None); + } else if matches.opt_present("all") { + command::command(command::CommandEnum::All); + return Ok(None); + } else if matches.opt_present("chart_a") { + command::command(command::CommandEnum::ChartA); + return Ok(None); + } + if let Some(v) = matches.opt_str("chart_b") { + command::command(command::CommandEnum::ChartB(v)); + return Ok(None); + } + let conf = matches.opt_str("f"); + let (config, vnt_link_config, cmd) = if conf.is_some() { + match config::read_config(&conf.unwrap()) { + Ok(c) => c, + Err(e) => { + return Err(anyhow::anyhow!("conf err {}", e)); + } + } + } else { + if !matches.opt_present("k") { + print_usage(&program, opts); + return Err(anyhow::anyhow!("parameter -k not found .")); + } + #[cfg(target_os = "windows")] + #[cfg(feature = "integrated_tun")] + let tap = matches.opt_present("a"); + #[cfg(feature = "integrated_tun")] + let device_name = matches.opt_str("nic"); + let token: String = matches.opt_get("k").unwrap().unwrap(); + let device_id = matches.opt_get_default("d", String::new()).unwrap(); + let device_id = if device_id.is_empty() { + config::get_device_id() + } else { + device_id + }; + if device_id.is_empty() { + print_usage(&program, opts); + return Err(anyhow::anyhow!("parameter -d not found .")); + } + let name = matches + .opt_get_default( + "n", + gethostname::gethostname() + .to_str() + .unwrap_or("UnknownName") + .to_string(), + ) + .unwrap(); + let server_address_str = matches + .opt_get_default("s", "vnt.wherewego.top:29872".to_string()) + .unwrap(); + + let mut stun_server = matches.opt_strs("e"); + if stun_server.is_empty() { + for x in config::PUB_STUN { + stun_server.push(x.to_string()); + } + } + let dns = matches.opt_strs("dns"); + let in_ip = matches.opt_strs("i"); + let in_ip = match ips_parse(&in_ip) { + Ok(in_ip) => in_ip, + Err(e) => { + print_usage(&program, opts); + println!(); + println!("-i: {:?} {}", in_ip, e); + return Err(anyhow::anyhow!("example: -i 192.168.0.0/24,10.26.0.3")); + } + }; + let out_ip = matches.opt_strs("o"); + let out_ip = match out_ips_parse(&out_ip) { + Ok(out_ip) => out_ip, + Err(e) => { + print_usage(&program, opts); + println!(); + println!("-o: {:?} {}", out_ip, e); + return Err(anyhow::anyhow!("example: -o 0.0.0.0/0")); + } + }; + let password: Option = matches.opt_get("w").unwrap(); + let server_encrypt = matches.opt_present("W"); + #[cfg(not(feature = "server_encrypt"))] + { + if server_encrypt { + println!("Server encryption not supported"); + return Err(anyhow::anyhow!("Server encryption not supported")); + } + } + let mtu: Option = matches.opt_get("u").unwrap(); + let mtu = if let Some(mtu) = mtu { + match u32::from_str(&mtu) { + Ok(mtu) => Some(mtu), + Err(e) => { + print_usage(&program, opts); + println!(); + println!("'-u {}' {}", mtu, e); + return Err(anyhow::anyhow!("'-u {}' {}", mtu, e)); + } + } + } else { + None + }; + let virtual_ip: Option = matches.opt_get("ip").unwrap(); + let virtual_ip = + virtual_ip.map(|v| Ipv4Addr::from_str(&v).expect(&format!("'--ip {}' error", v))); + if let Some(virtual_ip) = virtual_ip { + if virtual_ip.is_unspecified() || virtual_ip.is_broadcast() || virtual_ip.is_multicast() + { + return Err(anyhow::anyhow!("'--ip {}' invalid", virtual_ip)); + } + } + let relay = matches.opt_present("relay"); + + let cipher_model = match matches.opt_get::("model") { + Ok(model) => { + #[cfg(not(any(feature = "aes_gcm", feature = "server_encrypt")))] + { + if password.is_some() && model.is_none() { + return Err(anyhow::anyhow!("'--model ' undefined")); + } + model.unwrap_or(CipherModel::None) + } + #[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))] + model.unwrap_or(CipherModel::AesGcm) + } + Err(e) => { + return Err(anyhow::anyhow!("'--model ' invalid,{}", e)); + } + }; + + let finger = matches.opt_present("finger"); + let punch_model = matches + .opt_get::("punch") + .unwrap() + .unwrap_or(PunchModel::All); + let use_channel_type = matches + .opt_get::("use-channel") + .unwrap() + .unwrap_or_else(|| { + if relay { + UseChannelType::Relay + } else { + UseChannelType::All + } + }); + + let ports = matches + .opt_get::("ports") + .unwrap_or(None) + .map(|v| v.split(",").map(|x| x.parse().unwrap_or(0)).collect()); + + let cmd = matches.opt_present("cmd"); + #[cfg(feature = "ip_proxy")] + #[cfg(feature = "integrated_tun")] + let no_proxy = matches.opt_present("no-proxy"); + let first_latency = matches.opt_present("first-latency"); + let packet_loss = matches + .opt_get::("packet-loss") + .expect("--packet-loss"); + let packet_delay = matches + .opt_get::("packet-delay") + .expect("--packet-delay") + .unwrap_or(0); + #[cfg(feature = "port_mapping")] + let port_mapping_list = matches.opt_strs("mapping"); + let vnt_mapping_list = matches.opt_strs("vnt-mapping"); + let disable_stats = matches.opt_present("disable-stats"); + let compressor = if let Some(compressor) = matches.opt_str("compressor").as_ref() { + Compressor::from_str(compressor) + .map_err(|e| anyhow!("{}", e)) + .unwrap() + } else { + Compressor::None + }; + let config = match Config::new( + #[cfg(feature = "integrated_tun")] + #[cfg(target_os = "windows")] + tap, + token, + device_id, + name, + server_address_str, + dns, + stun_server, + in_ip, + out_ip, + password, + mtu, + virtual_ip, + #[cfg(feature = "integrated_tun")] + #[cfg(feature = "ip_proxy")] + no_proxy, + server_encrypt, + cipher_model, + finger, + punch_model, + ports, + first_latency, + #[cfg(feature = "integrated_tun")] + device_name, + use_channel_type, + packet_loss, + packet_delay, + #[cfg(feature = "port_mapping")] + port_mapping_list, + compressor, + !disable_stats, + ) { + Ok(config) => config, + Err(e) => { + println!("config error: {}", e); + std::process::exit(1); + } + }; + (config, vnt_mapping_list, cmd) + }; + println!("version {}", vnt::VNT_VERSION); + println!("Serial:{}", generated_serial_number::SERIAL_NUMBER); + log::info!( + "version:{},Serial:{}", + vnt::VNT_VERSION, + generated_serial_number::SERIAL_NUMBER + ); + Ok(Some((config, vnt_link_config, cmd))) +} + +fn print_usage(program: &str, _opts: Options) { + println!("Usage: {} [options]", program); + println!("version:{}", vnt::VNT_VERSION); + println!("Serial:{}", generated_serial_number::SERIAL_NUMBER); + println!("Options:"); + println!( + " -k {}", + green("使用相同的token,就能组建一个局域网络".to_string()) + ); + println!(" -n 给设备一个名字,便于区分不同设备,默认使用系统版本"); + println!(" -d 设备唯一标识符,不使用--ip参数时,服务端凭此参数分配虚拟ip,注意不能重复"); + println!( + " -s 注册和中继服务器地址,协议支持使用tcp://和ws://和wss://,默认为udp://" + ); + println!(" -e stun服务器,用于探测NAT类型,可使用多个地址,如-e stun.miwifi.com -e turn.cloudflare.com"); + #[cfg(target_os = "windows")] + #[cfg(feature = "integrated_tun")] + println!( + " -a 使用tap模式,默认使用tun模式,使用tap时需要配合'--nic'参数指定tap网卡" + ); + println!(" -i 配置点对网(IP代理)时使用,-i 192.168.0.0/24,10.26.0.3表示允许接收网段192.168.0.0/24的数据"); + println!(" 并转发到10.26.0.3,可指定多个网段"); + println!(" -o 配置点对网时使用,-o 192.168.0.0/24表示允许将数据转发到192.168.0.0/24,可指定多个网段"); + + println!(" -w 使用该密码生成的密钥对客户端数据进行加密,并且服务端无法解密,使用相同密码的客户端才能通信"); + #[cfg(feature = "server_encrypt")] + println!(" -W 加密当前客户端和服务端通信的数据,请留意服务端指纹是否正确"); + println!(" -u 自定义mtu(不加密默认为1450,加密默认为1410)"); + #[cfg(feature = "file_config")] + println!(" -f 读取配置文件中的配置"); + + println!(" --ip 指定虚拟ip,指定的ip不能和其他设备重复,必须有效并且在服务端所属网段下,默认情况由服务端分配"); + let mut enums = String::new(); + #[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))] + enums.push_str("/aes_gcm"); + #[cfg(feature = "chacha20_poly1305")] + enums.push_str("/chacha20_poly1305/chacha20"); + #[cfg(feature = "aes_cbc")] + enums.push_str("/aes_cbc"); + #[cfg(feature = "aes_ecb")] + enums.push_str("/aes_ecb"); + #[cfg(feature = "sm4_cbc")] + enums.push_str("/sm4_cbc"); + enums.push_str("/xor"); + println!( + " --model 加密模式(默认aes_gcm),可选值{}", + &enums[1..] + ); + #[cfg(any( + feature = "aes_gcm", + feature = "chacha20_poly1305", + feature = "server_encrypt", + feature = "aes_cbc", + feature = "aes_ecb", + feature = "sm4_cbc" + ))] + println!(" --finger 增加数据指纹校验,可增加安全性,如果服务端开启指纹校验,则客户端也必须开启"); + println!(" --punch 取值ipv4/ipv6/all,ipv4表示仅使用ipv4打洞"); + println!(" --ports 取值0~65535,指定本地监听的一组端口,默认监听两个随机端口,使用过多端口会增加网络负担"); + #[cfg(feature = "command")] + println!(" --cmd 开启交互式命令,使用此参数开启控制台输入"); + #[cfg(feature = "ip_proxy")] + #[cfg(feature = "integrated_tun")] + println!(" --no-proxy 关闭内置代理,如需点对网则需要配置网卡NAT转发"); + println!(" --first-latency 优先低延迟的通道,默认情况优先使用p2p通道"); + println!(" --use-channel 使用通道 relay/p2p/all,默认两者都使用"); + #[cfg(not(feature = "vn-link-model"))] + println!(" --nic 指定虚拟网卡名称"); + println!(" --packet-loss <0> 模拟丢包,取值0~1之间的小数,程序会按设定的概率主动丢包,可用于模拟弱网"); + println!( + " --packet-delay <0> 模拟延迟,整数,单位毫秒(ms),程序会按设定的值延迟发包,可用于模拟弱网" + ); + println!(" --dns DNS服务器地址,可使用多个dns,不指定时使用系统解析"); + + #[cfg(feature = "port_mapping")] + println!(" --mapping 端口映射,例如 --mapping udp:0.0.0.0:80-domain:80 映射目标是本地路由能访问的设备"); + + #[cfg(all(feature = "lz4", feature = "zstd"))] + println!(" --compressor 启用压缩,可选值lz4/zstd<,level>,level为压缩级别,例如 --compressor lz4 或--compressor zstd,10"); + #[cfg(feature = "lz4")] + #[cfg(not(feature = "zstd"))] + println!(" --compressor 启用压缩,可选值lz4,例如 --compressor lz4"); + #[cfg(feature = "zstd")] + #[cfg(not(feature = "lz4"))] + println!(" --compressor 启用压缩,可选值zstd<,level>,level为压缩级别,例如 --compressor zstd,10"); + + #[cfg(not(feature = "integrated_tun"))] + println!( + " --vnt-mapping {}", + green( + "vnt地址映射,例如 --vnt-mapping tcp:80-10.26.0.10:80 映射目标是vnt网络或其子网中的设备" + .to_string() + ) + ); + println!(" --disable-stats 关闭流量统计"); + println!(); + #[cfg(feature = "command")] + { + // #[cfg(not(feature = "integrated_tun"))] + // println!( + // " --add {}", + // yellow("后台运行时,添加VNT地址映射 用法同'--vnt-mapping'".to_string()) + // ); + println!( + " --list {}", + yellow("后台运行时,查看其他设备列表".to_string()) + ); + println!( + " --all {}", + yellow("后台运行时,查看其他设备完整信息".to_string()) + ); + println!( + " --info {}", + yellow("后台运行时,查看当前设备信息".to_string()) + ); + println!( + " --route {}", + yellow("后台运行时,查看数据转发路径".to_string()) + ); + println!( + " --chart_a {}", + yellow("后台运行时,查看所有IP的流量统计".to_string()) + ); + println!( + " --chart_b {}", + yellow("后台运行时,查看单个IP的历史流量".to_string()) + ); + println!( + " --stop {}", + yellow("停止后台运行".to_string()) + ); + } + println!(" -h, --help 帮助"); +} + +fn green(str: String) -> impl std::fmt::Display { + style(str).green() +} + +#[cfg(feature = "command")] +fn yellow(str: String) -> impl std::fmt::Display { + style(str).yellow() +} diff --git a/vnt-cli/src/command/client.rs b/common/src/command/client.rs similarity index 66% rename from vnt-cli/src/command/client.rs rename to common/src/command/client.rs index 0630c790..e6caab9b 100644 --- a/vnt-cli/src/command/client.rs +++ b/common/src/command/client.rs @@ -4,10 +4,10 @@ use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket}; use std::str::FromStr; use std::time::Duration; -use crate::command::entity::{DeviceItem, Info, RouteItem}; +use crate::command::entity::{ChartA, ChartB, DeviceItem, Info, RouteItem}; pub struct CommandClient { - buf: [u8; 10240], + buf: Vec, udp: UdpSocket, } @@ -25,12 +25,12 @@ impl CommandClient { )))?; Ok(Self { udp, - buf: [0; 10240], + buf: vec![0; 65536 * 8], }) } } fn read_command_port() -> io::Result { - let path_buf = crate::app_home()?.join("command-port"); + let path_buf = crate::cli::app_home()?.join("command-port"); let port = std::fs::read_to_string(path_buf)?; match u16::from_str(&port) { Ok(port) => Ok(port), @@ -53,14 +53,33 @@ impl CommandClient { pub fn info(&mut self) -> io::Result { self.send_cmd(b"info") } + pub fn chart_a(&mut self) -> io::Result { + self.send_cmd(b"chart_a") + } + pub fn chart_b(&mut self, input: &str) -> io::Result { + let cmd = if input.is_empty() { + "chart_b".to_string() + } else { + format!("chart_b:{}", input) + }; + self.send_cmd(cmd.as_bytes()) + } fn send_cmd<'a, V: Deserialize<'a>>(&'a mut self, cmd: &[u8]) -> io::Result { self.udp.send(cmd)?; let len = self.udp.recv(&mut self.buf)?; match serde_yaml::from_slice::(&self.buf[..len]) { Ok(val) => Ok(val), Err(e) => { - log::error!("{:?},{:?}", &self.buf[..len], e); - Err(io::Error::new(io::ErrorKind::Other, "data error")) + log::error!( + "send_cmd {:?} {:?},{:?}", + std::str::from_utf8(cmd), + std::str::from_utf8(&self.buf[..len]), + e + ); + Err(io::Error::new( + io::ErrorKind::Other, + format!("data error {:?} buf_len={}", e, len), + )) } } } diff --git a/vnt-cli/src/command/entity.rs b/common/src/command/entity.rs similarity index 67% rename from vnt-cli/src/command/entity.rs rename to common/src/command/entity.rs index 5c06d18a..b42f0540 100644 --- a/vnt-cli/src/command/entity.rs +++ b/common/src/command/entity.rs @@ -1,5 +1,7 @@ use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use std::net::{Ipv4Addr, SocketAddr}; + #[derive(Serialize, Deserialize, Debug)] pub struct Info { pub name: String, @@ -12,11 +14,11 @@ pub struct Info { pub public_ips: String, pub local_addr: String, pub ipv6_addr: String, - pub up: u64, - pub down: u64, pub port_mapping_list: Vec<(bool, SocketAddr, String)>, pub in_ips: Vec<(u32, u32, Ipv4Addr)>, pub out_ips: Vec<(u32, u32)>, + pub udp_listen_addr: Vec, + pub tcp_listen_addr: String, } #[derive(Serialize, Deserialize, Debug)] @@ -44,3 +46,22 @@ pub struct DeviceItem { pub current_client_secret: bool, pub current_client_secret_hash: Vec, } + +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct ChartA { + pub disable_stats: bool, + pub up_total: u64, + pub down_total: u64, + pub up_map: HashMap, + pub down_map: HashMap, +} + +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct ChartB { + pub disable_stats: bool, + pub ip: Option, + pub up_total: u64, + pub up_list: Vec, + pub down_total: u64, + pub down_list: Vec, +} diff --git a/vnt-cli/src/command/mod.rs b/common/src/command/mod.rs similarity index 56% rename from vnt-cli/src/command/mod.rs rename to common/src/command/mod.rs index fc1cd1ce..b408b88d 100644 --- a/vnt-cli/src/command/mod.rs +++ b/common/src/command/mod.rs @@ -1,7 +1,10 @@ +use std::collections::HashSet; use std::io; +use std::net::Ipv4Addr; +use vnt::channel::ConnectProtocol; use vnt::core::Vnt; -use crate::command::entity::{DeviceItem, Info, RouteItem}; +use crate::command::entity::{ChartA, ChartB, DeviceItem, Info, RouteItem}; use crate::console_out; pub mod client; @@ -13,9 +16,56 @@ pub enum CommandEnum { List, All, Info, + ChartA, + ChartB(String), Stop, } +pub fn command_str(cmd: &str, vnt: &Vnt) -> bool { + if cmd.is_empty() { + return false; + } + let cmd = cmd.to_lowercase(); + let cmd = cmd.trim(); + match cmd { + "list" => { + let list = command_list(&vnt); + console_out::console_device_list(list); + } + "info" => { + let info = command_info(&vnt); + console_out::console_info(info); + } + "route" => { + let route = command_route(&vnt); + console_out::console_route_table(route); + } + "all" => { + let list = command_list(&vnt); + console_out::console_device_list_all(list); + } + "chart_a" => { + let chart = command_chart_a(&vnt); + console_out::console_chart_a(chart); + } + "stop" => { + let _ = vnt.stop(); + return false; + } + _ => {} + } + if let Some(ip) = cmd.strip_prefix("chart_b") { + let chart = if ip.is_empty() { + command_chart_b(&vnt, &vnt.current_device().virtual_gateway.to_string()) + } else { + command_chart_b(&vnt, &ip[1..]) + }; + console_out::console_chart_b(chart); + } + println!(); + return true; +} + pub fn command(cmd: CommandEnum) { if let Err(e) = command_(cmd) { println!("cmd: {:?}", e); @@ -41,6 +91,14 @@ fn command_(cmd: CommandEnum) -> io::Result<()> { let info = command_client.info()?; console_out::console_info(info); } + CommandEnum::ChartA => { + let chart = command_client.chart_a()?; + console_out::console_chart_a(chart); + } + CommandEnum::ChartB(input) => { + let chart = command_client.chart_b(&input)?; + console_out::console_chart_b(chart); + } CommandEnum::Stop => { command_client.stop()?; } @@ -50,6 +108,7 @@ fn command_(cmd: CommandEnum) -> io::Result<()> { pub fn command_route(vnt: &Vnt) -> Vec { let route_table = vnt.route_table(); + let server_addr = vnt.config().server_address_str.clone(); let mut route_list = Vec::with_capacity(route_table.len()); for (destination, routes) in route_table { for route in routes { @@ -62,11 +121,14 @@ pub fn command_route(vnt: &Vnt) -> Vec { } else { route.rt.to_string() }; - let interface = if route.is_tcp { - format!("tcp@{}", route.addr) - } else { - route.addr.to_string() + let interface = match route.protocol { + ConnectProtocol::UDP => route.addr.to_string(), + ConnectProtocol::TCP => { + format!("tcp@{}", route.addr) + } + ConnectProtocol::WS | ConnectProtocol::WSS => server_addr.clone(), }; + let item = RouteItem { destination: destination.to_string(), next_hop, @@ -114,7 +176,7 @@ pub fn command_list(vnt: &Vnt) -> Vec { }; let (nat_traversal_type, rt) = if let Some(route) = vnt.route(&peer.virtual_ip) { let nat_traversal_type = if route.metric == 1 { - if route.is_tcp { + if route.protocol.is_base_tcp() { "tcp-p2p" } else { "p2p" @@ -164,6 +226,7 @@ pub fn command_list(vnt: &Vnt) -> Vec { } pub fn command_info(vnt: &Vnt) -> Info { + let config = vnt.config(); let current_device = vnt.current_device(); let nat_info = vnt.nat_info(); let name = vnt.name().to_string(); @@ -171,7 +234,11 @@ pub fn command_info(vnt: &Vnt) -> Info { let virtual_gateway = current_device.virtual_gateway().to_string(); let virtual_netmask = current_device.virtual_netmask.to_string(); let connect_status = format!("{:?}", vnt.connection_status()); - let relay_server = current_device.connect_server.to_string(); + let relay_server = if current_device.connect_server.port() == 0 { + config.server_address_str.clone() + } else { + current_device.connect_server.to_string() + }; let nat_type = format!("{:?}", nat_info.nat_type); let public_ips: Vec = nat_info.public_ips.iter().map(|v| v.to_string()).collect(); let public_ips = public_ips.join(","); @@ -183,14 +250,18 @@ pub fn command_info(vnt: &Vnt) -> Info { .ipv6() .map(|v| v.to_string()) .unwrap_or("None".to_string()); - let up = vnt.up_stream(); - let down = vnt.down_stream(); #[cfg(feature = "port_mapping")] let port_mapping_list = vnt.config().port_mapping_list.clone(); #[cfg(not(feature = "port_mapping"))] let port_mapping_list = vec![]; let in_ips = vnt.config().in_ips.clone(); let out_ips = vnt.config().out_ips.clone(); + let udp_listen_addr = nat_info + .udp_ports + .iter() + .map(|port| format!("0.0.0.0:{}", port)) + .collect(); + let tcp_listen_addr = format!("0.0.0.0:{}", nat_info.tcp_port); Info { name, virtual_ip, @@ -202,10 +273,80 @@ pub fn command_info(vnt: &Vnt) -> Info { public_ips, local_addr, ipv6_addr, - up, - down, port_mapping_list, in_ips, out_ips, + udp_listen_addr, + tcp_listen_addr, + } +} + +pub fn command_chart_a(vnt: &Vnt) -> ChartA { + let disable_stats = !vnt.config().enable_traffic; + if disable_stats { + let mut chart = ChartA::default(); + chart.disable_stats = true; + return chart; + } + let (up_total, up_map) = vnt.up_stream_all().unwrap_or_default(); + let (down_total, down_map) = vnt.down_stream_all().unwrap_or_default(); + ChartA { + disable_stats, + up_total, + down_total, + up_map, + down_map, + } +} + +pub fn command_chart_b(vnt: &Vnt, input_str: &str) -> ChartB { + let disable_stats = !vnt.config().enable_traffic; + if disable_stats { + let mut chart = ChartB::default(); + chart.disable_stats = true; + return chart; + } + let (_, up_map) = vnt.up_stream_history().unwrap_or_default(); + let (_, down_map) = vnt.down_stream_history().unwrap_or_default(); + let up_keys: HashSet<_> = up_map.keys().cloned().collect(); + let down_keys: HashSet<_> = down_map.keys().cloned().collect(); + let mut keys: Vec = up_keys.union(&down_keys).cloned().collect(); + keys.sort(); + if let Some(ip) = find_matching_ipv4_address(input_str, &keys) { + let (up_total, up_list) = up_map.get(&ip).cloned().unwrap_or_default(); + let (down_total, down_list) = down_map.get(&ip).cloned().unwrap_or_default(); + ChartB { + disable_stats, + ip: Some(ip), + up_total, + up_list, + down_total, + down_list, + } + } else { + ChartB::default() + } +} + +fn match_from_end(input_str: &str, ip: &str) -> bool { + let mut input_chars = input_str.chars().rev(); + let mut ip_chars = ip.chars().rev(); + + while let (Some(ic), Some(pc)) = (input_chars.next(), ip_chars.next()) { + if ic != pc { + return false; + } + } + + input_chars.next().is_none() // Ensure all input characters matched +} + +fn find_matching_ipv4_address(input_str: &str, ip_addresses: &[Ipv4Addr]) -> Option { + for &ip in ip_addresses { + let ip_str = ip.to_string(); + if match_from_end(input_str, &ip_str) { + return Some(ip); + } } + None } diff --git a/vnt-cli/src/command/server.rs b/common/src/command/server.rs similarity index 72% rename from vnt-cli/src/command/server.rs rename to common/src/command/server.rs index 4719c73c..38a7c148 100644 --- a/vnt-cli/src/command/server.rs +++ b/common/src/command/server.rs @@ -1,7 +1,7 @@ +use crate::command::command_chart_b; use std::io; use std::io::Write; use std::net::UdpSocket; - use vnt::core::Vnt; pub struct CommandServer {} @@ -48,7 +48,7 @@ impl CommandServer { } } fn save_port(port: u16) -> io::Result<()> { - let path_buf = crate::app_home()?.join("command-port"); + let path_buf = crate::cli::app_home()?.join("command-port"); let mut file = std::fs::File::create(path_buf)?; file.write_all(port.to_string().as_bytes())?; file.sync_all() @@ -63,15 +63,26 @@ fn command(cmd: &str, vnt: &Vnt) -> io::Result { .unwrap_or_else(|e| format!("error {:?}", e)), "info" => serde_yaml::to_string(&crate::command::command_info(vnt)) .unwrap_or_else(|e| format!("error {:?}", e)), + "chart_a" => serde_yaml::to_string(&crate::command::command_chart_a(vnt)) + .unwrap_or_else(|e| format!("error {:?}", e)), "stop" => { vnt.stop(); "stopped".to_string() } _ => { - format!( - "command '{}' not found. Try to enter: 'route'/'list'/'stop' \n", - cmd - ) + if let Some(ip) = cmd.strip_prefix("chart_b") { + let chart = if ip.is_empty() { + command_chart_b(&vnt, &vnt.current_device().virtual_gateway.to_string()) + } else { + command_chart_b(&vnt, &ip[1..]) + }; + serde_yaml::to_string(&chart).unwrap_or_else(|e| format!("error {:?}", e)) + } else { + format!( + "command '{}' not found. Try to enter: 'route'/'list'/'stop' \n", + cmd + ) + } } }; Ok(out_str) diff --git a/vnt-cli/src/config/file_config.rs b/common/src/config/file_config.rs similarity index 81% rename from vnt-cli/src/config/file_config.rs rename to common/src/config/file_config.rs index 0fbe4362..2c9b24e5 100644 --- a/vnt-cli/src/config/file_config.rs +++ b/common/src/config/file_config.rs @@ -2,9 +2,9 @@ use anyhow::anyhow; use std::net::Ipv4Addr; use std::str::FromStr; -use serde::{Deserialize, Serialize}; - use crate::config::get_device_id; +use crate::{args_parse, config}; +use serde::{Deserialize, Serialize}; use vnt::channel::punch::PunchModel; use vnt::channel::UseChannelType; use vnt::cipher::CipherModel; @@ -32,7 +32,6 @@ pub struct FileConfig { #[cfg(feature = "ip_proxy")] pub no_proxy: bool, pub server_encrypt: bool, - pub parallel: usize, pub cipher_model: Option, pub finger: bool, pub punch_model: String, @@ -45,22 +44,27 @@ pub struct FileConfig { #[cfg(feature = "port_mapping")] pub mapping: Vec, pub compressor: Option, + pub vnt_mapping: Vec, + pub disable_stats: bool, } impl Default for FileConfig { fn default() -> Self { + let mut stun_server = Vec::new(); + for x in config::PUB_STUN { + stun_server.push(x.to_string()); + } Self { #[cfg(target_os = "windows")] tap: false, token: "".to_string(), device_id: get_device_id(), - name: os_info::get().to_string(), + name: gethostname::gethostname() + .to_str() + .unwrap_or("UnknownName") + .to_string(), server_address: "nat1.wherewego.top:29872".to_string(), - stun_server: vec![ - "stun1.l.google.com:19302".to_string(), - "stun2.l.google.com:19302".to_string(), - "stun.miwifi.com:3478".to_string(), - ], + stun_server, dns: vec![], in_ips: vec![], out_ips: vec![], @@ -72,7 +76,6 @@ impl Default for FileConfig { #[cfg(feature = "ip_proxy")] no_proxy: false, server_encrypt: false, - parallel: 1, cipher_model: None, finger: false, punch_model: "all".to_string(), @@ -85,11 +88,13 @@ impl Default for FileConfig { #[cfg(feature = "port_mapping")] mapping: vec![], compressor: None, + vnt_mapping: vec![], + disable_stats: false, } } } -pub fn read_config(file_path: &str) -> anyhow::Result<(Config, bool)> { +pub fn read_config(file_path: &str) -> anyhow::Result<(Config, Vec, bool)> { let conf = std::fs::read_to_string(file_path)?; let file_conf = match serde_yaml::from_str::(&conf) { Ok(val) => val, @@ -102,13 +107,13 @@ pub fn read_config(file_path: &str) -> anyhow::Result<(Config, bool)> { return Err(anyhow!("token is_empty")); } - let in_ips = match common::args_parse::ips_parse(&file_conf.in_ips) { + let in_ips = match args_parse::ips_parse(&file_conf.in_ips) { Ok(in_ips) => in_ips, Err(e) => { return Err(anyhow!("in_ips {:?} error:{}", &file_conf.in_ips, e)); } }; - let out_ips = match common::args_parse::out_ips_parse(&file_conf.out_ips) { + let out_ips = match args_parse::out_ips_parse(&file_conf.out_ips) { Ok(out_ips) => out_ips, Err(e) => { return Err(anyhow!("out_ips {:?} error:{}", &file_conf.out_ips, e)); @@ -122,6 +127,10 @@ pub fn read_config(file_path: &str) -> anyhow::Result<(Config, bool)> { #[cfg(not(any(feature = "aes_gcm", feature = "server_encrypt")))] if file_conf.password.is_some() && file_conf.cipher_model.is_none() { Err(anyhow!("cipher_model undefined"))? + } else if let Some(v) = file_conf.cipher_model { + CipherModel::from_str(&v).map_err(|e| anyhow!("{}", e))? + } else { + CipherModel::None } #[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))] CipherModel::AesGcm @@ -137,6 +146,7 @@ pub fn read_config(file_path: &str) -> anyhow::Result<(Config, bool)> { }; let config = Config::new( #[cfg(target_os = "windows")] + #[cfg(feature = "integrated_tun")] file_conf.tap, file_conf.token, file_conf.device_id, @@ -148,17 +158,17 @@ pub fn read_config(file_path: &str) -> anyhow::Result<(Config, bool)> { out_ips, file_conf.password, file_conf.mtu, - file_conf.tcp, virtual_ip, + #[cfg(feature = "integrated_tun")] #[cfg(feature = "ip_proxy")] file_conf.no_proxy, file_conf.server_encrypt, - file_conf.parallel, cipher_model, file_conf.finger, punch_model, file_conf.ports, file_conf.first_latency, + #[cfg(feature = "integrated_tun")] file_conf.device_name, use_channel_type, file_conf.packet_loss, @@ -166,6 +176,8 @@ pub fn read_config(file_path: &str) -> anyhow::Result<(Config, bool)> { #[cfg(feature = "port_mapping")] file_conf.mapping, compressor, + !file_conf.disable_stats, )?; - Ok((config, file_conf.cmd)) + + Ok((config, file_conf.vnt_mapping, file_conf.cmd)) } diff --git a/vnt-cli/src/config/mod.rs b/common/src/config/mod.rs similarity index 69% rename from vnt-cli/src/config/mod.rs rename to common/src/config/mod.rs index 4472e71c..8810c92e 100644 --- a/vnt-cli/src/config/mod.rs +++ b/common/src/config/mod.rs @@ -1,19 +1,26 @@ +pub const PUB_STUN: [&'static str; 4] = [ + "stun.miwifi.com", + "stun.chat.bilibili.com", + "stun.hitv.com", + "stun.cdnbye.com", +]; #[cfg(feature = "file_config")] mod file_config; +use crate::identifier; #[cfg(feature = "file_config")] pub use file_config::read_config; #[cfg(not(feature = "file_config"))] -pub fn read_config(_file_path: &str) -> anyhow::Result<(vnt::core::Config, bool)> { +pub fn read_config(_file_path: &str) -> anyhow::Result<(vnt::core::Config, Vec, bool)> { unimplemented!() } pub fn get_device_id() -> String { - if let Some(id) = common::identifier::get_unique_identifier() { + if let Some(id) = identifier::get_unique_identifier() { id } else { - let path_buf = match crate::app_home() { + let path_buf = match crate::cli::app_home() { Ok(path_buf) => path_buf.join("device-id"), Err(e) => { log::warn!("{:?}", e); diff --git a/vnt-cli/src/console_out/mod.rs b/common/src/console_out/mod.rs similarity index 69% rename from vnt-cli/src/console_out/mod.rs rename to common/src/console_out/mod.rs index 44bde16c..2efde469 100644 --- a/vnt-cli/src/console_out/mod.rs +++ b/common/src/console_out/mod.rs @@ -1,7 +1,8 @@ use console::{style, Style}; +use std::collections::HashSet; use std::net::Ipv4Addr; -use crate::command::entity::{DeviceItem, Info, RouteItem}; +use crate::command::entity::{ChartA, ChartB, DeviceItem, Info, RouteItem}; pub mod table; @@ -21,11 +22,14 @@ pub fn console_info(status: Info) { println!("NAT type: {}", style(status.nat_type).green()); println!("Relay server: {}", style(status.relay_server).green()); + println!( + "Udp listen: {}", + style(status.udp_listen_addr.join(", ")).green() + ); + println!("Tcp listen: {}", style(status.tcp_listen_addr).green()); println!("Public ips: {}", style(status.public_ips).green()); println!("Local addr: {}", style(status.local_addr).green()); println!("IPv6: {}", style(status.ipv6_addr).green()); - println!("Up: {}", style(convert(status.up)).green()); - println!("Down: {}", style(convert(status.down)).green()); if !status.port_mapping_list.is_empty() { println!("------------------------------------------"); @@ -237,3 +241,120 @@ pub fn console_device_list_all(mut list: Vec) { } table::println_table(out_list) } + +pub fn console_chart_a(chart_a: ChartA) { + if chart_a.disable_stats { + println!("Traffic statistics not enabled"); + return; + } + println!(); + println!("-----------------------------------------------------------------"); + println!( + "Upload total = {}", + style(convert(chart_a.up_total)).green() + ); + println!( + "Download total = {}", + style(convert(chart_a.down_total)).green() + ); + println!("-----------------------------------------------------------------"); + let up_keys: HashSet<_> = chart_a.up_map.keys().cloned().collect(); + let down_keys: HashSet<_> = chart_a.down_map.keys().cloned().collect(); + let mut keys: Vec = up_keys.union(&down_keys).cloned().collect(); + // 排序 + keys.sort(); + + // 找到最大的值,用于缩放条形图长度 + let up_max_value = *chart_a.up_map.values().max().unwrap_or(&0); + let down_max_value = *chart_a.down_map.values().max().unwrap_or(&0); + let max_value = up_max_value.max(down_max_value); + let max_value = max_value.max(1); + let max_height = 50; + // 打印条形图 + for key in &keys { + if let Some(&value) = chart_a.up_map.get(key) { + let bar = "█".repeat(((value as f64 / max_value as f64) * max_height as f64) as usize); + println!( + "{:<10} | {} upload {}", + key, + bar, + style(convert(value)).green() + ); + } + if let Some(&value) = chart_a.down_map.get(key) { + let bar = "█".repeat(((value as f64 / max_value as f64) * max_height as f64) as usize); + println!( + "{:<10} | {} download {}", + key, + bar, + style(convert(value)).green() + ); + } + println!("-"); + } +} + +pub fn console_chart_b(chart_b: ChartB) { + if chart_b.disable_stats { + println!("Traffic statistics not enabled"); + return; + } + let ip = if let Some(ip) = chart_b.ip { + ip + } else { + println!("Ip: None"); + return; + }; + println!("---------------------------- upload ----------------------------"); + println!("IP: {}", ip); + println!("Upload total: {}", style(convert(chart_b.up_total)).green()); + println!( + "Max: {}", + style(convert( + chart_b + .up_list + .iter() + .max() + .cloned() + .map_or(0, |v| v as u64) + )) + .green() + ); + console_chart_b_list(chart_b.up_list); + println!("---------------------------- download ----------------------------"); + println!("IP: {}", ip); + println!( + "Download total: {}", + style(convert(chart_b.down_total)).green() + ); + println!( + "Max: {}", + style(convert( + chart_b + .down_list + .iter() + .max() + .cloned() + .map_or(0, |v| v as u64) + )) + .green() + ); + console_chart_b_list(chart_b.down_list); +} +fn console_chart_b_list(list: Vec) { + let max_value = *list.iter().max().unwrap_or(&0); + let max_value = max_value.max(1); + let max_height = max_value.min(20); + // 遍历从最大高度到0 + for i in (0..=max_height).rev() { + for &value in &list { + let scaled_value = (value as f64 / max_value as f64 * max_height as f64) as usize; + if scaled_value >= i { + print!("█"); + } else { + print!(" "); + } + } + println!(); + } +} diff --git a/vnt-cli/src/console_out/table.rs b/common/src/console_out/table.rs similarity index 100% rename from vnt-cli/src/console_out/table.rs rename to common/src/console_out/table.rs diff --git a/common/src/identifier.rs b/common/src/identifier.rs index 4d3590bc..329acd35 100644 --- a/common/src/identifier.rs +++ b/common/src/identifier.rs @@ -52,6 +52,19 @@ pub fn get_unique_identifier() -> Option { pub fn get_unique_identifier() -> Option { use std::process::Command; + // Try to execute 'dmidecode' command to get the system identifier first. + if let Ok(output) = Command::new("dmidecode") + .arg("-s") + .arg("system-uuid") + .output() + { + let identifier = String::from_utf8_lossy(&output.stdout).trim().to_owned(); + if !identifier.is_empty() { + return Some(identifier.to_string()); + } + } + + // Try to read file /etc/machine-id if 'dmidecode' command cannot be executed or get nothing. // 对 linux 或 wsl 来说,读取 /etc/machine-id 即可获取当前操作系统的 // 唯一标识,而且某些环境没有预装`dmidecode`命令 if let Ok(identifier) = std::fs::read_to_string("/etc/machine-id") { @@ -61,22 +74,5 @@ pub fn get_unique_identifier() -> Option { } } - let output = match Command::new("dmidecode") - .arg("-s") - .arg("system-uuid") - .output() - { - Ok(output) => output, - Err(_) => { - return None; - } - }; - - let result = String::from_utf8_lossy(&output.stdout); - let identifier = result.trim(); - if identifier.is_empty() { - None - } else { - Some(identifier.to_string()) - } + None } diff --git a/common/src/lib.rs b/common/src/lib.rs index 7ce4c80b..d2183874 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -1,2 +1,12 @@ pub mod args_parse; +#[cfg(feature = "command")] +pub mod command; +pub mod config; +#[cfg(feature = "command")] +mod console_out; pub mod identifier; + +pub mod cli; +mod generated_serial_number; + +pub mod callback; diff --git a/vn-link-cli/Cargo.toml b/vn-link-cli/Cargo.toml new file mode 100644 index 00000000..8a1d4b71 --- /dev/null +++ b/vn-link-cli/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "vn-link-cli" +version = "1.2.11" +edition = "2021" + +[dependencies] +vn-link = { path = "../vn-link", default-features = false } +common = { path = "../common", default-features = false } +tokio = { version = "1.37.0", features = ["full"] } +log = "0.4.17" + +[features] +default = ["default-feature"] +default-feature = ["server_encrypt", "aes_gcm", "aes_cbc", "aes_ecb", "sm4_cbc", "chacha20_poly1305", "port_mapping", "log", "command", "file_config", "lz4"] + +openssl = ["vn-link/openssl", "common/openssl"] +openssl-vendored = ["vn-link/openssl-vendored", "common/openssl-vendored"] +ring-cipher = ["vn-link/ring-cipher", "common/ring-cipher"] +aes_cbc = ["vn-link/aes_cbc", "common/aes_cbc"] +aes_ecb = ["vn-link/aes_ecb", "common/aes_ecb"] +sm4_cbc = ["vn-link/sm4_cbc", "common/sm4_cbc"] +aes_gcm = ["vn-link/aes_gcm", "common/aes_gcm"] +chacha20_poly1305 = ["vn-link/chacha20_poly1305", "common/chacha20_poly1305"] +server_encrypt = ["vn-link/server_encrypt", "common/server_encrypt"] +port_mapping = ["vn-link/port_mapping", "common/port_mapping"] +lz4 = ["vn-link/lz4_compress", "common/lz4"] +zstd = ["vn-link/zstd_compress", "common/zstd"] +upnp = ["vn-link/upnp", "common/upnp"] +ws = ["vn-link/ws", "common/ws"] +wss = ["vn-link/wss", "common/wss"] +log = ["common/log"] +command = ["common/command"] +file_config = ["common/file_config"] + diff --git a/vn-link-cli/README.md b/vn-link-cli/README.md new file mode 100644 index 00000000..3c373588 --- /dev/null +++ b/vn-link-cli/README.md @@ -0,0 +1,61 @@ +# 端口映射模式 + +## 一、特点 + +1. 不需要tap/tun虚拟网卡 +2. 不需要管理员/root权限 +3. 不改变本地路由 +4. 使用端口映射来访问目标服务 + +## 二、作用 + +和vnt互补,能简单快速构建网络,外部依赖更少 + +## 三、使用方式 + +和vnt的使用方式一样,只是多了"--vnt-mapping"这个参数 + +### vn-link作为被访问端,不需要额外配置vnt-mapping + +### vn-link访问vnt或者vn-link,需要加vnt-mapping + +例如: + +设备A 运行vnt(虚拟IP 10.26.0.A),设备B 运行vn-link(虚拟IP 10.26.0.B)。 + +如果要用B访问A上的tcp 80端口,则在设备B上需要加--vnt-mapping "tcp:port1-10.26.0.A:80" + +这个参数的作用是将B上的***本地端口port1***转发到设备A的地址10.26.0.A: +80,此时在设备B上可以访问本地port1端口从而间接访问10.26.0.A:80 + +## 四、vn-link的子网代理 + +vn-link也支持点对网参数。 还是接着上面的例子 + +假设 设备C在设备A的子网下,C的子网IP为192.168.1.C,A的子网IP为192.168.1.A,要在设备B上访问C + +则在B上加这些参数 + +- --vnt-mapping "tcp:port2-192.168.1.C:80" (将本地port2端口映射到C的80端口) +- -i 192.168.1.0/24,10.26.0.A (将目标192.168.1.0/24的数据发送到10.26.0.A,也就是A节点) + +在A上加参数 + +- -o 0.0.0.0/0 (允许所有流量转发) + +***再次说明,vn-link作为被访问端时和vnt使用方式一致,vn-link作为访问端时需要加--vnt-mapping映射端口*** + +***vn-link是基于端口映射的使用模式,不会改变本地路由*** + +## 五、参数介绍 + +--vnt-mapping支持udp/tcp,例如 --vnt-mapping "tcp:port1-remoteIp:remotePort" + +- 第一部分为协议,支持使用udp/tcp +- 第二部分是本地端口,注意不要和本地服务的端口冲突 +- 第三部分是目标机器的地址,一般是目标虚拟IP地址,如果配置了点对网参数(-i和-o)则也可以是目标子网地址 + + + + + diff --git a/vn-link-cli/src/main.rs b/vn-link-cli/src/main.rs new file mode 100644 index 00000000..33d526da --- /dev/null +++ b/vn-link-cli/src/main.rs @@ -0,0 +1,84 @@ +use common::callback; +use vn_link::config::VnLinkConfig; +use vn_link::vnt::core::Config; + +fn main() { + let (config, vnt_link_config, cmd) = match common::cli::parse_args_config() { + Ok(rs) => { + if let Some(rs) = rs { + rs + } else { + return; + } + } + Err(e) => { + println!("{}", e); + return; + } + }; + let vnt_link_config = VnLinkConfig::new(vn_link::config::convert(vnt_link_config).unwrap()); + main0(config, vnt_link_config, cmd) +} + +#[tokio::main] +async fn main0(config: Config, vn_link_config: VnLinkConfig, _show_cmd: bool) { + #[cfg(feature = "port_mapping")] + for (is_tcp, addr, dest) in config.port_mapping_list.iter() { + if *is_tcp { + println!("TCP port mapping {}->{}", addr, dest) + } else { + println!("UDP port mapping {}->{}", addr, dest) + } + } + for x in &vn_link_config.mapping { + if x.protocol.is_tcp() { + println!("TCP vnt addr mapping 127.0.0.1:{}->{}", x.src_port, x.dest) + } else { + println!("UDP vnt addr mapping 127.0.0.1:{}->{}", x.src_port, x.dest) + } + } + + let vnt_util = match vn_link::VnLink::new(config, vn_link_config, callback::VntHandler {}).await + { + Ok(vnt) => vnt, + Err(e) => { + println!("error: {:?}", e); + std::process::exit(1); + } + }; + + #[cfg(feature = "command")] + { + let vnt_c = vnt_util.as_vnt().clone(); + std::thread::Builder::new() + .name("CommandServer".into()) + .spawn(move || { + if let Err(e) = common::command::server::CommandServer::new().start(vnt_c) { + log::warn!("cmd:{:?}", e); + } + }) + .expect("CommandServer"); + let vnt_c = vnt_util.as_vnt(); + if _show_cmd { + use tokio::io::AsyncBufReadExt; + let mut cmd = String::new(); + let mut reader = tokio::io::BufReader::new(tokio::io::stdin()); + loop { + cmd.clear(); + println!("======== input:list,info,route,all,stop,chart_a,chart_b[:ip] ========"); + match reader.read_line(&mut cmd).await { + Ok(len) => { + if !common::command::command_str(&cmd[..len], vnt_c) { + break; + } + } + Err(e) => { + println!("input err:{}", e); + break; + } + } + } + } + } + vnt_util.wait().await +} diff --git a/vn-link/Cargo.toml b/vn-link/Cargo.toml new file mode 100644 index 00000000..5e449978 --- /dev/null +++ b/vn-link/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "vn-link" +version = "1.2.11" +edition = "2021" + +[dependencies] +lwip-rs = { git = "https://github.com/lbl8603/lwip-rs" } +vnt = { path = "../vnt", package = "vnt", default-features = false } +log = "0.4.17" +anyhow = "1.0.82" +parking_lot = "0.12.1" + +tokio = { version = "1.37.0", features = ["full"] } +crossbeam-utils = "0.8" + +[features] +default = ["server_encrypt", "aes_gcm", "aes_cbc", "aes_ecb", "sm4_cbc", "chacha20_poly1305", "port_mapping", "lz4_compress"] +openssl = ["vnt/openssl"] +openssl-vendored = ["vnt/openssl-vendored"] +ring-cipher = ["vnt/ring-cipher"] +aes_cbc = ["vnt/aes_cbc"] +aes_ecb = ["vnt/aes_ecb"] +sm4_cbc = ["vnt/sm4_cbc"] +aes_gcm = ["vnt/aes_gcm"] +chacha20_poly1305 = ["vnt/chacha20_poly1305"] +server_encrypt = ["vnt/server_encrypt"] +port_mapping = ["vnt/port_mapping"] +lz4_compress = ["vnt/lz4_compress"] +zstd_compress = ["vnt/zstd_compress"] +upnp = ["vnt/upnp"] +ws = ["vnt/ws"] +wss = ["vnt/wss"] \ No newline at end of file diff --git a/vn-link/src/config.rs b/vn-link/src/config.rs new file mode 100644 index 00000000..58e2c6ab --- /dev/null +++ b/vn-link/src/config.rs @@ -0,0 +1,85 @@ +use anyhow::Context; +use std::net::SocketAddr; +use std::str::FromStr; + +#[derive(Clone, Debug)] +pub struct VnLinkConfig { + pub mapping: Vec, +} + +impl VnLinkConfig { + pub fn new(mapping: Vec) -> Self { + Self { mapping } + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum LinkProtocol { + Tcp, + Udp, +} + +impl LinkProtocol { + pub fn is_tcp(&self) -> bool { + self == &LinkProtocol::Tcp + } +} + +#[derive(Copy, Clone, Debug)] +pub struct LinkItem { + pub protocol: LinkProtocol, + pub src_port: u16, + pub dest: SocketAddr, +} + +impl LinkItem { + pub fn new(protocol: LinkProtocol, src_port: u16, dest: SocketAddr) -> Self { + Self { + protocol, + src_port, + dest, + } + } +} + +pub fn convert(vec: Vec) -> anyhow::Result> { + let mut rs = Vec::with_capacity(vec.len()); + for x in vec { + let string = x.trim().to_lowercase(); + if let Some(udp_mapping) = string.strip_prefix("udp:") { + let mut split = udp_mapping.split("-"); + let bind_port = split + .next() + .with_context(|| format!("vnt-mapping error {:?},eg: udp:80-10.26.0.10:8080", x))?; + let bind_port = u16::from_str(bind_port) + .with_context(|| format!("udp_mapping error {}", bind_port))?; + let dest = split + .next() + .with_context(|| format!("vnt-mapping error {:?},eg: udp:80-10.26.0.10:8080", x))?; + let dest_addr = SocketAddr::from_str(dest) + .with_context(|| format!("udp_mapping error {}", dest))?; + rs.push(LinkItem::new(LinkProtocol::Udp, bind_port, dest_addr)); + continue; + } + if let Some(tcp_mapping) = string.strip_prefix("tcp:") { + let mut split = tcp_mapping.split("-"); + let bind_port = split + .next() + .with_context(|| format!("vnt-mapping error {:?},eg: tcp:80-10.26.0.10:8080", x))?; + let bind_port = u16::from_str(bind_port) + .with_context(|| format!("tcp_mapping error {}", bind_port))?; + let dest = split + .next() + .with_context(|| format!("vnt-mapping error {:?},eg: tcp:80-10.26.0.10:8080", x))?; + let dest_addr = SocketAddr::from_str(dest) + .with_context(|| format!("tcp_mapping error {}", dest))?; + rs.push(LinkItem::new(LinkProtocol::Tcp, bind_port, dest_addr)); + continue; + } + Err(anyhow::anyhow!( + "vnt-mapping error {:?},eg: tcp:80-10.26.0.10:8080", + x + ))?; + } + Ok(rs) +} diff --git a/vn-link/src/in_mapping/mod.rs b/vn-link/src/in_mapping/mod.rs new file mode 100644 index 00000000..f7a6a764 --- /dev/null +++ b/vn-link/src/in_mapping/mod.rs @@ -0,0 +1,2 @@ +pub mod tcp; +pub mod udp; diff --git a/vn-link/src/in_mapping/tcp.rs b/vn-link/src/in_mapping/tcp.rs new file mode 100644 index 00000000..d8414690 --- /dev/null +++ b/vn-link/src/in_mapping/tcp.rs @@ -0,0 +1,46 @@ +use crate::out_mapping::tcp::tcp_copy; +use crossbeam_utils::atomic::AtomicCell; +use lwip_rs::tcp_stream::TcpStream as LwIpTcpStream; +use std::net::{IpAddr, SocketAddr}; +use std::sync::Arc; +use std::time::Duration; +use tokio::net::TcpListener; +use vnt::handle::CurrentDeviceInfo; + +pub async fn tcp_mapping_listen( + tcp_listener: TcpListener, + current_device: Arc>, + dest: SocketAddr, +) { + loop { + let (stream, addr) = match tcp_listener.accept().await { + Ok((stream, addr)) => (stream, addr), + Err(e) => { + log::warn!("tcp_mapping_listen {:?} dest {}", e, dest); + continue; + } + }; + let current_info = current_device.load(); + if current_info.virtual_ip.is_unspecified() { + continue; + } + if let IpAddr::V4(ip) = dest.ip() { + if ip == current_info.virtual_ip { + //防止用错参数的 + log::warn!("目的地址不能是本地虚拟ip tcp->{}", dest); + continue; + } + } + let src = SocketAddr::new(IpAddr::V4(current_info.virtual_ip), addr.port()); + tokio::spawn(async move { + match LwIpTcpStream::connect(src, dest, Duration::from_secs(5)).await { + Ok(lw_tcp) => { + tcp_copy(lw_tcp, stream); + } + Err(e) => { + log::warn!("{} {}->{} {}", addr, src, dest, e); + } + }; + }); + } +} diff --git a/vn-link/src/in_mapping/udp.rs b/vn-link/src/in_mapping/udp.rs new file mode 100644 index 00000000..b7ac232e --- /dev/null +++ b/vn-link/src/in_mapping/udp.rs @@ -0,0 +1,63 @@ +use std::collections::HashMap; +use std::net::{IpAddr, SocketAddr}; +use std::sync::Arc; +use std::time::Instant; + +use crossbeam_utils::atomic::AtomicCell; +use parking_lot::Mutex; +use tokio::net::UdpSocket; + +use lwip_rs::udp::UdpSocketWrite; +use vnt::handle::CurrentDeviceInfo; + +pub async fn udp_mapping_start( + udp: UdpSocket, + lwip_udp_write: UdpSocketWrite, + current_device: Arc>, + in_udp_map: &Arc< + Mutex< + HashMap< + (SocketAddr, SocketAddr), + (Arc, Option, Arc>), + >, + >, + >, + + dest: SocketAddr, +) { + let udp = Arc::new(udp); + let mut buf = [0u8; 65536]; + loop { + let (len, addr) = match udp.recv_from(&mut buf).await { + Ok(rs) => rs, + Err(e) => { + log::warn!("recv_from {} {}", dest, e); + continue; + } + }; + let current_info = current_device.load(); + if current_info.virtual_ip.is_unspecified() { + continue; + } + if let IpAddr::V4(ip) = dest.ip() { + if ip == current_info.virtual_ip { + //防止用错参数的 + log::warn!("目的地址不能是本地虚拟ip udp->{}", dest); + continue; + } + } + let src = SocketAddr::new(IpAddr::V4(current_info.virtual_ip), addr.port()); + in_udp_map.lock().insert( + (dest, src), + ( + udp.clone(), + Some(addr), + Arc::new(AtomicCell::new(Instant::now())), + ), + ); + + if let Err(e) = lwip_udp_write.send(&buf[..len], &src, &dest) { + log::warn!("lwip_udp_write {}->{} {}", src, dest, e); + } + } +} diff --git a/vn-link/src/lib.rs b/vn-link/src/lib.rs new file mode 100644 index 00000000..990bdb4c --- /dev/null +++ b/vn-link/src/lib.rs @@ -0,0 +1,8 @@ +pub mod config; +mod in_mapping; +mod out_mapping; +mod vnt_link; + +pub use vnt; + +pub use vnt_link::*; diff --git a/vn-link/src/out_mapping/mod.rs b/vn-link/src/out_mapping/mod.rs new file mode 100644 index 00000000..f7a6a764 --- /dev/null +++ b/vn-link/src/out_mapping/mod.rs @@ -0,0 +1,2 @@ +pub mod tcp; +pub mod udp; diff --git a/vn-link/src/out_mapping/tcp.rs b/vn-link/src/out_mapping/tcp.rs new file mode 100644 index 00000000..7f3e9f6e --- /dev/null +++ b/vn-link/src/out_mapping/tcp.rs @@ -0,0 +1,64 @@ +use std::net::{IpAddr, Ipv4Addr}; +use std::sync::Arc; + +use crossbeam_utils::atomic::AtomicCell; +use tokio::net::TcpStream; + +use lwip_rs::tcp_listener::TcpListener; +use lwip_rs::tcp_stream::TcpStream as LwIpTcpStream; +use vnt::handle::CurrentDeviceInfo; + +pub async fn tcp_mapping_listen( + mut tcp_listener: TcpListener, + current_device: Arc>, +) { + loop { + let stream = match tcp_listener.accept().await { + Ok(stream) => stream, + Err(e) => { + log::warn!("tcp_mapping_listen err {:?}", e); + break; + } + }; + let device_info = current_device.load(); + tokio::spawn(async move { + let dest = stream.dest_addr(); + let src = stream.src_addr(); + if let Err(e) = tcp_mapping_handle(stream, device_info).await { + log::warn!("tcp_mapping_handle {}->{} {:?}", src, dest, e) + } + }); + } +} + +async fn tcp_mapping_handle( + tcp_stream: LwIpTcpStream, + device_info: CurrentDeviceInfo, +) -> anyhow::Result<()> { + let mut dest = tcp_stream.dest_addr(); + // let src = tcp_stream.src_addr(); + if let IpAddr::V4(ip) = dest.ip() { + if ip.is_unspecified() + || ip.is_broadcast() + || ip.is_multicast() + || ip == device_info.virtual_ip + || ip == device_info.broadcast_ip + { + //是自己 + dest.set_ip(IpAddr::V4(Ipv4Addr::LOCALHOST)); + } + } + let peer_stream = TcpStream::connect(dest).await?; + if dest.port() == peer_stream.local_addr()?.port() { + return Err(anyhow::anyhow!("tcp port loop")); + } + tcp_copy(tcp_stream, peer_stream); + Ok(()) +} + +pub(crate) fn tcp_copy(lw_tcp: LwIpTcpStream, tokio_tcp: TcpStream) { + let (mut write, mut read) = lw_tcp.into_split(); + let (mut peer_read, mut peer_write) = tokio_tcp.into_split(); + tokio::spawn(async move { tokio::io::copy(&mut read, &mut peer_write).await }); + tokio::spawn(async move { tokio::io::copy(&mut peer_read, &mut write).await }); +} diff --git a/vn-link/src/out_mapping/udp.rs b/vn-link/src/out_mapping/udp.rs new file mode 100644 index 00000000..04c030c4 --- /dev/null +++ b/vn-link/src/out_mapping/udp.rs @@ -0,0 +1,137 @@ +use crossbeam_utils::atomic::AtomicCell; +use lwip_rs::udp::{UdpSocketRead, UdpSocketWrite}; +use parking_lot::Mutex; +use std::collections::HashMap; +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use std::sync::Arc; +use std::time::{Duration, Instant}; +use tokio::net::UdpSocket; +use vnt::handle::CurrentDeviceInfo; + +pub async fn udp_mapping_start( + lwip_udp_write: UdpSocketWrite, + mut lwip_udp_read: UdpSocketRead, + current_device: Arc>, + in_udp_map: Arc< + Mutex< + HashMap< + (SocketAddr, SocketAddr), + (Arc, Option, Arc>), + >, + >, + >, +) { + loop { + let (buf, src, dest) = match lwip_udp_read.recv().await { + Ok(rs) => rs, + Err(e) => { + log::warn!("udp_mapping err {:?}", e); + break; + } + }; + if let Err(e) = handle( + ¤t_device, + &lwip_udp_write, + &in_udp_map, + buf, + src, + dest, + ) + .await + { + log::warn!("udp_mapping err {}->{} {:?}", src, dest, e) + } + } +} + +async fn handle( + current_device: &AtomicCell, + lwip_udp_write: &UdpSocketWrite, + map: &Arc< + Mutex< + HashMap< + (SocketAddr, SocketAddr), + (Arc, Option, Arc>), + >, + >, + >, + buf: Vec, + src: SocketAddr, + dest: SocketAddr, +) -> anyhow::Result<()> { + let option = map.lock().get(&(src, dest)).cloned(); + + if let Some((dest_udp, addr, time)) = option { + time.store(Instant::now()); + if let Some(addr) = addr { + dest_udp.send_to(&buf, addr).await?; + } else { + dest_udp.send(&buf).await?; + } + } else { + let mut real_dest = dest; + let peer_udp_socket = match UdpSocket::bind(format!("0.0.0.0:{}", src.port())).await { + Ok(udp) => udp, + Err(_) => UdpSocket::bind("0.0.0.0:0").await?, + }; + if let IpAddr::V4(ip) = dest.ip() { + let device_info = current_device.load(); + if ip.is_unspecified() + || ip.is_broadcast() + || ip.is_multicast() + || ip == device_info.virtual_ip + || ip == device_info.broadcast_ip + { + //是自己 + real_dest.set_ip(IpAddr::V4(Ipv4Addr::LOCALHOST)); + } + } + peer_udp_socket.connect(real_dest).await?; + peer_udp_socket.send(&buf).await?; + let peer_udp_socket = Arc::new(peer_udp_socket); + let time = Arc::new(AtomicCell::new(Instant::now())); + let map = map.clone(); + map.lock() + .insert((src, dest), (peer_udp_socket.clone(), None, time.clone())); + let lwip_udp_write = lwip_udp_write.clone(); + tokio::spawn(async move { + peer_udp_handle(peer_udp_socket, lwip_udp_write, src, dest, time).await; + map.lock().remove(&(src, dest)); + }); + } + Ok(()) +} + +async fn peer_udp_handle( + peer_udp_socket: Arc, + lwip_udp_write: UdpSocketWrite, + src: SocketAddr, + dest: SocketAddr, + time: Arc>, +) { + let mut buf = [0u8; 65536]; + loop { + match tokio::time::timeout(Duration::from_secs(600), peer_udp_socket.recv(&mut buf)).await { + Ok(rs) => match rs { + Ok(len) => match lwip_udp_write.send(&buf[..len], &dest, &src) { + Ok(_) => {} + Err(e) => { + log::warn!("udp proxy {}->{} {:?}", dest, src, e); + break; + } + }, + Err(e) => { + log::warn!("udp proxy {}->{} {:?}", dest, src, e); + break; + } + }, + Err(_) => { + if time.load().elapsed() > Duration::from_secs(580) { + //超时关闭 + log::warn!("udp proxy timeout {}->{}", dest, src,); + break; + } + } + } + } +} diff --git a/vn-link/src/vnt_link/mod.rs b/vn-link/src/vnt_link/mod.rs new file mode 100644 index 00000000..c009efdc --- /dev/null +++ b/vn-link/src/vnt_link/mod.rs @@ -0,0 +1,217 @@ +use std::collections::HashMap; +use std::net::SocketAddr; +use std::sync::Arc; +use std::time::Instant; + +use anyhow::Context; +use crossbeam_utils::atomic::AtomicCell; +use parking_lot::Mutex; +use tokio::net::{TcpListener, UdpSocket}; +use tokio::sync::watch::{channel, Sender}; + +use lwip_rs::stack::{NetStack, NetStackWrite}; +use lwip_rs::tcp_listener::TcpListener as LwIPTcpListener; +use lwip_rs::udp::{UdpSocket as LwIpUdpSocket, UdpSocketWrite}; +use vnt::channel::BUFFER_SIZE; +use vnt::core::{Config, Vnt}; +use vnt::packet::ip::ipv4::packet::IpV4Packet; +use vnt::protocol::HEAD_LEN; +use vnt::vnt_device::DeviceWrite; +use vnt::VntCallback; + +use crate::config::{LinkItem, LinkProtocol, VnLinkConfig}; +use crate::{in_mapping, out_mapping}; + +pub struct VnLink { + vnt: Vnt, + in_udp_map: Arc< + Mutex< + HashMap< + (SocketAddr, SocketAddr), + (Arc, Option, Arc>), + >, + >, + >, + lwip_udp_write: UdpSocketWrite, + shutdown_tx: Sender, +} + +impl VnLink { + pub async fn new( + vnt_config: Config, + vn_link_config: VnLinkConfig, + callback: Call, + ) -> anyhow::Result { + let stack = NetStack::new(HEAD_LEN, 1024, vnt_config.mtu.unwrap_or(1420) as u16).await; + let udp = LwIpUdpSocket::new()?; + let tcp_listener = LwIPTcpListener::new()?; + let (shutdown_tx, shutdown_rx) = channel(false); + let (net_stack_write, mut net_stack_read) = stack.into_split(); + let vnt = Vnt::new_device(vnt_config, callback, VntDevice { net_stack_write })?; + let shutdown_tx_ = shutdown_tx.clone(); + let w = vnt.add_stop_listener("vnt-link".into(), move || { + let _ = shutdown_tx_.send(true); + })?; + let ip_sender = vnt.ipv4_packet_sender().unwrap(); + let mut shutdown_rx_ = shutdown_rx.clone(); + tokio::spawn(async move { + let mut extend = [0; BUFFER_SIZE]; + loop { + tokio::select! { + _ = shutdown_rx_.changed() => { + break; + } + rs = net_stack_read.recv_ip() => { + match rs{ + Ok((mut buf, start_index, len)) => { + let ipv4_packet = if let Ok(packet) = + IpV4Packet::new(&buf[start_index..len]) + { + packet + } else { + continue; + }; + let destination_ip = ipv4_packet.destination_ip(); + let source_ip = ipv4_packet.source_ip(); + + if let Err(e) = ip_sender.send_ip(&mut buf, len, &mut extend, destination_ip) { + log::warn!("{}->{},{}", source_ip, destination_ip, e); + } + }, + Err(e) => { + log::error!("net_stack_read {:?}", e); + break; + } + }; + } + } + } + w.stop_all(); + }); + + let (lwip_udp_write, lwip_udp_read) = udp.into_split(); + let in_udp_map: Arc< + Mutex< + HashMap< + (SocketAddr, SocketAddr), + (Arc, Option, Arc>), + >, + >, + > = Arc::new(Mutex::new(HashMap::new())); + + let current_device_info = vnt.current_device_info(); + let in_udp_map_ = in_udp_map.clone(); + let lwip_udp_write_ = lwip_udp_write.clone(); + let vnt_ = vnt.clone(); + let mut shutdown_rx_ = shutdown_rx.clone(); + tokio::spawn(async move { + tokio::select! { + _ = shutdown_rx_.changed() => {} + _ = out_mapping::udp::udp_mapping_start( + lwip_udp_write_, + lwip_udp_read, + current_device_info, + in_udp_map_, + ) => {} + } + + vnt_.stop(); + }); + let current_device_info = vnt.current_device_info(); + let vnt_ = vnt.clone(); + let mut shutdown_rx_ = shutdown_rx.clone(); + + tokio::spawn(async move { + tokio::select! { + _ = shutdown_rx_.changed() => {} + _ = out_mapping::tcp::tcp_mapping_listen(tcp_listener, current_device_info) => {} + } + vnt_.stop(); + }); + let link = Self { + vnt, + in_udp_map, + lwip_udp_write, + shutdown_tx, + }; + link.add_mapping(vn_link_config.mapping).await?; + Ok(link) + } + pub async fn add_mapping(&self, mapping: Vec) -> anyhow::Result<()> { + for item in mapping { + let current_device_info = self.vnt.current_device_info(); + if item.dest.ip().is_unspecified() { + Err(anyhow::anyhow!("dest_address {:?} is_unspecified", item))? + } + let mut shutdown_rx_ = self.shutdown_tx.subscribe(); + if *shutdown_rx_.borrow() { + Err(anyhow::anyhow!("mapping stop"))? + } + if item.protocol == LinkProtocol::Udp { + let lwip_udp_write = self.lwip_udp_write.clone(); + let in_udp_map = self.in_udp_map.clone(); + //只能本机访问,不然不同IP的相同来源端口会有问题 + let udp = UdpSocket::bind(format!("127.0.0.1:{}", item.src_port)) + .await + .with_context(|| format!("udp bind failed {}", item.src_port))?; + tokio::spawn(async move { + tokio::select! { + _ = shutdown_rx_.changed() => {} + _ = in_mapping::udp::udp_mapping_start( + udp, + lwip_udp_write, + current_device_info, + &in_udp_map, + item.dest, + ) => {} + } + }); + } else { + let listener = TcpListener::bind(format!("127.0.0.1:{}", item.src_port)) + .await + .with_context(|| format!("tcp bind failed {}", item.src_port))?; + tokio::spawn(async move { + tokio::select! { + _ = shutdown_rx_.changed() => {} + _ = in_mapping::tcp::tcp_mapping_listen( + listener, + current_device_info, + item.dest, + ) => {} + } + }); + } + } + Ok(()) + } + pub fn stop(&self) { + self.as_vnt().stop() + } + pub async fn wait(&self) { + loop { + let mut receiver = self.shutdown_tx.subscribe(); + if *receiver.borrow() { + return; + } + if receiver.changed().await.is_err() { + return; + } + } + } + + pub fn as_vnt(&self) -> &Vnt { + &self.vnt + } +} + +#[derive(Clone)] +pub struct VntDevice { + net_stack_write: NetStackWrite, +} + +impl DeviceWrite for VntDevice { + fn write(&self, buf: &[u8]) -> std::io::Result { + self.net_stack_write.send_ip(buf)?; + Ok(buf.len()) + } +} diff --git a/vnt-cli/Cargo.toml b/vnt-cli/Cargo.toml index dfe91204..f4675e48 100644 --- a/vnt-cli/Cargo.toml +++ b/vnt-cli/Cargo.toml @@ -1,26 +1,16 @@ [package] name = "vnt-cli" -version = "1.2.10" +version = "1.2.11" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -vnt = { path = "../vnt", package = "vnt", default-features = false } -common = { path = "../common" } -getopts = "0.2.21" -console = "0.15.2" -os_info = "3.7.0" -serde = "1.0" -serde_yaml = "0.9.32" +vnt = { path = "../vnt", package = "vnt", default-features = false, features = ["integrated_tun"] } +common = { path = "../common", default-features = false, features = ["integrated_tun"] } log = "0.4.17" -log4rs = { version = "1.2.0", optional = true } anyhow = "1.0.82" -[dependencies.uuid] -version = "1.4.1" -features = [ - "v4", # Lets you generate random UUIDs -] + [target.'cfg(any(target_os = "linux",target_os = "macos"))'.dependencies] sudo = "0.6.0" @@ -30,24 +20,28 @@ signal-hook = "0.3.17" winapi = { version = "0.3.9", features = ["handleapi", "processthreadsapi", "winnt", "securitybaseapi", "impl-default"] } [features] -default = ["server_encrypt", "aes_gcm", "aes_cbc", "aes_ecb", "sm4_cbc", "chacha20_poly1305", "ip_proxy", "port_mapping", "log", "command", "file_config", "lz4"] -openssl = ["vnt/openssl"] -openssl-vendored = ["vnt/openssl-vendored"] -ring-cipher = ["vnt/ring-cipher"] -aes_cbc = ["vnt/aes_cbc"] -aes_ecb = ["vnt/aes_ecb"] -sm4_cbc = ["vnt/sm4_cbc"] -aes_gcm = ["vnt/aes_gcm"] -chacha20_poly1305 = ["vnt/chacha20_poly1305"] -server_encrypt = ["vnt/server_encrypt"] -ip_proxy = ["vnt/ip_proxy"] -port_mapping = ["vnt/port_mapping"] -lz4 = ["vnt/lz4_compress"] -zstd = ["vnt/zstd_compress"] -log = ["log4rs"] -command = [] -file_config = [] +default = ["default-feature"] +default-feature = ["server_encrypt", "aes_gcm", "aes_cbc", "aes_ecb", "sm4_cbc", "chacha20_poly1305", "ip_proxy", "port_mapping", "log", "command", "file_config", "lz4", "ws"] + +openssl = ["vnt/openssl", "common/openssl"] +openssl-vendored = ["vnt/openssl-vendored", "common/openssl-vendored"] +ring-cipher = ["vnt/ring-cipher", "common/ring-cipher"] +aes_cbc = ["vnt/aes_cbc", "common/aes_cbc"] +aes_ecb = ["vnt/aes_ecb", "common/aes_ecb"] +sm4_cbc = ["vnt/sm4_cbc", "common/sm4_cbc"] +aes_gcm = ["vnt/aes_gcm", "common/aes_gcm"] +chacha20_poly1305 = ["vnt/chacha20_poly1305", "common/chacha20_poly1305"] +server_encrypt = ["vnt/server_encrypt", "common/server_encrypt"] +port_mapping = ["vnt/port_mapping", "common/port_mapping"] +lz4 = ["vnt/lz4_compress", "common/lz4"] +zstd = ["vnt/zstd_compress", "common/zstd"] +ip_proxy = ["vnt/ip_proxy", "common/ip_proxy"] +upnp = ["vnt/upnp", "common/upnp"] +ws = ["vnt/ws", "common/ws"] +wss = ["vnt/wss", "common/wss"] +log = ["common/log"] +command = ["common/command"] +file_config = ["common/file_config"] [build-dependencies] -embed-manifest = "1.4.0" rand = "0.8.5" chrono = "0.4.23" \ No newline at end of file diff --git a/vnt-cli/README.md b/vnt-cli/README.md index 34f9d55b..92518ae3 100644 --- a/vnt-cli/README.md +++ b/vnt-cli/README.md @@ -34,6 +34,8 @@ 注意:仅在windows上支持使用tap,用于兼容低版本windows系统(低版本windows不支持wintun) +使用tap模式需要手动创建tap网卡,使用--nic参数指定已经创建好的tap网卡名称 + ### --nic `` 指定虚拟网卡名称,默认tun模式使用vnt-tun,tap模式使用vnt-tap @@ -92,18 +94,18 @@ aes_gcm/aes_cbc/aes_ecb/sm4_cbc/chacha20_poly1305/chacha20/xor,默认使用aes 特别说明:xor只是对数据进行简单异或,仅仅避免了明文传输,安全性很差,同时对性能影响也极小; -| 密码位数 | model | 加密算法 | -|-------|-------------------|-------------------| -| 1~8位 | aes_gcm | AES128-GCM | -| `>=`8 | aes_gcm | AES256-GCM | -| 1~8位 | aes_cbc | AES128-CBC | -| `>=`8 | aes_cbc | AES256-CBC | -| 1~8位 | aes_ecb | AES128-ECB | -| `>=`8 | aes_ecb | AES256-ECB | -| `>0` | sm4_cbc | SM4-CBC | -| `>0` | chacha20_poly1305 | ChaCha20-Poly1305 | -| `>0` | chacha20 | ChaCha20 | -| `>0` | xor | 简单异或混淆 | +| 密码位数 | model | 加密算法 | +|--------|-------------------|-------------------| +| `< 8` | aes_gcm | AES128-GCM | +| `>= 8` | aes_gcm | AES256-GCM | +| `< 8` | aes_cbc | AES128-CBC | +| `>= 8` | aes_cbc | AES256-CBC | +| `< 8` | aes_ecb | AES128-ECB | +| `>= 8` | aes_ecb | AES256-ECB | +| `> 0` | sm4_cbc | SM4-CBC | +| `> 0` | chacha20_poly1305 | ChaCha20-Poly1305 | +| `> 0` | chacha20 | ChaCha20 | +| `> 0` | xor | 简单异或混淆 | ### --finger @@ -141,9 +143,9 @@ aes_gcm/aes_cbc/aes_ecb/sm4_cbc/chacha20_poly1305/chacha20/xor,默认使用aes 当地址解析失败时,会依次尝试后面的dns,直到有A记录、AAAA记录(或TXT记录)的解析结果 -### --mapping `10.26.0.10:80>` +### --mapping `` -端口映射,可以设置多个映射地址,例如 '--mapping udp:0.0.0.0:80->10.26.0.10:80 --mapping tcp:0.0.0.0:80->10.26.0.11:81' +端口映射,可以设置多个映射地址,例如 '--mapping udp:0.0.0.0:80-10.26.0.10:80 --mapping tcp:0.0.0.0:80-10.26.0.11:81' 表示将本地udp 80端口的数据转发到10.26.0.10:80,将本地tcp 80端口的数据转发到10.26.0.11:81,转发的目的地址可以使用域名+端口 ### --compressor `` @@ -196,9 +198,9 @@ dns: - 223.5.5.5 # 首选dns - 8.8.8.8 # 备选dns mapping: - - udp:0.0.0.0:80->10.26.0.10:80 # 映射udp数据 - - tcp:0.0.0.0:80->10.26.0.10:81 # 映射tcp数据 - - tcp:0.0.0.0:82->localhost:83 # 映射tcp数据 + - udp:0.0.0.0:80-10.26.0.10:80 # 映射udp数据 + - tcp:0.0.0.0:80-10.26.0.10:81 # 映射tcp数据 + - tcp:0.0.0.0:82-localhost:83 # 映射tcp数据 ``` 或者需要哪个配置就加哪个,当然token是必须的 diff --git a/vnt-cli/src/main.rs b/vnt-cli/src/main.rs index fbdd09c1..4c5a23f2 100644 --- a/vnt-cli/src/main.rs +++ b/vnt-cli/src/main.rs @@ -1,350 +1,29 @@ -use anyhow::anyhow; -use std::io; -use std::net::Ipv4Addr; -use std::path::PathBuf; -use std::str::FromStr; - -use console::style; -use getopts::Options; - -use common::args_parse::{ips_parse, out_ips_parse}; -use vnt::channel::punch::PunchModel; -use vnt::channel::UseChannelType; -use vnt::cipher::CipherModel; -use vnt::compression::Compressor; +use common::callback; use vnt::core::{Config, Vnt}; - -#[cfg(feature = "command")] -mod command; -mod config; -#[cfg(feature = "command")] -mod console_out; -mod generated_serial_number; mod root_check; - -pub fn app_home() -> io::Result { - let root_path = match std::env::current_exe() { - Ok(path) => { - if let Some(v) = path.as_path().parent() { - v.to_path_buf() +fn main() { + let (config, _vnt_link_config, cmd) = match common::cli::parse_args_config() { + Ok(rs) => { + if let Some(rs) = rs { + rs } else { - log::warn!("current_exe parent none:{:?}", path); - PathBuf::new() + return; } } Err(e) => { - log::warn!("current_exe err:{:?}", e); - PathBuf::new() - } - }; - let path = root_path.join("env"); - if !path.exists() { - std::fs::create_dir_all(&path)?; - } - Ok(path) -} - -fn main() { - #[cfg(feature = "log")] - let _ = log4rs::init_file("log4rs.yaml", Default::default()); - let args: Vec = std::env::args().collect(); - let program = args[0].clone(); - let mut opts = Options::new(); - opts.optopt("k", "", "组网标识", ""); - opts.optopt("n", "", "设备名称", ""); - opts.optopt("d", "", "设备标识", ""); - opts.optflag("c", "", "关闭交互式命令"); - opts.optopt("s", "", "注册和中继服务器地址", ""); - opts.optmulti("e", "", "stun服务器", ""); - opts.optflag("a", "", "使用tap模式"); - opts.optopt("", "nic", "虚拟网卡名称,windows下使用tap则必填", ""); - opts.optmulti("i", "", "配置点对网(IP代理)入站时使用", ""); - opts.optmulti("o", "", "配置点对网出站时使用", ""); - opts.optopt("w", "", "客户端加密", ""); - opts.optflag("W", "", "服务端加密"); - opts.optopt("u", "", "自定义mtu(默认为1430)", ""); - opts.optflag("", "tcp", "tcp"); - opts.optopt("", "ip", "指定虚拟ip", ""); - opts.optflag("", "relay", "仅使用服务器转发"); - opts.optopt("", "par", "任务并行度(必须为正整数)", ""); - opts.optopt("", "model", "加密模式", ""); - opts.optflag("", "finger", "指纹校验"); - opts.optopt("", "punch", "取值ipv4/ipv6", ""); - opts.optopt("", "ports", "监听的端口", ""); - opts.optflag("", "cmd", "开启窗口输入"); - opts.optflag("", "no-proxy", "关闭内置代理"); - opts.optflag("", "first-latency", "优先延迟"); - opts.optopt("", "use-channel", "使用通道 relay/p2p", ""); - opts.optopt("", "packet-loss", "丢包率", ""); - opts.optopt("", "packet-delay", "延迟", ""); - opts.optmulti("", "dns", "dns", ""); - opts.optmulti("", "mapping", "mapping", ""); - opts.optopt("f", "", "配置文件", ""); - opts.optopt("", "compressor", "压缩算法", ""); - //"后台运行时,查看其他设备列表" - opts.optflag("", "list", "后台运行时,查看其他设备列表"); - opts.optflag("", "all", "后台运行时,查看其他设备完整信息"); - opts.optflag("", "info", "后台运行时,查看当前设备信息"); - opts.optflag("", "route", "后台运行时,查看数据转发路径"); - opts.optflag("", "stop", "停止后台运行"); - opts.optflag("h", "help", "帮助"); - let matches = match opts.parse(&args[1..]) { - Ok(m) => m, - Err(f) => { - print_usage(&program, opts); - println!("{}", f.to_string()); + println!("{}", e); return; } }; - if matches.opt_present("h") || args.len() == 1 { - print_usage(&program, opts); - return; - } + main0(config, cmd) +} +fn main0(config: Config, _show_cmd: bool) { if !root_check::is_app_elevated() { println!("Please run it with administrator or root privileges"); #[cfg(any(target_os = "linux", target_os = "macos"))] sudo::escalate_if_needed().unwrap(); return; } - #[cfg(feature = "command")] - if matches.opt_present("list") { - command::command(command::CommandEnum::List); - return; - } else if matches.opt_present("info") { - command::command(command::CommandEnum::Info); - return; - } else if matches.opt_present("stop") { - command::command(command::CommandEnum::Stop); - return; - } else if matches.opt_present("route") { - command::command(command::CommandEnum::Route); - return; - } else if matches.opt_present("all") { - command::command(command::CommandEnum::All); - return; - } - let conf = matches.opt_str("f"); - let (config, cmd) = if conf.is_some() { - match config::read_config(&conf.unwrap()) { - Ok(c) => c, - Err(e) => { - println!("conf err {}", e); - return; - } - } - } else { - if !matches.opt_present("k") { - print_usage(&program, opts); - println!("parameter -k not found ."); - return; - } - #[cfg(target_os = "windows")] - let tap = matches.opt_present("a"); - let device_name = matches.opt_str("nic"); - let token: String = matches.opt_get("k").unwrap().unwrap(); - let device_id = matches.opt_get_default("d", String::new()).unwrap(); - let device_id = if device_id.is_empty() { - config::get_device_id() - } else { - device_id - }; - if device_id.is_empty() { - print_usage(&program, opts); - println!("parameter -d not found ."); - return; - } - let name = matches - .opt_get_default("n", os_info::get().to_string()) - .unwrap(); - let server_address_str = matches - .opt_get_default("s", "nat1.wherewego.top:29872".to_string()) - .unwrap(); - - let mut stun_server = matches.opt_strs("e"); - if stun_server.is_empty() { - stun_server.push("stun1.l.google.com:19302".to_string()); - stun_server.push("stun2.l.google.com:19302".to_string()); - stun_server.push("stun.miwifi.com:3478".to_string()); - } - let dns = matches.opt_strs("dns"); - let in_ip = matches.opt_strs("i"); - let in_ip = match ips_parse(&in_ip) { - Ok(in_ip) => in_ip, - Err(e) => { - print_usage(&program, opts); - println!(); - println!("-i: {:?} {}", in_ip, e); - println!("example: -i 192.168.0.0/24,10.26.0.3"); - return; - } - }; - let out_ip = matches.opt_strs("o"); - let out_ip = match out_ips_parse(&out_ip) { - Ok(out_ip) => out_ip, - Err(e) => { - print_usage(&program, opts); - println!(); - println!("-o: {:?} {}", out_ip, e); - println!("example: -o 0.0.0.0/0"); - return; - } - }; - let password: Option = matches.opt_get("w").unwrap(); - let server_encrypt = matches.opt_present("W"); - #[cfg(not(feature = "server_encrypt"))] - { - if server_encrypt { - println!("Server encryption not supported"); - return; - } - } - let mtu: Option = matches.opt_get("u").unwrap(); - let mtu = if let Some(mtu) = mtu { - match u32::from_str(&mtu) { - Ok(mtu) => Some(mtu), - Err(e) => { - print_usage(&program, opts); - println!(); - println!("'-u {}' {}", mtu, e); - return; - } - } - } else { - None - }; - let virtual_ip: Option = matches.opt_get("ip").unwrap(); - let virtual_ip = - virtual_ip.map(|v| Ipv4Addr::from_str(&v).expect(&format!("'--ip {}' error", v))); - if let Some(virtual_ip) = virtual_ip { - if virtual_ip.is_unspecified() || virtual_ip.is_broadcast() || virtual_ip.is_multicast() - { - println!("'--ip {}' invalid", virtual_ip); - return; - } - } - let tcp_channel = matches.opt_present("tcp"); - let relay = matches.opt_present("relay"); - - let parallel = matches.opt_get::("par").unwrap().unwrap_or(1); - if parallel == 0 { - println!("'--par {}' invalid", parallel); - return; - } - - let cipher_model = match matches.opt_get::("model") { - Ok(model) => { - #[cfg(not(any(feature = "aes_gcm", feature = "server_encrypt")))] - { - if password.is_some() && model.is_none() { - println!("'--model ' undefined"); - return; - } - model.unwrap_or(CipherModel::None) - } - #[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))] - model.unwrap_or(CipherModel::AesGcm) - } - Err(e) => { - println!("'--model ' invalid,{}", e); - return; - } - }; - - let finger = matches.opt_present("finger"); - let punch_model = matches - .opt_get::("punch") - .unwrap() - .unwrap_or(PunchModel::All); - let use_channel_type = matches - .opt_get::("use-channel") - .unwrap() - .unwrap_or_else(|| { - if relay { - UseChannelType::Relay - } else { - UseChannelType::All - } - }); - - let ports = matches - .opt_get::("ports") - .unwrap_or(None) - .map(|v| v.split(",").map(|x| x.parse().unwrap_or(0)).collect()); - - let cmd = matches.opt_present("cmd"); - #[cfg(feature = "ip_proxy")] - let no_proxy = matches.opt_present("no-proxy"); - let first_latency = matches.opt_present("first-latency"); - let packet_loss = matches - .opt_get::("packet-loss") - .expect("--packet-loss"); - let packet_delay = matches - .opt_get::("packet-delay") - .expect("--packet-delay") - .unwrap_or(0); - #[cfg(feature = "port_mapping")] - let port_mapping_list = matches.opt_strs("mapping"); - let compressor = if let Some(compressor) = matches.opt_str("compressor").as_ref() { - Compressor::from_str(compressor) - .map_err(|e| anyhow!("{}", e)) - .unwrap() - } else { - Compressor::None - }; - let config = match Config::new( - #[cfg(target_os = "windows")] - tap, - token, - device_id, - name, - server_address_str, - dns, - stun_server, - in_ip, - out_ip, - password, - mtu, - tcp_channel, - virtual_ip, - #[cfg(feature = "ip_proxy")] - no_proxy, - server_encrypt, - parallel, - cipher_model, - finger, - punch_model, - ports, - first_latency, - device_name, - use_channel_type, - packet_loss, - packet_delay, - #[cfg(feature = "port_mapping")] - port_mapping_list, - compressor, - ) { - Ok(config) => config, - Err(e) => { - println!("config.toml error: {}", e); - return; - } - }; - (config, cmd) - }; - println!("version {}", vnt::VNT_VERSION); - println!("Serial:{}", generated_serial_number::SERIAL_NUMBER); - log::info!( - "version:{},Serial:{}", - vnt::VNT_VERSION, - generated_serial_number::SERIAL_NUMBER - ); - main0(config, cmd); - std::process::exit(0); -} - -mod callback; - -fn main0(config: Config, _show_cmd: bool) { #[cfg(feature = "port_mapping")] for (is_tcp, addr, dest) in config.port_mapping_list.iter() { if *is_tcp { @@ -353,7 +32,13 @@ fn main0(config: Config, _show_cmd: bool) { println!("UDP port mapping {}->{}", addr, dest) } } - let vnt_util = Vnt::new(config, callback::VntHandler {}).unwrap(); + let vnt_util = match Vnt::new(config, callback::VntHandler {}) { + Ok(vnt) => vnt, + Err(e) => { + println!("error: {:?}", e); + std::process::exit(1); + } + }; #[cfg(any(target_os = "linux", target_os = "macos"))] { let vnt_c = vnt_util.clone(); @@ -383,7 +68,7 @@ fn main0(config: Config, _show_cmd: bool) { std::thread::Builder::new() .name("CommandServer".into()) .spawn(move || { - if let Err(e) = command::server::CommandServer::new().start(vnt_c) { + if let Err(e) = common::command::server::CommandServer::new().start(vnt_c) { log::warn!("cmd:{:?}", e); } }) @@ -392,10 +77,10 @@ fn main0(config: Config, _show_cmd: bool) { let mut cmd = String::new(); loop { cmd.clear(); - println!("======== input:list,info,route,all,stop ========"); - match io::stdin().read_line(&mut cmd) { + println!("======== input:list,info,route,all,stop,chart_a,chart_b[:ip] ========"); + match std::io::stdin().read_line(&mut cmd) { Ok(len) => { - if !command(&cmd[..len], &vnt_util) { + if !common::command::command_str(&cmd[..len], &vnt_util) { break; } } @@ -410,151 +95,3 @@ fn main0(config: Config, _show_cmd: bool) { vnt_util.wait() } - -#[cfg(feature = "command")] -fn command(cmd: &str, vnt: &Vnt) -> bool { - if cmd.is_empty() { - return false; - } - match cmd.to_lowercase().trim() { - "list" => { - let list = command::command_list(&vnt); - console_out::console_device_list(list); - } - "info" => { - let info = command::command_info(&vnt); - console_out::console_info(info); - } - "route" => { - let route = command::command_route(&vnt); - console_out::console_route_table(route); - } - "all" => { - let list = command::command_list(&vnt); - console_out::console_device_list_all(list); - } - "stop" => { - let _ = vnt.stop(); - return false; - } - _ => {} - } - println!(); - return true; -} - -fn print_usage(program: &str, _opts: Options) { - println!("Usage: {} [options]", program); - println!("version:{}", vnt::VNT_VERSION); - println!("Serial:{}", generated_serial_number::SERIAL_NUMBER); - println!("Options:"); - println!( - " -k {}", - green("使用相同的token,就能组建一个局域网络".to_string()) - ); - println!(" -n 给设备一个名字,便于区分不同设备,默认使用系统版本"); - println!(" -d 设备唯一标识符,不使用--ip参数时,服务端凭此参数分配虚拟ip,注意不能重复"); - println!(" -s 注册和中继服务器地址,以'TXT:'开头表示解析TXT记录"); - println!(" -e stun服务器,用于探测NAT类型,可使用多个地址,如-e stun1.l.google.com -e stun2.l.google.com"); - #[cfg(target_os = "windows")] - println!( - " -a 使用tap模式,默认使用tun模式,使用tap时需要配合'--nic'参数指定tap网卡" - ); - println!(" -i 配置点对网(IP代理)时使用,-i 192.168.0.0/24,10.26.0.3表示允许接收网段192.168.0.0/24的数据"); - println!(" 并转发到10.26.0.3,可指定多个网段"); - println!(" -o 配置点对网时使用,-o 192.168.0.0/24表示允许将数据转发到192.168.0.0/24,可指定多个网段"); - - println!(" -w 使用该密码生成的密钥对客户端数据进行加密,并且服务端无法解密,使用相同密码的客户端才能通信"); - #[cfg(feature = "server_encrypt")] - println!(" -W 加密当前客户端和服务端通信的数据,请留意服务端指纹是否正确"); - println!(" -u 自定义mtu(不加密默认为1450,加密默认为1410)"); - #[cfg(feature = "file_config")] - println!(" -f 读取配置文件中的配置"); - - println!(" --tcp 和服务端使用tcp通信,默认使用udp,遇到udp qos时可指定使用tcp"); - println!(" --ip 指定虚拟ip,指定的ip不能和其他设备重复,必须有效并且在服务端所属网段下,默认情况由服务端分配"); - println!(" --par 任务并行度(必须为正整数),默认值为1"); - let mut enums = String::new(); - #[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))] - enums.push_str("/aes_gcm"); - #[cfg(feature = "chacha20_poly1305")] - enums.push_str("/chacha20_poly1305/chacha20"); - #[cfg(feature = "aes_cbc")] - enums.push_str("/aes_cbc"); - #[cfg(feature = "aes_ecb")] - enums.push_str("/aes_ecb"); - #[cfg(feature = "sm4_cbc")] - enums.push_str("/sm4_cbc"); - enums.push_str("/xor"); - println!( - " --model 加密模式(默认aes_gcm),可选值{}", - &enums[1..] - ); - #[cfg(any( - feature = "aes_gcm", - feature = "chacha20_poly1305", - feature = "server_encrypt", - feature = "aes_cbc", - feature = "aes_ecb", - feature = "sm4_cbc" - ))] - println!(" --finger 增加数据指纹校验,可增加安全性,如果服务端开启指纹校验,则客户端也必须开启"); - println!(" --punch 取值ipv4/ipv6/all,ipv4表示仅使用ipv4打洞"); - println!(" --ports 取值0~65535,指定本地监听的一组端口,默认监听两个随机端口,使用过多端口会增加网络负担"); - #[cfg(feature = "command")] - println!(" --cmd 开启交互式命令,使用此参数开启控制台输入"); - #[cfg(feature = "ip_proxy")] - println!(" --no-proxy 关闭内置代理,如需点对网则需要配置网卡NAT转发"); - println!(" --first-latency 优先低延迟的通道,默认情况优先使用p2p通道"); - println!(" --use-channel 使用通道 relay/p2p/all,默认两者都使用"); - println!(" --nic 指定虚拟网卡名称"); - println!(" --packet-loss <0> 模拟丢包,取值0~1之间的小数,程序会按设定的概率主动丢包,可用于模拟弱网"); - println!( - " --packet-delay <0> 模拟延迟,整数,单位毫秒(ms),程序会按设定的值延迟发包,可用于模拟弱网" - ); - println!(" --dns DNS服务器地址,可使用多个dns,不指定时使用系统解析"); - #[cfg(feature = "port_mapping")] - println!(" --mapping 端口映射,例如 --mapping udp:0.0.0.0:80->10.26.0.10:80 --mapping tcp:0.0.0.0:80->10.26.0.10:80"); - #[cfg(all(feature = "lz4", feature = "zstd"))] - println!(" --compressor 启用压缩,可选值lz4/zstd<,level>,level为压缩级别,例如 --compressor lz4 或--compressor zstd,10"); - #[cfg(feature = "lz4")] - #[cfg(not(feature = "zstd"))] - println!(" --compressor 启用压缩,可选值lz4,例如 --compressor lz4"); - #[cfg(feature = "zstd")] - #[cfg(not(feature = "lz4"))] - println!(" --compressor 启用压缩,可选值zstd<,level>,level为压缩级别,例如 --compressor zstd,10"); - println!(); - #[cfg(feature = "command")] - { - println!( - " --list {}", - yellow("后台运行时,查看其他设备列表".to_string()) - ); - println!( - " --all {}", - yellow("后台运行时,查看其他设备完整信息".to_string()) - ); - println!( - " --info {}", - yellow("后台运行时,查看当前设备信息".to_string()) - ); - println!( - " --route {}", - yellow("后台运行时,查看数据转发路径".to_string()) - ); - println!( - " --stop {}", - yellow("停止后台运行".to_string()) - ); - } - println!(" -h, --help 帮助"); -} - -fn green(str: String) -> impl std::fmt::Display { - style(str).green() -} - -#[cfg(feature = "command")] -fn yellow(str: String) -> impl std::fmt::Display { - style(str).yellow() -} diff --git a/vnt-jni/Cargo.toml b/vnt-jni/Cargo.toml deleted file mode 100644 index 7ac63b5e..00000000 --- a/vnt-jni/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "vnt-jni" -version = "1.2.10" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -common = { path = "../common" } -vnt = {path="../vnt"} -parking_lot = "0.12.1" - -jni = { version = "0.21.1", default-features = false } -log = "0.4.20" -spki = { version = "0.7.2", features = ["fingerprint", "alloc","base64","pem"]} - -[target.'cfg(target_os = "android")'.dependencies] -android_logger = "0.13" - -[lib] -crate-type = ["staticlib", "cdylib"] \ No newline at end of file diff --git a/vnt-jni/README.md b/vnt-jni/README.md deleted file mode 100644 index 676a70ca..00000000 --- a/vnt-jni/README.md +++ /dev/null @@ -1 +0,0 @@ -## 提供给安卓端使用 \ No newline at end of file diff --git a/vnt-jni/java/top/wherewego/vnt/jni/CallBack.java b/vnt-jni/java/top/wherewego/vnt/jni/CallBack.java deleted file mode 100644 index 52e22f3e..00000000 --- a/vnt-jni/java/top/wherewego/vnt/jni/CallBack.java +++ /dev/null @@ -1,77 +0,0 @@ -package top.wherewego.vnt.jni; - -import top.wherewego.vnt.jni.param.*; - -/** - * 回调 - * - * @author https://github.com/lbl8603/vnt - */ -public interface CallBack { - /** - * 连接成功的回调 - */ - void success(); - - /** - * 创建虚拟网卡成功的回调方法 - * 仅在 windows/linux/macos上使用 - * - * @param info 网卡信息 - */ - void createTun(DeviceInfo info); - - /** - * 连接服务端 - * - * @param info 将要连接的服务端信息 - */ - void connect(ConnectInfo info); - - /** - * 和服务端握手 - * - * @param info 握手信息 - * @return 是否确认握手 - */ - boolean handshake(HandshakeInfo info); - - /** - * 注册成功回调 - * - * @param info 注册信息 - * @return 是否确认注册信息 - */ - boolean register(RegisterInfo info); - - /** - * 创建网卡回调 - * 仅在android上使用 - * - * @param info 创建配置 - * @return 网卡fd - */ - - int generateTun(DeviceConfig info); - - /** - * 对端用户列表 - * - * @param infoArray - */ - void peerClientList(PeerClientInfo[] infoArray); - - - /** - * 异常回调 - * - * @param info 错误信息 - */ - void error(ErrorInfo info); - - /** - * 服务停止 - */ - void stop(); - -} diff --git a/vnt-jni/java/top/wherewego/vnt/jni/Config.java b/vnt-jni/java/top/wherewego/vnt/jni/Config.java deleted file mode 100644 index 2bfe7eed..00000000 --- a/vnt-jni/java/top/wherewego/vnt/jni/Config.java +++ /dev/null @@ -1,333 +0,0 @@ -package top.wherewego.vnt.jni; - -import java.io.Serializable; -import java.util.Arrays; - -/** - * 启动配置 - * - * @author https://github.com/lbl8603/vnt - */ -public class Config implements Serializable { - /** - * 是否是tap模式,仅支持windows - */ - private boolean tap; - /** - * 组网标识 - */ - private String token; - /** - * 设备名称 - */ - private String name; - /** - * 客户端间加密的密码 - */ - private String password; - /** - * 客户端间加密模式 aes_gcm/aes_cbc/aes_ecb/sm4_cbc - */ - private String cipherModel; - /** - * 打洞模式 ipv4/ipv6/all - */ - private String punchModel; - /** - * mtu 默认自动计算 - */ - private Integer mtu; - /** - * 是否开启服务端加密 - */ - private boolean serverEncrypt; - /** - * 设备id,请使用唯一值 - */ - private String deviceId; - /** - * 服务端地址 - */ - private String server; - /** - * dns地址 - */ - private String[] dns; - /** - * 端口映射 - */ - private String[] portMapping; - /** - * stun服务地址 - */ - private String[] stunServer; - /** - * 和服务端使用tcp通信,默认使用udp - */ - private boolean tcp; - /** - * 指定组网IP - */ - private String ip; - /** - * 开启加密指纹校验 - */ - private boolean finger; - /** - * 延迟优先,默认p2p优先 - */ - private boolean firstLatency; - /** - * 点对网入口 格式 192.168.0.0/26,10.26.0.2 - */ - private String[] inIps; - /** - * 点对网出口 格式 192.168.0.0/26 - */ - private String[] outIps; - /** - * 端口组,udp会监听一组端口,tcp监听ports[0]端口 - */ - private int[] ports; - /** - * 虚拟网卡名称 仅在linux、windows、macos上支持 - */ - private String deviceName; - /** - * enum: relay/p2p/all - */ - private String useChannel; - /** - * 模拟丢包率,取0~1之间的数,为null表示不丢包,1表示全部丢包 - */ - private Double packetLossRate; - /** - * 模拟延迟 单位毫秒(ms) - */ - private Integer packetDelay; - - public Config() { - } - - public boolean isTap() { - return tap; - } - - public void setTap(boolean tap) { - this.tap = tap; - } - - public String getToken() { - return token; - } - - public void setToken(String token) { - this.token = token; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getCipherModel() { - return cipherModel; - } - - public void setCipherModel(String cipherModel) { - this.cipherModel = cipherModel; - } - - public String getPunchModel() { - return punchModel; - } - - public void setPunchModel(String punchModel) { - this.punchModel = punchModel; - } - - public Integer getMtu() { - return mtu; - } - - public void setMtu(Integer mtu) { - this.mtu = mtu; - } - - public boolean isServerEncrypt() { - return serverEncrypt; - } - - public void setServerEncrypt(boolean serverEncrypt) { - this.serverEncrypt = serverEncrypt; - } - - - public String getDeviceId() { - return deviceId; - } - - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - } - - public String getServer() { - return server; - } - - public void setServer(String server) { - this.server = server; - } - - public String[] getDns() { - return dns; - } - - public void setDns(String[] dns) { - this.dns = dns; - } - public String[] getPortMapping() { - return portMapping; - } - - public void setPortMapping(String[] portMapping) { - this.portMapping = portMapping; - } - - public String[] getStunServer() { - return stunServer; - } - - public void setStunServer(String[] stunServer) { - this.stunServer = stunServer; - } - - public boolean isTcp() { - return tcp; - } - - public void setTcp(boolean tcp) { - this.tcp = tcp; - } - - public String getIp() { - return ip; - } - - public void setIp(String ip) { - this.ip = ip; - } - - public boolean isFinger() { - return finger; - } - - public void setFinger(boolean finger) { - this.finger = finger; - } - - public boolean isFirstLatency() { - return firstLatency; - } - - public void setFirstLatency(boolean firstLatency) { - this.firstLatency = firstLatency; - } - - public String[] getInIps() { - return inIps; - } - - public void setInIps(String[] inIps) { - this.inIps = inIps; - } - - public String[] getOutIps() { - return outIps; - } - - public void setOutIps(String[] outIps) { - this.outIps = outIps; - } - - public int[] getPorts() { - return ports; - } - - public void setPorts(int[] ports) { - this.ports = ports; - } - - public String getDeviceName() { - return deviceName; - } - - public void setDeviceName(String deviceName) { - this.deviceName = deviceName; - } - - public String getUseChannel() { - return useChannel; - } - - public void setUseChannel(String useChannel) { - this.useChannel = useChannel; - } - - public Double getPacketLossRate() { - return packetLossRate; - } - - public void setPacketLossRate(Double packetLossRate) { - this.packetLossRate = packetLossRate; - } - - public Integer getPacketDelay() { - return packetDelay; - } - - public void setPacketDelay(Integer packetDelay) { - this.packetDelay = packetDelay; - } - - @Override - public String toString() { - return "Config{" + - "tap=" + tap + - ", token='" + token + '\'' + - ", name='" + name + '\'' + - ", password='" + password + '\'' + - ", cipherModel='" + cipherModel + '\'' + - ", punchModel='" + punchModel + '\'' + - ", mtu=" + mtu + - ", serverEncrypt=" + serverEncrypt + - ", deviceId='" + deviceId + '\'' + - ", server='" + server + '\'' + - ", dns=" + Arrays.toString(dns) + - ", portMapping=" + Arrays.toString(portMapping) + - ", stunServer=" + Arrays.toString(stunServer) + - ", tcp=" + tcp + - ", ip='" + ip + '\'' + - ", finger=" + finger + - ", firstLatency=" + firstLatency + - ", inIps=" + Arrays.toString(inIps) + - ", outIps=" + Arrays.toString(outIps) + - ", ports=" + Arrays.toString(ports) + - ", deviceName='" + deviceName + '\'' + - ", useChannel='" + useChannel + '\'' + - ", packetLossRate=" + packetLossRate + - ", packetDelay=" + packetDelay + - '}'; - } -} diff --git a/vnt-jni/java/top/wherewego/vnt/jni/IpUtils.java b/vnt-jni/java/top/wherewego/vnt/jni/IpUtils.java deleted file mode 100644 index 607666f7..00000000 --- a/vnt-jni/java/top/wherewego/vnt/jni/IpUtils.java +++ /dev/null @@ -1,43 +0,0 @@ -package top.wherewego.vnt.jni; - -/** - * ip转换 - * - * @author https://github.com/lbl8603/vnt - */ -public class IpUtils { - /** - * 将整数的ip地址转成字符串,例如 0 转成 "0.0.0.0" - * - * @param ipAddress - * @return - */ - public static String intToIpAddress(int ipAddress) { - - return ((ipAddress & 0xFF000000) >>> 24) + "." + - ((ipAddress & 0x00FF0000) >>> 16) + "." + - ((ipAddress & 0x0000FF00) >>> 8) + "." + - (ipAddress & 0x000000FF); - } - - /** - * 返回掩码的长度 - * - * @param subnetMask - * @return - */ - public static int subnetMaskToPrefixLength(int subnetMask) { - int prefixLength = 0; - int bit = 1 << 31; - - while (subnetMask != 0) { - if ((subnetMask & bit) != bit) { - break; - } - prefixLength++; - subnetMask <<= 1; - } - - return prefixLength; - } -} diff --git a/vnt-jni/java/top/wherewego/vnt/jni/PeerRouteInfo.java b/vnt-jni/java/top/wherewego/vnt/jni/PeerRouteInfo.java deleted file mode 100644 index adc92a7d..00000000 --- a/vnt-jni/java/top/wherewego/vnt/jni/PeerRouteInfo.java +++ /dev/null @@ -1,46 +0,0 @@ -package top.wherewego.vnt.jni; - -/** - * 对端设备信息 - * - * @author https://github.com/lbl8603/vnt - */ -public class PeerRouteInfo { - private final int virtualIp; - private final String name; - private final String status; - private final Route route; - - public PeerRouteInfo(int virtualIp, String name, String status, Route route) { - this.virtualIp = virtualIp; - this.name = name; - this.status = status; - this.route = route; - } - - public int getVirtualIp() { - return virtualIp; - } - - public String getName() { - return name; - } - - public String getStatus() { - return status; - } - - public Route getRoute() { - return route; - } - - @Override - public String toString() { - return "PeerDeviceInfo{" + - "virtualIp=" + IpUtils.intToIpAddress(virtualIp) + - ", name='" + name + '\'' + - ", status='" + status + '\'' + - ", route=" + route + - '}'; - } -} diff --git a/vnt-jni/java/top/wherewego/vnt/jni/Route.java b/vnt-jni/java/top/wherewego/vnt/jni/Route.java deleted file mode 100644 index fe687027..00000000 --- a/vnt-jni/java/top/wherewego/vnt/jni/Route.java +++ /dev/null @@ -1,50 +0,0 @@ -package top.wherewego.vnt.jni; - -/** - * 路由信息 - * - * @author https://github.com/lbl8603/vnt - */ -public class Route { - /** - * 是否使用tcp - */ - private final boolean tcp; - private final String address; - private final byte metric; - private final int rt; - - - public Route(boolean tcp, String address, byte metric, int rt) { - this.tcp = tcp; - this.address = address; - this.metric = metric; - this.rt = rt; - } - - public boolean isTcp() { - return tcp; - } - - public String getAddress() { - return address; - } - - public byte getMetric() { - return metric; - } - - public int getRt() { - return rt; - } - - @Override - public String toString() { - return "Route{" + - "tcp=" + tcp + - ", address='" + address + '\'' + - ", metric=" + metric + - ", rt=" + rt + - '}'; - } -} diff --git a/vnt-jni/java/top/wherewego/vnt/jni/Vnt.java b/vnt-jni/java/top/wherewego/vnt/jni/Vnt.java deleted file mode 100644 index e931a562..00000000 --- a/vnt-jni/java/top/wherewego/vnt/jni/Vnt.java +++ /dev/null @@ -1,53 +0,0 @@ -package top.wherewego.vnt.jni; - -import java.io.Closeable; -import java.io.IOException; - -/** - * vnt的Java映射 - * - * @author https://github.com/lbl8603/vnt - */ -public class Vnt implements Closeable { - private final long raw; - - public Vnt(Config config, CallBack callBack) throws Exception { - this.raw = new0(config, callBack); - if (this.raw == 0) { - throw new RuntimeException(); - } - } - - public void stop() { - stop0(raw); - } - - public void await() { - wait0(raw); - } - - public boolean awaitTimeout(long ms) { - return waitTimeout0(raw, ms); - } - - public PeerRouteInfo[] list() { - return list0(raw); - } - - private native long new0(Config config, CallBack callBack) throws Exception; - - private native void stop0(long raw); - - private native void wait0(long raw); - - private native boolean waitTimeout0(long raw, long ms); - - private native void drop0(long raw); - - private native PeerRouteInfo[] list0(long raw); - - @Override - public void close() throws IOException { - drop0(raw); - } -} diff --git a/vnt-jni/java/top/wherewego/vnt/jni/param/ConnectInfo.java b/vnt-jni/java/top/wherewego/vnt/jni/param/ConnectInfo.java deleted file mode 100644 index 414fb96b..00000000 --- a/vnt-jni/java/top/wherewego/vnt/jni/param/ConnectInfo.java +++ /dev/null @@ -1,32 +0,0 @@ -package top.wherewego.vnt.jni.param; - -/** - * 连接信息 - * - * @author https://github.com/lbl8603/vnt - */ -public class ConnectInfo { - private final long count; - private final String address; - - public ConnectInfo(long count, String address) { - this.count = count; - this.address = address; - } - - public long getCount() { - return count; - } - - public String getAddress() { - return address; - } - - @Override - public String toString() { - return "ConnectInfo{" + - "count=" + count + - ", address='" + address + '\'' + - '}'; - } -} diff --git a/vnt-jni/java/top/wherewego/vnt/jni/param/DeviceConfig.java b/vnt-jni/java/top/wherewego/vnt/jni/param/DeviceConfig.java deleted file mode 100644 index 91aa843f..00000000 --- a/vnt-jni/java/top/wherewego/vnt/jni/param/DeviceConfig.java +++ /dev/null @@ -1,72 +0,0 @@ -package top.wherewego.vnt.jni.param; - -import top.wherewego.vnt.jni.IpUtils; - -import java.util.Arrays; - -/** - * 创建网卡所需信息,仅在android上使用 - * - * @author https://github.com/lbl8603/vnt - */ -public class DeviceConfig { - /** - * 虚拟IP - */ - public final int virtualIp; - /** - * 掩码 - */ - public final int virtualNetmask; - /** - * 网关 - */ - public final int virtualGateway; - /** - * 虚拟网段 - */ - public final int virtualNetwork; - /** - * 额外路由,来自点对网的路由配置 - */ - public final String[] externalRoute; - - public DeviceConfig(int virtualIp, int virtualNetmask, int virtualGateway, int virtualNetwork, String[] externalRoute) { - this.virtualIp = virtualIp; - this.virtualNetmask = virtualNetmask; - this.virtualGateway = virtualGateway; - this.virtualNetwork = virtualNetwork; - this.externalRoute = externalRoute; - } - - public int getVirtualIp() { - return virtualIp; - } - - public int getVirtualNetmask() { - return virtualNetmask; - } - - public int getVirtualGateway() { - return virtualGateway; - } - - public int getVirtualNetwork() { - return virtualNetwork; - } - - public String[] getExternalRoute() { - return externalRoute; - } - - @Override - public String toString() { - return "DeviceConfig{" + - "virtualIp=" + IpUtils.intToIpAddress(virtualIp) + - ", virtualNetmask=" + IpUtils.intToIpAddress(virtualNetmask) + - ", virtualGateway=" + IpUtils.intToIpAddress(virtualGateway) + - ", virtualNetwork=" + IpUtils.intToIpAddress(virtualNetwork) + - ", externalRoute=" + Arrays.toString(externalRoute) + - '}'; - } -} diff --git a/vnt-jni/java/top/wherewego/vnt/jni/param/DeviceInfo.java b/vnt-jni/java/top/wherewego/vnt/jni/param/DeviceInfo.java deleted file mode 100644 index ddb8f0a1..00000000 --- a/vnt-jni/java/top/wherewego/vnt/jni/param/DeviceInfo.java +++ /dev/null @@ -1,38 +0,0 @@ -package top.wherewego.vnt.jni.param; - -/** - * 网卡信息 仅在 windows/linux/macos上使用 - * - * @author https://github.com/lbl8603/vnt - */ -public class DeviceInfo { - /** - * 虚拟网卡名称 - */ - private final String name; - /** - * 虚拟网卡版本 - */ - private final String version; - - public DeviceInfo(String name, String version) { - this.name = name; - this.version = version; - } - - public String getName() { - return name; - } - - public String getVersion() { - return version; - } - - @Override - public String toString() { - return "DeviceInfo{" + - "name='" + name + '\'' + - ", version='" + version + '\'' + - '}'; - } -} diff --git a/vnt-jni/java/top/wherewego/vnt/jni/param/ErrorInfo.java b/vnt-jni/java/top/wherewego/vnt/jni/param/ErrorInfo.java deleted file mode 100644 index f24e4433..00000000 --- a/vnt-jni/java/top/wherewego/vnt/jni/param/ErrorInfo.java +++ /dev/null @@ -1,65 +0,0 @@ -package top.wherewego.vnt.jni.param; - -/** - * 异常回调信息 - * - * @author https://github.com/lbl8603/vnt - */ -public class ErrorInfo { - /** - * 错误码 - */ - public final ErrorCodeEnum code; - /** - * 错误信息,可能为空 - */ - public final String msg; - - public ErrorInfo(int code, String msg) { - switch (code) { - case 1: - this.code = ErrorCodeEnum.TokenError; - break; - case 2: - this.code = ErrorCodeEnum.Disconnect; - break; - case 3: - this.code = ErrorCodeEnum.AddressExhausted; - break; - case 4: - this.code = ErrorCodeEnum.IpAlreadyExists; - break; - case 5: - this.code = ErrorCodeEnum.InvalidIp; - break; - default: - this.code = ErrorCodeEnum.Unknown; - } - this.msg = msg; - } - - public ErrorCodeEnum getCode() { - return code; - } - - public String getMsg() { - return msg; - } - - public enum ErrorCodeEnum { - TokenError, - Disconnect, - AddressExhausted, - IpAlreadyExists, - InvalidIp, - Unknown, - } - - @Override - public String toString() { - return "ErrorInfo{" + - "code=" + code + - ", msg='" + msg + '\'' + - '}'; - } -} diff --git a/vnt-jni/java/top/wherewego/vnt/jni/param/HandshakeInfo.java b/vnt-jni/java/top/wherewego/vnt/jni/param/HandshakeInfo.java deleted file mode 100644 index 0bc81484..00000000 --- a/vnt-jni/java/top/wherewego/vnt/jni/param/HandshakeInfo.java +++ /dev/null @@ -1,54 +0,0 @@ -package top.wherewego.vnt.jni.param; - -/** - * 握手回调信息 - * - * @author https://github.com/lbl8603/vnt - */ -public class HandshakeInfo { - /** - * 公钥 pem格式 CRLF分隔,不加密时为空 - */ - private final String publicKey; - /** - * 公钥签名,不加密时为空 - */ - private final String finger; - /** - * 服务端版本 - */ - private final String version; - - public HandshakeInfo() { - this.publicKey = "publicKey"; - this.finger = "finger"; - this.version = "version"; - } - - public HandshakeInfo(String publicKey, String finger, String version) { - this.publicKey = publicKey; - this.finger = finger; - this.version = version; - } - - public String getPublicKey() { - return publicKey; - } - - public String getFinger() { - return finger; - } - - public String getVersion() { - return version; - } - - @Override - public String toString() { - return "HandshakeInfo{" + - "publicKey='" + publicKey + '\'' + - ", finger='" + finger + '\'' + - ", version='" + version + '\'' + - '}'; - } -} diff --git a/vnt-jni/java/top/wherewego/vnt/jni/param/PeerClientInfo.java b/vnt-jni/java/top/wherewego/vnt/jni/param/PeerClientInfo.java deleted file mode 100644 index f19a7598..00000000 --- a/vnt-jni/java/top/wherewego/vnt/jni/param/PeerClientInfo.java +++ /dev/null @@ -1,60 +0,0 @@ -package top.wherewego.vnt.jni.param; - -import top.wherewego.vnt.jni.IpUtils; - -/** - * 创建网卡所需信息,仅在android上使用 - * - * @author https://github.com/lbl8603/vnt - */ -public class PeerClientInfo { - /** - * 虚拟IP - */ - public final int virtualIp; - /** - * 名称 - */ - public final String name; - /** - * 是否在线 - */ - public final boolean online; - /** - * 是否开启客户端加密,不同加密状态的不能通信 - */ - public final boolean clientSecret; - - public PeerClientInfo(int virtualIp, String name, boolean online, boolean clientSecret) { - this.virtualIp = virtualIp; - this.name = name; - this.online = online; - this.clientSecret = clientSecret; - } - - public int getVirtualIp() { - return virtualIp; - } - - public String getName() { - return name; - } - - public boolean isOnline() { - return online; - } - - public boolean isClientSecret() { - return clientSecret; - } - - @Override - public String toString() { - return "PeerDeviceInfo{" + - "virtualIp=" + IpUtils.intToIpAddress(virtualIp) + - ", name='" + name + '\'' + - ", online=" + online + - ", clientSecret=" + clientSecret + - '}'; - } -} diff --git a/vnt-jni/java/top/wherewego/vnt/jni/param/RegisterInfo.java b/vnt-jni/java/top/wherewego/vnt/jni/param/RegisterInfo.java deleted file mode 100644 index 3ff5cd04..00000000 --- a/vnt-jni/java/top/wherewego/vnt/jni/param/RegisterInfo.java +++ /dev/null @@ -1,50 +0,0 @@ -package top.wherewego.vnt.jni.param; - -import top.wherewego.vnt.jni.IpUtils; - -/** - * 注册回调信息 - * - * @author https://github.com/lbl8603/vnt - */ -public class RegisterInfo { - /** - * 虚拟IP - */ - public final int virtualIp; - /** - * 掩码 - */ - public final int virtualNetmask; - /** - * 网关 - */ - public final int virtualGateway; - - public RegisterInfo(int virtualIp, int virtualNetmask, int virtualGateway) { - this.virtualIp = virtualIp; - this.virtualNetmask = virtualNetmask; - this.virtualGateway = virtualGateway; - } - - public int getVirtualIp() { - return virtualIp; - } - - public int getVirtualNetmask() { - return virtualNetmask; - } - - public int getVirtualGateway() { - return virtualGateway; - } - - @Override - public String toString() { - return "RegisterInfo{" + - "virtualIp='" + IpUtils.intToIpAddress(virtualIp) + '\'' + - ", virtualNetmask='" + IpUtils.intToIpAddress(virtualNetmask) + '\'' + - ", virtualGateway='" + IpUtils.intToIpAddress(virtualGateway) + '\'' + - '}'; - } -} diff --git a/vnt-jni/src/callback.rs b/vnt-jni/src/callback.rs deleted file mode 100644 index ba723ba3..00000000 --- a/vnt-jni/src/callback.rs +++ /dev/null @@ -1,322 +0,0 @@ -use std::sync::Arc; - -use jni::objects::{GlobalRef, JClass, JObject, JString, JValue}; -use jni::{JNIEnv, JavaVM}; -use spki::der::pem::LineEnding; -use spki::EncodePublicKey; - -use vnt::handle::callback::ConnectInfo; -#[cfg(target_os = "android")] -use vnt::handle::callback::DeviceConfig; -#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] -use vnt::DeviceInfo; -use vnt::{ErrorInfo, HandshakeInfo, PeerClientInfo, RegisterInfo, VntCallback}; - -#[derive(Clone)] -pub struct CallBack { - jvm: Arc, - this: GlobalRef, - connect_info_class: GlobalRef, - handshake_info_class: GlobalRef, - error_info_class: GlobalRef, - register_info_class: GlobalRef, - #[cfg(target_os = "android")] - device_config_class: GlobalRef, - peer_client_info_class: GlobalRef, - #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] - device_info_class: GlobalRef, -} - -unsafe impl Send for CallBack {} - -fn find_class_global_ref(env: &mut JNIEnv, class: &str) -> jni::errors::Result { - let class = env.find_class(class)?; - env.new_global_ref(class) -} -impl CallBack { - pub fn new(jvm: JavaVM, this: GlobalRef) -> jni::errors::Result { - let mut env = jvm.attach_current_thread_as_daemon()?; - let connect_info_class = - find_class_global_ref(&mut env, "top/wherewego/vnt/jni/param/ConnectInfo")?; - let handshake_info_class = - find_class_global_ref(&mut env, "top/wherewego/vnt/jni/param/HandshakeInfo")?; - let error_info_class = - find_class_global_ref(&mut env, "top/wherewego/vnt/jni/param/ErrorInfo")?; - let register_info_class = - find_class_global_ref(&mut env, "top/wherewego/vnt/jni/param/RegisterInfo")?; - #[cfg(target_os = "android")] - let device_config_class = crate::callback::find_class_global_ref( - &mut env, - "top/wherewego/vnt/jni/param/DeviceConfig", - )?; - let peer_client_info_class = - find_class_global_ref(&mut env, "top/wherewego/vnt/jni/param/PeerClientInfo")?; - #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] - let device_info_class = - find_class_global_ref(&mut env, "top/wherewego/vnt/jni/param/DeviceInfo")?; - Ok(Self { - jvm: Arc::new(jvm), - this, - connect_info_class, - handshake_info_class, - error_info_class, - register_info_class, - #[cfg(target_os = "android")] - device_config_class, - peer_client_info_class, - #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] - device_info_class, - }) - } -} - -impl CallBack { - fn success0(&self) -> jni::errors::Result<()> { - let mut env = self.jvm.attach_current_thread_as_daemon()?; - env.call_method(&self.this, "success", "()V", &[])?; - Ok(()) - } - #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] - fn create_tun0(&self, info: DeviceInfo) -> jni::errors::Result<()> { - let mut env = self.jvm.attach_current_thread_as_daemon()?; - let class = unsafe { JClass::from_raw(self.device_info_class.as_raw()) }; - let param = env.new_object( - class, - "(Ljava/lang/String;Ljava/lang/String;)V", - &[ - JValue::Object(&env.new_string(info.name)?.into()), - JValue::Object(&env.new_string(info.version)?.into()), - ], - )?; - env.call_method( - &self.this, - "createTun", - "(Ltop/wherewego/vnt/jni/param/DeviceInfo;)V", - &[JValue::Object(¶m)], - )?; - Ok(()) - } - fn connect0(&self, info: ConnectInfo) -> jni::errors::Result<()> { - let mut env = self.jvm.attach_current_thread_as_daemon()?; - let class = unsafe { JClass::from_raw(self.connect_info_class.as_raw()) }; - let param = env.new_object( - class, - "(JLjava/lang/String;)V", - &[ - JValue::Long(info.count as _), - JValue::Object(&env.new_string(info.address.to_string())?.into()), - ], - )?; - env.call_method( - &self.this, - "connect", - "(Ltop/wherewego/vnt/jni/param/ConnectInfo;)V", - &[JValue::Object(¶m)], - )?; - Ok(()) - } - fn handshake0(&self, info: HandshakeInfo) -> jni::errors::Result { - let mut env = self.jvm.attach_current_thread_as_daemon()?; - let public_key = if let Some(public_key) = info.public_key { - match public_key.to_public_key_pem(LineEnding::CRLF) { - Ok(public_key) => env.new_string(public_key)?, - Err(e) => { - log::warn!("{:?}", e); - JString::default() - } - } - } else { - JString::default() - }; - let finger = if let Some(finger) = info.finger { - env.new_string(finger)? - } else { - JString::default() - }; - let class = unsafe { JClass::from_raw(self.handshake_info_class.as_raw()) }; - - let param = env.new_object( - class, - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", - &[ - JValue::Object(&public_key), - JValue::Object(&finger), - JValue::Object(&env.new_string(info.version)?.into()), - ], - )?; - let rs = env.call_method( - &self.this, - "handshake", - "(Ltop/wherewego/vnt/jni/param/HandshakeInfo;)Z", - &[JValue::Object(¶m)], - )?; - rs.z() - } - fn register0(&self, info: RegisterInfo) -> jni::errors::Result { - let mut env = self.jvm.attach_current_thread_as_daemon()?; - let class = unsafe { JClass::from_raw(self.register_info_class.as_raw()) }; - let param = env.new_object( - class, - "(III)V", - &[ - JValue::Int(u32::from(info.virtual_ip) as _), - JValue::Int(u32::from(info.virtual_netmask) as _), - JValue::Int(u32::from(info.virtual_gateway) as _), - ], - )?; - let rs = env.call_method( - &self.this, - "register", - "(Ltop/wherewego/vnt/jni/param/RegisterInfo;)Z", - &[JValue::Object(¶m)], - )?; - rs.z() - } - #[cfg(target_os = "android")] - fn generate_tun0(&self, info: DeviceConfig) -> jni::errors::Result { - let mut env = self.jvm.attach_current_thread_as_daemon()?; - let class = unsafe { JClass::from_raw(self.device_config_class.as_raw()) }; - - let object_array = env.new_object_array( - info.external_route.len() as _, - "java/lang/String", - JObject::null(), - )?; - for (index, (network, mask)) in info.external_route.into_iter().enumerate() { - let param = - env.new_string(format!("{}/{}", network, u32::from(mask).leading_ones()))?; - env.set_object_array_element(&object_array, index as _, ¶m)?; - } - let param = env.new_object( - class, - "(IIII[Ljava/lang/String;)V", - &[ - JValue::Int(u32::from(info.virtual_ip) as _), - JValue::Int(u32::from(info.virtual_netmask) as _), - JValue::Int(u32::from(info.virtual_gateway) as _), - JValue::Int(u32::from(info.virtual_network) as _), - JValue::Object(&object_array), - ], - )?; - let rs = env.call_method( - &self.this, - "generateTun", - "(Ltop/wherewego/vnt/jni/param/DeviceConfig;)I", - &[JValue::Object(¶m)], - )?; - rs.i().map(|v| v as _) - } - fn peer_client_list0(&self, info_vec: Vec) -> jni::errors::Result<()> { - let mut env = self.jvm.attach_current_thread_as_daemon()?; - let class = unsafe { JClass::from_raw(self.peer_client_info_class.as_raw()) }; - let object_array = env.new_object_array(info_vec.len() as _, &class, JObject::null())?; - for (index, info) in info_vec.into_iter().enumerate() { - let param = env.new_object( - &class, - "(ILjava/lang/String;ZZ)V", - &[ - JValue::Int(u32::from(info.virtual_ip) as _), - JValue::Object(&env.new_string(info.name)?.into()), - JValue::Bool(info.status.is_online() as _), - JValue::Bool(info.client_secret as _), - ], - )?; - env.set_object_array_element(&object_array, index as _, ¶m)?; - } - - env.call_method( - &self.this, - "peerClientList", - "([Ltop/wherewego/vnt/jni/param/PeerClientInfo;)V", - &[JValue::Object(&object_array)], - )?; - Ok(()) - } - - fn error0(&self, info: ErrorInfo) -> jni::errors::Result<()> { - let code: u8 = info.code.into(); - let mut env = self.jvm.attach_current_thread_as_daemon()?; - let class = unsafe { JClass::from_raw(self.error_info_class.as_raw()) }; - let msg = if let Some(msg) = info.msg { - env.new_string(msg)? - } else { - JString::default() - }; - let param = env.new_object( - class, - "(ILjava/lang/String;)V", - &[JValue::Int(code as _), JValue::Object(&msg.into())], - )?; - env.call_method( - &self.this, - "error", - "(Ltop/wherewego/vnt/jni/param/ErrorInfo;)V", - &[JValue::Object(¶m)], - )?; - Ok(()) - } - fn stop0(&self) -> jni::errors::Result<()> { - let mut env = self.jvm.attach_current_thread_as_daemon()?; - env.call_method(&self.this, "stop", "()V", &[])?; - Ok(()) - } -} - -impl VntCallback for CallBack { - fn success(&self) { - if let Err(e) = self.success0() { - log::warn!("success {:?}", e); - } - } - #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] - fn create_tun(&self, info: DeviceInfo) { - if let Err(e) = self.create_tun0(info) { - log::warn!("create_tun {:?}", e); - } - } - - fn connect(&self, info: ConnectInfo) { - if let Err(e) = self.connect0(info) { - log::warn!("connect {:?}", e); - } - } - - fn handshake(&self, info: HandshakeInfo) -> bool { - self.handshake0(info).unwrap_or_else(|e| { - log::warn!("handshake {:?}", e); - false - }) - } - - fn register(&self, info: RegisterInfo) -> bool { - self.register0(info).unwrap_or_else(|e| { - log::warn!("register {:?}", e); - false - }) - } - #[cfg(target_os = "android")] - fn generate_tun(&self, info: DeviceConfig) -> u32 { - self.generate_tun0(info).unwrap_or_else(|e| { - log::warn!("generate_tun {:?}", e); - 0 - }) - } - - fn peer_client_list(&self, info: Vec) { - if let Err(e) = self.peer_client_list0(info) { - log::warn!("peer_client_list {:?}", e); - } - } - - fn error(&self, info: ErrorInfo) { - if let Err(e) = self.error0(info) { - log::warn!("error {:?}", e); - } - } - - fn stop(&self) { - if let Err(e) = self.stop0() { - log::warn!("stop {:?}", e); - } - } -} diff --git a/vnt-jni/src/config.rs b/vnt-jni/src/config.rs deleted file mode 100644 index 20ccf4bb..00000000 --- a/vnt-jni/src/config.rs +++ /dev/null @@ -1,135 +0,0 @@ -use std::str::FromStr; - -use jni::errors::Error; -use jni::objects::JObject; -use jni::JNIEnv; - -use vnt::channel::punch::PunchModel; -use vnt::channel::UseChannelType; -use vnt::cipher::CipherModel; -use vnt::compression::Compressor; -use vnt::core::Config; - -use crate::utils::*; - -pub fn new_config(env: &mut JNIEnv, config: JObject) -> Result { - #[cfg(target_os = "windows")] - let tap = env.get_field(&config, "tap", "Z")?.z()?; - let token = to_string_not_null(env, &config, "token")?; - let name = to_string_not_null(env, &config, "name")?; - let device_id = to_string_not_null(env, &config, "deviceId")?; - let password = to_string(env, &config, "password")?; - let server_address_str = to_string_not_null(env, &config, "server")?; - let stun_server = to_string_array_not_null(env, &config, "stunServer")?; - let dns = to_string_array(env, &config, "dns")?.unwrap_or_else(|| vec![]); - let port_mapping = to_string_array(env, &config, "portMapping")?.unwrap_or_else(|| vec![]); - let cipher_model = to_string_not_null(env, &config, "cipherModel")?; - let punch_model = to_string(env, &config, "punchModel")?; - let mtu = to_integer(env, &config, "mtu")?.map(|v| v as u32); - let tcp = env.get_field(&config, "tcp", "Z")?.z()?; - let server_encrypt = env.get_field(&config, "serverEncrypt", "Z")?.z()?; - let use_channel = to_string(env, &config, "useChannel")?; - let finger = env.get_field(&config, "finger", "Z")?.z()?; - let first_latency = env.get_field(&config, "firstLatency", "Z")?.z()?; - let packet_delay = to_integer(env, &config, "packetDelay")? - .map(|v| v as u32) - .unwrap_or_default(); - let packet_loss_rate = to_double(env, &config, "packetLossRate")?; - - let in_ips = to_string_array(env, &config, "inIps")?; - let out_ips = to_string_array(env, &config, "outIps")?; - let ports = - to_i32_array(env, &config, "ports")?.map(|v| v.into_iter().map(|v| v as u16).collect()); - let ip = if let Some(ip) = to_string(env, &config, "ip")? { - match ip.parse() { - Ok(ip) => Some(ip), - Err(e) => { - env.throw_new( - "java/lang/RuntimeException", - format!("ip {} err: {}", ip, e), - ) - .expect("throw"); - return Err(Error::JavaException); - } - } - } else { - None - }; - let in_ips = if let Some(in_ips) = in_ips { - match common::args_parse::ips_parse(&in_ips) { - Ok(in_ips) => in_ips, - Err(e) => { - env.throw_new("java/lang/RuntimeException", format!("in_ips {}", e)) - .expect("throw"); - return Err(Error::JavaException); - } - } - } else { - vec![] - }; - let out_ips = if let Some(out_ips) = out_ips { - match common::args_parse::out_ips_parse(&out_ips) { - Ok(out_ips) => out_ips, - Err(e) => { - env.throw_new("java/lang/RuntimeException", format!("out_ips {}", e)) - .expect("throw"); - return Err(Error::JavaException); - } - } - } else { - vec![] - }; - - let cipher_model = match CipherModel::from_str(&cipher_model) { - Ok(cipher_model) => cipher_model, - Err(e) => { - env.throw_new("java/lang/RuntimeException", format!("cipher_model {}", e)) - .expect("throw"); - return Err(Error::JavaException); - } - }; - #[cfg(not(target_os = "android"))] - let device_name = to_string(env, &config, "deviceName")?; - let config = match Config::new( - #[cfg(target_os = "windows")] - tap, - token, - device_id, - name, - server_address_str, - dns, - stun_server, - in_ips, - out_ips, - password, - mtu, - tcp, - ip, - false, - server_encrypt, - 1, - cipher_model, - finger, - PunchModel::from_str(&punch_model.unwrap_or_default()).unwrap_or_default(), - ports, - first_latency, - #[cfg(not(target_os = "android"))] - device_name, - UseChannelType::from_str(&use_channel.unwrap_or_default()).unwrap_or_default(), - packet_loss_rate, - packet_delay, - port_mapping, - Compressor::None, - ) { - Ok(config) => config, - Err(e) => { - env.throw_new( - "java/lang/RuntimeException", - format!("vnt start error {:?}", e), - ) - .expect("throw"); - return Err(Error::JavaException); - } - }; - Ok(config) -} diff --git a/vnt-jni/src/lib.rs b/vnt-jni/src/lib.rs deleted file mode 100644 index 7ec5f133..00000000 --- a/vnt-jni/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod callback; -pub mod config; -pub mod utils; -pub mod vnt; -pub(crate) mod vnt_logger; diff --git a/vnt-jni/src/utils.rs b/vnt-jni/src/utils.rs deleted file mode 100644 index 46a5a1d7..00000000 --- a/vnt-jni/src/utils.rs +++ /dev/null @@ -1,131 +0,0 @@ -use jni::errors::Error; -use jni::objects::{JIntArray, JObject, JObjectArray, JString}; -use jni::JNIEnv; - -pub fn to_string_not_null( - env: &mut JNIEnv, - config: &JObject, - name: &'static str, -) -> Result { - let value = env.get_field(config, name, "Ljava/lang/String;")?.l()?; - if value.is_null() { - env.throw_new("java/lang/NullPointerException", name) - .expect("throw"); - return Err(Error::NullPtr(name)); - } - let binding = JString::from(value); - let value = env.get_string(binding.as_ref())?; - match value.to_str() { - Ok(value) => Ok(value.to_string()), - Err(_) => { - env.throw_new("java/lang/RuntimeException", "not utf-8") - .expect("throw"); - return Err(Error::JavaException); - } - } -} - -pub fn to_string(env: &mut JNIEnv, config: &JObject, name: &str) -> Result, Error> { - let value = env.get_field(config, name, "Ljava/lang/String;")?.l()?; - if value.is_null() { - return Ok(None); - } - let tmp = JString::from(value); - let value = env.get_string(tmp.as_ref())?; - match value.to_str() { - Ok(value) => Ok(Some(value.to_string())), - Err(_) => { - env.throw_new("java/lang/RuntimeException", "not utf-8") - .expect("throw"); - return Err(Error::JavaException); - } - } -} - -pub fn to_string_array_not_null( - env: &mut JNIEnv, - config: &JObject, - name: &str, -) -> Result, Error> { - match to_string_array(env, config, name)? { - None => { - env.throw_new("java/lang/NullPointerException", name) - .expect("throw"); - return Err(Error::JavaException); - } - Some(rs) => Ok(rs), - } -} - -pub fn to_string_array( - env: &mut JNIEnv, - config: &JObject, - name: &str, -) -> Result>, Error> { - let value = env.get_field(config, name, "[Ljava/lang/String;")?.l()?; - if value.is_null() { - return Ok(None); - } - let arr = JObjectArray::from(value); - let len = env.get_array_length(&arr)?; - let mut rs = Vec::with_capacity(len as usize); - for index in 0..len { - let object = env.get_object_array_element(&arr, index)?; - if object.is_null() { - env.throw_new( - "java/lang/NullPointerException", - format!("{},index={}", name, index), - ) - .expect("throw"); - return Err(Error::JavaException); - } - match env.get_string(JString::from(object).as_ref())?.to_str() { - Ok(value) => { - rs.push(value.to_string()); - } - Err(_) => { - env.throw_new("java/lang/RuntimeException", "not utf-8") - .expect("throw"); - return Err(Error::JavaException); - } - } - } - Ok(Some(rs)) -} - -pub fn to_i32_array( - env: &mut JNIEnv, - config: &JObject, - name: &str, -) -> Result>, Error> { - let obj = env.get_field(&config, name, "[I")?.l()?; - if obj.is_null() { - Ok(None) - } else { - let j_arr = JIntArray::from(obj); - let len = env.get_array_length(&j_arr)?; - let mut arr = vec![0i32; len as usize]; - env.get_int_array_region(j_arr, 0, &mut arr)?; - Ok(Some(arr)) - } -} -pub fn to_integer(env: &mut JNIEnv, config: &JObject, name: &str) -> Result, Error> { - let value = env.get_field(config, name, "Ljava/lang/Integer;")?.l()?; - if value.is_null() { - return Ok(None); - } - // 调用 intValue - return Ok(Some( - env.call_method(value, "intValue", "()I", &[])?.i()? as _ - )); -} -pub fn to_double(env: &mut JNIEnv, config: &JObject, name: &str) -> Result, Error> { - let value = env.get_field(config, name, "Ljava/lang/Double;")?.l()?; - if value.is_null() { - return Ok(None); - } - // 调用 intValue - return Ok(Some( - env.call_method(value, "doubleValue", "()D", &[])?.d()? as _, - )); -} diff --git a/vnt-jni/src/vnt.rs b/vnt-jni/src/vnt.rs deleted file mode 100644 index 6156b080..00000000 --- a/vnt-jni/src/vnt.rs +++ /dev/null @@ -1,184 +0,0 @@ -use std::ptr; -use std::time::Duration; - -use jni::errors::Error; -use jni::objects::{JClass, JObject, JValue}; -use jni::sys::{jboolean, jint, jlong, jobject, jobjectArray, jsize}; -use jni::JNIEnv; - -use vnt::channel::Route; -use vnt::core::Vnt; -use vnt::handle::PeerDeviceInfo; - -use crate::callback::CallBack; -#[no_mangle] -pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_Vnt_new0( - mut env: JNIEnv<'static>, - _class: JClass, - config: JObject, - call_back: JObject<'static>, -) -> jlong { - crate::vnt_logger::init_log(); - let jvm = if let Ok(jvm) = env.get_java_vm() { - jvm - } else { - return 0; - }; - match crate::config::new_config(&mut env, config) { - Ok(config) => { - let call_back = if let Ok(call_back) = env.new_global_ref(call_back) { - call_back - } else { - return 0; - }; - let call_back = match CallBack::new(jvm, call_back) { - Ok(call_back) => call_back, - Err(_) => { - return 0; - } - }; - let vnt_util = match Vnt::new(config, call_back) { - Ok(vnt_util) => vnt_util, - Err(e) => { - env.throw_new( - "java/lang/RuntimeException", - format!("vnt start error {}", e), - ) - .expect("throw"); - return 0; - } - }; - let ptr = Box::into_raw(Box::new(vnt_util)); - return ptr as jlong; - } - Err(_) => {} - } - return 0; -} - -#[no_mangle] -pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_Vnt_stop0( - _env: JNIEnv, - _class: JClass, - raw_vnt: jlong, -) { - let vnt = raw_vnt as *mut Vnt; - let _ = (&*vnt).stop(); -} - -#[no_mangle] -pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_Vnt_wait0( - _env: JNIEnv, - _class: JClass, - raw_vnt: jlong, -) { - let vnt = raw_vnt as *mut Vnt; - let _ = (&*vnt).wait(); -} -#[no_mangle] -pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_Vnt_waitTimeout0( - _env: JNIEnv, - _class: JClass, - raw_vnt: jlong, - time: jlong, -) -> jboolean { - let vnt = raw_vnt as *mut Vnt; - (&*vnt).wait_timeout(Duration::from_millis(time as _)) as _ -} - -#[no_mangle] -pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_Vnt_drop0( - _env: JNIEnv, - _class: JClass, - raw_vnt: jlong, -) { - let vnt = raw_vnt as *mut Vnt; - let _ = Box::from_raw(vnt).stop(); -} - -#[no_mangle] -pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_Vnt_list0( - mut env: JNIEnv, - _class: JClass, - raw_vnt: jlong, -) -> jobjectArray { - let vnt = raw_vnt as *mut Vnt; - let vnt = &mut *vnt; - let list = vnt.device_list(); - - let arr = match env.new_object_array( - list.len() as jsize, - "top/wherewego/vnt/jni/PeerRouteInfo", - JObject::null(), - ) { - Ok(arr) => arr, - Err(e) => { - env.throw_new("java/lang/RuntimeException", format!("error:{:?}", e)) - .expect("throw"); - return ptr::null_mut(); - } - }; - for (index, peer) in list.into_iter().enumerate() { - let route = if let Some(route) = vnt.route(&peer.virtual_ip) { - match route_parse(&mut env, route) { - Ok(route) => JObject::from_raw(route), - Err(_) => JObject::null(), - } - } else { - JObject::null() - }; - match peer_device_info_parse(&mut env, peer, route) { - Ok(peer) => { - match env.set_object_array_element(&arr, index as jsize, JObject::from_raw(peer)) { - Ok(_) => {} - Err(e) => { - env.throw_new("java/lang/RuntimeException", format!("error:{:?}", e)) - .expect("throw"); - return ptr::null_mut(); - } - } - } - Err(e) => { - env.throw_new("java/lang/RuntimeException", format!("error:{:?}", e)) - .expect("throw"); - return ptr::null_mut(); - } - } - } - arr.as_raw() -} - -fn route_parse(env: &mut JNIEnv, route: Route) -> Result { - let rs = env.new_object( - "top/wherewego/vnt/jni/Route", - "(ZLjava/lang/String;BI)V", - &[ - JValue::Bool(route.is_tcp as _), - JValue::Object(&env.new_string(route.addr.to_string())?.into()), - JValue::Byte(route.metric as _), - JValue::Int(route.rt as _), - ], - )?; - Ok(rs.as_raw()) -} - -fn peer_device_info_parse( - env: &mut JNIEnv, - peer: PeerDeviceInfo, - route: JObject, -) -> Result { - let virtual_ip = u32::from(peer.virtual_ip); - let name = peer.name.to_string(); - let status = format!("{:?}", peer.status); - let rs = env.new_object( - "top/wherewego/vnt/jni/PeerRouteInfo", - "(ILjava/lang/String;Ljava/lang/String;Ltop/wherewego/vnt/jni/Route;)V", - &[ - JValue::Int(virtual_ip as jint), - JValue::Object(&env.new_string(name)?.into()), - JValue::Object(&env.new_string(status)?.into()), - JValue::Object(&route), - ], - )?; - Ok(rs.as_raw()) -} diff --git a/vnt-jni/src/vnt_logger.rs b/vnt-jni/src/vnt_logger.rs deleted file mode 100644 index 56b64eb7..00000000 --- a/vnt-jni/src/vnt_logger.rs +++ /dev/null @@ -1,12 +0,0 @@ -#[cfg(target_os = "android")] -pub fn init_log() { - use android_logger::Config; - use log::LevelFilter; - android_logger::init_once( - Config::default() - .with_max_level(LevelFilter::Info) // limit log level - .with_tag("vnt_jni"), // logs will show under mytag tag - ); -} -#[cfg(not(target_os = "android"))] -pub fn init_log() {} diff --git a/vnt/Cargo.toml b/vnt/Cargo.toml index 3a1ee645..dd95ec4c 100644 --- a/vnt/Cargo.toml +++ b/vnt/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "vnt" -version = "1.2.10" +version = "1.2.11" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tun = { path = "tun" } +tun = { path = "tun", optional = true } packet = { path = "./packet" } bytes = "1.5.0" log = "0.4.17" @@ -17,7 +17,7 @@ parking_lot = "0.12.1" rand = "0.8.5" sha2 = { version = "0.10.6", features = ["oid"] } thiserror = "1.0.37" -protobuf = "3.2.0" +protobuf = "=3.2.0" socket2 = { version = "0.5.2", features = ["all"] } aes-gcm = { version = "0.10.2", optional = true } ring = { version = "0.17.0", optional = true } @@ -32,27 +32,32 @@ spki = { version = "0.7.2", features = ["fingerprint", "alloc", "base64"], optio openssl-sys = { git = "https://github.com/lbl8603/rust-openssl", optional = true } libsm = { git = "https://github.com/lbl8603/libsm", optional = true } -mio = { version = "0.8.10", features = ["os-poll", "net", "os-ext"] } +mio = { version = "=0.8.11", features = ["os-poll", "net", "os-ext"] } crossbeam-queue = "0.3.11" anyhow = "1.0.82" dns-parser = "0.8.0" -tokio = { version = "1.37.0", features = ["full"], optional = true } +tokio = { version = "1.37.0", features = ["full"] } lz4_flex = { version = "0.11", default-features = false, optional = true } zstd = { version = "0.13.1", optional = true } +fnv = "1.0.7" +igd = { version = "0.12.1", optional = true } +tokio-tungstenite = { version = "0.23.1", optional = true } +rustls = { version = "0.23.0", features = ["ring"], default-features = false, optional = true } +futures-util = "0.3.30" [target.'cfg(target_os = "windows")'.dependencies] libloading = "0.8.0" [build-dependencies] -protobuf-codegen = "3.2.0" +protobuf-codegen = "=3.2.0" protoc-bin-vendored = "3.0.0" cfg_aliases = "0.2.1" [features] -default = ["server_encrypt", "aes_gcm", "aes_cbc", "aes_ecb", "sm4_cbc", "chacha20_poly1305", "ip_proxy", "port_mapping", "lz4_compress", "zstd_compress"] +default = ["server_encrypt", "aes_gcm", "aes_cbc", "aes_ecb", "sm4_cbc", "chacha20_poly1305", "ip_proxy", "port_mapping", "lz4_compress", "zstd_compress", "integrated_tun"] openssl = ["openssl-sys"] # 从源码编译 openssl-vendored = ["openssl-sys/vendored"] @@ -63,7 +68,11 @@ sm4_cbc = ["libsm"] aes_gcm = ["aes-gcm"] chacha20_poly1305 = ["chacha20poly1305", "chacha20"] server_encrypt = ["aes-gcm", "rsa", "spki"] -ip_proxy = ["tokio"] -port_mapping = ["tokio"] +ip_proxy = [] +port_mapping = [] lz4_compress = ["lz4_flex"] zstd_compress = ["zstd"] +integrated_tun = ["tun"] +upnp = ["igd"] +ws = ["tokio-tungstenite"] +wss = ["ws", "tokio-tungstenite/rustls-tls-native-roots", "tokio-tungstenite/rustls-tls-webpki-roots", "rustls"] \ No newline at end of file diff --git a/vnt/src/channel/context.rs b/vnt/src/channel/context.rs index 47de7da1..a78c999a 100644 --- a/vnt/src/channel/context.rs +++ b/vnt/src/channel/context.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use fnv::FnvHashMap; use std::net::{Ipv4Addr, SocketAddr, SocketAddrV6, UdpSocket}; use std::ops::Deref; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -11,8 +11,10 @@ use parking_lot::RwLock; use rand::Rng; use crate::channel::punch::NatType; -use crate::channel::sender::{AcceptSocketSender, ChannelSender, PacketSender}; -use crate::channel::{Route, RouteKey, UseChannelType, DEFAULT_RT}; +use crate::channel::sender::{AcceptSocketSender, PacketSender}; +use crate::channel::{ConnectProtocol, Route, RouteKey, UseChannelType, DEFAULT_RT}; +use crate::protocol::NetPacket; +use crate::util::limit::TrafficMeterMultiAddress; /// 传输通道上下文,持有udp socket、tcp socket和路由信息 #[derive(Clone)] @@ -25,10 +27,12 @@ impl ChannelContext { main_udp_socket: Vec, use_channel_type: UseChannelType, first_latency: bool, - is_tcp: bool, + protocol: ConnectProtocol, packet_loss_rate: Option, packet_delay: u32, use_ipv6: bool, + up_traffic_meter: Option, + down_traffic_meter: Option, ) -> Self { let channel_num = main_udp_socket.len(); assert_ne!(channel_num, 0, "not channel"); @@ -44,22 +48,21 @@ impl ChannelContext { .unwrap_or(0); let inner = ContextInner { main_udp_socket, - sub_udp_socket: RwLock::new(Vec::with_capacity(64)), - tcp_map: RwLock::new(HashMap::with_capacity(64)), + sub_udp_socket: RwLock::new(Vec::new()), + packet_map: RwLock::new(FnvHashMap::default()), route_table: RouteTable::new(use_channel_type, first_latency, channel_num), - is_tcp, + protocol, packet_loss_rate, packet_delay, main_index: AtomicUsize::new(0), use_ipv6, + up_traffic_meter, + down_traffic_meter, }; Self { inner: Arc::new(inner), } } - pub fn sender(&self) -> ChannelSender { - ChannelSender::new(self.clone()) - } } impl Deref for ChannelContext { @@ -71,7 +74,7 @@ impl Deref for ChannelContext { } /// 对称网络增加的udp socket数目,有助于增加打洞成功率 -pub const SYMMETRIC_CHANNEL_NUM: usize = 100; +pub const SYMMETRIC_CHANNEL_NUM: usize = 84; const PACKET_LOSS_RATE_DENOMINATOR: u32 = 100_0000; pub struct ContextInner { @@ -80,17 +83,19 @@ pub struct ContextInner { // 对称网络增加的udp socket sub_udp_socket: RwLock>, // tcp数据发送器 - pub(crate) tcp_map: RwLock>, + pub(crate) packet_map: RwLock>, // 路由信息 pub route_table: RouteTable, - // 是否使用tcp连接服务器 - is_tcp: bool, + // 使用什么协议连接服务器 + protocol: ConnectProtocol, //控制丢包率,取值v=[0,100_0000] 丢包率r=v/100_0000 packet_loss_rate: u32, //控制延迟 packet_delay: u32, main_index: AtomicUsize, use_ipv6: bool, + pub(crate) up_traffic_meter: Option, + pub(crate) down_traffic_meter: Option, } impl ContextInner { @@ -101,11 +106,11 @@ impl ContextInner { pub fn is_cone(&self) -> bool { self.sub_udp_socket.read().is_empty() } - pub fn is_main_tcp(&self) -> bool { - self.is_tcp + pub fn main_protocol(&self) -> ConnectProtocol { + self.protocol } pub fn is_udp_main(&self, route_key: &RouteKey) -> bool { - !route_key.is_tcp() && route_key.index < self.main_udp_socket.len() + route_key.protocol().is_udp() && route_key.index < self.main_udp_socket.len() } pub fn first_latency(&self) -> bool { self.route_table.first_latency @@ -160,7 +165,7 @@ impl ContextInner { Ok(ports) } pub fn send_tcp(&self, buf: &[u8], addr: SocketAddr) -> io::Result<()> { - if let Some(tcp) = self.tcp_map.read().get(&addr) { + if let Some(tcp) = self.packet_map.read().get(&addr) { tcp.try_send(buf) } else { Err(io::Error::from(io::ErrorKind::NotFound)) @@ -182,13 +187,20 @@ impl ContextInner { Ok(()) } /// 将数据发送到默认通道,一般发往服务器才用此方法 - pub fn send_default(&self, buf: &[u8], addr: SocketAddr) -> io::Result<()> { - if self.is_tcp { - //服务端地址只在重连时检测变化 - self.send_tcp(buf, addr) + pub fn send_default>( + &self, + buf: &NetPacket, + addr: SocketAddr, + ) -> io::Result<()> { + if self.protocol.is_udp() { + self.send_main_udp(self.main_index.load(Ordering::Relaxed), buf.buffer(), addr)? } else { - self.send_main_udp(self.main_index.load(Ordering::Relaxed), buf, addr) + self.send_tcp(buf.buffer(), addr)? + } + if let Some(up_traffic_meter) = &self.up_traffic_meter { + up_traffic_meter.add_traffic(buf.destination(), buf.data_len()); } + Ok(()) } pub fn change_main_index(&self) { @@ -202,7 +214,7 @@ impl ContextInner { if let Err(e) = udp.send_to(buf, addr) { log::warn!("{:?},add={:?}", e, addr); } - thread::sleep(Duration::from_millis(1)); + thread::sleep(Duration::from_millis(3)); } } pub fn try_send_all_main(&self, buf: &[u8], addr: SocketAddr) { @@ -213,9 +225,9 @@ impl ContextInner { } } /// 发送网络数据 - pub fn send_ipv4_by_id( + pub fn send_ipv4_by_id>( &self, - buf: &[u8], + buf: &NetPacket, id: &Ipv4Addr, server_addr: SocketAddr, send_default: bool, @@ -225,6 +237,7 @@ impl ContextInner { return Ok(()); } } + if self.packet_delay > 0 { thread::sleep(Duration::from_millis(self.packet_delay as _)); } @@ -241,7 +254,7 @@ impl ContextInner { Ok(()) } /// 将数据发到指定id - pub fn send_by_id(&self, buf: &[u8], id: &Ipv4Addr) -> io::Result<()> { + pub fn send_by_id>(&self, buf: &NetPacket, id: &Ipv4Addr) -> io::Result<()> { let mut c = 0; loop { let route = self.route_table.get_route_by_id(c, id)?; @@ -261,25 +274,35 @@ impl ContextInner { } } /// 将数据发到指定路由 - pub fn send_by_key(&self, buf: &[u8], route_key: RouteKey) -> io::Result<()> { - if route_key.is_tcp { - self.send_tcp(buf, route_key.addr) - } else { - if let Some(main_udp) = self.main_udp_socket.get(route_key.index) { - main_udp.send_to(buf, route_key.addr)?; - } else { - if let Some(udp) = self - .sub_udp_socket - .read() - .get(route_key.index - self.main_udp_socket.len()) - { - udp.send_to(buf, route_key.addr)?; + pub fn send_by_key>( + &self, + buf: &NetPacket, + route_key: RouteKey, + ) -> io::Result<()> { + match route_key.protocol() { + ConnectProtocol::UDP => { + if let Some(main_udp) = self.main_udp_socket.get(route_key.index) { + main_udp.send_to(buf.buffer(), route_key.addr)?; } else { - Err(io::Error::from(io::ErrorKind::NotFound))? + if let Some(udp) = self + .sub_udp_socket + .read() + .get(route_key.index - self.main_udp_socket.len()) + { + udp.send_to(buf.buffer(), route_key.addr)?; + } else { + Err(io::Error::from(io::ErrorKind::NotFound))? + } } } - Ok(()) + ConnectProtocol::TCP | ConnectProtocol::WS | ConnectProtocol::WSS => { + self.send_tcp(buf.buffer(), route_key.addr)? + } + } + if let Some(up_traffic_meter) = &self.up_traffic_meter { + up_traffic_meter.add_traffic(buf.destination(), buf.data_len()); } + Ok(()) } pub fn remove_route(&self, ip: &Ipv4Addr, route_key: RouteKey) { self.route_table.remove_route(ip, route_key) @@ -288,7 +311,7 @@ impl ContextInner { pub struct RouteTable { pub(crate) route_table: - RwLock)>)>>, + RwLock)>)>>, first_latency: bool, channel_num: usize, use_channel_type: UseChannelType, @@ -297,7 +320,7 @@ pub struct RouteTable { impl RouteTable { fn new(use_channel_type: UseChannelType, first_latency: bool, channel_num: usize) -> Self { Self { - route_table: RwLock::new(HashMap::with_capacity(64)), + route_table: RwLock::new(FnvHashMap::with_capacity_and_hasher(64, Default::default())), use_channel_type, first_latency, channel_num, diff --git a/vnt/src/channel/handler.rs b/vnt/src/channel/handler.rs index 4715fb92..7c6abe71 100644 --- a/vnt/src/channel/handler.rs +++ b/vnt/src/channel/handler.rs @@ -1,9 +1,9 @@ use crate::channel::context::ChannelContext; use crate::channel::RouteKey; -pub trait RecvChannelHandler: Clone + Send + 'static { +pub trait RecvChannelHandler: Clone + Send + Sync + 'static { fn handle( - &mut self, + &self, buf: &mut [u8], extend: &mut [u8], route_key: RouteKey, diff --git a/vnt/src/channel/idle.rs b/vnt/src/channel/idle.rs index ecb877f3..c145819a 100644 --- a/vnt/src/channel/idle.rs +++ b/vnt/src/channel/idle.rs @@ -39,7 +39,7 @@ impl Idle { } } } - let sleep_time = self.read_idle - max; + let sleep_time = self.read_idle.checked_sub(max).unwrap_or_default(); return IdleType::Sleep(sleep_time); } } diff --git a/vnt/src/channel/mod.rs b/vnt/src/channel/mod.rs index 7536edb2..639ab4cf 100644 --- a/vnt/src/channel/mod.rs +++ b/vnt/src/channel/mod.rs @@ -1,12 +1,16 @@ use anyhow::Context; use std::net::{SocketAddr, UdpSocket}; use std::str::FromStr; +use tokio::sync::mpsc::channel; use crate::channel::context::ChannelContext; use crate::channel::handler::RecvChannelHandler; -use crate::channel::sender::AcceptSocketSender; +use crate::channel::sender::{AcceptSocketSender, ConnectUtil}; use crate::channel::tcp_channel::tcp_listen; use crate::channel::udp_channel::udp_listen; +#[cfg(feature = "ws")] +use crate::channel::ws_channel::ws_connect_accept; +use crate::util::limit::TrafficMeterMultiAddress; use crate::util::StopManager; pub mod context; @@ -17,14 +21,21 @@ pub mod punch; pub mod sender; pub mod tcp_channel; pub mod udp_channel; +#[cfg(feature = "ws")] +pub mod ws_channel; + +pub const BUFFER_SIZE: usize = 1024 * 64; +// 这里留个坑,tcp是支持_TCP_MAX_PACKET_SIZE长度的, +// 但是缓存只用BUFFER_SIZE,会导致多余的数据接收不了 +const TCP_MAX_PACKET_SIZE: usize = (1 << 24) - 1; -pub const BUFFER_SIZE: usize = 1024 * 16; #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum UseChannelType { Relay, P2p, All, } + impl UseChannelType { pub fn is_only_relay(&self) -> bool { self == &UseChannelType::Relay @@ -36,6 +47,7 @@ impl UseChannelType { self == &UseChannelType::All } } + impl FromStr for UseChannelType { type Err = String; @@ -48,15 +60,49 @@ impl FromStr for UseChannelType { } } } + impl Default for UseChannelType { fn default() -> Self { UseChannelType::All } } +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub enum ConnectProtocol { + UDP, + TCP, + WS, + WSS, +} + +impl ConnectProtocol { + #[inline] + pub fn is_tcp(&self) -> bool { + self == &ConnectProtocol::TCP + } + #[inline] + pub fn is_udp(&self) -> bool { + self == &ConnectProtocol::UDP + } + #[inline] + pub fn is_ws(&self) -> bool { + self == &ConnectProtocol::WS + } + #[inline] + pub fn is_wss(&self) -> bool { + self == &ConnectProtocol::WSS + } + pub fn is_transport(&self) -> bool { + self.is_tcp() || self.is_udp() + } + pub fn is_base_tcp(&self) -> bool { + self.is_tcp() || self.is_ws() || self.is_wss() + } +} + #[derive(Copy, Clone, Debug)] pub struct Route { - pub is_tcp: bool, + pub protocol: ConnectProtocol, index: usize, pub addr: SocketAddr, pub metric: u8, @@ -68,11 +114,19 @@ pub struct RouteSortKey { pub metric: u8, pub rt: i64, } + const DEFAULT_RT: i64 = 9999; + impl Route { - pub fn new(is_tcp: bool, index: usize, addr: SocketAddr, metric: u8, rt: i64) -> Self { + pub fn new( + protocol: ConnectProtocol, + index: usize, + addr: SocketAddr, + metric: u8, + rt: i64, + ) -> Self { Self { - is_tcp, + protocol, index, addr, metric, @@ -81,7 +135,7 @@ impl Route { } pub fn from(route_key: RouteKey, metric: u8, rt: i64) -> Self { Self { - is_tcp: route_key.is_tcp, + protocol: route_key.protocol, index: route_key.index, addr: route_key.addr, metric, @@ -90,7 +144,7 @@ impl Route { } pub fn from_default_rt(route_key: RouteKey, metric: u8) -> Self { Self { - is_tcp: route_key.is_tcp, + protocol: route_key.protocol, index: route_key.index, addr: route_key.addr, metric, @@ -99,7 +153,7 @@ impl Route { } pub fn route_key(&self) -> RouteKey { RouteKey { - is_tcp: self.is_tcp, + protocol: self.protocol, index: self.index, addr: self.addr, } @@ -117,35 +171,39 @@ impl Route { #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] pub struct RouteKey { - is_tcp: bool, + protocol: ConnectProtocol, index: usize, pub addr: SocketAddr, } impl RouteKey { - pub(crate) fn new(is_tcp: bool, index: usize, addr: SocketAddr) -> Self { + pub(crate) fn new(protocol: ConnectProtocol, index: usize, addr: SocketAddr) -> Self { Self { - is_tcp, + protocol, index, addr, } } - pub fn is_tcp(&self) -> bool { - self.is_tcp + #[inline] + pub fn protocol(&self) -> ConnectProtocol { + self.protocol } + #[inline] pub fn index(&self) -> usize { self.index } } -pub fn init_context( +pub(crate) fn init_context( ports: Vec, use_channel_type: UseChannelType, first_latency: bool, - is_tcp: bool, + protocol: ConnectProtocol, packet_loss_rate: Option, packet_delay: u32, -) -> anyhow::Result<(ChannelContext, mio::net::TcpListener)> { + up_traffic_meter: Option, + down_traffic_meter: Option, +) -> anyhow::Result<(ChannelContext, std::net::TcpListener)> { assert!(!ports.is_empty(), "not channel"); let mut udps = Vec::with_capacity(ports.len()); //检查系统是否支持ipv6 @@ -172,11 +230,8 @@ pub fn init_context( address, ) }; - if let Err(e) = socket.set_send_buffer_size(2 * 1024 * 1024) { - log::warn!("set_send_buffer_size {:?}", e); - } if let Err(e) = socket.set_recv_buffer_size(2 * 1024 * 1024) { - log::warn!("set_send_buffer_size {:?}", e); + log::warn!("set_recv_buffer_size {:?}", e); } socket .bind(&address.into()) @@ -188,10 +243,12 @@ pub fn init_context( udps, use_channel_type, first_latency, - is_tcp, + protocol, packet_loss_rate, packet_delay, use_ipv6, + up_traffic_meter, + down_traffic_meter, ); let port = context.main_local_udp_port()?[0]; @@ -229,32 +286,37 @@ pub fn init_context( socket.listen(128)?; socket.set_nonblocking(true)?; socket.set_nodelay(false)?; - let tcp_listener = mio::net::TcpListener::from_std(socket.into()); - Ok((context, tcp_listener)) + Ok((context, socket.into())) } -pub fn init_channel( - tcp_listener: mio::net::TcpListener, +pub(crate) fn init_channel( + tcp_listener: std::net::TcpListener, context: ChannelContext, stop_manager: StopManager, recv_handler: H, ) -> anyhow::Result<( AcceptSocketSender>>, - AcceptSocketSender<(mio::net::TcpStream, SocketAddr, Option>)>, + ConnectUtil, )> where H: RecvChannelHandler, { + let (tcp_connect_s, tcp_connect_r) = channel(16); + let (ws_connect_s, _ws_connect_r) = channel(16); + let connect_util = ConnectUtil::new(tcp_connect_s, ws_connect_s); // udp监听,udp_socket_sender 用于NAT类型切换 let udp_socket_sender = udp_listen(stop_manager.clone(), recv_handler.clone(), context.clone())?; // 建立tcp监听,tcp_socket_sender 用于tcp 直连 - let tcp_socket_sender = tcp_listen( + tcp_listen( tcp_listener, - stop_manager.clone(), + tcp_connect_r, recv_handler.clone(), context.clone(), + stop_manager.clone(), )?; + #[cfg(feature = "ws")] + ws_connect_accept(_ws_connect_r, recv_handler, context.clone(), stop_manager)?; - Ok((udp_socket_sender, tcp_socket_sender)) + Ok((udp_socket_sender, connect_util)) } diff --git a/vnt/src/channel/punch.rs b/vnt/src/channel/punch.rs index 6c424a56..5b2a760a 100644 --- a/vnt/src/channel/punch.rs +++ b/vnt/src/channel/punch.rs @@ -1,16 +1,19 @@ +use crossbeam_utils::atomic::AtomicCell; use std::collections::HashMap; use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; +use std::ops::{Div, Mul}; use std::str::FromStr; +use std::sync::Arc; use std::time::Duration; use std::{io, thread}; -use mio::net::TcpStream; use rand::prelude::SliceRandom; use rand::Rng; use crate::channel::context::ChannelContext; -use crate::channel::sender::AcceptSocketSender; +use crate::channel::sender::ConnectUtil; use crate::external_route::ExternalRoute; +use crate::handle::CurrentDeviceInfo; use crate::nat::NatTest; #[derive(Copy, Clone, Eq, PartialEq, Debug)] @@ -47,7 +50,7 @@ pub struct NatInfo { pub nat_type: NatType, pub(crate) local_ipv4: Option, pub(crate) ipv6: Option, - pub(crate) udp_ports: Vec, + pub udp_ports: Vec, pub tcp_port: u16, } @@ -99,26 +102,25 @@ impl NatInfo { nat_type, } } - pub fn update_addr(&mut self, index: usize, ip: Ipv4Addr, port: u16) { + pub fn update_addr(&mut self, index: usize, ip: Ipv4Addr, port: u16) -> bool { + let mut updated = false; if port != 0 { if let Some(public_port) = self.public_ports.get_mut(index) { if *public_port != port { + updated = true; log::info!("端口变化={}:{} index={}", ip, port, index) } *public_port = port; } } - if !ip.is_multicast() - && !ip.is_broadcast() - && !ip.is_unspecified() - && !ip.is_loopback() - && !ip.is_private() - { + if crate::nat::is_ipv4_global(&ip) { if !self.public_ips.contains(&ip) { self.public_ips.push(ip); + updated = true; log::info!("ip变化={},{:?}", ip, self.public_ips) } } + updated } pub fn local_ipv4(&self) -> Option { self.local_ipv4 @@ -186,9 +188,10 @@ pub struct Punch { port_index: HashMap, punch_model: PunchModel, is_tcp: bool, - tcp_socket_sender: AcceptSocketSender<(TcpStream, SocketAddr, Option>)>, + connect_util: ConnectUtil, external_route: ExternalRoute, nat_test: NatTest, + current_device: Arc>, } impl Punch { @@ -196,9 +199,10 @@ impl Punch { context: ChannelContext, punch_model: PunchModel, is_tcp: bool, - tcp_socket_sender: AcceptSocketSender<(TcpStream, SocketAddr, Option>)>, + connect_util: ConnectUtil, external_route: ExternalRoute, nat_test: NatTest, + current_device: Arc>, ) -> Self { let mut port_vec: Vec = (1..65535).collect(); port_vec.push(65535); @@ -210,34 +214,20 @@ impl Punch { port_index: HashMap::new(), punch_model, is_tcp, - tcp_socket_sender, + connect_util, external_route, nat_test, + current_device, } } } impl Punch { - fn connect_tcp(&self, buf: &[u8], addr: SocketAddr) -> bool { + fn connect_tcp(&self, buf: &[u8], addr: SocketAddr) { if self.nat_test.is_local_address(true, addr) { - return false; + return; } - // mio是非阻塞的,不能立马判断是否能连接成功,所以用标准库的tcp - match std::net::TcpStream::connect_timeout(&addr, Duration::from_millis(100)) { - Ok(tcp_stream) => { - if tcp_stream.set_nonblocking(true).is_err() { - return false; - } - return self - .tcp_socket_sender - .try_add_socket((TcpStream::from_std(tcp_stream), addr, Some(buf.to_vec()))) - .is_ok(); - } - Err(e) => { - log::warn!("连接到tcp失败,addr={},err={}", addr, e); - } - } - false + self.connect_util.try_connect_tcp(buf.to_vec(), addr); } pub fn punch( &mut self, @@ -245,19 +235,21 @@ impl Punch { id: Ipv4Addr, mut nat_info: NatInfo, punch_tcp: bool, + count: usize, ) -> io::Result<()> { if self.context.route_table.no_need_punch(&id) { log::info!("已打洞成功,无需打洞:{:?}", id); return Ok(()); } - nat_info - .public_ips - .retain(|ip| self.external_route.route(&ip).is_none()); - nat_info - .local_ipv4 - .filter(|ip| self.external_route.route(&ip).is_none()); + let device_info = self.current_device.load(); + nat_info.public_ips.retain(|ip| { + self.external_route.route(ip).is_none() && device_info.not_in_network(*ip) + }); + nat_info.local_ipv4.filter(|ip| { + self.external_route.route(ip).is_none() && device_info.not_in_network(*ip) + }); nat_info.ipv6.filter(|ip| { - if let Some(ip) = ip.to_ipv4_mapped() { + if let Some(ip) = ip.to_ipv4() { self.external_route.route(&ip).is_none() } else { true @@ -266,22 +258,16 @@ impl Punch { if punch_tcp && self.is_tcp && nat_info.tcp_port != 0 { //向tcp发起连接 if let Some(ipv6_addr) = nat_info.local_tcp_ipv6addr() { - if self.connect_tcp(buf, ipv6_addr) { - // return Ok(()); - } + self.connect_tcp(buf, ipv6_addr) } //向tcp发起连接 if let Some(ipv4_addr) = nat_info.local_tcp_ipv4addr() { - if self.connect_tcp(buf, ipv4_addr) { - // return Ok(()); - } + self.connect_tcp(buf, ipv4_addr) } if nat_info.nat_type == NatType::Cone && nat_info.public_ips.len() == 1 { let addr = SocketAddr::V4(SocketAddrV4::new(nat_info.public_ips[0], nat_info.tcp_port)); - if self.connect_tcp(buf, addr) { - // return Ok(()); - } + self.connect_tcp(buf, addr) } } let channel_num = self.context.channel_num(); @@ -316,7 +302,11 @@ impl Punch { //预测范围内最多发送max_k1个包 let max_k1 = 60; //全局最多发送max_k2个包 - let max_k2 = rand::thread_rng().gen_range(600..800); + let mut max_k2: usize = rand::thread_rng().gen_range(600..800); + if count > 2 { + //递减探测规模 + max_k2 = max_k2.mul(2).div(count).max(max_k1 as usize); + } let port = nat_info.public_ports.get(0).map(|e| *e).unwrap_or(0); if nat_info.public_port_range < max_k1 * 3 { //端口变化不大时,在预测的范围内随机发送 @@ -332,8 +322,7 @@ impl Punch { } else { (max_port - min_port + 1) as usize }; - let mut nums: Vec = (min_port..max_port).collect(); - nums.push(max_port); + let mut nums: Vec = (min_port..=max_port).collect(); nums.shuffle(&mut rand::thread_rng()); self.punch_symmetric(&nums[..k], buf, &nat_info.public_ips, max_k1 as usize)?; } @@ -397,7 +386,7 @@ impl Punch { } let addr = SocketAddr::V4(SocketAddrV4::new(*pub_ip, *port)); self.context.send_main_udp(0, buf, addr)?; - thread::sleep(Duration::from_millis(2)); + thread::sleep(Duration::from_millis(3)); } } Ok(ports.len()) diff --git a/vnt/src/channel/sender.rs b/vnt/src/channel/sender.rs index bfc6d923..08e9252c 100644 --- a/vnt/src/channel/sender.rs +++ b/vnt/src/channel/sender.rs @@ -1,31 +1,110 @@ use std::io; -use std::ops::Deref; +use std::net::{Ipv4Addr, SocketAddr}; use std::sync::mpsc::{SyncSender, TrySendError}; use std::sync::Arc; -use mio::Token; +use crossbeam_utils::atomic::AtomicCell; +use tokio::sync::mpsc::Sender; use crate::channel::context::ChannelContext; -use crate::channel::notify::{AcceptNotify, WritableNotify}; +use crate::channel::notify::AcceptNotify; +use crate::cipher::Cipher; +use crate::compression::Compressor; +use crate::external_route::ExternalRoute; +use crate::handle::CurrentDeviceInfo; +use crate::protocol; +use crate::protocol::{ip_turn_packet, NetPacket}; #[derive(Clone)] -pub struct ChannelSender { +pub struct IpPacketSender { context: ChannelContext, + current_device: Arc>, + compressor: Compressor, + client_cipher: Cipher, + ip_route: ExternalRoute, } -impl ChannelSender { - pub fn new(context: ChannelContext) -> Self { - Self { context } +impl IpPacketSender { + pub fn new( + context: ChannelContext, + current_device: Arc>, + compressor: Compressor, + client_cipher: Cipher, + ip_route: ExternalRoute, + ) -> Self { + Self { + context, + current_device, + compressor, + client_cipher, + ip_route, + } } -} + pub fn self_virtual_ip(&self) -> Ipv4Addr { + self.current_device.load().virtual_ip + } + pub fn send_ip( + &self, + buf: &mut [u8], + data_len: usize, + auxiliary_buf: &mut [u8], + mut dest_ip: Ipv4Addr, + ) -> anyhow::Result<()> { + let device_info = self.current_device.load(); + let src_ip = device_info.virtual_ip; + if src_ip.is_unspecified() { + return Ok(()); + } + if let Some(v) = self.ip_route.route(&dest_ip) { + dest_ip = v; + } + if dest_ip.is_multicast() || dest_ip.is_broadcast() || dest_ip == device_info.broadcast_ip { + //广播 + dest_ip = Ipv4Addr::BROADCAST; + } -impl Deref for ChannelSender { - type Target = ChannelContext; + let mut net_packet = NetPacket::new0(data_len, buf)?; + let mut auxiliary = NetPacket::new(auxiliary_buf)?; + net_packet.set_default_version(); + net_packet.set_protocol(protocol::Protocol::IpTurn); + net_packet.set_transport_protocol(ip_turn_packet::Protocol::Ipv4.into()); + net_packet.first_set_ttl(6); + net_packet.set_source(src_ip); + net_packet.set_destination(dest_ip); - fn deref(&self) -> &Self::Target { - &self.context + let mut net_packet = if self.compressor.compress(&net_packet, &mut auxiliary)? { + auxiliary.set_default_version(); + auxiliary.set_protocol(protocol::Protocol::IpTurn); + auxiliary.set_transport_protocol(ip_turn_packet::Protocol::Ipv4.into()); + auxiliary.first_set_ttl(6); + auxiliary.set_source(src_ip); + auxiliary.set_destination(dest_ip); + auxiliary + } else { + net_packet + }; + self.client_cipher.encrypt_ipv4(&mut net_packet)?; + if dest_ip.is_broadcast() { + //走服务端广播 + self.context + .send_default(&net_packet, device_info.connect_server)?; + return Ok(()); + } + + if device_info.not_in_network(dest_ip) { + //不是一个网段的直接忽略 + return Ok(()); + } + self.context.send_ipv4_by_id( + &net_packet, + &dest_ip, + device_info.connect_server, + device_info.status.online(), + )?; + Ok(()) } } + pub struct AcceptSocketSender { sender: SyncSender, notify: AcceptNotify, @@ -39,6 +118,7 @@ impl Clone for AcceptSocketSender { } } } + impl AcceptSocketSender { pub fn new(notify: AcceptNotify, sender: SyncSender) -> Self { Self { sender, notify } @@ -53,53 +133,54 @@ impl AcceptSocketSender { } } } - #[derive(Clone)] pub struct PacketSender { - inner: Arc, + sender: Sender>, } impl PacketSender { - pub fn new(notify: WritableNotify, buffer: SyncSender>, token: Token) -> Self { - Self { - inner: Arc::new(PacketSenderInner { - token, - notify, - buffer, - }), - } + pub fn new(sender: Sender>) -> Self { + Self { sender } } - #[inline] pub fn try_send(&self, buf: &[u8]) -> io::Result<()> { - self.inner.try_send(buf) - } - pub fn shutdown(&self) -> io::Result<()> { - self.inner.shutdown() + match self.sender.try_send(buf.to_vec()) { + Ok(_) => Ok(()), + Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => Err(io::Error::new( + io::ErrorKind::WouldBlock, + "通道已满,发生丢包", + )), + Err(_) => Err(io::Error::new( + io::ErrorKind::ConnectionRefused, + "通道关闭,发生丢包", + )), + } } } -pub struct PacketSenderInner { - token: Token, - notify: WritableNotify, - buffer: SyncSender>, +#[derive(Clone)] +pub struct ConnectUtil { + connect_tcp: Sender<(Vec, SocketAddr)>, + connect_ws: Sender<(Vec, String)>, } -impl PacketSenderInner { - #[inline] - fn try_send(&self, buf: &[u8]) -> io::Result<()> { - let len = buf.len(); - let mut buf_vec = Vec::with_capacity(buf.len() + 4); - buf_vec.extend_from_slice(&[0, 0, (len >> 8) as u8, (len & 0xFF) as u8]); - buf_vec.extend_from_slice(buf); - match self.buffer.try_send(buf_vec) { - Ok(_) => self.notify.notify(self.token, true), - Err(e) => match e { - TrySendError::Disconnected(_) => Err(io::Error::from(io::ErrorKind::WriteZero)), - TrySendError::Full(_) => Err(io::Error::from(io::ErrorKind::WouldBlock)), - }, +impl ConnectUtil { + pub fn new( + connect_tcp: Sender<(Vec, SocketAddr)>, + connect_ws: Sender<(Vec, String)>, + ) -> Self { + Self { + connect_tcp, + connect_ws, } } - fn shutdown(&self) -> io::Result<()> { - self.notify.notify(self.token, false) + pub fn try_connect_tcp(&self, buf: Vec, addr: SocketAddr) { + if self.connect_tcp.try_send((buf, addr)).is_err() { + log::warn!("try_connect_tcp failed {}", addr); + } + } + pub fn try_connect_ws(&self, buf: Vec, addr: String) { + if self.connect_ws.try_send((buf, addr)).is_err() { + log::warn!("try_connect_ws failed"); + } } } diff --git a/vnt/src/channel/tcp_channel.rs b/vnt/src/channel/tcp_channel.rs index c5a0219b..97debc95 100644 --- a/vnt/src/channel/tcp_channel.rs +++ b/vnt/src/channel/tcp_channel.rs @@ -1,462 +1,186 @@ -use std::collections::HashMap; -use std::io::{Read, Write}; -use std::net::{Shutdown, SocketAddr}; -#[cfg(any(unix))] -use std::os::fd::FromRawFd; -#[cfg(any(unix))] -use std::os::fd::IntoRawFd; -#[cfg(windows)] -use std::os::windows::io::FromRawSocket; -#[cfg(windows)] -use std::os::windows::io::IntoRawSocket; -use std::sync::mpsc::{sync_channel, Receiver, SyncSender, TryRecvError, TrySendError}; -use std::{io, thread}; - -use mio::net::{TcpListener, TcpStream}; -use mio::{Events, Interest, Poll, Registry, Token, Waker}; +use anyhow::{anyhow, Context}; +use std::net::SocketAddr; +use std::thread; +use std::time::Duration; +use tokio::io::{AsyncReadExt, AsyncWrite, AsyncWriteExt}; +use tokio::net::tcp::OwnedReadHalf; +use tokio::net::{TcpListener, TcpStream}; +use tokio::sync::mpsc::{channel, Receiver}; use crate::channel::context::ChannelContext; use crate::channel::handler::RecvChannelHandler; -use crate::channel::notify::{AcceptNotify, WritableNotify}; -use crate::channel::sender::{AcceptSocketSender, PacketSender}; -use crate::channel::{RouteKey, BUFFER_SIZE}; +use crate::channel::sender::PacketSender; +use crate::channel::{ConnectProtocol, RouteKey, BUFFER_SIZE, TCP_MAX_PACKET_SIZE}; use crate::util::StopManager; -const SERVER: Token = Token(0); -const NOTIFY: Token = Token(1); - /// 监听tcp端口,等待客户端连接 pub fn tcp_listen( - tcp_server: TcpListener, - stop_manager: StopManager, + tcp_server: std::net::TcpListener, + receiver: Receiver<(Vec, SocketAddr)>, recv_handler: H, context: ChannelContext, -) -> anyhow::Result>)>> + stop_manager: StopManager, +) -> anyhow::Result<()> where H: RecvChannelHandler, { - let (tcp_sender, tcp_receiver) = sync_channel(64); - let poll = Poll::new()?; - let waker = AcceptNotify::new(Waker::new(poll.registry(), NOTIFY)?); - let accept = AcceptSocketSender::new(waker.clone(), tcp_sender); - let worker = { - let waker = waker.clone(); - stop_manager.add_listener("tcp_listen".into(), move || { - if let Err(e) = waker.stop() { - log::error!("{:?}", e); - } - })? - }; - + let (stop_sender, stop_receiver) = tokio::sync::oneshot::channel::<()>(); + let worker = stop_manager.add_listener("tcpChannel".into(), move || { + let _ = stop_sender.send(()); + })?; + let runtime = tokio::runtime::Builder::new_multi_thread() + .worker_threads(2) + .enable_all() + .build() + .context("tcp tokio runtime build failed")?; thread::Builder::new() - .name("tcpRead".into()) + .name("tcpChannel".into()) .spawn(move || { - if let Err(e) = tcp_listen0( - poll, - tcp_server, - &stop_manager, - waker, - tcp_receiver, - recv_handler, - context, - ) { - log::error!("{:?}", e); - } + runtime.spawn(async move { + { + let recv_handler = recv_handler.clone(); + let context = context.clone(); + tokio::spawn(async move { + if let Err(e) = tcp_accept(tcp_server, recv_handler, context).await { + log::warn!("tcp_listen {:?}", e); + } + }); + } + tokio::spawn( + async move { connect_tcp_handle(receiver, recv_handler, context).await }, + ); + }); + runtime.block_on(async { + let _ = stop_receiver.await; + }); + runtime.shutdown_background(); worker.stop_all(); - })?; - Ok(accept) + }) + .context("tcp thread build failed")?; + Ok(()) } -fn tcp_listen0( - mut poll: Poll, - mut tcp_server: TcpListener, - stop_manager: &StopManager, - accept_notify: AcceptNotify, - accept_tcp_receiver: Receiver<(TcpStream, SocketAddr, Option>)>, - mut recv_handler: H, +async fn connect_tcp_handle( + mut receiver: Receiver<(Vec, SocketAddr)>, + recv_handler: H, context: ChannelContext, -) -> anyhow::Result<()> -where +) where H: RecvChannelHandler, { - let (tcp_sender, tcp_receiver) = sync_channel(64); - let write_waker = init_writable_handler(tcp_receiver, stop_manager.clone(), context.clone())?; - poll.registry() - .register(&mut tcp_server, SERVER, Interest::READABLE)?; - let mut events = Events::with_capacity(1024); - - let mut read_map: HashMap, usize)> = - HashMap::with_capacity(32); - let mut extend = [0; BUFFER_SIZE]; - loop { - poll.poll(&mut events, None)?; - for event in events.iter() { - match event.token() { - SERVER => loop { - match tcp_server.accept() { - Ok((stream, addr)) => { - accept_handle( - stream, - addr, - None, - &write_waker, - &mut read_map, - &tcp_sender, - poll.registry(), - )?; - } - Err(e) => { - if e.kind() == io::ErrorKind::WouldBlock { - break; - } - return Err(e)?; - } - } - }, - NOTIFY => { - if accept_notify.is_stop() { - return Ok(()); - } - if accept_notify.is_add_socket() { - while let Ok((stream, addr, init_buf)) = accept_tcp_receiver.try_recv() { - accept_handle( - stream, - addr, - init_buf, - &write_waker, - &mut read_map, - &tcp_sender, - poll.registry(), - )?; - } - } - } - token => { - if event.is_readable() { - if let Err(e) = readable_handle( - &token, - &mut read_map, - &mut recv_handler, - &context, - &mut extend, - ) { - closed_handle_r(&token, &mut read_map); - log::warn!("{:?}", e); - if let Err(e) = write_waker.notify(token, false) { - log::warn!("{:?}", e); - } - } - } else { - closed_handle_r(&token, &mut read_map); - if let Err(e) = write_waker.notify(token, false) { - log::warn!("{:?}", e); - } - } - } + while let Some((data, addr)) = receiver.recv().await { + let recv_handler = recv_handler.clone(); + let context = context.clone(); + tokio::spawn(async move { + if let Err(e) = connect_tcp0(data, addr, recv_handler, context).await { + log::warn!("发送失败,链接终止:{:?},{:?}", addr, e); } - } + }); } } -/// 处理写事件 - -fn init_writable_handler( - receiver: Receiver<(TcpStream, Token, SocketAddr, Option>)>, - stop_manager: StopManager, +async fn connect_tcp0( + data: Vec, + addr: SocketAddr, + recv_handler: H, context: ChannelContext, -) -> anyhow::Result { - let poll = Poll::new()?; - let writable_notify = WritableNotify::new(Waker::new(poll.registry(), NOTIFY)?); - let worker = { - let writable_notify = writable_notify.clone(); - stop_manager.add_listener("tcp_writable_handler".into(), move || { - if let Err(e) = writable_notify.stop() { - log::error!("{:?}", e); - } - })? - }; - { - let writable_notify = writable_notify.clone(); - thread::Builder::new() - .name("tcpWriteableListen".into()) - .spawn(move || { - if let Err(e) = tcp_writable_listen(receiver, poll, writable_notify, &context) { - log::error!("{:?}", e); - } - worker.stop_all(); - })?; - } +) -> anyhow::Result<()> +where + H: RecvChannelHandler, +{ + let mut stream = + tokio::time::timeout(Duration::from_secs(3), TcpStream::connect(addr)).await??; + tcp_write(&mut stream, &data).await?; - Ok(writable_notify) + tcp_stream_handle(stream, addr, recv_handler, context).await; + Ok(()) } -/// 处理写事件 -fn tcp_writable_listen( - receiver: Receiver<(TcpStream, Token, SocketAddr, Option>)>, - mut poll: Poll, - writable_notify: WritableNotify, - context: &ChannelContext, -) -> io::Result<()> { - let mut events = Events::with_capacity(1024); - let mut write_map: HashMap< - Token, - ( - TcpStream, - SocketAddr, - Receiver>, - Option<(Vec, usize)>, - ), - > = HashMap::with_capacity(32); - loop { - poll.poll(&mut events, None)?; - for event in events.iter() { - match event.token() { - NOTIFY => { - if writable_notify.is_stop() { - //服务停止 - return Ok(()); - } - if writable_notify.is_need_write() { - // 需要写入数据 - if let Some(tokens) = writable_notify.take_all() { - for (token, state) in tokens { - if !state { - closed_handle_w(&token, &mut write_map, &context); - continue; - } - if let Err(e) = writable_handle(&token, &mut write_map) { - closed_handle_w(&token, &mut write_map, &context); - log::warn!("{:?}", e); - } - } - } - } - if writable_notify.is_add_socket() { - //添加tcp连接,并监听写事件 - while let Ok((mut stream, token, addr, init_buf)) = receiver.try_recv() { - if let Err(e) = stream.set_nodelay(true) { - log::warn!("set_nodelay err={:?}", e); - } - if let Err(e) = - poll.registry() - .register(&mut stream, token, Interest::WRITABLE) - { - log::warn!("registry err={:?}", e); - continue; - } - let (sender, receiver) = sync_channel(128); - let packet_sender = - PacketSender::new(writable_notify.clone(), sender, token); - if let Some(init_buf) = init_buf { - packet_sender.try_send(&init_buf)?; - } +async fn tcp_accept( + tcp_server: std::net::TcpListener, + recv_handler: H, + context: ChannelContext, +) -> anyhow::Result<()> +where + H: RecvChannelHandler, +{ + let tcp_server = TcpListener::from_std(tcp_server)?; - context.tcp_map.write().insert(addr, packet_sender); - write_map.insert(token, (stream, addr, receiver, None)); - } - } - } - token => { - if event.is_writable() { - if let Err(e) = writable_handle(&token, &mut write_map) { - closed_handle_w(&token, &mut write_map, &context); - log::warn!("{:?}", e); - } - } else { - closed_handle_w(&token, &mut write_map, &context); - } - } - } - } + loop { + let (stream, addr) = tcp_server.accept().await?; + tcp_stream_handle(stream, addr, recv_handler.clone(), context.clone()).await; } } -fn accept_handle( +pub async fn tcp_stream_handle( stream: TcpStream, addr: SocketAddr, - init_buf: Option>, - write_waker: &WritableNotify, - read_map: &mut HashMap, usize)>, - tcp_sender: &SyncSender<(TcpStream, Token, SocketAddr, Option>)>, - registry: &Registry, -) -> io::Result<()> { - #[cfg(windows)] - let (tcp_stream, index) = unsafe { - let fd = stream.into_raw_socket(); - (std::net::TcpStream::from_raw_socket(fd), fd as usize) - }; - #[cfg(any(unix))] - let (tcp_stream, index) = unsafe { - let fd = stream.into_raw_fd(); - (std::net::TcpStream::from_raw_fd(fd), fd as usize) - }; - if index == 0 || index == 1 { - log::error!("index err={:?}", addr); - return Ok(()); - } - let token = Token(index); - match tcp_stream.try_clone() { - Ok(tcp_writer) => { - match tcp_sender.try_send((TcpStream::from_std(tcp_writer), token, addr, init_buf)) { - Ok(_) => { - if let Err(e) = write_waker.add_socket() { - log::error!("write_waker,err={:?},addr={:?}", e, addr); - return Ok(()); - } - } - Err(e) => { - return match e { - TrySendError::Full(_) => { - log::error!("Full,addr={:?}", addr); - Ok(()) - } - TrySendError::Disconnected(_) => { - Err(io::Error::new(io::ErrorKind::Other, "write thread exit")) - } - }; - } + recv_handler: H, + context: ChannelContext, +) where + H: RecvChannelHandler, +{ + let _ = stream.set_nodelay(true); + let (r, mut w) = stream.into_split(); + let (sender, mut receiver) = channel::>(100); + context + .packet_map + .write() + .insert(addr, PacketSender::new(sender)); + tokio::spawn(async move { + while let Some(data) = receiver.recv().await { + if let Err(e) = tcp_write(&mut w, &data).await { + log::info!("发送失败,tcp链接终止:{:?},{:?}", addr, e); + break; } } - Err(e) => { - log::error!("try_clone err={:?},addr={:?}", e, addr); - return Ok(()); + let _ = w.shutdown().await; + }); + tokio::spawn(async move { + if let Err(e) = tcp_read(r, addr, &context, recv_handler).await { + log::warn!("tcp_read {:?}", e) } + context.packet_map.write().remove(&addr); + }); +} + +async fn tcp_write(w: &mut W, buf: &[u8]) -> anyhow::Result<()> { + let len = buf.len(); + if len > TCP_MAX_PACKET_SIZE { + return Err(anyhow!("超过了tcp的最大长度传输")); } - let mut stream = TcpStream::from_std(tcp_stream); - if let Err(e) = registry.register(&mut stream, token, Interest::READABLE) { - log::error!("registry err={:?},addr={:?}", e, addr); - return Ok(()); - } - read_map.insert( - token, - ( - RouteKey::new(true, index, addr), - stream, - Box::new([0; BUFFER_SIZE]), - 0, - ), - ); + w.write_all(&[0, (len >> 16) as u8, (len >> 8) as u8, len as u8]) + .await?; + w.write_all(&buf).await?; Ok(()) } -fn readable_handle( - token: &Token, - map: &mut HashMap, usize)>, - recv_handler: &mut H, +async fn tcp_read( + mut read: OwnedReadHalf, + addr: SocketAddr, context: &ChannelContext, - extend: &mut [u8], -) -> io::Result<()> + recv_handler: H, +) -> anyhow::Result<()> where H: RecvChannelHandler, { - if let Some((route_key, stream, buf, begin)) = map.get_mut(token) { - loop { - let end = if *begin >= 4 { - 4 + (((buf[2] as u16) << 8) | buf[3] as u16) as usize - } else { - 4 - }; - if end > BUFFER_SIZE { - return Err(io::Error::from(io::ErrorKind::InvalidData)); - } - match stream.read(&mut buf[*begin..end]) { - Ok(len) => { - if len == 0 { - return Err(io::Error::from(io::ErrorKind::UnexpectedEof)); - } - *begin += len; - if end > 4 && *begin == end { - recv_handler.handle(&mut buf[4..end], extend, *route_key, context); - *begin = 0; - } - } - Err(e) => { - if e.kind() == io::ErrorKind::WouldBlock { - break; - } - return Err(e); - } - } + let mut head = [0; 4]; + let mut buf = [0; BUFFER_SIZE]; + let mut extend = [0; BUFFER_SIZE]; + loop { + read.read_exact(&mut head).await?; + if head[0] != 0 { + return Err(anyhow!("tcp数据流错误 {}", addr)); } - } - Ok(()) -} - -fn writable_handle( - token: &Token, - map: &mut HashMap< - Token, - ( - TcpStream, - SocketAddr, - Receiver>, - Option<(Vec, usize)>, - ), - >, -) -> io::Result<()> { - if let Some((stream, _, receiver, last)) = map.get_mut(token) { - loop { - if let Some((buf, begin)) = last { - match stream.write(&buf[*begin..]) { - Ok(len) => { - if len == 0 { - return Err(io::Error::from(io::ErrorKind::WriteZero)); - } - if len + *begin == buf.len() { - *last = None; - } else { - *begin += len; - continue; - } - } - Err(e) => { - if e.kind() == io::ErrorKind::WouldBlock { - break; - } - return Err(e); - } - } - } - match receiver.try_recv() { - Ok(buf) => *last = Some((buf, 0)), - Err(e) => match e { - TryRecvError::Empty => { - break; - } - TryRecvError::Disconnected => { - return Err(io::Error::from(io::ErrorKind::Other)); - } - }, - } + let len = ((head[1] as usize) << 16) | ((head[2] as usize) << 8) | head[3] as usize; + if len < 12 || len > buf.len() { + return Err(anyhow!("tcp数据长度无效 {}", addr)); } - } - Ok(()) -} - -fn closed_handle_r( - token: &Token, - map: &mut HashMap, usize)>, -) { - if let Some((_, tcp, _, _)) = map.remove(token) { - let _ = tcp.shutdown(Shutdown::Both); - } -} - -fn closed_handle_w( - token: &Token, - map: &mut HashMap< - Token, - ( - TcpStream, - SocketAddr, - Receiver>, - Option<(Vec, usize)>, - ), - >, - context: &ChannelContext, -) { - if let Some((tcp, addr, _, _)) = map.remove(token) { - context.tcp_map.write().remove(&addr); - let _ = tcp.shutdown(Shutdown::Both); + read.read_exact(&mut buf[..len]).await?; + recv_handler.handle( + &mut buf[..len], + &mut extend, + RouteKey::new(ConnectProtocol::TCP, 0, addr), + context, + ); } } diff --git a/vnt/src/channel/udp_channel.rs b/vnt/src/channel/udp_channel.rs index 4a0a26a5..a895e6f5 100644 --- a/vnt/src/channel/udp_channel.rs +++ b/vnt/src/channel/udp_channel.rs @@ -10,7 +10,7 @@ use crate::channel::context::ChannelContext; use crate::channel::handler::RecvChannelHandler; use crate::channel::notify::AcceptNotify; use crate::channel::sender::AcceptSocketSender; -use crate::channel::{RouteKey, BUFFER_SIZE}; +use crate::channel::{ConnectProtocol, RouteKey, BUFFER_SIZE}; use crate::util::StopManager; pub fn udp_listen( @@ -60,7 +60,7 @@ where fn sub_udp_listen0( mut poll: Poll, - mut recv_handler: H, + recv_handler: H, context: ChannelContext, accept_notify: AcceptNotify, accept_receiver: Receiver>>, @@ -73,7 +73,10 @@ where let mut extend = [0; BUFFER_SIZE]; let mut read_map: HashMap = HashMap::with_capacity(32); loop { - poll.poll(&mut events, None)?; + if let Err(e) = poll.poll(&mut events, None) { + crate::ignore_io_interrupted(e)?; + continue; + } for event in events.iter() { match event.token() { NOTIFY => { @@ -117,7 +120,7 @@ where recv_handler.handle( &mut buf[..len], &mut extend, - RouteKey::new(false, token.0, addr), + RouteKey::new(ConnectProtocol::UDP, token.0, addr), &context, ); } @@ -235,7 +238,7 @@ where pub fn main_udp_listen0( mut poll: Poll, - mut recv_handler: H, + recv_handler: H, context: ChannelContext, ) -> io::Result<()> where @@ -256,7 +259,10 @@ where let mut events = Events::with_capacity(udps.len()); let mut extend = [0; BUFFER_SIZE]; loop { - poll.poll(&mut events, None)?; + if let Err(e) = poll.poll(&mut events, None) { + crate::ignore_io_interrupted(e)?; + continue; + } for x in events.iter() { let index = match x.token() { NOTIFY => return Ok(()), @@ -274,7 +280,7 @@ where recv_handler.handle( &mut buf[..len], &mut extend, - RouteKey::new(false, index, addr), + RouteKey::new(ConnectProtocol::UDP, index, addr), &context, ); } diff --git a/vnt/src/channel/ws_channel.rs b/vnt/src/channel/ws_channel.rs new file mode 100644 index 00000000..f7862628 --- /dev/null +++ b/vnt/src/channel/ws_channel.rs @@ -0,0 +1,159 @@ +use crate::channel::{ConnectProtocol, RouteKey, BUFFER_SIZE}; +use anyhow::Context; +use futures_util::stream::SplitStream; +use futures_util::{SinkExt, StreamExt}; +use std::convert::Into; +use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; +use std::thread; +use std::time::Duration; +use tokio::net::TcpStream; +use tokio::sync::mpsc::{channel, Receiver}; +use tokio_tungstenite::tungstenite::http::StatusCode; +use tokio_tungstenite::tungstenite::{Error, Message}; +use tokio_tungstenite::{connect_async, MaybeTlsStream, WebSocketStream}; + +use crate::channel::context::ChannelContext; +use crate::channel::handler::RecvChannelHandler; +use crate::channel::sender::PacketSender; +use crate::util::StopManager; + +/// ws协议, +/// 暂时只允许用ws连服务端,不能用ws打洞/连客户端 +pub fn ws_connect_accept( + receiver: Receiver<(Vec, String)>, + recv_handler: H, + context: ChannelContext, + stop_manager: StopManager, +) -> anyhow::Result<()> +where + H: RecvChannelHandler, +{ + let (stop_sender, stop_receiver) = tokio::sync::oneshot::channel::<()>(); + let worker = stop_manager.add_listener("wsChannel".into(), move || { + let _ = stop_sender.send(()); + })?; + let runtime = tokio::runtime::Builder::new_multi_thread() + .worker_threads(2) + .enable_all() + .build() + .context("ws tokio runtime build failed")?; + thread::Builder::new() + .name("wsChannel".into()) + .spawn(move || { + runtime.spawn(async move { connect_ws_handle(receiver, recv_handler, context).await }); + runtime.block_on(async { + let _ = stop_receiver.await; + }); + runtime.shutdown_background(); + worker.stop_all(); + }) + .context("ws thread build failed")?; + Ok(()) +} + +async fn connect_ws_handle( + mut receiver: Receiver<(Vec, String)>, + recv_handler: H, + context: ChannelContext, +) where + H: RecvChannelHandler, +{ + while let Some((data, url)) = receiver.recv().await { + let recv_handler = recv_handler.clone(); + let context = context.clone(); + tokio::spawn(async move { + if let Err(e) = connect_ws(data, url, recv_handler, context).await { + log::warn!("发送失败,ws链接终止:{:?}", e); + } + }); + } +} +const WS_ADDR: SocketAddr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)); + +async fn connect_ws( + data: Vec, + mut url: String, + recv_handler: H, + context: ChannelContext, +) -> anyhow::Result<()> +where + H: RecvChannelHandler, +{ + let mut count = 0; + log::info!("尝试建立连接 {:?}", url); + let (mut ws, response) = loop { + count += 1; + if count > 3 { + Err(anyhow::anyhow!("发生多次重定向,链接终止"))? + } + match tokio::time::timeout(Duration::from_secs(3), connect_async(url)).await? { + Ok(rs) => break rs, + Err(e) => { + if let Error::Http(res) = &e { + if res.status() == StatusCode::MOVED_PERMANENTLY + || res.status() == StatusCode::FOUND + || res.status() == StatusCode::SEE_OTHER + || res.status() == StatusCode::TEMPORARY_REDIRECT + || res.status() == StatusCode::PERMANENT_REDIRECT + { + if let Some(v) = res.headers().get("Location") { + if let Ok(redirect) = v.to_str() { + log::info!("url重定向响应头 {:?}", res.headers()); + log::info!("url重定向地址 {}", redirect); + url = redirect.to_string(); + continue; + } + } + } + } + return Err(e)?; + } + } + }; + log::info!("ws协议握手 {:?}", response); + ws.send(Message::Binary(data)).await?; + let (mut ws_write, ws_read) = ws.split(); + let (sender, mut receiver) = channel::>(100); + context + .packet_map + .write() + .insert(WS_ADDR, PacketSender::new(sender)); + tokio::spawn(async move { + while let Some(data) = receiver.recv().await { + if let Err(e) = ws_write.send(Message::Binary(data)).await { + log::warn!("websocket err {:?}", e); + break; + } + } + let _ = ws_write.close().await; + }); + if let Err(e) = ws_read_handle(ws_read, recv_handler, &context).await { + log::warn!("{:?}", e); + } + context.packet_map.write().remove(&WS_ADDR); + Ok(()) +} +async fn ws_read_handle( + mut ws_read: SplitStream>>, + recv_handler: H, + context: &ChannelContext, +) -> anyhow::Result<()> +where + H: RecvChannelHandler, +{ + let mut extend = [0; BUFFER_SIZE]; + let route_key = RouteKey::new(ConnectProtocol::WS, 0, WS_ADDR); + while let Some(msg) = ws_read.next().await { + let msg = msg.context("Error during WebSocket ")?; + match msg { + Message::Text(txt) => log::info!("Received text message: {}", txt), + Message::Binary(mut data) => { + recv_handler.handle(&mut data, &mut extend, route_key, context); + } + Message::Ping(_) | Message::Pong(_) => (), + Message::Close(_) => break, + _ => {} + } + } + Ok(()) +} diff --git a/vnt/src/cipher/aes_cbc/rs_aes_cbc.rs b/vnt/src/cipher/aes_cbc/rs_aes_cbc.rs index b16d33a0..c765121b 100644 --- a/vnt/src/cipher/aes_cbc/rs_aes_cbc.rs +++ b/vnt/src/cipher/aes_cbc/rs_aes_cbc.rs @@ -59,12 +59,7 @@ impl AesCbcCipher { return Err(anyhow!("aes_cbc data err")); } let mut iv = [0; 16]; - iv[0..4].copy_from_slice(&net_packet.source().octets()); - iv[4..8].copy_from_slice(&net_packet.destination().octets()); - iv[8] = net_packet.protocol().into(); - iv[9] = net_packet.transport_protocol(); - iv[10] = net_packet.is_gateway() as u8; - iv[11] = net_packet.source_ttl(); + iv[0..12].copy_from_slice(&net_packet.head_tag()); if let Some(finger) = &self.finger { iv[12..16].copy_from_slice(&finger.hash[0..4]); } @@ -102,12 +97,7 @@ impl AesCbcCipher { ) -> anyhow::Result<()> { let data_len = net_packet.data_len(); let mut iv = [0; 16]; - iv[0..4].copy_from_slice(&net_packet.source().octets()); - iv[4..8].copy_from_slice(&net_packet.destination().octets()); - iv[8] = net_packet.protocol().into(); - iv[9] = net_packet.transport_protocol(); - iv[10] = net_packet.is_gateway() as u8; - iv[11] = net_packet.source_ttl(); + iv[0..12].copy_from_slice(&net_packet.head_tag()); if let Some(finger) = &self.finger { iv[12..16].copy_from_slice(&finger.hash[0..4]); net_packet.set_data_len(data_len + 16)?; @@ -146,3 +136,18 @@ impl AesCbcCipher { }; } } +#[test] +fn test_aes_cbc() { + let d = AesCbcCipher::new_128([0; 16], Some(Finger::new("123"))); + let mut p = NetPacket::new_encrypt([0; 100]).unwrap(); + let src = p.buffer().to_vec(); + d.encrypt_ipv4(&mut p).unwrap(); + d.decrypt_ipv4(&mut p).unwrap(); + assert_eq!(p.buffer(), &src); + let d = AesCbcCipher::new_128([0; 16], None); + let mut p = NetPacket::new_encrypt([0; 100]).unwrap(); + let src = p.buffer().to_vec(); + d.encrypt_ipv4(&mut p).unwrap(); + d.decrypt_ipv4(&mut p).unwrap(); + assert_eq!(p.buffer(), &src); +} diff --git a/vnt/src/cipher/aes_ecb/openssl_aes_ecb.rs b/vnt/src/cipher/aes_ecb/openssl_aes_ecb.rs index 649fa047..338865ed 100644 --- a/vnt/src/cipher/aes_ecb/openssl_aes_ecb.rs +++ b/vnt/src/cipher/aes_ecb/openssl_aes_ecb.rs @@ -110,13 +110,7 @@ impl AesEcbCipher { } if let Some(finger) = &self.finger { - let mut nonce_raw = [0; 12]; - nonce_raw[0..4].copy_from_slice(&net_packet.source().octets()); - nonce_raw[4..8].copy_from_slice(&net_packet.destination().octets()); - nonce_raw[8] = net_packet.protocol().into(); - nonce_raw[9] = net_packet.transport_protocol(); - nonce_raw[10] = net_packet.is_gateway() as u8; - nonce_raw[11] = net_packet.source_ttl(); + let nonce_raw = net_packet.head_tag(); let len = net_packet.payload().len(); if len < 12 { return Err(anyhow!("data len err")); @@ -202,13 +196,7 @@ impl AesEcbCipher { net_packet.payload_mut().copy_from_slice(ciphertext); net_packet.set_encrypt_flag(true); if let Some(finger) = &self.finger { - let mut nonce_raw = [0; 12]; - nonce_raw[0..4].copy_from_slice(&net_packet.source().octets()); - nonce_raw[4..8].copy_from_slice(&net_packet.destination().octets()); - nonce_raw[8] = net_packet.protocol().into(); - nonce_raw[9] = net_packet.transport_protocol(); - nonce_raw[10] = net_packet.is_gateway() as u8; - nonce_raw[11] = net_packet.source_ttl(); + let nonce_raw = net_packet.head_tag(); let finger = finger.calculate_finger(&nonce_raw, ciphertext); let src_data_len = net_packet.data_len(); //设置实际长度 @@ -224,6 +212,8 @@ impl AesEcbCipher { fn test_openssl_aes_ecb() { let d = AesEcbCipher::new_128([0; 16], Some(Finger::new("123"))); let mut p = NetPacket::new_encrypt([0; 100]).unwrap(); + let src = p.buffer().to_vec(); d.encrypt_ipv4(&mut p).unwrap(); d.decrypt_ipv4(&mut p).unwrap(); + assert_eq!(p.buffer(), &src); } diff --git a/vnt/src/cipher/aes_ecb/rs_aes_ecb.rs b/vnt/src/cipher/aes_ecb/rs_aes_ecb.rs index 5fd94855..ab68703a 100644 --- a/vnt/src/cipher/aes_ecb/rs_aes_ecb.rs +++ b/vnt/src/cipher/aes_ecb/rs_aes_ecb.rs @@ -54,13 +54,7 @@ impl AesEcbCipher { } if let Some(finger) = &self.finger { - let mut nonce_raw = [0; 12]; - nonce_raw[0..4].copy_from_slice(&net_packet.source().octets()); - nonce_raw[4..8].copy_from_slice(&net_packet.destination().octets()); - nonce_raw[8] = net_packet.protocol().into(); - nonce_raw[9] = net_packet.transport_protocol(); - nonce_raw[10] = net_packet.is_gateway() as u8; - nonce_raw[11] = net_packet.source_ttl(); + let nonce_raw = net_packet.head_tag(); let len = net_packet.payload().len(); if len < 12 { return Err(anyhow!("payload len <12")); @@ -134,13 +128,7 @@ impl AesEcbCipher { net_packet.set_encrypt_flag(true); if let Some(finger) = &self.finger { - let mut nonce_raw = [0; 12]; - nonce_raw[0..4].copy_from_slice(&net_packet.source().octets()); - nonce_raw[4..8].copy_from_slice(&net_packet.destination().octets()); - nonce_raw[8] = net_packet.protocol().into(); - nonce_raw[9] = net_packet.transport_protocol(); - nonce_raw[10] = net_packet.is_gateway() as u8; - nonce_raw[11] = net_packet.source_ttl(); + let nonce_raw = net_packet.head_tag(); let finger = finger.calculate_finger(&nonce_raw, buf); let src_data_len = net_packet.data_len(); //设置实际长度 diff --git a/vnt/src/cipher/aes_gcm/aes_gcm_cipher.rs b/vnt/src/cipher/aes_gcm/aes_gcm_cipher.rs index e261a494..3b2ac781 100644 --- a/vnt/src/cipher/aes_gcm/aes_gcm_cipher.rs +++ b/vnt/src/cipher/aes_gcm/aes_gcm_cipher.rs @@ -47,13 +47,7 @@ impl AesGcmCipher { log::error!("数据异常,长度小于{}", AES_GCM_ENCRYPTION_RESERVED); return Err(anyhow!("data err")); } - let mut nonce_raw = [0; 12]; - nonce_raw[0..4].copy_from_slice(&net_packet.source().octets()); - nonce_raw[4..8].copy_from_slice(&net_packet.destination().octets()); - nonce_raw[8] = net_packet.protocol().into(); - nonce_raw[9] = net_packet.transport_protocol(); - nonce_raw[10] = net_packet.is_gateway() as u8; - nonce_raw[11] = net_packet.source_ttl(); + let nonce_raw = net_packet.head_tag(); let nonce: &GenericArray = Nonce::from_slice(&nonce_raw); let mut secret_body = SecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; @@ -89,13 +83,7 @@ impl AesGcmCipher { if net_packet.reserve() < AES_GCM_ENCRYPTION_RESERVED { return Err(anyhow!("too short")); } - let mut nonce_raw = [0; 12]; - nonce_raw[0..4].copy_from_slice(&net_packet.source().octets()); - nonce_raw[4..8].copy_from_slice(&net_packet.destination().octets()); - nonce_raw[8] = net_packet.protocol().into(); - nonce_raw[9] = net_packet.transport_protocol(); - nonce_raw[10] = net_packet.is_gateway() as u8; - nonce_raw[11] = net_packet.source_ttl(); + let nonce_raw = net_packet.head_tag(); let nonce: &GenericArray = Nonce::from_slice(&nonce_raw); let data_len = net_packet.data_len() + AES_GCM_ENCRYPTION_RESERVED; net_packet.set_data_len(data_len)?; @@ -123,3 +111,22 @@ impl AesGcmCipher { }; } } + +#[test] +fn test_aes_gcm() { + let d = AesGcmCipher::new_256([0; 32], Some(Finger::new("123"))); + let mut p = + NetPacket::new_encrypt([1; 13 + crate::protocol::body::ENCRYPTION_RESERVED]).unwrap(); + let src = p.buffer().to_vec(); + d.encrypt_ipv4(&mut p).unwrap(); + d.decrypt_ipv4(&mut p).unwrap(); + assert_eq!(p.buffer(), &src); + + let d = AesGcmCipher::new_256([0; 32], None); + let mut p = + NetPacket::new_encrypt([0; 13 + crate::protocol::body::ENCRYPTION_RESERVED]).unwrap(); + let src = p.buffer().to_vec(); + d.encrypt_ipv4(&mut p).unwrap(); + d.decrypt_ipv4(&mut p).unwrap(); + assert_eq!(p.buffer(), &src); +} diff --git a/vnt/src/cipher/aes_gcm/ring_aes_gcm_cipher.rs b/vnt/src/cipher/aes_gcm/ring_aes_gcm_cipher.rs index 4123d83b..fbea94a5 100644 --- a/vnt/src/cipher/aes_gcm/ring_aes_gcm_cipher.rs +++ b/vnt/src/cipher/aes_gcm/ring_aes_gcm_cipher.rs @@ -62,13 +62,7 @@ impl AesGcmCipher { log::error!("数据异常,长度小于{}", AES_GCM_ENCRYPTION_RESERVED); return Err(anyhow!("data err")); } - let mut nonce_raw = [0; 12]; - nonce_raw[0..4].copy_from_slice(&net_packet.source().octets()); - nonce_raw[4..8].copy_from_slice(&net_packet.destination().octets()); - nonce_raw[8] = net_packet.protocol().into(); - nonce_raw[9] = net_packet.transport_protocol(); - nonce_raw[10] = net_packet.is_gateway() as u8; - nonce_raw[11] = net_packet.source_ttl(); + let nonce_raw = net_packet.head_tag(); let nonce = aead::Nonce::assume_unique_for_key(nonce_raw); let mut secret_body = SecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; if let Some(finger) = &self.finger { @@ -100,13 +94,7 @@ impl AesGcmCipher { &self, net_packet: &mut NetPacket, ) -> anyhow::Result<()> { - let mut nonce_raw = [0; 12]; - nonce_raw[0..4].copy_from_slice(&net_packet.source().octets()); - nonce_raw[4..8].copy_from_slice(&net_packet.destination().octets()); - nonce_raw[8] = net_packet.protocol().into(); - nonce_raw[9] = net_packet.transport_protocol(); - nonce_raw[10] = net_packet.is_gateway() as u8; - nonce_raw[11] = net_packet.source_ttl(); + let nonce_raw = net_packet.head_tag(); let nonce = aead::Nonce::assume_unique_for_key(nonce_raw); let data_len = net_packet.data_len() + AES_GCM_ENCRYPTION_RESERVED; net_packet.set_data_len(data_len)?; @@ -139,3 +127,22 @@ impl AesGcmCipher { }; } } + +#[test] +fn test_aes_gcm() { + let d = AesGcmCipher::new_256([0; 32], Some(Finger::new("123"))); + let mut p = + NetPacket::new_encrypt([0; 13 + crate::protocol::body::ENCRYPTION_RESERVED]).unwrap(); + let src = p.buffer().to_vec(); + d.encrypt_ipv4(&mut p).unwrap(); + d.decrypt_ipv4(&mut p).unwrap(); + assert_eq!(p.buffer(), &src); + + let d = AesGcmCipher::new_256([0; 32], None); + let mut p = + NetPacket::new_encrypt([0; 13 + crate::protocol::body::ENCRYPTION_RESERVED]).unwrap(); + let src = p.buffer().to_vec(); + d.encrypt_ipv4(&mut p).unwrap(); + d.decrypt_ipv4(&mut p).unwrap(); + assert_eq!(p.buffer(), &src); +} diff --git a/vnt/src/cipher/chacha20/rs_chacha20.rs b/vnt/src/cipher/chacha20/rs_chacha20.rs index 5a78d9a4..3da6d7ca 100644 --- a/vnt/src/cipher/chacha20/rs_chacha20.rs +++ b/vnt/src/cipher/chacha20/rs_chacha20.rs @@ -3,8 +3,11 @@ use anyhow::anyhow; use chacha20::cipher::{Key, KeyIvInit, StreamCipher}; use chacha20::ChaCha20; +use crate::cipher::finger::{gen_nonce, gen_random_nonce}; use crate::cipher::Finger; -use crate::protocol::body::ChaCah20SecretBody; +use crate::protocol::body::{ + IVSecretBody, SecretTail, SecretTailMut, FINGER_RESERVED, RANDOM_RESERVED, +}; use crate::protocol::NetPacket; #[derive(Clone)] @@ -34,29 +37,22 @@ impl ChaCha20Cipher { //未加密的数据直接丢弃 return Err(anyhow!("not encrypt")); } - let mut iv = [0; 12]; - iv[0..4].copy_from_slice(&net_packet.source().octets()); - iv[4..8].copy_from_slice(&net_packet.destination().octets()); - iv[8] = net_packet.protocol().into(); - iv[9] = net_packet.transport_protocol(); - iv[10] = net_packet.is_gateway() as u8; - iv[11] = net_packet.source_ttl(); + let mut head_tag = net_packet.head_tag(); - let mut secret_body = - ChaCah20SecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; + let mut secret_body = IVSecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; if let Some(finger) = &self.finger { - let finger = finger.calculate_finger(&iv[..12], secret_body.en_body()); + let finger = finger.calculate_finger(&head_tag, secret_body.data()); if &finger != secret_body.finger() { return Err(anyhow!("ChaCha20 finger err")); } } - + gen_nonce(&mut head_tag, secret_body.random_buf()); ChaCha20::new( Key::::from_slice(&self.key), - Iv::::from_slice(&iv), + Iv::::from_slice(&head_tag), ) - .apply_keystream(secret_body.en_body_mut()); - let len = secret_body.en_body().len(); + .apply_keystream(secret_body.data_mut()); + let len = secret_body.data().len(); net_packet.set_encrypt_flag(false); net_packet.set_payload_len(len)?; Ok(()) @@ -66,29 +62,26 @@ impl ChaCha20Cipher { net_packet: &mut NetPacket, ) -> anyhow::Result<()> { let data_len = net_packet.data_len(); - let mut iv = [0; 12]; - iv[0..4].copy_from_slice(&net_packet.source().octets()); - iv[4..8].copy_from_slice(&net_packet.destination().octets()); - iv[8] = net_packet.protocol().into(); - iv[9] = net_packet.transport_protocol(); - iv[10] = net_packet.is_gateway() as u8; - iv[11] = net_packet.source_ttl(); + let head_tag = net_packet.head_tag(); if let Some(_) = &self.finger { - net_packet.set_data_len(data_len + 12)?; + net_packet.set_data_len(data_len + RANDOM_RESERVED + FINGER_RESERVED)?; + } else { + net_packet.set_data_len(data_len + RANDOM_RESERVED)?; } - let mut secret_body = - ChaCah20SecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; + let mut secret_body = IVSecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; + let mut nonce = head_tag; + secret_body.set_random(&gen_random_nonce(&mut nonce)); + ChaCha20::new( Key::::from_slice(&self.key), - Iv::::from_slice(&iv), + Iv::::from_slice(&nonce), ) - .apply_keystream(secret_body.en_body_mut()); + .apply_keystream(secret_body.data_mut()); if let Some(finger) = &self.finger { - let finger = finger.calculate_finger(&iv[..12], secret_body.en_body_mut()); - let mut secret_body = ChaCah20SecretBody::new(net_packet.payload_mut(), true)?; + let finger = finger.calculate_finger(&head_tag, secret_body.data()); + let mut secret_body = IVSecretBody::new(net_packet.payload_mut(), true)?; secret_body.set_finger(&finger)?; } - net_packet.set_encrypt_flag(true); Ok(()) } @@ -98,7 +91,7 @@ impl ChaCha20Cipher { fn test_chacha20() { let d = ChaCha20Cipher::new_256([0; 32], Some(Finger::new("123"))); let mut p = - NetPacket::new_encrypt([0; 13 + crate::protocol::body::ENCRYPTION_RESERVED]).unwrap(); + NetPacket::new_encrypt([1; 13 + crate::protocol::body::ENCRYPTION_RESERVED]).unwrap(); let src = p.buffer().to_vec(); d.encrypt_ipv4(&mut p).unwrap(); d.decrypt_ipv4(&mut p).unwrap(); @@ -106,7 +99,7 @@ fn test_chacha20() { let d = ChaCha20Cipher::new_256([0; 32], None); let mut p = - NetPacket::new_encrypt([0; 13 + crate::protocol::body::ENCRYPTION_RESERVED]).unwrap(); + NetPacket::new_encrypt([2; 13 + crate::protocol::body::ENCRYPTION_RESERVED]).unwrap(); let src = p.buffer().to_vec(); d.encrypt_ipv4(&mut p).unwrap(); d.decrypt_ipv4(&mut p).unwrap(); diff --git a/vnt/src/cipher/chacha20_poly1305/ring_chacha20_poly1305.rs b/vnt/src/cipher/chacha20_poly1305/ring_chacha20_poly1305.rs index f27e11a4..1759a3cd 100644 --- a/vnt/src/cipher/chacha20_poly1305/ring_chacha20_poly1305.rs +++ b/vnt/src/cipher/chacha20_poly1305/ring_chacha20_poly1305.rs @@ -3,8 +3,11 @@ use anyhow::anyhow; use ring::aead; use ring::aead::{LessSafeKey, UnboundKey}; +use crate::cipher::finger::{gen_nonce, gen_random_nonce}; use crate::cipher::Finger; -use crate::protocol::body::{SecretBody, AES_GCM_ENCRYPTION_RESERVED}; +use crate::protocol::body::{ + AEADSecretBody, SecretTail, SecretTailMut, FINGER_RESERVED, RANDOM_RESERVED, TAG_RESERVED, +}; use crate::protocol::NetPacket; #[derive(Clone)] @@ -40,34 +43,29 @@ impl ChaCha20Poly1305Cipher { //未加密的数据直接丢弃 return Err(anyhow!("not encrypt")); } - if net_packet.payload().len() < AES_GCM_ENCRYPTION_RESERVED { - log::error!("数据异常,长度小于{}", AES_GCM_ENCRYPTION_RESERVED); + if net_packet.payload().len() < TAG_RESERVED { + log::error!("数据异常,长度小于{}", TAG_RESERVED); return Err(anyhow!("data err")); } - let mut nonce_raw = [0; 12]; - nonce_raw[0..4].copy_from_slice(&net_packet.source().octets()); - nonce_raw[4..8].copy_from_slice(&net_packet.destination().octets()); - nonce_raw[8] = net_packet.protocol().into(); - nonce_raw[9] = net_packet.transport_protocol(); - nonce_raw[10] = net_packet.is_gateway() as u8; - nonce_raw[11] = net_packet.source_ttl(); - let nonce = aead::Nonce::assume_unique_for_key(nonce_raw); - let mut secret_body = SecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; + let mut head_tag = net_packet.head_tag(); + let mut secret_body = AEADSecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; if let Some(finger) = &self.finger { - let finger = finger.calculate_finger(&nonce_raw, secret_body.en_body()); + let finger = finger.calculate_finger(&head_tag, secret_body.data_tag_mut()); if &finger != secret_body.finger() { return Err(anyhow!("ring CHACHA20_POLY1305 finger err")); } } - + gen_nonce(&mut head_tag, secret_body.random_buf()); + let nonce = aead::Nonce::assume_unique_for_key(head_tag); let rs = self .cipher - .open_in_place(nonce, aead::Aad::empty(), secret_body.en_body_mut()); + .open_in_place(nonce, aead::Aad::empty(), secret_body.data_tag_mut()); if let Err(e) = rs { return Err(anyhow!("ring CHACHA20_POLY1305 解密失败:{}", e)); } + let len = secret_body.data().len(); net_packet.set_encrypt_flag(false); - net_packet.set_data_len(net_packet.data_len() - AES_GCM_ENCRYPTION_RESERVED)?; + net_packet.set_payload_len(len)?; return Ok(()); } /// net_packet 必须预留足够长度 @@ -77,23 +75,23 @@ impl ChaCha20Poly1305Cipher { &self, net_packet: &mut NetPacket, ) -> anyhow::Result<()> { - let mut nonce_raw = [0; 12]; - nonce_raw[0..4].copy_from_slice(&net_packet.source().octets()); - nonce_raw[4..8].copy_from_slice(&net_packet.destination().octets()); - nonce_raw[8] = net_packet.protocol().into(); - nonce_raw[9] = net_packet.transport_protocol(); - nonce_raw[10] = net_packet.is_gateway() as u8; - nonce_raw[11] = net_packet.source_ttl(); - let nonce = aead::Nonce::assume_unique_for_key(nonce_raw); - let data_len = net_packet.data_len() + AES_GCM_ENCRYPTION_RESERVED; - net_packet.set_data_len(data_len)?; - let mut secret_body = SecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; + let head_tag = net_packet.head_tag(); + let data_len = net_packet.data_len(); + if self.finger.is_some() { + net_packet.set_data_len(data_len + TAG_RESERVED + RANDOM_RESERVED + FINGER_RESERVED)?; + } else { + net_packet.set_data_len(data_len + TAG_RESERVED + RANDOM_RESERVED)?; + } + let mut secret_body = AEADSecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; + let mut nonce = head_tag; + secret_body.set_random(&gen_random_nonce(&mut nonce)); + let nonce = aead::Nonce::assume_unique_for_key(nonce); let rs = self.cipher.seal_in_place_separate_tag( nonce, aead::Aad::empty(), - secret_body.body_mut(), + secret_body.data_mut(), ); - return match rs { + match rs { Ok(tag) => { let tag = tag.as_ref(); if tag.len() != 16 { @@ -101,14 +99,14 @@ impl ChaCha20Poly1305Cipher { } secret_body.set_tag(tag)?; if let Some(finger) = &self.finger { - let finger = finger.calculate_finger(&nonce_raw, secret_body.en_body()); + let finger = finger.calculate_finger(&head_tag, secret_body.data_tag_mut()); secret_body.set_finger(&finger)?; } net_packet.set_encrypt_flag(true); Ok(()) } Err(e) => Err(anyhow!("ring CHACHA20_POLY1305 加密失败:{}", e)), - }; + } } } diff --git a/vnt/src/cipher/chacha20_poly1305/rs_chacha20_poly1305.rs b/vnt/src/cipher/chacha20_poly1305/rs_chacha20_poly1305.rs index 9764f70c..39388db3 100644 --- a/vnt/src/cipher/chacha20_poly1305/rs_chacha20_poly1305.rs +++ b/vnt/src/cipher/chacha20_poly1305/rs_chacha20_poly1305.rs @@ -1,11 +1,13 @@ +use crate::cipher::finger::{gen_nonce, gen_random_nonce}; +use crate::cipher::Finger; +use crate::protocol::body::{ + AEADSecretBody, SecretTail, SecretTailMut, FINGER_RESERVED, RANDOM_RESERVED, TAG_RESERVED, +}; +use crate::protocol::NetPacket; use anyhow::anyhow; use chacha20poly1305::aead::{Nonce, Tag}; use chacha20poly1305::{AeadInPlace, ChaCha20Poly1305, Key, KeyInit}; -use crate::cipher::Finger; -use crate::protocol::body::{SecretBody, AES_GCM_ENCRYPTION_RESERVED}; -use crate::protocol::NetPacket; - #[derive(Clone)] pub struct ChaCha20Poly1305Cipher { key: Vec, @@ -24,6 +26,7 @@ impl ChaCha20Poly1305Cipher { } } } + impl ChaCha20Poly1305Cipher { pub fn key(&self) -> &[u8] { &self.key @@ -39,35 +42,31 @@ impl ChaCha20Poly1305Cipher { //未加密的数据直接丢弃 return Err(anyhow!("not encrypt")); } - if net_packet.payload().len() < AES_GCM_ENCRYPTION_RESERVED { - log::error!("数据异常,长度小于{}", AES_GCM_ENCRYPTION_RESERVED); + if net_packet.payload().len() < TAG_RESERVED { + log::error!("数据异常,长度小于{}", TAG_RESERVED); return Err(anyhow!("data err")); } - let mut nonce_raw = [0; 12]; - nonce_raw[0..4].copy_from_slice(&net_packet.source().octets()); - nonce_raw[4..8].copy_from_slice(&net_packet.destination().octets()); - nonce_raw[8] = net_packet.protocol().into(); - nonce_raw[9] = net_packet.transport_protocol(); - nonce_raw[10] = net_packet.is_gateway() as u8; - nonce_raw[11] = net_packet.source_ttl(); - let mut secret_body = SecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; + let mut head_tag = net_packet.head_tag(); + let mut secret_body = AEADSecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; if let Some(finger) = &self.finger { - let finger = finger.calculate_finger(&nonce_raw, secret_body.en_body()); + let finger = finger.calculate_finger(&head_tag, secret_body.data_tag_mut()); if &finger != secret_body.finger() { return Err(anyhow!("rs CHACHA20_POLY1305 finger err")); } } - let nonce: Nonce = nonce_raw.into(); + gen_nonce(&mut head_tag, secret_body.random_buf()); + let nonce: Nonce = head_tag.into(); let tag: Tag = Tag::::from_slice(secret_body.tag()).clone(); if let Err(e) = self.cipher - .decrypt_in_place_detached(&nonce, &[], secret_body.body_mut(), &tag) + .decrypt_in_place_detached(&nonce, &[], secret_body.data_mut(), &tag) { return Err(anyhow!("rs CHACHA20_POLY1305 decrypt_ipv4 {:?}", e)); } + let len = secret_body.data().len(); net_packet.set_encrypt_flag(false); - net_packet.set_data_len(net_packet.data_len() - AES_GCM_ENCRYPTION_RESERVED)?; + net_packet.set_payload_len(len)?; Ok(()) } /// net_packet 必须预留足够长度 @@ -77,21 +76,21 @@ impl ChaCha20Poly1305Cipher { &self, net_packet: &mut NetPacket, ) -> anyhow::Result<()> { - let mut nonce_raw = [0; 12]; - nonce_raw[0..4].copy_from_slice(&net_packet.source().octets()); - nonce_raw[4..8].copy_from_slice(&net_packet.destination().octets()); - nonce_raw[8] = net_packet.protocol().into(); - nonce_raw[9] = net_packet.transport_protocol(); - nonce_raw[10] = net_packet.is_gateway() as u8; - nonce_raw[11] = net_packet.source_ttl(); - let nonce = nonce_raw.into(); - let data_len = net_packet.data_len() + AES_GCM_ENCRYPTION_RESERVED; - net_packet.set_data_len(data_len)?; - let mut secret_body = SecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; + let head_tag = net_packet.head_tag(); + let data_len = net_packet.data_len(); + if self.finger.is_some() { + net_packet.set_data_len(data_len + TAG_RESERVED + RANDOM_RESERVED + FINGER_RESERVED)?; + } else { + net_packet.set_data_len(data_len + TAG_RESERVED + RANDOM_RESERVED)?; + } + let mut secret_body = AEADSecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; + let mut nonce = head_tag; + secret_body.set_random(&gen_random_nonce(&mut nonce)); + let nonce = nonce.into(); let rs = self .cipher - .encrypt_in_place_detached(&nonce, &[], secret_body.body_mut()); - return match rs { + .encrypt_in_place_detached(&nonce, &[], secret_body.data_mut()); + match rs { Ok(tag) => { let tag: &[u8] = tag.as_ref(); if tag.len() != 16 { @@ -99,14 +98,14 @@ impl ChaCha20Poly1305Cipher { } secret_body.set_tag(tag)?; if let Some(finger) = &self.finger { - let finger = finger.calculate_finger(&nonce_raw, secret_body.en_body()); + let finger = finger.calculate_finger(&head_tag, secret_body.data_tag_mut()); secret_body.set_finger(&finger)?; } net_packet.set_encrypt_flag(true); Ok(()) } Err(e) => Err(anyhow!("rs CHACHA20_POLY1305 加密失败:{}", e)), - }; + } } } @@ -118,4 +117,10 @@ fn test_rs_chacha20_poly1305() { d.encrypt_ipv4(&mut p).unwrap(); d.decrypt_ipv4(&mut p).unwrap(); assert_eq!(p.buffer(), &src); + let d = ChaCha20Poly1305Cipher::new_256([0; 32], None); + let mut p = NetPacket::new_encrypt([0; 73]).unwrap(); + let src = p.buffer().to_vec(); + d.encrypt_ipv4(&mut p).unwrap(); + d.decrypt_ipv4(&mut p).unwrap(); + assert_eq!(p.buffer(), &src); } diff --git a/vnt/src/cipher/cipher.rs b/vnt/src/cipher/cipher.rs index 3aba856a..3613e799 100644 --- a/vnt/src/cipher/cipher.rs +++ b/vnt/src/cipher/cipher.rs @@ -122,7 +122,7 @@ impl Cipher { model: CipherModel, password: Option, token: Option, - ) -> Self { + ) -> anyhow::Result { if let Some(password) = password { #[cfg(cipher)] let key: [u8; 32] = { @@ -136,33 +136,33 @@ impl Cipher { let finger = token.map(|token| Finger::new(&token)); if password.len() < 8 { let aes = AesGcmCipher::new_128(key[..16].try_into().unwrap(), finger); - Cipher::AesGcm((aes, key[..16].to_vec())) + Ok(Cipher::AesGcm((aes, key[..16].to_vec()))) } else { let aes = AesGcmCipher::new_256(key, finger); - Cipher::AesGcm((aes, key.to_vec())) + Ok(Cipher::AesGcm((aes, key.to_vec()))) } } #[cfg(feature = "chacha20_poly1305")] CipherModel::Chacha20Poly1305 => { let finger = token.map(|token| Finger::new(&token)); let chacha = ChaCha20Poly1305Cipher::new_256(key, finger); - Cipher::Chacha20Poly1305(chacha) + Ok(Cipher::Chacha20Poly1305(chacha)) } #[cfg(feature = "chacha20_poly1305")] CipherModel::Chacha20 => { let finger = token.map(|token| Finger::new(&token)); let chacha = ChaCha20Cipher::new_256(key, finger); - Cipher::Chacha20(chacha) + Ok(Cipher::Chacha20(chacha)) } #[cfg(feature = "aes_cbc")] CipherModel::AesCbc => { let finger = token.map(|token| Finger::new(&token)); if password.len() < 8 { let aes = AesCbcCipher::new_128(key[..16].try_into().unwrap(), finger); - Cipher::AesCbc(aes) + Ok(Cipher::AesCbc(aes)) } else { let aes = AesCbcCipher::new_256(key, finger); - Cipher::AesCbc(aes) + Ok(Cipher::AesCbc(aes)) } } #[cfg(feature = "aes_ecb")] @@ -170,28 +170,32 @@ impl Cipher { let finger = token.map(|token| Finger::new(&token)); if password.len() < 8 { let aes = AesEcbCipher::new_128(key[..16].try_into().unwrap(), finger); - Cipher::AesEcb(aes) + Ok(Cipher::AesEcb(aes)) } else { let aes = AesEcbCipher::new_256(key, finger); - Cipher::AesEcb(aes) + Ok(Cipher::AesEcb(aes)) } } #[cfg(feature = "sm4_cbc")] CipherModel::Sm4Cbc => { let finger = token.map(|token| Finger::new(&token)); let aes = Sm4CbcCipher::new_128(key[..16].try_into().unwrap(), finger); - Cipher::Sm4Cbc(aes) + Ok(Cipher::Sm4Cbc(aes)) } CipherModel::Xor => { - let _token = token; - Cipher::Xor(XORCipher::new_256(crate::cipher::xor::simple_hash( - &password, + if token.is_some() { + Err(anyhow::anyhow!( + "'finger' and 'xor' cannot be used simultaneously" + ))? + } + Ok(Cipher::Xor(XORCipher::new_256( + crate::cipher::xor::simple_hash(&password), ))) } - CipherModel::None => Cipher::None, + CipherModel::None => Ok(Cipher::None), } } else { - Cipher::None + Ok(Cipher::None) } } #[cfg(not(any(feature = "aes_gcm", feature = "server_encrypt")))] diff --git a/vnt/src/cipher/finger.rs b/vnt/src/cipher/finger.rs index 73d6dc3b..f070a7a1 100644 --- a/vnt/src/cipher/finger.rs +++ b/vnt/src/cipher/finger.rs @@ -1,4 +1,5 @@ use anyhow::anyhow; +use rand::RngCore; use sha2::Digest; @@ -49,3 +50,27 @@ impl Finger { return key[20..].try_into().unwrap(); } } +impl> NetPacket { + pub fn head_tag(&self) -> [u8; 12] { + let mut tag = [0; 12]; + tag[0..4].copy_from_slice(&self.buffer()[4..8]); + tag[4..8].copy_from_slice(&self.buffer()[8..12]); + tag[8] = self.protocol().into(); + tag[9] = self.transport_protocol(); + tag[10] = self.is_gateway() as u8; + tag[11] = self.source_ttl(); + tag + } +} +pub fn gen_nonce(tag: &mut [u8], random: &[u8]) { + tag[8] = random[0] ^ tag[8]; + tag[9] = random[1] ^ tag[9]; + tag[10] = random[2] ^ tag[10]; + tag[11] = random[3] ^ tag[11]; +} +pub fn gen_random_nonce(tag: &mut [u8; 12]) -> [u8; 4] { + let mut random = [0; 4]; + rand::thread_rng().fill_bytes(&mut random); + gen_nonce(tag, &random); + random +} diff --git a/vnt/src/cipher/sm4_cbc/rs_sm4_cbc.rs b/vnt/src/cipher/sm4_cbc/rs_sm4_cbc.rs index 68b16159..885749cf 100644 --- a/vnt/src/cipher/sm4_cbc/rs_sm4_cbc.rs +++ b/vnt/src/cipher/sm4_cbc/rs_sm4_cbc.rs @@ -48,13 +48,7 @@ impl Sm4CbcCipher { } if let Some(finger) = &self.finger { - let mut nonce_raw = [0; 12]; - nonce_raw[0..4].copy_from_slice(&net_packet.source().octets()); - nonce_raw[4..8].copy_from_slice(&net_packet.destination().octets()); - nonce_raw[8] = net_packet.protocol().into(); - nonce_raw[9] = net_packet.transport_protocol(); - nonce_raw[10] = net_packet.is_gateway() as u8; - nonce_raw[11] = net_packet.source_ttl(); + let nonce_raw = net_packet.head_tag(); let len = net_packet.payload().len(); if len < 12 { return Err(anyhow!("payload len <12")); @@ -126,13 +120,7 @@ impl Sm4CbcCipher { net_packet.payload_mut()[..len].copy_from_slice(&out[..len]); net_packet.payload_mut()[len..].copy_from_slice(&iv); if let Some(finger) = &self.finger { - let mut nonce_raw = [0; 12]; - nonce_raw[0..4].copy_from_slice(&net_packet.source().octets()); - nonce_raw[4..8].copy_from_slice(&net_packet.destination().octets()); - nonce_raw[8] = net_packet.protocol().into(); - nonce_raw[9] = net_packet.transport_protocol(); - nonce_raw[10] = net_packet.is_gateway() as u8; - nonce_raw[11] = net_packet.source_ttl(); + let nonce_raw = net_packet.head_tag(); let finger = finger.calculate_finger(&nonce_raw, net_packet.payload()); let src_data_len = net_packet.data_len(); //设置实际长度 diff --git a/vnt/src/compression/mod.rs b/vnt/src/compression/mod.rs index 797ed424..d9f06a83 100644 --- a/vnt/src/compression/mod.rs +++ b/vnt/src/compression/mod.rs @@ -135,6 +135,7 @@ impl Compressor { } #[test] +#[cfg(feature = "zstd_compress")] fn test_lz4() { use crate::protocol::extension::{CompressionAlgorithm, ExtensionTailPacket}; let lz4 = Compressor::Lz4; @@ -166,7 +167,7 @@ fn test_lz4() { unimplemented!() } }, - ExtensionTailPacket::Unknown => { + _ => { unimplemented!() } } @@ -174,6 +175,7 @@ fn test_lz4() { assert_eq!(in_packet.payload(), src_out_packet.payload()) } #[test] +#[cfg(feature = "zstd_compress")] fn test_zstd() { use crate::protocol::extension::{CompressionAlgorithm, ExtensionTailPacket}; let zstd = Compressor::Zstd(22); @@ -209,7 +211,7 @@ fn test_zstd() { unimplemented!() } }, - ExtensionTailPacket::Unknown => { + _ => { unimplemented!() } } diff --git a/vnt/src/core/conn.rs b/vnt/src/core/conn.rs index bd80078a..dfd35ade 100644 --- a/vnt/src/core/conn.rs +++ b/vnt/src/core/conn.rs @@ -1,21 +1,22 @@ use std::collections::HashMap; use std::net::Ipv4Addr; +use std::ops::Deref; use std::sync::Arc; use std::time::Duration; use crossbeam_utils::atomic::AtomicCell; use parking_lot::{Mutex, RwLock}; use rand::Rng; -#[cfg(not(target_os = "android"))] -use tun::device::IFace; use crate::channel::context::ChannelContext; use crate::channel::idle::Idle; use crate::channel::punch::{NatInfo, Punch}; +use crate::channel::sender::IpPacketSender; use crate::channel::{init_channel, init_context, Route, RouteKey}; use crate::cipher::Cipher; #[cfg(feature = "server_encrypt")] use crate::cipher::RsaCipher; +use crate::compression::Compressor; use crate::core::Config; use crate::external_route::{AllowExternalRoute, ExternalRoute}; use crate::handle::handshaker::Handshake; @@ -23,16 +24,44 @@ use crate::handle::maintain::PunchReceiver; use crate::handle::recv_data::RecvDataHandler; use crate::handle::{maintain, BaseConfigInfo, ConnectStatus, CurrentDeviceInfo, PeerDeviceInfo}; use crate::nat::NatTest; +#[cfg(feature = "integrated_tun")] use crate::tun_tap_device::tun_create_helper::{DeviceAdapter, TunDeviceHelper}; -use crate::util::{ - Scheduler, SingleU64Adder, StopManager, U64Adder, WatchSingleU64Adder, WatchU64Adder, -}; +use crate::tun_tap_device::vnt_device::DeviceWrite; +use crate::util::limit::TrafficMeterMultiAddress; +use crate::util::{Scheduler, StopManager}; use crate::{nat, VntCallback}; -#[cfg(not(target_os = "android"))] -use crate::{tun_tap_device, DeviceInfo}; #[derive(Clone)] pub struct Vnt { + inner: Arc, +} + +impl Vnt { + #[cfg(feature = "integrated_tun")] + pub fn new(config: Config, callback: Call) -> anyhow::Result { + let inner = Arc::new(VntInner::new(config, callback)?); + Ok(Self { inner }) + } + #[cfg(not(feature = "integrated_tun"))] + pub fn new_device( + config: Config, + callback: Call, + device: Device, + ) -> anyhow::Result { + let inner = Arc::new(VntInner::new_device(config, callback, device)?); + Ok(Self { inner }) + } +} + +impl Deref for Vnt { + type Target = VntInner; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +pub struct VntInner { stop_manager: StopManager, config: Config, current_device: Arc>, @@ -40,14 +69,41 @@ pub struct Vnt { device_list: Arc)>>, context: Arc>>, peer_nat_info_map: Arc>>, - down_count_watcher: WatchU64Adder, - up_count_watcher: WatchSingleU64Adder, client_secret_hash: Option<[u8; 16]>, + compressor: Compressor, + client_cipher: Cipher, + external_route: ExternalRoute, + up_traffic_meter: Option, + down_traffic_meter: Option, } -impl Vnt { +impl VntInner { + #[cfg(feature = "integrated_tun")] pub fn new(config: Config, callback: Call) -> anyhow::Result { - log::info!("config.toml:{:?}", config); + VntInner::new_device0(config, callback, DeviceAdapter::default()) + } + #[cfg(not(feature = "integrated_tun"))] + pub fn new_device( + config: Config, + callback: Call, + device: Device, + ) -> anyhow::Result { + VntInner::new_device0(config, callback, device) + } + fn new_device0( + config: Config, + callback: Call, + device: Device, + ) -> anyhow::Result { + log::info!("config: {:?}", config); + let (up_traffic_meter, down_traffic_meter) = if config.enable_traffic { + ( + Some(TrafficMeterMultiAddress::default()), + Some(TrafficMeterMultiAddress::default()), + ) + } else { + (None, None) + }; //服务端非对称加密 #[cfg(feature = "server_encrypt")] let rsa_cipher: Arc>> = Arc::new(Mutex::new(None)); @@ -66,7 +122,7 @@ impl Vnt { }; //客户端对称加密 let client_cipher = - Cipher::new_password(config.cipher_model, config.password.clone(), finger); + Cipher::new_password(config.cipher_model, config.password.clone(), finger)?; //当前设备信息 let current_device = Arc::new(AtomicCell::new(CurrentDeviceInfo::new0( config.server_address, @@ -84,6 +140,13 @@ impl Vnt { config.device_id.clone(), config.server_address_str.clone(), config.name_servers.clone(), + config.mtu.unwrap_or(1420), + #[cfg(feature = "integrated_tun")] + #[cfg(target_os = "windows")] + config.tap, + #[cfg(feature = "integrated_tun")] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] + config.device_name.clone(), ); // 服务停止管理器 let stop_manager = { @@ -111,9 +174,11 @@ impl Vnt { ports, config.use_channel_type, config.first_latency, - config.tcp, + config.protocol, config.packet_loss_rate, config.packet_delay, + up_traffic_meter.clone(), + down_traffic_meter.clone(), )?; let local_ipv4 = nat::local_ipv4(); let local_ipv6 = nat::local_ipv6(); @@ -128,24 +193,13 @@ impl Vnt { udp_ports, tcp_port, ); - - // pc上先创建虚拟网卡 - #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] - let device = { - log::info!("开始创建tun"); - let device = tun_tap_device::create_device(&config)?; - log::info!("创建tun成功"); - let tun_info = DeviceInfo::new(device.name()?, device.version()?); - log::info!("tun信息{:?}", tun_info); - callback.create_tun(tun_info); - device - }; // 定时器 let scheduler = Scheduler::new(stop_manager.clone())?; let external_route = ExternalRoute::new(config.in_ips.clone()); let out_external_route = AllowExternalRoute::new(config.out_ips.clone()); #[cfg(feature = "ip_proxy")] + #[cfg(feature = "integrated_tun")] let proxy_map = if !config.out_ips.is_empty() && !config.no_proxy { Some(crate::ip_proxy::init_proxy( context.clone(), @@ -159,33 +213,26 @@ impl Vnt { let (punch_sender, punch_receiver) = maintain::punch_channel(); let peer_nat_info_map: Arc>> = Arc::new(RwLock::new(HashMap::with_capacity(16))); - let down_counter = - U64Adder::with_capacity(config.ports.as_ref().map(|v| v.len()).unwrap_or_default() + 8); - let down_count_watcher = down_counter.watch(); let handshake = Handshake::new( #[cfg(feature = "server_encrypt")] rsa_cipher.clone(), ); - let up_counter = SingleU64Adder::new(); - let up_count_watcher = up_counter.watch(); - let tun_helper = TunDeviceHelper::new( - stop_manager.clone(), - context.clone(), - current_device.clone(), - external_route.clone(), - #[cfg(feature = "ip_proxy")] - proxy_map.clone(), - client_cipher.clone(), - server_cipher.clone(), - config.parallel, - up_counter, - device_list.clone(), - config.compressor, - ); - #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] - let device_adapter = DeviceAdapter::new(device.clone()); - #[cfg(target_os = "android")] - let device_adapter = DeviceAdapter::new(tun_helper); + #[cfg(feature = "integrated_tun")] + let tun_device_helper = { + TunDeviceHelper::new( + stop_manager.clone(), + context.clone(), + current_device.clone(), + external_route.clone(), + #[cfg(feature = "ip_proxy")] + proxy_map.clone(), + client_cipher.clone(), + server_cipher.clone(), + device_list.clone(), + config.compressor, + device.clone().into_device_adapter(), + ) + }; let handler = RecvDataHandler::new( #[cfg(feature = "server_encrypt")] @@ -193,7 +240,7 @@ impl Vnt { server_cipher.clone(), client_cipher.clone(), current_device.clone(), - device_adapter, + device, device_list.clone(), config_info.clone(), nat_test.clone(), @@ -203,33 +250,36 @@ impl Vnt { external_route.clone(), out_external_route, #[cfg(feature = "ip_proxy")] + #[cfg(feature = "integrated_tun")] proxy_map.clone(), - down_counter, handshake.clone(), + #[cfg(feature = "integrated_tun")] + tun_device_helper, ); //初始化网络数据通道 - let (udp_socket_sender, tcp_socket_sender) = + let (udp_socket_sender, connect_util) = init_channel(tcp_listener, context.clone(), stop_manager.clone(), handler)?; // 打洞逻辑 let punch = Punch::new( context.clone(), config.punch_model, - config.tcp, - tcp_socket_sender.clone(), + config.protocol.is_base_tcp(), + connect_util.clone(), external_route.clone(), nat_test.clone(), + current_device.clone(), ); - #[cfg(not(target_os = "android"))] - tun_helper.start(device)?; + // #[cfg(not(target_os = "android"))] + // tun_helper.start(device)?; maintain::idle_gateway( &scheduler, context.clone(), current_device.clone(), config_info.clone(), - tcp_socket_sender.clone(), + connect_util.clone(), callback.clone(), 0, handshake, @@ -238,8 +288,6 @@ impl Vnt { let context = context.clone(); let nat_test = nat_test.clone(); let device_list = device_list.clone(); - let down_count_watcher = down_count_watcher.clone(); - let up_count_watcher = up_count_watcher.clone(); let config_info = config_info.clone(); let current_device = current_device.clone(); if !config.use_channel_type.is_only_relay() { @@ -251,6 +299,7 @@ impl Vnt { udp_socket_sender, ); } + let client_cipher = client_cipher.clone(); //延迟启动 scheduler.timeout(Duration::from_secs(3), move |scheduler| { start( @@ -265,12 +314,10 @@ impl Vnt { config_info, punch, callback, - down_count_watcher, - up_count_watcher, ); }); } - + let compressor = config.compressor; Ok(Self { stop_manager, config, @@ -279,9 +326,12 @@ impl Vnt { device_list, context: Arc::new(Mutex::new(Some(context))), peer_nat_info_map, - down_count_watcher, - up_count_watcher, client_secret_hash: config_info.client_secret_hash, + compressor, + client_cipher, + external_route, + up_traffic_meter, + down_traffic_meter, }) } } @@ -298,8 +348,6 @@ pub fn start( config_info: BaseConfigInfo, punch: Punch, callback: Call, - down_count_watcher: WatchU64Adder, - up_count_watcher: WatchSingleU64Adder, ) { // 定时心跳 maintain::heartbeat( @@ -353,16 +401,10 @@ pub fn start( punch, ); } - maintain::up_status( - scheduler, - context.clone(), - current_device.clone(), - down_count_watcher, - up_count_watcher, - ) + maintain::up_status(scheduler, context.clone(), current_device.clone()) } -impl Vnt { +impl VntInner { pub fn name(&self) -> &str { &self.config.name } @@ -378,6 +420,9 @@ impl Vnt { pub fn current_device(&self) -> CurrentDeviceInfo { self.current_device.load() } + pub fn current_device_info(&self) -> Arc> { + self.current_device.clone() + } pub fn peer_nat_info(&self, ip: &Ipv4Addr) -> Option { self.peer_nat_info_map.read().get(ip).cloned() } @@ -414,16 +459,39 @@ impl Vnt { } } pub fn up_stream(&self) -> u64 { - self.up_count_watcher.get() + self.up_traffic_meter.as_ref().map_or(0, |v| v.total()) + } + pub fn up_stream_all(&self) -> Option<(u64, HashMap)> { + self.up_traffic_meter.as_ref().map(|v| v.get_all()) + } + pub fn up_stream_history(&self) -> Option<(u64, HashMap)>)> { + self.up_traffic_meter.as_ref().map(|v| v.get_all_history()) } pub fn down_stream(&self) -> u64 { - self.down_count_watcher.get() + self.down_traffic_meter.as_ref().map_or(0, |v| v.total()) + } + pub fn down_stream_all(&self) -> Option<(u64, HashMap)> { + self.down_traffic_meter.as_ref().map(|v| v.get_all()) + } + pub fn down_stream_history(&self) -> Option<(u64, HashMap)>)> { + self.down_traffic_meter + .as_ref() + .map(|v| v.get_all_history()) } pub fn stop(&self) { //退出协助回收资源 let _ = self.context.lock().take(); self.stop_manager.stop() } + pub fn is_stopped(&self) -> bool { + self.stop_manager.is_stopped() + } + pub fn add_stop_listener(&self, name: String, f: F) -> anyhow::Result + where + F: FnOnce() + Send + 'static, + { + self.stop_manager.add_listener(name, f) + } pub fn wait(&self) { self.stop_manager.wait() } @@ -433,4 +501,23 @@ impl Vnt { pub fn config(&self) -> &Config { &self.config } + pub fn ipv4_packet_sender(&self) -> Option { + if let Some(c) = self.context.lock().as_ref() { + Some(IpPacketSender::new( + c.clone(), + self.current_device.clone(), + self.compressor.clone(), + self.client_cipher.clone(), + self.external_route.clone(), + )) + } else { + None + } + } +} + +impl Drop for VntInner { + fn drop(&mut self) { + self.stop(); + } } diff --git a/vnt/src/core/mod.rs b/vnt/src/core/mod.rs index 70fd2350..44238fac 100644 --- a/vnt/src/core/mod.rs +++ b/vnt/src/core/mod.rs @@ -5,7 +5,7 @@ use std::str::FromStr; pub use conn::Vnt; use crate::channel::punch::PunchModel; -use crate::channel::UseChannelType; +use crate::channel::{ConnectProtocol, UseChannelType}; use crate::cipher::CipherModel; use crate::compression::Compressor; use crate::util::{address_choose, dns_query_all}; @@ -14,6 +14,7 @@ mod conn; #[derive(Clone, Debug)] pub struct Config { + #[cfg(feature = "integrated_tun")] #[cfg(target_os = "windows")] pub tap: bool, pub token: String, @@ -27,18 +28,19 @@ pub struct Config { pub out_ips: Vec<(u32, u32)>, pub password: Option, pub mtu: Option, - pub tcp: bool, + pub protocol: ConnectProtocol, pub ip: Option, #[cfg(feature = "ip_proxy")] + #[cfg(feature = "integrated_tun")] pub no_proxy: bool, pub server_encrypt: bool, - pub parallel: usize, pub cipher_model: CipherModel, pub finger: bool, pub punch_model: PunchModel, pub ports: Option>, pub first_latency: bool, - #[cfg(not(target_os = "android"))] + #[cfg(feature = "integrated_tun")] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] pub device_name: Option, pub use_channel_type: UseChannelType, //控制丢包率 @@ -48,11 +50,14 @@ pub struct Config { #[cfg(feature = "port_mapping")] pub port_mapping_list: Vec<(bool, SocketAddr, String)>, pub compressor: Compressor, + pub enable_traffic: bool, } impl Config { pub fn new( - #[cfg(target_os = "windows")] tap: bool, + #[cfg(feature = "integrated_tun")] + #[cfg(target_os = "windows")] + tap: bool, token: String, device_id: String, name: String, @@ -63,23 +68,26 @@ impl Config { out_ips: Vec<(u32, u32)>, password: Option, mtu: Option, - tcp: bool, ip: Option, - #[cfg(feature = "ip_proxy")] no_proxy: bool, + #[cfg(feature = "integrated_tun")] + #[cfg(feature = "ip_proxy")] + no_proxy: bool, server_encrypt: bool, - parallel: usize, cipher_model: CipherModel, finger: bool, punch_model: PunchModel, ports: Option>, first_latency: bool, - #[cfg(not(target_os = "android"))] device_name: Option, + #[cfg(feature = "integrated_tun")] + #[cfg(not(target_os = "android"))] + device_name: Option, use_channel_type: UseChannelType, packet_loss_rate: Option, packet_delay: u32, // 例如 [udp:127.0.0.1:80->10.26.0.10:8080,tcp:127.0.0.1:80->10.26.0.10:8080] #[cfg(feature = "port_mapping")] port_mapping_list: Vec, compressor: Compressor, + enable_traffic: bool, ) -> anyhow::Result { for x in stun_server.iter_mut() { if !x.contains(":") { @@ -102,8 +110,33 @@ impl Config { if name.is_empty() || name.len() > 128 { return Err(anyhow!("name too long")); } - let server_address = - address_choose(dns_query_all(&server_address_str, name_servers.clone())?)?; + let mut server_address_str = server_address_str.to_lowercase(); + let mut _query_dns = true; + let mut protocol = ConnectProtocol::UDP; + if server_address_str.starts_with("ws://") { + #[cfg(not(feature = "ws"))] + Err(anyhow!("Ws not supported"))?; + protocol = ConnectProtocol::WS; + _query_dns = false; + } + if server_address_str.starts_with("wss://") { + #[cfg(not(feature = "wss"))] + Err(anyhow!("Wss not supported"))?; + protocol = ConnectProtocol::WSS; + _query_dns = false; + } + + let mut server_address = "0.0.0.0:0".parse().unwrap(); + if _query_dns { + if let Some(s) = server_address_str.strip_prefix("udp://") { + server_address_str = s.to_string(); + } else if let Some(s) = server_address_str.strip_prefix("tcp://") { + server_address_str = s.to_string(); + protocol = ConnectProtocol::TCP; + } + server_address = + address_choose(dns_query_all(&server_address_str, name_servers.clone())?)?; + } #[cfg(feature = "port_mapping")] let port_mapping_list = crate::port_mapping::convert(port_mapping_list)?; @@ -112,6 +145,7 @@ impl Config { } in_ips.sort_by(|(dest1, _, _), (dest2, _, _)| dest2.cmp(dest1)); Ok(Self { + #[cfg(feature = "integrated_tun")] #[cfg(target_os = "windows")] tap, token, @@ -125,17 +159,18 @@ impl Config { out_ips, password, mtu, - tcp, + protocol, ip, #[cfg(feature = "ip_proxy")] + #[cfg(feature = "integrated_tun")] no_proxy, server_encrypt, - parallel, cipher_model, finger, punch_model, ports, first_latency, + #[cfg(feature = "integrated_tun")] #[cfg(not(target_os = "android"))] device_name, use_channel_type, @@ -144,6 +179,7 @@ impl Config { #[cfg(feature = "port_mapping")] port_mapping_list, compressor, + enable_traffic, }) } } diff --git a/vnt/src/handle/callback.rs b/vnt/src/handle/callback.rs index 8e7219cc..7d33f61f 100644 --- a/vnt/src/handle/callback.rs +++ b/vnt/src/handle/callback.rs @@ -189,9 +189,16 @@ impl Into for ErrorType { } } -#[cfg(target_os = "android")] -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct DeviceConfig { + #[cfg(feature = "integrated_tun")] + #[cfg(target_os = "windows")] + pub tap: bool, + #[cfg(feature = "integrated_tun")] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] + pub device_name: Option, + //虚拟网卡mtu值 + pub mtu: u32, //本机虚拟IP pub virtual_ip: Ipv4Addr, //子网掩码 @@ -204,9 +211,15 @@ pub struct DeviceConfig { pub external_route: Vec<(Ipv4Addr, Ipv4Addr)>, } -#[cfg(target_os = "android")] impl DeviceConfig { pub fn new( + #[cfg(feature = "integrated_tun")] + #[cfg(target_os = "windows")] + tap: bool, + #[cfg(feature = "integrated_tun")] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] + device_name: Option, + mtu: u32, virtual_ip: Ipv4Addr, virtual_netmask: Ipv4Addr, virtual_gateway: Ipv4Addr, @@ -214,6 +227,13 @@ impl DeviceConfig { external_route: Vec<(Ipv4Addr, Ipv4Addr)>, ) -> Self { Self { + #[cfg(feature = "integrated_tun")] + #[cfg(target_os = "windows")] + tap, + #[cfg(feature = "integrated_tun")] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] + device_name, + mtu, virtual_ip, virtual_netmask, virtual_gateway, @@ -223,7 +243,6 @@ impl DeviceConfig { } } -#[cfg(target_os = "android")] impl Display for DeviceConfig { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str(&format!( @@ -272,6 +291,7 @@ pub trait VntCallback: Clone + Send + Sync + 'static { /// 创建网卡的信息 #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] + #[cfg(feature = "integrated_tun")] fn create_tun(&self, _info: DeviceInfo) {} /// 连接 fn connect(&self, _info: ConnectInfo) {} @@ -283,8 +303,11 @@ pub trait VntCallback: Clone + Send + Sync + 'static { fn register(&self, _info: RegisterInfo) -> bool { true } + #[cfg(not(feature = "integrated_tun"))] + fn create_device(&self, _info: DeviceConfig) {} #[cfg(target_os = "android")] - fn generate_tun(&self, _info: DeviceConfig) -> u32 { + #[cfg(feature = "integrated_tun")] + fn generate_tun(&self, _info: DeviceConfig) -> usize { 0 } fn peer_client_list(&self, _info: Vec) {} diff --git a/vnt/src/handle/handshaker.rs b/vnt/src/handle/handshaker.rs index 9ee3493e..bd8a401e 100644 --- a/vnt/src/handle/handshaker.rs +++ b/vnt/src/handle/handshaker.rs @@ -19,13 +19,6 @@ use crate::proto::message::SecretHandshakeRequest; use crate::protocol::body::RSA_ENCRYPTION_RESERVED; use crate::protocol::{service_packet, NetPacket, Protocol, MAX_TTL}; -pub enum HandshakeEnum { - NotSecret, - KeyError, - Timeout, - ServerError(String), - Other(String), -} #[derive(Clone)] pub struct Handshake { time: Arc>, @@ -37,7 +30,11 @@ impl Handshake { #[cfg(feature = "server_encrypt")] rsa_cipher: Arc>>, ) -> Self { Handshake { - time: Arc::new(AtomicCell::new(Instant::now() - Duration::from_secs(60))), + time: Arc::new(AtomicCell::new( + Instant::now() + .checked_sub(Duration::from_secs(60)) + .unwrap_or(Instant::now()), + )), #[cfg(feature = "server_encrypt")] rsa_cipher, } @@ -50,7 +47,7 @@ impl Handshake { } let request_packet = self.handshake_request_packet(secret)?; log::info!("发送握手请求,secret={},{:?}", secret, addr); - context.send_default(request_packet.buffer(), addr)?; + context.send_default(&request_packet, addr)?; self.time.store(Instant::now()); Ok(()) } diff --git a/vnt/src/handle/maintain/addr_request.rs b/vnt/src/handle/maintain/addr_request.rs index dc66f64f..1030e5c3 100644 --- a/vnt/src/handle/maintain/addr_request.rs +++ b/vnt/src/handle/maintain/addr_request.rs @@ -40,7 +40,6 @@ fn pub_address_request( ) { let channel_num = context.channel_num(); let index = count % channel_num; - let mut time = if index == channel_num - 1 { 19 } else { 1 }; if let Err(e) = addr_request0( &context, ¤t_device_info, @@ -51,12 +50,20 @@ fn pub_address_request( log::warn!("{:?}", e); } let nat_info = nat_test.nat_info(); - if nat_info.nat_type == NatType::Symmetric { + let time = if !nat_info.public_ports.contains(&0) && !nat_info.public_ips.is_empty() { //对称网络探测端口没啥作用,把频率放低,(锥形网络也只在打洞前需要探测端口,后续可以改改) - if !nat_info.public_ports.contains(&0) && !nat_info.public_ips.is_empty() { - time = 600; + if nat_info.nat_type == NatType::Symmetric { + 600 + } else { + if index == channel_num - 1 { + 19 + } else { + 7 + } } - } + } else { + 3 + }; let rs = scheduler.timeout(Duration::from_secs(time), move |s| { pub_address_request( @@ -85,7 +92,7 @@ fn addr_request0( return Ok(()); } - if current_dev.connect_server.is_ipv4() && !context.is_main_tcp() { + if current_dev.connect_server.is_ipv4() && !context.main_protocol().is_base_tcp() { // 如果连接的是ipv4服务,则探测公网端口 let gateway_ip = current_dev.virtual_gateway; let src_ip = current_dev.virtual_ip; diff --git a/vnt/src/handle/maintain/heartbeat.rs b/vnt/src/handle/maintain/heartbeat.rs index e5b4a943..f18ac8db 100644 --- a/vnt/src/handle/maintain/heartbeat.rs +++ b/vnt/src/handle/maintain/heartbeat.rs @@ -59,8 +59,7 @@ fn heartbeat0( let mut is_send_gateway = false; match heartbeat_packet_server(device_list, server_cipher, src_ip, gateway_ip) { Ok(net_packet) => { - if let Err(e) = context.send_default(net_packet.buffer(), current_device.connect_server) - { + if let Err(e) = context.send_default(&net_packet, current_device.connect_server) { log::warn!("heartbeat err={:?}", e) } else { is_send_gateway = true @@ -88,7 +87,7 @@ fn heartbeat0( } }; for route in routes { - if let Err(e) = context.send_by_key(net_packet.buffer(), route.route_key()) { + if let Err(e) = context.send_by_key(&net_packet, route.route_key()) { log::warn!("heartbeat err={:?}", e) } } @@ -113,8 +112,7 @@ fn heartbeat0( continue; } }; - if let Err(e) = context.send_default(net_packet.buffer(), current_device.connect_server) - { + if let Err(e) = context.send_default(&net_packet, current_device.connect_server) { log::error!("heartbeat_packet send_default err={:?}", e); } } @@ -195,7 +193,7 @@ fn client_relay0( if current_device.is_gateway(ip) { continue; } - if let Err(e) = context.send_by_key(client_packet.buffer(), route.route_key()) { + if let Err(e) = context.send_by_key(&client_packet, route.route_key()) { log::error!("{:?}", e); } if index >= 2 { diff --git a/vnt/src/handle/maintain/idle.rs b/vnt/src/handle/maintain/idle.rs index 2589f78e..d610018e 100644 --- a/vnt/src/handle/maintain/idle.rs +++ b/vnt/src/handle/maintain/idle.rs @@ -1,14 +1,13 @@ use std::io; -use std::net::SocketAddr; use std::sync::Arc; use std::time::Duration; use crossbeam_utils::atomic::AtomicCell; -use mio::net::TcpStream; use crate::channel::context::ChannelContext; use crate::channel::idle::{Idle, IdleType}; -use crate::channel::sender::AcceptSocketSender; +use crate::channel::sender::ConnectUtil; +use crate::channel::ConnectProtocol; use crate::handle::callback::{ConnectInfo, ErrorType}; use crate::handle::handshaker::Handshake; use crate::handle::{BaseConfigInfo, ConnectStatus, CurrentDeviceInfo}; @@ -36,7 +35,7 @@ pub fn idle_gateway( context: ChannelContext, current_device_info: Arc>, config: BaseConfigInfo, - tcp_socket_sender: AcceptSocketSender<(TcpStream, SocketAddr, Option>)>, + connect_util: ConnectUtil, call: Call, mut connect_count: usize, handshake: Handshake, @@ -45,7 +44,7 @@ pub fn idle_gateway( &context, ¤t_device_info, &config, - &tcp_socket_sender, + &connect_util, &call, &mut connect_count, &handshake, @@ -56,7 +55,7 @@ pub fn idle_gateway( context, current_device_info, config, - tcp_socket_sender, + connect_util, call, connect_count, handshake, @@ -71,7 +70,7 @@ fn idle_gateway0( context: &ChannelContext, current_device: &AtomicCell, config: &BaseConfigInfo, - tcp_socket_sender: &AcceptSocketSender<(TcpStream, SocketAddr, Option>)>, + connect_util: &ConnectUtil, call: &Call, connect_count: &mut usize, handshake: &Handshake, @@ -80,7 +79,7 @@ fn idle_gateway0( context, current_device, config, - tcp_socket_sender, + connect_util, call, connect_count, handshake, @@ -120,7 +119,7 @@ fn check_gateway_channel( context: &ChannelContext, current_device_info: &AtomicCell, config: &BaseConfigInfo, - tcp_socket_sender: &AcceptSocketSender<(TcpStream, SocketAddr, Option>)>, + connect_util: &ConnectUtil, call: &Call, count: &mut usize, handshake: &Handshake, @@ -128,28 +127,29 @@ fn check_gateway_channel( let mut current_device = current_device_info.load(); if current_device.status.offline() { *count += 1; - // 探测服务器地址 - current_device = domain_request0(current_device_info, config); + let connect_protocol = context.main_protocol(); + if connect_protocol.is_transport() { + // 传输层的协议需要探测服务器地址 + current_device = domain_request0(current_device_info, config); + } //需要重连 call.connect(ConnectInfo::new(*count, current_device.connect_server)); log::info!("发送握手请求,{:?}", config); if let Err(e) = handshake.send(context, config.server_secret, current_device.connect_server) { log::warn!("{:?}", e); - if context.is_main_tcp() { - let request_packet = handshake.handshake_request_packet(config.server_secret)?; - //tcp需要重连 - let tcp_stream = std::net::TcpStream::connect_timeout( - ¤t_device.connect_server, - Duration::from_secs(5), - )?; - tcp_stream.set_nonblocking(true)?; - if let Err(e) = tcp_socket_sender.try_add_socket(( - TcpStream::from_std(tcp_stream), - current_device.connect_server, - Some(request_packet.into_buffer()), - )) { - log::warn!("{:?}", e) + let request_packet = handshake.handshake_request_packet(config.server_secret)?; + match connect_protocol { + ConnectProtocol::UDP => {} + ConnectProtocol::TCP => { + connect_util.try_connect_tcp( + request_packet.into_buffer(), + current_device.connect_server, + ); + } + ConnectProtocol::WS | ConnectProtocol::WSS => { + connect_util + .try_connect_ws(request_packet.into_buffer(), config.server_addr.clone()); } } } diff --git a/vnt/src/handle/maintain/punch.rs b/vnt/src/handle/maintain/punch.rs index 8c00f459..69c5ff8a 100644 --- a/vnt/src/handle/maintain/punch.rs +++ b/vnt/src/handle/maintain/punch.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; use std::net::Ipv4Addr; +use std::ops::{Div, Mul}; use std::sync::mpsc::{sync_channel, Receiver, SyncSender}; use std::sync::Arc; use std::thread; @@ -158,7 +159,7 @@ fn punch_start( log::error!("{:?}", e); continue; } - if let Err(e) = punch.punch(packet.buffer(), peer_ip, nat_info, count < 2) { + if let Err(e) = punch.punch(packet.buffer(), peer_ip, nat_info, count < 2, count) { log::warn!("{:?}", e) } } @@ -190,7 +191,7 @@ fn punch_request( ) { log::warn!("{:?}", e) } - let sleep_time = [5, 6, 7]; + let sleep_time = [6, 7]; Duration::from_secs(sleep_time[count % sleep_time.len()]) } else { Duration::from_secs(5) @@ -249,9 +250,11 @@ fn punch0( .lock() .get(&info.virtual_ip) .cloned() - .unwrap_or(0); + .unwrap_or(0) + .mul(2) + .div(3); let p2p_num = context.route_table.p2p_num(&info.virtual_ip); - let mut max_punch_interval = 70; + let mut max_punch_interval = 50; if p2p_num > 0 { if punch_count == 0 { continue; @@ -286,7 +289,7 @@ fn punch0( punch_count, total_count, ); - context.send_default(packet.buffer(), current_device.connect_server)?; + context.send_default(&packet, current_device.connect_server)?; break; } } diff --git a/vnt/src/handle/maintain/re_nat_type.rs b/vnt/src/handle/maintain/re_nat_type.rs index 975f4b20..e8eca7dd 100644 --- a/vnt/src/handle/maintain/re_nat_type.rs +++ b/vnt/src/handle/maintain/re_nat_type.rs @@ -42,6 +42,9 @@ fn retrieve_nat_type0( log::warn!("nat re_test {:?}", e); } }; + #[cfg(feature = "upnp")] + nat_test.reset_upnp(); + log::info!("刷新nat成功") } }) .expect("natTest"); diff --git a/vnt/src/handle/maintain/up_status.rs b/vnt/src/handle/maintain/up_status.rs index 74d554b7..25b51e78 100644 --- a/vnt/src/handle/maintain/up_status.rs +++ b/vnt/src/handle/maintain/up_status.rs @@ -3,7 +3,7 @@ use crate::handle::CurrentDeviceInfo; use crate::proto::message::{ClientStatusInfo, PunchNatType, RouteItem}; use crate::protocol::body::ENCRYPTION_RESERVED; use crate::protocol::{service_packet, NetPacket, Protocol, HEAD_LEN, MAX_TTL}; -use crate::util::{Scheduler, WatchSingleU64Adder, WatchU64Adder}; +use crate::util::Scheduler; use crossbeam_utils::atomic::AtomicCell; use protobuf::Message; use std::io; @@ -15,17 +15,9 @@ pub fn up_status( scheduler: &Scheduler, context: ChannelContext, current_device_info: Arc>, - down_count_watcher: WatchU64Adder, - up_count_watcher: WatchSingleU64Adder, ) { let _ = scheduler.timeout(Duration::from_secs(60), move |x| { - up_status0( - x, - context, - current_device_info, - down_count_watcher, - up_count_watcher, - ) + up_status0(x, context, current_device_info) }); } @@ -33,25 +25,12 @@ fn up_status0( scheduler: &Scheduler, context: ChannelContext, current_device_info: Arc>, - down_count_watcher: WatchU64Adder, - up_count_watcher: WatchSingleU64Adder, ) { - if let Err(e) = send_up_status_packet( - &context, - ¤t_device_info, - &down_count_watcher, - &up_count_watcher, - ) { + if let Err(e) = send_up_status_packet(&context, ¤t_device_info) { log::warn!("{:?}", e) } let rs = scheduler.timeout(Duration::from_secs(10 * 60), move |x| { - up_status0( - x, - context, - current_device_info, - down_count_watcher, - up_count_watcher, - ) + up_status0(x, context, current_device_info) }); if !rs { log::info!("定时任务停止"); @@ -61,8 +40,6 @@ fn up_status0( fn send_up_status_packet( context: &ChannelContext, current_device_info: &AtomicCell, - down_count_watcher: &WatchU64Adder, - up_count_watcher: &WatchSingleU64Adder, ) -> io::Result<()> { let device_info = current_device_info.load(); if device_info.status.offline() { @@ -79,8 +56,8 @@ fn send_up_status_packet( item.next_ip = ip.into(); message.p2p_list.push(item); } - message.up_stream = up_count_watcher.get(); - message.down_stream = down_count_watcher.get(); + message.up_stream = context.up_traffic_meter.as_ref().map_or(0, |v| v.total()); + message.down_stream = context.down_traffic_meter.as_ref().map_or(0, |v| v.total()); message.nat_type = protobuf::EnumOrUnknown::new(if context.is_cone() { PunchNatType::Cone } else { @@ -99,6 +76,6 @@ fn send_up_status_packet( net_packet.set_source(device_info.virtual_ip); net_packet.set_destination(device_info.virtual_gateway); net_packet.set_payload(&buf)?; - context.send_default(net_packet.buffer(), device_info.connect_server)?; + context.send_default(&net_packet, device_info.connect_server)?; Ok(()) } diff --git a/vnt/src/handle/mod.rs b/vnt/src/handle/mod.rs index e59631d0..e39305fa 100644 --- a/vnt/src/handle/mod.rs +++ b/vnt/src/handle/mod.rs @@ -1,5 +1,5 @@ use crossbeam_utils::atomic::AtomicCell; -use std::net::{Ipv4Addr, SocketAddr}; +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; pub mod callback; mod extension; @@ -7,6 +7,7 @@ pub mod handshaker; pub mod maintain; pub mod recv_data; pub mod registrar; +#[cfg(feature = "integrated_tun")] pub mod tun_tap; const SELF_IP: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 2); @@ -21,12 +22,6 @@ pub fn now_time() -> u64 { } } -/// 是否在一个网段 -fn check_dest(dest: Ipv4Addr, virtual_netmask: Ipv4Addr, virtual_network: Ipv4Addr) -> bool { - u32::from_be_bytes(dest.octets()) & u32::from_be_bytes(virtual_netmask.octets()) - == u32::from_be_bytes(virtual_network.octets()) -} - #[derive(Clone, Debug, Eq, PartialEq)] pub struct PeerDeviceInfo { pub virtual_ip: Ipv4Addr, @@ -64,6 +59,13 @@ pub struct BaseConfigInfo { pub device_id: String, pub server_addr: String, pub name_servers: Vec, + pub mtu: u32, + #[cfg(feature = "integrated_tun")] + #[cfg(target_os = "windows")] + pub tap: bool, + #[cfg(feature = "integrated_tun")] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] + pub device_name: Option, } impl BaseConfigInfo { @@ -76,6 +78,13 @@ impl BaseConfigInfo { device_id: String, server_addr: String, name_servers: Vec, + mtu: u32, + #[cfg(feature = "integrated_tun")] + #[cfg(target_os = "windows")] + tap: bool, + #[cfg(feature = "integrated_tun")] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] + device_name: Option, ) -> Self { Self { name, @@ -86,6 +95,13 @@ impl BaseConfigInfo { device_id, server_addr, name_servers, + mtu, + #[cfg(feature = "integrated_tun")] + #[cfg(target_os = "windows")] + tap, + #[cfg(feature = "integrated_tun")] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] + device_name, } } } @@ -213,9 +229,24 @@ impl CurrentDeviceInfo { pub fn virtual_gateway(&self) -> Ipv4Addr { self.virtual_gateway } + #[inline] pub fn is_gateway(&self, ip: &Ipv4Addr) -> bool { &self.virtual_gateway == ip || ip == &GATEWAY_IP } + #[inline] + pub fn not_in_network(&self, ip: Ipv4Addr) -> bool { + u32::from(ip) & u32::from(self.virtual_netmask) != u32::from(self.virtual_network) + } + pub fn is_server_addr(&self, addr: SocketAddr) -> bool { + if self.connect_server == addr { + return true; + } + let f = |ip: IpAddr| match ip { + IpAddr::V4(v4) => Some(v4), + IpAddr::V6(v6) => v6.to_ipv4(), + }; + addr.port() == self.connect_server.port() && f(addr.ip()) == f(self.connect_server.ip()) + } } pub fn change_status( current_device: &AtomicCell, diff --git a/vnt/src/handle/recv_data/client.rs b/vnt/src/handle/recv_data/client.rs index 5d619d64..54a7b932 100644 --- a/vnt/src/handle/recv_data/client.rs +++ b/vnt/src/handle/recv_data/client.rs @@ -9,8 +9,6 @@ use protobuf::Message; use packet::icmp::{icmp, Kind}; use packet::ip::ipv4; use packet::ip::ipv4::packet::IpV4Packet; -#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] -use tun::device::IFace; use crate::channel::context::ChannelContext; use crate::channel::punch::NatInfo; @@ -30,30 +28,33 @@ use crate::protocol::control_packet::ControlPacket; use crate::protocol::{ control_packet, ip_turn_packet, other_turn_packet, NetPacket, Protocol, MAX_TTL, }; -use crate::tun_tap_device::tun_create_helper::DeviceAdapter; +use crate::tun_tap_device::vnt_device::DeviceWrite; /// 处理来源于客户端的包 #[derive(Clone)] -pub struct ClientPacketHandler { - device: DeviceAdapter, +pub struct ClientPacketHandler { + device: Device, client_cipher: Cipher, punch_sender: PunchSender, peer_nat_info_map: Arc>>, nat_test: NatTest, route: AllowExternalRoute, #[cfg(feature = "ip_proxy")] + #[cfg(feature = "integrated_tun")] ip_proxy_map: Option, } -impl ClientPacketHandler { +impl ClientPacketHandler { pub fn new( - device: DeviceAdapter, + device: Device, client_cipher: Cipher, punch_sender: PunchSender, peer_nat_info_map: Arc>>, nat_test: NatTest, route: AllowExternalRoute, - #[cfg(feature = "ip_proxy")] ip_proxy_map: Option, + #[cfg(feature = "integrated_tun")] + #[cfg(feature = "ip_proxy")] + ip_proxy_map: Option, ) -> Self { Self { device, @@ -62,13 +63,14 @@ impl ClientPacketHandler { peer_nat_info_map, nat_test, route, + #[cfg(feature = "integrated_tun")] #[cfg(feature = "ip_proxy")] ip_proxy_map, } } } -impl PacketHandler for ClientPacketHandler { +impl PacketHandler for ClientPacketHandler { fn handle( &self, mut net_packet: NetPacket<&mut [u8]>, @@ -110,7 +112,7 @@ impl PacketHandler for ClientPacketHandler { } } -impl ClientPacketHandler { +impl ClientPacketHandler { fn ip_turn( &self, mut net_packet: NetPacket<&mut [u8]>, @@ -138,7 +140,7 @@ impl ClientPacketHandler { net_packet.set_destination(source); //不管加不加密,和接收到的数据长度都一致 self.client_cipher.encrypt_ipv4(&mut net_packet)?; - context.send_by_key(net_packet.buffer(), route_key)?; + context.send_by_key(&net_packet, route_key)?; return Ok(()); } } @@ -183,6 +185,7 @@ impl ClientPacketHandler { _ => {} } #[cfg(feature = "ip_proxy")] + #[cfg(feature = "integrated_tun")] if let Some(ip_proxy_map) = &self.ip_proxy_map { if ip_proxy_map.recv_handle(&mut ipv4, source, destination)? { return Ok(()); @@ -214,7 +217,7 @@ impl ClientPacketHandler { net_packet.set_destination(source); net_packet.first_set_ttl(MAX_TTL); self.client_cipher.encrypt_ipv4(&mut net_packet)?; - context.send_by_key(net_packet.buffer(), route_key)?; + context.send_by_key(&net_packet, route_key)?; let route = Route::from_default_rt(route_key, metric); context.route_table.add_route_if_absent(source, route); } @@ -235,7 +238,7 @@ impl ClientPacketHandler { //忽略掉来源于自己的包 if self .nat_test - .is_local_address(route_key.is_tcp(), route_key.addr) + .is_local_address(route_key.protocol().is_base_tcp(), route_key.addr) { return Ok(()); } @@ -246,7 +249,7 @@ impl ClientPacketHandler { net_packet.set_destination(source); net_packet.first_set_ttl(1); self.client_cipher.encrypt_ipv4(&mut net_packet)?; - context.send_by_key(net_packet.buffer(), route_key)?; + context.send_by_key(&net_packet, route_key)?; // 收到PunchRequest就添加路由,会导致单向通信的问题,删掉试试 // let route = Route::from_default_rt(route_key, 1); // context.route_table.add_route_if_absent(source, route); @@ -258,7 +261,7 @@ impl ClientPacketHandler { } if self .nat_test - .is_local_address(route_key.is_tcp(), route_key.addr) + .is_local_address(route_key.protocol().is_base_tcp(), route_key.addr) { return Ok(()); } @@ -278,7 +281,7 @@ impl ClientPacketHandler { addr_packet.set_ipv4(ipv4); addr_packet.set_port(route_key.addr.port()); self.client_cipher.encrypt_ipv4(&mut packet)?; - context.send_by_key(packet.buffer(), route_key)?; + context.send_by_key(&packet, route_key)?; } std::net::IpAddr::V6(_) => {} }, @@ -374,7 +377,7 @@ impl ClientPacketHandler { punch_packet.set_payload(&bytes)?; self.client_cipher.encrypt_ipv4(&mut punch_packet)?; if self.punch_sender.send(true, source, peer_nat_info) { - context.send_by_key(punch_packet.buffer(), route_key)?; + context.send_by_key(&punch_packet, route_key)?; } } else { self.punch_sender.send(false, source, peer_nat_info); diff --git a/vnt/src/handle/recv_data/mod.rs b/vnt/src/handle/recv_data/mod.rs index bb14c208..e2bd5d6d 100644 --- a/vnt/src/handle/recv_data/mod.rs +++ b/vnt/src/handle/recv_data/mod.rs @@ -24,34 +24,35 @@ use crate::handle::{BaseConfigInfo, CurrentDeviceInfo, PeerDeviceInfo, SELF_IP}; #[cfg(feature = "ip_proxy")] use crate::ip_proxy::IpProxyMap; use crate::nat::NatTest; -use crate::protocol::NetPacket; -use crate::tun_tap_device::tun_create_helper::DeviceAdapter; -use crate::util::U64Adder; +use crate::protocol::{NetPacket, HEAD_LEN}; +use crate::tun_tap_device::vnt_device::DeviceWrite; mod client; mod server; mod turn; #[derive(Clone)] -pub struct RecvDataHandler { +pub struct RecvDataHandler { current_device: Arc>, turn: TurnPacketHandler, - client: ClientPacketHandler, - server: ServerPacketHandler, - counter: U64Adder, + client: ClientPacketHandler, + server: ServerPacketHandler, nat_test: NatTest, } -impl RecvChannelHandler for RecvDataHandler { +impl RecvChannelHandler for RecvDataHandler { fn handle( - &mut self, + &self, buf: &mut [u8], extend: &mut [u8], route_key: RouteKey, context: &ChannelContext, ) { + if buf.len() < HEAD_LEN { + return; + } //判断stun响应包 - if !route_key.is_tcp() { + if route_key.protocol().is_udp() { if let Ok(rs) = self .nat_test .recv_data(route_key.index(), route_key.addr, buf) @@ -72,13 +73,13 @@ impl RecvChannelHandler for RecvDataHandler { } } -impl RecvDataHandler { +impl RecvDataHandler { pub fn new( #[cfg(feature = "server_encrypt")] rsa_cipher: Arc>>, server_cipher: Cipher, client_cipher: Cipher, current_device: Arc>, - device: DeviceAdapter, + device: Device, device_list: Arc)>>, config_info: BaseConfigInfo, nat_test: NatTest, @@ -87,9 +88,12 @@ impl RecvDataHandler { peer_nat_info_map: Arc>>, external_route: ExternalRoute, route: AllowExternalRoute, - #[cfg(feature = "ip_proxy")] ip_proxy_map: Option, - counter: U64Adder, + #[cfg(feature = "integrated_tun")] + #[cfg(feature = "ip_proxy")] + ip_proxy_map: Option, handshake: Handshake, + #[cfg(feature = "integrated_tun")] + tun_device_helper: crate::tun_tap_device::tun_create_helper::TunDeviceHelper, ) -> Self { let server = ServerPacketHandler::new( #[cfg(feature = "server_encrypt")] @@ -103,6 +107,8 @@ impl RecvDataHandler { callback, external_route.clone(), handshake, + #[cfg(feature = "integrated_tun")] + tun_device_helper, ); let client = ClientPacketHandler::new( device.clone(), @@ -111,6 +117,7 @@ impl RecvDataHandler { peer_nat_info_map, nat_test.clone(), route, + #[cfg(feature = "integrated_tun")] #[cfg(feature = "ip_proxy")] ip_proxy_map, ); @@ -120,23 +127,21 @@ impl RecvDataHandler { turn, client, server, - counter, nat_test, } } fn handle0( - &mut self, + &self, buf: &mut [u8], extend: &mut [u8], route_key: RouteKey, context: &ChannelContext, ) -> anyhow::Result<()> { - // 统计流量 - self.counter.add(buf.len() as _); let net_packet = NetPacket::new(buf)?; + let extend = NetPacket::unchecked(extend); if net_packet.ttl() == 0 || net_packet.source_ttl() < net_packet.ttl() { - log::warn!("丢弃过时包:{:?}", net_packet.head()); + log::warn!("丢弃过时包:{:?} {}", net_packet.head(), route_key.addr); return Ok(()); } let current_device = self.current_device.load(); @@ -148,6 +153,10 @@ impl RecvDataHandler { || dest.is_unspecified() || dest == current_device.broadcast_ip { + // 统计流量 + if let Some(down_traffic_meter) = &context.down_traffic_meter { + down_traffic_meter.add_traffic(net_packet.source(), net_packet.data_len()) + } //发给自己的包 if net_packet.is_gateway() { //服务端-客户端包 diff --git a/vnt/src/handle/recv_data/server.rs b/vnt/src/handle/recv_data/server.rs index 747e3be5..66ecfc08 100644 --- a/vnt/src/handle/recv_data/server.rs +++ b/vnt/src/handle/recv_data/server.rs @@ -12,8 +12,6 @@ use protobuf::Message; use packet::icmp::{icmp, Kind}; use packet::ip::ipv4; use packet::ip::ipv4::packet::IpV4Packet; -#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] -use tun::device::IFace; use crate::channel::context::ChannelContext; use crate::channel::{Route, RouteKey}; @@ -26,50 +24,50 @@ use crate::handle::callback::{ErrorInfo, ErrorType, HandshakeInfo, RegisterInfo, use crate::handle::handshaker; use crate::handle::handshaker::Handshake; use crate::handle::recv_data::PacketHandler; -use crate::handle::{ - registrar, BaseConfigInfo, ConnectStatus, CurrentDeviceInfo, PeerDeviceInfo, GATEWAY_IP, -}; +use crate::handle::{registrar, BaseConfigInfo, ConnectStatus, CurrentDeviceInfo, PeerDeviceInfo}; use crate::nat::NatTest; use crate::proto::message::{DeviceList, HandshakeResponse, RegistrationResponse}; use crate::protocol::body::ENCRYPTION_RESERVED; use crate::protocol::control_packet::ControlPacket; use crate::protocol::error_packet::InErrorPacket; use crate::protocol::{ip_turn_packet, service_packet, NetPacket, Protocol, MAX_TTL}; -use crate::tun_tap_device::tun_create_helper::DeviceAdapter; +use crate::tun_tap_device::vnt_device::DeviceWrite; use crate::{proto, PeerClientInfo}; /// 处理来源于服务端的包 #[derive(Clone)] -pub struct ServerPacketHandler { +pub struct ServerPacketHandler { #[cfg(feature = "server_encrypt")] rsa_cipher: Arc>>, server_cipher: Cipher, current_device: Arc>, - device: DeviceAdapter, + device: Device, device_list: Arc)>>, config_info: BaseConfigInfo, nat_test: NatTest, callback: Call, #[cfg(feature = "server_encrypt")] up_key_time: Arc>, - #[cfg(not(target_os = "android"))] - route_record: Arc>>, external_route: ExternalRoute, handshake: Handshake, + #[cfg(feature = "integrated_tun")] + tun_device_helper: crate::tun_tap_device::tun_create_helper::TunDeviceHelper, } -impl ServerPacketHandler { +impl ServerPacketHandler { pub fn new( #[cfg(feature = "server_encrypt")] rsa_cipher: Arc>>, server_cipher: Cipher, current_device: Arc>, - device: DeviceAdapter, + device: Device, device_list: Arc)>>, config_info: BaseConfigInfo, nat_test: NatTest, callback: Call, external_route: ExternalRoute, handshake: Handshake, + #[cfg(feature = "integrated_tun")] + tun_device_helper: crate::tun_tap_device::tun_create_helper::TunDeviceHelper, ) -> Self { Self { #[cfg(feature = "server_encrypt")] @@ -82,16 +80,20 @@ impl ServerPacketHandler { nat_test, callback, #[cfg(feature = "server_encrypt")] - up_key_time: Arc::new(AtomicCell::new(Instant::now() - Duration::from_secs(60))), - #[cfg(not(target_os = "android"))] - route_record: Arc::new(Mutex::default()), + up_key_time: Arc::new(AtomicCell::new( + Instant::now() + .checked_sub(Duration::from_secs(60)) + .unwrap_or(Instant::now()), + )), external_route, handshake, + #[cfg(feature = "integrated_tun")] + tun_device_helper, } } } -impl PacketHandler for ServerPacketHandler { +impl PacketHandler for ServerPacketHandler { fn handle( &self, mut net_packet: NetPacket<&mut [u8]>, @@ -100,6 +102,15 @@ impl PacketHandler for ServerPacketHandler { context: &ChannelContext, current_device: &CurrentDeviceInfo, ) -> anyhow::Result<()> { + if !current_device.is_server_addr(route_key.addr) { + //拦截不是服务端的流量 + log::info!( + "route_key={:?},不是来源于服务端地址{}", + route_key, + current_device.connect_server + ); + return Ok(()); + } context .route_table .update_read_time(&net_packet.source(), &route_key); @@ -129,7 +140,7 @@ impl PacketHandler for ServerPacketHandler { self.config_info.token.clone(), key, )?; - context.send_by_key(packet.buffer(), route_key)?; + context.send_by_key(&packet, route_key)?; } } } @@ -153,7 +164,7 @@ impl PacketHandler for ServerPacketHandler { key, )?; drop(guard); - context.send_by_key(packet.buffer(), route_key)?; + context.send_by_key(&packet, route_key)?; return Ok(()); } log::warn!( @@ -188,7 +199,7 @@ impl PacketHandler for ServerPacketHandler { self.config_info.token.clone(), key, )?; - context.send_by_key(packet.buffer(), route_key)?; + context.send_by_key(&packet, route_key)?; self.rsa_cipher.lock().replace(rsa_cipher); } return Ok(()); @@ -246,7 +257,7 @@ impl PacketHandler for ServerPacketHandler { } } -impl ServerPacketHandler { +impl ServerPacketHandler { fn service( &self, context: &ChannelContext, @@ -305,80 +316,85 @@ impl ServerPacketHandler { if old.virtual_ip != Ipv4Addr::UNSPECIFIED { log::info!("ip发生变化,old:{:?},response={:?}", old, response); } - #[cfg(target_os = "android")] + let device_config = crate::handle::callback::DeviceConfig::new( + #[cfg(feature = "integrated_tun")] + #[cfg(target_os = "windows")] + self.config_info.tap, + #[cfg(feature = "integrated_tun")] + #[cfg(any( + target_os = "windows", + target_os = "linux", + target_os = "macos" + ))] + self.config_info.device_name.clone(), + self.config_info.mtu, + virtual_ip, + virtual_netmask, + virtual_gateway, + virtual_network, + self.external_route.to_route(), + ); + #[cfg(not(feature = "integrated_tun"))] + self.callback.create_device(device_config); + #[cfg(feature = "integrated_tun")] { - let device_config = crate::handle::callback::DeviceConfig::new( - virtual_ip, - virtual_netmask, - virtual_gateway, - virtual_network, - self.external_route.to_route(), - ); - let device_fd = self.callback.generate_tun(device_config); - if device_fd == 0 { - self.callback.error(ErrorInfo::new_msg( - ErrorType::Unknown, - "device_fd == 0".into(), - )); - } else { - if let Err(e) = self.device.start(device_fd as _) { - self.callback.error(ErrorInfo::new_msg( - ErrorType::Unknown, - format!("{:?}", e), - )); + self.tun_device_helper.stop(); + #[cfg(any( + target_os = "windows", + target_os = "linux", + target_os = "macos" + ))] + match crate::tun_tap_device::create_device(device_config) { + Ok(device) => { + use tun::device::IFace; + let tun_info = crate::handle::callback::DeviceInfo::new( + device.name().unwrap_or("unknown".into()), + device.version().unwrap_or("unknown".into()), + ); + log::info!("tun信息{:?}", tun_info); + self.callback.create_tun(tun_info); + self.tun_device_helper.start(device)?; } - } - } - #[cfg(not(target_os = "android"))] - { - if let Err(e) = self.device.set_ip(virtual_ip, virtual_netmask) { - log::error!("LocalIpExists {:?}", e); - self.callback.error(ErrorInfo::new_msg( - ErrorType::LocalIpExists, - format!("set_ip {:?}", e), - )); - return Ok(()); - } - let mut guard = self.route_record.lock(); - for (dest, mask) in guard.drain(..) { - if let Err(e) = self.device.delete_route(dest, mask) { - log::warn!("删除路由失败 ={:?}", e); + Err(e) => { + log::error!("{:?}", e); + self.callback.error(e); } } - if let Err(e) = - self.device.add_route(virtual_network, virtual_netmask, 1) + #[cfg(target_os = "android")] { - log::warn!("添加默认路由失败 ={:?}", e); - } else { - guard.push((virtual_network, virtual_netmask)); - } - if let Err(e) = - self.device - .add_route(Ipv4Addr::BROADCAST, Ipv4Addr::BROADCAST, 1) - { - log::warn!("添加广播路由失败 ={:?}", e); - } else { - guard.push((Ipv4Addr::BROADCAST, Ipv4Addr::BROADCAST)); - } - - if let Err(e) = self.device.add_route( - Ipv4Addr::from([224, 0, 0, 0]), - Ipv4Addr::from([240, 0, 0, 0]), - 1, - ) { - log::warn!("添加组播路由失败 ={:?}", e); - } else { - guard.push(( - Ipv4Addr::from([224, 0, 0, 0]), - Ipv4Addr::from([240, 0, 0, 0]), - )); - } - - for (dest, mask) in self.external_route.to_route() { - if let Err(e) = self.device.add_route(dest, mask, 1) { - log::warn!("添加路由失败 ={:?}", e); + let device_config = crate::handle::callback::DeviceConfig::new( + self.config_info.mtu, + virtual_ip, + virtual_netmask, + virtual_gateway, + virtual_network, + self.external_route.to_route(), + ); + let device_fd = self.callback.generate_tun(device_config); + if device_fd == 0 { + self.callback.error(ErrorInfo::new_msg( + ErrorType::Unknown, + "device_fd == 0".into(), + )); } else { - guard.push((dest, mask)); + match tun::Device::new(device_fd as _) { + Ok(device) => { + if let Err(e) = + self.tun_device_helper.start(Arc::new(device)) + { + self.callback.error(ErrorInfo::new_msg( + ErrorType::Unknown, + format!("{:?}", e), + )); + } + } + Err(e) => { + self.callback.error(ErrorInfo::new_msg( + ErrorType::Unknown, + format!("{:?}", e), + )); + } + } } } } @@ -468,7 +484,7 @@ impl ServerPacketHandler { )?; log::info!("发送注册请求,{:?}", self.config_info); //注册请求只发送到默认通道 - context.send_default(response.buffer(), current_device.connect_server)?; + context.send_default(&response, current_device.connect_server)?; Ok(()) } fn error( @@ -543,7 +559,7 @@ impl ServerPacketHandler { //纪元不一致,可能有新客户端连接,向服务端拉取客户端列表 let mut poll_device = NetPacket::new_encrypt([0; 12 + ENCRYPTION_RESERVED])?; poll_device.set_source(current_device.virtual_ip); - poll_device.set_destination(GATEWAY_IP); + poll_device.set_destination(current_device.virtual_gateway); poll_device.set_default_version(); poll_device.set_gateway_flag(true); poll_device.first_set_ttl(MAX_TTL); @@ -552,7 +568,7 @@ impl ServerPacketHandler { .set_transport_protocol(service_packet::Protocol::PullDeviceList.into()); self.server_cipher.encrypt_ipv4(&mut poll_device)?; //发送到默认服务端即可 - context.send_default(poll_device.buffer(), current_device.connect_server)?; + context.send_default(&poll_device, current_device.connect_server)?; } } ControlPacket::AddrResponse(addr_packet) => { diff --git a/vnt/src/handle/recv_data/turn.rs b/vnt/src/handle/recv_data/turn.rs index f4d70ffc..59f82d9b 100644 --- a/vnt/src/handle/recv_data/turn.rs +++ b/vnt/src/handle/recv_data/turn.rs @@ -27,6 +27,10 @@ impl PacketHandler for TurnPacketHandler { // ttl减一 let ttl = net_packet.incr_ttl(); if ttl > 0 { + if net_packet.is_gateway() { + // 暂时不转发服务端包 + return Ok(()); + } let destination = net_packet.destination(); if let Some(route) = context.route_table.route_one(&destination) { if route.addr == route_key.addr { @@ -36,7 +40,7 @@ impl PacketHandler for TurnPacketHandler { } if route.metric <= ttl { return context - .send_by_key(net_packet.buffer(), route.route_key()) + .send_by_key(&net_packet, route.route_key()) .context("转发失败"); } } diff --git a/vnt/src/handle/tun_tap/channel_group.rs b/vnt/src/handle/tun_tap/channel_group.rs deleted file mode 100644 index 531a4139..00000000 --- a/vnt/src/handle/tun_tap/channel_group.rs +++ /dev/null @@ -1,30 +0,0 @@ -use std::sync::mpsc::{sync_channel, Receiver, SendError, SyncSender}; - -pub fn channel_group(size: usize, bound: usize) -> (GroupSyncSender, Vec>) { - let mut senders = Vec::with_capacity(size); - let mut receivers = Vec::with_capacity(size); - for _ in 0..size { - let (s, r) = sync_channel(bound); - senders.push(s); - receivers.push(r); - } - ( - GroupSyncSender { - count: 0, - base: senders, - }, - receivers, - ) -} - -pub struct GroupSyncSender { - count: usize, - base: Vec>, -} - -impl GroupSyncSender { - pub fn send(&mut self, t: T) -> Result<(), SendError> { - self.count += 1; - self.base[self.count % self.base.len()].send(t) - } -} diff --git a/vnt/src/handle/tun_tap/mod.rs b/vnt/src/handle/tun_tap/mod.rs index 5d804067..abbe9440 100644 --- a/vnt/src/handle/tun_tap/mod.rs +++ b/vnt/src/handle/tun_tap/mod.rs @@ -1,11 +1,43 @@ -mod channel_group; pub mod tun_handler; #[cfg(unix)] mod unix; + +use crossbeam_utils::atomic::AtomicCell; +use parking_lot::Mutex; +use std::sync::Arc; #[cfg(unix)] pub(crate) use unix::*; + #[cfg(target_os = "windows")] mod windows; + #[cfg(target_os = "windows")] pub(crate) use windows::*; + +/// 仅仅是停止tun,不停止vnt +#[derive(Clone, Default)] +pub struct DeviceStop { + f: Arc>>>, + stopped: Arc>, +} + +impl DeviceStop { + pub fn set_stop_fn(&self, f: F) + where + F: FnOnce() + Send + 'static, + { + self.f.lock().replace(Box::new(f)); + } + pub fn stop(&self) { + if let Some(f) = self.f.lock().take() { + f() + } + } + pub fn stopped(&self) { + self.stopped.store(true); + } + pub fn is_stopped(&self) -> bool { + self.stopped.load() + } +} diff --git a/vnt/src/handle/tun_tap/tun_handler.rs b/vnt/src/handle/tun_tap/tun_handler.rs index 9729f67f..6aee8f2f 100644 --- a/vnt/src/handle/tun_tap/tun_handler.rs +++ b/vnt/src/handle/tun_tap/tun_handler.rs @@ -13,12 +13,11 @@ use tun::device::IFace; use tun::Device; use crate::channel::context::ChannelContext; -use crate::channel::BUFFER_SIZE; use crate::cipher::Cipher; use crate::compression::Compressor; use crate::external_route::ExternalRoute; -use crate::handle::tun_tap::channel_group::channel_group; -use crate::handle::{check_dest, CurrentDeviceInfo, PeerDeviceInfo}; +use crate::handle::tun_tap::DeviceStop; +use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo}; #[cfg(feature = "ip_proxy")] use crate::ip_proxy::IpProxyMap; #[cfg(feature = "ip_proxy")] @@ -27,7 +26,7 @@ use crate::protocol; use crate::protocol::body::ENCRYPTION_RESERVED; use crate::protocol::ip_turn_packet::BroadcastPacket; use crate::protocol::{ip_turn_packet, NetPacket, MAX_TTL}; -use crate::util::{SingleU64Adder, StopManager}; +use crate::util::StopManager; fn icmp(device_writer: &Device, mut ipv4_packet: IpV4Packet<&mut [u8]>) -> anyhow::Result<()> { if ipv4_packet.protocol() == Protocol::Icmp { @@ -54,89 +53,31 @@ pub fn start( #[cfg(feature = "ip_proxy")] ip_proxy_map: Option, client_cipher: Cipher, server_cipher: Cipher, - parallel: usize, - mut up_counter: SingleU64Adder, device_list: Arc)>>, compressor: Compressor, + device_stop: DeviceStop, ) -> io::Result<()> { - if parallel > 1 { - let (sender, receivers) = channel_group::<(Vec, usize)>(parallel, 16); - for (index, receiver) in receivers.into_iter().enumerate() { - let context = context.clone(); - let device = device.clone(); - let current_device = current_device.clone(); - let ip_route = ip_route.clone(); - #[cfg(feature = "ip_proxy")] - let ip_proxy_map = ip_proxy_map.clone(); - let client_cipher = client_cipher.clone(); - let server_cipher = server_cipher.clone(); - let device_list = device_list.clone(); - thread::Builder::new() - .name(format!("tunHandler-{}", index)) - .spawn(move || { - let mut extend = [0; BUFFER_SIZE]; - while let Ok((mut buf, len)) = receiver.recv() { - #[cfg(not(target_os = "macos"))] - let start = 0; - #[cfg(target_os = "macos")] - let start = 4; - match handle( - &context, - &mut buf[start..], - len, - &mut extend, - &device, - current_device.load(), - &ip_route, - #[cfg(feature = "ip_proxy")] - &ip_proxy_map, - &client_cipher, - &server_cipher, - &device_list, - &compressor, - ) { - Ok(_) => {} - Err(e) => { - log::warn!("{:?}", e) - } - } - } - })?; - } - thread::Builder::new() - .name("tunHandlerM".into()) - .spawn(move || { - if let Err(e) = crate::handle::tun_tap::start_multi( - stop_manager, - device, - sender, - &mut up_counter, - ) { - log::warn!("stop:{}", e); - } - })?; - } else { - thread::Builder::new() - .name("tunHandlerS".into()) - .spawn(move || { - if let Err(e) = crate::handle::tun_tap::start_simple( - stop_manager, - &context, - device, - current_device, - ip_route, - #[cfg(feature = "ip_proxy")] - ip_proxy_map, - client_cipher, - server_cipher, - &mut up_counter, - device_list, - compressor, - ) { - log::warn!("stop:{}", e); - } - })?; - } + thread::Builder::new() + .name("tunHandlerS".into()) + .spawn(move || { + if let Err(e) = crate::handle::tun_tap::start_simple( + stop_manager, + &context, + device, + current_device, + ip_route, + #[cfg(feature = "ip_proxy")] + ip_proxy_map, + client_cipher, + server_cipher, + device_list, + compressor, + device_stop, + ) { + log::warn!("stop:{}", e); + } + })?; + Ok(()) } @@ -164,10 +105,7 @@ fn broadcast( break; } if let Some(route) = sender.route_table.route_one_p2p(&peer_ip) { - if sender - .send_by_key(net_packet.buffer(), route.route_key()) - .is_ok() - { + if sender.send_by_key(&net_packet, route.route_key()).is_ok() { p2p_ips.push(peer_ip); continue; } @@ -182,7 +120,7 @@ fn broadcast( if p2p_ips.is_empty() { //都没有p2p则直接由服务器转发 if current_device.status.online() { - sender.send_default(net_packet.buffer(), current_device.connect_server)?; + sender.send_default(&net_packet, current_device.connect_server)?; } return Ok(()); } @@ -192,7 +130,7 @@ fn broadcast( //非直连的广播要改变目的地址,不然服务端收到了会再次广播 net_packet.set_destination(peer_ip); sender.send_ipv4_by_id( - net_packet.buffer(), + &net_packet, &peer_ip, current_device.connect_server, current_device.status.online(), @@ -220,7 +158,7 @@ fn broadcast( broadcast.set_address(&p2p_ips)?; broadcast.set_data(net_packet.buffer())?; server_cipher.encrypt_ipv4(&mut server_packet)?; - sender.send_default(server_packet.buffer(), current_device.connect_server)?; + sender.send_default(&server_packet, current_device.connect_server)?; Ok(()) } @@ -268,17 +206,13 @@ pub(crate) fn handle( if protocol == Protocol::Icmp { net_packet.set_gateway_flag(true); server_cipher.encrypt_ipv4(&mut net_packet)?; - context.send_default(net_packet.buffer(), current_device.connect_server)?; + context.send_default(&net_packet, current_device.connect_server)?; } return Ok(()); } if !dest_ip.is_multicast() && !dest_ip.is_broadcast() && current_device.broadcast_ip != dest_ip { - if !check_dest( - dest_ip, - current_device.virtual_netmask, - current_device.virtual_network, - ) { + if current_device.not_in_network(dest_ip) { if let Some(r_dest_ip) = ip_route.route(&dest_ip) { //路由的目标不能是自己 if r_dest_ip == src_ip { @@ -330,7 +264,7 @@ pub(crate) fn handle( client_cipher.encrypt_ipv4(&mut net_packet)?; context.send_ipv4_by_id( - net_packet.buffer(), + &net_packet, &dest_ip, current_device.connect_server, current_device.status.online(), diff --git a/vnt/src/handle/tun_tap/unix.rs b/vnt/src/handle/tun_tap/unix.rs index c49323a1..a75d02c6 100644 --- a/vnt/src/handle/tun_tap/unix.rs +++ b/vnt/src/handle/tun_tap/unix.rs @@ -3,11 +3,11 @@ use crate::channel::BUFFER_SIZE; use crate::cipher::Cipher; use crate::compression::Compressor; use crate::external_route::ExternalRoute; -use crate::handle::tun_tap::channel_group::GroupSyncSender; +use crate::handle::tun_tap::DeviceStop; use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo}; #[cfg(feature = "ip_proxy")] use crate::ip_proxy::IpProxyMap; -use crate::util::{SingleU64Adder, StopManager}; +use crate::util::StopManager; use crossbeam_utils::atomic::AtomicCell; use mio::event::Source; use mio::unix::SourceFd; @@ -30,16 +30,27 @@ pub(crate) fn start_simple( #[cfg(feature = "ip_proxy")] ip_proxy_map: Option, client_cipher: Cipher, server_cipher: Cipher, - up_counter: &mut SingleU64Adder, device_list: Arc)>>, compressor: Compressor, + device_stop: DeviceStop, ) -> anyhow::Result<()> { let poll = Poll::new()?; let waker = Arc::new(Waker::new(poll.registry(), STOP)?); let _waker = waker.clone(); - let worker = stop_manager.add_listener("tun_device".into(), move || { - let _ = waker.wake(); - })?; + let worker = { + stop_manager.add_listener("tun_device".into(), move || { + if let Err(e) = waker.wake() { + log::warn!("{:?}", e); + } + })? + }; + let worker_cell = Arc::new(AtomicCell::new(Some(worker))); + let _worker_cell = worker_cell.clone(); + device_stop.set_stop_fn(move || { + if let Some(worker) = _worker_cell.take() { + worker.stop_self() + } + }); if let Err(e) = start_simple0( poll, context, @@ -50,13 +61,15 @@ pub(crate) fn start_simple( ip_proxy_map, client_cipher, server_cipher, - up_counter, device_list, compressor, ) { log::error!("{:?}", e); }; - worker.stop_all(); + device_stop.stopped(); + if let Some(worker) = worker_cell.take() { + worker.stop_all(); + } drop(_waker); Ok(()) } @@ -70,7 +83,6 @@ fn start_simple0( #[cfg(feature = "ip_proxy")] ip_proxy_map: Option, client_cipher: Cipher, server_cipher: Cipher, - up_counter: &mut SingleU64Adder, device_list: Arc)>>, compressor: Compressor, ) -> anyhow::Result<()> { @@ -79,29 +91,35 @@ fn start_simple0( let fd = device.as_tun_fd(); fd.set_nonblock()?; SourceFd(&fd.as_raw_fd()).register(poll.registry(), FD, Interest::READABLE)?; - let mut evnets = Events::with_capacity(4); + let mut events = Events::with_capacity(4); #[cfg(not(target_os = "macos"))] let start = 12; #[cfg(target_os = "macos")] let start = 12 - 4; loop { - poll.poll(&mut evnets, None)?; - for event in evnets.iter() { + if let Err(e) = poll.poll(&mut events, None) { + crate::ignore_io_interrupted(e)?; + continue; + } + for event in events.iter() { if event.token() == STOP { return Ok(()); } + let mut retries = 0; loop { let len = match fd.read(&mut buf[start..]) { Ok(len) => len + start, Err(e) => { if e.kind() == io::ErrorKind::WouldBlock { + retries += 1; + if retries < 8 { + continue; + } break; } Err(e)? } }; - //单线程的 - up_counter.add(len as u64); // buf是重复利用的,需要重置头部 buf[..12].fill(0); match crate::handle::tun_tap::tun_handler::handle( @@ -128,65 +146,3 @@ fn start_simple0( } } } - -pub(crate) fn start_multi( - stop_manager: StopManager, - device: Arc, - group_sync_sender: GroupSyncSender<(Vec, usize)>, - up_counter: &mut SingleU64Adder, -) -> anyhow::Result<()> { - let poll = Poll::new()?; - let waker = Arc::new(Waker::new(poll.registry(), STOP)?); - let _waker = waker.clone(); - let worker = stop_manager.add_listener("tun_device".into(), move || { - let _ = waker.wake(); - })?; - if let Err(e) = start_multi0(poll, device, group_sync_sender, up_counter) { - log::error!("{:?}", e); - }; - worker.stop_all(); - drop(_waker); - Ok(()) -} - -fn start_multi0( - mut poll: Poll, - device: Arc, - mut group_sync_sender: GroupSyncSender<(Vec, usize)>, - up_counter: &mut SingleU64Adder, -) -> anyhow::Result<()> { - let fd = device.as_tun_fd(); - fd.set_nonblock()?; - SourceFd(&fd.as_raw_fd()).register(poll.registry(), FD, Interest::READABLE)?; - let mut evnets = Events::with_capacity(4); - let mut buf = vec![0; 1024 * 16]; - #[cfg(not(target_os = "macos"))] - let start = 12; - #[cfg(target_os = "macos")] - let start = 12 - 4; - loop { - poll.poll(&mut evnets, None)?; - for event in evnets.iter() { - if event.token() == STOP { - return Ok(()); - } - loop { - let len = match fd.read(&mut buf[start..]) { - Ok(len) => len + start, - Err(e) => { - if e.kind() == io::ErrorKind::WouldBlock { - break; - } - Err(e)? - } - }; - //单线程的 - up_counter.add(len as u64); - if group_sync_sender.send((buf, len)).is_err() { - return Ok(()); - } - buf = vec![0; 1024 * 16]; - } - } - } -} diff --git a/vnt/src/handle/tun_tap/windows.rs b/vnt/src/handle/tun_tap/windows.rs index 0d03b1e5..0df3e4ad 100644 --- a/vnt/src/handle/tun_tap/windows.rs +++ b/vnt/src/handle/tun_tap/windows.rs @@ -3,11 +3,11 @@ use crate::channel::BUFFER_SIZE; use crate::cipher::Cipher; use crate::compression::Compressor; use crate::external_route::ExternalRoute; -use crate::handle::tun_tap::channel_group::GroupSyncSender; +use crate::handle::tun_tap::DeviceStop; use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo}; #[cfg(feature = "ip_proxy")] use crate::ip_proxy::IpProxyMap; -use crate::util::{SingleU64Adder, StopManager}; +use crate::util::StopManager; use crossbeam_utils::atomic::AtomicCell; use parking_lot::Mutex; use std::sync::Arc; @@ -23,9 +23,9 @@ pub(crate) fn start_simple( #[cfg(feature = "ip_proxy")] ip_proxy_map: Option, client_cipher: Cipher, server_cipher: Cipher, - up_counter: &mut SingleU64Adder, device_list: Arc)>>, compressor: Compressor, + device_stop: DeviceStop, ) -> anyhow::Result<()> { let worker = { let device = device.clone(); @@ -35,6 +35,16 @@ pub(crate) fn start_simple( } })? }; + let worker_cell = Arc::new(AtomicCell::new(Some(worker))); + + { + let worker_cell = worker_cell.clone(); + device_stop.set_stop_fn(move || { + if let Some(worker) = worker_cell.take() { + worker.stop_self() + } + }); + } if let Err(e) = start_simple0( context, device, @@ -44,15 +54,18 @@ pub(crate) fn start_simple( ip_proxy_map, client_cipher, server_cipher, - up_counter, device_list, compressor, ) { log::error!("{:?}", e); } - worker.stop_all(); + device_stop.stopped(); + if let Some(worker) = worker_cell.take() { + worker.stop_all(); + } Ok(()) } + fn start_simple0( context: &ChannelContext, device: Arc, @@ -61,7 +74,6 @@ fn start_simple0( #[cfg(feature = "ip_proxy")] ip_proxy_map: Option, client_cipher: Cipher, server_cipher: Cipher, - up_counter: &mut SingleU64Adder, device_list: Arc)>>, compressor: Compressor, ) -> anyhow::Result<()> { @@ -70,7 +82,6 @@ fn start_simple0( loop { let len = device.read(&mut buf[12..])? + 12; //单线程的 - up_counter.add(len as u64); // buf是重复利用的,需要重置头部 buf[..12].fill(0); match crate::handle::tun_tap::tun_handler::handle( @@ -95,38 +106,3 @@ fn start_simple0( } } } -pub(crate) fn start_multi( - stop_manager: StopManager, - device: Arc, - group_sync_sender: GroupSyncSender<(Vec, usize)>, - up_counter: &mut SingleU64Adder, -) -> anyhow::Result<()> { - let worker = { - let device = device.clone(); - stop_manager.add_listener("tun_device_multi".into(), move || { - if let Err(e) = device.shutdown() { - log::warn!("{:?}", e); - } - })? - }; - if let Err(e) = start_multi0(device, group_sync_sender, up_counter) { - log::error!("{:?}", e); - }; - worker.stop_all(); - Ok(()) -} -fn start_multi0( - device: Arc, - mut group_sync_sender: GroupSyncSender<(Vec, usize)>, - up_counter: &mut SingleU64Adder, -) -> anyhow::Result<()> { - loop { - let mut buf = vec![0; 1024 * 16]; - let len = device.read(&mut buf[12..])? + 12; - //单线程的 - up_counter.add(len as u64); - if group_sync_sender.send((buf, len)).is_err() { - return Ok(()); - } - } -} diff --git a/vnt/src/ip_proxy/icmp_proxy.rs b/vnt/src/ip_proxy/icmp_proxy.rs index 93557cf4..ffb7d2e5 100644 --- a/vnt/src/ip_proxy/icmp_proxy.rs +++ b/vnt/src/ip_proxy/icmp_proxy.rs @@ -155,7 +155,7 @@ fn recv_handle( return; } if let Err(e) = context.send_ipv4_by_id( - net_packet.buffer(), + &net_packet, &dest_ip, current_device.connect_server, current_device.status.online(), diff --git a/vnt/src/lib.rs b/vnt/src/lib.rs index 0a074561..4de75969 100644 --- a/vnt/src/lib.rs +++ b/vnt/src/lib.rs @@ -3,17 +3,29 @@ pub const VNT_VERSION: &'static str = env!("CARGO_PKG_VERSION"); pub mod channel; pub mod cipher; pub mod core; -pub mod external_route; +mod external_route; pub mod handle; #[cfg(feature = "ip_proxy")] -pub mod ip_proxy; +mod ip_proxy; pub mod nat; #[cfg(feature = "port_mapping")] -pub mod port_mapping; -pub mod proto; +mod port_mapping; +mod proto; pub mod protocol; -pub mod tun_tap_device; +mod tun_tap_device; +pub use tun_tap_device::*; pub mod util; pub use handle::callback::*; + pub mod compression; +pub use packet; + +pub(crate) fn ignore_io_interrupted(e: std::io::Error) -> std::io::Result<()> { + if e.kind() == std::io::ErrorKind::Interrupted { + log::warn!("ignore_io_interrupted"); + Ok(()) + } else { + Err(e) + } +} diff --git a/vnt/src/nat/mod.rs b/vnt/src/nat/mod.rs index b442c30f..6c3c4be9 100644 --- a/vnt/src/nat/mod.rs +++ b/vnt/src/nat/mod.rs @@ -2,16 +2,18 @@ use anyhow::Context; use std::io; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, ToSocketAddrs}; use std::net::{SocketAddr, UdpSocket}; -use std::ops::Sub; use std::sync::Arc; use std::time::{Duration, Instant}; use crossbeam_utils::atomic::AtomicCell; use parking_lot::Mutex; +use rand::prelude::SliceRandom; use rand::Rng; use crate::channel::punch::{NatInfo, NatType}; use crate::proto::message::PunchNatType; +#[cfg(feature = "upnp")] +use crate::util::UPnP; mod stun; @@ -47,12 +49,62 @@ pub fn local_ipv6_() -> io::Result { pub fn local_ipv6() -> Option { match local_ipv6_() { - Ok(ipv6) => Some(ipv6), + Ok(ipv6) => { + if is_ipv6_global(&ipv6) { + return Some(ipv6); + } + } Err(e) => { log::warn!("获取ipv6失败:{:?}", e); - None } } + None +} + +pub const fn is_ipv4_global(ipv4: &Ipv4Addr) -> bool { + !(ipv4.octets()[0] == 0 // "This network" + || ipv4.is_private() + || ipv4.octets()[0] == 100 && (ipv4.octets()[1] & 0b1100_0000 == 0b0100_0000)//ipv4.is_shared() + || ipv4.is_loopback() + || ipv4.is_link_local() + // addresses reserved for future protocols (`192.0.0.0/24`) + // .9 and .10 are documented as globally reachable so they're excluded + || ( + ipv4.octets()[0] == 192 && ipv4.octets()[1] == 0 && ipv4.octets()[2] == 0 + && ipv4.octets()[3] != 9 && ipv4.octets()[3] != 10 + ) + || ipv4.is_documentation() + || ipv4.octets()[0] == 198 && (ipv4.octets()[1] & 0xfe) == 18//ipv4.is_benchmarking() + || ipv4.octets()[0] & 240 == 240 && !ipv4.is_broadcast()//ipv4.is_reserved() + || ipv4.is_broadcast()) +} + +pub const fn is_ipv6_global(ipv6addr: &Ipv6Addr) -> bool { + !(ipv6addr.is_unspecified() + || ipv6addr.is_loopback() + // IPv4-mapped Address (`::ffff:0:0/96`) + || matches!(ipv6addr.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) + // IPv4-IPv6 Translat. (`64:ff9b:1::/48`) + || matches!(ipv6addr.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) + // Discard-Only Address Block (`100::/64`) + || matches!(ipv6addr.segments(), [0x100, 0, 0, 0, _, _, _, _]) + // IETF Protocol Assignments (`2001::/23`) + || (matches!(ipv6addr.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) + && !( + // Port Control Protocol Anycast (`2001:1::1`) + u128::from_be_bytes(ipv6addr.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 + // Traversal Using Relays around NAT Anycast (`2001:1::2`) + || u128::from_be_bytes(ipv6addr.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 + // AMT (`2001:3::/32`) + || matches!(ipv6addr.segments(), [0x2001, 3, _, _, _, _, _, _]) + // AS112-v6 (`2001:4:112::/48`) + || matches!(ipv6addr.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) + // ORCHIDv2 (`2001:20::/28`) + || matches!(ipv6addr.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) + )) + || (ipv6addr.segments()[0] == 0x2001) && (ipv6addr.segments()[1] == 0xdb8)//ipv6addr.is_documentation() + || (ipv6addr.segments()[0] & 0xfe00) == 0xfc00//ipv6addr.is_unique_local() + || (ipv6addr.segments()[0] & 0xffc0) == 0xfe80) //ipv6addr.is_unicast_link_local()) } #[derive(Clone)] @@ -62,6 +114,8 @@ pub struct NatTest { time: Arc>, udp_ports: Vec, tcp_port: u16, + #[cfg(feature = "upnp")] + upnp: UPnP, } impl From for PunchNatType { @@ -92,7 +146,9 @@ impl NatTest { tcp_port: u16, ) -> NatTest { if stun_server.len() > 5 { + stun_server.shuffle(&mut rand::thread_rng()); stun_server.truncate(5); + log::info!("stun_server truncate {:?}", stun_server); } let ports = vec![0; udp_ports.len()]; let nat_info = NatInfo::new( @@ -106,14 +162,27 @@ impl NatTest { NatType::Cone, ); let info = Arc::new(Mutex::new(nat_info)); + #[cfg(feature = "upnp")] + let upnp = UPnP::default(); + #[cfg(feature = "upnp")] + for port in &udp_ports { + upnp.add_udp_port(*port); + } + #[cfg(feature = "upnp")] + upnp.add_tcp_port(tcp_port); + let instant = Instant::now(); NatTest { stun_server, info, time: Arc::new(AtomicCell::new( - Instant::now().sub(Duration::from_secs(100)), + instant + .checked_sub(Duration::from_secs(100)) + .unwrap_or(instant), )), udp_ports, tcp_port, + #[cfg(feature = "upnp")] + upnp, } } pub fn can_update(&self) -> bool { @@ -185,7 +254,7 @@ impl NatTest { } false } - pub fn update_addr(&self, index: usize, ip: Ipv4Addr, port: u16) { + pub fn update_addr(&self, index: usize, ip: Ipv4Addr, port: u16) -> bool { let mut guard = self.info.lock(); guard.update_addr(index, ip, port) } @@ -204,6 +273,13 @@ impl NatTest { Ok(guard.clone()) } + #[cfg(feature = "upnp")] + pub fn reset_upnp(&self) { + let local_ipv4 = self.info.lock().local_ipv4.clone(); + if let Some(local_ipv4) = local_ipv4 { + self.upnp.reset(local_ipv4) + } + } pub fn send_data(&self) -> anyhow::Result<(Vec, SocketAddr)> { let len = self.stun_server.len(); let stun_server = if len == 1 { @@ -224,46 +300,55 @@ impl NatTest { source_addr: SocketAddr, buf: &[u8], ) -> anyhow::Result { - if let Some(addr) = stun::recv_stun_response(buf) { - if let SocketAddr::V4(addr) = addr { - let mut check_fail = true; - let source_ip = match source_addr.ip() { - IpAddr::V4(ip) => ip, - IpAddr::V6(ip) => { - if let Some(ip) = ip.to_ipv4_mapped() { - ip - } else { - return Ok(false); - } - } - }; - 'a: for stun_server in &self.stun_server { - for x in stun_server.to_socket_addrs()? { - if source_addr.port() == x.port() { - if let IpAddr::V4(ip) = x.ip() { - if ip == source_ip { - check_fail = false; - break 'a; - } - }; - } + if buf[0] == 0x01 && buf[1] == 0x01 { + if let Some(addr) = stun::recv_stun_response(buf) { + if let Err(e) = self.recv_data_(index, source_addr, addr) { + log::warn!("{:?}", e); + } + } + Ok(true) + } else { + Ok(false) + } + } + fn recv_data_( + &self, + index: usize, + source_addr: SocketAddr, + addr: SocketAddr, + ) -> anyhow::Result<()> { + if let SocketAddr::V4(addr) = addr { + let mut check_fail = true; + let source_ip = match source_addr.ip() { + IpAddr::V4(ip) => ip, + IpAddr::V6(ip) => { + if let Some(ip) = ip.to_ipv4() { + ip + } else { + return Ok(()); } } - if check_fail { - return Ok(false); + }; + 'a: for stun_server in &self.stun_server { + for x in stun_server.to_socket_addrs()? { + if source_addr.port() == x.port() { + if let IpAddr::V4(ip) = x.ip() { + if ip == source_ip { + check_fail = false; + break 'a; + } + }; + } } - let ip = addr.ip(); - if !ip.is_multicast() - && !ip.is_broadcast() - && !ip.is_unspecified() - && !ip.is_loopback() - && !ip.is_private() - { - self.update_addr(index, *addr.ip(), addr.port()); - return Ok(true); + } + if !check_fail { + if is_ipv4_global(addr.ip()) { + if self.update_addr(index, *addr.ip(), addr.port()) { + log::info!("回应地址{:?},来源stun {:?}", addr, source_addr) + } } } } - return Ok(false); + Ok(()) } } diff --git a/vnt/src/nat/stun.rs b/vnt/src/nat/stun.rs index 05b9a58a..dcc0e7ea 100644 --- a/vnt/src/nat/stun.rs +++ b/vnt/src/nat/stun.rs @@ -9,17 +9,12 @@ use std::net::UdpSocket; use stun_format::Attr; pub fn stun_test_nat(stun_servers: Vec) -> io::Result<(NatType, Vec, u16)> { - let mut th = Vec::new(); - for _ in 0..2 { - let stun_servers = stun_servers.clone(); - let handle = std::thread::spawn(move || stun_test_nat0(stun_servers)); - th.push(handle); - } let mut nat_type = NatType::Cone; let mut port_range = 0; let mut hash_set = HashSet::new(); - for x in th { - match x.join().unwrap() { + for _ in 0..2 { + let stun_servers = stun_servers.clone(); + match stun_test_nat0(stun_servers) { Ok((nat_type_t, ip_list_t, port_range_t)) => { if nat_type_t == NatType::Symmetric { nat_type = NatType::Symmetric; @@ -86,13 +81,13 @@ fn test_nat(udp: &UdpSocket, stun_server: &String) -> io::Result { if mapped_addr2.is_ipv4() { addr.insert(mapped_addr1); @@ -116,6 +111,7 @@ fn test_nat(udp: &UdpSocket, stun_server: &String) -> io::Result rs, Err(e) => { - log::warn!("stun error {:?}", e); + log::warn!("stun {} error {:?}", stun_server, e); continue; } }; @@ -198,9 +194,6 @@ pub fn send_stun_request() -> Vec { } pub fn recv_stun_response(buf: &[u8]) -> Option { - if buf[0] != 0x01 && buf[1] != 0x01 { - return None; - } let msg = stun_format::Msg::from(buf); if let Some(tid) = msg.tid() { if tid & TAG != TAG { diff --git a/vnt/src/port_mapping/mod.rs b/vnt/src/port_mapping/mod.rs index 52a71a55..33354cce 100644 --- a/vnt/src/port_mapping/mod.rs +++ b/vnt/src/port_mapping/mod.rs @@ -15,10 +15,10 @@ pub fn convert(vec: Vec) -> anyhow::Result"); + let mut split = udp_mapping.split("-"); let bind_addr = split.next().with_context(|| { format!( - "udp_mapping error {:?},eg: udp:127.0.0.1:80->10.26.0.10:8080", + "udp_mapping error {:?},eg: udp:127.0.0.1:80-10.26.0.10:8080", x ) })?; @@ -26,7 +26,7 @@ pub fn convert(vec: Vec) -> anyhow::Result10.26.0.10:8080", + "udp_mapping error {:?},eg: udp:127.0.0.1:80-10.26.0.10:8080", x ) })?; @@ -34,10 +34,10 @@ pub fn convert(vec: Vec) -> anyhow::Result"); + let mut split = tcp_mapping.split("-"); let bind_addr = split.next().with_context(|| { format!( - "tcp_mapping error {:?},eg: tcp:127.0.0.1:80->10.26.0.10:8080", + "tcp_mapping error {:?},eg: tcp:127.0.0.1:80-10.26.0.10:8080", x ) })?; @@ -45,7 +45,7 @@ pub fn convert(vec: Vec) -> anyhow::Result10.26.0.10:8080", + "tcp_mapping error {:?},eg: tcp:127.0.0.1:80-10.26.0.10:8080", x ) })?; @@ -53,7 +53,7 @@ pub fn convert(vec: Vec) -> anyhow::Result10.26.0.10:8080", + "port_mapping error {:?},eg: tcp:127.0.0.1:80-10.26.0.10:8080", x ))?; } diff --git a/vnt/src/port_mapping/tcp_mapping.rs b/vnt/src/port_mapping/tcp_mapping.rs index 32a717b6..6bdc61f1 100644 --- a/vnt/src/port_mapping/tcp_mapping.rs +++ b/vnt/src/port_mapping/tcp_mapping.rs @@ -6,7 +6,11 @@ pub async fn tcp_mapping(bind_addr: SocketAddr, destination: String) -> anyhow:: let tcp_listener = TcpListener::bind(bind_addr) .await .with_context(|| format!("TCP binding {:?} failed", bind_addr))?; - tokio::spawn(tcp_mapping_(bind_addr, tcp_listener, destination)); + tokio::spawn(async move { + if let Err(e) = tcp_mapping_(bind_addr, tcp_listener, destination).await { + log::warn!("tcp_mapping {:?}", e); + } + }); Ok(()) } diff --git a/vnt/src/protocol/body.rs b/vnt/src/protocol/body.rs index bfa5fd2e..07d39c02 100644 --- a/vnt/src/protocol/body.rs +++ b/vnt/src/protocol/body.rs @@ -4,6 +4,261 @@ pub const ENCRYPTION_RESERVED: usize = 16 + 32 + 12; pub const AES_GCM_ENCRYPTION_RESERVED: usize = 32; pub const RSA_ENCRYPTION_RESERVED: usize = 32; +pub const RANDOM_RESERVED: usize = 4; +pub const FINGER_RESERVED: usize = 12; +pub const TAG_RESERVED: usize = 16; + +/* ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| random(32) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| finger(32) | +| finger(32) | +| finger(32) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ +pub trait SecretTail { + fn buffer(&self) -> &[u8]; + fn exist_finger(&self) -> bool; + fn random_buf(&self) -> &[u8] { + let buf = self.buffer(); + let mut end = buf.len(); + if self.exist_finger() { + end -= FINGER_RESERVED; + } + &buf[end - RANDOM_RESERVED..end] + } + fn finger(&self) -> &[u8] { + if self.exist_finger() { + let buf = self.buffer(); + let end = buf.len(); + &buf[end - FINGER_RESERVED..end] + } else { + &[] + } + } +} + +pub trait SecretTailMut: SecretTail { + fn buffer_mut(&mut self) -> &mut [u8]; + fn set_random(&mut self, random: &[u8]) { + let f = self.exist_finger(); + let buf = self.buffer_mut(); + let mut end = buf.len(); + if f { + end -= FINGER_RESERVED; + } + buf[end - RANDOM_RESERVED..end].copy_from_slice(random); + } + fn set_finger(&mut self, finger: &[u8]) -> io::Result<()> { + if self.exist_finger() { + if finger.len() != FINGER_RESERVED { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "finger.len != 12", + )); + } + let buf = self.buffer_mut(); + let end = buf.len(); + buf[end - FINGER_RESERVED..end].copy_from_slice(finger); + Ok(()) + } else { + Err(io::Error::new( + io::ErrorKind::InvalidData, + "not exist finger", + )) + } + } +} + +/* aead加密数据体 + 0 15 31 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 数据体 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | tag(32) | + | tag(32) | + | tag(32) | + | tag(32) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | random(32) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | finger(32) | + | finger(32) | + | finger(32) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + 注:finger用于快速校验数据是否被修改,上层可使用token、协议头参与计算finger, + 确保服务端和客户端都能感知修改(服务端不能解密也能校验指纹) +*/ +pub struct AEADSecretBody { + buffer: B, + exist_finger: bool, +} + +impl> AEADSecretBody { + pub fn new(buffer: B, exist_finger: bool) -> io::Result> { + let len = buffer.as_ref().len(); + let min_len = if exist_finger { + TAG_RESERVED + RANDOM_RESERVED + FINGER_RESERVED + } else { + TAG_RESERVED + RANDOM_RESERVED + }; + // 不能大于udp最大载荷长度 + if len < min_len || len > 65535 - 20 - 8 - 12 { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + format!("AEADSecretBody length overflow {}", len), + )); + } + Ok(AEADSecretBody { + buffer, + exist_finger, + }) + } + pub fn data(&self) -> &[u8] { + let mut end = self.buffer.as_ref().len() - TAG_RESERVED - RANDOM_RESERVED; + if self.exist_finger { + end -= FINGER_RESERVED; + } + &self.buffer.as_ref()[..end] + } + pub fn tag(&self) -> &[u8] { + let mut end = self.buffer.as_ref().len() - RANDOM_RESERVED; + if self.exist_finger { + end -= FINGER_RESERVED; + } + &self.buffer.as_ref()[end - TAG_RESERVED..end] + } +} + +impl> SecretTail for AEADSecretBody { + #[inline] + fn buffer(&self) -> &[u8] { + self.buffer.as_ref() + } + #[inline] + fn exist_finger(&self) -> bool { + self.exist_finger + } +} + +impl + AsMut<[u8]>> SecretTailMut for AEADSecretBody { + #[inline] + fn buffer_mut(&mut self) -> &mut [u8] { + self.buffer.as_mut() + } +} + +impl + AsMut<[u8]>> AEADSecretBody { + /// 数据部分 + pub fn data_mut(&mut self) -> &mut [u8] { + let mut end = self.buffer.as_ref().len() - RANDOM_RESERVED - TAG_RESERVED; + if self.exist_finger { + end -= FINGER_RESERVED; + } + &mut self.buffer.as_mut()[..end] + } + /// 数据和tag部分 + pub fn data_tag_mut(&mut self) -> &mut [u8] { + let mut end = self.buffer.as_ref().len() - RANDOM_RESERVED; + if self.exist_finger { + end -= FINGER_RESERVED; + } + &mut self.buffer.as_mut()[..end] + } + pub fn set_tag(&mut self, tag: &[u8]) -> io::Result<()> { + if tag.len() != 16 { + return Err(io::Error::new(io::ErrorKind::InvalidData, "tag.len != 16")); + } + let mut end = self.buffer.as_ref().len() - RANDOM_RESERVED; + if self.exist_finger { + end -= FINGER_RESERVED; + } + self.buffer.as_mut()[end - TAG_RESERVED..end].copy_from_slice(tag); + Ok(()) + } +} + +/* 带随机数的加密数据体 + 0 15 31 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 数据体 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | random(32) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | finger(32) | + | finger(32) | + | finger(32) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + 注:finger用于快速校验数据是否被修改,上层可使用token、协议头参与计算finger, + 确保服务端和客户端都能感知修改(服务端不能解密也能校验指纹) +*/ +pub struct IVSecretBody { + buffer: B, + exist_finger: bool, +} + +impl> IVSecretBody { + pub fn new(buffer: B, exist_finger: bool) -> io::Result> { + let len = buffer.as_ref().len(); + let min_len = if exist_finger { + FINGER_RESERVED + RANDOM_RESERVED + } else { + RANDOM_RESERVED + }; + // 不能大于udp最大载荷长度 + if len < min_len || len > 65535 - 20 - 8 - 12 { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + format!("IVSecretBody length overflow {}", len), + )); + } + Ok(IVSecretBody { + buffer, + exist_finger, + }) + } + pub fn data(&self) -> &[u8] { + let mut end = self.buffer.as_ref().len() - RANDOM_RESERVED; + if self.exist_finger { + end -= FINGER_RESERVED; + } + &self.buffer.as_ref()[..end] + } +} + +impl + AsMut<[u8]>> IVSecretBody { + pub fn data_mut(&mut self) -> &mut [u8] { + let mut end = self.buffer.as_ref().len() - RANDOM_RESERVED; + if self.exist_finger { + end -= FINGER_RESERVED; + } + &mut self.buffer.as_mut()[..end] + } +} + +impl> SecretTail for IVSecretBody { + #[inline] + fn buffer(&self) -> &[u8] { + self.buffer.as_ref() + } + #[inline] + fn exist_finger(&self) -> bool { + self.exist_finger + } +} + +impl + AsMut<[u8]>> SecretTailMut for IVSecretBody { + #[inline] + fn buffer_mut(&mut self) -> &mut [u8] { + self.buffer.as_mut() + } +} + /* aes_gcm加密数据体 0 15 31 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 @@ -253,85 +508,6 @@ impl + AsMut<[u8]>> AesCbcSecretBody { &mut self.buffer.as_mut()[..end] } } -/* ChaCah20加密数据体 - 0 15 31 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 数据体 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | finger(32) | - | finger(32) | - | finger(32) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - 注:finger用于快速校验数据是否被修改,上层可使用token、协议头参与计算finger, - 确保服务端和客户端都能感知修改(服务端不能解密也能校验指纹) -*/ -pub struct ChaCah20SecretBody { - buffer: B, - exist_finger: bool, -} - -impl> ChaCah20SecretBody { - pub fn new(buffer: B, exist_finger: bool) -> io::Result> { - let len = buffer.as_ref().len(); - let min_len = if exist_finger { 12 } else { 0 }; - // 不能大于udp最大载荷长度 - if len < min_len || len > 65535 - 20 - 8 - 12 { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "ChaCah20SecretBody length overflow", - )); - } - Ok(ChaCah20SecretBody { - buffer, - exist_finger, - }) - } - pub fn en_body(&self) -> &[u8] { - let mut end = self.buffer.as_ref().len(); - if self.exist_finger { - end -= 12; - } - &self.buffer.as_ref()[..end] - } - pub fn finger(&self) -> &[u8] { - if self.exist_finger { - let end = self.buffer.as_ref().len(); - &self.buffer.as_ref()[end - 12..end] - } else { - &[] - } - } -} - -impl + AsMut<[u8]>> ChaCah20SecretBody { - pub fn set_finger(&mut self, finger: &[u8]) -> io::Result<()> { - if self.exist_finger { - if finger.len() != 12 { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "finger.len != 12", - )); - } - let end = self.buffer.as_ref().len(); - self.buffer.as_mut()[end - 12..end].copy_from_slice(finger); - Ok(()) - } else { - Err(io::Error::new( - io::ErrorKind::InvalidData, - "cbc not exist finger", - )) - } - } - pub fn en_body_mut(&mut self) -> &mut [u8] { - let mut end = self.buffer.as_ref().len(); - if self.exist_finger { - end -= 12; - } - &mut self.buffer.as_mut()[..end] - } -} /* rsa加密数据体 0 15 31 diff --git a/vnt/src/protocol/mod.rs b/vnt/src/protocol/mod.rs index 0a96cc30..e718646d 100644 --- a/vnt/src/protocol/mod.rs +++ b/vnt/src/protocol/mod.rs @@ -6,7 +6,7 @@ use std::{fmt, io}; 0 15 31 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |e |s |u |u| 版本(4) | 协议(8) | 上层协议(8) | 初始ttl(4) | 生存时间(4) | + |e |s |x |u| 版本(4) | 协议(8) | 上层协议(8) | 初始ttl(4) | 生存时间(4) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 源ip地址(32) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -14,7 +14,7 @@ use std::{fmt, io}; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 数据体 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - 注:e为是否加密标志,s为服务端通信包标志,u未使用 + 注:e为是否加密标志,s为服务端通信包标志,x扩展标志,u未使用 */ pub const HEAD_LEN: usize = 12; @@ -128,15 +128,15 @@ impl> NetPacket { "length overflow", )); } - // 不能大于udp最大载荷长度 - if data_len < 12 || data_len > 65535 - 20 - 8 { + if data_len < 12 { return Err(io::Error::new( io::ErrorKind::InvalidData, - "length overflow", + "data_len too short", )); } Ok(NetPacket { data_len, buffer }) } + #[inline] pub fn buffer(&self) -> &[u8] { &self.buffer.as_ref()[..self.data_len] } @@ -214,8 +214,7 @@ impl + AsMut<[u8]>> NetPacket { } pub fn set_gateway_flag(&mut self, is_gateway: bool) { if is_gateway { - // 后面的版本再改为0x40,改了之后不兼容1.2.5之前的版本 - self.buffer.as_mut()[0] = self.buffer.as_ref()[0] | 0x50 + self.buffer.as_mut()[0] = self.buffer.as_ref()[0] | 0x40 } else { self.buffer.as_mut()[0] = self.buffer.as_ref()[0] & 0xBF }; diff --git a/vnt/src/tun_tap_device/create_device.rs b/vnt/src/tun_tap_device/create_device.rs index 5b6fd4e5..7485cde6 100644 --- a/vnt/src/tun_tap_device/create_device.rs +++ b/vnt/src/tun_tap_device/create_device.rs @@ -1,4 +1,6 @@ +use crate::{DeviceConfig, ErrorInfo, ErrorType}; use std::io; +use std::net::Ipv4Addr; use std::sync::Arc; use tun::device::IFace; use tun::Device; @@ -8,8 +10,47 @@ const DEFAULT_TUN_NAME: &str = "vnt-tun"; #[cfg(target_os = "windows")] const DEFAULT_TAP_NAME: &str = "vnt-tap"; -#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] -pub fn create_device(config: &crate::core::Config) -> io::Result> { +pub fn create_device(config: DeviceConfig) -> Result, ErrorInfo> { + let device = match create_device0(&config) { + Ok(device) => device, + Err(e) => { + return Err(ErrorInfo::new_msg( + ErrorType::Unknown, + format!("create device {:?}", e), + )); + } + }; + if let Err(e) = device.set_ip(config.virtual_ip, config.virtual_netmask) { + log::error!("LocalIpExists {:?}", e); + return Err(ErrorInfo::new_msg( + ErrorType::LocalIpExists, + format!("set_ip {:?}", e), + )); + } + if let Err(e) = device.add_route(config.virtual_network, config.virtual_netmask, 1) { + log::warn!("添加默认路由失败 ={:?}", e); + } + if let Err(e) = device.add_route(Ipv4Addr::BROADCAST, Ipv4Addr::BROADCAST, 1) { + log::warn!("添加广播路由失败 ={:?}", e); + } + + if let Err(e) = device.add_route( + Ipv4Addr::from([224, 0, 0, 0]), + Ipv4Addr::from([240, 0, 0, 0]), + 1, + ) { + log::warn!("添加组播路由失败 ={:?}", e); + } + + for (dest, mask) in config.external_route { + if let Err(e) = device.add_route(dest, mask, 1) { + log::warn!("添加路由失败 ={:?}", e); + } + } + Ok(device) +} + +fn create_device0(config: &DeviceConfig) -> io::Result> { #[cfg(target_os = "windows")] let default_name: &str = if config.tap { DEFAULT_TAP_NAME @@ -37,14 +78,7 @@ pub fn create_device(config: &crate::core::Config) -> io::Result> { .unwrap_or(default_name.to_string()), config.tap, )?); - let mtu = config.mtu.unwrap_or_else(|| { - if config.password.is_none() { - 1450 - } else { - 1410 - } - }); - device.set_mtu(mtu)?; + device.set_mtu(config.mtu)?; Ok(device) } diff --git a/vnt/src/tun_tap_device/mod.rs b/vnt/src/tun_tap_device/mod.rs index f5037c8a..9519ae40 100644 --- a/vnt/src/tun_tap_device/mod.rs +++ b/vnt/src/tun_tap_device/mod.rs @@ -1,6 +1,11 @@ #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] +#[cfg(feature = "integrated_tun")] pub use create_device::create_device; #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] +#[cfg(feature = "integrated_tun")] mod create_device; +#[cfg(feature = "integrated_tun")] pub mod tun_create_helper; + +pub mod vnt_device; diff --git a/vnt/src/tun_tap_device/tun_create_helper.rs b/vnt/src/tun_tap_device/tun_create_helper.rs index 67b76657..76babcf0 100644 --- a/vnt/src/tun_tap_device/tun_create_helper.rs +++ b/vnt/src/tun_tap_device/tun_create_helper.rs @@ -4,69 +4,60 @@ use std::sync::Arc; use crossbeam_utils::atomic::AtomicCell; use parking_lot::Mutex; +use tun::device::IFace; use tun::Device; use crate::channel::context::ChannelContext; use crate::cipher::Cipher; use crate::compression::Compressor; use crate::external_route::ExternalRoute; +use crate::handle::tun_tap::DeviceStop; use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo}; #[cfg(feature = "ip_proxy")] use crate::ip_proxy::IpProxyMap; -use crate::util::{SingleU64Adder, StopManager}; -#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] +use crate::tun_tap_device::vnt_device::DeviceWrite; +use crate::util::StopManager; + #[repr(transparent)] -#[derive(Clone)] +#[derive(Clone, Default)] pub struct DeviceAdapter { - tun: Arc, + tun: Arc>>>, } + impl DeviceAdapter { - #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] - pub fn new(tun: Arc) -> Self { - Self { tun } + pub fn insert(&self, device: Arc) { + let r = self.tun.lock().replace(device); + assert!(r.is_none()); } - #[cfg(target_os = "android")] - pub fn new(tun_device_helper: TunDeviceHelper) -> Self { - Self { - tun: Arc::new(AtomicCell::new(-1 as _)), - tun_device_helper, - } + /// 要保证先remove 再insert + pub fn remove(&self) { + drop(self.tun.lock().take()); } } -#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] -impl std::ops::Deref for DeviceAdapter { - type Target = Arc; - fn deref(&self) -> &Self::Target { - &self.tun +impl DeviceWrite for DeviceAdapter { + #[inline] + fn write(&self, buf: &[u8]) -> io::Result { + if let Some(tun) = self.tun.lock().as_ref() { + tun.write(buf) + } else { + Err(io::Error::new(io::ErrorKind::NotFound, "not tun device")) + } } -} -#[cfg(target_os = "android")] -#[derive(Clone)] -pub struct DeviceAdapter { - tun: Arc>, - tun_device_helper: TunDeviceHelper, -} -#[cfg(target_os = "android")] -impl DeviceAdapter { - pub fn write(&self, buf: &[u8]) -> io::Result { - let fd = self.tun.load(); - tun::Fd(fd).write(buf) - } - pub fn start(&self, fd: std::os::fd::RawFd) -> io::Result<()> { - //安卓端fd是由外部释放的,所以这里这么搞免得加锁 - self.tun_device_helper.start(Arc::new(Device::new(fd)?))?; - self.tun.store(fd); - Ok(()) + fn into_device_adapter(self) -> DeviceAdapter { + self } } #[derive(Clone)] pub struct TunDeviceHelper { - inner: Arc>>, + inner: Arc>, + device_adapter: DeviceAdapter, + device_stop: Arc>>, } +#[derive(Clone)] struct TunDeviceHelperInner { stop_manager: StopManager, context: ChannelContext, @@ -76,8 +67,6 @@ struct TunDeviceHelperInner { ip_proxy_map: Option, client_cipher: Cipher, server_cipher: Cipher, - parallel: usize, - up_counter: SingleU64Adder, device_list: Arc)>>, compressor: Compressor, } @@ -91,48 +80,62 @@ impl TunDeviceHelper { #[cfg(feature = "ip_proxy")] ip_proxy_map: Option, client_cipher: Cipher, server_cipher: Cipher, - parallel: usize, - up_counter: SingleU64Adder, device_list: Arc)>>, compressor: Compressor, + device_adapter: DeviceAdapter, ) -> Self { + let inner = TunDeviceHelperInner { + stop_manager, + context, + current_device, + ip_route, + #[cfg(feature = "ip_proxy")] + ip_proxy_map, + client_cipher, + server_cipher, + device_list, + compressor, + }; Self { - inner: Arc::new(AtomicCell::new(Some(TunDeviceHelperInner { - stop_manager, - context, - current_device, - ip_route, - #[cfg(feature = "ip_proxy")] - ip_proxy_map, - client_cipher, - server_cipher, - parallel, - up_counter, - device_list, - compressor, - }))), + inner: Arc::new(Mutex::new(inner)), + device_adapter, + device_stop: Default::default(), } } - pub fn start(&self, device: Arc) -> io::Result<()> { - if let Some(inner) = self.inner.take() { - crate::handle::tun_tap::tun_handler::start( - inner.stop_manager, - inner.context, - device, - inner.current_device, - inner.ip_route, - #[cfg(feature = "ip_proxy")] - inner.ip_proxy_map, - inner.client_cipher, - inner.server_cipher, - inner.parallel, - inner.up_counter, - inner.device_list, - inner.compressor, - )?; - Ok(()) - } else { - Err(io::Error::new(io::ErrorKind::Other, "Repeated start")) + pub fn stop(&self) { + //先停止旧的,再启动新的,改变旧网卡的IP太麻烦 + if let Some(device_stop) = self.device_stop.lock().take() { + self.device_adapter.remove(); + loop { + device_stop.stop(); + std::thread::sleep(std::time::Duration::from_millis(300)); + //确保停止了 + if device_stop.is_stopped() { + break; + } + } } } + /// 要保证先stop 再start + pub fn start(&self, device: Arc) -> io::Result<()> { + self.device_adapter.insert(device.clone()); + let device_stop = DeviceStop::default(); + let s = self.device_stop.lock().replace(device_stop.clone()); + assert!(s.is_none()); + let inner = self.inner.lock().clone(); + crate::handle::tun_tap::tun_handler::start( + inner.stop_manager, + inner.context, + device, + inner.current_device, + inner.ip_route, + #[cfg(feature = "ip_proxy")] + inner.ip_proxy_map, + inner.client_cipher, + inner.server_cipher, + inner.device_list, + inner.compressor, + device_stop, + ) + } } diff --git a/vnt/src/tun_tap_device/vnt_device.rs b/vnt/src/tun_tap_device/vnt_device.rs new file mode 100644 index 00000000..9c3e8cc5 --- /dev/null +++ b/vnt/src/tun_tap_device/vnt_device.rs @@ -0,0 +1,7 @@ +use std::io; + +pub trait DeviceWrite: Clone + Send + Sync + 'static { + fn write(&self, buf: &[u8]) -> io::Result; + #[cfg(feature = "integrated_tun")] + fn into_device_adapter(self) -> crate::tun_tap_device::tun_create_helper::DeviceAdapter; +} diff --git a/vnt/src/util/counter/adder.rs b/vnt/src/util/counter/adder.rs index 38a17498..48880744 100644 --- a/vnt/src/util/counter/adder.rs +++ b/vnt/src/util/counter/adder.rs @@ -1,138 +1,33 @@ -use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; -/// 不安全的并发计数器,谨慎使用 +use crossbeam_utils::atomic::AtomicCell; +#[derive(Clone, Default)] pub struct U64Adder { - global_index: Arc, - inner: Arc, - index: usize, -} -pub struct SingleU64Adder { - inner: Arc, -} -impl SingleU64Adder { - pub fn new() -> Self { - Self { - inner: Arc::new(SingleU64AdderInner::new()), - } - } - pub fn add(&mut self, num: u64) { - self.inner.add(num); - } - pub fn get(&self) -> u64 { - self.inner.get() - } - pub fn watch(&self) -> WatchSingleU64Adder { - WatchSingleU64Adder { - inner: self.inner.clone(), - } - } -} - -struct SingleU64AdderInner { - ptr: *mut u64, -} - -impl SingleU64AdderInner { - fn new() -> Self { - Self { - ptr: Box::into_raw(Box::new(0)), - } - } - #[inline(always)] - fn add(&self, num: u64) { - unsafe { *self.ptr += num } - } - - fn get(&self) -> u64 { - unsafe { *self.ptr } - } -} -impl Drop for SingleU64AdderInner { - fn drop(&mut self) { - unsafe { - let _ = Box::from_raw(self.ptr); - } - } -} - -unsafe impl Send for SingleU64AdderInner {} - -unsafe impl Sync for SingleU64AdderInner {} - -struct U64AdderInner { - base: Vec, -} - -impl U64AdderInner { - pub fn get(&self) -> u64 { - let mut count = 0; - for counter in self.base.iter() { - count += counter.get() - } - count - } + count: Arc>, } impl U64Adder { - /// 计数槽容量 - pub fn with_capacity(capacity: usize) -> Self { - let mut base = Vec::with_capacity(capacity); - for _ in 0..capacity { - base.push(SingleU64AdderInner::new()) - } - let inner = Arc::new(U64AdderInner { base }); - U64Adder { - global_index: Arc::new(AtomicUsize::new(1)), - inner, - index: 0, - } - } - pub fn add(&mut self, num: u64) { - self.inner.base[self.index].add(num); + pub fn add(&self, num: u64) { + self.count.fetch_add(num); } pub fn get(&self) -> u64 { - self.inner.get() + self.count.load() } pub fn watch(&self) -> WatchU64Adder { WatchU64Adder { - inner: self.inner.clone(), - } - } -} - -impl Clone for U64Adder { - fn clone(&self) -> Self { - let index = self.global_index.fetch_add(1, Ordering::AcqRel); - if index > self.inner.base.len() { - panic!() - } - - Self { - global_index: self.global_index.clone(), - inner: self.inner.clone(), - index, + count: self.count.clone(), } } } #[derive(Clone)] pub struct WatchU64Adder { - inner: Arc, + count: Arc>, } impl WatchU64Adder { pub fn get(&self) -> u64 { - self.inner.get() - } -} -#[derive(Clone)] -pub struct WatchSingleU64Adder { - inner: Arc, -} -impl WatchSingleU64Adder { - pub fn get(&self) -> u64 { - self.inner.get() + self.count.load() } } diff --git a/vnt/src/util/limit/mod.rs b/vnt/src/util/limit/mod.rs new file mode 100644 index 00000000..0492bd1a --- /dev/null +++ b/vnt/src/util/limit/mod.rs @@ -0,0 +1,5 @@ +mod rate_limiter; +pub use rate_limiter::*; + +mod traffic_meter; +pub use traffic_meter::*; diff --git a/vnt/src/util/limit/rate_limiter.rs b/vnt/src/util/limit/rate_limiter.rs new file mode 100644 index 00000000..35f8e206 --- /dev/null +++ b/vnt/src/util/limit/rate_limiter.rs @@ -0,0 +1,62 @@ +use parking_lot::Mutex; +use std::sync::Arc; +use std::time::Instant; + +#[derive(Clone)] +pub struct ConcurrentRateLimiter { + inner: Arc>, +} + +impl ConcurrentRateLimiter { + pub fn new(capacity: usize, refill_rate: usize) -> Self { + let inner = RateLimiter::new(capacity, refill_rate); + Self { + inner: Arc::new(Mutex::new(inner)), + } + } + pub fn try_acquire(&self) -> bool { + self.inner.lock().try_acquire() + } +} + +pub struct RateLimiter { + capacity: usize, + tokens: usize, + refill_rate: usize, + last_refill: Instant, +} + +impl RateLimiter { + // 初始化限流器 + pub fn new(capacity: usize, refill_rate: usize) -> Self { + Self { + capacity, + tokens: capacity, + refill_rate, + last_refill: Instant::now(), + } + } + + // 尝试获取一个令牌 + pub fn try_acquire(&mut self) -> bool { + self.refill(); + if self.tokens > 0 { + self.tokens -= 1; + true + } else { + false + } + } + + // 补充令牌 + fn refill(&mut self) { + let now = Instant::now(); + let elapsed = now.duration_since(self.last_refill).as_secs() as usize; + let new_tokens = elapsed * self.refill_rate; + + if new_tokens > 0 { + self.tokens = std::cmp::min(self.capacity, self.tokens + new_tokens); + self.last_refill = now; + } + } +} diff --git a/vnt/src/util/limit/traffic_meter.rs b/vnt/src/util/limit/traffic_meter.rs new file mode 100644 index 00000000..3d4eb50e --- /dev/null +++ b/vnt/src/util/limit/traffic_meter.rs @@ -0,0 +1,132 @@ +use parking_lot::Mutex; +use std::collections::{HashMap, VecDeque}; +use std::net::Ipv4Addr; +use std::sync::Arc; +use std::time::{Duration, Instant}; + +#[derive(Clone)] +pub struct TrafficMeterMultiAddress { + history_capacity: usize, + inner: Arc)>>, +} + +impl Default for TrafficMeterMultiAddress { + fn default() -> Self { + TrafficMeterMultiAddress::new(100) + } +} + +impl TrafficMeterMultiAddress { + pub fn new(history_capacity: usize) -> Self { + let inner = Arc::new(Mutex::new((0, HashMap::new()))); + Self { + inner, + history_capacity, + } + } + pub fn add_traffic(&self, ip: Ipv4Addr, amount: usize) { + let mut guard = self.inner.lock(); + guard.0 += amount as u64; + guard + .1 + .entry(ip) + .or_insert(TrafficMeter::new(self.history_capacity)) + .add_traffic(amount) + } + pub fn total(&self) -> u64 { + self.inner.lock().0 + } + pub fn get_all(&self) -> (u64, HashMap) { + let guard = self.inner.lock(); + ( + guard.0, + guard.1.iter().map(|(ip, t)| (*ip, t.total())).collect(), + ) + } + pub fn get_all_history(&self) -> (u64, HashMap)>) { + let guard = self.inner.lock(); + ( + guard.0, + guard + .1 + .iter() + .map(|(ip, t)| (*ip, (t.total(), t.get_history()))) + .collect(), + ) + } + pub fn get_history(&self, ip: &Ipv4Addr) -> Option<(u64, Vec)> { + self.inner + .lock() + .1 + .get(ip) + .map(|t| (t.total(), t.get_history())) + } +} + +#[derive(Clone)] +pub struct ConcurrentTrafficMeter { + inner: Arc>, +} + +impl ConcurrentTrafficMeter { + pub fn new(history_capacity: usize) -> Self { + let inner = Arc::new(Mutex::new(TrafficMeter::new(history_capacity))); + Self { inner } + } + pub fn add_traffic(&self, amount: usize) { + self.inner.lock().add_traffic(amount) + } + pub fn get_history(&self) -> Vec { + self.inner.lock().get_history() + } +} + +pub struct TrafficMeter { + start_time: Instant, + total: u64, + count: usize, + history_capacity: usize, + history: VecDeque, +} + +impl TrafficMeter { + // 初始化一个新的 TrafficMeter + pub fn new(history_capacity: usize) -> Self { + Self { + start_time: Instant::now(), + total: 0, + count: 0, + history: VecDeque::with_capacity(history_capacity), + history_capacity, + } + } + + // 增加流量计数 + pub fn add_traffic(&mut self, amount: usize) { + self.total += amount as u64; + self.count += amount; + self.check_time(); + } + + // 检查时间是否超过一秒,如果是,记录流量并重置计数器和时间 + fn check_time(&mut self) { + if self.start_time.elapsed() >= Duration::new(1, 0) { + // 将当前计数添加到历史记录 + if self.history.len() >= self.history_capacity { + self.history.pop_front(); // 保持历史记录不超过capacity + } + self.history.push_back(self.count); + + // 重置计数器和时间 + self.count = 0; + self.start_time = Instant::now(); + } + } + pub fn total(&self) -> u64 { + self.total + } + // 获取流量记录 + pub fn get_history(&self) -> Vec { + self.history.iter().cloned().collect() + } +} diff --git a/vnt/src/util/mod.rs b/vnt/src/util/mod.rs index 9fae45b9..fade277d 100644 --- a/vnt/src/util/mod.rs +++ b/vnt/src/util/mod.rs @@ -1,10 +1,17 @@ mod notify; mod scheduler; -pub use notify::StopManager; +pub use notify::{StopManager, Worker}; pub use scheduler::Scheduler; -mod counter; -pub use counter::*; +// mod counter; +// pub use counter::*; mod dns_query; pub use dns_query::*; + +#[cfg(feature = "upnp")] +mod upnp; +#[cfg(feature = "upnp")] +pub use upnp::*; + +pub mod limit; diff --git a/vnt/src/util/notify.rs b/vnt/src/util/notify.rs index defa7005..3495f8c1 100644 --- a/vnt/src/util/notify.rs +++ b/vnt/src/util/notify.rs @@ -28,7 +28,7 @@ impl StopManager { self.inner.add_listener(name, f) } pub fn stop(&self) { - self.inner.stop(""); + self.inner.stop(); } pub fn wait(&self) { self.inner.wait(); @@ -36,8 +36,8 @@ impl StopManager { pub fn wait_timeout(&self, dur: Duration) -> bool { self.inner.wait_timeout(dur) } - pub fn is_stop(&self) -> bool { - self.inner.state.load(Ordering::Acquire) + pub fn is_stopped(&self) -> bool { + self.inner.is_stopped() } } @@ -81,17 +81,17 @@ impl StopManagerInner { guard.1.push((name.clone(), Box::new(f))); Ok(Worker::new(name, self.clone())) } - fn stop(&self, skip_name: &str) { + fn stop(&self) { self.state.store(true, Ordering::Release); let mut guard = self.listeners.lock(); guard.0 = true; - for (name, listener) in guard.1.drain(..) { - if &name == skip_name { - continue; - } + for (_name, listener) in guard.1.drain(..) { listener(); } } + pub fn is_stopped(&self) -> bool { + self.worker_num.load(Ordering::Acquire) == 0 + } fn wait(&self) { { let mut guard = self.park_threads.lock(); @@ -118,6 +118,7 @@ impl StopManagerInner { self.worker_num.load(Ordering::Acquire) == 0 } fn stop_call(&self) { + self.stop(); if let Some(call) = self.stop_call.lock().take() { call(); } @@ -136,6 +137,19 @@ impl Worker { } fn release0(&self) { let inner = &self.inner; + let worker_name = &self.name; + { + let mut mutex_guard = inner.listeners.lock(); + if let Some(pos) = mutex_guard + .1 + .iter() + .position(|(name, _)| name == worker_name) + { + let (_, listener) = mutex_guard.1.remove(pos); + listener(); + } + } + let count = inner.worker_num.fetch_sub(1, Ordering::AcqRel); if count == 1 { for x in inner.park_threads.lock().drain(..) { @@ -145,7 +159,10 @@ impl Worker { } } pub fn stop_all(self) { - self.inner.stop(&self.name) + self.inner.stop() + } + pub fn stop_self(self) { + drop(self) } } diff --git a/vnt/src/util/scheduler.rs b/vnt/src/util/scheduler.rs index 5d5485e1..808cafde 100644 --- a/vnt/src/util/scheduler.rs +++ b/vnt/src/util/scheduler.rs @@ -1,5 +1,8 @@ use crate::util::StopManager; +use crossbeam_utils::atomic::AtomicCell; use std::collections::BinaryHeap; +use std::sync::mpsc::TrySendError; +use std::sync::Arc; use std::{ cmp::Ordering, sync::mpsc::{sync_channel, Receiver, SyncSender}, @@ -10,45 +13,62 @@ struct DelayedTask { f: Box, next: Instant, } + impl Eq for DelayedTask {} + impl PartialEq for DelayedTask { fn eq(&self, other: &Self) -> bool { self.next.eq(&other.next) } } + impl PartialOrd for DelayedTask { fn partial_cmp(&self, other: &Self) -> Option { self.next.partial_cmp(&other.next).map(|ord| ord.reverse()) } } + impl Ord for DelayedTask { fn cmp(&self, other: &Self) -> Ordering { self.next.cmp(&other.next).reverse() } } + enum Op { Task(DelayedTask), Stop, } + #[derive(Clone)] pub struct Scheduler { sender: SyncSender, + state: Arc>, +} + +#[derive(Copy, Clone, Eq, PartialEq)] +enum SchedulerState { + Running, + ShutdownNow, // 立即停止任务执行,队列中剩余的任务不再执行 + _Shutdown, //执行完队列中剩余的任务再停止 } + impl Scheduler { pub fn new(stop_manager: StopManager) -> anyhow::Result { let (sender, receiver) = sync_channel::(32); - let s = Self { sender }; + let state = Arc::new(AtomicCell::new(SchedulerState::Running)); + let s = Self { sender, state }; let s_inner = s.clone(); let worker = { let scheduler = s.clone(); stop_manager.add_listener("Scheduler".into(), move || { - scheduler.shutdown(); + scheduler.shutdown_now(); })? }; std::thread::Builder::new() .name("Scheduler".into()) .spawn(move || { - run(receiver, s_inner); + run(receiver, &s_inner); + s_inner.shutdown_now(); worker.stop_all(); }) .expect("Scheduler"); @@ -58,20 +78,44 @@ impl Scheduler { where F: FnOnce(&Scheduler) + Send + 'static, { + if self.state.load() != SchedulerState::Running { + log::error!("定时任务执行停止"); + return false; + } let task = DelayedTask { f: Box::new(f), next: Instant::now().checked_add(time).unwrap(), }; - self.sender.send(Op::Task(task)).is_ok() + // 如果是任务中调用此方法,那这里用send可能会导致整个定时任务阻塞 + // 任务总数不能大于或等于通道长度,所以改成try_send快速失败 + match self.sender.try_send(Op::Task(task)) { + Ok(_) => true, + Err(e) => { + match e { + TrySendError::Full(_) => { + log::error!("定时任务队列达到上限"); + } + TrySendError::Disconnected(_) => { + log::error!("定时任务执行停止 通道关闭"); + } + } + false + } + } } - pub fn shutdown(self) { + pub fn shutdown_now(&self) { + self.state.store(SchedulerState::ShutdownNow); let _ = self.sender.send(Op::Stop); } } -fn run(receiver: Receiver, s_inner: Scheduler) { + +fn run(receiver: Receiver, s_inner: &Scheduler) { let mut binary_heap = BinaryHeap::::with_capacity(32); loop { while let Some(task) = binary_heap.peek() { + if s_inner.state.load() == SchedulerState::ShutdownNow { + return; + } let now = Instant::now(); if now < task.next { //需要等待对应时间 @@ -89,7 +133,7 @@ fn run(receiver: Receiver, s_inner: Scheduler) { } } else { if let Some(task) = binary_heap.pop() { - (task.f)(&s_inner); + (task.f)(s_inner); } } } @@ -120,6 +164,7 @@ fn run(receiver: Receiver, s_inner: Scheduler) { } } } + fn add_task(op: Op, binary_heap: &mut BinaryHeap) -> bool { return match op { Op::Task(task) => { diff --git a/vnt/src/util/upnp.rs b/vnt/src/util/upnp.rs new file mode 100644 index 00000000..ae3755df --- /dev/null +++ b/vnt/src/util/upnp.rs @@ -0,0 +1,81 @@ +use igd::{search_gateway, PortMappingProtocol}; +use std::net::{Ipv4Addr, SocketAddrV4}; +use std::ops::Deref; +use std::sync::Arc; + +use parking_lot::Mutex; + +#[derive(Clone, Default)] +pub struct UPnP { + inner: Arc, +} + +impl Deref for UPnP { + type Target = UpnpInner; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +#[derive(Default)] +pub struct UpnpInner { + list: Mutex>, +} + +impl UpnpInner { + pub fn add_tcp_port(&self, port: u16) { + self.list.lock().push((PortMappingProtocol::TCP, port)); + } + pub fn add_udp_port(&self, port: u16) { + self.list.lock().push((PortMappingProtocol::UDP, port)); + } + pub fn reset(&self, local_ip: Ipv4Addr) { + let gateway = match search_gateway(Default::default()) { + Ok(gateway) => gateway, + Err(e) => { + log::warn!("search_gateway {:?}", e); + return; + } + }; + let guard = self.list.lock(); + + // 不支持upnp的情况会阻塞30秒,之后再改这个库 + for (protocol, port) in guard.iter() { + let local_addr = SocketAddrV4::new(local_ip, *port); + log::info!("add upnp protocol={} {}", protocol, local_addr); + if let Err(e) = gateway.add_port(*protocol, *port, local_addr, 700, "upnp") { + log::warn!( + "add upnp failed protocol={},port={} err:{:?}", + protocol, + port, + e + ); + } + } + } +} + +impl Drop for UpnpInner { + fn drop(&mut self) { + // let gateway = match search_gateway(Default::default()) { + // Ok(gateway) => gateway, + // Err(e) => { + // log::warn!("search_gateway {:?}", e); + // return; + // } + // }; + // + // let guard = self.list.lock(); + // for (protocol, port) in guard.iter() { + // if let Err(e) = gateway.remove_port(*protocol, *port) { + // log::warn!( + // "remove upnp failed protocol={},port={} err:{:?}", + // protocol, + // port, + // e + // ); + // } + // } + } +} diff --git a/vnt/tun/src/linux/device.rs b/vnt/tun/src/linux/device.rs index 77450a3c..a90a1762 100644 --- a/vnt/tun/src/linux/device.rs +++ b/vnt/tun/src/linux/device.rs @@ -5,8 +5,8 @@ use std::os::fd::AsRawFd; use std::{io, mem, ptr}; use libc::{ - c_char, c_short, ifreq, AF_INET, IFF_MULTI_QUEUE, IFF_NO_PI, IFF_RUNNING, IFF_TUN, - IFF_UP, IFNAMSIZ, O_RDWR, SOCK_DGRAM, + c_char, c_short, ifreq, AF_INET, IFF_MULTI_QUEUE, IFF_NO_PI, IFF_RUNNING, IFF_TUN, IFF_UP, + IFNAMSIZ, O_RDWR, SOCK_DGRAM, }; use crate::device::IFace; diff --git a/vnt/tun/src/unix/fd.rs b/vnt/tun/src/unix/fd.rs index 4c3f9733..7c2f9736 100644 --- a/vnt/tun/src/unix/fd.rs +++ b/vnt/tun/src/unix/fd.rs @@ -62,9 +62,7 @@ impl IntoRawFd for Fd { impl Drop for Fd { fn drop(&mut self) { unsafe { - if self.0 >= 0 { - libc::close(self.0); - } + libc::close(self.0); } } } diff --git a/vnt/tun/src/windows/tun/mod.rs b/vnt/tun/src/windows/tun/mod.rs index 62c0326c..d155c8ff 100644 --- a/vnt/tun/src/windows/tun/mod.rs +++ b/vnt/tun/src/windows/tun/mod.rs @@ -78,7 +78,9 @@ impl Device { )); } wintun_log::set_default_logger_if_unset(&win_tun); - let _ = Self::delete_for_name(&win_tun, &name_utf16); + if Self::delete_for_name(&win_tun, &name_utf16).is_ok() { + std::thread::sleep(std::time::Duration::from_millis(500)); + } let mut guid_bytes: [u8; 16] = [0u8; 16]; rand::thread_rng().fill(&mut guid_bytes); let guid = u128::from_ne_bytes(guid_bytes); @@ -101,7 +103,7 @@ impl Device { )); } // 开启session - let session = win_tun.WintunStartSession(adapter, MAX_RING_CAPACITY); + let session = win_tun.WintunStartSession(adapter, 4 * 1024 * 1024); if session.is_null() { log::error!("session.is_null {:?}", io::Error::last_os_error()); return Err(io::Error::new( @@ -172,7 +174,7 @@ impl IFace for Device { fn shutdown(&self) -> io::Result<()> { unsafe { - if 0 == synchapi::SetEvent(self.shutdown_event) { + if winapi::shared::minwindef::TRUE == synchapi::SetEvent(self.shutdown_event) { Ok(()) } else { Err(io::Error::last_os_error()) @@ -237,7 +239,10 @@ impl Device { if last_error == winapi::shared::winerror::ERROR_NO_MORE_ITEMS { Ok(None) } else { - Err(io::Error::new(io::ErrorKind::Other, "try_receive failed")) + Err(io::Error::new( + io::ErrorKind::Other, + format!("try_receive failed {:?}", io::Error::last_os_error()), + )) } } else { Ok(Some(packet::TunPacket { @@ -254,13 +259,21 @@ impl Device { loop { //Try 16 times to receive without blocking so we don't have to issue a syscall to wait //for the event if packets are being received at a rapid rate - for _i in 0..20 { - match self.try_receive()? { - None => { - continue; - } - Some(packet) => { - return Ok(packet); + for i in 0..20 { + match self.try_receive() { + Ok(data) => match data { + None => { + continue; + } + Some(packet) => { + return Ok(packet); + } + }, + Err(e) => { + if i > 10 { + // 某些系统存在错误退出的情况(原因不明),这里尝试忽略部分错误 + return Err(e); + } } } } @@ -284,11 +297,11 @@ impl Device { if result == winbase::WAIT_OBJECT_0 { //We have data! continue; - } else if result == winbase::WAIT_OBJECT_0 + 1 { + } else { //Shutdown event triggered return Err(io::Error::new( io::ErrorKind::Other, - "Shutdown event triggered", + format!("Shutdown event triggered {}", io::Error::last_os_error()), )); } } @@ -336,8 +349,8 @@ impl Drop for Device { } self.win_tun.WintunEndSession(self.session); self.win_tun.WintunCloseAdapter(self.adapter); - if 0 != self.win_tun.WintunDeleteDriver() { - log::warn!("WintunDeleteDriver failed") + if winapi::shared::minwindef::FALSE == self.win_tun.WintunDeleteDriver() { + log::warn!("WintunDeleteDriver failed {:?}", io::Error::last_os_error()) } } }