diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..16823e5 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,22 @@ +[build] +# target = ["x86_64-unknown-linux-musl"] +# target = ["x86_64-unknown-linux-gnu"] +# target = ["aarch64-linux-android"] +# target = ["x86_64-linux-android"] +# target = ["aarch64-apple-ios"] +# target = ["x86_64-apple-ios"] +# target = ["x86_64-pc-windows-msvc"] +# target = ["x86_64-apple-darwin"] +# target = ["x86_64-unknown-freebsd"] + +[target.aarch64-unknown-linux-musl] +linker = "aarch64-linux-musl-gcc" +rustflags = ["-C", "target-feature=+crt-static", "-C", "strip=symbols"] + +[target.mipsel-unknown-linux-musl] +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"] + diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 0ee4919..51c3a56 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -7,71 +7,62 @@ on: env: CARGO_TERM_COLOR: always -permissions: - contents: write + defaults: run: + # necessary for windows shell: bash +permissions: + contents: write + packages: write jobs: build: + name: Build ${{ matrix.TARGET }} strategy: fail-fast: false matrix: include: - - TARGET: i686-unknown-linux-musl - OS: ubuntu-latest - FEATURES: ring-cipher,openssl-vendored,wss - TARGET: x86_64-unknown-linux-musl OS: ubuntu-latest - FEATURES: ring-cipher,wss - TARGET: aarch64-unknown-linux-musl OS: ubuntu-latest - FEATURES: ring-cipher,wss - TARGET: armv7-unknown-linux-musleabihf OS: ubuntu-latest - FEATURES: ring-cipher,wss - TARGET: armv7-unknown-linux-musleabi OS: ubuntu-latest - FEATURES: ring-cipher,wss - TARGET: arm-unknown-linux-musleabihf OS: ubuntu-latest - FEATURES: ring-cipher,wss - TARGET: arm-unknown-linux-musleabi OS: ubuntu-latest - FEATURES: ring-cipher,wss - TARGET: x86_64-apple-darwin OS: macos-latest - FEATURES: ring-cipher,wss - TARGET: aarch64-apple-darwin OS: macos-latest - FEATURES: ring-cipher,wss - TARGET: i686-pc-windows-msvc OS: windows-latest - FEATURES: ring-cipher,wss - TARGET: x86_64-pc-windows-msvc OS: windows-latest - FEATURES: ring-cipher,wss + - TARGET: aarch64-pc-windows-msvc + OS: windows-latest - TARGET: mipsel-unknown-linux-musl OS: ubuntu-latest - FEATURES: ring-cipher,wss - TARGET: mips-unknown-linux-musl OS: ubuntu-latest - FEATURES: ring-cipher,wss - + - TARGET: x86_64-unknown-freebsd + OS: ubuntu-latest + ARTIFACT_NAME: freebsd-13.2-x86_64 + BSD_VERSION: 13.2 runs-on: ${{ matrix.OS }} env: - NAME: vnt-cli + NAME: Vnt TARGET: ${{ matrix.TARGET }} OS: ${{ matrix.OS }} FEATURES: ${{ matrix.FEATURES }} - steps: - uses: actions/checkout@v4 - - name: Init submodules run: git submodule update --init --recursive --remote && git submodule status - - name: Cargo cache uses: actions/cache@v4 with: @@ -79,69 +70,89 @@ jobs: ~/.cargo/registry ./target key: build-cargo-registry-${{matrix.TARGET}} - + - name: Install Protobuf (MacOS) + if: runner.os == 'macOS' + run: brew install protobuf + - name: Install Protobuf (Windows) + if: runner.os == 'Windows' + run: choco install protoc - 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 Rust toolchain - uses: actions-rs/toolchain@v1 + - name: Build Vnt X86_64-FreeBSD + uses: cross-platform-actions/action@v0.23.0 + if: ${{ endsWith(matrix.TARGET, 'freebsd') }} + env: + TARGET: ${{ matrix.TARGET }} with: - toolchain: nightly - profile: minimal - override: true - - - name: Verify Rust version + operating_system: freebsd + environment_variables: TARGET + architecture: x86-64 + version: ${{ matrix.BSD_VERSION }} + shell: bash + memory: 5G + cpu_count: 4 + run: | + uname -a + echo $SHELL + pwd + ls -lah + whoami + env | sort + sudo pkg install -y git protobuf llvm15 + curl --proto 'https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + source $HOME/.cargo/env + export CC=clang + export CXX=clang++ + export CARGO_TERM_COLOR=always + cargo build --release --verbose --bin vnt2_web --features vnt-web --target $TARGET + cargo build --release --verbose --bin vnt2_cli --features vnt-ipc --target $TARGET + cargo build --release --verbose --bin vnt2_ctrl --features vnt-ipc --target $TARGET + - name: Install and configure dependencies + if: ${{ ! endsWith(matrix.TARGET, 'freebsd') }} run: | - 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/ + # dependencies are only needed on ubuntu as that's the only place where + # we make cross-compilation + + if [[ $OS =~ ^ubuntu.*$ ]]; then + sudo apt-get update && sudo apt-get install protobuf-compiler 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-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 + ;; + esac + + + if [ -n "$MUSL_URI" ]; then + mkdir -p ./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 fi - if [[ $TARGET =~ ^mips.*$ ]]; then cd /opt/musl_gcc/${URL}-cross/lib/gcc/${URL}/11.2.1 cp libgcc_eh.a libunwind.a @@ -152,13 +163,10 @@ jobs: 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 <>~/.cargo/config.toml <vnt-cli.exe -k 123456 - ``` -2. 可以执行info命令查看当前设备的虚拟ip - ```shell - root@DESKTOP-0BCHNIO:/opt# ./vnt-cli --info - Name: Ubuntu 18.04 (bionic) [64-bit] - Virtual ip: 10.26.0.2 - Virtual gateway: 10.26.0.1 - Virtual netmask: 255.255.255.0 - Connection status: Connected - NAT type: Cone - Relay server: 43.139.56.10:29871 - Public ips: 120.228.76.75 - Local ip: 172.25.165.58 - ``` -3. 也可以执行list命令查看其他设备的虚拟ip - ```shell - root@DESKTOP-0BCHNIO:/opt# ./vnt-cli --list - Name Virtual Ip P2P/Relay Rt Status - Windows 10.0.22621 (Windows 11 Professional) [64-bit] 10.26.0.3 p2p 2 Online - CentOS 7.9.2009 (Core) [64-bit] 10.26.0.4 p2p 35 Online - ``` -4. 最后可以用虚拟ip实现设备间相互访问 - - ssh -5. 帮助,使用-h命令查看 - -### 使用须知 - -- token的作用是标识一个虚拟局域网,当使用公共服务器时,建议使用一个唯一值当token(比如uuid),否则有可能连接到其他人创建的虚拟局域网中 -- 默认使用公共服务器做注册和中继,目前的配置是2核4G 4Mbps,有需要再扩展~ -- vnt-cli需要使用命令行运行 -- Mac和Linux下需要加可执行权限(例如:chmod +x ./vnt-cli) -- 可以自己搭中继服务器([server](https://github.com/vnt-dev/vnts)) - -### 直接使用 - -[**下载release文件**](https://github.com/vnt-dev/vnt/releases) - -[**帮助文档**](https://rustvnt.com) - -### 自行编译 - -
点击展开 - -前提条件:安装rust编译环境([install rust](https://www.rust-lang.org/zh-CN/tools/install)) +## 使用vnt2_cli组网 +使用方式和vnt1.0一样,只是增减了一些功能,具体参数请查看 -h ``` -到项目根目录下执行 cargo build -p vnt-cli - -也可按需编译,将得到更小的二进制文件,使用--no-default-features排除默认features - -cargo build -p vnt-cli --no-default-features +# 启动程序 服务端可以使用101.35.230.139:6660 +./vnt2_cli -k 123456 -s 101.35.230.139:6660 ``` -features说明 +``` +# 查看组网信息 +./vnt2_ctrl info +``` + +## 使用vnt2_web组网 -| feature | 说明 | 是否默认 | -|-------------------|--------------------------------|------| -| openssl | 使用openssl中的加密算法 | 否 | -| openssl-vendored | 从源码编译openssl | 否 | -| ring-cipher | 使用ring中的加密算法 | 否 | -| aes_cbc | 支持aes_cbc加密 | 是 | -| aes_ecb | 支持aes_ecb加密 | 是 | -| aes_gcm | 支持aes_gcm加密 | 是 | -| sm4_cbc | 支持sm4_cbc加密 | 是 | -| chacha20_poly1305 | 支持chacha20和chacha20_poly1305加密 | 是 | -| server_encrypt | 支持服务端加密 | 是 | -| ip_proxy | 内置ip代理 | 是 | -| port_mapping | 端口映射 | 是 | -| log | 日志 | 是 | -| command | list、route等命令 | 是 | -| file_config | yaml配置文件 | 是 | -| lz4 | lz4压缩 | 是 | -| zstd | zstd压缩 | 否 | -| upnp | upnp协议 | 否 | -| ws | ws协议 | 是 | -| wss | wss协议 | 是 | +1. 启动程序 -
+ ``` + # 启动程序 + ./vnt2_web + ``` +2. 浏览器打开 http://127.0.0.1:19099 +3. 在页面上添加组网配置,再启动组网 -### 支持平台 -- Mac -- 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) -- Android +# VNT2.0新特性 -### GUI +1. 提升安全性,支持tcp-tls、quic、wss协议连接服务器,和服务端强制使用tls加密,并支持证书绑定,防止伪造服务端攻击 +2. 提升流量稳定性,支持使用quic代理流量,支持FEC冗余传输 +3. 简化操作,去除了大量vnt1.0的重复和无用的配置参数 +4. vnt-link、vnt合二为一 +5. 支持有tun模式、无tun模式、端口映射 +6. 全功能的情况下,减少程序体积 +7. 性能提升,支持linux-offload +8. 更规范的api接入,可以轻松自定义客户端 +9. 支持同时连接多个服务端,可以容灾和负载均衡 -支持安卓和Windows [下载](https://github.com/vnt-dev/VntApp/releases/) +# 说明 -### 特性 +vnt2.0整体重构了一遍,和1.0不兼容,同时也可能引入新的bug,欢迎反馈 -- IP层数据转发 -- NAT穿透 - - 点对点穿透 - - 服务端中继转发 - - 客户端中继转发 -- IP代理(点对点、点对网) -- p2p组播/广播 -- 客户端数据加密(`aes-gcm`、`chacha20-poly1305`等多种加密算法) -- 服务端数据加密(`rsa` + `aes-gcm`) -- 多通道UDP应对QOS -- 支持TCP、UDP、WebSocket等多种协议 -- 支持数据压缩 +其他平台后续再推出 -### 更多玩法 +### 相关库 +1. tun虚拟网卡(https://github.com/tun-rs/tun-rs) +2. 路由设置(https://github.com/tun-rs/route_manager) +3. 用户态协议栈(用于quic代理和无tun模式出口)(https://github.com/rustp2p/tcp_ip) +4. 打洞通道处理(https://github.com/rustp2p/rustp2p/tree/master/rustp2p-core) -1. 和远程桌面(如mstsc)搭配,超低延迟的体验 -2. 安装samba服务,共享磁盘 -3. 点对网,访问内网其他机器、IP代理(结合启动参数'-i'和'-o') -### Todo -- ~~桌面UI(已支持)~~ -- 使用FEC、ARQ等方式提升弱网环境的稳定性 -### 常见问题 - -
展开 - -#### 问题1: 设置网络地址失败 - -##### 可能原因: - -vnt默认使用10.26.0.0/24网段,和本地网络适配器的ip冲突 - -##### 解决方法: - -1. 方法一:找到冲突的IP,将其改成别的 -2. 方法二:自建服务器,指定其他不会冲突的网段 -3. 方法三:增加参数-d ,设置不同的id会让服务端分配不同的IP,从而绕开有冲突的IP - -#### 问题2: windows系统上wintun.dll加载失败 - -##### 可能原因: - -没有下载wintun.dll 或者使用的wintun.dll有问题 - -##### 解决方法: - -1. 下载最新版的wintun.dll [下载链接](https://www.wintun.net/builds/wintun-0.14.1.zip) -2. 解压后找到对应架构的目录,通常是amd64 -3. 将对应的wintun.dll放到和vnt-cli同目录下(或者放到C盘Windows目录下) -4. 再次启动vnt-cli - -#### 问题3: 丢包严重,或是不能正常组网通信 - -##### 可能原因: - -某些宽带下(比如广电宽带)UDP丢包严重 - -##### 解决方法: - -1. 使用TCP模式中继转发(vnt-cli增加--tcp参数) -2. 如果p2p后效果很差,可以选择禁用p2p(vnt-cli增加--use-channel relay 参数) - -#### 问题4:重启后虚拟IP发生变化,或指定了IP不能启动 - -##### 可能原因: - -设备重启后程序自动获取的id值改变,导致注册时重新分配了新的IP,或是IP冲突 - -##### 解决方法: - -1. 命令行启动增加-d参数(使用配置文件启动则在配置文件中增加device_id参数),要保证每个设备的值都不一样,取值可以任意64位以内字符串 - -
- -### 交流群 - -对VNT有任何问题均可以加群联系作者 - -QQ群1: 1034868233(满人) - -QQ群2: 950473757 - -QQ群3: 1060550456 - -### 赞助 - -如果VNT对你有帮助,欢迎打赏作者 - - - -### 其他 - -可使用社区小伙伴搭建的中继服务器 - -1. -s vnt.8443.eu.org:29871 -2. -s vnt.wherewego.top:29872 - -### 参与贡献 - - - - diff --git a/common/Cargo.toml b/common/Cargo.toml deleted file mode 100644 index 5aec21f..0000000 --- a/common/Cargo.toml +++ /dev/null @@ -1,46 +0,0 @@ -[package] -name = "common" -version = "1.2.16" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -vnt = { path = "../vnt", package = "vnt", default-features = false } -anyhow = "1.0.82" -console = "0.15.2" -log = "0.4.17" -log4rs = { version = "1.3.0", optional = true } -serde = { version = "1.0", features = ["derive"] } -serde_yaml = "0.9.32" -getopts = "0.2.21" -gethostname = "0.4.3" -uuid = { version = "1.8.0", features = ["v4"] } -sys-locale = "0.3.1" - -[features] -default = [] -openssl = ["vnt/openssl"] -openssl-vendored = ["vnt/openssl-vendored"] -ring-cipher = ["vnt/ring-cipher"] -aes_cbc = ["vnt/aes_cbc"] -aes_ecb = ["vnt/aes_ecb"] -sm4_cbc = ["vnt/sm4_cbc"] -aes_gcm = ["vnt/aes_gcm"] -chacha20_poly1305 = ["vnt/chacha20_poly1305"] -server_encrypt = ["vnt/server_encrypt"] -ip_proxy = ["vnt/ip_proxy"] -port_mapping = ["vnt/port_mapping"] -lz4 = ["vnt/lz4_compress"] -zstd = ["vnt/zstd_compress"] -upnp = ["vnt/upnp"] -ws = ["vnt/ws"] -wss = ["vnt/wss"] -command = [] -file_config = [] -log = ["log4rs"] -integrated_tun = ["vnt/integrated_tun"] - -[build-dependencies] -rand = "0.8.5" -chrono = "0.4.23" \ No newline at end of file diff --git a/common/build.rs b/common/build.rs deleted file mode 100644 index 8970ed9..0000000 --- a/common/build.rs +++ /dev/null @@ -1,16 +0,0 @@ -use rand::Rng; -use std::fs::File; -use std::io::Write; - -fn main() { - let now_time = chrono::Local::now(); - let serial_number = format!( - "{}-{}", - &now_time.format("%y%m%d%H%M").to_string(), - rand::thread_rng().gen_range(100..1000) - ); - let generated_code = format!(r#"pub const SERIAL_NUMBER: &str = "{}";"#, serial_number); - let dest_path = "src/generated_serial_number.rs"; - let mut file = File::create(&dest_path).unwrap(); - file.write_all(generated_code.as_bytes()).unwrap(); -} diff --git a/common/src/args_parse.rs b/common/src/args_parse.rs deleted file mode 100644 index 9f3ff01..0000000 --- a/common/src/args_parse.rs +++ /dev/null @@ -1,82 +0,0 @@ -use std::net::Ipv4Addr; - -pub fn ips_parse(ips: &Vec) -> Result, String> { - let mut in_ips_c = vec![]; - for x in ips { - let mut split = x.split(","); - let net = if let Some(net) = split.next() { - net - } else { - return Err(format!("ipv4/mask,ipv4 {:?}", x)); - }; - let ip = if let Some(ip) = split.next() { - ip - } else { - return Err(format!("ipv4/mask,ipv4 {:?}", x)); - }; - let ip = if let Ok(ip) = ip.parse::() { - ip - } else { - return Err(format!("not ipv4 {:?}", ip)); - }; - let mut split = net.split("/"); - let dest = if let Some(dest) = split.next() { - dest - } else { - return Err(format!("no ipv4/mask {:?}", net)); - }; - let mask = if let Some(mask) = split.next() { - mask - } else { - return Err(format!("no netmask {:?}", net)); - }; - let dest = if let Ok(dest) = dest.parse::() { - dest - } else { - return Err(format!("not ipv4 {:?}", dest)); - }; - let mask = to_ip(mask)?; - in_ips_c.push((u32::from_be_bytes(dest.octets()), mask, ip)); - } - Ok(in_ips_c) -} - -pub fn out_ips_parse(ips: &Vec) -> Result, String> { - let mut in_ips_c = vec![]; - for x in ips { - let mut split = x.split("/"); - let dest = if let Some(dest) = split.next() { - dest - } else { - return Err(format!("no ipv4/mask {:?}", x)); - }; - let mask = if let Some(mask) = split.next() { - mask - } else { - return Err(format!("no netmask {:?}", x)); - }; - let dest = if let Ok(dest) = dest.parse::() { - dest - } else { - return Err(format!("not ipv4 {:?}", dest)); - }; - let mask = to_ip(mask)?; - in_ips_c.push((u32::from_be_bytes(dest.octets()), mask)); - } - Ok(in_ips_c) -} - -pub fn to_ip(mask: &str) -> Result { - if let Ok(m) = mask.parse::() { - if m > 32 { - return Err("not netmask".to_string()); - } - let mut mask = 0u32; - for i in 0..m { - mask = mask | (1 << (31 - i)); - } - Ok(mask) - } else { - Err("not netmask".to_string()) - } -} diff --git a/common/src/callback.rs b/common/src/callback.rs deleted file mode 100644 index dff2f1e..0000000 --- a/common/src/callback.rs +++ /dev/null @@ -1,52 +0,0 @@ -use std::process; - -use console::style; -use vnt::{ConnectInfo, ErrorInfo, ErrorType, HandshakeInfo, RegisterInfo, VntCallback}; - -#[derive(Clone)] -pub struct VntHandler {} - -impl VntCallback for VntHandler { - fn success(&self) { - println!(" {} ", style("====== Connect Successfully ======").green()) - } - #[cfg(feature = "integrated_tun")] - fn create_tun(&self, info: vnt::DeviceInfo) { - println!("create_tun {}", info) - } - - fn connect(&self, info: ConnectInfo) { - println!("connect {}", info) - } - - fn handshake(&self, info: HandshakeInfo) -> bool { - println!("handshake {}", info); - true - } - - fn register(&self, info: RegisterInfo) -> bool { - println!("register {}", style(info).green()); - true - } - - fn error(&self, info: ErrorInfo) { - log::error!("error {:?}", info); - println!("{}", style(format!("error {}", info)).red()); - match info.code { - ErrorType::TokenError - | ErrorType::AddressExhausted - | ErrorType::IpAlreadyExists - | ErrorType::InvalidIp - | ErrorType::LocalIpExists - | ErrorType::FailedToCrateDevice => { - self.stop(); - } - _ => {} - } - } - - fn stop(&self) { - println!("stopped"); - process::exit(0) - } -} diff --git a/common/src/cli.rs b/common/src/cli.rs deleted file mode 100644 index c645003..0000000 --- a/common/src/cli.rs +++ /dev/null @@ -1,620 +0,0 @@ -use crate::args_parse::{ips_parse, out_ips_parse}; -#[cfg(feature = "command")] -use crate::command; -use crate::{config, generated_serial_number}; -use anyhow::anyhow; -use console::style; -use getopts::Options; -use std::collections::HashMap; -use std::io; -use std::net::Ipv4Addr; -use std::path::PathBuf; -use std::str::FromStr; -use sys_locale::get_locale; -use vnt::channel::punch::PunchModel; -use vnt::channel::UseChannelType; -use vnt::cipher::CipherModel; -use vnt::compression::Compressor; -use vnt::core::Config; - -pub fn app_home() -> io::Result { - let root_path = match std::env::current_exe() { - Ok(path) => { - if let Some(v) = path.as_path().parent() { - v.to_path_buf() - } else { - log::warn!("current_exe parent none:{:?}", path); - PathBuf::new() - } - } - Err(e) => { - log::warn!("current_exe err:{:?}", e); - PathBuf::new() - } - }; - let path = root_path.join("env"); - if !path.exists() { - std::fs::create_dir_all(&path)?; - } - Ok(path) -} - -pub fn parse_args_config() -> anyhow::Result, bool)>> { - #[cfg(feature = "log")] - let _ = log4rs::init_file("log4rs.yaml", Default::default()); - let args: Vec = std::env::args().collect(); - let program = args[0].clone(); - let mut opts = Options::new(); - opts.optopt("k", "", "组网标识", ""); - opts.optopt("n", "", "设备名称", ""); - opts.optopt("d", "", "设备标识", ""); - opts.optflag("c", "", "关闭交互式命令"); - opts.optopt("s", "", "注册和中继服务器地址", ""); - opts.optmulti("e", "", "stun服务器", ""); - opts.optflag("a", "", "使用tap模式"); - opts.optopt("", "nic", "虚拟网卡名称,windows下使用tap则必填", ""); - opts.optmulti("i", "", "配置点对网(IP代理)入站时使用", ""); - opts.optmulti("o", "", "配置点对网出站时使用", ""); - opts.optopt("w", "", "客户端加密", ""); - opts.optflag("W", "", "服务端加密"); - opts.optopt("u", "", "自定义mtu(默认为1430)", ""); - opts.optopt("", "ip", "指定虚拟ip", ""); - opts.optflag("", "relay", "仅使用服务器转发"); - opts.optopt("", "par", "任务并行度(必须为正整数)", ""); - opts.optopt("", "model", "加密模式", ""); - opts.optflag("", "finger", "指纹校验"); - opts.optopt("", "punch", "取值ipv4/ipv6", ""); - opts.optopt("", "ports", "监听的端口", ""); - opts.optflag("", "cmd", "开启窗口输入"); - opts.optflag("", "no-proxy", "关闭内置代理"); - opts.optflag("", "first-latency", "优先延迟"); - opts.optopt("", "use-channel", "使用通道 relay/p2p", ""); - opts.optopt("", "packet-loss", "丢包率", ""); - opts.optopt("", "packet-delay", "延迟", ""); - opts.optmulti("", "dns", "dns", ""); - opts.optmulti("", "mapping", "mapping", ""); - opts.optmulti("", "vnt-mapping", "vnt-mapping", ""); - opts.optopt("f", "", "配置文件", ""); - opts.optopt("", "compressor", "压缩算法", ""); - opts.optopt("", "local-dev", "指定本地ipv4网卡名称", ""); - opts.optflag("", "disable-stats", "关闭流量统计"); - opts.optflag("", "allow-wg", "允许接入WireGuard"); - //"后台运行时,查看其他设备列表" - opts.optflag("", "add", "后台运行时,添加地址"); - opts.optflag("", "list", "后台运行时,查看其他设备列表"); - opts.optflag("", "all", "后台运行时,查看其他设备完整信息"); - opts.optflag("", "info", "后台运行时,查看当前设备信息"); - opts.optflag("", "route", "后台运行时,查看数据转发路径"); - opts.optflag("", "chart_a", "后台运行时,查看流量统计"); - opts.optopt("", "chart_b", "后台运行时,查看流量统计", ""); - opts.optflag("", "stop", "停止后台运行"); - opts.optflag("h", "help", "帮助"); - let matches = match opts.parse(&args[1..]) { - Ok(m) => m, - Err(f) => { - print_usage(&program, opts); - return Err(anyhow::anyhow!("{}", f.to_string())); - } - }; - if matches.opt_present("h") || args.len() == 1 { - print_usage(&program, opts); - return Ok(None); - } - - #[cfg(feature = "command")] - if matches.opt_present("list") { - command::command(command::CommandEnum::List); - return Ok(None); - } else if matches.opt_present("info") { - command::command(command::CommandEnum::Info); - return Ok(None); - } else if matches.opt_present("stop") { - command::command(command::CommandEnum::Stop); - return Ok(None); - } else if matches.opt_present("route") { - command::command(command::CommandEnum::Route); - return Ok(None); - } else if matches.opt_present("all") { - command::command(command::CommandEnum::All); - return Ok(None); - } - #[cfg(feature = "command")] - if matches.opt_present("chart_a") { - command::command(command::CommandEnum::ChartA); - return Ok(None); - } - #[cfg(feature = "command")] - if let Some(v) = matches.opt_str("chart_b") { - command::command(command::CommandEnum::ChartB(v)); - return Ok(None); - } - let conf = matches.opt_str("f"); - let (config, vnt_link_config, cmd) = if conf.is_some() { - match config::read_config(&conf.unwrap()) { - Ok(c) => c, - Err(e) => { - return Err(anyhow::anyhow!("conf err {}", e)); - } - } - } else { - if !matches.opt_present("k") { - print_usage(&program, opts); - return Err(anyhow::anyhow!("parameter -k not found .")); - } - #[cfg(feature = "integrated_tun")] - let device_name = matches.opt_str("nic"); - let token: String = matches.opt_get("k").unwrap().unwrap(); - let device_id = matches.opt_get_default("d", String::new()).unwrap(); - let device_id = if device_id.is_empty() { - config::get_device_id() - } else { - device_id - }; - if device_id.is_empty() { - print_usage(&program, opts); - return Err(anyhow::anyhow!("parameter -d not found .")); - } - let name = matches - .opt_get_default( - "n", - gethostname::gethostname() - .to_str() - .unwrap_or("UnknownName") - .to_string(), - ) - .unwrap(); - let server_address_str = matches - .opt_get_default("s", "vnt.wherewego.top:29872".to_string()) - .unwrap(); - - let mut stun_server = matches.opt_strs("e"); - if stun_server.is_empty() { - for x in config::PUB_STUN { - stun_server.push(x.to_string()); - } - } - let dns = matches.opt_strs("dns"); - let in_ip = matches.opt_strs("i"); - let in_ip = match ips_parse(&in_ip) { - Ok(in_ip) => in_ip, - Err(e) => { - print_usage(&program, opts); - println!(); - println!("-i: {:?} {}", in_ip, e); - return Err(anyhow::anyhow!("example: -i 192.168.0.0/24,10.26.0.3")); - } - }; - let out_ip = matches.opt_strs("o"); - let out_ip = match out_ips_parse(&out_ip) { - Ok(out_ip) => out_ip, - Err(e) => { - print_usage(&program, opts); - println!(); - println!("-o: {:?} {}", out_ip, e); - return Err(anyhow::anyhow!("example: -o 0.0.0.0/0")); - } - }; - let password: Option = matches.opt_get("w").unwrap(); - let server_encrypt = matches.opt_present("W"); - #[cfg(not(feature = "server_encrypt"))] - { - if server_encrypt { - println!("Server encryption not supported"); - return Err(anyhow::anyhow!("Server encryption not supported")); - } - } - let mtu: Option = matches.opt_get("u").unwrap(); - let mtu = if let Some(mtu) = mtu { - match u32::from_str(&mtu) { - Ok(mtu) => Some(mtu), - Err(e) => { - print_usage(&program, opts); - println!(); - println!("'-u {}' {}", mtu, e); - return Err(anyhow::anyhow!("'-u {}' {}", mtu, e)); - } - } - } else { - None - }; - let virtual_ip: Option = matches.opt_get("ip").unwrap(); - let virtual_ip = - virtual_ip.map(|v| Ipv4Addr::from_str(&v).expect(&format!("'--ip {}' error", v))); - if let Some(virtual_ip) = virtual_ip { - if virtual_ip.is_unspecified() || virtual_ip.is_broadcast() || virtual_ip.is_multicast() - { - return Err(anyhow::anyhow!("'--ip {}' invalid", virtual_ip)); - } - } - let relay = matches.opt_present("relay"); - - let cipher_model = match matches.opt_get::("model") { - Ok(model) => { - #[cfg(not(any(feature = "aes_gcm", feature = "server_encrypt")))] - { - if password.is_some() && model.is_none() { - return Err(anyhow::anyhow!("'--model ' undefined")); - } - model.unwrap_or(CipherModel::None) - } - #[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))] - model.unwrap_or(CipherModel::AesGcm) - } - Err(e) => { - return Err(anyhow::anyhow!("'--model ' invalid,{}", e)); - } - }; - - let finger = matches.opt_present("finger"); - let punch_model = matches - .opt_get::("punch") - .unwrap() - .unwrap_or(PunchModel::All); - let use_channel_type = matches - .opt_get::("use-channel") - .unwrap() - .unwrap_or_else(|| { - if relay { - UseChannelType::Relay - } else { - UseChannelType::All - } - }); - - let ports = matches - .opt_get::("ports") - .unwrap_or(None) - .map(|v| v.split(",").map(|x| x.parse().unwrap_or(0)).collect()); - - let cmd = matches.opt_present("cmd"); - #[cfg(feature = "ip_proxy")] - #[cfg(feature = "integrated_tun")] - let no_proxy = matches.opt_present("no-proxy"); - let first_latency = matches.opt_present("first-latency"); - let packet_loss = matches - .opt_get::("packet-loss") - .expect("--packet-loss"); - let packet_delay = matches - .opt_get::("packet-delay") - .expect("--packet-delay") - .unwrap_or(0); - #[cfg(feature = "port_mapping")] - let port_mapping_list = matches.opt_strs("mapping"); - let vnt_mapping_list = matches.opt_strs("vnt-mapping"); - let local_dev: Option = 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() { - Compressor::from_str(compressor) - .map_err(|e| anyhow!("{}", e)) - .unwrap() - } else { - Compressor::None - }; - let config = Config::new( - #[cfg(feature = "integrated_tun")] - #[cfg(target_os = "windows")] - false, - token, - device_id, - name, - server_address_str, - dns, - stun_server, - in_ip, - out_ip, - password, - mtu, - virtual_ip, - #[cfg(feature = "integrated_tun")] - #[cfg(feature = "ip_proxy")] - no_proxy, - server_encrypt, - cipher_model, - finger, - punch_model, - ports, - first_latency, - #[cfg(feature = "integrated_tun")] - device_name, - use_channel_type, - packet_loss, - packet_delay, - #[cfg(feature = "port_mapping")] - port_mapping_list, - compressor, - !disable_stats, - allow_wire_guard, - local_dev, - )?; - (config, vnt_mapping_list, cmd) - }; - println!("version {}", vnt::VNT_VERSION); - println!("Serial:{}", generated_serial_number::SERIAL_NUMBER); - log::info!( - "version:{},Serial:{}", - vnt::VNT_VERSION, - generated_serial_number::SERIAL_NUMBER - ); - Ok(Some((config, vnt_link_config, cmd))) -} - -fn get_description(key: &str, language: &str) -> String { - // 设置一个全局的映射来存储中英文对照 - let descriptions: HashMap<&str, (&str, &str)> = [ - ("-k ", ("使用相同的token,就能组建一个局域网络", "Use the same token to form a local network")), - ("-n ", ("给设备一个名字,便于区分不同设备,默认使用系统版本", "Give the device a name to distinguish it, defaults to system version")), - ("-d ", ("设备唯一标识符,不使用--ip参数时,服务端凭此参数分配虚拟ip,注意不能重复", "Device unique identifier, used by the server to allocate virtual IP when --ip parameter is not used, must be unique")), - ("-s ", ("注册和中继服务器地址,协议支持使用tcp://和ws://和wss://,默认为udp://", "Registration and relay server address, protocols support using tcp://, ws://, and wss://, default is udp://")), - ("-e ", ("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")), - ("-i ", ("配置点对网(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 ", ("配置点对网时使用,-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 ", ("使用该密码生成的密钥对客户端数据进行加密,并且服务端无法解密,使用相同密码的客户端才能通信", "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(默认为1420)", "Customize MTU (default is 1420)")), - ("-f ", ("读取配置文件中的配置", "Read configuration from file")), - ("--ip ", ("指定虚拟ip,指定的ip不能和其他设备重复,必须有效并且在服务端所属网段下,默认情况由服务端分配", "Specify virtual IP, must be unique and valid within server subnet, by default allocated by server")), - ("--model ", ("加密模式(默认aes_gcm),可选值", "Encryption mode (default aes_gcm), options ")), - ("--finger", ("增加数据指纹校验,可增加安全性,如果服务端开启指纹校验,则客户端也必须开启", "Add data fingerprint verification for increased security, client must enable if server does")), - ("--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 ", ("取值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")), - ("--first-latency", ("优先低延迟的通道,默认情况优先使用p2p通道", "Prioritize low-latency channels, defaults to prioritizing p2p channel")), - ("--use-channel ", ("使用通道 relay/p2p/all,默认两者都使用", "Use channel relay/p2p/all, defaults to using both")), - ("--nic ", ("指定虚拟网卡名称", "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>", ("模拟延迟,正整数,单位毫秒,程序将根据设定值延迟发送数据包,可用于模拟弱网", "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 ", ("DNS服务器地址,可使用多个dns,不指定时使用系统解析", "DNS server address, can specify multiple DNS servers, defaults to system resolution if not specified")), - ("--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/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,例如 --compressor lz4", "Enable compression, option lz4, e.g., --compressor lz4")), - ("--compressor-zstd ", ("启用压缩,可选值zstd<,level>,level为压缩级别,例如 --compressor zstd,10", "Enable compression, options zstd<,level>, level is compression level, e.g., --compressor zstd,10")), - ("--vnt-mapping ", ("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")), - ("--route", ("后台运行时,查看数据转发路径", "View data forwarding path when running in background")), - ("--chart_a", ("后台运行时,查看所有IP的流量统计", "View traffic statistics of all IPs when running in background")), - ("--chart_b ", ("后台运行时,查看单个IP的历史流量", "View historical traffic of a single IP when running in background")), - ("--stop", ("停止后台运行", "Stop running in background")) - // ... 其他选项 - ] - .iter() - .cloned() - .collect(); - - if let Some(&(zh, en)) = descriptions.get(key) { - if language.starts_with("zh") { - return zh.to_string(); // 返回 String 类型 - } - // 默认返回英文 - return en.to_string(); // 返回 String 类型 - } - // 如果没有找到对应的键,则返回空字符串 - String::new() -} - -fn print_usage(program: &str, _opts: Options) { - // 获取系统语言 Locale::user_default().unwrap_or_else(|_| Locale::default()); - let language = get_locale().unwrap_or_else(|| String::from("en-US")); - println!("Usage: {} [options]", program); - println!("version:{}", vnt::VNT_VERSION); - println!("Serial:{}", generated_serial_number::SERIAL_NUMBER); - println!("Options:"); - println!( - " -k {}", - green(get_description("-k ", &language).to_string()) - ); - println!( - " -n {}", - get_description("-n ", &language) - ); - println!( - " -d {}", - get_description("-d ", &language) - ); - println!( - " -s {}", - get_description("-s ", &language) - ); - println!( - " -e {}", - get_description("-e ", &language) - ); - - println!( - " -i {}", - get_description("-i ", &language) - ); - println!( - " -o {}", - get_description("-o ", &language) - ); - println!( - " -w {}", - get_description("-w ", &language) - ); - #[cfg(feature = "server_encrypt")] - println!(" -W {}", get_description("-W", &language)); - println!( - " -u {}", - get_description("-u ", &language) - ); - #[cfg(feature = "file_config")] - println!( - " -f {}", - get_description("-f ", &language) - ); - - println!( - " --ip {}", - get_description("--ip ", &language) - ); - let mut enums = String::new(); - #[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))] - enums.push_str("/aes_gcm"); - #[cfg(feature = "chacha20_poly1305")] - enums.push_str("/chacha20_poly1305/chacha20"); - #[cfg(feature = "aes_cbc")] - enums.push_str("/aes_cbc"); - #[cfg(feature = "aes_ecb")] - enums.push_str("/aes_ecb"); - #[cfg(feature = "sm4_cbc")] - enums.push_str("/sm4_cbc"); - enums.push_str("/xor"); - println!( - " --model {}{}", - get_description("--model ", &language), - &enums[1..] - ); - #[cfg(any( - feature = "aes_gcm", - feature = "chacha20_poly1305", - feature = "server_encrypt", - feature = "aes_cbc", - feature = "aes_ecb", - feature = "sm4_cbc" - ))] - println!( - " --finger {}", - get_description("--finger", &language) - ); - println!( - " --punch {}", - get_description("--punch ", &language) - ); - println!( - " --ports {}", - get_description("--ports ", &language) - ); - #[cfg(feature = "command")] - println!( - " --cmd {}", - get_description("--cmd", &language) - ); - #[cfg(feature = "ip_proxy")] - #[cfg(feature = "integrated_tun")] - println!( - " --no-proxy {}", - get_description("--no-proxy", &language) - ); - println!( - " --first-latency {}", - get_description("--first-latency", &language) - ); - println!( - " --use-channel {}", - get_description("--use-channel ", &language) - ); - #[cfg(feature = "integrated_tun")] - println!( - " --nic {}", - get_description("--nic ", &language) - ); - println!( - " --packet-loss <0> {}", - get_description("--packet-loss <0>", &language) - ); - println!( - " --packet-delay <0> {}", - get_description("--packet-delay <0>", &language) - ); - println!( - " --dns {}", - get_description("--dns ", &language) - ); - - #[cfg(feature = "port_mapping")] - println!( - " --mapping {}", - get_description("--mapping ", &language) - ); - - #[cfg(all(feature = "lz4", feature = "zstd"))] - println!( - " --compressor {}", - get_description("--compressor-all ", &language) - ); - #[cfg(feature = "lz4")] - #[cfg(not(feature = "zstd"))] - println!( - " --compressor {}", - get_description("--compressor-lz4 ", &language) - ); - #[cfg(feature = "zstd")] - #[cfg(not(feature = "lz4"))] - println!( - " --compressor {}", - get_description("--compressor-zstd ", &language) - ); - - #[cfg(not(feature = "integrated_tun"))] - println!( - " --vnt-mapping {}", - green(get_description("--vnt-mapping ", &language).to_string()) - ); - println!( - " --local-dev {}", - get_description("--local-dev", &language) - ); - println!( - " --disable-stats {}", - get_description("--disable-stats", &language) - ); - println!( - " --allow-wg {}", - get_description("--allow-wg", &language) - ); - println!(); - #[cfg(feature = "command")] - { - // #[cfg(not(feature = "integrated_tun"))] - // println!( - // " --add {}", - // yellow("后台运行时,添加VNT地址映射 用法同'--vnt-mapping'".to_string()) - // ); - println!( - " --list {}", - yellow(get_description("--list", &language).to_string()) - ); - println!( - " --all {}", - yellow(get_description("--all", &language).to_string()) - ); - println!( - " --info {}", - yellow(get_description("--info", &language).to_string()) - ); - println!( - " --route {}", - yellow(get_description("--route", &language).to_string()) - ); - println!( - " --chart_a {}", - yellow(get_description("--chart_a", &language).to_string()) - ); - println!( - " --chart_b {}", - yellow(get_description("--chart_b ", &language).to_string()) - ); - println!( - " --stop {}", - yellow(get_description("--stop", &language).to_string()) - ); - } - println!(" -h, --help display help information(显示帮助信息)"); -} - -fn green(str: String) -> impl std::fmt::Display { - style(str).green() -} - -#[cfg(feature = "command")] -fn yellow(str: String) -> impl std::fmt::Display { - style(str).yellow() -} diff --git a/common/src/command/client.rs b/common/src/command/client.rs deleted file mode 100644 index e6caab9..0000000 --- a/common/src/command/client.rs +++ /dev/null @@ -1,92 +0,0 @@ -use serde::Deserialize; -use std::io; -use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket}; -use std::str::FromStr; -use std::time::Duration; - -use crate::command::entity::{ChartA, ChartB, DeviceItem, Info, RouteItem}; - -pub struct CommandClient { - buf: Vec, - udp: UdpSocket, -} - -impl CommandClient { - pub fn new() -> io::Result { - let port = read_command_port().unwrap_or_else(|e| { - log::warn!("read_command_port:{:?}", e); - 39271 - }); - let udp = UdpSocket::bind("127.0.0.1:0")?; - udp.set_read_timeout(Some(Duration::from_secs(5)))?; - udp.connect(SocketAddr::V4(SocketAddrV4::new( - Ipv4Addr::new(127, 0, 0, 1), - port, - )))?; - Ok(Self { - udp, - buf: vec![0; 65536 * 8], - }) - } -} -fn read_command_port() -> io::Result { - let path_buf = crate::cli::app_home()?.join("command-port"); - let port = std::fs::read_to_string(path_buf)?; - match u16::from_str(&port) { - Ok(port) => Ok(port), - Err(_) => { - return Err(io::Error::new( - io::ErrorKind::Other, - "'command-port' file error", - )); - } - } -} - -impl CommandClient { - pub fn list(&mut self) -> io::Result> { - self.send_cmd(b"list") - } - pub fn route(&mut self) -> io::Result> { - self.send_cmd(b"route") - } - pub fn info(&mut self) -> io::Result { - self.send_cmd(b"info") - } - pub fn chart_a(&mut self) -> io::Result { - self.send_cmd(b"chart_a") - } - pub fn chart_b(&mut self, input: &str) -> io::Result { - let cmd = if input.is_empty() { - "chart_b".to_string() - } else { - format!("chart_b:{}", input) - }; - self.send_cmd(cmd.as_bytes()) - } - fn send_cmd<'a, V: Deserialize<'a>>(&'a mut self, cmd: &[u8]) -> io::Result { - self.udp.send(cmd)?; - let len = self.udp.recv(&mut self.buf)?; - match serde_yaml::from_slice::(&self.buf[..len]) { - Ok(val) => Ok(val), - Err(e) => { - log::error!( - "send_cmd {:?} {:?},{:?}", - std::str::from_utf8(cmd), - std::str::from_utf8(&self.buf[..len]), - e - ); - Err(io::Error::new( - io::ErrorKind::Other, - format!("data error {:?} buf_len={}", e, len), - )) - } - } - } - pub fn stop(&self) -> io::Result { - self.udp.send(b"stop")?; - let mut buf = [0; 10240]; - let len = self.udp.recv(&mut buf)?; - Ok(String::from_utf8(buf[..len].to_vec()).unwrap()) - } -} diff --git a/common/src/command/entity.rs b/common/src/command/entity.rs deleted file mode 100644 index 9770897..0000000 --- a/common/src/command/entity.rs +++ /dev/null @@ -1,68 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::net::{Ipv4Addr, SocketAddr}; - -#[derive(Serialize, Deserialize, Debug)] -pub struct Info { - pub name: String, - pub virtual_ip: String, - pub virtual_gateway: String, - pub virtual_netmask: String, - pub connect_status: String, - pub relay_server: String, - pub nat_type: String, - pub public_ips: String, - pub local_addr: String, - pub ipv6_addr: String, - pub port_mapping_list: Vec<(bool, SocketAddr, String)>, - pub in_ips: Vec<(u32, u32, Ipv4Addr)>, - pub out_ips: Vec<(u32, u32)>, - pub udp_listen_addr: Vec, - pub tcp_listen_addr: String, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct RouteItem { - pub destination: String, - pub next_hop: String, - pub metric: String, - pub rt: String, - pub interface: String, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct DeviceItem { - pub name: String, - pub virtual_ip: String, - pub nat_type: String, - pub public_ips: String, - pub local_ip: String, - pub ipv6: String, - pub nat_traversal_type: String, - pub rt: String, - pub status: String, - pub client_secret: bool, - pub client_secret_hash: Vec, - pub current_client_secret: bool, - pub current_client_secret_hash: Vec, - pub wire_guard: bool, -} - -#[derive(Serialize, Deserialize, Debug, Default)] -pub struct ChartA { - pub disable_stats: bool, - pub up_total: u64, - pub down_total: u64, - pub up_map: HashMap, - pub down_map: HashMap, -} - -#[derive(Serialize, Deserialize, Debug, Default)] -pub struct ChartB { - pub disable_stats: bool, - pub ip: Option, - pub up_total: u64, - pub up_list: Vec, - pub down_total: u64, - pub down_list: Vec, -} diff --git a/common/src/command/mod.rs b/common/src/command/mod.rs deleted file mode 100644 index 2ffa8bc..0000000 --- a/common/src/command/mod.rs +++ /dev/null @@ -1,353 +0,0 @@ -use std::collections::HashSet; -use std::io; -use std::net::Ipv4Addr; -use vnt::channel::ConnectProtocol; -use vnt::core::Vnt; - -use crate::command::entity::{ChartA, ChartB, DeviceItem, Info, RouteItem}; -use crate::console_out; - -pub mod client; -pub mod entity; -pub mod server; - -pub enum CommandEnum { - Route, - List, - All, - Info, - ChartA, - ChartB(String), - Stop, -} - -pub fn command_str(cmd: &str, vnt: &Vnt) -> bool { - if cmd.is_empty() { - return false; - } - let cmd = cmd.to_lowercase(); - let cmd = cmd.trim(); - match cmd { - "list" => { - let list = command_list(&vnt); - console_out::console_device_list(list); - } - "info" => { - let info = command_info(&vnt); - console_out::console_info(info); - } - "route" => { - let route = command_route(&vnt); - console_out::console_route_table(route); - } - "all" => { - let list = command_list(&vnt); - console_out::console_device_list_all(list); - } - "chart_a" => { - let chart = command_chart_a(&vnt); - console_out::console_chart_a(chart); - } - "stop" => { - let _ = vnt.stop(); - return false; - } - _ => {} - } - if let Some(ip) = cmd.strip_prefix("chart_b") { - let chart = if ip.is_empty() { - command_chart_b(&vnt, &vnt.current_device().virtual_gateway.to_string()) - } else { - command_chart_b(&vnt, &ip[1..]) - }; - console_out::console_chart_b(chart); - } - println!(); - return true; -} - -pub fn command(cmd: CommandEnum) { - if let Err(e) = command_(cmd) { - println!("cmd: {:?}", e); - } -} - -fn command_(cmd: CommandEnum) -> io::Result<()> { - let mut command_client = client::CommandClient::new()?; - match cmd { - CommandEnum::Route => { - let list = command_client.route()?; - console_out::console_route_table(list); - } - CommandEnum::List => { - let list = command_client.list()?; - console_out::console_device_list(list); - } - CommandEnum::All => { - let list = command_client.list()?; - console_out::console_device_list_all(list); - } - CommandEnum::Info => { - let info = command_client.info()?; - console_out::console_info(info); - } - CommandEnum::ChartA => { - let chart = command_client.chart_a()?; - console_out::console_chart_a(chart); - } - CommandEnum::ChartB(input) => { - let chart = command_client.chart_b(&input)?; - console_out::console_chart_b(chart); - } - CommandEnum::Stop => { - command_client.stop()?; - } - } - Ok(()) -} - -pub fn command_route(vnt: &Vnt) -> Vec { - let route_table = vnt.route_table(); - let server_addr = vnt.config().server_address_str.clone(); - let mut route_list = Vec::with_capacity(route_table.len()); - for (destination, routes) in route_table { - for route in routes { - let next_hop = vnt - .route_key(&route.route_key()) - .map_or(String::new(), |v| v.to_string()); - let metric = route.metric.to_string(); - let rt = if route.rt < 0 { - "".to_string() - } else { - route.rt.to_string() - }; - let interface = match route.protocol { - ConnectProtocol::UDP => route.addr.to_string(), - ConnectProtocol::TCP => { - format!("tcp@{}", route.addr) - } - ConnectProtocol::WS | ConnectProtocol::WSS => server_addr.clone(), - }; - - let item = RouteItem { - destination: destination.to_string(), - next_hop, - metric, - rt, - interface, - }; - route_list.push(item); - } - } - route_list -} - -pub fn command_list(vnt: &Vnt) -> Vec { - let info = vnt.current_device(); - let device_list = vnt.device_list(); - let mut list = Vec::new(); - let current_client_secret = vnt.client_encrypt(); - let client_encrypt_hash = vnt.client_encrypt_hash().unwrap_or(&[]); - for peer in device_list { - let name = peer.name; - let virtual_ip = peer.virtual_ip.to_string(); - let (nat_type, public_ips, local_ip, ipv6) = - if let Some(nat_info) = vnt.peer_nat_info(&peer.virtual_ip) { - let nat_type = format!("{:?}", nat_info.nat_type); - let public_ips: Vec = - nat_info.public_ips.iter().map(|v| v.to_string()).collect(); - let public_ips = public_ips.join(","); - let local_ip = nat_info - .local_ipv4() - .map(|v| v.to_string()) - .unwrap_or("None".to_string()); - let ipv6 = nat_info - .ipv6() - .map(|v| v.to_string()) - .unwrap_or("None".to_string()); - (nat_type, public_ips, local_ip, ipv6) - } else { - ( - "".to_string(), - "".to_string(), - "".to_string(), - "".to_string(), - ) - }; - let (nat_traversal_type, rt) = if let Some(route) = vnt.route(&peer.virtual_ip) { - let nat_traversal_type = if route.metric == 1 { - if route.protocol.is_base_tcp() { - "tcp-p2p" - } else { - "p2p" - } - } else { - let next_hop = vnt.route_key(&route.route_key()); - if let Some(next_hop) = next_hop { - if info.is_gateway(&next_hop) { - "server-relay" - } else { - "client-relay" - } - } else { - "server-relay" - } - } - .to_string(); - let rt = if route.rt < 0 { - "".to_string() - } else { - route.rt.to_string() - }; - (nat_traversal_type, rt) - } else { - ("relay".to_string(), "".to_string()) - }; - let status = format!("{:?}", peer.status); - let client_secret = peer.client_secret; - let item = DeviceItem { - name, - virtual_ip, - nat_type, - public_ips, - local_ip, - ipv6, - nat_traversal_type, - rt, - status, - client_secret, - client_secret_hash: peer.client_secret_hash, - current_client_secret, - current_client_secret_hash: client_encrypt_hash.to_vec(), - wire_guard: peer.wireguard, - }; - list.push(item); - } - list -} - -pub fn command_info(vnt: &Vnt) -> Info { - let config = vnt.config(); - let current_device = vnt.current_device(); - let nat_info = vnt.nat_info(); - let name = vnt.name().to_string(); - let virtual_ip = current_device.virtual_ip().to_string(); - let virtual_gateway = current_device.virtual_gateway().to_string(); - let virtual_netmask = current_device.virtual_netmask.to_string(); - let connect_status = format!("{:?}", vnt.connection_status()); - let relay_server = if current_device.connect_server.port() == 0 { - config.server_address_str.clone() - } else { - current_device.connect_server.to_string() - }; - let nat_type = format!("{:?}", nat_info.nat_type); - let public_ips: Vec = nat_info.public_ips.iter().map(|v| v.to_string()).collect(); - let public_ips = public_ips.join(","); - let local_addr = nat_info - .local_ipv4() - .map(|v| v.to_string()) - .unwrap_or("None".to_string()); - let ipv6_addr = nat_info - .ipv6() - .map(|v| v.to_string()) - .unwrap_or("None".to_string()); - #[cfg(feature = "port_mapping")] - let port_mapping_list = vnt.config().port_mapping_list.clone(); - #[cfg(not(feature = "port_mapping"))] - let port_mapping_list = vec![]; - let in_ips = vnt.config().in_ips.clone(); - let out_ips = vnt.config().out_ips.clone(); - let udp_listen_addr = nat_info - .udp_ports - .iter() - .map(|port| format!("0.0.0.0:{}", port)) - .collect(); - let tcp_listen_addr = format!("0.0.0.0:{}", nat_info.tcp_port); - Info { - name, - virtual_ip, - virtual_gateway, - virtual_netmask, - connect_status, - relay_server, - nat_type, - public_ips, - local_addr, - ipv6_addr, - port_mapping_list, - in_ips, - out_ips, - udp_listen_addr, - tcp_listen_addr, - } -} - -pub fn command_chart_a(vnt: &Vnt) -> ChartA { - let disable_stats = !vnt.config().enable_traffic; - if disable_stats { - let mut chart = ChartA::default(); - chart.disable_stats = true; - return chart; - } - let (up_total, up_map) = vnt.up_stream_all().unwrap_or_default(); - let (down_total, down_map) = vnt.down_stream_all().unwrap_or_default(); - ChartA { - disable_stats, - up_total, - down_total, - up_map, - down_map, - } -} - -pub fn command_chart_b(vnt: &Vnt, input_str: &str) -> ChartB { - let disable_stats = !vnt.config().enable_traffic; - if disable_stats { - let mut chart = ChartB::default(); - chart.disable_stats = true; - return chart; - } - let (_, up_map) = vnt.up_stream_history().unwrap_or_default(); - let (_, down_map) = vnt.down_stream_history().unwrap_or_default(); - let up_keys: HashSet<_> = up_map.keys().cloned().collect(); - let down_keys: HashSet<_> = down_map.keys().cloned().collect(); - let mut keys: Vec = up_keys.union(&down_keys).cloned().collect(); - keys.sort(); - if let Some(ip) = find_matching_ipv4_address(input_str, &keys) { - let (up_total, up_list) = up_map.get(&ip).cloned().unwrap_or_default(); - let (down_total, down_list) = down_map.get(&ip).cloned().unwrap_or_default(); - ChartB { - disable_stats, - ip: Some(ip), - up_total, - up_list, - down_total, - down_list, - } - } else { - ChartB::default() - } -} - -fn match_from_end(input_str: &str, ip: &str) -> bool { - let mut input_chars = input_str.chars().rev(); - let mut ip_chars = ip.chars().rev(); - - while let (Some(ic), Some(pc)) = (input_chars.next(), ip_chars.next()) { - if ic != pc { - return false; - } - } - - input_chars.next().is_none() // Ensure all input characters matched -} - -fn find_matching_ipv4_address(input_str: &str, ip_addresses: &[Ipv4Addr]) -> Option { - for &ip in ip_addresses { - let ip_str = ip.to_string(); - if match_from_end(input_str, &ip_str) { - return Some(ip); - } - } - None -} diff --git a/common/src/command/server.rs b/common/src/command/server.rs deleted file mode 100644 index 38a7c14..0000000 --- a/common/src/command/server.rs +++ /dev/null @@ -1,89 +0,0 @@ -use crate::command::command_chart_b; -use std::io; -use std::io::Write; -use std::net::UdpSocket; -use vnt::core::Vnt; - -pub struct CommandServer {} - -impl CommandServer { - pub fn new() -> Self { - Self {} - } -} - -impl CommandServer { - pub fn start(self, vnt: Vnt) -> io::Result<()> { - let udp = if let Ok(udp) = UdpSocket::bind("127.0.0.1:39271") { - udp - } else { - UdpSocket::bind("127.0.0.1:0")? - }; - let addr = udp.local_addr()?; - log::info!("启动后台cmd:{:?}", addr); - if let Err(e) = save_port(addr.port()) { - log::warn!("保存后台命令端口失败:{:?}", e); - } - - let mut buf = [0u8; 64]; - loop { - let (len, addr) = udp.recv_from(&mut buf)?; - match std::str::from_utf8(&buf[..len]) { - Ok(cmd) => { - if let Ok(out) = command(cmd, &vnt) { - if let Err(e) = udp.send_to(out.as_bytes(), addr) { - log::warn!("cmd={},err={:?}", cmd, e); - } - if "stopped" == &out { - break; - } - } - } - Err(e) => { - log::warn!("{:?}", e); - } - } - } - Ok(()) - } -} -fn save_port(port: u16) -> io::Result<()> { - let path_buf = crate::cli::app_home()?.join("command-port"); - let mut file = std::fs::File::create(path_buf)?; - file.write_all(port.to_string().as_bytes())?; - file.sync_all() -} - -fn command(cmd: &str, vnt: &Vnt) -> io::Result { - let cmd = cmd.trim(); - let out_str = match cmd { - "route" => serde_yaml::to_string(&crate::command::command_route(vnt)) - .unwrap_or_else(|e| format!("error {:?}", e)), - "list" => serde_yaml::to_string(&crate::command::command_list(vnt)) - .unwrap_or_else(|e| format!("error {:?}", e)), - "info" => serde_yaml::to_string(&crate::command::command_info(vnt)) - .unwrap_or_else(|e| format!("error {:?}", e)), - "chart_a" => serde_yaml::to_string(&crate::command::command_chart_a(vnt)) - .unwrap_or_else(|e| format!("error {:?}", e)), - "stop" => { - vnt.stop(); - "stopped".to_string() - } - _ => { - if let Some(ip) = cmd.strip_prefix("chart_b") { - let chart = if ip.is_empty() { - command_chart_b(&vnt, &vnt.current_device().virtual_gateway.to_string()) - } else { - command_chart_b(&vnt, &ip[1..]) - }; - serde_yaml::to_string(&chart).unwrap_or_else(|e| format!("error {:?}", e)) - } else { - format!( - "command '{}' not found. Try to enter: 'route'/'list'/'stop' \n", - cmd - ) - } - } - }; - Ok(out_str) -} diff --git a/common/src/config/file_config.rs b/common/src/config/file_config.rs deleted file mode 100644 index a86163e..0000000 --- a/common/src/config/file_config.rs +++ /dev/null @@ -1,190 +0,0 @@ -use anyhow::anyhow; -use std::net::Ipv4Addr; -use std::str::FromStr; - -use crate::config::get_device_id; -use crate::{args_parse, config}; -use serde::{Deserialize, Serialize}; -use vnt::channel::punch::PunchModel; -use vnt::channel::UseChannelType; -use vnt::cipher::CipherModel; -use vnt::compression::Compressor; -use vnt::core::Config; - -#[derive(Serialize, Deserialize, Debug)] -#[serde(default)] -pub struct FileConfig { - #[cfg(target_os = "windows")] - pub tap: bool, - pub token: String, - pub device_id: String, - pub name: String, - pub server_address: String, - pub stun_server: Vec, - pub dns: Vec, - pub in_ips: Vec, - pub out_ips: Vec, - pub password: Option, - pub mtu: Option, - pub tcp: bool, - pub ip: Option, - pub use_channel: String, - #[cfg(feature = "ip_proxy")] - pub no_proxy: bool, - pub server_encrypt: bool, - pub cipher_model: Option, - pub finger: bool, - pub punch_model: String, - pub ports: Option>, - pub cmd: bool, - pub first_latency: bool, - pub device_name: Option, - pub packet_loss: Option, - pub packet_delay: u32, - #[cfg(feature = "port_mapping")] - pub mapping: Vec, - pub compressor: Option, - pub vnt_mapping: Vec, - pub disable_stats: bool, - // 允许传递wg流量 - pub allow_wire_guard: bool, - pub local_dev: Option, -} - -impl Default for FileConfig { - fn default() -> Self { - let mut stun_server = Vec::new(); - for x in config::PUB_STUN { - stun_server.push(x.to_string()); - } - Self { - #[cfg(target_os = "windows")] - tap: false, - token: "".to_string(), - device_id: get_device_id(), - name: gethostname::gethostname() - .to_str() - .unwrap_or("UnknownName") - .to_string(), - server_address: "nat1.wherewego.top:29872".to_string(), - stun_server, - dns: vec![], - in_ips: vec![], - out_ips: vec![], - password: None, - mtu: None, - tcp: false, - ip: None, - use_channel: "all".to_string(), - #[cfg(feature = "ip_proxy")] - no_proxy: false, - server_encrypt: false, - cipher_model: None, - finger: false, - punch_model: "all".to_string(), - ports: None, - cmd: false, - first_latency: false, - device_name: None, - packet_loss: None, - packet_delay: 0, - #[cfg(feature = "port_mapping")] - mapping: vec![], - compressor: None, - vnt_mapping: vec![], - disable_stats: false, - allow_wire_guard: false, - local_dev: None, - } - } -} - -pub fn read_config(file_path: &str) -> anyhow::Result<(Config, Vec, bool)> { - let conf = std::fs::read_to_string(file_path)?; - let file_conf = match serde_yaml::from_str::(&conf) { - Ok(val) => val, - Err(e) => { - log::error!("serde_yaml::from_str {:?}", e); - return Err(anyhow!("serde_yaml::from_str {:?}", e)); - } - }; - if file_conf.token.is_empty() { - return Err(anyhow!("token is_empty")); - } - - let in_ips = match args_parse::ips_parse(&file_conf.in_ips) { - Ok(in_ips) => in_ips, - Err(e) => { - return Err(anyhow!("in_ips {:?} error:{}", &file_conf.in_ips, e)); - } - }; - let out_ips = match args_parse::out_ips_parse(&file_conf.out_ips) { - Ok(out_ips) => out_ips, - Err(e) => { - return Err(anyhow!("out_ips {:?} error:{}", &file_conf.out_ips, e)); - } - }; - let virtual_ip = match file_conf.ip.clone().map(|v| Ipv4Addr::from_str(&v)) { - None => None, - Some(r) => Some(r.map_err(|e| anyhow!("ip {:?} error:{}", &file_conf.ip, e))?), - }; - 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() { - Err(anyhow!("cipher_model undefined"))? - } else { - CipherModel::None - } - #[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))] - CipherModel::AesGcm - }; - - let punch_model = PunchModel::from_str(&file_conf.punch_model).map_err(|e| anyhow!("{}", e))?; - let use_channel_type = - UseChannelType::from_str(&file_conf.use_channel).map_err(|e| anyhow!("{}", e))?; - let compressor = if let Some(compressor) = file_conf.compressor.as_ref() { - Compressor::from_str(compressor).map_err(|e| anyhow!("{}", e))? - } else { - Compressor::None - }; - let config = Config::new( - #[cfg(target_os = "windows")] - #[cfg(feature = "integrated_tun")] - file_conf.tap, - file_conf.token, - file_conf.device_id, - file_conf.name, - file_conf.server_address, - file_conf.dns, - file_conf.stun_server, - in_ips, - out_ips, - file_conf.password, - file_conf.mtu, - virtual_ip, - #[cfg(feature = "integrated_tun")] - #[cfg(feature = "ip_proxy")] - file_conf.no_proxy, - file_conf.server_encrypt, - cipher_model, - file_conf.finger, - punch_model, - file_conf.ports, - file_conf.first_latency, - #[cfg(feature = "integrated_tun")] - file_conf.device_name, - use_channel_type, - file_conf.packet_loss, - file_conf.packet_delay, - #[cfg(feature = "port_mapping")] - file_conf.mapping, - compressor, - !file_conf.disable_stats, - file_conf.allow_wire_guard, - file_conf.local_dev, - )?; - - Ok((config, file_conf.vnt_mapping, file_conf.cmd)) -} diff --git a/common/src/config/mod.rs b/common/src/config/mod.rs deleted file mode 100644 index 8810c92..0000000 --- a/common/src/config/mod.rs +++ /dev/null @@ -1,38 +0,0 @@ -pub const PUB_STUN: [&'static str; 4] = [ - "stun.miwifi.com", - "stun.chat.bilibili.com", - "stun.hitv.com", - "stun.cdnbye.com", -]; -#[cfg(feature = "file_config")] -mod file_config; - -use crate::identifier; -#[cfg(feature = "file_config")] -pub use file_config::read_config; - -#[cfg(not(feature = "file_config"))] -pub fn read_config(_file_path: &str) -> anyhow::Result<(vnt::core::Config, Vec, bool)> { - unimplemented!() -} - -pub fn get_device_id() -> String { - if let Some(id) = identifier::get_unique_identifier() { - id - } else { - let path_buf = match crate::cli::app_home() { - Ok(path_buf) => path_buf.join("device-id"), - Err(e) => { - log::warn!("{:?}", e); - return String::new(); - } - }; - if let Ok(id) = std::fs::read_to_string(path_buf.as_path()) { - id - } else { - let id = uuid::Uuid::new_v4().to_string(); - let _ = std::fs::write(path_buf, &id); - id - } - } -} diff --git a/common/src/console_out/mod.rs b/common/src/console_out/mod.rs deleted file mode 100644 index 3a3bc7c..0000000 --- a/common/src/console_out/mod.rs +++ /dev/null @@ -1,366 +0,0 @@ -use console::{style, Style}; -use std::collections::HashSet; -use std::net::Ipv4Addr; - -use crate::command::entity::{ChartA, ChartB, DeviceItem, Info, RouteItem}; - -pub mod table; - -pub fn console_info(status: Info) { - println!("Name: {}", style(status.name).green()); - println!("Virtual ip: {}", style(status.virtual_ip).green()); - println!("Virtual gateway: {}", style(status.virtual_gateway).green()); - println!("Virtual netmask: {}", style(status.virtual_netmask).green()); - if status.connect_status.eq_ignore_ascii_case("Connected") { - println!( - "Connection status: {}", - style(status.connect_status).green() - ); - } else { - println!("Connection status: {}", style(status.connect_status).red()); - } - - println!("NAT type: {}", style(status.nat_type).green()); - println!("Relay server: {}", style(status.relay_server).green()); - println!( - "Udp listen: {}", - style(status.udp_listen_addr.join(", ")).green() - ); - println!("Tcp listen: {}", style(status.tcp_listen_addr).green()); - println!("Public ips: {}", style(status.public_ips).green()); - println!("Local addr: {}", style(status.local_addr).green()); - println!("IPv6: {}", style(status.ipv6_addr).green()); - - if !status.port_mapping_list.is_empty() { - println!("------------------------------------------"); - println!("Port mapping {}", status.port_mapping_list.len()); - for (is_tcp, addr, dest) in status.port_mapping_list { - if is_tcp { - println!(" TCP: {} -> {}", addr, dest) - } else { - println!(" UDP: {} -> {}", addr, dest) - } - } - } - if !status.in_ips.is_empty() || !status.out_ips.is_empty() { - println!("------------------------------------------"); - } - if !status.in_ips.is_empty() { - println!("IP forwarding {}", status.in_ips.len()); - for (dest, mask, ip) in status.in_ips { - println!( - " -- {} --> {}/{}", - ip, - Ipv4Addr::from(dest), - mask.count_ones() - ) - } - } - if !status.out_ips.is_empty() { - println!("Allows network {}", status.out_ips.len()); - for (dest, mask) in status.out_ips { - println!(" {}/{}", Ipv4Addr::from(dest), mask.count_ones()) - } - } -} - -fn convert(num: u64) -> String { - let gigabytes = num / (1024 * 1024 * 1024); - let remaining_bytes = num % (1024 * 1024 * 1024); - let megabytes = remaining_bytes / (1024 * 1024); - let remaining_bytes = remaining_bytes % (1024 * 1024); - let kilobytes = remaining_bytes / 1024; - let remaining_bytes = remaining_bytes % 1024; - let mut s = String::new(); - if gigabytes > 0 { - s.push_str(&format!("{} GB ", gigabytes)); - } - if megabytes > 0 { - s.push_str(&format!("{} MB ", megabytes)); - } - if kilobytes > 0 { - s.push_str(&format!("{} KB ", kilobytes)); - } - if remaining_bytes > 0 { - s.push_str(&format!("{} bytes", remaining_bytes)); - } - s -} - -pub fn console_route_table(mut list: Vec) { - if list.is_empty() { - println!("No route found"); - return; - } - list.sort_by(|t1, t2| t1.destination.cmp(&t2.destination)); - let mut out_list = Vec::with_capacity(list.len()); - - out_list.push(vec![ - ("Destination".to_string(), Style::new()), - ("Next Hop".to_string(), Style::new()), - ("Metric".to_string(), Style::new()), - ("Rt".to_string(), Style::new()), - ("Interface".to_string(), Style::new()), - ]); - for item in list { - out_list.push(vec![ - (item.destination, Style::new().green()), - (item.next_hop, Style::new().green()), - (item.metric, Style::new().green()), - (item.rt, Style::new().green()), - (item.interface, Style::new().green()), - ]); - } - - table::println_table(out_list) -} - -pub fn console_device_list(mut list: Vec) { - if list.is_empty() { - println!("No other devices found"); - return; - } - list.sort_by(|t1, t2| t1.virtual_ip.cmp(&t2.virtual_ip)); - list.sort_by(|t1, t2| t1.status.cmp(&t2.status)); - let mut out_list = Vec::with_capacity(list.len()); - //表头 - out_list.push(vec![ - ("Name".to_string(), Style::new()), - ("Virtual Ip".to_string(), Style::new()), - ("Status".to_string(), Style::new()), - ("P2P/Relay".to_string(), Style::new()), - ("Rt".to_string(), Style::new()), - ]); - for item in list { - let name = if item.wire_guard { - format!("{}(wg)", item.name) - } else { - item.name - }; - if &item.status == "Online" { - if !item.wire_guard - && (item.client_secret != item.current_client_secret - || (!item.current_client_secret_hash.is_empty() - && !item.client_secret_hash.is_empty() - && item.current_client_secret_hash != item.client_secret_hash)) - { - //加密状态不一致,无法通信的 - out_list.push(vec![ - (name, Style::new().red()), - (item.virtual_ip, Style::new().red()), - (item.status, Style::new().red()), - ("Mismatch".to_string(), Style::new().red()), - ("".to_string(), Style::new().red()), - ]); - } else { - if item.nat_traversal_type.contains("p2p") { - out_list.push(vec![ - (name, Style::new().green()), - (item.virtual_ip, Style::new().green()), - (item.status, Style::new().green()), - (item.nat_traversal_type, Style::new().green()), - (item.rt, Style::new().green()), - ]); - } else { - out_list.push(vec![ - (name, Style::new().yellow()), - (item.virtual_ip, Style::new().yellow()), - (item.status, Style::new().yellow()), - (item.nat_traversal_type, Style::new().yellow()), - (item.rt, Style::new().yellow()), - ]); - } - } - } else { - out_list.push(vec![ - (name, Style::new().color256(102)), - (item.virtual_ip, Style::new().color256(102)), - (item.status, Style::new().color256(102)), - ("".to_string(), Style::new().color256(102)), - ("".to_string(), Style::new().color256(102)), - ]); - } - } - table::println_table(out_list) -} - -pub fn console_device_list_all(mut list: Vec) { - if list.is_empty() { - println!("No other devices found"); - return; - } - list.sort_by(|t1, t2| t1.virtual_ip.cmp(&t2.virtual_ip)); - list.sort_by(|t1, t2| t1.status.cmp(&t2.status)); - let mut out_list = Vec::with_capacity(list.len()); - //表头 - out_list.push(vec![ - ("Name".to_string(), Style::new()), - ("Virtual Ip".to_string(), Style::new()), - ("Status".to_string(), Style::new()), - ("P2P/Relay".to_string(), Style::new()), - ("Rt".to_string(), Style::new()), - ("NAT Type".to_string(), Style::new()), - ("Public Ips".to_string(), Style::new()), - ("Local Ip".to_string(), Style::new()), - ("IPv6".to_string(), Style::new()), - ]); - for item in list { - if &item.status == "Online" { - if &item.nat_traversal_type == "p2p" { - out_list.push(vec![ - (item.name, Style::new().green()), - (item.virtual_ip, Style::new().green()), - (item.status, Style::new().green()), - (item.nat_traversal_type, Style::new().green()), - (item.rt, Style::new().green()), - (item.nat_type, Style::new().green()), - (item.public_ips, Style::new().green()), - (item.local_ip, Style::new().green()), - (item.ipv6, Style::new().green()), - ]); - } else { - out_list.push(vec![ - (item.name, Style::new().yellow()), - (item.virtual_ip, Style::new().yellow()), - (item.status, Style::new().yellow()), - (item.nat_traversal_type, Style::new().yellow()), - (item.rt, Style::new().yellow()), - (item.nat_type, Style::new().yellow()), - (item.public_ips, Style::new().yellow()), - (item.local_ip, Style::new().yellow()), - (item.ipv6, Style::new().yellow()), - ]); - } - } else { - out_list.push(vec![ - (item.name, Style::new().color256(102)), - (item.virtual_ip, Style::new().color256(102)), - (item.status, Style::new().color256(102)), - ("".to_string(), Style::new().color256(102)), - ("".to_string(), Style::new().color256(102)), - ("".to_string(), Style::new().color256(102)), - ("".to_string(), Style::new().color256(102)), - ("".to_string(), Style::new().color256(102)), - ("".to_string(), Style::new().color256(102)), - ]); - } - } - table::println_table(out_list) -} - -pub fn console_chart_a(chart_a: ChartA) { - if chart_a.disable_stats { - println!("Traffic statistics not enabled"); - return; - } - println!(); - println!("-----------------------------------------------------------------"); - println!( - "Upload total = {}", - style(convert(chart_a.up_total)).green() - ); - println!( - "Download total = {}", - style(convert(chart_a.down_total)).green() - ); - println!("-----------------------------------------------------------------"); - let up_keys: HashSet<_> = chart_a.up_map.keys().cloned().collect(); - let down_keys: HashSet<_> = chart_a.down_map.keys().cloned().collect(); - let mut keys: Vec = up_keys.union(&down_keys).cloned().collect(); - // 排序 - keys.sort(); - - // 找到最大的值,用于缩放条形图长度 - let up_max_value = *chart_a.up_map.values().max().unwrap_or(&0); - let down_max_value = *chart_a.down_map.values().max().unwrap_or(&0); - let max_value = up_max_value.max(down_max_value); - let max_value = max_value.max(1); - let max_height = 50; - // 打印条形图 - for key in &keys { - if let Some(&value) = chart_a.up_map.get(key) { - let bar = "█".repeat(((value as f64 / max_value as f64) * max_height as f64) as usize); - println!( - "{:<10} | {} upload {}", - key, - bar, - style(convert(value)).green() - ); - } - if let Some(&value) = chart_a.down_map.get(key) { - let bar = "█".repeat(((value as f64 / max_value as f64) * max_height as f64) as usize); - println!( - "{:<10} | {} download {}", - key, - bar, - style(convert(value)).green() - ); - } - println!("-"); - } -} - -pub fn console_chart_b(chart_b: ChartB) { - if chart_b.disable_stats { - println!("Traffic statistics not enabled"); - return; - } - let ip = if let Some(ip) = chart_b.ip { - ip - } else { - println!("Ip: None"); - return; - }; - println!("---------------------------- upload ----------------------------"); - println!("IP: {}", ip); - println!("Upload total: {}", style(convert(chart_b.up_total)).green()); - println!( - "Max: {}", - style(convert( - chart_b - .up_list - .iter() - .max() - .cloned() - .map_or(0, |v| v as u64) - )) - .green() - ); - console_chart_b_list(chart_b.up_list); - println!("---------------------------- download ----------------------------"); - println!("IP: {}", ip); - println!( - "Download total: {}", - style(convert(chart_b.down_total)).green() - ); - println!( - "Max: {}", - style(convert( - chart_b - .down_list - .iter() - .max() - .cloned() - .map_or(0, |v| v as u64) - )) - .green() - ); - console_chart_b_list(chart_b.down_list); -} -fn console_chart_b_list(list: Vec) { - let max_value = *list.iter().max().unwrap_or(&0); - let max_value = max_value.max(1); - let max_height = max_value.min(20); - // 遍历从最大高度到0 - for i in (0..=max_height).rev() { - for &value in &list { - let scaled_value = (value as f64 / max_value as f64 * max_height as f64) as usize; - if scaled_value >= i { - print!("█"); - } else { - print!(" "); - } - } - println!(); - } -} diff --git a/common/src/console_out/table.rs b/common/src/console_out/table.rs deleted file mode 100644 index aacc006..0000000 --- a/common/src/console_out/table.rs +++ /dev/null @@ -1,23 +0,0 @@ -use console::Style; - -pub fn println_table(table: Vec>) { - if table.is_empty() { - return; - } - let mut width_list = vec![0; table[0].len()]; - for in_list in table.iter() { - for (index, (item, _)) in in_list.iter().enumerate() { - let width = console::measure_text_width(item) + 4; - if width_list[index] < width { - width_list[index] = width; - } - } - } - for in_list in table { - for (col, (item, style)) in in_list.iter().enumerate() { - let str = format!("{:1$}", item, width_list[col]); - print!("{}", style.apply_to(str)); - } - println!() - } -} diff --git a/common/src/identifier.rs b/common/src/identifier.rs deleted file mode 100644 index 329acd3..0000000 --- a/common/src/identifier.rs +++ /dev/null @@ -1,78 +0,0 @@ -#[cfg(target_os = "windows")] -pub fn get_unique_identifier() -> Option { - use std::os::windows::process::CommandExt; - use std::process::Command; - let output = match Command::new("wmic") - .creation_flags(0x08000000) - .args(&["csproduct", "get", "UUID"]) - .output() - { - Ok(output) => output, - Err(_) => { - return None; - } - }; - - let result = String::from_utf8_lossy(&output.stdout); - let identifier = result.lines().nth(1).unwrap_or("").trim(); - if identifier.is_empty() { - None - } else { - Some(identifier.to_string()) - } -} - -#[cfg(target_os = "macos")] -pub fn get_unique_identifier() -> Option { - use std::process::Command; - let output = match Command::new("ioreg") - .args(&["-rd1", "-c", "IOPlatformExpertDevice"]) - .output() - { - Ok(output) => output, - Err(_) => { - return None; - } - }; - - let result = String::from_utf8_lossy(&output.stdout); - let identifier = result - .lines() - .find(|line| line.contains("IOPlatformUUID")) - .unwrap_or("") - .trim(); - if identifier.is_empty() { - None - } else { - Some(identifier.to_string()) - } -} - -#[cfg(target_os = "linux")] -pub fn get_unique_identifier() -> Option { - use std::process::Command; - - // Try to execute 'dmidecode' command to get the system identifier first. - if let Ok(output) = Command::new("dmidecode") - .arg("-s") - .arg("system-uuid") - .output() - { - let identifier = String::from_utf8_lossy(&output.stdout).trim().to_owned(); - if !identifier.is_empty() { - return Some(identifier.to_string()); - } - } - - // Try to read file /etc/machine-id if 'dmidecode' command cannot be executed or get nothing. - // 对 linux 或 wsl 来说,读取 /etc/machine-id 即可获取当前操作系统的 - // 唯一标识,而且某些环境没有预装`dmidecode`命令 - if let Ok(identifier) = std::fs::read_to_string("/etc/machine-id") { - let identifier = identifier.trim(); - if !identifier.is_empty() { - return Some(identifier.to_string()); - } - } - - None -} diff --git a/common/src/lib.rs b/common/src/lib.rs deleted file mode 100644 index d218387..0000000 --- a/common/src/lib.rs +++ /dev/null @@ -1,12 +0,0 @@ -pub mod args_parse; -#[cfg(feature = "command")] -pub mod command; -pub mod config; -#[cfg(feature = "command")] -mod console_out; -pub mod identifier; - -pub mod cli; -mod generated_serial_number; - -pub mod callback; diff --git a/dll/amd64/wintun.dll b/dll/amd64/wintun.dll new file mode 100644 index 0000000..aee04e7 Binary files /dev/null and b/dll/amd64/wintun.dll differ diff --git a/dll/arm/wintun.dll b/dll/arm/wintun.dll new file mode 100644 index 0000000..0017794 Binary files /dev/null and b/dll/arm/wintun.dll differ diff --git a/dll/arm64/wintun.dll b/dll/arm64/wintun.dll new file mode 100644 index 0000000..dc4e4ae Binary files /dev/null and b/dll/arm64/wintun.dll differ diff --git a/dll/x86/wintun.dll b/dll/x86/wintun.dll new file mode 100644 index 0000000..2ab97db Binary files /dev/null and b/dll/x86/wintun.dll differ diff --git a/documents/img/ssh.jpg b/documents/img/ssh.jpg deleted file mode 100644 index ea3dde1..0000000 Binary files a/documents/img/ssh.jpg and /dev/null differ diff --git a/src/args_config.rs b/src/args_config.rs new file mode 100644 index 0000000..924edd1 --- /dev/null +++ b/src/args_config.rs @@ -0,0 +1,468 @@ +use anyhow::anyhow; +use clap::Parser; +use ipnet::Ipv4Net; +use serde::{Deserialize, Serialize}; +use std::net::Ipv4Addr; +use std::path::{Path, PathBuf}; +use vnt_core::context::config::Config; +use vnt_core::nat::NetInput; +use vnt_core::tls::verifier::CertValidationMode; +use vnt_core::tunnel_core::server::transport::config::ProtocolAddress; +use vnt_ipc as vnt_core; +use vnt_ipc::port_mapping::PortMapping; + +#[derive(Debug, Deserialize, Serialize, Default)] +pub struct FileConfig { + pub server: Option>, + pub network_code: Option, + pub ip: Option, + pub no_punch: Option, + pub rtx: Option, + pub compress: Option, + pub fec: Option, + pub input: Option>, + pub output: Option>, + pub no_nat: Option, + pub no_tun: Option, + pub mtu: Option, + pub ctrl_port: Option, + pub port_mapping: Option>, + pub allow_mapping: Option, + pub device_id: Option, + pub device_name: Option, + pub tun_name: Option, + pub password: Option, + pub cert_mode: Option, + pub udp_stun: Option>, + pub tcp_stun: Option>, +} + +impl FileConfig { + pub fn load(path: &Path) -> anyhow::Result { + let content = std::fs::read_to_string(path)?; + Ok(toml::from_str(&content)?) + } + + pub fn save(&self, path: &Path) -> anyhow::Result<()> { + let content = toml::to_string_pretty(self)?; + std::fs::write(path, content)?; + Ok(()) + } + + pub fn to_server_addr(&self) -> anyhow::Result> { + if let Some(server_raw) = &self.server { + let mut server_addr = Vec::with_capacity(server_raw.len()); + for x in server_raw { + server_addr.push( + x.parse::() + .map_err(|e| anyhow!("invalid server address '{}': {}", x, e))?, + ) + } + Ok(server_addr) + } else { + Ok(Vec::new()) + } + } + pub fn to_port_mapping(&self) -> anyhow::Result> { + if let Some(port_mapping_raw) = &self.port_mapping { + let mut port_mapping = Vec::with_capacity(port_mapping_raw.len()); + for x in port_mapping_raw { + port_mapping.push( + x.parse::() + .map_err(|e| anyhow!("invalid port_mapping '{}': {}", x, e))?, + ) + } + Ok(port_mapping) + } else { + Ok(Vec::new()) + } + } +} + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +pub struct Args { + /// 服务器地址 例如 `-s quic://127.0.0.1:29872`, 支持quic/tcp/wss/dynamic + #[clap(short, long)] + pub server: Vec, + /// 网络编号,相同编号的会组同一个局域网 + #[clap(short, long)] + pub network_code: Option, + #[clap(short = 'k', long, hide = true)] + pub token: Option, + /// 自定义虚拟IP + #[clap(long)] + pub ip: Option, + /// 启用加密,设置加密密码 + #[clap(short, long)] + pub password: Option, + /// 启用quic优化传输 + #[clap(long)] + pub rtx: bool, + /// 启用压缩 (LZ4) + #[clap(short = 'z', long)] + pub compress: bool, + /// 启用 FEC 前向纠错,损失一定带宽来提升网络稳定性 + #[clap(long)] + pub fec: bool, + /// 入栈监听网段 + #[clap(short, long)] + pub input: Vec, + /// 出栈允许网段 + #[clap(short, long)] + pub output: Vec, + /// 自定义设备名称 + #[clap(long, alias = "name")] + pub device_name: Option, + /// 设备id + #[clap(long, alias = "id")] + pub device_id: Option, + /// 关闭打洞 + #[clap(long)] + pub no_punch: bool, + /// 服务端证书验证 + #[clap(long)] + pub cert_mode: Option, + /// 虚拟网卡名称 + #[clap(long)] + pub tun_name: Option, + /// 关闭内置子网NAT + #[clap(long)] + pub no_nat: bool, + /// 禁用tun,禁用后只能充当流量出口或者进行端口映射,无需管理员权限 + #[clap(long)] + pub no_tun: bool, + /// 端口映射,格式为:协议://本地监听地址-目标虚拟IP-目标映射地址 + #[clap(long)] + pub port_mapping: Vec, + /// 是否允许作为端口映射出口,开启后其他设备才可使用本设备的ip为"目标虚拟IP" + #[clap(long)] + pub allow_mapping: bool, + /// 设置mtu + #[clap(long)] + pub mtu: Option, + /// 控制端口,设置0时禁用控制服务 + #[clap(long)] + pub ctrl_port: Option, + /// 读取配置文件 + #[arg(long)] + pub conf: Option, + /// 输出配置文件示例 + #[clap(long)] + pub conf_example: bool, +} +impl Args { + pub fn parse_compatible() -> Self { + let mut args = Args::parse(); + if args.network_code.is_none() { + args.network_code = args.token.clone(); + } + args + } +} + +pub struct CtrlConfig { + pub ctrl_port: Option, +} + +pub fn build_config_from_args_and_file( + args: Option, + file: Option, +) -> anyhow::Result<(Config, CtrlConfig)> { + match (args, file) { + (Some(args), Some(file)) => build_from_args_and_file(args, file), + (Some(args), None) => build_from_args_only(args), + (None, Some(file)) => build_from_file_only(file), + (None, None) => Err(anyhow!("neither args nor config file provided")), + } +} + +fn build_from_args_and_file(args: Args, file: FileConfig) -> anyhow::Result<(Config, CtrlConfig)> { + let server_addr = if args.server.is_empty() { + file.to_server_addr()? + } else { + args.server + }; + let port_mapping = if args.port_mapping.is_empty() { + file.to_port_mapping()? + } else { + args.port_mapping + }; + + let network_code = args + .network_code + .or_else(|| file.network_code.clone()) + .ok_or_else(|| anyhow!("network_code is required"))?; + + let cert_mode = args + .cert_mode + .or_else(|| file.cert_mode.as_deref().and_then(|s| s.parse().ok())) + .unwrap_or(CertValidationMode::InsecureSkipVerification); + + let device_id = match args.device_id.or_else(|| file.device_id.clone()) { + Some(id) => id, + None => vnt_core::utils::device_id::get_device_id()?, + }; + + let input = if args.input.is_empty() { + file.input.unwrap_or_default() + } else { + args.input + }; + + let output = if args.output.is_empty() { + file.output.unwrap_or_default() + } else { + args.output + }; + let mut udp_stun = file.udp_stun.unwrap_or_default(); + for x in udp_stun.iter_mut() { + if !x.contains(':') { + x.push_str(":3478"); + } + } + let mut tcp_stun = file.tcp_stun.unwrap_or_default(); + for x in tcp_stun.iter_mut() { + if !x.contains(':') { + x.push_str(":3478"); + } + } + + let config = Config { + server_addr, + network_code, + ip: args.ip.or(file.ip), + no_punch: args.no_punch || file.no_punch.unwrap_or(false), + rtx: args.rtx || file.rtx.unwrap_or(false), + compress: args.compress || file.compress.unwrap_or(false), + fec: args.fec || file.fec.unwrap_or(false), + device_id, + device_name: args + .device_name + .or_else(|| file.device_name.clone()) + .unwrap_or_else(default_hostname), + tun_name: args.tun_name.or_else(|| file.tun_name.clone()), + password: args.password.or_else(|| file.password.clone()), + cert_mode, + input, + output, + no_nat: args.no_nat || file.no_nat.unwrap_or(false), + no_tun: args.no_tun || file.no_tun.unwrap_or(false), + mtu: args.mtu.or(file.mtu), + port_mapping, + allow_port_mapping: args.allow_mapping || file.allow_mapping.unwrap_or(false), + udp_stun, + tcp_stun, + }; + + let ctrl_config = CtrlConfig { + ctrl_port: args.ctrl_port.or(file.ctrl_port), + }; + Ok((config, ctrl_config)) +} + +fn build_from_args_only(args: Args) -> anyhow::Result<(Config, CtrlConfig)> { + let device_id = match args.device_id { + Some(id) => id, + None => vnt_core::utils::device_id::get_device_id()?, + }; + let config = Config { + server_addr: args.server, + network_code: args + .network_code + .ok_or_else(|| anyhow!("network_code is required"))?, + ip: args.ip, + no_punch: args.no_punch, + rtx: args.rtx, + input: args.input, + compress: args.compress, + fec: args.fec, + device_id, + device_name: args.device_name.unwrap_or_else(default_hostname), + tun_name: args.tun_name, + password: args.password, + cert_mode: args + .cert_mode + .unwrap_or(CertValidationMode::InsecureSkipVerification), + output: args.output, + no_nat: args.no_nat, + no_tun: args.no_tun, + mtu: args.mtu, + port_mapping: args.port_mapping, + allow_port_mapping: args.allow_mapping, + ..Default::default() + }; + let ctrl_config = CtrlConfig { + ctrl_port: args.ctrl_port, + }; + Ok((config, ctrl_config)) +} + +fn build_from_file_only(file: FileConfig) -> anyhow::Result<(Config, CtrlConfig)> { + let server_addr = file.to_server_addr()?; + let port_mapping = file.to_port_mapping()?; + + let cert_mode = file + .cert_mode + .as_deref() + .and_then(|s| s.parse().ok()) + .unwrap_or(CertValidationMode::InsecureSkipVerification); + + let device_id = match file.device_id.clone() { + Some(id) => id, + None => vnt_core::utils::device_id::get_device_id()?, + }; + let mut udp_stun = file.udp_stun.unwrap_or_default(); + for x in udp_stun.iter_mut() { + if !x.contains(':') { + x.push_str(":3478"); + } + } + let mut tcp_stun = file.tcp_stun.unwrap_or_default(); + for x in tcp_stun.iter_mut() { + if !x.contains(':') { + x.push_str(":3478"); + } + } + + let config = Config { + server_addr, + network_code: file + .network_code + .ok_or_else(|| anyhow!("network_code is required"))?, + ip: file.ip, + no_punch: file.no_punch.unwrap_or(false), + rtx: file.rtx.unwrap_or(false), + input: file.input.unwrap_or_default(), + compress: file.compress.unwrap_or(false), + fec: file.fec.unwrap_or(false), + device_id, + device_name: file.device_name.clone().unwrap_or_else(default_hostname), + tun_name: file.tun_name.clone(), + password: file.password.clone(), + cert_mode, + output: file.output.unwrap_or_default(), + no_nat: file.no_nat.unwrap_or(false), + no_tun: file.no_tun.unwrap_or(false), + mtu: file.mtu, + port_mapping, + allow_port_mapping: file.allow_mapping.unwrap_or(false), + udp_stun, + tcp_stun, + }; + let ctrl_config = CtrlConfig { + ctrl_port: file.ctrl_port, + }; + Ok((config, ctrl_config)) +} + +fn default_hostname() -> String { + hostname::get() + .ok() + .and_then(|v| v.into_string().ok()) + .unwrap_or_default() +} + +impl FileConfig { + pub fn print_example(path: Option<&Path>) -> anyhow::Result<()> { + const VERSION: &str = env!("CARGO_PKG_VERSION"); + + let example = format!( + r#"# ================================== +# VNT 配置文件示例(程序版本 v{version}) +# ================================== + +# --- 网络配置 --- +# 网络编号,相同网络编号的会组在同一个虚拟网 (必填) +network_code = "your_network_code" + +# 服务器地址列表(支持 quic / tcp / wss / dynamic) (必填) +# dynamic 协议使用dns txt解析记录值 +server = ["quic://1.2.3.4:29872"] + +# ===简单使用以下参数可以不动=== + +# 自定义虚拟 IP (可选) +# ip = "10.10.0.2" + +# 是否启用quic优化传输 (默认 false,设置为true时开启) +# rtx = false + +# 是否启用 FEC 前向纠错,损失一定带宽来提升网络稳定性(默认 false,设置为true时开启) +# fec = false + +# 是否关闭 P2P 打洞 (默认 false,设置为true时关闭) +# no_punch = false + +# 是否启用 LZ4 压缩 (默认 false,设置为true时开启) +# compress = false + +# 入栈监听网段 (逗号分隔的 CIDR 和目标 IP),用于点对网,将指定网段的流量发送到目标节点 +# input = ["192.168.0.0/24,10.26.0.2", "192.168.1.0/24,10.26.0.3"] + +# 出栈允许网段,用于点对网,允许指定网段的转发 +# output = ["0.0.0.0/0"] + +# 是否关闭内置子网NAT,关闭(设为true)后需要配置网卡转发,否则无法使用点对网。通常关闭内置子网NAT,使用系统的网卡转发,点对网性能会更好 +# no_nat = false + +# 是否关闭TUN虚拟网卡,关闭(设为true)后只能充当流量出口或者进行端口映射,关闭后无需管理员权限 +# no_tun = false + +# 端口映射,格式为:协议://本地监听地址-目标虚拟IP-目标映射地址 +# 端口映射用于在本地监听指定端口,并将收到的网络流量经由指定虚拟节点转发到目标地址,从而实现跨网络或内网服务访问 +# 例如 port_mapping = ["tcp://0.0.0.0:81-10.0.0.2-10.0.0.2:80"] +# tcp://0.0.0.0:81-10.0.0.2-10.0.0.2:80 则表示将本地tcp的81端口的数据转发到10.0.0.2:80 +# tcp://0.0.0.0:81-10.0.0.2-192.168.1.10:80 则表示将本地tcp的81端口的数据经过10.0.0.2转到192.168.1.10:80 +# tcp://0.0.0.0:81-10.0.0.2-anyonehost:80 则表示将本地tcp的81端口的数据经过10.0.0.2转到anyonehost:80 +# port_mapping = [] + +# 是否允许作为端口映射出口,开启(设置为true)后其他设备才可使用本设备的ip为"目标虚拟IP" +# 开启后虚拟网络其他设备可以使用此设备当跳板访问其他网络 +# allow_mapping = false + +# 控制服务的 tcp 端口 +# ctrl_port = 11233 + +# MTU 设置 +# mtu = 1400 + +# --- 设备配置 --- + +# 设备名称 (可选,默认读取本机 hostname) +# device_name = "my-device" + +# 设备 ID (可选,不填自动生成,不同设备ID不能相同) +# device_id = "device-id-xxxx" + +# 虚拟网卡名称 +# tun_name = "vnt-tun" + +# --- 安全配置 --- + +# 加密密码 (可选) +# password = "123456" + +# 证书校验方式: +# skip 跳过验证(默认) +# standard 使用系统证书验证 +# finger 使用证书指纹验证,服务端启动时日志会输出指纹, +# 例如 finger:3bdd8675606837cdf95d5e13445606315762315a78555f9da652940a25feaec1 +# cert_mode = "skip" + +# --- 其他配置 --- +# 自定义stun地址,分别用于udp打洞和tcp打洞,需要单独配置,不设置则用默认stun +# udp_stun = ["stun.chat.bilibili.com"] +# tcp_stun = ["stun.nextcloud.com:443"] +"#, + version = VERSION + ); + println!("--- 示例配置文件内容 ---\n{}", example); + if let Some(p) = path { + std::fs::write(p, &example)?; + println!("示例配置文件已写入 {}", p.display()); + } + + Ok(()) + } +} diff --git a/src/extract_wintun_dll.rs b/src/extract_wintun_dll.rs new file mode 100644 index 0000000..a633404 --- /dev/null +++ b/src/extract_wintun_dll.rs @@ -0,0 +1,33 @@ +use std::fs; +use std::io::{self, Write}; +use std::path::Path; + +#[cfg(target_arch = "x86_64")] +const WINTUN_DLL: &[u8] = include_bytes!("../dll/amd64/wintun.dll"); + +#[cfg(target_arch = "x86")] +const WINTUN_DLL: &[u8] = include_bytes!("../dll/x86/wintun.dll"); + +#[cfg(target_arch = "aarch64")] +const WINTUN_DLL: &[u8] = include_bytes!("../dll/arm64/wintun.dll"); + +#[cfg(target_arch = "arm")] +const WINTUN_DLL: &[u8] = include_bytes!("../dll/arm/wintun.dll"); + +pub fn extract_wintun() { + if let Err(e) = extract_wintun_impl() { + log::error!("extract wintun.dll {:?}", e); + } +} +fn extract_wintun_impl() -> io::Result<()> { + let path = std::env::current_exe() + .ok() + .and_then(|p| p.parent().map(|d| d.join("wintun.dll"))) + .unwrap_or_else(|| Path::new("wintun.dll").to_path_buf()); + + if !path.exists() { + let mut file = fs::File::create(&path)?; + file.write_all(WINTUN_DLL)?; + } + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..f4ee9bc --- /dev/null +++ b/src/lib.rs @@ -0,0 +1 @@ +pub mod log; diff --git a/src/log.rs b/src/log.rs new file mode 100644 index 0000000..738c81a --- /dev/null +++ b/src/log.rs @@ -0,0 +1,61 @@ +use std::io::Write; +use std::path::PathBuf; +pub fn log_init(log_name: &str) { + log_init0(log_name, None); +} +pub fn log_init0(log_name: &str, yaml: Option<&str>) { + let path = PathBuf::from("logs"); + + if !path.exists() { + let _ = std::fs::create_dir(&path); + } + + let log_config = path.join(yaml.unwrap_or("log4rs.yaml")); + + if !log_config.exists() + && let Ok(mut f) = std::fs::File::create(&log_config) + { + let log_file = path.join(format!("{log_name}.log")); + let mut string = String::new(); + string.push_str(log_name); + string.push_str(".{}.log"); + let log_file_pattern = path.join(&string); + + let c = format!( + r#" +refresh_rate: 30 seconds +appenders: + rolling_file: + kind: rolling_file + path: {} + append: true + encoder: + pattern: "{{d}} [{{f}}:{{L}}] {{h({{l}})}} {{M}}:{{m}}{{n}}" + policy: + kind: compound + trigger: + kind: size + limit: 10 mb + roller: + kind: fixed_window + pattern: {} + base: 1 + count: 5 + console: + kind: console + encoder: + pattern: "{{d}} {{h({{l}})}} {{m}}{{n}}" +root: + level: info + appenders: + - rolling_file + - console +"#, + log_file.display(), + log_file_pattern.display() + ); + let _ = f.write_all(c.as_bytes()); + } + + let _ = log4rs::init_file(log_config, Default::default()); +} diff --git a/src/main_cli.rs b/src/main_cli.rs new file mode 100644 index 0000000..55abef0 --- /dev/null +++ b/src/main_cli.rs @@ -0,0 +1,142 @@ +use anyhow::Context; +use args_config::{build_config_from_args_and_file, Args, FileConfig}; +use route_manager::Route; +use std::path::Path; +use vnt_ipc as vnt_core; + +use vnt_core::core::NetworkManager; +use vnt_core::utils::task_control::TaskGroupManager; +pub mod args_config; + +#[cfg(windows)] +mod extract_wintun_dll; + +#[tokio::main] +pub async fn main() { + if let Err(e) = main0().await { + log::error!("{:?}", e); + } +} +async fn main0() -> anyhow::Result<()> { + let args = Args::parse_compatible(); + + vnt2::log::log_init("vnt2"); + log::info!("version: {:?}", env!("CARGO_PKG_VERSION")); + #[cfg(windows)] + extract_wintun_dll::extract_wintun(); + if args.conf_example { + FileConfig::print_example(Some(Path::new("example_config.toml")))?; + return Ok(()); + } + let file_cfg = if let Some(path) = args.conf.as_ref() { + log::info!("loaded config from {:?}", path); + Some(FileConfig::load(path).context("failed to load config")?) + } else { + None + }; + let (config, ctrl_config) = + build_config_from_args_and_file(Some(args), file_cfg).context("invalid configuration")?; + + log::info!( + "server: {}", + config + .server_addr + .iter() + .map(|x| x.to_string()) + .collect::>() + .join(", ") + ); + log::info!("network code: {}", config.network_code); + log::info!("device id: {}", config.device_id); + log::info!("device name: {}", config.device_name); + log::info!("cert mode: {}", config.cert_mode); + log::info!("compress: {}", config.compress); + log::info!("rtx(quic channel): {}", config.rtx); + let sub_input = config.input.clone(); + if !config.input.is_empty() { + let x = config + .input + .iter() + .map(|v| v.to_string()) + .collect::>() + .join(","); + log::info!("Sub network input:{x}"); + } + if !config.output.is_empty() { + let x = config + .output + .iter() + .map(|v| v.to_string()) + .collect::>() + .join(","); + log::info!("Sub network output:{x}"); + } + + if let Some(password_sign) = config.key_sign() { + log::info!("password sign: {:?}", password_sign); + } + + let group_manager = TaskGroupManager::new(); + let (task_group, task_group_guard) = + group_manager.create_task().context("create task group")?; + + let mut network_manager = NetworkManager::create_network(Box::new(config), task_group) + .await + .context("create network")?; + let reg_msg = network_manager.register().await.context("register")?; + + if !network_manager.is_no_tun() { + log::info!("启动网络:{}/{}", reg_msg.ip, reg_msg.prefix_len); + network_manager.start_tun().await.context("start tun")?; + network_manager + .set_network_ip(reg_msg.ip, reg_msg.prefix_len) + .await + .context("set network ip")?; + if !sub_input.is_empty() { + let if_index = network_manager + .tun_if_index() + .await + .context("tun_if_index")?; + let mut route_manager = route_manager::RouteManager::new()?; + for x in sub_input { + let route = Route::new(x.net.network().into(), x.net.prefix_len()) + .with_gateway(x.target_ip.into()) + .with_if_index(if_index); + if let Err(e) = route_manager.add(&route) { + log::error!("add route [{route}] error: {e:?}"); + } else { + log::info!("add route [{route}] successful"); + } + } + } + } else { + log::info!( + "启动网络:{}/{} (无虚拟网卡)", + reg_msg.ip, + reg_msg.prefix_len + ); + } + let vnt_api = network_manager.vnt_api(); + if ctrl_config.ctrl_port.is_none_or(|p| p != 0) { + tokio::spawn(async move { + if let Err(e) = vnt_ipc::server::run_server(ctrl_config.ctrl_port, vnt_api).await { + log::error!("ipc:{e:?}"); + } + }); + } + loop { + tokio::select! { + _ = network_manager.wait_all_stopped() => { + break; + } + + _ = tokio::signal::ctrl_c() => { + log::info!("Ctrl+c received!"); + break; + } + } + } + drop(task_group_guard); + log::info!("stop network"); + Ok(()) +} diff --git a/src/main_ctrl.rs b/src/main_ctrl.rs new file mode 100644 index 0000000..dfe9112 --- /dev/null +++ b/src/main_ctrl.rs @@ -0,0 +1,42 @@ +use clap::{Parser, Subcommand}; +use vnt_ipc::message::ipc_request::IpcCmd; + +/// 操作vnt进程 +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct Args { + #[command(subcommand)] + command: Commands, + /// 核心进程控制端口 + #[clap(short, long)] + port: Option, +} + +#[derive(Subcommand, Debug)] +enum Commands { + /// 输出当前程序信息 + Info, + /// 输出客户端ip列表 + Ips, + /// 输出客户端信息列表 + #[command(alias = "list")] + Clients, + /// 输出IP路由信息 + Route, +} + +#[tokio::main] +pub async fn main() { + let args = Args::parse(); + let port = args.port; + let cmd = match args.command { + Commands::Info => IpcCmd::AppInfo(Default::default()), + + Commands::Ips => IpcCmd::ClientIps(Default::default()), + Commands::Clients => IpcCmd::ClientList(Default::default()), + Commands::Route => IpcCmd::AllRoute(Default::default()), + }; + if let Err(e) = vnt_ipc::client::run_client(cmd, port).await { + eprintln!("{:?}", e); + } +} diff --git a/src/main_web.rs b/src/main_web.rs new file mode 100644 index 0000000..abe2eb7 --- /dev/null +++ b/src/main_web.rs @@ -0,0 +1,36 @@ +use clap::Parser; +use std::net::SocketAddr; +use std::path::PathBuf; + +#[cfg(windows)] +mod extract_wintun_dll; + +/// vnt web服务 +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct Args { + /// 本地http服务监听地址 + #[clap(long)] + addr: Option, + /// 加载vnt配置路径,配置内容参考web端的配置格式 + #[clap(long)] + conf: Option, +} + +#[tokio::main] +pub async fn main() { + if let Err(e) = main0().await { + log::error!("{:?}", e); + } +} +async fn main0() -> anyhow::Result<()> { + let args = Args::parse(); + + vnt2::log::log_init("vnt2"); + log::info!("version: {:?}", env!("CARGO_PKG_VERSION")); + #[cfg(windows)] + extract_wintun_dll::extract_wintun(); + let addr = args.addr.unwrap_or("127.0.0.1:19099".parse()?); + vnt_web::run_http_server(addr, args.conf).await?; + Ok(()) +} diff --git a/vn-link-cli/Cargo.toml b/vn-link-cli/Cargo.toml deleted file mode 100644 index df9ef7c..0000000 --- a/vn-link-cli/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[package] -name = "vn-link-cli" -version = "1.2.16" -edition = "2021" - -[dependencies] -vn-link = { path = "../vn-link", default-features = false } -common = { path = "../common", default-features = false } -tokio = { version = "1.37.0", features = ["full"] } -log = "0.4.17" - -[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"] - -openssl = ["vn-link/openssl", "common/openssl"] -openssl-vendored = ["vn-link/openssl-vendored", "common/openssl-vendored"] -ring-cipher = ["vn-link/ring-cipher", "common/ring-cipher"] -aes_cbc = ["vn-link/aes_cbc", "common/aes_cbc"] -aes_ecb = ["vn-link/aes_ecb", "common/aes_ecb"] -sm4_cbc = ["vn-link/sm4_cbc", "common/sm4_cbc"] -aes_gcm = ["vn-link/aes_gcm", "common/aes_gcm"] -chacha20_poly1305 = ["vn-link/chacha20_poly1305", "common/chacha20_poly1305"] -server_encrypt = ["vn-link/server_encrypt", "common/server_encrypt"] -port_mapping = ["vn-link/port_mapping", "common/port_mapping"] -lz4 = ["vn-link/lz4_compress", "common/lz4"] -zstd = ["vn-link/zstd_compress", "common/zstd"] -upnp = ["vn-link/upnp", "common/upnp"] -ws = ["vn-link/ws", "common/ws"] -wss = ["vn-link/wss", "common/wss"] -log = ["common/log"] -command = ["common/command"] -file_config = ["common/file_config"] - diff --git a/vn-link-cli/README.md b/vn-link-cli/README.md deleted file mode 100644 index 3c37358..0000000 --- a/vn-link-cli/README.md +++ /dev/null @@ -1,61 +0,0 @@ -# 端口映射模式 - -## 一、特点 - -1. 不需要tap/tun虚拟网卡 -2. 不需要管理员/root权限 -3. 不改变本地路由 -4. 使用端口映射来访问目标服务 - -## 二、作用 - -和vnt互补,能简单快速构建网络,外部依赖更少 - -## 三、使用方式 - -和vnt的使用方式一样,只是多了"--vnt-mapping"这个参数 - -### vn-link作为被访问端,不需要额外配置vnt-mapping - -### vn-link访问vnt或者vn-link,需要加vnt-mapping - -例如: - -设备A 运行vnt(虚拟IP 10.26.0.A),设备B 运行vn-link(虚拟IP 10.26.0.B)。 - -如果要用B访问A上的tcp 80端口,则在设备B上需要加--vnt-mapping "tcp:port1-10.26.0.A:80" - -这个参数的作用是将B上的***本地端口port1***转发到设备A的地址10.26.0.A: -80,此时在设备B上可以访问本地port1端口从而间接访问10.26.0.A:80 - -## 四、vn-link的子网代理 - -vn-link也支持点对网参数。 还是接着上面的例子 - -假设 设备C在设备A的子网下,C的子网IP为192.168.1.C,A的子网IP为192.168.1.A,要在设备B上访问C - -则在B上加这些参数 - -- --vnt-mapping "tcp:port2-192.168.1.C:80" (将本地port2端口映射到C的80端口) -- -i 192.168.1.0/24,10.26.0.A (将目标192.168.1.0/24的数据发送到10.26.0.A,也就是A节点) - -在A上加参数 - -- -o 0.0.0.0/0 (允许所有流量转发) - -***再次说明,vn-link作为被访问端时和vnt使用方式一致,vn-link作为访问端时需要加--vnt-mapping映射端口*** - -***vn-link是基于端口映射的使用模式,不会改变本地路由*** - -## 五、参数介绍 - ---vnt-mapping支持udp/tcp,例如 --vnt-mapping "tcp:port1-remoteIp:remotePort" - -- 第一部分为协议,支持使用udp/tcp -- 第二部分是本地端口,注意不要和本地服务的端口冲突 -- 第三部分是目标机器的地址,一般是目标虚拟IP地址,如果配置了点对网参数(-i和-o)则也可以是目标子网地址 - - - - - diff --git a/vn-link-cli/build.rs b/vn-link-cli/build.rs deleted file mode 100644 index 4d63de1..0000000 --- a/vn-link-cli/build.rs +++ /dev/null @@ -1,5 +0,0 @@ -fn main() { - // 配置 thunk-rs 来链接 Windows 7 兼容库,并自动设置链接参数 - #[cfg(target_os = "windows")] - thunk::thunk(); -} diff --git a/vn-link-cli/src/main.rs b/vn-link-cli/src/main.rs deleted file mode 100644 index e374033..0000000 --- a/vn-link-cli/src/main.rs +++ /dev/null @@ -1,89 +0,0 @@ -use common::callback; -use vn_link::config::VnLinkConfig; -use vn_link::vnt::core::Config; - -fn main() { - let (config, vnt_link_config, cmd) = match common::cli::parse_args_config() { - Ok(rs) => { - if let Some(rs) = rs { - rs - } else { - return; - } - } - Err(e) => { - log::error!( - "parse error={:?} cmd={:?}", - e, - std::env::args().collect::>() - ); - println!("Error {:?}", e); - return; - } - }; - let vnt_link_config = VnLinkConfig::new(vn_link::config::convert(vnt_link_config).unwrap()); - main0(config, vnt_link_config, cmd) -} - -#[tokio::main] -async fn main0(config: Config, vn_link_config: VnLinkConfig, _show_cmd: bool) { - #[cfg(feature = "port_mapping")] - for (is_tcp, addr, dest) in config.port_mapping_list.iter() { - if *is_tcp { - println!("TCP port mapping {}->{}", addr, dest) - } else { - println!("UDP port mapping {}->{}", addr, dest) - } - } - for x in &vn_link_config.mapping { - if x.protocol.is_tcp() { - println!("TCP vnt addr mapping 127.0.0.1:{}->{}", x.src_port, x.dest) - } else { - println!("UDP vnt addr mapping 127.0.0.1:{}->{}", x.src_port, x.dest) - } - } - - let vnt_util = match vn_link::VnLink::new(config, vn_link_config, callback::VntHandler {}).await - { - Ok(vnt) => vnt, - Err(e) => { - println!("error: {:?}", e); - std::process::exit(1); - } - }; - - #[cfg(feature = "command")] - { - let vnt_c = vnt_util.as_vnt().clone(); - std::thread::Builder::new() - .name("CommandServer".into()) - .spawn(move || { - if let Err(e) = common::command::server::CommandServer::new().start(vnt_c) { - log::warn!("cmd:{:?}", e); - } - }) - .expect("CommandServer"); - let vnt_c = vnt_util.as_vnt(); - if _show_cmd { - use tokio::io::AsyncBufReadExt; - let mut cmd = String::new(); - let mut reader = tokio::io::BufReader::new(tokio::io::stdin()); - loop { - cmd.clear(); - println!("======== input:list,info,route,all,stop,chart_a,chart_b[:ip] ========"); - match reader.read_line(&mut cmd).await { - Ok(len) => { - if !common::command::command_str(&cmd[..len], vnt_c) { - break; - } - } - Err(e) => { - println!("input err:{}", e); - break; - } - } - } - } - } - vnt_util.wait().await -} diff --git a/vn-link/Cargo.toml b/vn-link/Cargo.toml deleted file mode 100644 index 07e2e5d..0000000 --- a/vn-link/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -name = "vn-link" -version = "1.2.16" -edition = "2021" - -[dependencies] -lwip-rs = { git = "https://github.com/vnt-dev/lwip-rs" } -vnt = { path = "../vnt", package = "vnt", default-features = false } -log = "0.4.17" -anyhow = "1.0.82" -parking_lot = "0.12.1" - -tokio = { version = "1.37.0", features = ["full"] } -crossbeam-utils = "0.8" - -[features] -default = ["server_encrypt", "aes_gcm", "aes_cbc", "aes_ecb", "sm4_cbc", "chacha20_poly1305", "port_mapping", "lz4_compress"] -openssl = ["vnt/openssl"] -openssl-vendored = ["vnt/openssl-vendored"] -ring-cipher = ["vnt/ring-cipher"] -aes_cbc = ["vnt/aes_cbc"] -aes_ecb = ["vnt/aes_ecb"] -sm4_cbc = ["vnt/sm4_cbc"] -aes_gcm = ["vnt/aes_gcm"] -chacha20_poly1305 = ["vnt/chacha20_poly1305"] -server_encrypt = ["vnt/server_encrypt"] -port_mapping = ["vnt/port_mapping"] -lz4_compress = ["vnt/lz4_compress"] -zstd_compress = ["vnt/zstd_compress"] -upnp = ["vnt/upnp"] -ws = ["vnt/ws"] -wss = ["vnt/wss"] \ No newline at end of file diff --git a/vn-link/src/config.rs b/vn-link/src/config.rs deleted file mode 100644 index 58e2c6a..0000000 --- a/vn-link/src/config.rs +++ /dev/null @@ -1,85 +0,0 @@ -use anyhow::Context; -use std::net::SocketAddr; -use std::str::FromStr; - -#[derive(Clone, Debug)] -pub struct VnLinkConfig { - pub mapping: Vec, -} - -impl VnLinkConfig { - pub fn new(mapping: Vec) -> Self { - Self { mapping } - } -} - -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub enum LinkProtocol { - Tcp, - Udp, -} - -impl LinkProtocol { - pub fn is_tcp(&self) -> bool { - self == &LinkProtocol::Tcp - } -} - -#[derive(Copy, Clone, Debug)] -pub struct LinkItem { - pub protocol: LinkProtocol, - pub src_port: u16, - pub dest: SocketAddr, -} - -impl LinkItem { - pub fn new(protocol: LinkProtocol, src_port: u16, dest: SocketAddr) -> Self { - Self { - protocol, - src_port, - dest, - } - } -} - -pub fn convert(vec: Vec) -> anyhow::Result> { - let mut rs = Vec::with_capacity(vec.len()); - for x in vec { - let string = x.trim().to_lowercase(); - if let Some(udp_mapping) = string.strip_prefix("udp:") { - let mut split = udp_mapping.split("-"); - let bind_port = split - .next() - .with_context(|| format!("vnt-mapping error {:?},eg: udp:80-10.26.0.10:8080", x))?; - let bind_port = u16::from_str(bind_port) - .with_context(|| format!("udp_mapping error {}", bind_port))?; - let dest = split - .next() - .with_context(|| format!("vnt-mapping error {:?},eg: udp:80-10.26.0.10:8080", x))?; - let dest_addr = SocketAddr::from_str(dest) - .with_context(|| format!("udp_mapping error {}", dest))?; - rs.push(LinkItem::new(LinkProtocol::Udp, bind_port, dest_addr)); - continue; - } - if let Some(tcp_mapping) = string.strip_prefix("tcp:") { - let mut split = tcp_mapping.split("-"); - let bind_port = split - .next() - .with_context(|| format!("vnt-mapping error {:?},eg: tcp:80-10.26.0.10:8080", x))?; - let bind_port = u16::from_str(bind_port) - .with_context(|| format!("tcp_mapping error {}", bind_port))?; - let dest = split - .next() - .with_context(|| format!("vnt-mapping error {:?},eg: tcp:80-10.26.0.10:8080", x))?; - let dest_addr = SocketAddr::from_str(dest) - .with_context(|| format!("tcp_mapping error {}", dest))?; - rs.push(LinkItem::new(LinkProtocol::Tcp, bind_port, dest_addr)); - continue; - } - Err(anyhow::anyhow!( - "vnt-mapping error {:?},eg: tcp:80-10.26.0.10:8080", - x - ))?; - } - Ok(rs) -} diff --git a/vn-link/src/in_mapping/mod.rs b/vn-link/src/in_mapping/mod.rs deleted file mode 100644 index f7a6a76..0000000 --- a/vn-link/src/in_mapping/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod tcp; -pub mod udp; diff --git a/vn-link/src/in_mapping/tcp.rs b/vn-link/src/in_mapping/tcp.rs deleted file mode 100644 index d841469..0000000 --- a/vn-link/src/in_mapping/tcp.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::out_mapping::tcp::tcp_copy; -use crossbeam_utils::atomic::AtomicCell; -use lwip_rs::tcp_stream::TcpStream as LwIpTcpStream; -use std::net::{IpAddr, SocketAddr}; -use std::sync::Arc; -use std::time::Duration; -use tokio::net::TcpListener; -use vnt::handle::CurrentDeviceInfo; - -pub async fn tcp_mapping_listen( - tcp_listener: TcpListener, - current_device: Arc>, - dest: SocketAddr, -) { - loop { - let (stream, addr) = match tcp_listener.accept().await { - Ok((stream, addr)) => (stream, addr), - Err(e) => { - log::warn!("tcp_mapping_listen {:?} dest {}", e, dest); - continue; - } - }; - let current_info = current_device.load(); - if current_info.virtual_ip.is_unspecified() { - continue; - } - if let IpAddr::V4(ip) = dest.ip() { - if ip == current_info.virtual_ip { - //防止用错参数的 - log::warn!("目的地址不能是本地虚拟ip tcp->{}", dest); - continue; - } - } - let src = SocketAddr::new(IpAddr::V4(current_info.virtual_ip), addr.port()); - tokio::spawn(async move { - match LwIpTcpStream::connect(src, dest, Duration::from_secs(5)).await { - Ok(lw_tcp) => { - tcp_copy(lw_tcp, stream); - } - Err(e) => { - log::warn!("{} {}->{} {}", addr, src, dest, e); - } - }; - }); - } -} diff --git a/vn-link/src/in_mapping/udp.rs b/vn-link/src/in_mapping/udp.rs deleted file mode 100644 index b7ac232..0000000 --- a/vn-link/src/in_mapping/udp.rs +++ /dev/null @@ -1,63 +0,0 @@ -use std::collections::HashMap; -use std::net::{IpAddr, SocketAddr}; -use std::sync::Arc; -use std::time::Instant; - -use crossbeam_utils::atomic::AtomicCell; -use parking_lot::Mutex; -use tokio::net::UdpSocket; - -use lwip_rs::udp::UdpSocketWrite; -use vnt::handle::CurrentDeviceInfo; - -pub async fn udp_mapping_start( - udp: UdpSocket, - lwip_udp_write: UdpSocketWrite, - current_device: Arc>, - in_udp_map: &Arc< - Mutex< - HashMap< - (SocketAddr, SocketAddr), - (Arc, Option, Arc>), - >, - >, - >, - - dest: SocketAddr, -) { - let udp = Arc::new(udp); - let mut buf = [0u8; 65536]; - loop { - let (len, addr) = match udp.recv_from(&mut buf).await { - Ok(rs) => rs, - Err(e) => { - log::warn!("recv_from {} {}", dest, e); - continue; - } - }; - let current_info = current_device.load(); - if current_info.virtual_ip.is_unspecified() { - continue; - } - if let IpAddr::V4(ip) = dest.ip() { - if ip == current_info.virtual_ip { - //防止用错参数的 - log::warn!("目的地址不能是本地虚拟ip udp->{}", dest); - continue; - } - } - let src = SocketAddr::new(IpAddr::V4(current_info.virtual_ip), addr.port()); - in_udp_map.lock().insert( - (dest, src), - ( - udp.clone(), - Some(addr), - Arc::new(AtomicCell::new(Instant::now())), - ), - ); - - if let Err(e) = lwip_udp_write.send(&buf[..len], &src, &dest) { - log::warn!("lwip_udp_write {}->{} {}", src, dest, e); - } - } -} diff --git a/vn-link/src/lib.rs b/vn-link/src/lib.rs deleted file mode 100644 index 990bdb4..0000000 --- a/vn-link/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub mod config; -mod in_mapping; -mod out_mapping; -mod vnt_link; - -pub use vnt; - -pub use vnt_link::*; diff --git a/vn-link/src/out_mapping/mod.rs b/vn-link/src/out_mapping/mod.rs deleted file mode 100644 index f7a6a76..0000000 --- a/vn-link/src/out_mapping/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod tcp; -pub mod udp; diff --git a/vn-link/src/out_mapping/tcp.rs b/vn-link/src/out_mapping/tcp.rs deleted file mode 100644 index 7f3e9f6..0000000 --- a/vn-link/src/out_mapping/tcp.rs +++ /dev/null @@ -1,64 +0,0 @@ -use std::net::{IpAddr, Ipv4Addr}; -use std::sync::Arc; - -use crossbeam_utils::atomic::AtomicCell; -use tokio::net::TcpStream; - -use lwip_rs::tcp_listener::TcpListener; -use lwip_rs::tcp_stream::TcpStream as LwIpTcpStream; -use vnt::handle::CurrentDeviceInfo; - -pub async fn tcp_mapping_listen( - mut tcp_listener: TcpListener, - current_device: Arc>, -) { - loop { - let stream = match tcp_listener.accept().await { - Ok(stream) => stream, - Err(e) => { - log::warn!("tcp_mapping_listen err {:?}", e); - break; - } - }; - let device_info = current_device.load(); - tokio::spawn(async move { - let dest = stream.dest_addr(); - let src = stream.src_addr(); - if let Err(e) = tcp_mapping_handle(stream, device_info).await { - log::warn!("tcp_mapping_handle {}->{} {:?}", src, dest, e) - } - }); - } -} - -async fn tcp_mapping_handle( - tcp_stream: LwIpTcpStream, - device_info: CurrentDeviceInfo, -) -> anyhow::Result<()> { - let mut dest = tcp_stream.dest_addr(); - // let src = tcp_stream.src_addr(); - if let IpAddr::V4(ip) = dest.ip() { - if ip.is_unspecified() - || ip.is_broadcast() - || ip.is_multicast() - || ip == device_info.virtual_ip - || ip == device_info.broadcast_ip - { - //是自己 - dest.set_ip(IpAddr::V4(Ipv4Addr::LOCALHOST)); - } - } - let peer_stream = TcpStream::connect(dest).await?; - if dest.port() == peer_stream.local_addr()?.port() { - return Err(anyhow::anyhow!("tcp port loop")); - } - tcp_copy(tcp_stream, peer_stream); - Ok(()) -} - -pub(crate) fn tcp_copy(lw_tcp: LwIpTcpStream, tokio_tcp: TcpStream) { - let (mut write, mut read) = lw_tcp.into_split(); - let (mut peer_read, mut peer_write) = tokio_tcp.into_split(); - tokio::spawn(async move { tokio::io::copy(&mut read, &mut peer_write).await }); - tokio::spawn(async move { tokio::io::copy(&mut peer_read, &mut write).await }); -} diff --git a/vn-link/src/out_mapping/udp.rs b/vn-link/src/out_mapping/udp.rs deleted file mode 100644 index 04c030c..0000000 --- a/vn-link/src/out_mapping/udp.rs +++ /dev/null @@ -1,137 +0,0 @@ -use crossbeam_utils::atomic::AtomicCell; -use lwip_rs::udp::{UdpSocketRead, UdpSocketWrite}; -use parking_lot::Mutex; -use std::collections::HashMap; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; -use std::sync::Arc; -use std::time::{Duration, Instant}; -use tokio::net::UdpSocket; -use vnt::handle::CurrentDeviceInfo; - -pub async fn udp_mapping_start( - lwip_udp_write: UdpSocketWrite, - mut lwip_udp_read: UdpSocketRead, - current_device: Arc>, - in_udp_map: Arc< - Mutex< - HashMap< - (SocketAddr, SocketAddr), - (Arc, Option, Arc>), - >, - >, - >, -) { - loop { - let (buf, src, dest) = match lwip_udp_read.recv().await { - Ok(rs) => rs, - Err(e) => { - log::warn!("udp_mapping err {:?}", e); - break; - } - }; - if let Err(e) = handle( - ¤t_device, - &lwip_udp_write, - &in_udp_map, - buf, - src, - dest, - ) - .await - { - log::warn!("udp_mapping err {}->{} {:?}", src, dest, e) - } - } -} - -async fn handle( - current_device: &AtomicCell, - lwip_udp_write: &UdpSocketWrite, - map: &Arc< - Mutex< - HashMap< - (SocketAddr, SocketAddr), - (Arc, Option, Arc>), - >, - >, - >, - buf: Vec, - src: SocketAddr, - dest: SocketAddr, -) -> anyhow::Result<()> { - let option = map.lock().get(&(src, dest)).cloned(); - - if let Some((dest_udp, addr, time)) = option { - time.store(Instant::now()); - if let Some(addr) = addr { - dest_udp.send_to(&buf, addr).await?; - } else { - dest_udp.send(&buf).await?; - } - } else { - let mut real_dest = dest; - let peer_udp_socket = match UdpSocket::bind(format!("0.0.0.0:{}", src.port())).await { - Ok(udp) => udp, - Err(_) => UdpSocket::bind("0.0.0.0:0").await?, - }; - if let IpAddr::V4(ip) = dest.ip() { - let device_info = current_device.load(); - if ip.is_unspecified() - || ip.is_broadcast() - || ip.is_multicast() - || ip == device_info.virtual_ip - || ip == device_info.broadcast_ip - { - //是自己 - real_dest.set_ip(IpAddr::V4(Ipv4Addr::LOCALHOST)); - } - } - peer_udp_socket.connect(real_dest).await?; - peer_udp_socket.send(&buf).await?; - let peer_udp_socket = Arc::new(peer_udp_socket); - let time = Arc::new(AtomicCell::new(Instant::now())); - let map = map.clone(); - map.lock() - .insert((src, dest), (peer_udp_socket.clone(), None, time.clone())); - let lwip_udp_write = lwip_udp_write.clone(); - tokio::spawn(async move { - peer_udp_handle(peer_udp_socket, lwip_udp_write, src, dest, time).await; - map.lock().remove(&(src, dest)); - }); - } - Ok(()) -} - -async fn peer_udp_handle( - peer_udp_socket: Arc, - lwip_udp_write: UdpSocketWrite, - src: SocketAddr, - dest: SocketAddr, - time: Arc>, -) { - let mut buf = [0u8; 65536]; - loop { - match tokio::time::timeout(Duration::from_secs(600), peer_udp_socket.recv(&mut buf)).await { - Ok(rs) => match rs { - Ok(len) => match lwip_udp_write.send(&buf[..len], &dest, &src) { - Ok(_) => {} - Err(e) => { - log::warn!("udp proxy {}->{} {:?}", dest, src, e); - break; - } - }, - Err(e) => { - log::warn!("udp proxy {}->{} {:?}", dest, src, e); - break; - } - }, - Err(_) => { - if time.load().elapsed() > Duration::from_secs(580) { - //超时关闭 - log::warn!("udp proxy timeout {}->{}", dest, src,); - break; - } - } - } - } -} diff --git a/vn-link/src/vnt_link/mod.rs b/vn-link/src/vnt_link/mod.rs deleted file mode 100644 index c009efd..0000000 --- a/vn-link/src/vnt_link/mod.rs +++ /dev/null @@ -1,217 +0,0 @@ -use std::collections::HashMap; -use std::net::SocketAddr; -use std::sync::Arc; -use std::time::Instant; - -use anyhow::Context; -use crossbeam_utils::atomic::AtomicCell; -use parking_lot::Mutex; -use tokio::net::{TcpListener, UdpSocket}; -use tokio::sync::watch::{channel, Sender}; - -use lwip_rs::stack::{NetStack, NetStackWrite}; -use lwip_rs::tcp_listener::TcpListener as LwIPTcpListener; -use lwip_rs::udp::{UdpSocket as LwIpUdpSocket, UdpSocketWrite}; -use vnt::channel::BUFFER_SIZE; -use vnt::core::{Config, Vnt}; -use vnt::packet::ip::ipv4::packet::IpV4Packet; -use vnt::protocol::HEAD_LEN; -use vnt::vnt_device::DeviceWrite; -use vnt::VntCallback; - -use crate::config::{LinkItem, LinkProtocol, VnLinkConfig}; -use crate::{in_mapping, out_mapping}; - -pub struct VnLink { - vnt: Vnt, - in_udp_map: Arc< - Mutex< - HashMap< - (SocketAddr, SocketAddr), - (Arc, Option, Arc>), - >, - >, - >, - lwip_udp_write: UdpSocketWrite, - shutdown_tx: Sender, -} - -impl VnLink { - pub async fn new( - vnt_config: Config, - vn_link_config: VnLinkConfig, - callback: Call, - ) -> anyhow::Result { - let stack = NetStack::new(HEAD_LEN, 1024, vnt_config.mtu.unwrap_or(1420) as u16).await; - let udp = LwIpUdpSocket::new()?; - let tcp_listener = LwIPTcpListener::new()?; - let (shutdown_tx, shutdown_rx) = channel(false); - let (net_stack_write, mut net_stack_read) = stack.into_split(); - let vnt = Vnt::new_device(vnt_config, callback, VntDevice { net_stack_write })?; - let shutdown_tx_ = shutdown_tx.clone(); - let w = vnt.add_stop_listener("vnt-link".into(), move || { - let _ = shutdown_tx_.send(true); - })?; - let ip_sender = vnt.ipv4_packet_sender().unwrap(); - let mut shutdown_rx_ = shutdown_rx.clone(); - tokio::spawn(async move { - let mut extend = [0; BUFFER_SIZE]; - loop { - tokio::select! { - _ = shutdown_rx_.changed() => { - break; - } - rs = net_stack_read.recv_ip() => { - match rs{ - Ok((mut buf, start_index, len)) => { - let ipv4_packet = if let Ok(packet) = - IpV4Packet::new(&buf[start_index..len]) - { - packet - } else { - continue; - }; - let destination_ip = ipv4_packet.destination_ip(); - let source_ip = ipv4_packet.source_ip(); - - if let Err(e) = ip_sender.send_ip(&mut buf, len, &mut extend, destination_ip) { - log::warn!("{}->{},{}", source_ip, destination_ip, e); - } - }, - Err(e) => { - log::error!("net_stack_read {:?}", e); - break; - } - }; - } - } - } - w.stop_all(); - }); - - let (lwip_udp_write, lwip_udp_read) = udp.into_split(); - let in_udp_map: Arc< - Mutex< - HashMap< - (SocketAddr, SocketAddr), - (Arc, Option, Arc>), - >, - >, - > = Arc::new(Mutex::new(HashMap::new())); - - let current_device_info = vnt.current_device_info(); - let in_udp_map_ = in_udp_map.clone(); - let lwip_udp_write_ = lwip_udp_write.clone(); - let vnt_ = vnt.clone(); - let mut shutdown_rx_ = shutdown_rx.clone(); - tokio::spawn(async move { - tokio::select! { - _ = shutdown_rx_.changed() => {} - _ = out_mapping::udp::udp_mapping_start( - lwip_udp_write_, - lwip_udp_read, - current_device_info, - in_udp_map_, - ) => {} - } - - vnt_.stop(); - }); - let current_device_info = vnt.current_device_info(); - let vnt_ = vnt.clone(); - let mut shutdown_rx_ = shutdown_rx.clone(); - - tokio::spawn(async move { - tokio::select! { - _ = shutdown_rx_.changed() => {} - _ = out_mapping::tcp::tcp_mapping_listen(tcp_listener, current_device_info) => {} - } - vnt_.stop(); - }); - let link = Self { - vnt, - in_udp_map, - lwip_udp_write, - shutdown_tx, - }; - link.add_mapping(vn_link_config.mapping).await?; - Ok(link) - } - pub async fn add_mapping(&self, mapping: Vec) -> anyhow::Result<()> { - for item in mapping { - let current_device_info = self.vnt.current_device_info(); - if item.dest.ip().is_unspecified() { - Err(anyhow::anyhow!("dest_address {:?} is_unspecified", item))? - } - let mut shutdown_rx_ = self.shutdown_tx.subscribe(); - if *shutdown_rx_.borrow() { - Err(anyhow::anyhow!("mapping stop"))? - } - if item.protocol == LinkProtocol::Udp { - let lwip_udp_write = self.lwip_udp_write.clone(); - let in_udp_map = self.in_udp_map.clone(); - //只能本机访问,不然不同IP的相同来源端口会有问题 - let udp = UdpSocket::bind(format!("127.0.0.1:{}", item.src_port)) - .await - .with_context(|| format!("udp bind failed {}", item.src_port))?; - tokio::spawn(async move { - tokio::select! { - _ = shutdown_rx_.changed() => {} - _ = in_mapping::udp::udp_mapping_start( - udp, - lwip_udp_write, - current_device_info, - &in_udp_map, - item.dest, - ) => {} - } - }); - } else { - let listener = TcpListener::bind(format!("127.0.0.1:{}", item.src_port)) - .await - .with_context(|| format!("tcp bind failed {}", item.src_port))?; - tokio::spawn(async move { - tokio::select! { - _ = shutdown_rx_.changed() => {} - _ = in_mapping::tcp::tcp_mapping_listen( - listener, - current_device_info, - item.dest, - ) => {} - } - }); - } - } - Ok(()) - } - pub fn stop(&self) { - self.as_vnt().stop() - } - pub async fn wait(&self) { - loop { - let mut receiver = self.shutdown_tx.subscribe(); - if *receiver.borrow() { - return; - } - if receiver.changed().await.is_err() { - return; - } - } - } - - pub fn as_vnt(&self) -> &Vnt { - &self.vnt - } -} - -#[derive(Clone)] -pub struct VntDevice { - net_stack_write: NetStackWrite, -} - -impl DeviceWrite for VntDevice { - fn write(&self, buf: &[u8]) -> std::io::Result { - self.net_stack_write.send_ip(buf)?; - Ok(buf.len()) - } -} diff --git a/vnt-cli/Cargo.toml b/vnt-cli/Cargo.toml deleted file mode 100644 index 4de7503..0000000 --- a/vnt-cli/Cargo.toml +++ /dev/null @@ -1,51 +0,0 @@ -[package] -name = "vnt-cli" -version = "1.2.16" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -vnt = { path = "../vnt", package = "vnt", default-features = false, features = ["integrated_tun"] } -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" -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"] - -openssl = ["vnt/openssl", "common/openssl"] -openssl-vendored = ["vnt/openssl-vendored", "common/openssl-vendored"] -ring-cipher = ["vnt/ring-cipher", "common/ring-cipher"] -aes_cbc = ["vnt/aes_cbc", "common/aes_cbc"] -aes_ecb = ["vnt/aes_ecb", "common/aes_ecb"] -sm4_cbc = ["vnt/sm4_cbc", "common/sm4_cbc"] -aes_gcm = ["vnt/aes_gcm", "common/aes_gcm"] -chacha20_poly1305 = ["vnt/chacha20_poly1305", "common/chacha20_poly1305"] -server_encrypt = ["vnt/server_encrypt", "common/server_encrypt"] -port_mapping = ["vnt/port_mapping", "common/port_mapping"] -lz4 = ["vnt/lz4_compress", "common/lz4"] -zstd = ["vnt/zstd_compress", "common/zstd"] -ip_proxy = ["vnt/ip_proxy", "common/ip_proxy"] -upnp = ["vnt/upnp", "common/upnp"] -ws = ["vnt/ws", "common/ws"] -wss = ["vnt/wss", "common/wss"] -log = ["common/log"] -command = ["common/command"] -file_config = ["common/file_config"] -[build-dependencies] -rand = "0.8.5" -chrono = "0.4.23" - -[target.'cfg(windows)'.build-dependencies] -thunk-rs = { version = "0.3.3", features = ["win7"] } diff --git a/vnt-cli/README.md b/vnt-cli/README.md deleted file mode 100644 index d6b74ee..0000000 --- a/vnt-cli/README.md +++ /dev/null @@ -1,256 +0,0 @@ -## 模块介绍 - -体积小,可以在服务器、路由器等环境使用 - -## 详细参数说明 - -### -k `` - -一个虚拟局域网的标识,在同一服务器下,相同token的设备会组建一个局域网 - -### -n `` - -设备名称,方便区分不同设备 - -### -d `` - -设备id,每台设备的唯一标识,注意不要重复 - -### -c - -关闭控制台交互式命令,后台运行时可以加此参数 - -### -s `` - -注册和中继服务器地址,注册和转发数据,以'TXT:'开头表示解析TXT记录,TXT记录内容必须是'host:port'形式的服务器地址 - -### -e `` - -使用stun服务探测客户端NAT类型,不同类型有不同的打洞策略 - -### -a - -加了此参数表示使用tap网卡,默认使用tun网卡,tun网卡效率更高 - -注意:仅在windows上支持使用tap,用于兼容低版本windows系统(低版本windows不支持wintun) - -使用tap模式需要手动创建tap网卡,使用--nic参数指定已经创建好的tap网卡名称 - -### --nic `` - -指定虚拟网卡名称,默认tun模式使用vnt-tun,tap模式使用vnt-tap - -### -i ``、-o `` - -配置点对网(IP代理)时使用,例如A(虚拟ip:10.26.0.2)通过B(虚拟ip:10.26.0.3,本地出口ip:192.168.0.10)访问C( -目标网段192.168.0.0/24), - -则在A配置 **'-i 192.168.0.0/24,10.26.0.3'** ,表示将192.168.0.0/24网段的数据都转发到10.26.0.3节点 - -在B配置 **'-o 192.168.0.0/24'** ,表示允许将数据转发到 192.168.0.0/24 ,允许转发所有网段可以使用 **'-o 0.0.0.0/0'** - --i和-o参数均可使用多次,来指定不同网段,例如 **'-o 192.168.1.0/24 -o 192.168.2.0/24'** -表示允许转发目标为192.168.1.0/24或192.168.2.0/24这两个网段的数据 - -### -w `` - -提升通信安全性,使用该密码生成的密钥对客户端数据进行加密,并且服务端无法解密(包括中继数据)。使用相同密码的客户端才能通信 - -| 密码位数 | 加密算法 | -|-------|------------| -| 小于8 | AES128-GCM | -| 大于等于8 | AES256-GCM | - -### -W - -开启和服务端通信的数据加密,采用rsa+aes256gcm加密客户端和服务端之间通信的数据,可以避免token泄漏、中间人攻击 - -注意: - -1. -w ``是用于客户端-客户端之间的加密,password不会传递到服务端,只添加这个参数不会加密客户端-服务端通信的数据 -2. -W 用于开启客户端-服务端之间的加密 - -### -u `` - -设置虚拟网卡的mtu值,大多数情况下使用默认值效率会更高,也可根据实际情况微调这个值,不加密默认为1450,加密默认为1410 - -### ~~--tcp~~ - -~~和服务端使用tcp通信。有些网络提供商对UDP限制比较大,这个时候可以选择使用TCP模式,提高稳定性。一般来说udp延迟和消耗更低~~ - -新版本使用 `-s tcp://`的形式使用tcp - -### --ip `` - -指定虚拟ip,指定的ip不能和其他设备重复,必须有效并且在服务端所属网段下,默认情况由服务端分配 - -### --par `` - -任务并行度(必须为正整数),默认值为1,该值表示处理网卡读写的任务数,组网设备数较多、处理延迟较大时可适当调大此值 - -### --model `` - -加密模式,可选值 -aes_gcm/aes_cbc/aes_ecb/sm4_cbc/chacha20_poly1305/chacha20/xor,默认使用aes_gcm,通常情况aes_gcm和chacha20_poly1305安全性高。 -各种加密模式的安全性和速度都不相同,请按需选取 - -特别说明:xor只是对数据进行简单异或,仅仅避免了明文传输,安全性很差,同时对性能影响也极小; - -| 密码位数 | model | 加密算法 | -|--------|-------------------|-------------------| -| `< 8` | aes_gcm | AES128-GCM | -| `>= 8` | aes_gcm | AES256-GCM | -| `< 8` | aes_cbc | AES128-CBC | -| `>= 8` | aes_cbc | AES256-CBC | -| `< 8` | aes_ecb | AES128-ECB | -| `>= 8` | aes_ecb | AES256-ECB | -| `> 0` | sm4_cbc | SM4-CBC | -| `> 0` | chacha20_poly1305 | ChaCha20-Poly1305 | -| `> 0` | chacha20 | ChaCha20 | -| `> 0` | xor | 简单异或混淆 | - -### --finger - -开启数据指纹校验,可增加安全性,如果服务端开启指纹校验,则客户端也必须开启,开启会损耗一部分性能 - -注意:默认情况下服务端不会对中转的数据做校验,如果要对中转的数据做校验,则需要客户端、服务端都开启此参数 - -### --punch `` - -取值ipv4/ipv6,选择只使用ipv4打洞或者只使用ipv6打洞,默认两者都会使用 - -### --ports `` - -指定本地监听的端口组,多个端口使用逗号分隔,多个端口可以分摊流量,增加并发、减缓流量限制,tcp会监听端口组的第一个端口,用于tcp直连 - -- 例1:‘--ports 12345,12346,12347’ 表示udp监听12345、12346、12347这三个端口,tcp监听12345端口 -- 例2:‘--ports 0,0’ 表示udp监听两个未使用的端口,tcp监听一个未使用的端口 - -### --cmd - -开启交互式命令,开启后可以直接在窗口下输入命令,如需后台运行请勿开启 - -### --first_latency - -优先使用低延迟通道,默认情况下优先使用p2p通道,某些情况下可能p2p比客户端中继延迟更高,可使用此参数进行优化传输 - -### --no-proxy - -关闭内置的ip代理,内置的代理较为简单,而且一般来说直接使用网卡NAT转发性能会更高, -有需要可以自行配置NAT转发,[可参考‘编译’小节中的NAT配置](https://github.com/vnt-dev/vnt#%E7%BC%96%E8%AF%91) - -### --dns `<223.5.5.5>` - -设置域名解析服务器地址,可以设置多个。如果使用TXT记录的域名,则dns默认使用223.5.5.5和114.114.114.114,端口省略值为53 - -当地址解析失败时,会依次尝试后面的dns,直到有A记录、AAAA记录(或TXT记录)的解析结果 - -### --mapping `` - -端口映射,可以设置多个映射地址,例如 '--mapping udp:0.0.0.0:80-10.26.0.10:80 --mapping tcp:0.0.0.0:80-10.26.0.11:81' -表示将本地udp 80端口的数据转发到10.26.0.10:80,将本地tcp 80端口的数据转发到10.26.0.11:81,转发的目的地址可以使用域名+端口 - -### --compressor `` - -启用压缩,默认仅支持lz4压缩,开启压缩后,如果数据包长度大于等于128,则会使用压缩,否则还是会按原数据发送 - -也支持开启zstd压缩,但是需要自行编译,编译时加入参数--features zstd - -如果宽度速度比较慢,可以考虑使用高级别的压缩 - -### -f `` - -指定配置文件 -配置文件采用yaml格式,可参考: - -```yaml -# 全部参数 -tap: false #是否使用tap 仅在windows上支持使用tap -token: xxx #组网token -device_id: xxx #当前设备id -name: windows 11 #当前设备名称 -server_address: ip:port #注册和中继服务器 -stun_server: #stun服务器 - - stun1.l.google.com:19302 - - stun2.l.google.com:19302 -in_ips: #代理ip入站 - - 192.168.1.0/24,10.26.0.3 -out_ips: #代理ip出站 - - 0.0.0.0/0 -password: xxx #密码 -mtu: 1420 #mtu -tcp: false #tcp模式 -ip: 10.26.0.2 #指定虚拟ip -use_channel: relay #relay:仅中继模式.p2p:仅直连模式 -server_encrypt: true #服务端加密 -parallel: 1 #任务并行度 -cipher_model: aes_gcm #客户端加密算法 -finger: false #关闭数据指纹 -punch_model: ipv4 #打洞模式,表示只使用ipv4地址打洞,默认会同时使用v6和v4 -ports: - - 0 #使用随机端口,tcp监听此端口 - - 0 -cmd: false #关闭控制台输入 -no_proxy: false #是否关闭内置代理,true为关闭 -first_latency: false #是否优先低延迟通道,默认为false,表示优先使用p2p通道 -device_name: vnt-tun #网卡名称 -packet_loss: 0 #指定丢包率 取值0~1之间的数 用于模拟弱网 -packet_delay: 0 #指定延迟 单位毫秒 用于模拟弱网 -dns: - - 223.5.5.5 # 首选dns - - 8.8.8.8 # 备选dns -mapping: - - udp:0.0.0.0:80-10.26.0.10:80 # 映射udp数据 - - tcp:0.0.0.0:80-10.26.0.10:81 # 映射tcp数据 - - tcp:0.0.0.0:82-localhost:83 # 映射tcp数据 -disable_stats: false # 为true表示关闭统计 -allow_wire_guard: false # 为true则表示允许接入wg -``` - -或者需要哪个配置就加哪个,当然token是必须的 - -```yaml -# 部分参数 -token: xxx #组网token -``` - -### --use-channel `` - -- relay:仅中继模式,会禁止打洞/p2p直连,只使用服务器转发 -- p2p:仅直连模式,会禁止网络数据从服务器/客户端转发,只会使用服务器转发控制包 - -### --packet-loss `<0>` - -模拟丢包,取值0~1之间的小数,程序会按设定的概率主动丢包。在模拟弱网环境时会有帮助。 - -### --packet-delay `<0>` - -模拟延迟,整数,单位毫秒(ms),程序会按设定的值延迟发包,可用于模拟弱网 - -### --disable-stats - -关闭流量统计 - -### --allow-wg - -允许接入WireGuard客户端,和wg混用时必须开启此参数 - -### --list - -在后台运行时,查看其他设备列表 - -### --all - -在后台运行时,查看其他设备完整信息 - -### --info - -在后台运行时,查看当前设备信息 - -### --route - -在后台运行时,查看数据转发路径 - -### --stop - -停止后台运行 diff --git a/vnt-cli/build.rs b/vnt-cli/build.rs deleted file mode 100644 index 4d63de1..0000000 --- a/vnt-cli/build.rs +++ /dev/null @@ -1,5 +0,0 @@ -fn main() { - // 配置 thunk-rs 来链接 Windows 7 兼容库,并自动设置链接参数 - #[cfg(target_os = "windows")] - thunk::thunk(); -} diff --git a/vnt-cli/src/main.rs b/vnt-cli/src/main.rs deleted file mode 100644 index 77809b9..0000000 --- a/vnt-cli/src/main.rs +++ /dev/null @@ -1,104 +0,0 @@ -use common::callback; -use console::style; -use vnt::core::{Config, Vnt}; -mod root_check; -fn main() { - let (config, _vnt_link_config, cmd) = match common::cli::parse_args_config() { - Ok(rs) => { - if let Some(rs) = rs { - rs - } else { - return; - } - } - Err(e) => { - log::error!( - "parse error={:?} cmd={:?}", - e, - std::env::args().collect::>() - ); - println!("{}", style(format!("Error {:?}", e)).red()); - return; - } - }; - main0(config, cmd) -} -fn main0(config: Config, _show_cmd: bool) { - if !root_check::is_app_elevated() { - println!("Please run it with administrator or root privileges"); - #[cfg(any(target_os = "linux", target_os = "macos"))] - sudo::escalate_if_needed().unwrap(); - return; - } - #[cfg(feature = "port_mapping")] - for (is_tcp, addr, dest) in config.port_mapping_list.iter() { - if *is_tcp { - println!("TCP port mapping {}->{}", addr, dest) - } else { - println!("UDP port mapping {}->{}", addr, dest) - } - } - 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); - } - }; - #[cfg(any(target_os = "linux", target_os = "macos"))] - { - let vnt_c = vnt_util.clone(); - let mut signals = signal_hook::iterator::Signals::new(&[ - signal_hook::consts::SIGINT, - signal_hook::consts::SIGTERM, - ]) - .unwrap(); - let handle = signals.handle(); - std::thread::spawn(move || { - for sig in signals.forever() { - match sig { - signal_hook::consts::SIGINT | signal_hook::consts::SIGTERM => { - println!("Received SIGINT, {}", sig); - vnt_c.stop(); - handle.close(); - break; - } - _ => {} - } - } - }); - } - #[cfg(feature = "command")] - { - let vnt_c = vnt_util.clone(); - std::thread::Builder::new() - .name("CommandServer".into()) - .spawn(move || { - if let Err(e) = common::command::server::CommandServer::new().start(vnt_c) { - log::warn!("cmd:{:?}", e); - } - }) - .expect("CommandServer"); - if _show_cmd { - let mut cmd = String::new(); - loop { - cmd.clear(); - println!("======== input:list,info,route,all,stop,chart_a,chart_b[:ip] ========"); - match std::io::stdin().read_line(&mut cmd) { - Ok(len) => { - if !common::command::command_str(&cmd[..len], &vnt_util) { - break; - } - } - Err(e) => { - println!("input err:{}", e); - break; - } - } - } - } - } - - vnt_util.wait() -} diff --git a/vnt-cli/src/root_check/mod.rs b/vnt-cli/src/root_check/mod.rs deleted file mode 100644 index 3cf4643..0000000 --- a/vnt-cli/src/root_check/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -#[cfg(target_os = "windows")] -mod windows; - -#[cfg(target_os = "windows")] -pub use windows::is_app_elevated; - -#[cfg(any(target_os = "linux", target_os = "macos"))] -mod unix; - -#[cfg(any(target_os = "linux", target_os = "macos"))] -pub use unix::is_app_elevated; diff --git a/vnt-cli/src/root_check/unix.rs b/vnt-cli/src/root_check/unix.rs deleted file mode 100644 index 3f24a49..0000000 --- a/vnt-cli/src/root_check/unix.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub fn is_app_elevated() -> bool { - sudo::RunningAs::Root == sudo::check() -} diff --git a/vnt-cli/src/root_check/windows.rs b/vnt-cli/src/root_check/windows.rs deleted file mode 100644 index c3531bf..0000000 --- a/vnt-cli/src/root_check/windows.rs +++ /dev/null @@ -1,76 +0,0 @@ -/// 使用 https://github.com/spa5k/is_sudo/blob/main/src/window.rs -use std::io::Error; -use std::ptr; - -use winapi::um::handleapi::CloseHandle; -use winapi::um::processthreadsapi::{GetCurrentProcess, OpenProcessToken}; -use winapi::um::securitybaseapi::GetTokenInformation; -use winapi::um::winnt::{TokenElevation, HANDLE, TOKEN_ELEVATION, TOKEN_QUERY}; - -// Use std::io::Error::last_os_error for errors. -// NOTE: For this example I'm simple passing on the OS error. -// However, customising the error could provide more context - -/// Returns true if the current process has admin rights, otherwise false. -pub fn is_app_elevated() -> bool { - _is_app_elevated().unwrap_or(false) -} - -/// On success returns a bool indicating if the current process has admin rights. -/// Otherwise returns an OS error. -/// -/// This is unlikely to fail but if it does it's even more unlikely that you have admin permissions anyway. -/// Therefore the public function above simply eats the error and returns a bool. -fn _is_app_elevated() -> Result { - let token = QueryAccessToken::from_current_process()?; - token.is_elevated() -} - -/// A safe wrapper around querying Windows access tokens. -pub struct QueryAccessToken(HANDLE); - -impl QueryAccessToken { - pub fn from_current_process() -> Result { - unsafe { - let mut handle: HANDLE = ptr::null_mut(); - let result = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut handle); - - if result != 0 { - Ok(Self(handle)) - } else { - Err(Error::last_os_error()) - } - } - } - - /// On success returns a bool indicating if the access token has elevated privilidges. - /// Otherwise returns an OS error. - pub fn is_elevated(&self) -> Result { - unsafe { - let mut elevation = TOKEN_ELEVATION::default(); - let size = std::mem::size_of::() as u32; - let mut ret_size = size; - // The weird looking repetition of `as *mut _` is casting the reference to a c_void pointer. - if GetTokenInformation( - self.0, - TokenElevation, - &mut elevation as *mut _ as *mut _, - size, - &mut ret_size, - ) != 0 - { - Ok(elevation.TokenIsElevated != 0) - } else { - Err(Error::last_os_error()) - } - } - } -} - -impl Drop for QueryAccessToken { - fn drop(&mut self) { - if !self.0.is_null() { - unsafe { CloseHandle(self.0) }; - } - } -} diff --git a/vnt-core/Cargo.toml b/vnt-core/Cargo.toml new file mode 100644 index 0000000..69db3bf --- /dev/null +++ b/vnt-core/Cargo.toml @@ -0,0 +1,58 @@ +[package] +name = "vnt-core" +version = "2.0.0" +edition = "2024" + +[dependencies] + + +tokio = { version = "1", features = ["full"] } +tokio-util = { version = "0.7", features = ["codec"] } +futures = "0.3" + +tun-rs = { version = "2", features = ["async", "async_framed"] } + +rust-p2p-core = { version="0.4" } +tcp_ip = { git = "https://github.com/rustp2p/tcp_ip" } + +anyhow = "1" +parking_lot = "0.12" + +quinn = { version = "0.11", default-features = false, features = ["rustls", "runtime-tokio"] } + +tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] } +rustls = { version = "0.23", default-features = false, features = ["ring"] } +hex = "0.4" +sha2 = "0.10" +rustls-native-certs = "0.8.2" +log = "0.4" +bytes = "1.11.0" +rand = "0.9" +time = { version = "0.3", features = ["macros", "formatting", "local-offset"] } +pnet_packet = "0.35" +ring = "0.17.14" + +prost = "0.14" +tokio-tungstenite = "0.28.0" +tungstenite = "0.28.0" +uuid = { version = "1.18.1", features = ["v4"] } +ipnet = { version = "2.11", features = ["serde"] } +getifaddrs = "0.6.0" +dns-parser = "0.8" + +lz4_flex = "0.12.0" +reed-solomon-erasure = "6.0" + +serde = { version = "1.0.228", features = ["derive"] } +rcgen = "0.14.6" +machine-uid = "0.5.4" + +zerocopy = { version = "0.8.31", features = ["derive"] } +socket2 = { version = "0.6.1", features = ["all"] } + +[target.'cfg(target_os = "windows")'.dependencies] +winapi = { version = "0.3.9", features = ["winreg"] } +widestring = "1.2" + +[build-dependencies] +prost-build = "0.14" diff --git a/vnt-core/build.rs b/vnt-core/build.rs new file mode 100644 index 0000000..b31a0f5 --- /dev/null +++ b/vnt-core/build.rs @@ -0,0 +1,15 @@ +fn main() { + let mut config = prost_build::Config::new(); + config.protoc_arg("--experimental_allow_proto3_optional"); + config + .compile_protos( + &[ + "proto/control_message.proto", + "proto/rpc.proto", + "proto/client.proto", + "proto/fec.proto", + ], + &["proto"], + ) + .unwrap(); +} diff --git a/vnt-core/proto/client.proto b/vnt-core/proto/client.proto new file mode 100644 index 0000000..f5d9515 --- /dev/null +++ b/vnt-core/proto/client.proto @@ -0,0 +1,62 @@ +syntax = "proto3"; + +package protocol.client; + +message QuicProxyHandshake{ + oneof handshake{ + TcpProxyHandshake tcp = 1; + IpProxyHandshake ip = 2; + PortProxyHandshake tcp_port_mapping = 3; + PortProxyHandshake udp_port_mapping = 4; + } + +} + + +message TcpProxyHandshake{ + fixed32 src_ip = 1; + uint32 src_port = 2; + fixed32 dst_ip = 3; + uint32 dst_port = 4; +} +message PortProxyHandshake{ + string src_ip = 1; + uint32 src_port = 2; + string dst_host = 3; + uint32 dst_port = 4; +} + +message IpProxyHandshake{ + uint32 ip_next_header_protocol = 1; + fixed32 src_ip = 2; + fixed32 dst_ip = 3; +} + +enum NatType { + NAT_TYPE_CONE = 0; + NAT_TYPE_SYMMETRIC = 1; +} +message NatInfo { + + NatType nat_type = 1; + + repeated fixed32 public_ips = 2; + + repeated uint32 public_udp_ports = 3; + + uint32 public_port_range = 4; + + repeated fixed32 local_ipv4s = 5; + + optional bytes ipv6 = 6; + + repeated uint32 local_udp_ports = 7; + + uint32 local_tcp_port = 8; + + uint32 public_tcp_port = 9; +} + +message PunchInfo{ + NatInfo nat_info = 1; +} \ No newline at end of file diff --git a/vnt-core/proto/control_message.proto b/vnt-core/proto/control_message.proto new file mode 100644 index 0000000..a309e86 --- /dev/null +++ b/vnt-core/proto/control_message.proto @@ -0,0 +1,72 @@ +syntax = "proto3"; + +package protocol.control_message; + +enum RegistrationMode { + NORMAL = 0; + PRE_REGISTER = 1; +} + +message RegRequestMsg { + string network_code = 1; + string device_id = 2; + optional fixed32 ip = 3; + string name = 4; + string version = 5; + optional string key_sign = 6; + bool ip_variable = 7; + fixed32 server_id = 8; + RegistrationMode registration_mode = 9; +} + + +message RegResponseMsg { + fixed32 ip = 1; + uint32 prefix_len = 2; + fixed32 gateway = 3; + string server_version = 4; +} + +message ConfirmRegMsg { +} + +message ConfirmRegResponseMsg { + bool success = 1; +} + +message ErrorResponseMsg { + uint32 code = 1; + string message = 2; +} + +message RequestMessage{ + oneof request_payload{ + RegRequestMsg reg = 1; + ConfirmRegMsg confirm_reg = 2; + } +} +message ResponseMessage{ + oneof response_payload{ + RegResponseMsg reg = 1; + ErrorResponseMsg error = 2; + ConfirmRegResponseMsg confirm_reg = 3; + } +} + +message SelectiveBroadcast { + repeated fixed32 ips = 1; + bytes data = 2; +} + +message ClientSimpleInfo{ + fixed32 ip = 1; + bool online = 2; +} + +message ClientSimpleInfoList{ + uint64 data_version = 1; + repeated ClientSimpleInfo list = 2; + bool is_all = 3; + int64 time = 4; +} + diff --git a/vnt-core/proto/fec.proto b/vnt-core/proto/fec.proto new file mode 100644 index 0000000..a320f96 --- /dev/null +++ b/vnt-core/proto/fec.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package protocol.fec; + +message FecPacket { + uint64 group_id = 1; // FEC 组号(按目标IP分组) + uint32 packet_index = 2; // 包在组内的序号(0-based) + bytes payload = 3; + optional ParityData parity_data = 4; +} + + + +// 冗余包(携带FEC恢复所需的元数据) +message ParityData { + uint32 data_shards = 1; // 原始包数量 + uint32 parity_shards = 2; // 冗余包数量 +} diff --git a/vnt-core/proto/rpc.proto b/vnt-core/proto/rpc.proto new file mode 100644 index 0000000..e6077e7 --- /dev/null +++ b/vnt-core/proto/rpc.proto @@ -0,0 +1,37 @@ +syntax = "proto3"; + +package protocol.rpc; + +message RpcMessageRequest{ + uint64 id = 1; + oneof rpc_req_payload{ + ClientListRequest client_list_req = 2; + } +} + +message ClientListRequest{ + +} + + + +message RpcMessageResponse{ + uint64 id = 1; + oneof rpc_res_payload{ + ClientListResponse client_list_res = 2; + } +} + +message ClientInfo{ + string name = 1; + string version = 2; + fixed32 ip = 3; + optional string key_sign = 4; + bool online = 5; + int64 last_connected_time = 6; + string id = 7; +} + +message ClientListResponse{ + repeated ClientInfo list = 1; +} \ No newline at end of file diff --git a/vnt-core/src/api/mod.rs b/vnt-core/src/api/mod.rs new file mode 100644 index 0000000..a551b9f --- /dev/null +++ b/vnt-core/src/api/mod.rs @@ -0,0 +1,94 @@ +use crate::context::config::Config; +use crate::context::{AppState, NetworkAddr, PacketLossInfo, ServerNodeInfo, TrafficInfo}; +use crate::protocol::control_message::ClientSimpleInfo; +use crate::tunnel_core::p2p::route_table::Route; +use crate::tunnel_core::server::rpc::ServerRPC; +use rust_p2p_core::nat::NatInfo; +use std::net::Ipv4Addr; + +#[derive(Clone)] +pub struct VntApi { + app_state: AppState, + server_rpc: ServerRPC, +} + +impl VntApi { + pub(crate) fn new(app_state: AppState, server_rpc: ServerRPC) -> Self { + Self { + app_state, + server_rpc, + } + } + pub fn server_rpc(&self) -> &ServerRPC { + &self.server_rpc + } + /// 获取启动配置 + pub fn get_config(&self) -> Option> { + self.app_state.get_config() + } + /// 获取所有客户端ip + pub fn client_ips(&self) -> Vec { + self.app_state.client_ips() + } + /// 判断目标IP是否直连 + pub fn is_direct(&self, ip: &Ipv4Addr) -> bool { + self.app_state.route_table.p2p_num(ip) > 0 + } + /// 查找路由 + pub fn find_route(&self, ip: &Ipv4Addr) -> Option { + self.app_state.route_table.get_route_by_id(ip).ok() + } + pub fn get_rtt(&self, ip: &Ipv4Addr) -> Option { + if let Some(route) = self.find_route(ip) { + Some(route.rtt()) + } else { + self.server_node_rtt(ip).map(|v| v * 2) + } + } + /// 获取所有路由 + pub fn route_table(&self) -> Vec<(Ipv4Addr, Vec)> { + self.app_state.route_table.route_table() + } + /// 获取服务器节点 + pub fn server_node_list(&self) -> Vec { + self.app_state.server_info_collection.server_node_list() + } + pub fn server_node_rtt(&self, ip: &Ipv4Addr) -> Option { + self.app_state.server_info_collection.get_server_rtt(ip) + } + /// 获取网络配置 + pub fn network(&self) -> Option { + self.app_state.get_network() + } + /// 获取当前的nat信息 + pub fn nat_info(&self) -> Option { + self.app_state.get_nat_info() + } + pub fn peer_nat_info(&self, ip: &Ipv4Addr) -> Option { + self.app_state.get_peer_info(ip).and_then(|v| v.nat_info) + } + pub fn packet_loss_info(&self, ip: &Ipv4Addr) -> Option { + self.app_state.packet_loss_stats.get_loss_info(ip) + } + pub fn all_packet_loss_info(&self) -> Vec { + self.app_state.packet_loss_stats.get_all_loss_info() + } + pub fn reset_packet_loss(&self, ip: &Ipv4Addr) { + self.app_state.packet_loss_stats.reset(ip) + } + pub fn reset_all_packet_loss(&self) { + self.app_state.packet_loss_stats.reset_all() + } + pub fn traffic_info(&self, ip: &Ipv4Addr) -> Option { + self.app_state.traffic_stats.get_traffic_info(ip) + } + pub fn all_traffic_info(&self) -> Vec { + self.app_state.traffic_stats.get_all_traffic_info() + } + pub fn reset_traffic(&self, ip: &Ipv4Addr) { + self.app_state.traffic_stats.reset(ip) + } + pub fn reset_all_traffic(&self) { + self.app_state.traffic_stats.reset_all() + } +} diff --git a/vnt-core/src/compression/lz4_compression.rs b/vnt-core/src/compression/lz4_compression.rs new file mode 100644 index 0000000..10f98e7 --- /dev/null +++ b/vnt-core/src/compression/lz4_compression.rs @@ -0,0 +1,132 @@ +use crate::protocol::ip_packet_protocol::{HEAD_LENGTH, NetPacket}; +use crate::protocol::transmission::TransmissionBytes; +use std::io; + +#[derive(Clone)] +pub struct LZ4Compression { + min_size: usize, // 只压缩大于此大小的数据包 +} + +impl LZ4Compression { + pub fn new() -> Self { + Self::with_min_size(256) + } + pub fn with_min_size(min_size: usize) -> Self { + Self { min_size } + } + + /// 压缩数据包,返回新的压缩后的数据包 + /// reserve: 尾部预留空间(用于后续加密等操作) + pub fn compress( + &self, + pkt: NetPacket, + reserve: usize, + ) -> io::Result> { + let payload = pkt.payload(); + if payload.len() < self.min_size { + return Ok(pkt); + } + let compressed = lz4_flex::compress_prepend_size(payload); + if compressed.len() >= payload.len() { + return Ok(pkt); + } + let total_len = HEAD_LENGTH + compressed.len(); + let mut buf = TransmissionBytes::zeroed_size(total_len, reserve); + + buf[..HEAD_LENGTH].copy_from_slice(&pkt.buffer()[..HEAD_LENGTH]); + buf[HEAD_LENGTH..total_len].copy_from_slice(&compressed); + + let mut packet = NetPacket::new(buf)?; + packet.set_compressed_flag(true); + Ok(packet) + } + + /// 解压缩数据包,返回新的解压后的数据包 + /// reserve: 尾部预留空间(用于后续加密等操作) + pub fn decompress( + &self, + pkt: NetPacket, + ) -> io::Result> { + if !pkt.is_compressed() { + return Ok(pkt); + } + let payload = pkt.payload(); + + let decompressed = lz4_flex::decompress_size_prepended(payload).map_err(|e| { + io::Error::new( + io::ErrorKind::InvalidData, + format!("decompress failed: {}", e), + ) + })?; + + let total_len = HEAD_LENGTH + decompressed.len(); + let mut buf = TransmissionBytes::zeroed(total_len); + + buf[..HEAD_LENGTH].copy_from_slice(&pkt.buffer()[..HEAD_LENGTH]); + buf[HEAD_LENGTH..total_len].copy_from_slice(&decompressed); + + let mut packet = NetPacket::new(buf)?; + packet.set_compressed_flag(false); + Ok(packet) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::protocol::ip_packet_protocol::{HEAD_LENGTH, NetPacket}; + use crate::protocol::transmission::TransmissionBytes; + + fn make_packet(data: &[u8]) -> NetPacket { + let mut buf = TransmissionBytes::zeroed(HEAD_LENGTH + data.len()); + buf[HEAD_LENGTH..HEAD_LENGTH + data.len()].copy_from_slice(data); + NetPacket::new(buf).unwrap() + } + + #[test] + fn test_lz4_compress_and_decompress() { + let lz = LZ4Compression::with_min_size(10); + + // --- 构造原始包 --- + let payload = vec![1u8; 200]; + let original = make_packet(&payload); + + // --- 压缩 --- + let compressed = lz.compress(original, 0).unwrap(); + assert!(compressed.is_compressed()); + + // 压缩后的 payload 应变小 + assert!( + compressed.payload().len() < payload.len(), + "压缩后 payload 应该更小" + ); + + // --- 解压 --- + let decompressed = lz.decompress(compressed).unwrap(); + + // 标志应清除 + assert!(!decompressed.is_compressed()); + + // HEAD 不变 + assert_eq!( + &decompressed.buffer()[..HEAD_LENGTH], + &[0u8; HEAD_LENGTH][..], + "HEAD 必须保持不变" + ); + + // payload 必须等于原始 payload + assert_eq!(decompressed.payload(), &payload[..]); + } + + #[test] + fn test_no_compress_when_small() { + let lz = LZ4Compression::with_min_size(100); + + let pkt = make_packet(&[7; 20]); + let compressed = lz.compress(pkt, 0).unwrap(); + + assert!(!compressed.is_compressed(), "小包不应该被压缩"); + + assert_eq!(compressed.payload(), &[7; 20][..]); + } +} diff --git a/vnt-core/src/compression/mod.rs b/vnt-core/src/compression/mod.rs new file mode 100644 index 0000000..92c19b6 --- /dev/null +++ b/vnt-core/src/compression/mod.rs @@ -0,0 +1,45 @@ +use crate::compression::lz4_compression::LZ4Compression; +use crate::protocol::ip_packet_protocol::NetPacket; +use crate::protocol::transmission::TransmissionBytes; +use std::io; + +mod lz4_compression; + +#[derive(Clone)] +pub(crate) struct PacketCompression { + compression: Option, +} + +impl PacketCompression { + pub(crate) fn new(enabled: bool) -> Self { + Self { + compression: if enabled { + Some(LZ4Compression::new()) + } else { + None + }, + } + } + + pub(crate) fn compress( + &self, + pkt: NetPacket, + reserve: usize, + ) -> io::Result> { + if let Some(compression) = self.compression.as_ref() { + return compression.compress(pkt, reserve); + } + + Ok(pkt) + } + + pub(crate) fn decompress( + &self, + pkt: NetPacket, + ) -> io::Result> { + if let Some(compression) = self.compression.as_ref() { + return compression.decompress(pkt); + } + Ok(pkt) + } +} diff --git a/vnt-core/src/context/config.rs b/vnt-core/src/context/config.rs new file mode 100644 index 0000000..e0a1f99 --- /dev/null +++ b/vnt-core/src/context/config.rs @@ -0,0 +1,101 @@ +use crate::crypto::PacketCrypto; +use crate::nat::NetInput; +use crate::port_mapping::PortMapping; +use crate::tls::verifier::CertValidationMode; +use crate::tunnel_core::server::transport::config::{ConnectRegConfig, ProtocolAddress}; +use anyhow::bail; +use ipnet::Ipv4Net; +use std::collections::HashSet; +use std::net::Ipv4Addr; + +pub const MAX_NETWORK_CODE_LEN: usize = 32; +pub const MAX_DEVICE_ID_LEN: usize = 64; +pub const MAX_NAME_LEN: usize = 128; +pub const MAX_VERSION_LEN: usize = 32; +pub const MAX_MTU: u16 = 1500; + +#[derive(Debug, Clone, Default)] +pub struct Config { + pub server_addr: Vec, + pub cert_mode: CertValidationMode, + pub network_code: String, + pub device_id: String, + pub device_name: String, + pub tun_name: Option, + pub ip: Option, + pub password: Option, + pub no_punch: bool, + pub compress: bool, + pub rtx: bool, + pub fec: bool, + pub input: Vec, + pub output: Vec, + pub no_nat: bool, + pub no_tun: bool, + pub mtu: Option, + pub port_mapping: Vec, + pub allow_port_mapping: bool, + pub udp_stun: Vec, + pub tcp_stun: Vec, +} +impl Config { + pub fn check(&self) -> anyhow::Result<()> { + if self.server_addr.is_empty() { + bail!("服务器地址不能为空"); + } + if self.server_addr.len() > 1 { + let mut set = HashSet::new(); + + for a in self.server_addr.iter() { + if !set.insert(a.address.as_str()) { + bail!("服务器地址不能相同") + } + } + } + + if self.network_code.len() > MAX_NETWORK_CODE_LEN { + bail!( + "network_code length exceeds {} characters (current: {})", + MAX_NETWORK_CODE_LEN, + self.network_code.len() + ) + } + + if self.device_id.len() > MAX_DEVICE_ID_LEN { + bail!( + "device_id length exceeds {} characters (current: {})", + MAX_DEVICE_ID_LEN, + self.device_id.len() + ) + } + + if self.device_name.len() > MAX_NAME_LEN { + bail!( + "name length exceeds {} characters (current: {})", + MAX_NAME_LEN, + self.device_name.len() + ) + } + if let Some(mtu) = self.mtu + && mtu > MAX_MTU + { + bail!("MTU is too large (Maximum mtu: {MAX_MTU})",) + } + Ok(()) + } + pub fn key_sign(&self) -> Option { + self.password.as_ref().map(|p| PacketCrypto::key_sign(p)) + } + pub(crate) fn to_connect_config(&self, index: usize) -> ConnectRegConfig { + ConnectRegConfig { + server_addr: self.server_addr[index].clone(), + cert_mode: self.cert_mode.clone(), + network_code: self.network_code.clone(), + device_id: self.device_id.clone(), + device_name: self.device_name.clone(), + ip: self.ip, + key_sign: self.key_sign(), + ip_variable: self.ip.is_none(), + } + } +} diff --git a/vnt-core/src/context/mod.rs b/vnt-core/src/context/mod.rs new file mode 100644 index 0000000..a65cc47 --- /dev/null +++ b/vnt-core/src/context/mod.rs @@ -0,0 +1,632 @@ +use crate::context::config::Config; +use crate::context::nat::{MyNatInfo, PunchBackoff}; +use crate::nat::SubnetExternalRoute; +use crate::protocol::client_message::PunchInfo; +use crate::protocol::control_message::{ClientSimpleInfo, ClientSimpleInfoList}; +use crate::tunnel_core::p2p::route_table::RouteTable; +use crate::tunnel_core::server::transport::config::ProtocolAddress; +use ipnet::Ipv4Net; +use parking_lot::{Mutex, RwLock}; +use rust_p2p_core::nat::NatInfo; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::net::Ipv4Addr; +use std::sync::Arc; +#[derive(Default)] +struct PingStats { + sent: u64, + received: u64, +} + +#[derive(Default)] +struct TrafficCounter { + tx_bytes: u64, + rx_bytes: u64, +} + +#[derive(Clone, Default)] +pub struct TrafficStats { + inner: Arc>>>>, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct TrafficInfo { + pub ip: Ipv4Addr, + pub tx_bytes: u64, + pub rx_bytes: u64, +} + +impl TrafficStats { + fn get_or_create(&self, ip: Ipv4Addr) -> Arc> { + { + let read = self.inner.read(); + if let Some(counter) = read.get(&ip) { + return counter.clone(); + } + } + let mut write = self.inner.write(); + write + .entry(ip) + .or_insert_with(|| Arc::new(Mutex::new(TrafficCounter::default()))) + .clone() + } + + pub fn record_tx(&self, ip: Ipv4Addr, bytes: u64) { + let counter = self.get_or_create(ip); + counter.lock().tx_bytes += bytes; + } + + pub fn record_rx(&self, ip: Ipv4Addr, bytes: u64) { + let counter = self.get_or_create(ip); + counter.lock().rx_bytes += bytes; + } + + pub fn get_traffic_info(&self, ip: &Ipv4Addr) -> Option { + let read = self.inner.read(); + read.get(ip).map(|counter| { + let guard = counter.lock(); + TrafficInfo { + ip: *ip, + tx_bytes: guard.tx_bytes, + rx_bytes: guard.rx_bytes, + } + }) + } + + pub fn get_all_traffic_info(&self) -> Vec { + let read = self.inner.read(); + read.iter() + .map(|(ip, counter)| { + let guard = counter.lock(); + TrafficInfo { + ip: *ip, + tx_bytes: guard.tx_bytes, + rx_bytes: guard.rx_bytes, + } + }) + .collect() + } + + pub fn reset(&self, ip: &Ipv4Addr) { + let read = self.inner.read(); + if let Some(counter) = read.get(ip) { + *counter.lock() = TrafficCounter::default(); + } + } + + pub fn reset_all(&self) { + let read = self.inner.read(); + for counter in read.values() { + *counter.lock() = TrafficCounter::default(); + } + } + + pub fn clear(&self) { + self.inner.write().clear(); + } +} + +#[derive(Clone, Default)] +pub struct PacketLossStats { + inner: Arc>>>>, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct PacketLossInfo { + pub ip: Ipv4Addr, + pub sent: u64, + pub received: u64, + pub loss_rate: f64, +} + +impl PacketLossStats { + fn get_or_create(&self, ip: Ipv4Addr) -> Arc> { + { + let read = self.inner.read(); + if let Some(stats) = read.get(&ip) { + return stats.clone(); + } + } + let mut write = self.inner.write(); + write + .entry(ip) + .or_insert_with(|| Arc::new(Mutex::new(PingStats::default()))) + .clone() + } + + pub fn record_sent(&self, ip: Ipv4Addr) { + let stats = self.get_or_create(ip); + stats.lock().sent += 1; + } + + pub fn record_received(&self, ip: Ipv4Addr) { + let stats = self.get_or_create(ip); + stats.lock().received += 1; + } + + pub fn get_loss_info(&self, ip: &Ipv4Addr) -> Option { + let read = self.inner.read(); + read.get(ip).map(|stats| { + let guard = stats.lock(); + let loss_rate = if guard.sent > 0 { + 1.0 - (guard.received as f64 / guard.sent as f64) + } else { + 0.0 + }; + PacketLossInfo { + ip: *ip, + sent: guard.sent, + received: guard.received, + loss_rate, + } + }) + } + + pub fn get_all_loss_info(&self) -> Vec { + let read = self.inner.read(); + read.iter() + .map(|(ip, stats)| { + let guard = stats.lock(); + let loss_rate = if guard.sent > 0 { + 1.0 - (guard.received as f64 / guard.sent as f64) + } else { + 0.0 + }; + PacketLossInfo { + ip: *ip, + sent: guard.sent, + received: guard.received, + loss_rate, + } + }) + .collect() + } + + pub fn reset(&self, ip: &Ipv4Addr) { + let read = self.inner.read(); + if let Some(stats) = read.get(ip) { + *stats.lock() = PingStats::default(); + } + } + + pub fn reset_all(&self) { + let read = self.inner.read(); + for stats in read.values() { + *stats.lock() = PingStats::default(); + } + } + + pub fn clear(&self) { + self.inner.write().clear(); + } +} + +pub mod config; +pub(crate) mod nat; + +#[derive(Clone, Default)] +pub(crate) struct AppState { + config: Arc>>>, + pub(crate) network: SharedNetworkAddr, + pub(crate) server_info_collection: ServerInfoCollection, + pub(crate) peer_map: PeerInfoMap, + pub(crate) route_table: RouteTable, + pub(crate) subnet_route: SubnetExternalRoute, + pub(crate) nat_info: MyNatInfo, + pub(crate) punch_backoff: PunchBackoff, + pub(crate) packet_loss_stats: PacketLossStats, + pub(crate) traffic_stats: TrafficStats, +} +#[derive(Clone, Default)] +pub(crate) struct SharedNetworkAddr { + inner: Arc>>, +} +impl SharedNetworkAddr { + pub fn network(&self) -> Option { + self.inner.lock().as_ref().map(|v| v.network()) + } + pub fn ip(&self) -> Option { + self.inner.lock().map(|v| v.ip) + } + pub fn get(&self) -> Option { + *self.inner.lock() + } + pub fn set(&self, addr: NetworkAddr) { + *self.inner.lock() = Some(addr); + } + pub fn clear(&self) { + *self.inner.lock() = None; + } +} + +/// 网络路由封装,包含本地网络信息和子网路由 +#[derive(Clone)] +pub(crate) struct NetworkRoute { + pub network: SharedNetworkAddr, + pub subnet_route: SubnetExternalRoute, +} + +impl NetworkRoute { + pub fn new(network: SharedNetworkAddr, subnet_route: SubnetExternalRoute) -> Self { + Self { + network, + subnet_route, + } + } + + /// 检查 IP 是否在本地网络或子网路由中 + pub fn network_contains(&self, ip: &Ipv4Addr) -> bool { + if let Some(network) = self.network.network() + && network.contains(ip) + { + return true; + } + self.subnet_route.route(ip).is_some() + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct PeerClientInfo { + pub nat_info: Option, +} + +#[derive(Clone, Default)] +pub(crate) struct PeerInfoMap { + inner: Arc>>, +} + +impl PeerInfoMap { + pub fn get(&self, ip: &Ipv4Addr) -> Option { + self.inner.lock().get(ip).cloned() + } + + pub fn update_nat_info(&self, ip: Ipv4Addr, nat_info: NatInfo) { + let mut guard = self.inner.lock(); + if let Some(v) = guard.get_mut(&ip) { + v.nat_info = Some(nat_info); + return; + } + guard.insert( + ip, + PeerClientInfo { + nat_info: Some(nat_info), + }, + ); + } + + pub fn clear(&self) { + self.inner.lock().clear(); + } +} + +#[derive(Clone, Default)] +pub(crate) struct ServerInfoCollection { + client_simple_list: Arc>>, + server_node_map: Arc>>, +} +#[derive(Clone, Default)] +pub struct ServerNodeInfo { + pub server_id: u32, + pub server_addr: ProtocolAddress, + pub connected: bool, + pub rtt: Option, + pub data_version: u64, + pub client_map: HashMap, + pub last_connected_time: Option, + pub disconnected_time: Option, + pub server_version: Option, +} +impl ServerInfoCollection { + pub fn server_client_ip_map(&self) -> HashMap, u32)> { + self.server_node_map + .read() + .iter() + .map(|(k, v)| { + ( + *k, + ( + v.client_map + .iter() + .filter(|(_, v)| v.online) + .map(|(k, _)| *k) + .collect(), + v.rtt.unwrap_or(500), + ), + ) + }) + .collect() + } + pub fn server_node_list(&self) -> Vec { + self.server_node_map.read().values().cloned().collect() + } + pub fn update_server(&self, addr: Vec<(u32, ProtocolAddress)>) { + let mut server_node_map_guard = self.server_node_map.write(); + let mut client_simple_list_guard = self.client_simple_list.write(); + server_node_map_guard.clear(); + client_simple_list_guard.clear(); + for (server_id, server_addr) in addr { + let server_node = ServerNodeInfo { + server_id, + server_addr, + ..Default::default() + }; + server_node_map_guard.insert(server_id, server_node); + } + } + pub fn find_connected_server(&self, server_ids: &[u32]) -> Option { + let map = self.server_node_map.read(); + + server_ids + .iter() + .filter_map(|id| { + let server = map.get(id)?; + + if !server.connected { + return None; + } + + let rtt = server.rtt.unwrap_or(u32::MAX); + + Some((*id, rtt)) + }) + .min_by_key(|(_, rtt)| *rtt) + .map(|(id, _)| id) + } + pub fn find_ip_to_server(&self, server_ids: &[u32], ip: &Ipv4Addr) -> Option { + let map = self.server_node_map.read(); + + server_ids + .iter() + .filter_map(|id| { + let server = map.get(id)?; + + if !server.connected { + return None; + } + + let client = server.client_map.get(ip)?; + if !client.online { + return None; + } + + let rtt = server.rtt.unwrap_or(u32::MAX); + + Some((*id, rtt)) + }) + .min_by_key(|(_, rtt)| *rtt) + .map(|(id, _)| id) + } + pub fn client_online_ips(&self) -> Vec { + self.client_simple_list + .read() + .iter() + .filter(|v| v.online) + .map(|c| c.ip) + .collect() + } + pub fn client_ips(&self) -> Vec { + self.client_simple_list.read().clone() + } + pub fn data_version(&self, server_id: u32) -> u64 { + self.server_node_map + .read() + .get(&server_id) + .map(|v| v.data_version) + .unwrap_or(0) + } + pub fn update_client_simple_list( + &self, + server_id: u32, + self_ip: Ipv4Addr, + client_simple_list: ClientSimpleInfoList, + now: i64, + ) { + let mut guard = self.server_node_map.write(); + let server_node = guard.entry(server_id).or_default(); + if now > client_simple_list.time { + server_node.rtt = Some((now - client_simple_list.time) as u32); + } + server_node.data_version = client_simple_list.data_version; + let map: HashMap = client_simple_list + .list + .into_iter() + .filter(|v| v.ip != self_ip) + .map(|info| (info.ip, info)) + .collect(); + + if client_simple_list.is_all { + server_node.client_map = map; + } else { + server_node.client_map.extend(map); + } + let mut client_simple_map = HashMap::::new(); + for (_, server_node) in guard.iter() { + for (_, x) in server_node.client_map.iter() { + if let Some(v) = client_simple_map.get_mut(&x.ip) { + if x.online { + v.online = true; + } + } else { + client_simple_map.insert(x.ip, x.clone()); + } + } + } + + let mut guard = self.client_simple_list.write(); + *guard = client_simple_map.into_values().collect() + } + pub fn set_server_connected(&self, server_id: u32, val: bool) -> bool { + let mut mutex_guard = self.server_node_map.write(); + let server_node = mutex_guard.entry(server_id).or_default(); + let old = server_node.connected; + server_node.connected = val; + old + } + pub fn is_any_server_connected(&self, server_ids: Option<&[u32]>) -> bool { + let guard = self.server_node_map.read(); + if let Some(server_ids) = server_ids { + for id in server_ids { + if guard.get(id).map(|v| v.connected).unwrap_or(false) { + return true; + } + } + } else { + for (_, server_node) in guard.iter() { + if server_node.connected { + return true; + } + } + } + + false + } + pub fn is_server_connected(&self, server_id: u32) -> bool { + self.server_node_map + .read() + .get(&server_id) + .map(|v| v.connected) + .unwrap_or(false) + } + pub fn set_last_connected_time(&self, server_id: u32, last_connected_time: Option) { + self.server_node_map + .write() + .entry(server_id) + .or_default() + .last_connected_time = last_connected_time; + } + pub fn set_disconnected_time(&self, server_id: u32, last_connected_time: Option) { + self.server_node_map + .write() + .entry(server_id) + .or_default() + .disconnected_time = last_connected_time; + } + pub fn set_server_rtt(&self, server_id: u32, rtt: u32) { + if let Some(v) = self.server_node_map.write().get_mut(&server_id) { + v.rtt = Some(rtt); + } + } + pub fn set_server_version(&self, server_id: u32, version: String) { + if let Some(v) = self.server_node_map.write().get_mut(&server_id) { + v.server_version = Some(version); + } + } + pub fn get_server_rtt(&self, ip: &Ipv4Addr) -> Option { + let server_node_map_guard = self.server_node_map.read(); + for (_, server_node) in server_node_map_guard.iter() { + if !server_node.connected { + continue; + } + if let Some(v) = server_node.client_map.get(ip) + && v.online + { + return server_node.rtt; + } + } + None + } + pub fn exists_online_client_ip(&self, ip: &Ipv4Addr) -> bool { + self.client_simple_list + .read() + .iter() + .any(|v| v.ip == *ip && v.online) + } + pub fn clear(&self) { + self.client_simple_list.write().clear(); + let mut guard = self.server_node_map.write(); + for server_node in guard.values_mut() { + server_node.connected = false; + server_node.rtt = None; + server_node.data_version = 0; + server_node.client_map.clear(); + server_node.last_connected_time = None; + server_node.disconnected_time = None; + } + } +} +#[derive(Copy, Clone, Debug)] +pub struct NetworkAddr { + pub gateway: Ipv4Addr, + pub broadcast: Ipv4Addr, + pub ip: Ipv4Addr, + pub prefix_len: u8, +} +impl NetworkAddr { + pub fn network(&self) -> Ipv4Net { + Ipv4Net::new_assert(self.ip, self.prefix_len) + } +} + +impl AppState { + pub fn stop_network(&self) { + self.network.clear(); + self.server_info_collection.clear(); + self.peer_map.clear(); + // route_table 来自外部 crate,会在任务停止后自动失效 + self.nat_info.clear(); + self.punch_backoff.clear(); + self.packet_loss_stats.clear(); + self.traffic_stats.clear(); + } + + fn network(&self) -> Option { + self.network.network() + } + pub fn get_network(&self) -> Option { + self.network.get() + } + fn network_contains(&self, ip: &Ipv4Addr) -> bool { + let Some(network) = self.network() else { + return false; + }; + if network.contains(ip) { + return true; + } + self.subnet_route.route(ip).is_some() + } + pub fn client_ips(&self) -> Vec { + self.server_info_collection.client_ips() + } + pub fn get_peer_info(&self, ip: &Ipv4Addr) -> Option { + self.peer_map.get(ip) + } + + pub fn set_config(&self, config: Box) { + *self.config.lock() = Some(config); + } + + pub fn get_config(&self) -> Option> { + self.config.lock().clone() + } + pub(crate) fn udp_stun(&self) -> Vec { + self.config + .lock() + .as_ref() + .map(|v| v.udp_stun.clone()) + .unwrap_or_default() + } + pub(crate) fn tcp_stun(&self) -> Vec { + self.config + .lock() + .as_ref() + .map(|v| v.tcp_stun.clone()) + .unwrap_or_default() + } +} +impl AppState { + pub fn get_punch_info(&self) -> Option { + self.nat_info.get().map(|info| PunchInfo { + nat_info: self.filter_ip(info), + }) + } + pub fn get_nat_info(&self) -> Option { + self.nat_info.get().map(|info| self.filter_ip(info)) + } + pub fn filter_ip(&self, mut info: NatInfo) -> NatInfo { + if self.network_contains(&info.local_ipv4) { + info.local_ipv4 = Ipv4Addr::UNSPECIFIED; + } + info.local_ipv4s.retain(|ip| !self.network_contains(ip)); + info + } +} diff --git a/vnt-core/src/context/nat.rs b/vnt-core/src/context/nat.rs new file mode 100644 index 0000000..224b9ff --- /dev/null +++ b/vnt-core/src/context/nat.rs @@ -0,0 +1,147 @@ +use parking_lot::RwLock; +use rust_p2p_core::nat::NatInfo; +use rust_p2p_core::route::Index; +use rust_p2p_core::tunnel::udp::UDPIndex; +use std::collections::HashMap; +use std::net::{Ipv4Addr, SocketAddr}; +use std::sync::Arc; + +#[derive(Clone, Default)] +pub struct MyNatInfo { + nat_info: Arc>>, +} +impl MyNatInfo { + pub fn get(&self) -> Option { + self.nat_info.read().clone() + } + pub fn update_public_addr(&self, index: Index, addr: SocketAddr) { + let (ip, port) = if let Some(r) = mapping_addr(addr) { + r + } else { + return; + }; + log::debug!("public_addr:{},{},index={index:?}", ip, port); + let mut nat_info = self.nat_info.write(); + let Some(nat_info) = nat_info.as_mut() else { + return; + }; + if rust_p2p_core::extend::addr::is_ipv4_global(&ip) { + if !nat_info.public_ips.contains(&ip) { + nat_info.public_ips.push(ip); + } + match index { + Index::Udp(index) => { + let index = match index { + UDPIndex::MainV4(index) => index, + UDPIndex::MainV6(index) => index, + UDPIndex::SubV4(_) => return, + }; + if let Some(p) = nat_info.public_udp_ports.get_mut(index) { + *p = port; + } + } + Index::Tcp(_) => { + nat_info.public_tcp_port = port; + } + _ => {} + } + } else { + log::debug!("not public addr: {addr:?}") + } + } + pub fn update_tcp_public_addr(&self, addr: SocketAddr) { + let SocketAddr::V4(addr) = addr else { + return; + }; + let ip = *addr.ip(); + let port = addr.port(); + log::info!("tcp_public_addr, {}:{}", ip, port); + let mut nat_info = self.nat_info.write(); + let Some(nat_info) = nat_info.as_mut() else { + return; + }; + if ip.is_unspecified() && port == 0 { + nat_info.public_tcp_port = 0; + return; + } + if rust_p2p_core::extend::addr::is_ipv4_global(&ip) { + if !nat_info.public_ips.contains(&ip) { + nat_info.public_ips.push(ip); + } + nat_info.public_tcp_port = port; + } else { + log::debug!("not public addr: {addr:?}") + } + } + pub fn replace_nat_info(&self, nat_info: NatInfo) { + self.nat_info.write().replace(nat_info); + } + pub fn clear(&self) { + *self.nat_info.write() = None; + } +} +fn mapping_addr(addr: SocketAddr) -> Option<(Ipv4Addr, u16)> { + match addr { + SocketAddr::V4(addr) => Some((*addr.ip(), addr.port())), + SocketAddr::V6(addr) => addr.ip().to_ipv4_mapped().map(|ip| (ip, addr.port())), + } +} + +#[derive(Copy, Clone, Debug)] +pub struct PunchState { + pub count: i64, + pub last_ts: i64, +} + +#[derive(Clone, Default)] +pub struct PunchBackoff { + inner: Arc>>, +} + +impl PunchBackoff { + const MAX_BACKOFF_MS: i64 = 3_600_000; // 1h + const BASE_MS: i64 = 3000; + + fn now() -> i64 { + crate::utils::time::now_ts_ms() + } + + pub fn record(&self, ip: Ipv4Addr) { + let mut map = self.inner.write(); + let entry = map.entry(ip).or_insert(PunchState { + count: 0, + last_ts: 0, + }); + entry.count += 1; + entry.last_ts = Self::now(); + } + #[allow(dead_code)] + pub fn reset(&self, ip: Ipv4Addr) { + self.inner.write().remove(&ip); + } + #[allow(dead_code)] + pub fn get(&self, ip: &Ipv4Addr) -> Option { + self.inner.read().get(ip).copied() + } + + pub fn should_punch(&self, ip: Ipv4Addr) -> bool { + let map = self.inner.read(); + let Some(state) = map.get(&ip) else { + return true; + }; + + let now = Self::now(); + let elapsed = now - state.last_ts; + + let mut backoff = Self::BASE_MS * state.count; + if backoff > Self::MAX_BACKOFF_MS { + backoff = Self::MAX_BACKOFF_MS; + } + + elapsed >= backoff + } + #[allow(dead_code)] + pub fn clear(&self) { + self.inner.write().clear(); + } +} diff --git a/vnt-core/src/core/mod.rs b/vnt-core/src/core/mod.rs new file mode 100644 index 0000000..e2b562f --- /dev/null +++ b/vnt-core/src/core/mod.rs @@ -0,0 +1,353 @@ +use crate::api::VntApi; +use crate::compression::PacketCompression; +use crate::context::config::Config; +use crate::context::{AppState, NetworkAddr, NetworkRoute}; +use crate::crypto::PacketCrypto; +use crate::enhanced_tunnel::enhanced_ipv4_tunnel; +use crate::enhanced_tunnel::inbound::EnhancedInbound; +use crate::enhanced_tunnel::outbound::EnhancedOutbound; +use crate::fec::{FecDecoder, FecEncoder}; +use crate::nat::internal_nat::{InternalNatInbound, PortMappingManager}; +use crate::nat::{AllowSubnetExternalRoute, SubnetExternalRoute}; +use crate::tun::enhanced_tun::EnhancedTunInbound; +use crate::tun::{DeviceConfig, DeviceIOManager, TunDataInbound, TunReceiver, tun_channel}; +use crate::tunnel_core::outbound::{BasicOutbound, HybridOutbound}; +use crate::tunnel_core::p2p::inbound::{P2pInboundConfig, P2pInboundHandler}; +use crate::tunnel_core::p2p::transport::punch::NatPuncher; +use crate::tunnel_core::p2p::transport::task::init_tunnel; +use crate::tunnel_core::server::connection_manager::{ + InboundHandlerConfig, ServerTurnManager, coordinated_registration, create_server_tunnel, +}; +use crate::tunnel_core::server::rpc::ServerRPC; +use crate::utils::task_control::TaskGroup; +use anyhow::bail; +use ipnet::Ipv4Net; +use std::net::Ipv4Addr; + +pub const DEFAULT_MTU: u16 = 1380; + +/// Context for deferred registration +struct RegistrationContext { + server_managers: Vec, + subnet_external_route: SubnetExternalRoute, + puncher: NatPuncher, + packet_crypto: PacketCrypto, + packet_compression: PacketCompression, + enhanced_inbound: EnhancedInbound, + fec_decoder: FecDecoder, +} + +pub struct NetworkManager { + config: Box, + app_state: AppState, + task_group: TaskGroup, + device_io_manager: DeviceIOManager, + enhanced_outbound: Option, + server_rpc: ServerRPC, + tun_receiver: Option, + registration_context: Option>, +} + +impl NetworkManager { + pub async fn create_network( + config: Box, + task_group: TaskGroup, + ) -> anyhow::Result { + let app_state = AppState::default(); + config.check()?; + let mtu = config.mtu.unwrap_or(DEFAULT_MTU); + let packet_crypto = PacketCrypto::new_from_str(config.password.as_deref()); + let packet_compression = PacketCompression::new(config.compress); + let (server_manager_list, tunnel_to_server, server_rpc) = + create_server_tunnel(app_state.clone(), &config, packet_crypto.clone()); + let device_io_manager = DeviceIOManager::new(task_group.clone()); + let allow_subnet = AllowSubnetExternalRoute::new(config.output.clone()); + + let (puncher, p2p_socket, p2p_task) = if !config.no_punch { + let (puncher, p2p_socket_manager, p2p_task) = init_tunnel( + task_group.clone(), + app_state.clone(), + tunnel_to_server.clone(), + packet_crypto.clone(), + ) + .await?; + + (Some(puncher), Some(p2p_socket_manager), Some(p2p_task)) + } else { + (None, None, None) + }; + let puncher = NatPuncher::new( + app_state.network.clone(), + app_state.punch_backoff.clone(), + puncher, + packet_crypto.clone(), + ); + let subnet_external_route = app_state.subnet_route.clone(); + subnet_external_route.set_route_table(config.input.clone()); + + let fec_decoder = FecDecoder::new(); + let basic_outbound = BasicOutbound::new( + tunnel_to_server.clone(), + p2p_socket.clone(), + packet_crypto.clone(), + ); + let fec_encoder = if config.fec { + Some(FecEncoder::new(&task_group, basic_outbound.clone())) + } else { + None + }; + + let hybrid_outbound = HybridOutbound::new( + app_state.network.clone(), + app_state.server_info_collection.clone(), + app_state.traffic_stats.clone(), + basic_outbound, + packet_compression.clone(), + subnet_external_route.clone(), + fec_encoder, + ); + let port_mapping_manager = PortMappingManager::new( + config.no_tun, + config.allow_port_mapping, + app_state.network.clone(), + ); + let internal_nat_inbound = if config.no_nat && !config.no_tun { + None + } else { + let nat_inbound = InternalNatInbound::create( + &task_group, + mtu, + hybrid_outbound.clone(), + allow_subnet.clone(), + app_state.network.clone(), + config.no_tun, + ) + .await?; + Some(nat_inbound) + }; + + let (enhanced_tun_inbound, tun_receiver) = if config.no_tun { + ( + EnhancedTunInbound::Nat( + internal_nat_inbound + .clone() + .expect("internal_nat_inbound must be Some when no_tun is true"), + ), + None, + ) + } else { + let (tun_inbound, tun_receiver) = tun_channel(); + let tun_data_sender = TunDataInbound::new(tun_inbound, allow_subnet.clone()); + (EnhancedTunInbound::Tun(tun_data_sender), Some(tun_receiver)) + }; + + let (enhanced_inbound, enhanced_outbound) = enhanced_ipv4_tunnel( + app_state.clone(), + task_group.clone(), + enhanced_tun_inbound, + crate::enhanced_tunnel::TunnelConfig { + mtu, + password: config.password.clone(), + open_quic_client: config.rtx, + port_mapping: config.port_mapping.clone(), + }, + crate::enhanced_tunnel::TunnelComponents { + hybrid_outbound: hybrid_outbound.clone(), + external_route: subnet_external_route.clone(), + internal_nat_inbound, + port_mapping_manager, + }, + ) + .await?; + + if let Some(p2p_task) = p2p_task { + let handler = P2pInboundHandler::new(P2pInboundConfig { + network_route: NetworkRoute::new( + app_state.network.clone(), + subnet_external_route.clone(), + ), + route_table: app_state.route_table.clone(), + packet_loss_stats: app_state.packet_loss_stats.clone(), + packet_crypto: packet_crypto.clone(), + packet_compression: packet_compression.clone(), + enhanced_inbound: enhanced_inbound.clone(), + fec_decoder: fec_decoder.clone(), + }); + p2p_task.start(handler); + } + + let registration_context = Box::new(RegistrationContext { + server_managers: server_manager_list, + subnet_external_route, + puncher, + packet_crypto, + packet_compression, + enhanced_inbound, + fec_decoder, + }); + + app_state.set_config(config.clone()); + Ok(Self { + config, + app_state, + task_group, + device_io_manager, + enhanced_outbound, + server_rpc, + tun_receiver, + registration_context: Some(registration_context), + }) + } + + /// Register with server(s) and start data handling tasks. + /// This method can only be called once. + /// Returns the registration response on success. + pub async fn register(&mut self) -> anyhow::Result { + let Some(mut ctx) = self.registration_context.take() else { + bail!("register can only be called once"); + }; + + let is_multi_server = ctx.server_managers.len() > 1; + + let reg_response = if is_multi_server { + // Multi-server: coordinated pre-registration + log::info!( + "Multi-server mode: performing coordinated registration for {} servers", + ctx.server_managers.len() + ); + let reg_response = coordinated_registration(&mut ctx.server_managers).await?; + log::info!( + "Coordinated registration completed, IP: {}, prefix_len: {}", + reg_response.ip, + reg_response.prefix_len + ); + reg_response + } else { + // Single-server: normal registration + log::info!("Single-server mode: performing normal registration"); + let response = ctx.server_managers[0] + .connect_and_reg(crate::protocol::control_message::RegistrationMode::Normal) + .await?; + match response { + crate::protocol::control_message::ResponseMessage::Reg(reg) => { + log::info!( + "Registration completed, IP: {}, prefix_len: {}", + reg.ip, + reg.prefix_len + ); + reg + } + crate::protocol::control_message::ResponseMessage::Error(e) => { + bail!("Registration failed: {}", e.message); + } + crate::protocol::control_message::ResponseMessage::ConfirmReg(_) => { + bail!("Unexpected ConfirmReg response"); + } + } + }; + let network_addr = NetworkAddr { + gateway: reg_response.gateway, + broadcast: Ipv4Net::new(reg_response.ip, reg_response.prefix_len)?.broadcast(), + ip: reg_response.ip, + prefix_len: reg_response.prefix_len, + }; + self.app_state.network.set(network_addr); + + // 保存服务器版本信息 + if !reg_response.server_version.is_empty() { + for (index, _) in ctx.server_managers.iter().enumerate() { + self.app_state.server_info_collection.set_server_version( + index as u32, + reg_response.server_version.clone(), + ); + } + } + + // Start data handling tasks for all servers + for turn_manager in ctx.server_managers { + let handler_config = Box::new(InboundHandlerConfig { + network_route: NetworkRoute::new( + self.app_state.network.clone(), + ctx.subnet_external_route.clone(), + ), + server_info: self.app_state.server_info_collection.clone(), + nat_info: self.app_state.nat_info.clone(), + peer_map: self.app_state.peer_map.clone(), + punch_backoff: self.app_state.punch_backoff.clone(), + puncher: ctx.puncher.clone(), + packet_crypto: ctx.packet_crypto.clone(), + packet_compression: ctx.packet_compression.clone(), + enhanced_inbound: ctx.enhanced_inbound.clone(), + fec_decoder: ctx.fec_decoder.clone(), + }); + turn_manager.data_handle_task_connected(&self.task_group, handler_config, network_addr); + } + + Ok(network_addr) + } + + pub fn is_no_tun(&self) -> bool { + self.config.no_tun + } + + pub async fn start_tun(&mut self) -> anyhow::Result<()> { + let Some(receiver) = self.tun_receiver.take() else { + bail!("start_tun can only be called once"); + }; + let Some(enhanced_outbound) = self.enhanced_outbound.take() else { + bail!("start_tun can only be called once"); + }; + let mut config = DeviceConfig::default(); + config = config.set_mtu(self.config.mtu.unwrap_or(DEFAULT_MTU)); + if let Some(tun_name) = self.config.tun_name.clone() { + config = config.set_tun_name(tun_name); + } + self.device_io_manager + .start_task(config, receiver, enhanced_outbound) + .await + } + #[cfg(unix)] + pub async fn start_tun_fd(&mut self, tun_fd: Option) -> anyhow::Result<()> { + let Some(receiver) = self.tun_receiver.take() else { + bail!("start_tun_fd can only be called once"); + }; + let Some(enhanced_outbound) = self.enhanced_outbound.take() else { + bail!("start_tun_fd can only be called once"); + }; + let mut config = DeviceConfig::default(); + if let Some(tun_fd) = tun_fd { + config = config.set_tun_fd(tun_fd); + } + if let Some(tun_name) = self.config.tun_name.clone() { + config = config.set_tun_name(tun_name); + } + self.device_io_manager + .start_task(config, receiver, enhanced_outbound) + .await + } + #[cfg(not(target_os = "android"))] + pub async fn set_network_ip(&self, ip: Ipv4Addr, prefix_len: u8) -> anyhow::Result<()> { + self.device_io_manager.set_network(ip, prefix_len).await?; + Ok(()) + } + + fn stop_network(&mut self) { + self.task_group.stop(); + self.app_state.stop_network(); + } + #[cfg(not(target_os = "android"))] + pub async fn tun_if_index(&self) -> anyhow::Result { + self.device_io_manager.tun_if_index().await + } + pub async fn wait_all_stopped(&mut self) { + self.task_group.wait_all_stopped().await; + } + pub fn vnt_api(&self) -> VntApi { + VntApi::new(self.app_state.clone(), self.server_rpc.clone()) + } +} +impl Drop for NetworkManager { + fn drop(&mut self) { + self.stop_network(); + } +} diff --git a/vnt-core/src/crypto/chacha20_poly1305.rs b/vnt-core/src/crypto/chacha20_poly1305.rs new file mode 100644 index 0000000..633a4d0 --- /dev/null +++ b/vnt-core/src/crypto/chacha20_poly1305.rs @@ -0,0 +1,169 @@ +use crate::protocol::ip_packet_protocol::{HEAD_LENGTH, NetPacket}; +use ring::aead::{Aad, CHACHA20_POLY1305, LessSafeKey, Nonce, UnboundKey}; +use std::io; + +pub const TAG_LEN: usize = 16; + +#[derive(Clone)] +pub struct PacketCrypto { + key: LessSafeKey, +} + +impl PacketCrypto { + pub fn key_sign(s: &str) -> String { + use ring::digest::{Context, SHA256}; + + const PREFIX: &[u8] = b"KEY-BEGIN"; + const SUFFIX: &[u8] = b"KEY-END"; + + let mut ctx = Context::new(&SHA256); + ctx.update(PREFIX); + ctx.update(s.as_bytes()); + ctx.update(SUFFIX); + let digest = ctx.finish(); + let mut key_bytes = [0u8; 16]; + key_bytes.copy_from_slice(&digest.as_ref()[..16]); + key_bytes + .iter() + .map(|b| format!("{:02x}", b)) + .collect::() + } + pub fn new(key_bytes: [u8; 32]) -> Self { + let unbound = UnboundKey::new(&CHACHA20_POLY1305, &key_bytes).unwrap(); + let key = LessSafeKey::new(unbound); + Self { key } + } + pub fn new_from_str(s: &str) -> Self { + let hash = ring::digest::digest(&ring::digest::SHA256, s.as_bytes()); + let mut key_bytes = [0u8; 32]; + key_bytes.copy_from_slice(hash.as_ref()); + Self::new(key_bytes) + } + /// 根据包头生成 12 字节 nonce + pub fn make_nonce>(&self, pkt: &NetPacket) -> io::Result<[u8; 12]> { + let buf = pkt.buffer(); + + if buf.len() < HEAD_LENGTH { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "buffer too small", + )); + } + let msg_type = buf[0]; + let seq = &buf[4..8]; + let src = &buf[8..12]; + let dst = &buf[12..16]; + + let mut nonce12 = [0u8; 12]; + nonce12[0..4].copy_from_slice(seq); + nonce12[4..8].copy_from_slice(dst); + nonce12[8..12].copy_from_slice(src); + nonce12[0] = msg_type; + + Ok(nonce12) + } + + /// 原地加密(in-place) + /// payload 后需要预留16字节用于存放 tag + pub fn encrypt_in_place + AsMut<[u8]>>( + &self, + pkt: &mut NetPacket, + ) -> io::Result<()> { + let nonce = Nonce::assume_unique_for_key(self.make_nonce(pkt)?); + + let payload = pkt.payload_mut(); + let payload_len = payload.len() - TAG_LEN; // 实际 payload 长度(不含 tag 预留空间) + + // 只加密实际的 payload 部分 + let tag = self + .key + .seal_in_place_separate_tag(nonce, Aad::empty(), &mut payload[..payload_len]) + .map_err(|_| io::Error::other("encrypt failed"))?; + + // 将 tag 写入 payload 后的预留空间 + payload[payload_len..payload_len + TAG_LEN].copy_from_slice(tag.as_ref()); + + Ok(()) + } + + /// 原地解密(in-place) + pub fn decrypt_in_place + AsMut<[u8]>>( + &self, + pkt: &mut NetPacket, + ) -> io::Result { + let nonce = Nonce::assume_unique_for_key(self.make_nonce(pkt)?); + + let payload_with_tag = pkt.payload_mut(); + + let plaintext = self + .key + .open_in_place(nonce, Aad::empty(), payload_with_tag) + .map_err(|_| io::Error::other("decrypt failed"))?; + Ok(plaintext.len()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use bytes::BytesMut; + + // 用于构造一个简单的 NetPacket,包含头 16 字节 + payload + 16 字节 TAG 预留 + fn build_test_packet(payload_len: usize) -> NetPacket { + // 16 字节 head + payload + 16 字节预留 TAG + let total_len = HEAD_LENGTH + payload_len + TAG_LEN; + let mut buf = BytesMut::zeroed(total_len); + + // 构造一个头(16 字节) + buf[0] = 4; // MsgType::Ping + buf[4..8].copy_from_slice(&12345u32.to_be_bytes()); + buf[8..12].copy_from_slice(&111u32.to_be_bytes()); + buf[12..16].copy_from_slice(&222u32.to_be_bytes()); + + // 构造 payload(明文) + let payload_plain = &mut buf[HEAD_LENGTH..HEAD_LENGTH + payload_len]; + for (i, p) in payload_plain.iter_mut().enumerate() { + *p = (i as u8) ^ 0xAB; + } + + NetPacket::new(buf).unwrap() + } + + #[test] + fn test_encrypt_decrypt_in_place() { + let key = [7u8; 32]; + let crypto = PacketCrypto::new(key); + + let payload_len = 20; + let mut pkt = build_test_packet(payload_len); + + // 备份原 payload + let original_payload: Vec = + pkt.buffer()[HEAD_LENGTH..HEAD_LENGTH + payload_len].to_vec(); + + // 加密 + crypto.encrypt_in_place(&mut pkt).expect("encrypt failed"); + + let encrypted_buf = pkt.buffer(); + let tag_start = HEAD_LENGTH + payload_len; + let tag_end = tag_start + TAG_LEN; + + // TAG 不应该是全 0 + assert_ne!(&encrypted_buf[tag_start..tag_end], &[0u8; TAG_LEN]); + + // payload 已被加密,不等于明文 + assert_ne!( + &encrypted_buf[HEAD_LENGTH..HEAD_LENGTH + payload_len], + &original_payload[..] + ); + + // 解密 + crypto.decrypt_in_place(&mut pkt).expect("decrypt failed"); + + let decrypted_buf = pkt.buffer(); + let decrypted_payload = &decrypted_buf[HEAD_LENGTH..HEAD_LENGTH + payload_len]; + + // 解密后与原文一致 + assert_eq!(decrypted_payload, &original_payload[..]); + } +} diff --git a/vnt-core/src/crypto/mod.rs b/vnt-core/src/crypto/mod.rs new file mode 100644 index 0000000..bdf6666 --- /dev/null +++ b/vnt-core/src/crypto/mod.rs @@ -0,0 +1,47 @@ +use crate::crypto::chacha20_poly1305::TAG_LEN; +use crate::protocol::ip_packet_protocol::NetPacket; +use std::io; +use std::sync::Arc; + +mod chacha20_poly1305; + +use crate::protocol::transmission::{ExtendEnd, ShrinkEnd}; + +#[derive(Clone)] +pub(crate) struct PacketCrypto { + crypto: Option>, +} +impl PacketCrypto { + pub(crate) fn key_sign(s: &str) -> String { + chacha20_poly1305::PacketCrypto::key_sign(s) + } + + pub(crate) fn new_from_str(s: Option<&str>) -> Self { + Self { + crypto: s.map(chacha20_poly1305::PacketCrypto::new_from_str).map(Arc::new), + } + } + pub(crate) fn encrypt_reserve(&self) -> usize { + if self.crypto.is_some() { TAG_LEN } else { 0 } + } + pub(crate) fn encrypt_in_place + AsMut<[u8]> + ExtendEnd>( + &self, + pkt: &mut NetPacket, + ) -> io::Result<()> { + if let Some(crypto) = self.crypto.as_ref() { + pkt.source_buf_mut().extend_end(TAG_LEN); + return crypto.encrypt_in_place(pkt); + } + Ok(()) + } + pub(crate) fn decrypt_in_place + AsMut<[u8]> + ShrinkEnd>( + &self, + pkt: &mut NetPacket, + ) -> io::Result<()> { + if let Some(crypto) = self.crypto.as_ref() { + let _ = crypto.decrypt_in_place(pkt)?; + pkt.source_buf_mut().shrink_end(TAG_LEN); + } + Ok(()) + } +} diff --git a/vnt-core/src/enhanced_tunnel/inbound.rs b/vnt-core/src/enhanced_tunnel/inbound.rs new file mode 100644 index 0000000..9f97926 --- /dev/null +++ b/vnt-core/src/enhanced_tunnel/inbound.rs @@ -0,0 +1,72 @@ +use crate::context::{NetworkAddr, TrafficStats}; +use crate::enhanced_tunnel::quic_over::quic_inbound::EnhancedQuicInbound; +use crate::nat::internal_nat::InternalNatInbound; +use crate::protocol::ip_packet_protocol::{HEAD_LENGTH, MsgType, NetPacket}; +use crate::protocol::transmission::TransmissionBytes; +use crate::tun::enhanced_tun::EnhancedTunInbound; +use anyhow::{Context, bail}; +use pnet_packet::ipv4::Ipv4Packet; +use std::net::Ipv4Addr; + +#[derive(Clone)] +pub(crate) struct EnhancedInbound { + tun_data_inbound: EnhancedTunInbound, + quic_inbound: EnhancedQuicInbound, + internal_nat_inbound: Option, + traffic_stats: TrafficStats, +} + +impl EnhancedInbound { + pub fn new( + tun_data_inbound: EnhancedTunInbound, + quic_inbound: EnhancedQuicInbound, + internal_nat_inbound: Option, + traffic_stats: TrafficStats, + ) -> Self { + Self { + tun_data_inbound, + quic_inbound, + internal_nat_inbound, + traffic_stats, + } + } + pub async fn inbound( + &self, + network_addr: &NetworkAddr, + msg_type: MsgType, + src: Ipv4Addr, + packet: NetPacket, + ) -> anyhow::Result<()> { + let mut buf = packet.into_buffer(); + self.traffic_stats.record_rx(src, buf.len() as u64); + buf.advance_head(HEAD_LENGTH)?; + + match msg_type { + MsgType::Turn => { + if let Some(internal_nat_inbound) = self.internal_nat_inbound.as_ref() { + let Some(ipv4) = Ipv4Packet::new(&buf) else { + bail!("EnhancedInbound not ipv4") + }; + let dest = ipv4.get_destination(); + if dest != network_addr.ip && !network_addr.network().contains(&dest) { + internal_nat_inbound.send(&buf, network_addr).await?; + return Ok(()); + } + } + self.tun_data_inbound.inbound(buf, network_addr).await?; + } + MsgType::Broadcast | MsgType::ExcludeBroadcast => { + self.tun_data_inbound.inbound(buf, network_addr).await?; + } + MsgType::Quic => { + let payload = buf.into_bytes().freeze(); + self.quic_inbound + .inbound(payload, src) + .await + .context("inbound quic")?; + } + _ => {} + } + Ok(()) + } +} diff --git a/vnt-core/src/enhanced_tunnel/mod.rs b/vnt-core/src/enhanced_tunnel/mod.rs new file mode 100644 index 0000000..3021bc3 --- /dev/null +++ b/vnt-core/src/enhanced_tunnel/mod.rs @@ -0,0 +1,75 @@ +use crate::context::AppState; +use crate::enhanced_tunnel::inbound::EnhancedInbound; +use crate::enhanced_tunnel::outbound::EnhancedOutbound; +use crate::nat::SubnetExternalRoute; +use crate::nat::internal_nat::{InternalNatInbound, PortMappingManager}; +use crate::port_mapping::PortMapping; +use crate::tun::enhanced_tun::EnhancedTunInbound; +use crate::tunnel_core::outbound::HybridOutbound; +use crate::utils::task_control::TaskGroup; + +pub(crate) mod quic_over; + +pub(crate) mod inbound; +pub(crate) mod outbound; + +pub(crate) struct TunnelConfig { + pub mtu: u16, + pub password: Option, + pub open_quic_client: bool, + pub port_mapping: Vec, +} + +pub(crate) struct TunnelComponents { + pub hybrid_outbound: HybridOutbound, + pub external_route: SubnetExternalRoute, + pub internal_nat_inbound: Option, + pub port_mapping_manager: PortMappingManager, +} + +pub(crate) async fn enhanced_ipv4_tunnel( + app_state: AppState, + task_group: TaskGroup, + tun_data_sender: EnhancedTunInbound, + config: TunnelConfig, + components: TunnelComponents, +) -> anyhow::Result<(EnhancedInbound, Option)> { + let password = config.password.unwrap_or_else(|| "password".to_string()); + let tun = match &tun_data_sender { + EnhancedTunInbound::Tun(tun) => Some(tun.clone()), + EnhancedTunInbound::Nat(_) => None, + }; + let (inbound, outbound) = quic_over::boot::quic_tunnel_start( + app_state.clone(), + task_group, + tun, + quic_over::boot::QuicTunnelConfig { + mtu: config.mtu, + password, + open_quic_client: config.open_quic_client, + port_mapping: config.port_mapping, + }, + quic_over::boot::QuicTunnelComponents { + hybrid_outbound: components.hybrid_outbound.clone(), + external_route: components.external_route, + internal_nat_manager: components.internal_nat_inbound.clone(), + port_mapping_manager: components.port_mapping_manager, + }, + ) + .await?; + let enhanced_inbound = EnhancedInbound::new( + tun_data_sender, + inbound, + components.internal_nat_inbound, + app_state.traffic_stats.clone(), + ); + + let enhanced_outbound = outbound.map(|outbound| { + EnhancedOutbound::new( + app_state.network.clone(), + outbound, + components.hybrid_outbound, + ) + }); + Ok((enhanced_inbound, enhanced_outbound)) +} diff --git a/vnt-core/src/enhanced_tunnel/outbound.rs b/vnt-core/src/enhanced_tunnel/outbound.rs new file mode 100644 index 0000000..8059374 --- /dev/null +++ b/vnt-core/src/enhanced_tunnel/outbound.rs @@ -0,0 +1,68 @@ +use crate::context::SharedNetworkAddr; +use crate::enhanced_tunnel::quic_over::quic_outbound::EnhancedQuicOutbound; +use crate::protocol::transmission::TransmissionBytes; +use crate::tunnel_core::outbound::HybridOutbound; +use pnet_packet::ipv4::Ipv4Packet; + +pub struct EnhancedOutbound { + network: SharedNetworkAddr, + enhanced_quic_outbound: EnhancedQuicOutbound, + hybrid_outbound: HybridOutbound, +} + +impl EnhancedOutbound { + pub fn new( + network: SharedNetworkAddr, + enhanced_quic_outbound: EnhancedQuicOutbound, + hybrid_outbound: HybridOutbound, + ) -> Self { + Self { + network, + enhanced_quic_outbound, + hybrid_outbound, + } + } + pub async fn ipv4_outbound(&self, data: TransmissionBytes) { + if data.is_empty() || data[0] >> 4 != 4 { + return; + } + if let Err(e) = self.ipv4_outbound_impl(data).await { + log::warn!("EnhancedOutbound error: {:?}", e); + } + } + async fn ipv4_outbound_impl(&self, data: TransmissionBytes) -> anyhow::Result<()> { + let Some(ipv4) = Ipv4Packet::new(data.as_ref()) else { + return Ok(()); + }; + let Some(net) = self.network.get() else { + return Ok(()); + }; + let src = ipv4.get_source(); + + let dest = ipv4.get_destination(); + if dest == src || dest.is_unspecified() { + return Ok(()); + } + if dest == net.gateway { + // 发送到网关 + return self.hybrid_outbound.ipv4_gateway_outbound(net, data).await; + } + if dest.is_multicast() || dest == net.broadcast || dest.is_broadcast() { + // 广播 + return self + .hybrid_outbound + .ipv4_broadcast_outbound(net, data) + .await; + } + if self + .enhanced_quic_outbound + .outbound(&net, data.as_ref()) + .await + { + // 使用quic 通道传输 + return Ok(()); + } + // 使用通用通道传输 + self.hybrid_outbound.ipv4_outbound(net, data).await + } +} diff --git a/vnt-core/src/enhanced_tunnel/quic_over/boot.rs b/vnt-core/src/enhanced_tunnel/quic_over/boot.rs new file mode 100644 index 0000000..ad2a297 --- /dev/null +++ b/vnt-core/src/enhanced_tunnel/quic_over/boot.rs @@ -0,0 +1,200 @@ +use crate::context::AppState; +use crate::enhanced_tunnel::quic_over::enhanced_io::enhanced_inbound::{ + QuicDataInbound, create_enhanced_inbound, +}; +use crate::enhanced_tunnel::quic_over::enhanced_io::enhanced_outbound::create_enhanced_outbound; +use crate::enhanced_tunnel::quic_over::enhanced_io::socket::ExtendedQuicSocket; +use crate::enhanced_tunnel::quic_over::quic_client::QuicTunnelClient; +use crate::enhanced_tunnel::quic_over::quic_inbound::EnhancedQuicInbound; +use crate::enhanced_tunnel::quic_over::quic_outbound::EnhancedQuicOutbound; +use crate::enhanced_tunnel::quic_over::{quic_client, quic_server}; +use crate::nat::SubnetExternalRoute; +use crate::nat::internal_nat::{InternalNatInbound, PortMappingManager}; +use crate::port_mapping::PortMapping; +use crate::tls; +use crate::tun::TunDataInbound; +use crate::tunnel_core::outbound::HybridOutbound; +use crate::utils::task_control::TaskGroup; +use anyhow::Context; +use quinn::congestion::BbrConfig; +use quinn::crypto::rustls::QuicServerConfig; +use quinn::{ClientConfig, Endpoint, EndpointConfig, TransportConfig, default_runtime}; +use rustls::ServerConfig; +use sha2::{Digest, Sha256}; +use std::io; +use std::sync::Arc; +use std::time::Duration; +use tcp_ip::{IpStackConfig, IpStackRecv}; + +pub(crate) struct QuicTunnelConfig { + pub mtu: u16, + pub password: String, + pub open_quic_client: bool, + pub port_mapping: Vec, +} + +pub(crate) struct QuicTunnelComponents { + pub hybrid_outbound: HybridOutbound, + pub external_route: SubnetExternalRoute, + pub internal_nat_manager: Option, + pub port_mapping_manager: PortMappingManager, +} + +pub(crate) async fn quic_tunnel_start( + app_state: AppState, + task_group: TaskGroup, + tun_data_sender: Option, + config: QuicTunnelConfig, + components: QuicTunnelComponents, +) -> anyhow::Result<(EnhancedQuicInbound, Option)> { + let ip_stack_config = IpStackConfig { + mtu: config.mtu, + ..Default::default() + }; + let (ip_stack, ip_socket, quic_outbound) = if let Some(tun_data_sender) = tun_data_sender { + let (ip_stack, ip_stack_send, ip_stack_recv) = tcp_ip::ip_stack(ip_stack_config)?; + let ip_socket = tcp_ip::ip::IpSocket::bind_all(None, ip_stack.clone()).await?; + let ip_socket = Arc::new(ip_socket); + task_group.spawn(ip_stack_recv_task( + ip_stack_recv, + app_state.clone(), + tun_data_sender, + )); + let quic_outbound = + EnhancedQuicOutbound::new(config.open_quic_client, ip_stack_send, ip_stack.clone()); + + (Some(ip_stack), Some(ip_socket), Some(quic_outbound)) + } else { + (None, None, None) + }; + + let (inbound, endpoint) = create_quic_endpoint( + config.password, + task_group.clone(), + components.hybrid_outbound, + ) + .await?; + quic_server::server_listen( + &task_group, + endpoint.clone(), + ip_socket.clone(), + ip_stack.clone(), + components.internal_nat_manager, + components.port_mapping_manager, + ) + .await; + if config.open_quic_client { + let quic_client = + QuicTunnelClient::new(app_state.clone(), endpoint, components.external_route); + + // 客户端使用指纹验证 + if let (Some(ip_stack), Some(ip_socket)) = (ip_stack, ip_socket) { + quic_client::create_client( + quic_client.clone(), + task_group.clone(), + ip_stack.clone(), + ip_socket, + ) + .await; + } + if !config.port_mapping.is_empty() { + crate::port_mapping::port_mapping_start(&task_group, config.port_mapping, quic_client) + .await?; + } + } else if !config.port_mapping.is_empty() { + let quic_client = + QuicTunnelClient::new(app_state.clone(), endpoint, components.external_route); + + crate::port_mapping::port_mapping_start(&task_group, config.port_mapping, quic_client) + .await?; + } + + let quic_inbound = EnhancedQuicInbound::new(inbound); + Ok((quic_inbound, quic_outbound)) +} +async fn create_quic_endpoint( + password: String, + task_group: TaskGroup, + hybrid_outbound: HybridOutbound, +) -> anyhow::Result<(QuicDataInbound, Endpoint)> { + let (cert, private_key) = crate::tls::cert::generate_deterministic_cert(&password)?; + let mut hasher = Sha256::new(); + hasher.update(cert.as_ref()); + let calculated_hash: [u8; 32] = hasher.finalize().into(); + log::info!("QUIC Cert Fingerprint: {}", hex::encode(calculated_hash)); + + let outbound = create_enhanced_outbound(task_group.clone(), hybrid_outbound).await; + let (inbound, inbound_receiver) = create_enhanced_inbound(); + let socket = ExtendedQuicSocket::new(inbound_receiver, outbound); + + let server_config = ServerConfig::builder() + .with_no_client_auth() + .with_single_cert(vec![cert], private_key) + .context("TLS config error")?; + + let server_crypto = QuicServerConfig::try_from(server_config) + .map_err(|e| anyhow::anyhow!("QUIC TLS config error: {:?}", e))?; + let server_config = quinn::ServerConfig::with_crypto(Arc::new(server_crypto)); + // 替换运行时 + let runtime = default_runtime().ok_or_else(|| io::Error::other("no async runtime found"))?; + let fingerprint_verifier = tls::verifier::FingerprintVerifier::new(calculated_hash); + + let client_config = rustls::ClientConfig::builder() + .dangerous() + .with_custom_certificate_verifier(Arc::new(fingerprint_verifier)) + .with_no_client_auth(); + let mut client_config = ClientConfig::new(Arc::new( + quinn::crypto::rustls::QuicClientConfig::try_from(client_config) + .context("Failed to create QUIC client config")?, + )); + client_config.transport_config(build_transport_config()); + let mut endpoint_config = EndpointConfig::default(); + endpoint_config.max_udp_payload_size(1300)?; + let mut endpoint = quinn::Endpoint::new_with_abstract_socket( + endpoint_config, + Some(server_config), + Arc::new(socket), + runtime, + ) + .context("quic server create failed")?; + endpoint.set_default_client_config(client_config); + Ok((inbound, endpoint)) +} + +fn build_transport_config() -> Arc { + let mut transport = TransportConfig::default(); + transport.congestion_controller_factory(Arc::new(BbrConfig::default())); + transport.keep_alive_interval(Some(Duration::from_secs(5))); + + transport.max_idle_timeout(Some(Duration::from_secs(10).try_into().unwrap())); + + Arc::new(transport) +} + +async fn ip_stack_recv_task( + mut ip_stack_recv: IpStackRecv, + app_state: AppState, + tun_data_sender: TunDataInbound, +) { + let mut buf = vec![0u8; 1500]; + loop { + let len = match ip_stack_recv.recv(&mut buf).await { + Ok(len) => len, + Err(e) => { + log::error!("IP stack recv error: {:?}", e); + break; + } + }; + let Some(net) = app_state.get_network() else { + log::error!("not network"); + break; + }; + match tun_data_sender.send((&buf[..len]).into(), &net).await { + Ok(_) => {} + Err(e) => { + log::error!("IP stack send error: {:?}", e); + break; + } + } + } +} diff --git a/vnt-core/src/enhanced_tunnel/quic_over/enhanced_io/enhanced_inbound.rs b/vnt-core/src/enhanced_tunnel/quic_over/enhanced_io/enhanced_inbound.rs new file mode 100644 index 0000000..ae6183c --- /dev/null +++ b/vnt-core/src/enhanced_tunnel/quic_over/enhanced_io/enhanced_inbound.rs @@ -0,0 +1,93 @@ +use anyhow::anyhow; +use bytes::Bytes; +use parking_lot::Mutex; +use quinn::udp::RecvMeta; +use std::fmt::{Debug, Formatter}; +use std::io::IoSliceMut; +use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; +use std::sync::Arc; +use std::task::{Context, Poll}; +use tokio::sync::mpsc::{Receiver, Sender}; + +#[derive(Clone)] +pub struct QuicInnerInboundReceiver { + receiver: Arc>>, +} +#[derive(Clone)] +pub struct QuicDataInbound { + sender: Sender<(Bytes, Ipv4Addr)>, +} +impl QuicDataInbound { + pub async fn send(&self, data: Bytes, addr: Ipv4Addr) -> anyhow::Result<()> { + self.sender + .send((data, addr)) + .await + .map_err(|_e| anyhow!("quic data inbound error")) + } +} +impl Debug for QuicInnerInboundReceiver { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("EnhancedInbound").finish() + } +} +pub fn create_enhanced_inbound() -> (QuicDataInbound, QuicInnerInboundReceiver) { + let (sender, receiver) = tokio::sync::mpsc::channel(256); + ( + QuicDataInbound { sender }, + QuicInnerInboundReceiver::new(receiver), + ) +} +impl QuicInnerInboundReceiver { + pub fn new(receiver: Receiver<(Bytes, Ipv4Addr)>) -> Self { + Self { + receiver: Arc::new(Mutex::new(receiver)), + } + } + + pub fn poll_recv( + &self, + cx: &mut Context, + bufs: &mut [IoSliceMut<'_>], + meta: &mut [RecvMeta], + ) -> Poll> { + let mut guard = self.receiver.lock(); + let rs = guard.poll_recv(cx); + drop(guard); + match rs { + Poll::Ready(Some((buf, ip))) => { + let (buf_mut, meta) = match (bufs.get_mut(0), meta.get_mut(0)) { + (Some(b), Some(m)) => (b, m), + _ => { + return Poll::Ready(Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "no buffer available", + ))); + } + }; + + if buf_mut.len() < buf.len() { + return Poll::Ready(Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + format!( + "buffer too small: need {}, got {}", + buf.len(), + buf_mut.len() + ), + ))); + } + + buf_mut[..buf.len()].copy_from_slice(&buf); + + meta.len = buf.len(); + meta.stride = buf.len(); + meta.addr = SocketAddr::V4(SocketAddrV4::new(ip, 10000)); + Poll::Ready(Ok(1)) + } + Poll::Ready(None) => Poll::Ready(Err(std::io::Error::new( + std::io::ErrorKind::BrokenPipe, + "inbound channel closed", + ))), + Poll::Pending => Poll::Pending, + } + } +} diff --git a/vnt-core/src/enhanced_tunnel/quic_over/enhanced_io/enhanced_outbound.rs b/vnt-core/src/enhanced_tunnel/quic_over/enhanced_io/enhanced_outbound.rs new file mode 100644 index 0000000..a37f1f6 --- /dev/null +++ b/vnt-core/src/enhanced_tunnel/quic_over/enhanced_io/enhanced_outbound.rs @@ -0,0 +1,89 @@ +use crate::protocol::ip_packet_protocol::{HEAD_LENGTH, MsgType, NetPacket}; +use crate::protocol::transmission::TransmissionBytes; +use crate::tunnel_core::outbound::HybridOutbound; +use crate::utils::task_control::TaskGroup; +use quinn::UdpPoller; +use std::fmt::{Debug, Formatter}; +use std::io; +use std::net::Ipv4Addr; +use std::pin::Pin; +use std::task::{Context, Poll}; +use tokio::sync::mpsc::{Sender, error::TrySendError}; +use tokio_util::sync::PollSender; + +#[derive(Clone)] +pub struct QuicInnerOutbound { + sender: Sender<(Ipv4Addr, NetPacket)>, +} + +pub async fn create_enhanced_outbound( + task_group: TaskGroup, + hybrid_outbound: HybridOutbound, +) -> QuicInnerOutbound { + let (s, mut r) = tokio::sync::mpsc::channel(256); + + task_group.spawn(async move { + while let Some((dst, packet)) = r.recv().await { + if let Err(e) = hybrid_outbound.outbound_raw(dst, packet).await { + log::debug!("outbound error: {e:?}, dst={dst}"); + } + } + }); + + QuicInnerOutbound { sender: s } +} + +impl QuicInnerOutbound { + pub fn try_outbound(&self, buf: &[u8], dest: Ipv4Addr) -> io::Result<()> { + let send = match self.sender.try_reserve() { + Ok(send) => send, + Err(TrySendError::Full(_)) => { + return Err(io::Error::new( + io::ErrorKind::WouldBlock, + "outbound channel full", + )); + } + Err(TrySendError::Closed(_)) => { + return Err(io::Error::new( + io::ErrorKind::BrokenPipe, + "outbound channel closed", + )); + } + }; + let mut packet = NetPacket::new(TransmissionBytes::zeroed(HEAD_LENGTH + buf.len()))?; + packet.set_ttl(5); + packet.set_msg_type(MsgType::Quic); + packet.set_dest_id(dest.into()); + packet.set_payload(buf)?; + send.send((dest, packet)); + Ok(()) + } + pub fn create_io_poller(&self) -> Pin> { + Box::pin(EnhancedOutboundPoller { + sender: PollSender::new(self.sender.clone()), + }) + } +} +pub struct EnhancedOutboundPoller { + sender: PollSender<(Ipv4Addr, NetPacket)>, +} +impl Debug for EnhancedOutboundPoller { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("EnhancedOutboundPoller").finish() + } +} +impl UdpPoller for EnhancedOutboundPoller { + fn poll_writable(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + match self.sender.poll_reserve(cx) { + Poll::Ready(Ok(_)) => { + self.sender.abort_send(); + Poll::Ready(Ok(())) + } + Poll::Ready(Err(_e)) => Poll::Ready(Err(io::Error::new( + io::ErrorKind::BrokenPipe, + "outbound channel closed", + ))), + Poll::Pending => Poll::Pending, + } + } +} diff --git a/vnt-core/src/enhanced_tunnel/quic_over/enhanced_io/mod.rs b/vnt-core/src/enhanced_tunnel/quic_over/enhanced_io/mod.rs new file mode 100644 index 0000000..1f9ab80 --- /dev/null +++ b/vnt-core/src/enhanced_tunnel/quic_over/enhanced_io/mod.rs @@ -0,0 +1,3 @@ +pub mod enhanced_inbound; +pub mod enhanced_outbound; +pub mod socket; diff --git a/vnt-core/src/enhanced_tunnel/quic_over/enhanced_io/socket.rs b/vnt-core/src/enhanced_tunnel/quic_over/enhanced_io/socket.rs new file mode 100644 index 0000000..d466614 --- /dev/null +++ b/vnt-core/src/enhanced_tunnel/quic_over/enhanced_io/socket.rs @@ -0,0 +1,55 @@ +use crate::enhanced_tunnel::quic_over::enhanced_io::enhanced_inbound::QuicInnerInboundReceiver; +use crate::enhanced_tunnel::quic_over::enhanced_io::enhanced_outbound::QuicInnerOutbound; +use quinn::udp::{RecvMeta, Transmit}; +use quinn::{AsyncUdpSocket, UdpPoller}; +use std::fmt::{Debug, Formatter}; +use std::io::IoSliceMut; +use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; + +pub struct ExtendedQuicSocket { + inbound: QuicInnerInboundReceiver, + outbound: QuicInnerOutbound, +} +impl Debug for ExtendedQuicSocket { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("QuicSocket").finish() + } +} +impl ExtendedQuicSocket { + pub fn new(inbound: QuicInnerInboundReceiver, outbound: QuicInnerOutbound) -> Self { + Self { inbound, outbound } + } +} + +impl AsyncUdpSocket for ExtendedQuicSocket { + fn create_io_poller(self: Arc) -> Pin> { + self.outbound.create_io_poller() + } + + fn try_send(&self, transmit: &Transmit) -> std::io::Result<()> { + let IpAddr::V4(dest) = transmit.destination.ip() else { + return Ok(()); + }; + + self.outbound.try_outbound(transmit.contents, dest) + } + + fn poll_recv( + &self, + cx: &mut Context, + bufs: &mut [IoSliceMut<'_>], + meta: &mut [RecvMeta], + ) -> Poll> { + self.inbound.poll_recv(cx, bufs, meta) + } + + fn local_addr(&self) -> std::io::Result { + Ok(SocketAddr::V4(SocketAddrV4::new( + Ipv4Addr::new(127, 0, 0, 1), + 10000, + ))) + } +} diff --git a/vnt-core/src/enhanced_tunnel/quic_over/mod.rs b/vnt-core/src/enhanced_tunnel/quic_over/mod.rs new file mode 100644 index 0000000..71031bd --- /dev/null +++ b/vnt-core/src/enhanced_tunnel/quic_over/mod.rs @@ -0,0 +1,7 @@ +mod enhanced_io; +pub(crate) mod quic_client; +pub(crate) mod quic_inbound; +pub(crate) mod quic_outbound; +mod quic_server; + +pub(crate) mod boot; diff --git a/vnt-core/src/enhanced_tunnel/quic_over/quic_client.rs b/vnt-core/src/enhanced_tunnel/quic_over/quic_client.rs new file mode 100644 index 0000000..cc3feca --- /dev/null +++ b/vnt-core/src/enhanced_tunnel/quic_over/quic_client.rs @@ -0,0 +1,309 @@ +use crate::context::AppState; + +use crate::nat::SubnetExternalRoute; +use crate::protocol::client_message::{ + IpProxyHandshake, QuicProxyHandshake, TcpProxyHandshake, quic_proxy_handshake, +}; +use crate::utils::task_control::TaskGroup; +use anyhow::{Context, bail}; +use bytes::Bytes; +use futures::SinkExt; +use parking_lot::Mutex; +use pnet_packet::ip::IpNextHeaderProtocol; +use prost::Message; +use quinn::{Connection, Endpoint, RecvStream, SendStream}; +use std::collections::HashMap; +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use std::sync::Arc; +use tcp_ip::IpStack; +use tcp_ip::ip::IpSocket; +use tcp_ip::tcp::TcpStream; +use tokio::io::AsyncWriteExt; +use tokio::sync::OnceCell; +use tokio::sync::mpsc::Sender; +use tokio::sync::mpsc::error::TrySendError; +use tokio_util::codec::{FramedWrite, LengthDelimitedCodec}; + +#[derive(Clone)] +pub struct QuicTunnelClient { + app_state: AppState, + endpoint: Endpoint, + connection_map: Arc>>>>, + external_route: SubnetExternalRoute, +} + +impl QuicTunnelClient { + pub fn new( + app_state: AppState, + endpoint: Endpoint, + external_route: SubnetExternalRoute, + ) -> QuicTunnelClient { + Self { + app_state, + endpoint, + connection_map: Arc::new(Default::default()), + external_route, + } + } + + pub async fn open_bi(&self, mut dest: Ipv4Addr) -> anyhow::Result<(SendStream, RecvStream)> { + let Some(net) = self.app_state.get_network() else { + bail!("no network found"); + }; + if !net.network().contains(&dest) { + if let Some(v) = self.external_route.route(&dest) { + dest = v; + } else { + bail!("invalid route found:{dest}"); + } + } + let mut count = 0; + loop { + count += 1; + let cell = self + .connection_map + .lock() + .entry(dest) + .or_insert_with(|| Arc::new(OnceCell::new())) + .clone(); + let connection = cell + .get_or_try_init(|| async { + self.endpoint + .connect(SocketAddr::new(dest.into(), 10000), "localhost")? + .await + .context("connect failed") + }) + .await?; + + return match connection.open_bi().await { + Ok(rs) => Ok(rs), + Err(e) => { + self.connection_map.lock().remove(&dest); + if count == 1 { + continue; + } + Err(e.into()) + } + }; + } + } + pub async fn open_uni(&self, mut dest: Ipv4Addr) -> anyhow::Result { + let Some(net) = self.app_state.get_network() else { + bail!("no network found"); + }; + if !net.network().contains(&dest) { + if let Some(v) = self.external_route.route(&dest) { + dest = v; + } else { + bail!("invalid route found:{dest}"); + } + } + let mut count = 0; + loop { + count += 1; + let cell = self + .connection_map + .lock() + .entry(dest) + .or_insert_with(|| Arc::new(OnceCell::new())) + .clone(); + let connection = cell + .get_or_try_init(|| async { + self.endpoint + .connect(SocketAddr::new(dest.into(), 10000), "localhost")? + .await + .context("connect failed") + }) + .await?; + return match connection.open_uni().await { + Ok(rs) => Ok(rs), + Err(e) => { + self.connection_map.lock().remove(&dest); + if count == 1 { + continue; + } + Err(e.into()) + } + }; + } + } +} +pub(crate) async fn send_handshake( + send_stream: &mut SendStream, + handshake: QuicProxyHandshake, +) -> anyhow::Result<()> { + let handshake = handshake.encode_to_vec(); + send_stream.write_u16(handshake.len() as u16).await?; + send_stream.write_all(&handshake).await?; + Ok(()) +} + +pub async fn create_client( + quic_client: QuicTunnelClient, + task_group: TaskGroup, + ip_stack: IpStack, + ip_socket: Arc, +) { + task_group.spawn(tcp_listen( + task_group.clone(), + ip_stack.clone(), + quic_client.clone(), + )); + task_group.spawn(ip_listen(task_group.clone(), ip_socket, quic_client)); +} + +async fn tcp_listen( + task_group: TaskGroup, + ip_stack: IpStack, + quic_tunnel_client: QuicTunnelClient, +) { + if let Err(e) = tcp_listen_impl(task_group, ip_stack, quic_tunnel_client).await { + log::error!("tcp_listen {e:?}"); + } +} + +async fn tcp_listen_impl( + task_group: TaskGroup, + ip_stack: IpStack, + quic_tunnel_client: QuicTunnelClient, +) -> anyhow::Result<()> { + let mut listener = tcp_ip::tcp::TcpListener::bind_all(ip_stack).await?; + loop { + let (tcp_stream, addr) = listener.accept().await?; + let quic_tunnel_client = quic_tunnel_client.clone(); + task_group.spawn(async move { + if let Err(e) = tcp_stream_handle(tcp_stream, quic_tunnel_client).await { + log::error!("TCP stream handle failed with error: {e:?},addr={addr}"); + } + }); + } +} + +async fn tcp_stream_handle( + tcp_stream: TcpStream, + quic_tunnel_client: QuicTunnelClient, +) -> anyhow::Result<()> { + // 连接方向是反过来的,因为自己充当目标做了tcp卸载 + let SocketAddr::V4(peer_addr) = tcp_stream.local_addr()? else { + bail!("invalid IP address"); + }; + let SocketAddr::V4(local_addr) = tcp_stream.peer_addr()? else { + bail!("invalid IP address"); + }; + log::debug!("connect TCP stream {}->{}", local_addr, peer_addr); + + let (mut send_stream, mut recv_stream) = quic_tunnel_client.open_bi(*peer_addr.ip()).await?; + let handshake = QuicProxyHandshake { + handshake: Some(quic_proxy_handshake::Handshake::Tcp(TcpProxyHandshake { + src_ip: (*local_addr.ip()).into(), + src_port: local_addr.port().into(), + dst_ip: (*peer_addr.ip()).into(), + dst_port: peer_addr.port().into(), + })), + }; + send_handshake(&mut send_stream, handshake).await?; + let (mut tcp_w, mut tcp_r) = tcp_stream.split()?; + tokio::select! { + _ = tokio::io::copy(&mut recv_stream, &mut tcp_w) => {}, + _ = tokio::io::copy(&mut tcp_r, &mut send_stream) => {}, + } + log::debug!("disconnect TCP stream {}->{}", local_addr, peer_addr); + Ok(()) +} + +async fn ip_listen( + task_group: TaskGroup, + ip_socket: Arc, + quic_tunnel_client: QuicTunnelClient, +) { + if let Err(e) = ip_listen_impl(task_group, ip_socket, quic_tunnel_client).await { + log::error!("ip_listen {e:?}"); + } +} +#[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)] +struct IpKey { + protocol: IpNextHeaderProtocol, + src: Ipv4Addr, + dest: Ipv4Addr, +} +async fn ip_listen_impl( + task_group: TaskGroup, + ip_socket: Arc, + quic_tunnel_client: QuicTunnelClient, +) -> anyhow::Result<()> { + let mut buf = vec![0u8; 65536]; + let dest_map = Arc::new(Mutex::new(HashMap::>::new())); + + loop { + let (len, protocol, src, dest) = ip_socket.recv_protocol_from_to(&mut buf).await?; + let (IpAddr::V4(src), IpAddr::V4(dest)) = (src, dest) else { + continue; + }; + let key = IpKey { + protocol, + src, + dest, + }; + let bytes = Bytes::copy_from_slice(&buf[..len]); + + let tx = { + let mut map = dest_map.lock(); + if let Some(tx) = map.get(&key) { + tx.clone() + } else { + let (tx, rx) = tokio::sync::mpsc::channel::(128); + + spawn_dest_sender(task_group.clone(), key, rx, quic_tunnel_client.clone()); + + map.insert(key, tx.clone()); + tx + } + }; + + if let Err(err) = tx.try_send(bytes) { + match err { + TrySendError::Full(_) => {} + TrySendError::Closed(_) => { + let mut map = dest_map.lock(); + map.remove(&key); + } + } + } + } +} + +fn spawn_dest_sender( + task_group: TaskGroup, + key: IpKey, + mut rx: tokio::sync::mpsc::Receiver, + quic_tunnel_client: QuicTunnelClient, +) { + log::info!("send ip({}) packet {}->{}", key.protocol, key.src, key.dest); + task_group.spawn(async move { + let result = async { + let mut send_stream = quic_tunnel_client.open_uni(key.dest).await?; + + let handshake = QuicProxyHandshake { + handshake: Some(quic_proxy_handshake::Handshake::Ip(IpProxyHandshake { + ip_next_header_protocol: key.protocol.0 as _, + src_ip: key.src.into(), + dst_ip: key.dest.into(), + })), + }; + send_handshake(&mut send_stream, handshake).await?; + + let mut framed = FramedWrite::new(send_stream, LengthDelimitedCodec::new()); + + while let Some(pkt) = rx.recv().await { + framed.send(pkt).await?; + } + + Ok::<(), anyhow::Error>(()) + } + .await; + + if let Err(e) = result { + log::error!("key {:?} sender task exit: {:?}", key, e); + } + }); +} diff --git a/vnt-core/src/enhanced_tunnel/quic_over/quic_inbound.rs b/vnt-core/src/enhanced_tunnel/quic_over/quic_inbound.rs new file mode 100644 index 0000000..7aa8736 --- /dev/null +++ b/vnt-core/src/enhanced_tunnel/quic_over/quic_inbound.rs @@ -0,0 +1,18 @@ +use crate::enhanced_tunnel::quic_over::enhanced_io::enhanced_inbound::QuicDataInbound; +use bytes::Bytes; +use std::net::Ipv4Addr; + +#[derive(Clone)] +pub struct EnhancedQuicInbound { + quic_data_inbound: QuicDataInbound, +} + +impl EnhancedQuicInbound { + pub fn new(quic_data_inbound: QuicDataInbound) -> Self { + Self { quic_data_inbound } + } + pub async fn inbound(&self, data: Bytes, src: Ipv4Addr) -> anyhow::Result<()> { + self.quic_data_inbound.send(data, src).await?; + Ok(()) + } +} diff --git a/vnt-core/src/enhanced_tunnel/quic_over/quic_outbound.rs b/vnt-core/src/enhanced_tunnel/quic_over/quic_outbound.rs new file mode 100644 index 0000000..11c212f --- /dev/null +++ b/vnt-core/src/enhanced_tunnel/quic_over/quic_outbound.rs @@ -0,0 +1,94 @@ +use crate::context::NetworkAddr; +use pnet_packet::Packet; +use pnet_packet::ip::IpNextHeaderProtocols; +use pnet_packet::ipv4::{Ipv4Flags, Ipv4Packet}; +use pnet_packet::tcp::TcpFlags::{ACK, SYN}; +use pnet_packet::tcp::TcpPacket; +use std::net::SocketAddr; +use tcp_ip::{IpStack, IpStackSend}; + +pub struct EnhancedQuicOutbound { + open_quic_client: bool, + ip_stack_send: IpStackSend, + ip_stack: IpStack, +} + +impl EnhancedQuicOutbound { + pub fn new(open_quic_client: bool, ip_stack_send: IpStackSend, ip_stack: IpStack) -> Self { + Self { + open_quic_client, + ip_stack_send, + ip_stack, + } + } + pub async fn outbound(&self, _net: &NetworkAddr, data: &[u8]) -> bool { + let Some(ipv4) = Ipv4Packet::new(data) else { + return true; + }; + + if self.open_quic_client { + // 针对tcp 如果不是从IpStack建立的连接,则不使用IpStack解析 + if ipv4.get_next_level_protocol() == IpNextHeaderProtocols::Tcp { + let more_fragments = + ipv4.get_flags() & Ipv4Flags::MoreFragments == Ipv4Flags::MoreFragments; + let offset = ipv4.get_fragment_offset(); + let segmented = more_fragments || offset > 0; + if !segmented { + let Some(tcp) = TcpPacket::new(ipv4.payload()) else { + return true; + }; + // 不是第一个包 + if !(tcp.get_flags() & SYN == SYN && tcp.get_flags() & ACK != ACK) { + let local_addr = + SocketAddr::new(ipv4.get_source().into(), tcp.get_source()); + let peer_addr = + SocketAddr::new(ipv4.get_destination().into(), tcp.get_destination()); + // 在IpStack中找不到连接 + if !self + .ip_stack + .has_tcp_connection(local_addr, peer_addr) + .unwrap_or(false) + && !self + .ip_stack + .has_tcp_connection(peer_addr, local_addr) + .unwrap_or(false) + && !self + .ip_stack + .has_tcp_half_open(peer_addr, local_addr) + .unwrap_or(false) + { + return false; + } + } + } + } + _ = self.ip_stack_send.send_ip_packet(data).await; + return true; + } + // 判断tcp流 + if ipv4.get_next_level_protocol() == IpNextHeaderProtocols::Tcp { + let more_fragments = + ipv4.get_flags() & Ipv4Flags::MoreFragments == Ipv4Flags::MoreFragments; + let offset = ipv4.get_fragment_offset(); + let segmented = more_fragments || offset > 0; + if !segmented && let Some(tcp) = TcpPacket::new(ipv4.payload()) { + // 如果对端使用IpStack连接了自己,则也需要原路回复 + // 这是连接回复,所以方向是和流方向相反的 + let peer_addr = SocketAddr::new(ipv4.get_source().into(), tcp.get_source()); + let local_addr = + SocketAddr::new(ipv4.get_destination().into(), tcp.get_destination()); + + if self + .ip_stack + .has_tcp_connection(local_addr, peer_addr) + .unwrap_or(false) + { + _ = self.ip_stack_send.send_ip_packet(data).await; + return true; + } + } + } + + false + } +} diff --git a/vnt-core/src/enhanced_tunnel/quic_over/quic_server.rs b/vnt-core/src/enhanced_tunnel/quic_over/quic_server.rs new file mode 100644 index 0000000..90dfc6e --- /dev/null +++ b/vnt-core/src/enhanced_tunnel/quic_over/quic_server.rs @@ -0,0 +1,245 @@ +use crate::nat::internal_nat::{InternalNatInbound, PortMappingManager}; +use crate::protocol::client_message::QuicProxyHandshake; +use crate::protocol::client_message::quic_proxy_handshake::Handshake; +use crate::utils::task_control::TaskGroup; +use anyhow::{Context, bail}; +use futures::StreamExt; +use pnet_packet::ip::IpNextHeaderProtocol; +use prost::Message; +use quinn::{Connection, Endpoint, RecvStream, SendStream}; +use std::net::{Ipv4Addr, SocketAddr}; +use std::sync::Arc; +use tcp_ip::IpStack; +use tcp_ip::ip::IpSocket; +use tokio::io::AsyncReadExt; +use tokio_util::codec::{FramedRead, LengthDelimitedCodec}; + +pub async fn server_listen( + task_group: &TaskGroup, + endpoint: Endpoint, + ip_socket: Option>, + ip_stack: Option, + internal_nat_manager: Option, + port_mapping_manager: PortMappingManager, +) { + task_group.spawn(quic_endpoint_accept( + ip_stack, + task_group.clone(), + endpoint, + ip_socket, + internal_nat_manager, + port_mapping_manager, + )); +} + +async fn quic_endpoint_accept( + ip_stack: Option, + task_group: TaskGroup, + endpoint: Endpoint, + ip_socket: Option>, + internal_nat_manager: Option, + port_mapping_manager: PortMappingManager, +) { + while let Some(connecting) = endpoint.accept().await { + let remote_addr = connecting.remote_address(); + let task_group_clone = task_group.clone(); + let ip_socket = ip_socket.clone(); + let ip_stack = ip_stack.clone(); + let internal_nat_manager = internal_nat_manager.clone(); + let port_mapping_manager = port_mapping_manager.clone(); + task_group.spawn(async move { + match connecting.await { + Ok(connection) => { + log::info!("QUIC connection: {}", remote_addr); + if let Err(e) = quic_accept( + ip_stack, + task_group_clone, + connection, + ip_socket, + internal_nat_manager, + port_mapping_manager, + ) + .await + { + log::info!("quic close: {remote_addr},{e:?}",); + } + } + Err(e) => { + log::error!("connect: {:?},remote_addr={remote_addr}", e); + } + } + }); + } + log::warn!("quic server closed"); +} + +async fn quic_accept( + ip_stack: Option, + task_group_clone: TaskGroup, + connection: Connection, + ip_socket: Option>, + internal_nat_manager: Option, + port_mapping_manager: PortMappingManager, +) -> anyhow::Result<()> { + loop { + tokio::select! { + rs = connection.accept_bi()=>{ + let (send_stream, recv_stream) = rs?; + let ip_stack = ip_stack.clone(); + let internal_nat_manager = internal_nat_manager.clone(); + let port_mapping_manager = port_mapping_manager.clone(); + task_group_clone.spawn(async move { + if let Err(e) = quic_stream_bi_handle(ip_stack,send_stream, recv_stream,&internal_nat_manager,port_mapping_manager).await{ + log::error!("quic_stream_bi_handle: {e:?}"); + } + }); + } + rs = connection.accept_uni()=>{ + let recv_stream = rs?; + let ip_socket = ip_socket.clone(); + let internal_nat_manager = internal_nat_manager.clone(); + task_group_clone.spawn(async move { + if let Err(e) = quic_stream_uni_handle(recv_stream, ip_socket,&internal_nat_manager).await{ + log::error!("quic_stream_uni_handle: {e:?}"); + } + }); + } + } + } +} + +async fn quic_stream_bi_handle( + ip_stack: Option, + mut send_stream: SendStream, + mut recv_stream: RecvStream, + internal_nat_manager: &Option, + port_mapping_manager: PortMappingManager, +) -> anyhow::Result<()> { + let handshake = recv_handshake(&mut recv_stream).await?; + let Some(handshake) = handshake.handshake else { + return Ok(()); + }; + match handshake { + Handshake::Tcp(handshake) => { + let src = SocketAddr::new( + Ipv4Addr::from(handshake.src_ip).into(), + handshake.src_port as _, + ); + let dst_ip = Ipv4Addr::from(handshake.dst_ip); + let dst = SocketAddr::new(dst_ip.into(), handshake.dst_port as _); + if src == dst { + bail!("tcp handshake failed, ip: {}", src); + } + log::debug!("accept TCP stream {src}->{dst}"); + // 如果不是网段内的,并且启用了内置nat,则直接转发 + if let Some(internal_nat_manager) = internal_nat_manager { + if internal_nat_manager.use_nat(&dst_ip) { + internal_nat_manager + .tcp_nat(recv_stream, send_stream, dst_ip, dst.port()) + .await?; + return Ok(()); + } + if internal_nat_manager.no_tun() { + return Ok(()); + } + } + if let Some(ip_stack) = ip_stack { + let stream = tcp_ip::tcp::TcpStream::bind(ip_stack, src)? + .connect_to(dst) + .await?; + let (mut tcp_w, mut tcp_r) = stream.split()?; + tokio::select! { + _ = tokio::io::copy(&mut recv_stream, &mut tcp_w) => {}, + _ = tokio::io::copy(&mut tcp_r, &mut send_stream) => {}, + } + log::debug!("accept close TCP stream {src}->{dst}"); + } + } + Handshake::Ip(_) => {} + Handshake::TcpPortMapping(handshake) => { + port_mapping_manager + .tcp_mapping( + recv_stream, + send_stream, + handshake.dst_host, + handshake.dst_port as _, + ) + .await?; + } + Handshake::UdpPortMapping(handshake) => { + port_mapping_manager + .udp_mapping( + recv_stream, + send_stream, + handshake.dst_host, + handshake.dst_port as _, + ) + .await?; + } + } + Ok(()) +} + +async fn recv_handshake(recv_stream: &mut RecvStream) -> anyhow::Result { + let len = recv_stream.read_u16().await?; + let mut buf = vec![0u8; len as usize]; + recv_stream.read_exact(&mut buf).await?; + let handshake = QuicProxyHandshake::decode(&buf[..])?; + Ok(handshake) +} +async fn quic_stream_uni_handle( + mut recv_stream: RecvStream, + ip_socket: Option>, + internal_nat_manager: &Option, +) -> anyhow::Result<()> { + let handshake = recv_handshake(&mut recv_stream).await?; + let Some(handshake) = handshake.handshake else { + return Ok(()); + }; + match handshake { + Handshake::Tcp(_) => {} + Handshake::Ip(handshake) => { + let ip_next_header_protocol = + IpNextHeaderProtocol::new(handshake.ip_next_header_protocol as _); + let src_ip = Ipv4Addr::from(handshake.src_ip); + let dest_ip = Ipv4Addr::from(handshake.dst_ip); + log::debug!("recv IP({ip_next_header_protocol}) packet {src_ip}->{dest_ip}"); + let mut framed_read = FramedRead::new(recv_stream, LengthDelimitedCodec::new()); + // 如果不是网段内的,并且启用了内置nat,则直接转发 + if let Some(internal_nat_manager) = internal_nat_manager { + if internal_nat_manager.use_nat(&dest_ip) { + loop { + let buf = framed_read + .next() + .await + .context("receive quic stream failed")??; + internal_nat_manager + .send_ipv4_payload(ip_next_header_protocol, src_ip, dest_ip, buf) + .await?; + } + } + if internal_nat_manager.no_tun() { + return Ok(()); + } + } + let Some(ip_socket) = ip_socket else { + return Ok(()); + }; + let src_ip = src_ip.into(); + let dest_ip = dest_ip.into(); + loop { + let buf = framed_read + .next() + .await + .context("receive quic stream failed")??; + + ip_socket + .send_protocol_from_to(&buf, ip_next_header_protocol, src_ip, dest_ip) + .await?; + } + } + Handshake::TcpPortMapping(_) => {} + Handshake::UdpPortMapping(_) => {} + } + Ok(()) +} diff --git a/vnt-core/src/fec/decoder.rs b/vnt-core/src/fec/decoder.rs new file mode 100644 index 0000000..576a4f5 --- /dev/null +++ b/vnt-core/src/fec/decoder.rs @@ -0,0 +1,302 @@ +use crate::fec::encoder::FecPacket; +use crate::protocol::ip_packet_protocol::{HEAD_LENGTH, NetPacket}; +use crate::protocol::transmission::TransmissionBytes; +use anyhow::{Result, bail}; +use prost::Message; +use reed_solomon_erasure::galois_8::ReedSolomon; +use std::collections::HashMap; +use std::net::Ipv4Addr; +use std::sync::Arc; +use std::time::{Duration, Instant}; + +const GROUP_TIMEOUT: Duration = Duration::from_secs(3); +const MAX_GROUPS: usize = 1000; +const MAX_NUM: usize = 50; + +#[derive(Clone)] +pub struct FecDecoder { + inner: Arc>, +} + +struct FecDecoderInner { + groups: HashMap<(Ipv4Addr, u64), FecGroup>, + last_cleanup: Instant, +} + +struct FecGroup { + data_shards: usize, + parity_shards: usize, + received_original_count: usize, + received_shards: Vec>>, + last_update: Instant, +} +impl FecGroup { + fn is_done(&self) -> bool { + self.data_shards != 0 && self.received_original_count == self.data_shards + } + fn done(&mut self) { + self.received_original_count = self.data_shards; + self.received_shards = vec![]; + } +} +impl Default for FecGroup { + fn default() -> Self { + Self { + data_shards: 0, + parity_shards: 0, + received_original_count: 0, + received_shards: Vec::with_capacity(16), + last_update: Instant::now(), + } + } +} + +impl FecDecoder { + pub fn new() -> Self { + Self { + inner: Arc::new(parking_lot::Mutex::new(FecDecoderInner { + groups: HashMap::new(), + last_cleanup: Instant::now(), + })), + } + } + + /// 接收FEC包并尝试恢复丢失的包 + pub fn receive( + &self, + net_packet: NetPacket, + ) -> Result>>> { + let mut inner = self.inner.lock(); + let src_ip = Ipv4Addr::from(net_packet.src_id()); + let fec_packet = FecPacket::decode(net_packet.payload())?; + + let group_id = fec_packet.group_id; + let packet_index = fec_packet.packet_index as usize; + let payload = fec_packet.payload; + + if packet_index > MAX_NUM { + log::warn!( + "packet_index overflow, src={src_ip},group_id={group_id}, packet_index={packet_index}", + ); + bail!("packet_index overflow {src_ip}"); + } + let mut packet = None; + let group = inner.groups.entry((src_ip, group_id)).or_default(); + if group.is_done() { + return Ok(None); + } + if group + .received_shards + .get(packet_index) + .is_some_and(|v| v.is_some()) + { + return Ok(None); + } + if let Some(parity_data) = fec_packet.parity_data { + let data_shards = parity_data.data_shards as usize; + let parity_shards = parity_data.parity_shards as usize; + if data_shards > MAX_NUM { + bail!("data_shards overflow {src_ip}"); + } + if parity_shards > MAX_NUM { + bail!("parity_shards overflow {src_ip}"); + } + + if data_shards + parity_shards <= packet_index { + log::warn!( + "packet_index overflow in parity, src={},group_id={}, packet_index={}, total_shards={}", + src_ip, + group_id, + packet_index, + data_shards + parity_shards + ); + bail!("packet_index overflow {src_ip}"); + } + if group.data_shards != 0 && group.data_shards != data_shards { + bail!("group data_shards!=data_shards {src_ip}"); + } + if group.parity_shards != 0 && group.parity_shards != parity_shards { + bail!("group parity_shards!=parity_shards {src_ip}"); + } + group.data_shards = data_shards; + group.parity_shards = parity_shards; + if group.received_shards.len() < data_shards + parity_shards { + group + .received_shards + .resize(data_shards + parity_shards, None); + } + + group.received_shards[packet_index] = Some(payload); + } else { + let buffer = TransmissionBytes::zeroed(HEAD_LENGTH + payload.len()); + let mut result_packet = NetPacket::new(buffer)?; + result_packet.head_mut().copy_from_slice(net_packet.head()); + result_packet.set_fec_flag(false); + result_packet.set_payload(&payload)?; + packet = Some(result_packet); + + if group.received_shards.len() <= packet_index { + group.received_shards.resize(packet_index + 1, None); + } + + // 保存FEC数据: [type_byte, flags_byte, payload_len(u16), payload...] + let type_byte = net_packet.head()[0]; + let flags_byte = net_packet.head()[2]; + let mut batch_data = vec![0u8; 4 + payload.len()]; + batch_data[0] = type_byte; + batch_data[1] = flags_byte; + batch_data[2..4].copy_from_slice(&(payload.len() as u16).to_be_bytes()); + batch_data[4..].copy_from_slice(&payload); + group.received_shards[packet_index] = Some(batch_data); + group.received_original_count += 1; + } + group.last_update = Instant::now(); + + if group.is_done() { + group.done(); + if inner.last_cleanup.elapsed() > Duration::from_secs(1) { + Self::cleanup_old_groups(&mut inner.groups); + inner.last_cleanup = Instant::now(); + } + return Ok(packet.map(|v| vec![v])); + } + + let result = Self::try_decode(group, (src_ip, group_id), &net_packet)?; + + if inner.last_cleanup.elapsed() > Duration::from_secs(1) { + Self::cleanup_old_groups(&mut inner.groups); + inner.last_cleanup = Instant::now(); + } + match (packet, result) { + (Some(packet), Some(mut result)) => { + result.push(packet); + Ok(Some(result)) + } + (Some(packet), None) => Ok(Some(vec![packet])), + (None, Some(result)) => Ok(Some(result)), + (None, None) => Ok(None), + } + } + + /// 检查是否可以恢复丢失的包 + fn try_decode( + group: &mut FecGroup, + key: (Ipv4Addr, u64), + net_packet: &NetPacket, + ) -> Result>>> { + if group.data_shards == 0 { + return Ok(None); + } + if group.received_original_count == group.data_shards { + return Ok(None); + } + let received_count = group.received_shards.iter().filter(|s| s.is_some()).count(); + + if received_count < group.data_shards { + return Ok(None); + } + + Self::decode_with_rs(group, key, net_packet) + } + + /// Reed-Solomon解码恢复丢失的包 + fn decode_with_rs( + group: &mut FecGroup, + key: (Ipv4Addr, u64), + net_packet: &NetPacket, + ) -> Result>>> { + let (src_ip, group_id) = key; + + if group.received_shards.len() != group.data_shards + group.parity_shards { + bail!( + "received_shards.len()({}) != data_shards({})+parity_shards({}) src_ip={src_ip},group_id={group_id}", + group.received_shards.len(), + group.data_shards, + group.parity_shards, + ) + } + + let mut delivered_packets = vec![false; group.data_shards]; + for (index, x) in group.received_shards[..group.data_shards] + .iter() + .enumerate() + { + if x.is_some() { + delivered_packets[index] = true; + } + } + + let rs = ReedSolomon::new(group.data_shards, group.parity_shards)?; + rs.reconstruct(&mut group.received_shards)?; + + let mut result = Vec::new(); + for (i, shard) in group + .received_shards + .iter() + .enumerate() + .take(group.data_shards) + { + if let Some(shard) = shard { + if delivered_packets[i] { + continue; + } + + let net_packet = Self::rebuild_net_packet(net_packet, shard)?; + result.push(net_packet); + } + } + + group.done(); + + Ok(Some(result)) + } + + /// 从恢复的数据重建NetPacket + fn rebuild_net_packet( + current_packet: &NetPacket, + recovered_data: &[u8], + ) -> Result> { + if recovered_data.len() < 4 { + bail!("recovered_data too short"); + } + + let type_byte = recovered_data[0]; + let flags_byte = recovered_data[1]; + let payload_len = u16::from_be_bytes([recovered_data[2], recovered_data[3]]) as usize; + + if payload_len + 4 > recovered_data.len() { + bail!("invalid payload_len in recovered_data"); + } + + let payload = &recovered_data[4..4 + payload_len]; + + let buffer = TransmissionBytes::zeroed(HEAD_LENGTH + payload.len()); + let mut net_packet = NetPacket::new(buffer)?; + + net_packet.head_mut()[0] = type_byte; + net_packet.head_mut()[2] = flags_byte; + net_packet.set_src_id(current_packet.src_id()); + net_packet.set_dest_id(current_packet.dest_id()); + net_packet.set_ttl(current_packet.ttl()); + net_packet.set_fec_flag(false); + net_packet.set_payload(payload)?; + + Ok(net_packet) + } + + fn cleanup_old_groups(groups: &mut HashMap<(Ipv4Addr, u64), FecGroup>) { + let now = Instant::now(); + + groups.retain(|_, group| now.duration_since(group.last_update) < GROUP_TIMEOUT); + + if groups.len() > MAX_GROUPS { + let mut group_ids: Vec<_> = groups.iter().map(|(id, g)| (*id, g.last_update)).collect(); + group_ids.sort_by_key(|(_, last_update)| *last_update); + + let to_remove = group_ids.len() - MAX_GROUPS; + for (group_id, _) in group_ids.iter().take(to_remove) { + groups.remove(group_id); + } + } + } +} diff --git a/vnt-core/src/fec/encoder.rs b/vnt-core/src/fec/encoder.rs new file mode 100644 index 0000000..0f7eeab --- /dev/null +++ b/vnt-core/src/fec/encoder.rs @@ -0,0 +1,247 @@ +use crate::protocol::ip_packet_protocol::{HEAD_LENGTH, MsgType, NetPacket}; +use crate::protocol::transmission::TransmissionBytes; +use crate::tunnel_core::outbound::BasicOutbound; +use anyhow::{Result, bail}; +use parking_lot::Mutex; +use prost::Message; +use reed_solomon_erasure::galois_8::ReedSolomon; +use std::collections::HashMap; +use std::net::Ipv4Addr; +use std::sync::Arc; +use std::time::{Duration, Instant}; +use tokio::sync::mpsc; + +mod fec_proto { + include!(concat!(env!("OUT_DIR"), "/protocol.fec.rs")); +} +use crate::utils::task_control::TaskGroup; +pub use fec_proto::FecPacket; + +const BATCH_SIZE: usize = 10; +const REDUNDANCY_RATE: f32 = 0.2; +const BATCH_TIMEOUT_MS: u64 = 20; +const MIN_PARITY: usize = 1; +const BATCH_CHANNEL_SIZE: usize = 1024; + +#[derive(Clone)] +pub struct FecEncoder { + batch_states: Arc>>, + batch_tx: mpsc::Sender<(Ipv4Addr, Ipv4Addr, u64, Vec)>, +} + +struct DestBatchState { + group_id: u64, + current_batch: Vec, + deadline: Instant, + src_ip: Ipv4Addr, +} + +impl FecEncoder { + pub fn new(task_group: &TaskGroup, basic_outbound: BasicOutbound) -> Self { + let (batch_tx, batch_rx) = mpsc::channel(BATCH_CHANNEL_SIZE); + let batch_states = Arc::new(Mutex::new(HashMap::new())); + let encoder = Self { + batch_states: batch_states.clone(), + batch_tx, + }; + + task_group.spawn(fec_encoder_worker(batch_rx, basic_outbound, batch_states)); + + encoder + } + + /// 将数据包加入FEC批次并返回包装后的包 + pub fn encode( + &self, + mut packet: NetPacket, + ) -> Result> { + let src_ip = Ipv4Addr::from(packet.src_id()); + let dest = Ipv4Addr::from(packet.dest_id()); + if packet.payload().len() > u16::MAX as usize { + bail!("Payload too big"); + } + let original_payload = packet.payload().to_vec(); + let original_payload_len = original_payload.len(); + + let type_byte = packet.head()[0]; + let flags_byte = packet.head()[2]; + + // 组装FEC数据: [type_byte, flags_byte, payload_len(u16), payload...] + let batch_len = 4 + original_payload_len; + let mut batch_buffer = TransmissionBytes::zeroed(batch_len); + batch_buffer[0] = type_byte; + batch_buffer[1] = flags_byte; + batch_buffer[2..4].copy_from_slice(&(original_payload_len as u16).to_be_bytes()); + batch_buffer[4..batch_len].copy_from_slice(&original_payload); + + let (group_id, packet_index) = { + let mut states = self.batch_states.lock(); + let state = states.entry(dest).or_insert_with(|| DestBatchState { + group_id: 0, + current_batch: Vec::with_capacity(BATCH_SIZE), + deadline: Instant::now() + Duration::from_millis(BATCH_TIMEOUT_MS), + src_ip, + }); + + let group_id = state.group_id; + let packet_index = state.current_batch.len(); + + state.current_batch.push(batch_buffer); + + if state.current_batch.len() >= BATCH_SIZE { + let batch = std::mem::take(&mut state.current_batch); + state.group_id += 1; + state.deadline = Instant::now() + Duration::from_millis(BATCH_TIMEOUT_MS); + + if self + .batch_tx + .try_send((src_ip, dest, group_id, batch)) + .is_err() + { + log::warn!( + "failed to send batch to worker (channel full), dest={}, group_id={}", + dest, + group_id + ); + } + } + + (group_id, packet_index) + }; + + let fec_packet = FecPacket { + group_id, + packet_index: packet_index as u32, + payload: original_payload, + parity_data: None, + }; + + let fec_payload = fec_packet.encode_to_vec(); + packet + .source_buf_mut() + .resize(HEAD_LENGTH + fec_payload.len(), 0); + packet.set_payload(&fec_payload)?; + packet.set_fec_flag(true); + + Ok(packet) + } +} + +/// 后台worker,处理满批次和超时批次 +async fn fec_encoder_worker( + mut batch_rx: mpsc::Receiver<(Ipv4Addr, Ipv4Addr, u64, Vec)>, + basic_outbound: BasicOutbound, + batch_states: Arc>>, +) { + let mut timer = tokio::time::interval(Duration::from_millis(5)); + + loop { + tokio::select! { + Some((src,dest, group_id, mut items)) = batch_rx.recv() => { + if let Err(e) = encode_and_send_parity(src,dest, group_id, &mut items, &basic_outbound).await { + log::warn!("encode_and_send_parity error for {} group {}: {:?}", dest, group_id, e); + } + } + + _ = timer.tick() => { + let now = Instant::now(); + + let timeout_batches = { + let mut states = batch_states.lock(); + let mut batches = Vec::new(); + for (dest, state) in states.iter_mut() { + if !state.current_batch.is_empty() && now >= state.deadline { + let items = std::mem::take(&mut state.current_batch); + let group_id = state.group_id; + state.group_id += 1; + state.deadline = Instant::now() + Duration::from_millis(BATCH_TIMEOUT_MS); + batches.push((state.src_ip,*dest, group_id, items)); + } + } + batches + }; + + for (src,dest, group_id, mut items) in timeout_batches { + if let Err(e) = encode_and_send_parity(src, dest, group_id, &mut items, &basic_outbound).await { + log::warn!("encode_and_send_parity timeout error for {} group {}: {:?}", dest, group_id, e); + } + } + } + } + } +} + +/// Reed-Solomon编码并发送冗余包 +async fn encode_and_send_parity( + src: Ipv4Addr, + dest: Ipv4Addr, + group_id: u64, + items: &mut Vec, + basic_outbound: &BasicOutbound, +) -> Result<()> { + if items.is_empty() { + return Ok(()); + } + + let data_shards = items.len(); + let parity_shards = (data_shards as f32 * REDUNDANCY_RATE).ceil() as usize; + let parity_shards = parity_shards.max(MIN_PARITY); + + let max_len = items.iter().map(|buf| buf.len()).max().unwrap_or(0); + + if max_len == 0 { + log::warn!("max_len is 0, dest={}, group_id={}", dest, group_id); + return Ok(()); + } + + for buf in items.iter_mut() { + if buf.len() < max_len { + let padding = max_len - buf.len(); + buf.extend_end(padding); + } + } + + for _ in 0..parity_shards { + items.push(TransmissionBytes::zeroed(max_len)); + } + + let rs = ReedSolomon::new(data_shards, parity_shards)?; + let mut shard_refs: Vec<&mut [u8]> = items.iter_mut().map(|buf| buf.as_mut()).collect(); + rs.encode(&mut shard_refs)?; + + for (i, parity_buf) in items[data_shards..].iter().enumerate() { + let packet_index = (data_shards + i) as u32; + let fec_packet = FecPacket { + group_id, + packet_index, + payload: parity_buf.as_ref().to_vec(), + parity_data: Some(fec_proto::ParityData { + data_shards: data_shards as u32, + parity_shards: parity_shards as u32, + }), + }; + + let fec_payload = fec_packet.encode_to_vec(); + + let buffer = TransmissionBytes::zeroed(HEAD_LENGTH + fec_payload.len()); + let mut net_packet = NetPacket::new(buffer)?; + net_packet.set_msg_type(MsgType::Turn); + net_packet.set_src_id(src.into()); + net_packet.set_dest_id(dest.into()); + net_packet.set_ttl(5); + net_packet.set_payload(&fec_payload)?; + net_packet.set_fec_flag(true); + + if let Err(e) = basic_outbound.send_encrypted_packet(dest, net_packet).await { + log::warn!( + "failed to send parity packet {}: {:?}, dest={}, group_id={}", + packet_index, + e, + dest, + group_id + ); + } + } + + Ok(()) +} diff --git a/vnt-core/src/fec/mod.rs b/vnt-core/src/fec/mod.rs new file mode 100644 index 0000000..5640696 --- /dev/null +++ b/vnt-core/src/fec/mod.rs @@ -0,0 +1,5 @@ +mod decoder; +mod encoder; + +pub(crate) use decoder::FecDecoder; +pub(crate) use encoder::FecEncoder; diff --git a/vnt-core/src/lib.rs b/vnt-core/src/lib.rs new file mode 100644 index 0000000..33f4c45 --- /dev/null +++ b/vnt-core/src/lib.rs @@ -0,0 +1,15 @@ +pub(crate) mod compression; +pub mod context; +pub mod core; +pub mod crypto; +pub(crate) mod fec; +pub mod nat; +pub mod protocol; +pub mod tls; +pub(crate) mod tun; +pub mod tunnel_core; +pub mod utils; + +pub mod api; +pub(crate) mod enhanced_tunnel; +pub mod port_mapping; diff --git a/vnt-core/src/nat/internal_nat/icmp_nat.rs b/vnt-core/src/nat/internal_nat/icmp_nat.rs new file mode 100644 index 0000000..826895f --- /dev/null +++ b/vnt-core/src/nat/internal_nat/icmp_nat.rs @@ -0,0 +1,139 @@ +use crate::context::SharedNetworkAddr; +use crate::utils::task_control::TaskGroup; +use anyhow::Context; +use pnet_packet::Packet; +use pnet_packet::icmp::echo_reply::{Identifier, SequenceNumber}; +use pnet_packet::icmp::{IcmpPacket, IcmpTypes}; +use pnet_packet::ipv4::Ipv4Packet; +use std::collections::HashMap; +use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}; +use tcp_ip::IpStack; +use tcp_ip::icmp::IcmpSocket; +use tokio::net::UdpSocket; + +pub async fn start_icmp_nat( + task_group: &TaskGroup, + ip_stack: &IpStack, + no_tun: bool, + network: SharedNetworkAddr, +) -> anyhow::Result<()> { + let net_icmp_socket = socket2::Socket::new( + socket2::Domain::IPV4, + socket2::Type::RAW, + Some(socket2::Protocol::ICMPV4), + ) + .context("new Socket RAW ICMPV4 failed")?; + let addr: SocketAddrV4 = SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0); + net_icmp_socket + .bind(&socket2::SockAddr::from(addr)) + .context("bind Socket ICMPV4 failed")?; + net_icmp_socket.set_nonblocking(true)?; + + let std_socket: std::net::UdpSocket = net_icmp_socket.into(); + + let tokio_icmp_socket = UdpSocket::from_std(std_socket)?; + + let inner_icmp_socket = IcmpSocket::bind_all(ip_stack.clone()).await?; + task_group.spawn(async move { + if let Err(e) = task(tokio_icmp_socket, inner_icmp_socket, no_tun, network).await { + log::error!("icmp task failed: {:?}", e); + } + }); + Ok(()) +} +async fn task( + tokio_icmp_socket: UdpSocket, + inner_icmp_socket: IcmpSocket, + no_tun: bool, + network: SharedNetworkAddr, +) -> anyhow::Result<()> { + let mut buf1 = vec![0u8; 65536]; + let mut buf2 = vec![0u8; 65536]; + let mut map = HashMap::new(); + loop { + tokio::select! { + rs = tokio_icmp_socket.recv(&mut buf1) => { + let len = rs?; + tokio_icmp_socket_recv(&buf1[..len],&inner_icmp_socket,&map,no_tun,&network).await?; + } + rs = inner_icmp_socket.recv_from_to(&mut buf2) => { + let (len,src,dst) = rs?; + inner_icmp_socket_recv(&buf2[..len],src,dst,&tokio_icmp_socket,&mut map,no_tun,&network).await?; + } + } + } +} +async fn tokio_icmp_socket_recv( + buf: &[u8], + inner_icmp_socket: &IcmpSocket, + map: &HashMap<(Ipv4Addr, Identifier, SequenceNumber), Ipv4Addr>, + no_tun: bool, + network: &SharedNetworkAddr, +) -> anyhow::Result<()> { + let Some(ipv4) = Ipv4Packet::new(buf) else { + return Ok(()); + }; + let Some(icmp) = IcmpPacket::new(ipv4.payload()) else { + return Ok(()); + }; + if icmp.get_icmp_type() != IcmpTypes::EchoReply + && icmp.get_icmp_type() != IcmpTypes::EchoRequest + { + return Ok(()); + } + let payload = icmp.payload(); + if payload.len() < 4 { + return Ok(()); + } + let mut src = ipv4.get_source(); + let identifier = Identifier::new(u16::from_be_bytes([payload[0], payload[1]])); + let sequence_number = SequenceNumber::new(u16::from_be_bytes([payload[2], payload[3]])); + let Some(dst) = map.get(&(src, identifier, sequence_number)) else { + return Ok(()); + }; + if no_tun && src == Ipv4Addr::LOCALHOST { + src = network.ip().context("not ip")?; + } + + inner_icmp_socket + .send_from_to(ipv4.payload(), src.into(), (*dst).into()) + .await + .context("sending ICMPv4 failed")?; + Ok(()) +} +async fn inner_icmp_socket_recv( + buf: &[u8], + src: IpAddr, + dst: IpAddr, + tokio_icmp_socket: &UdpSocket, + map: &mut HashMap<(Ipv4Addr, Identifier, SequenceNumber), Ipv4Addr>, + no_tun: bool, + network: &SharedNetworkAddr, +) -> anyhow::Result<()> { + let (IpAddr::V4(src), IpAddr::V4(mut dst)) = (src, dst) else { + return Ok(()); + }; + let Some(icmp) = IcmpPacket::new(buf) else { + return Ok(()); + }; + if icmp.get_icmp_type() != IcmpTypes::EchoReply + && icmp.get_icmp_type() != IcmpTypes::EchoRequest + { + return Ok(()); + } + let payload = icmp.payload(); + if payload.len() < 4 { + return Ok(()); + } + if no_tun && dst == network.ip().context("not ip")? { + dst = Ipv4Addr::LOCALHOST; + } + + let identifier = Identifier::new(u16::from_be_bytes([payload[0], payload[1]])); + let sequence_number = SequenceNumber::new(u16::from_be_bytes([payload[2], payload[3]])); + map.insert((dst, identifier, sequence_number), src); + tokio_icmp_socket + .send_to(buf, SocketAddr::new(dst.into(), 0)) + .await?; + Ok(()) +} diff --git a/vnt-core/src/nat/internal_nat/mod.rs b/vnt-core/src/nat/internal_nat/mod.rs new file mode 100644 index 0000000..ffcfa3d --- /dev/null +++ b/vnt-core/src/nat/internal_nat/mod.rs @@ -0,0 +1,225 @@ +use crate::context::{NetworkAddr, SharedNetworkAddr}; +use crate::nat::AllowSubnetExternalRoute; +use crate::protocol::ip_packet_protocol::HEAD_LENGTH; +use crate::protocol::transmission::TransmissionBytes; +use crate::tunnel_core::outbound::HybridOutbound; +use crate::utils::task_control::TaskGroup; +use anyhow::Context; +use bytes::BytesMut; +use pnet_packet::ip::IpNextHeaderProtocol; +use pnet_packet::ipv4::Ipv4Packet; +use std::net::{Ipv4Addr, SocketAddr}; +use std::str::FromStr; +use std::sync::Arc; +use tcp_ip::{IpStackConfig, IpStackRecv, IpStackSend}; +use tokio::io::{AsyncRead, AsyncWrite}; + +#[cfg(not(target_os = "android"))] +mod icmp_nat; +mod tcp_nat; +mod udp_nat; +#[derive(Clone)] +pub(crate) struct InternalNatInbound { + no_tun: bool, + ip_stack_send: Arc, + allow_subnet: AllowSubnetExternalRoute, + network: SharedNetworkAddr, +} +impl InternalNatInbound { + pub async fn create( + task_group: &TaskGroup, + mtu: u16, + hybrid_outbound: HybridOutbound, + allow_subnet: AllowSubnetExternalRoute, + network: SharedNetworkAddr, + no_tun: bool, + ) -> anyhow::Result { + let ip_stack_config = IpStackConfig { + mtu, + ..Default::default() + }; + let (ip_stack, ip_stack_send, ip_stack_recv) = tcp_ip::ip_stack(ip_stack_config)?; + #[cfg(not(target_os = "android"))] + icmp_nat::start_icmp_nat(task_group, &ip_stack, no_tun, network.clone()).await?; + tcp_nat::start_tcp_nat(task_group, &ip_stack, no_tun, network.clone()).await?; + udp_nat::start_udp_nat(task_group, &ip_stack).await?; + task_group.spawn(async move { + if let Err(e) = ip_stack_recv_task(ip_stack_recv, hybrid_outbound).await { + log::error!("ip stack recv task error: {e:?}"); + } + }); + Ok(Self { + no_tun, + ip_stack_send: Arc::new(ip_stack_send), + allow_subnet, + network, + }) + } + pub async fn send(&self, data: &[u8], net: &NetworkAddr) -> anyhow::Result<()> { + if data[0] >> 4 != 4 { + return Ok(()); + } + let Some(ipv4) = Ipv4Packet::new(data) else { + return Ok(()); + }; + let dest = ipv4.get_destination(); + if net.network().contains(&dest) + || dest == net.broadcast + || dest.is_broadcast() + || dest.is_multicast() + || self.allow_subnet.allow(&dest) + { + self.ip_stack_send.send_ip_packet(data).await?; + } + Ok(()) + } + pub async fn send_ipv4_payload( + &self, + protocol: IpNextHeaderProtocol, + src_ip: Ipv4Addr, + dest_ip: Ipv4Addr, + payload: BytesMut, + ) -> anyhow::Result<()> { + self.ip_stack_send + .send_ipv4_payload(protocol, src_ip, dest_ip, payload) + .await?; + Ok(()) + } +} + +async fn ip_stack_recv_task( + mut ip_stack_recv: IpStackRecv, + hybrid_outbound: HybridOutbound, +) -> anyhow::Result<()> { + loop { + let mut bytes = TransmissionBytes::new_offset_zeroed(HEAD_LENGTH); + let len = ip_stack_recv.recv(&mut bytes).await?; + bytes.set_len(len)?; + if let Err(e) = hybrid_outbound.ipv4_outbound_common(bytes).await { + log::warn!("ip_stack_recv_task,{e:?}"); + } + } +} + +impl InternalNatInbound { + fn network_contains(&self, ip: &Ipv4Addr) -> bool { + self.network + .network() + .map(|net| net.contains(ip)) + .unwrap_or(false) + } + pub fn use_nat(&self, dst: &Ipv4Addr) -> bool { + if self.no_tun { + return self.allow_nat(dst); + } + if self.network_contains(dst) { + return false; + } + self.allow_subnet.allow(dst) + } + pub fn no_tun(&self) -> bool { + self.no_tun + } + pub fn allow_nat(&self, dst: &Ipv4Addr) -> bool { + self.allow_subnet.allow(dst) || self.network_contains(dst) + } + pub async fn tcp_nat( + &self, + recv_stream: R, + send_stream: W, + mut dest_ip: Ipv4Addr, + dest_port: u16, + ) -> anyhow::Result<()> + where + R: AsyncRead + Unpin, + W: AsyncWrite + Unpin, + { + if self.no_tun { + let net = self.network.get().context("no network")?; + if dest_ip == net.ip { + dest_ip = Ipv4Addr::LOCALHOST; + } else if net.network().contains(&dest_ip) { + return Ok(()); + } + } + let dst = SocketAddr::new(dest_ip.into(), dest_port); + tcp_nat::stream_nat(recv_stream, send_stream, dst).await + } +} + +#[derive(Clone)] +pub(crate) struct PortMappingManager { + no_tun: bool, + allow_port_mapping: bool, + network: SharedNetworkAddr, +} + +impl PortMappingManager { + pub fn new(no_tun: bool, allow_port_mapping: bool, network: SharedNetworkAddr) -> Self { + Self { + no_tun, + allow_port_mapping, + network, + } + } + pub async fn tcp_mapping( + &self, + recv_stream: R, + send_stream: W, + dest: String, + dest_port: u16, + ) -> anyhow::Result<()> + where + R: AsyncRead + Unpin, + W: AsyncWrite + Unpin, + { + if !self.allow_port_mapping { + log::debug!("port mapping not enabled"); + return Ok(()); + } + if self.no_tun + && let Ok(dest_ip) = Ipv4Addr::from_str(&dest) + { + let net = self.network.get().context("no network")?; + + if dest_ip == net.ip { + let dst = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), dest_port); + return tcp_nat::stream_nat(recv_stream, send_stream, dst).await; + } else if net.network().contains(&dest_ip) { + return Ok(()); + } + } + let dst = format!("{}:{}", dest, dest_port); + tcp_nat::stream_nat(recv_stream, send_stream, dst).await + } + pub async fn udp_mapping( + &self, + recv_stream: R, + send_stream: W, + dest: String, + dest_port: u16, + ) -> anyhow::Result<()> + where + R: AsyncRead + Unpin, + W: AsyncWrite + Unpin, + { + if !self.allow_port_mapping { + log::debug!("port mapping not enabled"); + return Ok(()); + } + if self.no_tun + && let Ok(dest_ip) = Ipv4Addr::from_str(&dest) + { + let net = self.network.get().context("no network")?; + + if dest_ip == net.ip { + let dst = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), dest_port); + return udp_nat::stream_nat(recv_stream, send_stream, dst).await; + } else if net.network().contains(&dest_ip) { + return Ok(()); + } + } + let dst = format!("{}:{}", dest, dest_port); + udp_nat::stream_nat(recv_stream, send_stream, dst).await + } +} diff --git a/vnt-core/src/nat/internal_nat/tcp_nat.rs b/vnt-core/src/nat/internal_nat/tcp_nat.rs new file mode 100644 index 0000000..3382985 --- /dev/null +++ b/vnt-core/src/nat/internal_nat/tcp_nat.rs @@ -0,0 +1,81 @@ +use crate::context::SharedNetworkAddr; +use crate::utils::task_control::TaskGroup; +use anyhow::Context; +use std::fmt::Debug; +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use tcp_ip::IpStack; +use tcp_ip::tcp::TcpListener; +use tokio::io::{AsyncRead, AsyncWrite}; +use tokio::net::{TcpStream, ToSocketAddrs}; + +pub async fn start_tcp_nat( + task_group: &TaskGroup, + ip_stack: &IpStack, + no_tun: bool, + network: SharedNetworkAddr, +) -> anyhow::Result<()> { + let tcp_listener = TcpListener::bind_all(ip_stack.clone()).await?; + let group = task_group.clone(); + task_group.spawn(async move { + if let Err(e) = listen_task(&group, tcp_listener, no_tun, network).await { + log::error!("listen task error: {:?}", e); + } + }); + Ok(()) +} + +async fn listen_task( + task_group: &TaskGroup, + mut tcp_listener: TcpListener, + no_tun: bool, + network: SharedNetworkAddr, +) -> anyhow::Result<()> { + loop { + let (stream, _addr) = tcp_listener.accept().await?; + let mut local_addr = stream.local_addr()?; + let peer_addr = stream.peer_addr()?; + if no_tun { + let IpAddr::V4(ip) = local_addr.ip() else { + continue; + }; + if ip == network.ip().context("not ip")? { + // 无tun的情况下写入本机的则写到localhost + local_addr.set_ip(IpAddr::V4(Ipv4Addr::LOCALHOST)); + } + } + task_group.spawn(async move { + if let Err(e) = stream_task(stream, local_addr).await { + log::error!("stream task Error: {:?},{peer_addr}->{local_addr}", e); + } + }); + } +} + +async fn stream_task( + mut inner_stream: tcp_ip::tcp::TcpStream, + addr: SocketAddr, +) -> anyhow::Result<()> { + let mut tokio_stream = TcpStream::connect(addr).await?; + tokio::io::copy_bidirectional(&mut inner_stream, &mut tokio_stream).await?; + Ok(()) +} + +pub(crate) async fn stream_nat( + mut recv_stream: R, + mut send_stream: W, + addr: A, +) -> anyhow::Result<()> +where + R: AsyncRead + Unpin, + W: AsyncWrite + Unpin, +{ + let mut tokio_stream = TcpStream::connect(&addr) + .await + .with_context(|| format!("error connecting to {:?}", addr))?; + let (mut tcp_r, mut tcp_w) = tokio_stream.split(); + tokio::select! { + _ = tokio::io::copy(&mut recv_stream, &mut tcp_w) => {}, + _ = tokio::io::copy(&mut tcp_r, &mut send_stream) => {}, + } + Ok(()) +} diff --git a/vnt-core/src/nat/internal_nat/udp_nat.rs b/vnt-core/src/nat/internal_nat/udp_nat.rs new file mode 100644 index 0000000..1d4d2a6 --- /dev/null +++ b/vnt-core/src/nat/internal_nat/udp_nat.rs @@ -0,0 +1,188 @@ +use crate::utils::task_control::TaskGroup; +use anyhow::Context; +use bytes::Bytes; +use futures::{SinkExt, StreamExt}; +use std::collections::HashMap; +use std::fmt::Debug; +use std::net::SocketAddr; +use std::sync::Arc; +use std::time::{Duration, Instant}; +use tcp_ip::IpStack; +use tokio::io::{AsyncRead, AsyncWrite}; +use tokio::net::ToSocketAddrs; +use tokio::sync::Mutex; +use tokio_util::codec::{FramedRead, FramedWrite, LengthDelimitedCodec}; + +struct NatEntry { + socket: Arc, + last_active: Instant, +} + +type NatTable = Arc>>; + +const NAT_IDLE_TIMEOUT: Duration = Duration::from_secs(60 * 5); +const NAT_GC_INTERVAL: Duration = Duration::from_secs(60); + +pub async fn start_udp_nat(task_group: &TaskGroup, ip_stack: &IpStack) -> anyhow::Result<()> { + let inner_socket = tcp_ip::udp::UdpSocket::bind_all(ip_stack.clone()).await?; + let inner_socket = Arc::new(inner_socket); + let nat_table: NatTable = Arc::new(Mutex::new(HashMap::new())); + let mut buf = vec![0u8; 65536]; + let group = task_group.clone(); + let nat_table_clone = nat_table.clone(); + task_group.spawn(async move { + loop { + let (len, src, dst) = match inner_socket.recv_from_to(&mut buf).await { + Ok(rs) => rs, + Err(e) => { + log::warn!("{e:?}"); + break; + } + }; + + if let Err(e) = + handle_outbound(&group, &inner_socket, &nat_table, src, dst, &buf[..len]).await + { + log::warn!("udp nat outbound error: {e:?}"); + } + } + }); + spawn_nat_gc(task_group, nat_table_clone); + Ok(()) +} + +async fn handle_outbound( + task_group: &TaskGroup, + inner: &Arc, + nat: &NatTable, + src: SocketAddr, + dst: SocketAddr, + packet: &[u8], +) -> anyhow::Result<()> { + let key = (src, dst); + + let socket = { + let mut table = nat.lock().await; + if let Some(entry) = table.get_mut(&key) { + entry.last_active = Instant::now(); + entry.socket.clone() + } else { + // 创建真实 UDP socket + let sock = tokio::net::UdpSocket::bind("0.0.0.0:0").await?; + sock.connect(dst).await?; + let sock = Arc::new(sock); + table.insert( + key, + NatEntry { + socket: sock.clone(), + last_active: Instant::now(), + }, + ); + + // 启动反向转发 + spawn_inbound( + task_group, + inner.clone(), + nat.clone(), + src, + dst, + sock.clone(), + ); + + sock + } + }; + + socket.send(packet).await?; + Ok(()) +} + +fn spawn_inbound( + task_group: &TaskGroup, + inner: Arc, + nat: NatTable, + src: SocketAddr, + dst: SocketAddr, + socket: Arc, +) { + task_group.spawn(async move { + let mut buf = vec![0u8; 65536]; + + loop { + let len = match socket.recv(&mut buf).await { + Ok(n) => n, + Err(_) => break, + }; + + // 反向写回 inner socket + if inner.send_from_to(&buf[..len], dst, src).await.is_err() { + break; + } + + // 更新活跃时间 + if let Some(entry) = nat.lock().await.get_mut(&(src, dst)) { + entry.last_active = Instant::now(); + } + } + + // 回收 NAT + nat.lock().await.remove(&(src, dst)); + }); +} + +fn spawn_nat_gc(task_group: &TaskGroup, nat: NatTable) { + task_group.spawn(async move { + let mut interval = tokio::time::interval(NAT_GC_INTERVAL); + + loop { + interval.tick().await; + + let now = Instant::now(); + let mut table = nat.lock().await; + + table.retain(|(src, dst), entry| { + let alive = now.duration_since(entry.last_active) < NAT_IDLE_TIMEOUT; + if !alive { + log::debug!("udp nat expired: {} -> {}", src, dst); + } + alive + }); + } + }); +} + +pub(crate) async fn stream_nat( + recv_stream: R, + send_stream: W, + addr: A, +) -> anyhow::Result<()> +where + R: AsyncRead + Unpin, + W: AsyncWrite + Unpin, +{ + let udp_socket = tokio::net::UdpSocket::bind("0.0.0.0:0").await?; + udp_socket + .connect(&addr) + .await + .with_context(|| format!("error connecting to {:?}", addr))?; + let mut framed_read = FramedRead::new(recv_stream, LengthDelimitedCodec::new()); + let mut framed_write = FramedWrite::new(send_stream, LengthDelimitedCodec::new()); + let mut buf = vec![0u8; 65536]; + + loop { + tokio::select! { + Some(buf) = framed_read.next() => { + let buf = buf?; + udp_socket.send(&buf).await?; + }, + rs = udp_socket.recv(&mut buf) =>{ + let len = rs?; + framed_write.send(Bytes::copy_from_slice(&buf[..len])).await?; + }, + else => { + break + } + } + } + Ok(()) +} diff --git a/vnt-core/src/nat/mod.rs b/vnt-core/src/nat/mod.rs new file mode 100644 index 0000000..cf8d69e --- /dev/null +++ b/vnt-core/src/nat/mod.rs @@ -0,0 +1,114 @@ +use ipnet::Ipv4Net; +use parking_lot::Mutex; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::fmt; +use std::net::Ipv4Addr; +use std::str::FromStr; +use std::sync::Arc; + +pub(crate) mod internal_nat; + +#[derive(Clone, Debug)] +pub struct NetInput { + pub net: Ipv4Net, + pub target_ip: Ipv4Addr, +} +impl FromStr for NetInput { + type Err = String; + + fn from_str(s: &str) -> Result { + let parts: Vec<&str> = s.split(',').map(|x| x.trim()).collect(); + if parts.len() != 2 { + return Err("格式错误,应为 net,target_ip 例如: 192.168.0.0/24,10.26.0.2".into()); + } + + let net = Ipv4Net::from_str(parts[0]).map_err(|e| format!("网络段格式错误: {}", e))?; + + let target_ip = + Ipv4Addr::from_str(parts[1]).map_err(|e| format!("目标 IP 格式错误: {}", e))?; + + Ok(NetInput { net, target_ip }) + } +} +impl fmt::Display for NetInput { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{},{}", self.net, self.target_ip) + } +} +impl Serialize for NetInput { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for NetInput { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + s.parse().map_err(serde::de::Error::custom) + } +} + +#[derive(Clone, Default)] +pub struct SubnetExternalRoute { + route_table: Arc>>, +} +impl SubnetExternalRoute { + pub fn new(mut route_table: Vec) -> Self { + route_table.sort_by_key(|r| std::cmp::Reverse(r.net.prefix_len())); + SubnetExternalRoute { + route_table: Arc::new(Mutex::new(route_table)), + } + } + pub fn set_route_table(&self, mut route_table: Vec) { + route_table.sort_by_key(|r| std::cmp::Reverse(r.net.prefix_len())); + *self.route_table.lock() = route_table; + } + pub fn route(&self, ip: &Ipv4Addr) -> Option { + let route_table = self.route_table.lock(); + if route_table.is_empty() { + return None; + } + for net in route_table.iter() { + if net.net.contains(ip) { + return Some(net.target_ip); + } + } + None + } + pub fn all_route(&self) -> Vec { + self.route_table.lock().clone() + } + pub fn reset_route(&self, route_table: Vec) { + *self.route_table.lock() = route_table; + } +} + +#[derive(Clone)] +pub struct AllowSubnetExternalRoute { + route_table: Arc>, +} +impl AllowSubnetExternalRoute { + pub fn new(mut route_table: Vec) -> Self { + route_table.sort_by_key(|r| r.prefix_len()); + Self { + route_table: Arc::new(route_table), + } + } + pub fn allow(&self, ip: &Ipv4Addr) -> bool { + if self.route_table.is_empty() { + return false; + } + for net in self.route_table.iter() { + if net.contains(ip) { + return true; + } + } + false + } +} diff --git a/vnt-core/src/port_mapping/mod.rs b/vnt-core/src/port_mapping/mod.rs new file mode 100644 index 0000000..5694310 --- /dev/null +++ b/vnt-core/src/port_mapping/mod.rs @@ -0,0 +1,100 @@ +use crate::enhanced_tunnel::quic_over::quic_client::QuicTunnelClient; +use crate::utils::task_control::TaskGroup; +use pnet_packet::ip::{IpNextHeaderProtocol, IpNextHeaderProtocols}; +use std::fmt; +use std::net::{Ipv4Addr, SocketAddr}; +use std::str::FromStr; + +pub(crate) mod tcp_port_mapping; +pub(crate) mod udp_port_mapping; + +pub(crate) async fn port_mapping_start( + task_group: &TaskGroup, + list: Vec, + quic_tunnel_client: QuicTunnelClient, +) -> anyhow::Result<()> { + tcp_port_mapping::start(task_group, &list, quic_tunnel_client.clone()).await?; + udp_port_mapping::start(task_group, &list, quic_tunnel_client).await?; + Ok(()) +} +#[derive(Debug, Clone)] +pub struct PortMapping { + pub protocol: IpNextHeaderProtocol, + pub src_addr: SocketAddr, + pub virtual_target_ip: Ipv4Addr, + pub dst_host: String, + pub dst_port: u16, +} + +impl fmt::Display for PortMapping { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}://{}-{}-{}:{}", + protocol_to_str(self.protocol), + self.src_addr, + self.virtual_target_ip, + self.dst_host, + self.dst_port + ) + } +} + +impl FromStr for PortMapping { + type Err = String; + + fn from_str(s: &str) -> Result { + let (proto_str, rest) = s.split_once("://").ok_or("missing '://'")?; + + let protocol = + str_to_protocol(proto_str).ok_or_else(|| format!("unknown protocol: {}", proto_str))?; + + let mut parts = rest.splitn(3, '-'); + + let src_addr = parts + .next() + .ok_or("missing src_addr")? + .parse::() + .map_err(|e| format!("invalid src_addr: {}", e))?; + + let virtual_target_ip = parts + .next() + .ok_or("missing virtual_target_ip")? + .parse::() + .map_err(|e| format!("invalid virtual_target_ip: {}", e))?; + + let dst = parts.next().ok_or("missing destination")?; + + let (dst_host, dst_port) = dst.rsplit_once(':').ok_or("missing dst port")?; + + let dst_port = dst_port + .parse::() + .map_err(|e| format!("invalid dst_port: {}", e))?; + if dst_port == 0 { + return Err("invalid dst port: 0".to_string()); + } + Ok(Self { + protocol, + src_addr, + virtual_target_ip, + dst_host: dst_host.to_string(), + dst_port, + }) + } +} + +fn protocol_to_str(p: IpNextHeaderProtocol) -> &'static str { + match p { + IpNextHeaderProtocols::Tcp => "tcp", + IpNextHeaderProtocols::Udp => "udp", + _ => "unknown", + } +} + +fn str_to_protocol(s: &str) -> Option { + match s.to_ascii_lowercase().as_str() { + "tcp" => Some(IpNextHeaderProtocols::Tcp), + "udp" => Some(IpNextHeaderProtocols::Udp), + _ => None, + } +} diff --git a/vnt-core/src/port_mapping/tcp_port_mapping.rs b/vnt-core/src/port_mapping/tcp_port_mapping.rs new file mode 100644 index 0000000..518a16e --- /dev/null +++ b/vnt-core/src/port_mapping/tcp_port_mapping.rs @@ -0,0 +1,85 @@ +use crate::enhanced_tunnel::quic_over::quic_client::{QuicTunnelClient, send_handshake}; +use crate::port_mapping::PortMapping; +use crate::protocol::client_message::{ + PortProxyHandshake, QuicProxyHandshake, quic_proxy_handshake, +}; +use crate::utils::task_control::TaskGroup; +use anyhow::Context; +use pnet_packet::ip::IpNextHeaderProtocols; +use std::net::{Ipv4Addr, SocketAddr}; +use tokio::net::{TcpListener, TcpStream}; + +pub async fn start( + task_group: &TaskGroup, + list: &Vec, + quic_tunnel_client: QuicTunnelClient, +) -> anyhow::Result<()> { + for x in list { + if x.protocol != IpNextHeaderProtocols::Tcp { + continue; + } + log::info!("Starting TCP port mapping on {}", x); + let listener = TcpListener::bind(x.src_addr) + .await + .with_context(|| format!("Tcp port mapping Failed to bind to {}", x.src_addr))?; + let group = task_group.clone(); + let tunnel_client = quic_tunnel_client.clone(); + let mapping = x.clone(); + task_group.spawn(async move { + if let Err(e) = listen(&group, listener, &mapping, tunnel_client).await { + log::error!("listen {:?},mapping:{mapping}", e); + } + }); + } + Ok(()) +} + +async fn listen( + task_group: &TaskGroup, + listener: TcpListener, + mapping: &PortMapping, + quic_tunnel_client: QuicTunnelClient, +) -> anyhow::Result<()> { + loop { + let (stream, addr) = listener.accept().await?; + let tunnel_client = quic_tunnel_client.clone(); + let target_ip = mapping.virtual_target_ip; + let dst_host = mapping.dst_host.clone(); + let dst_port = mapping.dst_port; + task_group.spawn(async move { + if let Err(e) = + stream_copy(stream, addr, target_ip, dst_host, dst_port, tunnel_client).await + { + log::error!("TCP TCP Stream Error: {:?}", e); + } + }); + } +} + +async fn stream_copy( + mut tcp_stream: TcpStream, + src: SocketAddr, + target_ip: Ipv4Addr, + dst_host: String, + dst_port: u16, + quic_tunnel_client: QuicTunnelClient, +) -> anyhow::Result<()> { + let (mut send_stream, mut recv_stream) = quic_tunnel_client.open_bi(target_ip).await?; + let handshake = QuicProxyHandshake { + handshake: Some(quic_proxy_handshake::Handshake::TcpPortMapping( + PortProxyHandshake { + src_ip: src.ip().to_string(), + src_port: src.port().into(), + dst_host, + dst_port: dst_port as _, + }, + )), + }; + send_handshake(&mut send_stream, handshake).await?; + let (mut tcp_r, mut tcp_w) = tcp_stream.split(); + tokio::select! { + _ = tokio::io::copy(&mut recv_stream, &mut tcp_w) => {}, + _ = tokio::io::copy(&mut tcp_r, &mut send_stream) => {}, + } + Ok(()) +} diff --git a/vnt-core/src/port_mapping/udp_port_mapping.rs b/vnt-core/src/port_mapping/udp_port_mapping.rs new file mode 100644 index 0000000..31af7ed --- /dev/null +++ b/vnt-core/src/port_mapping/udp_port_mapping.rs @@ -0,0 +1,145 @@ +use crate::enhanced_tunnel::quic_over::quic_client::{QuicTunnelClient, send_handshake}; +use crate::port_mapping::PortMapping; +use crate::protocol::client_message::{ + PortProxyHandshake, QuicProxyHandshake, quic_proxy_handshake, +}; +use crate::utils::task_control::TaskGroup; +use anyhow::Context; +use bytes::Bytes; +use futures::{SinkExt, StreamExt}; +use parking_lot::Mutex; +use pnet_packet::ip::IpNextHeaderProtocols; +use std::collections::HashMap; +use std::net::{Ipv4Addr, SocketAddr}; +use std::sync::Arc; +use std::time::Duration; +use tokio::net::UdpSocket; +use tokio::sync::mpsc::Sender; +use tokio::sync::mpsc::error::TrySendError; +use tokio_util::codec::{FramedRead, FramedWrite, LengthDelimitedCodec}; + +pub async fn start( + task_group: &TaskGroup, + list: &Vec, + quic_tunnel_client: QuicTunnelClient, +) -> anyhow::Result<()> { + for x in list { + if x.protocol != IpNextHeaderProtocols::Udp { + continue; + } + log::info!("Starting UDP port mapping on {}", x); + let udp = UdpSocket::bind(x.src_addr) + .await + .with_context(|| format!("Udp port mapping Failed to bind to {}", x.src_addr))?; + let udp_socket = Arc::new(udp); + let group = task_group.clone(); + let tunnel_client = quic_tunnel_client.clone(); + let mapping = x.clone(); + task_group.spawn(async move { + if let Err(e) = recv(&group, udp_socket, &mapping, tunnel_client).await { + log::error!("recv {:?},mapping:{mapping}", e); + } + }); + } + Ok(()) +} + +async fn recv( + task_group: &TaskGroup, + udp_socket: Arc, + mapping: &PortMapping, + quic_tunnel_client: QuicTunnelClient, +) -> anyhow::Result<()> { + let mut buf = vec![0u8; 65536]; + let dest_map = Arc::new(Mutex::new(HashMap::>::new())); + loop { + let (len, src) = udp_socket.recv_from(&mut buf).await?; + let bytes = Bytes::copy_from_slice(&buf[..len]); + + let tx = { + let mut map = dest_map.lock(); + if let Some(tx) = map.get(&src) { + tx.clone() + } else { + let (tx, rx) = tokio::sync::mpsc::channel::(128); + let udp_socket = udp_socket.clone(); + let virtual_target_ip = mapping.virtual_target_ip; + let dst_host = mapping.dst_host.clone(); + let dst_port = mapping.dst_port; + let tunnel_client = quic_tunnel_client.clone(); + task_group.spawn(async move { + if let Err(e) = udp_mapping_handle( + udp_socket, + src, + virtual_target_ip, + dst_host, + dst_port, + rx, + tunnel_client, + ) + .await + { + log::error!("udp_mapping_handle {e:?},src:{src}"); + } + }); + + map.insert(src, tx.clone()); + tx + } + }; + + if let Err(err) = tx.try_send(bytes) { + match err { + TrySendError::Full(_) => {} + TrySendError::Closed(_) => { + let mut map = dest_map.lock(); + map.remove(&src); + } + } + } + } +} + +async fn udp_mapping_handle( + udp_socket: Arc, + src: SocketAddr, + target_ip: Ipv4Addr, + dst_host: String, + dst_port: u16, + mut rx: tokio::sync::mpsc::Receiver, + quic_tunnel_client: QuicTunnelClient, +) -> anyhow::Result<()> { + let (mut send_stream, recv_stream) = quic_tunnel_client.open_bi(target_ip).await?; + + let handshake = QuicProxyHandshake { + handshake: Some(quic_proxy_handshake::Handshake::UdpPortMapping( + PortProxyHandshake { + src_ip: src.ip().to_string(), + src_port: src.port().into(), + dst_host, + dst_port: dst_port as _, + }, + )), + }; + send_handshake(&mut send_stream, handshake).await?; + let mut framed_write = FramedWrite::new(send_stream, LengthDelimitedCodec::new()); + let mut framed_read = FramedRead::new(recv_stream, LengthDelimitedCodec::new()); + loop { + tokio::select! { + Some(buf) = framed_read.next()=>{ + let buf = buf?; + udp_socket.send_to(&buf, src).await?; + }, + Some(buf) = rx.recv()=>{ + framed_write.send(buf).await? + }, + _ = tokio::time::sleep(Duration::from_secs(60)) =>{ + break; + }, + else => { + break; + } + } + } + Ok(()) +} diff --git a/vnt-core/src/protocol/client_message.rs b/vnt-core/src/protocol/client_message.rs new file mode 100644 index 0000000..2de4d4d --- /dev/null +++ b/vnt-core/src/protocol/client_message.rs @@ -0,0 +1,98 @@ +mod proto { + include!(concat!(env!("OUT_DIR"), "/protocol.client.rs")); +} + +use anyhow::bail; +use bytes::BytesMut; +use prost::Message; +use std::net::{Ipv4Addr, Ipv6Addr}; + +use crate::protocol::ProtoToBytesMut; +pub use proto::*; + +pub fn encode_nat_info(nat_info: &rust_p2p_core::nat::NatInfo) -> proto::NatInfo { + let nat_type = match nat_info.nat_type { + rust_p2p_core::nat::NatType::Cone => proto::NatType::Cone, + rust_p2p_core::nat::NatType::Symmetric => proto::NatType::Symmetric, + }; + + proto::NatInfo { + nat_type: nat_type.into(), + public_ips: nat_info.public_ips.iter().map(|v| (*v).into()).collect(), + public_udp_ports: nat_info + .public_udp_ports + .iter() + .map(|v| (*v).into()) + .collect(), + public_port_range: nat_info.public_port_range.into(), + local_ipv4s: nat_info.local_ipv4s.iter().map(|v| (*v).into()).collect(), + ipv6: nat_info.ipv6.map(|v| v.octets().to_vec()), + local_udp_ports: nat_info + .local_udp_ports + .iter() + .map(|v| (*v).into()) + .collect(), + local_tcp_port: nat_info.local_tcp_port.into(), + public_tcp_port: nat_info.public_tcp_port.into(), + } +} +pub fn decode_nat_info(msg: proto::NatInfo) -> anyhow::Result { + let nat_type = match msg.nat_type() { + proto::NatType::Cone => rust_p2p_core::nat::NatType::Cone, + proto::NatType::Symmetric => rust_p2p_core::nat::NatType::Symmetric, + }; + let ipv6: Option<[u8; 16]> = msg.ipv6.and_then(|v| v.as_slice().try_into().ok()); + + // Validate all ports fit in u16 + let validate_port = |p: u32| -> anyhow::Result { + u16::try_from(p).map_err(|_| anyhow::anyhow!("invalid port number: {}", p)) + }; + + let public_udp_ports: Result, _> = msg + .public_udp_ports + .into_iter() + .map(validate_port) + .collect(); + let local_udp_ports: Result, _> = + msg.local_udp_ports.into_iter().map(validate_port).collect(); + + Ok(rust_p2p_core::nat::NatInfo { + nat_type, + public_ips: msg.public_ips.into_iter().map(|v| v.into()).collect(), + public_udp_ports: public_udp_ports?, + mapping_tcp_addr: vec![], + mapping_udp_addr: vec![], + public_port_range: validate_port(msg.public_port_range)?, + local_ipv4: msg + .local_ipv4s + .first() + .map(|v| (*v).into()) + .unwrap_or(Ipv4Addr::UNSPECIFIED), + local_ipv4s: msg.local_ipv4s.into_iter().map(|v| v.into()).collect(), + ipv6: ipv6.map(Ipv6Addr::from), + local_udp_ports: local_udp_ports?, + local_tcp_port: validate_port(msg.local_tcp_port)?, + public_tcp_port: validate_port(msg.public_tcp_port)?, + }) +} +#[derive(Clone, Debug)] +pub struct PunchInfo { + pub nat_info: rust_p2p_core::nat::NatInfo, +} + +impl PunchInfo { + pub fn from_slice(buf: &[u8]) -> anyhow::Result { + let msg = proto::PunchInfo::decode(buf)?; + let Some(nat_info) = msg.nat_info else { + bail!("Punched info decode failed."); + }; + let nat_info = decode_nat_info(nat_info)?; + Ok(Self { nat_info }) + } + pub fn encode(&self) -> BytesMut { + let message = proto::PunchInfo { + nat_info: Some(encode_nat_info(&self.nat_info)), + }; + message.encode_bytes_mut() + } +} diff --git a/vnt-core/src/protocol/control_message.rs b/vnt-core/src/protocol/control_message.rs new file mode 100644 index 0000000..b1d7369 --- /dev/null +++ b/vnt-core/src/protocol/control_message.rs @@ -0,0 +1,275 @@ +use crate::protocol::ProtoToBytesMut; +pub(crate) use crate::protocol::control_message::proto::SelectiveBroadcast; +use crate::protocol::control_message::proto::request_message::RequestPayload; +use crate::protocol::control_message::proto::response_message::ResponsePayload; +use anyhow::bail; +use bytes::BytesMut; +use prost::Message; +use std::net::Ipv4Addr; + +mod proto { + include!(concat!(env!("OUT_DIR"), "/protocol.control_message.rs")); +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] +pub enum RegistrationMode { + #[default] + Normal = 0, + PreRegister = 1, +} + +impl From for proto::RegistrationMode { + fn from(mode: RegistrationMode) -> Self { + match mode { + RegistrationMode::Normal => proto::RegistrationMode::Normal, + RegistrationMode::PreRegister => proto::RegistrationMode::PreRegister, + } + } +} + +impl From for RegistrationMode { + fn from(mode: proto::RegistrationMode) -> Self { + match mode { + proto::RegistrationMode::Normal => RegistrationMode::Normal, + proto::RegistrationMode::PreRegister => RegistrationMode::PreRegister, + } + } +} +pub(crate) struct RegRequestMsg { + pub network_code: String, + pub device_id: String, + pub ip: Option, + pub name: String, + pub version: String, + pub key_sign: Option, + pub ip_variable: bool, + pub server_id: u32, + pub registration_mode: RegistrationMode, +} +impl RegRequestMsg { + // pub fn check(&self) -> anyhow::Result<()> { + // if self.network_code.is_empty() { + // return Err(anyhow!("network_code cannot be empty")); + // } + // if self.network_code.len() > MAX_NETWORK_CODE_LEN { + // return Err(anyhow!( + // "network_code length exceeds {} characters (current: {})", + // MAX_NETWORK_CODE_LEN, + // self.network_code.len() + // )); + // } + // if self.device_id.is_empty() { + // return Err(anyhow!("device_id cannot be empty")); + // } + // if self.device_id.len() > MAX_DEVICE_ID_LEN { + // return Err(anyhow!( + // "device_id length exceeds {} characters (current: {})", + // MAX_DEVICE_ID_LEN, + // self.device_id.len() + // )); + // } + // + // if self.name.len() > MAX_NAME_LEN { + // return Err(anyhow!( + // "name length exceeds {} characters (current: {})", + // MAX_NAME_LEN, + // self.name.len() + // )); + // } + // + // if self.version.len() > MAX_VERSION_LEN { + // return Err(anyhow!( + // "version length exceeds {} characters (current: {})", + // MAX_VERSION_LEN, + // self.version.len() + // )); + // } + // + // Ok(()) + // } + // pub fn from(msg: proto::RegRequestMsg) -> anyhow::Result { + // Ok(Self { + // network_code: msg.network_code, + // device_id: msg.device_id, + // ip: msg.ip.map(|ip| ip.into()), + // name: msg.name, + // version: msg.version, + // key_sign: msg.key_sign, + // ip_variable: msg.ip_variable, + // server_id: msg.server_id, + // }) + // } + pub fn to(self) -> proto::RegRequestMsg { + proto::RegRequestMsg { + network_code: self.network_code, + device_id: self.device_id, + ip: self.ip.map(|ip| ip.into()), + name: self.name, + version: self.version, + key_sign: self.key_sign, + ip_variable: self.ip_variable, + server_id: self.server_id, + registration_mode: proto::RegistrationMode::from(self.registration_mode).into(), + } + } +} +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct RegResponseMsg { + pub ip: Ipv4Addr, + pub prefix_len: u8, + pub gateway: Ipv4Addr, + pub server_version: String, +} +impl RegResponseMsg { + pub fn from(msg: proto::RegResponseMsg) -> anyhow::Result { + Ok(Self { + ip: msg.ip.into(), + prefix_len: (msg.prefix_len & 0xFF) as u8, + gateway: msg.gateway.into(), + server_version: msg.server_version, + }) + } + pub fn to(self) -> proto::RegResponseMsg { + proto::RegResponseMsg { + ip: self.ip.into(), + prefix_len: self.prefix_len as _, + gateway: self.gateway.into(), + server_version: self.server_version, + } + } +} +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct ErrorResponseMsg { + pub code: u32, + pub message: String, +} +impl ErrorResponseMsg { + pub fn from(msg: proto::ErrorResponseMsg) -> anyhow::Result { + Ok(Self { + code: msg.code, + message: msg.message, + }) + } + pub fn to(self) -> proto::ErrorResponseMsg { + proto::ErrorResponseMsg { + code: self.code, + message: self.message, + } + } +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct ConfirmRegResponseMsg { + pub success: bool, +} +impl ConfirmRegResponseMsg { + pub fn from(msg: proto::ConfirmRegResponseMsg) -> anyhow::Result { + Ok(Self { + success: msg.success, + }) + } + pub fn to(self) -> proto::ConfirmRegResponseMsg { + proto::ConfirmRegResponseMsg { + success: self.success, + } + } +} +pub(crate) enum RequestMessage { + Reg(RegRequestMsg), + ConfirmReg, +} +impl RequestMessage { + pub fn encode(self) -> BytesMut { + let request_payload = match self { + RequestMessage::Reg(reg) => RequestPayload::Reg(reg.to()), + RequestMessage::ConfirmReg => RequestPayload::ConfirmReg(proto::ConfirmRegMsg {}), + }; + proto::RequestMessage { + request_payload: Some(request_payload), + } + .encode_bytes_mut() + } +} +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum ResponseMessage { + Reg(RegResponseMsg), + Error(ErrorResponseMsg), + ConfirmReg(ConfirmRegResponseMsg), +} +impl ResponseMessage { + pub fn from_slice(buf: &[u8]) -> anyhow::Result { + let msg = proto::ResponseMessage::decode(buf)?; + let Some(payload) = msg.response_payload else { + bail!("unsupported") + }; + match payload { + ResponsePayload::Reg(reg) => Ok(ResponseMessage::Reg(RegResponseMsg::from(reg)?)), + ResponsePayload::Error(e) => Ok(ResponseMessage::Error(ErrorResponseMsg::from(e)?)), + ResponsePayload::ConfirmReg(c) => { + Ok(ResponseMessage::ConfirmReg(ConfirmRegResponseMsg::from(c)?)) + } + } + } + pub fn encode(self) -> BytesMut { + let response_payload = match self { + ResponseMessage::Reg(reg) => ResponsePayload::Reg(reg.to()), + ResponseMessage::Error(e) => ResponsePayload::Error(e.to()), + ResponseMessage::ConfirmReg(c) => ResponsePayload::ConfirmReg(c.to()), + }; + proto::ResponseMessage { + response_payload: Some(response_payload), + } + .encode_bytes_mut() + } +} + +impl SelectiveBroadcast { + pub fn new(ips: &[Ipv4Addr], data: Vec) -> Self { + SelectiveBroadcast { + ips: ips.iter().map(|v| (*v).into()).collect(), + data, + } + } +} + +#[derive(Debug, Clone)] +pub struct ClientSimpleInfo { + pub ip: Ipv4Addr, + pub online: bool, +} +impl ClientSimpleInfo { + pub fn from(msg: proto::ClientSimpleInfo) -> anyhow::Result { + Ok(Self { + ip: msg.ip.into(), + online: msg.online, + }) + } + pub fn to(self) -> proto::ClientSimpleInfo { + proto::ClientSimpleInfo { + ip: self.ip.into(), + online: self.online, + } + } +} +#[derive(Debug)] +pub struct ClientSimpleInfoList { + pub data_version: u64, + pub list: Vec, + pub is_all: bool, + pub time: i64, +} +impl ClientSimpleInfoList { + pub fn from_slice(buf: &[u8]) -> anyhow::Result { + let msg = proto::ClientSimpleInfoList::decode(buf)?; + let mut list = Vec::with_capacity(msg.list.len()); + for x in msg.list { + list.push(ClientSimpleInfo::from(x)?); + } + Ok(Self { + data_version: msg.data_version, + list, + is_all: msg.is_all, + time: msg.time, + }) + } +} diff --git a/vnt-core/src/protocol/ip_packet_protocol.rs b/vnt-core/src/protocol/ip_packet_protocol.rs new file mode 100644 index 0000000..27ac2fc --- /dev/null +++ b/vnt-core/src/protocol/ip_packet_protocol.rs @@ -0,0 +1,302 @@ +/* + 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 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 1 | msg_type(7) |max ttl(4) |curr ttl(4)| C | G | R | reserve(13) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | seq(32) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | src ID(32) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | dest ID(32) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | payload(n) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ +#![allow(dead_code)] +use crate::protocol::transmission::TransmissionBytes; +use bytes::{Bytes, BytesMut}; +use std::io; +use zerocopy::byteorder::{NetworkEndian, U32}; +use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Ref, Unaligned}; + +#[derive(Debug, FromBytes, IntoBytes, Unaligned, KnownLayout, Immutable)] +#[repr(C)] +pub struct NetHeader { + /// Byte 0: bit7 = 1, bit0..6 = msg_type + pub type_byte: u8, + /// Byte 1: high 4 = max ttl, low 4 = curr ttl + pub ttl_byte: u8, + /// Byte 2: C(0x80) | G(0x40) | reserve + pub flags_byte: u8, + /// Byte 3: reserve + pub _reserved: u8, + + pub seq: U32, + pub src_id: U32, + pub dest_id: U32, +} +const COMPRESSED: u8 = 0x80; +const GATEWAY: u8 = 0x40; +const FEC: u8 = 0x20; +impl NetHeader { + #[inline] + pub fn msg_type(&self) -> u8 { + self.type_byte & 0x7F + } + + #[inline] + pub fn set_msg_type(&mut self, msg_type: u8) { + self.type_byte = (msg_type & 0x7F) | 0x80; + } + + #[inline] + pub fn max_ttl(&self) -> u8 { + self.ttl_byte >> 4 + } + + #[inline] + pub fn curr_ttl(&self) -> u8 { + self.ttl_byte & 0x0F + } + + #[inline] + pub fn set_ttl(&mut self, max: u8, curr: u8) { + self.ttl_byte = (max << 4) | (curr & 0x0F); + } + + #[inline] + pub fn decr_ttl(&mut self) { + let curr = self.curr_ttl(); + if curr == 0 { + return; + } + self.ttl_byte = (self.ttl_byte & 0xF0) | (curr - 1); + } + + fn set_flag(&mut self, mask: u8, val: bool) { + if val { + self.flags_byte |= mask; + } else { + self.flags_byte &= !mask; + } + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum MsgType { + Turn = 1, + Broadcast = 2, + ExcludeBroadcast = 3, + TargetBroadcast = 4, + + Ping = 5, + Pong = 6, + PingTurn = 7, + PongTurn = 8, + + PunchStart1 = 9, + PunchStart2 = 10, + PunchReq = 11, + PunchRes = 12, + + PushClientIps = 13, + + RpcReq = 14, + RpcRes = 15, + + Quic = 17, +} +impl From for u8 { + fn from(val: MsgType) -> Self { + val as u8 + } +} + +impl TryFrom for MsgType { + type Error = io::Error; + + fn try_from(value: u8) -> Result { + let val = match value { + 1 => MsgType::Turn, + 2 => MsgType::Broadcast, + 3 => MsgType::ExcludeBroadcast, + 4 => MsgType::TargetBroadcast, + + 5 => MsgType::Ping, + 6 => MsgType::Pong, + 7 => MsgType::PingTurn, + 8 => MsgType::PongTurn, + + 9 => MsgType::PunchStart1, + 10 => MsgType::PunchStart2, + 11 => MsgType::PunchReq, + 12 => MsgType::PunchRes, + + 13 => MsgType::PushClientIps, + + 14 => MsgType::RpcReq, + 15 => MsgType::RpcRes, + + 17 => MsgType::Quic, + _ => { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("invalid msg type:{value}"), + )); + } + }; + Ok(val) + } +} + +pub const HEAD_LENGTH: usize = std::mem::size_of::(); + +pub struct NetPacket { + buffer: B, +} +impl> NetPacket { + pub fn new(buffer: B) -> io::Result> { + if buffer.as_ref().len() < HEAD_LENGTH { + return Err(io::ErrorKind::InvalidInput.into()); + } + Ok(NetPacket { buffer }) + } + fn header(&self) -> Ref<&[u8], NetHeader> { + // Safe: NetHeader is Unaligned and length is validated in new() + let (header, _) = Ref::<&[u8], NetHeader>::from_prefix(self.buffer.as_ref()).unwrap(); + header + } + pub fn buffer(&self) -> &[u8] { + self.buffer.as_ref() + } + pub fn into_buffer(self) -> B { + self.buffer + } + pub fn source_buf(&self) -> &B { + &self.buffer + } + pub fn msg_type(&self) -> io::Result { + self.header().msg_type().try_into() + } + pub fn max_ttl(&self) -> u8 { + self.header().max_ttl() + } + pub fn ttl(&self) -> u8 { + self.header().curr_ttl() + } + + pub fn seq(&self) -> u32 { + self.header().seq.get() + } + + pub fn src_id(&self) -> u32 { + self.header().src_id.get() + } + + pub fn dest_id(&self) -> u32 { + self.header().dest_id.get() + } + pub fn is_compressed(&self) -> bool { + (self.header().flags_byte & COMPRESSED) != 0 + } + pub fn is_gateway(&self) -> bool { + (self.header().flags_byte & GATEWAY) != 0 + } + pub fn is_fec(&self) -> bool { + (self.header().flags_byte & FEC) != 0 + } + pub fn head(&self) -> &[u8] { + &self.buffer.as_ref()[..HEAD_LENGTH] + } + pub fn payload(&self) -> &[u8] { + &self.buffer.as_ref()[HEAD_LENGTH..] + } +} + +impl + AsMut<[u8]>> NetPacket { + fn header_mut(&mut self) -> Ref<&mut [u8], NetHeader> { + // Safe: NetHeader is Unaligned and length is validated in new() + let (header, _) = Ref::<&mut [u8], NetHeader>::from_prefix(self.buffer.as_mut()).unwrap(); + header + } + + pub fn set_msg_type(&mut self, msg_type: MsgType) { + self.header_mut().set_msg_type(msg_type.into()); + } + + pub fn decr_ttl(&mut self){ + self.header_mut().decr_ttl() + } + + pub fn set_ttl(&mut self, ttl: u8) { + self.header_mut().set_ttl(ttl, ttl); + } + + pub fn set_seq(&mut self, seq: u32) { + self.header_mut().seq.set(seq); + } + + pub fn set_src_id(&mut self, id: u32) { + self.header_mut().src_id.set(id); + } + + pub fn set_dest_id(&mut self, id: u32) { + self.header_mut().dest_id.set(id); + } + + pub fn set_compressed_flag(&mut self, compressed: bool) { + self.header_mut().set_flag(COMPRESSED, compressed); + } + pub fn set_gateway_flag(&mut self, gateway: bool) { + self.header_mut().set_flag(GATEWAY, gateway); + } + pub fn set_fec_flag(&mut self, fec: bool) { + self.header_mut().set_flag(FEC, fec); + } + + pub fn set_payload(&mut self, data: &[u8]) -> io::Result<()> { + let buf = self.buffer.as_mut(); + if buf.len() < HEAD_LENGTH + data.len() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "Invalid message length", + )); + } + buf[HEAD_LENGTH..HEAD_LENGTH + data.len()].copy_from_slice(data); + Ok(()) + } + pub fn head_mut(&mut self) -> &mut [u8] { + &mut self.buffer.as_mut()[..HEAD_LENGTH] + } + pub fn payload_mut(&mut self) -> &mut [u8] { + &mut self.buffer.as_mut()[HEAD_LENGTH..] + } + pub fn source_buf_mut(&mut self) -> &mut B { + &mut self.buffer + } +} + +impl Clone for NetPacket { + fn clone(&self) -> Self { + NetPacket { + buffer: self.buffer.clone(), + } + } +} + +impl NetPacket { + pub fn into_bytes(self) -> NetPacket { + NetPacket { + buffer: self.buffer.freeze(), + } + } +} + +impl NetPacket { + pub fn into_bytes(self) -> NetPacket { + NetPacket { + buffer: self.buffer.into_bytes().freeze(), + } + } +} diff --git a/vnt-core/src/protocol/mod.rs b/vnt-core/src/protocol/mod.rs new file mode 100644 index 0000000..9d81685 --- /dev/null +++ b/vnt-core/src/protocol/mod.rs @@ -0,0 +1,21 @@ +use bytes::BytesMut; +use prost::Message; + +pub(crate) mod client_message; +pub mod control_message; +pub(crate) mod ip_packet_protocol; +pub(crate) mod rpc_message; +pub(crate) mod transmission; + +pub trait ProtoToBytesMut: Message { + fn encode_bytes_mut(&self) -> BytesMut + where + Self: Sized, + { + let mut bytes_mut = BytesMut::with_capacity(self.encoded_len()); + self.encode_raw(&mut bytes_mut); + bytes_mut + } +} + +impl ProtoToBytesMut for T {} diff --git a/vnt-core/src/protocol/rpc_message.rs b/vnt-core/src/protocol/rpc_message.rs new file mode 100644 index 0000000..a3d431e --- /dev/null +++ b/vnt-core/src/protocol/rpc_message.rs @@ -0,0 +1,4 @@ +mod proto { + include!(concat!(env!("OUT_DIR"), "/protocol.rpc.rs")); +} +pub use proto::*; diff --git a/vnt-core/src/protocol/transmission.rs b/vnt-core/src/protocol/transmission.rs new file mode 100644 index 0000000..28096e9 --- /dev/null +++ b/vnt-core/src/protocol/transmission.rs @@ -0,0 +1,266 @@ +use bytes::{Buf, Bytes, BytesMut}; +use std::borrow::{Borrow, BorrowMut}; +use std::io; +use std::ops::{Deref, DerefMut}; + +const DEFAULT_BUF_SIZE: usize = 2048; +#[derive(Clone)] +pub struct TransmissionBytes { + buf: BytesMut, + start: usize, + end: usize, +} +impl From for TransmissionBytes { + fn from(buf: BytesMut) -> TransmissionBytes { + let end = buf.len(); + Self { buf, start: 0, end } + } +} +impl From for TransmissionBytes { + fn from(buf: Bytes) -> TransmissionBytes { + let end = buf.len(); + Self { + buf: BytesMut::from(buf), + start: 0, + end, + } + } +} +impl From<&[u8]> for TransmissionBytes { + fn from(buf: &[u8]) -> TransmissionBytes { + let end = buf.len(); + Self { + buf: BytesMut::from(buf), + start: 0, + end, + } + } +} + +impl TransmissionBytes { + pub fn new_offset(start: usize) -> Self { + TransmissionBytes { + buf: BytesMut::zeroed(DEFAULT_BUF_SIZE), + start, + end: start, + } + } + pub fn new_offset_zeroed(start: usize) -> Self { + TransmissionBytes { + buf: BytesMut::zeroed(DEFAULT_BUF_SIZE), + start, + end: DEFAULT_BUF_SIZE, + } + } + pub fn zeroed(cap: usize) -> Self { + TransmissionBytes { + buf: BytesMut::zeroed(cap), + start: 0, + end: cap, + } + } + pub fn zeroed_size(size: usize, reserve: usize) -> Self { + TransmissionBytes { + buf: BytesMut::zeroed(size + reserve), + start: 0, + end: size, + } + } + #[allow(dead_code)] + pub fn with_capacity(head_room: usize, capacity: usize) -> Self { + TransmissionBytes { + buf: BytesMut::zeroed(capacity), + start: head_room, + end: head_room, + } + } + pub fn len(&self) -> usize { + self.end - self.start + } + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + #[allow(dead_code)] + pub fn capacity(&self) -> usize { + self.buf.capacity() + } + /// 头部可用空间(可向前扩展的字节数) + #[inline] + pub fn head_room(&self) -> usize { + self.start + } + + /// 尾部可用空间(可向后扩展的字节数) + #[inline] + #[allow(dead_code)] + pub fn tail_room(&self) -> usize { + self.buf.capacity() - self.end + } + #[inline] + fn as_slice(&self) -> &[u8] { + &self.buf[self.start..self.end] + } + + #[inline] + fn as_slice_mut(&mut self) -> &mut [u8] { + &mut self.buf[self.start..self.end] + } + pub fn put(&mut self, data: &[u8]) -> io::Result<()> { + let need = data.len(); + let free = self.buf.capacity() - self.end; + + if need > free { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("data too large:need={need},free={free}"), + )); + } + + self.buf[self.end..self.end + need].copy_from_slice(data); + self.end += need; + Ok(()) + } + pub fn retreat_head(&mut self, len: usize) -> io::Result<()> { + if len > self.head_room() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!( + "retreat_head beyond start: len={len}, head_room={}", + self.head_room() + ), + )); + } + self.start -= len; + Ok(()) + } + pub fn advance_head(&mut self, len: usize) -> io::Result<()> { + if len > self.len() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!( + "advance_head beyond end: len={len}, data_len={}", + self.len() + ), + )); + } + self.start += len; + Ok(()) + } + pub fn set_len(&mut self, new_len: usize) -> io::Result<()> { + let new_end = self.start + new_len; + if new_end > self.buf.capacity() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!( + "set_len exceeds capacity: new_len={new_len}, max={}", + self.buf.capacity() - self.start + ), + )); + } + self.end = new_end; + Ok(()) + } + pub fn resize(&mut self, new_len: usize, value: u8) { + let new_end = self.start + new_len; + self.buf.resize(new_end, value); + self.end = new_end; + } + pub fn extend_end(&mut self, n: usize) { + if self.end + n > self.buf.len() { + self.buf.resize(self.end + n, 0); + } + self.end += n; + } + pub fn shrink_end(&mut self, n: usize) { + if n >= self.end - self.start { + self.end = self.start; + } else { + self.end -= n; + } + } + #[allow(dead_code)] + pub fn clear(&mut self) { + self.start = 0; + self.end = 0; + } + pub fn into_bytes(mut self) -> BytesMut { + self.buf.truncate(self.end); + if self.start > 0 { + self.buf.advance(self.start); + } + self.buf + } +} + +impl AsRef<[u8]> for TransmissionBytes { + #[inline] + fn as_ref(&self) -> &[u8] { + self.as_slice() + } +} + +impl Deref for TransmissionBytes { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + self.as_ref() + } +} + +impl AsMut<[u8]> for TransmissionBytes { + #[inline] + fn as_mut(&mut self) -> &mut [u8] { + self.as_slice_mut() + } +} + +impl DerefMut for TransmissionBytes { + #[inline] + fn deref_mut(&mut self) -> &mut [u8] { + self.as_mut() + } +} + +impl Borrow<[u8]> for TransmissionBytes { + fn borrow(&self) -> &[u8] { + self.as_ref() + } +} + +impl BorrowMut<[u8]> for TransmissionBytes { + fn borrow_mut(&mut self) -> &mut [u8] { + self.as_mut() + } +} + +pub trait ShrinkEnd { + fn shrink_end(&mut self, n: usize); +} +pub trait ExtendEnd { + fn extend_end(&mut self, n: usize); +} + +impl ShrinkEnd for TransmissionBytes { + fn shrink_end(&mut self, n: usize) { + self.shrink_end(n); + } +} + +impl ExtendEnd for TransmissionBytes { + fn extend_end(&mut self, n: usize) { + self.extend_end(n); + } +} + +impl ShrinkEnd for &mut TransmissionBytes { + fn shrink_end(&mut self, n: usize) { + TransmissionBytes::shrink_end(self, n); + } +} + +impl ExtendEnd for &mut TransmissionBytes { + fn extend_end(&mut self, n: usize) { + TransmissionBytes::extend_end(self, n); + } +} diff --git a/vnt-core/src/tls/cert.rs b/vnt-core/src/tls/cert.rs new file mode 100644 index 0000000..88d01eb --- /dev/null +++ b/vnt-core/src/tls/cert.rs @@ -0,0 +1,114 @@ +use anyhow::{Context, Result}; +use rcgen::{CertificateParams, DnType, KeyPair, PKCS_ED25519, SerialNumber}; +use rustls::pki_types; +use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer}; +use sha2::{Digest, Sha256}; +use time::{Duration, OffsetDateTime}; + +const ED25519_PKCS8_V1_PREFIX: [u8; 16] = [ + 0x30, 0x2e, // Sequence (len 46) + 0x02, 0x01, 0x00, // Version 0 + 0x30, 0x05, // Sequence (len 5) + 0x06, 0x03, 0x2b, 0x65, 0x70, // OID: 1.3.101.112 (Ed25519) + 0x04, 0x22, // Octet String (len 34) - 包装私钥 + 0x04, 0x20, // Octet String (len 32) - 内部 CurvePrivateKey +]; + +pub fn generate_deterministic_cert( + password: &str, +) -> Result<(CertificateDer<'static>, PrivateKeyDer<'static>)> { + // 基于密码生成 32 字节的确定性种子 + let seed = derive_seed_from_password(password); + + let mut pkcs8_bytes = Vec::with_capacity(ED25519_PKCS8_V1_PREFIX.len() + seed.len()); + pkcs8_bytes.extend_from_slice(&ED25519_PKCS8_V1_PREFIX); + pkcs8_bytes.extend_from_slice(&seed); + let private_key_der = pki_types::PrivateKeyDer::try_from(pkcs8_bytes.clone()) + .map_err(|e| anyhow::anyhow!("Failed to convert private key: {}", e))?; + let key_pair = KeyPair::from_der_and_sign_algo(&private_key_der, &PKCS_ED25519) + .context("Failed to load determinstic Ed25519 key")?; + + let mut params = CertificateParams::new(vec!["deterministic-node".to_string()])?; + params + .distinguished_name + .push(DnType::CommonName, "Deterministic Self-Signed Cert"); + + let not_before = OffsetDateTime::UNIX_EPOCH; + let not_after = not_before + Duration::days(365 * 1000); + params.not_before = not_before; + params.not_after = not_after; + + let serial_number_bytes = derive_serial_number(password); + params.serial_number = Some(SerialNumber::from_slice(&serial_number_bytes)); + + // Ed25519 签名是确定性的 (RFC 8032),不需要随机数,因此每次运行结果字节完全一致 + let cert = params + .self_signed(&key_pair) + .context("Failed to sign certificate")?; + + let cert_der = cert.der().clone(); + + let private_key_der = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(pkcs8_bytes)); + + Ok((cert_der, private_key_der)) +} + +fn derive_serial_number(password: &str) -> [u8; 20] { + let mut hasher = Sha256::new(); + hasher.update(b"vnt-serial-v1:"); + hasher.update(password.as_bytes()); + let result = hasher.finalize(); + + let mut serial = [0u8; 20]; + serial.copy_from_slice(&result[..20]); + serial +} + +fn derive_seed_from_password(password: &str) -> [u8; 32] { + let mut hasher = Sha256::new(); + hasher.update(b"vnt-ed25519-seed-v1:"); + hasher.update(password.as_bytes()); + + let mut result = hasher.finalize(); + for _ in 0..10 { + let mut hasher = Sha256::new(); + hasher.update(result); + result = hasher.finalize(); + } + + result.into() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_deterministic_cert_generation() { + let password = "test_password_123"; + + // 生成两次证书 + let (cert1, key1) = generate_deterministic_cert(password).unwrap(); + let (cert2, key2) = generate_deterministic_cert(password).unwrap(); + + // 验证私钥相同 + assert_eq!( + key1.secret_der(), + key2.secret_der(), + "Private keys should be identical" + ); + + // 验证证书相同 + assert_eq!(cert1, cert2, "Certificates should be identical"); + } + + #[test] + fn test_different_passwords_generate_different_certs() { + let (cert1, key1) = generate_deterministic_cert("password1").unwrap(); + let (cert2, key2) = generate_deterministic_cert("password2").unwrap(); + + // 不同密码应该生成不同的证书和密钥 + assert_ne!(cert1, cert2); + assert_ne!(key1.secret_der(), key2.secret_der()); + } +} diff --git a/vnt-core/src/tls/mod.rs b/vnt-core/src/tls/mod.rs new file mode 100644 index 0000000..a565d1f --- /dev/null +++ b/vnt-core/src/tls/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod cert; +pub mod verifier; diff --git a/vnt-core/src/tls/verifier.rs b/vnt-core/src/tls/verifier.rs new file mode 100644 index 0000000..e269c2f --- /dev/null +++ b/vnt-core/src/tls/verifier.rs @@ -0,0 +1,228 @@ +use anyhow::Context; +use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; +use rustls::pki_types::{CertificateDer, ServerName, UnixTime}; +use rustls::{CertificateError, ClientConfig, Error, SignatureScheme}; +use sha2::{Digest, Sha256}; +use std::fmt; +use std::str::FromStr; + +#[derive(Debug)] +pub struct FingerprintVerifier { + pub expected_fingerprint: [u8; 32], +} +impl FingerprintVerifier { + pub fn new(expected_fingerprint: [u8; 32]) -> Self { + Self { + expected_fingerprint, + } + } +} + +impl ServerCertVerifier for FingerprintVerifier { + fn verify_server_cert( + &self, + end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], + _server_name: &ServerName, + _ocsp_response: &[u8], + _now: UnixTime, + ) -> Result { + let mut hasher = Sha256::new(); + hasher.update(end_entity.as_ref()); + let calculated_hash: [u8; 32] = hasher.finalize().into(); + + if calculated_hash == self.expected_fingerprint { + Ok(ServerCertVerified::assertion()) + } else { + log::error!( + "Certificate fingerprint mismatch. Expected: {:X?}, Got: {:X?}", + self.expected_fingerprint, + calculated_hash + ); + Err(Error::InvalidCertificate(CertificateError::BadSignature)) + } + } + + fn verify_tls12_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &rustls::DigitallySignedStruct, + ) -> Result { + Ok(HandshakeSignatureValid::assertion()) + } + + fn verify_tls13_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &rustls::DigitallySignedStruct, + ) -> Result { + Ok(HandshakeSignatureValid::assertion()) + } + + fn supported_verify_schemes(&self) -> Vec { + vec![ + // RSA schemes + rustls::SignatureScheme::RSA_PKCS1_SHA256, + rustls::SignatureScheme::RSA_PKCS1_SHA384, + rustls::SignatureScheme::RSA_PKCS1_SHA512, + rustls::SignatureScheme::RSA_PSS_SHA256, + rustls::SignatureScheme::RSA_PSS_SHA384, + rustls::SignatureScheme::RSA_PSS_SHA512, + // ECDSA schemes + rustls::SignatureScheme::ECDSA_NISTP256_SHA256, + rustls::SignatureScheme::ECDSA_NISTP384_SHA384, + rustls::SignatureScheme::ECDSA_NISTP521_SHA512, + // EdDSA schemes + rustls::SignatureScheme::ED25519, + rustls::SignatureScheme::ED448, + ] + } +} +#[derive(Debug)] +pub struct InsecureVerifier; + +impl ServerCertVerifier for InsecureVerifier { + fn verify_server_cert( + &self, + _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], + _server_name: &ServerName, + _ocsp_response: &[u8], + _now: UnixTime, + ) -> Result { + Ok(rustls::client::danger::ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &rustls::DigitallySignedStruct, + ) -> Result { + Ok(HandshakeSignatureValid::assertion()) + } + + fn verify_tls13_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &rustls::DigitallySignedStruct, + ) -> Result { + Ok(HandshakeSignatureValid::assertion()) + } + + fn supported_verify_schemes(&self) -> Vec { + vec![ + SignatureScheme::RSA_PKCS1_SHA256, + SignatureScheme::RSA_PKCS1_SHA384, + SignatureScheme::RSA_PKCS1_SHA512, + SignatureScheme::RSA_PSS_SHA256, + SignatureScheme::RSA_PSS_SHA384, + SignatureScheme::RSA_PSS_SHA512, + SignatureScheme::ECDSA_NISTP256_SHA256, + SignatureScheme::ECDSA_NISTP384_SHA384, + SignatureScheme::ECDSA_NISTP521_SHA512, + SignatureScheme::ED25519, + SignatureScheme::ED448, + ] + } +} + +pub fn load_root_cert() -> anyhow::Result { + let mut root_cert_store = rustls::RootCertStore::empty(); + let certs = rustls_native_certs::load_native_certs().certs; + for cert in certs { + root_cert_store + .add(cert) + .context("Failed to add native cert to store")?; + } + Ok(root_cert_store) +} + +#[derive(Debug, Clone, Default)] +pub enum CertValidationMode { + #[default] + InsecureSkipVerification, + VerifyFingerprint([u8; 32]), + Standard, +} +impl FromStr for CertValidationMode { + type Err = String; + + fn from_str(value: &str) -> Result { + let val = value.trim().to_lowercase(); + if val == "skip" { + return Ok(CertValidationMode::InsecureSkipVerification); + } + if val == "standard" { + return Ok(CertValidationMode::Standard); + } + if let Some(hex_str) = val.strip_prefix("finger:") { + let decoded = + hex::decode(hex_str).map_err(|e| format!("Invalid hex in fingerprint: {}", e))?; + + if decoded.len() != 32 { + return Err(format!( + "Fingerprint must be 32 bytes (64 hex chars), got {} bytes", + decoded.len() + )); + } + + let mut arr = [0u8; 32]; + arr.copy_from_slice(&decoded); + return Ok(CertValidationMode::VerifyFingerprint(arr)); + } + Err(format!("Unknown certificate validation mode: {}", value)) + } +} +impl fmt::Display for CertValidationMode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + CertValidationMode::InsecureSkipVerification => { + write!(f, "skip") + } + CertValidationMode::Standard => { + write!(f, "standard") + } + CertValidationMode::VerifyFingerprint(fingerprint) => { + let hex_str = hex::encode(fingerprint); + + write!(f, "finger:{}", hex_str) + } + } + } +} + +impl CertValidationMode { + pub fn build_verifier(&self) -> anyhow::Result> { + match self { + CertValidationMode::InsecureSkipVerification => { + Ok(std::sync::Arc::new(InsecureVerifier)) + } + CertValidationMode::VerifyFingerprint(fingerprint) => { + Ok(std::sync::Arc::new(FingerprintVerifier { + expected_fingerprint: *fingerprint, + })) + } + CertValidationMode::Standard => { + let root_store = load_root_cert()?; + let verifier = + rustls::client::WebPkiServerVerifier::builder(std::sync::Arc::new(root_store)) + .build()?; + + Ok(verifier) + } + } + } + pub fn create_tls_client_config(&self) -> anyhow::Result { + let verifier = self.build_verifier()?; + + let config = ClientConfig::builder() + .dangerous() + .with_custom_certificate_verifier(verifier) + .with_no_client_auth(); + Ok(config) + } +} diff --git a/vnt-core/src/tun/enhanced_tun.rs b/vnt-core/src/tun/enhanced_tun.rs new file mode 100644 index 0000000..3ad80b9 --- /dev/null +++ b/vnt-core/src/tun/enhanced_tun.rs @@ -0,0 +1,18 @@ +use crate::context::NetworkAddr; +use crate::nat::internal_nat::InternalNatInbound; +use crate::protocol::transmission::TransmissionBytes; +use crate::tun::TunDataInbound; + +#[derive(Clone)] +pub enum EnhancedTunInbound { + Tun(TunDataInbound), + Nat(InternalNatInbound), +} +impl EnhancedTunInbound { + pub async fn inbound(&self, data: TransmissionBytes, net: &NetworkAddr) -> anyhow::Result<()> { + match self { + EnhancedTunInbound::Tun(tun) => tun.send(data, net).await, + EnhancedTunInbound::Nat(nat) => nat.send(&data, net).await, + } + } +} diff --git a/vnt-core/src/tun/general.rs b/vnt-core/src/tun/general.rs new file mode 100644 index 0000000..e16f388 --- /dev/null +++ b/vnt-core/src/tun/general.rs @@ -0,0 +1,236 @@ +use crate::enhanced_tunnel::outbound::EnhancedOutbound; +use crate::protocol::ip_packet_protocol::HEAD_LENGTH; +use crate::protocol::transmission::TransmissionBytes; +use crate::utils::task_control::{SubTask, TaskGroup}; +use anyhow::{Context, bail}; +use bytes::BytesMut; +use futures::{SinkExt, StreamExt}; +use std::io; +use std::net::Ipv4Addr; +use std::sync::Arc; +use tokio::sync::mpsc::{Receiver, Sender}; +use tun_rs::async_framed::{Decoder, DeviceFramedRead, DeviceFramedWrite, Encoder}; +use tun_rs::{AsyncDevice, DeviceBuilder}; + +#[derive(Clone)] +pub struct DeviceIOManager { + task_group: TaskGroup, + device: DeviceMutex, +} +type DeviceMutex = Arc, Option<(Ipv4Addr, u8)>)>>; +pub struct DeviceTask { + device: Arc, + task_recv: SubTask, + task_send: SubTask, +} +#[derive(Debug, Default)] +pub struct DeviceConfig { + pub tun_name: Option, + #[cfg(unix)] + pub tun_fd: Option, + pub mtu: Option, +} + +impl DeviceConfig { + pub fn set_tun_name(mut self, tun_name: String) -> Self { + self.tun_name = Some(tun_name); + self + } + #[cfg(unix)] + pub fn set_tun_fd(mut self, tun_fd: i32) -> Self { + self.tun_fd = Some(tun_fd); + self + } + pub fn set_mtu(mut self, mtu: u16) -> Self { + self.mtu = Some(mtu); + self + } +} +#[derive(Clone)] +pub struct TunInbound { + pub(crate) sender: Sender, +} + +pub struct TunReceiver { + receiver: Receiver, +} +pub fn tun_channel() -> (TunInbound, TunReceiver) { + let (sender, receiver) = tokio::sync::mpsc::channel(1024); + (TunInbound { sender }, TunReceiver { receiver }) +} + +impl DeviceIOManager { + pub fn new(task_group: TaskGroup) -> DeviceIOManager { + Self { + task_group, + device: Arc::new(Default::default()), + } + } + pub async fn stop_task(&self) { + let mut guard = self.device.lock().await; + if let Some(dev) = guard.0.take() { + dev.task_recv.stop().await; + dev.task_send.stop().await; + } + } + pub async fn start_task( + &self, + device_config: DeviceConfig, + receiver: TunReceiver, + enhanced_outbound: EnhancedOutbound, + ) -> anyhow::Result<()> { + self.stop_task().await; + let task = create( + &self.task_group, + device_config, + receiver.receiver, + enhanced_outbound, + )?; + self.device.lock().await.0.replace(task); + Ok(()) + } + #[cfg(not(target_os = "android"))] + pub async fn tun_if_index(&self) -> anyhow::Result { + let guard = self.device.lock().await; + if let Some(v) = &guard.0 { + Ok(v.device.if_index()?) + } else { + bail!("device doesn't exist") + } + } + pub async fn set_network(&self, ip: Ipv4Addr, prefix_len: u8) -> anyhow::Result<()> { + let mut guard = self.device.lock().await; + let Some(dev) = guard.0.as_ref() else { + bail!("未启动tun") + }; + if let Some(v) = guard.1.as_ref() + && v.0 == ip + && v.1 == prefix_len + { + return Ok(()); + } + dev.device + .set_network_address(ip, prefix_len, None) + .context("设置IP失败")?; + guard.1 = Some((ip, prefix_len)); + Ok(()) + } +} + +fn create_tun(config: DeviceConfig) -> anyhow::Result { + #[cfg(unix)] + if let Some(fd) = config.tun_fd { + // SAFETY: Caller must ensure fd is a valid, open file descriptor for a TUN device. + // Using an invalid fd may cause undefined behavior. + unsafe { return Ok(AsyncDevice::from_fd(fd)?) } + } + let mut builder = DeviceBuilder::new(); + if let Some(tun_name) = config.tun_name { + builder = builder.name(tun_name); + } + if let Some(mtu) = config.mtu { + builder = builder.mtu(mtu); + } + #[cfg(windows)] + { + builder = builder.metric(1); + } + #[cfg(target_os = "linux")] + { + builder = builder.offload(true); + } + let dev = builder.build_async().context("创建tun失败")?; + #[cfg(target_os = "linux")] + { + _ = dev.set_tx_queue_len(1000); + } + Ok(dev) +} +fn create( + task_group: &TaskGroup, + config: DeviceConfig, + receiver: Receiver, + enhanced_outbound: EnhancedOutbound, +) -> anyhow::Result { + let device = Arc::new(create_tun(config)?); + + let device_framed_read = DeviceFramedRead::new(device.clone(), BytesCodec::new()); + let device_framed_write = DeviceFramedWrite::new(device.clone(), BytesCodec::new()); + + let task_recv = task_group.spawn(async move { + if let Err(e) = in_tun_loop(receiver, device_framed_write).await { + log::error!("in_tun_loop error: {e:?}") + } + }); + let task_send = task_group.spawn(async move { + if let Err(e) = out_tun_loop(device_framed_read, enhanced_outbound).await { + log::error!("out_tun_loop error: {e:?}"); + } + }); + + Ok(DeviceTask { + device, + task_recv, + task_send, + }) +} + +async fn in_tun_loop( + mut receiver: Receiver, + mut device_framed_write: DeviceFramedWrite>, +) -> anyhow::Result<()> { + while let Some(data) = receiver.recv().await { + match device_framed_write.send(data).await { + Ok(_) => {} + Err(e) => { + log::error!("send to tun error: {:?}", e); + return Err(anyhow::anyhow!(e)); + } + } + } + Ok(()) +} + +async fn out_tun_loop( + mut device_framed_read: DeviceFramedRead>, + enhanced_outbound: EnhancedOutbound, +) -> anyhow::Result<()> { + while let Some(rs) = device_framed_read.next().await { + let bytes_mut = rs?; + enhanced_outbound.ipv4_outbound(bytes_mut).await; + } + Ok(()) +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Default)] +pub struct BytesCodec(()); +impl BytesCodec { + pub fn new() -> BytesCodec { + BytesCodec(()) + } +} +impl Decoder for BytesCodec { + type Item = TransmissionBytes; + type Error = io::Error; + + fn decode(&mut self, buf: &mut BytesMut) -> Result, io::Error> { + if !buf.is_empty() { + let mut bytes = TransmissionBytes::new_offset(HEAD_LENGTH); + bytes.put(buf)?; + buf.clear(); + Ok(Some(bytes)) + } else { + Ok(None) + } + } +} + +impl Encoder for BytesCodec { + type Error = io::Error; + + fn encode(&mut self, data: TransmissionBytes, buf: &mut BytesMut) -> Result<(), io::Error> { + buf.reserve(data.len()); + buf.extend_from_slice(&data); + Ok(()) + } +} diff --git a/vnt-core/src/tun/mod.rs b/vnt-core/src/tun/mod.rs new file mode 100644 index 0000000..524e02b --- /dev/null +++ b/vnt-core/src/tun/mod.rs @@ -0,0 +1,6 @@ +mod general; +pub use general::*; +mod sender; +pub use sender::*; + +pub mod enhanced_tun; diff --git a/vnt-core/src/tun/sender.rs b/vnt-core/src/tun/sender.rs new file mode 100644 index 0000000..06679ae --- /dev/null +++ b/vnt-core/src/tun/sender.rs @@ -0,0 +1,40 @@ +use crate::context::NetworkAddr; +use crate::nat::AllowSubnetExternalRoute; +use crate::protocol::transmission::TransmissionBytes; +use crate::tun::TunInbound; +use pnet_packet::ipv4::Ipv4Packet; + +#[derive(Clone)] +pub struct TunDataInbound { + allow_subnet: AllowSubnetExternalRoute, + tun_inbound: TunInbound, +} +impl TunDataInbound { + pub fn new(tun_inbound: TunInbound, allow_subnet: AllowSubnetExternalRoute) -> Self { + Self { + allow_subnet, + tun_inbound, + } + } +} + +impl TunDataInbound { + pub async fn send(&self, data: TransmissionBytes, net: &NetworkAddr) -> anyhow::Result<()> { + if data[0] >> 4 != 4 { + return Ok(()); + } + let Some(ipv4) = Ipv4Packet::new(data.as_ref()) else { + return Ok(()); + }; + let dest = ipv4.get_destination(); + if net.network().contains(&dest) + || dest == net.broadcast + || dest.is_broadcast() + || dest.is_multicast() + || self.allow_subnet.allow(&dest) + { + self.tun_inbound.sender.send(data).await?; + } + Ok(()) + } +} diff --git a/vnt-core/src/tunnel_core/mod.rs b/vnt-core/src/tunnel_core/mod.rs new file mode 100644 index 0000000..f5b82c7 --- /dev/null +++ b/vnt-core/src/tunnel_core/mod.rs @@ -0,0 +1,4 @@ +pub(crate) mod p2p; +pub mod server; + +pub(crate) mod outbound; diff --git a/vnt-core/src/tunnel_core/outbound.rs b/vnt-core/src/tunnel_core/outbound.rs new file mode 100644 index 0000000..271a91d --- /dev/null +++ b/vnt-core/src/tunnel_core/outbound.rs @@ -0,0 +1,286 @@ +use crate::compression::PacketCompression; +use crate::context::{NetworkAddr, ServerInfoCollection, SharedNetworkAddr, TrafficStats}; +use crate::crypto::PacketCrypto; +use crate::fec::FecEncoder; +use crate::nat::SubnetExternalRoute; +use crate::protocol::ip_packet_protocol::{HEAD_LENGTH, MsgType, NetPacket}; +use crate::protocol::transmission::TransmissionBytes; +use crate::tunnel_core::p2p::outbound::P2pOutbound; +use crate::tunnel_core::server::outbound::ServerOutbound; +use anyhow::bail; +use bytes::Bytes; +use pnet_packet::ipv4::Ipv4Packet; +use std::net::Ipv4Addr; + +#[derive(Clone)] +pub(crate) struct BasicOutbound { + server_outbound: ServerOutbound, + p2p_outbound: Option, + packet_crypto: PacketCrypto, +} + +impl BasicOutbound { + pub fn new( + server_outbound: ServerOutbound, + p2p_outbound: Option, + packet_crypto: PacketCrypto, + ) -> Self { + Self { + server_outbound, + p2p_outbound, + packet_crypto, + } + } + + /// 获取加密保留空间大小 + pub fn encrypt_reserve(&self) -> usize { + self.packet_crypto.encrypt_reserve() + } + + /// 加密数据包 + pub fn encrypt_in_place( + &self, + packet: &mut NetPacket, + ) -> anyhow::Result<()> { + Ok(self.packet_crypto.encrypt_in_place(packet)?) + } + + /// 发送原始数据包到指定目标(通过P2P或服务器) + pub async fn send_raw( + &self, + dest: Ipv4Addr, + packet: NetPacket, + ) -> anyhow::Result<()> { + let packet = packet.into_bytes(); + if let Some(p2p) = self.p2p_outbound.as_ref() + && let Some(route) = p2p.get_route_by_id(&dest) + { + p2p.send_raw_to(packet, &route.route_key()).await?; + } else { + self.server_outbound.send_raw(dest, packet).await?; + } + Ok(()) + } + + /// 发送到默认服务器 + pub async fn send_default_raw( + &self, + packet: NetPacket, + ) -> anyhow::Result<()> { + let bytes = packet.into_buffer().into_bytes().freeze(); + self.server_outbound + .send_default_raw(NetPacket::new(bytes)?) + .await + } + + /// 广播发送 + pub async fn send_raw_broadcast( + &self, + exclude_ips: Option>, + packet: NetPacket, + ) -> anyhow::Result<()> { + self.server_outbound + .send_raw_broadcast(exclude_ips, packet) + .await + } + + /// 检查是否存在到目标的路由 + pub fn exists_route(&self, dest: &Ipv4Addr) -> bool { + if let Some(p2p) = self.p2p_outbound.as_ref() + && p2p.exists_route_by_id(dest) + { + return true; + } + self.server_outbound.exists_route(dest) + } + + /// P2P广播(内部转换类型) + pub fn p2p_broadcast_transmission( + &self, + list: &[Ipv4Addr], + max_count: usize, + packet: &NetPacket, + ) -> Option> { + if let Some(p2p) = self.p2p_outbound.as_ref() { + let vec = p2p.p2p_broadcast(list, max_count, packet); + if vec.is_empty() { None } else { Some(vec) } + } else { + None + } + } + + /// 发送加密后的数据包 + pub async fn send_encrypted_packet( + &self, + dest: Ipv4Addr, + mut packet: NetPacket, + ) -> anyhow::Result<()> { + // 加密 + self.packet_crypto.encrypt_in_place(&mut packet)?; + + // 发送 + if let Some(p2p) = self.p2p_outbound.as_ref() + && let Some(route) = p2p.get_route_by_id(&dest) + { + let bytes = packet.into_buffer().into_bytes().freeze(); + p2p.send_raw_to(NetPacket::new(bytes)?, &route.route_key()) + .await?; + } else { + let bytes = packet.into_buffer().into_bytes().freeze(); + self.server_outbound + .send_raw(dest, NetPacket::new(bytes)?) + .await?; + } + Ok(()) + } +} + +#[derive(Clone)] +pub(crate) struct HybridOutbound { + network: SharedNetworkAddr, + server_info: ServerInfoCollection, + traffic_stats: TrafficStats, + basic_outbound: BasicOutbound, + packet_compression: PacketCompression, + external_route: SubnetExternalRoute, + fec_encoder: Option, +} +impl HybridOutbound { + pub fn new( + network: SharedNetworkAddr, + server_info: ServerInfoCollection, + traffic_stats: TrafficStats, + basic_outbound: BasicOutbound, + packet_compression: PacketCompression, + external_route: SubnetExternalRoute, + fec_encoder: Option, + ) -> Self { + Self { + network, + server_info, + traffic_stats, + basic_outbound, + packet_compression, + external_route, + fec_encoder, + } + } + pub async fn outbound_raw( + &self, + dest: Ipv4Addr, + mut packet: NetPacket, + ) -> anyhow::Result<()> { + if packet.src_id() == 0 { + if let Some(ip) = self.network.ip() { + packet.set_src_id(ip.into()); + } else { + bail!("Not src ip") + } + } + + let len = packet.buffer().len() as u64; + + if let Some(fec_encoder) = &self.fec_encoder { + packet = fec_encoder.encode(packet)?; + } + + self.basic_outbound.send_raw(dest, packet).await?; + self.traffic_stats.record_tx(dest, len); + Ok(()) + } + pub async fn ipv4_outbound_common(&self, data: TransmissionBytes) -> anyhow::Result<()> { + let Some(net) = self.network.get() else { + bail!("Not src ip") + }; + self.ipv4_outbound(net, data).await + } + pub async fn ipv4_outbound( + &self, + net: NetworkAddr, + mut data: TransmissionBytes, + ) -> anyhow::Result<()> { + let Some(ipv4) = Ipv4Packet::new(data.as_ref()) else { + return Ok(()); + }; + let mut dest = ipv4.get_destination(); + let len = data.len() as u64; + data.retreat_head(HEAD_LENGTH)?; + let mut packet = NetPacket::new(data)?; + packet.set_msg_type(MsgType::Turn); + packet.set_src_id(net.ip.into()); + packet.set_ttl(5); + // 路由 + if !net.network().contains(&dest) { + if let Some(v) = self.external_route.route(&dest) { + dest = v; + } else { + return Ok(()); + } + } + packet.set_dest_id(dest.into()); + + packet = self + .packet_compression + .compress(packet, self.basic_outbound.encrypt_reserve())?; + + if let Some(fec_encoder) = &self.fec_encoder { + packet = fec_encoder.encode(packet)?; + } + + // 发送 + self.basic_outbound + .send_encrypted_packet(dest, packet) + .await?; + self.traffic_stats.record_tx(dest, len); + Ok(()) + } + pub async fn ipv4_gateway_outbound( + &self, + net: NetworkAddr, + mut data: TransmissionBytes, + ) -> anyhow::Result<()> { + data.retreat_head(HEAD_LENGTH)?; + let mut packet = NetPacket::new(data)?; + packet.set_msg_type(MsgType::Turn); + packet.set_src_id(net.ip.into()); + packet.set_dest_id(net.gateway.into()); + packet.set_ttl(5); + packet.set_gateway_flag(true); + self.basic_outbound.send_default_raw(packet).await?; + Ok(()) + } + pub async fn ipv4_broadcast_outbound( + &self, + net: NetworkAddr, + mut data: TransmissionBytes, + ) -> anyhow::Result<()> { + data.retreat_head(HEAD_LENGTH)?; + let mut packet = NetPacket::new(data)?; + packet.set_msg_type(MsgType::Broadcast); + packet.set_src_id(net.ip.into()); + packet.set_dest_id(Ipv4Addr::BROADCAST.into()); + packet.set_ttl(5); + let mut packet = self + .packet_compression + .compress(packet, self.basic_outbound.encrypt_reserve())?; + self.basic_outbound.encrypt_in_place(&mut packet)?; + let packet_bytes = packet.into_bytes(); + let list = self.server_info.client_online_ips(); + let exclude_ips = self + .basic_outbound + .p2p_broadcast_transmission(&list, 16, &packet_bytes); + if let Some(exclude_ips) = &exclude_ips + && exclude_ips.len() == list.len() + { + return Ok(()); + } + + self.basic_outbound + .send_raw_broadcast(exclude_ips, packet_bytes) + .await + } + #[allow(dead_code)] + pub fn has_route(&self, dest: &Ipv4Addr) -> bool { + self.basic_outbound.exists_route(dest) + } +} diff --git a/vnt-core/src/tunnel_core/p2p/inbound.rs b/vnt-core/src/tunnel_core/p2p/inbound.rs new file mode 100644 index 0000000..9a4e61c --- /dev/null +++ b/vnt-core/src/tunnel_core/p2p/inbound.rs @@ -0,0 +1,268 @@ +use crate::compression::PacketCompression; +use crate::context::{NetworkRoute, PacketLossStats}; +use crate::crypto::PacketCrypto; +use crate::enhanced_tunnel::inbound::EnhancedInbound; +use crate::fec::FecDecoder; +use crate::protocol::ip_packet_protocol::{HEAD_LENGTH, MsgType, NetPacket}; +use crate::protocol::transmission::TransmissionBytes; +use crate::tunnel_core::p2p::outbound::P2pOutbound; +use crate::tunnel_core::p2p::route_table::{Route, RouteTable}; +use anyhow::bail; +use rust_p2p_core::route::RouteKey; +use rust_p2p_core::tunnel::Tunnel; +use std::net::{IpAddr, Ipv4Addr}; + +struct PacketContext { + msg_type: MsgType, + src_ip: Ipv4Addr, + dest_ip: Ipv4Addr, + max_ttl: u8, + ttl: u8, +} + +pub(crate) struct P2pInboundConfig { + pub network_route: NetworkRoute, + pub route_table: RouteTable, + pub packet_loss_stats: PacketLossStats, + pub packet_crypto: PacketCrypto, + pub packet_compression: PacketCompression, + pub enhanced_inbound: EnhancedInbound, + pub fec_decoder: FecDecoder, +} + +#[derive(Clone)] +pub(crate) struct P2pInboundHandler { + network_route: NetworkRoute, + route_table: RouteTable, + packet_loss_stats: PacketLossStats, + packet_crypto: PacketCrypto, + packet_compression: PacketCompression, + enhanced_inbound: EnhancedInbound, + fec_decoder: FecDecoder, +} + +impl P2pInboundHandler { + pub fn new(config: P2pInboundConfig) -> Self { + Self { + network_route: config.network_route, + route_table: config.route_table, + packet_loss_stats: config.packet_loss_stats, + packet_crypto: config.packet_crypto, + packet_compression: config.packet_compression, + enhanced_inbound: config.enhanced_inbound, + fec_decoder: config.fec_decoder, + } + } + fn network_contains(&self, ip: &Ipv4Addr) -> bool { + self.network_route.network_contains(ip) + } + pub async fn next_handle( + &self, + buf: TransmissionBytes, + route_key: RouteKey, + p2p_socket_manager: &P2pOutbound, + tunnel: &mut Tunnel, + ) { + if let Err(e) = self + .next_handle_impl(buf, route_key, p2p_socket_manager, tunnel) + .await + { + log::warn!( + "Error while handling P2pInboundHandler: {:?},route={route_key:?}", + e + ); + } + } + async fn next_handle_impl( + &self, + buf: TransmissionBytes, + route_key: RouteKey, + p2p_socket_manager: &P2pOutbound, + tunnel: &mut Tunnel, + ) -> anyhow::Result<()> { + let mut net_packet = NetPacket::new(buf)?; + let msg_type = net_packet.msg_type()?; + let src_ip = Ipv4Addr::from(net_packet.src_id()); + let dest_ip = Ipv4Addr::from(net_packet.dest_id()); + if src_ip == dest_ip { + return Ok(()); + } + net_packet.decr_ttl(); + + let max_ttl = net_packet.max_ttl(); + let ttl = net_packet.ttl(); + if max_ttl <= ttl { + return Ok(()); + } + let Some(net) = self.network_route.network.get() else { + bail!("未找到自身IP") + }; + if net.ip != dest_ip + && !dest_ip.is_broadcast() + && !dest_ip.is_unspecified() + && dest_ip != net.broadcast + { + // 帮忙转发数据包 + if ttl >= 1 { + if let Some(route) = p2p_socket_manager.get_route_by_id(&dest_ip) { + p2p_socket_manager + .send_raw_to(net_packet.into_bytes(), &route.route_key()) + .await?; + } else { + log::debug!("未找到到 {} 的路由,无法转发", dest_ip); + } + } + return Ok(()); + } + + if msg_type == MsgType::Quic { + if net_packet.is_fec() { + let packets = self.fec_decoder.receive(net_packet)?; + if let Some(packets) = packets { + for pkt in packets { + self.enhanced_inbound + .inbound(&net, msg_type, src_ip, pkt) + .await?; + } + } + return Ok(()); + } + self.enhanced_inbound + .inbound(&net, msg_type, src_ip, net_packet) + .await?; + return Ok(()); + } + + // 解密 + self.packet_crypto.decrypt_in_place(&mut net_packet)?; + + let ctx = PacketContext { + msg_type, + src_ip, + dest_ip, + max_ttl, + ttl, + }; + + // FEC 解码(始终尝试解码,如果有 FEC 标志) + if net_packet.is_fec() { + let packets = self.fec_decoder.receive(net_packet)?; + if let Some(packets) = packets { + for pkt in packets { + let pkt = self.packet_compression.decompress(pkt)?; + self.process_decompressed_packet(&net, route_key, tunnel, pkt, &ctx) + .await?; + } + } + return Ok(()); + } + + // 解压缩 + let net_packet = self.packet_compression.decompress(net_packet)?; + self.process_decompressed_packet(&net, route_key, tunnel, net_packet, &ctx) + .await + } + + async fn process_decompressed_packet( + &self, + net: &crate::context::NetworkAddr, + route_key: RouteKey, + tunnel: &mut Tunnel, + net_packet: NetPacket, + ctx: &PacketContext, + ) -> anyhow::Result<()> { + match ctx.msg_type { + MsgType::Turn | MsgType::Broadcast | MsgType::ExcludeBroadcast => { + self.enhanced_inbound + .inbound(net, ctx.msg_type, ctx.src_ip, net_packet) + .await?; + } + MsgType::Ping => { + let metric = ctx.max_ttl - ctx.ttl; + self.route_table + .add_route_if_absent(ctx.src_ip, Route::from_default_rt(route_key, metric)); + let mut packet = NetPacket::new(TransmissionBytes::zeroed_size( + HEAD_LENGTH + 8, + self.packet_crypto.encrypt_reserve(), + ))?; + packet.set_msg_type(MsgType::Pong); + packet.set_ttl(1); + packet.set_src_id(ctx.dest_ip.into()); + packet.set_dest_id(ctx.src_ip.into()); + packet.set_payload(net_packet.payload())?; + self.packet_crypto.encrypt_in_place(&mut packet)?; + tunnel + .send_to(packet.into_bytes().into_buffer(), route_key.addr()) + .await?; + } + MsgType::Pong => { + if net_packet.payload().len() >= 8 { + let metric = ctx.max_ttl - ctx.ttl; + let time = i64::from_be_bytes(net_packet.payload()[..8].try_into()?); + let now = crate::utils::time::now_ts_ms(); + if now >= time { + self.route_table.add_route( + ctx.src_ip, + Route::from(route_key, metric, (now - time) as _), + ); + self.packet_loss_stats.record_received(ctx.src_ip); + } + } + } + MsgType::PunchStart1 => {} + MsgType::PunchStart2 => {} + MsgType::PunchReq => { + if let IpAddr::V4(ip) = route_key.addr().ip() + && self.network_contains(&ip) + { + log::info!("===========loop PunchReq {route_key:?} {:?}", ctx.src_ip); + return Ok(()); + } + log::info!( + "PunchReq 打洞成功 {}->{},route={route_key:?}", + ctx.src_ip, + ctx.dest_ip + ); + self.route_table.add_owner_route(ctx.src_ip, route_key); + let mut packet = NetPacket::new(TransmissionBytes::zeroed_size( + HEAD_LENGTH + 8, + self.packet_crypto.encrypt_reserve(), + ))?; + packet.set_msg_type(MsgType::PunchRes); + packet.set_ttl(1); + packet.set_src_id(ctx.dest_ip.into()); + packet.set_dest_id(ctx.src_ip.into()); + packet.set_payload(&crate::utils::time::now_ts_ms().to_be_bytes())?; + + self.packet_crypto.encrypt_in_place(&mut packet)?; + tunnel + .send_to(packet.into_bytes().into_buffer(), route_key.addr()) + .await?; + } + MsgType::PunchRes => { + if let IpAddr::V4(ip) = route_key.addr().ip() + && self.network_contains(&ip) + { + log::info!("===========loop PunchRes {route_key:?} {:?}", ctx.src_ip); + return Ok(()); + } + log::info!( + "PunchRes 打洞成功 {}->{},route={route_key:?}", + ctx.src_ip, + ctx.dest_ip + ); + self.route_table.add_owner_route(ctx.src_ip, route_key); + } + MsgType::PingTurn => {} + MsgType::PongTurn => {} + _ => {} + } + Ok(()) + } + + pub async fn tcp_disconnect(&self, route_key: RouteKey) { + if let Some(ip) = self.route_table.get_id_by_route_key(&route_key) { + self.route_table.remove_route(&ip, &route_key); + } + } +} diff --git a/vnt-core/src/tunnel_core/p2p/mod.rs b/vnt-core/src/tunnel_core/p2p/mod.rs new file mode 100644 index 0000000..220b13a --- /dev/null +++ b/vnt-core/src/tunnel_core/p2p/mod.rs @@ -0,0 +1,5 @@ +pub(crate) mod inbound; +pub(crate) mod outbound; +pub(crate) mod transport; + +pub(crate) mod route_table; diff --git a/vnt-core/src/tunnel_core/p2p/outbound.rs b/vnt-core/src/tunnel_core/p2p/outbound.rs new file mode 100644 index 0000000..962041a --- /dev/null +++ b/vnt-core/src/tunnel_core/p2p/outbound.rs @@ -0,0 +1,133 @@ +use crate::crypto::PacketCrypto; +use crate::protocol::ip_packet_protocol::NetPacket; +use crate::protocol::transmission::TransmissionBytes; +use crate::tunnel_core::p2p::route_table::{Route, RouteTable}; +use bytes::Bytes; +use rust_p2p_core::route::RouteKey; +use rust_p2p_core::tunnel::SocketManager; +use std::net::Ipv4Addr; + +#[derive(Clone)] +pub(crate) struct P2pOutbound { + manager: SocketManager, + route_table: RouteTable, + packet_crypto: PacketCrypto, +} +impl P2pOutbound { + pub fn new( + manager: SocketManager, + route_table: RouteTable, + packet_crypto: PacketCrypto, + ) -> Self { + Self { + manager, + route_table, + packet_crypto, + } + } + pub fn encrypt_reserve(&self) -> usize { + self.packet_crypto.encrypt_reserve() + } + // pub async fn send_raw(&self, buf: NetPacket) -> anyhow::Result<()> { + // let dest_id = Ipv4Addr::from(buf.dest_id()); + // let route = self.route_table.get_route_by_id(&dest_id)?; + // self.manager + // .send_to(buf.into_buffer(), &route.route_key()) + // .await?; + // Ok(()) + // } + // pub async fn send(&self, mut buf: NetPacket) -> anyhow::Result<()> { + // let dest_id = Ipv4Addr::from(buf.dest_id()); + // let route = self.route_table.get_route_by_id(&dest_id)?; + // self.packet_crypto.encrypt_in_place(&mut buf)?; + // self.manager + // .send_to(buf.into_buffer().into_bytes().freeze(), &route.route_key()) + // .await?; + // Ok(()) + // } + pub async fn send_raw_to( + &self, + buf: NetPacket, + route_key: &RouteKey, + ) -> anyhow::Result<()> { + self.manager.send_to(buf.into_buffer(), route_key).await?; + Ok(()) + } + pub async fn send_to( + &self, + mut buf: NetPacket, + route_key: &RouteKey, + ) -> anyhow::Result<()> { + self.packet_crypto.encrypt_in_place(&mut buf)?; + self.manager + .send_to(buf.into_buffer().into_bytes().freeze(), route_key) + .await?; + Ok(()) + } + pub fn get_route_by_id(&self, id: &Ipv4Addr) -> Option { + self.route_table.get_route_by_id(id).ok() + } + pub fn get_p2p_route_by_id(&self, id: &Ipv4Addr) -> Option { + self.route_table.get_route_by_id(id).ok().filter(|v| v.is_direct()) + } + pub fn exists_route_by_id(&self, id: &Ipv4Addr) -> bool { + self.route_table.exists(id) + } + + // pub async fn send_to_id( + // &self, + // buf: NetPacket, + // id: &Ipv4Addr, + // ) -> anyhow::Result { + // let Ok(route) = self.route_table.get_route_by_id(id) else { + // return Ok(false); + // }; + // self.send_to(buf, &route.route_key()).await?; + // Ok(true) + // } + // pub fn try_send_to_id( + // &self, + // buf: NetPacket, + // id: &Ipv4Addr, + // ) -> anyhow::Result { + // let Ok(route) = self.route_table.get_route_by_id(id) else { + // return Ok(false); + // }; + // self.try_send_to(buf, &route.route_key())?; + // Ok(true) + // } + // pub fn try_send_to( + // &self, + // buf: NetPacket, + // route_key: &RouteKey, + // ) -> anyhow::Result<()> { + // self.manager + // .try_send_to(buf.into_buffer().into_bytes(), route_key)?; + // Ok(()) + // } + pub fn p2p_broadcast( + &self, + ips: &[Ipv4Addr], + max: usize, + buf: &NetPacket, + ) -> Vec { + let mut list = Vec::with_capacity(ips.len().min(max)); + + for id in ips { + let Some(route) = self.get_p2p_route_by_id(id) else { + continue; + }; + if self + .manager + .try_send_to(buf.source_buf().clone(), &route.route_key()) + .is_ok() + { + list.push(*id); + if list.len() >= max { + break; + } + } + } + list + } +} diff --git a/vnt-core/src/tunnel_core/p2p/route_table.rs b/vnt-core/src/tunnel_core/p2p/route_table.rs new file mode 100644 index 0000000..64a972a --- /dev/null +++ b/vnt-core/src/tunnel_core/p2p/route_table.rs @@ -0,0 +1,265 @@ +use parking_lot::{Mutex, RwLock}; +use rust_p2p_core::route::{RouteKey, DEFAULT_RTT}; +use std::collections::HashMap; +use std::net::Ipv4Addr; +use std::sync::Arc; +use std::time::Instant; + + +#[derive(Copy, Clone, Debug)] +pub struct Route { + route_key: RouteKey, + metric: u8, + rtt: u32, +} +impl Route { + pub fn from(route_key: RouteKey, metric: u8, rtt: u32) -> Self { + Self { + route_key, + metric, + rtt, + } + } + pub fn from_default_rt(route_key: RouteKey, metric: u8) -> Self { + Self { + route_key, + metric, + rtt: DEFAULT_RTT, + } + } + pub fn route_key(&self) -> RouteKey { + self.route_key + } + + pub fn is_direct(&self) -> bool { + self.metric == 1 + } + pub fn rtt(&self) -> u32 { + self.rtt + } + pub fn metric(&self) -> u8 { + self.metric + } +} + +#[derive(Clone)] +pub struct RouteTable { + inner: Arc, +} + +#[derive(Default)] +struct RouteTableInner { + route_table: RwLock>>, + route_key_time: Mutex>, + route_key_owner: Mutex>, +} + +impl Default for RouteTable { + fn default() -> Self { + Self::new() + } +} + +impl RouteTable { + pub fn new() -> Self { + Self { + inner: Arc::new(RouteTableInner::default()), + } + } + + /// 获取指定 ID 的最优路由 + pub fn get_route_by_id(&self, id: &Ipv4Addr) -> anyhow::Result { + self.inner + .get_by_id(id) + .ok_or_else(|| anyhow::anyhow!("route not found for {}", id)) + } + + /// 检查是否存在到指定 ID 的路由 + pub fn exists(&self, id: &Ipv4Addr) -> bool { + self.inner.get_by_id(id).is_some() + } + + /// 判断是否需要打洞(没有路由或只有中继路由) + pub fn need_punch(&self, id: &Ipv4Addr) -> bool { + let guard = self.inner.route_table.read(); + let Some(list) = guard.get(id) else { + return true; + }; + // 如果没有直连路由(metric=1),则需要打洞 + !list.iter().any(|r| r.is_direct()) + } + + /// 获取直连路由数量(用于判断是否直连) + pub fn p2p_num(&self, id: &Ipv4Addr) -> usize { + let guard = self.inner.route_table.read(); + let Some(list) = guard.get(id) else { + return 0; + }; + list.iter().filter(|r| r.is_direct()).count() + } + + /// 添加 owner 路由(打洞请求响应时调用) + pub fn add_owner_route(&self, id: Ipv4Addr, key: RouteKey) { + self.inner.add_owner_route(id, key); + } + + /// 添加路由(心跳时调用,用于更新路由时间和添加跨节点转发路由) + pub fn add_route(&self, id: Ipv4Addr, route: Route) { + self.inner.add_route(id, route); + } + + /// 如果路由不存在则添加(用于 Ping 消息) + pub fn add_route_if_absent(&self, id: Ipv4Addr, route: Route) { + let guard = self.inner.route_table.read(); + if guard.contains_key(&id) { + return; + } + drop(guard); + self.inner.add_route(id, route); + } + + /// 获取所有路由表 + pub fn route_table(&self) -> Vec<(Ipv4Addr, Vec)> { + let guard = self.inner.route_table.read(); + guard.iter().map(|(k, v)| (*k, v.clone())).collect() + } + + /// 根据 RouteKey 查找对应的 IP + pub fn get_id_by_route_key(&self, route_key: &RouteKey) -> Option { + let owner_map = self.inner.route_key_owner.lock(); + owner_map.get(route_key).copied() + } + + /// 移除指定 IP 和 RouteKey 的路由 + pub fn remove_route(&self, id: &Ipv4Addr, route_key: &RouteKey) { + let mut table = self.inner.route_table.write(); + let mut owner_map = self.inner.route_key_owner.lock(); + let mut time_map = self.inner.route_key_time.lock(); + + if let Some(list) = table.get_mut(id) { + list.retain(|r| r.route_key() != *route_key); + if list.is_empty() { + table.remove(id); + } + } + + if let Some(owner_id) = owner_map.get(route_key) { + if owner_id == id { + owner_map.remove(route_key); + } + } + + time_map.remove(&(*id, *route_key)); + } + + /// 移除过期的路由 + pub fn remove_oldest_route(&self, expired_time: Instant) { + self.inner.remove_oldest_route(expired_time); + } +} + +impl RouteTableInner { + fn get_by_id(&self, id: &Ipv4Addr) -> Option { + let guard = self.route_table.read(); + let list = guard.get(id)?; + list.first().cloned() + } + + fn add_owner_route(&self, id: Ipv4Addr, key: RouteKey) { + let route = Route::from_default_rt(key, 1); + let mut guard = self.route_table.write(); + + self.route_key_owner.lock().insert(key, id); + self.route_key_time.lock().insert((id, key), Instant::now()); + + let list = guard.entry(id).or_insert_with(|| Vec::with_capacity(6)); + if list.iter().any(|v| v.route_key() == key) { + return; + } + list.push(route); + } + + fn add_route(&self, id: Ipv4Addr, route: Route) { + let key = route.route_key(); + let mut guard = self.route_table.write(); + + // 检查是否是 owner 路由 + let mut route_key_owner = self.route_key_owner.lock(); + if route.is_direct() { + route_key_owner.entry(key).or_insert(id); + } else { + if !guard.contains_key(&id) { + return; + } + } + + // 更新时间 + self.route_key_time.lock().insert((id, key), Instant::now()); + + let list = guard.entry(id).or_insert_with(|| Vec::with_capacity(6)); + + // 如果路由已存��,更新并重新排序 + if let Some(idx) = list.iter().position(|v| v.route_key() == key) { + list[idx] = route; + // 向前冒泡(如果 RTT 更小) + let mut i = idx; + while i > 0 && list[i].rtt() < list[i - 1].rtt() { + list.swap(i, i - 1); + i -= 1; + } + // 向后冒泡(如果 RTT 更大) + while i + 1 < list.len() && list[i].rtt() > list[i + 1].rtt() { + list.swap(i, i + 1); + i += 1; + } + return; + } + + // 插入新路由,保持按 RTT 排序 + let mut pos = list.len(); + for (i, r) in list.iter().enumerate() { + if route.rtt() < r.rtt() { + pos = i; + break; + } + } + list.insert(pos, route); + } + + fn remove_oldest_route(&self, expired_time: Instant) { + let mut expired_keys = Vec::new(); + { + let mut time_map = self.route_key_time.lock(); + time_map.retain(|(id, route_key), t| { + if *t <= expired_time { + expired_keys.push((*id, *route_key)); + false + } else { + true + } + }); + } + + if expired_keys.is_empty() { + return; + } + + let mut table = self.route_table.write(); + let mut owner_map = self.route_key_owner.lock(); + + for (id, route_key) in expired_keys { + if let Some(list) = table.get_mut(&id) { + list.retain(|r| r.route_key() != route_key); + if list.is_empty() { + table.remove(&id); + } + } + + if let Some(owner_id) = owner_map.get(&route_key) { + if *owner_id == id { + owner_map.remove(&route_key); + } + } + } + } +} diff --git a/vnt-core/src/tunnel_core/p2p/transport/mod.rs b/vnt-core/src/tunnel_core/p2p/transport/mod.rs new file mode 100644 index 0000000..15d8925 --- /dev/null +++ b/vnt-core/src/tunnel_core/p2p/transport/mod.rs @@ -0,0 +1,4 @@ +pub(crate) mod nat_test; +pub(crate) mod punch; + +pub(crate) mod task; diff --git a/vnt-core/src/tunnel_core/p2p/transport/nat_test.rs b/vnt-core/src/tunnel_core/p2p/transport/nat_test.rs new file mode 100644 index 0000000..604472c --- /dev/null +++ b/vnt-core/src/tunnel_core/p2p/transport/nat_test.rs @@ -0,0 +1,387 @@ +use crate::context::AppState; +use rust_p2p_core::nat::{NatInfo, NatType}; +use rust_p2p_core::tunnel::SocketManager; +use rust_p2p_core::tunnel::udp::Model; +use std::collections::HashMap; +use std::io; +use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}; +use std::time::Duration; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::net::TcpStream; + +pub async fn my_nat_info(app_context: AppState, socket_manager: SocketManager) { + loop { + my_nat_info_impl(&app_context, &socket_manager).await; + tokio::time::sleep(Duration::from_secs(60 * 30)).await; + } +} +async fn my_nat_info_impl(app_context: &AppState, socket_manager: &SocketManager) { + let network = app_context.network.network(); + let mut local_ipv4s = Vec::new(); + let mut local_ipv6 = Vec::new(); + match getifaddrs::getifaddrs() { + Ok(addrs) => { + for x in addrs { + let Some(ip) = x.address.ip_addr() else { + continue; + }; + if ip.is_loopback() { + continue; + } + if ip.is_unspecified() { + continue; + } + if ip.is_multicast() { + continue; + } + + match ip { + IpAddr::V4(addr) => { + if addr.is_documentation() { + continue; + } + if addr.is_broadcast() { + continue; + } + if let Some(network) = &network + && network.contains(&addr) + { + continue; + } + local_ipv4s.push(addr); + } + IpAddr::V6(addr) => { + if addr.is_unique_local() { + continue; + } + if addr.is_unicast_link_local() { + continue; + } + local_ipv6.push(addr); + } + } + } + } + Err(e) => { + log::error!("getifaddrs error: {e}"); + } + } + log::info!("local_ipv4s: {:?}", local_ipv4s); + let local_ipv4 = rust_p2p_core::extend::addr::local_ipv4() + .await + .unwrap_or_else(|e| { + log::warn!("local ipv4 failed {e:?}"); + local_ipv4s + .first() + .cloned() + .unwrap_or(Ipv4Addr::UNSPECIFIED) + }); + local_ipv4s = vec![local_ipv4]; + let mut ipv6 = rust_p2p_core::extend::addr::local_ipv6().await.ok(); + if let Some(addr) = ipv6 { + if addr.is_loopback() + || addr.is_unique_local() + || addr.is_unicast_link_local() + || addr.is_unspecified() + || addr.is_multicast() + { + ipv6 = local_ipv6.first().cloned(); + } + } else { + ipv6 = local_ipv6.first().cloned(); + } + let local_udp_ports = socket_manager + .udp_socket_manager_as_ref() + .unwrap() + .local_ports() + .unwrap(); + let local_tcp_port = socket_manager + .tcp_socket_manager_as_ref() + .unwrap() + .local_addr() + .port(); + log::info!( + "local_ipv4={local_ipv4},ipv6={ipv6:?},local_udp_ports:{local_udp_ports:?},local_tcp_port:{local_tcp_port:?}" + ); + let mut public_ports = local_udp_ports.clone(); + public_ports.fill(0); + let mut nat_info = NatInfo { + nat_type: NatType::Cone, + public_ips: vec![], + public_udp_ports: public_ports, + mapping_tcp_addr: vec![], + mapping_udp_addr: vec![], + public_port_range: 0, + local_ipv4s, + local_ipv4, + ipv6, + local_udp_ports, + local_tcp_port, + public_tcp_port: 0, + }; + let mut stun_server = app_context.udp_stun(); + if stun_server.is_empty() { + stun_server = default_udp_stun(); + } + let (nat_type, public_ips, port_range) = rust_p2p_core::stun::stun_test_nat(stun_server, None) + .await + .unwrap_or_else(|e| { + log::warn!("stun_test_nat {e:?}"); + (NatType::Cone, vec![], 0) + }); + log::info!("nat_type:{nat_type:?},public_ips:{public_ips:?},port_range={port_range}"); + nat_info.nat_type = nat_type; + nat_info.public_ips = public_ips; + nat_info.public_port_range = port_range; + app_context.nat_info.replace_nat_info(nat_info); + let model = match nat_type { + NatType::Cone => Model::Low, + NatType::Symmetric => Model::High, + }; + if let Err(e) = socket_manager + .udp_socket_manager_as_ref() + .unwrap() + .switch_model(model) + { + log::error!("switch_model error: {e:?}"); + } +} + +pub async fn query_udp_public_addr_loop(app_context: AppState, socket_manager: SocketManager) { + let mut udp_stun_servers = app_context.udp_stun(); + if udp_stun_servers.is_empty() { + udp_stun_servers = default_udp_stun(); + } + let udp_len = udp_stun_servers.len(); + let mut udp_count = 0; + let stun_request = rust_p2p_core::stun::send_stun_request(); + loop { + let stun = &udp_stun_servers[udp_count % udp_len]; + udp_count += 1; + match tokio::net::lookup_host(stun.as_str()).await { + Ok(mut addr) => { + if let Some(addr) = addr.next() + && let Some(w) = socket_manager.udp_socket_manager_as_ref() + && let Err(e) = w.detect_pub_addrs(&stun_request, addr).await + { + log::info!("detect_pub_addrs {e:?} {addr:?}"); + } + } + Err(e) => { + log::info!("query_public_addr lookup_host {e:?} {stun:?}",); + } + } + let not_port = app_context + .get_nat_info() + .map(|v| v.public_udp_ports.contains(&0)) + .unwrap_or(true); + if not_port { + tokio::time::sleep(Duration::from_secs(2)).await; + } else { + tokio::time::sleep(Duration::from_secs(60)).await; + } + } +} + +pub(crate) async fn query_tcp_public_addr_loop( + app_context: AppState, + socket_manager: SocketManager, +) { + use rand::Rng; + use rand::seq::SliceRandom; + + let tcp_stun_servers = { + let servers = app_context.tcp_stun(); + if servers.is_empty() { + default_tcp_stun() + } else { + servers + } + }; + + if tcp_stun_servers.is_empty() { + return; + } + log::debug!("tcp_stun_servers = {tcp_stun_servers:?}"); + + let stun_request = rust_p2p_core::stun::send_stun_request(); + let target_conn_count = tcp_stun_servers.len().min(2); + let mut active_connections: HashMap = HashMap::new(); + + 'outer: loop { + while active_connections.len() < target_conn_count { + let mut candidates: Vec<&String> = tcp_stun_servers.iter().collect(); + candidates.shuffle(&mut rand::rng()); + + let mut connected = false; + for stun in candidates { + let addr = match tokio::net::lookup_host(stun.as_str()).await { + Ok(mut addrs) => addrs.next(), + Err(e) => { + log::debug!("lookup_host failed {stun} {e}"); + continue; + } + }; + + let Some(addr) = addr else { + continue; + }; + + if active_connections.contains_key(&addr) { + continue; + } + + let Some(w) = socket_manager.tcp_socket_manager_as_ref() else { + continue; + }; + + match tokio::time::timeout(Duration::from_secs(5), w.connect_reuse_port_raw(addr)) + .await + { + Ok(Ok(mut tcp_stream)) => { + let write_result = tokio::time::timeout( + Duration::from_secs(5), + tcp_stream.write_all(&stun_request), + ) + .await; + + if let Ok(Ok(_)) = write_result { + match stun_tcp_read(&mut tcp_stream).await { + Ok(pub_addr) => { + log::debug!( + "update_tcp_public_addr {stun} {addr} -> {pub_addr}" + ); + + let existing_pub_addr = + active_connections.values().next().map(|(_, p)| *p); + + if let Some(existing) = existing_pub_addr + && existing != pub_addr + { + log::debug!( + "pub_addr mismatch: {existing} != {pub_addr}, wait 60s" + ); + active_connections.clear(); + app_context.nat_info.update_tcp_public_addr( + SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0).into(), + ); + tokio::time::sleep(Duration::from_secs(5 * 60)).await; + continue 'outer; + } + + active_connections.insert(addr, (tcp_stream, pub_addr)); + connected = true; + break; + } + Err(e) => { + log::debug!("stun_tcp_read failed {stun} {addr} {e}"); + } + } + } else { + log::debug!("write stun request failed {stun} {addr}"); + } + } + Ok(Err(e)) => { + log::debug!("connect_reuse_port_raw failed {stun} {addr} {e}"); + } + Err(_) => { + log::debug!("connect_reuse_port_raw timeout {stun} {addr}"); + } + } + } + + if !connected { + break; + } + } + let existing_pub_addr = active_connections.values().next().map(|(_, p)| *p); + if let Some(existing) = existing_pub_addr { + app_context.nat_info.update_tcp_public_addr(existing); + } + + let sleep_secs = rand::rng().random_range(10u64..=15); + tokio::time::sleep(Duration::from_secs(sleep_secs)).await; + + let mut to_remove = Vec::new(); + let addrs: Vec = active_connections.keys().cloned().collect(); + + for addr in addrs { + let (tcp_stream, _) = active_connections.get_mut(&addr).unwrap(); + let mut buf = [0u8; 1024]; + + match tcp_stream.try_read(&mut buf) { + Ok(0) => { + log::warn!("stun tcp close {addr} EOF"); + to_remove.push(addr); + continue; + } + Err(e) if e.kind() != std::io::ErrorKind::WouldBlock => { + log::warn!("stun tcp read error {addr} {e}"); + to_remove.push(addr); + continue; + } + _ => {} + } + + match tokio::time::timeout(Duration::from_secs(3), tcp_stream.write_all(&stun_request)) + .await + { + Ok(Ok(_)) => {} + Ok(Err(e)) => { + log::warn!("stun tcp write error {addr} {e}"); + to_remove.push(addr); + } + Err(_) => { + log::warn!("stun tcp write timeout {addr}"); + to_remove.push(addr); + } + } + } + + for addr in to_remove { + active_connections.remove(&addr); + } + } +} + +async fn stun_tcp_read(tcp_stream: &mut TcpStream) -> io::Result { + let mut head = [0; 20]; + match tokio::time::timeout(Duration::from_secs(5), tcp_stream.read_exact(&mut head)).await { + Ok(rs) => rs?, + Err(_) => Err(io::Error::from(io::ErrorKind::TimedOut))?, + }; + let len = u16::from_be_bytes([head[2], head[3]]) as usize; + let mut buf = vec![0; len + 20]; + buf[..20].copy_from_slice(&head); + match tokio::time::timeout( + Duration::from_secs(5), + tcp_stream.read_exact(&mut buf[20..]), + ) + .await + { + Ok(rs) => rs?, + Err(_) => Err(io::Error::from(io::ErrorKind::TimedOut))?, + }; + if let Some(addr) = rust_p2p_core::stun::recv_stun_response(&buf) { + Ok(addr) + } else { + log::debug!("stun_tcp_read {buf:?}"); + Err(io::Error::from(io::ErrorKind::InvalidData)) + } +} + +fn default_udp_stun() -> Vec { + vec![ + "stun.miwifi.com:3478".to_string(), + "stun.chat.bilibili.com:3478".to_string(), + "stun.l.google.com:19302".to_string(), + ] +} + +fn default_tcp_stun() -> Vec { + vec![ + "stun.flashdance.cx:3478".to_string(), + "stun.sipnet.net:3478".to_string(), + "stun.nextcloud.com:443".to_string(), + ] +} diff --git a/vnt-core/src/tunnel_core/p2p/transport/punch.rs b/vnt-core/src/tunnel_core/p2p/transport/punch.rs new file mode 100644 index 0000000..1faa917 --- /dev/null +++ b/vnt-core/src/tunnel_core/p2p/transport/punch.rs @@ -0,0 +1,148 @@ +use crate::context::nat::PunchBackoff; +use crate::context::{ServerInfoCollection, SharedNetworkAddr}; +use crate::crypto::PacketCrypto; +use crate::protocol::client_message::PunchInfo; +use crate::protocol::ip_packet_protocol::{HEAD_LENGTH, MsgType, NetPacket}; +use crate::protocol::transmission::TransmissionBytes; +use crate::tunnel_core::p2p::route_table::RouteTable; +use crate::tunnel_core::server::outbound::ServerOutbound; +use anyhow::bail; +use log::error; +use rand::seq::SliceRandom; +use rust_p2p_core::punch::{PunchModel, Puncher}; +use std::net::Ipv4Addr; +use std::time::Duration; + +pub struct PunchTaskContext { + pub network: SharedNetworkAddr, + pub server_info: ServerInfoCollection, + pub punch_backoff: PunchBackoff, + pub punch_info_getter: PunchInfoGetter, +} + +pub type PunchInfoGetter = std::sync::Arc Option + Send + Sync>; + +pub async fn punch_task( + tunnel_to_server: ServerOutbound, + route_table: RouteTable, + ctx: PunchTaskContext, +) -> anyhow::Result<()> { + loop { + tokio::time::sleep(Duration::from_secs(5)).await; + let Some(src_ip) = ctx.network.ip() else { + continue; + }; + let Some(punch_info) = (ctx.punch_info_getter)() else { + continue; + }; + let mut list = ctx.server_info.client_online_ips(); + list.shuffle(&mut rand::rng()); + list.truncate(5); + for dest_ip in list { + if dest_ip <= src_ip { + continue; + } + if ctx.server_info.is_any_server_connected(None) && route_table.need_punch(&dest_ip) { + if !ctx.punch_backoff.should_punch(dest_ip) { + continue; + } + log::info!("punching {dest_ip}"); + + let data = punch_info.encode(); + let mut net_packet = NetPacket::new(TransmissionBytes::zeroed_size( + HEAD_LENGTH + data.len(), + tunnel_to_server.encrypt_reserve(), + ))?; + net_packet.set_msg_type(MsgType::PunchStart1); + net_packet.set_ttl(2); + net_packet.set_src_id(src_ip.into()); + net_packet.set_dest_id(dest_ip.into()); + net_packet.set_payload(data.as_ref())?; + if let Err(e) = tunnel_to_server.send(dest_ip, net_packet).await { + error!("punch send error {:?}", e); + } + } + } + } +} +#[derive(Clone)] +pub struct NatPuncher { + network: SharedNetworkAddr, + punch_backoff: PunchBackoff, + puncher: Option, + packet_crypto: PacketCrypto, +} + +impl NatPuncher { + pub fn new( + network: SharedNetworkAddr, + punch_backoff: PunchBackoff, + puncher: Option, + packet_crypto: PacketCrypto, + ) -> Self { + Self { + network, + punch_backoff, + puncher, + packet_crypto, + } + } + pub fn punch(&self, dest_ip: Ipv4Addr, punch_info: PunchInfo) -> anyhow::Result { + if self.puncher.is_none() { + return Ok(false); + } + if !self.punch_backoff.should_punch(dest_ip) { + return Ok(false); + } + self.punch_uncheck_delay(dest_ip, punch_info, Some(Duration::from_millis(50)))?; + Ok(true) + } + pub fn punch_uncheck(&self, dest_ip: Ipv4Addr, punch_info: PunchInfo) -> anyhow::Result<()> { + self.punch_uncheck_delay(dest_ip, punch_info, None) + } + pub fn punch_uncheck_delay( + &self, + dest_ip: Ipv4Addr, + punch_info: PunchInfo, + time: Option, + ) -> anyhow::Result<()> { + let Some(puncher) = self.puncher.clone() else { + return Ok(()); + }; + let Some(src_ip) = self.network.ip() else { + bail!("not ip"); + }; + let packet_crypto = self.packet_crypto.clone(); + tokio::spawn(async move { + if let Some(time) = time { + tokio::time::sleep(time).await; + } + if let Err(e) = punch_now(puncher, src_ip, dest_ip, punch_info, packet_crypto).await { + log::warn!("punch send error {:?}", e); + } + }); + Ok(()) + } +} +async fn punch_now( + puncher: Puncher, + src_ip: Ipv4Addr, + dest_ip: Ipv4Addr, + nat_info: PunchInfo, + packet_crypto: PacketCrypto, +) -> anyhow::Result<()> { + let mut packet = NetPacket::new(TransmissionBytes::zeroed_size( + HEAD_LENGTH + 8, + packet_crypto.encrypt_reserve(), + ))?; + packet.set_msg_type(MsgType::PunchReq); + packet.set_ttl(1); + packet.set_src_id(src_ip.into()); + packet.set_dest_id(dest_ip.into()); + packet.set_payload(&crate::utils::time::now_ts_ms().to_be_bytes())?; + packet_crypto.encrypt_in_place(&mut packet)?; + let buf = packet.buffer(); + let punch_info = rust_p2p_core::punch::PunchInfo::new(PunchModel::all(), nat_info.nat_info); + puncher.punch_now(Some(buf), buf, punch_info).await?; + Ok(()) +} diff --git a/vnt-core/src/tunnel_core/p2p/transport/task.rs b/vnt-core/src/tunnel_core/p2p/transport/task.rs new file mode 100644 index 0000000..b82ec21 --- /dev/null +++ b/vnt-core/src/tunnel_core/p2p/transport/task.rs @@ -0,0 +1,337 @@ +use crate::context::nat::MyNatInfo; +use crate::context::{AppState, PacketLossStats, SharedNetworkAddr}; +use crate::crypto::PacketCrypto; +use crate::protocol::ip_packet_protocol::{HEAD_LENGTH, MsgType, NetPacket}; +use crate::protocol::transmission::TransmissionBytes; +use crate::tunnel_core::p2p::inbound::P2pInboundHandler; +use crate::tunnel_core::p2p::outbound::P2pOutbound; +use crate::tunnel_core::p2p::route_table::RouteTable; +use crate::tunnel_core::p2p::transport::nat_test::{ + my_nat_info, query_tcp_public_addr_loop, query_udp_public_addr_loop, +}; +use crate::tunnel_core::p2p::transport::punch::{PunchTaskContext, punch_task}; +use crate::tunnel_core::server::outbound::ServerOutbound; +use crate::utils::task_control::TaskGroup; +use rust_p2p_core::punch::Puncher; +use rust_p2p_core::tunnel::{Tunnel, TunnelDispatcher, new_tunnel_component}; +use std::net::Ipv4Addr; +use std::sync::Arc; +use std::time::Duration; + +pub async fn init_tunnel( + task_group: TaskGroup, + app_state: AppState, + tunnel_to_server: ServerOutbound, + packet_crypto: PacketCrypto, +) -> anyhow::Result<(Puncher, P2pOutbound, P2pTask)> { + let udp_config = rust_p2p_core::tunnel::config::UdpTunnelConfig::default() + .set_main_udp_count(2) + .set_sub_udp_count(82); + let tcp_config = rust_p2p_core::tunnel::config::TcpTunnelConfig::new(Box::new( + rust_p2p_core::tunnel::tcp::LengthPrefixedInitCodec, + )) + .set_tcp_multiplexing_limit(2); + let config = rust_p2p_core::tunnel::config::TunnelConfig::empty() + .set_udp_tunnel_config(udp_config) + .set_tcp_tunnel_config(tcp_config); + let (tunnel_dispatcher, puncher) = new_tunnel_component(config)?; + let route_table = app_state.route_table.clone(); + let socket_manager = P2pOutbound::new( + tunnel_dispatcher.socket_manager(), + route_table.clone(), + packet_crypto, + ); + task_group.spawn(my_nat_info( + app_state.clone(), + tunnel_dispatcher.socket_manager(), + )); + let manager = tunnel_dispatcher.socket_manager(); + task_group.spawn(query_udp_public_addr_loop( + app_state.clone(), + manager.clone(), + )); + task_group.spawn(query_tcp_public_addr_loop(app_state.clone(), manager)); + + task_group.spawn(route_timeout_task(route_table.clone())); + let app_state_for_punch = app_state.clone(); + let punch_ctx = PunchTaskContext { + network: app_state.network.clone(), + server_info: app_state.server_info_collection.clone(), + punch_backoff: app_state.punch_backoff.clone(), + punch_info_getter: Arc::new(move || app_state_for_punch.get_punch_info()), + }; + task_group.spawn(punch_task(tunnel_to_server, route_table.clone(), punch_ctx)); + task_group.spawn(ping_all( + app_state.network.clone(), + app_state.packet_loss_stats.clone(), + route_table.clone(), + socket_manager.clone(), + )); + task_group.spawn(relay_probe_task( + app_state.network.clone(), + app_state.server_info_collection.clone(), + route_table.clone(), + socket_manager.clone(), + )); + let p2p_task = P2pTask { + task_group, + nat_info: app_state.nat_info.clone(), + socket_manager: socket_manager.clone(), + tunnel_dispatcher, + }; + Ok((puncher, socket_manager, p2p_task)) +} +pub struct P2pTask { + task_group: TaskGroup, + nat_info: MyNatInfo, + socket_manager: P2pOutbound, + tunnel_dispatcher: TunnelDispatcher, +} +impl P2pTask { + pub fn start(self, p2p_inbound_handler: P2pInboundHandler) { + self.task_group.spawn(tunnel_dispatch_task( + self.nat_info, + self.task_group.clone(), + self.tunnel_dispatcher, + p2p_inbound_handler, + self.socket_manager, + )); + } +} + +pub async fn ping_all( + network: SharedNetworkAddr, + packet_loss_stats: PacketLossStats, + route_table: RouteTable, + socket_manager: P2pOutbound, +) { + loop { + tokio::time::sleep(Duration::from_secs(5)).await; + let Some(src) = network.ip() else { + continue; + }; + let vec = route_table.route_table(); + + for (id, list) in vec { + for (index, route) in list.iter().enumerate() { + if index > 2 { + break; + } + let Ok(mut ping) = NetPacket::new(TransmissionBytes::zeroed_size( + HEAD_LENGTH + 8, + socket_manager.encrypt_reserve(), + )) else { + continue; + }; + ping.set_msg_type(MsgType::Ping); + ping.set_ttl(1); + ping.set_src_id(src.into()); + ping.set_dest_id(id.into()); + ping.set_payload(&crate::utils::time::now_ts_ms().to_be_bytes()) + .unwrap(); + if socket_manager + .send_to(ping, &route.route_key()) + .await + .is_ok() + { + packet_loss_stats.record_sent(id); + } + } + tokio::time::sleep(Duration::from_millis(10)).await; + } + } +} +pub async fn route_timeout_task(route_table: RouteTable) { + loop { + tokio::time::sleep(Duration::from_secs(10)).await; + let expired_time = std::time::Instant::now() - Duration::from_secs(10); + route_table.remove_oldest_route(expired_time); + } +} + +/// 客户端中继探测任务 +/// 每5分钟执行一次,找到所有未直连的目标IP,通过Ping消息发送给已打洞的客户端 +pub async fn relay_probe_task( + network: SharedNetworkAddr, + server_info: crate::context::ServerInfoCollection, + route_table: RouteTable, + socket_manager: P2pOutbound, +) { + use rand::prelude::*; + + loop { + tokio::time::sleep(Duration::from_secs(300)).await; // 5分钟 + + let Some(src) = network.ip() else { + continue; + }; + + let online_ips = server_info.client_online_ips(); + if online_ips.is_empty() { + continue; + } + + let mut non_direct_targets = Vec::new(); + for ip in online_ips { + if ip == src { + continue; + } + + let is_direct = route_table + .get_route_by_id(&ip) + .ok() + .map(|route| route.is_direct()) + .unwrap_or(false); + + if !is_direct { + non_direct_targets.push(ip); + if non_direct_targets.len() >= 20 { + break; + } + } + } + + if non_direct_targets.is_empty() { + continue; + } + + let non_direct_count = non_direct_targets.len(); + + let targets_to_probe: Vec = { + let mut rng = rand::rng(); + if non_direct_targets.len() <= 10 { + non_direct_targets + } else { + non_direct_targets + .choose_multiple(&mut rng, 10) + .copied() + .collect() + } + }; + + let all_routes = route_table.route_table(); + let mut direct_peers = Vec::new(); + for (ip, routes) in &all_routes { + if let Some(best_route) = routes.first() { + if best_route.is_direct() { + direct_peers.push((*ip, best_route.route_key())); + } + } + } + + if direct_peers.is_empty() { + log::debug!("No direct peers available for relay probe"); + continue; + } + + let max_probes_per_target = 3.min(direct_peers.len()); + + for target_ip in &targets_to_probe { + let selected_peers: Vec<_> = { + let mut rng = rand::rng(); + direct_peers + .iter() + .filter(|(ip, _)| ip != target_ip) + .choose_multiple(&mut rng, max_probes_per_target) + .into_iter() + .cloned() + .collect() + }; + + for (relay_ip, route_key) in selected_peers { + // 构造Ping消息,目标是target_ip,但发送给relay_ip + let Ok(mut ping) = NetPacket::new(TransmissionBytes::zeroed_size( + HEAD_LENGTH + 8, + socket_manager.encrypt_reserve(), + )) else { + continue; + }; + + ping.set_msg_type(MsgType::Ping); + ping.set_ttl(2); // TTL设为2,允许中继一次 + ping.set_src_id(src.into()); + ping.set_dest_id((*target_ip).into()); + ping.set_payload(&crate::utils::time::now_ts_ms().to_be_bytes()) + .unwrap(); + + // 发送给已打洞的客户端,让它中继到目标 + if let Err(e) = socket_manager.send_to(ping, &route_key).await { + log::debug!( + "Failed to send relay probe to {} for target {}: {:?}", + relay_ip, + target_ip, + e + ); + } + } + + // 控制发送速率,避免网络拥塞 + tokio::time::sleep(Duration::from_millis(50)).await; + } + + log::info!( + "Relay probe task completed: {} targets probed (from {} non-direct), {} direct peers", + targets_to_probe.len(), + non_direct_count, + direct_peers.len() + ); + } +} + +/// 隧道收发调度与数据分发 +pub async fn tunnel_dispatch_task( + nat_info: MyNatInfo, + task_group: TaskGroup, + mut tunnel_factory: TunnelDispatcher, + p2p_inbound_handler: P2pInboundHandler, + p2p_socket_manager: P2pOutbound, +) { + loop { + let mut tunnel = match tunnel_factory.dispatch().await { + Ok(rs) => rs, + Err(e) => { + log::error!("tunnel disptach :{e:?}"); + return; + } + }; + log::info!("tunnel {:?}-{:?}", tunnel.protocol(), tunnel.remote_addr()); + let p2p_inbound_handler = p2p_inbound_handler.clone(); + let nat_info = nat_info.clone(); + let p2p_socket_manager = p2p_socket_manager.clone(); + task_group.spawn(async move { + let mut buf = vec![0; 65536]; + while let Some(rs) = tunnel.recv_from(&mut buf).await { + let (len, route_key) = match rs { + Ok(rs) => rs, + Err(e) => { + log::warn!("recv_from {e:?}"); + if tunnel.protocol().is_udp() { + continue; + } + break; + } + }; + if tunnel.protocol().is_udp() + && rust_p2p_core::stun::is_stun_response(&buf[..len]) + && let Some(pub_addr) = rust_p2p_core::stun::recv_stun_response(&buf[..len]) + { + nat_info.update_public_addr(route_key.index(), pub_addr); + continue; + } + let mut bytes = TransmissionBytes::zeroed(len); + bytes.copy_from_slice(&buf[..len]); + p2p_inbound_handler + .next_handle(bytes, route_key, &p2p_socket_manager, &mut tunnel) + .await; + } + log::info!( + "drop tunnel {:?}-{:?}", + tunnel.protocol(), + tunnel.remote_addr() + ); + if let Tunnel::Tcp(tcp) = tunnel { + p2p_inbound_handler.tcp_disconnect(tcp.route_key()).await; + } + }); + } +} diff --git a/vnt-core/src/tunnel_core/server/connection_manager.rs b/vnt-core/src/tunnel_core/server/connection_manager.rs new file mode 100644 index 0000000..22e6298 --- /dev/null +++ b/vnt-core/src/tunnel_core/server/connection_manager.rs @@ -0,0 +1,346 @@ +use crate::compression::PacketCompression; +use crate::context::config::Config; +use crate::context::nat::{MyNatInfo, PunchBackoff}; +use crate::context::{AppState, NetworkAddr, NetworkRoute, PeerInfoMap, ServerInfoCollection}; +use crate::crypto::PacketCrypto; +use crate::enhanced_tunnel::inbound::EnhancedInbound; +use crate::fec::FecDecoder; +use crate::protocol::control_message::{ + ConfirmRegResponseMsg, RegResponseMsg, RegistrationMode, RequestMessage, ResponseMessage, +}; +use crate::tunnel_core::p2p::transport::punch::NatPuncher; +use crate::tunnel_core::server::inbound::ServerTurnInboundHandler; +use crate::tunnel_core::server::outbound::ServerOutbound; +use crate::tunnel_core::server::rpc::{RpcNotifier, ServerRPC}; +use crate::tunnel_core::server::transport::TransportClient; +use crate::tunnel_core::server::transport::config::ConnectRegConfig; +use crate::utils::task_control::TaskGroup; +use anyhow::bail; +use bytes::Bytes; +use std::collections::HashMap; +use std::net::Ipv4Addr; +use std::sync::Arc; +use std::time::{Duration, Instant}; +use tokio::sync::mpsc::{Receiver, Sender}; + +pub struct InboundHandlerConfig { + pub network_route: NetworkRoute, + pub server_info: ServerInfoCollection, + pub nat_info: MyNatInfo, + pub peer_map: PeerInfoMap, + pub punch_backoff: PunchBackoff, + pub puncher: NatPuncher, + pub packet_crypto: PacketCrypto, + pub packet_compression: PacketCompression, + pub enhanced_inbound: EnhancedInbound, + pub fec_decoder: FecDecoder, +} + +pub struct ServerTurnManager { + server_id: u32, + config: ConnectRegConfig, + receiver: Option>, + notifier: RpcNotifier, + transport_client: TransportClient, +} +pub(crate) fn create_server_tunnel( + app_state: AppState, + config: &Config, + packet_crypto: PacketCrypto, +) -> (Vec, ServerOutbound, ServerRPC) { + let mut rpc_notifier: HashMap = HashMap::new(); + let mut sender_map: HashMap> = HashMap::new(); + let mut server_manager_list = Vec::with_capacity(config.server_addr.len()); + let mut server_addr_list = Vec::with_capacity(config.server_addr.len()); + for (index, server_addr) in config.server_addr.iter().enumerate() { + let connect_reg_config = config.to_connect_config(index); + + let server_id = index as u32; + + let (s, r) = tokio::sync::mpsc::channel(1024); + + let notifier = RpcNotifier::new(); + let manager = + ServerTurnManager::new(server_id, connect_reg_config.clone(), r, notifier.clone()); + server_addr_list.push((server_id, server_addr.clone())); + rpc_notifier.insert(server_id, notifier); + sender_map.insert(server_id, s); + server_manager_list.push(manager); + } + let server_info_collection = app_state.server_info_collection.clone(); + server_info_collection.update_server(server_addr_list); + let tunnel_to_server = + ServerOutbound::new(Arc::new(sender_map), server_info_collection, packet_crypto); + + let server_rpc = ServerRPC::new(tunnel_to_server.clone(), rpc_notifier); + + (server_manager_list, tunnel_to_server, server_rpc) +} + +impl ServerTurnManager { + pub fn new( + server_id: u32, + config: ConnectRegConfig, + receiver: Receiver<(Bytes, Instant)>, + notifier: RpcNotifier, + ) -> Self { + let connector = TransportClient::new(); + Self { + server_id, + transport_client: connector, + config, + receiver: Some(receiver), + notifier, + } + } + pub fn disconnect(&mut self) { + self.transport_client.disconnect(); + } + + pub async fn connect_and_reg( + &mut self, + mode: RegistrationMode, + ) -> anyhow::Result { + let connect_config = self.config.to_connect_config().await?; + log::info!( + "Connecting to server[{}] {:?} with mode {:?}", + self.server_id, + connect_config, + mode, + ); + + self.transport_client + .connect_timeout(&connect_config, Duration::from_secs(10)) + .await?; + + let reg_msg = self.config.reg_msg_request(self.server_id, mode); + let request_msg = RequestMessage::Reg(reg_msg); + let encoded = request_msg.encode(); + + self.transport_client + .send(encoded.freeze()) + .await?; + let buf = self + .transport_client + .next_timeout(Duration::from_secs(10)) + .await?; + let response = ResponseMessage::from_slice(&buf)?; + match &response { + ResponseMessage::Reg(_) => {} + ResponseMessage::Error(_e) => { + self.disconnect(); + } + ResponseMessage::ConfirmReg(_) => { + self.disconnect(); + } + } + Ok(response) + } + + pub async fn send_confirm(&mut self) -> anyhow::Result { + self.transport_client + .send(RequestMessage::ConfirmReg.encode().freeze()) + .await?; + let buf = self + .transport_client + .next_timeout(Duration::from_secs(10)) + .await?; + let response = ResponseMessage::from_slice(&buf)?; + match response { + ResponseMessage::ConfirmReg(msg) => Ok(msg), + ResponseMessage::Error(e) => bail!("Confirm failed: {}", e.message), + _ => bail!("Unexpected response"), + } + } + + pub fn set_ip(&mut self, ip: Ipv4Addr) { + self.config.ip = Some(ip); + } + + /// Start data handling task with an already established connection. + pub fn data_handle_task_connected( + mut self, + task_group: &TaskGroup, + config: Box, + initial_response: NetworkAddr, + ) { + let data_handler = + ServerTurnInboundHandler::new(self.server_id, initial_response, config); + let task_group_ = task_group.clone(); + let Some(mut receiver) = self.receiver.take() else { + unreachable!() + }; + + task_group.spawn(async move { + let mut already_connected = true; + loop { + if !already_connected { + self.disconnect(); + data_handler.handle_disconnected(); + let msg = match self.connect_and_reg(RegistrationMode::Normal).await { + Ok(msg) => msg, + Err(e) => { + log::error!("连接服务器失败:{e:?}"); + tokio::time::sleep(std::time::Duration::from_secs(5)).await; + continue; + } + }; + match &msg { + ResponseMessage::Reg(reg) => { + if reg.ip != initial_response.ip + || reg.prefix_len != initial_response.prefix_len + || reg.gateway != initial_response.gateway + { + log::error!("虚拟网络发生变化"); + break; + } + // 保存服务器版本 + if !reg.server_version.is_empty() { + data_handler.set_server_version(reg.server_version.clone()); + } + } + ResponseMessage::Error(e) => { + log::error!("注册失败 {e:?}"); + break; + } + _ => { + log::error!("错误的注册消息"); + break; + } + } + } + log::info!("已连接服务器:{}", self.config.server_addr); + data_handler.handle_connected(); + + if let Err(e) = self + .data_handle_loop(&mut receiver, &data_handler) + .await + { + log::error!("Error on data_handle_loop: {:?}", e); + } + already_connected = false; + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + } + self.disconnect(); + data_handler.handle_disconnected(); + task_group_.stop(); + }); + } + + pub async fn data_handle_loop( + &mut self, + receiver: &mut Receiver<(Bytes, Instant)>, + data_handler: &ServerTurnInboundHandler, + ) -> anyhow::Result<()> { + let mut time = crate::utils::time::now_ts_ms(); + let mut ping_interval = tokio::time::interval(Duration::from_secs(5)); + loop { + tokio::select! { + Some((buf,expired)) = receiver.recv() => { + if expired < Instant::now(){ + continue; + } + self.transport_client.send(buf).await?; + } + rs = self.transport_client.next() => { + time = crate::utils::time::now_ts_ms(); + let data = rs?; + if let Err(e) = data_handler.handle(&mut self.transport_client,data, &self.notifier,time).await{ + log::warn!("Error handling data: {:?}", e); + } + } + _ = ping_interval.tick() => { + let now = crate::utils::time::now_ts_ms(); + if now > time + Duration::from_secs(20).as_millis() as i64 { + bail!("timeout") + } + data_handler.handle_ping(&mut self.transport_client,now).await?; + } + else => { + bail!("receiver closed"); + } + } + } + } +} + +/// Coordinated multi-server pre-registration. +/// 1. First server uses PRE_REGISTER mode to get IP +/// 2. Other servers pre-register with the obtained IP +/// 3. Send confirmation to all servers +/// 4. Return the registration response +pub async fn coordinated_registration( + managers: &mut Vec, +) -> anyhow::Result { + if managers.is_empty() { + bail!("No servers to register"); + } + + // Step 1: First server pre-register to get IP + log::info!( + "Starting coordinated registration with {} servers", + managers.len() + ); + let first_response = managers[0] + .connect_and_reg(RegistrationMode::PreRegister) + .await?; + + let ip = match &first_response { + ResponseMessage::Reg(reg) => reg.ip, + ResponseMessage::Error(e) => bail!("First server registration failed: {}", e.message), + _ => bail!("Unexpected response from first server"), + }; + log::info!("Got IP {} from first server", ip); + + // Step 2: Set IP and pre-register with other servers + for manager in managers.iter_mut().skip(1) { + manager.set_ip(ip); + } + + if managers.len() > 1 { + let other_results: Vec<_> = futures::future::join_all( + managers + .iter_mut() + .skip(1) + .map(|m| m.connect_and_reg(RegistrationMode::PreRegister)), + ) + .await; + + // Check all responses + for (i, result) in other_results.iter().enumerate() { + match result { + Ok(ResponseMessage::Reg(_)) => { + log::info!("Server {} pre-registered successfully", i + 1); + } + Ok(ResponseMessage::Error(e)) => { + bail!("Server {} registration failed: {}", i + 1, e.message) + } + Err(e) => bail!("Server {} registration failed: {}", i + 1, e), + _ => bail!("Unexpected response from server {}", i + 1), + } + } + } + + // Step 3: Send confirmation to all servers + log::info!("Sending confirmation to all servers"); + let confirm_results: Vec<_> = + futures::future::join_all(managers.iter_mut().map(|m| m.send_confirm())).await; + + // Check all confirmation responses + for (i, result) in confirm_results.into_iter().enumerate() { + match result { + Ok(msg) if msg.success => { + log::info!("Server {} confirmed successfully", i); + } + Ok(_) => bail!("Server {} confirmation failed", i), + Err(e) => bail!("Server {} confirmation failed: {}", i, e), + } + } + + log::info!("Coordinated registration completed successfully"); + // Return first server's response (contains IP info) + match first_response { + ResponseMessage::Reg(reg) => Ok(reg), + _ => unreachable!(), + } +} diff --git a/vnt-core/src/tunnel_core/server/inbound.rs b/vnt-core/src/tunnel_core/server/inbound.rs new file mode 100644 index 0000000..abe9e78 --- /dev/null +++ b/vnt-core/src/tunnel_core/server/inbound.rs @@ -0,0 +1,332 @@ +use crate::compression::PacketCompression; +use crate::context::nat::{MyNatInfo, PunchBackoff}; +use crate::context::{NetworkAddr, NetworkRoute, PeerInfoMap, ServerInfoCollection}; +use crate::crypto::PacketCrypto; +use crate::enhanced_tunnel::inbound::EnhancedInbound; +use crate::fec::FecDecoder; +use crate::protocol::client_message::PunchInfo; +use crate::protocol::control_message::ClientSimpleInfoList; +use crate::protocol::ip_packet_protocol::{HEAD_LENGTH, MsgType, NetPacket}; +use crate::protocol::rpc_message::RpcMessageResponse; +use crate::protocol::transmission::TransmissionBytes; +use crate::tunnel_core::p2p::transport::punch::NatPuncher; +use crate::tunnel_core::server::rpc::RpcNotifier; +use crate::tunnel_core::server::transport::TransportClient; +use anyhow::bail; +use pnet_packet::Packet; +use pnet_packet::icmp::{IcmpPacket, IcmpTypes}; +use pnet_packet::ipv4::Ipv4Packet; +use prost::Message; +use rust_p2p_core::nat::NatInfo; +use std::net::Ipv4Addr; + +pub(crate) struct ServerTurnInboundHandler { + server_id: u32, + network_addr: Option, + network_route: NetworkRoute, + server_info: ServerInfoCollection, + nat_info: MyNatInfo, + peer_map: PeerInfoMap, + punch_backoff: PunchBackoff, + puncher: NatPuncher, + packet_crypto: PacketCrypto, + packet_compression: PacketCompression, + enhanced_inbound: EnhancedInbound, + fec_decoder: FecDecoder, +} +impl ServerTurnInboundHandler { + pub fn new( + server_id: u32, + network_addr: NetworkAddr, + config: Box, + ) -> Self { + let config = *config; + Self { + server_id, + network_addr: Some(network_addr), + network_route: config.network_route, + server_info: config.server_info, + nat_info: config.nat_info, + peer_map: config.peer_map, + punch_backoff: config.punch_backoff, + puncher: config.puncher, + packet_crypto: config.packet_crypto, + packet_compression: config.packet_compression, + enhanced_inbound: config.enhanced_inbound, + fec_decoder: config.fec_decoder, + } + } + fn network_contains(&self, ip: &Ipv4Addr) -> bool { + self.network_route.network_contains(ip) + } + fn filter_ip(&self, mut info: NatInfo) -> NatInfo { + if self.network_contains(&info.local_ipv4) { + info.local_ipv4 = Ipv4Addr::UNSPECIFIED; + } + info.local_ipv4s.retain(|ip| !self.network_contains(ip)); + info + } + fn get_punch_info(&self) -> Option { + self.nat_info.get().map(|info| PunchInfo { + nat_info: self.filter_ip(info), + }) + } + fn update_peer_nat_info(&self, ip: Ipv4Addr, nat_info: NatInfo) { + self.peer_map.update_nat_info(ip, nat_info); + } + + pub async fn handle_server_data( + & self, + transport_client: &mut TransportClient, + network_addr: NetworkAddr, + data: TransmissionBytes, + rpc_notifier: &RpcNotifier, + now: i64, + ) -> anyhow::Result<()> { + let net_packet = NetPacket::new(data)?; + let src = net_packet.src_id().into(); + let msg_type = net_packet.msg_type()?; + let mut net_packet = self.packet_compression.decompress(net_packet)?; + + match msg_type { + MsgType::Turn => { + // 只允许icmp EchoReply + let Some(ipv4) = Ipv4Packet::new(net_packet.payload()) else { + return Ok(()); + }; + if ipv4.get_version() != 4 { + return Ok(()); + } + if ipv4.get_next_level_protocol() != pnet_packet::ip::IpNextHeaderProtocols::Icmp { + return Ok(()); + } + let Some(icmp) = IcmpPacket::new(ipv4.payload()) else { + return Ok(()); + }; + if icmp.get_icmp_type() != IcmpTypes::EchoReply { + return Ok(()); + } + self.enhanced_inbound + .inbound(&network_addr, msg_type, src, net_packet) + .await?; + } + MsgType::Ping => { + net_packet.set_ttl(2); + net_packet.set_msg_type(MsgType::Pong); + net_packet.set_src_id(network_addr.ip.into()); + net_packet.set_dest_id(src.into()); + transport_client.send_turn(net_packet).await?; + } + MsgType::PongTurn => { + // 服务端ping 回复,记录延迟 + if net_packet.payload().len() == 8 + 8 { + let time = i64::from_be_bytes(net_packet.payload()[..8].try_into()?); + // let data_version = u64::from_be_bytes(net_packet.payload()[8..].try_into()?); + if now >= time { + self.server_info + .set_server_rtt(self.server_id, (now - time) as u32); + } + } + } + MsgType::PushClientIps => { + let list = ClientSimpleInfoList::from_slice(net_packet.payload())?; + self.server_info.update_client_simple_list( + self.server_id, + network_addr.ip, + list, + now, + ); + } + MsgType::RpcRes => { + // 设置rpc响应 + let response = RpcMessageResponse::decode(net_packet.payload())?; + rpc_notifier.notify_response(response); + } + _ => {} + } + Ok(()) + } + pub async fn handle_client_data( + &self, + network_addr: NetworkAddr, + transport_client: &mut TransportClient, + data: TransmissionBytes, + ) -> anyhow::Result<()> { + let mut net_packet = NetPacket::new(data)?; + let msg_type = net_packet.msg_type()?; + let src = Ipv4Addr::from(net_packet.src_id()); + let dest = Ipv4Addr::from(net_packet.dest_id()); + + if msg_type == MsgType::Quic { + // QUIC 数据不加密不压缩,但可能有 FEC + if net_packet.is_fec() { + let packets = self.fec_decoder.receive(net_packet)?; + if let Some(packets) = packets { + for pkt in packets { + self.enhanced_inbound + .inbound(&network_addr, msg_type, src, pkt) + .await?; + } + } + return Ok(()); + } + self.enhanced_inbound + .inbound(&network_addr, msg_type, src, net_packet) + .await?; + return Ok(()); + } + + // 解密 + if let Err(e) = self.packet_crypto.decrypt_in_place(&mut net_packet) { + log::error!("{},mst_type={msg_type:?},src={src},dst={dest}", e); + return Ok(()); + } + + // FEC 解码(始终尝试解码,如果有 FEC 标志) + if net_packet.is_fec() { + let packets = self.fec_decoder.receive(net_packet)?; + if let Some(packets) = packets { + for pkt in packets { + let pkt = self.packet_compression.decompress(pkt)?; + self.process_decompressed_packet( + network_addr, + transport_client, + pkt, + msg_type, + src, + dest, + ) + .await?; + } + } + return Ok(()); + } + + // 解压缩 + let net_packet = self.packet_compression.decompress(net_packet)?; + self.process_decompressed_packet( + network_addr, + transport_client, + net_packet, + msg_type, + src, + dest, + ) + .await + } + + async fn process_decompressed_packet( + &self, + network_addr: NetworkAddr, + transport_client: &mut TransportClient, + net_packet: NetPacket, + msg_type: MsgType, + src: Ipv4Addr, + dest: Ipv4Addr, + ) -> anyhow::Result<()> { + match msg_type { + MsgType::Turn | MsgType::Broadcast => { + self.enhanced_inbound + .inbound(&network_addr, msg_type, src, net_packet) + .await?; + } + MsgType::PunchStart1 => { + // 对方发起打洞 + let peer_punch_info = PunchInfo::from_slice(net_packet.payload())?; + let Some(self_punch_info) = self.get_punch_info() else { + return Ok(()); + }; + log::info!( + "对方主动发起打洞 对方nat信息={peer_punch_info:?},自己nat信息={self_punch_info:?} {src}->{dest}" + ); + self.update_peer_nat_info(src, peer_punch_info.nat_info.clone()); + let rs = self.puncher.punch(src, peer_punch_info)?; + if rs { + let bytes_mut = self_punch_info.encode(); + let mut net_packet = NetPacket::new(TransmissionBytes::zeroed_size( + HEAD_LENGTH + bytes_mut.len(), + self.packet_crypto.encrypt_reserve(), + ))?; + net_packet.set_msg_type(MsgType::PunchStart2); + net_packet.set_ttl(2); + net_packet.set_src_id(dest.into()); + net_packet.set_dest_id(src.into()); + net_packet.set_payload(&bytes_mut)?; + self.packet_crypto.encrypt_in_place(&mut net_packet)?; + transport_client.send_turn(net_packet).await?; + }else{ + log::info!("限制打洞频率") + } + } + MsgType::PunchStart2 => { + self.punch_backoff.record(src); + // 对方回复开始打洞 + let peer_punch_info = PunchInfo::from_slice(net_packet.payload())?; + self.update_peer_nat_info(src, peer_punch_info.nat_info.clone()); + log::info!("对方回复开始打洞 {:?} {src}->{dest}", peer_punch_info); + self.puncher.punch_uncheck(src, peer_punch_info)?; + } + _ => {} + } + Ok(()) + } + + pub async fn handle( + &self, + transport_client: &mut TransportClient, + data: TransmissionBytes, + rpc_notifier: &RpcNotifier, + now: i64, + ) -> anyhow::Result<()> { + let net_packet = NetPacket::new(&data)?; + let Some(network_addr) = self.network_addr else { + bail!("未找到自身IP") + }; + + if net_packet.is_gateway() { + // 服务端数据 + return self + .handle_server_data(transport_client, network_addr, data, rpc_notifier, now) + .await; + } + let dest = Ipv4Addr::from(net_packet.dest_id()); + if !dest.is_broadcast() && !dest.is_unspecified() && network_addr.ip != dest { + return Ok(()); + } + self.handle_client_data(network_addr, transport_client, data) + .await + } + pub async fn handle_ping( + &self, + transport_client: &mut TransportClient, + now: i64, + ) -> anyhow::Result<()> { + let mut ping_packet = NetPacket::new(TransmissionBytes::zeroed(HEAD_LENGTH + 8 + 8))?; + ping_packet.set_ttl(1); + ping_packet.set_msg_type(MsgType::PingTurn); + ping_packet.set_gateway_flag(true); + ping_packet.set_payload(&now.to_be_bytes())?; + ping_packet.payload_mut()[0..8].copy_from_slice(&now.to_be_bytes()); + ping_packet.payload_mut()[8..] + .copy_from_slice(&self.server_info.data_version(self.server_id).to_be_bytes()); + transport_client + .send(ping_packet.into_buffer().into_bytes().freeze()) + .await?; + Ok(()) + } + pub fn handle_connected(&self) { + self.server_info.set_server_connected(self.server_id, true); + self.server_info + .set_last_connected_time(self.server_id, Some(crate::utils::time::now_ts_ms())); + self.server_info.set_disconnected_time(self.server_id, None); + } + pub fn set_server_version(&self, version: String) { + self.server_info.set_server_version(self.server_id, version); + } + pub fn handle_disconnected(&self) { + if self.server_info.set_server_connected(self.server_id, false) { + self.server_info + .set_disconnected_time(self.server_id, Some(crate::utils::time::now_ts_ms())); + } + } +} diff --git a/vnt-core/src/tunnel_core/server/mod.rs b/vnt-core/src/tunnel_core/server/mod.rs new file mode 100644 index 0000000..60b2855 --- /dev/null +++ b/vnt-core/src/tunnel_core/server/mod.rs @@ -0,0 +1,5 @@ +pub(crate) mod connection_manager; +pub(crate) mod inbound; +pub(crate) mod outbound; +pub(crate) mod rpc; +pub mod transport; diff --git a/vnt-core/src/tunnel_core/server/outbound.rs b/vnt-core/src/tunnel_core/server/outbound.rs new file mode 100644 index 0000000..51b4669 --- /dev/null +++ b/vnt-core/src/tunnel_core/server/outbound.rs @@ -0,0 +1,329 @@ +use crate::context::ServerInfoCollection; +use crate::crypto::PacketCrypto; +use crate::protocol::ProtoToBytesMut; +use crate::protocol::control_message::SelectiveBroadcast; +use crate::protocol::ip_packet_protocol::{HEAD_LENGTH, MsgType, NetPacket}; +use crate::protocol::transmission::TransmissionBytes; +use anyhow::{Context, bail}; +use bytes::Bytes; +use std::collections::HashMap; +use std::net::Ipv4Addr; +use std::sync::Arc; +use std::time::{Duration, Instant}; +use tokio::sync::mpsc::Sender; + +#[derive(Clone)] +pub(crate) struct ServerOutbound { + server_id_list: Arc>, + sender: Arc>>, + server_info_collection: ServerInfoCollection, + packet_crypto: PacketCrypto, +} +impl ServerOutbound { + pub fn new( + sender: Arc>>, + server_info_collection: ServerInfoCollection, + packet_crypto: PacketCrypto, + ) -> Self { + let server_id_list = Arc::new(sender.keys().copied().collect()); + Self { + server_id_list, + sender, + server_info_collection, + packet_crypto, + } + } + pub fn exists_route(&self, dest: &Ipv4Addr) -> bool { + self.server_info_collection.exists_online_client_ip(dest) + } + pub fn server_id_list(&self) -> &Vec { + &self.server_id_list + } + pub fn encrypt_reserve(&self) -> usize { + self.packet_crypto.encrypt_reserve() + } + + pub async fn send_to_gateway_expired( + &self, + server_id: u32, + mut buf: NetPacket, + expired: Duration, + ) -> anyhow::Result<()> { + if !self.server_info_collection.is_server_connected(server_id) { + bail!("未连接服务器") + } + buf.set_gateway_flag(true); + self.send_expired_impl(server_id, buf, expired).await + } + + pub async fn send( + &self, + dest_ip: Ipv4Addr, + buf: NetPacket, + ) -> anyhow::Result<()> { + self.send_expired(dest_ip, buf, Duration::from_secs(5)) + .await + } + pub async fn send_expired( + &self, + dest_ip: Ipv4Addr, + buf: NetPacket, + expired: Duration, + ) -> anyhow::Result<()> { + let Some(server_id) = self + .server_info_collection + .find_ip_to_server(&self.server_id_list, &dest_ip) + else { + bail!("not found ip route: {dest_ip}") + }; + self.send_expired_impl(server_id, buf, expired).await + } + async fn send_expired_impl( + &self, + server_id: u32, + mut buf: NetPacket, + expired: Duration, + ) -> anyhow::Result<()> { + if !buf.is_gateway() { + self.packet_crypto.encrypt_in_place(&mut buf)?; + } + let Some(sender) = self.sender.get(&server_id) else { + bail!("not found server") + }; + sender + .send_timeout( + ( + buf.into_buffer().into_bytes().freeze(), + Instant::now() + expired, + ), + expired, + ) + .await + .context("connect server task failed") + } + + pub async fn send_raw(&self, dest_ip: Ipv4Addr, buf: NetPacket) -> anyhow::Result<()> { + let Some(server_id) = self + .server_info_collection + .find_ip_to_server(&self.server_id_list, &dest_ip) + else { + bail!("not found ip route: {dest_ip}") + }; + let expired = Duration::from_secs(5); + let Some(sender) = self.sender.get(&server_id) else { + bail!("not found server") + }; + sender + .send_timeout((buf.into_buffer(), Instant::now() + expired), expired) + .await + .context("connect server task failed") + } + pub async fn send_default_raw(&self, buf: NetPacket) -> anyhow::Result<()> { + let Some(server_id) = self + .server_info_collection + .find_connected_server(&self.server_id_list) + else { + bail!("not found default route") + }; + let expired = Duration::from_secs(5); + let Some(sender) = self.sender.get(&server_id) else { + bail!("not found server") + }; + sender + .send_timeout((buf.into_buffer(), Instant::now() + expired), expired) + .await + .context("connect server task failed") + } + + pub async fn send_raw_broadcast( + &self, + exclude_ips: Option>, + buf: NetPacket, + ) -> anyhow::Result<()> { + let buf = buf.into_buffer(); + let expired = Duration::from_secs(5); + + let map: HashMap, u32)> = + self.server_info_collection.server_client_ip_map(); + if map.is_empty() { + bail!("no connected servers with clients"); + } + + // 只有一个服务器,直接发送 + if map.len() == 1 { + let (server_id, (ips, _)) = map.iter().next().expect("map has exactly one element"); + if ips.is_empty() { + return Ok(()); + } + let sender = self + .sender + .get(server_id) + .context("server sender not found")?; + + return if let Some(exclude_ips) = exclude_ips { + send_exclude_broadcast(sender.clone(), buf, exclude_ips, expired).await + } else { + send_direct(sender.clone(), buf, expired).await + }; + } + + // 找到最优服务器 + let (max_server_id, (max_ips, _)) = map + .iter() + .max_by(|(_, (ips_a, rtt_a)), (_, (ips_b, rtt_b))| { + let score_a = ips_a.len() as f64 / (*rtt_a as f64 + 1.0); + let score_b = ips_b.len() as f64 / (*rtt_b as f64 + 1.0); + score_a + .partial_cmp(&score_b) + .unwrap_or(std::cmp::Ordering::Equal) + }) + .context("failed to find server with most IPs")?; + if max_ips.is_empty() { + return Ok(()); + } + let max_ip_set: std::collections::HashSet<_> = max_ips.iter().collect(); + let exclude_set: std::collections::HashSet<_> = exclude_ips + .as_ref() + .map(|ips| ips.iter().collect()) + .unwrap_or_default(); + + let mut handles = Vec::new(); + + // 任务1: 向主服务器发送 + let sender = self + .sender + .get(max_server_id) + .cloned() + .context("max server sender not found")?; + + if let Some(exclude_ips) = exclude_ips.clone() { + let buf_clone = buf.clone(); + let handle = tokio::spawn(async move { + send_exclude_broadcast(sender, buf_clone, exclude_ips, expired).await + }); + handles.push(handle); + } else { + let buf_clone = buf.clone(); + let handle = tokio::spawn(async move { send_direct(sender, buf_clone, expired).await }); + handles.push(handle); + } + + // 任务2-N: 向其他服务器发送目标广播 + for (server_id, (ips, _rtt)) in map.iter() { + if *server_id == *max_server_id { + continue; + } + + // 筛选目标IP:不在最大服务器中,也不在排除列表中 + let target_ips: Vec = ips + .iter() + .filter(|ip| !max_ip_set.contains(ip) && !exclude_set.contains(ip)) + .copied() + .collect(); + + if target_ips.is_empty() { + continue; + } + + let sender = match self.sender.get(server_id).cloned() { + Some(s) => s, + None => continue, + }; + let buf_clone = buf.clone(); + + let handle = tokio::spawn(async move { + send_target_broadcast(sender, target_ips, buf_clone, expired).await + }); + handles.push(handle); + } + + // 等待所有任务完成 + let mut errors = Vec::new(); + for handle in handles { + match handle.await { + Ok(Ok(())) => {} + Ok(Err(e)) => errors.push(e), + Err(e) => errors.push(anyhow::anyhow!("task join error: {}", e)), + } + } + + if !errors.is_empty() { + bail!( + "broadcast failed with {} errors: {:?}", + errors.len(), + errors + ); + } + + Ok(()) + } +} +async fn send_exclude_broadcast( + sender: Sender<(Bytes, Instant)>, + buf: Bytes, + exclude_ips: Vec, + expired: Duration, +) -> anyhow::Result<()> { + let broadcast = SelectiveBroadcast::new(&exclude_ips, buf.to_vec()); + let bytes = broadcast.encode_bytes_mut(); + + let mut packet = NetPacket::new(TransmissionBytes::zeroed(HEAD_LENGTH + bytes.len()))?; + packet.set_msg_type(MsgType::ExcludeBroadcast); + packet.set_ttl(5); + packet.payload_mut().copy_from_slice(&bytes); + + sender + .send_timeout( + ( + packet.into_buffer().into_bytes().freeze(), + Instant::now() + expired, + ), + expired, + ) + .await + .context("failed to send exclude broadcast")?; + + Ok(()) +} + +// 直接发送原始数据 +async fn send_direct( + sender: Sender<(Bytes, Instant)>, + buf: Bytes, + expired: Duration, +) -> anyhow::Result<()> { + sender + .send_timeout((buf, Instant::now() + expired), expired) + .await + .context("failed to send direct broadcast")?; + + Ok(()) +} + +async fn send_target_broadcast( + sender: Sender<(Bytes, Instant)>, + target_ips: Vec, + buf: Bytes, + expired: Duration, +) -> anyhow::Result<()> { + let target_broadcast = SelectiveBroadcast::new(&target_ips, buf.to_vec()); + let target_bytes = target_broadcast.encode_bytes_mut(); + + let mut packet = NetPacket::new(TransmissionBytes::zeroed(HEAD_LENGTH + target_bytes.len()))?; + packet.set_msg_type(MsgType::TargetBroadcast); + packet.set_ttl(5); + packet.payload_mut().copy_from_slice(&target_bytes); + + sender + .send_timeout( + ( + packet.into_buffer().into_bytes().freeze(), + Instant::now() + expired, + ), + expired, + ) + .await + .context("failed to send target broadcast")?; + + Ok(()) +} diff --git a/vnt-core/src/tunnel_core/server/rpc.rs b/vnt-core/src/tunnel_core/server/rpc.rs new file mode 100644 index 0000000..0765222 --- /dev/null +++ b/vnt-core/src/tunnel_core/server/rpc.rs @@ -0,0 +1,150 @@ +use crate::protocol::ip_packet_protocol::{HEAD_LENGTH, MsgType, NetPacket}; +use crate::protocol::rpc_message::rpc_message_request::RpcReqPayload; +use crate::protocol::rpc_message::rpc_message_response::RpcResPayload; +use crate::protocol::rpc_message::{ + ClientInfo, ClientListRequest, ClientListResponse, RpcMessageRequest, RpcMessageResponse, +}; +use crate::protocol::transmission::TransmissionBytes; +use crate::tunnel_core::server::outbound::ServerOutbound; +use anyhow::bail; +use parking_lot::Mutex; +use prost::Message; +use std::collections::HashMap; +use std::sync::Arc; +use std::time::Duration; +use tokio::sync::oneshot; +use tokio::sync::oneshot::Sender; + +#[derive(Clone)] +pub struct ServerRPC { + tunnel_to_server: ServerOutbound, + rpc_notifier: HashMap, +} +#[derive(Clone)] +pub(crate) struct RpcNotifier { + pending_requests: Arc>>>, + rpc_id: Arc>, +} + +impl RpcNotifier { + pub fn new() -> Self { + Self { + pending_requests: Arc::new(Mutex::new(HashMap::new())), + rpc_id: Arc::new(Mutex::new(0)), + } + } + + pub fn create_request_and_waiter(&self) -> RpcResponseWaiter { + let id: u64 = { + let mut id_lock = self.rpc_id.lock(); + *id_lock += 1; + *id_lock + }; + + let (tx, rx) = oneshot::channel(); + + { + let mut pending = self.pending_requests.lock(); + pending.insert(id, tx); + } + + RpcResponseWaiter { + id, + pending_requests_handle: Arc::clone(&self.pending_requests), + rx, + } + } + + pub fn notify_response(&self, response: RpcMessageResponse) { + let mut pending = self.pending_requests.lock(); + + if let Some(tx) = pending.remove(&response.id) { + let _ = tx.send(response); + } + } +} + +pub(crate) struct RpcResponseWaiter { + id: u64, + pending_requests_handle: Arc>>>, + rx: oneshot::Receiver, +} + +impl RpcResponseWaiter { + pub async fn wait_for_response( + mut self, + timeout: Duration, + ) -> anyhow::Result { + let result = tokio::time::timeout(timeout, &mut self.rx).await; + + match result { + Err(_) => bail!("timeout waiting for response"), + Ok(Ok(response)) => Ok(response), + Ok(Err(_)) => bail!("closed connection"), + } + } +} + +impl Drop for RpcResponseWaiter { + fn drop(&mut self) { + let mut pending = self.pending_requests_handle.lock(); + let _ = pending.remove(&self.id); + } +} + +impl ServerRPC { + pub(crate) fn new( + tunnel_to_server: ServerOutbound, + rpc_notifier: HashMap, + ) -> Self { + Self { + tunnel_to_server, + rpc_notifier, + } + } + + pub async fn client_list(&self) -> anyhow::Result { + let mut map: HashMap = HashMap::new(); + for server_id in self.tunnel_to_server.server_id_list() { + match self.client_list_target(*server_id).await { + Ok(rs) => { + for client in rs.list { + map.entry(client.id.clone()).or_insert(client); + } + } + Err(e) => { + log::error!("client list target failed: {}", e); + } + } + } + + Ok(ClientListResponse { + list: map.into_values().collect(), + }) + } + pub async fn client_list_target(&self, server_id: u32) -> anyhow::Result { + let Some(rpc_notifier) = self.rpc_notifier.get(&server_id) else { + bail!("no RPC notifier"); + }; + let waiter = rpc_notifier.create_request_and_waiter(); + let request = RpcMessageRequest { + id: waiter.id, + rpc_req_payload: Some(RpcReqPayload::ClientListReq(ClientListRequest::default())), + }; + let buf = request.encode_to_vec(); + let mut packet = NetPacket::new(TransmissionBytes::zeroed(HEAD_LENGTH + buf.len()))?; + packet.set_msg_type(MsgType::RpcReq); + packet.set_gateway_flag(true); + packet.set_ttl(1); + packet.set_payload(&buf)?; + + self.tunnel_to_server + .send_to_gateway_expired(server_id, packet, Duration::from_secs(1)) + .await?; + let response = waiter.wait_for_response(Duration::from_secs(3)).await?; + if let Some(RpcResPayload::ClientListRes(res)) = response.rpc_res_payload { + return Ok(res); + } + bail!("unexpected response: {:?}", response); + } +} diff --git a/vnt-core/src/tunnel_core/server/transport/config.rs b/vnt-core/src/tunnel_core/server/transport/config.rs new file mode 100644 index 0000000..918db06 --- /dev/null +++ b/vnt-core/src/tunnel_core/server/transport/config.rs @@ -0,0 +1,173 @@ +use crate::protocol::control_message::{RegRequestMsg, RegistrationMode}; +use crate::tls::verifier::CertValidationMode; +use anyhow::Context; +use rand::seq::SliceRandom; +use std::fmt; +use std::net::{Ipv4Addr, SocketAddr}; +use std::str::FromStr; + +#[derive(Debug, Clone)] +pub(crate) struct ConnectRegConfig { + pub server_addr: ProtocolAddress, + pub cert_mode: CertValidationMode, + pub network_code: String, + pub device_id: String, + pub device_name: String, + pub ip: Option, + pub key_sign: Option, + pub ip_variable: bool, +} +#[derive(Debug, Clone)] +pub(crate) struct ConnectConfig { + pub protocol_type: ProtocolType, + pub server_addr: SocketAddr, + pub server_domain: String, + pub cert_mode: CertValidationMode, +} +#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] +pub enum ProtocolType { + Quic, + #[default] + TlsTcp, + Wss, + Dynamic, +} +#[derive(Debug, Clone)] +pub struct ProtocolAddress { + pub protocol_type: ProtocolType, + pub address: String, +} +impl Default for ProtocolAddress { + fn default() -> Self { + Self { + protocol_type: ProtocolType::default(), + address: "127.0.0.1:29872".to_string(), + } + } +} +impl FromStr for ProtocolAddress { + type Err = String; + + fn from_str(s: &str) -> Result { + let (protocol_type, server_addr) = parse_server(s)?; + Ok(Self { + protocol_type, + address: server_addr, + }) + } +} +impl fmt::Display for ProtocolAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let prefix = match self.protocol_type { + ProtocolType::Quic => "quic://", + ProtocolType::TlsTcp => "tcp://", + ProtocolType::Wss => "wss://", + ProtocolType::Dynamic => "dynamic://", + }; + write!(f, "{}{}", prefix, self.address) + } +} +pub fn parse_server(val: &str) -> Result<(ProtocolType, String), String> { + let val = val.trim().to_lowercase(); + if let Some(s) = val.strip_prefix("quic://") { + return Ok((ProtocolType::Quic, s.to_string())); + } + if let Some(s) = val.strip_prefix("tcp://") { + return Ok((ProtocolType::TlsTcp, s.to_string())); + } + if let Some(s) = val.strip_prefix("wss://") { + return Ok((ProtocolType::Wss, s.to_string())); + } + if let Some(s) = val.strip_prefix("dynamic://") { + return Ok((ProtocolType::Dynamic, s.to_string())); + } + if val.contains("://") { + return Err(format!("Unknown protocol in server address: {}", val)); + } + Ok((ProtocolType::TlsTcp, val)) +} +impl ConnectRegConfig { + pub fn reg_msg_request( + &self, + server_id: u32, + registration_mode: RegistrationMode, + ) -> RegRequestMsg { + RegRequestMsg { + network_code: self.network_code.to_string(), + device_id: self.device_id.to_string(), + ip: self.ip, + name: self.device_name.to_string(), + version: env!("CARGO_PKG_VERSION").to_string(), + key_sign: self.key_sign.clone(), + ip_variable: self.ip_variable, + server_id, + registration_mode, + } + } + pub async fn to_connect_config(&self) -> anyhow::Result { + let (protocol_type, server_domain) = match self.server_addr.protocol_type { + ProtocolType::Dynamic => { + let mut txt = crate::utils::dns_query::dns_query_txt( + &self.server_addr.address, + vec![], + &None, + ) + .await?; + txt.shuffle(&mut rand::rng()); + let x = txt.first().context("DNS query failed")?; + let x = x.to_lowercase(); + let (protocol_type, domain) = if let Some(v) = x.strip_prefix("udp://") { + (ProtocolType::Quic, v) + } else if let Some(v) = x.strip_prefix("quic://") { + (ProtocolType::Quic, v) + } else if let Some(v) = x.strip_prefix("tcp://") { + (ProtocolType::TlsTcp, v) + } else if let Some(v) = x.strip_prefix("ws://") { + (ProtocolType::TlsTcp, v) + } else if let Some(v) = x.strip_prefix("wss://") { + (ProtocolType::TlsTcp, v) + } else { + (ProtocolType::TlsTcp, x.as_str()) + }; + (protocol_type, domain.to_owned()) + } + v => (v, self.server_addr.address.to_string()), + }; + let server_addr = + crate::utils::dns_query::dns_query_one(&server_domain, &vec![], &None).await?; + let server_domain = strip_port(&server_domain).to_owned(); + Ok(ConnectConfig { + protocol_type, + server_addr, + server_domain, + cert_mode: self.cert_mode.clone(), + }) + } +} +fn strip_port(addr: &str) -> &str { + if let Some(stripped) = addr.strip_prefix('[') + && let Some(pos) = stripped.find(']') + { + return &stripped[..pos]; + } + + if addr.contains(':') && !addr.contains('.') && addr.matches(':').count() > 1 { + return addr; + } + + if let Some((host, port)) = addr.rsplit_once(':') + && port.chars().all(|c| c.is_ascii_digit()) + { + return host; + } + + addr +} +impl ConnectConfig { + pub fn server_addr(&self) -> SocketAddr { + self.server_addr + } + pub fn server_name(&self) -> &String { + &self.server_domain + } +} diff --git a/vnt-core/src/tunnel_core/server/transport/mod.rs b/vnt-core/src/tunnel_core/server/transport/mod.rs new file mode 100644 index 0000000..64c89c1 --- /dev/null +++ b/vnt-core/src/tunnel_core/server/transport/mod.rs @@ -0,0 +1,105 @@ +use crate::protocol::ip_packet_protocol::NetPacket; +use crate::protocol::transmission::TransmissionBytes; +use crate::tunnel_core::server::transport::config::{ConnectConfig, ProtocolType}; +use crate::tunnel_core::server::transport::quic::QuicTransport; +use crate::tunnel_core::server::transport::tcp::TlsTcpTransport; +use crate::tunnel_core::server::transport::wss::WssTransport; +use anyhow::{Context, bail}; +use bytes::Bytes; +use std::time::Duration; + +pub mod config; +pub(crate) mod quic; +pub(crate) mod tcp; +pub(crate) mod wss; + +#[derive(Default)] +pub(crate) enum TransportClient { + Quic(QuicTransport), + TlsTcp(TlsTcpTransport), + Wss(WssTransport), + #[default] + Pending, +} + +impl TransportClient { + pub fn new() -> Self { + TransportClient::default() + } + pub fn disconnect(&mut self) { + match self { + TransportClient::Quic(c) => c.disconnect(), + TransportClient::TlsTcp(c) => c.disconnect(), + TransportClient::Wss(c) => c.disconnect(), + TransportClient::Pending => {} + }; + *self = TransportClient::Pending; + } + pub async fn connect_timeout( + &mut self, + config: &ConnectConfig, + timeout: Duration, + ) -> anyhow::Result<()> { + tokio::time::timeout(timeout, self.connect(config)) + .await + .context("timeout")? + } + pub async fn connect(&mut self, config: &ConnectConfig) -> anyhow::Result<()> { + match self { + TransportClient::Quic(c) => c.connect(config).await?, + TransportClient::TlsTcp(c) => c.connect(config).await?, + TransportClient::Wss(c) => c.connect(config).await?, + TransportClient::Pending => match config.protocol_type { + ProtocolType::Quic => { + let mut transport = QuicTransport::new(); + transport.connect(config).await?; + *self = TransportClient::Quic(transport); + } + ProtocolType::TlsTcp => { + let mut transport = TlsTcpTransport::new(); + transport.connect(config).await?; + *self = TransportClient::TlsTcp(transport); + } + ProtocolType::Wss => { + let mut transport = WssTransport::new(); + transport.connect(config).await?; + *self = TransportClient::Wss(transport); + } + ProtocolType::Dynamic => { + bail!("unreachable connect") + } + }, + }; + Ok(()) + } + + pub async fn send(&mut self, buf: Bytes) -> anyhow::Result<()> { + match self { + TransportClient::Quic(c) => c.send(buf).await, + TransportClient::TlsTcp(c) => c.send(buf).await, + TransportClient::Wss(c) => c.send(buf).await, + TransportClient::Pending => { + bail!("Not connected"); + } + } + } + pub async fn next(&mut self) -> anyhow::Result { + match self { + TransportClient::Quic(c) => c.next().await, + TransportClient::TlsTcp(c) => c.next().await, + TransportClient::Wss(c) => c.next().await, + + TransportClient::Pending => { + bail!("Not connected"); + } + } + } + pub async fn next_timeout(&mut self, timeout: Duration) -> anyhow::Result { + tokio::time::timeout(timeout, self.next()) + .await + .context("timeout")? + } + pub async fn send_turn(&mut self, buf: NetPacket) -> anyhow::Result<()> { + self.send(buf.into_buffer().into_bytes().freeze()).await + } +} diff --git a/vnt-core/src/tunnel_core/server/transport/quic.rs b/vnt-core/src/tunnel_core/server/transport/quic.rs new file mode 100644 index 0000000..ca9e0a1 --- /dev/null +++ b/vnt-core/src/tunnel_core/server/transport/quic.rs @@ -0,0 +1,90 @@ +use crate::protocol::transmission::TransmissionBytes; +use crate::tls::verifier::CertValidationMode; +use crate::tunnel_core::server::transport::config::ConnectConfig; +use anyhow::{Context, bail}; +use bytes::Bytes; +use futures::{SinkExt, StreamExt}; +use quinn::{ClientConfig, Endpoint, RecvStream, SendStream}; +use std::sync::Arc; +use tokio_util::codec::{FramedRead, FramedWrite, LengthDelimitedCodec}; + +#[derive(Default)] +pub struct QuicTransport { + framed: Option<( + FramedWrite, + FramedRead, + )>, +} +impl QuicTransport { + pub fn new() -> Self { + Default::default() + } + pub fn disconnect(&mut self) { + self.framed = None; + } + pub async fn connect(&mut self, config: &ConnectConfig) -> anyhow::Result<()> { + if self.framed.is_some() { + bail!("Already connected"); + } + let (w, r) = connect_quic(config).await?; + self.framed = Some((w, r)); + Ok(()) + } + pub async fn send(&mut self, buf: Bytes) -> anyhow::Result<()> { + let Some((w, _r)) = self.framed.as_mut() else { + bail!("Not connected"); + }; + w.send(buf).await.context("send to server failed") + } + pub async fn next(&mut self) -> anyhow::Result { + let Some((_w, r)) = self.framed.as_mut() else { + bail!("Not connected"); + }; + r.next() + .await + .context("EOF")? + .context("receive from server failed") + .map(TransmissionBytes::from) + } +} +pub async fn connect_quic( + config: &ConnectConfig, +) -> anyhow::Result<( + FramedWrite, + FramedRead, +)> { + let server_addr = config.server_addr(); + let server_name = config.server_name(); + let quic_config = create_client_config(&config.cert_mode)?; + let mut endpoint = match Endpoint::client((std::net::Ipv6Addr::UNSPECIFIED, 0).into()) { + Ok(endpoint) => endpoint, + Err(e) => { + log::warn!("Failed to create QUIC endpoint: {}", e); + Endpoint::client((std::net::Ipv4Addr::UNSPECIFIED, 0).into()) + .context("Failed to create QUIC endpoint")? + } + }; + + endpoint.set_default_client_config(quic_config); + let connection = endpoint + .connect(server_addr, server_name)? + .await + .context("Failed to establish QUIC connection")?; + let (send_stream, recv_stream) = connection + .open_bi() + .await + .context("Failed to open bidirectional stream")?; + let framed_write = FramedWrite::new(send_stream, LengthDelimitedCodec::new()); + let framed_read = FramedRead::new(recv_stream, LengthDelimitedCodec::new()); + Ok((framed_write, framed_read)) +} + +fn create_client_config(cert_mode: &CertValidationMode) -> anyhow::Result { + let config = cert_mode.create_tls_client_config()?; + let client_config = ClientConfig::new(Arc::new( + quinn::crypto::rustls::QuicClientConfig::try_from(config) + .context("Failed to create QUIC client config")?, + )); + + Ok(client_config) +} diff --git a/vnt-core/src/tunnel_core/server/transport/tcp.rs b/vnt-core/src/tunnel_core/server/transport/tcp.rs new file mode 100644 index 0000000..8b14f89 --- /dev/null +++ b/vnt-core/src/tunnel_core/server/transport/tcp.rs @@ -0,0 +1,79 @@ +use crate::protocol::transmission::TransmissionBytes; +use crate::tunnel_core::server::transport::config::ConnectConfig; +use anyhow::{Context, bail}; +use bytes::Bytes; +use futures::{SinkExt, StreamExt}; +use std::sync::Arc; +use tokio::net::TcpStream; +use tokio_rustls::{TlsConnector, client::TlsStream}; +use tokio_util::codec::{Framed, LengthDelimitedCodec}; + +type TlsTcpStream = TlsStream; + +#[derive(Default)] +pub struct TlsTcpTransport { + framed: Option>, +} + +impl TlsTcpTransport { + pub fn new() -> Self { + Default::default() + } + pub fn disconnect(&mut self) { + self.framed = None; + } + pub async fn connect(&mut self, config: &ConnectConfig) -> anyhow::Result<()> { + if self.framed.is_some() { + bail!("Already connected"); + } + let framed = connect_tls_tcp(config).await?; + self.framed = Some(framed); + Ok(()) + } + pub async fn send(&mut self, buf: Bytes) -> anyhow::Result<()> { + let Some(framed) = self.framed.as_mut() else { + bail!("Not connected"); + }; + framed.send(buf).await.context("send to server failed") + } + pub async fn next(&mut self) -> anyhow::Result { + let Some(framed) = self.framed.as_mut() else { + bail!("Not connected"); + }; + framed + .next() + .await + .context("EOF")? + .context("receive from server failed") + .map(TransmissionBytes::from) + } +} + +pub async fn connect_tls_tcp( + config: &ConnectConfig, +) -> anyhow::Result> { + let server_addr = config.server_addr(); + let server_name = config.server_name().clone(); + + let rustls_config = config.cert_mode.create_tls_client_config()?; + let connector = TlsConnector::from(Arc::new(rustls_config)); + + let tcp_stream = TcpStream::connect(server_addr) + .await + .context("Failed to establish underlying TCP connection")?; + if let Err(e) = tcp_stream.set_nodelay(true) { + log::error!("Failed to set TCP_NODELAY: {}", e); + } + let dns_name = server_name + .try_into() + .context("Invalid server name for TLS")?; + + let tls_stream = connector + .connect(dns_name, tcp_stream) + .await + .context("Failed to perform TLS handshake")?; + + let framed = Framed::new(tls_stream, LengthDelimitedCodec::new()); + + Ok(framed) +} diff --git a/vnt-core/src/tunnel_core/server/transport/wss.rs b/vnt-core/src/tunnel_core/server/transport/wss.rs new file mode 100644 index 0000000..5ef72d9 --- /dev/null +++ b/vnt-core/src/tunnel_core/server/transport/wss.rs @@ -0,0 +1,96 @@ +use crate::protocol::transmission::TransmissionBytes; +use crate::tunnel_core::server::transport::config::ConnectConfig; +use anyhow::{Context, bail}; +use bytes::Bytes; +use futures::{SinkExt, StreamExt}; +use std::sync::Arc; +use tokio::net::TcpStream; +use tokio_rustls::{TlsConnector, client::TlsStream}; +use tokio_tungstenite::{WebSocketStream, client_async, tungstenite::Message}; + +type WssStream = WebSocketStream>; + +#[derive(Default)] +pub struct WssTransport { + stream: Option, +} + +impl WssTransport { + pub fn new() -> Self { + Default::default() + } + pub fn disconnect(&mut self) { + self.stream = None; + } + pub async fn connect(&mut self, config: &ConnectConfig) -> anyhow::Result<()> { + if self.stream.is_some() { + bail!("Already connected"); + } + let stream = connect_wss(config).await?; + self.stream = Some(stream); + Ok(()) + } + pub async fn send(&mut self, buf: Bytes) -> anyhow::Result<()> { + let Some(framed) = self.stream.as_mut() else { + bail!("Not connected"); + }; + framed + .send(Message::Binary(buf)) + .await + .context("send to server failed") + } + pub async fn next(&mut self) -> anyhow::Result { + let Some(framed) = self.stream.as_mut() else { + bail!("Not connected"); + }; + loop { + let message = framed + .next() + .await + .context("EOF")? + .context("receive from server failed")?; + match message { + Message::Binary(buf) => { + return Ok(TransmissionBytes::from(buf)); + } + Message::Close(_) => { + bail!("Disconnected"); + } + _ => { + continue; + } + } + } + } +} + +pub async fn connect_wss(config: &ConnectConfig) -> anyhow::Result { + let server_addr = config.server_addr(); + let server_name = config.server_name().clone(); + + let rustls_config = config.cert_mode.create_tls_client_config()?; + let connector = TlsConnector::from(Arc::new(rustls_config)); + + let tcp_stream = TcpStream::connect(server_addr) + .await + .context("Failed to establish underlying TCP connection")?; + if let Err(e) = tcp_stream.set_nodelay(true) { + log::error!("Failed to set TCP_NODELAY: {}", e); + } + let url = format!("wss://{}", server_name); + + let dns_name = server_name + .try_into() + .context("Invalid server name for TLS")?; + + let tls_stream = connector + .connect(dns_name, tcp_stream) + .await + .context("Failed to perform TLS handshake")?; + + let (ws_stream, _response) = client_async(url, tls_stream) + .await + .context("Failed to perform WebSocket handshake")?; + + Ok(ws_stream) +} diff --git a/vnt-core/src/utils/device_id.rs b/vnt-core/src/utils/device_id.rs new file mode 100644 index 0000000..15a337c --- /dev/null +++ b/vnt-core/src/utils/device_id.rs @@ -0,0 +1,29 @@ +use anyhow::Context; +use std::fs; +use std::path::Path; +pub fn get_device_id() -> anyhow::Result { + match machine_uid::get() { + Ok(id) => return Ok(id), + Err(e) => { + log::warn!("Failed to get system ID: {}. Using fallback.", e); + } + } + + get_fallback_id() +} +fn get_fallback_id() -> anyhow::Result { + let path = Path::new("device_id"); + + if let Ok(content) = fs::read_to_string(path) { + let id = content.trim(); + if !id.is_empty() { + return Ok(id.to_string()); + } + } + + let new_id = uuid::Uuid::new_v4().to_string(); + + fs::write(path, &new_id).context("Failed to write device_id file")?; + + Ok(new_id) +} diff --git a/vnt-core/src/utils/dns_query.rs b/vnt-core/src/utils/dns_query.rs new file mode 100644 index 0000000..e26bd7b --- /dev/null +++ b/vnt-core/src/utils/dns_query.rs @@ -0,0 +1,251 @@ +use anyhow::{Context, anyhow}; +use dns_parser::{Builder, Packet, QueryClass, QueryType, RData, ResponseCode}; +use rand::seq::SliceRandom; +use std::io; +use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr}; +use std::str::FromStr; +use std::time::Duration; +use tokio::net::UdpSocket; + +use rust_p2p_core::socket::LocalInterface; + +pub async fn dns_query_txt( + domain: &str, + mut name_servers: Vec, + default_interface: &Option, +) -> io::Result> { + let mut err: Option = None; + if name_servers.is_empty() { + name_servers.push("223.5.5.5:53".into()); + name_servers.push("114.114.114.114:53".into()); + } + for name_server in name_servers { + match txt_dns(domain, name_server, default_interface).await { + Ok(addr) => { + if !addr.is_empty() { + return Ok(addr); + } + } + Err(e) => { + err.replace(e); + } + } + continue; + } + if let Some(e) = err { + Err(e) + } else { + Err(io::Error::other(format!("DNS query failed {domain:?}"))) + } +} +pub async fn dns_query_one( + domain: &str, + name_servers: &Vec, + default_interface: &Option, +) -> anyhow::Result { + let mut vec = dns_query_all(domain, name_servers, default_interface).await?; + vec.shuffle(&mut rand::rng()); + vec.pop().context("DNS query failed") +} +pub async fn dns_query_all( + domain: &str, + name_servers: &Vec, + default_interface: &Option, +) -> anyhow::Result> { + match SocketAddr::from_str(domain) { + Ok(addr) => Ok(vec![addr]), + Err(_) => { + if name_servers.is_empty() { + let addrs: Vec = tokio::net::lookup_host(domain) + .await + .map_err(|e| io::Error::other(format!("DNS query failed: {domain:?},{e:?}")))? + .collect(); + return Ok(addrs); + } + + let mut err: Option = None; + for name_server in name_servers { + let end_index = domain + .rfind(':') + .ok_or_else(|| io::Error::other(format!("not port: {domain:?}")))?; + let host = &domain[..end_index]; + let port = u16::from_str(&domain[end_index + 1..]) + .map_err(|_| io::Error::other(format!("not port: {domain:?}")))?; + let th1 = { + let host = host.to_string(); + let name_server = name_server.clone(); + let default_interface = default_interface.clone(); + tokio::spawn(a_dns(host, name_server, default_interface.clone())) + }; + let th2 = { + let host = host.to_string(); + let name_server = name_server.clone(); + let default_interface = default_interface.clone(); + tokio::spawn(aaaa_dns(host, name_server, default_interface.clone())) + }; + let mut addr = Vec::new(); + match th1.await? { + Ok(rs) => { + for ip in rs { + addr.push(SocketAddr::new(ip.into(), port)); + } + } + Err(e) => { + err.replace(e); + } + } + match th2.await? { + Ok(rs) => { + for ip in rs { + addr.push(SocketAddr::new(ip.into(), port)); + } + } + Err(e) => { + if addr.is_empty() { + err.replace(e); + continue; + } + } + } + if addr.is_empty() { + continue; + } + return Ok(addr); + } + if let Some(e) = err { + Err(e.into()) + } else { + Err(anyhow!("DNS query failed {domain:?}")) + } + } + } +} + +async fn query<'a>( + udp: &UdpSocket, + domain: &str, + name_server: SocketAddr, + record_type: QueryType, + buf: &'a mut [u8], +) -> io::Result> { + let mut builder = Builder::new_query(1, true); + builder.add_question(domain, false, record_type, QueryClass::IN); + let packet = builder.build().unwrap(); + + udp.connect(name_server).await?; + let mut count = 0; + let len = loop { + udp.send(&packet).await?; + + match tokio::time::timeout(Duration::from_secs(3), udp.recv(buf)).await { + Ok(len) => { + break len?; + } + Err(_) => { + count += 1; + if count < 3 { + continue; + } + Err(io::Error::other(format!("DNS {name_server:?} recv error ")))? + } + }; + }; + + let pkt = Packet::parse(&buf[..len]).map_err(|e| { + io::Error::other(format!( + "domain {domain:?} DNS {name_server:?} data error: {e}" + )) + })?; + if pkt.header.response_code != ResponseCode::NoError { + return Err(io::Error::other(format!( + "response_code {} DNS {:?} domain {:?}", + pkt.header.response_code, name_server, domain + ))); + } + if pkt.answers.is_empty() { + return Err(io::Error::other(format!( + "No records received DNS {name_server:?} domain {domain:?}" + ))); + } + + Ok(pkt) +} + +pub async fn txt_dns( + domain: &str, + name_server: String, + default_interface: &Option, +) -> io::Result> { + let name_server: SocketAddr = name_server + .parse() + .map_err(|e| io::Error::other(format!("dns {name_server} is error :{e:?}")))?; + let udp = bind_udp(name_server, default_interface)?; + let mut buf = vec![0u8; 65536]; + let message = query(&udp, domain, name_server, QueryType::TXT, &mut buf).await?; + let mut rs = Vec::new(); + for record in message.answers { + if let RData::TXT(txt) = record.data { + for x in txt.iter() { + let txt = std::str::from_utf8(x) + .map_err(|_| io::Error::other("record type txt is not string"))?; + rs.push(txt.to_string()); + } + } + } + Ok(rs) +} + +fn bind_udp( + name_server: SocketAddr, + default_interface: &Option, +) -> io::Result { + let addr: SocketAddr = if name_server.is_ipv4() { + "0.0.0.0:0" + .parse() + .expect("valid IPv4 socket address literal") + } else { + "[::]:0".parse().expect("valid IPv6 socket address literal") + }; + let socket = rust_p2p_core::socket::bind_udp(addr, default_interface.as_ref())?; + UdpSocket::from_std(socket.into()) +} + +pub async fn a_dns( + domain: String, + name_server: String, + default_interface: Option, +) -> io::Result> { + let name_server: SocketAddr = name_server + .parse() + .map_err(|e| io::Error::other(format!("dns {name_server} is error :{e:?}")))?; + let udp = bind_udp(name_server, &default_interface)?; + let mut buf = vec![0u8; 65536]; + let message = query(&udp, &domain, name_server, QueryType::A, &mut buf).await?; + let mut rs = Vec::new(); + for record in message.answers { + if let RData::A(a) = record.data { + rs.push(a.0); + } + } + Ok(rs) +} + +pub async fn aaaa_dns( + domain: String, + name_server: String, + default_interface: Option, +) -> io::Result> { + let name_server: SocketAddr = name_server + .parse() + .map_err(|e| io::Error::other(format!("dns {name_server} is error :{e:?}")))?; + let udp = bind_udp(name_server, &default_interface)?; + let mut buf = vec![0u8; 65536]; + let message = query(&udp, &domain, name_server, QueryType::AAAA, &mut buf).await?; + let mut rs = Vec::new(); + for record in message.answers { + if let RData::AAAA(a) = record.data { + rs.push(a.0); + } + } + Ok(rs) +} diff --git a/vnt-core/src/utils/mod.rs b/vnt-core/src/utils/mod.rs new file mode 100644 index 0000000..bc70bbc --- /dev/null +++ b/vnt-core/src/utils/mod.rs @@ -0,0 +1,11 @@ +pub mod device_id; +pub(crate) mod dns_query; +pub mod task_control; +pub(crate) mod time { + pub fn now_ts_ms() -> i64 { + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_millis() as i64 + } +} diff --git a/vnt-core/src/utils/task_control.rs b/vnt-core/src/utils/task_control.rs new file mode 100644 index 0000000..bed1d28 --- /dev/null +++ b/vnt-core/src/utils/task_control.rs @@ -0,0 +1,255 @@ +use parking_lot::Mutex; +use std::collections::HashMap; +use std::future::Future; +use std::sync::{Arc, Weak}; +use tokio::sync::Notify; +use tokio::task::{Id, JoinHandle}; + +struct TaskGroupState { + stopped: bool, + tasks: HashMap>, +} + +struct TaskGroupInner { + state: Mutex, + all_stopped_notify: Notify, +} + +impl TaskGroupInner { + fn new() -> Self { + Self { + state: Mutex::new(TaskGroupState { + stopped: false, + tasks: HashMap::new(), + }), + all_stopped_notify: Notify::new(), + } + } + + fn spawn(self: &Arc, f: F) -> Option + where + F: Future + Send + 'static, + F::Output: Send + 'static, + { + let mut state = self.state.lock(); + if state.stopped { + return None; + } + + let guard = TaskGuard { + inner: Arc::downgrade(self), + }; + + let handle = tokio::spawn(async move { + let _guard = guard; + f.await; + }); + + let task_id = handle.id(); + state.tasks.insert(task_id, handle); + Some(task_id) + } + + fn stop(&self) { + let mut state = self.state.lock(); + state.stopped = true; + for (_, handle) in state.tasks.drain() { + handle.abort(); + } + } + + fn is_stopped(&self) -> bool { + self.state.lock().stopped + } + + fn remove_task(&self, task_id: Id) { + let all_stopped = { + let mut state = self.state.lock(); + state.tasks.remove(&task_id); + if state.tasks.is_empty() { + state.stopped = true; + true + } else { + false + } + }; + if all_stopped { + self.all_stopped_notify.notify_waiters(); + } + } + + async fn abort_task(&self, task_id: Id) { + let handle = self.state.lock().tasks.remove(&task_id); + if let Some(handle) = handle { + handle.abort(); + _ = handle.await; + } + } + + async fn join_all(&self) { + let tasks = std::mem::take(&mut self.state.lock().tasks); + for (_, h) in tasks { + let _ = h.await; + } + } + + fn all_tasks_stopped(&self) -> bool { + let state = self.state.lock(); + state.stopped && state.tasks.is_empty() + } +} + +impl Drop for TaskGroupInner { + fn drop(&mut self) { + self.stop(); + } +} + +struct TaskGuard { + inner: Weak, +} + +impl Drop for TaskGuard { + fn drop(&mut self) { + if let Some(inner) = self.inner.upgrade() { + let task_id = tokio::task::id(); + inner.remove_task(task_id); + } + } +} + +#[derive(Clone)] +pub struct TaskGroup { + inner: Arc, +} + +impl TaskGroup { + fn new() -> Self { + Self { + inner: Arc::new(TaskGroupInner::new()), + } + } + + pub fn stop(&self) { + self.inner.stop(); + } + + pub fn is_stopped(&self) -> bool { + self.inner.is_stopped() + } + + pub fn spawn(&self, f: F) -> SubTask + where + F: Future + Send + 'static, + F::Output: Send + 'static, + { + match self.inner.spawn(f) { + Some(task_id) => SubTask::new(task_id, Arc::downgrade(&self.inner)), + None => SubTask::empty(), + } + } + + pub async fn join_all(&self) { + self.inner.join_all().await; + } + + pub async fn wait_all_stopped(&self) { + loop { + if self.inner.all_tasks_stopped() { + return; + } + self.inner.all_stopped_notify.notified().await; + } + } +} + +pub struct SubTask { + task_id: Option, + inner: Weak, +} + +impl SubTask { + fn new(task_id: Id, inner: Weak) -> Self { + Self { + task_id: Some(task_id), + inner, + } + } + + fn empty() -> Self { + Self { + task_id: None, + inner: Weak::new(), + } + } + + pub async fn stop(&self) { + if let Some(task_id) = self.task_id + && let Some(inner) = self.inner.upgrade() + { + inner.abort_task(task_id).await; + } + } + + pub fn is_running(&self) -> bool { + if let Some(task_id) = self.task_id + && let Some(inner) = self.inner.upgrade() + { + return inner.state.lock().tasks.contains_key(&task_id); + } + false + } + + pub fn id(&self) -> Option { + self.task_id + } +} +#[derive(Clone, Default)] +pub struct TaskGroupManager { + task_group: Arc>>, +} + +impl TaskGroupManager { + pub fn new() -> Self { + TaskGroupManager::default() + } + + pub fn is_running(&self) -> bool { + self.task_group.lock().is_some() + } + + pub fn is_stopped(&self) -> bool { + self.task_group.lock().is_none() + } + + pub fn create_task(&self) -> anyhow::Result<(TaskGroup, TaskGroupGuard)> { + let mut guard = self.task_group.lock(); + if guard.is_some() { + anyhow::bail!("运行中") + } + + let task_group = TaskGroup::new(); + guard.replace(task_group.clone()); + let stop_guard = TaskGroupGuard { + task_group: self.task_group.clone(), + }; + Ok((task_group, stop_guard)) + } + + pub fn stop(&self) { + let option = self.task_group.lock(); + if let Some(task_group) = option.as_ref() { + task_group.stop(); + } + } +} +pub struct TaskGroupGuard { + task_group: Arc>>, +} +impl Drop for TaskGroupGuard { + fn drop(&mut self) { + if let Some(task_group) = self.task_group.lock().take() { + task_group.stop(); + } + } +} diff --git a/vnt-ipc/Cargo.toml b/vnt-ipc/Cargo.toml new file mode 100644 index 0000000..96a1abf --- /dev/null +++ b/vnt-ipc/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "vnt-ipc" +version = "2.0.0" +edition = "2024" + +[dependencies] + +vnt-core = { path = "../vnt-core" } +anyhow = "1.0.100" +futures = "0.3.31" +prost = "0.14.1" +tokio = "1.48.0" +tokio-util = "0.7.17" +log = "0.4.29" +console = "0.16.2" +time = "0.3.44" +cli-table = "0.5.0" + +[build-dependencies] +prost-build = "0.14" \ No newline at end of file diff --git a/vnt-ipc/build.rs b/vnt-ipc/build.rs new file mode 100644 index 0000000..3dde738 --- /dev/null +++ b/vnt-ipc/build.rs @@ -0,0 +1,7 @@ +fn main() { + let mut config = prost_build::Config::new(); + config.protoc_arg("--experimental_allow_proto3_optional"); + config + .compile_protos(&["proto/local_ipc.proto"], &["proto"]) + .unwrap(); +} diff --git a/vnt-ipc/proto/local_ipc.proto b/vnt-ipc/proto/local_ipc.proto new file mode 100644 index 0000000..266e730 --- /dev/null +++ b/vnt-ipc/proto/local_ipc.proto @@ -0,0 +1,103 @@ +syntax = "proto3"; + +package protocol.local_ipc; + +message IpcRequest{ + oneof ipc_cmd{ + AppInfoCMD app_info = 1; + ClientIpsCMD client_ips = 2; + ClientListCMD client_list = 3; + AllRouteCMD all_route = 4; + } +} +message AppInfoCMD{ + +} +message ClientIpsCMD{ + +} +message ClientListCMD{ + +} +message AllRouteCMD{ + +} + + +message IpcResponse{ + oneof response_payload{ + AppInfo app_info = 1; + ClientIpList client_ips = 2; + ClientInfoList client_list = 3; + ClientRouteList all_route = 4; + } +} + +message AppInfo{ + repeated ServerInfo server_info = 1; + string name = 3; + string version = 4; + optional uint32 ip = 5; + uint32 online_client_num = 6; + uint32 offline_client_num = 7; + uint32 direct_client_num = 8; + string device_id = 11; + optional string nat_type = 12; + repeated string public_ipv4s = 13; + optional string public_ipv6 = 14; +} + +message ServerInfo{ + string server = 1; + bool connected = 2; + optional uint32 server_rtt = 3; + optional int64 last_connected_time = 4; +} + +message ClientInfoList{ + repeated ClientInfoItem items = 1; +} + +message ClientInfoItem{ + uint32 ip = 1; + string name = 2; + string version = 3; + bool online = 4; + bool is_direct = 5; + int64 last_connected_time = 6; + optional uint32 rtt = 7; + bool key_equal = 8; + optional PacketLoss packet_loss = 9; +} + +message PacketLoss{ + uint64 sent = 1; + uint64 received = 2; + double loss_rate = 3; +} + +message ClientRouteList{ + repeated ClientRouteItem items = 1; +} + +message ClientRouteItem{ + uint32 ip = 1; + repeated Route route_list = 2; +} + +message Route{ + string addr = 1; + uint32 metric = 2; + uint32 rtt = 3; +} + +message ClientIpList{ + repeated ClientIpItem items = 1; +} + +message ClientIpItem{ + uint32 ip = 1; + bool online = 2; + bool is_direct = 3; + optional uint32 rtt = 4; +} \ No newline at end of file diff --git a/vnt-ipc/src/client.rs b/vnt-ipc/src/client.rs new file mode 100644 index 0000000..13bfdd1 --- /dev/null +++ b/vnt-ipc/src/client.rs @@ -0,0 +1,285 @@ +use crate::message::ipc_request::IpcCmd; +use crate::message::ipc_response::ResponsePayload; +use crate::message::{ + AppInfo, ClientInfoList, ClientIpList, ClientRouteList, IpcRequest, IpcResponse, +}; +use crate::{DEFAULT_PORT, get_port_file_path}; +use anyhow::Context; +use cli_table::{Cell, Style, Table, print_stdout}; +use console::style; +use futures::{SinkExt, StreamExt}; +use prost::Message; +use std::fs; +use std::net::Ipv4Addr; +use std::time::Duration; +use time::macros::format_description; +use time::{OffsetDateTime, UtcOffset}; +use tokio::net::TcpStream; +use tokio_util::codec::{Framed, LengthDelimitedCodec}; + +pub async fn run_client(cmd: IpcCmd, port: Option) -> anyhow::Result<()> { + let path = get_port_file_path(); + let port = if let Some(port) = port { + port + } else { + match fs::read_to_string(&path) { + Ok(port_string) => port_string.trim().parse().context("parse port error")?, + Err(_) => DEFAULT_PORT, + } + }; + + let addr = format!("127.0.0.1:{}", port); + + let stream = tokio::time::timeout(Duration::from_secs(3), TcpStream::connect(&addr)) + .await + .context("Connection timed out")??; + + let mut framed = Framed::new(stream, LengthDelimitedCodec::new()); + + framed + .send(IpcRequest { ipc_cmd: Some(cmd) }.encode_to_vec().into()) + .await?; + let response = framed.next().await.context("Unexpected end of stream")??; + let response = IpcResponse::decode(response).context("decode response error")?; + match response + .response_payload + .context("response payload is empty")? + { + ResponsePayload::AppInfo(info) => { + print_app_info(info); + } + ResponsePayload::ClientIps(list) => print_client_ip_list(list)?, + ResponsePayload::ClientList(list) => { + print_client_list(list)?; + } + ResponsePayload::AllRoute(route_list) => { + print_route_list(route_list)?; + } + }; + Ok(()) +} + +fn key_style(s: &str) -> console::StyledObject<&str> { + style(s).cyan().bright().bold() +} +pub fn print_app_info(info: AppInfo) { + println!( + "\n{}", + style("--- Application Information ---").italic().dim() + ); + + for server_info in info.server_info { + let status_colored = if server_info.connected { + if let Some(rtt) = server_info.server_rtt { + style(format!("Online {rtt}ms")) + .green() + .bright() + .to_string() + } else { + style("Online").green().bright().to_string() + } + } else { + style("Offline").red().bright().to_string() + }; + + println!( + "{:24}{} ({})", + key_style("Server:"), + style(&server_info.server).white(), + status_colored + ); + + let time_str = server_info + .last_connected_time + .map(|v| style(ts_to_string(v / 1000)).white().to_string()) + .unwrap_or_else(|| "Never".to_string()); + + println!("{:24}{}", key_style("Last Connected Time:"), time_str); + } + + println!("{:24}{}", key_style("Name:"), style(&info.name).white()); + println!("{:24}{}", key_style("Id:"), style(&info.device_id).white()); + println!( + "{:24}{}", + key_style("Version:"), + style(&info.version).white() + ); + + let ip_str = info + .ip + .map(|v| { + style(Ipv4Addr::from(v).to_string()) + .yellow() + .bright() + .to_string() + }) + .unwrap_or_else(|| style("N/A").red().bright().to_string()); + + println!("{:24}{}", key_style("IP:"), ip_str); + + let total_clients = info.online_client_num + info.offline_client_num; + + println!( + "{:24}{}", + key_style("Total Clients:"), + style(total_clients).color256(213) + ); + + println!( + "{:24}{}", + key_style("Online Clients:"), + style(info.online_client_num).color256(82) + ); + + println!( + "{:24}{}", + key_style("P2P Clients:"), + style(info.direct_client_num).color256(117) + ); + println!( + "{:24}{}", + key_style("Nat Type:"), + style(info.nat_type.unwrap_or_else(|| "Unknown".to_string())).color256(117) + ); + println!( + "{:24}{}", + key_style("Ipv6:"), + style(info.public_ipv6.unwrap_or_default()).color256(117) + ); + println!( + "{:24}{}", + key_style("Public Ipv4:"), + style(info.public_ipv4s.join(",")).color256(117) + ); + println!( + "{}", + style("-------------------------------").italic().dim() + ); +} + +fn print_client_ip_list(list: ClientIpList) -> anyhow::Result<()> { + println!("\n--- Client List ({}) ---", list.items.len()); + + let table = list + .items + .iter() + .map(|item| { + vec![ + Ipv4Addr::from(item.ip).to_string().cell(), + item.online.to_string().cell(), + item.is_direct.to_string().cell(), + item.rtt.map(|v| v.to_string()).unwrap_or_default().cell(), + ] + }) + .table() + .title(vec![ + "IP".cell().bold(true), + "Online".cell().bold(true), + "P2P".cell().bold(true), + "RTT".cell().bold(true), + ]); + + print_stdout(table)?; + println!("\n"); + Ok(()) +} + +fn print_client_list(list: ClientInfoList) -> anyhow::Result<()> { + println!("\n--- Client List ({}) ---", list.items.len()); + + let table = list + .items + .iter() + .map(|item| { + let key_equal: bool = item.key_equal; + let mut ip_str = Ipv4Addr::from(item.ip).to_string(); + if !key_equal { + ip_str.push_str("(Key Mismatch)"); + } + let loss_str = item + .packet_loss + .as_ref() + .map(|v| format!("{:.1}%", v.loss_rate)) + .unwrap_or_default(); + + vec![ + ip_str.cell(), + item.name.clone().cell(), + item.version.clone().cell(), + item.online.to_string().cell(), + item.is_direct.to_string().cell(), + item.rtt.map(|v| v.to_string()).unwrap_or_default().cell(), + loss_str.cell(), + ts_to_string(item.last_connected_time).cell(), + ] + }) + .table() + .title(vec![ + "IP".cell().bold(true), + "Name".cell().bold(true), + "Version".cell().bold(true), + "Online".cell().bold(true), + "P2P".cell().bold(true), + "RTT".cell().bold(true), + "Loss".cell().bold(true), + "Last Connected Time".cell().bold(true), + ]); + + print_stdout(table)?; + println!("\n"); + Ok(()) +} + +pub fn print_route_list(route_list: ClientRouteList) -> anyhow::Result<()> { + let total_routes: usize = route_list + .items + .iter() + .map(|item| item.route_list.len()) + .sum(); + + println!("\n--- All Routes List (Total: {}) ---", total_routes); + + let mut rows = Vec::new(); + for client_route in route_list.items { + let client_ip_str = Ipv4Addr::from(client_route.ip).to_string(); + + for route in client_route.route_list { + rows.push(vec![ + client_ip_str.clone().cell(), + route.metric.to_string().cell(), + route.rtt.to_string().cell(), + route.addr.clone().cell(), + ]); + } + } + + let table = rows.table().title(vec![ + "Destination IP".cell().bold(true), + "Metric".cell().bold(true), + "RTT (ms)".cell().bold(true), + "Remote Address".cell().bold(true), + ]); + + print_stdout(table)?; + println!("\n"); + Ok(()) +} + +pub fn ts_to_string(ts_secs: i64) -> String { + let dt = match OffsetDateTime::from_unix_timestamp(ts_secs) { + Ok(dt) => dt, + Err(_) => { + return String::new(); + } + }; + let local_offset = match UtcOffset::local_offset_at(dt) { + Ok(offset) => offset, + Err(_e) => match UtcOffset::from_hms(8, 0, 0) { + Ok(offset) => offset, + Err(_) => return String::new(), + }, + }; + let dt_local = dt.to_offset(local_offset); + let format = format_description!("[year]-[month]-[day] [hour]:[minute]:[second]"); + dt_local.format(&format).unwrap() +} diff --git a/vnt-ipc/src/lib.rs b/vnt-ipc/src/lib.rs new file mode 100644 index 0000000..e9fc7aa --- /dev/null +++ b/vnt-ipc/src/lib.rs @@ -0,0 +1,12 @@ +pub mod client; +pub mod message; +pub mod server; + +pub use vnt_core::*; +const DEFAULT_PORT: u16 = 11233; + +const PORT_FILE: &str = "PORT"; + +fn get_port_file_path() -> std::path::PathBuf { + std::path::PathBuf::from(PORT_FILE) +} diff --git a/vnt-ipc/src/message.rs b/vnt-ipc/src/message.rs new file mode 100644 index 0000000..c6e193c --- /dev/null +++ b/vnt-ipc/src/message.rs @@ -0,0 +1,4 @@ +mod proto { + include!(concat!(env!("OUT_DIR"), "/protocol.local_ipc.rs")); +} +pub use proto::*; diff --git a/vnt-ipc/src/server.rs b/vnt-ipc/src/server.rs new file mode 100644 index 0000000..bfd3018 --- /dev/null +++ b/vnt-ipc/src/server.rs @@ -0,0 +1,194 @@ +use crate::{DEFAULT_PORT, get_port_file_path}; + +use crate::message::ipc_request::IpcCmd; +use crate::message::ipc_response::ResponsePayload; +use crate::message::{ + AppInfo, ClientInfoItem, ClientInfoList, ClientIpItem, ClientIpList, ClientRouteItem, + ClientRouteList, IpcRequest, IpcResponse, PacketLoss, Route, ServerInfo, +}; +use anyhow::bail; +use futures::{SinkExt, StreamExt}; +use prost::Message; +use std::fs; +use std::net::Ipv4Addr; +use tokio::io::{self}; +use tokio::net::{TcpListener, TcpStream}; +use tokio_util::codec::{Framed, LengthDelimitedCodec}; +use vnt_core::api::VntApi; + +async fn handle_connection(stream: TcpStream, vnt_api: VntApi) -> anyhow::Result<()> { + let mut framed = Framed::new(stream, LengthDelimitedCodec::new()); + + if let Some(Ok(message)) = framed.next().await { + let request = IpcRequest::decode(message.as_ref())?; + let Some(cmd) = request.ipc_cmd else { + bail!("Received an IpcRequest but it was None"); + }; + let rs = match cmd { + IpcCmd::AppInfo(_) => { + let info = app_info(&vnt_api); + ResponsePayload::AppInfo(info) + } + IpcCmd::ClientIps(_) => { + let items = vnt_api + .client_ips() + .into_iter() + .map(|v| { + let rtt = if let Some(v) = vnt_api.find_route(&v.ip) { + Some(v.rtt()) + } else { + vnt_api.server_node_rtt(&v.ip).map(|v| v * 2) + }; + ClientIpItem { + ip: v.ip.into(), + online: v.online, + is_direct: vnt_api.is_direct(&v.ip), + rtt, + } + }) + .collect(); + ResponsePayload::ClientIps(ClientIpList { items }) + } + IpcCmd::ClientList(_) => { + let client_list = vnt_api.server_rpc().client_list().await?; + let key_sign = vnt_api.get_config().and_then(|config| config.key_sign()); + let items = client_list + .list + .into_iter() + .map(|v| { + let ip = Ipv4Addr::from(v.ip); + let rtt = if let Some(v) = vnt_api.find_route(&ip) { + Some(v.rtt()) + } else { + vnt_api.server_node_rtt(&ip).map(|v| v * 2) + }; + let packet_loss = vnt_api.packet_loss_info(&ip).map(|info| PacketLoss { + sent: info.sent, + received: info.received, + loss_rate: info.loss_rate, + }); + ClientInfoItem { + ip: v.ip, + name: v.name, + version: v.version, + online: v.online, + is_direct: vnt_api.is_direct(&Ipv4Addr::from(v.ip)), + last_connected_time: v.last_connected_time, + rtt, + key_equal: key_sign == v.key_sign, + packet_loss, + } + }) + .collect(); + ResponsePayload::ClientList(ClientInfoList { items }) + } + IpcCmd::AllRoute(_) => { + let route_list = all_route(&vnt_api); + ResponsePayload::AllRoute(route_list) + } + }; + let rs = IpcResponse { + response_payload: Some(rs), + }; + framed.send(rs.encode_to_vec().into()).await?; + } + + Ok(()) +} +fn app_info(vnt_api: &VntApi) -> AppInfo { + let config = vnt_api.get_config(); + let ips = vnt_api.client_ips(); + let online_client_num = ips.iter().filter(|v| v.online).count() as _; + let offline_client_num = ips.iter().filter(|v| !v.online).count() as _; + let direct_client_num = ips.iter().filter(|ip| vnt_api.is_direct(&ip.ip)).count() as _; + let server_node_list = vnt_api.server_node_list(); + let nat_info = vnt_api.nat_info(); + AppInfo { + server_info: server_node_list + .into_iter() + .map(|v| ServerInfo { + server: v.server_addr.to_string(), + connected: v.connected, + server_rtt: v.rtt, + last_connected_time: v.last_connected_time, + }) + .collect(), + name: config + .as_ref() + .map(|v| v.device_name.clone()) + .unwrap_or_default(), + version: env!("CARGO_PKG_VERSION").to_string(), + ip: vnt_api.network().map(|v| v.ip.into()), + online_client_num, + offline_client_num, + direct_client_num, + device_id: config.map(|v| v.device_id.clone()).unwrap_or_default(), + nat_type: nat_info.as_ref().map(|v| format!("{:?}", v.nat_type)), + public_ipv4s: nat_info + .as_ref() + .map(|v| { + v.public_ips + .iter() + .map(|v| v.to_string()) + .collect::>() + }) + .unwrap_or_default(), + public_ipv6: nat_info + .as_ref() + .and_then(|v| v.ipv6.map(|v| v.to_string())), + } +} +fn all_route(vnt_api: &VntApi) -> ClientRouteList { + let vec = vnt_api.route_table(); + let mut items = Vec::with_capacity(vec.len()); + for (ip, route_list) in vec { + items.push(ClientRouteItem { + ip: ip.into(), + route_list: route_list + .into_iter() + .map(|v| Route { + addr: v.route_key().to_string(), + metric: v.metric() as _, + rtt: v.rtt(), + }) + .collect(), + }) + } + ClientRouteList { items } +} + +pub async fn run_server(bind_port: Option, vnt_api: VntApi) -> anyhow::Result<()> { + let mut port = bind_port.unwrap_or(DEFAULT_PORT); + let listener; + + loop { + match TcpListener::bind(format!("127.0.0.1:{}", port)).await { + Ok(l) => { + listener = l; + break; + } + Err(e) if bind_port.is_none() && e.kind() == io::ErrorKind::AddrInUse => { + port = 0; + } + Err(e) => bail!("bind :{e:?}"), + } + } + + let bound_addr = listener.local_addr()?; + log::info!("IPC Listening on {}", bound_addr); + let actual_port = bound_addr.port(); + + let path = get_port_file_path(); + fs::write(&path, actual_port.to_string())?; + + loop { + let (stream, peer_addr) = listener.accept().await?; + log::info!("IPC Connection from {}", peer_addr); + let vnt_api = vnt_api.clone(); + tokio::spawn(async move { + if let Err(e) = handle_connection(stream, vnt_api).await { + log::warn!("IPC Error handling client: {:?},peer_addr={peer_addr}", e); + } + }); + } +} diff --git a/vnt-web/Cargo.toml b/vnt-web/Cargo.toml new file mode 100644 index 0000000..d9535ed --- /dev/null +++ b/vnt-web/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "vnt-web" +version = "2.0.0" +edition = "2024" + +[dependencies] +vnt-core = { path = "../vnt-core" } +tokio = { version = "1", features = ["full"] } +axum = "0.8.8" +tower-http = { version = "0.6", features = ["fs", "cors", "trace"] } + +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +anyhow = "1.0.100" +log = "0.4.29" +ipnet = "2.11.0" +hostname = "0.4.2" +route_manager = "0.2.11" +toml = "0.9.8" +rust-embed = "8.0" +mime_guess = "2.0" +parking_lot = "0.12" +time = { version = "0.3.45", features = ["local-offset", "formatting", "macros"] } \ No newline at end of file diff --git a/vnt-web/src/lib.rs b/vnt-web/src/lib.rs new file mode 100644 index 0000000..44d9192 --- /dev/null +++ b/vnt-web/src/lib.rs @@ -0,0 +1,17 @@ +mod service_http; + +pub use service_http::run_http_server; + +struct ScopeGuard(Option); + +impl Drop for ScopeGuard { + fn drop(&mut self) { + if let Some(f) = self.0.take() { + f(); + } + } +} + +fn defer(f: F) -> ScopeGuard { + ScopeGuard(Some(f)) +} diff --git a/vnt-web/src/service_http.rs b/vnt-web/src/service_http.rs new file mode 100644 index 0000000..c0bcd67 --- /dev/null +++ b/vnt-web/src/service_http.rs @@ -0,0 +1,1105 @@ +use crate::defer; +use anyhow::{Context, anyhow}; +use axum::body::Body; +use axum::http::{HeaderMap, HeaderValue, StatusCode, Uri, header}; +use axum::response::IntoResponse; +use axum::{ + Json, Router, + extract::{Query, Request, State}, + middleware, + response::Response, + routing::{get, post}, +}; +use ipnet::Ipv4Net; +use mime_guess::from_path; +use parking_lot::Mutex; +use rust_embed::RustEmbed; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr}; +use std::path::{Path, PathBuf}; +use std::sync::Arc; +use std::time::{Instant, SystemTime, UNIX_EPOCH}; +use time::{OffsetDateTime, format_description}; +use tokio::fs; +use tokio::net::TcpListener; +use tower_http::cors::{Any, CorsLayer}; +use vnt_core::api::VntApi; +use vnt_core::context::config::Config as CoreConfig; +use vnt_core::core::{DEFAULT_MTU, NetworkManager}; +use vnt_core::nat::NetInput; +use vnt_core::port_mapping::PortMapping; +use vnt_core::tls::verifier::CertValidationMode; +use vnt_core::tunnel_core::server::transport::config::ProtocolAddress; +use vnt_core::utils::task_control::TaskGroupManager; + +const CONFIG_DIR: &str = "vnt_config"; +const CURRENT_CONFIG_RECORD: &str = "vnt_current_config.txt"; + +#[derive(Serialize, Clone, Copy, PartialEq, Eq, Default)] +#[serde(rename_all = "lowercase")] +enum VntStatus { + #[default] + Stopped, + Starting, + Running, +} + +#[derive(Clone)] +struct HttpAppState { + task_group_manager: TaskGroupManager, + inner: Arc>, +} + +#[derive(Default)] +struct HttpAppStateInner { + vnt: Option, + status: VntStatus, + start_logs: Vec, +} + +impl HttpAppState { + fn starting(&self) -> anyhow::Result<()> { + let mut inner = self.inner.lock(); + if inner.status != VntStatus::Stopped { + return Err(anyhow!("VNT is already starting or running")); + } + if inner.vnt.is_some() { + return Err(anyhow!("VNT is already running")); + } + inner.status = VntStatus::Starting; + inner.start_logs.clear(); + Ok(()) + } + fn stopped(&self) { + let mut inner = self.inner.lock(); + inner.vnt.take(); + inner.status = VntStatus::Stopped; + } + fn starting_to_stopped(&self) { + let mut inner = self.inner.lock(); + if inner.status != VntStatus::Starting { + return; + } + inner.vnt.take(); + inner.status = VntStatus::Stopped; + inner + .start_logs + .push(format!("[{}] 启动中断", HttpAppState::timestamp())); + } + fn starting_to_running(&self) { + let mut inner = self.inner.lock(); + if inner.status != VntStatus::Starting { + log::error!("starting_to_running VNT is not starting"); + return; + } + inner.status = VntStatus::Running; + inner.start_logs.clear(); + } + + fn record_log(&self, msg: impl Into) { + let mut inner = self.inner.lock(); + if inner.status != VntStatus::Starting { + return; + } + inner + .start_logs + .push(format!("[{}] {}", Self::timestamp(), msg.into())); + } + fn record_log_and_stopped(&self, msg: impl Into) { + let mut inner = self.inner.lock(); + if inner.status != VntStatus::Starting { + return; + } + inner + .start_logs + .push(format!("[{}] {}", Self::timestamp(), msg.into())); + inner.status = VntStatus::Stopped; + } + fn status(&self) -> VntStatus { + self.inner.lock().status + } + + fn timestamp() -> String { + let now = OffsetDateTime::now_local().unwrap_or_else(|_| OffsetDateTime::now_utc()); + let format = format_description::parse("[hour]:[minute]:[second]").unwrap(); + now.format(&format) + .unwrap_or_else(|_| "00:00:00".to_string()) + } +} + +struct VntHandler { + api: VntApi, + config_name: String, + config_file_name: String, +} + +#[derive(Serialize)] +struct ApiResponse { + code: i32, + msg: String, + data: Option, +} + +impl ApiResponse { + fn success(data: T) -> Self { + Self { + code: 0, + msg: "success".to_string(), + data: Some(data), + } + } + + fn error(msg: impl Into) -> Self { + Self { + code: -1, + msg: msg.into(), + data: None, + } + } +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct StartConfig { + pub config_name: Option, + pub server: Vec, + pub cert_mode: Option, + pub network_code: String, + pub device_id: Option, + pub device_name: Option, + pub tun_name: Option, + pub ip: Option, + pub password: Option, + #[serde(default)] + pub no_punch: bool, + #[serde(default)] + pub compress: bool, + #[serde(default)] + pub rtx: bool, + #[serde(default)] + pub fec: bool, + #[serde(default)] + pub input: Vec, + #[serde(default)] + pub output: Vec, + #[serde(default)] + pub no_nat: bool, + #[serde(default)] + pub no_tun: bool, + pub mtu: Option, + #[serde(default)] + pub port_mapping: Vec, + #[serde(default)] + pub allow_mapping: bool, + #[serde(default)] + pub udp_stun: Vec, + #[serde(default)] + pub tcp_stun: Vec, +} + +#[derive(Deserialize)] +struct SaveConfigReq { + file_name: Option, + config: String, +} + +#[derive(Deserialize)] +struct FileReq { + file_name: String, +} + +#[derive(Serialize)] +struct ConfigSummary { + file_name: String, + config_name: String, +} + +#[derive(Serialize, Default)] +struct HttpAppInfo { + name: String, + version: String, + ip: Option, + prefix_len: Option, + gateway: Option, + device_id: String, + status: VntStatus, + current_config_name: Option, + current_config_file: Option, + online_client_num: usize, + offline_client_num: usize, + direct_client_num: usize, + server_info: Vec, + nat_type: Option, + public_ipv6: Option, + public_ipv4s: Vec, + network_code: Option, + mtu: Option, + fec: Option, + compress: Option, + encrypt: Option, + rtx: Option, +} + +#[derive(Serialize)] +struct HttpServerInfo { + server: String, + connected: bool, + server_rtt: Option, + server_version: Option, +} + +#[derive(Serialize)] +struct HttpClientItem { + ip: Ipv4Addr, + name: Option, + online: bool, + route: Option, + version: String, + last_connected_time: i64, + key_equal: i32, + nat_info: Option, + packet_loss: Option, + traffic: Option, +} + +#[derive(Serialize)] +struct HttpClientNatInfo { + nat_type: String, + public_ips: Vec, + ipv6: Option, +} + +#[derive(Serialize)] +struct HttpPacketLoss { + sent: u64, + received: u64, + loss_rate: f64, +} + +#[derive(Serialize)] +struct HttpTraffic { + tx_bytes: u64, + rx_bytes: u64, +} + +#[derive(Serialize)] +struct HttpRouteItem { + ip: Ipv4Addr, + routes: Vec, +} + +#[derive(Serialize)] +struct HttpRouteDetail { + addr: String, + protocol: String, + metric: u8, + rtt: u32, +} + +#[derive(Serialize)] +struct StartStatusResponse { + status: VntStatus, + logs: Vec, +} + +async fn get_start_status( + State(state): State, +) -> Json> { + let lock = state.inner.lock(); + Json(ApiResponse::success(StartStatusResponse { + status: lock.status, + logs: lock.start_logs.clone(), + })) +} + +async fn logging_middleware(req: Request, next: axum::middleware::Next) -> Response { + let method = req.method().clone(); + let uri = req.uri().clone(); + let start = Instant::now(); + let response = next.run(req).await; + log::info!( + "Request: {} {} | Status: {} | Took: {:?}", + method, + uri, + response.status(), + start.elapsed() + ); + response +} + +#[derive(RustEmbed)] +#[folder = "static/"] +struct Asset; + +pub async fn run_http_server( + addr: SocketAddr, + start_config_file_name: Option, +) -> anyhow::Result<()> { + fs::create_dir_all(CONFIG_DIR) + .await + .context("Failed to create config directory")?; + + let state = HttpAppState { + task_group_manager: TaskGroupManager::new(), + inner: Arc::new(Default::default()), + }; + + // 自动启动逻辑 + let auto_start_file = determine_auto_start_file(start_config_file_name).await; + + if let Some((file_name, path)) = auto_start_file { + log::info!("Auto starting VNT with config: {:?}", path); + let state_clone = state.clone(); + tokio::spawn(async move { + if let Err(e) = start_vnt_internal(&state_clone, file_name, path).await { + log::error!("Auto start failed: {:?}", e); + } + }); + } + + let cors = CorsLayer::new() + .allow_origin(Any) + .allow_methods(Any) + .allow_headers(Any); + + let app = Router::new() + .route("/api/info", get(get_info)) + .route("/api/peers", get(get_peers)) + .route("/api/routes", get(get_routes)) + .route("/api/start/status", get(get_start_status)) + .route("/api/start", post(start_vnt_handler)) + .route("/api/stop", post(stop_vnt_handler)) + .route("/api/restart", post(restart_vnt_handler)) + .route("/api/config/list", get(list_configs)) + .route( + "/api/config", + get(get_config).post(save_config).delete(delete_config), + ) + .layer(cors) + .layer(middleware::from_fn(logging_middleware)) + .with_state(state) + .fallback(static_handler); + + log::info!("HTTP API Listening on http://{}", addr); + let listener = TcpListener::bind(addr).await?; + axum::serve(listener, app) + .with_graceful_shutdown(shutdown_signal()) + .await?; + + Ok(()) +} + +/// 确定自动启动的配置文件 +async fn determine_auto_start_file( + start_config_file_name: Option, +) -> Option<(String, PathBuf)> { + let path = if let Some(name) = start_config_file_name { + Some(name) + } else if Path::new(CURRENT_CONFIG_RECORD).exists() { + fs::read_to_string(CURRENT_CONFIG_RECORD) + .await + .ok() + .filter(|content| !content.trim().is_empty()) + .map(|content| Path::new(CONFIG_DIR).join(content.trim())) + } else { + None + }; + + path.and_then(|p| { + let file_name = p.file_name()?.to_str()?.to_string(); + if p.exists() { + Some((file_name, p)) + } else { + log::warn!("Auto start config file not found: {:?}", p); + None + } + }) +} + +fn build_headers_for_path(path: &str) -> HeaderMap { + let mut headers = HeaderMap::new(); + + let is_gz = path.ends_with(".gz"); + + let mime = if is_gz { + let original = path.trim_end_matches(".gz"); + from_path(original).first_or_octet_stream() + } else { + from_path(path).first_or_octet_stream() + }; + headers.insert( + header::CONTENT_TYPE, + HeaderValue::from_str(mime.as_ref()).unwrap(), + ); + + if is_gz { + headers.insert(header::CONTENT_ENCODING, HeaderValue::from_static("gzip")); + headers.insert(header::VARY, HeaderValue::from_static("Accept-Encoding")); + } + headers.insert( + header::CACHE_CONTROL, + HeaderValue::from_static("public, max-age=31536000, immutable"), + ); + headers +} +async fn static_handler(uri: Uri) -> impl IntoResponse { + let path = uri.path().trim_start_matches('/'); + let path = if path.is_empty() { "index.html" } else { path }; + + // 先尝试从本地文件读取 + let local_path = Path::new("static").join(path); + if local_path.is_file() + && let Ok(content) = tokio::fs::read(&local_path).await + { + log::debug!("Serving file from local filesystem: {:?}", local_path); + let mime = from_path(&local_path).first_or_octet_stream(); + return ([(header::CONTENT_TYPE, mime.as_ref())], content).into_response(); + } + + // 从内嵌数据中读取 + if let Some(content) = Asset::get(path) { + log::debug!("Serving file from embedded assets: {}", path); + let headers = build_headers_for_path(path); + return (headers, Body::from(content.data)).into_response(); + } + + (StatusCode::NOT_FOUND, "404 Not Found").into_response() +} + +/// 启动 VNT 服务的入口函数 +async fn start_vnt_internal( + state: &HttpAppState, + file_name: String, + file_path: PathBuf, +) -> anyhow::Result<()> { + log::info!("Starting VNT service: {}", file_name); + state.starting()?; + + let state_for_error = state.clone(); + let on_error_guard = defer(move || { + state_for_error.starting_to_stopped(); + }); + + state.record_log(format!("启动配置: {}", file_name)); + state.record_log("读取配置文件"); + + // 读取并解析配置 + let content = fs::read_to_string(&file_path) + .await + .with_context(|| format!("Config file not found: {:?}", file_path))?; + + state.record_log("解析配置文件内容"); + let cfg: StartConfig = toml::from_str(&content).context("Failed to parse TOML config")?; + + let config_display_name = cfg.config_name.clone().unwrap_or_else(|| file_name.clone()); + let core_config = convert_config(cfg)?; + let sub_input = core_config.input.clone(); + + state.record_log("创建异步任务组"); + let (task_group, task_group_guard) = state + .task_group_manager + .create_task() + .context("Create task failed")?; + + state.record_log("创建组网管理器"); + + let state_clone = state.clone(); + tokio::spawn(async move { + let result = start_vnt_network( + state_clone.clone(), + file_name, + config_display_name, + core_config, + sub_input, + task_group, + task_group_guard, + ) + .await; + + if let Err(e) = result { + log::error!("Failed to start VNT network: {:?}", e); + state_clone.record_log_and_stopped(format!("启动失败: {}", e)); + } + drop(on_error_guard); + }); + + Ok(()) +} + +/// 执行实际的网络启动操作 +async fn start_vnt_network( + state: HttpAppState, + file_name: String, + config_display_name: String, + core_config: CoreConfig, + sub_input: Vec, + task_group: vnt_core::utils::task_control::TaskGroup, + task_group_guard: vnt_core::utils::task_control::TaskGroupGuard, +) -> anyhow::Result<()> { + let mut network_manager = NetworkManager::create_network(Box::new(core_config), task_group.clone()) + .await + .map_err(|e| anyhow!("Create network failed: {:?}", e))?; + + let vnt_api = network_manager.vnt_api(); + + { + let mut lock = state.inner.lock(); + if lock.vnt.is_some() { + return Err(anyhow!("VNT is already running")); + } + lock.vnt = Some(VntHandler { + api: vnt_api, + config_name: config_display_name, + config_file_name: file_name.clone(), + }); + } + + let state_for_vnt_cleanup = state.clone(); + let vnt_cleanup_guard = defer(move || { + state_for_vnt_cleanup.stopped(); + }); + + state.record_log("连接服务器,执行注册"); + log::info!("Registering with server"); + + let reg_msg = network_manager + .register() + .await + .context("Registration failed")?; + + state.record_log(format!("注册成功 {}/{}", reg_msg.ip, reg_msg.prefix_len)); + log::info!("Network Started: {}/{}", reg_msg.ip, reg_msg.prefix_len); + if !network_manager.is_no_tun() { + state.record_log("正在创建 TUN 虚拟网卡"); + network_manager.start_tun().await?; + + state.record_log("创建 TUN 虚拟网卡成功,设置 IP"); + network_manager + .set_network_ip(reg_msg.ip, reg_msg.prefix_len) + .await?; + state.record_log("设置 IP 成功"); + + // 配置子网路由 + if !sub_input.is_empty() + && let Ok(if_index) = network_manager.tun_if_index().await + && let Ok(mut route_manager) = route_manager::RouteManager::new() + { + state.record_log("配置子网路由"); + for input in &sub_input { + let route = + route_manager::Route::new(input.net.network().into(), input.net.prefix_len()) + .with_gateway(input.target_ip.into()) + .with_if_index(if_index); + + if let Err(e) = route_manager.add(&route) { + log::error!("add route [{route}] error: {e:?}"); + } else { + log::info!("add route [{route}] successful"); + } + } + } + } + + state.starting_to_running(); + + // 启动网络管理任务 + task_group.spawn(async move { + network_manager.wait_all_stopped().await; + drop(task_group_guard); + drop(network_manager); + drop(vnt_cleanup_guard); + log::info!("Network manager stopped."); + }); + + // 记录当前配置 + if let Err(e) = fs::write(CURRENT_CONFIG_RECORD, &file_name).await { + log::warn!("Failed to record current config: {}", e); + } + Ok(()) +} + +fn is_valid_file_name(file_name: &str) -> bool { + !file_name.is_empty() + && !file_name.contains("..") + && !file_name.contains('/') + && !file_name.contains('\\') +} + +async fn start_vnt_handler( + State(state): State, + Json(req): Json, +) -> Json> { + if !is_valid_file_name(&req.file_name) { + return Json(ApiResponse::error("Invalid file name")); + } + + let path = Path::new(CONFIG_DIR).join(&req.file_name); + if !path.exists() { + return Json(ApiResponse::error("Config file not found")); + } + + match start_vnt_internal(&state, req.file_name, path).await { + Ok(_) => Json(ApiResponse::success(())), + Err(e) => Json(ApiResponse::error(format!("Start failed: {:?}", e))), + } +} + +async fn stop_vnt_handler(State(state): State) -> Json> { + if state.status() == VntStatus::Stopped { + return Json(ApiResponse::error("Vnt stopped")); + } + state.task_group_manager.stop(); + + let _ = fs::write(CURRENT_CONFIG_RECORD, "").await; + Json(ApiResponse::success(())) +} + +async fn restart_vnt_handler( + State(state): State, + Json(req): Json, +) -> Json> { + if !is_valid_file_name(&req.file_name) { + return Json(ApiResponse::error("Invalid file name")); + } + + let path = Path::new(CONFIG_DIR).join(&req.file_name); + if !path.exists() { + return Json(ApiResponse::error("Config file not found")); + } + + // 先停止(如果正在运行则停止,否则忽略) + if state.status() != VntStatus::Stopped { + state.task_group_manager.stop(); + // 等待停止完成 + for _ in 0..50 { + if state.status() == VntStatus::Stopped { + break; + } + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + } + } + + // 再启动 + match start_vnt_internal(&state, req.file_name, path).await { + Ok(_) => Json(ApiResponse::success(())), + Err(e) => Json(ApiResponse::error(format!("Restart failed: {:?}", e))), + } +} + +async fn get_info(State(state): State) -> Json> { + let lock = state.inner.lock(); + let status = lock.status; + + let info = if let Some(handler) = lock.vnt.as_ref() { + let api = &handler.api; + let config = api.get_config(); + let ips = api.client_ips(); + let server_node_list = api.server_node_list(); + let nat_info = api.nat_info(); + let network = api.network(); + + HttpAppInfo { + name: config + .as_ref() + .map(|v| v.device_name.clone()) + .unwrap_or_default(), + version: env!("CARGO_PKG_VERSION").to_string(), + ip: network.map(|v| v.ip), + prefix_len: network.map(|v| v.prefix_len), + gateway: network.map(|v| v.gateway), + device_id: config + .as_ref() + .map(|v| v.device_id.clone()) + .unwrap_or_default(), + status, + current_config_name: Some(handler.config_name.clone()), + current_config_file: Some(handler.config_file_name.clone()), + online_client_num: ips.iter().filter(|v| v.online).count(), + offline_client_num: ips.iter().filter(|v| !v.online).count(), + direct_client_num: ips.iter().filter(|ip| api.is_direct(&ip.ip)).count(), + server_info: server_node_list + .into_iter() + .map(|v| HttpServerInfo { + server: v.server_addr.to_string(), + connected: v.connected, + server_rtt: v.rtt, + server_version: v.server_version, + }) + .collect(), + nat_type: nat_info.as_ref().map(|v| format!("{:?}", v.nat_type)), + public_ipv4s: nat_info + .as_ref() + .map(|v| v.public_ips.clone()) + .unwrap_or_default(), + public_ipv6: nat_info.as_ref().and_then(|v| v.ipv6), + network_code: config.as_ref().map(|v| v.network_code.clone()), + mtu: config.as_ref().map(|v| v.mtu.unwrap_or(DEFAULT_MTU)), + fec: config.as_ref().map(|v| v.fec), + compress: config.as_ref().map(|v| v.compress), + encrypt: config.as_ref().map(|v| v.password.is_some()), + rtx: config.as_ref().map(|v| v.rtx), + } + } else { + HttpAppInfo { + version: env!("CARGO_PKG_VERSION").to_string(), + status, + ..Default::default() + } + }; + + Json(ApiResponse::success(info)) +} + +async fn list_configs() -> Json>> { + let mut result = Vec::new(); + + let Ok(mut entries) = fs::read_dir(CONFIG_DIR).await else { + return Json(ApiResponse::success(result)); + }; + + while let Ok(Some(entry)) = entries.next_entry().await { + let path = entry.path(); + + if path.extension().is_none_or(|ext| ext != "toml") { + continue; + } + + let Ok(content) = fs::read_to_string(&path).await else { + continue; + }; + + match toml::from_str::(&content) { + Ok(cfg) => { + let file_name = path + .file_name() + .and_then(|s| s.to_str()) + .unwrap_or("") + .to_string(); + + result.push(ConfigSummary { + file_name, + config_name: cfg + .config_name + .unwrap_or_else(|| entry.file_name().to_string_lossy().to_string()), + }); + } + Err(e) => { + log::warn!("Failed to parse configuration file {:?}: {:?}", path, e); + } + } + } + + result.sort_by(|a, b| b.file_name.cmp(&a.file_name)); + Json(ApiResponse::success(result)) +} + +async fn save_config(Json(req): Json) -> Json> { + // 验证配置格式 + if let Err(e) = toml::from_str::(&req.config) { + log::warn!("Failed to parse configuration: {:?}", e); + return Json(ApiResponse::error(format!("Invalid TOML format: {}", e))); + } + + let file_name = req + .file_name + .filter(|name| !name.is_empty()) + .unwrap_or_else(|| { + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis(); + format!("{}.toml", now) + }); + + if !is_valid_file_name(&file_name) { + return Json(ApiResponse::error("Invalid file name")); + } + + let target_path = Path::new(CONFIG_DIR).join(&file_name); + + match fs::write(&target_path, &req.config).await { + Ok(_) => Json(ApiResponse::success(())), + Err(e) => Json(ApiResponse::error(format!("Write config failed: {}", e))), + } +} + +async fn get_config(Query(req): Query) -> Json> { + if !is_valid_file_name(&req.file_name) { + return Json(ApiResponse::error("Invalid file name")); + } + + let path = Path::new(CONFIG_DIR).join(&req.file_name); + + if !path.exists() { + return Json(ApiResponse::error("Config file not found")); + } + + match fs::read_to_string(&path).await { + Ok(content) => Json(ApiResponse::success(content)), + Err(e) => Json(ApiResponse::error(format!("Read file failed: {}", e))), + } +} + +async fn delete_config( + State(state): State, + Query(req): Query, +) -> Json> { + if !is_valid_file_name(&req.file_name) { + return Json(ApiResponse::error("Invalid file name")); + } + { + if let Some(vnt) = &state.inner.lock().vnt + && vnt.config_file_name == req.file_name + { + return Json(ApiResponse::error("此配置已被使用,不能删除")); + } + } + + let path = Path::new(CONFIG_DIR).join(&req.file_name); + + if !path.exists() { + return Json(ApiResponse::error("Config file not found")); + } + + match fs::remove_file(&path).await { + Ok(_) => Json(ApiResponse::success(())), + Err(e) => Json(ApiResponse::error(format!("Delete failed: {}", e))), + } +} + +fn convert_config(cfg: StartConfig) -> anyhow::Result { + let server_addrs: Vec = cfg + .server + .iter() + .map(|s| { + s.parse() + .map_err(|e| anyhow!("invalid server address '{}': {}", s, e)) + }) + .collect::>()?; + + let port_mapping: Vec = cfg + .port_mapping + .iter() + .map(|s| { + s.parse() + .map_err(|e| anyhow!("invalid port_mapping '{}': {}", s, e)) + }) + .collect::>()?; + + let cert_mode = match cfg.cert_mode.as_deref() { + Some(s) => s + .parse() + .map_err(|e| anyhow!("invalid cert_mode '{}': {}", s, e))?, + None => CertValidationMode::InsecureSkipVerification, + }; + + let device_id = match cfg.device_id { + Some(id) => id, + None => vnt_core::utils::device_id::get_device_id() + .map_err(|e| anyhow!("failed to get device_id: {}", e))?, + }; + + let device_name = cfg.device_name.unwrap_or_else(|| { + hostname::get() + .ok() + .and_then(|v| v.into_string().ok()) + .unwrap_or_default() + }); + let mut udp_stun = cfg.udp_stun; + for x in udp_stun.iter_mut() { + if !x.contains(':') { + x.push_str(":3478"); + } + } + let mut tcp_stun = cfg.tcp_stun; + for x in tcp_stun.iter_mut() { + if !x.contains(':') { + x.push_str(":3478"); + } + } + Ok(CoreConfig { + server_addr: server_addrs, + network_code: cfg.network_code, + ip: cfg.ip, + no_punch: cfg.no_punch, + rtx: cfg.rtx, + compress: cfg.compress, + device_id, + device_name, + tun_name: cfg.tun_name, + password: cfg.password, + cert_mode, + input: cfg.input, + output: cfg.output, + no_nat: cfg.no_nat, + no_tun: cfg.no_tun, + mtu: cfg.mtu, + port_mapping, + allow_port_mapping: cfg.allow_mapping, + udp_stun, + tcp_stun, + fec: cfg.fec, + }) +} + +async fn shutdown_signal() { + let ctrl_c = async { + tokio::signal::ctrl_c() + .await + .expect("failed to install Ctrl+C handler"); + }; + + #[cfg(unix)] + let terminate = async { + tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) + .expect("failed to install signal handler") + .recv() + .await; + }; + + #[cfg(not(unix))] + let terminate = std::future::pending::<()>(); + + tokio::select! { + _ = ctrl_c => {}, + _ = terminate => {}, + } +} + +async fn get_peers(State(state): State) -> Json>> { + let api = state.inner.lock().vnt.as_ref().map(|v| v.api.clone()); + + let Some(api) = api else { + return Json(ApiResponse::error("VNT not running")); + }; + + let key_sign = api.get_config().and_then(|config| config.key_sign()); + + let calc_key_equal = |peer_key_sign: &Option| -> i32 { + match (&key_sign, peer_key_sign) { + (None, None) => 2, + (Some(k1), Some(k2)) if k1 == k2 => 1, + (Some(_), Some(_)) => 5, + (Some(_), None) => 3, + (None, Some(_)) => 4, + } + }; + + let build_nat_info = |ip: &Ipv4Addr| -> Option { + api.peer_nat_info(ip).map(|v| HttpClientNatInfo { + nat_type: format!("{:?}", v.nat_type), + public_ips: v.public_ips, + ipv6: v.ipv6, + }) + }; + + let build_packet_loss = |ip: &Ipv4Addr| -> Option { + api.packet_loss_info(ip).map(|v| HttpPacketLoss { + sent: v.sent, + received: v.received, + loss_rate: v.loss_rate, + }) + }; + + let build_traffic = |ip: &Ipv4Addr| -> Option { + api.traffic_info(ip).map(|v| HttpTraffic { + tx_bytes: v.tx_bytes, + rx_bytes: v.rx_bytes, + }) + }; + + let build_route = |ip: &Ipv4Addr| -> Option { + api.find_route(ip).map(|route| HttpRouteDetail { + addr: route.route_key().to_string(), + protocol: route.route_key().protocol().to_string(), + metric: route.metric(), + rtt: route.rtt(), + }) + }; + + // 先从本地获取基础数据 + let mut merged: HashMap = api + .client_ips() + .into_iter() + .map(|v| { + let ip = v.ip; + let route = build_route(&ip); + // 如果有路由,说明设备在线(可以直接通信) + let has_route = route.is_some(); + ( + ip, + HttpClientItem { + ip, + name: None, + online: v.online || has_route, + route, + version: String::new(), + last_connected_time: 0, + key_equal: 0, + nat_info: build_nat_info(&ip), + packet_loss: build_packet_loss(&ip), + traffic: build_traffic(&ip), + }, + ) + }) + .collect(); + + // 从服务器获取更详细的信息 + if let Ok(resp) = api.server_rpc().client_list().await { + for v in resp.list { + let ip = Ipv4Addr::from(v.ip); + let route = build_route(&ip); + // 如果有路由,说明设备在线(可以直接通信) + let has_route = route.is_some(); + merged.insert( + ip, + HttpClientItem { + ip, + name: Some(v.name), + online: v.online || has_route, + route, + version: v.version, + last_connected_time: v.last_connected_time, + key_equal: calc_key_equal(&v.key_sign), + nat_info: build_nat_info(&ip), + packet_loss: build_packet_loss(&ip), + traffic: build_traffic(&ip), + }, + ); + } + } else { + log::warn!("Failed to get client list from server"); + } + + let mut items: Vec = merged.into_values().collect(); + items.sort_by_key(|it| it.ip); + + Json(ApiResponse::success(items)) +} + +async fn get_routes(State(state): State) -> Json>> { + let lock = state.inner.lock(); + + let Some(handler) = lock.vnt.as_ref() else { + return Json(ApiResponse::error("VNT not running")); + }; + + let table = handler.api.route_table(); + let items: Vec = table + .into_iter() + .map(|(ip, route_list)| HttpRouteItem { + ip, + routes: route_list + .into_iter() + .map(|v| HttpRouteDetail { + addr: v.route_key().to_string(), + protocol: v.route_key().protocol().to_string(), + metric: v.metric(), + rtt: v.rtt(), + }) + .collect(), + }) + .collect(); + + Json(ApiResponse::success(items)) +} diff --git a/vnt-web/static/index.html b/vnt-web/static/index.html new file mode 100644 index 0000000..444b568 --- /dev/null +++ b/vnt-web/static/index.html @@ -0,0 +1,2884 @@ + + + + + + VNT Dashboard + + + + + + + + +
+ + + +
+ +
+
+
+ + {{ info.status === 'running' ? '已运行' : + (info.status === 'starting' ? '启动中...' : + '未启动') }} +
+ +
+ + + + 服务器: {{ isServerConnected ? '已连接' : + '未连接' }} +
+ +
+ {{ info.ip }} +
+
+ +
+ 设备: + {{ info.name || '' }} + {{ info.device_id.substring(0, 8) }}... +
+
+ + +
+ + + + + +
+ + +
+
+
+
+
+
+
+

+ {{ startStatus === 'starting' ? + '正在启动组网...' : (startStatus === + 'running' ? '启动成功' : '启动失败') }} +

+
+ {{ startStatus }} +
+
+
+ >>> + {{ log }} +
+
+ 等待后续步骤... +
+
+ 启动失败: + 请检查配置或网络连接。 +
+
+
+ + +
+
+
+ + + +
+
+
+
+ NAT Type + {{ tooltipState.info.nat_type }} +
+
+ Public IPv4: +
+ {{ pip }} +
+
+
+ IPv6: +
+ {{ tooltipState.info.ipv6 }} +
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + diff --git a/vnt-web/static/tailwindcss3.4.17.js.gz b/vnt-web/static/tailwindcss3.4.17.js.gz new file mode 100644 index 0000000..774ef19 Binary files /dev/null and b/vnt-web/static/tailwindcss3.4.17.js.gz differ diff --git a/vnt-web/static/vue-router.global.prod.js.gz b/vnt-web/static/vue-router.global.prod.js.gz new file mode 100644 index 0000000..0df3f3c Binary files /dev/null and b/vnt-web/static/vue-router.global.prod.js.gz differ diff --git a/vnt-web/static/vue.global.prod.js.gz b/vnt-web/static/vue.global.prod.js.gz new file mode 100644 index 0000000..cab7f19 Binary files /dev/null and b/vnt-web/static/vue.global.prod.js.gz differ diff --git a/vnt/Cargo.toml b/vnt/Cargo.toml deleted file mode 100644 index 44d0ba1..0000000 --- a/vnt/Cargo.toml +++ /dev/null @@ -1,89 +0,0 @@ -[package] -name = "vnt" -version = "1.2.16" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -tun-rs = { version = "2.5.0", optional = true, features = ["interruptible"] } -packet = { path = "./packet" } -bytes = "1.5.0" -log = "0.4.17" -libc = "0.2.137" -crossbeam-utils = "0.8" -crossbeam-epoch = "0.9.15" -parking_lot = "0.12.1" -rand = "0.8.5" -sha2 = { version = "0.10.6", features = ["oid"] } -thiserror = "1.0.37" -protobuf = "=3.2.0" -socket2 = { version = "0.5.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 } -ecb = { version = "0.1.2", optional = true } -chacha20poly1305 = { version = "0.10.1", optional = true } -chacha20 = { version = "0.9.1", optional = true } -aes = "0.8.3" -stun-format = { version = "1.0.1", features = ["fmt", "rfc3489"] } -rsa = { version = "0.9.2", features = [], optional = true } -spki = { version = "0.7.2", features = ["fingerprint", "alloc", "base64"], optional = true } -openssl-sys = { git = "https://github.com/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.46.1", features = ["full"] } - -lz4_flex = { version = "0.11", default-features = false, optional = true } -zstd = { version = "0.13.1", optional = true } - -fnv = "1.0.7" -igd = { version = "0.12.1", optional = true } -tokio-tungstenite = { version = "0.23.1", optional = true } -rustls = { version = "0.23.0", features = ["ring", "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" -protoc-bin-vendored = "3.0.0" -cfg_aliases = "0.2.1" - -[features] -default = ["server_encrypt", "aes_gcm", "aes_cbc", "aes_ecb", "sm4_cbc", "chacha20_poly1305", "ip_proxy", "port_mapping", "lz4_compress", "zstd_compress", "integrated_tun"] -openssl = ["openssl-sys"] -# 从源码编译 -openssl-vendored = ["openssl-sys/vendored"] -ring-cipher = ["ring"] -aes_cbc = ["cbc"] -aes_ecb = ["ecb"] -sm4_cbc = ["libsm"] -aes_gcm = ["aes-gcm"] -chacha20_poly1305 = ["chacha20poly1305", "chacha20"] -server_encrypt = ["aes-gcm", "rsa", "spki"] -ip_proxy = [] -port_mapping = [] -lz4_compress = ["lz4_flex"] -zstd_compress = ["zstd"] -integrated_tun = ["tun-rs"] -upnp = ["igd"] -ws = ["tokio-tungstenite"] -wss = ["ws", "tokio-tungstenite/rustls-tls-native-roots", "tokio-tungstenite/rustls-tls-webpki-roots", "rustls"] diff --git a/vnt/LICENSE b/vnt/LICENSE deleted file mode 100644 index 261eeb9..0000000 --- a/vnt/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vnt/build.rs b/vnt/build.rs deleted file mode 100644 index 5dd2e36..0000000 --- a/vnt/build.rs +++ /dev/null @@ -1,27 +0,0 @@ -use cfg_aliases::cfg_aliases; - -fn main() { - cfg_aliases! { - cipher: { - any(feature = "aes_gcm", - feature = "chacha20_poly1305", - feature = "server_encrypt", - feature = "aes_cbc", - feature = "aes_ecb", - feature = "sm4_cbc" - )}, - } - - std::fs::create_dir_all("src/proto").unwrap(); - protobuf_codegen::Codegen::new() - .pure() - .out_dir("src/proto") - .inputs(&["proto/message.proto"]) - .include("proto") - // .customize( - // protobuf_codegen::Customize::default() - // .tokio_bytes(true) - // ) - .run() - .expect("Codegen failed."); -} diff --git a/vnt/packet/Cargo.toml b/vnt/packet/Cargo.toml deleted file mode 100644 index e1a6166..0000000 --- a/vnt/packet/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "packet" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -byteorder = "1.4.3" diff --git a/vnt/packet/src/arp/arp.rs b/vnt/packet/src/arp/arp.rs deleted file mode 100644 index ea027a3..0000000 --- a/vnt/packet/src/arp/arp.rs +++ /dev/null @@ -1,122 +0,0 @@ -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 { - buffer: B, -} - -impl> ArpPacket { - pub fn unchecked(buffer: B) -> Self { - Self { buffer } - } - pub fn new(buffer: B) -> io::Result { - if buffer.as_ref().len() != 28 { - Err(io::Error::from(io::ErrorKind::InvalidData))?; - } - let packet = Self::unchecked(buffer); - Ok(packet) - } -} - -impl> ArpPacket { - /// 硬件类型 以太网类型为1 - pub fn hardware_type(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[0..2].try_into().unwrap()) - } - /// 上层协议类型,ipv4是0x0800 - pub fn protocol_type(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap()) - } - /// 如果是MAC地址 则长度为6 - pub fn hardware_size(&self) -> u8 { - self.buffer.as_ref()[4] - } - /// 如果是IPv4 则长度为4 - pub fn protocol_size(&self) -> u8 { - self.buffer.as_ref()[5] - } - /// 操作类型,请求和响应 1:ARP请求,2:ARP响应,3:RARP请求,4:RARP响应 - pub fn op_code(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap()) - } - /// 发送端硬件地址,仅支持以太网 - pub fn sender_hardware_addr(&self) -> &[u8] { - &self.buffer.as_ref()[8..14] - } - /// 发送端协议地址,仅支持IPv4 - pub fn sender_protocol_addr(&self) -> &[u8] { - &self.buffer.as_ref()[14..18] - } - /// 接收端硬件地址,仅支持以太网 - pub fn target_hardware_addr(&self) -> &[u8] { - &self.buffer.as_ref()[18..24] - } - /// 接收端协议地址,仅支持IPv4 - pub fn target_protocol_addr(&self) -> &[u8] { - &self.buffer.as_ref()[24..28] - } -} - -impl + AsMut<[u8]>> ArpPacket { - /// 硬件类型 以太网类型为1 - pub fn set_hardware_type(&mut self, value: u16) { - self.buffer.as_mut()[0..2].copy_from_slice(&value.to_be_bytes()) - } - /// 上层协议类型,ipv4是0x0800 - pub fn set_protocol_type(&mut self, value: u16) { - self.buffer.as_mut()[2..4].copy_from_slice(&value.to_be_bytes()) - } - /// 如果是MAC地址 则长度为6 - pub fn set_hardware_size(&mut self, value: u8) { - self.buffer.as_mut()[4] = value - } - /// 如果是IPv4 则长度为4 - pub fn set_protocol_size(&mut self, value: u8) { - self.buffer.as_mut()[5] = value - } - /// 操作类型,请求和响应 1:ARP请求,2:ARP响应,3:RARP请求,4:RARP响应 - pub fn set_op_code(&mut self, value: u16) { - self.buffer.as_mut()[6..8].copy_from_slice(&value.to_be_bytes()) - } - /// 发送端硬件地址,仅支持以太网 - pub fn set_sender_hardware_addr(&mut self, buf: &[u8]) { - self.buffer.as_mut()[8..14].copy_from_slice(buf) - } - /// 发送端协议地址,仅支持IPv4 - pub fn set_sender_protocol_addr(&mut self, buf: &[u8]) { - self.buffer.as_mut()[14..18].copy_from_slice(buf) - } - /// 接收端硬件地址,仅支持以太网 - pub fn set_target_hardware_addr(&mut self, buf: &[u8]) { - self.buffer.as_mut()[18..24].copy_from_slice(buf) - } - /// 接收端协议地址,仅支持IPv4 - pub fn set_target_protocol_addr(&mut self, buf: &[u8]) { - self.buffer.as_mut()[24..28].copy_from_slice(buf) - } -} - -impl> fmt::Debug for ArpPacket { - 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() - } -} diff --git a/vnt/packet/src/arp/mod.rs b/vnt/packet/src/arp/mod.rs deleted file mode 100644 index 6a5d36a..0000000 --- a/vnt/packet/src/arp/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod arp; diff --git a/vnt/packet/src/ethernet/mod.rs b/vnt/packet/src/ethernet/mod.rs deleted file mode 100644 index c9cf115..0000000 --- a/vnt/packet/src/ethernet/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod packet; -pub mod protocol; diff --git a/vnt/packet/src/ethernet/packet.rs b/vnt/packet/src/ethernet/packet.rs deleted file mode 100644 index f54619e..0000000 --- a/vnt/packet/src/ethernet/packet.rs +++ /dev/null @@ -1,77 +0,0 @@ -use crate::ethernet::protocol::Protocol; -use std::{fmt, io}; - -/// 以太网帧协议 -/// https://www.ietf.org/rfc/rfc894.txt -/* - 0 6 12 14 (字节) - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 目的地址 | 源地址 | 类型 | -*/ -pub struct EthernetPacket { - pub buffer: B, -} - -impl> EthernetPacket { - pub fn unchecked(buffer: B) -> EthernetPacket { - EthernetPacket { buffer } - } - - pub fn new(buffer: B) -> io::Result> { - let packet = EthernetPacket::unchecked(buffer); - //头部固定14位 - if packet.buffer.as_ref().len() < 14 { - Err(io::Error::from(io::ErrorKind::InvalidData))?; - } - - Ok(packet) - } -} - -impl> EthernetPacket { - /// 目的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 + AsMut<[u8]>> EthernetPacket { - 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> fmt::Debug for EthernetPacket { - 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() - } -} diff --git a/vnt/packet/src/ethernet/protocol.rs b/vnt/packet/src/ethernet/protocol.rs deleted file mode 100644 index e775a28..0000000 --- a/vnt/packet/src/ethernet/protocol.rs +++ /dev/null @@ -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 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 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, - } - } -} diff --git a/vnt/packet/src/icmp/icmp.rs b/vnt/packet/src/icmp/icmp.rs deleted file mode 100644 index 4ae4ab5..0000000 --- a/vnt/packet/src/icmp/icmp.rs +++ /dev/null @@ -1,167 +0,0 @@ -use crate::cal_checksum; -use crate::icmp::{Code, Kind}; -use crate::ip::ipv4::packet::IpV4Packet; -use byteorder::{BigEndian, ReadBytesExt}; -use std::{fmt, io}; - -/// icmp 协议 -/* https://www.rfc-editor.org/rfc/rfc792 - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Code | Checksum | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 不同Type和Code有不同含义 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 数据体 不同Type和Code有不同含义 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - -*/ - -pub struct IcmpPacket { - pub buffer: B, -} - -impl> IcmpPacket { - pub fn unchecked(buffer: B) -> Self { - Self { buffer } - } - pub fn new(buffer: B) -> io::Result { - if buffer.as_ref().len() < 8 { - Err(io::Error::from(io::ErrorKind::InvalidData))?; - } - let packet = Self::unchecked(buffer); - Ok(packet) - } -} - -impl + AsMut<[u8]>> IcmpPacket { - pub fn set_kind(&mut self, kind: Kind) { - self.buffer.as_mut()[0] = kind.into(); - } - pub fn update_checksum(&mut self) { - self.buffer.as_mut()[2..4].copy_from_slice(&[0, 0]); - let checksum = cal_checksum(self.buffer.as_ref()); - self.buffer.as_mut()[2..4].copy_from_slice(&checksum.to_be_bytes()); - } -} - -impl> IcmpPacket { - pub fn kind(&self) -> Kind { - Kind::from(self.buffer.as_ref()[0]) - } - pub fn code(&self) -> Code { - Code::from(self.kind(), self.buffer.as_ref()[1]) - } - pub fn checksum(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap()) - } - pub fn is_valid(&self) -> bool { - self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0 - } - pub fn header_other(&self) -> HeaderOther { - match self.kind() { - Kind::EchoReply - | Kind::EchoRequest - | Kind::TimestampRequest - | Kind::TimestampReply - | Kind::InformationRequest - | Kind::InformationReply => { - let ide = u16::from_be_bytes(self.buffer.as_ref()[4..6].try_into().unwrap()); - let seq = u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap()); - HeaderOther::Identifier(ide, seq) - } - Kind::DestinationUnreachable | Kind::TimeExceeded | Kind::SourceQuench => { - let bytes = self.buffer.as_ref(); - HeaderOther::Unused(bytes[4], bytes[5], bytes[6], bytes[7]) - } - Kind::Redirect => { - let bytes = self.buffer.as_ref(); - HeaderOther::Address(bytes[4], bytes[5], bytes[6], bytes[7]) - } - Kind::ParameterProblem => HeaderOther::Pointer(self.buffer.as_ref()[4]), - _ => { - let bytes = self.buffer.as_ref(); - HeaderOther::UnKnown(bytes[4], bytes[5], bytes[6], bytes[7]) - } - } - } - pub fn payload(&self) -> &[u8] { - &self.buffer.as_ref()[8..] - } - pub fn description(&self) -> Description<&[u8]> { - use std::io::Cursor; - match self.kind() { - Kind::DestinationUnreachable - | Kind::TimeExceeded - | Kind::ParameterProblem - | Kind::SourceQuench - | Kind::Redirect => match IpV4Packet::new(self.payload()) { - Ok(d) => Description::Ip(d), - Err(_) => Description::Other(self.payload()), - }, - Kind::TimestampRequest | Kind::TimestampReply => { - let mut buffer = Cursor::new(self.payload()); - - Description::Timestamp( - buffer.read_u32::().unwrap(), - buffer.read_u32::().unwrap(), - buffer.read_u32::().unwrap(), - ) - } - _ => Description::Other(self.payload()), - } - } -} - -impl> fmt::Debug for IcmpPacket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct(if self.is_valid() { - "icmp::Packet" - } else { - "icmp::Packet!" - }) - .field("kind", &self.kind()) - .field("code", &self.code()) - .field("checksum", &self.checksum()) - .field("payload", &self.payload()) - .finish() - } -} - -#[derive(Debug)] -pub enum HeaderOther { - /// 全零 - Unused(u8, u8, u8, u8), - /// If code = 0, identifies the octet where an error was detected. - Pointer(u8), - /// Address of the gateway to which traffic for the network specified - /// in the internet destination network field of the original - /// datagram's data should be sent. - Address(u8, u8, u8, u8), - /// Identifier | Sequence Number - Identifier(u16, u16), - UnKnown(u8, u8, u8, u8), -} - -pub enum Description { - Ip(IpV4Packet), - ///时间戳 Originate Timestamp,Receive Timestamp,Transmit Timestamp - Timestamp(u32, u32, u32), - Other(B), -} - -impl + std::fmt::Debug> fmt::Debug for Description { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Description::Ip(packet) => f.debug_struct(&format!("{:?}", packet)).finish(), - Description::Timestamp(originate, receive, transmit) => f - .debug_struct("") - .field("originate", originate) - .field("receive", receive) - .field("transmit", transmit) - .finish(), - Description::Other(bytes) => f.debug_struct(&format!("{:?}", bytes)).finish(), - } - } -} diff --git a/vnt/packet/src/icmp/mod.rs b/vnt/packet/src/icmp/mod.rs deleted file mode 100644 index dc9c214..0000000 --- a/vnt/packet/src/icmp/mod.rs +++ /dev/null @@ -1,414 +0,0 @@ -pub mod icmp; - -#[derive(Eq, PartialEq, Copy, Clone, Debug)] -pub enum Kind { - /// ping应答,type=0 - /* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Code | Checksum | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Identifier | Sequence Number | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Data ... - +-+-+-+-+- - */ - EchoReply, - /// 目的地不可达,差错报文的一种,路由器收到一个不能转发的数据报,会向源地址返回这个报文,type=3 - /* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Code | Checksum | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | unused | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Internet Header + 64 bits of Original Data Datagram | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - DestinationUnreachable, - /// 源抑制报文,用于防止接收端缓存溢出,接收设备发送这个来请求源设备降低发送速度,type=4 - /* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Code | Checksum | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | unused | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Internet Header + 64 bits of Original Data Datagram | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - SourceQuench, - /// 重定向报文,当路由器接收包的接口正好是去往目的地的出口时,会向源地址发送重定向报文,告知源直接将数据发往自己的下一跳,type=5 - /* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Code | Checksum | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Gateway Internet Address | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Internet Header + 64 bits of Original Data Datagram | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - Redirect, - /// ping请求,type=8 - EchoRequest, - /// 路由器通告,type=9, - RouterAdvertisement, - /// 路由器请求,type=10 - RouterSolicitation, - /// 报文ttl为0后,路由器会向源发送此报文,type=11 - /// Tracert工作原理: - /// 首先向目的地发送ttl=1的包,下一跳路由器收到后ttl-1,此时ttl=0,将向源发送 ICMP time exceeded - /// 再发送ttl=2的包,以此类推,直到目标主机接收到改包,此时不会回复ICMP time exceeded,代表已经探测到目的地 - /* - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Code | Checksum | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | unused | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Internet Header + 64 bits of Original Data Datagram | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - TimeExceeded, - /// 参数错误,数据有误、校验和不对等,type=12 - /* - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Code | Checksum | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Pointer | unused | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Internet Header + 64 bits of Original Data Datagram | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - 注:Pointer指示错误的位置 - */ - ParameterProblem, - /// 时间戳请求,type=13 - /* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Code | Checksum | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Identifier | Sequence Number | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Originate Timestamp | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Receive Timestamp | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Transmit Timestamp | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - TimestampRequest, - /// 时间戳响应,type=14 - TimestampReply, - /// 信息请求,type=15 - /* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Code | Checksum | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Identifier | Sequence Number | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - InformationRequest, - /// 信息响应,type=16 - InformationReply, - /// 地址掩码请求,type=17 - AddressMaskRequest, - /// 地址掩码应答,type=18 - AddressMaskReply, - /// - TraceRoute, - /// - Unknown(u8), -} - -impl From for Kind { - fn from(value: u8) -> Kind { - use self::Kind::*; - - match value { - 0 => EchoReply, - 3 => DestinationUnreachable, - 4 => SourceQuench, - 5 => Redirect, - 8 => EchoRequest, - 9 => RouterAdvertisement, - 10 => RouterSolicitation, - 11 => TimeExceeded, - 12 => ParameterProblem, - 13 => TimestampRequest, - 14 => TimestampReply, - 15 => InformationRequest, - 16 => InformationReply, - 17 => AddressMaskRequest, - 18 => AddressMaskReply, - 30 => TraceRoute, - v => Unknown(v), - } - } -} - -impl Into for Kind { - fn into(self) -> u8 { - use self::Kind::*; - match self { - EchoReply => 0, - DestinationUnreachable => 3, - SourceQuench => 4, - Redirect => 5, - EchoRequest => 8, - RouterAdvertisement => 9, - RouterSolicitation => 10, - TimeExceeded => 11, - ParameterProblem => 12, - TimestampRequest => 13, - TimestampReply => 14, - InformationRequest => 15, - InformationReply => 16, - AddressMaskRequest => 17, - AddressMaskReply => 18, - TraceRoute => 30, - Unknown(v) => v, - } - } -} - -#[derive(Eq, PartialEq, Copy, Clone, Debug)] -pub enum Code { - DestinationUnreachable(DestinationUnreachable), - Redirect(Redirect), - ParameterProblem(ParameterProblem), - Other(u8), -} - -impl Code { - pub fn from(kind: Kind, code: u8) -> Code { - match kind { - Kind::DestinationUnreachable => { - Code::DestinationUnreachable(DestinationUnreachable::from(code)) - } - Kind::Redirect => Code::Redirect(Redirect::from(code)), - Kind::ParameterProblem => Code::ParameterProblem(ParameterProblem::from(code)), - _ => Code::Other(code), - } - } -} - -#[derive(Eq, PartialEq, Copy, Clone, Debug)] -pub enum DestinationUnreachable { - /// 网络不可达 - DestinationNetworkUnreachable, - /// 主机不可达 - DestinationHostUnreachable, - /// 协议不可达 - DestinationProtocolUnreachable, - /// 端口不可达 - DestinationPortUnreachable, - /// 需要进行分片但设置不分片比特 - FragmentationRequired, - /// 源站选路失败 - SourceRouteFailed, - /// 目的网络未知 - DestinationNetworkUnknown, - /// 目的主机未知 - DestinationHostUnknown, - /// 源主机被隔离(作废不用) - SourceHostIsolated, - /// 目的网络被强制禁止 - NetworkAdministrativelyProhibited, - /// 目的主机被强制禁止 - HostAdministrativelyProhibited, - /// 由于服务类型TOS,网络不可达 - NetworkUnreachableForTos, - /// 由于服务类型TOS,主机不可达 - HostUnreachableForTos, - /// 由于过滤,通信被强制禁止 - CommunicationAdministrativelyProhibited, - /// 主机越权 - HostPrecedenceViolation, - /// 优先中止生效 - PrecedentCutoffInEffect, - /// - Unknown(u8), -} - -/// Codes for Redirect Message packets. -#[derive(Eq, PartialEq, Copy, Clone, Debug)] -pub enum Redirect { - /// 对网络重定向 - RedirectDatagramForNetwork, - /// 对主机重定向 - RedirectDatagramForHost, - /// 对服务类型和网络重定向 - RedirectDatagramForTosAndNetwork, - /// 对服务类型和主机重定向 - RedirectDatagramForTosAndHost, - /// - Unknown(u8), -} - -/// Codes for TimeExceeded Message packets. -#[derive(Eq, PartialEq, Copy, Clone, Debug)] -pub enum TimeExceeded { - /// TTL超时报文 - Transit, - /// 分片重组超时报文 - Reassembly, - /// - Unknown(u8), -} -/// Codes for Parameter Problem packets. -#[derive(Eq, PartialEq, Copy, Clone, Debug)] -pub enum ParameterProblem { - /// 坏的IP首部(包括各种差错) - PointerIndicatesError, - /// 缺少必需的选项 - MissingRequiredData, - /// 长度错误 - BadLength, - /// - Unknown(u8), -} - -impl From for DestinationUnreachable { - fn from(value: u8) -> Self { - use self::DestinationUnreachable::*; - - match value { - 0 => DestinationNetworkUnreachable, - 1 => DestinationHostUnreachable, - 2 => DestinationProtocolUnreachable, - 3 => DestinationPortUnreachable, - 4 => FragmentationRequired, - 5 => SourceRouteFailed, - 6 => DestinationNetworkUnknown, - 7 => DestinationHostUnknown, - 8 => SourceHostIsolated, - 9 => NetworkAdministrativelyProhibited, - 10 => HostAdministrativelyProhibited, - 11 => NetworkUnreachableForTos, - 12 => HostUnreachableForTos, - 13 => CommunicationAdministrativelyProhibited, - 14 => HostPrecedenceViolation, - 15 => PrecedentCutoffInEffect, - v => Unknown(v), - } - } -} - -impl Into for DestinationUnreachable { - fn into(self) -> u8 { - use self::DestinationUnreachable::*; - - match self { - DestinationNetworkUnreachable => 0, - DestinationHostUnreachable => 1, - DestinationProtocolUnreachable => 2, - DestinationPortUnreachable => 3, - FragmentationRequired => 4, - SourceRouteFailed => 5, - DestinationNetworkUnknown => 6, - DestinationHostUnknown => 7, - SourceHostIsolated => 8, - NetworkAdministrativelyProhibited => 9, - HostAdministrativelyProhibited => 10, - NetworkUnreachableForTos => 11, - HostUnreachableForTos => 12, - CommunicationAdministrativelyProhibited => 13, - HostPrecedenceViolation => 14, - PrecedentCutoffInEffect => 15, - Unknown(v) => v, - } - } -} - -impl From for Redirect { - fn from(value: u8) -> Self { - use self::Redirect::*; - - match value { - 0 => RedirectDatagramForNetwork, - 1 => RedirectDatagramForHost, - 2 => RedirectDatagramForTosAndNetwork, - 3 => RedirectDatagramForTosAndHost, - v => Unknown(v), - } - } -} - -impl Into for Redirect { - fn into(self) -> u8 { - use self::Redirect::*; - - match self { - RedirectDatagramForNetwork => 0, - RedirectDatagramForHost => 1, - RedirectDatagramForTosAndNetwork => 2, - RedirectDatagramForTosAndHost => 3, - Unknown(v) => v, - } - } -} - -impl From for TimeExceeded { - fn from(value: u8) -> Self { - use self::TimeExceeded::*; - - match value { - 0 => Transit, - 1 => Reassembly, - v => Unknown(v), - } - } -} - -impl Into for TimeExceeded { - fn into(self) -> u8 { - use self::TimeExceeded::*; - - match self { - Transit => 0, - Reassembly => 1, - Unknown(v) => v, - } - } -} - -impl From for ParameterProblem { - fn from(value: u8) -> Self { - use self::ParameterProblem::*; - - match value { - 0 => PointerIndicatesError, - 1 => MissingRequiredData, - 2 => BadLength, - v => Unknown(v), - } - } -} - -impl Into for ParameterProblem { - fn into(self) -> u8 { - use self::ParameterProblem::*; - - match self { - PointerIndicatesError => 0, - MissingRequiredData => 1, - BadLength => 2, - Unknown(v) => v, - } - } -} diff --git a/vnt/packet/src/igmp/igmp_v1.rs b/vnt/packet/src/igmp/igmp_v1.rs deleted file mode 100644 index 58617f6..0000000 --- a/vnt/packet/src/igmp/igmp_v1.rs +++ /dev/null @@ -1,117 +0,0 @@ -use crate::cal_checksum; -use std::net::Ipv4Addr; -use std::{fmt, io}; - -/// igmp v1 -/* https://datatracker.ietf.org/doc/html/rfc1112 - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |Version| Type | Unused | Checksum | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Group Address | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -*/ -/// v1版本的报文 -pub struct IgmpV1Packet { - pub buffer: B, -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum IgmpV1Type { - /// 0x11 所有组224.0.0.1或者特定组 - Query, - /// 0x12 - ReportV1, - Unknown(u8), -} - -impl From for IgmpV1Type { - fn from(value: u8) -> IgmpV1Type { - use self::IgmpV1Type::*; - - match value { - 0x11 => Query, - 0x12 => ReportV1, - v => Unknown(v), - } - } -} - -impl Into for IgmpV1Type { - fn into(self) -> u8 { - match self { - IgmpV1Type::Query => 0x11, - IgmpV1Type::ReportV1 => 0x12, - IgmpV1Type::Unknown(v) => v, - } - } -} - -impl> IgmpV1Packet { - pub fn unchecked(buffer: B) -> Self { - Self { buffer } - } - pub fn new(buffer: B) -> io::Result { - if buffer.as_ref().len() != 8 { - Err(io::Error::from(io::ErrorKind::InvalidData)) - } else { - let packet = Self::unchecked(buffer); - Ok(packet) - } - } -} - -impl> IgmpV1Packet { - pub fn version(&self) -> u8 { - self.buffer.as_ref()[0] >> 4 - } - pub fn igmp_type(&self) -> IgmpV1Type { - IgmpV1Type::from(self.buffer.as_ref()[0] & 0x0F) - } - pub fn unused(&self) -> u8 { - self.buffer.as_ref()[1] - } - pub fn checksum(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap()) - } - pub fn is_valid(&self) -> bool { - self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0 - } - pub fn group_address(&self) -> Ipv4Addr { - let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap(); - Ipv4Addr::from(tmp) - } -} - -impl + AsMut<[u8]>> IgmpV1Packet { - pub fn set_version(&mut self, version: u8) { - self.buffer.as_mut()[0] = (version << 4) | 0x0F & self.buffer.as_mut()[0] - } - pub fn set_type(&mut self, igmp_type: IgmpV1Type) { - let t: u8 = igmp_type.into(); - self.buffer.as_mut()[0] = self.buffer.as_mut()[0] & 0xF0 | t - } - pub fn set_checksum(&mut self, checksum: u16) { - self.buffer.as_mut()[2..4].copy_from_slice(&checksum.to_be_bytes()); - } - pub fn update_checksum(&mut self) { - self.set_checksum(0); - self.set_checksum(cal_checksum(self.buffer.as_ref())); - } - pub fn set_group_address(&mut self, group_address: Ipv4Addr) { - self.buffer.as_mut()[4..8].copy_from_slice(&group_address.octets()); - } -} - -impl> fmt::Debug for IgmpV1Packet { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("igmp::V1") - .field("version", &self.version()) - .field("type", &self.igmp_type()) - .field("checksum", &self.checksum()) - .field("is_valid", &self.is_valid()) - .field("group_address", &self.group_address()) - .finish() - } -} diff --git a/vnt/packet/src/igmp/igmp_v2.rs b/vnt/packet/src/igmp/igmp_v2.rs deleted file mode 100644 index 13e65cd..0000000 --- a/vnt/packet/src/igmp/igmp_v2.rs +++ /dev/null @@ -1,118 +0,0 @@ -use crate::cal_checksum; -use std::net::Ipv4Addr; -use std::{fmt, io}; - -/// igmp v2 -/* https://www.rfc-editor.org/rfc/rfc2236.html - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Max Resp Time | Checksum | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Group Address | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -*/ - -/// v2版本的报文 -pub struct IgmpV2Packet { - pub buffer: B, -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum IgmpV2Type { - /// 0x11 所有组224.0.0.1或者特定组 - Query, - /// 0x16 - ReportV2, - LeaveV2, - Unknown(u8), -} - -impl From for IgmpV2Type { - fn from(value: u8) -> IgmpV2Type { - use self::IgmpV2Type::*; - - match value { - 0x11 => Query, - 0x16 => ReportV2, - 0x17 => LeaveV2, - v => Unknown(v), - } - } -} - -impl Into for IgmpV2Type { - fn into(self) -> u8 { - match self { - IgmpV2Type::Query => 0x11, - IgmpV2Type::ReportV2 => 0x16, - IgmpV2Type::LeaveV2 => 0x17, - IgmpV2Type::Unknown(v) => v, - } - } -} - -impl> IgmpV2Packet { - pub fn unchecked(buffer: B) -> Self { - Self { buffer } - } - pub fn new(buffer: B) -> io::Result { - if buffer.as_ref().len() != 8 { - Err(io::Error::from(io::ErrorKind::InvalidData)) - } else { - let packet = Self::unchecked(buffer); - Ok(packet) - } - } -} - -impl> IgmpV2Packet { - pub fn igmp_type(&self) -> IgmpV2Type { - IgmpV2Type::from(self.buffer.as_ref()[0]) - } - pub fn max_resp_time(&self) -> u8 { - self.buffer.as_ref()[1] - } - pub fn checksum(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap()) - } - pub fn is_valid(&self) -> bool { - self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0 - } - pub fn group_address(&self) -> Ipv4Addr { - let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap(); - Ipv4Addr::from(tmp) - } -} - -impl + AsMut<[u8]>> IgmpV2Packet { - pub fn set_type(&mut self, igmp_type: IgmpV2Type) { - self.buffer.as_mut()[0] = igmp_type.into() - } - pub fn set_max_resp_time(&mut self, resp: u8) { - self.buffer.as_mut()[1] = resp - } - pub fn set_checksum(&mut self, checksum: u16) { - self.buffer.as_mut()[2..4].copy_from_slice(&checksum.to_be_bytes()); - } - pub fn update_checksum(&mut self) { - self.set_checksum(0); - self.set_checksum(cal_checksum(self.buffer.as_ref())); - } - pub fn set_group_address(&mut self, group_address: Ipv4Addr) { - self.buffer.as_mut()[4..8].copy_from_slice(&group_address.octets()); - } -} - -impl> fmt::Debug for IgmpV2Packet { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("igmp::V2") - .field("type", &self.igmp_type()) - .field("max_resp_time", &self.max_resp_time()) - .field("checksum", &self.checksum()) - .field("is_valid", &self.is_valid()) - .field("group_address", &self.group_address()) - .finish() - } -} diff --git a/vnt/packet/src/igmp/igmp_v3.rs b/vnt/packet/src/igmp/igmp_v3.rs deleted file mode 100644 index 0ec2df9..0000000 --- a/vnt/packet/src/igmp/igmp_v3.rs +++ /dev/null @@ -1,493 +0,0 @@ -use std::net::Ipv4Addr; -use std::{fmt, io}; - -use crate::cal_checksum; - -/// igmp v3 -/* https://www.rfc-editor.org/rfc/rfc3376 -Query: - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type = 0x11 | Max Resp Code | Checksum | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Group Address | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Resv |S| QRV | QQIC | Number of Sources (N) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Source Address [1] | - +- -+ - | Source Address [2] | - +- . -+ - . . . - . . . - +- -+ - | Source Address [N] | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - ------------------------------------------------------------------------------ - -Report: - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type = 0x22 | Reserved | Checksum | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Reserved | Number of Group Records (M) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - . . - . Group Record [1] . - . . - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - . . - . Group Record [2] . - . . - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | . | - . . . - | . | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - . . - . Group Record [M] . - . . - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - -Group Record: - - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Record Type | Aux Data Len | Number of Sources (N) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Multicast Address | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Source Address [1] | - +- -+ - | Source Address [2] | - +- -+ - . . . - . . . - . . . - +- -+ - | Source Address [N] | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - . . - . Auxiliary Data . - . . - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - -Record Type: - 1 MODE_IS_INCLUDE 表示主机希望加入指定组播组并指定了一个或多个源地址 - 2 MODE_IS_EXCLUDE 表示主机希望加入指定组播组但排除了一个或多个源地址 - 3 CHANGE_TO_INCLUDE_MODE 表示主机正在将组播组的过滤模式从排除切换为包括,指定了一个或多个源地址 - 4 CHANGE_TO_EXCLUDE_MODE 表示主机正在将组播组的过滤模式从包括切换为排除,指定了一个或多个源地址 - 5 ALLOW_NEW_SOURCES 表示主机希望在已有的源地址列表中添加新的源地址,指定了一个或多个源地址 - 6 BLOCK_OLD_SOURCES 表示主机希望在已有的源地址列表中删除旧的源地址,指定了一个或多个源地址 - */ -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum IgmpV3Type { - /// 0x11 所有组224.0.0.1或者特定组 - Query, - /// 0x22 - ReportV3, - Unknown(u8), -} - -impl From for IgmpV3Type { - fn from(value: u8) -> IgmpV3Type { - use self::IgmpV3Type::*; - - match value { - 0x11 => Query, - 0x22 => ReportV3, - v => Unknown(v), - } - } -} - -impl Into for IgmpV3Type { - fn into(self) -> u8 { - match self { - IgmpV3Type::Query => 0x11, - IgmpV3Type::ReportV3 => 0x22, - IgmpV3Type::Unknown(v) => v, - } - } -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum IgmpV3RecordType { - //1 MODE_IS_INCLUDE 表示主机希望加入指定组播组并指定了一个或多个源地址 - ModeIsInclude, - //2 MODE_IS_EXCLUDE 表示主机希望加入指定组播组但排除了一个或多个源地址 - ModeIsExclude, - //3 CHANGE_TO_INCLUDE_MODE 表示主机正在将组播组的过滤模式从排除切换为包括,指定了一个或多个源地址 - ChangeToIncludeMode, - //4 CHANGE_TO_EXCLUDE_MODE 表示主机正在将组播组的过滤模式从包括切换为排除,指定了一个或多个源地址 - ChangeToExcludeMode, - //5 ALLOW_NEW_SOURCES 表示主机希望在已有的源地址列表中添加新的源地址,指定了一个或多个源地址 - AllowNewSources, - //6 BLOCK_OLD_SOURCES 表示主机希望在已有的源地址列表中删除旧的源地址,指定了一个或多个源地址 - BlockOldSources, - Unknown(u8), -} - -impl From for IgmpV3RecordType { - fn from(value: u8) -> IgmpV3RecordType { - use self::IgmpV3RecordType::*; - - match value { - 1 => ModeIsInclude, - 2 => ModeIsExclude, - 3 => ChangeToIncludeMode, - 4 => ChangeToExcludeMode, - 5 => AllowNewSources, - 6 => BlockOldSources, - v => Unknown(v), - } - } -} - -impl Into for IgmpV3RecordType { - fn into(self) -> u8 { - use self::IgmpV3RecordType::*; - - match self { - ModeIsInclude => 1, - ModeIsExclude => 2, - ChangeToIncludeMode => 3, - ChangeToExcludeMode => 4, - AllowNewSources => 5, - BlockOldSources => 6, - Unknown(v) => v, - } - } -} - -/// v3版本的query报文 -pub struct IgmpV3QueryPacket { - pub buffer: B, -} - -impl> IgmpV3QueryPacket { - pub fn unchecked(buffer: B) -> Self { - Self { buffer } - } - pub fn new(buffer: B) -> io::Result { - if buffer.as_ref().len() < 12 { - Err(io::Error::from(io::ErrorKind::InvalidData)) - } else { - let packet = Self::unchecked(buffer); - Ok(packet) - } - } -} - -impl + AsMut<[u8]>> IgmpV3QueryPacket { - pub fn set_igmp_type(&mut self) { - self.buffer.as_mut()[0] = IgmpV3Type::Query.into(); - } - pub fn set_max_resp_code(&mut self, code: u8) { - self.buffer.as_mut()[1] = code; - } - pub fn set_group_address(&mut self, addr: Ipv4Addr) { - self.buffer.as_mut()[4..8].copy_from_slice(&addr.octets()) - } - pub fn set_checksum(&mut self, checksum: u16) { - self.buffer.as_mut()[2..4].copy_from_slice(&checksum.to_be_bytes()) - } - pub fn set_qrv(&mut self, qrv: u8) { - self.buffer.as_mut()[8] = (self.buffer.as_ref()[8] & (!0x07)) | (qrv & 0x07) - } - pub fn set_qqic(&mut self, qqic: u8) { - self.buffer.as_mut()[9] = qqic - } - - pub fn update_checksum(&mut self) { - self.set_checksum(0); - let checksum = cal_checksum(self.buffer.as_ref()); - self.set_checksum(checksum); - } -} - -impl> IgmpV3QueryPacket { - pub fn igmp_type(&self) -> IgmpV3Type { - IgmpV3Type::from(self.buffer.as_ref()[0]) - } - pub fn max_resp_code(&self) -> u8 { - self.buffer.as_ref()[1] - } - pub fn checksum(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap()) - } - pub fn is_valid(&self) -> bool { - self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0 - } - pub fn group_address(&self) -> Ipv4Addr { - let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap(); - Ipv4Addr::from(tmp) - } - /// 保留字段,设置为0 - pub fn resv(&self) -> u8 { - self.buffer.as_ref()[8] >> 4 - } - /// 标志位 - /// 该比特位为1时,所有收到此查询报文的其他路由器不启动定时器刷新过程,但是此查询报文并不抑制查询者选举过程和路由器的主机侧处理过程;默认未置位。 - pub fn s(&self) -> u8 { - (self.buffer.as_ref()[8] & 0x0F) >> 3 - } - /// 查询者向网络通告的健壮系数 - /// 此参数可使查询者使用自己的健壮系统同步其他组播路由器的健壮系数; - /// 其他路由器接收到查询报文时,如果发现该字段非0,则将自己的健壮系数调整为该字段的值;如果发现该字段为0,则不做处理。默认健壮系数值为2。 - pub fn qrv(&self) -> u8 { - self.buffer.as_ref()[8] & 0x07 - } - /// IGMP查询者的查询间隔 - /// 非查询者收到查询报文时,如果发现该字段非0,则将自己的查询间隔参数调整为该字段的值:如果发现该字段为0,则不做处理。默认值为60。 - pub fn qqic(&self) -> u8 { - self.buffer.as_ref()[9] - } - /// 报文中包含的组播源的数量 - /// 对于普遍组查询报文和特定组查询报文,该字段为0;对于特定源组查询报文,该字段非0 - pub fn source_number(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[10..12].try_into().unwrap()) - } - pub fn source_addresses(&self) -> Option> { - let num = self.source_number(); - if num == 0 { - None - } else { - let num = num as usize; - let mut list = Vec::with_capacity(num); - let buf = self.buffer.as_ref(); - let len = buf.len(); - for index in 0..num { - let start = (12 + index * 4) as usize; - let end = start + 4; - if end > len { - return None; - } - let tmp: [u8; 4] = buf[start..end].try_into().unwrap(); - list.push(Ipv4Addr::from(tmp)); - } - Some(list) - } - } - pub fn source_address(&self, index: u16) -> Option { - if self.source_number() >= index { - None - } else { - let start = (12 + index * 4) as usize; - let end = start + 4; - let buf = self.buffer.as_ref(); - let len = buf.len(); - if end > len { - return None; - } - let tmp: [u8; 4] = buf[start..end].try_into().unwrap(); - Some(Ipv4Addr::from(tmp)) - } - } -} - -/// v3版本的query报文 -pub struct IgmpV3ReportPacket { - pub buffer: B, -} - -impl> IgmpV3ReportPacket { - pub fn unchecked(buffer: B) -> Self { - Self { buffer } - } - pub fn new(buffer: B) -> io::Result { - if buffer.as_ref().len() < 8 { - Err(io::Error::from(io::ErrorKind::InvalidData)) - } else { - let packet = Self::unchecked(buffer); - Ok(packet) - } - } -} - -impl> IgmpV3ReportPacket { - pub fn igmp_type(&self) -> IgmpV3Type { - IgmpV3Type::from(self.buffer.as_ref()[0]) - } - pub fn reserved1(&self) -> u8 { - self.buffer.as_ref()[1] - } - pub fn checksum(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap()) - } - pub fn is_valid(&self) -> bool { - self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0 - } - pub fn reserved2(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[4..6].try_into().unwrap()) - } - pub fn record_number(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap()) - } - pub fn group_records(&self) -> Option>> { - let num = self.record_number(); - if num == 0 { - None - } else { - let num = num as usize; - let mut list = Vec::with_capacity(num); - let mut start = 8 as usize; - let buf = self.buffer.as_ref(); - let len = buf.len(); - for _ in 0..num { - if start >= len { - return None; - } - if let Ok(record) = IgmpV3RecordPacket::new(&buf[start..]) { - let end = start - + 8 - + record.aux_data_len() as usize * 4 - + record.source_number() as usize * 4; - if end > len { - return None; - } - list.push(IgmpV3RecordPacket::new(&buf[start..end]).unwrap()); - start = end; - } else { - return None; - } - } - Some(list) - } - } -} - -/// group record -pub struct IgmpV3RecordPacket { - pub buffer: B, -} - -impl> IgmpV3RecordPacket { - pub fn unchecked(buffer: B) -> Self { - Self { buffer } - } - pub fn new(buffer: B) -> io::Result { - if buffer.as_ref().len() < 8 { - Err(io::Error::from(io::ErrorKind::InvalidData)) - } else { - let packet = Self::unchecked(buffer); - Ok(packet) - } - } -} - -impl> IgmpV3RecordPacket { - pub fn record_type(&self) -> IgmpV3RecordType { - IgmpV3RecordType::from(self.buffer.as_ref()[0]) - } - /// 辅助数据长度 以4字节为单位 - pub fn aux_data_len(&self) -> u8 { - self.buffer.as_ref()[1] - } - /// 源地址数 - pub fn source_number(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap()) - } - ///多播地址 - pub fn multicast_address(&self) -> Ipv4Addr { - let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap(); - Ipv4Addr::from(tmp) - } - pub fn source_addresses(&self) -> Option> { - let num = self.source_number(); - if num == 0 { - None - } else { - let num = num as usize; - let mut list = Vec::with_capacity(num); - let buf = self.buffer.as_ref(); - let len = buf.len(); - for index in 0..num { - let start = (8 + index * 4) as usize; - let end = start + 4; - if end > len { - return None; - } - let tmp: [u8; 4] = buf[start..end].try_into().unwrap(); - list.push(Ipv4Addr::from(tmp)); - } - Some(list) - } - } - pub fn source_address(&self, index: u16) -> Option { - if self.source_number() >= index { - None - } else { - let start = (8 + index * 4) as usize; - let end = start + 4; - if end > self.buffer.as_ref().len() { - return None; - } - let tmp: [u8; 4] = self.buffer.as_ref()[start..end].try_into().unwrap(); - Some(Ipv4Addr::from(tmp)) - } - } - /// 在文档中没有定义辅助数据的作用,通常应该是空的 - pub fn auxiliary_data(&self) -> &[u8] { - let start = 8 + self.source_number() as usize * 4; - let end = start + self.aux_data_len() as usize * 4; - if end > self.buffer.as_ref().len() { - return &[]; - } - &self.buffer.as_ref()[start..end] - } -} - -impl> fmt::Debug for IgmpV3QueryPacket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("igmp::V3Query") - .field("type", &self.igmp_type()) - .field("max_resp_code", &self.max_resp_code()) - .field("checksum", &self.checksum()) - .field("is_valid", &self.is_valid()) - .field("group_address", &self.group_address()) - .field("s", &self.s()) - .field("qrv", &self.qrv()) - .field("qqic", &self.qqic()) - .field("number of sources", &self.source_number()) - .field("source_addresses", &self.source_addresses()) - .finish() - } -} - -impl> fmt::Debug for IgmpV3ReportPacket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("igmp::V3Report") - .field("type", &self.igmp_type()) - .field("reserved1", &self.reserved1()) - .field("checksum", &self.checksum()) - .field("is_valid", &self.is_valid()) - .field("reserved2", &self.reserved2()) - .field("record_number", &self.record_number()) - .field("group_records", &self.group_records()) - .finish() - } -} - -impl> fmt::Debug for IgmpV3RecordPacket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("igmp::V3Record") - .field("record_type", &self.record_type()) - .field("aux_data_len", &self.aux_data_len()) - .field("source_number", &self.source_number()) - .field("multicast_address", &self.multicast_address()) - .field("source_addresses", &self.source_addresses()) - .field("auxiliary_data", &self.auxiliary_data()) - .finish() - } -} diff --git a/vnt/packet/src/igmp/mod.rs b/vnt/packet/src/igmp/mod.rs deleted file mode 100644 index 113ab19..0000000 --- a/vnt/packet/src/igmp/mod.rs +++ /dev/null @@ -1,46 +0,0 @@ -pub mod igmp_v1; -pub mod igmp_v2; -pub mod igmp_v3; - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum IgmpType { - /// 0x11 所有组224.0.0.1或者特定组 - Query, - /// 0x12 - ReportV1, - /// 0x16 - ReportV2, - /// 0x22 - ReportV3, - /// 0x17 目标组固定是 224.0.0.2 - LeaveV2, - Unknown(u8), -} - -impl From for IgmpType { - fn from(value: u8) -> IgmpType { - use self::IgmpType::*; - - match value { - 0x11 => Query, - 0x12 => ReportV1, - 0x16 => ReportV2, - 0x22 => ReportV3, - 0x17 => LeaveV2, - v => Unknown(v), - } - } -} - -impl Into for IgmpType { - fn into(self) -> u8 { - match self { - IgmpType::Query => 0x11, - IgmpType::ReportV1 => 0x12, - IgmpType::ReportV2 => 0x16, - IgmpType::ReportV3 => 0x22, - IgmpType::LeaveV2 => 0x17, - IgmpType::Unknown(v) => v, - } - } -} diff --git a/vnt/packet/src/ip/ipv4/mod.rs b/vnt/packet/src/ip/ipv4/mod.rs deleted file mode 100644 index c9cf115..0000000 --- a/vnt/packet/src/ip/ipv4/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod packet; -pub mod protocol; diff --git a/vnt/packet/src/ip/ipv4/packet.rs b/vnt/packet/src/ip/ipv4/packet.rs deleted file mode 100644 index 5a41021..0000000 --- a/vnt/packet/src/ip/ipv4/packet.rs +++ /dev/null @@ -1,235 +0,0 @@ -use std::net::Ipv4Addr; -use std::{fmt, io}; - -use crate::cal_checksum; -use crate::ip::ipv4::protocol::Protocol; - -/// ip协议 -/* -RFC: 791 https://www.ietf.org/rfc/rfc791.txt - - 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 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 版本(4) | 头部长度(4) | 服务类型(8) | 总字节数(16) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 标识(16) | 标志(3) | 片偏移(13) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 生存时间(8) | 协议(8) | 头部校验和(16) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 源ip地址(32) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 目的ip地址(32) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 选项 + 填充 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - 数据体 - 注:头部长度单位是4字节,所以ip头最长60字节,选项最长40字节,选项填充按4字节对齐 -*/ - -pub struct IpV4Packet { - pub buffer: B, -} - -impl> IpV4Packet { - pub fn unchecked(buffer: B) -> Self { - Self { buffer } - } - pub fn new(buffer: B) -> io::Result { - if buffer.as_ref().len() < 20 { - Err(io::Error::new(io::ErrorKind::InvalidData, "len < 20"))?; - } - if buffer.as_ref()[0] >> 4 != 4 { - Err(io::Error::new(io::ErrorKind::InvalidData, "not ipv4"))?; - } - let packet = Self::unchecked(buffer); - if packet.buffer.as_ref().len() < packet.header_len() as usize * 4 { - Err(io::Error::new(io::ErrorKind::InvalidData, "head_len err"))?; - } - Ok(packet) - } -} - -impl> IpV4Packet { - pub fn header(&self) -> &[u8] { - &self.buffer.as_ref()[..(self.header_len() as usize * 4)] - } - pub fn payload(&self) -> &[u8] { - &self.buffer.as_ref()[(self.header_len() as usize * 4)..] - } -} - -impl + AsMut<[u8]>> IpV4Packet { - pub fn header_mut(&mut self) -> &mut [u8] { - let len = self.header_len() as usize * 4; - &mut self.buffer.as_mut()[..len] - } - pub fn payload_mut(&mut self) -> &mut [u8] { - let len = self.header_len() as usize * 4; - &mut self.buffer.as_mut()[len..] - } - pub fn set_protocol(&mut self, value: Protocol) { - self.header_mut()[9] = value.into(); - } - pub fn set_source_ip(&mut self, value: Ipv4Addr) { - self.header_mut()[12..16].copy_from_slice(&value.octets()); - } - pub fn set_destination_ip(&mut self, value: Ipv4Addr) { - self.header_mut()[16..20].copy_from_slice(&value.octets()); - } - pub fn set_flags(&mut self, flags: u8) { - self.buffer.as_mut()[6] = (self.buffer.as_ref()[6] & 0b11100000) | (flags << 5) - } - pub fn set_ttl(&mut self, ttl: u8) { - self.buffer.as_mut()[8] = ttl - } - fn set_checksum(&mut self, value: u16) { - self.header_mut()[10..12].copy_from_slice(&value.to_be_bytes()) - } - /// 更新校验和 - pub fn update_checksum(&mut self) { - //先将校验和置0 - self.set_checksum(0); - self.set_checksum(cal_checksum(self.header())) - } -} - -impl> IpV4Packet { - /// 版本号,ipv4的为4 - pub fn version(&self) -> u8 { - self.buffer.as_ref()[0] >> 4 - } - - /// 头部长度,以4字节为单位 - pub fn header_len(&self) -> u8 { - self.buffer.as_ref()[0] & 0b1111 - } - - /// 差异化服务编码点 - /// - /// 类别(3)+丢失概率(2)+用途(1) - /// - /// - /// 类别子字段值 | 名称 - /// ---|:--- - /// 000 | 常规(Routine) - /// 001 | 优先(Priority) - /// 010 | 立即(Immediate) - /// 011 | 瞬间(Flash) - /// 100 | 瞬间覆盖(Flash Override) - /// 101 | 严重(CRITIC/ECP) - /// 110 | 网间控制(Internetwork Control) - /// 111 | 网络控制(Network Control) - /// - /// - /// 参考:https://www.modb.pro/db/477116 - pub fn dscp(&self) -> u8 { - self.buffer.as_ref()[1] >> 2 - } - - /// 显示拥塞 00:发送主机不支持ECN 01或者10:发送主机支持ECN 11:路由器正在经历拥塞 - pub fn ecn(&self) -> u8 { - self.buffer.as_ref()[1] & 0b11 - } - - /// ip报总字节数 - pub fn length(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap()) - } - - /// 标识. ip报文在数据链路层可能会被拆分,同一报文的不同分组标识字段相同 - pub fn id(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[4..6].try_into().unwrap()) - } - - /// 标志 3位. - /// 第1位没有使用 - /// 第2位表示不分段位(DF) - /// 0:允许数据报分段 - /// 1:数据报不能分段 - /// 置1之后路由器不能对其分段处理,如果超过MTU值则路由器不能对其转发将其丢弃,并向源点发送错误消息 - /// 第3位表示更多段位 - /// 0:数据包后面没有包,该包为最后的包 - /// 1:数据包后面有更多的包 - pub fn flags(&self) -> u8 { - self.buffer.as_ref()[6] >> 5 - } - - /// 片偏移 13位. - /// 以字节为单位,用于指明分段起始点相对于包头起始点的偏移量 - /// 由于分段到达时可能错序,所以分段的偏移字段可以使接收者按照正确的顺序重组数据包 - pub fn offset(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap()) & 0x1fff - } - - /// 生存时间. - /// 每一跳 减1 到0了则会被丢弃 - pub fn ttl(&self) -> u8 { - self.buffer.as_ref()[8] - } - - /// 协议. - pub fn protocol(&self) -> Protocol { - self.buffer.as_ref()[9].into() - } - - /// 首部校验和 - pub fn checksum(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[10..12].try_into().unwrap()) - } - /// 验证校验和 - /// - /// TCP/IP协议栈不会自己计算校验和,而是简单地将一个空的校验和字段(零或随机填充)交给网卡硬件。 - /// 所以抓到发出去的包校验和可能是错误的 - pub fn is_valid(&self) -> bool { - self.checksum() == 0 || cal_checksum(self.header()) == 0 - } - /// 源ip. - pub fn source_ip(&self) -> Ipv4Addr { - Ipv4Addr::new( - self.buffer.as_ref()[12], - self.buffer.as_ref()[13], - self.buffer.as_ref()[14], - self.buffer.as_ref()[15], - ) - } - - /// 目标ip. - pub fn destination_ip(&self) -> Ipv4Addr { - Ipv4Addr::new( - self.buffer.as_ref()[16], - self.buffer.as_ref()[17], - self.buffer.as_ref()[18], - self.buffer.as_ref()[19], - ) - } - - /// 选项. - pub fn options(&self) -> &[u8] { - &self.buffer.as_ref()[20..(self.header_len() as usize * 4)] - } -} - -impl> fmt::Debug for IpV4Packet { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ip::v4::Packet") - .field("version", &self.version()) - .field("header_len", &self.header_len()) - .field("dscp", &self.dscp()) - .field("ecn", &self.ecn()) - .field("length", &self.length()) - .field("id", &self.id()) - .field("flags", &self.flags()) - .field("offset", &self.offset()) - .field("ttl", &self.ttl()) - .field("protocol", &self.protocol()) - .field("checksum", &self.checksum()) - .field("is_valid", &self.is_valid()) - .field("source", &self.source_ip()) - .field("destination", &self.destination_ip()) - .field("options", &self.options()) - .field("payload", &self.payload()) - .finish() - } -} diff --git a/vnt/packet/src/ip/ipv4/protocol.rs b/vnt/packet/src/ip/ipv4/protocol.rs deleted file mode 100644 index 957fa08..0000000 --- a/vnt/packet/src/ip/ipv4/protocol.rs +++ /dev/null @@ -1,742 +0,0 @@ -#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Debug)] -pub enum Protocol { - /// - Hopopt, - - /// - Icmp, - - /// - Igmp, - - /// - Ggp, - - /// - Ipv4, - - /// - St, - - /// - Tcp, - - /// - Cbt, - - /// - Egp, - - /// - Igp, - - /// - BbnRccMon, - - /// - NvpII, - - /// - Pup, - - /// - Argus, - - /// - Emcon, - - /// - Xnet, - - /// - Chaos, - - /// - Udp, - - /// - Mux, - - /// - DcnMeas, - - /// - Hmp, - - /// - Prm, - - /// - XnsIdp, - - /// - Trunk1, - - /// - Trunk2, - - /// - Leaf1, - - /// - Leaf2, - - /// - Rdp, - - /// - Irtp, - - /// - IsoTp4, - - /// - Netblt, - - /// - MfeNsp, - - /// - MeritInp, - - /// - Dccp, - - /// - ThreePc, - - /// - Idpr, - - /// - Xtp, - - /// - Ddp, - - /// - IdprCmtp, - - /// - TpPlusPlus, - - /// - Il, - - /// - Ipv6, - - /// - Sdrp, - - /// - Ipv6Route, - - /// - Ipv6Frag, - - /// - Idrp, - - /// - Rsvp, - - /// - Gre, - - /// - Dsr, - - /// - Bna, - - /// - Esp, - - /// - Ah, - - /// - INlsp, - - /// - Swipe, - - /// - Narp, - - /// - Mobile, - - /// - Tlsp, - - /// - Skip, - - /// - Ipv6Icmp, - - /// - Ipv6NoNxt, - - /// - Ipv6Opts, - - /// - HostInternal, - - /// - Cftp, - - /// - LocalNetwork, - - /// - SatExpak, - - /// - Kryptolan, - - /// - Rvd, - - /// - Ippc, - - /// - DistributedFs, - - /// - SatMon, - - /// - Visa, - - /// - Ipcv, - - /// - Cpnx, - - /// - Cphb, - - /// - Wsn, - - /// - Pvp, - - /// - BrSatMon, - - /// - SunNd, - - /// - WbMon, - - /// - WbExpak, - - /// - IsoIp, - - /// - Vmtp, - - /// - SecureVmtp, - - /// - Vines, - - /// - TtpOrIptm, - - /// - NsfnetIgp, - - /// - Dgp, - - /// - Tcf, - - /// - Eigrp, - - /// - OspfigP, - - /// - SpriteRpc, - - /// - Larp, - - /// - Mtp, - - /// - Ax25, - - /// - IpIp, - - /// - Micp, - - /// - SccSp, - - /// - Etherip, - - /// - Encap, - - /// - PrivEncryption, - - /// - Gmtp, - - /// - Ifmp, - - /// - Pnni, - - /// - Pim, - - /// - Aris, - - /// - Scps, - - /// - Qnx, - - /// - AN, - - /// - IpComp, - - /// - Snp, - - /// - CompaqPeer, - - /// - IpxInIp, - - /// - Vrrp, - - /// - Pgm, - - /// - ZeroHop, - - /// - L2tp, - - /// - Ddx, - - /// - Iatp, - - /// - Stp, - - /// - Srp, - - /// - Uti, - - /// - Smp, - - /// - Sm, - - /// - Ptp, - - /// - IsisOverIpv4, - - /// - Fire, - - /// - Crtp, - - /// - Crudp, - - /// - Sscopmce, - - /// - Iplt, - - /// - Sps, - - /// - Pipe, - - /// - Sctp, - - /// - Fc, - - /// - RsvpE2eIgnore, - - /// - MobilityHeader, - - /// - UdpLite, - - /// - MplsInIp, - - /// - Manet, - - /// - Hip, - - /// - Shim6, - - /// - Wesp, - Rohc, - Test1, - Test2, - Unknown(u8), -} - -impl From for Protocol { - fn from(value: u8) -> Protocol { - use self::Protocol::*; - - match value { - 0 => Hopopt, - 1 => Icmp, - 2 => Igmp, - 3 => Ggp, - 4 => Ipv4, - 5 => St, - 6 => Tcp, - 7 => Cbt, - 8 => Egp, - 9 => Igp, - 10 => BbnRccMon, - 11 => NvpII, - 12 => Pup, - 13 => Argus, - 14 => Emcon, - 15 => Xnet, - 16 => Chaos, - 17 => Udp, - 18 => Mux, - 19 => DcnMeas, - 20 => Hmp, - 21 => Prm, - 22 => XnsIdp, - 23 => Trunk1, - 24 => Trunk2, - 25 => Leaf1, - 26 => Leaf2, - 27 => Rdp, - 28 => Irtp, - 29 => IsoTp4, - 30 => Netblt, - 31 => MfeNsp, - 32 => MeritInp, - 33 => Dccp, - 34 => ThreePc, - 35 => Idpr, - 36 => Xtp, - 37 => Ddp, - 38 => IdprCmtp, - 39 => TpPlusPlus, - 40 => Il, - 41 => Ipv6, - 42 => Sdrp, - 43 => Ipv6Route, - 44 => Ipv6Frag, - 45 => Idrp, - 46 => Rsvp, - 47 => Gre, - 48 => Dsr, - 49 => Bna, - 50 => Esp, - 51 => Ah, - 52 => INlsp, - 53 => Swipe, - 54 => Narp, - 55 => Mobile, - 56 => Tlsp, - 57 => Skip, - 58 => Ipv6Icmp, - 59 => Ipv6NoNxt, - 60 => Ipv6Opts, - 61 => HostInternal, - 62 => Cftp, - 63 => LocalNetwork, - 64 => SatExpak, - 65 => Kryptolan, - 66 => Rvd, - 67 => Ippc, - 68 => DistributedFs, - 69 => SatMon, - 70 => Visa, - 71 => Ipcv, - 72 => Cpnx, - 73 => Cphb, - 74 => Wsn, - 75 => Pvp, - 76 => BrSatMon, - 77 => SunNd, - 78 => WbMon, - 79 => WbExpak, - 80 => IsoIp, - 81 => Vmtp, - 82 => SecureVmtp, - 83 => Vines, - 84 => TtpOrIptm, - 85 => NsfnetIgp, - 86 => Dgp, - 87 => Tcf, - 88 => Eigrp, - 89 => OspfigP, - 90 => SpriteRpc, - 91 => Larp, - 92 => Mtp, - 93 => Ax25, - 94 => IpIp, - 95 => Micp, - 96 => SccSp, - 97 => Etherip, - 98 => Encap, - 99 => PrivEncryption, - 100 => Gmtp, - 101 => Ifmp, - 102 => Pnni, - 103 => Pim, - 104 => Aris, - 105 => Scps, - 106 => Qnx, - 107 => AN, - 108 => IpComp, - 109 => Snp, - 110 => CompaqPeer, - 111 => IpxInIp, - 112 => Vrrp, - 113 => Pgm, - 114 => ZeroHop, - 115 => L2tp, - 116 => Ddx, - 117 => Iatp, - 118 => Stp, - 119 => Srp, - 120 => Uti, - 121 => Smp, - 122 => Sm, - 123 => Ptp, - 124 => IsisOverIpv4, - 125 => Fire, - 126 => Crtp, - 127 => Crudp, - 128 => Sscopmce, - 129 => Iplt, - 130 => Sps, - 131 => Pipe, - 132 => Sctp, - 133 => Fc, - 134 => RsvpE2eIgnore, - 135 => MobilityHeader, - 136 => UdpLite, - 137 => MplsInIp, - 138 => Manet, - 139 => Hip, - 140 => Shim6, - 141 => Wesp, - 142 => Rohc, - 253 => Test1, - 254 => Test2, - p => Unknown(p), - } - } -} - -impl Into for Protocol { - fn into(self) -> u8 { - use self::Protocol::*; - - match self { - Hopopt => 0, - Icmp => 1, - Igmp => 2, - Ggp => 3, - Ipv4 => 4, - St => 5, - Tcp => 6, - Cbt => 7, - Egp => 8, - Igp => 9, - BbnRccMon => 10, - NvpII => 11, - Pup => 12, - Argus => 13, - Emcon => 14, - Xnet => 15, - Chaos => 16, - Udp => 17, - Mux => 18, - DcnMeas => 19, - Hmp => 20, - Prm => 21, - XnsIdp => 22, - Trunk1 => 23, - Trunk2 => 24, - Leaf1 => 25, - Leaf2 => 26, - Rdp => 27, - Irtp => 28, - IsoTp4 => 29, - Netblt => 30, - MfeNsp => 31, - MeritInp => 32, - Dccp => 33, - ThreePc => 34, - Idpr => 35, - Xtp => 36, - Ddp => 37, - IdprCmtp => 38, - TpPlusPlus => 39, - Il => 40, - Ipv6 => 41, - Sdrp => 42, - Ipv6Route => 43, - Ipv6Frag => 44, - Idrp => 45, - Rsvp => 46, - Gre => 47, - Dsr => 48, - Bna => 49, - Esp => 50, - Ah => 51, - INlsp => 52, - Swipe => 53, - Narp => 54, - Mobile => 55, - Tlsp => 56, - Skip => 57, - Ipv6Icmp => 58, - Ipv6NoNxt => 59, - Ipv6Opts => 60, - HostInternal => 61, - Cftp => 62, - LocalNetwork => 63, - SatExpak => 64, - Kryptolan => 65, - Rvd => 66, - Ippc => 67, - DistributedFs => 68, - SatMon => 69, - Visa => 70, - Ipcv => 71, - Cpnx => 72, - Cphb => 73, - Wsn => 74, - Pvp => 75, - BrSatMon => 76, - SunNd => 77, - WbMon => 78, - WbExpak => 79, - IsoIp => 80, - Vmtp => 81, - SecureVmtp => 82, - Vines => 83, - TtpOrIptm => 84, - NsfnetIgp => 85, - Dgp => 86, - Tcf => 87, - Eigrp => 88, - OspfigP => 89, - SpriteRpc => 90, - Larp => 91, - Mtp => 92, - Ax25 => 93, - IpIp => 94, - Micp => 95, - SccSp => 96, - Etherip => 97, - Encap => 98, - PrivEncryption => 99, - Gmtp => 100, - Ifmp => 101, - Pnni => 102, - Pim => 103, - Aris => 104, - Scps => 105, - Qnx => 106, - AN => 107, - IpComp => 108, - Snp => 109, - CompaqPeer => 110, - IpxInIp => 111, - Vrrp => 112, - Pgm => 113, - ZeroHop => 114, - L2tp => 115, - Ddx => 116, - Iatp => 117, - Stp => 118, - Srp => 119, - Uti => 120, - Smp => 121, - Sm => 122, - Ptp => 123, - IsisOverIpv4 => 124, - Fire => 125, - Crtp => 126, - Crudp => 127, - Sscopmce => 128, - Iplt => 129, - Sps => 130, - Pipe => 131, - Sctp => 132, - Fc => 133, - RsvpE2eIgnore => 134, - MobilityHeader => 135, - UdpLite => 136, - MplsInIp => 137, - Manet => 138, - Hip => 139, - Shim6 => 140, - Wesp => 141, - Rohc => 142, - Test1 => 253, - Test2 => 254, - Unknown(p) => p, - } - } -} diff --git a/vnt/packet/src/ip/mod.rs b/vnt/packet/src/ip/mod.rs deleted file mode 100644 index 7908624..0000000 --- a/vnt/packet/src/ip/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -use ipv4::packet::IpV4Packet; -use std::io; - -pub mod ipv4; - -pub enum IpPacket { - V4(IpV4Packet), -} - -impl> IpPacket { - pub fn new(buffer: B) -> io::Result { - match buffer.as_ref()[0] >> 4 { - 4 => Ok(IpPacket::V4(IpV4Packet::new(buffer)?)), - _ => Err(io::Error::from(io::ErrorKind::InvalidData)), - } - } -} diff --git a/vnt/packet/src/lib.rs b/vnt/packet/src/lib.rs deleted file mode 100644 index 56d6d8d..0000000 --- a/vnt/packet/src/lib.rs +++ /dev/null @@ -1,143 +0,0 @@ -use std::net::Ipv4Addr; - -use byteorder::BigEndian; -use byteorder::ReadBytesExt; - -pub mod arp; -pub mod ethernet; -pub mod icmp; -pub mod igmp; -pub mod ip; -pub mod tcp; -pub mod udp; -// pub enum IpUpperLayer { -// UDP(UdpPacket), -// Unknown(B), -// } -// -// impl> fmt::Debug for IpUpperLayer { -// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -// match self { -// IpUpperLayer::UDP(p) => { -// f.debug_struct("udp::Packet") -// .field("data", p).finish() -// } -// IpUpperLayer::Unknown(p) => { -// f.debug_struct("Unknown") -// .field("data", &p.as_ref()).finish() -// } -// } -// } -// } - -/// https://datatracker.ietf.org/doc/html/rfc1071 4.1节 -/// -/// 计算校验和,各协议都是通用的 -/// 计算: -/// 首先将校验和置0,然后对首部每个16位数进行二进制反码求和, -/// 得到校验和之后,持续取高16位加到低16位,直到高16位全为0 -/// 最后取反 -/// -/// 校验: -/// 在已有校验和的情况下,再计算校验和,正确的数据计算得到的值为0 -/* -unsigned short getChecksum(unsigned short * iphead, int count) -{ - unsigned long int sum = 0; - unsigned short checksum = 0; - - printf("\nStarting adress: %p\n", iphead); - - while(count > 1) { - sum += * (unsigned short *) (iphead); - count -=2; - printf("a: %p, content is: %d, new sum: %ld\n", iphead, (unsigned short) *(iphead), sum); - iphead++; - } - - if(count > 0) { - sum += * (unsigned short *) (iphead); - } - - while(sum >> 16) { - sum = (sum & 0xffff) + (sum >> 16); - } - - checksum = ~sum; - - return checksum; -} - */ -pub fn cal_checksum(buffer: &[u8]) -> u16 { - use std::io::Cursor; - let mut sum = 0; - let mut buffer = Cursor::new(buffer); - while let Ok(value) = buffer.read_u16::() { - sum += u32::from(value); - } - if let Ok(l) = buffer.read_u8() { - sum += u32c(l, 0); - } - while sum >> 16 != 0 { - sum = (sum & 0xffff) + (sum >> 16); - } - !sum as u16 -} - -/// ipv4上层协议校验和计算方式 -/// ipv4 udp伪首部 用于参与计算首部校验和 -/* - 0 7 8 15 16 23 24 31 - +--------+--------+--------+--------+ - | source address | - +--------+--------+--------+--------+ - | destination address | - +--------+--------+--------+--------+ - | zero |protocol| length | - +--------+--------+--------+--------+ -*/ -pub fn ipv4_cal_checksum( - buffer: &[u8], - src_ip: &Ipv4Addr, - dest_ip: &Ipv4Addr, - protocol: u8, -) -> u16 { - use std::io::Cursor; - let length = buffer.len(); - let mut sum = 0; - let src_ip = src_ip.octets(); - sum += u32c(src_ip[0], src_ip[1]); - sum += u32c(src_ip[2], src_ip[3]); - let dest_ip = dest_ip.octets(); - sum += u32c(dest_ip[0], dest_ip[1]); - sum += u32c(dest_ip[2], dest_ip[3]); - sum += u32c(0, protocol); - sum += length as u32; - let mut buffer = Cursor::new(buffer); - while let Ok(value) = buffer.read_u16::() { - sum += u32::from(value); - } - if let Ok(l) = buffer.read_u8() { - sum += u32c(l, 0); - } - while sum >> 16 != 0 { - sum = (sum & 0xffff) + (sum >> 16); - } - !sum as u16 -} - -#[inline] -fn u32c(x: u8, y: u8) -> u32 { - ((x as u32) << 8) | y as u32 -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let sum = cal_checksum(&[255, 255]); - println!("{:?}", sum); - } -} diff --git a/vnt/packet/src/tcp/mod.rs b/vnt/packet/src/tcp/mod.rs deleted file mode 100644 index ff641ba..0000000 --- a/vnt/packet/src/tcp/mod.rs +++ /dev/null @@ -1,42 +0,0 @@ -use std::fmt; - -pub mod tcp; - -pub struct Flags(u8); - -pub const FIN: u8 = 0b0000_0001; -pub const SYN: u8 = 0b0000_0010; -pub const RST: u8 = 0b0000_0100; -pub const PSH: u8 = 0b0000_1000; -pub const ACK: u8 = 0b0001_0000; -pub const URG: u8 = 0b0010_0000; - -impl fmt::Debug for Flags { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut str = String::with_capacity(22); - if self.0 & URG != 0 { - str.push_str("URG|"); - } - if self.0 & ACK != 0 { - str.push_str("ACK|"); - } - if self.0 & PSH != 0 { - str.push_str("PSH|"); - } - if self.0 & RST != 0 { - str.push_str("RST|"); - } - if self.0 & SYN != 0 { - str.push_str("SYN|"); - } - if self.0 & FIN != 0 { - str.push_str("FIN|"); - } - if str.is_empty() { - f.debug_struct("NULL").finish() - } else { - let len = str.len() - 1; - f.debug_struct(&str[..len]).finish() - } - } -} diff --git a/vnt/packet/src/tcp/tcp.rs b/vnt/packet/src/tcp/tcp.rs deleted file mode 100644 index a68ac9d..0000000 --- a/vnt/packet/src/tcp/tcp.rs +++ /dev/null @@ -1,178 +0,0 @@ -use std::net::Ipv4Addr; -use std::{fmt, io}; - -use crate::tcp::Flags; - -/// tcp -/* - https://www.rfc-editor.org/rfc/rfc793 - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Source Port | Destination Port | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Sequence Number | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Acknowledgment Number | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Data | |U|A|P|R|S|F| | - | Offset| Reserved |R|C|S|S|Y|I| Window | - | | |G|K|H|T|N|N| | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Checksum | Urgent Pointer | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Options | Padding | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | data | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - Source Port: 16位 源端口 - Destination Port:16位 目的端口 - Sequence Number:32位 序列号,如果存在syn标志,则为初始序列号 - Acknowledgment Number:32位 如果设置了ack标志,这个表示确认收到的序号 - Data Offset:4位 数据的开始偏移位,单位是4字节 - Reserved:6位 未使用,全零 - 控制位:6位 从左到右 - URG: 紧急指针 表示数据要优先处理 - ACK: 确认位 - PSH: 推送 要求把数据尽快的交给应用层,不做处理 - RST: 重置连接 - SYN: 同步序列号 - FIN: 结束发送 - Window: 16位 能接收的数据大小 - Checksum:16位 校验和,需要加入伪首部 - Urgent Pointer:16位 紧急指针 - Options+Padding:32位整数倍,最多40个字节 -*/ -pub struct TcpPacket { - source_ip: Ipv4Addr, - destination_ip: Ipv4Addr, - buffer: B, -} - -impl> TcpPacket { - pub fn unchecked(source_ip: Ipv4Addr, destination_ip: Ipv4Addr, buffer: B) -> TcpPacket { - TcpPacket { - source_ip, - destination_ip, - buffer, - } - } - pub fn new( - source_ip: Ipv4Addr, - destination_ip: Ipv4Addr, - buffer: B, - ) -> io::Result> { - let packet = TcpPacket::unchecked(source_ip, destination_ip, buffer); - - if packet.buffer.as_ref().len() < 20 { - Err(io::Error::from(io::ErrorKind::InvalidData))?; - } - - if packet.buffer.as_ref().len() < packet.data_offset() as usize * 4 { - Err(io::Error::from(io::ErrorKind::InvalidData))?; - } - - Ok(packet) - } -} -impl + AsMut<[u8]>> TcpPacket { - pub fn set_source_ip(&mut self, value: Ipv4Addr) { - self.source_ip = value; - } - pub fn set_destination_ip(&mut self, value: Ipv4Addr) { - self.destination_ip = value; - } -} -impl + AsMut<[u8]>> TcpPacket { - fn set_checksum(&mut self, value: u16) { - self.buffer.as_mut()[16..18].copy_from_slice(&value.to_be_bytes()) - } - pub fn set_source_port(&mut self, value: u16) { - self.buffer.as_mut()[0..2].copy_from_slice(&value.to_be_bytes()) - } - pub fn set_destination_port(&mut self, value: u16) { - self.buffer.as_mut()[2..4].copy_from_slice(&value.to_be_bytes()) - } - /// 更新校验和 - pub fn update_checksum(&mut self) { - //先将校验和置0 - self.set_checksum(0); - self.set_checksum(self.cal_checksum()) - } -} - -impl> TcpPacket { - /// 源端口 - pub fn source_port(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[0..2].try_into().unwrap()) - } - - /// 目标端口 - pub fn destination_port(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap()) - } - /// 序列号 - pub fn sequence(&self) -> u32 { - u32::from_be_bytes(self.buffer.as_ref()[4..8].try_into().unwrap()) - } - /// 确认号 - pub fn acknowledgment(&self) -> u32 { - u32::from_be_bytes(self.buffer.as_ref()[8..12].try_into().unwrap()) - } - /// 数据偏移 4字节为单位 - pub fn data_offset(&self) -> u8 { - self.buffer.as_ref()[12] >> 4 - } - pub fn flags(&self) -> Flags { - Flags(self.buffer.as_ref()[13]) - } - pub fn window(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[14..16].try_into().unwrap()) - } - pub fn checksum(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[16..18].try_into().unwrap()) - } - /// 验证校验和,ipv4中为0表示不使用校验和,ipv6校验和不能为0 - /// TCP/IP协议栈不会自己计算校验和,而是简单地将一个空的校验和字段(零或随机填充)交给网卡硬件。 - /// 所以抓到发出去的包校验和可能是错误的 - pub fn is_valid(&self) -> bool { - self.checksum() == 0 || self.cal_checksum() == 0 - } - fn cal_checksum(&self) -> u16 { - crate::ipv4_cal_checksum( - self.buffer.as_ref(), - &self.source_ip, - &self.destination_ip, - 6, - ) - } - pub fn urgent_pointer(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[18..20].try_into().unwrap()) - } - pub fn options(&self) -> &[u8] { - &self.buffer.as_ref()[20..(self.data_offset() as usize * 4)] - } - pub fn payload(&self) -> &[u8] { - &self.buffer.as_ref()[(self.data_offset() as usize * 4)..] - } -} - -impl> fmt::Debug for TcpPacket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("tcp::Packet") - .field("source", &self.source_port()) - .field("destination", &self.destination_port()) - .field("sequence", &self.sequence()) - .field("acknowledgment", &self.acknowledgment()) - .field("offset", &self.data_offset()) - .field("flags", &self.flags()) - .field("window", &self.window()) - .field("checksum", &self.checksum()) - .field("is_valid", &self.is_valid()) - .field("pointer", &self.urgent_pointer()) - .field("options", &self.options()) - .field("payload", &self.payload()) - .finish() - } -} diff --git a/vnt/packet/src/udp/mod.rs b/vnt/packet/src/udp/mod.rs deleted file mode 100644 index 7e5aaa1..0000000 --- a/vnt/packet/src/udp/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod udp; diff --git a/vnt/packet/src/udp/udp.rs b/vnt/packet/src/udp/udp.rs deleted file mode 100644 index 3960b90..0000000 --- a/vnt/packet/src/udp/udp.rs +++ /dev/null @@ -1,150 +0,0 @@ -use std::net::Ipv4Addr; -use std::{fmt, io}; - -/// udp协议 -/// -/* -RFC 768 https://www.ietf.org/rfc/rfc768.txt - - 0 7 8 15 16 23 24 31 - +--------+--------+--------+--------+ - | 源端口(16) | 目的端口(16) | - +--------+--------+--------+--------+ - | 长度(16) | 校验和(16) | - +--------+--------+--------+--------+ - | - | 载荷 ... - +---------------- ... - - 注:1.长度包含标头和数据体,以字节为单位 - 2.伪首部和载荷参与校验和的计算,位数不够则补0 -*/ - -/// ipv6 udp伪首部 -/* https://datatracker.ietf.org/doc/html/rfc2460 - - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - + + - | | - + Source Address + - | | - + + - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - + + - | | - + Destination Address + - | | - + + - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Upper-Layer Packet Length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | zero | Next Header | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -*/ - -pub struct UdpPacket { - source_ip: Ipv4Addr, - destination_ip: Ipv4Addr, - buffer: B, -} - -impl> UdpPacket { - pub fn unchecked(source_ip: Ipv4Addr, destination_ip: Ipv4Addr, buffer: B) -> UdpPacket { - UdpPacket { - source_ip, - destination_ip, - buffer, - } - } - pub fn new( - source_ip: Ipv4Addr, - destination_ip: Ipv4Addr, - buffer: B, - ) -> io::Result> { - if buffer.as_ref().len() < 8 { - Err(io::Error::from(io::ErrorKind::InvalidData))?; - } - let packet = Self::unchecked(source_ip, destination_ip, buffer); - Ok(packet) - } -} - -impl> UdpPacket { - /// 源端口 - pub fn source_port(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[0..2].try_into().unwrap()) - } - - /// 目标端口 - pub fn destination_port(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap()) - } - - /// 总字节数 - pub fn length(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[4..6].try_into().unwrap()) - } - - /// Checksum of the packet. - pub fn checksum(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap()) - } - /// 验证校验和,ipv4中为0表示不使用校验和,ipv6校验和不能为0 - pub fn is_valid(&self) -> bool { - self.checksum() == 0 || self.cal_checksum() == 0 - } - pub fn payload(&self) -> &[u8] { - &self.buffer.as_ref()[8..] - } - fn cal_checksum(&self) -> u16 { - crate::ipv4_cal_checksum( - self.buffer.as_ref(), - &self.source_ip, - &self.destination_ip, - 17, - ) - } -} - -// impl + AsMut<[u8]>> UdpPacket { -// fn header_mut(&mut self) -> &mut [u8] { -// &mut self.buffer.as_mut()[..8] -// } -// } - -impl + AsMut<[u8]>> UdpPacket { - /// 设置源端口 - pub fn set_source_port(&mut self, value: u16) { - self.buffer.as_mut()[0..2].copy_from_slice(&value.to_be_bytes()) - } - - /// 设置目的端口 - pub fn set_destination_port(&mut self, value: u16) { - self.buffer.as_mut()[2..4].copy_from_slice(&value.to_be_bytes()) - } - fn set_checksum(&mut self, value: u16) { - self.buffer.as_mut()[6..8].copy_from_slice(&value.to_be_bytes()) - } - pub fn update_checksum(&mut self) { - //先写0 - self.set_checksum(0); - self.set_checksum(self.cal_checksum()); - } -} - -impl> fmt::Debug for UdpPacket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("udp::Packet") - .field("source", &self.source_port()) - .field("destination", &self.destination_port()) - .field("length", &self.length()) - .field("checksum", &self.checksum()) - .field("is_valid", &self.is_valid()) - .field("payload", &self.payload()) - .finish() - } -} diff --git a/vnt/proto/message.proto b/vnt/proto/message.proto deleted file mode 100644 index 948a123..0000000 --- a/vnt/proto/message.proto +++ /dev/null @@ -1,94 +0,0 @@ -syntax = "proto3"; - -message HandshakeRequest { - string version = 1; - bool secret = 2; - string key_finger = 3; -} -message HandshakeResponse { - string version = 1; - bool secret = 2; - bytes public_key = 3; - string key_finger = 4; -} -message SecretHandshakeRequest { - string token = 1; - bytes key = 2; -} -message RegistrationRequest { - string token = 1; - string device_id = 2; - string name = 3; - bool is_fast = 4; - string version = 5; - fixed32 virtual_ip = 6; - bool allow_ip_change = 7; - bool client_secret = 8; - bytes client_secret_hash = 9; -} - -message RegistrationResponse { - fixed32 virtual_ip = 1; - fixed32 virtual_gateway = 2; - fixed32 virtual_netmask = 3; - uint32 epoch = 4; - repeated DeviceInfo device_info_list = 5; - fixed32 public_ip = 6; - uint32 public_port = 7; - bytes public_ipv6 = 8; -} -message DeviceInfo { - string name = 1; - fixed32 virtual_ip = 2; - uint32 device_status = 3; - bool client_secret = 4; - bytes client_secret_hash = 5; - bool wireguard = 6; -} - -message DeviceList { - uint32 epoch = 1; - repeated DeviceInfo device_info_list = 2; -} - -message PunchInfo { - repeated fixed32 public_ip_list = 2; - uint32 public_port = 3; - uint32 public_port_range = 4; - PunchNatType nat_type = 5; - bool reply = 6; - fixed32 local_ip = 7; - uint32 local_port = 8; - bytes ipv6 = 9; - uint32 ipv6_port = 10; - uint32 tcp_port = 11; - repeated uint32 udp_ports = 12; - repeated uint32 public_ports = 13; - 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; - repeated RouteItem p2p_list = 2; - uint64 up_stream = 3; - uint64 down_stream = 4; - PunchNatType nat_type = 5; -} -message RouteItem { - fixed32 next_ip = 1; -} \ No newline at end of file diff --git a/vnt/src/channel/context.rs b/vnt/src/channel/context.rs deleted file mode 100644 index 889c106..0000000 --- a/vnt/src/channel/context.rs +++ /dev/null @@ -1,559 +0,0 @@ -use fnv::FnvHashMap; -use std::net::{Ipv4Addr, SocketAddr, UdpSocket}; -use std::ops::Deref; -use std::sync::atomic::AtomicUsize; -use std::sync::Arc; -use std::time::{Duration, Instant}; -use std::{io, thread}; - -use crossbeam_utils::atomic::AtomicCell; -use parking_lot::RwLock; -use rand::Rng; - -use crate::channel::punch::NatType; -use crate::channel::sender::{AcceptSocketSender, PacketSender}; -use crate::channel::socket::LocalInterface; -use crate::channel::{ConnectProtocol, Route, RouteKey, UseChannelType, DEFAULT_RT}; -use crate::protocol::NetPacket; -use crate::util::limit::TrafficMeterMultiAddress; - -/// 传输通道上下文,持有udp socket、tcp socket和路由信息 -#[derive(Clone)] -pub struct ChannelContext { - inner: Arc, -} - -impl ChannelContext { - pub fn new( - main_udp_socket: Vec, - v4_len: usize, - use_channel_type: UseChannelType, - first_latency: bool, - protocol: ConnectProtocol, - packet_loss_rate: Option, - packet_delay: u32, - up_traffic_meter: Option, - down_traffic_meter: Option, - default_interface: LocalInterface, - ) -> Self { - let channel_num = v4_len; - assert_ne!(channel_num, 0, "not channel"); - let packet_loss_rate = packet_loss_rate - .map(|v| { - let v = (v * PACKET_LOSS_RATE_DENOMINATOR as f64) as u32; - if v > PACKET_LOSS_RATE_DENOMINATOR { - PACKET_LOSS_RATE_DENOMINATOR - } else { - v - } - }) - .unwrap_or(0); - let inner = ContextInner { - main_udp_socket, - 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, - up_traffic_meter, - down_traffic_meter, - default_interface, - default_route_key: AtomicCell::default(), - }; - Self { - inner: Arc::new(inner), - } - } -} - -impl Deref for ChannelContext { - type Target = ContextInner; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -/// 对称网络增加的udp socket数目,有助于增加打洞成功率 -pub const SYMMETRIC_CHANNEL_NUM: usize = 84; -const PACKET_LOSS_RATE_DENOMINATOR: u32 = 100_0000; - -pub struct ContextInner { - // 核心udp socket - pub(crate) main_udp_socket: Vec, - v4_len: usize, - // 对称网络增加的udp socket - sub_udp_socket: RwLock>, - // tcp数据发送器 - pub(crate) packet_map: RwLock>, - // 路由信息 - pub route_table: RouteTable, - // 使用什么协议连接服务器 - protocol: ConnectProtocol, - //控制丢包率,取值v=[0,100_0000] 丢包率r=v/100_0000 - packet_loss_rate: u32, - //控制延迟 - packet_delay: u32, - pub(crate) up_traffic_meter: Option, - pub(crate) down_traffic_meter: Option, - default_interface: LocalInterface, - default_route_key: AtomicCell>, -} - -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() - } - pub fn main_protocol(&self) -> ConnectProtocol { - self.protocol - } - pub fn is_udp_main(&self, route_key: &RouteKey) -> bool { - route_key.protocol().is_udp() && route_key.index < self.main_udp_socket.len() - } - pub fn first_latency(&self) -> bool { - self.route_table.first_latency - } - /// 切换NAT类型,不同的nat打洞模式会有不同 - pub fn switch( - &self, - nat_type: NatType, - udp_socket_sender: &AcceptSocketSender>>, - ) -> anyhow::Result<()> { - let mut write_guard = self.sub_udp_socket.write(); - match nat_type { - NatType::Symmetric => { - if !write_guard.is_empty() { - return Ok(()); - } - let mut vec = Vec::with_capacity(SYMMETRIC_CHANNEL_NUM); - for _ in 0..SYMMETRIC_CHANNEL_NUM { - let udp = 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); - for udp in vec.iter() { - let udp_socket = mio::net::UdpSocket::from_std(udp.try_clone()?); - mio_vec.push(udp_socket); - } - udp_socket_sender.try_add_socket(Some(mio_vec))?; - *write_guard = vec; - } - NatType::Cone => { - if write_guard.is_empty() { - return Ok(()); - } - udp_socket_sender.try_add_socket(None)?; - *write_guard = Vec::new(); - } - } - Ok(()) - } - #[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> { - let mut ports = Vec::new(); - 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], route_key: &RouteKey) -> io::Result<()> { - if let Some(tcp) = self.packet_map.read().get(route_key) { - tcp.try_send(buf) - } else { - Err(io::Error::new( - io::ErrorKind::NotFound, - format!("dest={:?}", route_key), - )) - } - } - 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")) - } - } - /// 将数据发送到默认通道,一般发往服务器才用此方法 - pub fn send_default>( - &self, - buf: &NetPacket, - addr: SocketAddr, - ) -> io::Result<()> { - if self.protocol.is_udp() { - if addr.is_ipv4() { - self.send_main_udp(0, buf.buffer(), addr)? - } else { - self.send_main_udp(self.v4_len, buf.buffer(), addr)? - } - } else { - 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()); - } - Ok(()) - } - - /// 此方法仅用于对称网络打洞 - pub fn try_send_all(&self, buf: &[u8], addr: SocketAddr) { - self.try_send_all_main(buf, addr); - for udp in self.sub_udp_socket.read().iter() { - if let Err(e) = udp.send_to(buf, addr) { - log::warn!("{:?},add={:?}", e, addr); - } - thread::sleep(Duration::from_millis(3)); - } - } - pub fn try_send_all_main(&self, buf: &[u8], addr: SocketAddr) { - for index in 0..self.channel_num() { - if let Err(e) = self.send_main_udp(index, buf, addr) { - log::warn!("{:?},add={:?}", e, addr); - } - } - } - /// 发送网络数据 - pub fn send_ipv4_by_id>( - &self, - buf: &NetPacket, - id: &Ipv4Addr, - server_addr: SocketAddr, - send_default: bool, - ) -> io::Result<()> { - if self.packet_loss_rate > 0 { - if rand::thread_rng().gen_ratio(self.packet_loss_rate, PACKET_LOSS_RATE_DENOMINATOR) { - return Ok(()); - } - } - - if self.packet_delay > 0 { - thread::sleep(Duration::from_millis(self.packet_delay as _)); - } - //优先发到直连到地址 - if let Err(e) = self.send_by_id(buf, id) { - if e.kind() != io::ErrorKind::NotFound { - log::warn!("{}:{:?}", id, e); - } - if !self.route_table.use_channel_type.is_only_p2p() && send_default { - //符合条件再发到服务器转发 - self.send_default(buf, server_addr)?; - } - } - Ok(()) - } - /// 将数据发到指定id - pub fn send_by_id>(&self, buf: &NetPacket, id: &Ipv4Addr) -> io::Result<()> { - let mut c = 0; - loop { - let route = self.route_table.get_route_by_id(c, id)?; - return if let Err(e) = self.send_by_key(buf, route.route_key()) { - //降低发送速率 - if e.kind() == io::ErrorKind::WouldBlock { - c += 1; - if c < 10 { - thread::sleep(Duration::from_micros(200)); - continue; - } - } - Err(e) - } else { - Ok(()) - }; - } - } - /// 将数据发到指定路由 - pub fn send_by_key>( - &self, - buf: &NetPacket, - route_key: RouteKey, - ) -> io::Result<()> { - match route_key.protocol() { - ConnectProtocol::UDP => { - if let Some(main_udp) = self.main_udp_socket.get(route_key.index) { - main_udp.send_to(buf.buffer(), route_key.addr)?; - } else { - if let Some(udp) = self - .sub_udp_socket - .read() - .get(route_key.index - self.main_len()) - { - udp.send_to(buf.buffer(), route_key.addr)?; - } else { - Err(io::Error::from(io::ErrorKind::NotFound))? - } - } - } - ConnectProtocol::TCP | ConnectProtocol::WS | ConnectProtocol::WSS => { - self.send_tcp(buf.buffer(), &route_key)? - } - } - if let Some(up_traffic_meter) = &self.up_traffic_meter { - up_traffic_meter.add_traffic(buf.destination(), buf.data_len()); - } - Ok(()) - } - pub fn remove_route(&self, ip: &Ipv4Addr, route_key: RouteKey) { - self.route_table.remove_route(ip, route_key) - } -} - -pub struct RouteTable { - pub(crate) route_table: - RwLock)>)>>, - first_latency: bool, - channel_num: usize, - use_channel_type: UseChannelType, -} - -impl RouteTable { - fn new(use_channel_type: UseChannelType, first_latency: bool, channel_num: usize) -> Self { - Self { - route_table: RwLock::new(FnvHashMap::with_capacity_and_hasher(64, Default::default())), - use_channel_type, - first_latency, - channel_num, - } - } -} - -impl RouteTable { - fn get_route_by_id(&self, index: usize, id: &Ipv4Addr) -> io::Result { - if let Some((_count, v)) = self.route_table.read().get(id) { - if self.first_latency { - if let Some((route, _)) = v.first() { - return Ok(*route); - } - } else { - let len = v.len(); - if len != 0 { - let route = &v[index % len].0; - // 跳过默认rt的路由(一般是刚加入的),这有助于提升稳定性 - if route.rt != DEFAULT_RT { - return Ok(*route); - } - for (route, _) in v { - if route.rt != DEFAULT_RT { - return Ok(*route); - } - } - } - } - } - Err(io::Error::new(io::ErrorKind::NotFound, "route not found")) - } - pub fn add_route_if_absent(&self, id: Ipv4Addr, route: Route) -> bool { - self.add_route_(id, route, true) - } - pub fn add_route(&self, id: Ipv4Addr, route: Route) -> bool { - self.add_route_(id, route, false) - } - fn add_route_(&self, id: Ipv4Addr, route: Route, only_if_absent: bool) -> bool { - // 限制通道类型 - match self.use_channel_type { - UseChannelType::P2p => { - if !route.is_p2p() { - return false; - } - } - _ => {} - } - let key = route.route_key(); - if only_if_absent { - if let Some((_, list)) = self.route_table.read().get(&id) { - for (x, _) in list { - if x.route_key() == key { - return true; - } - } - } - } - let mut route_table = self.route_table.write(); - let (_, list) = route_table - .entry(id) - .or_insert_with(|| (AtomicUsize::new(0), Vec::with_capacity(4))); - let mut exist = false; - for (x, time) in list.iter_mut() { - if x.metric < route.metric && !self.first_latency { - //非优先延迟的情况下 不能比当前的路径更长 - return false; - } - if x.route_key() == key { - if only_if_absent { - return true; - } - x.metric = route.metric; - x.rt = route.rt; - exist = true; - time.store(Instant::now()); - break; - } - } - if exist { - list.sort_by_key(|(k, _)| k.rt); - } else { - if !self.first_latency { - if route.is_p2p() { - //非优先延迟的情况下 添加了直连的则排除非直连的 - list.retain(|(k, _)| k.is_p2p()); - } - }; - list.sort_by_key(|(k, _)| k.rt); - list.push((route, AtomicCell::new(Instant::now()))); - } - return true; - } - // 直接移除会导致通道不稳定,所以废弃这个方法,后面改用多余通道不发心跳包,从而让通道自动过期 - // fn truncate_(&self, list: &mut Vec<(Route, AtomicCell)>, 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> { - if let Some((_, v)) = self.route_table.read().get(id) { - Some(v.iter().map(|(i, _)| *i).collect()) - } else { - None - } - } - pub fn route_one(&self, id: &Ipv4Addr) -> Option { - if let Some((_, v)) = self.route_table.read().get(id) { - v.first().map(|(i, _)| *i) - } else { - None - } - } - pub fn route_one_p2p(&self, id: &Ipv4Addr) -> Option { - if let Some((_, v)) = self.route_table.read().get(id) { - for (i, _) in v { - if i.is_p2p() { - return Some(*i); - } - } - } - None - } - pub fn route_to_id(&self, route_key: &RouteKey) -> Option { - let table = self.route_table.read(); - for (k, (_, v)) in table.iter() { - for (route, _) in v { - if &route.route_key() == route_key && route.is_p2p() { - return Some(*k); - } - } - } - None - } - pub fn no_need_punch(&self, id: &Ipv4Addr) -> bool { - if let Some((_, v)) = self.route_table.read().get(id) { - //p2p的通道数符合要求 - return v.iter().filter(|(k, _)| k.is_p2p()).count() >= self.channel_num; - } - false - } - pub fn p2p_num(&self, id: &Ipv4Addr) -> usize { - if let Some((_, v)) = self.route_table.read().get(id) { - v.iter().filter(|(k, _)| k.is_p2p()).count() - } else { - 0 - } - } - /// 返回所有路由 - pub fn route_table(&self) -> Vec<(Ipv4Addr, Vec)> { - let table = self.route_table.read(); - table - .iter() - .map(|(k, (_, v))| (k.clone(), v.iter().map(|(i, _)| *i).collect())) - .collect() - } - pub fn route_table_p2p(&self) -> Vec<(Ipv4Addr, Route)> { - let table = self.route_table.read(); - let mut list = Vec::with_capacity(8); - for (ip, (_, routes)) in table.iter() { - for (route, _) in routes.iter() { - if route.is_p2p() { - list.push((*ip, *route)); - break; - } - } - } - list - } - pub fn route_table_one(&self) -> Vec<(Ipv4Addr, Route)> { - let mut list = Vec::with_capacity(8); - let table = self.route_table.read(); - for (k, (_, v)) in table.iter() { - if let Some((route, _)) = v.first() { - list.push((*k, *route)); - } - } - list - } - pub fn remove_route(&self, id: &Ipv4Addr, route_key: RouteKey) { - let mut write_guard = self.route_table.write(); - if let Some((_, routes)) = write_guard.get_mut(id) { - routes.retain(|(x, _)| x.route_key() != route_key); - if routes.is_empty() { - write_guard.remove(id); - } - } - } - /// 更新路由入栈包的时刻,长时间没有收到数据的路由将会被剔除 - pub fn update_read_time(&self, id: &Ipv4Addr, route_key: &RouteKey) { - if let Some((_, routes)) = self.route_table.read().get(id) { - for (route, time) in routes { - if &route.route_key() == route_key { - time.store(Instant::now()); - break; - } - } - } - } -} diff --git a/vnt/src/channel/handler.rs b/vnt/src/channel/handler.rs deleted file mode 100644 index 7c6abe7..0000000 --- a/vnt/src/channel/handler.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::channel::context::ChannelContext; -use crate::channel::RouteKey; - -pub trait RecvChannelHandler: Clone + Send + Sync + 'static { - fn handle( - &self, - buf: &mut [u8], - extend: &mut [u8], - route_key: RouteKey, - context: &ChannelContext, - ); -} diff --git a/vnt/src/channel/idle.rs b/vnt/src/channel/idle.rs deleted file mode 100644 index c145819..0000000 --- a/vnt/src/channel/idle.rs +++ /dev/null @@ -1,45 +0,0 @@ -use std::net::Ipv4Addr; -use std::time::Duration; - -use crate::channel::context::ChannelContext; -use crate::channel::Route; - -pub struct Idle { - read_idle: Duration, - context: ChannelContext, -} - -impl Idle { - pub fn new(read_idle: Duration, context: ChannelContext) -> Self { - Self { read_idle, context } - } -} - -pub enum IdleType { - Timeout(Ipv4Addr, Route), - Sleep(Duration), - None, -} - -impl Idle { - /// 获取空闲路由 - pub fn next_idle(&self) -> IdleType { - let mut max = Duration::from_secs(0); - let read_guard = self.context.route_table.route_table.read(); - if read_guard.is_empty() { - return IdleType::None; - } - for (ip, (_, routes)) in read_guard.iter() { - for (route, time) in routes { - let last_read = time.load().elapsed(); - if last_read >= self.read_idle { - return IdleType::Timeout(*ip, *route); - } else if max < last_read { - max = last_read; - } - } - } - let sleep_time = self.read_idle.checked_sub(max).unwrap_or_default(); - return IdleType::Sleep(sleep_time); - } -} diff --git a/vnt/src/channel/mod.rs b/vnt/src/channel/mod.rs deleted file mode 100644 index 3c62424..0000000 --- a/vnt/src/channel/mod.rs +++ /dev/null @@ -1,365 +0,0 @@ -use anyhow::Context; -use std::net::{SocketAddr, UdpSocket}; -use std::str::FromStr; -use tokio::sync::mpsc::channel; - -use crate::channel::context::ChannelContext; -use crate::channel::handler::RecvChannelHandler; -use crate::channel::sender::{AcceptSocketSender, 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")] -use crate::channel::ws_channel::ws_connect_accept; -use crate::util::limit::TrafficMeterMultiAddress; -use crate::util::StopManager; - -pub mod context; -pub mod handler; -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")] -pub mod ws_channel; - -pub const BUFFER_SIZE: usize = 1024 * 64; -// 这里留个坑,tcp是支持_TCP_MAX_PACKET_SIZE长度的, -// 但是缓存只用BUFFER_SIZE,会导致多余的数据接收不了 -const TCP_MAX_PACKET_SIZE: usize = (1 << 24) - 1; - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum UseChannelType { - Relay, - P2p, - All, -} - -impl UseChannelType { - pub fn is_only_relay(&self) -> bool { - self == &UseChannelType::Relay - } - pub fn is_only_p2p(&self) -> bool { - self == &UseChannelType::P2p - } - pub fn is_all(&self) -> bool { - self == &UseChannelType::All - } -} - -impl FromStr for UseChannelType { - type Err = String; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().trim() { - "relay" => Ok(UseChannelType::Relay), - "p2p" => Ok(UseChannelType::P2p), - "all" => Ok(UseChannelType::All), - _ => Err(format!("not match '{}', enum: relay/p2p/all", s)), - } - } -} - -impl Default for UseChannelType { - fn default() -> Self { - UseChannelType::All - } -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub enum ConnectProtocol { - UDP, - TCP, - WS, - WSS, -} - -impl ConnectProtocol { - #[inline] - pub fn is_tcp(&self) -> bool { - self == &ConnectProtocol::TCP - } - #[inline] - pub fn is_udp(&self) -> bool { - self == &ConnectProtocol::UDP - } - #[inline] - pub fn is_ws(&self) -> bool { - self == &ConnectProtocol::WS - } - #[inline] - pub fn is_wss(&self) -> bool { - self == &ConnectProtocol::WSS - } - pub fn is_transport(&self) -> bool { - self.is_tcp() || self.is_udp() - } - pub fn is_base_tcp(&self) -> bool { - self.is_tcp() || self.is_ws() || self.is_wss() - } -} - -#[derive(Copy, Clone, Debug)] -pub struct Route { - pub protocol: ConnectProtocol, - index: usize, - pub addr: SocketAddr, - pub metric: u8, - pub rt: i64, -} - -#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] -pub struct RouteSortKey { - pub metric: u8, - pub rt: i64, -} - -const DEFAULT_RT: i64 = 9999; - -impl Route { - pub fn new( - protocol: ConnectProtocol, - index: usize, - addr: SocketAddr, - metric: u8, - rt: i64, - ) -> Self { - Self { - protocol, - index, - addr, - metric, - rt, - } - } - pub fn from(route_key: RouteKey, metric: u8, rt: i64) -> Self { - Self { - protocol: route_key.protocol, - index: route_key.index, - addr: route_key.addr, - metric, - rt, - } - } - pub fn from_default_rt(route_key: RouteKey, metric: u8) -> Self { - Self { - protocol: route_key.protocol, - index: route_key.index, - addr: route_key.addr, - metric, - rt: DEFAULT_RT, - } - } - pub fn route_key(&self) -> RouteKey { - RouteKey { - protocol: self.protocol, - index: self.index, - addr: self.addr, - } - } - pub fn sort_key(&self) -> RouteSortKey { - RouteSortKey { - metric: self.metric, - rt: self.rt, - } - } - pub fn is_p2p(&self) -> bool { - self.metric == 1 - } -} - -#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] -pub struct RouteKey { - protocol: ConnectProtocol, - index: usize, - pub addr: SocketAddr, -} - -impl RouteKey { - pub(crate) const fn new(protocol: ConnectProtocol, index: usize, addr: SocketAddr) -> Self { - Self { - protocol, - index, - addr, - } - } - #[inline] - pub fn protocol(&self) -> ConnectProtocol { - self.protocol - } - #[inline] - pub fn index(&self) -> usize { - self.index - } -} - -pub(crate) fn init_context( - ports: Vec, - use_channel_type: UseChannelType, - first_latency: bool, - protocol: ConnectProtocol, - packet_loss_rate: Option, - packet_delay: u32, - default_interface: LocalInterface, - up_traffic_meter: Option, - down_traffic_meter: Option, -) -> anyhow::Result<(ChannelContext, std::net::TcpListener)> { - assert!(!ports.is_empty(), "not channel"); - 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, - Err(e) => { - log::warn!("{:?}", e); - false - } - }; - for port in &ports { - 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 socket = bind_udp(addr_v4, &default_interface)?; - let main_channel_v4: UdpSocket = socket.into(); - main_udp_socket_v4.push(main_channel_v4); - } - } - 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( - main_udp_socket, - v4_len, - use_channel_type, - first_latency, - protocol, - packet_loss_rate, - packet_delay, - up_traffic_meter, - down_traffic_meter, - default_interface, - ); - - let port = context.main_local_udp_port()?[0]; - //监听v6+v4双栈,tcp通道使用异步io - let (socket, address) = if use_ipv6 { - let address: SocketAddr = format!("[::]:{}", port).parse().unwrap(); - let socket = socket2::Socket::new(socket2::Domain::IPV6, socket2::Type::STREAM, None)?; - socket - .set_only_v6(false) - .with_context(|| format!("set_only_v6 failed: {}", &address))?; - (socket, address) - } else { - let address: SocketAddr = format!("0.0.0.0:{}", port).parse().unwrap(); - let socket = socket2::Socket::new(socket2::Domain::IPV4, socket2::Type::STREAM, None)?; - (socket, address) - }; - 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 { - //端口可能冲突,则使用任意端口 - log::warn!("监听tcp端口失败 {:?},重试一次", address); - let address: SocketAddr = if use_ipv6 { - format!("[::]:{}", 0).parse().unwrap() - } else { - format!("0.0.0.0:{}", port).parse().unwrap() - }; - socket - .bind(&address.into()) - .with_context(|| format!("bind failed: {}", &address))?; - } else { - //手动指定的ip,直接报错 - Err(anyhow::anyhow!("{:?},bind failed: {}", e, address))?; - } - } - socket.listen(128)?; - socket.set_nonblocking(true)?; - 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::() { - 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( - tcp_listener: std::net::TcpListener, - context: ChannelContext, - stop_manager: StopManager, - recv_handler: H, -) -> anyhow::Result<( - AcceptSocketSender>>, - ConnectUtil, -)> -where - H: RecvChannelHandler, -{ - let (tcp_connect_s, tcp_connect_r) = channel(16); - let (ws_connect_s, _ws_connect_r) = channel(16); - let connect_util = ConnectUtil::new(tcp_connect_s, ws_connect_s); - // udp监听,udp_socket_sender 用于NAT类型切换 - let udp_socket_sender = - udp_listen(stop_manager.clone(), recv_handler.clone(), context.clone())?; - // 建立tcp监听,tcp_socket_sender 用于tcp 直连 - tcp_listen( - tcp_listener, - tcp_connect_r, - recv_handler.clone(), - context.clone(), - stop_manager.clone(), - )?; - #[cfg(feature = "ws")] - ws_connect_accept(_ws_connect_r, recv_handler, context.clone(), stop_manager)?; - - Ok((udp_socket_sender, connect_util)) -} diff --git a/vnt/src/channel/notify.rs b/vnt/src/channel/notify.rs deleted file mode 100644 index 5a6dc57..0000000 --- a/vnt/src/channel/notify.rs +++ /dev/null @@ -1,126 +0,0 @@ -use mio::{Token, Waker}; -use parking_lot::Mutex; -use std::io; -use std::ops::Deref; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::Arc; - -#[derive(Clone)] -pub struct WritableNotify { - inner: Arc, -} - -impl WritableNotify { - pub fn new(waker: Waker) -> Self { - Self { - inner: Arc::new(WritableNotifyInner { - waker, - state: AtomicUsize::new(0), - tokens: Mutex::new(Vec::with_capacity(8)), - }), - } - } -} - -impl Deref for WritableNotify { - type Target = WritableNotifyInner; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -pub struct WritableNotifyInner { - waker: Waker, - state: AtomicUsize, - tokens: Mutex>, -} - -impl WritableNotifyInner { - pub fn notify(&self, token: Token, state: bool) -> io::Result<()> { - { - let mut guard = self.tokens.lock(); - if guard.is_empty() || !guard.contains(&(token, state)) { - guard.push((token, state)); - } - drop(guard); - } - self.need_write() - } - - pub fn stop(&self) -> io::Result<()> { - self.state.store(0b001, Ordering::Release); - self.waker.wake() - } - pub fn need_write(&self) -> io::Result<()> { - self.state.fetch_or(0b010, Ordering::AcqRel); - self.waker.wake() - } - pub fn add_socket(&self) -> io::Result<()> { - self.state.fetch_or(0b100, Ordering::AcqRel); - self.waker.wake() - } - pub fn take_all(&self) -> Option> { - let mut guard = self.tokens.lock(); - if guard.is_empty() { - None - } else { - Some(guard.drain(..).collect()) - } - } - pub fn is_stop(&self) -> bool { - self.state.load(Ordering::Acquire) & 0b001 == 0b001 - } - pub fn is_need_write(&self) -> bool { - self.state.fetch_and(!0b010, Ordering::AcqRel) & 0b010 == 0b010 - } - pub fn is_add_socket(&self) -> bool { - self.state.fetch_and(!0b100, Ordering::AcqRel) & 0b100 == 0b100 - } -} - -#[derive(Clone)] -pub struct AcceptNotify { - inner: Arc, -} - -impl AcceptNotify { - pub fn new(waker: Waker) -> Self { - Self { - inner: Arc::new(AcceptNotifyInner { - waker, - state: AtomicUsize::new(0), - }), - } - } -} - -impl Deref for AcceptNotify { - type Target = AcceptNotifyInner; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -pub struct AcceptNotifyInner { - waker: Waker, - state: AtomicUsize, -} - -impl AcceptNotifyInner { - pub fn is_stop(&self) -> bool { - self.state.load(Ordering::Acquire) & 0b001 == 0b001 - } - pub fn is_add_socket(&self) -> bool { - self.state.fetch_and(!0b100, Ordering::AcqRel) & 0b100 == 0b100 - } - pub fn stop(&self) -> io::Result<()> { - self.state.store(0b001, Ordering::Release); - self.waker.wake() - } - pub fn add_socket(&self) -> io::Result<()> { - self.state.fetch_or(0b100, Ordering::AcqRel); - self.waker.wake() - } -} diff --git a/vnt/src/channel/punch.rs b/vnt/src/channel/punch.rs deleted file mode 100644 index 3775134..0000000 --- a/vnt/src/channel/punch.rs +++ /dev/null @@ -1,508 +0,0 @@ -use std::collections::HashMap; -use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; -use std::ops::{Div, Mul}; -use std::str::FromStr; -use std::sync::Arc; -use std::time::Duration; -use std::{io, thread}; - -use crossbeam_utils::atomic::AtomicCell; -use rand::prelude::SliceRandom; -use rand::Rng; - -use crate::channel::context::ChannelContext; -use crate::channel::sender::ConnectUtil; -use crate::handle::CurrentDeviceInfo; -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, - 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 { - type Err = String; - - fn from_str(s: &str) -> Result { - 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/ipv4-tcp/ipv4-udp/ipv6/ipv6-tcp/ipv6-udp/all", - s - )), - } - } -} - -impl Default for PunchModel { - fn default() -> Self { - PunchModel::All - } -} -impl From 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 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 { - pub public_ips: Vec, - pub public_ports: Vec, - pub public_port_range: u16, - pub nat_type: NatType, - pub(crate) local_ipv4: Option, - pub(crate) ipv6: Option, - pub udp_ports: Vec, - pub tcp_port: u16, - pub public_tcp_port: u16, - pub punch_model: PunchModel, -} - -#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] -pub enum NatType { - Symmetric, - Cone, -} - -impl NatType { - pub fn is_cone(&self) -> bool { - self == &NatType::Cone - } -} -impl From for PunchNatType { - fn from(value: NatType) -> Self { - match value { - NatType::Symmetric => PunchNatType::Symmetric, - NatType::Cone => PunchNatType::Cone, - } - } -} - -impl Into 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, - public_ports: Vec, - public_port_range: u16, - mut local_ipv4: Option, - mut ipv6: Option, - udp_ports: Vec, - tcp_port: u16, - public_tcp_port: u16, - mut nat_type: NatType, - punch_model: PunchModel, - ) -> Self { - public_ips.retain(|ip| { - !ip.is_multicast() - && !ip.is_broadcast() - && !ip.is_unspecified() - && !ip.is_loopback() - && !ip.is_private() - }); - if public_ips.len() > 1 { - nat_type = NatType::Symmetric; - } - if let Some(ip) = local_ipv4 { - if ip.is_multicast() || ip.is_broadcast() || ip.is_unspecified() || ip.is_loopback() { - local_ipv4 = None - } - } - if let Some(ip) = ipv6 { - if ip.is_multicast() || ip.is_unspecified() || ip.is_loopback() { - ipv6 = None - } - } - Self { - public_ips, - public_ports, - public_port_range, - local_ipv4, - ipv6, - udp_ports, - tcp_port, - public_tcp_port, - nat_type, - punch_model, - } - } - pub fn update_addr(&mut self, index: usize, ip: Ipv4Addr, port: u16) -> bool { - let mut updated = false; - if port != 0 { - if let Some(public_port) = self.public_ports.get_mut(index) { - if *public_port != port { - updated = true; - log::info!("端口变化={}:{} index={}", ip, port, index) - } - *public_port = port; - } - } - if is_ipv4_global(&ip) { - if !self.public_ips.contains(&ip) { - self.public_ips.push(ip); - updated = true; - log::info!("ip变化={},{:?}", ip, self.public_ips) - } - } - updated - } - pub fn update_tcp_port(&mut self, port: u16) { - self.public_tcp_port = port; - } - pub fn local_ipv4(&self) -> Option { - self.local_ipv4 - } - pub fn ipv6(&self) -> Option { - self.ipv6 - } - pub fn local_udp_ipv4addr(&self, index: usize) -> Option { - let len = self.udp_ports.len(); - if len == 0 { - return None; - } - if let Some(local_ipv4) = self.local_ipv4 { - Some(SocketAddr::V4(SocketAddrV4::new( - local_ipv4, - self.udp_ports[index % len], - ))) - } else { - None - } - } - pub fn local_udp_ipv6addr(&self, index: usize) -> Option { - let len = self.udp_ports.len(); - if len == 0 { - return None; - } - if let Some(ipv6) = self.ipv6 { - Some(SocketAddr::V6(SocketAddrV6::new( - ipv6, - self.udp_ports[index % len], - 0, - 0, - ))) - } else { - None - } - } - - pub fn local_tcp_ipv6addr(&self) -> Option { - if self.tcp_port == 0 { - return None; - } - if let Some(ipv6) = self.ipv6 { - Some(SocketAddr::V6(SocketAddrV6::new(ipv6, self.tcp_port, 0, 0))) - } else { - None - } - } - pub fn local_tcp_ipv4addr(&self) -> Option { - if self.tcp_port == 0 { - return None; - } - if let Some(ipv4) = self.local_ipv4 { - Some(SocketAddr::V4(SocketAddrV4::new(ipv4, self.tcp_port))) - } else { - None - } - } -} - -#[derive(Clone)] -pub struct Punch { - context: ChannelContext, - port_vec: Vec, - port_index: HashMap, - punch_model: PunchModel, - connect_util: ConnectUtil, - nat_test: NatTest, - current_device: Arc>, -} - -impl Punch { - pub fn new( - context: ChannelContext, - punch_model: PunchModel, - connect_util: ConnectUtil, - nat_test: NatTest, - current_device: Arc>, - ) -> Self { - let mut port_vec: Vec = (1..65535).collect(); - port_vec.push(65535); - let mut rng = rand::thread_rng(); - port_vec.shuffle(&mut rng); - Punch { - context, - port_vec, - port_index: HashMap::new(), - punch_model, - connect_util, - nat_test, - current_device, - } - } -} - -impl Punch { - fn connect_tcp(&self, buf: &[u8], addr: SocketAddr) { - if self.nat_test.is_local_address(true, addr) { - return; - } - 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, - buf: &[u8], - id: Ipv4Addr, - mut nat_info: NatInfo, - punch_tcp: bool, - count: usize, - ) -> io::Result<()> { - if self.context.route_table.no_need_punch(&id) { - log::info!("已打洞成功,无需打洞:{:?}", id); - return Ok(()); - } - let device_info = self.current_device.load(); - - 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 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) - } - } - 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) { - let _ = self.context.send_main_udp(index, buf, ipv4_addr); - } - } - } - // 可能是开放了端口的,需要打洞 - 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次的情况下 - // 猜中的概率 p = 1-((65535-n)/65535)*((65535-n-1)/(65535-1))*...*((65535-n-k+1)/(65535-k+1)) - // n取76,k取600,猜中的概率就超过50%了 - // 前提 自己是锥形网络,否则猜中了也通信不了 - - //预测范围内最多发送max_k1个包 - let max_k1 = 60; - //全局最多发送max_k2个包 - let mut max_k2: usize = rand::thread_rng().gen_range(600..800); - if count > 2 { - //递减探测规模 - max_k2 = max_k2.mul(2).div(count).max(max_k1 as usize); - } - let port = nat_info.public_ports.get(0).map(|e| *e).unwrap_or(0); - if nat_info.public_port_range < max_k1 * 3 { - //端口变化不大时,在预测的范围内随机发送 - let min_port = if port > nat_info.public_port_range { - port - nat_info.public_port_range - } else { - 1 - }; - let (max_port, overflow) = port.overflowing_add(nat_info.public_port_range); - let max_port = if overflow { 65535 } else { max_port }; - let k = if max_port - min_port + 1 > max_k1 { - max_k1 as usize - } else { - (max_port - min_port + 1) as usize - }; - let mut nums: Vec = (min_port..=max_port).collect(); - nums.shuffle(&mut rand::thread_rng()); - self.punch_symmetric(&nums[..k], buf, &nat_info.public_ips, max_k1 as usize)?; - } - let start = *self.port_index.entry(id.clone()).or_insert(0); - let mut end = start + max_k2; - if end > self.port_vec.len() { - end = self.port_vec.len(); - } - let mut index = start - + self.punch_symmetric( - &self.port_vec[start..end], - buf, - &nat_info.public_ips, - max_k2, - )?; - if index >= self.port_vec.len() { - index = 0 - } - self.port_index.insert(id, index); - } - NatType::Cone => { - let is_cone = self.context.is_cone(); - 'a: for index in 0..nat_info.public_ports.len().min(channel_num) { - for ip in &nat_info.public_ips { - let port = nat_info.public_ports[index]; - if port == 0 || ip.is_unspecified() { - continue; - } - let addr = SocketAddr::V4(SocketAddrV4::new(*ip, port)); - if is_cone { - self.context.send_main_udp(index, buf, addr)?; - } else { - //只有一方是对称,则对称方要使用全部端口发送数据,符合上述计算的概率 - self.context.try_send_all(buf, addr); - } - thread::sleep(Duration::from_millis(2)); - } - if !is_cone { - //对称网络数据只发一遍 - break 'a; - } - } - } - } - Ok(()) - } - - fn punch_symmetric( - &self, - ports: &[u16], - buf: &[u8], - ips: &Vec, - max: usize, - ) -> io::Result { - let mut count = 0; - for (index, port) in ports.iter().enumerate() { - for pub_ip in ips { - count += 1; - if count == max { - return Ok(index); - } - let addr = SocketAddr::V4(SocketAddrV4::new(*pub_ip, *port)); - self.context.send_main_udp(0, buf, addr)?; - thread::sleep(Duration::from_millis(3)); - } - } - Ok(ports.len()) - } -} diff --git a/vnt/src/channel/sender.rs b/vnt/src/channel/sender.rs deleted file mode 100644 index 00255c8..0000000 --- a/vnt/src/channel/sender.rs +++ /dev/null @@ -1,272 +0,0 @@ -use std::collections::HashMap; -use std::io; -use std::net::{Ipv4Addr, SocketAddr}; -use std::sync::mpsc::{SyncSender, TrySendError}; -use std::sync::Arc; - -use crossbeam_utils::atomic::AtomicCell; -use parking_lot::Mutex; -use tokio::sync::mpsc::Sender; - -use crate::channel::context::ChannelContext; -use crate::channel::notify::AcceptNotify; -use crate::cipher::Cipher; -use crate::compression::Compressor; -use crate::external_route::ExternalRoute; -use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo}; -use crate::protocol; -use crate::protocol::{ip_turn_packet, NetPacket}; - -#[derive(Clone)] -pub struct IpPacketSender { - context: ChannelContext, - current_device: Arc>, - compressor: Compressor, - client_cipher: Cipher, - server_cipher: Cipher, - ip_route: ExternalRoute, - device_map: Arc)>>, - allow_wire_guard: bool, -} - -impl IpPacketSender { - pub fn new( - context: ChannelContext, - current_device: Arc>, - compressor: Compressor, - client_cipher: Cipher, - server_cipher: Cipher, - ip_route: ExternalRoute, - device_map: Arc)>>, - allow_wire_guard: bool, - ) -> Self { - Self { - context, - current_device, - compressor, - client_cipher, - server_cipher, - ip_route, - device_map, - allow_wire_guard, - } - } - pub fn self_virtual_ip(&self) -> Ipv4Addr { - self.current_device.load().virtual_ip - } - pub fn send_ip( - &self, - buf: &mut [u8], - data_len: usize, - auxiliary_buf: &mut [u8], - mut dest_ip: Ipv4Addr, - ) -> anyhow::Result<()> { - let device_info = self.current_device.load(); - let src_ip = device_info.virtual_ip; - if src_ip.is_unspecified() { - return Ok(()); - } - if let Some(v) = self.ip_route.route(&dest_ip) { - dest_ip = v; - } - if dest_ip.is_multicast() { - //广播 - dest_ip = Ipv4Addr::BROADCAST; - } - let mut net_packet = NetPacket::new0(data_len, buf)?; - net_packet.set_default_version(); - net_packet.set_protocol(protocol::Protocol::IpTurn); - net_packet.set_transport_protocol(ip_turn_packet::Protocol::Ipv4.into()); - net_packet.first_set_ttl(6); - net_packet.set_source(src_ip); - net_packet.set_destination(dest_ip); - if self.allow_wire_guard { - if dest_ip.is_broadcast() || dest_ip == device_info.broadcast_ip { - let exists_wg = self - .device_map - .lock() - .1 - .values() - .any(|v| v.status.is_online() && v.wireguard); - if exists_wg { - send_to_wg_broadcast( - &self.context, - &net_packet, - &self.server_cipher, - &device_info, - )?; - } - } else { - let guard = self.device_map.lock(); - if let Some(peer_info) = guard.1.get(&dest_ip) { - if peer_info.wireguard { - if peer_info.status.is_offline() { - return Ok(()); - } - drop(guard); - send_to_wg( - &self.context, - &mut net_packet, - &self.server_cipher, - &device_info, - )?; - return Ok(()); - } - } - } - } - - let mut auxiliary = NetPacket::new(auxiliary_buf)?; - - let mut net_packet = if self.compressor.compress(&net_packet, &mut auxiliary)? { - auxiliary.set_default_version(); - auxiliary.set_protocol(protocol::Protocol::IpTurn); - auxiliary.set_transport_protocol(ip_turn_packet::Protocol::Ipv4.into()); - auxiliary.first_set_ttl(6); - auxiliary.set_source(src_ip); - auxiliary.set_destination(dest_ip); - auxiliary - } else { - net_packet - }; - self.client_cipher.encrypt_ipv4(&mut net_packet)?; - if dest_ip.is_broadcast() || dest_ip == device_info.broadcast_ip { - //走服务端广播 - self.context - .send_default(&net_packet, device_info.connect_server)?; - return Ok(()); - } - - if device_info.not_in_network(dest_ip) { - //不是一个网段的直接忽略 - return Ok(()); - } - self.context.send_ipv4_by_id( - &net_packet, - &dest_ip, - device_info.connect_server, - device_info.status.online(), - )?; - Ok(()) - } -} - -pub fn send_to_wg_broadcast( - sender: &ChannelContext, - net_packet: &NetPacket<&mut [u8]>, - server_cipher: &Cipher, - current_device: &CurrentDeviceInfo, -) -> anyhow::Result<()> { - let mut copy_packet = NetPacket::new0(net_packet.data_len(), [0; 65536])?; - copy_packet.set_default_version(); - copy_packet.set_protocol(protocol::Protocol::IpTurn); - copy_packet.set_transport_protocol(ip_turn_packet::Protocol::WGIpv4.into()); - copy_packet.first_set_ttl(6); - copy_packet.set_source(net_packet.source()); - copy_packet.set_destination(net_packet.destination()); - copy_packet.set_gateway_flag(true); - copy_packet.set_payload(net_packet.payload())?; - server_cipher.encrypt_ipv4(&mut copy_packet)?; - sender.send_default(©_packet, current_device.connect_server)?; - - Ok(()) -} -pub fn send_to_wg( - sender: &ChannelContext, - net_packet: &mut NetPacket<&mut [u8]>, - server_cipher: &Cipher, - current_device: &CurrentDeviceInfo, -) -> anyhow::Result<()> { - net_packet.set_transport_protocol(ip_turn_packet::Protocol::WGIpv4.into()); - net_packet.set_gateway_flag(true); - server_cipher.encrypt_ipv4(net_packet)?; - sender.send_default(&net_packet, current_device.connect_server)?; - - Ok(()) -} - -pub struct AcceptSocketSender { - sender: SyncSender, - notify: AcceptNotify, -} - -impl Clone for AcceptSocketSender { - fn clone(&self) -> Self { - Self { - sender: self.sender.clone(), - notify: self.notify.clone(), - } - } -} - -impl AcceptSocketSender { - pub fn new(notify: AcceptNotify, sender: SyncSender) -> Self { - Self { sender, notify } - } - pub fn try_add_socket(&self, t: T) -> io::Result<()> { - match self.sender.try_send(t) { - Ok(_) => self.notify.add_socket(), - Err(e) => match e { - TrySendError::Full(_) => Err(io::Error::from(io::ErrorKind::WouldBlock)), - TrySendError::Disconnected(_) => Err(io::Error::from(io::ErrorKind::WriteZero)), - }, - } - } -} -#[derive(Clone)] -pub struct PacketSender { - sender: Sender>, -} - -impl PacketSender { - pub fn new(sender: Sender>) -> Self { - Self { sender } - } - pub fn try_send(&self, buf: &[u8]) -> io::Result<()> { - match self.sender.try_send(buf.to_vec()) { - Ok(_) => Ok(()), - Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => Err(io::Error::new( - io::ErrorKind::WouldBlock, - "通道已满,发生丢包", - )), - Err(_) => Err(io::Error::new( - io::ErrorKind::ConnectionRefused, - "通道关闭,发生丢包", - )), - } - } -} - -#[derive(Clone)] -pub struct ConnectUtil { - connect_tcp: Sender<(Vec, Option, SocketAddr)>, - connect_ws: Sender<(Vec, String)>, -} - -impl ConnectUtil { - pub fn new( - connect_tcp: Sender<(Vec, Option, SocketAddr)>, - connect_ws: Sender<(Vec, String)>, - ) -> Self { - Self { - connect_tcp, - connect_ws, - } - } - pub fn try_connect_tcp(&self, buf: Vec, addr: SocketAddr) { - 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, addr: SocketAddr) { - // 打洞的连接可以绑定随机端口 - if self.connect_tcp.try_send((buf, Some(0), addr)).is_err() { - log::warn!("try_connect_tcp failed {}", addr); - } - } - pub fn try_connect_ws(&self, buf: Vec, addr: String) { - if self.connect_ws.try_send((buf, addr)).is_err() { - log::warn!("try_connect_ws failed"); - } - } -} diff --git a/vnt/src/channel/socket/mod.rs b/vnt/src/channel/socket/mod.rs deleted file mode 100644 index ccd88ae..0000000 --- a/vnt/src/channel/socket/mod.rs +++ /dev/null @@ -1,138 +0,0 @@ -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, -} - -pub async fn connect_tcp( - addr: SocketAddr, - bind_port: u16, - default_interface: &LocalInterface, -) -> anyhow::Result { - 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 { - create_tcp0(v4, 0, default_interface) -} -pub fn create_tcp0( - v4: bool, - bind_port: u16, - default_interface: &LocalInterface, -) -> anyhow::Result { - 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 { - 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 { - 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)) -} diff --git a/vnt/src/channel/socket/unix.rs b/vnt/src/channel/socket/unix.rs deleted file mode 100644 index a10807f..0000000 --- a/vnt/src/channel/socket/unix.rs +++ /dev/null @@ -1,46 +0,0 @@ -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 { -// 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 { -// Ok(LocalInterface::default()) -// } diff --git a/vnt/src/channel/socket/windows.rs b/vnt/src/channel/socket/windows.rs deleted file mode 100644 index 5322099..0000000 --- a/vnt/src/channel/socket/windows.rs +++ /dev/null @@ -1,56 +0,0 @@ -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 { -// // 获取最佳接口 -// 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 }) -// } diff --git a/vnt/src/channel/tcp_channel.rs b/vnt/src/channel/tcp_channel.rs deleted file mode 100644 index 4f6eb52..0000000 --- a/vnt/src/channel/tcp_channel.rs +++ /dev/null @@ -1,212 +0,0 @@ -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}; -use tokio::net::tcp::OwnedReadHalf; -use tokio::net::{TcpListener, TcpStream}; -use tokio::sync::mpsc::{channel, Receiver}; - -use crate::channel::context::ChannelContext; -use crate::channel::handler::RecvChannelHandler; -use crate::channel::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( - tcp_server: std::net::TcpListener, - receiver: Receiver<(Vec, Option, SocketAddr)>, - recv_handler: H, - context: ChannelContext, - stop_manager: StopManager, -) -> anyhow::Result<()> -where - H: RecvChannelHandler, -{ - let (stop_sender, stop_receiver) = tokio::sync::oneshot::channel::<()>(); - let worker = stop_manager.add_listener("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() - .build() - .context("tcp tokio runtime build failed")?; - thread::Builder::new() - .name("tcpChannel".into()) - .spawn(move || { - runtime.spawn(async move { - { - let recv_handler = recv_handler.clone(); - let context = context.clone(); - tokio::spawn(async move { - if let Err(e) = tcp_accept(tcp_server, recv_handler, context).await { - log::warn!("tcp_listen {:?}", e); - } - }); - } - tokio::spawn(async move { - connect_tcp_handle(receiver, recv_handler, context, bind_port).await - }); - }); - runtime.block_on(async { - let _ = stop_receiver.await; - }); - runtime.shutdown_background(); - worker.stop_all(); - }) - .context("tcp thread build failed")?; - Ok(()) -} - -async fn connect_tcp_handle( - mut receiver: Receiver<(Vec, Option, SocketAddr)>, - recv_handler: H, - context: ChannelContext, - listener_bind_port: u16, -) where - H: RecvChannelHandler, -{ - 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, bind_port).await { - log::warn!("连接失败,链接终止:{:?},{:?}", addr, e); - } - }); - } -} - -async fn connect_tcp0( - data: Vec, - addr: SocketAddr, - recv_handler: H, - context: ChannelContext, - bind_port: u16, -) -> anyhow::Result<()> -where - H: RecvChannelHandler, -{ - 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; - Ok(()) -} - -async fn tcp_accept( - tcp_server: std::net::TcpListener, - recv_handler: H, - context: ChannelContext, -) -> anyhow::Result<()> -where - H: RecvChannelHandler, -{ - let tcp_server = TcpListener::from_std(tcp_server)?; - - loop { - let (stream, addr) = tcp_server.accept().await?; - - tcp_stream_handle(stream, addr, recv_handler.clone(), context.clone()).await; - } -} - -pub async fn tcp_stream_handle( - stream: TcpStream, - addr: SocketAddr, - recv_handler: H, - context: ChannelContext, -) where - 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::>(100); - context - .packet_map - .write() - .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 { - log::info!("发送失败,tcp链接终止:{:?},{:?}", addr, e); - break; - } - } - let _ = w.shutdown().await; - }); - tokio::spawn(async move { - 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(&route_key); - }); -} - -async fn tcp_write(w: &mut W, buf: &[u8]) -> anyhow::Result<()> { - let len = buf.len(); - if len > TCP_MAX_PACKET_SIZE { - return Err(anyhow!("超过了tcp的最大长度传输")); - } - w.write_all(&[0, (len >> 16) as u8, (len >> 8) as u8, len as u8]) - .await?; - w.write_all(&buf).await?; - Ok(()) -} - -async fn tcp_read( - mut read: OwnedReadHalf, - addr: SocketAddr, - context: &ChannelContext, - recv_handler: H, - route_key: RouteKey, -) -> anyhow::Result<()> -where - H: RecvChannelHandler, -{ - let mut head = [0; 4]; - let mut buf = [0; BUFFER_SIZE]; - let mut extend = [0; BUFFER_SIZE]; - loop { - read.read_exact(&mut head).await?; - if head[0] != 0 { - return Err(anyhow!("tcp数据流错误 {}", addr)); - } - let len = ((head[1] as usize) << 16) | ((head[2] as usize) << 8) | head[3] as usize; - if len < 12 || len > buf.len() { - return Err(anyhow!("tcp数据长度无效 {}", addr)); - } - read.read_exact(&mut buf[..len]).await?; - recv_handler.handle(&mut buf[..len], &mut extend, route_key, context); - } -} diff --git a/vnt/src/channel/udp_channel.rs b/vnt/src/channel/udp_channel.rs deleted file mode 100644 index 213de42..0000000 --- a/vnt/src/channel/udp_channel.rs +++ /dev/null @@ -1,370 +0,0 @@ -use std::sync::mpsc::{sync_channel, Receiver}; -use std::{io, thread}; - -use mio::event::Source; -use mio::net::UdpSocket; -use mio::{Events, Interest, Poll, Token, Waker}; - -use crate::channel::context::ChannelContext; -use crate::channel::handler::RecvChannelHandler; -use crate::channel::notify::AcceptNotify; -use crate::channel::sender::AcceptSocketSender; -use crate::channel::{ConnectProtocol, RouteKey, BUFFER_SIZE}; -use crate::util::StopManager; - -pub fn udp_listen( - stop_manager: StopManager, - recv_handler: H, - context: ChannelContext, -) -> anyhow::Result>>> -where - H: RecvChannelHandler, -{ - main_udp_listen(stop_manager.clone(), recv_handler.clone(), context.clone())?; - sub_udp_listen(stop_manager, recv_handler, context) -} - -const NOTIFY: Token = Token(0); - -fn sub_udp_listen( - stop_manager: StopManager, - recv_handler: H, - context: ChannelContext, -) -> anyhow::Result>>> -where - H: RecvChannelHandler, -{ - let (udp_sender, udp_receiver) = sync_channel(64); - let poll = Poll::new()?; - let waker = AcceptNotify::new(Waker::new(poll.registry(), NOTIFY)?); - let worker = { - let waker = waker.clone(); - stop_manager.add_listener("sub_udp_listen".into(), move || { - if let Err(e) = waker.stop() { - log::error!("{:?}", e); - } - })? - }; - let accept = AcceptSocketSender::new(waker.clone(), udp_sender); - thread::Builder::new() - .name("subUdp".into()) - .spawn(move || { - if let Err(e) = sub_udp_listen0(poll, recv_handler, context, waker, udp_receiver) { - log::error!("{:?}", e); - } - worker.stop_all(); - })?; - Ok(accept) -} - -fn sub_udp_listen0( - mut poll: Poll, - recv_handler: H, - context: ChannelContext, - accept_notify: AcceptNotify, - accept_receiver: Receiver>>, -) -> io::Result<()> -where - H: RecvChannelHandler, -{ - let mut events = Events::with_capacity(1024); - let mut buf = [0; BUFFER_SIZE]; - let mut extend = [0; BUFFER_SIZE]; - let mut list: Vec = 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)?; - continue; - } - for event in events.iter() { - match event.token() { - NOTIFY => { - if accept_notify.is_stop() { - return Ok(()); - } - if accept_notify.is_add_socket() { - while let Ok(option) = accept_receiver.try_recv() { - match option { - None => { - log::info!("切换成锥形模式"); - for mut udp_socket in 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() - { - poll.registry().register( - &mut udp_socket, - Token(index), - Interest::READABLE, - )?; - list.push(udp_socket); - } - } - } - } - } - } - 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, index + main_len, addr), - &context, - ); - } - Err(e) => { - if e.kind() == io::ErrorKind::WouldBlock { - break; - } - log::error!("{:?}", e); - } - } - } - } - } - } - } - } -} - -// /// 阻塞 -// fn main_udp_listen( -// stop_manager: StopManager, -// recv_handler: H, -// context: Context, -// ) -> io::Result<()> -// where -// H: RecvChannelHandler, -// { -// for index in 0..context.main_udp_socket.len() { -// let stop_manager = stop_manager.clone(); -// let context = context.clone(); -// let recv_handler = recv_handler.clone(); -// thread::Builder::new() -// .name(format!("mainUdp{}", index)) -// .spawn(move || { -// if let Err(e) = main_udp_listen0(stop_manager, index, recv_handler, context) { -// log::error!("{:?}", e); -// } -// })?; -// } -// Ok(()) -// } -// -// pub fn main_udp_listen0( -// stop_manager: StopManager, -// index: usize, -// mut recv_handler: H, -// context: Context, -// ) -> io::Result<()> -// where -// H: RecvChannelHandler, -// { -// use std::time::Duration; -// let udp_socket = &context.main_udp_socket[index]; -// udp_socket.set_read_timeout(Some(Duration::from_secs(5)))?; -// udp_socket.set_write_timeout(Some(Duration::from_secs(1)))?; -// let local_addr = udp_socket.local_addr()?; -// let worker = stop_manager.add_listener(format!("main_udp_{}", index), move || { -// if let Ok(udp) = std::net::UdpSocket::bind("0.0.0.0:0") { -// let _ = udp.send_to(b"stop", format!("127.0.0.1:{}", local_addr.port())); -// } -// })?; -// -// let mut buf = [0; BUFFER_SIZE]; -// loop { -// match udp_socket.recv_from(&mut buf) { -// Ok((len, addr)) => { -// if &buf[..len] == b"stop" { -// if stop_manager.is_stop() { -// break; -// } -// } -// recv_handler.handle(&mut buf[..len], RouteKey::new(false, index, addr), &context); -// } -// Err(e) => { -// if stop_manager.is_stop() { -// break; -// } -// log::error!("index={},{:?},{}", index, udp_socket.local_addr(), e) -// } -// } -// } -// worker.stop_all(); -// Ok(()) -// } - -/// 非阻塞 -fn main_udp_listen( - stop_manager: StopManager, - recv_handler: H, - context: ChannelContext, -) -> anyhow::Result<()> -where - H: RecvChannelHandler, -{ - use std::sync::Arc; - let poll = Poll::new()?; - let waker = Arc::new(Waker::new(poll.registry(), NOTIFY)?); - let _waker = waker.clone(); - let worker = stop_manager.add_listener("main_udp".into(), move || { - if let Err(e) = waker.wake() { - log::error!("{:?}", e); - } - })?; - thread::Builder::new() - .name("mainUdp".into()) - .spawn(move || { - if let Err(e) = main_udp_listen0(poll, recv_handler, context) { - log::error!("{:?}", e); - } - drop(_waker); - worker.stop_all(); - })?; - Ok(()) -} - -pub fn main_udp_listen0( - mut poll: Poll, - recv_handler: H, - context: ChannelContext, -) -> io::Result<()> -where - H: RecvChannelHandler, -{ - let mut buf = [0; BUFFER_SIZE]; - let mut udps = Vec::with_capacity(context.main_udp_socket.len()); - - for (index, udp) in context.main_udp_socket.iter().enumerate() { - let udp_socket = udp.try_clone()?; - udp_socket.set_nonblocking(true)?; - let mut mio_udp = UdpSocket::from_std(udp_socket); - poll.registry() - .register(&mut mio_udp, Token(index + 1), Interest::READABLE)?; - udps.push(mio_udp); - } - - let mut events = Events::with_capacity(udps.len()); - let mut extend = [0; BUFFER_SIZE]; - loop { - if let Err(e) = poll.poll(&mut events, None) { - crate::ignore_io_interrupted(e)?; - continue; - } - for x in events.iter() { - let index = match x.token() { - NOTIFY => return Ok(()), - // 0的位置留给NOTIFY了,这里要再减回去,因为路由是通过index来找到对应udp的 - Token(index) => index - 1, - }; - let udp = if let Some(udp) = udps.get(index) { - udp - } else { - log::error!("{:?}", x); - continue; - }; - loop { - match udp.recv_from(&mut buf) { - Ok((len, addr)) => { - recv_handler.handle( - &mut buf[..len], - &mut extend, - RouteKey::new(ConnectProtocol::UDP, index, addr), - &context, - ); - } - Err(e) => { - if e.kind() == io::ErrorKind::WouldBlock { - break; - } - log::error!("main_udp_listen_{}={:?}", index, e); - } - } - } - } - } -} -// /// 用recvmmsg没什么帮助,这里记录下,以下是完整代码 -// #[cfg(unix)] -// pub fn main_udp_listen0(index: usize, mut recv_handler: H, context: Context) -> io::Result<()> -// where -// H: RecvChannelHandler, -// { -// use libc::{c_uint, mmsghdr, sockaddr_storage, socklen_t, timespec}; -// use std::os::fd::AsRawFd; -// -// let udp_socket = context.main_udp_socket[index].try_clone()?; -// let fd = udp_socket.as_raw_fd(); -// const MAX_MESSAGES: usize = 16; -// let mut iov: [libc::iovec; MAX_MESSAGES] = unsafe { std::mem::zeroed() }; -// let mut buf: [[u8; BUFFER_SIZE]; MAX_MESSAGES] = [[0; BUFFER_SIZE]; MAX_MESSAGES]; -// let mut msgs: [mmsghdr; MAX_MESSAGES] = unsafe { std::mem::zeroed() }; -// let mut addrs: [sockaddr_storage; MAX_MESSAGES] = unsafe { std::mem::zeroed() }; -// for i in 0..MAX_MESSAGES { -// iov[i].iov_base = buf[i].as_mut_ptr() as *mut libc::c_void; -// iov[i].iov_len = BUFFER_SIZE; -// msgs[i].msg_hdr.msg_iov = &mut iov[i]; -// msgs[i].msg_hdr.msg_iovlen = 1; -// msgs[i].msg_hdr.msg_name = &mut addrs[i] as *const _ as *mut libc::c_void; -// msgs[i].msg_hdr.msg_namelen = std::mem::size_of::() as socklen_t; -// } -// let mut time: timespec = unsafe { std::mem::zeroed() }; -// loop { -// if context.is_stop() { -// return Ok(()); -// } -// let res = -// unsafe { libc::recvmmsg(fd, msgs.as_mut_ptr(), MAX_MESSAGES as c_uint, 0, &mut time) }; -// if res == -1 { -// log::error!("main_udp_listen_{}={:?}", index, io::Error::last_os_error()); -// continue; -// } -// -// let nmsgs = res as usize; -// for i in 0..nmsgs { -// let msg = &mut buf[i][0..msgs[i].msg_len as usize]; -// let addr = sockaddr_to_socket_addr(&addrs[i], msgs[i].msg_hdr.msg_namelen); -// if msg == b"stop" { -// if context.is_stop() { -// return Ok(()); -// } -// } -// recv_handler.handle(msg, RouteKey::new(false, index, addr), &context); -// } -// } -// } -// -// #[cfg(unix)] -// fn sockaddr_to_socket_addr(addr: &libc::sockaddr_storage, _len: libc::socklen_t) -> SocketAddr { -// match addr.ss_family as libc::c_int { -// libc::AF_INET => { -// let addr_in = unsafe { *(addr as *const _ as *const libc::sockaddr_in) }; -// let ip = u32::from_be(addr_in.sin_addr.s_addr); -// let port = u16::from_be(addr_in.sin_port); -// SocketAddr::V4(std::net::SocketAddrV4::new(Ipv4Addr::from(ip), port)) -// } -// libc::AF_INET6 => { -// let addr_in6 = unsafe { *(addr as *const _ as *const libc::sockaddr_in6) }; -// let ip = std::net::Ipv6Addr::from(addr_in6.sin6_addr.s6_addr); -// let port = u16::from_be(addr_in6.sin6_port); -// SocketAddr::V6(std::net::SocketAddrV6::new(ip, port, 0, 0)) -// } -// _ => panic!("Unsupported address family"), -// } -// } diff --git a/vnt/src/channel/ws_channel.rs b/vnt/src/channel/ws_channel.rs deleted file mode 100644 index b11eed8..0000000 --- a/vnt/src/channel/ws_channel.rs +++ /dev/null @@ -1,173 +0,0 @@ -use crate::channel::{ConnectProtocol, RouteKey, BUFFER_SIZE}; -use anyhow::Context; -use futures_util::stream::SplitStream; -use futures_util::{SinkExt, StreamExt}; -use std::convert::Into; -use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; -use std::thread; -use std::time::Duration; -use tokio::net::TcpStream; -use tokio::sync::mpsc::{channel, Receiver}; -use tokio_tungstenite::tungstenite::http::StatusCode; -use tokio_tungstenite::tungstenite::{Error, Message}; -use tokio_tungstenite::{connect_async, MaybeTlsStream, WebSocketStream}; - -use crate::channel::context::ChannelContext; -use crate::channel::handler::RecvChannelHandler; -use crate::channel::sender::PacketSender; -use crate::util::StopManager; - -/// ws协议, -/// 暂时只允许用ws连服务端,不能用ws打洞/连客户端 -pub fn ws_connect_accept( - receiver: Receiver<(Vec, String)>, - recv_handler: H, - context: ChannelContext, - stop_manager: StopManager, -) -> anyhow::Result<()> -where - H: RecvChannelHandler, -{ - let (stop_sender, stop_receiver) = tokio::sync::oneshot::channel::<()>(); - let worker = stop_manager.add_listener("wsChannel".into(), move || { - let _ = stop_sender.send(()); - })?; - let runtime = tokio::runtime::Builder::new_multi_thread() - .worker_threads(2) - .enable_all() - .build() - .context("ws tokio runtime build failed")?; - thread::Builder::new() - .name("wsChannel".into()) - .spawn(move || { - runtime.spawn(async move { connect_ws_handle(receiver, recv_handler, context).await }); - runtime.block_on(async { - let _ = stop_receiver.await; - }); - runtime.shutdown_background(); - worker.stop_all(); - }) - .context("ws thread build failed")?; - Ok(()) -} - -async fn connect_ws_handle( - mut receiver: Receiver<(Vec, String)>, - recv_handler: H, - context: ChannelContext, -) where - H: RecvChannelHandler, -{ - 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, index).await { - log::warn!("发送失败,ws链接终止:{:?}", e); - } - }); - index += 1; - } -} -const WS_ADDR: SocketAddr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)); - -async fn connect_ws( - data: Vec, - mut url: String, - recv_handler: H, - context: ChannelContext, - index: usize, -) -> anyhow::Result<()> -where - H: RecvChannelHandler, -{ - let mut count = 0; - log::info!("尝试建立连接 {:?}", url); - let (mut ws, response) = loop { - count += 1; - if count > 3 { - Err(anyhow::anyhow!("发生多次重定向,链接终止"))? - } - match tokio::time::timeout(Duration::from_secs(3), connect_async(url)).await? { - Ok(rs) => break rs, - Err(e) => { - if let Error::Http(res) = &e { - if res.status() == StatusCode::MOVED_PERMANENTLY - || res.status() == StatusCode::FOUND - || res.status() == StatusCode::SEE_OTHER - || res.status() == StatusCode::TEMPORARY_REDIRECT - || res.status() == StatusCode::PERMANENT_REDIRECT - { - if let Some(v) = res.headers().get("Location") { - if let Ok(redirect) = v.to_str() { - log::info!("url重定向响应头 {:?}", res.headers()); - log::info!("url重定向地址 {}", redirect); - // 替换协议前缀 - 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; - } - } - } - } - return Err(e)?; - } - } - }; - log::info!("ws协议握手 {:?}", response); - ws.send(Message::Binary(data)).await?; - let (mut ws_write, ws_read) = ws.split(); - let (sender, mut receiver) = channel::>(100); - let route_key = RouteKey::new(ConnectProtocol::WS, index, WS_ADDR); - - context - .packet_map - .write() - .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 { - log::warn!("websocket err {:?}", e); - break; - } - } - let _ = ws_write.close().await; - }); - if let Err(e) = ws_read_handle(ws_read, recv_handler, &context, route_key).await { - log::warn!("{:?}", e); - } - context.packet_map.write().remove(&route_key); - Ok(()) -} -async fn ws_read_handle( - mut ws_read: SplitStream>>, - recv_handler: H, - context: &ChannelContext, - route_key: RouteKey, -) -> anyhow::Result<()> -where - H: RecvChannelHandler, -{ - let mut extend = [0; BUFFER_SIZE]; - while let Some(msg) = ws_read.next().await { - let msg = msg.context("Error during WebSocket ")?; - match msg { - Message::Text(txt) => log::info!("Received text message: {}", txt), - Message::Binary(mut data) => { - recv_handler.handle(&mut data, &mut extend, route_key, context); - } - Message::Ping(_) | Message::Pong(_) => (), - Message::Close(_) => break, - _ => {} - } - } - Ok(()) -} diff --git a/vnt/src/cipher/aes_cbc/mod.rs b/vnt/src/cipher/aes_cbc/mod.rs deleted file mode 100644 index 6286a84..0000000 --- a/vnt/src/cipher/aes_cbc/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod rs_aes_cbc; -pub use rs_aes_cbc::*; diff --git a/vnt/src/cipher/aes_cbc/rs_aes_cbc.rs b/vnt/src/cipher/aes_cbc/rs_aes_cbc.rs deleted file mode 100644 index c765121..0000000 --- a/vnt/src/cipher/aes_cbc/rs_aes_cbc.rs +++ /dev/null @@ -1,153 +0,0 @@ -use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyIvInit}; -use anyhow::anyhow; -use rand::RngCore; - -use crate::cipher::Finger; -use crate::protocol::body::AesCbcSecretBody; -use crate::protocol::{NetPacket, HEAD_LEN}; - -type Aes128CbcEnc = cbc::Encryptor; -type Aes128CbcDec = cbc::Decryptor; -type Aes256CbcEnc = cbc::Encryptor; -type Aes256CbcDec = cbc::Decryptor; - -#[derive(Clone)] -pub struct AesCbcCipher { - pub(crate) cipher: AesCbcEnum, - pub(crate) finger: Option, -} - -#[derive(Clone)] -pub enum AesCbcEnum { - AES128CBC([u8; 16]), - AES256CBC([u8; 32]), -} - -impl AesCbcCipher { - pub fn key(&self) -> &[u8] { - match &self.cipher { - AesCbcEnum::AES128CBC(key) => key, - AesCbcEnum::AES256CBC(key) => key, - } - } -} - -impl AesCbcCipher { - pub fn new_128(key: [u8; 16], finger: Option) -> Self { - Self { - cipher: AesCbcEnum::AES128CBC(key), - finger, - } - } - pub fn new_256(key: [u8; 32], finger: Option) -> Self { - Self { - cipher: AesCbcEnum::AES256CBC(key), - finger, - } - } - - pub fn decrypt_ipv4 + AsMut<[u8]>>( - &self, - net_packet: &mut NetPacket, - ) -> anyhow::Result<()> { - if !net_packet.is_encrypt() { - //未加密的数据直接丢弃 - return Err(anyhow!("not encrypt")); - } - if net_packet.payload().len() < 16 { - log::error!("数据异常,长度{}小于{}", net_packet.payload().len(), 16); - return Err(anyhow!("aes_cbc data err")); - } - let mut iv = [0; 16]; - iv[0..12].copy_from_slice(&net_packet.head_tag()); - if let Some(finger) = &self.finger { - iv[12..16].copy_from_slice(&finger.hash[0..4]); - } - - let mut secret_body = - AesCbcSecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; - if let Some(finger) = &self.finger { - let finger = finger.calculate_finger(&iv[..12], secret_body.en_body()); - if &finger != secret_body.finger() { - return Err(anyhow!("aes_cbc finger err")); - } - } - let rs = match &self.cipher { - AesCbcEnum::AES128CBC(key) => Aes128CbcDec::new(&(*key).into(), &iv.into()) - .decrypt_padded_mut::(secret_body.en_body_mut()), - AesCbcEnum::AES256CBC(key) => Aes256CbcDec::new(&(*key).into(), &iv.into()) - .decrypt_padded_mut::(secret_body.en_body_mut()), - }; - match rs { - Ok(buf) => { - let len = buf.len(); - net_packet.set_encrypt_flag(false); - //减去末尾的随机数 - net_packet.set_data_len(HEAD_LEN + len - 4)?; - Ok(()) - } - Err(e) => Err(anyhow!("aes_cbc 解密失败:{}", e)), - } - } - /// net_packet 必须预留足够长度 - /// data_len是有效载荷的长度 - pub fn encrypt_ipv4 + AsMut<[u8]>>( - &self, - net_packet: &mut NetPacket, - ) -> anyhow::Result<()> { - let data_len = net_packet.data_len(); - let mut iv = [0; 16]; - iv[0..12].copy_from_slice(&net_packet.head_tag()); - if let Some(finger) = &self.finger { - iv[12..16].copy_from_slice(&finger.hash[0..4]); - net_packet.set_data_len(data_len + 16)?; - } else { - net_packet.set_data_len(data_len + 4)?; - } - //先扩充随机数 - let mut secret_body = - AesCbcSecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; - secret_body.set_random(rand::thread_rng().next_u32()); - let p_len = secret_body.en_body().len(); - net_packet.set_data_len_max(); - let rs = match &self.cipher { - AesCbcEnum::AES128CBC(key) => Aes128CbcEnc::new(&(*key).into(), &iv.into()) - .encrypt_padded_mut::(net_packet.payload_mut(), p_len), - AesCbcEnum::AES256CBC(key) => Aes256CbcEnc::new(&(*key).into(), &iv.into()) - .encrypt_padded_mut::(net_packet.payload_mut(), p_len), - }; - return match rs { - Ok(buf) => { - let len = buf.len(); - if let Some(finger) = &self.finger { - let finger = finger.calculate_finger(&iv[..12], buf); - //设置实际长度 - net_packet.set_data_len(HEAD_LEN + len + finger.len())?; - let mut secret_body = AesCbcSecretBody::new(net_packet.payload_mut(), true)?; - secret_body.set_finger(&finger)?; - } else { - net_packet.set_data_len(HEAD_LEN + len)?; - } - - net_packet.set_encrypt_flag(true); - Ok(()) - } - Err(e) => Err(anyhow!("aes_cbc 加密失败:{}", e)), - }; - } -} -#[test] -fn test_aes_cbc() { - let d = AesCbcCipher::new_128([0; 16], Some(Finger::new("123"))); - let mut p = NetPacket::new_encrypt([0; 100]).unwrap(); - let src = p.buffer().to_vec(); - d.encrypt_ipv4(&mut p).unwrap(); - d.decrypt_ipv4(&mut p).unwrap(); - assert_eq!(p.buffer(), &src); - let d = AesCbcCipher::new_128([0; 16], None); - let mut p = NetPacket::new_encrypt([0; 100]).unwrap(); - let src = p.buffer().to_vec(); - d.encrypt_ipv4(&mut p).unwrap(); - d.decrypt_ipv4(&mut p).unwrap(); - assert_eq!(p.buffer(), &src); -} diff --git a/vnt/src/cipher/aes_ecb/mod.rs b/vnt/src/cipher/aes_ecb/mod.rs deleted file mode 100644 index eab811f..0000000 --- a/vnt/src/cipher/aes_ecb/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -#[cfg(not(any(feature = "openssl-vendored", feature = "openssl")))] -mod rs_aes_ecb; -#[cfg(not(any(feature = "openssl-vendored", feature = "openssl")))] -pub use rs_aes_ecb::*; - -#[cfg(any(feature = "openssl-vendored", feature = "openssl"))] -mod openssl_aes_ecb; -#[cfg(any(feature = "openssl-vendored", feature = "openssl"))] -pub use openssl_aes_ecb::*; diff --git a/vnt/src/cipher/aes_ecb/openssl_aes_ecb.rs b/vnt/src/cipher/aes_ecb/openssl_aes_ecb.rs deleted file mode 100644 index 338865e..0000000 --- a/vnt/src/cipher/aes_ecb/openssl_aes_ecb.rs +++ /dev/null @@ -1,219 +0,0 @@ -use std::ptr; - -use anyhow::anyhow; -use libc::c_int; -use openssl_sys::EVP_CIPHER_CTX; - -use crate::cipher::Finger; -use crate::protocol::{NetPacket, HEAD_LEN}; - -pub struct AesEcbCipher { - key: Vec, - pub(crate) en_ctx: *mut EVP_CIPHER_CTX, - pub(crate) de_ctx: *mut EVP_CIPHER_CTX, - pub(crate) finger: Option, -} - -impl Drop for AesEcbCipher { - fn drop(&mut self) { - unsafe { - openssl_sys::EVP_CIPHER_CTX_free(self.de_ctx); - openssl_sys::EVP_CIPHER_CTX_free(self.en_ctx); - } - } -} - -impl Clone for AesEcbCipher { - fn clone(&self) -> Self { - if self.key.len() == 16 { - AesEcbCipher::new_128(self.key.clone().try_into().unwrap(), self.finger.clone()) - } else { - AesEcbCipher::new_256(self.key.clone().try_into().unwrap(), self.finger.clone()) - } - } -} - -unsafe impl Sync for AesEcbCipher {} - -unsafe impl Send for AesEcbCipher {} - -impl AesEcbCipher { - pub fn key(&self) -> &[u8] { - &self.key - } -} - -impl AesEcbCipher { - pub fn new_128(key: [u8; 16], finger: Option) -> Self { - unsafe { - let cipher = openssl_sys::EVP_aes_128_ecb(); - let en_ctx = openssl_sys::EVP_CIPHER_CTX_new(); - openssl_sys::EVP_EncryptInit_ex( - en_ctx, - cipher, - ptr::null_mut(), - key.as_ptr(), - ptr::null(), - ); - - let de_ctx = openssl_sys::EVP_CIPHER_CTX_new(); - openssl_sys::EVP_DecryptInit_ex( - de_ctx, - cipher, - ptr::null_mut(), - key.as_ptr(), - ptr::null(), - ); - Self { - key: key.to_vec(), - en_ctx, - de_ctx, - finger, - } - } - } - pub fn new_256(key: [u8; 32], finger: Option) -> Self { - unsafe { - let cipher = openssl_sys::EVP_aes_256_ecb(); - let en_ctx = openssl_sys::EVP_CIPHER_CTX_new(); - openssl_sys::EVP_EncryptInit_ex( - en_ctx, - cipher, - ptr::null_mut(), - key.as_ptr(), - ptr::null(), - ); - let de_ctx = openssl_sys::EVP_CIPHER_CTX_new(); - openssl_sys::EVP_DecryptInit_ex( - de_ctx, - cipher, - ptr::null_mut(), - key.as_ptr(), - ptr::null(), - ); - Self { - key: key.to_vec(), - en_ctx, - de_ctx, - finger, - } - } - } - - pub fn decrypt_ipv4 + AsMut<[u8]>>( - &self, - net_packet: &mut NetPacket, - ) -> anyhow::Result<()> { - if !net_packet.is_encrypt() { - //未加密的数据直接丢弃 - return Err(anyhow!("not encrypt")); - } - - if let Some(finger) = &self.finger { - let nonce_raw = net_packet.head_tag(); - let len = net_packet.payload().len(); - if len < 12 { - return Err(anyhow!("data len err")); - } - let secret_body = &net_packet.payload()[..len - 12]; - let finger = finger.calculate_finger(&nonce_raw, secret_body); - if &finger != &net_packet.payload()[len - 12..] { - return Err(anyhow!("finger err")); - } - net_packet.set_data_len(net_packet.data_len() - finger.len())?; - } - if net_packet.payload().len() < 16 { - log::error!("数据异常,长度{}小于{}", net_packet.payload().len(), 16); - return Err(anyhow!("data err")); - } - let input = net_packet.payload(); - let mut out = [0u8; 1024 * 5]; - let mut out_len = 0; - let ctx = self.de_ctx; - unsafe { - let out_ptr = out.as_mut_ptr(); - let in_len = input.len() as c_int; - openssl_sys::EVP_DecryptUpdate(ctx, out_ptr, &mut out_len, input.as_ptr(), in_len); - let mut last_len = 0; - openssl_sys::EVP_DecryptFinal_ex(ctx, out_ptr.offset(out_len as isize), &mut last_len); - out_len += last_len; - } - let out_len = out_len as usize; - let text = &out[..out_len]; - { - //校验头部 - let src_net_packet = NetPacket::new(text)?; - if src_net_packet.source() != net_packet.source() { - return Err(anyhow!("data err")); - } - if src_net_packet.destination() != net_packet.destination() { - return Err(anyhow!("data err")); - } - if src_net_packet.protocol() != net_packet.protocol() { - return Err(anyhow!("data err")); - } - if src_net_packet.transport_protocol() != net_packet.transport_protocol() { - return Err(anyhow!("data err")); - } - if src_net_packet.is_gateway() != net_packet.is_gateway() { - return Err(anyhow!("data err")); - } - if src_net_packet.source_ttl() != net_packet.source_ttl() { - return Err(anyhow!("data err")); - } - } - net_packet.set_encrypt_flag(false); - net_packet.set_data_len(out_len)?; - net_packet.set_payload(&text[12..])?; - Ok(()) - } - /// net_packet 必须预留足够长度 大于 12+16+16 - /// data_len是有效载荷的长度 - pub fn encrypt_ipv4 + AsMut<[u8]>>( - &self, - net_packet: &mut NetPacket, - ) -> anyhow::Result<()> { - let input = net_packet.buffer(); - let mut out = [0u8; 1024 * 5]; - let mut out_len = 0; - let ctx = self.en_ctx; - //将头部也参与加密 - unsafe { - let out_ptr = out.as_mut_ptr(); - let in_len = input.len() as c_int; - openssl_sys::EVP_EncryptUpdate(ctx, out_ptr, &mut out_len, input.as_ptr(), in_len); - let mut last_len = 0; - openssl_sys::EVP_EncryptFinal_ex(ctx, out_ptr.offset(out_len as isize), &mut last_len); - out_len += last_len; - } - let out_len = out_len as usize; - if out_len == 0 { - return Err(anyhow!("ciphertext len err")); - } - //密文 - let ciphertext = &out[..out_len]; - net_packet.set_data_len(HEAD_LEN + out_len)?; - net_packet.payload_mut().copy_from_slice(ciphertext); - net_packet.set_encrypt_flag(true); - if let Some(finger) = &self.finger { - let nonce_raw = net_packet.head_tag(); - let finger = finger.calculate_finger(&nonce_raw, ciphertext); - let src_data_len = net_packet.data_len(); - //设置实际长度 - net_packet.set_data_len(src_data_len + finger.len())?; - - net_packet.buffer_mut()[src_data_len..].copy_from_slice(&finger); - } - Ok(()) - } -} - -#[test] -fn test_openssl_aes_ecb() { - let d = AesEcbCipher::new_128([0; 16], Some(Finger::new("123"))); - let mut p = NetPacket::new_encrypt([0; 100]).unwrap(); - let src = p.buffer().to_vec(); - d.encrypt_ipv4(&mut p).unwrap(); - d.decrypt_ipv4(&mut p).unwrap(); - assert_eq!(p.buffer(), &src); -} diff --git a/vnt/src/cipher/aes_ecb/rs_aes_ecb.rs b/vnt/src/cipher/aes_ecb/rs_aes_ecb.rs deleted file mode 100644 index ab68703..0000000 --- a/vnt/src/cipher/aes_ecb/rs_aes_ecb.rs +++ /dev/null @@ -1,154 +0,0 @@ -use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyInit}; -use anyhow::anyhow; - -use crate::cipher::Finger; -use crate::protocol::{NetPacket, HEAD_LEN}; - -type Aes128EcbEnc = ecb::Encryptor; -type Aes128EcbDec = ecb::Decryptor; -type Aes256EcbEnc = ecb::Encryptor; -type Aes256EcbDec = ecb::Decryptor; - -#[derive(Clone)] -pub struct AesEcbCipher { - key: AesEcbEnum, - pub(crate) finger: Option, -} - -#[derive(Clone, Copy)] -pub enum AesEcbEnum { - AES128ECB([u8; 16]), - AES256ECB([u8; 32]), -} - -impl AesEcbCipher { - pub fn key(&self) -> &[u8] { - match &self.key { - AesEcbEnum::AES128ECB(key) => key, - AesEcbEnum::AES256ECB(key) => key, - } - } -} - -impl AesEcbCipher { - pub fn new_128(key: [u8; 16], finger: Option) -> Self { - Self { - key: AesEcbEnum::AES128ECB(key), - finger, - } - } - pub fn new_256(key: [u8; 32], finger: Option) -> Self { - Self { - key: AesEcbEnum::AES256ECB(key), - finger, - } - } - - pub fn decrypt_ipv4 + AsMut<[u8]>>( - &self, - net_packet: &mut NetPacket, - ) -> anyhow::Result<()> { - if !net_packet.is_encrypt() { - //未加密的数据直接丢弃 - return Err(anyhow!("not encrypt")); - } - - if let Some(finger) = &self.finger { - let nonce_raw = net_packet.head_tag(); - let len = net_packet.payload().len(); - if len < 12 { - return Err(anyhow!("payload len <12")); - } - let secret_body = &net_packet.payload()[..len - 12]; - let finger = finger.calculate_finger(&nonce_raw, secret_body); - if &finger != &net_packet.payload()[len - 12..] { - return Err(anyhow!("finger err")); - } - net_packet.set_data_len(net_packet.data_len() - finger.len())?; - } - if net_packet.payload().len() < 16 { - log::error!("数据异常,长度{}小于{}", net_packet.payload().len(), 16); - return Err(anyhow!("data err")); - } - let mut out = [0u8; 1024 * 5]; - let rs = match self.key { - AesEcbEnum::AES128ECB(key) => Aes128EcbDec::new(&key.into()) - .decrypt_padded_b2b_mut::(net_packet.payload(), &mut out), - AesEcbEnum::AES256ECB(key) => Aes256EcbDec::new(&key.into()) - .decrypt_padded_b2b_mut::(net_packet.payload(), &mut out), - }; - match rs { - Ok(buf) => { - //校验头部 - let src_net_packet = NetPacket::new(buf)?; - if src_net_packet.source() != net_packet.source() { - return Err(anyhow!("data err")); - } - if src_net_packet.destination() != net_packet.destination() { - return Err(anyhow!("data err")); - } - if src_net_packet.protocol() != net_packet.protocol() { - return Err(anyhow!("data err")); - } - if src_net_packet.transport_protocol() != net_packet.transport_protocol() { - return Err(anyhow!("data err")); - } - if src_net_packet.is_gateway() != net_packet.is_gateway() { - return Err(anyhow!("data err")); - } - if src_net_packet.source_ttl() != net_packet.source_ttl() { - return Err(anyhow!("data err")); - } - net_packet.set_data_len(buf.len())?; - net_packet.set_payload(src_net_packet.payload())?; - net_packet.set_encrypt_flag(false); - Ok(()) - } - Err(e) => Err(anyhow!("aes_ecb解密失败:{}", e)), - } - } - /// net_packet 必须预留足够长度 - /// data_len是有效载荷的长度 - pub fn encrypt_ipv4 + AsMut<[u8]>>( - &self, - net_packet: &mut NetPacket, - ) -> anyhow::Result<()> { - let mut out = [0u8; 1024 * 5]; - let rs = match self.key { - AesEcbEnum::AES128ECB(key) => Aes128EcbEnc::new(&key.into()) - .encrypt_padded_b2b_mut::(net_packet.buffer(), &mut out), - AesEcbEnum::AES256ECB(key) => Aes256EcbEnc::new(&key.into()) - .encrypt_padded_b2b_mut::(net_packet.buffer(), &mut out), - }; - - return match rs { - Ok(buf) => { - net_packet.set_data_len(HEAD_LEN + buf.len())?; - net_packet.set_payload(buf)?; - net_packet.set_encrypt_flag(true); - - if let Some(finger) = &self.finger { - let nonce_raw = net_packet.head_tag(); - let finger = finger.calculate_finger(&nonce_raw, buf); - let src_data_len = net_packet.data_len(); - //设置实际长度 - net_packet.set_data_len(src_data_len + finger.len())?; - - net_packet.buffer_mut()[src_data_len..].copy_from_slice(&finger); - } - Ok(()) - } - Err(e) => Err(anyhow!("aes_ecb加密失败:{}", e)), - }; - } -} - -#[test] -fn test_aes_ecb() { - let d = AesEcbCipher::new_128([0; 16], Some(Finger::new("123"))); - let mut p = NetPacket::new_encrypt([0; 100]).unwrap(); - let src = p.buffer().to_vec(); - d.encrypt_ipv4(&mut p).unwrap(); - d.decrypt_ipv4(&mut p).unwrap(); - assert_eq!(p.buffer(), &src) -} diff --git a/vnt/src/cipher/aes_gcm/aes_gcm_cipher.rs b/vnt/src/cipher/aes_gcm/aes_gcm_cipher.rs deleted file mode 100644 index 3b2ac78..0000000 --- a/vnt/src/cipher/aes_gcm/aes_gcm_cipher.rs +++ /dev/null @@ -1,132 +0,0 @@ -use aes_gcm::aead::consts::{U12, U16}; -use aes_gcm::aead::generic_array::GenericArray; -use aes_gcm::{AeadInPlace, Aes128Gcm, Aes256Gcm, Key, KeyInit, Nonce, Tag}; -use anyhow::anyhow; -use rand::RngCore; - -use crate::cipher::finger::Finger; -use crate::protocol::{body::SecretBody, body::AES_GCM_ENCRYPTION_RESERVED, NetPacket}; - -#[derive(Clone)] -pub struct AesGcmCipher { - pub(crate) cipher: AesGcmEnum, - pub(crate) finger: Option, -} - -#[derive(Clone)] -pub enum AesGcmEnum { - AES128GCM(Aes128Gcm), - AES256GCM(Aes256Gcm), -} - -impl AesGcmCipher { - pub fn new_128(key: [u8; 16], finger: Option) -> Self { - let key: &Key = &key.into(); - Self { - cipher: AesGcmEnum::AES128GCM(Aes128Gcm::new(key)), - finger, - } - } - pub fn new_256(key: [u8; 32], finger: Option) -> Self { - let key: &Key = &key.into(); - Self { - cipher: AesGcmEnum::AES256GCM(Aes256Gcm::new(key)), - finger, - } - } - - pub fn decrypt_ipv4 + AsMut<[u8]>>( - &self, - net_packet: &mut NetPacket, - ) -> anyhow::Result<()> { - if !net_packet.is_encrypt() { - //未加密的数据直接丢弃 - return Err(anyhow!("not encrypt")); - } - if net_packet.payload().len() < AES_GCM_ENCRYPTION_RESERVED { - log::error!("数据异常,长度小于{}", AES_GCM_ENCRYPTION_RESERVED); - return Err(anyhow!("data err")); - } - let nonce_raw = net_packet.head_tag(); - let nonce: &GenericArray = Nonce::from_slice(&nonce_raw); - - let mut secret_body = SecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; - let tag = secret_body.tag(); - if let Some(finger) = &self.finger { - let finger = finger.calculate_finger(&nonce_raw, secret_body.en_body()); - if &finger != secret_body.finger() { - return Err(anyhow!("finger err")); - } - } - let tag: GenericArray = Tag::clone_from_slice(tag); - let rs = match &self.cipher { - AesGcmEnum::AES128GCM(aes_gcm) => { - aes_gcm.decrypt_in_place_detached(nonce, &[], secret_body.body_mut(), &tag) - } - AesGcmEnum::AES256GCM(aes_gcm) => { - aes_gcm.decrypt_in_place_detached(nonce, &[], secret_body.body_mut(), &tag) - } - }; - if let Err(e) = rs { - return Err(anyhow!("解密失败:{}", e)); - } - net_packet.set_encrypt_flag(false); - net_packet.set_data_len(net_packet.data_len() - AES_GCM_ENCRYPTION_RESERVED)?; - return Ok(()); - } - /// net_packet 必须预留足够长度 - /// data_len是有效载荷的长度 - pub fn encrypt_ipv4 + AsMut<[u8]>>( - &self, - net_packet: &mut NetPacket, - ) -> anyhow::Result<()> { - if net_packet.reserve() < AES_GCM_ENCRYPTION_RESERVED { - return Err(anyhow!("too short")); - } - let nonce_raw = net_packet.head_tag(); - let nonce: &GenericArray = Nonce::from_slice(&nonce_raw); - let data_len = net_packet.data_len() + AES_GCM_ENCRYPTION_RESERVED; - net_packet.set_data_len(data_len)?; - let mut secret_body = SecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; - secret_body.set_random(rand::thread_rng().next_u32()); - let rs = match &self.cipher { - AesGcmEnum::AES128GCM(aes_gcm) => { - aes_gcm.encrypt_in_place_detached(nonce, &[], secret_body.body_mut()) - } - AesGcmEnum::AES256GCM(aes_gcm) => { - aes_gcm.encrypt_in_place_detached(nonce, &[], secret_body.body_mut()) - } - }; - return match rs { - Ok(tag) => { - secret_body.set_tag(tag.as_slice())?; - if let Some(finger) = &self.finger { - let finger = finger.calculate_finger(&nonce_raw, secret_body.en_body()); - secret_body.set_finger(&finger)?; - } - net_packet.set_encrypt_flag(true); - Ok(()) - } - Err(e) => Err(anyhow!("加密失败:{}", e)), - }; - } -} - -#[test] -fn test_aes_gcm() { - let d = AesGcmCipher::new_256([0; 32], Some(Finger::new("123"))); - let mut p = - NetPacket::new_encrypt([1; 13 + crate::protocol::body::ENCRYPTION_RESERVED]).unwrap(); - let src = p.buffer().to_vec(); - d.encrypt_ipv4(&mut p).unwrap(); - d.decrypt_ipv4(&mut p).unwrap(); - assert_eq!(p.buffer(), &src); - - let d = AesGcmCipher::new_256([0; 32], None); - let mut p = - NetPacket::new_encrypt([0; 13 + crate::protocol::body::ENCRYPTION_RESERVED]).unwrap(); - let src = p.buffer().to_vec(); - d.encrypt_ipv4(&mut p).unwrap(); - d.decrypt_ipv4(&mut p).unwrap(); - assert_eq!(p.buffer(), &src); -} diff --git a/vnt/src/cipher/aes_gcm/mod.rs b/vnt/src/cipher/aes_gcm/mod.rs deleted file mode 100644 index ade96e4..0000000 --- a/vnt/src/cipher/aes_gcm/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -#[cfg(feature = "ring-cipher")] -mod ring_aes_gcm_cipher; -#[cfg(feature = "ring-cipher")] -pub use ring_aes_gcm_cipher::*; - -#[cfg(not(feature = "ring-cipher"))] -mod aes_gcm_cipher; -#[cfg(not(feature = "ring-cipher"))] -pub use aes_gcm_cipher::*; diff --git a/vnt/src/cipher/aes_gcm/ring_aes_gcm_cipher.rs b/vnt/src/cipher/aes_gcm/ring_aes_gcm_cipher.rs deleted file mode 100644 index fbea94a..0000000 --- a/vnt/src/cipher/aes_gcm/ring_aes_gcm_cipher.rs +++ /dev/null @@ -1,148 +0,0 @@ -use anyhow::anyhow; -use rand::RngCore; -use ring::aead; -use ring::aead::{LessSafeKey, UnboundKey}; - -use crate::cipher::Finger; -use crate::protocol::body::{SecretBody, AES_GCM_ENCRYPTION_RESERVED}; -use crate::protocol::NetPacket; - -#[derive(Clone)] -pub struct AesGcmCipher { - pub(crate) cipher: AesGcmEnum, - pub(crate) finger: Option, -} - -pub enum AesGcmEnum { - AesGCM128(LessSafeKey, [u8; 16]), - AesGCM256(LessSafeKey, [u8; 32]), -} - -impl Clone for AesGcmEnum { - fn clone(&self) -> Self { - match &self { - AesGcmEnum::AesGCM128(_, key) => { - let c = - LessSafeKey::new(UnboundKey::new(&aead::AES_128_GCM, key.as_slice()).unwrap()); - AesGcmEnum::AesGCM128(c, *key) - } - AesGcmEnum::AesGCM256(_, key) => { - let c = - LessSafeKey::new(UnboundKey::new(&aead::AES_256_GCM, key.as_slice()).unwrap()); - AesGcmEnum::AesGCM256(c, *key) - } - } - } -} - -impl AesGcmCipher { - pub fn new_128(key: [u8; 16], finger: Option) -> Self { - let cipher = LessSafeKey::new(UnboundKey::new(&aead::AES_128_GCM, &key).unwrap()); - Self { - cipher: AesGcmEnum::AesGCM128(cipher, key), - finger, - } - } - pub fn new_256(key: [u8; 32], finger: Option) -> Self { - let cipher = LessSafeKey::new(UnboundKey::new(&aead::AES_256_GCM, &key).unwrap()); - Self { - cipher: AesGcmEnum::AesGCM256(cipher, key), - finger, - } - } - pub fn decrypt_ipv4 + AsMut<[u8]>>( - &self, - net_packet: &mut NetPacket, - ) -> anyhow::Result<()> { - if !net_packet.is_encrypt() { - //未加密的数据直接丢弃 - return Err(anyhow!("not encrypt")); - } - if net_packet.payload().len() < AES_GCM_ENCRYPTION_RESERVED { - log::error!("数据异常,长度小于{}", AES_GCM_ENCRYPTION_RESERVED); - return Err(anyhow!("data err")); - } - let nonce_raw = net_packet.head_tag(); - let nonce = aead::Nonce::assume_unique_for_key(nonce_raw); - let mut secret_body = SecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; - if let Some(finger) = &self.finger { - let finger = finger.calculate_finger(&nonce_raw, secret_body.en_body()); - if &finger != secret_body.finger() { - return Err(anyhow!("ring aes finger err")); - } - } - - let rs = match &self.cipher { - AesGcmEnum::AesGCM128(cipher, _) => { - cipher.open_in_place(nonce, aead::Aad::empty(), secret_body.en_body_mut()) - } - AesGcmEnum::AesGCM256(cipher, _) => { - cipher.open_in_place(nonce, aead::Aad::empty(), secret_body.en_body_mut()) - } - }; - if let Err(e) = rs { - return Err(anyhow!("解密失败:{}", e)); - } - net_packet.set_encrypt_flag(false); - net_packet.set_data_len(net_packet.data_len() - AES_GCM_ENCRYPTION_RESERVED)?; - return Ok(()); - } - /// net_packet 必须预留足够长度 - /// data_len是有效载荷的长度 - /// 返回加密后载荷的长度 - pub fn encrypt_ipv4 + AsMut<[u8]>>( - &self, - net_packet: &mut NetPacket, - ) -> anyhow::Result<()> { - let nonce_raw = net_packet.head_tag(); - let nonce = aead::Nonce::assume_unique_for_key(nonce_raw); - let data_len = net_packet.data_len() + AES_GCM_ENCRYPTION_RESERVED; - net_packet.set_data_len(data_len)?; - let mut secret_body = SecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; - secret_body.set_random(rand::thread_rng().next_u32()); - - let rs = match &self.cipher { - AesGcmEnum::AesGCM128(cipher, _) => { - cipher.seal_in_place_separate_tag(nonce, aead::Aad::empty(), secret_body.body_mut()) - } - AesGcmEnum::AesGCM256(cipher, _) => { - cipher.seal_in_place_separate_tag(nonce, aead::Aad::empty(), secret_body.body_mut()) - } - }; - return match rs { - Ok(tag) => { - let tag = tag.as_ref(); - if tag.len() != 16 { - return Err(anyhow!("加密tag长度错误:{}", tag.len())); - } - secret_body.set_tag(tag)?; - if let Some(finger) = &self.finger { - let finger = finger.calculate_finger(&nonce_raw, secret_body.en_body()); - secret_body.set_finger(&finger)?; - } - net_packet.set_encrypt_flag(true); - Ok(()) - } - Err(e) => Err(anyhow!("加密失败:{}", e)), - }; - } -} - -#[test] -fn test_aes_gcm() { - let d = AesGcmCipher::new_256([0; 32], Some(Finger::new("123"))); - let mut p = - NetPacket::new_encrypt([0; 13 + crate::protocol::body::ENCRYPTION_RESERVED]).unwrap(); - let src = p.buffer().to_vec(); - d.encrypt_ipv4(&mut p).unwrap(); - d.decrypt_ipv4(&mut p).unwrap(); - assert_eq!(p.buffer(), &src); - - let d = AesGcmCipher::new_256([0; 32], None); - let mut p = - NetPacket::new_encrypt([0; 13 + crate::protocol::body::ENCRYPTION_RESERVED]).unwrap(); - let src = p.buffer().to_vec(); - d.encrypt_ipv4(&mut p).unwrap(); - d.decrypt_ipv4(&mut p).unwrap(); - assert_eq!(p.buffer(), &src); -} diff --git a/vnt/src/cipher/chacha20/mod.rs b/vnt/src/cipher/chacha20/mod.rs deleted file mode 100644 index b249c3a..0000000 --- a/vnt/src/cipher/chacha20/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod rs_chacha20; -pub use rs_chacha20::*; diff --git a/vnt/src/cipher/chacha20/rs_chacha20.rs b/vnt/src/cipher/chacha20/rs_chacha20.rs deleted file mode 100644 index 3da6d7c..0000000 --- a/vnt/src/cipher/chacha20/rs_chacha20.rs +++ /dev/null @@ -1,107 +0,0 @@ -use aes::cipher::Iv; -use anyhow::anyhow; -use chacha20::cipher::{Key, KeyIvInit, StreamCipher}; -use chacha20::ChaCha20; - -use crate::cipher::finger::{gen_nonce, gen_random_nonce}; -use crate::cipher::Finger; -use crate::protocol::body::{ - IVSecretBody, SecretTail, SecretTailMut, FINGER_RESERVED, RANDOM_RESERVED, -}; -use crate::protocol::NetPacket; - -#[derive(Clone)] -pub struct ChaCha20Cipher { - key: [u8; 32], - pub(crate) finger: Option, -} - -impl ChaCha20Cipher { - pub fn new_256(key: [u8; 32], finger: Option) -> Self { - Self { key, finger } - } -} - -impl ChaCha20Cipher { - pub fn key(&self) -> &[u8] { - &self.key - } -} - -impl ChaCha20Cipher { - pub fn decrypt_ipv4 + AsMut<[u8]>>( - &self, - net_packet: &mut NetPacket, - ) -> anyhow::Result<()> { - if !net_packet.is_encrypt() { - //未加密的数据直接丢弃 - return Err(anyhow!("not encrypt")); - } - let mut head_tag = net_packet.head_tag(); - - let mut secret_body = IVSecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; - if let Some(finger) = &self.finger { - let finger = finger.calculate_finger(&head_tag, secret_body.data()); - if &finger != secret_body.finger() { - return Err(anyhow!("ChaCha20 finger err")); - } - } - gen_nonce(&mut head_tag, secret_body.random_buf()); - ChaCha20::new( - Key::::from_slice(&self.key), - Iv::::from_slice(&head_tag), - ) - .apply_keystream(secret_body.data_mut()); - let len = secret_body.data().len(); - net_packet.set_encrypt_flag(false); - net_packet.set_payload_len(len)?; - Ok(()) - } - pub fn encrypt_ipv4 + AsMut<[u8]>>( - &self, - net_packet: &mut NetPacket, - ) -> anyhow::Result<()> { - let data_len = net_packet.data_len(); - let head_tag = net_packet.head_tag(); - if let Some(_) = &self.finger { - net_packet.set_data_len(data_len + RANDOM_RESERVED + FINGER_RESERVED)?; - } else { - net_packet.set_data_len(data_len + RANDOM_RESERVED)?; - } - let mut secret_body = IVSecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; - let mut nonce = head_tag; - secret_body.set_random(&gen_random_nonce(&mut nonce)); - - ChaCha20::new( - Key::::from_slice(&self.key), - Iv::::from_slice(&nonce), - ) - .apply_keystream(secret_body.data_mut()); - if let Some(finger) = &self.finger { - let finger = finger.calculate_finger(&head_tag, secret_body.data()); - let mut secret_body = IVSecretBody::new(net_packet.payload_mut(), true)?; - secret_body.set_finger(&finger)?; - } - net_packet.set_encrypt_flag(true); - Ok(()) - } -} - -#[test] -fn test_chacha20() { - let d = ChaCha20Cipher::new_256([0; 32], Some(Finger::new("123"))); - let mut p = - NetPacket::new_encrypt([1; 13 + crate::protocol::body::ENCRYPTION_RESERVED]).unwrap(); - let src = p.buffer().to_vec(); - d.encrypt_ipv4(&mut p).unwrap(); - d.decrypt_ipv4(&mut p).unwrap(); - assert_eq!(p.buffer(), &src); - - let d = ChaCha20Cipher::new_256([0; 32], None); - let mut p = - NetPacket::new_encrypt([2; 13 + crate::protocol::body::ENCRYPTION_RESERVED]).unwrap(); - let src = p.buffer().to_vec(); - d.encrypt_ipv4(&mut p).unwrap(); - d.decrypt_ipv4(&mut p).unwrap(); - assert_eq!(p.buffer(), &src); -} diff --git a/vnt/src/cipher/chacha20_poly1305/mod.rs b/vnt/src/cipher/chacha20_poly1305/mod.rs deleted file mode 100644 index 1b4260f..0000000 --- a/vnt/src/cipher/chacha20_poly1305/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -#[cfg(feature = "ring-cipher")] -mod ring_chacha20_poly1305; -#[cfg(feature = "ring-cipher")] -pub use ring_chacha20_poly1305::*; - -#[cfg(not(feature = "ring-cipher"))] -mod rs_chacha20_poly1305; -#[cfg(not(feature = "ring-cipher"))] -pub use rs_chacha20_poly1305::*; diff --git a/vnt/src/cipher/chacha20_poly1305/ring_chacha20_poly1305.rs b/vnt/src/cipher/chacha20_poly1305/ring_chacha20_poly1305.rs deleted file mode 100644 index 1759a3c..0000000 --- a/vnt/src/cipher/chacha20_poly1305/ring_chacha20_poly1305.rs +++ /dev/null @@ -1,127 +0,0 @@ -use anyhow::anyhow; - -use ring::aead; -use ring::aead::{LessSafeKey, UnboundKey}; - -use crate::cipher::finger::{gen_nonce, gen_random_nonce}; -use crate::cipher::Finger; -use crate::protocol::body::{ - AEADSecretBody, SecretTail, SecretTailMut, FINGER_RESERVED, RANDOM_RESERVED, TAG_RESERVED, -}; -use crate::protocol::NetPacket; - -#[derive(Clone)] -pub struct ChaCha20Poly1305Cipher { - key: Vec, - pub(crate) cipher: LessSafeKey, - pub(crate) finger: Option, -} - -impl ChaCha20Poly1305Cipher { - pub fn new_256(key: [u8; 32], finger: Option) -> Self { - let cipher = LessSafeKey::new(UnboundKey::new(&aead::CHACHA20_POLY1305, &key).unwrap()); - Self { - key: key.to_vec(), - cipher, - finger, - } - } -} - -impl ChaCha20Poly1305Cipher { - pub fn key(&self) -> &[u8] { - &self.key - } -} - -impl ChaCha20Poly1305Cipher { - pub fn decrypt_ipv4 + AsMut<[u8]>>( - &self, - net_packet: &mut NetPacket, - ) -> anyhow::Result<()> { - if !net_packet.is_encrypt() { - //未加密的数据直接丢弃 - return Err(anyhow!("not encrypt")); - } - if net_packet.payload().len() < TAG_RESERVED { - log::error!("数据异常,长度小于{}", TAG_RESERVED); - return Err(anyhow!("data err")); - } - let mut head_tag = net_packet.head_tag(); - let mut secret_body = AEADSecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; - if let Some(finger) = &self.finger { - let finger = finger.calculate_finger(&head_tag, secret_body.data_tag_mut()); - if &finger != secret_body.finger() { - return Err(anyhow!("ring CHACHA20_POLY1305 finger err")); - } - } - gen_nonce(&mut head_tag, secret_body.random_buf()); - let nonce = aead::Nonce::assume_unique_for_key(head_tag); - let rs = self - .cipher - .open_in_place(nonce, aead::Aad::empty(), secret_body.data_tag_mut()); - if let Err(e) = rs { - return Err(anyhow!("ring CHACHA20_POLY1305 解密失败:{}", e)); - } - let len = secret_body.data().len(); - net_packet.set_encrypt_flag(false); - net_packet.set_payload_len(len)?; - return Ok(()); - } - /// net_packet 必须预留足够长度 - /// data_len是有效载荷的长度 - /// 返回加密后载荷的长度 - pub fn encrypt_ipv4 + AsMut<[u8]>>( - &self, - net_packet: &mut NetPacket, - ) -> anyhow::Result<()> { - let head_tag = net_packet.head_tag(); - let data_len = net_packet.data_len(); - if self.finger.is_some() { - net_packet.set_data_len(data_len + TAG_RESERVED + RANDOM_RESERVED + FINGER_RESERVED)?; - } else { - net_packet.set_data_len(data_len + TAG_RESERVED + RANDOM_RESERVED)?; - } - let mut secret_body = AEADSecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; - let mut nonce = head_tag; - secret_body.set_random(&gen_random_nonce(&mut nonce)); - let nonce = aead::Nonce::assume_unique_for_key(nonce); - let rs = self.cipher.seal_in_place_separate_tag( - nonce, - aead::Aad::empty(), - secret_body.data_mut(), - ); - match rs { - Ok(tag) => { - let tag = tag.as_ref(); - if tag.len() != 16 { - return Err(anyhow!("加密tag长度错误:{}", tag.len())); - } - secret_body.set_tag(tag)?; - if let Some(finger) = &self.finger { - let finger = finger.calculate_finger(&head_tag, secret_body.data_tag_mut()); - secret_body.set_finger(&finger)?; - } - net_packet.set_encrypt_flag(true); - Ok(()) - } - Err(e) => Err(anyhow!("ring CHACHA20_POLY1305 加密失败:{}", e)), - } - } -} - -#[test] -fn test_ring_chacha20_poly1305() { - let d = ChaCha20Poly1305Cipher::new_256([0; 32], Some(Finger::new("123"))); - let mut p = NetPacket::new_encrypt([0; 73]).unwrap(); - let src = p.buffer().to_vec(); - d.encrypt_ipv4(&mut p).unwrap(); - d.decrypt_ipv4(&mut p).unwrap(); - assert_eq!(p.buffer(), &src); - let d = ChaCha20Poly1305Cipher::new_256([0; 32], None); - let mut p = NetPacket::new_encrypt([0; 73]).unwrap(); - let src = p.buffer().to_vec(); - d.encrypt_ipv4(&mut p).unwrap(); - d.decrypt_ipv4(&mut p).unwrap(); - assert_eq!(p.buffer(), &src); -} diff --git a/vnt/src/cipher/chacha20_poly1305/rs_chacha20_poly1305.rs b/vnt/src/cipher/chacha20_poly1305/rs_chacha20_poly1305.rs deleted file mode 100644 index 39388db..0000000 --- a/vnt/src/cipher/chacha20_poly1305/rs_chacha20_poly1305.rs +++ /dev/null @@ -1,126 +0,0 @@ -use crate::cipher::finger::{gen_nonce, gen_random_nonce}; -use crate::cipher::Finger; -use crate::protocol::body::{ - AEADSecretBody, SecretTail, SecretTailMut, FINGER_RESERVED, RANDOM_RESERVED, TAG_RESERVED, -}; -use crate::protocol::NetPacket; -use anyhow::anyhow; -use chacha20poly1305::aead::{Nonce, Tag}; -use chacha20poly1305::{AeadInPlace, ChaCha20Poly1305, Key, KeyInit}; - -#[derive(Clone)] -pub struct ChaCha20Poly1305Cipher { - key: Vec, - pub(crate) cipher: ChaCha20Poly1305, - pub(crate) finger: Option, -} - -impl ChaCha20Poly1305Cipher { - pub fn new_256(key: [u8; 32], finger: Option) -> Self { - let key: &Key = &key.into(); - let cipher = ChaCha20Poly1305::new(key); - Self { - key: key.to_vec(), - cipher, - finger, - } - } -} - -impl ChaCha20Poly1305Cipher { - pub fn key(&self) -> &[u8] { - &self.key - } -} - -impl ChaCha20Poly1305Cipher { - pub fn decrypt_ipv4 + AsMut<[u8]>>( - &self, - net_packet: &mut NetPacket, - ) -> anyhow::Result<()> { - if !net_packet.is_encrypt() { - //未加密的数据直接丢弃 - return Err(anyhow!("not encrypt")); - } - if net_packet.payload().len() < TAG_RESERVED { - log::error!("数据异常,长度小于{}", TAG_RESERVED); - return Err(anyhow!("data err")); - } - let mut head_tag = net_packet.head_tag(); - let mut secret_body = AEADSecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; - if let Some(finger) = &self.finger { - let finger = finger.calculate_finger(&head_tag, secret_body.data_tag_mut()); - if &finger != secret_body.finger() { - return Err(anyhow!("rs CHACHA20_POLY1305 finger err")); - } - } - gen_nonce(&mut head_tag, secret_body.random_buf()); - let nonce: Nonce = head_tag.into(); - let tag: Tag = - Tag::::from_slice(secret_body.tag()).clone(); - if let Err(e) = - self.cipher - .decrypt_in_place_detached(&nonce, &[], secret_body.data_mut(), &tag) - { - return Err(anyhow!("rs CHACHA20_POLY1305 decrypt_ipv4 {:?}", e)); - } - let len = secret_body.data().len(); - net_packet.set_encrypt_flag(false); - net_packet.set_payload_len(len)?; - Ok(()) - } - /// net_packet 必须预留足够长度 - /// data_len是有效载荷的长度 - /// 返回加密后载荷的长度 - pub fn encrypt_ipv4 + AsMut<[u8]>>( - &self, - net_packet: &mut NetPacket, - ) -> anyhow::Result<()> { - let head_tag = net_packet.head_tag(); - let data_len = net_packet.data_len(); - if self.finger.is_some() { - net_packet.set_data_len(data_len + TAG_RESERVED + RANDOM_RESERVED + FINGER_RESERVED)?; - } else { - net_packet.set_data_len(data_len + TAG_RESERVED + RANDOM_RESERVED)?; - } - let mut secret_body = AEADSecretBody::new(net_packet.payload_mut(), self.finger.is_some())?; - let mut nonce = head_tag; - secret_body.set_random(&gen_random_nonce(&mut nonce)); - let nonce = nonce.into(); - let rs = self - .cipher - .encrypt_in_place_detached(&nonce, &[], secret_body.data_mut()); - match rs { - Ok(tag) => { - let tag: &[u8] = tag.as_ref(); - if tag.len() != 16 { - return Err(anyhow!("加密tag长度错误:{}", tag.len(),)); - } - secret_body.set_tag(tag)?; - if let Some(finger) = &self.finger { - let finger = finger.calculate_finger(&head_tag, secret_body.data_tag_mut()); - secret_body.set_finger(&finger)?; - } - net_packet.set_encrypt_flag(true); - Ok(()) - } - Err(e) => Err(anyhow!("rs CHACHA20_POLY1305 加密失败:{}", e)), - } - } -} - -#[test] -fn test_rs_chacha20_poly1305() { - let d = ChaCha20Poly1305Cipher::new_256([0; 32], Some(Finger::new("123"))); - let mut p = NetPacket::new_encrypt([0; 73]).unwrap(); - let src = p.buffer().to_vec(); - d.encrypt_ipv4(&mut p).unwrap(); - d.decrypt_ipv4(&mut p).unwrap(); - assert_eq!(p.buffer(), &src); - let d = ChaCha20Poly1305Cipher::new_256([0; 32], None); - let mut p = NetPacket::new_encrypt([0; 73]).unwrap(); - let src = p.buffer().to_vec(); - d.encrypt_ipv4(&mut p).unwrap(); - d.decrypt_ipv4(&mut p).unwrap(); - assert_eq!(p.buffer(), &src); -} diff --git a/vnt/src/cipher/cipher.rs b/vnt/src/cipher/cipher.rs deleted file mode 100644 index 3613e79..0000000 --- a/vnt/src/cipher/cipher.rs +++ /dev/null @@ -1,335 +0,0 @@ -use std::fmt::Display; -use std::str::FromStr; - -use anyhow::anyhow; -#[cfg(cipher)] -use sha2::Digest; - -#[cfg(feature = "aes_cbc")] -use crate::cipher::aes_cbc::AesCbcCipher; -#[cfg(feature = "aes_ecb")] -use crate::cipher::aes_ecb::AesEcbCipher; -#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))] -use crate::cipher::aes_gcm::AesGcmCipher; -#[cfg(feature = "chacha20_poly1305")] -use crate::cipher::chacha20::ChaCha20Cipher; -#[cfg(feature = "chacha20_poly1305")] -use crate::cipher::chacha20_poly1305::ChaCha20Poly1305Cipher; -#[cfg(feature = "sm4_cbc")] -use crate::cipher::sm4_cbc::Sm4CbcCipher; -use crate::cipher::xor::XORCipher; -#[cfg(cipher)] -use crate::cipher::Finger; -use crate::protocol::NetPacket; - -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub enum CipherModel { - #[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))] - AesGcm, - #[cfg(feature = "chacha20_poly1305")] - Chacha20Poly1305, - #[cfg(feature = "chacha20_poly1305")] - Chacha20, - #[cfg(feature = "aes_cbc")] - AesCbc, - #[cfg(feature = "aes_ecb")] - AesEcb, - #[cfg(feature = "sm4_cbc")] - Sm4Cbc, - Xor, - None, -} - -impl Display for CipherModel { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let str = match self { - #[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))] - CipherModel::AesGcm => "aes_gcm".to_string(), - #[cfg(feature = "chacha20_poly1305")] - CipherModel::Chacha20Poly1305 => "chacha20_poly1305".to_string(), - #[cfg(feature = "chacha20_poly1305")] - CipherModel::Chacha20 => "chacha20".to_string(), - #[cfg(feature = "aes_cbc")] - CipherModel::AesCbc => "aes_cbc".to_string(), - #[cfg(feature = "aes_ecb")] - CipherModel::AesEcb => "aes_ecb".to_string(), - #[cfg(feature = "sm4_cbc")] - CipherModel::Sm4Cbc => "sm4_cbc".to_string(), - CipherModel::Xor => "xor".to_string(), - CipherModel::None => "none".to_string(), - }; - write!(f, "{}", str) - } -} - -impl FromStr for CipherModel { - type Err = String; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().trim() { - #[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))] - "aes_gcm" => Ok(CipherModel::AesGcm), - #[cfg(feature = "chacha20_poly1305")] - "chacha20_poly1305" => Ok(CipherModel::Chacha20Poly1305), - #[cfg(feature = "chacha20_poly1305")] - "chacha20" => Ok(CipherModel::Chacha20), - #[cfg(feature = "aes_cbc")] - "aes_cbc" => Ok(CipherModel::AesCbc), - #[cfg(feature = "aes_ecb")] - "aes_ecb" => Ok(CipherModel::AesEcb), - #[cfg(feature = "sm4_cbc")] - "sm4_cbc" => Ok(CipherModel::Sm4Cbc), - "xor" => Ok(CipherModel::Xor), - _ => { - let mut enums = String::new(); - #[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))] - enums.push_str("/aes_gcm"); - #[cfg(feature = "chacha20_poly1305")] - enums.push_str("/chacha20_poly1305/chacha20"); - #[cfg(feature = "aes_cbc")] - enums.push_str("/aes_cbc"); - #[cfg(feature = "aes_ecb")] - enums.push_str("/aes_ecb"); - #[cfg(feature = "sm4_cbc")] - enums.push_str("/sm4_cbc"); - enums.push_str("/xor"); - Err(format!("not match '{}', enum:{}", s, &enums[1..])) - } - } - } -} - -#[derive(Clone)] -pub enum Cipher { - #[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))] - AesGcm((AesGcmCipher, Vec)), - #[cfg(feature = "chacha20_poly1305")] - Chacha20Poly1305(ChaCha20Poly1305Cipher), - #[cfg(feature = "chacha20_poly1305")] - Chacha20(ChaCha20Cipher), - #[cfg(feature = "aes_cbc")] - AesCbc(AesCbcCipher), - #[cfg(feature = "aes_ecb")] - AesEcb(AesEcbCipher), - #[cfg(feature = "sm4_cbc")] - Sm4Cbc(Sm4CbcCipher), - Xor(XORCipher), - None, -} - -impl Cipher { - pub fn new_password( - model: CipherModel, - password: Option, - token: Option, - ) -> anyhow::Result { - if let Some(password) = password { - #[cfg(cipher)] - let key: [u8; 32] = { - let mut hasher = sha2::Sha256::new(); - hasher.update(password.as_bytes()); - hasher.finalize().into() - }; - match model { - #[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))] - CipherModel::AesGcm => { - let finger = token.map(|token| Finger::new(&token)); - if password.len() < 8 { - let aes = AesGcmCipher::new_128(key[..16].try_into().unwrap(), finger); - Ok(Cipher::AesGcm((aes, key[..16].to_vec()))) - } else { - let aes = AesGcmCipher::new_256(key, finger); - Ok(Cipher::AesGcm((aes, key.to_vec()))) - } - } - #[cfg(feature = "chacha20_poly1305")] - CipherModel::Chacha20Poly1305 => { - let finger = token.map(|token| Finger::new(&token)); - let chacha = ChaCha20Poly1305Cipher::new_256(key, finger); - Ok(Cipher::Chacha20Poly1305(chacha)) - } - #[cfg(feature = "chacha20_poly1305")] - CipherModel::Chacha20 => { - let finger = token.map(|token| Finger::new(&token)); - let chacha = ChaCha20Cipher::new_256(key, finger); - Ok(Cipher::Chacha20(chacha)) - } - #[cfg(feature = "aes_cbc")] - CipherModel::AesCbc => { - let finger = token.map(|token| Finger::new(&token)); - if password.len() < 8 { - let aes = AesCbcCipher::new_128(key[..16].try_into().unwrap(), finger); - Ok(Cipher::AesCbc(aes)) - } else { - let aes = AesCbcCipher::new_256(key, finger); - Ok(Cipher::AesCbc(aes)) - } - } - #[cfg(feature = "aes_ecb")] - CipherModel::AesEcb => { - let finger = token.map(|token| Finger::new(&token)); - if password.len() < 8 { - let aes = AesEcbCipher::new_128(key[..16].try_into().unwrap(), finger); - Ok(Cipher::AesEcb(aes)) - } else { - let aes = AesEcbCipher::new_256(key, finger); - Ok(Cipher::AesEcb(aes)) - } - } - #[cfg(feature = "sm4_cbc")] - CipherModel::Sm4Cbc => { - let finger = token.map(|token| Finger::new(&token)); - let aes = Sm4CbcCipher::new_128(key[..16].try_into().unwrap(), finger); - Ok(Cipher::Sm4Cbc(aes)) - } - CipherModel::Xor => { - if token.is_some() { - Err(anyhow::anyhow!( - "'finger' and 'xor' cannot be used simultaneously" - ))? - } - Ok(Cipher::Xor(XORCipher::new_256( - crate::cipher::xor::simple_hash(&password), - ))) - } - CipherModel::None => Ok(Cipher::None), - } - } else { - Ok(Cipher::None) - } - } - #[cfg(not(any(feature = "aes_gcm", feature = "server_encrypt")))] - pub fn new_key(_key: [u8; 32], _token: String) -> anyhow::Result { - Err(anyhow!("key error")) - } - #[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))] - pub fn new_key(key: [u8; 32], token: String) -> anyhow::Result { - let finger = Some(Finger::new(&token)); - match key.len() { - 16 => { - let aes = AesGcmCipher::new_128(key[..16].try_into().unwrap(), finger); - Ok(Cipher::AesGcm((aes, key[..16].to_vec()))) - } - 32 => { - let aes = AesGcmCipher::new_256(key, finger); - Ok(Cipher::AesGcm((aes, key.to_vec()))) - } - _ => Err(anyhow!("key error")), - } - } - pub fn decrypt_ipv4 + AsMut<[u8]>>( - &self, - net_packet: &mut NetPacket, - ) -> anyhow::Result<()> { - match self { - #[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))] - Cipher::AesGcm((aes_gcm, _)) => aes_gcm.decrypt_ipv4(net_packet), - #[cfg(feature = "aes_cbc")] - Cipher::AesCbc(aes_cbc) => aes_cbc.decrypt_ipv4(net_packet), - #[cfg(feature = "chacha20_poly1305")] - Cipher::Chacha20Poly1305(chacha20poly1305) => chacha20poly1305.decrypt_ipv4(net_packet), - #[cfg(feature = "chacha20_poly1305")] - Cipher::Chacha20(chacha20) => chacha20.decrypt_ipv4(net_packet), - #[cfg(feature = "aes_ecb")] - Cipher::AesEcb(aes_ecb) => aes_ecb.decrypt_ipv4(net_packet), - #[cfg(feature = "sm4_cbc")] - Cipher::Sm4Cbc(sm4_cbc) => sm4_cbc.decrypt_ipv4(net_packet), - Cipher::Xor(xor) => xor.decrypt_ipv4(net_packet), - Cipher::None => { - if net_packet.is_encrypt() { - return Err(anyhow!("not key")); - } - Ok(()) - } - } - } - pub fn encrypt_ipv4 + AsMut<[u8]>>( - &self, - net_packet: &mut NetPacket, - ) -> anyhow::Result<()> { - match self { - #[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))] - Cipher::AesGcm((aes_gcm, _)) => aes_gcm.encrypt_ipv4(net_packet), - #[cfg(feature = "chacha20_poly1305")] - Cipher::Chacha20Poly1305(chacha20poly1305) => chacha20poly1305.encrypt_ipv4(net_packet), - #[cfg(feature = "chacha20_poly1305")] - Cipher::Chacha20(chacha20) => chacha20.encrypt_ipv4(net_packet), - #[cfg(feature = "aes_cbc")] - Cipher::AesCbc(aes_cbc) => aes_cbc.encrypt_ipv4(net_packet), - #[cfg(feature = "aes_ecb")] - Cipher::AesEcb(aes_ecb) => aes_ecb.encrypt_ipv4(net_packet), - #[cfg(feature = "sm4_cbc")] - Cipher::Sm4Cbc(sm4_cbc) => sm4_cbc.encrypt_ipv4(net_packet), - Cipher::Xor(xor) => xor.encrypt_ipv4(net_packet), - Cipher::None => Ok(()), - } - } - #[cfg(not(cipher))] - pub fn check_finger + AsMut<[u8]>>( - &self, - _net_packet: &NetPacket, - ) -> anyhow::Result<()> { - Ok(()) - } - #[cfg(cipher)] - pub fn check_finger>(&self, net_packet: &NetPacket) -> anyhow::Result<()> { - match self { - #[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))] - Cipher::AesGcm((aes_gcm, _)) => aes_gcm - .finger - .as_ref() - .map(|f| f.check_finger(net_packet)) - .unwrap_or(Ok(())), - #[cfg(feature = "chacha20_poly1305")] - Cipher::Chacha20Poly1305(chacha20poly1305) => chacha20poly1305 - .finger - .as_ref() - .map(|f| f.check_finger(net_packet)) - .unwrap_or(Ok(())), - #[cfg(feature = "chacha20_poly1305")] - Cipher::Chacha20(chacha20) => chacha20 - .finger - .as_ref() - .map(|f| f.check_finger(net_packet)) - .unwrap_or(Ok(())), - #[cfg(feature = "aes_cbc")] - Cipher::AesCbc(aes_cbc) => aes_cbc - .finger - .as_ref() - .map(|f| f.check_finger(net_packet)) - .unwrap_or(Ok(())), - #[cfg(feature = "aes_ecb")] - Cipher::AesEcb(aes_ecb) => aes_ecb - .finger - .as_ref() - .map(|f| f.check_finger(net_packet)) - .unwrap_or(Ok(())), - #[cfg(feature = "sm4_cbc")] - Cipher::Sm4Cbc(sm4_cbc) => sm4_cbc - .finger - .as_ref() - .map(|f| f.check_finger(net_packet)) - .unwrap_or(Ok(())), - Cipher::Xor(_) => Ok(()), - Cipher::None => Ok(()), - } - } - pub fn key(&self) -> Option<&[u8]> { - match self { - #[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))] - Cipher::AesGcm((_, key)) => Some(key), - #[cfg(feature = "chacha20_poly1305")] - Cipher::Chacha20Poly1305(chacha20poly1305) => Some(chacha20poly1305.key()), - #[cfg(feature = "chacha20_poly1305")] - Cipher::Chacha20(chacha20) => Some(chacha20.key()), - #[cfg(feature = "aes_cbc")] - Cipher::AesCbc(aes_cbc) => Some(aes_cbc.key()), - #[cfg(feature = "aes_ecb")] - Cipher::AesEcb(aes_ecb) => Some(aes_ecb.key()), - #[cfg(feature = "sm4_cbc")] - Cipher::Sm4Cbc(sm4_cbc) => Some(sm4_cbc.key()), - Cipher::Xor(xor) => Some(xor.key()), - Cipher::None => None, - } - } -} diff --git a/vnt/src/cipher/finger.rs b/vnt/src/cipher/finger.rs deleted file mode 100644 index f070a7a..0000000 --- a/vnt/src/cipher/finger.rs +++ /dev/null @@ -1,76 +0,0 @@ -use anyhow::anyhow; -use rand::RngCore; - -use sha2::Digest; - -use crate::protocol::NetPacket; - -#[derive(Clone)] -pub struct Finger { - pub(crate) hash: [u8; 32], -} - -impl Finger { - pub fn new(str: &str) -> Self { - let mut hasher = sha2::Sha256::new(); - hasher.update(str.as_bytes()); - let hash: [u8; 32] = hasher.finalize().into(); - Finger { hash } - } - pub fn check_finger>(&self, net_packet: &NetPacket) -> anyhow::Result<()> { - if !net_packet.is_encrypt() { - //未加密的数据直接丢弃 - return Err(anyhow!("not encrypt")); - } - let payload_len = net_packet.payload().len(); - if payload_len < 12 { - log::error!("数据异常,长度小于{}", 12); - return Err(anyhow!("data err")); - } - let mut nonce_raw = [0; 12]; - nonce_raw[0..4].copy_from_slice(&net_packet.source().octets()); - nonce_raw[4..8].copy_from_slice(&net_packet.destination().octets()); - nonce_raw[8] = net_packet.protocol().into(); - nonce_raw[9] = net_packet.transport_protocol(); - nonce_raw[10] = net_packet.is_gateway() as u8; - nonce_raw[11] = net_packet.source_ttl(); - let payload = net_packet.payload(); - let finger = self.calculate_finger(&nonce_raw, &payload[..payload_len - 12]); - if &finger[..] != &payload[payload_len - 12..] { - return Err(anyhow!("finger err")); - } - Ok(()) - } - pub fn calculate_finger(&self, nonce: &[u8], secret_body: &[u8]) -> [u8; 12] { - let mut hasher = sha2::Sha256::new(); - hasher.update(nonce); - hasher.update(secret_body); - hasher.update(&self.hash); - let key: [u8; 32] = hasher.finalize().into(); - return key[20..].try_into().unwrap(); - } -} -impl> NetPacket { - pub fn head_tag(&self) -> [u8; 12] { - let mut tag = [0; 12]; - tag[0..4].copy_from_slice(&self.buffer()[4..8]); - tag[4..8].copy_from_slice(&self.buffer()[8..12]); - tag[8] = self.protocol().into(); - tag[9] = self.transport_protocol(); - tag[10] = self.is_gateway() as u8; - tag[11] = self.source_ttl(); - tag - } -} -pub fn gen_nonce(tag: &mut [u8], random: &[u8]) { - tag[8] = random[0] ^ tag[8]; - tag[9] = random[1] ^ tag[9]; - tag[10] = random[2] ^ tag[10]; - tag[11] = random[3] ^ tag[11]; -} -pub fn gen_random_nonce(tag: &mut [u8; 12]) -> [u8; 4] { - let mut random = [0; 4]; - rand::thread_rng().fill_bytes(&mut random); - gen_nonce(tag, &random); - random -} diff --git a/vnt/src/cipher/mod.rs b/vnt/src/cipher/mod.rs deleted file mode 100644 index 1cc12d7..0000000 --- a/vnt/src/cipher/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -mod cipher; -#[cfg(cipher)] -mod finger; - -pub use cipher::Cipher; -pub use cipher::CipherModel; -#[cfg(cipher)] -pub use finger::Finger; -#[cfg(feature = "server_encrypt")] -mod rsa_cipher; -#[cfg(feature = "server_encrypt")] -pub use rsa_cipher::RsaCipher; - -#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))] -mod aes_gcm; - -#[cfg(feature = "chacha20_poly1305")] -mod chacha20; -#[cfg(feature = "chacha20_poly1305")] -mod chacha20_poly1305; - -#[cfg(feature = "aes_ecb")] -mod aes_ecb; - -#[cfg(feature = "aes_cbc")] -mod aes_cbc; - -#[cfg(feature = "sm4_cbc")] -mod sm4_cbc; - -mod xor; -pub use xor::simple_hash; diff --git a/vnt/src/cipher/rsa_cipher.rs b/vnt/src/cipher/rsa_cipher.rs deleted file mode 100644 index f66a230..0000000 --- a/vnt/src/cipher/rsa_cipher.rs +++ /dev/null @@ -1,112 +0,0 @@ -use std::io; - -use { - crate::protocol::body::{RsaSecretBody, RSA_ENCRYPTION_RESERVED}, - rand::Rng, - rsa::pkcs8::der::Decode, - rsa::RsaPublicKey, - sha2::Digest, - spki::{DecodePublicKey, EncodePublicKey}, -}; - -use crate::protocol::NetPacket; - -#[derive(Clone)] -pub struct RsaCipher { - inner: Inner, -} -#[derive(Clone)] -struct Inner { - public_key: RsaPublicKey, - finger: String, -} - -impl RsaCipher { - pub fn new(der: &[u8]) -> io::Result { - match RsaPublicKey::from_public_key_der(der) { - Ok(public_key) => { - let finger = finger(&public_key)?; - let inner = Inner { public_key, finger }; - Ok(Self { inner }) - } - Err(e) => Err(io::Error::new( - io::ErrorKind::Other, - format!("from_public_key_der failed {}", e), - )), - } - } - pub fn finger(&self) -> &String { - &self.inner.finger - } - pub fn public_key(&self) -> io::Result<&RsaPublicKey> { - return Ok(&self.inner.public_key); - } -} -pub fn finger(public_key: &RsaPublicKey) -> io::Result { - match public_key.to_public_key_der() { - Ok(der) => match rsa::pkcs8::SubjectPublicKeyInfoRef::from_der(der.as_bytes()) { - Ok(spki) => match spki.fingerprint_base64() { - Ok(finger) => Ok(finger), - Err(e) => Err(io::Error::new( - io::ErrorKind::Other, - format!("fingerprint_base64 error {}", e), - )), - }, - Err(e) => Err(io::Error::new( - io::ErrorKind::Other, - format!("from_der error {}", e), - )), - }, - Err(e) => Err(io::Error::new( - io::ErrorKind::Other, - format!("to_public_key_der error {}", e), - )), - } -} - -impl RsaCipher { - /// net_packet 必须预留足够长度 - pub fn encrypt + AsMut<[u8]>>( - &self, - net_packet: &mut NetPacket, - ) -> io::Result>> { - if net_packet.reserve() < RSA_ENCRYPTION_RESERVED { - return Err(io::Error::new(io::ErrorKind::Other, "too short")); - } - let data_len = net_packet.data_len() + RSA_ENCRYPTION_RESERVED; - net_packet.set_data_len(data_len)?; - let mut nonce_raw = [0; 12]; - nonce_raw[0..4].copy_from_slice(&net_packet.source().octets()); - nonce_raw[4..8].copy_from_slice(&net_packet.destination().octets()); - nonce_raw[8] = net_packet.protocol().into(); - nonce_raw[9] = net_packet.transport_protocol(); - nonce_raw[10] = net_packet.is_gateway() as u8; - nonce_raw[11] = net_packet.source_ttl(); - - let mut secret_body = RsaSecretBody::new(net_packet.payload_mut())?; - let mut rng = rand::thread_rng(); - rng.fill(secret_body.random_mut()); - - let mut hasher = sha2::Sha256::new(); - hasher.update(secret_body.body()); - hasher.update(nonce_raw); - let key: [u8; 32] = hasher.finalize().into(); - secret_body.set_finger(&key[16..])?; - match self.inner.public_key.encrypt( - &mut rng, - rsa::pkcs1v15::Pkcs1v15Encrypt, - secret_body.buffer(), - ) { - Ok(enc_data) => { - let mut net_packet_e = NetPacket::new(vec![0; 12 + enc_data.len()])?; - net_packet_e.buffer_mut()[..12].copy_from_slice(&net_packet.buffer()[..12]); - net_packet_e.set_payload(&enc_data)?; - Ok(net_packet_e) - } - Err(e) => Err(io::Error::new( - io::ErrorKind::Other, - format!("encrypt failed {}", e), - )), - } - } -} diff --git a/vnt/src/cipher/sm4_cbc/mod.rs b/vnt/src/cipher/sm4_cbc/mod.rs deleted file mode 100644 index 77a92eb..0000000 --- a/vnt/src/cipher/sm4_cbc/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod rs_sm4_cbc; -pub use rs_sm4_cbc::*; diff --git a/vnt/src/cipher/sm4_cbc/rs_sm4_cbc.rs b/vnt/src/cipher/sm4_cbc/rs_sm4_cbc.rs deleted file mode 100644 index 885749c..0000000 --- a/vnt/src/cipher/sm4_cbc/rs_sm4_cbc.rs +++ /dev/null @@ -1,153 +0,0 @@ -use crate::cipher::Finger; -use crate::protocol::{NetPacket, HEAD_LEN}; -use anyhow::anyhow; -use libsm::sm4::cipher_mode::CipherMode; -use libsm::sm4::Sm4CipherMode; -use rand::RngCore; - -pub struct Sm4CbcCipher { - key: [u8; 16], - pub(crate) cipher: Sm4CipherMode, - pub(crate) finger: Option, -} - -impl Clone for Sm4CbcCipher { - fn clone(&self) -> Self { - let cipher = Sm4CipherMode::new(&self.key, CipherMode::Cbc).unwrap(); - Self { - key: self.key, - cipher, - finger: self.finger.clone(), - } - } -} - -impl Sm4CbcCipher { - pub fn key(&self) -> &[u8] { - &self.key - } -} - -impl Sm4CbcCipher { - pub fn new_128(key: [u8; 16], finger: Option) -> Self { - let cipher = Sm4CipherMode::new(&key, CipherMode::Cbc).unwrap(); - Self { - key, - cipher, - finger, - } - } - - pub fn decrypt_ipv4 + AsMut<[u8]>>( - &self, - net_packet: &mut NetPacket, - ) -> anyhow::Result<()> { - if !net_packet.is_encrypt() { - //未加密的数据直接丢弃 - return Err(anyhow!("not encrypt")); - } - - if let Some(finger) = &self.finger { - let nonce_raw = net_packet.head_tag(); - let len = net_packet.payload().len(); - if len < 12 { - return Err(anyhow!("payload len <12")); - } - let secret_body = &net_packet.payload()[..len - 12]; - let finger = finger.calculate_finger(&nonce_raw, secret_body); - if &finger != &net_packet.payload()[len - 12..] { - return Err(anyhow!("finger err")); - } - net_packet.set_data_len(net_packet.data_len() - finger.len())?; - } - let payload = net_packet.payload(); - let len = payload.len(); - if len < 16 || len > 1024 * 4 { - log::error!("数据异常,长度{}小于16或大于4096", len); - return Err(anyhow!("data err")); - } - let mut out = [0u8; 1024 * 4]; - let data = &payload[..len - 16]; - let iv = &payload[len - 16..]; - match self.cipher.decrypt(data, iv, &mut out) { - Ok(len) => { - let src_net_packet = NetPacket::new(&out[..len])?; - if src_net_packet.source() != net_packet.source() { - return Err(anyhow!("data err")); - } - if src_net_packet.destination() != net_packet.destination() { - return Err(anyhow!("data err")); - } - if src_net_packet.protocol() != net_packet.protocol() { - return Err(anyhow!("data err")); - } - if src_net_packet.transport_protocol() != net_packet.transport_protocol() { - return Err(anyhow!("data err")); - } - if src_net_packet.is_gateway() != net_packet.is_gateway() { - return Err(anyhow!("data err")); - } - if src_net_packet.source_ttl() != net_packet.source_ttl() { - return Err(anyhow!("data err")); - } - net_packet.set_data_len(len)?; - net_packet.set_payload(src_net_packet.payload())?; - net_packet.set_encrypt_flag(false); - Ok(()) - } - Err(e) => Err(anyhow!("sm4_cbc解密失败:{}", e)), - } - } - /// net_packet 必须预留足够长度 - /// data_len是有效载荷的长度 - pub fn encrypt_ipv4 + AsMut<[u8]>>( - &self, - net_packet: &mut NetPacket, - ) -> anyhow::Result<()> { - let mut out = [0u8; 1024 * 4]; - let mut iv = [0u8; 16]; - rand::thread_rng().fill_bytes(&mut iv); - if net_packet.data_len() > 1024 * 4 - 32 { - log::error!( - "数据异常,长度{}大于1024 * 4 - 32", - net_packet.buffer().len() - ); - return Err(anyhow!("data err")); - } - match self.cipher.encrypt(net_packet.buffer(), &iv, &mut out) { - Ok(len) => { - net_packet.set_data_len(HEAD_LEN + len + 16)?; - net_packet.payload_mut()[..len].copy_from_slice(&out[..len]); - net_packet.payload_mut()[len..].copy_from_slice(&iv); - if let Some(finger) = &self.finger { - let nonce_raw = net_packet.head_tag(); - let finger = finger.calculate_finger(&nonce_raw, net_packet.payload()); - let src_data_len = net_packet.data_len(); - //设置实际长度 - net_packet.set_data_len(src_data_len + finger.len())?; - - net_packet.buffer_mut()[src_data_len..].copy_from_slice(&finger); - } - net_packet.set_encrypt_flag(true); - Ok(()) - } - Err(e) => Err(anyhow!("sm4_cbc加密失败:{}", e)), - } - } -} - -#[test] -fn test_sm4_ecb() { - let d = Sm4CbcCipher::new_128([0; 16], Some(Finger::new("123"))); - let mut p = NetPacket::new_encrypt([1; 1024]).unwrap(); - let src = p.buffer().to_vec(); - d.encrypt_ipv4(&mut p).unwrap(); - d.decrypt_ipv4(&mut p).unwrap(); - assert_eq!(p.buffer(), &src); - let d = Sm4CbcCipher::new_128([0; 16], None); - let mut p = NetPacket::new_encrypt([1; 102]).unwrap(); - let src = p.buffer().to_vec(); - d.encrypt_ipv4(&mut p).unwrap(); - d.decrypt_ipv4(&mut p).unwrap(); - assert_eq!(p.buffer(), &src) -} diff --git a/vnt/src/cipher/xor/mod.rs b/vnt/src/cipher/xor/mod.rs deleted file mode 100644 index fe38376..0000000 --- a/vnt/src/cipher/xor/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod xor; -pub use xor::*; diff --git a/vnt/src/cipher/xor/xor.rs b/vnt/src/cipher/xor/xor.rs deleted file mode 100644 index 7a76241..0000000 --- a/vnt/src/cipher/xor/xor.rs +++ /dev/null @@ -1,84 +0,0 @@ -use anyhow::anyhow; - -use crate::protocol::NetPacket; - -pub fn simple_hash(input: &str) -> [u8; 32] { - let mut result = [0u8; 32]; - let bytes = input.as_bytes(); - for (index, v) in result.iter_mut().enumerate() { - *v = bytes[index % bytes.len()]; - } - - let mut state = 0u8; - - for (i, &byte) in bytes.iter().enumerate() { - let combined = byte.wrapping_add(state).rotate_left((i % 8) as u32); - result[i % 32] ^= combined; - state = state.wrapping_add(byte).rotate_left(3); - } - - for i in 0..32 { - result[i] = result[i] - .rotate_left((result[(i + 1) % 32] % 8) as u32) - .wrapping_add(state); - state = state.wrapping_add(result[i]).rotate_left(3); - } - - result -} - -#[derive(Clone)] -pub struct XORCipher { - key: [u8; 32], -} - -impl XORCipher { - pub fn new_256(key: [u8; 32]) -> Self { - Self { key } - } -} - -impl XORCipher { - pub fn key(&self) -> &[u8] { - &self.key - } -} - -impl XORCipher { - pub fn decrypt_ipv4 + AsMut<[u8]>>( - &self, - net_packet: &mut NetPacket, - ) -> anyhow::Result<()> { - if !net_packet.is_encrypt() { - //未加密的数据直接丢弃 - return Err(anyhow!("not encrypt")); - } - let key = &self.key; - for (i, byte) in net_packet.payload_mut().iter_mut().enumerate() { - *byte ^= key[i & 31]; - } - net_packet.set_encrypt_flag(false); - Ok(()) - } - pub fn encrypt_ipv4 + AsMut<[u8]>>( - &self, - net_packet: &mut NetPacket, - ) -> anyhow::Result<()> { - net_packet.set_encrypt_flag(true); - let key = &self.key; - for (i, byte) in net_packet.payload_mut().iter_mut().enumerate() { - *byte ^= key[i & 31]; - } - Ok(()) - } -} - -#[test] -fn test_xor() { - let d = XORCipher::new_256(simple_hash("password")); - let mut p = NetPacket::new_encrypt([0; 1000]).unwrap(); - let src = p.buffer().to_vec(); - d.encrypt_ipv4(&mut p).unwrap(); - d.decrypt_ipv4(&mut p).unwrap(); - assert_eq!(p.buffer(), &src) -} diff --git a/vnt/src/compression/lz4_compress.rs b/vnt/src/compression/lz4_compress.rs deleted file mode 100644 index 6c28abc..0000000 --- a/vnt/src/compression/lz4_compress.rs +++ /dev/null @@ -1,33 +0,0 @@ -use anyhow::anyhow; - -use crate::protocol::NetPacket; - -#[derive(Clone)] -pub struct Lz4Compressor; - -impl Lz4Compressor { - pub fn compress, O: AsRef<[u8]> + AsMut<[u8]>>( - in_net_packet: &NetPacket, - out: &mut NetPacket, - ) -> anyhow::Result<()> { - out.set_data_len_max(); - let len = match lz4_flex::compress_into(in_net_packet.payload(), out.payload_mut()) { - Ok(len) => len, - Err(e) => Err(anyhow!("Lz4 compress {}", e))?, - }; - out.set_payload_len(len)?; - Ok(()) - } - pub fn decompress, O: AsRef<[u8]> + AsMut<[u8]>>( - in_net_packet: &NetPacket, - out: &mut NetPacket, - ) -> anyhow::Result<()> { - out.set_data_len_max(); - let len = match lz4_flex::decompress_into(in_net_packet.payload(), out.payload_mut()) { - Ok(len) => len, - Err(e) => Err(anyhow!("Lz4 decompress {}", e))?, - }; - out.set_payload_len(len)?; - Ok(()) - } -} diff --git a/vnt/src/compression/mod.rs b/vnt/src/compression/mod.rs deleted file mode 100644 index d9f06a8..0000000 --- a/vnt/src/compression/mod.rs +++ /dev/null @@ -1,220 +0,0 @@ -use std::str::FromStr; - -use anyhow::anyhow; - -#[cfg(feature = "lz4_compress")] -use crate::compression::lz4_compress::Lz4Compressor; -#[cfg(feature = "zstd_compress")] -use crate::compression::zstd_compress::ZstdCompressor; -use crate::protocol::extension::CompressionAlgorithm; -#[cfg(feature = "zstd_compress")] -use zstd::zstd_safe::CompressionLevel; - -use crate::protocol::NetPacket; - -#[cfg(feature = "lz4_compress")] -mod lz4_compress; -#[cfg(feature = "zstd_compress")] -mod zstd_compress; - -#[derive(Clone, Copy, Debug)] -pub enum Compressor { - #[cfg(feature = "lz4_compress")] - Lz4, - #[cfg(feature = "zstd_compress")] - Zstd(CompressionLevel), - None, -} - -impl FromStr for Compressor { - type Err = String; - #[cfg(not(any(feature = "lz4_compress", feature = "zstd_compress")))] - fn from_str(s: &str) -> Result { - Err(format!("not match '{}', Compression not supported", s)) - } - #[cfg(any(feature = "lz4_compress", feature = "zstd_compress"))] - fn from_str(s: &str) -> Result { - let str = s.trim().to_lowercase(); - match str.as_str() { - #[cfg(feature = "lz4_compress")] - "lz4" => Ok(Compressor::Lz4), - #[cfg(feature = "zstd_compress")] - "zstd" => Ok(Compressor::Zstd(9)), - "none" => Ok(Compressor::None), - _ => { - #[cfg(feature = "zstd_compress")] - { - let string_array: Vec = str.split(',').map(|s| s.to_string()).collect(); - if string_array.len() != 2 || string_array[0] != "zstd" { - return Err(format!("not match '{}', exp: zstd,10", s)); - } - return match CompressionLevel::from_str(&string_array[1]) { - Ok(level) => Ok(Compressor::Zstd(level)), - Err(_) => Err(format!("not match '{}', exp: zstd,10", s)), - }; - } - #[cfg(not(feature = "zstd_compress"))] - #[cfg(feature = "lz4_compress")] - return Err(format!("not match '{}', exp: lz4", s)); - } - } - } -} - -#[cfg(not(any(feature = "lz4_compress", feature = "zstd_compress")))] -impl Compressor { - pub fn compress, O: AsRef<[u8]> + AsMut<[u8]>>( - &self, - _in_net_packet: &NetPacket, - _out: &mut NetPacket, - ) -> anyhow::Result { - Ok(false) - } - pub fn decompress, O: AsRef<[u8]> + AsMut<[u8]>>( - _algorithm: CompressionAlgorithm, - _in_net_packet: &NetPacket, - _out: &mut NetPacket, - ) -> anyhow::Result<()> { - Err(anyhow!("Unsupported decompress")) - } -} - -#[cfg(any(feature = "lz4_compress", feature = "zstd_compress"))] -impl Compressor { - pub fn compress, O: AsRef<[u8]> + AsMut<[u8]>>( - &self, - in_net_packet: &NetPacket, - out: &mut NetPacket, - ) -> anyhow::Result { - match self { - #[cfg(feature = "lz4_compress")] - Compressor::Lz4 => { - if in_net_packet.data_len() < 128 { - return Ok(false); - } - Lz4Compressor::compress(in_net_packet, out)?; - let mut compression_extension_tail = out.append_compression_extension_tail()?; - compression_extension_tail.set_algorithm(CompressionAlgorithm::Lz4); - //压缩没效果,则放弃压缩 - if out.data_len() >= in_net_packet.data_len() - 16 { - return Ok(false); - } - return Ok(true); - } - #[cfg(feature = "zstd_compress")] - Compressor::Zstd(level) => { - if in_net_packet.data_len() < 128 { - return Ok(false); - } - ZstdCompressor::compress(*level, in_net_packet, out)?; - let mut compression_extension_tail = out.append_compression_extension_tail()?; - compression_extension_tail.set_algorithm(CompressionAlgorithm::Zstd); - //压缩没效果,则放弃压缩 - if out.data_len() >= in_net_packet.data_len() - 16 { - return Ok(false); - } - return Ok(true); - } - Compressor::None => {} - } - Ok(false) - } - pub fn decompress, O: AsRef<[u8]> + AsMut<[u8]>>( - algorithm: CompressionAlgorithm, - in_net_packet: &NetPacket, - out: &mut NetPacket, - ) -> anyhow::Result<()> { - match algorithm { - #[cfg(feature = "lz4_compress")] - CompressionAlgorithm::Lz4 => Lz4Compressor::decompress(in_net_packet, out), - #[cfg(feature = "zstd_compress")] - CompressionAlgorithm::Zstd => ZstdCompressor::decompress(in_net_packet, out), - _ => Err(anyhow!("Unknown decompress {:?}", algorithm)), - } - } -} - -#[test] -#[cfg(feature = "zstd_compress")] -fn test_lz4() { - use crate::protocol::extension::{CompressionAlgorithm, ExtensionTailPacket}; - let lz4 = Compressor::Lz4; - let in_packet = NetPacket::new([ - 65, 108, 105, 99, 101, 32, 119, 97, 116, 32, 98, 101, 103, 105, 110, 110, 105, 110, 103, - 32, 116, 111, 32, 103, 101, 116, 32, 118, 101, 114, 121, 32, 116, 105, 114, 101, 100, 32, - 111, 102, 32, 115, 105, 116, 116, 105, 110, 103, 32, 98, 121, 32, 104, 101, 114, 32, 115, - 105, 115, 116, 101, 114, 32, 111, 110, 32, 116, 104, 101, 32, 98, 97, 110, 107, 44, 32, 97, - 110, 100, 32, 111, 102, 32, 104, 97, 118, 105, 110, 103, 32, 110, 111, 116, 104, 105, 110, - 103, 32, 116, 111, 32, 100, 111, 58, 32, 111, 110, 99, 101, 32, 111, 114, 32, 116, 119, - 105, 99, 101, 32, 115, 104, 101, 32, 104, 97, 100, 32, 112, 101, 101, 112, 101, 100, 32, - 105, 110, 116, 111, 32, 116, 104, 101, 32, 98, 111, 111, 107, 32, 104, 101, 114, 32, 115, - 105, 115, 116, 101, 114, 32, 119, 97, 115, 32, 114, 101, 97, 100, 105, 110, 103, 44, 32, - 98, 117, 116, 32, 105, 116, 32, 104, 97, 100, 32, 110, 111, 32, 112, 105, 99, 116, 117, - 114, 101, 115, 32, 111, 114, 32, 99, 111, 110, 118, 101, 114, 115, 97, 116, 105, - ]) - .unwrap(); - let mut out_packet = NetPacket::new([0; 1000]).unwrap(); - let mut src_out_packet = NetPacket::new([0; 1000]).unwrap(); - lz4.compress(&in_packet, &mut out_packet).unwrap(); - let tail = out_packet.split_tail_packet().unwrap(); - match tail { - ExtensionTailPacket::Compression(c) => match c.algorithm() { - CompressionAlgorithm::Lz4 => { - Compressor::decompress(CompressionAlgorithm::Lz4, &out_packet, &mut src_out_packet) - .unwrap(); - } - _ => { - unimplemented!() - } - }, - _ => { - unimplemented!() - } - } - assert!(!out_packet.is_extension()); - assert_eq!(in_packet.payload(), src_out_packet.payload()) -} -#[test] -#[cfg(feature = "zstd_compress")] -fn test_zstd() { - use crate::protocol::extension::{CompressionAlgorithm, ExtensionTailPacket}; - let zstd = Compressor::Zstd(22); - let in_packet = NetPacket::new([ - 65, 108, 105, 99, 101, 32, 119, 97, 115, 32, 98, 101, 103, 105, 110, 110, 105, 110, 103, - 32, 116, 111, 32, 103, 101, 116, 32, 118, 101, 114, 121, 32, 116, 105, 114, 101, 100, 32, - 111, 102, 32, 115, 105, 116, 116, 105, 110, 103, 32, 98, 121, 32, 104, 101, 114, 32, 115, - 105, 115, 116, 101, 114, 32, 111, 110, 32, 116, 104, 101, 32, 98, 97, 110, 107, 44, 32, 97, - 110, 100, 32, 111, 102, 32, 104, 97, 118, 105, 110, 103, 32, 110, 111, 116, 104, 105, 110, - 103, 32, 116, 111, 32, 100, 111, 58, 32, 111, 110, 99, 101, 32, 111, 114, 32, 116, 119, - 105, 99, 101, 32, 115, 104, 101, 32, 104, 97, 100, 32, 112, 101, 101, 112, 101, 100, 32, - 105, 110, 116, 111, 32, 116, 104, 101, 32, 98, 111, 111, 107, 32, 104, 101, 114, 32, 115, - 105, 115, 116, 101, 114, 32, 119, 97, 115, 32, 114, 101, 97, 100, 105, 110, 103, 44, 32, - 98, 117, 116, 32, 105, 116, 32, 104, 97, 100, 32, 110, 111, 32, 112, 105, 99, 116, 117, - 114, 101, 115, 32, 111, 114, 32, 99, 111, 110, 118, 101, 114, 115, 97, 116, 105, - ]) - .unwrap(); - let mut out_packet = NetPacket::new([0; 1000]).unwrap(); - let mut src_out_packet = NetPacket::new([0; 1000]).unwrap(); - zstd.compress(&in_packet, &mut out_packet).unwrap(); - let tail = out_packet.split_tail_packet().unwrap(); - match tail { - ExtensionTailPacket::Compression(c) => match c.algorithm() { - CompressionAlgorithm::Zstd => { - Compressor::decompress( - CompressionAlgorithm::Zstd, - &out_packet, - &mut src_out_packet, - ) - .unwrap(); - } - _ => { - unimplemented!() - } - }, - _ => { - unimplemented!() - } - } - assert!(!out_packet.is_extension()); - assert_eq!(in_packet.payload(), src_out_packet.payload()) -} diff --git a/vnt/src/compression/zstd_compress.rs b/vnt/src/compression/zstd_compress.rs deleted file mode 100644 index d647ecb..0000000 --- a/vnt/src/compression/zstd_compress.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crate::protocol::NetPacket; -use anyhow::anyhow; -use zstd::zstd_safe::CompressionLevel; - -#[derive(Clone)] -pub struct ZstdCompressor; - -impl ZstdCompressor { - pub fn compress, O: AsRef<[u8]> + AsMut<[u8]>>( - compression_level: CompressionLevel, - in_net_packet: &NetPacket, - out: &mut NetPacket, - ) -> anyhow::Result<()> { - out.set_data_len_max(); - let len = match zstd::zstd_safe::compress( - out.payload_mut(), - in_net_packet.payload(), - compression_level, - ) { - Ok(len) => len, - Err(e) => Err(anyhow!("zstd compress {}", e))?, - }; - out.set_payload_len(len)?; - Ok(()) - } - pub fn decompress, O: AsRef<[u8]> + AsMut<[u8]>>( - in_net_packet: &NetPacket, - out: &mut NetPacket, - ) -> anyhow::Result<()> { - out.set_data_len_max(); - let len = match zstd::zstd_safe::decompress(out.payload_mut(), in_net_packet.payload()) { - Ok(len) => len, - Err(e) => Err(anyhow!("zstd decompress {}", e))?, - }; - out.set_payload_len(len)?; - Ok(()) - } -} diff --git a/vnt/src/core/conn.rs b/vnt/src/core/conn.rs deleted file mode 100644 index 22cc3f6..0000000 --- a/vnt/src/core/conn.rs +++ /dev/null @@ -1,538 +0,0 @@ -use std::collections::HashMap; -use std::net::Ipv4Addr; -use std::ops::Deref; -use std::sync::Arc; -use std::time::Duration; - -use crossbeam_utils::atomic::AtomicCell; -use parking_lot::{Mutex, RwLock}; -use rand::Rng; - -use crate::channel::context::ChannelContext; -use crate::channel::idle::Idle; -use crate::channel::punch::{NatInfo, Punch}; -use crate::channel::sender::IpPacketSender; -use crate::channel::{init_channel, init_context, Route, RouteKey}; -use crate::cipher::Cipher; -#[cfg(feature = "server_encrypt")] -use crate::cipher::RsaCipher; -use crate::compression::Compressor; -use crate::core::Config; -use crate::external_route::{AllowExternalRoute, ExternalRoute}; -use crate::handle::handshaker::Handshake; -use crate::handle::maintain::PunchReceiver; -use crate::handle::recv_data::RecvDataHandler; -use crate::handle::{maintain, BaseConfigInfo, ConnectStatus, CurrentDeviceInfo, PeerDeviceInfo}; -use crate::nat::NatTest; -#[cfg(feature = "integrated_tun")] -use crate::tun_tap_device::tun_create_helper::{DeviceAdapter, TunDeviceHelper}; -use crate::tun_tap_device::vnt_device::DeviceWrite; -use crate::util::limit::TrafficMeterMultiAddress; -use crate::util::{Scheduler, StopManager}; -use crate::{nat, VntCallback}; - -#[derive(Clone)] -pub struct Vnt { - inner: Arc, -} - -impl Vnt { - #[cfg(feature = "integrated_tun")] - pub fn new(config: Config, callback: Call) -> anyhow::Result { - let inner = Arc::new(VntInner::new(config, callback)?); - Ok(Self { inner }) - } - #[cfg(not(feature = "integrated_tun"))] - pub fn new_device( - config: Config, - callback: Call, - device: Device, - ) -> anyhow::Result { - let inner = Arc::new(VntInner::new_device(config, callback, device)?); - Ok(Self { inner }) - } -} - -impl Deref for Vnt { - type Target = VntInner; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -pub struct VntInner { - stop_manager: StopManager, - config: Config, - current_device: Arc>, - nat_test: NatTest, - device_map: Arc)>>, - context: Arc>>, - peer_nat_info_map: Arc>>, - client_secret_hash: Option<[u8; 16]>, - compressor: Compressor, - client_cipher: Cipher, - server_cipher: Cipher, - external_route: ExternalRoute, - up_traffic_meter: Option, - down_traffic_meter: Option, -} - -impl VntInner { - #[cfg(feature = "integrated_tun")] - pub fn new(config: Config, callback: Call) -> anyhow::Result { - VntInner::new_device0(config, callback, DeviceAdapter::default()) - } - #[cfg(not(feature = "integrated_tun"))] - pub fn new_device( - config: Config, - callback: Call, - device: Device, - ) -> anyhow::Result { - VntInner::new_device0(config, callback, device) - } - fn new_device0( - config: Config, - callback: Call, - device: Device, - ) -> anyhow::Result { - log::info!("config: {:?}", config); - let (up_traffic_meter, down_traffic_meter) = if config.enable_traffic { - ( - Some(TrafficMeterMultiAddress::default()), - Some(TrafficMeterMultiAddress::default()), - ) - } else { - (None, None) - }; - - //服务端非对称加密 - #[cfg(feature = "server_encrypt")] - let rsa_cipher: Arc>> = Arc::new(Mutex::new(None)); - //服务端对称加密 - let server_cipher: Cipher = if config.server_encrypt { - let mut key = [0u8; 32]; - rand::thread_rng().fill(&mut key); - Cipher::new_key(key, config.token.clone())? - } else { - Cipher::None - }; - let finger = if config.finger { - Some(config.token.clone()) - } else { - None - }; - //客户端对称加密 - let client_cipher = - Cipher::new_password(config.cipher_model, config.password.clone(), finger)?; - //当前设备信息 - let current_device = Arc::new(AtomicCell::new(CurrentDeviceInfo::new0( - config.server_address, - ))); - //设备列表 - let device_map: Arc)>> = - 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(), - config.token.clone(), - config.ip, - config.password_hash(), - config.server_encrypt, - config.device_id.clone(), - config.server_address_str.clone(), - config.name_servers.clone(), - config.mtu.unwrap_or(1420), - #[cfg(feature = "integrated_tun")] - #[cfg(target_os = "windows")] - config.tap, - #[cfg(feature = "integrated_tun")] - #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] - config.device_name.clone(), - config.allow_wire_guard, - default_interface.clone(), - ); - // 服务停止管理器 - let stop_manager = { - let callback = callback.clone(); - StopManager::new(move || callback.stop()) - }; - #[cfg(feature = "port_mapping")] - crate::port_mapping::start_port_mapping( - stop_manager.clone(), - config.port_mapping_list.clone(), - )?; - let mut ports = config.ports.as_ref().map_or(vec![0, 0], |v| { - if v.is_empty() { - vec![0, 0] - } else { - v.clone() - } - }); - if config.use_channel_type.is_only_relay() { - //中继模式下只监听一个端口就够了 - ports.truncate(1); - } - //通道上下文 - let (context, tcp_listener) = init_context( - ports, - config.use_channel_type, - config.first_latency, - config.protocol, - config.packet_loss_rate, - config.packet_delay, - default_interface, - up_traffic_meter.clone(), - down_traffic_meter.clone(), - )?; - let local_ipv6 = nat::local_ipv6(); - let udp_ports = context.main_local_udp_port()?; - let tcp_port = tcp_listener.local_addr()?.port(); - //nat检测工具 - let nat_test = NatTest::new( - context.channel_num(), - config.stun_server.clone(), - local_ipv4, - local_ipv6, - udp_ports, - tcp_port, - config.local_ipv4.is_none(), - config.punch_model, - ); - // 定时器 - let scheduler = Scheduler::new(stop_manager.clone())?; - let external_route = ExternalRoute::new(config.in_ips.clone()); - let out_external_route = AllowExternalRoute::new(config.out_ips.clone()); - - #[cfg(feature = "ip_proxy")] - #[cfg(feature = "integrated_tun")] - let proxy_map = if !config.out_ips.is_empty() && !config.no_proxy { - Some(crate::ip_proxy::init_proxy( - context.clone(), - stop_manager.clone(), - current_device.clone(), - client_cipher.clone(), - )?) - } else { - None - }; - let (punch_sender, punch_receiver) = maintain::punch_channel(); - let peer_nat_info_map: Arc>> = - Arc::new(RwLock::new(HashMap::with_capacity(16))); - let handshake = Handshake::new( - #[cfg(feature = "server_encrypt")] - rsa_cipher.clone(), - ); - #[cfg(feature = "integrated_tun")] - let tun_device_helper = { - TunDeviceHelper::new( - stop_manager.clone(), - context.clone(), - current_device.clone(), - external_route.clone(), - #[cfg(feature = "ip_proxy")] - proxy_map.clone(), - client_cipher.clone(), - server_cipher.clone(), - device_map.clone(), - config.compressor, - device.clone().into_device_adapter(), - ) - }; - - let handler = RecvDataHandler::new( - #[cfg(feature = "server_encrypt")] - rsa_cipher, - server_cipher.clone(), - client_cipher.clone(), - current_device.clone(), - device, - device_map.clone(), - config_info.clone(), - nat_test.clone(), - callback.clone(), - punch_sender, - peer_nat_info_map.clone(), - external_route.clone(), - out_external_route, - #[cfg(feature = "ip_proxy")] - #[cfg(feature = "integrated_tun")] - proxy_map.clone(), - handshake.clone(), - #[cfg(feature = "integrated_tun")] - tun_device_helper, - ); - - //初始化网络数据通道 - let (udp_socket_sender, connect_util) = - init_channel(tcp_listener, context.clone(), stop_manager.clone(), handler)?; - // 打洞逻辑 - let punch = Punch::new( - context.clone(), - config.punch_model, - connect_util.clone(), - nat_test.clone(), - current_device.clone(), - ); - - // #[cfg(not(target_os = "android"))] - // tun_helper.start(device)?; - - maintain::idle_gateway( - &scheduler, - context.clone(), - current_device.clone(), - config_info.clone(), - connect_util.clone(), - callback.clone(), - 0, - handshake, - ); - { - let context = context.clone(); - let nat_test = nat_test.clone(); - let device_map = device_map.clone(); - let config_info = config_info.clone(); - let current_device = current_device.clone(); - if !config.use_channel_type.is_only_relay() { - // 定时nat探测 - maintain::retrieve_nat_type( - &scheduler, - context.clone(), - nat_test.clone(), - udp_socket_sender, - ); - } - let client_cipher = client_cipher.clone(); - let server_cipher = server_cipher.clone(); - //延迟启动 - scheduler.timeout(Duration::from_secs(1), move |scheduler| { - start( - scheduler, - context, - nat_test, - device_map, - current_device, - client_cipher, - server_cipher, - punch_receiver, - config_info, - punch, - callback, - ); - }); - } - let compressor = config.compressor; - Ok(Self { - stop_manager, - config, - current_device, - nat_test, - device_map, - context: Arc::new(Mutex::new(Some(context))), - peer_nat_info_map, - client_secret_hash: config_info.client_secret_hash, - compressor, - client_cipher, - server_cipher, - external_route, - up_traffic_meter, - down_traffic_meter, - }) - } -} - -pub fn start( - scheduler: &Scheduler, - context: ChannelContext, - nat_test: NatTest, - device_map: Arc)>>, - current_device: Arc>, - client_cipher: Cipher, - server_cipher: Cipher, - punch_receiver: PunchReceiver, - config_info: BaseConfigInfo, - punch: Punch, - callback: Call, -) { - // 定时心跳 - maintain::heartbeat( - &scheduler, - context.clone(), - current_device.clone(), - device_map.clone(), - client_cipher.clone(), - server_cipher.clone(), - ); - // 路由空闲检测逻辑 - let idle = Idle::new(Duration::from_secs(10), context.clone()); - // 定时空闲检查 - maintain::idle_route( - &scheduler, - idle, - context.clone(), - current_device.clone(), - callback, - ); - // 定时客户端中继检测 - if !context.use_channel_type().is_only_p2p() { - maintain::client_relay( - &scheduler, - context.clone(), - current_device.clone(), - device_map.clone(), - client_cipher.clone(), - ); - } - - if !context.use_channel_type().is_only_relay() { - // 定时地址探测 - maintain::addr_request( - &scheduler, - context.clone(), - current_device.clone(), - nat_test.clone(), - config_info.clone(), - ); - // 定时打洞 - maintain::punch( - &scheduler, - context.clone(), - nat_test.clone(), - device_map.clone(), - current_device.clone(), - client_cipher.clone(), - punch_receiver, - punch, - ); - } - maintain::up_status(scheduler, context.clone(), current_device.clone()) -} - -impl VntInner { - pub fn name(&self) -> &str { - &self.config.name - } - pub fn server_encrypt(&self) -> bool { - self.config.server_encrypt - } - pub fn client_encrypt(&self) -> bool { - self.config.password.is_some() - } - pub fn client_encrypt_hash(&self) -> Option<&[u8]> { - self.client_secret_hash.as_ref().map(|v| v.as_ref()) - } - pub fn current_device(&self) -> CurrentDeviceInfo { - self.current_device.load() - } - pub fn current_device_info(&self) -> Arc> { - self.current_device.clone() - } - pub fn peer_nat_info(&self, ip: &Ipv4Addr) -> Option { - self.peer_nat_info_map.read().get(ip).cloned() - } - pub fn connection_status(&self) -> ConnectStatus { - self.current_device.load().status - } - pub fn nat_info(&self) -> NatInfo { - self.nat_test.nat_info() - } - pub fn device_list(&self) -> Vec { - let device_list_lock = self.device_map.lock(); - let (_epoch, device_list) = device_list_lock.clone(); - drop(device_list_lock); - device_list.into_values().collect() - } - pub fn route(&self, ip: &Ipv4Addr) -> Option { - self.context.lock().as_ref()?.route_table.route_one(ip) - } - pub fn is_gateway(&self, ip: &Ipv4Addr) -> bool { - self.current_device.load().is_gateway(ip) - } - pub fn route_key(&self, route_key: &RouteKey) -> Option { - self.context - .lock() - .as_ref()? - .route_table - .route_to_id(route_key) - } - pub fn route_table(&self) -> Vec<(Ipv4Addr, Vec)> { - if let Some(context) = self.context.lock().as_ref() { - context.route_table.route_table() - } else { - vec![] - } - } - pub fn up_stream(&self) -> u64 { - self.up_traffic_meter.as_ref().map_or(0, |v| v.total()) - } - pub fn up_stream_all(&self) -> Option<(u64, HashMap)> { - self.up_traffic_meter.as_ref().map(|v| v.get_all()) - } - pub fn up_stream_history(&self) -> Option<(u64, HashMap)>)> { - self.up_traffic_meter.as_ref().map(|v| v.get_all_history()) - } - pub fn down_stream(&self) -> u64 { - self.down_traffic_meter.as_ref().map_or(0, |v| v.total()) - } - pub fn down_stream_all(&self) -> Option<(u64, HashMap)> { - self.down_traffic_meter.as_ref().map(|v| v.get_all()) - } - pub fn down_stream_history(&self) -> Option<(u64, HashMap)>)> { - self.down_traffic_meter - .as_ref() - .map(|v| v.get_all_history()) - } - pub fn stop(&self) { - //退出协助回收资源 - let _ = self.context.lock().take(); - self.stop_manager.stop() - } - pub fn is_stopped(&self) -> bool { - self.stop_manager.is_stopped() - } - pub fn add_stop_listener(&self, name: String, f: F) -> anyhow::Result - where - F: FnOnce() + Send + 'static, - { - self.stop_manager.add_listener(name, f) - } - pub fn wait(&self) { - self.stop_manager.wait() - } - pub fn wait_timeout(&self, dur: Duration) -> bool { - self.stop_manager.wait_timeout(dur) - } - pub fn config(&self) -> &Config { - &self.config - } - pub fn ipv4_packet_sender(&self) -> Option { - if let Some(c) = self.context.lock().as_ref() { - Some(IpPacketSender::new( - c.clone(), - self.current_device.clone(), - self.compressor.clone(), - self.client_cipher.clone(), - self.server_cipher.clone(), - self.external_route.clone(), - self.device_map.clone(), - self.config.allow_wire_guard, - )) - } else { - None - } - } -} - -impl Drop for VntInner { - fn drop(&mut self) { - self.stop(); - } -} diff --git a/vnt/src/core/mod.rs b/vnt/src/core/mod.rs deleted file mode 100644 index c55262e..0000000 --- a/vnt/src/core/mod.rs +++ /dev/null @@ -1,247 +0,0 @@ -use anyhow::anyhow; -use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr}; -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; -use crate::util::{address_choose, dns_query_all}; - -mod conn; - -#[derive(Clone, Debug)] -pub struct Config { - #[cfg(feature = "integrated_tun")] - #[cfg(target_os = "windows")] - pub tap: bool, - pub token: String, - pub device_id: String, - pub name: String, - pub server_address: SocketAddr, - pub server_address_str: String, - pub name_servers: Vec, - pub stun_server: Vec, - pub in_ips: Vec<(u32, u32, Ipv4Addr)>, - pub out_ips: Vec<(u32, u32)>, - pub password: Option, - pub mtu: Option, - pub protocol: ConnectProtocol, - pub ip: Option, - #[cfg(feature = "ip_proxy")] - #[cfg(feature = "integrated_tun")] - pub no_proxy: bool, - pub server_encrypt: bool, - pub cipher_model: CipherModel, - pub finger: bool, - pub punch_model: PunchModel, - pub ports: Option>, - pub first_latency: bool, - #[cfg(feature = "integrated_tun")] - #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] - pub device_name: Option, - pub use_channel_type: UseChannelType, - //控制丢包率 - pub packet_loss_rate: Option, - pub packet_delay: u32, - // 端口映射 - #[cfg(feature = "port_mapping")] - pub port_mapping_list: Vec<(bool, SocketAddr, String)>, - pub compressor: Compressor, - pub enable_traffic: bool, - pub allow_wire_guard: bool, - pub local_ipv4: Option, - pub local_interface: LocalInterface, -} - -impl Config { - pub fn new( - #[cfg(feature = "integrated_tun")] - #[cfg(target_os = "windows")] - tap: bool, - token: String, - device_id: String, - name: String, - server_address_str: String, - mut name_servers: Vec, - mut stun_server: Vec, - mut in_ips: Vec<(u32, u32, Ipv4Addr)>, - out_ips: Vec<(u32, u32)>, - password: Option, - mtu: Option, - ip: Option, - #[cfg(feature = "integrated_tun")] - #[cfg(feature = "ip_proxy")] - no_proxy: bool, - server_encrypt: bool, - cipher_model: CipherModel, - finger: bool, - punch_model: PunchModel, - ports: Option>, - first_latency: bool, - #[cfg(feature = "integrated_tun")] - #[cfg(not(target_os = "android"))] - device_name: Option, - use_channel_type: UseChannelType, - packet_loss_rate: Option, - packet_delay: u32, - // 例如 [udp:127.0.0.1:80->10.26.0.10:8080,tcp:127.0.0.1:80->10.26.0.10:8080] - #[cfg(feature = "port_mapping")] port_mapping_list: Vec, - compressor: Compressor, - enable_traffic: bool, - // 允许传递wg流量 - allow_wire_guard: bool, - local_dev: Option, - ) -> anyhow::Result { - for x in stun_server.iter_mut() { - if !x.contains(":") { - x.push_str(":3478"); - } - } - for x in name_servers.iter_mut() { - if Ipv6Addr::from_str(x).is_ok() { - x.push_str(":53"); - } else if !x.contains(":") { - x.push_str(":53"); - } - } - if token.is_empty() || token.len() > 128 { - return Err(anyhow!("token too long")); - } - if device_id.is_empty() || device_id.len() > 128 { - return Err(anyhow!("device_id too long")); - } - if name.is_empty() || name.len() > 128 { - return Err(anyhow!("name too long")); - } - let mut server_address_str = server_address_str.to_lowercase(); - let mut _query_dns = true; - let mut protocol = ConnectProtocol::UDP; - if server_address_str.starts_with("ws://") { - #[cfg(not(feature = "ws"))] - Err(anyhow!("Ws not supported"))?; - protocol = ConnectProtocol::WS; - _query_dns = false; - } - if server_address_str.starts_with("wss://") { - #[cfg(not(feature = "wss"))] - Err(anyhow!("Wss not supported"))?; - protocol = ConnectProtocol::WSS; - _query_dns = false; - } - - let mut server_address = "0.0.0.0:0".parse().unwrap(); - if _query_dns { - if let Some(s) = server_address_str.strip_prefix("udp://") { - server_address_str = s.to_string(); - } else if let Some(s) = server_address_str.strip_prefix("tcp://") { - server_address_str = s.to_string(); - protocol = ConnectProtocol::TCP; - } - 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)?; - - for (dest, mask, _) in &mut in_ips { - *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")] - tap, - token, - device_id, - name, - server_address, - server_address_str, - name_servers, - stun_server, - in_ips, - out_ips, - password, - mtu, - protocol, - ip, - #[cfg(feature = "ip_proxy")] - #[cfg(feature = "integrated_tun")] - no_proxy, - server_encrypt, - cipher_model, - finger, - punch_model, - ports, - first_latency, - #[cfg(feature = "integrated_tun")] - #[cfg(not(target_os = "android"))] - device_name, - use_channel_type, - packet_loss_rate, - packet_delay, - #[cfg(feature = "port_mapping")] - port_mapping_list, - compressor, - enable_traffic, - allow_wire_guard, - local_ipv4, - local_interface, - }) - } -} - -impl Config { - pub fn password_hash(&self) -> Option<[u8; 16]> { - if let Some(p) = self.password.as_ref() { - match self.cipher_model { - CipherModel::Xor => { - let key = crate::cipher::simple_hash(&format!("Xor{}{}", p, self.token)); - Some(key[16..].try_into().unwrap()) - } - CipherModel::None => None, - #[cfg(cipher)] - _ => { - use sha2::Digest; - let mut hasher = sha2::Sha256::new(); - hasher.update(self.cipher_model.to_string().as_bytes()); - hasher.update(p.as_bytes()); - hasher.update(self.token.as_bytes()); - let key: [u8; 32] = hasher.finalize().into(); - Some(key[16..].try_into().unwrap()) - } - } - } else { - None - } - } -} diff --git a/vnt/src/external_route/mod.rs b/vnt/src/external_route/mod.rs deleted file mode 100644 index ad95bbe..0000000 --- a/vnt/src/external_route/mod.rs +++ /dev/null @@ -1,66 +0,0 @@ -use std::net::Ipv4Addr; -use std::sync::Arc; - -// 目标网段,子网掩码,网关 -#[derive(Clone)] -pub struct ExternalRoute { - route_table: Vec<(u32, u32, Ipv4Addr)>, -} - -impl ExternalRoute { - pub fn new(mut route_table: Vec<(u32, u32, Ipv4Addr)>) -> Self { - for (dest, mask, _) in &mut route_table { - *dest = *mask & *dest; - } - route_table.sort_by(|(dest1, _, _), (dest2, _, _)| dest2.cmp(dest1)); - Self { route_table } - } - pub fn route(&self, ip: &Ipv4Addr) -> Option { - if self.route_table.is_empty() { - return None; - } - let ip = u32::from_be_bytes(ip.octets()); - for (dest, mask, gateway) in self.route_table.iter() { - if *mask & ip == *dest { - return Some(*gateway); - } - } - None - } - pub fn to_route(&self) -> Vec<(Ipv4Addr, Ipv4Addr)> { - self.route_table - .iter() - .map(|(dest, mask, _)| (Ipv4Addr::from(*dest), Ipv4Addr::from(*mask))) - .collect::>() - } -} - -// 目标网段,子网掩码 -#[derive(Clone)] -pub struct AllowExternalRoute { - route_table: Arc>, -} - -impl AllowExternalRoute { - pub fn new(mut route_table: Vec<(u32, u32)>) -> Self { - for (dest, mask) in &mut route_table { - *dest = *mask & *dest; - } - route_table.sort_by(|(dest1, _), (dest2, _)| dest2.cmp(dest1)); - Self { - route_table: Arc::new(route_table), - } - } - pub fn allow(&self, ip: &Ipv4Addr) -> bool { - if self.route_table.is_empty() { - return false; - } - let ip = u32::from_be_bytes(ip.octets()); - for (dest, mask) in self.route_table.iter() { - if *mask & ip == *mask & *dest { - return true; - } - } - false - } -} diff --git a/vnt/src/handle/callback.rs b/vnt/src/handle/callback.rs deleted file mode 100644 index 7f32eb4..0000000 --- a/vnt/src/handle/callback.rs +++ /dev/null @@ -1,322 +0,0 @@ -use crate::handle::PeerDeviceStatus; -#[cfg(feature = "server_encrypt")] -use rsa::RsaPublicKey; -use std::fmt::{Display, Formatter}; -use std::io; -use std::net::{Ipv4Addr, SocketAddr}; - -#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] -#[derive(Debug)] -pub struct DeviceInfo { - pub name: String, - pub version: String, -} - -#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] -impl Display for DeviceInfo { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(&format!("name={} ,version={}", self.name, self.version)) - } -} - -#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] -impl DeviceInfo { - pub fn new(name: String, version: String) -> Self { - return Self { name, version }; - } -} - -#[derive(Debug)] -pub struct ConnectInfo { - // 第几次连接,从1开始 - pub count: usize, - // 服务端地址 - pub address: SocketAddr, -} - -impl Display for ConnectInfo { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(&format!("count={} ,address={}", self.count, self.address)) - } -} - -impl ConnectInfo { - pub fn new(count: usize, address: SocketAddr) -> Self { - Self { count, address } - } -} - -#[derive(Debug)] -pub struct HandshakeInfo { - //服务端公钥 - #[cfg(feature = "server_encrypt")] - pub public_key: Option, - //服务端指纹 - #[cfg(feature = "server_encrypt")] - pub finger: Option, - //服务端版本 - pub version: String, -} - -impl Display for HandshakeInfo { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - #[cfg(feature = "server_encrypt")] - return match &self.finger { - None => f.write_str(&format!("no_secret server version={}", self.version)), - Some(finger) => f.write_str(&format!( - "finger={} ,server version={}", - finger, self.version - )), - }; - #[cfg(not(feature = "server_encrypt"))] - f.write_str(&format!("server version={}", self.version)) - } -} - -#[cfg(feature = "server_encrypt")] -impl HandshakeInfo { - pub fn new(public_key: RsaPublicKey, finger: String, version: String) -> Self { - Self { - public_key: Some(public_key), - finger: Some(finger), - version, - } - } - pub fn new_no_secret(version: String) -> Self { - Self { - public_key: None, - finger: None, - version, - } - } -} - -#[cfg(not(feature = "server_encrypt"))] -impl HandshakeInfo { - pub fn new_no_secret(version: String) -> Self { - Self { version } - } -} - -#[derive(Debug)] -pub struct RegisterInfo { - //本机虚拟IP - pub virtual_ip: Ipv4Addr, - //子网掩码 - pub virtual_netmask: Ipv4Addr, - //虚拟网关 - pub virtual_gateway: Ipv4Addr, -} - -impl Display for RegisterInfo { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(&format!( - "ip={} ,netmask={} ,gateway={}", - self.virtual_ip, self.virtual_netmask, self.virtual_gateway, - )) - } -} - -impl RegisterInfo { - pub fn new(virtual_ip: Ipv4Addr, virtual_netmask: Ipv4Addr, virtual_gateway: Ipv4Addr) -> Self { - Self { - virtual_ip, - virtual_netmask, - virtual_gateway, - } - } -} - -#[derive(Debug)] -pub struct ErrorInfo { - pub code: ErrorType, - pub msg: Option, - pub source: Option, -} - -impl Display for ErrorInfo { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(&format!("ErrorType={:?} ", self.code))?; - if let Some(msg) = &self.msg { - f.write_str(&format!(",msg={:?} ", msg))?; - } - if let Some(source) = &self.source { - f.write_str(&format!(",source={:?} ", source))?; - } - Ok(()) - } -} - -impl ErrorInfo { - pub fn new(code: ErrorType) -> Self { - Self { - code, - msg: None, - source: None, - } - } - pub fn new_msg(code: ErrorType, msg: String) -> Self { - Self { - code, - msg: Some(msg), - source: None, - } - } -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum ErrorType { - TokenError, - Disconnect, - AddressExhausted, - IpAlreadyExists, - InvalidIp, - LocalIpExists, - FailedToCrateDevice, - Warn, - Unknown, -} - -impl Into for ErrorType { - fn into(self) -> u8 { - match self { - ErrorType::TokenError => 1, - ErrorType::Disconnect => 2, - ErrorType::AddressExhausted => 3, - ErrorType::IpAlreadyExists => 4, - ErrorType::InvalidIp => 5, - ErrorType::LocalIpExists => 6, - ErrorType::FailedToCrateDevice => 101, - ErrorType::Warn => 102, - ErrorType::Unknown => 255, - } - } -} - -#[derive(Clone, Debug)] -pub struct DeviceConfig { - #[cfg(feature = "integrated_tun")] - #[cfg(target_os = "windows")] - pub tap: bool, - #[cfg(feature = "integrated_tun")] - #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] - pub device_name: Option, - //虚拟网卡mtu值 - pub mtu: u32, - //本机虚拟IP - pub virtual_ip: Ipv4Addr, - //子网掩码 - pub virtual_netmask: Ipv4Addr, - //虚拟网关 - pub virtual_gateway: Ipv4Addr, - //虚拟网段 - pub virtual_network: Ipv4Addr, - // 额外的路由 - pub external_route: Vec<(Ipv4Addr, Ipv4Addr)>, -} - -impl DeviceConfig { - pub fn new( - #[cfg(feature = "integrated_tun")] - #[cfg(target_os = "windows")] - tap: bool, - #[cfg(feature = "integrated_tun")] - #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] - device_name: Option, - mtu: u32, - virtual_ip: Ipv4Addr, - virtual_netmask: Ipv4Addr, - virtual_gateway: Ipv4Addr, - virtual_network: Ipv4Addr, - external_route: Vec<(Ipv4Addr, Ipv4Addr)>, - ) -> Self { - Self { - #[cfg(feature = "integrated_tun")] - #[cfg(target_os = "windows")] - tap, - #[cfg(feature = "integrated_tun")] - #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] - device_name, - mtu, - virtual_ip, - virtual_netmask, - virtual_gateway, - virtual_network, - external_route, - } - } -} - -impl Display for DeviceConfig { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(&format!( - "ip={} ,netmask={} ,gateway={}, external_route={:?}", - self.virtual_ip, self.virtual_netmask, self.virtual_gateway, self.external_route - )) - } -} - -#[derive(Debug)] -pub struct PeerClientInfo { - pub virtual_ip: Ipv4Addr, - pub name: String, - pub status: PeerDeviceStatus, - pub client_secret: bool, -} - -impl PeerClientInfo { - pub fn new( - virtual_ip: Ipv4Addr, - name: String, - status: PeerDeviceStatus, - client_secret: bool, - ) -> Self { - Self { - virtual_ip, - name, - status, - client_secret, - } - } -} - -impl Display for PeerClientInfo { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(&format!( - "ip={} ,name={} ,status={:?}, client_secret={}", - self.virtual_ip, self.name, self.status, self.client_secret - )) - } -} - -pub trait VntCallback: Clone + Send + Sync + 'static { - /// 启动成功 - fn success(&self) {} - - /// 创建网卡的信息 - #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] - #[cfg(feature = "integrated_tun")] - fn create_tun(&self, _info: DeviceInfo) {} - /// 连接 - fn connect(&self, _info: ConnectInfo) {} - /// 握手,返回false则拒绝握手,可在此处检查服务端信息 - fn handshake(&self, _info: HandshakeInfo) -> bool { - true - } - /// 注册,返回false则拒绝注册 - fn register(&self, _info: RegisterInfo) -> bool { - true - } - #[cfg(not(feature = "integrated_tun"))] - fn create_device(&self, _info: DeviceConfig) {} - #[cfg(target_os = "android")] - #[cfg(feature = "integrated_tun")] - fn generate_tun(&self, _info: DeviceConfig) -> usize { - 0 - } - fn peer_client_list(&self, _info: Vec) {} - /// 异常信息 - fn error(&self, _info: ErrorInfo) {} - /// 服务停止 - fn stop(&self) {} -} diff --git a/vnt/src/handle/extension/mod.rs b/vnt/src/handle/extension/mod.rs deleted file mode 100644 index 45e13cb..0000000 --- a/vnt/src/handle/extension/mod.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::compression::Compressor; -use crate::protocol::extension::ExtensionTailPacket; -use crate::protocol::NetPacket; -use anyhow::anyhow; - -pub fn handle_extension_tail + AsMut<[u8]>, O: AsRef<[u8]> + AsMut<[u8]>>( - in_net_packet: &mut NetPacket, - out: &mut NetPacket, -) -> anyhow::Result { - if in_net_packet.is_extension() { - let tail_packet = in_net_packet.split_tail_packet()?; - match tail_packet { - ExtensionTailPacket::Compression(extension) => { - let compression_algorithm = extension.algorithm(); - Compressor::decompress(compression_algorithm, &in_net_packet, out)?; - out.head_mut().copy_from_slice(in_net_packet.head()); - Ok(true) - } - ExtensionTailPacket::Unknown => Err(anyhow!("Unknown decompress")), - } - } else { - Ok(false) - } -} diff --git a/vnt/src/handle/handshaker.rs b/vnt/src/handle/handshaker.rs deleted file mode 100644 index bd8a401..0000000 --- a/vnt/src/handle/handshaker.rs +++ /dev/null @@ -1,112 +0,0 @@ -use std::io; -use std::net::SocketAddr; -use std::sync::Arc; -use std::time::{Duration, Instant}; - -use crossbeam_utils::atomic::AtomicCell; -#[cfg(feature = "server_encrypt")] -use parking_lot::Mutex; -use protobuf::Message; - -use crate::channel::context::ChannelContext; -#[cfg(feature = "server_encrypt")] -use crate::cipher::RsaCipher; -use crate::handle::{GATEWAY_IP, SELF_IP}; -use crate::proto::message::HandshakeRequest; -#[cfg(feature = "server_encrypt")] -use crate::proto::message::SecretHandshakeRequest; -#[cfg(feature = "server_encrypt")] -use crate::protocol::body::RSA_ENCRYPTION_RESERVED; -use crate::protocol::{service_packet, NetPacket, Protocol, MAX_TTL}; - -#[derive(Clone)] -pub struct Handshake { - time: Arc>, - #[cfg(feature = "server_encrypt")] - rsa_cipher: Arc>>, -} -impl Handshake { - pub fn new( - #[cfg(feature = "server_encrypt")] rsa_cipher: Arc>>, - ) -> Self { - Handshake { - time: Arc::new(AtomicCell::new( - Instant::now() - .checked_sub(Duration::from_secs(60)) - .unwrap_or(Instant::now()), - )), - #[cfg(feature = "server_encrypt")] - rsa_cipher, - } - } - pub fn send(&self, context: &ChannelContext, secret: bool, addr: SocketAddr) -> io::Result<()> { - let last = self.time.load(); - //短时间不重复发送 - if last.elapsed() < Duration::from_secs(3) { - return Ok(()); - } - let request_packet = self.handshake_request_packet(secret)?; - log::info!("发送握手请求,secret={},{:?}", secret, addr); - context.send_default(&request_packet, addr)?; - self.time.store(Instant::now()); - Ok(()) - } - /// 第一次握手数据 - pub fn handshake_request_packet(&self, secret: bool) -> io::Result>> { - let mut request = HandshakeRequest::new(); - request.secret = secret; - request.version = crate::VNT_VERSION.to_string(); - #[cfg(feature = "server_encrypt")] - if let Some(finger) = self.rsa_cipher.lock().as_ref().map(|v| v.finger().clone()) { - request.key_finger = finger; - } - let bytes = request.write_to_bytes().map_err(|e| { - io::Error::new( - io::ErrorKind::Other, - format!("handshake_request_packet {:?}", e), - ) - })?; - let buf = vec![0u8; 12 + bytes.len()]; - let mut net_packet = NetPacket::new(buf)?; - net_packet.set_default_version(); - net_packet.set_gateway_flag(true); - net_packet.set_destination(GATEWAY_IP); - net_packet.set_source(SELF_IP); - net_packet.set_protocol(Protocol::Service); - net_packet.set_transport_protocol(service_packet::Protocol::HandshakeRequest.into()); - net_packet.first_set_ttl(MAX_TTL); - net_packet.set_payload(&bytes)?; - Ok(net_packet) - } -} - -/// 第二次加密握手 -#[cfg(feature = "server_encrypt")] -pub fn secret_handshake_request_packet( - rsa_cipher: &RsaCipher, - token: String, - key: &[u8], -) -> io::Result>> { - let mut request = SecretHandshakeRequest::new(); - request.token = token; - request.key = key.to_vec(); - let bytes = request.write_to_bytes().map_err(|e| { - io::Error::new( - io::ErrorKind::Other, - format!("secret_handshake_request_packet {:?}", e), - ) - })?; - let mut net_packet = NetPacket::new0( - 12 + bytes.len(), - vec![0u8; 12 + bytes.len() + RSA_ENCRYPTION_RESERVED], - )?; - net_packet.set_default_version(); - net_packet.set_gateway_flag(true); - net_packet.set_destination(GATEWAY_IP); - net_packet.set_source(SELF_IP); - net_packet.set_protocol(Protocol::Service); - net_packet.set_transport_protocol(service_packet::Protocol::SecretHandshakeRequest.into()); - net_packet.first_set_ttl(MAX_TTL); - net_packet.set_payload(&bytes)?; - Ok(rsa_cipher.encrypt(&mut net_packet)?) -} diff --git a/vnt/src/handle/maintain/addr_request.rs b/vnt/src/handle/maintain/addr_request.rs deleted file mode 100644 index fff00e8..0000000 --- a/vnt/src/handle/maintain/addr_request.rs +++ /dev/null @@ -1,71 +0,0 @@ -use std::sync::Arc; -use std::time::Duration; - -use crossbeam_utils::atomic::AtomicCell; - -use crate::channel::context::ChannelContext; -use crate::channel::punch::NatType; -use crate::handle::{BaseConfigInfo, CurrentDeviceInfo}; -use crate::nat::NatTest; -use crate::util::Scheduler; - -pub fn addr_request( - scheduler: &Scheduler, - context: ChannelContext, - current_device_info: Arc>, - nat_test: NatTest, - _config: BaseConfigInfo, -) { - pub_address_request(scheduler, context, current_device_info.clone(), nat_test, 0); -} - -fn pub_address_request( - scheduler: &Scheduler, - context: ChannelContext, - current_device_info: Arc>, - nat_test: NatTest, - count: usize, -) { - let channel_num = context.channel_num(); - let index = count % channel_num; - if let Err(e) = addr_request0(&context, ¤t_device_info, &nat_test, index) { - log::warn!("{:?}", e); - } - let nat_info = nat_test.nat_info(); - let time = if !nat_info.public_ports.contains(&0) && !nat_info.public_ips.is_empty() { - //对称网络探测端口没啥作用,把频率放低,(锥形网络也只在打洞前需要探测端口,后续可以改改) - if nat_info.nat_type == NatType::Symmetric { - 600 - } else { - if index == channel_num - 1 { - 19 - } else { - 9 - } - } - } else { - 3 - }; - - let rs = scheduler.timeout(Duration::from_secs(time), move |s| { - pub_address_request(s, context, current_device_info, nat_test, index + 1) - }); - if !rs { - log::info!("定时任务停止"); - } -} - -fn addr_request0( - context: &ChannelContext, - current_device: &AtomicCell, - nat_test: &NatTest, - index: usize, -) -> anyhow::Result<()> { - let current_dev = current_device.load(); - if current_dev.status.offline() { - return Ok(()); - } - let (data, addr) = nat_test.send_data()?; - context.send_main_udp(index, &data, addr)?; - Ok(()) -} diff --git a/vnt/src/handle/maintain/heartbeat.rs b/vnt/src/handle/maintain/heartbeat.rs deleted file mode 100644 index 5327a11..0000000 --- a/vnt/src/handle/maintain/heartbeat.rs +++ /dev/null @@ -1,260 +0,0 @@ -use std::collections::HashMap; -use std::net::Ipv4Addr; -use std::sync::Arc; -use std::time::Duration; - -use crossbeam_utils::atomic::AtomicCell; -use parking_lot::Mutex; -use rand::prelude::SliceRandom; - -use crate::channel::context::ChannelContext; -use crate::cipher::Cipher; -use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo}; -use crate::protocol::body::ENCRYPTION_RESERVED; -use crate::protocol::control_packet::PingPacket; -use crate::protocol::{control_packet, NetPacket, Protocol}; -use crate::util::Scheduler; - -/// 定时发送心跳包 -pub fn heartbeat( - scheduler: &Scheduler, - context: ChannelContext, - current_device_info: Arc>, - device_map: Arc)>>, - client_cipher: Cipher, - server_cipher: Cipher, -) { - heartbeat0( - &context, - ¤t_device_info.load(), - &device_map, - &client_cipher, - &server_cipher, - ); - // 心跳包 3秒发送一次 - let rs = scheduler.timeout(Duration::from_secs(3), |s| { - heartbeat( - s, - context, - current_device_info, - device_map, - client_cipher, - server_cipher, - ) - }); - if !rs { - log::info!("定时任务停止"); - } -} - -fn heartbeat0( - context: &ChannelContext, - current_device: &CurrentDeviceInfo, - device_map: &Mutex<(u16, HashMap)>, - client_cipher: &Cipher, - server_cipher: &Cipher, -) { - 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) { - Ok(net_packet) => { - if let Err(e) = context.send_default(&net_packet, current_device.connect_server) { - log::warn!("heartbeat err={:?}", e) - } else { - is_send_gateway = true - } - } - Err(e) => { - log::error!("heartbeat_packet err={:?}", e); - } - } - - for (dest_ip, routes) in context.route_table.route_table() { - let net_packet = if current_device.is_gateway(&dest_ip) { - if is_send_gateway { - continue; - } - heartbeat_packet_server(device_map, server_cipher, src_ip, gateway_ip) - } else { - heartbeat_packet_client(client_cipher, src_ip, dest_ip) - }; - let net_packet = match net_packet { - Ok(net_packet) => net_packet, - Err(e) => { - log::error!("heartbeat_packet err={:?}", e); - continue; - } - }; - for (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) - } - } - } - let peer_list = { device_map.lock().1.clone() }; - for peer in peer_list.values() { - if !peer.status.is_online() || peer.wireguard { - continue; - } - if current_device.is_gateway(&peer.virtual_ip) { - continue; - } - if current_device.status.offline() { - continue; - } - if context.route_table.route_one(&peer.virtual_ip).is_none() { - //路由为空,则向服务端地址发送 - let net_packet = match heartbeat_packet_client(client_cipher, src_ip, peer.virtual_ip) { - Ok(net_packet) => net_packet, - Err(e) => { - log::error!("heartbeat_packet err={:?}", e); - continue; - } - }; - if let Err(e) = context.send_default(&net_packet, current_device.connect_server) { - log::error!("heartbeat_packet send_default err={:?}", e); - } - } - } -} - -/// 客户端中继路径探测,延迟启动 -pub fn client_relay( - scheduler: &Scheduler, - context: ChannelContext, - current_device: Arc>, - device_map: Arc)>>, - client_cipher: Cipher, -) { - let rs = scheduler.timeout(Duration::from_secs(30), move |s| { - client_relay_(s, context, current_device, device_map, client_cipher) - }); - if !rs { - log::info!("定时任务停止"); - } -} - -/// 客户端中继路径探测,每30秒探测一次 -fn client_relay_( - scheduler: &Scheduler, - context: ChannelContext, - current_device: Arc>, - device_map: Arc)>>, - client_cipher: Cipher, -) { - if let Err(e) = client_relay0( - &context, - ¤t_device.load(), - &device_map, - &client_cipher, - ) { - log::error!("{:?}", e); - } - let rs = scheduler.timeout(Duration::from_secs(30), move |s| { - client_relay_(s, context, current_device, device_map, client_cipher) - }); - if !rs { - log::info!("定时任务停止"); - } -} - -fn client_relay0( - context: &ChannelContext, - current_device: &CurrentDeviceInfo, - device_map: &Mutex<(u16, HashMap)>, - client_cipher: &Cipher, -) -> anyhow::Result<()> { - // 离线了不再探测 - if current_device.status.offline() { - return Ok(()); - } - let peer_list = { device_map.lock().1.clone() }; - let mut routes = context.route_table.route_table_p2p(); - for peer in peer_list.values() { - if peer.wireguard - || !peer.status.is_online() - || peer.virtual_ip == current_device.virtual_ip - { - continue; - } - if context - .route_table - .route_one_p2p(&peer.virtual_ip) - .is_some() - && !context.first_latency() - { - continue; - } - let client_packet = - heartbeat_packet_client(client_cipher, current_device.virtual_ip, peer.virtual_ip)?; - - //随机发送到其他地址,看有没有客户端符合转发条件 - routes.shuffle(&mut rand::thread_rng()); - - for (index, (ip, route)) in routes.iter().enumerate() { - if current_device.is_gateway(ip) { - continue; - } - if let Err(e) = context.send_by_key(&client_packet, route.route_key()) { - log::error!("{:?}", e); - } - if index >= 2 { - break; - } - } - } - Ok(()) -} - -/// 构建心跳包 -fn heartbeat_packet( - src: Ipv4Addr, - dest: Ipv4Addr, -) -> anyhow::Result> { - let mut net_packet = NetPacket::new_encrypt([0u8; 12 + 4 + ENCRYPTION_RESERVED])?; - net_packet.set_default_version(); - net_packet.set_protocol(Protocol::Control); - net_packet.set_transport_protocol(control_packet::Protocol::Ping.into()); - net_packet.first_set_ttl(5); - net_packet.set_source(src); - net_packet.set_destination(dest); - let mut ping = PingPacket::new(net_packet.payload_mut())?; - ping.set_time(crate::handle::now_time() as u16); - Ok(net_packet) -} - -fn heartbeat_packet_client( - client_cipher: &Cipher, - src: Ipv4Addr, - dest: Ipv4Addr, -) -> anyhow::Result> { - let mut net_packet = heartbeat_packet(src, dest)?; - client_cipher.encrypt_ipv4(&mut net_packet)?; - Ok(net_packet) -} - -fn heartbeat_packet_server( - device_map: &Mutex<(u16, HashMap)>, - server_cipher: &Cipher, - src: Ipv4Addr, - dest: Ipv4Addr, -) -> anyhow::Result> { - let mut net_packet = heartbeat_packet(src, dest)?; - let mut ping = PingPacket::new(net_packet.payload_mut())?; - ping.set_epoch(device_map.lock().0); - net_packet.set_gateway_flag(true); - server_cipher.encrypt_ipv4(&mut net_packet)?; - Ok(net_packet) -} diff --git a/vnt/src/handle/maintain/idle.rs b/vnt/src/handle/maintain/idle.rs deleted file mode 100644 index 0e4edcd..0000000 --- a/vnt/src/handle/maintain/idle.rs +++ /dev/null @@ -1,210 +0,0 @@ -use std::io; -use std::sync::Arc; -use std::time::Duration; - -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; -use crate::handle::{BaseConfigInfo, ConnectStatus, CurrentDeviceInfo}; -use crate::util::{address_choose, dns_query_all, Scheduler}; -use crate::{ErrorInfo, VntCallback}; - -pub fn idle_route( - scheduler: &Scheduler, - idle: Idle, - context: ChannelContext, - current_device_info: Arc>, - call: Call, -) { - let delay = idle_route0(&idle, &context, ¤t_device_info, &call); - let rs = scheduler.timeout(delay, move |s| { - idle_route(s, idle, context, current_device_info, call) - }); - if !rs { - log::info!("定时任务停止"); - } -} - -pub fn idle_gateway( - scheduler: &Scheduler, - context: ChannelContext, - current_device_info: Arc>, - config: BaseConfigInfo, - connect_util: ConnectUtil, - call: Call, - mut connect_count: usize, - handshake: Handshake, -) { - idle_gateway0( - &context, - ¤t_device_info, - &config, - &connect_util, - &call, - &mut connect_count, - &handshake, - ); - let rs = scheduler.timeout(Duration::from_secs(8), move |s| { - idle_gateway( - s, - context, - current_device_info, - config, - connect_util, - call, - connect_count, - handshake, - ) - }); - if !rs { - log::info!("定时任务停止"); - } -} - -fn idle_gateway0( - context: &ChannelContext, - current_device: &AtomicCell, - config: &BaseConfigInfo, - connect_util: &ConnectUtil, - call: &Call, - connect_count: &mut usize, - handshake: &Handshake, -) { - if let Err(e) = check_gateway_channel( - context, - current_device, - config, - connect_util, - call, - connect_count, - handshake, - ) { - let cur = current_device.load(); - call.error(ErrorInfo::new_msg( - ErrorType::Disconnect, - format!("connect:{},error:{:?}", cur.connect_server, e), - )); - } -} - -fn idle_route0( - idle: &Idle, - context: &ChannelContext, - current_device: &AtomicCell, - call: &Call, -) -> Duration { - let cur = current_device.load(); - match idle.next_idle() { - IdleType::Timeout(ip, route) => { - log::info!("route Timeout {:?},{:?}", ip, route); - context.remove_route(&ip, route.route_key()); - if cur.is_gateway(&ip) { - //网关路由过期,则需要改变状态 - crate::handle::change_status(current_device, ConnectStatus::Connecting); - call.error(ErrorInfo::new(ErrorType::Disconnect)); - } - Duration::from_millis(100) - } - IdleType::Sleep(duration) => duration, - IdleType::None => Duration::from_millis(3000), - } -} - -fn check_gateway_channel( - context: &ChannelContext, - current_device_info: &AtomicCell, - config: &BaseConfigInfo, - connect_util: &ConnectUtil, - call: &Call, - count: &mut usize, - handshake: &Handshake, -) -> io::Result<()> { - let mut current_device = current_device_info.load(); - if current_device.status.offline() { - *count += 1; - let connect_protocol = context.main_protocol(); - if connect_protocol.is_transport() { - // 传输层的协议需要探测服务器地址 - current_device = - domain_request0(current_device_info, config, context.default_interface()); - } - //需要重连 - call.connect(ConnectInfo::new(*count, current_device.connect_server)); - log::info!("发送握手请求,{:?}", config); - if let Err(e) = handshake.send(context, config.server_secret, current_device.connect_server) - { - log::warn!("{:?}", e); - let request_packet = handshake.handshake_request_packet(config.server_secret)?; - match connect_protocol { - ConnectProtocol::UDP => {} - ConnectProtocol::TCP => { - connect_util.try_connect_tcp( - request_packet.into_buffer(), - current_device.connect_server, - ); - } - ConnectProtocol::WS | ConnectProtocol::WSS => { - connect_util - .try_connect_ws(request_packet.into_buffer(), config.server_addr.clone()); - } - } - } - } - Ok(()) -} - -pub fn domain_request0( - current_device: &AtomicCell, - config: &BaseConfigInfo, - default_interface: &LocalInterface, -) -> CurrentDeviceInfo { - let mut current_dev = current_device.load(); - - // 探测服务端地址变化 - match dns_query_all( - &config.server_addr, - config.name_servers.clone(), - default_interface, - ) { - Ok(addrs) => { - log::info!( - "domain {} dns {:?} addr {:?}", - config.server_addr, - config.name_servers, - addrs - ); - - match address_choose(addrs) { - Ok(addr) => { - if addr != current_dev.connect_server { - let mut tmp = current_dev.clone(); - tmp.connect_server = addr; - let rs = current_device.compare_exchange(current_dev, tmp); - log::info!( - "服务端地址变化,旧地址:{},新地址:{},替换结果:{}", - current_dev.connect_server, - addr, - rs.is_ok() - ); - if rs.is_ok() { - current_dev.connect_server = addr; - } - } - } - Err(e) => { - log::error!("域名地址选择失败:{:?},domain={}", e, config.server_addr); - } - } - } - Err(e) => { - log::error!("域名解析失败:{:?},domain={}", e, config.server_addr); - } - } - current_dev -} diff --git a/vnt/src/handle/maintain/mod.rs b/vnt/src/handle/maintain/mod.rs deleted file mode 100644 index 5e9dfe0..0000000 --- a/vnt/src/handle/maintain/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -mod heartbeat; -pub use heartbeat::client_relay; -pub use heartbeat::heartbeat; - -mod re_nat_type; -pub use re_nat_type::retrieve_nat_type; - -mod addr_request; -pub use addr_request::*; - -mod punch; -pub use punch::*; - -mod idle; -pub use idle::idle_gateway; -pub use idle::idle_route; - -mod up_status; -pub use up_status::*; diff --git a/vnt/src/handle/maintain/punch.rs b/vnt/src/handle/maintain/punch.rs deleted file mode 100644 index cef4039..0000000 --- a/vnt/src/handle/maintain/punch.rs +++ /dev/null @@ -1,354 +0,0 @@ -use std::collections::HashMap; -use std::net::Ipv4Addr; -use std::ops::{Div, Mul}; -use std::sync::mpsc::{sync_channel, Receiver, SyncSender}; -use std::sync::Arc; -use std::thread; -use std::time::Duration; - -use anyhow::anyhow; -use crossbeam_utils::atomic::AtomicCell; -use parking_lot::Mutex; -use protobuf::Message; -use rand::prelude::SliceRandom; - -use crate::channel::context::ChannelContext; -use crate::channel::punch::{NatInfo, NatType, Punch}; -use crate::cipher::Cipher; -use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo}; -use crate::nat::NatTest; -use crate::proto::message::{PunchInfo, PunchNatType}; -use crate::protocol::body::ENCRYPTION_RESERVED; -use crate::protocol::{control_packet, other_turn_packet, NetPacket, Protocol, MAX_TTL}; -use crate::util::Scheduler; - -#[derive(Clone)] -pub struct PunchSender { - sender_self: SyncSender<(Ipv4Addr, NatInfo)>, - sender_peer: SyncSender<(Ipv4Addr, NatInfo)>, - sender_cone_self: SyncSender<(Ipv4Addr, NatInfo)>, - sender_cone_peer: SyncSender<(Ipv4Addr, NatInfo)>, -} - -impl PunchSender { - pub fn send(&self, src_peer: bool, ip: Ipv4Addr, info: NatInfo) -> bool { - log::info!( - "发送打洞协商消息,是否对端发起:{},ip:{},info:{:?}", - src_peer, - ip, - info - ); - let sender = match info.nat_type { - NatType::Symmetric => { - if src_peer { - &self.sender_peer - } else { - &self.sender_self - } - } - NatType::Cone => { - if src_peer { - &self.sender_cone_peer - } else { - &self.sender_cone_self - } - } - }; - sender.try_send((ip, info)).is_ok() - } -} - -pub struct PunchReceiver { - receiver_peer: Receiver<(Ipv4Addr, NatInfo)>, - receiver_self: Receiver<(Ipv4Addr, NatInfo)>, - receiver_cone_peer: Receiver<(Ipv4Addr, NatInfo)>, - receiver_cone_self: Receiver<(Ipv4Addr, NatInfo)>, -} - -pub fn punch_channel() -> (PunchSender, PunchReceiver) { - let (sender_self, receiver_self) = sync_channel(0); - let (sender_peer, receiver_peer) = sync_channel(0); - let (sender_cone_peer, receiver_cone_peer) = sync_channel(0); - let (sender_cone_self, receiver_cone_self) = sync_channel(0); - ( - PunchSender { - sender_self, - sender_peer, - sender_cone_peer, - sender_cone_self, - }, - PunchReceiver { - receiver_peer, - receiver_self, - receiver_cone_peer, - receiver_cone_self, - }, - ) -} - -pub fn punch( - scheduler: &Scheduler, - context: ChannelContext, - nat_test: NatTest, - device_map: Arc)>>, - current_device: Arc>, - client_cipher: Cipher, - receiver: PunchReceiver, - punch: Punch, -) { - let punch_record = Arc::new(Mutex::new(HashMap::new())); - let last_punch_record = HashMap::new(); - punch_request( - scheduler, - context, - nat_test, - device_map, - current_device.clone(), - client_cipher.clone(), - 0, - punch_record.clone(), - last_punch_record, - ); - let f = |receiver: Receiver<(Ipv4Addr, NatInfo)>| { - let punch = punch.clone(); - let current_device = current_device.clone(); - let client_cipher = client_cipher.clone(); - let punch_record = punch_record.clone(); - thread::Builder::new() - .name("punch".into()) - .spawn(move || { - punch_start(receiver, punch, current_device, client_cipher, punch_record); - }) - .expect("punch"); - }; - f(receiver.receiver_peer); - f(receiver.receiver_self); - f(receiver.receiver_cone_peer); - f(receiver.receiver_cone_self); -} - -/// 接收打洞消息,配合对端打洞 -fn punch_start( - receiver: Receiver<(Ipv4Addr, NatInfo)>, - mut punch: Punch, - current_device: Arc>, - client_cipher: Cipher, - punch_record: Arc>>, -) { - while let Ok((peer_ip, nat_info)) = receiver.recv() { - let mut packet = NetPacket::new_encrypt([0u8; 12 + ENCRYPTION_RESERVED]).unwrap(); - packet.set_default_version(); - packet.first_set_ttl(1); - packet.set_protocol(Protocol::Control); - packet.set_transport_protocol(control_packet::Protocol::PunchRequest.into()); - packet.set_source(current_device.load().virtual_ip()); - packet.set_destination(peer_ip); - let count = { - let mut guard = punch_record.lock(); - if let Some(v) = guard.get_mut(&peer_ip) { - *v += 1; - *v - } else { - guard.insert(peer_ip, 0); - 0 - } - }; - log::info!("第{}次发起打洞,目标:{:?},{:?} ", count, peer_ip, nat_info); - - if let Err(e) = client_cipher.encrypt_ipv4(&mut packet) { - log::error!("{:?}", e); - continue; - } - if let Err(e) = punch.punch(packet.buffer(), peer_ip, nat_info, count < 2, count) { - log::warn!("{:?}", e) - } - } -} - -/// 定时发起打洞请求 -fn punch_request( - scheduler: &Scheduler, - context: ChannelContext, - nat_test: NatTest, - device_map: Arc)>>, - current_device: Arc>, - client_cipher: Cipher, - count: usize, - punch_record: Arc>>, - mut last_punch_record: HashMap, -) { - let curr = current_device.load(); - let secs = if curr.status.online() { - if let Err(e) = punch0( - &context, - &nat_test, - &device_map, - curr, - &client_cipher, - &punch_record, - &mut last_punch_record, - count, - ) { - log::warn!("{:?}", e) - } - let sleep_time = [6, 7]; - Duration::from_secs(sleep_time[count % sleep_time.len()]) - } else { - Duration::from_secs(5) - }; - let rs = scheduler.timeout(secs, move |s| { - punch_request( - s, - context, - nat_test, - device_map, - current_device, - client_cipher, - count + 1, - punch_record, - last_punch_record, - ); - }); - if !rs { - log::info!("定时任务停止"); - } -} -#[derive(Copy, Clone, Default)] -struct PunchRecordItem { - pub punch_record: usize, - pub last_p2p_num: usize, -} - -/// 随机对需要打洞的客户端发起打洞请求 -fn punch0( - context: &ChannelContext, - nat_test: &NatTest, - device_map: &Arc)>>, - current_device: CurrentDeviceInfo, - client_cipher: &Cipher, - punch_record: &Mutex>, - last_punch_record: &mut HashMap, - total_count: usize, -) -> anyhow::Result<()> { - let nat_info = nat_test.nat_info(); - if total_count < 10 - && (nat_info.public_ips.is_empty() - || nat_info.public_ports.is_empty() - || nat_info.public_ports[0] == 0 - || nat_info.public_ports.iter().filter(|&&v| v == 0).count() - > nat_info.public_ports.len() / 2) - { - log::info!("未获取到公网地址,暂时放弃打洞,第{}轮", total_count); - return Ok(()); - } - let current_ip = current_device.virtual_ip; - let mut list: Vec = device_map - .lock() - .1 - .values() - .filter(|info| !info.wireguard && info.virtual_ip > current_ip) - .cloned() - .collect(); - list.shuffle(&mut rand::thread_rng()); - for info in list { - 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() - .unwrap_or(0) - .mul(2) - .div(3); - let p2p_num = context.route_table.p2p_num(&info.virtual_ip); - let mut max_punch_interval = 50; - if p2p_num > 0 { - if p2p_num >= context.channel_num() { - //通道数满足要求,不再打洞 - if punch_count != 0 { - punch_record.lock().remove(&info.virtual_ip); - } - continue; - } - //有p2p通道,但是通道数量不够,则继续打洞 - // 提高等待上限 - max_punch_interval = 300; - } - // 能发起打洞的前提是自己空闲,这里会间隔5秒以上发起一次打洞,所以假定上一轮打洞已结束 - 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_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(), - &nat_info, - info.virtual_ip, - )?; - log::info!( - "目标:{:?},当前nat:{:?} 第{}次发起打洞协商请求, 第:{}轮", - info.virtual_ip, - nat_info, - punch_count, - total_count, - ); - context.send_default(&packet, current_device.connect_server)?; - break; - } - } - Ok(()) -} - -fn punch_packet( - client_cipher: &Cipher, - virtual_ip: Ipv4Addr, - nat_info: &NatInfo, - dest: Ipv4Addr, -) -> anyhow::Result>> { - let mut punch_reply = PunchInfo::new(); - punch_reply.reply = false; - punch_reply.public_ip_list = nat_info - .public_ips - .iter() - .map(|ip| u32::from_be_bytes(ip.octets())) - .collect(); - punch_reply.public_port = nat_info.public_ports.get(0).map_or(0, |v| *v as u32); - punch_reply.public_ports = nat_info.public_ports.iter().map(|e| *e as u32).collect(); - punch_reply.public_port_range = nat_info.public_port_range as u32; - punch_reply.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; - punch_reply.udp_ports = nat_info.udp_ports.iter().map(|e| *e as u32).collect(); - if let Some(ipv6) = nat_info.ipv6 { - punch_reply.ipv6_port = nat_info.udp_ports[0] as u32; - punch_reply.ipv6 = ipv6.octets().to_vec(); - } - punch_reply.nat_type = protobuf::EnumOrUnknown::new(PunchNatType::from(nat_info.nat_type)); - punch_reply.punch_model = protobuf::EnumOrUnknown::new(nat_info.punch_model.into()); - log::info!("请求打洞={:?}", punch_reply); - let bytes = punch_reply - .write_to_bytes() - .map_err(|e| anyhow!("punch_packet {:?}", e))?; - let mut net_packet = NetPacket::new_encrypt(vec![0u8; 12 + bytes.len() + ENCRYPTION_RESERVED])?; - net_packet.set_default_version(); - net_packet.set_protocol(Protocol::OtherTurn); - net_packet.set_transport_protocol(other_turn_packet::Protocol::Punch.into()); - net_packet.first_set_ttl(MAX_TTL); - net_packet.set_source(virtual_ip); - net_packet.set_destination(dest); - net_packet.set_payload(&bytes)?; - client_cipher.encrypt_ipv4(&mut net_packet)?; - Ok(net_packet) -} diff --git a/vnt/src/handle/maintain/re_nat_type.rs b/vnt/src/handle/maintain/re_nat_type.rs deleted file mode 100644 index ca6d29f..0000000 --- a/vnt/src/handle/maintain/re_nat_type.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::thread; -use std::time::Duration; - -use crate::channel::context::ChannelContext; -use crate::channel::sender::AcceptSocketSender; -use crate::nat; -use crate::nat::NatTest; -use crate::util::Scheduler; - -/// 10分钟探测一次nat -pub fn retrieve_nat_type( - scheduler: &Scheduler, - context: ChannelContext, - nat_test: NatTest, - udp_socket_sender: AcceptSocketSender>>, -) { - retrieve_nat_type0(context.clone(), nat_test.clone(), udp_socket_sender.clone()); - scheduler.timeout(Duration::from_secs(60 * 10), move |s| { - retrieve_nat_type(s, context, nat_test, udp_socket_sender) - }); -} - -fn retrieve_nat_type0( - context: ChannelContext, - nat_test: NatTest, - udp_socket_sender: AcceptSocketSender>>, -) { - thread::Builder::new() - .name("natTest".into()) - .spawn(move || { - if nat_test.can_update() { - 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, context.default_interface()) { - Ok(nat_info) => { - log::info!("当前nat信息:{:?}", nat_info); - if let Err(e) = context.switch(nat_info.nat_type, &udp_socket_sender) { - log::warn!("{:?}", e); - } - } - Err(e) => { - log::warn!("nat re_test {:?}", e); - } - }; - #[cfg(feature = "upnp")] - nat_test.reset_upnp(); - log::info!("刷新nat结束") - } - }) - .expect("natTest"); -} diff --git a/vnt/src/handle/maintain/up_status.rs b/vnt/src/handle/maintain/up_status.rs deleted file mode 100644 index 25b51e7..0000000 --- a/vnt/src/handle/maintain/up_status.rs +++ /dev/null @@ -1,81 +0,0 @@ -use crate::channel::context::ChannelContext; -use crate::handle::CurrentDeviceInfo; -use crate::proto::message::{ClientStatusInfo, PunchNatType, RouteItem}; -use crate::protocol::body::ENCRYPTION_RESERVED; -use crate::protocol::{service_packet, NetPacket, Protocol, HEAD_LEN, MAX_TTL}; -use crate::util::Scheduler; -use crossbeam_utils::atomic::AtomicCell; -use protobuf::Message; -use std::io; -use std::sync::Arc; -use std::time::Duration; - -/// 上报状态给服务器 -pub fn up_status( - scheduler: &Scheduler, - context: ChannelContext, - current_device_info: Arc>, -) { - let _ = scheduler.timeout(Duration::from_secs(60), move |x| { - up_status0(x, context, current_device_info) - }); -} - -fn up_status0( - scheduler: &Scheduler, - context: ChannelContext, - current_device_info: Arc>, -) { - if let Err(e) = send_up_status_packet(&context, ¤t_device_info) { - log::warn!("{:?}", e) - } - let rs = scheduler.timeout(Duration::from_secs(10 * 60), move |x| { - up_status0(x, context, current_device_info) - }); - if !rs { - log::info!("定时任务停止"); - } -} - -fn send_up_status_packet( - context: &ChannelContext, - current_device_info: &AtomicCell, -) -> io::Result<()> { - let device_info = current_device_info.load(); - if device_info.status.offline() { - return Ok(()); - } - let routes = context.route_table.route_table_p2p(); - if routes.is_empty() { - return Ok(()); - } - let mut message = ClientStatusInfo::new(); - message.source = device_info.virtual_ip.into(); - for (ip, _) in routes { - let mut item = RouteItem::new(); - item.next_ip = ip.into(); - message.p2p_list.push(item); - } - message.up_stream = context.up_traffic_meter.as_ref().map_or(0, |v| v.total()); - message.down_stream = context.down_traffic_meter.as_ref().map_or(0, |v| v.total()); - message.nat_type = protobuf::EnumOrUnknown::new(if context.is_cone() { - PunchNatType::Cone - } else { - PunchNatType::Symmetric - }); - let buf = message - .write_to_bytes() - .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("up_status_packet {:?}", e)))?; - let mut net_packet = - NetPacket::new_encrypt(vec![0; HEAD_LEN + buf.len() + ENCRYPTION_RESERVED])?; - net_packet.set_default_version(); - net_packet.set_gateway_flag(true); - net_packet.set_protocol(Protocol::Service); - net_packet.set_transport_protocol_into(service_packet::Protocol::ClientStatusInfo); - net_packet.first_set_ttl(MAX_TTL); - net_packet.set_source(device_info.virtual_ip); - net_packet.set_destination(device_info.virtual_gateway); - net_packet.set_payload(&buf)?; - context.send_default(&net_packet, device_info.connect_server)?; - Ok(()) -} diff --git a/vnt/src/handle/mod.rs b/vnt/src/handle/mod.rs deleted file mode 100644 index 2fec003..0000000 --- a/vnt/src/handle/mod.rs +++ /dev/null @@ -1,276 +0,0 @@ -use crate::channel::socket::LocalInterface; -use crossbeam_utils::atomic::AtomicCell; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - -pub mod callback; -mod extension; -pub mod handshaker; -pub mod maintain; -pub mod recv_data; -pub mod registrar; -#[cfg(feature = "integrated_tun")] -pub mod tun_tap; - -const SELF_IP: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 2); -const GATEWAY_IP: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 1); - -pub fn now_time() -> u64 { - let now = std::time::SystemTime::now(); - if let Ok(timestamp) = now.duration_since(std::time::UNIX_EPOCH) { - timestamp.as_secs() * 1000 + u64::from(timestamp.subsec_millis()) - } else { - 0 - } -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct PeerDeviceInfo { - pub virtual_ip: Ipv4Addr, - pub name: String, - pub status: PeerDeviceStatus, - pub client_secret: bool, - pub client_secret_hash: Vec, - pub wireguard: bool, -} - -impl PeerDeviceInfo { - pub fn new( - virtual_ip: Ipv4Addr, - name: String, - status: u8, - client_secret: bool, - client_secret_hash: Vec, - wireguard: bool, - ) -> Self { - Self { - virtual_ip, - name, - status: PeerDeviceStatus::from(status), - client_secret, - client_secret_hash, - wireguard, - } - } -} - -#[derive(Clone, Debug)] -pub struct BaseConfigInfo { - pub name: String, - pub token: String, - pub ip: Option, - pub client_secret_hash: Option<[u8; 16]>, - pub server_secret: bool, - pub device_id: String, - pub server_addr: String, - pub name_servers: Vec, - pub mtu: u32, - #[cfg(feature = "integrated_tun")] - #[cfg(target_os = "windows")] - pub tap: bool, - #[cfg(feature = "integrated_tun")] - #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] - pub device_name: Option, - pub allow_wire_guard: bool, - pub default_interface: LocalInterface, -} - -impl BaseConfigInfo { - pub fn new( - name: String, - token: String, - ip: Option, - client_secret_hash: Option<[u8; 16]>, - server_secret: bool, - device_id: String, - server_addr: String, - name_servers: Vec, - mtu: u32, - #[cfg(feature = "integrated_tun")] - #[cfg(target_os = "windows")] - tap: bool, - #[cfg(feature = "integrated_tun")] - #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] - device_name: Option, - allow_wire_guard: bool, - default_interface: LocalInterface, - ) -> Self { - Self { - name, - token, - ip, - client_secret_hash, - server_secret, - device_id, - server_addr, - name_servers, - mtu, - #[cfg(feature = "integrated_tun")] - #[cfg(target_os = "windows")] - tap, - #[cfg(feature = "integrated_tun")] - #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] - device_name, - allow_wire_guard, - default_interface, - } - } -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] -pub enum PeerDeviceStatus { - Online, - Offline, -} - -impl PeerDeviceStatus { - pub fn is_online(&self) -> bool { - self == &PeerDeviceStatus::Online - } - pub fn is_offline(&self) -> bool { - self == &PeerDeviceStatus::Offline - } -} - -impl Into for PeerDeviceStatus { - fn into(self) -> u8 { - match self { - PeerDeviceStatus::Online => 0, - PeerDeviceStatus::Offline => 1, - } - } -} - -impl From for PeerDeviceStatus { - fn from(value: u8) -> Self { - match value { - 0 => PeerDeviceStatus::Online, - _ => PeerDeviceStatus::Offline, - } - } -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum ConnectStatus { - Connecting, - Connected, -} - -impl ConnectStatus { - pub fn online(&self) -> bool { - self == &ConnectStatus::Connected - } - pub fn offline(&self) -> bool { - self == &ConnectStatus::Connecting - } -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct CurrentDeviceInfo { - //本机虚拟IP - pub virtual_ip: Ipv4Addr, - //子网掩码 - pub virtual_netmask: Ipv4Addr, - //虚拟网关 - pub virtual_gateway: Ipv4Addr, - //网络地址 - pub virtual_network: Ipv4Addr, - //直接广播地址 - pub broadcast_ip: Ipv4Addr, - //链接的服务器地址 - pub connect_server: SocketAddr, - //连接状态 - pub status: ConnectStatus, -} - -impl CurrentDeviceInfo { - pub fn new( - virtual_ip: Ipv4Addr, - virtual_netmask: Ipv4Addr, - virtual_gateway: Ipv4Addr, - connect_server: SocketAddr, - ) -> Self { - let broadcast_ip = (!u32::from_be_bytes(virtual_netmask.octets())) - | u32::from_be_bytes(virtual_gateway.octets()); - let broadcast_ip = Ipv4Addr::from(broadcast_ip); - let virtual_network = u32::from_be_bytes(virtual_netmask.octets()) - & u32::from_be_bytes(virtual_gateway.octets()); - let virtual_network = Ipv4Addr::from(virtual_network); - Self { - virtual_ip, - virtual_netmask, - virtual_gateway, - virtual_network, - broadcast_ip, - connect_server, - status: ConnectStatus::Connecting, - } - } - pub fn new0(connect_server: SocketAddr) -> Self { - Self { - virtual_ip: Ipv4Addr::UNSPECIFIED, - virtual_gateway: Ipv4Addr::UNSPECIFIED, - virtual_netmask: Ipv4Addr::UNSPECIFIED, - virtual_network: Ipv4Addr::UNSPECIFIED, - broadcast_ip: Ipv4Addr::UNSPECIFIED, - connect_server, - status: ConnectStatus::Connecting, - } - } - pub fn update( - &mut self, - virtual_ip: Ipv4Addr, - virtual_netmask: Ipv4Addr, - virtual_gateway: Ipv4Addr, - ) { - let broadcast_ip = (!u32::from_be_bytes(virtual_netmask.octets())) - | u32::from_be_bytes(virtual_ip.octets()); - let broadcast_ip = Ipv4Addr::from(broadcast_ip); - 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; - self.virtual_gateway = virtual_gateway; - self.broadcast_ip = broadcast_ip; - self.virtual_network = virtual_network; - } - #[inline] - pub fn virtual_ip(&self) -> Ipv4Addr { - self.virtual_ip - } - #[inline] - pub fn virtual_gateway(&self) -> Ipv4Addr { - self.virtual_gateway - } - #[inline] - pub fn is_gateway(&self, ip: &Ipv4Addr) -> bool { - &self.virtual_gateway == ip || ip == &GATEWAY_IP - } - #[inline] - pub fn not_in_network(&self, ip: Ipv4Addr) -> bool { - u32::from(ip) & u32::from(self.virtual_netmask) != u32::from(self.virtual_network) - } - pub fn is_server_addr(&self, addr: SocketAddr) -> bool { - if self.connect_server == addr { - return true; - } - let f = |ip: IpAddr| match ip { - IpAddr::V4(v4) => Some(v4), - IpAddr::V6(v6) => v6.to_ipv4(), - }; - addr.port() == self.connect_server.port() && f(addr.ip()) == f(self.connect_server.ip()) - } -} -pub fn change_status( - current_device: &AtomicCell, - connect_status: ConnectStatus, -) -> CurrentDeviceInfo { - loop { - let cur = current_device.load(); - let mut new_info = cur; - new_info.status = connect_status; - if current_device.compare_exchange(cur, new_info).is_ok() { - return new_info; - } - } -} diff --git a/vnt/src/handle/recv_data/client.rs b/vnt/src/handle/recv_data/client.rs deleted file mode 100644 index e658ad7..0000000 --- a/vnt/src/handle/recv_data/client.rs +++ /dev/null @@ -1,401 +0,0 @@ -use anyhow::anyhow; -use std::collections::HashMap; -use std::net::{Ipv4Addr, Ipv6Addr}; -use std::sync::Arc; - -use parking_lot::RwLock; -use protobuf::Message; - -use packet::icmp::{icmp, Kind}; -use packet::ip::ipv4; -use packet::ip::ipv4::packet::IpV4Packet; - -use crate::channel::context::ChannelContext; -use crate::channel::punch::NatInfo; -use crate::channel::{Route, RouteKey}; -use crate::cipher::Cipher; -use crate::external_route::AllowExternalRoute; -use crate::handle::extension::handle_extension_tail; -use crate::handle::maintain::PunchSender; -use crate::handle::recv_data::PacketHandler; -use crate::handle::CurrentDeviceInfo; -#[cfg(feature = "ip_proxy")] -use crate::ip_proxy::{IpProxyMap, ProxyHandler}; -use crate::nat::NatTest; -use crate::proto::message::{PunchInfo, PunchNatType}; -use crate::protocol::body::ENCRYPTION_RESERVED; -use crate::protocol::control_packet::ControlPacket; -use crate::protocol::{ - control_packet, ip_turn_packet, other_turn_packet, NetPacket, Protocol, MAX_TTL, -}; -use crate::tun_tap_device::vnt_device::DeviceWrite; - -/// 处理来源于客户端的包 -#[derive(Clone)] -pub struct ClientPacketHandler { - device: Device, - client_cipher: Cipher, - punch_sender: PunchSender, - peer_nat_info_map: Arc>>, - nat_test: NatTest, - route: AllowExternalRoute, - #[cfg(feature = "ip_proxy")] - #[cfg(feature = "integrated_tun")] - ip_proxy_map: Option, -} - -impl ClientPacketHandler { - pub fn new( - device: Device, - client_cipher: Cipher, - punch_sender: PunchSender, - peer_nat_info_map: Arc>>, - nat_test: NatTest, - route: AllowExternalRoute, - #[cfg(feature = "integrated_tun")] - #[cfg(feature = "ip_proxy")] - ip_proxy_map: Option, - ) -> Self { - Self { - device, - client_cipher, - punch_sender, - peer_nat_info_map, - nat_test, - route, - #[cfg(feature = "integrated_tun")] - #[cfg(feature = "ip_proxy")] - ip_proxy_map, - } - } -} - -impl PacketHandler for ClientPacketHandler { - fn handle( - &self, - mut net_packet: NetPacket<&mut [u8]>, - mut extend: NetPacket<&mut [u8]>, - route_key: RouteKey, - context: &ChannelContext, - current_device: &CurrentDeviceInfo, - ) -> anyhow::Result<()> { - self.client_cipher.decrypt_ipv4(&mut net_packet)?; - context - .route_table - .update_read_time(&net_packet.source(), &route_key); - //处理扩展 - let net_packet = if net_packet.is_extension() { - //这样重用数组,减少一次数据拷贝 - if handle_extension_tail(&mut net_packet, &mut extend)? { - extend - } else { - net_packet - } - } else { - net_packet - }; - match net_packet.protocol() { - Protocol::Service => {} - Protocol::Error => {} - Protocol::Control => { - self.control(context, current_device, net_packet, route_key)?; - } - Protocol::IpTurn => { - self.ip_turn(net_packet, context, current_device, route_key)?; - } - Protocol::OtherTurn => { - self.other_turn(context, current_device, net_packet, route_key)?; - } - Protocol::Unknown(_) => {} - } - Ok(()) - } -} - -impl ClientPacketHandler { - fn ip_turn( - &self, - mut net_packet: NetPacket<&mut [u8]>, - context: &ChannelContext, - current_device: &CurrentDeviceInfo, - route_key: RouteKey, - ) -> anyhow::Result<()> { - let destination = net_packet.destination(); - let source = net_packet.source(); - match ip_turn_packet::Protocol::from(net_packet.transport_protocol()) { - ip_turn_packet::Protocol::Ipv4 => { - let mut ipv4 = IpV4Packet::new(net_packet.payload_mut())?; - match ipv4.protocol() { - ipv4::protocol::Protocol::Icmp => { - if ipv4.destination_ip() == destination { - let mut icmp_packet = icmp::IcmpPacket::new(ipv4.payload_mut())?; - if icmp_packet.kind() == Kind::EchoRequest { - //开启ping - icmp_packet.set_kind(Kind::EchoReply); - icmp_packet.update_checksum(); - ipv4.set_source_ip(destination); - ipv4.set_destination_ip(source); - ipv4.update_checksum(); - net_packet.set_source(destination); - net_packet.set_destination(source); - //不管加不加密,和接收到的数据长度都一致 - self.client_cipher.encrypt_ipv4(&mut net_packet)?; - context.send_by_key(&net_packet, route_key)?; - return Ok(()); - } - } - } - _ => {} - } - // ip代理只关心实际目标 - let real_dest = ipv4.destination_ip(); - if real_dest != destination - && !(real_dest.is_broadcast() - || real_dest.is_multicast() - || real_dest == current_device.broadcast_ip - || real_dest.is_unspecified()) - { - if !self.route.allow(&real_dest) { - //拦截不符合的目标 - return Ok(()); - } - match ipv4.protocol() { - ipv4::protocol::Protocol::Tcp => { - let payload = ipv4.payload(); - if payload.len() < 20 { - return Ok(()); - } - let destination_port = - u16::from_be_bytes(payload[2..4].try_into().unwrap()); - if self.nat_test.is_local_tcp(real_dest, destination_port) { - return Ok(()); - } - } - ipv4::protocol::Protocol::Udp => { - let payload = ipv4.payload(); - if payload.len() < 8 { - return Ok(()); - } - let destination_port = - u16::from_be_bytes(payload[2..4].try_into().unwrap()); - if self.nat_test.is_local_udp(real_dest, destination_port) { - return Ok(()); - } - } - _ => {} - } - #[cfg(feature = "ip_proxy")] - #[cfg(feature = "integrated_tun")] - if let Some(ip_proxy_map) = &self.ip_proxy_map { - if ip_proxy_map.recv_handle(&mut ipv4, source, destination)? { - return Ok(()); - } - } - } - self.device.write(net_packet.payload())?; - } - ip_turn_packet::Protocol::WGIpv4 => { - // WG客户端的数据不会直接发过来,不用处理 - } - ip_turn_packet::Protocol::Ipv4Broadcast => { - //客户端不帮忙转发广播包,所以不会出现这种类型的数据 - } - ip_turn_packet::Protocol::Unknown(_) => {} - } - Ok(()) - } - fn control( - &self, - context: &ChannelContext, - current_device: &CurrentDeviceInfo, - mut net_packet: NetPacket<&mut [u8]>, - route_key: RouteKey, - ) -> anyhow::Result<()> { - let metric = net_packet.source_ttl() - net_packet.ttl() + 1; - let source = net_packet.source(); - match ControlPacket::new(net_packet.transport_protocol(), net_packet.payload())? { - ControlPacket::PingPacket(_) => { - let route = Route::from_default_rt(route_key, metric); - 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; - if current_time < pong_packet.time() { - return Ok(()); - } - let rt = (current_time - pong_packet.time()) as i64; - let route = Route::from(route_key, metric, rt); - context.route_table.add_route(source, route); - } - ControlPacket::PunchRequest => { - log::info!("PunchRequest={:?},source={}", route_key, source); - if context.use_channel_type().is_only_relay() { - return Ok(()); - } - //忽略掉来源于自己的包 - if self - .nat_test - .is_local_address(route_key.protocol().is_base_tcp(), route_key.addr) - { - return Ok(()); - } - - //回应 - net_packet.set_transport_protocol(control_packet::Protocol::PunchResponse.into()); - net_packet.set_source(current_device.virtual_ip); - net_packet.set_destination(source); - net_packet.first_set_ttl(1); - self.client_cipher.encrypt_ipv4(&mut net_packet)?; - context.send_by_key(&net_packet, route_key)?; - // 收到PunchRequest就添加路由,会导致单向通信的问题,删掉试试 - // let route = Route::from_default_rt(route_key, 1); - // context.route_table.add_route_if_absent(source, route); - } - ControlPacket::PunchResponse => { - log::info!("PunchResponse={:?},source={}", route_key, source); - if context.use_channel_type().is_only_relay() { - return Ok(()); - } - if self - .nat_test - .is_local_address(route_key.protocol().is_base_tcp(), route_key.addr) - { - return Ok(()); - } - 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() { - std::net::IpAddr::V4(ipv4) => { - let mut packet = NetPacket::new_encrypt([0; 12 + 6 + ENCRYPTION_RESERVED])?; - packet.set_default_version(); - packet.set_protocol(Protocol::Control); - packet.set_transport_protocol(control_packet::Protocol::AddrResponse.into()); - packet.first_set_ttl(MAX_TTL); - packet.set_source(current_device.virtual_ip); - packet.set_destination(source); - let mut addr_packet = control_packet::AddrPacket::new(packet.payload_mut())?; - addr_packet.set_ipv4(ipv4); - addr_packet.set_port(route_key.addr.port()); - self.client_cipher.encrypt_ipv4(&mut packet)?; - context.send_by_key(&packet, route_key)?; - } - std::net::IpAddr::V6(_) => {} - }, - ControlPacket::AddrResponse(_) => {} - } - Ok(()) - } - fn other_turn( - &self, - context: &ChannelContext, - current_device: &CurrentDeviceInfo, - net_packet: NetPacket<&mut [u8]>, - route_key: RouteKey, - ) -> anyhow::Result<()> { - if context.use_channel_type().is_only_relay() { - return Ok(()); - } - let source = net_packet.source(); - match other_turn_packet::Protocol::from(net_packet.transport_protocol()) { - other_turn_packet::Protocol::Punch => { - let mut punch_info = PunchInfo::parse_from_bytes(net_packet.payload()) - .map_err(|e| anyhow!("PunchInfo {:?}", e))?; - let public_ips = punch_info - .public_ip_list - .iter() - .map(|v| Ipv4Addr::from(v.to_be_bytes())) - .collect(); - let local_ipv4 = Some(Ipv4Addr::from(punch_info.local_ip.to_be_bytes())); - let tcp_port = punch_info.tcp_port as u16; - let 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)) - } else { - None - }; - //兼容旧版本 - if punch_info.public_ports.is_empty() { - punch_info.public_ports.push(punch_info.public_port); - } - //兼容旧版本 - if punch_info.udp_ports.is_empty() { - punch_info.udp_ports.push(punch_info.local_port); - } - let peer_nat_info = NatInfo::new( - public_ips, - punch_info.public_ports.iter().map(|e| *e as u16).collect(), - punch_info.public_port_range as u16, - local_ipv4, - ipv6, - punch_info.udp_ports.iter().map(|e| *e as u16).collect(), - tcp_port, - 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(); - self.peer_nat_info_map.write().insert(source, peer_nat_info); - } - if !punch_info.reply { - let mut punch_reply = PunchInfo::new(); - punch_reply.reply = true; - let nat_info = self.nat_test.nat_info(); - punch_reply.public_ip_list = nat_info - .public_ips - .iter() - .map(|ip| u32::from_be_bytes(ip.octets())) - .collect(); - punch_reply.public_port = nat_info.public_ports.get(0).map_or(0, |v| *v as u32); - punch_reply.public_ports = - nat_info.public_ports.iter().map(|e| *e as u32).collect(); - punch_reply.public_port_range = nat_info.public_port_range as u32; - punch_reply.tcp_port = nat_info.tcp_port as u32; - punch_reply.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; - punch_reply.udp_ports = nat_info.udp_ports.iter().map(|e| *e as u32).collect(); - if let Some(ipv6) = nat_info.ipv6() { - punch_reply.ipv6 = ipv6.octets().to_vec(); - punch_reply.ipv6_port = nat_info.udp_ports[0] as u32; - } - let bytes = punch_reply - .write_to_bytes() - .map_err(|e| anyhow!("punch_reply {:?}", e))?; - let mut punch_packet = - NetPacket::new_encrypt(vec![0u8; 12 + bytes.len() + ENCRYPTION_RESERVED])?; - punch_packet.set_default_version(); - punch_packet.set_protocol(Protocol::OtherTurn); - punch_packet.set_transport_protocol(other_turn_packet::Protocol::Punch.into()); - punch_packet.first_set_ttl(MAX_TTL); - punch_packet.set_source(current_device.virtual_ip()); - punch_packet.set_destination(source); - punch_packet.set_payload(&bytes)?; - self.client_cipher.encrypt_ipv4(&mut punch_packet)?; - if self.punch_sender.send(true, source, peer_nat_info) { - context.send_by_key(&punch_packet, route_key)?; - } - } else { - self.punch_sender.send(false, source, peer_nat_info); - } - } - other_turn_packet::Protocol::Unknown(e) => { - log::warn!("不支持的转发协议 {:?},source:{:?}", e, source); - } - } - Ok(()) - } -} diff --git a/vnt/src/handle/recv_data/mod.rs b/vnt/src/handle/recv_data/mod.rs deleted file mode 100644 index 4b745c1..0000000 --- a/vnt/src/handle/recv_data/mod.rs +++ /dev/null @@ -1,187 +0,0 @@ -use std::collections::HashMap; -use std::net::Ipv4Addr; -use std::sync::Arc; -use std::thread; - -use crossbeam_utils::atomic::AtomicCell; -use parking_lot::{Mutex, RwLock}; - -use crate::channel::context::ChannelContext; -use crate::channel::handler::RecvChannelHandler; -use crate::channel::punch::NatInfo; -use crate::channel::RouteKey; -use crate::cipher::Cipher; -#[cfg(feature = "server_encrypt")] -use crate::cipher::RsaCipher; -use crate::external_route::{AllowExternalRoute, ExternalRoute}; -use crate::handle::callback::VntCallback; -use crate::handle::handshaker::Handshake; -use crate::handle::maintain::PunchSender; -use crate::handle::recv_data::client::ClientPacketHandler; -use crate::handle::recv_data::server::ServerPacketHandler; -use crate::handle::recv_data::turn::TurnPacketHandler; -use crate::handle::{BaseConfigInfo, CurrentDeviceInfo, PeerDeviceInfo, SELF_IP}; -#[cfg(feature = "ip_proxy")] -use crate::ip_proxy::IpProxyMap; -use crate::nat::NatTest; -use crate::protocol::{NetPacket, HEAD_LEN}; -use crate::tun_tap_device::vnt_device::DeviceWrite; - -mod client; -mod server; -mod turn; - -#[derive(Clone)] -pub struct RecvDataHandler { - current_device: Arc>, - turn: TurnPacketHandler, - client: ClientPacketHandler, - server: ServerPacketHandler, - nat_test: NatTest, -} - -impl RecvChannelHandler for RecvDataHandler { - fn handle( - &self, - buf: &mut [u8], - extend: &mut [u8], - route_key: RouteKey, - context: &ChannelContext, - ) { - if buf.len() < HEAD_LEN { - return; - } - //判断stun响应包 - if route_key.protocol().is_udp() { - if let Ok(rs) = self - .nat_test - .recv_data(route_key.index(), route_key.addr, buf) - { - if rs { - return; - } - } - } - if let Err(e) = self.handle0(buf, extend, route_key, context) { - log::error!( - "[{}]-{:?}-{:?}", - thread::current().name().unwrap_or(""), - route_key.addr, - e - ); - } - } -} - -impl RecvDataHandler { - pub fn new( - #[cfg(feature = "server_encrypt")] rsa_cipher: Arc>>, - server_cipher: Cipher, - client_cipher: Cipher, - current_device: Arc>, - device: Device, - device_map: Arc)>>, - config_info: BaseConfigInfo, - nat_test: NatTest, - callback: Call, - punch_sender: PunchSender, - peer_nat_info_map: Arc>>, - external_route: ExternalRoute, - route: AllowExternalRoute, - #[cfg(feature = "integrated_tun")] - #[cfg(feature = "ip_proxy")] - ip_proxy_map: Option, - handshake: Handshake, - #[cfg(feature = "integrated_tun")] - tun_device_helper: crate::tun_tap_device::tun_create_helper::TunDeviceHelper, - ) -> Self { - let server = ServerPacketHandler::new( - #[cfg(feature = "server_encrypt")] - rsa_cipher, - server_cipher, - current_device.clone(), - device.clone(), - device_map, - config_info, - nat_test.clone(), - callback, - external_route.clone(), - handshake, - #[cfg(feature = "integrated_tun")] - tun_device_helper, - ); - let client = ClientPacketHandler::new( - device.clone(), - client_cipher, - punch_sender, - peer_nat_info_map, - nat_test.clone(), - route, - #[cfg(feature = "integrated_tun")] - #[cfg(feature = "ip_proxy")] - ip_proxy_map, - ); - let turn = TurnPacketHandler::new(); - Self { - current_device, - turn, - client, - server, - nat_test, - } - } - fn handle0( - &self, - buf: &mut [u8], - extend: &mut [u8], - route_key: RouteKey, - context: &ChannelContext, - ) -> anyhow::Result<()> { - let net_packet = NetPacket::new(buf)?; - - let extend = NetPacket::unchecked(extend); - if net_packet.ttl() == 0 || net_packet.source_ttl() < net_packet.ttl() { - log::warn!("丢弃过时包:{:?} {}", net_packet.head(), route_key.addr); - return Ok(()); - } - let current_device = self.current_device.load(); - let dest = net_packet.destination(); - if dest == current_device.virtual_ip - || dest.is_broadcast() - || dest.is_multicast() - || dest == SELF_IP - || dest.is_unspecified() - || dest == current_device.broadcast_ip - { - // 统计流量 - if let Some(down_traffic_meter) = &context.down_traffic_meter { - down_traffic_meter.add_traffic(net_packet.source(), net_packet.data_len()) - } - //发给自己的包 - if net_packet.is_gateway() { - //服务端-客户端包 - self.server - .handle(net_packet, extend, route_key, context, ¤t_device) - } else { - //客户端-客户端包 - self.client - .handle(net_packet, extend, route_key, context, ¤t_device) - } - } else { - //转发包 - self.turn - .handle(net_packet, extend, route_key, context, ¤t_device) - } - } -} - -pub trait PacketHandler { - fn handle( - &self, - net_packet: NetPacket<&mut [u8]>, - extend: NetPacket<&mut [u8]>, - route_key: RouteKey, - context: &ChannelContext, - current_device: &CurrentDeviceInfo, - ) -> anyhow::Result<()>; -} diff --git a/vnt/src/handle/recv_data/server.rs b/vnt/src/handle/recv_data/server.rs deleted file mode 100644 index bd66f24..0000000 --- a/vnt/src/handle/recv_data/server.rs +++ /dev/null @@ -1,599 +0,0 @@ -use anyhow::anyhow; -use std::collections::HashMap; -use std::io; -use std::net::Ipv4Addr; -use std::sync::Arc; -#[cfg(feature = "server_encrypt")] -use std::time::{Duration, Instant}; - -use crossbeam_utils::atomic::AtomicCell; -use 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}; -use crate::cipher::Cipher; -#[cfg(feature = "server_encrypt")] -use crate::cipher::RsaCipher; -use crate::external_route::ExternalRoute; -use crate::handle::callback::{ErrorInfo, ErrorType, HandshakeInfo, RegisterInfo, VntCallback}; -#[cfg(feature = "server_encrypt")] -use crate::handle::handshaker; -use crate::handle::handshaker::Handshake; -use crate::handle::recv_data::PacketHandler; -use crate::handle::{registrar, BaseConfigInfo, ConnectStatus, CurrentDeviceInfo, PeerDeviceInfo}; -use crate::nat::NatTest; -use crate::proto::message::{DeviceList, HandshakeResponse, RegistrationResponse}; -use crate::protocol::body::ENCRYPTION_RESERVED; -use crate::protocol::control_packet::ControlPacket; -use crate::protocol::error_packet::InErrorPacket; -use crate::protocol::{ip_turn_packet, service_packet, NetPacket, Protocol, MAX_TTL}; -use crate::tun_tap_device::vnt_device::DeviceWrite; -use crate::{proto, PeerClientInfo}; - -/// 处理来源于服务端的包 -#[derive(Clone)] -pub struct ServerPacketHandler { - #[cfg(feature = "server_encrypt")] - rsa_cipher: Arc>>, - server_cipher: Cipher, - current_device: Arc>, - device: Device, - device_map: Arc)>>, - config_info: BaseConfigInfo, - nat_test: NatTest, - callback: Call, - #[cfg(feature = "server_encrypt")] - up_key_time: Arc>, - external_route: ExternalRoute, - handshake: Handshake, - #[cfg(feature = "integrated_tun")] - tun_device_helper: crate::tun_tap_device::tun_create_helper::TunDeviceHelper, -} - -impl ServerPacketHandler { - pub fn new( - #[cfg(feature = "server_encrypt")] rsa_cipher: Arc>>, - server_cipher: Cipher, - current_device: Arc>, - device: Device, - device_map: Arc)>>, - config_info: BaseConfigInfo, - nat_test: NatTest, - callback: Call, - external_route: ExternalRoute, - handshake: Handshake, - #[cfg(feature = "integrated_tun")] - tun_device_helper: crate::tun_tap_device::tun_create_helper::TunDeviceHelper, - ) -> Self { - Self { - #[cfg(feature = "server_encrypt")] - rsa_cipher, - server_cipher, - current_device, - device, - device_map, - config_info, - nat_test, - callback, - #[cfg(feature = "server_encrypt")] - up_key_time: Arc::new(AtomicCell::new( - Instant::now() - .checked_sub(Duration::from_secs(60)) - .unwrap_or(Instant::now()), - )), - external_route, - handshake, - #[cfg(feature = "integrated_tun")] - tun_device_helper, - } - } -} - -impl PacketHandler for ServerPacketHandler { - fn handle( - &self, - mut net_packet: NetPacket<&mut [u8]>, - _extend: NetPacket<&mut [u8]>, - route_key: RouteKey, - context: &ChannelContext, - current_device: &CurrentDeviceInfo, - ) -> anyhow::Result<()> { - if !current_device.is_server_addr(route_key.addr) { - //拦截不是服务端的流量 - log::warn!( - "route_key={:?},不是来源于服务端地址{}", - route_key, - current_device.connect_server - ); - } - context - .route_table - .update_read_time(&net_packet.source(), &route_key); - if net_packet.protocol() == Protocol::Error - && net_packet.transport_protocol() - == crate::protocol::error_packet::Protocol::NoKey.into() - { - //服务端通知客户端上传密钥 - #[cfg(feature = "server_encrypt")] - { - let mutex_guard = self.rsa_cipher.lock(); - if let Some(rsa_cipher) = mutex_guard.as_ref() { - let last = self.up_key_time.load(); - if last.elapsed() < Duration::from_secs(1) - || self - .up_key_time - .compare_exchange(last, Instant::now()) - .is_err() - { - //短时间不重复上传服务端密钥 - return Ok(()); - } - if let Some(key) = self.server_cipher.key() { - log::info!("上传密钥到服务端:{:?}", route_key); - let packet = handshaker::secret_handshake_request_packet( - rsa_cipher, - self.config_info.token.clone(), - key, - )?; - context.send_by_key(&packet, route_key)?; - } - } - } - return Ok(()); - } else if net_packet.protocol() == Protocol::Service - && net_packet.transport_protocol() == service_packet::Protocol::HandshakeResponse.into() - { - let response = HandshakeResponse::parse_from_bytes(net_packet.payload()) - .map_err(|e| 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() { - { - let guard = self.rsa_cipher.lock(); - if let Some(rsa_cipher) = guard.as_ref() { - if rsa_cipher.finger() == &response.key_finger { - let packet = handshaker::secret_handshake_request_packet( - rsa_cipher, - self.config_info.token.clone(), - key, - )?; - drop(guard); - context.send_by_key(&packet, route_key)?; - return Ok(()); - } - log::warn!( - "拒绝服务端密钥对变化,原指纹:{:?},新指纹:{:?},addr:{:?}", - rsa_cipher.finger(), - response.key_finger, - route_key - ); - return Ok(()); - } - drop(guard); - } - let rsa_cipher = RsaCipher::new(&response.public_key)?; - if rsa_cipher.finger() != &response.key_finger { - log::info!( - "服务端密钥和指纹不匹 配拒绝握手,指纹1:{:?},指纹2:{:?}", - rsa_cipher.finger(), - response.key_finger - ); - return Ok(()); - } - let handshake_info = HandshakeInfo::new( - rsa_cipher.public_key()?.clone(), - response.key_finger, - response.version, - ); - log::info!("加密握手请求:{:?}", handshake_info); - - if self.callback.handshake(handshake_info) { - let packet = handshaker::secret_handshake_request_packet( - &rsa_cipher, - self.config_info.token.clone(), - key, - )?; - context.send_by_key(&packet, route_key)?; - self.rsa_cipher.lock().replace(rsa_cipher); - } - return Ok(()); - } - #[cfg(feature = "server_encrypt")] - if let Ok(rsa_cipher) = RsaCipher::new(&response.public_key) { - self.rsa_cipher.lock().replace(rsa_cipher); - } - let handshake_info = HandshakeInfo::new_no_secret(response.version); - if self.callback.handshake(handshake_info) { - //没有加密,则发送注册请求 - self.register(current_device, context, route_key)?; - } - - return Ok(()); - } - //服务端数据解密 - self.server_cipher.decrypt_ipv4(&mut net_packet)?; - match net_packet.protocol() { - Protocol::Service => { - self.service(context, current_device, net_packet, route_key)?; - } - Protocol::Error => { - self.error(context, current_device, net_packet, route_key)?; - } - Protocol::Control => { - self.control(context, current_device, net_packet, route_key)?; - } - Protocol::IpTurn => { - match ip_turn_packet::Protocol::from(net_packet.transport_protocol()) { - ip_turn_packet::Protocol::Ipv4 => { - let ipv4 = IpV4Packet::new(net_packet.payload())?; - match ipv4.protocol() { - ipv4::protocol::Protocol::Icmp => { - if ipv4.destination_ip() == current_device.virtual_ip { - let icmp_packet = icmp::IcmpPacket::new(ipv4.payload())?; - if icmp_packet.kind() == Kind::EchoReply { - //网关ip ping的回应 - self.device.write(net_packet.payload())?; - return Ok(()); - } - } - } - _ => {} - } - } - ip_turn_packet::Protocol::WGIpv4 => { - if self.config_info.allow_wire_guard { - self.device.write(net_packet.payload())?; - } - } - ip_turn_packet::Protocol::Ipv4Broadcast => {} - ip_turn_packet::Protocol::Unknown(_) => {} - } - } - Protocol::OtherTurn => {} - Protocol::Unknown(_) => {} - } - Ok(()) - } -} - -impl ServerPacketHandler { - fn service( - &self, - context: &ChannelContext, - current_device: &CurrentDeviceInfo, - net_packet: NetPacket<&mut [u8]>, - route_key: RouteKey, - ) -> anyhow::Result<()> { - match service_packet::Protocol::from(net_packet.transport_protocol()) { - service_packet::Protocol::RegistrationResponse => { - let response = RegistrationResponse::parse_from_bytes(net_packet.payload()) - .map_err(|e| { - io::Error::new( - io::ErrorKind::Other, - format!("RegistrationResponse {:?}", e), - ) - })?; - let virtual_ip = Ipv4Addr::from(response.virtual_ip); - let virtual_netmask = Ipv4Addr::from(response.virtual_netmask); - let virtual_gateway = Ipv4Addr::from(response.virtual_gateway); - let virtual_network = - Ipv4Addr::from(response.virtual_ip & response.virtual_netmask); - let register_info = RegisterInfo::new(virtual_ip, virtual_netmask, virtual_gateway); - log::info!("注册成功:{:?}", register_info); - if self.callback.register(register_info) { - let route = Route::from_default_rt(route_key, 1); - context - .route_table - .add_route_if_absent(virtual_gateway, route); - let public_ip = response.public_ip.into(); - let public_port = response.public_port as u16; - self.nat_test - .update_addr(route_key.index(), public_ip, public_port); - if 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 { - let mut new_current_device = cur; - new_current_device.update(virtual_ip, virtual_netmask, virtual_gateway); - new_current_device.virtual_ip = virtual_ip; - new_current_device.virtual_netmask = virtual_netmask; - new_current_device.virtual_gateway = virtual_gateway; - new_current_device.status = ConnectStatus::Connected; - if let Err(c) = self - .current_device - .compare_exchange(cur, new_current_device) - { - cur = c; - } else { - break; - } - } - - if old.virtual_ip != virtual_ip - || old.virtual_gateway != virtual_gateway - || old.virtual_netmask != virtual_netmask - { - if old.virtual_ip != Ipv4Addr::UNSPECIFIED { - log::info!("ip发生变化,old:{:?},response={:?}", old, response); - } - let device_config = crate::handle::callback::DeviceConfig::new( - #[cfg(feature = "integrated_tun")] - #[cfg(target_os = "windows")] - self.config_info.tap, - #[cfg(feature = "integrated_tun")] - #[cfg(any( - target_os = "windows", - target_os = "linux", - target_os = "macos" - ))] - self.config_info.device_name.clone(), - self.config_info.mtu, - virtual_ip, - virtual_netmask, - virtual_gateway, - virtual_network, - self.external_route.to_route(), - ); - #[cfg(not(feature = "integrated_tun"))] - self.callback.create_device(device_config); - #[cfg(feature = "integrated_tun")] - { - self.tun_device_helper.stop(); - #[cfg(any( - target_os = "windows", - target_os = "linux", - target_os = "macos" - ))] - match crate::tun_tap_device::create_device( - device_config, - &self.callback, - ) { - Ok(device) => { - let tun_info = crate::handle::callback::DeviceInfo::new( - device.name().unwrap_or("unknown".into()), - "".into(), - ); - log::info!("tun信息{:?}", tun_info); - self.callback.create_tun(tun_info); - self.tun_device_helper - .start(device, self.config_info.allow_wire_guard)?; - } - Err(e) => { - log::error!("{:?}", e); - self.callback.error(e); - } - } - #[cfg(target_os = "android")] - { - let device_config = crate::handle::callback::DeviceConfig::new( - self.config_info.mtu, - virtual_ip, - virtual_netmask, - virtual_gateway, - virtual_network, - self.external_route.to_route(), - ); - let device_fd = self.callback.generate_tun(device_config); - if device_fd == 0 { - self.callback.error(ErrorInfo::new_msg( - ErrorType::FailedToCrateDevice, - "device_fd == 0".into(), - )); - } else { - 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), - )); - } - } - } - } - } - self.set_device_info_list(response.device_info_list, response.epoch as _); - if old.status.offline() { - self.callback.success(); - } - } - } - service_packet::Protocol::PushDeviceList => { - let response = DeviceList::parse_from_bytes(net_packet.payload()).map_err(|e| { - io::Error::new(io::ErrorKind::Other, format!("PushDeviceList {:?}", e)) - })?; - self.set_device_info_list(response.device_info_list, response.epoch as _); - } - service_packet::Protocol::SecretHandshakeResponse => { - log::info!("SecretHandshakeResponse"); - //加密握手结束,发送注册数据 - self.register(current_device, context, route_key)?; - } - _ => { - log::warn!( - "service_packet::Protocol::Unknown = {:?}", - net_packet.head() - ); - } - } - Ok(()) - } - fn set_device_info_list(&self, device_info_list: Vec, epoch: u16) { - let ip_list: Vec = device_info_list - .into_iter() - .map(|info| { - PeerDeviceInfo::new( - Ipv4Addr::from(info.virtual_ip), - info.name, - info.device_status as u8, - info.client_secret, - info.client_secret_hash, - info.wireguard, - ) - }) - .collect(); - { - let mut dev = self.device_map.lock(); - //这里可能会收到旧的消息,但是随着时间推移总会收到新的 - dev.0 = epoch; - dev.1.clear(); - for info in ip_list.clone() { - dev.1.insert(info.virtual_ip, info); - } - } - self.callback.peer_client_list( - ip_list - .into_iter() - .map(|v| PeerClientInfo::new(v.virtual_ip, v.name, v.status, v.client_secret)) - .collect(), - ); - } - fn register( - &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(); - let client_secret = self - .config_info - .client_secret_hash - .as_ref() - .map(|v| v.as_ref()); - let mut ip = self.config_info.ip; - if ip.is_none() { - ip = Some(current_device.virtual_ip) - } - let response = registrar::registration_request_packet( - &self.server_cipher, - token, - device_id, - name, - ip, - false, - false, - client_secret, - )?; - log::info!("发送注册请求,{:?}", self.config_info); - //注册请求只发送到默认通道 - context.send_default(&response, current_device.connect_server)?; - Ok(()) - } - fn error( - &self, - context: &ChannelContext, - _current_device: &CurrentDeviceInfo, - net_packet: NetPacket<&mut [u8]>, - route_key: RouteKey, - ) -> io::Result<()> { - match InErrorPacket::new(net_packet.transport_protocol(), net_packet.payload())? { - InErrorPacket::TokenError => { - // token错误,可能是服务端设置了白名单 - let err = ErrorInfo::new(ErrorType::TokenError); - self.callback.error(err); - } - InErrorPacket::Disconnect => { - crate::handle::change_status(&self.current_device, ConnectStatus::Connecting); - let err = ErrorInfo::new(ErrorType::Disconnect); - self.callback.error(err); - //掉线epoch要归零 - { - let mut dev = self.device_map.lock(); - dev.0 = 0; - drop(dev); - } - self.handshake - .send(context, self.config_info.server_secret, route_key.addr)?; - // self.register(current_device, context, route_key)?; - } - InErrorPacket::AddressExhausted => { - // 地址用尽 - let err = ErrorInfo::new(ErrorType::AddressExhausted); - self.callback.error(err); - } - InErrorPacket::OtherError(e) => { - let err = ErrorInfo::new_msg(ErrorType::Unknown, e.message()?); - self.callback.error(err); - } - InErrorPacket::IpAlreadyExists => { - let err = ErrorInfo::new(ErrorType::IpAlreadyExists); - self.callback.error(err); - } - InErrorPacket::InvalidIp => { - let err = ErrorInfo::new(ErrorType::InvalidIp); - self.callback.error(err); - } - InErrorPacket::NoKey => { - //这个类型最开头已经处理过,这里忽略 - } - } - Ok(()) - } - fn control( - &self, - context: &ChannelContext, - current_device: &CurrentDeviceInfo, - net_packet: NetPacket<&mut [u8]>, - route_key: RouteKey, - ) -> anyhow::Result<()> { - match ControlPacket::new(net_packet.transport_protocol(), net_packet.payload())? { - ControlPacket::PongPacket(pong_packet) => { - let current_time = crate::handle::now_time() as u16; - if current_time < pong_packet.time() { - return Ok(()); - } - let metric = net_packet.source_ttl() - net_packet.ttl() + 1; - let rt = (current_time - pong_packet.time()) as i64; - let route = Route::from(route_key, metric, rt); - context.route_table.add_route(net_packet.source(), route); - let epoch = self.device_map.lock().0; - if pong_packet.epoch() != epoch { - //纪元不一致,可能有新客户端连接,向服务端拉取客户端列表 - let mut poll_device = NetPacket::new_encrypt([0; 12 + ENCRYPTION_RESERVED])?; - poll_device.set_source(current_device.virtual_ip); - poll_device.set_destination(current_device.virtual_gateway); - poll_device.set_default_version(); - poll_device.set_gateway_flag(true); - poll_device.first_set_ttl(MAX_TTL); - poll_device.set_protocol(Protocol::Service); - poll_device - .set_transport_protocol(service_packet::Protocol::PullDeviceList.into()); - self.server_cipher.encrypt_ipv4(&mut poll_device)?; - //发送到默认服务端即可 - context.send_default(&poll_device, current_device.connect_server)?; - } - } - ControlPacket::AddrResponse(addr_packet) => { - //更新本地公网ipv4 - self.nat_test.update_addr( - route_key.index(), - addr_packet.ipv4(), - addr_packet.port(), - ); - } - _ => {} - } - Ok(()) - } -} diff --git a/vnt/src/handle/recv_data/turn.rs b/vnt/src/handle/recv_data/turn.rs deleted file mode 100644 index 59f82d9..0000000 --- a/vnt/src/handle/recv_data/turn.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::channel::context::ChannelContext; -use crate::channel::RouteKey; -use crate::handle::recv_data::PacketHandler; -use crate::handle::CurrentDeviceInfo; -use crate::protocol::NetPacket; -use anyhow::Context; - -/// 处理客户端中转包 -#[derive(Clone)] -pub struct TurnPacketHandler {} - -impl TurnPacketHandler { - pub fn new() -> Self { - Self {} - } -} - -impl PacketHandler for TurnPacketHandler { - fn handle( - &self, - mut net_packet: NetPacket<&mut [u8]>, - _extend: NetPacket<&mut [u8]>, - route_key: RouteKey, - context: &ChannelContext, - _current_device: &CurrentDeviceInfo, - ) -> anyhow::Result<()> { - // ttl减一 - let ttl = net_packet.incr_ttl(); - if ttl > 0 { - if net_packet.is_gateway() { - // 暂时不转发服务端包 - return Ok(()); - } - let destination = net_packet.destination(); - if let Some(route) = context.route_table.route_one(&destination) { - if route.addr == route_key.addr { - //防止环路 - log::warn!("来源和目标相同 {:?},{:?}", route_key, net_packet.head()); - return Ok(()); - } - if route.metric <= ttl { - return context - .send_by_key(&net_packet, route.route_key()) - .context("转发失败"); - } - } - //其他没有路由的不转发 - } - log::info!("没有路由 {:?},{:?}", route_key, net_packet.head()); - Ok(()) - } -} diff --git a/vnt/src/handle/registrar.rs b/vnt/src/handle/registrar.rs deleted file mode 100644 index 417fea5..0000000 --- a/vnt/src/handle/registrar.rs +++ /dev/null @@ -1,54 +0,0 @@ -use anyhow::anyhow; -use std::net::Ipv4Addr; - -use protobuf::Message; - -use crate::cipher::Cipher; -use crate::handle::{GATEWAY_IP, SELF_IP}; -use crate::proto::message::RegistrationRequest; -use crate::protocol::body::ENCRYPTION_RESERVED; -use crate::protocol::{service_packet, NetPacket, Protocol, MAX_TTL}; - -/// 注册数据 -pub fn registration_request_packet( - server_cipher: &Cipher, - token: String, - device_id: String, - name: String, - ip: Option, - is_fast: bool, - allow_ip_change: bool, - client_secret_hash: Option<&[u8]>, -) -> anyhow::Result>> { - let mut request = RegistrationRequest::new(); - request.token = token; - request.device_id = device_id; - request.name = name; - if let Some(ip) = ip { - request.virtual_ip = ip.into(); - } - request.allow_ip_change = allow_ip_change; - request.is_fast = is_fast; - request.version = crate::VNT_VERSION.to_string(); - if let Some(client_secret_hash) = client_secret_hash { - request.client_secret = true; - request - .client_secret_hash - .extend_from_slice(client_secret_hash); - } - let bytes = request - .write_to_bytes() - .map_err(|e| anyhow!("RegistrationRequest {:?}", e))?; - let buf = vec![0u8; 12 + bytes.len() + ENCRYPTION_RESERVED]; - let mut net_packet = NetPacket::new_encrypt(buf)?; - net_packet.set_destination(GATEWAY_IP); - net_packet.set_source(SELF_IP); - net_packet.set_default_version(); - net_packet.set_gateway_flag(true); - net_packet.set_protocol(Protocol::Service); - net_packet.set_transport_protocol(service_packet::Protocol::RegistrationRequest.into()); - net_packet.first_set_ttl(MAX_TTL); - net_packet.set_payload(&bytes)?; - server_cipher.encrypt_ipv4(&mut net_packet)?; - Ok(net_packet) -} diff --git a/vnt/src/handle/tun_tap/mod.rs b/vnt/src/handle/tun_tap/mod.rs deleted file mode 100644 index 6571e48..0000000 --- a/vnt/src/handle/tun_tap/mod.rs +++ /dev/null @@ -1,35 +0,0 @@ -pub mod tun_handler; - -use crossbeam_utils::atomic::AtomicCell; -use parking_lot::Mutex; -use std::sync::Arc; -mod platform; - -pub(crate) use platform::*; - -/// 仅仅是停止tun,不停止vnt -#[derive(Clone, Default)] -pub struct DeviceStop { - f: Arc>>>, - stopped: Arc>, -} - -impl DeviceStop { - pub fn set_stop_fn(&self, f: F) - where - F: FnOnce() + Send + 'static, - { - self.f.lock().replace(Box::new(f)); - } - pub fn stop(&self) { - if let Some(f) = self.f.lock().take() { - f() - } - } - pub fn stopped(&self) { - self.stopped.store(true); - } - pub fn is_stopped(&self) -> bool { - self.stopped.load() - } -} diff --git a/vnt/src/handle/tun_tap/platform.rs b/vnt/src/handle/tun_tap/platform.rs deleted file mode 100644 index 4cdc5d3..0000000 --- a/vnt/src/handle/tun_tap/platform.rs +++ /dev/null @@ -1,115 +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 parking_lot::Mutex; -use std::collections::HashMap; -use std::net::Ipv4Addr; -use std::sync::Arc; -use tun_rs::{InterruptEvent, SyncDevice}; - -pub(crate) fn start_simple( - stop_manager: StopManager, - context: &ChannelContext, - device: Arc, - current_device: Arc>, - ip_route: ExternalRoute, - #[cfg(feature = "ip_proxy")] ip_proxy_map: Option, - client_cipher: Cipher, - server_cipher: Cipher, - device_map: Arc)>>, - compressor: Compressor, - device_stop: DeviceStop, - allow_wire_guard: bool, -) -> anyhow::Result<()> { - let event = Arc::new(InterruptEvent::new()?); - let worker = { - let event = event.clone(); - stop_manager.add_listener("tun_device".into(), move || { - if let Err(e) = event.trigger() { - 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( - context, - device, - event, - 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(); - } - Ok(()) -} - -fn start_simple0( - context: &ChannelContext, - device: Arc, - event: Arc, - current_device: Arc>, - ip_route: ExternalRoute, - #[cfg(feature = "ip_proxy")] ip_proxy_map: Option, - client_cipher: Cipher, - server_cipher: Cipher, - device_map: Arc)>>, - compressor: Compressor, - allow_wire_guard: bool, -) -> anyhow::Result<()> { - let mut buf = [0; BUFFER_SIZE]; - let mut extend = [0; BUFFER_SIZE]; - loop { - let len = device.recv_intr(&mut buf[12..],&event)? + 12; - // 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!("tun/tap {:?}", e) - } - } - } -} diff --git a/vnt/src/handle/tun_tap/tun_handler.rs b/vnt/src/handle/tun_tap/tun_handler.rs deleted file mode 100644 index d3be016..0000000 --- a/vnt/src/handle/tun_tap/tun_handler.rs +++ /dev/null @@ -1,288 +0,0 @@ -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 tun_rs::SyncDevice; - -use crate::channel::context::ChannelContext; -use crate::channel::sender::{send_to_wg, send_to_wg_broadcast}; -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; -#[cfg(feature = "ip_proxy")] -use crate::ip_proxy::ProxyHandler; -use crate::protocol; -use crate::protocol::body::ENCRYPTION_RESERVED; -use crate::protocol::ip_turn_packet::BroadcastPacket; -use crate::protocol::{ip_turn_packet, NetPacket, MAX_TTL}; -use crate::util::StopManager; -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 { - icmp.set_kind(Kind::EchoReply); - icmp.update_checksum(); - let src = ipv4_packet.source_ip(); - ipv4_packet.set_source_ip(ipv4_packet.destination_ip()); - ipv4_packet.set_destination_ip(src); - ipv4_packet.update_checksum(); - device_writer.send(ipv4_packet.buffer)?; - } - } - Ok(()) -} - -pub fn start( - stop_manager: StopManager, - context: ChannelContext, - device: Arc, - current_device: Arc>, - ip_route: ExternalRoute, - #[cfg(feature = "ip_proxy")] ip_proxy_map: Option, - client_cipher: Cipher, - server_cipher: Cipher, - device_map: Arc)>>, - compressor: Compressor, - device_stop: DeviceStop, - allow_wire_guard: bool, -) -> io::Result<()> { - thread::Builder::new() - .name("tunHandlerS".into()) - .spawn(move || { - if let Err(e) = crate::handle::tun_tap::start_simple( - stop_manager, - &context, - device, - current_device, - ip_route, - #[cfg(feature = "ip_proxy")] - ip_proxy_map, - client_cipher, - server_cipher, - device_map, - compressor, - device_stop, - allow_wire_guard, - ) { - log::warn!("stop:{}", e); - } - })?; - - Ok(()) -} - -fn broadcast( - server_cipher: &Cipher, - sender: &ChannelContext, - net_packet: &mut NetPacket<&mut [u8]>, - current_device: &CurrentDeviceInfo, - device_map: &Mutex<(u16, HashMap)>, -) -> anyhow::Result<()> { - let list: Vec = device_map - .lock() - .1 - .values() - .filter(|info| !info.wireguard && info.status.is_online()) - .map(|info| info.virtual_ip) - .collect(); - if list.is_empty() { - return Ok(()); - } - const MAX_COUNT: usize = 8; - let mut p2p_ips = Vec::with_capacity(8); - let mut relay = false; - let mut overflow = false; - for (index, peer_ip) in list.into_iter().enumerate() { - if index > MAX_COUNT { - overflow = true; - break; - } - if let Some(route) = sender.route_table.route_one_p2p(&peer_ip) { - if sender.send_by_key(&net_packet, route.route_key()).is_ok() { - p2p_ips.push(peer_ip); - continue; - } - } - relay = true; - } - if !overflow && !relay { - //全部p2p,不需要服务器中转 - return Ok(()); - } - if current_device.status.offline() { - //离线的不再转发 - return Ok(()); - } - if p2p_ips.is_empty() { - //都没有p2p则直接由服务器转发 - sender.send_default(&net_packet, current_device.connect_server)?; - return Ok(()); - } - - let buf = vec![0u8; 12 + 1 + p2p_ips.len() * 4 + net_packet.data_len() + ENCRYPTION_RESERVED]; - //剩余的发送到服务端,需要告知哪些已发送过 - let mut server_packet = NetPacket::new_encrypt(buf)?; - server_packet.set_default_version(); - server_packet.set_gateway_flag(true); - server_packet.first_set_ttl(MAX_TTL); - server_packet.set_source(net_packet.source()); - //使用对应的目的地址 - server_packet.set_destination(net_packet.destination()); - server_packet.set_protocol(protocol::Protocol::IpTurn); - server_packet.set_transport_protocol(ip_turn_packet::Protocol::Ipv4Broadcast.into()); - - let mut broadcast = BroadcastPacket::unchecked(server_packet.payload_mut()); - broadcast.set_address(&p2p_ips)?; - broadcast.set_data(net_packet.buffer())?; - server_cipher.encrypt_ipv4(&mut server_packet)?; - sender.send_default(&server_packet, current_device.connect_server)?; - Ok(()) -} - -/// 接收tun数据,并且转发到udp上 -/// 实现一个原地发送,必须保证是如下结构 -/// |12字节开头|ip报文|至少1024字节结尾| -/// -pub(crate) fn handle( - context: &ChannelContext, - buf: &mut [u8], - data_len: usize, //数据总长度=12+ip包长度 - extend: &mut [u8], - device_writer: &SyncDevice, - current_device: CurrentDeviceInfo, - ip_route: &ExternalRoute, - #[cfg(feature = "ip_proxy")] proxy_map: &Option, - client_cipher: &Cipher, - server_cipher: &Cipher, - device_map: &Mutex<(u16, HashMap)>, - compressor: &Compressor, - allow_wire_guard: bool, -) -> anyhow::Result<()> { - //忽略掉结构不对的情况(ipv6数据、win tap会读到空数据),不然日志打印太多了 - let ipv4_packet = match IpV4Packet::new(&mut buf[12..data_len]) { - Ok(packet) => packet, - Err(_) => return Ok(()), - }; - let src_ip = ipv4_packet.source_ip(); - let dest_ip = ipv4_packet.destination_ip(); - if src_ip == dest_ip { - return icmp(&device_writer, ipv4_packet); - } - let protocol = ipv4_packet.protocol(); - let src_ip = ipv4_packet.source_ip(); - let mut dest_ip = ipv4_packet.destination_ip(); - let mut net_packet = NetPacket::new0(data_len, buf)?; - let mut out = NetPacket::unchecked(extend); - net_packet.set_default_version(); - net_packet.set_protocol(protocol::Protocol::IpTurn); - net_packet.set_transport_protocol(ip_turn_packet::Protocol::Ipv4.into()); - net_packet.first_set_ttl(6); - net_packet.set_source(src_ip); - net_packet.set_destination(dest_ip); - if dest_ip == current_device.virtual_gateway { - // 发到网关的加密方式不一样,要单独处理 - if protocol == Protocol::Icmp { - net_packet.set_gateway_flag(true); - server_cipher.encrypt_ipv4(&mut net_packet)?; - context.send_default(&net_packet, current_device.connect_server)?; - } - return Ok(()); - } - if !dest_ip.is_multicast() && !dest_ip.is_broadcast() && current_device.broadcast_ip != dest_ip - { - if current_device.not_in_network(dest_ip) { - if let Some(r_dest_ip) = ip_route.route(&dest_ip) { - //路由的目标不能是自己 - if r_dest_ip == src_ip { - return Ok(()); - } - //需要修改目的地址 - dest_ip = r_dest_ip; - net_packet.set_destination(r_dest_ip); - } else { - return Ok(()); - } - } - #[cfg(feature = "ip_proxy")] - if let Some(proxy_map) = proxy_map { - let mut ipv4_packet = IpV4Packet::new(net_packet.payload_mut())?; - proxy_map.send_handle(&mut ipv4_packet)?; - } - } - - if dest_ip.is_multicast() { - //当作广播处理 - dest_ip = Ipv4Addr::BROADCAST; - net_packet.set_destination(Ipv4Addr::BROADCAST); - } - let is_broadcast = dest_ip.is_broadcast() || current_device.broadcast_ip == dest_ip; - if allow_wire_guard { - if is_broadcast { - // wg客户端和vnt客户端分开广播 - let exists_wg = device_map - .lock() - .1 - .values() - .any(|v| v.status.is_online() && v.wireguard); - if exists_wg { - send_to_wg_broadcast(context, &net_packet, server_cipher, ¤t_device)?; - } - } else { - // 如果是wg客户端则发到vnts转发 - let guard = device_map.lock(); - if let Some(peer_info) = guard.1.get(&dest_ip) { - if peer_info.status.is_offline() { - return Ok(()); - } - if peer_info.wireguard { - drop(guard); - send_to_wg(context, &mut net_packet, server_cipher, ¤t_device)?; - return Ok(()); - } - } - } - } - - let mut net_packet = if compressor.compress(&net_packet, &mut out)? { - out.set_default_version(); - out.set_protocol(protocol::Protocol::IpTurn); - out.set_transport_protocol(ip_turn_packet::Protocol::Ipv4.into()); - out.first_set_ttl(6); - out.set_source(src_ip); - out.set_destination(dest_ip); - out - } else { - net_packet - }; - if is_broadcast { - // 广播 发送到直连目标 - client_cipher.encrypt_ipv4(&mut net_packet)?; - broadcast( - server_cipher, - context, - &mut net_packet, - ¤t_device, - device_map, - )?; - return Ok(()); - } - - client_cipher.encrypt_ipv4(&mut net_packet)?; - context.send_ipv4_by_id( - &net_packet, - &dest_ip, - current_device.connect_server, - current_device.status.online(), - )?; - Ok(()) -} diff --git a/vnt/src/ip_proxy/icmp_proxy.rs b/vnt/src/ip_proxy/icmp_proxy.rs deleted file mode 100644 index 9afca4b..0000000 --- a/vnt/src/ip_proxy/icmp_proxy.rs +++ /dev/null @@ -1,225 +0,0 @@ -use anyhow::Context; -use std::collections::HashMap; -use std::io; -use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}; -use std::sync::Arc; - -use crossbeam_utils::atomic::AtomicCell; -use parking_lot::Mutex; -use tokio::net::UdpSocket; - -use packet::icmp::icmp; -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; -use crate::protocol; -use crate::protocol::{NetPacket, MAX_TTL}; -#[derive(Clone)] -pub struct IcmpProxy { - icmp_socket: Arc, - // 对端-> 真实来源 - nat_map: Arc>>, -} - -impl IcmpProxy { - pub async fn new( - context: ChannelContext, - current_device: Arc>, - client_cipher: Cipher, - default_interface: &LocalInterface, - ) -> anyhow::Result { - #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] - let icmp_socket = socket2::Socket::new( - socket2::Domain::IPV4, - socket2::Type::RAW, - Some(socket2::Protocol::ICMPV4), - ) - .context("new Socket RAW ICMPV4 failed")?; - #[cfg(target_os = "android")] - let icmp_socket = socket2::Socket::new( - socket2::Domain::IPV4, - socket2::Type::DGRAM, - Some(socket2::Protocol::ICMPV4), - ) - .context("new Socket DGRAM ICMPV4 failed")?; - let addr: SocketAddrV4 = SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0); - icmp_socket - .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()?)?; - let nat_map: Arc>> = - Arc::new(Mutex::new(HashMap::with_capacity(16))); - { - let nat_map = nat_map.clone(); - tokio::spawn(async { - if let Err(e) = icmp_proxy( - tokio_icmp_socket, - nat_map, - context, - current_device, - client_cipher, - ) - .await - { - log::warn!("icmp_proxy:{:?}", e); - } - }); - } - Ok(Self { - icmp_socket: Arc::new(std_socket), - nat_map, - }) - } -} - -async fn icmp_proxy( - icmp_socket: UdpSocket, - // 对端-> 真实来源 - nat_map: Arc>>, - context: ChannelContext, - current_device: Arc>, - client_cipher: Cipher, -) -> io::Result<()> { - let mut buf = [0u8; 65535 - 20 - 8]; - #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] - let start = 12; - #[cfg(target_os = "android")] - let start = 12 + 20; - loop { - let (len, addr) = icmp_socket.recv_from(&mut buf[start..]).await?; - if let IpAddr::V4(peer_ip) = addr.ip() { - #[cfg(target_os = "android")] - { - let buf = &mut buf[12..]; - // ipv4 头部20字节 - buf[0] = 0b0100_0110; - //写入总长度 - buf[2..4].copy_from_slice(&((20 + len) as u16).to_be_bytes()); - - let mut ipv4 = IpV4Packet::unchecked(buf); - ipv4.set_flags(2); - ipv4.set_ttl(1); - ipv4.set_protocol(packet::ip::ipv4::protocol::Protocol::Icmp); - ipv4.set_source_ip(peer_ip); - } - recv_handle( - &mut buf, - start + len, - peer_ip, - &nat_map, - &context, - ¤t_device, - &client_cipher, - ); - } - } -} - -fn recv_handle( - buf: &mut [u8], - data_len: usize, - peer_ip: Ipv4Addr, - nat_map: &Mutex>, - context: &ChannelContext, - current_device: &AtomicCell, - client_cipher: &Cipher, -) { - match IpV4Packet::new(&mut buf[12..data_len]) { - Ok(mut ipv4_packet) => match icmp::IcmpPacket::new(ipv4_packet.payload()) { - Ok(icmp_packet) => match icmp_packet.header_other() { - HeaderOther::Identifier(id, seq) => { - if let Some(dest_ip) = nat_map.lock().get(&(peer_ip, id, seq)).cloned() { - ipv4_packet.set_destination_ip(dest_ip); - ipv4_packet.update_checksum(); - - let current_device = current_device.load(); - let virtual_ip = current_device.virtual_ip(); - - let mut net_packet = NetPacket::new0(data_len, buf).unwrap(); - net_packet.set_default_version(); - net_packet.set_protocol(protocol::Protocol::IpTurn); - net_packet.set_transport_protocol( - protocol::ip_turn_packet::Protocol::Ipv4.into(), - ); - net_packet.first_set_ttl(MAX_TTL); - net_packet.set_source(virtual_ip); - net_packet.set_destination(dest_ip); - if let Err(e) = client_cipher.encrypt_ipv4(&mut net_packet) { - log::warn!("加密失败:{}", e); - return; - } - if let Err(e) = context.send_ipv4_by_id( - &net_packet, - &dest_ip, - current_device.connect_server, - current_device.status.online(), - ) { - log::warn!("发送到目标失败:{}", e); - } - } - } - h => { - log::debug!("不支持的icmp代理 {:?},{:?}", peer_ip, h) - } - }, - Err(e) => { - log::warn!("icmp {:?}", e) - } - }, - Err(e) => { - log::warn!("icmp {:?}", e) - } - } -} - -/// icmp用Identifier来区分,没有Identifier的一律不转发 -impl ProxyHandler for IcmpProxy { - fn recv_handle( - &self, - ipv4: &mut IpV4Packet<&mut [u8]>, - source: Ipv4Addr, - destination: Ipv4Addr, - ) -> io::Result { - if ipv4.offset() != 0 || ipv4.flags() & 1 == 1 { - // ip分片的直接丢弃 - return Ok(true); - } - let dest_ip = ipv4.destination_ip(); - //转发到代理目标地址 - let icmp_packet = icmp::IcmpPacket::new(ipv4.payload())?; - match icmp_packet.header_other() { - HeaderOther::Identifier(id, seq) => { - self.nat_map.lock().insert((dest_ip, id, seq), source); - self.icmp_socket.send_to( - ipv4.payload(), - SocketAddr::from(SocketAddrV4::new(dest_ip, 0)), - )?; - } - header_other => { - log::warn!( - "不支持的ip代理Icmp协议:{}->{}->{},{:?}", - source, - destination, - dest_ip, - header_other - ); - } - } - Ok(true) - } - - fn send_handle(&self, _ipv4: &mut IpV4Packet<&mut [u8]>) -> io::Result<()> { - Ok(()) - } -} diff --git a/vnt/src/ip_proxy/mod.rs b/vnt/src/ip_proxy/mod.rs deleted file mode 100644 index 6f16916..0000000 --- a/vnt/src/ip_proxy/mod.rs +++ /dev/null @@ -1,126 +0,0 @@ -use std::net::Ipv4Addr; -use std::sync::Arc; -use std::{io, thread}; - -use crossbeam_utils::atomic::AtomicCell; - -use packet::ip::ipv4; -use packet::ip::ipv4::packet::IpV4Packet; - -use crate::channel::context::ChannelContext; -use crate::cipher::Cipher; -use crate::handle::CurrentDeviceInfo; -#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] -use crate::ip_proxy::icmp_proxy::IcmpProxy; -use crate::ip_proxy::tcp_proxy::TcpProxy; -use crate::ip_proxy::udp_proxy::UdpProxy; -use crate::util::StopManager; - -#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] -pub mod icmp_proxy; -pub mod tcp_proxy; -pub mod udp_proxy; - -pub trait ProxyHandler { - fn recv_handle( - &self, - ipv4: &mut IpV4Packet<&mut [u8]>, - source: Ipv4Addr, - destination: Ipv4Addr, - ) -> io::Result; - fn send_handle(&self, ipv4: &mut IpV4Packet<&mut [u8]>) -> io::Result<()>; -} - -#[derive(Clone)] -pub struct IpProxyMap { - #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] - icmp_proxy: IcmpProxy, - tcp_proxy: TcpProxy, - udp_proxy: UdpProxy, -} - -pub fn init_proxy( - context: ChannelContext, - stop_manager: StopManager, - current_device: Arc>, - client_cipher: Cipher, -) -> anyhow::Result { - let runtime = tokio::runtime::Builder::new_multi_thread() - .enable_all() - .thread_name("ipProxy") - .build()?; - let proxy_map = runtime.block_on(init_proxy0(context, current_device, client_cipher))?; - let (sender, receiver) = tokio::sync::oneshot::channel::<()>(); - let worker = stop_manager.add_listener("ipProxy".into(), move || { - let _ = sender.send(()); - })?; - thread::Builder::new() - .name("ipProxy".into()) - .spawn(move || { - runtime.block_on(async { - let _ = receiver.await; - }); - runtime.shutdown_background(); - drop(worker); - })?; - - return Ok(proxy_map); -} - -async fn init_proxy0( - context: ChannelContext, - _current_device: Arc>, - _client_cipher: Cipher, -) -> anyhow::Result { - 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, &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"))] - icmp_proxy, - tcp_proxy, - udp_proxy, - }) -} - -impl ProxyHandler for IpProxyMap { - fn recv_handle( - &self, - ipv4: &mut IpV4Packet<&mut [u8]>, - source: Ipv4Addr, - destination: Ipv4Addr, - ) -> io::Result { - match ipv4.protocol() { - ipv4::protocol::Protocol::Tcp => self.tcp_proxy.recv_handle(ipv4, source, destination), - ipv4::protocol::Protocol::Udp => self.udp_proxy.recv_handle(ipv4, source, destination), - #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] - ipv4::protocol::Protocol::Icmp => { - self.icmp_proxy.recv_handle(ipv4, source, destination) - } - _ => { - log::warn!( - "不支持的ip代理ipv4协议{:?}:{}->{}->{}", - ipv4.protocol(), - source, - destination, - ipv4.destination_ip() - ); - Ok(false) - } - } - } - - fn send_handle(&self, ipv4: &mut IpV4Packet<&mut [u8]>) -> io::Result<()> { - match ipv4.protocol() { - ipv4::protocol::Protocol::Tcp => self.tcp_proxy.send_handle(ipv4), - ipv4::protocol::Protocol::Udp => self.udp_proxy.send_handle(ipv4), - #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] - ipv4::protocol::Protocol::Icmp => self.icmp_proxy.send_handle(ipv4), - _ => Ok(()), - } - } -} diff --git a/vnt/src/ip_proxy/tcp_proxy.rs b/vnt/src/ip_proxy/tcp_proxy.rs deleted file mode 100644 index 333f8df..0000000 --- a/vnt/src/ip_proxy/tcp_proxy.rs +++ /dev/null @@ -1,160 +0,0 @@ -use anyhow::Context; -use std::net::{Ipv4Addr, SocketAddrV4}; -use std::sync::Arc; -use std::time::Duration; -use std::{collections::HashMap, io, net::SocketAddr}; - -use parking_lot::Mutex; -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; - -#[derive(Clone)] -pub struct TcpProxy { - port: u16, - nat_map: Arc>>, -} - -impl TcpProxy { - pub async fn new(default_interface: LocalInterface) -> anyhow::Result { - let nat_map: Arc>> = - Arc::new(Mutex::new(HashMap::with_capacity(16))); - let tcp_listener = TcpListener::bind(format!("0.0.0.0:{}", 0)) - .await - .context("TcpProxy bind failed")?; - let port = tcp_listener.local_addr()?.port(); - { - let nat_map = nat_map.clone(); - tokio::spawn(tcp_proxy(tcp_listener, nat_map, default_interface)); - } - Ok(Self { port, nat_map }) - } -} - -impl ProxyHandler for TcpProxy { - fn recv_handle( - &self, - ipv4: &mut IpV4Packet<&mut [u8]>, - source: Ipv4Addr, - destination: Ipv4Addr, - ) -> io::Result { - let dest_ip = ipv4.destination_ip(); - //转发到代理目标地址 - let mut tcp_packet = TcpPacket::new(source, destination, ipv4.payload_mut())?; - let source_port = tcp_packet.source_port(); - let dest_port = tcp_packet.destination_port(); - tcp_packet.set_destination_port(self.port); - tcp_packet.update_checksum(); - ipv4.set_destination_ip(destination); - ipv4.update_checksum(); - let key = SocketAddrV4::new(source, source_port); - self.nat_map - .lock() - .insert(key, SocketAddrV4::new(dest_ip, dest_port)); - Ok(false) - } - - fn send_handle(&self, ipv4: &mut IpV4Packet<&mut [u8]>) -> io::Result<()> { - let src_ip = ipv4.source_ip(); - let dest_ip = ipv4.destination_ip(); - let dest_addr = { - let tcp_packet = TcpPacket::new(src_ip, dest_ip, ipv4.payload_mut())?; - SocketAddrV4::new(dest_ip, tcp_packet.destination_port()) - }; - if let Some(source_addr) = self.nat_map.lock().get(&dest_addr) { - let source_ip = *source_addr.ip(); - let mut tcp_packet = TcpPacket::new(source_ip, dest_ip, ipv4.payload_mut())?; - tcp_packet.set_source_port(source_addr.port()); - tcp_packet.update_checksum(); - ipv4.set_source_ip(source_ip); - ipv4.update_checksum(); - } - Ok(()) - } -} - -async fn tcp_proxy( - tcp_listener: TcpListener, - nat_map: Arc>>, - 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(), - &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 { - log::warn!("tcp代理异常: 来源:{},未找到目标", sender_addr); - } - } - SocketAddr::V6(_) => {} - }, - Err(e) => { - log::warn!("tcp代理监听:{:?}", e); - } - } - } -} -/// 优先使用来源端口建立tcp连接 -async fn tcp_connect( - src_port: u16, - addr: SocketAddr, - default_interface: &LocalInterface, -) -> anyhow::Result { - 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(true); - let tcp_stream = tokio::time::timeout(Duration::from_secs(5), socket.connect(addr)) - .await - .with_context(|| format!("TCP connection timeout {}", addr))? - .with_context(|| format!("TCP connection target failed {}", addr))?; - Ok(tcp_stream) -} - -async fn proxy( - sender_addr: SocketAddrV4, - dest_addr: SocketAddrV4, - client: TcpStream, - server: TcpStream, -) { - let (mut client_read, mut client_write) = client.into_split(); - let (mut server_read, mut server_write) = server.into_split(); - tokio::spawn(async move { - if let Err(e) = tokio::io::copy(&mut client_read, &mut server_write).await { - log::warn!("client tcp proxy {}->{},{:?}", sender_addr, dest_addr, e); - } - }); - if let Err(e) = tokio::io::copy(&mut server_read, &mut client_write).await { - log::warn!("server tcp proxy {}->{},{:?}", sender_addr, dest_addr, e); - } -} diff --git a/vnt/src/ip_proxy/udp_proxy.rs b/vnt/src/ip_proxy/udp_proxy.rs deleted file mode 100644 index 591330a..0000000 --- a/vnt/src/ip_proxy/udp_proxy.rs +++ /dev/null @@ -1,192 +0,0 @@ -use anyhow::Context; -use crossbeam_utils::atomic::AtomicCell; -use std::net::{Ipv4Addr, SocketAddrV4}; -use std::sync::Arc; -use std::time::{Duration, Instant}; -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; - -#[derive(Clone)] -pub struct UdpProxy { - port: u16, - nat_map: Arc>>, -} - -impl UdpProxy { - pub async fn new(default_interface: LocalInterface) -> anyhow::Result { - let nat_map: Arc>> = - Arc::new(Mutex::new(HashMap::with_capacity(16))); - let udp = UdpSocket::bind(format!("0.0.0.0:{}", 0)) - .await - .context("UdpProxy bind failed")?; - let port = udp.local_addr()?.port(); - { - let nat_map = nat_map.clone(); - tokio::spawn(async move { - if let Err(e) = udp_proxy(udp, nat_map, default_interface).await { - log::warn!("udp_proxy:{:?}", e); - } - }); - } - Ok(Self { port, nat_map }) - } -} - -impl ProxyHandler for UdpProxy { - fn recv_handle( - &self, - ipv4: &mut IpV4Packet<&mut [u8]>, - source: Ipv4Addr, - destination: Ipv4Addr, - ) -> io::Result { - let dest_ip = ipv4.destination_ip(); - //转发到代理目标地址 - let mut udp_packet = UdpPacket::new(source, destination, ipv4.payload_mut())?; - let source_port = udp_packet.source_port(); - let dest_port = udp_packet.destination_port(); - udp_packet.set_destination_port(self.port); - udp_packet.update_checksum(); - ipv4.set_destination_ip(destination); - ipv4.update_checksum(); - let key = SocketAddrV4::new(source, source_port); - self.nat_map - .lock() - .insert(key.into(), SocketAddrV4::new(dest_ip, dest_port).into()); - Ok(false) - } - - fn send_handle(&self, ipv4: &mut IpV4Packet<&mut [u8]>) -> io::Result<()> { - let src_ip = ipv4.source_ip(); - let dest_ip = ipv4.destination_ip(); - let dest_addr = { - let udp_packet = UdpPacket::new(src_ip, dest_ip, ipv4.payload_mut())?; - SocketAddrV4::new(dest_ip, udp_packet.destination_port()) - }; - if let Some(source_addr) = self.nat_map.lock().get(&dest_addr) { - let source_ip = *source_addr.ip(); - let mut udp_packet = UdpPacket::new(source_ip, dest_ip, ipv4.payload_mut())?; - udp_packet.set_source_port(source_addr.port()); - udp_packet.update_checksum(); - ipv4.set_source_ip(source_ip); - ipv4.update_checksum(); - } - Ok(()) - } -} - -async fn udp_proxy( - udp: UdpSocket, - nat_map: Arc>>, - default_interface: LocalInterface, -) -> anyhow::Result<()> { - let mut buf = [0u8; 65536]; - - let inner_map: Arc, Arc>)>>> = - Arc::new(Mutex::new(HashMap::with_capacity(64))); - let udp_socket = Arc::new(udp); - loop { - 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, - &default_interface, - ) - .await - { - log::warn!("udp proxy {} {:?}", sender_addr, e); - } - } - SocketAddr::V6(_) => {} - }, - Err(e) => { - log::warn!("udp代理异常:{:?}", e); - } - }; - } -} - -async fn udp_proxy0( - buf: &[u8], - sender_addr: SocketAddrV4, - inner_map: &Arc, Arc>)>>>, - map: &Arc>>, - udp_socket: &Arc, - default_interface: &LocalInterface, -) -> anyhow::Result<()> { - let option = inner_map.lock().get(&sender_addr).cloned(); - if let Some((udp, time)) = option { - time.store(Instant::now()); - udp.send(buf).await?; - } else { - let option = map.lock().get(&sender_addr).cloned(); - if let Some(dest_addr) = option { - //先使用相同的端口,冲突了再随机端口 - 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); - let inner_map = inner_map.clone(); - let time = Arc::new(AtomicCell::new(Instant::now())); - inner_map - .lock() - .insert(sender_addr, (peer_udp_socket.clone(), time.clone())); - let udp_socket = udp_socket.clone(); - let map = map.clone(); - tokio::spawn(async move { - let mut buf = [0u8; 65536]; - loop { - match tokio::time::timeout( - Duration::from_secs(600), - peer_udp_socket.recv(&mut buf), - ) - .await - { - Ok(rs) => match rs { - Ok(len) => match udp_socket.send_to(&buf[..len], sender_addr).await { - Ok(_) => {} - Err(e) => { - log::warn!("udp proxy {}->{} {:?}", sender_addr, dest_addr, e); - break; - } - }, - Err(e) => { - log::warn!("udp proxy {}->{} {:?}", sender_addr, dest_addr, e); - - break; - } - }, - Err(_) => { - if time.load().elapsed() > Duration::from_secs(580) { - //超时关闭 - log::warn!("udp proxy timeout {}->{}", sender_addr, dest_addr); - break; - } - } - } - } - inner_map.lock().remove(&sender_addr); - map.lock().remove(&sender_addr); - }); - } - } - Ok(()) -} diff --git a/vnt/src/lib.rs b/vnt/src/lib.rs deleted file mode 100644 index 4de7596..0000000 --- a/vnt/src/lib.rs +++ /dev/null @@ -1,31 +0,0 @@ -pub const VNT_VERSION: &'static str = env!("CARGO_PKG_VERSION"); - -pub mod channel; -pub mod cipher; -pub mod core; -mod external_route; -pub mod handle; -#[cfg(feature = "ip_proxy")] -mod ip_proxy; -pub mod nat; -#[cfg(feature = "port_mapping")] -mod port_mapping; -mod proto; -pub mod protocol; -mod tun_tap_device; -pub use tun_tap_device::*; -pub mod util; - -pub use handle::callback::*; - -pub mod compression; -pub use packet; - -pub(crate) fn ignore_io_interrupted(e: std::io::Error) -> std::io::Result<()> { - if e.kind() == std::io::ErrorKind::Interrupted { - log::warn!("ignore_io_interrupted"); - Ok(()) - } else { - Err(e) - } -} diff --git a/vnt/src/nat/mod.rs b/vnt/src/nat/mod.rs deleted file mode 100644 index 628eddf..0000000 --- a/vnt/src/nat/mod.rs +++ /dev/null @@ -1,354 +0,0 @@ -use anyhow::{anyhow, Context}; -use std::io; -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, ToSocketAddrs}; -use std::net::{SocketAddr, UdpSocket}; -use std::sync::Arc; -use std::time::{Duration, Instant}; - -use crossbeam_utils::atomic::AtomicCell; -use parking_lot::Mutex; -use rand::prelude::SliceRandom; -use rand::Rng; - -use crate::channel::punch::{NatInfo, NatType, PunchModel}; -use crate::channel::socket::LocalInterface; -#[cfg(feature = "upnp")] -use crate::util::UPnP; - -mod stun; - -pub fn local_ipv4_() -> io::Result { - let socket = UdpSocket::bind("0.0.0.0:0")?; - socket.connect("8.8.8.8:80")?; - let addr = socket.local_addr()?; - match addr.ip() { - IpAddr::V4(ip) => Ok(ip), - IpAddr::V6(_) => Ok(Ipv4Addr::UNSPECIFIED), - } -} - -pub fn local_ipv4() -> Option { - match local_ipv4_() { - Ok(ipv4) => Some(ipv4), - Err(e) => { - log::warn!("获取ipv4失败:{:?}", e); - None - } - } -} - -pub fn local_ipv6_() -> io::Result { - let socket = UdpSocket::bind("[::]:0")?; - socket.connect("[2001:4860:4860:0000:0000:0000:0000:8888]:80")?; - let addr = socket.local_addr()?; - match addr.ip() { - IpAddr::V4(_) => Ok(Ipv6Addr::UNSPECIFIED), - IpAddr::V6(ip) => Ok(ip), - } -} - -pub fn local_ipv6() -> Option { - match local_ipv6_() { - Ok(ipv6) => { - if is_ipv6_global(&ipv6) { - return Some(ipv6); - } - } - Err(e) => { - log::warn!("获取ipv6失败:{:?}", e); - } - } - None -} - -pub const fn is_ipv4_global(ipv4: &Ipv4Addr) -> bool { - !(ipv4.octets()[0] == 0 // "This network" - || ipv4.is_private() - || ipv4.octets()[0] == 100 && (ipv4.octets()[1] & 0b1100_0000 == 0b0100_0000)//ipv4.is_shared() - || ipv4.is_loopback() - || ipv4.is_link_local() - // addresses reserved for future protocols (`192.0.0.0/24`) - // .9 and .10 are documented as globally reachable so they're excluded - || ( - ipv4.octets()[0] == 192 && ipv4.octets()[1] == 0 && ipv4.octets()[2] == 0 - && ipv4.octets()[3] != 9 && ipv4.octets()[3] != 10 - ) - || ipv4.is_documentation() - || ipv4.octets()[0] == 198 && (ipv4.octets()[1] & 0xfe) == 18//ipv4.is_benchmarking() - || ipv4.octets()[0] & 240 == 240 && !ipv4.is_broadcast()//ipv4.is_reserved() - || ipv4.is_broadcast()) -} - -pub const fn is_ipv6_global(ipv6addr: &Ipv6Addr) -> bool { - !(ipv6addr.is_unspecified() - || ipv6addr.is_loopback() - // IPv4-mapped Address (`::ffff:0:0/96`) - || matches!(ipv6addr.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) - // IPv4-IPv6 Translat. (`64:ff9b:1::/48`) - || matches!(ipv6addr.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) - // Discard-Only Address Block (`100::/64`) - || matches!(ipv6addr.segments(), [0x100, 0, 0, 0, _, _, _, _]) - // IETF Protocol Assignments (`2001::/23`) - || (matches!(ipv6addr.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) - && !( - // Port Control Protocol Anycast (`2001:1::1`) - u128::from_be_bytes(ipv6addr.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 - // Traversal Using Relays around NAT Anycast (`2001:1::2`) - || u128::from_be_bytes(ipv6addr.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 - // AMT (`2001:3::/32`) - || matches!(ipv6addr.segments(), [0x2001, 3, _, _, _, _, _, _]) - // AS112-v6 (`2001:4:112::/48`) - || matches!(ipv6addr.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) - // ORCHIDv2 (`2001:20::/28`) - || matches!(ipv6addr.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) - )) - || (ipv6addr.segments()[0] == 0x2001) && (ipv6addr.segments()[1] == 0xdb8)//ipv6addr.is_documentation() - || (ipv6addr.segments()[0] & 0xfe00) == 0xfc00//ipv6addr.is_unique_local() - || (ipv6addr.segments()[0] & 0xffc0) == 0xfe80) //ipv6addr.is_unicast_link_local()) -} - -#[derive(Clone)] -pub struct NatTest { - stun_server: Vec, - info: Arc>, - time: Arc>, - udp_ports: Vec, - tcp_port: u16, - #[cfg(feature = "upnp")] - upnp: UPnP, - pub(crate) update_local_ipv4: bool, -} - -impl NatTest { - pub fn new( - _channel_num: usize, - stun_server: Vec, - local_ipv4: Option, - ipv6: Option, - udp_ports: Vec, - tcp_port: u16, - update_local_ipv4: bool, - punch_model: PunchModel, - ) -> NatTest { - let ports = vec![0; udp_ports.len()]; - let nat_info = NatInfo::new( - Vec::new(), - ports, - 0, - local_ipv4, - ipv6, - udp_ports.clone(), - tcp_port, - 0, - NatType::Cone, - punch_model, - ); - let info = Arc::new(Mutex::new(nat_info)); - #[cfg(feature = "upnp")] - let upnp = UPnP::default(); - #[cfg(feature = "upnp")] - for port in &udp_ports { - upnp.add_udp_port(*port); - } - #[cfg(feature = "upnp")] - upnp.add_tcp_port(tcp_port); - let instant = Instant::now(); - NatTest { - stun_server, - info, - time: Arc::new(AtomicCell::new( - instant - .checked_sub(Duration::from_secs(100)) - .unwrap_or(instant), - )), - udp_ports, - tcp_port, - #[cfg(feature = "upnp")] - upnp, - update_local_ipv4, - } - } - pub fn can_update(&self) -> bool { - let last = self.time.load(); - last.elapsed() > Duration::from_secs(10) - && self.time.compare_exchange(last, Instant::now()).is_ok() - } - - pub fn nat_info(&self) -> NatInfo { - self.info.lock().clone() - } - pub fn is_local_udp(&self, ipv4: Ipv4Addr, port: u16) -> bool { - for x in &self.udp_ports { - if x == &port { - let guard = self.info.lock(); - if let Some(ip) = guard.local_ipv4 { - if ipv4 == ip { - return true; - } - } - break; - } - } - false - } - pub fn is_local_tcp(&self, ipv4: Ipv4Addr, port: u16) -> bool { - if self.tcp_port == port { - let guard = self.info.lock(); - if let Some(ip) = guard.local_ipv4 { - if ipv4 == ip { - return true; - } - } - } - false - } - pub fn is_local_address(&self, is_tcp: bool, addr: SocketAddr) -> bool { - let port = addr.port(); - let check_ip = || { - let guard = self.info.lock(); - match addr.ip() { - IpAddr::V4(ipv4) => { - if let Some(ip) = guard.local_ipv4 { - if ipv4 == ip { - return true; - } - } - } - IpAddr::V6(ipv6) => { - if let Some(ip) = guard.ipv6 { - if ipv6 == ip { - return true; - } - } - } - } - false - }; - if is_tcp { - if self.tcp_port == port { - return check_ip(); - } - } else { - for x in &self.udp_ports { - if x == &port { - return check_ip(); - } - } - } - false - } - pub fn update_addr(&self, index: usize, ip: Ipv4Addr, port: u16) -> bool { - 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, - ipv6: Option, - default_interface: &LocalInterface, - ) -> anyhow::Result { - let mut stun_server = self.stun_server.clone(); - if stun_server.len() > 5 { - stun_server.shuffle(&mut rand::thread_rng()); - stun_server.truncate(5); - log::info!("stun_server truncate {:?}", 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"))? - } - let mut guard = self.info.lock(); - guard.nat_type = nat_type; - guard.public_ips = public_ips; - guard.public_port_range = port_range; - if local_ipv4.is_some() { - guard.local_ipv4 = local_ipv4; - } - guard.ipv6 = ipv6; - - Ok(guard.clone()) - } - #[cfg(feature = "upnp")] - pub fn reset_upnp(&self) { - let local_ipv4 = self.info.lock().local_ipv4.clone(); - if let Some(local_ipv4) = local_ipv4 { - self.upnp.reset(local_ipv4) - } - } - pub fn send_data(&self) -> anyhow::Result<(Vec, SocketAddr)> { - let len = self.stun_server.len(); - let stun_server = if len == 1 { - &self.stun_server[0] - } else { - let index = rand::thread_rng().gen_range(0..self.stun_server.len()); - &self.stun_server[index] - }; - let addr = stun_server - .to_socket_addrs()? - .next() - .with_context(|| format!("stun error {:?}", stun_server))?; - Ok((stun::send_stun_request(), addr)) - } - pub fn recv_data( - &self, - index: usize, - source_addr: SocketAddr, - buf: &[u8], - ) -> anyhow::Result { - if buf[0] == 0x01 && buf[1] == 0x01 { - if let Some(addr) = stun::recv_stun_response(buf) { - if let Err(e) = self.recv_data_(index, source_addr, addr) { - log::warn!("{:?}", e); - } - } - Ok(true) - } else { - Ok(false) - } - } - fn recv_data_( - &self, - index: usize, - source_addr: SocketAddr, - addr: SocketAddr, - ) -> anyhow::Result<()> { - if let SocketAddr::V4(addr) = addr { - let mut check_fail = true; - let source_ip = match source_addr.ip() { - IpAddr::V4(ip) => ip, - IpAddr::V6(ip) => { - if let Some(ip) = ip.to_ipv4() { - ip - } else { - return Ok(()); - } - } - }; - 'a: for stun_server in &self.stun_server { - for x in stun_server.to_socket_addrs()? { - if source_addr.port() == x.port() { - if let IpAddr::V4(ip) = x.ip() { - if ip == source_ip { - check_fail = false; - break 'a; - } - }; - } - } - } - if !check_fail { - if is_ipv4_global(addr.ip()) { - if self.update_addr(index, *addr.ip(), addr.port()) { - log::info!("回应地址{:?},来源stun {:?}", addr, source_addr) - } - } - } - } - Ok(()) - } -} diff --git a/vnt/src/nat/stun.rs b/vnt/src/nat/stun.rs deleted file mode 100644 index 6fcdb9b..0000000 --- a/vnt/src/nat/stun.rs +++ /dev/null @@ -1,224 +0,0 @@ -use std::collections::HashSet; -use std::io; -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, - default_interface: &LocalInterface, -) -> anyhow::Result<(NatType, Vec, 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, default_interface) { - Ok((nat_type_t, ip_list_t, port_range_t)) => { - if nat_type_t == NatType::Symmetric { - nat_type = NatType::Symmetric; - } - for x in ip_list_t { - hash_set.insert(x); - } - if port_range < port_range_t { - port_range = port_range_t; - } - } - Err(e) => { - log::warn!("{:?}", e); - } - } - } - Ok((nat_type, hash_set.into_iter().collect(), port_range)) -} - -pub fn stun_test_nat0( - stun_servers: Vec, - default_interface: &LocalInterface, -) -> anyhow::Result<(NatType, Vec, 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; - let mut max_port = 0; - let mut hash_set = HashSet::new(); - let mut pub_addrs = HashSet::new(); - for x in &stun_servers { - match test_nat(&udp, x) { - Ok(addr) => { - pub_addrs.extend(addr); - } - Err(e) => { - log::warn!("stun {} error {:?} ", x, e); - } - } - } - if pub_addrs.len() > 1 { - nat_type = NatType::Symmetric; - } - for addr in &pub_addrs { - if let SocketAddr::V4(addr) = addr { - hash_set.insert(*addr.ip()); - if min_port > addr.port() { - min_port = addr.port() - } - if max_port < addr.port() { - max_port = addr.port() - } - } - } - if hash_set.is_empty() { - Ok((nat_type, vec![], 0)) - } else { - Ok(( - nat_type, - hash_set.into_iter().collect(), - max_port - min_port, - )) - } -} - -fn test_nat(udp: &UdpSocket, stun_server: &String) -> io::Result> { - udp.connect(stun_server)?; - let tid = rand::thread_rng().next_u64() as u128; - let mut addr = HashSet::new(); - let (mapped_addr1, changed_addr1) = test_nat_(&udp, stun_server, true, true, tid)?; - if mapped_addr1.is_ipv4() { - addr.insert(mapped_addr1); - } - if let Some(changed_addr1) = changed_addr1 { - if udp.connect(changed_addr1).is_ok() { - match test_nat_(&udp, stun_server, false, false, tid + 1) { - Ok((mapped_addr2, _)) => { - if mapped_addr2.is_ipv4() { - addr.insert(mapped_addr1); - } - } - Err(e) => { - log::warn!("stun {} error {:?} ", stun_server, e); - } - } - } - } - log::info!( - "stun {} mapped_addr {:?} changed_addr {:?}", - stun_server, - addr, - changed_addr1, - ); - - Ok(addr) -} - -fn test_nat_( - udp: &UdpSocket, - stun_server: &String, - change_ip: bool, - change_port: bool, - tid: u128, -) -> io::Result<(SocketAddr, Option)> { - for _ in 0..2 { - let mut buf = [0u8; 28]; - let mut msg = stun_format::MsgBuilder::from(buf.as_mut_slice()); - msg.typ(stun_format::MsgType::BindingRequest); - msg.tid(tid); - msg.add_attr(Attr::ChangeRequest { - change_ip, - change_port, - }); - udp.send(msg.as_bytes())?; - let mut buf = [0; 10240]; - let (len, _addr) = match udp.recv_from(&mut buf) { - Ok(rs) => rs, - Err(e) => { - log::warn!("stun {} error {:?}", stun_server, e); - continue; - } - }; - let msg = stun_format::Msg::from(&buf[..len]); - let mut mapped_addr = None; - let mut changed_addr = None; - for x in msg.attrs_iter() { - match x { - Attr::MappedAddress(addr) => { - if mapped_addr.is_none() { - let _ = mapped_addr.insert(stun_addr(addr)); - } - } - Attr::ChangedAddress(addr) => { - if changed_addr.is_none() { - let _ = changed_addr.insert(stun_addr(addr)); - } - } - Attr::XorMappedAddress(addr) => { - if mapped_addr.is_none() { - let _ = mapped_addr.insert(stun_addr(addr)); - } - } - _ => {} - } - if changed_addr.is_some() && mapped_addr.is_some() { - return Ok((mapped_addr.unwrap(), changed_addr)); - } - } - if let Some(addr) = mapped_addr { - return Ok((addr, changed_addr)); - } - } - Err(io::Error::new(io::ErrorKind::Other, "stun response err")) -} - -fn stun_addr(addr: stun_format::SocketAddr) -> SocketAddr { - match addr { - stun_format::SocketAddr::V4(ip, port) => { - SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from(ip), port)) - } - stun_format::SocketAddr::V6(ip, port) => { - SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::from(ip), port, 0, 0)) - } - } -} - -const TAG: u128 = 1827549368 << 64; - -pub fn send_stun_request() -> Vec { - let mut buf = [0u8; 28]; - let mut msg = stun_format::MsgBuilder::from(buf.as_mut_slice()); - msg.typ(stun_format::MsgType::BindingRequest); - let id = rand::thread_rng().next_u64() as u128; - msg.tid(id | TAG); - msg.add_attr(Attr::ChangeRequest { - change_ip: false, - change_port: false, - }); - msg.as_bytes().to_vec() -} - -pub fn recv_stun_response(buf: &[u8]) -> Option { - let msg = stun_format::Msg::from(buf); - if let Some(tid) = msg.tid() { - if tid & TAG != TAG { - return None; - } - } - for x in msg.attrs_iter() { - match x { - Attr::MappedAddress(addr) => { - return Some(stun_addr(addr)); - } - Attr::XorMappedAddress(addr) => { - return Some(stun_addr(addr)); - } - _ => {} - } - } - None -} diff --git a/vnt/src/port_mapping/mod.rs b/vnt/src/port_mapping/mod.rs deleted file mode 100644 index dccb8cd..0000000 --- a/vnt/src/port_mapping/mod.rs +++ /dev/null @@ -1,101 +0,0 @@ -use std::net::SocketAddr; -use std::str::FromStr; -use std::thread; - -use anyhow::Context; - -use crate::util::StopManager; - -mod tcp_mapping; - -mod udp_mapping; - -pub fn convert(vec: Vec) -> anyhow::Result> { - let mut rs = Vec::with_capacity(vec.len()); - for x in vec { - let string = x.trim().to_lowercase(); - if let Some(udp_mapping) = string.strip_prefix("udp:") { - let mut split = udp_mapping.split("-"); - let bind_addr = split.next().with_context(|| { - format!( - "udp_mapping error {:?},eg: udp:127.0.0.1:80-10.26.0.10:8080", - x - ) - })?; - let bind_addr = SocketAddr::from_str(bind_addr) - .with_context(|| format!("udp_mapping error {}", bind_addr))?; - let dest = split.next().with_context(|| { - format!( - "udp_mapping error {:?},eg: udp:127.0.0.1:80-10.26.0.10:8080", - x - ) - })?; - rs.push((false, bind_addr, dest.to_string())); - continue; - } - if let Some(tcp_mapping) = string.strip_prefix("tcp:") { - let mut split = tcp_mapping.split("-"); - let bind_addr = split.next().with_context(|| { - format!( - "tcp_mapping error {:?},eg: tcp:127.0.0.1:80-10.26.0.10:8080", - x - ) - })?; - let bind_addr = SocketAddr::from_str(bind_addr) - .with_context(|| format!("udp_mapping error {}", bind_addr))?; - let dest = split.next().with_context(|| { - format!( - "tcp_mapping error {:?},eg: tcp:127.0.0.1:80-10.26.0.10:8080", - x - ) - })?; - rs.push((true, bind_addr, dest.to_string())); - continue; - } - Err(anyhow::anyhow!( - "port_mapping error {:?},eg: tcp:127.0.0.1:80-10.26.0.10:8080", - x - ))?; - } - Ok(rs) -} -pub fn start_port_mapping( - stop_manager: StopManager, - vec: Vec<(bool, SocketAddr, String)>, -) -> anyhow::Result<()> { - if vec.is_empty() { - return Ok(()); - } - - let (sender, receiver) = tokio::sync::oneshot::channel::<()>(); - let worker = stop_manager.add_listener("portMapping".into(), move || { - let _ = sender.send(()); - })?; - 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; - }); - runtime.shutdown_background(); - drop(worker); - })?; - - Ok(()) -} - -async fn start_port_mapping0(vec: Vec<(bool, SocketAddr, String)>) -> anyhow::Result<()> { - for (is_tcp, bind_addr, destination) in vec { - if is_tcp { - tcp_mapping::tcp_mapping(bind_addr, destination).await?; - } else { - udp_mapping::udp_mapping(bind_addr, destination).await?; - } - } - Ok(()) -} diff --git a/vnt/src/port_mapping/tcp_mapping.rs b/vnt/src/port_mapping/tcp_mapping.rs deleted file mode 100644 index 82e780c..0000000 --- a/vnt/src/port_mapping/tcp_mapping.rs +++ /dev/null @@ -1,54 +0,0 @@ -use anyhow::Context; -use std::net::SocketAddr; -use tokio::net::{TcpListener, TcpStream}; - -pub async fn tcp_mapping(bind_addr: SocketAddr, destination: String) -> anyhow::Result<()> { - let tcp_listener = TcpListener::bind(bind_addr) - .await - .with_context(|| format!("TCP binding {:?} failed", bind_addr))?; - tokio::spawn(async move { - if let Err(e) = tcp_mapping_(bind_addr, tcp_listener, destination).await { - log::warn!("tcp_mapping {:?}", e); - } - }); - Ok(()) -} - -async fn tcp_mapping_( - bind_addr: SocketAddr, - tcp_listener: TcpListener, - destination: String, -) -> anyhow::Result<()> { - loop { - let (tcp_stream, _) = tcp_listener.accept().await?; - - let destination = destination.clone(); - tokio::spawn(async move { - if let Err(e) = copy(tcp_stream, &destination).await { - log::warn!("tcp port mapping {}->{} {:?}", bind_addr, destination, e); - } - }); - } -} - -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))?; - let _ = source_tcp.set_nodelay(true); - let _ = dest_tcp.set_nodelay(true); - - let destination = dest_tcp.peer_addr()?; - let (mut client_read, mut client_write) = source_tcp.into_split(); - let (mut server_read, mut server_write) = dest_tcp.into_split(); - tokio::spawn(async move { - if let Err(e) = tokio::io::copy(&mut client_read, &mut server_write).await { - log::warn!("client tcp proxy ->{:},{:?}", destination, e); - } - }); - if let Err(e) = tokio::io::copy(&mut server_read, &mut client_write).await { - log::warn!("server tcp proxy ->{:?},{:?}", destination, e); - } - Ok(()) -} diff --git a/vnt/src/port_mapping/udp_mapping.rs b/vnt/src/port_mapping/udp_mapping.rs deleted file mode 100644 index 56d8893..0000000 --- a/vnt/src/port_mapping/udp_mapping.rs +++ /dev/null @@ -1,107 +0,0 @@ -use anyhow::Context; -use crossbeam_utils::atomic::AtomicCell; -use parking_lot::Mutex; -use std::collections::HashMap; -use std::net::SocketAddr; -use std::sync::Arc; -use std::time::{Duration, Instant}; -use tokio::net::UdpSocket; - -pub async fn udp_mapping(bind_addr: SocketAddr, destination: String) -> anyhow::Result<()> { - let udp = UdpSocket::bind(bind_addr) - .await - .with_context(|| format!("port proxy UDP binding {:?} failed", bind_addr))?; - let udp = Arc::new(udp); - - let inner_map: Arc, Arc>)>>> = - Arc::new(Mutex::new(HashMap::with_capacity(64))); - - tokio::spawn(async move { - let mut buf = [0; 65536]; - loop { - match udp.recv_from(&mut buf).await { - Ok((len, src_addr)) => { - if let Err(e) = - udp_mapping0(&buf[..len], src_addr, &inner_map, &udp, &destination).await - { - log::warn!("udp port mapping {}->{} {:?}", src_addr, destination, e); - } - } - Err(e) => { - log::warn!("port proxy UDP {:?}", e); - } - } - } - }); - Ok(()) -} - -async fn udp_mapping0( - buf: &[u8], - src_addr: SocketAddr, - inner_map: &Arc, Arc>)>>>, - udp_socket: &Arc, - destination: &String, -) -> anyhow::Result<()> { - let option = inner_map.lock().get(&src_addr).cloned(); - if let Some((udp, time)) = option { - time.store(Instant::now()); - udp.send(buf).await?; - } else { - let dest_udp = UdpSocket::bind("0.0.0.0:0").await?; - dest_udp.connect(destination).await?; - dest_udp.send(buf).await?; - let destination_addr = dest_udp.peer_addr()?; - let udp_socket = udp_socket.clone(); - let inner_map = inner_map.clone(); - let dest_udp = Arc::new(dest_udp); - let time = Arc::new(AtomicCell::new(Instant::now())); - inner_map - .lock() - .insert(src_addr, (dest_udp.clone(), time.clone())); - tokio::spawn(async move { - let mut buf = [0u8; 65536]; - loop { - match tokio::time::timeout(Duration::from_secs(600), dest_udp.recv(&mut buf)).await - { - Ok(rs) => match rs { - Ok(len) => match udp_socket.send_to(&buf[..len], src_addr).await { - Ok(_) => {} - Err(e) => { - log::warn!( - "udp port mapping {}->{} {:?}", - src_addr, - destination_addr, - e - ); - break; - } - }, - Err(e) => { - log::warn!( - "udp port mapping {}->{} {:?}", - src_addr, - destination_addr, - e - ); - break; - } - }, - Err(_) => { - if time.load().elapsed() > Duration::from_secs(580) { - //超时关闭 - log::warn!( - "udp port mapping timeout {}->{} ", - src_addr, - destination_addr - ); - break; - } - } - } - } - inner_map.lock().remove(&src_addr); - }); - } - Ok(()) -} diff --git a/vnt/src/protocol/body.rs b/vnt/src/protocol/body.rs deleted file mode 100644 index 07d39c0..0000000 --- a/vnt/src/protocol/body.rs +++ /dev/null @@ -1,593 +0,0 @@ -use std::{fmt, io}; - -pub const ENCRYPTION_RESERVED: usize = 16 + 32 + 12; -pub const AES_GCM_ENCRYPTION_RESERVED: usize = 32; -pub const RSA_ENCRYPTION_RESERVED: usize = 32; - -pub const RANDOM_RESERVED: usize = 4; -pub const FINGER_RESERVED: usize = 12; -pub const TAG_RESERVED: usize = 16; - -/* -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| random(32) | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| finger(32) | -| finger(32) | -| finger(32) | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -*/ -pub trait SecretTail { - fn buffer(&self) -> &[u8]; - fn exist_finger(&self) -> bool; - fn random_buf(&self) -> &[u8] { - let buf = self.buffer(); - let mut end = buf.len(); - if self.exist_finger() { - end -= FINGER_RESERVED; - } - &buf[end - RANDOM_RESERVED..end] - } - fn finger(&self) -> &[u8] { - if self.exist_finger() { - let buf = self.buffer(); - let end = buf.len(); - &buf[end - FINGER_RESERVED..end] - } else { - &[] - } - } -} - -pub trait SecretTailMut: SecretTail { - fn buffer_mut(&mut self) -> &mut [u8]; - fn set_random(&mut self, random: &[u8]) { - let f = self.exist_finger(); - let buf = self.buffer_mut(); - let mut end = buf.len(); - if f { - end -= FINGER_RESERVED; - } - buf[end - RANDOM_RESERVED..end].copy_from_slice(random); - } - fn set_finger(&mut self, finger: &[u8]) -> io::Result<()> { - if self.exist_finger() { - if finger.len() != FINGER_RESERVED { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "finger.len != 12", - )); - } - let buf = self.buffer_mut(); - let end = buf.len(); - buf[end - FINGER_RESERVED..end].copy_from_slice(finger); - Ok(()) - } else { - Err(io::Error::new( - io::ErrorKind::InvalidData, - "not exist finger", - )) - } - } -} - -/* aead加密数据体 - 0 15 31 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 数据体 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | tag(32) | - | tag(32) | - | tag(32) | - | tag(32) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | random(32) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | finger(32) | - | finger(32) | - | finger(32) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - 注:finger用于快速校验数据是否被修改,上层可使用token、协议头参与计算finger, - 确保服务端和客户端都能感知修改(服务端不能解密也能校验指纹) -*/ -pub struct AEADSecretBody { - buffer: B, - exist_finger: bool, -} - -impl> AEADSecretBody { - pub fn new(buffer: B, exist_finger: bool) -> io::Result> { - let len = buffer.as_ref().len(); - let min_len = if exist_finger { - TAG_RESERVED + RANDOM_RESERVED + FINGER_RESERVED - } else { - TAG_RESERVED + RANDOM_RESERVED - }; - // 不能大于udp最大载荷长度 - if len < min_len || len > 65535 - 20 - 8 - 12 { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - format!("AEADSecretBody length overflow {}", len), - )); - } - Ok(AEADSecretBody { - buffer, - exist_finger, - }) - } - pub fn data(&self) -> &[u8] { - let mut end = self.buffer.as_ref().len() - TAG_RESERVED - RANDOM_RESERVED; - if self.exist_finger { - end -= FINGER_RESERVED; - } - &self.buffer.as_ref()[..end] - } - pub fn tag(&self) -> &[u8] { - let mut end = self.buffer.as_ref().len() - RANDOM_RESERVED; - if self.exist_finger { - end -= FINGER_RESERVED; - } - &self.buffer.as_ref()[end - TAG_RESERVED..end] - } -} - -impl> SecretTail for AEADSecretBody { - #[inline] - fn buffer(&self) -> &[u8] { - self.buffer.as_ref() - } - #[inline] - fn exist_finger(&self) -> bool { - self.exist_finger - } -} - -impl + AsMut<[u8]>> SecretTailMut for AEADSecretBody { - #[inline] - fn buffer_mut(&mut self) -> &mut [u8] { - self.buffer.as_mut() - } -} - -impl + AsMut<[u8]>> AEADSecretBody { - /// 数据部分 - pub fn data_mut(&mut self) -> &mut [u8] { - let mut end = self.buffer.as_ref().len() - RANDOM_RESERVED - TAG_RESERVED; - if self.exist_finger { - end -= FINGER_RESERVED; - } - &mut self.buffer.as_mut()[..end] - } - /// 数据和tag部分 - pub fn data_tag_mut(&mut self) -> &mut [u8] { - let mut end = self.buffer.as_ref().len() - RANDOM_RESERVED; - if self.exist_finger { - end -= FINGER_RESERVED; - } - &mut self.buffer.as_mut()[..end] - } - pub fn set_tag(&mut self, tag: &[u8]) -> io::Result<()> { - if tag.len() != 16 { - return Err(io::Error::new(io::ErrorKind::InvalidData, "tag.len != 16")); - } - let mut end = self.buffer.as_ref().len() - RANDOM_RESERVED; - if self.exist_finger { - end -= FINGER_RESERVED; - } - self.buffer.as_mut()[end - TAG_RESERVED..end].copy_from_slice(tag); - Ok(()) - } -} - -/* 带随机数的加密数据体 - 0 15 31 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 数据体 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | random(32) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | finger(32) | - | finger(32) | - | finger(32) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - 注:finger用于快速校验数据是否被修改,上层可使用token、协议头参与计算finger, - 确保服务端和客户端都能感知修改(服务端不能解密也能校验指纹) -*/ -pub struct IVSecretBody { - buffer: B, - exist_finger: bool, -} - -impl> IVSecretBody { - pub fn new(buffer: B, exist_finger: bool) -> io::Result> { - let len = buffer.as_ref().len(); - let min_len = if exist_finger { - FINGER_RESERVED + RANDOM_RESERVED - } else { - RANDOM_RESERVED - }; - // 不能大于udp最大载荷长度 - if len < min_len || len > 65535 - 20 - 8 - 12 { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - format!("IVSecretBody length overflow {}", len), - )); - } - Ok(IVSecretBody { - buffer, - exist_finger, - }) - } - pub fn data(&self) -> &[u8] { - let mut end = self.buffer.as_ref().len() - RANDOM_RESERVED; - if self.exist_finger { - end -= FINGER_RESERVED; - } - &self.buffer.as_ref()[..end] - } -} - -impl + AsMut<[u8]>> IVSecretBody { - pub fn data_mut(&mut self) -> &mut [u8] { - let mut end = self.buffer.as_ref().len() - RANDOM_RESERVED; - if self.exist_finger { - end -= FINGER_RESERVED; - } - &mut self.buffer.as_mut()[..end] - } -} - -impl> SecretTail for IVSecretBody { - #[inline] - fn buffer(&self) -> &[u8] { - self.buffer.as_ref() - } - #[inline] - fn exist_finger(&self) -> bool { - self.exist_finger - } -} - -impl + AsMut<[u8]>> SecretTailMut for IVSecretBody { - #[inline] - fn buffer_mut(&mut self) -> &mut [u8] { - self.buffer.as_mut() - } -} - -/* aes_gcm加密数据体 - 0 15 31 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 数据体 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | random(32) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | tag(32) | - | tag(32) | - | tag(32) | - | tag(32) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | finger(32) | - | finger(32) | - | finger(32) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - 注:finger用于快速校验数据是否被修改,上层可使用token、协议头参与计算finger, - 确保服务端和客户端都能感知修改(服务端不能解密也能校验指纹) -*/ -pub struct SecretBody { - buffer: B, - exist_finger: bool, -} - -impl> SecretBody { - pub fn new(buffer: B, exist_finger: bool) -> io::Result> { - let len = buffer.as_ref().len(); - let min_len = if exist_finger { 32 } else { 32 - 12 }; - // 不能大于udp最大载荷长度 - if len < min_len || len > 65535 - 20 - 8 - 12 { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "SecretBody length overflow", - )); - } - Ok(SecretBody { - buffer, - exist_finger, - }) - } - pub fn random(&self) -> u32 { - let mut end = self.buffer.as_ref().len() - 16; - if self.exist_finger { - end -= 12; - } - u32::from_be_bytes(self.buffer.as_ref()[end - 4..end].try_into().unwrap()) - } - pub fn body(&self) -> &[u8] { - let mut end = self.buffer.as_ref().len() - 16; - if self.exist_finger { - end -= 12; - } - &self.buffer.as_ref()[..end] - } - pub fn tag(&self) -> &[u8] { - let mut end = self.buffer.as_ref().len(); - if self.exist_finger { - end -= 12; - } - &self.buffer.as_ref()[end - 16..end] - } - /// 数据部分+tag部分 - pub fn en_body(&self) -> &[u8] { - let mut end = self.buffer.as_ref().len(); - if self.exist_finger { - end -= 12; - } - &self.buffer.as_ref()[..end] - } - pub fn finger(&self) -> &[u8] { - if self.exist_finger { - let end = self.buffer.as_ref().len(); - &self.buffer.as_ref()[end - 12..end] - } else { - &[] - } - } - pub fn buffer(&self) -> &[u8] { - self.buffer.as_ref() - } -} - -impl + AsMut<[u8]>> SecretBody { - pub fn set_random(&mut self, random: u32) { - let mut end = self.buffer.as_ref().len() - 16; - if self.exist_finger { - end -= 12; - } - self.buffer.as_mut()[end - 4..end].copy_from_slice(&random.to_be_bytes()); - } - - pub fn set_tag(&mut self, tag: &[u8]) -> io::Result<()> { - if tag.len() != 16 { - return Err(io::Error::new(io::ErrorKind::InvalidData, "tag.len != 16")); - } - let mut end = self.buffer.as_ref().len(); - if self.exist_finger { - end -= 12; - } - self.buffer.as_mut()[end - 16..end].copy_from_slice(tag); - Ok(()) - } - pub fn set_finger(&mut self, finger: &[u8]) -> io::Result<()> { - if self.exist_finger { - if finger.len() != 12 { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "finger.len != 12", - )); - } - let end = self.buffer.as_ref().len(); - self.buffer.as_mut()[end - 12..end].copy_from_slice(finger); - Ok(()) - } else { - Err(io::Error::new( - io::ErrorKind::InvalidData, - "not exist finger", - )) - } - } - - /// 数据部分 - pub fn body_mut(&mut self) -> &mut [u8] { - let mut end = self.buffer.as_ref().len() - 16; - if self.exist_finger { - end -= 12; - } - &mut self.buffer.as_mut()[..end] - } - pub fn tag_mut(&mut self) -> &mut [u8] { - let mut end = self.buffer.as_ref().len(); - if self.exist_finger { - end -= 12; - } - &mut self.buffer.as_mut()[end - 16..end] - } - /// 数据部分+tag部分 - pub fn en_body_mut(&mut self) -> &mut [u8] { - let mut end = self.buffer.as_ref().len(); - if self.exist_finger { - end -= 12; - } - &mut self.buffer.as_mut()[..end] - } - pub fn buffer_mut(&mut self) -> &mut [u8] { - self.buffer.as_mut() - } -} - -impl> fmt::Debug for SecretBody { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SecretBody") - .field("random", &self.random()) - .field("body", &self.body()) - .field("tag", &self.tag()) - .field("finger", &self.finger()) - .finish() - } -} -/* aes_cbc加密数据体 - 0 15 31 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 数据体 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | random(32) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | finger(32) | - | finger(32) | - | finger(32) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - 注:finger用于快速校验数据是否被修改,上层可使用token、协议头参与计算finger, - 确保服务端和客户端都能感知修改(服务端不能解密也能校验指纹) -*/ -pub struct AesCbcSecretBody { - buffer: B, - exist_finger: bool, -} - -impl> AesCbcSecretBody { - pub fn new(buffer: B, exist_finger: bool) -> io::Result> { - let len = buffer.as_ref().len(); - let min_len = if exist_finger { 16 } else { 16 - 12 }; - // 不能大于udp最大载荷长度 - if len < min_len || len > 65535 - 20 - 8 - 12 { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "AesCbcSecretBody length overflow", - )); - } - Ok(AesCbcSecretBody { - buffer, - exist_finger, - }) - } - pub fn en_body(&self) -> &[u8] { - let mut end = self.buffer.as_ref().len(); - if self.exist_finger { - end -= 12; - } - &self.buffer.as_ref()[..end] - } - pub fn finger(&self) -> &[u8] { - if self.exist_finger { - let end = self.buffer.as_ref().len(); - &self.buffer.as_ref()[end - 12..end] - } else { - &[] - } - } -} - -impl + AsMut<[u8]>> AesCbcSecretBody { - pub fn set_random(&mut self, random: u32) { - let mut end = self.buffer.as_ref().len(); - if self.exist_finger { - end -= 12; - } - self.buffer.as_mut()[end - 4..end].copy_from_slice(&random.to_be_bytes()); - } - pub fn set_finger(&mut self, finger: &[u8]) -> io::Result<()> { - if self.exist_finger { - if finger.len() != 12 { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "finger.len != 12", - )); - } - let end = self.buffer.as_ref().len(); - self.buffer.as_mut()[end - 12..end].copy_from_slice(finger); - Ok(()) - } else { - Err(io::Error::new( - io::ErrorKind::InvalidData, - "cbc not exist finger", - )) - } - } - pub fn en_body_mut(&mut self) -> &mut [u8] { - let mut end = self.buffer.as_ref().len(); - if self.exist_finger { - end -= 12; - } - &mut self.buffer.as_mut()[..end] - } -} - -/* rsa加密数据体 - 0 15 31 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 数据体(n) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | random(32) | - | random(32) | - | random(32) | - | random(32) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | finger(32) | - | finger(32) | - | finger(32) | - | finger(32) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -*/ -pub struct RsaSecretBody { - buffer: B, -} - -impl> RsaSecretBody { - pub fn new(buffer: B) -> io::Result> { - let len = buffer.as_ref().len(); - // 不能大于udp最大载荷长度 - if len < 32 || len > 65535 - 20 - 8 - 12 { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "length overflow", - )); - } - Ok(RsaSecretBody { buffer }) - } - pub fn data(&self) -> &[u8] { - let end = self.buffer.as_ref().len() - 32; - &self.buffer.as_ref()[..end] - } - pub fn random(&self) -> &[u8] { - let end = self.buffer.as_ref().len() - 16; - &self.buffer.as_ref()[end - 16..end] - } - pub fn body(&self) -> &[u8] { - let end = self.buffer.as_ref().len() - 16; - &self.buffer.as_ref()[..end] - } - pub fn finger(&self) -> &[u8] { - let end = self.buffer.as_ref().len() - 16; - &self.buffer.as_ref()[end..] - } - pub fn buffer(&self) -> &[u8] { - &self.buffer.as_ref() - } -} - -impl + AsMut<[u8]>> RsaSecretBody { - pub fn set_random(&mut self, random: &[u8]) -> io::Result<()> { - if random.len() != 16 { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "random.len != 16", - )); - } - let end = self.buffer.as_ref().len() - 16; - self.buffer.as_mut()[end - 16..end].copy_from_slice(random); - Ok(()) - } - pub fn random_mut(&mut self) -> &mut [u8] { - let end = self.buffer.as_ref().len() - 16; - &mut self.buffer.as_mut()[end - 16..end] - } - pub fn set_finger(&mut self, finger: &[u8]) -> io::Result<()> { - if finger.len() != 16 { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "finger.len != 16", - )); - } - let end = self.buffer.as_ref().len(); - self.buffer.as_mut()[end - 16..end].copy_from_slice(finger); - Ok(()) - } -} diff --git a/vnt/src/protocol/control_packet.rs b/vnt/src/protocol/control_packet.rs deleted file mode 100644 index dbd1bd6..0000000 --- a/vnt/src/protocol/control_packet.rs +++ /dev/null @@ -1,165 +0,0 @@ -use std::net::Ipv4Addr; -use std::{fmt, io}; - -#[derive(Eq, PartialEq, Copy, Clone, Debug)] -pub enum Protocol { - /// ping请求 - /* - 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 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | time | echo | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - Ping, - /* - 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 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | time | echo | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - Pong, - /// 打洞请求 - PunchRequest, - /// 打洞响应 - PunchResponse, - ///获取对端看到的地址 - AddrRequest, - AddrResponse, - Unknown(u8), -} - -impl From for Protocol { - fn from(value: u8) -> Self { - match value { - 1 => Protocol::Ping, - 2 => Protocol::Pong, - 3 => Protocol::PunchRequest, - 4 => Protocol::PunchResponse, - 5 => Protocol::AddrRequest, - 6 => Protocol::AddrResponse, - val => Protocol::Unknown(val), - } - } -} - -impl Into for Protocol { - fn into(self) -> u8 { - match self { - Protocol::Ping => 1, - Protocol::Pong => 2, - Protocol::PunchRequest => 3, - Protocol::PunchResponse => 4, - Protocol::AddrRequest => 5, - Protocol::AddrResponse => 6, - Protocol::Unknown(val) => val, - } - } -} - -pub enum ControlPacket { - PingPacket(PingPacket), - PongPacket(PongPacket), - PunchRequest, - PunchResponse, - AddrRequest, - AddrResponse(AddrPacket), -} - -impl> ControlPacket { - pub fn new(protocol: u8, buffer: B) -> io::Result> { - match Protocol::from(protocol) { - Protocol::Ping => Ok(ControlPacket::PingPacket(PingPacket::new(buffer)?)), - Protocol::Pong => Ok(ControlPacket::PongPacket(PongPacket::new(buffer)?)), - Protocol::PunchRequest => Ok(ControlPacket::PunchRequest), - Protocol::PunchResponse => Ok(ControlPacket::PunchResponse), - Protocol::AddrRequest => Ok(ControlPacket::AddrRequest), - Protocol::AddrResponse => Ok(ControlPacket::AddrResponse(AddrPacket::new(buffer)?)), - Protocol::Unknown(_) => Err(io::Error::new(io::ErrorKind::InvalidData, "Unsupported")), - } - } -} - -/// 网络探针 -pub struct PingPacket { - buffer: B, -} - -pub type PongPacket = PingPacket; - -impl> PingPacket { - pub fn new(buffer: B) -> io::Result> { - let len = buffer.as_ref().len(); - if len < 4 { - return Err(io::Error::new(io::ErrorKind::InvalidData, "len < 4")); - } - Ok(PingPacket { buffer }) - } -} - -impl> PingPacket { - pub fn time(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[..2].try_into().unwrap()) - } - pub fn epoch(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap()) - } -} - -impl + AsMut<[u8]>> PingPacket { - pub fn set_time(&mut self, time: u16) { - self.buffer.as_mut()[..2].copy_from_slice(&time.to_be_bytes()) - } - pub fn set_epoch(&mut self, epoch: u16) { - self.buffer.as_mut()[2..4].copy_from_slice(&epoch.to_be_bytes()) - } -} - -impl> fmt::Debug for PingPacket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("PingPacket") - .field("time", &self.time()) - .field("epoch", &self.epoch()) - .finish() - } -} - -pub struct AddrPacket { - buffer: B, -} - -impl> AddrPacket { - pub fn new(buffer: B) -> io::Result> { - let len = buffer.as_ref().len(); - if len < 6 { - return Err(io::Error::new(io::ErrorKind::InvalidData, "len < 6")); - } - Ok(AddrPacket { buffer }) - } - pub fn ipv4(&self) -> Ipv4Addr { - let buf = self.buffer.as_ref(); - Ipv4Addr::new(buf[0], buf[1], buf[2], buf[3]) - } - pub fn port(&self) -> u16 { - u16::from_be_bytes(self.buffer.as_ref()[4..6].try_into().unwrap()) - } -} - -impl + AsMut<[u8]>> AddrPacket { - pub fn set_ipv4(&mut self, ip: Ipv4Addr) { - self.buffer.as_mut()[..4].copy_from_slice(&ip.octets()) - } - pub fn set_port(&mut self, port: u16) { - self.buffer.as_mut()[4..6].copy_from_slice(&port.to_be_bytes()) - } -} - -impl> fmt::Debug for AddrPacket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("AddrPacket") - .field("ipv4", &self.ipv4()) - .field("port", &self.port()) - .finish() - } -} diff --git a/vnt/src/protocol/error_packet.rs b/vnt/src/protocol/error_packet.rs deleted file mode 100644 index cea9467..0000000 --- a/vnt/src/protocol/error_packet.rs +++ /dev/null @@ -1,89 +0,0 @@ -use std::io; - -#[derive(Eq, PartialEq, Copy, Clone, Debug)] -pub enum Protocol { - TokenError, - Disconnect, - AddressExhausted, - IpAlreadyExists, - InvalidIp, - NoKey, - Other(u8), -} - -impl From for Protocol { - fn from(value: u8) -> Self { - match value { - 1 => Self::TokenError, - 2 => Self::Disconnect, - 3 => Self::AddressExhausted, - 4 => Self::IpAlreadyExists, - 5 => Self::InvalidIp, - 6 => Self::NoKey, - val => Self::Other(val), - } - } -} - -impl Into for Protocol { - fn into(self) -> u8 { - match self { - Protocol::TokenError => 1, - Protocol::Disconnect => 2, - Protocol::AddressExhausted => 3, - Protocol::IpAlreadyExists => 4, - Protocol::InvalidIp => 5, - Protocol::NoKey => 6, - Protocol::Other(val) => val, - } - } -} - -pub enum InErrorPacket { - TokenError, - Disconnect, - AddressExhausted, - IpAlreadyExists, - InvalidIp, - NoKey, - OtherError(ErrorPacket), -} - -impl> InErrorPacket { - pub fn new(protocol: u8, buffer: B) -> io::Result> { - match Protocol::from(protocol) { - Protocol::TokenError => Ok(InErrorPacket::TokenError), - Protocol::Disconnect => Ok(InErrorPacket::Disconnect), - Protocol::AddressExhausted => Ok(InErrorPacket::AddressExhausted), - Protocol::IpAlreadyExists => Ok(InErrorPacket::IpAlreadyExists), - Protocol::InvalidIp => Ok(InErrorPacket::InvalidIp), - Protocol::NoKey => Ok(InErrorPacket::NoKey), - Protocol::Other(_) => Ok(InErrorPacket::OtherError(ErrorPacket::new(buffer)?)), - } - } -} - -pub struct ErrorPacket { - buffer: B, -} - -impl> ErrorPacket { - pub fn new(buffer: B) -> io::Result> { - Ok(Self { buffer }) - } -} - -impl> ErrorPacket { - pub fn message(&self) -> io::Result { - match String::from_utf8(self.buffer.as_ref().to_vec()) { - Ok(str) => Ok(str), - Err(_) => Err(io::Error::new(io::ErrorKind::Other, "Utf8Error")), - } - } -} - -impl + AsMut<[u8]>> ErrorPacket { - pub fn set_message(&mut self, message: &str) { - self.buffer.as_mut().copy_from_slice(message.as_bytes()) - } -} diff --git a/vnt/src/protocol/extension.rs b/vnt/src/protocol/extension.rs deleted file mode 100644 index 1e43e06..0000000 --- a/vnt/src/protocol/extension.rs +++ /dev/null @@ -1,141 +0,0 @@ -/* 扩展协议 - 0 15 31 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 扩展数据(n) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 扩展数据(n) | type(8) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - 注:扩展数据的长度由type决定 -*/ - -use anyhow::anyhow; -use std::io; - -use crate::protocol::NetPacket; - -#[derive(Eq, PartialEq, Copy, Clone, Debug)] -pub enum ExtensionTailType { - Compression, - Unknown(u8), -} - -impl From for ExtensionTailType { - fn from(value: u8) -> Self { - if value == 0 { - ExtensionTailType::Compression - } else { - ExtensionTailType::Unknown(value) - } - } -} - -pub enum ExtensionTailPacket { - Compression(CompressionExtensionTail), - Unknown, -} - -impl + AsMut<[u8]>> NetPacket { - /// 分离尾部数据 - pub fn split_tail_packet(&mut self) -> anyhow::Result> { - if self.is_extension() { - let payload = self.payload(); - if let Some(v) = payload.last() { - return match ExtensionTailType::from(*v) { - ExtensionTailType::Compression => { - let data_len = self.data_len - 4; - self.set_data_len(data_len)?; - self.set_extension_flag(false); - Ok(ExtensionTailPacket::Compression( - CompressionExtensionTail::new( - &self.raw_buffer()[data_len..data_len + 4], - ), - )) - } - ExtensionTailType::Unknown(e) => Err(anyhow!("unknown extension {}", e)), - }; - } - } - Err(anyhow!("not extension")) - } - /// 追加压缩扩展 - pub fn append_compression_extension_tail( - &mut self, - ) -> io::Result> { - let len = self.data_len; - //增加数据长度 - self.set_data_len(self.data_len + 4)?; - self.set_extension_flag(true); - let mut tail = CompressionExtensionTail::new(&mut self.buffer_mut()[len..]); - tail.init(); - return Ok(tail); - } -} - -/* 扩展协议 - 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 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | algorithm(8) | | type(8) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - 注:扩展数据的长度由type决定 -*/ -/// 压缩扩展 -pub struct CompressionExtensionTail { - buffer: B, -} - -impl> CompressionExtensionTail { - pub fn new(buffer: B) -> CompressionExtensionTail { - assert_eq!(buffer.as_ref().len(), 4); - CompressionExtensionTail { buffer } - } -} - -impl> CompressionExtensionTail { - pub fn algorithm(&self) -> CompressionAlgorithm { - self.buffer.as_ref()[0].into() - } -} - -impl + AsMut<[u8]>> CompressionExtensionTail { - pub fn init(&mut self) { - self.buffer.as_mut().fill(0); - } - pub fn set_algorithm(&mut self, algorithm: CompressionAlgorithm) { - self.buffer.as_mut()[0] = algorithm.into() - } -} - -#[derive(Eq, PartialEq, Copy, Clone, Debug)] -pub enum CompressionAlgorithm { - #[cfg(feature = "lz4_compress")] - Lz4, - #[cfg(feature = "zstd_compress")] - Zstd, - Unknown(u8), -} - -impl From for CompressionAlgorithm { - fn from(value: u8) -> Self { - match value { - #[cfg(feature = "lz4_compress")] - 1 => CompressionAlgorithm::Lz4, - #[cfg(feature = "zstd_compress")] - 2 => CompressionAlgorithm::Zstd, - v => CompressionAlgorithm::Unknown(v), - } - } -} - -impl From for u8 { - fn from(value: CompressionAlgorithm) -> Self { - match value { - #[cfg(feature = "lz4_compress")] - CompressionAlgorithm::Lz4 => 1, - #[cfg(feature = "zstd_compress")] - CompressionAlgorithm::Zstd => 2, - CompressionAlgorithm::Unknown(val) => val, - } - } -} diff --git a/vnt/src/protocol/ip_turn_packet.rs b/vnt/src/protocol/ip_turn_packet.rs deleted file mode 100644 index edccdd5..0000000 --- a/vnt/src/protocol/ip_turn_packet.rs +++ /dev/null @@ -1,120 +0,0 @@ -#![allow(dead_code)] - -use std::io; -use std::net::Ipv4Addr; - -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub enum Protocol { - Ipv4, - WGIpv4, - Ipv4Broadcast, - Unknown(u8), -} - -impl From for Protocol { - fn from(value: u8) -> Self { - match value { - 4 => Protocol::Ipv4, - 5 => Protocol::WGIpv4, - 201 => Protocol::Ipv4Broadcast, - val => Protocol::Unknown(val), - } - } -} - -impl From for u8 { - fn from(val: Protocol) -> Self { - match val { - Protocol::Ipv4 => 4, - Protocol::WGIpv4 => 5, - Protocol::Ipv4Broadcast => 201, - Protocol::Unknown(val) => val, - } - } -} - -pub struct BroadcastPacket { - buffer: B, -} - -impl> BroadcastPacket { - pub fn unchecked(buffer: B) -> Self { - Self { buffer } - } - pub fn new(buffer: B) -> io::Result { - let len = buffer.as_ref().len(); - let packet = Self::unchecked(buffer); - if len < 2 + 4 || packet.addr_num() == 0 { - Err(io::Error::new( - io::ErrorKind::InvalidData, - "BroadcastPacket InvalidData", - )) - } else { - Ok(packet) - } - } -} - -impl> BroadcastPacket { - pub fn addr_num(&self) -> u8 { - self.buffer.as_ref()[0] - } - /// 已经发送给了这些地址 - pub fn addresses(&self) -> Vec { - let num = self.addr_num() as usize; - let mut list = Vec::with_capacity(num); - let buf = self.buffer.as_ref(); - let mut offset = 1; - for _ in 0..num { - list.push(Ipv4Addr::new( - buf[offset], - buf[offset + 1], - buf[offset + 2], - buf[offset + 3], - )); - offset += 4; - } - list - } - pub fn data(&self) -> io::Result<&[u8]> { - let start = 1 + self.addr_num() as usize * 4; - if start > self.buffer.as_ref().len() { - Err(io::Error::new(io::ErrorKind::InvalidData, "InvalidData")) - } else { - Ok(&self.buffer.as_ref()[start..]) - } - } -} - -impl + AsMut<[u8]>> BroadcastPacket { - pub fn set_address(&mut self, addr: &[Ipv4Addr]) -> io::Result<()> { - let buf = self.buffer.as_mut(); - if buf.len() < 1 + addr.len() * 4 || addr.len() > u8::MAX as usize { - Err(io::Error::new( - io::ErrorKind::InvalidData, - "addr invalid data", - )) - } else { - buf[0] = addr.len() as u8; - let mut offset = 1; - for ip in addr { - buf[offset..offset + 4].copy_from_slice(&ip.octets()); - offset += 4; - } - Ok(()) - } - } - pub fn set_data(&mut self, data: &[u8]) -> io::Result<()> { - let num = self.addr_num() as usize; - let start = 1 + 4 * num; - let buf = self.buffer.as_mut(); - if start >= buf.len() || start + data.len() != buf.len() { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "data invalid data", - )); - } - buf[start..].copy_from_slice(data); - Ok(()) - } -} diff --git a/vnt/src/protocol/mod.rs b/vnt/src/protocol/mod.rs deleted file mode 100644 index e718646..0000000 --- a/vnt/src/protocol/mod.rs +++ /dev/null @@ -1,309 +0,0 @@ -use crate::protocol::body::ENCRYPTION_RESERVED; -use std::net::Ipv4Addr; -use std::{fmt, io}; - -/* - 0 15 31 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |e |s |x |u| 版本(4) | 协议(8) | 上层协议(8) | 初始ttl(4) | 生存时间(4) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 源ip地址(32) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 目的ip地址(32) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 数据体 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - 注:e为是否加密标志,s为服务端通信包标志,x扩展标志,u未使用 -*/ -pub const HEAD_LEN: usize = 12; - -pub mod body; -pub mod control_packet; -pub mod error_packet; -pub mod extension; -pub mod ip_turn_packet; -pub mod other_turn_packet; -pub mod service_packet; - -#[derive(Eq, PartialEq, Copy, Clone, Debug)] -pub enum Version { - V2, - Unknown(u8), -} - -impl From for Version { - fn from(value: u8) -> Self { - match value { - // 版本从2开始,用于和stun协议的binging响应区分开 - 2 => Version::V2, - val => Version::Unknown(val), - } - } -} - -impl Into for Version { - fn into(self) -> u8 { - match self { - Version::V2 => 2, - Version::Unknown(val) => val, - } - } -} - -#[derive(Eq, PartialEq, Copy, Clone, Debug)] -pub enum Protocol { - /// 服务包 用于和服务端交互 - Service, - /// 响应异常 - Error, - /// 控制协议 - Control, - /// 转发ip数据 - IpTurn, - /// 转发其他数据 - OtherTurn, - Unknown(u8), -} - -impl From for Protocol { - fn from(value: u8) -> Self { - match value { - 1 => Protocol::Service, - 2 => Protocol::Error, - 3 => Protocol::Control, - 4 => Protocol::IpTurn, - 5 => Protocol::OtherTurn, - val => Protocol::Unknown(val), - } - } -} - -impl Into for Protocol { - fn into(self) -> u8 { - match self { - Protocol::Service => 1, - Protocol::Error => 2, - Protocol::Control => 3, - Protocol::IpTurn => 4, - Protocol::OtherTurn => 5, - Protocol::Unknown(val) => val, - } - } -} - -pub const MAX_TTL: u8 = 0b1111; -pub const MAX_SOURCE: u8 = 0b11110000; - -#[derive(Copy, Clone)] -pub struct NetPacket { - data_len: usize, - buffer: B, -} - -impl> NetPacket { - pub fn unchecked(buffer: B) -> Self { - let data_len = buffer.as_ref().len(); - Self { data_len, buffer } - } - pub fn new(buffer: B) -> io::Result> { - let data_len = buffer.as_ref().len(); - Self::new0(data_len, buffer) - } - pub fn new_encrypt(buffer: B) -> io::Result> { - if 12 + ENCRYPTION_RESERVED > buffer.as_ref().len() { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "length overflow", - )); - } - //加密需要预留ENCRYPTION_RESERVED字节 - let data_len = buffer.as_ref().len() - ENCRYPTION_RESERVED; - Self::new0(data_len, buffer) - } - pub fn new0(data_len: usize, buffer: B) -> io::Result> { - if data_len > buffer.as_ref().len() { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "length overflow", - )); - } - if data_len < 12 { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "data_len too short", - )); - } - Ok(NetPacket { data_len, buffer }) - } - #[inline] - pub fn buffer(&self) -> &[u8] { - &self.buffer.as_ref()[..self.data_len] - } - pub fn raw_buffer(&self) -> &[u8] { - self.buffer.as_ref() - } - pub fn data_len(&self) -> usize { - self.data_len - } - pub fn reserve(&self) -> usize { - self.buffer.as_ref().len() - self.data_len - } - pub fn into_buffer(self) -> B { - self.buffer - } -} - -impl> NetPacket { - /// 数据加密 - pub fn is_encrypt(&self) -> bool { - self.buffer.as_ref()[0] & 0x80 == 0x80 - } - /// 网关通信的标识 - pub fn is_gateway(&self) -> bool { - self.buffer.as_ref()[0] & 0x40 == 0x40 - } - /// 扩展协议 - pub fn is_extension(&self) -> bool { - self.buffer.as_ref()[0] & 0x20 == 0x20 - } - pub fn version(&self) -> Version { - Version::from(self.buffer.as_ref()[0] & 0x0F) - } - pub fn protocol(&self) -> Protocol { - Protocol::from(self.buffer.as_ref()[1]) - } - pub fn transport_protocol(&self) -> u8 { - self.buffer.as_ref()[2] - } - pub fn ttl(&self) -> u8 { - self.buffer.as_ref()[3] & MAX_TTL - } - pub fn source_ttl(&self) -> u8 { - self.buffer.as_ref()[3] >> 4 - } - pub fn source(&self) -> Ipv4Addr { - let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap(); - Ipv4Addr::from(tmp) - } - pub fn destination(&self) -> Ipv4Addr { - let tmp: [u8; 4] = self.buffer.as_ref()[8..12].try_into().unwrap(); - Ipv4Addr::from(tmp) - } - pub fn payload(&self) -> &[u8] { - &self.buffer.as_ref()[12..self.data_len] - } - pub fn head(&self) -> &[u8] { - &self.buffer.as_ref()[..12] - } -} - -impl + AsMut<[u8]>> NetPacket { - pub fn head_mut(&mut self) -> &mut [u8] { - &mut self.buffer.as_mut()[..12] - } - pub fn buffer_mut(&mut self) -> &mut [u8] { - &mut self.buffer.as_mut()[..self.data_len] - } - pub fn set_encrypt_flag(&mut self, is_encrypt: bool) { - if is_encrypt { - self.buffer.as_mut()[0] = self.buffer.as_ref()[0] | 0x80 - } else { - self.buffer.as_mut()[0] = self.buffer.as_ref()[0] & 0x7F - }; - } - pub fn set_gateway_flag(&mut self, is_gateway: bool) { - if is_gateway { - self.buffer.as_mut()[0] = self.buffer.as_ref()[0] | 0x40 - } else { - self.buffer.as_mut()[0] = self.buffer.as_ref()[0] & 0xBF - }; - } - pub fn set_extension_flag(&mut self, is_extension: bool) { - if is_extension { - self.buffer.as_mut()[0] = self.buffer.as_ref()[0] | 0x20 - } else { - self.buffer.as_mut()[0] = self.buffer.as_ref()[0] & 0xDF - }; - } - pub fn set_default_version(&mut self) { - let v: u8 = Version::V2.into(); - self.buffer.as_mut()[0] = (self.buffer.as_ref()[0] & 0xF0) | (0x0F & v); - } - pub fn set_protocol(&mut self, protocol: Protocol) { - self.buffer.as_mut()[1] = protocol.into(); - } - pub fn set_transport_protocol(&mut self, transport_protocol: u8) { - self.buffer.as_mut()[2] = transport_protocol; - } - pub fn set_transport_protocol_into>(&mut self, transport_protocol: P) { - self.buffer.as_mut()[2] = transport_protocol.into(); - } - pub fn first_set_ttl(&mut self, ttl: u8) { - self.buffer.as_mut()[3] = ttl << 4 | ttl; - } - pub fn set_ttl(&mut self, ttl: u8) { - self.buffer.as_mut()[3] = (self.buffer.as_mut()[3] & MAX_SOURCE) | (MAX_TTL & ttl); - } - pub fn incr_ttl(&mut self) -> u8 { - let ttl = self.ttl() - 1; - self.set_ttl(ttl); - ttl - } - pub fn set_source_ttl(&mut self, source_ttl: u8) { - self.buffer.as_mut()[3] = (source_ttl << 4) | (MAX_TTL & self.buffer.as_ref()[3]); - } - pub fn set_source(&mut self, source: Ipv4Addr) { - self.buffer.as_mut()[4..8].copy_from_slice(&source.octets()); - } - pub fn set_destination(&mut self, destination: Ipv4Addr) { - self.buffer.as_mut()[8..12].copy_from_slice(&destination.octets()); - } - pub fn set_payload(&mut self, payload: &[u8]) -> io::Result<()> { - if self.data_len - 12 != payload.len() { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "data_len - 12 != payload.len", - )); - } - self.buffer.as_mut()[12..self.data_len].copy_from_slice(payload); - Ok(()) - } - pub fn payload_mut(&mut self) -> &mut [u8] { - &mut self.buffer.as_mut()[12..self.data_len] - } - pub fn set_data_len(&mut self, data_len: usize) -> io::Result<()> { - if data_len > self.buffer.as_ref().len() || data_len < 12 { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "data_len invalid", - )); - } - self.data_len = data_len; - Ok(()) - } - pub fn set_payload_len(&mut self, payload_len: usize) -> io::Result<()> { - let data_len = HEAD_LEN + payload_len; - self.set_data_len(data_len) - } - pub fn set_data_len_max(&mut self) { - self.data_len = self.buffer.as_ref().len(); - } -} - -impl> fmt::Debug for NetPacket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("NetPacket") - .field("version", &self.version()) - .field("gateway", &self.is_gateway()) - .field("encrypt", &self.is_encrypt()) - .field("protocol", &self.protocol()) - .field("transport_protocol", &self.transport_protocol()) - .field("ttl", &self.ttl()) - .field("source_ttl", &self.source_ttl()) - .field("source", &self.source()) - .field("destination", &self.destination()) - .field("payload", &self.payload()) - .finish() - } -} diff --git a/vnt/src/protocol/other_turn_packet.rs b/vnt/src/protocol/other_turn_packet.rs deleted file mode 100644 index d224104..0000000 --- a/vnt/src/protocol/other_turn_packet.rs +++ /dev/null @@ -1,23 +0,0 @@ -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub enum Protocol { - Punch, - Unknown(u8), -} - -impl From for Protocol { - fn from(value: u8) -> Self { - match value { - 1 => Protocol::Punch, - val => Protocol::Unknown(val), - } - } -} - -impl Into for Protocol { - fn into(self) -> u8 { - match self { - Protocol::Punch => 1, - Protocol::Unknown(val) => val, - } - } -} diff --git a/vnt/src/protocol/service_packet.rs b/vnt/src/protocol/service_packet.rs deleted file mode 100644 index dd40f6c..0000000 --- a/vnt/src/protocol/service_packet.rs +++ /dev/null @@ -1,53 +0,0 @@ -#[derive(Eq, PartialEq, Copy, Clone, Debug)] -pub enum Protocol { - /// 注册请求 - RegistrationRequest, - /// 注册响应 - RegistrationResponse, - /// 拉取设备列表 - PullDeviceList, - /// 推送设备列表 - PushDeviceList, - /// 和服务端握手 - HandshakeRequest, - HandshakeResponse, - SecretHandshakeRequest, - SecretHandshakeResponse, - /// 客户端上报状态 - ClientStatusInfo, - Unknown(u8), -} - -impl From for Protocol { - fn from(value: u8) -> Self { - match value { - 1 => Self::RegistrationRequest, - 2 => Self::RegistrationResponse, - 3 => Self::PullDeviceList, - 4 => Self::PushDeviceList, - 5 => Self::HandshakeRequest, - 6 => Self::HandshakeResponse, - 7 => Self::SecretHandshakeRequest, - 8 => Self::SecretHandshakeResponse, - 9 => Self::ClientStatusInfo, - val => Self::Unknown(val), - } - } -} - -impl Into for Protocol { - fn into(self) -> u8 { - match self { - Self::RegistrationRequest => 1, - Self::RegistrationResponse => 2, - Self::PullDeviceList => 3, - Self::PushDeviceList => 4, - Self::HandshakeRequest => 5, - Self::HandshakeResponse => 6, - Self::SecretHandshakeRequest => 7, - Self::SecretHandshakeResponse => 8, - Self::ClientStatusInfo => 9, - Self::Unknown(val) => val, - } - } -} diff --git a/vnt/src/tun_tap_device/create_device.rs b/vnt/src/tun_tap_device/create_device.rs deleted file mode 100644 index 9a58d7c..0000000 --- a/vnt/src/tun_tap_device/create_device.rs +++ /dev/null @@ -1,241 +0,0 @@ -use std::io; -use std::net::Ipv4Addr; -use std::sync::Arc; -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"; - -pub fn create_device( - config: DeviceConfig, - call: &Call, -) -> Result, ErrorInfo> { - let device = match create_device0(&config) { - Ok(device) => device, - Err(e) => { - return Err(ErrorInfo::new_msg( - ErrorType::FailedToCrateDevice, - format!("create device {:?}", e), - )); - } - }; - #[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) = add_route( - index, - Ipv4Addr::from([224, 0, 0, 0]), - Ipv4Addr::from([240, 0, 0, 0]), - ) { - log::warn!("添加组播路由失败 ={:?}", e); - } - - for (dest, mask) in config.external_route { - 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> { - 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 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_name = config - .device_name - .clone() - .unwrap_or(DEFAULT_TUN_NAME.to_string()); - if &device_name == DEFAULT_TUN_NAME { - delete_device(DEFAULT_TUN_NAME); - } - } - - let device = tun_builder.mtu(config.mtu as u16).build_sync()?; - Ok(Arc::new(device)) -} - -#[cfg(target_os = "linux")] -fn delete_device(name: &str) { - // 删除默认网卡,此操作有风险,后续可能去除 - use std::process::Command; - let cmd = format!("ip link delete {}", name); - let delete_tun = Command::new("sh") - .arg("-c") - .arg(&cmd) - .output() - .expect("sh exec error!"); - if !delete_tun.status.success() { - log::warn!("删除网卡失败:{:?}", delete_tun); - } -} -#[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::("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::("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 { - 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) -} diff --git a/vnt/src/tun_tap_device/mod.rs b/vnt/src/tun_tap_device/mod.rs deleted file mode 100644 index 9519ae4..0000000 --- a/vnt/src/tun_tap_device/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] -#[cfg(feature = "integrated_tun")] -pub use create_device::create_device; - -#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] -#[cfg(feature = "integrated_tun")] -mod create_device; -#[cfg(feature = "integrated_tun")] -pub mod tun_create_helper; - -pub mod vnt_device; diff --git a/vnt/src/tun_tap_device/tun_create_helper.rs b/vnt/src/tun_tap_device/tun_create_helper.rs deleted file mode 100644 index 1509248..0000000 --- a/vnt/src/tun_tap_device/tun_create_helper.rs +++ /dev/null @@ -1,141 +0,0 @@ -use std::collections::HashMap; -use std::io; -use std::net::Ipv4Addr; -use std::sync::Arc; - -use crate::channel::context::ChannelContext; -use crate::cipher::Cipher; -use crate::compression::Compressor; -use crate::external_route::ExternalRoute; -use crate::handle::tun_tap::DeviceStop; -use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo}; -#[cfg(feature = "ip_proxy")] -use crate::ip_proxy::IpProxyMap; -use crate::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>>>, -} - -impl DeviceAdapter { - pub fn insert(&self, device: Arc) { - let r = self.tun.lock().replace(device); - assert!(r.is_none()); - } - /// 要保证先remove 再insert - pub fn remove(&self) { - drop(self.tun.lock().take()); - } -} - -impl DeviceWrite for DeviceAdapter { - #[inline] - fn write(&self, buf: &[u8]) -> io::Result { - if let Some(tun) = self.tun.lock().as_ref() { - tun.send(buf) - } else { - Err(io::Error::new(io::ErrorKind::NotFound, "not tun device")) - } - } - - fn into_device_adapter(self) -> DeviceAdapter { - self - } -} - -#[derive(Clone)] -pub struct TunDeviceHelper { - inner: Arc>, - device_adapter: DeviceAdapter, - device_stop: Arc>>, -} - -#[derive(Clone)] -struct TunDeviceHelperInner { - stop_manager: StopManager, - context: ChannelContext, - current_device: Arc>, - ip_route: ExternalRoute, - #[cfg(feature = "ip_proxy")] - ip_proxy_map: Option, - client_cipher: Cipher, - server_cipher: Cipher, - device_map: Arc)>>, - compressor: Compressor, -} - -impl TunDeviceHelper { - pub fn new( - stop_manager: StopManager, - context: ChannelContext, - current_device: Arc>, - ip_route: ExternalRoute, - #[cfg(feature = "ip_proxy")] ip_proxy_map: Option, - client_cipher: Cipher, - server_cipher: Cipher, - device_map: Arc)>>, - compressor: Compressor, - device_adapter: DeviceAdapter, - ) -> Self { - let inner = TunDeviceHelperInner { - stop_manager, - context, - current_device, - ip_route, - #[cfg(feature = "ip_proxy")] - ip_proxy_map, - client_cipher, - server_cipher, - device_map, - compressor, - }; - Self { - inner: Arc::new(Mutex::new(inner)), - device_adapter, - device_stop: Default::default(), - } - } - pub fn stop(&self) { - //先停止旧的,再启动新的,改变旧网卡的IP太麻烦 - if let Some(device_stop) = self.device_stop.lock().take() { - self.device_adapter.remove(); - loop { - device_stop.stop(); - std::thread::sleep(std::time::Duration::from_millis(300)); - //确保停止了 - if device_stop.is_stopped() { - break; - } - } - } - } - /// 要保证先stop 再start - pub fn start(&self, device: Arc, 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()); - assert!(s.is_none()); - let inner = self.inner.lock().clone(); - crate::handle::tun_tap::tun_handler::start( - inner.stop_manager, - inner.context, - device, - inner.current_device, - inner.ip_route, - #[cfg(feature = "ip_proxy")] - inner.ip_proxy_map, - inner.client_cipher, - inner.server_cipher, - inner.device_map, - inner.compressor, - device_stop, - allow_wire_guard, - ) - } -} diff --git a/vnt/src/tun_tap_device/vnt_device.rs b/vnt/src/tun_tap_device/vnt_device.rs deleted file mode 100644 index 9c3e8cc..0000000 --- a/vnt/src/tun_tap_device/vnt_device.rs +++ /dev/null @@ -1,7 +0,0 @@ -use std::io; - -pub trait DeviceWrite: Clone + Send + Sync + 'static { - fn write(&self, buf: &[u8]) -> io::Result; - #[cfg(feature = "integrated_tun")] - fn into_device_adapter(self) -> crate::tun_tap_device::tun_create_helper::DeviceAdapter; -} diff --git a/vnt/src/util/counter/adder.rs b/vnt/src/util/counter/adder.rs deleted file mode 100644 index 4888074..0000000 --- a/vnt/src/util/counter/adder.rs +++ /dev/null @@ -1,33 +0,0 @@ -use std::sync::Arc; - -use crossbeam_utils::atomic::AtomicCell; - -#[derive(Clone, Default)] -pub struct U64Adder { - count: Arc>, -} - -impl U64Adder { - pub fn add(&self, num: u64) { - self.count.fetch_add(num); - } - pub fn get(&self) -> u64 { - self.count.load() - } - pub fn watch(&self) -> WatchU64Adder { - WatchU64Adder { - count: self.count.clone(), - } - } -} - -#[derive(Clone)] -pub struct WatchU64Adder { - count: Arc>, -} - -impl WatchU64Adder { - pub fn get(&self) -> u64 { - self.count.load() - } -} diff --git a/vnt/src/util/counter/mod.rs b/vnt/src/util/counter/mod.rs deleted file mode 100644 index 0ebd885..0000000 --- a/vnt/src/util/counter/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod adder; -pub use adder::*; diff --git a/vnt/src/util/dns_query.rs b/vnt/src/util/dns_query.rs deleted file mode 100644 index e3067b2..0000000 --- a/vnt/src/util/dns_query.rs +++ /dev/null @@ -1,436 +0,0 @@ -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}; -use std::str::FromStr; -use std::time::Duration; -use std::{io, thread}; - -thread_local! { - static HISTORY: RefCell> = RefCell::new(HashMap::new()); -} - -/// 保留一个地址使用记录,使用过的地址后续不再选中,直到地址全使用过 -pub fn address_choose(addrs: Vec) -> anyhow::Result { - HISTORY.with(|history| { - let mut available = Vec::new(); - for x in &addrs { - let num = history.borrow().get(x).map_or(0, |v| *v); - if num < 3 { - available.push(*x); - } - } - if available.is_empty() { - available = addrs; - history.borrow_mut().clear(); - } - let addr = address_choose0(available)?; - history - .borrow_mut() - .entry(addr) - .and_modify(|v| { - *v += 1; - }) - .or_insert(1); - Ok(addr) - }) -} - -/// 后续实现选择延迟最低的可用地址,需要服务端配合 -/// 现在是选择第一个地址,优先ipv6 -fn address_choose0(addrs: Vec) -> anyhow::Result { - let v4: Vec = addrs.iter().filter(|v| v.is_ipv4()).copied().collect(); - let v6: Vec = addrs.iter().filter(|v| v.is_ipv6()).copied().collect(); - let check_addr = |addrs: &Vec| -> anyhow::Result { - let mut err = Vec::new(); - if !addrs.is_empty() { - let udp = if addrs[0].is_ipv6() { - UdpSocket::bind("[::]:0")? - } else { - UdpSocket::bind("0.0.0.0:0")? - }; - for addr in addrs { - if let Err(e) = udp.connect(addr) { - err.push((*addr, e)); - } else { - return Ok(*addr); - } - } - } - Err(anyhow::anyhow!("Unable to connect to address {:?}", err)) - }; - if v6.is_empty() { - return check_addr(&v4); - } - if v4.is_empty() { - return check_addr(&v6); - } - match check_addr(&v6) { - Ok(addr) => Ok(addr), - Err(e1) => match check_addr(&v4) { - Ok(addr) => Ok(addr), - Err(e2) => Err(anyhow::anyhow!("{} , {}", e1, e2)), - }, - } -} - -pub fn dns_query_all( - domain: &str, - mut name_servers: Vec, - default_interface: &LocalInterface, -) -> anyhow::Result> { - let mut current_domain = domain.to_string(); // 引入可变变量存储当前域名 - match SocketAddr::from_str(¤t_domain) { - Ok(addr) => Ok(vec![addr]), - Err(_) => { - // 重定向判断 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(current_domain - .to_socket_addrs() - .with_context(|| format!("DNS query failed {:?}", current_domain))? - .collect()); - } - } - - let mut err: Option = None; - for name_server in name_servers { - if let Some(domain) = txt_domain.as_ref() { - match txt_dns(domain, name_server, default_interface) { - Ok(addr) => { - if !addr.is_empty() { - println!("TXT: {:?}", addr); - return Ok(addr); - } - } - Err(e) => { - if let Some(err) = &mut err { - *err = anyhow::anyhow!("{} {}", err, e); - } else { - err.replace(anyhow::anyhow!("{}", e)); - } - } - } - continue; - } - - let end_index = current_domain - .rfind(':') - .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", current_domain))?; - let th1 = { - let host = host.to_string(); - let name_server = name_server.clone(); - 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(); - 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() { - Ok(rs) => { - for ip in rs { - addr.push(SocketAddr::new(ip.into(), port)); - } - } - Err(e) => { - err.replace(anyhow::anyhow!("{}", e)); - } - } - match th2.join().unwrap() { - Ok(rs) => { - for ip in rs { - addr.push(SocketAddr::new(ip.into(), port)); - } - } - Err(e) => { - if addr.is_empty() { - if let Some(err) = &mut err { - *err = anyhow::anyhow!("{},{}", err, e); - } else { - err.replace(anyhow::anyhow!("{}", e)); - } - continue; - } - } - } - if addr.is_empty() { - continue; - } - return Ok(addr); - } - if let Some(e) = err { - Err(e) - } else { - 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> { - // 确保域名有 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 = 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, - name_server: SocketAddr, - record_type: QueryType, - buf: &'a mut [u8], -) -> anyhow::Result> { - let mut builder = Builder::new_query(1, true); - builder.add_question(domain, false, record_type, QueryClass::IN); - let packet = builder.build().unwrap(); - - udp.connect(name_server) - .with_context(|| format!("DNS {:?} error ", name_server))?; - let mut count = 0; - let len = loop { - udp.send(&packet)?; - - match udp.recv(buf) { - Ok(len) => { - break len; - } - Err(e) => { - if e.kind() == io::ErrorKind::TimedOut || e.kind() == io::ErrorKind::WouldBlock { - count += 1; - if count < 3 { - continue; - } - } - Err(e).with_context(|| format!("DNS {:?} recv error ", name_server))? - } - }; - }; - - let pkt = Packet::parse(&buf[..len]) - .with_context(|| format!("domain {:?} DNS {:?} data error ", domain, name_server))?; - if pkt.header.response_code != ResponseCode::NoError { - return Err(anyhow::anyhow!( - "response_code {} DNS {:?} domain {:?}", - pkt.header.response_code, - name_server, - domain - )); - } - if pkt.answers.is_empty() { - return Err(anyhow::anyhow!( - "No records received DNS {:?} domain {:?}", - name_server, - domain - )); - } - - Ok(pkt) -} - -pub fn txt_dns( - domain: &str, - name_server: String, - default_interface: &LocalInterface, -) -> anyhow::Result> { - let name_server: SocketAddr = name_server.parse()?; - 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(); - for record in message.answers { - if let RData::TXT(txt) = record.data { - for x in txt.iter() { - let txt = std::str::from_utf8(x).context("record type txt is not string")?; - let addr = - SocketAddr::from_str(txt).context("record type txt is not SocketAddr")?; - rs.push(addr); - } - } - } - Ok(rs) -} - -fn bind_udp( - name_server: SocketAddr, - default_interface: &LocalInterface, -) -> anyhow::Result { - let addr: SocketAddr = if name_server.is_ipv4() { - "0.0.0.0:0".parse().unwrap() - } else { - "[::]:0".parse().unwrap() - }; - 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, - default_interface: &LocalInterface, -) -> anyhow::Result> { - let name_server: SocketAddr = name_server.parse()?; - 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(); - for record in message.answers { - if let RData::A(a) = record.data { - rs.push(a.0); - } - } - Ok(rs) -} - -pub fn aaaa_dns( - domain: String, - name_server: String, - default_interface: &LocalInterface, -) -> anyhow::Result> { - let name_server: SocketAddr = name_server.parse()?; - 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(); - for record in message.answers { - if let RData::AAAA(a) = record.data { - rs.push(a.0); - } - } - Ok(rs) -} diff --git a/vnt/src/util/limit/mod.rs b/vnt/src/util/limit/mod.rs deleted file mode 100644 index 0492bd1..0000000 --- a/vnt/src/util/limit/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod rate_limiter; -pub use rate_limiter::*; - -mod traffic_meter; -pub use traffic_meter::*; diff --git a/vnt/src/util/limit/rate_limiter.rs b/vnt/src/util/limit/rate_limiter.rs deleted file mode 100644 index 35f8e20..0000000 --- a/vnt/src/util/limit/rate_limiter.rs +++ /dev/null @@ -1,62 +0,0 @@ -use parking_lot::Mutex; -use std::sync::Arc; -use std::time::Instant; - -#[derive(Clone)] -pub struct ConcurrentRateLimiter { - inner: Arc>, -} - -impl ConcurrentRateLimiter { - pub fn new(capacity: usize, refill_rate: usize) -> Self { - let inner = RateLimiter::new(capacity, refill_rate); - Self { - inner: Arc::new(Mutex::new(inner)), - } - } - pub fn try_acquire(&self) -> bool { - self.inner.lock().try_acquire() - } -} - -pub struct RateLimiter { - capacity: usize, - tokens: usize, - refill_rate: usize, - last_refill: Instant, -} - -impl RateLimiter { - // 初始化限流器 - pub fn new(capacity: usize, refill_rate: usize) -> Self { - Self { - capacity, - tokens: capacity, - refill_rate, - last_refill: Instant::now(), - } - } - - // 尝试获取一个令牌 - pub fn try_acquire(&mut self) -> bool { - self.refill(); - if self.tokens > 0 { - self.tokens -= 1; - true - } else { - false - } - } - - // 补充令牌 - fn refill(&mut self) { - let now = Instant::now(); - let elapsed = now.duration_since(self.last_refill).as_secs() as usize; - let new_tokens = elapsed * self.refill_rate; - - if new_tokens > 0 { - self.tokens = std::cmp::min(self.capacity, self.tokens + new_tokens); - self.last_refill = now; - } - } -} diff --git a/vnt/src/util/limit/traffic_meter.rs b/vnt/src/util/limit/traffic_meter.rs deleted file mode 100644 index 3d4eb50..0000000 --- a/vnt/src/util/limit/traffic_meter.rs +++ /dev/null @@ -1,132 +0,0 @@ -use parking_lot::Mutex; -use std::collections::{HashMap, VecDeque}; -use std::net::Ipv4Addr; -use std::sync::Arc; -use std::time::{Duration, Instant}; - -#[derive(Clone)] -pub struct TrafficMeterMultiAddress { - history_capacity: usize, - inner: Arc)>>, -} - -impl Default for TrafficMeterMultiAddress { - fn default() -> Self { - TrafficMeterMultiAddress::new(100) - } -} - -impl TrafficMeterMultiAddress { - pub fn new(history_capacity: usize) -> Self { - let inner = Arc::new(Mutex::new((0, HashMap::new()))); - Self { - inner, - history_capacity, - } - } - pub fn add_traffic(&self, ip: Ipv4Addr, amount: usize) { - let mut guard = self.inner.lock(); - guard.0 += amount as u64; - guard - .1 - .entry(ip) - .or_insert(TrafficMeter::new(self.history_capacity)) - .add_traffic(amount) - } - pub fn total(&self) -> u64 { - self.inner.lock().0 - } - pub fn get_all(&self) -> (u64, HashMap) { - let guard = self.inner.lock(); - ( - guard.0, - guard.1.iter().map(|(ip, t)| (*ip, t.total())).collect(), - ) - } - pub fn get_all_history(&self) -> (u64, HashMap)>) { - let guard = self.inner.lock(); - ( - guard.0, - guard - .1 - .iter() - .map(|(ip, t)| (*ip, (t.total(), t.get_history()))) - .collect(), - ) - } - pub fn get_history(&self, ip: &Ipv4Addr) -> Option<(u64, Vec)> { - self.inner - .lock() - .1 - .get(ip) - .map(|t| (t.total(), t.get_history())) - } -} - -#[derive(Clone)] -pub struct ConcurrentTrafficMeter { - inner: Arc>, -} - -impl ConcurrentTrafficMeter { - pub fn new(history_capacity: usize) -> Self { - let inner = Arc::new(Mutex::new(TrafficMeter::new(history_capacity))); - Self { inner } - } - pub fn add_traffic(&self, amount: usize) { - self.inner.lock().add_traffic(amount) - } - pub fn get_history(&self) -> Vec { - self.inner.lock().get_history() - } -} - -pub struct TrafficMeter { - start_time: Instant, - total: u64, - count: usize, - history_capacity: usize, - history: VecDeque, -} - -impl TrafficMeter { - // 初始化一个新的 TrafficMeter - pub fn new(history_capacity: usize) -> Self { - Self { - start_time: Instant::now(), - total: 0, - count: 0, - history: VecDeque::with_capacity(history_capacity), - history_capacity, - } - } - - // 增加流量计数 - pub fn add_traffic(&mut self, amount: usize) { - self.total += amount as u64; - self.count += amount; - self.check_time(); - } - - // 检查时间是否超过一秒,如果是,记录流量并重置计数器和时间 - fn check_time(&mut self) { - if self.start_time.elapsed() >= Duration::new(1, 0) { - // 将当前计数添加到历史记录 - if self.history.len() >= self.history_capacity { - self.history.pop_front(); // 保持历史记录不超过capacity - } - self.history.push_back(self.count); - - // 重置计数器和时间 - self.count = 0; - self.start_time = Instant::now(); - } - } - pub fn total(&self) -> u64 { - self.total - } - // 获取流量记录 - pub fn get_history(&self) -> Vec { - self.history.iter().cloned().collect() - } -} diff --git a/vnt/src/util/mod.rs b/vnt/src/util/mod.rs deleted file mode 100644 index fade277..0000000 --- a/vnt/src/util/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -mod notify; -mod scheduler; -pub use notify::{StopManager, Worker}; -pub use scheduler::Scheduler; - -// mod counter; -// pub use counter::*; - -mod dns_query; -pub use dns_query::*; - -#[cfg(feature = "upnp")] -mod upnp; -#[cfg(feature = "upnp")] -pub use upnp::*; - -pub mod limit; diff --git a/vnt/src/util/notify.rs b/vnt/src/util/notify.rs deleted file mode 100644 index 3495f8c..0000000 --- a/vnt/src/util/notify.rs +++ /dev/null @@ -1,174 +0,0 @@ -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; -use std::sync::Arc; -use std::thread; -use std::thread::Thread; -use std::time::Duration; - -use anyhow::anyhow; -use parking_lot::Mutex; - -#[derive(Clone)] -pub struct StopManager { - inner: Arc, -} - -impl StopManager { - pub fn new(f: F) -> Self - where - F: FnOnce() + Send + 'static, - { - Self { - inner: Arc::new(StopManagerInner::new(f)), - } - } - pub fn add_listener(&self, name: String, f: F) -> anyhow::Result - where - F: FnOnce() + Send + 'static, - { - self.inner.add_listener(name, f) - } - pub fn stop(&self) { - self.inner.stop(); - } - pub fn wait(&self) { - self.inner.wait(); - } - pub fn wait_timeout(&self, dur: Duration) -> bool { - self.inner.wait_timeout(dur) - } - pub fn is_stopped(&self) -> bool { - self.inner.is_stopped() - } -} - -struct StopManagerInner { - listeners: Mutex<(bool, Vec<(String, Box)>)>, - park_threads: Mutex>, - worker_num: AtomicUsize, - state: AtomicBool, - stop_call: Mutex>>, -} - -impl StopManagerInner { - fn new(f: F) -> Self - where - F: FnOnce() + Send + 'static, - { - Self { - listeners: Mutex::new((false, Vec::with_capacity(32))), - park_threads: Mutex::new(Vec::with_capacity(4)), - worker_num: AtomicUsize::new(0), - state: AtomicBool::new(false), - stop_call: Mutex::new(Some(Box::new(f))), - } - } - fn add_listener(self: &Arc, name: String, f: F) -> anyhow::Result - where - F: FnOnce() + Send + 'static, - { - if name.is_empty() { - return Err(anyhow!("name cannot be empty")); - } - let mut guard = self.listeners.lock(); - if guard.0 { - return Err(anyhow!("stopped")); - } - for (n, _) in &guard.1 { - if &name == n { - return Err(anyhow!("stop add_listener {:?} name already exists", name)); - } - } - guard.1.push((name.clone(), Box::new(f))); - Ok(Worker::new(name, self.clone())) - } - fn stop(&self) { - self.state.store(true, Ordering::Release); - let mut guard = self.listeners.lock(); - guard.0 = true; - for (_name, listener) in guard.1.drain(..) { - listener(); - } - } - pub fn is_stopped(&self) -> bool { - self.worker_num.load(Ordering::Acquire) == 0 - } - fn wait(&self) { - { - let mut guard = self.park_threads.lock(); - guard.push(thread::current()); - drop(guard); - } - loop { - if self.worker_num.load(Ordering::Acquire) == 0 { - return; - } - thread::park() - } - } - fn wait_timeout(&self, dur: Duration) -> bool { - { - let mut guard = self.park_threads.lock(); - guard.push(thread::current()); - drop(guard); - } - if self.worker_num.load(Ordering::Acquire) == 0 { - return true; - } - thread::park_timeout(dur); - self.worker_num.load(Ordering::Acquire) == 0 - } - fn stop_call(&self) { - self.stop(); - if let Some(call) = self.stop_call.lock().take() { - call(); - } - } -} - -pub struct Worker { - name: String, - inner: Arc, -} - -impl Worker { - fn new(name: String, inner: Arc) -> Self { - let _ = inner.worker_num.fetch_add(1, Ordering::AcqRel); - Self { name, inner } - } - fn release0(&self) { - let inner = &self.inner; - let worker_name = &self.name; - { - let mut mutex_guard = inner.listeners.lock(); - if let Some(pos) = mutex_guard - .1 - .iter() - .position(|(name, _)| name == worker_name) - { - let (_, listener) = mutex_guard.1.remove(pos); - listener(); - } - } - - let count = inner.worker_num.fetch_sub(1, Ordering::AcqRel); - if count == 1 { - for x in inner.park_threads.lock().drain(..) { - x.unpark(); - } - self.inner.stop_call(); - } - } - pub fn stop_all(self) { - self.inner.stop() - } - pub fn stop_self(self) { - drop(self) - } -} - -impl Drop for Worker { - fn drop(&mut self) { - self.release0(); - log::info!("stop {}", self.name); - } -} diff --git a/vnt/src/util/scheduler.rs b/vnt/src/util/scheduler.rs deleted file mode 100644 index 808cafd..0000000 --- a/vnt/src/util/scheduler.rs +++ /dev/null @@ -1,176 +0,0 @@ -use crate::util::StopManager; -use crossbeam_utils::atomic::AtomicCell; -use std::collections::BinaryHeap; -use std::sync::mpsc::TrySendError; -use std::sync::Arc; -use std::{ - cmp::Ordering, - sync::mpsc::{sync_channel, Receiver, SyncSender}, - time::{Duration, Instant}, -}; - -struct DelayedTask { - f: Box, - next: Instant, -} - -impl Eq for DelayedTask {} - -impl PartialEq for DelayedTask { - fn eq(&self, other: &Self) -> bool { - self.next.eq(&other.next) - } -} - -impl PartialOrd for DelayedTask { - fn partial_cmp(&self, other: &Self) -> Option { - self.next.partial_cmp(&other.next).map(|ord| ord.reverse()) - } -} - -impl Ord for DelayedTask { - fn cmp(&self, other: &Self) -> Ordering { - self.next.cmp(&other.next).reverse() - } -} - -enum Op { - Task(DelayedTask), - Stop, -} - -#[derive(Clone)] -pub struct Scheduler { - sender: SyncSender, - state: Arc>, -} - -#[derive(Copy, Clone, Eq, PartialEq)] -enum SchedulerState { - Running, - ShutdownNow, // 立即停止任务执行,队列中剩余的任务不再执行 - _Shutdown, //执行完队列中剩余的任务再停止 -} - -impl Scheduler { - pub fn new(stop_manager: StopManager) -> anyhow::Result { - let (sender, receiver) = sync_channel::(32); - let state = Arc::new(AtomicCell::new(SchedulerState::Running)); - let s = Self { sender, state }; - let s_inner = s.clone(); - let worker = { - let scheduler = s.clone(); - stop_manager.add_listener("Scheduler".into(), move || { - scheduler.shutdown_now(); - })? - }; - std::thread::Builder::new() - .name("Scheduler".into()) - .spawn(move || { - run(receiver, &s_inner); - s_inner.shutdown_now(); - worker.stop_all(); - }) - .expect("Scheduler"); - Ok(s) - } - pub fn timeout(&self, time: Duration, f: F) -> bool - where - F: FnOnce(&Scheduler) + Send + 'static, - { - if self.state.load() != SchedulerState::Running { - log::error!("定时任务执行停止"); - return false; - } - let task = DelayedTask { - f: Box::new(f), - next: Instant::now().checked_add(time).unwrap(), - }; - // 如果是任务中调用此方法,那这里用send可能会导致整个定时任务阻塞 - // 任务总数不能大于或等于通道长度,所以改成try_send快速失败 - match self.sender.try_send(Op::Task(task)) { - Ok(_) => true, - Err(e) => { - match e { - TrySendError::Full(_) => { - log::error!("定时任务队列达到上限"); - } - TrySendError::Disconnected(_) => { - log::error!("定时任务执行停止 通道关闭"); - } - } - false - } - } - } - pub fn shutdown_now(&self) { - self.state.store(SchedulerState::ShutdownNow); - let _ = self.sender.send(Op::Stop); - } -} - -fn run(receiver: Receiver, s_inner: &Scheduler) { - let mut binary_heap = BinaryHeap::::with_capacity(32); - loop { - while let Some(task) = binary_heap.peek() { - if s_inner.state.load() == SchedulerState::ShutdownNow { - return; - } - let now = Instant::now(); - if now < task.next { - //需要等待对应时间 - match receiver.recv_timeout(task.next - now) { - Ok(op) => { - if add_task(op, &mut binary_heap) { - continue; - } - return; - } - Err(e) => match e { - std::sync::mpsc::RecvTimeoutError::Timeout => continue, - std::sync::mpsc::RecvTimeoutError::Disconnected => return, - }, - } - } else { - if let Some(task) = binary_heap.pop() { - (task.f)(s_inner); - } - } - } - //取出所有任务 - loop { - match receiver.try_recv() { - Ok(op) => { - if add_task(op, &mut binary_heap) { - continue; - } - return; - } - Err(e) => match e { - std::sync::mpsc::TryRecvError::Empty => break, - std::sync::mpsc::TryRecvError::Disconnected => return, - }, - } - } - - if binary_heap.is_empty() { - //任务队列为空时陷入等待 - if let Ok(op) = receiver.recv() { - if add_task(op, &mut binary_heap) { - continue; - } - } - return; - } - } -} - -fn add_task(op: Op, binary_heap: &mut BinaryHeap) -> bool { - return match op { - Op::Task(task) => { - binary_heap.push(task); - true - } - Op::Stop => false, - }; -} diff --git a/vnt/src/util/upnp.rs b/vnt/src/util/upnp.rs deleted file mode 100644 index ae3755d..0000000 --- a/vnt/src/util/upnp.rs +++ /dev/null @@ -1,81 +0,0 @@ -use igd::{search_gateway, PortMappingProtocol}; -use std::net::{Ipv4Addr, SocketAddrV4}; -use std::ops::Deref; -use std::sync::Arc; - -use parking_lot::Mutex; - -#[derive(Clone, Default)] -pub struct UPnP { - inner: Arc, -} - -impl Deref for UPnP { - type Target = UpnpInner; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -#[derive(Default)] -pub struct UpnpInner { - list: Mutex>, -} - -impl UpnpInner { - pub fn add_tcp_port(&self, port: u16) { - self.list.lock().push((PortMappingProtocol::TCP, port)); - } - pub fn add_udp_port(&self, port: u16) { - self.list.lock().push((PortMappingProtocol::UDP, port)); - } - pub fn reset(&self, local_ip: Ipv4Addr) { - let gateway = match search_gateway(Default::default()) { - Ok(gateway) => gateway, - Err(e) => { - log::warn!("search_gateway {:?}", e); - return; - } - }; - let guard = self.list.lock(); - - // 不支持upnp的情况会阻塞30秒,之后再改这个库 - for (protocol, port) in guard.iter() { - let local_addr = SocketAddrV4::new(local_ip, *port); - log::info!("add upnp protocol={} {}", protocol, local_addr); - if let Err(e) = gateway.add_port(*protocol, *port, local_addr, 700, "upnp") { - log::warn!( - "add upnp failed protocol={},port={} err:{:?}", - protocol, - port, - e - ); - } - } - } -} - -impl Drop for UpnpInner { - fn drop(&mut self) { - // let gateway = match search_gateway(Default::default()) { - // Ok(gateway) => gateway, - // Err(e) => { - // log::warn!("search_gateway {:?}", e); - // return; - // } - // }; - // - // let guard = self.list.lock(); - // for (protocol, port) in guard.iter() { - // if let Err(e) = gateway.remove_port(*protocol, *port) { - // log::warn!( - // "remove upnp failed protocol={},port={} err:{:?}", - // protocol, - // port, - // e - // ); - // } - // } - } -}