Compare commits

...
73 Commits
Author SHA1 Message Date
lubeilin 71a2e3c592 去除控制台输出 2023-07-24 23:52:27 +08:00
lubeilin aa6d3a6843 修复tcp模式下的p2p问题 2023-07-24 21:37:19 +08:00
lubeilin 9b42c5d092 支持自定义ip、服务端tcp通道、可选择禁止p2p 2023-07-24 00:42:27 +08:00
lbl8603 2f7817ce5b Update README.md 2023-07-19 21:24:15 +08:00
lbl8603 e7c6bcf9a9 Update README.md 2023-07-19 17:42:42 +08:00
lbl8603 b6b8971b12 Update README.md 2023-07-19 11:35:42 +08:00
lbl8603 ba4a0e008b Update README.md 2023-07-19 10:08:06 +08:00
lbl8603 4baccc5047 Update README.md 2023-07-19 09:52:53 +08:00
lubeilin 6cf71c8068 Merge remote-tracking branch 'origin/main' 2023-07-18 23:32:34 +08:00
lubeilin 8e556a20ee 打包排除源码 2023-07-18 23:32:10 +08:00
lbl8603 5f28a5044a Update README.md 2023-07-18 22:27:22 +08:00
lbl8603 16d295bf32 Update README.md 2023-07-18 20:42:09 +08:00
lbl8603 32163e077e Update README.md 2023-07-18 17:37:17 +08:00
lbl8603 6a5a40a7d3 Update README.md 2023-07-18 17:19:34 +08:00
lbl8603 161ecc865c Update README.md 2023-07-18 17:17:30 +08:00
lubeilin 1a5d40ff2d 修复mac端问题 2023-07-17 23:04:21 +08:00
lubeilin 6b988e0612 1.1.0 2023-07-17 22:56:37 +08:00
lubeilin 8d44934382 修改后台命令 2023-07-17 08:42:42 +08:00
lubeilin 6b140b0f71 增加桌面端 2023-07-17 01:32:34 +08:00
lubeilin c2b7b02f3f 调整jni模块、优化cmd模块展示 2023-07-17 01:31:05 +08:00
lubeilin 24140c2145 增加精简客户端模块,适用于服务端、路由器 2023-07-05 23:38:02 +08:00
lubeilin a6050e5f59 增加jni模块 2023-07-05 23:37:30 +08:00
lubeilin 50e97fd95f 增加安卓端支持、优化广播、增加停止监听 2023-07-05 23:36:41 +08:00
lubeilin 890e5f7391 默认使用广播代替组播、精简依赖 2023-06-29 23:14:59 +08:00
lubeilin be3bf82e35 支持客户端加密 2023-06-26 22:38:15 +08:00
lubeilin e8af503130 支持p2p组播/广播 2023-06-23 15:38:13 +08:00
lubeilin 466174ef88 增加国际化输出 2023-06-23 15:33:08 +08:00
lubeilin ba2c792e2a 使用tokio改写处理逻辑 2023-06-23 15:32:49 +08:00
lubeilin 17f3fcf9b0 优化tun\tap处理 2023-06-23 15:31:44 +08:00
lubeilin 11d1ff4b42 igmp解析 2023-06-23 15:30:07 +08:00
lubeilin b640bc50ef 合并tun、tap配置,减少重复代码 2023-06-23 15:29:03 +08:00
lubeilin 4bbd5282ee 优化tun设置 2023-06-23 15:28:11 +08:00
lubeilin d7fd504f8f 添加igmp、调整返回值 2023-06-23 15:25:00 +08:00
lubeilin cf4375b405 使用tokio改写网络通道 2023-06-23 15:17:46 +08:00
lbl8603 45da060c1a Update README.md 2023-06-03 20:48:29 +08:00
lbl8603 4dc84914dd Update README.md 2023-06-03 20:47:55 +08:00
lbl8603 c1ee7ac11c Update README.md 2023-06-03 09:38:46 +08:00
lubeilin 409b57c171 update 2023-06-03 09:37:51 +08:00
lubeilin ff4580b9bf 优化重连逻辑和路径探测 2023-06-02 18:28:33 +08:00
lubeilin 6daa75d2f2 优化网卡配置,增加metric设置 2023-06-02 18:28:02 +08:00
lubeilin 59f07f2d75 修复windows服务不能自启动的问题 2023-06-02 18:26:38 +08:00
lbl8603 44035685c8 Update README.md 2023-06-01 12:42:01 +08:00
lubeilin c8d0f3850f 修改ttl 2023-05-29 22:49:34 +08:00
lubeilin b846f54d79 update 2023-05-29 19:14:11 +08:00
lubeilin bb7b1d23af tun增加重试 2023-05-29 19:05:11 +08:00
lubeilin 9e700b3094 替换数据文件名称 2023-05-29 12:46:06 +08:00
lubeilin 38f28e313c windows服务 增加异常输出 2023-05-28 21:40:23 +08:00
lubeilin 63f03fb11f windows服务配置 2023-05-28 21:29:37 +08:00
lubeilin cf7854906f windows服务配置 2023-05-28 21:11:47 +08:00
lubeilin 78ae5f3036 读取配置文件 2023-05-28 20:33:59 +08:00
lubeilin c412a256b8 支持点对网 2023-05-28 20:28:58 +08:00
Droid-MAX 94def558fc Merge pull request #3 from lbl8603/main
update from upstream
2023-05-12 17:01:32 +08:00
lubeilin 25e9c523d3 修复已知问题 2023-05-12 16:54:44 +08:00
Droid-MAX a009d06000 Update rust.yml 2023-05-11 21:10:09 +08:00
lubeilin 675e0c52ff 调整代码顺序 2023-05-11 20:56:55 +08:00
Droid-MAX dc4219753a Update rust.yml 2023-05-11 20:53:01 +08:00
Droid-MAX c003b9acbb Update rust.yml 2023-05-10 12:25:36 +08:00
Droid-MAX f4869ebd1d Update rust.yml 2023-05-10 11:57:46 +08:00
Droid-MAX de4f42537d Update rust.yml 2023-05-10 11:47:52 +08:00
Droid-MAX b9ebe80ef3 Update rust.yml 2023-05-10 11:41:16 +08:00
lubeilin ea5ba750d4 sync 2023-05-09 22:47:59 +08:00
lubeilin b594afce16 readme 2023-05-07 20:28:35 +08:00
lubeilin ed3c44d6cf 保存参数到配置文件 2023-05-07 19:05:15 +08:00
lubeilin 35ed7f7e45 支持tap网卡,优化tun网卡配置 2023-05-07 18:32:11 +08:00
lubeilin 068580e036 Merge remote-tracking branch 'upstream/main' 2023-05-07 13:34:28 +08:00
Droid-MAX ea61b06e58 Update rust.yml 2023-04-26 11:27:02 +08:00
Droid-MAX f260c26e4f Update rust.yml 2023-04-26 11:11:03 +08:00
lubeilin 9cea433a8b 修复不加日志启动失败的问题 2023-04-25 23:17:31 +08:00
Droid-MAX 38dce9c13b Keep log file names in the same format 2023-04-25 12:51:03 +08:00
lubeilin 04731f1ce5 解决部分linux CPU飙升问题 2023-04-24 23:11:10 +08:00
lubeilin 3ddcb629c0 修改版本 2023-04-23 18:45:35 +08:00
lubeilin 12bd058152 增加日志 2023-04-23 18:44:01 +08:00
lubeilin c9b1bf5a5e 完善错误提示 2023-04-23 17:41:16 +08:00
163 changed files with 9324 additions and 5343 deletions
+38 -31
View File
@@ -2,7 +2,8 @@ name: Rust
on:
push:
pull_request:
tags:
- '*'
env:
CARGO_TERM_COLOR: always
@@ -13,23 +14,23 @@ defaults:
shell: bash
jobs:
# test:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v2
# - name: Init submodules
# uses: snickerbockers/submodules-init@v4
# - name: Cargo cache
# uses: actions/cache@v2
# with:
# path: |
# ~/.cargo/registry
# ./target
# key: test-cargo-registry
# - name: List
# run: find ./
# - name: Run tests
# run: cargo test --verbose
# test:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v2
# - name: Init submodules
# uses: snickerbockers/submodules-init@v4
# - name: Cargo cache
# uses: actions/cache@v2
# with:
# path: |
# ~/.cargo/registry
# ./target
# key: test-cargo-registry
# - name: List
# run: find ./
# - name: Run tests
# run: cargo test --verbose
build:
strategy:
@@ -37,6 +38,8 @@ jobs:
matrix:
# a list of all the targets
include:
- TARGET: i686-unknown-linux-musl # test in an alpine container on a mac
OS: ubuntu-latest
- TARGET: x86_64-unknown-linux-gnu # tested in a debian container on a mac
OS: ubuntu-latest
- TARGET: x86_64-unknown-linux-musl # test in an alpine container on a mac
@@ -55,14 +58,16 @@ jobs:
OS: ubuntu-latest
- TARGET: x86_64-apple-darwin # tested on a mac, is not properly signed so there are security warnings
OS: macos-latest
- TARGET: x86_64-pc-windows-msvc # tested on a windows machine
OS: windows-latest
- TARGET: aarch64-apple-darwin # tested on a mac, is not properly signed so there are security warnings
OS: macos-latest
- TARGET: i686-pc-windows-msvc # tested on a windows machine
OS: windows-latest
- TARGET: x86_64-pc-windows-msvc # tested on a windows machine
OS: windows-latest
# needs: test
runs-on: ${{ matrix.OS }}
env:
NAME: switch-desktop # change with the name of your project
NAME: vnt-cli # change with the name of your project
TARGET: ${{ matrix.TARGET }}
OS: ${{ matrix.OS }}
steps:
@@ -83,9 +88,8 @@ jobs:
# dependencies are only needed on ubuntu as that's the only place where
# we make cross-compilation
if [[ $OS =~ ^ubuntu.*$ ]]; then
sudo apt-get install -qq crossbuild-essential-arm64 crossbuild-essential-armhf
sudo apt-get update && sudo apt-get install -qq crossbuild-essential-arm64 crossbuild-essential-armhf
fi
# some additional configuration for cross-compilation on linux
cat >>~/.cargo/config <<EOF
[target.aarch64-unknown-linux-gnu]
@@ -104,7 +108,7 @@ jobs:
- name: Install rust target
run: rustup target add $TARGET
- name: Run build
run: cargo build --package switch-desktop --release --verbose --target $TARGET
run: cargo build --package vnt-cli --release --verbose --target $TARGET
- name: List target
run: find ./target
- name: Compress
@@ -121,15 +125,14 @@ jobs:
else
TAG=$GITHUB_SHA
fi
mv ./target/$TARGET/release/$EXEC ./$EXEC
tar -czf ./artifacts/$NAME-$TARGET-$TAG.tar.gz $EXEC
mv ./target/$TARGET/release/$EXEC ./artifacts/$EXEC
tar -czf ./artifacts/$NAME-$TARGET-$TAG.tar.gz -C ./artifacts $EXEC
- name: Archive artifact
uses: actions/upload-artifact@v2
with:
name: switch-desktop
name: vnt-cli
path: |
./artifacts
# deploys to github releases on tag
deploy:
if: startsWith(github.ref, 'refs/tags/')
@@ -139,11 +142,15 @@ jobs:
- name: Download artifacts
uses: actions/download-artifact@v2
with:
name: switch-desktop
name: vnt-cli
path: ./artifacts
- name: List
run: find ./artifacts
- name: Release
uses: softprops/action-gh-release@v1
uses: svenstaro/upload-release-action@v2
with:
files: ./artifacts/*.tar.gz
repo_token: ${{ secrets.YOURTOKEN }}
file: ./artifacts/*.tar.gz
tag: ${{ github.ref }}
overwrite: true
file_glob: true
-3
View File
@@ -1,3 +0,0 @@
[submodule "switch/p2p_channel"]
path = switch/p2p_channel
url = https://github.com/lbl8603/p2p_channel
+13 -1
View File
@@ -1,2 +1,14 @@
[workspace]
members = ["switch","switch-desktop"]
members = ["vnt","common","vnt-cli","vnt-jni"]
[profile.release]
opt-level = 'z'
debug = 0
debug-assertions = false
strip= "debuginfo"
overflow-checks = true
lto = true
panic = 'abort'
incremental = false
codegen-units = 1
rpath = false
+76 -35
View File
@@ -1,23 +1,23 @@
# switch
A virtual network tool (VPN)
# Vnt
A virtual network tool (VPN)
将不同网络下的多个设备虚拟到一个局域网下
### 示例:
### vnt-cli参数详解 [参数说明](https://github.com/lbl8603/vnt/blob/main/vnt-cli/README.md)
### 快速使用:
1. 指定一个token,在多台设备上运行该程序,例如:
```shell
# linux上
root@DESKTOP-0BCHNIO:/opt# ./switch-desktop start --token 123456
# 在另一台linux上使用nohup后台运行,不在命令行指定配置时,将在home/.switch/config文件中读取配置
[root@izj6cemne76ykdzkataftfz switch]# nohup ./switch-desktop start &
root@DESKTOP-0BCHNIO:/opt# ./vnt-cli -k 123456
# 在另一台linux上使用nohup后台运行
root@izj6cemne76ykdzkataftfz vnt# nohup ./vnt-cli -k 123456 &
# windows上
D:\switch\bin_v1>switch-desktop.exe start --token 123456
D:\vnt\bin_v1>vnt-cli.exe -k 123456
```
2. 可以执行status命令查看当前设备的虚拟ip
2. 可以执行info命令查看当前设备的虚拟ip
```shell
root@DESKTOP-0BCHNIO:/opt# ./switch-desktop status
root@DESKTOP-0BCHNIO:/opt# ./vnt-cli --info
Name: Ubuntu 18.04 (bionic) [64-bit]
Virtual ip: 10.26.0.2
Virtual gateway: 10.26.0.1
@@ -30,54 +30,95 @@
```
3. 也可以执行list命令查看其他设备的虚拟ip
```shell
root@DESKTOP-0BCHNIO:/opt# ./switch-desktop list
root@DESKTOP-0BCHNIO:/opt# ./vnt-cli --list
Name Virtual Ip P2P/Relay Rt Status
Windows 10.0.22621 (Windows 11 Professional) [64-bit] 10.26.0.3 p2p 2 Online
CentOS 7.9.2009 (Core) [64-bit] 10.26.0.4 p2p 35 Online
```
4. 最后可以用虚拟ip实现设备间相互访问
1. ping
<img width="506" alt="ping" src="https://raw.githubusercontent.com/lbl8603/switch/dev/documents/img/ping.jpg">
2. ssh
<img width="506" alt="ssh" src="https://raw.githubusercontent.com/lbl8603/switch/dev/documents/img/ssh.jpg">
<img width="506" alt="ssh" src="https://raw.githubusercontent.com/lbl8603/vnt/dev/documents/img/ssh.jpg">
5. 帮助,使用-h命令查看
### 更多玩法
1. 和远程桌面(如mstsc)搭配,超低延迟的体验
2. 安装samba服务,共享磁盘
3. 搭配公网服务器nginx反向代理,在公网访问本地文件
3. 搭配公网服务器nginx反向代理,在公网访问内网文件或服务
4. 点对网,访问内网其他机器、IP代理(结合启动参数'-i'和'-o')
### 使用须知
- token的作用是标识一个虚拟局域网,当使用公共服务器时,建议使用一个唯一值当token(比如uuid),否则有可能连接到其他人创建的虚拟局域网中
- 建议指定deviceId,默认使用MAC地址,在某些环境下可能发生变化
- 公共服务器目前的配置是2核4G 4Mbps,有需要再扩展~
- 默认使用公共服务器,目前的配置是2核4G 4Mbps,有需要再扩展~
- 需要root/管理员权限
- 使用命令行运行
- Mac和Linux下需要加可执行权限(例如:chmod +x ./switch-macos)
- 自己搭注册和中继服务器(https://github.com/lbl8603/switch-server)
- vnt-cli需要使用命令行运行
- Mac和Linux下需要加可执行权限(例如:chmod +x ./vnt-cli)
- 可以自己搭注册和中继服务器([server](https://github.com/lbl8603/vnts))
### 编译
前提条件:安装rust编译环境(https://www.rust-lang.org/zh-CN/tools/install)
到项目根目录下执行 cargo build -p switch-desktop
前提条件:安装rust编译环境([install rust](https://www.rust-lang.org/zh-CN/tools/install))
到项目根目录下执行 cargo build -p vnt-cli
### 支持平台
- Mac
- Linux
- Windows
- 依赖 wintun.dll(https://www.wintun.net/)
- 使用tun网卡 依赖wintun.dll([win-tun](https://www.wintun.net/))(将dll放到同目录下,建议使用版本0.14.1)
- 使用tap网卡 依赖tap-windows([win-tap](https://build.openvpn.net/downloads/releases/))(建议使用版本9.24.7)
- Android
- [SwitchApp](https://github.com/lbl8603/SwitchApp)
### 特性
- IP层数据转发
- tun虚拟网卡
- tun虚拟网卡
- tap虚拟网卡
- NAT穿透
- 点对点穿透
- 服务端中继转发
- 客户端中继转发
- 点对点穿透
- 服务端中继转发
- 客户端中继转发
- IP代理
- p2p组播/广播
- 客户端数据加密
### Todo
- 支持安卓
- 数据加密
- 桌面UI(测试中)
- 服务端数据加密
- 支持Ipv6
### 常见问题
<details> <summary>展开</summary>
#### 问题1: 设置网络地址失败
##### 可能原因:
vnt默认使用10.26.0.0/24网段,和本地网络适配器的ip冲突
##### 解决方法:
1. 方法一:找到冲突的IP,将其改成别的
2. 方法二:自建服务器,指定其他不会冲突的网段
3. 方法三:增加参数-d <device-id> ,设置不同的id会让服务端分配不同的IP,从而绕开有冲突的IP
#### 问题2: windows系统上wintun.dll加载失败
##### 可能原因:
没有下载wintun.dll 或者使用的wintun.dll有问题
##### 解决方法:
1. 下载最新版的wintun.dll [下载链接](https://www.wintun.net/builds/wintun-0.14.1.zip)
2. 解压后找到对应架构的目录,通常是amd64
3. 将对应的wintun.dll放到和vnt-cli同目录下(或者放到C盘Windows目录下)
4. 再次启动vnt-cli
</details>
### 交流群
QQ:1034868233
+8
View File
@@ -0,0 +1,8 @@
[package]
name = "common"
version = "1.1.1"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+50
View File
@@ -0,0 +1,50 @@
use std::net::Ipv4Addr;
pub fn ips_parse(ips: &Vec<String>) -> Result<Vec<(u32, u32, Ipv4Addr)>, String> {
let mut in_ips_c = vec![];
for x in ips {
let mut split = x.split(",");
let net = if let Some(net) = split.next() {
net
} else {
return Err("ipv4/mask,ipv4".to_string());
};
let ip = if let Some(ip) = split.next() {
ip
} else {
return Err("ipv4/mask,ipv4".to_string());
};
let ip = if let Ok(ip) = ip.parse::<Ipv4Addr>() {
ip
} else {
return Err("not ipv4".to_string());
};
let mut split = net.split("/");
let dest = if let Some(dest) = split.next() {
dest
} else {
return Err("no ipv4/mask".to_string());
};
let mask = if let Some(mask) = split.next() {
mask
} else {
return Err("no netmask".to_string());
};
let dest = if let Ok(dest) = dest.parse::<Ipv4Addr>() {
dest
} else {
return Err("not ipv4".to_string());
};
let mask = if let Ok(m) = mask.parse::<u32>() {
let mut mask = 0 as u32;
for i in 0..m {
mask = mask | (1 << (31 - i));
}
mask
} else {
return Err("not netmask".to_string());
};
in_ips_c.push((u32::from_be_bytes(dest.octets()), mask, ip));
}
Ok(in_ips_c)
}
+68
View File
@@ -0,0 +1,68 @@
use std::process::Command;
#[cfg(target_os = "windows")]
pub fn get_unique_identifier() -> Option<String> {
use std::os::windows::process::CommandExt;
let output = match Command::new("wmic")
.creation_flags(0x08000000)
.args(&["csproduct", "get", "UUID"])
.output() {
Ok(output) => { output }
Err(_) => {
return None;
}
};
let result = String::from_utf8_lossy(&output.stdout);
let identifier = result.lines().nth(1).unwrap_or("").trim();
if identifier.is_empty() {
None
} else {
Some(identifier.to_string())
}
}
#[cfg(target_os = "macos")]
pub fn get_unique_identifier() -> Option<String> {
let output = match Command::new("ioreg")
.args(&["-rd1", "-c", "IOPlatformExpertDevice"])
.output() {
Ok(output) => { output }
Err(_) => {
return None;
}
};
let result = String::from_utf8_lossy(&output.stdout);
let identifier = result
.lines()
.find(|line| line.contains("IOPlatformUUID"))
.unwrap_or("").trim();
if identifier.is_empty() {
None
} else {
Some(identifier.to_string())
}
}
#[cfg(target_os = "linux")]
pub fn get_unique_identifier() -> Option<String> {
let output = match Command::new("dmidecode")
.arg("-s")
.arg("system-uuid")
.output() {
Ok(output) => { output }
Err(_) => {
return None;
}
};
let result = String::from_utf8_lossy(&output.stdout);
let identifier = result.trim().to_string();
if identifier.is_empty() {
None
} else {
Some(identifier.to_string())
}
}
+2
View File
@@ -0,0 +1,2 @@
pub mod identifier;
pub mod args_parse;
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

-59
View File
@@ -1,59 +0,0 @@
use std::io;
use console::style;
use crate::console_out;
pub mod client;
pub mod server;
pub mod entity;
pub enum CommandEnum {
Route,
List,
ListAll,
Status,
#[cfg(any(unix))]
Stop,
}
pub fn command(cmd: CommandEnum) {
if let Err(e) = command_(cmd) {
println!("{}:{:?}", style("连接后台服务错误(Connection background service error)").red(), e);
}
}
fn command_(cmd: CommandEnum) -> io::Result<()> {
match client::CommandClient::new() {
Ok(command_client) => {
match cmd {
CommandEnum::Route => {
let list = command_client.route()?;
console_out::console_route_table(list);
}
CommandEnum::List => {
let list = command_client.list()?;
console_out::console_device_list(list);
}
CommandEnum::ListAll => {
let list = command_client.list()?;
console_out::console_device_list_all(list);
}
CommandEnum::Status => {
let status = command_client.status()?;
console_out::console_status(status);
}
#[cfg(any(unix))]
CommandEnum::Stop => {
command_client.stop()?;
}
}
}
Err(e) => {
log::error!("{:?}",e);
println!(
"{}:{:?}",
style("连接后台服务错误(Connection background service error)").red(), e
);
}
};
Ok(())
}
-189
View File
@@ -1,189 +0,0 @@
use std::io;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
use std::sync::Arc;
use switch::core::Switch;
use crate::command::entity::{DeviceItem, RouteItem, Status};
pub struct CommandServer {}
impl CommandServer {
pub fn new() -> Self {
Self {}
}
}
impl CommandServer {
pub fn start(&self, switch: Arc<Switch>) -> io::Result<()> {
let mut port = 21637 as u16;
let udp = loop {
match UdpSocket::bind(SocketAddr::V4(SocketAddrV4::new(
Ipv4Addr::new(127, 0, 0, 1),
port,
))) {
Ok(udp) => {
break udp;
}
Err(e) => {
if e.kind() == io::ErrorKind::AddrInUse {
port += 1;
} else {
log::error!("创建udp失败 {:?}", e);
return Err(e);
}
}
}
};
crate::config::update_command_port(port)?;
let mut buf = [0u8; 64];
loop {
let (len, addr) = udp.recv_from(&mut buf)?;
match std::str::from_utf8(&buf[..len]) {
Ok(cmd) => {
if let Ok(out) = command(cmd, &switch) {
udp.send_to(out.as_bytes(), addr)?;
}
}
Err(e) => {
log::warn!("{:?}", e);
}
}
}
}
}
pub fn command_route(switch: &Switch) -> Vec<RouteItem> {
let route_table = switch.route_table();
let mut route_list = Vec::with_capacity(route_table.len());
for (destination, route) in route_table {
let next_hop = switch.route_key(&route.route_key()).map_or(String::new(), |v| v.to_string());
let metric = route.metric.to_string();
let rt = if route.rt < 0 {
"".to_string()
} else {
route.rt.to_string()
};
let interface = route.addr.to_string();
let item = RouteItem {
destination: destination.to_string(),
next_hop,
metric,
rt,
interface,
};
route_list.push(item);
}
route_list
}
pub fn command_list(switch: &Switch) -> Vec<DeviceItem> {
let device_list = switch.device_list();
let mut list = Vec::new();
for peer in device_list {
let name = peer.name;
let virtual_ip = peer.virtual_ip.to_string();
let (nat_type, public_ips, local_ip) = if let Some(nat_info) = switch.peer_nat_info(&peer.virtual_ip) {
let nat_type = format!("{:?}", nat_info.nat_type);
let public_ips: Vec<String> = nat_info.public_ips.iter().map(|v| v.to_string()).collect();
let public_ips = public_ips.join(",");
let local_ip = nat_info.local_ip.to_string();
(nat_type, public_ips, local_ip)
} else {
("".to_string(), "".to_string(), "".to_string())
};
let (nat_traversal_type, rt) = if let Some(route) = switch.route(&peer.virtual_ip) {
let nat_traversal_type = if route.metric == 1 { "p2p" } else { "relay" }.to_string();
let rt = if route.rt < 0 {
"".to_string()
} else {
route.rt.to_string()
};
(nat_traversal_type, rt)
} else {
("relay".to_string(), "".to_string())
};
let status = format!("{:?}", peer.status);
let item = DeviceItem {
name,
virtual_ip,
nat_type,
public_ips,
local_ip,
nat_traversal_type,
rt,
status,
};
list.push(item);
}
list
}
pub fn command_status(switch: &Switch) -> Status {
let current_device = switch.current_device();
let nat_info = switch.nat_info();
let name = switch.name().to_string();
let virtual_ip = current_device.virtual_ip().to_string();
let virtual_gateway = current_device.virtual_gateway().to_string();
let virtual_netmask = current_device.virtual_netmask.to_string();
let connect_status = format!("{:?}", switch.connection_status());
let relay_server = current_device.connect_server.to_string();
let nat_type = format!("{:?}", nat_info.nat_type);
let public_ips: Vec<String> = nat_info.public_ips.iter().map(|v| v.to_string()).collect();
let public_ips = public_ips.join(",");
let local_ip = nat_info.local_ip.to_string();
Status {
name,
virtual_ip,
virtual_gateway,
virtual_netmask,
connect_status,
relay_server,
nat_type,
public_ips,
local_ip,
}
}
fn command(cmd: &str, switch: &Switch) -> io::Result<String> {
let out_str = match cmd {
"route" => {
match serde_json::to_string(&command_route(switch)) {
Ok(str) => {
str
}
Err(e) => {
format!("{:?}", e)
}
}
}
"list" => {
match serde_json::to_string(&command_list(switch)) {
Ok(str) => {
str
}
Err(e) => {
format!("{:?}", e)
}
}
}
"status" => {
match serde_json::to_string(&command_status(switch)) {
Ok(str) => {
str
}
Err(e) => {
format!("{:?}", e)
}
}
}
"stop" => {
switch.stop()?;
"stopping".to_string()
}
_ => {
format!("command '{}' not fount. \n Try to enter: 'help'\n", cmd)
}
};
Ok(out_str)
}
-50
View File
@@ -1,50 +0,0 @@
use std::io;
use crate::config::SWITCH_HOME_PATH;
#[cfg(target_os = "windows")]
pub fn log_service_init() -> io::Result<()> {
log_init_("switch-service.log")
}
pub fn log_init() -> io::Result<()> {
log_init_("switch.log")
}
pub fn log_init_(file_name:&str) -> io::Result<()> {
let home = SWITCH_HOME_PATH.lock().clone();
let home = if let Some(home) = home {
home
} else {
return Err(io::Error::new(io::ErrorKind::Other, "not found"));
};
if !home.exists() {
std::fs::create_dir(&home)?;
}
let stderr = log4rs::append::console::ConsoleAppender::builder()
.target(log4rs::append::console::Target::Stderr)
.build();
let logfile = log4rs::append::file::FileAppender::builder()
// Pattern: https://docs.rs/log4rs/*/log4rs/encode/pattern/index.html
.encoder(Box::new(log4rs::encode::pattern::PatternEncoder::new(
"{d(%+)(utc)} [{f}:{L}] {h({l})} {M}:{m}{n}\n",
)))
.build(home.join(file_name))?;
match log4rs::Config::builder()
.appender(log4rs::config::Appender::builder().build("logfile", Box::new(logfile)))
.appender(
log4rs::config::Appender::builder()
.filter(Box::new(log4rs::filter::threshold::ThresholdFilter::new(
log::LevelFilter::Error,
)))
.build("stderr", Box::new(stderr)),
)
.build(
log4rs::config::Root::builder()
.appender("logfile")
.appender("stderr")
.build(log::LevelFilter::Info),
) {
Ok(config) => {
let _ = log4rs::init_config(config);
}
Err(_) => {}
}
Ok(())
}
-262
View File
@@ -1,262 +0,0 @@
use std::fs::{File, OpenOptions};
use std::io;
use std::io::{Read, Write};
use std::net::{SocketAddr, ToSocketAddrs};
use std::path::PathBuf;
use lazy_static::lazy_static;
use parking_lot::Mutex;
use serde::{Deserialize, Serialize};
use crate::StartArgs;
pub mod log_config;
pub struct StartConfig {
pub name: String,
pub token: String,
pub server: SocketAddr,
pub nat_test_server: Vec<SocketAddr>,
pub device_id: String,
}
pub fn default_config(start_args: StartArgs) -> Result<StartConfig, String> {
let args_config = read_config();
if args_config.is_none() && start_args.token.is_none() {
return Err("找不到token(Token not found)".to_string());
}
let token = start_args.token.unwrap_or_else(|| args_config.as_ref().unwrap().token.clone()).trim().to_string();
if token.is_empty() {
return Err("token不能为空(Token cannot be empty)".to_string());
}
if token.len() > 64 {
return Err("token不能超过64字符(Token cannot exceed 64 characters)".to_string());
}
let name = start_args.name.unwrap_or_else(|| {
if let Some(c) = &args_config {
if !c.name.is_empty() {
return c.name.clone();
}
}
os_info::get().to_string()
});
let name = name.trim();
let name = if name.len() > 64 {
name[..64].to_string()
} else {
name.to_string()
};
let device_id = start_args.device_id.unwrap_or_else(|| {
if let Some(c) = &args_config {
if !c.device_id.is_empty() {
return c.device_id.clone();
}
}
if let Ok(Some(mac_address)) = mac_address::get_mac_address() {
mac_address.to_string()
} else {
"".to_string()
}
});
if device_id.is_empty() || device_id.len() > 64 {
return Err("设备id不能为空并且长度不能大于64字符(The device id cannot be empty and the length cannot be greater than 64 characters)".to_string());
}
let server = match start_args.server.unwrap_or_else(|| {
if let Some(c) = &args_config {
if !c.server.is_empty() {
return c.server.clone();
}
}
"nat1.wherewego.top:29871".to_string()
}).to_socket_addrs() {
Ok(mut server) => {
if let Some(addr) = server.next() {
addr
} else {
return Err("中继服务器地址错误( Relay server address error)".to_string());
}
}
Err(e) => {
return Err(format!("中继服务器地址错误( Relay server address error) :{:?}", e));
}
};
let nat_test_server = start_args.nat_test_server.unwrap_or_else(|| {
if let Some(c) = &args_config {
if !c.nat_test_server.is_empty() {
return c.nat_test_server.join(",");
}
}
"nat1.wherewego.top:35061,nat1.wherewego.top:35062,nat2.wherewego.top:35061,nat2.wherewego.top:35062".to_string()
}).split(",").flat_map(|a| a.to_socket_addrs()).flatten()
.collect::<Vec<_>>();
if nat_test_server.is_empty() {
return Err("NAT检测服务地址错误(NAT detection service address error)".to_string());
}
let base_config = StartConfig {
name,
token,
server,
nat_test_server,
device_id,
};
Ok(base_config)
}
lazy_static! {
static ref CONFIG: Mutex<Option<ArgsConfig>> = Mutex::new(None);
pub static ref SWITCH_HOME_PATH: Mutex<Option<PathBuf>> = Mutex::new(None);
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ArgsConfig {
#[serde(default = "default_version")]
pub version: String,
#[serde(default = "default_str")]
pub token: String,
#[serde(default = "default_str")]
pub name: String,
pub command_port: Option<u16>,
#[serde(default = "default_str")]
pub server: String,
#[serde(default = "default_resource_vec")]
pub nat_test_server: Vec<String>,
#[serde(default = "default_str")]
pub device_id: String,
#[serde(default = "default_pid")]
pub pid: u32,
}
fn default_version() -> String {
"1.0".to_string()
}
fn default_str() -> String {
"".to_string()
}
fn default_resource_vec() -> Vec<String> {
vec![]
}
fn default_pid() -> u32 {
0
}
impl ArgsConfig {
pub fn new(token: String, name: String, server: String, nat_test_server: Vec<String>, device_id: String) -> Self {
Self {
version: "1.0".to_string(),
token,
name,
command_port: None,
server,
nat_test_server,
device_id,
pid: 0,
}
}
}
pub fn lock_file() -> io::Result<File> {
let path = SWITCH_HOME_PATH.lock().clone().unwrap().join(".lock");
Ok(File::create(path)?)
}
pub fn save_config(config: ArgsConfig) -> io::Result<()> {
let config_path = SWITCH_HOME_PATH.lock().clone().unwrap().join("config");
save_config_(config, config_path)
}
fn save_config_(config: ArgsConfig, config_path: PathBuf) -> io::Result<()> {
let mut config_lock = CONFIG.lock();
config_lock.take();
let str = serde_yaml::to_string(&config).unwrap();
let mut file = File::create(config_path)?;
file.write_all(str.as_bytes())
}
pub fn update_pid(pid: u32) -> io::Result<()> {
let home_lock = SWITCH_HOME_PATH.lock();
if let Some(home) = home_lock.clone() {
drop(home_lock);
let config_path = home.join("config");
if let Some(mut config) = read_config() {
config.pid = pid;
return save_config_(config, config_path);
}
}
Err(io::Error::new(io::ErrorKind::Other, "not found"))
}
#[cfg(any(unix))]
pub fn read_pid() -> io::Result<u32> {
let home = SWITCH_HOME_PATH.lock().clone().unwrap();
let config = read_config_(home)?;
Ok(config.pid)
}
pub fn update_command_port(port: u16) -> io::Result<()> {
let home_lock = SWITCH_HOME_PATH.lock();
if let Some(home) = home_lock.clone() {
drop(home_lock);
let config_path = home.join("config");
if let Some(mut config) = read_config() {
config.command_port = Some(port);
return save_config_(config, config_path);
}
}
Err(io::Error::new(io::ErrorKind::Other, "not found"))
}
pub fn read_command_port() -> io::Result<u16> {
let home = SWITCH_HOME_PATH.lock().clone().unwrap();
let config = read_config_(home)?;
if let Some(p) = config.command_port {
Ok(p)
} else {
Err(io::Error::new(io::ErrorKind::Other, "not fount config"))
}
}
pub fn read_config() -> Option<ArgsConfig> {
let mut lock = CONFIG.lock();
let c = lock.clone();
if c.is_some() {
return c;
}
if let Some(home) = SWITCH_HOME_PATH.lock().clone() {
match read_config_(home.to_path_buf()) {
Ok(config) => {
lock.replace(config.clone());
Some(config)
}
Err(e) => {
log::error!("{:?},path:{:?}", e,home);
None
}
}
} else {
None
}
}
pub fn set_home(home: PathBuf) {
SWITCH_HOME_PATH.lock().replace(home);
}
fn read_config_(home: PathBuf) -> io::Result<ArgsConfig> {
let config_path = home.join("config");
let mut file = if config_path.exists() {
File::open(config_path)?
} else {
OpenOptions::new().read(true).write(true).truncate(false).create(true).open(config_path)?
};
let mut str = String::new();
file.read_to_string(&mut str)?;
match serde_yaml::from_str::<ArgsConfig>(&str) {
Ok(config) => Ok(config),
Err(e) => {
log::warn!("{:?}", e);
Err(io::Error::new(io::ErrorKind::Other, "config error"))
}
}
}
-237
View File
@@ -1,237 +0,0 @@
use std::thread;
use std::time::Duration;
use clap::{Parser, Subcommand};
use console::style;
use switch::core::Switch;
use crate::config::log_config::log_init;
mod command;
mod config;
#[cfg(target_os = "windows")]
mod windows;
#[cfg(any(unix))]
mod unix;
mod console_out;
#[derive(Parser, Debug)]
#[command(
author = "Lu Beilin",
version,
about = "一个虚拟网络工具,启动后会获取一个ip,相同token下的设备之间可以用ip直接通信"
)]
pub struct BaseArgs {
#[clap(subcommand)]
command: Commands,
}
#[derive(Subcommand, Debug)]
enum Commands {
/// 启动
Start(StartArgs),
/// 停止后台服务
Stop,
/// 安装服务
/// Install service
#[cfg(target_os = "windows")]
Install(InstallArgs),
/// 卸载服务
/// Uninstall service
#[cfg(target_os = "windows")]
Uninstall,
/// 配置
#[cfg(target_os = "windows")]
Config(ConfigArgs),
/// 查看路由
/// View route
Route,
/// 查看设备列表
/// View device list
List {
/// 查看所有
#[arg(short, long)]
all: bool
},
/// 查看设备当前状态
/// View the current status of the device
Status,
}
#[derive(Parser, Debug)]
pub struct StartArgs {
/// 不超过64个字符
/// 相同token的设备之间才能通信。
/// 建议使用uuid保证唯一性。
/// No more than 64 characters
/// Only devices with the same token can communicate with each other.
/// It is recommended to use uuid to ensure uniqueness
#[arg(long)]
token: Option<String>,
/// 给设备一个名称,为空时默认用系统版本信息
/// Give the device a name. If it is blank, the system version information will be used by default
#[arg(long, action)]
name: Option<String>,
/// 设备唯一标识,为空时默认使用MAC地址,不超过64个字符
/// Unique identification of the device. If it is blank, the MAC address is used by default. No more than 64 characters
#[arg(long)]
device_id: Option<String>,
/// 注册和中继服务器地址
/// Register and relay server address
#[arg(long)]
server: Option<String>,
/// NAT检测服务地址,使用逗号分隔
/// NAT detection service address. Use comma to separate
#[arg(long)]
nat_test_server: Option<String>,
/// 关闭命令服务,关闭后不能在其他进程直接使用route、list等命令查看信息
/// Turn off the command service. After turning off, you cannot directly use the route, list and other commands to view information in other processes
#[cfg(any(unix))]
#[arg(long)]
off_command_server: bool,
/// 记录日志,输出在 home/.switch 目录下,长时间使用时不建议开启
/// Output the log in the "home/.switch" directory
#[arg(long)]
log: bool,
}
#[cfg(target_os = "windows")]
#[derive(Parser, Debug)]
pub struct InstallArgs {
/// 安装路径
/// Service installation path
#[arg(long)]
path: String,
/// 服务开机自启动
/// Autostart on system startup
#[arg(long)]
auto: bool,
}
#[cfg(target_os = "windows")]
#[derive(Parser, Debug)]
pub struct ConfigArgs {
/// 服务开机自启动
/// Autostart on system startup
#[arg(long)]
auto: bool,
}
#[cfg(windows)]
fn main() {
let args: Vec<_> = std::env::args().collect();
if args.len() == 3 && args[1] == windows::SERVICE_FLAG {
//以服务的方式启动
config::set_home(std::path::PathBuf::from(&args[2]));
windows::service::start();
return;
} else {
let home = dirs::home_dir().unwrap().join(".switch");
config::set_home(home);
let args = BaseArgs::parse();
if let Commands::Start(start_args) = &args.command {
if start_args.log {
let _ = log_init();
}
}
windows::main0(args);
}
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
fn main() {
let home = dirs::home_dir().unwrap().join(".switch");
config::set_home(home);
let args = BaseArgs::parse();
if let Commands::Start(start_args) = &args.command {
if start_args.log {
let _ = log_init();
}
}
unix::main0(args);
}
pub fn console_listen(switch: &Switch) {
use console::Term;
let term = Term::stdout();
println!("{}", style("启动成功 started").green());
let current_device = switch.current_device();
println!(
"当前虚拟ip(virtual ip): {:?}",
style(current_device.virtual_ip()).green()
);
println!(
"虚拟网关(virtual gateway): {:?}",
style(current_device.virtual_gateway()).green()
);
loop {
println!(
"{}",
style("Please enter the command (Usage: list,status,exit,help):").color256(102)
);
match term.read_line() {
Ok(cmd) => {
if cmd.is_empty() {
log::warn!("非正常返回");
return;
}
if command(cmd.trim(), &switch).is_err() {
println!("{}", style("stopping").red());
if let Err(e) = switch.stop() {
println!("stop:{:?}", e);
}
thread::sleep(Duration::from_secs(2));
break;
}
}
Err(e) => {
log::error!("read_line:{:?}", e);
println!("{}", style("stopping...").red());
if let Err(e) = switch.stop() {
log::error!("stop:{:?}", e);
}
std::thread::sleep(std::time::Duration::from_secs(1));
break;
}
}
}
println!("{}", style("stopped").red());
}
fn command(cmd: &str, switch: &Switch) -> Result<(), ()> {
match cmd {
"route" => {
let list = command::server::command_route(switch);
console_out::console_route_table(list);
}
"list" => {
let list = command::server::command_list(switch);
console_out::console_device_list(list);
}
"status" => {
let status = command::server::command_status(switch);
console_out::console_status(status);
}
"help" | "h" => {
println!("Options: ");
println!(
"{} , Query the virtual IP of other devices",
style("list").green()
);
println!("{} , View current device status", style("status").green());
println!("{} , Exit the program", style("exit").green());
}
"exit" => {
return Err(());
}
_ => {
println!("command '{}' not fount. ", style(cmd).red());
println!("Try to enter: '{}'", style("help").green());
}
}
Ok(())
}
-137
View File
@@ -1,137 +0,0 @@
use std::sync::Arc;
use console::style;
use fs2::FileExt;
use switch::core::{Config, Switch};
use crate::{BaseArgs, Commands, config};
use crate::command::{command, CommandEnum};
pub fn main0(base_args: BaseArgs) {
match base_args.command {
Commands::Start(args) => {
let off_command_server = args.off_command_server;
match config::default_config(args) {
Ok(start_config) => {
if sudo::RunningAs::Root != sudo::check() {
println!(
"{}",
style("需要使用root权限执行(Need to execute with root permission)...").red()
);
sudo::escalate_if_needed().unwrap();
}
let config = Config::new(
start_config.token.clone(),
start_config.device_id.clone(),
start_config.name.clone(),
start_config.server,
start_config.nat_test_server.clone(),
);
let nat_test_server = start_config.nat_test_server.iter().map(|v| v.to_string()).collect::<Vec<String>>();
let args_config = config::ArgsConfig::new(
start_config.token.clone(),
start_config.name.clone(),
start_config.server.to_string(),
nat_test_server,
start_config.device_id.clone(),
);
let lock = match config::lock_file() {
Ok(lock) => {
lock
}
Err(e) => {
log::error!("{:?}",e);
return;
}
};
if lock.try_lock_exclusive().is_err() {
println!("{}", style("文件被重复打开").red());
return;
}
if let Err(e) = config::save_config(args_config) {
log::error!("{:?}",e);
lock.unlock().unwrap();
return;
}
let switch = match Switch::start(config) {
Ok(switch) => {
switch
}
Err(e) => {
log::error!("{:?}", e);
lock.unlock().unwrap();
return;
}
};
let switch = Arc::new(switch);
let command_server = crate::command::server::CommandServer::new();
if off_command_server {
crate::console_listen(&switch);
log::info!("前台任务结束");
} else {
if let Err(e) = config::update_pid(std::process::id()) {
log::error!("{:?}", e);
}
let switch1 = switch.clone();
let handle = std::thread::Builder::new().name("cmd-server".into()).spawn(move || {
if let Err(e) = command_server.start(switch1) {
log::error!("{:?}", e);
}
}).unwrap();
crate::console_listen(&switch);
if let Err(e) = handle.join() {
log::error!("后台任务异常{:?}",e);
} else {
log::info!("后台任务结束");
}
}
lock.unlock().unwrap();
}
Err(e) => {
log::error!("{:?}", e);
}
}
}
Commands::Stop => {
if sudo::RunningAs::Root != sudo::check() {
println!(
"{}",
style("需要使用root权限执行(Need to execute with root permission)...").red()
);
sudo::escalate_if_needed().unwrap();
}
command(CommandEnum::Stop);
if let Ok(pid) = config::read_pid() {
if pid != 0 {
let kill_cmd = format!("kill {}", pid);
let kill_out = std::process::Command::new("sh")
.arg("-c")
.arg(&kill_cmd)
.output()
.expect("sh exec error!");
if !kill_out.status.success() {
println!("cmd:{:?},err:{:?}", kill_cmd, kill_out);
return;
}
}
}
println!("stopped")
}
Commands::Route => {
command(CommandEnum::Route);
}
Commands::List { all } => {
if all {
command(CommandEnum::ListAll);
} else {
command(CommandEnum::List);
}
}
Commands::Status => {
command(CommandEnum::Status);
}
}
}
-358
View File
@@ -1,358 +0,0 @@
use std::{io, thread};
use std::ffi::OsString;
use std::net::UdpSocket;
use std::path::PathBuf;
use std::time::Duration;
use console::style;
use fs2::FileExt;
use windows_service::Error;
use windows_service::service::{
ServiceAccess, ServiceErrorControl, ServiceInfo, ServiceStartType, ServiceState, ServiceType,
};
use windows_service::service_manager::{ServiceManager, ServiceManagerAccess};
use switch::core::{Config, Switch};
use crate::{BaseArgs, Commands, config};
use crate::command::{command, CommandEnum};
pub mod service;
mod windows_admin_check;
pub const SERVICE_FLAG: &'static str = "start_switch_service_v1_";
pub const SERVICE_NAME: &'static str = "switch-service-v1";
pub const SERVICE_TYPE: ServiceType = ServiceType::OWN_PROCESS;
fn admin_check() -> bool {
if !windows_admin_check::is_app_elevated() {
println!(
"{}",
style("请使用管理员权限运行(Please run with administrator privileges)").red()
);
true
} else {
false
}
}
fn not_started() -> bool {
match service_state() {
Ok(state) => {
if state == ServiceState::Running {
return false;
} else {
println!("服务未启动")
}
}
Err(e) => {
println!("{:?}", e);
}
}
return true;
}
pub fn main0(base_args: BaseArgs) {
match base_args.command {
Commands::Start(args) => {
if admin_check() {
return;
}
{
// 允许应用通过防火墙
let _udp = UdpSocket::bind("0.0.0.0:0").unwrap();
}
let out_log = args.log;
match config::default_config(args) {
Ok(start_config) => {
match service_state() {
Ok(state) => {
if state == ServiceState::Stopped {
if let Err(e) = config::save_config(config::ArgsConfig::new(
start_config.token.clone(),
start_config.name.clone(),
start_config.server.to_string(),
start_config.nat_test_server.iter().map(|v| v.to_string()).collect::<Vec<String>>(),
start_config.device_id.clone(),
)) {
log::error!("{:?}",e);
return;
}
match start(out_log) {
Ok(_) => {
//需要检查启动状态
thread::sleep(Duration::from_secs(2));
println!("{}", style("启动成功(Start successfully)").green())
}
Err(e) => {
log::error!("{:?}", e);
}
}
} else {
println!("服务未停止(Service not stopped)");
}
}
Err(e) => {
match e {
Error::Winapi(ref e) => {
if let Some(code) = e.raw_os_error() {
if code == 1060 {
//指定的服务未安装。
println!(
"{}",
style("服务未安装,在当前进程启动(The service is not installed and started in the current process)").red()
);
let config = Config::new(
start_config.token,
start_config.device_id,
start_config.name,
start_config.server,
start_config.nat_test_server,
);
let lock = match config::lock_file() {
Ok(lock) => {
lock
}
Err(e) => {
log::error!("{:?}",e);
return;
}
};
if lock.try_lock_exclusive().is_err() {
println!("{}", style("文件被重复打开").red());
return;
}
match Switch::start(config) {
Ok(switch) => {
crate::console_listen(&switch);
}
Err(e) => {
log::error!("{:?}", e);
}
}
lock.unlock().unwrap();
return;
}
}
}
_ => {}
}
println!("{:?}", e);
}
}
}
Err(e) => {
println!("{}", style(e).red());
}
};
pause();
}
Commands::Stop => {
if not_started() {
return;
}
if admin_check() {
return;
}
match stop() {
Ok(_) => {
println!("{}", style("停止成功(Stopped successfully)").green())
}
Err(e) => {
log::error!("{:?}", e);
}
}
pause();
}
Commands::Install(args) => {
if admin_check() {
return;
}
let path: PathBuf = args.path.into();
if !path.exists() {
std::fs::create_dir_all(&path).unwrap();
}
if !path.is_dir() {
println!("参数必须为文件目录(Parameter must be a file directory)");
} else {
if let Err(e) = install(path, args.auto) {
log::error!("{:?}", e);
} else {
println!("{}", style("安装成功(Installation succeeded)").green())
}
}
pause();
}
Commands::Uninstall => {
if admin_check() {
return;
}
if let Err(e) = uninstall() {
log::error!("{:?}", e);
} else {
println!("{}", style("卸载成功(Uninstall succeeded)").green())
}
pause();
}
Commands::Config(args) => {
if let Err(e) = change(args.auto) {
log::error!("{:?}", e);
} else {
println!("{}", style("配置成功(Config succeeded)").green())
}
pause();
}
Commands::Route => {
if not_started() {
return;
}
command(CommandEnum::Route);
}
Commands::List { all } => {
if not_started() {
return;
}
if all {
command(CommandEnum::ListAll);
} else {
command(CommandEnum::List);
}
}
Commands::Status => {
if not_started() {
return;
}
command(CommandEnum::Status);
}
}
}
fn pause() {
println!(
"{}",
style("按任意键退出(Press any key to exit)...").green()
);
use console::Term;
let term = Term::stdout();
let _ = term.read_char().unwrap();
}
fn install(path: PathBuf, auto: bool) -> Result<(), Error> {
let manager_access = ServiceManagerAccess::CONNECT | ServiceManagerAccess::CREATE_SERVICE;
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
let current_exe_path = std::env::current_exe().unwrap();
let service_path = path.join("switch-service-v1.exe");
std::fs::copy(current_exe_path, service_path.as_path()).unwrap();
if let Err(e) = std::fs::copy("wintun.dll", path.join("wintun.dll").as_path()) {
if e.kind() == io::ErrorKind::NotFound {
println!("Not fount 'wintun.dll'. Please put 'wintun.dll' in the current directory");
std::process::exit(0);
} else {
panic!("{:?}", e)
}
}
let mut launch_arguments = Vec::new();
launch_arguments.push(OsString::from(SERVICE_FLAG));
launch_arguments.push(OsString::from(
dirs::home_dir().unwrap().join(".switch").to_str().unwrap(),
));
let start_type = if auto {
ServiceStartType::AutoStart
} else {
ServiceStartType::OnDemand
};
let service_info = ServiceInfo {
name: OsString::from(SERVICE_NAME),
display_name: OsString::from("switch service v1"),
service_type: SERVICE_TYPE,
start_type,
error_control: ServiceErrorControl::Normal,
executable_path: service_path.into(),
launch_arguments,
dependencies: vec![],
account_name: None, // run as System
account_password: None,
};
let service = service_manager.create_service(&service_info, ServiceAccess::CHANGE_CONFIG)?;
service.set_description("A VPN")?;
Ok(())
}
fn change(auto: bool) -> Result<(), Error> {
let manager_access = ServiceManagerAccess::CONNECT;
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
let service_access = ServiceAccess::QUERY_CONFIG | ServiceAccess::CHANGE_CONFIG;
let service = service_manager.open_service(SERVICE_NAME, service_access)?;
let config = service.query_config()?;
let start_type = if auto {
ServiceStartType::AutoStart
} else {
ServiceStartType::OnDemand
};
let mut launch_arguments = Vec::new();
launch_arguments.push(OsString::from(SERVICE_FLAG));
launch_arguments.push(OsString::from(
dirs::home_dir().unwrap().join(".switch").to_str().unwrap(),
));
let service_info = ServiceInfo {
name: OsString::from(SERVICE_NAME),
display_name: config.display_name,
service_type: SERVICE_TYPE,
start_type,
error_control: config.error_control,
executable_path: config.executable_path,
launch_arguments,
dependencies: config.dependencies,
account_name: None, // run as System
account_password: None,
};
service.change_config(&service_info)?;
Ok(())
}
fn uninstall() -> Result<(), Error> {
let manager_access = ServiceManagerAccess::CONNECT;
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
let service_access = ServiceAccess::QUERY_STATUS | ServiceAccess::STOP | ServiceAccess::DELETE;
let service = service_manager.open_service(SERVICE_NAME, service_access)?;
let service_status = service.query_status()?;
if service_status.current_state != ServiceState::Stopped {
service.stop()?;
// Wait for service to stop
thread::sleep(Duration::from_secs(1));
}
service.delete()?;
Ok(())
}
fn start(out_log: bool) -> Result<(), Error> {
let manager_access = ServiceManagerAccess::CONNECT;
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
let service = service_manager.open_service(SERVICE_NAME, ServiceAccess::START)?;
if out_log {
service.start(&["log"])
} else {
service.start(&[""])
}
}
fn service_state() -> Result<ServiceState, Error> {
let manager_access = ServiceManagerAccess::CONNECT;
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
let service_access = ServiceAccess::QUERY_STATUS;
let service = service_manager.open_service(SERVICE_NAME, service_access)?;
let service_status = service.query_status()?;
return Ok(service_status.current_state);
}
fn stop() -> Result<(), Error> {
let manager_access = ServiceManagerAccess::CONNECT;
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
let service = service_manager.open_service(SERVICE_NAME, ServiceAccess::STOP)?;
service.stop()?;
Ok(())
}
-143
View File
@@ -1,143 +0,0 @@
// #[macro_use]
// extern crate windows_service;
use std::ffi::OsString;
use std::net::ToSocketAddrs;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use windows_service::{define_windows_service, service_control_handler, service_dispatcher};
use windows_service::service::{
ServiceControl, ServiceControlAccept, ServiceExitCode, ServiceState, ServiceStatus,
};
use windows_service::service_control_handler::ServiceControlHandlerResult;
use switch::core::{Config, Switch};
use crate::config;
use crate::windows::config::read_config;
use crate::windows::SERVICE_NAME;
define_windows_service!(ffi_service_main, switch_service_main);
pub fn switch_service_main(arguments: Vec<OsString>) {
if !arguments.is_empty() {
if let Some(str) = arguments[0].to_str() {
if str == "log" {
let _ = config::log_config::log_service_init();
}
}
}
thread::spawn(|| match service_main() {
Ok(_) => {}
Err(e) => {
log::error!("{:?}", e);
}
});
}
fn service_main() -> windows_service::Result<()> {
let parker = crossbeam::sync::Parker::new();
let un_parker = parker.unparker().clone();
let event_handler = move |control_event| -> ServiceControlHandlerResult {
match control_event {
// Notifies a service to report its current status information to the service
// control manager. Always return NoError even if not implemented.
ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
// Handle stop
ServiceControl::Stop => {
un_parker.unpark();
log::info!("handler 服务停止");
ServiceControlHandlerResult::NoError
}
_ => ServiceControlHandlerResult::NotImplemented,
}
};
// Register system service event handler.
// The returned status handle should be used to report service status changes to the system.
let status_handle =
service_control_handler::register(SERVICE_NAME, event_handler)?;
// Tell the system that service is running
status_handle.set_service_status(ServiceStatus {
service_type: crate::windows::SERVICE_TYPE,
current_state: ServiceState::Running,
controls_accepted: ServiceControlAccept::STOP,
exit_code: ServiceExitCode::Win32(0),
checkpoint: 0,
wait_hint: Duration::default(),
process_id: None,
})?;
match start_switch() {
Ok(switch) => {
parker.park();
if let Err(e) = switch.stop() {
log::warn!("switch stop:{:?}",e)
}
}
Err(e) => {
log::error!("{:?}",e);
}
}
status_handle.set_service_status(ServiceStatus {
service_type: crate::windows::SERVICE_TYPE,
current_state: ServiceState::Stopped,
controls_accepted: ServiceControlAccept::empty(),
exit_code: ServiceExitCode::Win32(0),
checkpoint: 0,
wait_hint: Duration::default(),
process_id: None,
})
}
fn start_switch() -> switch::Result<Arc<Switch>> {
if let Some(config) = read_config() {
let device_id = config.device_id;
if device_id.trim().is_empty() {
return Err(switch::error::Error::Stop("Device id error".to_string()));
}
let server_address = if let Some(server_address) = config.server
.to_socket_addrs()?
.next() {
server_address
} else {
return Err(switch::error::Error::Stop("server address error".to_string()));
};
let nat_test_server = config.nat_test_server.iter()
.flat_map(|a| a.to_socket_addrs())
.flatten()
.collect::<Vec<_>>();
if nat_test_server.is_empty() {
return Err(switch::error::Error::Stop("nat test server address error".to_string()));
}
let config = Config::new(
config.token,
device_id,
config.name,
server_address,
nat_test_server);
let switch = Switch::start(config)?;
log::info!("switch-service服务启动");
let switch = Arc::new(switch);
let command_server = crate::command::server::CommandServer::new();
let switch1 = switch.clone();
thread::spawn(move || {
if let Err(e) = config::update_pid(std::process::id()) {
log::error!("{:?}", e);
}
if let Err(e) = command_server.start(switch1) {
log::error!("{:?}", e);
}
});
Ok(switch)
} else {
Err(switch::error::Error::Stop("配置文件为空".to_string()))
}
}
pub fn start() {
log::info!("以服务的方式启动");
service_dispatcher::start(SERVICE_NAME, ffi_service_main).unwrap();
}
-25
View File
@@ -1,25 +0,0 @@
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("the buffer is too small")]
SmallBuffer,
#[error("the packet is invalid")]
InvalidPacket,
#[error("Unimplemented")]
Unimplemented,
// #[error("the vaue is invalid for the field")]
// InvalidValue,
//
// #[error("the value has already been defined")]
// AlreadyDefined,
//
// #[error(transparent)]
// Io(#[from] io::Error),
//
// #[error(transparent)]
// Nul(#[from] ffi::NulError),
}
pub type Result<T> = ::std::result::Result<T, Error>;
-140
View File
@@ -1,140 +0,0 @@
use std::io;
use std::net::{Ipv4Addr, SocketAddr};
use std::sync::Arc;
use crossbeam::atomic::AtomicCell;
use crossbeam_skiplist::SkipMap;
use parking_lot::Mutex;
use p2p_channel::boot::Boot;
use p2p_channel::channel::{Channel, Route, RouteKey};
use p2p_channel::punch::NatInfo;
use crate::handle::{ConnectStatus, CurrentDeviceInfo, heartbeat_handler, PeerDeviceInfo, punch_handler, recv_handler, registration_handler, tun_handler};
use crate::nat::NatTest;
use crate::tun_device;
use crate::tun_device::TunReader;
pub struct Switch {
name: String,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
tun_reader: TunReader,
nat_channel: Channel<Ipv4Addr>,
/// 0. 机器纪元,每一次上线或者下线都会增1,用于感知网络中机器变化
/// 服务端和客户端的不一致,则服务端会推送新的设备列表
/// 1. 网络中的虚拟ip列表
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
nat_test: NatTest,
connect_status: Arc<AtomicCell<ConnectStatus>>,
peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>>,
}
impl Switch {
pub fn start(config: Config) -> crate::Result<Switch> {
let (mut channel, punch, idle) = Boot::new::<Ipv4Addr>(80, 15000, 0)?;
let response = registration_handler::registration(&mut channel, config.server_address, config.token.clone(), config.device_id.clone(), config.name.clone())?;
let register = Arc::new(registration_handler::Register::new(channel.sender()?, config.server_address, config.token.clone(), config.device_id.clone(), config.name.clone()));
let device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>> = Arc::new(Mutex::new((0, Vec::new())));
let peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>> = Arc::new(SkipMap::new());
let connect_status = Arc::new(AtomicCell::new(ConnectStatus::Connected));
let virtual_ip = Ipv4Addr::from(response.virtual_ip);
let virtual_gateway = Ipv4Addr::from(response.virtual_gateway);
let virtual_netmask = Ipv4Addr::from(response.virtual_netmask);
let current_device = Arc::new(AtomicCell::new(CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, config.server_address)));
let local_ip = crate::nat::local_ip()?;
let local_port = channel.local_addr()?.port();
// NAT检测
let nat_test = NatTest::new(config.nat_test_server.clone(), Ipv4Addr::from(response.public_ip), response.public_port as u16, local_ip, local_port);
// tun通道
let (tun_writer, tun_reader) = tun_device::create_tun(virtual_ip, virtual_netmask, virtual_gateway)?;
// 定时心跳
heartbeat_handler::start_heartbeat(channel.sender()?, device_list.clone(), current_device.clone());
// 空闲检查
heartbeat_handler::start_idle(idle, channel.sender()?);
// 打洞处理
punch_handler::start_cone(punch.try_clone()?, current_device.clone());
punch_handler::start_symmetric(punch, current_device.clone());
punch_handler::start_punch(nat_test.clone(), device_list.clone(), channel.sender()?, current_device.clone());
//tun数据接收处理
for _ in 0..2 {
tun_handler::start(channel.sender()?, tun_reader.clone(), tun_writer.clone(), current_device.clone());
}
//外部数据接收处理
let channel_recv_handler = recv_handler::RecvHandler::new(channel.try_clone()?, current_device.clone(), device_list.clone(), register.clone(),
nat_test.clone(), tun_writer.clone(), connect_status.clone(), peer_nat_info_map.clone());
for _ in 0..2 {
recv_handler::start(channel_recv_handler.try_clone()?);
}
Ok(Switch {
name: config.name,
current_device,
tun_reader,
nat_channel: channel,
nat_test,
device_list,
connect_status,
peer_nat_info_map,
})
}
}
impl Switch {
pub fn name(&self) -> &str {
&self.name
}
pub fn current_device(&self) -> CurrentDeviceInfo {
self.current_device.load()
}
pub fn peer_nat_info(&self, ip: &Ipv4Addr) -> Option<NatInfo> {
self.peer_nat_info_map.get(ip).map(|e| e.value().clone())
}
pub fn connection_status(&self) -> ConnectStatus {
self.connect_status.load()
}
pub fn nat_info(&self) -> NatInfo {
self.nat_test.nat_info()
}
pub fn device_list(&self) -> Vec<PeerDeviceInfo> {
let device_list_lock = self.device_list.lock();
let (_epoch, device_list) = device_list_lock.clone();
drop(device_list_lock);
device_list
}
pub fn route(&self, ip: &Ipv4Addr) -> Option<Route> {
self.nat_channel.route(ip)
}
pub fn route_key(&self, route_key: &RouteKey) -> Option<Ipv4Addr> {
self.nat_channel.route_to_id(route_key)
}
pub fn route_table(&self) -> Vec<(Ipv4Addr, Route)> {
self.nat_channel.route_table()
}
pub fn stop(&self) -> io::Result<()> {
self.tun_reader.close();
self.nat_channel.close()?;
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct Config {
pub token: String,
pub device_id: String,
pub name: String,
pub server_address: SocketAddr,
pub nat_test_server: Vec<SocketAddr>,
}
impl Config {
pub fn new(token: String,
device_id: String,
name: String,
server_address: SocketAddr,
nat_test_server: Vec<SocketAddr>, ) -> Self {
Self {
token,
device_id,
name,
server_address,
nat_test_server,
}
}
}
-117
View File
@@ -1,117 +0,0 @@
use std::{io, thread};
use std::net::Ipv4Addr;
use std::sync::Arc;
use std::time::Duration;
use chrono::Local;
use crossbeam::atomic::AtomicCell;
use parking_lot::Mutex;
use rand::prelude::SliceRandom;
use p2p_channel::channel::Route;
use p2p_channel::channel::sender::Sender;
use p2p_channel::idle::Idle;
use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo};
use crate::protocol::{control_packet, MAX_TTL, NetPacket, Protocol, Version};
use crate::protocol::control_packet::PingPacket;
pub fn start_idle(idle: Idle<Ipv4Addr>, sender: Sender<Ipv4Addr>) {
thread::Builder::new().name("idle".into()).spawn(move || {
if let Err(e) = start_idle_(idle, sender) {
log::info!("空闲检测线程停止:{:?}",e);
}
}).unwrap();
}
fn start_idle_(idle: Idle<Ipv4Addr>, sender: Sender<Ipv4Addr>) -> io::Result<()> {
loop {
let (idle_status, peer_ips, route) = idle.next_idle()?;
log::warn!("peer_ip:{:?},route:{:?},idle_status:{:?}",peer_ips,route,idle_status);
for peer_ip in peer_ips {
sender.remove_route(&peer_ip);
}
}
}
pub fn start_heartbeat(sender: Sender<Ipv4Addr>, device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) {
thread::Builder::new().name("heartbeat".into()).spawn(move || {
if let Err(e) = start_heartbeat_(sender, device_list, current_device) {
log::info!("空闲检测线程停止:{:?}",e);
}
}).unwrap();
}
fn set_now_time(packet: &mut NetPacket<[u8; 16]>) -> io::Result<()> {
let current_time = Local::now().timestamp_millis() as u16;
let mut ping = PingPacket::new(packet.payload_mut())?;
ping.set_time(current_time);
Ok(())
}
fn start_heartbeat_(sender: Sender<Ipv4Addr>, device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) -> io::Result<()> {
let mut net_packet = NetPacket::new([0u8; 16])?;
net_packet.set_version(Version::V1);
net_packet.set_protocol(Protocol::Control);
net_packet.set_transport_protocol(control_packet::Protocol::Ping.into());
net_packet.first_set_ttl(MAX_TTL);
let mut count = 0;
loop {
let current_device = current_device.load();
net_packet.set_source(current_device.virtual_ip());
{
let mut ping = PingPacket::new(net_packet.payload_mut())?;
let epoch = { device_list.lock().0 };
ping.set_epoch(epoch);
}
if count < 7 || count % 7 == 0 {
let mut route_list: Option<Vec<(Ipv4Addr, Route)>> = None;
let peer_list = device_list.lock().1.clone();
for peer in peer_list {
set_now_time(&mut net_packet)?;
net_packet.first_set_ttl(MAX_TTL);
net_packet.set_destination(peer.virtual_ip);
if sender.send_to_id(net_packet.buffer(), &peer.virtual_ip).is_err() {
//没有路由则发送到网关
let _ = sender.send_to_addr(net_packet.buffer(), current_device.connect_server);
//再随机发送到其他地址,看有没有客户端符合转发条件
let route_list = route_list.get_or_insert_with(|| {
let mut l = sender.route_table();
l.shuffle(&mut rand::thread_rng());
l
});
let mut num = 0;
net_packet.first_set_ttl(2);
for (peer_ip, route) in route_list.iter() {
if peer_ip != &peer.virtual_ip && route.metric == 1 {
set_now_time(&mut net_packet)?;
let _ = sender.send_to_route(net_packet.buffer(), &route.route_key());
num += 1;
}
if num >= 3 {
break;
}
}
}
thread::sleep(Duration::from_millis(1));
}
set_now_time(&mut net_packet)?;
net_packet.set_destination(current_device.virtual_gateway());
if let Err(e) = sender.send_to_addr(net_packet.buffer(), current_device.connect_server) {
log::warn!("connect_server:{:?},e:{:?}",current_device.connect_server,e);
}
} else {
for (peer_ip, route) in sender.route_table().iter() {
set_now_time(&mut net_packet)?;
net_packet.set_destination(*peer_ip);
if let Err(e) = sender.send_to_route(net_packet.buffer(), &route.route_key()) {
log::warn!("peer_ip:{:?},route:{:?},e:{:?}",peer_ip,route,e);
}
thread::sleep(Duration::from_millis(1));
}
}
count += 1;
thread::sleep(Duration::from_millis(5000));
}
}
-140
View File
@@ -1,140 +0,0 @@
use std::{io, thread};
use std::net::{IpAddr, Ipv4Addr};
use std::sync::Arc;
use std::time::Duration;
use crossbeam::atomic::AtomicCell;
use parking_lot::Mutex;
use protobuf::Message;
use rand::prelude::SliceRandom;
use p2p_channel::channel::sender::Sender;
use p2p_channel::punch::{NatInfo, NatType, Punch};
use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo};
use crate::nat::NatTest;
use crate::proto::message::{PunchInfo, PunchNatType};
use crate::protocol::{control_packet, MAX_TTL, NetPacket, Protocol, turn_packet, Version};
pub fn start_cone(punch: Punch<Ipv4Addr>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) {
thread::Builder::new().name("punch-cone".into()).spawn(move || {
if let Err(e) = start_(true, punch, current_device) {
log::warn!("锥形网络打洞处理线程停止 {:?}",e);
}
}).unwrap();
}
pub fn start_symmetric(punch: Punch<Ipv4Addr>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) {
thread::Builder::new().name("punch-symmetric".into()).spawn(move || {
if let Err(e) = start_(false, punch, current_device) {
log::warn!("对称网络打洞处理线程停止 {:?}",e);
}
}).unwrap();
}
fn start_(is_cone: bool, mut punch: Punch<Ipv4Addr>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) -> io::Result<()> {
let mut packet = NetPacket::new([0u8; 12])?;
packet.set_version(Version::V1);
packet.first_set_ttl(1);
packet.set_protocol(Protocol::Control);
packet.set_transport_protocol(control_packet::Protocol::PunchRequest.into());
loop {
let (peer_ip, nat_info) = if is_cone {
punch.next_cone(None)?
} else {
punch.next_symmetric(None)?
};
if let Some(route) = punch.sender().route(&peer_ip) {
if route.metric == 1 {
//直连地址不需要打洞
continue;
}
}
packet.set_source(current_device.load().virtual_ip());
packet.set_destination(peer_ip);
log::info!("发起打洞,目标:{:?},{:?}",peer_ip,nat_info);
if let Err(e) = punch.punch(packet.buffer(), peer_ip, nat_info) {
log::warn!("peer_ip:{:?},e:{:?}",peer_ip,e);
}
}
}
pub fn start_punch(nat_test: NatTest, device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>, sender: Sender<Ipv4Addr>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) {
thread::Builder::new().name("punch-send-request".into()).spawn(move || {
if let Err(e) = start_punch_(nat_test, device_list, sender, current_device) {
log::warn!("对称网络打洞处理线程停止 {:?}",e);
}
}).unwrap();
}
fn start_punch_(nat_test: NatTest, device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>, sender: Sender<Ipv4Addr>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) -> crate::Result<()> {
loop {
if sender.is_close() {
return Ok(());
}
let current_device = current_device.load();
let nat_info = nat_test.nat_info();
{
let mut list = device_list.lock().clone().1;
list.shuffle(&mut rand::thread_rng());
let mut count = 0;
for info in list {
if info.virtual_ip <= current_device.virtual_ip {
continue;
}
if let Some(route) = sender.route(&info.virtual_ip) {
if route.metric == 1 {
//直连地址不需要打洞
continue;
}
}
count += 1;
if count > 3 {
break;
}
let buf = punch_packet(current_device.virtual_ip(), &nat_info, info.virtual_ip)?;
sender.send_to_addr(&buf, current_device.connect_server)?;
}
}
match nat_info.nat_type {
NatType::Symmetric => {
thread::sleep(Duration::from_secs(28));
}
NatType::Cone => {
thread::sleep(Duration::from_secs(20));
}
}
}
}
pub fn punch_packet(virtual_ip: Ipv4Addr, nat_info: &NatInfo, dest: Ipv4Addr) -> crate::Result<Vec<u8>> {
let mut punch_reply = PunchInfo::new();
punch_reply.reply = false;
punch_reply.public_ip_list = nat_info.public_ips.iter().map(|i| {
match i {
IpAddr::V4(ip) => {
u32::from_be_bytes(ip.octets())
}
IpAddr::V6(_) => {
panic!()
}
}
}).collect();
punch_reply.public_port = nat_info.public_port as u32;
punch_reply.public_port_range = nat_info.public_port_range as u32;
punch_reply.local_ip = match nat_info.local_ip {
IpAddr::V4(ip) => u32::from_be_bytes(ip.octets()),
IpAddr::V6(_) => {
panic!()
}
};
punch_reply.local_port = nat_info.local_port as u32;
punch_reply.nat_type = protobuf::EnumOrUnknown::new(PunchNatType::from(nat_info.nat_type));
let bytes = punch_reply.write_to_bytes()?;
let mut net_packet = NetPacket::new(vec![0u8; 12 + bytes.len()])?;
net_packet.set_version(Version::V1);
net_packet.set_protocol(Protocol::OtherTurn);
net_packet.set_transport_protocol(turn_packet::Protocol::Punch.into());
net_packet.first_set_ttl(MAX_TTL);
net_packet.set_source(virtual_ip);
net_packet.set_destination(dest);
net_packet.set_payload(&bytes);
Ok(net_packet.into_buffer())
}
-393
View File
@@ -1,393 +0,0 @@
use std::{io, thread};
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::sync::Arc;
use chrono::Local;
use crossbeam::atomic::AtomicCell;
use crossbeam_skiplist::SkipMap;
use parking_lot::Mutex;
use protobuf::Message;
use p2p_channel::channel::{Channel, Route, RouteKey};
use p2p_channel::punch::NatInfo;
use packet::icmp::{icmp, Kind};
use packet::ip::ipv4;
use packet::ip::ipv4::packet::IpV4Packet;
use crate::error::Error;
use crate::handle::{check_dest, ConnectStatus, CurrentDeviceInfo, PeerDeviceInfo};
use crate::handle::registration_handler::Register;
use crate::nat;
use crate::nat::NatTest;
use crate::proto::message::{DeviceList, PunchInfo, PunchNatType, RegistrationResponse};
use crate::protocol::{control_packet, MAX_TTL, NetPacket, Protocol, service_packet, turn_packet, Version};
use crate::protocol::control_packet::ControlPacket;
use crate::protocol::error_packet::InErrorPacket;
use crate::tun_device::TunWriter;
pub fn start(mut handler: RecvHandler) {
thread::Builder::new().name("udp-recv-handler".into()).spawn(move || {
let mut buf = [0; 4096];
loop {
match handler.channel.recv_from(&mut buf, None) {
Ok((len, route)) => {
if let Err(e) = handler.handle(&mut buf[..len], &route) {
log::warn!("数据处理失败:{:?},e:{:?}",route,e);
if let Error::Stop(_) = e {
let _ = handler.channel.close();
break;
}
}
}
Err(e) => {
log::warn!("{:?}",e);
// 检查关闭状态
if handler.channel.is_close() {
break;
}
}
}
}
}).unwrap();
}
pub struct RecvHandler {
channel: Channel<Ipv4Addr>,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
register: Arc<Register>,
nat_test: NatTest,
tun_writer: TunWriter,
connect_status: Arc<AtomicCell<ConnectStatus>>,
peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>>,
}
impl RecvHandler {
pub fn new(channel: Channel<Ipv4Addr>,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
register: Arc<Register>,
nat_test: NatTest,
tun_writer: TunWriter,
connect_status: Arc<AtomicCell<ConnectStatus>>,
peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>>,
) -> Self {
Self {
channel,
current_device,
device_list,
register,
nat_test,
tun_writer,
connect_status,
peer_nat_info_map,
}
}
pub fn try_clone(&self) -> io::Result<Self> {
Ok(Self {
channel: self.channel.try_clone()?,
current_device: self.current_device.clone(),
device_list: self.device_list.clone(),
register: self.register.clone(),
nat_test: self.nat_test.clone(),
tun_writer: self.tun_writer.clone(),
connect_status: self.connect_status.clone(),
peer_nat_info_map: self.peer_nat_info_map.clone(),
})
}
}
impl RecvHandler {
fn handle(&self, buf: &mut [u8], route_key: &RouteKey) -> crate::Result<()> {
let mut net_packet = NetPacket::new(buf)?;
if net_packet.ttl() == 0 {
return Ok(());
}
let source = net_packet.source();
let current_device = self.current_device.load();
if source == current_device.virtual_ip() {
return Ok(());
}
let destination = net_packet.destination();
if !destination.is_broadcast() && destination != current_device.broadcast_address
&& current_device.virtual_ip() != destination && self.connect_status.load() == ConnectStatus::Connected {
if !check_dest(source, current_device.virtual_netmask, current_device.virtual_network) {
log::warn!("转发数据,源地址错误:{:?},当前网络:{:?},route_key:{:?}",source,current_device.virtual_network,route_key);
return Ok(());
}
if !check_dest(destination, current_device.virtual_netmask, current_device.virtual_network) {
log::warn!("转发数据,目的地址错误:{:?},当前网络:{:?},route_key:{:?}",destination,current_device.virtual_network,route_key);
return Ok(());
}
let ttl = net_packet.ttl();
if ttl > 1 {
// 转发
net_packet.set_ttl(ttl - 1);
if let Some(route) = self.channel.route(&destination) {
if route.metric <= net_packet.ttl() {
self.channel.send_to_route(net_packet.buffer(), &route.route_key())?;
}
} else if (ttl > 2 || destination == current_device.virtual_gateway())
&& source != current_device.virtual_gateway() {
//网关默认要转发一次,生存时间不够的发到网关也会被丢弃
self.channel.send_to_addr(net_packet.buffer(), current_device.connect_server)?;
}
}
return Ok(());
}
match net_packet.protocol() {
Protocol::Ipv4Turn => {
let mut ipv4 = IpV4Packet::new(net_packet.payload_mut())?;
if ipv4.protocol() == ipv4::protocol::Protocol::Icmp {
let mut icmp_packet = icmp::IcmpPacket::new(ipv4.payload_mut())?;
if icmp_packet.kind() == Kind::EchoRequest {
//开启ping
icmp_packet.set_kind(Kind::EchoReply);
icmp_packet.update_checksum();
ipv4.set_source_ip(destination);
ipv4.set_destination_ip(source);
ipv4.update_checksum();
net_packet.set_source(destination);
net_packet.set_destination(source);
self.channel.send_to_route(net_packet.buffer(), route_key)?;
return Ok(());
}
}
self.tun_writer.write(net_packet.payload())?;
}
Protocol::Service => {
self.service(current_device, source, net_packet, route_key)?;
}
Protocol::Error => {
self.error(current_device, source, net_packet, route_key)?;
}
Protocol::Control => {
self.control(current_device, source, net_packet, route_key)?;
}
Protocol::OtherTurn => {
self.other_turn(current_device, source, net_packet, route_key)?;
}
Protocol::UnKnow(e) => {
log::info!("不支持的协议:{}",e);
}
}
Ok(())
}
fn service(&self, current_device: CurrentDeviceInfo, source: Ipv4Addr, net_packet: NetPacket<&mut [u8]>, route_key: &RouteKey) -> crate::Result<()> {
if route_key.addr != current_device.connect_server || source != current_device.virtual_gateway() {
return Ok(());
}
match service_packet::Protocol::from(net_packet.transport_protocol()) {
service_packet::Protocol::RegistrationRequest => {}
service_packet::Protocol::RegistrationResponse => {
let response = RegistrationResponse::parse_from_bytes(net_packet.payload())?;
let local_port = self.channel.local_addr()?.port();
let local_ip = nat::local_ip()?;
let nat_info = self.nat_test.re_test(Ipv4Addr::from(response.public_ip), response.public_port as u16, local_ip, local_port);
self.channel.set_nat_type(nat_info.nat_type)?;
let new_ip = Ipv4Addr::from(response.virtual_ip);
let current_ip = current_device.virtual_ip();
if current_ip != new_ip {
// ip发生变化
log::info!("ip发生变化,old_ip:{:?},new_ip:{:?}",current_ip,new_ip);
let old_netmask = current_device.virtual_netmask;
let old_gateway = current_device.virtual_gateway();
let virtual_ip = Ipv4Addr::from(response.virtual_ip);
let virtual_gateway = Ipv4Addr::from(response.virtual_gateway);
let virtual_netmask = Ipv4Addr::from(response.virtual_netmask);
self.tun_writer.change_ip(virtual_ip, virtual_netmask, virtual_gateway, old_netmask, old_gateway)?;
let new_current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway,
virtual_netmask, current_device.connect_server);
if let Err(e) = self.current_device.compare_exchange(current_device, new_current_device) {
log::warn!("替换失败:{:?}",e);
}
}
self.connect_status.store(ConnectStatus::Connected);
}
service_packet::Protocol::PollDeviceList => {}
service_packet::Protocol::PushDeviceList => {
let device_list_t = DeviceList::parse_from_bytes(net_packet.payload())?;
let ip_list = device_list_t
.device_info_list
.into_iter()
.map(|info| {
PeerDeviceInfo::new(
Ipv4Addr::from(info.virtual_ip),
info.name,
info.device_status as u8,
)
})
.collect();
let mut dev = self.device_list.lock();
if dev.0 != device_list_t.epoch as u16 {
dev.0 = device_list_t.epoch as u16;
dev.1 = ip_list;
}
}
service_packet::Protocol::UnKnow(u) => {
log::warn!("未知服务协议:{}",u);
}
}
Ok(())
}
fn error(&self, current_device: CurrentDeviceInfo, source: Ipv4Addr, net_packet: NetPacket<&mut [u8]>, route_key: &RouteKey) -> crate::Result<()> {
if route_key.addr != current_device.connect_server || source != current_device.virtual_gateway() {
return Ok(());
}
match InErrorPacket::new(net_packet.transport_protocol(), net_packet.payload())? {
InErrorPacket::TokenError => {
return Err(Error::Stop("Token error".to_string()));
}
InErrorPacket::Disconnect => {
self.connect_status.store(ConnectStatus::Connecting);
self.register.fast_register()?;
}
InErrorPacket::AddressExhausted => {
//地址用尽
return Err(Error::Stop("IP address has been exhausted".to_string()));
}
InErrorPacket::OtherError(e) => {
log::error!("OtherError {:?}", e.message());
}
}
Ok(())
}
fn control(&self, current_device: CurrentDeviceInfo, source: Ipv4Addr, mut net_packet: NetPacket<&mut [u8]>, route_key: &RouteKey) -> crate::Result<()> {
match ControlPacket::new(net_packet.transport_protocol(), net_packet.payload())? {
ControlPacket::PingPacket(_) => {
let metric = net_packet.source_ttl() - net_packet.ttl() + 1;
net_packet.set_transport_protocol(control_packet::Protocol::Pong.into());
net_packet.set_source(current_device.virtual_ip());
net_packet.set_destination(source);
net_packet.first_set_ttl(MAX_TTL);
self.channel.send_to_route(net_packet.buffer(), route_key)?;
if metric == 1 {
if let Some(current_route) = self.channel.route(&source) {
if current_route.metric > 1 {
let route = Route::from(*route_key, 1, -1);
self.channel.add_route(source, route);
}
}
}
}
ControlPacket::PongPacket(pong_packet) => {
let current_time = Local::now().timestamp_millis() as u16;
if current_time < pong_packet.time() {
return Ok(());
}
let rt = (current_time - pong_packet.time()) as i64;
let metric = net_packet.source_ttl() - net_packet.ttl() + 1;
if let Some(current_route) = self.channel.route(&source) {
if &current_route.route_key() == route_key {
self.channel.update_route(&source, metric, rt);
} else if current_route.metric >= metric && current_route.rt > rt {
let route = Route::from(*route_key, metric, rt);
self.channel.add_route(source, route);
}
} else {
let route = Route::from(*route_key, metric, rt);
self.channel.add_route(source, route);
}
if route_key.addr == current_device.connect_server && source == current_device.virtual_gateway() {
let epoch = self.device_list.lock().0;
if pong_packet.epoch() != epoch {
let mut poll_device = NetPacket::new([0; 12])?;
poll_device.set_source(current_device.virtual_ip());
poll_device.set_destination(source);
poll_device.set_version(Version::V1);
poll_device.first_set_ttl(MAX_TTL);
poll_device.set_protocol(Protocol::Service);
poll_device.set_transport_protocol(service_packet::Protocol::PollDeviceList.into());
self.channel.send_to_route(poll_device.buffer(), route_key)?;
}
}
}
ControlPacket::PunchRequest => {
// log::info!("PunchRequest route_key:{:?}",route_key);
//回应
net_packet.set_transport_protocol(control_packet::Protocol::PunchResponse.into());
net_packet.set_source(current_device.virtual_ip());
net_packet.set_destination(source);
net_packet.first_set_ttl(1);
self.channel.send_to_route(net_packet.buffer(), route_key)?;
let route = Route::from(*route_key, 1, -1);
self.channel.add_route(source, route);
}
ControlPacket::PunchResponse => {
// log::info!("PunchResponse route_key:{:?}",route_key);
let route = Route::from(*route_key, 1, -1);
self.channel.add_route(net_packet.source(), route);
}
}
Ok(())
}
fn other_turn(&self, current_device: CurrentDeviceInfo, source: Ipv4Addr, net_packet: NetPacket<&mut [u8]>, route_key: &RouteKey) -> crate::Result<()> {
match turn_packet::Protocol::from(net_packet.transport_protocol()) {
turn_packet::Protocol::Punch => {
let punch_info = PunchInfo::parse_from_bytes(net_packet.payload())?;
let public_ips = punch_info.public_ip_list.
iter().map(|v| { IpAddr::from(v.to_be_bytes()) }).collect();
let peer_nat_info = NatInfo::new(public_ips,
punch_info.public_port as u16,
punch_info.public_port_range as u16,
IpAddr::from(punch_info.local_ip.to_be_bytes()),
punch_info.local_port as u16,
punch_info.nat_type.enum_value_or_default().into());
self.peer_nat_info_map.insert(source, peer_nat_info.clone());
if !punch_info.reply {
let mut punch_reply = PunchInfo::new();
punch_reply.reply = true;
let nat_info = self.nat_test.nat_info();
punch_reply.public_ip_list = nat_info.public_ips.iter().map(|i| {
match i {
IpAddr::V4(ip) => u32::from_be_bytes(ip.octets()),
IpAddr::V6(_) => 0
}
}).collect();
punch_reply.public_port = nat_info.public_port as u32;
punch_reply.public_port_range = nat_info.public_port_range as u32;
punch_reply.nat_type =
protobuf::EnumOrUnknown::new(PunchNatType::from(nat_info.nat_type));
punch_reply.local_ip = match nat_info.local_ip {
IpAddr::V4(ip) => u32::from_be_bytes(ip.octets()),
IpAddr::V6(_) => 0
};
punch_reply.local_port = nat_info.local_port as u32;
let bytes = punch_reply.write_to_bytes()?;
let mut net_packet =
NetPacket::new(vec![0u8; 12 + bytes.len()])?;
net_packet.set_version(Version::V1);
net_packet.set_protocol(Protocol::OtherTurn);
net_packet.set_transport_protocol(
turn_packet::Protocol::Punch.into(),
);
net_packet.first_set_ttl(MAX_TTL);
net_packet.set_source(current_device.virtual_ip());
net_packet.set_destination(source);
net_packet.set_payload(&bytes);
if !peer_nat_info.local_ip.is_unspecified() && peer_nat_info.local_port != 0 {
let mut packet = NetPacket::new([0u8; 12])?;
packet.set_version(Version::V1);
packet.first_set_ttl(1);
packet.set_protocol(Protocol::Control);
packet.set_transport_protocol(control_packet::Protocol::PunchRequest.into());
packet.set_source(current_device.virtual_ip());
packet.set_destination(source);
let _ = self.channel.send_to_addr(packet.buffer(), SocketAddr::new(peer_nat_info.local_ip, peer_nat_info.local_port));
}
if let Err(e) = self.channel.punch(source, peer_nat_info) {
log::warn!("发送到打洞通道失败 {:?}",e);
return Ok(());
}
self.channel.send_to_route(net_packet.buffer(), route_key)?;
} else {
let _ = self.channel.punch(source, peer_nat_info);
}
}
turn_packet::Protocol::UnKnow(e) => {
log::warn!("不支持的转发协议 {:?},source:{:?}",e,source);
}
}
Ok(())
}
}
-136
View File
@@ -1,136 +0,0 @@
use std::io;
use std::net::{Ipv4Addr, SocketAddr};
use std::sync::atomic::{AtomicI64, Ordering};
use std::time::Duration;
use chrono::Local;
use protobuf::Message;
use p2p_channel::channel::Channel;
use p2p_channel::channel::sender::Sender;
use crate::error::*;
use crate::proto::message::{RegistrationRequest, RegistrationResponse};
use crate::protocol::error_packet::InErrorPacket;
use crate::protocol::{service_packet, NetPacket, Protocol, Version, MAX_TTL};
///向中继服务器注册,token标识一个虚拟网关,device_id防止多次注册时得到的ip不一致
pub fn registration(
channel: &mut Channel<Ipv4Addr>,
server_address: SocketAddr,
token: String,
device_id: String,
name: String,
) -> Result<RegistrationResponse> {
let request_packet =
registration_request_packet(token.clone(), device_id.clone(), name.clone(), false)?;
let buf = request_packet.buffer();
let mut recv_buf = [0u8; 10240];
channel.send_to_addr(buf, server_address)?;
let (len, route) = channel.recv_from(&mut recv_buf, Some(Duration::from_millis(300)))?;
if server_address != route.addr {
return Err(Error::Warn(format!("数据来源错误:{:?}", route.addr)));
}
let net_packet = NetPacket::new(&recv_buf[..len])?;
return match net_packet.protocol() {
Protocol::Service => {
match service_packet::Protocol::from(net_packet.transport_protocol()) {
service_packet::Protocol::RegistrationResponse => {
let response =
RegistrationResponse::parse_from_bytes(net_packet.payload())?;
Ok(response)
}
_ => {
Err(Error::Warn(format!("数据错误:{:?}", net_packet)))
}
}
}
Protocol::Error => {
match InErrorPacket::new(
net_packet.transport_protocol(),
net_packet.payload(),
) {
Ok(e) => match e {
InErrorPacket::TokenError => Err(Error::Stop("token错误".to_string())),
InErrorPacket::Disconnect => Err(Error::Warn("断开连接".to_string())),
InErrorPacket::AddressExhausted => Err(Error::Stop("地址用尽".to_string())),
InErrorPacket::OtherError(e) => match e.message() {
Ok(str) => Err(Error::Warn(str)),
Err(e) => Err(Error::Warn(format!("{:?}", e))),
},
},
Err(e) => Err(Error::Warn(format!("{:?}", e))),
}
}
_ => {
Err(Error::Warn(format!("数据错误:{:?}", net_packet)))
}
};
}
fn registration_request_packet(
token: String,
device_id: String,
name: String,
is_fast: bool,
) -> crate::Result<NetPacket<Vec<u8>>> {
let mut request = RegistrationRequest::new();
request.token = token;
request.device_id = device_id;
request.name = name;
request.is_fast = is_fast;
let bytes = request.write_to_bytes()?;
let buf = vec![0u8; 12 + bytes.len()];
let mut net_packet = NetPacket::new(buf)?;
net_packet.set_version(Version::V1);
net_packet.set_protocol(Protocol::Service);
net_packet.set_transport_protocol(service_packet::Protocol::RegistrationRequest.into());
net_packet.first_set_ttl(MAX_TTL);
net_packet.set_payload(&bytes);
Ok(net_packet)
}
pub struct Register {
sender: Sender<Ipv4Addr>,
server_address: SocketAddr,
token: String,
device_id: String,
name: String,
time: AtomicI64,
}
impl Register {
pub fn new(sender: Sender<Ipv4Addr>,
server_address: SocketAddr,
token: String,
device_id: String,
name: String, ) -> Self {
Self {
sender,
server_address,
token,
device_id,
name,
time: AtomicI64::new(0),
}
}
pub fn fast_register(&self) -> io::Result<()> {
let last = self.time.load(Ordering::Relaxed);
let new = Local::now().timestamp_millis();
if new - last < 1000
|| self.time
.compare_exchange(last, new, Ordering::Relaxed, Ordering::Relaxed)
.is_err()
{
//短时间不重复注册
return Ok(());
}
log::info!("重新连接");
let request_packet =
registration_request_packet(self.token.clone(),
self.device_id.clone(),
self.name.clone(), false).unwrap();
let buf = request_packet.buffer();
self.sender.send_to_addr(buf, self.server_address)?;
Ok(())
}
}
-120
View File
@@ -1,120 +0,0 @@
use std::{io, thread};
/// 接收tun数据,并且转发到udp上
use std::net::Ipv4Addr;
use std::sync::Arc;
use crossbeam::atomic::AtomicCell;
use p2p_channel::channel::sender::Sender;
use packet::icmp::icmp::IcmpPacket;
use packet::icmp::Kind;
use packet::ip::ipv4;
use packet::ip::ipv4::packet::IpV4Packet;
use crate::error::*;
use crate::handle::{check_dest, CurrentDeviceInfo};
use crate::protocol::{MAX_TTL, NetPacket, Protocol, Version};
use crate::tun_device::{TunReader, TunWriter};
fn icmp(tun_writer: &TunWriter, mut ipv4_packet: IpV4Packet<&mut [u8]>) -> Result<()> {
if ipv4_packet.protocol() == ipv4::protocol::Protocol::Icmp {
let mut icmp = IcmpPacket::new(ipv4_packet.payload_mut())?;
if icmp.kind() == Kind::EchoRequest {
icmp.set_kind(Kind::EchoReply);
icmp.update_checksum();
let src = ipv4_packet.source_ip();
ipv4_packet.set_source_ip(ipv4_packet.destination_ip());
ipv4_packet.set_destination_ip(src);
ipv4_packet.update_checksum();
tun_writer.write(ipv4_packet.buffer)?;
}
}
Ok(())
}
#[inline]
fn handle(sender: &Sender<Ipv4Addr>, data: &mut [u8], tun_writer: &TunWriter, current_device: CurrentDeviceInfo, net_packet: &mut NetPacket<Vec<u8>>) -> Result<()> {
let data_len = data.len();
let ipv4_packet = match IpV4Packet::new(data) {
Ok(ipv4_packet) => ipv4_packet,
Err(packet::error::Error::Unimplemented) => {
return Ok(());
}
Err(e) => Err(e)?,
};
let src_ip = ipv4_packet.source_ip();
let dest_ip = ipv4_packet.destination_ip();
// if dest_ip == cur_info.broadcast_address {
// // 启动服务后会收到对137端口的广播
// // 137端口是在局域网中提供计算机的名字或IP地址查询服务
// return Ok(());
// }
if src_ip != current_device.virtual_ip() || (!check_dest(dest_ip, current_device.virtual_netmask, current_device.virtual_network) && !dest_ip.is_broadcast()) {
return Ok(());
}
if src_ip == dest_ip {
return icmp(&tun_writer, ipv4_packet);
}
net_packet.set_source(src_ip);
net_packet.set_destination(dest_ip);
net_packet.set_payload(ipv4_packet.buffer);
//优先发到直连到地址
if sender.send_to_id(&net_packet.buffer()[..(12 + data_len)], &dest_ip).is_err() {
sender.send_to_addr(&net_packet.buffer()[..(12 + data_len)], current_device.connect_server)?;
}
return Ok(());
}
pub fn start(sender: Sender<Ipv4Addr>,
tun_reader: TunReader,
tun_writer: TunWriter,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>, ) {
thread::Builder::new().name("tun-handler".into()).spawn(move || {
if let Err(e) = start_(sender, tun_reader, tun_writer, current_device) {
log::warn!("{:?}",e);
}
}).unwrap();
}
#[cfg(target_os = "windows")]
fn start_(sender: Sender<Ipv4Addr>,
tun_reader: TunReader,
tun_writer: TunWriter,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>, ) -> io::Result<()> {
let mut net_packet = NetPacket::new(vec![0u8; 4 + 8 + 1500])?;
net_packet.set_version(Version::V1);
net_packet.set_protocol(Protocol::Ipv4Turn);
net_packet.set_transport_protocol(ipv4::protocol::Protocol::Ipv4.into());
net_packet.set_ttl(MAX_TTL);
loop {
let mut data = tun_reader.next()?;
match handle(&sender, data.bytes_mut(), &tun_writer, current_device.load(), &mut net_packet) {
Ok(_) => {}
Err(e) => {
log::warn!("{:?}", e)
}
}
}
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
fn start_(sender: Sender<Ipv4Addr>,
tun_reader: TunReader,
tun_writer: TunWriter,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>, ) -> io::Result<()> {
let mut net_packet = NetPacket::new(vec![0u8; 4 + 8 + 1500])?;
net_packet.set_version(Version::V1);
net_packet.set_protocol(Protocol::Ipv4Turn);
net_packet.set_transport_protocol(ipv4::protocol::Protocol::Ipv4.into());
net_packet.set_ttl(MAX_TTL);
let mut buf = [0; 4096];
loop {
let data = tun_reader.read(&mut buf)?;
match handle(&sender, data, &tun_writer, current_device.load(), &mut net_packet) {
Ok(_) => {}
Err(e) => {
log::warn!("{:?}", e)
}
}
}
}
-87
View File
@@ -1,87 +0,0 @@
use std::io;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::sync::Arc;
use parking_lot::Mutex;
use p2p_channel::punch::{NatInfo, NatType};
use crate::proto::message::PunchNatType;
pub mod check;
use std::net::UdpSocket;
pub fn local_ip() -> io::Result<IpAddr> {
let socket = UdpSocket::bind("0.0.0.0:0")?;
socket.connect("8.8.8.8:80")?;
let addr = socket.local_addr()?;
Ok(addr.ip())
}
#[derive(Clone)]
pub struct NatTest {
nat_test_server: Arc<Vec<SocketAddr>>,
info: Arc<Mutex<NatInfo>>,
}
impl From<NatType> for PunchNatType {
fn from(value: NatType) -> Self {
match value {
NatType::Symmetric => PunchNatType::Symmetric,
NatType::Cone => PunchNatType::Cone
}
}
}
impl Into<NatType> for PunchNatType {
fn into(self) -> NatType {
match self {
PunchNatType::Symmetric => NatType::Symmetric,
PunchNatType::Cone => NatType::Cone
}
}
}
impl NatTest {
pub fn new(nat_test_server: Vec<SocketAddr>, public_ip: Ipv4Addr, public_port: u16, local_ip: IpAddr, local_port: u16) -> NatTest {
let info = NatTest::re_test_(&nat_test_server, public_ip, public_port, local_ip, local_port);
NatTest {
nat_test_server: Arc::new(nat_test_server),
info: Arc::new(Mutex::new(info)),
}
}
pub fn nat_info(&self) -> NatInfo {
self.info.lock().clone()
}
pub fn re_test(&self, public_ip: Ipv4Addr, public_port: u16, local_ip: IpAddr, local_port: u16) -> NatInfo {
let info = NatTest::re_test_(&self.nat_test_server, public_ip, public_port, local_ip, local_port);
*self.info.lock() = info.clone();
info
}
fn re_test_(nat_test_server: &Vec<SocketAddr>, public_ip: Ipv4Addr, public_port: u16, local_ip: IpAddr, local_port: u16) -> NatInfo {
return match check::public_ip_list(nat_test_server) {
Ok((nat_type, ips, port_range)) => {
let mut public_ips = Vec::new();
public_ips.push(IpAddr::from(public_ip));
for ip in ips {
if ip != public_ip {
public_ips.push(IpAddr::from(ip));
}
}
NatInfo::new(public_ips,
public_port,
port_range,
local_ip, local_port,
nat_type, )
}
Err(e) => {
log::warn!("{:?}",e);
NatInfo::new(
vec![IpAddr::from(public_ip)],
public_port,
0,
local_ip, local_port,
NatType::Cone,
)
}
};
}
}
-35
View File
@@ -1,35 +0,0 @@
use crate::tun_device::{TunReader, TunWriter};
use std::net::Ipv4Addr;
use std::sync::Arc;
use tun::Device;
use parking_lot::Mutex;
pub fn create_tun(
address: Ipv4Addr,
netmask: Ipv4Addr,
gateway: Ipv4Addr,
) -> crate::error::Result<(TunWriter, TunReader)> {
let mut config = tun::Configuration::default();
config
.destination(gateway)
.address(address)
.netmask(netmask)
.mtu(1420)
// .queues(2) 用多个队列有兼容性问题
.up();
//
// config.platform(|config| {
// config.packet_information(true);
// });
let dev = tun::create(&config).unwrap();
let packet_information = dev.has_packet_information();
let queue = dev.queue(0).unwrap();
let reader = queue.reader();
let writer = queue.writer();
Ok((
TunWriter(writer, packet_information, Arc::new(Mutex::new(dev))),
TunReader(reader, packet_information),
))
}
-71
View File
@@ -1,71 +0,0 @@
use std::net::Ipv4Addr;
use std::process::Command;
use std::io;
use tun::Device;
use parking_lot::Mutex;
use std::sync::Arc;
use crate::tun_device::{TunReader, TunWriter};
pub fn create_tun(
address: Ipv4Addr,
netmask: Ipv4Addr,
gateway: Ipv4Addr,
) -> crate::error::Result<(TunWriter, TunReader)> {
let mut config = tun::Configuration::default();
config
.destination(gateway)
.address(address)
.netmask(netmask)
.mtu(1420)
.up();
let dev = tun::create(&config).unwrap();
config_ip(dev.name(), address, netmask, gateway)?;
// println!("{:?}", if_config_out);
// let cmd_str: String = " ifconfig|grep flags=8051|awk -F ':' '{print $1}'|tail -1".to_string();
//
// let cmd_str_out = Command::new("sh")
// .arg("-c")
// .arg(cmd_str)
// .output()
// .expect("sh exec error!");
// if !cmd_str_out.status.success(){
// return Err(Error::Stop(format!("设置路由失败:{:?}", cmd_str_out)));
// }
// println!("{:?}", cmd_str_out);
let packet_information = dev.has_packet_information();
let queue = dev.queue(0).unwrap();
let reader = queue.reader();
let writer = queue.writer();
Ok((
TunWriter(writer, packet_information, Arc::new(Mutex::new(dev))),
TunReader(reader, packet_information),
))
}
pub(crate) fn config_ip(name: &str, address: Ipv4Addr, netmask: Ipv4Addr, gateway: Ipv4Addr) -> io::Result<()> {
let up_eth_str: String = format!("ifconfig {} {:?} {:?} up ", name, address, gateway);
let route_add_str: String = format!(
"sudo route -n add -net {:?} -netmask {:?} {:?}",
address, netmask, gateway
);
let up_eth_out = Command::new("sh")
.arg("-c")
.arg(up_eth_str)
.output()
.expect("sh exec error!");
if !up_eth_out.status.success() {
return Err(io::Error::new(io::ErrorKind::Other, format!("设置网络地址失败: {:?}", up_eth_out)));
}
let if_config_out = Command::new("sh")
.arg("-c")
.arg(route_add_str)
.output()
.expect("sh exec error!");
if !if_config_out.status.success() {
return Err(io::Error::new(io::ErrorKind::Other, format!("添加路由失败: {:?}", if_config_out)));
}
Ok(())
}
-19
View File
@@ -1,19 +0,0 @@
#[cfg(any(target_os = "linux", target_os = "android"))]
pub use linux::create_tun;
#[cfg(target_os = "macos")]
pub use mac::create_tun;
#[cfg(any(unix))]
pub use unix::{TunReader, TunWriter};
#[cfg(target_os = "windows")]
pub use windows::create_tun;
#[cfg(target_os = "windows")]
pub use windows::{TunReader, TunWriter};
#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod linux;
#[cfg(target_os = "macos")]
pub mod mac;
#[cfg(any(unix))]
pub mod unix;
#[cfg(target_os = "windows")]
pub mod windows;
-76
View File
@@ -1,76 +0,0 @@
use std::io;
use std::sync::Arc;
use bytes::BufMut;
use tun::platform::posix::{Reader, Writer};
use std::net::Ipv4Addr;
use std::os::unix::io::AsRawFd;
#[cfg(any(target_os = "linux", target_os = "android"))]
use tun::platform::linux::Device;
#[cfg(any(target_os = "macos", target_os = "ios"))]
use tun::platform::macos::Device;
use parking_lot::Mutex;
#[derive(Clone)]
pub struct TunReader(pub(crate) Reader, pub(crate) bool);
impl TunReader {
pub fn read<'a>(&'a self, buf: &'a mut [u8]) -> io::Result<&mut [u8]> {
let len = self.0.read(buf)?;
if self.1 {
Ok(&mut buf[4..len])
} else {
Ok(&mut buf[..len])
}
}
pub fn close(&self) {
unsafe {
let raw = self.0.as_raw_fd();
if raw >= 0 {
libc::close(raw);
}
}
}
}
#[derive(Clone)]
pub struct TunWriter(pub(crate) Writer, pub(crate) bool, pub(crate) Arc<Mutex<Device>>);
impl TunWriter {
pub fn write(&self, packet: &[u8]) -> io::Result<()> {
if self.1 {
let mut buf = Vec::<u8>::with_capacity(4 + packet.len());
buf.put_u16(0);
#[cfg(any(target_os = "macos", target_os = "ios"))]
buf.put_u16(libc::PF_INET as u16);
#[cfg(any(target_os = "linux", target_os = "android"))]
buf.put_u16(libc::ETH_P_IP as u16);
buf.extend_from_slice(packet);
self.0.write_all(&buf)
} else {
self.0.write_all(packet)
}
}
pub fn change_ip(&self, address: Ipv4Addr, netmask: Ipv4Addr,
gateway: Ipv4Addr, _old_netmask: Ipv4Addr, _old_gateway: Ipv4Addr) -> io::Result<()> {
let mut config = tun::Configuration::default();
use tun::Device;
config
.destination(gateway)
.address(address)
.netmask(netmask)
.mtu(1420)
// .queues(2)
.up();
let mut dev = self.2.lock();
if let Err(e) = dev.configure(&config) {
return Err(io::Error::new(io::ErrorKind::Other, format!("{:?}", e)));
}
#[cfg(target_os = "macos")]
if let Err(e) = crate::tun_device::mac::config_ip(dev.name(), address, netmask, gateway){
log::error!("{}",e);
}
return Ok(());
}
}
-215
View File
@@ -1,215 +0,0 @@
use std::io;
use std::net::Ipv4Addr;
use std::sync::Arc;
use libloading::Library;
use parking_lot::Mutex;
use wintun::{Adapter, Packet, Session};
pub const INTERFACE_NAME: &str = "Switch-V1";
pub const POOL_NAME: &str = "Switch-V1";
#[derive(Clone)]
pub struct TunWriter(Arc<Session>, Arc<Mutex<u32>>);
impl TunWriter {
pub fn write(&self, buf: &[u8]) -> io::Result<()> {
match self.0.allocate_send_packet(buf.len() as u16) {
Ok(mut packet) => {
packet.bytes_mut().copy_from_slice(buf);
self.0.send_packet(packet);
return Ok(());
}
Err(_) => {}
}
return Err(io::Error::new(io::ErrorKind::Other, "send err"));
}
pub fn change_ip(&self, address: Ipv4Addr, netmask: Ipv4Addr,
gateway: Ipv4Addr, old_netmask: Ipv4Addr, old_gateway: Ipv4Addr) -> io::Result<()> {
let index = self.1.lock();
if let Err(e) = delete_route(*index, old_netmask, old_gateway) {
log::warn!("{:?}",e);
}
config_ip(*index, address, netmask, gateway)
}
}
#[derive(Clone)]
pub struct TunReader(pub(crate) Arc<Session>);
impl TunReader {
pub fn next(&self) -> io::Result<Packet> {
match self.0.receive_blocking() {
Ok(packet) => {
return Ok(packet);
}
Err(_) => {}
}
return Err(io::Error::new(io::ErrorKind::Other, "read err"));
}
pub fn close(&self) {
self.0.shutdown()
}
}
pub fn create_tun(
address: Ipv4Addr,
netmask: Ipv4Addr,
gateway: Ipv4Addr,
) -> io::Result<(TunWriter, TunReader)> {
let win_tun = unsafe {
match Library::new("wintun.dll") {
Ok(library) => match wintun::load_from_library(library) {
Ok(win_tun) => win_tun,
Err(e) => {
return Err(io::Error::new(io::ErrorKind::Other, format!("{:?}", e)));
}
},
Err(e) => {
log::error!("wintun.dll not found");
return Err(io::Error::new(io::ErrorKind::Other, format!("wintun.dll not found {:?}", e)));
}
}
};
if let Ok(adapter) = Adapter::open(&win_tun, INTERFACE_NAME) {
log::warn!("Switch-V1 未正常退出");
drop(adapter);
std::thread::sleep(std::time::Duration::from_secs(1));
};
let adapter = match Adapter::create(&win_tun, POOL_NAME, INTERFACE_NAME, None) {
Ok(adapter) => adapter,
Err(e) => return Err(io::Error::new(io::ErrorKind::Other, format!("{:?}", e))),
};
let session = Arc::new(adapter.start_session(wintun::MAX_RING_CAPACITY).unwrap());
let index = match adapter.get_adapter_index() {
Ok(index) => {
index
}
Err(e) => {
log::error!("get_adapter_index err {:?}",e);
get_if_index()
}
};
config_ip(index, address, netmask, gateway)?;
let reader_session = session.clone();
Ok((TunWriter(session.clone(), Arc::new(Mutex::new(index))), TunReader(reader_session)))
}
fn get_if_index() -> u32 {
let cmd = format!("netsh int ipv4 show interfaces {} |findstr IfIndex", INTERFACE_NAME);
let out = std::process::Command::new("cmd")
.arg("/C")
.arg(&cmd)
.output()
.unwrap();
if !out.status.success() {
log::warn!("1获取网络接口索引失败:cmd={:?},out={:?}",cmd,out);
return 0;
}
if let Ok(stdout) = String::from_utf8(out.stdout) {
if let Some(start) = stdout.find(":") {
if let Some(end) = stdout.find("\r\n") {
if let Ok(index) = stdout[start + 1..end].trim().parse::<u32>() {
return index;
}
}
}
}
log::warn!("2获取网络接口索引失败:cmd={:?}",cmd);
0
}
fn config_ip(index: u32, address: Ipv4Addr, netmask: Ipv4Addr, gateway: Ipv4Addr) -> io::Result<()> {
if index == 0 {
return Err(io::Error::new(io::ErrorKind::Other, format!("网络接口索引错误: {:?}", index)));
}
let set_mtu = format!(
"netsh interface ipv4 set subinterface {} mtu=1420 store=persistent",
index
);
let set_metric = format!("netsh interface ip set interface {} metric=1", index);
let set_address = format!(
"netsh interface ip set address {} static {:?} {:?} ", // gateway={:?}
index, address, netmask,
);
// 执行网卡初始化命令
let out = std::process::Command::new("cmd")
.arg("/C")
.arg(set_mtu)
.output()
.unwrap();
if !out.status.success() {
return Err(io::Error::new(io::ErrorKind::Other, format!("设置mtu失败: {:?}", out)));
}
let out = std::process::Command::new("cmd")
.arg("/C")
.arg(set_metric)
.output()
.unwrap();
if !out.status.success() {
return Err(io::Error::new(io::ErrorKind::Other, format!("设置接口跃点失败: {:?}", out)));
}
let out = std::process::Command::new("cmd")
.arg("/C")
.arg(&set_address)
.output()
.unwrap();
if !out.status.success() {
log::error!("cmd={:?},out={:?}",set_address,out);
return Err(io::Error::new(io::ErrorKind::Other, format!("设置网络地址失败: {:?}", out)));
}
let dest = {
let ip = address.octets();
let mask = netmask.octets();
Ipv4Addr::from([
ip[0] & mask[0],
ip[1] & mask[1],
ip[2] & mask[2],
ip[3] & mask[3],
])
};
let set_route = format!(
"route add {:?} mask {:?} {:?} if {}",
dest, netmask, gateway, index
);
// 执行添加路由命令
let out = std::process::Command::new("cmd")
.arg("/C")
.arg(&set_route)
.output()
.unwrap();
if !out.status.success() {
log::error!("cmd={:?},out={:?}",set_route,out);
return Err(io::Error::new(io::ErrorKind::Other, format!("添加路由失败: {:?}", out)));
}
Ok(())
}
fn delete_route(index: u32, netmask: Ipv4Addr, gateway: Ipv4Addr) -> io::Result<()> {
if index == 0 {
return Err(io::Error::new(io::ErrorKind::Other, format!("网络接口索引错误: {:?}", index)));
}
let mask = netmask.octets();
let ip = gateway.octets();
let dest = Ipv4Addr::from([
ip[0] & mask[0],
ip[1] & mask[1],
ip[2] & mask[2],
ip[3] & mask[3],
]);
let delete_route = format!(
"route delete {:?} mask {:?} {:?} if {}",
dest, netmask, gateway, index
);
// 删除路由
let out = std::process::Command::new("cmd")
.arg("/C")
.arg(delete_route)
.output()
.unwrap();
if !out.status.success() {
return Err(io::Error::new(io::ErrorKind::Other, format!("删除路由失败: {:?}", out)));
}
Ok(())
}
-14
View File
@@ -1,14 +0,0 @@
out.pcap
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
/.idea
-60
View File
@@ -1,60 +0,0 @@
# ChangeLog
This format is based on [Keep a Changelog](https://keepachangelog.com/)
and this project adheres to [Semantic Versioning](https://semver.org).
## [0.2.1] - 2021-12-03
### Fixed
Type in readme
## [0.2.0] - 2021-12-03
Added support for wintun 0.14.
### Breaking Changes
- Wintun driver versions before `0.14` are no longer support due to beraking
changes in the C API
- `Adapter::create` returns a `Result<Adapter, ...>` instead of a `Result<CreateData, ...>`.
This was done because the underlying Wintun function was changed to only return an adapter handle
- `Adapter::create` the pool parameter was removed because it was also removed from the C function
- `Adapter::delete` takes no parameters and returns a `Result<(), ()>`.
The `force_close_sessions` parameter was removed because it was removed from the
C function. Same for the bool inside the Ok(..) variant
- `Adapter::create` and `Adapter::open` return `Arc<Adapter>` instead of `Adapter`
- `get_running_driver_version` now returns a proper Result<Version, ()>.
### Added
- `reset_logger` function to disable logging after a logger has been set.
## [0.1.5] - 2021-08-27
### Fixed
- Readme on crates.io
## [0.1.4] - 2021-08-27
### Added
- `panic_on_unsent_packets` feature flag to help in debugging ring buffer blockage issues
## [0.1.3] - 2021-06-28
### Fixed
- Cargo.toml metadata to include `package.metadata.docs.rs.default-target`.
Fixes build issue on docs.rs (we can only build docs on windows, 0.1.1 doesn't work)
## [0.1.2] - 2021-06-28
docs.rs testing
## [0.1.1] - 2021-06-28
- Cargo.toml metadata to build on linux
## [0.1.0] - 2021-06-28
First release with initial api
-427
View File
@@ -1,427 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "derive-into-owned"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "576fce04d31d592013a5887ba8d9c3830adff329e5096d7e1eb5e8e61262ca62"
dependencies = [
"quote 0.3.15",
"syn 0.11.11",
]
[[package]]
name = "either"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "env_logger"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "getrandom"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "hwaddr"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e414433a9e4338f4e87fa29d0670c883a5e73e7955c45f4a49130c0aa992c85b"
dependencies = [
"phf",
]
[[package]]
name = "itertools"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
dependencies = [
"either",
]
[[package]]
name = "libc"
version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
[[package]]
name = "libloading"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afe203d669ec979b7128619bae5a63b7b42e9203c1b29146079ee05e2f604b52"
dependencies = [
"cfg-if",
"winapi",
]
[[package]]
name = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "once_cell"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
[[package]]
name = "packet"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c136c7ad0619ed4f88894aecf66ad86c80683e7b5d707996e6a3a7e0e3916944"
dependencies = [
"bitflags",
"byteorder",
"hwaddr",
"thiserror",
]
[[package]]
name = "pcap-file"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ad13fed1a83120159aea81b265074f21d753d157dd16b10cc3790ecba40a341"
dependencies = [
"byteorder",
"derive-into-owned",
"thiserror",
]
[[package]]
name = "phf"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
dependencies = [
"phf_shared",
]
[[package]]
name = "phf_shared"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
dependencies = [
"siphasher",
]
[[package]]
name = "ppv-lite86"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
[[package]]
name = "proc-macro2"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
dependencies = [
"unicode-xid 0.2.2",
]
[[package]]
name = "quote"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
[[package]]
name = "quote"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
dependencies = [
"rand_core",
]
[[package]]
name = "regex"
version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "siphasher"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b"
[[package]]
name = "subprocess"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "055cf3ebc2981ad8f0a5a17ef6652f652d87831f79fddcba2ac57bcb9a0aa407"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "syn"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
dependencies = [
"quote 0.3.15",
"synom",
"unicode-xid 0.0.4",
]
[[package]]
name = "syn"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
dependencies = [
"proc-macro2",
"quote 1.0.10",
"unicode-xid 0.2.2",
]
[[package]]
name = "synom"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
dependencies = [
"unicode-xid 0.0.4",
]
[[package]]
name = "termcolor"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
"winapi-util",
]
[[package]]
name = "thiserror"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
dependencies = [
"proc-macro2",
"quote 1.0.10",
"syn 1.0.82",
]
[[package]]
name = "unicode-xid"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "widestring"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "wintun"
version = "0.2.1"
dependencies = [
"env_logger",
"itertools",
"libloading",
"log",
"once_cell",
"packet",
"pcap-file",
"rand",
"subprocess",
"widestring",
"winapi",
]
-35
View File
@@ -1,35 +0,0 @@
[package]
name = "wintun"
version = "0.2.1"
edition = "2021"
authors = ["null.black Inc. <[email protected]>", "Troy Neubauer <[email protected]>"]
repository = "https://github.com/nulldotblack/wintun"
readme = "README.md"
documentation = "https://docs.rs/wintun/"
description = "Safe idiomatic bindings to the WinTun C library"
license = "MIT"
keywords = ["wintun", "tap", "tun", "vpn", "wireguard"]
categories = ["api-bindings"]
[package.metadata.docs.rs]
default-target = "x86_64-pc-windows-msvc"
targets = ["aarch64-pc-windows-msvc", "i686-pc-windows-msvc", "x86_64-pc-windows-msvc"]
[features]
panic_on_unsent_packets = []
[dependencies]
winapi = { version = "0.3", features = ["synchapi", "winbase", "winerror", "ipexport", "iphlpapi", "handleapi"] }
widestring = "0.4"
libloading = "0.7"
once_cell = "1.8"
log = "0.4"
rand = "0.8.3"
itertools = "0.10.1"
[dev-dependencies]
env_logger = "0.8"
winapi = { version = "0.3", features = ["netioapi", "iptypes", "iphlpapi", "nldef"] }
packet = "0.1.4"
pcap-file = "1.1.1"
subprocess = "0.2.7"
-7
View File
@@ -1,7 +0,0 @@
Copyright 2021 null.black Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-80
View File
@@ -1,80 +0,0 @@
# wintun
Safe rust idiomatic bindings for the Wintun C library: <https://wintun.net>
All features of the Wintun library are wrapped using pure rust types and functions to make
usage feel ergonomic.
## Usage
Inside your code load the wintun.dll signed driver file, downloaded from <https://wintun.net>,
using [`load`], [`load_from_path`] or [`load_from_library`].
Then either call [`Adapter::create`] or [`Adapter::open`] to obtain a wintun
adapter. Start a session with [`Adapter::start_session`].
## Example
```rust
use std::sync::Arc;
//Must be run as Administrator because we create network adapters
//Load the wintun dll file so that we can call the underlying C functions
//Unsafe because we are loading an arbitrary dll file
let wintun = unsafe { wintun::load_from_path("path/to/wintun.dll") }
.expect("Failed to load wintun dll");
//Try to open an adapter with the name "Demo"
let adapter = match wintun::Adapter::open(&wintun, "Demo") {
Ok(a) => a,
Err(_) => {
//If loading failed (most likely it didn't exist), create a new one
wintun::Adapter::create(&wintun, "Example", "Demo", None)
.expect("Failed to create wintun adapter!")
}
};
//Specify the size of the ring buffer the wintun driver should use.
let session = Arc::new(adapter.start_session(wintun::MAX_RING_CAPACITY).unwrap());
//Get a 20 byte packet from the ring buffer
let mut packet = session.allocate_send_packet(20).unwrap();
let bytes: &mut [u8] = packet.bytes_mut();
//Write IPV4 version and header length
bytes[0] = 0x40;
//Finish writing IP header
bytes[9] = 0x69;
bytes[10] = 0x04;
bytes[11] = 0x20;
//...
//Send the packet to wintun virtual adapter for processing by the system
session.send_packet(packet);
//Stop any readers blocking for data on other threads
//Only needed when a blocking reader is preventing shutdown Ie. it holds an Arc to the
//session, blocking it from being dropped
session.shutdown();
//the session is stopped on drop
//drop(session);
//drop(adapter)
//And the adapter closes its resources when dropped
```
See `examples/wireshark.rs` for a more complete example that writes received packets to a pcap
file.
## Features
- `panic_on_unsent_packets`: Panics if a send packet is dropped without being sent. Useful for
debugging packet issues because unsent packets that are dropped without being sent hold up
wintun's internal ring buffer.
## TODO:
- Add async support
Requires hooking into a windows specific reactor and registering read interest on wintun's read
handle. Asyncify other slow operations via tokio::spawn_blocking. As always, PR's are welcome!
License: MIT
-7
View File
@@ -1,7 +0,0 @@
#!/bin/bash
bindgen \
--allowlist-function "Wintun.*" \
--allowlist-type "WINTUN_.*" \
--dynamic-loading wintun \
--dynamic-link-require-all \
wintun/wintun_functions.h > src/wintun_raw.rs
-345
View File
@@ -1,345 +0,0 @@
/// Representation of a winton adapter with safe idiomatic bindings to the functionality provided by
/// the WintunAdapter* C functions.
///
/// The [`Adapter::create`] and [`Adapter::open`] functions serve as the entry point to using
/// wintun functionality
use crate::error;
use crate::session;
use crate::util;
use crate::util::UnsafeHandle;
use crate::wintun_raw;
use crate::Wintun;
use std::ptr;
use std::sync::Arc;
use itertools::Itertools;
use log::*;
use once_cell::sync::OnceCell;
use rand::Rng;
use widestring::U16CStr;
use widestring::U16CString;
use winapi::{
shared::winerror,
um::{ipexport, iphlpapi, synchapi},
};
/// Wrapper around a <https://git.zx2c4.com/wintun/about/#wintun_adapter_handle>
pub struct Adapter {
adapter: UnsafeHandle<wintun_raw::WINTUN_ADAPTER_HANDLE>,
wintun: Wintun,
guid: u128,
}
fn encode_utf16(string: &str, max_characters: usize) -> Result<U16CString, error::WintunError> {
let utf16 = U16CString::from_str(string)?;
if utf16.len() >= max_characters {
//max_characters is the maximum number of characters including the null terminator. And .len() measures the
//number of characters (excluding the null terminator). Therefore we can hold a string with
//max_characters - 1 because the null terminator sits in the last element. However a string
//of length max_characters needs max_characters + 1 to store the null terminator the >=
//check holds
Err(format!(
//TODO: Better error handling
"Length too large. Size: {}, Max: {}",
utf16.len(),
max_characters
)
.into())
} else {
Ok(utf16)
}
}
fn encode_pool_name(name: &str) -> Result<U16CString, error::WintunError> {
encode_utf16(name, crate::MAX_POOL)
}
fn encode_adapter_name(name: &str) -> Result<U16CString, error::WintunError> {
encode_utf16(name, crate::MAX_POOL)
}
fn get_adapter_luid(wintun: &Wintun, adapter: wintun_raw::WINTUN_ADAPTER_HANDLE) -> u64 {
let mut luid: wintun_raw::NET_LUID = unsafe { std::mem::zeroed() };
unsafe { wintun.WintunGetAdapterLUID(adapter, &mut luid as *mut wintun_raw::NET_LUID) };
unsafe { std::mem::transmute(luid) }
}
impl Adapter {
//TODO: Call get last error for error information on failure and improve error types
/// Creates a new wintun adapter inside the pool `pool` with name `name`
///
/// Optionally a GUID can be specified that will become the GUID of this adapter once created.
/// Adapters obtained via this function will be able to return their adapter index via
/// [`Adapter::get_adapter_index`]
pub fn create(
wintun: &Wintun,
pool: &str,
name: &str,
guid: Option<u128>,
) -> Result<Arc<Adapter>, error::WintunError> {
let pool_utf16 = encode_pool_name(pool)?;
let name_utf16 = encode_adapter_name(name)?;
let guid = match guid {
Some(guid) => guid,
None => {
// Use random bytes so that we can identify this adapter in get_adapter_index
let mut guid_bytes: [u8; 16] = [0u8; 16];
rand::thread_rng().fill(&mut guid_bytes);
u128::from_ne_bytes(guid_bytes)
}
};
//SAFETY: guid is a unique integer so transmuting either all zeroes or the user's preferred
//guid to the winapi guid type is safe and will allow the windows kernel to see our GUID
let guid_struct: wintun_raw::GUID = unsafe { std::mem::transmute(guid) };
//TODO: The guid of the adapter once created might differ from the one provided because of
//the byte order of the segments of the GUID struct that are larger than a byte. Verify
//that this works as expected
let guid_ptr = &guid_struct as *const wintun_raw::GUID;
crate::log::set_default_logger_if_unset(wintun);
//SAFETY: the function is loaded from the wintun dll properly, we are providing valid
//pointers, and all the strings are correct null terminated UTF-16. This safety rationale
//applies for all Wintun* functions below
let result = unsafe {
wintun.WintunCreateAdapter(pool_utf16.as_ptr(), name_utf16.as_ptr(), guid_ptr)
};
if result.is_null() {
Err("Failed to crate adapter".into())
} else {
Ok(Arc::new(Adapter {
adapter: UnsafeHandle(result),
wintun: wintun.clone(),
guid,
}))
}
}
/// Attempts to open an existing wintun interface name `name`.
///
/// Adapters opened via this call will have an unknown GUID meaning [`Adapter::get_adapter_index`]
/// will always fail because knowing the adapter's GUID is required to determine its index.
/// Currently a workaround is to delete and re-create a new adapter every time one is needed so
/// that it gets created with a known GUID, allowing [`Adapter::get_adapter_index`] to works as
/// expected. There is likely a way to get the GUID of our adapter using the Windows Registry
/// or via the Win32 API, so PR's that solve this issue are always welcome!
pub fn open(wintun: &Wintun, name: &str) -> Result<Arc<Adapter>, error::WintunError> {
let name_utf16 = encode_adapter_name(name)?;
crate::log::set_default_logger_if_unset(wintun);
let result = unsafe { wintun.WintunOpenAdapter(name_utf16.as_ptr()) };
if result.is_null() {
Err("WintunOpenAdapter failed".into())
} else {
Ok(Arc::new(Adapter {
adapter: UnsafeHandle(result),
wintun: wintun.clone(),
// TODO: get GUID somehow
guid: 0,
}))
}
}
/// Delete an adapter, consuming it in the process
pub fn delete(self) -> Result<(), ()> {
//Dropping an adapter closes it
drop(self);
// Return a result here so that if later the API changes to be fallible, we can support it
// without making a breaking change
Ok(())
}
/// Initiates a new wintun session on the given adapter.
///
/// Capacity is the size in bytes of the ring buffer used internally by the driver. Must be
/// a power of two between [`crate::MIN_RING_CAPACITY`] and [`crate::MIN_RING_CAPACITY`].
pub fn start_session(
self: &Arc<Self>,
capacity: u32,
) -> Result<session::Session, error::WintunError> {
let range = crate::MIN_RING_CAPACITY..=crate::MAX_RING_CAPACITY;
if !range.contains(&capacity) {
return Err(Box::new(error::ApiError::CapacityOutOfRange(
error::OutOfRangeData {
range,
value: capacity,
},
)));
}
if !capacity.is_power_of_two() {
return Err(Box::new(error::ApiError::CapacityNotPowerOfTwo(capacity)));
}
let result = unsafe { self.wintun.WintunStartSession(self.adapter.0, capacity) };
if result.is_null() {
Err("WintunStartSession failed".into())
} else {
Ok(session::Session {
session: UnsafeHandle(result),
wintun: self.wintun.clone(),
read_event: OnceCell::new(),
shutdown_event: unsafe {
//SAFETY: We follow the contract required by CreateEventA. See MSDN
//(the pointers are allowed to be null, and 0 is okay for the others)
UnsafeHandle(synchapi::CreateEventA(
std::ptr::null_mut(),
0,
0,
std::ptr::null_mut(),
))
},
adapter: Arc::clone(self),
})
}
}
/// Returns the Win32 LUID for this adapter
pub fn get_luid(&self) -> u64 {
get_adapter_luid(&self.wintun, self.adapter.0)
}
/// Returns the Win32 interface index of this adapter. Useful for specifying the interface
/// when executing `netsh interface ip` commands
pub fn get_adapter_index(&self) -> Result<u32, error::WintunError> {
let mut buf_len: u32 = 0;
//First figure out the size of the buffer needed to store the adapter info
//SAFETY: We are upholding the contract of GetInterfaceInfo. buf_len is a valid pointer to
//stack memory
let result =
unsafe { iphlpapi::GetInterfaceInfo(std::ptr::null_mut(), &mut buf_len as *mut u32) };
if result != winerror::NO_ERROR && result != winerror::ERROR_INSUFFICIENT_BUFFER {
let err_msg = util::get_error_message(result);
error!("Failed to get interface info: {}", err_msg);
//TODO: Better error types
return Err(format!("GetInterfaceInfo failed: {}", err_msg).into());
}
//Allocate a buffer of the requested size
//IP_INTERFACE_INFO must be aligned by at least 4 byte boundaries so use u32 as the
//underlying data storage type
let buf_elements = buf_len as usize / std::mem::size_of::<u32>() + 1;
//Round up incase integer division truncated a byte that filled a partial element
let mut buf: Vec<u32> = vec![0; buf_elements];
let buf_bytes = buf.len() * std::mem::size_of::<u32>();
assert!(buf_bytes >= buf_len as usize);
//SAFETY:
//
// 1. We are upholding the contract of GetInterfaceInfo.
// 2. `final_buf_len` is an aligned, valid pointer to stack memory
// 3. buf is a valid, non-null pointer to at least `buf_len` bytes of heap memory,
// aligned to at least 4 byte boundaries
//
//Get the info
let mut final_buf_len: u32 = buf_len;
let result = unsafe {
iphlpapi::GetInterfaceInfo(
buf.as_mut_ptr() as *mut ipexport::IP_INTERFACE_INFO,
&mut final_buf_len as *mut u32,
)
};
if result != winerror::NO_ERROR {
let err_msg = util::get_error_message(result);
//TODO: maybe over allocate the buffer in case the needed size changes between the two
//calls to GetInterfaceInfo if another adapter is added
error!(
"Failed to get interface info a second time: {}. Original len: {}, final len: {}",
err_msg, buf_len, final_buf_len
);
return Err(format!("GetInterfaceInfo failed a second time: {}", err_msg).into());
}
let info = buf.as_mut_ptr() as *const ipexport::IP_INTERFACE_INFO;
//SAFETY:
// info is a valid, non-null, at least 4 byte aligned pointer obtained from
// Vec::with_capacity that is readable for up to `buf_len` bytes which is guaranteed to be
// larger than on IP_INTERFACE_INFO struct as the kernel would never ask for less memory then
// what it will write. The largest type inside IP_INTERFACE_INFO is a u32 therefore
// a painter to IP_INTERFACE_INFO requires an alignment of at leant 4 bytes, which
// Vec<u32>::as_mut_ptr() provides
let adapter_base = unsafe { &*info };
let adapter_count = adapter_base.NumAdapters;
let first_adapter = &adapter_base.Adapter as *const ipexport::IP_ADAPTER_INDEX_MAP;
// SAFETY:
// 1. first_adapter is a valid, non null pointer, aligned to at least 4 byte boundaries
// obtained from moving a multiple of 4 offset into the buf given by Vec::with_capacity.
// 2. We gave GetInterfaceInfo a buffer of at least least `buf_len` bytes to work with and it
// succeeded in writing the adapter information within the bounds of that buffer, otherwise
// it would've failed. Because the operation succeeded, we know that reading n=NumAdapters
// IP_ADAPTER_INDEX_MAP structs stays within the bounds of buf's buffer
let interfaces =
unsafe { std::slice::from_raw_parts(first_adapter, adapter_count as usize) };
let mut tmp = Vec::new();
for interface in interfaces {
let name =
unsafe { U16CStr::from_ptr_str(&interface.Name as *const u16).to_string_lossy() };
//Nam is something like: \DEVICE\TCPIP_{29C47F55-C7BD-433A-8BF7-408DFD3B3390}
//where the GUID is the {29C4...90}, separated by dashes
let open = name.chars().position(|c| c == '{').ok_or(format!(
"Failed to find {{ character inside adapter name: {}",
name
))?;
let close = name.chars().position(|c| c == '}').ok_or(format!(
"Failed to find }} character inside adapter name: {}",
name
))?;
let digits: Vec<u8> = name[open..close]
.chars()
.filter(|c| c.is_digit(16))
.chunks(2)
.into_iter()
.filter_map(|mut chunk| {
//Filter out chunks that have < 2 digits
if let Some(a) = chunk.next() {
if let Some(b) = chunk.next() {
return Some((a, b));
}
}
None
})
.map(|digits| {
let chars: [u8; 2] = [digits.0 as u8, digits.1 as u8];
let s = std::str::from_utf8(&chars).unwrap();
u8::from_str_radix(s, 16).unwrap()
})
.collect();
//Our index is the adapter which has a guid in its name that matches ours
//For now we just check for a guid with the same hex bytes in any order
//TODO: byte swap GUID from name so that we can compare self.guid with the parsed GUID
//directly
let mut match_count = 0;
for byte in self.guid.to_ne_bytes() {
if digits.contains(&byte) {
match_count += 1;
}
}
tmp.push(format!("interfaces name={:?},digits={:?},index={:?}", name,digits, interface.Index));
if match_count == digits.len() {
return Ok(interface.Index);
}
}
log::info!("interfaces:{:?},guid={}",tmp,self.guid);
Err("Unable to find matching GUID".into())
}
}
impl Drop for Adapter {
fn drop(&mut self) {
//Close adapter on drop
//This is why we need an Arc of wintun
unsafe { self.wintun.WintunCloseAdapter(self.adapter.0) };
self.adapter = UnsafeHandle(ptr::null_mut());
}
}
-36
View File
@@ -1,36 +0,0 @@
use std::fmt::Display;
pub type WintunError = Box<dyn std::error::Error>;
/// Error type used to convey that a value is outside of a range that it must fall inside
#[derive(Debug)]
pub struct OutOfRangeData<T> {
pub range: std::ops::RangeInclusive<T>,
pub value: T,
}
/// Error type returned when preconditions of this API are broken
#[derive(Debug)]
pub enum ApiError {
CapacityNotPowerOfTwo(u32),
CapacityOutOfRange(OutOfRangeData<u32>),
}
impl Display for ApiError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self {
ApiError::CapacityOutOfRange(data) => write!(
f,
"Capacity {} out of range. Must be within {}..={}",
data.value,
data.range.start(),
data.range.end()
),
ApiError::CapacityNotPowerOfTwo(cap) => {
write!(f, "Capacity {} is not a power of two", cap)
}
}
}
}
impl std::error::Error for ApiError {}
-174
View File
@@ -1,174 +0,0 @@
//! Safe rust idiomatic bindings for the Wintun C library: <https://wintun.net>
//!
//! All features of the Wintun library are wrapped using pure rust types and functions to make
//! usage feel ergonomic.
//!
//! # Usage
//!
//! Inside your code load the wintun.dll signed driver file, downloaded from <https://wintun.net>,
//! using [`load`], [`load_from_path`] or [`load_from_library`].
//!
//! Then either call [`Adapter::create`] or [`Adapter::open`] to obtain a wintun
//! adapter. Start a session with [`Adapter::start_session`].
//!
//! # Example
//! ```no_run
//! use std::sync::Arc;
//!
//! //Must be run as Administrator because we create network adapters
//! //Load the wintun dll file so that we can call the underlying C functions
//! //Unsafe because we are loading an arbitrary dll file
//! let wintun = unsafe { wintun::load_from_path("path/to/wintun.dll") }
//! .expect("Failed to load wintun dll");
//!
//! //Try to open an adapter with the name "Demo"
//! let adapter = match wintun::Adapter::open(&wintun, "Demo") {
//! Ok(a) => a,
//! Err(_) => {
//! //If loading failed (most likely it didn't exist), create a new one
//! wintun::Adapter::create(&wintun, "Example", "Demo", None)
//! .expect("Failed to create wintun adapter!")
//! }
//! };
//! //Specify the size of the ring buffer the wintun driver should use.
//! let session = Arc::new(adapter.start_session(wintun::MAX_RING_CAPACITY).unwrap());
//!
//! //Get a 20 byte packet from the ring buffer
//! let mut packet = session.allocate_send_packet(20).unwrap();
//! let bytes: &mut [u8] = packet.bytes_mut();
//! //Write IPV4 version and header length
//! bytes[0] = 0x40;
//!
//! //Finish writing IP header
//! bytes[9] = 0x69;
//! bytes[10] = 0x04;
//! bytes[11] = 0x20;
//! //...
//!
//! //Send the packet to wintun virtual adapter for processing by the system
//! session.send_packet(packet);
//!
//! //Stop any readers blocking for data on other threads
//! //Only needed when a blocking reader is preventing shutdown Ie. it holds an Arc to the
//! //session, blocking it from being dropped
//! session.shutdown();
//!
//! //the session is stopped on drop
//! //drop(session);
//!
//! //drop(adapter)
//! //And the adapter closes its resources when dropped
//! ```
//!
//! See `examples/wireshark.rs` for a more complete example that writes received packets to a pcap
//! file.
//!
//! # Features
//!
//! - `panic_on_unsent_packets`: Panics if a send packet is dropped without being sent. Useful for
//! debugging packet issues because unsent packets that are dropped without being sent hold up
//! wintun's internal ring buffer.
//!
//! # TODO:
//! - Add async support
//! Requires hooking into a windows specific reactor and registering read interest on wintun's read
//! handle. Asyncify other slow operations via tokio::spawn_blocking. As always, PR's are welcome!
//!
mod adapter;
mod error;
mod log;
mod packet;
mod session;
mod util;
//Generated by bingen
#[allow(
non_snake_case,
dead_code,
unused_variables,
non_camel_case_types,
deref_nullptr,
clippy::all
)]
mod wintun_raw;
pub use crate::adapter::Adapter;
pub use crate::error::{ApiError, OutOfRangeData, WintunError};
pub use crate::log::{default_logger, reset_logger, set_logger};
pub use crate::packet::Packet;
pub use crate::session::Session;
pub use crate::util::get_running_driver_version;
// TODO: Get bindgen to scrape these from the `wintun.h`
// We need to make sure these stay up to date
/// The maximum size of wintun's internal ring buffer (in bytes)
pub const MAX_RING_CAPACITY: u32 = 0x400_0000;
/// The minimum size of wintun's internal ring buffer (in bytes)
pub const MIN_RING_CAPACITY: u32 = 0x2_0000;
/// Maximum pool name length including zero terminator
pub const MAX_POOL: usize = 256;
pub type Wintun = Arc<wintun_raw::wintun>;
use std::sync::Arc;
/// Attempts to load the Wintun library from the current directory using the default name "wintun.dll".
///
/// Use [`load_from_path`] with an absolute path when more control is needed as to where wintun.dll is
///
///
/// # Safety
/// This function loads a dll file with the name wintun.dll using the default system search paths.
/// This is inherently unsafe as a user could simply rename undefined_behavior.dll to wintun.dll
/// and do nefarious things inside of its DllMain function. In most cases, a regular wintun.dll
/// file which exports all of the required functions for these bindings to work is loaded. Because
/// WinTun is a well-written and well-tested library, loading a _normal_ wintun.dll file should be safe.
/// Hoverer one can never be too cautious when loading a dll file.
///
/// For more information see [`libloading`]'s dynamic library safety guarantees: [`libloading`][`libloading::Library::new`]
pub unsafe fn load() -> Result<Wintun, libloading::Error> {
load_from_path("wintun")
}
/// Attempts to load the Wintun library as a dynamic library from the given path.
///
///
/// # Safety
/// This function loads a dll file with the path provided.
/// This is inherently unsafe as a user could simply rename undefined_behavior.dll to wintun.dll
/// and do nefarious things inside of its DllMain function. In most cases, a regular wintun.dll
/// file which exports all of the required functions for these bindings to work is loaded. Because
/// WinTun is a well-written and well-tested library, loading a _normal_ wintun.dll file should be safe.
/// Hoverer one can never be too cautious when loading a dll file.
///
/// For more information see [`libloading`]'s dynamic library safety guarantees: [`libloading`][`libloading::Library::new`]
pub unsafe fn load_from_path<P>(path: P) -> Result<Wintun, libloading::Error>
where
P: AsRef<::std::ffi::OsStr>,
{
check_version(wintun_raw::wintun::new(path)?)
}
/// Attempts to load the Wintun library from an existing [`libloading::Library`].
///
///
/// # Safety
/// This function loads the required WinTun functions using the provided library. Reading a symbol table
/// of a dynamic library and transmuting the function pointers inside to have the parameters and return
/// values expected by the functions documented at: <https://git.zx2c4.com/wintun/about/#reference>
/// is inherently unsafe.
///
/// For more information see [`libloading`]'s dynamic library safety guarantees: [`libloading::Library::new`]
pub unsafe fn load_from_library<L>(library: L) -> Result<Wintun, libloading::Error>
where
L: Into<libloading::Library>,
{
check_version(wintun_raw::wintun::from_library(library)?)
}
fn check_version(lib: wintun_raw::wintun) -> Result<Wintun, libloading::Error> {
Ok(Arc::new(lib))
}
-88
View File
@@ -1,88 +0,0 @@
use crate::session;
use std::sync::Arc;
pub(crate) enum Kind {
SendPacketPending, //Send packet type, but not sent yet
SendPacketSent, //Send packet type - sent
ReceivePacket,
}
/// Represents a wintun packet
pub struct Packet {
pub(crate) kind: Kind,
//This lifetime is not actually 'static, however before you get your pitchforks let me explain...
//The bytes in this slice live for as long at the session that allocated them, or until
//WintunReleaseReceivePacket, or WintunSendPacket is called on them (whichever happens first).
//The wrapper functions that call into WintunReleaseReceivePacket, and WintunSendPacket
//consume the packet, meaning the end of this packet's lifetime coincides with the end of byte's
//lifetime. Because we never copy out of bytes, this pointer becomes inaccessible when the
//packet is dropped.
//
//This just leaves packets potentially outliving the session that allocated them posing a
//problem.
//Fortunately we have an Arc to the session that allocated this packet, meaning that the lifetime
//of the session that created this packet is at least as long as the packet.
//Because this is private (to external users) and we only write to this field when allocating
//new packets, it is impossible for the memory that is pointed to by bytes to outlive the
//underlying memory allocated by wintun.
//
//So what I told you was true, from a certain point of view.
//From the point of view of this packet, bytes' lifetime is 'static because we are always
//dropped before the underlying memory is freed
//
//Its also important to know that WintunAllocateSendPacket and WintunReceivePacket always
//return sections of memory that never overlap, so we have exclusive access to the memory,
//therefore mut is okay here.
pub(crate) bytes: &'static mut [u8],
//Share ownership of session to prevent the session from being dropped before packets that
//belong to it
pub(crate) session: Arc<session::Session>,
}
impl Packet {
/// Returns the bytes this packet holds as &mut.
/// The lifetime of the bytes is tied to the lifetime of this packet.
pub fn bytes_mut(&mut self) -> &mut [u8] {
self.bytes
}
/// Returns an immutable reference to the bytes this packet holds.
/// The lifetime of the bytes is tied to the lifetime of this packet.
pub fn bytes(&self) -> &[u8] {
self.bytes
}
}
impl Drop for Packet {
fn drop(&mut self) {
match self.kind {
Kind::ReceivePacket => {
unsafe {
//SAFETY:
//
// 1. We share ownership of the session therefore it hasn't been dropped yet
// 2. Bytes is valid because each packet holds exclusive access to a region of the
// ring buffer that the wintun session owns. We return that region of
// memory back to wintun here
self.session
.wintun
.WintunReleaseReceivePacket(self.session.session.0, self.bytes.as_ptr())
};
}
Kind::SendPacketPending => {
//If someone allocates a packet with session.allocate_send_packet() and then it is
//dropped without being sent, this will hold up the send queue because wintun expects
//that every allocated packet is sent
#[cfg(feature = "panic_on_unsent_packets")]
panic!("Packet was never sent!");
}
Kind::SendPacketSent => {
//Nop
}
}
}
}
-182
View File
@@ -1,182 +0,0 @@
extern crate winapi;
use crate::packet;
use crate::util::UnsafeHandle;
use crate::wintun_raw;
use crate::Adapter;
use crate::Wintun;
use once_cell::sync::OnceCell;
use winapi::shared::winerror;
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::handleapi;
use winapi::um::synchapi;
use winapi::um::winbase;
use winapi::um::winnt;
use std::sync::Arc;
use std::{ptr, slice};
/// Wrapper around a <https://git.zx2c4.com/wintun/about/#wintun_session_handle>
pub struct Session {
/// The session handle given to us by WintunStartSession
pub(crate) session: UnsafeHandle<wintun_raw::WINTUN_SESSION_HANDLE>,
/// Shared dll for required wintun driver functions
pub(crate) wintun: Wintun,
/// Windows event handle that is signaled by the wintun driver when data becomes available to
/// read
pub(crate) read_event: OnceCell<UnsafeHandle<winnt::HANDLE>>,
/// Windows event handle that is signaled when [`Session::shutdown`] is called force blocking
/// readers to exit
pub(crate) shutdown_event: UnsafeHandle<winnt::HANDLE>,
/// The adapter that owns this session
pub(crate) adapter: Arc<Adapter>,
}
impl Session {
/// Allocates a send packet of the specified size. Wraps WintunAllocateSendPacket
///
/// All packets returned from this function must be sent using [`Session::send_packet`] because
/// wintun establishes the send packet order based on the invocation order of this function.
/// Therefore if a packet is allocated using this function, and then never sent, it will hold
/// up the send queue for all other packets allocated in the future. It is okay for the session
/// to shutdown with allocated packets that have not yet been sent
pub fn allocate_send_packet(self: &Arc<Self>, size: u16) -> Result<packet::Packet, ()> {
let ptr = unsafe {
self.wintun
.WintunAllocateSendPacket(self.session.0, size as u32)
};
if ptr.is_null() {
Err(())
} else {
Ok(packet::Packet {
//SAFETY: ptr is non null, aligned for u8, and readable for up to size bytes (which
//must be less than isize::MAX because bytes is a u16
bytes: unsafe { slice::from_raw_parts_mut(ptr, size as usize) },
session: self.clone(),
kind: packet::Kind::SendPacketPending,
})
}
}
/// Sends a packet previously allocated with [`Session::allocate_send_packet`]
pub fn send_packet(&self, mut packet: packet::Packet) {
assert!(matches!(packet.kind, packet::Kind::SendPacketPending));
unsafe {
self.wintun
.WintunSendPacket(self.session.0, packet.bytes.as_ptr())
};
//Mark the packet at sent
packet.kind = packet::Kind::SendPacketSent;
}
/// Attempts to receive a packet from the virtual interface without blocking.
/// If there are no packets currently in the receive queue, this function returns Ok(None)
/// without blocking. If blocking until a packet is desirable, use [`Session::receive_blocking`]
pub fn try_receive(self: &Arc<Self>) -> Result<Option<packet::Packet>, ()> {
let mut size = 0u32;
let ptr = unsafe {
self.wintun
.WintunReceivePacket(self.session.0, &mut size as *mut u32)
};
debug_assert!(size <= u16::MAX as u32);
if ptr.is_null() {
//Wintun returns ERROR_NO_MORE_ITEMS instead of blocking if packets are not available
let last_error = unsafe { GetLastError() };
if last_error == winerror::ERROR_NO_MORE_ITEMS {
Ok(None)
} else {
Err(())
}
} else {
Ok(Some(packet::Packet {
kind: packet::Kind::ReceivePacket,
//SAFETY: ptr is non null, aligned for u8, and readable for up to size bytes (which
//must be less than isize::MAX because bytes is a u16
bytes: unsafe { slice::from_raw_parts_mut(ptr, size as usize) },
session: self.clone(),
}))
}
}
/// Returns the low level read event handle that is signaled when more data becomes available
/// to read
pub(crate) fn get_read_wait_event(&self) -> Result<winnt::HANDLE, ()> {
Ok(self
.read_event
.get_or_init(|| unsafe {
UnsafeHandle(self.wintun.WintunGetReadWaitEvent(self.session.0) as winnt::HANDLE)
})
.0)
}
/// Blocks until a packet is available, returning the next packet in the receive queue once this happens.
/// If the session is closed via [`Session::shutdown`] all threads currently blocking inside this function
/// will return Err(())
pub fn receive_blocking(self: &Arc<Self>) -> Result<packet::Packet, ()> {
loop {
//Try 5 times to receive without blocking so we don't have to issue a syscall to wait
//for the event if packets are being received at a rapid rate
for _ in 0..5 {
match self.try_receive() {
Err(err) => return Err(err),
Ok(Some(packet)) => return Ok(packet),
Ok(None) => {
//Try again
continue;
}
}
}
//Wait on both the read handle and the shutdown handle so that we stop when requested
let handles = [self.get_read_wait_event()?, self.shutdown_event.0];
let result = unsafe {
//SAFETY: We abide by the requirements of WaitForMultipleObjects, handles is a
//pointer to valid, aligned, stack memory
synchapi::WaitForMultipleObjects(
2,
&handles as *const winnt::HANDLE,
0,
winbase::INFINITE,
)
};
match result {
winbase::WAIT_FAILED => return Err(()),
_ => {
if result == winbase::WAIT_OBJECT_0 {
//We have data!
continue;
} else if result == winbase::WAIT_OBJECT_0 + 1 {
//Shutdown event triggered
return Err(());
}
}
}
}
}
/// Cancels any active calls to [`Session::receive_blocking`] making them instantly return Err(_) so that session can be shutdown cleanly
pub fn shutdown(&self) {
let _ = unsafe { synchapi::SetEvent(self.shutdown_event.0) };
let _ = unsafe { handleapi::CloseHandle(self.shutdown_event.0) };
}
}
impl Drop for Session {
fn drop(&mut self) {
let _ = Arc::clone(&self.adapter);
unsafe { self.wintun.WintunEndSession(self.session.0) };
self.session.0 = ptr::null_mut();
//Adapter must be dropped after we call `WintunEndSession`,
//if `self.adapter is the last reference
//drop(self.adapter)
}
}
-66
View File
@@ -1,66 +0,0 @@
use winapi::{
shared::ntdef::{LANG_NEUTRAL, SUBLANG_DEFAULT},
um::{winbase, winnt::MAKELANGID},
};
use std::mem::MaybeUninit;
use std::ptr;
use widestring::U16Str;
/// A wrapper struct that allows a type to be Send and Sync
pub(crate) struct UnsafeHandle<T>(pub T);
/// We never read from the pointer. It only serves as a handle we pass to the kernel or C code that
/// doesn't have the same mutable aliasing restrictions we have in Rust
unsafe impl<T> Send for UnsafeHandle<T> {}
unsafe impl<T> Sync for UnsafeHandle<T> {}
/// Returns a a human readable error message from a windows error code
pub fn get_error_message(err_code: u32) -> String {
const LEN: usize = 256;
let mut buf = MaybeUninit::<[u16; LEN]>::uninit();
//SAFETY: name is a allocated on the stack above therefore it must be valid, non-null and
//aligned for u16
let first = unsafe { *buf.as_mut_ptr() }.as_mut_ptr();
//Write default null terminator in case WintunGetAdapterName leaves name unchanged
unsafe { first.write(0u16) };
let chars_written = unsafe {
winbase::FormatMessageW(
winbase::FORMAT_MESSAGE_FROM_SYSTEM | winbase::FORMAT_MESSAGE_IGNORE_INSERTS,
ptr::null(),
err_code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) as u32,
first,
LEN as u32,
ptr::null_mut(),
)
};
//SAFETY: first is a valid, non-null, aligned, pointer
format!(
"{} ({})",
unsafe { U16Str::from_ptr(first, chars_written as usize) }.to_string_lossy(),
err_code
)
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Version {
pub major: u16,
pub minor: u16,
}
/// Returns the major and minor version of the wintun driver
pub fn get_running_driver_version(wintun: &crate::Wintun) -> Result<Version, ()> {
let version = unsafe { wintun.WintunGetRunningDriverVersion() };
if version == 0 {
Err(())
} else {
Ok(Version {
major: ((version >> 16) & 0xFF) as u16,
minor: (version & 0xFF) as u16,
})
}
}
-84
View File
@@ -1,84 +0,0 @@
Prebuilt Binaries License
-------------------------
1. DEFINITIONS. "Software" means the precise contents of the "wintun.dll"
files that are included in the .zip file that contains this document as
downloaded from wintun.net/builds.
2. LICENSE GRANT. WireGuard LLC grants to you a non-exclusive and
non-transferable right to use Software for lawful purposes under certain
obligations and limited rights as set forth in this agreement.
3. RESTRICTIONS. Software is owned and copyrighted by WireGuard LLC. It is
licensed, not sold. Title to Software and all associated intellectual
property rights are retained by WireGuard. You must not:
a. reverse engineer, decompile, disassemble, extract from, or otherwise
modify the Software;
b. modify or create derivative work based upon Software in whole or in
parts, except insofar as only the API interfaces of the "wintun.h" file
distributed alongside the Software (the "Permitted API") are used;
c. remove any proprietary notices, labels, or copyrights from the Software;
d. resell, redistribute, lease, rent, transfer, sublicense, or otherwise
transfer rights of the Software without the prior written consent of
WireGuard LLC, except insofar as the Software is distributed alongside
other software that uses the Software only via the Permitted API;
e. use the name of WireGuard LLC, the WireGuard project, the Wintun
project, or the names of its contributors to endorse or promote products
derived from the Software without specific prior written consent.
4. LIMITED WARRANTY. THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF
ANY KIND. WIREGUARD LLC HEREBY EXCLUDES AND DISCLAIMS ALL IMPLIED OR
STATUTORY WARRANTIES, INCLUDING ANY WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE, QUALITY, NON-INFRINGEMENT, TITLE, RESULTS,
EFFORTS, OR QUIET ENJOYMENT. THERE IS NO WARRANTY THAT THE PRODUCT WILL BE
ERROR-FREE OR WILL FUNCTION WITHOUT INTERRUPTION. YOU ASSUME THE ENTIRE
RISK FOR THE RESULTS OBTAINED USING THE PRODUCT. TO THE EXTENT THAT
WIREGUARD LLC MAY NOT DISCLAIM ANY WARRANTY AS A MATTER OF APPLICABLE LAW,
THE SCOPE AND DURATION OF SUCH WARRANTY WILL BE THE MINIMUM PERMITTED UNDER
SUCH LAW. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR
A PARTICULAR PURPOSE OR NON-INFRINGEMENT ARE DISCLAIMED, EXCEPT TO THE
EXTENT THAT THESE DISCLAIMERS ARE HELD TO BE LEGALLY INVALID.
5. LIMITATION OF LIABILITY. To the extent not prohibited by law, in no event
WireGuard LLC or any third-party-developer will be liable for any lost
revenue, profit or data or for special, indirect, consequential, incidental
or punitive damages, however caused regardless of the theory of liability,
arising out of or related to the use of or inability to use Software, even
if WireGuard LLC has been advised of the possibility of such damages.
Solely you are responsible for determining the appropriateness of using
Software and accept full responsibility for all risks associated with its
exercise of rights under this agreement, including but not limited to the
risks and costs of program errors, compliance with applicable laws, damage
to or loss of data, programs or equipment, and unavailability or
interruption of operations. The foregoing limitations will apply even if
the above stated warranty fails of its essential purpose. You acknowledge,
that it is in the nature of software that software is complex and not
completely free of errors. In no event shall WireGuard LLC or any
third-party-developer be liable to you under any theory for any damages
suffered by you or any user of Software or for any special, incidental,
indirect, consequential or similar damages (including without limitation
damages for loss of business profits, business interruption, loss of
business information or any other pecuniary loss) arising out of the use or
inability to use Software, even if WireGuard LLC has been advised of the
possibility of such damages and regardless of the legal or quitable theory
(contract, tort, or otherwise) upon which the claim is based.
6. TERMINATION. This agreement is affected until terminated. You may
terminate this agreement at any time. This agreement will terminate
immediately without notice from WireGuard LLC if you fail to comply with
the terms and conditions of this agreement. Upon termination, you must
delete Software and all copies of Software and cease all forms of
distribution of Software.
7. SEVERABILITY. If any provision of this agreement is held to be
unenforceable, this agreement will remain in effect with the provision
omitted, unless omission would frustrate the intent of the parties, in
which case this agreement will immediately terminate.
8. RESERVATION OF RIGHTS. All rights not expressly granted in this agreement
are reserved by WireGuard LLC. For example, WireGuard LLC reserves the
right at any time to cease development of Software, to alter distribution
details, features, specifications, capabilities, functions, licensing
terms, release dates, APIs, ABIs, general availability, or other
characteristics of the Software.
-270
View File
@@ -1,270 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 OR MIT
*
* Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
*/
#pragma once
#include <winsock2.h>
#include <windows.h>
#include <ipexport.h>
#include <ifdef.h>
#include <ws2ipdef.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef ALIGNED
# if defined(_MSC_VER)
# define ALIGNED(n) __declspec(align(n))
# elif defined(__GNUC__)
# define ALIGNED(n) __attribute__((aligned(n)))
# else
# error "Unable to define ALIGNED"
# endif
#endif
/* MinGW is missing this one, unfortunately. */
#ifndef _Post_maybenull_
# define _Post_maybenull_
#endif
#pragma warning(push)
#pragma warning(disable : 4324) /* structure was padded due to alignment specifier */
/**
* A handle representing Wintun adapter
*/
typedef struct _WINTUN_ADAPTER *WINTUN_ADAPTER_HANDLE;
/**
* Creates a new Wintun adapter.
*
* @param Name The requested name of the adapter. Zero-terminated string of up to MAX_ADAPTER_NAME-1
* characters.
*
* @param TunnelType Name of the adapter tunnel type. Zero-terminated string of up to MAX_ADAPTER_NAME-1
* characters.
*
* @param RequestedGUID The GUID of the created network adapter, which then influences NLA generation deterministically.
* If it is set to NULL, the GUID is chosen by the system at random, and hence a new NLA entry is
* created for each new adapter. It is called "requested" GUID because the API it uses is
* completely undocumented, and so there could be minor interesting complications with its usage.
*
* @return If the function succeeds, the return value is the adapter handle. Must be released with
* WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call
* GetLastError.
*/
typedef _Must_inspect_result_
_Return_type_success_(return != NULL)
_Post_maybenull_
WINTUN_ADAPTER_HANDLE(WINAPI WINTUN_CREATE_ADAPTER_FUNC)
(_In_z_ LPCWSTR Name, _In_z_ LPCWSTR TunnelType, _In_opt_ const GUID *RequestedGUID);
/**
* Opens an existing Wintun adapter.
*
* @param Name The requested name of the adapter. Zero-terminated string of up to MAX_ADAPTER_NAME-1
* characters.
*
* @return If the function succeeds, the return value is the adapter handle. Must be released with
* WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call
* GetLastError.
*/
typedef _Must_inspect_result_
_Return_type_success_(return != NULL)
_Post_maybenull_
WINTUN_ADAPTER_HANDLE(WINAPI WINTUN_OPEN_ADAPTER_FUNC)(_In_z_ LPCWSTR Name);
/**
* Releases Wintun adapter resources and, if adapter was created with WintunCreateAdapter, removes adapter.
*
* @param Adapter Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter.
*/
typedef VOID(WINAPI WINTUN_CLOSE_ADAPTER_FUNC)(_In_opt_ WINTUN_ADAPTER_HANDLE Adapter);
/**
* Deletes the Wintun driver if there are no more adapters in use.
*
* @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To
* get extended error information, call GetLastError.
*/
typedef _Return_type_success_(return != FALSE)
BOOL(WINAPI WINTUN_DELETE_DRIVER_FUNC)(VOID);
/**
* Returns the LUID of the adapter.
*
* @param Adapter Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter
*
* @param Luid Pointer to LUID to receive adapter LUID.
*/
typedef VOID(WINAPI WINTUN_GET_ADAPTER_LUID_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _Out_ NET_LUID *Luid);
/**
* Determines the version of the Wintun driver currently loaded.
*
* @return If the function succeeds, the return value is the version number. If the function fails, the return value is
* zero. To get extended error information, call GetLastError. Possible errors include the following:
* ERROR_FILE_NOT_FOUND Wintun not loaded
*/
typedef _Return_type_success_(return != 0)
DWORD(WINAPI WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC)(VOID);
/**
* Determines the level of logging, passed to WINTUN_LOGGER_CALLBACK.
*/
typedef enum
{
WINTUN_LOG_INFO, /**< Informational */
WINTUN_LOG_WARN, /**< Warning */
WINTUN_LOG_ERR /**< Error */
} WINTUN_LOGGER_LEVEL;
/**
* Called by internal logger to report diagnostic messages
*
* @param Level Message level.
*
* @param Timestamp Message timestamp in in 100ns intervals since 1601-01-01 UTC.
*
* @param Message Message text.
*/
typedef VOID(CALLBACK *WINTUN_LOGGER_CALLBACK)(
_In_ WINTUN_LOGGER_LEVEL Level,
_In_ DWORD64 Timestamp,
_In_z_ LPCWSTR Message);
/**
* Sets logger callback function.
*
* @param NewLogger Pointer to callback function to use as a new global logger. NewLogger may be called from various
* threads concurrently. Should the logging require serialization, you must handle serialization in
* NewLogger. Set to NULL to disable.
*/
typedef VOID(WINAPI WINTUN_SET_LOGGER_FUNC)(_In_ WINTUN_LOGGER_CALLBACK NewLogger);
/**
* Minimum ring capacity.
*/
#define WINTUN_MIN_RING_CAPACITY 0x20000 /* 128kiB */
/**
* Maximum ring capacity.
*/
#define WINTUN_MAX_RING_CAPACITY 0x4000000 /* 64MiB */
/**
* A handle representing Wintun session
*/
typedef struct _TUN_SESSION *WINTUN_SESSION_HANDLE;
/**
* Starts Wintun session.
*
* @param Adapter Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter
*
* @param Capacity Rings capacity. Must be between WINTUN_MIN_RING_CAPACITY and WINTUN_MAX_RING_CAPACITY (incl.)
* Must be a power of two.
*
* @return Wintun session handle. Must be released with WintunEndSession. If the function fails, the return value is
* NULL. To get extended error information, call GetLastError.
*/
typedef _Must_inspect_result_
_Return_type_success_(return != NULL)
_Post_maybenull_
WINTUN_SESSION_HANDLE(WINAPI WINTUN_START_SESSION_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _In_ DWORD Capacity);
/**
* Ends Wintun session.
*
* @param Session Wintun session handle obtained with WintunStartSession
*/
typedef VOID(WINAPI WINTUN_END_SESSION_FUNC)(_In_ WINTUN_SESSION_HANDLE Session);
/**
* Gets Wintun session's read-wait event handle.
*
* @param Session Wintun session handle obtained with WintunStartSession
*
* @return Pointer to receive event handle to wait for available data when reading. Should
* WintunReceivePackets return ERROR_NO_MORE_ITEMS (after spinning on it for a while under heavy
* load), wait for this event to become signaled before retrying WintunReceivePackets. Do not call
* CloseHandle on this event - it is managed by the session.
*/
typedef HANDLE(WINAPI WINTUN_GET_READ_WAIT_EVENT_FUNC)(_In_ WINTUN_SESSION_HANDLE Session);
/**
* Maximum IP packet size
*/
#define WINTUN_MAX_IP_PACKET_SIZE 0xFFFF
/**
* Retrieves one or packet. After the packet content is consumed, call WintunReleaseReceivePacket with Packet returned
* from this function to release internal buffer. This function is thread-safe.
*
* @param Session Wintun session handle obtained with WintunStartSession
*
* @param PacketSize Pointer to receive packet size.
*
* @return Pointer to layer 3 IPv4 or IPv6 packet. Client may modify its content at will. If the function fails, the
* return value is NULL. To get extended error information, call GetLastError. Possible errors include the
* following:
* ERROR_HANDLE_EOF Wintun adapter is terminating;
* ERROR_NO_MORE_ITEMS Wintun buffer is exhausted;
* ERROR_INVALID_DATA Wintun buffer is corrupt
*/
typedef _Must_inspect_result_
_Return_type_success_(return != NULL)
_Post_maybenull_
_Post_writable_byte_size_(*PacketSize)
BYTE *(WINAPI WINTUN_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _Out_ DWORD *PacketSize);
/**
* Releases internal buffer after the received packet has been processed by the client. This function is thread-safe.
*
* @param Session Wintun session handle obtained with WintunStartSession
*
* @param Packet Packet obtained with WintunReceivePacket
*/
typedef VOID(
WINAPI WINTUN_RELEASE_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet);
/**
* Allocates memory for a packet to send. After the memory is filled with packet data, call WintunSendPacket to send
* and release internal buffer. WintunAllocateSendPacket is thread-safe and the WintunAllocateSendPacket order of
* calls define the packet sending order.
*
* @param Session Wintun session handle obtained with WintunStartSession
*
* @param PacketSize Exact packet size. Must be less or equal to WINTUN_MAX_IP_PACKET_SIZE.
*
* @return Returns pointer to memory where to prepare layer 3 IPv4 or IPv6 packet for sending. If the function fails,
* the return value is NULL. To get extended error information, call GetLastError. Possible errors include the
* following:
* ERROR_HANDLE_EOF Wintun adapter is terminating;
* ERROR_BUFFER_OVERFLOW Wintun buffer is full;
*/
typedef _Must_inspect_result_
_Return_type_success_(return != NULL)
_Post_maybenull_
_Post_writable_byte_size_(PacketSize)
BYTE *(WINAPI WINTUN_ALLOCATE_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ DWORD PacketSize);
/**
* Sends the packet and releases internal buffer. WintunSendPacket is thread-safe, but the WintunAllocateSendPacket
* order of calls define the packet sending order. This means the packet is not guaranteed to be sent in the
* WintunSendPacket yet.
*
* @param Session Wintun session handle obtained with WintunStartSession
*
* @param Packet Packet obtained with WintunAllocateSendPacket
*/
typedef VOID(WINAPI WINTUN_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet);
#pragma warning(pop)
#ifdef __cplusplus
}
#endif
-19
View File
@@ -1,19 +0,0 @@
// Information about functions taken from:
// https://git.zx2c4.com/wintun/tree/example/example.c
#include "wintun.h"
WINTUN_CREATE_ADAPTER_FUNC WintunCreateAdapter;
WINTUN_CLOSE_ADAPTER_FUNC WintunCloseAdapter;
WINTUN_OPEN_ADAPTER_FUNC WintunOpenAdapter;
WINTUN_GET_ADAPTER_LUID_FUNC WintunGetAdapterLUID;
WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC WintunGetRunningDriverVersion;
WINTUN_DELETE_DRIVER_FUNC WintunDeleteDriver;
WINTUN_SET_LOGGER_FUNC WintunSetLogger;
WINTUN_START_SESSION_FUNC WintunStartSession;
WINTUN_END_SESSION_FUNC WintunEndSession;
WINTUN_GET_READ_WAIT_EVENT_FUNC WintunGetReadWaitEvent;
WINTUN_RECEIVE_PACKET_FUNC WintunReceivePacket;
WINTUN_RELEASE_RECEIVE_PACKET_FUNC WintunReleaseReceivePacket;
WINTUN_ALLOCATE_SEND_PACKET_FUNC WintunAllocateSendPacket;
WINTUN_SEND_PACKET_FUNC WintunSendPacket;
@@ -1,36 +1,35 @@
[package]
name = "switch-desktop"
version = "0.1.0"
name = "vnt-cli"
version = "1.1.1"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
switch = {path="../switch"}
mac_address = "1.1.4"
clap = { version = "4.0.32", features = ["derive"] }
vnt = {path="../vnt"}
common = {path="../common"}
tokio = { version = "1.28.1", features = ["full"] }
getopts = "0.2.21"
console = "0.15.2"
os_info = "3.7.0"
dirs = "4.0.0"
log = "0.4.17"
log4rs = "1.2.0"
#tokio = { version = "1.24.1", features = ["full"] }
chrono = "0.4.23"
serde = "1.0"
serde_yaml = "0.9"
serde_json = "1.0.94"
crossbeam = "0.8.2"
lazy_static = "1.4.0"
parking_lot = "0.12.1"
log = "0.4.17"
[dependencies.uuid]
version = "1.4.1"
features = [
"v4", # Lets you generate random UUIDs
]
fs2 = "0.4.3"
os_info = "3.5.1"
[target.'cfg(any(target_os = "linux",target_os = "macos"))'.dependencies]
sudo = "0.6.0"
[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3.9", features = ["handleapi", "processthreadsapi", "winnt", "securitybaseapi", "impl-default"] }
#runas = "0.2.1"
windows-service = "0.5.0"
[features]
default = []
mini = []
[build-dependencies]
embed-manifest = "1.4.0"
+40
View File
@@ -0,0 +1,40 @@
## 模块介绍
体积小,可以在服务器、路由器等环境使用
## 详细参数说明
### -k
一个虚拟局域网的标识,在同一服务器下,相同token的设备会组建一个局域网
### -n
设备名称,方便区分不同设备
### -d
设备id,每台设备的唯一标识,注意不要重复
### -c
关闭控制台交互式命令,后台运行时可以加此参数
### -s
注册和中继服务器地址,注册和转发数据
### -e
探测客户端NAT类型,不同类型有不同的打洞策略
### -a
加了此参数表示使用tap网卡,默认使用tun网卡,tun网卡效率更高
### -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.10** ,表示允许将192.168.0.0/24的数据从网卡192.168.0.10转发出去
### -w
提升通信安全性,使用该密码生成的密钥对客户端数据进行加密,并且服务端无法解密。使用相同密码的客户端才能通信
### -m
模拟组播,高频使用组播通信时,可以尝试开启此参数,默认情况下会把组播当作广播发给所有节点
默认情况(组播当广播发送):稳定性好,使用组播频率低时更省流量
模拟组播:高频使用组播时防止广播泛洪,客户端和中继服务器会维护组播成员等信息,注意使用此选项时,虚拟网内所有成员都需要开启此选项
### -u
设置虚拟网卡的mtu值,大多数情况下使用默认值效率会更高,也可根据实际情况微调这个值,默认值为1430
+10
View File
@@ -0,0 +1,10 @@
// use embed_manifest::{embed_manifest, new_manifest};
// use embed_manifest::manifest::ExecutionLevel;
fn main() {
////强制用管理员运行貌似体验更差了
// if std::env::var_os("CARGO_CFG_WINDOWS").is_some() {
// embed_manifest(new_manifest("vnt")
// .requested_execution_level(ExecutionLevel::RequireAdministrator)).expect("unable to embed manifest file");
// }
}
@@ -1,8 +1,9 @@
use std::io;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
use std::str::FromStr;
use std::time::Duration;
use crate::command::entity::{DeviceItem, RouteItem, Status};
use crate::command::entity::{DeviceItem, RouteItem, Info};
pub struct CommandClient {
udp: UdpSocket,
@@ -10,7 +11,17 @@ pub struct CommandClient {
impl CommandClient {
pub fn new() -> io::Result<Self> {
let port = crate::config::read_command_port()?;
let path_buf = crate::app_home()?.join("command-port");
if !path_buf.exists() {
return Err(io::Error::new(io::ErrorKind::Other, "not started"));
}
let port = std::fs::read_to_string(path_buf)?;
let port = match u16::from_str(&port) {
Ok(port) => { port }
Err(_) => {
return Err(io::Error::new(io::ErrorKind::Other, "'command-port' file error"));
}
};
let udp = UdpSocket::bind("127.0.0.1:0")?;
udp.set_read_timeout(Some(Duration::from_secs(2)))?;
udp.connect(SocketAddr::V4(SocketAddrV4::new(
@@ -50,11 +61,11 @@ impl CommandClient {
}
}
}
pub fn status(&self) -> io::Result<Status> {
self.udp.send(b"status")?;
pub fn info(&self) -> io::Result<Info> {
self.udp.send(b"info")?;
let mut buf = [0; 10240];
let len = self.udp.recv(&mut buf)?;
match serde_json::from_slice::<Status>(&buf[..len]) {
match serde_json::from_slice::<Info>(&buf[..len]) {
Ok(val) => {
Ok(val)
}
@@ -64,7 +75,6 @@ impl CommandClient {
}
}
}
#[cfg(any(unix))]
pub fn stop(&self) -> io::Result<String> {
self.udp.send(b"stop")?;
let mut buf = [0; 10240];
@@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct Status {
pub struct Info {
pub name: String,
pub virtual_ip: String,
pub virtual_gateway: String,
+141
View File
@@ -0,0 +1,141 @@
use std::io;
use vnt::core::Vnt;
use crate::command::entity::{DeviceItem, RouteItem, Info};
use crate::console_out;
pub mod client;
pub mod server;
pub mod entity;
pub enum CommandEnum {
Route,
List,
All,
Info,
Stop,
}
pub fn command(cmd: CommandEnum) {
if let Err(e) = command_(cmd) {
println!("cmd: {}", e);
}
}
fn command_(cmd: CommandEnum) -> io::Result<()> {
let command_client = client::CommandClient::new()?;
match cmd {
CommandEnum::Route => {
let list = command_client.route()?;
console_out::console_route_table(list);
}
CommandEnum::List => {
let list = command_client.list()?;
console_out::console_device_list(list);
}
CommandEnum::All => {
let list = command_client.list()?;
console_out::console_device_list_all(list);
}
CommandEnum::Info => {
let info = command_client.info()?;
console_out::console_info(info);
}
CommandEnum::Stop => {
command_client.stop()?;
}
}
Ok(())
}
pub fn command_route(vnt: &Vnt) -> Vec<RouteItem> {
let route_table = vnt.route_table();
let mut route_list = Vec::with_capacity(route_table.len());
for (destination, route) in route_table {
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 = route.addr.to_string();
let item = RouteItem {
destination: destination.to_string(),
next_hop,
metric,
rt,
interface,
};
route_list.push(item);
}
route_list
}
pub fn command_list(vnt: &Vnt) -> Vec<DeviceItem> {
let device_list = vnt.device_list();
let mut list = Vec::new();
for peer in device_list {
let name = peer.name;
let virtual_ip = peer.virtual_ip.to_string();
let (nat_type, public_ips, local_ip) = if let Some(nat_info) = vnt.peer_nat_info(&peer.virtual_ip) {
let nat_type = format!("{:?}", nat_info.nat_type);
let public_ips: Vec<String> = nat_info.public_ips.iter().map(|v| v.to_string()).collect();
let public_ips = public_ips.join(",");
let local_ip = nat_info.local_ip.to_string();
(nat_type, public_ips, local_ip)
} else {
("".to_string(), "".to_string(), "".to_string())
};
let (nat_traversal_type, rt) = if let Some(route) = vnt.route(&peer.virtual_ip) {
let nat_traversal_type = if route.metric == 1 { "p2p" } else { "relay" }.to_string();
let rt = if route.rt < 0 {
"".to_string()
} else {
route.rt.to_string()
};
(nat_traversal_type, rt)
} else {
("relay".to_string(), "".to_string())
};
let status = format!("{:?}", peer.status);
let item = DeviceItem {
name,
virtual_ip,
nat_type,
public_ips,
local_ip,
nat_traversal_type,
rt,
status,
};
list.push(item);
}
list
}
pub fn command_info(vnt: &Vnt) -> Info {
let current_device = vnt.current_device();
let nat_info = vnt.nat_info();
let name = vnt.name().to_string();
let virtual_ip = current_device.virtual_ip().to_string();
let virtual_gateway = current_device.virtual_gateway().to_string();
let virtual_netmask = current_device.virtual_netmask.to_string();
let connect_status = format!("{:?}", vnt.connection_status());
let relay_server = current_device.connect_server.to_string();
let nat_type = format!("{:?}", nat_info.nat_type);
let public_ips: Vec<String> = nat_info.public_ips.iter().map(|v| v.to_string()).collect();
let public_ips = public_ips.join(",");
let local_ip = nat_info.local_ip.to_string();
Info {
name,
virtual_ip,
virtual_gateway,
virtual_netmask,
connect_status,
relay_server,
nat_type,
public_ips,
local_ip,
}
}
+102
View File
@@ -0,0 +1,102 @@
use std::io;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use tokio::net::UdpSocket;
use vnt::core::Vnt;
pub struct CommandServer {}
impl CommandServer {
pub fn new() -> Self {
Self {}
}
}
impl CommandServer {
pub async fn start(self, vnt: Vnt) -> io::Result<()> {
let mut port = 21637 as u16;
let udp = loop {
match UdpSocket::bind(SocketAddr::V4(SocketAddrV4::new(
Ipv4Addr::new(127, 0, 0, 1),
port,
))).await {
Ok(udp) => {
break udp;
}
Err(e) => {
if e.kind() == io::ErrorKind::AddrInUse {
port += 1;
} else {
log::error!("创建udp失败 {:?}", e);
return Err(e);
}
}
}
};
let path_buf = crate::app_home()?.join("command-port");
std::fs::write(path_buf, udp.local_addr()?.port().to_string())?;
let mut buf = [0u8; 64];
loop {
let (len, addr) = udp.recv_from(&mut buf).await?;
match std::str::from_utf8(&buf[..len]) {
Ok(cmd) => {
if let Ok(out) = command(cmd, &vnt) {
let _ = udp.send_to(out.as_bytes(), addr).await;
if "stopped" == &out {
break;
}
}
}
Err(e) => {
log::warn!("{:?}", e);
}
}
}
Ok(())
}
}
fn command(cmd: &str, vnt: &Vnt) -> io::Result<String> {
let out_str = match cmd {
"route" => {
match serde_json::to_string(&crate::command::command_route(vnt)) {
Ok(str) => {
str
}
Err(e) => {
format!("{:?}", e)
}
}
}
"list" => {
match serde_json::to_string(&crate::command::command_list(vnt)) {
Ok(str) => {
str
}
Err(e) => {
format!("{:?}", e)
}
}
}
"info" => {
match serde_json::to_string(&crate::command::command_info(vnt)) {
Ok(str) => {
str
}
Err(e) => {
format!("{:?}", e)
}
}
}
"stop" => {
vnt.stop()?;
"stopped".to_string()
}
_ => {
format!("command '{}' not found. \n Try to enter: 'help'\n", cmd)
}
};
Ok(out_str)
}
@@ -1,10 +1,10 @@
use console::{style, Style};
use crate::command::entity::{DeviceItem, RouteItem, Status};
use crate::command::entity::{DeviceItem, RouteItem, Info};
pub mod table;
pub fn console_status(status: Status) {
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());
+324
View File
@@ -0,0 +1,324 @@
use std::io;
use std::net::{Ipv4Addr, ToSocketAddrs};
use std::path::PathBuf;
use std::str::FromStr;
use console::style;
use getopts::Options;
use tokio::io::{AsyncBufReadExt, BufReader};
use common::args_parse::ips_parse;
use vnt::core::{Config, VntUtil};
use vnt::handle::registration_handler::ReqEnum;
mod command;
mod console_out;
mod root_check;
pub fn app_home() -> io::Result<PathBuf> {
let path = dirs::home_dir().ok_or(io::Error::new(io::ErrorKind::Other, "not home"))?.join(".vnt-cli");
if !path.exists() {
std::fs::create_dir_all(&path)?;
}
Ok(path)
}
#[tokio::main]
async fn main() {
main0().await;
std::process::exit(0);
}
async fn main0() {
let args: Vec<String> = std::env::args().collect();
let program = args[0].clone();
let mut opts = Options::new();
opts.long_only(false);
opts.optopt("k", "", &format!("{}", green("必选,使用相同的token,就能组建一个局域网络".to_string())), "<token>");
opts.optopt("n", "", "给设备一个名字,默认使用系统版本", "<name>");
opts.optopt("d", "", "设备唯一标识符,不使用--ip参数时,服务端凭此参数分配ip", "<id>");
opts.optflag("c", "", "关闭交互式命令,使用此参数禁用控制台输入");
opts.optopt("s", "", "注册和中继服务器地址", "<server>");
opts.optopt("e", "", "NAT探测服务器地址,使用逗号分隔", "<addr1,addr2>");
opts.optflag("a", "", "使用tap模式,默认使用tun模式");
opts.optmulti("i", "", "配置点对网(IP代理)时使用,-i 192.168.0.0/24,10.26.0.3,表示允许接收网段192.168.0.0/24的数据并转发到10.26.0.3", "<in-ip>");
opts.optmulti("o", "", "配置点对网时使用,-o 192.168.0.0/24,192.168.0.10,表示允许目标为192.168.0.0/24的数据从网卡192.168.0.10转发出去", "<out-ip>");
opts.optopt("w", "", "使用该密码生成的密钥对客户端数据进行加密,并且服务端无法解密,使用相同密码的客户端才能通信", "<password>");
opts.optflag("m", "", "模拟组播,默认情况下组播数据会被当作广播发送,开启后会模拟真实组播的数据发送");
opts.optopt("u", "", "虚拟网卡mtu值", "<mtu>");
opts.optflag("", "tcp", "和服务端使用tcp通信,默认使用udp,一般来说udp延迟和消耗更低");
opts.optopt("", "ip", "指定虚拟ip,指定的ip不能和其他设备重复,必须有效并且在服务端所属网段下,默认情况由服务端分配", "<IP>");
opts.optflag("", "relay", "仅使用服务器转发,不使用p2p,默认情况允许使用p2p");
//"后台运行时,查看其他设备列表"
opts.optflag("", "list", &format!("{}", yellow("后台运行时,查看其他设备列表".to_string())));
opts.optflag("", "all", &format!("{}", yellow("后台运行时,查看其他设备完整信息".to_string())));
opts.optflag("", "info", &format!("{}", yellow("后台运行时,查看当前设备信息".to_string())));
opts.optflag("", "route", &format!("{}", yellow("后台运行时,查看数据转发路径".to_string())));
opts.optflag("", "stop", &format!("{}", yellow("停止后台运行".to_string())));
opts.optflag("h", "help", "帮助");
let matches = match opts.parse(&args[1..]) {
Ok(m) => { m }
Err(f) => {
print_usage(&program, opts);
println!("{}", f.to_string());
return;
}
};
if matches.opt_present("h") || args.len() == 1 {
print_usage(&program, opts);
return;
}
if !root_check::is_app_elevated() {
println!("Please run it with administrator or root privileges");
#[cfg(any(target_os = "linux", target_os = "macos"))]
sudo::escalate_if_needed().unwrap();
return;
}
if matches.opt_present("list") {
command::command(command::CommandEnum::List);
return;
} else if matches.opt_present("info") {
command::command(command::CommandEnum::Info);
return;
} else if matches.opt_present("stop") {
command::command(command::CommandEnum::Stop);
return;
} else if matches.opt_present("route") {
command::command(command::CommandEnum::Route);
return;
} else if matches.opt_present("all") {
command::command(command::CommandEnum::All);
return;
}
if !matches.opt_present("k") {
print_usage(&program, opts);
println!("parameter -k not found .");
return;
}
let tap = matches.opt_present("a");
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() {
if let Some(id) = common::identifier::get_unique_identifier() {
id
} else {
let path_buf = app_home().unwrap().join("device-id");
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
}
}
} else {
device_id
};
if device_id.is_empty() {
print_usage(&program, opts);
println!("parameter -d not found .");
return;
}
let name = matches.opt_get_default("n", os_info::get().to_string()).unwrap();
let server_address_str = matches.opt_get_default("s", "nat1.wherewego.top:29871".to_string()).unwrap();
let server_address = match server_address_str.to_socket_addrs() {
Ok(mut addr) => {
if let Some(addr) = addr.next() {
addr
} else {
println!("parameter -s error .");
return;
}
}
Err(e) => {
println!("parameter -s error {}.", e);
return;
}
};
let nat_test_server = matches.opt_get_default("e",
"nat1.wherewego.top:35061,nat1.wherewego.top:35062,nat2.wherewego.top:35061,nat2.wherewego.top:35062".to_string()).unwrap();
let nat_test_server = nat_test_server.split(",").flat_map(|a| a.to_socket_addrs()).flatten()
.collect::<Vec<_>>();
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 {}", e);
return;
}
};
let out_ip = matches.opt_strs("o");
let out_ip = match ips_parse(&out_ip) {
Ok(out_ip) => { out_ip }
Err(e) => {
print_usage(&program, opts);
println!();
println!("-o {}", e);
return;
}
};
let password: Option<String> = matches.opt_get("w").unwrap();
let simulate_multicast = matches.opt_present("m");
let unused_cmd = matches.opt_present("c");
let mtu: Option<String> = matches.opt_get("u").unwrap();
let mtu = if let Some(mtu) = mtu {
match u16::from_str(&mtu) {
Ok(mtu) => {
Some(mtu)
}
Err(e) => {
print_usage(&program, opts);
println!();
println!("-u {}", e);
return;
}
}
} else {
None
};
let virtual_ip: Option<String> = matches.opt_get("ip").unwrap();
let virtual_ip = virtual_ip.map(|v| Ipv4Addr::from_str(&v).expect("--ip error"));
if let Some(virtual_ip) = virtual_ip {
if virtual_ip.is_unspecified() || virtual_ip.is_broadcast() || virtual_ip.is_multicast() {
println!("--ip invalid");
return;
}
}
let tcp_channel = matches.opt_present("tcp");
let relay = matches.opt_present("relay");
let config = Config::new(tap,
token, device_id, name,
server_address, server_address_str,
nat_test_server, in_ip,
out_ip, password, simulate_multicast, mtu, tcp_channel, virtual_ip, relay);
let mut vnt_util = VntUtil::new(config).await.unwrap();
let response = loop {
match vnt_util.connect().await {
Ok(response) => {
break response;
}
Err(e) => {
match e {
ReqEnum::TokenError => {
println!("token error");
}
ReqEnum::AddressExhausted => {
println!("address exhausted");
}
ReqEnum::Timeout => {
println!("timeout...");
continue;
}
ReqEnum::ServerError(str) => {
println!("error:{}", str);
continue;
}
ReqEnum::Other(str) => {
println!("error:{}", str);
continue;
}
ReqEnum::IpAlreadyExists => {
println!("ip already exists");
}
ReqEnum::InvalidIp => {
println!("invalid ip");
}
}
return;
}
}
};
println!(" ====== Connect Successfully ====== ");
println!("virtual_gateway:{}", response.virtual_gateway);
println!("virtual_ip:{}", green(response.virtual_ip.to_string()));
let driver_info = vnt_util.create_iface().unwrap();
println!(" ====== Create Network Interface Successfully ====== ");
println!("name:{}", driver_info.name);
println!("version:{}", driver_info.version);
let mut vnt = match vnt_util.build().await {
Ok(vnt) => {
vnt
}
Err(e) => {
println!("error:{}", e);
return;
}
};
println!(" ====== Start Successfully ====== ");
let vnt_c = vnt.clone();
tokio::spawn(async {
if let Err(e) = command::server::CommandServer::new().start(vnt_c).await {
println!("command error :{}", e);
}
});
if !unused_cmd {
let stdin = tokio::io::stdin();
let mut cmd = String::new();
let mut reader = BufReader::new(stdin);
loop {
cmd.clear();
println!("input:list,info,route,all,stop");
tokio::select! {
_ = vnt.wait_stop()=>{
break;
}
rs = reader.read_line(&mut cmd)=>{
match rs {
Ok(len) => {
if len ==0 {
break;
}
match cmd[..len].to_lowercase().trim() {
"list" => {
let list = command::command_list(&vnt);
console_out::console_device_list(list);
}
"info"=>{
let info = command::command_info(&vnt);
console_out::console_info(info);
}
"route" =>{
let route = command::command_route(&vnt);
console_out::console_route_table(route);
}
"all" =>{
let list = command::command_list(&vnt);
console_out::console_device_list_all(list);
}
"stop" =>{
let _ = vnt.stop();
break;
}
_ => {
}
}
println!();
}
Err(e) => {
println!("input err:{}",e);
break;
}
}
}
}
}
}
vnt.wait_stop().await;
}
fn print_usage(program: &str, opts: Options) {
let brief = format!("Usage: {} [options]", program);
println!("version:1.1.1");
println!("{}", opts.usage(&brief));
}
fn green(str: String) -> impl std::fmt::Display {
style(str).green()
}
fn yellow(str: String) -> impl std::fmt::Display {
style(str).yellow()
}
+11
View File
@@ -0,0 +1,11 @@
#[cfg(target_os = "windows")]
mod windows;
#[cfg(target_os = "windows")]
pub use windows::is_app_elevated;
#[cfg(any(target_os = "linux", target_os = "macos"))]
mod unix;
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub use unix::is_app_elevated;
+3
View File
@@ -0,0 +1,3 @@
pub fn is_app_elevated() -> bool {
sudo::RunningAs::Root == sudo::check()
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "vnt-jni"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
vnt = {path="../vnt"}
jni = { version = "0.21.1", default-features = false }
[lib]
crate-type = ["staticlib", "cdylib"]
+1
View File
@@ -0,0 +1 @@
## 提供给安卓端使用
+2
View File
@@ -0,0 +1,2 @@
pub mod vnt_util;
pub mod vnt;
+138
View File
@@ -0,0 +1,138 @@
use std::ptr;
use jni::errors::Error;
use jni::JNIEnv;
use jni::objects::{JClass, JObject, JValue};
use jni::sys::{jboolean, jbyte, jint, jlong, jobject, jobjectArray, jsize};
use vnt::channel::Route;
use vnt::core::sync::VntSync;
use vnt::handle::PeerDeviceInfo;
#[no_mangle]
pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_Vnt_stop0(
_env: JNIEnv,
_class: JClass,
raw_vnt: jlong,
) {
let vnt = raw_vnt as *mut VntSync;
let _ = (&*vnt).stop();
}
#[no_mangle]
pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_Vnt_waitStop0(
_env: JNIEnv,
_class: JClass,
raw_vnt: jlong,
) {
let vnt = raw_vnt as *mut VntSync;
let _ = (&mut *vnt).wait_stop();
}
#[no_mangle]
pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_Vnt_waitStopMs0(
_env: JNIEnv,
_class: JClass,
raw_vnt: jlong,
ms: jlong,
) -> jboolean {
let vnt = raw_vnt as *mut VntSync;
if (&mut *vnt).wait_stop_ms(ms as _) {
jni::sys::JNI_TRUE
} else {
jni::sys::JNI_FALSE
}
}
#[no_mangle]
pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_Vnt_drop0(
_env: JNIEnv,
_class: JClass,
raw_vnt: jlong,
) {
let vnt = raw_vnt as *mut VntSync;
let _ = Box::from_raw(vnt).stop();
}
#[no_mangle]
pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_Vnt_list0(
mut env: JNIEnv,
_class: JClass,
raw_vnt: jlong,
) -> jobjectArray {
let vnt = raw_vnt as *mut VntSync;
let vnt = &mut *vnt;
let list = vnt.device_list();
let arr = match env.new_object_array(
list.len() as jsize,
"top/wherewego/vnt/jni/PeerDeviceInfo",
JObject::null(),
) {
Ok(arr) => { arr }
Err(e) => {
env.throw_new("java/lang/RuntimeException", format!("error:{:?}", e))
.expect("throw");
return ptr::null_mut();
}
};
for (index, peer) in list.into_iter().enumerate() {
let route = if let Some(route) = vnt.route(&peer.virtual_ip) {
match route_parse(&mut env, route) {
Ok(route) => {
JObject::from_raw(route)
}
Err(_) => {
JObject::null()
}
}
} else {
JObject::null()
};
match peer_device_info_parse(&mut env, peer, route) {
Ok(peer) => {
match env.set_object_array_element(&arr, index as jsize, JObject::from_raw(peer)) {
Ok(_) => {}
Err(e) => {
env.throw_new("java/lang/RuntimeException", format!("error:{:?}", e))
.expect("throw");
return ptr::null_mut();
}
}
}
Err(e) => {
env.throw_new("java/lang/RuntimeException", format!("error:{:?}", e))
.expect("throw");
return ptr::null_mut();
}
}
}
arr.as_raw()
}
fn route_parse(env: &mut JNIEnv, route: Route) -> Result<jobject, Error> {
let address = route.addr.to_string();
let metric = route.metric;
let rt = route.rt;
let rs = env.new_object(
"top/wherewego/vnt/jni/Route",
"(Ljava/lang/String;BI)V",
&[JValue::Object(&env.new_string(address)?.into()),
JValue::Byte(metric as jbyte),
JValue::Int(rt as jint)],
)?;
Ok(rs.as_raw())
}
fn peer_device_info_parse(env: &mut JNIEnv, peer: PeerDeviceInfo, route: JObject) -> Result<jobject, Error> {
let virtual_ip = u32::from(peer.virtual_ip);
let name = peer.name.to_string();
let status = format!("{:?}", peer.status);
let rs = env.new_object(
"top/wherewego/vnt/jni/PeerDeviceInfo",
"(ILjava/lang/String;Ljava/lang/String;Ltop/wherewego/vnt/jni/Route;)V",
&[JValue::Int(virtual_ip as jint),
JValue::Object(&env.new_string(name)?.into()),
JValue::Object(&env.new_string(status)?.into()),
JValue::Object(&route)],
)?;
Ok(rs.as_raw())
}
+253
View File
@@ -0,0 +1,253 @@
use std::net::ToSocketAddrs;
use std::ptr;
use jni::errors::Error;
use jni::objects::{JClass, JObject, JString, JValue};
#[cfg(not(target_os = "android"))]
use jni::sys::jboolean;
use jni::sys::{jint, jlong, jobject};
use jni::JNIEnv;
use vnt::core::Config;
use vnt::core::sync::VntUtilSync;
use vnt::handle::registration_handler::{RegResponse, ReqEnum};
#[cfg(not(target_os = "android"))]
use vnt::tun_tap_device::DriverInfo;
fn to_string_not_null(env: &mut JNIEnv, config: &JObject, name: &'static str) -> Result<String, Error> {
let value = env.get_field(config, name, "Ljava/lang/String;")?.l()?;
if value.is_null() {
env.throw_new("Ljava/lang/NullPointerException", name)
.expect("throw");
return Err(Error::NullPtr(name));
}
let binding = JString::from(value);
let value = env.get_string(binding.as_ref())?;
match value.to_str() {
Ok(value) => Ok(value.to_string()),
Err(_) => {
env.throw_new("Ljava/lang/RuntimeException", "not utf-8")
.expect("throw");
return Err(Error::JavaException);
}
}
}
fn to_string(env: &mut JNIEnv, config: &JObject, name: &str) -> Result<Option<String>, Error> {
let value = env.get_field(config, name, "Ljava/lang/String;")?.l()?;
if value.is_null() {
return Ok(None);
}
let tmp = JString::from(value);
let value = env.get_string(tmp.as_ref())?;
match value.to_str() {
Ok(value) => Ok(Some(value.to_string())),
Err(_) => {
env.throw_new("Ljava/lang/RuntimeException", "not utf-8")
.expect("throw");
return Err(Error::JavaException);
}
}
}
fn new_sync(env: &mut JNIEnv, config: JObject) -> Result<VntUtilSync, Error> {
let token = to_string_not_null(env, &config, "token")?;
let name = to_string_not_null(env, &config, "name")?;
let device_id = to_string_not_null(env, &config, "deviceId")?;
let password = to_string(env, &config, "password")?;
let server_address_str = to_string_not_null(env, &config, "server")?;
let nat_test_server = to_string_not_null(env, &config, "natTestServer")?;
let server_address = match server_address_str.to_socket_addrs() {
Ok(mut rs) => {
if let Some(addr) = rs.next() {
addr
} else {
env.throw_new("Ljava/lang/RuntimeException", "server address err")
.expect("throw");
return Err(Error::JavaException);
}
}
Err(e) => {
env.throw_new("Ljava/lang/RuntimeException", format!("server address {}", e))
.expect("throw");
return Err(Error::JavaException);
}
};
let nat_test_server = nat_test_server.split(",").flat_map(|a| a.trim().to_socket_addrs()).flatten()
.collect::<Vec<_>>();
let config = Config::new(false,
token, device_id, name,
server_address, server_address_str,
nat_test_server, vec![],
vec![], password, false, None, false, None, false);
match VntUtilSync::new(config) {
Ok(vnt_util) => {
Ok(vnt_util)
}
Err(e) => {
env.throw_new("Ljava/lang/RuntimeException", format!("vnt start error {}", e))
.expect("throw");
return Err(Error::JavaException);
}
}
}
#[no_mangle]
pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_VntUtil_new0(
mut env: JNIEnv,
_class: JClass,
config: JObject,
) -> jlong {
match new_sync(&mut env, config) {
Ok(vnt_util) => {
let ptr = Box::into_raw(Box::new(vnt_util));
return ptr as jlong;
}
Err(_) => {}
}
return 0;
}
#[no_mangle]
pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_VntUtil_connect0(
mut env: JNIEnv,
_class: JClass,
raw_vnt_util: jlong,
) -> jobject {
let raw_vnt_util = raw_vnt_util as *mut VntUtilSync;
match (&mut *raw_vnt_util).connect() {
Ok(response) => {
match reg_response(&mut env, response) {
Ok(res) => {
return res;
}
Err(e) => {
env.throw(format!("vnt connect error {}", e)).expect("throw");
}
}
}
Err(e) => {
match e {
ReqEnum::TokenError => {
env.throw_new("top/wherewego/vnt/jni/exception/TokenErrorException", "TokenError")
.expect("throw");
}
ReqEnum::AddressExhausted => {
env.throw_new("top/wherewego/vnt/jni/exception/AddressExhaustedException", "AddressExhausted")
.expect("throw");
}
ReqEnum::Timeout => {
env.throw_new("top/wherewego/vnt/jni/exception/TimeoutException", "Timeout")
.expect("throw");
}
ReqEnum::ServerError(str) => {
env.throw_new("java/lang/RuntimeException", format!("vnt connect error {}", str))
.expect("throw");
}
ReqEnum::Other(str) => {
env.throw_new("java/lang/RuntimeException", format!("vnt connect error {}", str))
.expect("throw");
}
ReqEnum::IpAlreadyExists => {
env.throw_new("top/wherewego/vnt/jni/exception/IpAlreadyExistsException", "IpAlreadyExists")
.expect("throw");
}
ReqEnum::InvalidIp => {
env.throw_new("top/wherewego/vnt/jni/exception/InvalidIpException", "InvalidIp")
.expect("throw");
}
}
}
}
return ptr::null_mut();
}
#[cfg(target_os = "android")]
#[no_mangle]
pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_VntUtil_createIface0(
_env: JNIEnv,
_class: JClass,
raw_vnt_util: jlong,
fd: jint,
) {
let raw_vnt_util = raw_vnt_util as *mut VntUtilSync;
(&mut *raw_vnt_util).create_iface(fd as i32);
}
#[cfg(not(target_os = "android"))]
#[no_mangle]
pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_VntUtil_createIface0(
mut env: JNIEnv,
_class: JClass,
raw_vnt_util: jlong,
) -> jobject {
let raw_vnt_util = raw_vnt_util as *mut VntUtilSync;
let rs = (&mut *raw_vnt_util).create_iface();
match rs {
Ok(driver_info) => {
match driver_info_e(&mut env, driver_info) {
Ok(res) => {
return res;
}
Err(e) => {
env.throw(format!("vnt create iface error {}", e)).expect("throw");
}
}
}
Err(e) => {
env.throw_new("java/lang/RuntimeException", format!("vnt create iface error {}", e))
.expect("throw");
}
}
return ptr::null_mut();
}
#[no_mangle]
pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_VntUtil_build0(
mut env: JNIEnv,
_class: JClass,
raw_vnt_util: jlong,
) -> jlong {
let raw_vnt_util = Box::from_raw(raw_vnt_util as *mut VntUtilSync);
match raw_vnt_util.build() {
Ok(rs) => {
return Box::into_raw(Box::new(rs)) as jlong;
}
Err(e) => {
env.throw_new("java/lang/RuntimeException", format!("vnt start error:{:?}", e))
.expect("throw");
}
}
return 0;
}
fn reg_response(env: &mut JNIEnv, response: RegResponse) -> Result<jobject, Error> {
let virtual_ip = u32::from(response.virtual_ip);
let virtual_gateway = u32::from(response.virtual_gateway);
let virtual_netmask = u32::from(response.virtual_netmask);
let response = env.new_object(
"top/wherewego/vnt/jni/RegResponse",
"(III)V",
&[JValue::Int(virtual_ip as jint),
JValue::Int(virtual_gateway as jint),
JValue::Int(virtual_netmask as jint)],
)?;
Ok(response.into_raw())
}
#[cfg(not(target_os = "android"))]
fn driver_info_e(env: &mut JNIEnv, driver_info: DriverInfo) -> Result<jobject, Error> {
let is_tun = driver_info.device_type.is_tun();
let name = driver_info.name;
let version = driver_info.version;
let mac = driver_info.mac.unwrap_or(String::new());
let response = env.new_object(
"top/wherewego/vnt/jni/DriverInfo",
"(ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
&[JValue::Bool(is_tun as jboolean),
JValue::Object(&env.new_string(name)?.into()),
JValue::Object(&env.new_string(version)?.into()),
JValue::Object(&env.new_string(mac)?.into()), ],
)?;
Ok(response.into_raw())
}
+10 -17
View File
@@ -1,42 +1,35 @@
[package]
name = "switch"
version = "0.1.0"
name = "vnt"
version = "1.1.1"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
packet = { path = "./packet" }
p2p_channel = { path = "./p2p_channel" }
bytes = "1.3.0"
log = "0.4.17"
libc = "0.2.137"
dashmap = "5.4.0"
crossbeam = "0.8.2"
crossbeam-utils = "0.8"
crossbeam-skiplist = "0.1"
parking_lot = "0.12.1"
rsa = "0.7.2"
rand = "0.8.5"
sha2 = { version = "0.10.6", features = ["oid"] }
aes-gcm = "0.10.2"
thiserror = "1.0.37"
chrono = "0.4.23"
#lazy_static = "1.4.0"
#moka = "0.9.6"
protobuf = "3.2.0"
#local-ip-address = "0.4.9"
#mio = {version = "0.8.6",features = ["os-poll", "net"]}
#tokio = { version = "1.24.1", features = ["full"] }
[target.'cfg(any(unix))'.dependencies]
socket2 ={ version = "0.5.2", features = ["all"] }
tokio = { version = "1.28.1", features = ["full"] }
[target.'cfg(any(target_os = "linux",target_os = "macos"))'.dependencies]
tun = { path = "./rust-tun" }
[target.'cfg(target_os = "windows")'.dependencies]
wintun = { path = "./wintun" }
win-tun-tap = {path = "./win-tun-tap"}
libloading = "0.7.4"
[build-dependencies]
protobuf-codegen = "3.2.0"
protoc-bin-vendored = "3.0.0"
View File
View File
@@ -7,4 +7,3 @@ edition = "2021"
[dependencies]
byteorder = "1.4.3"
thiserror = "1.0.37"
+122
View File
@@ -0,0 +1,122 @@
use std::{fmt, io};
/// 地址解析协议,由IP地址找到MAC地址
/// https://www.ietf.org/rfc/rfc6747.txt
/*
0 2 4 5 6 8 10 (字节)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 硬件类型|协议类型|硬件地址长度|协议地址长度|操作类型|
| 源MAC地址 | 源ip地址 |
| 目的MAC地址 | 目的ip地址 |
*/
pub struct ArpPacket<B> {
buffer: B,
}
impl<B: AsRef<[u8]>> ArpPacket<B> {
pub fn unchecked(buffer: B) -> Self {
Self { buffer }
}
pub fn new(buffer: B) -> io::Result<Self> {
if buffer.as_ref().len() != 28 {
Err(io::Error::from(io::ErrorKind::InvalidData))?;
}
let packet = Self::unchecked(buffer);
Ok(packet)
}
}
impl<B: AsRef<[u8]>> ArpPacket<B> {
/// 硬件类型 以太网类型为1
pub fn hardware_type(&self) -> u16 {
u16::from_be_bytes(self.buffer.as_ref()[0..2].try_into().unwrap())
}
/// 上层协议类型,ipv4是0x0800
pub fn protocol_type(&self) -> u16 {
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
}
/// 如果是MAC地址 则长度为6
pub fn hardware_size(&self) -> u8 {
self.buffer.as_ref()[4]
}
/// 如果是IPv4 则长度为4
pub fn protocol_size(&self) -> u8 {
self.buffer.as_ref()[5]
}
/// 操作类型,请求和响应 1:ARP请求,2:ARP响应,3RARP请求,4RARP响应
pub fn op_code(&self) -> u16 {
u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap())
}
/// 发送端硬件地址,仅支持以太网
pub fn sender_hardware_addr(&self) -> &[u8] {
&self.buffer.as_ref()[8..14]
}
/// 发送端协议地址,仅支持IPv4
pub fn sender_protocol_addr(&self) -> &[u8] {
&self.buffer.as_ref()[14..18]
}
/// 接收端硬件地址,仅支持以太网
pub fn target_hardware_addr(&self) -> &[u8] {
&self.buffer.as_ref()[18..24]
}
/// 接收端协议地址,仅支持IPv4
pub fn target_protocol_addr(&self) -> &[u8] {
&self.buffer.as_ref()[24..28]
}
}
impl<B: AsRef<[u8]> + AsMut<[u8]>> ArpPacket<B> {
/// 硬件类型 以太网类型为1
pub fn set_hardware_type(&mut self, value: u16) {
self.buffer.as_mut()[0..2].copy_from_slice(&value.to_be_bytes())
}
/// 上层协议类型,ipv4是0x0800
pub fn set_protocol_type(&mut self, value: u16) {
self.buffer.as_mut()[2..4].copy_from_slice(&value.to_be_bytes())
}
/// 如果是MAC地址 则长度为6
pub fn set_hardware_size(&mut self, value: u8) {
self.buffer.as_mut()[4] = value
}
/// 如果是IPv4 则长度为4
pub fn set_protocol_size(&mut self, value: u8) {
self.buffer.as_mut()[5] = value
}
/// 操作类型,请求和响应 1:ARP请求,2:ARP响应,3RARP请求,4RARP响应
pub fn set_op_code(&mut self, value: u16) {
self.buffer.as_mut()[6..8].copy_from_slice(&value.to_be_bytes())
}
/// 发送端硬件地址,仅支持以太网
pub fn set_sender_hardware_addr(&mut self, buf: &[u8]) {
self.buffer.as_mut()[8..14].copy_from_slice(buf)
}
/// 发送端协议地址,仅支持IPv4
pub fn set_sender_protocol_addr(&mut self, buf: &[u8]) {
self.buffer.as_mut()[14..18].copy_from_slice(buf)
}
/// 接收端硬件地址,仅支持以太网
pub fn set_target_hardware_addr(&mut self, buf: &[u8]) {
self.buffer.as_mut()[18..24].copy_from_slice(buf)
}
/// 接收端协议地址,仅支持IPv4
pub fn set_target_protocol_addr(&mut self, buf: &[u8]) {
self.buffer.as_mut()[24..28].copy_from_slice(buf)
}
}
impl<B: AsRef<[u8]>> fmt::Debug for ArpPacket<B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ArpPacket")
.field("hardware_type", &self.hardware_type())
.field("protocol_type", &self.protocol_type())
.field("hardware_size", &self.hardware_size())
.field("protocol_size", &self.protocol_size())
.field("op_code", &self.op_code())
.field("sender_hardware_addr", &self.sender_hardware_addr())
.field("sender_protocol_addr", &self.sender_protocol_addr())
.field("target_hardware_addr", &self.target_hardware_addr())
.field("target_protocol_addr", &self.target_protocol_addr())
.finish()
}
}
+1
View File
@@ -0,0 +1 @@
pub mod arp;
+2
View File
@@ -0,0 +1,2 @@
pub mod packet;
pub mod protocol;
+77
View File
@@ -0,0 +1,77 @@
use std::{fmt, io};
use crate::ethernet::protocol::Protocol;
/// 以太网帧协议
/// https://www.ietf.org/rfc/rfc894.txt
/*
0 6 12 14 (字节)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 目的地址 | 源地址 | 类型 |
*/
pub struct EthernetPacket<B> {
pub buffer: B,
}
impl<B: AsRef<[u8]>> EthernetPacket<B> {
pub fn unchecked(buffer: B) -> EthernetPacket<B> {
EthernetPacket { buffer }
}
pub fn new(buffer: B) -> io::Result<EthernetPacket<B>> {
let packet = EthernetPacket::unchecked(buffer);
//头部固定14位
if packet.buffer.as_ref().len() < 14 {
Err(io::Error::from(io::ErrorKind::InvalidData))?;
}
Ok(packet)
}
}
impl<B: AsRef<[u8]>> EthernetPacket<B> {
/// 目的MAC地址
pub fn destination(&self) -> &[u8] {
&self.buffer.as_ref()[0..6]
}
/// 源MAC地址
pub fn source(&self) -> &[u8] {
&self.buffer.as_ref()[6..12]
}
/// 3层协议
pub fn protocol(&self) -> Protocol {
u16::from_be_bytes(self.buffer.as_ref()[12..14].try_into().unwrap()).into()
}
/// 载荷
pub fn payload(&self) -> &[u8] {
&self.buffer.as_ref()[14..]
}
}
impl<B: AsRef<[u8]> + AsMut<[u8]>> EthernetPacket<B> {
pub fn set_destination(&mut self, value: &[u8]) {
self.buffer.as_mut()[0..6].copy_from_slice(value);
}
pub fn set_source(&mut self, value: &[u8]) {
self.buffer.as_mut()[6..12].copy_from_slice(value);
}
pub fn set_protocol(&mut self, value: Protocol) {
let p: u16 = value.into();
self.buffer.as_mut()[12..14].copy_from_slice(&p.to_be_bytes())
}
pub fn payload_mut(&mut self) -> &mut [u8] {
&mut self.buffer.as_mut()[14..]
}
}
impl<B: AsRef<[u8]>> fmt::Debug for EthernetPacket<B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EthernetPacket")
.field("destination", &self.destination())
.field("source", &self.source())
.field("protocol", &self.protocol())
.field("payload", &self.payload())
.finish()
}
}
+141
View File
@@ -0,0 +1,141 @@
/// 以太网帧协议
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub enum Protocol {
///
Ipv4,
///
Arp,
///
WakeOnLan,
///
Trill,
///
DecNet,
///
Rarp,
///
AppleTalk,
///
Aarp,
///
Ipx,
///
Qnx,
///
Ipv6,
///
FlowControl,
///
CobraNet,
///
Mpls,
///
MplsMulticast,
///
PppoeDiscovery,
///
PppoeSession,
///
Vlan,
///
PBridge,
///
Lldp,
///
Ptp,
///
Cfm,
///
QinQ,
///
Unknown(u16),
}
impl From<u16> for Protocol {
fn from(value: u16) -> Protocol {
use self::Protocol::*;
match value {
0x0800 => Ipv4,
0x0806 => Arp,
0x0842 => WakeOnLan,
0x22f3 => Trill,
0x6003 => DecNet,
0x8035 => Rarp,
0x809b => AppleTalk,
0x80f3 => Aarp,
0x8137 => Ipx,
0x8204 => Qnx,
0x86dd => Ipv6,
0x8808 => FlowControl,
0x8819 => CobraNet,
0x8847 => Mpls,
0x8848 => MplsMulticast,
0x8863 => PppoeDiscovery,
0x8864 => PppoeSession,
0x8100 => Vlan,
0x88a8 => PBridge,
0x88cc => Lldp,
0x88f7 => Ptp,
0x8902 => Cfm,
0x9100 => QinQ,
n => Unknown(n),
}
}
}
impl Into<u16> for Protocol {
fn into(self) -> u16 {
use self::Protocol::*;
match self {
Ipv4 => 0x0800,
Arp => 0x0806,
WakeOnLan => 0x0842,
Trill => 0x22f3,
DecNet => 0x6003,
Rarp => 0x8035,
AppleTalk => 0x809b,
Aarp => 0x80f3,
Ipx => 0x8137,
Qnx => 0x8204,
Ipv6 => 0x86dd,
FlowControl => 0x8808,
CobraNet => 0x8819,
Mpls => 0x8847,
MplsMulticast => 0x8848,
PppoeDiscovery => 0x8863,
PppoeSession => 0x8864,
Vlan => 0x8100,
PBridge => 0x88a8,
Lldp => 0x88cc,
Ptp => 0x88f7,
Cfm => 0x8902,
QinQ => 0x9100,
Unknown(n) => n,
}
}
}
@@ -1,9 +1,9 @@
use std::fmt;
use std::{fmt, io};
use byteorder::{BigEndian, ReadBytesExt};
use crate::cal_checksum;
use crate::error::*;
use crate::icmp::{Code, Kind};
use crate::ip::ipv4::packet::IpV4Packet;
/// icmp 协议
/* https://www.rfc-editor.org/rfc/rfc792
0 1 2 3
@@ -17,20 +17,18 @@ use crate::error::*;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
use crate::icmp::{Code, Kind};
use crate::ip::ipv4::packet::IpV4Packet;
pub struct IcmpPacket<B> {
buffer: B,
pub buffer: B,
}
impl<B: AsRef<[u8]>> IcmpPacket<B> {
pub fn unchecked(buffer: B) -> Self {
Self { buffer }
}
pub fn new(buffer: B) -> Result<Self> {
pub fn new(buffer: B) -> io::Result<Self> {
if buffer.as_ref().len() < 8 {
Err(Error::SmallBuffer)?
Err(io::Error::from(io::ErrorKind::InvalidData))?;
}
let packet = Self::unchecked(buffer);
Ok(packet)
@@ -56,9 +54,7 @@ impl<B: AsRef<[u8]>> IcmpPacket<B> {
Code::from(self.kind(), self.buffer.as_ref()[1])
}
pub fn checksum(&self) -> u16 {
(&self.buffer.as_ref()[2..])
.read_u16::<BigEndian>()
.unwrap()
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
}
pub fn is_valid(&self) -> bool {
self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0
@@ -71,12 +67,8 @@ impl<B: AsRef<[u8]>> IcmpPacket<B> {
| Kind::TimestampReply
| Kind::InformationRequest
| Kind::InformationReply => {
let ide = (&self.buffer.as_ref()[4..])
.read_u16::<BigEndian>()
.unwrap();
let seq = (&self.buffer.as_ref()[6..])
.read_u16::<BigEndian>()
.unwrap();
let ide =u16::from_be_bytes(self.buffer.as_ref()[4..6].try_into().unwrap());
let seq = u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap());
HeaderOther::Identifier(ide, seq)
}
Kind::DestinationUnreachable | Kind::TimeExceeded | Kind::SourceQuench => {
@@ -110,6 +102,7 @@ impl<B: AsRef<[u8]>> IcmpPacket<B> {
},
Kind::TimestampRequest | Kind::TimestampReply => {
let mut buffer = Cursor::new(self.payload());
Description::Timestamp(
buffer.read_u32::<BigEndian>().unwrap(),
buffer.read_u32::<BigEndian>().unwrap(),
@@ -128,11 +121,11 @@ impl<B: AsRef<[u8]>> fmt::Debug for IcmpPacket<B> {
} else {
"icmp::Packet!"
})
.field("kind", &self.kind())
.field("code", &self.code())
.field("checksum", &self.checksum())
.field("payload", &self.payload())
.finish()
.field("kind", &self.kind())
.field("code", &self.code())
.field("checksum", &self.checksum())
.field("payload", &self.payload())
.finish()
}
}
+117
View File
@@ -0,0 +1,117 @@
use std::{fmt, io};
use std::net::Ipv4Addr;
use crate::cal_checksum;
/// igmp v1
/* https://datatracker.ietf.org/doc/html/rfc1112
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| Type | Unused | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Group Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
/// v1版本的报文
pub struct IgmpV1Packet<B> {
pub buffer: B,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum IgmpV1Type {
/// 0x11 所有组224.0.0.1或者特定组
Query,
/// 0x12
ReportV1,
Unknown(u8),
}
impl From<u8> for IgmpV1Type {
fn from(value: u8) -> IgmpV1Type {
use self::IgmpV1Type::*;
match value {
0x11 => Query,
0x12 => ReportV1,
v => Unknown(v),
}
}
}
impl Into<u8> for IgmpV1Type {
fn into(self) -> u8 {
match self {
IgmpV1Type::Query => 0x11,
IgmpV1Type::ReportV1 => 0x12,
IgmpV1Type::Unknown(v) => v
}
}
}
impl<B: AsRef<[u8]>> IgmpV1Packet<B> {
pub fn unchecked(buffer: B) -> Self {
Self { buffer }
}
pub fn new(buffer: B) -> io::Result<Self> {
if buffer.as_ref().len() != 8 {
Err(io::Error::from(io::ErrorKind::InvalidData))
} else {
let packet = Self::unchecked(buffer);
Ok(packet)
}
}
}
impl<B: AsRef<[u8]>> IgmpV1Packet<B> {
pub fn version(&self) -> u8 {
self.buffer.as_ref()[0] >> 4
}
pub fn igmp_type(&self) -> IgmpV1Type {
IgmpV1Type::from(self.buffer.as_ref()[0] & 0x0F)
}
pub fn unused(&self) -> u8 {
self.buffer.as_ref()[1]
}
pub fn checksum(&self) -> u16 {
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
}
pub fn is_valid(&self) -> bool {
self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0
}
pub fn group_address(&self) -> Ipv4Addr {
let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap();
Ipv4Addr::from(tmp)
}
}
impl<B: AsRef<[u8]> + AsMut<[u8]>> IgmpV1Packet<B> {
pub fn set_version(&mut self, version: u8) {
self.buffer.as_mut()[0] = (version << 4) | 0x0F & self.buffer.as_mut()[0]
}
pub fn set_type(&mut self, igmp_type: IgmpV1Type) {
let t: u8 = igmp_type.into();
self.buffer.as_mut()[0] = self.buffer.as_mut()[0] & 0xF0 | t
}
pub fn set_checksum(&mut self, checksum: u16) {
self.buffer.as_mut()[2..4].copy_from_slice(&checksum.to_be_bytes());
}
pub fn update_checksum(&mut self) {
self.set_checksum(0);
self.set_checksum(cal_checksum(self.buffer.as_ref()));
}
pub fn set_group_address(&mut self, group_address: Ipv4Addr) {
self.buffer.as_mut()[4..8].copy_from_slice(&group_address.octets());
}
}
impl<B: AsRef<[u8]>> fmt::Debug for IgmpV1Packet<B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("igmp::V1")
.field("version", &self.version())
.field("type", &self.igmp_type())
.field("checksum", &self.checksum())
.field("is_valid", &self.is_valid())
.field("group_address", &self.group_address())
.finish()
}
}
+118
View File
@@ -0,0 +1,118 @@
use std::{fmt, io};
use std::net::Ipv4Addr;
use crate::cal_checksum;
/// igmp v2
/* https://www.rfc-editor.org/rfc/rfc2236.html
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Max Resp Time | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Group Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
/// v2版本的报文
pub struct IgmpV2Packet<B> {
pub buffer: B,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum IgmpV2Type {
/// 0x11 所有组224.0.0.1或者特定组
Query,
/// 0x16
ReportV2,
LeaveV2,
Unknown(u8),
}
impl From<u8> for IgmpV2Type {
fn from(value: u8) -> IgmpV2Type {
use self::IgmpV2Type::*;
match value {
0x11 => Query,
0x16 => ReportV2,
0x17 => LeaveV2,
v => Unknown(v),
}
}
}
impl Into<u8> for IgmpV2Type {
fn into(self) -> u8 {
match self {
IgmpV2Type::Query => 0x11,
IgmpV2Type::ReportV2 => 0x16,
IgmpV2Type::LeaveV2 => 0x17,
IgmpV2Type::Unknown(v) => v
}
}
}
impl<B: AsRef<[u8]>> IgmpV2Packet<B> {
pub fn unchecked(buffer: B) -> Self {
Self { buffer }
}
pub fn new(buffer: B) -> io::Result<Self> {
if buffer.as_ref().len() != 8 {
Err(io::Error::from(io::ErrorKind::InvalidData))
} else {
let packet = Self::unchecked(buffer);
Ok(packet)
}
}
}
impl<B: AsRef<[u8]>> IgmpV2Packet<B> {
pub fn igmp_type(&self) -> IgmpV2Type {
IgmpV2Type::from(self.buffer.as_ref()[0])
}
pub fn max_resp_time(&self) -> u8 {
self.buffer.as_ref()[1]
}
pub fn checksum(&self) -> u16 {
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
}
pub fn is_valid(&self) -> bool {
self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0
}
pub fn group_address(&self) -> Ipv4Addr {
let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap();
Ipv4Addr::from(tmp)
}
}
impl<B: AsRef<[u8]> + AsMut<[u8]>> IgmpV2Packet<B> {
pub fn set_type(&mut self, igmp_type: IgmpV2Type) {
self.buffer.as_mut()[0] = igmp_type.into()
}
pub fn set_max_resp_time(&mut self, resp: u8) {
self.buffer.as_mut()[1] = resp
}
pub fn set_checksum(&mut self, checksum: u16) {
self.buffer.as_mut()[2..4].copy_from_slice(&checksum.to_be_bytes());
}
pub fn update_checksum(&mut self) {
self.set_checksum(0);
self.set_checksum(cal_checksum(self.buffer.as_ref()));
}
pub fn set_group_address(&mut self, group_address: Ipv4Addr) {
self.buffer.as_mut()[4..8].copy_from_slice(&group_address.octets());
}
}
impl<B: AsRef<[u8]>> fmt::Debug for IgmpV2Packet<B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("igmp::V2")
.field("type", &self.igmp_type())
.field("max_resp_time", &self.max_resp_time())
.field("checksum", &self.checksum())
.field("is_valid", &self.is_valid())
.field("group_address", &self.group_address())
.finish()
}
}
+491
View File
@@ -0,0 +1,491 @@
use std::{fmt, io};
use std::net::Ipv4Addr;
use crate::cal_checksum;
/// igmp v3
/* https://www.rfc-editor.org/rfc/rfc3376
Query:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type = 0x11 | Max Resp Code | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Group Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Resv |S| QRV | QQIC | Number of Sources (N) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address [1] |
+- -+
| Source Address [2] |
+- . -+
. . .
. . .
+- -+
| Source Address [N] |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-----------------------------------------------------------------------------
Report:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type = 0x22 | Reserved | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Reserved | Number of Group Records (M) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
. .
. Group Record [1] .
. .
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
. .
. Group Record [2] .
. .
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| . |
. . .
| . |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
. .
. Group Record [M] .
. .
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Group Record:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Record Type | Aux Data Len | Number of Sources (N) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Multicast Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address [1] |
+- -+
| Source Address [2] |
+- -+
. . .
. . .
. . .
+- -+
| Source Address [N] |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
. .
. Auxiliary Data .
. .
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Record Type:
1 MODE_IS_INCLUDE 表示主机希望加入指定组播组并指定了一个或多个源地址
2 MODE_IS_EXCLUDE 表示主机希望加入指定组播组但排除了一个或多个源地址
3 CHANGE_TO_INCLUDE_MODE 表示主机正在将组播组的过滤模式从排除切换为包括,指定了一个或多个源地址
4 CHANGE_TO_EXCLUDE_MODE 表示主机正在将组播组的过滤模式从包括切换为排除,指定了一个或多个源地址
5 ALLOW_NEW_SOURCES 表示主机希望在已有的源地址列表中添加新的源地址,指定了一个或多个源地址
6 BLOCK_OLD_SOURCES 表示主机希望在已有的源地址列表中删除旧的源地址,指定了一个或多个源地址
*/
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum IgmpV3Type {
/// 0x11 所有组224.0.0.1或者特定组
Query,
/// 0x22
ReportV3,
Unknown(u8),
}
impl From<u8> for IgmpV3Type {
fn from(value: u8) -> IgmpV3Type {
use self::IgmpV3Type::*;
match value {
0x11 => Query,
0x22 => ReportV3,
v => Unknown(v),
}
}
}
impl Into<u8> for IgmpV3Type {
fn into(self) -> u8 {
match self {
IgmpV3Type::Query => 0x11,
IgmpV3Type::ReportV3 => 0x22,
IgmpV3Type::Unknown(v) => v
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum IgmpV3RecordType {
//1 MODE_IS_INCLUDE 表示主机希望加入指定组播组并指定了一个或多个源地址
ModeIsInclude,
//2 MODE_IS_EXCLUDE 表示主机希望加入指定组播组但排除了一个或多个源地址
ModeIsExclude,
//3 CHANGE_TO_INCLUDE_MODE 表示主机正在将组播组的过滤模式从排除切换为包括,指定了一个或多个源地址
ChangeToIncludeMode,
//4 CHANGE_TO_EXCLUDE_MODE 表示主机正在将组播组的过滤模式从包括切换为排除,指定了一个或多个源地址
ChangeToExcludeMode,
//5 ALLOW_NEW_SOURCES 表示主机希望在已有的源地址列表中添加新的源地址,指定了一个或多个源地址
AllowNewSources,
//6 BLOCK_OLD_SOURCES 表示主机希望在已有的源地址列表中删除旧的源地址,指定了一个或多个源地址
BlockOldSources,
Unknown(u8),
}
impl From<u8> for IgmpV3RecordType {
fn from(value: u8) -> IgmpV3RecordType {
use self::IgmpV3RecordType::*;
match value {
1 => ModeIsInclude,
2 => ModeIsExclude,
3 => ChangeToIncludeMode,
4 => ChangeToExcludeMode,
5 => AllowNewSources,
6 => BlockOldSources,
v => Unknown(v),
}
}
}
impl Into<u8> for IgmpV3RecordType {
fn into(self) -> u8 {
use self::IgmpV3RecordType::*;
match self {
ModeIsInclude => 1,
ModeIsExclude => 2,
ChangeToIncludeMode => 3,
ChangeToExcludeMode => 4,
AllowNewSources => 5,
BlockOldSources => 6,
Unknown(v) => v,
}
}
}
/// v3版本的query报文
pub struct IgmpV3QueryPacket<B> {
pub buffer: B,
}
impl<B: AsRef<[u8]>> IgmpV3QueryPacket<B> {
pub fn unchecked(buffer: B) -> Self {
Self { buffer }
}
pub fn new(buffer: B) -> io::Result<Self> {
if buffer.as_ref().len() < 12 {
Err(io::Error::from(io::ErrorKind::InvalidData))
} else {
let packet = Self::unchecked(buffer);
Ok(packet)
}
}
}
impl<B: AsRef<[u8]> + AsMut<[u8]>> IgmpV3QueryPacket<B> {
pub fn set_igmp_type(&mut self) {
self.buffer.as_mut()[0] = IgmpV3Type::Query.into();
}
pub fn set_max_resp_code(&mut self, code: u8) {
self.buffer.as_mut()[1] = code;
}
pub fn set_group_address(&mut self, addr: Ipv4Addr) {
self.buffer.as_mut()[4..8].copy_from_slice(&addr.octets())
}
pub fn set_checksum(&mut self, checksum: u16) {
self.buffer.as_mut()[2..4].copy_from_slice(&checksum.to_be_bytes())
}
pub fn set_qrv(&mut self, qrv: u8) {
self.buffer.as_mut()[8] = (self.buffer.as_ref()[8]&(!0x07)) | (qrv & 0x07)
}
pub fn set_qqic(&mut self, qqic: u8) {
self.buffer.as_mut()[9] = qqic
}
pub fn update_checksum(&mut self) {
self.set_checksum(0);
let checksum = cal_checksum(self.buffer.as_ref());
self.set_checksum(checksum);
}
}
impl<B: AsRef<[u8]>> IgmpV3QueryPacket<B> {
pub fn igmp_type(&self) -> IgmpV3Type {
IgmpV3Type::from(self.buffer.as_ref()[0])
}
pub fn max_resp_code(&self) -> u8 {
self.buffer.as_ref()[1]
}
pub fn checksum(&self) -> u16 {
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
}
pub fn is_valid(&self) -> bool {
self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0
}
pub fn group_address(&self) -> Ipv4Addr {
let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap();
Ipv4Addr::from(tmp)
}
/// 保留字段,设置为0
pub fn resv(&self) -> u8 {
self.buffer.as_ref()[8] >> 4
}
/// 标志位
/// 该比特位为1时,所有收到此查询报文的其他路由器不启动定时器刷新过程,但是此查询报文并不抑制查询者选举过程和路由器的主机侧处理过程;默认未置位。
pub fn s(&self) -> u8 {
(self.buffer.as_ref()[8] & 0x0F) >> 3
}
/// 查询者向网络通告的健壮系数
/// 此参数可使查询者使用自己的健壮系统同步其他组播路由器的健壮系数;
/// 其他路由器接收到查询报文时,如果发现该字段非0,则将自己的健壮系数调整为该字段的值;如果发现该字段为0,则不做处理。默认健壮系数值为2。
pub fn qrv(&self) -> u8 {
self.buffer.as_ref()[8] & 0x07
}
/// IGMP查询者的查询间隔
/// 非查询者收到查询报文时,如果发现该字段非0,则将自己的查询间隔参数调整为该字段的值:如果发现该字段为0,则不做处理。默认值为60。
pub fn qqic(&self) -> u8 {
self.buffer.as_ref()[9]
}
/// 报文中包含的组播源的数量
/// 对于普遍组查询报文和特定组查询报文,该字段为0;对于特定源组查询报文,该字段非0
pub fn source_number(&self) -> u16 {
u16::from_be_bytes(self.buffer.as_ref()[10..12].try_into().unwrap())
}
pub fn source_addresses(&self) -> Option<Vec<Ipv4Addr>> {
let num = self.source_number();
if num == 0 {
None
} else {
let num = num as usize;
let mut list = Vec::with_capacity(num);
let buf = self.buffer.as_ref();
let len = buf.len();
for index in 0..num {
let start = (12 + index * 4) as usize;
let end = start + 4;
if end > len {
return None;
}
let tmp: [u8; 4] = buf[start..end].try_into().unwrap();
list.push(Ipv4Addr::from(tmp));
}
Some(list)
}
}
pub fn source_address(&self, index: u16) -> Option<Ipv4Addr> {
if self.source_number() >= index {
None
} else {
let start = (12 + index * 4) as usize;
let end = start + 4;
let buf = self.buffer.as_ref();
let len = buf.len();
if end > len {
return None;
}
let tmp: [u8; 4] = buf[start..end].try_into().unwrap();
Some(Ipv4Addr::from(tmp))
}
}
}
/// v3版本的query报文
pub struct IgmpV3ReportPacket<B> {
pub buffer: B,
}
impl<B: AsRef<[u8]>> IgmpV3ReportPacket<B> {
pub fn unchecked(buffer: B) -> Self {
Self { buffer }
}
pub fn new(buffer: B) -> io::Result<Self> {
if buffer.as_ref().len() < 8 {
Err(io::Error::from(io::ErrorKind::InvalidData))
} else {
let packet = Self::unchecked(buffer);
Ok(packet)
}
}
}
impl<B: AsRef<[u8]>> IgmpV3ReportPacket<B> {
pub fn igmp_type(&self) -> IgmpV3Type {
IgmpV3Type::from(self.buffer.as_ref()[0])
}
pub fn reserved1(&self) -> u8 {
self.buffer.as_ref()[1]
}
pub fn checksum(&self) -> u16 {
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
}
pub fn is_valid(&self) -> bool {
self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0
}
pub fn reserved2(&self) -> u16 {
u16::from_be_bytes(self.buffer.as_ref()[4..6].try_into().unwrap())
}
pub fn record_number(&self) -> u16 {
u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap())
}
pub fn group_records(&self) -> Option<Vec<IgmpV3RecordPacket<&[u8]>>> {
let num = self.record_number();
if num == 0 {
None
} else {
let num = num as usize;
let mut list = Vec::with_capacity(num);
let mut start = 8 as usize;
let buf = self.buffer.as_ref();
let len = buf.len();
for _ in 0..num {
if start >= len {
return None;
}
if let Ok(record) = IgmpV3RecordPacket::new(&buf[start..]) {
let end = start + 8 + record.aux_data_len() as usize * 4 + record.source_number() as usize * 4;
if end > len {
return None;
}
list.push(IgmpV3RecordPacket::new(&buf[start..end]).unwrap());
start = end;
} else {
return None;
}
}
Some(list)
}
}
}
/// group record
pub struct IgmpV3RecordPacket<B> {
pub buffer: B,
}
impl<B: AsRef<[u8]>> IgmpV3RecordPacket<B> {
pub fn unchecked(buffer: B) -> Self {
Self { buffer }
}
pub fn new(buffer: B) -> io::Result<Self> {
if buffer.as_ref().len() < 8 {
Err(io::Error::from(io::ErrorKind::InvalidData))
} else {
let packet = Self::unchecked(buffer);
Ok(packet)
}
}
}
impl<B: AsRef<[u8]>> IgmpV3RecordPacket<B> {
pub fn record_type(&self) -> IgmpV3RecordType {
IgmpV3RecordType::from(self.buffer.as_ref()[0])
}
/// 辅助数据长度 以4字节为单位
pub fn aux_data_len(&self) -> u8 {
self.buffer.as_ref()[1]
}
/// 源地址数
pub fn source_number(&self) -> u16 {
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
}
///多播地址
pub fn multicast_address(&self) -> Ipv4Addr {
let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap();
Ipv4Addr::from(tmp)
}
pub fn source_addresses(&self) -> Option<Vec<Ipv4Addr>> {
let num = self.source_number();
if num == 0 {
None
} else {
let num = num as usize;
let mut list = Vec::with_capacity(num);
let buf = self.buffer.as_ref();
let len = buf.len();
for index in 0..num {
let start = (8 + index * 4) as usize;
let end = start + 4;
if end > len {
return None;
}
let tmp: [u8; 4] = buf[start..end].try_into().unwrap();
list.push(Ipv4Addr::from(tmp));
}
Some(list)
}
}
pub fn source_address(&self, index: u16) -> Option<Ipv4Addr> {
if self.source_number() >= index {
None
} else {
let start = (8 + index * 4) as usize;
let end = start + 4;
if end > self.buffer.as_ref().len() {
return None;
}
let tmp: [u8; 4] = self.buffer.as_ref()[start..end].try_into().unwrap();
Some(Ipv4Addr::from(tmp))
}
}
/// 在文档中没有定义辅助数据的作用,通常应该是空的
pub fn auxiliary_data(&self) -> &[u8] {
let start = 8 + self.source_number() as usize * 4;
let end = start + self.aux_data_len() as usize * 4;
if end > self.buffer.as_ref().len() {
return &[];
}
&self.buffer.as_ref()[start..end]
}
}
impl<B: AsRef<[u8]>> fmt::Debug for IgmpV3QueryPacket<B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("igmp::V3Query")
.field("type", &self.igmp_type())
.field("max_resp_code", &self.max_resp_code())
.field("checksum", &self.checksum())
.field("is_valid", &self.is_valid())
.field("group_address", &self.group_address())
.field("s", &self.s())
.field("qrv", &self.qrv())
.field("qqic", &self.qqic())
.field("number of sources", &self.source_number())
.field("source_addresses", &self.source_addresses())
.finish()
}
}
impl<B: AsRef<[u8]>> fmt::Debug for IgmpV3ReportPacket<B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("igmp::V3Report")
.field("type", &self.igmp_type())
.field("reserved1", &self.reserved1())
.field("checksum", &self.checksum())
.field("is_valid", &self.is_valid())
.field("reserved2", &self.reserved2())
.field("record_number", &self.record_number())
.field("group_records", &self.group_records())
.finish()
}
}
impl<B: AsRef<[u8]>> fmt::Debug for IgmpV3RecordPacket<B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("igmp::V3Record")
.field("record_type", &self.record_type())
.field("aux_data_len", &self.aux_data_len())
.field("source_number", &self.source_number())
.field("multicast_address", &self.multicast_address())
.field("source_addresses", &self.source_addresses())
.field("auxiliary_data", &self.auxiliary_data())
.finish()
}
}
+46
View File
@@ -0,0 +1,46 @@
pub mod igmp_v1;
pub mod igmp_v2;
pub mod igmp_v3;
#[derive(Debug,Copy, Clone,Eq, PartialEq)]
pub enum IgmpType {
/// 0x11 所有组224.0.0.1或者特定组
Query,
/// 0x12
ReportV1,
/// 0x16
ReportV2,
/// 0x22
ReportV3,
/// 0x17 目标组固定是 224.0.0.2
LeaveV2,
Unknown(u8),
}
impl From<u8> for IgmpType {
fn from(value: u8) -> IgmpType {
use self::IgmpType::*;
match value {
0x11 => Query,
0x12 => ReportV1,
0x16 => ReportV2,
0x22 => ReportV3,
0x17 => LeaveV2,
v => Unknown(v),
}
}
}
impl Into<u8> for IgmpType {
fn into(self) -> u8 {
match self {
IgmpType::Query => 0x11,
IgmpType::ReportV1 => 0x12,
IgmpType::ReportV2 => 0x16,
IgmpType::ReportV3 => 0x22,
IgmpType::LeaveV2 => 0x17,
IgmpType::Unknown(v) => v
}
}
}
@@ -1,10 +1,8 @@
use std::fmt;
use std::{fmt, io};
use std::net::Ipv4Addr;
use byteorder::{BigEndian, ReadBytesExt};
use crate::cal_checksum;
use crate::error::*;
use crate::ip::ipv4::protocol::Protocol;
/// ip协议
@@ -39,16 +37,16 @@ impl<B: AsRef<[u8]>> IpV4Packet<B> {
pub fn unchecked(buffer: B) -> Self {
Self { buffer }
}
pub fn new(buffer: B) -> Result<Self> {
if buffer.as_ref()[0] >> 4 != 4 {
Err(Error::Unimplemented)?
}
pub fn new(buffer: B) -> io::Result<Self> {
if buffer.as_ref().len() < 20 {
Err(Error::SmallBuffer)?
Err(io::Error::new(io::ErrorKind::InvalidData, "len < 20"))?;
}
if buffer.as_ref()[0] >> 4 != 4 {
Err(io::Error::new(io::ErrorKind::InvalidData, "not ipv4"))?;
}
let packet = Self::unchecked(buffer);
if packet.buffer.as_ref().len() < packet.header_len() as usize * 4 {
Err(Error::SmallBuffer)?
Err(io::Error::new(io::ErrorKind::InvalidData, "head_len err"))?;
}
Ok(packet)
}
@@ -60,17 +58,6 @@ impl<B: AsRef<[u8]>> IpV4Packet<B> {
}
pub fn payload(&self) -> &[u8] {
&self.buffer.as_ref()[(self.header_len() as usize * 4)..]
// match self.protocol() {
// Protocol::Udp => {
// let udp = UdpPacket::new(IpAddr::V4(self.source_ip()),
// IpAddr::V4(self.destination_ip()),
// &self.buffer.as_ref()[(self.header_len() as usize * 4)..])?;
// Ok(crate::IpUpperLayer::UDP(udp))
// }
// _ => {
// Ok(crate::IpUpperLayer::Unknown(self.buffer.as_ref()));
// }
// }
}
}
@@ -83,13 +70,18 @@ impl<B: AsRef<[u8]> + AsMut<[u8]>> IpV4Packet<B> {
let len = self.header_len() as usize * 4;
&mut self.buffer.as_mut()[len..]
}
pub fn set_protocol(&mut self, value: Protocol) {
self.header_mut()[9] = value.into();
}
pub fn set_source_ip(&mut self, value: Ipv4Addr) {
self.header_mut()[12..16].copy_from_slice(&value.octets());
}
pub fn set_destination_ip(&mut self, value: Ipv4Addr) {
self.header_mut()[16..20].copy_from_slice(&value.octets());
}
pub fn set_flags(&mut self, flags: u8) {
self.buffer.as_mut()[6] = (self.buffer.as_ref()[6] & 0b11100000) | (flags << 5)
}
fn set_checksum(&mut self, value: u16) {
self.header_mut()[10..12].copy_from_slice(&value.to_be_bytes())
}
@@ -141,16 +133,12 @@ impl<B: AsRef<[u8]>> IpV4Packet<B> {
/// ip报总字节数
pub fn length(&self) -> u16 {
(&self.buffer.as_ref()[2..])
.read_u16::<BigEndian>()
.unwrap()
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
}
/// 标识. ip报文在数据链路层可能会被拆分,同一报文的不同分组标识字段相同
pub fn id(&self) -> u16 {
(&self.buffer.as_ref()[4..])
.read_u16::<BigEndian>()
.unwrap()
u16::from_be_bytes(self.buffer.as_ref()[4..6].try_into().unwrap())
}
/// 标志 3位.
@@ -170,10 +158,7 @@ impl<B: AsRef<[u8]>> IpV4Packet<B> {
/// 以字节为单位,用于指明分段起始点相对于包头起始点的偏移量
/// 由于分段到达时可能错序,所以分段的偏移字段可以使接收者按照正确的顺序重组数据包
pub fn offset(&self) -> u16 {
(&self.buffer.as_ref()[6..])
.read_u16::<BigEndian>()
.unwrap()
& 0x1fff
u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap()) & 0x1fff
}
/// 生存时间.
@@ -189,9 +174,7 @@ impl<B: AsRef<[u8]>> IpV4Packet<B> {
/// 首部校验和
pub fn checksum(&self) -> u16 {
(&self.buffer.as_ref()[10..])
.read_u16::<BigEndian>()
.unwrap()
u16::from_be_bytes(self.buffer.as_ref()[10..12].try_into().unwrap())
}
/// 验证校验和
///
@@ -1,4 +1,4 @@
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
#[derive(Eq, PartialEq,Ord, PartialOrd, Copy, Clone, Debug)]
pub enum Protocol {
///
Hopopt,
@@ -1,7 +1,6 @@
use std::io;
use ipv4::packet::IpV4Packet;
use crate::error::*;
pub mod ipv4;
pub enum IpPacket<B> {
@@ -9,10 +8,10 @@ pub enum IpPacket<B> {
}
impl<B: AsRef<[u8]>> IpPacket<B> {
pub fn new(buffer: B) -> Result<Self> {
pub fn new(buffer: B) -> io::Result<Self> {
match buffer.as_ref()[0] >> 4 {
4 => Ok(IpPacket::V4(IpV4Packet::new(buffer)?)),
_ => Err(Error::InvalidPacket),
_ => Err(io::Error::from(io::ErrorKind::InvalidData)),
}
}
}
@@ -3,12 +3,13 @@ use std::net::Ipv4Addr;
use byteorder::BigEndian;
use byteorder::ReadBytesExt;
pub mod error;
pub mod icmp;
pub mod igmp;
pub mod ip;
pub mod tcp;
pub mod udp;
pub mod ethernet;
pub mod arp;
// pub enum IpUpperLayer<B> {
// UDP(UdpPacket<B>),
// Unknown(B),
@@ -1,9 +1,6 @@
use std::fmt;
use std::net::IpAddr;
use std::{fmt, io};
use std::net::Ipv4Addr;
use byteorder::{BigEndian, ReadBytesExt};
use crate::error::*;
use crate::tcp::Flags;
/// tcp
@@ -48,59 +45,69 @@ use crate::tcp::Flags;
Options+Padding:3240
*/
pub struct TcpPacket<B> {
source_ip: IpAddr,
destination_ip: IpAddr,
source_ip: Ipv4Addr,
destination_ip: Ipv4Addr,
buffer: B,
}
impl<B: AsRef<[u8]>> TcpPacket<B> {
pub fn unchecked(source_ip: IpAddr, destination_ip: IpAddr, buffer: B) -> TcpPacket<B> {
pub fn unchecked(source_ip: Ipv4Addr, destination_ip: Ipv4Addr, buffer: B) -> TcpPacket<B> {
TcpPacket {
source_ip,
destination_ip,
buffer,
}
}
pub fn new(source_ip: IpAddr, destination_ip: IpAddr, buffer: B) -> Result<TcpPacket<B>> {
pub fn new(source_ip: Ipv4Addr, destination_ip: Ipv4Addr, buffer: B) -> io::Result<TcpPacket<B>> {
let packet = TcpPacket::unchecked(source_ip, destination_ip, buffer);
if packet.buffer.as_ref().len() < 20 {
Err(Error::SmallBuffer)?
Err(io::Error::from(io::ErrorKind::InvalidData))?;
}
if packet.buffer.as_ref().len() < packet.data_offset() as usize * 4 {
Err(Error::SmallBuffer)?
Err(io::Error::from(io::ErrorKind::InvalidData))?;
}
Ok(packet)
}
}
impl<B: AsRef<[u8]> + AsMut<[u8]>> TcpPacket<B> {
fn set_checksum(&mut self, value: u16) {
self.buffer.as_mut()[16..18].copy_from_slice(&value.to_be_bytes())
}
pub fn set_source_port(&mut self, value: u16) {
self.buffer.as_mut()[0..2].copy_from_slice(&value.to_be_bytes())
}
pub fn set_destination_port(&mut self, value: u16) {
self.buffer.as_mut()[2..4].copy_from_slice(&value.to_be_bytes())
}
/// 更新校验和
pub fn update_checksum(&mut self) {
//先将校验和置0
self.set_checksum(0);
self.set_checksum(self.cal_checksum())
}
}
impl<B: AsRef<[u8]>> TcpPacket<B> {
/// 源端口
pub fn source_port(&self) -> u16 {
(&self.buffer.as_ref()[0..])
.read_u16::<BigEndian>()
.unwrap()
u16::from_be_bytes(self.buffer.as_ref()[0..2].try_into().unwrap())
}
/// 目标端口
pub fn destination_port(&self) -> u16 {
(&self.buffer.as_ref()[2..])
.read_u16::<BigEndian>()
.unwrap()
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
}
/// 序列号
pub fn sequence(&self) -> u32 {
(&self.buffer.as_ref()[4..])
.read_u32::<BigEndian>()
.unwrap()
u32::from_be_bytes(self.buffer.as_ref()[4..8].try_into().unwrap())
}
/// 确认号
pub fn acknowledgment(&self) -> u32 {
(&self.buffer.as_ref()[8..])
.read_u32::<BigEndian>()
.unwrap()
u32::from_be_bytes(self.buffer.as_ref()[8..12].try_into().unwrap())
}
/// 数据偏移 4字节为单位
pub fn data_offset(&self) -> u8 {
@@ -110,14 +117,10 @@ impl<B: AsRef<[u8]>> TcpPacket<B> {
Flags(self.buffer.as_ref()[13])
}
pub fn window(&self) -> u16 {
(&self.buffer.as_ref()[14..])
.read_u16::<BigEndian>()
.unwrap()
u16::from_be_bytes(self.buffer.as_ref()[14..16].try_into().unwrap())
}
pub fn checksum(&self) -> u16 {
(&self.buffer.as_ref()[16..])
.read_u16::<BigEndian>()
.unwrap()
u16::from_be_bytes(self.buffer.as_ref()[16..18].try_into().unwrap())
}
/// 验证校验和,ipv4中为0表示不使用校验和,ipv6校验和不能为0
/// TCP/IP协议栈不会自己计算校验和,而是简单地将一个空的校验和字段(零或随机填充)交给网卡硬件。
@@ -126,26 +129,16 @@ impl<B: AsRef<[u8]>> TcpPacket<B> {
self.checksum() == 0 || self.cal_checksum() == 0
}
fn cal_checksum(&self) -> u16 {
match self.source_ip {
IpAddr::V4(src) => {
if let IpAddr::V4(dest) = self.destination_ip {
return crate::ipv4_cal_checksum(
self.buffer.as_ref(),
&src,
&dest,
6,
self.buffer.as_ref().len() as u16,
);
}
}
IpAddr::V6(_src) => {}
}
unimplemented!()
crate::ipv4_cal_checksum(
self.buffer.as_ref(),
&self.source_ip,
&self.destination_ip,
6,
self.buffer.as_ref().len() as u16,
)
}
pub fn urgent_pointer(&self) -> u16 {
(&self.buffer.as_ref()[18..])
.read_u16::<BigEndian>()
.unwrap()
u16::from_be_bytes(self.buffer.as_ref()[18..20].try_into().unwrap())
}
pub fn options(&self) -> &[u8] {
&self.buffer.as_ref()[20..(self.data_offset() as usize * 4)]
@@ -1,11 +1,5 @@
use std::fmt;
use std::io::Cursor;
use std::net::IpAddr;
use byteorder::WriteBytesExt;
use byteorder::{BigEndian, ReadBytesExt};
use crate::error::*;
use std::{fmt, io};
use std::net::Ipv4Addr;
/// udp协议
///
@@ -53,22 +47,22 @@ RFC 768 https://www.ietf.org/rfc/rfc768.txt
*/
pub struct UdpPacket<B> {
source_ip: IpAddr,
destination_ip: IpAddr,
source_ip: Ipv4Addr,
destination_ip: Ipv4Addr,
buffer: B,
}
impl<B: AsRef<[u8]>> UdpPacket<B> {
pub fn unchecked(source_ip: IpAddr, destination_ip: IpAddr, buffer: B) -> UdpPacket<B> {
pub fn unchecked(source_ip: Ipv4Addr, destination_ip: Ipv4Addr, buffer: B) -> UdpPacket<B> {
UdpPacket {
source_ip,
destination_ip,
buffer,
}
}
pub fn new(source_ip: IpAddr, destination_ip: IpAddr, buffer: B) -> Result<UdpPacket<B>> {
pub fn new(source_ip: Ipv4Addr, destination_ip: Ipv4Addr, buffer: B) -> io::Result<UdpPacket<B>> {
if buffer.as_ref().len() < 8 {
Err(Error::SmallBuffer)?
Err(io::Error::from(io::ErrorKind::InvalidData))?;
}
let packet = Self::unchecked(source_ip, destination_ip, buffer);
Ok(packet)
@@ -78,30 +72,22 @@ impl<B: AsRef<[u8]>> UdpPacket<B> {
impl<B: AsRef<[u8]>> UdpPacket<B> {
/// 源端口
pub fn source_port(&self) -> u16 {
(&self.buffer.as_ref()[0..])
.read_u16::<BigEndian>()
.unwrap()
u16::from_be_bytes(self.buffer.as_ref()[0..2].try_into().unwrap())
}
/// 目标端口
pub fn destination_port(&self) -> u16 {
(&self.buffer.as_ref()[2..])
.read_u16::<BigEndian>()
.unwrap()
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
}
/// 总字节数
pub fn length(&self) -> u16 {
(&self.buffer.as_ref()[4..])
.read_u16::<BigEndian>()
.unwrap()
u16::from_be_bytes(self.buffer.as_ref()[4..6].try_into().unwrap())
}
/// Checksum of the packet.
pub fn checksum(&self) -> u16 {
(&self.buffer.as_ref()[6..])
.read_u16::<BigEndian>()
.unwrap()
u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap())
}
/// 验证校验和,ipv4中为0表示不使用校验和,ipv6校验和不能为0
pub fn is_valid(&self) -> bool {
@@ -111,55 +97,39 @@ impl<B: AsRef<[u8]>> UdpPacket<B> {
&self.buffer.as_ref()[8..]
}
fn cal_checksum(&self) -> u16 {
match self.source_ip {
IpAddr::V4(src) => {
if let IpAddr::V4(dest) = self.destination_ip {
return crate::ipv4_cal_checksum(
self.buffer.as_ref(),
&src,
&dest,
17,
self.length(),
);
}
}
IpAddr::V6(_src) => {}
}
unimplemented!()
crate::ipv4_cal_checksum(
self.buffer.as_ref(),
&self.source_ip,
&self.destination_ip,
17,
self.length(),
)
}
}
impl<B: AsRef<[u8]> + AsMut<[u8]>> UdpPacket<B> {
fn header_mut(&mut self) -> &mut [u8] {
&mut self.buffer.as_mut()[..8]
}
}
// impl<B: AsRef<[u8]> + AsMut<[u8]>> UdpPacket<B> {
// fn header_mut(&mut self) -> &mut [u8] {
// &mut self.buffer.as_mut()[..8]
// }
// }
impl<B: AsRef<[u8]> + AsMut<[u8]>> UdpPacket<B> {
/// 设置源端口
pub fn set_source_port(&mut self, value: u16) -> &mut Self {
Cursor::new(&mut self.header_mut()[0..])
.write_u16::<BigEndian>(value)
.unwrap();
self
pub fn set_source_port(&mut self, value: u16) {
self.buffer.as_mut()[0..2].copy_from_slice(&value.to_be_bytes())
}
/// 设置目的端口
pub fn set_destination_port(&mut self, value: u16) -> &mut Self {
Cursor::new(&mut self.header_mut()[2..])
.write_u16::<BigEndian>(value)
.unwrap();
self
pub fn set_destination_port(&mut self, value: u16) {
self.buffer.as_mut()[2..4].copy_from_slice(&value.to_be_bytes())
}
fn set_checknum(&mut self, value: u16) {
Cursor::new(&mut self.header_mut()[6..])
.write_u16::<BigEndian>(value)
.unwrap();
fn set_checksum(&mut self, value: u16) {
self.buffer.as_mut()[6..8].copy_from_slice(&value.to_be_bytes())
}
pub fn update_checknum(&mut self) {
pub fn update_checksum(&mut self) {
//先写0
self.set_checknum(0);
self.set_checknum(self.cal_checksum());
self.set_checksum(0);
self.set_checksum(self.cal_checksum());
}
}
@@ -4,6 +4,9 @@ message RegistrationRequest{
string device_id = 2;
string name = 3;
bool is_fast = 4;
string version = 5;
fixed32 virtual_ip = 6;
bool allow_ip_change = 7;
}
message RegistrationResponse{
@@ -14,6 +17,7 @@ message RegistrationResponse{
repeated DeviceInfo device_info_list = 5;
fixed32 public_ip = 6;
uint32 public_port = 7;
bytes public_ipv6 = 8;
}
message DeviceInfo{
string name = 1;
@@ -34,6 +38,7 @@ message PunchInfo{
bool reply = 6;
fixed32 local_ip = 7;
uint32 local_port = 8;
repeated bytes public_ipv6_list = 9;
}
enum PunchNatType{
Symmetric = 0;
@@ -11,7 +11,7 @@
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
//
// 0. You just DO WHAT THE FUCK YOU WANT TO.
#![cfg(unix)]
mod error;
pub use crate::error::*;
@@ -27,15 +27,6 @@ pub mod macos;
#[cfg(target_os = "macos")]
pub use self::macos::{create, Configuration, Device, Queue};
#[cfg(target_os = "ios")]
pub mod ios;
#[cfg(target_os = "ios")]
pub use self::ios::{create, Configuration, Device, Queue};
#[cfg(target_os = "android")]
pub mod android;
#[cfg(target_os = "android")]
pub use self::android::{create, Configuration, Device, Queue};
#[cfg(test)]
mod test {
@@ -112,6 +112,11 @@ impl AsRawFd for Reader {
self.0.as_raw_fd()
}
}
impl AsRawFd for Writer {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
//
// impl AsRawFd for Writer {
// fn as_raw_fd(&self) -> RawFd {
+540
View File
@@ -0,0 +1,540 @@
use std::io;
use std::net::{Ipv4Addr, SocketAddr};
use std::sync::Arc;
use std::time::{Duration, Instant};
use crossbeam_skiplist::SkipMap;
use crossbeam_utils::atomic::AtomicCell;
use parking_lot::Mutex;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpStream, UdpSocket};
use tokio::net::tcp::OwnedReadHalf;
use tokio::sync::watch::{channel, Receiver, Sender};
use crate::channel::{Route, RouteKey, Status};
use crate::channel::punch::NatType;
use crate::core::status::VntWorker;
use crate::handle::CurrentDeviceInfo;
use crate::handle::recv_handler::ChannelDataHandler;
pub struct ContextInner {
pub(crate) lock: Mutex<()>,
//udp用于打洞、服务端通信(可选)
pub(crate) main_channel: Arc<UdpSocket>,
//在udp的基础上,可以选择使用tcp和服务端通信
pub(crate) main_tcp_channel: Option<tokio::sync::mpsc::Sender<Vec<u8>>>,
pub(crate) route_table: SkipMap<Ipv4Addr, Vec<Route>>,
pub(crate) route_table_time: SkipMap<(RouteKey, Ipv4Addr), AtomicCell<Instant>>,
pub(crate) status_receiver: Receiver<Status>,
pub(crate) status_sender: Sender<Status>,
pub(crate) udp_map: SkipMap<usize, Arc<UdpSocket>>,
pub(crate) channel_num: usize,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
}
#[derive(Clone)]
pub struct Context {
pub(crate) inner: Arc<ContextInner>,
}
impl Context {
pub fn new(main_channel: Arc<UdpSocket>, main_tcp_channel: Option<tokio::sync::mpsc::Sender<Vec<u8>>>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>, _channel_num: usize) -> Self {
//当前版本只支持一个通道
let channel_num = 1;
let (status_sender, status_receiver) = channel(Status::Cone);
let inner = Arc::new(ContextInner {
lock: Mutex::new(()),
main_channel,
main_tcp_channel,
route_table: SkipMap::new(),
route_table_time: SkipMap::new(),
status_receiver,
status_sender,
udp_map: SkipMap::new(),
channel_num,
current_device,
});
Self {
inner
}
}
}
impl Context {
pub fn is_close(&self) -> bool {
*self.inner.status_receiver.borrow() == Status::Close
}
pub fn is_cone(&self) -> bool {
*self.inner.status_receiver.borrow() == Status::Cone
}
pub fn close(&self) {
let _ = self.inner.status_sender.send(Status::Close);
}
pub fn switch(&self, nat_type: NatType) {
match nat_type {
NatType::Symmetric => {
self.switch_to_symmetric();
}
NatType::Cone => {
self.switch_to_cone();
}
}
}
pub fn switch_to_cone(&self) {
let _ = self.inner.status_sender.send(Status::Cone);
}
pub fn switch_to_symmetric(&self) {
let _ = self.inner.status_sender.send(Status::Symmetric);
}
pub fn main_local_port(&self) -> io::Result<u16> {
self.inner.main_channel.local_addr().map(|k| k.port())
}
pub async fn send_main_udp(&self, buf: &[u8], addr: SocketAddr) -> io::Result<usize> {
self.inner.main_channel.send_to(buf, addr).await
}
pub async fn send_main(&self, buf: &[u8], addr: SocketAddr) -> io::Result<usize> {
if let Some(sender) = &self.inner.main_tcp_channel {
let mut vec = vec![0; 4 + buf.len()];
vec[4..].copy_from_slice(buf);
if sender.send(vec).await.is_ok() {
Ok(buf.len())
} else {
Err(io::Error::new(io::ErrorKind::Other, "send_main err"))
}
} else {
self.inner.main_channel.send_to(buf, addr).await
}
}
pub fn try_send_main(&self, buf: &[u8], addr: SocketAddr) -> io::Result<usize> {
if let Some(sender) = &self.inner.main_tcp_channel {
let mut vec = vec![0; 4 + buf.len()];
vec[4..].copy_from_slice(buf);
if sender.try_send(vec).is_ok() {
Ok(buf.len())
} else {
Err(io::Error::new(io::ErrorKind::Other, "try_send_main err"))
}
} else {
self.inner.main_channel.try_send_to(buf, addr)
}
}
pub(crate) async fn send_all(&self, buf: &[u8], addr: SocketAddr) -> io::Result<()> {
for udp in self.inner.udp_map.iter() {
udp.value().send_to(buf, addr).await?;
}
Ok(())
}
pub async fn send_by_id(&self, buf: &[u8], id: &Ipv4Addr) -> io::Result<usize> {
if let Some(v) = self.inner.route_table.get(id) {
if v.value().is_empty() {
return Err(io::Error::new(io::ErrorKind::NotFound, "route not found"));
}
let route = v.value()[0];
drop(v);
if !route.is_p2p() {
if let Some(time) = self.inner.route_table_time.get(&(route.route_key(), *id)) {
//借道传输时,长时间不通信的通道不使用
if time.value().load().elapsed() > Duration::from_secs(3) {
return Err(io::Error::new(io::ErrorKind::NotFound, "route time out"));
}
}
}
if let Some(udp) = self.inner.udp_map.get(&route.index) {
return udp.value().send_to(buf, route.addr).await;
}
}
Err(io::Error::new(io::ErrorKind::NotFound, "route not found"))
}
pub fn try_send_by_id(&self, buf: &[u8], id: &Ipv4Addr) -> io::Result<usize> {
if let Some(v) = self.inner.route_table.get(id) {
if v.value().is_empty() {
return Err(io::Error::new(io::ErrorKind::NotFound, "route not found"));
}
let route = v.value()[0];
drop(v);
if let Some(udp) = self.inner.udp_map.get(&route.index) {
return udp.value().try_send_to(buf, route.addr);
}
}
Err(io::Error::new(io::ErrorKind::NotFound, "route not found"))
}
pub async fn send_by_key(&self, buf: &[u8], route_key: &RouteKey) -> io::Result<usize> {
if route_key.index == 0 {
if let Some(sender) = &self.inner.main_tcp_channel {
let mut vec = vec![0; 4 + buf.len()];
vec[4..].copy_from_slice(buf);
return if sender.send(vec).await.is_ok() {
Ok(buf.len())
} else {
Err(io::Error::new(io::ErrorKind::Other, "send_by_key err"))
};
}
}
if let Some(udp) = self.inner.udp_map.get(&route_key.index) {
return udp.value().send_to(buf, route_key.addr).await;
}
Err(io::Error::new(io::ErrorKind::NotFound, "route not found"))
}
pub fn try_send_by_key(&self, buf: &[u8], route_key: &RouteKey) -> io::Result<usize> {
if route_key.index == 0 {
if let Some(sender) = &self.inner.main_tcp_channel {
let mut vec = vec![0; 4 + buf.len()];
vec[4..].copy_from_slice(buf);
return if sender.try_send(vec).is_ok() {
Ok(buf.len())
} else {
Err(io::Error::new(io::ErrorKind::Other, "try_send_by_key err"))
};
}
}
if let Some(udp) = self.inner.udp_map.get(&route_key.index) {
return udp.value().try_send_to(buf, route_key.addr);
}
Err(io::Error::new(io::ErrorKind::NotFound, "route not found"))
}
pub fn add_route_if_absent(&self, id: Ipv4Addr, route: Route) {
self.add_route_(id, route, true)
}
pub fn add_route(&self, id: Ipv4Addr, route: Route) {
self.add_route_(id, route, false)
}
fn add_route_(&self, id: Ipv4Addr, route: Route, only_if_absent: bool) {
let key = route.route_key();
let guard = self.inner.lock.lock();
let mut list = if let Some(entry) = self.inner.route_table.get(&id) {
entry.value().clone()
} else {
Vec::with_capacity(4)
};
let mut exist = false;
for x in list.iter_mut() {
if x.metric < route.metric {
//不能比当前的路径更长
return;
}
if x.route_key() == key {
if only_if_absent {
return;
}
x.metric = route.metric;
x.rt = route.rt;
exist = true;
break;
}
}
if exist {
list.sort_by_key(|k| k.sort_key());
} else {
if route.metric == 1 {
//添加了直连的则排除非直连的
list.retain(|k| k.metric == 1);
}
list.push(route);
list.sort_by_key(|k| k.sort_key());
let max_len = self.inner.channel_num + 1;
if list.len() > max_len {
list.truncate(max_len);
}
}
self.inner.route_table.insert(id, list);
self.inner.route_table_time.insert((key, id), AtomicCell::new(Instant::now()));
drop(guard);
}
pub fn route(&self, id: &Ipv4Addr) -> Option<Vec<Route>> {
if let Some(v) = self.inner.route_table.get(id) {
Some(v.value().clone())
} else {
None
}
}
pub fn route_one(&self, id: &Ipv4Addr) -> Option<Route> {
if let Some(v) = self.inner.route_table.get(id) {
v.value().iter().max_by_key(|k| k.sort_key()).map(|k| *k)
} else {
None
}
}
pub fn route_to_id(&self, route_key: &RouteKey) -> Option<Ipv4Addr> {
for x in self.inner.route_table_time.iter() {
if &x.key().0 == route_key {
return Some(x.key().1);
}
}
None
}
pub fn need_punch(&self, id: &Ipv4Addr) -> bool {
if let Some(v) = self.inner.route_table.get(id) {
if v.value().iter().filter(|k| k.is_p2p()).count() >= self.inner.channel_num {
return false;
}
}
true
}
pub fn route_table(&self) -> Vec<(Ipv4Addr, Vec<Route>)> {
self.inner.route_table.iter().map(|k| (k.key().clone(), k.value().clone())).collect()
}
pub fn route_table_one(&self) -> Vec<(Ipv4Addr, Route)> {
let mut v = Vec::with_capacity(8);
for x in self.inner.route_table.iter() {
if let Some(route) = x.value().first() {
v.push((*x.key(), *route));
}
}
v
}
pub fn direct_route_table_one(&self) -> Vec<(Ipv4Addr, Route)> {
let mut v = Vec::with_capacity(8);
for x in self.inner.route_table.iter() {
if let Some(route) = x.value().first() {
if route.metric == 1 {
v.push((*x.key(), *route));
}
}
}
v
}
pub fn remove_route_all(&self, id: &Ipv4Addr) {
let guard = self.inner.lock.lock();
if let Some(v) = self.inner.route_table.remove(id) {
for x in v.value() {
self.inner.route_table_time.remove(&(x.route_key(), *id));
}
}
drop(guard);
}
pub fn remove_route(&self, id: &Ipv4Addr, route_key: RouteKey) {
let guard = self.inner.lock.lock();
if let Some(v) = self.inner.route_table.get(id) {
let mut routes = v.value().clone();
drop(v);
routes.retain(|x| x.route_key() != route_key);
self.inner.route_table.insert(*id, routes);
self.inner.route_table_time.remove(&(route_key, *id));
}
drop(guard);
}
pub fn update_read_time(&self, id: &Ipv4Addr, route_key: &RouteKey) {
if let Some(time) = self.inner.route_table_time.get(&(*route_key, *id)) {
time.value().store(Instant::now());
}
}
}
pub struct Channel {
context: Context,
handler: ChannelDataHandler,
}
impl Channel {
pub fn new(context: Context,
handler: ChannelDataHandler, ) -> Self {
Self {
context,
handler,
}
}
}
impl Channel {
async fn handle(handler: &mut ChannelDataHandler,
context: &Context,
id: usize,
result: io::Result<(usize, SocketAddr)>,
buf: &mut [u8], start: usize) {
match result {
Ok((len, addr)) => {
handler.handle(buf, start, start + len, RouteKey::new(id, addr), context).await;
}
Err(e) => {
log::error!("{:?}",e)
}
}
}
async fn tcp_handle(mut tcp_r: OwnedReadHalf, context: Context,
mut handler: ChannelDataHandler, head_reserve: usize, ) -> io::Result<()> {
let mut buf = [0; 4096];
let addr = tcp_r.peer_addr()?;
let key = RouteKey::new(0, addr);
loop {
tcp_r.read_exact(&mut buf[head_reserve..head_reserve + 4]).await?;
let len = 4 + (((buf[head_reserve + 2] as u16) << 8) | buf[head_reserve + 3] as u16) as usize;
tcp_r.read_exact(&mut buf[head_reserve + 4..head_reserve + len]).await?;
handler.handle(&mut buf[4..], head_reserve, head_reserve + len - 4, key, &context).await;
}
}
async fn start_tcp(mut worker: VntWorker, tcp_stream: TcpStream, mut receiver: tokio::sync::mpsc::Receiver<Vec<u8>>, context: Context, handler: ChannelDataHandler, head_reserve: usize) {
let (tcp_r, mut tcp_w) = tcp_stream.into_split();
{
let context = context.clone();
let handler = handler.clone();
tokio::spawn(async move {
if let Err(e) = Self::tcp_handle(tcp_r, context, handler, head_reserve).await {
log::info!("tcp链接断开:{:?}",e);
}
});
}
loop {
tokio::select! {
_=worker.stop_wait()=>{
break;
}
rs=receiver.recv()=>{
if let Some(mut data) = rs{
if data.len()<4{
continue
}
let len = data.len() - 4;
data[2] = (len >> 8) as u8;
data[3] = (len & 0xFF) as u8;
if let Err(e) = tcp_w.write_all(&data).await {
if context.is_close() {
break;
}
log::info!("发送失败,需要重连:{:?}",e);
let _ = tcp_w.shutdown().await;
match TcpStream::connect(context.inner.current_device.load().connect_server).await {
Ok(tcp_stream) => {
let (r, w) = tcp_stream.into_split();
tcp_w = w;
let context = context.clone();
let handler = handler.clone();
tokio::spawn(async move {
if let Err(e) = Self::tcp_handle(r, context, handler, head_reserve).await {
log::info!("tcp 链接断开:{:?}",e);
}
});
}
Err(e) => {
log::info!("重连失败:{:?}",e);
}
};
}
}else{
break;
}
}
}
}
worker.stop_all();
}
pub async fn start(self,
mut worker: VntWorker,
tcp: Option<(TcpStream, tokio::sync::mpsc::Receiver<Vec<u8>>)>,
head_reserve: usize,//头部预留字节
symmetric_channel_num: usize,//对称网络,则再加一组监听,提升打洞成功率
relay: bool,
) {
let context = self.context;
let main_channel = context.inner.main_channel.clone();
let handler = self.handler.clone();
if let Some((tcp_stream, receiver)) = tcp {
tokio::spawn(Self::start_tcp(worker.worker("main_channel_tcp"), tcp_stream, receiver, context.clone(), handler.clone(), head_reserve));
}
tokio::spawn(Self::start_(worker.worker("main_channel_1"), context.clone(), handler.clone(), main_channel.clone(), head_reserve, true));
// tokio::spawn(Self::start_(worker.worker("main_channel_2"), context.clone(), handler, main_channel, head_reserve, true));
if relay {
worker.stop_wait().await;
return;
}
let mut cur_status = Status::Cone;
let mut status_receiver = context.inner.status_receiver.clone();
loop {
tokio::select! {
_=worker.stop_wait()=>{
break;
}
rs=status_receiver.changed()=>{
match rs {
Ok(_) => {
let s = status_receiver.borrow().clone();
match s {
Status::Cone => {
cur_status = Status::Cone;
}
Status::Symmetric => {
if cur_status == Status::Symmetric {
continue;
}
cur_status = Status::Symmetric;
for _ in 0..symmetric_channel_num {
match UdpSocket::bind("0.0.0.0:0").await {
Ok(udp) => {
let udp = Arc::new(udp);
let context = context.clone();
let handler = self.handler.clone();
tokio::spawn(Self::start_(worker.worker("symmetric_channel"),context, handler, udp, head_reserve, false));
}
Err(e) => {
log::error!("{}",e);
}
}
}
}
Status::Close => {
break;
}
}
}
Err(_) => {
break;
}
}
}
}
}
worker.stop_all();
}
async fn start_(mut worker: VntWorker, context: Context,
mut handler: ChannelDataHandler,
udp: Arc<UdpSocket>,
head_reserve: usize,
is_core: bool) {
let mut status_receiver = context.inner.status_receiver.clone();
#[cfg(target_os = "windows")]
use std::os::windows::io::AsRawSocket;
#[cfg(target_os = "windows")]
let id = 1 + udp.as_raw_socket() as usize;
#[cfg(any(unix))]
use std::os::fd::AsRawFd;
#[cfg(any(unix))]
let id = 1 + udp.as_raw_fd() as usize;
context.inner.udp_map.insert(id, udp.clone());
let mut buf = [0; 4096];
loop {
tokio::select! {
rs=udp.recv_from(&mut buf[head_reserve..])=>{
Self::handle(&mut handler,&context,id,rs,&mut buf,head_reserve).await;
}
changed=status_receiver.changed()=>{
match changed {
Ok(_) => {
match *status_receiver.borrow() {
Status::Cone => {
if !is_core{
break;
}
}
Status::Close=>{
break;
}
Status::Symmetric => {}
}
}
Err(_) => {
break;
}
}
}
_=worker.stop_wait()=>{
break;
}
}
}
context.inner.udp_map.remove(&id);
if is_core {
worker.stop_all();
}
}
}
+48
View File
@@ -0,0 +1,48 @@
use std::io;
use std::io::{Error, ErrorKind};
use std::net::Ipv4Addr;
use std::time::Duration;
use crate::channel::channel::Context;
use crate::channel::RouteKey;
pub struct Idle {
read_idle: Duration,
context: Context,
}
impl Idle {
pub fn new(read_idle: Duration,
context: Context, ) -> Self {
Self {
read_idle,
context,
}
}
}
impl Idle {
/// 获取空闲路由
pub async fn next_idle(&self) -> io::Result<(Ipv4Addr, RouteKey)> {
loop {
let mut max = Duration::from_secs(0);
for entry in self.context.inner.route_table_time.iter() {
let last_read = entry.value().load().elapsed();
if last_read >= self.read_idle {
return Ok((entry.key().1.clone(), entry.key().0.clone()));
} else {
if max < last_read {
max = last_read;
}
}
}
if self.read_idle > max {
let sleep_time = self.read_idle - max;
tokio::time::sleep(sleep_time).await;
}
if self.context.is_close() {
return Err(Error::new(ErrorKind::Other, "closed"));
}
}
}
}
+78
View File
@@ -0,0 +1,78 @@
use std::net::SocketAddr;
pub mod channel;
pub mod punch;
pub mod idle;
pub mod sender;
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum Status {
Cone,
Symmetric,
Close,
}
#[derive(Copy, Clone, Debug)]
pub struct Route {
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,
}
impl Route {
pub fn new(index: usize,
addr: SocketAddr, metric: u8, rt: i64, ) -> Self {
Self {
index,
addr,
metric,
rt,
}
}
pub fn from(route_key: RouteKey, metric: u8, rt: i64) -> Self {
Self {
index: route_key.index,
addr: route_key.addr,
metric,
rt,
}
}
pub fn route_key(&self) -> RouteKey {
RouteKey {
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 {
index: usize,
pub addr: SocketAddr,
}
impl RouteKey {
pub(crate) fn new(index: usize,
addr: SocketAddr, ) -> Self {
Self {
index,
addr,
}
}
}
+155
View File
@@ -0,0 +1,155 @@
use std::collections::HashMap;
use std::io;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::time::Duration;
use rand::prelude::SliceRandom;
use crate::channel::channel::Context;
#[derive(Clone, Debug)]
pub struct NatInfo {
pub public_ips: Vec<Ipv4Addr>,
pub public_port: u16,
pub public_port_range: u16,
pub local_ip: Ipv4Addr,
pub local_port: u16,
pub nat_type: NatType,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum NatType {
Symmetric,
Cone,
}
impl NatInfo {
pub fn new(mut public_ips: Vec<Ipv4Addr>,
public_port: u16,
public_port_range: u16,
local_ip: Ipv4Addr,
local_port: u16,
nat_type: NatType, ) -> Self {
public_ips.retain(|ip| {
!ip.is_loopback() && !ip.is_private()
});
Self {
public_ips,
public_port,
public_port_range,
local_ip,
local_port,
nat_type,
}
}
}
#[derive(Clone)]
pub struct Punch {
context: Context,
port_vec: Vec<u16>,
port_index: HashMap<Ipv4Addr, usize>,
}
impl Punch {
pub fn new(context: Context) -> Self {
let mut port_vec: Vec<u16> = (1..65535).collect();
port_vec.push(65535);
let mut rng = rand::thread_rng();
port_vec.shuffle(&mut rng);
Punch {
context,
port_vec,
port_index: HashMap::new(),
}
}
}
impl Punch {
pub async fn punch(&mut self, buf: &[u8], id: Ipv4Addr, nat_info: NatInfo) -> io::Result<()> {
if !self.context.need_punch(&id) {
return Ok(());
}
if !nat_info.local_ip.is_unspecified() || nat_info.local_port != 0 {
let _ = self.context.send_main_udp(buf, SocketAddr::V4(SocketAddrV4::new(nat_info.local_ip, nat_info.local_port))).await;
}
match nat_info.nat_type {
NatType::Symmetric => {
// 假设对方绑定n个端口,通过NAT对外映射出n个 公网ip:公网端口,自己随机尝试k次的情况下
// 猜中的概率 p = 1-((65535-n)/65535)*((65535-n-1)/(65535-1))*...*((65535-n-k+1)/(65535-k+1))
// n取76,k取600,猜中的概率就超过50%了
// 前提 自己是锥形网络,否则猜中了也通信不了
//预测范围内最多发送max_k1个包
let max_k1 = 60;
//全局最多发送max_k2个包
let max_k2 = 800;
if nat_info.public_port_range < max_k1 * 3 {
//端口变化不大时,在预测的范围内随机发送
let min_port = if nat_info.public_port > nat_info.public_port_range {
nat_info.public_port - nat_info.public_port_range
} else {
1
};
let (max_port, overflow) = nat_info.public_port.overflowing_add(nat_info.public_port_range);
let max_port = if overflow {
65535
} else {
max_port
};
let k = if max_port - min_port + 1 > max_k1 {
max_k1 as usize
} else {
(max_port - min_port + 1) as usize
};
let mut nums: Vec<u16> = (min_port..max_port).collect();
nums.push(max_port);
{
let mut rng = rand::thread_rng();
nums.shuffle(&mut rng);
}
self.punch_symmetric(&nums[..k], buf, &nat_info.public_ips, max_k1 as usize).await?;
}
let start = *self.port_index.entry(id.clone()).or_insert(0);
let mut end = start + max_k2;
let mut index = end;
if end >= self.port_vec.len() {
end = self.port_vec.len();
index = 0
}
self.punch_symmetric(&self.port_vec[start..end], buf, &nat_info.public_ips, max_k2).await?;
self.port_index.insert(id, index);
}
NatType::Cone => {
let is_cone = self.context.is_cone();
for ip in nat_info.public_ips {
let addr = SocketAddr::V4(SocketAddrV4::new(ip, nat_info.public_port));
if is_cone {
self.context.send_main_udp(buf, addr).await?;
} else {
//只有一方是对称,则对称方要使用全部端口发送数据,符合上述计算的概率
self.context.send_all(buf, addr).await?;
}
tokio::time::sleep(Duration::from_millis(2)).await;
}
}
}
Ok(())
}
async fn punch_symmetric(&self, ports: &[u16], buf: &[u8], ips: &Vec<Ipv4Addr>, max: usize) -> io::Result<()> {
let mut count = 0;
for port in ports {
for pub_ip in ips {
count += 1;
if count == max {
return Ok(());
}
let addr = SocketAddr::V4(SocketAddrV4::new(*pub_ip, *port));
self.context.send_main_udp(buf, addr).await?;
tokio::time::sleep(Duration::from_millis(2)).await;
}
}
Ok(())
}
}
+23
View File
@@ -0,0 +1,23 @@
use std::ops::Deref;
use crate::channel::channel::Context;
#[derive(Clone)]
pub struct ChannelSender {
context: Context,
}
impl ChannelSender {
pub fn new(context: Context) -> Self {
Self {
context,
}
}
}
impl Deref for ChannelSender {
type Target = Context;
fn deref(&self) -> &Self::Target {
&self.context
}
}
+115
View File
@@ -0,0 +1,115 @@
use std::io;
use aes_gcm::{AeadInPlace, Aes128Gcm, Aes256Gcm, Key, Nonce, Tag,KeyInit};
use aes_gcm::aead::consts::{U12, U16};
use aes_gcm::aead::generic_array::GenericArray;
use sha2::Digest;
use crate::protocol;
use crate::protocol::{ip_turn_packet, NetPacket};
#[derive(Clone)]
pub enum Cipher {
AesGCM128(Aes128Gcm),
AesGCM256(Aes256Gcm),
None,
}
impl Cipher {
pub fn new(password: Option<String>) -> Self {
if let Some(password) = password {
let mut hasher = sha2::Sha256::new();
hasher.update(password.as_bytes());
let key: [u8; 32] = hasher.finalize().into();
if password.len() < 8 {
let key: &Key<Aes128Gcm> = key[..16].into();
Cipher::AesGCM128(Aes128Gcm::new(&key))
} else {
let key: &Key<Aes256Gcm> = &key.into();
Cipher::AesGCM256(Aes256Gcm::new(&key))
}
} else {
Cipher::None
}
}
pub fn decrypt_ipv4(&self, net_packet: &mut NetPacket<&mut [u8]>) -> io::Result<Option<usize>> {
match &self {
Cipher::None => {
return Ok(None);
}
_ => {}
}
if !net_packet.is_encrypt() {
//未加密的数据直接丢弃
return Err(io::Error::new(io::ErrorKind::Other, "not encrypt"));
}
if net_packet.payload().len() < 16 {
log::error!("数据异常,长度小于16");
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
}
let mut nonce = [0; 12];
nonce[0..4].copy_from_slice(&net_packet.source().octets());
nonce[4..8].copy_from_slice(&net_packet.destination().octets());
nonce[8] = protocol::Protocol::IpTurn.into();
nonce[9] = ip_turn_packet::Protocol::Ipv4.into();
let nonce: &GenericArray<u8, U12> = Nonce::from_slice(&nonce);
let payload_len = net_packet.payload().len() - 16;
let tag: GenericArray<u8, U16> = Tag::clone_from_slice(&net_packet.payload()[payload_len..]);
let rs = match &self {
Cipher::AesGCM128(cipher) => {
cipher.decrypt_in_place_detached(nonce, &[], &mut net_packet.payload_mut()[..payload_len], &tag)
}
Cipher::AesGCM256(cipher) => {
cipher.decrypt_in_place_detached(nonce, &[], &mut net_packet.payload_mut()[..payload_len], &tag)
}
Cipher::None => {
return Ok(None);
}
};
if let Err(e) = rs {
return Err(io::Error::new(io::ErrorKind::Other, format!("解密失败:{}", e)));
}
return Ok(Some(payload_len));
}
/// net_packet 必须预留足够长度
/// data_len是有效载荷的长度
/// 返回加密后载荷的长度
pub fn encrypt_ipv4(&self, payload_len: usize, net_packet: &mut NetPacket<&mut [u8]>) -> io::Result<Option<usize>> {
match &self {
Cipher::None => {
return Ok(None);
}
_ => {}
}
let mut nonce = [0; 12];
nonce[0..4].copy_from_slice(&net_packet.source().octets());
nonce[4..8].copy_from_slice(&net_packet.destination().octets());
nonce[8] = protocol::Protocol::IpTurn.into();
nonce[9] = ip_turn_packet::Protocol::Ipv4.into();
let nonce: &GenericArray<u8, U12> = Nonce::from_slice(&nonce);
let rs = match &self {
Cipher::AesGCM128(cipher) => {
cipher.encrypt_in_place_detached(nonce, &[], &mut net_packet.payload_mut()[..payload_len])
}
Cipher::AesGCM256(cipher) => {
cipher.encrypt_in_place_detached(nonce, &[], &mut net_packet.payload_mut()[..payload_len])
}
Cipher::None => {
return Ok(None);
}
};
return match rs {
Ok(tag) => {
if tag.len() != 16 {
return Err(io::Error::new(io::ErrorKind::Other, format!("加密tag长度错误:{}", tag.len())));
}
net_packet.set_encrypt_flag(true);
net_packet.payload_mut()[payload_len..payload_len + 16].copy_from_slice(tag.as_slice());
Ok(Some(payload_len + 16))
}
Err(e) => {
Err(io::Error::new(io::ErrorKind::Other, format!("加密失败:{}", e)))
}
};
}
}
+381
View File
@@ -0,0 +1,381 @@
use std::io;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::sync::Arc;
use std::time::Duration;
use crossbeam_skiplist::SkipMap;
use crossbeam_utils::atomic::AtomicCell;
use parking_lot::Mutex;
use tokio::net::{TcpStream, UdpSocket};
use tokio::sync::mpsc::channel;
use crate::channel::{Route, RouteKey};
use crate::channel::channel::{Channel, Context};
use crate::channel::idle::Idle;
use crate::channel::punch::{NatInfo, Punch};
use crate::channel::sender::ChannelSender;
use crate::cipher::Cipher;
use crate::core::status::VntStatusManger;
use crate::error::Error;
use crate::external_route::ExternalRoute;
use crate::handle::{ConnectStatus, CurrentDeviceInfo, heartbeat_handler, PeerDeviceInfo, punch_handler, registration_handler};
use crate::handle::recv_handler::ChannelDataHandler;
use crate::handle::registration_handler::{RegResponse, ReqEnum};
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
use crate::handle::tun_tap::tap_handler;
use crate::handle::tun_tap::tun_handler;
use crate::igmp_server::IgmpServer;
use crate::nat::NatTest;
use crate::tun_tap_device;
use crate::tun_tap_device::{DeviceReader, DeviceWriter};
pub mod status;
pub mod sync;
#[derive(Clone)]
pub struct Vnt {
name: String,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
context: Context,
vnt_status_manager: VntStatusManger,
device_writer: DeviceWriter,
/// 0. 机器纪元,每一次上线或者下线都会增1,用于感知网络中机器变化
/// 服务端和客户端的不一致,则服务端会推送新的设备列表
/// 1. 网络中的虚拟ip列表
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
nat_test: NatTest,
connect_status: Arc<AtomicCell<ConnectStatus>>,
peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>>,
}
pub struct VntUtil {
config: Config,
main_channel: UdpSocket,
main_tcp_channel: Option<TcpStream>,
response: Option<RegResponse>,
iface: Option<(DeviceWriter, DeviceReader)>,
}
impl VntUtil {
pub async fn new(config: Config) -> io::Result<VntUtil> {
let main_channel = UdpSocket::bind("0.0.0.0:0").await?;
Ok(VntUtil {
config,
main_channel,
main_tcp_channel: None,
response: None,
iface: None,
})
}
pub async fn connect(&mut self) -> Result<RegResponse, ReqEnum> {
if self.config.tcp {
match TcpStream::connect(self.config.server_address).await {
Ok(tcp) => {
let _ = self.main_tcp_channel.insert(tcp);
}
Err(e) => {
return Err(ReqEnum::Other(format!("connect error:{}", e)));
}
}
}
match registration_handler::registration(&self.main_channel, self.main_tcp_channel.as_mut(), self.config.server_address,
self.config.token.clone(), self.config.device_id.clone(),
self.config.name.clone(), self.config.ip.unwrap_or(Ipv4Addr::UNSPECIFIED)).await {
Ok(res) => {
let _ = self.response.insert(res.clone());
Ok(res)
}
Err(e) => {
Err(e)
}
}
}
#[cfg(any(target_os = "android"))]
pub fn create_iface(&mut self, vpn_fd: i32) {
let (device_writer, device_reader) = tun_tap_device::create(vpn_fd);
let _ = self.iface.insert((device_writer, device_reader));
}
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
pub fn create_iface(&mut self) -> io::Result<tun_tap_device::DriverInfo> {
if self.iface.is_some() {
return Err(io::Error::from(io::ErrorKind::AlreadyExists));
}
let response = match &self.response {
None => {
return Err(io::Error::from(io::ErrorKind::AlreadyExists));
}
Some(res) => {
res
}
};
let device_type = if self.config.tap {
#[cfg(windows)]
{
//删除tun网卡避免ip冲突,因为非正常退出会保留网卡
tun_tap_device::delete_device(tun_tap_device::DeviceType::Tun);
}
tun_tap_device::DeviceType::Tap
} else {
#[cfg(windows)]
{
//删除tap网卡避免ip冲突,非正常退出会保留网卡
tun_tap_device::delete_device(tun_tap_device::DeviceType::Tap);
}
tun_tap_device::DeviceType::Tun
};
let mtu = self.config.mtu.unwrap_or(1430);
let in_ips = self.config.in_ips.iter().map(|(dest, mask, _)| { (Ipv4Addr::from(*dest & *mask), Ipv4Addr::from(*mask)) }).collect::<Vec<(Ipv4Addr, Ipv4Addr)>>();
let (device_writer, device_reader, driver_info) = tun_tap_device::create_device(device_type, response.virtual_ip,
response.virtual_netmask, response.virtual_gateway, in_ips, mtu)?;
let _ = self.iface.insert((device_writer, device_reader));
Ok(driver_info)
}
pub async fn build(self) -> crate::Result<Vnt> {
let response = match self.response {
None => {
return Err(Error::Stop("response None".to_string()));
}
Some(res) => {
res
}
};
let (device_writer, device_reader) = match self.iface {
None => {
return Err(Error::Stop("iface None".to_string()));
}
Some(res) => {
res
}
};
let config = self.config;
let vnt_status_manager = VntStatusManger::new();
let cipher = Cipher::new(config.password.clone());
let virtual_ip = response.virtual_ip;
let virtual_gateway = response.virtual_gateway;
let virtual_netmask = response.virtual_netmask;
let current_device = Arc::new(AtomicCell::new(CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, config.server_address)));
let (cone_sender, cone_receiver) = channel(3);
let (symmetric_sender, symmetric_receiver) = channel(2);
let (tcp_sender, tcp) = if let Some(main_tcp_channel) = self.main_tcp_channel {
let (tcp_sender, tcp_receiver) = channel::<Vec<u8>>(100);
(Some(tcp_sender), Some((main_tcp_channel, tcp_receiver)))
} else {
(None, None)
};
let context = Context::new(Arc::new(self.main_channel), tcp_sender, current_device.clone(), 1);
let punch = Punch::new(context.clone());
let idle = Idle::new(Duration::from_secs(16), context.clone());
let channel_sender = ChannelSender::new(context.clone());
let register = Arc::new(registration_handler::Register::new(channel_sender.clone(),
config.server_address, config.token.clone(),
config.device_id.clone(), config.name.clone()));
let device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>> = Arc::new(Mutex::new((response.epoch, response.device_info_list)));
let peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>> = Arc::new(SkipMap::new());
let connect_status = Arc::new(AtomicCell::new(ConnectStatus::Connected));
let local_ip = crate::nat::local_ip()?;
let local_port = context.main_local_port()?;
// NAT检测
let nat_test = NatTest::new(config.nat_test_server.clone(), response.public_ip, response.public_port, local_ip, local_port);
let out_ips = config.out_ips.iter().map(|(_, _, ip)| *ip).collect::<Vec<Ipv4Addr>>();
let out_external_route = ExternalRoute::new(config.out_ips);
let in_external_route = if config.in_ips.is_empty() {
None
} else {
Some(ExternalRoute::new(config.in_ips))
};
let (tcp_proxy, udp_proxy, ip_proxy_map) = if out_ips.is_empty() {
(None, None, None)
} else {
let (tcp_proxy, udp_proxy, ip_proxy_map) = crate::ip_proxy::init_proxy(channel_sender.clone(), out_ips, current_device.clone()).await?;
(Some(tcp_proxy), Some(udp_proxy), Some(ip_proxy_map))
};
let igmp_server = if config.simulate_multicast {
Some(IgmpServer::new(device_writer.clone()))
} else {
None
};
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
if config.tap {
tap_handler::start(vnt_status_manager.worker("tap_handler"), channel_sender.clone(), device_reader, device_writer.clone(),
igmp_server.clone(), current_device.clone(), in_external_route, ip_proxy_map.clone(), cipher.clone());
} else {
tun_handler::start(vnt_status_manager.worker("tun_handler"), channel_sender.clone(), device_reader, device_writer.clone(),
igmp_server.clone(), current_device.clone(), in_external_route, ip_proxy_map.clone(), cipher.clone());
}
#[cfg(any(target_os = "android"))]
tun_handler::start(vnt_status_manager.worker("android tun_handler"), channel_sender.clone(), device_reader, device_writer.clone(),
igmp_server.clone(), current_device.clone(), in_external_route, ip_proxy_map.clone(), cipher.clone());
//外部数据接收处理
let channel_recv_handler = ChannelDataHandler::new(current_device.clone(), device_list.clone(),
register.clone(), nat_test.clone(), igmp_server,
device_writer.clone(), connect_status.clone(),
peer_nat_info_map.clone(), ip_proxy_map, out_external_route,
cone_sender, symmetric_sender, cipher, config.relay);
{
let channel = Channel::new(context.clone(), channel_recv_handler);
let channel_worker = vnt_status_manager.worker("channel_worker");
let relay = config.relay;
if let Some(tcp_proxy) = tcp_proxy {
tokio::spawn(tcp_proxy.start());
}
if let Some(udp_proxy) = udp_proxy {
tokio::spawn(udp_proxy.start());
}
tokio::spawn(async move {
channel.start(channel_worker, tcp, 14, 65, relay).await
});
}
{
let other_worker = vnt_status_manager.worker("punch_handler");
let nat_test = nat_test.clone();
let device_list = device_list.clone();
let current_device = current_device.clone();
// 定时心跳
heartbeat_handler::start_heartbeat(other_worker.worker("heartbeat"), channel_sender.clone(), device_list.clone(), current_device.clone(), config.server_address_str);
// 空闲检查
heartbeat_handler::start_idle(other_worker.worker("idle"), idle, channel_sender.clone());
if !config.relay {
// 打洞处理
punch_handler::start(other_worker.worker("cone_receiver"), cone_receiver, punch.clone(), current_device.clone());
punch_handler::start(other_worker.worker("symmetric_receiver"), symmetric_receiver, punch, current_device.clone());
tokio::spawn(punch_handler::start_punch(other_worker, nat_test,
device_list, channel_sender, current_device));
}
}
context.switch(nat_test.nat_info().nat_type);
Ok(Vnt {
name: config.name,
current_device,
context,
vnt_status_manager,
device_writer,
nat_test,
device_list,
connect_status,
peer_nat_info_map,
})
}
}
impl Vnt {
pub fn name(&self) -> &str {
&self.name
}
pub fn current_device(&self) -> CurrentDeviceInfo {
self.current_device.load()
}
pub fn peer_nat_info(&self, ip: &Ipv4Addr) -> Option<NatInfo> {
self.peer_nat_info_map.get(ip).map(|e| e.value().clone())
}
pub fn connection_status(&self) -> ConnectStatus {
self.connect_status.load()
}
pub fn nat_info(&self) -> NatInfo {
self.nat_test.nat_info()
}
pub fn device_list(&self) -> Vec<PeerDeviceInfo> {
let device_list_lock = self.device_list.lock();
let (_epoch, device_list) = device_list_lock.clone();
drop(device_list_lock);
device_list
}
pub fn route(&self, ip: &Ipv4Addr) -> Option<Route> {
self.context.route_one(ip)
}
pub fn route_key(&self, route_key: &RouteKey) -> Option<Ipv4Addr> {
self.context.route_to_id(route_key)
}
pub fn route_table(&self) -> Vec<(Ipv4Addr, Route)> {
self.context.route_table_one()
}
pub fn stop(&self) -> io::Result<()> {
self.context.close();
self.vnt_status_manager.stop_all();
self.device_writer.close()?;
let virtual_gateway = self.current_device.load().virtual_gateway;
let _ = std::net::UdpSocket::bind("0.0.0.0:0")?.send_to(&[0],
SocketAddr::V4(SocketAddrV4::new(virtual_gateway, 10000)));
Ok(())
}
pub async fn wait_stop(&mut self) {
self.vnt_status_manager.wait().await;
let _ = self.stop();
}
pub async fn wait_stop_ms(&mut self, ms: Duration) -> bool {
tokio::select! {
_=self.vnt_status_manager.wait()=>{
let _ = self.stop();
return true;
}
_=tokio::time::sleep(ms)=>{
return false;
}
}
}
}
impl Drop for Vnt {
fn drop(&mut self) {
let _ = self.stop();
}
}
#[derive(Clone, Debug)]
pub struct Config {
pub tap: bool,
pub token: String,
pub device_id: String,
pub name: String,
pub server_address: SocketAddr,
pub server_address_str: String,
pub nat_test_server: Vec<SocketAddr>,
pub in_ips: Vec<(u32, u32, Ipv4Addr)>,
pub out_ips: Vec<(u32, u32, Ipv4Addr)>,
pub password: Option<String>,
pub simulate_multicast: bool,
pub mtu: Option<u16>,
pub tcp: bool,
pub ip: Option<Ipv4Addr>,
pub relay: bool,
}
impl Config {
pub fn new(tap: bool, token: String,
device_id: String,
name: String,
server_address: SocketAddr,
server_address_str: String,
nat_test_server: Vec<SocketAddr>,
in_ips: Vec<(u32, u32, Ipv4Addr)>, out_ips: Vec<(u32, u32, Ipv4Addr)>,
password: Option<String>, simulate_multicast: bool, mtu: Option<u16>, tcp: bool,
ip: Option<Ipv4Addr>,
relay: bool, ) -> Self {
Self {
tap,
token,
device_id,
name,
server_address,
server_address_str,
nat_test_server,
in_ips,
out_ips,
password,
simulate_multicast,
mtu,
tcp,
ip,
relay,
}
}
}
+89
View File
@@ -0,0 +1,89 @@
use std::sync::Arc;
use tokio::sync::watch;
use tokio::sync::watch::{Receiver, Sender};
use crate::util::wait::WaitGroup;
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum VntStatus {
Starting,
Stopping,
}
pub struct VntWorker {
_name: String,
wg: WaitGroup,
status_s: Arc<Sender<VntStatus>>,
status_r: Receiver<VntStatus>,
}
impl VntWorker {
pub fn worker(&self, name: &str) -> Self {
self.wg.add();
VntWorker {
_name: name.to_string(),
wg: self.wg.clone(),
status_s: self.status_s.clone(),
status_r: self.status_r.clone(),
}
}
}
impl Drop for VntWorker {
fn drop(&mut self) {
self.wg.done();
}
}
impl VntWorker {
pub fn stop_all(&self) {
let _ = self.status_s.send(VntStatus::Stopping);
}
pub async fn stop_wait(&mut self) {
loop {
if *self.status_r.borrow() == VntStatus::Stopping {
return;
}
match self.status_r.changed().await {
Ok(_) => {
if *self.status_r.borrow() == VntStatus::Stopping {
return;
}
}
Err(_) => { return; }
}
}
}
}
#[derive(Clone)]
pub struct VntStatusManger {
wg: WaitGroup,
status_s: Arc<Sender<VntStatus>>,
status_r: Receiver<VntStatus>,
}
impl VntStatusManger {
pub fn new() -> Self {
let (status_s, status_r) = watch::channel(VntStatus::Starting);
Self {
wg: WaitGroup::new(),
status_s: Arc::new(status_s),
status_r,
}
}
pub fn stop_all(&self) {
let _ = self.status_s.send(VntStatus::Stopping);
}
pub async fn wait(&mut self) {
self.wg.wait().await
}
pub fn worker(&self, name: &str) -> VntWorker {
self.wg.add();
VntWorker {
_name: name.to_string(),
wg: self.wg.clone(),
status_s: self.status_s.clone(),
status_r: self.status_r.clone(),
}
}
}
+69
View File
@@ -0,0 +1,69 @@
use std::io;
use std::ops::Deref;
use std::time::Duration;
use tokio::runtime::Runtime;
use crate::core::{Config, Vnt, VntUtil};
use crate::handle::registration_handler::{RegResponse, ReqEnum};
pub struct VntUtilSync {
vnt_util: VntUtil,
runtime: Runtime,
}
pub struct VntSync {
vnt: Vnt,
runtime: Runtime,
}
impl VntUtilSync {
pub fn new(config: Config) -> io::Result<VntUtilSync> {
let runtime = tokio::runtime::Builder::new_multi_thread().enable_all().build().unwrap();
let vnt_util = runtime.block_on(VntUtil::new(config))?;
Ok(VntUtilSync {
vnt_util,
runtime,
})
}
pub fn connect(&mut self) -> Result<RegResponse, ReqEnum> {
self.runtime.block_on(self.vnt_util.connect())
}
#[cfg(any(target_os = "android"))]
pub fn create_iface(&mut self, vpn_fd: i32) {
self.vnt_util.create_iface(vpn_fd)
}
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
pub fn create_iface(&mut self) -> io::Result<crate::tun_tap_device::DriverInfo> {
self.vnt_util.create_iface()
}
pub fn build(self) -> crate::Result<VntSync> {
let runtime = self.runtime;
let vnt = runtime.block_on(self.vnt_util.build())?;
{
let mut vnt = vnt.clone();
std::thread::spawn(move || {
runtime.block_on(vnt.wait_stop())
});
}
Ok(VntSync {
vnt,
runtime: tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap(),
})
}
}
impl VntSync {
pub fn wait_stop(&mut self) {
self.runtime.block_on(self.vnt.wait_stop())
}
pub fn wait_stop_ms(&mut self, ms: u64) -> bool {
self.runtime.block_on(self.vnt.wait_stop_ms(Duration::from_millis(ms)))
}
}
impl Deref for VntSync {
type Target = Vnt;
fn deref(&self) -> &Self::Target {
&self.vnt
}
}
@@ -1,16 +1,11 @@
use std::io;
use crossbeam::channel::RecvError;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("packet error")]
PacketError(#[from] packet::error::Error),
#[error("Io error")]
Io(#[from] io::Error),
#[error("Channel error")]
Channel(#[from] RecvError),
#[error("Protobuf error")]
Protobuf(#[from] protobuf::Error),
#[error("Invalid packet")]
+26
View File
@@ -0,0 +1,26 @@
use std::net::Ipv4Addr;
use std::sync::Arc;
// 目标ip,子网掩码,网关
#[derive(Clone)]
pub struct ExternalRoute {
route_table: Arc<Vec<(u32, u32, Ipv4Addr)>>,
}
impl ExternalRoute {
pub fn new(route_table: Vec<(u32, u32, Ipv4Addr)>) -> Self {
Self {
route_table:Arc::new(route_table)
}
}
pub fn route(&self, ip: &Ipv4Addr) -> Option<Ipv4Addr> {
let ip = u32::from_be_bytes(ip.octets());
for (dest, mask, gateway) in self.route_table.iter() {
if *mask & ip == *mask & *dest {
return Some(*gateway);
}
}
None
}
}
+193
View File
@@ -0,0 +1,193 @@
use std::net::{Ipv4Addr, ToSocketAddrs};
use std::sync::Arc;
use std::time::Duration;
use std::io;
use crossbeam_utils::atomic::AtomicCell;
use parking_lot::Mutex;
use rand::prelude::SliceRandom;
use crate::channel::idle::Idle;
use crate::channel::Route;
use crate::channel::sender::ChannelSender;
use crate::core::status::VntWorker;
use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo};
use crate::protocol::control_packet::PingPacket;
use crate::protocol::{control_packet, MAX_TTL, NetPacket, Protocol, Version};
pub fn start_idle(mut worker: VntWorker, idle: Idle, sender: ChannelSender) {
tokio::spawn(async move {
tokio::select! {
_=worker.stop_wait()=>{
return;
}
rs=start_idle_(idle, sender)=>{
if let Err(e) = rs {
log::warn!("空闲检测任务停止:{:?}", e);
}
}
}
worker.stop_all();
});
}
async fn start_idle_(idle: Idle, sender: ChannelSender) -> io::Result<()> {
loop {
let (peer_ip, route) = idle.next_idle().await?;
log::info!(
"peer_ip:{:?},route:{:?}",
peer_ip,
route
);
sender.remove_route(&peer_ip, route);
}
}
pub fn start_heartbeat(
mut worker: VntWorker,
sender: ChannelSender,
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
server_address_str: String,
) {
tokio::spawn(async move {
tokio::select! {
_=worker.stop_wait()=>{
return;
}
rs=start_heartbeat_(sender, device_list, current_device,server_address_str)=>{
if let Err(e) = rs {
log::warn!("心跳任务停止:{:?}", e);
}
}
}
worker.stop_all();
});
}
fn set_now_time(packet: &mut NetPacket<[u8; 16]>) -> io::Result<()> {
let current_time = crate::handle::now_time() as u16;
let mut ping = PingPacket::new(packet.payload_mut())?;
ping.set_time(current_time);
Ok(())
}
async fn start_heartbeat_(
sender: ChannelSender,
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
server_address_str: String,
) -> io::Result<()> {
let mut net_packet = NetPacket::new([0u8; 16])?;
net_packet.set_version(Version::V1);
net_packet.set_protocol(Protocol::Control);
net_packet.set_transport_protocol(control_packet::Protocol::Ping.into());
//只寻找两跳以内能到的目标
net_packet.first_set_ttl(2);
let mut count = 0;
loop {
if sender.is_close() {
return Ok(());
}
let mut current_dev = current_device.load();
if count % 10 == 0 {
let mut packet = NetPacket::new([0; 12])?;
packet.set_version(Version::V1);
packet.set_protocol(Protocol::Control);
packet.set_transport_protocol(
control_packet::Protocol::AddrRequest.into(),
);
packet.first_set_ttl(MAX_TTL);
packet.set_source(current_dev.virtual_ip());
packet.set_destination(current_dev.virtual_gateway);
let _ = sender.send_main_udp(packet.buffer(), current_dev.connect_server).await;
}
if count % 20 == 19 {
if let Ok(mut addr) = server_address_str.to_socket_addrs() {
if let Some(addr) = addr.next() {
if addr != current_dev.connect_server {
let mut tmp = current_dev.clone();
tmp.connect_server = addr;
if current_device.compare_exchange(current_dev, tmp).is_ok() {
current_dev.connect_server = addr;
}
}
}
}
}
net_packet.set_source(current_dev.virtual_ip());
{
let mut ping = PingPacket::new(net_packet.payload_mut())?;
let epoch = { device_list.lock().0 };
ping.set_epoch(epoch);
}
set_now_time(&mut net_packet)?;
net_packet.set_destination(current_dev.virtual_gateway());
if let Err(e) = sender.send_main(net_packet.buffer(), current_dev.connect_server).await
{
log::warn!(
"connect_server:{:?},e:{:?}",
current_dev.connect_server,
e
);
}
if count < 7 || count % 7 == 0 {
let mut route_list: Option<Vec<(Ipv4Addr, Vec<Route>)>> = None;
let peer_list = { device_list.lock().1.clone() };
for peer in peer_list {
if peer.virtual_ip == current_dev.virtual_ip {
continue;
}
set_now_time(&mut net_packet)?;
net_packet.set_destination(peer.virtual_ip);
if let Some(route) = sender.route_one(&peer.virtual_ip) {
let _ = sender.send_by_key(net_packet.buffer(), &route.route_key()).await;
if route.is_p2p() {
continue;
}
} else {
//没有直连路由则发送到网关
let _ = sender.send_main(net_packet.buffer(), current_dev.connect_server).await;
continue;
}
//再随机发送到其他地址,看有没有客户端符合转发条件
let route_list = route_list.get_or_insert_with(|| {
let mut l = sender.route_table();
l.shuffle(&mut rand::thread_rng());
l
});
let mut num = 0;
'a: for (peer_ip, route_list) in route_list.iter() {
for route in route_list {
if peer_ip != &peer.virtual_ip && route.is_p2p() {
set_now_time(&mut net_packet)?;
let _ = sender.try_send_by_key(net_packet.buffer(), &route.route_key());
num += 1;
break;
}
if num >= 3 {
break 'a;
}
}
}
tokio::time::sleep(Duration::from_millis(1)).await;
}
} else {
for (peer_ip, route_list) in sender.route_table().iter() {
net_packet.set_destination(*peer_ip);
for route in route_list {
set_now_time(&mut net_packet)?;
if let Err(e) = sender.send_by_key(net_packet.buffer(), &route.route_key()).await {
log::warn!("peer_ip:{:?},route:{:?},e:{:?}", peer_ip, route, e);
}
tokio::time::sleep(Duration::from_millis(2)).await;
}
}
}
count += 1;
tokio::time::sleep(Duration::from_millis(5000)).await;
}
}
@@ -2,9 +2,18 @@ use std::net::{Ipv4Addr, SocketAddr};
pub mod heartbeat_handler;
pub mod punch_handler;
pub mod registration_handler;
pub mod tun_handler;
pub mod recv_handler;
pub mod registration_handler;
pub mod tun_tap;
pub fn now_time() -> u64 {
let now = std::time::SystemTime::now();
if let Ok(timestamp) = now.duration_since(std::time::UNIX_EPOCH) {
timestamp.as_secs() * 1000 + u64::from(timestamp.subsec_millis())
} else {
0
}
}
/// 是否在一个网段
fn check_dest(dest: Ipv4Addr, virtual_netmask: Ipv4Addr, virtual_network: Ipv4Addr) -> bool {
@@ -29,7 +38,7 @@ impl PeerDeviceInfo {
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[derive(Copy, Clone, Debug, Eq, PartialEq,Ord, PartialOrd)]
pub enum PeerDeviceStatus {
Online,
Offline,
@@ -70,6 +79,7 @@ pub struct CurrentDeviceInfo {
pub broadcast_address: Ipv4Addr,
//链接的服务器地址
pub connect_server: SocketAddr,
}
impl CurrentDeviceInfo {
@@ -103,7 +113,3 @@ impl CurrentDeviceInfo {
self.virtual_gateway
}
}
+140
View File
@@ -0,0 +1,140 @@
use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo};
use crate::nat::NatTest;
use crate::proto::message::{PunchInfo, PunchNatType};
use crate::protocol::{control_packet, other_turn_packet, NetPacket, Protocol, Version, MAX_TTL};
use crossbeam_utils::atomic::AtomicCell;
use parking_lot::Mutex;
use protobuf::Message;
use rand::prelude::SliceRandom;
use std::net::Ipv4Addr;
use std::sync::Arc;
use std::time::Duration;
use std::io;
use tokio::sync::mpsc::Receiver;
use crate::channel::punch::{NatInfo, Punch};
use crate::channel::sender::ChannelSender;
use crate::core::status::VntWorker;
pub fn start(mut worker: VntWorker, receiver: Receiver<(Ipv4Addr, NatInfo)>, punch: Punch, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) {
tokio::spawn(async move {
tokio::select! {
_=start0(receiver, punch, current_device)=>{}
_=worker.stop_wait()=>{
return;
}
}
worker.stop_all();
});
}
pub async fn start0(mut receiver: Receiver<(Ipv4Addr, NatInfo)>, mut punch: Punch, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) {
while let Some((peer_ip, nat_info)) = receiver.recv().await {
if let Err(e) = start_(&mut punch, &current_device, peer_ip, nat_info).await {
log::warn!("网络打洞异常 {:?}", e);
}
}
}
async fn start_(
punch: &mut Punch,
current_device: &Arc<AtomicCell<CurrentDeviceInfo>>,
peer_ip: Ipv4Addr,
nat_info: NatInfo,
) -> io::Result<()> {
let mut packet = NetPacket::new([0u8; 12])?;
packet.set_version(Version::V1);
packet.first_set_ttl(1);
packet.set_protocol(Protocol::Control);
packet.set_transport_protocol(control_packet::Protocol::PunchRequest.into());
packet.set_source(current_device.load().virtual_ip());
packet.set_destination(peer_ip);
log::info!("发起打洞,目标:{:?},{:?}", peer_ip, nat_info);
punch.punch(packet.buffer(), peer_ip, nat_info).await
}
pub async fn start_punch(
mut worker: VntWorker,
nat_test: NatTest,
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
sender: ChannelSender,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
) {
let mut num = 0;
let sleep_time = [3, 5, 7, 11, 13, 17, 19, 23, 29];
loop {
if sender.is_close() {
break;
}
tokio::select! {
rs= start_punch_(Duration::from_secs(sleep_time[num % sleep_time.len()]),&nat_test, &device_list, &sender, &current_device)=>{
if let Err(e) = rs {
log::warn!("打洞处理任务异常 {:?}", e);
}
}
_=worker.stop_wait()=>{
break;
}
}
num += 1;
}
}
async fn start_punch_(
sleep_time: Duration,
nat_test: &NatTest,
device_list: &Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
sender: &ChannelSender,
current_device: &Arc<AtomicCell<CurrentDeviceInfo>>,
) -> crate::Result<()> {
let current_device = current_device.load();
let nat_info = nat_test.nat_info();
let mut list = device_list.lock().clone().1;
list.shuffle(&mut rand::thread_rng());
let mut count = 0;
for info in list {
if info.virtual_ip <= current_device.virtual_ip {
continue;
}
if !sender.need_punch(&info.virtual_ip) {
continue;
}
count += 1;
if count > 2 {
break;
}
let buf = punch_packet(current_device.virtual_ip(), &nat_info, info.virtual_ip)?;
let _ = sender.send_main(&buf, current_device.connect_server).await;
}
tokio::time::sleep(sleep_time).await;
Ok(())
}
pub fn punch_packet(
virtual_ip: Ipv4Addr,
nat_info: &NatInfo,
dest: Ipv4Addr,
) -> crate::Result<Vec<u8>> {
let mut punch_reply = PunchInfo::new();
punch_reply.reply = false;
punch_reply.public_ip_list = nat_info
.public_ips
.iter()
.map(|ip| u32::from_be_bytes(ip.octets()))
.collect();
punch_reply.public_port = nat_info.public_port as u32;
punch_reply.public_port_range = nat_info.public_port_range as u32;
punch_reply.local_ip = u32::from_be_bytes(nat_info.local_ip.octets());
punch_reply.local_port = nat_info.local_port as u32;
punch_reply.nat_type = protobuf::EnumOrUnknown::new(PunchNatType::from(nat_info.nat_type));
let bytes = punch_reply.write_to_bytes()?;
let mut net_packet = NetPacket::new(vec![0u8; 12 + bytes.len()])?;
net_packet.set_version(Version::V1);
net_packet.set_protocol(Protocol::OtherTurn);
net_packet.set_transport_protocol(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);
Ok(net_packet.into_buffer())
}
+524
View File
@@ -0,0 +1,524 @@
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::sync::Arc;
use crossbeam_utils::atomic::AtomicCell;
use crossbeam_skiplist::SkipMap;
use parking_lot::Mutex;
use protobuf::Message;
use tokio::sync::mpsc::Sender;
use packet::icmp::{icmp, Kind};
use packet::icmp::icmp::HeaderOther;
use packet::ip::ipv4;
use packet::ip::ipv4::packet::IpV4Packet;
use crate::channel::channel::Context;
use crate::channel::punch::{NatInfo, NatType};
use crate::channel::{Route, RouteKey};
use crate::cipher::Cipher;
use crate::error::Error;
use crate::external_route::ExternalRoute;
use crate::handle::{check_dest, ConnectStatus, CurrentDeviceInfo, PeerDeviceInfo, PeerDeviceStatus};
use crate::handle::registration_handler::Register;
use crate::igmp_server::IgmpServer;
use crate::ip_proxy::IpProxyMap;
use crate::nat;
use crate::nat::NatTest;
use crate::proto::message::{DeviceList, PunchInfo, PunchNatType, RegistrationResponse};
use crate::protocol::{control_packet, MAX_TTL, NetPacket, Protocol, service_packet, other_turn_packet, Version, ip_turn_packet};
use crate::protocol::control_packet::ControlPacket;
use crate::protocol::error_packet::InErrorPacket;
use crate::tun_tap_device::DeviceWriter;
#[derive(Clone)]
pub struct ChannelDataHandler {
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
register: Arc<Register>,
nat_test: NatTest,
igmp_server: Option<IgmpServer>,
device_writer: DeviceWriter,
connect_status: Arc<AtomicCell<ConnectStatus>>,
peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>>,
ip_proxy_map: Option<IpProxyMap>,
out_external_route: ExternalRoute,
cone_sender: Sender<(Ipv4Addr, NatInfo)>,
symmetric_sender: Sender<(Ipv4Addr, NatInfo)>,
cipher: Cipher,
relay: bool,
}
impl ChannelDataHandler {
pub fn new(current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
register: Arc<Register>,
nat_test: NatTest,
igmp_server: Option<IgmpServer>,
device_writer: DeviceWriter,
connect_status: Arc<AtomicCell<ConnectStatus>>,
peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>>,
ip_proxy_map: Option<IpProxyMap>,
out_external_route: ExternalRoute,
cone_sender: Sender<(Ipv4Addr, NatInfo)>,
symmetric_sender: Sender<(Ipv4Addr, NatInfo)>,
cipher: Cipher,
relay: bool, ) -> Self {
Self {
current_device,
device_list,
register,
nat_test,
igmp_server,
device_writer,
connect_status,
peer_nat_info_map,
ip_proxy_map,
out_external_route,
cone_sender,
symmetric_sender,
cipher,
relay,
}
}
}
impl ChannelDataHandler {
pub async fn handle(&mut self, buf: &mut [u8], start: usize, end: usize, route_key: RouteKey, context: &Context) {
assert_eq!(start, 14);
match self.handle0(&mut buf[..end], &route_key, context).await {
Ok(_) => {}
Err(e) => {
log::error!("{:?}",e);
}
}
}
async fn handle0(&self, buf: &mut [u8], route_key: &RouteKey, context: &Context) -> crate::Result<()> {
let mut net_packet = NetPacket::new(&mut buf[14..])?;
if net_packet.ttl() == 0 {
return Ok(());
}
let source = net_packet.source();
let current_device = self.current_device.load();
let destination = net_packet.destination();
let not_broadcast = !destination.is_broadcast() && !destination.is_multicast() && destination != current_device.broadcast_address;
if current_device.virtual_ip() != destination
&& not_broadcast && !destination.is_unspecified()
&& self.connect_status.load() == ConnectStatus::Connected {
if !check_dest(source, current_device.virtual_netmask, current_device.virtual_network) {
log::warn!("转发数据,源地址错误:{:?},当前网络:{:?},route_key:{:?}",source,current_device.virtual_network,route_key);
return Ok(());
}
if !check_dest(destination, current_device.virtual_netmask, current_device.virtual_network) {
log::warn!("转发数据,目的地址错误:{:?},当前网络:{:?},route_key:{:?}",destination,current_device.virtual_network,route_key);
return Ok(());
}
net_packet.set_ttl(net_packet.ttl() - 1);
let ttl = net_packet.ttl();
if ttl > 0 {
// 转发
if let Some(route) = context.route_one(&destination) {
if route.metric <= net_packet.ttl() {
context.send_by_key(net_packet.buffer(), &route.route_key()).await?;
}
} else if (ttl > 1 || destination == current_device.virtual_gateway())
&& source != current_device.virtual_gateway() {
//网关默认要转发一次,生存时间不够的发到网关也会被丢弃
context.send_main(net_packet.buffer(), current_device.connect_server).await?;
}
}
return Ok(());
}
match net_packet.protocol() {
Protocol::IpTurn => {
match ip_turn_packet::Protocol::from(net_packet.transport_protocol()) {
ip_turn_packet::Protocol::Icmp => {
let ipv4 = IpV4Packet::new(net_packet.payload())?;
if ipv4.protocol() == ipv4::protocol::Protocol::Icmp {
self.device_writer.write_ipv4(&mut buf[12..])?;
return Ok(());
}
}
ip_turn_packet::Protocol::Igmp => {
if let Some(igmp_server) = &self.igmp_server {
let ipv4 = IpV4Packet::new(net_packet.payload())?;
if ipv4.protocol() == ipv4::protocol::Protocol::Igmp {
igmp_server.handle(ipv4.payload(), source)?;
}
}
return Ok(());
}
ip_turn_packet::Protocol::Ipv4 => {
let data = if let Some(payload_len) = self.cipher.decrypt_ipv4(&mut net_packet)? {
&mut net_packet.payload_mut()[..payload_len]
} else {
net_packet.payload_mut()
};
let mut ipv4 = IpV4Packet::new(data)?;
match ipv4.protocol() {
ipv4::protocol::Protocol::Igmp => {
if let Some(igmp_server) = &self.igmp_server {
igmp_server.handle(ipv4.payload(), source)?;
}
return Ok(());
}
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);
//不管加不加密,和接收到的数据长度都一致
let _ = self.cipher.encrypt_ipv4(net_packet.payload().len() - 16, &mut net_packet)?;
context.send_by_key(net_packet.buffer(), route_key).await?;
return Ok(());
}
}
}
_ => {}
}
if not_broadcast && ipv4.destination_ip() != destination {
if let Some(ip_proxy_map) = &self.ip_proxy_map {
if let Some(gate_way) = self.out_external_route.route(&ipv4.destination_ip()) {
match ipv4.protocol() {
ipv4::protocol::Protocol::Tcp => {
let dest_ip = ipv4.destination_ip();
//转发到代理目标地址
let mut tcp_packet = packet::tcp::tcp::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(ip_proxy_map.tcp_proxy_port);
tcp_packet.update_checksum();
ipv4.set_destination_ip(destination);
ipv4.update_checksum();
ip_proxy_map.tcp_proxy_map.insert(SocketAddrV4::new(source, source_port),
(SocketAddrV4::new(gate_way, 0), SocketAddrV4::new(dest_ip, dest_port)));
}
ipv4::protocol::Protocol::Udp => {
let dest_ip = ipv4.destination_ip();
//转发到代理目标地址
let mut udp_packet = packet::udp::udp::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(ip_proxy_map.udp_proxy_port);
udp_packet.update_checksum();
ipv4.set_destination_ip(destination);
ipv4.update_checksum();
ip_proxy_map.udp_proxy_map.insert(SocketAddrV4::new(source, source_port),
(SocketAddrV4::new(gate_way, 0), SocketAddrV4::new(dest_ip, dest_port)));
}
ipv4::protocol::Protocol::Icmp => {
let dest_ip = ipv4.destination_ip();
//转发到代理目标地址
let icmp_packet = icmp::IcmpPacket::new(ipv4.payload())?;
match icmp_packet.header_other() {
HeaderOther::Identifier(id, seq) => {
ip_proxy_map.icmp_proxy_map.insert((dest_ip, id, seq), source);
ip_proxy_map.send_icmp(ipv4.payload(), &gate_way, &dest_ip)?;
}
_ => {
return Ok(());
}
}
}
_ => {
return Ok(());
}
}
}
}
}
//传输协议12字节
self.device_writer.write_ipv4(&mut buf[12..])?;
return Ok(());
}
ip_turn_packet::Protocol::Ipv4Broadcast => {
//客户端不帮忙转发广播包,所以不会出现这种类型的数据
}
ip_turn_packet::Protocol::Unknown(_) => {}
}
}
Protocol::Service => {
self.service(context, current_device, source, net_packet, route_key).await?;
}
Protocol::Error => {
self.error(context, current_device, source, net_packet, route_key).await?;
}
Protocol::Control => {
self.control(context, current_device, source, net_packet, route_key).await?;
}
Protocol::OtherTurn => {
self.other_turn(context, current_device, source, net_packet, route_key).await?;
}
Protocol::UnKnow(e) => {
log::info!("不支持的协议:{}",e);
}
}
Ok(())
}
async fn service(&self, context: &Context, current_device: CurrentDeviceInfo, _source: Ipv4Addr, net_packet: NetPacket<&mut [u8]>, route_key: &RouteKey) -> crate::Result<()> {
match service_packet::Protocol::from(net_packet.transport_protocol()) {
service_packet::Protocol::RegistrationRequest => {}
service_packet::Protocol::RegistrationResponse => {
let response = RegistrationResponse::parse_from_bytes(net_packet.payload())?;
let local_port = context.main_local_port()?;
let local_ip = nat::local_ip()?;
let nat_info = self.nat_test.re_test(Ipv4Addr::from(response.public_ip),
response.public_port as u16, local_ip, local_port);
context.switch(nat_info.nat_type);
let new_ip = Ipv4Addr::from(response.virtual_ip);
let current_ip = current_device.virtual_ip();
if current_ip != new_ip {
// ip发生变化
log::info!("ip发生变化,old_ip:{:?},new_ip:{:?}",current_ip,new_ip);
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
let old_netmask = current_device.virtual_netmask;
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
let old_gateway = current_device.virtual_gateway();
let virtual_ip = Ipv4Addr::from(response.virtual_ip);
let virtual_gateway = Ipv4Addr::from(response.virtual_gateway);
let virtual_netmask = Ipv4Addr::from(response.virtual_netmask);
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
self.device_writer.change_ip(virtual_ip, virtual_netmask, virtual_gateway, old_netmask, old_gateway)?;
let new_current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway,
virtual_netmask, current_device.connect_server);
if let Err(e) = self.current_device.compare_exchange(current_device, new_current_device) {
log::warn!("替换失败:{:?}",e);
}
}
self.connect_status.store(ConnectStatus::Connected);
}
service_packet::Protocol::PollDeviceList => {}
service_packet::Protocol::PushDeviceList => {
let device_list_t = DeviceList::parse_from_bytes(net_packet.payload())?;
let ip_list: Vec<PeerDeviceInfo> = device_list_t
.device_info_list
.into_iter()
.map(|info| {
PeerDeviceInfo::new(
Ipv4Addr::from(info.virtual_ip),
info.name,
info.device_status as u8,
)
})
.collect();
let route = Route::from(*route_key, 2, 99);
for x in &ip_list {
if x.status == PeerDeviceStatus::Online {
context.add_route_if_absent(x.virtual_ip, route);
}
}
let mut dev = self.device_list.lock();
if dev.0 != device_list_t.epoch as u16 {
dev.0 = device_list_t.epoch as u16;
dev.1 = ip_list;
}
}
service_packet::Protocol::Unknown(u) => {
log::warn!("未知服务协议:{}",u);
}
}
Ok(())
}
async fn error(&self, _context: &Context, current_device: CurrentDeviceInfo, _source: Ipv4Addr, net_packet: NetPacket<&mut [u8]>, _route_key: &RouteKey) -> crate::Result<()> {
log::info!("current_device:{:?}",current_device);
match InErrorPacket::new(net_packet.transport_protocol(), net_packet.payload())? {
InErrorPacket::TokenError => {
return Err(Error::Stop("Token error".to_string()));
}
InErrorPacket::Disconnect => {
{
//掉线epoch要归零
let mut dev = self.device_list.lock();
dev.0 = 0;
}
self.connect_status.store(ConnectStatus::Connecting);
self.register.fast_register(current_device.virtual_ip).await?;
}
InErrorPacket::AddressExhausted => {
//地址用尽
return Err(Error::Stop("IP address has been exhausted".to_string()));
}
InErrorPacket::OtherError(e) => {
log::error!("OtherError {:?}", e.message());
}
InErrorPacket::IpAlreadyExists => {
log::error!("IpAlreadyExists");
}
InErrorPacket::InvalidIp => {
log::error!("InvalidIp");
}
}
Ok(())
}
async fn control(&self, context: &Context, current_device: CurrentDeviceInfo, source: Ipv4Addr, mut net_packet: NetPacket<&mut [u8]>, route_key: &RouteKey) -> crate::Result<()> {
let metric = net_packet.source_ttl() - net_packet.ttl() + 1;
match ControlPacket::new(net_packet.transport_protocol(), net_packet.payload())? {
ControlPacket::PingPacket(_) => {
context.update_read_time(&source, route_key);
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);
context.send_by_key(net_packet.buffer(), route_key).await?;
let route = Route::from(*route_key, metric, 99);
context.add_route_if_absent(source, route);
}
ControlPacket::PongPacket(pong_packet) => {
context.update_read_time(&source, route_key);
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.add_route(source, route);
if source == current_device.virtual_gateway() {
let epoch = self.device_list.lock().0;
if pong_packet.epoch() != epoch {
let mut poll_device = NetPacket::new([0; 12])?;
poll_device.set_source(current_device.virtual_ip());
poll_device.set_destination(source);
poll_device.set_version(Version::V1);
poll_device.first_set_ttl(MAX_TTL);
poll_device.set_protocol(Protocol::Service);
poll_device.set_transport_protocol(service_packet::Protocol::PollDeviceList.into());
context.send_main(poll_device.buffer(), current_device.connect_server).await?;
}
}
}
ControlPacket::PunchRequest => {
if self.relay {
return Ok(());
}
//回应
net_packet.set_transport_protocol(control_packet::Protocol::PunchResponse.into());
net_packet.set_source(current_device.virtual_ip());
net_packet.set_destination(source);
net_packet.first_set_ttl(1);
context.send_by_key(net_packet.buffer(), route_key).await?;
let route = Route::from(*route_key, metric, 99);
context.add_route_if_absent(source, route);
}
ControlPacket::PunchResponse => {
if self.relay {
return Ok(());
}
// log::info!("PunchResponse route_key:{:?}",route_key);
let route = Route::from(*route_key, metric, 99);
context.add_route_if_absent(source, route);
}
ControlPacket::AddrRequest => {
match route_key.addr.ip() {
std::net::IpAddr::V4(ipv4) => {
let mut packet = NetPacket::new([0;12+6])?;
packet.set_version(Version::V1);
packet.set_protocol(Protocol::Control);
packet.set_transport_protocol(
control_packet::Protocol::AddrResponse.into(),
);
packet.first_set_ttl(MAX_TTL);
packet.set_source(current_device.virtual_ip());
packet.set_destination(source);
let mut addr_packet = control_packet::AddrPacket::new(packet.payload_mut())?;
addr_packet.set_ipv4(ipv4);
addr_packet.set_port(route_key.addr.port());
context.send_by_key(packet.buffer(), route_key).await?;
}
std::net::IpAddr::V6(_) => {}
}
}
ControlPacket::AddrResponse(addr_packet) => {
if addr_packet.port() != 0
&& !addr_packet.ipv4().is_multicast()
&& !addr_packet.ipv4().is_broadcast()
&& !addr_packet.ipv4().is_unspecified()
&& !addr_packet.ipv4().is_loopback()
&& !addr_packet.ipv4().is_private() {
self.nat_test.update_addr(addr_packet.ipv4(), addr_packet.port())
}
}
}
Ok(())
}
async fn other_turn(&self, context: &Context, current_device: CurrentDeviceInfo, source: Ipv4Addr, net_packet: NetPacket<&mut [u8]>, route_key: &RouteKey) -> crate::Result<()> {
if self.relay {
return Ok(());
}
match other_turn_packet::Protocol::from(net_packet.transport_protocol()) {
other_turn_packet::Protocol::Punch => {
let punch_info = PunchInfo::parse_from_bytes(net_packet.payload())?;
let public_ips = punch_info.public_ip_list.
iter().map(|v| { Ipv4Addr::from(v.to_be_bytes()) }).collect();
let peer_nat_info = NatInfo::new(public_ips,
punch_info.public_port as u16,
punch_info.public_port_range as u16,
Ipv4Addr::from(punch_info.local_ip.to_be_bytes()),
punch_info.local_port as u16,
punch_info.nat_type.enum_value_or_default().into());
self.peer_nat_info_map.insert(source, peer_nat_info.clone());
if !punch_info.reply {
let mut punch_reply = PunchInfo::new();
punch_reply.reply = true;
let nat_info = self.nat_test.nat_info();
punch_reply.public_ip_list = nat_info.public_ips.iter().map(|ip| u32::from_be_bytes(ip.octets())).collect();
punch_reply.public_port = nat_info.public_port as u32;
punch_reply.public_port_range = nat_info.public_port_range as u32;
punch_reply.nat_type =
protobuf::EnumOrUnknown::new(PunchNatType::from(nat_info.nat_type));
punch_reply.local_ip = u32::from_be_bytes(nat_info.local_ip.octets());
punch_reply.local_port = nat_info.local_port as u32;
let bytes = punch_reply.write_to_bytes()?;
let mut net_packet =
NetPacket::new(vec![0u8; 12 + bytes.len()])?;
net_packet.set_version(Version::V1);
net_packet.set_protocol(Protocol::OtherTurn);
net_packet.set_transport_protocol(
other_turn_packet::Protocol::Punch.into(),
);
net_packet.first_set_ttl(MAX_TTL);
net_packet.set_source(current_device.virtual_ip());
net_packet.set_destination(source);
net_packet.set_payload(&bytes);
if !peer_nat_info.local_ip.is_unspecified() && peer_nat_info.local_port != 0 {
let mut packet = NetPacket::new([0u8; 12])?;
packet.set_version(Version::V1);
packet.first_set_ttl(1);
packet.set_protocol(Protocol::Control);
packet.set_transport_protocol(control_packet::Protocol::PunchRequest.into());
packet.set_source(current_device.virtual_ip());
packet.set_destination(source);
let _ = context.send_main(packet.buffer(), SocketAddr::V4(SocketAddrV4::new(peer_nat_info.local_ip, peer_nat_info.local_port))).await;
}
if self.punch(source, peer_nat_info).await {
context.send_by_key(net_packet.buffer(), route_key).await?;
}
} else {
self.punch(source, peer_nat_info).await;
}
}
other_turn_packet::Protocol::Unknown(e) => {
log::warn!("不支持的转发协议 {:?},source:{:?}",e,source);
}
}
Ok(())
}
async fn punch(&self, peer_ip: Ipv4Addr, peer_nat_info: NatInfo) -> bool {
match peer_nat_info.nat_type {
NatType::Symmetric => {
self.symmetric_sender.try_send((peer_ip, peer_nat_info)).is_ok()
}
NatType::Cone => {
self.cone_sender.try_send((peer_ip, peer_nat_info)).is_ok()
}
}
}
}
+244
View File
@@ -0,0 +1,244 @@
use std::io;
use std::net::{Ipv4Addr, SocketAddr};
use std::time::{Duration, Instant};
use crossbeam_utils::atomic::AtomicCell;
use protobuf::Message;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpStream, UdpSocket};
use crate::channel::sender::ChannelSender;
use crate::handle::PeerDeviceInfo;
use crate::proto::message::{RegistrationRequest, RegistrationResponse};
use crate::protocol::error_packet::InErrorPacket;
use crate::protocol::{service_packet, NetPacket, Protocol, Version, MAX_TTL};
pub enum ReqEnum {
TokenError,
AddressExhausted,
IpAlreadyExists,
InvalidIp,
Timeout,
ServerError(String),
Other(String),
}
#[derive(Clone, Debug)]
pub struct RegResponse {
pub virtual_ip: Ipv4Addr,
pub virtual_gateway: Ipv4Addr,
pub virtual_netmask: Ipv4Addr,
pub epoch: u16,
pub device_info_list: Vec<PeerDeviceInfo>,
pub public_ip: Ipv4Addr,
pub public_port: u16,
}
///向中继服务器注册,token标识一个虚拟网关,device_id防止多次注册时得到的ip不一致
pub async fn registration(
main_channel: &UdpSocket,
main_tcp_channel: Option<&mut TcpStream>,
server_address: SocketAddr,
token: String,
device_id: String,
name: String,
ip: Ipv4Addr,
) -> Result<RegResponse, ReqEnum> {
let request_packet =
registration_request_packet(token.clone(), device_id.clone(), name.clone(), ip, false, false).unwrap();
let buf = request_packet.buffer();
let mut recv_buf = [0u8; 10240];
let recv_buf = if let Some(main_tcp_channel) = main_tcp_channel {
let mut vec = vec![0; 4 + buf.len()];
let len = buf.len();
vec[2] = (len >> 8) as u8;
vec[3] = (len & 0xFF) as u8;
vec[4..].copy_from_slice(buf);
if let Err(e) = main_tcp_channel.write_all(&vec).await {
return Err(ReqEnum::Other(format!("send error:{}", e)));
}
if let Err(e) = main_tcp_channel.read_exact(&mut recv_buf[..4]).await {
return Err(ReqEnum::Other(format!("read error:{}", e)));
}
let len = 4 + (((recv_buf[2] as u16) << 8) | recv_buf[3] as u16) as usize;
if let Err(e) = main_tcp_channel.read_exact(&mut recv_buf[4..len]).await {
return Err(ReqEnum::Other(format!("read error:{}", e)));
}
&recv_buf[4..len]
} else {
if let Err(e) = main_channel.send_to(buf, server_address).await {
return Err(ReqEnum::Other(format!("send error:{}", e)));
}
match tokio::time::timeout(Duration::from_millis(300), main_channel.recv_from(&mut recv_buf)).await {
Ok(rs) => {
match rs {
Ok((len, addr)) => {
if server_address != addr {
return Err(ReqEnum::Other(format!("invalid data,from {}", addr)));
}
&recv_buf[..len]
}
Err(e) => {
return Err(ReqEnum::Other(format!("receiver error:{}", e)));
}
}
}
Err(_) => {
return Err(ReqEnum::Timeout);
}
}
};
let net_packet = match NetPacket::new(recv_buf) {
Ok(net_packet) => {
net_packet
}
Err(e) => {
return Err(ReqEnum::ServerError(format!("{}", e)));
}
};
match net_packet.protocol() {
Protocol::Service => {
match service_packet::Protocol::from(net_packet.transport_protocol()) {
service_packet::Protocol::RegistrationResponse => {
match RegistrationResponse::parse_from_bytes(net_packet.payload()) {
Ok(response) => {
let device_info_list: Vec<PeerDeviceInfo> = response
.device_info_list
.into_iter()
.map(|info| {
PeerDeviceInfo::new(
Ipv4Addr::from(info.virtual_ip),
info.name,
info.device_status as u8,
)
})
.collect();
Ok(RegResponse {
virtual_ip: Ipv4Addr::from(response.virtual_ip),
virtual_gateway: Ipv4Addr::from(response.virtual_gateway),
virtual_netmask: Ipv4Addr::from(response.virtual_netmask),
epoch: response.epoch as u16,
device_info_list,
public_ip: Ipv4Addr::from(response.public_ip),
public_port: response.public_port as u16,
})
}
Err(_) => {
Err(ReqEnum::ServerError("invalid data".to_string()))
}
}
}
_ => {
Err(ReqEnum::ServerError("invalid data".to_string()))
}
}
}
Protocol::Error => {
match InErrorPacket::new(net_packet.transport_protocol(), net_packet.payload()) {
Ok(e) => match e {
InErrorPacket::TokenError => Err(ReqEnum::TokenError),
InErrorPacket::Disconnect => {
Err(ReqEnum::ServerError("disconnect".to_string()))
}
InErrorPacket::AddressExhausted => {
Err(ReqEnum::AddressExhausted)
}
InErrorPacket::OtherError(e) => match e.message() {
Ok(str) => {
Err(ReqEnum::ServerError(str))
}
Err(e) => Err(ReqEnum::Other(format!("{}", e))),
},
InErrorPacket::IpAlreadyExists => {
Err(ReqEnum::IpAlreadyExists)
}
InErrorPacket::InvalidIp => {
Err(ReqEnum::InvalidIp)
}
},
Err(e) => Err(ReqEnum::Other(format!("{}", e))),
}
}
_ => Err(ReqEnum::ServerError("invalid data".to_string())),
}
}
fn registration_request_packet(
token: String,
device_id: String,
name: String,
ip: Ipv4Addr,
is_fast: bool,
allow_ip_change: bool,
) -> crate::Result<NetPacket<Vec<u8>>> {
let mut request = RegistrationRequest::new();
request.token = token;
request.device_id = device_id;
request.name = name;
request.virtual_ip = ip.into();
request.allow_ip_change = allow_ip_change;
request.is_fast = is_fast;
request.version = "1.1.1".to_string();
let bytes = request.write_to_bytes()?;
let buf = vec![0u8; 12 + bytes.len()];
let mut net_packet = NetPacket::new(buf)?;
net_packet.set_version(Version::V1);
net_packet.set_protocol(Protocol::Service);
net_packet.set_transport_protocol(service_packet::Protocol::RegistrationRequest.into());
net_packet.first_set_ttl(MAX_TTL);
net_packet.set_payload(&bytes);
Ok(net_packet)
}
pub struct Register {
sender: ChannelSender,
server_address: SocketAddr,
token: String,
device_id: String,
name: String,
time: AtomicCell<Instant>,
}
impl Register {
pub fn new(
sender: ChannelSender,
server_address: SocketAddr,
token: String,
device_id: String,
name: String,
) -> Self {
Self {
sender,
server_address,
token,
device_id,
name,
time: AtomicCell::new(Instant::now()),
}
}
pub async fn fast_register(&self, ip: Ipv4Addr) -> io::Result<()> {
let last = self.time.load();
if last.elapsed() < Duration::from_secs(2)
|| self
.time
.compare_exchange(last, Instant::now())
.is_err()
{
//短时间不重复注册
return Ok(());
}
log::info!("重新连接");
let request_packet = registration_request_packet(
self.token.clone(),
self.device_id.clone(),
self.name.clone(),
ip,
false,
true,
)
.unwrap();
let buf = request_packet.buffer();
self.sender.send_main(buf, self.server_address).await?;
Ok(())
}
}
+231
View File
@@ -0,0 +1,231 @@
use std::net::{Ipv4Addr, SocketAddrV4};
use packet::ip::ipv4::packet::IpV4Packet;
use packet::ip::ipv4::protocol::Protocol;
use packet::tcp::tcp::TcpPacket;
use packet::udp::udp::UdpPacket;
use crate::channel::sender::ChannelSender;
use crate::cipher::Cipher;
use crate::external_route::ExternalRoute;
use crate::handle::{check_dest, CurrentDeviceInfo};
use crate::ip_proxy::IpProxyMap;
use crate::protocol::{ip_turn_packet, NetPacket, Version};
use crate::error::*;
use crate::igmp_server::IgmpServer;
use crate::protocol;
use crate::protocol::ip_turn_packet::BroadcastPacketEnd;
pub mod tun_handler;
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
pub mod tap_handler;
async fn broadcast(sender: &ChannelSender, net_packet: &mut NetPacket<&mut [u8]>, data_len: usize, current_device: &CurrentDeviceInfo) -> Result<()> {
let mut peer_ips = Vec::with_capacity(8);
let vec = sender.route_table_one();
let mut relay_count = 0;
const MAX_COUNT: usize = u8::MAX as usize;
for (peer_ip, route) in vec {
if peer_ip == current_device.virtual_gateway {
continue;
}
if peer_ips.len() == MAX_COUNT {
break;
}
if route.is_p2p()
&& sender.send_by_key(&net_packet.buffer()[..data_len], &route.route_key()).await.is_ok() {
peer_ips.push(peer_ip);
} else {
relay_count += 1;
}
}
if relay_count == 0 && !peer_ips.is_empty() && peer_ips.len() != MAX_COUNT {
//不需要转发
return Ok(());
}
if peer_ips.is_empty() {
sender.send_main(&net_packet.buffer()[..data_len], current_device.connect_server).await?;
} else {
let end_len = 1 + peer_ips.len() * 4;
//剩余的发送到服务端,需要告知哪些已发送过
//放在末尾可以减少复制次数
let mut broadcast = BroadcastPacketEnd::unchecked(&mut net_packet.buffer_mut()[data_len..data_len + end_len]);
broadcast.set_address(&peer_ips)?;
net_packet.set_transport_protocol(ip_turn_packet::Protocol::Ipv4Broadcast.into());
sender.send_main(&net_packet.buffer()[..(data_len + end_len)], current_device.connect_server).await?;
}
Ok(())
}
async fn multicast(igmp_server: &IgmpServer, multicast_addr: Ipv4Addr, sender: &ChannelSender, net_packet: &mut NetPacket<&mut [u8]>, data_len: usize, current_device: &CurrentDeviceInfo) -> Result<()> {
let mut peer_ips = Vec::with_capacity(8);
let vec = sender.route_table_one();
let mut relay_count = 0;
const MAX_COUNT: usize = u8::MAX as usize;
if let Some(members) = igmp_server.load(&multicast_addr) {
for (peer_ip, route) in vec {
if peer_ip == current_device.virtual_gateway {
continue;
}
let is_send = { members.read().is_send(&peer_ip) };
if is_send {
if peer_ips.len() == MAX_COUNT {
break;
}
if route.is_p2p()
&& sender.send_by_key(&net_packet.buffer()[..data_len], &route.route_key()).await.is_ok() {
peer_ips.push(peer_ip);
} else {
relay_count += 1;
}
}
}
}
if relay_count == 0 && !peer_ips.is_empty() && peer_ips.len() != MAX_COUNT {
//不需要转发
return Ok(());
}
if peer_ips.is_empty() {
sender.send_main(&net_packet.buffer()[..data_len], current_device.connect_server).await?;
} else {
let end_len = 1 + peer_ips.len() * 4;
//剩余的发送到服务端,需要告知哪些已发送过
//放在末尾可以减少复制次数
let mut broadcast = BroadcastPacketEnd::unchecked(&mut net_packet.buffer_mut()[data_len..data_len + end_len]);
broadcast.set_address(&peer_ips)?;
net_packet.set_transport_protocol(ip_turn_packet::Protocol::Ipv4Broadcast.into());
sender.send_main(&net_packet.buffer()[..(data_len + end_len)], current_device.connect_server).await?;
}
Ok(())
}
/// 实现一个原地发送,必须保证是如下结构
/// |12字节开头|ip报文|至少1024字节+12字节结尾|
///
#[inline]
pub async fn base_handle(sender: &ChannelSender, buf: &mut [u8],
mut data_len: usize,//数据总长度=ip长度+12
igmp_server: &Option<IgmpServer>,
current_device: CurrentDeviceInfo,
ip_route: &Option<ExternalRoute>, proxy_map: &Option<IpProxyMap>, cipher: &Cipher) -> Result<()> {
let ipv4_packet = IpV4Packet::new(&buf[12..data_len])?;
let protocol = ipv4_packet.protocol();
let ip_head_len = ipv4_packet.header_len() as usize * 4;
let src_ip = ipv4_packet.source_ip();
let mut dest_ip = ipv4_packet.destination_ip();
let mut net_packet = NetPacket::new(buf)?;
net_packet.set_version(Version::V1);
net_packet.set_protocol(protocol::Protocol::IpTurn);
net_packet.set_transport_protocol(ip_turn_packet::Protocol::Ipv4.into());
net_packet.first_set_ttl(3);
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_transport_protocol(ip_turn_packet::Protocol::Icmp.into());
//发送到服务端的不加密
sender.send_main(&net_packet.buffer()[..data_len], current_device.connect_server).await?;
}
return Ok(());
}
if dest_ip.is_multicast() {
match protocol {
Protocol::Igmp => {
if igmp_server.is_some() {
net_packet.set_transport_protocol(ip_turn_packet::Protocol::Igmp.into());
//发送到服务端
net_packet.set_destination(current_device.virtual_gateway);
sender.send_main(&net_packet.buffer()[..data_len], current_device.connect_server).await?;
}
return Ok(());
}
Protocol::Udp => {
if let Some(igmp_server) = igmp_server {
if let Some(len) = cipher.encrypt_ipv4(data_len - 12, &mut net_packet)? {
data_len = 12 + len;
}
multicast(igmp_server, dest_ip, sender, &mut net_packet, data_len, &current_device).await?;
return Ok(());
} else {
//当广播
dest_ip = Ipv4Addr::BROADCAST;
net_packet.set_destination(dest_ip);
}
}
_ => {
return Ok(());
}
}
}
if dest_ip.is_broadcast() || current_device.broadcast_address == dest_ip {
// 广播 发送到直连目标
if Protocol::Udp == protocol {
if let Some(len) = cipher.encrypt_ipv4(data_len - 12, &mut net_packet)? {
data_len = 12 + len;
}
broadcast(sender, &mut net_packet, data_len, &current_device).await?;
}
return Ok(());
}
if !check_dest(dest_ip, current_device.virtual_netmask, current_device.virtual_network) {
if let Some(ip_route) = ip_route {
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(());
}
} else {
return Ok(());
}
} else if let Some(proxy_map) = proxy_map {
match protocol {
Protocol::Tcp => {
let dest_addr = {
let tcp_packet = TcpPacket::new(src_ip, dest_ip, &mut net_packet.buffer_mut()[12 + ip_head_len..data_len])?;
SocketAddrV4::new(dest_ip, tcp_packet.destination_port())
};
if let Some(entry) = proxy_map.tcp_proxy_map.get(&dest_addr) {
let source_addr = entry.value().1;
let source_ip = *source_addr.ip();
let mut tcp_packet = TcpPacket::new(source_ip, dest_ip, &mut net_packet.buffer_mut()[12 + ip_head_len..data_len])?;
tcp_packet.set_source_port(source_addr.port());
tcp_packet.update_checksum();
let mut ipv4_packet = IpV4Packet::new(&mut net_packet.buffer_mut()[12..data_len])?;
ipv4_packet.set_source_ip(source_ip);
ipv4_packet.update_checksum();
}
}
Protocol::Udp => {
let dest_addr = {
let udp_packet = UdpPacket::new(src_ip, dest_ip, &mut net_packet.buffer_mut()[12 + ip_head_len..data_len])?;
SocketAddrV4::new(dest_ip, udp_packet.destination_port())
};
if let Some(entry) = proxy_map.udp_proxy_map.get(&dest_addr) {
let source_addr = entry.value().1;
let source_ip = *source_addr.ip();
let mut udp_packet = UdpPacket::new(source_ip, dest_ip, &mut net_packet.buffer_mut()[12 + ip_head_len..data_len])?;
udp_packet.set_source_port(source_addr.port());
udp_packet.update_checksum();
let mut ipv4_packet = IpV4Packet::new(&mut net_packet.buffer_mut()[12..data_len])?;
ipv4_packet.set_source_ip(source_ip);
ipv4_packet.update_checksum();
}
}
_ => {}
}
}
if let Some(len) = cipher.encrypt_ipv4(data_len - 12, &mut net_packet)? {
data_len = 12 + len;
}
//优先发到直连到地址
if sender.send_by_id(&net_packet.buffer()[..data_len], &dest_ip).await.is_err() {
sender.send_main(&net_packet.buffer()[..data_len], current_device.connect_server).await?;
}
return Ok(());
}
+124
View File
@@ -0,0 +1,124 @@
use std::{io, thread};
use std::sync::Arc;
use crossbeam_utils::atomic::AtomicCell;
use packet::arp::arp::ArpPacket;
use packet::ethernet;
use packet::ethernet::packet::EthernetPacket;
use packet::icmp::icmp::IcmpPacket;
use packet::icmp::Kind;
use packet::ip::ipv4;
use packet::ip::ipv4::packet::IpV4Packet;
use crate::channel::sender::ChannelSender;
use crate::cipher::Cipher;
use crate::core::status::VntWorker;
use crate::external_route::ExternalRoute;
use crate::handle::CurrentDeviceInfo;
use crate::igmp_server::IgmpServer;
use crate::ip_proxy::IpProxyMap;
use crate::tun_tap_device::{DeviceReader, DeviceWriter};
pub fn start(worker: VntWorker, sender: ChannelSender,
device_reader: DeviceReader,
device_writer: DeviceWriter,
igmp_server: Option<IgmpServer>,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
ip_route: Option<ExternalRoute>,
ip_proxy_map: Option<IpProxyMap>,
cipher: Cipher) {
thread::Builder::new().name("tap_handler".into()).spawn(move || {
tokio::runtime::Builder::new_current_thread()
.enable_all().build().unwrap()
.block_on(async move {
if let Err(e) = start_(sender, device_reader,
device_writer, igmp_server,
current_device, ip_route, ip_proxy_map, cipher).await {
log::warn!("tap:{:?}",e);
}
worker.stop_all();
});
}).unwrap();
}
async fn start_(sender: ChannelSender,
device_reader: DeviceReader,
device_writer: DeviceWriter,
igmp_server: Option<IgmpServer>,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
ip_route: Option<ExternalRoute>,
ip_proxy_map: Option<IpProxyMap>,
cipher: Cipher) -> io::Result<()> {
let mut buf = [0; 4096];
loop {
//ip拆包了会直接丢弃?
let len = device_reader.read(&mut buf)?;
if let Err(e) = handle(&mut buf, len, &igmp_server, &current_device, &device_writer, &sender, &ip_route, &ip_proxy_map, &cipher).await {
log::warn!("tap handle{:?}",e);
}
}
}
async fn handle(buf: &mut [u8], len: usize, igmp_server: &Option<IgmpServer>, current_device: &AtomicCell<CurrentDeviceInfo>,
device_writer: &DeviceWriter, sender: &ChannelSender, ip_route: &Option<ExternalRoute>, proxy_map: &Option<IpProxyMap>, cipher: &Cipher) -> crate::Result<()> {
let mut ethernet_packet = EthernetPacket::new(&mut buf[..len])?;
let current_device = current_device.load();
match ethernet_packet.protocol() {
ethernet::protocol::Protocol::Arp => {
let mut out_ethernet_packet = EthernetPacket::unchecked(ethernet_packet.buffer.to_vec());
let arp_packet = ArpPacket::unchecked(ethernet_packet.payload());
let mut out_arp_packet = ArpPacket::unchecked(out_ethernet_packet.payload_mut());
let sender_h = arp_packet.sender_hardware_addr();
let sender_p = arp_packet.sender_protocol_addr();
let target_p = arp_packet.target_protocol_addr();
if target_p == &[0, 0, 0, 0] || sender_p == &[0, 0, 0, 0] || target_p == sender_p {
return Ok(());
}
//回复一个虚假的MAC地址
out_arp_packet.set_sender_hardware_addr(&[target_p[0], target_p[1], target_p[2], target_p[3], !sender_h[5], 234]);
out_arp_packet.set_sender_protocol_addr(target_p);
out_arp_packet.set_target_hardware_addr(sender_h);
out_arp_packet.set_target_protocol_addr(sender_p);
out_arp_packet.set_op_code(2);
out_ethernet_packet.set_source(&[target_p[0], target_p[1], target_p[2], target_p[3], !sender_h[5], 234]);
out_ethernet_packet.set_destination(sender_h);
device_writer.write_ethernet_tap(&out_ethernet_packet.buffer)?;
}
ethernet::protocol::Protocol::Ipv4 => {
let mut ipv4_packet = IpV4Packet::unchecked(ethernet_packet.payload_mut());
let src_ip = ipv4_packet.source_ip();
if src_ip != current_device.virtual_ip() {
return Ok(());
}
let dest_ip = ipv4_packet.destination_ip();
let protocol = ipv4_packet.protocol();
if src_ip == dest_ip {
if protocol == ipv4::protocol::Protocol::Icmp {
let mut icmp = IcmpPacket::new(ipv4_packet.payload_mut())?;
if icmp.kind() == Kind::EchoRequest {
icmp.set_kind(Kind::EchoReply);
icmp.update_checksum();
ipv4_packet.set_source_ip(dest_ip);
ipv4_packet.set_destination_ip(src_ip);
ipv4_packet.update_checksum();
let source = ethernet_packet.source().to_vec();
let dest = ethernet_packet.destination().to_vec();
ethernet_packet.set_source(&dest);
ethernet_packet.set_destination(&source);
device_writer.write_ethernet_tap(&ethernet_packet.buffer)?;
}
}
return Ok(());
}
// 以太网帧头部14字节,预留12字节
return crate::handle::tun_tap::base_handle(sender, &mut buf[2..], len - 2, igmp_server, current_device,
ip_route, proxy_map, cipher).await;
}
_ => {
// log::warn!("不支持的二层协议:{:?}",p)
}
}
Ok(())
}
+101
View File
@@ -0,0 +1,101 @@
use std::{io, thread};
use std::sync::Arc;
use crossbeam_utils::atomic::AtomicCell;
use packet::icmp::Kind;
use packet::icmp::icmp::IcmpPacket;
use packet::ip::ipv4;
use packet::ip::ipv4::packet::IpV4Packet;
use crate::channel::sender::ChannelSender;
use crate::cipher::Cipher;
use crate::core::status::VntWorker;
use crate::error::*;
use crate::external_route::ExternalRoute;
use crate::handle::CurrentDeviceInfo;
use crate::igmp_server::IgmpServer;
use crate::ip_proxy::IpProxyMap;
use crate::tun_tap_device::{DeviceReader, DeviceWriter};
fn icmp(device_writer: &DeviceWriter, mut ipv4_packet: IpV4Packet<&mut [u8]>) -> Result<()> {
if ipv4_packet.protocol() == ipv4::protocol::Protocol::Icmp {
let mut icmp = IcmpPacket::new(ipv4_packet.payload_mut())?;
if icmp.kind() == Kind::EchoRequest {
icmp.set_kind(Kind::EchoReply);
icmp.update_checksum();
let src = ipv4_packet.source_ip();
ipv4_packet.set_source_ip(ipv4_packet.destination_ip());
ipv4_packet.set_destination_ip(src);
ipv4_packet.update_checksum();
device_writer.write_ipv4_tun(ipv4_packet.buffer)?;
}
}
Ok(())
}
/// 接收tun数据,并且转发到udp上
#[inline]
async fn handle(sender: &ChannelSender, data: &mut [u8], len: usize, device_writer: &DeviceWriter, igmp_server: &Option<IgmpServer>, current_device: CurrentDeviceInfo,
ip_route: &Option<ExternalRoute>, proxy_map: &Option<IpProxyMap>, cipher: &Cipher) -> Result<()> {
let ipv4_packet = if let Ok(ipv4_packet) = IpV4Packet::new(&mut data[12..len]) {
ipv4_packet
} else {
return Ok(());
};
let src_ip = ipv4_packet.source_ip();
let dest_ip = ipv4_packet.destination_ip();
if src_ip != current_device.virtual_ip() {
return Ok(());
}
if src_ip == dest_ip {
return icmp(&device_writer, ipv4_packet);
}
return crate::handle::tun_tap::base_handle(sender, data, len, igmp_server, current_device, ip_route, proxy_map, cipher).await;
}
pub fn start(worker: VntWorker, sender: ChannelSender,
device_reader: DeviceReader,
device_writer: DeviceWriter,
igmp_server: Option<IgmpServer>,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
ip_route: Option<ExternalRoute>,
ip_proxy_map: Option<IpProxyMap>,
cipher: Cipher) {
thread::Builder::new().name("tun_handler".into()).spawn(move || {
tokio::runtime::Builder::new_current_thread()
.enable_all().build().unwrap()
.block_on(async move {
if let Err(e) = start_(sender, device_reader, &device_writer, igmp_server, current_device, ip_route, ip_proxy_map, cipher).await {
log::warn!("stop:{}",e);
}
let _ = device_writer.close();
worker.stop_all();
})
}).unwrap();
}
async fn start_(sender: ChannelSender,
device_reader: DeviceReader,
device_writer: &DeviceWriter,
igmp_server: Option<IgmpServer>,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
ip_route: Option<ExternalRoute>,
ip_proxy_map: Option<IpProxyMap>,
cipher: Cipher) -> io::Result<()> {
let mut buf = [0; 4096];
loop {
if sender.is_close() {
return Ok(());
}
let len = device_reader.read(&mut buf[12..])? + 12;
#[cfg(any(target_os = "macos"))]
let mut buf = &mut buf[4..];
match handle(&sender, &mut buf, len, device_writer, &igmp_server, current_device.load(), &ip_route, &ip_proxy_map, &cipher).await {
Ok(_) => {}
Err(e) => {
log::warn!("{:?}", e)
}
}
}
}
+236
View File
@@ -0,0 +1,236 @@
use std::collections::{HashMap, HashSet};
use std::net::Ipv4Addr;
use std::sync::Arc;
use std::time::{Duration, Instant};
use crossbeam_skiplist::SkipMap;
use parking_lot::RwLock;
use packet::igmp::igmp_v2::IgmpV2Packet;
use packet::igmp::igmp_v3::{IgmpV3QueryPacket, IgmpV3RecordType, IgmpV3ReportPacket};
use packet::igmp::IgmpType;
use packet::ip::ipv4::protocol::Protocol;
use crate::tun_tap_device::DeviceWriter;
//1. 定时发送query,启动时20秒一次,连发3次,之后8分钟一次
//2. 接收网关的igmp report 维护组播源信息
#[derive(Clone, Debug)]
pub struct Multicast {
//成员虚拟ip
members: HashMap<Ipv4Addr, Instant>,
//是否是过滤模式
//成员过滤或包含的源ip
map: HashMap<Ipv4Addr, (bool, HashSet<Ipv4Addr>)>,
}
impl Multicast {
pub fn new() -> Self {
Self {
members: Default::default(),
map: Default::default(),
}
}
pub fn is_send(&self, ip: &Ipv4Addr) -> bool {
if self.members.contains_key(ip) {
if let Some((is_include, set)) = self.map.get(ip) {
if *is_include {
set.contains(ip)
} else {
!set.contains(ip)
}
} else {
true
}
} else {
false
}
}
}
#[derive(Clone)]
pub struct IgmpServer {
multicast: Arc<SkipMap<Ipv4Addr, Arc<RwLock<Multicast>>>>,
}
impl IgmpServer {
pub fn new(device_writer: DeviceWriter) -> Self {
let multicast: Arc<SkipMap<Ipv4Addr, Arc<RwLock<Multicast>>>> = Arc::new(SkipMap::new());
std::thread::spawn(move || {
//预留以太网帧头和ip头
let mut buf = [0; 14 + 24 + 12];
let dest = Ipv4Addr::new(224, 0, 0, 1);
let src = Ipv4Addr::new(10, 26, 0, 1);
{
let buf = &mut buf[14..];
let len = buf.len();
// ipv4 头部20字节
buf[0] = 0b0100_0110;
//写入总长度
buf[2..4].copy_from_slice(&(len as u16).to_be_bytes());
//ttl
buf[8] = 1;
buf[20] = 0x94;
buf[21] = 0x04;
let mut ipv4 = packet::ip::ipv4::packet::IpV4Packet::unchecked(buf);
ipv4.set_flags(2);
ipv4.set_protocol(Protocol::Igmp);
ipv4.set_source_ip(src);
ipv4.set_destination_ip(dest);
ipv4.update_checksum();
}
{
let mut igmp_query = IgmpV3QueryPacket::unchecked(&mut buf[14 + 24..]);
igmp_query.set_igmp_type();
igmp_query.set_max_resp_code(50);
igmp_query.set_group_address(Ipv4Addr::UNSPECIFIED);
igmp_query.set_qrv(2);
igmp_query.set_qqic(10);
igmp_query.update_checksum();
}
loop {
let _ = device_writer.write_ipv4(&mut buf);
std::thread::sleep(Duration::from_secs(20))
}
});
Self {
multicast,
}
}
pub fn load(&self, multicast_addr: &Ipv4Addr) -> Option<Arc<RwLock<Multicast>>> {
if let Some(entry) = self.multicast.get(multicast_addr) {
Some(entry.value().clone())
} else {
None
}
}
pub fn handle(&self, buf: &[u8], source: Ipv4Addr) -> crate::Result<()> {
for x in self.multicast.iter() {
let mut list = Vec::new();
let mut write_guard = x.value().write();
for (ip, time) in &write_guard.members {
if time.elapsed() > Duration::from_secs(30) {
list.push(*ip);
}
}
for ip in list {
write_guard.members.remove(&ip);
write_guard.map.remove(&ip);
}
}
match IgmpType::from(buf[0]) {
IgmpType::Query => {}
IgmpType::ReportV1 | IgmpType::ReportV2 => {
//加入组播,v1和v2差不多
let report = IgmpV2Packet::new(buf)?;
let multicast_addr = report.group_address();
if !multicast_addr.is_multicast() {
return Ok(());
}
let multi = self.multicast.get_or_insert_with(multicast_addr, || {
Arc::new(RwLock::new(Multicast::new()))
});
let mut guard = multi.value().write();
guard.members.insert(source, Instant::now());
}
IgmpType::LeaveV2 => {
//退出组播
let leave = IgmpV2Packet::new(buf)?;
let multicast_addr = leave.group_address();
if !multicast_addr.is_multicast() {
return Ok(());
}
if let Some(entry) = self.multicast.get(&multicast_addr) {
let mut guard = entry.value().write();
guard.map.remove(&source);
guard.members.remove(&source);
}
}
IgmpType::ReportV3 => {
let report = IgmpV3ReportPacket::new(buf)?;
if let Some(group_records) = report.group_records() {
for group_record in group_records {
let multicast_addr = group_record.multicast_address();
if !multicast_addr.is_multicast() {
return Ok(());
}
let multi = self.multicast.get_or_insert_with(multicast_addr, || {
Arc::new(RwLock::new(Multicast::new()))
});
let mut guard = multi.value().write();
match group_record.record_type() {
IgmpV3RecordType::ModeIsInclude | IgmpV3RecordType::ChangeToIncludeMode => {
match group_record.source_addresses() {
None => {
//不接收所有
guard.members.remove(&source);
guard.map.remove(&source);
}
Some(src) => {
guard.members.insert(source, Instant::now());
guard.map.insert(source, (true, HashSet::from_iter(src)));
}
}
}
IgmpV3RecordType::ModeIsExclude | IgmpV3RecordType::ChangeToExcludeMode => {
match group_record.source_addresses() {
None => {
//接收所有
guard.members.insert(source, Instant::now());
guard.map.remove(&source);
}
Some(src) => {
guard.members.insert(source, Instant::now());
guard.map.insert(source, (false, HashSet::from_iter(src)));
}
}
}
IgmpV3RecordType::AllowNewSources => {
//在已有源的基础上,接收目标源,如果是排除模式,则删除;是包含模式则添加
match group_record.source_addresses() {
None => {}
Some(src) => {
match guard.map.get_mut(&source) {
None => {}
Some((is_include, set)) => {
for ip in src {
if *is_include {
set.insert(ip);
} else {
set.remove(&ip);
}
}
}
}
}
}
}
IgmpV3RecordType::BlockOldSources => {
//在已有源的基础上,不接收目标源
match group_record.source_addresses() {
None => {}
Some(src) => {
match guard.map.get_mut(&source) {
None => {}
Some((is_include, set)) => {
for ip in src {
if *is_include {
set.remove(&ip);
} else {
set.insert(ip);
}
}
}
}
}
}
}
IgmpV3RecordType::Unknown(_) => {}
}
}
}
}
IgmpType::Unknown(_) => {}
}
Ok(())
}
}
+135
View File
@@ -0,0 +1,135 @@
use std::io;
use std::mem::MaybeUninit;
use std::net::{IpAddr, Ipv4Addr, SocketAddrV4};
use std::sync::Arc;
use crossbeam_utils::atomic::AtomicCell;
use crossbeam_skiplist::SkipMap;
use socket2::{Domain, SockAddr, Socket, Type};
use packet::icmp::icmp;
use packet::icmp::icmp::HeaderOther;
use packet::ip::ipv4;
use crate::channel::sender::ChannelSender;
use crate::handle::CurrentDeviceInfo;
use crate::protocol::{MAX_TTL, NetPacket, Protocol, Version};
pub struct IcmpProxy {
icmp_socket: Arc<Socket>,
// 对端-> 真实来源
icmp_proxy_map: Arc<SkipMap<(Ipv4Addr, u16, u16), Ipv4Addr>>,
sender: ChannelSender,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
}
impl IcmpProxy {
pub fn new(addr: SocketAddrV4, icmp_proxy_map: Arc<SkipMap<(Ipv4Addr, u16, u16), Ipv4Addr>>, sender: ChannelSender, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) -> io::Result<IcmpProxy> {
let icmp_socket = Arc::new(Socket::new(Domain::IPV4, Type::RAW, Some(socket2::Protocol::ICMPV4))?);
icmp_socket.bind(&SockAddr::from(addr))?;
// // 设置 SIO_RCVALL 参数
// #[cfg(windows)]
// {
// use std::os::windows::io::AsRawSocket;
// let raw_fd = icmp_socket.as_raw_socket();
// let mut rcvall: winapi::shared::minwindef::DWORD = 1;
// let mut bytes_returned: winapi::shared::minwindef::DWORD = 0;
// let result = unsafe {
// winapi::um::winsock2::WSAIoctl(
// raw_fd as _,
// winapi::shared::mstcpip::SIO_RCVALL,
// &mut rcvall as *mut winapi::shared::minwindef::DWORD as *mut std::ffi::c_void,
// std::mem::size_of::<winapi::shared::minwindef::DWORD>() as winapi::shared::minwindef::DWORD,
// std::ptr::null_mut(),
// 0,
// &mut bytes_returned as winapi::shared::minwindef::LPDWORD,
// std::ptr::null_mut(),
// None,
// )
// };
// if result != 0 {
// return Err(io::Error::from_raw_os_error(unsafe { winapi::um::winsock2::WSAGetLastError() }));
// }
// }
Ok(IcmpProxy {
icmp_socket,
icmp_proxy_map,
sender,
current_device,
})
}
pub fn icmp_socket(&self) -> Arc<Socket> {
self.icmp_socket.clone()
}
pub fn start(self) {
let mut buf = [0 as u8; 1500];
let data: &mut [MaybeUninit<u8>] =
unsafe { std::mem::transmute(&mut buf[..]) };
let mut net_packet = NetPacket::new([0u8; 4 + 8 + 1500]).unwrap();
net_packet.set_version(Version::V1);
net_packet.set_protocol(Protocol::IpTurn);
net_packet.set_transport_protocol(ipv4::protocol::Protocol::Icmp.into());
net_packet.first_set_ttl(MAX_TTL);
loop {
match self.recv(data) {
Ok((len, peer_ip)) => {
match peer_ip {
IpAddr::V4(peer_ip) => {
match ipv4::packet::IpV4Packet::new(&mut buf[..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(entry) = self.icmp_proxy_map.get(&(peer_ip, id, seq)) {
//将数据发送到真实的来源
let dest_ip = *entry.value();
ipv4_packet.set_destination_ip(dest_ip);
ipv4_packet.update_checksum();
let current_device = self.current_device.load();
let virtual_ip = current_device.virtual_ip();
let connect_server = current_device.connect_server;
net_packet.set_source(virtual_ip);
net_packet.set_destination(dest_ip);
let data_len = ipv4_packet.buffer.len();
net_packet.set_payload(ipv4_packet.buffer);
if self.sender.try_send_by_id(&net_packet.buffer()[..(12 + data_len)], &dest_ip).is_err() {
let _ = self.sender.try_send_main(&net_packet.buffer()[..(12 + data_len)], connect_server);
}
}
}
_ => {
continue;
}
}
}
Err(_) => {}
};
}
Err(_) => {}
}
}
IpAddr::V6(_) => {}
}
}
Err(e) => {
log::warn!("icmp代理异常:{:?}",e);
}
}
}
}
fn recv(&self, buf: &mut [MaybeUninit<u8>]) -> io::Result<(usize, IpAddr)> {
let (size, addr) = self.icmp_socket.recv_from(buf)?;
let addr = match addr.as_socket() {
None => {
IpAddr::V4(Ipv4Addr::UNSPECIFIED)
}
Some(add) => {
add.ip()
}
};
Ok((size, addr))
}
// fn send_to(&self, buf: &[u8], addr: SocketAddrV4) -> io::Result<usize> {
// self.icmp_socket.send_to(buf, &SockAddr::from(addr))
// }
}
+77
View File
@@ -0,0 +1,77 @@
use std::{io, thread};
use std::collections::HashMap;
use std::net::{Ipv4Addr, SocketAddrV4};
use std::sync::Arc;
use crossbeam_utils::atomic::AtomicCell;
use crossbeam_skiplist::SkipMap;
use socket2::{SockAddr, Socket};
use tokio::net::{TcpListener, UdpSocket};
use crate::channel::sender::ChannelSender;
use crate::handle::CurrentDeviceInfo;
use crate::ip_proxy::icmp_proxy::IcmpProxy;
use crate::ip_proxy::tcp_proxy::TcpProxy;
use crate::ip_proxy::udp_proxy::UdpProxy;
pub mod icmp_proxy;
pub mod tcp_proxy;
pub mod udp_proxy;
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Debug)]
pub enum Protocol {
Icmp,
Tcp,
Udp,
}
#[derive(Clone)]
pub struct IpProxyMap {
pub(crate) tcp_proxy_port: u16,
pub(crate) udp_proxy_port: u16,
//真实源地址 -> (绑定地址,目的地址)
pub(crate) tcp_proxy_map: Arc<SkipMap<SocketAddrV4, (SocketAddrV4, SocketAddrV4)>>,
pub(crate) udp_proxy_map: Arc<SkipMap<SocketAddrV4, (SocketAddrV4, SocketAddrV4)>>,
// icmp用Identifier来区分,没有Identifier的一律不转发
pub(crate) icmp_proxy_map: Arc<SkipMap<(Ipv4Addr, u16, u16), Ipv4Addr>>,
icmp_sockets: HashMap<Ipv4Addr, Arc<Socket>>,
}
impl IpProxyMap {
pub fn send_icmp(&self, buf: &[u8], src: &Ipv4Addr, dest: &Ipv4Addr) -> io::Result<usize> {
if let Some(socket) = self.icmp_sockets.get(src) {
socket.send_to(buf, &SockAddr::from(SocketAddrV4::new(*dest, 0)))
} else {
Err(io::Error::new(io::ErrorKind::Other, format!("not found src:{},dest:{}", src, dest)))
}
}
}
pub async fn init_proxy(sender: ChannelSender, bind_ips: Vec<Ipv4Addr>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) -> io::Result<(TcpProxy, UdpProxy, IpProxyMap)> {
let mut icmp_sockets = HashMap::new();
let tcp_proxy_map: Arc<SkipMap<SocketAddrV4, (SocketAddrV4, SocketAddrV4)>> = Arc::new(SkipMap::new());
let udp_proxy_map: Arc<SkipMap<SocketAddrV4, (SocketAddrV4, SocketAddrV4)>> = Arc::new(SkipMap::new());
let icmp_proxy_map: Arc<SkipMap<(Ipv4Addr, u16, u16), Ipv4Addr>> = Arc::new(SkipMap::new());
let tcp_listener = TcpListener::bind("0.0.0.0:0").await?;
let udp_socket = UdpSocket::bind("0.0.0.0:0").await?;
let tcp_proxy_port = tcp_listener.local_addr()?.port();
let udp_proxy_port = udp_socket.local_addr()?.port();
let tcp_proxy = TcpProxy::new(tcp_listener, tcp_proxy_map.clone());
let udp_proxy = UdpProxy::new(udp_socket, udp_proxy_map.clone());
for ip in bind_ips {
let addr = SocketAddrV4::new(ip, 0);
let icmp_proxy_map = icmp_proxy_map.clone();
let icmp_proxy = IcmpProxy::new(addr, icmp_proxy_map, sender.clone(), current_device.clone())?;
icmp_sockets.insert(ip, icmp_proxy.icmp_socket());
thread::spawn(move || {
icmp_proxy.start();
});
}
Ok((tcp_proxy, udp_proxy, IpProxyMap {
tcp_proxy_port,
udp_proxy_port,
tcp_proxy_map,
udp_proxy_map,
icmp_proxy_map,
icmp_sockets,
}))
}
+71
View File
@@ -0,0 +1,71 @@
use std::io;
use std::net::{SocketAddr, SocketAddrV4};
use std::sync::Arc;
use crossbeam_skiplist::SkipMap;
use tokio::net::{TcpListener, TcpStream};
pub struct TcpProxy {
tcp_listener: TcpListener,
map: Arc<SkipMap<SocketAddrV4, (SocketAddrV4, SocketAddrV4)>>,
}
impl TcpProxy {
pub fn new(tcp_listener: TcpListener, map: Arc<SkipMap<SocketAddrV4, (SocketAddrV4, SocketAddrV4)>>) -> Self {
Self {
tcp_listener,
map,
}
}
pub async fn start(self) {
let tcp_listener = self.tcp_listener;
let map = self.map;
loop {
match tcp_listener.accept().await {
Ok((tcp_stream, sender_addr)) => {
match sender_addr {
SocketAddr::V4(sender_addr) => {
if let Some(entry) = map.get(&sender_addr) {
let (src_addr, dest_addr) = *entry.value();
let peer_tcp_stream = match TcpStream::connect(dest_addr).await {
Ok(peer_tcp_stream) => {peer_tcp_stream}
Err(e) => {
log::warn!("tcp代理异常:{:?},来源:{},目标:{}",e,src_addr,dest_addr);
continue;
}
};
let map = map.clone();
tokio::spawn(async move {
match proxy(tcp_stream, peer_tcp_stream).await {
Ok(_) => {}
Err(e) => {
log::warn!("tcp代理异常:{:?},来源:{},目标:{}",e,src_addr,dest_addr);
}
}
map.remove(&sender_addr);
});
}
}
SocketAddr::V6(_) => {}
}
}
Err(e) => {
log::warn!("tcp代理监听:{:?}",e);
}
}
}
}
}
async fn proxy(mut client: TcpStream, mut server: TcpStream) -> io::Result<()> {
let (mut client_reader, mut client_writer) = client.split();
let (mut server_reader, mut server_writer) = server.split();
let client_to_server = tokio::io::copy(&mut client_reader, &mut server_writer);
let server_to_client = tokio::io::copy(&mut server_reader, &mut client_writer);
tokio::try_join!(client_to_server, server_to_client)?;
Ok(())
}
+97
View File
@@ -0,0 +1,97 @@
use std::io;
use std::net::{SocketAddr, SocketAddrV4};
use std::sync::Arc;
use std::time::Duration;
use crossbeam_skiplist::SkipMap;
use tokio::net::UdpSocket;
/// 一个udp代理,作用是利用系统协议栈,将udp数据报解析出来再转发到目的地址
pub struct UdpProxy {
udp_socket: Arc<UdpSocket>,
map: Arc<SkipMap<SocketAddrV4, (SocketAddrV4, SocketAddrV4)>>,
}
impl UdpProxy {
pub fn new(udp_socket: UdpSocket, map: Arc<SkipMap<SocketAddrV4, (SocketAddrV4, SocketAddrV4)>>) -> Self {
let udp_socket = Arc::new(udp_socket);
Self {
udp_socket,
map,
}
}
pub async fn start(self) {
let map = self.map;
let udp_socket = self.udp_socket;
let mut buf = [0u8; 65536];
let inner_map: Arc<SkipMap<SocketAddrV4, Arc<UdpSocket>>> = Arc::new(SkipMap::new());
loop {
match udp_socket.recv_from(&mut buf).await {
Ok((len, sender_addr)) => {
match sender_addr {
SocketAddr::V4(sender_addr) => {
match start0(&buf[..len], sender_addr, &inner_map, &map, &udp_socket).await {
Ok(_) => {}
Err(e) => {
log::warn!("udp代理异常:{:?},来源:{}",e,sender_addr);
}
}
}
SocketAddr::V6(_) => {}
}
}
Err(e) => {
log::warn!("udp代理异常:{:?}",e);
}
};
}
}
}
async fn start0(buf: &[u8], sender_addr: SocketAddrV4, inner_map: &Arc<SkipMap<SocketAddrV4, Arc<UdpSocket>>>, map: &Arc<SkipMap<SocketAddrV4, (SocketAddrV4, SocketAddrV4)>>, udp_socket: &Arc<UdpSocket>) -> io::Result<()> {
if let Some(entry) = inner_map.get(&sender_addr) {
entry.value().send(buf).await?;
} else if let Some(entry) = map.get(&sender_addr) {
let (src_addr, dest_addr) = *entry.value();
let peer_udp_socket = UdpSocket::bind("0.0.0.0:0").await?;
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();
inner_map.insert(sender_addr, peer_udp_socket.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(300), 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代理异常:{:?},来源:{},目标:{}",e,src_addr,dest_addr);
break;
}
}
}
Err(e) => {
log::warn!("udp代理异常:{:?},来源:{},目标:{}",e,src_addr,dest_addr);
break;
}
}
}
Err(_) => {
//超时关闭
log::warn!("udp代理超时关闭,来源:{},目标:{}",src_addr,dest_addr);
break;
}
}
}
inner_map.remove(&sender_addr);
map.remove(&sender_addr);
});
}
Ok(())
}
+7 -4
View File
@@ -1,8 +1,5 @@
use crate::error::Error;
pub use p2p_channel::channel::{Route, RouteKey};
pub type Result<T> = std::result::Result<T, Error>;
pub mod error;
@@ -10,5 +7,11 @@ pub mod handle;
pub mod nat;
pub mod proto;
pub mod protocol;
pub mod tun_device;
pub mod ip_proxy;
pub mod external_route;
pub mod igmp_server;
pub mod tun_tap_device;
pub mod core;
pub mod channel;
pub mod util;
pub mod cipher;
@@ -2,24 +2,9 @@ use std::collections::HashSet;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
use std::time::Duration;
use std::{io, thread};
use p2p_channel::punch::NatType;
use crate::channel::punch::NatType;
// #[derive(Debug, Copy, Clone, PartialEq)]
// pub enum NatType {
// Symmetric,
// Cone,
// }
//
// impl Into<u8> for NatType {
// fn into(self) -> u8 {
// match self {
// NatType::Symmetric => 0,
// NatType::Cone => 1,
// }
// }
// }
/// 返回所有公网ip和端口变化范围
pub fn public_ip_list(addrs: &Vec<SocketAddr>) -> io::Result<(NatType, Vec<Ipv4Addr>, u16)> {
let mut hash_set = HashSet::new();
@@ -73,7 +58,6 @@ pub fn public_ip_list_(
udp: &UdpSocket,
addrs: &Vec<SocketAddr>,
) -> io::Result<(HashSet<Ipv4Addr>, u16, u16)> {
// println!("local port {:?}", udp.local_addr().unwrap().port());
udp.set_read_timeout(Some(Duration::from_millis(300)))?;
let mut buf = [0u8; 128];
for addr in addrs {
+135
View File
@@ -0,0 +1,135 @@
use crate::proto::message::PunchNatType;
use parking_lot::Mutex;
use std::io;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::sync::Arc;
pub mod check;
use std::net::UdpSocket;
use crate::channel::punch::{NatInfo, NatType};
pub fn local_ip() -> io::Result<Ipv4Addr> {
let socket = UdpSocket::bind("0.0.0.0:0")?;
socket.connect("8.8.8.8:80")?;
let addr = socket.local_addr()?;
match addr.ip() {
IpAddr::V4(ip) => {
Ok(ip)
}
IpAddr::V6(_) => {
Ok(Ipv4Addr::UNSPECIFIED)
}
}
}
#[derive(Clone)]
pub struct NatTest {
nat_test_server: Arc<Vec<SocketAddr>>,
info: Arc<Mutex<NatInfo>>,
}
impl From<NatType> for PunchNatType {
fn from(value: NatType) -> Self {
match value {
NatType::Symmetric => PunchNatType::Symmetric,
NatType::Cone => PunchNatType::Cone,
}
}
}
impl Into<NatType> for PunchNatType {
fn into(self) -> NatType {
match self {
PunchNatType::Symmetric => NatType::Symmetric,
PunchNatType::Cone => NatType::Cone,
}
}
}
impl NatTest {
pub fn new(
nat_test_server: Vec<SocketAddr>,
public_ip: Ipv4Addr,
public_port: u16,
local_ip: Ipv4Addr,
local_port: u16,
) -> NatTest {
let info = NatTest::re_test_(
&nat_test_server,
public_ip,
public_port,
local_ip,
local_port,
);
NatTest {
nat_test_server: Arc::new(nat_test_server),
info: Arc::new(Mutex::new(info)),
}
}
pub fn nat_info(&self) -> NatInfo {
self.info.lock().clone()
}
pub fn update_addr(&self, ip: Ipv4Addr, port: u16) {
let mut guard = self.info.lock();
guard.public_port = port;
if !guard.public_ips.contains(&ip) {
guard.public_ips.push(ip);
}
}
pub fn re_test(
&self,
public_ip: Ipv4Addr,
public_port: u16,
local_ip: Ipv4Addr,
local_port: u16,
) -> NatInfo {
let info = NatTest::re_test_(
&self.nat_test_server,
public_ip,
public_port,
local_ip,
local_port,
);
*self.info.lock() = info.clone();
info
}
fn re_test_(
nat_test_server: &Vec<SocketAddr>,
public_ip: Ipv4Addr,
public_port: u16,
local_ip: Ipv4Addr,
local_port: u16,
) -> NatInfo {
return match check::public_ip_list(nat_test_server) {
Ok((nat_type, ips, port_range)) => {
let mut public_ips = Vec::new();
public_ips.push(Ipv4Addr::from(public_ip));
for ip in ips {
if ip != public_ip {
public_ips.push(ip);
}
}
NatInfo::new(
public_ips,
public_port,
port_range,
local_ip,
local_port,
nat_type,
)
}
Err(e) => {
log::warn!("{:?}", e);
NatInfo::new(
vec![public_ip],
public_port,
0,
local_ip,
local_port,
NatType::Cone,
)
}
};
}
}
@@ -37,6 +37,12 @@ pub struct RegistrationRequest {
pub name: ::std::string::String,
// @@protoc_insertion_point(field:RegistrationRequest.is_fast)
pub is_fast: bool,
// @@protoc_insertion_point(field:RegistrationRequest.version)
pub version: ::std::string::String,
// @@protoc_insertion_point(field:RegistrationRequest.virtual_ip)
pub virtual_ip: u32,
// @@protoc_insertion_point(field:RegistrationRequest.allow_ip_change)
pub allow_ip_change: bool,
// special fields
// @@protoc_insertion_point(special_field:RegistrationRequest.special_fields)
pub special_fields: ::protobuf::SpecialFields,
@@ -54,7 +60,7 @@ impl RegistrationRequest {
}
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
let mut fields = ::std::vec::Vec::with_capacity(4);
let mut fields = ::std::vec::Vec::with_capacity(7);
let mut oneofs = ::std::vec::Vec::with_capacity(0);
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
"token",
@@ -76,6 +82,21 @@ impl RegistrationRequest {
|m: &RegistrationRequest| { &m.is_fast },
|m: &mut RegistrationRequest| { &mut m.is_fast },
));
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
"version",
|m: &RegistrationRequest| { &m.version },
|m: &mut RegistrationRequest| { &mut m.version },
));
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
"virtual_ip",
|m: &RegistrationRequest| { &m.virtual_ip },
|m: &mut RegistrationRequest| { &mut m.virtual_ip },
));
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
"allow_ip_change",
|m: &RegistrationRequest| { &m.allow_ip_change },
|m: &mut RegistrationRequest| { &mut m.allow_ip_change },
));
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<RegistrationRequest>(
"RegistrationRequest",
fields,
@@ -106,6 +127,15 @@ impl ::protobuf::Message for RegistrationRequest {
32 => {
self.is_fast = is.read_bool()?;
},
42 => {
self.version = is.read_string()?;
},
53 => {
self.virtual_ip = is.read_fixed32()?;
},
56 => {
self.allow_ip_change = is.read_bool()?;
},
tag => {
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
},
@@ -130,6 +160,15 @@ impl ::protobuf::Message for RegistrationRequest {
if self.is_fast != false {
my_size += 1 + 1;
}
if !self.version.is_empty() {
my_size += ::protobuf::rt::string_size(5, &self.version);
}
if self.virtual_ip != 0 {
my_size += 1 + 4;
}
if self.allow_ip_change != false {
my_size += 1 + 1;
}
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
self.special_fields.cached_size().set(my_size as u32);
my_size
@@ -148,6 +187,15 @@ impl ::protobuf::Message for RegistrationRequest {
if self.is_fast != false {
os.write_bool(4, self.is_fast)?;
}
if !self.version.is_empty() {
os.write_string(5, &self.version)?;
}
if self.virtual_ip != 0 {
os.write_fixed32(6, self.virtual_ip)?;
}
if self.allow_ip_change != false {
os.write_bool(7, self.allow_ip_change)?;
}
os.write_unknown_fields(self.special_fields.unknown_fields())?;
::std::result::Result::Ok(())
}
@@ -169,6 +217,9 @@ impl ::protobuf::Message for RegistrationRequest {
self.device_id.clear();
self.name.clear();
self.is_fast = false;
self.version.clear();
self.virtual_ip = 0;
self.allow_ip_change = false;
self.special_fields.clear();
}
@@ -178,6 +229,9 @@ impl ::protobuf::Message for RegistrationRequest {
device_id: ::std::string::String::new(),
name: ::std::string::String::new(),
is_fast: false,
version: ::std::string::String::new(),
virtual_ip: 0,
allow_ip_change: false,
special_fields: ::protobuf::SpecialFields::new(),
};
&instance
@@ -219,6 +273,8 @@ pub struct RegistrationResponse {
pub public_ip: u32,
// @@protoc_insertion_point(field:RegistrationResponse.public_port)
pub public_port: u32,
// @@protoc_insertion_point(field:RegistrationResponse.public_ipv6)
pub public_ipv6: ::std::vec::Vec<u8>,
// special fields
// @@protoc_insertion_point(special_field:RegistrationResponse.special_fields)
pub special_fields: ::protobuf::SpecialFields,
@@ -236,7 +292,7 @@ impl RegistrationResponse {
}
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
let mut fields = ::std::vec::Vec::with_capacity(7);
let mut fields = ::std::vec::Vec::with_capacity(8);
let mut oneofs = ::std::vec::Vec::with_capacity(0);
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
"virtual_ip",
@@ -273,6 +329,11 @@ impl RegistrationResponse {
|m: &RegistrationResponse| { &m.public_port },
|m: &mut RegistrationResponse| { &mut m.public_port },
));
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
"public_ipv6",
|m: &RegistrationResponse| { &m.public_ipv6 },
|m: &mut RegistrationResponse| { &mut m.public_ipv6 },
));
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<RegistrationResponse>(
"RegistrationResponse",
fields,
@@ -312,6 +373,9 @@ impl ::protobuf::Message for RegistrationResponse {
56 => {
self.public_port = is.read_uint32()?;
},
66 => {
self.public_ipv6 = is.read_bytes()?;
},
tag => {
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
},
@@ -346,6 +410,9 @@ impl ::protobuf::Message for RegistrationResponse {
if self.public_port != 0 {
my_size += ::protobuf::rt::uint32_size(7, self.public_port);
}
if !self.public_ipv6.is_empty() {
my_size += ::protobuf::rt::bytes_size(8, &self.public_ipv6);
}
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
self.special_fields.cached_size().set(my_size as u32);
my_size
@@ -373,6 +440,9 @@ impl ::protobuf::Message for RegistrationResponse {
if self.public_port != 0 {
os.write_uint32(7, self.public_port)?;
}
if !self.public_ipv6.is_empty() {
os.write_bytes(8, &self.public_ipv6)?;
}
os.write_unknown_fields(self.special_fields.unknown_fields())?;
::std::result::Result::Ok(())
}
@@ -397,6 +467,7 @@ impl ::protobuf::Message for RegistrationResponse {
self.device_info_list.clear();
self.public_ip = 0;
self.public_port = 0;
self.public_ipv6.clear();
self.special_fields.clear();
}
@@ -409,6 +480,7 @@ impl ::protobuf::Message for RegistrationResponse {
device_info_list: ::std::vec::Vec::new(),
public_ip: 0,
public_port: 0,
public_ipv6: ::std::vec::Vec::new(),
special_fields: ::protobuf::SpecialFields::new(),
};
&instance
@@ -749,6 +821,8 @@ pub struct PunchInfo {
pub local_ip: u32,
// @@protoc_insertion_point(field:PunchInfo.local_port)
pub local_port: u32,
// @@protoc_insertion_point(field:PunchInfo.public_ipv6_list)
pub public_ipv6_list: ::std::vec::Vec<::std::vec::Vec<u8>>,
// special fields
// @@protoc_insertion_point(special_field:PunchInfo.special_fields)
pub special_fields: ::protobuf::SpecialFields,
@@ -766,7 +840,7 @@ impl PunchInfo {
}
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
let mut fields = ::std::vec::Vec::with_capacity(7);
let mut fields = ::std::vec::Vec::with_capacity(8);
let mut oneofs = ::std::vec::Vec::with_capacity(0);
fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>(
"public_ip_list",
@@ -803,6 +877,11 @@ impl PunchInfo {
|m: &PunchInfo| { &m.local_port },
|m: &mut PunchInfo| { &mut m.local_port },
));
fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>(
"public_ipv6_list",
|m: &PunchInfo| { &m.public_ipv6_list },
|m: &mut PunchInfo| { &mut m.public_ipv6_list },
));
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<PunchInfo>(
"PunchInfo",
fields,
@@ -845,6 +924,9 @@ impl ::protobuf::Message for PunchInfo {
64 => {
self.local_port = is.read_uint32()?;
},
74 => {
self.public_ipv6_list.push(is.read_bytes()?);
},
tag => {
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
},
@@ -876,6 +958,9 @@ impl ::protobuf::Message for PunchInfo {
if self.local_port != 0 {
my_size += ::protobuf::rt::uint32_size(8, self.local_port);
}
for value in &self.public_ipv6_list {
my_size += ::protobuf::rt::bytes_size(9, &value);
};
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
self.special_fields.cached_size().set(my_size as u32);
my_size
@@ -903,6 +988,9 @@ impl ::protobuf::Message for PunchInfo {
if self.local_port != 0 {
os.write_uint32(8, self.local_port)?;
}
for v in &self.public_ipv6_list {
os.write_bytes(9, &v)?;
};
os.write_unknown_fields(self.special_fields.unknown_fields())?;
::std::result::Result::Ok(())
}
@@ -927,6 +1015,7 @@ impl ::protobuf::Message for PunchInfo {
self.reply = false;
self.local_ip = 0;
self.local_port = 0;
self.public_ipv6_list.clear();
self.special_fields.clear();
}
@@ -939,6 +1028,7 @@ impl ::protobuf::Message for PunchInfo {
reply: false,
local_ip: 0,
local_port: 0,
public_ipv6_list: ::std::vec::Vec::new(),
special_fields: ::protobuf::SpecialFields::new(),
};
&instance
@@ -1017,28 +1107,33 @@ impl PunchNatType {
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\rmessage.proto\"u\n\x13RegistrationRequest\x12\x14\n\x05token\x18\x01\
\x20\x01(\tR\x05token\x12\x1b\n\tdevice_id\x18\x02\x20\x01(\tR\x08device\
Id\x12\x12\n\x04name\x18\x03\x20\x01(\tR\x04name\x12\x17\n\x07is_fast\
\x18\x04\x20\x01(\x08R\x06isFast\"\x92\x02\n\x14RegistrationResponse\x12\
\x1d\n\nvirtual_ip\x18\x01\x20\x01(\x07R\tvirtualIp\x12'\n\x0fvirtual_ga\
teway\x18\x02\x20\x01(\x07R\x0evirtualGateway\x12'\n\x0fvirtual_netmask\
\x18\x03\x20\x01(\x07R\x0evirtualNetmask\x12\x14\n\x05epoch\x18\x04\x20\
\x01(\rR\x05epoch\x125\n\x10device_info_list\x18\x05\x20\x03(\x0b2\x0b.D\
eviceInfoR\x0edeviceInfoList\x12\x1b\n\tpublic_ip\x18\x06\x20\x01(\x07R\
\x08publicIp\x12\x1f\n\x0bpublic_port\x18\x07\x20\x01(\rR\npublicPort\"d\
\n\nDeviceInfo\x12\x12\n\x04name\x18\x01\x20\x01(\tR\x04name\x12\x1d\n\n\
virtual_ip\x18\x02\x20\x01(\x07R\tvirtualIp\x12#\n\rdevice_status\x18\
\x03\x20\x01(\rR\x0cdeviceStatus\"Y\n\nDeviceList\x12\x14\n\x05epoch\x18\
\x01\x20\x01(\rR\x05epoch\x125\n\x10device_info_list\x18\x02\x20\x03(\
\x0b2\x0b.DeviceInfoR\x0edeviceInfoList\"\xf8\x01\n\tPunchInfo\x12$\n\
\x0epublic_ip_list\x18\x02\x20\x03(\x07R\x0cpublicIpList\x12\x1f\n\x0bpu\
blic_port\x18\x03\x20\x01(\rR\npublicPort\x12*\n\x11public_port_range\
\x18\x04\x20\x01(\rR\x0fpublicPortRange\x12(\n\x08nat_type\x18\x05\x20\
\x01(\x0e2\r.PunchNatTypeR\x07natType\x12\x14\n\x05reply\x18\x06\x20\x01\
(\x08R\x05reply\x12\x19\n\x08local_ip\x18\x07\x20\x01(\x07R\x07localIp\
\x12\x1d\n\nlocal_port\x18\x08\x20\x01(\rR\tlocalPort*'\n\x0cPunchNatTyp\
e\x12\r\n\tSymmetric\x10\0\x12\x08\n\x04Cone\x10\x01b\x06proto3\
\n\rmessage.proto\"\xd6\x01\n\x13RegistrationRequest\x12\x14\n\x05token\
\x18\x01\x20\x01(\tR\x05token\x12\x1b\n\tdevice_id\x18\x02\x20\x01(\tR\
\x08deviceId\x12\x12\n\x04name\x18\x03\x20\x01(\tR\x04name\x12\x17\n\x07\
is_fast\x18\x04\x20\x01(\x08R\x06isFast\x12\x18\n\x07version\x18\x05\x20\
\x01(\tR\x07version\x12\x1d\n\nvirtual_ip\x18\x06\x20\x01(\x07R\tvirtual\
Ip\x12&\n\x0fallow_ip_change\x18\x07\x20\x01(\x08R\rallowIpChange\"\xb3\
\x02\n\x14RegistrationResponse\x12\x1d\n\nvirtual_ip\x18\x01\x20\x01(\
\x07R\tvirtualIp\x12'\n\x0fvirtual_gateway\x18\x02\x20\x01(\x07R\x0evirt\
ualGateway\x12'\n\x0fvirtual_netmask\x18\x03\x20\x01(\x07R\x0evirtualNet\
mask\x12\x14\n\x05epoch\x18\x04\x20\x01(\rR\x05epoch\x125\n\x10device_in\
fo_list\x18\x05\x20\x03(\x0b2\x0b.DeviceInfoR\x0edeviceInfoList\x12\x1b\
\n\tpublic_ip\x18\x06\x20\x01(\x07R\x08publicIp\x12\x1f\n\x0bpublic_port\
\x18\x07\x20\x01(\rR\npublicPort\x12\x1f\n\x0bpublic_ipv6\x18\x08\x20\
\x01(\x0cR\npublicIpv6\"d\n\nDeviceInfo\x12\x12\n\x04name\x18\x01\x20\
\x01(\tR\x04name\x12\x1d\n\nvirtual_ip\x18\x02\x20\x01(\x07R\tvirtualIp\
\x12#\n\rdevice_status\x18\x03\x20\x01(\rR\x0cdeviceStatus\"Y\n\nDeviceL\
ist\x12\x14\n\x05epoch\x18\x01\x20\x01(\rR\x05epoch\x125\n\x10device_inf\
o_list\x18\x02\x20\x03(\x0b2\x0b.DeviceInfoR\x0edeviceInfoList\"\xa2\x02\
\n\tPunchInfo\x12$\n\x0epublic_ip_list\x18\x02\x20\x03(\x07R\x0cpublicIp\
List\x12\x1f\n\x0bpublic_port\x18\x03\x20\x01(\rR\npublicPort\x12*\n\x11\
public_port_range\x18\x04\x20\x01(\rR\x0fpublicPortRange\x12(\n\x08nat_t\
ype\x18\x05\x20\x01(\x0e2\r.PunchNatTypeR\x07natType\x12\x14\n\x05reply\
\x18\x06\x20\x01(\x08R\x05reply\x12\x19\n\x08local_ip\x18\x07\x20\x01(\
\x07R\x07localIp\x12\x1d\n\nlocal_port\x18\x08\x20\x01(\rR\tlocalPort\
\x12(\n\x10public_ipv6_list\x18\t\x20\x03(\x0cR\x0epublicIpv6List*'\n\
\x0cPunchNatType\x12\r\n\tSymmetric\x10\0\x12\x08\n\x04Cone\x10\x01b\x06\
proto3\
";
/// `FileDescriptorProto` object which was a source for this generated file
@@ -1,5 +1,5 @@
use std::{fmt, io};
use std::net::Ipv4Addr;
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub enum Protocol {
@@ -18,7 +18,10 @@ pub enum Protocol {
PunchRequest,
/// 打洞响应
PunchResponse,
UnKnow(u8),
///获取对端看到的地址
AddrRequest,
AddrResponse,
Unknown(u8),
}
impl From<u8> for Protocol {
@@ -28,7 +31,9 @@ impl From<u8> for Protocol {
2 => Protocol::Pong,
3 => Protocol::PunchRequest,
4 => Protocol::PunchResponse,
val => Protocol::UnKnow(val),
5 => Protocol::AddrRequest,
6 => Protocol::AddrResponse,
val => Protocol::Unknown(val),
}
}
}
@@ -40,7 +45,9 @@ impl Into<u8> for Protocol {
Protocol::Pong => 2,
Protocol::PunchRequest => 3,
Protocol::PunchResponse => 4,
Protocol::UnKnow(val) => val,
Protocol::AddrRequest => 5,
Protocol::AddrResponse => 6,
Protocol::Unknown(val) => val,
}
}
}
@@ -50,6 +57,8 @@ pub enum ControlPacket<B> {
PongPacket(PongPacket<B>),
PunchRequest,
PunchResponse,
AddrRequest,
AddrResponse(AddrPacket<B>),
}
impl<B: AsRef<[u8]>> ControlPacket<B> {
@@ -59,18 +68,19 @@ impl<B: AsRef<[u8]>> ControlPacket<B> {
Protocol::Pong => Ok(ControlPacket::PongPacket(PongPacket::new(buffer)?)),
Protocol::PunchRequest => Ok(ControlPacket::PunchRequest),
Protocol::PunchResponse => Ok(ControlPacket::PunchResponse),
Protocol::UnKnow(_) => Err(io::Error::new(io::ErrorKind::InvalidData, "Unsupported")),
Protocol::AddrRequest => Ok(ControlPacket::AddrRequest),
Protocol::AddrResponse => Ok(ControlPacket::AddrResponse(AddrPacket::new(buffer)?)),
Protocol::Unknown(_) => Err(io::Error::new(io::ErrorKind::InvalidData, "Unsupported")),
}
}
}
/// 网络探针
#[derive(Copy, Clone)]
pub struct PingPacket<B> {
buffer: B,
}
type PongPacket<B> = PingPacket<B>;
pub type PongPacket<B> = PingPacket<B>;
impl<B: AsRef<[u8]>> PingPacket<B> {
pub fn new(buffer: B) -> io::Result<PingPacket<B>> {
@@ -107,4 +117,43 @@ impl<B: AsRef<[u8]>> fmt::Debug for PingPacket<B> {
.field("epoch", &self.epoch())
.finish()
}
}
}
pub struct AddrPacket<B> {
buffer: B,
}
impl<B: AsRef<[u8]>> AddrPacket<B> {
pub fn new(buffer: B) -> io::Result<AddrPacket<B>> {
let len = buffer.as_ref().len();
if len != 6 {
return Err(io::Error::new(io::ErrorKind::InvalidData, "len != 6"));
}
Ok(AddrPacket { buffer })
}
pub fn ipv4(&self) -> Ipv4Addr {
let buf = self.buffer.as_ref();
Ipv4Addr::new(buf[0], buf[1], buf[2], buf[3])
}
pub fn port(&self) -> u16 {
u16::from_be_bytes(self.buffer.as_ref()[4..6].try_into().unwrap())
}
}
impl<B: AsRef<[u8]> + AsMut<[u8]>> AddrPacket<B> {
pub fn set_ipv4(&mut self, ip: Ipv4Addr) {
self.buffer.as_mut()[..4].copy_from_slice(&ip.octets())
}
pub fn set_port(&mut self, port: u16) {
self.buffer.as_mut()[4..6].copy_from_slice(&port.to_be_bytes())
}
}
impl<B: AsRef<[u8]>> fmt::Debug for AddrPacket<B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AddrPacket")
.field("ipv4", &self.ipv4())
.field("port", &self.port())
.finish()
}
}
@@ -5,6 +5,8 @@ pub enum Protocol {
TokenError,
Disconnect,
AddressExhausted,
IpAlreadyExists,
InvalidIp,
Other(u8),
}
@@ -14,6 +16,8 @@ impl From<u8> for Protocol {
1 => Self::TokenError,
2 => Self::Disconnect,
3 => Self::AddressExhausted,
4 => Self::IpAlreadyExists,
5 => Self::InvalidIp,
val => Self::Other(val),
}
}
@@ -25,6 +29,8 @@ impl Into<u8> for Protocol {
Protocol::TokenError => 1,
Protocol::Disconnect => 2,
Protocol::AddressExhausted => 3,
Protocol::IpAlreadyExists => 4,
Protocol::InvalidIp => 5,
Protocol::Other(val) => val,
}
}
@@ -34,6 +40,8 @@ pub enum InErrorPacket<B> {
TokenError,
Disconnect,
AddressExhausted,
IpAlreadyExists,
InvalidIp,
OtherError(ErrorPacket<B>),
}
@@ -43,6 +51,8 @@ impl<B: AsRef<[u8]>> InErrorPacket<B> {
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::Other(_) => Ok(InErrorPacket::OtherError(ErrorPacket::new(buffer)?)),
}
}
+105
View File
@@ -0,0 +1,105 @@
use std::io;
use std::net::Ipv4Addr;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Protocol {
Icmp,
Igmp,
Ipv4,
Ipv4Broadcast,
Unknown(u8),
}
impl From<u8> for Protocol {
fn from(value: u8) -> Self {
match value {
1 => Protocol::Icmp,
2 => Protocol::Igmp,
4 => Protocol::Ipv4,
201 => Protocol::Ipv4Broadcast,
val => Protocol::Unknown(val),
}
}
}
impl Into<u8> for Protocol {
fn into(self) -> u8 {
match self {
Protocol::Icmp => 1,
Protocol::Igmp => 2,
Protocol::Ipv4 => 4,
Protocol::Ipv4Broadcast => 201,
Protocol::Unknown(val) => val,
}
}
}
pub struct BroadcastPacketEnd<B> {
buffer: B,
}
impl<B: AsRef<[u8]>> BroadcastPacketEnd<B> {
pub fn unchecked(buffer: B) -> Self {
Self { buffer }
}
pub fn new(buffer: B) -> io::Result<Self> {
let len = buffer.as_ref().len();
let packet = Self::unchecked(buffer);
if len < 1 || packet.len() != len {
Err(io::Error::new(
io::ErrorKind::InvalidData,
"InvalidData",
))
} else {
Ok(packet)
}
}
}
impl<B: AsRef<[u8]>> BroadcastPacketEnd<B> {
pub fn len(&self) -> usize {
1 + self.num() as usize * 4
}
pub fn num(&self) -> u8 {
let len = self.buffer.as_ref().len();
self.buffer.as_ref()[len - 1]
}
/// 已经发送给了这些地址
/// 从尾往头拿
pub fn addresses(&self) -> Vec<Ipv4Addr> {
let num = self.num() as usize;
let mut list = Vec::with_capacity(num);
let buf = self.buffer.as_ref();
let mut offset = buf.len() + 4 - 2;
for _ in 0..num {
offset -= 4;
list.push(Ipv4Addr::new(buf[offset - 3], buf[offset - 2], buf[offset - 1], buf[offset]));
}
list
}
}
impl<B: AsRef<[u8]> + AsMut<[u8]>> BroadcastPacketEnd<B> {
/// 从头往尾放
pub fn set_address(&mut self, addr: &[Ipv4Addr]) -> io::Result<()> {
let buf = self.buffer.as_mut();
if buf.len() < 1 + addr.len() * 4 || addr.len() > u8::MAX as usize {
Err(io::Error::new(
io::ErrorKind::InvalidData,
"InvalidData",
))
} else {
let mut offset = 0;
for ip in addr {
buf[offset..offset + 4].copy_from_slice(&ip.octets());
offset += 4;
}
self.buffer.as_mut()[offset] = addr.len() as u8;
Ok(())
}
}
}
@@ -1,24 +1,25 @@
use std::{fmt, io};
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| (8) | (8) | (8) | ttl(4) | (4) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ip地址(32) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ip地址(32) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| p|unused| (4) | (8) | (8) | ttl(4) | (4) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ip地址(32) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ip地址(32) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
pub mod control_packet;
pub mod error_packet;
pub mod service_packet;
pub mod turn_packet;
pub mod ip_turn_packet;
pub mod other_turn_packet;
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub enum Version {
@@ -52,8 +53,8 @@ pub enum Protocol {
Error,
/// 控制协议
Control,
/// 转发ipv4数据
Ipv4Turn,
/// 转发ip数据
IpTurn,
/// 转发其他数据
OtherTurn,
UnKnow(u8),
@@ -65,7 +66,7 @@ impl From<u8> for Protocol {
1 => Protocol::Service,
2 => Protocol::Error,
3 => Protocol::Control,
4 => Protocol::Ipv4Turn,
4 => Protocol::IpTurn,
5 => Protocol::OtherTurn,
val => Protocol::UnKnow(val),
}
@@ -78,7 +79,7 @@ impl Into<u8> for Protocol {
Protocol::Service => 1,
Protocol::Error => 2,
Protocol::Control => 3,
Protocol::Ipv4Turn => 4,
Protocol::IpTurn => 4,
Protocol::OtherTurn => 5,
Protocol::UnKnow(val) => val,
}
@@ -98,7 +99,10 @@ impl<B: AsRef<[u8]>> NetPacket<B> {
let len = buffer.as_ref().len();
// 不能大于udp最大载荷长度
if len < 12 || len > 65535 - 20 - 8 {
return Err(io::Error::new(io::ErrorKind::InvalidData, "length overflow"));
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"length overflow",
));
}
Ok(NetPacket { buffer })
}
@@ -111,8 +115,11 @@ impl<B: AsRef<[u8]>> NetPacket<B> {
}
impl<B: AsRef<[u8]>> NetPacket<B> {
pub fn is_encrypt(&self) -> bool {
self.buffer.as_ref()[0] & 0x80 == 0x80
}
pub fn version(&self) -> Version {
Version::from(self.buffer.as_ref()[0])
Version::from(self.buffer.as_ref()[0] & 0x0F)
}
pub fn protocol(&self) -> Protocol {
Protocol::from(self.buffer.as_ref()[1])
@@ -140,8 +147,19 @@ impl<B: AsRef<[u8]>> NetPacket<B> {
}
impl<B: AsRef<[u8]> + AsMut<[u8]>> NetPacket<B> {
pub fn buffer_mut(&mut self) -> &mut [u8] {
self.buffer.as_mut()
}
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_version(&mut self, version: Version) {
self.buffer.as_mut()[0] = version.into();
let v: u8 = version.into();
self.buffer.as_mut()[0] = (self.buffer.as_ref()[0] & 0xF0) | (0x0F & v);
}
pub fn set_protocol(&mut self, protocol: Protocol) {
self.buffer.as_mut()[1] = protocol.into();
@@ -1,16 +1,14 @@
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Protocol {
Punch,
UnKnow(u8),
Unknown(u8),
}
impl From<u8> for Protocol {
fn from(value: u8) -> Self {
match value {
1 => Protocol::Punch,
val => Protocol::UnKnow(val),
val => Protocol::Unknown(val),
}
}
}
@@ -19,7 +17,7 @@ impl Into<u8> for Protocol {
fn into(self) -> u8 {
match self {
Protocol::Punch => 1,
Protocol::UnKnow(val) => val,
Protocol::Unknown(val) => val,
}
}
}
@@ -8,7 +8,7 @@ pub enum Protocol {
PollDeviceList,
/// 推送设备列表
PushDeviceList,
UnKnow(u8),
Unknown(u8),
}
impl From<u8> for Protocol {
@@ -18,7 +18,7 @@ impl From<u8> for Protocol {
2 => Self::RegistrationResponse,
3 => Self::PollDeviceList,
4 => Self::PushDeviceList,
val => Self::UnKnow(val),
val => Self::Unknown(val),
}
}
}
@@ -30,7 +30,7 @@ impl Into<u8> for Protocol {
Self::RegistrationResponse => 2,
Self::PollDeviceList => 3,
Self::PushDeviceList => 4,
Self::UnKnow(val) => val,
Self::Unknown(val) => val,
}
}
}
+48
View File
@@ -0,0 +1,48 @@
use std::io;
use std::os::unix::io::RawFd;
#[derive(Clone)]
pub struct DeviceWriter(RawFd);
pub struct DeviceReader(RawFd);
impl DeviceWriter {
pub fn write_ipv4_tun(&self, buf: &[u8]) -> io::Result<()> {
unsafe {
let amount = libc::write(self.0, buf.as_ptr() as *const _, buf.len());
if amount < 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
}
///写入ipv4数据,为了兼容其他代码,头部空了14个字节
pub fn write_ipv4(&self, buf: &[u8]) -> io::Result<()> {
let buf = &buf[14..];
self.write_ipv4_tun(buf)
}
pub fn close(&self) -> io::Result<()> {
// unsafe {
// libc::close(self.0);
// }
Ok(())
}
}
impl DeviceReader {
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
unsafe {
let amount = libc::read(self.0, buf.as_mut_ptr() as *mut _, buf.len());
if amount < 0 {
return Err(io::Error::last_os_error());
}
Ok(amount as usize)
}
}
}
pub fn create(fd: i32) -> (DeviceWriter, DeviceReader) {
(DeviceWriter(fd as _), DeviceReader(fd as _))
}
+125
View File
@@ -0,0 +1,125 @@
use std::io;
use std::net::Ipv4Addr;
use crate::tun_tap_device::{DeviceReader, DeviceType, DeviceWriter, DriverInfo};
use tun::Device;
use parking_lot::Mutex;
use std::process::Command;
use std::sync::Arc;
use crate::tun_tap_device::linux_mac::DeviceW;
impl DeviceWriter {
pub fn change_ip(&self, address: Ipv4Addr, netmask: Ipv4Addr,
gateway: Ipv4Addr, _old_netmask: Ipv4Addr, _old_gateway: Ipv4Addr) -> io::Result<()> {
let mut config = tun::Configuration::default();
config
.destination(gateway)
.address(address)
.netmask(netmask)
// .queues(2)
.up();
let mut dev = self.lock.lock();
if let Err(e) = dev.configure(&config) {
return Err(io::Error::new(io::ErrorKind::Other, format!("{:?}", e)));
}
let name = dev.name();
for (address, netmask) in &self.in_ips {
add_route(name, *address, *netmask)?;
}
// 当前网段路由
// add_route(name, address, netmask)?;
// 广播和组播路由
add_route(name, Ipv4Addr::BROADCAST, Ipv4Addr::BROADCAST)?;
add_route(name, Ipv4Addr::from([224, 0, 0, 0]), Ipv4Addr::from([240, 0, 0, 0]))?;
return Ok(());
}
}
pub fn add_route(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> {
let route_add_str: String = format!(
"ip route add {:?}/{:?} dev {}",
address, netmask, name
);
let route_add_out = Command::new("sh")
.arg("-c")
.arg(&route_add_str)
.output()
.expect("sh exec error!");
if !route_add_out.status.success() {
return Err(io::Error::new(io::ErrorKind::Other, format!("添加路由失败: cmd:{},out:{:?}", route_add_str, route_add_out)));
}
Ok(())
}
pub fn create_device(device_type: DeviceType,
address: Ipv4Addr,
netmask: Ipv4Addr,
gateway: Ipv4Addr,
in_ips: Vec<(Ipv4Addr, Ipv4Addr)>,
mtu: u16,
) -> io::Result<(DeviceWriter, DeviceReader,DriverInfo)> {
let mut config = tun::Configuration::default();
config
.destination(gateway)
.address(address)
.netmask(netmask)
.mtu(mtu.into())
// .queues(2) 用多个队列有兼容性问题
.up();
match device_type {
DeviceType::Tun => {}
DeviceType::Tap => {
config.layer(tun::Layer::L2);
}
}
let dev = tun::create(&config).unwrap();
let packet_information = dev.has_packet_information();
let queue = dev.queue(0).unwrap();
let reader = queue.reader();
let writer = queue.writer();
let name = dev.name();
for (address, netmask) in &in_ips {
add_route(name, *address, *netmask)?;
}
// 当前网段路由
// add_route(name, address, netmask)?;
// 广播和组播路由
add_route(name, Ipv4Addr::BROADCAST, Ipv4Addr::BROADCAST)?;
add_route(name, Ipv4Addr::from([224, 0, 0, 0]), Ipv4Addr::from([240, 0, 0, 0]))?;
let device_w = match device_type {
DeviceType::Tun => {
DeviceW::Tun(writer)
}
DeviceType::Tap => {
let get_mac_cmd = format!("cat /sys/class/net/{}/address", name);
let mac_out = Command::new("sh")
.arg("-c")
.arg(get_mac_cmd)
.output()
.expect("sh exec error!");
if !mac_out.status.success() {
return Err(io::Error::new(io::ErrorKind::Other, format!("获取mac地址错误: {:?}", mac_out)));
}
let mac_str = String::from_utf8(mac_out.stdout).unwrap();
let mut mac = [0; 6];
let mut split = mac_str.split(":");
for i in 0..6 {
mac[i] = u8::from_str_radix(&split.next().unwrap()[..2], 16).unwrap();
}
DeviceW::Tap((writer, mac))
}
};
let driver_info = DriverInfo {
device_type,
name:name.to_string(),
version:String::new(),
mac: None,
};
Ok((
DeviceWriter::new(device_w, Arc::new(Mutex::new(dev)), in_ips, address, packet_information),
DeviceReader::new(reader),
driver_info,
))
}
pub fn delete_device(_device_type: DeviceType) {}
+137
View File
@@ -0,0 +1,137 @@
use std::io;
use std::sync::Arc;
use bytes::BufMut;
use tun::platform::posix::{Reader, Writer};
use std::net::Ipv4Addr;
use std::os::unix::io::AsRawFd;
#[cfg(any(target_os = "linux"))]
use tun::platform::linux::Device;
#[cfg(any(target_os = "macos"))]
use tun::platform::macos::Device;
use parking_lot::Mutex;
use packet::ethernet;
use packet::ethernet::packet::EthernetPacket;
#[derive(Clone)]
pub enum DeviceW {
Tun(Writer),
Tap((Writer, [u8; 6])),
}
impl DeviceW {
pub fn is_tun(&self) -> bool {
match self {
DeviceW::Tun(_) => {
true
}
DeviceW::Tap(_) => {
false
}
}
}
}
#[derive(Clone)]
pub struct DeviceWriter {
writer: DeviceW,
pub lock: Arc<Mutex<Device>>,
pub in_ips: Vec<(Ipv4Addr, Ipv4Addr)>,
packet_information: bool,
}
impl DeviceWriter {
pub fn new(writer: DeviceW,lock: Arc<Mutex<Device>>, in_ips: Vec<(Ipv4Addr, Ipv4Addr)>, _ip: Ipv4Addr, packet_information: bool) -> Self {
Self {
writer,
lock,
in_ips,
packet_information,
}
}
}
impl DeviceWriter {
pub fn write(packet_information: bool, writer: &Writer, packet: &[u8]) -> io::Result<()> {
if packet_information {
let mut buf = Vec::<u8>::with_capacity(4 + packet.len());
buf.put_u16(0);
#[cfg(any(target_os = "macos", target_os = "ios"))]
buf.put_u16(libc::PF_INET as u16);
#[cfg(any(target_os = "linux", target_os = "android"))]
buf.put_u16(libc::ETH_P_IP as u16);
buf.extend_from_slice(packet);
writer.write_all(&buf)
} else {
writer.write_all(packet)
}
}
///tun网卡写入ipv4数据
pub fn write_ipv4_tun(&self, buf: &[u8]) -> io::Result<()> {
match &self.writer {
DeviceW::Tun(writer) => {
Self::write(self.packet_information, writer, buf)
}
DeviceW::Tap(_) => {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
}
}
/// tap网卡写入以太网帧
pub fn write_ethernet_tap(&self, buf: &[u8]) -> io::Result<()> {
match &self.writer {
DeviceW::Tun(_) => {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
DeviceW::Tap((writer, _)) => {
Self::write(self.packet_information, writer, buf)
}
}
}
///写入ipv4数据,头部必须留14字节,给tap写入以太网帧头
pub fn write_ipv4(&self, buf: &mut [u8]) -> io::Result<()> {
match &self.writer {
DeviceW::Tun(writer) => {
Self::write(self.packet_information, writer, &buf[14..])
}
DeviceW::Tap((writer, mac)) => {
let source_mac = [buf[14 + 12], buf[14 + 13], buf[14 + 14], buf[14 + 15], !mac[5], 234];
let mut ethernet_packet = EthernetPacket::unchecked(buf);
ethernet_packet.set_source(&source_mac);
ethernet_packet.set_destination(mac);
ethernet_packet.set_protocol(ethernet::protocol::Protocol::Ipv4);
Self::write(self.packet_information, writer, &ethernet_packet.buffer)
}
}
}
pub fn close(&self) -> io::Result<()> {
unsafe {
match &self.writer {
DeviceW::Tun(writer) => {
libc::close(writer.as_raw_fd());
}
DeviceW::Tap((writer, _)) => {
libc::close(writer.as_raw_fd());
}
}
}
Ok(())
}
pub fn is_tun(&self) -> bool {
self.writer.is_tun()
}
}
pub struct DeviceReader(Reader);
impl DeviceReader {
pub fn new(device: Reader) -> Self {
DeviceReader(device)
}
}
impl DeviceReader {
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
}
}
+118
View File
@@ -0,0 +1,118 @@
use std::io;
use std::net::Ipv4Addr;
use crate::tun_tap_device::{DeviceReader, DeviceType, DeviceWriter, DriverInfo};
use tun::Device;
use parking_lot::Mutex;
use std::process::Command;
use std::sync::Arc;
use crate::tun_tap_device::linux_mac::DeviceW;
impl DeviceWriter {
pub fn change_ip(&self, address: Ipv4Addr, netmask: Ipv4Addr,
gateway: Ipv4Addr, _old_netmask: Ipv4Addr, _old_gateway: Ipv4Addr) -> io::Result<()> {
let mut config = tun::Configuration::default();
config
.destination(gateway)
.address(address)
.netmask(netmask)
.up();
let mut dev = self.lock.lock();
if let Err(e) = dev.configure(&config) {
return Err(io::Error::new(io::ErrorKind::Other, format!("{:?}", e)));
}
if let Err(e) = config_ip(dev.name(), address, netmask, gateway) {
log::error!("{}",e);
}
let name = dev.name();
for (address, netmask) in &self.in_ips {
add_route(name, *address, *netmask)?;
}
// 当前网段路由
add_route(name, address, netmask)?;
// 广播和组播路由
add_route(name, Ipv4Addr::BROADCAST, Ipv4Addr::BROADCAST)?;
add_route(name, Ipv4Addr::from([224, 0, 0, 0]), Ipv4Addr::from([240, 0, 0, 0]))?;
return Ok(());
}
}
pub fn create_device(device_type: DeviceType,
address: Ipv4Addr,
netmask: Ipv4Addr,
gateway: Ipv4Addr,
in_ips: Vec<(Ipv4Addr, Ipv4Addr)>,
mtu: u16,
) -> io::Result<(DeviceWriter, DeviceReader, DriverInfo)> {
match device_type {
DeviceType::Tun => {}
DeviceType::Tap => {
unimplemented!()
}
}
let mut config = tun::Configuration::default();
config
.destination(gateway)
.address(address)
.netmask(netmask)
.mtu(mtu.into())
.up();
let dev = tun::create(&config).unwrap();
let name = dev.name();
config_ip(name, address, netmask, gateway)?;
for (address, netmask) in &in_ips {
add_route(name, *address, *netmask)?;
}
// 当前网段路由
add_route(name, address, netmask)?;
// 广播和组播路由
add_route(name, Ipv4Addr::BROADCAST, Ipv4Addr::BROADCAST)?;
add_route(name, Ipv4Addr::from([224, 0, 0, 0]), Ipv4Addr::from([240, 0, 0, 0]))?;
let packet_information = dev.has_packet_information();
let queue = dev.queue(0).unwrap();
let reader = queue.reader();
let writer = queue.writer();
let driver_info = DriverInfo {
device_type,
name: name.to_string(),
version: String::new(),
mac: None,
};
Ok((
DeviceWriter::new(DeviceW::Tun(writer), Arc::new(Mutex::new(dev)), in_ips, address, packet_information),
DeviceReader::new(reader),
driver_info
))
}
fn add_route(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> {
let route_add_str: String = format!(
"route -n add {} -netmask {} -interface {}",
address, netmask, name
);
let route_add_out = Command::new("sh")
.arg("-c")
.arg(&route_add_str)
.output()
.expect("sh exec error!");
if !route_add_out.status.success() {
return Err(io::Error::new(io::ErrorKind::Other, format!("添加路由失败: cmd:{},out:{:?}", route_add_str, route_add_out)));
}
Ok(())
}
fn config_ip(name: &str, address: Ipv4Addr, _netmask: Ipv4Addr, gateway: Ipv4Addr) -> io::Result<()> {
let up_eth_str: String = format!("ifconfig {} {:?} {:?} up ", name, address, gateway);
let up_eth_out = Command::new("sh")
.arg("-c")
.arg(&up_eth_str)
.output()
.expect("sh exec error!");
if !up_eth_out.status.success() {
return Err(io::Error::new(io::ErrorKind::Other, format!("设置网络地址失败: cmd:{},out:{:?}", up_eth_str, up_eth_out)));
}
Ok(())
}
pub fn delete_device(_device_type: DeviceType) {}
+53
View File
@@ -0,0 +1,53 @@
#[cfg(target_os = "windows")]
mod windows;
#[cfg(any(target_os = "linux"))]
mod linux;
#[cfg(target_os = "macos")]
mod mac;
#[cfg(any(target_os = "linux", target_os = "macos"))]
mod linux_mac;
#[cfg(target_os = "android")]
mod android;
#[cfg(any(target_os = "linux"))]
pub use linux::create_device;
#[cfg(any(target_os = "linux"))]
pub use linux::delete_device;
#[cfg(target_os = "android")]
pub use android::create;
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub use linux_mac::{DeviceWriter, DeviceReader};
#[cfg(target_os = "android")]
pub use android::{DeviceWriter, DeviceReader};
#[cfg(target_os = "macos")]
pub use mac::create_device;
#[cfg(target_os = "macos")]
pub use mac::delete_device;
#[cfg(target_os = "windows")]
pub use windows::create_device;
#[cfg(target_os = "windows")]
pub use windows::delete_device;
#[cfg(target_os = "windows")]
pub use windows::{DeviceWriter, DeviceReader};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum DeviceType {
Tun,
Tap,
}
impl DeviceType {
pub fn is_tun(&self) -> bool {
*self == DeviceType::Tun
}
}
#[derive(Clone)]
pub struct DriverInfo {
pub device_type: DeviceType,
pub name: String,
pub version: String,
pub mac: Option<String>,
}
+372
View File
@@ -0,0 +1,372 @@
use std::{io, thread};
use std::net::Ipv4Addr;
use std::os::windows::process::CommandExt;
use std::sync::Arc;
use std::time::Duration;
use libloading::Library;
use parking_lot::Mutex;
use packet::ethernet;
use packet::ethernet::packet::EthernetPacket;
use win_tun_tap::{IFace, TapDevice, TunDevice};
use crate::tun_tap_device::{DriverInfo, DeviceType};
pub const TUN_INTERFACE_NAME: &str = "Vnt-Tun-V1";
pub const TUN_POOL_NAME: &str = "Vnt-Tun-V1";
pub const TAP_INTERFACE_NAME: &str = "Vnt-Tap-V1";
pub enum Device {
Tun(TunDevice),
Tap((TapDevice, [u8; 6])),
}
impl Device {
pub fn is_tun(&self) -> bool {
match self {
Device::Tun(_) => {
true
}
Device::Tap(_) => {
false
}
}
}
}
#[derive(Clone)]
pub struct DeviceWriter {
device: Arc<Device>,
lock: Arc<Mutex<()>>,
in_ips: Vec<(Ipv4Addr, Ipv4Addr)>,
}
impl DeviceWriter {
pub fn new(device: Arc<Device>, in_ips: Vec<(Ipv4Addr, Ipv4Addr)>, _ip: Ipv4Addr) -> Self {
Self {
device,
lock: Arc::new(Default::default()),
in_ips,
}
}
}
impl DeviceWriter {
///tun网卡写入ipv4数据
pub fn write_ipv4_tun(&self, buf: &[u8]) -> io::Result<()> {
match self.device.as_ref() {
Device::Tun(dev) => {
let mut packet = dev.allocate_send_packet(buf.len() as u16)?;
packet.bytes_mut().copy_from_slice(buf);
dev.send_packet(packet);
Ok(())
}
Device::Tap(_) => {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
}
}
/// tap网卡写入以太网帧
pub fn write_ethernet_tap(&self, buf: &[u8]) -> io::Result<()> {
match self.device.as_ref() {
Device::Tun(_) => {
Err(io::Error::from(io::ErrorKind::Unsupported))
}
Device::Tap((dev, _)) => {
dev.write(buf)?;
Ok(())
}
}
}
///写入ipv4数据,头部必须留14字节,给tap写入以太网帧头
pub fn write_ipv4(&self, buf: &mut [u8]) -> io::Result<()> {
match self.device.as_ref() {
Device::Tun(dev) => {
let mut packet = dev.allocate_send_packet((buf.len() - 14) as u16)?;
packet.bytes_mut().copy_from_slice(&buf[14..]);
dev.send_packet(packet);
}
Device::Tap((dev, mac)) => {
let source_mac = [buf[14 + 12], buf[14 + 13], buf[14 + 14], buf[14 + 15], !mac[5], 234];
let mut ethernet_packet = EthernetPacket::unchecked(buf);
ethernet_packet.set_source(&source_mac);
ethernet_packet.set_destination(mac);
ethernet_packet.set_protocol(ethernet::protocol::Protocol::Ipv4);
dev.write(&ethernet_packet.buffer)?;
}
}
Ok(())
}
pub fn change_ip(
&self,
address: Ipv4Addr,
netmask: Ipv4Addr,
gateway: Ipv4Addr,
old_netmask: Ipv4Addr,
old_gateway: Ipv4Addr,
) -> io::Result<()> {
let _guard = self.lock.lock();
let dev: &dyn IFace = match self.device.as_ref() {
Device::Tun(dev) => {
dev as &dyn IFace
}
Device::Tap((dev, _)) => {
dev as &dyn IFace
}
};
if let Err(e) =
dev.delete_route(dest(old_gateway, old_gateway), old_netmask, old_gateway)
{
log::warn!("{:?}", e);
}
dev.set_ip(address, netmask)?;
for (address, netmask) in &self.in_ips {
dev.add_route(*address, *netmask, gateway, 1)?;
}
// 当前网段路由
dev.add_route(address, netmask, gateway, 1)?;
// 广播和组播路由
dev.add_route(Ipv4Addr::BROADCAST, Ipv4Addr::BROADCAST, gateway, 1)?;
dev.add_route(Ipv4Addr::from([224, 0, 0, 0]), Ipv4Addr::from([240, 0, 0, 0]), gateway, 1)?;
delete_cache();
Ok(())
}
pub fn close(&self) -> io::Result<()> {
match self.device.as_ref() {
Device::Tun(dev) => {
dev.shutdown()
}
Device::Tap((dev, _)) => {
dev.shutdown()
}
}
}
pub fn is_tun(&self) -> bool {
self.device.is_tun()
}
}
fn dest(ip: Ipv4Addr, mask: Ipv4Addr) -> Ipv4Addr {
let ip = ip.octets();
let mask = mask.octets();
Ipv4Addr::from([
ip[0] & mask[0],
ip[1] & mask[1],
ip[2] & mask[2],
ip[3] & mask[3],
])
}
pub struct DeviceReader {
device: Arc<Device>,
}
impl DeviceReader {
pub fn new(device: Arc<Device>) -> Self {
Self {
device,
}
}
}
impl DeviceReader {
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
match self.device.as_ref() {
Device::Tun(dev) => {
let packet = dev.receive_blocking()?;
let packet = packet.bytes();
let len = packet.len();
if len > buf.len() {
return Err(io::Error::new(io::ErrorKind::InvalidData, "data too long"));
}
buf[..len].copy_from_slice(packet);
Ok(len)
}
Device::Tap((dev, _)) => {
dev.read(buf)
}
}
}
}
fn create_tun(
address: Ipv4Addr,
netmask: Ipv4Addr,
gateway: Ipv4Addr,
in_ips: Vec<(Ipv4Addr, Ipv4Addr)>,
mtu: u16,
) -> io::Result<(DeviceWriter, DeviceReader, DriverInfo)> {
unsafe {
match Library::new("wintun.dll") {
Ok(lib) => match TunDevice::delete_for_name(lib, TUN_INTERFACE_NAME) {
Ok(_) => {
thread::sleep(Duration::from_millis(5));
}
Err(_) => {}
},
Err(e) => {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("wintun.dll not found {:?}", e),
));
}
}
let tun_device = match TunDevice::create(
Library::new("wintun.dll").unwrap(),
TUN_POOL_NAME,
TUN_INTERFACE_NAME,
) {
Ok(tun_device) => tun_device,
Err(_) => {
thread::sleep(Duration::from_millis(200));
match TunDevice::create(
Library::new("wintun.dll").unwrap(),
TUN_POOL_NAME,
TUN_INTERFACE_NAME,
) {
Ok(tun_device) => tun_device,
Err(e) => {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("{:?}", e),
));
}
}
}
};
let name = tun_device.get_name()?;
let version = format!("{:?}", tun_device.version()?);
tun_device.set_ip(address, netmask)?;
tun_device.set_metric(1)?;
tun_device.set_mtu(mtu)?;
// ip代理路由
for (address, netmask) in &in_ips {
tun_device.add_route(*address, *netmask, gateway, 1)?;
}
// 当前网段路由
tun_device.add_route(address, netmask, gateway, 1)?;
// 广播和组播路由
tun_device.add_route(Ipv4Addr::BROADCAST, Ipv4Addr::BROADCAST, gateway, 1)?;
tun_device.add_route(Ipv4Addr::from([224, 0, 0, 0]), Ipv4Addr::from([240, 0, 0, 0]), gateway, 1)?;
delete_cache();
let device = Arc::new(Device::Tun(tun_device));
let driver_info = DriverInfo {
device_type: DeviceType::Tun,
name,
version,
mac: None,
};
Ok((
DeviceWriter::new(device.clone(), in_ips, address),
DeviceReader::new(device),
driver_info
))
}
}
fn delete_cache() {
//清除路由缓存
let delete_cache = "netsh interface ip delete destinationcache";
let out = std::process::Command::new("cmd")
.creation_flags(0x08000000)
.arg("/C")
.arg(delete_cache)
.output()
.unwrap();
if !out.status.success() {
log::warn!("删除缓存失败:{:?}",out);
}
}
fn delete_tun() {
unsafe {
match Library::new("wintun.dll") {
Ok(lib) => match TunDevice::delete_for_name(lib, TUN_INTERFACE_NAME) {
Ok(_) => {}
Err(_) => {}
},
Err(_) => {}
}
}
}
fn create_tap(
address: Ipv4Addr,
netmask: Ipv4Addr,
gateway: Ipv4Addr,
in_ips: Vec<(Ipv4Addr, Ipv4Addr)>,
mtu: u16,
) -> io::Result<(DeviceWriter, DeviceReader, DriverInfo)> {
let tap_device = match TapDevice::open(TAP_INTERFACE_NAME) {
Ok(tap_device) => tap_device,
Err(e) => {
log::warn!("{:?}", e);
let tap_device = TapDevice::create()?;
tap_device.set_name(TAP_INTERFACE_NAME)?;
tap_device
}
};
let mac = tap_device.get_mac()?;
let name = tap_device.get_name()?;
let version = format!("{:?}", tap_device.get_version()?);
let mac_str = format!("mac:{:x?}", mac);
tap_device.set_ip(address, netmask)?;
tap_device.set_metric(1)?;
tap_device.set_mtu(mtu)?;
tap_device.set_status(true)?;
tap_device.add_route(address, netmask, gateway, 1)?;
for (address, netmask) in &in_ips {
tap_device.add_route(*address, *netmask, gateway, 1)?;
}
// 广播和组播路由
tap_device.add_route(Ipv4Addr::BROADCAST, Ipv4Addr::BROADCAST, gateway, 1)?;
tap_device.add_route(Ipv4Addr::from([224, 0, 0, 0]), Ipv4Addr::from([240, 0, 0, 0]), gateway, 1)?;
delete_cache();
let tap = Arc::new(Device::Tap((tap_device, mac)));
let driver_info = DriverInfo {
device_type: DeviceType::Tap,
name,
version,
mac: Some(mac_str),
};
Ok((
DeviceWriter::new(tap.clone(), in_ips, address),
DeviceReader::new(tap),
driver_info
))
}
fn delete_tap() {
let tap_device = match TapDevice::open(TAP_INTERFACE_NAME) {
Ok(tap_device) => tap_device,
Err(_) => {
return;
}
};
let _ = tap_device.delete();
}
pub fn create_device(device_type: DeviceType, address: Ipv4Addr,
netmask: Ipv4Addr,
gateway: Ipv4Addr,
in_ips: Vec<(Ipv4Addr, Ipv4Addr)>,
mtu: u16) -> io::Result<(DeviceWriter, DeviceReader, DriverInfo)> {
match device_type {
DeviceType::Tun => {
create_tun(address, netmask, gateway, in_ips, mtu)
}
DeviceType::Tap => {
create_tap(address, netmask, gateway, in_ips, mtu)
}
}
}
pub fn delete_device(device_type: DeviceType) {
match device_type {
DeviceType::Tun => {
delete_tun()
}
DeviceType::Tap => {
delete_tap()
}
}
}
+1
View File
@@ -0,0 +1 @@
pub mod wait;
+44
View File
@@ -0,0 +1,44 @@
use std::sync::Arc;
use std::sync::atomic::{AtomicIsize, Ordering};
use tokio::sync::watch::{channel, Receiver, Sender};
#[derive(Clone)]
pub struct WaitGroup {
count: Arc<AtomicIsize>,
receiver: Receiver<usize>,
sender: Arc<Sender<usize>>,
}
impl WaitGroup {
pub fn new() -> Self {
let (sender, receiver) = channel(1);
Self {
count: Arc::new(Default::default()),
receiver,
sender: Arc::new(sender),
}
}
pub fn add(&self) {
let _ = self.count.fetch_add(1, Ordering::Relaxed);
}
pub fn done(&self) {
let i = self.count.fetch_sub(1, Ordering::Relaxed);
if i == 1 {
let _ = self.sender.send(0);
}
}
pub async fn wait(&mut self) {
loop {
if 0 == *self.receiver.borrow() {
return;
}
if self.receiver.changed().await.is_ok() {
if 0 == *self.receiver.borrow() {
return;
}
} else {
return;
}
}
}
}
+33
View File
@@ -0,0 +1,33 @@
[package]
name = "win-tun-tap"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
log = "0.4.17"
winreg = "0.7"
scopeguard = "1.1"
libloading = "0.7"
widestring = "0.4"
once_cell = "1.8"
itertools = "0.10.1"
rand = "0.8.5"
[dependencies.winapi]
version = "0.3"
features = [
"errhandlingapi",
"combaseapi",
"ioapiset",
"winioctl",
"setupapi",
"synchapi",
"netioapi",
"fileapi",
"winbase",
"winerror",
"ipexport",
"iphlpapi",
"handleapi"
]
+534
View File
@@ -0,0 +1,534 @@
// Many things will be used in the future
#![allow(unused)]
//! Module holding safe wrappers over winapi functions
use winapi::shared::basetsd::*;
use winapi::shared::guiddef::GUID;
use winapi::shared::ifdef::*;
use winapi::shared::minwindef::*;
use winapi::shared::netioapi::*;
use winapi::shared::winerror::*;
use winapi::um::combaseapi::*;
use winapi::um::errhandlingapi::*;
use winapi::um::fileapi::*;
use winapi::um::handleapi::*;
use winapi::um::ioapiset::*;
use winapi::um::setupapi::*;
use winapi::um::synchapi::*;
use winapi::um::winioctl::*;
use winapi::um::winnt::*;
use winapi::um::winreg::*;
use std::{io, mem, ptr};
use std::error::Error;
use winapi::um::minwinbase::OVERLAPPED_u;
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
#[repr(C)]
#[derive(Clone, Copy)]
/// Custom type to handle variable size SP_DRVINFO_DETAIL_DATA_W
pub struct SP_DRVINFO_DETAIL_DATA_W2 {
pub cbSize: DWORD,
pub InfDate: FILETIME,
pub CompatIDsOffset: DWORD,
pub CompatIDsLength: DWORD,
pub Reserved: ULONG_PTR,
pub SectionName: [WCHAR; 256],
pub InfFileName: [WCHAR; 260],
pub DrvDescription: [WCHAR; 256],
pub HardwareID: [WCHAR; 512],
}
pub fn string_from_guid(guid: &GUID) -> io::Result<Vec<WCHAR>> {
// GUID_STRING_CHARACTERS + 1
let mut string = vec![0; 39];
match unsafe {
StringFromGUID2(guid, string.as_mut_ptr(), string.len() as _)
} {
0 => Err(io::Error::new(io::ErrorKind::Other, "Insufficent buffer")),
_ => Ok(string),
}
}
pub fn alias_to_luid(alias: &[WCHAR]) -> io::Result<NET_LUID> {
let mut luid = unsafe { mem::zeroed() };
match unsafe { ConvertInterfaceAliasToLuid(alias.as_ptr(), &mut luid) } {
0 => Ok(luid),
err => Err(io::Error::from_raw_os_error(err as _)),
}
}
pub fn luid_to_index(luid: &NET_LUID) -> io::Result<NET_IFINDEX> {
let mut index = 0;
match unsafe { ConvertInterfaceLuidToIndex(luid, &mut index) } {
0 => Ok(index),
err => Err(io::Error::from_raw_os_error(err as _)),
}
}
pub fn luid_to_guid(luid: &NET_LUID) -> io::Result<GUID> {
let mut guid = unsafe { mem::zeroed() };
match unsafe { ConvertInterfaceLuidToGuid(luid, &mut guid) } {
0 => Ok(guid),
err => Err(io::Error::from_raw_os_error(err as _)),
}
}
pub fn luid_to_alias(luid: &NET_LUID) -> io::Result<Vec<WCHAR>> {
// IF_MAX_STRING_SIZE + 1
let mut alias = vec![0; 257];
match unsafe {
ConvertInterfaceLuidToAlias(luid, alias.as_mut_ptr(), alias.len())
} {
0 => {
Ok(alias)
}
err => Err(io::Error::from_raw_os_error(err as _)),
}
}
pub fn close_handle(handle: HANDLE) -> io::Result<()> {
match unsafe { CloseHandle(handle) } {
0 => Err(io::Error::last_os_error()),
_ => Ok(()),
}
}
pub fn create_file(
file_name: &[WCHAR],
desired_access: DWORD,
share_mode: DWORD,
creation_disposition: DWORD,
flags_and_attributes: DWORD,
) -> io::Result<HANDLE> {
match unsafe {
CreateFileW(
file_name.as_ptr(),
desired_access,
share_mode,
ptr::null_mut(),
creation_disposition,
flags_and_attributes,
ptr::null_mut(),
)
} {
INVALID_HANDLE_VALUE => Err(io::Error::last_os_error()),
handle => Ok(handle),
}
}
pub fn read_file(handle: HANDLE, buffer: &mut [u8]) -> io::Result<DWORD> {
let mut ret = 0;
//https://www.cnblogs.com/linyilong3/archive/2012/05/03/2480451.html
unsafe {
let mut ip_overlapped = winapi::um::minwinbase::OVERLAPPED {
Internal: 0,
InternalHigh: 0,
u: Default::default(),
hEvent: ptr::null_mut(),
};
if 0 == ReadFile(
handle,
buffer.as_mut_ptr() as _,
buffer.len() as _,
&mut ret,
&mut ip_overlapped, ) {
let e = io::Error::last_os_error();
if e.raw_os_error().unwrap_or(0) == 997 {
if 0 == GetOverlappedResult(handle, &mut ip_overlapped, &mut ret, 1) {
return Err(e);
}
} else {
return Err(e);
}
}
Ok(ret)
}
}
pub fn write_file(handle: HANDLE, buffer: &[u8]) -> io::Result<DWORD> {
let mut ret = 0;
let mut ip_overlapped = winapi::um::minwinbase::OVERLAPPED {
Internal: 0,
InternalHigh: 0,
u: Default::default(),
hEvent: ptr::null_mut(),
};
unsafe {
if 0 == WriteFile(
handle,
buffer.as_ptr() as _,
buffer.len() as _,
&mut ret,
&mut ip_overlapped,
) {
let e = io::Error::last_os_error();
if e.raw_os_error().unwrap_or(0) == 997 {
if 0 == GetOverlappedResult(handle, &mut ip_overlapped, &mut ret, 1) {
return Err(e);
}
} else {
return Err(e);
}
}
Ok(ret)
}
}
pub fn create_device_info_list(guid: &GUID) -> io::Result<HDEVINFO> {
match unsafe { SetupDiCreateDeviceInfoList(guid, ptr::null_mut()) } {
INVALID_HANDLE_VALUE => Err(io::Error::last_os_error()),
devinfo => Ok(devinfo),
}
}
pub fn get_class_devs(guid: &GUID, flags: DWORD) -> io::Result<HDEVINFO> {
match unsafe {
SetupDiGetClassDevsW(guid, ptr::null(), ptr::null_mut(), flags)
} {
INVALID_HANDLE_VALUE => Err(io::Error::last_os_error()),
devinfo => Ok(devinfo),
}
}
pub fn destroy_device_info_list(devinfo: HDEVINFO) -> io::Result<()> {
match unsafe { SetupDiDestroyDeviceInfoList(devinfo) } {
0 => Err(io::Error::last_os_error()),
_ => Ok(()),
}
}
pub fn class_name_from_guid(guid: &GUID) -> io::Result<Vec<WCHAR>> {
let mut class_name = vec![0; 32];
match unsafe {
SetupDiClassNameFromGuidW(
guid,
class_name.as_mut_ptr(),
class_name.len() as _,
ptr::null_mut(),
)
} {
0 => Err(io::Error::last_os_error()),
_ => Ok(class_name),
}
}
pub fn create_device_info(
devinfo: HDEVINFO,
device_name: &[WCHAR],
guid: &GUID,
device_description: &[WCHAR],
creation_flags: DWORD,
) -> io::Result<SP_DEVINFO_DATA> {
let mut devinfo_data: SP_DEVINFO_DATA = unsafe { mem::zeroed() };
devinfo_data.cbSize = mem::size_of_val(&devinfo_data) as _;
match unsafe {
SetupDiCreateDeviceInfoW(
devinfo,
device_name.as_ptr(),
guid,
device_description.as_ptr(),
ptr::null_mut(),
creation_flags,
&mut devinfo_data,
)
} {
0 => Err(io::Error::last_os_error()),
_ => Ok(devinfo_data),
}
}
pub fn set_selected_device(
devinfo: HDEVINFO,
devinfo_data: &SP_DEVINFO_DATA,
) -> io::Result<()> {
match unsafe {
SetupDiSetSelectedDevice(devinfo, devinfo_data as *const _ as _)
} {
0 => Err(io::Error::last_os_error()),
_ => Ok(()),
}
}
pub fn set_device_registry_property(
devinfo: HDEVINFO,
devinfo_data: &SP_DEVINFO_DATA,
property: DWORD,
value: &[WCHAR],
) -> io::Result<()> {
match unsafe {
SetupDiSetDeviceRegistryPropertyW(
devinfo,
devinfo_data as *const _ as _,
property,
value.as_ptr() as _,
(value.len() * 2) as _,
)
} {
0 => Err(io::Error::last_os_error()),
_ => Ok(()),
}
}
pub fn get_device_registry_property(
devinfo: HDEVINFO,
devinfo_data: &SP_DEVINFO_DATA,
property: DWORD,
) -> io::Result<Vec<WCHAR>> {
let mut value = vec![0; 32];
match unsafe {
SetupDiGetDeviceRegistryPropertyW(
devinfo,
devinfo_data as *const _ as _,
property,
ptr::null_mut(),
value.as_mut_ptr() as _,
(value.len() * 2) as _,
ptr::null_mut(),
)
} {
0 => Err(io::Error::last_os_error()),
_ => Ok(value),
}
}
pub fn build_driver_info_list(
devinfo: HDEVINFO,
devinfo_data: &SP_DEVINFO_DATA,
driver_type: DWORD,
) -> io::Result<()> {
match unsafe {
SetupDiBuildDriverInfoList(
devinfo,
devinfo_data as *const _ as _,
driver_type,
)
} {
0 => Err(io::Error::last_os_error()),
_ => Ok(()),
}
}
pub fn destroy_driver_info_list(
devinfo: HDEVINFO,
devinfo_data: &SP_DEVINFO_DATA,
driver_type: DWORD,
) -> io::Result<()> {
match unsafe {
SetupDiDestroyDriverInfoList(
devinfo,
devinfo_data as *const _ as _,
driver_type,
)
} {
0 => Err(io::Error::last_os_error()),
_ => Ok(()),
}
}
pub fn get_driver_info_detail(
devinfo: HDEVINFO,
devinfo_data: &SP_DEVINFO_DATA,
drvinfo_data: &SP_DRVINFO_DATA_W,
) -> io::Result<SP_DRVINFO_DETAIL_DATA_W2> {
let mut drvinfo_detail: SP_DRVINFO_DETAIL_DATA_W2 =
unsafe { mem::zeroed() };
drvinfo_detail.cbSize = mem::size_of::<SP_DRVINFO_DETAIL_DATA_W>() as _;
match unsafe {
SetupDiGetDriverInfoDetailW(
devinfo,
devinfo_data as *const _ as _,
drvinfo_data as *const _ as _,
&mut drvinfo_detail as *mut _ as _,
mem::size_of_val(&drvinfo_detail) as _,
ptr::null_mut(),
)
} {
0 => Err(io::Error::last_os_error()),
_ => Ok(drvinfo_detail),
}
}
pub fn set_selected_driver(
devinfo: HDEVINFO,
devinfo_data: &SP_DEVINFO_DATA,
drvinfo_data: &SP_DRVINFO_DATA_W,
) -> io::Result<()> {
match unsafe {
SetupDiSetSelectedDriverW(
devinfo,
devinfo_data as *const _ as _,
drvinfo_data as *const _ as _,
)
} {
0 => Err(io::Error::last_os_error()),
_ => Ok(()),
}
}
pub fn set_class_install_params(
devinfo: HDEVINFO,
devinfo_data: &SP_DEVINFO_DATA,
params: &impl Copy,
) -> io::Result<()> {
match unsafe {
SetupDiSetClassInstallParamsW(
devinfo,
devinfo_data as *const _ as _,
params as *const _ as _,
mem::size_of_val(params) as _,
)
} {
0 => Err(io::Error::last_os_error()),
_ => Ok(()),
}
}
pub fn call_class_installer(
devinfo: HDEVINFO,
devinfo_data: &SP_DEVINFO_DATA,
install_function: DI_FUNCTION,
) -> io::Result<()> {
match unsafe {
SetupDiCallClassInstaller(
install_function,
devinfo,
devinfo_data as *const _ as _,
)
} {
0 => Err(io::Error::last_os_error()),
_ => Ok(()),
}
}
pub fn open_dev_reg_key(
devinfo: HDEVINFO,
devinfo_data: &SP_DEVINFO_DATA,
scope: DWORD,
hw_profile: DWORD,
key_type: DWORD,
sam_desired: REGSAM,
) -> io::Result<HKEY> {
const INVALID_KEY_VALUE: HKEY = INVALID_HANDLE_VALUE as _;
match unsafe {
SetupDiOpenDevRegKey(
devinfo,
devinfo_data as *const _ as _,
scope,
hw_profile,
key_type,
sam_desired,
)
} {
INVALID_KEY_VALUE => Err(io::Error::last_os_error()),
key => Ok(key),
}
}
pub fn notify_change_key_value(
key: HKEY,
watch_subtree: BOOL,
notify_filter: DWORD,
milliseconds: DWORD,
) -> io::Result<()> {
let event = match unsafe {
CreateEventW(ptr::null_mut(), FALSE, FALSE, ptr::null())
} {
INVALID_HANDLE_VALUE => Err(io::Error::last_os_error()),
event => Ok(event),
}?;
match unsafe {
RegNotifyChangeKeyValue(key, watch_subtree, notify_filter, event, TRUE)
} {
0 => Ok(()),
err => Err(io::Error::from_raw_os_error(err)),
}?;
match unsafe { WaitForSingleObject(event, milliseconds) } {
0 => Ok(()),
0x102 => Err(io::Error::new(
io::ErrorKind::TimedOut,
"Registry timed out",
)),
_ => Err(io::Error::last_os_error()),
}
}
pub fn enum_driver_info(
devinfo: HDEVINFO,
devinfo_data: &SP_DEVINFO_DATA,
driver_type: DWORD,
member_index: DWORD,
) -> Option<io::Result<SP_DRVINFO_DATA_W>> {
let mut drvinfo_data: SP_DRVINFO_DATA_W = unsafe { mem::zeroed() };
drvinfo_data.cbSize = mem::size_of_val(&drvinfo_data) as _;
match unsafe {
SetupDiEnumDriverInfoW(
devinfo,
devinfo_data as *const _ as _,
driver_type,
member_index,
&mut drvinfo_data,
)
} {
0 if unsafe { GetLastError() == ERROR_NO_MORE_ITEMS } => None,
0 => Some(Err(io::Error::last_os_error())),
_ => Some(Ok(drvinfo_data)),
}
}
pub fn enum_device_info(
devinfo: HDEVINFO,
member_index: DWORD,
) -> Option<io::Result<SP_DEVINFO_DATA>> {
let mut devinfo_data: SP_DEVINFO_DATA = unsafe { mem::zeroed() };
devinfo_data.cbSize = mem::size_of_val(&devinfo_data) as _;
match unsafe {
SetupDiEnumDeviceInfo(devinfo, member_index, &mut devinfo_data)
} {
0 if unsafe { GetLastError() == ERROR_NO_MORE_ITEMS } => None,
0 => Some(Err(io::Error::last_os_error())),
_ => Some(Ok(devinfo_data)),
}
}
pub fn device_io_control(
handle: HANDLE,
io_control_code: DWORD,
in_buffer: &impl Copy,
out_buffer: &mut impl Copy,
) -> io::Result<()> {
let mut junk = 0;
match unsafe {
DeviceIoControl(
handle,
io_control_code,
in_buffer as *const _ as _,
mem::size_of_val(in_buffer) as _,
out_buffer as *mut _ as _,
mem::size_of_val(out_buffer) as _,
&mut junk,
ptr::null_mut(),
)
} {
0 => Err(io::Error::last_os_error()),
_ => Ok(()),
}
}
+47
View File
@@ -0,0 +1,47 @@
#![cfg(windows)]
mod tap;
mod tun;
mod ffi;
mod netsh;
mod route;
use std::{io, net};
use std::net::Ipv4Addr;
pub use tap::TapDevice;
pub use tun::*;
/// Encode a string as a utf16 buffer
fn encode_utf16(string: &str) -> Vec<u16> {
use std::iter::once;
string.encode_utf16().chain(once(0)).collect()
}
/// Decode a string from a utf16 buffer
fn decode_utf16(string: &[u16]) -> String {
let end = string.iter().position(|b| *b == 0).unwrap_or(string.len());
String::from_utf16_lossy(&string[..end])
}
pub trait IFace {
fn shutdown(&self) -> io::Result<()>;
/// 获取接口索引
fn get_index(&self) -> io::Result<u32>;
/// 获取名称
fn get_name(&self) -> io::Result<String>;
/// 设置名称
fn set_name(&self, new_name: &str) -> io::Result<()>;
/// 设置ip
fn set_ip(&self, address: Ipv4Addr, mask: Ipv4Addr) -> io::Result<()>;
/// 设置路由
fn add_route(&self, dest: Ipv4Addr,
netmask: Ipv4Addr,
gateway: Ipv4Addr, metric: u16) -> io::Result<()>;
/// 删除路由
fn delete_route(&self, dest: Ipv4Addr,
netmask: Ipv4Addr,
gateway: Ipv4Addr, ) -> io::Result<()>;
/// 设置最大传输单元
fn set_mtu(&self, mtu: u16) -> io::Result<()>;
/// 设置跃点
fn set_metric(&self, metric: u16) -> io::Result<()>;
}
+65
View File
@@ -0,0 +1,65 @@
use std::io;
use std::net::Ipv4Addr;
use std::os::windows::process::CommandExt;
/// 设置网卡名称
pub fn set_interface_name(old_name: &str, new_name: &str) -> io::Result<()> {
let cmd = format!(" netsh interface set interface name={:?} newname={:?}", old_name, new_name);
let out = std::process::Command::new("cmd")
.creation_flags(0x08000000) //winapi-0.3.9/src/um/winbase.rs:283
.arg("/C")
.arg(&cmd)
.output()?;
if !out.status.success() {
log::warn!("修改网卡名称失败:cmd={:?},out={:?}",cmd,out);
return Err(io::Error::new(io::ErrorKind::Other, "修改网卡名称失败"));
}
Ok(())
}
/// 设置网卡ip
pub fn set_interface_ip(index: u32, address: &Ipv4Addr, netmask: &Ipv4Addr) -> io::Result<()> {
let set_address = format!(
"netsh interface ip set address {} static {:?} {:?} ",
index, address, netmask,
);
let out = std::process::Command::new("cmd")
.creation_flags(0x08000000)
.arg("/C")
.arg(&set_address)
.output()?;
if !out.status.success() {
log::error!("cmd={:?},out={:?}",set_address,out);
return Err(io::Error::new(io::ErrorKind::Other, format!("设置网络地址失败: {:?}", out)));
}
Ok(())
}
pub fn set_interface_mtu(index: u32, mtu: u16) -> io::Result<()> {
let set_mtu = format!(
"netsh interface ipv4 set subinterface {} mtu={} store=persistent",
index, mtu
);
let out = std::process::Command::new("cmd")
.creation_flags(0x08000000)
.arg("/C")
.arg(&set_mtu)
.output()?;
if !out.status.success() {
log::error!("cmd={:?},out={:?}",set_mtu,out);
return Err(io::Error::new(io::ErrorKind::Other, format!("设置mtu失败: {:?}", out)));
}
Ok(())
}
pub fn set_interface_metric(index: u32, metric: u16) -> io::Result<()> {
let set_metric = format!("netsh interface ip set interface {} metric={}", index,metric);
let out = std::process::Command::new("cmd")
.creation_flags(0x08000000)
.arg("/C")
.arg(&set_metric)
.output()?;
if !out.status.success() {
log::error!("cmd={:?},out={:?}",set_metric,out);
return Err(io::Error::new(io::ErrorKind::Other, format!("设置metric失败: {:?}", out)));
}
Ok(())
}
+47
View File
@@ -0,0 +1,47 @@
use std::io;
use std::net::Ipv4Addr;
use std::os::windows::process::CommandExt;
/// 添加路由
pub fn add_route(index: u32, dest: Ipv4Addr,
netmask: Ipv4Addr,
gateway: Ipv4Addr, metric: u16) -> io::Result<()> {
let set_route = format!(
"route add {:?} mask {:?} {:?} metric {} if {}",
dest, netmask, gateway, metric, index
);
// 执行添加路由命令
let out = std::process::Command::new("cmd")
.creation_flags(0x08000000)
.arg("/C")
.arg(&set_route)
.output()
.unwrap();
if !out.status.success() {
log::error!("cmd={:?},out={:?}",set_route,out);
return Err(io::Error::new(io::ErrorKind::Other, format!("添加路由失败: {:?}", out)));
}
Ok(())
}
/// 删除路由
pub fn delete_route(index: u32, dest: Ipv4Addr, netmask: Ipv4Addr, gateway: Ipv4Addr) -> io::Result<()> {
if index == 0 {
return Err(io::Error::new(io::ErrorKind::Other, format!("网络接口索引错误: {:?}", index)));
}
let delete_route = format!(
"route delete {:?} mask {:?} {:?} if {}",
dest, netmask, gateway, index
);
// 删除路由
let out = std::process::Command::new("cmd")
.creation_flags(0x08000000)
.arg("/C")
.arg(delete_route)
.output()
.unwrap();
if !out.status.success() {
return Err(io::Error::new(io::ErrorKind::Other, format!("删除路由失败: {:?}", out)));
}
Ok(())
}
+328
View File
@@ -0,0 +1,328 @@
use winapi::shared::ifdef::NET_LUID;
use winapi::shared::minwindef::*;
use winapi::um::fileapi::*;
use winapi::um::setupapi::*;
use winapi::um::winnt::*;
use scopeguard::{guard, ScopeGuard};
use winreg::RegKey;
use std::io;
use winapi::um::winbase::FILE_FLAG_OVERLAPPED;
use crate::{decode_utf16, encode_utf16, ffi};
/// tap-windows hardware ID
const HARDWARE_ID: &str = "tap0901";
winapi::DEFINE_GUID! {
GUID_NETWORK_ADAPTER,
0x4d36e972, 0xe325, 0x11ce,
0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18
}
/// Create a new interface and returns its NET_LUID
pub fn create_interface() -> io::Result<NET_LUID> {
let devinfo = ffi::create_device_info_list(&GUID_NETWORK_ADAPTER)?;
let _guard = guard((), |_| {
let _ = ffi::destroy_device_info_list(devinfo);
});
let class_name = ffi::class_name_from_guid(&GUID_NETWORK_ADAPTER)?;
let devinfo_data = ffi::create_device_info(
devinfo,
&class_name,
&GUID_NETWORK_ADAPTER,
&encode_utf16(""),
DICD_GENERATE_ID,
)?;
ffi::set_selected_device(devinfo, &devinfo_data)?;
ffi::set_device_registry_property(
devinfo,
&devinfo_data,
SPDRP_HARDWAREID,
&encode_utf16(HARDWARE_ID),
)?;
ffi::build_driver_info_list(devinfo, &devinfo_data, SPDIT_COMPATDRIVER)?;
let _guard = guard((), |_| {
let _ = ffi::destroy_driver_info_list(
devinfo,
&devinfo_data,
SPDIT_COMPATDRIVER,
);
});
let mut driver_version = 0;
let mut member_index = 0;
while let Some(drvinfo_data) = ffi::enum_driver_info(
devinfo,
&devinfo_data,
SPDIT_COMPATDRIVER,
member_index,
) {
member_index += 1;
let drvinfo_data = match drvinfo_data {
Ok(drvinfo_data) => drvinfo_data,
_ => continue,
};
if drvinfo_data.DriverVersion <= driver_version {
continue;
}
let drvinfo_detail = match ffi::get_driver_info_detail(
devinfo,
&devinfo_data,
&drvinfo_data,
) {
Ok(drvinfo_detail) => drvinfo_detail,
_ => continue,
};
let is_compatible = drvinfo_detail
.HardwareID
.split(|b| *b == 0)
.map(|id| decode_utf16(id))
.any(|id| id.eq_ignore_ascii_case(HARDWARE_ID));
if !is_compatible {
continue;
}
match ffi::set_selected_driver(devinfo, &devinfo_data, &drvinfo_data) {
Ok(_) => (),
_ => continue,
}
driver_version = drvinfo_data.DriverVersion;
}
if driver_version == 0 {
return Err(io::Error::new(io::ErrorKind::NotFound, "No driver found"));
}
let uninstaller = guard((), |_| {
let _ = ffi::call_class_installer(devinfo, &devinfo_data, DIF_REMOVE);
});
ffi::call_class_installer(devinfo, &devinfo_data, DIF_REGISTERDEVICE)?;
let _ = ffi::call_class_installer(
devinfo,
&devinfo_data,
DIF_REGISTER_COINSTALLERS,
);
let _ = ffi::call_class_installer(
devinfo,
&devinfo_data,
DIF_INSTALLINTERFACES,
);
ffi::call_class_installer(devinfo, &devinfo_data, DIF_INSTALLDEVICE)?;
let key = ffi::open_dev_reg_key(
devinfo,
&devinfo_data,
DICS_FLAG_GLOBAL,
0,
DIREG_DRV,
KEY_QUERY_VALUE | KEY_NOTIFY,
)?;
let key = RegKey::predef(key);
while let Err(_) = key.get_value::<DWORD, &str>("*IfType") {
ffi::notify_change_key_value(
key.raw_handle(),
TRUE,
REG_NOTIFY_CHANGE_NAME,
2000,
)?;
}
while let Err(_) = key.get_value::<DWORD, &str>("NetLuidIndex") {
ffi::notify_change_key_value(
key.raw_handle(),
TRUE,
REG_NOTIFY_CHANGE_NAME,
2000,
)?;
}
let if_type: DWORD = key.get_value("*IfType")?;
let luid_index: DWORD = key.get_value("NetLuidIndex")?;
// Defuse the uninstaller
ScopeGuard::into_inner(uninstaller);
let mut luid = NET_LUID { Value: 0 };
luid.set_IfType(if_type as _);
luid.set_NetLuidIndex(luid_index as _);
Ok(luid)
}
/// Check if the given interface exists and is a valid tap-windows device
pub fn check_interface(luid: &NET_LUID) -> io::Result<()> {
let devinfo = ffi::get_class_devs(&GUID_NETWORK_ADAPTER, DIGCF_PRESENT)?;
let _guard = guard((), |_| {
let _ = ffi::destroy_device_info_list(devinfo);
});
let mut member_index = 0;
while let Some(devinfo_data) = ffi::enum_device_info(devinfo, member_index)
{
member_index += 1;
let devinfo_data = match devinfo_data {
Ok(devinfo_data) => devinfo_data,
Err(_) => continue,
};
let hardware_id = match ffi::get_device_registry_property(
devinfo,
&devinfo_data,
SPDRP_HARDWAREID,
) {
Ok(hardware_id) => hardware_id,
Err(_) => continue,
};
if !decode_utf16(&hardware_id).eq_ignore_ascii_case(HARDWARE_ID) {
continue;
}
let key = match ffi::open_dev_reg_key(
devinfo,
&devinfo_data,
DICS_FLAG_GLOBAL,
0,
DIREG_DRV,
KEY_QUERY_VALUE | KEY_NOTIFY,
) {
Ok(key) => RegKey::predef(key),
Err(_) => continue,
};
let if_type: DWORD = match key.get_value("*IfType") {
Ok(if_type) => if_type,
Err(_) => continue,
};
let luid_index: DWORD = match key.get_value("NetLuidIndex") {
Ok(luid_index) => luid_index,
Err(_) => continue,
};
let mut luid2 = NET_LUID { Value: 0 };
luid2.set_IfType(if_type as _);
luid2.set_NetLuidIndex(luid_index as _);
if luid.Value != luid2.Value {
continue;
}
// Found it!
return Ok(());
}
Err(io::Error::new(io::ErrorKind::NotFound, "TAP Device not found"))
}
/// Deletes an existing interface
pub fn delete_interface(luid: &NET_LUID) -> io::Result<()> {
let devinfo = ffi::get_class_devs(&GUID_NETWORK_ADAPTER, DIGCF_PRESENT)?;
let _guard = guard((), |_| {
let _ = ffi::destroy_device_info_list(devinfo);
});
let mut member_index = 0;
while let Some(devinfo_data) = ffi::enum_device_info(devinfo, member_index)
{
member_index += 1;
let devinfo_data = match devinfo_data {
Ok(devinfo_data) => devinfo_data,
Err(_) => continue,
};
let hardware_id = match ffi::get_device_registry_property(
devinfo,
&devinfo_data,
SPDRP_HARDWAREID,
) {
Ok(hardware_id) => hardware_id,
Err(_) => continue,
};
if !decode_utf16(&hardware_id).eq_ignore_ascii_case(HARDWARE_ID) {
continue;
}
let key = match ffi::open_dev_reg_key(
devinfo,
&devinfo_data,
DICS_FLAG_GLOBAL,
0,
DIREG_DRV,
KEY_QUERY_VALUE | KEY_NOTIFY,
) {
Ok(key) => RegKey::predef(key),
Err(_) => continue,
};
let if_type: DWORD = match key.get_value("*IfType") {
Ok(if_type) => if_type,
Err(_) => continue,
};
let luid_index: DWORD = match key.get_value("NetLuidIndex") {
Ok(luid_index) => luid_index,
Err(_) => continue,
};
let mut luid2 = NET_LUID { Value: 0 };
luid2.set_IfType(if_type as _);
luid2.set_NetLuidIndex(luid_index as _);
if luid.Value != luid2.Value {
continue;
}
// Found it!
return ffi::call_class_installer(devinfo, &devinfo_data, DIF_REMOVE);
}
Err(io::Error::new(io::ErrorKind::NotFound, "TAP Device not found"))
}
/// Open an handle to an interface
pub fn open_interface(luid: &NET_LUID) -> io::Result<HANDLE> {
let guid = ffi::luid_to_guid(luid)
.and_then(|guid| ffi::string_from_guid(&guid))?;
let path = format!(r"\\.\Global\{}.tap", &decode_utf16(&guid));
ffi::create_file(
&encode_utf16(&path),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
OPEN_EXISTING,
FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,//FILE_ATTRIBUTE_SYSTEM,
)
}
+182
View File
@@ -0,0 +1,182 @@
use std::{io, time};
use std::net::Ipv4Addr;
use winapi::shared::ifdef::NET_LUID;
use winapi::um::winioctl::*;
use winapi::um::winnt::HANDLE;
use crate::{decode_utf16, encode_utf16, ffi, IFace, netsh, route};
mod iface;
pub struct TapDevice {
index: u32,
luid: NET_LUID,
handle: HANDLE,
}
unsafe impl Send for TapDevice {}
unsafe impl Sync for TapDevice {}
impl TapDevice {
/// Retieve the mac of the interface
pub fn get_mac(&self) -> io::Result<[u8; 6]> {
let mut mac = [0; 6];
ffi::device_io_control(
self.handle,
CTL_CODE(FILE_DEVICE_UNKNOWN, 1, METHOD_BUFFERED, FILE_ANY_ACCESS),
&(),
&mut mac,
)
.map(|_| mac)
}
/// Retrieve the version of the driver
pub fn get_version(&self) -> io::Result<[u32; 3]> {
let mut version = [0; 3];
ffi::device_io_control(
self.handle,
CTL_CODE(FILE_DEVICE_UNKNOWN, 2, METHOD_BUFFERED, FILE_ANY_ACCESS),
&(),
&mut version,
)
.map(|_| version)
}
/// Retieve the mtu of the interface
pub fn get_mtu(&self) -> io::Result<u32> {
let mut mtu = 0;
ffi::device_io_control(
self.handle,
CTL_CODE(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_ANY_ACCESS),
&(),
&mut mtu,
)
.map(|_| mtu)
}
/// Set the status of the interface, true for connected,
/// false for disconnected.
pub fn set_status(&self, status: bool) -> io::Result<()> {
let status: u32 = if status { 1 } else { 0 };
ffi::device_io_control(
self.handle,
CTL_CODE(FILE_DEVICE_UNKNOWN, 6, METHOD_BUFFERED, FILE_ANY_ACCESS),
&status,
&mut (),
)
}
}
impl TapDevice {
pub fn create() -> io::Result<Self> {
let luid = iface::create_interface()?;
// Even after retrieving the luid, we might need to wait
let start = time::Instant::now();
let handle = loop {
// If we surpassed 2 seconds just return
let now = time::Instant::now();
if now - start > time::Duration::from_secs(3) {
return Err(io::Error::new(
io::ErrorKind::TimedOut,
"Interface timed out",
));
}
match iface::open_interface(&luid) {
Err(_) => {
std::thread::yield_now();
continue;
}
Ok(handle) => break handle,
};
};
let index = ffi::luid_to_index(&luid).map(|index| index as u32)?;
Ok(Self { index, luid, handle })
}
pub fn open(name: &str) -> io::Result<Self> {
let name = encode_utf16(name);
let luid = ffi::alias_to_luid(&name)?;
iface::check_interface(&luid)?;
let handle = iface::open_interface(&luid)?;
let index = ffi::luid_to_index(&luid).map(|index| index as u32)?;
Ok(Self { index, luid, handle })
}
pub fn delete(self) -> io::Result<()> {
iface::delete_interface(&self.luid)
}
}
impl IFace for TapDevice {
fn shutdown(&self) -> io::Result<()> {
self.set_status(false)
}
fn get_index(&self) -> io::Result<u32> {
Ok(self.index)
}
fn get_name(&self) -> io::Result<String> {
ffi::luid_to_alias(&self.luid).map(|name| decode_utf16(&name))
}
fn set_name(&self, new_name: &str) -> io::Result<()> {
let name = self.get_name()?;
netsh::set_interface_name(&name, new_name)
}
fn set_ip(&self, address: Ipv4Addr, mask: Ipv4Addr) -> io::Result<()> {
let index = self.get_index()?;
netsh::set_interface_ip(index, &address, &mask)
}
fn add_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr, gateway: Ipv4Addr, metric: u16) -> io::Result<()> {
let index = self.get_index()?;
route::add_route(index, dest, netmask, gateway,metric)
}
fn delete_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr, gateway: Ipv4Addr) -> io::Result<()> {
let index = self.get_index()?;
route::delete_route(index, dest, netmask, gateway)
}
fn set_mtu(&self, mtu: u16) -> io::Result<()> {
let index = self.get_index()?;
netsh::set_interface_mtu(index, mtu)
}
fn set_metric(&self, metric: u16) -> io::Result<()> {
let index = self.get_index()?;
netsh::set_interface_metric(index, metric)
}
}
impl TapDevice {
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
ffi::read_file(self.handle, buf).map(|res| res as _)
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
ffi::write_file(self.handle, buf).map(|res| res as _)
}
}
impl Drop for TapDevice {
fn drop(&mut self) {
let _ = ffi::close_handle(self.handle);
let _ = iface::delete_interface(&self.luid);
}
}
@@ -1,17 +1,16 @@
use crate::wintun_raw;
use crate::Wintun;
use log::*;
use widestring::U16CStr;
use std::sync::atomic::{AtomicBool, Ordering};
use widestring::U16CStr;
use crate::tun::wintun_raw;
/// Sets the logger wintun will use when logging. Maps to the WintunSetLogger C function
pub fn set_logger(wintun: &Wintun, f: wintun_raw::WINTUN_LOGGER_CALLBACK) {
unsafe { wintun.WintunSetLogger(f) };
pub fn set_logger(win_tun: &wintun_raw::wintun, f: wintun_raw::WINTUN_LOGGER_CALLBACK) {
unsafe { win_tun.WintunSetLogger(f) };
}
pub fn reset_logger(wintun: &Wintun) {
set_logger(wintun, None);
pub fn reset_logger(win_tun: &wintun_raw::wintun) {
set_logger(win_tun, None);
}
static SET_LOGGER: AtomicBool = AtomicBool::new(false);
@@ -38,11 +37,11 @@ pub unsafe extern "C" fn default_logger(
}
}
pub(crate) fn set_default_logger_if_unset(wintun: &Wintun) {
pub(crate) fn set_default_logger_if_unset(win_tun: &wintun_raw::wintun) {
if SET_LOGGER
.compare_exchange(false, true, Ordering::SeqCst, Ordering::Relaxed)
.is_ok()
{
set_logger(wintun, Some(default_logger));
set_logger(win_tun, Some(default_logger));
}
}
+315
View File
@@ -0,0 +1,315 @@
use std::io;
use std::net::Ipv4Addr;
use winapi::um::{handleapi, synchapi, winbase, winnt};
use crate::{decode_utf16, encode_utf16, ffi, IFace, netsh, route};
use rand::Rng;
mod wintun_raw;
mod log;
pub mod packet;
/// The maximum size of wintun's internal ring buffer (in bytes)
pub const MAX_RING_CAPACITY: u32 = 0x400_0000;
/// The minimum size of wintun's internal ring buffer (in bytes)
pub const MIN_RING_CAPACITY: u32 = 0x2_0000;
/// Maximum pool name length including zero terminator
pub const MAX_POOL: usize = 256;
pub struct TunDevice {
pub(crate) luid: u64,
pub(crate) index: u32,
/// The session handle given to us by WintunStartSession
pub(crate) session: wintun_raw::WINTUN_SESSION_HANDLE,
/// Shared dll for required wintun driver functions
pub(crate) win_tun: wintun_raw::wintun,
/// Windows event handle that is signaled by the wintun driver when data becomes available to
/// read
pub(crate) read_event: winnt::HANDLE,
/// Windows event handle that is signaled when [`TunSession::shutdown`] is called force blocking
/// readers to exit
pub(crate) shutdown_event: winnt::HANDLE,
/// The adapter that owns this session
pub(crate) adapter: wintun_raw::WINTUN_ADAPTER_HANDLE,
}
unsafe impl Send for TunDevice {}
unsafe impl Sync for TunDevice {}
impl TunDevice {
pub unsafe fn create<L>(library: L, pool: &str, name: &str) -> io::Result<Self>
where L: Into<libloading::Library>, {
let win_tun = match wintun_raw::wintun::from_library(library) {
Ok(win_tun) => { win_tun }
Err(e) => {
return Err(io::Error::new(io::ErrorKind::Other, format!("library error {:?} ", e)));
}
};
let pool_utf16 = encode_utf16(pool);
if pool_utf16.len() > MAX_POOL {
return Err(io::Error::new(io::ErrorKind::Other, format!("长度大于{}:{:?}", MAX_POOL, pool)));
}
let name_utf16 = encode_utf16(name);
if name_utf16.len() > MAX_POOL {
return Err(io::Error::new(io::ErrorKind::Other, format!("长度大于{}:{:?}", MAX_POOL, pool)));
}
let mut guid_bytes: [u8; 16] = [0u8; 16];
rand::thread_rng().fill(&mut guid_bytes);
let guid = u128::from_ne_bytes(guid_bytes);
//SAFETY: guid is a unique integer so transmuting either all zeroes or the user's preferred
//guid to the winapi guid type is safe and will allow the windows kernel to see our GUID
let guid_struct: wintun_raw::GUID = unsafe { std::mem::transmute(guid) };
let guid_ptr = &guid_struct as *const wintun_raw::GUID;
log::set_default_logger_if_unset(&win_tun);
//SAFETY: the function is loaded from the wintun dll properly, we are providing valid
//pointers, and all the strings are correct null terminated UTF-16. This safety rationale
//applies for all Wintun* functions below
let adapter = win_tun.WintunCreateAdapter(pool_utf16.as_ptr(), name_utf16.as_ptr(), guid_ptr);
if adapter.is_null() {
return Err(io::Error::new(io::ErrorKind::Other, "Failed to crate adapter"));
}
Self::init(win_tun, adapter)
}
pub unsafe fn init(win_tun: wintun_raw::wintun, adapter: wintun_raw::WINTUN_ADAPTER_HANDLE) -> io::Result<Self> {
// 开启session
let session = win_tun.WintunStartSession(adapter, 128 * 1024);
if session.is_null() {
return Err(io::Error::new(io::ErrorKind::Other, "WintunStartSession failed"));
}
//SAFETY: We follow the contract required by CreateEventA. See MSDN
//(the pointers are allowed to be null, and 0 is okay for the others)
let shutdown_event = synchapi::CreateEventA(std::ptr::null_mut(),
0, 0, std::ptr::null_mut());
let read_event = win_tun.WintunGetReadWaitEvent(session) as winnt::HANDLE;
let mut luid: wintun_raw::NET_LUID = std::mem::zeroed();
win_tun.WintunGetAdapterLUID(adapter, &mut luid as *mut wintun_raw::NET_LUID);
let index = ffi::luid_to_index(&std::mem::transmute(luid)).map(|index| index as u32)?;
Ok(TunDevice {
luid: std::mem::transmute(luid),
index,
session,
win_tun,
read_event,
shutdown_event,
adapter,
})
}
pub unsafe fn delete_for_name<L>(library: L, name: &str) -> io::Result<()>
where L: Into<libloading::Library>, {
let win_tun = match wintun_raw::wintun::from_library(library) {
Ok(win_tun) => win_tun,
Err(e) => {
return Err(io::Error::new(io::ErrorKind::Other, format!("library error {:?} ", e)));
}
};
log::set_default_logger_if_unset(&win_tun);
let name_utf16 = encode_utf16(name);
let adapter = win_tun.WintunOpenAdapter(name_utf16.as_ptr());
if adapter.is_null() {
return Err(io::Error::new(io::ErrorKind::Other, "Failed to open adapter"));
}
win_tun.WintunCloseAdapter(adapter);
win_tun.WintunDeleteDriver();
Ok(())
}
pub fn delete(self) -> io::Result<()> {
drop(self);
Ok(())
}
pub fn version(&self) -> io::Result<Version> {
let version = unsafe { self.win_tun.WintunGetRunningDriverVersion() };
if version == 0 {
return Err(io::Error::new(io::ErrorKind::Other, "WintunGetRunningDriverVersion"));
} else {
Ok(Version {
major: ((version >> 16) & 0xFF) as u16,
minor: (version & 0xFF) as u16,
})
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Version {
pub major: u16,
pub minor: u16,
}
// impl TunDevice {
// fn get_adapter_luid(&self) -> u64 {
// let mut luid: wintun_raw::NET_LUID = unsafe { std::mem::zeroed() };
// unsafe { self.win_tun.WintunGetAdapterLUID(self.adapter, &mut luid as *mut wintun_raw::NET_LUID) };
// unsafe { std::mem::transmute(luid) }
// }
// }
impl IFace for TunDevice {
fn shutdown(&self) -> io::Result<()> {
let _ = unsafe { synchapi::SetEvent(self.shutdown_event) };
let _ = unsafe { handleapi::CloseHandle(self.shutdown_event) };
Ok(())
}
fn get_index(&self) -> io::Result<u32> {
Ok(self.index)
}
fn get_name(&self) -> io::Result<String> {
let luid = self.luid;
ffi::luid_to_alias(&unsafe { std::mem::transmute(luid) }).map(|name| {
decode_utf16(&name)
})
}
fn set_name(&self, new_name: &str) -> io::Result<()> {
let name = self.get_name()?;
netsh::set_interface_name(&name, new_name)
}
fn set_ip(&self, address: Ipv4Addr, mask: Ipv4Addr) -> io::Result<()>{
netsh::set_interface_ip(self.get_index()?, &address, &mask)
}
fn add_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr, gateway: Ipv4Addr, metric: u16) -> io::Result<()> {
route::add_route(self.get_index()?, dest, netmask, gateway, metric)
}
fn delete_route(&self, dest: Ipv4Addr, netmask: Ipv4Addr, gateway: Ipv4Addr) -> io::Result<()> {
route::delete_route(self.get_index()?, dest, netmask, gateway)
}
fn set_mtu(&self, mtu: u16) -> io::Result<()> {
netsh::set_interface_mtu(self.get_index()?, mtu)
}
fn set_metric(&self, metric: u16) -> io::Result<()> {
let index = self.get_index()?;
netsh::set_interface_metric(index, metric)
}
}
impl TunDevice {
pub fn try_receive(&self) -> io::Result<Option<packet::TunPacket>> {
let mut size = 0u32;
let bytes_ptr = unsafe {
self.win_tun
.WintunReceivePacket(self.session, &mut size as *mut u32)
};
debug_assert!(size <= u16::MAX as u32);
if bytes_ptr.is_null() {
//Wintun returns ERROR_NO_MORE_ITEMS instead of blocking if packets are not available
let last_error = unsafe { winapi::um::errhandlingapi::GetLastError() };
if last_error == winapi::shared::winerror::ERROR_NO_MORE_ITEMS {
Ok(None)
} else {
Err(io::Error::new(io::ErrorKind::Other, "try_receive failed"))
}
} else {
Ok(Some(packet::TunPacket {
kind: packet::Kind::ReceivePacket,
size: size as usize,
//SAFETY: ptr is non null, aligned for u8, and readable for up to size bytes (which
//must be less than isize::MAX because bytes is a u16
bytes_ptr,
tun_device: Some(&self),
}))
}
}
pub fn receive_blocking(&self) -> io::Result<packet::TunPacket> {
loop {
//Try 5 times to receive without blocking so we don't have to issue a syscall to wait
//for the event if packets are being received at a rapid rate
for _ in 0..5 {
match self.try_receive()? {
None => {
continue;
}
Some(packet) => {
return Ok(packet);
}
}
}
//Wait on both the read handle and the shutdown handle so that we stop when requested
let handles = [self.read_event, self.shutdown_event];
let result = unsafe {
//SAFETY: We abide by the requirements of WaitForMultipleObjects, handles is a
//pointer to valid, aligned, stack memory
synchapi::WaitForMultipleObjects(
2,
&handles as *const winnt::HANDLE,
0,
winbase::INFINITE,
)
};
match result {
winbase::WAIT_FAILED => return Err(io::Error::new(io::ErrorKind::Other, "WAIT_FAILED")),
_ => {
if result == winbase::WAIT_OBJECT_0 {
//We have data!
continue;
} else if result == winbase::WAIT_OBJECT_0 + 1 {
//Shutdown event triggered
return Err(io::Error::new(io::ErrorKind::Other, "Shutdown event triggered"));
}
}
}
}
}
}
impl TunDevice {
pub fn allocate_send_packet(&self, size: u16) -> io::Result<packet::TunPacket> {
let bytes_ptr = unsafe {
self.win_tun.WintunAllocateSendPacket(self.session, size as u32)
};
if bytes_ptr.is_null() {
Err(io::Error::new(io::ErrorKind::Other, "allocate_send_packet failed"))
} else {
Ok(packet::TunPacket {
kind: packet::Kind::SendPacketPending,
size: size as usize,
//SAFETY: ptr is non null, aligned for u8, and readable for up to size bytes (which
//must be less than isize::MAX because bytes is a u16
bytes_ptr,
tun_device: None,
})
}
}
pub fn send_packet(&self, mut packet: packet::TunPacket) {
assert!(matches!(packet.kind, packet::Kind::SendPacketPending));
unsafe {
self.win_tun
.WintunSendPacket(self.session, packet.bytes_ptr)
};
//Mark the packet at sent
packet.kind = packet::Kind::SendPacketSent;
}
}
impl Drop for TunDevice {
fn drop(&mut self) {
//Close adapter on drop
//This is why we need an Arc of wintun
unsafe {
self.win_tun.WintunCloseAdapter(self.adapter);
self.win_tun.WintunDeleteDriver()
};
}
}
+64
View File
@@ -0,0 +1,64 @@
use crate::TunDevice;
pub(crate) enum Kind {
SendPacketPending,
//Send packet type, but not sent yet
SendPacketSent,
//Send packet type - sent
ReceivePacket,
}
/// Represents a wintun packet
pub struct TunPacket<'a> {
pub(crate) kind: Kind,
pub(crate) size:usize,
pub(crate) bytes_ptr: *const u8,
//Share ownership of session to prevent the session from being dropped before packets that
//belong to it
pub(crate) tun_device: Option<&'a TunDevice>,
}
impl <'a>TunPacket<'a> {
/// Returns the bytes this packet holds as &mut.
/// The lifetime of the bytes is tied to the lifetime of this packet.
pub fn bytes_mut(&mut self) -> &mut [u8] {
unsafe { std::slice::from_raw_parts_mut(self.bytes_ptr as *mut u8, self.size) }
}
/// Returns an immutable reference to the bytes this packet holds.
/// The lifetime of the bytes is tied to the lifetime of this packet.
pub fn bytes(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.bytes_ptr,self.size) }
}
}
impl <'a>Drop for TunPacket<'a> {
fn drop(&mut self) {
match self.kind {
Kind::ReceivePacket => {
unsafe {
//SAFETY:
//
// 1. We share ownership of the session therefore it hasn't been dropped yet
// 2. Bytes is valid because each packet holds exclusive access to a region of the
// ring buffer that the wintun session owns. We return that region of
// memory back to wintun here
let tun_device = self.tun_device.unwrap();
tun_device.win_tun
.WintunReleaseReceivePacket(tun_device.session, self.bytes_ptr)
};
}
Kind::SendPacketPending => {
//If someone allocates a packet with session.allocate_send_packet() and then it is
//dropped without being sent, this will hold up the send queue because wintun expects
//that every allocated packet is sent
panic!("Packet was never sent!");
}
Kind::SendPacketSent => {
//Nop
}
}
}
}
@@ -1,5 +1,4 @@
/* automatically generated by rust-bindgen 0.59.1 */
#[repr(C)]
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct __BindgenBitfieldUnit<Storage> {
@@ -12,8 +11,8 @@ impl<Storage> __BindgenBitfieldUnit<Storage> {
}
}
impl<Storage> __BindgenBitfieldUnit<Storage>
where
Storage: AsRef<[u8]> + AsMut<[u8]>,
where
Storage: AsRef<[u8]> + AsMut<[u8]>,
{
#[inline]
pub fn get_bit(&self, index: usize) -> bool {
@@ -113,40 +112,40 @@ fn bindgen_test_layout__GUID() {
unsafe { &(*(::std::ptr::null::<_GUID>())).Data1 as *const _ as usize },
0usize,
concat!(
"Offset of field: ",
stringify!(_GUID),
"::",
stringify!(Data1)
"Offset of field: ",
stringify!(_GUID),
"::",
stringify!(Data1)
)
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<_GUID>())).Data2 as *const _ as usize },
4usize,
concat!(
"Offset of field: ",
stringify!(_GUID),
"::",
stringify!(Data2)
"Offset of field: ",
stringify!(_GUID),
"::",
stringify!(Data2)
)
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<_GUID>())).Data3 as *const _ as usize },
6usize,
concat!(
"Offset of field: ",
stringify!(_GUID),
"::",
stringify!(Data3)
"Offset of field: ",
stringify!(_GUID),
"::",
stringify!(Data3)
)
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<_GUID>())).Data4 as *const _ as usize },
8usize,
concat!(
"Offset of field: ",
stringify!(_GUID),
"::",
stringify!(Data4)
"Offset of field: ",
stringify!(_GUID),
"::",
stringify!(Data4)
)
);
}
@@ -249,20 +248,20 @@ fn bindgen_test_layout__NET_LUID_LH() {
unsafe { &(*(::std::ptr::null::<_NET_LUID_LH>())).Value as *const _ as usize },
0usize,
concat!(
"Offset of field: ",
stringify!(_NET_LUID_LH),
"::",
stringify!(Value)
"Offset of field: ",
stringify!(_NET_LUID_LH),
"::",
stringify!(Value)
)
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<_NET_LUID_LH>())).Info as *const _ as usize },
0usize,
concat!(
"Offset of field: ",
stringify!(_NET_LUID_LH),
"::",
stringify!(Info)
"Offset of field: ",
stringify!(_NET_LUID_LH),
"::",
stringify!(Info)
)
);
}
@@ -311,33 +310,33 @@ pub struct wintun {
pub WintunCloseAdapter: unsafe extern "C" fn(arg1: WINTUN_ADAPTER_HANDLE),
pub WintunOpenAdapter: unsafe extern "C" fn(arg1: LPCWSTR) -> WINTUN_ADAPTER_HANDLE,
pub WintunGetAdapterLUID:
unsafe extern "C" fn(arg1: WINTUN_ADAPTER_HANDLE, arg2: *mut NET_LUID),
unsafe extern "C" fn(arg1: WINTUN_ADAPTER_HANDLE, arg2: *mut NET_LUID),
pub WintunGetRunningDriverVersion: unsafe extern "C" fn() -> DWORD,
pub WintunDeleteDriver: unsafe extern "C" fn() -> BOOL,
pub WintunSetLogger: unsafe extern "C" fn(arg1: WINTUN_LOGGER_CALLBACK),
pub WintunStartSession:
unsafe extern "C" fn(arg1: WINTUN_ADAPTER_HANDLE, arg2: DWORD) -> WINTUN_SESSION_HANDLE,
unsafe extern "C" fn(arg1: WINTUN_ADAPTER_HANDLE, arg2: DWORD) -> WINTUN_SESSION_HANDLE,
pub WintunEndSession: unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE),
pub WintunGetReadWaitEvent: unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE) -> HANDLE,
pub WintunReceivePacket:
unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE, arg2: *mut DWORD) -> *mut BYTE,
unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE, arg2: *mut DWORD) -> *mut BYTE,
pub WintunReleaseReceivePacket:
unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE, arg2: *const BYTE),
unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE, arg2: *const BYTE),
pub WintunAllocateSendPacket:
unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE, arg2: DWORD) -> *mut BYTE,
unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE, arg2: DWORD) -> *mut BYTE,
pub WintunSendPacket: unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE, arg2: *const BYTE),
}
impl wintun {
pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
where
P: AsRef<::std::ffi::OsStr>,
where
P: AsRef<::std::ffi::OsStr>,
{
let library = ::libloading::Library::new(path)?;
Self::from_library(library)
}
pub unsafe fn from_library<L>(library: L) -> Result<Self, ::libloading::Error>
where
L: Into<::libloading::Library>,
where
L: Into<::libloading::Library>,
{
let __library = library.into();
let WintunCreateAdapter = __library.get(b"WintunCreateAdapter\0").map(|sym| *sym)?;