Compare commits

...
81 Commits
Author SHA1 Message Date
vnt-dev 71da72da34 Merge pull request #171 from cfdworld/main
Update Cargo.toml for client to add support for tls1.2 when use wss
2026-01-27 13:49:46 +08:00
hello c6ef2031fa Update Cargo.toml for client to add support for tls1.2 when use wss
client add support for tls1.2 when use wss
2026-01-27 13:25:40 +08:00
lbl a82933a7a8 调整运行时 2025-09-23 09:27:54 +08:00
lbl 76444ef17d Merge remote-tracking branch 'refs/remotes/origin/1.2.x' 2025-09-23 09:16:58 +08:00
lubeilin fde33380be 调整超时时间 2025-09-18 18:27:31 +08:00
vnt-dev eedb6684a5 Merge pull request #158 from vnt-dev/vnt-dev-patch-2
Update README.md
2025-09-17 18:25:55 +08:00
vnt-dev a335a5bcdf Update README.md 2025-09-17 18:25:24 +08:00
vnt-dev 4a347d853e Merge pull request #157 from radish0416/main
修改rust.yml文件,云编译通过
2025-09-17 10:01:01 +08:00
radish0416 79f55fe7fd Update rust.yml 2025-09-17 09:30:52 +08:00
lixianjiang affe8fb719 修改rust.yml文件,云编译通过 2025-09-17 08:41:44 +08:00
lbl 22eee048d0 Use the latest version of tun-rs 2025-07-19 22:10:53 +08:00
lbl 2b2a550a98 not specify the rust version 2025-06-28 17:38:46 +08:00
lbl 14427ce437 Update Dependencies 2025-06-28 17:31:00 +08:00
lbl 39c170892e fmt 2025-06-28 17:29:59 +08:00
lbl b82276e6fa Merge branch 'refs/heads/main' into 1.2.x
# Conflicts:
#	vnt/src/channel/ws_channel.rs
#	vnt/src/util/dns_query.rs
2025-06-28 14:57:29 +08:00
vnt-dev 154e8ef09a Merge pull request #146 from lmq8267/1.2.x
1.2.x
2025-04-27 11:40:00 +08:00
VNT-[bot] 97a2f7c306 无网络不终止程序继续运行 2025-04-26 10:20:09 +08:00
VNT-[bot] 8d39d6045a 添加thunk-rs链接Windows 7兼容库 2025-04-26 10:15:27 +08:00
VNT-[bot] 04b60773b9 增加30x重定向功能(服务器地址以http:前缀)例如 tcp://http:域名 2025-04-26 10:08:48 +08:00
VNT-[bot] 774a80dc46 升级mips和i686的编译 2025-04-26 09:57:33 +08:00
vnt-dev 1cba257531 Merge pull request #131 from lmq8267/patch-4
升级mips和i686的编译
2025-04-25 18:02:05 +08:00
vnt-dev 16cc2abe7a Merge pull request #130 from lmq8267/patch-3
替换前缀
2025-04-25 18:01:29 +08:00
vnt-dev 595a49ee3f Merge pull request #129 from lmq8267/patch-2
添加一下 http 302重定向
2025-04-25 18:00:57 +08:00
vnt-dev f894f62eb2 Merge pull request #128 from lmq8267/patch-1
添加一下http 302重定向
2025-04-25 18:00:41 +08:00
lmq8267 a98d6a7594 升级mips和i686的编译 2025-03-10 22:18:10 +08:00
lmq8267 9674dfa5dc 替换前缀 2025-03-10 20:07:43 +08:00
lmq8267 00068cdcfd 添加一下 http 302重定向 2025-03-10 20:03:54 +08:00
lmq8267 846f128689 添加一下http 302重定向 2025-03-10 20:02:43 +08:00
lbl 2225edeb2e add unsafe block 2025-02-24 11:09:22 +08:00
lbl ef48007922 fix type error 2025-02-24 10:57:11 +08:00
lbl 1d5257363c use SyncDevice 2025-02-24 10:24:48 +08:00
lbl 0238d1ab7e Modify the compilation method for the MIPS target platform. 2025-02-21 16:31:48 +08:00
lbl ffbb85dfff Merge branch 'refs/heads/1.2.x-' into main-1 2025-02-21 15:29:51 +08:00
lbl 563aca39aa Use the new version of tun-rs 2025-02-13 17:02:06 +08:00
lbl 274f4df43a upload file 2025-02-06 19:06:13 +08:00
lbl 1fae7c9979 modify component name 2025-02-06 18:15:51 +08:00
lbl 46b37c0753 use v4 2025-02-06 17:56:29 +08:00
lbl f737f591b6 version = "1.2.16" 2025-02-06 16:28:43 +08:00
lbl ebf8337ad7 default tun name use vnt-tun 2025-02-06 16:26:47 +08:00
lbl e2d8a462a2 version = "1.2.15" 2025-01-05 10:48:27 +08:00
lbl b09cd690f0 去除tap 2025-01-05 10:42:27 +08:00
lbl 76bd79d97a update tun config 2024-12-31 14:20:15 +08:00
lbl c0d8645833 replace tun 2024-12-31 11:26:59 +08:00
vnt-dev 501a9e8198 Merge pull request #99 from vnt-dev/1.2.x
1.2.x
2024-10-20 14:13:57 +08:00
lubeilin 8aa4736b01 Merge remote-tracking branch 'vnt-release/1.2.x' into 1.2.x- 2024-10-14 16:53:45 +08:00
lubeilin d5e10a8210 调整数据读取 2024-10-14 16:53:35 +08:00
lbl 1165ee52c7 v3 2024-10-13 12:36:37 +08:00
lbl 8a66bbe1af version=1.2.14 2024-10-13 12:12:41 +08:00
lbl c91d8a2734 log 2024-10-13 12:11:16 +08:00
lbl 09a315a0f1 绑定本地网卡名称 2024-10-13 12:09:30 +08:00
vnt-dev 2979f2ef6f Merge pull request #97 from vnt-dev/vnt-dev-patch-1
Update README.md
2024-10-03 15:49:38 +08:00
vnt-dev 8225cb7877 Update README.md 2024-10-03 15:49:18 +08:00
vnt-dev 129ca1ec85 Merge pull request #86 from vnt-dev/1.2.x
1.2.x
2024-08-21 17:00:34 +08:00
lbl8603 96b67c6162 更新文档 2024-08-12 22:04:31 +08:00
lbl8603 ce02b30b2b 去除默认绑定网卡的逻辑 2024-08-12 22:04:17 +08:00
lbl8603 2fbdfe920e 增加绑定网卡 2024-08-08 22:02:46 +08:00
lbl8603 7901407d69 解决已知问题 2024-08-08 21:30:19 +08:00
lbl8603 e6cfdec9c6 调整默认网卡的逻辑 2024-08-08 20:38:58 +08:00
lbl8603 7e0de2c4e6 忽略绑定失败的错误 2024-08-07 23:14:13 +08:00
lbl8603 803af55246 增加日志 2024-08-07 22:34:48 +08:00
lbl8603 8e1a8fa7f0 增加feature判断 2024-08-07 21:48:58 +08:00
lbl8603 fc6bf407c4 wintun.dll校验失败则返回提示 2024-08-07 21:32:19 +08:00
lbl8603 dc4e472f20 提示虚拟网卡创建失败的信息 2024-08-07 21:29:55 +08:00
lbl8603 afff49e61a set_reuse_port 2024-08-06 22:03:18 +08:00
lbl8603 0f9ac2ac87 NAT1下的tcp打洞 2024-08-05 20:44:54 +08:00
vnt-dev cce23b9661 Merge pull request #67 from vnt-dev/1.2.x
1.2.x
2024-08-04 16:29:29 +08:00
lbl8603 4347ed68c3 Merge branch 'refs/heads/bugfix/2024-08-01-修复打洞问题' into 1.2.x 2024-08-01 23:24:10 +08:00
lbl8603 64d68c8a84 修复打洞问题 2024-08-01 23:23:23 +08:00
lbl8603 210b7c959e 调整日志 2024-08-01 21:56:09 +08:00
lbl8603 e8322adbcc 修复重启后不打洞的问题 2024-08-01 21:26:31 +08:00
lbl8603 e523b02b58 android增加wg选项 2024-07-31 21:37:19 +08:00
lbl8603 13df028cd9 更新版本号 2024-07-31 11:01:31 +08:00
lbl8603 4550bea063 更新版本 2024-07-31 11:00:20 +08:00
lbl8603 be8d4cbb8b fmt 2024-07-31 10:00:28 +08:00
lbl8603 ace0268a8c Merge branch 'refs/heads/1.2.x' into features/2024-07-30-分离协议栈
# Conflicts:
#	vnt/src/channel/punch.rs
2024-07-31 09:57:55 +08:00
lbl8603 56fd0d7a4f v4 socket绑定网卡 2024-07-30 23:37:54 +08:00
lbl8603 91ee751b07 Merge branch 'refs/heads/bugfix-2024-07-30-issues#65' into 1.2.x 2024-07-30 22:54:37 +08:00
lbl8603 fa9903ace1 修复配置文件没有读取加密方式的问题 2024-07-30 22:54:17 +08:00
lbl8603 c2854352df Merge branch 'refs/heads/features/2024-07-30-调整文档' into 1.2.x 2024-07-30 22:52:26 +08:00
lbl8603 b3472fb5b3 调整说明 2024-07-30 22:52:04 +08:00
lbl8603 e373c9ee16 支持选择tcp或udp打洞 2024-07-25 20:54:08 +08:00
84 changed files with 1581 additions and 6727 deletions
+138 -115
View File
@@ -7,155 +7,157 @@ on:
env:
CARGO_TERM_COLOR: always
permissions:
contents: write
defaults:
run:
# necessary for windows
shell: bash
jobs:
# test:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v2
# - name: Init submodules
# uses: snickerbockers/submodules-init@v4
# - name: Cargo cache
# uses: actions/cache@v2
# with:
# path: |
# ~/.cargo/registry
# ./target
# key: test-cargo-registry
# - name: List
# run: find ./
# - name: Run tests
# run: cargo test --verbose
build:
strategy:
fail-fast: false
matrix:
# a list of all the targets
# 选择使用openssl或者ring,并不是所有平台都支持
include:
- TARGET: i686-unknown-linux-musl # test in an alpine container on a mac
- TARGET: i686-unknown-linux-musl
OS: ubuntu-latest
FEATURES: default
- TARGET: x86_64-unknown-linux-musl # test in an alpine container on a mac
FEATURES: ring-cipher,openssl-vendored,wss
- TARGET: x86_64-unknown-linux-musl
OS: ubuntu-latest
FEATURES: ring-cipher,wss
- TARGET: aarch64-unknown-linux-musl # tested on aws t4g.nano in alpine container
- TARGET: aarch64-unknown-linux-musl
OS: ubuntu-latest
FEATURES: ring-cipher,wss
- TARGET: armv7-unknown-linux-musleabihf # raspberry pi 2-3-4, not tested
- TARGET: armv7-unknown-linux-musleabihf
OS: ubuntu-latest
FEATURES: ring-cipher,wss
- TARGET: armv7-unknown-linux-musleabi # raspberry pi 2-3-4, not tested
- TARGET: armv7-unknown-linux-musleabi
OS: ubuntu-latest
FEATURES: ring-cipher,wss
- TARGET: arm-unknown-linux-musleabihf # raspberry pi 0-1, not tested
- TARGET: arm-unknown-linux-musleabihf
OS: ubuntu-latest
FEATURES: ring-cipher,wss
- TARGET: arm-unknown-linux-musleabi # raspberry pi 0-1, not tested
- TARGET: arm-unknown-linux-musleabi
OS: ubuntu-latest
FEATURES: ring-cipher,wss
- TARGET: x86_64-apple-darwin # tested on a mac, is not properly signed so there are security warnings
- TARGET: x86_64-apple-darwin
OS: macos-latest
FEATURES: ring-cipher,wss
- TARGET: aarch64-apple-darwin # tested on a mac, is not properly signed so there are security warnings
- TARGET: aarch64-apple-darwin
OS: macos-latest
FEATURES: ring-cipher,wss
- TARGET: i686-pc-windows-msvc # tested on a windows machine
OS: windows-2019
FEATURES: ring-cipher,wss
- TARGET: x86_64-pc-windows-msvc # tested on a windows machine
- TARGET: i686-pc-windows-msvc
OS: windows-latest
FEATURES: ring-cipher,wss
- TARGET: mipsel-unknown-linux-musl # openwrt
- TARGET: x86_64-pc-windows-msvc
OS: windows-latest
FEATURES: ring-cipher,wss
- TARGET: mipsel-unknown-linux-musl
OS: ubuntu-latest
FEATURES: ring-cipher,wss
- TARGET: mips-unknown-linux-musl # openwrt
- TARGET: mips-unknown-linux-musl
OS: ubuntu-latest
FEATURES: ring-cipher,wss
# needs: test
runs-on: ${{ matrix.OS }}
env:
NAME: vnt-cli # change with the name of your project
NAME: vnt-cli
TARGET: ${{ matrix.TARGET }}
OS: ${{ matrix.OS }}
FEATURES: ${{ matrix.FEATURES }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Init submodules
uses: snickerbockers/submodules-init@v4
run: git submodule update --init --recursive --remote && git submodule status
- name: Cargo cache
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
./target
key: build-cargo-registry-${{matrix.TARGET}}
- name: Use strawberry perl
if: startsWith(matrix.os, 'windows')
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
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
profile: minimal
override: true
- name: Verify Rust version
run: |
# dependencies are only needed on ubuntu as that's the only place where
# we make cross-compilation
rustup set auto-self-update disable
if [[ $OS =~ ^ubuntu.*$ ]]; then
sudo apt-get update && sudo apt-get install clang llvm libc6-dev-i386 crossbuild-essential-arm64 crossbuild-essential-armhf musl-tools libboost-all-dev libc6-dev -y
# curl -s musl.cc | grep mipsel
case $TARGET in
mipsel-unknown-linux-musl)
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
;;
armv7-unknown-linux-musleabi)
MUSL_URI=armv7m-linux-musleabi-cross
;;
arm-unknown-linux-musleabihf)
MUSL_URI=arm-linux-musleabihf-cross
;;
arm-unknown-linux-musleabi)
MUSL_URI=arm-linux-musleabi-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
else
rustup install 1.77
rustup default 1.77
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
else
rustup install 1.77
rustup default 1.77
rustc --version
cargo --version
- name: Install dependencies on Ubuntu
if: startsWith(matrix.OS, 'ubuntu')
run: |
sudo apt-get update && sudo apt-get install -y clang llvm libc6-dev-i386 \
crossbuild-essential-arm64 crossbuild-essential-armhf musl-tools libboost-all-dev libc6-dev wget tar
# Install musl cross toolchains if needed
case $TARGET in
mipsel-unknown-linux-musl)
MUSL_URI=mipsel-linux-muslsf-cross
URL=mipsel-linux-muslsf
;;
aarch64-unknown-linux-musl)
MUSL_URI=aarch64-linux-musl-cross
;;
armv7-unknown-linux-musleabihf)
MUSL_URI=armv7l-linux-musleabihf-cross
;;
armv7-unknown-linux-musleabi)
MUSL_URI=armv7m-linux-musleabi-cross
;;
arm-unknown-linux-musleabihf)
MUSL_URI=arm-linux-musleabihf-cross
;;
arm-unknown-linux-musleabi)
MUSL_URI=arm-linux-musleabi-cross
;;
mips-unknown-linux-musl)
MUSL_URI=mips-linux-muslsf-cross
URL=mips-linux-muslsf
;;
i686-unknown-linux-musl)
MUSL_URI=i686-linux-musl-cross
;;
esac
if [ -n "$MUSL_URI" ]; then
mkdir -p /opt/musl_gcc
wget -c https://github.com/rustp2p/musl-cross/releases/download/0.0.1/$MUSL_URI.tgz -P /opt/musl_gcc/
tar zxf /opt/musl_gcc/$MUSL_URI.tgz -C /opt/musl_gcc/
sudo ln -s /opt/musl_gcc/$MUSL_URI/bin/*gcc /usr/bin/
fi
rustup -V
# some additional configuration for cross-compilation on linux
if [[ $TARGET =~ ^mips.*$ ]]; then
cd /opt/musl_gcc/${URL}-cross/lib/gcc/${URL}/11.2.1
cp libgcc_eh.a libunwind.a
rustup toolchain install nightly-x86_64-unknown-linux-gnu
rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu
RUST_LIB_SRC=$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/
if [[ -f $RUST_LIB_SRC/library/Cargo.lock && ! -f $RUST_LIB_SRC/Cargo.lock ]]; then
cp -f $RUST_LIB_SRC/library/Cargo.lock $RUST_LIB_SRC/Cargo.lock
fi
fi
rustup -V
- name: Configure cargo for cross-compilation
run: |
cat >>~/.cargo/config <<EOF
[target.x86_64-unknown-linux-musl]
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
@@ -175,11 +177,17 @@ jobs:
linker = "arm-linux-musleabi-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"]
linker = "mipsel-linux-muslsf-gcc"
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols","-C", "link-arg=-static",
"-C", "relocation-model=static","-C", "link-arg=-no-pie","--cfg", "compiler_builtins_no_debug",
"-L", "/opt/musl_gcc/mipsel-linux-muslsf-cross/mipsel-linux-muslsf/lib",
"-L", "/opt/musl_gcc/mipsel-linux-muslsf-cross/lib/gcc/mipsel-linux-muslsf/11.2.1"]
[target.mips-unknown-linux-musl]
linker = "mips-linux-musl-gcc"
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
linker = "mips-linux-muslsf-gcc"
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols","-C", "link-arg=-static",
"-C", "relocation-model=static","-C", "link-arg=-no-pie","--cfg", "compiler_builtins_no_debug",
"-L", "/opt/musl_gcc/mips-linux-muslsf-cross/mips-linux-muslsf/lib",
"-L", "/opt/musl_gcc/mips-linux-muslsf-cross/lib/gcc/mips-linux-muslsf/11.2.1"]
[target.x86_64-pc-windows-msvc]
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
[target.i686-pc-windows-msvc]
@@ -189,20 +197,34 @@ jobs:
[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"]
linker = "i686-linux-musl-gcc"
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
EOF
- name: Install rust target
if: ${{ ! endsWith(matrix.TARGET, 'freebsd') && ! startsWith(matrix.TARGET, 'mips') }}
run: rustup target add $TARGET
- name: Run build vn-link-cli
run: cargo build --package vn-link-cli --release --verbose --target $TARGET --features $FEATURES
run: |
if [[ $TARGET =~ ^mips.*$ ]]; then
cargo +nightly build --package vn-link-cli --release --target $TARGET -Z build-std=std,panic_abort --features $FEATURES
else
cargo build --package vn-link-cli --release --target $TARGET --features $FEATURES
fi
- name: Run build vnt-cli
run: cargo build --package vnt-cli --release --verbose --target $TARGET --features $FEATURES
run: |
if [[ $TARGET =~ ^mips.*$ ]]; then
cargo +nightly build --package vnt-cli --release --target $TARGET -Z build-std=std,panic_abort --features $FEATURES
else
cargo build --package vnt-cli --release --target $TARGET --features $FEATURES
fi
- name: List target
run: find ./target
- name: Compress
run: |
mkdir -p ./artifacts
# windows is the only OS using a different convention for executable file name
if [[ $OS =~ ^windows.*$ ]]; then
EXEC_VNT_CLI=vnt-cli.exe
EXEC_VN_LINK_CLI=vn-link-cli.exe
@@ -220,30 +242,31 @@ jobs:
mv ./README ./artifacts/README.txt
cd ./artifacts
tar -czf vnt-$TARGET-$TAG.tar.gz *
mkdir upload_file
mv vnt-$TARGET-$TAG.tar.gz ./upload_file/
- name: Archive artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: vnt-cli
path: |
./artifacts
# deploys to github releases on tag
name: vnt-cli-${{ matrix.TARGET }}
path: ./artifacts/upload_file
deploy:
if: startsWith(github.ref, 'refs/tags/')
needs: build
runs-on: ubuntu-latest
steps:
- name: Download artifacts
uses: actions/download-artifact@v2
uses: actions/download-artifact@v4
with:
name: vnt-cli
path: ./artifacts
- name: List
run: find ./artifacts
- name: Release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.YOURTOKEN }}
file: ./artifacts/*.tar.gz
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ./artifacts/**/*.tar.gz
tag: ${{ github.ref }}
overwrite: true
file_glob: true
file_glob: true
Generated
-2511
View File
@@ -1,2511 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aead"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
dependencies = [
"crypto-common",
"generic-array",
]
[[package]]
name = "aes"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
]
[[package]]
name = "aes-gcm"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
dependencies = [
"aead",
"aes",
"cipher",
"ctr",
"ghash",
"subtle",
]
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "anyhow"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
[[package]]
name = "arc-swap"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
[[package]]
name = "attohttpc"
version = "0.16.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb8867f378f33f78a811a8eb9bf108ad99430d7aad43315dd9319c827ef6247"
dependencies = [
"http 0.2.12",
"log",
"url",
"wildmatch",
]
[[package]]
name = "autocfg"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
[[package]]
name = "backtrace"
version = "0.3.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64ct"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "bindgen"
version = "0.69.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0"
dependencies = [
"bitflags 2.5.0",
"cexpr",
"clang-sys",
"itertools",
"lazy_static",
"lazycell",
"log",
"prettyplease",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn 2.0.60",
"which",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "block-padding"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
[[package]]
name = "cbc"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
dependencies = [
"cipher",
]
[[package]]
name = "cc"
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695"
dependencies = [
"jobserver",
"libc",
"once_cell",
]
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cfg_aliases"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "chacha20"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
]
[[package]]
name = "chacha20poly1305"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
dependencies = [
"aead",
"chacha20",
"cipher",
"poly1305",
"zeroize",
]
[[package]]
name = "chrono"
version = "0.4.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-targets 0.52.5",
]
[[package]]
name = "cipher"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common",
"inout",
"zeroize",
]
[[package]]
name = "clang-sys"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "common"
version = "1.2.12"
dependencies = [
"anyhow",
"chrono",
"console",
"gethostname",
"getopts",
"log",
"log4rs",
"rand",
"serde",
"serde_yaml",
"sys-locale",
"uuid",
"vnt",
]
[[package]]
name = "console"
version = "0.15.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
dependencies = [
"encode_unicode",
"lazy_static",
"libc",
"unicode-width",
"windows-sys 0.52.0",
]
[[package]]
name = "const-oid"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "core-foundation"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]]
name = "cpufeatures"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
dependencies = [
"libc",
]
[[package]]
name = "crossbeam"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-epoch",
"crossbeam-queue",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-queue"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"rand_core",
"typenum",
]
[[package]]
name = "ctr"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
dependencies = [
"cipher",
]
[[package]]
name = "data-encoding"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
[[package]]
name = "der"
version = "0.7.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
dependencies = [
"const-oid",
"pem-rfc7468",
"zeroize",
]
[[package]]
name = "derivative"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "destructure_traitobject"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7"
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"const-oid",
"crypto-common",
]
[[package]]
name = "dns-parser"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4d33be9473d06f75f58220f71f7a9317aca647dc061dbd3c361b0bef505fbea"
dependencies = [
"byteorder",
"quick-error",
]
[[package]]
name = "ecb"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a8bfa975b1aec2145850fcaa1c6fe269a16578c44705a532ae3edc92b8881c7"
dependencies = [
"cipher",
]
[[package]]
name = "either"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
[[package]]
name = "encode_unicode"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "fastrand"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [
"percent-encoding",
]
[[package]]
name = "futures-core"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]]
name = "futures-macro"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.60",
]
[[package]]
name = "futures-sink"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
[[package]]
name = "futures-task"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
[[package]]
name = "futures-util"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [
"futures-core",
"futures-macro",
"futures-sink",
"futures-task",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "gethostname"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818"
dependencies = [
"libc",
"windows-targets 0.48.5",
]
[[package]]
name = "getopts"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
dependencies = [
"unicode-width",
]
[[package]]
name = "getrandom"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi",
"wasm-bindgen",
]
[[package]]
name = "ghash"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1"
dependencies = [
"opaque-debug",
"polyval",
]
[[package]]
name = "gimli"
version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "hermit-abi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "home"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "http"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "http"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "httparse"
version = "1.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "iana-time-zone"
version = "0.1.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "idna"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
dependencies = [
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "igd"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "556b5a75cd4adb7c4ea21c64af1c48cefb2ce7d43dc4352c720a1fe47c21f355"
dependencies = [
"attohttpc",
"log",
"rand",
"url",
"xmltree",
]
[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown 0.12.3",
]
[[package]]
name = "indexmap"
version = "2.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [
"equivalent",
"hashbrown 0.14.3",
]
[[package]]
name = "inout"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
dependencies = [
"block-padding",
"generic-array",
]
[[package]]
name = "ioctl-sys"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bd11f3a29434026f5ff98c730b668ba74b1033637b8817940b54d040696133c"
[[package]]
name = "itertools"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "jobserver"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
dependencies = [
"libc",
]
[[package]]
name = "js-sys"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
dependencies = [
"spin 0.5.2",
]
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "libloading"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
dependencies = [
"cfg-if",
"windows-targets 0.52.5",
]
[[package]]
name = "libm"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
[[package]]
name = "libsm"
version = "0.5.1"
source = "git+https://github.com/vnt-dev/libsm#f9e1b8cf20d0829efb4934a940d0ba4f3ee0ac14"
dependencies = [
"byteorder",
"getrandom",
"lazy_static",
"num-bigint",
"num-integer",
"num-traits",
"rand",
"yasna",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
[[package]]
name = "lock_api"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
dependencies = [
"serde",
]
[[package]]
name = "log-mdc"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7"
[[package]]
name = "log4rs"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0816135ae15bd0391cf284eab37e6e3ee0a6ee63d2ceeb659862bd8d0a984ca6"
dependencies = [
"anyhow",
"arc-swap",
"chrono",
"derivative",
"fnv",
"humantime",
"libc",
"log",
"log-mdc",
"once_cell",
"parking_lot",
"rand",
"serde",
"serde-value",
"serde_json",
"serde_yaml",
"thiserror",
"thread-id",
"typemap-ors",
"winapi",
]
[[package]]
name = "lwip-rs"
version = "0.1.0"
source = "git+https://github.com/vnt-dev/lwip-rs#3133f0c3bde55333a27641182ae3550e7acc2a39"
dependencies = [
"bindgen",
"cc",
"crossbeam",
"lazy_static",
"log",
"parking_lot",
"tokio",
]
[[package]]
name = "lz4_flex"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5"
[[package]]
name = "memchr"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
dependencies = [
"adler",
]
[[package]]
name = "mio"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [
"libc",
"log",
"wasi",
"windows-sys 0.48.0",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "num-bigint"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-bigint-dig"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151"
dependencies = [
"byteorder",
"lazy_static",
"libm",
"num-integer",
"num-iter",
"num-traits",
"rand",
"smallvec",
"zeroize",
]
[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
dependencies = [
"autocfg",
"libm",
]
[[package]]
name = "num_cpus"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "object"
version = "0.32.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "opaque-debug"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
[[package]]
name = "openssl-probe"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-src"
version = "300.1.3+3.1.2"
source = "git+https://github.com/lbl8603/openssl-src-rs#871c5d6cfe8a7cb1c5725638d7563c0d96ca4c04"
dependencies = [
"cc",
]
[[package]]
name = "openssl-sys"
version = "0.9.93"
source = "git+https://github.com/vnt-dev/rust-openssl#e8b3d2c02d2d07a0e4a82ff4e4bef210f4bc71c8"
dependencies = [
"cc",
"libc",
"openssl-src",
"pkg-config",
"vcpkg",
]
[[package]]
name = "ordered-float"
version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c"
dependencies = [
"num-traits",
]
[[package]]
name = "packet"
version = "0.1.0"
dependencies = [
"byteorder",
]
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets 0.48.5",
]
[[package]]
name = "pem-rfc7468"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
dependencies = [
"base64ct",
]
[[package]]
name = "percent-encoding"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pin-project-lite"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkcs1"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
dependencies = [
"der",
"pkcs8",
"spki",
]
[[package]]
name = "pkcs8"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
dependencies = [
"der",
"spki",
]
[[package]]
name = "pkg-config"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
[[package]]
name = "poly1305"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
dependencies = [
"cpufeatures",
"opaque-debug",
"universal-hash",
]
[[package]]
name = "polyval"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
dependencies = [
"cfg-if",
"cpufeatures",
"opaque-debug",
"universal-hash",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "prettyplease"
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e"
dependencies = [
"proc-macro2",
"syn 2.0.60",
]
[[package]]
name = "proc-macro2"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
dependencies = [
"unicode-ident",
]
[[package]]
name = "protobuf"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b55bad9126f378a853655831eb7363b7b01b81d19f8cb1218861086ca4a1a61e"
dependencies = [
"once_cell",
"protobuf-support",
"thiserror",
]
[[package]]
name = "protobuf-codegen"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dd418ac3c91caa4032d37cb80ff0d44e2ebe637b2fb243b6234bf89cdac4901"
dependencies = [
"anyhow",
"once_cell",
"protobuf",
"protobuf-parse",
"regex",
"tempfile",
"thiserror",
]
[[package]]
name = "protobuf-parse"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d39b14605eaa1f6a340aec7f320b34064feb26c93aec35d6a9a2272a8ddfa49"
dependencies = [
"anyhow",
"indexmap 1.9.3",
"log",
"protobuf",
"protobuf-support",
"tempfile",
"thiserror",
"which",
]
[[package]]
name = "protobuf-support"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5d4d7b8601c814cfb36bcebb79f0e61e45e1e93640cf778837833bbed05c372"
dependencies = [
"thiserror",
]
[[package]]
name = "protoc-bin-vendored"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "005ca8623e5633e298ad1f917d8be0a44bcf406bf3cde3b80e63003e49a3f27d"
dependencies = [
"protoc-bin-vendored-linux-aarch_64",
"protoc-bin-vendored-linux-ppcle_64",
"protoc-bin-vendored-linux-x86_32",
"protoc-bin-vendored-linux-x86_64",
"protoc-bin-vendored-macos-x86_64",
"protoc-bin-vendored-win32",
]
[[package]]
name = "protoc-bin-vendored-linux-aarch_64"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fb9fc9cce84c8694b6ea01cc6296617b288b703719b725b8c9c65f7c5874435"
[[package]]
name = "protoc-bin-vendored-linux-ppcle_64"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02d2a07dcf7173a04d49974930ccbfb7fd4d74df30ecfc8762cf2f895a094516"
[[package]]
name = "protoc-bin-vendored-linux-x86_32"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54fef0b04fcacba64d1d80eed74a20356d96847da8497a59b0a0a436c9165b0"
[[package]]
name = "protoc-bin-vendored-linux-x86_64"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8782f2ce7d43a9a5c74ea4936f001e9e8442205c244f7a3d4286bd4c37bc924"
[[package]]
name = "protoc-bin-vendored-macos-x86_64"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5de656c7ee83f08e0ae5b81792ccfdc1d04e7876b1d9a38e6876a9e09e02537"
[[package]]
name = "protoc-bin-vendored-win32"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9653c3ed92974e34c5a6e0a510864dab979760481714c172e0a34e437cb98804"
[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-ex"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42776a943a4f890410259860d4c308d32792ecbeaca4932a2b94b04f6d27bb9a"
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[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.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "redox_syscall"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "regex"
version = "1.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
[[package]]
name = "ring"
version = "0.17.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
dependencies = [
"cc",
"cfg-if",
"getrandom",
"libc",
"spin 0.9.8",
"untrusted",
"windows-sys 0.52.0",
]
[[package]]
name = "rsa"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc"
dependencies = [
"const-oid",
"digest",
"num-bigint-dig",
"num-integer",
"num-traits",
"pkcs1",
"pkcs8",
"rand_core",
"signature",
"spki",
"subtle",
"zeroize",
]
[[package]]
name = "rustc-demangle"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustix"
version = "0.38.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89"
dependencies = [
"bitflags 2.5.0",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.52.0",
]
[[package]]
name = "rustls"
version = "0.23.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402"
dependencies = [
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-native-certs"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a88d6d420651b496bdd98684116959239430022a115c1240e6c3993be0b15fba"
dependencies = [
"openssl-probe",
"rustls-pemfile",
"rustls-pki-types",
"schannel",
"security-framework",
]
[[package]]
name = "rustls-pemfile"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d"
dependencies = [
"base64",
"rustls-pki-types",
]
[[package]]
name = "rustls-pki-types"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
[[package]]
name = "rustls-webpki"
version = "0.102.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]]
name = "ryu"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
[[package]]
name = "schannel"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "security-framework"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0"
dependencies = [
"bitflags 2.5.0",
"core-foundation",
"core-foundation-sys",
"libc",
"security-framework-sys",
]
[[package]]
name = "security-framework-sys"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "serde"
version = "1.0.198"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde-value"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
dependencies = [
"ordered-float",
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.198"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.60",
]
[[package]]
name = "serde_json"
version = "1.0.116"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "serde_yaml"
version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [
"indexmap 2.2.6",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]]
name = "sha1"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "sha2"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
dependencies = [
"libc",
]
[[package]]
name = "signature"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
dependencies = [
"digest",
"rand_core",
]
[[package]]
name = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
[[package]]
name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "socket2"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "spki"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
dependencies = [
"base64ct",
"der",
"sha2",
]
[[package]]
name = "stun-bytes"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bebd666814c8af1e06e4d489f9c3d03a4aef20bae651550c52e6e95e09f3f12"
dependencies = [
"r-ex",
]
[[package]]
name = "stun-format"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6ad3c54fde6dfd6fe5048a71515b392d22c29730533663b5300e1b511d78df5"
dependencies = [
"r-ex",
"stun-bytes",
]
[[package]]
name = "subtle"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "sudo"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88bd84d4c082e18e37fef52c0088e4407dabcef19d23a607fb4b5ee03b7d5b83"
dependencies = [
"libc",
"log",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "sys-locale"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e801cf239ecd6ccd71f03d270d67dd53d13e90aab208bf4b8fe4ad957ea949b0"
dependencies = [
"libc",
]
[[package]]
name = "tempfile"
version = "3.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
dependencies = [
"cfg-if",
"fastrand",
"rustix",
"windows-sys 0.52.0",
]
[[package]]
name = "thiserror"
version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.60",
]
[[package]]
name = "thread-id"
version = "4.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0ec81c46e9eb50deaa257be2f148adf052d1fb7701cfd55ccfab2525280b70b"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "tinyvec"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787"
dependencies = [
"backtrace",
"bytes",
"libc",
"mio",
"num_cpus",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.48.0",
]
[[package]]
name = "tokio-macros"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.60",
]
[[package]]
name = "tokio-rustls"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
dependencies = [
"rustls",
"rustls-pki-types",
"tokio",
]
[[package]]
name = "tokio-tungstenite"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd"
dependencies = [
"futures-util",
"log",
"rustls",
"rustls-native-certs",
"rustls-pki-types",
"tokio",
"tokio-rustls",
"tungstenite",
"webpki-roots",
]
[[package]]
name = "tun"
version = "0.1.0"
dependencies = [
"ioctl-sys",
"libc",
"libloading",
"log",
"rand",
"sha2",
"widestring",
"winapi",
]
[[package]]
name = "tungstenite"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8"
dependencies = [
"byteorder",
"bytes",
"data-encoding",
"http 1.1.0",
"httparse",
"log",
"rand",
"rustls",
"rustls-pki-types",
"sha1",
"thiserror",
"utf-8",
]
[[package]]
name = "typemap-ors"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a68c24b707f02dd18f1e4ccceb9d49f2058c2fb86384ef9972592904d7a28867"
dependencies = [
"unsafe-any-ors",
]
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-bidi"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-normalization"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-width"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
[[package]]
name = "universal-hash"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
dependencies = [
"crypto-common",
"subtle",
]
[[package]]
name = "unsafe-any-ors"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a303d30665362d9680d7d91d78b23f5f899504d4f08b3c4cf08d055d87c0ad"
dependencies = [
"destructure_traitobject",
]
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "url"
version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
]
[[package]]
name = "utf-8"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "uuid"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
dependencies = [
"getrandom",
]
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "vn-link"
version = "1.2.12"
dependencies = [
"anyhow",
"crossbeam-utils",
"log",
"lwip-rs",
"parking_lot",
"tokio",
"vnt",
]
[[package]]
name = "vn-link-cli"
version = "1.2.12"
dependencies = [
"common",
"log",
"tokio",
"vn-link",
]
[[package]]
name = "vnt"
version = "1.2.12"
dependencies = [
"aes",
"aes-gcm",
"anyhow",
"bytes",
"cbc",
"cfg_aliases",
"chacha20",
"chacha20poly1305",
"crossbeam-epoch",
"crossbeam-queue",
"crossbeam-utils",
"dns-parser",
"ecb",
"fnv",
"futures-util",
"igd",
"libc",
"libloading",
"libsm",
"log",
"lz4_flex",
"mio",
"openssl-sys",
"packet",
"parking_lot",
"protobuf",
"protobuf-codegen",
"protoc-bin-vendored",
"rand",
"ring",
"rsa",
"rustls",
"sha2",
"socket2",
"spki",
"stun-format",
"thiserror",
"tokio",
"tokio-tungstenite",
"tun",
"zstd",
]
[[package]]
name = "vnt-cli"
version = "1.2.12"
dependencies = [
"anyhow",
"chrono",
"common",
"log",
"rand",
"signal-hook",
"sudo",
"vnt",
"winapi",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.60",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.60",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
[[package]]
name = "webpki-roots"
version = "0.26.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "which"
version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
dependencies = [
"either",
"home",
"once_cell",
"rustix",
]
[[package]]
name = "widestring"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311"
[[package]]
name = "wildmatch"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f44b95f62d34113cf558c93511ac93027e03e9c29a60dd0fd70e6e025c7270a"
[[package]]
name = "winapi"
version = "0.3.9"
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-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.5",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.5",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
"windows_aarch64_gnullvm 0.52.5",
"windows_aarch64_msvc 0.52.5",
"windows_i686_gnu 0.52.5",
"windows_i686_gnullvm",
"windows_i686_msvc 0.52.5",
"windows_x86_64_gnu 0.52.5",
"windows_x86_64_gnullvm 0.52.5",
"windows_x86_64_msvc 0.52.5",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
[[package]]
name = "xml-rs"
version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193"
[[package]]
name = "xmltree"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7d8a75eaf6557bb84a65ace8609883db44a29951042ada9b393151532e41fcb"
dependencies = [
"xml-rs",
]
[[package]]
name = "yasna"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd"
dependencies = [
"num-bigint",
]
[[package]]
name = "zeroize"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
[[package]]
name = "zstd"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a"
dependencies = [
"zstd-safe",
]
[[package]]
name = "zstd-safe"
version = "7.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a"
dependencies = [
"zstd-sys",
]
[[package]]
name = "zstd-sys"
version = "2.0.10+zstd.1.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa"
dependencies = [
"cc",
"pkg-config",
]
+37 -127
View File
@@ -1,12 +1,12 @@
# Vnt
# VNT
A virtual network tool (VPN)
🚀An efficient VPN
将不同网络下的多个设备虚拟到一个局域网下
🚀一个简单、高效、能快速组建虚拟局域网的工具
### vnt-cli参数详解 [参数说明](https://github.com/vnt-dev/vnt/blob/main/vnt-cli/README.md)
### 快速使用
### 快速开始
1. 指定一个token,在多台设备上运行该程序,例如:
```shell
@@ -42,24 +42,23 @@ A virtual network tool (VPN)
<img width="506" alt="ssh" src="https://raw.githubusercontent.com/vnt-dev/vnt/main/documents/img/ssh.jpg">
5. 帮助,使用-h命令查看
### 更多玩法
1. 和远程桌面(如mstsc)搭配,超低延迟的体验
2. 安装samba服务,共享磁盘
3. 搭配公网服务器nginx反向代理,在公网访问内网文件或服务
4. 点对网,访问内网其他机器、IP代理(结合启动参数'-i'和'-o')
### 使用须知
- token的作用是标识一个虚拟局域网,当使用公共服务器时,建议使用一个唯一值当token(比如uuid),否则有可能连接到其他人创建的虚拟局域网中
- 默认使用公共服务器做注册和中继,目前的配置是2核4G 4Mbps,有需要再扩展~
- 需要root/管理员权限
- vnt-cli需要使用命令行运行
- Mac和Linux下需要加可执行权限(例如:chmod +x ./vnt-cli)
- 可以自己搭注册和中继服务器([server](https://github.com/vnt-dev/vnts))
- vnt使用stun服务器探测网络NAT类型,默认使用谷歌和腾讯的stun服务器,也可自己搭建(-e参数指定)
- 可以自己搭中继服务器([server](https://github.com/vnt-dev/vnts))
### 编译
### 直接使用
[**下载release文件**](https://github.com/vnt-dev/vnt/releases)
[**帮助文档**](https://rustvnt.com)
### 自行编译
<details> <summary>点击展开</summary>
前提条件:安装rust编译环境([install rust](https://www.rust-lang.org/zh-CN/tools/install))
@@ -91,82 +90,9 @@ features说明
| file_config | yaml配置文件 | 是 |
| lz4 | lz4压缩 | 是 |
| zstd | zstd压缩 | 否 |
### 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
[![Packaging status](https://repology.org/badge/vertical-allrepos/vnt.svg)](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
```
| upnp | upnp协议 | 否 |
| ws | ws协议 | 是 |
| wss | wss协议 | 是 |
</details>
@@ -176,7 +102,7 @@ sudo pfctl -f /etc/pf.conf -e
- Linux
- Windows
- 默认使用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)
- 可选择使用tap网卡 依赖tap-windows([win-tap](https://build.openvpn.net/downloads/releases/))(建议使用版本9.24.7)
- Android
### GUI
@@ -186,50 +112,28 @@ sudo pfctl -f /etc/pf.conf -e
### 特性
- IP层数据转发
- tun虚拟网卡
- tap虚拟网卡
- NAT穿透
- 点对点穿透
- 服务端中继转发
- 客户端中继转发
- IP代理
- IP代理(点对点、点对网)
- p2p组播/广播
- 客户端数据加密
- 服务端数据加密
- 客户端数据加密(`aes-gcm`、`chacha20-poly1305`等多种加密算法)
- 服务端数据加密(`rsa` + `aes-gcm`)
- 多通道UDP应对QOS
- 支持TCP、UDP、WebSocket等多种协议
- 支持数据压缩
### 结构
### 更多玩法
<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>
1. 和远程桌面(如mstsc)搭配,超低延迟的体验
2. 安装samba服务,共享磁盘
3. 点对网,访问内网其他机器、IP代理(结合启动参数'-i'和'-o')
### Todo
- 桌面UI(测试中)
- ~~桌面UI(已支持)~~
- 使用FEC、ARQ等方式提升弱网环境的稳定性
### 常见问题
@@ -287,8 +191,14 @@ vnt默认使用10.26.0.0/24网段,和本地网络适配器的ip冲突
对VNT有任何问题均可以加群联系作者
QQ: 1034868233
QQ群1: 1034868233(满人)
QQ群2: 950473757
QQ群3: 1060550456
### 赞助
如果VNT对你有帮助,欢迎打赏作者
<img width="300" alt="" src="https://github.com/vnt-dev/vnt/assets/49143209/0d3a7311-43fc-4ed7-9507-863b5d69b6b2">
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "common"
version = "1.2.12"
version = "1.2.16"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+3 -2
View File
@@ -10,7 +10,7 @@ impl VntCallback for VntHandler {
fn success(&self) {
println!(" {} ", style("====== Connect Successfully ======").green())
}
#[cfg(feature = "vnt-model")]
#[cfg(feature = "integrated_tun")]
fn create_tun(&self, info: vnt::DeviceInfo) {
println!("create_tun {}", info)
}
@@ -37,7 +37,8 @@ impl VntCallback for VntHandler {
| ErrorType::AddressExhausted
| ErrorType::IpAlreadyExists
| ErrorType::InvalidIp
| ErrorType::LocalIpExists => {
| ErrorType::LocalIpExists
| ErrorType::FailedToCrateDevice => {
self.stop();
}
_ => {}
+23 -22
View File
@@ -76,6 +76,7 @@ pub fn parse_args_config() -> anyhow::Result<Option<(Config, Vec<String>, bool)>
opts.optmulti("", "vnt-mapping", "vnt-mapping", "<mapping>");
opts.optopt("f", "", "配置文件", "<conf>");
opts.optopt("", "compressor", "压缩算法", "<lz4>");
opts.optopt("", "local-dev", "指定本地ipv4网卡名称", "<NAME>");
opts.optflag("", "disable-stats", "关闭流量统计");
opts.optflag("", "allow-wg", "允许接入WireGuard");
//"后台运行时,查看其他设备列表"
@@ -140,9 +141,6 @@ pub fn parse_args_config() -> anyhow::Result<Option<(Config, Vec<String>, bool)>
print_usage(&program, opts);
return Err(anyhow::anyhow!("parameter -k not found ."));
}
#[cfg(target_os = "windows")]
#[cfg(feature = "integrated_tun")]
let tap = matches.opt_present("a");
#[cfg(feature = "integrated_tun")]
let device_name = matches.opt_str("nic");
let token: String = matches.opt_get("k").unwrap().unwrap();
@@ -283,6 +281,8 @@ pub fn parse_args_config() -> anyhow::Result<Option<(Config, Vec<String>, bool)>
#[cfg(feature = "port_mapping")]
let port_mapping_list = matches.opt_strs("mapping");
let vnt_mapping_list = matches.opt_strs("vnt-mapping");
let local_dev: Option<String> = matches.opt_get("local-dev").unwrap();
let disable_stats = matches.opt_present("disable-stats");
let allow_wire_guard = matches.opt_present("allow-wg");
let compressor = if let Some(compressor) = matches.opt_str("compressor").as_ref() {
@@ -292,10 +292,10 @@ pub fn parse_args_config() -> anyhow::Result<Option<(Config, Vec<String>, bool)>
} else {
Compressor::None
};
let config = match Config::new(
let config = Config::new(
#[cfg(feature = "integrated_tun")]
#[cfg(target_os = "windows")]
tap,
false,
token,
device_id,
name,
@@ -326,13 +326,8 @@ pub fn parse_args_config() -> anyhow::Result<Option<(Config, Vec<String>, bool)>
compressor,
!disable_stats,
allow_wire_guard,
) {
Ok(config) => config,
Err(e) => {
println!("config error: {}", e);
std::process::exit(1);
}
};
local_dev,
)?;
(config, vnt_mapping_list, cmd)
};
println!("version {}", vnt::VNT_VERSION);
@@ -353,17 +348,16 @@ fn get_description(key: &str, language: &str) -> String {
("-d <id>", ("设备唯一标识符,不使用--ip参数时,服务端凭此参数分配虚拟ip,注意不能重复", "Device unique identifier, used by the server to allocate virtual IP when --ip parameter is not used, must be unique")),
("-s <server>", ("注册和中继服务器地址,协议支持使用tcp://和ws://和wss://,默认为udp://", "Registration and relay server address, protocols support using tcp://, ws://, and wss://, default is udp://")),
("-e <stun-server>", ("stun服务器,用于探测NAT类型,可使用多个地址,如-e stun.miwifi.com -e turn.cloudflare.com", "STUN server for detecting NAT type, can specify multiple addresses, e.g., -e stun.miwifi.com -e turn.cloudflare.com")),
("-a", ("使用tap模式,默认使用tun模式,使用tap时需要配合'--nic'参数指定tap网卡", "Use tap mode, default is tun mode, specify '--nic' parameter with tap network card")),
("-i <in-ip>", ("配置点对网(IP代理)时使用,-i 192.168.0.0/24,10.26.0.3表示允许接收网段192.168.0.0/24的数据并转发到10.26.0.3,可指定多个网段", "Used when configuring point-to-point network (IP proxy), -i 192.168.0.0/24,10.26.0.3 allows receiving data from subnet 192.168.0.0/24 and forwarding to 10.26.0.3, specify multiple subnets")),
("-o <out-ip>", ("配置点对网时使用,-o 192.168.0.0/24表示允许将数据转发到192.168.0.0/24,可指定多个网段", "Used when configuring point-to-point network, -o 192.168.0.0/24 allows forwarding data to 192.168.0.0/24, specify multiple subnets")),
("-w <password>", ("使用该密码生成的密钥对客户端数据进行加密,并且服务端无法解密,使用相同密码的客户端才能通信", "Encrypt client data with keys generated by this password, server cannot decrypt, clients must use the same password to communicate")),
("-W", ("加密当前客户端和服务端通信的数据,请留意服务端指纹是否正确", "Encrypt the data currently being communicated between the client and server, please pay attention to whether the server fingerprint is correct")),
("-u <mtu>", ("自定义mtu(不加密默认为1450,加密默认为1410", "Customize MTU (1450 by default without encryption, 1410 with encryption)")),
("-u <mtu>", ("自定义mtu(默认为1420)", "Customize MTU (default is 1420)")),
("-f <conf_file>", ("读取配置文件中的配置", "Read configuration from file")),
("--ip <ip>", ("指定虚拟ip,指定的ip不能和其他设备重复,必须有效并且在服务端所属网段下,默认情况由服务端分配", "Specify virtual IP, must be unique and valid within server subnet, by default allocated by server")),
("--model <model>", ("加密模式(默认aes_gcm),可选值{}", "Encryption mode (default aes_gcm), options {}")),
("--model <model>", ("加密模式(默认aes_gcm),可选值", "Encryption mode (default aes_gcm), options ")),
("--finger", ("增加数据指纹校验,可增加安全性,如果服务端开启指纹校验,则客户端也必须开启", "Add data fingerprint verification for increased security, client must enable if server does")),
("--punch <punch>", ("取值ipv4/ipv6/all,ipv4表示仅使用ipv4打洞", "Values ipv4/ipv6/all, ipv4 for IPv4 hole punching only")),
("--punch <punch>", ("取值ipv4/ipv6/ipv4-tcp/ipv4-udp/ipv6-tcp/ipv6-udp/all,ipv4表示仅使用ipv4打洞", "Values ipv4/ipv6/ipv4-tcp/ipv4-udp/ipv6-tcp/ipv6-udp/all, ipv4 for IPv4 hole punching only")),
("--ports <port,port>", ("取值0~65535,指定本地监听的一组端口,默认监听两个随机端口,使用过多端口会增加网络负担", "Values 0~65535, specify a group of local listening ports, defaults to two random ports, using many ports increases network load")),
("--cmd", ("开启交互式命令,使用此参数开启控制台输入", "Enable interactive command mode, use this parameter to enable console input")),
("--no-proxy", ("关闭内置代理,如需点对网则需要配置网卡NAT转发", "Disable built-in proxy, configure network card NAT forwarding for point-to-point networking")),
@@ -371,14 +365,16 @@ fn get_description(key: &str, language: &str) -> String {
("--use-channel <p2p>", ("使用通道 relay/p2p/all,默认两者都使用", "Use channel relay/p2p/all, defaults to using both")),
("--nic <tun0>", ("指定虚拟网卡名称", "Specify virtual network card name")),
("--packet-loss <0>", ("模拟丢包,取值0~1之间的小数,程序会按设定的概率主动丢包,可用于模拟弱网", "Simulate packet loss, value between 0 and 1, program actively drops packets based on set probability, useful for simulating weak networks")),
("--packet-delay <0>", ("模拟丢包,取值0~1之间的小数,程序会按设定的概率主动丢包,可用于模拟弱网", "Simulate latency, integer, in milliseconds (ms). The program will delay sending packets according to the set value and can be used to simulate weak networks")),
("--packet-delay <0>", ("模拟延迟,正整数,单位毫秒,程序将根据设定值延迟发送数据包,可用于模拟弱网", "Simulate latency, integer, in milliseconds (ms). The program will delay sending packets according to the set value and can be used to simulate weak networks")),
("--dns <host:port>", ("DNS服务器地址,可使用多个dns,不指定时使用系统解析", "DNS server address, can specify multiple DNS servers, defaults to system resolution if not specified")),
("--mapping <mapping>", ("端口映射,例如 --mapping udp:0.0.0.0:80-domain:80 映射目标是本地路由能访问的设备", "Port mapping, e.g., --mapping udp:0.0.0.0:80-domain:80 maps to a device accessible by local routing")),
("--compressor-all <lz4>", ("启用压缩,可选值lz4/zstd<,level>,level为压缩级别,例如 --compressor lz4 或--compressor zstd,10", "Enable compression, options lz4/zstd<,level>, level is compression level, e.g., --compressor lz4 or --compressor zstd,10")),
("--compressor-lz4 <lz4>", ("启用压缩,可选值lz4,例如 --compressor lz4", "Enable compression, option lz4, e.g., --compressor lz4")),
("--compressor-zstd <zstd>", ("启用压缩,可选值zstd<,level>,level为压缩级别,例如 --compressor zstd,10", "Enable compression, options zstd<,level>, level is compression level, e.g., --compressor zstd,10")),
("--vnt-mapping <x>", ("vnt地址映射,例如 --vnt-mapping tcp:80-10.26.0.10:80 映射目标是vnt网络或其子网中的设备", "VNT address mapping, e.g., --vnt-mapping tcp:80-10.26.0.10:80 maps to a device in VNT network or its subnet")),
("--local-dev", ("本地出口网卡的名称", "name of local export network card")),
("--disable-stats", ("关闭流量统计", "Disable traffic statistics")),
("--allow-wg", ("允许接入WireGuard客户端", "Allow access to WireGuard client")),
("--list", ("后台运行时,查看其他设备列表", "View list of other devices when running in background")),
("--all", ("后台运行时,查看其他设备完整信息", "View complete information of other devices when running in background")),
("--info", ("后台运行时,查看当前设备信息", "View information of current device when running in background")),
@@ -430,9 +426,7 @@ fn print_usage(program: &str, _opts: Options) {
" -e <stun-server> {}",
get_description("-e <stun-server>", &language)
);
#[cfg(target_os = "windows")]
#[cfg(feature = "integrated_tun")]
println!(" -a {}", get_description("-a", &language));
println!(
" -i <in-ip> {}",
get_description("-i <in-ip>", &language)
@@ -517,7 +511,7 @@ fn print_usage(program: &str, _opts: Options) {
" --use-channel <p2p> {}",
get_description("--use-channel <p2p>", &language)
);
#[cfg(not(feature = "vn-link-model"))]
#[cfg(feature = "integrated_tun")]
println!(
" --nic <tun0> {}",
get_description("--nic <tun0>", &language)
@@ -564,11 +558,18 @@ fn print_usage(program: &str, _opts: Options) {
" --vnt-mapping <x> {}",
green(get_description("--vnt-mapping <x>", &language).to_string())
);
println!(
" --local-dev <NAME> {}",
get_description("--local-dev", &language)
);
println!(
" --disable-stats {}",
get_description("--disable-stats", &language)
);
println!(" --allow-wg 允许接入WireGuard客户端");
println!(
" --allow-wg {}",
get_description("--allow-wg", &language)
);
println!();
#[cfg(feature = "command")]
{
+9 -6
View File
@@ -48,6 +48,7 @@ pub struct FileConfig {
pub disable_stats: bool,
// 允许传递wg流量
pub allow_wire_guard: bool,
pub local_dev: Option<String>,
}
impl Default for FileConfig {
@@ -93,6 +94,7 @@ impl Default for FileConfig {
vnt_mapping: vec![],
disable_stats: false,
allow_wire_guard: false,
local_dev: None,
}
}
}
@@ -102,8 +104,8 @@ pub fn read_config(file_path: &str) -> anyhow::Result<(Config, Vec<String>, bool
let file_conf = match serde_yaml::from_str::<FileConfig>(&conf) {
Ok(val) => val,
Err(e) => {
log::error!("{:?}", e);
return Err(anyhow!("{}", e));
log::error!("serde_yaml::from_str {:?}", e);
return Err(anyhow!("serde_yaml::from_str {:?}", e));
}
};
if file_conf.token.is_empty() {
@@ -126,12 +128,12 @@ pub fn read_config(file_path: &str) -> anyhow::Result<(Config, Vec<String>, bool
None => None,
Some(r) => Some(r.map_err(|e| anyhow!("ip {:?} error:{}", &file_conf.ip, e))?),
};
let cipher_model = {
let cipher_model = if let Some(v) = file_conf.cipher_model {
CipherModel::from_str(&v).map_err(|e| anyhow!("{}", e))?
} else {
#[cfg(not(any(feature = "aes_gcm", feature = "server_encrypt")))]
if file_conf.password.is_some() && file_conf.cipher_model.is_none() {
if file_conf.password.is_some() {
Err(anyhow!("cipher_model undefined"))?
} else if let Some(v) = file_conf.cipher_model {
CipherModel::from_str(&v).map_err(|e| anyhow!("{}", e))?
} else {
CipherModel::None
}
@@ -181,6 +183,7 @@ pub fn read_config(file_path: &str) -> anyhow::Result<(Config, Vec<String>, bool
compressor,
!file_conf.disable_stats,
file_conf.allow_wire_guard,
file_conf.local_dev,
)?;
Ok((config, file_conf.vnt_mapping, file_conf.cmd))
+4 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "vn-link-cli"
version = "1.2.12"
version = "1.2.16"
edition = "2021"
[dependencies]
@@ -9,6 +9,9 @@ common = { path = "../common", default-features = false }
tokio = { version = "1.37.0", features = ["full"] }
log = "0.4.17"
[target.'cfg(windows)'.build-dependencies]
thunk-rs = { version = "0.3.3", features = ["win7"] }
[features]
default = ["default-feature"]
default-feature = ["server_encrypt", "aes_gcm", "aes_cbc", "aes_ecb", "sm4_cbc", "chacha20_poly1305", "port_mapping", "log", "command", "file_config", "lz4", "ws"]
+5
View File
@@ -0,0 +1,5 @@
fn main() {
// 配置 thunk-rs 来链接 Windows 7 兼容库,并自动设置链接参数
#[cfg(target_os = "windows")]
thunk::thunk();
}
+6 -1
View File
@@ -12,7 +12,12 @@ fn main() {
}
}
Err(e) => {
println!("{}", e);
log::error!(
"parse error={:?} cmd={:?}",
e,
std::env::args().collect::<Vec<String>>()
);
println!("Error {:?}", e);
return;
}
};
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "vn-link"
version = "1.2.12"
version = "1.2.16"
edition = "2021"
[dependencies]
+7 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "vnt-cli"
version = "1.2.12"
version = "1.2.16"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -10,7 +10,7 @@ vnt = { path = "../vnt", package = "vnt", default-features = false, features = [
common = { path = "../common", default-features = false, features = ["integrated_tun"] }
log = "0.4.17"
anyhow = "1.0.82"
console = "0.15.2"
[target.'cfg(any(target_os = "linux",target_os = "macos"))'.dependencies]
sudo = "0.6.0"
@@ -19,6 +19,7 @@ signal-hook = "0.3.17"
[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3.9", features = ["handleapi", "processthreadsapi", "winnt", "securitybaseapi", "impl-default"] }
[features]
default = ["default-feature"]
default-feature = ["server_encrypt", "aes_gcm", "aes_cbc", "aes_ecb", "sm4_cbc", "chacha20_poly1305", "ip_proxy", "port_mapping", "log", "command", "file_config", "lz4", "ws"]
@@ -44,4 +45,7 @@ command = ["common/command"]
file_config = ["common/file_config"]
[build-dependencies]
rand = "0.8.5"
chrono = "0.4.23"
chrono = "0.4.23"
[target.'cfg(windows)'.build-dependencies]
thunk-rs = { version = "0.3.3", features = ["win7"] }
+4 -2
View File
@@ -74,9 +74,11 @@
设置虚拟网卡的mtu值,大多数情况下使用默认值效率会更高,也可根据实际情况微调这个值,不加密默认为1450,加密默认为1410
### --tcp
### ~~--tcp~~
和服务端使用tcp通信。有些网络提供商对UDP限制比较大,这个时候可以选择使用TCP模式,提高稳定性。一般来说udp延迟和消耗更低
~~和服务端使用tcp通信。有些网络提供商对UDP限制比较大,这个时候可以选择使用TCP模式,提高稳定性。一般来说udp延迟和消耗更低~~
新版本使用 `-s tcp://`的形式使用tcp
### --ip `<IP>`
+5
View File
@@ -0,0 +1,5 @@
fn main() {
// 配置 thunk-rs 来链接 Windows 7 兼容库,并自动设置链接参数
#[cfg(target_os = "windows")]
thunk::thunk();
}
+8 -1
View File
@@ -1,4 +1,5 @@
use common::callback;
use console::style;
use vnt::core::{Config, Vnt};
mod root_check;
fn main() {
@@ -11,7 +12,12 @@ fn main() {
}
}
Err(e) => {
println!("{}", e);
log::error!(
"parse error={:?} cmd={:?}",
e,
std::env::args().collect::<Vec<String>>()
);
println!("{}", style(format!("Error {:?}", e)).red());
return;
}
};
@@ -35,6 +41,7 @@ fn main0(config: Config, _show_cmd: bool) {
let vnt_util = match Vnt::new(config, callback::VntHandler {}) {
Ok(vnt) => vnt,
Err(e) => {
log::error!("vnt create error {:?}", e);
println!("error: {:?}", e);
std::process::exit(1);
}
+19 -8
View File
@@ -1,12 +1,12 @@
[package]
name = "vnt"
version = "1.2.12"
version = "1.2.16"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
tun = { path = "tun", optional = true }
tun-rs = { version = "2.5.0", optional = true, features = ["interruptible"] }
packet = { path = "./packet" }
bytes = "1.5.0"
log = "0.4.17"
@@ -18,7 +18,7 @@ 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"] }
socket2 = { version = "0.5.7", features = ["all"] }
aes-gcm = { version = "0.10.2", optional = true }
ring = { version = "0.17.0", optional = true }
cbc = { version = "0.1.2", optional = true }
@@ -31,13 +31,14 @@ 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/vnt-dev/rust-openssl", optional = true }
libsm = { git = "https://github.com/vnt-dev/libsm", optional = true }
http_req = { git = "https://github.com/lmq8267/http_req.git", default-features = false, features = ["rust-tls"] }
mio = { version = "=0.8.11", features = ["os-poll", "net", "os-ext"] }
crossbeam-queue = "0.3.11"
anyhow = "1.0.82"
dns-parser = "0.8.0"
tokio = { version = "1.37.0", features = ["full"] }
tokio = { version = "1.46.1", features = ["full"] }
lz4_flex = { version = "0.11", default-features = false, optional = true }
zstd = { version = "0.13.1", optional = true }
@@ -45,11 +46,21 @@ zstd = { version = "0.13.1", optional = true }
fnv = "1.0.7"
igd = { version = "0.12.1", optional = true }
tokio-tungstenite = { version = "0.23.1", optional = true }
rustls = { version = "0.23.0", features = ["ring"], default-features = false, optional = true }
rustls = { version = "0.23.0", features = ["ring", "tls12"], default-features = false, optional = true }
network-interface = "2.0.0"
futures-util = "0.3.30"
[target.'cfg(target_os = "windows")'.dependencies]
winreg = "0.55.0"
libloading = "0.8.0"
windows-sys = { version = "0.60.2", features = ["Win32_Foundation",
"Win32_NetworkManagement",
"Win32_NetworkManagement_IpHelper",
"Win32_Networking_WinSock",
"Win32_System_IO",
"Win32_System_Threading",
"Win32_System_WindowsProgramming", ] }
[build-dependencies]
protobuf-codegen = "=3.2.0"
@@ -72,7 +83,7 @@ ip_proxy = []
port_mapping = []
lz4_compress = ["lz4_flex"]
zstd_compress = ["zstd"]
integrated_tun = ["tun"]
integrated_tun = ["tun-rs"]
upnp = ["igd"]
ws = ["tokio-tungstenite"]
wss = ["ws", "tokio-tungstenite/rustls-tls-native-roots", "tokio-tungstenite/rustls-tls-webpki-roots", "rustls"]
wss = ["ws", "tokio-tungstenite/rustls-tls-native-roots", "tokio-tungstenite/rustls-tls-webpki-roots", "rustls"]
+4 -7
View File
@@ -71,14 +71,12 @@ unsigned short getChecksum(unsigned short * iphead, int count)
pub fn cal_checksum(buffer: &[u8]) -> u16 {
use std::io::Cursor;
let mut sum = 0;
let length = buffer.len();
let mut buffer = Cursor::new(buffer);
while let Ok(value) = buffer.read_u16::<BigEndian>() {
sum += u32::from(value);
}
if length & 1 == 1 {
//奇数,说明还有一位,不足的补0
sum += u32c(buffer.read_u8().unwrap(), 0);
if let Ok(l) = buffer.read_u8() {
sum += u32c(l, 0);
}
while sum >> 16 != 0 {
sum = (sum & 0xffff) + (sum >> 16);
@@ -119,9 +117,8 @@ pub fn ipv4_cal_checksum(
while let Ok(value) = buffer.read_u16::<BigEndian>() {
sum += u32::from(value);
}
if length & 1 == 1 {
//奇数,说明还有一位
sum += u32c(buffer.read_u8().unwrap(), 0);
if let Ok(l) = buffer.read_u8() {
sum += u32c(l, 0);
}
while sum >> 16 != 0 {
sum = (sum & 0xffff) + (sum >> 16);
+12
View File
@@ -64,11 +64,23 @@ message PunchInfo {
uint32 tcp_port = 11;
repeated uint32 udp_ports = 12;
repeated uint32 public_ports = 13;
uint32 public_tcp_port = 14;
PunchNatModel punch_model = 15;
}
enum PunchNatType {
Symmetric = 0;
Cone = 1;
}
enum PunchNatModel {
All = 0;
IPv4 = 1;
IPv6 = 2;
IPv4Tcp = 3;
IPv4Udp = 4;
IPv6Tcp = 5;
IPv6Udp = 6;
}
/// 向服务器上报客户端状态信息
message ClientStatusInfo {
fixed32 source = 1;
+81 -89
View File
@@ -1,7 +1,7 @@
use fnv::FnvHashMap;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV6, UdpSocket};
use std::net::{Ipv4Addr, SocketAddr, UdpSocket};
use std::ops::Deref;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::atomic::AtomicUsize;
use std::sync::Arc;
use std::time::{Duration, Instant};
use std::{io, thread};
@@ -12,6 +12,7 @@ use rand::Rng;
use crate::channel::punch::NatType;
use crate::channel::sender::{AcceptSocketSender, PacketSender};
use crate::channel::socket::LocalInterface;
use crate::channel::{ConnectProtocol, Route, RouteKey, UseChannelType, DEFAULT_RT};
use crate::protocol::NetPacket;
use crate::util::limit::TrafficMeterMultiAddress;
@@ -25,16 +26,17 @@ pub struct ChannelContext {
impl ChannelContext {
pub fn new(
main_udp_socket: Vec<UdpSocket>,
v4_len: usize,
use_channel_type: UseChannelType,
first_latency: bool,
protocol: ConnectProtocol,
packet_loss_rate: Option<f64>,
packet_delay: u32,
use_ipv6: bool,
up_traffic_meter: Option<TrafficMeterMultiAddress>,
down_traffic_meter: Option<TrafficMeterMultiAddress>,
default_interface: LocalInterface,
) -> Self {
let channel_num = main_udp_socket.len();
let channel_num = v4_len;
assert_ne!(channel_num, 0, "not channel");
let packet_loss_rate = packet_loss_rate
.map(|v| {
@@ -48,16 +50,17 @@ impl ChannelContext {
.unwrap_or(0);
let inner = ContextInner {
main_udp_socket,
v4_len,
sub_udp_socket: RwLock::new(Vec::new()),
packet_map: RwLock::new(FnvHashMap::default()),
route_table: RouteTable::new(use_channel_type, first_latency, channel_num),
protocol,
packet_loss_rate,
packet_delay,
main_index: AtomicUsize::new(0),
use_ipv6,
up_traffic_meter,
down_traffic_meter,
default_interface,
default_route_key: AtomicCell::default(),
};
Self {
inner: Arc::new(inner),
@@ -80,10 +83,11 @@ const PACKET_LOSS_RATE_DENOMINATOR: u32 = 100_0000;
pub struct ContextInner {
// 核心udp socket
pub(crate) main_udp_socket: Vec<UdpSocket>,
v4_len: usize,
// 对称网络增加的udp socket
sub_udp_socket: RwLock<Vec<UdpSocket>>,
// tcp数据发送器
pub(crate) packet_map: RwLock<FnvHashMap<SocketAddr, PacketSender>>,
pub(crate) packet_map: RwLock<FnvHashMap<RouteKey, PacketSender>>,
// 路由信息
pub route_table: RouteTable,
// 使用什么协议连接服务器
@@ -92,16 +96,22 @@ pub struct ContextInner {
packet_loss_rate: u32,
//控制延迟
packet_delay: u32,
main_index: AtomicUsize,
use_ipv6: bool,
pub(crate) up_traffic_meter: Option<TrafficMeterMultiAddress>,
pub(crate) down_traffic_meter: Option<TrafficMeterMultiAddress>,
default_interface: LocalInterface,
default_route_key: AtomicCell<Option<RouteKey>>,
}
impl ContextInner {
pub fn use_channel_type(&self) -> UseChannelType {
self.route_table.use_channel_type
}
pub fn default_interface(&self) -> &LocalInterface {
&self.default_interface
}
pub fn set_default_route_key(&self, route_key: RouteKey) {
self.default_route_key.store(Some(route_key));
}
/// 通过sub_udp_socket是否为空来判断是否为锥形网络
pub fn is_cone(&self) -> bool {
self.sub_udp_socket.read().is_empty()
@@ -120,7 +130,7 @@ impl ContextInner {
&self,
nat_type: NatType,
udp_socket_sender: &AcceptSocketSender<Option<Vec<mio::net::UdpSocket>>>,
) -> io::Result<()> {
) -> anyhow::Result<()> {
let mut write_guard = self.sub_udp_socket.write();
match nat_type {
NatType::Symmetric => {
@@ -129,9 +139,11 @@ impl ContextInner {
}
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)?;
let udp = crate::channel::socket::bind_udp(
"0.0.0.0:0".parse().unwrap(),
&self.default_interface,
)?;
let udp: UdpSocket = udp.into();
vec.push(udp);
}
let mut mio_vec = Vec::with_capacity(SYMMETRIC_CHANNEL_NUM);
@@ -152,39 +164,39 @@ impl ContextInner {
}
Ok(())
}
#[inline]
pub fn channel_num(&self) -> usize {
self.v4_len
}
#[inline]
pub fn main_len(&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() {
for udp in self.main_udp_socket[..self.v4_len].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.packet_map.read().get(&addr) {
pub fn send_tcp(&self, buf: &[u8], route_key: &RouteKey) -> io::Result<()> {
if let Some(tcp) = self.packet_map.read().get(route_key) {
tcp.try_send(buf)
} else {
Err(io::Error::from(io::ErrorKind::NotFound))
Err(io::Error::new(
io::ErrorKind::NotFound,
format!("dest={:?}", route_key),
))
}
}
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,
));
}
pub fn send_main_udp(&self, index: usize, buf: &[u8], addr: SocketAddr) -> io::Result<()> {
if let Some(udp) = self.main_udp_socket.get(index) {
udp.send_to(buf, addr)?;
Ok(())
} else {
Err(io::Error::new(io::ErrorKind::Other, "overflow"))
}
self.main_udp_socket[index].send_to(buf, addr)?;
Ok(())
}
/// 将数据发送到默认通道,一般发往服务器才用此方法
pub fn send_default<B: AsRef<[u8]>>(
@@ -193,9 +205,20 @@ impl ContextInner {
addr: SocketAddr,
) -> io::Result<()> {
if self.protocol.is_udp() {
self.send_main_udp(self.main_index.load(Ordering::Relaxed), buf.buffer(), addr)?
if addr.is_ipv4() {
self.send_main_udp(0, buf.buffer(), addr)?
} else {
self.send_main_udp(self.v4_len, buf.buffer(), addr)?
}
} else {
self.send_tcp(buf.buffer(), addr)?
if let Some(key) = self.default_route_key.load() {
self.send_tcp(buf.buffer(), &key)?
} else {
return Err(io::Error::new(
io::ErrorKind::NotFound,
format!("dest={:?}", addr),
));
}
}
if let Some(up_traffic_meter) = &self.up_traffic_meter {
up_traffic_meter.add_traffic(buf.destination(), buf.data_len());
@@ -203,10 +226,6 @@ impl ContextInner {
Ok(())
}
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);
@@ -287,7 +306,7 @@ impl ContextInner {
if let Some(udp) = self
.sub_udp_socket
.read()
.get(route_key.index - self.main_udp_socket.len())
.get(route_key.index - self.main_len())
{
udp.send_to(buf.buffer(), route_key.addr)?;
} else {
@@ -296,7 +315,7 @@ impl ContextInner {
}
}
ConnectProtocol::TCP | ConnectProtocol::WS | ConnectProtocol::WSS => {
self.send_tcp(buf.buffer(), route_key.addr)?
self.send_tcp(buf.buffer(), &route_key)?
}
}
if let Some(up_traffic_meter) = &self.up_traffic_meter {
@@ -372,19 +391,11 @@ impl RouteTable {
let key = route.route_key();
if only_if_absent {
if let Some((_, list)) = self.route_table.read().get(&id) {
let mut p2p_num = 0;
for (x, _) in list {
if x.is_p2p() {
p2p_num += 1;
}
if x.route_key() == key {
return true;
}
}
if !self.first_latency && p2p_num >= self.channel_num {
// 非优先延迟的情况下,通道满了则不用再添加
return false;
}
}
}
let mut route_table = self.route_table.write();
@@ -409,61 +420,42 @@ impl RouteTable {
}
}
if exist {
// 这个排序还有待优化,因为后加入的大概率排最后,被直接淘汰的概率也大,可能导致更好的通道被移除了
list.sort_by_key(|(k, _)| k.rt);
//如果延迟都稳定了,则去除多余通道
for (route, _) in list.iter() {
if route.rt == DEFAULT_RT {
return true;
}
}
//延迟优先模式需要更多的通道探测延迟最低的路线
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());
}
if self.channel_num <= list.len() {
return false;
}
};
//增加路由表容量,避免波动
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())));
}
return true;
}
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);
}
// 直接移除会导致通道不稳定,所以废弃这个方法,后面改用多余通道不发心跳包,从而让通道自动过期
// 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())
+70 -27
View File
@@ -6,6 +6,7 @@ use tokio::sync::mpsc::channel;
use crate::channel::context::ChannelContext;
use crate::channel::handler::RecvChannelHandler;
use crate::channel::sender::{AcceptSocketSender, ConnectUtil};
use crate::channel::socket::{bind_udp, LocalInterface};
use crate::channel::tcp_channel::tcp_listen;
use crate::channel::udp_channel::udp_listen;
#[cfg(feature = "ws")]
@@ -19,6 +20,7 @@ pub mod idle;
pub mod notify;
pub mod punch;
pub mod sender;
pub mod socket;
pub mod tcp_channel;
pub mod udp_channel;
#[cfg(feature = "ws")]
@@ -177,7 +179,7 @@ pub struct RouteKey {
}
impl RouteKey {
pub(crate) fn new(protocol: ConnectProtocol, index: usize, addr: SocketAddr) -> Self {
pub(crate) const fn new(protocol: ConnectProtocol, index: usize, addr: SocketAddr) -> Self {
Self {
protocol,
index,
@@ -201,11 +203,13 @@ pub(crate) fn init_context(
protocol: ConnectProtocol,
packet_loss_rate: Option<f64>,
packet_delay: u32,
default_interface: LocalInterface,
up_traffic_meter: Option<TrafficMeterMultiAddress>,
down_traffic_meter: Option<TrafficMeterMultiAddress>,
) -> anyhow::Result<(ChannelContext, std::net::TcpListener)> {
assert!(!ports.is_empty(), "not channel");
let mut udps = Vec::with_capacity(ports.len());
let mut main_udp_socket_v4 = Vec::with_capacity(ports.len());
let mut main_udp_socket_v6 = Vec::with_capacity(ports.len());
//检查系统是否支持ipv6
let use_ipv6 = match socket2::Socket::new(socket2::Domain::IPV6, socket2::Type::DGRAM, None) {
Ok(_) => true,
@@ -215,40 +219,33 @@ pub(crate) fn init_context(
}
};
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)?;
socket
.set_only_v6(false)
.with_context(|| format!("set_only_v6 failed: {}", &address))?;
(socket, address)
let addr_v4: SocketAddr = format!("0.0.0.0:{}", port).parse().unwrap();
if use_ipv6 {
let (main_channel_v4, main_channel_v6) = bind_udp_v4_and_v6(*port, &default_interface)?;
main_udp_socket_v4.push(main_channel_v4);
main_udp_socket_v6.push(main_channel_v6);
} else {
let address: SocketAddr = format!("0.0.0.0:{}", port).parse().unwrap();
(
socket2::Socket::new(socket2::Domain::IPV4, socket2::Type::DGRAM, None)?,
address,
)
};
if let Err(e) = socket.set_recv_buffer_size(2 * 1024 * 1024) {
log::warn!("set_recv_buffer_size {:?}", e);
let socket = bind_udp(addr_v4, &default_interface)?;
let main_channel_v4: UdpSocket = socket.into();
main_udp_socket_v4.push(main_channel_v4);
}
socket
.bind(&address.into())
.with_context(|| format!("bind failed: {}", &address))?;
let main_channel: UdpSocket = socket.into();
udps.push(main_channel);
}
let mut main_udp_socket =
Vec::with_capacity(main_udp_socket_v4.len() + main_udp_socket_v6.len());
let v4_len = main_udp_socket_v4.len();
main_udp_socket.append(&mut main_udp_socket_v4);
main_udp_socket.append(&mut main_udp_socket_v6);
let context = ChannelContext::new(
udps,
main_udp_socket,
v4_len,
use_channel_type,
first_latency,
protocol,
packet_loss_rate,
packet_delay,
use_ipv6,
up_traffic_meter,
down_traffic_meter,
default_interface,
);
let port = context.main_local_udp_port()?[0];
@@ -265,7 +262,13 @@ pub(crate) fn init_context(
let socket = socket2::Socket::new(socket2::Domain::IPV4, socket2::Type::STREAM, None)?;
(socket, address)
};
socket
.set_reuse_address(true)
.context("set_reuse_address")?;
#[cfg(unix)]
if let Err(e) = socket.set_reuse_port(true) {
log::warn!("set_reuse_port {:?}", e)
}
if let Err(e) = socket.bind(&address.into()) {
if ports[0] == 0 {
//端口可能冲突,则使用任意端口
@@ -285,9 +288,49 @@ pub(crate) fn init_context(
}
socket.listen(128)?;
socket.set_nonblocking(true)?;
socket.set_nodelay(false)?;
socket.set_nodelay(true)?;
Ok((context, socket.into()))
}
fn bind_udp_v4_and_v6(
port: u16,
default_interface: &LocalInterface,
) -> anyhow::Result<(UdpSocket, UdpSocket)> {
let mut count = 0;
loop {
let addr_v4: SocketAddr = format!("0.0.0.0:{}", port).parse().unwrap();
let socket = bind_udp(addr_v4, default_interface)?;
if let Err(e) = socket.set_recv_buffer_size(2 * 1024 * 1024) {
log::warn!("set_recv_buffer_size {:?}", e);
}
let main_channel_v4: UdpSocket = socket.into();
let addr = main_channel_v4.local_addr()?;
let addr_v6: SocketAddr = format!("[::]:{}", addr.port()).parse().unwrap();
let socket = if port == 0 {
match bind_udp(addr_v6, default_interface) {
Ok(socket) => socket,
Err(e) => {
if count > 10 {
return Err(e);
}
if let Some(e) = e.downcast_ref::<std::io::Error>() {
if e.kind() == std::io::ErrorKind::AddrInUse {
count += 1;
continue;
}
}
Err(e)?
}
}
} else {
bind_udp(addr_v6, default_interface)?
};
if let Err(e) = socket.set_recv_buffer_size(2 * 1024 * 1024) {
log::warn!("set_recv_buffer_size {:?}", e);
}
let main_channel_v6: UdpSocket = socket.into();
return Ok((main_channel_v4, main_channel_v6));
}
}
pub(crate) fn init_channel<H>(
tcp_listener: std::net::TcpListener,
+161 -46
View File
@@ -1,4 +1,3 @@
use crossbeam_utils::atomic::AtomicCell;
use std::collections::HashMap;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use std::ops::{Div, Mul};
@@ -7,20 +6,46 @@ use std::sync::Arc;
use std::time::Duration;
use std::{io, thread};
use crossbeam_utils::atomic::AtomicCell;
use rand::prelude::SliceRandom;
use rand::Rng;
use crate::channel::context::ChannelContext;
use crate::channel::sender::ConnectUtil;
use crate::external_route::ExternalRoute;
use crate::handle::CurrentDeviceInfo;
use crate::nat::NatTest;
use crate::nat::{is_ipv4_global, NatTest};
use crate::proto::message::{PunchNatModel, PunchNatType};
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum PunchModel {
All,
IPv4,
IPv6,
All,
IPv4Tcp,
IPv4Udp,
IPv6Tcp,
IPv6Udp,
}
impl PunchModel {
pub fn use_tcp(&self) -> bool {
self != &PunchModel::IPv4Udp && self != &PunchModel::IPv6Udp
}
pub fn use_udp(&self) -> bool {
self != &PunchModel::IPv4Tcp && self != &PunchModel::IPv6Tcp
}
pub fn use_ipv6(&self) -> bool {
self == &PunchModel::All
|| self == &PunchModel::IPv6
|| self == &PunchModel::IPv6Tcp
|| self == &PunchModel::IPv6Udp
}
pub fn use_ipv4(&self) -> bool {
self == &PunchModel::All
|| self == &PunchModel::IPv4
|| self == &PunchModel::IPv4Tcp
|| self == &PunchModel::IPv4Udp
}
}
impl FromStr for PunchModel {
@@ -30,8 +55,15 @@ impl FromStr for PunchModel {
match s.to_lowercase().trim() {
"ipv4" => Ok(PunchModel::IPv4),
"ipv6" => Ok(PunchModel::IPv6),
"ipv4-tcp" => Ok(PunchModel::IPv4Tcp),
"ipv4-udp" => Ok(PunchModel::IPv4Udp),
"ipv6-tcp" => Ok(PunchModel::IPv6Tcp),
"ipv6-udp" => Ok(PunchModel::IPv6Udp),
"all" => Ok(PunchModel::All),
_ => Err(format!("not match '{}', enum: ipv4/ipv6/all", s)),
_ => Err(format!(
"not match '{}', enum: ipv4/ipv4-tcp/ipv4-udp/ipv6/ipv6-tcp/ipv6-udp/all",
s
)),
}
}
}
@@ -41,6 +73,33 @@ impl Default for PunchModel {
PunchModel::All
}
}
impl From<PunchModel> for PunchNatModel {
fn from(value: PunchModel) -> Self {
match value {
PunchModel::All => PunchNatModel::All,
PunchModel::IPv4 => PunchNatModel::IPv4,
PunchModel::IPv6 => PunchNatModel::IPv6,
PunchModel::IPv4Tcp => PunchNatModel::IPv4Tcp,
PunchModel::IPv4Udp => PunchNatModel::IPv4Udp,
PunchModel::IPv6Tcp => PunchNatModel::IPv6Tcp,
PunchModel::IPv6Udp => PunchNatModel::IPv6Udp,
}
}
}
impl Into<PunchModel> for PunchNatModel {
fn into(self) -> PunchModel {
match self {
PunchNatModel::All => PunchModel::All,
PunchNatModel::IPv4 => PunchModel::IPv4,
PunchNatModel::IPv6 => PunchModel::IPv6,
PunchNatModel::IPv4Tcp => PunchModel::IPv4Tcp,
PunchNatModel::IPv4Udp => PunchModel::IPv4Udp,
PunchNatModel::IPv6Tcp => PunchModel::IPv6Tcp,
PunchNatModel::IPv6Udp => PunchModel::IPv6Udp,
}
}
}
#[derive(Clone, Debug)]
pub struct NatInfo {
@@ -52,6 +111,8 @@ pub struct NatInfo {
pub(crate) ipv6: Option<Ipv6Addr>,
pub udp_ports: Vec<u16>,
pub tcp_port: u16,
pub public_tcp_port: u16,
pub punch_model: PunchModel,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
@@ -60,6 +121,29 @@ pub enum NatType {
Cone,
}
impl NatType {
pub fn is_cone(&self) -> bool {
self == &NatType::Cone
}
}
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 NatInfo {
pub fn new(
mut public_ips: Vec<Ipv4Addr>,
@@ -69,7 +153,9 @@ impl NatInfo {
mut ipv6: Option<Ipv6Addr>,
udp_ports: Vec<u16>,
tcp_port: u16,
public_tcp_port: u16,
mut nat_type: NatType,
punch_model: PunchModel,
) -> Self {
public_ips.retain(|ip| {
!ip.is_multicast()
@@ -99,7 +185,9 @@ impl NatInfo {
ipv6,
udp_ports,
tcp_port,
public_tcp_port,
nat_type,
punch_model,
}
}
pub fn update_addr(&mut self, index: usize, ip: Ipv4Addr, port: u16) -> bool {
@@ -113,7 +201,7 @@ impl NatInfo {
*public_port = port;
}
}
if crate::nat::is_ipv4_global(&ip) {
if is_ipv4_global(&ip) {
if !self.public_ips.contains(&ip) {
self.public_ips.push(ip);
updated = true;
@@ -122,6 +210,9 @@ impl NatInfo {
}
updated
}
pub fn update_tcp_port(&mut self, port: u16) {
self.public_tcp_port = port;
}
pub fn local_ipv4(&self) -> Option<Ipv4Addr> {
self.local_ipv4
}
@@ -187,9 +278,7 @@ pub struct Punch {
port_vec: Vec<u16>,
port_index: HashMap<Ipv4Addr, usize>,
punch_model: PunchModel,
is_tcp: bool,
connect_util: ConnectUtil,
external_route: ExternalRoute,
nat_test: NatTest,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
}
@@ -198,9 +287,7 @@ impl Punch {
pub fn new(
context: ChannelContext,
punch_model: PunchModel,
is_tcp: bool,
connect_util: ConnectUtil,
external_route: ExternalRoute,
nat_test: NatTest,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
) -> Self {
@@ -213,9 +300,7 @@ impl Punch {
port_vec,
port_index: HashMap::new(),
punch_model,
is_tcp,
connect_util,
external_route,
nat_test,
current_device,
}
@@ -227,7 +312,10 @@ impl Punch {
if self.nat_test.is_local_address(true, addr) {
return;
}
self.connect_util.try_connect_tcp(buf.to_vec(), addr);
if addr.ip().is_unspecified() || addr.port() == 0 {
return;
}
self.connect_util.try_connect_tcp_punch(buf.to_vec(), addr);
}
pub fn punch(
&mut self,
@@ -242,34 +330,58 @@ impl Punch {
return Ok(());
}
let device_info = self.current_device.load();
nat_info.public_ips.retain(|ip| {
self.external_route.route(ip).is_none() && device_info.not_in_network(*ip)
});
nat_info.local_ipv4.filter(|ip| {
self.external_route.route(ip).is_none() && device_info.not_in_network(*ip)
});
nat_info.ipv6.filter(|ip| {
if let Some(ip) = ip.to_ipv4() {
self.external_route.route(&ip).is_none()
} else {
true
}
});
if punch_tcp && self.is_tcp && nat_info.tcp_port != 0 {
nat_info
.public_ips
.retain(|ip| is_ipv4_global(ip) && device_info.not_in_network(*ip));
nat_info.public_ports.retain(|port| *port != 0);
nat_info.udp_ports.retain(|port| *port != 0);
nat_info.local_ipv4 = nat_info
.local_ipv4
.filter(|ip| device_info.not_in_network(*ip));
if punch_tcp && self.punch_model.use_tcp() && nat_info.punch_model.use_tcp() {
//向tcp发起连接
if let Some(ipv6_addr) = nat_info.local_tcp_ipv6addr() {
self.connect_tcp(buf, ipv6_addr)
if self.punch_model.use_ipv6() && nat_info.punch_model.use_ipv6() {
if let Some(ipv6_addr) = nat_info.local_tcp_ipv6addr() {
self.connect_tcp(buf, ipv6_addr)
}
}
//向tcp发起连接
if let Some(ipv4_addr) = nat_info.local_tcp_ipv4addr() {
self.connect_tcp(buf, ipv4_addr)
}
for ip in &nat_info.public_ips {
let addr = SocketAddr::V4(SocketAddrV4::new(*ip, nat_info.tcp_port));
self.connect_tcp(buf, addr)
if self.punch_model.use_ipv4() && nat_info.punch_model.use_ipv4() {
if let Some(ipv4_addr) = nat_info.local_tcp_ipv4addr() {
self.connect_tcp(buf, ipv4_addr)
}
for ip in &nat_info.public_ips {
let addr = SocketAddr::V4(SocketAddrV4::new(*ip, nat_info.tcp_port));
self.connect_tcp(buf, addr);
}
if nat_info.nat_type.is_cone() && nat_info.public_tcp_port != 0 {
for ip in &nat_info.public_ips {
let addr = SocketAddr::V4(SocketAddrV4::new(*ip, nat_info.public_tcp_port));
self.connect_tcp(buf, addr);
}
}
}
}
if !self.punch_model.use_udp() || !nat_info.punch_model.use_udp() {
return Ok(());
}
let channel_num = self.context.channel_num();
let main_len = self.context.main_len();
if self.punch_model.use_ipv6() && nat_info.punch_model.use_ipv6() {
for index in channel_num..main_len {
if let Some(ipv6_addr) = nat_info.local_udp_ipv6addr(index) {
if !self.nat_test.is_local_address(false, ipv6_addr) {
let rs = self.context.send_main_udp(index, buf, ipv6_addr);
log::info!("发送到ipv6地址:{:?},rs={:?} {}", ipv6_addr, rs, id);
}
}
}
}
if !self.punch_model.use_ipv4() || !nat_info.punch_model.use_ipv4() {
return Ok(());
}
for index in 0..channel_num {
if let Some(ipv4_addr) = nat_info.local_udp_ipv4addr(index) {
if !self.nat_test.is_local_address(false, ipv4_addr) {
@@ -277,20 +389,23 @@ impl Punch {
}
}
}
if self.punch_model != PunchModel::IPv4 {
for index in 0..channel_num {
if let Some(ipv6_addr) = nat_info.local_udp_ipv6addr(index) {
if !self.nat_test.is_local_address(false, ipv6_addr) {
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(());
}
// 可能是开放了端口的,需要打洞
for index in 0..channel_num {
for port in &nat_info.udp_ports {
if *port == 0 {
continue;
}
for ip in &nat_info.public_ips {
if ip.is_unspecified() {
continue;
}
let addr = SocketAddrV4::new(*ip, *port);
let _ = self.context.send_main_udp(index, buf, addr.into());
thread::sleep(Duration::from_millis(3));
}
}
}
match nat_info.nat_type {
NatType::Symmetric => {
// 假设对方绑定n个端口,通过NAT对外映射出n个 公网ip:公网端口,自己随机尝试k次的情况下
+9 -3
View File
@@ -239,13 +239,13 @@ impl PacketSender {
#[derive(Clone)]
pub struct ConnectUtil {
connect_tcp: Sender<(Vec<u8>, SocketAddr)>,
connect_tcp: Sender<(Vec<u8>, Option<u16>, SocketAddr)>,
connect_ws: Sender<(Vec<u8>, String)>,
}
impl ConnectUtil {
pub fn new(
connect_tcp: Sender<(Vec<u8>, SocketAddr)>,
connect_tcp: Sender<(Vec<u8>, Option<u16>, SocketAddr)>,
connect_ws: Sender<(Vec<u8>, String)>,
) -> Self {
Self {
@@ -254,7 +254,13 @@ impl ConnectUtil {
}
}
pub fn try_connect_tcp(&self, buf: Vec<u8>, addr: SocketAddr) {
if self.connect_tcp.try_send((buf, addr)).is_err() {
if self.connect_tcp.try_send((buf, None, addr)).is_err() {
log::warn!("try_connect_tcp failed {}", addr);
}
}
pub fn try_connect_tcp_punch(&self, buf: Vec<u8>, addr: SocketAddr) {
// 打洞的连接可以绑定随机端口
if self.connect_tcp.try_send((buf, Some(0), addr)).is_err() {
log::warn!("try_connect_tcp failed {}", addr);
}
}
+138
View File
@@ -0,0 +1,138 @@
use anyhow::{anyhow, Context};
use network_interface::{NetworkInterface, NetworkInterfaceConfig};
use socket2::Protocol;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
#[cfg(unix)]
mod unix;
#[cfg(windows)]
mod windows;
pub trait VntSocketTrait {
fn set_ip_unicast_if(&self, _interface: &LocalInterface) -> anyhow::Result<()> {
Ok(())
}
}
#[derive(Clone, Debug, Default)]
pub struct LocalInterface {
index: u32,
#[cfg(unix)]
name: Option<String>,
}
pub async fn connect_tcp(
addr: SocketAddr,
bind_port: u16,
default_interface: &LocalInterface,
) -> anyhow::Result<tokio::net::TcpStream> {
let socket = create_tcp0(addr.is_ipv4(), bind_port, default_interface)?;
Ok(socket.connect(addr).await?)
}
pub fn create_tcp(
v4: bool,
default_interface: &LocalInterface,
) -> anyhow::Result<tokio::net::TcpSocket> {
create_tcp0(v4, 0, default_interface)
}
pub fn create_tcp0(
v4: bool,
bind_port: u16,
default_interface: &LocalInterface,
) -> anyhow::Result<tokio::net::TcpSocket> {
let socket = if v4 {
socket2::Socket::new(
socket2::Domain::IPV4,
socket2::Type::STREAM,
Some(Protocol::TCP),
)?
} else {
socket2::Socket::new(
socket2::Domain::IPV6,
socket2::Type::STREAM,
Some(Protocol::TCP),
)?
};
if v4 {
if let Err(e) = socket.set_ip_unicast_if(default_interface) {
log::warn!("set_ip_unicast_if {:?}", e)
}
}
if bind_port != 0 {
socket
.set_reuse_address(true)
.context("set_reuse_address")?;
#[cfg(unix)]
if let Err(e) = socket.set_reuse_port(true) {
log::warn!("set_reuse_port {:?}", e)
}
if v4 {
let addr: SocketAddr = format!("0.0.0.0:{}", bind_port).parse().unwrap();
socket.bind(&addr.into())?;
} else {
socket.set_only_v6(true)?;
let addr: SocketAddr = format!("[::]:{}", bind_port).parse().unwrap();
socket.bind(&addr.into())?;
}
}
socket.set_nonblocking(true)?;
socket.set_nodelay(true)?;
Ok(tokio::net::TcpSocket::from_std_stream(socket.into()))
}
pub fn bind_udp_ops(
addr: SocketAddr,
only_v6: bool,
default_interface: &LocalInterface,
) -> anyhow::Result<socket2::Socket> {
let socket = if addr.is_ipv4() {
let socket = socket2::Socket::new(
socket2::Domain::IPV4,
socket2::Type::DGRAM,
Some(Protocol::UDP),
)?;
if let Err(e) = socket.set_ip_unicast_if(default_interface) {
log::warn!("set_ip_unicast_if {:?}", e)
}
socket
} else {
let socket = socket2::Socket::new(
socket2::Domain::IPV6,
socket2::Type::DGRAM,
Some(Protocol::UDP),
)?;
socket
.set_only_v6(only_v6)
.with_context(|| format!("set_only_v6 failed: {}", &addr))?;
socket
};
socket.set_nonblocking(true)?;
socket.bind(&addr.into())?;
Ok(socket)
}
pub fn bind_udp(
addr: SocketAddr,
default_interface: &LocalInterface,
) -> anyhow::Result<socket2::Socket> {
bind_udp_ops(addr, true, default_interface).with_context(|| format!("{}", addr))
}
pub fn get_interface(dest_name: String) -> anyhow::Result<(LocalInterface, Ipv4Addr)> {
let network_interfaces = NetworkInterface::show()?;
for iface in network_interfaces {
if iface.name == dest_name {
for addr in iface.addr {
if let IpAddr::V4(ip) = addr.ip() {
return Ok((
LocalInterface {
index: iface.index,
#[cfg(unix)]
name: Some(iface.name),
},
ip,
));
}
}
}
}
Err(anyhow!("No network card with name {} found", dest_name))
}
+46
View File
@@ -0,0 +1,46 @@
use crate::channel::socket::{LocalInterface, VntSocketTrait};
#[cfg(any(target_os = "linux", target_os = "macos"))]
use anyhow::Context;
#[cfg(target_os = "linux")]
impl VntSocketTrait for socket2::Socket {
fn set_ip_unicast_if(&self, interface: &LocalInterface) -> anyhow::Result<()> {
if let Some(name) = &interface.name {
self.bind_device(Some(name.as_bytes()))
.context("bind_device")?;
}
Ok(())
}
}
#[cfg(target_os = "macos")]
impl VntSocketTrait for socket2::Socket {
fn set_ip_unicast_if(&self, interface: &LocalInterface) -> anyhow::Result<()> {
if interface.index != 0 {
self.bind_device_by_index_v4(std::num::NonZeroU32::new(interface.index))
.with_context(|| format!("bind_device_by_index_v4 {:?}", interface))?;
}
Ok(())
}
}
#[cfg(target_os = "android")]
impl VntSocketTrait for socket2::Socket {
fn set_ip_unicast_if(&self, _interface: &LocalInterface) -> anyhow::Result<()> {
Ok(())
}
}
// #[cfg(any(target_os = "linux", target_os = "macos"))]
// pub fn get_best_interface(dest_ip: Ipv4Addr) -> anyhow::Result<LocalInterface> {
// match get_interface(dest_ip) {
// Ok(iface) => return Ok(iface),
// Err(e) => {
// log::warn!("not find interface e={:?},ip={}", e, dest_ip);
// }
// }
// // 应该再查路由表找到默认路由的
// Ok(LocalInterface::default())
// }
// #[cfg(target_os = "android")]
// pub fn get_best_interface(_dest_ip: Ipv4Addr) -> anyhow::Result<LocalInterface> {
// Ok(LocalInterface::default())
// }
+56
View File
@@ -0,0 +1,56 @@
use std::mem;
use std::os::windows::io::AsRawSocket;
use windows_sys::core::PCSTR;
use windows_sys::Win32::Networking::WinSock::{
htonl, setsockopt, IPPROTO_IP, IP_UNICAST_IF, SOCKET_ERROR,
};
use crate::channel::socket::{LocalInterface, VntSocketTrait};
impl VntSocketTrait for socket2::Socket {
fn set_ip_unicast_if(&self, interface: &LocalInterface) -> anyhow::Result<()> {
let index = interface.index;
if index == 0 {
return Ok(());
}
let raw_socket = self.as_raw_socket();
let result = unsafe {
let best_interface = htonl(index);
setsockopt(
raw_socket as usize,
IPPROTO_IP,
IP_UNICAST_IF,
&best_interface as *const _ as PCSTR,
mem::size_of_val(&best_interface) as i32,
)
};
if result == SOCKET_ERROR {
Err(anyhow::anyhow!(
"Failed to set IP_UNICAST_IF: {:?} {}",
std::io::Error::last_os_error(),
index
))?;
}
Ok(())
}
}
// pub fn get_best_interface(dest_ip: Ipv4Addr) -> anyhow::Result<LocalInterface> {
// // 获取最佳接口
// let index = unsafe {
// let mut dest: SOCKADDR_IN = mem::zeroed();
// dest.sin_family = AF_INET as u16;
// dest.sin_addr.S_un.S_addr = u32::from_ne_bytes(dest_ip.octets());
//
// let mut index: u32 = 0;
// if GetBestInterfaceEx(&dest as *const _ as *mut SOCKADDR, &mut index) != 0 {
// Err(anyhow::anyhow!(
// "Failed to GetBestInterfaceEx: {:?}",
// std::io::Error::last_os_error()
// ))?;
// }
// index
// };
// Ok(LocalInterface { index })
// }
+46 -20
View File
@@ -1,5 +1,9 @@
use anyhow::{anyhow, Context};
use std::net::SocketAddr;
#[cfg(unix)]
use std::os::unix::io::AsRawFd;
#[cfg(windows)]
use std::os::windows::io::AsRawSocket;
use std::thread;
use std::time::Duration;
use tokio::io::{AsyncReadExt, AsyncWrite, AsyncWriteExt};
@@ -10,13 +14,14 @@ use tokio::sync::mpsc::{channel, Receiver};
use crate::channel::context::ChannelContext;
use crate::channel::handler::RecvChannelHandler;
use crate::channel::sender::PacketSender;
use crate::channel::socket::create_tcp0;
use crate::channel::{ConnectProtocol, RouteKey, BUFFER_SIZE, TCP_MAX_PACKET_SIZE};
use crate::util::StopManager;
/// 监听tcp端口,等待客户端连接
pub fn tcp_listen<H>(
tcp_server: std::net::TcpListener,
receiver: Receiver<(Vec<u8>, SocketAddr)>,
receiver: Receiver<(Vec<u8>, Option<u16>, SocketAddr)>,
recv_handler: H,
context: ChannelContext,
stop_manager: StopManager,
@@ -28,6 +33,7 @@ where
let worker = stop_manager.add_listener("tcpChannel".into(), move || {
let _ = stop_sender.send(());
})?;
let bind_port = tcp_server.local_addr()?.port();
let runtime = tokio::runtime::Builder::new_multi_thread()
.worker_threads(2)
.enable_all()
@@ -46,9 +52,9 @@ where
}
});
}
tokio::spawn(
async move { connect_tcp_handle(receiver, recv_handler, context).await },
);
tokio::spawn(async move {
connect_tcp_handle(receiver, recv_handler, context, bind_port).await
});
});
runtime.block_on(async {
let _ = stop_receiver.await;
@@ -61,18 +67,24 @@ where
}
async fn connect_tcp_handle<H>(
mut receiver: Receiver<(Vec<u8>, SocketAddr)>,
mut receiver: Receiver<(Vec<u8>, Option<u16>, SocketAddr)>,
recv_handler: H,
context: ChannelContext,
listener_bind_port: u16,
) where
H: RecvChannelHandler,
{
while let Some((data, addr)) = receiver.recv().await {
while let Some((data, bind_port, addr)) = receiver.recv().await {
let recv_handler = recv_handler.clone();
let context = context.clone();
let bind_port = if let Some(bind_port) = bind_port {
bind_port
} else {
listener_bind_port
};
tokio::spawn(async move {
if let Err(e) = connect_tcp0(data, addr, recv_handler, context).await {
log::warn!("发送失败,链接终止:{:?},{:?}", addr, e);
if let Err(e) = connect_tcp0(data, addr, recv_handler, context, bind_port).await {
log::warn!("连接失败,链接终止:{:?},{:?}", addr, e);
}
});
}
@@ -83,12 +95,23 @@ async fn connect_tcp0<H>(
addr: SocketAddr,
recv_handler: H,
context: ChannelContext,
bind_port: u16,
) -> anyhow::Result<()>
where
H: RecvChannelHandler,
{
let mut stream =
tokio::time::timeout(Duration::from_secs(3), TcpStream::connect(addr)).await??;
let socket = if bind_port != 0 {
match create_tcp0(addr.is_ipv4(), bind_port, context.default_interface()) {
Ok(socket) => socket,
Err(e) => {
log::warn!("{:?}", e);
create_tcp0(addr.is_ipv4(), 0, context.default_interface())?
}
}
} else {
create_tcp0(addr.is_ipv4(), 0, context.default_interface())?
};
let mut stream = tokio::time::timeout(Duration::from_secs(3), socket.connect(addr)).await??;
tcp_write(&mut stream, &data).await?;
tcp_stream_handle(stream, addr, recv_handler, context).await;
@@ -107,6 +130,7 @@ where
loop {
let (stream, addr) = tcp_server.accept().await?;
tcp_stream_handle(stream, addr, recv_handler.clone(), context.clone()).await;
}
}
@@ -120,12 +144,18 @@ pub async fn tcp_stream_handle<H>(
H: RecvChannelHandler,
{
let _ = stream.set_nodelay(true);
let local = stream.local_addr();
#[cfg(windows)]
let index = stream.as_raw_socket() as usize;
#[cfg(unix)]
let index = stream.as_raw_fd() as usize;
let route_key = RouteKey::new(ConnectProtocol::TCP, index, addr);
let (r, mut w) = stream.into_split();
let (sender, mut receiver) = channel::<Vec<u8>>(100);
context
.packet_map
.write()
.insert(addr, PacketSender::new(sender));
.insert(route_key, PacketSender::new(sender));
tokio::spawn(async move {
while let Some(data) = receiver.recv().await {
if let Err(e) = tcp_write(&mut w, &data).await {
@@ -136,10 +166,10 @@ pub async fn tcp_stream_handle<H>(
let _ = w.shutdown().await;
});
tokio::spawn(async move {
if let Err(e) = tcp_read(r, addr, &context, recv_handler).await {
log::warn!("tcp_read {:?}", e)
if let Err(e) = tcp_read(r, addr, &context, recv_handler, route_key).await {
log::warn!("tcp_read {:?} {local:?}-{addr}", e)
}
context.packet_map.write().remove(&addr);
context.packet_map.write().remove(&route_key);
});
}
@@ -159,6 +189,7 @@ async fn tcp_read<H>(
addr: SocketAddr,
context: &ChannelContext,
recv_handler: H,
route_key: RouteKey,
) -> anyhow::Result<()>
where
H: RecvChannelHandler,
@@ -176,11 +207,6 @@ where
return Err(anyhow!("tcp数据长度无效 {}", addr));
}
read.read_exact(&mut buf[..len]).await?;
recv_handler.handle(
&mut buf[..len],
&mut extend,
RouteKey::new(ConnectProtocol::TCP, 0, addr),
context,
);
recv_handler.handle(&mut buf[..len], &mut extend, route_key, context);
}
}
+14 -9
View File
@@ -1,4 +1,3 @@
use std::collections::HashMap;
use std::sync::mpsc::{sync_channel, Receiver};
use std::{io, thread};
@@ -71,7 +70,8 @@ where
let mut events = Events::with_capacity(1024);
let mut buf = [0; BUFFER_SIZE];
let mut extend = [0; BUFFER_SIZE];
let mut read_map: HashMap<Token, UdpSocket> = HashMap::with_capacity(32);
let mut list: Vec<UdpSocket> = Vec::with_capacity(100);
let main_len = context.main_len();
loop {
if let Err(e) = poll.poll(&mut events, None) {
crate::ignore_io_interrupted(e)?;
@@ -88,39 +88,43 @@ where
match option {
None => {
log::info!("切换成锥形模式");
for (_, mut udp_socket) in read_map.drain() {
for mut udp_socket in list.drain(..) {
if let Err(e) = udp_socket.deregister(poll.registry()) {
log::error!("{:?}", e);
}
}
}
Some(socket_list) => {
for mut udp_socket in list.drain(..) {
if let Err(e) = udp_socket.deregister(poll.registry()) {
log::error!("deregister {:?}", e);
}
}
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,
Token(index),
Interest::READABLE,
)?;
read_map.insert(token, udp_socket);
list.push(udp_socket);
}
}
}
}
}
}
token => {
if let Some(udp_socket) = read_map.get(&token) {
Token(index) => {
if let Some(udp_socket) = list.get(index) {
loop {
match udp_socket.recv_from(&mut buf) {
Ok((len, addr)) => {
recv_handler.handle(
&mut buf[..len],
&mut extend,
RouteKey::new(ConnectProtocol::UDP, token.0, addr),
RouteKey::new(ConnectProtocol::UDP, index + main_len, addr),
&context,
);
}
@@ -266,6 +270,7 @@ where
for x in events.iter() {
let index = match x.token() {
NOTIFY => return Ok(()),
// 0的位置留给NOTIFY了,这里要再减回去,因为路由是通过index来找到对应udp的
Token(index) => index - 1,
};
let udp = if let Some(udp) = udps.get(index) {
+20 -6
View File
@@ -58,14 +58,16 @@ async fn connect_ws_handle<H>(
) where
H: RecvChannelHandler,
{
let mut index = 0;
while let Some((data, url)) = receiver.recv().await {
let recv_handler = recv_handler.clone();
let context = context.clone();
tokio::spawn(async move {
if let Err(e) = connect_ws(data, url, recv_handler, context).await {
if let Err(e) = connect_ws(data, url, recv_handler, context, index).await {
log::warn!("发送失败,ws链接终止:{:?}", e);
}
});
index += 1;
}
}
const WS_ADDR: SocketAddr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0));
@@ -75,6 +77,7 @@ async fn connect_ws<H>(
mut url: String,
recv_handler: H,
context: ChannelContext,
index: usize,
) -> anyhow::Result<()>
where
H: RecvChannelHandler,
@@ -100,7 +103,16 @@ where
if let Ok(redirect) = v.to_str() {
log::info!("url重定向响应头 {:?}", res.headers());
log::info!("url重定向地址 {}", redirect);
url = redirect.to_string();
// 替换协议前缀
if redirect.starts_with("http://") {
url = redirect.replacen("http://", "ws://", 1);
} else if redirect.starts_with("https://") {
url = redirect.replacen("https://", "wss://", 1);
} else {
url = redirect.to_string();
}
println!("Location{}", url);
log::info!("修改后的重定向地址: {}", url);
continue;
}
}
@@ -114,10 +126,12 @@ where
ws.send(Message::Binary(data)).await?;
let (mut ws_write, ws_read) = ws.split();
let (sender, mut receiver) = channel::<Vec<u8>>(100);
let route_key = RouteKey::new(ConnectProtocol::WS, index, WS_ADDR);
context
.packet_map
.write()
.insert(WS_ADDR, PacketSender::new(sender));
.insert(route_key, PacketSender::new(sender));
tokio::spawn(async move {
while let Some(data) = receiver.recv().await {
if let Err(e) = ws_write.send(Message::Binary(data)).await {
@@ -127,22 +141,22 @@ where
}
let _ = ws_write.close().await;
});
if let Err(e) = ws_read_handle(ws_read, recv_handler, &context).await {
if let Err(e) = ws_read_handle(ws_read, recv_handler, &context, route_key).await {
log::warn!("{:?}", e);
}
context.packet_map.write().remove(&WS_ADDR);
context.packet_map.write().remove(&route_key);
Ok(())
}
async fn ws_read_handle<H>(
mut ws_read: SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>,
recv_handler: H,
context: &ChannelContext,
route_key: RouteKey,
) -> anyhow::Result<()>
where
H: RecvChannelHandler,
{
let mut extend = [0; BUFFER_SIZE];
let route_key = RouteKey::new(ConnectProtocol::WS, 0, WS_ADDR);
while let Some(msg) = ws_read.next().await {
let msg = msg.context("Error during WebSocket ")?;
match msg {
+13 -4
View File
@@ -105,6 +105,7 @@ impl VntInner {
} else {
(None, None)
};
//服务端非对称加密
#[cfg(feature = "server_encrypt")]
let rsa_cipher: Arc<Mutex<Option<RsaCipher>>> = Arc::new(Mutex::new(None));
@@ -131,6 +132,13 @@ impl VntInner {
//设备列表
let device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>> =
Arc::new(Mutex::new((0, HashMap::with_capacity(16))));
let local_ipv4 = if let Some(local_ipv4) = config.local_ipv4 {
Some(local_ipv4)
} else {
nat::local_ipv4()
};
let default_interface = config.local_interface.clone();
//基础信息
let config_info = BaseConfigInfo::new(
config.name.clone(),
@@ -149,6 +157,7 @@ impl VntInner {
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
config.device_name.clone(),
config.allow_wire_guard,
default_interface.clone(),
);
// 服务停止管理器
let stop_manager = {
@@ -179,10 +188,10 @@ impl VntInner {
config.protocol,
config.packet_loss_rate,
config.packet_delay,
default_interface,
up_traffic_meter.clone(),
down_traffic_meter.clone(),
)?;
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();
@@ -194,6 +203,8 @@ impl VntInner {
local_ipv6,
udp_ports,
tcp_port,
config.local_ipv4.is_none(),
config.punch_model,
);
// 定时器
let scheduler = Scheduler::new(stop_manager.clone())?;
@@ -266,9 +277,7 @@ impl VntInner {
let punch = Punch::new(
context.clone(),
config.punch_model,
config.protocol.is_base_tcp(),
connect_util.clone(),
external_route.clone(),
nat_test.clone(),
current_device.clone(),
);
@@ -304,7 +313,7 @@ impl VntInner {
let client_cipher = client_cipher.clone();
let server_cipher = server_cipher.clone();
//延迟启动
scheduler.timeout(Duration::from_secs(3), move |scheduler| {
scheduler.timeout(Duration::from_secs(1), move |scheduler| {
start(
scheduler,
context,
+34 -2
View File
@@ -5,6 +5,7 @@ use std::str::FromStr;
pub use conn::Vnt;
use crate::channel::punch::PunchModel;
use crate::channel::socket::LocalInterface;
use crate::channel::{ConnectProtocol, UseChannelType};
use crate::cipher::CipherModel;
use crate::compression::Compressor;
@@ -52,6 +53,8 @@ pub struct Config {
pub compressor: Compressor,
pub enable_traffic: bool,
pub allow_wire_guard: bool,
pub local_ipv4: Option<Ipv4Addr>,
pub local_interface: LocalInterface,
}
impl Config {
@@ -91,6 +94,7 @@ impl Config {
enable_traffic: bool,
// 允许传递wg流量
allow_wire_guard: bool,
local_dev: Option<String>,
) -> anyhow::Result<Self> {
for x in stun_server.iter_mut() {
if !x.contains(":") {
@@ -137,9 +141,28 @@ impl Config {
server_address_str = s.to_string();
protocol = ConnectProtocol::TCP;
}
server_address =
address_choose(dns_query_all(&server_address_str, name_servers.clone())?)?;
let address_result = dns_query_all(
&server_address_str,
name_servers.clone(),
&LocalInterface::default(),
);
match address_result {
Ok(address) => match address_choose(address) {
Ok(resolved_address) => {
server_address = resolved_address;
}
Err(e) => {
log::error!("Failed to choose address: {}", e);
println!("Failed to choose address: {}", e);
}
},
Err(e) => {
log::error!("DNS query failed: {}", e);
println!("DNS query failed: {}", e);
}
}
}
#[cfg(feature = "port_mapping")]
let port_mapping_list = crate::port_mapping::convert(port_mapping_list)?;
@@ -147,6 +170,13 @@ impl Config {
*dest = *mask & *dest;
}
in_ips.sort_by(|(dest1, _, _), (dest2, _, _)| dest2.cmp(dest1));
let (local_interface, local_ipv4) = if let Some(local_dev) = local_dev {
let (default_interface, ip) = crate::channel::socket::get_interface(local_dev)?;
log::info!("default_interface = {:?} local_ip= {ip}", default_interface);
(default_interface, Some(ip))
} else {
(LocalInterface::default(), None)
};
Ok(Self {
#[cfg(feature = "integrated_tun")]
#[cfg(target_os = "windows")]
@@ -184,6 +214,8 @@ impl Config {
compressor,
enable_traffic,
allow_wire_guard,
local_ipv4,
local_interface,
})
}
}
+4
View File
@@ -172,6 +172,8 @@ pub enum ErrorType {
IpAlreadyExists,
InvalidIp,
LocalIpExists,
FailedToCrateDevice,
Warn,
Unknown,
}
@@ -184,6 +186,8 @@ impl Into<u8> for ErrorType {
ErrorType::IpAlreadyExists => 4,
ErrorType::InvalidIp => 5,
ErrorType::LocalIpExists => 6,
ErrorType::FailedToCrateDevice => 101,
ErrorType::Warn => 102,
ErrorType::Unknown => 255,
}
}
+11 -1
View File
@@ -56,6 +56,7 @@ fn heartbeat0(
) {
let gateway_ip = current_device.virtual_gateway;
let src_ip = current_device.virtual_ip;
let channel_num = context.channel_num();
// 可能服务器ip发生变化,导致发送失败
let mut is_send_gateway = false;
match heartbeat_packet_server(device_map, server_cipher, src_ip, gateway_ip) {
@@ -87,7 +88,16 @@ fn heartbeat0(
continue;
}
};
for route in routes {
for (index, route) in routes.iter().enumerate() {
let limit = if context.first_latency() {
channel_num + 1
} else {
channel_num
};
if index >= limit {
// 多余的通道不再发送心跳包,让它自动过期
break;
}
if let Err(e) = context.send_by_key(&net_packet, route.route_key()) {
log::warn!("heartbeat err={:?}", e)
}
+10 -3
View File
@@ -7,6 +7,7 @@ use crossbeam_utils::atomic::AtomicCell;
use crate::channel::context::ChannelContext;
use crate::channel::idle::{Idle, IdleType};
use crate::channel::sender::ConnectUtil;
use crate::channel::socket::LocalInterface;
use crate::channel::ConnectProtocol;
use crate::handle::callback::{ConnectInfo, ErrorType};
use crate::handle::handshaker::Handshake;
@@ -49,7 +50,7 @@ pub fn idle_gateway<Call: VntCallback>(
&mut connect_count,
&handshake,
);
let rs = scheduler.timeout(Duration::from_secs(5), move |s| {
let rs = scheduler.timeout(Duration::from_secs(8), move |s| {
idle_gateway(
s,
context,
@@ -130,7 +131,8 @@ fn check_gateway_channel<Call: VntCallback>(
let connect_protocol = context.main_protocol();
if connect_protocol.is_transport() {
// 传输层的协议需要探测服务器地址
current_device = domain_request0(current_device_info, config);
current_device =
domain_request0(current_device_info, config, context.default_interface());
}
//需要重连
call.connect(ConnectInfo::new(*count, current_device.connect_server));
@@ -160,11 +162,16 @@ fn check_gateway_channel<Call: VntCallback>(
pub fn domain_request0(
current_device: &AtomicCell<CurrentDeviceInfo>,
config: &BaseConfigInfo,
default_interface: &LocalInterface,
) -> CurrentDeviceInfo {
let mut current_dev = current_device.load();
// 探测服务端地址变化
match dns_query_all(&config.server_addr, config.name_servers.clone()) {
match dns_query_all(
&config.server_addr,
config.name_servers.clone(),
default_interface,
) {
Ok(addrs) => {
log::info!(
"domain {} dns {:?} addr {:?}",
+31 -15
View File
@@ -149,7 +149,7 @@ fn punch_start(
*v += 1;
*v
} else {
guard.insert(peer_ip, 1);
guard.insert(peer_ip, 0);
0
}
};
@@ -175,7 +175,7 @@ fn punch_request(
client_cipher: Cipher,
count: usize,
punch_record: Arc<Mutex<HashMap<Ipv4Addr, usize>>>,
mut last_punch_record: HashMap<Ipv4Addr, usize>,
mut last_punch_record: HashMap<Ipv4Addr, PunchRecordItem>,
) {
let curr = current_device.load();
let secs = if curr.status.online() {
@@ -213,6 +213,11 @@ fn punch_request(
log::info!("定时任务停止");
}
}
#[derive(Copy, Clone, Default)]
struct PunchRecordItem {
pub punch_record: usize,
pub last_p2p_num: usize,
}
/// 随机对需要打洞的客户端发起打洞请求
fn punch0(
@@ -222,7 +227,7 @@ fn punch0(
current_device: CurrentDeviceInfo,
client_cipher: &Cipher,
punch_record: &Mutex<HashMap<Ipv4Addr, usize>>,
last_punch_record: &mut HashMap<Ipv4Addr, usize>,
last_punch_record: &mut HashMap<Ipv4Addr, PunchRecordItem>,
total_count: usize,
) -> anyhow::Result<()> {
let nat_info = nat_test.nat_info();
@@ -241,12 +246,17 @@ fn punch0(
.lock()
.1
.values()
.filter(|info| !info.wireguard && info.status.is_online() && info.virtual_ip > current_ip)
.filter(|info| !info.wireguard && info.virtual_ip > current_ip)
.cloned()
.collect();
list.shuffle(&mut rand::thread_rng());
for info in list {
let punch_count = punch_record
if info.status.is_offline() {
// 客户端掉线了要重置打洞记录
punch_record.lock().remove(&info.virtual_ip);
continue;
}
let mut punch_count = punch_record
.lock()
.get(&info.virtual_ip)
.cloned()
@@ -256,12 +266,11 @@ fn punch0(
let p2p_num = context.route_table.p2p_num(&info.virtual_ip);
let mut max_punch_interval = 50;
if p2p_num > 0 {
if punch_count == 0 {
continue;
}
if p2p_num >= context.channel_num() {
//通道数满足要求,不再打洞
punch_record.lock().remove(&info.virtual_ip);
if punch_count != 0 {
punch_record.lock().remove(&info.virtual_ip);
}
continue;
}
//有p2p通道,但是通道数量不够,则继续打洞
@@ -269,13 +278,18 @@ fn punch0(
max_punch_interval = 300;
}
// 能发起打洞的前提是自己空闲,这里会间隔5秒以上发起一次打洞,所以假定上一轮打洞已结束
let last_punch = last_punch_record
.get(&info.virtual_ip)
.cloned()
.unwrap_or(0);
let last_punch = last_punch_record.entry(info.virtual_ip).or_default();
if last_punch.last_p2p_num > p2p_num {
// 打的洞掉线了,需要重置重新打
punch_record.lock().remove(&info.virtual_ip);
punch_count = 0;
}
// 梯度增加打洞时间间隔
if total_count > last_punch + punch_count.min(max_punch_interval) {
last_punch_record.insert(info.virtual_ip, total_count);
if total_count > last_punch.punch_record + punch_count.min(max_punch_interval) {
// 记录打洞周期,抑制下一次打洞,从而递减打洞频率
last_punch.punch_record = total_count;
last_punch.last_p2p_num = p2p_num;
let packet = punch_packet(
client_cipher,
current_device.virtual_ip(),
@@ -312,6 +326,7 @@ fn punch_packet(
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.public_tcp_port = nat_info.public_tcp_port 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;
@@ -321,6 +336,7 @@ fn punch_packet(
punch_reply.ipv6 = ipv6.octets().to_vec();
}
punch_reply.nat_type = protobuf::EnumOrUnknown::new(PunchNatType::from(nat_info.nat_type));
punch_reply.punch_model = protobuf::EnumOrUnknown::new(nat_info.punch_model.into());
log::info!("请求打洞={:?}", punch_reply);
let bytes = punch_reply
.write_to_bytes()
+6 -2
View File
@@ -29,9 +29,13 @@ fn retrieve_nat_type0(
.name("natTest".into())
.spawn(move || {
if nat_test.can_update() {
let local_ipv4 = nat::local_ipv4();
let local_ipv4 = if nat_test.update_local_ipv4 {
nat::local_ipv4()
} else {
None
};
let local_ipv6 = nat::local_ipv6();
match nat_test.re_test(local_ipv4, local_ipv6) {
match nat_test.re_test(local_ipv4, local_ipv6, context.default_interface()) {
Ok(nat_info) => {
log::info!("当前nat信息:{:?}", nat_info);
if let Err(e) = context.switch(nat_info.nat_type, &udp_socket_sender) {
+7 -3
View File
@@ -1,3 +1,4 @@
use crate::channel::socket::LocalInterface;
use crossbeam_utils::atomic::AtomicCell;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
@@ -70,6 +71,7 @@ pub struct BaseConfigInfo {
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
pub device_name: Option<String>,
pub allow_wire_guard: bool,
pub default_interface: LocalInterface,
}
impl BaseConfigInfo {
@@ -90,6 +92,7 @@ impl BaseConfigInfo {
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
device_name: Option<String>,
allow_wire_guard: bool,
default_interface: LocalInterface,
) -> Self {
Self {
name,
@@ -108,6 +111,7 @@ impl BaseConfigInfo {
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
device_name,
allow_wire_guard,
default_interface,
}
}
}
@@ -219,10 +223,10 @@ impl CurrentDeviceInfo {
virtual_gateway: Ipv4Addr,
) {
let broadcast_ip = (!u32::from_be_bytes(virtual_netmask.octets()))
| u32::from_be_bytes(virtual_gateway.octets());
| u32::from_be_bytes(virtual_ip.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 =
u32::from_be_bytes(virtual_netmask.octets()) & u32::from_be_bytes(virtual_ip.octets());
let virtual_network = Ipv4Addr::from(virtual_network);
self.virtual_ip = virtual_ip;
self.virtual_netmask = virtual_netmask;
+14 -12
View File
@@ -216,17 +216,13 @@ impl<Device: DeviceWrite> ClientPacketHandler<Device> {
match ControlPacket::new(net_packet.transport_protocol(), net_packet.payload())? {
ControlPacket::PingPacket(_) => {
let route = Route::from_default_rt(route_key, metric);
if context.route_table.add_route_if_absent(source, route)
|| net_packet.source() < current_device.virtual_ip
{
//在路由表中,或者来源比自己小,就需要回复,注意不能调换顺序
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, route_key)?;
}
context.route_table.add_route_if_absent(source, route);
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, route_key)?;
}
ControlPacket::PongPacket(pong_packet) => {
let current_time = crate::handle::now_time() as u16;
@@ -272,7 +268,7 @@ impl<Device: DeviceWrite> ClientPacketHandler<Device> {
{
return Ok(());
}
let route = Route::from_default_rt(route_key, 1);
let route = Route::from_default_rt(route_key, metric);
context.route_table.add_route_if_absent(source, route);
}
ControlPacket::AddrRequest => match route_key.addr.ip() {
@@ -318,6 +314,7 @@ impl<Device: DeviceWrite> ClientPacketHandler<Device> {
.collect();
let local_ipv4 = Some(Ipv4Addr::from(punch_info.local_ip.to_be_bytes()));
let tcp_port = punch_info.tcp_port as u16;
let public_tcp_port = punch_info.public_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))
@@ -340,7 +337,9 @@ impl<Device: DeviceWrite> ClientPacketHandler<Device> {
ipv6,
punch_info.udp_ports.iter().map(|e| *e as u16).collect(),
tcp_port,
public_tcp_port,
punch_info.nat_type.enum_value_or_default().into(),
punch_info.punch_model.enum_value_or_default().into(),
);
{
let peer_nat_info = peer_nat_info.clone();
@@ -360,8 +359,11 @@ impl<Device: DeviceWrite> ClientPacketHandler<Device> {
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.public_tcp_port = nat_info.public_tcp_port as u32;
punch_reply.nat_type =
protobuf::EnumOrUnknown::new(PunchNatType::from(nat_info.nat_type));
punch_reply.punch_model =
protobuf::EnumOrUnknown::new(nat_info.punch_model.into());
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;
+30 -28
View File
@@ -7,12 +7,11 @@ use std::sync::Arc;
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 parking_lot::Mutex;
use protobuf::Message;
use crate::channel::context::ChannelContext;
use crate::channel::{Route, RouteKey};
@@ -105,12 +104,11 @@ impl<Call: VntCallback, Device: DeviceWrite> PacketHandler for ServerPacketHandl
) -> anyhow::Result<()> {
if !current_device.is_server_addr(route_key.addr) {
//拦截不是服务端的流量
log::info!(
log::warn!(
"route_key={:?},不是来源于服务端地址{}",
route_key,
current_device.connect_server
);
return Ok(());
}
context
.route_table
@@ -152,6 +150,8 @@ impl<Call: VntCallback, Device: DeviceWrite> PacketHandler for ServerPacketHandl
let response = HandshakeResponse::parse_from_bytes(net_packet.payload())
.map_err(|e| anyhow!("HandshakeResponse {:?}", e))?;
log::info!("握手响应:{:?},{}", route_key, response);
//设置为默认通道
context.set_default_route_key(route_key);
//如果开启了加密,则发送加密握手请求
#[cfg(feature = "server_encrypt")]
if let Some(key) = self.server_cipher.key() {
@@ -212,7 +212,7 @@ impl<Call: VntCallback, Device: DeviceWrite> PacketHandler for ServerPacketHandl
let handshake_info = HandshakeInfo::new_no_secret(response.version);
if self.callback.handshake(handshake_info) {
//没有加密,则发送注册请求
self.register(current_device, context)?;
self.register(current_device, context, route_key)?;
}
return Ok(());
@@ -296,6 +296,10 @@ impl<Call: VntCallback, Device: DeviceWrite> ServerPacketHandler<Call, Device> {
let public_port = response.public_port as u16;
self.nat_test
.update_addr(route_key.index(), public_ip, public_port);
if route_key.protocol().is_tcp() {
log::info!("更新公网tcp端口 {public_port}");
self.nat_test.update_tcp_port(public_port);
}
let old = current_device;
let mut cur = *current_device;
loop {
@@ -350,12 +354,14 @@ impl<Call: VntCallback, Device: DeviceWrite> ServerPacketHandler<Call, Device> {
target_os = "linux",
target_os = "macos"
))]
match crate::tun_tap_device::create_device(device_config) {
match crate::tun_tap_device::create_device(
device_config,
&self.callback,
) {
Ok(device) => {
use tun::device::IFace;
let tun_info = crate::handle::callback::DeviceInfo::new(
device.name().unwrap_or("unknown".into()),
device.version().unwrap_or("unknown".into()),
"".into(),
);
log::info!("tun信息{:?}", tun_info);
self.callback.create_tun(tun_info);
@@ -380,27 +386,20 @@ impl<Call: VntCallback, Device: DeviceWrite> ServerPacketHandler<Call, Device> {
let device_fd = self.callback.generate_tun(device_config);
if device_fd == 0 {
self.callback.error(ErrorInfo::new_msg(
ErrorType::Unknown,
ErrorType::FailedToCrateDevice,
"device_fd == 0".into(),
));
} else {
match tun::Device::new(device_fd as _) {
Ok(device) => {
if let Err(e) =
self.tun_device_helper.start(Arc::new(device))
{
self.callback.error(ErrorInfo::new_msg(
ErrorType::Unknown,
format!("{:?}", e),
));
}
}
Err(e) => {
self.callback.error(ErrorInfo::new_msg(
ErrorType::Unknown,
format!("{:?}", e),
));
}
let device =
unsafe { tun_rs::SyncDevice::from_fd(device_fd as _) };
if let Err(e) = self
.tun_device_helper
.start(Arc::new(device), self.config_info.allow_wire_guard)
{
self.callback.error(ErrorInfo::new_msg(
ErrorType::FailedToCrateDevice,
format!("{:?}", e),
));
}
}
}
@@ -421,7 +420,7 @@ impl<Call: VntCallback, Device: DeviceWrite> ServerPacketHandler<Call, Device> {
service_packet::Protocol::SecretHandshakeResponse => {
log::info!("SecretHandshakeResponse");
//加密握手结束,发送注册数据
self.register(current_device, context)?;
self.register(current_device, context, route_key)?;
}
_ => {
log::warn!(
@@ -466,11 +465,14 @@ impl<Call: VntCallback, Device: DeviceWrite> ServerPacketHandler<Call, Device> {
&self,
current_device: &CurrentDeviceInfo,
context: &ChannelContext,
route_key: RouteKey,
) -> anyhow::Result<()> {
if current_device.status.online() {
log::info!("已连接的不需要注册,{:?}", self.config_info);
return Ok(());
}
//设置为默认通道
context.set_default_route_key(route_key);
let token = self.config_info.token.clone();
let device_id = self.config_info.device_id.clone();
let name = self.config_info.name.clone();
+2 -10
View File
@@ -1,19 +1,11 @@
pub mod tun_handler;
#[cfg(unix)]
mod unix;
use crossbeam_utils::atomic::AtomicCell;
use parking_lot::Mutex;
use std::sync::Arc;
#[cfg(unix)]
pub(crate) use unix::*;
mod platform;
#[cfg(target_os = "windows")]
mod windows;
#[cfg(target_os = "windows")]
pub(crate) use windows::*;
pub(crate) use platform::*;
/// 仅仅是停止tun,不停止vnt
#[derive(Clone, Default)]
@@ -13,13 +13,12 @@ use parking_lot::Mutex;
use std::collections::HashMap;
use std::net::Ipv4Addr;
use std::sync::Arc;
use tun::device::IFace;
use tun::Device;
use tun_rs::{InterruptEvent, SyncDevice};
pub(crate) fn start_simple(
stop_manager: StopManager,
context: &ChannelContext,
device: Arc<Device>,
device: Arc<SyncDevice>,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
ip_route: ExternalRoute,
#[cfg(feature = "ip_proxy")] ip_proxy_map: Option<IpProxyMap>,
@@ -30,10 +29,11 @@ pub(crate) fn start_simple(
device_stop: DeviceStop,
allow_wire_guard: bool,
) -> anyhow::Result<()> {
let event = Arc::new(InterruptEvent::new()?);
let worker = {
let device = device.clone();
let event = event.clone();
stop_manager.add_listener("tun_device".into(), move || {
if let Err(e) = device.shutdown() {
if let Err(e) = event.trigger() {
log::warn!("{:?}", e);
}
})?
@@ -51,6 +51,7 @@ pub(crate) fn start_simple(
if let Err(e) = start_simple0(
context,
device,
event,
current_device,
ip_route,
#[cfg(feature = "ip_proxy")]
@@ -72,7 +73,8 @@ pub(crate) fn start_simple(
fn start_simple0(
context: &ChannelContext,
device: Arc<Device>,
device: Arc<SyncDevice>,
event: Arc<InterruptEvent>,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
ip_route: ExternalRoute,
#[cfg(feature = "ip_proxy")] ip_proxy_map: Option<IpProxyMap>,
@@ -85,8 +87,7 @@ fn start_simple0(
let mut buf = [0; BUFFER_SIZE];
let mut extend = [0; BUFFER_SIZE];
loop {
let len = device.read(&mut buf[12..])? + 12;
//单线程的
let len = device.recv_intr(&mut buf[12..],&event)? + 12;
// buf是重复利用的,需要重置头部
buf[..12].fill(0);
match crate::handle::tun_tap::tun_handler::handle(
+9 -11
View File
@@ -1,16 +1,14 @@
use crossbeam_utils::atomic::AtomicCell;
use packet::icmp::icmp::IcmpPacket;
use packet::icmp::Kind;
use packet::ip::ipv4::packet::IpV4Packet;
use packet::ip::ipv4::protocol::Protocol;
use parking_lot::Mutex;
use std::collections::HashMap;
use std::net::Ipv4Addr;
use std::sync::Arc;
use std::{io, thread};
use packet::icmp::icmp::IcmpPacket;
use packet::icmp::Kind;
use packet::ip::ipv4::packet::IpV4Packet;
use packet::ip::ipv4::protocol::Protocol;
use tun::device::IFace;
use tun::Device;
use tun_rs::SyncDevice;
use crate::channel::context::ChannelContext;
use crate::channel::sender::{send_to_wg, send_to_wg_broadcast};
@@ -28,7 +26,7 @@ use crate::protocol::body::ENCRYPTION_RESERVED;
use crate::protocol::ip_turn_packet::BroadcastPacket;
use crate::protocol::{ip_turn_packet, NetPacket, MAX_TTL};
use crate::util::StopManager;
fn icmp(device_writer: &Device, mut ipv4_packet: IpV4Packet<&mut [u8]>) -> anyhow::Result<()> {
fn icmp(device_writer: &SyncDevice, mut ipv4_packet: IpV4Packet<&mut [u8]>) -> anyhow::Result<()> {
if ipv4_packet.protocol() == Protocol::Icmp {
let mut icmp = IcmpPacket::new(ipv4_packet.payload_mut())?;
if icmp.kind() == Kind::EchoRequest {
@@ -38,7 +36,7 @@ fn icmp(device_writer: &Device, mut ipv4_packet: IpV4Packet<&mut [u8]>) -> anyho
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)?;
device_writer.send(ipv4_packet.buffer)?;
}
}
Ok(())
@@ -47,7 +45,7 @@ fn icmp(device_writer: &Device, mut ipv4_packet: IpV4Packet<&mut [u8]>) -> anyho
pub fn start(
stop_manager: StopManager,
context: ChannelContext,
device: Arc<Device>,
device: Arc<SyncDevice>,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
ip_route: ExternalRoute,
#[cfg(feature = "ip_proxy")] ip_proxy_map: Option<IpProxyMap>,
@@ -160,7 +158,7 @@ pub(crate) fn handle(
buf: &mut [u8],
data_len: usize, //数据总长度=12+ip包长度
extend: &mut [u8],
device_writer: &Device,
device_writer: &SyncDevice,
current_device: CurrentDeviceInfo,
ip_route: &ExternalRoute,
#[cfg(feature = "ip_proxy")] proxy_map: &Option<IpProxyMap>,
-154
View File
@@ -1,154 +0,0 @@
use crate::channel::context::ChannelContext;
use crate::channel::BUFFER_SIZE;
use crate::cipher::Cipher;
use crate::compression::Compressor;
use crate::external_route::ExternalRoute;
use crate::handle::tun_tap::DeviceStop;
use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo};
#[cfg(feature = "ip_proxy")]
use crate::ip_proxy::IpProxyMap;
use crate::util::StopManager;
use crossbeam_utils::atomic::AtomicCell;
use mio::event::Source;
use mio::unix::SourceFd;
use mio::{Events, Interest, Poll, Token, Waker};
use parking_lot::Mutex;
use std::collections::HashMap;
use std::io;
use std::net::Ipv4Addr;
use std::os::fd::AsRawFd;
use std::sync::Arc;
use tun::Device;
const STOP: Token = Token(0);
const FD: Token = Token(1);
pub(crate) fn start_simple(
stop_manager: StopManager,
context: &ChannelContext,
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,
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
compressor: Compressor,
device_stop: DeviceStop,
allow_wire_guard: bool,
) -> anyhow::Result<()> {
let poll = Poll::new()?;
let waker = Arc::new(Waker::new(poll.registry(), STOP)?);
let _waker = waker.clone();
let worker = {
stop_manager.add_listener("tun_device".into(), move || {
if let Err(e) = waker.wake() {
log::warn!("{:?}", e);
}
})?
};
let worker_cell = Arc::new(AtomicCell::new(Some(worker)));
let _worker_cell = worker_cell.clone();
device_stop.set_stop_fn(move || {
if let Some(worker) = _worker_cell.take() {
worker.stop_self()
}
});
if let Err(e) = start_simple0(
poll,
context,
device,
current_device,
ip_route,
#[cfg(feature = "ip_proxy")]
ip_proxy_map,
client_cipher,
server_cipher,
device_map,
compressor,
allow_wire_guard,
) {
log::error!("{:?}", e);
};
device_stop.stopped();
if let Some(worker) = worker_cell.take() {
worker.stop_all();
}
drop(_waker);
Ok(())
}
fn start_simple0(
mut poll: Poll,
context: &ChannelContext,
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,
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
compressor: Compressor,
allow_wire_guard: bool,
) -> anyhow::Result<()> {
let mut buf = [0; BUFFER_SIZE];
let mut extend = [0; BUFFER_SIZE];
let fd = device.as_tun_fd();
fd.set_nonblock()?;
SourceFd(&fd.as_raw_fd()).register(poll.registry(), FD, Interest::READABLE)?;
let mut events = Events::with_capacity(4);
#[cfg(not(target_os = "macos"))]
let start = 12;
#[cfg(target_os = "macos")]
let start = 12 - 4;
loop {
if let Err(e) = poll.poll(&mut events, None) {
crate::ignore_io_interrupted(e)?;
continue;
}
for event in events.iter() {
if event.token() == STOP {
return Ok(());
}
let mut retries = 0;
loop {
let len = match fd.read(&mut buf[start..]) {
Ok(len) => len + start,
Err(e) => {
if e.kind() == io::ErrorKind::WouldBlock {
retries += 1;
if retries < 8 {
continue;
}
break;
}
Err(e)?
}
};
// buf是重复利用的,需要重置头部
buf[..12].fill(0);
match crate::handle::tun_tap::tun_handler::handle(
context,
&mut buf,
len,
&mut extend,
&device,
current_device.load(),
&ip_route,
#[cfg(feature = "ip_proxy")]
&ip_proxy_map,
&client_cipher,
&server_cipher,
&device_map,
&compressor,
allow_wire_guard,
) {
Ok(_) => {}
Err(e) => {
log::warn!("{:?}", e)
}
}
}
}
}
}
+5
View File
@@ -13,6 +13,7 @@ use packet::icmp::icmp::HeaderOther;
use packet::ip::ipv4::packet::IpV4Packet;
use crate::channel::context::ChannelContext;
use crate::channel::socket::{LocalInterface, VntSocketTrait};
use crate::cipher::Cipher;
use crate::handle::CurrentDeviceInfo;
use crate::ip_proxy::ProxyHandler;
@@ -30,6 +31,7 @@ impl IcmpProxy {
context: ChannelContext,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
client_cipher: Cipher,
default_interface: &LocalInterface,
) -> anyhow::Result<Self> {
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
let icmp_socket = socket2::Socket::new(
@@ -50,6 +52,9 @@ impl IcmpProxy {
.bind(&socket2::SockAddr::from(addr))
.context("bind Socket ICMPV4 failed")?;
icmp_socket.set_nonblocking(true)?;
if let Err(e) = icmp_socket.set_ip_unicast_if(default_interface) {
log::warn!("set_ip_unicast_if {:?}", e)
}
let std_socket: std::net::UdpSocket = icmp_socket.into();
let tokio_icmp_socket = UdpSocket::from_std(std_socket.try_clone()?)?;
+6 -4
View File
@@ -68,14 +68,16 @@ pub fn init_proxy(
}
async fn init_proxy0(
_context: ChannelContext,
context: ChannelContext,
_current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
_client_cipher: Cipher,
) -> anyhow::Result<IpProxyMap> {
let default_interface = context.default_interface().clone();
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
let icmp_proxy = IcmpProxy::new(_context, _current_device, _client_cipher).await?;
let tcp_proxy = TcpProxy::new().await?;
let udp_proxy = UdpProxy::new().await?;
let icmp_proxy =
IcmpProxy::new(context, _current_device, _client_cipher, &default_interface).await?;
let tcp_proxy = TcpProxy::new(default_interface.clone()).await?;
let udp_proxy = UdpProxy::new(default_interface.clone()).await?;
Ok(IpProxyMap {
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
+32 -21
View File
@@ -5,13 +5,13 @@ use std::time::Duration;
use std::{collections::HashMap, io, net::SocketAddr};
use parking_lot::Mutex;
use tokio::net::{TcpListener, TcpSocket, TcpStream};
use tokio::net::{TcpListener, TcpStream};
use crate::channel::socket::{create_tcp, LocalInterface};
use crate::ip_proxy::ProxyHandler;
use packet::ip::ipv4::packet::IpV4Packet;
use packet::tcp::tcp::TcpPacket;
use crate::ip_proxy::ProxyHandler;
#[derive(Clone)]
pub struct TcpProxy {
port: u16,
@@ -19,7 +19,7 @@ pub struct TcpProxy {
}
impl TcpProxy {
pub async fn new() -> anyhow::Result<Self> {
pub async fn new(default_interface: LocalInterface) -> anyhow::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))
@@ -28,7 +28,7 @@ impl TcpProxy {
let port = tcp_listener.local_addr()?.port();
{
let nat_map = nat_map.clone();
tokio::spawn(tcp_proxy(tcp_listener, nat_map));
tokio::spawn(tcp_proxy(tcp_listener, nat_map, default_interface));
}
Ok(Self { port, nat_map })
}
@@ -79,26 +79,33 @@ impl ProxyHandler for TcpProxy {
async fn tcp_proxy(
tcp_listener: TcpListener,
nat_map: Arc<Mutex<HashMap<SocketAddrV4, SocketAddrV4>>>,
default_interface: LocalInterface,
) {
loop {
match tcp_listener.accept().await {
Ok((tcp_stream, sender_addr)) => match sender_addr {
SocketAddr::V4(sender_addr) => {
if let Some(dest_addr) = nat_map.lock().get(&sender_addr).cloned() {
let default_interface = default_interface.clone();
tokio::spawn(async move {
let peer_tcp_stream =
match tcp_connect(sender_addr.port(), dest_addr.into()).await {
Ok(peer_tcp_stream) => peer_tcp_stream,
Err(e) => {
log::warn!(
"tcp代理异常:{:?},来源:{},目标:{}",
e,
sender_addr,
dest_addr
);
return;
}
};
let peer_tcp_stream = match tcp_connect(
sender_addr.port(),
dest_addr.into(),
&default_interface,
)
.await
{
Ok(peer_tcp_stream) => peer_tcp_stream,
Err(e) => {
log::warn!(
"tcp代理异常:{:?},来源:{},目标:{}",
e,
sender_addr,
dest_addr
);
return;
}
};
proxy(sender_addr, dest_addr, tcp_stream, peer_tcp_stream).await
});
} else {
@@ -114,15 +121,19 @@ async fn tcp_proxy(
}
}
/// 优先使用来源端口建立tcp连接
async fn tcp_connect(src_port: u16, addr: SocketAddr) -> anyhow::Result<TcpStream> {
let socket = TcpSocket::new_v4()?;
async fn tcp_connect(
src_port: u16,
addr: SocketAddr,
default_interface: &LocalInterface,
) -> anyhow::Result<TcpStream> {
let socket = create_tcp(true, default_interface)?;
if socket
.bind(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, src_port).into())
.is_err()
{
socket.bind(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0).into())?;
}
let _ = socket.set_nodelay(false);
let _ = socket.set_nodelay(true);
let tcp_stream = tokio::time::timeout(Duration::from_secs(5), socket.connect(addr))
.await
.with_context(|| format!("TCP connection timeout {}", addr))?
+26 -15
View File
@@ -8,11 +8,11 @@ use std::{collections::HashMap, io, net::SocketAddr};
use parking_lot::Mutex;
use tokio::net::UdpSocket;
use crate::channel::socket::{bind_udp, LocalInterface};
use crate::ip_proxy::ProxyHandler;
use packet::ip::ipv4::packet::IpV4Packet;
use packet::udp::udp::UdpPacket;
use crate::ip_proxy::ProxyHandler;
#[derive(Clone)]
pub struct UdpProxy {
port: u16,
@@ -20,7 +20,7 @@ pub struct UdpProxy {
}
impl UdpProxy {
pub async fn new() -> anyhow::Result<Self> {
pub async fn new(default_interface: LocalInterface) -> anyhow::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))
@@ -29,8 +29,8 @@ impl UdpProxy {
let port = udp.local_addr()?.port();
{
let nat_map = nat_map.clone();
tokio::spawn(async {
if let Err(e) = udp_proxy(udp, nat_map).await {
tokio::spawn(async move {
if let Err(e) = udp_proxy(udp, nat_map, default_interface).await {
log::warn!("udp_proxy:{:?}", e);
}
});
@@ -84,7 +84,8 @@ impl ProxyHandler for UdpProxy {
async fn udp_proxy(
udp: UdpSocket,
nat_map: Arc<Mutex<HashMap<SocketAddrV4, SocketAddrV4>>>,
) -> io::Result<()> {
default_interface: LocalInterface,
) -> anyhow::Result<()> {
let mut buf = [0u8; 65536];
let inner_map: Arc<Mutex<HashMap<SocketAddrV4, (Arc<UdpSocket>, Arc<AtomicCell<Instant>>)>>> =
@@ -94,9 +95,15 @@ async fn udp_proxy(
match udp_socket.recv_from(&mut buf).await {
Ok((len, sender_addr)) => match sender_addr {
SocketAddr::V4(sender_addr) => {
if let Err(e) =
udp_proxy0(&buf[..len], sender_addr, &inner_map, &nat_map, &udp_socket)
.await
if let Err(e) = udp_proxy0(
&buf[..len],
sender_addr,
&inner_map,
&nat_map,
&udp_socket,
&default_interface,
)
.await
{
log::warn!("udp proxy {} {:?}", sender_addr, e);
}
@@ -116,7 +123,8 @@ async fn udp_proxy0(
inner_map: &Arc<Mutex<HashMap<SocketAddrV4, (Arc<UdpSocket>, Arc<AtomicCell<Instant>>)>>>,
map: &Arc<Mutex<HashMap<SocketAddrV4, SocketAddrV4>>>,
udp_socket: &Arc<UdpSocket>,
) -> io::Result<()> {
default_interface: &LocalInterface,
) -> anyhow::Result<()> {
let option = inner_map.lock().get(&sender_addr).cloned();
if let Some((udp, time)) = option {
time.store(Instant::now());
@@ -125,11 +133,14 @@ async fn udp_proxy0(
let option = map.lock().get(&sender_addr).cloned();
if let Some(dest_addr) = option {
//先使用相同的端口,冲突了再随机端口
let peer_udp_socket =
match UdpSocket::bind(format!("0.0.0.0:{}", sender_addr.port())).await {
Ok(udp) => udp,
Err(_) => UdpSocket::bind("0.0.0.0:0").await?,
};
let peer_udp_socket = match bind_udp(
format!("0.0.0.0:{}", sender_addr.port()).parse().unwrap(),
default_interface,
) {
Ok(udp) => udp,
Err(_) => bind_udp("0.0.0.0:0".parse().unwrap(), default_interface)?,
};
let peer_udp_socket = UdpSocket::from_std(peer_udp_socket.into())?;
peer_udp_socket.connect(dest_addr).await?;
peer_udp_socket.send(buf).await?;
let peer_udp_socket = Arc::new(peer_udp_socket);
+18 -22
View File
@@ -10,8 +10,8 @@ use parking_lot::Mutex;
use rand::prelude::SliceRandom;
use rand::Rng;
use crate::channel::punch::{NatInfo, NatType};
use crate::proto::message::PunchNatType;
use crate::channel::punch::{NatInfo, NatType, PunchModel};
use crate::channel::socket::LocalInterface;
#[cfg(feature = "upnp")]
use crate::util::UPnP;
@@ -116,24 +116,7 @@ pub struct NatTest {
tcp_port: u16,
#[cfg(feature = "upnp")]
upnp: UPnP,
}
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,
}
}
pub(crate) update_local_ipv4: bool,
}
impl NatTest {
@@ -144,6 +127,8 @@ impl NatTest {
ipv6: Option<Ipv6Addr>,
udp_ports: Vec<u16>,
tcp_port: u16,
update_local_ipv4: bool,
punch_model: PunchModel,
) -> NatTest {
let ports = vec![0; udp_ports.len()];
let nat_info = NatInfo::new(
@@ -154,7 +139,9 @@ impl NatTest {
ipv6,
udp_ports.clone(),
tcp_port,
0,
NatType::Cone,
punch_model,
);
let info = Arc::new(Mutex::new(nat_info));
#[cfg(feature = "upnp")]
@@ -178,6 +165,7 @@ impl NatTest {
tcp_port,
#[cfg(feature = "upnp")]
upnp,
update_local_ipv4,
}
}
pub fn can_update(&self) -> bool {
@@ -253,10 +241,15 @@ impl NatTest {
let mut guard = self.info.lock();
guard.update_addr(index, ip, port)
}
pub fn update_tcp_port(&self, port: u16) {
let mut guard = self.info.lock();
guard.update_tcp_port(port)
}
pub fn re_test(
&self,
local_ipv4: Option<Ipv4Addr>,
ipv6: Option<Ipv6Addr>,
default_interface: &LocalInterface,
) -> anyhow::Result<NatInfo> {
let mut stun_server = self.stun_server.clone();
if stun_server.len() > 5 {
@@ -264,7 +257,8 @@ impl NatTest {
stun_server.truncate(5);
log::info!("stun_server truncate {:?}", stun_server);
}
let (nat_type, public_ips, port_range) = stun::stun_test_nat(stun_server)?;
let (nat_type, public_ips, port_range) =
stun::stun_test_nat(stun_server, default_interface)?;
if public_ips.is_empty() {
Err(anyhow!("public_ips.is_empty"))?
}
@@ -272,7 +266,9 @@ impl NatTest {
guard.nat_type = nat_type;
guard.public_ips = public_ips;
guard.public_port_range = port_range;
guard.local_ipv4 = local_ipv4;
if local_ipv4.is_some() {
guard.local_ipv4 = local_ipv4;
}
guard.ipv6 = ipv6;
Ok(guard.clone())
+13 -4
View File
@@ -4,17 +4,21 @@ use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use std::time::Duration;
use crate::channel::punch::NatType;
use crate::channel::socket::{bind_udp, LocalInterface};
use rand::RngCore;
use std::net::UdpSocket;
use stun_format::Attr;
pub fn stun_test_nat(stun_servers: Vec<String>) -> io::Result<(NatType, Vec<Ipv4Addr>, u16)> {
pub fn stun_test_nat(
stun_servers: Vec<String>,
default_interface: &LocalInterface,
) -> anyhow::Result<(NatType, Vec<Ipv4Addr>, u16)> {
let mut nat_type = NatType::Cone;
let mut port_range = 0;
let mut hash_set = HashSet::new();
for _ in 0..2 {
let stun_servers = stun_servers.clone();
match stun_test_nat0(stun_servers) {
match stun_test_nat0(stun_servers, default_interface) {
Ok((nat_type_t, ip_list_t, port_range_t)) => {
if nat_type_t == NatType::Symmetric {
nat_type = NatType::Symmetric;
@@ -34,8 +38,13 @@ pub fn stun_test_nat(stun_servers: Vec<String>) -> io::Result<(NatType, Vec<Ipv4
Ok((nat_type, hash_set.into_iter().collect(), port_range))
}
pub fn stun_test_nat0(stun_servers: Vec<String>) -> io::Result<(NatType, Vec<Ipv4Addr>, u16)> {
let udp = UdpSocket::bind("0.0.0.0:0")?;
pub fn stun_test_nat0(
stun_servers: Vec<String>,
default_interface: &LocalInterface,
) -> anyhow::Result<(NatType, Vec<Ipv4Addr>, u16)> {
let udp = bind_udp("0.0.0.0:0".parse().unwrap(), default_interface)?;
udp.set_nonblocking(false)?;
let udp: UdpSocket = udp.into();
udp.set_read_timeout(Some(Duration::from_millis(500)))?;
let mut nat_type = NatType::Cone;
let mut min_port = u16::MAX;
+6 -5
View File
@@ -66,11 +66,7 @@ pub fn start_port_mapping(
if vec.is_empty() {
return Ok(());
}
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.thread_name("portMapping")
.build()?;
runtime.block_on(start_port_mapping0(vec))?;
let (sender, receiver) = tokio::sync::oneshot::channel::<()>();
let worker = stop_manager.add_listener("portMapping".into(), move || {
let _ = sender.send(());
@@ -78,6 +74,11 @@ pub fn start_port_mapping(
thread::Builder::new()
.name("portMapping".into())
.spawn(move || {
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.thread_name("portMapping")
.build().unwrap();
runtime.block_on(start_port_mapping0(vec)).unwrap();
runtime.block_on(async {
let _ = receiver.await;
});
+1
View File
@@ -32,6 +32,7 @@ async fn tcp_mapping_(
}
async fn copy(source_tcp: TcpStream, destination: &String) -> anyhow::Result<()> {
// 或许这里也应该绑定最匹配的网卡,不然全局代理会影响映射
let dest_tcp = TcpStream::connect(destination)
.await
.with_context(|| format!("TCP connection target failed {:?}", destination))?;
+186 -43
View File
@@ -1,64 +1,85 @@
use crate::{DeviceConfig, ErrorInfo, ErrorType};
use std::io;
use std::net::Ipv4Addr;
use std::sync::Arc;
use tun::device::IFace;
use tun::Device;
use tun_rs::SyncDevice;
use crate::{DeviceConfig, ErrorInfo, ErrorType, VntCallback};
#[cfg(any(target_os = "windows", target_os = "linux"))]
const DEFAULT_TUN_NAME: &str = "vnt-tun";
#[cfg(target_os = "windows")]
const DEFAULT_TAP_NAME: &str = "vnt-tap";
pub fn create_device(config: DeviceConfig) -> Result<Arc<Device>, ErrorInfo> {
pub fn create_device<Call: VntCallback>(
config: DeviceConfig,
call: &Call,
) -> Result<Arc<SyncDevice>, ErrorInfo> {
let device = match create_device0(&config) {
Ok(device) => device,
Err(e) => {
return Err(ErrorInfo::new_msg(
ErrorType::Unknown,
ErrorType::FailedToCrateDevice,
format!("create device {:?}", e),
));
}
};
if let Err(e) = device.set_ip(config.virtual_ip, config.virtual_netmask) {
log::error!("LocalIpExists {:?}", e);
return Err(ErrorInfo::new_msg(
ErrorType::LocalIpExists,
format!("set_ip {:?}", e),
));
}
if let Err(e) = device.add_route(config.virtual_network, config.virtual_netmask, 1) {
log::warn!("添加默认路由失败 ={:?}", e);
}
if let Err(e) = device.add_route(Ipv4Addr::BROADCAST, Ipv4Addr::BROADCAST, 1) {
#[cfg(windows)]
let index = device.if_index().unwrap();
#[cfg(unix)]
let index = &device.name().unwrap();
if let Err(e) = add_route(index, Ipv4Addr::BROADCAST, Ipv4Addr::BROADCAST) {
log::warn!("添加广播路由失败 ={:?}", e);
}
if let Err(e) = device.add_route(
if let Err(e) = add_route(
index,
Ipv4Addr::from([224, 0, 0, 0]),
Ipv4Addr::from([240, 0, 0, 0]),
1,
) {
log::warn!("添加组播路由失败 ={:?}", e);
}
for (dest, mask) in config.external_route {
if let Err(e) = device.add_route(dest, mask, 1) {
log::warn!("添加路由失败 ={:?}", e);
if let Err(e) = add_route(index, dest, mask) {
log::warn!("添加路由失败,请检查-i参数是否和现有路由冲突 ={:?}", e);
call.error(ErrorInfo::new_msg(
ErrorType::Warn,
format!(
"警告! 添加路由失败,请检查-i参数是否和现有路由冲突 ={:?}",
e
),
))
}
}
Ok(device)
}
fn create_device0(config: &DeviceConfig) -> io::Result<Arc<Device>> {
fn create_device0(config: &DeviceConfig) -> io::Result<Arc<SyncDevice>> {
let mut tun_builder = tun_rs::DeviceBuilder::default();
tun_builder = tun_builder.ipv4(config.virtual_ip, config.virtual_netmask, None);
match &config.device_name {
None => {
#[cfg(any(target_os = "windows", target_os = "linux"))]
{
tun_builder = tun_builder.name(DEFAULT_TUN_NAME);
}
}
Some(name) => {
tun_builder = tun_builder.name(name);
}
}
#[cfg(target_os = "windows")]
let default_name: &str = if config.tap {
DEFAULT_TAP_NAME
} else {
DEFAULT_TUN_NAME
};
{
let name = config
.device_name
.clone()
.unwrap_or_else(|| DEFAULT_TUN_NAME.to_string());
_ = delete_adapter_info_from_reg(&name);
tun_builder = tun_builder.metric(0).ring_capacity(4 * 1024 * 1024);
}
#[cfg(target_os = "linux")]
let device = {
{
let device_name = config
.device_name
.clone()
@@ -66,20 +87,10 @@ fn create_device0(config: &DeviceConfig) -> io::Result<Arc<Device>> {
if &device_name == DEFAULT_TUN_NAME {
delete_device(DEFAULT_TUN_NAME);
}
Arc::new(Device::new(Some(device_name))?)
};
#[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,
)?);
device.set_mtu(config.mtu)?;
Ok(device)
}
let device = tun_builder.mtu(config.mtu as u16).build_sync()?;
Ok(Arc::new(device))
}
#[cfg(target_os = "linux")]
@@ -96,3 +107,135 @@ fn delete_device(name: &str) {
log::warn!("删除网卡失败:{:?}", delete_tun);
}
}
#[cfg(windows)]
fn delete_adapter_info_from_reg(dev_name: &str) -> std::io::Result<()> {
use std::collections::HashSet;
use winreg::{enums::HKEY_LOCAL_MACHINE, enums::KEY_ALL_ACCESS, RegKey};
let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
let profiles_key = hklm.open_subkey_with_flags(
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkList\\Profiles",
KEY_ALL_ACCESS,
)?;
let mut profile_guid_set = HashSet::new();
for sub_key_name in profiles_key.enum_keys().filter_map(Result::ok) {
let sub_key = profiles_key.open_subkey(&sub_key_name)?;
match sub_key.get_value::<String, _>("Description") {
Ok(profile_name) => {
if dev_name == profile_name {
match profiles_key.delete_subkey_all(&sub_key_name) {
Ok(_) => {
log::info!("deleted Profiles sub_key: {}", sub_key_name);
profile_guid_set.insert(sub_key_name);
}
Err(e) => {
log::warn!("Failed to delete Profiles sub_key {}: {}", sub_key_name, e)
}
}
}
}
Err(e) => log::warn!(
"Failed to read Description for sub_key {}: {}",
sub_key_name,
e
),
}
}
let unmanaged_key = hklm.open_subkey_with_flags(
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkList\\Signatures\\Unmanaged",
KEY_ALL_ACCESS,
)?;
for sub_key_name in unmanaged_key.enum_keys().filter_map(Result::ok) {
let sub_key = unmanaged_key.open_subkey(&sub_key_name)?;
match sub_key.get_value::<String, _>("ProfileGuid") {
Ok(profile_guid) => {
if profile_guid_set.contains(&profile_guid) {
match unmanaged_key.delete_subkey_all(&sub_key_name) {
Ok(_) => log::info!("deleted Unmanaged sub_key: {}", sub_key_name),
Err(e) => {
log::warn!("Failed to delete Unmanaged sub_key {}: {}", sub_key_name, e)
}
}
}
}
Err(e) => log::warn!(
"Failed to read Description for sub_key {}: {}",
sub_key_name,
e
),
}
}
Ok(())
}
#[cfg(target_os = "windows")]
pub fn add_route(index: u32, dest: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> {
let cmd = format!(
"route add {:?} mask {:?} {:?} metric {} if {}",
dest,
netmask,
Ipv4Addr::UNSPECIFIED,
1,
index
);
exe_cmd(&cmd)
}
#[cfg(target_os = "windows")]
pub fn exe_cmd(cmd: &str) -> io::Result<()> {
use std::os::windows::process::CommandExt;
println!("exe cmd: {}", cmd);
let out = std::process::Command::new("cmd")
.creation_flags(windows_sys::Win32::System::Threading::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(())
}
#[cfg(target_os = "macos")]
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(())
}
#[cfg(target_os = "linux")]
pub fn add_route(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> {
let cmd = if netmask.is_broadcast() {
format!("route add -host {:?} {}", address, name)
} else {
format!(
"route add -net {}/{} {}",
address,
u32::from(netmask).count_ones(),
name
)
};
exe_cmd(&cmd)?;
Ok(())
}
#[cfg(any(target_os = "macos", target_os = "linux"))]
pub fn exe_cmd(cmd: &str) -> io::Result<std::process::Output> {
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)
}
+7 -10
View File
@@ -3,12 +3,6 @@ use std::io;
use std::net::Ipv4Addr;
use std::sync::Arc;
use crossbeam_utils::atomic::AtomicCell;
use parking_lot::Mutex;
use tun::device::IFace;
use tun::Device;
use crate::channel::context::ChannelContext;
use crate::cipher::Cipher;
use crate::compression::Compressor;
@@ -19,15 +13,18 @@ use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo};
use crate::ip_proxy::IpProxyMap;
use crate::tun_tap_device::vnt_device::DeviceWrite;
use crate::util::StopManager;
use crossbeam_utils::atomic::AtomicCell;
use parking_lot::Mutex;
use tun_rs::SyncDevice;
#[repr(transparent)]
#[derive(Clone, Default)]
pub struct DeviceAdapter {
tun: Arc<Mutex<Option<Arc<Device>>>>,
tun: Arc<Mutex<Option<Arc<SyncDevice>>>>,
}
impl DeviceAdapter {
pub fn insert(&self, device: Arc<Device>) {
pub fn insert(&self, device: Arc<SyncDevice>) {
let r = self.tun.lock().replace(device);
assert!(r.is_none());
}
@@ -41,7 +38,7 @@ impl DeviceWrite for DeviceAdapter {
#[inline]
fn write(&self, buf: &[u8]) -> io::Result<usize> {
if let Some(tun) = self.tun.lock().as_ref() {
tun.write(buf)
tun.send(buf)
} else {
Err(io::Error::new(io::ErrorKind::NotFound, "not tun device"))
}
@@ -119,7 +116,7 @@ impl TunDeviceHelper {
}
}
/// 要保证先stop 再start
pub fn start(&self, device: Arc<Device>, allow_wire_guard: bool) -> io::Result<()> {
pub fn start(&self, device: Arc<SyncDevice>, allow_wire_guard: bool) -> io::Result<()> {
self.device_adapter.insert(device.clone());
let device_stop = DeviceStop::default();
let s = self.device_stop.lock().replace(device_stop.clone());
+174 -26
View File
@@ -1,3 +1,8 @@
use crate::channel::socket::LocalInterface;
use anyhow::Context;
use dns_parser::{Builder, Packet, QueryClass, QueryType, RData, ResponseCode};
use http_req::request::{RedirectPolicy, Request};
use http_req::uri::Uri;
use std::cell::RefCell;
use std::collections::HashMap;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs, UdpSocket};
@@ -5,9 +10,6 @@ use std::str::FromStr;
use std::time::Duration;
use std::{io, thread};
use anyhow::Context;
use dns_parser::{Builder, Packet, QueryClass, QueryType, RData, ResponseCode};
thread_local! {
static HISTORY: RefCell<HashMap<SocketAddr,usize>> = RefCell::new(HashMap::new());
}
@@ -79,22 +81,49 @@ fn address_choose0(addrs: Vec<SocketAddr>) -> anyhow::Result<SocketAddr> {
pub fn dns_query_all(
domain: &str,
mut name_servers: Vec<String>,
default_interface: &LocalInterface,
) -> anyhow::Result<Vec<SocketAddr>> {
match SocketAddr::from_str(domain) {
let mut current_domain = domain.to_string(); // 引入可变变量存储当前域名
match SocketAddr::from_str(&current_domain) {
Ok(addr) => Ok(vec![addr]),
Err(_) => {
let txt_domain = domain
// 重定向判断 http:
let current_domain_lower = current_domain.to_lowercase();
let redirect_domain = current_domain_lower
.strip_prefix("http:")
.or_else(|| current_domain_lower.strip_prefix("https:"))
.map(|v| v.to_string());
// 执行重定向检查
if let Some(stripped) = redirect_domain {
if let Some(redirected_url) = check_for_redirect(&stripped)? {
// 去掉 URL 开头的协议部分
let final_domain = remove_http_prefix(&redirected_url);
println!("Server Address: {}", final_domain);
// 检查是否为 IP 和端口组合
if let Ok(socket_addr) = SocketAddr::from_str(&final_domain) {
// 如果是 IP 和端口格式,直接返回结果
return Ok(vec![socket_addr]);
} else {
// 如果不是 IP 和端口格式,则更新为重定向地址
current_domain = final_domain;
}
}
}
let txt_domain = current_domain
.to_lowercase()
.strip_prefix("txt:")
.map(|v| v.to_string());
if name_servers.is_empty() {
if txt_domain.is_some() {
name_servers.push("223.5.5.5:53".into());
name_servers.push("119.29.29.29:53".into());
name_servers.push("114.114.114.114:53".into());
} else {
return Ok(domain
return Ok(current_domain
.to_socket_addrs()
.with_context(|| format!("DNS query failed {:?}", domain))?
.with_context(|| format!("DNS query failed {:?}", current_domain))?
.collect());
}
}
@@ -102,9 +131,10 @@ pub fn dns_query_all(
let mut err: Option<anyhow::Error> = None;
for name_server in name_servers {
if let Some(domain) = txt_domain.as_ref() {
match txt_dns(domain, name_server) {
match txt_dns(domain, name_server, default_interface) {
Ok(addr) => {
if !addr.is_empty() {
println!("TXT: {:?}", addr);
return Ok(addr);
}
}
@@ -118,21 +148,24 @@ pub fn dns_query_all(
}
continue;
}
let end_index = domain
let end_index = current_domain
.rfind(':')
.with_context(|| format!("{:?} not port", domain))?;
.with_context(|| format!("{:?} not port", current_domain))?;
let host = &domain[..end_index];
let port = u16::from_str(&domain[end_index + 1..])
.with_context(|| format!("{:?} not port", domain))?;
.with_context(|| format!("{:?} not port", current_domain))?;
let th1 = {
let host = host.to_string();
let name_server = name_server.clone();
thread::spawn(move || a_dns(host, name_server))
let default_interface = default_interface.clone();
thread::spawn(move || a_dns(host, name_server, &default_interface))
};
let th2 = {
let host = host.to_string();
let name_server = name_server.clone();
thread::spawn(move || aaaa_dns(host, name_server))
let default_interface = default_interface.clone();
thread::spawn(move || aaaa_dns(host, name_server, &default_interface))
};
let mut addr = Vec::new();
match th1.join().unwrap() {
@@ -170,12 +203,110 @@ pub fn dns_query_all(
if let Some(e) = err {
Err(e)
} else {
Err(anyhow::anyhow!("DNS query failed {:?}", domain))
Err(anyhow::anyhow!("DNS query failed {:?}", current_domain))
}
}
}
}
fn parse_host_port(addr: &str) -> bool {
// 处理 IPv6 地址(格式为 [::1]:8080
if addr.starts_with('[') {
if let Some(idx) = addr.rfind(']') {
if let Some(port_idx) = addr[idx + 1..].find(':') {
let port = &addr[idx + 1 + port_idx + 1..]; // 提取端口部分
return !port.is_empty() && port.chars().all(|c| c.is_numeric());
}
}
} else {
// 处理 IPv4 和普通域名(格式为 example.com:443 或 192.168.1.1:8080
if let Some((_host, port)) = addr.rsplit_once(':') {
return !port.is_empty() && port.chars().all(|c| c.is_numeric());
}
}
false
}
fn check_for_redirect(domain: &String) -> anyhow::Result<Option<String>> {
// 确保域名有 http:// 或 https:// 前缀
let mut url = if domain.starts_with("http://") || domain.starts_with("https://") {
domain.clone()
} else {
format!("http://{}", domain)
};
let mut count = 0; // 重定向次数计数器
let mut last_redirect_url: Option<String> = None; // 记录最后一个重定向的 URL
loop {
count += 1;
if count > 3 {
println!("重定向次数超过 3 次,跳过");
return Ok(last_redirect_url);
}
// 解析 URL
let uri = match Uri::try_from(url.as_str()) {
Ok(u) => u,
Err(e) => {
println!("解析地址失败: {}", e);
return Ok(last_redirect_url);
}
};
let mut response_body = Vec::new();
// 发送 HTTP 请求
let response = match Request::new(&uri)
.timeout(Duration::from_secs(10))
.redirect_policy(RedirectPolicy::Limit(0))
.send(&mut response_body)
{
Ok(resp) => {
println!("HTTP Status Code: {}", resp.status_code());
resp
}
Err(e) => {
return Ok(last_redirect_url);
}
};
let body_str = String::from_utf8_lossy(&response_body);
let cleaned_body = body_str.replace('\n', "").replace('\r', "");
println!("Response Body: {}", cleaned_body);
// 处理 3XX 重定向
if response.status_code().is_redirect() {
if let Some(location) = response.headers().get("Location") {
url = location.to_string().trim_end_matches('/').to_string();
last_redirect_url = Some(url.clone()); // 更新最后的重定向地址
println!("Location: {}", url);
continue;
} else {
return Ok(last_redirect_url);
}
}
// 处理 200 响应
else if response.status_code().is_success() {
for line in body_str.lines() {
let trimmed = line.trim();
if parse_host_port(trimmed) {
println!("text: {}", trimmed);
return Ok(Some(trimmed.to_string()));
}
}
return Ok(last_redirect_url);
}
return Ok(last_redirect_url);
}
}
/// 去掉 http:// 或 https:// 前缀
fn remove_http_prefix(url: &str) -> String {
url.trim_start_matches("http://")
.trim_start_matches("https://")
.to_string()
}
fn query<'a>(
udp: &UdpSocket,
domain: &str,
@@ -230,9 +361,13 @@ fn query<'a>(
Ok(pkt)
}
pub fn txt_dns(domain: &str, name_server: String) -> anyhow::Result<Vec<SocketAddr>> {
pub fn txt_dns(
domain: &str,
name_server: String,
default_interface: &LocalInterface,
) -> anyhow::Result<Vec<SocketAddr>> {
let name_server: SocketAddr = name_server.parse()?;
let udp = bind_udp(name_server)?;
let udp = bind_udp(name_server, default_interface)?;
let mut buf = [0; 65536];
let message = query(&udp, domain, name_server, QueryType::TXT, &mut buf)?;
let mut rs = Vec::new();
@@ -249,19 +384,28 @@ pub fn txt_dns(domain: &str, name_server: String) -> anyhow::Result<Vec<SocketAd
Ok(rs)
}
fn bind_udp(name_server: SocketAddr) -> anyhow::Result<UdpSocket> {
let udp = if name_server.is_ipv4() {
UdpSocket::bind("0.0.0.0:0")?
fn bind_udp(
name_server: SocketAddr,
default_interface: &LocalInterface,
) -> anyhow::Result<UdpSocket> {
let addr: SocketAddr = if name_server.is_ipv4() {
"0.0.0.0:0".parse().unwrap()
} else {
UdpSocket::bind("[::]:0")?
"[::]:0".parse().unwrap()
};
udp.set_read_timeout(Some(Duration::from_millis(800)))?;
Ok(udp)
let socket = crate::channel::socket::bind_udp(addr, default_interface)?;
socket.set_nonblocking(false)?;
socket.set_read_timeout(Some(Duration::from_millis(800)))?;
Ok(socket.into())
}
pub fn a_dns(domain: String, name_server: String) -> anyhow::Result<Vec<Ipv4Addr>> {
pub fn a_dns(
domain: String,
name_server: String,
default_interface: &LocalInterface,
) -> anyhow::Result<Vec<Ipv4Addr>> {
let name_server: SocketAddr = name_server.parse()?;
let udp = bind_udp(name_server)?;
let udp = bind_udp(name_server, default_interface)?;
let mut buf = [0; 65536];
let message = query(&udp, &domain, name_server, QueryType::A, &mut buf)?;
let mut rs = Vec::new();
@@ -273,9 +417,13 @@ pub fn a_dns(domain: String, name_server: String) -> anyhow::Result<Vec<Ipv4Addr
Ok(rs)
}
pub fn aaaa_dns(domain: String, name_server: String) -> anyhow::Result<Vec<Ipv6Addr>> {
pub fn aaaa_dns(
domain: String,
name_server: String,
default_interface: &LocalInterface,
) -> anyhow::Result<Vec<Ipv6Addr>> {
let name_server: SocketAddr = name_server.parse()?;
let udp = bind_udp(name_server)?;
let udp = bind_udp(name_server, default_interface)?;
let mut buf = [0; 65536];
let message = query(&udp, &domain, name_server, QueryType::AAAA, &mut buf)?;
let mut rs = Vec::new();
-33
View File
@@ -1,33 +0,0 @@
[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"
sha2 = { version = "0.10.6", features = ["oid"] }
[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"
]}
-61
View File
@@ -1,61 +0,0 @@
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 Device {
pub fn as_tun_fd(&self) -> &Fd {
&self.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)
}
}
-24
View File
@@ -1,24 +0,0 @@
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>;
}
-33
View File
@@ -1,33 +0,0 @@
/// 参考
/// https://github.com/meh/rust-tun
/// https://github.com/Tazdevil971/tap-windows
/// https://github.com/nulldotblack/wintun
pub mod device;
#[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;
#[cfg(windows)]
mod packet;
-281
View File
@@ -1,281 +0,0 @@
#![allow(dead_code)]
use std::ffi::{CStr, CString};
use std::net::Ipv4Addr;
use std::os::fd::AsRawFd;
use std::{io, mem, ptr};
use libc::{
c_char, c_short, ifreq, AF_INET, IFF_MULTI_QUEUE, IFF_NO_PI, IFF_RUNNING, IFF_TUN, IFF_UP,
IFNAMSIZ, O_RDWR, SOCK_DGRAM,
};
use crate::device::IFace;
use crate::linux::route;
use crate::linux::sys::*;
use crate::unix::{exe_cmd, Fd, SockAddr};
pub struct Device {
name: String,
ctl: Fd,
tun: Fd,
}
impl Device {
pub fn new(name: Option<String>) -> 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 = IFF_TUN as 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 set_txqueuelen = format!("ifconfig {} txqueuelen 1000", name);
if let Err(e) = exe_cmd(&set_txqueuelen) {
log::warn!("{:?}", e);
}
Device { name, 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.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 Device {
pub fn as_tun_fd(&self) -> &Fd {
&self.tun
}
}
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> {
self.tun.read(buf)
}
fn write(&self, buf: &[u8]) -> io::Result<usize> {
self.tun.write(buf)
}
}
-4
View File
@@ -1,4 +0,0 @@
mod device;
pub use device::Device;
mod route;
mod sys;
-16
View File
@@ -1,16 +0,0 @@
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(())
}
-21
View File
@@ -1,21 +0,0 @@
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);
-297
View File
@@ -1,297 +0,0 @@
#![allow(dead_code)]
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 Device {
pub fn as_tun_fd(&self) -> &Fd {
&self.tun
}
}
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)
}
}
-5
View File
@@ -1,5 +0,0 @@
mod device;
pub use device::Device;
mod sys;
mod route;
-20
View File
@@ -1,20 +0,0 @@
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(())
}
-119
View File
@@ -1,119 +0,0 @@
//! Bindings to internal macOS stuff.
use ioctl::*;
use libc::{c_char, c_int, c_short, c_uint, c_ushort, c_void, sockaddr, IFNAMSIZ};
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 {
pub ctl_id: c_uint,
pub ctl_name: [c_char; 96],
}
#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Copy, Clone)]
pub struct sockaddr_ctl {
pub sc_len: c_char,
pub sc_family: c_char,
pub ss_sysaddr: c_ushort,
pub sc_id: c_uint,
pub sc_unit: c_uint,
pub sc_reserved: [c_uint; 5],
}
#[repr(C)]
#[derive(Copy, Clone)]
pub union ifrn {
pub name: [c_char; IFNAMSIZ],
}
#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Copy, Clone)]
pub struct ifdevmtu {
pub current: c_int,
pub min: c_int,
pub max: c_int,
}
#[repr(C)]
#[derive(Copy, Clone)]
pub union ifku {
pub ptr: *mut c_void,
pub value: c_int,
}
#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Copy, Clone)]
pub struct ifkpi {
pub module_id: c_uint,
pub type_: c_uint,
pub ifku: ifku,
}
#[repr(C)]
#[derive(Copy, Clone)]
pub union ifru {
pub addr: sockaddr,
pub dstaddr: sockaddr,
pub broadaddr: sockaddr,
pub flags: c_short,
pub metric: c_int,
pub mtu: c_int,
pub phys: c_int,
pub media: c_int,
pub intval: c_int,
pub data: *mut c_void,
pub devmtu: ifdevmtu,
pub wake_flags: c_uint,
pub route_refcnt: c_uint,
pub cap: [c_int; 2],
pub functional_type: c_uint,
}
#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Copy, Clone)]
pub struct ifreq {
pub ifrn: ifrn,
pub ifru: ifru,
}
#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Copy, Clone)]
pub struct ifaliasreq {
pub ifran: [c_char; IFNAMSIZ],
pub addr: sockaddr,
pub broadaddr: sockaddr,
pub mask: sockaddr,
}
ioctl!(readwrite ctliocginfo with 'N', 3; ctl_info);
ioctl!(write siocsifflags with 'i', 16; ifreq);
ioctl!(readwrite siocgifflags with 'i', 17; ifreq);
ioctl!(write siocsifaddr with 'i', 12; ifreq);
ioctl!(readwrite siocgifaddr with 'i', 33; ifreq);
ioctl!(write siocsifdstaddr with 'i', 14; ifreq);
ioctl!(readwrite siocgifdstaddr with 'i', 34; ifreq);
ioctl!(write siocsifbrdaddr with 'i', 19; ifreq);
ioctl!(readwrite siocgifbrdaddr with 'i', 35; ifreq);
ioctl!(write siocsifnetmask with 'i', 22; ifreq);
ioctl!(readwrite siocgifnetmask with 'i', 37; ifreq);
ioctl!(write siocsifmtu with 'i', 52; ifreq);
ioctl!(readwrite siocgifmtu with 'i', 51; ifreq);
ioctl!(write siocaifaddr with 'i', 26; ifaliasreq);
ioctl!(write siocdifaddr with 'i', 25; ifreq);
-1
View File
@@ -1 +0,0 @@
pub mod packet;
-123
View File
@@ -1,123 +0,0 @@
#![allow(dead_code)]
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响应,3RARP请求,4RARP响应
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响应,3RARP请求,4RARP响应
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()
}
}
-2
View File
@@ -1,2 +0,0 @@
pub mod packet;
pub mod protocol;
-80
View File
@@ -1,80 +0,0 @@
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()
}
}
-141
View File
@@ -1,141 +0,0 @@
/// 以太网帧协议
#[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,
}
}
}
-67
View File
@@ -1,67 +0,0 @@
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(&ether.buffer)
}
-68
View File
@@ -1,68 +0,0 @@
use libc::{fcntl, F_GETFL, F_SETFL, O_NONBLOCK};
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))
}
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 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
}
}
#[cfg(not(target_os = "android"))]
impl Drop for Fd {
fn drop(&mut self) {
unsafe {
libc::close(self.0);
}
}
}
-28
View File
@@ -1,28 +0,0 @@
mod fd;
pub use fd::Fd;
#[cfg(any(target_os = "macos", target_os = "linux"))]
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)
}
-67
View File
@@ -1,67 +0,0 @@
use libc::{in_addr, sockaddr, sockaddr_in};
use std::{io, mem, net::Ipv4Addr, ptr};
use io::Result;
/// A wrapper for `sockaddr_in`.
#[derive(Copy, Clone)]
pub struct SockAddr(sockaddr_in);
impl SockAddr {
/// Create a new `SockAddr` from a generic `sockaddr`.
pub fn new(value: &sockaddr) -> Result<Self> {
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) }
}
/// # Safety
/// Create a new `SockAddr` and not check the source.
pub unsafe fn unchecked(value: &sockaddr) -> Result<Self> {
Ok(SockAddr(ptr::read(value as *const _ as *const _)))
}
/// # Safety
/// Get a generic pointer to the `SockAddr`.
pub unsafe fn as_ptr(&self) -> *const sockaddr {
&self.0 as *const _ as *const sockaddr
}
}
impl From<Ipv4Addr> for SockAddr {
fn from(ip: Ipv4Addr) -> SockAddr {
let octets = ip.octets();
let mut addr = unsafe { mem::zeroed::<sockaddr_in>() };
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),
};
SockAddr(addr)
}
}
impl From<SockAddr> for Ipv4Addr {
fn from(addr: SockAddr) -> Ipv4Addr {
let ip = addr.0.sin_addr.s_addr;
let [a, b, c, d] = ip.to_ne_bytes();
Ipv4Addr::new(a, b, c, d)
}
}
impl From<SockAddr> for sockaddr {
fn from(addr: SockAddr) -> sockaddr {
unsafe { mem::transmute(addr.0) }
}
}
impl From<SockAddr> for sockaddr_in {
fn from(addr: SockAddr) -> sockaddr_in {
addr.0
}
}
-91
View File
@@ -1,91 +0,0 @@
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),
}
}
}
-502
View File
@@ -1,502 +0,0 @@
// 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(()),
}
}
-43
View File
@@ -1,43 +0,0 @@
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(())
}
-48
View File
@@ -1,48 +0,0 @@
#![allow(dead_code)]
use crate::windows::exe_cmd;
use std::io;
use std::net::Ipv4Addr;
/// 设置网卡名称
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)
}
-33
View File
@@ -1,33 +0,0 @@
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)
}
-185
View File
@@ -1,185 +0,0 @@
#![allow(dead_code)]
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::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 (),
)
}
}
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)
}
}
}
-362
View File
@@ -1,362 +0,0 @@
#![allow(dead_code)]
use libloading::Library;
use sha2::Digest;
use std::io;
use std::net::Ipv4Addr;
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);
if Self::delete_for_name(&win_tun, &name_utf16).is_ok() {
std::thread::sleep(std::time::Duration::from_millis(500));
}
let guid_bytes: [u8; 16] = hash_guid(&name);
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 = 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, 4 * 1024 * 1024);
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(())
}
}
fn hash_guid(input: &str) -> [u8; 16] {
let mut hasher = sha2::Sha256::new();
hasher.update(b"VNT");
hasher.update(input.as_bytes());
hasher.update(b"2024");
let hash: [u8; 32] = hasher.finalize().into();
hash[..16].try_into().unwrap()
}
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 winapi::shared::minwindef::TRUE == 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,
format!("try_receive failed {:?}", io::Error::last_os_error()),
))
}
} 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() {
Ok(data) => match data {
None => {
continue;
}
Some(packet) => {
return Ok(packet);
}
},
Err(e) => {
if i > 10 {
// 某些系统存在错误退出的情况(原因不明),这里尝试忽略部分错误
return Err(e);
}
}
}
}
//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 {
//Shutdown event triggered
return Err(io::Error::new(
io::ErrorKind::Other,
format!("Shutdown event triggered {}", io::Error::last_os_error()),
));
}
}
}
}
}
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 winapi::shared::minwindef::FALSE == self.win_tun.WintunDeleteDriver() {
log::warn!("WintunDeleteDriver failed {:?}", io::Error::last_os_error())
}
}
}
}
-64
View File
@@ -1,64 +0,0 @@
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
}
}
}
}
-48
View File
@@ -1,48 +0,0 @@
#![allow(dead_code)]
use log::*;
use crate::windows::tun::wintun_raw;
use std::sync::atomic::{AtomicBool, Ordering};
use widestring::U16CStr;
/// Sets the logger wintun will use when logging. Maps to the WintunSetLogger C function
pub fn set_logger(win_tun: &wintun_raw::wintun, f: wintun_raw::WINTUN_LOGGER_CALLBACK) {
unsafe { win_tun.WintunSetLogger(f) };
}
pub fn reset_logger(win_tun: &wintun_raw::wintun) {
set_logger(win_tun, None);
}
static SET_LOGGER: AtomicBool = AtomicBool::new(false);
/// The logger that is active by default. Logs messages to the log crate
///
/// # Safety
/// `message` must be a valid pointer that points to an aligned null terminated UTF-16 string
pub unsafe extern "C" fn default_logger(
level: wintun_raw::WINTUN_LOGGER_LEVEL,
_timestamp: wintun_raw::DWORD64,
message: *const wintun_raw::WCHAR,
) {
//Cant wait for RFC 2585
#[allow(unused_unsafe)]
//Wintun will always give us a valid UTF16 null termineted string
let msg = unsafe { U16CStr::from_ptr_str(message) };
let utf8_msg = msg.to_string_lossy();
match level {
wintun_raw::WINTUN_LOGGER_LEVEL_WINTUN_LOG_INFO => info!("WinTun: {}", utf8_msg),
wintun_raw::WINTUN_LOGGER_LEVEL_WINTUN_LOG_WARN => warn!("WinTun: {}", utf8_msg),
wintun_raw::WINTUN_LOGGER_LEVEL_WINTUN_LOG_ERR => error!("WinTun: {}", utf8_msg),
_ => error!("WinTun: {} (with invalid log level {})", utf8_msg, level),
}
}
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(win_tun, Some(default_logger));
}
}
-449
View File
@@ -1,449 +0,0 @@
#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
/* automatically generated by rust-bindgen 0.59.1 */
#[repr(C)]
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct __BindgenBitfieldUnit<Storage> {
storage: Storage,
}
impl<Storage> __BindgenBitfieldUnit<Storage> {
#[inline]
pub const fn new(storage: Storage) -> Self {
Self { storage }
}
}
impl<Storage> __BindgenBitfieldUnit<Storage>
where
Storage: AsRef<[u8]> + AsMut<[u8]>,
{
#[inline]
pub fn get_bit(&self, index: usize) -> bool {
debug_assert!(index / 8 < self.storage.as_ref().len());
let byte_index = index / 8;
let byte = self.storage.as_ref()[byte_index];
let bit_index = if cfg!(target_endian = "big") {
7 - (index % 8)
} else {
index % 8
};
let mask = 1 << bit_index;
byte & mask == mask
}
#[inline]
pub fn set_bit(&mut self, index: usize, val: bool) {
debug_assert!(index / 8 < self.storage.as_ref().len());
let byte_index = index / 8;
let byte = &mut self.storage.as_mut()[byte_index];
let bit_index = if cfg!(target_endian = "big") {
7 - (index % 8)
} else {
index % 8
};
let mask = 1 << bit_index;
if val {
*byte |= mask;
} else {
*byte &= !mask;
}
}
#[inline]
pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 {
debug_assert!(bit_width <= 64);
debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
let mut val = 0;
for i in 0..(bit_width as usize) {
if self.get_bit(i + bit_offset) {
let index = if cfg!(target_endian = "big") {
bit_width as usize - 1 - i
} else {
i
};
val |= 1 << index;
}
}
val
}
#[inline]
pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) {
debug_assert!(bit_width <= 64);
debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
for i in 0..(bit_width as usize) {
let mask = 1 << i;
let val_bit_is_set = val & mask == mask;
let index = if cfg!(target_endian = "big") {
bit_width as usize - 1 - i
} else {
i
};
self.set_bit(index + bit_offset, val_bit_is_set);
}
}
}
pub type wchar_t = ::std::os::raw::c_ushort;
pub type DWORD = ::std::os::raw::c_ulong;
pub type BOOL = ::std::os::raw::c_int;
pub type BYTE = ::std::os::raw::c_uchar;
pub type ULONG64 = ::std::os::raw::c_ulonglong;
pub type DWORD64 = ::std::os::raw::c_ulonglong;
pub type WCHAR = wchar_t;
pub type LPCWSTR = *const WCHAR;
pub type HANDLE = *mut ::std::os::raw::c_void;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct _GUID {
pub Data1: ::std::os::raw::c_ulong,
pub Data2: ::std::os::raw::c_ushort,
pub Data3: ::std::os::raw::c_ushort,
pub Data4: [::std::os::raw::c_uchar; 8usize],
}
#[test]
fn bindgen_test_layout__GUID() {
assert_eq!(
::std::mem::size_of::<_GUID>(),
16usize,
concat!("Size of: ", stringify!(_GUID))
);
assert_eq!(
::std::mem::align_of::<_GUID>(),
4usize,
concat!("Alignment of ", stringify!(_GUID))
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<_GUID>())).Data1 as *const _ as usize },
0usize,
concat!(
"Offset of field: ",
stringify!(_GUID),
"::",
stringify!(Data1)
)
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<_GUID>())).Data2 as *const _ as usize },
4usize,
concat!(
"Offset of field: ",
stringify!(_GUID),
"::",
stringify!(Data2)
)
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<_GUID>())).Data3 as *const _ as usize },
6usize,
concat!(
"Offset of field: ",
stringify!(_GUID),
"::",
stringify!(Data3)
)
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<_GUID>())).Data4 as *const _ as usize },
8usize,
concat!(
"Offset of field: ",
stringify!(_GUID),
"::",
stringify!(Data4)
)
);
}
pub type GUID = _GUID;
#[repr(C)]
#[derive(Copy, Clone)]
pub union _NET_LUID_LH {
pub Value: ULONG64,
pub Info: _NET_LUID_LH__bindgen_ty_1,
}
#[repr(C)]
#[repr(align(8))]
#[derive(Debug, Copy, Clone)]
pub struct _NET_LUID_LH__bindgen_ty_1 {
pub _bitfield_align_1: [u32; 0],
pub _bitfield_1: __BindgenBitfieldUnit<[u8; 8usize]>,
}
#[test]
fn bindgen_test_layout__NET_LUID_LH__bindgen_ty_1() {
assert_eq!(
::std::mem::size_of::<_NET_LUID_LH__bindgen_ty_1>(),
8usize,
concat!("Size of: ", stringify!(_NET_LUID_LH__bindgen_ty_1))
);
assert_eq!(
::std::mem::align_of::<_NET_LUID_LH__bindgen_ty_1>(),
8usize,
concat!("Alignment of ", stringify!(_NET_LUID_LH__bindgen_ty_1))
);
}
impl _NET_LUID_LH__bindgen_ty_1 {
#[inline]
pub fn Reserved(&self) -> ULONG64 {
unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 24u8) as u64) }
}
#[inline]
pub fn set_Reserved(&mut self, val: ULONG64) {
unsafe {
let val: u64 = ::std::mem::transmute(val);
self._bitfield_1.set(0usize, 24u8, val as u64)
}
}
#[inline]
pub fn NetLuidIndex(&self) -> ULONG64 {
unsafe { ::std::mem::transmute(self._bitfield_1.get(24usize, 24u8) as u64) }
}
#[inline]
pub fn set_NetLuidIndex(&mut self, val: ULONG64) {
unsafe {
let val: u64 = ::std::mem::transmute(val);
self._bitfield_1.set(24usize, 24u8, val as u64)
}
}
#[inline]
pub fn IfType(&self) -> ULONG64 {
unsafe { ::std::mem::transmute(self._bitfield_1.get(48usize, 16u8) as u64) }
}
#[inline]
pub fn set_IfType(&mut self, val: ULONG64) {
unsafe {
let val: u64 = ::std::mem::transmute(val);
self._bitfield_1.set(48usize, 16u8, val as u64)
}
}
#[inline]
pub fn new_bitfield_1(
Reserved: ULONG64,
NetLuidIndex: ULONG64,
IfType: ULONG64,
) -> __BindgenBitfieldUnit<[u8; 8usize]> {
let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 8usize]> = Default::default();
__bindgen_bitfield_unit.set(0usize, 24u8, {
let Reserved: u64 = unsafe { ::std::mem::transmute(Reserved) };
Reserved as u64
});
__bindgen_bitfield_unit.set(24usize, 24u8, {
let NetLuidIndex: u64 = unsafe { ::std::mem::transmute(NetLuidIndex) };
NetLuidIndex as u64
});
__bindgen_bitfield_unit.set(48usize, 16u8, {
let IfType: u64 = unsafe { ::std::mem::transmute(IfType) };
IfType as u64
});
__bindgen_bitfield_unit
}
}
#[test]
fn bindgen_test_layout__NET_LUID_LH() {
assert_eq!(
::std::mem::size_of::<_NET_LUID_LH>(),
8usize,
concat!("Size of: ", stringify!(_NET_LUID_LH))
);
assert_eq!(
::std::mem::align_of::<_NET_LUID_LH>(),
8usize,
concat!("Alignment of ", stringify!(_NET_LUID_LH))
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<_NET_LUID_LH>())).Value as *const _ as usize },
0usize,
concat!(
"Offset of field: ",
stringify!(_NET_LUID_LH),
"::",
stringify!(Value)
)
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<_NET_LUID_LH>())).Info as *const _ as usize },
0usize,
concat!(
"Offset of field: ",
stringify!(_NET_LUID_LH),
"::",
stringify!(Info)
)
);
}
pub type NET_LUID_LH = _NET_LUID_LH;
pub type NET_LUID = NET_LUID_LH;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct _WINTUN_ADAPTER {
_unused: [u8; 0],
}
#[doc = " A handle representing Wintun adapter"]
pub type WINTUN_ADAPTER_HANDLE = *mut _WINTUN_ADAPTER;
#[doc = "< Informational"]
pub const WINTUN_LOGGER_LEVEL_WINTUN_LOG_INFO: WINTUN_LOGGER_LEVEL = 0;
#[doc = "< Warning"]
pub const WINTUN_LOGGER_LEVEL_WINTUN_LOG_WARN: WINTUN_LOGGER_LEVEL = 1;
#[doc = "< Error"]
pub const WINTUN_LOGGER_LEVEL_WINTUN_LOG_ERR: WINTUN_LOGGER_LEVEL = 2;
#[doc = " Determines the level of logging, passed to WINTUN_LOGGER_CALLBACK."]
pub type WINTUN_LOGGER_LEVEL = ::std::os::raw::c_int;
#[doc = " Called by internal logger to report diagnostic messages"]
#[doc = ""]
#[doc = " @param Level Message level."]
#[doc = ""]
#[doc = " @param Timestamp Message timestamp in in 100ns intervals since 1601-01-01 UTC."]
#[doc = ""]
#[doc = " @param Message Message text."]
pub type WINTUN_LOGGER_CALLBACK = ::std::option::Option<
unsafe extern "C" fn(Level: WINTUN_LOGGER_LEVEL, Timestamp: DWORD64, Message: LPCWSTR),
>;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct _TUN_SESSION {
_unused: [u8; 0],
}
#[doc = " A handle representing Wintun session"]
pub type WINTUN_SESSION_HANDLE = *mut _TUN_SESSION;
extern crate libloading;
pub struct wintun {
__library: ::libloading::Library,
pub WintunCreateAdapter: unsafe extern "C" fn(
arg1: LPCWSTR,
arg2: LPCWSTR,
arg3: *const GUID,
) -> WINTUN_ADAPTER_HANDLE,
pub WintunCloseAdapter: unsafe extern "C" fn(arg1: WINTUN_ADAPTER_HANDLE),
pub WintunOpenAdapter: unsafe extern "C" fn(arg1: LPCWSTR) -> WINTUN_ADAPTER_HANDLE,
pub WintunGetAdapterLUID:
unsafe extern "C" fn(arg1: WINTUN_ADAPTER_HANDLE, arg2: *mut NET_LUID),
pub WintunGetRunningDriverVersion: unsafe extern "C" fn() -> DWORD,
pub WintunDeleteDriver: unsafe extern "C" fn() -> BOOL,
pub WintunSetLogger: unsafe extern "C" fn(arg1: WINTUN_LOGGER_CALLBACK),
pub WintunStartSession:
unsafe extern "C" fn(arg1: WINTUN_ADAPTER_HANDLE, arg2: DWORD) -> WINTUN_SESSION_HANDLE,
pub WintunEndSession: unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE),
pub WintunGetReadWaitEvent: unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE) -> HANDLE,
pub WintunReceivePacket:
unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE, arg2: *mut DWORD) -> *mut BYTE,
pub WintunReleaseReceivePacket:
unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE, arg2: *const BYTE),
pub WintunAllocateSendPacket:
unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE, arg2: DWORD) -> *mut BYTE,
pub WintunSendPacket: unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE, arg2: *const BYTE),
}
impl wintun {
pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
where
P: AsRef<::std::ffi::OsStr>,
{
let library = ::libloading::Library::new(path)?;
Self::from_library(library)
}
pub unsafe fn from_library<L>(library: L) -> Result<Self, ::libloading::Error>
where
L: Into<::libloading::Library>,
{
let __library = library.into();
let WintunCreateAdapter = __library.get(b"WintunCreateAdapter\0").map(|sym| *sym)?;
let WintunCloseAdapter = __library.get(b"WintunCloseAdapter\0").map(|sym| *sym)?;
let WintunOpenAdapter = __library.get(b"WintunOpenAdapter\0").map(|sym| *sym)?;
let WintunGetAdapterLUID = __library.get(b"WintunGetAdapterLUID\0").map(|sym| *sym)?;
let WintunGetRunningDriverVersion = __library
.get(b"WintunGetRunningDriverVersion\0")
.map(|sym| *sym)?;
let WintunDeleteDriver = __library.get(b"WintunDeleteDriver\0").map(|sym| *sym)?;
let WintunSetLogger = __library.get(b"WintunSetLogger\0").map(|sym| *sym)?;
let WintunStartSession = __library.get(b"WintunStartSession\0").map(|sym| *sym)?;
let WintunEndSession = __library.get(b"WintunEndSession\0").map(|sym| *sym)?;
let WintunGetReadWaitEvent = __library.get(b"WintunGetReadWaitEvent\0").map(|sym| *sym)?;
let WintunReceivePacket = __library.get(b"WintunReceivePacket\0").map(|sym| *sym)?;
let WintunReleaseReceivePacket = __library
.get(b"WintunReleaseReceivePacket\0")
.map(|sym| *sym)?;
let WintunAllocateSendPacket = __library
.get(b"WintunAllocateSendPacket\0")
.map(|sym| *sym)?;
let WintunSendPacket = __library.get(b"WintunSendPacket\0").map(|sym| *sym)?;
Ok(wintun {
__library,
WintunCreateAdapter,
WintunCloseAdapter,
WintunOpenAdapter,
WintunGetAdapterLUID,
WintunGetRunningDriverVersion,
WintunDeleteDriver,
WintunSetLogger,
WintunStartSession,
WintunEndSession,
WintunGetReadWaitEvent,
WintunReceivePacket,
WintunReleaseReceivePacket,
WintunAllocateSendPacket,
WintunSendPacket,
})
}
pub unsafe fn WintunCreateAdapter(
&self,
arg1: LPCWSTR,
arg2: LPCWSTR,
arg3: *const GUID,
) -> WINTUN_ADAPTER_HANDLE {
(self.WintunCreateAdapter)(arg1, arg2, arg3)
}
pub unsafe fn WintunCloseAdapter(&self, arg1: WINTUN_ADAPTER_HANDLE) -> () {
(self.WintunCloseAdapter)(arg1)
}
pub unsafe fn WintunOpenAdapter(&self, arg1: LPCWSTR) -> WINTUN_ADAPTER_HANDLE {
(self.WintunOpenAdapter)(arg1)
}
pub unsafe fn WintunGetAdapterLUID(
&self,
arg1: WINTUN_ADAPTER_HANDLE,
arg2: *mut NET_LUID,
) -> () {
(self.WintunGetAdapterLUID)(arg1, arg2)
}
pub unsafe fn WintunGetRunningDriverVersion(&self) -> DWORD {
(self.WintunGetRunningDriverVersion)()
}
pub unsafe fn WintunDeleteDriver(&self) -> BOOL {
(self.WintunDeleteDriver)()
}
pub unsafe fn WintunSetLogger(&self, arg1: WINTUN_LOGGER_CALLBACK) -> () {
(self.WintunSetLogger)(arg1)
}
pub unsafe fn WintunStartSession(
&self,
arg1: WINTUN_ADAPTER_HANDLE,
arg2: DWORD,
) -> WINTUN_SESSION_HANDLE {
(self.WintunStartSession)(arg1, arg2)
}
pub unsafe fn WintunEndSession(&self, arg1: WINTUN_SESSION_HANDLE) -> () {
(self.WintunEndSession)(arg1)
}
pub unsafe fn WintunGetReadWaitEvent(&self, arg1: WINTUN_SESSION_HANDLE) -> HANDLE {
(self.WintunGetReadWaitEvent)(arg1)
}
pub unsafe fn WintunReceivePacket(
&self,
arg1: WINTUN_SESSION_HANDLE,
arg2: *mut DWORD,
) -> *mut BYTE {
(self.WintunReceivePacket)(arg1, arg2)
}
pub unsafe fn WintunReleaseReceivePacket(
&self,
arg1: WINTUN_SESSION_HANDLE,
arg2: *const BYTE,
) -> () {
(self.WintunReleaseReceivePacket)(arg1, arg2)
}
pub unsafe fn WintunAllocateSendPacket(
&self,
arg1: WINTUN_SESSION_HANDLE,
arg2: DWORD,
) -> *mut BYTE {
(self.WintunAllocateSendPacket)(arg1, arg2)
}
pub unsafe fn WintunSendPacket(&self, arg1: WINTUN_SESSION_HANDLE, arg2: *const BYTE) -> () {
(self.WintunSendPacket)(arg1, arg2)
}
}