Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9badbe180c | ||
|
|
30b1e71aa1 | ||
|
|
b36cc352d5 | ||
|
|
12d888cefc | ||
|
|
ad1df41029 | ||
|
|
67498dfc82 | ||
|
|
cf52fdde57 | ||
|
|
a20082d40b | ||
|
|
8e29b020e0 | ||
|
|
20132861e9 | ||
|
|
a1e9b3c133 | ||
|
|
8a4d21849e | ||
|
|
d2c5aac178 | ||
|
|
066e655c30 | ||
|
|
8627046deb | ||
|
|
76b86bbd0f | ||
|
|
e78994ceb4 | ||
|
|
c63813e590 | ||
|
|
39becec2e5 | ||
|
|
44e379df82 | ||
|
|
b2b58a23c1 | ||
|
|
03529bc339 | ||
|
|
996a80e710 | ||
|
|
ac837077f6 | ||
|
|
b25f330b0c | ||
|
|
c783a96e13 | ||
|
|
999a5b9117 | ||
|
|
6850681352 | ||
|
|
f2b21c9e4a | ||
|
|
d27f065507 | ||
|
|
5e913568a3 | ||
|
|
97b6db3485 | ||
|
|
ae63ce80b5 | ||
|
|
c0e930a3ef | ||
|
|
f8ee8e242f | ||
|
|
20e3ae4d58 | ||
|
|
a910d7a673 | ||
|
|
a4b5b2a028 | ||
|
|
a8a1346c98 | ||
|
|
734e613452 | ||
|
|
e2003d8dea | ||
|
|
f0a2edadfa | ||
|
|
4c174c3aac | ||
|
|
147156d96d | ||
|
|
64d62272a6 | ||
|
|
91713f0fed | ||
|
|
e7f2571469 | ||
|
|
48b41c4b22 | ||
|
|
042cc7c797 | ||
|
|
24c1edff8c | ||
|
|
ec2a74d06f | ||
|
|
63316ee23e | ||
|
|
f0139c68f7 | ||
|
|
590721a4da | ||
|
|
521280537a | ||
|
|
1eb2a0f5cf | ||
|
|
f0ac70c995 | ||
|
|
2570a9ead2 | ||
|
|
54da8ac71f | ||
|
|
bd73ed213e | ||
|
|
20fd060821 | ||
|
|
e8df40925a | ||
|
|
2c0a5d459a | ||
|
|
b67814f125 | ||
|
|
e3f328cc9b | ||
|
|
b6aba36510 | ||
|
|
22720a8f8f | ||
|
|
cc717a4060 | ||
|
|
5264455eba | ||
|
|
b0d6b1f884 | ||
|
|
7cd8647c0c | ||
|
|
19187aff47 | ||
|
|
23b536f771 | ||
|
|
119f719a9f | ||
|
|
9673eaab05 | ||
|
|
390b8dd242 | ||
|
|
1b1663c87e | ||
|
|
7bc4203deb | ||
|
|
c798c36914 | ||
|
|
14e7b518b5 | ||
|
|
88eb3f6514 | ||
|
|
d78e48211d | ||
|
|
55a510aa34 | ||
|
|
8d7eb5ba1b | ||
|
|
ceadadee68 | ||
|
|
e3e2a262e4 | ||
|
|
5850a59ec9 | ||
|
|
318c267c34 | ||
|
|
98bd713a91 | ||
|
|
fe499d0476 | ||
|
|
ff665d7dcf | ||
|
|
573537fbfb | ||
|
|
9e1bd28e2d | ||
|
|
27a6c686a7 | ||
|
|
2f6d743931 | ||
|
|
ec3aa01b9c | ||
|
|
02b988e898 | ||
|
|
b57cd2ed45 | ||
|
|
af5d284470 | ||
|
|
e7f1165287 | ||
|
|
e64e17267d | ||
|
|
7098111ad1 | ||
|
|
57b903ea29 | ||
|
|
d06082fc7a | ||
|
|
77a78daac1 | ||
|
|
d72fab04a2 | ||
|
|
76eaead0b9 | ||
|
|
858ca9bbe7 | ||
|
|
cbc4a7378c | ||
|
|
ee34f525e6 | ||
|
|
698e2531e8 | ||
|
|
16f833ec72 | ||
|
|
94d6caef7e | ||
|
|
364012f9dd | ||
|
|
cf4b1f418f | ||
|
|
c6465977ef | ||
|
|
c577e6381f | ||
|
|
134e31f563 | ||
|
|
0580b89f48 | ||
|
|
37080af275 | ||
|
|
26d68ac059 | ||
|
|
6292c1c381 | ||
|
|
b0c3f25a29 | ||
|
|
d2e09d3da5 | ||
|
|
293c5b90a4 | ||
|
|
e2323361f9 | ||
|
|
d05cff99ee | ||
|
|
e9b1b2ef3b | ||
|
|
a8ea2c14fc | ||
|
|
0688cb4515 | ||
|
|
c3261d7a57 | ||
|
|
ef8d13f61b | ||
|
|
fc104d5dee | ||
|
|
9ad0525216 | ||
|
|
3ea1250b53 | ||
|
|
9df34207f7 | ||
|
|
dacce892ff | ||
|
|
a9495f1d30 | ||
|
|
0892c6eee9 | ||
|
|
f746eadcf1 | ||
|
|
da2371541c | ||
|
|
cca91d4331 | ||
|
|
11aa3b1d2c | ||
|
|
d0b0c61a03 | ||
|
|
b0501db4e5 | ||
|
|
465a5c75ae | ||
|
|
978460aca8 | ||
|
|
4b86752c79 | ||
|
|
bd61cad7b6 | ||
|
|
40cbd2e26b | ||
|
|
2cab580b4e | ||
|
|
3d243fb01d | ||
|
|
ca4e8d14f0 | ||
|
|
9c098c55c9 | ||
|
|
707b07b8d3 | ||
|
|
de5a6971f0 | ||
|
|
056036c4d2 | ||
|
|
1cfb188845 | ||
|
|
73a2c31854 | ||
|
|
92eea536f8 | ||
|
|
00936a923e | ||
|
|
ba69ba78af | ||
|
|
17b206bace | ||
|
|
58d5a4f5da | ||
|
|
2438d14175 | ||
|
|
99f8526799 | ||
|
|
56fcbd64ed | ||
|
|
3766b2b7c1 | ||
|
|
baf0698fe4 | ||
|
|
301938b9fc | ||
|
|
16a37c713a | ||
|
|
d412a769dd | ||
|
|
c8eecc87fd | ||
|
|
6a11db70c8 | ||
|
|
d7c121a756 | ||
|
|
3429ee8bd6 | ||
|
|
4422f9f8b7 | ||
|
|
57ed454c93 | ||
|
|
236205c0f3 | ||
|
|
99b4bf0041 | ||
|
|
9495e39700 | ||
|
|
75e244e3a8 | ||
|
|
bf8397e6e3 | ||
|
|
8cee974866 | ||
|
|
5e018801db | ||
|
|
c4f7f4c19e | ||
|
|
29945eb3da | ||
|
|
7db415d243 | ||
|
|
808dcec795 | ||
|
|
ad8fecc319 | ||
|
|
27ae9a89da | ||
|
|
b3a4a4de5e | ||
|
|
ca76c35f6a | ||
|
|
073c820da6 | ||
|
|
954f0d2d05 | ||
|
|
a943f5bffc | ||
|
|
c3cff7c5b5 | ||
|
|
acb5a8a325 | ||
|
|
1a4e375dbf | ||
|
|
eec7d73ebe | ||
|
|
f4dda173c2 | ||
|
|
96fb8c881d | ||
|
|
aeebbd18fd | ||
|
|
baa71a51eb | ||
|
|
84e70149a1 | ||
|
|
959f2aa783 | ||
|
|
99b2aa9522 | ||
|
|
561fa9f8fe | ||
|
|
ad9dd6a7f7 | ||
|
|
65758eb94c | ||
|
|
fb7ccf4d11 | ||
|
|
b9fe5e0fe2 | ||
|
|
c3368481ad | ||
|
|
cdf5c3a508 | ||
|
|
f9217625e1 | ||
|
|
323f6c9221 | ||
|
|
2e507f1a9d | ||
|
|
e611d69a25 | ||
|
|
45d8c6717d | ||
|
|
756539c3bd | ||
|
|
3d4fcdbc96 | ||
|
|
3cd19dbc1f | ||
|
|
7b770a1bb0 | ||
|
|
0472b1590e | ||
|
|
81764433d8 | ||
|
|
9ee6e101c4 | ||
|
|
565e80b994 | ||
|
|
7ccc4535e4 | ||
|
|
d1bde297d6 | ||
|
|
c5398a51e8 | ||
|
|
fddb59992b | ||
|
|
665e25b1a3 | ||
|
|
fdb4bb1155 | ||
|
|
f104e191ed | ||
|
|
6311d75ac0 | ||
|
|
c3f134e332 | ||
|
|
5bccdf3bbe | ||
|
|
9e881eeecd | ||
|
|
fbe01d8cbf | ||
|
|
d336d938a9 | ||
|
|
598923c95a | ||
|
|
0afe4c4417 | ||
|
|
dff51caf35 | ||
|
|
7c30f2691a | ||
|
|
c6aca2c2dc | ||
|
|
28ac8cf88e | ||
|
|
62b2af54a2 | ||
|
|
a2d45da44d | ||
|
|
d937f392d3 | ||
|
|
2c9abf314a | ||
|
|
ab7abd0c1f | ||
|
|
71a2e3c592 | ||
|
|
aa6d3a6843 | ||
|
|
9b42c5d092 | ||
|
|
2f7817ce5b | ||
|
|
e7c6bcf9a9 | ||
|
|
b6b8971b12 | ||
|
|
ba4a0e008b | ||
|
|
4baccc5047 | ||
|
|
6cf71c8068 | ||
|
|
8e556a20ee | ||
|
|
5f28a5044a | ||
|
|
16d295bf32 | ||
|
|
32163e077e | ||
|
|
6a5a40a7d3 | ||
|
|
161ecc865c | ||
|
|
1a5d40ff2d | ||
|
|
6b988e0612 | ||
|
|
8d44934382 | ||
|
|
6b140b0f71 | ||
|
|
c2b7b02f3f | ||
|
|
24140c2145 | ||
|
|
a6050e5f59 | ||
|
|
50e97fd95f | ||
|
|
890e5f7391 | ||
|
|
be3bf82e35 | ||
|
|
e8af503130 | ||
|
|
466174ef88 | ||
|
|
ba2c792e2a | ||
|
|
17f3fcf9b0 | ||
|
|
11d1ff4b42 | ||
|
|
b640bc50ef | ||
|
|
4bbd5282ee | ||
|
|
d7fd504f8f | ||
|
|
cf4375b405 | ||
|
|
45da060c1a | ||
|
|
4dc84914dd | ||
|
|
c1ee7ac11c | ||
|
|
409b57c171 | ||
|
|
ff4580b9bf | ||
|
|
6daa75d2f2 | ||
|
|
59f07f2d75 | ||
|
|
44035685c8 | ||
|
|
c8d0f3850f | ||
|
|
b846f54d79 | ||
|
|
bb7b1d23af | ||
|
|
9e700b3094 | ||
|
|
38f28e313c | ||
|
|
63f03fb11f | ||
|
|
cf7854906f | ||
|
|
78ae5f3036 | ||
|
|
c412a256b8 | ||
|
|
94def558fc | ||
|
|
25e9c523d3 | ||
|
|
a009d06000 | ||
|
|
675e0c52ff | ||
|
|
dc4219753a | ||
|
|
c003b9acbb | ||
|
|
f4869ebd1d | ||
|
|
de4f42537d | ||
|
|
b9ebe80ef3 | ||
|
|
ea5ba750d4 | ||
|
|
b594afce16 | ||
|
|
ed3c44d6cf | ||
|
|
35ed7f7e45 | ||
|
|
068580e036 | ||
|
|
ea61b06e58 | ||
|
|
f260c26e4f | ||
|
|
9cea433a8b | ||
|
|
38dce9c13b | ||
|
|
04731f1ce5 | ||
|
|
3ddcb629c0 | ||
|
|
12bd058152 | ||
|
|
c9b1bf5a5e |
+96
-29
@@ -2,7 +2,8 @@ name: Rust
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -36,35 +37,48 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# a list of all the targets
|
||||
# 选择使用openssl或者ring,并不是所有平台都支持
|
||||
include:
|
||||
- TARGET: x86_64-unknown-linux-gnu # tested in a debian container on a mac
|
||||
- TARGET: i686-unknown-linux-musl # test in an alpine container on a mac
|
||||
OS: ubuntu-latest
|
||||
FEATURES: default
|
||||
- TARGET: x86_64-unknown-linux-musl # test in an alpine container on a mac
|
||||
OS: ubuntu-latest
|
||||
- TARGET: aarch64-unknown-linux-gnu # tested on aws t4g.nano
|
||||
OS: ubuntu-latest
|
||||
FEATURES: ring-cipher,openssl-vendored
|
||||
- TARGET: aarch64-unknown-linux-musl # tested on aws t4g.nano in alpine container
|
||||
OS: ubuntu-latest
|
||||
- TARGET: armv7-unknown-linux-gnueabihf # raspberry pi 2-3-4, not tested
|
||||
OS: ubuntu-latest
|
||||
FEATURES: ring-cipher,openssl-vendored
|
||||
- TARGET: armv7-unknown-linux-musleabihf # raspberry pi 2-3-4, not tested
|
||||
OS: ubuntu-latest
|
||||
- TARGET: arm-unknown-linux-gnueabihf # raspberry pi 0-1, not tested
|
||||
OS: ubuntu-latest
|
||||
FEATURES: openssl-vendored
|
||||
- TARGET: arm-unknown-linux-musleabihf # raspberry pi 0-1, not tested
|
||||
OS: ubuntu-latest
|
||||
FEATURES: ring-cipher,openssl-vendored
|
||||
- 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
|
||||
- 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
|
||||
- TARGET: i686-pc-windows-msvc # tested on a windows machine
|
||||
OS: windows-2019
|
||||
FEATURES: ring-cipher,openssl-vendored
|
||||
- TARGET: x86_64-pc-windows-msvc # tested on a windows machine
|
||||
OS: windows-latest
|
||||
- TARGET: i686-pc-windows-msvc # tested on a windows machine
|
||||
OS: windows-latest
|
||||
FEATURES: ring-cipher,openssl-vendored
|
||||
- TARGET: mipsel-unknown-linux-musl # openwrt
|
||||
OS: ubuntu-latest
|
||||
FEATURES: openssl-vendored,ring-cipher
|
||||
- TARGET: mips-unknown-linux-musl # openwrt
|
||||
OS: ubuntu-latest
|
||||
FEATURES: openssl-vendored
|
||||
# needs: test
|
||||
runs-on: ${{ matrix.OS }}
|
||||
env:
|
||||
NAME: switch-desktop # change with the name of your project
|
||||
NAME: vnt-cli # change with the name of your project
|
||||
TARGET: ${{ matrix.TARGET }}
|
||||
OS: ${{ matrix.OS }}
|
||||
FEATURES: ${{ matrix.FEATURES }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Init submodules
|
||||
@@ -76,6 +90,9 @@ jobs:
|
||||
~/.cargo/registry
|
||||
./target
|
||||
key: build-cargo-registry-${{matrix.TARGET}}
|
||||
- name: Use strawberry perl
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
run: echo OPENSSL_SRC_PERL=C:/Strawberry/perl/bin/perl >> $GITHUB_ENV
|
||||
- name: List
|
||||
run: find ./
|
||||
- name: Install and configure dependencies
|
||||
@@ -83,28 +100,75 @@ jobs:
|
||||
# dependencies are only needed on ubuntu as that's the only place where
|
||||
# we make cross-compilation
|
||||
if [[ $OS =~ ^ubuntu.*$ ]]; then
|
||||
sudo apt-get install -qq crossbuild-essential-arm64 crossbuild-essential-armhf
|
||||
sudo apt-get update && sudo apt-get install -qq crossbuild-essential-arm64 crossbuild-essential-armhf musl-tools
|
||||
# curl -s musl.cc | grep mipsel
|
||||
case $TARGET in
|
||||
mipsel-unknown-linux-musl)
|
||||
MUSL_URI=mipsel-linux-musl-cross
|
||||
;;
|
||||
aarch64-unknown-linux-musl)
|
||||
MUSL_URI=aarch64-linux-musl-cross
|
||||
;;
|
||||
armv7-unknown-linux-musleabihf)
|
||||
MUSL_URI=armv7l-linux-musleabihf-cross
|
||||
;;
|
||||
arm-unknown-linux-musleabihf)
|
||||
MUSL_URI=arm-linux-musleabihf-cross
|
||||
;;
|
||||
mips-unknown-linux-musl)
|
||||
MUSL_URI=mips-linux-musl-cross
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ $TARGET =~ ^mips.*$ ]]; then
|
||||
# mips平台使用1.71.1版本
|
||||
rustup install 1.71.1
|
||||
rustup default 1.71.1
|
||||
fi
|
||||
|
||||
if [ -n "$MUSL_URI" ]; then
|
||||
mkdir -p ./musl_gcc
|
||||
wget -c https://musl.cc/$MUSL_URI.tgz -P ./musl_gcc/
|
||||
tar zxf ./musl_gcc/$MUSL_URI.tgz -C ./musl_gcc/
|
||||
sudo ln -s $(pwd)/musl_gcc/$MUSL_URI/bin/*gcc /usr/bin/
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
# some additional configuration for cross-compilation on linux
|
||||
cat >>~/.cargo/config <<EOF
|
||||
[target.aarch64-unknown-linux-gnu]
|
||||
linker = "aarch64-linux-gnu-gcc"
|
||||
[target.x86_64-unknown-linux-musl]
|
||||
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
|
||||
[target.aarch64-unknown-linux-musl]
|
||||
linker = "aarch64-linux-gnu-gcc"
|
||||
[target.armv7-unknown-linux-gnueabihf]
|
||||
linker = "arm-linux-gnueabihf-gcc"
|
||||
linker = "aarch64-linux-musl-gcc"
|
||||
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
|
||||
[target.armv7-unknown-linux-musleabihf]
|
||||
linker = "arm-linux-gnueabihf-gcc"
|
||||
[target.arm-unknown-linux-gnueabihf]
|
||||
linker = "arm-linux-gnueabihf-gcc"
|
||||
linker = "armv7l-linux-musleabihf-gcc"
|
||||
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
|
||||
[target.arm-unknown-linux-musleabihf]
|
||||
linker = "arm-linux-gnueabihf-gcc"
|
||||
linker = "arm-linux-musleabihf-gcc"
|
||||
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
|
||||
[target.mipsel-unknown-linux-musl]
|
||||
linker = "mipsel-linux-musl-gcc"
|
||||
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
|
||||
[target.mips-unknown-linux-musl]
|
||||
linker = "mips-linux-musl-gcc"
|
||||
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
|
||||
[target.x86_64-pc-windows-msvc]
|
||||
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
|
||||
[target.i686-pc-windows-msvc]
|
||||
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
|
||||
[target.x86_64-apple-darwin]
|
||||
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
|
||||
[target.aarch64-apple-darwin]
|
||||
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
|
||||
[target.i686-unknown-linux-musl]
|
||||
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
|
||||
EOF
|
||||
- name: Install rust target
|
||||
run: rustup target add $TARGET
|
||||
- name: Run build
|
||||
run: cargo build --package switch-desktop --release --verbose --target $TARGET
|
||||
run: cargo build --package vnt-cli --release --verbose --target $TARGET --features $FEATURES
|
||||
- name: List target
|
||||
run: find ./target
|
||||
- name: Compress
|
||||
@@ -121,15 +185,14 @@ jobs:
|
||||
else
|
||||
TAG=$GITHUB_SHA
|
||||
fi
|
||||
mv ./target/$TARGET/release/$EXEC ./$EXEC
|
||||
tar -czf ./artifacts/$NAME-$TARGET-$TAG.tar.gz $EXEC
|
||||
mv ./target/$TARGET/release/$EXEC ./artifacts/$EXEC
|
||||
tar -czf ./artifacts/$NAME-$TARGET-$TAG.tar.gz -C ./artifacts $EXEC
|
||||
- name: Archive artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: switch-desktop
|
||||
name: vnt-cli
|
||||
path: |
|
||||
./artifacts
|
||||
|
||||
# deploys to github releases on tag
|
||||
deploy:
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
@@ -139,11 +202,15 @@ jobs:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: switch-desktop
|
||||
name: vnt-cli
|
||||
path: ./artifacts
|
||||
- name: List
|
||||
run: find ./artifacts
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
files: ./artifacts/*.tar.gz
|
||||
repo_token: ${{ secrets.YOURTOKEN }}
|
||||
file: ./artifacts/*.tar.gz
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
file_glob: true
|
||||
@@ -1,3 +0,0 @@
|
||||
[submodule "switch/p2p_channel"]
|
||||
path = switch/p2p_channel
|
||||
url = https://github.com/lbl8603/p2p_channel
|
||||
+12
-1
@@ -1,2 +1,13 @@
|
||||
[workspace]
|
||||
members = ["switch","switch-desktop"]
|
||||
members = ["vnt","common","vnt-cli","vnt-jni"]
|
||||
|
||||
[profile.release]
|
||||
opt-level = 'z'
|
||||
debug = 0
|
||||
debug-assertions = false
|
||||
strip= "debuginfo"
|
||||
lto = true
|
||||
panic = 'abort'
|
||||
incremental = false
|
||||
codegen-units = 1
|
||||
rpath = false
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
# switch
|
||||
# Vnt
|
||||
|
||||
A virtual network tool (VPN)
|
||||
|
||||
将不同网络下的多个设备虚拟到一个局域网下
|
||||
|
||||
### vnt-cli参数详解 [参数说明](https://github.com/lbl8603/vnt/blob/main/vnt-cli/README.md)
|
||||
|
||||
### 示例:
|
||||
### 快速使用:
|
||||
|
||||
1. 指定一个token,在多台设备上运行该程序,例如:
|
||||
```shell
|
||||
# linux上
|
||||
root@DESKTOP-0BCHNIO:/opt# ./switch-desktop start --token 123456
|
||||
# 在另一台linux上使用nohup后台运行,不在命令行指定配置时,将在home/.switch/config文件中读取配置
|
||||
[root@izj6cemne76ykdzkataftfz switch]# nohup ./switch-desktop start &
|
||||
root@DESKTOP-0BCHNIO:/opt# ./vnt-cli -k 123456
|
||||
# 在另一台linux上使用nohup后台运行
|
||||
root@izj6cemne76ykdzkataftfz vnt# nohup ./vnt-cli -k 123456 &
|
||||
# windows上
|
||||
D:\switch\bin_v1>switch-desktop.exe start --token 123456
|
||||
D:\vnt\bin_v1>vnt-cli.exe -k 123456
|
||||
```
|
||||
2. 可以执行status命令查看当前设备的虚拟ip
|
||||
2. 可以执行info命令查看当前设备的虚拟ip
|
||||
```shell
|
||||
root@DESKTOP-0BCHNIO:/opt# ./switch-desktop status
|
||||
root@DESKTOP-0BCHNIO:/opt# ./vnt-cli --info
|
||||
Name: Ubuntu 18.04 (bionic) [64-bit]
|
||||
Virtual ip: 10.26.0.2
|
||||
Virtual gateway: 10.26.0.1
|
||||
@@ -30,54 +32,247 @@
|
||||
```
|
||||
3. 也可以执行list命令查看其他设备的虚拟ip
|
||||
```shell
|
||||
root@DESKTOP-0BCHNIO:/opt# ./switch-desktop list
|
||||
root@DESKTOP-0BCHNIO:/opt# ./vnt-cli --list
|
||||
Name Virtual Ip P2P/Relay Rt Status
|
||||
Windows 10.0.22621 (Windows 11 Professional) [64-bit] 10.26.0.3 p2p 2 Online
|
||||
CentOS 7.9.2009 (Core) [64-bit] 10.26.0.4 p2p 35 Online
|
||||
```
|
||||
4. 最后可以用虚拟ip实现设备间相互访问
|
||||
1. ping
|
||||
|
||||
<img width="506" alt="ping" src="https://raw.githubusercontent.com/lbl8603/switch/dev/documents/img/ping.jpg">
|
||||
2. ssh
|
||||
|
||||
<img width="506" alt="ssh" src="https://raw.githubusercontent.com/lbl8603/switch/dev/documents/img/ssh.jpg">
|
||||
<img width="506" alt="ssh" src="https://raw.githubusercontent.com/lbl8603/vnt/dev/documents/img/ssh.jpg">
|
||||
5. 帮助,使用-h命令查看
|
||||
|
||||
### 更多玩法
|
||||
|
||||
1. 和远程桌面(如mstsc)搭配,超低延迟的体验
|
||||
2. 安装samba服务,共享磁盘
|
||||
3. 搭配公网服务器nginx反向代理,在公网访问本地文件
|
||||
|
||||
3. 搭配公网服务器nginx反向代理,在公网访问内网文件或服务
|
||||
4. 点对网,访问内网其他机器、IP代理(结合启动参数'-i'和'-o')
|
||||
|
||||
### 使用须知
|
||||
- token的作用是标识一个虚拟局域网,当使用公共服务器时,建议使用一个唯一值当token(比如uuid),否则有可能连接到其他人创建的虚拟局域网中
|
||||
- 建议指定deviceId,默认使用MAC地址,在某些环境下可能发生变化
|
||||
- 公共服务器目前的配置是2核4G 4Mbps,有需要再扩展~
|
||||
- 需要root/管理员权限
|
||||
- 使用命令行运行
|
||||
- Mac和Linux下需要加可执行权限(例如:chmod +x ./switch-macos)
|
||||
- 自己搭注册和中继服务器(https://github.com/lbl8603/switch-server)
|
||||
### 编译
|
||||
前提条件:安装rust编译环境(https://www.rust-lang.org/zh-CN/tools/install)
|
||||
|
||||
到项目根目录下执行 cargo build -p switch-desktop
|
||||
- token的作用是标识一个虚拟局域网,当使用公共服务器时,建议使用一个唯一值当token(比如uuid),否则有可能连接到其他人创建的虚拟局域网中
|
||||
- 默认使用公共服务器做注册和中继,目前的配置是2核4G 4Mbps,有需要再扩展~
|
||||
- 需要root/管理员权限
|
||||
- vnt-cli需要使用命令行运行
|
||||
- Mac和Linux下需要加可执行权限(例如:chmod +x ./vnt-cli)
|
||||
- 可以自己搭注册和中继服务器([server](https://github.com/lbl8603/vnts))
|
||||
- vnt使用stun服务器探测网络NAT类型,默认使用谷歌和腾讯的stun服务器,也可自己搭建(-e参数指定)
|
||||
|
||||
### 编译
|
||||
|
||||
前提条件:安装rust编译环境([install rust](https://www.rust-lang.org/zh-CN/tools/install))
|
||||
|
||||
```
|
||||
到项目根目录下执行 cargo build -p vnt-cli
|
||||
|
||||
也可按需编译,将得到更小的二进制文件,使用--no-default-features排除默认features
|
||||
|
||||
cargo build -p vnt-cli --no-default-features
|
||||
```
|
||||
|
||||
features说明
|
||||
|
||||
| feature | 说明 | 是否默认 |
|
||||
|------------------|----------------------|------|
|
||||
| openssl | 使用openssl中的aes_ecb算法 | 否 |
|
||||
| openssl-vendored | 从源码编译openssl | 否 |
|
||||
| ring-cipher | 使用ring中的aes_gcm算法 | 否 |
|
||||
| aes_cbc | 支持aes_cbc加密 | 是 |
|
||||
| aes_ecb | 支持aes_ecb加密 | 是 |
|
||||
| aes_gcm | 支持aes_gcm加密 | 是 |
|
||||
| sm4_cbc | 支持sm4_cbc加密 | 是 |
|
||||
| server_encrypt | 支持服务端加密 | 是 |
|
||||
| ip_proxy | 内置ip代理 | 是 |
|
||||
|
||||
### ip转发/代理
|
||||
如果编译时去除了内置的ip代理(或使用--no-proxy关闭了代理),则可以使用网卡NAT转发来实现点对网,
|
||||
一般来说使用网卡NAT转发会比内置的ip代理性能更好
|
||||
<details> <summary>NAT配置可参考如下示例,点击展开</summary>
|
||||
|
||||
### 在出口一端做如下配置
|
||||
注意原有的-i(入口)和-o(出口)的参数不能少
|
||||
|
||||
### windows
|
||||
参考 https://learn.microsoft.com/zh-cn/virtualization/hyper-v-on-windows/user-guide/setup-nat-network
|
||||
```shell
|
||||
#设置nat,名字可以自己取,网段是vnt的网段
|
||||
New-NetNat -Name vntnat -InternalIPInterfaceAddressPrefix 10.26.0.0/24
|
||||
#查看设置
|
||||
Get-NetNat
|
||||
```
|
||||
### linux
|
||||
```shell
|
||||
# 开启ip转发
|
||||
sudo sysctl -w net.ipv4.ip_forward=1
|
||||
# 开启nat转发 表示来源10.26.0.0/24的数据通过nat映射后再从vnt-tun以外的其他网卡发出去
|
||||
sudo iptables -t nat -A POSTROUTING ! -o vnt-tun -s 10.26.0.0/24 -j MASQUERADE
|
||||
# 或者这样 表示来源10.26.0.0/24的数据通过nat映射后再从eth0网卡发出去
|
||||
sudo iptables -t nat -A POSTROUTING -o eth0 -s 10.26.0.0/24 -j MASQUERADE
|
||||
# 查看设置
|
||||
iptables -vnL -t nat
|
||||
```
|
||||
|
||||
### Arch Linux
|
||||
|
||||
[](https://repology.org/project/vnt/versions)
|
||||
|
||||
- 通过 AUR 安装 [vnt-git](https://aur.archlinux.org/packages/vnt-git)
|
||||
|
||||
```bash
|
||||
yay -Syu vnt
|
||||
```
|
||||
|
||||
- 通过 `systemd` 设置开机自启及配置
|
||||
|
||||
```bash
|
||||
sudo systemctl enable --now vnt-cli@
|
||||
sudo systemctl status vnt-cli@
|
||||
```
|
||||
|
||||
- 启用内置 `IPv4` 转发规则
|
||||
|
||||
```bash
|
||||
sudo sysctl --system
|
||||
```
|
||||
|
||||
- 通过内置防火墙文件配置防火墙转发规则
|
||||
|
||||
```bash
|
||||
sudo cat /etc/vnt/iptables-vnt.rules >> /etc/iptables/iptables.rules
|
||||
sudo iptables-restore iptables.rules
|
||||
```
|
||||
|
||||
### macos
|
||||
```shell
|
||||
# 开启ip转发
|
||||
sudo sysctl -w net.ipv4.ip_forward=1
|
||||
# 配置NAT转发规则
|
||||
# 在/etc/pf.conf文件中添加以下规则,en0是出口网卡,10.26.0.0/24是来源网段
|
||||
nat on en0 from 10.26.0.0/24 to any -> (en0)
|
||||
# 加载规则
|
||||
sudo pfctl -f /etc/pf.conf -e
|
||||
```
|
||||
</details>
|
||||
|
||||
### 支持平台
|
||||
|
||||
- Mac
|
||||
- Linux
|
||||
- Arch Linux `yay -Syu vnt`
|
||||
- Windows
|
||||
- 依赖 wintun.dll(https://www.wintun.net/)
|
||||
- 默认使用tun网卡 依赖wintun.dll([win-tun](https://www.wintun.net/))(将dll放到同目录下,建议使用版本0.14.1)
|
||||
- 使用tap网卡 依赖tap-windows([win-tap](https://build.openvpn.net/downloads/releases/))(建议使用版本9.24.7)
|
||||
- Android
|
||||
- [VntApp](https://github.com/lbl8603/VntApp)
|
||||
|
||||
### 特性
|
||||
|
||||
- IP层数据转发
|
||||
- tun虚拟网卡
|
||||
- tap虚拟网卡
|
||||
- NAT穿透
|
||||
- 点对点穿透
|
||||
- 服务端中继转发
|
||||
- 客户端中继转发
|
||||
- IP代理
|
||||
- p2p组播/广播
|
||||
- 客户端数据加密
|
||||
- 服务端数据加密
|
||||
|
||||
### 结构
|
||||
|
||||
<details> <summary>展开</summary>
|
||||
|
||||
<pre>
|
||||
|
||||
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 |unused| 版本(4) | 协议(8) | 上层协议(8) |初始ttl(4)|生存时间(4) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 源ip地址(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 目的ip地址(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 数据体(n) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
| 指纹(96) |
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
注:
|
||||
1. e为是否加密标志,s为服务端通信包标志,unused占两位未使用;
|
||||
2. 开启加密时,数据体为加密后的密文(加密方式取决于密码长度和加密模式),
|
||||
且会存在指纹,指纹使用sha256生成,用于对数据包完整性和真实性的校验
|
||||
</pre>
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
### Todo
|
||||
- 支持安卓
|
||||
- 数据加密
|
||||
|
||||
- 桌面UI(测试中)
|
||||
|
||||
### 常见问题
|
||||
|
||||
<details> <summary>展开</summary>
|
||||
|
||||
#### 问题1: 设置网络地址失败
|
||||
|
||||
##### 可能原因:
|
||||
|
||||
vnt默认使用10.26.0.0/24网段,和本地网络适配器的ip冲突
|
||||
|
||||
##### 解决方法:
|
||||
|
||||
1. 方法一:找到冲突的IP,将其改成别的
|
||||
2. 方法二:自建服务器,指定其他不会冲突的网段
|
||||
3. 方法三:增加参数-d <device-id> ,设置不同的id会让服务端分配不同的IP,从而绕开有冲突的IP
|
||||
|
||||
#### 问题2: windows系统上wintun.dll加载失败
|
||||
|
||||
##### 可能原因:
|
||||
|
||||
没有下载wintun.dll 或者使用的wintun.dll有问题
|
||||
|
||||
##### 解决方法:
|
||||
|
||||
1. 下载最新版的wintun.dll [下载链接](https://www.wintun.net/builds/wintun-0.14.1.zip)
|
||||
2. 解压后找到对应架构的目录,通常是amd64
|
||||
3. 将对应的wintun.dll放到和vnt-cli同目录下(或者放到C盘Windows目录下)
|
||||
4. 再次启动vnt-cli
|
||||
|
||||
#### 问题3: 丢包严重,或是不能正常组网通信
|
||||
|
||||
##### 可能原因:
|
||||
|
||||
某些宽带下(比如广电宽带)UDP丢包严重
|
||||
|
||||
##### 解决方法:
|
||||
|
||||
1. 使用TCP模式中继转发(vnt-cli增加--tcp参数)
|
||||
2. 如果p2p后效果很差,可以选择禁用p2p(vnt-cli增加--use-channel relay 参数)
|
||||
|
||||
#### 问题4:重启后虚拟IP发生变化,或指定了IP不能启动
|
||||
##### 可能原因:
|
||||
设备重启后程序自动获取的id值改变,导致注册时重新分配了新的IP,或是IP冲突
|
||||
##### 解决方法:
|
||||
1. 命令行启动增加-d参数(使用配置文件启动则在配置文件中增加device_id参数),要保证每个设备的值都不一样,取值可以任意64位以内字符串
|
||||
</details>
|
||||
|
||||
### 交流群
|
||||
|
||||
QQ: 1034868233
|
||||
|
||||
### 其他
|
||||
|
||||
可使用社区小伙伴搭建的中继服务器
|
||||
|
||||
1. -s vnt.8443.eu.org:29871
|
||||
|
||||
### 参与贡献
|
||||
|
||||
<a href="https://github.com/lbl8603/vnt/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=lbl8603/vnt" />
|
||||
</a>
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "common"
|
||||
version = "1.2.9"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
@@ -0,0 +1,82 @@
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
pub fn ips_parse(ips: &Vec<String>) -> Result<Vec<(u32, u32, Ipv4Addr)>, String> {
|
||||
let mut in_ips_c = vec![];
|
||||
for x in ips {
|
||||
let mut split = x.split(",");
|
||||
let net = if let Some(net) = split.next() {
|
||||
net
|
||||
} else {
|
||||
return Err("ipv4/mask,ipv4".to_string());
|
||||
};
|
||||
let ip = if let Some(ip) = split.next() {
|
||||
ip
|
||||
} else {
|
||||
return Err("ipv4/mask,ipv4".to_string());
|
||||
};
|
||||
let ip = if let Ok(ip) = ip.parse::<Ipv4Addr>() {
|
||||
ip
|
||||
} else {
|
||||
return Err("not ipv4".to_string());
|
||||
};
|
||||
let mut split = net.split("/");
|
||||
let dest = if let Some(dest) = split.next() {
|
||||
dest
|
||||
} else {
|
||||
return Err("no ipv4/mask".to_string());
|
||||
};
|
||||
let mask = if let Some(mask) = split.next() {
|
||||
mask
|
||||
} else {
|
||||
return Err("no netmask".to_string());
|
||||
};
|
||||
let dest = if let Ok(dest) = dest.parse::<Ipv4Addr>() {
|
||||
dest
|
||||
} else {
|
||||
return Err("not ipv4".to_string());
|
||||
};
|
||||
let mask = to_ip(mask)?;
|
||||
in_ips_c.push((u32::from_be_bytes(dest.octets()), mask, ip));
|
||||
}
|
||||
Ok(in_ips_c)
|
||||
}
|
||||
|
||||
pub fn out_ips_parse(ips: &Vec<String>) -> Result<Vec<(u32, u32)>, String> {
|
||||
let mut in_ips_c = vec![];
|
||||
for x in ips {
|
||||
let mut split = x.split("/");
|
||||
let dest = if let Some(dest) = split.next() {
|
||||
dest
|
||||
} else {
|
||||
return Err("no ipv4/mask".to_string());
|
||||
};
|
||||
let mask = if let Some(mask) = split.next() {
|
||||
mask
|
||||
} else {
|
||||
return Err("no netmask".to_string());
|
||||
};
|
||||
let dest = if let Ok(dest) = dest.parse::<Ipv4Addr>() {
|
||||
dest
|
||||
} else {
|
||||
return Err("not ipv4".to_string());
|
||||
};
|
||||
let mask = to_ip(mask)?;
|
||||
in_ips_c.push((u32::from_be_bytes(dest.octets()), mask));
|
||||
}
|
||||
Ok(in_ips_c)
|
||||
}
|
||||
|
||||
pub fn to_ip(mask: &str) -> Result<u32, String> {
|
||||
if let Ok(m) = mask.parse::<u32>() {
|
||||
if m >= 32 {
|
||||
return Err("not netmask".to_string());
|
||||
}
|
||||
let mut mask = 0 as u32;
|
||||
for i in 0..m {
|
||||
mask = mask | (1 << (31 - i));
|
||||
}
|
||||
Ok(mask)
|
||||
} else {
|
||||
Err("not netmask".to_string())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn get_unique_identifier() -> Option<String> {
|
||||
use std::os::windows::process::CommandExt;
|
||||
use std::process::Command;
|
||||
let output = match Command::new("wmic")
|
||||
.creation_flags(0x08000000)
|
||||
.args(&["csproduct", "get", "UUID"])
|
||||
.output()
|
||||
{
|
||||
Ok(output) => output,
|
||||
Err(_) => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let result = String::from_utf8_lossy(&output.stdout);
|
||||
let identifier = result.lines().nth(1).unwrap_or("").trim();
|
||||
if identifier.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(identifier.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn get_unique_identifier() -> Option<String> {
|
||||
use std::process::Command;
|
||||
let output = match Command::new("ioreg")
|
||||
.args(&["-rd1", "-c", "IOPlatformExpertDevice"])
|
||||
.output()
|
||||
{
|
||||
Ok(output) => output,
|
||||
Err(_) => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let result = String::from_utf8_lossy(&output.stdout);
|
||||
let identifier = result
|
||||
.lines()
|
||||
.find(|line| line.contains("IOPlatformUUID"))
|
||||
.unwrap_or("")
|
||||
.trim();
|
||||
if identifier.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(identifier.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn get_unique_identifier() -> Option<String> {
|
||||
use std::process::Command;
|
||||
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().to_string();
|
||||
if identifier.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(identifier.to_string())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod args_parse;
|
||||
pub mod identifier;
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 31 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 39 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 97 KiB |
@@ -1,36 +0,0 @@
|
||||
[package]
|
||||
name = "switch-desktop"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
switch = {path="../switch"}
|
||||
mac_address = "1.1.4"
|
||||
clap = { version = "4.0.32", features = ["derive"] }
|
||||
console = "0.15.2"
|
||||
dirs = "4.0.0"
|
||||
log = "0.4.17"
|
||||
log4rs = "1.2.0"
|
||||
#tokio = { version = "1.24.1", features = ["full"] }
|
||||
chrono = "0.4.23"
|
||||
|
||||
serde = "1.0"
|
||||
serde_yaml = "0.9"
|
||||
serde_json = "1.0.94"
|
||||
crossbeam = "0.8.2"
|
||||
lazy_static = "1.4.0"
|
||||
parking_lot = "0.12.1"
|
||||
|
||||
fs2 = "0.4.3"
|
||||
|
||||
os_info = "3.5.1"
|
||||
[target.'cfg(any(target_os = "linux",target_os = "macos"))'.dependencies]
|
||||
sudo = "0.6.0"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
winapi = { version = "0.3.9", features = ["handleapi", "processthreadsapi", "winnt", "securitybaseapi", "impl-default"] }
|
||||
#runas = "0.2.1"
|
||||
windows-service = "0.5.0"
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
use std::io;
|
||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::command::entity::{DeviceItem, RouteItem, Status};
|
||||
|
||||
pub struct CommandClient {
|
||||
udp: UdpSocket,
|
||||
}
|
||||
|
||||
impl CommandClient {
|
||||
pub fn new() -> io::Result<Self> {
|
||||
let port = crate::config::read_command_port()?;
|
||||
let udp = UdpSocket::bind("127.0.0.1:0")?;
|
||||
udp.set_read_timeout(Some(Duration::from_secs(2)))?;
|
||||
udp.connect(SocketAddr::V4(SocketAddrV4::new(
|
||||
Ipv4Addr::new(127, 0, 0, 1),
|
||||
port,
|
||||
)))?;
|
||||
Ok(Self { udp })
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandClient {
|
||||
pub fn list(&self) -> io::Result<Vec<DeviceItem>> {
|
||||
self.udp.send(b"list")?;
|
||||
let mut buf = [0; 10240];
|
||||
let len = self.udp.recv(&mut buf)?;
|
||||
match serde_json::from_slice::<Vec<DeviceItem>>(&buf[..len]) {
|
||||
Ok(val) => {
|
||||
Ok(val)
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{:?}",e);
|
||||
Err(io::Error::new(io::ErrorKind::Other, "data error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn route(&self) -> io::Result<Vec<RouteItem>> {
|
||||
self.udp.send(b"route")?;
|
||||
let mut buf = [0; 10240];
|
||||
let len = self.udp.recv(&mut buf)?;
|
||||
match serde_json::from_slice::<Vec<RouteItem>>(&buf[..len]) {
|
||||
Ok(val) => {
|
||||
Ok(val)
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{:?}",e);
|
||||
Err(io::Error::new(io::ErrorKind::Other, "data error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn status(&self) -> io::Result<Status> {
|
||||
self.udp.send(b"status")?;
|
||||
let mut buf = [0; 10240];
|
||||
let len = self.udp.recv(&mut buf)?;
|
||||
match serde_json::from_slice::<Status>(&buf[..len]) {
|
||||
Ok(val) => {
|
||||
Ok(val)
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{:?},{:?}",&buf[..len],e);
|
||||
Err(io::Error::new(io::ErrorKind::Other, "data error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(any(unix))]
|
||||
pub fn stop(&self) -> io::Result<String> {
|
||||
self.udp.send(b"stop")?;
|
||||
let mut buf = [0; 10240];
|
||||
let len = self.udp.recv(&mut buf)?;
|
||||
Ok(String::from_utf8(buf[..len].to_vec()).unwrap())
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
use std::io;
|
||||
use console::style;
|
||||
use crate::console_out;
|
||||
|
||||
pub mod client;
|
||||
pub mod server;
|
||||
pub mod entity;
|
||||
|
||||
pub enum CommandEnum {
|
||||
Route,
|
||||
List,
|
||||
ListAll,
|
||||
Status,
|
||||
#[cfg(any(unix))]
|
||||
Stop,
|
||||
}
|
||||
|
||||
pub fn command(cmd: CommandEnum) {
|
||||
if let Err(e) = command_(cmd) {
|
||||
println!("{}:{:?}", style("连接后台服务错误(Connection background service error)").red(), e);
|
||||
}
|
||||
}
|
||||
|
||||
fn command_(cmd: CommandEnum) -> io::Result<()> {
|
||||
match client::CommandClient::new() {
|
||||
Ok(command_client) => {
|
||||
match cmd {
|
||||
CommandEnum::Route => {
|
||||
let list = command_client.route()?;
|
||||
console_out::console_route_table(list);
|
||||
}
|
||||
CommandEnum::List => {
|
||||
let list = command_client.list()?;
|
||||
console_out::console_device_list(list);
|
||||
}
|
||||
CommandEnum::ListAll => {
|
||||
let list = command_client.list()?;
|
||||
console_out::console_device_list_all(list);
|
||||
}
|
||||
CommandEnum::Status => {
|
||||
let status = command_client.status()?;
|
||||
console_out::console_status(status);
|
||||
}
|
||||
#[cfg(any(unix))]
|
||||
CommandEnum::Stop => {
|
||||
command_client.stop()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{:?}",e);
|
||||
println!(
|
||||
"{}:{:?}",
|
||||
style("连接后台服务错误(Connection background service error)").red(), e
|
||||
);
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
use std::io;
|
||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
|
||||
use std::sync::Arc;
|
||||
|
||||
use switch::core::Switch;
|
||||
use crate::command::entity::{DeviceItem, RouteItem, Status};
|
||||
|
||||
|
||||
pub struct CommandServer {}
|
||||
|
||||
impl CommandServer {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandServer {
|
||||
pub fn start(&self, switch: Arc<Switch>) -> io::Result<()> {
|
||||
let mut port = 21637 as u16;
|
||||
let udp = loop {
|
||||
match UdpSocket::bind(SocketAddr::V4(SocketAddrV4::new(
|
||||
Ipv4Addr::new(127, 0, 0, 1),
|
||||
port,
|
||||
))) {
|
||||
Ok(udp) => {
|
||||
break udp;
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::AddrInUse {
|
||||
port += 1;
|
||||
} else {
|
||||
log::error!("创建udp失败 {:?}", e);
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
crate::config::update_command_port(port)?;
|
||||
let mut buf = [0u8; 64];
|
||||
loop {
|
||||
let (len, addr) = udp.recv_from(&mut buf)?;
|
||||
match std::str::from_utf8(&buf[..len]) {
|
||||
Ok(cmd) => {
|
||||
if let Ok(out) = command(cmd, &switch) {
|
||||
udp.send_to(out.as_bytes(), addr)?;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn command_route(switch: &Switch) -> Vec<RouteItem> {
|
||||
let route_table = switch.route_table();
|
||||
let mut route_list = Vec::with_capacity(route_table.len());
|
||||
for (destination, route) in route_table {
|
||||
let next_hop = switch.route_key(&route.route_key()).map_or(String::new(), |v| v.to_string());
|
||||
let metric = route.metric.to_string();
|
||||
let rt = if route.rt < 0 {
|
||||
"".to_string()
|
||||
} else {
|
||||
route.rt.to_string()
|
||||
};
|
||||
let interface = route.addr.to_string();
|
||||
let item = RouteItem {
|
||||
destination: destination.to_string(),
|
||||
next_hop,
|
||||
metric,
|
||||
rt,
|
||||
interface,
|
||||
};
|
||||
route_list.push(item);
|
||||
}
|
||||
route_list
|
||||
}
|
||||
|
||||
pub fn command_list(switch: &Switch) -> Vec<DeviceItem> {
|
||||
let device_list = switch.device_list();
|
||||
let mut list = Vec::new();
|
||||
for peer in device_list {
|
||||
let name = peer.name;
|
||||
let virtual_ip = peer.virtual_ip.to_string();
|
||||
let (nat_type, public_ips, local_ip) = if let Some(nat_info) = switch.peer_nat_info(&peer.virtual_ip) {
|
||||
let nat_type = format!("{:?}", nat_info.nat_type);
|
||||
let public_ips: Vec<String> = nat_info.public_ips.iter().map(|v| v.to_string()).collect();
|
||||
let public_ips = public_ips.join(",");
|
||||
let local_ip = nat_info.local_ip.to_string();
|
||||
(nat_type, public_ips, local_ip)
|
||||
} else {
|
||||
("".to_string(), "".to_string(), "".to_string())
|
||||
};
|
||||
let (nat_traversal_type, rt) = if let Some(route) = switch.route(&peer.virtual_ip) {
|
||||
let nat_traversal_type = if route.metric == 1 { "p2p" } else { "relay" }.to_string();
|
||||
let rt = if route.rt < 0 {
|
||||
"".to_string()
|
||||
} else {
|
||||
route.rt.to_string()
|
||||
};
|
||||
(nat_traversal_type, rt)
|
||||
} else {
|
||||
("relay".to_string(), "".to_string())
|
||||
};
|
||||
let status = format!("{:?}", peer.status);
|
||||
let item = DeviceItem {
|
||||
name,
|
||||
virtual_ip,
|
||||
nat_type,
|
||||
public_ips,
|
||||
local_ip,
|
||||
nat_traversal_type,
|
||||
rt,
|
||||
status,
|
||||
};
|
||||
list.push(item);
|
||||
}
|
||||
list
|
||||
}
|
||||
|
||||
pub fn command_status(switch: &Switch) -> Status {
|
||||
let current_device = switch.current_device();
|
||||
let nat_info = switch.nat_info();
|
||||
let name = switch.name().to_string();
|
||||
let virtual_ip = current_device.virtual_ip().to_string();
|
||||
let virtual_gateway = current_device.virtual_gateway().to_string();
|
||||
let virtual_netmask = current_device.virtual_netmask.to_string();
|
||||
let connect_status = format!("{:?}", switch.connection_status());
|
||||
let relay_server = current_device.connect_server.to_string();
|
||||
let nat_type = format!("{:?}", nat_info.nat_type);
|
||||
let public_ips: Vec<String> = nat_info.public_ips.iter().map(|v| v.to_string()).collect();
|
||||
let public_ips = public_ips.join(",");
|
||||
let local_ip = nat_info.local_ip.to_string();
|
||||
Status {
|
||||
name,
|
||||
virtual_ip,
|
||||
virtual_gateway,
|
||||
virtual_netmask,
|
||||
connect_status,
|
||||
relay_server,
|
||||
nat_type,
|
||||
public_ips,
|
||||
local_ip,
|
||||
}
|
||||
}
|
||||
|
||||
fn command(cmd: &str, switch: &Switch) -> io::Result<String> {
|
||||
let out_str = match cmd {
|
||||
"route" => {
|
||||
match serde_json::to_string(&command_route(switch)) {
|
||||
Ok(str) => {
|
||||
str
|
||||
}
|
||||
Err(e) => {
|
||||
format!("{:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
"list" => {
|
||||
match serde_json::to_string(&command_list(switch)) {
|
||||
Ok(str) => {
|
||||
str
|
||||
}
|
||||
Err(e) => {
|
||||
format!("{:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
"status" => {
|
||||
match serde_json::to_string(&command_status(switch)) {
|
||||
Ok(str) => {
|
||||
str
|
||||
}
|
||||
Err(e) => {
|
||||
format!("{:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
"stop" => {
|
||||
switch.stop()?;
|
||||
"stopping".to_string()
|
||||
}
|
||||
_ => {
|
||||
format!("command '{}' not fount. \n Try to enter: 'help'\n", cmd)
|
||||
}
|
||||
};
|
||||
Ok(out_str)
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
use std::io;
|
||||
use crate::config::SWITCH_HOME_PATH;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn log_service_init() -> io::Result<()> {
|
||||
log_init_("switch-service.log")
|
||||
}
|
||||
pub fn log_init() -> io::Result<()> {
|
||||
log_init_("switch.log")
|
||||
}
|
||||
pub fn log_init_(file_name:&str) -> io::Result<()> {
|
||||
let home = SWITCH_HOME_PATH.lock().clone();
|
||||
let home = if let Some(home) = home {
|
||||
home
|
||||
} else {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "not found"));
|
||||
};
|
||||
if !home.exists() {
|
||||
std::fs::create_dir(&home)?;
|
||||
}
|
||||
let stderr = log4rs::append::console::ConsoleAppender::builder()
|
||||
.target(log4rs::append::console::Target::Stderr)
|
||||
.build();
|
||||
let logfile = log4rs::append::file::FileAppender::builder()
|
||||
// Pattern: https://docs.rs/log4rs/*/log4rs/encode/pattern/index.html
|
||||
.encoder(Box::new(log4rs::encode::pattern::PatternEncoder::new(
|
||||
"{d(%+)(utc)} [{f}:{L}] {h({l})} {M}:{m}{n}\n",
|
||||
)))
|
||||
.build(home.join(file_name))?;
|
||||
match log4rs::Config::builder()
|
||||
.appender(log4rs::config::Appender::builder().build("logfile", Box::new(logfile)))
|
||||
.appender(
|
||||
log4rs::config::Appender::builder()
|
||||
.filter(Box::new(log4rs::filter::threshold::ThresholdFilter::new(
|
||||
log::LevelFilter::Error,
|
||||
)))
|
||||
.build("stderr", Box::new(stderr)),
|
||||
)
|
||||
.build(
|
||||
log4rs::config::Root::builder()
|
||||
.appender("logfile")
|
||||
.appender("stderr")
|
||||
.build(log::LevelFilter::Info),
|
||||
) {
|
||||
Ok(config) => {
|
||||
let _ = log4rs::init_config(config);
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,262 +0,0 @@
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use parking_lot::Mutex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::StartArgs;
|
||||
|
||||
pub mod log_config;
|
||||
|
||||
pub struct StartConfig {
|
||||
pub name: String,
|
||||
pub token: String,
|
||||
pub server: SocketAddr,
|
||||
pub nat_test_server: Vec<SocketAddr>,
|
||||
pub device_id: String,
|
||||
}
|
||||
|
||||
pub fn default_config(start_args: StartArgs) -> Result<StartConfig, String> {
|
||||
let args_config = read_config();
|
||||
if args_config.is_none() && start_args.token.is_none() {
|
||||
return Err("找不到token(Token not found)".to_string());
|
||||
}
|
||||
let token = start_args.token.unwrap_or_else(|| args_config.as_ref().unwrap().token.clone()).trim().to_string();
|
||||
if token.is_empty() {
|
||||
return Err("token不能为空(Token cannot be empty)".to_string());
|
||||
}
|
||||
if token.len() > 64 {
|
||||
return Err("token不能超过64字符(Token cannot exceed 64 characters)".to_string());
|
||||
}
|
||||
let name = start_args.name.unwrap_or_else(|| {
|
||||
if let Some(c) = &args_config {
|
||||
if !c.name.is_empty() {
|
||||
return c.name.clone();
|
||||
}
|
||||
}
|
||||
os_info::get().to_string()
|
||||
});
|
||||
let name = name.trim();
|
||||
let name = if name.len() > 64 {
|
||||
name[..64].to_string()
|
||||
} else {
|
||||
name.to_string()
|
||||
};
|
||||
let device_id = start_args.device_id.unwrap_or_else(|| {
|
||||
if let Some(c) = &args_config {
|
||||
if !c.device_id.is_empty() {
|
||||
return c.device_id.clone();
|
||||
}
|
||||
}
|
||||
if let Ok(Some(mac_address)) = mac_address::get_mac_address() {
|
||||
mac_address.to_string()
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
});
|
||||
if device_id.is_empty() || device_id.len() > 64 {
|
||||
return Err("设备id不能为空并且长度不能大于64字符(The device id cannot be empty and the length cannot be greater than 64 characters)".to_string());
|
||||
}
|
||||
let server = match start_args.server.unwrap_or_else(|| {
|
||||
if let Some(c) = &args_config {
|
||||
if !c.server.is_empty() {
|
||||
return c.server.clone();
|
||||
}
|
||||
}
|
||||
"nat1.wherewego.top:29871".to_string()
|
||||
}).to_socket_addrs() {
|
||||
Ok(mut server) => {
|
||||
if let Some(addr) = server.next() {
|
||||
addr
|
||||
} else {
|
||||
return Err("中继服务器地址错误( Relay server address error)".to_string());
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(format!("中继服务器地址错误( Relay server address error) :{:?}", e));
|
||||
}
|
||||
};
|
||||
let nat_test_server = start_args.nat_test_server.unwrap_or_else(|| {
|
||||
if let Some(c) = &args_config {
|
||||
if !c.nat_test_server.is_empty() {
|
||||
return c.nat_test_server.join(",");
|
||||
}
|
||||
}
|
||||
"nat1.wherewego.top:35061,nat1.wherewego.top:35062,nat2.wherewego.top:35061,nat2.wherewego.top:35062".to_string()
|
||||
}).split(",").flat_map(|a| a.to_socket_addrs()).flatten()
|
||||
.collect::<Vec<_>>();
|
||||
if nat_test_server.is_empty() {
|
||||
return Err("NAT检测服务地址错误(NAT detection service address error)".to_string());
|
||||
}
|
||||
let base_config = StartConfig {
|
||||
name,
|
||||
token,
|
||||
server,
|
||||
nat_test_server,
|
||||
device_id,
|
||||
};
|
||||
Ok(base_config)
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref CONFIG: Mutex<Option<ArgsConfig>> = Mutex::new(None);
|
||||
pub static ref SWITCH_HOME_PATH: Mutex<Option<PathBuf>> = Mutex::new(None);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct ArgsConfig {
|
||||
#[serde(default = "default_version")]
|
||||
pub version: String,
|
||||
#[serde(default = "default_str")]
|
||||
pub token: String,
|
||||
#[serde(default = "default_str")]
|
||||
pub name: String,
|
||||
pub command_port: Option<u16>,
|
||||
#[serde(default = "default_str")]
|
||||
pub server: String,
|
||||
#[serde(default = "default_resource_vec")]
|
||||
pub nat_test_server: Vec<String>,
|
||||
#[serde(default = "default_str")]
|
||||
pub device_id: String,
|
||||
#[serde(default = "default_pid")]
|
||||
pub pid: u32,
|
||||
}
|
||||
|
||||
fn default_version() -> String {
|
||||
"1.0".to_string()
|
||||
}
|
||||
|
||||
fn default_str() -> String {
|
||||
"".to_string()
|
||||
}
|
||||
|
||||
fn default_resource_vec() -> Vec<String> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn default_pid() -> u32 {
|
||||
0
|
||||
}
|
||||
|
||||
impl ArgsConfig {
|
||||
pub fn new(token: String, name: String, server: String, nat_test_server: Vec<String>, device_id: String) -> Self {
|
||||
Self {
|
||||
version: "1.0".to_string(),
|
||||
token,
|
||||
name,
|
||||
command_port: None,
|
||||
server,
|
||||
nat_test_server,
|
||||
device_id,
|
||||
pid: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn lock_file() -> io::Result<File> {
|
||||
let path = SWITCH_HOME_PATH.lock().clone().unwrap().join(".lock");
|
||||
Ok(File::create(path)?)
|
||||
}
|
||||
|
||||
pub fn save_config(config: ArgsConfig) -> io::Result<()> {
|
||||
let config_path = SWITCH_HOME_PATH.lock().clone().unwrap().join("config");
|
||||
save_config_(config, config_path)
|
||||
}
|
||||
|
||||
fn save_config_(config: ArgsConfig, config_path: PathBuf) -> io::Result<()> {
|
||||
let mut config_lock = CONFIG.lock();
|
||||
config_lock.take();
|
||||
let str = serde_yaml::to_string(&config).unwrap();
|
||||
let mut file = File::create(config_path)?;
|
||||
file.write_all(str.as_bytes())
|
||||
}
|
||||
|
||||
pub fn update_pid(pid: u32) -> io::Result<()> {
|
||||
let home_lock = SWITCH_HOME_PATH.lock();
|
||||
if let Some(home) = home_lock.clone() {
|
||||
drop(home_lock);
|
||||
let config_path = home.join("config");
|
||||
if let Some(mut config) = read_config() {
|
||||
config.pid = pid;
|
||||
return save_config_(config, config_path);
|
||||
}
|
||||
}
|
||||
Err(io::Error::new(io::ErrorKind::Other, "not found"))
|
||||
}
|
||||
|
||||
#[cfg(any(unix))]
|
||||
pub fn read_pid() -> io::Result<u32> {
|
||||
let home = SWITCH_HOME_PATH.lock().clone().unwrap();
|
||||
let config = read_config_(home)?;
|
||||
Ok(config.pid)
|
||||
}
|
||||
|
||||
pub fn update_command_port(port: u16) -> io::Result<()> {
|
||||
let home_lock = SWITCH_HOME_PATH.lock();
|
||||
if let Some(home) = home_lock.clone() {
|
||||
drop(home_lock);
|
||||
let config_path = home.join("config");
|
||||
if let Some(mut config) = read_config() {
|
||||
config.command_port = Some(port);
|
||||
return save_config_(config, config_path);
|
||||
}
|
||||
}
|
||||
Err(io::Error::new(io::ErrorKind::Other, "not found"))
|
||||
}
|
||||
|
||||
pub fn read_command_port() -> io::Result<u16> {
|
||||
let home = SWITCH_HOME_PATH.lock().clone().unwrap();
|
||||
let config = read_config_(home)?;
|
||||
if let Some(p) = config.command_port {
|
||||
Ok(p)
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "not fount config"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_config() -> Option<ArgsConfig> {
|
||||
let mut lock = CONFIG.lock();
|
||||
let c = lock.clone();
|
||||
if c.is_some() {
|
||||
return c;
|
||||
}
|
||||
if let Some(home) = SWITCH_HOME_PATH.lock().clone() {
|
||||
match read_config_(home.to_path_buf()) {
|
||||
Ok(config) => {
|
||||
lock.replace(config.clone());
|
||||
Some(config)
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{:?},path:{:?}", e,home);
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_home(home: PathBuf) {
|
||||
SWITCH_HOME_PATH.lock().replace(home);
|
||||
}
|
||||
|
||||
fn read_config_(home: PathBuf) -> io::Result<ArgsConfig> {
|
||||
let config_path = home.join("config");
|
||||
let mut file = if config_path.exists() {
|
||||
File::open(config_path)?
|
||||
} else {
|
||||
OpenOptions::new().read(true).write(true).truncate(false).create(true).open(config_path)?
|
||||
};
|
||||
let mut str = String::new();
|
||||
file.read_to_string(&mut str)?;
|
||||
match serde_yaml::from_str::<ArgsConfig>(&str) {
|
||||
Ok(config) => Ok(config),
|
||||
Err(e) => {
|
||||
log::warn!("{:?}", e);
|
||||
Err(io::Error::new(io::ErrorKind::Other, "config error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
use console::{style, Style};
|
||||
|
||||
use crate::command::entity::{DeviceItem, RouteItem, Status};
|
||||
|
||||
pub mod table;
|
||||
|
||||
pub fn console_status(status: Status) {
|
||||
println!("Name: {}", style(status.name).green());
|
||||
println!("Virtual ip: {}", style(status.virtual_ip).green());
|
||||
println!("Virtual gateway: {}", style(status.virtual_gateway).green());
|
||||
println!("Virtual netmask: {}", style(status.virtual_netmask).green());
|
||||
println!("Connection status: {}", style(status.connect_status).green());
|
||||
println!("NAT type: {}", style(status.nat_type).green());
|
||||
println!("Relay server: {}", style(status.relay_server).green());
|
||||
println!("Public ips: {}", style(status.public_ips).green());
|
||||
println!("Local ip: {}", style(status.local_ip).green());
|
||||
}
|
||||
|
||||
pub fn console_route_table(mut list: Vec<RouteItem>) {
|
||||
if list.is_empty() {
|
||||
println!("No route found");
|
||||
return;
|
||||
}
|
||||
list.sort_by(|t1, t2| t1.destination.cmp(&t2.destination));
|
||||
let mut out_list = Vec::with_capacity(list.len());
|
||||
|
||||
out_list.push(vec![("Destination".to_string(), Style::new()),
|
||||
("Next Hop".to_string(), Style::new()),
|
||||
("Metric".to_string(), Style::new()),
|
||||
("Rt".to_string(), Style::new()),
|
||||
("Interface".to_string(), Style::new()), ]);
|
||||
for item in list {
|
||||
out_list.push(vec![(item.destination, Style::new().green()),
|
||||
(item.next_hop, Style::new().green()),
|
||||
(item.metric, Style::new().green()),
|
||||
(item.rt, Style::new().green()),
|
||||
(item.interface, Style::new().green())]);
|
||||
}
|
||||
|
||||
table::println_table(out_list)
|
||||
}
|
||||
|
||||
pub fn console_device_list(mut list: Vec<DeviceItem>) {
|
||||
if list.is_empty() {
|
||||
println!("No other devices found");
|
||||
return;
|
||||
}
|
||||
list.sort_by(|t1, t2| t1.virtual_ip.cmp(&t2.virtual_ip));
|
||||
list.sort_by(|t1, t2| t1.status.cmp(&t2.status));
|
||||
let mut out_list = Vec::with_capacity(list.len());
|
||||
//表头
|
||||
out_list.push(vec![("Name".to_string(), Style::new()),
|
||||
("Virtual Ip".to_string(), Style::new()),
|
||||
("Status".to_string(), Style::new()),
|
||||
("P2P/Relay".to_string(), Style::new()),
|
||||
("Rt".to_string(), Style::new())]);
|
||||
for item in list {
|
||||
if &item.status == "Online" {
|
||||
if &item.nat_traversal_type == "p2p" {
|
||||
out_list.push(vec![(item.name, Style::new().green()),
|
||||
(item.virtual_ip, Style::new().green()),
|
||||
(item.status, Style::new().green()),
|
||||
(item.nat_traversal_type, Style::new().green()),
|
||||
(item.rt, Style::new().green())]);
|
||||
} else {
|
||||
out_list.push(vec![(item.name, Style::new().yellow()),
|
||||
(item.virtual_ip, Style::new().yellow()),
|
||||
(item.status, Style::new().yellow()),
|
||||
(item.nat_traversal_type, Style::new().yellow()),
|
||||
(item.rt, Style::new().yellow())]);
|
||||
}
|
||||
} else {
|
||||
out_list.push(vec![(item.name, Style::new().color256(102)),
|
||||
(item.virtual_ip, Style::new().color256(102)),
|
||||
(item.status, Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102))]);
|
||||
}
|
||||
}
|
||||
table::println_table(out_list)
|
||||
}
|
||||
|
||||
pub fn console_device_list_all(mut list: Vec<DeviceItem>) {
|
||||
if list.is_empty() {
|
||||
println!("No other devices found");
|
||||
return;
|
||||
}
|
||||
list.sort_by(|t1, t2| t1.virtual_ip.cmp(&t2.virtual_ip));
|
||||
list.sort_by(|t1, t2| t1.status.cmp(&t2.status));
|
||||
let mut out_list = Vec::with_capacity(list.len());
|
||||
//表头
|
||||
out_list.push(vec![("Name".to_string(), Style::new()),
|
||||
("Virtual Ip".to_string(), Style::new()),
|
||||
("Status".to_string(), Style::new()),
|
||||
("NAT Type".to_string(), Style::new()),
|
||||
("Public Ips".to_string(), Style::new()),
|
||||
("Local Ip".to_string(), Style::new()),
|
||||
("P2P/Relay".to_string(), Style::new()),
|
||||
("Rt".to_string(), Style::new())]);
|
||||
for item in list {
|
||||
if &item.status == "Online" {
|
||||
if &item.nat_traversal_type == "p2p" {
|
||||
out_list.push(vec![(item.name, Style::new().green()),
|
||||
(item.virtual_ip, Style::new().green()),
|
||||
(item.status, Style::new().green()),
|
||||
(item.nat_traversal_type, Style::new().green()),
|
||||
(item.rt, Style::new().green()),
|
||||
(item.nat_type, Style::new().green()),
|
||||
(item.public_ips, Style::new().green()),
|
||||
(item.local_ip, Style::new().green())]);
|
||||
} else {
|
||||
out_list.push(vec![(item.name, Style::new().yellow()),
|
||||
(item.virtual_ip, Style::new().yellow()),
|
||||
(item.status, Style::new().yellow()),
|
||||
(item.nat_traversal_type, Style::new().yellow()),
|
||||
(item.rt, Style::new().yellow()),
|
||||
(item.nat_type, Style::new().yellow()),
|
||||
(item.public_ips, Style::new().yellow()),
|
||||
(item.local_ip, Style::new().yellow()), ]);
|
||||
}
|
||||
} else {
|
||||
out_list.push(vec![(item.name, Style::new().color256(102)),
|
||||
(item.virtual_ip, Style::new().color256(102)),
|
||||
(item.status, Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)), ]);
|
||||
}
|
||||
}
|
||||
table::println_table(out_list)
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use clap::{Parser, Subcommand};
|
||||
use console::style;
|
||||
|
||||
use switch::core::Switch;
|
||||
|
||||
use crate::config::log_config::log_init;
|
||||
|
||||
mod command;
|
||||
mod config;
|
||||
#[cfg(target_os = "windows")]
|
||||
mod windows;
|
||||
|
||||
#[cfg(any(unix))]
|
||||
mod unix;
|
||||
mod console_out;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(
|
||||
author = "Lu Beilin",
|
||||
version,
|
||||
about = "一个虚拟网络工具,启动后会获取一个ip,相同token下的设备之间可以用ip直接通信"
|
||||
)]
|
||||
pub struct BaseArgs {
|
||||
#[clap(subcommand)]
|
||||
command: Commands,
|
||||
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
enum Commands {
|
||||
/// 启动
|
||||
Start(StartArgs),
|
||||
/// 停止后台服务
|
||||
Stop,
|
||||
/// 安装服务
|
||||
/// Install service
|
||||
#[cfg(target_os = "windows")]
|
||||
Install(InstallArgs),
|
||||
/// 卸载服务
|
||||
/// Uninstall service
|
||||
#[cfg(target_os = "windows")]
|
||||
Uninstall,
|
||||
/// 配置
|
||||
#[cfg(target_os = "windows")]
|
||||
Config(ConfigArgs),
|
||||
/// 查看路由
|
||||
/// View route
|
||||
Route,
|
||||
/// 查看设备列表
|
||||
/// View device list
|
||||
List {
|
||||
/// 查看所有
|
||||
#[arg(short, long)]
|
||||
all: bool
|
||||
},
|
||||
/// 查看设备当前状态
|
||||
/// View the current status of the device
|
||||
Status,
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct StartArgs {
|
||||
/// 不超过64个字符
|
||||
/// 相同token的设备之间才能通信。
|
||||
/// 建议使用uuid保证唯一性。
|
||||
/// No more than 64 characters
|
||||
/// Only devices with the same token can communicate with each other.
|
||||
/// It is recommended to use uuid to ensure uniqueness
|
||||
#[arg(long)]
|
||||
token: Option<String>,
|
||||
/// 给设备一个名称,为空时默认用系统版本信息
|
||||
/// Give the device a name. If it is blank, the system version information will be used by default
|
||||
#[arg(long, action)]
|
||||
name: Option<String>,
|
||||
/// 设备唯一标识,为空时默认使用MAC地址,不超过64个字符
|
||||
/// Unique identification of the device. If it is blank, the MAC address is used by default. No more than 64 characters
|
||||
#[arg(long)]
|
||||
device_id: Option<String>,
|
||||
/// 注册和中继服务器地址
|
||||
/// Register and relay server address
|
||||
#[arg(long)]
|
||||
server: Option<String>,
|
||||
/// NAT检测服务地址,使用逗号分隔
|
||||
/// NAT detection service address. Use comma to separate
|
||||
#[arg(long)]
|
||||
nat_test_server: Option<String>,
|
||||
/// 关闭命令服务,关闭后不能在其他进程直接使用route、list等命令查看信息
|
||||
/// Turn off the command service. After turning off, you cannot directly use the route, list and other commands to view information in other processes
|
||||
#[cfg(any(unix))]
|
||||
#[arg(long)]
|
||||
off_command_server: bool,
|
||||
/// 记录日志,输出在 home/.switch 目录下,长时间使用时不建议开启
|
||||
/// Output the log in the "home/.switch" directory
|
||||
#[arg(long)]
|
||||
log: bool,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct InstallArgs {
|
||||
/// 安装路径
|
||||
/// Service installation path
|
||||
#[arg(long)]
|
||||
path: String,
|
||||
/// 服务开机自启动
|
||||
/// Autostart on system startup
|
||||
#[arg(long)]
|
||||
auto: bool,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct ConfigArgs {
|
||||
/// 服务开机自启动
|
||||
/// Autostart on system startup
|
||||
#[arg(long)]
|
||||
auto: bool,
|
||||
}
|
||||
|
||||
|
||||
#[cfg(windows)]
|
||||
fn main() {
|
||||
let args: Vec<_> = std::env::args().collect();
|
||||
if args.len() == 3 && args[1] == windows::SERVICE_FLAG {
|
||||
//以服务的方式启动
|
||||
config::set_home(std::path::PathBuf::from(&args[2]));
|
||||
windows::service::start();
|
||||
return;
|
||||
} else {
|
||||
let home = dirs::home_dir().unwrap().join(".switch");
|
||||
config::set_home(home);
|
||||
let args = BaseArgs::parse();
|
||||
if let Commands::Start(start_args) = &args.command {
|
||||
if start_args.log {
|
||||
let _ = log_init();
|
||||
}
|
||||
}
|
||||
windows::main0(args);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
fn main() {
|
||||
let home = dirs::home_dir().unwrap().join(".switch");
|
||||
config::set_home(home);
|
||||
let args = BaseArgs::parse();
|
||||
if let Commands::Start(start_args) = &args.command {
|
||||
if start_args.log {
|
||||
let _ = log_init();
|
||||
}
|
||||
}
|
||||
unix::main0(args);
|
||||
}
|
||||
|
||||
pub fn console_listen(switch: &Switch) {
|
||||
use console::Term;
|
||||
let term = Term::stdout();
|
||||
println!("{}", style("启动成功 started").green());
|
||||
let current_device = switch.current_device();
|
||||
println!(
|
||||
"当前虚拟ip(virtual ip): {:?}",
|
||||
style(current_device.virtual_ip()).green()
|
||||
);
|
||||
println!(
|
||||
"虚拟网关(virtual gateway): {:?}",
|
||||
style(current_device.virtual_gateway()).green()
|
||||
);
|
||||
loop {
|
||||
println!(
|
||||
"{}",
|
||||
style("Please enter the command (Usage: list,status,exit,help):").color256(102)
|
||||
);
|
||||
match term.read_line() {
|
||||
Ok(cmd) => {
|
||||
if cmd.is_empty() {
|
||||
log::warn!("非正常返回");
|
||||
return;
|
||||
}
|
||||
if command(cmd.trim(), &switch).is_err() {
|
||||
println!("{}", style("stopping").red());
|
||||
if let Err(e) = switch.stop() {
|
||||
println!("stop:{:?}", e);
|
||||
}
|
||||
thread::sleep(Duration::from_secs(2));
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("read_line:{:?}", e);
|
||||
println!("{}", style("stopping...").red());
|
||||
if let Err(e) = switch.stop() {
|
||||
log::error!("stop:{:?}", e);
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("{}", style("stopped").red());
|
||||
}
|
||||
|
||||
|
||||
fn command(cmd: &str, switch: &Switch) -> Result<(), ()> {
|
||||
match cmd {
|
||||
"route" => {
|
||||
let list = command::server::command_route(switch);
|
||||
console_out::console_route_table(list);
|
||||
}
|
||||
"list" => {
|
||||
let list = command::server::command_list(switch);
|
||||
console_out::console_device_list(list);
|
||||
}
|
||||
"status" => {
|
||||
let status = command::server::command_status(switch);
|
||||
console_out::console_status(status);
|
||||
}
|
||||
"help" | "h" => {
|
||||
println!("Options: ");
|
||||
println!(
|
||||
"{} , Query the virtual IP of other devices",
|
||||
style("list").green()
|
||||
);
|
||||
println!("{} , View current device status", style("status").green());
|
||||
println!("{} , Exit the program", style("exit").green());
|
||||
}
|
||||
"exit" => {
|
||||
return Err(());
|
||||
}
|
||||
_ => {
|
||||
println!("command '{}' not fount. ", style(cmd).red());
|
||||
println!("Try to enter: '{}'", style("help").green());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use console::style;
|
||||
use fs2::FileExt;
|
||||
|
||||
use switch::core::{Config, Switch};
|
||||
|
||||
use crate::{BaseArgs, Commands, config};
|
||||
use crate::command::{command, CommandEnum};
|
||||
|
||||
|
||||
pub fn main0(base_args: BaseArgs) {
|
||||
match base_args.command {
|
||||
Commands::Start(args) => {
|
||||
let off_command_server = args.off_command_server;
|
||||
match config::default_config(args) {
|
||||
Ok(start_config) => {
|
||||
if sudo::RunningAs::Root != sudo::check() {
|
||||
println!(
|
||||
"{}",
|
||||
style("需要使用root权限执行(Need to execute with root permission)...").red()
|
||||
);
|
||||
sudo::escalate_if_needed().unwrap();
|
||||
}
|
||||
|
||||
let config = Config::new(
|
||||
start_config.token.clone(),
|
||||
start_config.device_id.clone(),
|
||||
start_config.name.clone(),
|
||||
start_config.server,
|
||||
start_config.nat_test_server.clone(),
|
||||
);
|
||||
let nat_test_server = start_config.nat_test_server.iter().map(|v| v.to_string()).collect::<Vec<String>>();
|
||||
let args_config = config::ArgsConfig::new(
|
||||
start_config.token.clone(),
|
||||
start_config.name.clone(),
|
||||
start_config.server.to_string(),
|
||||
nat_test_server,
|
||||
start_config.device_id.clone(),
|
||||
);
|
||||
let lock = match config::lock_file() {
|
||||
Ok(lock) => {
|
||||
lock
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{:?}",e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
if lock.try_lock_exclusive().is_err() {
|
||||
println!("{}", style("文件被重复打开").red());
|
||||
return;
|
||||
}
|
||||
if let Err(e) = config::save_config(args_config) {
|
||||
log::error!("{:?}",e);
|
||||
lock.unlock().unwrap();
|
||||
return;
|
||||
}
|
||||
let switch = match Switch::start(config) {
|
||||
Ok(switch) => {
|
||||
switch
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{:?}", e);
|
||||
lock.unlock().unwrap();
|
||||
return;
|
||||
}
|
||||
};
|
||||
let switch = Arc::new(switch);
|
||||
let command_server = crate::command::server::CommandServer::new();
|
||||
if off_command_server {
|
||||
crate::console_listen(&switch);
|
||||
log::info!("前台任务结束");
|
||||
} else {
|
||||
if let Err(e) = config::update_pid(std::process::id()) {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
let switch1 = switch.clone();
|
||||
let handle = std::thread::Builder::new().name("cmd-server".into()).spawn(move || {
|
||||
if let Err(e) = command_server.start(switch1) {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
}).unwrap();
|
||||
crate::console_listen(&switch);
|
||||
if let Err(e) = handle.join() {
|
||||
log::error!("后台任务异常{:?}",e);
|
||||
} else {
|
||||
log::info!("后台任务结束");
|
||||
}
|
||||
}
|
||||
lock.unlock().unwrap();
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Commands::Stop => {
|
||||
if sudo::RunningAs::Root != sudo::check() {
|
||||
println!(
|
||||
"{}",
|
||||
style("需要使用root权限执行(Need to execute with root permission)...").red()
|
||||
);
|
||||
sudo::escalate_if_needed().unwrap();
|
||||
}
|
||||
command(CommandEnum::Stop);
|
||||
if let Ok(pid) = config::read_pid() {
|
||||
if pid != 0 {
|
||||
let kill_cmd = format!("kill {}", pid);
|
||||
let kill_out = std::process::Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(&kill_cmd)
|
||||
.output()
|
||||
.expect("sh exec error!");
|
||||
if !kill_out.status.success() {
|
||||
println!("cmd:{:?},err:{:?}", kill_cmd, kill_out);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("stopped")
|
||||
}
|
||||
Commands::Route => {
|
||||
command(CommandEnum::Route);
|
||||
}
|
||||
Commands::List { all } => {
|
||||
if all {
|
||||
command(CommandEnum::ListAll);
|
||||
} else {
|
||||
command(CommandEnum::List);
|
||||
}
|
||||
}
|
||||
Commands::Status => {
|
||||
command(CommandEnum::Status);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,358 +0,0 @@
|
||||
use std::{io, thread};
|
||||
use std::ffi::OsString;
|
||||
use std::net::UdpSocket;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
use console::style;
|
||||
use fs2::FileExt;
|
||||
use windows_service::Error;
|
||||
use windows_service::service::{
|
||||
ServiceAccess, ServiceErrorControl, ServiceInfo, ServiceStartType, ServiceState, ServiceType,
|
||||
};
|
||||
use windows_service::service_manager::{ServiceManager, ServiceManagerAccess};
|
||||
|
||||
use switch::core::{Config, Switch};
|
||||
|
||||
use crate::{BaseArgs, Commands, config};
|
||||
use crate::command::{command, CommandEnum};
|
||||
|
||||
pub mod service;
|
||||
mod windows_admin_check;
|
||||
|
||||
pub const SERVICE_FLAG: &'static str = "start_switch_service_v1_";
|
||||
pub const SERVICE_NAME: &'static str = "switch-service-v1";
|
||||
pub const SERVICE_TYPE: ServiceType = ServiceType::OWN_PROCESS;
|
||||
|
||||
fn admin_check() -> bool {
|
||||
if !windows_admin_check::is_app_elevated() {
|
||||
println!(
|
||||
"{}",
|
||||
style("请使用管理员权限运行(Please run with administrator privileges)").red()
|
||||
);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn not_started() -> bool {
|
||||
match service_state() {
|
||||
Ok(state) => {
|
||||
if state == ServiceState::Running {
|
||||
return false;
|
||||
} else {
|
||||
println!("服务未启动")
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("{:?}", e);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn main0(base_args: BaseArgs) {
|
||||
match base_args.command {
|
||||
Commands::Start(args) => {
|
||||
if admin_check() {
|
||||
return;
|
||||
}
|
||||
{
|
||||
// 允许应用通过防火墙
|
||||
let _udp = UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||
}
|
||||
let out_log = args.log;
|
||||
match config::default_config(args) {
|
||||
Ok(start_config) => {
|
||||
match service_state() {
|
||||
Ok(state) => {
|
||||
if state == ServiceState::Stopped {
|
||||
if let Err(e) = config::save_config(config::ArgsConfig::new(
|
||||
start_config.token.clone(),
|
||||
start_config.name.clone(),
|
||||
start_config.server.to_string(),
|
||||
start_config.nat_test_server.iter().map(|v| v.to_string()).collect::<Vec<String>>(),
|
||||
start_config.device_id.clone(),
|
||||
)) {
|
||||
log::error!("{:?}",e);
|
||||
return;
|
||||
}
|
||||
match start(out_log) {
|
||||
Ok(_) => {
|
||||
//需要检查启动状态
|
||||
thread::sleep(Duration::from_secs(2));
|
||||
println!("{}", style("启动成功(Start successfully)").green())
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("服务未停止(Service not stopped)");
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
match e {
|
||||
Error::Winapi(ref e) => {
|
||||
if let Some(code) = e.raw_os_error() {
|
||||
if code == 1060 {
|
||||
//指定的服务未安装。
|
||||
println!(
|
||||
"{}",
|
||||
style("服务未安装,在当前进程启动(The service is not installed and started in the current process)").red()
|
||||
);
|
||||
let config = Config::new(
|
||||
start_config.token,
|
||||
start_config.device_id,
|
||||
start_config.name,
|
||||
start_config.server,
|
||||
start_config.nat_test_server,
|
||||
);
|
||||
let lock = match config::lock_file() {
|
||||
Ok(lock) => {
|
||||
lock
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{:?}",e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
if lock.try_lock_exclusive().is_err() {
|
||||
println!("{}", style("文件被重复打开").red());
|
||||
return;
|
||||
}
|
||||
match Switch::start(config) {
|
||||
Ok(switch) => {
|
||||
crate::console_listen(&switch);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
}
|
||||
lock.unlock().unwrap();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
println!("{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("{}", style(e).red());
|
||||
}
|
||||
};
|
||||
pause();
|
||||
}
|
||||
Commands::Stop => {
|
||||
if not_started() {
|
||||
return;
|
||||
}
|
||||
if admin_check() {
|
||||
return;
|
||||
}
|
||||
match stop() {
|
||||
Ok(_) => {
|
||||
println!("{}", style("停止成功(Stopped successfully)").green())
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
}
|
||||
pause();
|
||||
}
|
||||
Commands::Install(args) => {
|
||||
if admin_check() {
|
||||
return;
|
||||
}
|
||||
let path: PathBuf = args.path.into();
|
||||
if !path.exists() {
|
||||
std::fs::create_dir_all(&path).unwrap();
|
||||
}
|
||||
if !path.is_dir() {
|
||||
println!("参数必须为文件目录(Parameter must be a file directory)");
|
||||
} else {
|
||||
if let Err(e) = install(path, args.auto) {
|
||||
log::error!("{:?}", e);
|
||||
} else {
|
||||
println!("{}", style("安装成功(Installation succeeded)").green())
|
||||
}
|
||||
}
|
||||
pause();
|
||||
}
|
||||
Commands::Uninstall => {
|
||||
if admin_check() {
|
||||
return;
|
||||
}
|
||||
if let Err(e) = uninstall() {
|
||||
log::error!("{:?}", e);
|
||||
} else {
|
||||
println!("{}", style("卸载成功(Uninstall succeeded)").green())
|
||||
}
|
||||
pause();
|
||||
}
|
||||
Commands::Config(args) => {
|
||||
if let Err(e) = change(args.auto) {
|
||||
log::error!("{:?}", e);
|
||||
} else {
|
||||
println!("{}", style("配置成功(Config succeeded)").green())
|
||||
}
|
||||
pause();
|
||||
}
|
||||
Commands::Route => {
|
||||
if not_started() {
|
||||
return;
|
||||
}
|
||||
command(CommandEnum::Route);
|
||||
}
|
||||
Commands::List { all } => {
|
||||
if not_started() {
|
||||
return;
|
||||
}
|
||||
if all {
|
||||
command(CommandEnum::ListAll);
|
||||
} else {
|
||||
command(CommandEnum::List);
|
||||
}
|
||||
}
|
||||
Commands::Status => {
|
||||
if not_started() {
|
||||
return;
|
||||
}
|
||||
command(CommandEnum::Status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pause() {
|
||||
println!(
|
||||
"{}",
|
||||
style("按任意键退出(Press any key to exit)...").green()
|
||||
);
|
||||
use console::Term;
|
||||
let term = Term::stdout();
|
||||
let _ = term.read_char().unwrap();
|
||||
}
|
||||
|
||||
fn install(path: PathBuf, auto: bool) -> Result<(), Error> {
|
||||
let manager_access = ServiceManagerAccess::CONNECT | ServiceManagerAccess::CREATE_SERVICE;
|
||||
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
|
||||
let current_exe_path = std::env::current_exe().unwrap();
|
||||
let service_path = path.join("switch-service-v1.exe");
|
||||
std::fs::copy(current_exe_path, service_path.as_path()).unwrap();
|
||||
if let Err(e) = std::fs::copy("wintun.dll", path.join("wintun.dll").as_path()) {
|
||||
if e.kind() == io::ErrorKind::NotFound {
|
||||
println!("Not fount 'wintun.dll'. Please put 'wintun.dll' in the current directory");
|
||||
std::process::exit(0);
|
||||
} else {
|
||||
panic!("{:?}", e)
|
||||
}
|
||||
}
|
||||
let mut launch_arguments = Vec::new();
|
||||
launch_arguments.push(OsString::from(SERVICE_FLAG));
|
||||
launch_arguments.push(OsString::from(
|
||||
dirs::home_dir().unwrap().join(".switch").to_str().unwrap(),
|
||||
));
|
||||
let start_type = if auto {
|
||||
ServiceStartType::AutoStart
|
||||
} else {
|
||||
ServiceStartType::OnDemand
|
||||
};
|
||||
let service_info = ServiceInfo {
|
||||
name: OsString::from(SERVICE_NAME),
|
||||
display_name: OsString::from("switch service v1"),
|
||||
service_type: SERVICE_TYPE,
|
||||
start_type,
|
||||
error_control: ServiceErrorControl::Normal,
|
||||
executable_path: service_path.into(),
|
||||
launch_arguments,
|
||||
dependencies: vec![],
|
||||
account_name: None, // run as System
|
||||
account_password: None,
|
||||
};
|
||||
let service = service_manager.create_service(&service_info, ServiceAccess::CHANGE_CONFIG)?;
|
||||
service.set_description("A VPN")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn change(auto: bool) -> Result<(), Error> {
|
||||
let manager_access = ServiceManagerAccess::CONNECT;
|
||||
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
|
||||
|
||||
let service_access = ServiceAccess::QUERY_CONFIG | ServiceAccess::CHANGE_CONFIG;
|
||||
let service = service_manager.open_service(SERVICE_NAME, service_access)?;
|
||||
let config = service.query_config()?;
|
||||
let start_type = if auto {
|
||||
ServiceStartType::AutoStart
|
||||
} else {
|
||||
ServiceStartType::OnDemand
|
||||
};
|
||||
let mut launch_arguments = Vec::new();
|
||||
launch_arguments.push(OsString::from(SERVICE_FLAG));
|
||||
launch_arguments.push(OsString::from(
|
||||
dirs::home_dir().unwrap().join(".switch").to_str().unwrap(),
|
||||
));
|
||||
let service_info = ServiceInfo {
|
||||
name: OsString::from(SERVICE_NAME),
|
||||
display_name: config.display_name,
|
||||
service_type: SERVICE_TYPE,
|
||||
start_type,
|
||||
error_control: config.error_control,
|
||||
executable_path: config.executable_path,
|
||||
launch_arguments,
|
||||
dependencies: config.dependencies,
|
||||
account_name: None, // run as System
|
||||
account_password: None,
|
||||
};
|
||||
service.change_config(&service_info)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn uninstall() -> Result<(), Error> {
|
||||
let manager_access = ServiceManagerAccess::CONNECT;
|
||||
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
|
||||
|
||||
let service_access = ServiceAccess::QUERY_STATUS | ServiceAccess::STOP | ServiceAccess::DELETE;
|
||||
let service = service_manager.open_service(SERVICE_NAME, service_access)?;
|
||||
|
||||
let service_status = service.query_status()?;
|
||||
if service_status.current_state != ServiceState::Stopped {
|
||||
service.stop()?;
|
||||
// Wait for service to stop
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
}
|
||||
service.delete()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn start(out_log: bool) -> Result<(), Error> {
|
||||
let manager_access = ServiceManagerAccess::CONNECT;
|
||||
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
|
||||
let service = service_manager.open_service(SERVICE_NAME, ServiceAccess::START)?;
|
||||
if out_log {
|
||||
service.start(&["log"])
|
||||
} else {
|
||||
service.start(&[""])
|
||||
}
|
||||
}
|
||||
|
||||
fn service_state() -> Result<ServiceState, Error> {
|
||||
let manager_access = ServiceManagerAccess::CONNECT;
|
||||
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
|
||||
|
||||
let service_access = ServiceAccess::QUERY_STATUS;
|
||||
let service = service_manager.open_service(SERVICE_NAME, service_access)?;
|
||||
let service_status = service.query_status()?;
|
||||
return Ok(service_status.current_state);
|
||||
}
|
||||
|
||||
fn stop() -> Result<(), Error> {
|
||||
let manager_access = ServiceManagerAccess::CONNECT;
|
||||
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
|
||||
let service = service_manager.open_service(SERVICE_NAME, ServiceAccess::STOP)?;
|
||||
service.stop()?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
// #[macro_use]
|
||||
// extern crate windows_service;
|
||||
|
||||
use std::ffi::OsString;
|
||||
use std::net::ToSocketAddrs;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use windows_service::{define_windows_service, service_control_handler, service_dispatcher};
|
||||
use windows_service::service::{
|
||||
ServiceControl, ServiceControlAccept, ServiceExitCode, ServiceState, ServiceStatus,
|
||||
};
|
||||
use windows_service::service_control_handler::ServiceControlHandlerResult;
|
||||
|
||||
use switch::core::{Config, Switch};
|
||||
|
||||
use crate::config;
|
||||
use crate::windows::config::read_config;
|
||||
use crate::windows::SERVICE_NAME;
|
||||
|
||||
define_windows_service!(ffi_service_main, switch_service_main);
|
||||
pub fn switch_service_main(arguments: Vec<OsString>) {
|
||||
if !arguments.is_empty() {
|
||||
if let Some(str) = arguments[0].to_str() {
|
||||
if str == "log" {
|
||||
let _ = config::log_config::log_service_init();
|
||||
}
|
||||
}
|
||||
}
|
||||
thread::spawn(|| match service_main() {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn service_main() -> windows_service::Result<()> {
|
||||
let parker = crossbeam::sync::Parker::new();
|
||||
let un_parker = parker.unparker().clone();
|
||||
let event_handler = move |control_event| -> ServiceControlHandlerResult {
|
||||
match control_event {
|
||||
// Notifies a service to report its current status information to the service
|
||||
// control manager. Always return NoError even if not implemented.
|
||||
ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
|
||||
|
||||
// Handle stop
|
||||
ServiceControl::Stop => {
|
||||
un_parker.unpark();
|
||||
log::info!("handler 服务停止");
|
||||
ServiceControlHandlerResult::NoError
|
||||
}
|
||||
_ => ServiceControlHandlerResult::NotImplemented,
|
||||
}
|
||||
};
|
||||
|
||||
// Register system service event handler.
|
||||
// The returned status handle should be used to report service status changes to the system.
|
||||
let status_handle =
|
||||
service_control_handler::register(SERVICE_NAME, event_handler)?;
|
||||
|
||||
// Tell the system that service is running
|
||||
status_handle.set_service_status(ServiceStatus {
|
||||
service_type: crate::windows::SERVICE_TYPE,
|
||||
current_state: ServiceState::Running,
|
||||
controls_accepted: ServiceControlAccept::STOP,
|
||||
exit_code: ServiceExitCode::Win32(0),
|
||||
checkpoint: 0,
|
||||
wait_hint: Duration::default(),
|
||||
process_id: None,
|
||||
})?;
|
||||
match start_switch() {
|
||||
Ok(switch) => {
|
||||
parker.park();
|
||||
if let Err(e) = switch.stop() {
|
||||
log::warn!("switch stop:{:?}",e)
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{:?}",e);
|
||||
}
|
||||
}
|
||||
status_handle.set_service_status(ServiceStatus {
|
||||
service_type: crate::windows::SERVICE_TYPE,
|
||||
current_state: ServiceState::Stopped,
|
||||
controls_accepted: ServiceControlAccept::empty(),
|
||||
exit_code: ServiceExitCode::Win32(0),
|
||||
checkpoint: 0,
|
||||
wait_hint: Duration::default(),
|
||||
process_id: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn start_switch() -> switch::Result<Arc<Switch>> {
|
||||
if let Some(config) = read_config() {
|
||||
let device_id = config.device_id;
|
||||
if device_id.trim().is_empty() {
|
||||
return Err(switch::error::Error::Stop("Device id error".to_string()));
|
||||
}
|
||||
let server_address = if let Some(server_address) = config.server
|
||||
.to_socket_addrs()?
|
||||
.next() {
|
||||
server_address
|
||||
} else {
|
||||
return Err(switch::error::Error::Stop("server address error".to_string()));
|
||||
};
|
||||
let nat_test_server = config.nat_test_server.iter()
|
||||
.flat_map(|a| a.to_socket_addrs())
|
||||
.flatten()
|
||||
.collect::<Vec<_>>();
|
||||
if nat_test_server.is_empty() {
|
||||
return Err(switch::error::Error::Stop("nat test server address error".to_string()));
|
||||
}
|
||||
let config = Config::new(
|
||||
config.token,
|
||||
device_id,
|
||||
config.name,
|
||||
server_address,
|
||||
nat_test_server);
|
||||
let switch = Switch::start(config)?;
|
||||
log::info!("switch-service服务启动");
|
||||
let switch = Arc::new(switch);
|
||||
let command_server = crate::command::server::CommandServer::new();
|
||||
let switch1 = switch.clone();
|
||||
thread::spawn(move || {
|
||||
if let Err(e) = config::update_pid(std::process::id()) {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
if let Err(e) = command_server.start(switch1) {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
});
|
||||
Ok(switch)
|
||||
} else {
|
||||
Err(switch::error::Error::Stop("配置文件为空".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start() {
|
||||
log::info!("以服务的方式启动");
|
||||
service_dispatcher::start(SERVICE_NAME, ffi_service_main).unwrap();
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
[package]
|
||||
name = "switch"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
packet = { path = "./packet" }
|
||||
p2p_channel = { path = "./p2p_channel" }
|
||||
bytes = "1.3.0"
|
||||
log = "0.4.17"
|
||||
libc = "0.2.137"
|
||||
|
||||
dashmap = "5.4.0"
|
||||
crossbeam = "0.8.2"
|
||||
crossbeam-skiplist = "0.1"
|
||||
parking_lot = "0.12.1"
|
||||
|
||||
rsa = "0.7.2"
|
||||
rand = "0.8.5"
|
||||
sha2 = { version = "0.10.6", features = ["oid"] }
|
||||
|
||||
thiserror = "1.0.37"
|
||||
chrono = "0.4.23"
|
||||
#lazy_static = "1.4.0"
|
||||
#moka = "0.9.6"
|
||||
protobuf = "3.2.0"
|
||||
#local-ip-address = "0.4.9"
|
||||
|
||||
#mio = {version = "0.8.6",features = ["os-poll", "net"]}
|
||||
#tokio = { version = "1.24.1", features = ["full"] }
|
||||
[target.'cfg(any(unix))'.dependencies]
|
||||
tun = { path = "./rust-tun" }
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
wintun = { path = "./wintun" }
|
||||
libloading = "0.7.4"
|
||||
|
||||
[build-dependencies]
|
||||
protobuf-codegen = "3.2.0"
|
||||
protoc-bin-vendored = "3.0.0"
|
||||
Submodule switch/p2p_channel deleted from 9d2e02f629
@@ -1,25 +0,0 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("the buffer is too small")]
|
||||
SmallBuffer,
|
||||
|
||||
#[error("the packet is invalid")]
|
||||
InvalidPacket,
|
||||
#[error("Unimplemented")]
|
||||
Unimplemented,
|
||||
// #[error("the vaue is invalid for the field")]
|
||||
// InvalidValue,
|
||||
//
|
||||
// #[error("the value has already been defined")]
|
||||
// AlreadyDefined,
|
||||
//
|
||||
// #[error(transparent)]
|
||||
// Io(#[from] io::Error),
|
||||
//
|
||||
// #[error(transparent)]
|
||||
// Nul(#[from] ffi::NulError),
|
||||
}
|
||||
|
||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||
@@ -1,25 +0,0 @@
|
||||
[package]
|
||||
name = "tun"
|
||||
version = "0.5.4"
|
||||
edition = "2018"
|
||||
|
||||
authors = ["meh. <[email protected]>"]
|
||||
license = "WTFPL"
|
||||
|
||||
description = "TUN device creation and handling."
|
||||
repository = "https://github.com/meh/rust-tun"
|
||||
keywords = ["tun", "network", "tunnel", "bindings"]
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
thiserror = "1"
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "ios", target_os = "android"))'.dependencies]
|
||||
bytes = { version = "1", optional = true }
|
||||
byteorder = { version = "1", optional = true }
|
||||
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies]
|
||||
ioctl = { version = "0.6", package = "ioctl-sys" }
|
||||
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
use std::net::{SocketAddr, SocketAddrV4};
|
||||
|
||||
use crate::error::*;
|
||||
|
||||
/// Helper trait to convert things into IPv4 addresses.
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
pub trait IntoAddress {
|
||||
/// Convert the type to an `Ipv4Addr`.
|
||||
fn into_address(&self) -> Result<Ipv4Addr>;
|
||||
}
|
||||
|
||||
impl IntoAddress for u32 {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
Ok(Ipv4Addr::new(
|
||||
((*self) & 0xff) as u8,
|
||||
((*self >> 8) & 0xff) as u8,
|
||||
((*self >> 16) & 0xff) as u8,
|
||||
((*self >> 24) & 0xff) as u8,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAddress for i32 {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
(*self as u32).into_address()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAddress for (u8, u8, u8, u8) {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
Ok(Ipv4Addr::new(self.0, self.1, self.2, self.3))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAddress for str {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
self.parse().map_err(|_| Error::InvalidAddress)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoAddress for &'a str {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
(*self).into_address()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAddress for String {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
(&**self).into_address()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoAddress for &'a String {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
(&**self).into_address()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAddress for Ipv4Addr {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
Ok(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoAddress for &'a Ipv4Addr {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
(&**self).into_address()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAddress for IpAddr {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
match *self {
|
||||
IpAddr::V4(ref value) => Ok(*value),
|
||||
|
||||
IpAddr::V6(_) => Err(Error::InvalidAddress),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoAddress for &'a IpAddr {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
(&**self).into_address()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAddress for SocketAddrV4 {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
Ok(*self.ip())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoAddress for &'a SocketAddrV4 {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
(&**self).into_address()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAddress for SocketAddr {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
match *self {
|
||||
SocketAddr::V4(ref value) => Ok(*value.ip()),
|
||||
|
||||
SocketAddr::V6(_) => Err(Error::InvalidAddress),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoAddress for &'a SocketAddr {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
(&**self).into_address()
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::net::Ipv4Addr;
|
||||
use std::os::unix::io::RawFd;
|
||||
|
||||
use crate::address::IntoAddress;
|
||||
use crate::platform;
|
||||
|
||||
/// TUN interface OSI layer of operation.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Layer {
|
||||
L2,
|
||||
L3,
|
||||
}
|
||||
|
||||
impl Default for Layer {
|
||||
fn default() -> Self {
|
||||
Layer::L3
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration builder for a TUN interface.
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct Configuration {
|
||||
pub(crate) name: Option<String>,
|
||||
pub(crate) platform: platform::Configuration,
|
||||
|
||||
pub(crate) address: Option<Ipv4Addr>,
|
||||
pub(crate) destination: Option<Ipv4Addr>,
|
||||
pub(crate) broadcast: Option<Ipv4Addr>,
|
||||
pub(crate) netmask: Option<Ipv4Addr>,
|
||||
pub(crate) mtu: Option<i32>,
|
||||
pub(crate) enabled: Option<bool>,
|
||||
pub(crate) layer: Option<Layer>,
|
||||
pub(crate) queues: Option<usize>,
|
||||
pub(crate) raw_fd: Option<RawFd>,
|
||||
}
|
||||
|
||||
impl Configuration {
|
||||
/// Access the platform dependant configuration.
|
||||
pub fn platform<F>(&mut self, f: F) -> &mut Self
|
||||
where
|
||||
F: FnOnce(&mut platform::Configuration),
|
||||
{
|
||||
f(&mut self.platform);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the name.
|
||||
pub fn name<S: AsRef<str>>(&mut self, name: S) -> &mut Self {
|
||||
self.name = Some(name.as_ref().into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the address.
|
||||
pub fn address<A: IntoAddress>(&mut self, value: A) -> &mut Self {
|
||||
self.address = Some(value.into_address().unwrap());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the destination address.
|
||||
pub fn destination<A: IntoAddress>(&mut self, value: A) -> &mut Self {
|
||||
self.destination = Some(value.into_address().unwrap());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the broadcast address.
|
||||
pub fn broadcast<A: IntoAddress>(&mut self, value: A) -> &mut Self {
|
||||
self.broadcast = Some(value.into_address().unwrap());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the netmask.
|
||||
pub fn netmask<A: IntoAddress>(&mut self, value: A) -> &mut Self {
|
||||
self.netmask = Some(value.into_address().unwrap());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the MTU.
|
||||
pub fn mtu(&mut self, value: i32) -> &mut Self {
|
||||
self.mtu = Some(value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the interface to be enabled once created.
|
||||
pub fn up(&mut self) -> &mut Self {
|
||||
self.enabled = Some(true);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the interface to be disabled once created.
|
||||
pub fn down(&mut self) -> &mut Self {
|
||||
self.enabled = Some(false);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the OSI layer of operation.
|
||||
pub fn layer(&mut self, value: Layer) -> &mut Self {
|
||||
self.layer = Some(value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the number of queues.
|
||||
pub fn queues(&mut self, value: usize) -> &mut Self {
|
||||
self.queues = Some(value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the raw fd.
|
||||
pub fn raw_fd(&mut self, fd: RawFd) -> &mut Self {
|
||||
self.raw_fd = Some(fd);
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::configuration::Configuration;
|
||||
use crate::error::*;
|
||||
|
||||
/// A TUN device.
|
||||
pub trait Device {
|
||||
type Queue ;
|
||||
|
||||
/// Reconfigure the device.
|
||||
fn configure(&mut self, config: &Configuration) -> Result<()> {
|
||||
if let Some(ip) = config.address {
|
||||
self.set_address(ip)?;
|
||||
}
|
||||
|
||||
if let Some(ip) = config.destination {
|
||||
self.set_destination(ip)?;
|
||||
}
|
||||
|
||||
if let Some(ip) = config.broadcast {
|
||||
self.set_broadcast(ip)?;
|
||||
}
|
||||
|
||||
if let Some(ip) = config.netmask {
|
||||
self.set_netmask(ip)?;
|
||||
}
|
||||
|
||||
if let Some(mtu) = config.mtu {
|
||||
self.set_mtu(mtu)?;
|
||||
}
|
||||
|
||||
if let Some(enabled) = config.enabled {
|
||||
self.enabled(enabled)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the device name.
|
||||
fn name(&self) -> &str;
|
||||
|
||||
/// Set the device name.
|
||||
fn set_name(&mut self, name: &str) -> Result<()>;
|
||||
|
||||
/// Turn on or off the interface.
|
||||
fn enabled(&mut self, value: bool) -> Result<()>;
|
||||
|
||||
/// Get the address.
|
||||
fn address(&self) -> Result<Ipv4Addr>;
|
||||
|
||||
/// Set the address.
|
||||
fn set_address(&mut self, value: Ipv4Addr) -> Result<()>;
|
||||
|
||||
/// Get the destination address.
|
||||
fn destination(&self) -> Result<Ipv4Addr>;
|
||||
|
||||
/// Set the destination address.
|
||||
fn set_destination(&mut self, value: Ipv4Addr) -> Result<()>;
|
||||
|
||||
/// Get the broadcast address.
|
||||
fn broadcast(&self) -> Result<Ipv4Addr>;
|
||||
|
||||
/// Set the broadcast address.
|
||||
fn set_broadcast(&mut self, value: Ipv4Addr) -> Result<()>;
|
||||
|
||||
/// Get the netmask.
|
||||
fn netmask(&self) -> Result<Ipv4Addr>;
|
||||
|
||||
/// Set the netmask.
|
||||
fn set_netmask(&mut self, value: Ipv4Addr) -> Result<()>;
|
||||
|
||||
/// Get the MTU.
|
||||
fn mtu(&self) -> Result<i32>;
|
||||
|
||||
/// Set the MTU.
|
||||
fn set_mtu(&mut self, value: i32) -> Result<()>;
|
||||
|
||||
/// Get a device queue.
|
||||
fn queue(&self, index: usize) -> Option<&Self::Queue>;
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::{ffi, io, num};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("invalid configuration")]
|
||||
InvalidConfig,
|
||||
|
||||
#[error("not implementated")]
|
||||
NotImplemented,
|
||||
|
||||
#[error("device name too long")]
|
||||
NameTooLong,
|
||||
|
||||
#[error("invalid device name")]
|
||||
InvalidName,
|
||||
|
||||
#[error("invalid address")]
|
||||
InvalidAddress,
|
||||
|
||||
#[error("invalid file descriptor")]
|
||||
InvalidDescriptor,
|
||||
|
||||
#[error("unsuported network layer of operation")]
|
||||
UnsupportedLayer,
|
||||
|
||||
#[error("invalid queues number")]
|
||||
InvalidQueuesNumber,
|
||||
|
||||
#[error(transparent)]
|
||||
Io(#[from] io::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
Nul(#[from] ffi::NulError),
|
||||
|
||||
#[error(transparent)]
|
||||
ParseNum(#[from] num::ParseIntError),
|
||||
}
|
||||
|
||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||
@@ -1,32 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
mod error;
|
||||
pub use crate::error::*;
|
||||
|
||||
mod address;
|
||||
pub use crate::address::IntoAddress;
|
||||
|
||||
mod device;
|
||||
pub use crate::device::Device;
|
||||
|
||||
mod configuration;
|
||||
pub use crate::configuration::{Configuration, Layer};
|
||||
|
||||
pub mod platform;
|
||||
pub use crate::platform::create;
|
||||
|
||||
pub fn configure() -> Configuration {
|
||||
Configuration::default()
|
||||
}
|
||||
@@ -1,384 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
use std::vec::Vec;
|
||||
|
||||
use libc;
|
||||
use libc::{c_char, c_short};
|
||||
use libc::{AF_INET, O_RDWR, SOCK_DGRAM};
|
||||
|
||||
use crate::configuration::{Configuration, Layer};
|
||||
use crate::device::Device as D;
|
||||
use crate::error::*;
|
||||
use crate::platform::linux::sys::*;
|
||||
use crate::platform::posix::{self, Fd, SockAddr};
|
||||
|
||||
/// A TUN device using the TUN/TAP Linux driver.
|
||||
pub struct Device {
|
||||
name: String,
|
||||
queues: Vec<Queue>,
|
||||
ctl: Fd,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
/// Create a new `Device` for the given `Configuration`.
|
||||
pub fn new(config: &Configuration) -> Result<Self> {
|
||||
let mut device = unsafe {
|
||||
let dev = match config.name.as_ref() {
|
||||
Some(name) => {
|
||||
let name = CString::new(name.clone())?;
|
||||
|
||||
if name.as_bytes_with_nul().len() > IFNAMSIZ {
|
||||
return Err(Error::NameTooLong);
|
||||
}
|
||||
|
||||
Some(name)
|
||||
}
|
||||
|
||||
None => None,
|
||||
};
|
||||
|
||||
let mut queues = Vec::new();
|
||||
|
||||
let mut req: ifreq = mem::zeroed();
|
||||
|
||||
if let Some(dev) = dev.as_ref() {
|
||||
ptr::copy_nonoverlapping(
|
||||
dev.as_ptr() as *const c_char,
|
||||
req.ifrn.name.as_mut_ptr(),
|
||||
dev.as_bytes().len(),
|
||||
);
|
||||
}
|
||||
|
||||
let device_type: c_short = config.layer.unwrap_or(Layer::L3).into();
|
||||
|
||||
let queues_num = config.queues.unwrap_or(1);
|
||||
if queues_num < 1 {
|
||||
return Err(Error::InvalidQueuesNumber);
|
||||
}
|
||||
|
||||
req.ifru.flags = device_type
|
||||
| if config.platform.packet_information {
|
||||
0
|
||||
} else {
|
||||
IFF_NO_PI
|
||||
}
|
||||
| if queues_num > 1 { IFF_MULTI_QUEUE } else { 0 };
|
||||
|
||||
for _ in 0..queues_num {
|
||||
let tun = Fd::new(libc::open(b"/dev/net/tun\0".as_ptr() as *const _, O_RDWR))
|
||||
.map_err(|_| io::Error::last_os_error())?;
|
||||
|
||||
if tunsetiff(tun.0, &mut req as *mut _ as *mut _) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
queues.push(Queue {
|
||||
tun: Arc::new(tun),
|
||||
pi_enabled: config.platform.packet_information,
|
||||
});
|
||||
}
|
||||
|
||||
let ctl = Fd::new(libc::socket(AF_INET, SOCK_DGRAM, 0))
|
||||
.map_err(|_| io::Error::last_os_error())?;
|
||||
|
||||
Device {
|
||||
name: CStr::from_ptr(req.ifrn.name.as_ptr())
|
||||
.to_string_lossy()
|
||||
.into(),
|
||||
queues,
|
||||
ctl,
|
||||
}
|
||||
};
|
||||
|
||||
device.configure(config)?;
|
||||
|
||||
Ok(device)
|
||||
}
|
||||
|
||||
/// Prepare a new request.
|
||||
unsafe fn request(&self) -> ifreq {
|
||||
let mut req: ifreq = mem::zeroed();
|
||||
ptr::copy_nonoverlapping(
|
||||
self.name.as_ptr() as *const c_char,
|
||||
req.ifrn.name.as_mut_ptr(),
|
||||
self.name.len(),
|
||||
);
|
||||
|
||||
req
|
||||
}
|
||||
|
||||
// /// Make the device persistent.
|
||||
// pub fn persist(&mut self) -> Result<()> {
|
||||
// unsafe {
|
||||
// if tunsetpersist(self.as_raw_fd(), &1) < 0 {
|
||||
// Err(io::Error::last_os_error().into())
|
||||
// } else {
|
||||
// Ok(())
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// Set the owner of the device.
|
||||
// pub fn user(&mut self, value: i32) -> Result<()> {
|
||||
// unsafe {
|
||||
// if tunsetowner(self.as_raw_fd(), &value) < 0 {
|
||||
// Err(io::Error::last_os_error().into())
|
||||
// } else {
|
||||
// Ok(())
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /// Set the group of the device.
|
||||
// pub fn group(&mut self, value: i32) -> Result<()> {
|
||||
// unsafe {
|
||||
// if tunsetgroup(self.as_raw_fd(), &value) < 0 {
|
||||
// Err(io::Error::last_os_error().into())
|
||||
// } else {
|
||||
// Ok(())
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
/// Return whether the device has packet information
|
||||
pub fn has_packet_information(&self) -> bool {
|
||||
self.queues[0].has_packet_information()
|
||||
}
|
||||
|
||||
/// Set non-blocking mode
|
||||
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||
self.queues[0].set_nonblock()
|
||||
}
|
||||
}
|
||||
|
||||
impl D for Device {
|
||||
type Queue = Queue;
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn set_name(&mut self, value: &str) -> Result<()> {
|
||||
unsafe {
|
||||
let name = CString::new(value)?;
|
||||
|
||||
if name.as_bytes_with_nul().len() > IFNAMSIZ {
|
||||
return Err(Error::NameTooLong);
|
||||
}
|
||||
|
||||
let mut req = self.request();
|
||||
ptr::copy_nonoverlapping(
|
||||
name.as_ptr() as *const c_char,
|
||||
req.ifru.newname.as_mut_ptr(),
|
||||
value.len(),
|
||||
);
|
||||
|
||||
if siocsifname(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
self.name = value.into();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn enabled(&mut self, value: bool) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifflags(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
if value {
|
||||
req.ifru.flags |= IFF_UP | IFF_RUNNING;
|
||||
} else {
|
||||
req.ifru.flags &= !IFF_UP;
|
||||
}
|
||||
|
||||
if siocsifflags(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn address(&self) -> Result<Ipv4Addr> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
SockAddr::new(&req.ifru.addr).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_address(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.addr = SockAddr::from(value).into();
|
||||
|
||||
if siocsifaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn destination(&self) -> Result<Ipv4Addr> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifdstaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
SockAddr::new(&req.ifru.dstaddr).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_destination(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.dstaddr = SockAddr::from(value).into();
|
||||
|
||||
if siocsifdstaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn broadcast(&self) -> Result<Ipv4Addr> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifbrdaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
SockAddr::new(&req.ifru.broadaddr).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_broadcast(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.broadaddr = SockAddr::from(value).into();
|
||||
|
||||
if siocsifbrdaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn netmask(&self) -> Result<Ipv4Addr> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifnetmask(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
SockAddr::new(&req.ifru.netmask).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_netmask(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.netmask = SockAddr::from(value).into();
|
||||
|
||||
if siocsifnetmask(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn mtu(&self) -> Result<i32> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifmtu(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(req.ifru.mtu)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_mtu(&mut self, value: i32) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.mtu = value;
|
||||
|
||||
if siocsifmtu(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn queue(&self, index: usize) -> Option<&Self::Queue> {
|
||||
self.queues.get(index)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Queue {
|
||||
tun: Arc<Fd>,
|
||||
pi_enabled: bool,
|
||||
}
|
||||
|
||||
impl Queue {
|
||||
pub fn has_packet_information(&self) -> bool {
|
||||
self.pi_enabled
|
||||
}
|
||||
|
||||
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||
self.tun.set_nonblock()
|
||||
}
|
||||
pub fn reader(&self) -> posix::Reader {
|
||||
posix::Reader(self.tun.clone())
|
||||
}
|
||||
pub fn writer(&self) -> posix::Writer {
|
||||
posix::Writer(self.tun.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Layer> for c_short {
|
||||
fn from(layer: Layer) -> Self {
|
||||
match layer {
|
||||
Layer::L2 => IFF_TAP,
|
||||
Layer::L3 => IFF_TUN,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
//! Linux specific functionality.
|
||||
|
||||
pub mod sys;
|
||||
|
||||
mod device;
|
||||
pub use self::device::{Device, Queue};
|
||||
|
||||
use crate::configuration::Configuration as C;
|
||||
use crate::error::*;
|
||||
|
||||
/// Linux-only interface configuration.
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
pub struct Configuration {
|
||||
pub(crate) packet_information: bool,
|
||||
}
|
||||
|
||||
impl Configuration {
|
||||
/// Enable or disable packet information, when enabled the first 4 bytes of
|
||||
/// each packet is a header with flags and protocol type.
|
||||
pub fn packet_information(&mut self, value: bool) -> &mut Self {
|
||||
self.packet_information = value;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a TUN device with the given name.
|
||||
pub fn create(configuration: &C) -> Result<Device> {
|
||||
Device::new(configuration)
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
//! Bindings to internal Linux stuff.
|
||||
|
||||
use ioctl::*;
|
||||
use libc::sockaddr;
|
||||
use libc::{c_char, c_int, c_short, c_uchar, c_uint, c_ulong, c_ushort, c_void};
|
||||
|
||||
pub const IFNAMSIZ: usize = 16;
|
||||
|
||||
pub const IFF_UP: c_short = 0x1;
|
||||
pub const IFF_RUNNING: c_short = 0x40;
|
||||
|
||||
pub const IFF_TUN: c_short = 0x0001;
|
||||
pub const IFF_TAP: c_short = 0x0002;
|
||||
pub const IFF_NO_PI: c_short = 0x1000;
|
||||
pub const IFF_MULTI_QUEUE: c_short = 0x0100;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ifmap {
|
||||
pub mem_start: c_ulong,
|
||||
pub mem_end: c_ulong,
|
||||
pub base_addr: c_ushort,
|
||||
pub irq: c_uchar,
|
||||
pub dma: c_uchar,
|
||||
pub port: c_uchar,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub union ifsu {
|
||||
pub raw_hdlc_proto: *mut c_void,
|
||||
pub cisco: *mut c_void,
|
||||
pub fr: *mut c_void,
|
||||
pub fr_pvc: *mut c_void,
|
||||
pub fr_pvc_info: *mut c_void,
|
||||
pub sync: *mut c_void,
|
||||
pub te1: *mut c_void,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct if_settings {
|
||||
pub type_: c_uint,
|
||||
pub size: c_uint,
|
||||
pub ifsu: ifsu,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub union ifrn {
|
||||
pub name: [c_char; IFNAMSIZ],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub union ifru {
|
||||
pub addr: sockaddr,
|
||||
pub dstaddr: sockaddr,
|
||||
pub broadaddr: sockaddr,
|
||||
pub netmask: sockaddr,
|
||||
pub hwaddr: sockaddr,
|
||||
|
||||
pub flags: c_short,
|
||||
pub ivalue: c_int,
|
||||
pub mtu: c_int,
|
||||
pub map: ifmap,
|
||||
pub slave: [c_char; IFNAMSIZ],
|
||||
pub newname: [c_char; IFNAMSIZ],
|
||||
pub data: *mut c_void,
|
||||
pub settings: if_settings,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ifreq {
|
||||
pub ifrn: ifrn,
|
||||
pub ifru: ifru,
|
||||
}
|
||||
|
||||
ioctl!(bad read siocgifflags with 0x8913; ifreq);
|
||||
ioctl!(bad write siocsifflags with 0x8914; ifreq);
|
||||
ioctl!(bad read siocgifaddr with 0x8915; ifreq);
|
||||
ioctl!(bad write siocsifaddr with 0x8916; ifreq);
|
||||
ioctl!(bad read siocgifdstaddr with 0x8917; ifreq);
|
||||
ioctl!(bad write siocsifdstaddr with 0x8918; ifreq);
|
||||
ioctl!(bad read siocgifbrdaddr with 0x8919; ifreq);
|
||||
ioctl!(bad write siocsifbrdaddr with 0x891a; ifreq);
|
||||
ioctl!(bad read siocgifnetmask with 0x891b; ifreq);
|
||||
ioctl!(bad write siocsifnetmask with 0x891c; ifreq);
|
||||
ioctl!(bad read siocgifmtu with 0x8921; ifreq);
|
||||
ioctl!(bad write siocsifmtu with 0x8922; ifreq);
|
||||
ioctl!(bad write siocsifname with 0x8923; ifreq);
|
||||
|
||||
ioctl!(write tunsetiff with b'T', 202; c_int);
|
||||
ioctl!(write tunsetpersist with b'T', 203; c_int);
|
||||
ioctl!(write tunsetowner with b'T', 204; c_int);
|
||||
ioctl!(write tunsetgroup with b'T', 206; c_int);
|
||||
@@ -1,445 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
#![allow(unused_variables)]
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use libc;
|
||||
use libc::{AF_INET, c_char, c_uint, c_void, SOCK_DGRAM, sockaddr, socklen_t};
|
||||
|
||||
use crate::configuration::{Configuration, Layer};
|
||||
use crate::device::Device as D;
|
||||
use crate::error::*;
|
||||
use crate::platform::macos::sys::*;
|
||||
use crate::platform::posix::{self, Fd, SockAddr};
|
||||
|
||||
/// A TUN device using the TUN macOS driver.
|
||||
pub struct Device {
|
||||
name: String,
|
||||
queue: Queue,
|
||||
ctl: Fd,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
/// Create a new `Device` for the given `Configuration`.
|
||||
pub fn new(config: &Configuration) -> Result<Self> {
|
||||
let id = if let Some(name) = config.name.as_ref() {
|
||||
if name.len() > IFNAMSIZ {
|
||||
return Err(Error::NameTooLong);
|
||||
}
|
||||
|
||||
if !name.starts_with("utun") {
|
||||
return Err(Error::InvalidName);
|
||||
}
|
||||
|
||||
name[4..].parse()?
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
if config.layer.filter(|l| *l != Layer::L3).is_some() {
|
||||
return Err(Error::UnsupportedLayer);
|
||||
}
|
||||
|
||||
let queues_number = config.queues.unwrap_or(1);
|
||||
if queues_number != 1 {
|
||||
return Err(Error::InvalidQueuesNumber);
|
||||
}
|
||||
|
||||
let mut device = unsafe {
|
||||
let tun = Fd::new(libc::socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL))
|
||||
.map_err(|_| io::Error::last_os_error())?;
|
||||
|
||||
let mut info = ctl_info {
|
||||
ctl_id: 0,
|
||||
ctl_name: {
|
||||
let mut buffer = [0; 96];
|
||||
for (i, o) in UTUN_CONTROL_NAME.as_bytes().iter().zip(buffer.iter_mut()) {
|
||||
*o = *i as _;
|
||||
}
|
||||
buffer
|
||||
},
|
||||
};
|
||||
|
||||
if ctliocginfo(tun.0, &mut info as *mut _ as *mut _) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
let addr = sockaddr_ctl {
|
||||
sc_id: info.ctl_id,
|
||||
sc_len: mem::size_of::<sockaddr_ctl>() as _,
|
||||
sc_family: AF_SYSTEM,
|
||||
ss_sysaddr: AF_SYS_CONTROL,
|
||||
sc_unit: id as c_uint,
|
||||
sc_reserved: [0; 5],
|
||||
};
|
||||
|
||||
if libc::connect(
|
||||
tun.0,
|
||||
&addr as *const sockaddr_ctl as *const sockaddr,
|
||||
mem::size_of_val(&addr) as socklen_t,
|
||||
) < 0
|
||||
{
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
let mut name = [0u8; 64];
|
||||
let mut name_len: socklen_t = 64;
|
||||
|
||||
if libc::getsockopt(
|
||||
tun.0,
|
||||
SYSPROTO_CONTROL,
|
||||
UTUN_OPT_IFNAME,
|
||||
&mut name as *mut _ as *mut c_void,
|
||||
&mut name_len as *mut socklen_t,
|
||||
) < 0
|
||||
{
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
let ctl = Fd::new(libc::socket(AF_INET, SOCK_DGRAM, 0))
|
||||
.map_err(|_| io::Error::last_os_error())?;
|
||||
|
||||
Device {
|
||||
name: CStr::from_ptr(name.as_ptr() as *const c_char)
|
||||
.to_string_lossy()
|
||||
.into(),
|
||||
queue: Queue { tun: Arc::new(tun) },
|
||||
ctl: ctl,
|
||||
}
|
||||
};
|
||||
|
||||
device.configure(&config)?;
|
||||
|
||||
Ok(device)
|
||||
}
|
||||
|
||||
/// Prepare a new request.
|
||||
pub unsafe fn request(&self) -> ifreq {
|
||||
let mut req: ifreq = mem::zeroed();
|
||||
ptr::copy_nonoverlapping(
|
||||
self.name.as_ptr() as *const c_char,
|
||||
req.ifrn.name.as_mut_ptr(),
|
||||
self.name.len(),
|
||||
);
|
||||
|
||||
req
|
||||
}
|
||||
|
||||
/// Set the IPv4 alias of the device.
|
||||
pub fn set_alias(&mut self, addr: Ipv4Addr, broadaddr: Ipv4Addr, mask: Ipv4Addr) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req: ifaliasreq = mem::zeroed();
|
||||
ptr::copy_nonoverlapping(
|
||||
self.name.as_ptr() as *const c_char,
|
||||
req.ifran.as_mut_ptr(),
|
||||
self.name.len(),
|
||||
);
|
||||
|
||||
req.addr = SockAddr::from(addr).into();
|
||||
req.broadaddr = SockAddr::from(broadaddr).into();
|
||||
req.mask = SockAddr::from(mask).into();
|
||||
|
||||
if siocaifaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// /// Split the interface into a `Reader` and `Writer`.
|
||||
// pub fn split(self) -> (posix::Reader, posix::Writer) {
|
||||
// let fd = Arc::new(self.queue.tun);
|
||||
// (posix::Reader(fd.clone()), posix::Writer(fd.clone()))
|
||||
// }
|
||||
|
||||
/// Return whether the device has packet information
|
||||
pub fn has_packet_information(&self) -> bool {
|
||||
self.queue.has_packet_information()
|
||||
}
|
||||
|
||||
/// Set non-blocking mode
|
||||
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||
self.queue.set_nonblock()
|
||||
}
|
||||
}
|
||||
|
||||
// impl Read for Device {
|
||||
// fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
// self.queue.tun.read(buf)
|
||||
// }
|
||||
//
|
||||
// fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
// self.queue.tun.read_vectored(bufs)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl Write for Device {
|
||||
// fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
// self.queue.tun.write(buf)
|
||||
// }
|
||||
//
|
||||
// fn flush(&mut self) -> io::Result<()> {
|
||||
// self.queue.tun.flush()
|
||||
// }
|
||||
//
|
||||
// fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
// self.queue.tun.write_vectored(bufs)
|
||||
// }
|
||||
// }
|
||||
|
||||
impl D for Device {
|
||||
type Queue = Queue;
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
// XXX: Cannot set interface name on Darwin.
|
||||
fn set_name(&mut self, value: &str) -> Result<()> {
|
||||
Err(Error::InvalidName)
|
||||
}
|
||||
|
||||
fn enabled(&mut self, value: bool) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifflags(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
if value {
|
||||
req.ifru.flags |= IFF_UP | IFF_RUNNING;
|
||||
} else {
|
||||
req.ifru.flags &= !IFF_UP;
|
||||
}
|
||||
|
||||
if siocsifflags(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn address(&self) -> Result<Ipv4Addr> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
SockAddr::new(&req.ifru.addr).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_address(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.addr = SockAddr::from(value).into();
|
||||
|
||||
if siocsifaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn destination(&self) -> Result<Ipv4Addr> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifdstaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
SockAddr::new(&req.ifru.dstaddr).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_destination(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.dstaddr = SockAddr::from(value).into();
|
||||
|
||||
if siocsifdstaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn broadcast(&self) -> Result<Ipv4Addr> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifbrdaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
SockAddr::new(&req.ifru.broadaddr).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_broadcast(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.broadaddr = SockAddr::from(value).into();
|
||||
|
||||
if siocsifbrdaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn netmask(&self) -> Result<Ipv4Addr> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifnetmask(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
SockAddr::unchecked(&req.ifru.addr).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_netmask(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.addr = SockAddr::from(value).into();
|
||||
|
||||
if siocsifnetmask(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn mtu(&self) -> Result<i32> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifmtu(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(req.ifru.mtu)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_mtu(&mut self, value: i32) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.mtu = value;
|
||||
|
||||
if siocsifmtu(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn queue(&self, index: usize) -> Option<&Self::Queue> {
|
||||
if index > 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(&self.queue)
|
||||
}
|
||||
}
|
||||
|
||||
// impl AsRawFd for Device {
|
||||
// fn as_raw_fd(&self) -> RawFd {
|
||||
// self.queue.as_raw_fd()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl IntoRawFd for Device {
|
||||
// fn into_raw_fd(self) -> RawFd {
|
||||
// self.queue.into_raw_fd()
|
||||
// }
|
||||
// }
|
||||
|
||||
pub struct Queue {
|
||||
tun: Arc<Fd>,
|
||||
}
|
||||
|
||||
impl Queue {
|
||||
pub fn has_packet_information(&self) -> bool {
|
||||
// on macos this is always the case
|
||||
true
|
||||
}
|
||||
|
||||
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||
self.tun.set_nonblock()
|
||||
}
|
||||
|
||||
pub fn reader(&self) -> posix::Reader {
|
||||
posix::Reader(self.tun.clone())
|
||||
}
|
||||
pub fn writer(&self) -> posix::Writer {
|
||||
posix::Writer(self.tun.clone())
|
||||
}
|
||||
}
|
||||
|
||||
// impl AsRawFd for Queue {
|
||||
// fn as_raw_fd(&self) -> RawFd {
|
||||
// self.tun.as_raw_fd()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl IntoRawFd for Queue {
|
||||
// fn into_raw_fd(self) -> RawFd {
|
||||
// self.tun.into_raw_fd()
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl Read for Queue {
|
||||
// fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
// self.tun.read(buf)
|
||||
// }
|
||||
//
|
||||
// fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
// self.tun.read_vectored(bufs)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl Write for Queue {
|
||||
// fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
// self.tun.write(buf)
|
||||
// }
|
||||
//
|
||||
// fn flush(&mut self) -> io::Result<()> {
|
||||
// self.tun.flush()
|
||||
// }
|
||||
//
|
||||
// fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
// self.tun.write_vectored(bufs)
|
||||
// }
|
||||
// }
|
||||
@@ -1,32 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
//! macOS specific functionality.
|
||||
|
||||
pub mod sys;
|
||||
|
||||
mod device;
|
||||
pub use self::device::{Device, Queue};
|
||||
|
||||
use crate::configuration::Configuration as C;
|
||||
use crate::error::*;
|
||||
|
||||
/// macOS-only interface configuration.
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
pub struct Configuration {}
|
||||
|
||||
/// Create a TUN device with the given name.
|
||||
pub fn create(configuration: &C) -> Result<Device> {
|
||||
Device::new(&configuration)
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
//! Platform specific modules.
|
||||
|
||||
#[cfg(unix)]
|
||||
pub mod posix;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub mod linux;
|
||||
#[cfg(target_os = "linux")]
|
||||
pub use self::linux::{create, Configuration, Device, Queue};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub mod macos;
|
||||
#[cfg(target_os = "macos")]
|
||||
pub use self::macos::{create, Configuration, Device, Queue};
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
pub mod ios;
|
||||
#[cfg(target_os = "ios")]
|
||||
pub use self::ios::{create, Configuration, Device, Queue};
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
pub mod android;
|
||||
#[cfg(target_os = "android")]
|
||||
pub use self::android::{create, Configuration, Device, Queue};
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::configuration::Configuration;
|
||||
use crate::device::Device;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
#[test]
|
||||
fn create() {
|
||||
let dev = super::create(
|
||||
Configuration::default()
|
||||
.name("utun6")
|
||||
.address("192.168.50.1")
|
||||
.netmask("255.255.0.0")
|
||||
.mtu(1400)
|
||||
.up(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
"192.168.50.1".parse::<Ipv4Addr>().unwrap(),
|
||||
dev.address().unwrap()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
"255.255.0.0".parse::<Ipv4Addr>().unwrap(),
|
||||
dev.netmask().unwrap()
|
||||
);
|
||||
|
||||
assert_eq!(1400, dev.mtu().unwrap());
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||
|
||||
use crate::error::*;
|
||||
use libc::{self, fcntl, F_GETFL, F_SETFL, O_NONBLOCK};
|
||||
|
||||
/// POSIX file descriptor support for `io` traits.
|
||||
pub struct Fd(pub RawFd);
|
||||
|
||||
impl Fd {
|
||||
pub fn new(value: RawFd) -> Result<Self> {
|
||||
if value < 0 {
|
||||
return Err(Error::InvalidDescriptor);
|
||||
}
|
||||
|
||||
Ok(Fd(value))
|
||||
}
|
||||
|
||||
/// Enable non-blocking mode
|
||||
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||
match unsafe { fcntl(self.0, F_SETFL, fcntl(self.0, F_GETFL) | O_NONBLOCK) } {
|
||||
0 => Ok(()),
|
||||
_ => Err(io::Error::last_os_error()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Fd {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let amount = libc::read(self.0, buf.as_mut_ptr() as *mut _, buf.len());
|
||||
|
||||
if amount < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(amount as usize)
|
||||
}
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let iov = bufs.as_ptr().cast();
|
||||
let iovcnt = bufs.len().min(libc::c_int::MAX as usize) as _;
|
||||
|
||||
let n = libc::readv(self.0, iov, iovcnt);
|
||||
if n < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(n as usize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Fd {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let amount = libc::write(self.0, buf.as_ptr() as *const _, buf.len());
|
||||
|
||||
if amount < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(amount as usize)
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let iov = bufs.as_ptr().cast();
|
||||
let iovcnt = bufs.len().min(libc::c_int::MAX as usize) as _;
|
||||
|
||||
let n = libc::writev(self.0, iov, iovcnt);
|
||||
if n < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(n as usize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Fd {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for Fd {
|
||||
fn into_raw_fd(mut self) -> RawFd {
|
||||
let fd = self.0;
|
||||
self.0 = -1;
|
||||
fd
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Fd {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if self.0 >= 0 {
|
||||
libc::close(self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
//! POSIX compliant support.
|
||||
|
||||
mod sockaddr;
|
||||
pub use self::sockaddr::SockAddr;
|
||||
|
||||
mod fd;
|
||||
pub use self::fd::Fd;
|
||||
|
||||
mod split;
|
||||
pub use self::split::{Reader, Writer};
|
||||
@@ -1,120 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::os::unix::io::{AsRawFd,RawFd};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::platform::posix::Fd;
|
||||
use libc;
|
||||
|
||||
/// Read-only end for a file descriptor.
|
||||
#[derive(Clone)]
|
||||
pub struct Reader(pub(crate) Arc<Fd>);
|
||||
|
||||
/// Write-only end for a file descriptor.
|
||||
#[derive(Clone)]
|
||||
pub struct Writer(pub(crate) Arc<Fd>);
|
||||
|
||||
impl Reader {
|
||||
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let amount = libc::read(self.0.as_raw_fd(), buf.as_mut_ptr() as *mut _, buf.len());
|
||||
|
||||
if amount < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(amount as usize)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_vectored(&self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let mut msg: libc::msghdr = mem::zeroed();
|
||||
// msg.msg_name: NULL
|
||||
// msg.msg_namelen: 0
|
||||
msg.msg_iov = bufs.as_mut_ptr().cast();
|
||||
msg.msg_iovlen = bufs.len().min(libc::c_int::MAX as usize) as _;
|
||||
|
||||
let n = libc::recvmsg(self.0.as_raw_fd(), &mut msg, 0);
|
||||
if n < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(n as usize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Writer {
|
||||
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let amount = libc::write(self.0.as_raw_fd(), buf.as_ptr() as *const _, buf.len());
|
||||
|
||||
if amount < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(amount as usize)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn write_vectored(&self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let mut msg: libc::msghdr = mem::zeroed();
|
||||
// msg.msg_name = NULL
|
||||
// msg.msg_namelen = 0
|
||||
msg.msg_iov = bufs.as_ptr() as *mut _;
|
||||
msg.msg_iovlen = bufs.len().min(libc::c_int::MAX as usize) as _;
|
||||
|
||||
let n = libc::sendmsg(self.0.as_raw_fd(), &msg, 0);
|
||||
if n < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(n as usize)
|
||||
}
|
||||
}
|
||||
pub fn write_all(&self, mut buf: &[u8]) -> io::Result<()> {
|
||||
while !buf.is_empty() {
|
||||
match self.write(buf) {
|
||||
Ok(0) => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::WriteZero,
|
||||
"failed to write whole buffer",
|
||||
));
|
||||
}
|
||||
Ok(n) => buf = &buf[n..],
|
||||
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Reader {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.0.as_raw_fd()
|
||||
}
|
||||
}
|
||||
//
|
||||
// impl AsRawFd for Writer {
|
||||
// fn as_raw_fd(&self) -> RawFd {
|
||||
// self.0.as_raw_fd()
|
||||
// }
|
||||
// }
|
||||
@@ -1,140 +0,0 @@
|
||||
use std::io;
|
||||
use std::net::{Ipv4Addr, SocketAddr};
|
||||
use std::sync::Arc;
|
||||
use crossbeam::atomic::AtomicCell;
|
||||
use crossbeam_skiplist::SkipMap;
|
||||
use parking_lot::Mutex;
|
||||
use p2p_channel::boot::Boot;
|
||||
use p2p_channel::channel::{Channel, Route, RouteKey};
|
||||
use p2p_channel::punch::NatInfo;
|
||||
use crate::handle::{ConnectStatus, CurrentDeviceInfo, heartbeat_handler, PeerDeviceInfo, punch_handler, recv_handler, registration_handler, tun_handler};
|
||||
use crate::nat::NatTest;
|
||||
use crate::tun_device;
|
||||
use crate::tun_device::TunReader;
|
||||
|
||||
pub struct Switch {
|
||||
name: String,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
tun_reader: TunReader,
|
||||
nat_channel: Channel<Ipv4Addr>,
|
||||
/// 0. 机器纪元,每一次上线或者下线都会增1,用于感知网络中机器变化
|
||||
/// 服务端和客户端的不一致,则服务端会推送新的设备列表
|
||||
/// 1. 网络中的虚拟ip列表
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
nat_test: NatTest,
|
||||
connect_status: Arc<AtomicCell<ConnectStatus>>,
|
||||
peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>>,
|
||||
}
|
||||
|
||||
impl Switch {
|
||||
pub fn start(config: Config) -> crate::Result<Switch> {
|
||||
let (mut channel, punch, idle) = Boot::new::<Ipv4Addr>(80, 15000, 0)?;
|
||||
let response = registration_handler::registration(&mut channel, config.server_address, config.token.clone(), config.device_id.clone(), config.name.clone())?;
|
||||
let register = Arc::new(registration_handler::Register::new(channel.sender()?, config.server_address, config.token.clone(), config.device_id.clone(), config.name.clone()));
|
||||
let device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>> = Arc::new(Mutex::new((0, Vec::new())));
|
||||
let peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>> = Arc::new(SkipMap::new());
|
||||
let connect_status = Arc::new(AtomicCell::new(ConnectStatus::Connected));
|
||||
let virtual_ip = Ipv4Addr::from(response.virtual_ip);
|
||||
let virtual_gateway = Ipv4Addr::from(response.virtual_gateway);
|
||||
let virtual_netmask = Ipv4Addr::from(response.virtual_netmask);
|
||||
let current_device = Arc::new(AtomicCell::new(CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, config.server_address)));
|
||||
let local_ip = crate::nat::local_ip()?;
|
||||
let local_port = channel.local_addr()?.port();
|
||||
// NAT检测
|
||||
let nat_test = NatTest::new(config.nat_test_server.clone(), Ipv4Addr::from(response.public_ip), response.public_port as u16, local_ip, local_port);
|
||||
// tun通道
|
||||
let (tun_writer, tun_reader) = tun_device::create_tun(virtual_ip, virtual_netmask, virtual_gateway)?;
|
||||
|
||||
// 定时心跳
|
||||
heartbeat_handler::start_heartbeat(channel.sender()?, device_list.clone(), current_device.clone());
|
||||
// 空闲检查
|
||||
heartbeat_handler::start_idle(idle, channel.sender()?);
|
||||
// 打洞处理
|
||||
punch_handler::start_cone(punch.try_clone()?, current_device.clone());
|
||||
punch_handler::start_symmetric(punch, current_device.clone());
|
||||
punch_handler::start_punch(nat_test.clone(), device_list.clone(), channel.sender()?, current_device.clone());
|
||||
//tun数据接收处理
|
||||
for _ in 0..2 {
|
||||
tun_handler::start(channel.sender()?, tun_reader.clone(), tun_writer.clone(), current_device.clone());
|
||||
}
|
||||
//外部数据接收处理
|
||||
let channel_recv_handler = recv_handler::RecvHandler::new(channel.try_clone()?, current_device.clone(), device_list.clone(), register.clone(),
|
||||
nat_test.clone(), tun_writer.clone(), connect_status.clone(), peer_nat_info_map.clone());
|
||||
for _ in 0..2 {
|
||||
recv_handler::start(channel_recv_handler.try_clone()?);
|
||||
}
|
||||
Ok(Switch {
|
||||
name: config.name,
|
||||
current_device,
|
||||
tun_reader,
|
||||
nat_channel: channel,
|
||||
nat_test,
|
||||
device_list,
|
||||
connect_status,
|
||||
peer_nat_info_map,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Switch {
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
pub fn current_device(&self) -> CurrentDeviceInfo {
|
||||
self.current_device.load()
|
||||
}
|
||||
pub fn peer_nat_info(&self, ip: &Ipv4Addr) -> Option<NatInfo> {
|
||||
self.peer_nat_info_map.get(ip).map(|e| e.value().clone())
|
||||
}
|
||||
pub fn connection_status(&self) -> ConnectStatus {
|
||||
self.connect_status.load()
|
||||
}
|
||||
pub fn nat_info(&self) -> NatInfo {
|
||||
self.nat_test.nat_info()
|
||||
}
|
||||
pub fn device_list(&self) -> Vec<PeerDeviceInfo> {
|
||||
let device_list_lock = self.device_list.lock();
|
||||
let (_epoch, device_list) = device_list_lock.clone();
|
||||
drop(device_list_lock);
|
||||
device_list
|
||||
}
|
||||
pub fn route(&self, ip: &Ipv4Addr) -> Option<Route> {
|
||||
self.nat_channel.route(ip)
|
||||
}
|
||||
pub fn route_key(&self, route_key: &RouteKey) -> Option<Ipv4Addr> {
|
||||
self.nat_channel.route_to_id(route_key)
|
||||
}
|
||||
pub fn route_table(&self) -> Vec<(Ipv4Addr, Route)> {
|
||||
self.nat_channel.route_table()
|
||||
}
|
||||
pub fn stop(&self) -> io::Result<()> {
|
||||
self.tun_reader.close();
|
||||
self.nat_channel.close()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Config {
|
||||
pub token: String,
|
||||
pub device_id: String,
|
||||
pub name: String,
|
||||
pub server_address: SocketAddr,
|
||||
pub nat_test_server: Vec<SocketAddr>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new(token: String,
|
||||
device_id: String,
|
||||
name: String,
|
||||
server_address: SocketAddr,
|
||||
nat_test_server: Vec<SocketAddr>, ) -> Self {
|
||||
Self {
|
||||
token,
|
||||
device_id,
|
||||
name,
|
||||
server_address,
|
||||
nat_test_server,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
use std::io;
|
||||
|
||||
use crossbeam::channel::RecvError;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("packet error")]
|
||||
PacketError(#[from] packet::error::Error),
|
||||
#[error("Io error")]
|
||||
Io(#[from] io::Error),
|
||||
#[error("Channel error")]
|
||||
Channel(#[from] RecvError),
|
||||
#[error("Protobuf error")]
|
||||
Protobuf(#[from] protobuf::Error),
|
||||
#[error("Invalid packet")]
|
||||
InvalidPacket,
|
||||
#[error("Not support")]
|
||||
NotSupport,
|
||||
#[error("Stop")]
|
||||
Stop(String),
|
||||
#[error("Warn")]
|
||||
Warn(String),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
@@ -1,117 +0,0 @@
|
||||
use std::{io, thread};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use chrono::Local;
|
||||
use crossbeam::atomic::AtomicCell;
|
||||
use parking_lot::Mutex;
|
||||
use rand::prelude::SliceRandom;
|
||||
|
||||
use p2p_channel::channel::Route;
|
||||
use p2p_channel::channel::sender::Sender;
|
||||
use p2p_channel::idle::Idle;
|
||||
|
||||
use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo};
|
||||
use crate::protocol::{control_packet, MAX_TTL, NetPacket, Protocol, Version};
|
||||
use crate::protocol::control_packet::PingPacket;
|
||||
|
||||
pub fn start_idle(idle: Idle<Ipv4Addr>, sender: Sender<Ipv4Addr>) {
|
||||
thread::Builder::new().name("idle".into()).spawn(move || {
|
||||
if let Err(e) = start_idle_(idle, sender) {
|
||||
log::info!("空闲检测线程停止:{:?}",e);
|
||||
}
|
||||
}).unwrap();
|
||||
}
|
||||
|
||||
fn start_idle_(idle: Idle<Ipv4Addr>, sender: Sender<Ipv4Addr>) -> io::Result<()> {
|
||||
loop {
|
||||
let (idle_status, peer_ips, route) = idle.next_idle()?;
|
||||
log::warn!("peer_ip:{:?},route:{:?},idle_status:{:?}",peer_ips,route,idle_status);
|
||||
for peer_ip in peer_ips {
|
||||
sender.remove_route(&peer_ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_heartbeat(sender: Sender<Ipv4Addr>, device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) {
|
||||
thread::Builder::new().name("heartbeat".into()).spawn(move || {
|
||||
if let Err(e) = start_heartbeat_(sender, device_list, current_device) {
|
||||
log::info!("空闲检测线程停止:{:?}",e);
|
||||
}
|
||||
}).unwrap();
|
||||
}
|
||||
|
||||
fn set_now_time(packet: &mut NetPacket<[u8; 16]>) -> io::Result<()> {
|
||||
let current_time = Local::now().timestamp_millis() as u16;
|
||||
let mut ping = PingPacket::new(packet.payload_mut())?;
|
||||
ping.set_time(current_time);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn start_heartbeat_(sender: Sender<Ipv4Addr>, device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) -> io::Result<()> {
|
||||
let mut net_packet = NetPacket::new([0u8; 16])?;
|
||||
net_packet.set_version(Version::V1);
|
||||
net_packet.set_protocol(Protocol::Control);
|
||||
net_packet.set_transport_protocol(control_packet::Protocol::Ping.into());
|
||||
net_packet.first_set_ttl(MAX_TTL);
|
||||
let mut count = 0;
|
||||
loop {
|
||||
let current_device = current_device.load();
|
||||
net_packet.set_source(current_device.virtual_ip());
|
||||
{
|
||||
let mut ping = PingPacket::new(net_packet.payload_mut())?;
|
||||
let epoch = { device_list.lock().0 };
|
||||
ping.set_epoch(epoch);
|
||||
}
|
||||
if count < 7 || count % 7 == 0 {
|
||||
let mut route_list: Option<Vec<(Ipv4Addr, Route)>> = None;
|
||||
let peer_list = device_list.lock().1.clone();
|
||||
for peer in peer_list {
|
||||
set_now_time(&mut net_packet)?;
|
||||
net_packet.first_set_ttl(MAX_TTL);
|
||||
net_packet.set_destination(peer.virtual_ip);
|
||||
if sender.send_to_id(net_packet.buffer(), &peer.virtual_ip).is_err() {
|
||||
//没有路由则发送到网关
|
||||
let _ = sender.send_to_addr(net_packet.buffer(), current_device.connect_server);
|
||||
//再随机发送到其他地址,看有没有客户端符合转发条件
|
||||
let route_list = route_list.get_or_insert_with(|| {
|
||||
let mut l = sender.route_table();
|
||||
l.shuffle(&mut rand::thread_rng());
|
||||
l
|
||||
});
|
||||
let mut num = 0;
|
||||
net_packet.first_set_ttl(2);
|
||||
for (peer_ip, route) in route_list.iter() {
|
||||
if peer_ip != &peer.virtual_ip && route.metric == 1 {
|
||||
set_now_time(&mut net_packet)?;
|
||||
let _ = sender.send_to_route(net_packet.buffer(), &route.route_key());
|
||||
num += 1;
|
||||
}
|
||||
if num >= 3 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
}
|
||||
set_now_time(&mut net_packet)?;
|
||||
net_packet.set_destination(current_device.virtual_gateway());
|
||||
if let Err(e) = sender.send_to_addr(net_packet.buffer(), current_device.connect_server) {
|
||||
log::warn!("connect_server:{:?},e:{:?}",current_device.connect_server,e);
|
||||
}
|
||||
} else {
|
||||
for (peer_ip, route) in sender.route_table().iter() {
|
||||
set_now_time(&mut net_packet)?;
|
||||
net_packet.set_destination(*peer_ip);
|
||||
if let Err(e) = sender.send_to_route(net_packet.buffer(), &route.route_key()) {
|
||||
log::warn!("peer_ip:{:?},route:{:?},e:{:?}",peer_ip,route,e);
|
||||
}
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
}
|
||||
}
|
||||
|
||||
count += 1;
|
||||
thread::sleep(Duration::from_millis(5000));
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
use std::net::{Ipv4Addr, SocketAddr};
|
||||
|
||||
pub mod heartbeat_handler;
|
||||
pub mod punch_handler;
|
||||
pub mod registration_handler;
|
||||
pub mod tun_handler;
|
||||
pub mod recv_handler;
|
||||
|
||||
/// 是否在一个网段
|
||||
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,
|
||||
pub name: String,
|
||||
pub status: PeerDeviceStatus,
|
||||
}
|
||||
|
||||
impl PeerDeviceInfo {
|
||||
pub fn new(virtual_ip: Ipv4Addr, name: String, status: u8) -> Self {
|
||||
Self {
|
||||
virtual_ip,
|
||||
name,
|
||||
status: PeerDeviceStatus::from(status),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum PeerDeviceStatus {
|
||||
Online,
|
||||
Offline,
|
||||
}
|
||||
|
||||
impl Into<u8> for PeerDeviceStatus {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
PeerDeviceStatus::Online => 0,
|
||||
PeerDeviceStatus::Offline => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for PeerDeviceStatus {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
0 => PeerDeviceStatus::Online,
|
||||
_ => PeerDeviceStatus::Offline,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum ConnectStatus {
|
||||
Connecting,
|
||||
Connected,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct CurrentDeviceInfo {
|
||||
virtual_ip: Ipv4Addr,
|
||||
pub virtual_gateway: Ipv4Addr,
|
||||
pub virtual_netmask: Ipv4Addr,
|
||||
//网络地址
|
||||
pub virtual_network: Ipv4Addr,
|
||||
//直接广播地址
|
||||
pub broadcast_address: Ipv4Addr,
|
||||
//链接的服务器地址
|
||||
pub connect_server: SocketAddr,
|
||||
}
|
||||
|
||||
impl CurrentDeviceInfo {
|
||||
pub fn new(
|
||||
virtual_ip: Ipv4Addr,
|
||||
virtual_gateway: Ipv4Addr,
|
||||
virtual_netmask: Ipv4Addr,
|
||||
connect_server: SocketAddr,
|
||||
) -> Self {
|
||||
let broadcast_address = (!u32::from_be_bytes(virtual_netmask.octets()))
|
||||
| u32::from_be_bytes(virtual_gateway.octets());
|
||||
let broadcast_address = Ipv4Addr::from(broadcast_address);
|
||||
let virtual_network = u32::from_be_bytes(virtual_netmask.octets())
|
||||
& u32::from_be_bytes(virtual_gateway.octets());
|
||||
let virtual_network = Ipv4Addr::from(virtual_network);
|
||||
Self {
|
||||
virtual_ip,
|
||||
virtual_netmask,
|
||||
virtual_gateway,
|
||||
virtual_network,
|
||||
broadcast_address,
|
||||
connect_server,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn virtual_ip(&self) -> Ipv4Addr {
|
||||
self.virtual_ip
|
||||
}
|
||||
#[inline]
|
||||
pub fn virtual_gateway(&self) -> Ipv4Addr {
|
||||
self.virtual_gateway
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
use std::{io, thread};
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use crossbeam::atomic::AtomicCell;
|
||||
use parking_lot::Mutex;
|
||||
use protobuf::Message;
|
||||
use rand::prelude::SliceRandom;
|
||||
use p2p_channel::channel::sender::Sender;
|
||||
use p2p_channel::punch::{NatInfo, NatType, Punch};
|
||||
use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo};
|
||||
use crate::nat::NatTest;
|
||||
use crate::proto::message::{PunchInfo, PunchNatType};
|
||||
use crate::protocol::{control_packet, MAX_TTL, NetPacket, Protocol, turn_packet, Version};
|
||||
|
||||
pub fn start_cone(punch: Punch<Ipv4Addr>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) {
|
||||
thread::Builder::new().name("punch-cone".into()).spawn(move || {
|
||||
if let Err(e) = start_(true, punch, current_device) {
|
||||
log::warn!("锥形网络打洞处理线程停止 {:?}",e);
|
||||
}
|
||||
}).unwrap();
|
||||
}
|
||||
|
||||
pub fn start_symmetric(punch: Punch<Ipv4Addr>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) {
|
||||
thread::Builder::new().name("punch-symmetric".into()).spawn(move || {
|
||||
if let Err(e) = start_(false, punch, current_device) {
|
||||
log::warn!("对称网络打洞处理线程停止 {:?}",e);
|
||||
}
|
||||
}).unwrap();
|
||||
}
|
||||
|
||||
fn start_(is_cone: bool, mut punch: Punch<Ipv4Addr>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) -> io::Result<()> {
|
||||
let mut packet = NetPacket::new([0u8; 12])?;
|
||||
packet.set_version(Version::V1);
|
||||
packet.first_set_ttl(1);
|
||||
packet.set_protocol(Protocol::Control);
|
||||
packet.set_transport_protocol(control_packet::Protocol::PunchRequest.into());
|
||||
loop {
|
||||
let (peer_ip, nat_info) = if is_cone {
|
||||
punch.next_cone(None)?
|
||||
} else {
|
||||
punch.next_symmetric(None)?
|
||||
};
|
||||
if let Some(route) = punch.sender().route(&peer_ip) {
|
||||
if route.metric == 1 {
|
||||
//直连地址不需要打洞
|
||||
continue;
|
||||
}
|
||||
}
|
||||
packet.set_source(current_device.load().virtual_ip());
|
||||
packet.set_destination(peer_ip);
|
||||
log::info!("发起打洞,目标:{:?},{:?}",peer_ip,nat_info);
|
||||
if let Err(e) = punch.punch(packet.buffer(), peer_ip, nat_info) {
|
||||
log::warn!("peer_ip:{:?},e:{:?}",peer_ip,e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_punch(nat_test: NatTest, device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>, sender: Sender<Ipv4Addr>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) {
|
||||
thread::Builder::new().name("punch-send-request".into()).spawn(move || {
|
||||
if let Err(e) = start_punch_(nat_test, device_list, sender, current_device) {
|
||||
log::warn!("对称网络打洞处理线程停止 {:?}",e);
|
||||
}
|
||||
}).unwrap();
|
||||
}
|
||||
|
||||
fn start_punch_(nat_test: NatTest, device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>, sender: Sender<Ipv4Addr>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) -> crate::Result<()> {
|
||||
loop {
|
||||
if sender.is_close() {
|
||||
return Ok(());
|
||||
}
|
||||
let current_device = current_device.load();
|
||||
let nat_info = nat_test.nat_info();
|
||||
{
|
||||
let mut list = device_list.lock().clone().1;
|
||||
list.shuffle(&mut rand::thread_rng());
|
||||
let mut count = 0;
|
||||
for info in list {
|
||||
if info.virtual_ip <= current_device.virtual_ip {
|
||||
continue;
|
||||
}
|
||||
if let Some(route) = sender.route(&info.virtual_ip) {
|
||||
if route.metric == 1 {
|
||||
//直连地址不需要打洞
|
||||
continue;
|
||||
}
|
||||
}
|
||||
count += 1;
|
||||
if count > 3 {
|
||||
break;
|
||||
}
|
||||
let buf = punch_packet(current_device.virtual_ip(), &nat_info, info.virtual_ip)?;
|
||||
sender.send_to_addr(&buf, current_device.connect_server)?;
|
||||
}
|
||||
}
|
||||
match nat_info.nat_type {
|
||||
NatType::Symmetric => {
|
||||
thread::sleep(Duration::from_secs(28));
|
||||
}
|
||||
NatType::Cone => {
|
||||
thread::sleep(Duration::from_secs(20));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn punch_packet(virtual_ip: Ipv4Addr, nat_info: &NatInfo, dest: Ipv4Addr) -> crate::Result<Vec<u8>> {
|
||||
let mut punch_reply = PunchInfo::new();
|
||||
punch_reply.reply = false;
|
||||
punch_reply.public_ip_list = nat_info.public_ips.iter().map(|i| {
|
||||
match i {
|
||||
IpAddr::V4(ip) => {
|
||||
u32::from_be_bytes(ip.octets())
|
||||
}
|
||||
IpAddr::V6(_) => {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
}).collect();
|
||||
punch_reply.public_port = nat_info.public_port as u32;
|
||||
punch_reply.public_port_range = nat_info.public_port_range as u32;
|
||||
punch_reply.local_ip = match nat_info.local_ip {
|
||||
IpAddr::V4(ip) => u32::from_be_bytes(ip.octets()),
|
||||
IpAddr::V6(_) => {
|
||||
panic!()
|
||||
}
|
||||
};
|
||||
punch_reply.local_port = nat_info.local_port as u32;
|
||||
punch_reply.nat_type = protobuf::EnumOrUnknown::new(PunchNatType::from(nat_info.nat_type));
|
||||
let bytes = punch_reply.write_to_bytes()?;
|
||||
let mut net_packet = NetPacket::new(vec![0u8; 12 + bytes.len()])?;
|
||||
net_packet.set_version(Version::V1);
|
||||
net_packet.set_protocol(Protocol::OtherTurn);
|
||||
net_packet.set_transport_protocol(turn_packet::Protocol::Punch.into());
|
||||
net_packet.first_set_ttl(MAX_TTL);
|
||||
net_packet.set_source(virtual_ip);
|
||||
net_packet.set_destination(dest);
|
||||
net_packet.set_payload(&bytes);
|
||||
Ok(net_packet.into_buffer())
|
||||
}
|
||||
@@ -1,393 +0,0 @@
|
||||
use std::{io, thread};
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
use std::sync::Arc;
|
||||
|
||||
use chrono::Local;
|
||||
use crossbeam::atomic::AtomicCell;
|
||||
use crossbeam_skiplist::SkipMap;
|
||||
use parking_lot::Mutex;
|
||||
use protobuf::Message;
|
||||
|
||||
use p2p_channel::channel::{Channel, Route, RouteKey};
|
||||
use p2p_channel::punch::NatInfo;
|
||||
use packet::icmp::{icmp, Kind};
|
||||
use packet::ip::ipv4;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::handle::{check_dest, ConnectStatus, CurrentDeviceInfo, PeerDeviceInfo};
|
||||
use crate::handle::registration_handler::Register;
|
||||
use crate::nat;
|
||||
use crate::nat::NatTest;
|
||||
use crate::proto::message::{DeviceList, PunchInfo, PunchNatType, RegistrationResponse};
|
||||
use crate::protocol::{control_packet, MAX_TTL, NetPacket, Protocol, service_packet, turn_packet, Version};
|
||||
use crate::protocol::control_packet::ControlPacket;
|
||||
use crate::protocol::error_packet::InErrorPacket;
|
||||
use crate::tun_device::TunWriter;
|
||||
|
||||
pub fn start(mut handler: RecvHandler) {
|
||||
thread::Builder::new().name("udp-recv-handler".into()).spawn(move || {
|
||||
let mut buf = [0; 4096];
|
||||
loop {
|
||||
match handler.channel.recv_from(&mut buf, None) {
|
||||
Ok((len, route)) => {
|
||||
if let Err(e) = handler.handle(&mut buf[..len], &route) {
|
||||
log::warn!("数据处理失败:{:?},e:{:?}",route,e);
|
||||
if let Error::Stop(_) = e {
|
||||
let _ = handler.channel.close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("{:?}",e);
|
||||
// 检查关闭状态
|
||||
if handler.channel.is_close() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}).unwrap();
|
||||
}
|
||||
|
||||
pub struct RecvHandler {
|
||||
channel: Channel<Ipv4Addr>,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
register: Arc<Register>,
|
||||
nat_test: NatTest,
|
||||
tun_writer: TunWriter,
|
||||
connect_status: Arc<AtomicCell<ConnectStatus>>,
|
||||
peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>>,
|
||||
}
|
||||
|
||||
impl RecvHandler {
|
||||
pub fn new(channel: Channel<Ipv4Addr>,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
register: Arc<Register>,
|
||||
nat_test: NatTest,
|
||||
tun_writer: TunWriter,
|
||||
connect_status: Arc<AtomicCell<ConnectStatus>>,
|
||||
peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
channel,
|
||||
current_device,
|
||||
device_list,
|
||||
register,
|
||||
nat_test,
|
||||
tun_writer,
|
||||
connect_status,
|
||||
peer_nat_info_map,
|
||||
}
|
||||
}
|
||||
pub fn try_clone(&self) -> io::Result<Self> {
|
||||
Ok(Self {
|
||||
channel: self.channel.try_clone()?,
|
||||
current_device: self.current_device.clone(),
|
||||
device_list: self.device_list.clone(),
|
||||
register: self.register.clone(),
|
||||
nat_test: self.nat_test.clone(),
|
||||
tun_writer: self.tun_writer.clone(),
|
||||
connect_status: self.connect_status.clone(),
|
||||
peer_nat_info_map: self.peer_nat_info_map.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl RecvHandler {
|
||||
fn handle(&self, buf: &mut [u8], route_key: &RouteKey) -> crate::Result<()> {
|
||||
let mut net_packet = NetPacket::new(buf)?;
|
||||
if net_packet.ttl() == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
let source = net_packet.source();
|
||||
let current_device = self.current_device.load();
|
||||
if source == current_device.virtual_ip() {
|
||||
return Ok(());
|
||||
}
|
||||
let destination = net_packet.destination();
|
||||
if !destination.is_broadcast() && destination != current_device.broadcast_address
|
||||
&& current_device.virtual_ip() != destination && self.connect_status.load() == ConnectStatus::Connected {
|
||||
if !check_dest(source, current_device.virtual_netmask, current_device.virtual_network) {
|
||||
log::warn!("转发数据,源地址错误:{:?},当前网络:{:?},route_key:{:?}",source,current_device.virtual_network,route_key);
|
||||
return Ok(());
|
||||
}
|
||||
if !check_dest(destination, current_device.virtual_netmask, current_device.virtual_network) {
|
||||
log::warn!("转发数据,目的地址错误:{:?},当前网络:{:?},route_key:{:?}",destination,current_device.virtual_network,route_key);
|
||||
return Ok(());
|
||||
}
|
||||
let ttl = net_packet.ttl();
|
||||
if ttl > 1 {
|
||||
// 转发
|
||||
net_packet.set_ttl(ttl - 1);
|
||||
if let Some(route) = self.channel.route(&destination) {
|
||||
if route.metric <= net_packet.ttl() {
|
||||
self.channel.send_to_route(net_packet.buffer(), &route.route_key())?;
|
||||
}
|
||||
} else if (ttl > 2 || destination == current_device.virtual_gateway())
|
||||
&& source != current_device.virtual_gateway() {
|
||||
//网关默认要转发一次,生存时间不够的发到网关也会被丢弃
|
||||
self.channel.send_to_addr(net_packet.buffer(), current_device.connect_server)?;
|
||||
}
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
match net_packet.protocol() {
|
||||
Protocol::Ipv4Turn => {
|
||||
let mut ipv4 = IpV4Packet::new(net_packet.payload_mut())?;
|
||||
if ipv4.protocol() == ipv4::protocol::Protocol::Icmp {
|
||||
let mut icmp_packet = icmp::IcmpPacket::new(ipv4.payload_mut())?;
|
||||
if icmp_packet.kind() == Kind::EchoRequest {
|
||||
//开启ping
|
||||
icmp_packet.set_kind(Kind::EchoReply);
|
||||
icmp_packet.update_checksum();
|
||||
ipv4.set_source_ip(destination);
|
||||
ipv4.set_destination_ip(source);
|
||||
ipv4.update_checksum();
|
||||
net_packet.set_source(destination);
|
||||
net_packet.set_destination(source);
|
||||
self.channel.send_to_route(net_packet.buffer(), route_key)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
self.tun_writer.write(net_packet.payload())?;
|
||||
}
|
||||
Protocol::Service => {
|
||||
self.service(current_device, source, net_packet, route_key)?;
|
||||
}
|
||||
Protocol::Error => {
|
||||
self.error(current_device, source, net_packet, route_key)?;
|
||||
}
|
||||
Protocol::Control => {
|
||||
self.control(current_device, source, net_packet, route_key)?;
|
||||
}
|
||||
Protocol::OtherTurn => {
|
||||
self.other_turn(current_device, source, net_packet, route_key)?;
|
||||
}
|
||||
Protocol::UnKnow(e) => {
|
||||
log::info!("不支持的协议:{}",e);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn service(&self, current_device: CurrentDeviceInfo, source: Ipv4Addr, net_packet: NetPacket<&mut [u8]>, route_key: &RouteKey) -> crate::Result<()> {
|
||||
if route_key.addr != current_device.connect_server || source != current_device.virtual_gateway() {
|
||||
return Ok(());
|
||||
}
|
||||
match service_packet::Protocol::from(net_packet.transport_protocol()) {
|
||||
service_packet::Protocol::RegistrationRequest => {}
|
||||
service_packet::Protocol::RegistrationResponse => {
|
||||
let response = RegistrationResponse::parse_from_bytes(net_packet.payload())?;
|
||||
let local_port = self.channel.local_addr()?.port();
|
||||
let local_ip = nat::local_ip()?;
|
||||
let nat_info = self.nat_test.re_test(Ipv4Addr::from(response.public_ip), response.public_port as u16, local_ip, local_port);
|
||||
self.channel.set_nat_type(nat_info.nat_type)?;
|
||||
let new_ip = Ipv4Addr::from(response.virtual_ip);
|
||||
let current_ip = current_device.virtual_ip();
|
||||
if current_ip != new_ip {
|
||||
// ip发生变化
|
||||
log::info!("ip发生变化,old_ip:{:?},new_ip:{:?}",current_ip,new_ip);
|
||||
let old_netmask = current_device.virtual_netmask;
|
||||
let old_gateway = current_device.virtual_gateway();
|
||||
let virtual_ip = Ipv4Addr::from(response.virtual_ip);
|
||||
let virtual_gateway = Ipv4Addr::from(response.virtual_gateway);
|
||||
let virtual_netmask = Ipv4Addr::from(response.virtual_netmask);
|
||||
self.tun_writer.change_ip(virtual_ip, virtual_netmask, virtual_gateway, old_netmask, old_gateway)?;
|
||||
let new_current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway,
|
||||
virtual_netmask, current_device.connect_server);
|
||||
if let Err(e) = self.current_device.compare_exchange(current_device, new_current_device) {
|
||||
log::warn!("替换失败:{:?}",e);
|
||||
}
|
||||
}
|
||||
self.connect_status.store(ConnectStatus::Connected);
|
||||
}
|
||||
service_packet::Protocol::PollDeviceList => {}
|
||||
service_packet::Protocol::PushDeviceList => {
|
||||
let device_list_t = DeviceList::parse_from_bytes(net_packet.payload())?;
|
||||
let ip_list = device_list_t
|
||||
.device_info_list
|
||||
.into_iter()
|
||||
.map(|info| {
|
||||
PeerDeviceInfo::new(
|
||||
Ipv4Addr::from(info.virtual_ip),
|
||||
info.name,
|
||||
info.device_status as u8,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let mut dev = self.device_list.lock();
|
||||
if dev.0 != device_list_t.epoch as u16 {
|
||||
dev.0 = device_list_t.epoch as u16;
|
||||
dev.1 = ip_list;
|
||||
}
|
||||
}
|
||||
service_packet::Protocol::UnKnow(u) => {
|
||||
log::warn!("未知服务协议:{}",u);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn error(&self, current_device: CurrentDeviceInfo, source: Ipv4Addr, net_packet: NetPacket<&mut [u8]>, route_key: &RouteKey) -> crate::Result<()> {
|
||||
if route_key.addr != current_device.connect_server || source != current_device.virtual_gateway() {
|
||||
return Ok(());
|
||||
}
|
||||
match InErrorPacket::new(net_packet.transport_protocol(), net_packet.payload())? {
|
||||
InErrorPacket::TokenError => {
|
||||
return Err(Error::Stop("Token error".to_string()));
|
||||
}
|
||||
InErrorPacket::Disconnect => {
|
||||
self.connect_status.store(ConnectStatus::Connecting);
|
||||
self.register.fast_register()?;
|
||||
}
|
||||
InErrorPacket::AddressExhausted => {
|
||||
//地址用尽
|
||||
return Err(Error::Stop("IP address has been exhausted".to_string()));
|
||||
}
|
||||
InErrorPacket::OtherError(e) => {
|
||||
log::error!("OtherError {:?}", e.message());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn control(&self, current_device: CurrentDeviceInfo, source: Ipv4Addr, mut net_packet: NetPacket<&mut [u8]>, route_key: &RouteKey) -> crate::Result<()> {
|
||||
match ControlPacket::new(net_packet.transport_protocol(), net_packet.payload())? {
|
||||
ControlPacket::PingPacket(_) => {
|
||||
let metric = net_packet.source_ttl() - net_packet.ttl() + 1;
|
||||
net_packet.set_transport_protocol(control_packet::Protocol::Pong.into());
|
||||
net_packet.set_source(current_device.virtual_ip());
|
||||
net_packet.set_destination(source);
|
||||
net_packet.first_set_ttl(MAX_TTL);
|
||||
self.channel.send_to_route(net_packet.buffer(), route_key)?;
|
||||
if metric == 1 {
|
||||
if let Some(current_route) = self.channel.route(&source) {
|
||||
if current_route.metric > 1 {
|
||||
let route = Route::from(*route_key, 1, -1);
|
||||
self.channel.add_route(source, route);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ControlPacket::PongPacket(pong_packet) => {
|
||||
let current_time = Local::now().timestamp_millis() as u16;
|
||||
if current_time < pong_packet.time() {
|
||||
return Ok(());
|
||||
}
|
||||
let rt = (current_time - pong_packet.time()) as i64;
|
||||
let metric = net_packet.source_ttl() - net_packet.ttl() + 1;
|
||||
if let Some(current_route) = self.channel.route(&source) {
|
||||
if ¤t_route.route_key() == route_key {
|
||||
self.channel.update_route(&source, metric, rt);
|
||||
} else if current_route.metric >= metric && current_route.rt > rt {
|
||||
let route = Route::from(*route_key, metric, rt);
|
||||
self.channel.add_route(source, route);
|
||||
}
|
||||
} else {
|
||||
let route = Route::from(*route_key, metric, rt);
|
||||
self.channel.add_route(source, route);
|
||||
}
|
||||
if route_key.addr == current_device.connect_server && source == current_device.virtual_gateway() {
|
||||
let epoch = self.device_list.lock().0;
|
||||
if pong_packet.epoch() != epoch {
|
||||
let mut poll_device = NetPacket::new([0; 12])?;
|
||||
poll_device.set_source(current_device.virtual_ip());
|
||||
poll_device.set_destination(source);
|
||||
poll_device.set_version(Version::V1);
|
||||
poll_device.first_set_ttl(MAX_TTL);
|
||||
poll_device.set_protocol(Protocol::Service);
|
||||
poll_device.set_transport_protocol(service_packet::Protocol::PollDeviceList.into());
|
||||
self.channel.send_to_route(poll_device.buffer(), route_key)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
ControlPacket::PunchRequest => {
|
||||
// log::info!("PunchRequest route_key:{:?}",route_key);
|
||||
//回应
|
||||
net_packet.set_transport_protocol(control_packet::Protocol::PunchResponse.into());
|
||||
net_packet.set_source(current_device.virtual_ip());
|
||||
net_packet.set_destination(source);
|
||||
net_packet.first_set_ttl(1);
|
||||
self.channel.send_to_route(net_packet.buffer(), route_key)?;
|
||||
let route = Route::from(*route_key, 1, -1);
|
||||
self.channel.add_route(source, route);
|
||||
}
|
||||
ControlPacket::PunchResponse => {
|
||||
// log::info!("PunchResponse route_key:{:?}",route_key);
|
||||
let route = Route::from(*route_key, 1, -1);
|
||||
self.channel.add_route(net_packet.source(), route);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn other_turn(&self, current_device: CurrentDeviceInfo, source: Ipv4Addr, net_packet: NetPacket<&mut [u8]>, route_key: &RouteKey) -> crate::Result<()> {
|
||||
match turn_packet::Protocol::from(net_packet.transport_protocol()) {
|
||||
turn_packet::Protocol::Punch => {
|
||||
let punch_info = PunchInfo::parse_from_bytes(net_packet.payload())?;
|
||||
let public_ips = punch_info.public_ip_list.
|
||||
iter().map(|v| { IpAddr::from(v.to_be_bytes()) }).collect();
|
||||
let peer_nat_info = NatInfo::new(public_ips,
|
||||
punch_info.public_port as u16,
|
||||
punch_info.public_port_range as u16,
|
||||
IpAddr::from(punch_info.local_ip.to_be_bytes()),
|
||||
punch_info.local_port as u16,
|
||||
punch_info.nat_type.enum_value_or_default().into());
|
||||
self.peer_nat_info_map.insert(source, peer_nat_info.clone());
|
||||
if !punch_info.reply {
|
||||
let mut punch_reply = PunchInfo::new();
|
||||
punch_reply.reply = true;
|
||||
let nat_info = self.nat_test.nat_info();
|
||||
punch_reply.public_ip_list = nat_info.public_ips.iter().map(|i| {
|
||||
match i {
|
||||
IpAddr::V4(ip) => u32::from_be_bytes(ip.octets()),
|
||||
IpAddr::V6(_) => 0
|
||||
}
|
||||
}).collect();
|
||||
punch_reply.public_port = nat_info.public_port as u32;
|
||||
punch_reply.public_port_range = nat_info.public_port_range as u32;
|
||||
punch_reply.nat_type =
|
||||
protobuf::EnumOrUnknown::new(PunchNatType::from(nat_info.nat_type));
|
||||
punch_reply.local_ip = match nat_info.local_ip {
|
||||
IpAddr::V4(ip) => u32::from_be_bytes(ip.octets()),
|
||||
IpAddr::V6(_) => 0
|
||||
};
|
||||
punch_reply.local_port = nat_info.local_port as u32;
|
||||
let bytes = punch_reply.write_to_bytes()?;
|
||||
let mut net_packet =
|
||||
NetPacket::new(vec![0u8; 12 + bytes.len()])?;
|
||||
net_packet.set_version(Version::V1);
|
||||
net_packet.set_protocol(Protocol::OtherTurn);
|
||||
net_packet.set_transport_protocol(
|
||||
turn_packet::Protocol::Punch.into(),
|
||||
);
|
||||
net_packet.first_set_ttl(MAX_TTL);
|
||||
net_packet.set_source(current_device.virtual_ip());
|
||||
net_packet.set_destination(source);
|
||||
net_packet.set_payload(&bytes);
|
||||
if !peer_nat_info.local_ip.is_unspecified() && peer_nat_info.local_port != 0 {
|
||||
let mut packet = NetPacket::new([0u8; 12])?;
|
||||
packet.set_version(Version::V1);
|
||||
packet.first_set_ttl(1);
|
||||
packet.set_protocol(Protocol::Control);
|
||||
packet.set_transport_protocol(control_packet::Protocol::PunchRequest.into());
|
||||
packet.set_source(current_device.virtual_ip());
|
||||
packet.set_destination(source);
|
||||
let _ = self.channel.send_to_addr(packet.buffer(), SocketAddr::new(peer_nat_info.local_ip, peer_nat_info.local_port));
|
||||
}
|
||||
if let Err(e) = self.channel.punch(source, peer_nat_info) {
|
||||
log::warn!("发送到打洞通道失败 {:?}",e);
|
||||
return Ok(());
|
||||
}
|
||||
self.channel.send_to_route(net_packet.buffer(), route_key)?;
|
||||
} else {
|
||||
let _ = self.channel.punch(source, peer_nat_info);
|
||||
}
|
||||
}
|
||||
turn_packet::Protocol::UnKnow(e) => {
|
||||
log::warn!("不支持的转发协议 {:?},source:{:?}",e,source);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
use std::io;
|
||||
use std::net::{Ipv4Addr, SocketAddr};
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
||||
use std::time::Duration;
|
||||
|
||||
use chrono::Local;
|
||||
use protobuf::Message;
|
||||
use p2p_channel::channel::Channel;
|
||||
use p2p_channel::channel::sender::Sender;
|
||||
|
||||
use crate::error::*;
|
||||
use crate::proto::message::{RegistrationRequest, RegistrationResponse};
|
||||
use crate::protocol::error_packet::InErrorPacket;
|
||||
use crate::protocol::{service_packet, NetPacket, Protocol, Version, MAX_TTL};
|
||||
|
||||
///向中继服务器注册,token标识一个虚拟网关,device_id防止多次注册时得到的ip不一致
|
||||
pub fn registration(
|
||||
channel: &mut Channel<Ipv4Addr>,
|
||||
server_address: SocketAddr,
|
||||
token: String,
|
||||
device_id: String,
|
||||
name: String,
|
||||
) -> Result<RegistrationResponse> {
|
||||
let request_packet =
|
||||
registration_request_packet(token.clone(), device_id.clone(), name.clone(), false)?;
|
||||
let buf = request_packet.buffer();
|
||||
let mut recv_buf = [0u8; 10240];
|
||||
channel.send_to_addr(buf, server_address)?;
|
||||
let (len, route) = channel.recv_from(&mut recv_buf, Some(Duration::from_millis(300)))?;
|
||||
if server_address != route.addr {
|
||||
return Err(Error::Warn(format!("数据来源错误:{:?}", route.addr)));
|
||||
}
|
||||
let net_packet = NetPacket::new(&recv_buf[..len])?;
|
||||
return match net_packet.protocol() {
|
||||
Protocol::Service => {
|
||||
match service_packet::Protocol::from(net_packet.transport_protocol()) {
|
||||
service_packet::Protocol::RegistrationResponse => {
|
||||
let response =
|
||||
RegistrationResponse::parse_from_bytes(net_packet.payload())?;
|
||||
Ok(response)
|
||||
}
|
||||
_ => {
|
||||
Err(Error::Warn(format!("数据错误:{:?}", net_packet)))
|
||||
}
|
||||
}
|
||||
}
|
||||
Protocol::Error => {
|
||||
match InErrorPacket::new(
|
||||
net_packet.transport_protocol(),
|
||||
net_packet.payload(),
|
||||
) {
|
||||
Ok(e) => match e {
|
||||
InErrorPacket::TokenError => Err(Error::Stop("token错误".to_string())),
|
||||
InErrorPacket::Disconnect => Err(Error::Warn("断开连接".to_string())),
|
||||
InErrorPacket::AddressExhausted => Err(Error::Stop("地址用尽".to_string())),
|
||||
InErrorPacket::OtherError(e) => match e.message() {
|
||||
Ok(str) => Err(Error::Warn(str)),
|
||||
Err(e) => Err(Error::Warn(format!("{:?}", e))),
|
||||
},
|
||||
},
|
||||
Err(e) => Err(Error::Warn(format!("{:?}", e))),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
Err(Error::Warn(format!("数据错误:{:?}", net_packet)))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn registration_request_packet(
|
||||
token: String,
|
||||
device_id: String,
|
||||
name: String,
|
||||
is_fast: bool,
|
||||
) -> crate::Result<NetPacket<Vec<u8>>> {
|
||||
let mut request = RegistrationRequest::new();
|
||||
request.token = token;
|
||||
request.device_id = device_id;
|
||||
request.name = name;
|
||||
request.is_fast = is_fast;
|
||||
let bytes = request.write_to_bytes()?;
|
||||
let buf = vec![0u8; 12 + bytes.len()];
|
||||
let mut net_packet = NetPacket::new(buf)?;
|
||||
net_packet.set_version(Version::V1);
|
||||
net_packet.set_protocol(Protocol::Service);
|
||||
net_packet.set_transport_protocol(service_packet::Protocol::RegistrationRequest.into());
|
||||
net_packet.first_set_ttl(MAX_TTL);
|
||||
net_packet.set_payload(&bytes);
|
||||
Ok(net_packet)
|
||||
}
|
||||
|
||||
pub struct Register {
|
||||
sender: Sender<Ipv4Addr>,
|
||||
server_address: SocketAddr,
|
||||
token: String,
|
||||
device_id: String,
|
||||
name: String,
|
||||
time: AtomicI64,
|
||||
}
|
||||
|
||||
impl Register {
|
||||
pub fn new(sender: Sender<Ipv4Addr>,
|
||||
server_address: SocketAddr,
|
||||
token: String,
|
||||
device_id: String,
|
||||
name: String, ) -> Self {
|
||||
Self {
|
||||
sender,
|
||||
server_address,
|
||||
token,
|
||||
device_id,
|
||||
name,
|
||||
time: AtomicI64::new(0),
|
||||
}
|
||||
}
|
||||
pub fn fast_register(&self) -> io::Result<()> {
|
||||
let last = self.time.load(Ordering::Relaxed);
|
||||
let new = Local::now().timestamp_millis();
|
||||
if new - last < 1000
|
||||
|| self.time
|
||||
.compare_exchange(last, new, Ordering::Relaxed, Ordering::Relaxed)
|
||||
.is_err()
|
||||
{
|
||||
//短时间不重复注册
|
||||
return Ok(());
|
||||
}
|
||||
log::info!("重新连接");
|
||||
let request_packet =
|
||||
registration_request_packet(self.token.clone(),
|
||||
self.device_id.clone(),
|
||||
self.name.clone(), false).unwrap();
|
||||
let buf = request_packet.buffer();
|
||||
self.sender.send_to_addr(buf, self.server_address)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
use std::{io, thread};
|
||||
/// 接收tun数据,并且转发到udp上
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
use crossbeam::atomic::AtomicCell;
|
||||
|
||||
use p2p_channel::channel::sender::Sender;
|
||||
use packet::icmp::icmp::IcmpPacket;
|
||||
use packet::icmp::Kind;
|
||||
use packet::ip::ipv4;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
|
||||
use crate::error::*;
|
||||
use crate::handle::{check_dest, CurrentDeviceInfo};
|
||||
use crate::protocol::{MAX_TTL, NetPacket, Protocol, Version};
|
||||
use crate::tun_device::{TunReader, TunWriter};
|
||||
|
||||
|
||||
fn icmp(tun_writer: &TunWriter, mut ipv4_packet: IpV4Packet<&mut [u8]>) -> Result<()> {
|
||||
if ipv4_packet.protocol() == ipv4::protocol::Protocol::Icmp {
|
||||
let mut icmp = IcmpPacket::new(ipv4_packet.payload_mut())?;
|
||||
if icmp.kind() == Kind::EchoRequest {
|
||||
icmp.set_kind(Kind::EchoReply);
|
||||
icmp.update_checksum();
|
||||
let src = ipv4_packet.source_ip();
|
||||
ipv4_packet.set_source_ip(ipv4_packet.destination_ip());
|
||||
ipv4_packet.set_destination_ip(src);
|
||||
ipv4_packet.update_checksum();
|
||||
tun_writer.write(ipv4_packet.buffer)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn handle(sender: &Sender<Ipv4Addr>, data: &mut [u8], tun_writer: &TunWriter, current_device: CurrentDeviceInfo, net_packet: &mut NetPacket<Vec<u8>>) -> Result<()> {
|
||||
let data_len = data.len();
|
||||
let ipv4_packet = match IpV4Packet::new(data) {
|
||||
Ok(ipv4_packet) => ipv4_packet,
|
||||
Err(packet::error::Error::Unimplemented) => {
|
||||
return Ok(());
|
||||
}
|
||||
Err(e) => Err(e)?,
|
||||
};
|
||||
let src_ip = ipv4_packet.source_ip();
|
||||
let dest_ip = ipv4_packet.destination_ip();
|
||||
// if dest_ip == cur_info.broadcast_address {
|
||||
// // 启动服务后会收到对137端口的广播
|
||||
// // 137端口是在局域网中提供计算机的名字或IP地址查询服务
|
||||
// return Ok(());
|
||||
// }
|
||||
if src_ip != current_device.virtual_ip() || (!check_dest(dest_ip, current_device.virtual_netmask, current_device.virtual_network) && !dest_ip.is_broadcast()) {
|
||||
return Ok(());
|
||||
}
|
||||
if src_ip == dest_ip {
|
||||
return icmp(&tun_writer, ipv4_packet);
|
||||
}
|
||||
net_packet.set_source(src_ip);
|
||||
net_packet.set_destination(dest_ip);
|
||||
net_packet.set_payload(ipv4_packet.buffer);
|
||||
//优先发到直连到地址
|
||||
if sender.send_to_id(&net_packet.buffer()[..(12 + data_len)], &dest_ip).is_err() {
|
||||
sender.send_to_addr(&net_packet.buffer()[..(12 + data_len)], current_device.connect_server)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub fn start(sender: Sender<Ipv4Addr>,
|
||||
tun_reader: TunReader,
|
||||
tun_writer: TunWriter,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>, ) {
|
||||
thread::Builder::new().name("tun-handler".into()).spawn(move || {
|
||||
if let Err(e) = start_(sender, tun_reader, tun_writer, current_device) {
|
||||
log::warn!("{:?}",e);
|
||||
}
|
||||
}).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn start_(sender: Sender<Ipv4Addr>,
|
||||
tun_reader: TunReader,
|
||||
tun_writer: TunWriter,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>, ) -> io::Result<()> {
|
||||
let mut net_packet = NetPacket::new(vec![0u8; 4 + 8 + 1500])?;
|
||||
net_packet.set_version(Version::V1);
|
||||
net_packet.set_protocol(Protocol::Ipv4Turn);
|
||||
net_packet.set_transport_protocol(ipv4::protocol::Protocol::Ipv4.into());
|
||||
net_packet.set_ttl(MAX_TTL);
|
||||
loop {
|
||||
let mut data = tun_reader.next()?;
|
||||
match handle(&sender, data.bytes_mut(), &tun_writer, current_device.load(), &mut net_packet) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
log::warn!("{:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
fn start_(sender: Sender<Ipv4Addr>,
|
||||
tun_reader: TunReader,
|
||||
tun_writer: TunWriter,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>, ) -> io::Result<()> {
|
||||
let mut net_packet = NetPacket::new(vec![0u8; 4 + 8 + 1500])?;
|
||||
net_packet.set_version(Version::V1);
|
||||
net_packet.set_protocol(Protocol::Ipv4Turn);
|
||||
net_packet.set_transport_protocol(ipv4::protocol::Protocol::Ipv4.into());
|
||||
net_packet.set_ttl(MAX_TTL);
|
||||
let mut buf = [0; 4096];
|
||||
loop {
|
||||
let data = tun_reader.read(&mut buf)?;
|
||||
match handle(&sender, data, &tun_writer, current_device.load(), &mut net_packet) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
log::warn!("{:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
use crate::error::Error;
|
||||
|
||||
|
||||
pub use p2p_channel::channel::{Route, RouteKey};
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
pub mod error;
|
||||
pub mod handle;
|
||||
pub mod nat;
|
||||
pub mod proto;
|
||||
pub mod protocol;
|
||||
pub mod tun_device;
|
||||
pub mod core;
|
||||
@@ -1,171 +0,0 @@
|
||||
use std::collections::HashSet;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
|
||||
use std::time::Duration;
|
||||
use std::{io, thread};
|
||||
use p2p_channel::punch::NatType;
|
||||
|
||||
|
||||
// #[derive(Debug, Copy, Clone, PartialEq)]
|
||||
// pub enum NatType {
|
||||
// Symmetric,
|
||||
// Cone,
|
||||
// }
|
||||
//
|
||||
// impl Into<u8> for NatType {
|
||||
// fn into(self) -> u8 {
|
||||
// match self {
|
||||
// NatType::Symmetric => 0,
|
||||
// NatType::Cone => 1,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/// 返回所有公网ip和端口变化范围
|
||||
pub fn public_ip_list(addrs: &Vec<SocketAddr>) -> io::Result<(NatType, Vec<Ipv4Addr>, u16)> {
|
||||
let mut hash_set = HashSet::new();
|
||||
let mut max_port_range = 0;
|
||||
let mut nat_type = NatType::Cone;
|
||||
let mut port = 88;
|
||||
for _ in 0..3 {
|
||||
let udp = loop {
|
||||
match UdpSocket::bind(SocketAddr::new(IpAddr::from(Ipv4Addr::from(0)), port)) {
|
||||
Ok(udp) => {
|
||||
break udp;
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::AddrInUse {
|
||||
port += 1;
|
||||
continue;
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
let (set, min_port, max_port) = public_ip_list_(&udp, addrs)?;
|
||||
drop(udp);
|
||||
let port_range = max_port - min_port;
|
||||
//有多个ip或者端口有变化,说明是对称nat
|
||||
if nat_type == NatType::Cone && (set.len() > 1 || port_range != 0) {
|
||||
nat_type = NatType::Symmetric;
|
||||
}
|
||||
if max_port_range < port_range {
|
||||
max_port_range = port_range;
|
||||
}
|
||||
for x in set {
|
||||
hash_set.insert(x);
|
||||
}
|
||||
thread::sleep(Duration::from_micros(5));
|
||||
}
|
||||
Ok((nat_type, hash_set.into_iter().collect(), max_port_range))
|
||||
}
|
||||
|
||||
/// 测试样本较少,可能不对
|
||||
///
|
||||
/// - 移动宽带:锥形网络、一个ip、端口和局域网端口不相同
|
||||
/// - 电信宽带:锥形网络、一个ip,端口和局域网端口不相同
|
||||
/// - 联调宽带:对称网络、端口不变ip轮流用
|
||||
/// - 移动4g:对称网络、ip端口都变 使用小的端口变化量小
|
||||
/// - 联通4g:对称网络、只有一个ip 端口变化大
|
||||
/// - 电信4g:对称网络只有一个ip 公网端口比较连续
|
||||
/// - 综上:客户端使用小端口,针对对称网络 尝试所有ip 公网端口+-变化量的范围
|
||||
/// - 打通概率 移动宽带=电信宽带>联调宽带>电信4g>移动4g>>联调4g
|
||||
pub fn public_ip_list_(
|
||||
udp: &UdpSocket,
|
||||
addrs: &Vec<SocketAddr>,
|
||||
) -> io::Result<(HashSet<Ipv4Addr>, u16, u16)> {
|
||||
// println!("local port {:?}", udp.local_addr().unwrap().port());
|
||||
udp.set_read_timeout(Some(Duration::from_millis(300)))?;
|
||||
let mut buf = [0u8; 128];
|
||||
for addr in addrs {
|
||||
let _ = udp.send_to(b"NatTest", addr)?;
|
||||
}
|
||||
let mut hash_set = HashSet::new();
|
||||
let mut count = 0;
|
||||
let mut min_port = 65535;
|
||||
let mut max_port = 0;
|
||||
for _ in 0..addrs.len() {
|
||||
if let Ok(len) = udp.recv(&mut buf) {
|
||||
if len != 16 || &buf[..10] != &b"NatType213"[..] {
|
||||
continue;
|
||||
}
|
||||
let port = u16::from_be_bytes([buf[14], buf[15]]);
|
||||
if min_port > port {
|
||||
min_port = port;
|
||||
}
|
||||
if max_port < port {
|
||||
max_port = port;
|
||||
}
|
||||
let ip = Ipv4Addr::new(buf[10], buf[11], buf[12], buf[13]);
|
||||
hash_set.insert(ip);
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
if count <= 1 {
|
||||
return Err(io::Error::from(io::ErrorKind::TimedOut));
|
||||
}
|
||||
Ok((hash_set, min_port, max_port))
|
||||
}
|
||||
|
||||
/// 返回nat类型
|
||||
pub fn nat_test() -> io::Result<NatType> {
|
||||
for _ in 0..3 {
|
||||
if NatType::Symmetric == nat_test_()? {
|
||||
return Ok(NatType::Symmetric);
|
||||
}
|
||||
thread::sleep(Duration::from_micros(5));
|
||||
}
|
||||
Ok(NatType::Cone)
|
||||
}
|
||||
|
||||
pub fn nat_test_() -> io::Result<NatType> {
|
||||
let udp = UdpSocket::bind("0.0.0.0:0")?;
|
||||
udp.set_read_timeout(Some(Duration::from_millis(300)))?;
|
||||
let mut buf = [0u8; 128];
|
||||
let _ = udp.send_to(b"NatTest", "nat1.wherewego.top:35061")?;
|
||||
let _ = udp.send_to(b"NatTest", "nat1.wherewego.top:35062")?;
|
||||
let _ = udp.send_to(b"NatTest", "nat2.wherewego.top:35061")?;
|
||||
let _ = udp.send_to(b"NatTest", "nat2.wherewego.top:35062")?;
|
||||
let mut tmp_ip_port: Option<[u8; 6]> = None;
|
||||
let mut count = 0;
|
||||
for _ in 0..4 {
|
||||
if let Ok(len) = udp.recv(&mut buf) {
|
||||
if len != 16 || &buf[..10] != &b"NatType213"[..] {
|
||||
continue;
|
||||
}
|
||||
count += 1;
|
||||
let mut ip_port = [0u8; 6];
|
||||
ip_port.copy_from_slice(&buf[10..16]);
|
||||
if let Some(tmp_ip_port) = &tmp_ip_port {
|
||||
if tmp_ip_port != &ip_port {
|
||||
return Ok(NatType::Symmetric);
|
||||
}
|
||||
} else {
|
||||
tmp_ip_port = Some(ip_port);
|
||||
}
|
||||
}
|
||||
}
|
||||
if count <= 1 {
|
||||
return Err(io::Error::from(io::ErrorKind::TimedOut));
|
||||
}
|
||||
Ok(NatType::Cone)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nat_test_run() {
|
||||
let udp = UdpSocket::bind("0.0.0.0:101").unwrap();
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs, UdpSocket};
|
||||
let addrs = vec![
|
||||
"nat1.wherewego.top:35062"
|
||||
.to_socket_addrs()
|
||||
.unwrap()
|
||||
.next()
|
||||
.unwrap(),
|
||||
"nat2.wherewego.top:35062"
|
||||
.to_socket_addrs()
|
||||
.unwrap()
|
||||
.next()
|
||||
.unwrap(),
|
||||
];
|
||||
let print = public_ip_list_(&udp, &addrs).unwrap();
|
||||
println!("{:?}", print);
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
use std::io;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
use std::sync::Arc;
|
||||
use parking_lot::Mutex;
|
||||
use p2p_channel::punch::{NatInfo, NatType};
|
||||
use crate::proto::message::PunchNatType;
|
||||
|
||||
pub mod check;
|
||||
|
||||
use std::net::UdpSocket;
|
||||
|
||||
pub fn local_ip() -> io::Result<IpAddr> {
|
||||
let socket = UdpSocket::bind("0.0.0.0:0")?;
|
||||
socket.connect("8.8.8.8:80")?;
|
||||
let addr = socket.local_addr()?;
|
||||
Ok(addr.ip())
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NatTest {
|
||||
nat_test_server: Arc<Vec<SocketAddr>>,
|
||||
info: Arc<Mutex<NatInfo>>,
|
||||
}
|
||||
|
||||
impl From<NatType> for PunchNatType {
|
||||
fn from(value: NatType) -> Self {
|
||||
match value {
|
||||
NatType::Symmetric => PunchNatType::Symmetric,
|
||||
NatType::Cone => PunchNatType::Cone
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<NatType> for PunchNatType {
|
||||
fn into(self) -> NatType {
|
||||
match self {
|
||||
PunchNatType::Symmetric => NatType::Symmetric,
|
||||
PunchNatType::Cone => NatType::Cone
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NatTest {
|
||||
pub fn new(nat_test_server: Vec<SocketAddr>, public_ip: Ipv4Addr, public_port: u16, local_ip: IpAddr, local_port: u16) -> NatTest {
|
||||
let info = NatTest::re_test_(&nat_test_server, public_ip, public_port, local_ip, local_port);
|
||||
NatTest {
|
||||
nat_test_server: Arc::new(nat_test_server),
|
||||
info: Arc::new(Mutex::new(info)),
|
||||
}
|
||||
}
|
||||
pub fn nat_info(&self) -> NatInfo {
|
||||
self.info.lock().clone()
|
||||
}
|
||||
pub fn re_test(&self, public_ip: Ipv4Addr, public_port: u16, local_ip: IpAddr, local_port: u16) -> NatInfo {
|
||||
let info = NatTest::re_test_(&self.nat_test_server, public_ip, public_port, local_ip, local_port);
|
||||
*self.info.lock() = info.clone();
|
||||
info
|
||||
}
|
||||
fn re_test_(nat_test_server: &Vec<SocketAddr>, public_ip: Ipv4Addr, public_port: u16, local_ip: IpAddr, local_port: u16) -> NatInfo {
|
||||
return match check::public_ip_list(nat_test_server) {
|
||||
Ok((nat_type, ips, port_range)) => {
|
||||
let mut public_ips = Vec::new();
|
||||
public_ips.push(IpAddr::from(public_ip));
|
||||
for ip in ips {
|
||||
if ip != public_ip {
|
||||
public_ips.push(IpAddr::from(ip));
|
||||
}
|
||||
}
|
||||
NatInfo::new(public_ips,
|
||||
public_port,
|
||||
port_range,
|
||||
local_ip, local_port,
|
||||
nat_type, )
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("{:?}",e);
|
||||
NatInfo::new(
|
||||
vec![IpAddr::from(public_ip)],
|
||||
public_port,
|
||||
0,
|
||||
local_ip, local_port,
|
||||
NatType::Cone,
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
use std::{fmt, io};
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
/*
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 版本(8) | 协议(8) | 上层协议(8) | 初始ttl(4) | 生存时间(4) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 源ip地址(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 目的ip地址(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 数据体 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
pub mod control_packet;
|
||||
pub mod error_packet;
|
||||
pub mod service_packet;
|
||||
pub mod turn_packet;
|
||||
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||
pub enum Version {
|
||||
V1,
|
||||
UnKnow(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for Version {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
1 => Version::V1,
|
||||
val => Version::UnKnow(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for Version {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
Version::V1 => 1,
|
||||
Version::UnKnow(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||
pub enum Protocol {
|
||||
/// 服务包 用于和服务端交互
|
||||
Service,
|
||||
/// 响应异常
|
||||
Error,
|
||||
/// 控制协议
|
||||
Control,
|
||||
/// 转发ipv4数据
|
||||
Ipv4Turn,
|
||||
/// 转发其他数据
|
||||
OtherTurn,
|
||||
UnKnow(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for Protocol {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
1 => Protocol::Service,
|
||||
2 => Protocol::Error,
|
||||
3 => Protocol::Control,
|
||||
4 => Protocol::Ipv4Turn,
|
||||
5 => Protocol::OtherTurn,
|
||||
val => Protocol::UnKnow(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for Protocol {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
Protocol::Service => 1,
|
||||
Protocol::Error => 2,
|
||||
Protocol::Control => 3,
|
||||
Protocol::Ipv4Turn => 4,
|
||||
Protocol::OtherTurn => 5,
|
||||
Protocol::UnKnow(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const MAX_TTL: u8 = 0b1111;
|
||||
pub const MAX_SOURCE: u8 = 0b11110000;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct NetPacket<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> NetPacket<B> {
|
||||
pub fn new(buffer: B) -> io::Result<NetPacket<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
// 不能大于udp最大载荷长度
|
||||
if len < 12 || len > 65535 - 20 - 8 {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData, "length overflow"));
|
||||
}
|
||||
Ok(NetPacket { buffer })
|
||||
}
|
||||
pub fn buffer(&self) -> &[u8] {
|
||||
self.buffer.as_ref()
|
||||
}
|
||||
pub fn into_buffer(self) -> B {
|
||||
self.buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> NetPacket<B> {
|
||||
pub fn version(&self) -> Version {
|
||||
Version::from(self.buffer.as_ref()[0])
|
||||
}
|
||||
pub fn protocol(&self) -> Protocol {
|
||||
Protocol::from(self.buffer.as_ref()[1])
|
||||
}
|
||||
pub fn transport_protocol(&self) -> u8 {
|
||||
self.buffer.as_ref()[2]
|
||||
}
|
||||
pub fn ttl(&self) -> u8 {
|
||||
self.buffer.as_ref()[3] & MAX_TTL
|
||||
}
|
||||
pub fn source_ttl(&self) -> u8 {
|
||||
self.buffer.as_ref()[3] >> 4
|
||||
}
|
||||
pub fn source(&self) -> Ipv4Addr {
|
||||
let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap();
|
||||
Ipv4Addr::from(tmp)
|
||||
}
|
||||
pub fn destination(&self) -> Ipv4Addr {
|
||||
let tmp: [u8; 4] = self.buffer.as_ref()[8..12].try_into().unwrap();
|
||||
Ipv4Addr::from(tmp)
|
||||
}
|
||||
pub fn payload(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[12..]
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> NetPacket<B> {
|
||||
pub fn set_version(&mut self, version: Version) {
|
||||
self.buffer.as_mut()[0] = version.into();
|
||||
}
|
||||
pub fn set_protocol(&mut self, protocol: Protocol) {
|
||||
self.buffer.as_mut()[1] = protocol.into();
|
||||
}
|
||||
pub fn set_transport_protocol(&mut self, transport_protocol: u8) {
|
||||
self.buffer.as_mut()[2] = transport_protocol;
|
||||
}
|
||||
pub fn first_set_ttl(&mut self, ttl: u8) {
|
||||
self.buffer.as_mut()[3] = ttl << 4 | ttl;
|
||||
}
|
||||
pub fn set_ttl(&mut self, ttl: u8) {
|
||||
self.buffer.as_mut()[3] = (self.buffer.as_mut()[3] & MAX_SOURCE) | (MAX_TTL & ttl);
|
||||
}
|
||||
pub fn set_source_ttl(&mut self, source_ttl: u8) {
|
||||
self.buffer.as_mut()[3] = (source_ttl << 4) | (MAX_TTL & self.buffer.as_ref()[3]);
|
||||
}
|
||||
pub fn set_source(&mut self, source: Ipv4Addr) {
|
||||
self.buffer.as_mut()[4..8].copy_from_slice(&source.octets());
|
||||
}
|
||||
pub fn set_destination(&mut self, destination: Ipv4Addr) {
|
||||
self.buffer.as_mut()[8..12].copy_from_slice(&destination.octets());
|
||||
}
|
||||
pub fn set_payload(&mut self, payload: &[u8]) {
|
||||
self.buffer.as_mut()[12..payload.len() + 12].copy_from_slice(payload);
|
||||
}
|
||||
pub fn payload_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.buffer.as_mut()[12..]
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for NetPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("NetPacket")
|
||||
.field("version", &self.version())
|
||||
.field("protocol", &self.protocol())
|
||||
.field("transport_protocol", &self.transport_protocol())
|
||||
.field("ttl", &self.ttl())
|
||||
.field("source_ttl", &self.source_ttl())
|
||||
.field("source", &self.source())
|
||||
.field("destination", &self.destination())
|
||||
.field("payload", &self.payload())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
use crate::tun_device::{TunReader, TunWriter};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
use tun::Device;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
pub fn create_tun(
|
||||
address: Ipv4Addr,
|
||||
netmask: Ipv4Addr,
|
||||
gateway: Ipv4Addr,
|
||||
) -> crate::error::Result<(TunWriter, TunReader)> {
|
||||
let mut config = tun::Configuration::default();
|
||||
|
||||
config
|
||||
.destination(gateway)
|
||||
.address(address)
|
||||
.netmask(netmask)
|
||||
.mtu(1420)
|
||||
// .queues(2) 用多个队列有兼容性问题
|
||||
.up();
|
||||
//
|
||||
// config.platform(|config| {
|
||||
// config.packet_information(true);
|
||||
// });
|
||||
|
||||
let dev = tun::create(&config).unwrap();
|
||||
let packet_information = dev.has_packet_information();
|
||||
let queue = dev.queue(0).unwrap();
|
||||
let reader = queue.reader();
|
||||
let writer = queue.writer();
|
||||
Ok((
|
||||
TunWriter(writer, packet_information, Arc::new(Mutex::new(dev))),
|
||||
TunReader(reader, packet_information),
|
||||
))
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
use std::net::Ipv4Addr;
|
||||
use std::process::Command;
|
||||
use std::io;
|
||||
use tun::Device;
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::tun_device::{TunReader, TunWriter};
|
||||
|
||||
pub fn create_tun(
|
||||
address: Ipv4Addr,
|
||||
netmask: Ipv4Addr,
|
||||
gateway: Ipv4Addr,
|
||||
) -> crate::error::Result<(TunWriter, TunReader)> {
|
||||
let mut config = tun::Configuration::default();
|
||||
|
||||
config
|
||||
.destination(gateway)
|
||||
.address(address)
|
||||
.netmask(netmask)
|
||||
.mtu(1420)
|
||||
.up();
|
||||
|
||||
let dev = tun::create(&config).unwrap();
|
||||
config_ip(dev.name(), address, netmask, gateway)?;
|
||||
// println!("{:?}", if_config_out);
|
||||
// let cmd_str: String = " ifconfig|grep flags=8051|awk -F ':' '{print $1}'|tail -1".to_string();
|
||||
//
|
||||
// let cmd_str_out = Command::new("sh")
|
||||
// .arg("-c")
|
||||
// .arg(cmd_str)
|
||||
// .output()
|
||||
// .expect("sh exec error!");
|
||||
// if !cmd_str_out.status.success(){
|
||||
// return Err(Error::Stop(format!("设置路由失败:{:?}", cmd_str_out)));
|
||||
// }
|
||||
// println!("{:?}", cmd_str_out);
|
||||
let packet_information = dev.has_packet_information();
|
||||
let queue = dev.queue(0).unwrap();
|
||||
let reader = queue.reader();
|
||||
let writer = queue.writer();
|
||||
Ok((
|
||||
TunWriter(writer, packet_information, Arc::new(Mutex::new(dev))),
|
||||
TunReader(reader, packet_information),
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn config_ip(name: &str, address: Ipv4Addr, netmask: Ipv4Addr, gateway: Ipv4Addr) -> io::Result<()> {
|
||||
let up_eth_str: String = format!("ifconfig {} {:?} {:?} up ", name, address, gateway);
|
||||
let route_add_str: String = format!(
|
||||
"sudo route -n add -net {:?} -netmask {:?} {:?}",
|
||||
address, netmask, gateway
|
||||
);
|
||||
let up_eth_out = Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(up_eth_str)
|
||||
.output()
|
||||
.expect("sh exec error!");
|
||||
if !up_eth_out.status.success() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("设置网络地址失败: {:?}", up_eth_out)));
|
||||
}
|
||||
let if_config_out = Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(route_add_str)
|
||||
.output()
|
||||
.expect("sh exec error!");
|
||||
if !if_config_out.status.success() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("添加路由失败: {:?}", if_config_out)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub use linux::create_tun;
|
||||
#[cfg(target_os = "macos")]
|
||||
pub use mac::create_tun;
|
||||
#[cfg(any(unix))]
|
||||
pub use unix::{TunReader, TunWriter};
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use windows::create_tun;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use windows::{TunReader, TunWriter};
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub mod linux;
|
||||
#[cfg(target_os = "macos")]
|
||||
pub mod mac;
|
||||
#[cfg(any(unix))]
|
||||
pub mod unix;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub mod windows;
|
||||
@@ -1,76 +0,0 @@
|
||||
use std::io;
|
||||
use std::sync::Arc;
|
||||
|
||||
use bytes::BufMut;
|
||||
use tun::platform::posix::{Reader, Writer};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use tun::platform::linux::Device;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
use tun::platform::macos::Device;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TunReader(pub(crate) Reader, pub(crate) bool);
|
||||
|
||||
impl TunReader {
|
||||
pub fn read<'a>(&'a self, buf: &'a mut [u8]) -> io::Result<&mut [u8]> {
|
||||
let len = self.0.read(buf)?;
|
||||
if self.1 {
|
||||
Ok(&mut buf[4..len])
|
||||
} else {
|
||||
Ok(&mut buf[..len])
|
||||
}
|
||||
}
|
||||
pub fn close(&self) {
|
||||
unsafe {
|
||||
let raw = self.0.as_raw_fd();
|
||||
if raw >= 0 {
|
||||
libc::close(raw);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TunWriter(pub(crate) Writer, pub(crate) bool, pub(crate) Arc<Mutex<Device>>);
|
||||
|
||||
impl TunWriter {
|
||||
pub fn write(&self, packet: &[u8]) -> io::Result<()> {
|
||||
if self.1 {
|
||||
let mut buf = Vec::<u8>::with_capacity(4 + packet.len());
|
||||
buf.put_u16(0);
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
buf.put_u16(libc::PF_INET as u16);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
buf.put_u16(libc::ETH_P_IP as u16);
|
||||
buf.extend_from_slice(packet);
|
||||
self.0.write_all(&buf)
|
||||
} else {
|
||||
self.0.write_all(packet)
|
||||
}
|
||||
}
|
||||
pub fn change_ip(&self, address: Ipv4Addr, netmask: Ipv4Addr,
|
||||
gateway: Ipv4Addr, _old_netmask: Ipv4Addr, _old_gateway: Ipv4Addr) -> io::Result<()> {
|
||||
let mut config = tun::Configuration::default();
|
||||
use tun::Device;
|
||||
config
|
||||
.destination(gateway)
|
||||
.address(address)
|
||||
.netmask(netmask)
|
||||
.mtu(1420)
|
||||
// .queues(2)
|
||||
.up();
|
||||
let mut dev = self.2.lock();
|
||||
if let Err(e) = dev.configure(&config) {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("{:?}", e)));
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
if let Err(e) = crate::tun_device::mac::config_ip(dev.name(), address, netmask, gateway){
|
||||
log::error!("{}",e);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use libloading::Library;
|
||||
use parking_lot::Mutex;
|
||||
use wintun::{Adapter, Packet, Session};
|
||||
|
||||
pub const INTERFACE_NAME: &str = "Switch-V1";
|
||||
pub const POOL_NAME: &str = "Switch-V1";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TunWriter(Arc<Session>, Arc<Mutex<u32>>);
|
||||
|
||||
impl TunWriter {
|
||||
pub fn write(&self, buf: &[u8]) -> io::Result<()> {
|
||||
match self.0.allocate_send_packet(buf.len() as u16) {
|
||||
Ok(mut packet) => {
|
||||
packet.bytes_mut().copy_from_slice(buf);
|
||||
self.0.send_packet(packet);
|
||||
return Ok(());
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "send err"));
|
||||
}
|
||||
pub fn change_ip(&self, address: Ipv4Addr, netmask: Ipv4Addr,
|
||||
gateway: Ipv4Addr, old_netmask: Ipv4Addr, old_gateway: Ipv4Addr) -> io::Result<()> {
|
||||
let index = self.1.lock();
|
||||
if let Err(e) = delete_route(*index, old_netmask, old_gateway) {
|
||||
log::warn!("{:?}",e);
|
||||
}
|
||||
config_ip(*index, address, netmask, gateway)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TunReader(pub(crate) Arc<Session>);
|
||||
|
||||
|
||||
impl TunReader {
|
||||
pub fn next(&self) -> io::Result<Packet> {
|
||||
match self.0.receive_blocking() {
|
||||
Ok(packet) => {
|
||||
return Ok(packet);
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "read err"));
|
||||
}
|
||||
pub fn close(&self) {
|
||||
self.0.shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_tun(
|
||||
address: Ipv4Addr,
|
||||
netmask: Ipv4Addr,
|
||||
gateway: Ipv4Addr,
|
||||
) -> io::Result<(TunWriter, TunReader)> {
|
||||
let win_tun = unsafe {
|
||||
match Library::new("wintun.dll") {
|
||||
Ok(library) => match wintun::load_from_library(library) {
|
||||
Ok(win_tun) => win_tun,
|
||||
Err(e) => {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("{:?}", e)));
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("wintun.dll not found");
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("wintun.dll not found {:?}", e)));
|
||||
}
|
||||
}
|
||||
};
|
||||
if let Ok(adapter) = Adapter::open(&win_tun, INTERFACE_NAME) {
|
||||
log::warn!("Switch-V1 未正常退出");
|
||||
drop(adapter);
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
};
|
||||
let adapter = match Adapter::create(&win_tun, POOL_NAME, INTERFACE_NAME, None) {
|
||||
Ok(adapter) => adapter,
|
||||
Err(e) => return Err(io::Error::new(io::ErrorKind::Other, format!("{:?}", e))),
|
||||
};
|
||||
let session = Arc::new(adapter.start_session(wintun::MAX_RING_CAPACITY).unwrap());
|
||||
let index = match adapter.get_adapter_index() {
|
||||
Ok(index) => {
|
||||
index
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("get_adapter_index err {:?}",e);
|
||||
get_if_index()
|
||||
}
|
||||
};
|
||||
config_ip(index, address, netmask, gateway)?;
|
||||
let reader_session = session.clone();
|
||||
Ok((TunWriter(session.clone(), Arc::new(Mutex::new(index))), TunReader(reader_session)))
|
||||
}
|
||||
|
||||
fn get_if_index() -> u32 {
|
||||
let cmd = format!("netsh int ipv4 show interfaces {} |findstr IfIndex", INTERFACE_NAME);
|
||||
let out = std::process::Command::new("cmd")
|
||||
.arg("/C")
|
||||
.arg(&cmd)
|
||||
.output()
|
||||
.unwrap();
|
||||
if !out.status.success() {
|
||||
log::warn!("1获取网络接口索引失败:cmd={:?},out={:?}",cmd,out);
|
||||
return 0;
|
||||
}
|
||||
if let Ok(stdout) = String::from_utf8(out.stdout) {
|
||||
if let Some(start) = stdout.find(":") {
|
||||
if let Some(end) = stdout.find("\r\n") {
|
||||
if let Ok(index) = stdout[start + 1..end].trim().parse::<u32>() {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log::warn!("2获取网络接口索引失败:cmd={:?}",cmd);
|
||||
0
|
||||
}
|
||||
|
||||
fn config_ip(index: u32, address: Ipv4Addr, netmask: Ipv4Addr, gateway: Ipv4Addr) -> io::Result<()> {
|
||||
if index == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("网络接口索引错误: {:?}", index)));
|
||||
}
|
||||
let set_mtu = format!(
|
||||
"netsh interface ipv4 set subinterface {} mtu=1420 store=persistent",
|
||||
index
|
||||
);
|
||||
let set_metric = format!("netsh interface ip set interface {} metric=1", index);
|
||||
let set_address = format!(
|
||||
"netsh interface ip set address {} static {:?} {:?} ", // gateway={:?}
|
||||
index, address, netmask,
|
||||
);
|
||||
// 执行网卡初始化命令
|
||||
let out = std::process::Command::new("cmd")
|
||||
.arg("/C")
|
||||
.arg(set_mtu)
|
||||
.output()
|
||||
.unwrap();
|
||||
if !out.status.success() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("设置mtu失败: {:?}", out)));
|
||||
}
|
||||
let out = std::process::Command::new("cmd")
|
||||
.arg("/C")
|
||||
.arg(set_metric)
|
||||
.output()
|
||||
.unwrap();
|
||||
if !out.status.success() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("设置接口跃点失败: {:?}", out)));
|
||||
}
|
||||
let out = std::process::Command::new("cmd")
|
||||
.arg("/C")
|
||||
.arg(&set_address)
|
||||
.output()
|
||||
.unwrap();
|
||||
if !out.status.success() {
|
||||
log::error!("cmd={:?},out={:?}",set_address,out);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("设置网络地址失败: {:?}", out)));
|
||||
}
|
||||
let dest = {
|
||||
let ip = address.octets();
|
||||
let mask = netmask.octets();
|
||||
Ipv4Addr::from([
|
||||
ip[0] & mask[0],
|
||||
ip[1] & mask[1],
|
||||
ip[2] & mask[2],
|
||||
ip[3] & mask[3],
|
||||
])
|
||||
};
|
||||
let set_route = format!(
|
||||
"route add {:?} mask {:?} {:?} if {}",
|
||||
dest, netmask, gateway, index
|
||||
);
|
||||
// 执行添加路由命令
|
||||
let out = std::process::Command::new("cmd")
|
||||
.arg("/C")
|
||||
.arg(&set_route)
|
||||
.output()
|
||||
.unwrap();
|
||||
if !out.status.success() {
|
||||
log::error!("cmd={:?},out={:?}",set_route,out);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("添加路由失败: {:?}", out)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn delete_route(index: u32, netmask: Ipv4Addr, gateway: Ipv4Addr) -> io::Result<()> {
|
||||
if index == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("网络接口索引错误: {:?}", index)));
|
||||
}
|
||||
let mask = netmask.octets();
|
||||
let ip = gateway.octets();
|
||||
let dest = Ipv4Addr::from([
|
||||
ip[0] & mask[0],
|
||||
ip[1] & mask[1],
|
||||
ip[2] & mask[2],
|
||||
ip[3] & mask[3],
|
||||
]);
|
||||
let delete_route = format!(
|
||||
"route delete {:?} mask {:?} {:?} if {}",
|
||||
dest, netmask, gateway, index
|
||||
);
|
||||
// 删除路由
|
||||
let out = std::process::Command::new("cmd")
|
||||
.arg("/C")
|
||||
.arg(delete_route)
|
||||
.output()
|
||||
.unwrap();
|
||||
if !out.status.success() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("删除路由失败: {:?}", out)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
|
||||
out.pcap
|
||||
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
/.idea
|
||||
@@ -1,60 +0,0 @@
|
||||
# ChangeLog
|
||||
|
||||
This format is based on [Keep a Changelog](https://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](https://semver.org).
|
||||
|
||||
## [0.2.1] - 2021-12-03
|
||||
|
||||
### Fixed
|
||||
Type in readme
|
||||
|
||||
## [0.2.0] - 2021-12-03
|
||||
|
||||
Added support for wintun 0.14.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- Wintun driver versions before `0.14` are no longer support due to beraking
|
||||
changes in the C API
|
||||
- `Adapter::create` returns a `Result<Adapter, ...>` instead of a `Result<CreateData, ...>`.
|
||||
This was done because the underlying Wintun function was changed to only return an adapter handle
|
||||
- `Adapter::create` the pool parameter was removed because it was also removed from the C function
|
||||
- `Adapter::delete` takes no parameters and returns a `Result<(), ()>`.
|
||||
The `force_close_sessions` parameter was removed because it was removed from the
|
||||
C function. Same for the bool inside the Ok(..) variant
|
||||
- `Adapter::create` and `Adapter::open` return `Arc<Adapter>` instead of `Adapter`
|
||||
- `get_running_driver_version` now returns a proper Result<Version, ()>.
|
||||
|
||||
### Added
|
||||
|
||||
- `reset_logger` function to disable logging after a logger has been set.
|
||||
|
||||
## [0.1.5] - 2021-08-27
|
||||
|
||||
### Fixed
|
||||
|
||||
- Readme on crates.io
|
||||
|
||||
## [0.1.4] - 2021-08-27
|
||||
|
||||
### Added
|
||||
- `panic_on_unsent_packets` feature flag to help in debugging ring buffer blockage issues
|
||||
|
||||
## [0.1.3] - 2021-06-28
|
||||
|
||||
### Fixed
|
||||
|
||||
- Cargo.toml metadata to include `package.metadata.docs.rs.default-target`.
|
||||
Fixes build issue on docs.rs (we can only build docs on windows, 0.1.1 doesn't work)
|
||||
|
||||
## [0.1.2] - 2021-06-28
|
||||
docs.rs testing
|
||||
|
||||
## [0.1.1] - 2021-06-28
|
||||
|
||||
- Cargo.toml metadata to build on linux
|
||||
|
||||
## [0.1.0] - 2021-06-28
|
||||
|
||||
First release with initial api
|
||||
|
||||
Generated
-427
@@ -1,427 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "derive-into-owned"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "576fce04d31d592013a5887ba8d9c3830adff329e5096d7e1eb5e8e61262ca62"
|
||||
dependencies = [
|
||||
"quote 0.3.15",
|
||||
"syn 0.11.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "hwaddr"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e414433a9e4338f4e87fa29d0670c883a5e73e7955c45f4a49130c0aa992c85b"
|
||||
dependencies = [
|
||||
"phf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afe203d669ec979b7128619bae5a63b7b42e9203c1b29146079ee05e2f604b52"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
|
||||
|
||||
[[package]]
|
||||
name = "packet"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c136c7ad0619ed4f88894aecf66ad86c80683e7b5d707996e6a3a7e0e3916944"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"hwaddr",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pcap-file"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ad13fed1a83120159aea81b265074f21d753d157dd16b10cc3790ecba40a341"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"derive-into-owned",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
|
||||
dependencies = [
|
||||
"unicode-xid 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_hc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b"
|
||||
|
||||
[[package]]
|
||||
name = "subprocess"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "055cf3ebc2981ad8f0a5a17ef6652f652d87831f79fddcba2ac57bcb9a0aa407"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.11.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
|
||||
dependencies = [
|
||||
"quote 0.3.15",
|
||||
"synom",
|
||||
"unicode-xid 0.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.10",
|
||||
"unicode-xid 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synom"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
|
||||
dependencies = [
|
||||
"unicode-xid 0.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.10",
|
||||
"syn 1.0.82",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.2+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "wintun"
|
||||
version = "0.2.1"
|
||||
dependencies = [
|
||||
"env_logger",
|
||||
"itertools",
|
||||
"libloading",
|
||||
"log",
|
||||
"once_cell",
|
||||
"packet",
|
||||
"pcap-file",
|
||||
"rand",
|
||||
"subprocess",
|
||||
"widestring",
|
||||
"winapi",
|
||||
]
|
||||
@@ -1,35 +0,0 @@
|
||||
[package]
|
||||
name = "wintun"
|
||||
version = "0.2.1"
|
||||
edition = "2021"
|
||||
authors = ["null.black Inc. <[email protected]>", "Troy Neubauer <[email protected]>"]
|
||||
repository = "https://github.com/nulldotblack/wintun"
|
||||
readme = "README.md"
|
||||
documentation = "https://docs.rs/wintun/"
|
||||
description = "Safe idiomatic bindings to the WinTun C library"
|
||||
license = "MIT"
|
||||
keywords = ["wintun", "tap", "tun", "vpn", "wireguard"]
|
||||
categories = ["api-bindings"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
default-target = "x86_64-pc-windows-msvc"
|
||||
targets = ["aarch64-pc-windows-msvc", "i686-pc-windows-msvc", "x86_64-pc-windows-msvc"]
|
||||
|
||||
[features]
|
||||
panic_on_unsent_packets = []
|
||||
|
||||
[dependencies]
|
||||
winapi = { version = "0.3", features = ["synchapi", "winbase", "winerror", "ipexport", "iphlpapi", "handleapi"] }
|
||||
widestring = "0.4"
|
||||
libloading = "0.7"
|
||||
once_cell = "1.8"
|
||||
log = "0.4"
|
||||
rand = "0.8.3"
|
||||
itertools = "0.10.1"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.8"
|
||||
winapi = { version = "0.3", features = ["netioapi", "iptypes", "iphlpapi", "nldef"] }
|
||||
packet = "0.1.4"
|
||||
pcap-file = "1.1.1"
|
||||
subprocess = "0.2.7"
|
||||
@@ -1,7 +0,0 @@
|
||||
Copyright 2021 null.black Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,80 +0,0 @@
|
||||
# wintun
|
||||
|
||||
Safe rust idiomatic bindings for the Wintun C library: <https://wintun.net>
|
||||
|
||||
All features of the Wintun library are wrapped using pure rust types and functions to make
|
||||
usage feel ergonomic.
|
||||
|
||||
## Usage
|
||||
|
||||
Inside your code load the wintun.dll signed driver file, downloaded from <https://wintun.net>,
|
||||
using [`load`], [`load_from_path`] or [`load_from_library`].
|
||||
|
||||
Then either call [`Adapter::create`] or [`Adapter::open`] to obtain a wintun
|
||||
adapter. Start a session with [`Adapter::start_session`].
|
||||
|
||||
## Example
|
||||
```rust
|
||||
use std::sync::Arc;
|
||||
|
||||
//Must be run as Administrator because we create network adapters
|
||||
//Load the wintun dll file so that we can call the underlying C functions
|
||||
//Unsafe because we are loading an arbitrary dll file
|
||||
let wintun = unsafe { wintun::load_from_path("path/to/wintun.dll") }
|
||||
.expect("Failed to load wintun dll");
|
||||
|
||||
//Try to open an adapter with the name "Demo"
|
||||
let adapter = match wintun::Adapter::open(&wintun, "Demo") {
|
||||
Ok(a) => a,
|
||||
Err(_) => {
|
||||
//If loading failed (most likely it didn't exist), create a new one
|
||||
wintun::Adapter::create(&wintun, "Example", "Demo", None)
|
||||
.expect("Failed to create wintun adapter!")
|
||||
}
|
||||
};
|
||||
//Specify the size of the ring buffer the wintun driver should use.
|
||||
let session = Arc::new(adapter.start_session(wintun::MAX_RING_CAPACITY).unwrap());
|
||||
|
||||
//Get a 20 byte packet from the ring buffer
|
||||
let mut packet = session.allocate_send_packet(20).unwrap();
|
||||
let bytes: &mut [u8] = packet.bytes_mut();
|
||||
//Write IPV4 version and header length
|
||||
bytes[0] = 0x40;
|
||||
|
||||
//Finish writing IP header
|
||||
bytes[9] = 0x69;
|
||||
bytes[10] = 0x04;
|
||||
bytes[11] = 0x20;
|
||||
//...
|
||||
|
||||
//Send the packet to wintun virtual adapter for processing by the system
|
||||
session.send_packet(packet);
|
||||
|
||||
//Stop any readers blocking for data on other threads
|
||||
//Only needed when a blocking reader is preventing shutdown Ie. it holds an Arc to the
|
||||
//session, blocking it from being dropped
|
||||
session.shutdown();
|
||||
|
||||
//the session is stopped on drop
|
||||
//drop(session);
|
||||
|
||||
//drop(adapter)
|
||||
//And the adapter closes its resources when dropped
|
||||
```
|
||||
|
||||
See `examples/wireshark.rs` for a more complete example that writes received packets to a pcap
|
||||
file.
|
||||
|
||||
## Features
|
||||
|
||||
- `panic_on_unsent_packets`: Panics if a send packet is dropped without being sent. Useful for
|
||||
debugging packet issues because unsent packets that are dropped without being sent hold up
|
||||
wintun's internal ring buffer.
|
||||
|
||||
## TODO:
|
||||
- Add async support
|
||||
Requires hooking into a windows specific reactor and registering read interest on wintun's read
|
||||
handle. Asyncify other slow operations via tokio::spawn_blocking. As always, PR's are welcome!
|
||||
|
||||
|
||||
License: MIT
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
bindgen \
|
||||
--allowlist-function "Wintun.*" \
|
||||
--allowlist-type "WINTUN_.*" \
|
||||
--dynamic-loading wintun \
|
||||
--dynamic-link-require-all \
|
||||
wintun/wintun_functions.h > src/wintun_raw.rs
|
||||
@@ -1,345 +0,0 @@
|
||||
/// Representation of a winton adapter with safe idiomatic bindings to the functionality provided by
|
||||
/// the WintunAdapter* C functions.
|
||||
///
|
||||
/// The [`Adapter::create`] and [`Adapter::open`] functions serve as the entry point to using
|
||||
/// wintun functionality
|
||||
use crate::error;
|
||||
use crate::session;
|
||||
use crate::util;
|
||||
use crate::util::UnsafeHandle;
|
||||
use crate::wintun_raw;
|
||||
use crate::Wintun;
|
||||
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use itertools::Itertools;
|
||||
use log::*;
|
||||
use once_cell::sync::OnceCell;
|
||||
use rand::Rng;
|
||||
|
||||
use widestring::U16CStr;
|
||||
use widestring::U16CString;
|
||||
|
||||
use winapi::{
|
||||
shared::winerror,
|
||||
um::{ipexport, iphlpapi, synchapi},
|
||||
};
|
||||
|
||||
/// Wrapper around a <https://git.zx2c4.com/wintun/about/#wintun_adapter_handle>
|
||||
pub struct Adapter {
|
||||
adapter: UnsafeHandle<wintun_raw::WINTUN_ADAPTER_HANDLE>,
|
||||
wintun: Wintun,
|
||||
guid: u128,
|
||||
}
|
||||
|
||||
fn encode_utf16(string: &str, max_characters: usize) -> Result<U16CString, error::WintunError> {
|
||||
let utf16 = U16CString::from_str(string)?;
|
||||
if utf16.len() >= max_characters {
|
||||
//max_characters is the maximum number of characters including the null terminator. And .len() measures the
|
||||
//number of characters (excluding the null terminator). Therefore we can hold a string with
|
||||
//max_characters - 1 because the null terminator sits in the last element. However a string
|
||||
//of length max_characters needs max_characters + 1 to store the null terminator the >=
|
||||
//check holds
|
||||
Err(format!(
|
||||
//TODO: Better error handling
|
||||
"Length too large. Size: {}, Max: {}",
|
||||
utf16.len(),
|
||||
max_characters
|
||||
)
|
||||
.into())
|
||||
} else {
|
||||
Ok(utf16)
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_pool_name(name: &str) -> Result<U16CString, error::WintunError> {
|
||||
encode_utf16(name, crate::MAX_POOL)
|
||||
}
|
||||
|
||||
fn encode_adapter_name(name: &str) -> Result<U16CString, error::WintunError> {
|
||||
encode_utf16(name, crate::MAX_POOL)
|
||||
}
|
||||
|
||||
fn get_adapter_luid(wintun: &Wintun, adapter: wintun_raw::WINTUN_ADAPTER_HANDLE) -> u64 {
|
||||
let mut luid: wintun_raw::NET_LUID = unsafe { std::mem::zeroed() };
|
||||
unsafe { wintun.WintunGetAdapterLUID(adapter, &mut luid as *mut wintun_raw::NET_LUID) };
|
||||
unsafe { std::mem::transmute(luid) }
|
||||
}
|
||||
|
||||
impl Adapter {
|
||||
//TODO: Call get last error for error information on failure and improve error types
|
||||
|
||||
/// Creates a new wintun adapter inside the pool `pool` with name `name`
|
||||
///
|
||||
/// Optionally a GUID can be specified that will become the GUID of this adapter once created.
|
||||
/// Adapters obtained via this function will be able to return their adapter index via
|
||||
/// [`Adapter::get_adapter_index`]
|
||||
pub fn create(
|
||||
wintun: &Wintun,
|
||||
pool: &str,
|
||||
name: &str,
|
||||
guid: Option<u128>,
|
||||
) -> Result<Arc<Adapter>, error::WintunError> {
|
||||
let pool_utf16 = encode_pool_name(pool)?;
|
||||
let name_utf16 = encode_adapter_name(name)?;
|
||||
|
||||
let guid = match guid {
|
||||
Some(guid) => guid,
|
||||
None => {
|
||||
// Use random bytes so that we can identify this adapter in get_adapter_index
|
||||
let mut guid_bytes: [u8; 16] = [0u8; 16];
|
||||
rand::thread_rng().fill(&mut guid_bytes);
|
||||
u128::from_ne_bytes(guid_bytes)
|
||||
}
|
||||
};
|
||||
//SAFETY: guid is a unique integer so transmuting either all zeroes or the user's preferred
|
||||
//guid to the winapi guid type is safe and will allow the windows kernel to see our GUID
|
||||
let guid_struct: wintun_raw::GUID = unsafe { std::mem::transmute(guid) };
|
||||
//TODO: The guid of the adapter once created might differ from the one provided because of
|
||||
//the byte order of the segments of the GUID struct that are larger than a byte. Verify
|
||||
//that this works as expected
|
||||
|
||||
let guid_ptr = &guid_struct as *const wintun_raw::GUID;
|
||||
|
||||
crate::log::set_default_logger_if_unset(wintun);
|
||||
|
||||
//SAFETY: the function is loaded from the wintun dll properly, we are providing valid
|
||||
//pointers, and all the strings are correct null terminated UTF-16. This safety rationale
|
||||
//applies for all Wintun* functions below
|
||||
let result = unsafe {
|
||||
wintun.WintunCreateAdapter(pool_utf16.as_ptr(), name_utf16.as_ptr(), guid_ptr)
|
||||
};
|
||||
|
||||
if result.is_null() {
|
||||
Err("Failed to crate adapter".into())
|
||||
} else {
|
||||
Ok(Arc::new(Adapter {
|
||||
adapter: UnsafeHandle(result),
|
||||
wintun: wintun.clone(),
|
||||
guid,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to open an existing wintun interface name `name`.
|
||||
///
|
||||
/// Adapters opened via this call will have an unknown GUID meaning [`Adapter::get_adapter_index`]
|
||||
/// will always fail because knowing the adapter's GUID is required to determine its index.
|
||||
/// Currently a workaround is to delete and re-create a new adapter every time one is needed so
|
||||
/// that it gets created with a known GUID, allowing [`Adapter::get_adapter_index`] to works as
|
||||
/// expected. There is likely a way to get the GUID of our adapter using the Windows Registry
|
||||
/// or via the Win32 API, so PR's that solve this issue are always welcome!
|
||||
pub fn open(wintun: &Wintun, name: &str) -> Result<Arc<Adapter>, error::WintunError> {
|
||||
let name_utf16 = encode_adapter_name(name)?;
|
||||
|
||||
crate::log::set_default_logger_if_unset(wintun);
|
||||
|
||||
let result = unsafe { wintun.WintunOpenAdapter(name_utf16.as_ptr()) };
|
||||
|
||||
if result.is_null() {
|
||||
Err("WintunOpenAdapter failed".into())
|
||||
} else {
|
||||
Ok(Arc::new(Adapter {
|
||||
adapter: UnsafeHandle(result),
|
||||
wintun: wintun.clone(),
|
||||
// TODO: get GUID somehow
|
||||
guid: 0,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete an adapter, consuming it in the process
|
||||
pub fn delete(self) -> Result<(), ()> {
|
||||
//Dropping an adapter closes it
|
||||
drop(self);
|
||||
// Return a result here so that if later the API changes to be fallible, we can support it
|
||||
// without making a breaking change
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Initiates a new wintun session on the given adapter.
|
||||
///
|
||||
/// Capacity is the size in bytes of the ring buffer used internally by the driver. Must be
|
||||
/// a power of two between [`crate::MIN_RING_CAPACITY`] and [`crate::MIN_RING_CAPACITY`].
|
||||
pub fn start_session(
|
||||
self: &Arc<Self>,
|
||||
capacity: u32,
|
||||
) -> Result<session::Session, error::WintunError> {
|
||||
let range = crate::MIN_RING_CAPACITY..=crate::MAX_RING_CAPACITY;
|
||||
if !range.contains(&capacity) {
|
||||
return Err(Box::new(error::ApiError::CapacityOutOfRange(
|
||||
error::OutOfRangeData {
|
||||
range,
|
||||
value: capacity,
|
||||
},
|
||||
)));
|
||||
}
|
||||
if !capacity.is_power_of_two() {
|
||||
return Err(Box::new(error::ApiError::CapacityNotPowerOfTwo(capacity)));
|
||||
}
|
||||
|
||||
let result = unsafe { self.wintun.WintunStartSession(self.adapter.0, capacity) };
|
||||
|
||||
if result.is_null() {
|
||||
Err("WintunStartSession failed".into())
|
||||
} else {
|
||||
Ok(session::Session {
|
||||
session: UnsafeHandle(result),
|
||||
wintun: self.wintun.clone(),
|
||||
read_event: OnceCell::new(),
|
||||
shutdown_event: unsafe {
|
||||
//SAFETY: We follow the contract required by CreateEventA. See MSDN
|
||||
//(the pointers are allowed to be null, and 0 is okay for the others)
|
||||
UnsafeHandle(synchapi::CreateEventA(
|
||||
std::ptr::null_mut(),
|
||||
0,
|
||||
0,
|
||||
std::ptr::null_mut(),
|
||||
))
|
||||
},
|
||||
adapter: Arc::clone(self),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the Win32 LUID for this adapter
|
||||
pub fn get_luid(&self) -> u64 {
|
||||
get_adapter_luid(&self.wintun, self.adapter.0)
|
||||
}
|
||||
|
||||
/// Returns the Win32 interface index of this adapter. Useful for specifying the interface
|
||||
/// when executing `netsh interface ip` commands
|
||||
pub fn get_adapter_index(&self) -> Result<u32, error::WintunError> {
|
||||
let mut buf_len: u32 = 0;
|
||||
//First figure out the size of the buffer needed to store the adapter info
|
||||
//SAFETY: We are upholding the contract of GetInterfaceInfo. buf_len is a valid pointer to
|
||||
//stack memory
|
||||
let result =
|
||||
unsafe { iphlpapi::GetInterfaceInfo(std::ptr::null_mut(), &mut buf_len as *mut u32) };
|
||||
if result != winerror::NO_ERROR && result != winerror::ERROR_INSUFFICIENT_BUFFER {
|
||||
let err_msg = util::get_error_message(result);
|
||||
error!("Failed to get interface info: {}", err_msg);
|
||||
//TODO: Better error types
|
||||
return Err(format!("GetInterfaceInfo failed: {}", err_msg).into());
|
||||
}
|
||||
|
||||
//Allocate a buffer of the requested size
|
||||
//IP_INTERFACE_INFO must be aligned by at least 4 byte boundaries so use u32 as the
|
||||
//underlying data storage type
|
||||
let buf_elements = buf_len as usize / std::mem::size_of::<u32>() + 1;
|
||||
//Round up incase integer division truncated a byte that filled a partial element
|
||||
let mut buf: Vec<u32> = vec![0; buf_elements];
|
||||
|
||||
let buf_bytes = buf.len() * std::mem::size_of::<u32>();
|
||||
assert!(buf_bytes >= buf_len as usize);
|
||||
|
||||
//SAFETY:
|
||||
//
|
||||
// 1. We are upholding the contract of GetInterfaceInfo.
|
||||
// 2. `final_buf_len` is an aligned, valid pointer to stack memory
|
||||
// 3. buf is a valid, non-null pointer to at least `buf_len` bytes of heap memory,
|
||||
// aligned to at least 4 byte boundaries
|
||||
//
|
||||
//Get the info
|
||||
let mut final_buf_len: u32 = buf_len;
|
||||
let result = unsafe {
|
||||
iphlpapi::GetInterfaceInfo(
|
||||
buf.as_mut_ptr() as *mut ipexport::IP_INTERFACE_INFO,
|
||||
&mut final_buf_len as *mut u32,
|
||||
)
|
||||
};
|
||||
if result != winerror::NO_ERROR {
|
||||
let err_msg = util::get_error_message(result);
|
||||
//TODO: maybe over allocate the buffer in case the needed size changes between the two
|
||||
//calls to GetInterfaceInfo if another adapter is added
|
||||
error!(
|
||||
"Failed to get interface info a second time: {}. Original len: {}, final len: {}",
|
||||
err_msg, buf_len, final_buf_len
|
||||
);
|
||||
return Err(format!("GetInterfaceInfo failed a second time: {}", err_msg).into());
|
||||
}
|
||||
let info = buf.as_mut_ptr() as *const ipexport::IP_INTERFACE_INFO;
|
||||
//SAFETY:
|
||||
// info is a valid, non-null, at least 4 byte aligned pointer obtained from
|
||||
// Vec::with_capacity that is readable for up to `buf_len` bytes which is guaranteed to be
|
||||
// larger than on IP_INTERFACE_INFO struct as the kernel would never ask for less memory then
|
||||
// what it will write. The largest type inside IP_INTERFACE_INFO is a u32 therefore
|
||||
// a painter to IP_INTERFACE_INFO requires an alignment of at leant 4 bytes, which
|
||||
// Vec<u32>::as_mut_ptr() provides
|
||||
let adapter_base = unsafe { &*info };
|
||||
let adapter_count = adapter_base.NumAdapters;
|
||||
let first_adapter = &adapter_base.Adapter as *const ipexport::IP_ADAPTER_INDEX_MAP;
|
||||
|
||||
// SAFETY:
|
||||
// 1. first_adapter is a valid, non null pointer, aligned to at least 4 byte boundaries
|
||||
// obtained from moving a multiple of 4 offset into the buf given by Vec::with_capacity.
|
||||
// 2. We gave GetInterfaceInfo a buffer of at least least `buf_len` bytes to work with and it
|
||||
// succeeded in writing the adapter information within the bounds of that buffer, otherwise
|
||||
// it would've failed. Because the operation succeeded, we know that reading n=NumAdapters
|
||||
// IP_ADAPTER_INDEX_MAP structs stays within the bounds of buf's buffer
|
||||
let interfaces =
|
||||
unsafe { std::slice::from_raw_parts(first_adapter, adapter_count as usize) };
|
||||
let mut tmp = Vec::new();
|
||||
for interface in interfaces {
|
||||
let name =
|
||||
unsafe { U16CStr::from_ptr_str(&interface.Name as *const u16).to_string_lossy() };
|
||||
//Nam is something like: \DEVICE\TCPIP_{29C47F55-C7BD-433A-8BF7-408DFD3B3390}
|
||||
//where the GUID is the {29C4...90}, separated by dashes
|
||||
let open = name.chars().position(|c| c == '{').ok_or(format!(
|
||||
"Failed to find {{ character inside adapter name: {}",
|
||||
name
|
||||
))?;
|
||||
let close = name.chars().position(|c| c == '}').ok_or(format!(
|
||||
"Failed to find }} character inside adapter name: {}",
|
||||
name
|
||||
))?;
|
||||
let digits: Vec<u8> = name[open..close]
|
||||
.chars()
|
||||
.filter(|c| c.is_digit(16))
|
||||
.chunks(2)
|
||||
.into_iter()
|
||||
.filter_map(|mut chunk| {
|
||||
//Filter out chunks that have < 2 digits
|
||||
if let Some(a) = chunk.next() {
|
||||
if let Some(b) = chunk.next() {
|
||||
return Some((a, b));
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
.map(|digits| {
|
||||
let chars: [u8; 2] = [digits.0 as u8, digits.1 as u8];
|
||||
let s = std::str::from_utf8(&chars).unwrap();
|
||||
u8::from_str_radix(s, 16).unwrap()
|
||||
})
|
||||
.collect();
|
||||
|
||||
//Our index is the adapter which has a guid in its name that matches ours
|
||||
//For now we just check for a guid with the same hex bytes in any order
|
||||
//TODO: byte swap GUID from name so that we can compare self.guid with the parsed GUID
|
||||
//directly
|
||||
let mut match_count = 0;
|
||||
for byte in self.guid.to_ne_bytes() {
|
||||
if digits.contains(&byte) {
|
||||
match_count += 1;
|
||||
}
|
||||
}
|
||||
tmp.push(format!("interfaces name={:?},digits={:?},index={:?}", name,digits, interface.Index));
|
||||
if match_count == digits.len() {
|
||||
return Ok(interface.Index);
|
||||
}
|
||||
}
|
||||
log::info!("interfaces:{:?},guid={}",tmp,self.guid);
|
||||
Err("Unable to find matching GUID".into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Adapter {
|
||||
fn drop(&mut self) {
|
||||
//Close adapter on drop
|
||||
//This is why we need an Arc of wintun
|
||||
unsafe { self.wintun.WintunCloseAdapter(self.adapter.0) };
|
||||
self.adapter = UnsafeHandle(ptr::null_mut());
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
pub type WintunError = Box<dyn std::error::Error>;
|
||||
|
||||
/// Error type used to convey that a value is outside of a range that it must fall inside
|
||||
#[derive(Debug)]
|
||||
pub struct OutOfRangeData<T> {
|
||||
pub range: std::ops::RangeInclusive<T>,
|
||||
pub value: T,
|
||||
}
|
||||
|
||||
/// Error type returned when preconditions of this API are broken
|
||||
#[derive(Debug)]
|
||||
pub enum ApiError {
|
||||
CapacityNotPowerOfTwo(u32),
|
||||
CapacityOutOfRange(OutOfRangeData<u32>),
|
||||
}
|
||||
|
||||
impl Display for ApiError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match &self {
|
||||
ApiError::CapacityOutOfRange(data) => write!(
|
||||
f,
|
||||
"Capacity {} out of range. Must be within {}..={}",
|
||||
data.value,
|
||||
data.range.start(),
|
||||
data.range.end()
|
||||
),
|
||||
ApiError::CapacityNotPowerOfTwo(cap) => {
|
||||
write!(f, "Capacity {} is not a power of two", cap)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ApiError {}
|
||||
@@ -1,174 +0,0 @@
|
||||
//! Safe rust idiomatic bindings for the Wintun C library: <https://wintun.net>
|
||||
//!
|
||||
//! All features of the Wintun library are wrapped using pure rust types and functions to make
|
||||
//! usage feel ergonomic.
|
||||
//!
|
||||
//! # Usage
|
||||
//!
|
||||
//! Inside your code load the wintun.dll signed driver file, downloaded from <https://wintun.net>,
|
||||
//! using [`load`], [`load_from_path`] or [`load_from_library`].
|
||||
//!
|
||||
//! Then either call [`Adapter::create`] or [`Adapter::open`] to obtain a wintun
|
||||
//! adapter. Start a session with [`Adapter::start_session`].
|
||||
//!
|
||||
//! # Example
|
||||
//! ```no_run
|
||||
//! use std::sync::Arc;
|
||||
//!
|
||||
//! //Must be run as Administrator because we create network adapters
|
||||
//! //Load the wintun dll file so that we can call the underlying C functions
|
||||
//! //Unsafe because we are loading an arbitrary dll file
|
||||
//! let wintun = unsafe { wintun::load_from_path("path/to/wintun.dll") }
|
||||
//! .expect("Failed to load wintun dll");
|
||||
//!
|
||||
//! //Try to open an adapter with the name "Demo"
|
||||
//! let adapter = match wintun::Adapter::open(&wintun, "Demo") {
|
||||
//! Ok(a) => a,
|
||||
//! Err(_) => {
|
||||
//! //If loading failed (most likely it didn't exist), create a new one
|
||||
//! wintun::Adapter::create(&wintun, "Example", "Demo", None)
|
||||
//! .expect("Failed to create wintun adapter!")
|
||||
//! }
|
||||
//! };
|
||||
//! //Specify the size of the ring buffer the wintun driver should use.
|
||||
//! let session = Arc::new(adapter.start_session(wintun::MAX_RING_CAPACITY).unwrap());
|
||||
//!
|
||||
//! //Get a 20 byte packet from the ring buffer
|
||||
//! let mut packet = session.allocate_send_packet(20).unwrap();
|
||||
//! let bytes: &mut [u8] = packet.bytes_mut();
|
||||
//! //Write IPV4 version and header length
|
||||
//! bytes[0] = 0x40;
|
||||
//!
|
||||
//! //Finish writing IP header
|
||||
//! bytes[9] = 0x69;
|
||||
//! bytes[10] = 0x04;
|
||||
//! bytes[11] = 0x20;
|
||||
//! //...
|
||||
//!
|
||||
//! //Send the packet to wintun virtual adapter for processing by the system
|
||||
//! session.send_packet(packet);
|
||||
//!
|
||||
//! //Stop any readers blocking for data on other threads
|
||||
//! //Only needed when a blocking reader is preventing shutdown Ie. it holds an Arc to the
|
||||
//! //session, blocking it from being dropped
|
||||
//! session.shutdown();
|
||||
//!
|
||||
//! //the session is stopped on drop
|
||||
//! //drop(session);
|
||||
//!
|
||||
//! //drop(adapter)
|
||||
//! //And the adapter closes its resources when dropped
|
||||
//! ```
|
||||
//!
|
||||
//! See `examples/wireshark.rs` for a more complete example that writes received packets to a pcap
|
||||
//! file.
|
||||
//!
|
||||
//! # Features
|
||||
//!
|
||||
//! - `panic_on_unsent_packets`: Panics if a send packet is dropped without being sent. Useful for
|
||||
//! debugging packet issues because unsent packets that are dropped without being sent hold up
|
||||
//! wintun's internal ring buffer.
|
||||
//!
|
||||
//! # TODO:
|
||||
//! - Add async support
|
||||
//! Requires hooking into a windows specific reactor and registering read interest on wintun's read
|
||||
//! handle. Asyncify other slow operations via tokio::spawn_blocking. As always, PR's are welcome!
|
||||
//!
|
||||
|
||||
mod adapter;
|
||||
mod error;
|
||||
mod log;
|
||||
mod packet;
|
||||
mod session;
|
||||
mod util;
|
||||
|
||||
//Generated by bingen
|
||||
#[allow(
|
||||
non_snake_case,
|
||||
dead_code,
|
||||
unused_variables,
|
||||
non_camel_case_types,
|
||||
deref_nullptr,
|
||||
clippy::all
|
||||
)]
|
||||
mod wintun_raw;
|
||||
|
||||
pub use crate::adapter::Adapter;
|
||||
pub use crate::error::{ApiError, OutOfRangeData, WintunError};
|
||||
pub use crate::log::{default_logger, reset_logger, set_logger};
|
||||
pub use crate::packet::Packet;
|
||||
pub use crate::session::Session;
|
||||
pub use crate::util::get_running_driver_version;
|
||||
|
||||
// TODO: Get bindgen to scrape these from the `wintun.h`
|
||||
// We need to make sure these stay up to date
|
||||
/// The maximum size of wintun's internal ring buffer (in bytes)
|
||||
pub const MAX_RING_CAPACITY: u32 = 0x400_0000;
|
||||
|
||||
/// The minimum size of wintun's internal ring buffer (in bytes)
|
||||
pub const MIN_RING_CAPACITY: u32 = 0x2_0000;
|
||||
|
||||
/// Maximum pool name length including zero terminator
|
||||
pub const MAX_POOL: usize = 256;
|
||||
|
||||
pub type Wintun = Arc<wintun_raw::wintun>;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Attempts to load the Wintun library from the current directory using the default name "wintun.dll".
|
||||
///
|
||||
/// Use [`load_from_path`] with an absolute path when more control is needed as to where wintun.dll is
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
/// This function loads a dll file with the name wintun.dll using the default system search paths.
|
||||
/// This is inherently unsafe as a user could simply rename undefined_behavior.dll to wintun.dll
|
||||
/// and do nefarious things inside of its DllMain function. In most cases, a regular wintun.dll
|
||||
/// file which exports all of the required functions for these bindings to work is loaded. Because
|
||||
/// WinTun is a well-written and well-tested library, loading a _normal_ wintun.dll file should be safe.
|
||||
/// Hoverer one can never be too cautious when loading a dll file.
|
||||
///
|
||||
/// For more information see [`libloading`]'s dynamic library safety guarantees: [`libloading`][`libloading::Library::new`]
|
||||
pub unsafe fn load() -> Result<Wintun, libloading::Error> {
|
||||
load_from_path("wintun")
|
||||
}
|
||||
|
||||
/// Attempts to load the Wintun library as a dynamic library from the given path.
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
/// This function loads a dll file with the path provided.
|
||||
/// This is inherently unsafe as a user could simply rename undefined_behavior.dll to wintun.dll
|
||||
/// and do nefarious things inside of its DllMain function. In most cases, a regular wintun.dll
|
||||
/// file which exports all of the required functions for these bindings to work is loaded. Because
|
||||
/// WinTun is a well-written and well-tested library, loading a _normal_ wintun.dll file should be safe.
|
||||
/// Hoverer one can never be too cautious when loading a dll file.
|
||||
///
|
||||
/// For more information see [`libloading`]'s dynamic library safety guarantees: [`libloading`][`libloading::Library::new`]
|
||||
pub unsafe fn load_from_path<P>(path: P) -> Result<Wintun, libloading::Error>
|
||||
where
|
||||
P: AsRef<::std::ffi::OsStr>,
|
||||
{
|
||||
check_version(wintun_raw::wintun::new(path)?)
|
||||
}
|
||||
|
||||
/// Attempts to load the Wintun library from an existing [`libloading::Library`].
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
/// This function loads the required WinTun functions using the provided library. Reading a symbol table
|
||||
/// of a dynamic library and transmuting the function pointers inside to have the parameters and return
|
||||
/// values expected by the functions documented at: <https://git.zx2c4.com/wintun/about/#reference>
|
||||
/// is inherently unsafe.
|
||||
///
|
||||
/// For more information see [`libloading`]'s dynamic library safety guarantees: [`libloading::Library::new`]
|
||||
pub unsafe fn load_from_library<L>(library: L) -> Result<Wintun, libloading::Error>
|
||||
where
|
||||
L: Into<libloading::Library>,
|
||||
{
|
||||
check_version(wintun_raw::wintun::from_library(library)?)
|
||||
}
|
||||
|
||||
fn check_version(lib: wintun_raw::wintun) -> Result<Wintun, libloading::Error> {
|
||||
Ok(Arc::new(lib))
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
use crate::session;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
pub(crate) enum Kind {
|
||||
SendPacketPending, //Send packet type, but not sent yet
|
||||
SendPacketSent, //Send packet type - sent
|
||||
ReceivePacket,
|
||||
}
|
||||
|
||||
/// Represents a wintun packet
|
||||
pub struct Packet {
|
||||
pub(crate) kind: Kind,
|
||||
|
||||
//This lifetime is not actually 'static, however before you get your pitchforks let me explain...
|
||||
//The bytes in this slice live for as long at the session that allocated them, or until
|
||||
//WintunReleaseReceivePacket, or WintunSendPacket is called on them (whichever happens first).
|
||||
//The wrapper functions that call into WintunReleaseReceivePacket, and WintunSendPacket
|
||||
//consume the packet, meaning the end of this packet's lifetime coincides with the end of byte's
|
||||
//lifetime. Because we never copy out of bytes, this pointer becomes inaccessible when the
|
||||
//packet is dropped.
|
||||
//
|
||||
//This just leaves packets potentially outliving the session that allocated them posing a
|
||||
//problem.
|
||||
//Fortunately we have an Arc to the session that allocated this packet, meaning that the lifetime
|
||||
//of the session that created this packet is at least as long as the packet.
|
||||
//Because this is private (to external users) and we only write to this field when allocating
|
||||
//new packets, it is impossible for the memory that is pointed to by bytes to outlive the
|
||||
//underlying memory allocated by wintun.
|
||||
//
|
||||
//So what I told you was true, from a certain point of view.
|
||||
//From the point of view of this packet, bytes' lifetime is 'static because we are always
|
||||
//dropped before the underlying memory is freed
|
||||
//
|
||||
//Its also important to know that WintunAllocateSendPacket and WintunReceivePacket always
|
||||
//return sections of memory that never overlap, so we have exclusive access to the memory,
|
||||
//therefore mut is okay here.
|
||||
pub(crate) bytes: &'static mut [u8],
|
||||
|
||||
//Share ownership of session to prevent the session from being dropped before packets that
|
||||
//belong to it
|
||||
pub(crate) session: Arc<session::Session>,
|
||||
}
|
||||
|
||||
impl Packet {
|
||||
/// Returns the bytes this packet holds as &mut.
|
||||
/// The lifetime of the bytes is tied to the lifetime of this packet.
|
||||
pub fn bytes_mut(&mut self) -> &mut [u8] {
|
||||
self.bytes
|
||||
}
|
||||
|
||||
/// Returns an immutable reference to the bytes this packet holds.
|
||||
/// The lifetime of the bytes is tied to the lifetime of this packet.
|
||||
pub fn bytes(&self) -> &[u8] {
|
||||
self.bytes
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Packet {
|
||||
fn drop(&mut self) {
|
||||
match self.kind {
|
||||
Kind::ReceivePacket => {
|
||||
unsafe {
|
||||
//SAFETY:
|
||||
//
|
||||
// 1. We share ownership of the session therefore it hasn't been dropped yet
|
||||
// 2. Bytes is valid because each packet holds exclusive access to a region of the
|
||||
// ring buffer that the wintun session owns. We return that region of
|
||||
// memory back to wintun here
|
||||
self.session
|
||||
.wintun
|
||||
.WintunReleaseReceivePacket(self.session.session.0, self.bytes.as_ptr())
|
||||
};
|
||||
}
|
||||
Kind::SendPacketPending => {
|
||||
//If someone allocates a packet with session.allocate_send_packet() and then it is
|
||||
//dropped without being sent, this will hold up the send queue because wintun expects
|
||||
//that every allocated packet is sent
|
||||
|
||||
#[cfg(feature = "panic_on_unsent_packets")]
|
||||
panic!("Packet was never sent!");
|
||||
}
|
||||
Kind::SendPacketSent => {
|
||||
//Nop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
extern crate winapi;
|
||||
|
||||
use crate::packet;
|
||||
use crate::util::UnsafeHandle;
|
||||
use crate::wintun_raw;
|
||||
use crate::Adapter;
|
||||
use crate::Wintun;
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
|
||||
use winapi::shared::winerror;
|
||||
use winapi::um::errhandlingapi::GetLastError;
|
||||
use winapi::um::handleapi;
|
||||
use winapi::um::synchapi;
|
||||
use winapi::um::winbase;
|
||||
use winapi::um::winnt;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::{ptr, slice};
|
||||
|
||||
/// Wrapper around a <https://git.zx2c4.com/wintun/about/#wintun_session_handle>
|
||||
pub struct Session {
|
||||
/// The session handle given to us by WintunStartSession
|
||||
pub(crate) session: UnsafeHandle<wintun_raw::WINTUN_SESSION_HANDLE>,
|
||||
|
||||
/// Shared dll for required wintun driver functions
|
||||
pub(crate) wintun: Wintun,
|
||||
|
||||
/// Windows event handle that is signaled by the wintun driver when data becomes available to
|
||||
/// read
|
||||
pub(crate) read_event: OnceCell<UnsafeHandle<winnt::HANDLE>>,
|
||||
|
||||
/// Windows event handle that is signaled when [`Session::shutdown`] is called force blocking
|
||||
/// readers to exit
|
||||
pub(crate) shutdown_event: UnsafeHandle<winnt::HANDLE>,
|
||||
|
||||
/// The adapter that owns this session
|
||||
pub(crate) adapter: Arc<Adapter>,
|
||||
}
|
||||
|
||||
impl Session {
|
||||
/// Allocates a send packet of the specified size. Wraps WintunAllocateSendPacket
|
||||
///
|
||||
/// All packets returned from this function must be sent using [`Session::send_packet`] because
|
||||
/// wintun establishes the send packet order based on the invocation order of this function.
|
||||
/// Therefore if a packet is allocated using this function, and then never sent, it will hold
|
||||
/// up the send queue for all other packets allocated in the future. It is okay for the session
|
||||
/// to shutdown with allocated packets that have not yet been sent
|
||||
pub fn allocate_send_packet(self: &Arc<Self>, size: u16) -> Result<packet::Packet, ()> {
|
||||
let ptr = unsafe {
|
||||
self.wintun
|
||||
.WintunAllocateSendPacket(self.session.0, size as u32)
|
||||
};
|
||||
if ptr.is_null() {
|
||||
Err(())
|
||||
} else {
|
||||
Ok(packet::Packet {
|
||||
//SAFETY: ptr is non null, aligned for u8, and readable for up to size bytes (which
|
||||
//must be less than isize::MAX because bytes is a u16
|
||||
bytes: unsafe { slice::from_raw_parts_mut(ptr, size as usize) },
|
||||
session: self.clone(),
|
||||
kind: packet::Kind::SendPacketPending,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends a packet previously allocated with [`Session::allocate_send_packet`]
|
||||
pub fn send_packet(&self, mut packet: packet::Packet) {
|
||||
assert!(matches!(packet.kind, packet::Kind::SendPacketPending));
|
||||
|
||||
unsafe {
|
||||
self.wintun
|
||||
.WintunSendPacket(self.session.0, packet.bytes.as_ptr())
|
||||
};
|
||||
//Mark the packet at sent
|
||||
packet.kind = packet::Kind::SendPacketSent;
|
||||
}
|
||||
|
||||
/// Attempts to receive a packet from the virtual interface without blocking.
|
||||
/// If there are no packets currently in the receive queue, this function returns Ok(None)
|
||||
/// without blocking. If blocking until a packet is desirable, use [`Session::receive_blocking`]
|
||||
pub fn try_receive(self: &Arc<Self>) -> Result<Option<packet::Packet>, ()> {
|
||||
let mut size = 0u32;
|
||||
|
||||
let ptr = unsafe {
|
||||
self.wintun
|
||||
.WintunReceivePacket(self.session.0, &mut size as *mut u32)
|
||||
};
|
||||
|
||||
debug_assert!(size <= u16::MAX as u32);
|
||||
if ptr.is_null() {
|
||||
//Wintun returns ERROR_NO_MORE_ITEMS instead of blocking if packets are not available
|
||||
let last_error = unsafe { GetLastError() };
|
||||
if last_error == winerror::ERROR_NO_MORE_ITEMS {
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
} else {
|
||||
Ok(Some(packet::Packet {
|
||||
kind: packet::Kind::ReceivePacket,
|
||||
//SAFETY: ptr is non null, aligned for u8, and readable for up to size bytes (which
|
||||
//must be less than isize::MAX because bytes is a u16
|
||||
bytes: unsafe { slice::from_raw_parts_mut(ptr, size as usize) },
|
||||
session: self.clone(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the low level read event handle that is signaled when more data becomes available
|
||||
/// to read
|
||||
pub(crate) fn get_read_wait_event(&self) -> Result<winnt::HANDLE, ()> {
|
||||
Ok(self
|
||||
.read_event
|
||||
.get_or_init(|| unsafe {
|
||||
UnsafeHandle(self.wintun.WintunGetReadWaitEvent(self.session.0) as winnt::HANDLE)
|
||||
})
|
||||
.0)
|
||||
}
|
||||
|
||||
/// Blocks until a packet is available, returning the next packet in the receive queue once this happens.
|
||||
/// If the session is closed via [`Session::shutdown`] all threads currently blocking inside this function
|
||||
/// will return Err(())
|
||||
pub fn receive_blocking(self: &Arc<Self>) -> Result<packet::Packet, ()> {
|
||||
loop {
|
||||
//Try 5 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 _ in 0..5 {
|
||||
match self.try_receive() {
|
||||
Err(err) => return Err(err),
|
||||
Ok(Some(packet)) => return Ok(packet),
|
||||
Ok(None) => {
|
||||
//Try again
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
//Wait on both the read handle and the shutdown handle so that we stop when requested
|
||||
let handles = [self.get_read_wait_event()?, self.shutdown_event.0];
|
||||
let result = unsafe {
|
||||
//SAFETY: We abide by the requirements of WaitForMultipleObjects, handles is a
|
||||
//pointer to valid, aligned, stack memory
|
||||
synchapi::WaitForMultipleObjects(
|
||||
2,
|
||||
&handles as *const winnt::HANDLE,
|
||||
0,
|
||||
winbase::INFINITE,
|
||||
)
|
||||
};
|
||||
match result {
|
||||
winbase::WAIT_FAILED => return Err(()),
|
||||
_ => {
|
||||
if result == winbase::WAIT_OBJECT_0 {
|
||||
//We have data!
|
||||
continue;
|
||||
} else if result == winbase::WAIT_OBJECT_0 + 1 {
|
||||
//Shutdown event triggered
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Cancels any active calls to [`Session::receive_blocking`] making them instantly return Err(_) so that session can be shutdown cleanly
|
||||
pub fn shutdown(&self) {
|
||||
let _ = unsafe { synchapi::SetEvent(self.shutdown_event.0) };
|
||||
let _ = unsafe { handleapi::CloseHandle(self.shutdown_event.0) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Session {
|
||||
fn drop(&mut self) {
|
||||
let _ = Arc::clone(&self.adapter);
|
||||
unsafe { self.wintun.WintunEndSession(self.session.0) };
|
||||
self.session.0 = ptr::null_mut();
|
||||
|
||||
//Adapter must be dropped after we call `WintunEndSession`,
|
||||
//if `self.adapter is the last reference
|
||||
//drop(self.adapter)
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
use winapi::{
|
||||
shared::ntdef::{LANG_NEUTRAL, SUBLANG_DEFAULT},
|
||||
um::{winbase, winnt::MAKELANGID},
|
||||
};
|
||||
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ptr;
|
||||
|
||||
use widestring::U16Str;
|
||||
|
||||
/// A wrapper struct that allows a type to be Send and Sync
|
||||
pub(crate) struct UnsafeHandle<T>(pub T);
|
||||
|
||||
/// We never read from the pointer. It only serves as a handle we pass to the kernel or C code that
|
||||
/// doesn't have the same mutable aliasing restrictions we have in Rust
|
||||
unsafe impl<T> Send for UnsafeHandle<T> {}
|
||||
unsafe impl<T> Sync for UnsafeHandle<T> {}
|
||||
|
||||
/// Returns a a human readable error message from a windows error code
|
||||
pub fn get_error_message(err_code: u32) -> String {
|
||||
const LEN: usize = 256;
|
||||
let mut buf = MaybeUninit::<[u16; LEN]>::uninit();
|
||||
|
||||
//SAFETY: name is a allocated on the stack above therefore it must be valid, non-null and
|
||||
//aligned for u16
|
||||
let first = unsafe { *buf.as_mut_ptr() }.as_mut_ptr();
|
||||
//Write default null terminator in case WintunGetAdapterName leaves name unchanged
|
||||
unsafe { first.write(0u16) };
|
||||
let chars_written = unsafe {
|
||||
winbase::FormatMessageW(
|
||||
winbase::FORMAT_MESSAGE_FROM_SYSTEM | winbase::FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
ptr::null(),
|
||||
err_code,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) as u32,
|
||||
first,
|
||||
LEN as u32,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
|
||||
//SAFETY: first is a valid, non-null, aligned, pointer
|
||||
format!(
|
||||
"{} ({})",
|
||||
unsafe { U16Str::from_ptr(first, chars_written as usize) }.to_string_lossy(),
|
||||
err_code
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Version {
|
||||
pub major: u16,
|
||||
pub minor: u16,
|
||||
}
|
||||
|
||||
/// Returns the major and minor version of the wintun driver
|
||||
pub fn get_running_driver_version(wintun: &crate::Wintun) -> Result<Version, ()> {
|
||||
let version = unsafe { wintun.WintunGetRunningDriverVersion() };
|
||||
if version == 0 {
|
||||
Err(())
|
||||
} else {
|
||||
Ok(Version {
|
||||
major: ((version >> 16) & 0xFF) as u16,
|
||||
minor: (version & 0xFF) as u16,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
Prebuilt Binaries License
|
||||
-------------------------
|
||||
|
||||
1. DEFINITIONS. "Software" means the precise contents of the "wintun.dll"
|
||||
files that are included in the .zip file that contains this document as
|
||||
downloaded from wintun.net/builds.
|
||||
|
||||
2. LICENSE GRANT. WireGuard LLC grants to you a non-exclusive and
|
||||
non-transferable right to use Software for lawful purposes under certain
|
||||
obligations and limited rights as set forth in this agreement.
|
||||
|
||||
3. RESTRICTIONS. Software is owned and copyrighted by WireGuard LLC. It is
|
||||
licensed, not sold. Title to Software and all associated intellectual
|
||||
property rights are retained by WireGuard. You must not:
|
||||
a. reverse engineer, decompile, disassemble, extract from, or otherwise
|
||||
modify the Software;
|
||||
b. modify or create derivative work based upon Software in whole or in
|
||||
parts, except insofar as only the API interfaces of the "wintun.h" file
|
||||
distributed alongside the Software (the "Permitted API") are used;
|
||||
c. remove any proprietary notices, labels, or copyrights from the Software;
|
||||
d. resell, redistribute, lease, rent, transfer, sublicense, or otherwise
|
||||
transfer rights of the Software without the prior written consent of
|
||||
WireGuard LLC, except insofar as the Software is distributed alongside
|
||||
other software that uses the Software only via the Permitted API;
|
||||
e. use the name of WireGuard LLC, the WireGuard project, the Wintun
|
||||
project, or the names of its contributors to endorse or promote products
|
||||
derived from the Software without specific prior written consent.
|
||||
|
||||
4. LIMITED WARRANTY. THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF
|
||||
ANY KIND. WIREGUARD LLC HEREBY EXCLUDES AND DISCLAIMS ALL IMPLIED OR
|
||||
STATUTORY WARRANTIES, INCLUDING ANY WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE, QUALITY, NON-INFRINGEMENT, TITLE, RESULTS,
|
||||
EFFORTS, OR QUIET ENJOYMENT. THERE IS NO WARRANTY THAT THE PRODUCT WILL BE
|
||||
ERROR-FREE OR WILL FUNCTION WITHOUT INTERRUPTION. YOU ASSUME THE ENTIRE
|
||||
RISK FOR THE RESULTS OBTAINED USING THE PRODUCT. TO THE EXTENT THAT
|
||||
WIREGUARD LLC MAY NOT DISCLAIM ANY WARRANTY AS A MATTER OF APPLICABLE LAW,
|
||||
THE SCOPE AND DURATION OF SUCH WARRANTY WILL BE THE MINIMUM PERMITTED UNDER
|
||||
SUCH LAW. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
|
||||
WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR
|
||||
A PARTICULAR PURPOSE OR NON-INFRINGEMENT ARE DISCLAIMED, EXCEPT TO THE
|
||||
EXTENT THAT THESE DISCLAIMERS ARE HELD TO BE LEGALLY INVALID.
|
||||
|
||||
5. LIMITATION OF LIABILITY. To the extent not prohibited by law, in no event
|
||||
WireGuard LLC or any third-party-developer will be liable for any lost
|
||||
revenue, profit or data or for special, indirect, consequential, incidental
|
||||
or punitive damages, however caused regardless of the theory of liability,
|
||||
arising out of or related to the use of or inability to use Software, even
|
||||
if WireGuard LLC has been advised of the possibility of such damages.
|
||||
Solely you are responsible for determining the appropriateness of using
|
||||
Software and accept full responsibility for all risks associated with its
|
||||
exercise of rights under this agreement, including but not limited to the
|
||||
risks and costs of program errors, compliance with applicable laws, damage
|
||||
to or loss of data, programs or equipment, and unavailability or
|
||||
interruption of operations. The foregoing limitations will apply even if
|
||||
the above stated warranty fails of its essential purpose. You acknowledge,
|
||||
that it is in the nature of software that software is complex and not
|
||||
completely free of errors. In no event shall WireGuard LLC or any
|
||||
third-party-developer be liable to you under any theory for any damages
|
||||
suffered by you or any user of Software or for any special, incidental,
|
||||
indirect, consequential or similar damages (including without limitation
|
||||
damages for loss of business profits, business interruption, loss of
|
||||
business information or any other pecuniary loss) arising out of the use or
|
||||
inability to use Software, even if WireGuard LLC has been advised of the
|
||||
possibility of such damages and regardless of the legal or quitable theory
|
||||
(contract, tort, or otherwise) upon which the claim is based.
|
||||
|
||||
6. TERMINATION. This agreement is affected until terminated. You may
|
||||
terminate this agreement at any time. This agreement will terminate
|
||||
immediately without notice from WireGuard LLC if you fail to comply with
|
||||
the terms and conditions of this agreement. Upon termination, you must
|
||||
delete Software and all copies of Software and cease all forms of
|
||||
distribution of Software.
|
||||
|
||||
7. SEVERABILITY. If any provision of this agreement is held to be
|
||||
unenforceable, this agreement will remain in effect with the provision
|
||||
omitted, unless omission would frustrate the intent of the parties, in
|
||||
which case this agreement will immediately terminate.
|
||||
|
||||
8. RESERVATION OF RIGHTS. All rights not expressly granted in this agreement
|
||||
are reserved by WireGuard LLC. For example, WireGuard LLC reserves the
|
||||
right at any time to cease development of Software, to alter distribution
|
||||
details, features, specifications, capabilities, functions, licensing
|
||||
terms, release dates, APIs, ABIs, general availability, or other
|
||||
characteristics of the Software.
|
||||
@@ -1,270 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
*
|
||||
* Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <ipexport.h>
|
||||
#include <ifdef.h>
|
||||
#include <ws2ipdef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef ALIGNED
|
||||
# if defined(_MSC_VER)
|
||||
# define ALIGNED(n) __declspec(align(n))
|
||||
# elif defined(__GNUC__)
|
||||
# define ALIGNED(n) __attribute__((aligned(n)))
|
||||
# else
|
||||
# error "Unable to define ALIGNED"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* MinGW is missing this one, unfortunately. */
|
||||
#ifndef _Post_maybenull_
|
||||
# define _Post_maybenull_
|
||||
#endif
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4324) /* structure was padded due to alignment specifier */
|
||||
|
||||
/**
|
||||
* A handle representing Wintun adapter
|
||||
*/
|
||||
typedef struct _WINTUN_ADAPTER *WINTUN_ADAPTER_HANDLE;
|
||||
|
||||
/**
|
||||
* Creates a new Wintun adapter.
|
||||
*
|
||||
* @param Name The requested name of the adapter. Zero-terminated string of up to MAX_ADAPTER_NAME-1
|
||||
* characters.
|
||||
*
|
||||
* @param TunnelType Name of the adapter tunnel type. Zero-terminated string of up to MAX_ADAPTER_NAME-1
|
||||
* characters.
|
||||
*
|
||||
* @param RequestedGUID The GUID of the created network adapter, which then influences NLA generation deterministically.
|
||||
* If it is set to NULL, the GUID is chosen by the system at random, and hence a new NLA entry is
|
||||
* created for each new adapter. It is called "requested" GUID because the API it uses is
|
||||
* completely undocumented, and so there could be minor interesting complications with its usage.
|
||||
*
|
||||
* @return If the function succeeds, the return value is the adapter handle. Must be released with
|
||||
* WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call
|
||||
* GetLastError.
|
||||
*/
|
||||
typedef _Must_inspect_result_
|
||||
_Return_type_success_(return != NULL)
|
||||
_Post_maybenull_
|
||||
WINTUN_ADAPTER_HANDLE(WINAPI WINTUN_CREATE_ADAPTER_FUNC)
|
||||
(_In_z_ LPCWSTR Name, _In_z_ LPCWSTR TunnelType, _In_opt_ const GUID *RequestedGUID);
|
||||
|
||||
/**
|
||||
* Opens an existing Wintun adapter.
|
||||
*
|
||||
* @param Name The requested name of the adapter. Zero-terminated string of up to MAX_ADAPTER_NAME-1
|
||||
* characters.
|
||||
*
|
||||
* @return If the function succeeds, the return value is the adapter handle. Must be released with
|
||||
* WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call
|
||||
* GetLastError.
|
||||
*/
|
||||
typedef _Must_inspect_result_
|
||||
_Return_type_success_(return != NULL)
|
||||
_Post_maybenull_
|
||||
WINTUN_ADAPTER_HANDLE(WINAPI WINTUN_OPEN_ADAPTER_FUNC)(_In_z_ LPCWSTR Name);
|
||||
|
||||
/**
|
||||
* Releases Wintun adapter resources and, if adapter was created with WintunCreateAdapter, removes adapter.
|
||||
*
|
||||
* @param Adapter Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter.
|
||||
*/
|
||||
typedef VOID(WINAPI WINTUN_CLOSE_ADAPTER_FUNC)(_In_opt_ WINTUN_ADAPTER_HANDLE Adapter);
|
||||
|
||||
/**
|
||||
* Deletes the Wintun driver if there are no more adapters in use.
|
||||
*
|
||||
* @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To
|
||||
* get extended error information, call GetLastError.
|
||||
*/
|
||||
typedef _Return_type_success_(return != FALSE)
|
||||
BOOL(WINAPI WINTUN_DELETE_DRIVER_FUNC)(VOID);
|
||||
|
||||
/**
|
||||
* Returns the LUID of the adapter.
|
||||
*
|
||||
* @param Adapter Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter
|
||||
*
|
||||
* @param Luid Pointer to LUID to receive adapter LUID.
|
||||
*/
|
||||
typedef VOID(WINAPI WINTUN_GET_ADAPTER_LUID_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _Out_ NET_LUID *Luid);
|
||||
|
||||
/**
|
||||
* Determines the version of the Wintun driver currently loaded.
|
||||
*
|
||||
* @return If the function succeeds, the return value is the version number. If the function fails, the return value is
|
||||
* zero. To get extended error information, call GetLastError. Possible errors include the following:
|
||||
* ERROR_FILE_NOT_FOUND Wintun not loaded
|
||||
*/
|
||||
typedef _Return_type_success_(return != 0)
|
||||
DWORD(WINAPI WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC)(VOID);
|
||||
|
||||
/**
|
||||
* Determines the level of logging, passed to WINTUN_LOGGER_CALLBACK.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
WINTUN_LOG_INFO, /**< Informational */
|
||||
WINTUN_LOG_WARN, /**< Warning */
|
||||
WINTUN_LOG_ERR /**< Error */
|
||||
} WINTUN_LOGGER_LEVEL;
|
||||
|
||||
/**
|
||||
* Called by internal logger to report diagnostic messages
|
||||
*
|
||||
* @param Level Message level.
|
||||
*
|
||||
* @param Timestamp Message timestamp in in 100ns intervals since 1601-01-01 UTC.
|
||||
*
|
||||
* @param Message Message text.
|
||||
*/
|
||||
typedef VOID(CALLBACK *WINTUN_LOGGER_CALLBACK)(
|
||||
_In_ WINTUN_LOGGER_LEVEL Level,
|
||||
_In_ DWORD64 Timestamp,
|
||||
_In_z_ LPCWSTR Message);
|
||||
|
||||
/**
|
||||
* Sets logger callback function.
|
||||
*
|
||||
* @param NewLogger Pointer to callback function to use as a new global logger. NewLogger may be called from various
|
||||
* threads concurrently. Should the logging require serialization, you must handle serialization in
|
||||
* NewLogger. Set to NULL to disable.
|
||||
*/
|
||||
typedef VOID(WINAPI WINTUN_SET_LOGGER_FUNC)(_In_ WINTUN_LOGGER_CALLBACK NewLogger);
|
||||
|
||||
/**
|
||||
* Minimum ring capacity.
|
||||
*/
|
||||
#define WINTUN_MIN_RING_CAPACITY 0x20000 /* 128kiB */
|
||||
|
||||
/**
|
||||
* Maximum ring capacity.
|
||||
*/
|
||||
#define WINTUN_MAX_RING_CAPACITY 0x4000000 /* 64MiB */
|
||||
|
||||
/**
|
||||
* A handle representing Wintun session
|
||||
*/
|
||||
typedef struct _TUN_SESSION *WINTUN_SESSION_HANDLE;
|
||||
|
||||
/**
|
||||
* Starts Wintun session.
|
||||
*
|
||||
* @param Adapter Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter
|
||||
*
|
||||
* @param Capacity Rings capacity. Must be between WINTUN_MIN_RING_CAPACITY and WINTUN_MAX_RING_CAPACITY (incl.)
|
||||
* Must be a power of two.
|
||||
*
|
||||
* @return Wintun session handle. Must be released with WintunEndSession. If the function fails, the return value is
|
||||
* NULL. To get extended error information, call GetLastError.
|
||||
*/
|
||||
typedef _Must_inspect_result_
|
||||
_Return_type_success_(return != NULL)
|
||||
_Post_maybenull_
|
||||
WINTUN_SESSION_HANDLE(WINAPI WINTUN_START_SESSION_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _In_ DWORD Capacity);
|
||||
|
||||
/**
|
||||
* Ends Wintun session.
|
||||
*
|
||||
* @param Session Wintun session handle obtained with WintunStartSession
|
||||
*/
|
||||
typedef VOID(WINAPI WINTUN_END_SESSION_FUNC)(_In_ WINTUN_SESSION_HANDLE Session);
|
||||
|
||||
/**
|
||||
* Gets Wintun session's read-wait event handle.
|
||||
*
|
||||
* @param Session Wintun session handle obtained with WintunStartSession
|
||||
*
|
||||
* @return Pointer to receive event handle to wait for available data when reading. Should
|
||||
* WintunReceivePackets return ERROR_NO_MORE_ITEMS (after spinning on it for a while under heavy
|
||||
* load), wait for this event to become signaled before retrying WintunReceivePackets. Do not call
|
||||
* CloseHandle on this event - it is managed by the session.
|
||||
*/
|
||||
typedef HANDLE(WINAPI WINTUN_GET_READ_WAIT_EVENT_FUNC)(_In_ WINTUN_SESSION_HANDLE Session);
|
||||
|
||||
/**
|
||||
* Maximum IP packet size
|
||||
*/
|
||||
#define WINTUN_MAX_IP_PACKET_SIZE 0xFFFF
|
||||
|
||||
/**
|
||||
* Retrieves one or packet. After the packet content is consumed, call WintunReleaseReceivePacket with Packet returned
|
||||
* from this function to release internal buffer. This function is thread-safe.
|
||||
*
|
||||
* @param Session Wintun session handle obtained with WintunStartSession
|
||||
*
|
||||
* @param PacketSize Pointer to receive packet size.
|
||||
*
|
||||
* @return Pointer to layer 3 IPv4 or IPv6 packet. Client may modify its content at will. If the function fails, the
|
||||
* return value is NULL. To get extended error information, call GetLastError. Possible errors include the
|
||||
* following:
|
||||
* ERROR_HANDLE_EOF Wintun adapter is terminating;
|
||||
* ERROR_NO_MORE_ITEMS Wintun buffer is exhausted;
|
||||
* ERROR_INVALID_DATA Wintun buffer is corrupt
|
||||
*/
|
||||
typedef _Must_inspect_result_
|
||||
_Return_type_success_(return != NULL)
|
||||
_Post_maybenull_
|
||||
_Post_writable_byte_size_(*PacketSize)
|
||||
BYTE *(WINAPI WINTUN_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _Out_ DWORD *PacketSize);
|
||||
|
||||
/**
|
||||
* Releases internal buffer after the received packet has been processed by the client. This function is thread-safe.
|
||||
*
|
||||
* @param Session Wintun session handle obtained with WintunStartSession
|
||||
*
|
||||
* @param Packet Packet obtained with WintunReceivePacket
|
||||
*/
|
||||
typedef VOID(
|
||||
WINAPI WINTUN_RELEASE_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet);
|
||||
|
||||
/**
|
||||
* Allocates memory for a packet to send. After the memory is filled with packet data, call WintunSendPacket to send
|
||||
* and release internal buffer. WintunAllocateSendPacket is thread-safe and the WintunAllocateSendPacket order of
|
||||
* calls define the packet sending order.
|
||||
*
|
||||
* @param Session Wintun session handle obtained with WintunStartSession
|
||||
*
|
||||
* @param PacketSize Exact packet size. Must be less or equal to WINTUN_MAX_IP_PACKET_SIZE.
|
||||
*
|
||||
* @return Returns pointer to memory where to prepare layer 3 IPv4 or IPv6 packet for sending. If the function fails,
|
||||
* the return value is NULL. To get extended error information, call GetLastError. Possible errors include the
|
||||
* following:
|
||||
* ERROR_HANDLE_EOF Wintun adapter is terminating;
|
||||
* ERROR_BUFFER_OVERFLOW Wintun buffer is full;
|
||||
*/
|
||||
typedef _Must_inspect_result_
|
||||
_Return_type_success_(return != NULL)
|
||||
_Post_maybenull_
|
||||
_Post_writable_byte_size_(PacketSize)
|
||||
BYTE *(WINAPI WINTUN_ALLOCATE_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ DWORD PacketSize);
|
||||
|
||||
/**
|
||||
* Sends the packet and releases internal buffer. WintunSendPacket is thread-safe, but the WintunAllocateSendPacket
|
||||
* order of calls define the packet sending order. This means the packet is not guaranteed to be sent in the
|
||||
* WintunSendPacket yet.
|
||||
*
|
||||
* @param Session Wintun session handle obtained with WintunStartSession
|
||||
*
|
||||
* @param Packet Packet obtained with WintunAllocateSendPacket
|
||||
*/
|
||||
typedef VOID(WINAPI WINTUN_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet);
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,19 +0,0 @@
|
||||
// Information about functions taken from:
|
||||
// https://git.zx2c4.com/wintun/tree/example/example.c
|
||||
|
||||
#include "wintun.h"
|
||||
|
||||
WINTUN_CREATE_ADAPTER_FUNC WintunCreateAdapter;
|
||||
WINTUN_CLOSE_ADAPTER_FUNC WintunCloseAdapter;
|
||||
WINTUN_OPEN_ADAPTER_FUNC WintunOpenAdapter;
|
||||
WINTUN_GET_ADAPTER_LUID_FUNC WintunGetAdapterLUID;
|
||||
WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC WintunGetRunningDriverVersion;
|
||||
WINTUN_DELETE_DRIVER_FUNC WintunDeleteDriver;
|
||||
WINTUN_SET_LOGGER_FUNC WintunSetLogger;
|
||||
WINTUN_START_SESSION_FUNC WintunStartSession;
|
||||
WINTUN_END_SESSION_FUNC WintunEndSession;
|
||||
WINTUN_GET_READ_WAIT_EVENT_FUNC WintunGetReadWaitEvent;
|
||||
WINTUN_RECEIVE_PACKET_FUNC WintunReceivePacket;
|
||||
WINTUN_RELEASE_RECEIVE_PACKET_FUNC WintunReleaseReceivePacket;
|
||||
WINTUN_ALLOCATE_SEND_PACKET_FUNC WintunAllocateSendPacket;
|
||||
WINTUN_SEND_PACKET_FUNC WintunSendPacket;
|
||||
@@ -0,0 +1,44 @@
|
||||
[package]
|
||||
name = "vnt-cli"
|
||||
version = "1.2.9"
|
||||
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_json = "1.0.94"
|
||||
serde_yaml = "0.9.32"
|
||||
log = "0.4.17"
|
||||
log4rs = "1.2.0"
|
||||
[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"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
winapi = { version = "0.3.9", features = ["handleapi", "processthreadsapi", "winnt", "securitybaseapi", "impl-default"] }
|
||||
|
||||
[features]
|
||||
default = ["server_encrypt","aes_gcm","aes_cbc","aes_ecb","sm4_cbc","ip_proxy"]
|
||||
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"]
|
||||
server_encrypt=["vnt/server_encrypt"]
|
||||
ip_proxy=["vnt/ip_proxy"]
|
||||
[build-dependencies]
|
||||
embed-manifest = "1.4.0"
|
||||
rand = "0.9.0-alpha.0"
|
||||
@@ -0,0 +1,146 @@
|
||||
## 模块介绍
|
||||
体积小,可以在服务器、路由器等环境使用
|
||||
## 详细参数说明
|
||||
### -k `<token>`
|
||||
一个虚拟局域网的标识,在同一服务器下,相同token的设备会组建一个局域网
|
||||
### -n `<name>`
|
||||
设备名称,方便区分不同设备
|
||||
### -d `<id>`
|
||||
设备id,每台设备的唯一标识,注意不要重复
|
||||
### -c
|
||||
关闭控制台交互式命令,后台运行时可以加此参数
|
||||
### -s `<server>`
|
||||
注册和中继服务器地址,注册和转发数据
|
||||
### -e `<stun-server>`
|
||||
使用stun服务探测客户端NAT类型,不同类型有不同的打洞策略
|
||||
### -a
|
||||
加了此参数表示使用tap网卡,默认使用tun网卡,tun网卡效率更高
|
||||
### --nic `<tun0>`
|
||||
指定虚拟网卡名称,默认tun模式使用vnt-tun,tap模式使用vnt-tap
|
||||
### -i `<in-ip>`、-o `<out-ip>`
|
||||
|
||||
配置点对网(IP代理)时使用,例如A(虚拟ip:10.26.0.2)通过B(虚拟ip:10.26.0.3,本地出口ip:192.168.0.10)访问C(目标网段192.168.0.0/24),
|
||||
|
||||
则在A配置 **'-i 192.168.0.0/24,10.26.0.3'** ,表示将192.168.0.0/24网段的数据都转发到10.26.0.3节点
|
||||
|
||||
在B配置 **'-o 192.168.0.0/24'** ,表示允许将数据转发到 192.168.0.0/24 ,允许转发所有网段可以使用 **'-o 0.0.0.0/0'**
|
||||
|
||||
-i和-o参数均可使用多次,来指定不同网段,例如 **'-o 192.168.1.0/24 -o 192.168.2.0/24'** 表示允许转发目标为192.168.1.0/24或192.168.2.0/24这两个网段的数据
|
||||
|
||||
### -w `<password>`
|
||||
|
||||
提升通信安全性,使用该密码生成的密钥对客户端数据进行加密,并且服务端无法解密(包括中继数据)。使用相同密码的客户端才能通信
|
||||
|
||||
| 密码位数 | 加密算法 |
|
||||
|-------|------------|
|
||||
| 小于8 | AES128-GCM |
|
||||
| 大于等于8 | AES256-GCM |
|
||||
|
||||
### -W
|
||||
开启和服务端通信的数据加密,采用rsa+aes256gcm加密客户端和服务端之间通信的数据,可以避免token泄漏、中间人攻击
|
||||
|
||||
注意:
|
||||
1. -w `<password>`是用于客户端-客户端之间的加密,password不会传递到服务端,只添加这个参数不会加密客户端-服务端通信的数据
|
||||
2. -W 用于开启客户端-服务端之间的加密
|
||||
|
||||
### -u `<mtu>`
|
||||
|
||||
设置虚拟网卡的mtu值,大多数情况下使用默认值效率会更高,也可根据实际情况微调这个值,不加密默认为1450,加密默认为1410
|
||||
|
||||
### --tcp
|
||||
和服务端使用tcp通信。有些网络提供商对UDP限制比较大,这个时候可以选择使用TCP模式,提高稳定性。一般来说udp延迟和消耗更低
|
||||
### --ip `<IP>`
|
||||
指定虚拟ip,指定的ip不能和其他设备重复,必须有效并且在服务端所属网段下,默认情况由服务端分配
|
||||
### --par `<parallel>`
|
||||
任务并行度(必须为正整数),默认值为1,该值表示处理网卡读写的任务数,组网设备数较多、处理延迟较大时可适当调大此值
|
||||
### --model `<model>`
|
||||
加密模式,可选值 aes_gcm/aes_cbc/aes_ecb/sm4_cbc,默认使用aes_gcm,通常情况aes_gcm安全性高、aes_ecb性能更好,但是在低性能设备上sm4_cbc也许速度会更快;
|
||||
|
||||
|
||||
| 密码位数 | 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 |
|
||||
### --finger
|
||||
|
||||
开启数据指纹校验,可增加安全性,如果服务端开启指纹校验,则客户端也必须开启,开启会损耗一部分性能
|
||||
|
||||
注意:默认情况下服务端不会对中转的数据做校验,如果要对中转的数据做校验,则需要客户端、服务端都开启此参数
|
||||
### --punch `<punch>`
|
||||
取值ipv4/ipv6,选择只使用ipv4打洞或者只使用ipv6打洞,默认两则都会使用
|
||||
### --ports `<port1,port2>`
|
||||
指定本地监听的端口组,多个端口使用逗号分隔,多个端口可以分摊流量,增加并发,tcp会监听端口组的第一个端口,用于tcp直连
|
||||
|
||||
- 例1:‘--ports 12345,12346,12347’ 表示udp监听12345、12346、12347这三个端口,tcp监听12345端口
|
||||
- 例2:‘--ports 0,0’ 表示udp监听两个未使用的端口,tcp监听一个未使用的端口
|
||||
### --cmd
|
||||
开启交互式命令,开启后可以直接在窗口下输入命令,如需后台运行请勿开启
|
||||
### --first_latency
|
||||
优先使用低延迟通道,默认情况下优先使用p2p通道,某些情况下可能p2p比客户端中继延迟更高,可使用此参数进行优化传输
|
||||
### --no-proxy
|
||||
关闭内置的ip代理,内置的代理较为简单,而且一般来说直接使用网卡NAT转发性能会更高,
|
||||
有需要可以自行配置NAT转发,[可参考‘编译’小节中的NAT配置](https://github.com/lbl8603/vnt#%E7%BC%96%E8%AF%91)
|
||||
### -f `<conf>`
|
||||
指定配置文件
|
||||
配置文件采用yaml格式,可参考:
|
||||
```yaml
|
||||
# 全部参数
|
||||
tap: false #是否使用tap
|
||||
token: xxx #组网token
|
||||
device_id: xxx #当前设备id
|
||||
name: windows 11 #当前设备名称
|
||||
server_address: ip:port #注册和中继服务器
|
||||
stun_server: #stun服务器
|
||||
- stun1.l.google.com:19302
|
||||
- stun2.l.google.com:19302
|
||||
in_ips: #代理ip入站
|
||||
- 192.168.1.0/24,10.26.0.3
|
||||
out_ips: #代理ip出站
|
||||
- 0.0.0.0/0
|
||||
password: xxx #密码
|
||||
mtu: 1420 #mtu
|
||||
tcp: false #tcp模式
|
||||
ip: 10.26.0.2 #指定虚拟ip
|
||||
use_channel: relay #relay:仅中继模式.p2p:仅直连模式
|
||||
server_encrypt: true #服务端加密
|
||||
parallel: 1 #任务并行度
|
||||
cipher_model: aes_gcm #客户端加密算法
|
||||
finger: false #关闭数据指纹
|
||||
punch_model: ipv4 #打洞模式
|
||||
ports:
|
||||
- 0 #使用随机端口,tcp监听此端口
|
||||
- 0
|
||||
cmd: false #关闭控制台输入
|
||||
no_proxy: false #是否关闭内置代理,true为关闭
|
||||
first_latency: false #是否优先低延迟通道,默认为false,表示优先使用p2p通道
|
||||
device_name: vnt-tun #网卡名称
|
||||
packet_loss: 0 #指定丢包率 取值0~1之间的数 用于模拟弱网
|
||||
packet_delay: 0 #指定延迟 单位毫秒 用于模拟弱网
|
||||
```
|
||||
|
||||
或者需要哪个配置就加哪个,当然token是必须的
|
||||
```yaml
|
||||
# 部分参数
|
||||
token: xxx #组网token
|
||||
```
|
||||
### --use-channel `<relay/p2p>`
|
||||
- relay:仅中继模式,会禁止打洞/p2p直连,只使用服务器转发
|
||||
- p2p:仅直连模式,会禁止网络数据从服务器/客户端转发,只会使用服务器转发控制包
|
||||
### --packet-loss `<0>`
|
||||
模拟丢包,取值0~1之间的小数,程序会按设定的概率主动丢包。在模拟弱网环境会有帮助。
|
||||
|
||||
### --list
|
||||
在后台运行时,查看其他设备列表
|
||||
### --all
|
||||
在后台运行时,查看其他设备完整信息
|
||||
### --info
|
||||
在后台运行时,查看当前设备信息
|
||||
### --route
|
||||
在后台运行时,查看数据转发路径
|
||||
### --stop
|
||||
停止后台运行
|
||||
@@ -0,0 +1,17 @@
|
||||
use rand::Rng;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
fn main() {
|
||||
// 生成随机序列号
|
||||
let serial_number = format!(
|
||||
"{}-{}-{}",
|
||||
rand::thread_rng().gen_range(100..1000),
|
||||
rand::thread_rng().gen_range(100..1000),
|
||||
rand::thread_rng().gen_range(100..1000)
|
||||
);
|
||||
let generated_code = format!(r#"pub const SERIAL_NUMBER: &str = "{}";"#, serial_number);
|
||||
let dest_path = "src/generated_serial_number.rs";
|
||||
let mut file = File::create(&dest_path).unwrap();
|
||||
file.write_all(generated_code.as_bytes()).unwrap();
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
use std::process;
|
||||
|
||||
use console::style;
|
||||
|
||||
use vnt::handle::callback::{ConnectInfo, ErrorType};
|
||||
use vnt::{DeviceInfo, ErrorInfo, HandshakeInfo, RegisterInfo, VntCallback};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct VntHandler {}
|
||||
|
||||
impl VntCallback for VntHandler {
|
||||
fn success(&self) {
|
||||
println!(" {} ", style("====== Connect Successfully ======").green())
|
||||
}
|
||||
fn create_tun(&self, info: DeviceInfo) {
|
||||
println!("create_tun {}", info)
|
||||
}
|
||||
|
||||
fn connect(&self, info: ConnectInfo) {
|
||||
println!("connect {}", info)
|
||||
}
|
||||
|
||||
fn handshake(&self, info: HandshakeInfo) -> bool {
|
||||
println!("handshake {}", info);
|
||||
true
|
||||
}
|
||||
|
||||
fn register(&self, info: RegisterInfo) -> bool {
|
||||
println!("register {}", style(info).green());
|
||||
true
|
||||
}
|
||||
|
||||
fn error(&self, info: ErrorInfo) {
|
||||
log::error!("error {:?}", info);
|
||||
println!("{}", style(format!("error {}", info)).red());
|
||||
match info.code {
|
||||
ErrorType::TokenError
|
||||
| ErrorType::AddressExhausted
|
||||
| ErrorType::IpAlreadyExists
|
||||
| ErrorType::InvalidIp
|
||||
| ErrorType::LocalIpExists => {
|
||||
self.stop();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn stop(&self) {
|
||||
println!("stopped");
|
||||
process::exit(0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
use serde::Deserialize;
|
||||
use std::io;
|
||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::command::entity::{DeviceItem, Info, RouteItem};
|
||||
|
||||
pub struct CommandClient {
|
||||
buf: [u8; 10240],
|
||||
udp: UdpSocket,
|
||||
}
|
||||
|
||||
impl CommandClient {
|
||||
pub fn new() -> io::Result<Self> {
|
||||
let port = read_command_port().unwrap_or_else(|e| {
|
||||
log::warn!("read_command_port:{:?}", e);
|
||||
39271
|
||||
});
|
||||
let udp = UdpSocket::bind("127.0.0.1:0")?;
|
||||
udp.set_read_timeout(Some(Duration::from_secs(5)))?;
|
||||
udp.connect(SocketAddr::V4(SocketAddrV4::new(
|
||||
Ipv4Addr::new(127, 0, 0, 1),
|
||||
port,
|
||||
)))?;
|
||||
Ok(Self {
|
||||
udp,
|
||||
buf: [0; 10240],
|
||||
})
|
||||
}
|
||||
}
|
||||
fn read_command_port() -> io::Result<u16> {
|
||||
let path_buf = crate::app_home()?.join("command-port");
|
||||
let port = std::fs::read_to_string(path_buf)?;
|
||||
match u16::from_str(&port) {
|
||||
Ok(port) => Ok(port),
|
||||
Err(_) => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"'command-port' file error",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandClient {
|
||||
pub fn list(&mut self) -> io::Result<Vec<DeviceItem>> {
|
||||
self.send_cmd(b"list")
|
||||
}
|
||||
pub fn route(&mut self) -> io::Result<Vec<RouteItem>> {
|
||||
self.send_cmd(b"route")
|
||||
}
|
||||
pub fn info(&mut self) -> io::Result<Info> {
|
||||
self.send_cmd(b"info")
|
||||
}
|
||||
fn send_cmd<'a, V: Deserialize<'a>>(&'a mut self, cmd: &[u8]) -> io::Result<V> {
|
||||
self.udp.send(cmd)?;
|
||||
let len = self.udp.recv(&mut self.buf)?;
|
||||
match serde_yaml::from_slice::<V>(&self.buf[..len]) {
|
||||
Ok(val) => Ok(val),
|
||||
Err(e) => {
|
||||
log::error!("{:?},{:?}", &self.buf[..len], e);
|
||||
Err(io::Error::new(io::ErrorKind::Other, "data error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn stop(&self) -> io::Result<String> {
|
||||
self.udp.send(b"stop")?;
|
||||
let mut buf = [0; 10240];
|
||||
let len = self.udp.recv(&mut buf)?;
|
||||
Ok(String::from_utf8(buf[..len].to_vec()).unwrap())
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Status {
|
||||
pub struct Info {
|
||||
pub name: String,
|
||||
pub virtual_ip: String,
|
||||
pub virtual_gateway: String,
|
||||
@@ -9,7 +9,10 @@ pub struct Status {
|
||||
pub relay_server: String,
|
||||
pub nat_type: String,
|
||||
pub public_ips: String,
|
||||
pub local_ip: String,
|
||||
pub local_addr: String,
|
||||
pub ipv6_addr: String,
|
||||
pub up: u64,
|
||||
pub down: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
@@ -28,7 +31,10 @@ pub struct DeviceItem {
|
||||
pub nat_type: String,
|
||||
pub public_ips: String,
|
||||
pub local_ip: String,
|
||||
pub ipv6: String,
|
||||
pub nat_traversal_type: String,
|
||||
pub rt: String,
|
||||
pub status: String,
|
||||
pub client_secret: bool,
|
||||
pub current_client_secret: bool,
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
use std::io;
|
||||
use vnt::core::Vnt;
|
||||
|
||||
use crate::command::entity::{DeviceItem, Info, RouteItem};
|
||||
use crate::console_out;
|
||||
|
||||
pub mod client;
|
||||
pub mod entity;
|
||||
pub mod server;
|
||||
|
||||
pub enum CommandEnum {
|
||||
Route,
|
||||
List,
|
||||
All,
|
||||
Info,
|
||||
Stop,
|
||||
}
|
||||
|
||||
pub fn command(cmd: CommandEnum) {
|
||||
if let Err(e) = command_(cmd) {
|
||||
println!("cmd: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn command_(cmd: CommandEnum) -> io::Result<()> {
|
||||
let mut command_client = client::CommandClient::new()?;
|
||||
match cmd {
|
||||
CommandEnum::Route => {
|
||||
let list = command_client.route()?;
|
||||
console_out::console_route_table(list);
|
||||
}
|
||||
CommandEnum::List => {
|
||||
let list = command_client.list()?;
|
||||
console_out::console_device_list(list);
|
||||
}
|
||||
CommandEnum::All => {
|
||||
let list = command_client.list()?;
|
||||
console_out::console_device_list_all(list);
|
||||
}
|
||||
CommandEnum::Info => {
|
||||
let info = command_client.info()?;
|
||||
console_out::console_info(info);
|
||||
}
|
||||
CommandEnum::Stop => {
|
||||
command_client.stop()?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn command_route(vnt: &Vnt) -> Vec<RouteItem> {
|
||||
let route_table = vnt.route_table();
|
||||
let mut route_list = Vec::with_capacity(route_table.len());
|
||||
for (destination, routes) in route_table {
|
||||
for route in routes {
|
||||
let next_hop = vnt
|
||||
.route_key(&route.route_key())
|
||||
.map_or(String::new(), |v| v.to_string());
|
||||
let metric = route.metric.to_string();
|
||||
let rt = if route.rt < 0 {
|
||||
"".to_string()
|
||||
} else {
|
||||
route.rt.to_string()
|
||||
};
|
||||
let interface = if route.is_tcp {
|
||||
format!("tcp@{}", route.addr)
|
||||
} else {
|
||||
route.addr.to_string()
|
||||
};
|
||||
let item = RouteItem {
|
||||
destination: destination.to_string(),
|
||||
next_hop,
|
||||
metric,
|
||||
rt,
|
||||
interface,
|
||||
};
|
||||
route_list.push(item);
|
||||
}
|
||||
}
|
||||
route_list
|
||||
}
|
||||
|
||||
pub fn command_list(vnt: &Vnt) -> Vec<DeviceItem> {
|
||||
let info = vnt.current_device();
|
||||
let device_list = vnt.device_list();
|
||||
let mut list = Vec::new();
|
||||
let current_client_secret = vnt.client_encrypt();
|
||||
for peer in device_list {
|
||||
let name = peer.name;
|
||||
let virtual_ip = peer.virtual_ip.to_string();
|
||||
let (nat_type, public_ips, local_ip, ipv6) =
|
||||
if let Some(nat_info) = vnt.peer_nat_info(&peer.virtual_ip) {
|
||||
let nat_type = format!("{:?}", nat_info.nat_type);
|
||||
let public_ips: Vec<String> =
|
||||
nat_info.public_ips.iter().map(|v| v.to_string()).collect();
|
||||
let public_ips = public_ips.join(",");
|
||||
let local_ip = nat_info
|
||||
.local_ipv4()
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or("None".to_string());
|
||||
let ipv6 = nat_info
|
||||
.ipv6()
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or("None".to_string());
|
||||
(nat_type, public_ips, local_ip, ipv6)
|
||||
} else {
|
||||
(
|
||||
"".to_string(),
|
||||
"".to_string(),
|
||||
"".to_string(),
|
||||
"".to_string(),
|
||||
)
|
||||
};
|
||||
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 {
|
||||
"tcp-p2p"
|
||||
} else {
|
||||
"p2p"
|
||||
}
|
||||
} else {
|
||||
let next_hop = vnt.route_key(&route.route_key());
|
||||
if let Some(next_hop) = next_hop {
|
||||
if info.is_gateway(&next_hop) {
|
||||
"server-relay"
|
||||
} else {
|
||||
"client-relay"
|
||||
}
|
||||
} else {
|
||||
"server-relay"
|
||||
}
|
||||
}
|
||||
.to_string();
|
||||
let rt = if route.rt < 0 {
|
||||
"".to_string()
|
||||
} else {
|
||||
route.rt.to_string()
|
||||
};
|
||||
(nat_traversal_type, rt)
|
||||
} else {
|
||||
("relay".to_string(), "".to_string())
|
||||
};
|
||||
let status = format!("{:?}", peer.status);
|
||||
let client_secret = peer.client_secret;
|
||||
let item = DeviceItem {
|
||||
name,
|
||||
virtual_ip,
|
||||
nat_type,
|
||||
public_ips,
|
||||
local_ip,
|
||||
ipv6,
|
||||
nat_traversal_type,
|
||||
rt,
|
||||
status,
|
||||
client_secret,
|
||||
current_client_secret,
|
||||
};
|
||||
list.push(item);
|
||||
}
|
||||
list
|
||||
}
|
||||
|
||||
pub fn command_info(vnt: &Vnt) -> Info {
|
||||
let current_device = vnt.current_device();
|
||||
let nat_info = vnt.nat_info();
|
||||
let name = vnt.name().to_string();
|
||||
let virtual_ip = current_device.virtual_ip().to_string();
|
||||
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 nat_type = format!("{:?}", nat_info.nat_type);
|
||||
let public_ips: Vec<String> = nat_info.public_ips.iter().map(|v| v.to_string()).collect();
|
||||
let public_ips = public_ips.join(",");
|
||||
let local_addr = nat_info
|
||||
.local_ipv4()
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or("None".to_string());
|
||||
let ipv6_addr = nat_info
|
||||
.ipv6()
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or("None".to_string());
|
||||
let up = vnt.up_stream();
|
||||
let down = vnt.down_stream();
|
||||
Info {
|
||||
name,
|
||||
virtual_ip,
|
||||
virtual_gateway,
|
||||
virtual_netmask,
|
||||
connect_status,
|
||||
relay_server,
|
||||
nat_type,
|
||||
public_ips,
|
||||
local_addr,
|
||||
ipv6_addr,
|
||||
up,
|
||||
down,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
use std::net::UdpSocket;
|
||||
|
||||
use vnt::core::Vnt;
|
||||
|
||||
pub struct CommandServer {}
|
||||
|
||||
impl CommandServer {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandServer {
|
||||
pub fn start(self, vnt: Vnt) -> io::Result<()> {
|
||||
let udp = if let Ok(udp) = UdpSocket::bind("127.0.0.1:39271") {
|
||||
udp
|
||||
} else {
|
||||
UdpSocket::bind("127.0.0.1:0")?
|
||||
};
|
||||
let addr = udp.local_addr()?;
|
||||
log::info!("启动后台cmd:{:?}", addr);
|
||||
if let Err(e) = save_port(addr.port()) {
|
||||
log::warn!("保存后台命令端口失败:{:?}", e);
|
||||
}
|
||||
|
||||
let mut buf = [0u8; 64];
|
||||
loop {
|
||||
let (len, addr) = udp.recv_from(&mut buf)?;
|
||||
match std::str::from_utf8(&buf[..len]) {
|
||||
Ok(cmd) => {
|
||||
if let Ok(out) = command(cmd, &vnt) {
|
||||
if let Err(e) = udp.send_to(out.as_bytes(), addr) {
|
||||
log::warn!("cmd={},err={:?}", cmd, e);
|
||||
}
|
||||
if "stopped" == &out {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
fn save_port(port: u16) -> io::Result<()> {
|
||||
let path_buf = crate::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()
|
||||
}
|
||||
|
||||
fn command(cmd: &str, vnt: &Vnt) -> io::Result<String> {
|
||||
let cmd = cmd.trim();
|
||||
let out_str = match cmd {
|
||||
"route" => serde_yaml::to_string(&crate::command::command_route(vnt))
|
||||
.unwrap_or_else(|e| format!("error {:?}", e)),
|
||||
"list" => serde_yaml::to_string(&crate::command::command_list(vnt))
|
||||
.unwrap_or_else(|e| format!("error {:?}", e)),
|
||||
"info" => serde_yaml::to_string(&crate::command::command_info(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
|
||||
)
|
||||
}
|
||||
};
|
||||
Ok(out_str)
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
use std::io;
|
||||
use std::net::{Ipv4Addr, ToSocketAddrs};
|
||||
use std::str::FromStr;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use vnt::channel::punch::PunchModel;
|
||||
use vnt::channel::UseChannelType;
|
||||
use vnt::cipher::CipherModel;
|
||||
use vnt::core::Config;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(default)]
|
||||
pub struct FileConfig {
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
pub tap: bool,
|
||||
pub token: String,
|
||||
pub device_id: String,
|
||||
pub name: String,
|
||||
pub server_address: String,
|
||||
pub stun_server: Vec<String>,
|
||||
pub in_ips: Vec<String>,
|
||||
pub out_ips: Vec<String>,
|
||||
pub password: Option<String>,
|
||||
pub mtu: Option<u32>,
|
||||
pub tcp: bool,
|
||||
pub ip: Option<String>,
|
||||
pub use_channel: String,
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
pub no_proxy: bool,
|
||||
pub server_encrypt: bool,
|
||||
pub parallel: usize,
|
||||
pub cipher_model: String,
|
||||
pub finger: bool,
|
||||
pub punch_model: String,
|
||||
pub ports: Option<Vec<u16>>,
|
||||
pub cmd: bool,
|
||||
pub first_latency: bool,
|
||||
pub device_name: Option<String>,
|
||||
pub packet_loss: Option<f64>,
|
||||
pub packet_delay: u32,
|
||||
}
|
||||
|
||||
impl Default for FileConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
tap: false,
|
||||
token: "".to_string(),
|
||||
device_id: get_device_id(),
|
||||
name: os_info::get().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.qq.com:3478".to_string(),
|
||||
],
|
||||
in_ips: vec![],
|
||||
out_ips: vec![],
|
||||
password: None,
|
||||
mtu: None,
|
||||
tcp: false,
|
||||
ip: None,
|
||||
use_channel: "all".to_string(),
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
no_proxy: false,
|
||||
server_encrypt: false,
|
||||
parallel: 1,
|
||||
cipher_model: "aes_gcm".to_string(),
|
||||
finger: false,
|
||||
punch_model: "all".to_string(),
|
||||
ports: None,
|
||||
cmd: false,
|
||||
first_latency: false,
|
||||
device_name: None,
|
||||
packet_loss: None,
|
||||
packet_delay: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_config(file_path: &str) -> io::Result<(Config, bool)> {
|
||||
let conf = std::fs::read_to_string(file_path)?;
|
||||
let file_conf = match serde_yaml::from_str::<FileConfig>(&conf) {
|
||||
Ok(val) => val,
|
||||
Err(e) => {
|
||||
log::error!("{:?}", e);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("{}", e)));
|
||||
}
|
||||
};
|
||||
if file_conf.token.is_empty() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "token is_empty"));
|
||||
}
|
||||
let server_address = match file_conf.server_address.to_socket_addrs() {
|
||||
Ok(mut addr) => {
|
||||
if let Some(addr) = addr.next() {
|
||||
addr
|
||||
} else {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("server_address {:?} error", &file_conf.server_address),
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("server_address {:?} error:{}", &file_conf.server_address, e),
|
||||
));
|
||||
}
|
||||
};
|
||||
let in_ips = match common::args_parse::ips_parse(&file_conf.in_ips) {
|
||||
Ok(in_ips) => in_ips,
|
||||
Err(e) => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("in_ips {:?} error:{}", &file_conf.in_ips, e),
|
||||
));
|
||||
}
|
||||
};
|
||||
let out_ips = match common::args_parse::out_ips_parse(&file_conf.out_ips) {
|
||||
Ok(out_ips) => out_ips,
|
||||
Err(e) => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("out_ips {:?} error:{}", &file_conf.out_ips, e),
|
||||
));
|
||||
}
|
||||
};
|
||||
let virtual_ip = match file_conf.ip.clone().map(|v| Ipv4Addr::from_str(&v)) {
|
||||
None => None,
|
||||
Some(r) => Some(r.map_err(|e| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("ip {:?} error:{}", &file_conf.ip, e),
|
||||
)
|
||||
})?),
|
||||
};
|
||||
|
||||
let cipher_model = CipherModel::from_str(&file_conf.cipher_model)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
|
||||
let punch_model = PunchModel::from_str(&file_conf.punch_model)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
let use_channel_type = UseChannelType::from_str(&file_conf.use_channel)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
let config = Config::new(
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
file_conf.tap,
|
||||
file_conf.token,
|
||||
file_conf.device_id,
|
||||
file_conf.name,
|
||||
server_address,
|
||||
file_conf.server_address,
|
||||
file_conf.stun_server,
|
||||
in_ips,
|
||||
out_ips,
|
||||
file_conf.password,
|
||||
file_conf.mtu,
|
||||
file_conf.tcp,
|
||||
virtual_ip,
|
||||
#[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,
|
||||
file_conf.device_name,
|
||||
use_channel_type,
|
||||
file_conf.packet_loss,
|
||||
file_conf.packet_delay,
|
||||
)
|
||||
.unwrap();
|
||||
Ok((config, file_conf.cmd))
|
||||
}
|
||||
|
||||
pub fn get_device_id() -> String {
|
||||
if let Some(id) = common::identifier::get_unique_identifier() {
|
||||
id
|
||||
} else {
|
||||
let path_buf = match crate::app_home() {
|
||||
Ok(path_buf) => path_buf.join("device-id"),
|
||||
Err(e) => {
|
||||
log::warn!("{:?}", e);
|
||||
return String::new();
|
||||
}
|
||||
};
|
||||
if let Ok(id) = std::fs::read_to_string(path_buf.as_path()) {
|
||||
id
|
||||
} else {
|
||||
let id = uuid::Uuid::new_v4().to_string();
|
||||
let _ = std::fs::write(path_buf, &id);
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
use console::{style, Style};
|
||||
|
||||
use crate::command::entity::{DeviceItem, Info, RouteItem};
|
||||
|
||||
pub mod table;
|
||||
|
||||
pub fn console_info(status: Info) {
|
||||
println!("Name: {}", style(status.name).green());
|
||||
println!("Virtual ip: {}", style(status.virtual_ip).green());
|
||||
println!("Virtual gateway: {}", style(status.virtual_gateway).green());
|
||||
println!("Virtual netmask: {}", style(status.virtual_netmask).green());
|
||||
println!(
|
||||
"Connection status: {}",
|
||||
style(status.connect_status).green()
|
||||
);
|
||||
println!("NAT type: {}", style(status.nat_type).green());
|
||||
println!("Relay server: {}", style(status.relay_server).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());
|
||||
}
|
||||
fn convert(num: u64) -> String {
|
||||
let gigabytes = num / (1024 * 1024 * 1024);
|
||||
let remaining_bytes = num % (1024 * 1024 * 1024);
|
||||
let megabytes = remaining_bytes / (1024 * 1024);
|
||||
let remaining_bytes = remaining_bytes % (1024 * 1024);
|
||||
let kilobytes = remaining_bytes / 1024;
|
||||
let remaining_bytes = remaining_bytes % 1024;
|
||||
let mut s = String::new();
|
||||
if gigabytes > 0 {
|
||||
s.push_str(&format!("{} GB ", gigabytes));
|
||||
}
|
||||
if megabytes > 0 {
|
||||
s.push_str(&format!("{} MB ", megabytes));
|
||||
}
|
||||
if kilobytes > 0 {
|
||||
s.push_str(&format!("{} KB ", kilobytes));
|
||||
}
|
||||
if remaining_bytes > 0 {
|
||||
s.push_str(&format!("{} bytes", remaining_bytes));
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
pub fn console_route_table(mut list: Vec<RouteItem>) {
|
||||
if list.is_empty() {
|
||||
println!("No route found");
|
||||
return;
|
||||
}
|
||||
list.sort_by(|t1, t2| t1.destination.cmp(&t2.destination));
|
||||
let mut out_list = Vec::with_capacity(list.len());
|
||||
|
||||
out_list.push(vec![
|
||||
("Destination".to_string(), Style::new()),
|
||||
("Next Hop".to_string(), Style::new()),
|
||||
("Metric".to_string(), Style::new()),
|
||||
("Rt".to_string(), Style::new()),
|
||||
("Interface".to_string(), Style::new()),
|
||||
]);
|
||||
for item in list {
|
||||
out_list.push(vec![
|
||||
(item.destination, Style::new().green()),
|
||||
(item.next_hop, Style::new().green()),
|
||||
(item.metric, Style::new().green()),
|
||||
(item.rt, Style::new().green()),
|
||||
(item.interface, Style::new().green()),
|
||||
]);
|
||||
}
|
||||
|
||||
table::println_table(out_list)
|
||||
}
|
||||
|
||||
pub fn console_device_list(mut list: Vec<DeviceItem>) {
|
||||
if list.is_empty() {
|
||||
println!("No other devices found");
|
||||
return;
|
||||
}
|
||||
list.sort_by(|t1, t2| t1.virtual_ip.cmp(&t2.virtual_ip));
|
||||
list.sort_by(|t1, t2| t1.status.cmp(&t2.status));
|
||||
let mut out_list = Vec::with_capacity(list.len());
|
||||
//表头
|
||||
out_list.push(vec![
|
||||
("Name".to_string(), Style::new()),
|
||||
("Virtual Ip".to_string(), Style::new()),
|
||||
("Status".to_string(), Style::new()),
|
||||
("P2P/Relay".to_string(), Style::new()),
|
||||
("Rt".to_string(), Style::new()),
|
||||
]);
|
||||
for item in list {
|
||||
if &item.status == "Online" {
|
||||
if item.client_secret != item.current_client_secret {
|
||||
//加密状态不一致,无法通信的
|
||||
out_list.push(vec![
|
||||
(item.name, Style::new().red()),
|
||||
(item.virtual_ip, Style::new().red()),
|
||||
(item.status, Style::new().red()),
|
||||
("".to_string(), Style::new().red()),
|
||||
("".to_string(), Style::new().red()),
|
||||
]);
|
||||
} else {
|
||||
if item.nat_traversal_type.contains("p2p") {
|
||||
out_list.push(vec![
|
||||
(item.name, Style::new().green()),
|
||||
(item.virtual_ip, Style::new().green()),
|
||||
(item.status, Style::new().green()),
|
||||
(item.nat_traversal_type, Style::new().green()),
|
||||
(item.rt, Style::new().green()),
|
||||
]);
|
||||
} else {
|
||||
out_list.push(vec![
|
||||
(item.name, Style::new().yellow()),
|
||||
(item.virtual_ip, Style::new().yellow()),
|
||||
(item.status, Style::new().yellow()),
|
||||
(item.nat_traversal_type, Style::new().yellow()),
|
||||
(item.rt, Style::new().yellow()),
|
||||
]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out_list.push(vec![
|
||||
(item.name, Style::new().color256(102)),
|
||||
(item.virtual_ip, Style::new().color256(102)),
|
||||
(item.status, Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
table::println_table(out_list)
|
||||
}
|
||||
|
||||
pub fn console_device_list_all(mut list: Vec<DeviceItem>) {
|
||||
if list.is_empty() {
|
||||
println!("No other devices found");
|
||||
return;
|
||||
}
|
||||
list.sort_by(|t1, t2| t1.virtual_ip.cmp(&t2.virtual_ip));
|
||||
list.sort_by(|t1, t2| t1.status.cmp(&t2.status));
|
||||
let mut out_list = Vec::with_capacity(list.len());
|
||||
//表头
|
||||
out_list.push(vec![
|
||||
("Name".to_string(), Style::new()),
|
||||
("Virtual Ip".to_string(), Style::new()),
|
||||
("Status".to_string(), Style::new()),
|
||||
("P2P/Relay".to_string(), Style::new()),
|
||||
("Rt".to_string(), Style::new()),
|
||||
("NAT Type".to_string(), Style::new()),
|
||||
("Public Ips".to_string(), Style::new()),
|
||||
("Local Ip".to_string(), Style::new()),
|
||||
("IPv6".to_string(), Style::new()),
|
||||
]);
|
||||
for item in list {
|
||||
if &item.status == "Online" {
|
||||
if &item.nat_traversal_type == "p2p" {
|
||||
out_list.push(vec![
|
||||
(item.name, Style::new().green()),
|
||||
(item.virtual_ip, Style::new().green()),
|
||||
(item.status, Style::new().green()),
|
||||
(item.nat_traversal_type, Style::new().green()),
|
||||
(item.rt, Style::new().green()),
|
||||
(item.nat_type, Style::new().green()),
|
||||
(item.public_ips, Style::new().green()),
|
||||
(item.local_ip, Style::new().green()),
|
||||
(item.ipv6, Style::new().green()),
|
||||
]);
|
||||
} else {
|
||||
out_list.push(vec![
|
||||
(item.name, Style::new().yellow()),
|
||||
(item.virtual_ip, Style::new().yellow()),
|
||||
(item.status, Style::new().yellow()),
|
||||
(item.nat_traversal_type, Style::new().yellow()),
|
||||
(item.rt, Style::new().yellow()),
|
||||
(item.nat_type, Style::new().yellow()),
|
||||
(item.public_ips, Style::new().yellow()),
|
||||
(item.local_ip, Style::new().yellow()),
|
||||
(item.ipv6, Style::new().yellow()),
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
out_list.push(vec![
|
||||
(item.name, Style::new().color256(102)),
|
||||
(item.virtual_ip, Style::new().color256(102)),
|
||||
(item.status, Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
table::println_table(out_list)
|
||||
}
|
||||
@@ -7,7 +7,7 @@ pub fn println_table(table: Vec<Vec<(String, Style)>>) {
|
||||
let mut width_list = vec![0; table[0].len()];
|
||||
for in_list in table.iter() {
|
||||
for (index, (item, _)) in in_list.iter().enumerate() {
|
||||
let width = console::measure_text_width(item) + 6;
|
||||
let width = console::measure_text_width(item) + 4;
|
||||
if width_list[index] < width {
|
||||
width_list[index] = width;
|
||||
}
|
||||
@@ -0,0 +1,515 @@
|
||||
use std::net::{Ipv4Addr, ToSocketAddrs};
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::{io, thread};
|
||||
|
||||
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::core::{Config, Vnt};
|
||||
|
||||
mod command;
|
||||
mod config;
|
||||
mod console_out;
|
||||
mod generated_serial_number;
|
||||
mod root_check;
|
||||
|
||||
pub fn app_home() -> io::Result<PathBuf> {
|
||||
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)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _ = log4rs::init_file("log4rs.yaml", Default::default());
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
let program = args[0].clone();
|
||||
let mut opts = Options::new();
|
||||
opts.optopt("k", "", "组网标识", "<token>");
|
||||
opts.optopt("n", "", "设备名称", "<name>");
|
||||
opts.optopt("d", "", "设备标识", "<id>");
|
||||
opts.optflag("c", "", "关闭交互式命令");
|
||||
opts.optopt("s", "", "注册和中继服务器地址", "<server>");
|
||||
opts.optmulti("e", "", "stun服务器", "<stun-server>");
|
||||
opts.optflag("a", "", "使用tap模式");
|
||||
opts.optopt("", "nic", "虚拟网卡名称,windows下使用tap则必填", "<tun0>");
|
||||
opts.optmulti("i", "", "配置点对网(IP代理)入站时使用", "<in-ip>");
|
||||
opts.optmulti("o", "", "配置点对网出站时使用", "<out-ip>");
|
||||
opts.optopt("w", "", "客户端加密", "<password>");
|
||||
opts.optflag("W", "", "服务端加密");
|
||||
opts.optopt("u", "", "自定义mtu(默认为1430)", "<mtu>");
|
||||
opts.optflag("", "tcp", "tcp");
|
||||
opts.optopt("", "ip", "指定虚拟ip", "<ip>");
|
||||
opts.optflag("", "relay", "仅使用服务器转发");
|
||||
opts.optopt("", "par", "任务并行度(必须为正整数)", "<parallel>");
|
||||
opts.optopt("", "model", "加密模式", "<model>");
|
||||
opts.optflag("", "finger", "指纹校验");
|
||||
opts.optopt("", "punch", "取值ipv4/ipv6", "<punch>");
|
||||
opts.optopt("", "ports", "监听的端口", "<port,port>");
|
||||
opts.optflag("", "cmd", "开启窗口输入");
|
||||
opts.optflag("", "no-proxy", "关闭内置代理");
|
||||
opts.optflag("", "first-latency", "优先延迟");
|
||||
opts.optopt("", "use-channel", "使用通道 relay/p2p", "<use-channel>");
|
||||
opts.optopt("", "packet-loss", "丢包率", "<packet-loss>");
|
||||
opts.optopt("", "packet-delay", "延迟", "<packet-delay>");
|
||||
opts.optopt("f", "", "配置文件", "<conf>");
|
||||
//"后台运行时,查看其他设备列表"
|
||||
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());
|
||||
return;
|
||||
}
|
||||
};
|
||||
if matches.opt_present("h") || args.len() == 1 {
|
||||
print_usage(&program, opts);
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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(any(target_os = "windows", target_os = "linux"))]
|
||||
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 server_address = match server_address_str.to_socket_addrs() {
|
||||
Ok(mut addr) => {
|
||||
if let Some(addr) = addr.next() {
|
||||
addr
|
||||
} else {
|
||||
println!("parameter '-s {}' error .", server_address_str);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("parameter '-s {}' error {}.", server_address_str, e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
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.qq.com:3478".to_string());
|
||||
}
|
||||
|
||||
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<String> = 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<String> = 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<String> = 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::<usize>("par").unwrap().unwrap_or(1);
|
||||
if parallel == 0 {
|
||||
println!("'--par {}' invalid", parallel);
|
||||
return;
|
||||
}
|
||||
|
||||
let cipher_model = match matches.opt_get::<CipherModel>("model") {
|
||||
Ok(model) => {
|
||||
#[cfg(not(any(
|
||||
feature = "aes_gcm",
|
||||
feature = "server_encrypt",
|
||||
feature = "aes_cbc",
|
||||
feature = "aes_ecb",
|
||||
feature = "sm4_cbc"
|
||||
)))]
|
||||
{
|
||||
if password.is_some() && model.is_none() {
|
||||
println!("Encryption not supported");
|
||||
return;
|
||||
}
|
||||
}
|
||||
#[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::<PunchModel>("punch")
|
||||
.unwrap()
|
||||
.unwrap_or(PunchModel::All);
|
||||
let use_channel_type = matches
|
||||
.opt_get::<UseChannelType>("use-channel")
|
||||
.unwrap()
|
||||
.unwrap_or_else(|| {
|
||||
if relay {
|
||||
UseChannelType::Relay
|
||||
} else {
|
||||
UseChannelType::All
|
||||
}
|
||||
});
|
||||
|
||||
let ports = matches
|
||||
.opt_get::<String>("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::<f64>("packet-loss")
|
||||
.expect("--packet-loss");
|
||||
let packet_delay = matches
|
||||
.opt_get::<u32>("packet-delay")
|
||||
.expect("--packet-delay")
|
||||
.unwrap_or(0);
|
||||
let config = match Config::new(
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
tap,
|
||||
token,
|
||||
device_id,
|
||||
name,
|
||||
server_address,
|
||||
server_address_str,
|
||||
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,
|
||||
) {
|
||||
Ok(config) => config,
|
||||
Err(e) => {
|
||||
println!("config error: {}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
(config, cmd)
|
||||
};
|
||||
println!("version {}", vnt::VNT_VERSION);
|
||||
println!("Serial:{}", generated_serial_number::SERIAL_NUMBER);
|
||||
main0(config, cmd);
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
mod callback;
|
||||
|
||||
fn main0(config: Config, show_cmd: bool) {
|
||||
let vnt_util = Vnt::new(config, callback::VntHandler {}).unwrap();
|
||||
let vnt_c = vnt_util.clone();
|
||||
thread::Builder::new()
|
||||
.name("CommandServer".into())
|
||||
.spawn(move || {
|
||||
if let Err(e) = command::server::CommandServer::new().start(vnt_c) {
|
||||
log::warn!("cmd:{:?}", e);
|
||||
}
|
||||
})
|
||||
.expect("CommandServer");
|
||||
if show_cmd {
|
||||
let mut cmd = String::new();
|
||||
loop {
|
||||
cmd.clear();
|
||||
println!("======== input:list,info,route,all,stop ========");
|
||||
match io::stdin().read_line(&mut cmd) {
|
||||
Ok(len) => {
|
||||
if !command(&cmd[..len], &vnt_util) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("input err:{}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
vnt_util.wait()
|
||||
}
|
||||
|
||||
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 <token> {}",
|
||||
green("使用相同的token,就能组建一个局域网络".to_string())
|
||||
);
|
||||
println!(" -n <name> 给设备一个名字,便于区分不同设备,默认使用系统版本");
|
||||
println!(" -d <id> 设备唯一标识符,不使用--ip参数时,服务端凭此参数分配虚拟ip,注意不能重复");
|
||||
println!(" -s <server> 注册和中继服务器地址");
|
||||
println!(" -e <stun-server> stun服务器,用于探测NAT类型,可多次指定,如-e addr1 -e addr2");
|
||||
println!(" -a 使用tap模式,默认使用tun模式");
|
||||
println!(" -i <in-ip> 配置点对网(IP代理)时使用,-i 192.168.0.0/24,10.26.0.3表示允许接收网段192.168.0.0/24的数据");
|
||||
println!(" 并转发到10.26.0.3,可指定多个网段");
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
println!(" -o <out-ip> 配置点对网时使用,-o 192.168.0.0/24表示允许将数据转发到192.168.0.0/24,可指定多个网段");
|
||||
#[cfg(not(any(
|
||||
feature = "aes_gcm",
|
||||
feature = "server_encrypt",
|
||||
feature = "aes_cbc",
|
||||
feature = "aes_ecb",
|
||||
feature = "sm4_cbc"
|
||||
)))]
|
||||
let enums = String::new();
|
||||
#[cfg(any(
|
||||
feature = "aes_gcm",
|
||||
feature = "server_encrypt",
|
||||
feature = "aes_cbc",
|
||||
feature = "aes_ecb",
|
||||
feature = "sm4_cbc"
|
||||
))]
|
||||
let mut enums = String::new();
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
enums.push_str("/aes_gcm");
|
||||
#[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");
|
||||
if !enums.is_empty() {
|
||||
println!(" -w <password> 使用该密码生成的密钥对客户端数据进行加密,并且服务端无法解密,使用相同密码的客户端才能通信");
|
||||
}
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
println!(" -W 加密当前客户端和服务端通信的数据,请留意服务端指纹是否正确");
|
||||
println!(" -u <mtu> 自定义mtu(不加密默认为1450,加密默认为1410)");
|
||||
println!(" -f <conf_file> 读取配置文件中的配置");
|
||||
|
||||
println!(" --tcp 和服务端使用tcp通信,默认使用udp,遇到udp qos时可指定使用tcp");
|
||||
println!(" --ip <ip> 指定虚拟ip,指定的ip不能和其他设备重复,必须有效并且在服务端所属网段下,默认情况由服务端分配");
|
||||
println!(" --par <parallel> 任务并行度(必须为正整数),默认值为1");
|
||||
if !enums.is_empty() {
|
||||
println!(
|
||||
" --model <model> 加密模式(默认aes_gcm),可选值{}",
|
||||
&enums[1..]
|
||||
);
|
||||
}
|
||||
if !enums.is_empty() {
|
||||
println!(" --finger 增加数据指纹校验,可增加安全性,如果服务端开启指纹校验,则客户端也必须开启");
|
||||
}
|
||||
println!(" --punch <punch> 取值ipv4/ipv6/all,ipv4表示仅使用ipv4打洞");
|
||||
println!(" --ports <port,port> 取值0~65535,指定本地监听的一组端口,默认监听两个随机端口,使用过多端口会增加网络负担");
|
||||
println!(" --cmd 开启交互式命令,使用此参数开启控制台输入");
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
println!(" --no-proxy 关闭内置代理,如需点对网则需要配置网卡NAT转发");
|
||||
println!(" --first-latency 优先低延迟的通道,默认情况优先使用p2p通道");
|
||||
println!(" --use-channel <p2p> 使用通道 relay/p2p/all,默认两者都使用");
|
||||
println!(" --nic <tun0> 指定虚拟网卡名称");
|
||||
println!(" --packet-loss <0> 模拟丢包,取值0~1之间的小数,程序会按设定的概率主动丢包,可用于模拟弱网");
|
||||
println!(
|
||||
" --packet-delay <0> 模拟延迟,整数,单位毫秒(ms),程序会按设定的值延迟发包,可用于模拟弱网"
|
||||
);
|
||||
|
||||
println!();
|
||||
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()
|
||||
}
|
||||
|
||||
fn yellow(str: String) -> impl std::fmt::Display {
|
||||
style(str).yellow()
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#[cfg(target_os = "windows")]
|
||||
mod windows;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use windows::is_app_elevated;
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
mod unix;
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
pub use unix::is_app_elevated;
|
||||
@@ -0,0 +1,3 @@
|
||||
pub fn is_app_elevated() -> bool {
|
||||
sudo::RunningAs::Root == sudo::check()
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "vnt-jni"
|
||||
version = "1.2.9"
|
||||
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"]}
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib", "cdylib"]
|
||||
@@ -0,0 +1 @@
|
||||
## 提供给安卓端使用
|
||||
@@ -0,0 +1,53 @@
|
||||
package top.wherewego.vnt.jni;
|
||||
|
||||
import top.wherewego.vnt.jni.param.*;
|
||||
|
||||
/**
|
||||
* 回调
|
||||
*
|
||||
* @author https://github.com/lbl8603/vnt
|
||||
*/
|
||||
public interface CallBack {
|
||||
/**
|
||||
* 创建虚拟网卡成功的回调方法
|
||||
*
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* 异常回调
|
||||
*
|
||||
* @param info 错误信息
|
||||
*/
|
||||
void error(ErrorInfo info);
|
||||
|
||||
/**
|
||||
* 服务停止
|
||||
*/
|
||||
void stop();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
package top.wherewego.vnt.jni;
|
||||
|
||||
/**
|
||||
* 启动配置
|
||||
*
|
||||
* @author https://github.com/lbl8603/vnt
|
||||
*/
|
||||
public class Config {
|
||||
/**
|
||||
* 是否是tap模式,仅支持windows和linux
|
||||
*/
|
||||
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;
|
||||
/**
|
||||
* 仅使用中继转发
|
||||
*/
|
||||
private boolean relay;
|
||||
/**
|
||||
* 设备id,请使用唯一值
|
||||
*/
|
||||
private String deviceId;
|
||||
/**
|
||||
* 服务端地址
|
||||
*/
|
||||
private String server;
|
||||
/**
|
||||
* 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;
|
||||
/**
|
||||
* 虚拟网卡fd 仅在android上支持
|
||||
*/
|
||||
private int deviceFd;
|
||||
|
||||
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 boolean isRelay() {
|
||||
return relay;
|
||||
}
|
||||
|
||||
public void setRelay(boolean relay) {
|
||||
this.relay = relay;
|
||||
}
|
||||
|
||||
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[] 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 int getDeviceFd() {
|
||||
return deviceFd;
|
||||
}
|
||||
|
||||
public void setDeviceFd(int deviceFd) {
|
||||
this.deviceFd = deviceFd;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package top.wherewego.vnt.jni;
|
||||
|
||||
/**
|
||||
* @author lubeilin
|
||||
* @date: 2024/02/27 18:31
|
||||
*/
|
||||
public class IpUtils {
|
||||
public static String intToIpAddress(int ipAddress) {
|
||||
|
||||
return ((ipAddress & 0xFF000000) >>> 24) + "." +
|
||||
((ipAddress & 0x00FF0000) >>> 16) + "." +
|
||||
((ipAddress & 0x0000FF00) >>> 8) + "." +
|
||||
(ipAddress & 0x000000FF);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package top.wherewego.vnt.jni;
|
||||
|
||||
/**
|
||||
* 对端设备信息
|
||||
*
|
||||
* @author https://github.com/lbl8603/vnt
|
||||
*/
|
||||
public class PeerDeviceInfo {
|
||||
private final int virtualIp;
|
||||
private final String name;
|
||||
private final String status;
|
||||
private final Route route;
|
||||
|
||||
public PeerDeviceInfo(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 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package top.wherewego.vnt.jni;
|
||||
|
||||
/**
|
||||
* 路由信息
|
||||
*
|
||||
* @author https://github.com/lbl8603/vnt
|
||||
*/
|
||||
public class Route {
|
||||
private final String address;
|
||||
private final byte metric;
|
||||
private final int rt;
|
||||
|
||||
public Route(String address, byte metric, int rt) {
|
||||
this.address = address;
|
||||
this.metric = metric;
|
||||
this.rt = rt;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public byte getMetric() {
|
||||
return metric;
|
||||
}
|
||||
|
||||
public int getRt() {
|
||||
return rt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Route{" +
|
||||
"address='" + address + '\'' +
|
||||
", metric=" + metric +
|
||||
", rt=" + rt +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
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) {
|
||||
this.raw = new0(config, callBack);
|
||||
if(this.raw == 0){
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
stop0(raw);
|
||||
}
|
||||
|
||||
public void await() {
|
||||
wait0(raw);
|
||||
}
|
||||
|
||||
public PeerDeviceInfo[] list() {
|
||||
return list0(raw);
|
||||
}
|
||||
|
||||
private native long new0(Config config, CallBack callBack);
|
||||
|
||||
private native void stop0(long raw);
|
||||
|
||||
private native void wait0(long raw);
|
||||
|
||||
private native void drop0(long raw);
|
||||
|
||||
private native PeerDeviceInfo[] list0(long raw);
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
drop0(raw);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package top.wherewego.vnt.jni.param;
|
||||
|
||||
/**
|
||||
* 网卡信息
|
||||
*
|
||||
* @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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
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) {
|
||||
this.code = switch (code) {
|
||||
case 1 -> ErrorCodeEnum.TokenError;
|
||||
case 2 -> ErrorCodeEnum.Disconnect;
|
||||
case 3 -> ErrorCodeEnum.AddressExhausted;
|
||||
case 4 -> ErrorCodeEnum.IpAlreadyExists;
|
||||
case 5 -> ErrorCodeEnum.InvalidIp;
|
||||
case 6 -> ErrorCodeEnum.Unknown;
|
||||
default -> null;
|
||||
};
|
||||
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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package top.wherewego.vnt.jni.param;
|
||||
|
||||
/**
|
||||
* 注册回调信息
|
||||
*
|
||||
* @author https://github.com/lbl8603/vnt
|
||||
*/
|
||||
public class RegisterInfo {
|
||||
/**
|
||||
* 虚拟IP
|
||||
*/
|
||||
public final String virtualIp;
|
||||
/**
|
||||
* 掩码
|
||||
*/
|
||||
public final String virtualNetmask;
|
||||
/**
|
||||
* 网关
|
||||
*/
|
||||
public final String virtualGateway;
|
||||
|
||||
public RegisterInfo(String virtualIp, String virtualNetmask, String virtualGateway) {
|
||||
this.virtualIp = virtualIp;
|
||||
this.virtualNetmask = virtualNetmask;
|
||||
this.virtualGateway = virtualGateway;
|
||||
}
|
||||
|
||||
public String getVirtualIp() {
|
||||
return virtualIp;
|
||||
}
|
||||
|
||||
public String getVirtualNetmask() {
|
||||
return virtualNetmask;
|
||||
}
|
||||
|
||||
public String getVirtualGateway() {
|
||||
return virtualGateway;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RegisterInfo{" +
|
||||
"virtualIp='" + virtualIp + '\'' +
|
||||
", virtualNetmask='" + virtualNetmask + '\'' +
|
||||
", virtualGateway='" + virtualGateway + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use jni::objects::{GlobalRef, JString, JValue};
|
||||
use jni::{JNIEnv, JavaVM};
|
||||
use spki::der::pem::LineEnding;
|
||||
use spki::EncodePublicKey;
|
||||
|
||||
use vnt::handle::callback::ConnectInfo;
|
||||
use vnt::{DeviceInfo, ErrorInfo, HandshakeInfo, RegisterInfo, VntCallback};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CallBack {
|
||||
jvm: Arc<JavaVM>,
|
||||
this: GlobalRef,
|
||||
}
|
||||
|
||||
unsafe impl Send for CallBack {}
|
||||
|
||||
impl CallBack {
|
||||
pub fn new(jvm: JavaVM, this: GlobalRef) -> Self {
|
||||
Self {
|
||||
jvm: Arc::new(jvm),
|
||||
this,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CallBack {
|
||||
fn create_tun0(&self, info: DeviceInfo) -> jni::errors::Result<()> {
|
||||
let env = &mut self.jvm.attach_current_thread()? as &mut JNIEnv;
|
||||
let param = env.new_object(
|
||||
"top/wherewego/vnt/jni/param/DeviceInfo",
|
||||
"(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 env = &mut self.jvm.attach_current_thread()? as &mut JNIEnv;
|
||||
let param = env.new_object(
|
||||
"top/wherewego/vnt/jni/param/ConnectInfo",
|
||||
"(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<bool> {
|
||||
let env = &mut self.jvm.attach_current_thread()? as &mut JNIEnv;
|
||||
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 param = env.new_object(
|
||||
"top/wherewego/vnt/jni/param/HandshakeInfo",
|
||||
"(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<bool> {
|
||||
let env = &mut self.jvm.attach_current_thread()? as &mut JNIEnv;
|
||||
let param = env.new_object(
|
||||
"top/wherewego/vnt/jni/param/RegisterInfo",
|
||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
|
||||
&[
|
||||
JValue::Object(&env.new_string(info.virtual_ip.to_string())?.into()),
|
||||
JValue::Object(&env.new_string(info.virtual_netmask.to_string())?.into()),
|
||||
JValue::Object(&env.new_string(info.virtual_gateway.to_string())?.into()),
|
||||
],
|
||||
)?;
|
||||
let rs = env.call_method(
|
||||
&self.this,
|
||||
"register",
|
||||
"(Ltop/wherewego/vnt/jni/param/RegisterInfo;)Z",
|
||||
&[JValue::Object(¶m)],
|
||||
)?;
|
||||
rs.z()
|
||||
}
|
||||
fn error0(&self, info: ErrorInfo) -> jni::errors::Result<()> {
|
||||
let code: u8 = info.code.into();
|
||||
let env = &mut self.jvm.attach_current_thread()? as &mut JNIEnv;
|
||||
let msg = if let Some(msg) = info.msg {
|
||||
env.new_string(msg)?
|
||||
} else {
|
||||
JString::default()
|
||||
};
|
||||
let param = env.new_object(
|
||||
"top/wherewego/vnt/jni/param/ErrorInfo",
|
||||
"(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 env = &mut self.jvm.attach_current_thread()? as &mut JNIEnv;
|
||||
env.call_method(&self.this, "error", "()V", &[])?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl VntCallback for CallBack {
|
||||
fn success(&self) {
|
||||
}
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
use std::net::ToSocketAddrs;
|
||||
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::core::Config;
|
||||
|
||||
use crate::utils::*;
|
||||
|
||||
pub fn new_config(env: &mut JNIEnv, config: JObject) -> Result<Config, Error> {
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
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 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 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 server_address = match server_address_str.to_socket_addrs() {
|
||||
Ok(mut rs) => {
|
||||
if let Some(addr) = rs.next() {
|
||||
addr
|
||||
} else {
|
||||
env.throw_new("java/lang/RuntimeException", "server address err")
|
||||
.expect("throw");
|
||||
return Err(Error::JavaException);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
env.throw_new(
|
||||
"java/lang/RuntimeException",
|
||||
format!("server address {}", e),
|
||||
)
|
||||
.expect("throw");
|
||||
return Err(Error::JavaException);
|
||||
}
|
||||
};
|
||||
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")?;
|
||||
#[cfg(target_os = "android")]
|
||||
let device_fd = env.get_field(&config, "deviceFd", "I")?.i()? as i32;
|
||||
let config = match Config::new(
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
tap,
|
||||
token,
|
||||
device_id,
|
||||
name,
|
||||
server_address,
|
||||
server_address_str,
|
||||
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,
|
||||
#[cfg(target_os = "android")]
|
||||
device_fd,
|
||||
UseChannelType::from_str(&use_channel.unwrap_or_default()).unwrap_or_default(),
|
||||
None,
|
||||
0,
|
||||
) {
|
||||
Ok(config) => config,
|
||||
Err(e) => {
|
||||
env.throw_new(
|
||||
"java/lang/RuntimeException",
|
||||
format!("vnt start error {}", e),
|
||||
)
|
||||
.expect("throw");
|
||||
return Err(Error::JavaException);
|
||||
}
|
||||
};
|
||||
Ok(config)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
pub mod callback;
|
||||
pub mod config;
|
||||
pub mod utils;
|
||||
pub mod vnt;
|
||||
@@ -0,0 +1,121 @@
|
||||
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<String, Error> {
|
||||
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<Option<String>, 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<Vec<String>, 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<Option<Vec<String>>, 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<Option<Vec<i32>>, 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<Option<i32>, 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 _
|
||||
));
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
use std::ptr;
|
||||
|
||||
use jni::errors::Error;
|
||||
use jni::objects::{JClass, JObject, JValue};
|
||||
use jni::sys::{jbyte, 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 {
|
||||
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 vnt_util = match Vnt::new(config, CallBack::new(jvm, 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_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/PeerDeviceInfo",
|
||||
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<jobject, Error> {
|
||||
let address = route.addr.to_string();
|
||||
let metric = route.metric;
|
||||
let rt = route.rt;
|
||||
let rs = env.new_object(
|
||||
"top/wherewego/vnt/jni/Route",
|
||||
"(Ljava/lang/String;BI)V",
|
||||
&[
|
||||
JValue::Object(&env.new_string(address)?.into()),
|
||||
JValue::Byte(metric as jbyte),
|
||||
JValue::Int(rt as jint),
|
||||
],
|
||||
)?;
|
||||
Ok(rs.as_raw())
|
||||
}
|
||||
|
||||
fn peer_device_info_parse(
|
||||
env: &mut JNIEnv,
|
||||
peer: PeerDeviceInfo,
|
||||
route: JObject,
|
||||
) -> Result<jobject, Error> {
|
||||
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/PeerDeviceInfo",
|
||||
"(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())
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
[package]
|
||||
name = "vnt"
|
||||
version = "1.2.9"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
tun= {path = "tun"}
|
||||
packet = { path = "./packet" }
|
||||
bytes = "1.5.0"
|
||||
log = "0.4.17"
|
||||
libc = "0.2.137"
|
||||
crossbeam-utils = "0.8"
|
||||
crossbeam-epoch = "0.9.15"
|
||||
parking_lot = "0.12.1"
|
||||
rand = "0.8.5"
|
||||
sha2 = { version = "0.10.6", features = ["oid"] }
|
||||
thiserror = "1.0.37"
|
||||
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 }
|
||||
cbc = {version = "0.1.2",optional = true}
|
||||
ecb = {version = "0.1.2",optional = true}
|
||||
aes = "0.8.3"
|
||||
stun-format = { version = "1.0.1", features = ["fmt", "rfc3489"] }
|
||||
rsa = { version = "0.9.2", features = [] ,optional = true}
|
||||
spki = { version = "0.7.2", features = ["fingerprint", "alloc","base64"] ,optional = true}
|
||||
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"]}
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
libloading = "0.8.0"
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
protobuf-codegen = "3.2.0"
|
||||
protoc-bin-vendored = "3.0.0"
|
||||
|
||||
[features]
|
||||
default = ["server_encrypt","aes_gcm","aes_cbc","aes_ecb","sm4_cbc","ip_proxy"]
|
||||
openssl = ["openssl-sys"]
|
||||
# 从源码编译
|
||||
openssl-vendored = ["openssl-sys/vendored"]
|
||||
ring-cipher = ["ring"]
|
||||
aes_cbc=["cbc"]
|
||||
aes_ecb=["ecb"]
|
||||
sm4_cbc=["libsm"]
|
||||
aes_gcm=["aes-gcm"]
|
||||
server_encrypt =["aes-gcm","rsa","spki"]
|
||||
ip_proxy=[]
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -7,4 +7,3 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1.4.3"
|
||||
thiserror = "1.0.37"
|
||||
@@ -0,0 +1,122 @@
|
||||
use std::{fmt, io};
|
||||
|
||||
/// 地址解析协议,由IP地址找到MAC地址
|
||||
/// https://www.ietf.org/rfc/rfc6747.txt
|
||||
/*
|
||||
0 2 4 5 6 8 10 (字节)
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 硬件类型|协议类型|硬件地址长度|协议地址长度|操作类型|
|
||||
| 源MAC地址 | 源ip地址 |
|
||||
| 目的MAC地址 | 目的ip地址 |
|
||||
*/
|
||||
|
||||
pub struct ArpPacket<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> ArpPacket<B> {
|
||||
pub fn unchecked(buffer: B) -> Self {
|
||||
Self { buffer }
|
||||
}
|
||||
pub fn new(buffer: B) -> io::Result<Self> {
|
||||
if buffer.as_ref().len() != 28 {
|
||||
Err(io::Error::from(io::ErrorKind::InvalidData))?;
|
||||
}
|
||||
let packet = Self::unchecked(buffer);
|
||||
Ok(packet)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> ArpPacket<B> {
|
||||
/// 硬件类型 以太网类型为1
|
||||
pub fn hardware_type(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[0..2].try_into().unwrap())
|
||||
}
|
||||
/// 上层协议类型,ipv4是0x0800
|
||||
pub fn protocol_type(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
|
||||
}
|
||||
/// 如果是MAC地址 则长度为6
|
||||
pub fn hardware_size(&self) -> u8 {
|
||||
self.buffer.as_ref()[4]
|
||||
}
|
||||
/// 如果是IPv4 则长度为4
|
||||
pub fn protocol_size(&self) -> u8 {
|
||||
self.buffer.as_ref()[5]
|
||||
}
|
||||
/// 操作类型,请求和响应 1:ARP请求,2:ARP响应,3:RARP请求,4:RARP响应
|
||||
pub fn op_code(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap())
|
||||
}
|
||||
/// 发送端硬件地址,仅支持以太网
|
||||
pub fn sender_hardware_addr(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[8..14]
|
||||
}
|
||||
/// 发送端协议地址,仅支持IPv4
|
||||
pub fn sender_protocol_addr(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[14..18]
|
||||
}
|
||||
/// 接收端硬件地址,仅支持以太网
|
||||
pub fn target_hardware_addr(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[18..24]
|
||||
}
|
||||
/// 接收端协议地址,仅支持IPv4
|
||||
pub fn target_protocol_addr(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[24..28]
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> ArpPacket<B> {
|
||||
/// 硬件类型 以太网类型为1
|
||||
pub fn set_hardware_type(&mut self, value: u16) {
|
||||
self.buffer.as_mut()[0..2].copy_from_slice(&value.to_be_bytes())
|
||||
}
|
||||
/// 上层协议类型,ipv4是0x0800
|
||||
pub fn set_protocol_type(&mut self, value: u16) {
|
||||
self.buffer.as_mut()[2..4].copy_from_slice(&value.to_be_bytes())
|
||||
}
|
||||
/// 如果是MAC地址 则长度为6
|
||||
pub fn set_hardware_size(&mut self, value: u8) {
|
||||
self.buffer.as_mut()[4] = value
|
||||
}
|
||||
/// 如果是IPv4 则长度为4
|
||||
pub fn set_protocol_size(&mut self, value: u8) {
|
||||
self.buffer.as_mut()[5] = value
|
||||
}
|
||||
/// 操作类型,请求和响应 1:ARP请求,2:ARP响应,3:RARP请求,4:RARP响应
|
||||
pub fn set_op_code(&mut self, value: u16) {
|
||||
self.buffer.as_mut()[6..8].copy_from_slice(&value.to_be_bytes())
|
||||
}
|
||||
/// 发送端硬件地址,仅支持以太网
|
||||
pub fn set_sender_hardware_addr(&mut self, buf: &[u8]) {
|
||||
self.buffer.as_mut()[8..14].copy_from_slice(buf)
|
||||
}
|
||||
/// 发送端协议地址,仅支持IPv4
|
||||
pub fn set_sender_protocol_addr(&mut self, buf: &[u8]) {
|
||||
self.buffer.as_mut()[14..18].copy_from_slice(buf)
|
||||
}
|
||||
/// 接收端硬件地址,仅支持以太网
|
||||
pub fn set_target_hardware_addr(&mut self, buf: &[u8]) {
|
||||
self.buffer.as_mut()[18..24].copy_from_slice(buf)
|
||||
}
|
||||
/// 接收端协议地址,仅支持IPv4
|
||||
pub fn set_target_protocol_addr(&mut self, buf: &[u8]) {
|
||||
self.buffer.as_mut()[24..28].copy_from_slice(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for ArpPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("ArpPacket")
|
||||
.field("hardware_type", &self.hardware_type())
|
||||
.field("protocol_type", &self.protocol_type())
|
||||
.field("hardware_size", &self.hardware_size())
|
||||
.field("protocol_size", &self.protocol_size())
|
||||
.field("op_code", &self.op_code())
|
||||
.field("sender_hardware_addr", &self.sender_hardware_addr())
|
||||
.field("sender_protocol_addr", &self.sender_protocol_addr())
|
||||
.field("target_hardware_addr", &self.target_hardware_addr())
|
||||
.field("target_protocol_addr", &self.target_protocol_addr())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod arp;
|
||||
@@ -0,0 +1,77 @@
|
||||
use crate::ethernet::protocol::Protocol;
|
||||
use std::{fmt, io};
|
||||
|
||||
/// 以太网帧协议
|
||||
/// https://www.ietf.org/rfc/rfc894.txt
|
||||
/*
|
||||
0 6 12 14 (字节)
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 目的地址 | 源地址 | 类型 |
|
||||
*/
|
||||
pub struct EthernetPacket<B> {
|
||||
pub buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> EthernetPacket<B> {
|
||||
pub fn unchecked(buffer: B) -> EthernetPacket<B> {
|
||||
EthernetPacket { buffer }
|
||||
}
|
||||
|
||||
pub fn new(buffer: B) -> io::Result<EthernetPacket<B>> {
|
||||
let packet = EthernetPacket::unchecked(buffer);
|
||||
//头部固定14位
|
||||
if packet.buffer.as_ref().len() < 14 {
|
||||
Err(io::Error::from(io::ErrorKind::InvalidData))?;
|
||||
}
|
||||
|
||||
Ok(packet)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> EthernetPacket<B> {
|
||||
/// 目的MAC地址
|
||||
pub fn destination(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[0..6]
|
||||
}
|
||||
/// 源MAC地址
|
||||
pub fn source(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[6..12]
|
||||
}
|
||||
/// 3层协议
|
||||
pub fn protocol(&self) -> Protocol {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[12..14].try_into().unwrap()).into()
|
||||
}
|
||||
/// 载荷
|
||||
pub fn payload(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[14..]
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> EthernetPacket<B> {
|
||||
pub fn set_destination(&mut self, value: &[u8]) {
|
||||
self.buffer.as_mut()[0..6].copy_from_slice(value);
|
||||
}
|
||||
|
||||
pub fn set_source(&mut self, value: &[u8]) {
|
||||
self.buffer.as_mut()[6..12].copy_from_slice(value);
|
||||
}
|
||||
|
||||
pub fn set_protocol(&mut self, value: Protocol) {
|
||||
let p: u16 = value.into();
|
||||
self.buffer.as_mut()[12..14].copy_from_slice(&p.to_be_bytes())
|
||||
}
|
||||
pub fn payload_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.buffer.as_mut()[14..]
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for EthernetPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("EthernetPacket")
|
||||
.field("destination", &self.destination())
|
||||
.field("source", &self.source())
|
||||
.field("protocol", &self.protocol())
|
||||
.field("payload", &self.payload())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/// 以太网帧协议
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||
pub enum Protocol {
|
||||
///
|
||||
Ipv4,
|
||||
|
||||
///
|
||||
Arp,
|
||||
|
||||
///
|
||||
WakeOnLan,
|
||||
|
||||
///
|
||||
Trill,
|
||||
|
||||
///
|
||||
DecNet,
|
||||
|
||||
///
|
||||
Rarp,
|
||||
|
||||
///
|
||||
AppleTalk,
|
||||
|
||||
///
|
||||
Aarp,
|
||||
|
||||
///
|
||||
Ipx,
|
||||
|
||||
///
|
||||
Qnx,
|
||||
|
||||
///
|
||||
Ipv6,
|
||||
|
||||
///
|
||||
FlowControl,
|
||||
|
||||
///
|
||||
CobraNet,
|
||||
|
||||
///
|
||||
Mpls,
|
||||
|
||||
///
|
||||
MplsMulticast,
|
||||
|
||||
///
|
||||
PppoeDiscovery,
|
||||
|
||||
///
|
||||
PppoeSession,
|
||||
|
||||
///
|
||||
Vlan,
|
||||
|
||||
///
|
||||
PBridge,
|
||||
|
||||
///
|
||||
Lldp,
|
||||
|
||||
///
|
||||
Ptp,
|
||||
|
||||
///
|
||||
Cfm,
|
||||
|
||||
///
|
||||
QinQ,
|
||||
|
||||
///
|
||||
Unknown(u16),
|
||||
}
|
||||
|
||||
impl From<u16> for Protocol {
|
||||
fn from(value: u16) -> Protocol {
|
||||
use self::Protocol::*;
|
||||
|
||||
match value {
|
||||
0x0800 => Ipv4,
|
||||
0x0806 => Arp,
|
||||
0x0842 => WakeOnLan,
|
||||
0x22f3 => Trill,
|
||||
0x6003 => DecNet,
|
||||
0x8035 => Rarp,
|
||||
0x809b => AppleTalk,
|
||||
0x80f3 => Aarp,
|
||||
0x8137 => Ipx,
|
||||
0x8204 => Qnx,
|
||||
0x86dd => Ipv6,
|
||||
0x8808 => FlowControl,
|
||||
0x8819 => CobraNet,
|
||||
0x8847 => Mpls,
|
||||
0x8848 => MplsMulticast,
|
||||
0x8863 => PppoeDiscovery,
|
||||
0x8864 => PppoeSession,
|
||||
0x8100 => Vlan,
|
||||
0x88a8 => PBridge,
|
||||
0x88cc => Lldp,
|
||||
0x88f7 => Ptp,
|
||||
0x8902 => Cfm,
|
||||
0x9100 => QinQ,
|
||||
n => Unknown(n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u16> for Protocol {
|
||||
fn into(self) -> u16 {
|
||||
use self::Protocol::*;
|
||||
|
||||
match self {
|
||||
Ipv4 => 0x0800,
|
||||
Arp => 0x0806,
|
||||
WakeOnLan => 0x0842,
|
||||
Trill => 0x22f3,
|
||||
DecNet => 0x6003,
|
||||
Rarp => 0x8035,
|
||||
AppleTalk => 0x809b,
|
||||
Aarp => 0x80f3,
|
||||
Ipx => 0x8137,
|
||||
Qnx => 0x8204,
|
||||
Ipv6 => 0x86dd,
|
||||
FlowControl => 0x8808,
|
||||
CobraNet => 0x8819,
|
||||
Mpls => 0x8847,
|
||||
MplsMulticast => 0x8848,
|
||||
PppoeDiscovery => 0x8863,
|
||||
PppoeSession => 0x8864,
|
||||
Vlan => 0x8100,
|
||||
PBridge => 0x88a8,
|
||||
Lldp => 0x88cc,
|
||||
Ptp => 0x88f7,
|
||||
Cfm => 0x8902,
|
||||
QinQ => 0x9100,
|
||||
Unknown(n) => n,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::fmt;
|
||||
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
|
||||
use crate::cal_checksum;
|
||||
use crate::error::*;
|
||||
use crate::icmp::{Code, Kind};
|
||||
use crate::ip::ipv4::packet::IpV4Packet;
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use std::{fmt, io};
|
||||
|
||||
/// icmp 协议
|
||||
/* https://www.rfc-editor.org/rfc/rfc792
|
||||
0 1 2 3
|
||||
@@ -17,20 +17,18 @@ use crate::error::*;
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
*/
|
||||
use crate::icmp::{Code, Kind};
|
||||
use crate::ip::ipv4::packet::IpV4Packet;
|
||||
|
||||
pub struct IcmpPacket<B> {
|
||||
buffer: B,
|
||||
pub buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IcmpPacket<B> {
|
||||
pub fn unchecked(buffer: B) -> Self {
|
||||
Self { buffer }
|
||||
}
|
||||
pub fn new(buffer: B) -> Result<Self> {
|
||||
pub fn new(buffer: B) -> io::Result<Self> {
|
||||
if buffer.as_ref().len() < 8 {
|
||||
Err(Error::SmallBuffer)?
|
||||
Err(io::Error::from(io::ErrorKind::InvalidData))?;
|
||||
}
|
||||
let packet = Self::unchecked(buffer);
|
||||
Ok(packet)
|
||||
@@ -56,9 +54,7 @@ impl<B: AsRef<[u8]>> IcmpPacket<B> {
|
||||
Code::from(self.kind(), self.buffer.as_ref()[1])
|
||||
}
|
||||
pub fn checksum(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[2..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
|
||||
}
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0
|
||||
@@ -71,12 +67,8 @@ impl<B: AsRef<[u8]>> IcmpPacket<B> {
|
||||
| Kind::TimestampReply
|
||||
| Kind::InformationRequest
|
||||
| Kind::InformationReply => {
|
||||
let ide = (&self.buffer.as_ref()[4..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap();
|
||||
let seq = (&self.buffer.as_ref()[6..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap();
|
||||
let ide = u16::from_be_bytes(self.buffer.as_ref()[4..6].try_into().unwrap());
|
||||
let seq = u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap());
|
||||
HeaderOther::Identifier(ide, seq)
|
||||
}
|
||||
Kind::DestinationUnreachable | Kind::TimeExceeded | Kind::SourceQuench => {
|
||||
@@ -110,6 +102,7 @@ impl<B: AsRef<[u8]>> IcmpPacket<B> {
|
||||
},
|
||||
Kind::TimestampRequest | Kind::TimestampReply => {
|
||||
let mut buffer = Cursor::new(self.payload());
|
||||
|
||||
Description::Timestamp(
|
||||
buffer.read_u32::<BigEndian>().unwrap(),
|
||||
buffer.read_u32::<BigEndian>().unwrap(),
|
||||
@@ -0,0 +1,117 @@
|
||||
use crate::cal_checksum;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::{fmt, io};
|
||||
|
||||
/// igmp v1
|
||||
/* https://datatracker.ietf.org/doc/html/rfc1112
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|Version| Type | Unused | Checksum |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Group Address |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
/// v1版本的报文
|
||||
pub struct IgmpV1Packet<B> {
|
||||
pub buffer: B,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum IgmpV1Type {
|
||||
/// 0x11 所有组224.0.0.1或者特定组
|
||||
Query,
|
||||
/// 0x12
|
||||
ReportV1,
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for IgmpV1Type {
|
||||
fn from(value: u8) -> IgmpV1Type {
|
||||
use self::IgmpV1Type::*;
|
||||
|
||||
match value {
|
||||
0x11 => Query,
|
||||
0x12 => ReportV1,
|
||||
v => Unknown(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for IgmpV1Type {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
IgmpV1Type::Query => 0x11,
|
||||
IgmpV1Type::ReportV1 => 0x12,
|
||||
IgmpV1Type::Unknown(v) => v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IgmpV1Packet<B> {
|
||||
pub fn unchecked(buffer: B) -> Self {
|
||||
Self { buffer }
|
||||
}
|
||||
pub fn new(buffer: B) -> io::Result<Self> {
|
||||
if buffer.as_ref().len() != 8 {
|
||||
Err(io::Error::from(io::ErrorKind::InvalidData))
|
||||
} else {
|
||||
let packet = Self::unchecked(buffer);
|
||||
Ok(packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IgmpV1Packet<B> {
|
||||
pub fn version(&self) -> u8 {
|
||||
self.buffer.as_ref()[0] >> 4
|
||||
}
|
||||
pub fn igmp_type(&self) -> IgmpV1Type {
|
||||
IgmpV1Type::from(self.buffer.as_ref()[0] & 0x0F)
|
||||
}
|
||||
pub fn unused(&self) -> u8 {
|
||||
self.buffer.as_ref()[1]
|
||||
}
|
||||
pub fn checksum(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
|
||||
}
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0
|
||||
}
|
||||
pub fn group_address(&self) -> Ipv4Addr {
|
||||
let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap();
|
||||
Ipv4Addr::from(tmp)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> IgmpV1Packet<B> {
|
||||
pub fn set_version(&mut self, version: u8) {
|
||||
self.buffer.as_mut()[0] = (version << 4) | 0x0F & self.buffer.as_mut()[0]
|
||||
}
|
||||
pub fn set_type(&mut self, igmp_type: IgmpV1Type) {
|
||||
let t: u8 = igmp_type.into();
|
||||
self.buffer.as_mut()[0] = self.buffer.as_mut()[0] & 0xF0 | t
|
||||
}
|
||||
pub fn set_checksum(&mut self, checksum: u16) {
|
||||
self.buffer.as_mut()[2..4].copy_from_slice(&checksum.to_be_bytes());
|
||||
}
|
||||
pub fn update_checksum(&mut self) {
|
||||
self.set_checksum(0);
|
||||
self.set_checksum(cal_checksum(self.buffer.as_ref()));
|
||||
}
|
||||
pub fn set_group_address(&mut self, group_address: Ipv4Addr) {
|
||||
self.buffer.as_mut()[4..8].copy_from_slice(&group_address.octets());
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for IgmpV1Packet<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("igmp::V1")
|
||||
.field("version", &self.version())
|
||||
.field("type", &self.igmp_type())
|
||||
.field("checksum", &self.checksum())
|
||||
.field("is_valid", &self.is_valid())
|
||||
.field("group_address", &self.group_address())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
use crate::cal_checksum;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::{fmt, io};
|
||||
|
||||
/// igmp v2
|
||||
/* https://www.rfc-editor.org/rfc/rfc2236.html
|
||||
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type | Max Resp Time | Checksum |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Group Address |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
/// v2版本的报文
|
||||
pub struct IgmpV2Packet<B> {
|
||||
pub buffer: B,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum IgmpV2Type {
|
||||
/// 0x11 所有组224.0.0.1或者特定组
|
||||
Query,
|
||||
/// 0x16
|
||||
ReportV2,
|
||||
LeaveV2,
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for IgmpV2Type {
|
||||
fn from(value: u8) -> IgmpV2Type {
|
||||
use self::IgmpV2Type::*;
|
||||
|
||||
match value {
|
||||
0x11 => Query,
|
||||
0x16 => ReportV2,
|
||||
0x17 => LeaveV2,
|
||||
v => Unknown(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for IgmpV2Type {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
IgmpV2Type::Query => 0x11,
|
||||
IgmpV2Type::ReportV2 => 0x16,
|
||||
IgmpV2Type::LeaveV2 => 0x17,
|
||||
IgmpV2Type::Unknown(v) => v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IgmpV2Packet<B> {
|
||||
pub fn unchecked(buffer: B) -> Self {
|
||||
Self { buffer }
|
||||
}
|
||||
pub fn new(buffer: B) -> io::Result<Self> {
|
||||
if buffer.as_ref().len() != 8 {
|
||||
Err(io::Error::from(io::ErrorKind::InvalidData))
|
||||
} else {
|
||||
let packet = Self::unchecked(buffer);
|
||||
Ok(packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IgmpV2Packet<B> {
|
||||
pub fn igmp_type(&self) -> IgmpV2Type {
|
||||
IgmpV2Type::from(self.buffer.as_ref()[0])
|
||||
}
|
||||
pub fn max_resp_time(&self) -> u8 {
|
||||
self.buffer.as_ref()[1]
|
||||
}
|
||||
pub fn checksum(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
|
||||
}
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0
|
||||
}
|
||||
pub fn group_address(&self) -> Ipv4Addr {
|
||||
let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap();
|
||||
Ipv4Addr::from(tmp)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> IgmpV2Packet<B> {
|
||||
pub fn set_type(&mut self, igmp_type: IgmpV2Type) {
|
||||
self.buffer.as_mut()[0] = igmp_type.into()
|
||||
}
|
||||
pub fn set_max_resp_time(&mut self, resp: u8) {
|
||||
self.buffer.as_mut()[1] = resp
|
||||
}
|
||||
pub fn set_checksum(&mut self, checksum: u16) {
|
||||
self.buffer.as_mut()[2..4].copy_from_slice(&checksum.to_be_bytes());
|
||||
}
|
||||
pub fn update_checksum(&mut self) {
|
||||
self.set_checksum(0);
|
||||
self.set_checksum(cal_checksum(self.buffer.as_ref()));
|
||||
}
|
||||
pub fn set_group_address(&mut self, group_address: Ipv4Addr) {
|
||||
self.buffer.as_mut()[4..8].copy_from_slice(&group_address.octets());
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for IgmpV2Packet<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("igmp::V2")
|
||||
.field("type", &self.igmp_type())
|
||||
.field("max_resp_time", &self.max_resp_time())
|
||||
.field("checksum", &self.checksum())
|
||||
.field("is_valid", &self.is_valid())
|
||||
.field("group_address", &self.group_address())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,493 @@
|
||||
use std::net::Ipv4Addr;
|
||||
use std::{fmt, io};
|
||||
|
||||
use crate::cal_checksum;
|
||||
|
||||
/// igmp v3
|
||||
/* https://www.rfc-editor.org/rfc/rfc3376
|
||||
Query:
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type = 0x11 | Max Resp Code | Checksum |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Group Address |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Resv |S| QRV | QQIC | Number of Sources (N) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Source Address [1] |
|
||||
+- -+
|
||||
| Source Address [2] |
|
||||
+- . -+
|
||||
. . .
|
||||
. . .
|
||||
+- -+
|
||||
| Source Address [N] |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
Report:
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type = 0x22 | Reserved | Checksum |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Reserved | Number of Group Records (M) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
. .
|
||||
. Group Record [1] .
|
||||
. .
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
. .
|
||||
. Group Record [2] .
|
||||
. .
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| . |
|
||||
. . .
|
||||
| . |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
. .
|
||||
. Group Record [M] .
|
||||
. .
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
Group Record:
|
||||
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Record Type | Aux Data Len | Number of Sources (N) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Multicast Address |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Source Address [1] |
|
||||
+- -+
|
||||
| Source Address [2] |
|
||||
+- -+
|
||||
. . .
|
||||
. . .
|
||||
. . .
|
||||
+- -+
|
||||
| Source Address [N] |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
. .
|
||||
. Auxiliary Data .
|
||||
. .
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
Record Type:
|
||||
1 MODE_IS_INCLUDE 表示主机希望加入指定组播组并指定了一个或多个源地址
|
||||
2 MODE_IS_EXCLUDE 表示主机希望加入指定组播组但排除了一个或多个源地址
|
||||
3 CHANGE_TO_INCLUDE_MODE 表示主机正在将组播组的过滤模式从排除切换为包括,指定了一个或多个源地址
|
||||
4 CHANGE_TO_EXCLUDE_MODE 表示主机正在将组播组的过滤模式从包括切换为排除,指定了一个或多个源地址
|
||||
5 ALLOW_NEW_SOURCES 表示主机希望在已有的源地址列表中添加新的源地址,指定了一个或多个源地址
|
||||
6 BLOCK_OLD_SOURCES 表示主机希望在已有的源地址列表中删除旧的源地址,指定了一个或多个源地址
|
||||
*/
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum IgmpV3Type {
|
||||
/// 0x11 所有组224.0.0.1或者特定组
|
||||
Query,
|
||||
/// 0x22
|
||||
ReportV3,
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for IgmpV3Type {
|
||||
fn from(value: u8) -> IgmpV3Type {
|
||||
use self::IgmpV3Type::*;
|
||||
|
||||
match value {
|
||||
0x11 => Query,
|
||||
0x22 => ReportV3,
|
||||
v => Unknown(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for IgmpV3Type {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
IgmpV3Type::Query => 0x11,
|
||||
IgmpV3Type::ReportV3 => 0x22,
|
||||
IgmpV3Type::Unknown(v) => v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum IgmpV3RecordType {
|
||||
//1 MODE_IS_INCLUDE 表示主机希望加入指定组播组并指定了一个或多个源地址
|
||||
ModeIsInclude,
|
||||
//2 MODE_IS_EXCLUDE 表示主机希望加入指定组播组但排除了一个或多个源地址
|
||||
ModeIsExclude,
|
||||
//3 CHANGE_TO_INCLUDE_MODE 表示主机正在将组播组的过滤模式从排除切换为包括,指定了一个或多个源地址
|
||||
ChangeToIncludeMode,
|
||||
//4 CHANGE_TO_EXCLUDE_MODE 表示主机正在将组播组的过滤模式从包括切换为排除,指定了一个或多个源地址
|
||||
ChangeToExcludeMode,
|
||||
//5 ALLOW_NEW_SOURCES 表示主机希望在已有的源地址列表中添加新的源地址,指定了一个或多个源地址
|
||||
AllowNewSources,
|
||||
//6 BLOCK_OLD_SOURCES 表示主机希望在已有的源地址列表中删除旧的源地址,指定了一个或多个源地址
|
||||
BlockOldSources,
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for IgmpV3RecordType {
|
||||
fn from(value: u8) -> IgmpV3RecordType {
|
||||
use self::IgmpV3RecordType::*;
|
||||
|
||||
match value {
|
||||
1 => ModeIsInclude,
|
||||
2 => ModeIsExclude,
|
||||
3 => ChangeToIncludeMode,
|
||||
4 => ChangeToExcludeMode,
|
||||
5 => AllowNewSources,
|
||||
6 => BlockOldSources,
|
||||
v => Unknown(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for IgmpV3RecordType {
|
||||
fn into(self) -> u8 {
|
||||
use self::IgmpV3RecordType::*;
|
||||
|
||||
match self {
|
||||
ModeIsInclude => 1,
|
||||
ModeIsExclude => 2,
|
||||
ChangeToIncludeMode => 3,
|
||||
ChangeToExcludeMode => 4,
|
||||
AllowNewSources => 5,
|
||||
BlockOldSources => 6,
|
||||
Unknown(v) => v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// v3版本的query报文
|
||||
pub struct IgmpV3QueryPacket<B> {
|
||||
pub buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IgmpV3QueryPacket<B> {
|
||||
pub fn unchecked(buffer: B) -> Self {
|
||||
Self { buffer }
|
||||
}
|
||||
pub fn new(buffer: B) -> io::Result<Self> {
|
||||
if buffer.as_ref().len() < 12 {
|
||||
Err(io::Error::from(io::ErrorKind::InvalidData))
|
||||
} else {
|
||||
let packet = Self::unchecked(buffer);
|
||||
Ok(packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> IgmpV3QueryPacket<B> {
|
||||
pub fn set_igmp_type(&mut self) {
|
||||
self.buffer.as_mut()[0] = IgmpV3Type::Query.into();
|
||||
}
|
||||
pub fn set_max_resp_code(&mut self, code: u8) {
|
||||
self.buffer.as_mut()[1] = code;
|
||||
}
|
||||
pub fn set_group_address(&mut self, addr: Ipv4Addr) {
|
||||
self.buffer.as_mut()[4..8].copy_from_slice(&addr.octets())
|
||||
}
|
||||
pub fn set_checksum(&mut self, checksum: u16) {
|
||||
self.buffer.as_mut()[2..4].copy_from_slice(&checksum.to_be_bytes())
|
||||
}
|
||||
pub fn set_qrv(&mut self, qrv: u8) {
|
||||
self.buffer.as_mut()[8] = (self.buffer.as_ref()[8] & (!0x07)) | (qrv & 0x07)
|
||||
}
|
||||
pub fn set_qqic(&mut self, qqic: u8) {
|
||||
self.buffer.as_mut()[9] = qqic
|
||||
}
|
||||
|
||||
pub fn update_checksum(&mut self) {
|
||||
self.set_checksum(0);
|
||||
let checksum = cal_checksum(self.buffer.as_ref());
|
||||
self.set_checksum(checksum);
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IgmpV3QueryPacket<B> {
|
||||
pub fn igmp_type(&self) -> IgmpV3Type {
|
||||
IgmpV3Type::from(self.buffer.as_ref()[0])
|
||||
}
|
||||
pub fn max_resp_code(&self) -> u8 {
|
||||
self.buffer.as_ref()[1]
|
||||
}
|
||||
pub fn checksum(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
|
||||
}
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0
|
||||
}
|
||||
pub fn group_address(&self) -> Ipv4Addr {
|
||||
let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap();
|
||||
Ipv4Addr::from(tmp)
|
||||
}
|
||||
/// 保留字段,设置为0
|
||||
pub fn resv(&self) -> u8 {
|
||||
self.buffer.as_ref()[8] >> 4
|
||||
}
|
||||
/// 标志位
|
||||
/// 该比特位为1时,所有收到此查询报文的其他路由器不启动定时器刷新过程,但是此查询报文并不抑制查询者选举过程和路由器的主机侧处理过程;默认未置位。
|
||||
pub fn s(&self) -> u8 {
|
||||
(self.buffer.as_ref()[8] & 0x0F) >> 3
|
||||
}
|
||||
/// 查询者向网络通告的健壮系数
|
||||
/// 此参数可使查询者使用自己的健壮系统同步其他组播路由器的健壮系数;
|
||||
/// 其他路由器接收到查询报文时,如果发现该字段非0,则将自己的健壮系数调整为该字段的值;如果发现该字段为0,则不做处理。默认健壮系数值为2。
|
||||
pub fn qrv(&self) -> u8 {
|
||||
self.buffer.as_ref()[8] & 0x07
|
||||
}
|
||||
/// IGMP查询者的查询间隔
|
||||
/// 非查询者收到查询报文时,如果发现该字段非0,则将自己的查询间隔参数调整为该字段的值:如果发现该字段为0,则不做处理。默认值为60。
|
||||
pub fn qqic(&self) -> u8 {
|
||||
self.buffer.as_ref()[9]
|
||||
}
|
||||
/// 报文中包含的组播源的数量
|
||||
/// 对于普遍组查询报文和特定组查询报文,该字段为0;对于特定源组查询报文,该字段非0
|
||||
pub fn source_number(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[10..12].try_into().unwrap())
|
||||
}
|
||||
pub fn source_addresses(&self) -> Option<Vec<Ipv4Addr>> {
|
||||
let num = self.source_number();
|
||||
if num == 0 {
|
||||
None
|
||||
} else {
|
||||
let num = num as usize;
|
||||
let mut list = Vec::with_capacity(num);
|
||||
let buf = self.buffer.as_ref();
|
||||
let len = buf.len();
|
||||
for index in 0..num {
|
||||
let start = (12 + index * 4) as usize;
|
||||
let end = start + 4;
|
||||
if end > len {
|
||||
return None;
|
||||
}
|
||||
let tmp: [u8; 4] = buf[start..end].try_into().unwrap();
|
||||
list.push(Ipv4Addr::from(tmp));
|
||||
}
|
||||
Some(list)
|
||||
}
|
||||
}
|
||||
pub fn source_address(&self, index: u16) -> Option<Ipv4Addr> {
|
||||
if self.source_number() >= index {
|
||||
None
|
||||
} else {
|
||||
let start = (12 + index * 4) as usize;
|
||||
let end = start + 4;
|
||||
let buf = self.buffer.as_ref();
|
||||
let len = buf.len();
|
||||
if end > len {
|
||||
return None;
|
||||
}
|
||||
let tmp: [u8; 4] = buf[start..end].try_into().unwrap();
|
||||
Some(Ipv4Addr::from(tmp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// v3版本的query报文
|
||||
pub struct IgmpV3ReportPacket<B> {
|
||||
pub buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IgmpV3ReportPacket<B> {
|
||||
pub fn unchecked(buffer: B) -> Self {
|
||||
Self { buffer }
|
||||
}
|
||||
pub fn new(buffer: B) -> io::Result<Self> {
|
||||
if buffer.as_ref().len() < 8 {
|
||||
Err(io::Error::from(io::ErrorKind::InvalidData))
|
||||
} else {
|
||||
let packet = Self::unchecked(buffer);
|
||||
Ok(packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IgmpV3ReportPacket<B> {
|
||||
pub fn igmp_type(&self) -> IgmpV3Type {
|
||||
IgmpV3Type::from(self.buffer.as_ref()[0])
|
||||
}
|
||||
pub fn reserved1(&self) -> u8 {
|
||||
self.buffer.as_ref()[1]
|
||||
}
|
||||
pub fn checksum(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
|
||||
}
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0
|
||||
}
|
||||
pub fn reserved2(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[4..6].try_into().unwrap())
|
||||
}
|
||||
pub fn record_number(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap())
|
||||
}
|
||||
pub fn group_records(&self) -> Option<Vec<IgmpV3RecordPacket<&[u8]>>> {
|
||||
let num = self.record_number();
|
||||
if num == 0 {
|
||||
None
|
||||
} else {
|
||||
let num = num as usize;
|
||||
let mut list = Vec::with_capacity(num);
|
||||
let mut start = 8 as usize;
|
||||
let buf = self.buffer.as_ref();
|
||||
let len = buf.len();
|
||||
for _ in 0..num {
|
||||
if start >= len {
|
||||
return None;
|
||||
}
|
||||
if let Ok(record) = IgmpV3RecordPacket::new(&buf[start..]) {
|
||||
let end = start
|
||||
+ 8
|
||||
+ record.aux_data_len() as usize * 4
|
||||
+ record.source_number() as usize * 4;
|
||||
if end > len {
|
||||
return None;
|
||||
}
|
||||
list.push(IgmpV3RecordPacket::new(&buf[start..end]).unwrap());
|
||||
start = end;
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(list)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// group record
|
||||
pub struct IgmpV3RecordPacket<B> {
|
||||
pub buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IgmpV3RecordPacket<B> {
|
||||
pub fn unchecked(buffer: B) -> Self {
|
||||
Self { buffer }
|
||||
}
|
||||
pub fn new(buffer: B) -> io::Result<Self> {
|
||||
if buffer.as_ref().len() < 8 {
|
||||
Err(io::Error::from(io::ErrorKind::InvalidData))
|
||||
} else {
|
||||
let packet = Self::unchecked(buffer);
|
||||
Ok(packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IgmpV3RecordPacket<B> {
|
||||
pub fn record_type(&self) -> IgmpV3RecordType {
|
||||
IgmpV3RecordType::from(self.buffer.as_ref()[0])
|
||||
}
|
||||
/// 辅助数据长度 以4字节为单位
|
||||
pub fn aux_data_len(&self) -> u8 {
|
||||
self.buffer.as_ref()[1]
|
||||
}
|
||||
/// 源地址数
|
||||
pub fn source_number(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
|
||||
}
|
||||
///多播地址
|
||||
pub fn multicast_address(&self) -> Ipv4Addr {
|
||||
let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap();
|
||||
Ipv4Addr::from(tmp)
|
||||
}
|
||||
pub fn source_addresses(&self) -> Option<Vec<Ipv4Addr>> {
|
||||
let num = self.source_number();
|
||||
if num == 0 {
|
||||
None
|
||||
} else {
|
||||
let num = num as usize;
|
||||
let mut list = Vec::with_capacity(num);
|
||||
let buf = self.buffer.as_ref();
|
||||
let len = buf.len();
|
||||
for index in 0..num {
|
||||
let start = (8 + index * 4) as usize;
|
||||
let end = start + 4;
|
||||
if end > len {
|
||||
return None;
|
||||
}
|
||||
let tmp: [u8; 4] = buf[start..end].try_into().unwrap();
|
||||
list.push(Ipv4Addr::from(tmp));
|
||||
}
|
||||
Some(list)
|
||||
}
|
||||
}
|
||||
pub fn source_address(&self, index: u16) -> Option<Ipv4Addr> {
|
||||
if self.source_number() >= index {
|
||||
None
|
||||
} else {
|
||||
let start = (8 + index * 4) as usize;
|
||||
let end = start + 4;
|
||||
if end > self.buffer.as_ref().len() {
|
||||
return None;
|
||||
}
|
||||
let tmp: [u8; 4] = self.buffer.as_ref()[start..end].try_into().unwrap();
|
||||
Some(Ipv4Addr::from(tmp))
|
||||
}
|
||||
}
|
||||
/// 在文档中没有定义辅助数据的作用,通常应该是空的
|
||||
pub fn auxiliary_data(&self) -> &[u8] {
|
||||
let start = 8 + self.source_number() as usize * 4;
|
||||
let end = start + self.aux_data_len() as usize * 4;
|
||||
if end > self.buffer.as_ref().len() {
|
||||
return &[];
|
||||
}
|
||||
&self.buffer.as_ref()[start..end]
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for IgmpV3QueryPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("igmp::V3Query")
|
||||
.field("type", &self.igmp_type())
|
||||
.field("max_resp_code", &self.max_resp_code())
|
||||
.field("checksum", &self.checksum())
|
||||
.field("is_valid", &self.is_valid())
|
||||
.field("group_address", &self.group_address())
|
||||
.field("s", &self.s())
|
||||
.field("qrv", &self.qrv())
|
||||
.field("qqic", &self.qqic())
|
||||
.field("number of sources", &self.source_number())
|
||||
.field("source_addresses", &self.source_addresses())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for IgmpV3ReportPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("igmp::V3Report")
|
||||
.field("type", &self.igmp_type())
|
||||
.field("reserved1", &self.reserved1())
|
||||
.field("checksum", &self.checksum())
|
||||
.field("is_valid", &self.is_valid())
|
||||
.field("reserved2", &self.reserved2())
|
||||
.field("record_number", &self.record_number())
|
||||
.field("group_records", &self.group_records())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for IgmpV3RecordPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("igmp::V3Record")
|
||||
.field("record_type", &self.record_type())
|
||||
.field("aux_data_len", &self.aux_data_len())
|
||||
.field("source_number", &self.source_number())
|
||||
.field("multicast_address", &self.multicast_address())
|
||||
.field("source_addresses", &self.source_addresses())
|
||||
.field("auxiliary_data", &self.auxiliary_data())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
pub mod igmp_v1;
|
||||
pub mod igmp_v2;
|
||||
pub mod igmp_v3;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum IgmpType {
|
||||
/// 0x11 所有组224.0.0.1或者特定组
|
||||
Query,
|
||||
/// 0x12
|
||||
ReportV1,
|
||||
/// 0x16
|
||||
ReportV2,
|
||||
/// 0x22
|
||||
ReportV3,
|
||||
/// 0x17 目标组固定是 224.0.0.2
|
||||
LeaveV2,
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for IgmpType {
|
||||
fn from(value: u8) -> IgmpType {
|
||||
use self::IgmpType::*;
|
||||
|
||||
match value {
|
||||
0x11 => Query,
|
||||
0x12 => ReportV1,
|
||||
0x16 => ReportV2,
|
||||
0x22 => ReportV3,
|
||||
0x17 => LeaveV2,
|
||||
v => Unknown(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for IgmpType {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
IgmpType::Query => 0x11,
|
||||
IgmpType::ReportV1 => 0x12,
|
||||
IgmpType::ReportV2 => 0x16,
|
||||
IgmpType::ReportV3 => 0x22,
|
||||
IgmpType::LeaveV2 => 0x17,
|
||||
IgmpType::Unknown(v) => v,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod packet;
|
||||
pub mod protocol;
|
||||
@@ -1,10 +1,7 @@
|
||||
use std::fmt;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use std::{fmt, io};
|
||||
|
||||
use crate::cal_checksum;
|
||||
use crate::error::*;
|
||||
use crate::ip::ipv4::protocol::Protocol;
|
||||
|
||||
/// ip协议
|
||||
@@ -39,16 +36,16 @@ impl<B: AsRef<[u8]>> IpV4Packet<B> {
|
||||
pub fn unchecked(buffer: B) -> Self {
|
||||
Self { buffer }
|
||||
}
|
||||
pub fn new(buffer: B) -> Result<Self> {
|
||||
if buffer.as_ref()[0] >> 4 != 4 {
|
||||
Err(Error::Unimplemented)?
|
||||
}
|
||||
pub fn new(buffer: B) -> io::Result<Self> {
|
||||
if buffer.as_ref().len() < 20 {
|
||||
Err(Error::SmallBuffer)?
|
||||
Err(io::Error::new(io::ErrorKind::InvalidData, "len < 20"))?;
|
||||
}
|
||||
if buffer.as_ref()[0] >> 4 != 4 {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidData, "not ipv4"))?;
|
||||
}
|
||||
let packet = Self::unchecked(buffer);
|
||||
if packet.buffer.as_ref().len() < packet.header_len() as usize * 4 {
|
||||
Err(Error::SmallBuffer)?
|
||||
Err(io::Error::new(io::ErrorKind::InvalidData, "head_len err"))?;
|
||||
}
|
||||
Ok(packet)
|
||||
}
|
||||
@@ -60,17 +57,6 @@ impl<B: AsRef<[u8]>> IpV4Packet<B> {
|
||||
}
|
||||
pub fn payload(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[(self.header_len() as usize * 4)..]
|
||||
// match self.protocol() {
|
||||
// Protocol::Udp => {
|
||||
// let udp = UdpPacket::new(IpAddr::V4(self.source_ip()),
|
||||
// IpAddr::V4(self.destination_ip()),
|
||||
// &self.buffer.as_ref()[(self.header_len() as usize * 4)..])?;
|
||||
// Ok(crate::IpUpperLayer::UDP(udp))
|
||||
// }
|
||||
// _ => {
|
||||
// Ok(crate::IpUpperLayer::Unknown(self.buffer.as_ref()));
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,13 +69,18 @@ impl<B: AsRef<[u8]> + AsMut<[u8]>> IpV4Packet<B> {
|
||||
let len = self.header_len() as usize * 4;
|
||||
&mut self.buffer.as_mut()[len..]
|
||||
}
|
||||
|
||||
pub fn set_protocol(&mut self, value: Protocol) {
|
||||
self.header_mut()[9] = value.into();
|
||||
}
|
||||
pub fn set_source_ip(&mut self, value: Ipv4Addr) {
|
||||
self.header_mut()[12..16].copy_from_slice(&value.octets());
|
||||
}
|
||||
pub fn set_destination_ip(&mut self, value: Ipv4Addr) {
|
||||
self.header_mut()[16..20].copy_from_slice(&value.octets());
|
||||
}
|
||||
pub fn set_flags(&mut self, flags: u8) {
|
||||
self.buffer.as_mut()[6] = (self.buffer.as_ref()[6] & 0b11100000) | (flags << 5)
|
||||
}
|
||||
fn set_checksum(&mut self, value: u16) {
|
||||
self.header_mut()[10..12].copy_from_slice(&value.to_be_bytes())
|
||||
}
|
||||
@@ -141,16 +132,12 @@ impl<B: AsRef<[u8]>> IpV4Packet<B> {
|
||||
|
||||
/// ip报总字节数
|
||||
pub fn length(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[2..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
|
||||
}
|
||||
|
||||
/// 标识. ip报文在数据链路层可能会被拆分,同一报文的不同分组标识字段相同
|
||||
pub fn id(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[4..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[4..6].try_into().unwrap())
|
||||
}
|
||||
|
||||
/// 标志 3位.
|
||||
@@ -170,10 +157,7 @@ impl<B: AsRef<[u8]>> IpV4Packet<B> {
|
||||
/// 以字节为单位,用于指明分段起始点相对于包头起始点的偏移量
|
||||
/// 由于分段到达时可能错序,所以分段的偏移字段可以使接收者按照正确的顺序重组数据包
|
||||
pub fn offset(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[6..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
& 0x1fff
|
||||
u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap()) & 0x1fff
|
||||
}
|
||||
|
||||
/// 生存时间.
|
||||
@@ -189,9 +173,7 @@ impl<B: AsRef<[u8]>> IpV4Packet<B> {
|
||||
|
||||
/// 首部校验和
|
||||
pub fn checksum(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[10..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[10..12].try_into().unwrap())
|
||||
}
|
||||
/// 验证校验和
|
||||
///
|
||||
@@ -1,4 +1,4 @@
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Debug)]
|
||||
pub enum Protocol {
|
||||
///
|
||||
Hopopt,
|
||||
@@ -1,6 +1,5 @@
|
||||
use ipv4::packet::IpV4Packet;
|
||||
|
||||
use crate::error::*;
|
||||
use std::io;
|
||||
|
||||
pub mod ipv4;
|
||||
|
||||
@@ -9,10 +8,10 @@ pub enum IpPacket<B> {
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IpPacket<B> {
|
||||
pub fn new(buffer: B) -> Result<Self> {
|
||||
pub fn new(buffer: B) -> io::Result<Self> {
|
||||
match buffer.as_ref()[0] >> 4 {
|
||||
4 => Ok(IpPacket::V4(IpV4Packet::new(buffer)?)),
|
||||
_ => Err(Error::InvalidPacket),
|
||||
_ => Err(io::Error::from(io::ErrorKind::InvalidData)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,13 @@ use std::net::Ipv4Addr;
|
||||
use byteorder::BigEndian;
|
||||
use byteorder::ReadBytesExt;
|
||||
|
||||
pub mod error;
|
||||
pub mod arp;
|
||||
pub mod ethernet;
|
||||
pub mod icmp;
|
||||
pub mod igmp;
|
||||
pub mod ip;
|
||||
pub mod tcp;
|
||||
pub mod udp;
|
||||
|
||||
// pub enum IpUpperLayer<B> {
|
||||
// UDP(UdpPacket<B>),
|
||||
// Unknown(B),
|
||||
@@ -102,9 +103,9 @@ pub fn ipv4_cal_checksum(
|
||||
src_ip: &Ipv4Addr,
|
||||
dest_ip: &Ipv4Addr,
|
||||
protocol: u8,
|
||||
length: u16,
|
||||
) -> u16 {
|
||||
use std::io::Cursor;
|
||||
let length = buffer.len();
|
||||
let mut sum = 0;
|
||||
let src_ip = src_ip.octets();
|
||||
sum += u32c(src_ip[0], src_ip[1]);
|
||||
@@ -1,9 +1,6 @@
|
||||
use std::fmt;
|
||||
use std::net::IpAddr;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::{fmt, io};
|
||||
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
|
||||
use crate::error::*;
|
||||
use crate::tcp::Flags;
|
||||
|
||||
/// tcp
|
||||
@@ -48,59 +45,80 @@ use crate::tcp::Flags;
|
||||
Options+Padding:32位整数倍,最多40个字节
|
||||
*/
|
||||
pub struct TcpPacket<B> {
|
||||
source_ip: IpAddr,
|
||||
destination_ip: IpAddr,
|
||||
source_ip: Ipv4Addr,
|
||||
destination_ip: Ipv4Addr,
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> TcpPacket<B> {
|
||||
pub fn unchecked(source_ip: IpAddr, destination_ip: IpAddr, buffer: B) -> TcpPacket<B> {
|
||||
pub fn unchecked(source_ip: Ipv4Addr, destination_ip: Ipv4Addr, buffer: B) -> TcpPacket<B> {
|
||||
TcpPacket {
|
||||
source_ip,
|
||||
destination_ip,
|
||||
buffer,
|
||||
}
|
||||
}
|
||||
pub fn new(source_ip: IpAddr, destination_ip: IpAddr, buffer: B) -> Result<TcpPacket<B>> {
|
||||
pub fn new(
|
||||
source_ip: Ipv4Addr,
|
||||
destination_ip: Ipv4Addr,
|
||||
buffer: B,
|
||||
) -> io::Result<TcpPacket<B>> {
|
||||
let packet = TcpPacket::unchecked(source_ip, destination_ip, buffer);
|
||||
|
||||
if packet.buffer.as_ref().len() < 20 {
|
||||
Err(Error::SmallBuffer)?
|
||||
Err(io::Error::from(io::ErrorKind::InvalidData))?;
|
||||
}
|
||||
|
||||
if packet.buffer.as_ref().len() < packet.data_offset() as usize * 4 {
|
||||
Err(Error::SmallBuffer)?
|
||||
Err(io::Error::from(io::ErrorKind::InvalidData))?;
|
||||
}
|
||||
|
||||
Ok(packet)
|
||||
}
|
||||
}
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> TcpPacket<B> {
|
||||
pub fn set_source_ip(&mut self, value: Ipv4Addr) {
|
||||
self.source_ip = value;
|
||||
}
|
||||
pub fn set_destination_ip(&mut self, value: Ipv4Addr) {
|
||||
self.destination_ip = value;
|
||||
}
|
||||
}
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> TcpPacket<B> {
|
||||
fn set_checksum(&mut self, value: u16) {
|
||||
self.buffer.as_mut()[16..18].copy_from_slice(&value.to_be_bytes())
|
||||
}
|
||||
pub fn set_source_port(&mut self, value: u16) {
|
||||
self.buffer.as_mut()[0..2].copy_from_slice(&value.to_be_bytes())
|
||||
}
|
||||
pub fn set_destination_port(&mut self, value: u16) {
|
||||
self.buffer.as_mut()[2..4].copy_from_slice(&value.to_be_bytes())
|
||||
}
|
||||
/// 更新校验和
|
||||
pub fn update_checksum(&mut self) {
|
||||
//先将校验和置0
|
||||
self.set_checksum(0);
|
||||
self.set_checksum(self.cal_checksum())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> TcpPacket<B> {
|
||||
/// 源端口
|
||||
pub fn source_port(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[0..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[0..2].try_into().unwrap())
|
||||
}
|
||||
|
||||
/// 目标端口
|
||||
pub fn destination_port(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[2..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
|
||||
}
|
||||
/// 序列号
|
||||
pub fn sequence(&self) -> u32 {
|
||||
(&self.buffer.as_ref()[4..])
|
||||
.read_u32::<BigEndian>()
|
||||
.unwrap()
|
||||
u32::from_be_bytes(self.buffer.as_ref()[4..8].try_into().unwrap())
|
||||
}
|
||||
/// 确认号
|
||||
pub fn acknowledgment(&self) -> u32 {
|
||||
(&self.buffer.as_ref()[8..])
|
||||
.read_u32::<BigEndian>()
|
||||
.unwrap()
|
||||
u32::from_be_bytes(self.buffer.as_ref()[8..12].try_into().unwrap())
|
||||
}
|
||||
/// 数据偏移 4字节为单位
|
||||
pub fn data_offset(&self) -> u8 {
|
||||
@@ -110,14 +128,10 @@ impl<B: AsRef<[u8]>> TcpPacket<B> {
|
||||
Flags(self.buffer.as_ref()[13])
|
||||
}
|
||||
pub fn window(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[14..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[14..16].try_into().unwrap())
|
||||
}
|
||||
pub fn checksum(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[16..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[16..18].try_into().unwrap())
|
||||
}
|
||||
/// 验证校验和,ipv4中为0表示不使用校验和,ipv6校验和不能为0
|
||||
/// TCP/IP协议栈不会自己计算校验和,而是简单地将一个空的校验和字段(零或随机填充)交给网卡硬件。
|
||||
@@ -126,26 +140,15 @@ impl<B: AsRef<[u8]>> TcpPacket<B> {
|
||||
self.checksum() == 0 || self.cal_checksum() == 0
|
||||
}
|
||||
fn cal_checksum(&self) -> u16 {
|
||||
match self.source_ip {
|
||||
IpAddr::V4(src) => {
|
||||
if let IpAddr::V4(dest) = self.destination_ip {
|
||||
return crate::ipv4_cal_checksum(
|
||||
crate::ipv4_cal_checksum(
|
||||
self.buffer.as_ref(),
|
||||
&src,
|
||||
&dest,
|
||||
&self.source_ip,
|
||||
&self.destination_ip,
|
||||
6,
|
||||
self.buffer.as_ref().len() as u16,
|
||||
);
|
||||
}
|
||||
}
|
||||
IpAddr::V6(_src) => {}
|
||||
}
|
||||
unimplemented!()
|
||||
)
|
||||
}
|
||||
pub fn urgent_pointer(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[18..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[18..20].try_into().unwrap())
|
||||
}
|
||||
pub fn options(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[20..(self.data_offset() as usize * 4)]
|
||||
@@ -1,11 +1,5 @@
|
||||
use std::fmt;
|
||||
use std::io::Cursor;
|
||||
use std::net::IpAddr;
|
||||
|
||||
use byteorder::WriteBytesExt;
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
|
||||
use crate::error::*;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::{fmt, io};
|
||||
|
||||
/// udp协议
|
||||
///
|
||||
@@ -53,22 +47,26 @@ RFC 768 https://www.ietf.org/rfc/rfc768.txt
|
||||
*/
|
||||
|
||||
pub struct UdpPacket<B> {
|
||||
source_ip: IpAddr,
|
||||
destination_ip: IpAddr,
|
||||
source_ip: Ipv4Addr,
|
||||
destination_ip: Ipv4Addr,
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> UdpPacket<B> {
|
||||
pub fn unchecked(source_ip: IpAddr, destination_ip: IpAddr, buffer: B) -> UdpPacket<B> {
|
||||
pub fn unchecked(source_ip: Ipv4Addr, destination_ip: Ipv4Addr, buffer: B) -> UdpPacket<B> {
|
||||
UdpPacket {
|
||||
source_ip,
|
||||
destination_ip,
|
||||
buffer,
|
||||
}
|
||||
}
|
||||
pub fn new(source_ip: IpAddr, destination_ip: IpAddr, buffer: B) -> Result<UdpPacket<B>> {
|
||||
pub fn new(
|
||||
source_ip: Ipv4Addr,
|
||||
destination_ip: Ipv4Addr,
|
||||
buffer: B,
|
||||
) -> io::Result<UdpPacket<B>> {
|
||||
if buffer.as_ref().len() < 8 {
|
||||
Err(Error::SmallBuffer)?
|
||||
Err(io::Error::from(io::ErrorKind::InvalidData))?;
|
||||
}
|
||||
let packet = Self::unchecked(source_ip, destination_ip, buffer);
|
||||
Ok(packet)
|
||||
@@ -78,30 +76,22 @@ impl<B: AsRef<[u8]>> UdpPacket<B> {
|
||||
impl<B: AsRef<[u8]>> UdpPacket<B> {
|
||||
/// 源端口
|
||||
pub fn source_port(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[0..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[0..2].try_into().unwrap())
|
||||
}
|
||||
|
||||
/// 目标端口
|
||||
pub fn destination_port(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[2..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
|
||||
}
|
||||
|
||||
/// 总字节数
|
||||
pub fn length(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[4..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[4..6].try_into().unwrap())
|
||||
}
|
||||
|
||||
/// Checksum of the packet.
|
||||
pub fn checksum(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[6..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap())
|
||||
}
|
||||
/// 验证校验和,ipv4中为0表示不使用校验和,ipv6校验和不能为0
|
||||
pub fn is_valid(&self) -> bool {
|
||||
@@ -111,55 +101,38 @@ impl<B: AsRef<[u8]>> UdpPacket<B> {
|
||||
&self.buffer.as_ref()[8..]
|
||||
}
|
||||
fn cal_checksum(&self) -> u16 {
|
||||
match self.source_ip {
|
||||
IpAddr::V4(src) => {
|
||||
if let IpAddr::V4(dest) = self.destination_ip {
|
||||
return crate::ipv4_cal_checksum(
|
||||
crate::ipv4_cal_checksum(
|
||||
self.buffer.as_ref(),
|
||||
&src,
|
||||
&dest,
|
||||
&self.source_ip,
|
||||
&self.destination_ip,
|
||||
17,
|
||||
self.length(),
|
||||
);
|
||||
}
|
||||
}
|
||||
IpAddr::V6(_src) => {}
|
||||
}
|
||||
unimplemented!()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> UdpPacket<B> {
|
||||
fn header_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.buffer.as_mut()[..8]
|
||||
}
|
||||
}
|
||||
// impl<B: AsRef<[u8]> + AsMut<[u8]>> UdpPacket<B> {
|
||||
// fn header_mut(&mut self) -> &mut [u8] {
|
||||
// &mut self.buffer.as_mut()[..8]
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> UdpPacket<B> {
|
||||
/// 设置源端口
|
||||
pub fn set_source_port(&mut self, value: u16) -> &mut Self {
|
||||
Cursor::new(&mut self.header_mut()[0..])
|
||||
.write_u16::<BigEndian>(value)
|
||||
.unwrap();
|
||||
self
|
||||
pub fn set_source_port(&mut self, value: u16) {
|
||||
self.buffer.as_mut()[0..2].copy_from_slice(&value.to_be_bytes())
|
||||
}
|
||||
|
||||
/// 设置目的端口
|
||||
pub fn set_destination_port(&mut self, value: u16) -> &mut Self {
|
||||
Cursor::new(&mut self.header_mut()[2..])
|
||||
.write_u16::<BigEndian>(value)
|
||||
.unwrap();
|
||||
self
|
||||
pub fn set_destination_port(&mut self, value: u16) {
|
||||
self.buffer.as_mut()[2..4].copy_from_slice(&value.to_be_bytes())
|
||||
}
|
||||
fn set_checknum(&mut self, value: u16) {
|
||||
Cursor::new(&mut self.header_mut()[6..])
|
||||
.write_u16::<BigEndian>(value)
|
||||
.unwrap();
|
||||
fn set_checksum(&mut self, value: u16) {
|
||||
self.buffer.as_mut()[6..8].copy_from_slice(&value.to_be_bytes())
|
||||
}
|
||||
pub fn update_checknum(&mut self) {
|
||||
pub fn update_checksum(&mut self) {
|
||||
//先写0
|
||||
self.set_checknum(0);
|
||||
self.set_checknum(self.cal_checksum());
|
||||
self.set_checksum(0);
|
||||
self.set_checksum(self.cal_checksum());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,27 @@
|
||||
syntax = "proto3";
|
||||
message HandshakeRequest{
|
||||
string version = 1;
|
||||
bool secret = 2;
|
||||
}
|
||||
message HandshakeResponse{
|
||||
string version = 1;
|
||||
bool secret = 2;
|
||||
bytes public_key = 3;
|
||||
string key_finger = 4;
|
||||
}
|
||||
message SecretHandshakeRequest{
|
||||
string token = 1;
|
||||
bytes key = 2;
|
||||
}
|
||||
message RegistrationRequest{
|
||||
string token = 1;
|
||||
string device_id = 2;
|
||||
string name = 3;
|
||||
bool is_fast = 4;
|
||||
string version = 5;
|
||||
fixed32 virtual_ip = 6;
|
||||
bool allow_ip_change = 7;
|
||||
bool client_secret = 8;
|
||||
}
|
||||
|
||||
message RegistrationResponse{
|
||||
@@ -14,11 +32,13 @@ message RegistrationResponse{
|
||||
repeated DeviceInfo device_info_list = 5;
|
||||
fixed32 public_ip = 6;
|
||||
uint32 public_port = 7;
|
||||
bytes public_ipv6 = 8;
|
||||
}
|
||||
message DeviceInfo{
|
||||
string name = 1;
|
||||
fixed32 virtual_ip = 2;
|
||||
uint32 device_status = 3;
|
||||
bool client_secret = 4;
|
||||
}
|
||||
|
||||
message DeviceList{
|
||||
@@ -34,6 +54,11 @@ message PunchInfo{
|
||||
bool reply = 6;
|
||||
fixed32 local_ip = 7;
|
||||
uint32 local_port = 8;
|
||||
bytes ipv6 = 9;
|
||||
uint32 ipv6_port = 10;
|
||||
uint32 tcp_port = 11;
|
||||
repeated uint32 udp_ports = 12;
|
||||
repeated uint32 public_ports = 13;
|
||||
}
|
||||
enum PunchNatType{
|
||||
Symmetric = 0;
|
||||
@@ -0,0 +1,527 @@
|
||||
use std::collections::HashMap;
|
||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV6, UdpSocket};
|
||||
use std::ops::Deref;
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{io, thread};
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
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};
|
||||
|
||||
/// 传输通道上下文,持有udp socket、tcp socket和路由信息
|
||||
#[derive(Clone)]
|
||||
pub struct Context {
|
||||
inner: Arc<ContextInner>,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn new(
|
||||
main_udp_socket: Vec<UdpSocket>,
|
||||
use_channel_type: UseChannelType,
|
||||
first_latency: bool,
|
||||
is_tcp: bool,
|
||||
packet_loss_rate: Option<f64>,
|
||||
packet_delay: u32,
|
||||
use_ipv6: bool,
|
||||
) -> Self {
|
||||
let channel_num = main_udp_socket.len();
|
||||
assert_ne!(channel_num, 0, "not channel");
|
||||
let packet_loss_rate = packet_loss_rate
|
||||
.map(|v| {
|
||||
let v = (v * PACKET_LOSS_RATE_DENOMINATOR as f64) as u32;
|
||||
if v > PACKET_LOSS_RATE_DENOMINATOR {
|
||||
PACKET_LOSS_RATE_DENOMINATOR
|
||||
} else {
|
||||
v
|
||||
}
|
||||
})
|
||||
.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)),
|
||||
route_table: RouteTable::new(use_channel_type, first_latency, channel_num),
|
||||
is_tcp,
|
||||
state: AtomicBool::new(true),
|
||||
packet_loss_rate,
|
||||
packet_delay,
|
||||
main_index: AtomicUsize::new(0),
|
||||
use_ipv6,
|
||||
};
|
||||
Self {
|
||||
inner: Arc::new(inner),
|
||||
}
|
||||
}
|
||||
pub fn sender(&self) -> ChannelSender {
|
||||
ChannelSender::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Context {
|
||||
type Target = ContextInner;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
/// 对称网络增加的udp socket数目,有助于增加打洞成功率
|
||||
pub const SYMMETRIC_CHANNEL_NUM: usize = 100;
|
||||
const PACKET_LOSS_RATE_DENOMINATOR: u32 = 100_0000;
|
||||
pub struct ContextInner {
|
||||
// 核心udp socket
|
||||
pub(crate) main_udp_socket: Vec<UdpSocket>,
|
||||
// 对称网络增加的udp socket
|
||||
sub_udp_socket: RwLock<Vec<UdpSocket>>,
|
||||
// tcp数据发送器
|
||||
pub(crate) tcp_map: RwLock<HashMap<SocketAddr, PacketSender>>,
|
||||
// 路由信息
|
||||
pub route_table: RouteTable,
|
||||
// 是否使用tcp连接服务器
|
||||
is_tcp: bool,
|
||||
//状态
|
||||
state: AtomicBool,
|
||||
//控制丢包率,取值v=[0,100_0000] 丢包率r=v/100_0000
|
||||
packet_loss_rate: u32,
|
||||
//控制延迟
|
||||
packet_delay: u32,
|
||||
main_index: AtomicUsize,
|
||||
use_ipv6: bool,
|
||||
}
|
||||
|
||||
impl ContextInner {
|
||||
pub fn use_channel_type(&self) -> UseChannelType {
|
||||
self.route_table.use_channel_type
|
||||
}
|
||||
pub fn is_stop(&self) -> bool {
|
||||
!self.state.load(Ordering::Acquire)
|
||||
}
|
||||
pub fn stop(&self) {
|
||||
self.state.store(false, Ordering::Release);
|
||||
}
|
||||
/// 通过sub_udp_socket是否为空来判断是否为锥形网络
|
||||
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 is_udp_main(&self, route_key: &RouteKey) -> bool {
|
||||
!route_key.is_tcp() && route_key.index < self.main_udp_socket.len()
|
||||
}
|
||||
pub fn first_latency(&self) -> bool {
|
||||
self.route_table.first_latency
|
||||
}
|
||||
/// 切换NAT类型,不同的nat打洞模式会有不同
|
||||
pub fn switch(
|
||||
&self,
|
||||
nat_type: NatType,
|
||||
udp_socket_sender: &AcceptSocketSender<Option<Vec<mio::net::UdpSocket>>>,
|
||||
) -> io::Result<()> {
|
||||
let mut write_guard = self.sub_udp_socket.write();
|
||||
match nat_type {
|
||||
NatType::Symmetric => {
|
||||
if !write_guard.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let mut vec = Vec::with_capacity(SYMMETRIC_CHANNEL_NUM);
|
||||
for _ in 0..SYMMETRIC_CHANNEL_NUM {
|
||||
let udp = UdpSocket::bind("0.0.0.0:0")?;
|
||||
//副通道使用异步io
|
||||
udp.set_nonblocking(true)?;
|
||||
vec.push(udp);
|
||||
}
|
||||
let mut mio_vec = Vec::with_capacity(SYMMETRIC_CHANNEL_NUM);
|
||||
for udp in vec.iter() {
|
||||
let udp_socket = mio::net::UdpSocket::from_std(udp.try_clone()?);
|
||||
mio_vec.push(udp_socket);
|
||||
}
|
||||
udp_socket_sender.try_add_socket(Some(mio_vec))?;
|
||||
*write_guard = vec;
|
||||
}
|
||||
NatType::Cone => {
|
||||
if write_guard.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
udp_socket_sender.try_add_socket(None)?;
|
||||
*write_guard = Vec::new();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn channel_num(&self) -> usize {
|
||||
self.main_udp_socket.len()
|
||||
}
|
||||
/// 获取核心udp监听的端口,用于其他客户端连接
|
||||
pub fn main_local_udp_port(&self) -> io::Result<Vec<u16>> {
|
||||
let mut ports = Vec::new();
|
||||
for udp in self.main_udp_socket.iter() {
|
||||
ports.push(udp.local_addr()?.port())
|
||||
}
|
||||
Ok(ports)
|
||||
}
|
||||
pub fn send_tcp(&self, buf: &[u8], addr: SocketAddr) -> io::Result<()> {
|
||||
if let Some(tcp) = self.tcp_map.read().get(&addr) {
|
||||
tcp.try_send(buf)
|
||||
} else {
|
||||
Err(io::Error::from(io::ErrorKind::NotFound))
|
||||
}
|
||||
}
|
||||
pub fn send_main_udp(&self, index: usize, buf: &[u8], mut addr: SocketAddr) -> io::Result<()> {
|
||||
if self.use_ipv6 {
|
||||
//如果是v4地址则需要转换成v6
|
||||
if let SocketAddr::V4(ipv4) = addr {
|
||||
addr = SocketAddr::V6(SocketAddrV6::new(
|
||||
ipv4.ip().to_ipv6_mapped(),
|
||||
ipv4.port(),
|
||||
0,
|
||||
0,
|
||||
));
|
||||
}
|
||||
}
|
||||
self.main_udp_socket[index].send_to(buf, addr)?;
|
||||
Ok(())
|
||||
}
|
||||
/// 将数据发送到默认通道,一般发往服务器才用此方法
|
||||
pub fn send_default(&self, buf: &[u8], addr: SocketAddr) -> io::Result<()> {
|
||||
if self.is_tcp {
|
||||
//服务端地址只在重连时检测变化
|
||||
self.send_tcp(buf, addr)
|
||||
} else {
|
||||
self.send_main_udp(self.main_index.load(Ordering::Relaxed), buf, addr)
|
||||
}
|
||||
}
|
||||
pub fn change_main_index(&self) {
|
||||
let index = (self.main_index.load(Ordering::Relaxed) + 1) % self.main_udp_socket.len();
|
||||
self.main_index.store(index, Ordering::Relaxed);
|
||||
}
|
||||
/// 此方法仅用于对称网络打洞
|
||||
pub fn try_send_all(&self, buf: &[u8], addr: SocketAddr) {
|
||||
self.try_send_all_main(buf, addr);
|
||||
for udp in self.sub_udp_socket.read().iter() {
|
||||
if let Err(e) = udp.send_to(buf, addr) {
|
||||
log::warn!("{:?},add={:?}", e, addr);
|
||||
}
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
}
|
||||
}
|
||||
pub fn try_send_all_main(&self, buf: &[u8], addr: SocketAddr) {
|
||||
for index in 0..self.channel_num() {
|
||||
if let Err(e) = self.send_main_udp(index, buf, addr) {
|
||||
log::warn!("{:?},add={:?}", e, addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// 发送网络数据
|
||||
pub fn send_ipv4_by_id(
|
||||
&self,
|
||||
buf: &[u8],
|
||||
id: &Ipv4Addr,
|
||||
server_addr: SocketAddr,
|
||||
) -> io::Result<()> {
|
||||
if self.packet_loss_rate > 0 {
|
||||
if rand::thread_rng().gen_ratio(self.packet_loss_rate, PACKET_LOSS_RATE_DENOMINATOR) {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
if self.packet_delay > 0 {
|
||||
thread::sleep(Duration::from_millis(self.packet_delay as _));
|
||||
}
|
||||
//优先发到直连到地址
|
||||
if let Err(e) = self.send_by_id(buf, id) {
|
||||
if e.kind() != io::ErrorKind::NotFound {
|
||||
log::warn!("{}:{:?}", id, e);
|
||||
}
|
||||
if !self.route_table.use_channel_type.is_only_p2p() {
|
||||
//符合条件再发到服务器转发
|
||||
self.send_default(buf, server_addr)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
/// 将数据发到指定id
|
||||
pub fn send_by_id(&self, buf: &[u8], id: &Ipv4Addr) -> io::Result<()> {
|
||||
let mut c = 0;
|
||||
loop {
|
||||
let route = self.route_table.get_route_by_id(c, id)?;
|
||||
return if let Err(e) = self.send_by_key(buf, route.route_key()) {
|
||||
//降低发送速率
|
||||
if e.kind() == io::ErrorKind::WouldBlock {
|
||||
c += 1;
|
||||
if c < 10 {
|
||||
thread::sleep(Duration::from_micros(200));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Err(e)
|
||||
} else {
|
||||
Ok(())
|
||||
};
|
||||
}
|
||||
}
|
||||
/// 将数据发到指定路由
|
||||
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)?;
|
||||
} else {
|
||||
Err(io::Error::from(io::ErrorKind::NotFound))?
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
pub fn remove_route(&self, ip: &Ipv4Addr, route_key: RouteKey) {
|
||||
if self.route_table.remove_route(ip, route_key) {
|
||||
if route_key.is_tcp {
|
||||
if let Some(tcp) = self.tcp_map.write().remove(&route_key.addr) {
|
||||
if let Err(e) = tcp.shutdown() {
|
||||
log::warn!("{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RouteTable {
|
||||
pub(crate) route_table:
|
||||
RwLock<HashMap<Ipv4Addr, (AtomicUsize, Vec<(Route, AtomicCell<Instant>)>)>>,
|
||||
first_latency: bool,
|
||||
channel_num: usize,
|
||||
use_channel_type: UseChannelType,
|
||||
}
|
||||
|
||||
impl RouteTable {
|
||||
fn new(use_channel_type: UseChannelType, first_latency: bool, channel_num: usize) -> Self {
|
||||
Self {
|
||||
route_table: RwLock::new(HashMap::with_capacity(64)),
|
||||
use_channel_type,
|
||||
first_latency,
|
||||
channel_num,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RouteTable {
|
||||
fn get_route_by_id(&self, index: usize, id: &Ipv4Addr) -> io::Result<Route> {
|
||||
if let Some((_count, v)) = self.route_table.read().get(id) {
|
||||
if self.first_latency {
|
||||
if let Some((route, _)) = v.first() {
|
||||
return Ok(*route);
|
||||
}
|
||||
} else {
|
||||
let len = v.len();
|
||||
if len != 0 {
|
||||
return Ok(v[index % len].0);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(io::Error::new(io::ErrorKind::NotFound, "route not found"))
|
||||
}
|
||||
pub fn add_route_if_absent(&self, id: Ipv4Addr, route: Route) {
|
||||
self.add_route_(id, route, true)
|
||||
}
|
||||
pub fn add_route(&self, id: Ipv4Addr, route: Route) {
|
||||
self.add_route_(id, route, false)
|
||||
}
|
||||
fn add_route_(&self, id: Ipv4Addr, route: Route, only_if_absent: bool) {
|
||||
// 限制通道类型
|
||||
match self.use_channel_type {
|
||||
UseChannelType::P2p => {
|
||||
if !route.is_p2p() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let key = route.route_key();
|
||||
let mut route_table = self.route_table.write();
|
||||
let (_, list) = route_table
|
||||
.entry(id)
|
||||
.or_insert_with(|| (AtomicUsize::new(0), Vec::with_capacity(4)));
|
||||
let mut exist = false;
|
||||
for (x, time) in list.iter_mut() {
|
||||
if x.metric < route.metric && !self.first_latency {
|
||||
//非优先延迟的情况下 不能比当前的路径更长
|
||||
return;
|
||||
}
|
||||
if x.route_key() == key {
|
||||
if only_if_absent {
|
||||
return;
|
||||
}
|
||||
x.metric = route.metric;
|
||||
x.rt = route.rt;
|
||||
exist = true;
|
||||
time.store(Instant::now());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if exist {
|
||||
// 这个排序还有待优化,因为后加入的大概率排最后,被直接淘汰的概率也大,可能导致更好的通道被移除了
|
||||
list.sort_by_key(|(k, _)| k.rt);
|
||||
//如果延迟都稳定了,则去除多余通道
|
||||
for (route, _) in list.iter() {
|
||||
if route.rt == DEFAULT_RT {
|
||||
return;
|
||||
}
|
||||
}
|
||||
//延迟优先模式需要更多的通道探测延迟最低的路线
|
||||
let limit_len = if self.first_latency {
|
||||
self.channel_num + 2
|
||||
} else {
|
||||
self.channel_num
|
||||
};
|
||||
self.truncate_(list, limit_len);
|
||||
} else {
|
||||
if !self.first_latency {
|
||||
if route.is_p2p() {
|
||||
//非优先延迟的情况下 添加了直连的则排除非直连的
|
||||
list.retain(|(k, _)| k.is_p2p());
|
||||
}
|
||||
};
|
||||
//增加路由表容量,避免波动
|
||||
let limit_len = self.channel_num * 2;
|
||||
list.sort_by_key(|(k, _)| k.rt);
|
||||
self.truncate_(list, limit_len);
|
||||
list.push((route, AtomicCell::new(Instant::now())));
|
||||
}
|
||||
}
|
||||
fn truncate_(&self, list: &mut Vec<(Route, AtomicCell<Instant>)>, len: usize) {
|
||||
if list.len() <= len {
|
||||
return;
|
||||
}
|
||||
if self.first_latency {
|
||||
//找到第一个p2p通道
|
||||
if let Some(index) =
|
||||
list.iter()
|
||||
.enumerate()
|
||||
.find_map(|(index, (route, _))| if route.is_p2p() { Some(index) } else { None })
|
||||
{
|
||||
if index >= len {
|
||||
//保留第一个p2p通道
|
||||
let route = list.remove(index);
|
||||
list.truncate(len - 1);
|
||||
list.push(route);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
list.truncate(len);
|
||||
}
|
||||
pub fn route(&self, id: &Ipv4Addr) -> Option<Vec<Route>> {
|
||||
if let Some((_, v)) = self.route_table.read().get(id) {
|
||||
Some(v.iter().map(|(i, _)| *i).collect())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn route_one(&self, id: &Ipv4Addr) -> Option<Route> {
|
||||
if let Some((_, v)) = self.route_table.read().get(id) {
|
||||
v.first().map(|(i, _)| *i)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn route_one_p2p(&self, id: &Ipv4Addr) -> Option<Route> {
|
||||
if let Some((_, v)) = self.route_table.read().get(id) {
|
||||
for (i, _) in v {
|
||||
if i.is_p2p() {
|
||||
return Some(*i);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn route_to_id(&self, route_key: &RouteKey) -> Option<Ipv4Addr> {
|
||||
let table = self.route_table.read();
|
||||
for (k, (_, v)) in table.iter() {
|
||||
for (route, _) in v {
|
||||
if &route.route_key() == route_key && route.is_p2p() {
|
||||
return Some(*k);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn need_punch(&self, id: &Ipv4Addr) -> bool {
|
||||
if let Some((_, v)) = self.route_table.read().get(id) {
|
||||
//存在p2p的通道则不再打洞
|
||||
if v.iter().filter(|(k, _)| k.is_p2p()).count() >= 1 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
/// 返回所有路由
|
||||
pub fn route_table(&self) -> Vec<(Ipv4Addr, Vec<Route>)> {
|
||||
let table = self.route_table.read();
|
||||
table
|
||||
.iter()
|
||||
.map(|(k, (_, v))| (k.clone(), v.iter().map(|(i, _)| *i).collect()))
|
||||
.collect()
|
||||
}
|
||||
pub fn route_table_p2p(&self) -> Vec<(Ipv4Addr, Route)> {
|
||||
let table = self.route_table.read();
|
||||
let mut list = Vec::with_capacity(8);
|
||||
for (ip, (_, routes)) in table.iter() {
|
||||
if let Some((route, _)) = routes.first() {
|
||||
if route.is_p2p() {
|
||||
list.push((*ip, *route));
|
||||
}
|
||||
}
|
||||
}
|
||||
list
|
||||
}
|
||||
pub fn route_table_one(&self) -> Vec<(Ipv4Addr, Route)> {
|
||||
let mut list = Vec::with_capacity(8);
|
||||
let table = self.route_table.read();
|
||||
for (k, (_, v)) in table.iter() {
|
||||
if let Some((route, _)) = v.first() {
|
||||
list.push((*k, *route));
|
||||
}
|
||||
}
|
||||
list
|
||||
}
|
||||
pub fn remove_route(&self, id: &Ipv4Addr, route_key: RouteKey) -> bool {
|
||||
let mut write_guard = self.route_table.write();
|
||||
if let Some((_, routes)) = write_guard.get_mut(id) {
|
||||
routes.retain(|(x, _)| x.route_key() != route_key);
|
||||
if routes.is_empty() {
|
||||
write_guard.remove(id);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
/// 更新路由入栈包的时刻,长时间没有收到数据的路由将会被剔除
|
||||
pub fn update_read_time(&self, id: &Ipv4Addr, route_key: &RouteKey) {
|
||||
if let Some((_, routes)) = self.route_table.read().get(id) {
|
||||
for (route, time) in routes {
|
||||
if &route.route_key() == route_key {
|
||||
time.store(Instant::now());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use crate::channel::context::Context;
|
||||
use crate::channel::RouteKey;
|
||||
|
||||
pub trait RecvChannelHandler: Clone + Send + 'static {
|
||||
fn handle(&mut self, buf: &mut [u8], route_key: RouteKey, context: &Context);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
use std::net::Ipv4Addr;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::channel::context::Context;
|
||||
use crate::channel::Route;
|
||||
|
||||
pub struct Idle {
|
||||
read_idle: Duration,
|
||||
context: Context,
|
||||
}
|
||||
|
||||
impl Idle {
|
||||
pub fn new(read_idle: Duration, context: Context) -> Self {
|
||||
Self { read_idle, context }
|
||||
}
|
||||
}
|
||||
|
||||
pub enum IdleType {
|
||||
Timeout(Ipv4Addr, Route),
|
||||
Sleep(Duration),
|
||||
None,
|
||||
}
|
||||
|
||||
impl Idle {
|
||||
/// 获取空闲路由
|
||||
pub fn next_idle(&self) -> IdleType {
|
||||
let mut max = Duration::from_secs(0);
|
||||
let read_guard = self.context.route_table.route_table.read();
|
||||
if read_guard.is_empty() {
|
||||
return IdleType::None;
|
||||
}
|
||||
for (ip, (_, routes)) in read_guard.iter() {
|
||||
for (route, time) in routes {
|
||||
let last_read = time.load().elapsed();
|
||||
if last_read >= self.read_idle {
|
||||
return IdleType::Timeout(*ip, *route);
|
||||
} else if max < last_read {
|
||||
max = last_read;
|
||||
}
|
||||
}
|
||||
}
|
||||
let sleep_time = self.read_idle - max;
|
||||
return IdleType::Sleep(sleep_time);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
use std::io;
|
||||
use std::net::{SocketAddr, UdpSocket};
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::channel::context::Context;
|
||||
use crate::channel::handler::RecvChannelHandler;
|
||||
use crate::channel::sender::AcceptSocketSender;
|
||||
use crate::channel::tcp_channel::tcp_listen;
|
||||
use crate::channel::udp_channel::udp_listen;
|
||||
use crate::util::{io_convert, StopManager};
|
||||
|
||||
pub mod context;
|
||||
pub mod handler;
|
||||
pub mod idle;
|
||||
pub mod notify;
|
||||
pub mod punch;
|
||||
pub mod sender;
|
||||
pub mod tcp_channel;
|
||||
pub mod udp_channel;
|
||||
|
||||
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
|
||||
}
|
||||
pub fn is_only_p2p(&self) -> bool {
|
||||
self == &UseChannelType::P2p
|
||||
}
|
||||
pub fn is_all(&self) -> bool {
|
||||
self == &UseChannelType::All
|
||||
}
|
||||
}
|
||||
impl FromStr for UseChannelType {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.to_lowercase().trim() {
|
||||
"relay" => Ok(UseChannelType::Relay),
|
||||
"p2p" => Ok(UseChannelType::P2p),
|
||||
"all" => Ok(UseChannelType::All),
|
||||
_ => Err(format!("not match '{}', enum: relay/p2p/all", s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Default for UseChannelType {
|
||||
fn default() -> Self {
|
||||
UseChannelType::All
|
||||
}
|
||||
}
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub enum Status {
|
||||
Cone,
|
||||
Symmetric,
|
||||
Close,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Route {
|
||||
pub is_tcp: bool,
|
||||
index: usize,
|
||||
pub addr: SocketAddr,
|
||||
pub metric: u8,
|
||||
pub rt: i64,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct RouteSortKey {
|
||||
pub metric: u8,
|
||||
pub rt: i64,
|
||||
}
|
||||
const DEFAULT_RT: i64 = 999;
|
||||
impl Route {
|
||||
pub fn new(is_tcp: bool, index: usize, addr: SocketAddr, metric: u8, rt: i64) -> Self {
|
||||
Self {
|
||||
is_tcp,
|
||||
index,
|
||||
addr,
|
||||
metric,
|
||||
rt,
|
||||
}
|
||||
}
|
||||
pub fn from(route_key: RouteKey, metric: u8, rt: i64) -> Self {
|
||||
Self {
|
||||
is_tcp: route_key.is_tcp,
|
||||
index: route_key.index,
|
||||
addr: route_key.addr,
|
||||
metric,
|
||||
rt,
|
||||
}
|
||||
}
|
||||
pub fn from_default_rt(route_key: RouteKey, metric: u8) -> Self {
|
||||
Self {
|
||||
is_tcp: route_key.is_tcp,
|
||||
index: route_key.index,
|
||||
addr: route_key.addr,
|
||||
metric,
|
||||
rt: DEFAULT_RT,
|
||||
}
|
||||
}
|
||||
pub fn route_key(&self) -> RouteKey {
|
||||
RouteKey {
|
||||
is_tcp: self.is_tcp,
|
||||
index: self.index,
|
||||
addr: self.addr,
|
||||
}
|
||||
}
|
||||
pub fn sort_key(&self) -> RouteSortKey {
|
||||
RouteSortKey {
|
||||
metric: self.metric,
|
||||
rt: self.rt,
|
||||
}
|
||||
}
|
||||
pub fn is_p2p(&self) -> bool {
|
||||
self.metric == 1
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct RouteKey {
|
||||
is_tcp: bool,
|
||||
index: usize,
|
||||
pub addr: SocketAddr,
|
||||
}
|
||||
|
||||
impl RouteKey {
|
||||
pub(crate) fn new(is_tcp: bool, index: usize, addr: SocketAddr) -> Self {
|
||||
Self {
|
||||
is_tcp,
|
||||
index,
|
||||
addr,
|
||||
}
|
||||
}
|
||||
pub fn is_tcp(&self) -> bool {
|
||||
self.is_tcp
|
||||
}
|
||||
pub fn index(&self) -> usize {
|
||||
self.index
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_context(
|
||||
ports: Vec<u16>,
|
||||
use_channel_type: UseChannelType,
|
||||
first_latency: bool,
|
||||
is_tcp: bool,
|
||||
packet_loss_rate: Option<f64>,
|
||||
packet_delay: u32,
|
||||
) -> io::Result<(Context, mio::net::TcpListener)> {
|
||||
assert!(!ports.is_empty(), "not channel");
|
||||
let mut udps = Vec::with_capacity(ports.len());
|
||||
//检查系统是否支持ipv6
|
||||
let use_ipv6 = match socket2::Socket::new(socket2::Domain::IPV6, socket2::Type::DGRAM, None) {
|
||||
Ok(_) => true,
|
||||
Err(e) => {
|
||||
log::warn!("{:?}", e);
|
||||
false
|
||||
}
|
||||
};
|
||||
for port in &ports {
|
||||
//监听v6+v4双栈
|
||||
let (socket, address) = if use_ipv6 {
|
||||
let address: SocketAddr = format!("[::]:{}", port).parse().unwrap();
|
||||
let socket = socket2::Socket::new(socket2::Domain::IPV6, socket2::Type::DGRAM, None)?;
|
||||
io_convert(socket.set_only_v6(false), |_| {
|
||||
format!("set_only_v6 failed: {}", &address)
|
||||
})?;
|
||||
(socket, address)
|
||||
} else {
|
||||
let address: SocketAddr = format!("0.0.0.0:{}", port).parse().unwrap();
|
||||
(
|
||||
socket2::Socket::new(socket2::Domain::IPV4, socket2::Type::DGRAM, None)?,
|
||||
address,
|
||||
)
|
||||
};
|
||||
|
||||
io_convert(socket.set_reuse_address(true), |_| {
|
||||
format!("set_reuse_address failed: {}", &address)
|
||||
})?;
|
||||
io_convert(socket.set_send_buffer_size(2 * 1024 * 1024), |_| {
|
||||
format!("set_send_buffer_size failed: {}", &address)
|
||||
})?;
|
||||
io_convert(socket.set_recv_buffer_size(2 * 1024 * 1024), |_| {
|
||||
format!("set_recv_buffer_size failed: {}", &address)
|
||||
})?;
|
||||
io_convert(socket.bind(&address.into()), |_| {
|
||||
format!("bind failed: {}", &address)
|
||||
})?;
|
||||
let main_channel: UdpSocket = socket.into();
|
||||
main_channel.set_nonblocking(true)?;
|
||||
udps.push(main_channel);
|
||||
}
|
||||
let context = Context::new(
|
||||
udps,
|
||||
use_channel_type,
|
||||
first_latency,
|
||||
is_tcp,
|
||||
packet_loss_rate,
|
||||
packet_delay,
|
||||
use_ipv6,
|
||||
);
|
||||
|
||||
let port = context.main_local_udp_port()?[0];
|
||||
//监听v6+v4双栈,tcp通道使用异步io
|
||||
let (socket, address) = if use_ipv6 {
|
||||
let address: SocketAddr = format!("[::]:{}", port).parse().unwrap();
|
||||
let socket = socket2::Socket::new(socket2::Domain::IPV6, socket2::Type::STREAM, None)?;
|
||||
io_convert(socket.set_only_v6(false), |_| {
|
||||
format!("set_only_v6 failed: {}", &address)
|
||||
})?;
|
||||
(socket, address)
|
||||
} else {
|
||||
let address: SocketAddr = format!("0.0.0.0:{}", port).parse().unwrap();
|
||||
let socket = socket2::Socket::new(socket2::Domain::IPV4, socket2::Type::STREAM, None)?;
|
||||
(socket, address)
|
||||
};
|
||||
|
||||
io_convert(socket.set_reuse_address(true), |_| {
|
||||
format!("set_reuse_address failed: {}", &address)
|
||||
})?;
|
||||
if let Err(e) = socket.bind(&address.into()) {
|
||||
if ports[0] == 0 {
|
||||
//端口可能冲突,则使用任意端口
|
||||
log::warn!("监听tcp端口失败 {:?},重试一次", address);
|
||||
let address: SocketAddr = if use_ipv6 {
|
||||
format!("[::]:{}", 0).parse().unwrap()
|
||||
} else {
|
||||
format!("0.0.0.0:{}", port).parse().unwrap()
|
||||
};
|
||||
io_convert(socket.bind(&address.into()), |_| {
|
||||
format!("bind failed: {}", &address)
|
||||
})?;
|
||||
} else {
|
||||
//手动指定的ip,直接报错
|
||||
io_convert(Err(e), |_| format!("bind failed: {}", &address))?;
|
||||
}
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
pub fn init_channel<H>(
|
||||
tcp_listener: mio::net::TcpListener,
|
||||
context: Context,
|
||||
stop_manager: StopManager,
|
||||
recv_handler: H,
|
||||
) -> io::Result<(
|
||||
AcceptSocketSender<Option<Vec<mio::net::UdpSocket>>>,
|
||||
AcceptSocketSender<(mio::net::TcpStream, SocketAddr, Option<Vec<u8>>)>,
|
||||
)>
|
||||
where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
// 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_listener,
|
||||
stop_manager.clone(),
|
||||
recv_handler.clone(),
|
||||
context.clone(),
|
||||
)?;
|
||||
|
||||
Ok((udp_socket_sender, tcp_socket_sender))
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
use mio::{Token, Waker};
|
||||
use parking_lot::Mutex;
|
||||
use std::io;
|
||||
use std::ops::Deref;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WritableNotify {
|
||||
inner: Arc<WritableNotifyInner>,
|
||||
}
|
||||
|
||||
impl WritableNotify {
|
||||
pub fn new(waker: Waker) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(WritableNotifyInner {
|
||||
waker,
|
||||
state: AtomicUsize::new(0),
|
||||
tokens: Mutex::new(Vec::with_capacity(8)),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for WritableNotify {
|
||||
type Target = WritableNotifyInner;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WritableNotifyInner {
|
||||
waker: Waker,
|
||||
state: AtomicUsize,
|
||||
tokens: Mutex<Vec<(Token, bool)>>,
|
||||
}
|
||||
|
||||
impl WritableNotifyInner {
|
||||
pub fn notify(&self, token: Token, state: bool) -> io::Result<()> {
|
||||
{
|
||||
let mut guard = self.tokens.lock();
|
||||
if guard.is_empty() || !guard.contains(&(token, state)) {
|
||||
guard.push((token, state));
|
||||
}
|
||||
drop(guard);
|
||||
}
|
||||
self.need_write()
|
||||
}
|
||||
|
||||
pub fn stop(&self) -> io::Result<()> {
|
||||
self.state.store(0b001, Ordering::Release);
|
||||
self.waker.wake()
|
||||
}
|
||||
pub fn need_write(&self) -> io::Result<()> {
|
||||
self.state.fetch_or(0b010, Ordering::AcqRel);
|
||||
self.waker.wake()
|
||||
}
|
||||
pub fn add_socket(&self) -> io::Result<()> {
|
||||
self.state.fetch_or(0b100, Ordering::AcqRel);
|
||||
self.waker.wake()
|
||||
}
|
||||
pub fn take_all(&self) -> Option<Vec<(Token, bool)>> {
|
||||
let mut guard = self.tokens.lock();
|
||||
if guard.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(guard.drain(..).collect())
|
||||
}
|
||||
}
|
||||
pub fn is_stop(&self) -> bool {
|
||||
self.state.load(Ordering::Acquire) & 0b001 == 0b001
|
||||
}
|
||||
pub fn is_need_write(&self) -> bool {
|
||||
self.state.fetch_and(!0b010, Ordering::AcqRel) & 0b010 == 0b010
|
||||
}
|
||||
pub fn is_add_socket(&self) -> bool {
|
||||
self.state.fetch_and(!0b100, Ordering::AcqRel) & 0b100 == 0b100
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AcceptNotify {
|
||||
inner: Arc<AcceptNotifyInner>,
|
||||
}
|
||||
|
||||
impl AcceptNotify {
|
||||
pub fn new(waker: Waker) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(AcceptNotifyInner {
|
||||
waker,
|
||||
state: AtomicUsize::new(0),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for AcceptNotify {
|
||||
type Target = AcceptNotifyInner;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AcceptNotifyInner {
|
||||
waker: Waker,
|
||||
state: AtomicUsize,
|
||||
}
|
||||
|
||||
impl AcceptNotifyInner {
|
||||
pub fn is_stop(&self) -> bool {
|
||||
self.state.load(Ordering::Acquire) & 0b001 == 0b001
|
||||
}
|
||||
pub fn is_add_socket(&self) -> bool {
|
||||
self.state.fetch_and(!0b100, Ordering::AcqRel) & 0b100 == 0b100
|
||||
}
|
||||
pub fn stop(&self) -> io::Result<()> {
|
||||
self.state.store(0b001, Ordering::Release);
|
||||
self.waker.wake()
|
||||
}
|
||||
pub fn add_socket(&self) -> io::Result<()> {
|
||||
self.state.fetch_or(0b100, Ordering::AcqRel);
|
||||
self.waker.wake()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,367 @@
|
||||
use std::collections::HashMap;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
use std::{io, thread};
|
||||
|
||||
use mio::net::TcpStream;
|
||||
use rand::prelude::SliceRandom;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::channel::context::Context;
|
||||
use crate::channel::sender::AcceptSocketSender;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
pub enum PunchModel {
|
||||
IPv4,
|
||||
IPv6,
|
||||
All,
|
||||
}
|
||||
|
||||
impl FromStr for PunchModel {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.to_lowercase().trim() {
|
||||
"ipv4" => Ok(PunchModel::IPv4),
|
||||
"ipv6" => Ok(PunchModel::IPv6),
|
||||
"all" => Ok(PunchModel::All),
|
||||
_ => Err(format!("not match '{}', enum: ipv4/ipv6/all", s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PunchModel {
|
||||
fn default() -> Self {
|
||||
PunchModel::All
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NatInfo {
|
||||
pub public_ips: Vec<Ipv4Addr>,
|
||||
pub public_ports: Vec<u16>,
|
||||
pub public_port_range: u16,
|
||||
pub nat_type: NatType,
|
||||
pub(crate) local_ipv4: Option<Ipv4Addr>,
|
||||
pub(crate) ipv6: Option<Ipv6Addr>,
|
||||
pub(crate) udp_ports: Vec<u16>,
|
||||
pub tcp_port: u16,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum NatType {
|
||||
Symmetric,
|
||||
Cone,
|
||||
}
|
||||
|
||||
impl NatInfo {
|
||||
pub fn new(
|
||||
mut public_ips: Vec<Ipv4Addr>,
|
||||
public_ports: Vec<u16>,
|
||||
public_port_range: u16,
|
||||
mut local_ipv4: Option<Ipv4Addr>,
|
||||
mut ipv6: Option<Ipv6Addr>,
|
||||
udp_ports: Vec<u16>,
|
||||
tcp_port: u16,
|
||||
mut nat_type: NatType,
|
||||
) -> Self {
|
||||
public_ips.retain(|ip| {
|
||||
!ip.is_multicast()
|
||||
&& !ip.is_broadcast()
|
||||
&& !ip.is_unspecified()
|
||||
&& !ip.is_loopback()
|
||||
&& !ip.is_private()
|
||||
});
|
||||
if public_ips.len() > 1 {
|
||||
nat_type = NatType::Symmetric;
|
||||
}
|
||||
if let Some(ip) = local_ipv4 {
|
||||
if ip.is_multicast() || ip.is_broadcast() || ip.is_unspecified() || ip.is_loopback() {
|
||||
local_ipv4 = None
|
||||
}
|
||||
}
|
||||
if let Some(ip) = ipv6 {
|
||||
if ip.is_multicast() || ip.is_unspecified() || ip.is_loopback() {
|
||||
ipv6 = None
|
||||
}
|
||||
}
|
||||
Self {
|
||||
public_ips,
|
||||
public_ports,
|
||||
public_port_range,
|
||||
local_ipv4,
|
||||
ipv6,
|
||||
udp_ports,
|
||||
tcp_port,
|
||||
nat_type,
|
||||
}
|
||||
}
|
||||
pub fn update_addr(&mut self, index: usize, ip: Ipv4Addr, port: u16) {
|
||||
if port != 0 {
|
||||
if let Some(public_port) = self.public_ports.get_mut(index) {
|
||||
*public_port = port;
|
||||
}
|
||||
}
|
||||
if !ip.is_multicast()
|
||||
&& !ip.is_broadcast()
|
||||
&& !ip.is_unspecified()
|
||||
&& !ip.is_loopback()
|
||||
&& !ip.is_private()
|
||||
{
|
||||
if !self.public_ips.contains(&ip) {
|
||||
self.public_ips.push(ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn local_ipv4(&self) -> Option<Ipv4Addr> {
|
||||
self.local_ipv4
|
||||
}
|
||||
pub fn ipv6(&self) -> Option<Ipv6Addr> {
|
||||
self.ipv6
|
||||
}
|
||||
pub fn local_udp_ipv4addr(&self, index: usize) -> Option<SocketAddr> {
|
||||
let len = self.udp_ports.len();
|
||||
if len == 0 {
|
||||
return None;
|
||||
}
|
||||
if let Some(local_ipv4) = self.local_ipv4 {
|
||||
Some(SocketAddr::V4(SocketAddrV4::new(
|
||||
local_ipv4,
|
||||
self.udp_ports[index % len],
|
||||
)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn local_udp_ipv6addr(&self, index: usize) -> Option<SocketAddr> {
|
||||
let len = self.udp_ports.len();
|
||||
if len == 0 {
|
||||
return None;
|
||||
}
|
||||
if let Some(ipv6) = self.ipv6 {
|
||||
Some(SocketAddr::V6(SocketAddrV6::new(
|
||||
ipv6,
|
||||
self.udp_ports[index % len],
|
||||
0,
|
||||
0,
|
||||
)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn local_tcp_ipv6addr(&self) -> Option<SocketAddr> {
|
||||
if self.tcp_port == 0 {
|
||||
return None;
|
||||
}
|
||||
if let Some(ipv6) = self.ipv6 {
|
||||
Some(SocketAddr::V6(SocketAddrV6::new(ipv6, self.tcp_port, 0, 0)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn local_tcp_ipv4addr(&self) -> Option<SocketAddr> {
|
||||
if self.tcp_port == 0 {
|
||||
return None;
|
||||
}
|
||||
if let Some(ipv4) = self.local_ipv4 {
|
||||
Some(SocketAddr::V4(SocketAddrV4::new(ipv4, self.tcp_port)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Punch {
|
||||
context: Context,
|
||||
port_vec: Vec<u16>,
|
||||
port_index: HashMap<Ipv4Addr, usize>,
|
||||
punch_model: PunchModel,
|
||||
is_tcp: bool,
|
||||
tcp_socket_sender: AcceptSocketSender<(TcpStream, SocketAddr, Option<Vec<u8>>)>,
|
||||
}
|
||||
|
||||
impl Punch {
|
||||
pub fn new(
|
||||
context: Context,
|
||||
punch_model: PunchModel,
|
||||
is_tcp: bool,
|
||||
tcp_socket_sender: AcceptSocketSender<(TcpStream, SocketAddr, Option<Vec<u8>>)>,
|
||||
) -> Self {
|
||||
let mut port_vec: Vec<u16> = (1..65535).collect();
|
||||
port_vec.push(65535);
|
||||
let mut rng = rand::thread_rng();
|
||||
port_vec.shuffle(&mut rng);
|
||||
Punch {
|
||||
context,
|
||||
port_vec,
|
||||
port_index: HashMap::new(),
|
||||
punch_model,
|
||||
is_tcp,
|
||||
tcp_socket_sender,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Punch {
|
||||
fn connect_tcp(&self, buf: &[u8], addr: SocketAddr) -> bool {
|
||||
// mio是非阻塞的,不能立马判断是否能连接成功,所以用标准库的tcp
|
||||
match std::net::TcpStream::connect_timeout(&addr, Duration::from_secs(3)) {
|
||||
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
|
||||
}
|
||||
pub fn punch(&mut self, buf: &[u8], id: Ipv4Addr, nat_info: NatInfo) -> io::Result<()> {
|
||||
if !self.context.route_table.need_punch(&id) {
|
||||
log::info!("已打洞成功,无需打洞:{:?}", id);
|
||||
return Ok(());
|
||||
}
|
||||
if 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(());
|
||||
}
|
||||
}
|
||||
//向tcp发起连接
|
||||
if let Some(ipv4_addr) = nat_info.local_tcp_ipv4addr() {
|
||||
if self.connect_tcp(buf, ipv4_addr) {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
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(());
|
||||
}
|
||||
}
|
||||
}
|
||||
let channel_num = self.context.channel_num();
|
||||
for index in 0..channel_num {
|
||||
if let Some(ipv4_addr) = nat_info.local_udp_ipv4addr(index) {
|
||||
let _ = self.context.send_main_udp(index, buf, ipv4_addr);
|
||||
}
|
||||
}
|
||||
|
||||
if self.punch_model != PunchModel::IPv4 {
|
||||
for index in 0..channel_num {
|
||||
if let Some(ipv6_addr) = nat_info.local_udp_ipv6addr(index) {
|
||||
let rs = self.context.send_main_udp(index, buf, ipv6_addr);
|
||||
log::info!("发送到ipv6地址:{:?},rs={:?}", ipv6_addr, rs);
|
||||
if rs.is_ok() && self.punch_model == PunchModel::IPv6 {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
match nat_info.nat_type {
|
||||
NatType::Symmetric => {
|
||||
// 假设对方绑定n个端口,通过NAT对外映射出n个 公网ip:公网端口,自己随机尝试k次的情况下
|
||||
// 猜中的概率 p = 1-((65535-n)/65535)*((65535-n-1)/(65535-1))*...*((65535-n-k+1)/(65535-k+1))
|
||||
// n取76,k取600,猜中的概率就超过50%了
|
||||
// 前提 自己是锥形网络,否则猜中了也通信不了
|
||||
|
||||
//预测范围内最多发送max_k1个包
|
||||
let max_k1 = 60;
|
||||
//全局最多发送max_k2个包
|
||||
let max_k2 = rand::thread_rng().gen_range(600..800);
|
||||
let port = nat_info.public_ports.get(0).map(|e| *e).unwrap_or(0);
|
||||
if nat_info.public_port_range < max_k1 * 3 {
|
||||
//端口变化不大时,在预测的范围内随机发送
|
||||
let min_port = if port > nat_info.public_port_range {
|
||||
port - nat_info.public_port_range
|
||||
} else {
|
||||
1
|
||||
};
|
||||
let (max_port, overflow) = port.overflowing_add(nat_info.public_port_range);
|
||||
let max_port = if overflow { 65535 } else { max_port };
|
||||
let k = if max_port - min_port + 1 > max_k1 {
|
||||
max_k1 as usize
|
||||
} else {
|
||||
(max_port - min_port + 1) as usize
|
||||
};
|
||||
let mut nums: Vec<u16> = (min_port..max_port).collect();
|
||||
nums.push(max_port);
|
||||
nums.shuffle(&mut rand::thread_rng());
|
||||
self.punch_symmetric(&nums[..k], buf, &nat_info.public_ips, max_k1 as usize)?;
|
||||
}
|
||||
let start = *self.port_index.entry(id.clone()).or_insert(0);
|
||||
let mut end = start + max_k2;
|
||||
if end > self.port_vec.len() {
|
||||
end = self.port_vec.len();
|
||||
}
|
||||
let mut index = start
|
||||
+ self.punch_symmetric(
|
||||
&self.port_vec[start..end],
|
||||
buf,
|
||||
&nat_info.public_ips,
|
||||
max_k2,
|
||||
)?;
|
||||
if index >= self.port_vec.len() {
|
||||
index = 0
|
||||
}
|
||||
self.port_index.insert(id, index);
|
||||
}
|
||||
NatType::Cone => {
|
||||
let is_cone = self.context.is_cone();
|
||||
'a: for index in 0..nat_info.public_ports.len().min(channel_num) {
|
||||
for ip in &nat_info.public_ips {
|
||||
let port = nat_info.public_ports[index];
|
||||
if port == 0 || ip.is_unspecified() {
|
||||
continue;
|
||||
}
|
||||
let addr = SocketAddr::V4(SocketAddrV4::new(*ip, port));
|
||||
if is_cone {
|
||||
self.context.send_main_udp(index, buf, addr)?;
|
||||
} else {
|
||||
//只有一方是对称,则对称方要使用全部端口发送数据,符合上述计算的概率
|
||||
self.context.try_send_all(buf, addr);
|
||||
}
|
||||
thread::sleep(Duration::from_millis(2));
|
||||
}
|
||||
if !is_cone {
|
||||
//对称网络数据只发一遍
|
||||
break 'a;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn punch_symmetric(
|
||||
&self,
|
||||
ports: &[u16],
|
||||
buf: &[u8],
|
||||
ips: &Vec<Ipv4Addr>,
|
||||
max: usize,
|
||||
) -> io::Result<usize> {
|
||||
let mut count = 0;
|
||||
for (index, port) in ports.iter().enumerate() {
|
||||
for pub_ip in ips {
|
||||
count += 1;
|
||||
if count == max {
|
||||
return Ok(index);
|
||||
}
|
||||
let addr = SocketAddr::V4(SocketAddrV4::new(*pub_ip, *port));
|
||||
self.context.send_main_udp(0, buf, addr)?;
|
||||
thread::sleep(Duration::from_millis(2));
|
||||
}
|
||||
}
|
||||
Ok(ports.len())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
use std::io;
|
||||
use std::ops::Deref;
|
||||
use std::sync::mpsc::{SyncSender, TrySendError};
|
||||
use std::sync::Arc;
|
||||
|
||||
use mio::Token;
|
||||
|
||||
use crate::channel::context::Context;
|
||||
use crate::channel::notify::{AcceptNotify, WritableNotify};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ChannelSender {
|
||||
context: Context,
|
||||
}
|
||||
|
||||
impl ChannelSender {
|
||||
pub fn new(context: Context) -> Self {
|
||||
Self { context }
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ChannelSender {
|
||||
type Target = Context;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.context
|
||||
}
|
||||
}
|
||||
pub struct AcceptSocketSender<T> {
|
||||
sender: SyncSender<T>,
|
||||
notify: AcceptNotify,
|
||||
}
|
||||
|
||||
impl<T> Clone for AcceptSocketSender<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
sender: self.sender.clone(),
|
||||
notify: self.notify.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T> AcceptSocketSender<T> {
|
||||
pub fn new(notify: AcceptNotify, sender: SyncSender<T>) -> Self {
|
||||
Self { sender, notify }
|
||||
}
|
||||
pub fn try_add_socket(&self, t: T) -> io::Result<()> {
|
||||
match self.sender.try_send(t) {
|
||||
Ok(_) => self.notify.add_socket(),
|
||||
Err(e) => match e {
|
||||
TrySendError::Full(_) => Err(io::Error::from(io::ErrorKind::WouldBlock)),
|
||||
TrySendError::Disconnected(_) => Err(io::Error::from(io::ErrorKind::WriteZero)),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PacketSender {
|
||||
inner: Arc<PacketSenderInner>,
|
||||
}
|
||||
|
||||
impl PacketSender {
|
||||
pub fn new(notify: WritableNotify, buffer: SyncSender<Vec<u8>>, token: Token) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(PacketSenderInner {
|
||||
token,
|
||||
notify,
|
||||
buffer,
|
||||
}),
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn try_send(&self, buf: &[u8]) -> io::Result<()> {
|
||||
self.inner.try_send(buf)
|
||||
}
|
||||
pub fn shutdown(&self) -> io::Result<()> {
|
||||
self.inner.shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PacketSenderInner {
|
||||
token: Token,
|
||||
notify: WritableNotify,
|
||||
buffer: SyncSender<Vec<u8>>,
|
||||
}
|
||||
|
||||
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)),
|
||||
},
|
||||
}
|
||||
}
|
||||
fn shutdown(&self) -> io::Result<()> {
|
||||
self.notify.notify(self.token, false)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,456 @@
|
||||
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 crate::channel::context::Context;
|
||||
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::util::StopManager;
|
||||
|
||||
const SERVER: Token = Token(0);
|
||||
const NOTIFY: Token = Token(1);
|
||||
|
||||
/// 监听tcp端口,等待客户端连接
|
||||
pub fn tcp_listen<H>(
|
||||
tcp_server: TcpListener,
|
||||
stop_manager: StopManager,
|
||||
recv_handler: H,
|
||||
context: Context,
|
||||
) -> io::Result<AcceptSocketSender<(TcpStream, SocketAddr, Option<Vec<u8>>)>>
|
||||
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);
|
||||
}
|
||||
})?
|
||||
};
|
||||
|
||||
thread::Builder::new()
|
||||
.name("tcpRead".into())
|
||||
.spawn(move || {
|
||||
if let Err(e) = tcp_listen0(
|
||||
poll,
|
||||
tcp_server,
|
||||
&stop_manager,
|
||||
waker,
|
||||
tcp_receiver,
|
||||
recv_handler,
|
||||
context,
|
||||
) {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
worker.stop_all();
|
||||
})?;
|
||||
Ok(accept)
|
||||
}
|
||||
|
||||
fn tcp_listen0<H>(
|
||||
mut poll: Poll,
|
||||
mut tcp_server: TcpListener,
|
||||
stop_manager: &StopManager,
|
||||
accept_notify: AcceptNotify,
|
||||
accept_tcp_receiver: Receiver<(TcpStream, SocketAddr, Option<Vec<u8>>)>,
|
||||
mut recv_handler: H,
|
||||
context: Context,
|
||||
) -> io::Result<()>
|
||||
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<Token, (RouteKey, TcpStream, Box<[u8; BUFFER_SIZE]>, usize)> =
|
||||
HashMap::with_capacity(32);
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 处理写事件
|
||||
|
||||
fn init_writable_handler(
|
||||
receiver: Receiver<(TcpStream, Token, SocketAddr, Option<Vec<u8>>)>,
|
||||
stop_manager: StopManager,
|
||||
context: Context,
|
||||
) -> io::Result<WritableNotify> {
|
||||
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();
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(writable_notify)
|
||||
}
|
||||
|
||||
/// 处理写事件
|
||||
fn tcp_writable_listen(
|
||||
receiver: Receiver<(TcpStream, Token, SocketAddr, Option<Vec<u8>>)>,
|
||||
mut poll: Poll,
|
||||
writable_notify: WritableNotify,
|
||||
context: &Context,
|
||||
) -> io::Result<()> {
|
||||
let mut events = Events::with_capacity(1024);
|
||||
let mut write_map: HashMap<
|
||||
Token,
|
||||
(
|
||||
TcpStream,
|
||||
SocketAddr,
|
||||
Receiver<Vec<u8>>,
|
||||
Option<(Vec<u8>, 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)?;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn accept_handle(
|
||||
stream: TcpStream,
|
||||
addr: SocketAddr,
|
||||
init_buf: Option<Vec<u8>>,
|
||||
write_waker: &WritableNotify,
|
||||
read_map: &mut HashMap<Token, (RouteKey, TcpStream, Box<[u8; BUFFER_SIZE]>, usize)>,
|
||||
tcp_sender: &SyncSender<(TcpStream, Token, SocketAddr, Option<Vec<u8>>)>,
|
||||
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"))
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("try_clone err={:?},addr={:?}", e, addr);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
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,
|
||||
),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn readable_handle<H>(
|
||||
token: &Token,
|
||||
map: &mut HashMap<Token, (RouteKey, TcpStream, Box<[u8; BUFFER_SIZE]>, usize)>,
|
||||
recv_handler: &mut H,
|
||||
context: &Context,
|
||||
) -> io::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], *route_key, context);
|
||||
*begin = 0;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::WouldBlock {
|
||||
break;
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn writable_handle(
|
||||
token: &Token,
|
||||
map: &mut HashMap<
|
||||
Token,
|
||||
(
|
||||
TcpStream,
|
||||
SocketAddr,
|
||||
Receiver<Vec<u8>>,
|
||||
Option<(Vec<u8>, 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));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn closed_handle_r(
|
||||
token: &Token,
|
||||
map: &mut HashMap<Token, (RouteKey, TcpStream, Box<[u8; BUFFER_SIZE]>, 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<Vec<u8>>,
|
||||
Option<(Vec<u8>, usize)>,
|
||||
),
|
||||
>,
|
||||
context: &Context,
|
||||
) {
|
||||
if let Some((tcp, addr, _, _)) = map.remove(token) {
|
||||
context.tcp_map.write().remove(&addr);
|
||||
let _ = tcp.shutdown(Shutdown::Both);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::mpsc::{sync_channel, Receiver};
|
||||
use std::sync::Arc;
|
||||
use std::{io, thread};
|
||||
|
||||
use mio::event::Source;
|
||||
use mio::net::UdpSocket;
|
||||
use mio::{Events, Interest, Poll, Token, Waker};
|
||||
|
||||
use crate::channel::context::Context;
|
||||
use crate::channel::handler::RecvChannelHandler;
|
||||
use crate::channel::notify::AcceptNotify;
|
||||
use crate::channel::sender::AcceptSocketSender;
|
||||
use crate::channel::{RouteKey, BUFFER_SIZE};
|
||||
use crate::util::StopManager;
|
||||
|
||||
pub fn udp_listen<H>(
|
||||
stop_manager: StopManager,
|
||||
recv_handler: H,
|
||||
context: Context,
|
||||
) -> io::Result<AcceptSocketSender<Option<Vec<UdpSocket>>>>
|
||||
where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
main_udp_listen(stop_manager.clone(), recv_handler.clone(), context.clone())?;
|
||||
sub_udp_listen(stop_manager, recv_handler, context)
|
||||
}
|
||||
|
||||
const NOTIFY: Token = Token(0);
|
||||
|
||||
fn sub_udp_listen<H>(
|
||||
stop_manager: StopManager,
|
||||
recv_handler: H,
|
||||
context: Context,
|
||||
) -> io::Result<AcceptSocketSender<Option<Vec<UdpSocket>>>>
|
||||
where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
let (udp_sender, udp_receiver) = sync_channel(64);
|
||||
let poll = Poll::new()?;
|
||||
let waker = AcceptNotify::new(Waker::new(poll.registry(), NOTIFY)?);
|
||||
let worker = {
|
||||
let waker = waker.clone();
|
||||
stop_manager.add_listener("sub_udp_listen".into(), move || {
|
||||
if let Err(e) = waker.stop() {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
})?
|
||||
};
|
||||
let accept = AcceptSocketSender::new(waker.clone(), udp_sender);
|
||||
thread::Builder::new()
|
||||
.name("subUdp".into())
|
||||
.spawn(move || {
|
||||
if let Err(e) = sub_udp_listen0(poll, recv_handler, context, waker, udp_receiver) {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
worker.stop_all();
|
||||
})?;
|
||||
Ok(accept)
|
||||
}
|
||||
|
||||
fn sub_udp_listen0<H>(
|
||||
mut poll: Poll,
|
||||
mut recv_handler: H,
|
||||
context: Context,
|
||||
accept_notify: AcceptNotify,
|
||||
accept_receiver: Receiver<Option<Vec<UdpSocket>>>,
|
||||
) -> io::Result<()>
|
||||
where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
let mut events = Events::with_capacity(1024);
|
||||
let mut buf = [0; BUFFER_SIZE];
|
||||
let mut read_map: HashMap<Token, UdpSocket> = HashMap::with_capacity(32);
|
||||
loop {
|
||||
poll.poll(&mut events, None)?;
|
||||
for event in events.iter() {
|
||||
match event.token() {
|
||||
NOTIFY => {
|
||||
if accept_notify.is_stop() {
|
||||
return Ok(());
|
||||
}
|
||||
if accept_notify.is_add_socket() {
|
||||
while let Ok(option) = accept_receiver.try_recv() {
|
||||
match option {
|
||||
None => {
|
||||
log::info!("切换成锥形模式");
|
||||
for (_, mut udp_socket) in read_map.drain() {
|
||||
if let Err(e) = udp_socket.deregister(poll.registry()) {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(socket_list) => {
|
||||
log::info!("切换成对称模式 监听端口数:{}", socket_list.len());
|
||||
for (index, mut udp_socket) in
|
||||
socket_list.into_iter().enumerate()
|
||||
{
|
||||
let token = Token(index + context.channel_num());
|
||||
poll.registry().register(
|
||||
&mut udp_socket,
|
||||
token,
|
||||
Interest::READABLE,
|
||||
)?;
|
||||
read_map.insert(token, udp_socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
token => {
|
||||
if let Some(udp_socket) = read_map.get(&token) {
|
||||
loop {
|
||||
match udp_socket.recv_from(&mut buf) {
|
||||
Ok((len, addr)) => {
|
||||
recv_handler.handle(
|
||||
&mut buf[..len],
|
||||
RouteKey::new(false, token.0, addr),
|
||||
&context,
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::WouldBlock {
|
||||
break;
|
||||
}
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 阻塞监听
|
||||
fn main_udp_listen<H>(
|
||||
stop_manager: StopManager,
|
||||
recv_handler: H,
|
||||
context: Context,
|
||||
) -> io::Result<()>
|
||||
where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
let poll = Poll::new()?;
|
||||
let waker = Arc::new(Waker::new(poll.registry(), NOTIFY)?);
|
||||
let _waker = waker.clone();
|
||||
let worker = stop_manager.add_listener("main_udp".into(), move || {
|
||||
if let Err(e) = waker.wake() {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
})?;
|
||||
thread::Builder::new()
|
||||
.name("mainUdp".into())
|
||||
.spawn(move || {
|
||||
if let Err(e) = main_udp_listen0(poll, recv_handler, context) {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
drop(_waker);
|
||||
worker.stop_all();
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn main_udp_listen0<H>(mut poll: Poll, mut recv_handler: H, context: Context) -> io::Result<()>
|
||||
where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
let mut buf = [0; BUFFER_SIZE];
|
||||
let mut udps = Vec::with_capacity(context.main_udp_socket.len());
|
||||
|
||||
for (index, udp) in context.main_udp_socket.iter().enumerate() {
|
||||
let udp_socket = udp.try_clone()?;
|
||||
udp_socket.set_nonblocking(true)?;
|
||||
let mut mio_udp = UdpSocket::from_std(udp_socket);
|
||||
poll.registry()
|
||||
.register(&mut mio_udp, Token(index + 1), Interest::READABLE)?;
|
||||
udps.push(mio_udp);
|
||||
}
|
||||
|
||||
let mut events = Events::with_capacity(udps.len());
|
||||
loop {
|
||||
poll.poll(&mut events, None)?;
|
||||
for x in events.iter() {
|
||||
let index = match x.token() {
|
||||
NOTIFY => return Ok(()),
|
||||
Token(index) => index - 1,
|
||||
};
|
||||
loop {
|
||||
match udps[index].recv_from(&mut buf) {
|
||||
Ok((len, addr)) => {
|
||||
recv_handler.handle(
|
||||
&mut buf[..len],
|
||||
RouteKey::new(false, index, addr),
|
||||
&context,
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::WouldBlock {
|
||||
break;
|
||||
}
|
||||
log::error!("main_udp_listen_{}={:?}", index, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// /// 用recvmmsg没什么帮助,这里记录下,以下是完整代码
|
||||
// #[cfg(unix)]
|
||||
// pub fn main_udp_listen0<H>(index: usize, mut recv_handler: H, context: Context) -> io::Result<()>
|
||||
// where
|
||||
// H: RecvChannelHandler,
|
||||
// {
|
||||
// use libc::{c_uint, mmsghdr, sockaddr_storage, socklen_t, timespec};
|
||||
// use std::os::fd::AsRawFd;
|
||||
//
|
||||
// let udp_socket = context.main_udp_socket[index].try_clone()?;
|
||||
// let fd = udp_socket.as_raw_fd();
|
||||
// const MAX_MESSAGES: usize = 16;
|
||||
// let mut iov: [libc::iovec; MAX_MESSAGES] = unsafe { std::mem::zeroed() };
|
||||
// let mut buf: [[u8; BUFFER_SIZE]; MAX_MESSAGES] = [[0; BUFFER_SIZE]; MAX_MESSAGES];
|
||||
// let mut msgs: [mmsghdr; MAX_MESSAGES] = unsafe { std::mem::zeroed() };
|
||||
// let mut addrs: [sockaddr_storage; MAX_MESSAGES] = unsafe { std::mem::zeroed() };
|
||||
// for i in 0..MAX_MESSAGES {
|
||||
// iov[i].iov_base = buf[i].as_mut_ptr() as *mut libc::c_void;
|
||||
// iov[i].iov_len = BUFFER_SIZE;
|
||||
// msgs[i].msg_hdr.msg_iov = &mut iov[i];
|
||||
// msgs[i].msg_hdr.msg_iovlen = 1;
|
||||
// msgs[i].msg_hdr.msg_name = &mut addrs[i] as *const _ as *mut libc::c_void;
|
||||
// msgs[i].msg_hdr.msg_namelen = std::mem::size_of::<sockaddr_storage>() as socklen_t;
|
||||
// }
|
||||
// let mut time: timespec = unsafe { std::mem::zeroed() };
|
||||
// loop {
|
||||
// if context.is_stop() {
|
||||
// return Ok(());
|
||||
// }
|
||||
// let res =
|
||||
// unsafe { libc::recvmmsg(fd, msgs.as_mut_ptr(), MAX_MESSAGES as c_uint, 0, &mut time) };
|
||||
// if res == -1 {
|
||||
// log::error!("main_udp_listen_{}={:?}", index, io::Error::last_os_error());
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// let nmsgs = res as usize;
|
||||
// for i in 0..nmsgs {
|
||||
// let msg = &mut buf[i][0..msgs[i].msg_len as usize];
|
||||
// let addr = sockaddr_to_socket_addr(&addrs[i], msgs[i].msg_hdr.msg_namelen);
|
||||
// if msg == b"stop" {
|
||||
// if context.is_stop() {
|
||||
// return Ok(());
|
||||
// }
|
||||
// }
|
||||
// recv_handler.handle(msg, RouteKey::new(false, index, addr), &context);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[cfg(unix)]
|
||||
// fn sockaddr_to_socket_addr(addr: &libc::sockaddr_storage, _len: libc::socklen_t) -> SocketAddr {
|
||||
// match addr.ss_family as libc::c_int {
|
||||
// libc::AF_INET => {
|
||||
// let addr_in = unsafe { *(addr as *const _ as *const libc::sockaddr_in) };
|
||||
// let ip = u32::from_be(addr_in.sin_addr.s_addr);
|
||||
// let port = u16::from_be(addr_in.sin_port);
|
||||
// SocketAddr::V4(std::net::SocketAddrV4::new(Ipv4Addr::from(ip), port))
|
||||
// }
|
||||
// libc::AF_INET6 => {
|
||||
// let addr_in6 = unsafe { *(addr as *const _ as *const libc::sockaddr_in6) };
|
||||
// let ip = std::net::Ipv6Addr::from(addr_in6.sin6_addr.s6_addr);
|
||||
// let port = u16::from_be(addr_in6.sin6_port);
|
||||
// SocketAddr::V6(std::net::SocketAddrV6::new(ip, port, 0, 0))
|
||||
// }
|
||||
// _ => panic!("Unsupported address family"),
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1,155 @@
|
||||
use std::io;
|
||||
|
||||
use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyIvInit};
|
||||
use rand::RngCore;
|
||||
|
||||
use crate::cipher::Finger;
|
||||
use crate::protocol::body::AesCbcSecretBody;
|
||||
use crate::protocol::{NetPacket, HEAD_LEN};
|
||||
|
||||
type Aes128CbcEnc = cbc::Encryptor<aes::Aes128>;
|
||||
type Aes128CbcDec = cbc::Decryptor<aes::Aes128>;
|
||||
type Aes256CbcEnc = cbc::Encryptor<aes::Aes256>;
|
||||
type Aes256CbcDec = cbc::Decryptor<aes::Aes256>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AesCbcCipher {
|
||||
pub(crate) cipher: AesCbcEnum,
|
||||
pub(crate) finger: Option<Finger>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum AesCbcEnum {
|
||||
AES128CBC([u8; 16]),
|
||||
AES256CBC([u8; 32]),
|
||||
}
|
||||
|
||||
impl AesCbcCipher {
|
||||
pub fn key(&self) -> &[u8] {
|
||||
match &self.cipher {
|
||||
AesCbcEnum::AES128CBC(key) => key,
|
||||
AesCbcEnum::AES256CBC(key) => key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AesCbcCipher {
|
||||
pub fn new_128(key: [u8; 16], finger: Option<Finger>) -> Self {
|
||||
Self {
|
||||
cipher: AesCbcEnum::AES128CBC(key),
|
||||
finger,
|
||||
}
|
||||
}
|
||||
pub fn new_256(key: [u8; 32], finger: Option<Finger>) -> Self {
|
||||
Self {
|
||||
cipher: AesCbcEnum::AES256CBC(key),
|
||||
finger,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> io::Result<()> {
|
||||
if !net_packet.is_encrypt() {
|
||||
//未加密的数据直接丢弃
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "not encrypt"));
|
||||
}
|
||||
if net_packet.payload().len() < 16 {
|
||||
log::error!("数据异常,长度{}小于{}", net_packet.payload().len(), 16);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "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();
|
||||
if let Some(finger) = &self.finger {
|
||||
iv[12..16].copy_from_slice(&finger.hash[0..4]);
|
||||
}
|
||||
|
||||
let mut secret_body =
|
||||
AesCbcSecretBody::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());
|
||||
if &finger != secret_body.finger() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "finger err"));
|
||||
}
|
||||
}
|
||||
let rs = match &self.cipher {
|
||||
AesCbcEnum::AES128CBC(key) => Aes128CbcDec::new(&(*key).into(), &iv.into())
|
||||
.decrypt_padded_mut::<Pkcs7>(secret_body.en_body_mut()),
|
||||
AesCbcEnum::AES256CBC(key) => Aes256CbcDec::new(&(*key).into(), &iv.into())
|
||||
.decrypt_padded_mut::<Pkcs7>(secret_body.en_body_mut()),
|
||||
};
|
||||
match rs {
|
||||
Ok(buf) => {
|
||||
let len = buf.len();
|
||||
net_packet.set_encrypt_flag(false);
|
||||
//减去末尾的随机数
|
||||
net_packet.set_data_len(HEAD_LEN + len - 4)?;
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("解密失败:{}", e),
|
||||
)),
|
||||
}
|
||||
}
|
||||
/// net_packet 必须预留足够长度
|
||||
/// data_len是有效载荷的长度
|
||||
pub fn encrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> io::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();
|
||||
if let Some(finger) = &self.finger {
|
||||
iv[12..16].copy_from_slice(&finger.hash[0..4]);
|
||||
net_packet.set_data_len(data_len + 16)?;
|
||||
} else {
|
||||
net_packet.set_data_len(data_len + 4)?;
|
||||
}
|
||||
//先扩充随机数
|
||||
let mut secret_body =
|
||||
AesCbcSecretBody::new(net_packet.payload_mut(), self.finger.is_some())?;
|
||||
secret_body.set_random(rand::thread_rng().next_u32());
|
||||
let p_len = secret_body.en_body().len();
|
||||
net_packet.set_data_len_max();
|
||||
let rs = match &self.cipher {
|
||||
AesCbcEnum::AES128CBC(key) => Aes128CbcEnc::new(&(*key).into(), &iv.into())
|
||||
.encrypt_padded_mut::<Pkcs7>(net_packet.payload_mut(), p_len),
|
||||
AesCbcEnum::AES256CBC(key) => Aes256CbcEnc::new(&(*key).into(), &iv.into())
|
||||
.encrypt_padded_mut::<Pkcs7>(net_packet.payload_mut(), p_len),
|
||||
};
|
||||
return match rs {
|
||||
Ok(buf) => {
|
||||
let len = buf.len();
|
||||
if let Some(finger) = &self.finger {
|
||||
let finger = finger.calculate_finger(&iv[..12], buf);
|
||||
//设置实际长度
|
||||
net_packet.set_data_len(HEAD_LEN + len + finger.len())?;
|
||||
let mut secret_body = AesCbcSecretBody::new(net_packet.payload_mut(), true)?;
|
||||
secret_body.set_finger(&finger)?;
|
||||
} else {
|
||||
net_packet.set_data_len(HEAD_LEN + len)?;
|
||||
}
|
||||
|
||||
net_packet.set_encrypt_flag(true);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("加密失败:{}", e),
|
||||
)),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
use crate::cipher::Finger;
|
||||
use crate::protocol::{NetPacket, HEAD_LEN};
|
||||
use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyInit};
|
||||
use std::io;
|
||||
|
||||
type Aes128EcbEnc = ecb::Encryptor<aes::Aes128>;
|
||||
type Aes128EcbDec = ecb::Decryptor<aes::Aes128>;
|
||||
type Aes256EcbEnc = ecb::Encryptor<aes::Aes256>;
|
||||
type Aes256EcbDec = ecb::Decryptor<aes::Aes256>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AesEcbCipher {
|
||||
key: AesEcbEnum,
|
||||
pub(crate) finger: Option<Finger>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AesEcbEnum {
|
||||
AES128ECB([u8; 16]),
|
||||
AES256ECB([u8; 32]),
|
||||
}
|
||||
|
||||
impl AesEcbCipher {
|
||||
pub fn key(&self) -> &[u8] {
|
||||
match &self.key {
|
||||
AesEcbEnum::AES128ECB(key) => key,
|
||||
AesEcbEnum::AES256ECB(key) => key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AesEcbCipher {
|
||||
pub fn new_128(key: [u8; 16], finger: Option<Finger>) -> Self {
|
||||
Self {
|
||||
key: AesEcbEnum::AES128ECB(key),
|
||||
finger,
|
||||
}
|
||||
}
|
||||
pub fn new_256(key: [u8; 32], finger: Option<Finger>) -> Self {
|
||||
Self {
|
||||
key: AesEcbEnum::AES256ECB(key),
|
||||
finger,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> io::Result<()> {
|
||||
if !net_packet.is_encrypt() {
|
||||
//未加密的数据直接丢弃
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "not encrypt"));
|
||||
}
|
||||
|
||||
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 len = net_packet.payload().len();
|
||||
if len < 12 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "payload len <12"));
|
||||
}
|
||||
let secret_body = &net_packet.payload()[..len - 12];
|
||||
let finger = finger.calculate_finger(&nonce_raw, secret_body);
|
||||
if &finger != &net_packet.payload()[len - 12..] {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "finger err"));
|
||||
}
|
||||
net_packet.set_data_len(net_packet.data_len() - finger.len())?;
|
||||
}
|
||||
if net_packet.payload().len() < 16 {
|
||||
log::error!("数据异常,长度{}小于{}", net_packet.payload().len(), 16);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
let mut out = [0u8; 1024 * 5];
|
||||
let rs = match self.key {
|
||||
AesEcbEnum::AES128ECB(key) => Aes128EcbDec::new(&key.into())
|
||||
.decrypt_padded_b2b_mut::<Pkcs7>(net_packet.payload(), &mut out),
|
||||
AesEcbEnum::AES256ECB(key) => Aes256EcbDec::new(&key.into())
|
||||
.decrypt_padded_b2b_mut::<Pkcs7>(net_packet.payload(), &mut out),
|
||||
};
|
||||
match rs {
|
||||
Ok(buf) => {
|
||||
//校验头部
|
||||
let src_net_packet = NetPacket::new(buf)?;
|
||||
if src_net_packet.source() != net_packet.source() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
if src_net_packet.destination() != net_packet.destination() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
if src_net_packet.protocol() != net_packet.protocol() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
if src_net_packet.transport_protocol() != net_packet.transport_protocol() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
if src_net_packet.is_gateway() != net_packet.is_gateway() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
if src_net_packet.source_ttl() != net_packet.source_ttl() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
net_packet.set_data_len(buf.len())?;
|
||||
net_packet.set_payload(src_net_packet.payload())?;
|
||||
net_packet.set_encrypt_flag(false);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("aes_ecb解密失败:{}", e),
|
||||
)),
|
||||
}
|
||||
}
|
||||
/// net_packet 必须预留足够长度
|
||||
/// data_len是有效载荷的长度
|
||||
pub fn encrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> io::Result<()> {
|
||||
let mut out = [0u8; 1024 * 5];
|
||||
let rs = match self.key {
|
||||
AesEcbEnum::AES128ECB(key) => Aes128EcbEnc::new(&key.into())
|
||||
.encrypt_padded_b2b_mut::<Pkcs7>(net_packet.buffer(), &mut out),
|
||||
AesEcbEnum::AES256ECB(key) => Aes256EcbEnc::new(&key.into())
|
||||
.encrypt_padded_b2b_mut::<Pkcs7>(net_packet.buffer(), &mut out),
|
||||
};
|
||||
|
||||
return match rs {
|
||||
Ok(buf) => {
|
||||
net_packet.set_data_len(HEAD_LEN + buf.len())?;
|
||||
net_packet.set_payload(buf)?;
|
||||
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 finger = finger.calculate_finger(&nonce_raw, buf);
|
||||
let src_data_len = net_packet.data_len();
|
||||
//设置实际长度
|
||||
net_packet.set_data_len(src_data_len + finger.len())?;
|
||||
|
||||
net_packet.buffer_mut()[src_data_len..].copy_from_slice(&finger);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("aes_ecb加密失败:{}", e),
|
||||
)),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_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)
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
use std::io;
|
||||
|
||||
use aes_gcm::aead::consts::{U12, U16};
|
||||
use aes_gcm::aead::generic_array::GenericArray;
|
||||
use aes_gcm::{AeadInPlace, Aes128Gcm, Aes256Gcm, Key, KeyInit, Nonce, Tag};
|
||||
use rand::RngCore;
|
||||
|
||||
use crate::cipher::finger::Finger;
|
||||
use crate::protocol::{body::SecretBody, body::AES_GCM_ENCRYPTION_RESERVED, NetPacket};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AesGcmCipher {
|
||||
pub(crate) cipher: AesGcmEnum,
|
||||
pub(crate) finger: Option<Finger>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum AesGcmEnum {
|
||||
AES128GCM(Aes128Gcm),
|
||||
AES256GCM(Aes256Gcm),
|
||||
}
|
||||
|
||||
impl AesGcmCipher {
|
||||
pub fn new_128(key: [u8; 16], finger: Option<Finger>) -> Self {
|
||||
let key: &Key<Aes128Gcm> = &key.into();
|
||||
Self {
|
||||
cipher: AesGcmEnum::AES128GCM(Aes128Gcm::new(key)),
|
||||
finger,
|
||||
}
|
||||
}
|
||||
pub fn new_256(key: [u8; 32], finger: Option<Finger>) -> Self {
|
||||
let key: &Key<Aes256Gcm> = &key.into();
|
||||
Self {
|
||||
cipher: AesGcmEnum::AES256GCM(Aes256Gcm::new(key)),
|
||||
finger,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> io::Result<()> {
|
||||
if !net_packet.is_encrypt() {
|
||||
//未加密的数据直接丢弃
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "not encrypt"));
|
||||
}
|
||||
if net_packet.payload().len() < AES_GCM_ENCRYPTION_RESERVED {
|
||||
log::error!("数据异常,长度小于{}", AES_GCM_ENCRYPTION_RESERVED);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "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: &GenericArray<u8, U12> = Nonce::from_slice(&nonce_raw);
|
||||
|
||||
let mut secret_body = SecretBody::new(net_packet.payload_mut(), self.finger.is_some())?;
|
||||
let tag = secret_body.tag();
|
||||
if let Some(finger) = &self.finger {
|
||||
let finger = finger.calculate_finger(&nonce_raw, secret_body.en_body());
|
||||
if &finger != secret_body.finger() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "finger err"));
|
||||
}
|
||||
}
|
||||
let tag: GenericArray<u8, U16> = Tag::clone_from_slice(tag);
|
||||
let rs = match &self.cipher {
|
||||
AesGcmEnum::AES128GCM(aes_gcm) => {
|
||||
aes_gcm.decrypt_in_place_detached(nonce, &[], secret_body.body_mut(), &tag)
|
||||
}
|
||||
AesGcmEnum::AES256GCM(aes_gcm) => {
|
||||
aes_gcm.decrypt_in_place_detached(nonce, &[], secret_body.body_mut(), &tag)
|
||||
}
|
||||
};
|
||||
if let Err(e) = rs {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("解密失败:{}", e),
|
||||
));
|
||||
}
|
||||
net_packet.set_encrypt_flag(false);
|
||||
net_packet.set_data_len(net_packet.data_len() - AES_GCM_ENCRYPTION_RESERVED)?;
|
||||
return Ok(());
|
||||
}
|
||||
/// net_packet 必须预留足够长度
|
||||
/// data_len是有效载荷的长度
|
||||
pub fn encrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> io::Result<()> {
|
||||
if net_packet.reserve() < AES_GCM_ENCRYPTION_RESERVED {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "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: &GenericArray<u8, U12> = Nonce::from_slice(&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())?;
|
||||
secret_body.set_random(rand::thread_rng().next_u32());
|
||||
let rs = match &self.cipher {
|
||||
AesGcmEnum::AES128GCM(aes_gcm) => {
|
||||
aes_gcm.encrypt_in_place_detached(nonce, &[], secret_body.body_mut())
|
||||
}
|
||||
AesGcmEnum::AES256GCM(aes_gcm) => {
|
||||
aes_gcm.encrypt_in_place_detached(nonce, &[], secret_body.body_mut())
|
||||
}
|
||||
};
|
||||
return match rs {
|
||||
Ok(tag) => {
|
||||
secret_body.set_tag(tag.as_slice())?;
|
||||
if let Some(finger) = &self.finger {
|
||||
let finger = finger.calculate_finger(&nonce_raw, secret_body.en_body());
|
||||
secret_body.set_finger(&finger)?;
|
||||
}
|
||||
net_packet.set_encrypt_flag(true);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("加密失败:{}", e),
|
||||
)),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
#[cfg(not(any(feature = "openssl-vendored", feature = "openssl")))]
|
||||
use crate::cipher::aes_ecb::AesEcbCipher;
|
||||
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
use crate::cipher::aes_cbc::AesCbcCipher;
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
#[cfg(not(feature = "ring-cipher"))]
|
||||
use crate::cipher::aes_gcm_cipher::AesGcmCipher;
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
#[cfg(any(feature = "openssl-vendored", feature = "openssl"))]
|
||||
use crate::cipher::openssl_aes_ecb::AesEcbCipher;
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
#[cfg(feature = "ring-cipher")]
|
||||
use crate::cipher::ring_aes_gcm_cipher::AesGcmCipher;
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
use crate::cipher::sm4_cbc::Sm4CbcCipher;
|
||||
#[cfg(any(
|
||||
feature = "aes_gcm",
|
||||
feature = "server_encrypt",
|
||||
feature = "aes_cbc",
|
||||
feature = "aes_ecb",
|
||||
feature = "sm4_cbc"
|
||||
))]
|
||||
use crate::cipher::Finger;
|
||||
use crate::protocol::NetPacket;
|
||||
#[cfg(any(
|
||||
feature = "aes_gcm",
|
||||
feature = "server_encrypt",
|
||||
feature = "aes_cbc",
|
||||
feature = "aes_ecb",
|
||||
feature = "sm4_cbc"
|
||||
))]
|
||||
use sha2::Digest;
|
||||
use std::io;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
pub enum CipherModel {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
AesGcm,
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
AesCbc,
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
AesEcb,
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
Sm4Cbc,
|
||||
None,
|
||||
}
|
||||
|
||||
impl FromStr for CipherModel {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
#[cfg(not(any(
|
||||
feature = "aes_gcm",
|
||||
feature = "server_encrypt",
|
||||
feature = "aes_cbc",
|
||||
feature = "aes_ecb",
|
||||
feature = "sm4_cbc"
|
||||
)))]
|
||||
return Err(format!("not match '{}', no encrypt", s));
|
||||
#[cfg(any(
|
||||
feature = "aes_gcm",
|
||||
feature = "server_encrypt",
|
||||
feature = "aes_cbc",
|
||||
feature = "aes_ecb",
|
||||
feature = "sm4_cbc"
|
||||
))]
|
||||
match s.to_lowercase().trim() {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
"aes_gcm" => Ok(CipherModel::AesGcm),
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
"aes_cbc" => Ok(CipherModel::AesCbc),
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
"aes_ecb" => Ok(CipherModel::AesEcb),
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
"sm4_cbc" => Ok(CipherModel::Sm4Cbc),
|
||||
_ => {
|
||||
let mut enums = String::new();
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
enums.push_str("/aes_gcm");
|
||||
#[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");
|
||||
let str = if enums.is_empty() {
|
||||
"no encrypt"
|
||||
} else {
|
||||
&enums[1..]
|
||||
};
|
||||
Err(format!("not match '{}', enum:{}", s, str))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Cipher {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
AesGcm((AesGcmCipher, Vec<u8>)),
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
AesCbc(AesCbcCipher),
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
AesEcb(AesEcbCipher),
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
Sm4Cbc(Sm4CbcCipher),
|
||||
None,
|
||||
}
|
||||
impl Cipher {
|
||||
#[cfg(not(any(
|
||||
feature = "aes_gcm",
|
||||
feature = "server_encrypt",
|
||||
feature = "aes_cbc",
|
||||
feature = "aes_ecb",
|
||||
feature = "sm4_cbc"
|
||||
)))]
|
||||
pub fn new_password(
|
||||
_model: CipherModel,
|
||||
_password: Option<String>,
|
||||
_token: Option<String>,
|
||||
) -> Self {
|
||||
Cipher::None
|
||||
}
|
||||
#[cfg(any(
|
||||
feature = "aes_gcm",
|
||||
feature = "server_encrypt",
|
||||
feature = "aes_cbc",
|
||||
feature = "aes_ecb",
|
||||
feature = "sm4_cbc"
|
||||
))]
|
||||
pub fn new_password(
|
||||
model: CipherModel,
|
||||
password: Option<String>,
|
||||
token: Option<String>,
|
||||
) -> Self {
|
||||
let finger = token.map(|token| Finger::new(&token));
|
||||
if let Some(password) = password {
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
hasher.update(password.as_bytes());
|
||||
let key: [u8; 32] = hasher.finalize().into();
|
||||
match model {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
CipherModel::AesGcm => {
|
||||
if password.len() < 8 {
|
||||
let aes = AesGcmCipher::new_128(key[..16].try_into().unwrap(), finger);
|
||||
Cipher::AesGcm((aes, key[..16].to_vec()))
|
||||
} else {
|
||||
let aes = AesGcmCipher::new_256(key, finger);
|
||||
Cipher::AesGcm((aes, key.to_vec()))
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
CipherModel::AesCbc => {
|
||||
if password.len() < 8 {
|
||||
let aes = AesCbcCipher::new_128(key[..16].try_into().unwrap(), finger);
|
||||
Cipher::AesCbc(aes)
|
||||
} else {
|
||||
let aes = AesCbcCipher::new_256(key, finger);
|
||||
Cipher::AesCbc(aes)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
CipherModel::AesEcb => {
|
||||
if password.len() < 8 {
|
||||
let aes = AesEcbCipher::new_128(key[..16].try_into().unwrap(), finger);
|
||||
Cipher::AesEcb(aes)
|
||||
} else {
|
||||
let aes = AesEcbCipher::new_256(key, finger);
|
||||
Cipher::AesEcb(aes)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
CipherModel::Sm4Cbc => {
|
||||
let aes = Sm4CbcCipher::new_128(key[..16].try_into().unwrap(), finger);
|
||||
Cipher::Sm4Cbc(aes)
|
||||
}
|
||||
CipherModel::None => Cipher::None,
|
||||
}
|
||||
} else {
|
||||
Cipher::None
|
||||
}
|
||||
}
|
||||
#[cfg(not(any(
|
||||
feature = "aes_gcm",
|
||||
feature = "server_encrypt",
|
||||
feature = "aes_cbc",
|
||||
feature = "aes_ecb",
|
||||
feature = "sm4_cbc"
|
||||
)))]
|
||||
pub fn new_key(_key: [u8; 32], _token: String) -> io::Result<Self> {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "key error"))
|
||||
}
|
||||
#[cfg(any(
|
||||
feature = "aes_gcm",
|
||||
feature = "server_encrypt",
|
||||
feature = "aes_cbc",
|
||||
feature = "aes_ecb",
|
||||
feature = "sm4_cbc"
|
||||
))]
|
||||
pub fn new_key(key: [u8; 32], token: String) -> io::Result<Self> {
|
||||
let finger = Some(Finger::new(&token));
|
||||
match key.len() {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
16 => {
|
||||
let aes = AesGcmCipher::new_128(key[..16].try_into().unwrap(), finger);
|
||||
Ok(Cipher::AesGcm((aes, key[..16].to_vec())))
|
||||
}
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
32 => {
|
||||
let aes = AesGcmCipher::new_256(key, finger);
|
||||
Ok(Cipher::AesGcm((aes, key.to_vec())))
|
||||
}
|
||||
_ => Err(io::Error::new(io::ErrorKind::Other, "key error")),
|
||||
}
|
||||
}
|
||||
pub fn decrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> io::Result<()> {
|
||||
match self {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
Cipher::AesGcm((aes_gcm, _)) => aes_gcm.decrypt_ipv4(net_packet),
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
Cipher::AesCbc(aes_cbc) => aes_cbc.decrypt_ipv4(net_packet),
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
Cipher::AesEcb(aes_ecb) => aes_ecb.decrypt_ipv4(net_packet),
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
Cipher::Sm4Cbc(sm4_cbc) => sm4_cbc.decrypt_ipv4(net_packet),
|
||||
Cipher::None => {
|
||||
if net_packet.is_encrypt() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "not key"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(any(
|
||||
feature = "aes_gcm",
|
||||
feature = "server_encrypt",
|
||||
feature = "aes_cbc",
|
||||
feature = "aes_ecb",
|
||||
feature = "sm4_cbc"
|
||||
)))]
|
||||
pub fn encrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
_net_packet: &mut NetPacket<B>,
|
||||
) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(any(
|
||||
feature = "aes_gcm",
|
||||
feature = "server_encrypt",
|
||||
feature = "aes_cbc",
|
||||
feature = "aes_ecb",
|
||||
feature = "sm4_cbc"
|
||||
))]
|
||||
pub fn encrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> io::Result<()> {
|
||||
match self {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
Cipher::AesGcm((aes_gcm, _)) => aes_gcm.encrypt_ipv4(net_packet),
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
Cipher::AesCbc(aes_cbc) => aes_cbc.encrypt_ipv4(net_packet),
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
Cipher::AesEcb(aes_ecb) => aes_ecb.encrypt_ipv4(net_packet),
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
Cipher::Sm4Cbc(sm4_cbc) => sm4_cbc.encrypt_ipv4(net_packet),
|
||||
Cipher::None => Ok(()),
|
||||
}
|
||||
}
|
||||
#[cfg(not(any(
|
||||
feature = "aes_gcm",
|
||||
feature = "server_encrypt",
|
||||
feature = "aes_cbc",
|
||||
feature = "aes_ecb",
|
||||
feature = "sm4_cbc"
|
||||
)))]
|
||||
pub fn check_finger<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
_net_packet: &NetPacket<B>,
|
||||
) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(any(
|
||||
feature = "aes_gcm",
|
||||
feature = "server_encrypt",
|
||||
feature = "aes_cbc",
|
||||
feature = "aes_ecb",
|
||||
feature = "sm4_cbc"
|
||||
))]
|
||||
pub fn check_finger<B: AsRef<[u8]>>(&self, net_packet: &NetPacket<B>) -> io::Result<()> {
|
||||
match self {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
Cipher::AesGcm((aes_gcm, _)) => aes_gcm
|
||||
.finger
|
||||
.as_ref()
|
||||
.map(|f| f.check_finger(net_packet))
|
||||
.unwrap_or(Ok(())),
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
Cipher::AesCbc(aes_cbc) => aes_cbc
|
||||
.finger
|
||||
.as_ref()
|
||||
.map(|f| f.check_finger(net_packet))
|
||||
.unwrap_or(Ok(())),
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
Cipher::AesEcb(aes_ecb) => aes_ecb
|
||||
.finger
|
||||
.as_ref()
|
||||
.map(|f| f.check_finger(net_packet))
|
||||
.unwrap_or(Ok(())),
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
Cipher::Sm4Cbc(sm4_cbc) => sm4_cbc
|
||||
.finger
|
||||
.as_ref()
|
||||
.map(|f| f.check_finger(net_packet))
|
||||
.unwrap_or(Ok(())),
|
||||
Cipher::None => Ok(()),
|
||||
}
|
||||
}
|
||||
pub fn key(&self) -> Option<&[u8]> {
|
||||
match self {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
Cipher::AesGcm((_, key)) => Some(key),
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
Cipher::AesCbc(aes_cbc) => Some(aes_cbc.key()),
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
Cipher::AesEcb(aes_ecb) => Some(aes_ecb.key()),
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
Cipher::Sm4Cbc(sm4_cbc) => Some(sm4_cbc.key()),
|
||||
Cipher::None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
use std::io;
|
||||
|
||||
use sha2::Digest;
|
||||
|
||||
use crate::protocol::NetPacket;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Finger {
|
||||
pub(crate) hash: [u8; 32],
|
||||
}
|
||||
|
||||
impl Finger {
|
||||
pub fn new(str: &str) -> Self {
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
hasher.update(str.as_bytes());
|
||||
let hash: [u8; 32] = hasher.finalize().into();
|
||||
Finger { hash }
|
||||
}
|
||||
pub fn check_finger<B: AsRef<[u8]>>(&self, net_packet: &NetPacket<B>) -> io::Result<()> {
|
||||
if !net_packet.is_encrypt() {
|
||||
//未加密的数据直接丢弃
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "not encrypt"));
|
||||
}
|
||||
let payload_len = net_packet.payload().len();
|
||||
if payload_len < 12 {
|
||||
log::error!("数据异常,长度小于{}", 12);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "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 payload = net_packet.payload();
|
||||
let finger = self.calculate_finger(&nonce_raw, &payload[..payload_len - 12]);
|
||||
if &finger[..] != &payload[payload_len - 12..] {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "finger err"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn calculate_finger(&self, nonce: &[u8], secret_body: &[u8]) -> [u8; 12] {
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
hasher.update(nonce);
|
||||
hasher.update(secret_body);
|
||||
hasher.update(&self.hash);
|
||||
let key: [u8; 32] = hasher.finalize().into();
|
||||
return key[20..].try_into().unwrap();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
mod aes_cbc;
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
#[cfg(not(any(feature = "openssl-vendored", feature = "openssl")))]
|
||||
mod aes_ecb;
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
#[cfg(not(feature = "ring-cipher"))]
|
||||
mod aes_gcm_cipher;
|
||||
mod cipher;
|
||||
#[cfg(any(
|
||||
feature = "aes_gcm",
|
||||
feature = "server_encrypt",
|
||||
feature = "aes_cbc",
|
||||
feature = "aes_ecb",
|
||||
feature = "sm4_cbc"
|
||||
))]
|
||||
mod finger;
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
#[cfg(any(feature = "openssl-vendored", feature = "openssl"))]
|
||||
mod openssl_aes_ecb;
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
#[cfg(feature = "ring-cipher")]
|
||||
mod ring_aes_gcm_cipher;
|
||||
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
mod sm4_cbc;
|
||||
pub use cipher::Cipher;
|
||||
pub use cipher::CipherModel;
|
||||
#[cfg(any(
|
||||
feature = "aes_gcm",
|
||||
feature = "server_encrypt",
|
||||
feature = "aes_cbc",
|
||||
feature = "aes_ecb",
|
||||
feature = "sm4_cbc"
|
||||
))]
|
||||
pub use finger::Finger;
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
mod rsa_cipher;
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
pub use rsa_cipher::RsaCipher;
|
||||
@@ -0,0 +1,226 @@
|
||||
use crate::cipher::Finger;
|
||||
use crate::protocol::{NetPacket, HEAD_LEN};
|
||||
use libc::c_int;
|
||||
use openssl_sys::EVP_CIPHER_CTX;
|
||||
use std::{io, ptr};
|
||||
|
||||
pub struct AesEcbCipher {
|
||||
key: Vec<u8>,
|
||||
pub(crate) en_ctx: *mut EVP_CIPHER_CTX,
|
||||
pub(crate) de_ctx: *mut EVP_CIPHER_CTX,
|
||||
pub(crate) finger: Option<Finger>,
|
||||
}
|
||||
|
||||
impl Drop for AesEcbCipher {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
openssl_sys::EVP_CIPHER_CTX_free(self.de_ctx);
|
||||
openssl_sys::EVP_CIPHER_CTX_free(self.en_ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for AesEcbCipher {
|
||||
fn clone(&self) -> Self {
|
||||
if self.key.len() == 16 {
|
||||
AesEcbCipher::new_128(self.key.clone().try_into().unwrap(), self.finger.clone())
|
||||
} else {
|
||||
AesEcbCipher::new_256(self.key.clone().try_into().unwrap(), self.finger.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Sync for AesEcbCipher {}
|
||||
|
||||
unsafe impl Send for AesEcbCipher {}
|
||||
|
||||
impl AesEcbCipher {
|
||||
pub fn key(&self) -> &[u8] {
|
||||
&self.key
|
||||
}
|
||||
}
|
||||
|
||||
impl AesEcbCipher {
|
||||
pub fn new_128(key: [u8; 16], finger: Option<Finger>) -> Self {
|
||||
unsafe {
|
||||
let cipher = openssl_sys::EVP_aes_128_ecb();
|
||||
let en_ctx = openssl_sys::EVP_CIPHER_CTX_new();
|
||||
openssl_sys::EVP_EncryptInit_ex(
|
||||
en_ctx,
|
||||
cipher,
|
||||
ptr::null_mut(),
|
||||
key.as_ptr(),
|
||||
ptr::null(),
|
||||
);
|
||||
|
||||
let de_ctx = openssl_sys::EVP_CIPHER_CTX_new();
|
||||
openssl_sys::EVP_DecryptInit_ex(
|
||||
de_ctx,
|
||||
cipher,
|
||||
ptr::null_mut(),
|
||||
key.as_ptr(),
|
||||
ptr::null(),
|
||||
);
|
||||
Self {
|
||||
key: key.to_vec(),
|
||||
en_ctx,
|
||||
de_ctx,
|
||||
finger,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn new_256(key: [u8; 32], finger: Option<Finger>) -> Self {
|
||||
unsafe {
|
||||
let cipher = openssl_sys::EVP_aes_256_ecb();
|
||||
let en_ctx = openssl_sys::EVP_CIPHER_CTX_new();
|
||||
openssl_sys::EVP_EncryptInit_ex(
|
||||
en_ctx,
|
||||
cipher,
|
||||
ptr::null_mut(),
|
||||
key.as_ptr(),
|
||||
ptr::null(),
|
||||
);
|
||||
let de_ctx = openssl_sys::EVP_CIPHER_CTX_new();
|
||||
openssl_sys::EVP_DecryptInit_ex(
|
||||
de_ctx,
|
||||
cipher,
|
||||
ptr::null_mut(),
|
||||
key.as_ptr(),
|
||||
ptr::null(),
|
||||
);
|
||||
Self {
|
||||
key: key.to_vec(),
|
||||
en_ctx,
|
||||
de_ctx,
|
||||
finger,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> io::Result<()> {
|
||||
if !net_packet.is_encrypt() {
|
||||
//未加密的数据直接丢弃
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "not encrypt"));
|
||||
}
|
||||
|
||||
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 len = net_packet.payload().len();
|
||||
if len < 12 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data len err"));
|
||||
}
|
||||
let secret_body = &net_packet.payload()[..len - 12];
|
||||
let finger = finger.calculate_finger(&nonce_raw, secret_body);
|
||||
if &finger != &net_packet.payload()[len - 12..] {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "finger err"));
|
||||
}
|
||||
net_packet.set_data_len(net_packet.data_len() - finger.len())?;
|
||||
}
|
||||
if net_packet.payload().len() < 16 {
|
||||
log::error!("数据异常,长度{}小于{}", net_packet.payload().len(), 16);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
let input = net_packet.payload();
|
||||
let mut out = [0u8; 1024 * 5];
|
||||
let mut out_len = 0;
|
||||
let ctx = self.de_ctx;
|
||||
unsafe {
|
||||
let out_ptr = out.as_mut_ptr();
|
||||
let in_len = input.len() as c_int;
|
||||
openssl_sys::EVP_DecryptUpdate(ctx, out_ptr, &mut out_len, input.as_ptr(), in_len);
|
||||
let mut last_len = 0;
|
||||
openssl_sys::EVP_DecryptFinal_ex(ctx, out_ptr.offset(out_len as isize), &mut last_len);
|
||||
out_len += last_len;
|
||||
}
|
||||
let out_len = out_len as usize;
|
||||
let text = &out[..out_len];
|
||||
{
|
||||
//校验头部
|
||||
let src_net_packet = NetPacket::new(text)?;
|
||||
if src_net_packet.source() != net_packet.source() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
if src_net_packet.destination() != net_packet.destination() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
if src_net_packet.protocol() != net_packet.protocol() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
if src_net_packet.transport_protocol() != net_packet.transport_protocol() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
if src_net_packet.is_gateway() != net_packet.is_gateway() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
if src_net_packet.source_ttl() != net_packet.source_ttl() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
}
|
||||
net_packet.set_encrypt_flag(false);
|
||||
net_packet.set_data_len(out_len)?;
|
||||
net_packet.set_payload(&text[12..])?;
|
||||
Ok(())
|
||||
}
|
||||
/// net_packet 必须预留足够长度 大于 12+16+16
|
||||
/// data_len是有效载荷的长度
|
||||
pub fn encrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> io::Result<()> {
|
||||
let input = net_packet.buffer();
|
||||
let mut out = [0u8; 1024 * 5];
|
||||
let mut out_len = 0;
|
||||
let ctx = self.en_ctx;
|
||||
//将头部也参与加密
|
||||
unsafe {
|
||||
let out_ptr = out.as_mut_ptr();
|
||||
let in_len = input.len() as c_int;
|
||||
openssl_sys::EVP_EncryptUpdate(ctx, out_ptr, &mut out_len, input.as_ptr(), in_len);
|
||||
let mut last_len = 0;
|
||||
openssl_sys::EVP_EncryptFinal_ex(ctx, out_ptr.offset(out_len as isize), &mut last_len);
|
||||
out_len += last_len;
|
||||
}
|
||||
let out_len = out_len as usize;
|
||||
if out_len == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "ciphertext len err"));
|
||||
}
|
||||
//密文
|
||||
let ciphertext = &out[..out_len];
|
||||
net_packet.set_data_len(HEAD_LEN + out_len)?;
|
||||
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 finger = finger.calculate_finger(&nonce_raw, ciphertext);
|
||||
let src_data_len = net_packet.data_len();
|
||||
//设置实际长度
|
||||
net_packet.set_data_len(src_data_len + finger.len())?;
|
||||
|
||||
net_packet.buffer_mut()[src_data_len..].copy_from_slice(&finger);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
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();
|
||||
d.encrypt_ipv4(&mut p).unwrap();
|
||||
d.decrypt_ipv4(&mut p).unwrap();
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
use crate::cipher::Finger;
|
||||
use rand::RngCore;
|
||||
use ring::aead;
|
||||
use ring::aead::{LessSafeKey, UnboundKey};
|
||||
use std::io;
|
||||
|
||||
use crate::protocol::body::{SecretBody, AES_GCM_ENCRYPTION_RESERVED};
|
||||
use crate::protocol::NetPacket;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AesGcmCipher {
|
||||
pub(crate) cipher: AesGcmEnum,
|
||||
pub(crate) finger: Option<Finger>,
|
||||
}
|
||||
|
||||
pub enum AesGcmEnum {
|
||||
AesGCM128(LessSafeKey, [u8; 16]),
|
||||
AesGCM256(LessSafeKey, [u8; 32]),
|
||||
}
|
||||
|
||||
impl Clone for AesGcmEnum {
|
||||
fn clone(&self) -> Self {
|
||||
match &self {
|
||||
AesGcmEnum::AesGCM128(_, key) => {
|
||||
let c =
|
||||
LessSafeKey::new(UnboundKey::new(&aead::AES_128_GCM, key.as_slice()).unwrap());
|
||||
AesGcmEnum::AesGCM128(c, *key)
|
||||
}
|
||||
AesGcmEnum::AesGCM256(_, key) => {
|
||||
let c =
|
||||
LessSafeKey::new(UnboundKey::new(&aead::AES_256_GCM, key.as_slice()).unwrap());
|
||||
AesGcmEnum::AesGCM256(c, *key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AesGcmCipher {
|
||||
pub fn new_128(key: [u8; 16], finger: Option<Finger>) -> Self {
|
||||
let cipher = LessSafeKey::new(UnboundKey::new(&aead::AES_128_GCM, &key).unwrap());
|
||||
Self {
|
||||
cipher: AesGcmEnum::AesGCM128(cipher, key),
|
||||
finger,
|
||||
}
|
||||
}
|
||||
pub fn new_256(key: [u8; 32], finger: Option<Finger>) -> Self {
|
||||
let cipher = LessSafeKey::new(UnboundKey::new(&aead::AES_256_GCM, &key).unwrap());
|
||||
Self {
|
||||
cipher: AesGcmEnum::AesGCM256(cipher, key),
|
||||
finger,
|
||||
}
|
||||
}
|
||||
pub fn decrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> io::Result<()> {
|
||||
if !net_packet.is_encrypt() {
|
||||
//未加密的数据直接丢弃
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "not encrypt"));
|
||||
}
|
||||
if net_packet.payload().len() < AES_GCM_ENCRYPTION_RESERVED {
|
||||
log::error!("数据异常,长度小于{}", AES_GCM_ENCRYPTION_RESERVED);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "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())?;
|
||||
if let Some(finger) = &self.finger {
|
||||
let finger = finger.calculate_finger(&nonce_raw, secret_body.en_body());
|
||||
if &finger != secret_body.finger() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "ring aes finger err"));
|
||||
}
|
||||
}
|
||||
|
||||
let rs = match &self.cipher {
|
||||
AesGcmEnum::AesGCM128(cipher, _) => {
|
||||
cipher.open_in_place(nonce, aead::Aad::empty(), secret_body.en_body_mut())
|
||||
}
|
||||
AesGcmEnum::AesGCM256(cipher, _) => {
|
||||
cipher.open_in_place(nonce, aead::Aad::empty(), secret_body.en_body_mut())
|
||||
}
|
||||
};
|
||||
if let Err(e) = rs {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("解密失败:{}", e),
|
||||
));
|
||||
}
|
||||
net_packet.set_encrypt_flag(false);
|
||||
net_packet.set_data_len(net_packet.data_len() - AES_GCM_ENCRYPTION_RESERVED)?;
|
||||
return Ok(());
|
||||
}
|
||||
/// net_packet 必须预留足够长度
|
||||
/// data_len是有效载荷的长度
|
||||
/// 返回加密后载荷的长度
|
||||
pub fn encrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> io::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())?;
|
||||
secret_body.set_random(rand::thread_rng().next_u32());
|
||||
|
||||
let rs = match &self.cipher {
|
||||
AesGcmEnum::AesGCM128(cipher, _) => {
|
||||
cipher.seal_in_place_separate_tag(nonce, aead::Aad::empty(), secret_body.body_mut())
|
||||
}
|
||||
AesGcmEnum::AesGCM256(cipher, _) => {
|
||||
cipher.seal_in_place_separate_tag(nonce, aead::Aad::empty(), secret_body.body_mut())
|
||||
}
|
||||
};
|
||||
return match rs {
|
||||
Ok(tag) => {
|
||||
let tag = tag.as_ref();
|
||||
if tag.len() != 16 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("加密tag长度错误:{}", tag.len()),
|
||||
));
|
||||
}
|
||||
secret_body.set_tag(tag)?;
|
||||
if let Some(finger) = &self.finger {
|
||||
let finger = finger.calculate_finger(&nonce_raw, secret_body.en_body());
|
||||
secret_body.set_finger(&finger)?;
|
||||
}
|
||||
net_packet.set_encrypt_flag(true);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("加密失败:{}", e),
|
||||
)),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
use crate::protocol::NetPacket;
|
||||
use std::io;
|
||||
use {
|
||||
crate::protocol::body::{RsaSecretBody, RSA_ENCRYPTION_RESERVED},
|
||||
rand::Rng,
|
||||
rsa::pkcs8::der::Decode,
|
||||
rsa::RsaPublicKey,
|
||||
sha2::Digest,
|
||||
spki::{DecodePublicKey, EncodePublicKey},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RsaCipher {
|
||||
inner: Inner,
|
||||
}
|
||||
#[derive(Clone)]
|
||||
struct Inner {
|
||||
public_key: RsaPublicKey,
|
||||
}
|
||||
|
||||
impl RsaCipher {
|
||||
pub fn new(der: &[u8]) -> io::Result<Self> {
|
||||
match RsaPublicKey::from_public_key_der(der) {
|
||||
Ok(public_key) => {
|
||||
let inner = Inner { public_key };
|
||||
Ok(Self { inner })
|
||||
}
|
||||
Err(e) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("from_public_key_der failed {}", e),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finger(&self) -> io::Result<String> {
|
||||
match self.inner.public_key.to_public_key_der() {
|
||||
Ok(der) => match rsa::pkcs8::SubjectPublicKeyInfoRef::from_der(der.as_bytes()) {
|
||||
Ok(spki) => match spki.fingerprint_base64() {
|
||||
Ok(finger) => Ok(finger),
|
||||
Err(e) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("fingerprint_base64 error {}", e),
|
||||
)),
|
||||
},
|
||||
Err(e) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("from_der error {}", e),
|
||||
)),
|
||||
},
|
||||
Err(e) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("to_public_key_der error {}", e),
|
||||
)),
|
||||
}
|
||||
}
|
||||
pub fn public_key(&self) -> io::Result<&RsaPublicKey> {
|
||||
return Ok(&self.inner.public_key);
|
||||
}
|
||||
}
|
||||
|
||||
impl RsaCipher {
|
||||
/// net_packet 必须预留足够长度
|
||||
pub fn encrypt<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> io::Result<NetPacket<Vec<u8>>> {
|
||||
if net_packet.reserve() < RSA_ENCRYPTION_RESERVED {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "too short"));
|
||||
}
|
||||
let data_len = net_packet.data_len() + RSA_ENCRYPTION_RESERVED;
|
||||
net_packet.set_data_len(data_len)?;
|
||||
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 = RsaSecretBody::new(net_packet.payload_mut())?;
|
||||
let mut rng = rand::thread_rng();
|
||||
rng.fill(secret_body.random_mut());
|
||||
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
hasher.update(secret_body.body());
|
||||
hasher.update(nonce_raw);
|
||||
let key: [u8; 32] = hasher.finalize().into();
|
||||
secret_body.set_finger(&key[16..])?;
|
||||
match self.inner.public_key.encrypt(
|
||||
&mut rng,
|
||||
rsa::pkcs1v15::Pkcs1v15Encrypt,
|
||||
secret_body.buffer(),
|
||||
) {
|
||||
Ok(enc_data) => {
|
||||
let mut net_packet_e = NetPacket::new(vec![0; 12 + enc_data.len()])?;
|
||||
net_packet_e.buffer_mut()[..12].copy_from_slice(&net_packet.buffer()[..12]);
|
||||
net_packet_e.set_payload(&enc_data)?;
|
||||
Ok(net_packet_e)
|
||||
}
|
||||
Err(e) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("encrypt failed {}", e),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
use crate::cipher::Finger;
|
||||
use crate::protocol::{NetPacket, HEAD_LEN};
|
||||
use libsm::sm4::cipher_mode::CipherMode;
|
||||
use libsm::sm4::Sm4CipherMode;
|
||||
use rand::RngCore;
|
||||
use std::io;
|
||||
|
||||
pub struct Sm4CbcCipher {
|
||||
key: [u8; 16],
|
||||
pub(crate) cipher: Sm4CipherMode,
|
||||
pub(crate) finger: Option<Finger>,
|
||||
}
|
||||
|
||||
impl Clone for Sm4CbcCipher {
|
||||
fn clone(&self) -> Self {
|
||||
let cipher = Sm4CipherMode::new(&self.key, CipherMode::Cbc).unwrap();
|
||||
Self {
|
||||
key: self.key,
|
||||
cipher,
|
||||
finger: self.finger.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sm4CbcCipher {
|
||||
pub fn key(&self) -> &[u8] {
|
||||
&self.key
|
||||
}
|
||||
}
|
||||
|
||||
impl Sm4CbcCipher {
|
||||
pub fn new_128(key: [u8; 16], finger: Option<Finger>) -> Self {
|
||||
let cipher = Sm4CipherMode::new(&key, CipherMode::Cbc).unwrap();
|
||||
Self {
|
||||
key,
|
||||
cipher,
|
||||
finger,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> io::Result<()> {
|
||||
if !net_packet.is_encrypt() {
|
||||
//未加密的数据直接丢弃
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "not encrypt"));
|
||||
}
|
||||
|
||||
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 len = net_packet.payload().len();
|
||||
if len < 12 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "payload len <12"));
|
||||
}
|
||||
let secret_body = &net_packet.payload()[..len - 12];
|
||||
let finger = finger.calculate_finger(&nonce_raw, secret_body);
|
||||
if &finger != &net_packet.payload()[len - 12..] {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "finger err"));
|
||||
}
|
||||
net_packet.set_data_len(net_packet.data_len() - finger.len())?;
|
||||
}
|
||||
let payload = net_packet.payload();
|
||||
let len = payload.len();
|
||||
if len < 16 || len > 1024 * 4 {
|
||||
log::error!("数据异常,长度{}小于16或大于4096", len);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
let mut out = [0u8; 1024 * 4];
|
||||
let data = &payload[..len - 16];
|
||||
let iv = &payload[len - 16..];
|
||||
match self.cipher.decrypt(data, iv, &mut out) {
|
||||
Ok(len) => {
|
||||
let src_net_packet = NetPacket::new(&out[..len])?;
|
||||
if src_net_packet.source() != net_packet.source() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
if src_net_packet.destination() != net_packet.destination() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
if src_net_packet.protocol() != net_packet.protocol() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
if src_net_packet.transport_protocol() != net_packet.transport_protocol() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
if src_net_packet.is_gateway() != net_packet.is_gateway() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
if src_net_packet.source_ttl() != net_packet.source_ttl() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
net_packet.set_data_len(len)?;
|
||||
net_packet.set_payload(src_net_packet.payload())?;
|
||||
net_packet.set_encrypt_flag(false);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("sm4_cbc解密失败:{}", e),
|
||||
)),
|
||||
}
|
||||
}
|
||||
/// net_packet 必须预留足够长度
|
||||
/// data_len是有效载荷的长度
|
||||
pub fn encrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> io::Result<()> {
|
||||
let mut out = [0u8; 1024 * 4];
|
||||
let mut iv = [0u8; 16];
|
||||
rand::thread_rng().fill_bytes(&mut iv);
|
||||
if net_packet.data_len() > 1024 * 4 - 32 {
|
||||
log::error!(
|
||||
"数据异常,长度{}大于1024 * 4 - 32",
|
||||
net_packet.buffer().len()
|
||||
);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
match self.cipher.encrypt(net_packet.buffer(), &iv, &mut out) {
|
||||
Ok(len) => {
|
||||
net_packet.set_data_len(HEAD_LEN + len + 16)?;
|
||||
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 finger = finger.calculate_finger(&nonce_raw, net_packet.payload());
|
||||
let src_data_len = net_packet.data_len();
|
||||
//设置实际长度
|
||||
net_packet.set_data_len(src_data_len + finger.len())?;
|
||||
|
||||
net_packet.buffer_mut()[src_data_len..].copy_from_slice(&finger);
|
||||
}
|
||||
net_packet.set_encrypt_flag(true);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("sm4_cbc加密失败:{}", e),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sm4_ecb() {
|
||||
let d = Sm4CbcCipher::new_128([0; 16], Some(Finger::new("123")));
|
||||
let mut p = NetPacket::new_encrypt([1; 1024]).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 = Sm4CbcCipher::new_128([0; 16], None);
|
||||
let mut p = NetPacket::new_encrypt([1; 102]).unwrap();
|
||||
let src = p.buffer().to_vec();
|
||||
d.encrypt_ipv4(&mut p).unwrap();
|
||||
d.decrypt_ipv4(&mut p).unwrap();
|
||||
assert_eq!(p.buffer(), &src)
|
||||
}
|
||||
@@ -0,0 +1,367 @@
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use rand::Rng;
|
||||
|
||||
use tun::device::IFace;
|
||||
|
||||
use crate::channel::context::Context;
|
||||
use crate::channel::idle::Idle;
|
||||
use crate::channel::punch::{NatInfo, Punch};
|
||||
use crate::channel::{init_channel, init_context, Route, RouteKey};
|
||||
use crate::cipher::Cipher;
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
use crate::cipher::RsaCipher;
|
||||
use crate::core::Config;
|
||||
use crate::external_route::{AllowExternalRoute, ExternalRoute};
|
||||
use crate::handle::handshaker::Handshake;
|
||||
use crate::handle::maintain::PunchReceiver;
|
||||
use crate::handle::recv_data::RecvDataHandler;
|
||||
use crate::handle::{
|
||||
maintain, tun_tap, BaseConfigInfo, ConnectStatus, CurrentDeviceInfo, PeerDeviceInfo,
|
||||
};
|
||||
use crate::nat::NatTest;
|
||||
use crate::util::{
|
||||
Scheduler, SingleU64Adder, StopManager, U64Adder, WatchSingleU64Adder, WatchU64Adder,
|
||||
};
|
||||
use crate::{nat, tun_tap_device, DeviceInfo, VntCallback};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vnt {
|
||||
stop_manager: StopManager,
|
||||
config: Config,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
nat_test: NatTest,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
context: Context,
|
||||
peer_nat_info_map: Arc<RwLock<HashMap<Ipv4Addr, NatInfo>>>,
|
||||
down_count_watcher: WatchU64Adder,
|
||||
up_count_watcher: WatchSingleU64Adder,
|
||||
}
|
||||
|
||||
impl Vnt {
|
||||
pub fn new<Call: VntCallback>(config: Config, callback: Call) -> io::Result<Self> {
|
||||
log::info!("config:{:?}", config);
|
||||
//服务端非对称加密
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
let rsa_cipher: Arc<Mutex<Option<RsaCipher>>> = Arc::new(Mutex::new(None));
|
||||
//服务端对称加密
|
||||
let server_cipher: Cipher = if config.server_encrypt {
|
||||
let mut key = [0u8; 32];
|
||||
rand::thread_rng().fill(&mut key);
|
||||
Cipher::new_key(key, config.token.clone())?
|
||||
} else {
|
||||
Cipher::None
|
||||
};
|
||||
let finger = if config.finger {
|
||||
Some(config.token.clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
//客户端对称加密
|
||||
let client_cipher =
|
||||
Cipher::new_password(config.cipher_model, config.password.clone(), finger);
|
||||
//当前设备信息
|
||||
let current_device = Arc::new(AtomicCell::new(CurrentDeviceInfo::new0(
|
||||
config.server_address,
|
||||
)));
|
||||
//设备列表
|
||||
let device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>> =
|
||||
Arc::new(Mutex::new((0, Vec::with_capacity(16))));
|
||||
//基础信息
|
||||
let config_info = BaseConfigInfo::new(
|
||||
config.name.clone(),
|
||||
config.token.clone(),
|
||||
config.ip,
|
||||
config.password.is_some(),
|
||||
config.device_id.clone(),
|
||||
config.server_address_str.clone(),
|
||||
);
|
||||
let ports = config.ports.as_ref().map_or(vec![0, 0], |v| {
|
||||
if v.is_empty() {
|
||||
vec![0, 0]
|
||||
} else {
|
||||
v.clone()
|
||||
}
|
||||
});
|
||||
//通道上下文
|
||||
let (context, tcp_listener) = init_context(
|
||||
ports,
|
||||
config.use_channel_type,
|
||||
config.first_latency,
|
||||
config.tcp,
|
||||
config.packet_loss_rate,
|
||||
config.packet_delay,
|
||||
)?;
|
||||
let local_ipv4 = nat::local_ipv4();
|
||||
let local_ipv6 = nat::local_ipv6();
|
||||
let udp_ports = context.main_local_udp_port()?;
|
||||
let tcp_port = tcp_listener.local_addr()?.port();
|
||||
//nat检测工具
|
||||
let nat_test = NatTest::new(
|
||||
context.channel_num(),
|
||||
config.stun_server.clone(),
|
||||
local_ipv4,
|
||||
local_ipv6,
|
||||
udp_ports,
|
||||
tcp_port,
|
||||
);
|
||||
|
||||
// 虚拟网卡
|
||||
let device = tun_tap_device::create_device(&config)?;
|
||||
let tun_info = DeviceInfo::new(device.name()?, device.version()?);
|
||||
callback.create_tun(tun_info);
|
||||
// 服务停止管理器
|
||||
let stop_manager = {
|
||||
let callback = callback.clone();
|
||||
StopManager::new(move || callback.stop())
|
||||
};
|
||||
// 定时器
|
||||
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")]
|
||||
let proxy_map = if !config.out_ips.is_empty() && !config.no_proxy {
|
||||
Some(crate::ip_proxy::init_proxy(
|
||||
context.clone(),
|
||||
scheduler.clone(),
|
||||
stop_manager.clone(),
|
||||
current_device.clone(),
|
||||
client_cipher.clone(),
|
||||
)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let (punch_sender, punch_receiver) = maintain::punch_channel();
|
||||
let peer_nat_info_map: Arc<RwLock<HashMap<Ipv4Addr, NatInfo>>> =
|
||||
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();
|
||||
let handler = RecvDataHandler::new(
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
rsa_cipher,
|
||||
server_cipher.clone(),
|
||||
client_cipher.clone(),
|
||||
current_device.clone(),
|
||||
device.clone(),
|
||||
device_list.clone(),
|
||||
config_info.clone(),
|
||||
nat_test.clone(),
|
||||
callback.clone(),
|
||||
punch_sender,
|
||||
peer_nat_info_map.clone(),
|
||||
external_route.clone(),
|
||||
out_external_route,
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
proxy_map.clone(),
|
||||
down_counter,
|
||||
handshake.clone(),
|
||||
);
|
||||
|
||||
//初始化网络数据通道
|
||||
let (udp_socket_sender, tcp_socket_sender) =
|
||||
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(),
|
||||
);
|
||||
let up_counter = SingleU64Adder::new();
|
||||
let up_count_watcher = up_counter.watch();
|
||||
tun_tap::tun_handler::start(
|
||||
stop_manager.clone(),
|
||||
context.clone(),
|
||||
device.clone(),
|
||||
current_device.clone(),
|
||||
external_route,
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
proxy_map,
|
||||
client_cipher.clone(),
|
||||
server_cipher.clone(),
|
||||
config.parallel,
|
||||
up_counter,
|
||||
)?;
|
||||
maintain::idle_gateway(
|
||||
&scheduler,
|
||||
context.clone(),
|
||||
current_device.clone(),
|
||||
config_info.clone(),
|
||||
tcp_socket_sender.clone(),
|
||||
callback.clone(),
|
||||
0,
|
||||
handshake,
|
||||
);
|
||||
{
|
||||
let context = context.clone();
|
||||
let nat_test = nat_test.clone();
|
||||
let device_list = device_list.clone();
|
||||
let current_device = current_device.clone();
|
||||
if !config.use_channel_type.is_only_relay() {
|
||||
// 定时nat探测
|
||||
maintain::retrieve_nat_type(
|
||||
&scheduler,
|
||||
context.clone(),
|
||||
nat_test.clone(),
|
||||
udp_socket_sender,
|
||||
);
|
||||
}
|
||||
//延迟启动
|
||||
scheduler.timeout(Duration::from_secs(3), move |scheduler| {
|
||||
start(
|
||||
scheduler,
|
||||
context,
|
||||
nat_test,
|
||||
device_list,
|
||||
current_device,
|
||||
client_cipher,
|
||||
server_cipher,
|
||||
punch_receiver,
|
||||
config_info,
|
||||
punch,
|
||||
callback,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
stop_manager,
|
||||
config,
|
||||
current_device,
|
||||
nat_test,
|
||||
device_list,
|
||||
context,
|
||||
peer_nat_info_map,
|
||||
down_count_watcher,
|
||||
up_count_watcher,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start<Call: VntCallback>(
|
||||
scheduler: &Scheduler,
|
||||
context: Context,
|
||||
nat_test: NatTest,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
punch_receiver: PunchReceiver,
|
||||
config_info: BaseConfigInfo,
|
||||
punch: Punch,
|
||||
callback: Call,
|
||||
) {
|
||||
// 定时心跳
|
||||
maintain::heartbeat(
|
||||
&scheduler,
|
||||
context.clone(),
|
||||
current_device.clone(),
|
||||
device_list.clone(),
|
||||
client_cipher.clone(),
|
||||
server_cipher.clone(),
|
||||
);
|
||||
// 路由空闲检测逻辑
|
||||
let idle = Idle::new(Duration::from_secs(10), context.clone());
|
||||
// 定时空闲检查
|
||||
maintain::idle_route(
|
||||
&scheduler,
|
||||
idle,
|
||||
context.clone(),
|
||||
current_device.clone(),
|
||||
callback,
|
||||
);
|
||||
// 定时客户端中继检测
|
||||
if !context.use_channel_type().is_only_p2p() {
|
||||
maintain::client_relay(
|
||||
&scheduler,
|
||||
context.clone(),
|
||||
current_device.clone(),
|
||||
device_list.clone(),
|
||||
client_cipher.clone(),
|
||||
);
|
||||
}
|
||||
// 定时地址探测
|
||||
maintain::addr_request(
|
||||
&scheduler,
|
||||
context.clone(),
|
||||
current_device.clone(),
|
||||
server_cipher.clone(),
|
||||
config_info.clone(),
|
||||
);
|
||||
if !context.use_channel_type().is_only_relay() {
|
||||
// 定时打洞
|
||||
maintain::punch(
|
||||
&scheduler,
|
||||
context.clone(),
|
||||
nat_test.clone(),
|
||||
device_list.clone(),
|
||||
current_device.clone(),
|
||||
client_cipher.clone(),
|
||||
punch_receiver,
|
||||
punch,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl Vnt {
|
||||
pub fn name(&self) -> &str {
|
||||
&self.config.name
|
||||
}
|
||||
pub fn server_encrypt(&self) -> bool {
|
||||
self.config.server_encrypt
|
||||
}
|
||||
pub fn client_encrypt(&self) -> bool {
|
||||
self.config.password.is_some()
|
||||
}
|
||||
pub fn current_device(&self) -> CurrentDeviceInfo {
|
||||
self.current_device.load()
|
||||
}
|
||||
pub fn peer_nat_info(&self, ip: &Ipv4Addr) -> Option<NatInfo> {
|
||||
self.peer_nat_info_map.read().get(ip).cloned()
|
||||
}
|
||||
pub fn connection_status(&self) -> ConnectStatus {
|
||||
self.current_device.load().status
|
||||
}
|
||||
pub fn nat_info(&self) -> NatInfo {
|
||||
self.nat_test.nat_info()
|
||||
}
|
||||
pub fn device_list(&self) -> Vec<PeerDeviceInfo> {
|
||||
let device_list_lock = self.device_list.lock();
|
||||
let (_epoch, device_list) = device_list_lock.clone();
|
||||
drop(device_list_lock);
|
||||
device_list
|
||||
}
|
||||
pub fn route(&self, ip: &Ipv4Addr) -> Option<Route> {
|
||||
self.context.route_table.route_one(ip)
|
||||
}
|
||||
pub fn is_gateway(&self, ip: &Ipv4Addr) -> bool {
|
||||
self.current_device.load().is_gateway(ip)
|
||||
}
|
||||
pub fn route_key(&self, route_key: &RouteKey) -> Option<Ipv4Addr> {
|
||||
self.context.route_table.route_to_id(route_key)
|
||||
}
|
||||
pub fn route_table(&self) -> Vec<(Ipv4Addr, Vec<Route>)> {
|
||||
self.context.route_table.route_table()
|
||||
}
|
||||
pub fn up_stream(&self) -> u64 {
|
||||
self.up_count_watcher.get()
|
||||
}
|
||||
pub fn down_stream(&self) -> u64 {
|
||||
self.down_count_watcher.get()
|
||||
}
|
||||
pub fn stop(&self) {
|
||||
self.stop_manager.stop()
|
||||
}
|
||||
pub fn wait(&self) {
|
||||
self.stop_manager.wait()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
use std::io;
|
||||
use std::net::{Ipv4Addr, SocketAddr};
|
||||
|
||||
pub use conn::Vnt;
|
||||
|
||||
use crate::channel::punch::PunchModel;
|
||||
use crate::channel::UseChannelType;
|
||||
use crate::cipher::CipherModel;
|
||||
|
||||
mod conn;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Config {
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
pub tap: bool,
|
||||
pub token: String,
|
||||
pub device_id: String,
|
||||
pub name: String,
|
||||
pub server_address: SocketAddr,
|
||||
pub server_address_str: String,
|
||||
pub stun_server: Vec<String>,
|
||||
pub in_ips: Vec<(u32, u32, Ipv4Addr)>,
|
||||
pub out_ips: Vec<(u32, u32)>,
|
||||
pub password: Option<String>,
|
||||
pub mtu: Option<u32>,
|
||||
pub tcp: bool,
|
||||
pub ip: Option<Ipv4Addr>,
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
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<Vec<u16>>,
|
||||
pub first_latency: bool,
|
||||
#[cfg(not(target_os = "android"))]
|
||||
pub device_name: Option<String>,
|
||||
#[cfg(target_os = "android")]
|
||||
pub device_fd: i32,
|
||||
pub use_channel_type: UseChannelType,
|
||||
//控制丢包率
|
||||
pub packet_loss_rate: Option<f64>,
|
||||
pub packet_delay: u32,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new(
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))] tap: bool,
|
||||
token: String,
|
||||
device_id: String,
|
||||
name: String,
|
||||
server_address: SocketAddr,
|
||||
server_address_str: String,
|
||||
mut stun_server: Vec<String>,
|
||||
in_ips: Vec<(u32, u32, Ipv4Addr)>,
|
||||
out_ips: Vec<(u32, u32)>,
|
||||
password: Option<String>,
|
||||
mtu: Option<u32>,
|
||||
tcp: bool,
|
||||
ip: Option<Ipv4Addr>,
|
||||
#[cfg(feature = "ip_proxy")] no_proxy: bool,
|
||||
server_encrypt: bool,
|
||||
parallel: usize,
|
||||
cipher_model: CipherModel,
|
||||
finger: bool,
|
||||
punch_model: PunchModel,
|
||||
ports: Option<Vec<u16>>,
|
||||
first_latency: bool,
|
||||
#[cfg(not(target_os = "android"))] device_name: Option<String>,
|
||||
#[cfg(target_os = "android")] device_fd: i32,
|
||||
use_channel_type: UseChannelType,
|
||||
packet_loss_rate: Option<f64>,
|
||||
packet_delay: u32,
|
||||
) -> io::Result<Self> {
|
||||
for x in stun_server.iter_mut() {
|
||||
if !x.contains(":") {
|
||||
x.push_str(":3478");
|
||||
}
|
||||
}
|
||||
if token.is_empty() || token.len() > 128 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "token too long"));
|
||||
}
|
||||
if device_id.is_empty() || device_id.len() > 128 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "device_id too long"));
|
||||
}
|
||||
if name.is_empty() || name.len() > 128 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "name too long"));
|
||||
}
|
||||
Ok(Self {
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
tap,
|
||||
token,
|
||||
device_id,
|
||||
name,
|
||||
server_address,
|
||||
server_address_str,
|
||||
stun_server,
|
||||
in_ips,
|
||||
out_ips,
|
||||
password,
|
||||
mtu,
|
||||
tcp,
|
||||
ip,
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
no_proxy,
|
||||
server_encrypt,
|
||||
parallel,
|
||||
cipher_model,
|
||||
finger,
|
||||
punch_model,
|
||||
ports,
|
||||
first_latency,
|
||||
#[cfg(not(target_os = "android"))]
|
||||
device_name,
|
||||
#[cfg(target_os = "android")]
|
||||
device_fd,
|
||||
use_channel_type,
|
||||
packet_loss_rate,
|
||||
packet_delay,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
|
||||
// 目标ip,子网掩码,网关
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ExternalRoute {
|
||||
route_table: Vec<(u32, u32, Ipv4Addr)>,
|
||||
}
|
||||
|
||||
impl ExternalRoute {
|
||||
pub fn new(route_table: Vec<(u32, u32, Ipv4Addr)>) -> Self {
|
||||
Self { route_table }
|
||||
}
|
||||
pub fn route(&self, ip: &Ipv4Addr) -> Option<Ipv4Addr> {
|
||||
if self.route_table.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let ip = u32::from_be_bytes(ip.octets());
|
||||
for (dest, mask, gateway) in self.route_table.iter() {
|
||||
if *mask & ip == *mask & *dest {
|
||||
return Some(*gateway);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn to_route(&self) -> Vec<(Ipv4Addr, Ipv4Addr)> {
|
||||
self.route_table
|
||||
.iter()
|
||||
.map(|(dest, mask, _)| (Ipv4Addr::from(*dest & *mask), Ipv4Addr::from(*mask)))
|
||||
.collect::<Vec<(Ipv4Addr, Ipv4Addr)>>()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AllowExternalRoute {
|
||||
route_table: Arc<Vec<(u32, u32)>>,
|
||||
}
|
||||
|
||||
impl AllowExternalRoute {
|
||||
pub fn new(route_table: Vec<(u32, u32)>) -> Self {
|
||||
Self {
|
||||
route_table: Arc::new(route_table),
|
||||
}
|
||||
}
|
||||
pub fn allow(&self, ip: &Ipv4Addr) -> bool {
|
||||
if self.route_table.is_empty() {
|
||||
return false;
|
||||
}
|
||||
let ip = u32::from_be_bytes(ip.octets());
|
||||
for (dest, mask) in self.route_table.iter() {
|
||||
if *mask & ip == *mask & *dest {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
use rsa::RsaPublicKey;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::io;
|
||||
use std::net::{Ipv4Addr, SocketAddr};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DeviceInfo {
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
impl Display for DeviceInfo {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(&format!("name={} ,version={}", self.name, self.version))
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceInfo {
|
||||
pub fn new(name: String, version: String) -> Self {
|
||||
return Self { name, version };
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ConnectInfo {
|
||||
// 第几次连接,从1开始
|
||||
pub count: usize,
|
||||
// 服务端地址
|
||||
pub address: SocketAddr,
|
||||
}
|
||||
|
||||
impl Display for ConnectInfo {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(&format!("count={} ,address={}", self.count, self.address))
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectInfo {
|
||||
pub fn new(count: usize, address: SocketAddr) -> Self {
|
||||
Self { count, address }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HandshakeInfo {
|
||||
//服务端公钥
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
pub public_key: Option<RsaPublicKey>,
|
||||
//服务端指纹
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
pub finger: Option<String>,
|
||||
//服务端版本
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
impl Display for HandshakeInfo {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
return match &self.finger {
|
||||
None => f.write_str(&format!("no_secret server version={}", self.version)),
|
||||
Some(finger) => f.write_str(&format!(
|
||||
"finger={} ,server version={}",
|
||||
finger, self.version
|
||||
)),
|
||||
};
|
||||
#[cfg(not(feature = "server_encrypt"))]
|
||||
f.write_str(&format!("server version={}", self.version))
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
impl HandshakeInfo {
|
||||
pub fn new(public_key: RsaPublicKey, finger: String, version: String) -> Self {
|
||||
Self {
|
||||
public_key: Some(public_key),
|
||||
finger: Some(finger),
|
||||
version,
|
||||
}
|
||||
}
|
||||
pub fn new_no_secret(version: String) -> Self {
|
||||
Self {
|
||||
public_key: None,
|
||||
finger: None,
|
||||
version,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "server_encrypt"))]
|
||||
impl HandshakeInfo {
|
||||
pub fn new_no_secret(version: String) -> Self {
|
||||
Self { version }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RegisterInfo {
|
||||
//本机虚拟IP
|
||||
pub virtual_ip: Ipv4Addr,
|
||||
//子网掩码
|
||||
pub virtual_netmask: Ipv4Addr,
|
||||
//虚拟网关
|
||||
pub virtual_gateway: Ipv4Addr,
|
||||
}
|
||||
|
||||
impl Display for RegisterInfo {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(&format!(
|
||||
"ip={} ,netmask={} ,gateway={}",
|
||||
self.virtual_ip, self.virtual_netmask, self.virtual_gateway,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl RegisterInfo {
|
||||
pub fn new(virtual_ip: Ipv4Addr, virtual_netmask: Ipv4Addr, virtual_gateway: Ipv4Addr) -> Self {
|
||||
Self {
|
||||
virtual_ip,
|
||||
virtual_netmask,
|
||||
virtual_gateway,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ErrorInfo {
|
||||
pub code: ErrorType,
|
||||
pub msg: Option<String>,
|
||||
pub source: Option<io::Error>,
|
||||
}
|
||||
|
||||
impl Display for ErrorInfo {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(&format!("ErrorType={:?} ", self.code))?;
|
||||
if let Some(msg) = &self.msg {
|
||||
f.write_str(&format!(",msg={:?} ", msg))?;
|
||||
}
|
||||
if let Some(source) = &self.source {
|
||||
f.write_str(&format!(",source={:?} ", source))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ErrorInfo {
|
||||
pub fn new(code: ErrorType) -> Self {
|
||||
Self {
|
||||
code,
|
||||
msg: None,
|
||||
source: None,
|
||||
}
|
||||
}
|
||||
pub fn new_msg(code: ErrorType, msg: String) -> Self {
|
||||
Self {
|
||||
code,
|
||||
msg: Some(msg),
|
||||
source: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum ErrorType {
|
||||
TokenError,
|
||||
Disconnect,
|
||||
AddressExhausted,
|
||||
IpAlreadyExists,
|
||||
InvalidIp,
|
||||
LocalIpExists,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl Into<u8> for ErrorType {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
ErrorType::TokenError => 1,
|
||||
ErrorType::Disconnect => 2,
|
||||
ErrorType::AddressExhausted => 3,
|
||||
ErrorType::IpAlreadyExists => 4,
|
||||
ErrorType::InvalidIp => 5,
|
||||
ErrorType::LocalIpExists => 6,
|
||||
ErrorType::Unknown => 255,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait VntCallback: Clone + Send + Sync + 'static {
|
||||
/// 启动成功
|
||||
fn success(&self) {}
|
||||
|
||||
/// 创建网卡的信息
|
||||
fn create_tun(&self, _info: DeviceInfo) {}
|
||||
/// 连接
|
||||
fn connect(&self, _info: ConnectInfo) {}
|
||||
/// 握手,返回false则拒绝握手,可在此处检查服务端信息
|
||||
fn handshake(&self, _info: HandshakeInfo) -> bool {
|
||||
true
|
||||
}
|
||||
/// 注册,返回false则拒绝注册
|
||||
fn register(&self, _info: RegisterInfo) -> bool {
|
||||
true
|
||||
}
|
||||
/// 异常信息
|
||||
fn error(&self, _info: ErrorInfo) {}
|
||||
/// 服务停止
|
||||
fn stop(&self) {}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use protobuf::Message;
|
||||
|
||||
use crate::channel::context::Context;
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
use crate::cipher::RsaCipher;
|
||||
use crate::handle::{GATEWAY_IP, SELF_IP};
|
||||
use crate::proto::message::HandshakeRequest;
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
use crate::proto::message::SecretHandshakeRequest;
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
use crate::protocol::body::RSA_ENCRYPTION_RESERVED;
|
||||
use crate::protocol::{service_packet, NetPacket, Protocol, Version, MAX_TTL};
|
||||
|
||||
pub enum HandshakeEnum {
|
||||
NotSecret,
|
||||
KeyError,
|
||||
Timeout,
|
||||
ServerError(String),
|
||||
Other(String),
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct Handshake {
|
||||
time: Arc<AtomicCell<Instant>>,
|
||||
}
|
||||
impl Handshake {
|
||||
pub fn new() -> Self {
|
||||
Handshake {
|
||||
time: Arc::new(AtomicCell::new(Instant::now() - Duration::from_secs(60))),
|
||||
}
|
||||
}
|
||||
pub fn send(&self, context: &Context, secret: bool, addr: SocketAddr) -> io::Result<()> {
|
||||
let last = self.time.load();
|
||||
//短时间不重复发送
|
||||
if last.elapsed() < Duration::from_secs(5) {
|
||||
return Ok(());
|
||||
}
|
||||
let request_packet = handshake_request_packet(secret)?;
|
||||
log::info!("发送握手请求,secret={},{:?}", secret, addr);
|
||||
context.send_default(request_packet.buffer(), addr)?;
|
||||
self.time.store(Instant::now());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// 第一次握手数据
|
||||
pub fn handshake_request_packet(secret: bool) -> io::Result<NetPacket<Vec<u8>>> {
|
||||
let mut request = HandshakeRequest::new();
|
||||
request.secret = secret;
|
||||
request.version = crate::VNT_VERSION.to_string();
|
||||
let bytes = request.write_to_bytes().map_err(|e| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("handshake_request_packet {:?}", e),
|
||||
)
|
||||
})?;
|
||||
let buf = vec![0u8; 12 + bytes.len()];
|
||||
let mut net_packet = NetPacket::new(buf)?;
|
||||
net_packet.set_version(Version::V1);
|
||||
net_packet.set_gateway_flag(true);
|
||||
net_packet.set_destination(GATEWAY_IP);
|
||||
net_packet.set_source(SELF_IP);
|
||||
net_packet.set_protocol(Protocol::Service);
|
||||
net_packet.set_transport_protocol(service_packet::Protocol::HandshakeRequest.into());
|
||||
net_packet.first_set_ttl(MAX_TTL);
|
||||
net_packet.set_payload(&bytes)?;
|
||||
Ok(net_packet)
|
||||
}
|
||||
|
||||
/// 第二次加密握手
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
pub fn secret_handshake_request_packet(
|
||||
rsa_cipher: &RsaCipher,
|
||||
token: String,
|
||||
key: &[u8],
|
||||
) -> io::Result<NetPacket<Vec<u8>>> {
|
||||
let mut request = SecretHandshakeRequest::new();
|
||||
request.token = token;
|
||||
request.key = key.to_vec();
|
||||
let bytes = request.write_to_bytes().map_err(|e| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("secret_handshake_request_packet {:?}", e),
|
||||
)
|
||||
})?;
|
||||
let mut net_packet = NetPacket::new0(
|
||||
12 + bytes.len(),
|
||||
vec![0u8; 12 + bytes.len() + RSA_ENCRYPTION_RESERVED],
|
||||
)?;
|
||||
net_packet.set_version(Version::V1);
|
||||
net_packet.set_gateway_flag(true);
|
||||
net_packet.set_destination(GATEWAY_IP);
|
||||
net_packet.set_source(SELF_IP);
|
||||
net_packet.set_protocol(Protocol::Service);
|
||||
net_packet.set_transport_protocol(service_packet::Protocol::SecretHandshakeRequest.into());
|
||||
net_packet.first_set_ttl(MAX_TTL);
|
||||
net_packet.set_payload(&bytes)?;
|
||||
Ok(rsa_cipher.encrypt(&mut net_packet)?)
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
use std::net::ToSocketAddrs;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
|
||||
use crate::channel::context::Context;
|
||||
use crate::cipher::Cipher;
|
||||
use crate::handle::{BaseConfigInfo, CurrentDeviceInfo};
|
||||
use crate::protocol::body::ENCRYPTION_RESERVED;
|
||||
use crate::protocol::{control_packet, NetPacket, Protocol, Version, MAX_TTL};
|
||||
use crate::util::Scheduler;
|
||||
|
||||
pub fn addr_request(
|
||||
scheduler: &Scheduler,
|
||||
context: Context,
|
||||
current_device_info: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
server_cipher: Cipher,
|
||||
config: BaseConfigInfo,
|
||||
) {
|
||||
addr_request0(&context, ¤t_device_info, &server_cipher, &config);
|
||||
// 17秒发送一次
|
||||
let rs = scheduler.timeout(Duration::from_secs(17), |s| {
|
||||
addr_request(s, context, current_device_info, server_cipher, config)
|
||||
});
|
||||
if !rs {
|
||||
log::info!("定时任务停止");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addr_request0(
|
||||
context: &Context,
|
||||
current_device: &AtomicCell<CurrentDeviceInfo>,
|
||||
server_cipher: &Cipher,
|
||||
config: &BaseConfigInfo,
|
||||
) {
|
||||
let mut current_dev = current_device.load();
|
||||
// 探测服务端地址变化
|
||||
if let Ok(mut addr) = config.server_addr.to_socket_addrs() {
|
||||
if let Some(addr) = addr.next() {
|
||||
if addr != current_dev.connect_server {
|
||||
let mut tmp = current_dev.clone();
|
||||
tmp.connect_server = addr;
|
||||
let rs = current_device.compare_exchange(current_dev, tmp);
|
||||
current_dev.connect_server = addr;
|
||||
log::info!(
|
||||
"服务端地址变化,旧地址:{},新地址:{},替换结果:{}",
|
||||
current_dev.connect_server,
|
||||
addr,
|
||||
rs.is_ok()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if current_dev.connect_server.is_ipv4() {
|
||||
// 如果连接的是ipv4服务,则探测公网端口
|
||||
let gateway_ip = current_dev.virtual_gateway;
|
||||
let src_ip = current_dev.virtual_ip;
|
||||
let mut packet = NetPacket::new_encrypt([0; 12 + ENCRYPTION_RESERVED]).unwrap();
|
||||
packet.set_version(Version::V1);
|
||||
packet.set_gateway_flag(true);
|
||||
packet.set_protocol(Protocol::Control);
|
||||
packet.set_transport_protocol(control_packet::Protocol::AddrRequest.into());
|
||||
packet.first_set_ttl(MAX_TTL);
|
||||
packet.set_source(src_ip);
|
||||
packet.set_destination(gateway_ip);
|
||||
if let Err(e) = server_cipher.encrypt_ipv4(&mut packet) {
|
||||
log::warn!("AddrRequest err={:?}", e)
|
||||
} else {
|
||||
context.try_send_all_main(packet.buffer(), current_dev.connect_server);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::Mutex;
|
||||
use rand::prelude::SliceRandom;
|
||||
|
||||
use crate::channel::context::Context;
|
||||
use crate::cipher::Cipher;
|
||||
use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo};
|
||||
use crate::protocol::body::ENCRYPTION_RESERVED;
|
||||
use crate::protocol::control_packet::PingPacket;
|
||||
use crate::protocol::{control_packet, NetPacket, Protocol, Version};
|
||||
use crate::util::Scheduler;
|
||||
|
||||
/// 定时发送心跳包
|
||||
pub fn heartbeat(
|
||||
scheduler: &Scheduler,
|
||||
context: Context,
|
||||
current_device_info: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
) {
|
||||
heartbeat0(
|
||||
&context,
|
||||
¤t_device_info.load(),
|
||||
&device_list,
|
||||
&client_cipher,
|
||||
&server_cipher,
|
||||
);
|
||||
// 心跳包 3秒发送一次
|
||||
let rs = scheduler.timeout(Duration::from_secs(3), |s| {
|
||||
heartbeat(
|
||||
s,
|
||||
context,
|
||||
current_device_info,
|
||||
device_list,
|
||||
client_cipher,
|
||||
server_cipher,
|
||||
)
|
||||
});
|
||||
if !rs {
|
||||
log::info!("定时任务停止");
|
||||
}
|
||||
}
|
||||
|
||||
fn heartbeat0(
|
||||
context: &Context,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
device_list: &Mutex<(u16, Vec<PeerDeviceInfo>)>,
|
||||
client_cipher: &Cipher,
|
||||
server_cipher: &Cipher,
|
||||
) {
|
||||
let gateway_ip = current_device.virtual_gateway;
|
||||
let src_ip = current_device.virtual_ip;
|
||||
// 可能服务器ip发生变化,导致发送失败
|
||||
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)
|
||||
{
|
||||
log::warn!("heartbeat err={:?}", e)
|
||||
} else {
|
||||
is_send_gateway = true
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("heartbeat_packet err={:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
for (dest_ip, routes) in context.route_table.route_table() {
|
||||
let net_packet = if current_device.is_gateway(&dest_ip) {
|
||||
if is_send_gateway {
|
||||
continue;
|
||||
}
|
||||
heartbeat_packet_server(device_list, server_cipher, src_ip, gateway_ip)
|
||||
} else {
|
||||
heartbeat_packet_client(client_cipher, src_ip, dest_ip)
|
||||
};
|
||||
let net_packet = match net_packet {
|
||||
Ok(net_packet) => net_packet,
|
||||
Err(e) => {
|
||||
log::error!("heartbeat_packet err={:?}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
for route in routes {
|
||||
if let Err(e) = context.send_by_key(net_packet.buffer(), route.route_key()) {
|
||||
log::warn!("heartbeat err={:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
let peer_list = { device_list.lock().1.clone() };
|
||||
for peer in &peer_list {
|
||||
if !peer.status.is_online() {
|
||||
continue;
|
||||
}
|
||||
if current_device.is_gateway(&peer.virtual_ip) {
|
||||
continue;
|
||||
}
|
||||
if current_device.status.offline() {
|
||||
continue;
|
||||
}
|
||||
if context.route_table.route_one(&peer.virtual_ip).is_none() {
|
||||
//路由为空,则向服务端地址发送
|
||||
let net_packet = match heartbeat_packet_client(client_cipher, src_ip, peer.virtual_ip) {
|
||||
Ok(net_packet) => net_packet,
|
||||
Err(e) => {
|
||||
log::error!("heartbeat_packet err={:?}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if let Err(e) = context.send_default(net_packet.buffer(), current_device.connect_server)
|
||||
{
|
||||
log::error!("heartbeat_packet send_default err={:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 客户端中继路径探测,延迟启动
|
||||
pub fn client_relay(
|
||||
scheduler: &Scheduler,
|
||||
context: Context,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
client_cipher: Cipher,
|
||||
) {
|
||||
let rs = scheduler.timeout(Duration::from_secs(30), move |s| {
|
||||
client_relay_(s, context, current_device, device_list, client_cipher)
|
||||
});
|
||||
if !rs {
|
||||
log::info!("定时任务停止");
|
||||
}
|
||||
}
|
||||
|
||||
/// 客户端中继路径探测,每30秒探测一次
|
||||
fn client_relay_(
|
||||
scheduler: &Scheduler,
|
||||
context: Context,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
client_cipher: Cipher,
|
||||
) {
|
||||
if let Err(e) = client_relay0(
|
||||
&context,
|
||||
¤t_device.load(),
|
||||
&device_list,
|
||||
&client_cipher,
|
||||
) {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
let rs = scheduler.timeout(Duration::from_secs(30), move |s| {
|
||||
client_relay_(s, context, current_device, device_list, client_cipher)
|
||||
});
|
||||
if !rs {
|
||||
log::info!("定时任务停止");
|
||||
}
|
||||
}
|
||||
|
||||
fn client_relay0(
|
||||
context: &Context,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
device_list: &Mutex<(u16, Vec<PeerDeviceInfo>)>,
|
||||
client_cipher: &Cipher,
|
||||
) -> io::Result<()> {
|
||||
// 离线了不再探测
|
||||
if current_device.status.offline() {
|
||||
return Ok(());
|
||||
}
|
||||
let peer_list = { device_list.lock().1.clone() };
|
||||
let mut routes = context.route_table.route_table_p2p();
|
||||
for peer in &peer_list {
|
||||
if !peer.status.is_online() || peer.virtual_ip == current_device.virtual_ip {
|
||||
continue;
|
||||
}
|
||||
if context
|
||||
.route_table
|
||||
.route_one_p2p(&peer.virtual_ip)
|
||||
.is_some()
|
||||
&& !context.first_latency()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let client_packet =
|
||||
heartbeat_packet_client(client_cipher, current_device.virtual_ip, peer.virtual_ip)?;
|
||||
|
||||
//随机发送到其他地址,看有没有客户端符合转发条件
|
||||
routes.shuffle(&mut rand::thread_rng());
|
||||
|
||||
for (index, (ip, route)) in routes.iter().enumerate() {
|
||||
if current_device.is_gateway(ip) {
|
||||
continue;
|
||||
}
|
||||
if let Err(e) = context.send_by_key(client_packet.buffer(), route.route_key()) {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
if index >= 2 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 构建心跳包
|
||||
fn heartbeat_packet(
|
||||
src: Ipv4Addr,
|
||||
dest: Ipv4Addr,
|
||||
) -> io::Result<NetPacket<[u8; 12 + 4 + ENCRYPTION_RESERVED]>> {
|
||||
let mut net_packet = NetPacket::new_encrypt([0u8; 12 + 4 + ENCRYPTION_RESERVED])?;
|
||||
net_packet.set_version(Version::V1);
|
||||
net_packet.set_protocol(Protocol::Control);
|
||||
net_packet.set_transport_protocol(control_packet::Protocol::Ping.into());
|
||||
net_packet.first_set_ttl(5);
|
||||
net_packet.set_source(src);
|
||||
net_packet.set_destination(dest);
|
||||
let mut ping = PingPacket::new(net_packet.payload_mut())?;
|
||||
ping.set_time(crate::handle::now_time() as u16);
|
||||
Ok(net_packet)
|
||||
}
|
||||
|
||||
fn heartbeat_packet_client(
|
||||
client_cipher: &Cipher,
|
||||
src: Ipv4Addr,
|
||||
dest: Ipv4Addr,
|
||||
) -> io::Result<NetPacket<[u8; 12 + 4 + ENCRYPTION_RESERVED]>> {
|
||||
let mut net_packet = heartbeat_packet(src, dest)?;
|
||||
client_cipher.encrypt_ipv4(&mut net_packet)?;
|
||||
Ok(net_packet)
|
||||
}
|
||||
|
||||
fn heartbeat_packet_server(
|
||||
device_list: &Mutex<(u16, Vec<PeerDeviceInfo>)>,
|
||||
server_cipher: &Cipher,
|
||||
src: Ipv4Addr,
|
||||
dest: Ipv4Addr,
|
||||
) -> io::Result<NetPacket<[u8; 12 + 4 + ENCRYPTION_RESERVED]>> {
|
||||
let mut net_packet = heartbeat_packet(src, dest)?;
|
||||
let mut ping = PingPacket::new(net_packet.payload_mut())?;
|
||||
ping.set_epoch(device_list.lock().0);
|
||||
net_packet.set_gateway_flag(true);
|
||||
server_cipher.encrypt_ipv4(&mut net_packet)?;
|
||||
Ok(net_packet)
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
use crate::channel::context::Context;
|
||||
use crate::channel::idle::{Idle, IdleType};
|
||||
use crate::channel::sender::AcceptSocketSender;
|
||||
use crate::handle::callback::{ConnectInfo, ErrorType};
|
||||
use crate::handle::handshaker::Handshake;
|
||||
use crate::handle::{handshaker, BaseConfigInfo, ConnectStatus, CurrentDeviceInfo};
|
||||
use crate::util::Scheduler;
|
||||
use crate::{ErrorInfo, VntCallback};
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use mio::net::TcpStream;
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
pub fn idle_route<Call: VntCallback>(
|
||||
scheduler: &Scheduler,
|
||||
idle: Idle,
|
||||
context: Context,
|
||||
current_device_info: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
call: Call,
|
||||
) {
|
||||
let delay = idle_route0(&idle, &context, ¤t_device_info, &call);
|
||||
let rs = scheduler.timeout(delay, move |s| {
|
||||
idle_route(s, idle, context, current_device_info, call)
|
||||
});
|
||||
if !rs {
|
||||
log::info!("定时任务停止");
|
||||
}
|
||||
}
|
||||
pub fn idle_gateway<Call: VntCallback>(
|
||||
scheduler: &Scheduler,
|
||||
context: Context,
|
||||
current_device_info: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
config: BaseConfigInfo,
|
||||
tcp_socket_sender: AcceptSocketSender<(TcpStream, SocketAddr, Option<Vec<u8>>)>,
|
||||
call: Call,
|
||||
mut connect_count: usize,
|
||||
handshake: Handshake,
|
||||
) {
|
||||
idle_gateway0(
|
||||
&context,
|
||||
¤t_device_info,
|
||||
&config,
|
||||
&tcp_socket_sender,
|
||||
&call,
|
||||
&mut connect_count,
|
||||
&handshake,
|
||||
);
|
||||
let rs = scheduler.timeout(Duration::from_secs(5), move |s| {
|
||||
idle_gateway(
|
||||
s,
|
||||
context,
|
||||
current_device_info,
|
||||
config,
|
||||
tcp_socket_sender,
|
||||
call,
|
||||
connect_count,
|
||||
handshake,
|
||||
)
|
||||
});
|
||||
if !rs {
|
||||
log::info!("定时任务停止");
|
||||
}
|
||||
}
|
||||
fn idle_gateway0<Call: VntCallback>(
|
||||
context: &Context,
|
||||
current_device: &AtomicCell<CurrentDeviceInfo>,
|
||||
config: &BaseConfigInfo,
|
||||
tcp_socket_sender: &AcceptSocketSender<(TcpStream, SocketAddr, Option<Vec<u8>>)>,
|
||||
call: &Call,
|
||||
connect_count: &mut usize,
|
||||
handshake: &Handshake,
|
||||
) {
|
||||
if let Err(e) = check_gateway_channel(
|
||||
context,
|
||||
current_device,
|
||||
config,
|
||||
tcp_socket_sender,
|
||||
call,
|
||||
connect_count,
|
||||
handshake,
|
||||
) {
|
||||
let cur = current_device.load();
|
||||
call.error(ErrorInfo::new_msg(
|
||||
ErrorType::Disconnect,
|
||||
format!("connect:{},error:{:?}", cur.connect_server, e),
|
||||
));
|
||||
}
|
||||
}
|
||||
fn idle_route0<Call: VntCallback>(
|
||||
idle: &Idle,
|
||||
context: &Context,
|
||||
current_device: &AtomicCell<CurrentDeviceInfo>,
|
||||
call: &Call,
|
||||
) -> Duration {
|
||||
let cur = current_device.load();
|
||||
match idle.next_idle() {
|
||||
IdleType::Timeout(ip, route) => {
|
||||
log::info!("route Timeout {:?},{:?}", ip, route);
|
||||
context.remove_route(&ip, route.route_key());
|
||||
if cur.is_gateway(&ip) {
|
||||
//网关路由过期,则需要改变状态
|
||||
crate::handle::change_status(current_device, ConnectStatus::Connecting);
|
||||
call.error(ErrorInfo::new(ErrorType::Disconnect));
|
||||
}
|
||||
Duration::from_millis(100)
|
||||
}
|
||||
IdleType::Sleep(duration) => duration,
|
||||
IdleType::None => Duration::from_millis(3000),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_gateway_channel<Call: VntCallback>(
|
||||
context: &Context,
|
||||
current_device: &AtomicCell<CurrentDeviceInfo>,
|
||||
config: &BaseConfigInfo,
|
||||
tcp_socket_sender: &AcceptSocketSender<(TcpStream, SocketAddr, Option<Vec<u8>>)>,
|
||||
call: &Call,
|
||||
count: &mut usize,
|
||||
handshake: &Handshake,
|
||||
) -> io::Result<()> {
|
||||
let current_device = current_device.load();
|
||||
if current_device.status.offline() {
|
||||
*count += 1;
|
||||
//需要重连
|
||||
call.connect(ConnectInfo::new(*count, current_device.connect_server));
|
||||
log::info!("发送握手请求,{:?}", config);
|
||||
if let Err(e) = handshake.send(context, config.client_secret, current_device.connect_server)
|
||||
{
|
||||
log::warn!("{:?}", e);
|
||||
if context.is_main_tcp() {
|
||||
let request_packet = handshaker::handshake_request_packet(config.client_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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
mod heartbeat;
|
||||
pub use heartbeat::client_relay;
|
||||
pub use heartbeat::heartbeat;
|
||||
|
||||
mod re_nat_type;
|
||||
pub use re_nat_type::retrieve_nat_type;
|
||||
|
||||
mod addr_request;
|
||||
pub use addr_request::addr_request;
|
||||
|
||||
mod punch;
|
||||
pub use punch::*;
|
||||
|
||||
mod idle;
|
||||
pub use idle::idle_gateway;
|
||||
pub use idle::idle_route;
|
||||
@@ -0,0 +1,280 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::{io, thread};
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::Mutex;
|
||||
use protobuf::Message;
|
||||
use rand::prelude::SliceRandom;
|
||||
|
||||
use crate::channel::context::Context;
|
||||
use crate::channel::punch::{NatInfo, NatType, Punch};
|
||||
use crate::cipher::Cipher;
|
||||
use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo};
|
||||
use crate::nat::NatTest;
|
||||
use crate::proto::message::{PunchInfo, PunchNatType};
|
||||
use crate::protocol::body::ENCRYPTION_RESERVED;
|
||||
use crate::protocol::{control_packet, other_turn_packet, NetPacket, Protocol, Version, MAX_TTL};
|
||||
use crate::util::Scheduler;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PunchSender {
|
||||
sender_self: SyncSender<(Ipv4Addr, NatInfo)>,
|
||||
sender_peer: SyncSender<(Ipv4Addr, NatInfo)>,
|
||||
sender_cone_self: SyncSender<(Ipv4Addr, NatInfo)>,
|
||||
sender_cone_peer: SyncSender<(Ipv4Addr, NatInfo)>,
|
||||
}
|
||||
impl PunchSender {
|
||||
pub fn send(&self, src_peer: bool, ip: Ipv4Addr, info: NatInfo) -> bool {
|
||||
log::info!(
|
||||
"发送打洞协商消息,是否对端发起:{},ip:{},info:{:?}",
|
||||
src_peer,
|
||||
ip,
|
||||
info
|
||||
);
|
||||
let sender = match info.nat_type {
|
||||
NatType::Symmetric => {
|
||||
if src_peer {
|
||||
&self.sender_peer
|
||||
} else {
|
||||
&self.sender_self
|
||||
}
|
||||
}
|
||||
NatType::Cone => {
|
||||
if src_peer {
|
||||
&self.sender_cone_peer
|
||||
} else {
|
||||
&self.sender_cone_self
|
||||
}
|
||||
}
|
||||
};
|
||||
sender.try_send((ip, info)).is_ok()
|
||||
}
|
||||
}
|
||||
pub struct PunchReceiver {
|
||||
receiver_peer: Receiver<(Ipv4Addr, NatInfo)>,
|
||||
receiver_self: Receiver<(Ipv4Addr, NatInfo)>,
|
||||
receiver_cone_peer: Receiver<(Ipv4Addr, NatInfo)>,
|
||||
receiver_cone_self: Receiver<(Ipv4Addr, NatInfo)>,
|
||||
}
|
||||
pub fn punch_channel() -> (PunchSender, PunchReceiver) {
|
||||
let (sender_self, receiver_self) = sync_channel(1);
|
||||
let (sender_peer, receiver_peer) = sync_channel(1);
|
||||
let (sender_cone_peer, receiver_cone_peer) = sync_channel(1);
|
||||
let (sender_cone_self, receiver_cone_self) = sync_channel(1);
|
||||
(
|
||||
PunchSender {
|
||||
sender_self,
|
||||
sender_peer,
|
||||
sender_cone_peer,
|
||||
sender_cone_self,
|
||||
},
|
||||
PunchReceiver {
|
||||
receiver_peer,
|
||||
receiver_self,
|
||||
receiver_cone_peer,
|
||||
receiver_cone_self,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn punch(
|
||||
scheduler: &Scheduler,
|
||||
context: Context,
|
||||
nat_test: NatTest,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
client_cipher: Cipher,
|
||||
receiver: PunchReceiver,
|
||||
punch: Punch,
|
||||
) {
|
||||
punch_request(
|
||||
scheduler,
|
||||
context,
|
||||
nat_test,
|
||||
device_list,
|
||||
current_device.clone(),
|
||||
client_cipher.clone(),
|
||||
0,
|
||||
);
|
||||
let f = |receiver: Receiver<(Ipv4Addr, NatInfo)>| {
|
||||
let punch = punch.clone();
|
||||
let current_device = current_device.clone();
|
||||
let client_cipher = client_cipher.clone();
|
||||
thread::Builder::new()
|
||||
.name("punch".into())
|
||||
.spawn(move || {
|
||||
punch_start(receiver, punch, current_device, client_cipher);
|
||||
})
|
||||
.expect("punch");
|
||||
};
|
||||
f(receiver.receiver_peer);
|
||||
f(receiver.receiver_self);
|
||||
f(receiver.receiver_cone_peer);
|
||||
f(receiver.receiver_cone_self);
|
||||
}
|
||||
|
||||
/// 接收打洞消息,配合对端打洞
|
||||
fn punch_start(
|
||||
receiver: Receiver<(Ipv4Addr, NatInfo)>,
|
||||
mut punch: Punch,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
client_cipher: Cipher,
|
||||
) {
|
||||
while let Ok((peer_ip, nat_info)) = receiver.recv() {
|
||||
let mut packet = NetPacket::new_encrypt([0u8; 12 + ENCRYPTION_RESERVED]).unwrap();
|
||||
packet.set_version(Version::V1);
|
||||
packet.first_set_ttl(1);
|
||||
packet.set_protocol(Protocol::Control);
|
||||
packet.set_transport_protocol(control_packet::Protocol::PunchRequest.into());
|
||||
packet.set_source(current_device.load().virtual_ip());
|
||||
packet.set_destination(peer_ip);
|
||||
log::info!("发起打洞,目标:{:?},{:?}", peer_ip, nat_info);
|
||||
if let Err(e) = client_cipher.encrypt_ipv4(&mut packet) {
|
||||
log::error!("{:?}", e);
|
||||
continue;
|
||||
}
|
||||
if let Err(e) = punch.punch(packet.buffer(), peer_ip, nat_info) {
|
||||
log::warn!("{:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 定时发起打洞请求
|
||||
fn punch_request(
|
||||
scheduler: &Scheduler,
|
||||
context: Context,
|
||||
nat_test: NatTest,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
client_cipher: Cipher,
|
||||
count: usize,
|
||||
) {
|
||||
let curr = current_device.load();
|
||||
let secs = if curr.status.online() {
|
||||
if let Err(e) = punch0(&context, &nat_test, &device_list, curr, &client_cipher) {
|
||||
log::warn!("{:?}", e)
|
||||
}
|
||||
let sleep_time = [3, 5, 7, 11, 13, 17, 19, 23, 29];
|
||||
Duration::from_secs(sleep_time[count % sleep_time.len()])
|
||||
} else {
|
||||
Duration::from_secs(3)
|
||||
};
|
||||
let rs = scheduler.timeout(secs, move |s| {
|
||||
punch_request(
|
||||
s,
|
||||
context,
|
||||
nat_test,
|
||||
device_list,
|
||||
current_device,
|
||||
client_cipher,
|
||||
count + 1,
|
||||
);
|
||||
});
|
||||
if !rs {
|
||||
log::info!("定时任务停止");
|
||||
}
|
||||
}
|
||||
|
||||
/// 随机对需要打洞的客户端发起打洞请求
|
||||
fn punch0(
|
||||
context: &Context,
|
||||
nat_test: &NatTest,
|
||||
device_list: &Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
current_device: CurrentDeviceInfo,
|
||||
client_cipher: &Cipher,
|
||||
) -> io::Result<()> {
|
||||
let nat_info = nat_test.nat_info();
|
||||
let current_ip = current_device.virtual_ip;
|
||||
let mut list: Vec<PeerDeviceInfo> = device_list
|
||||
.lock()
|
||||
.1
|
||||
.iter()
|
||||
.filter(|info| info.status.is_online() && info.virtual_ip > current_ip)
|
||||
.cloned()
|
||||
.collect();
|
||||
list.shuffle(&mut rand::thread_rng());
|
||||
let mut count = 0;
|
||||
// 优先没打洞的
|
||||
list.sort_by(|v1, v2| {
|
||||
if context.route_table.route_one_p2p(&v1.virtual_ip).is_none() {
|
||||
Ordering::Less
|
||||
} else if context.route_table.route_one_p2p(&v2.virtual_ip).is_none() {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
});
|
||||
for info in list {
|
||||
if !info.status.is_online() {
|
||||
continue;
|
||||
}
|
||||
if info.virtual_ip <= current_device.virtual_ip {
|
||||
continue;
|
||||
}
|
||||
if !context.route_table.need_punch(&info.virtual_ip) {
|
||||
continue;
|
||||
}
|
||||
count += 1;
|
||||
if count > 2 {
|
||||
break;
|
||||
}
|
||||
let packet = punch_packet(
|
||||
client_cipher,
|
||||
current_device.virtual_ip(),
|
||||
&nat_info,
|
||||
info.virtual_ip,
|
||||
)?;
|
||||
log::info!(
|
||||
"发起打洞协商请求,目标:{:?},{:?}",
|
||||
info.virtual_ip,
|
||||
nat_info
|
||||
);
|
||||
context.send_default(packet.buffer(), current_device.connect_server)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn punch_packet(
|
||||
client_cipher: &Cipher,
|
||||
virtual_ip: Ipv4Addr,
|
||||
nat_info: &NatInfo,
|
||||
dest: Ipv4Addr,
|
||||
) -> io::Result<NetPacket<Vec<u8>>> {
|
||||
let mut punch_reply = PunchInfo::new();
|
||||
punch_reply.reply = false;
|
||||
punch_reply.public_ip_list = nat_info
|
||||
.public_ips
|
||||
.iter()
|
||||
.map(|ip| u32::from_be_bytes(ip.octets()))
|
||||
.collect();
|
||||
punch_reply.public_port = nat_info.public_ports.get(0).map_or(0, |v| *v as u32);
|
||||
punch_reply.public_ports = nat_info.public_ports.iter().map(|e| *e as u32).collect();
|
||||
punch_reply.public_port_range = nat_info.public_port_range as u32;
|
||||
punch_reply.local_ip = u32::from(nat_info.local_ipv4().unwrap_or(Ipv4Addr::UNSPECIFIED));
|
||||
punch_reply.local_port = nat_info.udp_ports[0] as u32;
|
||||
punch_reply.tcp_port = nat_info.tcp_port as u32;
|
||||
punch_reply.udp_ports = nat_info.udp_ports.iter().map(|e| *e as u32).collect();
|
||||
if let Some(ipv6) = nat_info.ipv6 {
|
||||
punch_reply.ipv6_port = nat_info.udp_ports[0] as u32;
|
||||
punch_reply.ipv6 = ipv6.octets().to_vec();
|
||||
}
|
||||
punch_reply.nat_type = protobuf::EnumOrUnknown::new(PunchNatType::from(nat_info.nat_type));
|
||||
log::info!("请求打洞={:?}", punch_reply);
|
||||
let bytes = punch_reply
|
||||
.write_to_bytes()
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, format!("punch_packet {:?}", e)))?;
|
||||
let mut net_packet = NetPacket::new_encrypt(vec![0u8; 12 + bytes.len() + ENCRYPTION_RESERVED])?;
|
||||
net_packet.set_version(Version::V1);
|
||||
net_packet.set_protocol(Protocol::OtherTurn);
|
||||
net_packet.set_transport_protocol(other_turn_packet::Protocol::Punch.into());
|
||||
net_packet.first_set_ttl(MAX_TTL);
|
||||
net_packet.set_source(virtual_ip);
|
||||
net_packet.set_destination(dest);
|
||||
net_packet.set_payload(&bytes)?;
|
||||
client_cipher.encrypt_ipv4(&mut net_packet)?;
|
||||
Ok(net_packet)
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::channel::context::Context;
|
||||
use crate::channel::sender::AcceptSocketSender;
|
||||
use crate::nat;
|
||||
use crate::nat::NatTest;
|
||||
use crate::util::Scheduler;
|
||||
|
||||
/// 10分钟探测一次nat
|
||||
pub fn retrieve_nat_type(
|
||||
scheduler: &Scheduler,
|
||||
context: Context,
|
||||
nat_test: NatTest,
|
||||
udp_socket_sender: AcceptSocketSender<Option<Vec<mio::net::UdpSocket>>>,
|
||||
) {
|
||||
retrieve_nat_type0(context.clone(), nat_test.clone(), udp_socket_sender.clone());
|
||||
scheduler.timeout(Duration::from_secs(60 * 10), move |s| {
|
||||
retrieve_nat_type(s, context, nat_test, udp_socket_sender)
|
||||
});
|
||||
}
|
||||
|
||||
fn retrieve_nat_type0(
|
||||
context: Context,
|
||||
nat_test: NatTest,
|
||||
udp_socket_sender: AcceptSocketSender<Option<Vec<mio::net::UdpSocket>>>,
|
||||
) {
|
||||
thread::Builder::new()
|
||||
.name("natTest".into())
|
||||
.spawn(move || {
|
||||
if nat_test.can_update() {
|
||||
let local_ipv4 = nat::local_ipv4();
|
||||
let local_ipv6 = nat::local_ipv6();
|
||||
match nat_test.re_test(local_ipv4, local_ipv6) {
|
||||
Ok(nat_info) => {
|
||||
log::info!("当前nat信息:{:?}", nat_info);
|
||||
if let Err(e) = context.switch(nat_info.nat_type, &udp_socket_sender) {
|
||||
log::warn!("{:?}", e);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("nat re_test {:?}", e);
|
||||
}
|
||||
};
|
||||
}
|
||||
})
|
||||
.expect("natTest");
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use std::net::{Ipv4Addr, SocketAddr};
|
||||
|
||||
pub mod callback;
|
||||
pub mod handshaker;
|
||||
pub mod maintain;
|
||||
pub mod recv_data;
|
||||
pub mod registrar;
|
||||
pub mod tun_tap;
|
||||
|
||||
const SELF_IP: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 2);
|
||||
const GATEWAY_IP: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 1);
|
||||
|
||||
pub fn now_time() -> u64 {
|
||||
let now = std::time::SystemTime::now();
|
||||
if let Ok(timestamp) = now.duration_since(std::time::UNIX_EPOCH) {
|
||||
timestamp.as_secs() * 1000 + u64::from(timestamp.subsec_millis())
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/// 是否在一个网段
|
||||
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,
|
||||
pub name: String,
|
||||
pub status: PeerDeviceStatus,
|
||||
pub client_secret: bool,
|
||||
}
|
||||
|
||||
impl PeerDeviceInfo {
|
||||
pub fn new(virtual_ip: Ipv4Addr, name: String, status: u8, client_secret: bool) -> Self {
|
||||
Self {
|
||||
virtual_ip,
|
||||
name,
|
||||
status: PeerDeviceStatus::from(status),
|
||||
client_secret,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BaseConfigInfo {
|
||||
pub name: String,
|
||||
pub token: String,
|
||||
pub ip: Option<Ipv4Addr>,
|
||||
pub client_secret: bool,
|
||||
pub device_id: String,
|
||||
pub server_addr: String,
|
||||
}
|
||||
|
||||
impl BaseConfigInfo {
|
||||
pub fn new(
|
||||
name: String,
|
||||
token: String,
|
||||
ip: Option<Ipv4Addr>,
|
||||
client_secret: bool,
|
||||
device_id: String,
|
||||
server_addr: String,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
token,
|
||||
ip,
|
||||
client_secret,
|
||||
device_id,
|
||||
server_addr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum PeerDeviceStatus {
|
||||
Online,
|
||||
Offline,
|
||||
}
|
||||
|
||||
impl PeerDeviceStatus {
|
||||
pub fn is_online(&self) -> bool {
|
||||
self == &PeerDeviceStatus::Online
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for PeerDeviceStatus {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
PeerDeviceStatus::Online => 0,
|
||||
PeerDeviceStatus::Offline => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for PeerDeviceStatus {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
0 => PeerDeviceStatus::Online,
|
||||
_ => PeerDeviceStatus::Offline,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum ConnectStatus {
|
||||
Connecting,
|
||||
Connected,
|
||||
}
|
||||
|
||||
impl ConnectStatus {
|
||||
pub fn online(&self) -> bool {
|
||||
self == &ConnectStatus::Connected
|
||||
}
|
||||
pub fn offline(&self) -> bool {
|
||||
self == &ConnectStatus::Connecting
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct CurrentDeviceInfo {
|
||||
//本机虚拟IP
|
||||
pub virtual_ip: Ipv4Addr,
|
||||
//子网掩码
|
||||
pub virtual_netmask: Ipv4Addr,
|
||||
//虚拟网关
|
||||
pub virtual_gateway: Ipv4Addr,
|
||||
//网络地址
|
||||
pub virtual_network: Ipv4Addr,
|
||||
//直接广播地址
|
||||
pub broadcast_ip: Ipv4Addr,
|
||||
//链接的服务器地址
|
||||
pub connect_server: SocketAddr,
|
||||
//连接状态
|
||||
pub status: ConnectStatus,
|
||||
}
|
||||
|
||||
impl CurrentDeviceInfo {
|
||||
pub fn new(
|
||||
virtual_ip: Ipv4Addr,
|
||||
virtual_netmask: Ipv4Addr,
|
||||
virtual_gateway: Ipv4Addr,
|
||||
connect_server: SocketAddr,
|
||||
) -> Self {
|
||||
let broadcast_ip = (!u32::from_be_bytes(virtual_netmask.octets()))
|
||||
| u32::from_be_bytes(virtual_gateway.octets());
|
||||
let broadcast_ip = Ipv4Addr::from(broadcast_ip);
|
||||
let virtual_network = u32::from_be_bytes(virtual_netmask.octets())
|
||||
& u32::from_be_bytes(virtual_gateway.octets());
|
||||
let virtual_network = Ipv4Addr::from(virtual_network);
|
||||
Self {
|
||||
virtual_ip,
|
||||
virtual_netmask,
|
||||
virtual_gateway,
|
||||
virtual_network,
|
||||
broadcast_ip,
|
||||
connect_server,
|
||||
status: ConnectStatus::Connecting,
|
||||
}
|
||||
}
|
||||
pub fn new0(connect_server: SocketAddr) -> Self {
|
||||
Self {
|
||||
virtual_ip: Ipv4Addr::UNSPECIFIED,
|
||||
virtual_gateway: Ipv4Addr::UNSPECIFIED,
|
||||
virtual_netmask: Ipv4Addr::UNSPECIFIED,
|
||||
virtual_network: Ipv4Addr::UNSPECIFIED,
|
||||
broadcast_ip: Ipv4Addr::UNSPECIFIED,
|
||||
connect_server,
|
||||
status: ConnectStatus::Connecting,
|
||||
}
|
||||
}
|
||||
pub fn update(
|
||||
&mut self,
|
||||
virtual_ip: Ipv4Addr,
|
||||
virtual_netmask: Ipv4Addr,
|
||||
virtual_gateway: Ipv4Addr,
|
||||
) {
|
||||
let broadcast_ip = (!u32::from_be_bytes(virtual_netmask.octets()))
|
||||
| u32::from_be_bytes(virtual_gateway.octets());
|
||||
let broadcast_ip = Ipv4Addr::from(broadcast_ip);
|
||||
let virtual_network = u32::from_be_bytes(virtual_netmask.octets())
|
||||
& u32::from_be_bytes(virtual_gateway.octets());
|
||||
let virtual_network = Ipv4Addr::from(virtual_network);
|
||||
self.virtual_ip = virtual_ip;
|
||||
self.virtual_netmask = virtual_netmask;
|
||||
self.virtual_gateway = virtual_gateway;
|
||||
self.broadcast_ip = broadcast_ip;
|
||||
self.virtual_network = virtual_network;
|
||||
}
|
||||
#[inline]
|
||||
pub fn virtual_ip(&self) -> Ipv4Addr {
|
||||
self.virtual_ip
|
||||
}
|
||||
#[inline]
|
||||
pub fn virtual_gateway(&self) -> Ipv4Addr {
|
||||
self.virtual_gateway
|
||||
}
|
||||
pub fn is_gateway(&self, ip: &Ipv4Addr) -> bool {
|
||||
&self.virtual_gateway == ip || ip == &GATEWAY_IP
|
||||
}
|
||||
}
|
||||
pub fn change_status(
|
||||
current_device: &AtomicCell<CurrentDeviceInfo>,
|
||||
connect_status: ConnectStatus,
|
||||
) -> CurrentDeviceInfo {
|
||||
loop {
|
||||
let cur = current_device.load();
|
||||
let mut new_info = cur;
|
||||
new_info.status = connect_status;
|
||||
if current_device.compare_exchange(cur, new_info).is_ok() {
|
||||
return new_info;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
use std::sync::Arc;
|
||||
|
||||
use parking_lot::RwLock;
|
||||
use protobuf::Message;
|
||||
|
||||
use packet::icmp::{icmp, Kind};
|
||||
use packet::ip::ipv4;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
use tun::device::IFace;
|
||||
use tun::Device;
|
||||
|
||||
use crate::channel::context::Context;
|
||||
use crate::channel::punch::NatInfo;
|
||||
use crate::channel::{Route, RouteKey};
|
||||
use crate::cipher::Cipher;
|
||||
use crate::external_route::AllowExternalRoute;
|
||||
use crate::handle::maintain::PunchSender;
|
||||
use crate::handle::recv_data::PacketHandler;
|
||||
use crate::handle::CurrentDeviceInfo;
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
use crate::ip_proxy::{IpProxyMap, ProxyHandler};
|
||||
use crate::nat::NatTest;
|
||||
use crate::proto::message::{PunchInfo, PunchNatType};
|
||||
use crate::protocol::body::ENCRYPTION_RESERVED;
|
||||
use crate::protocol::control_packet::ControlPacket;
|
||||
use crate::protocol::{
|
||||
control_packet, ip_turn_packet, other_turn_packet, NetPacket, Protocol, Version, MAX_TTL,
|
||||
};
|
||||
|
||||
/// 处理来源于客户端的包
|
||||
#[derive(Clone)]
|
||||
pub struct ClientPacketHandler {
|
||||
device: Arc<Device>,
|
||||
client_cipher: Cipher,
|
||||
punch_sender: PunchSender,
|
||||
peer_nat_info_map: Arc<RwLock<HashMap<Ipv4Addr, NatInfo>>>,
|
||||
nat_test: NatTest,
|
||||
route: AllowExternalRoute,
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
ip_proxy_map: Option<IpProxyMap>,
|
||||
}
|
||||
|
||||
impl ClientPacketHandler {
|
||||
pub fn new(
|
||||
device: Arc<Device>,
|
||||
client_cipher: Cipher,
|
||||
punch_sender: PunchSender,
|
||||
peer_nat_info_map: Arc<RwLock<HashMap<Ipv4Addr, NatInfo>>>,
|
||||
nat_test: NatTest,
|
||||
route: AllowExternalRoute,
|
||||
#[cfg(feature = "ip_proxy")] ip_proxy_map: Option<IpProxyMap>,
|
||||
) -> Self {
|
||||
Self {
|
||||
device,
|
||||
client_cipher,
|
||||
punch_sender,
|
||||
peer_nat_info_map,
|
||||
nat_test,
|
||||
route,
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
ip_proxy_map,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PacketHandler for ClientPacketHandler {
|
||||
fn handle(
|
||||
&self,
|
||||
mut net_packet: NetPacket<&mut [u8]>,
|
||||
route_key: RouteKey,
|
||||
context: &Context,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
) -> io::Result<()> {
|
||||
self.client_cipher.decrypt_ipv4(&mut net_packet)?;
|
||||
context
|
||||
.route_table
|
||||
.update_read_time(&net_packet.source(), &route_key);
|
||||
match net_packet.protocol() {
|
||||
Protocol::Service => {}
|
||||
Protocol::Error => {}
|
||||
Protocol::Control => {
|
||||
self.control(context, current_device, net_packet, route_key)?;
|
||||
}
|
||||
Protocol::IpTurn => {
|
||||
self.ip_turn(net_packet, context, current_device, route_key)?;
|
||||
}
|
||||
Protocol::OtherTurn => {
|
||||
self.other_turn(context, current_device, net_packet, route_key)?;
|
||||
}
|
||||
Protocol::Unknown(_) => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientPacketHandler {
|
||||
fn ip_turn(
|
||||
&self,
|
||||
mut net_packet: NetPacket<&mut [u8]>,
|
||||
context: &Context,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
route_key: RouteKey,
|
||||
) -> io::Result<()> {
|
||||
let destination = net_packet.destination();
|
||||
let source = net_packet.source();
|
||||
match ip_turn_packet::Protocol::from(net_packet.transport_protocol()) {
|
||||
ip_turn_packet::Protocol::Ipv4 => {
|
||||
let mut ipv4 = IpV4Packet::new(net_packet.payload_mut())?;
|
||||
match ipv4.protocol() {
|
||||
ipv4::protocol::Protocol::Icmp => {
|
||||
if ipv4.destination_ip() == destination {
|
||||
let mut icmp_packet = icmp::IcmpPacket::new(ipv4.payload_mut())?;
|
||||
if icmp_packet.kind() == Kind::EchoRequest {
|
||||
//开启ping
|
||||
icmp_packet.set_kind(Kind::EchoReply);
|
||||
icmp_packet.update_checksum();
|
||||
ipv4.set_source_ip(destination);
|
||||
ipv4.set_destination_ip(source);
|
||||
ipv4.update_checksum();
|
||||
net_packet.set_source(destination);
|
||||
net_packet.set_destination(source);
|
||||
//不管加不加密,和接收到的数据长度都一致
|
||||
self.client_cipher.encrypt_ipv4(&mut net_packet)?;
|
||||
context.send_by_key(net_packet.buffer(), route_key)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
// ip代理只关心实际目标
|
||||
let real_dest = ipv4.destination_ip();
|
||||
if real_dest != destination
|
||||
&& !(real_dest.is_broadcast()
|
||||
|| real_dest.is_multicast()
|
||||
|| real_dest == current_device.broadcast_ip
|
||||
|| real_dest.is_unspecified())
|
||||
{
|
||||
if !self.route.allow(&real_dest) {
|
||||
//拦截不符合的目标
|
||||
return Ok(());
|
||||
}
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
if let Some(ip_proxy_map) = &self.ip_proxy_map {
|
||||
if ip_proxy_map.recv_handle(&mut ipv4, source, destination)? {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
self.device.write(net_packet.payload())?;
|
||||
}
|
||||
ip_turn_packet::Protocol::Ipv4Broadcast => {
|
||||
//客户端不帮忙转发广播包,所以不会出现这种类型的数据
|
||||
}
|
||||
ip_turn_packet::Protocol::Unknown(_) => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn control(
|
||||
&self,
|
||||
context: &Context,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
mut net_packet: NetPacket<&mut [u8]>,
|
||||
route_key: RouteKey,
|
||||
) -> io::Result<()> {
|
||||
let metric = net_packet.source_ttl() - net_packet.ttl() + 1;
|
||||
let source = net_packet.source();
|
||||
match ControlPacket::new(net_packet.transport_protocol(), net_packet.payload())? {
|
||||
ControlPacket::PingPacket(_) => {
|
||||
net_packet.set_transport_protocol(control_packet::Protocol::Pong.into());
|
||||
net_packet.set_source(current_device.virtual_ip);
|
||||
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)?;
|
||||
let route = Route::from_default_rt(route_key, metric);
|
||||
context.route_table.add_route_if_absent(source, route);
|
||||
}
|
||||
ControlPacket::PongPacket(pong_packet) => {
|
||||
let current_time = crate::handle::now_time() as u16;
|
||||
if current_time < pong_packet.time() {
|
||||
return Ok(());
|
||||
}
|
||||
let rt = (current_time - pong_packet.time()) as i64;
|
||||
let route = Route::from(route_key, metric, rt);
|
||||
context.route_table.add_route(source, route);
|
||||
}
|
||||
ControlPacket::PunchRequest => {
|
||||
log::info!("PunchRequest={:?},source={}", route_key, source);
|
||||
if context.use_channel_type().is_only_relay() {
|
||||
return Ok(());
|
||||
}
|
||||
//回应
|
||||
net_packet.set_transport_protocol(control_packet::Protocol::PunchResponse.into());
|
||||
net_packet.set_source(current_device.virtual_ip);
|
||||
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)?;
|
||||
let route = Route::from_default_rt(route_key, 1);
|
||||
context.route_table.add_route_if_absent(source, route);
|
||||
}
|
||||
ControlPacket::PunchResponse => {
|
||||
log::info!("PunchResponse={:?},source={}", route_key, source);
|
||||
if context.use_channel_type().is_only_relay() {
|
||||
return Ok(());
|
||||
}
|
||||
let route = Route::from_default_rt(route_key, 1);
|
||||
context.route_table.add_route_if_absent(source, route);
|
||||
}
|
||||
ControlPacket::AddrRequest => match route_key.addr.ip() {
|
||||
std::net::IpAddr::V4(ipv4) => {
|
||||
let mut packet = NetPacket::new_encrypt([0; 12 + 6 + ENCRYPTION_RESERVED])?;
|
||||
packet.set_version(Version::V1);
|
||||
packet.set_protocol(Protocol::Control);
|
||||
packet.set_transport_protocol(control_packet::Protocol::AddrResponse.into());
|
||||
packet.first_set_ttl(MAX_TTL);
|
||||
packet.set_source(current_device.virtual_ip);
|
||||
packet.set_destination(source);
|
||||
let mut addr_packet = control_packet::AddrPacket::new(packet.payload_mut())?;
|
||||
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)?;
|
||||
}
|
||||
std::net::IpAddr::V6(_) => {}
|
||||
},
|
||||
ControlPacket::AddrResponse(_) => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn other_turn(
|
||||
&self,
|
||||
context: &Context,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
net_packet: NetPacket<&mut [u8]>,
|
||||
route_key: RouteKey,
|
||||
) -> io::Result<()> {
|
||||
if context.use_channel_type().is_only_relay() {
|
||||
return Ok(());
|
||||
}
|
||||
let source = net_packet.source();
|
||||
match other_turn_packet::Protocol::from(net_packet.transport_protocol()) {
|
||||
other_turn_packet::Protocol::Punch => {
|
||||
let mut punch_info =
|
||||
PunchInfo::parse_from_bytes(net_packet.payload()).map_err(|e| {
|
||||
io::Error::new(io::ErrorKind::Other, format!("PunchInfo {:?}", e))
|
||||
})?;
|
||||
let public_ips = punch_info
|
||||
.public_ip_list
|
||||
.iter()
|
||||
.map(|v| Ipv4Addr::from(v.to_be_bytes()))
|
||||
.collect();
|
||||
let local_ipv4 = Some(Ipv4Addr::from(punch_info.local_ip.to_be_bytes()));
|
||||
let tcp_port = punch_info.tcp_port as u16;
|
||||
let ipv6 = if punch_info.ipv6.len() == 16 {
|
||||
let ipv6: [u8; 16] = punch_info.ipv6.try_into().unwrap();
|
||||
Some(Ipv6Addr::from(ipv6))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
//兼容旧版本
|
||||
if punch_info.public_ports.is_empty() {
|
||||
punch_info.public_ports.push(punch_info.public_port);
|
||||
}
|
||||
//兼容旧版本
|
||||
if punch_info.udp_ports.is_empty() {
|
||||
punch_info.udp_ports.push(punch_info.local_port);
|
||||
}
|
||||
let peer_nat_info = NatInfo::new(
|
||||
public_ips,
|
||||
punch_info.public_ports.iter().map(|e| *e as u16).collect(),
|
||||
punch_info.public_port_range as u16,
|
||||
local_ipv4,
|
||||
ipv6,
|
||||
punch_info.udp_ports.iter().map(|e| *e as u16).collect(),
|
||||
tcp_port,
|
||||
punch_info.nat_type.enum_value_or_default().into(),
|
||||
);
|
||||
{
|
||||
let peer_nat_info = peer_nat_info.clone();
|
||||
self.peer_nat_info_map.write().insert(source, peer_nat_info);
|
||||
}
|
||||
if !punch_info.reply {
|
||||
let mut punch_reply = PunchInfo::new();
|
||||
punch_reply.reply = true;
|
||||
let nat_info = self.nat_test.nat_info();
|
||||
punch_reply.public_ip_list = nat_info
|
||||
.public_ips
|
||||
.iter()
|
||||
.map(|ip| u32::from_be_bytes(ip.octets()))
|
||||
.collect();
|
||||
punch_reply.public_port = nat_info.public_ports.get(0).map_or(0, |v| *v as u32);
|
||||
punch_reply.public_ports =
|
||||
nat_info.public_ports.iter().map(|e| *e as u32).collect();
|
||||
punch_reply.public_port_range = nat_info.public_port_range as u32;
|
||||
punch_reply.tcp_port = nat_info.tcp_port as u32;
|
||||
punch_reply.nat_type =
|
||||
protobuf::EnumOrUnknown::new(PunchNatType::from(nat_info.nat_type));
|
||||
punch_reply.local_ip =
|
||||
u32::from(nat_info.local_ipv4().unwrap_or(Ipv4Addr::UNSPECIFIED));
|
||||
punch_reply.local_port = nat_info.udp_ports[0] as u32;
|
||||
punch_reply.udp_ports = nat_info.udp_ports.iter().map(|e| *e as u32).collect();
|
||||
if let Some(ipv6) = nat_info.ipv6() {
|
||||
punch_reply.ipv6 = ipv6.octets().to_vec();
|
||||
punch_reply.ipv6_port = nat_info.udp_ports[0] as u32;
|
||||
}
|
||||
let bytes = punch_reply.write_to_bytes().map_err(|e| {
|
||||
io::Error::new(io::ErrorKind::Other, format!("punch_reply {:?}", e))
|
||||
})?;
|
||||
let mut punch_packet =
|
||||
NetPacket::new_encrypt(vec![0u8; 12 + bytes.len() + ENCRYPTION_RESERVED])?;
|
||||
punch_packet.set_version(Version::V1);
|
||||
punch_packet.set_protocol(Protocol::OtherTurn);
|
||||
punch_packet.set_transport_protocol(other_turn_packet::Protocol::Punch.into());
|
||||
punch_packet.first_set_ttl(MAX_TTL);
|
||||
punch_packet.set_source(current_device.virtual_ip());
|
||||
punch_packet.set_destination(source);
|
||||
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)?;
|
||||
}
|
||||
} else {
|
||||
self.punch_sender.send(false, source, peer_nat_info);
|
||||
}
|
||||
}
|
||||
other_turn_packet::Protocol::Unknown(e) => {
|
||||
log::warn!("不支持的转发协议 {:?},source:{:?}", e, source);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
use std::collections::HashMap;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
use std::{io, thread};
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
|
||||
use tun::Device;
|
||||
|
||||
use crate::channel::context::Context;
|
||||
use crate::channel::handler::RecvChannelHandler;
|
||||
use crate::channel::punch::NatInfo;
|
||||
use crate::channel::RouteKey;
|
||||
use crate::cipher::Cipher;
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
use crate::cipher::RsaCipher;
|
||||
use crate::external_route::{AllowExternalRoute, ExternalRoute};
|
||||
use crate::handle::callback::VntCallback;
|
||||
use crate::handle::handshaker::Handshake;
|
||||
use crate::handle::maintain::PunchSender;
|
||||
use crate::handle::recv_data::client::ClientPacketHandler;
|
||||
use crate::handle::recv_data::server::ServerPacketHandler;
|
||||
use crate::handle::recv_data::turn::TurnPacketHandler;
|
||||
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::util::U64Adder;
|
||||
|
||||
mod client;
|
||||
mod server;
|
||||
mod turn;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RecvDataHandler<Call> {
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
turn: TurnPacketHandler,
|
||||
client: ClientPacketHandler,
|
||||
server: ServerPacketHandler<Call>,
|
||||
counter: U64Adder,
|
||||
}
|
||||
|
||||
impl<Call: VntCallback> RecvChannelHandler for RecvDataHandler<Call> {
|
||||
fn handle(&mut self, buf: &mut [u8], route_key: RouteKey, context: &Context) {
|
||||
if let Err(e) = self.handle0(buf, route_key, context) {
|
||||
log::error!("[{}]-{:?}", thread::current().name().unwrap_or(""), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call: VntCallback> RecvDataHandler<Call> {
|
||||
pub fn new(
|
||||
#[cfg(feature = "server_encrypt")] rsa_cipher: Arc<Mutex<Option<RsaCipher>>>,
|
||||
server_cipher: Cipher,
|
||||
client_cipher: Cipher,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
device: Arc<Device>,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
config_info: BaseConfigInfo,
|
||||
nat_test: NatTest,
|
||||
callback: Call,
|
||||
punch_sender: PunchSender,
|
||||
peer_nat_info_map: Arc<RwLock<HashMap<Ipv4Addr, NatInfo>>>,
|
||||
external_route: ExternalRoute,
|
||||
route: AllowExternalRoute,
|
||||
#[cfg(feature = "ip_proxy")] ip_proxy_map: Option<IpProxyMap>,
|
||||
counter: U64Adder,
|
||||
handshake: Handshake,
|
||||
) -> Self {
|
||||
let server = ServerPacketHandler::new(
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
rsa_cipher,
|
||||
server_cipher,
|
||||
current_device.clone(),
|
||||
device.clone(),
|
||||
device_list,
|
||||
config_info,
|
||||
nat_test.clone(),
|
||||
callback,
|
||||
external_route,
|
||||
handshake,
|
||||
);
|
||||
let client = ClientPacketHandler::new(
|
||||
device.clone(),
|
||||
client_cipher,
|
||||
punch_sender,
|
||||
peer_nat_info_map,
|
||||
nat_test,
|
||||
route,
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
ip_proxy_map,
|
||||
);
|
||||
let turn = TurnPacketHandler::new();
|
||||
Self {
|
||||
current_device,
|
||||
turn,
|
||||
client,
|
||||
server,
|
||||
counter,
|
||||
}
|
||||
}
|
||||
fn handle0(
|
||||
&mut self,
|
||||
buf: &mut [u8],
|
||||
route_key: RouteKey,
|
||||
context: &Context,
|
||||
) -> io::Result<()> {
|
||||
// 统计流量
|
||||
self.counter.add(buf.len() as _);
|
||||
let net_packet = NetPacket::new(buf)?;
|
||||
if net_packet.ttl() == 0 || net_packet.source_ttl() < net_packet.ttl() {
|
||||
return Ok(());
|
||||
}
|
||||
let current_device = self.current_device.load();
|
||||
let dest = net_packet.destination();
|
||||
if dest == current_device.virtual_ip
|
||||
|| dest.is_broadcast()
|
||||
|| dest.is_multicast()
|
||||
|| dest == SELF_IP
|
||||
|| dest.is_unspecified()
|
||||
|| dest == current_device.broadcast_ip
|
||||
{
|
||||
//发给自己的包
|
||||
if net_packet.is_gateway() {
|
||||
//服务端-客户端包
|
||||
self.server
|
||||
.handle(net_packet, route_key, context, ¤t_device)
|
||||
} else {
|
||||
//客户端-客户端包
|
||||
self.client
|
||||
.handle(net_packet, route_key, context, ¤t_device)
|
||||
}
|
||||
} else {
|
||||
//转发包
|
||||
self.turn
|
||||
.handle(net_packet, route_key, context, ¤t_device)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PacketHandler {
|
||||
fn handle(
|
||||
&self,
|
||||
net_packet: NetPacket<&mut [u8]>,
|
||||
route_key: RouteKey,
|
||||
context: &Context,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
) -> io::Result<()>;
|
||||
}
|
||||
@@ -0,0 +1,484 @@
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::Mutex;
|
||||
use protobuf::Message;
|
||||
|
||||
use packet::icmp::{icmp, Kind};
|
||||
use packet::ip::ipv4;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
use tun::device::IFace;
|
||||
use tun::Device;
|
||||
|
||||
use crate::channel::context::Context;
|
||||
use crate::channel::{Route, RouteKey};
|
||||
use crate::cipher::Cipher;
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
use crate::cipher::RsaCipher;
|
||||
use crate::external_route::ExternalRoute;
|
||||
use crate::handle::callback::{ErrorInfo, ErrorType, HandshakeInfo, RegisterInfo, VntCallback};
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
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::nat::NatTest;
|
||||
use crate::proto;
|
||||
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, Version, MAX_TTL};
|
||||
|
||||
/// 处理来源于服务端的包
|
||||
#[derive(Clone)]
|
||||
pub struct ServerPacketHandler<Call> {
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
rsa_cipher: Arc<Mutex<Option<RsaCipher>>>,
|
||||
server_cipher: Cipher,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
device: Arc<Device>,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
config_info: BaseConfigInfo,
|
||||
nat_test: NatTest,
|
||||
callback: Call,
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
up_key_time: Arc<AtomicCell<Instant>>,
|
||||
route_record: Arc<Mutex<Vec<(Ipv4Addr, Ipv4Addr)>>>,
|
||||
external_route: ExternalRoute,
|
||||
handshake: Handshake,
|
||||
}
|
||||
|
||||
impl<Call> ServerPacketHandler<Call> {
|
||||
pub fn new(
|
||||
#[cfg(feature = "server_encrypt")] rsa_cipher: Arc<Mutex<Option<RsaCipher>>>,
|
||||
server_cipher: Cipher,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
device: Arc<Device>,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
config_info: BaseConfigInfo,
|
||||
nat_test: NatTest,
|
||||
callback: Call,
|
||||
external_route: ExternalRoute,
|
||||
handshake: Handshake,
|
||||
) -> Self {
|
||||
Self {
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
rsa_cipher,
|
||||
server_cipher,
|
||||
current_device,
|
||||
device,
|
||||
device_list,
|
||||
config_info,
|
||||
nat_test,
|
||||
callback,
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
up_key_time: Arc::new(AtomicCell::new(Instant::now() - Duration::from_secs(60))),
|
||||
route_record: Arc::new(Mutex::default()),
|
||||
external_route,
|
||||
handshake,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call: VntCallback> PacketHandler for ServerPacketHandler<Call> {
|
||||
fn handle(
|
||||
&self,
|
||||
mut net_packet: NetPacket<&mut [u8]>,
|
||||
route_key: RouteKey,
|
||||
context: &Context,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
) -> io::Result<()> {
|
||||
context
|
||||
.route_table
|
||||
.update_read_time(&net_packet.source(), &route_key);
|
||||
if net_packet.protocol() == Protocol::Error
|
||||
&& net_packet.transport_protocol()
|
||||
== crate::protocol::error_packet::Protocol::NoKey.into()
|
||||
{
|
||||
//服务端通知客户端上传密钥
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
{
|
||||
let mutex_guard = self.rsa_cipher.lock();
|
||||
if let Some(rsa_cipher) = mutex_guard.as_ref() {
|
||||
let last = self.up_key_time.load();
|
||||
if last.elapsed() < Duration::from_secs(1)
|
||||
|| self
|
||||
.up_key_time
|
||||
.compare_exchange(last, Instant::now())
|
||||
.is_err()
|
||||
{
|
||||
//短时间不重复上传服务端密钥
|
||||
return Ok(());
|
||||
}
|
||||
if let Some(key) = self.server_cipher.key() {
|
||||
log::info!("上传密钥到服务端:{:?}", route_key);
|
||||
let packet = handshaker::secret_handshake_request_packet(
|
||||
rsa_cipher,
|
||||
self.config_info.token.clone(),
|
||||
key,
|
||||
)?;
|
||||
context.send_by_key(packet.buffer(), route_key)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Ok(());
|
||||
} else if net_packet.protocol() == Protocol::Service
|
||||
&& net_packet.transport_protocol() == service_packet::Protocol::HandshakeResponse.into()
|
||||
{
|
||||
let response =
|
||||
HandshakeResponse::parse_from_bytes(net_packet.payload()).map_err(|e| {
|
||||
io::Error::new(io::ErrorKind::Other, format!("HandshakeResponse {:?}", e))
|
||||
})?;
|
||||
//如果开启了加密,则发送加密握手请求
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
if let Some(key) = self.server_cipher.key() {
|
||||
let rsa_cipher = RsaCipher::new(&response.public_key)?;
|
||||
let handshake_info = HandshakeInfo::new(
|
||||
rsa_cipher.public_key()?.clone(),
|
||||
rsa_cipher.finger()?,
|
||||
response.version,
|
||||
);
|
||||
log::info!("加密握手请求:{:?}", handshake_info);
|
||||
|
||||
if self.callback.handshake(handshake_info) {
|
||||
let packet = handshaker::secret_handshake_request_packet(
|
||||
&rsa_cipher,
|
||||
self.config_info.token.clone(),
|
||||
key,
|
||||
)?;
|
||||
context.send_by_key(packet.buffer(), route_key)?;
|
||||
self.rsa_cipher.lock().replace(rsa_cipher);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let handshake_info = HandshakeInfo::new_no_secret(response.version);
|
||||
if self.callback.handshake(handshake_info) {
|
||||
//没有加密,则发送注册请求
|
||||
self.register(current_device, context)?;
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
//服务端数据解密
|
||||
self.server_cipher.decrypt_ipv4(&mut net_packet)?;
|
||||
match net_packet.protocol() {
|
||||
Protocol::Service => {
|
||||
self.service(context, current_device, net_packet, route_key)?;
|
||||
}
|
||||
Protocol::Error => {
|
||||
self.error(context, current_device, net_packet, route_key)?;
|
||||
}
|
||||
Protocol::Control => {
|
||||
self.control(context, current_device, net_packet, route_key)?;
|
||||
}
|
||||
Protocol::IpTurn => {
|
||||
match ip_turn_packet::Protocol::from(net_packet.transport_protocol()) {
|
||||
ip_turn_packet::Protocol::Ipv4 => {
|
||||
let ipv4 = IpV4Packet::new(net_packet.payload())?;
|
||||
match ipv4.protocol() {
|
||||
ipv4::protocol::Protocol::Icmp => {
|
||||
if ipv4.destination_ip() == current_device.virtual_ip {
|
||||
let icmp_packet = icmp::IcmpPacket::new(ipv4.payload())?;
|
||||
if icmp_packet.kind() == Kind::EchoReply {
|
||||
//网关ip ping的回应
|
||||
self.device.write(net_packet.payload())?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
ip_turn_packet::Protocol::Ipv4Broadcast => {}
|
||||
ip_turn_packet::Protocol::Unknown(_) => {}
|
||||
}
|
||||
}
|
||||
Protocol::OtherTurn => {}
|
||||
Protocol::Unknown(_) => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call: VntCallback> ServerPacketHandler<Call> {
|
||||
fn service(
|
||||
&self,
|
||||
context: &Context,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
net_packet: NetPacket<&mut [u8]>,
|
||||
route_key: RouteKey,
|
||||
) -> io::Result<()> {
|
||||
match service_packet::Protocol::from(net_packet.transport_protocol()) {
|
||||
service_packet::Protocol::RegistrationResponse => {
|
||||
let response = RegistrationResponse::parse_from_bytes(net_packet.payload())
|
||||
.map_err(|e| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("RegistrationResponse {:?}", e),
|
||||
)
|
||||
})?;
|
||||
let virtual_ip = Ipv4Addr::from(response.virtual_ip);
|
||||
let virtual_netmask = Ipv4Addr::from(response.virtual_netmask);
|
||||
let virtual_gateway = Ipv4Addr::from(response.virtual_gateway);
|
||||
let virtual_network =
|
||||
Ipv4Addr::from(response.virtual_ip & response.virtual_netmask);
|
||||
let register_info = RegisterInfo::new(virtual_ip, virtual_netmask, virtual_gateway);
|
||||
log::info!("注册成功:{:?}", register_info);
|
||||
if self.callback.register(register_info) {
|
||||
let route = Route::from_default_rt(route_key, 1);
|
||||
context
|
||||
.route_table
|
||||
.add_route_if_absent(virtual_gateway, route);
|
||||
let old = current_device;
|
||||
let mut cur = *current_device;
|
||||
loop {
|
||||
let mut new_current_device = cur;
|
||||
new_current_device.update(virtual_ip, virtual_netmask, virtual_gateway);
|
||||
new_current_device.virtual_ip = virtual_ip;
|
||||
new_current_device.virtual_netmask = virtual_netmask;
|
||||
new_current_device.virtual_gateway = virtual_gateway;
|
||||
new_current_device.status = crate::handle::ConnectStatus::Connected;
|
||||
if let Err(c) = self
|
||||
.current_device
|
||||
.compare_exchange(cur, new_current_device)
|
||||
{
|
||||
cur = c;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let public_ip = response.public_ip.into();
|
||||
let public_port = response.public_port as u16;
|
||||
self.nat_test
|
||||
.update_addr(route_key.index(), public_ip, public_port);
|
||||
if old.virtual_ip != virtual_ip
|
||||
|| old.virtual_gateway != virtual_gateway
|
||||
|| old.virtual_netmask != virtual_netmask
|
||||
{
|
||||
if old.virtual_ip != Ipv4Addr::UNSPECIFIED {
|
||||
log::info!("ip发生变化,old:{:?},response={:?}", old, response);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
if let Err(e) = self.device.add_route(virtual_network, virtual_netmask, 1) {
|
||||
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);
|
||||
} else {
|
||||
guard.push((dest, mask));
|
||||
}
|
||||
}
|
||||
self.callback.success();
|
||||
}
|
||||
self.set_device_info_list(response.device_info_list, response.epoch as _);
|
||||
}
|
||||
}
|
||||
service_packet::Protocol::RegistrationRequest => {
|
||||
//不处理注册包
|
||||
}
|
||||
service_packet::Protocol::PollDeviceList => {}
|
||||
service_packet::Protocol::PushDeviceList => {
|
||||
let response = DeviceList::parse_from_bytes(net_packet.payload()).map_err(|e| {
|
||||
io::Error::new(io::ErrorKind::Other, format!("PushDeviceList {:?}", e))
|
||||
})?;
|
||||
self.set_device_info_list(response.device_info_list, response.epoch as _);
|
||||
}
|
||||
service_packet::Protocol::HandshakeRequest => {}
|
||||
service_packet::Protocol::HandshakeResponse => {}
|
||||
service_packet::Protocol::SecretHandshakeRequest => {}
|
||||
service_packet::Protocol::SecretHandshakeResponse => {
|
||||
log::info!("SecretHandshakeResponse");
|
||||
//加密握手结束,发送注册数据
|
||||
self.register(current_device, context)?;
|
||||
}
|
||||
service_packet::Protocol::Unknown(e) => {
|
||||
log::warn!("service_packet::Protocol::Unknown = {}", e);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn set_device_info_list(&self, device_info_list: Vec<proto::message::DeviceInfo>, epoch: u16) {
|
||||
let ip_list: Vec<PeerDeviceInfo> = device_info_list
|
||||
.into_iter()
|
||||
.map(|info| {
|
||||
PeerDeviceInfo::new(
|
||||
Ipv4Addr::from(info.virtual_ip),
|
||||
info.name,
|
||||
info.device_status as u8,
|
||||
info.client_secret,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let mut dev = self.device_list.lock();
|
||||
//这里可能会收到旧的消息,但是随着时间推移总会收到新的
|
||||
dev.0 = epoch;
|
||||
dev.1 = ip_list;
|
||||
}
|
||||
fn register(&self, current_device: &CurrentDeviceInfo, context: &Context) -> io::Result<()> {
|
||||
if current_device.status.online() {
|
||||
//已连接的不需要注册
|
||||
return Ok(());
|
||||
}
|
||||
let token = self.config_info.token.clone();
|
||||
let device_id = self.config_info.device_id.clone();
|
||||
let name = self.config_info.name.clone();
|
||||
let client_secret = self.config_info.client_secret;
|
||||
let ip = self.config_info.ip;
|
||||
let response = registrar::registration_request_packet(
|
||||
&self.server_cipher,
|
||||
token,
|
||||
device_id,
|
||||
name,
|
||||
ip,
|
||||
false,
|
||||
false,
|
||||
client_secret,
|
||||
)?;
|
||||
log::info!("发送注册请求,{:?}", self.config_info);
|
||||
//注册请求只发送到默认通道
|
||||
context.send_default(response.buffer(), current_device.connect_server)
|
||||
}
|
||||
fn error(
|
||||
&self,
|
||||
context: &Context,
|
||||
_current_device: &CurrentDeviceInfo,
|
||||
net_packet: NetPacket<&mut [u8]>,
|
||||
route_key: RouteKey,
|
||||
) -> io::Result<()> {
|
||||
match InErrorPacket::new(net_packet.transport_protocol(), net_packet.payload())? {
|
||||
InErrorPacket::TokenError => {
|
||||
// token错误,可能是服务端设置了白名单
|
||||
let err = ErrorInfo::new(ErrorType::TokenError);
|
||||
self.callback.error(err);
|
||||
}
|
||||
InErrorPacket::Disconnect => {
|
||||
crate::handle::change_status(&self.current_device, ConnectStatus::Connecting);
|
||||
let err = ErrorInfo::new(ErrorType::Disconnect);
|
||||
self.callback.error(err);
|
||||
//掉线epoch要归零
|
||||
{
|
||||
let mut dev = self.device_list.lock();
|
||||
dev.0 = 0;
|
||||
drop(dev);
|
||||
}
|
||||
self.handshake
|
||||
.send(context, self.config_info.client_secret, route_key.addr)?;
|
||||
// self.register(current_device, context, route_key)?;
|
||||
}
|
||||
InErrorPacket::AddressExhausted => {
|
||||
// 地址用尽
|
||||
let err = ErrorInfo::new(ErrorType::AddressExhausted);
|
||||
self.callback.error(err);
|
||||
}
|
||||
InErrorPacket::OtherError(e) => {
|
||||
let err = ErrorInfo::new_msg(ErrorType::Unknown, e.message()?);
|
||||
self.callback.error(err);
|
||||
}
|
||||
InErrorPacket::IpAlreadyExists => {
|
||||
let err = ErrorInfo::new(ErrorType::IpAlreadyExists);
|
||||
self.callback.error(err);
|
||||
}
|
||||
InErrorPacket::InvalidIp => {
|
||||
let err = ErrorInfo::new(ErrorType::InvalidIp);
|
||||
self.callback.error(err);
|
||||
}
|
||||
InErrorPacket::NoKey => {
|
||||
//这个类型最开头已经处理过,这里忽略
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn control(
|
||||
&self,
|
||||
context: &Context,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
net_packet: NetPacket<&mut [u8]>,
|
||||
route_key: RouteKey,
|
||||
) -> io::Result<()> {
|
||||
match ControlPacket::new(net_packet.transport_protocol(), net_packet.payload())? {
|
||||
ControlPacket::PongPacket(pong_packet) => {
|
||||
let current_time = crate::handle::now_time() as u16;
|
||||
if current_time < pong_packet.time() {
|
||||
return Ok(());
|
||||
}
|
||||
let metric = net_packet.source_ttl() - net_packet.ttl() + 1;
|
||||
let rt = (current_time - pong_packet.time()) as i64;
|
||||
let route = Route::from(route_key, metric, rt);
|
||||
context.route_table.add_route(net_packet.source(), route);
|
||||
let epoch = self.device_list.lock().0;
|
||||
if pong_packet.epoch() != epoch {
|
||||
//纪元不一致,可能有新客户端连接,向服务端拉取客户端列表
|
||||
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_version(Version::V1);
|
||||
poll_device.set_gateway_flag(true);
|
||||
poll_device.first_set_ttl(MAX_TTL);
|
||||
poll_device.set_protocol(Protocol::Service);
|
||||
poll_device
|
||||
.set_transport_protocol(service_packet::Protocol::PollDeviceList.into());
|
||||
self.server_cipher.encrypt_ipv4(&mut poll_device)?;
|
||||
//发送到默认服务端即可
|
||||
context.send_default(poll_device.buffer(), current_device.connect_server)?;
|
||||
}
|
||||
}
|
||||
ControlPacket::AddrResponse(addr_packet) => {
|
||||
//更新本地公网ipv4
|
||||
self.nat_test.update_addr(
|
||||
route_key.index(),
|
||||
addr_packet.ipv4(),
|
||||
addr_packet.port(),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
use crate::channel::context::Context;
|
||||
use crate::channel::RouteKey;
|
||||
use crate::handle::recv_data::PacketHandler;
|
||||
use crate::handle::CurrentDeviceInfo;
|
||||
use crate::protocol::NetPacket;
|
||||
|
||||
/// 处理客户端中转包
|
||||
#[derive(Clone)]
|
||||
pub struct TurnPacketHandler {}
|
||||
|
||||
impl TurnPacketHandler {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl PacketHandler for TurnPacketHandler {
|
||||
fn handle(
|
||||
&self,
|
||||
mut net_packet: NetPacket<&mut [u8]>,
|
||||
route_key: RouteKey,
|
||||
context: &Context,
|
||||
_current_device: &CurrentDeviceInfo,
|
||||
) -> std::io::Result<()> {
|
||||
// ttl减一
|
||||
let ttl = net_packet.incr_ttl();
|
||||
if ttl > 0 {
|
||||
let destination = net_packet.destination();
|
||||
if let Some(route) = context.route_table.route_one(&destination) {
|
||||
if route.addr == route_key.addr {
|
||||
//防止环路
|
||||
log::warn!(
|
||||
"来源和目标相同 {:?},{},{}",
|
||||
route_key,
|
||||
net_packet.source(),
|
||||
net_packet.destination()
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
if route.metric <= ttl {
|
||||
context.send_by_key(net_packet.buffer(), route.route_key())?;
|
||||
}
|
||||
}
|
||||
//其他没有路由的不转发
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use protobuf::Message;
|
||||
|
||||
use crate::cipher::Cipher;
|
||||
use crate::handle::{GATEWAY_IP, SELF_IP};
|
||||
use crate::proto::message::RegistrationRequest;
|
||||
use crate::protocol::body::ENCRYPTION_RESERVED;
|
||||
use crate::protocol::{service_packet, NetPacket, Protocol, Version, MAX_TTL};
|
||||
|
||||
/// 注册数据
|
||||
pub fn registration_request_packet(
|
||||
server_cipher: &Cipher,
|
||||
token: String,
|
||||
device_id: String,
|
||||
name: String,
|
||||
ip: Option<Ipv4Addr>,
|
||||
is_fast: bool,
|
||||
allow_ip_change: bool,
|
||||
client_secret: bool,
|
||||
) -> io::Result<NetPacket<Vec<u8>>> {
|
||||
let mut request = RegistrationRequest::new();
|
||||
request.token = token;
|
||||
request.device_id = device_id;
|
||||
request.name = name;
|
||||
if let Some(ip) = ip {
|
||||
request.virtual_ip = ip.into();
|
||||
}
|
||||
request.allow_ip_change = allow_ip_change;
|
||||
request.is_fast = is_fast;
|
||||
request.version = crate::VNT_VERSION.to_string();
|
||||
request.client_secret = client_secret;
|
||||
let bytes = request.write_to_bytes().map_err(|e| {
|
||||
io::Error::new(io::ErrorKind::Other, format!("RegistrationRequest {:?}", e))
|
||||
})?;
|
||||
let buf = vec![0u8; 12 + bytes.len() + ENCRYPTION_RESERVED];
|
||||
let mut net_packet = NetPacket::new_encrypt(buf)?;
|
||||
net_packet.set_destination(GATEWAY_IP);
|
||||
net_packet.set_source(SELF_IP);
|
||||
net_packet.set_version(Version::V1);
|
||||
net_packet.set_gateway_flag(true);
|
||||
net_packet.set_protocol(Protocol::Service);
|
||||
net_packet.set_transport_protocol(service_packet::Protocol::RegistrationRequest.into());
|
||||
net_packet.first_set_ttl(MAX_TTL);
|
||||
net_packet.set_payload(&bytes)?;
|
||||
server_cipher.encrypt_ipv4(&mut net_packet)?;
|
||||
Ok(net_packet)
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
use std::sync::mpsc::{sync_channel, Receiver, SendError, SyncSender};
|
||||
|
||||
pub fn channel_group<T>(size: usize, bound: usize) -> (GroupSyncSender<T>, Vec<Receiver<T>>) {
|
||||
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<T> {
|
||||
count: usize,
|
||||
base: Vec<SyncSender<T>>,
|
||||
}
|
||||
|
||||
impl<T> GroupSyncSender<T> {
|
||||
pub fn send(&mut self, t: T) -> Result<(), SendError<T>> {
|
||||
self.count += 1;
|
||||
self.base[self.count % self.base.len()].send(t)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::channel::context::Context;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
use packet::ip::ipv4::protocol::Protocol;
|
||||
|
||||
use crate::cipher::Cipher;
|
||||
use crate::external_route::ExternalRoute;
|
||||
use crate::handle::{check_dest, CurrentDeviceInfo};
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
use crate::ip_proxy::{IpProxyMap, ProxyHandler};
|
||||
use crate::protocol;
|
||||
use crate::protocol::body::ENCRYPTION_RESERVED;
|
||||
use crate::protocol::ip_turn_packet::BroadcastPacket;
|
||||
use crate::protocol::{ip_turn_packet, NetPacket, Version, MAX_TTL};
|
||||
|
||||
mod channel_group;
|
||||
pub mod tun_handler;
|
||||
|
||||
fn broadcast(
|
||||
server_cipher: &Cipher,
|
||||
sender: &Context,
|
||||
net_packet: &mut NetPacket<&mut [u8]>,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
) -> io::Result<()> {
|
||||
let mut peer_ips = Vec::with_capacity(8);
|
||||
let vec = sender.route_table.route_table_one();
|
||||
let mut relay_count = 0;
|
||||
const MAX_COUNT: usize = 8;
|
||||
for (peer_ip, route) in vec {
|
||||
if peer_ip == current_device.virtual_gateway {
|
||||
continue;
|
||||
}
|
||||
if peer_ips.len() == MAX_COUNT {
|
||||
break;
|
||||
}
|
||||
if route.is_p2p()
|
||||
&& sender
|
||||
.send_by_key(net_packet.buffer(), route.route_key())
|
||||
.is_ok()
|
||||
{
|
||||
peer_ips.push(peer_ip);
|
||||
} else {
|
||||
relay_count += 1;
|
||||
}
|
||||
}
|
||||
if relay_count == 0 && !peer_ips.is_empty() && peer_ips.len() != MAX_COUNT {
|
||||
//不需要转发
|
||||
return Ok(());
|
||||
}
|
||||
//转发到服务端的可选择广播,还要进行服务端加密
|
||||
if peer_ips.is_empty() {
|
||||
sender.send_default(net_packet.buffer(), current_device.connect_server)?;
|
||||
} else {
|
||||
let buf =
|
||||
vec![0u8; 12 + 1 + peer_ips.len() * 4 + net_packet.data_len() + ENCRYPTION_RESERVED];
|
||||
//剩余的发送到服务端,需要告知哪些已发送过
|
||||
let mut server_packet = NetPacket::new_encrypt(buf)?;
|
||||
server_packet.set_version(Version::V1);
|
||||
server_packet.set_gateway_flag(true);
|
||||
server_packet.first_set_ttl(MAX_TTL);
|
||||
server_packet.set_source(net_packet.source());
|
||||
//使用对应的目的地址
|
||||
server_packet.set_destination(net_packet.destination());
|
||||
server_packet.set_protocol(protocol::Protocol::IpTurn);
|
||||
server_packet.set_transport_protocol(ip_turn_packet::Protocol::Ipv4Broadcast.into());
|
||||
|
||||
let mut broadcast = BroadcastPacket::unchecked(server_packet.payload_mut());
|
||||
broadcast.set_address(&peer_ips)?;
|
||||
broadcast.set_data(net_packet.buffer())?;
|
||||
server_cipher.encrypt_ipv4(&mut server_packet)?;
|
||||
sender.send_default(server_packet.buffer(), current_device.connect_server)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 实现一个原地发送,必须保证是如下结构
|
||||
/// |12字节开头|ip报文|至少1024字节结尾|
|
||||
///
|
||||
#[inline]
|
||||
pub fn base_handle(
|
||||
context: &Context,
|
||||
buf: &mut [u8],
|
||||
data_len: usize, //数据总长度=12+ip包长度
|
||||
current_device: CurrentDeviceInfo,
|
||||
ip_route: &ExternalRoute,
|
||||
#[cfg(feature = "ip_proxy")] proxy_map: &Option<IpProxyMap>,
|
||||
client_cipher: &Cipher,
|
||||
server_cipher: &Cipher,
|
||||
) -> io::Result<()> {
|
||||
let ipv4_packet = IpV4Packet::new(&buf[12..data_len])?;
|
||||
let protocol = ipv4_packet.protocol();
|
||||
let src_ip = ipv4_packet.source_ip();
|
||||
let mut dest_ip = ipv4_packet.destination_ip();
|
||||
let mut net_packet = NetPacket::new0(data_len, buf)?;
|
||||
net_packet.set_version(Version::V1);
|
||||
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);
|
||||
if dest_ip == current_device.virtual_gateway {
|
||||
// 发到网关的加密方式不一样,要单独处理
|
||||
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)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
if dest_ip.is_multicast() {
|
||||
//当作广播处理
|
||||
dest_ip = Ipv4Addr::BROADCAST;
|
||||
net_packet.set_destination(Ipv4Addr::BROADCAST);
|
||||
}
|
||||
if dest_ip.is_broadcast() || current_device.broadcast_ip == dest_ip {
|
||||
// 广播 发送到直连目标
|
||||
client_cipher.encrypt_ipv4(&mut net_packet)?;
|
||||
broadcast(server_cipher, context, &mut net_packet, ¤t_device)?;
|
||||
return Ok(());
|
||||
}
|
||||
if !check_dest(
|
||||
dest_ip,
|
||||
current_device.virtual_netmask,
|
||||
current_device.virtual_network,
|
||||
) {
|
||||
if let Some(r_dest_ip) = ip_route.route(&dest_ip) {
|
||||
//路由的目标不能是自己
|
||||
if r_dest_ip == src_ip {
|
||||
return Ok(());
|
||||
}
|
||||
//需要修改目的地址
|
||||
dest_ip = r_dest_ip;
|
||||
net_packet.set_destination(r_dest_ip);
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
if let Some(proxy_map) = proxy_map {
|
||||
let mut ipv4_packet = IpV4Packet::new(net_packet.payload_mut())?;
|
||||
proxy_map.send_handle(&mut ipv4_packet)?;
|
||||
}
|
||||
client_cipher.encrypt_ipv4(&mut net_packet)?;
|
||||
context.send_ipv4_by_id(net_packet.buffer(), &dest_ip, current_device.connect_server)
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
use std::sync::Arc;
|
||||
use std::{io, thread};
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
|
||||
use packet::icmp::icmp::IcmpPacket;
|
||||
use packet::icmp::Kind;
|
||||
use packet::ip::ipv4;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
use tun::device::IFace;
|
||||
use tun::Device;
|
||||
|
||||
use crate::channel::context::Context;
|
||||
use crate::cipher::Cipher;
|
||||
use crate::external_route::ExternalRoute;
|
||||
use crate::handle::tun_tap::channel_group::{channel_group, GroupSyncSender};
|
||||
use crate::handle::CurrentDeviceInfo;
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
use crate::ip_proxy::IpProxyMap;
|
||||
use crate::util::{SingleU64Adder, StopManager};
|
||||
|
||||
fn icmp(device_writer: &Device, mut ipv4_packet: IpV4Packet<&mut [u8]>) -> io::Result<()> {
|
||||
if ipv4_packet.protocol() == ipv4::protocol::Protocol::Icmp {
|
||||
let mut icmp = IcmpPacket::new(ipv4_packet.payload_mut())?;
|
||||
if icmp.kind() == Kind::EchoRequest {
|
||||
icmp.set_kind(Kind::EchoReply);
|
||||
icmp.update_checksum();
|
||||
let src = ipv4_packet.source_ip();
|
||||
ipv4_packet.set_source_ip(ipv4_packet.destination_ip());
|
||||
ipv4_packet.set_destination_ip(src);
|
||||
ipv4_packet.update_checksum();
|
||||
device_writer.write(ipv4_packet.buffer)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 接收tun数据,并且转发到udp上
|
||||
fn handle(
|
||||
context: &Context,
|
||||
data: &mut [u8],
|
||||
len: usize,
|
||||
device_writer: &Device,
|
||||
current_device: CurrentDeviceInfo,
|
||||
ip_route: &ExternalRoute,
|
||||
#[cfg(feature = "ip_proxy")] proxy_map: &Option<IpProxyMap>,
|
||||
client_cipher: &Cipher,
|
||||
server_cipher: &Cipher,
|
||||
) -> io::Result<()> {
|
||||
//忽略掉结构不对的情况(ipv6数据、win tap会读到空数据),不然日志打印太多了
|
||||
let ipv4_packet = match IpV4Packet::new(&mut data[12..len]) {
|
||||
Ok(packet) => packet,
|
||||
Err(_) => return Ok(()),
|
||||
};
|
||||
let src_ip = ipv4_packet.source_ip();
|
||||
let dest_ip = ipv4_packet.destination_ip();
|
||||
if src_ip == dest_ip {
|
||||
return icmp(&device_writer, ipv4_packet);
|
||||
}
|
||||
return crate::handle::tun_tap::base_handle(
|
||||
context,
|
||||
data,
|
||||
len,
|
||||
current_device,
|
||||
ip_route,
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
proxy_map,
|
||||
client_cipher,
|
||||
server_cipher,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn start(
|
||||
stop_manager: StopManager,
|
||||
context: Context,
|
||||
device: Arc<Device>,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
ip_route: ExternalRoute,
|
||||
#[cfg(feature = "ip_proxy")] ip_proxy_map: Option<IpProxyMap>,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
parallel: usize,
|
||||
mut up_counter: SingleU64Adder,
|
||||
) -> io::Result<()> {
|
||||
let worker = {
|
||||
#[cfg(target_os = "macos")]
|
||||
let current_device = current_device.clone();
|
||||
let device = device.clone();
|
||||
stop_manager.add_listener("tun_device".into(), move || {
|
||||
if let Err(e) = device.shutdown() {
|
||||
log::warn!("{:?}", e);
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
let ip = current_device.load().virtual_ip;
|
||||
if let Ok(udp) = std::net::UdpSocket::bind("0.0.0.0:0") {
|
||||
let _ = udp.send_to(b"stop", format!("{:?}:1234", ip));
|
||||
}
|
||||
}
|
||||
})?
|
||||
};
|
||||
if parallel > 1 {
|
||||
let (sender, receivers) = channel_group::<(Vec<u8>, 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();
|
||||
thread::Builder::new()
|
||||
.name(format!("tunHandler-{}", index))
|
||||
.spawn(move || {
|
||||
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,
|
||||
&device,
|
||||
current_device.load(),
|
||||
&ip_route,
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
&ip_proxy_map,
|
||||
&client_cipher,
|
||||
&server_cipher,
|
||||
) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
log::warn!("{:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
})?;
|
||||
}
|
||||
thread::Builder::new()
|
||||
.name("tunHandlerM".into())
|
||||
.spawn(move || {
|
||||
if let Err(e) = start_multi(stop_manager, device, sender, &mut up_counter) {
|
||||
log::warn!("stop:{}", e);
|
||||
}
|
||||
worker.stop_all();
|
||||
})?;
|
||||
} else {
|
||||
thread::Builder::new()
|
||||
.name("tunHandlerS".into())
|
||||
.spawn(move || {
|
||||
if let Err(e) = start_simple(
|
||||
stop_manager,
|
||||
&context,
|
||||
device,
|
||||
current_device,
|
||||
ip_route,
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
ip_proxy_map,
|
||||
client_cipher,
|
||||
server_cipher,
|
||||
&mut up_counter,
|
||||
) {
|
||||
log::warn!("stop:{}", e);
|
||||
}
|
||||
worker.stop_all();
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn start_simple(
|
||||
stop_manager: StopManager,
|
||||
context: &Context,
|
||||
device: Arc<Device>,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
ip_route: ExternalRoute,
|
||||
#[cfg(feature = "ip_proxy")] ip_proxy_map: Option<IpProxyMap>,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
up_counter: &mut SingleU64Adder,
|
||||
) -> io::Result<()> {
|
||||
let mut buf = [0; 1024 * 16];
|
||||
loop {
|
||||
if stop_manager.is_stop() {
|
||||
return Ok(());
|
||||
}
|
||||
let len = device.read(&mut buf[12..])? + 12;
|
||||
//单线程的
|
||||
up_counter.add(len as u64);
|
||||
#[cfg(any(target_os = "macos"))]
|
||||
let mut buf = &mut buf[4..];
|
||||
// buf是重复利用的,需要重置头部
|
||||
buf[..12].fill(0);
|
||||
match handle(
|
||||
context,
|
||||
&mut buf,
|
||||
len,
|
||||
&device,
|
||||
current_device.load(),
|
||||
&ip_route,
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
&ip_proxy_map,
|
||||
&client_cipher,
|
||||
&server_cipher,
|
||||
) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
log::warn!("{:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn start_multi(
|
||||
stop_manager: StopManager,
|
||||
device: Arc<Device>,
|
||||
mut group_sync_sender: GroupSyncSender<(Vec<u8>, usize)>,
|
||||
up_counter: &mut SingleU64Adder,
|
||||
) -> io::Result<()> {
|
||||
loop {
|
||||
if stop_manager.is_stop() {
|
||||
return Ok(());
|
||||
}
|
||||
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(());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
use std::collections::HashMap;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||
use std::sync::Arc;
|
||||
use std::{io, thread};
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use mio::net::UdpSocket;
|
||||
use mio::{Events, Interest, Poll, Token, Waker};
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use packet::icmp::icmp;
|
||||
use packet::icmp::icmp::HeaderOther;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
|
||||
use crate::channel::context::Context;
|
||||
use crate::cipher::Cipher;
|
||||
use crate::handle::CurrentDeviceInfo;
|
||||
use crate::ip_proxy::ProxyHandler;
|
||||
use crate::protocol;
|
||||
use crate::protocol::{NetPacket, Version, MAX_TTL};
|
||||
use crate::util::StopManager;
|
||||
#[derive(Clone)]
|
||||
pub struct IcmpProxy {
|
||||
icmp_socket: Arc<std::net::UdpSocket>,
|
||||
// 对端-> 真实来源
|
||||
nat_map: Arc<Mutex<HashMap<(Ipv4Addr, u16, u16), Ipv4Addr>>>,
|
||||
}
|
||||
|
||||
impl IcmpProxy {
|
||||
pub fn new(
|
||||
context: Context,
|
||||
stop_manager: StopManager,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
client_cipher: Cipher,
|
||||
) -> io::Result<Self> {
|
||||
let icmp_socket = socket2::Socket::new(
|
||||
socket2::Domain::IPV4,
|
||||
socket2::Type::RAW,
|
||||
Some(socket2::Protocol::ICMPV4),
|
||||
)?;
|
||||
let addr: SocketAddrV4 = SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0);
|
||||
icmp_socket.bind(&socket2::SockAddr::from(addr))?;
|
||||
icmp_socket.set_nonblocking(true)?;
|
||||
let std_socket: std::net::UdpSocket = icmp_socket.into();
|
||||
let mio_icmp_socket = UdpSocket::from_std(std_socket.try_clone()?);
|
||||
let nat_map: Arc<Mutex<HashMap<(Ipv4Addr, u16, u16), Ipv4Addr>>> =
|
||||
Arc::new(Mutex::new(HashMap::with_capacity(16)));
|
||||
{
|
||||
let nat_map = nat_map.clone();
|
||||
thread::Builder::new()
|
||||
.name("icmpProxy".into())
|
||||
.spawn(move || {
|
||||
if let Err(e) = icmp_proxy(
|
||||
mio_icmp_socket,
|
||||
nat_map,
|
||||
context,
|
||||
stop_manager,
|
||||
current_device,
|
||||
client_cipher,
|
||||
) {
|
||||
log::warn!("icmp_proxy:{:?}", e);
|
||||
}
|
||||
})
|
||||
.expect("icmpProxy");
|
||||
}
|
||||
Ok(Self {
|
||||
icmp_socket: Arc::new(std_socket),
|
||||
nat_map,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const SERVER_VAL: usize = 0;
|
||||
const SERVER: Token = Token(SERVER_VAL);
|
||||
const NOTIFY_VAL: usize = 1;
|
||||
const NOTIFY: Token = Token(NOTIFY_VAL);
|
||||
|
||||
fn icmp_proxy(
|
||||
mut icmp_socket: UdpSocket,
|
||||
// 对端-> 真实来源
|
||||
nat_map: Arc<Mutex<HashMap<(Ipv4Addr, u16, u16), Ipv4Addr>>>,
|
||||
context: Context,
|
||||
stop_manager: StopManager,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
client_cipher: Cipher,
|
||||
) -> io::Result<()> {
|
||||
let mut poll = Poll::new()?;
|
||||
poll.registry()
|
||||
.register(&mut icmp_socket, SERVER, Interest::READABLE)?;
|
||||
let mut events = Events::with_capacity(32);
|
||||
let stop = Arc::new(Waker::new(poll.registry(), NOTIFY)?);
|
||||
let _stop = stop.clone();
|
||||
let _worker = stop_manager.add_listener("icmp_proxy".into(), move || {
|
||||
if let Err(e) = stop.wake() {
|
||||
log::warn!("stop icmp_proxy:{:?}", e);
|
||||
}
|
||||
})?;
|
||||
let mut buf = [0u8; 65535 - 20 - 8];
|
||||
loop {
|
||||
poll.poll(&mut events, None)?;
|
||||
if stop_manager.is_stop() {
|
||||
return Ok(());
|
||||
}
|
||||
for event in events.iter() {
|
||||
match event.token() {
|
||||
SERVER => readable_handle(
|
||||
&icmp_socket,
|
||||
&mut buf,
|
||||
&nat_map,
|
||||
&context,
|
||||
¤t_device,
|
||||
&client_cipher,
|
||||
),
|
||||
NOTIFY => {
|
||||
return Ok(());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn readable_handle(
|
||||
icmp_socket: &UdpSocket,
|
||||
buf: &mut [u8],
|
||||
nat_map: &Mutex<HashMap<(Ipv4Addr, u16, u16), Ipv4Addr>>,
|
||||
context: &Context,
|
||||
current_device: &AtomicCell<CurrentDeviceInfo>,
|
||||
client_cipher: &Cipher,
|
||||
) {
|
||||
loop {
|
||||
let (len, addr) = match icmp_socket.recv_from(&mut buf[12..]) {
|
||||
Ok(rs) => rs,
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::WouldBlock {
|
||||
break;
|
||||
}
|
||||
log::warn!("icmp_socket {:?}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
if let IpAddr::V4(peer_ip) = addr.ip() {
|
||||
recv_handle(
|
||||
buf,
|
||||
12 + len,
|
||||
peer_ip,
|
||||
&nat_map,
|
||||
&context,
|
||||
¤t_device,
|
||||
&client_cipher,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn recv_handle(
|
||||
buf: &mut [u8],
|
||||
data_len: usize,
|
||||
peer_ip: Ipv4Addr,
|
||||
nat_map: &Mutex<HashMap<(Ipv4Addr, u16, u16), Ipv4Addr>>,
|
||||
context: &Context,
|
||||
current_device: &AtomicCell<CurrentDeviceInfo>,
|
||||
client_cipher: &Cipher,
|
||||
) {
|
||||
match IpV4Packet::new(&mut buf[12..data_len]) {
|
||||
Ok(mut ipv4_packet) => match icmp::IcmpPacket::new(ipv4_packet.payload()) {
|
||||
Ok(icmp_packet) => match icmp_packet.header_other() {
|
||||
HeaderOther::Identifier(id, seq) => {
|
||||
if let Some(dest_ip) = nat_map.lock().get(&(peer_ip, id, seq)).cloned() {
|
||||
ipv4_packet.set_destination_ip(dest_ip);
|
||||
ipv4_packet.update_checksum();
|
||||
|
||||
let current_device = current_device.load();
|
||||
let virtual_ip = current_device.virtual_ip();
|
||||
|
||||
let mut net_packet = NetPacket::new0(data_len, buf).unwrap();
|
||||
net_packet.set_version(Version::V1);
|
||||
net_packet.set_protocol(protocol::Protocol::IpTurn);
|
||||
net_packet.set_transport_protocol(
|
||||
protocol::ip_turn_packet::Protocol::Ipv4.into(),
|
||||
);
|
||||
net_packet.first_set_ttl(MAX_TTL);
|
||||
net_packet.set_source(virtual_ip);
|
||||
net_packet.set_destination(dest_ip);
|
||||
if let Err(e) = client_cipher.encrypt_ipv4(&mut net_packet) {
|
||||
log::warn!("加密失败:{}", e);
|
||||
return;
|
||||
}
|
||||
if let Err(e) = context.send_ipv4_by_id(
|
||||
net_packet.buffer(),
|
||||
&dest_ip,
|
||||
current_device.connect_server,
|
||||
) {
|
||||
log::warn!("发送到目标失败:{}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Err(_) => {}
|
||||
},
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// icmp用Identifier来区分,没有Identifier的一律不转发
|
||||
impl ProxyHandler for IcmpProxy {
|
||||
fn recv_handle(
|
||||
&self,
|
||||
ipv4: &mut IpV4Packet<&mut [u8]>,
|
||||
source: Ipv4Addr,
|
||||
destination: Ipv4Addr,
|
||||
) -> io::Result<bool> {
|
||||
if ipv4.offset() != 0 || ipv4.flags() & 1 == 1 {
|
||||
// ip分片的直接丢弃
|
||||
return Ok(true);
|
||||
}
|
||||
let dest_ip = ipv4.destination_ip();
|
||||
//转发到代理目标地址
|
||||
let icmp_packet = icmp::IcmpPacket::new(ipv4.payload())?;
|
||||
match icmp_packet.header_other() {
|
||||
HeaderOther::Identifier(id, seq) => {
|
||||
self.nat_map.lock().insert((dest_ip, id, seq), source);
|
||||
self.icmp_socket.send_to(
|
||||
ipv4.payload(),
|
||||
SocketAddr::from(SocketAddrV4::new(dest_ip, 0)),
|
||||
)?;
|
||||
}
|
||||
_ => {
|
||||
log::warn!(
|
||||
"不支持的ip代理Icmp协议:{}->{}->{}",
|
||||
source,
|
||||
destination,
|
||||
dest_ip
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn send_handle(&self, _ipv4: &mut IpV4Packet<&mut [u8]>) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
|
||||
use packet::ip::ipv4;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
|
||||
use crate::channel::context::Context;
|
||||
use crate::cipher::Cipher;
|
||||
use crate::handle::CurrentDeviceInfo;
|
||||
use crate::ip_proxy::icmp_proxy::IcmpProxy;
|
||||
use crate::ip_proxy::tcp_proxy::TcpProxy;
|
||||
use crate::ip_proxy::udp_proxy::UdpProxy;
|
||||
use crate::util::{Scheduler, StopManager};
|
||||
|
||||
pub mod icmp_proxy;
|
||||
pub mod tcp_proxy;
|
||||
pub mod udp_proxy;
|
||||
|
||||
pub trait ProxyHandler {
|
||||
fn recv_handle(
|
||||
&self,
|
||||
ipv4: &mut IpV4Packet<&mut [u8]>,
|
||||
source: Ipv4Addr,
|
||||
destination: Ipv4Addr,
|
||||
) -> io::Result<bool>;
|
||||
fn send_handle(&self, ipv4: &mut IpV4Packet<&mut [u8]>) -> io::Result<()>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct IpProxyMap {
|
||||
icmp_proxy: IcmpProxy,
|
||||
tcp_proxy: TcpProxy,
|
||||
udp_proxy: UdpProxy,
|
||||
}
|
||||
|
||||
pub fn init_proxy(
|
||||
context: Context,
|
||||
scheduler: Scheduler,
|
||||
stop_manager: StopManager,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
client_cipher: Cipher,
|
||||
) -> io::Result<IpProxyMap> {
|
||||
let icmp_proxy = IcmpProxy::new(context, stop_manager.clone(), current_device, client_cipher)?;
|
||||
let tcp_proxy = TcpProxy::new(stop_manager.clone())?;
|
||||
let udp_proxy = UdpProxy::new(scheduler, stop_manager)?;
|
||||
|
||||
Ok(IpProxyMap {
|
||||
icmp_proxy,
|
||||
tcp_proxy,
|
||||
udp_proxy,
|
||||
})
|
||||
}
|
||||
|
||||
impl ProxyHandler for IpProxyMap {
|
||||
fn recv_handle(
|
||||
&self,
|
||||
ipv4: &mut IpV4Packet<&mut [u8]>,
|
||||
source: Ipv4Addr,
|
||||
destination: Ipv4Addr,
|
||||
) -> io::Result<bool> {
|
||||
match ipv4.protocol() {
|
||||
ipv4::protocol::Protocol::Tcp => self.tcp_proxy.recv_handle(ipv4, source, destination),
|
||||
ipv4::protocol::Protocol::Udp => self.udp_proxy.recv_handle(ipv4, source, destination),
|
||||
ipv4::protocol::Protocol::Icmp => {
|
||||
self.icmp_proxy.recv_handle(ipv4, source, destination)
|
||||
}
|
||||
_ => {
|
||||
log::warn!(
|
||||
"不支持的ip代理ipv4协议{:?}:{}->{}->{}",
|
||||
ipv4.protocol(),
|
||||
source,
|
||||
destination,
|
||||
ipv4.destination_ip()
|
||||
);
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn send_handle(&self, ipv4: &mut IpV4Packet<&mut [u8]>) -> io::Result<()> {
|
||||
match ipv4.protocol() {
|
||||
ipv4::protocol::Protocol::Tcp => self.tcp_proxy.send_handle(ipv4),
|
||||
ipv4::protocol::Protocol::Udp => self.udp_proxy.send_handle(ipv4),
|
||||
ipv4::protocol::Protocol::Icmp => self.icmp_proxy.send_handle(ipv4),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,457 @@
|
||||
use std::io::{Read, Write};
|
||||
use std::net::{Ipv4Addr, Shutdown, SocketAddrV4};
|
||||
#[cfg(unix)]
|
||||
use std::os::fd::AsRawFd;
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::io::AsRawSocket;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::{collections::HashMap, io, net::SocketAddr, thread};
|
||||
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use mio::net::TcpStream;
|
||||
use mio::{net::TcpListener, Events, Interest, Poll, Registry, Token, Waker};
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
use packet::tcp::tcp::TcpPacket;
|
||||
|
||||
use crate::ip_proxy::ProxyHandler;
|
||||
use crate::util::StopManager;
|
||||
|
||||
const SERVER_VAL: usize = 0;
|
||||
const SERVER: Token = Token(SERVER_VAL);
|
||||
const NOTIFY_VAL: usize = 1;
|
||||
const NOTIFY: Token = Token(NOTIFY_VAL);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TcpProxy {
|
||||
port: u16,
|
||||
nat_map: Arc<Mutex<HashMap<SocketAddrV4, SocketAddrV4>>>,
|
||||
}
|
||||
|
||||
impl TcpProxy {
|
||||
pub fn new(stop_manager: StopManager) -> io::Result<Self> {
|
||||
let nat_map: Arc<Mutex<HashMap<SocketAddrV4, SocketAddrV4>>> =
|
||||
Arc::new(Mutex::new(HashMap::with_capacity(16)));
|
||||
let tcp_listener = TcpListener::bind(format!("0.0.0.0:{}", 0).parse().unwrap())?;
|
||||
let port = tcp_listener.local_addr()?.port();
|
||||
{
|
||||
let nat_map = nat_map.clone();
|
||||
thread::Builder::new()
|
||||
.name("tcpProxy".into())
|
||||
.spawn(move || {
|
||||
if let Err(e) = tcp_proxy(tcp_listener, nat_map, stop_manager) {
|
||||
log::warn!("tcp_proxy:{:?}", e);
|
||||
}
|
||||
})
|
||||
.expect("tcpProxy");
|
||||
}
|
||||
Ok(Self { port, nat_map })
|
||||
}
|
||||
}
|
||||
|
||||
impl ProxyHandler for TcpProxy {
|
||||
fn recv_handle(
|
||||
&self,
|
||||
ipv4: &mut IpV4Packet<&mut [u8]>,
|
||||
source: Ipv4Addr,
|
||||
destination: Ipv4Addr,
|
||||
) -> io::Result<bool> {
|
||||
let dest_ip = ipv4.destination_ip();
|
||||
//转发到代理目标地址
|
||||
let mut tcp_packet = TcpPacket::new(source, destination, ipv4.payload_mut())?;
|
||||
let source_port = tcp_packet.source_port();
|
||||
let dest_port = tcp_packet.destination_port();
|
||||
tcp_packet.set_destination_port(self.port);
|
||||
tcp_packet.update_checksum();
|
||||
ipv4.set_destination_ip(destination);
|
||||
ipv4.update_checksum();
|
||||
let key = SocketAddrV4::new(source, source_port);
|
||||
self.nat_map
|
||||
.lock()
|
||||
.insert(key, SocketAddrV4::new(dest_ip, dest_port));
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn send_handle(&self, ipv4: &mut IpV4Packet<&mut [u8]>) -> io::Result<()> {
|
||||
let src_ip = ipv4.source_ip();
|
||||
let dest_ip = ipv4.destination_ip();
|
||||
let dest_addr = {
|
||||
let tcp_packet = TcpPacket::new(src_ip, dest_ip, ipv4.payload_mut())?;
|
||||
SocketAddrV4::new(dest_ip, tcp_packet.destination_port())
|
||||
};
|
||||
if let Some(source_addr) = self.nat_map.lock().get(&dest_addr) {
|
||||
let source_ip = *source_addr.ip();
|
||||
let mut tcp_packet = TcpPacket::new(source_ip, dest_ip, ipv4.payload_mut())?;
|
||||
tcp_packet.set_source_port(source_addr.port());
|
||||
tcp_packet.update_checksum();
|
||||
ipv4.set_source_ip(source_ip);
|
||||
ipv4.update_checksum();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn tcp_proxy(
|
||||
mut tcp_listener: TcpListener,
|
||||
nat_map: Arc<Mutex<HashMap<SocketAddrV4, SocketAddrV4>>>,
|
||||
stop_manager: StopManager,
|
||||
) -> io::Result<()> {
|
||||
let mut poll = Poll::new()?;
|
||||
poll.registry()
|
||||
.register(&mut tcp_listener, SERVER, Interest::READABLE)?;
|
||||
let mut events = Events::with_capacity(32);
|
||||
let mut tcp_map: HashMap<usize, ProxyValue> = HashMap::with_capacity(16);
|
||||
let mut mapping: HashMap<usize, usize> = HashMap::with_capacity(16);
|
||||
let stop = Arc::new(Waker::new(poll.registry(), NOTIFY)?);
|
||||
let _stop = stop.clone();
|
||||
let _worker = stop_manager.add_listener("tcp_proxy".into(), move || {
|
||||
if let Err(e) = stop.wake() {
|
||||
log::warn!("stop tcp_proxy:{:?}", e);
|
||||
}
|
||||
})?;
|
||||
loop {
|
||||
poll.poll(&mut events, None)?;
|
||||
if stop_manager.is_stop() {
|
||||
return Ok(());
|
||||
}
|
||||
for event in events.iter() {
|
||||
match event.token() {
|
||||
SERVER => {
|
||||
accept_handle(
|
||||
poll.registry(),
|
||||
&tcp_listener,
|
||||
&nat_map,
|
||||
&mut tcp_map,
|
||||
&mut mapping,
|
||||
);
|
||||
}
|
||||
NOTIFY => {
|
||||
return Ok(());
|
||||
}
|
||||
Token(index) => {
|
||||
let (val, src_index) = if let Some(v) = tcp_map.get_mut(&index) {
|
||||
(v, index)
|
||||
} else {
|
||||
if let Some(dest_index) = mapping.get(&index) {
|
||||
if let Some(v) = tcp_map.get_mut(dest_index) {
|
||||
(v, *dest_index)
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let (stream1, stream2, buf1, buf2, state1, state2) = val.as_mut(index);
|
||||
if event.is_readable() {
|
||||
if let Err(_) = readable_handle(stream1, stream2, buf1, state2) {
|
||||
*state1 |= READ_CLOSED;
|
||||
}
|
||||
}
|
||||
if event.is_writable() {
|
||||
let read = buf2.len() >= BUF_LEN;
|
||||
if let Err(_) = writable_handle(stream1, buf2) {
|
||||
*state1 |= WRITE_CLOSED;
|
||||
} else if read {
|
||||
if readable_handle(stream2, stream1, buf2, state1).is_err() {
|
||||
*state2 |= READ_CLOSED;
|
||||
}
|
||||
}
|
||||
}
|
||||
if event.is_read_closed() || event.is_error() {
|
||||
*state1 |= READ_CLOSED;
|
||||
}
|
||||
if event.is_write_closed() || event.is_error() {
|
||||
*state1 |= WRITE_CLOSED;
|
||||
}
|
||||
if is_write_closed(*state1) {
|
||||
let _ = stream1.shutdown(Shutdown::Write);
|
||||
let _ = stream2.shutdown(Shutdown::Read);
|
||||
}
|
||||
if is_read_closed(*state1) {
|
||||
let _ = stream1.shutdown(Shutdown::Read);
|
||||
if buf1.is_empty() {
|
||||
let _ = stream2.shutdown(Shutdown::Write);
|
||||
}
|
||||
}
|
||||
if (is_both_closed(*state1) && buf1.is_empty())
|
||||
|| (is_both_closed(*state2) && buf2.is_empty())
|
||||
|| (is_write_closed(*state1) && is_write_closed(*state2)
|
||||
|| (is_read_closed(*state1)
|
||||
&& is_read_closed(*state2)
|
||||
&& buf1.is_empty()
|
||||
&& buf2.is_empty()))
|
||||
{
|
||||
close(src_index, &mut tcp_map, &mut mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn accept_handle(
|
||||
registry: &Registry,
|
||||
tcp_listener: &TcpListener,
|
||||
nat_map: &Mutex<HashMap<SocketAddrV4, SocketAddrV4>>,
|
||||
tcp_map: &mut HashMap<usize, ProxyValue>,
|
||||
mapping: &mut HashMap<usize, usize>,
|
||||
) {
|
||||
loop {
|
||||
match tcp_listener.accept() {
|
||||
Ok((mut src_stream, addr)) => {
|
||||
#[cfg(windows)]
|
||||
let src_fd = src_stream.as_raw_socket() as usize;
|
||||
#[cfg(unix)]
|
||||
let src_fd = src_stream.as_raw_fd() as usize;
|
||||
if src_fd == SERVER_VAL || src_fd == NOTIFY_VAL {
|
||||
log::error!("fd错误:{:?}", src_fd);
|
||||
continue;
|
||||
}
|
||||
let addr = match addr {
|
||||
SocketAddr::V4(addr) => addr,
|
||||
SocketAddr::V6(_) => {
|
||||
// 忽略ipv6
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let _ = src_stream.set_nodelay(false);
|
||||
if let Some(dest_addr) = nat_map.lock().get(&addr).cloned() {
|
||||
match tcp_connect(addr.port(), dest_addr.into()) {
|
||||
Ok(mut dest_stream) => {
|
||||
#[cfg(windows)]
|
||||
let dest_fd = dest_stream.as_raw_socket() as usize;
|
||||
#[cfg(unix)]
|
||||
let dest_fd = dest_stream.as_raw_fd() as usize;
|
||||
if dest_fd == SERVER_VAL || dest_fd == NOTIFY_VAL {
|
||||
log::error!("fd错误:{:?}", dest_fd);
|
||||
continue;
|
||||
}
|
||||
if let Err(e) = registry.register(
|
||||
&mut src_stream,
|
||||
Token(src_fd),
|
||||
Interest::READABLE.add(Interest::WRITABLE),
|
||||
) {
|
||||
log::error!("register src_stream:{:?}", e);
|
||||
continue;
|
||||
}
|
||||
if let Err(e) = registry.register(
|
||||
&mut dest_stream,
|
||||
Token(dest_fd),
|
||||
Interest::READABLE.add(Interest::WRITABLE),
|
||||
) {
|
||||
log::error!("register dest_stream:{:?}", e);
|
||||
continue;
|
||||
}
|
||||
tcp_map.insert(
|
||||
src_fd,
|
||||
ProxyValue::new(src_stream, dest_stream, src_fd, dest_fd),
|
||||
);
|
||||
mapping.insert(dest_fd, src_fd);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("connect:{:?} {}->{}", e, addr, dest_addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::WouldBlock {
|
||||
break;
|
||||
}
|
||||
log::error!("accept:{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn tcp_connect(src_port: u16, addr: SocketAddr) -> io::Result<TcpStream> {
|
||||
let socket = socket2::Socket::new(
|
||||
socket2::Domain::IPV4,
|
||||
socket2::Type::STREAM,
|
||||
Some(socket2::Protocol::TCP),
|
||||
)?;
|
||||
if socket
|
||||
.bind(&SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, src_port).into())
|
||||
.is_err()
|
||||
{
|
||||
socket.bind(&SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0).into())?;
|
||||
}
|
||||
if let Err(e) = socket.set_tcp_keepalive(
|
||||
&socket2::TcpKeepalive::new()
|
||||
.with_time(Duration::from_secs(120))
|
||||
.with_interval(Duration::from_secs(10)),
|
||||
) {
|
||||
log::warn!("set_tcp_keepalive err {:?}", e);
|
||||
}
|
||||
let _ = socket.set_nodelay(false);
|
||||
socket.connect_timeout(&addr.into(), Duration::from_secs(3))?;
|
||||
socket.set_nonblocking(true)?;
|
||||
Ok(TcpStream::from_std(socket.into()))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ProxyValue {
|
||||
src_stream: TcpStream,
|
||||
dest_stream: TcpStream,
|
||||
src_fd: usize,
|
||||
dest_fd: usize,
|
||||
src_buf: BytesMut,
|
||||
dest_buf: BytesMut,
|
||||
src_state: u8,
|
||||
dest_state: u8,
|
||||
}
|
||||
|
||||
const BUF_LEN: usize = 65536;
|
||||
|
||||
impl ProxyValue {
|
||||
fn new(src_stream: TcpStream, dest_stream: TcpStream, src_fd: usize, dest_fd: usize) -> Self {
|
||||
Self {
|
||||
src_stream,
|
||||
dest_stream,
|
||||
src_fd,
|
||||
dest_fd,
|
||||
src_buf: BytesMut::with_capacity(BUF_LEN),
|
||||
dest_buf: BytesMut::with_capacity(BUF_LEN),
|
||||
src_state: NORMAL,
|
||||
dest_state: NORMAL,
|
||||
}
|
||||
}
|
||||
fn as_mut(
|
||||
&mut self,
|
||||
index: usize,
|
||||
) -> (
|
||||
&mut TcpStream,
|
||||
&mut TcpStream,
|
||||
&mut BytesMut,
|
||||
&mut BytesMut,
|
||||
&mut u8,
|
||||
&mut u8,
|
||||
) {
|
||||
if index == self.src_fd {
|
||||
(
|
||||
&mut self.src_stream,
|
||||
&mut self.dest_stream,
|
||||
&mut self.src_buf,
|
||||
&mut self.dest_buf,
|
||||
&mut self.src_state,
|
||||
&mut self.dest_state,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
&mut self.dest_stream,
|
||||
&mut self.src_stream,
|
||||
&mut self.dest_buf,
|
||||
&mut self.src_buf,
|
||||
&mut self.dest_state,
|
||||
&mut self.src_state,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn readable_handle(
|
||||
stream1: &mut TcpStream,
|
||||
stream2: &mut TcpStream,
|
||||
mid_buf: &mut BytesMut,
|
||||
state2: &mut u8,
|
||||
) -> io::Result<()> {
|
||||
let mut buf = [0; BUF_LEN];
|
||||
|
||||
loop {
|
||||
if mid_buf.len() >= BUF_LEN {
|
||||
// 达到上限不再继续读取
|
||||
return Ok(());
|
||||
}
|
||||
match stream1.read(&mut buf) {
|
||||
Ok(len) => {
|
||||
if len == 0 {
|
||||
return Err(io::Error::from(io::ErrorKind::UnexpectedEof));
|
||||
}
|
||||
let mut buf = &buf[..len];
|
||||
if mid_buf.is_empty() {
|
||||
// 直接写入,避免在buf中过渡
|
||||
while !buf.is_empty() {
|
||||
match stream2.write(buf) {
|
||||
Ok(end) => {
|
||||
if end == 0 {
|
||||
*state2 |= WRITE_CLOSED;
|
||||
return Err(io::Error::from(io::ErrorKind::WriteZero));
|
||||
}
|
||||
buf = &buf[end..];
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() != io::ErrorKind::WouldBlock {
|
||||
*state2 |= WRITE_CLOSED;
|
||||
return Err(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if buf.is_empty() {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
mid_buf.reserve(buf.len());
|
||||
mid_buf.put_slice(buf);
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::WouldBlock {
|
||||
break;
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn writable_handle(stream: &mut TcpStream, mid_buf: &mut BytesMut) -> io::Result<()> {
|
||||
while !mid_buf.is_empty() {
|
||||
match stream.write(&mid_buf) {
|
||||
Ok(len) => {
|
||||
let _ = mid_buf.split_to(len);
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::WouldBlock {
|
||||
break;
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn close(
|
||||
index: usize,
|
||||
tcp_map: &mut HashMap<usize, ProxyValue>,
|
||||
mapping: &mut HashMap<usize, usize>,
|
||||
) {
|
||||
if let Some(val) = tcp_map.remove(&index) {
|
||||
let _ = val.src_stream.shutdown(Shutdown::Both);
|
||||
let _ = val.dest_stream.shutdown(Shutdown::Both);
|
||||
mapping.remove(&val.src_fd);
|
||||
mapping.remove(&val.dest_fd);
|
||||
}
|
||||
}
|
||||
|
||||
const NORMAL: u8 = 0b00;
|
||||
const READ_CLOSED: u8 = 0b01;
|
||||
const WRITE_CLOSED: u8 = 0b10;
|
||||
const BOTH_CLOSED: u8 = 0b11;
|
||||
|
||||
fn is_read_closed(state: u8) -> bool {
|
||||
(state & READ_CLOSED == READ_CLOSED) || is_both_closed(state)
|
||||
}
|
||||
|
||||
fn is_write_closed(state: u8) -> bool {
|
||||
(state & WRITE_CLOSED == WRITE_CLOSED) || is_both_closed(state)
|
||||
}
|
||||
|
||||
fn is_both_closed(state: u8) -> bool {
|
||||
state & BOTH_CLOSED == BOTH_CLOSED
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
use std::net::{Ipv4Addr, SocketAddrV4};
|
||||
#[cfg(unix)]
|
||||
use std::os::fd::AsRawFd;
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::io::AsRawSocket;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{collections::HashMap, io, net::SocketAddr, rc::Rc, thread};
|
||||
|
||||
use mio::{net::UdpSocket, Events, Interest, Poll, Token};
|
||||
use mio::{Registry, Waker};
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
use packet::udp::udp::UdpPacket;
|
||||
|
||||
use crate::ip_proxy::ProxyHandler;
|
||||
use crate::util::{Scheduler, StopManager};
|
||||
|
||||
const SERVER_VAL: usize = 0;
|
||||
const SERVER: Token = Token(SERVER_VAL);
|
||||
const NOTIFY_VAL: usize = 1;
|
||||
const NOTIFY: Token = Token(NOTIFY_VAL);
|
||||
// 开了ip代理后使用mstsc,mstsc会误以为在真实局域网,从而不维护udp心跳,导致断连,所以这里尽量长一点过期时间
|
||||
const NAT_TIMEOUT: Duration = Duration::from_secs(20 * 60);
|
||||
const NAT_FAST_TIMEOUT: Duration = Duration::from_secs(5 * 60);
|
||||
const NAT_MAX: usize = 5_000;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UdpProxy {
|
||||
port: u16,
|
||||
nat_map: Arc<Mutex<HashMap<SocketAddrV4, SocketAddrV4>>>,
|
||||
}
|
||||
|
||||
impl UdpProxy {
|
||||
pub fn new(scheduler: Scheduler, stop_manager: StopManager) -> io::Result<Self> {
|
||||
let nat_map: Arc<Mutex<HashMap<SocketAddrV4, SocketAddrV4>>> =
|
||||
Arc::new(Mutex::new(HashMap::with_capacity(16)));
|
||||
let udp = UdpSocket::bind(format!("0.0.0.0:{}", 0).parse().unwrap())?;
|
||||
let port = udp.local_addr()?.port();
|
||||
{
|
||||
let nat_map = nat_map.clone();
|
||||
thread::Builder::new()
|
||||
.name("udpProxy".into())
|
||||
.spawn(move || {
|
||||
if let Err(e) = udp_proxy(udp, nat_map, scheduler, stop_manager) {
|
||||
log::warn!("udp_proxy:{:?}", e);
|
||||
}
|
||||
})
|
||||
.expect("udpProxy");
|
||||
}
|
||||
Ok(Self { port, nat_map })
|
||||
}
|
||||
}
|
||||
|
||||
impl ProxyHandler for UdpProxy {
|
||||
fn recv_handle(
|
||||
&self,
|
||||
ipv4: &mut IpV4Packet<&mut [u8]>,
|
||||
source: Ipv4Addr,
|
||||
destination: Ipv4Addr,
|
||||
) -> io::Result<bool> {
|
||||
let dest_ip = ipv4.destination_ip();
|
||||
//转发到代理目标地址
|
||||
let mut udp_packet = UdpPacket::new(source, destination, ipv4.payload_mut())?;
|
||||
let source_port = udp_packet.source_port();
|
||||
let dest_port = udp_packet.destination_port();
|
||||
udp_packet.set_destination_port(self.port);
|
||||
udp_packet.update_checksum();
|
||||
ipv4.set_destination_ip(destination);
|
||||
ipv4.update_checksum();
|
||||
let key = SocketAddrV4::new(source, source_port);
|
||||
self.nat_map
|
||||
.lock()
|
||||
.insert(key.into(), SocketAddrV4::new(dest_ip, dest_port).into());
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn send_handle(&self, ipv4: &mut IpV4Packet<&mut [u8]>) -> io::Result<()> {
|
||||
let src_ip = ipv4.source_ip();
|
||||
let dest_ip = ipv4.destination_ip();
|
||||
let dest_addr = {
|
||||
let udp_packet = UdpPacket::new(src_ip, dest_ip, ipv4.payload_mut())?;
|
||||
SocketAddrV4::new(dest_ip, udp_packet.destination_port())
|
||||
};
|
||||
if let Some(source_addr) = self.nat_map.lock().get(&dest_addr) {
|
||||
let source_ip = *source_addr.ip();
|
||||
let mut udp_packet = UdpPacket::new(source_ip, dest_ip, ipv4.payload_mut())?;
|
||||
udp_packet.set_source_port(source_addr.port());
|
||||
udp_packet.update_checksum();
|
||||
ipv4.set_source_ip(source_ip);
|
||||
ipv4.update_checksum();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn udp_proxy(
|
||||
mut udp: UdpSocket,
|
||||
nat_map: Arc<Mutex<HashMap<SocketAddrV4, SocketAddrV4>>>,
|
||||
scheduler: Scheduler,
|
||||
stop_manager: StopManager,
|
||||
) -> io::Result<()> {
|
||||
let mut poll = Poll::new()?;
|
||||
|
||||
poll.registry()
|
||||
.register(&mut udp, SERVER, Interest::READABLE)?;
|
||||
let mut events = Events::with_capacity(32);
|
||||
let mut buf = [0; 65536];
|
||||
let mut token_map: HashMap<Token, (Rc<UdpSocket>, SocketAddrV4, Instant)> =
|
||||
HashMap::with_capacity(64);
|
||||
let mut udp_map: HashMap<SocketAddrV4, (Rc<UdpSocket>, Instant)> = HashMap::with_capacity(64);
|
||||
let mut timeout = false;
|
||||
let waker = Arc::new(Waker::new(poll.registry(), NOTIFY)?);
|
||||
let stop = waker.clone();
|
||||
let _worker = stop_manager.add_listener("udp_proxy".into(), move || {
|
||||
if let Err(e) = stop.wake() {
|
||||
log::warn!("stop udp_proxy:{:?}", e);
|
||||
}
|
||||
})?;
|
||||
loop {
|
||||
let mut check = false;
|
||||
if token_map.is_empty() {
|
||||
poll.poll(&mut events, None)?;
|
||||
} else {
|
||||
//所有事件 50分钟超时
|
||||
if let Err(e) = poll.poll(&mut events, Some(Duration::from_secs(50 * 60))) {
|
||||
if e.kind() == io::ErrorKind::TimedOut || e.kind() == io::ErrorKind::WouldBlock {
|
||||
token_map.clear();
|
||||
udp_map.clear();
|
||||
continue;
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
if stop_manager.is_stop() {
|
||||
return Ok(());
|
||||
}
|
||||
for event in events.iter() {
|
||||
match event.token() {
|
||||
SERVER => server_handle(
|
||||
poll.registry(),
|
||||
&udp,
|
||||
&nat_map,
|
||||
&mut token_map,
|
||||
&mut udp_map,
|
||||
&mut buf,
|
||||
),
|
||||
NOTIFY => {
|
||||
check = true;
|
||||
}
|
||||
token => {
|
||||
if let Err(e) = readable_handle(&udp, &mut token_map, &token, &mut buf) {
|
||||
log::error!("发送目标失败:{:?}", e);
|
||||
if let Some((_, src_addr, _)) = token_map.remove(&token) {
|
||||
udp_map.remove(&src_addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if check {
|
||||
//超时校验
|
||||
if token_map.len() > NAT_MAX / 2 {
|
||||
check_handle(&mut token_map, &mut udp_map, NAT_FAST_TIMEOUT)
|
||||
} else {
|
||||
check_handle(&mut token_map, &mut udp_map, NAT_TIMEOUT)
|
||||
}
|
||||
timeout = false;
|
||||
}
|
||||
if !token_map.is_empty() && !timeout {
|
||||
//注册超时监听
|
||||
timeout = true;
|
||||
let waker = waker.clone();
|
||||
scheduler.timeout(NAT_FAST_TIMEOUT, move |_| {
|
||||
let _ = waker.wake();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_handle(
|
||||
token_map: &mut HashMap<Token, (Rc<UdpSocket>, SocketAddrV4, Instant)>,
|
||||
udp_map: &mut HashMap<SocketAddrV4, (Rc<UdpSocket>, Instant)>,
|
||||
timeout: Duration,
|
||||
) {
|
||||
let mut remove_list = Vec::new();
|
||||
for (token, (_, addr, time)) in token_map.iter() {
|
||||
if time.elapsed() > timeout {
|
||||
if let Some((_, time)) = udp_map.get(addr) {
|
||||
if time.elapsed() > timeout {
|
||||
//映射超时,需要移除
|
||||
remove_list.push(*token);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for token in remove_list {
|
||||
if let Some((_, src_addr, _)) = token_map.remove(&token) {
|
||||
udp_map.remove(&src_addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn server_handle(
|
||||
registry: &Registry,
|
||||
udp: &UdpSocket,
|
||||
nat_map: &Mutex<HashMap<SocketAddrV4, SocketAddrV4>>,
|
||||
token_map: &mut HashMap<Token, (Rc<UdpSocket>, SocketAddrV4, Instant)>,
|
||||
udp_map: &mut HashMap<SocketAddrV4, (Rc<UdpSocket>, Instant)>,
|
||||
buf: &mut [u8],
|
||||
) {
|
||||
loop {
|
||||
let (len, src_addr) = match udp.recv_from(buf) {
|
||||
Ok((len, src_addr)) => match src_addr {
|
||||
SocketAddr::V4(addr) => (len, addr),
|
||||
SocketAddr::V6(_) => {
|
||||
continue;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::WouldBlock {
|
||||
break;
|
||||
}
|
||||
log::error!("接收数据失败:{:?}", e);
|
||||
break;
|
||||
}
|
||||
};
|
||||
if let Some((dest_udp, time)) = udp_map.get_mut(&src_addr) {
|
||||
//发送失败就当丢包了
|
||||
let _ = dest_udp.send(&buf[..len]);
|
||||
*time = Instant::now();
|
||||
} else if let Some(dest_addr) = nat_map.lock().get(&src_addr).cloned() {
|
||||
if token_map.len() >= NAT_MAX {
|
||||
log::error!(
|
||||
"UDP NAT_MAX:src_addr={:?},dest_addr={:?}",
|
||||
src_addr,
|
||||
dest_addr
|
||||
);
|
||||
continue;
|
||||
}
|
||||
match udp_connect(src_addr.port(), dest_addr.into()) {
|
||||
Ok((token_val, mut dest_udp)) => {
|
||||
let token = Token(token_val);
|
||||
if let Err(e) = registry.register(&mut dest_udp, token, Interest::READABLE) {
|
||||
log::error!("register失败:{:?},addr={:?}", e, dest_addr);
|
||||
continue;
|
||||
}
|
||||
if dest_udp.send(&buf[..len]).is_ok() {
|
||||
let dest_udp = Rc::new(dest_udp);
|
||||
token_map.insert(token, (dest_udp.clone(), src_addr, Instant::now()));
|
||||
udp_map.insert(src_addr, (dest_udp, Instant::now()));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("绑定目标地址失败:{:?}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 得到一个 fd不为SERVER_VAL或者NOTYFY_VAL的socket
|
||||
fn udp_connect(src_port: u16, addr: SocketAddr) -> io::Result<(usize, UdpSocket)> {
|
||||
loop {
|
||||
let udp = if let Ok(udp) =
|
||||
UdpSocket::bind(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, src_port).into())
|
||||
{
|
||||
udp
|
||||
} else {
|
||||
UdpSocket::bind(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0).into())?
|
||||
};
|
||||
#[cfg(windows)]
|
||||
let fd = udp.as_raw_socket() as usize;
|
||||
#[cfg(unix)]
|
||||
let fd = udp.as_raw_fd() as usize;
|
||||
if fd == SERVER_VAL || fd == NOTIFY_VAL {
|
||||
continue;
|
||||
}
|
||||
// 只接收目标的数据
|
||||
udp.connect(addr)?;
|
||||
return Ok((fd, udp));
|
||||
}
|
||||
}
|
||||
|
||||
fn readable_handle(
|
||||
udp: &UdpSocket,
|
||||
token_map: &mut HashMap<Token, (Rc<UdpSocket>, SocketAddrV4, Instant)>,
|
||||
token: &Token,
|
||||
buf: &mut [u8],
|
||||
) -> io::Result<()> {
|
||||
if let Some((dest_udp, src_addr, time)) = token_map.get_mut(&token) {
|
||||
loop {
|
||||
let len = match dest_udp.recv(buf) {
|
||||
Ok(rs) => rs,
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::WouldBlock {
|
||||
break;
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
if len == 0 {
|
||||
return Err(io::Error::from(io::ErrorKind::UnexpectedEof));
|
||||
}
|
||||
let _ = udp.send_to(&buf[..len], (*src_addr).into());
|
||||
}
|
||||
*time = Instant::now();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
pub const VNT_VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
pub mod channel;
|
||||
pub mod cipher;
|
||||
pub mod core;
|
||||
pub mod external_route;
|
||||
pub mod handle;
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
pub mod ip_proxy;
|
||||
pub mod nat;
|
||||
pub mod proto;
|
||||
pub mod protocol;
|
||||
pub mod tun_tap_device;
|
||||
pub mod util;
|
||||
|
||||
pub use handle::callback::{DeviceInfo, ErrorInfo, HandshakeInfo, RegisterInfo, VntCallback};
|
||||
@@ -0,0 +1,139 @@
|
||||
use std::io;
|
||||
use std::net::UdpSocket;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
use std::ops::Sub;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use crate::channel::punch::{NatInfo, NatType};
|
||||
use crate::proto::message::PunchNatType;
|
||||
|
||||
mod stun;
|
||||
|
||||
pub fn local_ipv4_() -> io::Result<Ipv4Addr> {
|
||||
let socket = UdpSocket::bind("0.0.0.0:0")?;
|
||||
socket.connect("8.8.8.8:80")?;
|
||||
let addr = socket.local_addr()?;
|
||||
match addr.ip() {
|
||||
IpAddr::V4(ip) => Ok(ip),
|
||||
IpAddr::V6(_) => Ok(Ipv4Addr::UNSPECIFIED),
|
||||
}
|
||||
}
|
||||
pub fn local_ipv4() -> Option<Ipv4Addr> {
|
||||
match local_ipv4_() {
|
||||
Ok(ipv4) => Some(ipv4),
|
||||
Err(e) => {
|
||||
log::warn!("获取ipv4失败:{:?}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn local_ipv6_() -> io::Result<Ipv6Addr> {
|
||||
let socket = UdpSocket::bind("[::]:0")?;
|
||||
socket.connect("[2001:4860:4860:0000:0000:0000:0000:8888]:80")?;
|
||||
let addr = socket.local_addr()?;
|
||||
match addr.ip() {
|
||||
IpAddr::V4(_) => Ok(Ipv6Addr::UNSPECIFIED),
|
||||
IpAddr::V6(ip) => Ok(ip),
|
||||
}
|
||||
}
|
||||
pub fn local_ipv6() -> Option<Ipv6Addr> {
|
||||
match local_ipv6_() {
|
||||
Ok(ipv6) => Some(ipv6),
|
||||
Err(e) => {
|
||||
log::warn!("获取ipv6失败:{:?}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NatTest {
|
||||
stun_server: Vec<String>,
|
||||
info: Arc<Mutex<NatInfo>>,
|
||||
time: Arc<AtomicCell<Instant>>,
|
||||
}
|
||||
|
||||
impl From<NatType> for PunchNatType {
|
||||
fn from(value: NatType) -> Self {
|
||||
match value {
|
||||
NatType::Symmetric => PunchNatType::Symmetric,
|
||||
NatType::Cone => PunchNatType::Cone,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<NatType> for PunchNatType {
|
||||
fn into(self) -> NatType {
|
||||
match self {
|
||||
PunchNatType::Symmetric => NatType::Symmetric,
|
||||
PunchNatType::Cone => NatType::Cone,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NatTest {
|
||||
pub fn new(
|
||||
channel_num: usize,
|
||||
mut stun_server: Vec<String>,
|
||||
local_ipv4: Option<Ipv4Addr>,
|
||||
ipv6: Option<Ipv6Addr>,
|
||||
udp_ports: Vec<u16>,
|
||||
tcp_port: u16,
|
||||
) -> NatTest {
|
||||
let server = stun_server[0].clone();
|
||||
stun_server.resize(3, server);
|
||||
let mut ports = udp_ports.clone();
|
||||
ports.resize(channel_num, 0);
|
||||
let nat_info = NatInfo::new(
|
||||
Vec::new(),
|
||||
ports,
|
||||
0,
|
||||
local_ipv4,
|
||||
ipv6,
|
||||
udp_ports,
|
||||
tcp_port,
|
||||
NatType::Cone,
|
||||
);
|
||||
let info = Arc::new(Mutex::new(nat_info));
|
||||
NatTest {
|
||||
stun_server,
|
||||
info,
|
||||
time: Arc::new(AtomicCell::new(
|
||||
Instant::now().sub(Duration::from_secs(100)),
|
||||
)),
|
||||
}
|
||||
}
|
||||
pub fn can_update(&self) -> bool {
|
||||
let last = self.time.load();
|
||||
last.elapsed() > Duration::from_secs(10)
|
||||
&& self.time.compare_exchange(last, Instant::now()).is_ok()
|
||||
}
|
||||
|
||||
pub fn nat_info(&self) -> NatInfo {
|
||||
self.info.lock().clone()
|
||||
}
|
||||
pub fn update_addr(&self, index: usize, ip: Ipv4Addr, port: u16) {
|
||||
let mut guard = self.info.lock();
|
||||
guard.update_addr(index, ip, port)
|
||||
}
|
||||
pub fn re_test(
|
||||
&self,
|
||||
local_ipv4: Option<Ipv4Addr>,
|
||||
ipv6: Option<Ipv6Addr>,
|
||||
) -> io::Result<NatInfo> {
|
||||
let (nat_type, public_ips, port_range) = stun::stun_test_nat(self.stun_server.clone())?;
|
||||
let mut guard = self.info.lock();
|
||||
guard.nat_type = nat_type;
|
||||
guard.public_ips = public_ips;
|
||||
guard.public_port_range = port_range;
|
||||
guard.local_ipv4 = local_ipv4;
|
||||
guard.ipv6 = ipv6;
|
||||
|
||||
Ok(guard.clone())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
use std::collections::HashSet;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
||||
use std::time::Duration;
|
||||
use std::{io, thread};
|
||||
|
||||
use crate::channel::punch::NatType;
|
||||
use std::net::UdpSocket;
|
||||
use stun_format::Attr;
|
||||
|
||||
pub fn stun_test_nat(stun_servers: Vec<String>) -> io::Result<(NatType, Vec<Ipv4Addr>, u16)> {
|
||||
let mut h = Vec::new();
|
||||
for x in stun_servers {
|
||||
let handle = thread::spawn(move || test_nat(x));
|
||||
h.push(handle);
|
||||
}
|
||||
let mut nat_type = NatType::Cone;
|
||||
let mut port_range = 0;
|
||||
let mut hash_set = HashSet::new();
|
||||
for x in h {
|
||||
if let Ok(rs) = x.join() {
|
||||
if let Ok((nat_type_t, ip_list_t, port_range_t)) = rs {
|
||||
if nat_type_t == NatType::Symmetric {
|
||||
nat_type = NatType::Symmetric;
|
||||
}
|
||||
for x in ip_list_t {
|
||||
hash_set.insert(x);
|
||||
}
|
||||
if port_range < port_range_t {
|
||||
port_range = port_range_t;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok((nat_type, hash_set.into_iter().collect(), port_range))
|
||||
}
|
||||
|
||||
fn test_nat(stun_server: String) -> io::Result<(NatType, Vec<Ipv4Addr>, u16)> {
|
||||
let udp = UdpSocket::bind("0.0.0.0:0")?;
|
||||
udp.set_read_timeout(Some(Duration::from_millis(300)))?;
|
||||
udp.connect(stun_server)?;
|
||||
let mut port_range = 0;
|
||||
let mut hash_set = HashSet::new();
|
||||
let mut nat_type = NatType::Cone;
|
||||
match test_nat_(&udp, true, true) {
|
||||
Ok((mapped_addr1, changed_addr1)) => {
|
||||
match mapped_addr1.ip() {
|
||||
IpAddr::V4(ip) => {
|
||||
hash_set.insert(ip);
|
||||
}
|
||||
IpAddr::V6(_) => {}
|
||||
}
|
||||
if udp.connect(changed_addr1).is_ok() {
|
||||
if let Ok((mapped_addr2, _)) = test_nat_(&udp, false, false) {
|
||||
match mapped_addr2.ip() {
|
||||
IpAddr::V4(ip) => {
|
||||
hash_set.insert(ip);
|
||||
if mapped_addr1 != mapped_addr2 {
|
||||
nat_type = NatType::Symmetric;
|
||||
}
|
||||
}
|
||||
IpAddr::V6(_) => {}
|
||||
}
|
||||
port_range = mapped_addr2.port().abs_diff(mapped_addr1.port());
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
Ok((nat_type, hash_set.into_iter().collect(), port_range))
|
||||
}
|
||||
|
||||
fn test_nat_(
|
||||
udp: &UdpSocket,
|
||||
change_ip: bool,
|
||||
change_port: bool,
|
||||
) -> io::Result<(SocketAddr, SocketAddr)> {
|
||||
for _ in 0..2 {
|
||||
let mut buf = [0u8; 28];
|
||||
let mut msg = stun_format::MsgBuilder::from(buf.as_mut_slice());
|
||||
msg.typ(stun_format::MsgType::BindingRequest).unwrap();
|
||||
msg.tid(1).unwrap();
|
||||
msg.add_attr(Attr::ChangeRequest {
|
||||
change_ip,
|
||||
change_port,
|
||||
})
|
||||
.unwrap();
|
||||
udp.send(msg.as_bytes())?;
|
||||
let mut buf = [0; 10240];
|
||||
let (len, _addr) = match udp.recv_from(&mut buf) {
|
||||
Ok(rs) => rs,
|
||||
Err(_) => {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let msg = stun_format::Msg::from(&buf[..len]);
|
||||
let mut mapped_addr = None;
|
||||
let mut changed_addr = None;
|
||||
for x in msg.attrs_iter() {
|
||||
match x {
|
||||
Attr::MappedAddress(addr) => {
|
||||
if mapped_addr.is_none() {
|
||||
let _ = mapped_addr.insert(stun_addr(addr));
|
||||
}
|
||||
}
|
||||
Attr::ChangedAddress(addr) => {
|
||||
if changed_addr.is_none() {
|
||||
let _ = changed_addr.insert(stun_addr(addr));
|
||||
}
|
||||
}
|
||||
Attr::XorMappedAddress(addr) => {
|
||||
if mapped_addr.is_none() {
|
||||
let _ = mapped_addr.insert(stun_addr(addr));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if changed_addr.is_some() && mapped_addr.is_some() {
|
||||
return Ok((mapped_addr.unwrap(), changed_addr.unwrap()));
|
||||
}
|
||||
}
|
||||
if let Some(addr) = mapped_addr {
|
||||
return Ok((addr, changed_addr.unwrap_or(addr)));
|
||||
}
|
||||
}
|
||||
Err(io::Error::new(io::ErrorKind::Other, "stun response err"))
|
||||
}
|
||||
|
||||
fn stun_addr(addr: stun_format::SocketAddr) -> SocketAddr {
|
||||
match addr {
|
||||
stun_format::SocketAddr::V4(ip, port) => {
|
||||
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from(ip), port))
|
||||
}
|
||||
stun_format::SocketAddr::V6(ip, port) => {
|
||||
SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::from(ip), port, 0, 0))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,462 @@
|
||||
/// of protobuf runtime.
|
||||
const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_2_0;
|
||||
|
||||
#[derive(PartialEq,Clone,Default,Debug)]
|
||||
// @@protoc_insertion_point(message:HandshakeRequest)
|
||||
pub struct HandshakeRequest {
|
||||
// message fields
|
||||
// @@protoc_insertion_point(field:HandshakeRequest.version)
|
||||
pub version: ::std::string::String,
|
||||
// @@protoc_insertion_point(field:HandshakeRequest.secret)
|
||||
pub secret: bool,
|
||||
// special fields
|
||||
// @@protoc_insertion_point(special_field:HandshakeRequest.special_fields)
|
||||
pub special_fields: ::protobuf::SpecialFields,
|
||||
}
|
||||
|
||||
impl<'a> ::std::default::Default for &'a HandshakeRequest {
|
||||
fn default() -> &'a HandshakeRequest {
|
||||
<HandshakeRequest as ::protobuf::Message>::default_instance()
|
||||
}
|
||||
}
|
||||
|
||||
impl HandshakeRequest {
|
||||
pub fn new() -> HandshakeRequest {
|
||||
::std::default::Default::default()
|
||||
}
|
||||
|
||||
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
|
||||
let mut fields = ::std::vec::Vec::with_capacity(2);
|
||||
let mut oneofs = ::std::vec::Vec::with_capacity(0);
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"version",
|
||||
|m: &HandshakeRequest| { &m.version },
|
||||
|m: &mut HandshakeRequest| { &mut m.version },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"secret",
|
||||
|m: &HandshakeRequest| { &m.secret },
|
||||
|m: &mut HandshakeRequest| { &mut m.secret },
|
||||
));
|
||||
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<HandshakeRequest>(
|
||||
"HandshakeRequest",
|
||||
fields,
|
||||
oneofs,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::Message for HandshakeRequest {
|
||||
const NAME: &'static str = "HandshakeRequest";
|
||||
|
||||
fn is_initialized(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
|
||||
while let Some(tag) = is.read_raw_tag_or_eof()? {
|
||||
match tag {
|
||||
10 => {
|
||||
self.version = is.read_string()?;
|
||||
},
|
||||
16 => {
|
||||
self.secret = is.read_bool()?;
|
||||
},
|
||||
tag => {
|
||||
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
|
||||
},
|
||||
};
|
||||
}
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
|
||||
// Compute sizes of nested messages
|
||||
#[allow(unused_variables)]
|
||||
fn compute_size(&self) -> u64 {
|
||||
let mut my_size = 0;
|
||||
if !self.version.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(1, &self.version);
|
||||
}
|
||||
if self.secret != false {
|
||||
my_size += 1 + 1;
|
||||
}
|
||||
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
|
||||
self.special_fields.cached_size().set(my_size as u32);
|
||||
my_size
|
||||
}
|
||||
|
||||
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
|
||||
if !self.version.is_empty() {
|
||||
os.write_string(1, &self.version)?;
|
||||
}
|
||||
if self.secret != false {
|
||||
os.write_bool(2, self.secret)?;
|
||||
}
|
||||
os.write_unknown_fields(self.special_fields.unknown_fields())?;
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
|
||||
fn special_fields(&self) -> &::protobuf::SpecialFields {
|
||||
&self.special_fields
|
||||
}
|
||||
|
||||
fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
|
||||
&mut self.special_fields
|
||||
}
|
||||
|
||||
fn new() -> HandshakeRequest {
|
||||
HandshakeRequest::new()
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.version.clear();
|
||||
self.secret = false;
|
||||
self.special_fields.clear();
|
||||
}
|
||||
|
||||
fn default_instance() -> &'static HandshakeRequest {
|
||||
static instance: HandshakeRequest = HandshakeRequest {
|
||||
version: ::std::string::String::new(),
|
||||
secret: false,
|
||||
special_fields: ::protobuf::SpecialFields::new(),
|
||||
};
|
||||
&instance
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::MessageFull for HandshakeRequest {
|
||||
fn descriptor() -> ::protobuf::reflect::MessageDescriptor {
|
||||
static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new();
|
||||
descriptor.get(|| file_descriptor().message_by_package_relative_name("HandshakeRequest").unwrap()).clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for HandshakeRequest {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
::protobuf::text_format::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::reflect::ProtobufValue for HandshakeRequest {
|
||||
type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
|
||||
}
|
||||
|
||||
#[derive(PartialEq,Clone,Default,Debug)]
|
||||
// @@protoc_insertion_point(message:HandshakeResponse)
|
||||
pub struct HandshakeResponse {
|
||||
// message fields
|
||||
// @@protoc_insertion_point(field:HandshakeResponse.version)
|
||||
pub version: ::std::string::String,
|
||||
// @@protoc_insertion_point(field:HandshakeResponse.secret)
|
||||
pub secret: bool,
|
||||
// @@protoc_insertion_point(field:HandshakeResponse.public_key)
|
||||
pub public_key: ::std::vec::Vec<u8>,
|
||||
// @@protoc_insertion_point(field:HandshakeResponse.key_finger)
|
||||
pub key_finger: ::std::string::String,
|
||||
// special fields
|
||||
// @@protoc_insertion_point(special_field:HandshakeResponse.special_fields)
|
||||
pub special_fields: ::protobuf::SpecialFields,
|
||||
}
|
||||
|
||||
impl<'a> ::std::default::Default for &'a HandshakeResponse {
|
||||
fn default() -> &'a HandshakeResponse {
|
||||
<HandshakeResponse as ::protobuf::Message>::default_instance()
|
||||
}
|
||||
}
|
||||
|
||||
impl HandshakeResponse {
|
||||
pub fn new() -> HandshakeResponse {
|
||||
::std::default::Default::default()
|
||||
}
|
||||
|
||||
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
|
||||
let mut fields = ::std::vec::Vec::with_capacity(4);
|
||||
let mut oneofs = ::std::vec::Vec::with_capacity(0);
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"version",
|
||||
|m: &HandshakeResponse| { &m.version },
|
||||
|m: &mut HandshakeResponse| { &mut m.version },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"secret",
|
||||
|m: &HandshakeResponse| { &m.secret },
|
||||
|m: &mut HandshakeResponse| { &mut m.secret },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"public_key",
|
||||
|m: &HandshakeResponse| { &m.public_key },
|
||||
|m: &mut HandshakeResponse| { &mut m.public_key },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"key_finger",
|
||||
|m: &HandshakeResponse| { &m.key_finger },
|
||||
|m: &mut HandshakeResponse| { &mut m.key_finger },
|
||||
));
|
||||
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<HandshakeResponse>(
|
||||
"HandshakeResponse",
|
||||
fields,
|
||||
oneofs,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::Message for HandshakeResponse {
|
||||
const NAME: &'static str = "HandshakeResponse";
|
||||
|
||||
fn is_initialized(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
|
||||
while let Some(tag) = is.read_raw_tag_or_eof()? {
|
||||
match tag {
|
||||
10 => {
|
||||
self.version = is.read_string()?;
|
||||
},
|
||||
16 => {
|
||||
self.secret = is.read_bool()?;
|
||||
},
|
||||
26 => {
|
||||
self.public_key = is.read_bytes()?;
|
||||
},
|
||||
34 => {
|
||||
self.key_finger = is.read_string()?;
|
||||
},
|
||||
tag => {
|
||||
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
|
||||
},
|
||||
};
|
||||
}
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
|
||||
// Compute sizes of nested messages
|
||||
#[allow(unused_variables)]
|
||||
fn compute_size(&self) -> u64 {
|
||||
let mut my_size = 0;
|
||||
if !self.version.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(1, &self.version);
|
||||
}
|
||||
if self.secret != false {
|
||||
my_size += 1 + 1;
|
||||
}
|
||||
if !self.public_key.is_empty() {
|
||||
my_size += ::protobuf::rt::bytes_size(3, &self.public_key);
|
||||
}
|
||||
if !self.key_finger.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(4, &self.key_finger);
|
||||
}
|
||||
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
|
||||
self.special_fields.cached_size().set(my_size as u32);
|
||||
my_size
|
||||
}
|
||||
|
||||
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
|
||||
if !self.version.is_empty() {
|
||||
os.write_string(1, &self.version)?;
|
||||
}
|
||||
if self.secret != false {
|
||||
os.write_bool(2, self.secret)?;
|
||||
}
|
||||
if !self.public_key.is_empty() {
|
||||
os.write_bytes(3, &self.public_key)?;
|
||||
}
|
||||
if !self.key_finger.is_empty() {
|
||||
os.write_string(4, &self.key_finger)?;
|
||||
}
|
||||
os.write_unknown_fields(self.special_fields.unknown_fields())?;
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
|
||||
fn special_fields(&self) -> &::protobuf::SpecialFields {
|
||||
&self.special_fields
|
||||
}
|
||||
|
||||
fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
|
||||
&mut self.special_fields
|
||||
}
|
||||
|
||||
fn new() -> HandshakeResponse {
|
||||
HandshakeResponse::new()
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.version.clear();
|
||||
self.secret = false;
|
||||
self.public_key.clear();
|
||||
self.key_finger.clear();
|
||||
self.special_fields.clear();
|
||||
}
|
||||
|
||||
fn default_instance() -> &'static HandshakeResponse {
|
||||
static instance: HandshakeResponse = HandshakeResponse {
|
||||
version: ::std::string::String::new(),
|
||||
secret: false,
|
||||
public_key: ::std::vec::Vec::new(),
|
||||
key_finger: ::std::string::String::new(),
|
||||
special_fields: ::protobuf::SpecialFields::new(),
|
||||
};
|
||||
&instance
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::MessageFull for HandshakeResponse {
|
||||
fn descriptor() -> ::protobuf::reflect::MessageDescriptor {
|
||||
static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new();
|
||||
descriptor.get(|| file_descriptor().message_by_package_relative_name("HandshakeResponse").unwrap()).clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for HandshakeResponse {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
::protobuf::text_format::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::reflect::ProtobufValue for HandshakeResponse {
|
||||
type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
|
||||
}
|
||||
|
||||
#[derive(PartialEq,Clone,Default,Debug)]
|
||||
// @@protoc_insertion_point(message:SecretHandshakeRequest)
|
||||
pub struct SecretHandshakeRequest {
|
||||
// message fields
|
||||
// @@protoc_insertion_point(field:SecretHandshakeRequest.token)
|
||||
pub token: ::std::string::String,
|
||||
// @@protoc_insertion_point(field:SecretHandshakeRequest.key)
|
||||
pub key: ::std::vec::Vec<u8>,
|
||||
// special fields
|
||||
// @@protoc_insertion_point(special_field:SecretHandshakeRequest.special_fields)
|
||||
pub special_fields: ::protobuf::SpecialFields,
|
||||
}
|
||||
|
||||
impl<'a> ::std::default::Default for &'a SecretHandshakeRequest {
|
||||
fn default() -> &'a SecretHandshakeRequest {
|
||||
<SecretHandshakeRequest as ::protobuf::Message>::default_instance()
|
||||
}
|
||||
}
|
||||
|
||||
impl SecretHandshakeRequest {
|
||||
pub fn new() -> SecretHandshakeRequest {
|
||||
::std::default::Default::default()
|
||||
}
|
||||
|
||||
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
|
||||
let mut fields = ::std::vec::Vec::with_capacity(2);
|
||||
let mut oneofs = ::std::vec::Vec::with_capacity(0);
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"token",
|
||||
|m: &SecretHandshakeRequest| { &m.token },
|
||||
|m: &mut SecretHandshakeRequest| { &mut m.token },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"key",
|
||||
|m: &SecretHandshakeRequest| { &m.key },
|
||||
|m: &mut SecretHandshakeRequest| { &mut m.key },
|
||||
));
|
||||
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<SecretHandshakeRequest>(
|
||||
"SecretHandshakeRequest",
|
||||
fields,
|
||||
oneofs,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::Message for SecretHandshakeRequest {
|
||||
const NAME: &'static str = "SecretHandshakeRequest";
|
||||
|
||||
fn is_initialized(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
|
||||
while let Some(tag) = is.read_raw_tag_or_eof()? {
|
||||
match tag {
|
||||
10 => {
|
||||
self.token = is.read_string()?;
|
||||
},
|
||||
18 => {
|
||||
self.key = is.read_bytes()?;
|
||||
},
|
||||
tag => {
|
||||
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
|
||||
},
|
||||
};
|
||||
}
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
|
||||
// Compute sizes of nested messages
|
||||
#[allow(unused_variables)]
|
||||
fn compute_size(&self) -> u64 {
|
||||
let mut my_size = 0;
|
||||
if !self.token.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(1, &self.token);
|
||||
}
|
||||
if !self.key.is_empty() {
|
||||
my_size += ::protobuf::rt::bytes_size(2, &self.key);
|
||||
}
|
||||
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
|
||||
self.special_fields.cached_size().set(my_size as u32);
|
||||
my_size
|
||||
}
|
||||
|
||||
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
|
||||
if !self.token.is_empty() {
|
||||
os.write_string(1, &self.token)?;
|
||||
}
|
||||
if !self.key.is_empty() {
|
||||
os.write_bytes(2, &self.key)?;
|
||||
}
|
||||
os.write_unknown_fields(self.special_fields.unknown_fields())?;
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
|
||||
fn special_fields(&self) -> &::protobuf::SpecialFields {
|
||||
&self.special_fields
|
||||
}
|
||||
|
||||
fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
|
||||
&mut self.special_fields
|
||||
}
|
||||
|
||||
fn new() -> SecretHandshakeRequest {
|
||||
SecretHandshakeRequest::new()
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.token.clear();
|
||||
self.key.clear();
|
||||
self.special_fields.clear();
|
||||
}
|
||||
|
||||
fn default_instance() -> &'static SecretHandshakeRequest {
|
||||
static instance: SecretHandshakeRequest = SecretHandshakeRequest {
|
||||
token: ::std::string::String::new(),
|
||||
key: ::std::vec::Vec::new(),
|
||||
special_fields: ::protobuf::SpecialFields::new(),
|
||||
};
|
||||
&instance
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::MessageFull for SecretHandshakeRequest {
|
||||
fn descriptor() -> ::protobuf::reflect::MessageDescriptor {
|
||||
static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new();
|
||||
descriptor.get(|| file_descriptor().message_by_package_relative_name("SecretHandshakeRequest").unwrap()).clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for SecretHandshakeRequest {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
::protobuf::text_format::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::reflect::ProtobufValue for SecretHandshakeRequest {
|
||||
type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
|
||||
}
|
||||
|
||||
#[derive(PartialEq,Clone,Default,Debug)]
|
||||
// @@protoc_insertion_point(message:RegistrationRequest)
|
||||
pub struct RegistrationRequest {
|
||||
@@ -37,6 +493,14 @@ pub struct RegistrationRequest {
|
||||
pub name: ::std::string::String,
|
||||
// @@protoc_insertion_point(field:RegistrationRequest.is_fast)
|
||||
pub is_fast: bool,
|
||||
// @@protoc_insertion_point(field:RegistrationRequest.version)
|
||||
pub version: ::std::string::String,
|
||||
// @@protoc_insertion_point(field:RegistrationRequest.virtual_ip)
|
||||
pub virtual_ip: u32,
|
||||
// @@protoc_insertion_point(field:RegistrationRequest.allow_ip_change)
|
||||
pub allow_ip_change: bool,
|
||||
// @@protoc_insertion_point(field:RegistrationRequest.client_secret)
|
||||
pub client_secret: bool,
|
||||
// special fields
|
||||
// @@protoc_insertion_point(special_field:RegistrationRequest.special_fields)
|
||||
pub special_fields: ::protobuf::SpecialFields,
|
||||
@@ -54,7 +518,7 @@ impl RegistrationRequest {
|
||||
}
|
||||
|
||||
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
|
||||
let mut fields = ::std::vec::Vec::with_capacity(4);
|
||||
let mut fields = ::std::vec::Vec::with_capacity(8);
|
||||
let mut oneofs = ::std::vec::Vec::with_capacity(0);
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"token",
|
||||
@@ -76,6 +540,26 @@ impl RegistrationRequest {
|
||||
|m: &RegistrationRequest| { &m.is_fast },
|
||||
|m: &mut RegistrationRequest| { &mut m.is_fast },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"version",
|
||||
|m: &RegistrationRequest| { &m.version },
|
||||
|m: &mut RegistrationRequest| { &mut m.version },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"virtual_ip",
|
||||
|m: &RegistrationRequest| { &m.virtual_ip },
|
||||
|m: &mut RegistrationRequest| { &mut m.virtual_ip },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"allow_ip_change",
|
||||
|m: &RegistrationRequest| { &m.allow_ip_change },
|
||||
|m: &mut RegistrationRequest| { &mut m.allow_ip_change },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"client_secret",
|
||||
|m: &RegistrationRequest| { &m.client_secret },
|
||||
|m: &mut RegistrationRequest| { &mut m.client_secret },
|
||||
));
|
||||
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<RegistrationRequest>(
|
||||
"RegistrationRequest",
|
||||
fields,
|
||||
@@ -106,6 +590,18 @@ impl ::protobuf::Message for RegistrationRequest {
|
||||
32 => {
|
||||
self.is_fast = is.read_bool()?;
|
||||
},
|
||||
42 => {
|
||||
self.version = is.read_string()?;
|
||||
},
|
||||
53 => {
|
||||
self.virtual_ip = is.read_fixed32()?;
|
||||
},
|
||||
56 => {
|
||||
self.allow_ip_change = is.read_bool()?;
|
||||
},
|
||||
64 => {
|
||||
self.client_secret = is.read_bool()?;
|
||||
},
|
||||
tag => {
|
||||
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
|
||||
},
|
||||
@@ -130,6 +626,18 @@ impl ::protobuf::Message for RegistrationRequest {
|
||||
if self.is_fast != false {
|
||||
my_size += 1 + 1;
|
||||
}
|
||||
if !self.version.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(5, &self.version);
|
||||
}
|
||||
if self.virtual_ip != 0 {
|
||||
my_size += 1 + 4;
|
||||
}
|
||||
if self.allow_ip_change != false {
|
||||
my_size += 1 + 1;
|
||||
}
|
||||
if self.client_secret != false {
|
||||
my_size += 1 + 1;
|
||||
}
|
||||
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
|
||||
self.special_fields.cached_size().set(my_size as u32);
|
||||
my_size
|
||||
@@ -148,6 +656,18 @@ impl ::protobuf::Message for RegistrationRequest {
|
||||
if self.is_fast != false {
|
||||
os.write_bool(4, self.is_fast)?;
|
||||
}
|
||||
if !self.version.is_empty() {
|
||||
os.write_string(5, &self.version)?;
|
||||
}
|
||||
if self.virtual_ip != 0 {
|
||||
os.write_fixed32(6, self.virtual_ip)?;
|
||||
}
|
||||
if self.allow_ip_change != false {
|
||||
os.write_bool(7, self.allow_ip_change)?;
|
||||
}
|
||||
if self.client_secret != false {
|
||||
os.write_bool(8, self.client_secret)?;
|
||||
}
|
||||
os.write_unknown_fields(self.special_fields.unknown_fields())?;
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
@@ -169,6 +689,10 @@ impl ::protobuf::Message for RegistrationRequest {
|
||||
self.device_id.clear();
|
||||
self.name.clear();
|
||||
self.is_fast = false;
|
||||
self.version.clear();
|
||||
self.virtual_ip = 0;
|
||||
self.allow_ip_change = false;
|
||||
self.client_secret = false;
|
||||
self.special_fields.clear();
|
||||
}
|
||||
|
||||
@@ -178,6 +702,10 @@ impl ::protobuf::Message for RegistrationRequest {
|
||||
device_id: ::std::string::String::new(),
|
||||
name: ::std::string::String::new(),
|
||||
is_fast: false,
|
||||
version: ::std::string::String::new(),
|
||||
virtual_ip: 0,
|
||||
allow_ip_change: false,
|
||||
client_secret: false,
|
||||
special_fields: ::protobuf::SpecialFields::new(),
|
||||
};
|
||||
&instance
|
||||
@@ -219,6 +747,8 @@ pub struct RegistrationResponse {
|
||||
pub public_ip: u32,
|
||||
// @@protoc_insertion_point(field:RegistrationResponse.public_port)
|
||||
pub public_port: u32,
|
||||
// @@protoc_insertion_point(field:RegistrationResponse.public_ipv6)
|
||||
pub public_ipv6: ::std::vec::Vec<u8>,
|
||||
// special fields
|
||||
// @@protoc_insertion_point(special_field:RegistrationResponse.special_fields)
|
||||
pub special_fields: ::protobuf::SpecialFields,
|
||||
@@ -236,7 +766,7 @@ impl RegistrationResponse {
|
||||
}
|
||||
|
||||
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
|
||||
let mut fields = ::std::vec::Vec::with_capacity(7);
|
||||
let mut fields = ::std::vec::Vec::with_capacity(8);
|
||||
let mut oneofs = ::std::vec::Vec::with_capacity(0);
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"virtual_ip",
|
||||
@@ -273,6 +803,11 @@ impl RegistrationResponse {
|
||||
|m: &RegistrationResponse| { &m.public_port },
|
||||
|m: &mut RegistrationResponse| { &mut m.public_port },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"public_ipv6",
|
||||
|m: &RegistrationResponse| { &m.public_ipv6 },
|
||||
|m: &mut RegistrationResponse| { &mut m.public_ipv6 },
|
||||
));
|
||||
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<RegistrationResponse>(
|
||||
"RegistrationResponse",
|
||||
fields,
|
||||
@@ -312,6 +847,9 @@ impl ::protobuf::Message for RegistrationResponse {
|
||||
56 => {
|
||||
self.public_port = is.read_uint32()?;
|
||||
},
|
||||
66 => {
|
||||
self.public_ipv6 = is.read_bytes()?;
|
||||
},
|
||||
tag => {
|
||||
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
|
||||
},
|
||||
@@ -346,6 +884,9 @@ impl ::protobuf::Message for RegistrationResponse {
|
||||
if self.public_port != 0 {
|
||||
my_size += ::protobuf::rt::uint32_size(7, self.public_port);
|
||||
}
|
||||
if !self.public_ipv6.is_empty() {
|
||||
my_size += ::protobuf::rt::bytes_size(8, &self.public_ipv6);
|
||||
}
|
||||
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
|
||||
self.special_fields.cached_size().set(my_size as u32);
|
||||
my_size
|
||||
@@ -373,6 +914,9 @@ impl ::protobuf::Message for RegistrationResponse {
|
||||
if self.public_port != 0 {
|
||||
os.write_uint32(7, self.public_port)?;
|
||||
}
|
||||
if !self.public_ipv6.is_empty() {
|
||||
os.write_bytes(8, &self.public_ipv6)?;
|
||||
}
|
||||
os.write_unknown_fields(self.special_fields.unknown_fields())?;
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
@@ -397,6 +941,7 @@ impl ::protobuf::Message for RegistrationResponse {
|
||||
self.device_info_list.clear();
|
||||
self.public_ip = 0;
|
||||
self.public_port = 0;
|
||||
self.public_ipv6.clear();
|
||||
self.special_fields.clear();
|
||||
}
|
||||
|
||||
@@ -409,6 +954,7 @@ impl ::protobuf::Message for RegistrationResponse {
|
||||
device_info_list: ::std::vec::Vec::new(),
|
||||
public_ip: 0,
|
||||
public_port: 0,
|
||||
public_ipv6: ::std::vec::Vec::new(),
|
||||
special_fields: ::protobuf::SpecialFields::new(),
|
||||
};
|
||||
&instance
|
||||
@@ -442,6 +988,8 @@ pub struct DeviceInfo {
|
||||
pub virtual_ip: u32,
|
||||
// @@protoc_insertion_point(field:DeviceInfo.device_status)
|
||||
pub device_status: u32,
|
||||
// @@protoc_insertion_point(field:DeviceInfo.client_secret)
|
||||
pub client_secret: bool,
|
||||
// special fields
|
||||
// @@protoc_insertion_point(special_field:DeviceInfo.special_fields)
|
||||
pub special_fields: ::protobuf::SpecialFields,
|
||||
@@ -459,7 +1007,7 @@ impl DeviceInfo {
|
||||
}
|
||||
|
||||
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
|
||||
let mut fields = ::std::vec::Vec::with_capacity(3);
|
||||
let mut fields = ::std::vec::Vec::with_capacity(4);
|
||||
let mut oneofs = ::std::vec::Vec::with_capacity(0);
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"name",
|
||||
@@ -476,6 +1024,11 @@ impl DeviceInfo {
|
||||
|m: &DeviceInfo| { &m.device_status },
|
||||
|m: &mut DeviceInfo| { &mut m.device_status },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"client_secret",
|
||||
|m: &DeviceInfo| { &m.client_secret },
|
||||
|m: &mut DeviceInfo| { &mut m.client_secret },
|
||||
));
|
||||
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<DeviceInfo>(
|
||||
"DeviceInfo",
|
||||
fields,
|
||||
@@ -503,6 +1056,9 @@ impl ::protobuf::Message for DeviceInfo {
|
||||
24 => {
|
||||
self.device_status = is.read_uint32()?;
|
||||
},
|
||||
32 => {
|
||||
self.client_secret = is.read_bool()?;
|
||||
},
|
||||
tag => {
|
||||
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
|
||||
},
|
||||
@@ -524,6 +1080,9 @@ impl ::protobuf::Message for DeviceInfo {
|
||||
if self.device_status != 0 {
|
||||
my_size += ::protobuf::rt::uint32_size(3, self.device_status);
|
||||
}
|
||||
if self.client_secret != false {
|
||||
my_size += 1 + 1;
|
||||
}
|
||||
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
|
||||
self.special_fields.cached_size().set(my_size as u32);
|
||||
my_size
|
||||
@@ -539,6 +1098,9 @@ impl ::protobuf::Message for DeviceInfo {
|
||||
if self.device_status != 0 {
|
||||
os.write_uint32(3, self.device_status)?;
|
||||
}
|
||||
if self.client_secret != false {
|
||||
os.write_bool(4, self.client_secret)?;
|
||||
}
|
||||
os.write_unknown_fields(self.special_fields.unknown_fields())?;
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
@@ -559,6 +1121,7 @@ impl ::protobuf::Message for DeviceInfo {
|
||||
self.name.clear();
|
||||
self.virtual_ip = 0;
|
||||
self.device_status = 0;
|
||||
self.client_secret = false;
|
||||
self.special_fields.clear();
|
||||
}
|
||||
|
||||
@@ -567,6 +1130,7 @@ impl ::protobuf::Message for DeviceInfo {
|
||||
name: ::std::string::String::new(),
|
||||
virtual_ip: 0,
|
||||
device_status: 0,
|
||||
client_secret: false,
|
||||
special_fields: ::protobuf::SpecialFields::new(),
|
||||
};
|
||||
&instance
|
||||
@@ -749,6 +1313,16 @@ pub struct PunchInfo {
|
||||
pub local_ip: u32,
|
||||
// @@protoc_insertion_point(field:PunchInfo.local_port)
|
||||
pub local_port: u32,
|
||||
// @@protoc_insertion_point(field:PunchInfo.ipv6)
|
||||
pub ipv6: ::std::vec::Vec<u8>,
|
||||
// @@protoc_insertion_point(field:PunchInfo.ipv6_port)
|
||||
pub ipv6_port: u32,
|
||||
// @@protoc_insertion_point(field:PunchInfo.tcp_port)
|
||||
pub tcp_port: u32,
|
||||
// @@protoc_insertion_point(field:PunchInfo.udp_ports)
|
||||
pub udp_ports: ::std::vec::Vec<u32>,
|
||||
// @@protoc_insertion_point(field:PunchInfo.public_ports)
|
||||
pub public_ports: ::std::vec::Vec<u32>,
|
||||
// special fields
|
||||
// @@protoc_insertion_point(special_field:PunchInfo.special_fields)
|
||||
pub special_fields: ::protobuf::SpecialFields,
|
||||
@@ -766,7 +1340,7 @@ impl PunchInfo {
|
||||
}
|
||||
|
||||
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
|
||||
let mut fields = ::std::vec::Vec::with_capacity(7);
|
||||
let mut fields = ::std::vec::Vec::with_capacity(12);
|
||||
let mut oneofs = ::std::vec::Vec::with_capacity(0);
|
||||
fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>(
|
||||
"public_ip_list",
|
||||
@@ -803,6 +1377,31 @@ impl PunchInfo {
|
||||
|m: &PunchInfo| { &m.local_port },
|
||||
|m: &mut PunchInfo| { &mut m.local_port },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"ipv6",
|
||||
|m: &PunchInfo| { &m.ipv6 },
|
||||
|m: &mut PunchInfo| { &mut m.ipv6 },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"ipv6_port",
|
||||
|m: &PunchInfo| { &m.ipv6_port },
|
||||
|m: &mut PunchInfo| { &mut m.ipv6_port },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"tcp_port",
|
||||
|m: &PunchInfo| { &m.tcp_port },
|
||||
|m: &mut PunchInfo| { &mut m.tcp_port },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>(
|
||||
"udp_ports",
|
||||
|m: &PunchInfo| { &m.udp_ports },
|
||||
|m: &mut PunchInfo| { &mut m.udp_ports },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>(
|
||||
"public_ports",
|
||||
|m: &PunchInfo| { &m.public_ports },
|
||||
|m: &mut PunchInfo| { &mut m.public_ports },
|
||||
));
|
||||
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<PunchInfo>(
|
||||
"PunchInfo",
|
||||
fields,
|
||||
@@ -845,6 +1444,27 @@ impl ::protobuf::Message for PunchInfo {
|
||||
64 => {
|
||||
self.local_port = is.read_uint32()?;
|
||||
},
|
||||
74 => {
|
||||
self.ipv6 = is.read_bytes()?;
|
||||
},
|
||||
80 => {
|
||||
self.ipv6_port = is.read_uint32()?;
|
||||
},
|
||||
88 => {
|
||||
self.tcp_port = is.read_uint32()?;
|
||||
},
|
||||
98 => {
|
||||
is.read_repeated_packed_uint32_into(&mut self.udp_ports)?;
|
||||
},
|
||||
96 => {
|
||||
self.udp_ports.push(is.read_uint32()?);
|
||||
},
|
||||
106 => {
|
||||
is.read_repeated_packed_uint32_into(&mut self.public_ports)?;
|
||||
},
|
||||
104 => {
|
||||
self.public_ports.push(is.read_uint32()?);
|
||||
},
|
||||
tag => {
|
||||
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
|
||||
},
|
||||
@@ -876,6 +1496,21 @@ impl ::protobuf::Message for PunchInfo {
|
||||
if self.local_port != 0 {
|
||||
my_size += ::protobuf::rt::uint32_size(8, self.local_port);
|
||||
}
|
||||
if !self.ipv6.is_empty() {
|
||||
my_size += ::protobuf::rt::bytes_size(9, &self.ipv6);
|
||||
}
|
||||
if self.ipv6_port != 0 {
|
||||
my_size += ::protobuf::rt::uint32_size(10, self.ipv6_port);
|
||||
}
|
||||
if self.tcp_port != 0 {
|
||||
my_size += ::protobuf::rt::uint32_size(11, self.tcp_port);
|
||||
}
|
||||
for value in &self.udp_ports {
|
||||
my_size += ::protobuf::rt::uint32_size(12, *value);
|
||||
};
|
||||
for value in &self.public_ports {
|
||||
my_size += ::protobuf::rt::uint32_size(13, *value);
|
||||
};
|
||||
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
|
||||
self.special_fields.cached_size().set(my_size as u32);
|
||||
my_size
|
||||
@@ -903,6 +1538,21 @@ impl ::protobuf::Message for PunchInfo {
|
||||
if self.local_port != 0 {
|
||||
os.write_uint32(8, self.local_port)?;
|
||||
}
|
||||
if !self.ipv6.is_empty() {
|
||||
os.write_bytes(9, &self.ipv6)?;
|
||||
}
|
||||
if self.ipv6_port != 0 {
|
||||
os.write_uint32(10, self.ipv6_port)?;
|
||||
}
|
||||
if self.tcp_port != 0 {
|
||||
os.write_uint32(11, self.tcp_port)?;
|
||||
}
|
||||
for v in &self.udp_ports {
|
||||
os.write_uint32(12, *v)?;
|
||||
};
|
||||
for v in &self.public_ports {
|
||||
os.write_uint32(13, *v)?;
|
||||
};
|
||||
os.write_unknown_fields(self.special_fields.unknown_fields())?;
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
@@ -927,6 +1577,11 @@ impl ::protobuf::Message for PunchInfo {
|
||||
self.reply = false;
|
||||
self.local_ip = 0;
|
||||
self.local_port = 0;
|
||||
self.ipv6.clear();
|
||||
self.ipv6_port = 0;
|
||||
self.tcp_port = 0;
|
||||
self.udp_ports.clear();
|
||||
self.public_ports.clear();
|
||||
self.special_fields.clear();
|
||||
}
|
||||
|
||||
@@ -939,6 +1594,11 @@ impl ::protobuf::Message for PunchInfo {
|
||||
reply: false,
|
||||
local_ip: 0,
|
||||
local_port: 0,
|
||||
ipv6: ::std::vec::Vec::new(),
|
||||
ipv6_port: 0,
|
||||
tcp_port: 0,
|
||||
udp_ports: ::std::vec::Vec::new(),
|
||||
public_ports: ::std::vec::Vec::new(),
|
||||
special_fields: ::protobuf::SpecialFields::new(),
|
||||
};
|
||||
&instance
|
||||
@@ -1017,28 +1677,44 @@ impl PunchNatType {
|
||||
}
|
||||
|
||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
||||
\n\rmessage.proto\"u\n\x13RegistrationRequest\x12\x14\n\x05token\x18\x01\
|
||||
\x20\x01(\tR\x05token\x12\x1b\n\tdevice_id\x18\x02\x20\x01(\tR\x08device\
|
||||
Id\x12\x12\n\x04name\x18\x03\x20\x01(\tR\x04name\x12\x17\n\x07is_fast\
|
||||
\x18\x04\x20\x01(\x08R\x06isFast\"\x92\x02\n\x14RegistrationResponse\x12\
|
||||
\x1d\n\nvirtual_ip\x18\x01\x20\x01(\x07R\tvirtualIp\x12'\n\x0fvirtual_ga\
|
||||
teway\x18\x02\x20\x01(\x07R\x0evirtualGateway\x12'\n\x0fvirtual_netmask\
|
||||
\x18\x03\x20\x01(\x07R\x0evirtualNetmask\x12\x14\n\x05epoch\x18\x04\x20\
|
||||
\x01(\rR\x05epoch\x125\n\x10device_info_list\x18\x05\x20\x03(\x0b2\x0b.D\
|
||||
eviceInfoR\x0edeviceInfoList\x12\x1b\n\tpublic_ip\x18\x06\x20\x01(\x07R\
|
||||
\x08publicIp\x12\x1f\n\x0bpublic_port\x18\x07\x20\x01(\rR\npublicPort\"d\
|
||||
\n\nDeviceInfo\x12\x12\n\x04name\x18\x01\x20\x01(\tR\x04name\x12\x1d\n\n\
|
||||
virtual_ip\x18\x02\x20\x01(\x07R\tvirtualIp\x12#\n\rdevice_status\x18\
|
||||
\x03\x20\x01(\rR\x0cdeviceStatus\"Y\n\nDeviceList\x12\x14\n\x05epoch\x18\
|
||||
\x01\x20\x01(\rR\x05epoch\x125\n\x10device_info_list\x18\x02\x20\x03(\
|
||||
\x0b2\x0b.DeviceInfoR\x0edeviceInfoList\"\xf8\x01\n\tPunchInfo\x12$\n\
|
||||
\x0epublic_ip_list\x18\x02\x20\x03(\x07R\x0cpublicIpList\x12\x1f\n\x0bpu\
|
||||
blic_port\x18\x03\x20\x01(\rR\npublicPort\x12*\n\x11public_port_range\
|
||||
\x18\x04\x20\x01(\rR\x0fpublicPortRange\x12(\n\x08nat_type\x18\x05\x20\
|
||||
\x01(\x0e2\r.PunchNatTypeR\x07natType\x12\x14\n\x05reply\x18\x06\x20\x01\
|
||||
(\x08R\x05reply\x12\x19\n\x08local_ip\x18\x07\x20\x01(\x07R\x07localIp\
|
||||
\x12\x1d\n\nlocal_port\x18\x08\x20\x01(\rR\tlocalPort*'\n\x0cPunchNatTyp\
|
||||
e\x12\r\n\tSymmetric\x10\0\x12\x08\n\x04Cone\x10\x01b\x06proto3\
|
||||
\n\rmessage.proto\"D\n\x10HandshakeRequest\x12\x18\n\x07version\x18\x01\
|
||||
\x20\x01(\tR\x07version\x12\x16\n\x06secret\x18\x02\x20\x01(\x08R\x06sec\
|
||||
ret\"\x83\x01\n\x11HandshakeResponse\x12\x18\n\x07version\x18\x01\x20\
|
||||
\x01(\tR\x07version\x12\x16\n\x06secret\x18\x02\x20\x01(\x08R\x06secret\
|
||||
\x12\x1d\n\npublic_key\x18\x03\x20\x01(\x0cR\tpublicKey\x12\x1d\n\nkey_f\
|
||||
inger\x18\x04\x20\x01(\tR\tkeyFinger\"@\n\x16SecretHandshakeRequest\x12\
|
||||
\x14\n\x05token\x18\x01\x20\x01(\tR\x05token\x12\x10\n\x03key\x18\x02\
|
||||
\x20\x01(\x0cR\x03key\"\xfb\x01\n\x13RegistrationRequest\x12\x14\n\x05to\
|
||||
ken\x18\x01\x20\x01(\tR\x05token\x12\x1b\n\tdevice_id\x18\x02\x20\x01(\t\
|
||||
R\x08deviceId\x12\x12\n\x04name\x18\x03\x20\x01(\tR\x04name\x12\x17\n\
|
||||
\x07is_fast\x18\x04\x20\x01(\x08R\x06isFast\x12\x18\n\x07version\x18\x05\
|
||||
\x20\x01(\tR\x07version\x12\x1d\n\nvirtual_ip\x18\x06\x20\x01(\x07R\tvir\
|
||||
tualIp\x12&\n\x0fallow_ip_change\x18\x07\x20\x01(\x08R\rallowIpChange\
|
||||
\x12#\n\rclient_secret\x18\x08\x20\x01(\x08R\x0cclientSecret\"\xb3\x02\n\
|
||||
\x14RegistrationResponse\x12\x1d\n\nvirtual_ip\x18\x01\x20\x01(\x07R\tvi\
|
||||
rtualIp\x12'\n\x0fvirtual_gateway\x18\x02\x20\x01(\x07R\x0evirtualGatewa\
|
||||
y\x12'\n\x0fvirtual_netmask\x18\x03\x20\x01(\x07R\x0evirtualNetmask\x12\
|
||||
\x14\n\x05epoch\x18\x04\x20\x01(\rR\x05epoch\x125\n\x10device_info_list\
|
||||
\x18\x05\x20\x03(\x0b2\x0b.DeviceInfoR\x0edeviceInfoList\x12\x1b\n\tpubl\
|
||||
ic_ip\x18\x06\x20\x01(\x07R\x08publicIp\x12\x1f\n\x0bpublic_port\x18\x07\
|
||||
\x20\x01(\rR\npublicPort\x12\x1f\n\x0bpublic_ipv6\x18\x08\x20\x01(\x0cR\
|
||||
\npublicIpv6\"\x89\x01\n\nDeviceInfo\x12\x12\n\x04name\x18\x01\x20\x01(\
|
||||
\tR\x04name\x12\x1d\n\nvirtual_ip\x18\x02\x20\x01(\x07R\tvirtualIp\x12#\
|
||||
\n\rdevice_status\x18\x03\x20\x01(\rR\x0cdeviceStatus\x12#\n\rclient_sec\
|
||||
ret\x18\x04\x20\x01(\x08R\x0cclientSecret\"Y\n\nDeviceList\x12\x14\n\x05\
|
||||
epoch\x18\x01\x20\x01(\rR\x05epoch\x125\n\x10device_info_list\x18\x02\
|
||||
\x20\x03(\x0b2\x0b.DeviceInfoR\x0edeviceInfoList\"\x84\x03\n\tPunchInfo\
|
||||
\x12$\n\x0epublic_ip_list\x18\x02\x20\x03(\x07R\x0cpublicIpList\x12\x1f\
|
||||
\n\x0bpublic_port\x18\x03\x20\x01(\rR\npublicPort\x12*\n\x11public_port_\
|
||||
range\x18\x04\x20\x01(\rR\x0fpublicPortRange\x12(\n\x08nat_type\x18\x05\
|
||||
\x20\x01(\x0e2\r.PunchNatTypeR\x07natType\x12\x14\n\x05reply\x18\x06\x20\
|
||||
\x01(\x08R\x05reply\x12\x19\n\x08local_ip\x18\x07\x20\x01(\x07R\x07local\
|
||||
Ip\x12\x1d\n\nlocal_port\x18\x08\x20\x01(\rR\tlocalPort\x12\x12\n\x04ipv\
|
||||
6\x18\t\x20\x01(\x0cR\x04ipv6\x12\x1b\n\tipv6_port\x18\n\x20\x01(\rR\x08\
|
||||
ipv6Port\x12\x19\n\x08tcp_port\x18\x0b\x20\x01(\rR\x07tcpPort\x12\x1b\n\
|
||||
\tudp_ports\x18\x0c\x20\x03(\rR\x08udpPorts\x12!\n\x0cpublic_ports\x18\r\
|
||||
\x20\x03(\rR\x0bpublicPorts*'\n\x0cPunchNatType\x12\r\n\tSymmetric\x10\0\
|
||||
\x12\x08\n\x04Cone\x10\x01b\x06proto3\
|
||||
";
|
||||
|
||||
/// `FileDescriptorProto` object which was a source for this generated file
|
||||
@@ -1056,7 +1732,10 @@ pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor {
|
||||
file_descriptor.get(|| {
|
||||
let generated_file_descriptor = generated_file_descriptor_lazy.get(|| {
|
||||
let mut deps = ::std::vec::Vec::with_capacity(0);
|
||||
let mut messages = ::std::vec::Vec::with_capacity(5);
|
||||
let mut messages = ::std::vec::Vec::with_capacity(8);
|
||||
messages.push(HandshakeRequest::generated_message_descriptor_data());
|
||||
messages.push(HandshakeResponse::generated_message_descriptor_data());
|
||||
messages.push(SecretHandshakeRequest::generated_message_descriptor_data());
|
||||
messages.push(RegistrationRequest::generated_message_descriptor_data());
|
||||
messages.push(RegistrationResponse::generated_message_descriptor_data());
|
||||
messages.push(DeviceInfo::generated_message_descriptor_data());
|
||||
@@ -0,0 +1,338 @@
|
||||
use std::{fmt, io};
|
||||
|
||||
pub const ENCRYPTION_RESERVED: usize = 16 + 32 + 12;
|
||||
pub const AES_GCM_ENCRYPTION_RESERVED: usize = 32;
|
||||
pub const RSA_ENCRYPTION_RESERVED: usize = 32;
|
||||
|
||||
/* 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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 数据体 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| random(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| tag(32) |
|
||||
| tag(32) |
|
||||
| tag(32) |
|
||||
| tag(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| finger(32) |
|
||||
| finger(32) |
|
||||
| finger(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
注:finger用于快速校验数据是否被修改,上层可使用token、协议头参与计算finger,
|
||||
确保服务端和客户端都能感知修改(服务端不能解密也能校验指纹)
|
||||
*/
|
||||
pub struct SecretBody<B> {
|
||||
buffer: B,
|
||||
exist_finger: bool,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> SecretBody<B> {
|
||||
pub fn new(buffer: B, exist_finger: bool) -> io::Result<SecretBody<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
let min_len = if exist_finger { 32 } else { 32 - 12 };
|
||||
// 不能大于udp最大载荷长度
|
||||
if len < min_len || len > 65535 - 20 - 8 - 12 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"SecretBody length overflow",
|
||||
));
|
||||
}
|
||||
Ok(SecretBody {
|
||||
buffer,
|
||||
exist_finger,
|
||||
})
|
||||
}
|
||||
pub fn random(&self) -> u32 {
|
||||
let mut end = self.buffer.as_ref().len() - 16;
|
||||
if self.exist_finger {
|
||||
end -= 12;
|
||||
}
|
||||
u32::from_be_bytes(self.buffer.as_ref()[end - 4..end].try_into().unwrap())
|
||||
}
|
||||
pub fn body(&self) -> &[u8] {
|
||||
let mut end = self.buffer.as_ref().len() - 16;
|
||||
if self.exist_finger {
|
||||
end -= 12;
|
||||
}
|
||||
&self.buffer.as_ref()[..end]
|
||||
}
|
||||
pub fn tag(&self) -> &[u8] {
|
||||
let mut end = self.buffer.as_ref().len();
|
||||
if self.exist_finger {
|
||||
end -= 12;
|
||||
}
|
||||
&self.buffer.as_ref()[end - 16..end]
|
||||
}
|
||||
/// 数据部分+tag部分
|
||||
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 {
|
||||
&[]
|
||||
}
|
||||
}
|
||||
pub fn buffer(&self) -> &[u8] {
|
||||
self.buffer.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> SecretBody<B> {
|
||||
pub fn set_random(&mut self, random: u32) {
|
||||
let mut end = self.buffer.as_ref().len() - 16;
|
||||
if self.exist_finger {
|
||||
end -= 12;
|
||||
}
|
||||
self.buffer.as_mut()[end - 4..end].copy_from_slice(&random.to_be_bytes());
|
||||
}
|
||||
|
||||
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();
|
||||
if self.exist_finger {
|
||||
end -= 12;
|
||||
}
|
||||
self.buffer.as_mut()[end - 16..end].copy_from_slice(tag);
|
||||
Ok(())
|
||||
}
|
||||
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,
|
||||
"not exist finger",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// 数据部分
|
||||
pub fn body_mut(&mut self) -> &mut [u8] {
|
||||
let mut end = self.buffer.as_ref().len() - 16;
|
||||
if self.exist_finger {
|
||||
end -= 12;
|
||||
}
|
||||
&mut self.buffer.as_mut()[..end]
|
||||
}
|
||||
pub fn tag_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 - 16..end]
|
||||
}
|
||||
/// 数据部分+tag部分
|
||||
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]
|
||||
}
|
||||
pub fn buffer_mut(&mut self) -> &mut [u8] {
|
||||
self.buffer.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for SecretBody<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("SecretBody")
|
||||
.field("random", &self.random())
|
||||
.field("body", &self.body())
|
||||
.field("tag", &self.tag())
|
||||
.field("finger", &self.finger())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
/* aes_cbc加密数据体
|
||||
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 AesCbcSecretBody<B> {
|
||||
buffer: B,
|
||||
exist_finger: bool,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> AesCbcSecretBody<B> {
|
||||
pub fn new(buffer: B, exist_finger: bool) -> io::Result<AesCbcSecretBody<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
let min_len = if exist_finger { 16 } else { 16 - 12 };
|
||||
// 不能大于udp最大载荷长度
|
||||
if len < min_len || len > 65535 - 20 - 8 - 12 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"AesCbcSecretBody length overflow",
|
||||
));
|
||||
}
|
||||
Ok(AesCbcSecretBody {
|
||||
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<B: AsRef<[u8]> + AsMut<[u8]>> AesCbcSecretBody<B> {
|
||||
pub fn set_random(&mut self, random: u32) {
|
||||
let mut end = self.buffer.as_ref().len();
|
||||
if self.exist_finger {
|
||||
end -= 12;
|
||||
}
|
||||
self.buffer.as_mut()[end - 4..end].copy_from_slice(&random.to_be_bytes());
|
||||
}
|
||||
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
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 数据体(n) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| random(32) |
|
||||
| random(32) |
|
||||
| random(32) |
|
||||
| random(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| finger(32) |
|
||||
| finger(32) |
|
||||
| finger(32) |
|
||||
| finger(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
pub struct RsaSecretBody<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> RsaSecretBody<B> {
|
||||
pub fn new(buffer: B) -> io::Result<RsaSecretBody<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
// 不能大于udp最大载荷长度
|
||||
if len < 32 || len > 65535 - 20 - 8 - 12 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"length overflow",
|
||||
));
|
||||
}
|
||||
Ok(RsaSecretBody { buffer })
|
||||
}
|
||||
pub fn data(&self) -> &[u8] {
|
||||
let end = self.buffer.as_ref().len() - 32;
|
||||
&self.buffer.as_ref()[..end]
|
||||
}
|
||||
pub fn random(&self) -> &[u8] {
|
||||
let end = self.buffer.as_ref().len() - 16;
|
||||
&self.buffer.as_ref()[end - 16..end]
|
||||
}
|
||||
pub fn body(&self) -> &[u8] {
|
||||
let end = self.buffer.as_ref().len() - 16;
|
||||
&self.buffer.as_ref()[..end]
|
||||
}
|
||||
pub fn finger(&self) -> &[u8] {
|
||||
let end = self.buffer.as_ref().len() - 16;
|
||||
&self.buffer.as_ref()[end..]
|
||||
}
|
||||
pub fn buffer(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> RsaSecretBody<B> {
|
||||
pub fn set_random(&mut self, random: &[u8]) -> io::Result<()> {
|
||||
if random.len() != 16 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"random.len != 16",
|
||||
));
|
||||
}
|
||||
let end = self.buffer.as_ref().len() - 16;
|
||||
self.buffer.as_mut()[end - 16..end].copy_from_slice(random);
|
||||
Ok(())
|
||||
}
|
||||
pub fn random_mut(&mut self) -> &mut [u8] {
|
||||
let end = self.buffer.as_ref().len() - 16;
|
||||
&mut self.buffer.as_mut()[end - 16..end]
|
||||
}
|
||||
pub fn set_finger(&mut self, finger: &[u8]) -> io::Result<()> {
|
||||
if finger.len() != 16 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"finger.len != 16",
|
||||
));
|
||||
}
|
||||
let end = self.buffer.as_ref().len();
|
||||
self.buffer.as_mut()[end - 16..end].copy_from_slice(finger);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::net::Ipv4Addr;
|
||||
use std::{fmt, io};
|
||||
|
||||
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||
pub enum Protocol {
|
||||
/// ping请求
|
||||
@@ -18,7 +18,10 @@ pub enum Protocol {
|
||||
PunchRequest,
|
||||
/// 打洞响应
|
||||
PunchResponse,
|
||||
UnKnow(u8),
|
||||
///获取对端看到的地址
|
||||
AddrRequest,
|
||||
AddrResponse,
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for Protocol {
|
||||
@@ -28,7 +31,9 @@ impl From<u8> for Protocol {
|
||||
2 => Protocol::Pong,
|
||||
3 => Protocol::PunchRequest,
|
||||
4 => Protocol::PunchResponse,
|
||||
val => Protocol::UnKnow(val),
|
||||
5 => Protocol::AddrRequest,
|
||||
6 => Protocol::AddrResponse,
|
||||
val => Protocol::Unknown(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,7 +45,9 @@ impl Into<u8> for Protocol {
|
||||
Protocol::Pong => 2,
|
||||
Protocol::PunchRequest => 3,
|
||||
Protocol::PunchResponse => 4,
|
||||
Protocol::UnKnow(val) => val,
|
||||
Protocol::AddrRequest => 5,
|
||||
Protocol::AddrResponse => 6,
|
||||
Protocol::Unknown(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,6 +57,8 @@ pub enum ControlPacket<B> {
|
||||
PongPacket(PongPacket<B>),
|
||||
PunchRequest,
|
||||
PunchResponse,
|
||||
AddrRequest,
|
||||
AddrResponse(AddrPacket<B>),
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> ControlPacket<B> {
|
||||
@@ -59,18 +68,19 @@ impl<B: AsRef<[u8]>> ControlPacket<B> {
|
||||
Protocol::Pong => Ok(ControlPacket::PongPacket(PongPacket::new(buffer)?)),
|
||||
Protocol::PunchRequest => Ok(ControlPacket::PunchRequest),
|
||||
Protocol::PunchResponse => Ok(ControlPacket::PunchResponse),
|
||||
Protocol::UnKnow(_) => Err(io::Error::new(io::ErrorKind::InvalidData, "Unsupported")),
|
||||
Protocol::AddrRequest => Ok(ControlPacket::AddrRequest),
|
||||
Protocol::AddrResponse => Ok(ControlPacket::AddrResponse(AddrPacket::new(buffer)?)),
|
||||
Protocol::Unknown(_) => Err(io::Error::new(io::ErrorKind::InvalidData, "Unsupported")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 网络探针
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct PingPacket<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
type PongPacket<B> = PingPacket<B>;
|
||||
pub type PongPacket<B> = PingPacket<B>;
|
||||
|
||||
impl<B: AsRef<[u8]>> PingPacket<B> {
|
||||
pub fn new(buffer: B) -> io::Result<PingPacket<B>> {
|
||||
@@ -108,3 +118,42 @@ impl<B: AsRef<[u8]>> fmt::Debug for PingPacket<B> {
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AddrPacket<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> AddrPacket<B> {
|
||||
pub fn new(buffer: B) -> io::Result<AddrPacket<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
if len != 6 {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData, "len != 6"));
|
||||
}
|
||||
Ok(AddrPacket { buffer })
|
||||
}
|
||||
pub fn ipv4(&self) -> Ipv4Addr {
|
||||
let buf = self.buffer.as_ref();
|
||||
Ipv4Addr::new(buf[0], buf[1], buf[2], buf[3])
|
||||
}
|
||||
pub fn port(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[4..6].try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> AddrPacket<B> {
|
||||
pub fn set_ipv4(&mut self, ip: Ipv4Addr) {
|
||||
self.buffer.as_mut()[..4].copy_from_slice(&ip.octets())
|
||||
}
|
||||
pub fn set_port(&mut self, port: u16) {
|
||||
self.buffer.as_mut()[4..6].copy_from_slice(&port.to_be_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for AddrPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("AddrPacket")
|
||||
.field("ipv4", &self.ipv4())
|
||||
.field("port", &self.port())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
use crate::error::*;
|
||||
use std::io;
|
||||
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||
pub enum Protocol {
|
||||
TokenError,
|
||||
Disconnect,
|
||||
AddressExhausted,
|
||||
IpAlreadyExists,
|
||||
InvalidIp,
|
||||
NoKey,
|
||||
Other(u8),
|
||||
}
|
||||
|
||||
@@ -14,6 +17,9 @@ impl From<u8> for Protocol {
|
||||
1 => Self::TokenError,
|
||||
2 => Self::Disconnect,
|
||||
3 => Self::AddressExhausted,
|
||||
4 => Self::IpAlreadyExists,
|
||||
5 => Self::InvalidIp,
|
||||
6 => Self::NoKey,
|
||||
val => Self::Other(val),
|
||||
}
|
||||
}
|
||||
@@ -25,6 +31,9 @@ impl Into<u8> for Protocol {
|
||||
Protocol::TokenError => 1,
|
||||
Protocol::Disconnect => 2,
|
||||
Protocol::AddressExhausted => 3,
|
||||
Protocol::IpAlreadyExists => 4,
|
||||
Protocol::InvalidIp => 5,
|
||||
Protocol::NoKey => 6,
|
||||
Protocol::Other(val) => val,
|
||||
}
|
||||
}
|
||||
@@ -34,15 +43,21 @@ pub enum InErrorPacket<B> {
|
||||
TokenError,
|
||||
Disconnect,
|
||||
AddressExhausted,
|
||||
IpAlreadyExists,
|
||||
InvalidIp,
|
||||
NoKey,
|
||||
OtherError(ErrorPacket<B>),
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> InErrorPacket<B> {
|
||||
pub fn new(protocol: u8, buffer: B) -> Result<InErrorPacket<B>> {
|
||||
pub fn new(protocol: u8, buffer: B) -> io::Result<InErrorPacket<B>> {
|
||||
match Protocol::from(protocol) {
|
||||
Protocol::TokenError => Ok(InErrorPacket::TokenError),
|
||||
Protocol::Disconnect => Ok(InErrorPacket::Disconnect),
|
||||
Protocol::AddressExhausted => Ok(InErrorPacket::AddressExhausted),
|
||||
Protocol::IpAlreadyExists => Ok(InErrorPacket::IpAlreadyExists),
|
||||
Protocol::InvalidIp => Ok(InErrorPacket::InvalidIp),
|
||||
Protocol::NoKey => Ok(InErrorPacket::NoKey),
|
||||
Protocol::Other(_) => Ok(InErrorPacket::OtherError(ErrorPacket::new(buffer)?)),
|
||||
}
|
||||
}
|
||||
@@ -53,16 +68,16 @@ pub struct ErrorPacket<B> {
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> ErrorPacket<B> {
|
||||
pub fn new(buffer: B) -> Result<ErrorPacket<B>> {
|
||||
pub fn new(buffer: B) -> io::Result<ErrorPacket<B>> {
|
||||
Ok(Self { buffer })
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> ErrorPacket<B> {
|
||||
pub fn message(&self) -> Result<String> {
|
||||
pub fn message(&self) -> io::Result<String> {
|
||||
match String::from_utf8(self.buffer.as_ref().to_vec()) {
|
||||
Ok(str) => Ok(str),
|
||||
Err(_) => Err(Error::InvalidPacket),
|
||||
Err(_) => Err(io::Error::new(io::ErrorKind::Other, "Utf8Error")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
pub enum Protocol {
|
||||
Ipv4,
|
||||
Ipv4Broadcast,
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for Protocol {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
4 => Protocol::Ipv4,
|
||||
201 => Protocol::Ipv4Broadcast,
|
||||
val => Protocol::Unknown(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for Protocol {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
Protocol::Ipv4 => 4,
|
||||
Protocol::Ipv4Broadcast => 201,
|
||||
Protocol::Unknown(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BroadcastPacket<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> BroadcastPacket<B> {
|
||||
pub fn unchecked(buffer: B) -> Self {
|
||||
Self { buffer }
|
||||
}
|
||||
pub fn new(buffer: B) -> io::Result<Self> {
|
||||
let len = buffer.as_ref().len();
|
||||
let packet = Self::unchecked(buffer);
|
||||
if len < 2 + 4 || packet.addr_num() == 0 {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"BroadcastPacket InvalidData",
|
||||
))
|
||||
} else {
|
||||
Ok(packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> BroadcastPacket<B> {
|
||||
pub fn addr_num(&self) -> u8 {
|
||||
self.buffer.as_ref()[0]
|
||||
}
|
||||
/// 已经发送给了这些地址
|
||||
pub fn addresses(&self) -> Vec<Ipv4Addr> {
|
||||
let num = self.addr_num() as usize;
|
||||
let mut list = Vec::with_capacity(num);
|
||||
let buf = self.buffer.as_ref();
|
||||
let mut offset = 1;
|
||||
for _ in 0..num {
|
||||
list.push(Ipv4Addr::new(
|
||||
buf[offset],
|
||||
buf[offset + 1],
|
||||
buf[offset + 2],
|
||||
buf[offset + 3],
|
||||
));
|
||||
offset += 4;
|
||||
}
|
||||
list
|
||||
}
|
||||
pub fn data(&self) -> io::Result<&[u8]> {
|
||||
let start = 1 + self.addr_num() as usize * 4;
|
||||
if start > self.buffer.as_ref().len() {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidData, "InvalidData"))
|
||||
} else {
|
||||
Ok(&self.buffer.as_ref()[start..])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> BroadcastPacket<B> {
|
||||
pub fn set_address(&mut self, addr: &[Ipv4Addr]) -> io::Result<()> {
|
||||
let buf = self.buffer.as_mut();
|
||||
if buf.len() < 1 + addr.len() * 4 || addr.len() > u8::MAX as usize {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"addr invalid data",
|
||||
))
|
||||
} else {
|
||||
buf[0] = addr.len() as u8;
|
||||
let mut offset = 1;
|
||||
for ip in addr {
|
||||
buf[offset..offset + 4].copy_from_slice(&ip.octets());
|
||||
offset += 4;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
pub fn set_data(&mut self, data: &[u8]) -> io::Result<()> {
|
||||
let num = self.addr_num() as usize;
|
||||
let start = 1 + 4 * num;
|
||||
let buf = self.buffer.as_mut();
|
||||
if start >= buf.len() || start + data.len() != buf.len() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"data invalid data",
|
||||
));
|
||||
}
|
||||
buf[start..].copy_from_slice(data);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
use crate::protocol::body::ENCRYPTION_RESERVED;
|
||||
use std::net::Ipv4Addr;
|
||||
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) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 源ip地址(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 目的ip地址(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 数据体 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
注:e为是否加密标志,s为服务端通信包标志,u未使用
|
||||
*/
|
||||
pub const HEAD_LEN: usize = 12;
|
||||
|
||||
pub mod body;
|
||||
pub mod control_packet;
|
||||
pub mod error_packet;
|
||||
pub mod ip_turn_packet;
|
||||
pub mod other_turn_packet;
|
||||
pub mod service_packet;
|
||||
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||
pub enum Version {
|
||||
V1,
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for Version {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
1 => Version::V1,
|
||||
val => Version::Unknown(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for Version {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
Version::V1 => 1,
|
||||
Version::Unknown(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||
pub enum Protocol {
|
||||
/// 服务包 用于和服务端交互
|
||||
Service,
|
||||
/// 响应异常
|
||||
Error,
|
||||
/// 控制协议
|
||||
Control,
|
||||
/// 转发ip数据
|
||||
IpTurn,
|
||||
/// 转发其他数据
|
||||
OtherTurn,
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for Protocol {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
1 => Protocol::Service,
|
||||
2 => Protocol::Error,
|
||||
3 => Protocol::Control,
|
||||
4 => Protocol::IpTurn,
|
||||
5 => Protocol::OtherTurn,
|
||||
val => Protocol::Unknown(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for Protocol {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
Protocol::Service => 1,
|
||||
Protocol::Error => 2,
|
||||
Protocol::Control => 3,
|
||||
Protocol::IpTurn => 4,
|
||||
Protocol::OtherTurn => 5,
|
||||
Protocol::Unknown(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const MAX_TTL: u8 = 0b1111;
|
||||
pub const MAX_SOURCE: u8 = 0b11110000;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct NetPacket<B> {
|
||||
data_len: usize,
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> NetPacket<B> {
|
||||
pub fn new(buffer: B) -> io::Result<NetPacket<B>> {
|
||||
let data_len = buffer.as_ref().len();
|
||||
Self::new0(data_len, buffer)
|
||||
}
|
||||
pub fn new_encrypt(buffer: B) -> io::Result<NetPacket<B>> {
|
||||
if 12 + ENCRYPTION_RESERVED > buffer.as_ref().len() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"length overflow",
|
||||
));
|
||||
}
|
||||
//加密需要预留ENCRYPTION_RESERVED字节
|
||||
let data_len = buffer.as_ref().len() - ENCRYPTION_RESERVED;
|
||||
Self::new0(data_len, buffer)
|
||||
}
|
||||
pub fn new0(data_len: usize, buffer: B) -> io::Result<NetPacket<B>> {
|
||||
if data_len > buffer.as_ref().len() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"length overflow",
|
||||
));
|
||||
}
|
||||
// 不能大于udp最大载荷长度
|
||||
if data_len < 12 || data_len > 65535 - 20 - 8 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"length overflow",
|
||||
));
|
||||
}
|
||||
Ok(NetPacket { data_len, buffer })
|
||||
}
|
||||
pub fn buffer(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[..self.data_len]
|
||||
}
|
||||
pub fn raw_buffer(&self) -> &[u8] {
|
||||
self.buffer.as_ref()
|
||||
}
|
||||
pub fn data_len(&self) -> usize {
|
||||
self.data_len
|
||||
}
|
||||
pub fn reserve(&self) -> usize {
|
||||
self.buffer.as_ref().len() - self.data_len
|
||||
}
|
||||
pub fn into_buffer(self) -> B {
|
||||
self.buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> NetPacket<B> {
|
||||
/// 数据加密
|
||||
pub fn is_encrypt(&self) -> bool {
|
||||
self.buffer.as_ref()[0] & 0x80 == 0x80
|
||||
}
|
||||
/// 网关通信的标识
|
||||
pub fn is_gateway(&self) -> bool {
|
||||
self.buffer.as_ref()[0] & 0x40 == 0x40
|
||||
}
|
||||
pub fn version(&self) -> Version {
|
||||
Version::from(self.buffer.as_ref()[0] & 0x0F)
|
||||
}
|
||||
pub fn protocol(&self) -> Protocol {
|
||||
Protocol::from(self.buffer.as_ref()[1])
|
||||
}
|
||||
pub fn transport_protocol(&self) -> u8 {
|
||||
self.buffer.as_ref()[2]
|
||||
}
|
||||
pub fn ttl(&self) -> u8 {
|
||||
self.buffer.as_ref()[3] & MAX_TTL
|
||||
}
|
||||
pub fn source_ttl(&self) -> u8 {
|
||||
self.buffer.as_ref()[3] >> 4
|
||||
}
|
||||
pub fn source(&self) -> Ipv4Addr {
|
||||
let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap();
|
||||
Ipv4Addr::from(tmp)
|
||||
}
|
||||
pub fn destination(&self) -> Ipv4Addr {
|
||||
let tmp: [u8; 4] = self.buffer.as_ref()[8..12].try_into().unwrap();
|
||||
Ipv4Addr::from(tmp)
|
||||
}
|
||||
pub fn payload(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[12..self.data_len]
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> NetPacket<B> {
|
||||
pub fn buffer_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.buffer.as_mut()[..self.data_len]
|
||||
}
|
||||
pub fn set_encrypt_flag(&mut self, is_encrypt: bool) {
|
||||
if is_encrypt {
|
||||
self.buffer.as_mut()[0] = self.buffer.as_ref()[0] | 0x80
|
||||
} else {
|
||||
self.buffer.as_mut()[0] = self.buffer.as_ref()[0] & 0x7F
|
||||
};
|
||||
}
|
||||
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
|
||||
} else {
|
||||
self.buffer.as_mut()[0] = self.buffer.as_ref()[0] & 0xBF
|
||||
};
|
||||
}
|
||||
pub fn set_version(&mut self, version: Version) {
|
||||
let v: u8 = version.into();
|
||||
self.buffer.as_mut()[0] = (self.buffer.as_ref()[0] & 0xF0) | (0x0F & v);
|
||||
}
|
||||
pub fn set_protocol(&mut self, protocol: Protocol) {
|
||||
self.buffer.as_mut()[1] = protocol.into();
|
||||
}
|
||||
pub fn set_transport_protocol(&mut self, transport_protocol: u8) {
|
||||
self.buffer.as_mut()[2] = transport_protocol;
|
||||
}
|
||||
pub fn first_set_ttl(&mut self, ttl: u8) {
|
||||
self.buffer.as_mut()[3] = ttl << 4 | ttl;
|
||||
}
|
||||
pub fn set_ttl(&mut self, ttl: u8) {
|
||||
self.buffer.as_mut()[3] = (self.buffer.as_mut()[3] & MAX_SOURCE) | (MAX_TTL & ttl);
|
||||
}
|
||||
pub fn incr_ttl(&mut self) -> u8 {
|
||||
let ttl = self.ttl() - 1;
|
||||
self.set_ttl(ttl);
|
||||
ttl
|
||||
}
|
||||
pub fn set_source_ttl(&mut self, source_ttl: u8) {
|
||||
self.buffer.as_mut()[3] = (source_ttl << 4) | (MAX_TTL & self.buffer.as_ref()[3]);
|
||||
}
|
||||
pub fn set_source(&mut self, source: Ipv4Addr) {
|
||||
self.buffer.as_mut()[4..8].copy_from_slice(&source.octets());
|
||||
}
|
||||
pub fn set_destination(&mut self, destination: Ipv4Addr) {
|
||||
self.buffer.as_mut()[8..12].copy_from_slice(&destination.octets());
|
||||
}
|
||||
pub fn set_payload(&mut self, payload: &[u8]) -> io::Result<()> {
|
||||
if self.data_len - 12 != payload.len() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"data_len - 12 != payload.len",
|
||||
));
|
||||
}
|
||||
self.buffer.as_mut()[12..self.data_len].copy_from_slice(payload);
|
||||
Ok(())
|
||||
}
|
||||
pub fn payload_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.buffer.as_mut()[12..self.data_len]
|
||||
}
|
||||
pub fn set_data_len(&mut self, data_len: usize) -> io::Result<()> {
|
||||
if data_len > self.buffer.as_ref().len() || data_len < 12 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"data_len invalid",
|
||||
));
|
||||
}
|
||||
self.data_len = data_len;
|
||||
Ok(())
|
||||
}
|
||||
pub fn set_data_len_max(&mut self) {
|
||||
self.data_len = self.buffer.as_ref().len();
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for NetPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("NetPacket")
|
||||
.field("version", &self.version())
|
||||
.field("gateway", &self.is_gateway())
|
||||
.field("encrypt", &self.is_encrypt())
|
||||
.field("protocol", &self.protocol())
|
||||
.field("transport_protocol", &self.transport_protocol())
|
||||
.field("ttl", &self.ttl())
|
||||
.field("source_ttl", &self.source_ttl())
|
||||
.field("source", &self.source())
|
||||
.field("destination", &self.destination())
|
||||
.field("payload", &self.payload())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,14 @@
|
||||
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
pub enum Protocol {
|
||||
Punch,
|
||||
UnKnow(u8),
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for Protocol {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
1 => Protocol::Punch,
|
||||
val => Protocol::UnKnow(val),
|
||||
val => Protocol::Unknown(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +17,7 @@ impl Into<u8> for Protocol {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
Protocol::Punch => 1,
|
||||
Protocol::UnKnow(val) => val,
|
||||
Protocol::Unknown(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,12 @@ pub enum Protocol {
|
||||
PollDeviceList,
|
||||
/// 推送设备列表
|
||||
PushDeviceList,
|
||||
UnKnow(u8),
|
||||
/// 和服务端握手
|
||||
HandshakeRequest,
|
||||
HandshakeResponse,
|
||||
SecretHandshakeRequest,
|
||||
SecretHandshakeResponse,
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for Protocol {
|
||||
@@ -18,7 +23,11 @@ impl From<u8> for Protocol {
|
||||
2 => Self::RegistrationResponse,
|
||||
3 => Self::PollDeviceList,
|
||||
4 => Self::PushDeviceList,
|
||||
val => Self::UnKnow(val),
|
||||
5 => Self::HandshakeRequest,
|
||||
6 => Self::HandshakeResponse,
|
||||
7 => Self::SecretHandshakeRequest,
|
||||
8 => Self::SecretHandshakeResponse,
|
||||
val => Self::Unknown(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +39,11 @@ impl Into<u8> for Protocol {
|
||||
Self::RegistrationResponse => 2,
|
||||
Self::PollDeviceList => 3,
|
||||
Self::PushDeviceList => 4,
|
||||
Self::UnKnow(val) => val,
|
||||
Self::HandshakeRequest => 5,
|
||||
Self::HandshakeResponse => 6,
|
||||
Self::SecretHandshakeRequest => 7,
|
||||
Self::SecretHandshakeResponse => 8,
|
||||
Self::Unknown(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
use std::io;
|
||||
use std::sync::Arc;
|
||||
|
||||
use tun::device::IFace;
|
||||
use tun::Device;
|
||||
|
||||
use crate::core::Config;
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
const DEFAULT_TUN_NAME: &str = "vnt-tun";
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
const DEFAULT_TAP_NAME: &str = "vnt-tap";
|
||||
|
||||
pub fn create_device(config: &Config) -> io::Result<Arc<Device>> {
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
let default_name: &str = if config.tap {
|
||||
DEFAULT_TAP_NAME
|
||||
} else {
|
||||
DEFAULT_TUN_NAME
|
||||
};
|
||||
#[cfg(target_os = "linux")]
|
||||
let device = {
|
||||
let device_name = config
|
||||
.device_name
|
||||
.clone()
|
||||
.unwrap_or(default_name.to_string());
|
||||
if &device_name == default_name {
|
||||
delete_device(default_name);
|
||||
}
|
||||
Arc::new(Device::new(Some(device_name), config.tap)?)
|
||||
};
|
||||
#[cfg(target_os = "macos")]
|
||||
let device = Arc::new(Device::new(config.device_name.clone())?);
|
||||
#[cfg(target_os = "windows")]
|
||||
let device = Arc::new(Device::new(
|
||||
config
|
||||
.device_name
|
||||
.clone()
|
||||
.unwrap_or(default_name.to_string()),
|
||||
config.tap,
|
||||
)?);
|
||||
#[cfg(target_os = "android")]
|
||||
let device = Arc::new(Device::new(config.device_fd as _)?);
|
||||
#[cfg(not(target_os = "android"))]
|
||||
{
|
||||
let mtu = config.mtu.unwrap_or_else(|| {
|
||||
if config.password.is_none() {
|
||||
1450
|
||||
} else {
|
||||
1410
|
||||
}
|
||||
});
|
||||
device.set_mtu(mtu)?;
|
||||
}
|
||||
Ok(device)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn delete_device(name: &str) {
|
||||
// 删除默认网卡,此操作有风险,后续可能去除
|
||||
use std::process::Command;
|
||||
let cmd = format!("ip link delete {}", name);
|
||||
let delete_tun = Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(&cmd)
|
||||
.output()
|
||||
.expect("sh exec error!");
|
||||
if !delete_tun.status.success() {
|
||||
log::warn!("删除网卡失败:{:?}", delete_tun);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// 不安全的并发计数器,谨慎使用
|
||||
|
||||
pub struct U64Adder {
|
||||
global_index: Arc<AtomicUsize>,
|
||||
inner: Arc<U64AdderInner>,
|
||||
index: usize,
|
||||
}
|
||||
pub struct SingleU64Adder {
|
||||
inner: Arc<SingleU64AdderInner>,
|
||||
}
|
||||
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<SingleU64AdderInner>,
|
||||
}
|
||||
|
||||
impl U64AdderInner {
|
||||
pub fn get(&self) -> u64 {
|
||||
let mut count = 0;
|
||||
for counter in self.base.iter() {
|
||||
count += counter.get()
|
||||
}
|
||||
count
|
||||
}
|
||||
}
|
||||
|
||||
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 get(&self) -> u64 {
|
||||
self.inner.get()
|
||||
}
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WatchU64Adder {
|
||||
inner: Arc<U64AdderInner>,
|
||||
}
|
||||
|
||||
impl WatchU64Adder {
|
||||
pub fn get(&self) -> u64 {
|
||||
self.inner.get()
|
||||
}
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct WatchSingleU64Adder {
|
||||
inner: Arc<SingleU64AdderInner>,
|
||||
}
|
||||
impl WatchSingleU64Adder {
|
||||
pub fn get(&self) -> u64 {
|
||||
self.inner.get()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
mod adder;
|
||||
pub use adder::*;
|
||||
@@ -0,0 +1,9 @@
|
||||
mod notify;
|
||||
mod result_convert;
|
||||
pub use result_convert::io_convert;
|
||||
mod scheduler;
|
||||
pub use notify::StopManager;
|
||||
pub use scheduler::Scheduler;
|
||||
|
||||
mod counter;
|
||||
pub use counter::*;
|
||||
@@ -0,0 +1,143 @@
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::thread::Thread;
|
||||
use std::{io, thread};
|
||||
|
||||
use parking_lot::Mutex;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct StopManager {
|
||||
inner: Arc<StopManagerInner>,
|
||||
}
|
||||
|
||||
impl StopManager {
|
||||
pub fn new<F>(f: F) -> Self
|
||||
where
|
||||
F: FnOnce() + Send + 'static,
|
||||
{
|
||||
Self {
|
||||
inner: Arc::new(StopManagerInner::new(f)),
|
||||
}
|
||||
}
|
||||
pub fn add_listener<F>(&self, name: String, f: F) -> io::Result<Worker>
|
||||
where
|
||||
F: FnOnce() + Send + 'static,
|
||||
{
|
||||
self.inner.add_listener(name, f)
|
||||
}
|
||||
pub fn stop(&self) {
|
||||
self.inner.stop("");
|
||||
}
|
||||
pub fn wait(&self) {
|
||||
self.inner.wait();
|
||||
}
|
||||
pub fn is_stop(&self) -> bool {
|
||||
self.inner.state.load(Ordering::Acquire)
|
||||
}
|
||||
}
|
||||
|
||||
struct StopManagerInner {
|
||||
listeners: Mutex<(bool, Vec<(String, Box<dyn FnOnce() + Send>)>)>,
|
||||
park_threads: Mutex<Vec<Thread>>,
|
||||
worker_num: AtomicUsize,
|
||||
state: AtomicBool,
|
||||
stop_call: Mutex<Option<Box<dyn FnOnce() + Send>>>,
|
||||
}
|
||||
|
||||
impl StopManagerInner {
|
||||
fn new<F>(f: F) -> Self
|
||||
where
|
||||
F: FnOnce() + Send + 'static,
|
||||
{
|
||||
Self {
|
||||
listeners: Mutex::new((false, Vec::with_capacity(32))),
|
||||
park_threads: Mutex::new(Vec::with_capacity(4)),
|
||||
worker_num: AtomicUsize::new(0),
|
||||
state: AtomicBool::new(false),
|
||||
stop_call: Mutex::new(Some(Box::new(f))),
|
||||
}
|
||||
}
|
||||
fn add_listener<F>(self: &Arc<Self>, name: String, f: F) -> io::Result<Worker>
|
||||
where
|
||||
F: FnOnce() + Send + 'static,
|
||||
{
|
||||
if name.is_empty() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "name cannot be empty"));
|
||||
}
|
||||
let mut guard = self.listeners.lock();
|
||||
if guard.0 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "stopped"));
|
||||
}
|
||||
for (n, _) in &guard.1 {
|
||||
if &name == n {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("stop add_listener {:?} name already exists", name),
|
||||
));
|
||||
}
|
||||
}
|
||||
guard.1.push((name.clone(), Box::new(f)));
|
||||
Ok(Worker::new(name, self.clone()))
|
||||
}
|
||||
fn stop(&self, skip_name: &str) {
|
||||
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;
|
||||
}
|
||||
listener();
|
||||
}
|
||||
}
|
||||
fn wait(&self) {
|
||||
{
|
||||
let mut guard = self.park_threads.lock();
|
||||
guard.push(thread::current());
|
||||
drop(guard);
|
||||
}
|
||||
loop {
|
||||
if self.worker_num.load(Ordering::Acquire) == 0 {
|
||||
return;
|
||||
}
|
||||
thread::park()
|
||||
}
|
||||
}
|
||||
fn stop_call(&self) {
|
||||
if let Some(call) = self.stop_call.lock().take() {
|
||||
call();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Worker {
|
||||
name: String,
|
||||
inner: Arc<StopManagerInner>,
|
||||
}
|
||||
|
||||
impl Worker {
|
||||
fn new(name: String, inner: Arc<StopManagerInner>) -> Self {
|
||||
let _ = inner.worker_num.fetch_add(1, Ordering::AcqRel);
|
||||
Self { name, inner }
|
||||
}
|
||||
fn release0(&self) {
|
||||
let inner = &self.inner;
|
||||
let count = inner.worker_num.fetch_sub(1, Ordering::AcqRel);
|
||||
if count == 1 {
|
||||
for x in inner.park_threads.lock().drain(..) {
|
||||
x.unpark();
|
||||
}
|
||||
self.inner.stop_call();
|
||||
}
|
||||
}
|
||||
pub fn stop_all(self) {
|
||||
self.inner.stop(&self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Worker {
|
||||
fn drop(&mut self) {
|
||||
self.release0();
|
||||
log::info!("stop {}", self.name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
use std::fmt::Display;
|
||||
use std::io;
|
||||
|
||||
#[inline]
|
||||
pub fn io_convert<T, R: Display, F: FnOnce(&io::Error) -> R>(
|
||||
rs: io::Result<T>,
|
||||
f: F,
|
||||
) -> io::Result<T> {
|
||||
rs.map_err(|e| io::Error::new(e.kind(), format!("{},internal error:{:?}", f(&e), e)))
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
use crate::util::StopManager;
|
||||
use std::collections::BinaryHeap;
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
io,
|
||||
sync::mpsc::{sync_channel, Receiver, SyncSender},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
struct DelayedTask {
|
||||
f: Box<dyn FnOnce(&Scheduler) + Send>,
|
||||
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<Ordering> {
|
||||
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<Op>,
|
||||
}
|
||||
impl Scheduler {
|
||||
pub fn new(stop_manager: StopManager) -> io::Result<Self> {
|
||||
let (sender, receiver) = sync_channel::<Op>(32);
|
||||
let s = Self { sender };
|
||||
let s_inner = s.clone();
|
||||
let worker = {
|
||||
let scheduler = s.clone();
|
||||
stop_manager.add_listener("Scheduler".into(), move || {
|
||||
scheduler.shutdown();
|
||||
})?
|
||||
};
|
||||
std::thread::Builder::new()
|
||||
.name("Scheduler".into())
|
||||
.spawn(move || {
|
||||
run(receiver, s_inner);
|
||||
worker.stop_all();
|
||||
})
|
||||
.expect("Scheduler");
|
||||
Ok(s)
|
||||
}
|
||||
pub fn timeout<F>(&self, time: Duration, f: F) -> bool
|
||||
where
|
||||
F: FnOnce(&Scheduler) + Send + 'static,
|
||||
{
|
||||
let task = DelayedTask {
|
||||
f: Box::new(f),
|
||||
next: Instant::now().checked_add(time).unwrap(),
|
||||
};
|
||||
self.sender.send(Op::Task(task)).is_ok()
|
||||
}
|
||||
pub fn shutdown(self) {
|
||||
let _ = self.sender.send(Op::Stop);
|
||||
}
|
||||
}
|
||||
fn run(receiver: Receiver<Op>, s_inner: Scheduler) {
|
||||
let mut binary_heap = BinaryHeap::<DelayedTask>::with_capacity(32);
|
||||
loop {
|
||||
while let Some(task) = binary_heap.peek() {
|
||||
let now = Instant::now();
|
||||
if now < task.next {
|
||||
//需要等待对应时间
|
||||
match receiver.recv_timeout(task.next - now) {
|
||||
Ok(op) => {
|
||||
if add_task(op, &mut binary_heap) {
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
Err(e) => match e {
|
||||
std::sync::mpsc::RecvTimeoutError::Timeout => continue,
|
||||
std::sync::mpsc::RecvTimeoutError::Disconnected => return,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
if let Some(task) = binary_heap.pop() {
|
||||
(task.f)(&s_inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
//取出所有任务
|
||||
loop {
|
||||
match receiver.try_recv() {
|
||||
Ok(op) => {
|
||||
if add_task(op, &mut binary_heap) {
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
Err(e) => match e {
|
||||
std::sync::mpsc::TryRecvError::Empty => break,
|
||||
std::sync::mpsc::TryRecvError::Disconnected => return,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if binary_heap.is_empty() {
|
||||
//任务队列为空时陷入等待
|
||||
if let Ok(op) = receiver.recv() {
|
||||
if add_task(op, &mut binary_heap) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
fn add_task(op: Op, binary_heap: &mut BinaryHeap<DelayedTask>) -> bool {
|
||||
return match op {
|
||||
Op::Task(task) => {
|
||||
binary_heap.push(task);
|
||||
true
|
||||
}
|
||||
Op::Stop => false,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
[package]
|
||||
name = "tun"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.153"
|
||||
|
||||
log = { version = "0.4.20", features = [] }
|
||||
rand = "0.8.5"
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies]
|
||||
ioctl = { version = "0.8", package = "ioctl-sys" }
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
libloading = "0.8.0"
|
||||
widestring = "1.0.2"
|
||||
winapi = {version = "0.3",features = [
|
||||
"errhandlingapi",
|
||||
"combaseapi",
|
||||
"ioapiset",
|
||||
"winioctl",
|
||||
"setupapi",
|
||||
"synchapi",
|
||||
"netioapi",
|
||||
"fileapi","handleapi","winerror","minwindef","ifdef","basetsd","winnt","winreg","winbase","minwinbase",
|
||||
"impl-default"
|
||||
]}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
use crate::device::IFace;
|
||||
use crate::Fd;
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::os::fd::RawFd;
|
||||
|
||||
pub struct Device {
|
||||
fd: Fd,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
pub fn new(fd: RawFd) -> io::Result<Self> {
|
||||
Ok(Self { fd: Fd::new(fd)? })
|
||||
}
|
||||
}
|
||||
impl IFace for Device {
|
||||
fn version(&self) -> io::Result<String> {
|
||||
Ok(String::new())
|
||||
}
|
||||
|
||||
fn name(&self) -> io::Result<String> {
|
||||
Ok(String::new())
|
||||
}
|
||||
|
||||
fn shutdown(&self) -> io::Result<()> {
|
||||
Err(io::Error::from(io::ErrorKind::Unsupported))
|
||||
}
|
||||
|
||||
fn set_ip(&self, address: Ipv4Addr, mask: Ipv4Addr) -> io::Result<()> {
|
||||
Err(io::Error::from(io::ErrorKind::Unsupported))
|
||||
}
|
||||
|
||||
fn mtu(&self) -> io::Result<u32> {
|
||||
Err(io::Error::from(io::ErrorKind::Unsupported))
|
||||
}
|
||||
|
||||
fn set_mtu(&self, value: u32) -> io::Result<()> {
|
||||
Err(io::Error::from(io::ErrorKind::Unsupported))
|
||||
}
|
||||
|
||||
fn add_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr, metric: u16) -> io::Result<()> {
|
||||
Err(io::Error::from(io::ErrorKind::Unsupported))
|
||||
}
|
||||
|
||||
fn delete_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> {
|
||||
Err(io::Error::from(io::ErrorKind::Unsupported))
|
||||
}
|
||||
|
||||
fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.fd.read(buf)
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.fd.write(buf)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
use io::Result;
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
pub trait IFace {
|
||||
fn version(&self) -> Result<String>;
|
||||
/// Get the device name.
|
||||
fn name(&self) -> Result<String>;
|
||||
|
||||
fn shutdown(&self) -> Result<()>;
|
||||
|
||||
fn set_ip(&self, address: Ipv4Addr, mask: Ipv4Addr) -> Result<()>;
|
||||
|
||||
/// Get the MTU.
|
||||
fn mtu(&self) -> Result<u32>;
|
||||
|
||||
/// Set the MTU.
|
||||
fn set_mtu(&self, value: u32) -> Result<()>;
|
||||
fn add_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr, metric: u16) -> Result<()>;
|
||||
fn delete_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr) -> Result<()>;
|
||||
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize>;
|
||||
fn write(&self, buf: &[u8]) -> Result<usize>;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/// 参考
|
||||
/// https://github.com/meh/rust-tun
|
||||
/// https://github.com/Tazdevil971/tap-windows
|
||||
/// https://github.com/nulldotblack/wintun
|
||||
pub mod device;
|
||||
mod packet;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
mod linux;
|
||||
#[cfg(target_os = "linux")]
|
||||
pub use linux::Device;
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
mod android;
|
||||
#[cfg(target_os = "android")]
|
||||
pub use android::Device;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos;
|
||||
#[cfg(target_os = "macos")]
|
||||
pub use macos::Device;
|
||||
|
||||
#[cfg(unix)]
|
||||
mod unix;
|
||||
#[cfg(unix)]
|
||||
pub use unix::Fd;
|
||||
#[cfg(windows)]
|
||||
mod windows;
|
||||
|
||||
#[cfg(windows)]
|
||||
pub use windows::Device;
|
||||
@@ -0,0 +1,307 @@
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::process::Command;
|
||||
use std::{io, mem, ptr};
|
||||
|
||||
use libc::{
|
||||
c_char, c_short, ifreq, AF_INET, IFF_MULTI_QUEUE, IFF_NO_PI, IFF_RUNNING, IFF_TAP, IFF_TUN,
|
||||
IFF_UP, IFNAMSIZ, O_RDWR, SOCK_DGRAM,
|
||||
};
|
||||
|
||||
use crate::device::IFace;
|
||||
use crate::linux::route;
|
||||
use crate::linux::sys::*;
|
||||
use crate::packet;
|
||||
use crate::unix::{exe_cmd, Fd, SockAddr};
|
||||
|
||||
pub struct Device {
|
||||
name: String,
|
||||
ctl: Fd,
|
||||
tun: Fd,
|
||||
mac: Option<[u8; 6]>,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
pub fn new(name: Option<String>, tap: bool) -> io::Result<Self> {
|
||||
let device = unsafe {
|
||||
let dev = match name {
|
||||
Some(name) => {
|
||||
let name =
|
||||
CString::new(name).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
|
||||
if name.as_bytes_with_nul().len() > IFNAMSIZ {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidInput, "name too long"));
|
||||
}
|
||||
|
||||
Some(name)
|
||||
}
|
||||
|
||||
None => None,
|
||||
};
|
||||
|
||||
let mut req: ifreq = mem::zeroed();
|
||||
|
||||
if let Some(dev) = dev.as_ref() {
|
||||
ptr::copy_nonoverlapping(
|
||||
dev.as_ptr() as *const c_char,
|
||||
req.ifr_name.as_mut_ptr(),
|
||||
dev.as_bytes().len(),
|
||||
);
|
||||
}
|
||||
|
||||
let device_type: c_short = if tap { IFF_TAP } else { IFF_TUN } as c_short;
|
||||
|
||||
let queues_num = 1;
|
||||
|
||||
let iff_no_pi = IFF_NO_PI as c_short;
|
||||
let iff_multi_queue = IFF_MULTI_QUEUE as c_short;
|
||||
let packet_information = false;
|
||||
req.ifr_ifru.ifru_flags = device_type
|
||||
| if packet_information { 0 } else { iff_no_pi }
|
||||
| if queues_num > 1 { iff_multi_queue } else { 0 };
|
||||
|
||||
let tun = Fd::new(libc::open(b"/dev/net/tun\0".as_ptr() as *const _, O_RDWR))
|
||||
.map_err(|_| io::Error::last_os_error())?;
|
||||
|
||||
if tunsetiff(tun.0, &mut req as *mut _ as *mut _) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
let ctl = Fd::new(libc::socket(AF_INET, SOCK_DGRAM, 0))?;
|
||||
|
||||
let name = CStr::from_ptr(req.ifr_name.as_ptr())
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
let mac = if tap {
|
||||
let get_mac_cmd = format!("cat /sys/class/net/{}/address", name);
|
||||
let mac_out = exe_cmd(&get_mac_cmd)?;
|
||||
let mac_str = String::from_utf8(mac_out.stdout).unwrap();
|
||||
let mut mac = [0; 6];
|
||||
let mut split = mac_str.split(":");
|
||||
for i in 0..6 {
|
||||
mac[i] = u8::from_str_radix(&split.next().unwrap()[..2], 16).unwrap();
|
||||
}
|
||||
Some(mac)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let set_txqueuelen = format!("ifconfig {} txqueuelen 1000", name);
|
||||
if let Err(e) = exe_cmd(&set_txqueuelen){
|
||||
log::warn!("{:?}",e);
|
||||
}
|
||||
Device {
|
||||
name,
|
||||
tun,
|
||||
ctl,
|
||||
mac,
|
||||
}
|
||||
};
|
||||
device.enabled(true)?;
|
||||
Ok(device)
|
||||
}
|
||||
}
|
||||
|
||||
impl Device {
|
||||
fn enabled(&self, value: bool) -> io::Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifflags(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
if value {
|
||||
req.ifr_ifru.ifru_flags |= (IFF_UP | IFF_RUNNING) as c_short;
|
||||
} else {
|
||||
req.ifr_ifru.ifru_flags &= !(IFF_UP as c_short);
|
||||
}
|
||||
|
||||
if siocsifflags(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
unsafe fn request(&self) -> ifreq {
|
||||
let mut req: ifreq = mem::zeroed();
|
||||
ptr::copy_nonoverlapping(
|
||||
self.name.as_ptr() as *const c_char,
|
||||
req.ifr_name.as_mut_ptr(),
|
||||
self.name.len(),
|
||||
);
|
||||
req
|
||||
}
|
||||
fn address(&self) -> io::Result<Ipv4Addr> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
SockAddr::new(&req.ifr_ifru.ifru_addr).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_address(&self, value: Ipv4Addr) -> io::Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifr_ifru.ifru_addr = SockAddr::from(value).into();
|
||||
|
||||
if siocsifaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn destination(&self) -> io::Result<Ipv4Addr> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifdstaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
SockAddr::new(&req.ifr_ifru.ifru_dstaddr).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_destination(&self, value: Ipv4Addr) -> io::Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifr_ifru.ifru_dstaddr = SockAddr::from(value).into();
|
||||
|
||||
if siocsifdstaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn broadcast(&self) -> io::Result<Ipv4Addr> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifbrdaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
SockAddr::new(&req.ifr_ifru.ifru_broadaddr).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_broadcast(&self, value: Ipv4Addr) -> io::Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifr_ifru.ifru_broadaddr = SockAddr::from(value).into();
|
||||
|
||||
if siocsifbrdaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn netmask(&self) -> io::Result<Ipv4Addr> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifnetmask(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
SockAddr::new(&req.ifr_ifru.ifru_netmask).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_netmask(&self, value: Ipv4Addr) -> io::Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifr_ifru.ifru_netmask = SockAddr::from(value).into();
|
||||
|
||||
if siocsifnetmask(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IFace for Device {
|
||||
fn version(&self) -> io::Result<String> {
|
||||
Ok(String::new())
|
||||
}
|
||||
|
||||
fn name(&self) -> io::Result<String> {
|
||||
Ok(self.name.clone())
|
||||
}
|
||||
|
||||
fn shutdown(&self) -> io::Result<()> {
|
||||
exe_cmd(&format!("ip link delete {}", self.name))?;
|
||||
Ok(())
|
||||
}
|
||||
fn set_ip(&self, address: Ipv4Addr, mask: Ipv4Addr) -> io::Result<()> {
|
||||
self.set_address(address)?;
|
||||
self.set_netmask(mask)
|
||||
}
|
||||
|
||||
fn mtu(&self) -> io::Result<u32> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifmtu(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(req.ifr_ifru.ifru_mtu as u32)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_mtu(&self, value: u32) -> io::Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifr_ifru.ifru_mtu = value as _;
|
||||
|
||||
if siocsifmtu(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn add_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr, _metric: u16) -> io::Result<()> {
|
||||
route::add_route(&self.name, dest, netmask)
|
||||
}
|
||||
|
||||
fn delete_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> {
|
||||
route::del_route(&self.name, dest, netmask)
|
||||
}
|
||||
|
||||
fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
if self.mac.is_some() {
|
||||
packet::read_tap(
|
||||
buf,
|
||||
|eth_buf| self.tun.read(eth_buf),
|
||||
|eth_buf| self.tun.write(eth_buf),
|
||||
)
|
||||
} else {
|
||||
self.tun.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
if let Some(mac) = &self.mac {
|
||||
packet::write_tap(buf, |eth_buf| self.tun.write(eth_buf), mac)
|
||||
} else {
|
||||
self.tun.write(buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
mod device;
|
||||
pub use device::Device;
|
||||
mod route;
|
||||
mod sys;
|
||||
@@ -0,0 +1,16 @@
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::unix::exe_cmd;
|
||||
|
||||
pub fn add_route(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> {
|
||||
let cmd = format!("ip route add {:?}/{:?} dev {}", address, netmask, name);
|
||||
exe_cmd(&cmd)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn del_route(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> {
|
||||
let cmd = format!("ip route del {:?}/{:?} dev {}", address, netmask, name);
|
||||
exe_cmd(&cmd)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
use ioctl::*;
|
||||
use libc::{c_int, ifreq};
|
||||
|
||||
ioctl!(bad read siocgifflags with 0x8913; ifreq);
|
||||
ioctl!(bad write siocsifflags with 0x8914; ifreq);
|
||||
ioctl!(bad read siocgifaddr with 0x8915; ifreq);
|
||||
ioctl!(bad write siocsifaddr with 0x8916; ifreq);
|
||||
ioctl!(bad read siocgifdstaddr with 0x8917; ifreq);
|
||||
ioctl!(bad write siocsifdstaddr with 0x8918; ifreq);
|
||||
ioctl!(bad read siocgifbrdaddr with 0x8919; ifreq);
|
||||
ioctl!(bad write siocsifbrdaddr with 0x891a; ifreq);
|
||||
ioctl!(bad read siocgifnetmask with 0x891b; ifreq);
|
||||
ioctl!(bad write siocsifnetmask with 0x891c; ifreq);
|
||||
ioctl!(bad read siocgifmtu with 0x8921; ifreq);
|
||||
ioctl!(bad write siocsifmtu with 0x8922; ifreq);
|
||||
ioctl!(bad write siocsifname with 0x8923; ifreq);
|
||||
|
||||
ioctl!(write tunsetiff with b'T', 202; c_int);
|
||||
ioctl!(write tunsetpersist with b'T', 203; c_int);
|
||||
ioctl!(write tunsetowner with b'T', 204; c_int);
|
||||
ioctl!(write tunsetgroup with b'T', 206; c_int);
|
||||
@@ -0,0 +1,291 @@
|
||||
use std::ffi::{c_void, CStr};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::{io, mem, ptr};
|
||||
|
||||
use libc::{
|
||||
c_char, c_short, c_uint, sockaddr, socklen_t, AF_INET, AF_SYSTEM, AF_SYS_CONTROL, IFF_RUNNING,
|
||||
IFF_UP, IFNAMSIZ, PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL, UTUN_OPT_IFNAME,
|
||||
};
|
||||
|
||||
use crate::device::IFace;
|
||||
use crate::macos::route;
|
||||
use crate::macos::sys::*;
|
||||
use crate::unix::{Fd, SockAddr};
|
||||
|
||||
pub struct Device {
|
||||
name: String,
|
||||
ctl: Fd,
|
||||
tun: Fd,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
pub fn new(name: Option<String>) -> io::Result<Self> {
|
||||
let id = if let Some(name) = name {
|
||||
if name.len() > IFNAMSIZ {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidInput, "name too long"));
|
||||
}
|
||||
|
||||
if !name.starts_with("utun") {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid name"));
|
||||
}
|
||||
|
||||
name[4..]
|
||||
.parse::<u32>()
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?
|
||||
+ 1u32
|
||||
} else {
|
||||
0u32
|
||||
};
|
||||
let device = unsafe {
|
||||
let tun = Fd::new(libc::socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL))?;
|
||||
|
||||
let mut info = ctl_info {
|
||||
ctl_id: 0,
|
||||
ctl_name: {
|
||||
let mut buffer = [0; 96];
|
||||
for (i, o) in UTUN_CONTROL_NAME.as_bytes().iter().zip(buffer.iter_mut()) {
|
||||
*o = *i as _;
|
||||
}
|
||||
buffer
|
||||
},
|
||||
};
|
||||
|
||||
if ctliocginfo(tun.0, &mut info as *mut _ as *mut _) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
let addr = sockaddr_ctl {
|
||||
sc_id: info.ctl_id,
|
||||
sc_len: mem::size_of::<sockaddr_ctl>() as _,
|
||||
sc_family: AF_SYSTEM as _,
|
||||
ss_sysaddr: AF_SYS_CONTROL as _,
|
||||
sc_unit: id as c_uint,
|
||||
sc_reserved: [0; 5],
|
||||
};
|
||||
|
||||
let address = &addr as *const sockaddr_ctl as *const sockaddr;
|
||||
if libc::connect(tun.0, address, mem::size_of_val(&addr) as socklen_t) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
let mut name = [0u8; 64];
|
||||
let mut name_len: socklen_t = 64;
|
||||
|
||||
let optval = &mut name as *mut _ as *mut c_void;
|
||||
let optlen = &mut name_len as *mut socklen_t;
|
||||
if libc::getsockopt(tun.0, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, optval, optlen) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
let ctl = Fd::new(libc::socket(AF_INET, SOCK_DGRAM, 0))?;
|
||||
|
||||
Device {
|
||||
name: CStr::from_ptr(name.as_ptr() as *const c_char)
|
||||
.to_string_lossy()
|
||||
.into(),
|
||||
tun,
|
||||
ctl,
|
||||
}
|
||||
};
|
||||
device.enabled(true)?;
|
||||
Ok(device)
|
||||
}
|
||||
}
|
||||
|
||||
impl Device {
|
||||
fn enabled(&self, value: bool) -> io::Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifflags(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
if value {
|
||||
req.ifru.flags |= (IFF_UP | IFF_RUNNING) as c_short;
|
||||
} else {
|
||||
req.ifru.flags &= !(IFF_UP as c_short);
|
||||
}
|
||||
|
||||
if siocsifflags(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
unsafe fn request(&self) -> ifreq {
|
||||
let mut req: ifreq = mem::zeroed();
|
||||
ptr::copy_nonoverlapping(
|
||||
self.name.as_ptr() as *const c_char,
|
||||
req.ifrn.name.as_mut_ptr(),
|
||||
self.name.len(),
|
||||
);
|
||||
req
|
||||
}
|
||||
fn address(&self) -> io::Result<Ipv4Addr> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
SockAddr::new(&req.ifru.addr).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_address(&self, value: Ipv4Addr) -> io::Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.addr = SockAddr::from(value).into();
|
||||
|
||||
if siocsifaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn destination(&self) -> io::Result<Ipv4Addr> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifdstaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
SockAddr::new(&req.ifru.dstaddr).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_destination(&self, value: Ipv4Addr) -> io::Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.dstaddr = SockAddr::from(value).into();
|
||||
|
||||
if siocsifdstaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn broadcast(&self) -> io::Result<Ipv4Addr> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifbrdaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
SockAddr::new(&req.ifru.broadaddr).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_broadcast(&self, value: Ipv4Addr) -> io::Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.broadaddr = SockAddr::from(value).into();
|
||||
|
||||
if siocsifbrdaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn netmask(&self) -> io::Result<Ipv4Addr> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifnetmask(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
SockAddr::unchecked(&req.ifru.addr).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_netmask(&self, value: Ipv4Addr) -> io::Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.addr = SockAddr::from(value).into();
|
||||
|
||||
if siocsifnetmask(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IFace for Device {
|
||||
fn version(&self) -> io::Result<String> {
|
||||
Ok(String::new())
|
||||
}
|
||||
|
||||
fn name(&self) -> io::Result<String> {
|
||||
Ok(self.name.clone())
|
||||
}
|
||||
|
||||
fn shutdown(&self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_ip(&self, address: Ipv4Addr, mask: Ipv4Addr) -> io::Result<()> {
|
||||
self.set_address(address)?;
|
||||
self.set_netmask(mask)
|
||||
}
|
||||
|
||||
fn mtu(&self) -> io::Result<u32> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifmtu(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(req.ifru.mtu as _)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_mtu(&self, value: u32) -> io::Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.mtu = value as _;
|
||||
|
||||
if siocsifmtu(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn add_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr, _metric: u16) -> io::Result<()> {
|
||||
route::add_route(&self.name, dest, netmask)
|
||||
}
|
||||
|
||||
fn delete_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> {
|
||||
route::del_route(&self.name, dest, netmask)
|
||||
}
|
||||
|
||||
fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.tun.read(buf)
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
let mut packet = Vec::<u8>::with_capacity(4 + buf.len());
|
||||
packet.push(0);
|
||||
packet.push(0);
|
||||
packet.extend_from_slice(&(libc::PF_INET as u16).to_be_bytes());
|
||||
packet.extend_from_slice(buf);
|
||||
self.tun.write(&packet)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
mod device;
|
||||
pub use device::Device;
|
||||
mod sys;
|
||||
|
||||
mod route;
|
||||
@@ -0,0 +1,20 @@
|
||||
use crate::unix::exe_cmd;
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
pub fn add_route(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> {
|
||||
let cmd = format!(
|
||||
"route -n add {} -netmask {} -interface {}",
|
||||
address, netmask, name
|
||||
);
|
||||
exe_cmd(&cmd)?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn del_route(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> {
|
||||
let cmd = format!(
|
||||
"route -n delete {} -netmask {} -interface {}",
|
||||
address, netmask, name
|
||||
);
|
||||
exe_cmd(&cmd)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,35 +1,11 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
//! Bindings to internal macOS stuff.
|
||||
|
||||
use ioctl::*;
|
||||
use libc::sockaddr;
|
||||
use libc::{c_char, c_int, c_short, c_uint, c_ushort, c_void};
|
||||
use libc::{c_char, c_int, c_short, c_uint, c_ushort, c_void, sockaddr, IFNAMSIZ};
|
||||
|
||||
pub const IFNAMSIZ: usize = 16;
|
||||
|
||||
pub const IFF_UP: c_short = 0x1;
|
||||
pub const IFF_RUNNING: c_short = 0x40;
|
||||
|
||||
pub const AF_SYS_CONTROL: c_ushort = 2;
|
||||
pub const AF_SYSTEM: c_char = 32;
|
||||
pub const PF_SYSTEM: c_int = AF_SYSTEM as c_int;
|
||||
pub const SYSPROTO_CONTROL: c_int = 2;
|
||||
pub const UTUN_OPT_IFNAME: c_int = 2;
|
||||
pub const UTUN_CONTROL_NAME: &str = "com.apple.net.utun_control";
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ctl_info {
|
||||
@@ -37,6 +13,7 @@ pub struct ctl_info {
|
||||
pub ctl_name: [c_char; 96],
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct sockaddr_ctl {
|
||||
@@ -54,6 +31,7 @@ pub union ifrn {
|
||||
pub name: [c_char; IFNAMSIZ],
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ifdevmtu {
|
||||
@@ -69,6 +47,7 @@ pub union ifku {
|
||||
pub value: c_int,
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ifkpi {
|
||||
@@ -98,6 +77,7 @@ pub union ifru {
|
||||
pub functional_type: c_uint,
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ifreq {
|
||||
@@ -105,6 +85,7 @@ pub struct ifreq {
|
||||
pub ifru: ifru,
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ifaliasreq {
|
||||
@@ -0,0 +1 @@
|
||||
pub mod packet;
|
||||
@@ -0,0 +1,122 @@
|
||||
use std::{fmt, io};
|
||||
|
||||
/// 地址解析协议,由IP地址找到MAC地址
|
||||
/// https://www.ietf.org/rfc/rfc6747.txt
|
||||
/*
|
||||
0 2 4 5 6 8 10 (字节)
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 硬件类型|协议类型|硬件地址长度|协议地址长度|操作类型|
|
||||
| 源MAC地址 | 源ip地址 |
|
||||
| 目的MAC地址 | 目的ip地址 |
|
||||
*/
|
||||
|
||||
pub struct ArpPacket<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> ArpPacket<B> {
|
||||
pub fn unchecked(buffer: B) -> Self {
|
||||
Self { buffer }
|
||||
}
|
||||
pub fn new(buffer: B) -> io::Result<Self> {
|
||||
if buffer.as_ref().len() != 28 {
|
||||
Err(io::Error::from(io::ErrorKind::InvalidData))?;
|
||||
}
|
||||
let packet = Self::unchecked(buffer);
|
||||
Ok(packet)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> ArpPacket<B> {
|
||||
/// 硬件类型 以太网类型为1
|
||||
pub fn hardware_type(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[0..2].try_into().unwrap())
|
||||
}
|
||||
/// 上层协议类型,ipv4是0x0800
|
||||
pub fn protocol_type(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
|
||||
}
|
||||
/// 如果是MAC地址 则长度为6
|
||||
pub fn hardware_size(&self) -> u8 {
|
||||
self.buffer.as_ref()[4]
|
||||
}
|
||||
/// 如果是IPv4 则长度为4
|
||||
pub fn protocol_size(&self) -> u8 {
|
||||
self.buffer.as_ref()[5]
|
||||
}
|
||||
/// 操作类型,请求和响应 1:ARP请求,2:ARP响应,3:RARP请求,4:RARP响应
|
||||
pub fn op_code(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap())
|
||||
}
|
||||
/// 发送端硬件地址,仅支持以太网
|
||||
pub fn sender_hardware_addr(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[8..14]
|
||||
}
|
||||
/// 发送端协议地址,仅支持IPv4
|
||||
pub fn sender_protocol_addr(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[14..18]
|
||||
}
|
||||
/// 接收端硬件地址,仅支持以太网
|
||||
pub fn target_hardware_addr(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[18..24]
|
||||
}
|
||||
/// 接收端协议地址,仅支持IPv4
|
||||
pub fn target_protocol_addr(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[24..28]
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> ArpPacket<B> {
|
||||
/// 硬件类型 以太网类型为1
|
||||
pub fn set_hardware_type(&mut self, value: u16) {
|
||||
self.buffer.as_mut()[0..2].copy_from_slice(&value.to_be_bytes())
|
||||
}
|
||||
/// 上层协议类型,ipv4是0x0800
|
||||
pub fn set_protocol_type(&mut self, value: u16) {
|
||||
self.buffer.as_mut()[2..4].copy_from_slice(&value.to_be_bytes())
|
||||
}
|
||||
/// 如果是MAC地址 则长度为6
|
||||
pub fn set_hardware_size(&mut self, value: u8) {
|
||||
self.buffer.as_mut()[4] = value
|
||||
}
|
||||
/// 如果是IPv4 则长度为4
|
||||
pub fn set_protocol_size(&mut self, value: u8) {
|
||||
self.buffer.as_mut()[5] = value
|
||||
}
|
||||
/// 操作类型,请求和响应 1:ARP请求,2:ARP响应,3:RARP请求,4:RARP响应
|
||||
pub fn set_op_code(&mut self, value: u16) {
|
||||
self.buffer.as_mut()[6..8].copy_from_slice(&value.to_be_bytes())
|
||||
}
|
||||
/// 发送端硬件地址,仅支持以太网
|
||||
pub fn set_sender_hardware_addr(&mut self, buf: &[u8]) {
|
||||
self.buffer.as_mut()[8..14].copy_from_slice(buf)
|
||||
}
|
||||
/// 发送端协议地址,仅支持IPv4
|
||||
pub fn set_sender_protocol_addr(&mut self, buf: &[u8]) {
|
||||
self.buffer.as_mut()[14..18].copy_from_slice(buf)
|
||||
}
|
||||
/// 接收端硬件地址,仅支持以太网
|
||||
pub fn set_target_hardware_addr(&mut self, buf: &[u8]) {
|
||||
self.buffer.as_mut()[18..24].copy_from_slice(buf)
|
||||
}
|
||||
/// 接收端协议地址,仅支持IPv4
|
||||
pub fn set_target_protocol_addr(&mut self, buf: &[u8]) {
|
||||
self.buffer.as_mut()[24..28].copy_from_slice(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for ArpPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("ArpPacket")
|
||||
.field("hardware_type", &self.hardware_type())
|
||||
.field("protocol_type", &self.protocol_type())
|
||||
.field("hardware_size", &self.hardware_size())
|
||||
.field("protocol_size", &self.protocol_size())
|
||||
.field("op_code", &self.op_code())
|
||||
.field("sender_hardware_addr", &self.sender_hardware_addr())
|
||||
.field("sender_protocol_addr", &self.sender_protocol_addr())
|
||||
.field("target_hardware_addr", &self.target_hardware_addr())
|
||||
.field("target_protocol_addr", &self.target_protocol_addr())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod packet;
|
||||
pub mod protocol;
|
||||
@@ -0,0 +1,77 @@
|
||||
use crate::packet::ethernet::protocol::Protocol;
|
||||
use std::{fmt, io};
|
||||
|
||||
/// 以太网帧协议
|
||||
/// https://www.ietf.org/rfc/rfc894.txt
|
||||
/*
|
||||
0 6 12 14 (字节)
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 目的地址 | 源地址 | 类型 |
|
||||
*/
|
||||
pub struct EthernetPacket<B> {
|
||||
pub buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> EthernetPacket<B> {
|
||||
pub fn unchecked(buffer: B) -> EthernetPacket<B> {
|
||||
EthernetPacket { buffer }
|
||||
}
|
||||
|
||||
pub fn new(buffer: B) -> io::Result<EthernetPacket<B>> {
|
||||
let packet = EthernetPacket::unchecked(buffer);
|
||||
//头部固定14位
|
||||
if packet.buffer.as_ref().len() < 14 {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidData,format!("len={}", packet.buffer.as_ref().len())))?;
|
||||
}
|
||||
|
||||
Ok(packet)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> EthernetPacket<B> {
|
||||
/// 目的MAC地址
|
||||
pub fn destination(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[0..6]
|
||||
}
|
||||
/// 源MAC地址
|
||||
pub fn source(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[6..12]
|
||||
}
|
||||
/// 3层协议
|
||||
pub fn protocol(&self) -> Protocol {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[12..14].try_into().unwrap()).into()
|
||||
}
|
||||
/// 载荷
|
||||
pub fn payload(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[14..]
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> EthernetPacket<B> {
|
||||
pub fn set_destination(&mut self, value: &[u8]) {
|
||||
self.buffer.as_mut()[0..6].copy_from_slice(value);
|
||||
}
|
||||
|
||||
pub fn set_source(&mut self, value: &[u8]) {
|
||||
self.buffer.as_mut()[6..12].copy_from_slice(value);
|
||||
}
|
||||
|
||||
pub fn set_protocol(&mut self, value: Protocol) {
|
||||
let p: u16 = value.into();
|
||||
self.buffer.as_mut()[12..14].copy_from_slice(&p.to_be_bytes())
|
||||
}
|
||||
pub fn payload_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.buffer.as_mut()[14..]
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for EthernetPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("EthernetPacket")
|
||||
.field("destination", &self.destination())
|
||||
.field("source", &self.source())
|
||||
.field("protocol", &self.protocol())
|
||||
.field("payload", &self.payload())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/// 以太网帧协议
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||
pub enum Protocol {
|
||||
///
|
||||
Ipv4,
|
||||
|
||||
///
|
||||
Arp,
|
||||
|
||||
///
|
||||
WakeOnLan,
|
||||
|
||||
///
|
||||
Trill,
|
||||
|
||||
///
|
||||
DecNet,
|
||||
|
||||
///
|
||||
Rarp,
|
||||
|
||||
///
|
||||
AppleTalk,
|
||||
|
||||
///
|
||||
Aarp,
|
||||
|
||||
///
|
||||
Ipx,
|
||||
|
||||
///
|
||||
Qnx,
|
||||
|
||||
///
|
||||
Ipv6,
|
||||
|
||||
///
|
||||
FlowControl,
|
||||
|
||||
///
|
||||
CobraNet,
|
||||
|
||||
///
|
||||
Mpls,
|
||||
|
||||
///
|
||||
MplsMulticast,
|
||||
|
||||
///
|
||||
PppoeDiscovery,
|
||||
|
||||
///
|
||||
PppoeSession,
|
||||
|
||||
///
|
||||
Vlan,
|
||||
|
||||
///
|
||||
PBridge,
|
||||
|
||||
///
|
||||
Lldp,
|
||||
|
||||
///
|
||||
Ptp,
|
||||
|
||||
///
|
||||
Cfm,
|
||||
|
||||
///
|
||||
QinQ,
|
||||
|
||||
///
|
||||
Unknown(u16),
|
||||
}
|
||||
|
||||
impl From<u16> for Protocol {
|
||||
fn from(value: u16) -> Protocol {
|
||||
use self::Protocol::*;
|
||||
|
||||
match value {
|
||||
0x0800 => Ipv4,
|
||||
0x0806 => Arp,
|
||||
0x0842 => WakeOnLan,
|
||||
0x22f3 => Trill,
|
||||
0x6003 => DecNet,
|
||||
0x8035 => Rarp,
|
||||
0x809b => AppleTalk,
|
||||
0x80f3 => Aarp,
|
||||
0x8137 => Ipx,
|
||||
0x8204 => Qnx,
|
||||
0x86dd => Ipv6,
|
||||
0x8808 => FlowControl,
|
||||
0x8819 => CobraNet,
|
||||
0x8847 => Mpls,
|
||||
0x8848 => MplsMulticast,
|
||||
0x8863 => PppoeDiscovery,
|
||||
0x8864 => PppoeSession,
|
||||
0x8100 => Vlan,
|
||||
0x88a8 => PBridge,
|
||||
0x88cc => Lldp,
|
||||
0x88f7 => Ptp,
|
||||
0x8902 => Cfm,
|
||||
0x9100 => QinQ,
|
||||
n => Unknown(n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u16> for Protocol {
|
||||
fn into(self) -> u16 {
|
||||
use self::Protocol::*;
|
||||
|
||||
match self {
|
||||
Ipv4 => 0x0800,
|
||||
Arp => 0x0806,
|
||||
WakeOnLan => 0x0842,
|
||||
Trill => 0x22f3,
|
||||
DecNet => 0x6003,
|
||||
Rarp => 0x8035,
|
||||
AppleTalk => 0x809b,
|
||||
Aarp => 0x80f3,
|
||||
Ipx => 0x8137,
|
||||
Qnx => 0x8204,
|
||||
Ipv6 => 0x86dd,
|
||||
FlowControl => 0x8808,
|
||||
CobraNet => 0x8819,
|
||||
Mpls => 0x8847,
|
||||
MplsMulticast => 0x8848,
|
||||
PppoeDiscovery => 0x8863,
|
||||
PppoeSession => 0x8864,
|
||||
Vlan => 0x8100,
|
||||
PBridge => 0x88a8,
|
||||
Lldp => 0x88cc,
|
||||
Ptp => 0x88f7,
|
||||
Cfm => 0x8902,
|
||||
QinQ => 0x9100,
|
||||
Unknown(n) => n,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
use crate::packet::ethernet::protocol::Protocol;
|
||||
use std::io;
|
||||
|
||||
pub mod arp;
|
||||
pub mod ethernet;
|
||||
|
||||
const MAC: [u8; 6] = [0xf, 0xf, 0xf, 0xf, 0xe, 0x9];
|
||||
pub fn read_tap<W, R>(buf: &mut [u8], read_fn: R, write_fn: W) -> io::Result<usize>
|
||||
where
|
||||
W: Fn(&[u8]) -> io::Result<usize>,
|
||||
R: Fn(&mut [u8]) -> io::Result<usize>,
|
||||
{
|
||||
let mut eth_buf = [0; 65536];
|
||||
loop {
|
||||
let len = read_fn(&mut eth_buf)?;
|
||||
if len == 0{
|
||||
return Ok(len);
|
||||
}
|
||||
//处理arp包
|
||||
let mut ether = ethernet::packet::EthernetPacket::new(&mut eth_buf[..len])?;
|
||||
match ether.protocol() {
|
||||
Protocol::Ipv4 => {
|
||||
let len = ether.payload().len();
|
||||
if len > buf.len() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "short"));
|
||||
}
|
||||
buf[..len].copy_from_slice(ether.payload());
|
||||
return Ok(len);
|
||||
}
|
||||
Protocol::Arp => {
|
||||
let mut arp_packet = arp::packet::ArpPacket::unchecked(ether.payload_mut());
|
||||
let sender_h: [u8; 6] = arp_packet.sender_hardware_addr().try_into().unwrap();
|
||||
let sender_p: [u8; 4] = arp_packet.sender_protocol_addr().try_into().unwrap();
|
||||
let target_p: [u8; 4] = arp_packet.target_protocol_addr().try_into().unwrap();
|
||||
if target_p == [0, 0, 0, 0] || sender_p == [0, 0, 0, 0] || target_p == sender_p {
|
||||
continue;
|
||||
}
|
||||
if arp_packet.op_code() == 1 {
|
||||
//回复一个默认的MAC
|
||||
arp_packet.set_op_code(2);
|
||||
arp_packet.set_target_hardware_addr(&sender_h);
|
||||
arp_packet.set_target_protocol_addr(&sender_p);
|
||||
arp_packet.set_sender_protocol_addr(&target_p);
|
||||
arp_packet.set_sender_hardware_addr(&MAC);
|
||||
ether.set_destination(&sender_h);
|
||||
ether.set_source(&MAC);
|
||||
write_fn(ether.buffer)?;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
//忽略这些数据
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn write_tap<W>(buf: &[u8], write_fn: W, mac: &[u8; 6]) -> io::Result<usize>
|
||||
where
|
||||
W: Fn(&[u8]) -> io::Result<usize>,
|
||||
{
|
||||
// 封装二层数据
|
||||
let mut ether = ethernet::packet::EthernetPacket::unchecked(vec![0; 14 + buf.len()]);
|
||||
ether.set_source(&MAC);
|
||||
ether.set_destination(mac);
|
||||
ether.set_protocol(Protocol::Ipv4);
|
||||
ether.payload_mut().copy_from_slice(buf);
|
||||
write_fn(ðer.buffer)
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
use std::io;
|
||||
use std::os::fd::{AsRawFd, IntoRawFd, RawFd};
|
||||
|
||||
pub struct Fd(pub RawFd);
|
||||
|
||||
impl Fd {
|
||||
pub fn new(value: RawFd) -> io::Result<Self> {
|
||||
if value < 0 {
|
||||
return Err(io::Error::from(io::ErrorKind::InvalidInput));
|
||||
}
|
||||
Ok(Fd(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl Fd {
|
||||
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let amount = libc::read(self.0, buf.as_mut_ptr() as *mut _, buf.len());
|
||||
|
||||
if amount < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(amount as usize)
|
||||
}
|
||||
}
|
||||
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let amount = libc::write(self.0, buf.as_ptr() as *const _, buf.len());
|
||||
|
||||
if amount < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(amount as usize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Fd {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for Fd {
|
||||
fn into_raw_fd(mut self) -> RawFd {
|
||||
let fd = self.0;
|
||||
self.0 = -1;
|
||||
fd
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Fd {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if self.0 >= 0 {
|
||||
libc::close(self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
mod fd;
|
||||
|
||||
pub use fd::Fd;
|
||||
use std::process::Output;
|
||||
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
||||
mod sockaddr;
|
||||
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
||||
pub use sockaddr::SockAddr;
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
||||
pub fn exe_cmd(cmd: &str) -> std::io::Result<Output> {
|
||||
use std::io;
|
||||
use std::process::Command;
|
||||
println!("exe cmd: {}", cmd);
|
||||
let out = Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(cmd)
|
||||
.output()
|
||||
.expect("sh exec error!");
|
||||
if !out.status.success() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("cmd={},out={:?}", cmd, out),
|
||||
));
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
@@ -1,46 +1,17 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::mem;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::ptr;
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
use libc::c_uchar;
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use libc::c_ushort;
|
||||
|
||||
use libc::AF_INET as _AF_INET;
|
||||
use libc::{in_addr, sockaddr, sockaddr_in};
|
||||
use std::{io, mem, net::Ipv4Addr, ptr};
|
||||
|
||||
use crate::error::*;
|
||||
use io::Result;
|
||||
|
||||
/// A wrapper for `sockaddr_in`.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SockAddr(sockaddr_in);
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
const AF_INET: c_ushort = _AF_INET as c_ushort;
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
const AF_INET: c_uchar = _AF_INET as c_uchar;
|
||||
|
||||
impl SockAddr {
|
||||
/// Create a new `SockAddr` from a generic `sockaddr`.
|
||||
pub fn new(value: &sockaddr) -> Result<Self> {
|
||||
if value.sa_family != AF_INET {
|
||||
return Err(Error::InvalidAddress);
|
||||
if value.sa_family != libc::AF_INET as libc::sa_family_t {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "invalid address"));
|
||||
}
|
||||
|
||||
unsafe { Self::unchecked(value) }
|
||||
@@ -64,7 +35,7 @@ impl From<Ipv4Addr> for SockAddr {
|
||||
let octets = ip.octets();
|
||||
let mut addr = unsafe { mem::zeroed::<sockaddr_in>() };
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_family = libc::AF_INET as libc::sa_family_t;
|
||||
addr.sin_port = 0;
|
||||
addr.sin_addr = in_addr {
|
||||
s_addr: u32::from_ne_bytes(octets),
|
||||
@@ -0,0 +1,91 @@
|
||||
use crate::device::IFace;
|
||||
use crate::windows::{tap, tun};
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
pub enum Device {
|
||||
Tap(tap::Device),
|
||||
Tun(tun::Device),
|
||||
}
|
||||
|
||||
impl Device {
|
||||
pub fn new(name: String, tap: bool) -> io::Result<Self> {
|
||||
if tap {
|
||||
Ok(Device::Tap(tap::Device::new(name)?))
|
||||
} else {
|
||||
Ok(Device::Tun(tun::Device::new(name)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IFace for Device {
|
||||
fn version(&self) -> io::Result<String> {
|
||||
match self {
|
||||
Device::Tap(dev) => dev.version(),
|
||||
Device::Tun(dev) => dev.version(),
|
||||
}
|
||||
}
|
||||
|
||||
fn name(&self) -> io::Result<String> {
|
||||
match self {
|
||||
Device::Tap(dev) => dev.name(),
|
||||
Device::Tun(dev) => dev.name(),
|
||||
}
|
||||
}
|
||||
|
||||
fn shutdown(&self) -> io::Result<()> {
|
||||
match self {
|
||||
Device::Tap(dev) => dev.shutdown(),
|
||||
Device::Tun(dev) => dev.shutdown(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_ip(&self, address: Ipv4Addr, mask: Ipv4Addr) -> io::Result<()> {
|
||||
match self {
|
||||
Device::Tap(dev) => dev.set_ip(address, mask),
|
||||
Device::Tun(dev) => dev.set_ip(address, mask),
|
||||
}
|
||||
}
|
||||
|
||||
fn mtu(&self) -> io::Result<u32> {
|
||||
match self {
|
||||
Device::Tap(dev) => dev.mtu(),
|
||||
Device::Tun(dev) => dev.mtu(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_mtu(&self, value: u32) -> io::Result<()> {
|
||||
match self {
|
||||
Device::Tap(dev) => dev.set_mtu(value),
|
||||
Device::Tun(dev) => dev.set_mtu(value),
|
||||
}
|
||||
}
|
||||
|
||||
fn add_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr, metric: u16) -> io::Result<()> {
|
||||
match self {
|
||||
Device::Tap(dev) => dev.add_route(dest, netmask, metric),
|
||||
Device::Tun(dev) => dev.add_route(dest, netmask, metric),
|
||||
}
|
||||
}
|
||||
|
||||
fn delete_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> {
|
||||
match self {
|
||||
Device::Tap(dev) => dev.delete_route(dest, netmask),
|
||||
Device::Tun(dev) => dev.delete_route(dest, netmask),
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
match self {
|
||||
Device::Tap(dev) => dev.read(buf),
|
||||
Device::Tun(dev) => dev.read(buf),
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
match self {
|
||||
Device::Tap(dev) => dev.write(buf),
|
||||
Device::Tun(dev) => dev.write(buf),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,502 @@
|
||||
// Many things will be used in the future
|
||||
#![allow(unused)]
|
||||
|
||||
//! Module holding safe wrappers over winapi functions
|
||||
|
||||
use winapi::shared::basetsd::*;
|
||||
use winapi::shared::guiddef::GUID;
|
||||
use winapi::shared::ifdef::*;
|
||||
use winapi::shared::minwindef::*;
|
||||
use winapi::shared::netioapi::*;
|
||||
use winapi::shared::winerror::*;
|
||||
|
||||
use winapi::um::combaseapi::*;
|
||||
use winapi::um::errhandlingapi::*;
|
||||
use winapi::um::fileapi::*;
|
||||
use winapi::um::handleapi::*;
|
||||
use winapi::um::ioapiset::*;
|
||||
use winapi::um::setupapi::*;
|
||||
use winapi::um::synchapi::*;
|
||||
use winapi::um::winioctl::*;
|
||||
use winapi::um::winnt::*;
|
||||
use winapi::um::winreg::*;
|
||||
|
||||
use std::error::Error;
|
||||
use std::{io, mem, ptr};
|
||||
use winapi::um::minwinbase::OVERLAPPED_u;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[allow(non_snake_case)]
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
/// Custom type to handle variable size SP_DRVINFO_DETAIL_DATA_W
|
||||
pub struct SP_DRVINFO_DETAIL_DATA_W2 {
|
||||
pub cbSize: DWORD,
|
||||
pub InfDate: FILETIME,
|
||||
pub CompatIDsOffset: DWORD,
|
||||
pub CompatIDsLength: DWORD,
|
||||
pub Reserved: ULONG_PTR,
|
||||
pub SectionName: [WCHAR; 256],
|
||||
pub InfFileName: [WCHAR; 260],
|
||||
pub DrvDescription: [WCHAR; 256],
|
||||
pub HardwareID: [WCHAR; 512],
|
||||
}
|
||||
|
||||
pub fn string_from_guid(guid: &GUID) -> io::Result<Vec<WCHAR>> {
|
||||
// GUID_STRING_CHARACTERS + 1
|
||||
let mut string = vec![0; 39];
|
||||
|
||||
match unsafe { StringFromGUID2(guid, string.as_mut_ptr(), string.len() as _) } {
|
||||
0 => Err(io::Error::new(io::ErrorKind::Other, "Insufficent buffer")),
|
||||
_ => Ok(string),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn alias_to_luid(alias: &[WCHAR]) -> io::Result<NET_LUID> {
|
||||
let mut luid = unsafe { mem::zeroed() };
|
||||
|
||||
match unsafe { ConvertInterfaceAliasToLuid(alias.as_ptr(), &mut luid) } {
|
||||
0 => Ok(luid),
|
||||
err => Err(io::Error::from_raw_os_error(err as _)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn luid_to_index(luid: &NET_LUID) -> io::Result<NET_IFINDEX> {
|
||||
let mut index = 0;
|
||||
|
||||
match unsafe { ConvertInterfaceLuidToIndex(luid, &mut index) } {
|
||||
0 => Ok(index),
|
||||
err => Err(io::Error::from_raw_os_error(err as _)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn luid_to_guid(luid: &NET_LUID) -> io::Result<GUID> {
|
||||
let mut guid = unsafe { mem::zeroed() };
|
||||
|
||||
match unsafe { ConvertInterfaceLuidToGuid(luid, &mut guid) } {
|
||||
0 => Ok(guid),
|
||||
err => Err(io::Error::from_raw_os_error(err as _)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn luid_to_alias(luid: &NET_LUID) -> io::Result<Vec<WCHAR>> {
|
||||
// IF_MAX_STRING_SIZE + 1
|
||||
let mut alias = vec![0; 257];
|
||||
|
||||
match unsafe { ConvertInterfaceLuidToAlias(luid, alias.as_mut_ptr(), alias.len()) } {
|
||||
0 => Ok(alias),
|
||||
err => Err(io::Error::from_raw_os_error(err as _)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn close_handle(handle: HANDLE) -> io::Result<()> {
|
||||
match unsafe { CloseHandle(handle) } {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_file(
|
||||
file_name: &[WCHAR],
|
||||
desired_access: DWORD,
|
||||
share_mode: DWORD,
|
||||
creation_disposition: DWORD,
|
||||
flags_and_attributes: DWORD,
|
||||
) -> io::Result<HANDLE> {
|
||||
match unsafe {
|
||||
CreateFileW(
|
||||
file_name.as_ptr(),
|
||||
desired_access,
|
||||
share_mode,
|
||||
ptr::null_mut(),
|
||||
creation_disposition,
|
||||
flags_and_attributes,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
} {
|
||||
INVALID_HANDLE_VALUE => Err(io::Error::last_os_error()),
|
||||
handle => Ok(handle),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_file(handle: HANDLE, buffer: &mut [u8]) -> io::Result<DWORD> {
|
||||
let mut ret = 0;
|
||||
//https://www.cnblogs.com/linyilong3/archive/2012/05/03/2480451.html
|
||||
unsafe {
|
||||
let mut ip_overlapped = winapi::um::minwinbase::OVERLAPPED {
|
||||
Internal: 0,
|
||||
InternalHigh: 0,
|
||||
u: Default::default(),
|
||||
hEvent: ptr::null_mut(),
|
||||
};
|
||||
if 0 == ReadFile(
|
||||
handle,
|
||||
buffer.as_mut_ptr() as _,
|
||||
buffer.len() as _,
|
||||
&mut ret,
|
||||
&mut ip_overlapped,
|
||||
) {
|
||||
let e = io::Error::last_os_error();
|
||||
if e.raw_os_error().unwrap_or(0) == ERROR_IO_PENDING as _ {
|
||||
if 0 == GetOverlappedResult(handle, &mut ip_overlapped, &mut ret, 1) {
|
||||
return Err(e);
|
||||
}
|
||||
} else {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_file(handle: HANDLE, buffer: &[u8]) -> io::Result<DWORD> {
|
||||
let mut ret = 0;
|
||||
let mut ip_overlapped = winapi::um::minwinbase::OVERLAPPED {
|
||||
Internal: 0,
|
||||
InternalHigh: 0,
|
||||
u: Default::default(),
|
||||
hEvent: ptr::null_mut(),
|
||||
};
|
||||
unsafe {
|
||||
if 0 == WriteFile(
|
||||
handle,
|
||||
buffer.as_ptr() as _,
|
||||
buffer.len() as _,
|
||||
&mut ret,
|
||||
&mut ip_overlapped,
|
||||
) {
|
||||
let e = io::Error::last_os_error();
|
||||
if e.raw_os_error().unwrap_or(0) == ERROR_IO_PENDING as _ {
|
||||
if 0 == GetOverlappedResult(handle, &mut ip_overlapped, &mut ret, 1) {
|
||||
return Err(e);
|
||||
}
|
||||
} else {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_device_info_list(guid: &GUID) -> io::Result<HDEVINFO> {
|
||||
match unsafe { SetupDiCreateDeviceInfoList(guid, ptr::null_mut()) } {
|
||||
INVALID_HANDLE_VALUE => Err(io::Error::last_os_error()),
|
||||
devinfo => Ok(devinfo),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_class_devs(guid: &GUID, flags: DWORD) -> io::Result<HDEVINFO> {
|
||||
match unsafe { SetupDiGetClassDevsW(guid, ptr::null(), ptr::null_mut(), flags) } {
|
||||
INVALID_HANDLE_VALUE => Err(io::Error::last_os_error()),
|
||||
devinfo => Ok(devinfo),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn destroy_device_info_list(devinfo: HDEVINFO) -> io::Result<()> {
|
||||
match unsafe { SetupDiDestroyDeviceInfoList(devinfo) } {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn class_name_from_guid(guid: &GUID) -> io::Result<Vec<WCHAR>> {
|
||||
let mut class_name = vec![0; 32];
|
||||
|
||||
match unsafe {
|
||||
SetupDiClassNameFromGuidW(
|
||||
guid,
|
||||
class_name.as_mut_ptr(),
|
||||
class_name.len() as _,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
} {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(class_name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_device_info(
|
||||
devinfo: HDEVINFO,
|
||||
device_name: &[WCHAR],
|
||||
guid: &GUID,
|
||||
device_description: &[WCHAR],
|
||||
creation_flags: DWORD,
|
||||
) -> io::Result<SP_DEVINFO_DATA> {
|
||||
let mut devinfo_data: SP_DEVINFO_DATA = unsafe { mem::zeroed() };
|
||||
devinfo_data.cbSize = mem::size_of_val(&devinfo_data) as _;
|
||||
|
||||
match unsafe {
|
||||
SetupDiCreateDeviceInfoW(
|
||||
devinfo,
|
||||
device_name.as_ptr(),
|
||||
guid,
|
||||
device_description.as_ptr(),
|
||||
ptr::null_mut(),
|
||||
creation_flags,
|
||||
&mut devinfo_data,
|
||||
)
|
||||
} {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(devinfo_data),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_selected_device(devinfo: HDEVINFO, devinfo_data: &SP_DEVINFO_DATA) -> io::Result<()> {
|
||||
match unsafe { SetupDiSetSelectedDevice(devinfo, devinfo_data as *const _ as _) } {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_device_registry_property(
|
||||
devinfo: HDEVINFO,
|
||||
devinfo_data: &SP_DEVINFO_DATA,
|
||||
property: DWORD,
|
||||
value: &[WCHAR],
|
||||
) -> io::Result<()> {
|
||||
match unsafe {
|
||||
SetupDiSetDeviceRegistryPropertyW(
|
||||
devinfo,
|
||||
devinfo_data as *const _ as _,
|
||||
property,
|
||||
value.as_ptr() as _,
|
||||
(value.len() * 2) as _,
|
||||
)
|
||||
} {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_device_registry_property(
|
||||
devinfo: HDEVINFO,
|
||||
devinfo_data: &SP_DEVINFO_DATA,
|
||||
property: DWORD,
|
||||
) -> io::Result<Vec<WCHAR>> {
|
||||
let mut value = vec![0; 32];
|
||||
|
||||
match unsafe {
|
||||
SetupDiGetDeviceRegistryPropertyW(
|
||||
devinfo,
|
||||
devinfo_data as *const _ as _,
|
||||
property,
|
||||
ptr::null_mut(),
|
||||
value.as_mut_ptr() as _,
|
||||
(value.len() * 2) as _,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
} {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(value),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_driver_info_list(
|
||||
devinfo: HDEVINFO,
|
||||
devinfo_data: &SP_DEVINFO_DATA,
|
||||
driver_type: DWORD,
|
||||
) -> io::Result<()> {
|
||||
match unsafe { SetupDiBuildDriverInfoList(devinfo, devinfo_data as *const _ as _, driver_type) }
|
||||
{
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn destroy_driver_info_list(
|
||||
devinfo: HDEVINFO,
|
||||
devinfo_data: &SP_DEVINFO_DATA,
|
||||
driver_type: DWORD,
|
||||
) -> io::Result<()> {
|
||||
match unsafe {
|
||||
SetupDiDestroyDriverInfoList(devinfo, devinfo_data as *const _ as _, driver_type)
|
||||
} {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_driver_info_detail(
|
||||
devinfo: HDEVINFO,
|
||||
devinfo_data: &SP_DEVINFO_DATA,
|
||||
drvinfo_data: &SP_DRVINFO_DATA_W,
|
||||
) -> io::Result<SP_DRVINFO_DETAIL_DATA_W2> {
|
||||
let mut drvinfo_detail: SP_DRVINFO_DETAIL_DATA_W2 = unsafe { mem::zeroed() };
|
||||
drvinfo_detail.cbSize = mem::size_of::<SP_DRVINFO_DETAIL_DATA_W>() as _;
|
||||
|
||||
match unsafe {
|
||||
SetupDiGetDriverInfoDetailW(
|
||||
devinfo,
|
||||
devinfo_data as *const _ as _,
|
||||
drvinfo_data as *const _ as _,
|
||||
&mut drvinfo_detail as *mut _ as _,
|
||||
mem::size_of_val(&drvinfo_detail) as _,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
} {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(drvinfo_detail),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_selected_driver(
|
||||
devinfo: HDEVINFO,
|
||||
devinfo_data: &SP_DEVINFO_DATA,
|
||||
drvinfo_data: &SP_DRVINFO_DATA_W,
|
||||
) -> io::Result<()> {
|
||||
match unsafe {
|
||||
SetupDiSetSelectedDriverW(
|
||||
devinfo,
|
||||
devinfo_data as *const _ as _,
|
||||
drvinfo_data as *const _ as _,
|
||||
)
|
||||
} {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_class_install_params(
|
||||
devinfo: HDEVINFO,
|
||||
devinfo_data: &SP_DEVINFO_DATA,
|
||||
params: &impl Copy,
|
||||
) -> io::Result<()> {
|
||||
match unsafe {
|
||||
SetupDiSetClassInstallParamsW(
|
||||
devinfo,
|
||||
devinfo_data as *const _ as _,
|
||||
params as *const _ as _,
|
||||
mem::size_of_val(params) as _,
|
||||
)
|
||||
} {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_class_installer(
|
||||
devinfo: HDEVINFO,
|
||||
devinfo_data: &SP_DEVINFO_DATA,
|
||||
install_function: DI_FUNCTION,
|
||||
) -> io::Result<()> {
|
||||
match unsafe {
|
||||
SetupDiCallClassInstaller(install_function, devinfo, devinfo_data as *const _ as _)
|
||||
} {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_dev_reg_key(
|
||||
devinfo: HDEVINFO,
|
||||
devinfo_data: &SP_DEVINFO_DATA,
|
||||
scope: DWORD,
|
||||
hw_profile: DWORD,
|
||||
key_type: DWORD,
|
||||
sam_desired: REGSAM,
|
||||
) -> io::Result<HKEY> {
|
||||
const INVALID_KEY_VALUE: HKEY = INVALID_HANDLE_VALUE as _;
|
||||
|
||||
match unsafe {
|
||||
SetupDiOpenDevRegKey(
|
||||
devinfo,
|
||||
devinfo_data as *const _ as _,
|
||||
scope,
|
||||
hw_profile,
|
||||
key_type,
|
||||
sam_desired,
|
||||
)
|
||||
} {
|
||||
INVALID_KEY_VALUE => Err(io::Error::last_os_error()),
|
||||
key => Ok(key),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notify_change_key_value(
|
||||
key: HKEY,
|
||||
watch_subtree: BOOL,
|
||||
notify_filter: DWORD,
|
||||
milliseconds: DWORD,
|
||||
) -> io::Result<()> {
|
||||
let event = match unsafe { CreateEventW(ptr::null_mut(), FALSE, FALSE, ptr::null()) } {
|
||||
INVALID_HANDLE_VALUE => Err(io::Error::last_os_error()),
|
||||
event => Ok(event),
|
||||
}?;
|
||||
|
||||
match unsafe { RegNotifyChangeKeyValue(key, watch_subtree, notify_filter, event, TRUE) } {
|
||||
0 => Ok(()),
|
||||
err => Err(io::Error::from_raw_os_error(err)),
|
||||
}?;
|
||||
|
||||
match unsafe { WaitForSingleObject(event, milliseconds) } {
|
||||
0 => Ok(()),
|
||||
0x102 => Err(io::Error::new(
|
||||
io::ErrorKind::TimedOut,
|
||||
"Registry timed out",
|
||||
)),
|
||||
_ => Err(io::Error::last_os_error()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enum_driver_info(
|
||||
devinfo: HDEVINFO,
|
||||
devinfo_data: &SP_DEVINFO_DATA,
|
||||
driver_type: DWORD,
|
||||
member_index: DWORD,
|
||||
) -> Option<io::Result<SP_DRVINFO_DATA_W>> {
|
||||
let mut drvinfo_data: SP_DRVINFO_DATA_W = unsafe { mem::zeroed() };
|
||||
drvinfo_data.cbSize = mem::size_of_val(&drvinfo_data) as _;
|
||||
|
||||
match unsafe {
|
||||
SetupDiEnumDriverInfoW(
|
||||
devinfo,
|
||||
devinfo_data as *const _ as _,
|
||||
driver_type,
|
||||
member_index,
|
||||
&mut drvinfo_data,
|
||||
)
|
||||
} {
|
||||
0 if unsafe { GetLastError() == ERROR_NO_MORE_ITEMS } => None,
|
||||
0 => Some(Err(io::Error::last_os_error())),
|
||||
_ => Some(Ok(drvinfo_data)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enum_device_info(
|
||||
devinfo: HDEVINFO,
|
||||
member_index: DWORD,
|
||||
) -> Option<io::Result<SP_DEVINFO_DATA>> {
|
||||
let mut devinfo_data: SP_DEVINFO_DATA = unsafe { mem::zeroed() };
|
||||
devinfo_data.cbSize = mem::size_of_val(&devinfo_data) as _;
|
||||
|
||||
match unsafe { SetupDiEnumDeviceInfo(devinfo, member_index, &mut devinfo_data) } {
|
||||
0 if unsafe { GetLastError() == ERROR_NO_MORE_ITEMS } => None,
|
||||
0 => Some(Err(io::Error::last_os_error())),
|
||||
_ => Some(Ok(devinfo_data)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn device_io_control(
|
||||
handle: HANDLE,
|
||||
io_control_code: DWORD,
|
||||
in_buffer: &impl Copy,
|
||||
out_buffer: &mut impl Copy,
|
||||
) -> io::Result<()> {
|
||||
let mut junk = 0;
|
||||
|
||||
match unsafe {
|
||||
DeviceIoControl(
|
||||
handle,
|
||||
io_control_code,
|
||||
in_buffer as *const _ as _,
|
||||
mem::size_of_val(in_buffer) as _,
|
||||
out_buffer as *mut _ as _,
|
||||
mem::size_of_val(out_buffer) as _,
|
||||
&mut junk,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
} {
|
||||
0 => Err(io::Error::last_os_error()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
use std::io;
|
||||
use std::os::windows::process::CommandExt;
|
||||
use winapi::shared::minwindef::DWORD;
|
||||
use winapi::um::winbase::CREATE_NO_WINDOW;
|
||||
|
||||
mod device;
|
||||
mod ffi;
|
||||
mod netsh;
|
||||
mod route;
|
||||
mod tap;
|
||||
mod tun;
|
||||
pub use device::Device;
|
||||
|
||||
/// Encode a string as a utf16 buffer
|
||||
pub fn encode_utf16(string: &str) -> Vec<u16> {
|
||||
use std::iter::once;
|
||||
string.encode_utf16().chain(once(0)).collect()
|
||||
}
|
||||
|
||||
pub fn decode_utf16(string: &[u16]) -> String {
|
||||
let end = string.iter().position(|b| *b == 0).unwrap_or(string.len());
|
||||
String::from_utf16_lossy(&string[..end])
|
||||
}
|
||||
|
||||
pub const fn ctl_code(device_type: DWORD, function: DWORD, method: DWORD, access: DWORD) -> DWORD {
|
||||
(device_type << 16) | (access << 14) | (function << 2) | method
|
||||
}
|
||||
|
||||
pub fn exe_cmd(cmd: &str) -> io::Result<()> {
|
||||
println!("exe cmd: {}", cmd);
|
||||
let out = std::process::Command::new("cmd")
|
||||
.creation_flags(CREATE_NO_WINDOW)
|
||||
.arg("/C")
|
||||
.arg(&cmd)
|
||||
.output()?;
|
||||
if !out.status.success() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("cmd={},out={:?}", cmd, String::from_utf8(out.stderr)),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
use crate::windows::exe_cmd;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::{io, process};
|
||||
|
||||
/// 设置网卡名称
|
||||
pub fn set_interface_name(old_name: &str, new_name: &str) -> io::Result<()> {
|
||||
let cmd = format!(
|
||||
" netsh interface set interface name={:?} newname={:?}",
|
||||
old_name, new_name
|
||||
);
|
||||
exe_cmd(&cmd)
|
||||
}
|
||||
/// 删除缓存
|
||||
pub fn delete_cache() -> io::Result<()> {
|
||||
//清除缓存
|
||||
let cmd = "netsh interface ip delete destinationcache";
|
||||
exe_cmd(cmd)
|
||||
}
|
||||
|
||||
/// 设置网卡ip
|
||||
pub fn set_interface_ip(index: u32, address: &Ipv4Addr, netmask: &Ipv4Addr) -> io::Result<()> {
|
||||
let cmd = format!(
|
||||
"netsh interface ip set address {} static {:?} {:?} ",
|
||||
index, address, netmask,
|
||||
);
|
||||
exe_cmd(&cmd)
|
||||
}
|
||||
|
||||
pub fn set_interface_mtu(index: u32, mtu: u32) -> io::Result<()> {
|
||||
let cmd = format!(
|
||||
"netsh interface ipv4 set subinterface {} mtu={} store=persistent",
|
||||
index, mtu
|
||||
);
|
||||
exe_cmd(&cmd)
|
||||
}
|
||||
pub fn set_interface_metric(index: u32, metric: u16) -> io::Result<()> {
|
||||
let cmd = format!(
|
||||
"netsh interface ip set interface {} metric={}",
|
||||
index, metric
|
||||
);
|
||||
exe_cmd(&cmd)
|
||||
}
|
||||
/// 禁用ipv6
|
||||
pub fn disabled_ipv6(index: u32) -> io::Result<()> {
|
||||
let cmd = format!("netsh interface ipv6 set interface {} disabled", index);
|
||||
exe_cmd(&cmd)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::windows::exe_cmd;
|
||||
|
||||
/// 添加路由
|
||||
pub fn add_route(
|
||||
index: u32,
|
||||
dest: Ipv4Addr,
|
||||
netmask: Ipv4Addr,
|
||||
gateway: Ipv4Addr,
|
||||
metric: u16,
|
||||
) -> io::Result<()> {
|
||||
let cmd = format!(
|
||||
"route add {:?} mask {:?} {:?} metric {} if {}",
|
||||
dest, netmask, gateway, metric, index
|
||||
);
|
||||
exe_cmd(&cmd)
|
||||
}
|
||||
|
||||
/// 删除路由
|
||||
pub fn delete_route(
|
||||
index: u32,
|
||||
dest: Ipv4Addr,
|
||||
netmask: Ipv4Addr,
|
||||
gateway: Ipv4Addr,
|
||||
) -> io::Result<()> {
|
||||
let cmd = format!(
|
||||
"route delete {:?} mask {:?} {:?} if {}",
|
||||
dest, netmask, gateway, index
|
||||
);
|
||||
exe_cmd(&cmd)
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
use winapi::shared::ifdef::NET_LUID;
|
||||
use winapi::shared::minwindef::DWORD;
|
||||
use winapi::um::fileapi::OPEN_EXISTING;
|
||||
use winapi::um::winbase::FILE_FLAG_OVERLAPPED;
|
||||
use winapi::um::winioctl::{FILE_ANY_ACCESS, FILE_DEVICE_UNKNOWN, METHOD_BUFFERED};
|
||||
use winapi::um::winnt::{
|
||||
FILE_ATTRIBUTE_SYSTEM, FILE_SHARE_READ, FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE, HANDLE,
|
||||
};
|
||||
|
||||
use crate::device::IFace;
|
||||
use crate::packet;
|
||||
use crate::packet::ethernet::protocol::Protocol;
|
||||
use crate::packet::{arp, ethernet};
|
||||
use crate::windows::{ctl_code, decode_utf16, encode_utf16, ffi, netsh, route};
|
||||
|
||||
/* Present in 8.1 */
|
||||
const TAP_WIN_IOCTL_GET_MAC: DWORD =
|
||||
ctl_code(FILE_DEVICE_UNKNOWN, 1, METHOD_BUFFERED, FILE_ANY_ACCESS);
|
||||
const TAP_WIN_IOCTL_GET_VERSION: DWORD =
|
||||
ctl_code(FILE_DEVICE_UNKNOWN, 2, METHOD_BUFFERED, FILE_ANY_ACCESS);
|
||||
const TAP_WIN_IOCTL_GET_MTU: DWORD =
|
||||
ctl_code(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_ANY_ACCESS);
|
||||
const TAP_WIN_IOCTL_GET_INFO: DWORD =
|
||||
ctl_code(FILE_DEVICE_UNKNOWN, 4, METHOD_BUFFERED, FILE_ANY_ACCESS);
|
||||
const TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT: DWORD =
|
||||
ctl_code(FILE_DEVICE_UNKNOWN, 5, METHOD_BUFFERED, FILE_ANY_ACCESS);
|
||||
const TAP_WIN_IOCTL_SET_MEDIA_STATUS: DWORD =
|
||||
ctl_code(FILE_DEVICE_UNKNOWN, 6, METHOD_BUFFERED, FILE_ANY_ACCESS);
|
||||
const TAP_WIN_IOCTL_CONFIG_DHCP_MASQ: DWORD =
|
||||
ctl_code(FILE_DEVICE_UNKNOWN, 7, METHOD_BUFFERED, FILE_ANY_ACCESS);
|
||||
const TAP_WIN_IOCTL_GET_LOG_LINE: DWORD =
|
||||
ctl_code(FILE_DEVICE_UNKNOWN, 8, METHOD_BUFFERED, FILE_ANY_ACCESS);
|
||||
const TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT: DWORD =
|
||||
ctl_code(FILE_DEVICE_UNKNOWN, 9, METHOD_BUFFERED, FILE_ANY_ACCESS);
|
||||
/* Added in 8.2 */
|
||||
/* obsoletes TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT */
|
||||
const TAP_WIN_IOCTL_CONFIG_TUN: DWORD =
|
||||
ctl_code(FILE_DEVICE_UNKNOWN, 10, METHOD_BUFFERED, FILE_ANY_ACCESS);
|
||||
|
||||
pub struct Device {
|
||||
handle: HANDLE,
|
||||
index: u32,
|
||||
luid: NET_LUID,
|
||||
mac: [u8; 6],
|
||||
}
|
||||
|
||||
unsafe impl Send for Device {}
|
||||
|
||||
unsafe impl Sync for Device {}
|
||||
|
||||
impl Device {
|
||||
/// 打开设备,设置为TUN模式,激活网卡
|
||||
pub fn new(name: String) -> io::Result<Self> {
|
||||
let luid = ffi::alias_to_luid(&encode_utf16(&name)).map_err(|e| {
|
||||
io::Error::new(e.kind(), format!("alias_to_luid name={},err={:?}", name, e))
|
||||
})?;
|
||||
let guid = ffi::luid_to_guid(&luid)
|
||||
.and_then(|guid| ffi::string_from_guid(&guid))
|
||||
.map_err(|e| {
|
||||
io::Error::new(e.kind(), format!("luid_to_guid name={},err={:?}", name, e))
|
||||
})?;
|
||||
let path = format!(r"\\.\Global\{}.tap", decode_utf16(&guid));
|
||||
let handle = ffi::create_file(
|
||||
&encode_utf16(&path),
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
|
||||
)
|
||||
.map_err(|e| io::Error::new(e.kind(), format!("tap name={},err={:?}", name, e)))?;
|
||||
|
||||
// ep保存tun网卡的IP地址和掩码
|
||||
// let mut ep = [0;3];
|
||||
// ep[0] = Ipv4Addr::new(10,26,0,11).into();
|
||||
// ep[2] = Ipv4Addr::new(255,255,255,0).into();;
|
||||
// ep[1] = ep[0] & ep[2];
|
||||
// //tun模式收不到ipv4包,原因未知 https://github.com/OpenVPN/tap-windows6/issues/111
|
||||
// ffi::device_io_control(handle, TAP_WIN_IOCTL_CONFIG_TUN, &ep, &mut ()).map_err(
|
||||
// |e| {
|
||||
// io::Error::new(
|
||||
// e.kind(),
|
||||
// format!("TAP_WIN_IOCTL_CONFIG_TUN name={},err={:?}", name_str, e),
|
||||
// )
|
||||
// },
|
||||
// )?;
|
||||
let mut mac = [0u8; 6];
|
||||
ffi::device_io_control(handle, TAP_WIN_IOCTL_GET_MAC, &(), &mut mac)
|
||||
.map_err(|e| {
|
||||
io::Error::new(
|
||||
e.kind(),
|
||||
format!("TAP_WIN_IOCTL_CONFIG_TUN name={},err={:?}", name, e),
|
||||
)
|
||||
})
|
||||
.map_err(|e| io::Error::new(e.kind(), format!("TAP_WIN_IOCTL_GET_MAC,err={:?}", e)))?;
|
||||
let index = ffi::luid_to_index(&luid).map(|index| index as u32)?;
|
||||
// 设置网卡跃点
|
||||
if let Err(e) = netsh::set_interface_metric(index, 0) {
|
||||
log::warn!("{:?}",e);
|
||||
}
|
||||
let device = Self {
|
||||
handle,
|
||||
index,
|
||||
luid,
|
||||
mac,
|
||||
};
|
||||
device.enabled(true)?;
|
||||
Ok(device)
|
||||
}
|
||||
fn write_tap(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
ffi::write_file(self.handle, buf).map(|res| res as _)
|
||||
}
|
||||
fn enabled(&self, value: bool) -> io::Result<()> {
|
||||
let status: u32 = if value { 1 } else { 0 };
|
||||
ffi::device_io_control(
|
||||
self.handle,
|
||||
TAP_WIN_IOCTL_SET_MEDIA_STATUS,
|
||||
&status,
|
||||
&mut (),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const MAC: [u8; 6] = [0xf, 0xf, 0xf, 0xf, 0xe, 0x9];
|
||||
|
||||
impl IFace for Device {
|
||||
fn version(&self) -> io::Result<String> {
|
||||
let mut version = [0u32; 3];
|
||||
ffi::device_io_control(self.handle, TAP_WIN_IOCTL_GET_VERSION, &(), &mut version)?;
|
||||
Ok(format!("{}.{}.{}", version[0], version[1], version[2]))
|
||||
}
|
||||
fn name(&self) -> io::Result<String> {
|
||||
ffi::luid_to_alias(&self.luid).map(|name| decode_utf16(&name))
|
||||
}
|
||||
|
||||
fn shutdown(&self) -> io::Result<()> {
|
||||
self.enabled(false)
|
||||
}
|
||||
|
||||
fn set_ip(&self, address: Ipv4Addr, mask: Ipv4Addr) -> io::Result<()> {
|
||||
netsh::set_interface_ip(self.index, &address, &mask)
|
||||
}
|
||||
|
||||
fn mtu(&self) -> io::Result<u32> {
|
||||
let mut mtu = 0;
|
||||
ffi::device_io_control(self.handle, TAP_WIN_IOCTL_GET_MTU, &(), &mut mtu).map(|_| mtu)
|
||||
}
|
||||
|
||||
fn set_mtu(&self, value: u32) -> io::Result<()> {
|
||||
netsh::set_interface_mtu(self.index, value)
|
||||
}
|
||||
|
||||
fn add_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr, metric: u16) -> io::Result<()> {
|
||||
route::add_route(self.index, dest, netmask, Ipv4Addr::UNSPECIFIED, metric)?;
|
||||
netsh::delete_cache()
|
||||
}
|
||||
|
||||
fn delete_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> {
|
||||
route::delete_route(self.index, dest, netmask, Ipv4Addr::UNSPECIFIED)?;
|
||||
netsh::delete_cache()
|
||||
}
|
||||
|
||||
fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
packet::read_tap(
|
||||
buf,
|
||||
|eth_buf| ffi::read_file(self.handle, eth_buf).map(|res| res as usize),
|
||||
|eth_buf| ffi::write_file(self.handle, eth_buf).map(|res| res as _),
|
||||
)
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
// 封装二层数据
|
||||
packet::write_tap(
|
||||
buf,
|
||||
|eth_buf| ffi::write_file(self.handle, eth_buf).map(|res| res as _),
|
||||
&self.mac,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Device {
|
||||
fn drop(&mut self) {
|
||||
if let Err(e) = ffi::close_handle(self.handle) {
|
||||
log::warn!("close_handle={:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
use libloading::{Error, Library};
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use rand::Rng;
|
||||
use winapi::um::winbase;
|
||||
use winapi::um::{synchapi, winnt};
|
||||
|
||||
use crate::device::IFace;
|
||||
use crate::windows::decode_utf16;
|
||||
use crate::windows::{encode_utf16, ffi, netsh, route};
|
||||
|
||||
mod packet;
|
||||
mod wintun_log;
|
||||
mod wintun_raw;
|
||||
|
||||
/// The maximum size of wintun's internal ring buffer (in bytes)
|
||||
pub const MAX_RING_CAPACITY: u32 = 0x400_0000;
|
||||
|
||||
/// The minimum size of wintun's internal ring buffer (in bytes)
|
||||
pub const MIN_RING_CAPACITY: u32 = 0x2_0000;
|
||||
|
||||
/// Maximum pool name length including zero terminator
|
||||
pub const MAX_POOL: usize = 256;
|
||||
|
||||
pub struct Device {
|
||||
pub(crate) luid: u64,
|
||||
pub(crate) index: u32,
|
||||
/// The session handle given to us by WintunStartSession
|
||||
pub(crate) session: wintun_raw::WINTUN_SESSION_HANDLE,
|
||||
|
||||
/// Shared dll for required wintun driver functions
|
||||
pub(crate) win_tun: wintun_raw::wintun,
|
||||
|
||||
/// Windows event handle that is signaled by the wintun driver when data becomes available to
|
||||
/// read
|
||||
pub(crate) read_event: winnt::HANDLE,
|
||||
|
||||
/// Windows event handle that is signaled when [`TunSession::shutdown`] is called force blocking
|
||||
/// readers to exit
|
||||
pub(crate) shutdown_event: winnt::HANDLE,
|
||||
|
||||
/// The adapter that owns this session
|
||||
pub(crate) adapter: wintun_raw::WINTUN_ADAPTER_HANDLE,
|
||||
}
|
||||
|
||||
unsafe impl Send for Device {}
|
||||
|
||||
unsafe impl Sync for Device {}
|
||||
|
||||
impl Device {
|
||||
pub fn new(name: String) -> io::Result<Self> {
|
||||
unsafe {
|
||||
let library = match Library::new("wintun.dll") {
|
||||
Ok(library) => library,
|
||||
Err(e) => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("wintun.dll not found {:?}", e),
|
||||
));
|
||||
}
|
||||
};
|
||||
let win_tun = match wintun_raw::wintun::from_library(library) {
|
||||
Ok(win_tun) => win_tun,
|
||||
Err(e) => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("library error {:?} ", e),
|
||||
));
|
||||
}
|
||||
};
|
||||
let name_utf16 = encode_utf16(&name);
|
||||
if name_utf16.len() > MAX_POOL {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("too long {}:{:?}", MAX_POOL, name),
|
||||
));
|
||||
}
|
||||
wintun_log::set_default_logger_if_unset(&win_tun);
|
||||
let _ = Self::delete_for_name(&win_tun, &name_utf16);
|
||||
let mut guid_bytes: [u8; 16] = [0u8; 16];
|
||||
rand::thread_rng().fill(&mut guid_bytes);
|
||||
let guid = u128::from_ne_bytes(guid_bytes);
|
||||
//SAFETY: guid is a unique integer so transmuting either all zeroes or the user's preferred
|
||||
//guid to the winapi guid type is safe and will allow the windows kernel to see our GUID
|
||||
|
||||
let guid_struct: wintun_raw::GUID = unsafe { std::mem::transmute(guid) };
|
||||
let guid_ptr = &guid_struct as *const wintun_raw::GUID;
|
||||
|
||||
//SAFETY: the function is loaded from the wintun dll properly, we are providing valid
|
||||
//pointers, and all the strings are correct null terminated UTF-16. This safety rationale
|
||||
//applies for all Wintun* functions below
|
||||
let adapter =
|
||||
win_tun.WintunCreateAdapter(name_utf16.as_ptr(), name_utf16.as_ptr(), guid_ptr);
|
||||
if adapter.is_null() {
|
||||
log::error!("adapter.is_null {:?}", io::Error::last_os_error());
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"Failed to crate adapter",
|
||||
));
|
||||
}
|
||||
// 开启session
|
||||
let session = win_tun.WintunStartSession(adapter, MAX_RING_CAPACITY);
|
||||
if session.is_null() {
|
||||
log::error!("session.is_null {:?}", io::Error::last_os_error());
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"WintunStartSession failed",
|
||||
));
|
||||
}
|
||||
//SAFETY: We follow the contract required by CreateEventA. See MSDN
|
||||
//(the pointers are allowed to be null, and 0 is okay for the others)
|
||||
let shutdown_event =
|
||||
synchapi::CreateEventA(std::ptr::null_mut(), 0, 0, std::ptr::null_mut());
|
||||
let read_event = win_tun.WintunGetReadWaitEvent(session) as winnt::HANDLE;
|
||||
let mut luid: wintun_raw::NET_LUID = std::mem::zeroed();
|
||||
win_tun.WintunGetAdapterLUID(adapter, &mut luid as *mut wintun_raw::NET_LUID);
|
||||
let index = ffi::luid_to_index(&std::mem::transmute(luid)).map(|index| index as u32)?;
|
||||
// 设置网卡跃点
|
||||
if let Err(e) = netsh::set_interface_metric(index, 0) {
|
||||
log::warn!("{:?}",e);
|
||||
}
|
||||
Ok(Self {
|
||||
luid: std::mem::transmute(luid),
|
||||
index,
|
||||
session,
|
||||
win_tun,
|
||||
read_event,
|
||||
shutdown_event,
|
||||
adapter,
|
||||
})
|
||||
}
|
||||
}
|
||||
pub unsafe fn delete_for_name(
|
||||
win_tun: &wintun_raw::wintun,
|
||||
name_utf16: &Vec<u16>,
|
||||
) -> io::Result<()> {
|
||||
let adapter = win_tun.WintunOpenAdapter(name_utf16.as_ptr());
|
||||
if adapter.is_null() {
|
||||
log::error!(
|
||||
"delete_for_name adapter.is_null {:?}",
|
||||
io::Error::last_os_error()
|
||||
);
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"Failed to open adapter",
|
||||
));
|
||||
}
|
||||
win_tun.WintunCloseAdapter(adapter);
|
||||
win_tun.WintunDeleteDriver();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IFace for Device {
|
||||
fn version(&self) -> io::Result<String> {
|
||||
let version = unsafe { self.win_tun.WintunGetRunningDriverVersion() };
|
||||
if version == 0 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"WintunGetRunningDriverVersion",
|
||||
));
|
||||
} else {
|
||||
Ok(format!("{}.{}", (version >> 16) & 0xFFFF, version & 0xFFFF))
|
||||
}
|
||||
}
|
||||
fn name(&self) -> io::Result<String> {
|
||||
let luid = self.luid;
|
||||
ffi::luid_to_alias(&unsafe { std::mem::transmute(luid) }).map(|name| decode_utf16(&name))
|
||||
}
|
||||
|
||||
fn shutdown(&self) -> io::Result<()> {
|
||||
unsafe {
|
||||
if 0 == synchapi::SetEvent(self.shutdown_event) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::last_os_error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_ip(&self, address: Ipv4Addr, mask: Ipv4Addr) -> io::Result<()> {
|
||||
netsh::set_interface_ip(self.index, &address, &mask)
|
||||
}
|
||||
|
||||
fn mtu(&self) -> io::Result<u32> {
|
||||
Err(io::Error::from(io::ErrorKind::Unsupported))
|
||||
}
|
||||
|
||||
fn set_mtu(&self, value: u32) -> io::Result<()> {
|
||||
netsh::set_interface_mtu(self.index, value)
|
||||
}
|
||||
|
||||
fn add_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr, metric: u16) -> io::Result<()> {
|
||||
route::add_route(self.index, dest, netmask, Ipv4Addr::UNSPECIFIED, metric)?;
|
||||
netsh::delete_cache()
|
||||
}
|
||||
|
||||
fn delete_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> {
|
||||
route::delete_route(self.index, dest, netmask, Ipv4Addr::UNSPECIFIED)?;
|
||||
netsh::delete_cache()
|
||||
}
|
||||
|
||||
fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let packet = self.receive_blocking()?;
|
||||
let packet = packet.bytes();
|
||||
let len = packet.len();
|
||||
if len > buf.len() {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData, "data too long"));
|
||||
}
|
||||
buf[..len].copy_from_slice(packet);
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
let mut packet = self.allocate_send_packet(buf.len() as u16)?;
|
||||
packet.bytes_mut().copy_from_slice(buf);
|
||||
self.send_packet(packet);
|
||||
Ok(buf.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl Device {
|
||||
pub fn try_receive(&self) -> io::Result<Option<packet::TunPacket>> {
|
||||
let mut size = 0u32;
|
||||
|
||||
let bytes_ptr = unsafe {
|
||||
self.win_tun
|
||||
.WintunReceivePacket(self.session, &mut size as *mut u32)
|
||||
};
|
||||
|
||||
debug_assert!(size <= u16::MAX as u32);
|
||||
if bytes_ptr.is_null() {
|
||||
//Wintun returns ERROR_NO_MORE_ITEMS instead of blocking if packets are not available
|
||||
let last_error = unsafe { winapi::um::errhandlingapi::GetLastError() };
|
||||
if last_error == winapi::shared::winerror::ERROR_NO_MORE_ITEMS {
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "try_receive failed"))
|
||||
}
|
||||
} else {
|
||||
Ok(Some(packet::TunPacket {
|
||||
kind: packet::Kind::ReceivePacket,
|
||||
size: size as usize,
|
||||
//SAFETY: ptr is non null, aligned for u8, and readable for up to size bytes (which
|
||||
//must be less than isize::MAX because bytes is a u16
|
||||
bytes_ptr,
|
||||
tun_device: Some(&self),
|
||||
}))
|
||||
}
|
||||
}
|
||||
pub fn receive_blocking(&self) -> io::Result<packet::TunPacket> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
//Wait on both the read handle and the shutdown handle so that we stop when requested
|
||||
let handles = [self.read_event, self.shutdown_event];
|
||||
let result = unsafe {
|
||||
//SAFETY: We abide by the requirements of WaitForMultipleObjects, handles is a
|
||||
//pointer to valid, aligned, stack memory
|
||||
synchapi::WaitForMultipleObjects(
|
||||
2,
|
||||
&handles as *const winnt::HANDLE,
|
||||
0,
|
||||
winbase::INFINITE,
|
||||
)
|
||||
};
|
||||
match result {
|
||||
winbase::WAIT_FAILED => {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "WAIT_FAILED"));
|
||||
}
|
||||
_ => {
|
||||
if result == winbase::WAIT_OBJECT_0 {
|
||||
//We have data!
|
||||
continue;
|
||||
} else if result == winbase::WAIT_OBJECT_0 + 1 {
|
||||
//Shutdown event triggered
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"Shutdown event triggered",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn allocate_send_packet(&self, size: u16) -> io::Result<packet::TunPacket> {
|
||||
let bytes_ptr = unsafe {
|
||||
self.win_tun
|
||||
.WintunAllocateSendPacket(self.session, size as u32)
|
||||
};
|
||||
if bytes_ptr.is_null() {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"allocate_send_packet failed",
|
||||
))
|
||||
} else {
|
||||
Ok(packet::TunPacket {
|
||||
kind: packet::Kind::SendPacketPending,
|
||||
size: size as usize,
|
||||
//SAFETY: ptr is non null, aligned for u8, and readable for up to size bytes (which
|
||||
//must be less than isize::MAX because bytes is a u16
|
||||
bytes_ptr,
|
||||
tun_device: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
pub fn send_packet(&self, mut packet: packet::TunPacket) {
|
||||
assert!(matches!(packet.kind, packet::Kind::SendPacketPending));
|
||||
|
||||
unsafe {
|
||||
self.win_tun
|
||||
.WintunSendPacket(self.session, packet.bytes_ptr)
|
||||
};
|
||||
//Mark the packet at sent
|
||||
packet.kind = packet::Kind::SendPacketSent;
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Device {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if let Err(e) = ffi::close_handle(self.shutdown_event) {
|
||||
log::warn!("close shutdown_event={:?}", e)
|
||||
}
|
||||
self.win_tun.WintunEndSession(self.session);
|
||||
self.win_tun.WintunCloseAdapter(self.adapter);
|
||||
if 0 != self.win_tun.WintunDeleteDriver() {
|
||||
log::warn!("WintunDeleteDriver failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
use crate::windows::tun::Device;
|
||||
|
||||
pub(crate) enum Kind {
|
||||
SendPacketPending,
|
||||
//Send packet type, but not sent yet
|
||||
SendPacketSent,
|
||||
//Send packet type - sent
|
||||
ReceivePacket,
|
||||
}
|
||||
|
||||
/// Represents a wintun packet
|
||||
pub struct TunPacket<'a> {
|
||||
pub(crate) kind: Kind,
|
||||
pub(crate) size: usize,
|
||||
pub(crate) bytes_ptr: *const u8,
|
||||
|
||||
//Share ownership of session to prevent the session from being dropped before packets that
|
||||
//belong to it
|
||||
pub(crate) tun_device: Option<&'a Device>,
|
||||
}
|
||||
|
||||
impl<'a> TunPacket<'a> {
|
||||
/// Returns the bytes this packet holds as &mut.
|
||||
/// The lifetime of the bytes is tied to the lifetime of this packet.
|
||||
pub fn bytes_mut(&mut self) -> &mut [u8] {
|
||||
unsafe { std::slice::from_raw_parts_mut(self.bytes_ptr as *mut u8, self.size) }
|
||||
}
|
||||
|
||||
/// Returns an immutable reference to the bytes this packet holds.
|
||||
/// The lifetime of the bytes is tied to the lifetime of this packet.
|
||||
pub fn bytes(&self) -> &[u8] {
|
||||
unsafe { std::slice::from_raw_parts(self.bytes_ptr, self.size) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for TunPacket<'a> {
|
||||
fn drop(&mut self) {
|
||||
match self.kind {
|
||||
Kind::ReceivePacket => {
|
||||
unsafe {
|
||||
//SAFETY:
|
||||
//
|
||||
// 1. We share ownership of the session therefore it hasn't been dropped yet
|
||||
// 2. Bytes is valid because each packet holds exclusive access to a region of the
|
||||
// ring buffer that the wintun session owns. We return that region of
|
||||
// memory back to wintun here
|
||||
let tun_device = self.tun_device.unwrap();
|
||||
tun_device
|
||||
.win_tun
|
||||
.WintunReleaseReceivePacket(tun_device.session, self.bytes_ptr)
|
||||
};
|
||||
}
|
||||
Kind::SendPacketPending => {
|
||||
//If someone allocates a packet with session.allocate_send_packet() and then it is
|
||||
//dropped without being sent, this will hold up the send queue because wintun expects
|
||||
//that every allocated packet is sent
|
||||
panic!("Packet was never sent!");
|
||||
}
|
||||
Kind::SendPacketSent => {
|
||||
//Nop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,16 @@
|
||||
use crate::wintun_raw;
|
||||
use crate::Wintun;
|
||||
use log::*;
|
||||
|
||||
use crate::windows::tun::wintun_raw;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use widestring::U16CStr;
|
||||
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
/// Sets the logger wintun will use when logging. Maps to the WintunSetLogger C function
|
||||
pub fn set_logger(wintun: &Wintun, f: wintun_raw::WINTUN_LOGGER_CALLBACK) {
|
||||
unsafe { wintun.WintunSetLogger(f) };
|
||||
pub fn set_logger(win_tun: &wintun_raw::wintun, f: wintun_raw::WINTUN_LOGGER_CALLBACK) {
|
||||
unsafe { win_tun.WintunSetLogger(f) };
|
||||
}
|
||||
|
||||
pub fn reset_logger(wintun: &Wintun) {
|
||||
set_logger(wintun, None);
|
||||
pub fn reset_logger(win_tun: &wintun_raw::wintun) {
|
||||
set_logger(win_tun, None);
|
||||
}
|
||||
|
||||
static SET_LOGGER: AtomicBool = AtomicBool::new(false);
|
||||
@@ -38,11 +37,11 @@ pub unsafe extern "C" fn default_logger(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_default_logger_if_unset(wintun: &Wintun) {
|
||||
pub(crate) fn set_default_logger_if_unset(win_tun: &wintun_raw::wintun) {
|
||||
if SET_LOGGER
|
||||
.compare_exchange(false, true, Ordering::SeqCst, Ordering::Relaxed)
|
||||
.is_ok()
|
||||
{
|
||||
set_logger(wintun, Some(default_logger));
|
||||
set_logger(win_tun, Some(default_logger));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
/* automatically generated by rust-bindgen 0.59.1 */
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct __BindgenBitfieldUnit<Storage> {
|
||||
Reference in New Issue
Block a user