Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18df3c2c92 | ||
|
|
c08b9cefe9 | ||
|
|
258a35740f | ||
|
|
32cfe9a3a8 | ||
|
|
930a4fcf29 | ||
|
|
5a778d5fc3 | ||
|
|
1d64cfc930 | ||
|
|
3eab02bc68 | ||
|
|
dcdd03b746 | ||
|
|
51acb2da9b | ||
|
|
0d2c107e20 | ||
|
|
2089ba7997 | ||
|
|
8a032f86d8 | ||
|
|
5dcda4d088 | ||
|
|
8d76214193 | ||
|
|
24ddf04d7f | ||
|
|
79fa2a0823 | ||
|
|
ddfb5ac943 | ||
|
|
ed622409b0 | ||
|
|
b6beaa09ec | ||
|
|
7840f30b64 | ||
|
|
d7226d8522 | ||
|
|
8d5c9cedb3 | ||
|
|
6f7992ea9f | ||
|
|
5b2c2435d5 | ||
|
|
ebf84db204 | ||
|
|
94e83d80a6 | ||
|
|
bc80d5f641 | ||
|
|
81a5480250 | ||
|
|
e69d416dc4 | ||
|
|
15370203a1 | ||
|
|
8ca09d72bd | ||
|
|
d16cbe4ac6 | ||
|
|
49ac8827cd | ||
|
|
f2f686d31f | ||
|
|
6872ec3618 | ||
|
|
efb7053931 | ||
|
|
80109fd8aa | ||
|
|
b3abb175c1 | ||
|
|
f05577c927 | ||
|
|
c631f2bb0d | ||
|
|
ad5aac7ef5 | ||
|
|
5cd9179ce7 | ||
|
|
af58c3990d | ||
|
|
206c543e8c | ||
|
|
9820c56be6 | ||
|
|
2232965a1d | ||
|
|
6bfadfe5b8 | ||
|
|
c655d9650b | ||
|
|
61086089b3 | ||
|
|
c89d0124a1 | ||
|
|
a1a5839b20 | ||
|
|
c9e3dc83ab | ||
|
|
c179670b14 | ||
|
|
292893e9dd | ||
|
|
d956e493af | ||
|
|
05c85db52a | ||
|
|
49350933c3 | ||
|
|
fa3fed3fbd | ||
|
|
5df9a357ec | ||
|
|
bce9c57e60 | ||
|
|
f39de52c65 | ||
|
|
4001527a87 | ||
|
|
85be89ddf0 |
@@ -0,0 +1,149 @@
|
||||
name: Rust
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
defaults:
|
||||
run:
|
||||
# necessary for windows
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
# test:
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - name: Init submodules
|
||||
# uses: snickerbockers/submodules-init@v4
|
||||
# - name: Cargo cache
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: |
|
||||
# ~/.cargo/registry
|
||||
# ./target
|
||||
# key: test-cargo-registry
|
||||
# - name: List
|
||||
# run: find ./
|
||||
# - name: Run tests
|
||||
# run: cargo test --verbose
|
||||
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# a list of all the targets
|
||||
include:
|
||||
- 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
|
||||
OS: ubuntu-latest
|
||||
- TARGET: aarch64-unknown-linux-gnu # tested on aws t4g.nano
|
||||
OS: ubuntu-latest
|
||||
- TARGET: aarch64-unknown-linux-musl # tested on aws t4g.nano in alpine container
|
||||
OS: ubuntu-latest
|
||||
- TARGET: armv7-unknown-linux-gnueabihf # raspberry pi 2-3-4, not tested
|
||||
OS: ubuntu-latest
|
||||
- TARGET: armv7-unknown-linux-musleabihf # raspberry pi 2-3-4, not tested
|
||||
OS: ubuntu-latest
|
||||
- TARGET: arm-unknown-linux-gnueabihf # raspberry pi 0-1, not tested
|
||||
OS: ubuntu-latest
|
||||
- TARGET: arm-unknown-linux-musleabihf # raspberry pi 0-1, not tested
|
||||
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: i686-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
|
||||
TARGET: ${{ matrix.TARGET }}
|
||||
OS: ${{ matrix.OS }}
|
||||
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: build-cargo-registry-${{matrix.TARGET}}
|
||||
- name: List
|
||||
run: find ./
|
||||
- name: Install and configure dependencies
|
||||
run: |
|
||||
# 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
|
||||
fi
|
||||
|
||||
# some additional configuration for cross-compilation on linux
|
||||
cat >>~/.cargo/config <<EOF
|
||||
[target.aarch64-unknown-linux-gnu]
|
||||
linker = "aarch64-linux-gnu-gcc"
|
||||
[target.aarch64-unknown-linux-musl]
|
||||
linker = "aarch64-linux-gnu-gcc"
|
||||
[target.armv7-unknown-linux-gnueabihf]
|
||||
linker = "arm-linux-gnueabihf-gcc"
|
||||
[target.armv7-unknown-linux-musleabihf]
|
||||
linker = "arm-linux-gnueabihf-gcc"
|
||||
[target.arm-unknown-linux-gnueabihf]
|
||||
linker = "arm-linux-gnueabihf-gcc"
|
||||
[target.arm-unknown-linux-musleabihf]
|
||||
linker = "arm-linux-gnueabihf-gcc"
|
||||
EOF
|
||||
- name: Install rust target
|
||||
run: rustup target add $TARGET
|
||||
- name: Run build
|
||||
run: cargo build --package switch-desktop --release --verbose --target $TARGET
|
||||
- name: List target
|
||||
run: find ./target
|
||||
- name: Compress
|
||||
run: |
|
||||
mkdir -p ./artifacts
|
||||
# windows is the only OS using a different convention for executable file name
|
||||
if [[ $OS =~ ^windows.*$ ]]; then
|
||||
EXEC=$NAME.exe
|
||||
else
|
||||
EXEC=$NAME
|
||||
fi
|
||||
if [[ $GITHUB_REF_TYPE =~ ^tag$ ]]; then
|
||||
TAG=$GITHUB_REF_NAME
|
||||
else
|
||||
TAG=$GITHUB_SHA
|
||||
fi
|
||||
mv ./target/$TARGET/release/$EXEC ./$EXEC
|
||||
tar -czf ./artifacts/$NAME-$TARGET-$TAG.tar.gz $EXEC
|
||||
- name: Archive artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: switch-desktop
|
||||
path: |
|
||||
./artifacts
|
||||
|
||||
# deploys to github releases on tag
|
||||
deploy:
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: switch-desktop
|
||||
path: ./artifacts
|
||||
- name: List
|
||||
run: find ./artifacts
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: ./artifacts/*.tar.gz
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "switch/p2p_channel"]
|
||||
path = switch/p2p_channel
|
||||
url = https://github.com/lbl8603/p2p_channel
|
||||
+2
-44
@@ -1,44 +1,2 @@
|
||||
[package]
|
||||
name = "switch"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
packet = { path = "./packet" }
|
||||
bytes = "1.3.0"
|
||||
|
||||
libc = "0.2.137"
|
||||
|
||||
dashmap = "5.4.0"
|
||||
crossbeam = "0.8.2"
|
||||
parking_lot = "0.12.1"
|
||||
|
||||
rsa = "0.7.2"
|
||||
rand = "0.8.5"
|
||||
sha2 = { version = "0.10.6", features = ["oid"] }
|
||||
#colored = "2.0.0"
|
||||
|
||||
thiserror = "1.0.37"
|
||||
chrono = "0.4.23"
|
||||
lazy_static = "1.4.0"
|
||||
moka = "0.9.6"
|
||||
protobuf = "3.2.0"
|
||||
|
||||
console = "0.15.2"
|
||||
mac_address = "1.1.4"
|
||||
clap = { version = "4.0.32", features = ["derive"] }
|
||||
[target.'cfg(any(unix))'.dependencies]
|
||||
tun = { path = "./rust-tun" }
|
||||
sudo = "0.6.0"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
winapi = { version = "0.3.9", features = ["handleapi", "processthreadsapi", "winnt", "securitybaseapi", "impl-default"] }
|
||||
wintun = "0.2.1"
|
||||
libloading = "0.7.4"
|
||||
runas = "0.2.1"
|
||||
|
||||
[build-dependencies]
|
||||
protobuf-codegen = "3.2.0"
|
||||
protoc-bin-vendored = "3.0.0"
|
||||
[workspace]
|
||||
members = ["switch","switch-desktop"]
|
||||
|
||||
@@ -1,25 +1,68 @@
|
||||
# switch
|
||||
Virtual Network Tools
|
||||
A virtual network tool (VPN)
|
||||
|
||||
将不同网络下的设备虚拟到一个局域网下
|
||||
将不同网络下的多个设备虚拟到一个局域网下
|
||||
|
||||
|
||||
### 示例:
|
||||
|
||||
- 在一台mac设备上运行,获取到ip 10.13.0.2:
|
||||
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 &
|
||||
# windows上
|
||||
D:\switch\bin_v1>switch-desktop.exe start --token 123456
|
||||
```
|
||||
2. 可以执行status命令查看当前设备的虚拟ip
|
||||
```shell
|
||||
root@DESKTOP-0BCHNIO:/opt# ./switch-desktop status
|
||||
Name: Ubuntu 18.04 (bionic) [64-bit]
|
||||
Virtual ip: 10.26.0.2
|
||||
Virtual gateway: 10.26.0.1
|
||||
Virtual netmask: 255.255.255.0
|
||||
Connection status: Connected
|
||||
NAT type: Cone
|
||||
Relay server: 43.139.56.10:29871
|
||||
Public ips: 120.228.76.75
|
||||
Local ip: 172.25.165.58
|
||||
```
|
||||
3. 也可以执行list命令查看其他设备的虚拟ip
|
||||
```shell
|
||||
root@DESKTOP-0BCHNIO:/opt# ./switch-desktop 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="图片" src="https://user-images.githubusercontent.com/49143209/210379090-a3f21007-5a12-44d3-81d6-a69495209ea7.png">
|
||||
<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">
|
||||
|
||||
- 在另一台windows上运行,获取到ip 10.13.0.3:
|
||||
### 更多玩法
|
||||
|
||||

|
||||
1. 和远程桌面(如mstsc)搭配,超低延迟的体验
|
||||
2. 安装samba服务,共享磁盘
|
||||
3. 搭配公网服务器nginx反向代理,在公网访问本地文件
|
||||
|
||||
- 此时这两个设备之间就能用ip相互访问了
|
||||
|
||||
<img width="437" alt="图片" src="https://user-images.githubusercontent.com/49143209/210380969-4a7c0f23-1e88-4ab6-9cc2-0c0f086848ac.png">
|
||||
|
||||
### 使用须知
|
||||
- token的作用是标识一个虚拟局域网,当使用公共服务器时,建议使用一个唯一值当token(比如uuid),否则有可能连接到其他人创建的虚拟局域网中
|
||||
- 建议指定deviceId,默认使用MAC地址,在某些环境下可能发生变化
|
||||
- 公共服务器目前的配置是2核4G 4Mbps,有需要再扩展~
|
||||
- 需要root/管理员权限
|
||||
- 使用命令行运行
|
||||
- Mac和Linux下需要加可执行权限(例如:chmod +x ./switch-macos)
|
||||
- 自己搭注册和中继服务器(https://github.com/lbl8603/switch-server)
|
||||
### 编译
|
||||
前提条件:安装rust编译环境(https://www.rust-lang.org/zh-CN/tools/install)
|
||||
|
||||
到项目根目录下执行 cargo build -p switch-desktop
|
||||
|
||||
### 支持平台
|
||||
- Mac
|
||||
- Linux
|
||||
@@ -32,7 +75,9 @@ Virtual Network Tools
|
||||
- NAT穿透
|
||||
- 点对点穿透
|
||||
- 服务端中继转发
|
||||
- 客户端中继转发
|
||||
|
||||
### Todo
|
||||
- 支持安卓
|
||||
- 数据加密
|
||||
- 客户端中继转发
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 97 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
@@ -1,106 +0,0 @@
|
||||
TUN interfaces [](https://crates.io/crates/tun)  
|
||||
==============
|
||||
This crate allows the creation and usage of TUN interfaces, the aim is to make this cross-platform.
|
||||
|
||||
Usage
|
||||
-----
|
||||
First, add the following to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tun = "0.5"
|
||||
```
|
||||
|
||||
Next, add this to your crate root:
|
||||
|
||||
```rust
|
||||
extern crate tun;
|
||||
```
|
||||
|
||||
If you want to use the TUN interface with mio/tokio, you need to enable the `async` feature:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tun = { version = "0.5", features = ["async"] }
|
||||
```
|
||||
|
||||
Example
|
||||
-------
|
||||
The following example creates and configures a TUN interface and starts reading
|
||||
packets from it.
|
||||
|
||||
```rust
|
||||
use std::io::Read;
|
||||
|
||||
extern crate tun;
|
||||
|
||||
fn main() {
|
||||
let mut config = tun::Configuration::default();
|
||||
config.address((10, 0, 0, 1))
|
||||
.netmask((255, 255, 255, 0))
|
||||
.up();
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
config.platform(|config| {
|
||||
config.packet_information(true);
|
||||
});
|
||||
|
||||
let mut dev = tun::create(&config).unwrap();
|
||||
let mut buf = [0; 4096];
|
||||
|
||||
loop {
|
||||
let amount = dev.read(&mut buf).unwrap();
|
||||
println!("{:?}", &buf[0 .. amount]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Platforms
|
||||
=========
|
||||
Not every platform is supported.
|
||||
|
||||
Linux
|
||||
-----
|
||||
You will need the `tun` module to be loaded and root is required to create
|
||||
interfaces.
|
||||
|
||||
macOS
|
||||
-----
|
||||
It just werks, but you have to set up routing manually.
|
||||
|
||||
iOS
|
||||
----
|
||||
You can pass the file descriptor of the TUN device to `rust-tun` to create the interface.
|
||||
|
||||
Here is an example to create the TUN device on iOS and pass the `fd` to `rust-tun`:
|
||||
```swift
|
||||
// Swift
|
||||
class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
|
||||
let tunnelNetworkSettings = createTunnelSettings() // Configure TUN address, DNS, mtu, routing...
|
||||
setTunnelNetworkSettings(tunnelNetworkSettings) { [weak self] error in
|
||||
let tunFd = self?.packetFlow.value(forKeyPath: "socket.fileDescriptor") as! Int32
|
||||
DispatchQueue.global(qos: .default).async {
|
||||
start_tun(tunFd)
|
||||
}
|
||||
completionHandler(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```rust
|
||||
#[no_mangle]
|
||||
pub extern "C" fn start_tun(fd: std::os::raw::c_int) {
|
||||
let mut rt = tokio::runtime::Runtime::new().unwrap();
|
||||
rt.block_on(async {
|
||||
let mut cfg = tun::Configuration::default();
|
||||
cfg.raw_fd(fd);
|
||||
let mut tun = tun::create_as_async(&cfg).unwrap();
|
||||
let mut framed = tun.into_framed();
|
||||
while let Some(packet) = framed.next().await {
|
||||
...
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
@@ -1,78 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use packet::{builder::Builder, icmp, ip, Packet};
|
||||
use tun::{self, Configuration, TunPacket};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let mut config = Configuration::default();
|
||||
|
||||
config
|
||||
.address((10, 0, 0, 1))
|
||||
.netmask((255, 255, 255, 0))
|
||||
.up();
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
config.platform(|config| {
|
||||
config.packet_information(true);
|
||||
});
|
||||
|
||||
let dev = tun::create_as_async(&config).unwrap();
|
||||
|
||||
let mut framed = dev.into_framed();
|
||||
|
||||
while let Some(packet) = framed.next().await {
|
||||
match packet {
|
||||
Ok(pkt) => match ip::Packet::new(pkt.get_bytes()) {
|
||||
Ok(ip::Packet::V4(pkt)) => match icmp::Packet::new(pkt.payload()) {
|
||||
Ok(icmp) => match icmp.echo() {
|
||||
Ok(icmp) => {
|
||||
let reply = ip::v4::Builder::default()
|
||||
.id(0x42)
|
||||
.unwrap()
|
||||
.ttl(64)
|
||||
.unwrap()
|
||||
.source(pkt.destination())
|
||||
.unwrap()
|
||||
.destination(pkt.source())
|
||||
.unwrap()
|
||||
.icmp()
|
||||
.unwrap()
|
||||
.echo()
|
||||
.unwrap()
|
||||
.reply()
|
||||
.unwrap()
|
||||
.identifier(icmp.identifier())
|
||||
.unwrap()
|
||||
.sequence(icmp.sequence())
|
||||
.unwrap()
|
||||
.payload(icmp.payload())
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap();
|
||||
framed.send(TunPacket::new(reply)).await.unwrap();
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
},
|
||||
Err(err) => println!("Received an invalid packet: {:?}", err),
|
||||
_ => {}
|
||||
},
|
||||
Err(err) => panic!("Error: {:?}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use bytes::BytesMut;
|
||||
use futures::StreamExt;
|
||||
use packet::{ip::Packet, Error};
|
||||
use tokio_util::codec::{Decoder, FramedRead};
|
||||
|
||||
pub struct IPPacketCodec;
|
||||
|
||||
impl Decoder for IPPacketCodec {
|
||||
type Item = Packet<BytesMut>;
|
||||
type Error = Error;
|
||||
|
||||
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
if buf.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let buf = buf.split_to(buf.len());
|
||||
Ok(match Packet::no_payload(buf) {
|
||||
Ok(pkt) => Some(pkt),
|
||||
Err(err) => {
|
||||
println!("error {:?}", err);
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let mut config = tun::Configuration::default();
|
||||
|
||||
config
|
||||
.address((10, 0, 0, 1))
|
||||
.netmask((255, 255, 255, 0))
|
||||
.up();
|
||||
|
||||
let dev = tun::create_as_async(&config).unwrap();
|
||||
|
||||
let mut stream = FramedRead::new(dev, IPPacketCodec);
|
||||
|
||||
while let Some(packet) = stream.next().await {
|
||||
match packet {
|
||||
Ok(pkt) => println!("pkt: {:#?}", pkt),
|
||||
Err(err) => panic!("Error: {:?}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use futures::StreamExt;
|
||||
use packet::ip::Packet;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let mut config = tun::Configuration::default();
|
||||
|
||||
config
|
||||
.address((10, 0, 0, 1))
|
||||
.netmask((255, 255, 255, 0))
|
||||
.up();
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
config.platform(|config| {
|
||||
config.packet_information(true);
|
||||
});
|
||||
|
||||
let dev = tun::create_as_async(&config).unwrap();
|
||||
|
||||
let mut stream = dev.into_framed();
|
||||
|
||||
while let Some(packet) = stream.next().await {
|
||||
match packet {
|
||||
Ok(pkt) => println!("pkt: {:#?}", Packet::unchecked(pkt.get_bytes())),
|
||||
Err(err) => panic!("Error: {:?}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::io::Read;
|
||||
|
||||
fn main() {
|
||||
let mut config = tun::Configuration::default();
|
||||
|
||||
config
|
||||
.address((10, 0, 0, 1))
|
||||
.netmask((255, 255, 255, 0))
|
||||
.up();
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
config.platform(|config| {
|
||||
config.packet_information(true);
|
||||
});
|
||||
|
||||
let mut dev = tun::create(&config).unwrap();
|
||||
let mut buf = [0; 4096];
|
||||
|
||||
loop {
|
||||
let amount = dev.read(&mut buf).unwrap();
|
||||
println!("{:?}", &buf[0..amount]);
|
||||
}
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::io;
|
||||
|
||||
use byteorder::{NativeEndian, NetworkEndian, WriteBytesExt};
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
use tokio_util::codec::{Decoder, Encoder};
|
||||
|
||||
/// A packet protocol IP version
|
||||
#[derive(Debug)]
|
||||
enum PacketProtocol {
|
||||
IPv4,
|
||||
IPv6,
|
||||
Other(u8),
|
||||
}
|
||||
|
||||
// Note: the protocol in the packet information header is platform dependent.
|
||||
impl PacketProtocol {
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn into_pi_field(&self) -> Result<u16, io::Error> {
|
||||
match self {
|
||||
PacketProtocol::IPv4 => Ok(libc::ETH_P_IP as u16),
|
||||
PacketProtocol::IPv6 => Ok(libc::ETH_P_IPV6 as u16),
|
||||
PacketProtocol::Other(_) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"neither an IPv4 or IPv6 packet",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
fn into_pi_field(&self) -> Result<u16, io::Error> {
|
||||
match self {
|
||||
PacketProtocol::IPv4 => Ok(libc::PF_INET as u16),
|
||||
PacketProtocol::IPv6 => Ok(libc::PF_INET6 as u16),
|
||||
PacketProtocol::Other(_) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"neither an IPv4 or IPv6 packet",
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A Tun Packet to be sent or received on the TUN interface.
|
||||
#[derive(Debug)]
|
||||
pub struct TunPacket(PacketProtocol, Bytes);
|
||||
|
||||
/// Infer the protocol based on the first nibble in the packet buffer.
|
||||
fn infer_proto(buf: &[u8]) -> PacketProtocol {
|
||||
match buf[0] >> 4 {
|
||||
4 => PacketProtocol::IPv4,
|
||||
6 => PacketProtocol::IPv6,
|
||||
p => PacketProtocol::Other(p),
|
||||
}
|
||||
}
|
||||
|
||||
impl TunPacket {
|
||||
/// Create a new `TunPacket` based on a byte slice.
|
||||
pub fn new(bytes: Vec<u8>) -> TunPacket {
|
||||
let proto = infer_proto(&bytes);
|
||||
TunPacket(proto, Bytes::from(bytes))
|
||||
}
|
||||
|
||||
/// Return this packet's bytes.
|
||||
pub fn get_bytes(&self) -> &[u8] {
|
||||
&self.1
|
||||
}
|
||||
|
||||
pub fn into_bytes(self) -> Bytes {
|
||||
self.1
|
||||
}
|
||||
}
|
||||
|
||||
/// A TunPacket Encoder/Decoder.
|
||||
pub struct TunPacketCodec(bool, i32);
|
||||
|
||||
impl TunPacketCodec {
|
||||
/// Create a new `TunPacketCodec` specifying whether the underlying
|
||||
/// tunnel Device has enabled the packet information header.
|
||||
pub fn new(pi: bool, mtu: i32) -> TunPacketCodec {
|
||||
TunPacketCodec(pi, mtu)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decoder for TunPacketCodec {
|
||||
type Item = TunPacket;
|
||||
type Error = io::Error;
|
||||
|
||||
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
if buf.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut pkt = buf.split_to(buf.len());
|
||||
|
||||
// reserve enough space for the next packet
|
||||
if self.0 {
|
||||
buf.reserve(self.1 as usize + 4);
|
||||
} else {
|
||||
buf.reserve(self.1 as usize);
|
||||
}
|
||||
|
||||
// if the packet information is enabled we have to ignore the first 4 bytes
|
||||
if self.0 {
|
||||
let _ = pkt.split_to(4);
|
||||
}
|
||||
|
||||
let proto = infer_proto(pkt.as_ref());
|
||||
Ok(Some(TunPacket(proto, pkt.freeze())))
|
||||
}
|
||||
}
|
||||
|
||||
impl Encoder<TunPacket> for TunPacketCodec {
|
||||
type Error = io::Error;
|
||||
|
||||
fn encode(&mut self, item: TunPacket, dst: &mut BytesMut) -> Result<(), Self::Error> {
|
||||
dst.reserve(item.get_bytes().len() + 4);
|
||||
match item {
|
||||
TunPacket(proto, bytes) if self.0 => {
|
||||
// build the packet information header comprising of 2 u16
|
||||
// fields: flags and protocol.
|
||||
let mut buf = Vec::<u8>::with_capacity(4);
|
||||
|
||||
// flags is always 0
|
||||
buf.write_u16::<NativeEndian>(0).unwrap();
|
||||
// write the protocol as network byte order
|
||||
buf.write_u16::<NetworkEndian>(proto.into_pi_field()?)
|
||||
.unwrap();
|
||||
|
||||
dst.put_slice(&buf);
|
||||
dst.put(bytes);
|
||||
}
|
||||
TunPacket(_, bytes) => dst.put(bytes),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::io;
|
||||
use std::io::{IoSlice, Read, Write};
|
||||
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use futures_core::ready;
|
||||
use tokio::io::unix::AsyncFd;
|
||||
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use tokio_util::codec::Framed;
|
||||
|
||||
use crate::device::Device as D;
|
||||
use crate::platform::{Device, Queue};
|
||||
use crate::r#async::codec::*;
|
||||
|
||||
/// An async TUN device wrapper around a TUN device.
|
||||
pub struct AsyncDevice {
|
||||
inner: AsyncFd<Device>,
|
||||
}
|
||||
|
||||
impl AsyncDevice {
|
||||
/// Create a new `AsyncDevice` wrapping around a `Device`.
|
||||
pub fn new(device: Device) -> io::Result<AsyncDevice> {
|
||||
device.set_nonblock()?;
|
||||
Ok(AsyncDevice {
|
||||
inner: AsyncFd::new(device)?,
|
||||
})
|
||||
}
|
||||
/// Returns a shared reference to the underlying Device object
|
||||
pub fn get_ref(&self) -> &Device {
|
||||
self.inner.get_ref()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying Device object
|
||||
pub fn get_mut(&mut self) -> &mut Device {
|
||||
self.inner.get_mut()
|
||||
}
|
||||
|
||||
/// Consumes this AsyncDevice and return a Framed object (unified Stream and Sink interface)
|
||||
pub fn into_framed(mut self) -> Framed<Self, TunPacketCodec> {
|
||||
let pi = self.get_mut().has_packet_information();
|
||||
let codec = TunPacketCodec::new(pi, self.inner.get_ref().mtu().unwrap_or(1504));
|
||||
Framed::new(self, codec)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for AsyncDevice {
|
||||
fn poll_read(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut ReadBuf,
|
||||
) -> Poll<io::Result<()>> {
|
||||
loop {
|
||||
let mut guard = ready!(self.inner.poll_read_ready_mut(cx))?;
|
||||
let rbuf = buf.initialize_unfilled();
|
||||
match guard.try_io(|inner| inner.get_mut().read(rbuf)) {
|
||||
Ok(res) => return Poll::Ready(res.map(|n| buf.advance(n))),
|
||||
Err(_wb) => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for AsyncDevice {
|
||||
fn poll_write(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
loop {
|
||||
let mut guard = ready!(self.inner.poll_write_ready_mut(cx))?;
|
||||
match guard.try_io(|inner| inner.get_mut().write(buf)) {
|
||||
Ok(res) => return Poll::Ready(res),
|
||||
Err(_wb) => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
loop {
|
||||
let mut guard = ready!(self.inner.poll_write_ready_mut(cx))?;
|
||||
match guard.try_io(|inner| inner.get_mut().flush()) {
|
||||
Ok(res) => return Poll::Ready(res),
|
||||
Err(_wb) => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn poll_write_vectored(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
bufs: &[IoSlice<'_>],
|
||||
) -> Poll<Result<usize, io::Error>> {
|
||||
loop {
|
||||
let mut guard = ready!(self.inner.poll_write_ready_mut(cx))?;
|
||||
match guard.try_io(|inner| inner.get_mut().write_vectored(bufs)) {
|
||||
Ok(res) => return Poll::Ready(res),
|
||||
Err(_wb) => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// An async TUN device queue wrapper around a TUN device queue.
|
||||
pub struct AsyncQueue {
|
||||
inner: AsyncFd<Queue>,
|
||||
}
|
||||
|
||||
impl AsyncQueue {
|
||||
/// Create a new `AsyncQueue` wrapping around a `Queue`.
|
||||
pub fn new(queue: Queue) -> io::Result<AsyncQueue> {
|
||||
queue.set_nonblock()?;
|
||||
Ok(AsyncQueue {
|
||||
inner: AsyncFd::new(queue)?,
|
||||
})
|
||||
}
|
||||
/// Returns a shared reference to the underlying Queue object
|
||||
pub fn get_ref(&self) -> &Queue {
|
||||
self.inner.get_ref()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying Queue object
|
||||
pub fn get_mut(&mut self) -> &mut Queue {
|
||||
self.inner.get_mut()
|
||||
}
|
||||
|
||||
/// Consumes this AsyncQueue and return a Framed object (unified Stream and Sink interface)
|
||||
pub fn into_framed(mut self) -> Framed<Self, TunPacketCodec> {
|
||||
let pi = self.get_mut().has_packet_information();
|
||||
let codec = TunPacketCodec::new(pi, 1504);
|
||||
Framed::new(self, codec)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for AsyncQueue {
|
||||
fn poll_read(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut ReadBuf,
|
||||
) -> Poll<io::Result<()>> {
|
||||
loop {
|
||||
let mut guard = ready!(self.inner.poll_read_ready_mut(cx))?;
|
||||
let rbuf = buf.initialize_unfilled();
|
||||
match guard.try_io(|inner| inner.get_mut().read(rbuf)) {
|
||||
Ok(res) => return Poll::Ready(res.map(|n| buf.advance(n))),
|
||||
Err(_wb) => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for AsyncQueue {
|
||||
fn poll_write(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
loop {
|
||||
let mut guard = ready!(self.inner.poll_write_ready_mut(cx))?;
|
||||
match guard.try_io(|inner| inner.get_mut().write(buf)) {
|
||||
Ok(res) => return Poll::Ready(res),
|
||||
Err(_wb) => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
loop {
|
||||
let mut guard = ready!(self.inner.poll_write_ready_mut(cx))?;
|
||||
match guard.try_io(|inner| inner.get_mut().flush()) {
|
||||
Ok(res) => return Poll::Ready(res),
|
||||
Err(_wb) => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
//! Async specific modules.
|
||||
|
||||
use crate::error;
|
||||
|
||||
use crate::configuration::Configuration;
|
||||
use crate::platform::create;
|
||||
|
||||
mod device;
|
||||
pub use self::device::{AsyncDevice, AsyncQueue};
|
||||
|
||||
mod codec;
|
||||
pub use self::codec::{TunPacket, TunPacketCodec};
|
||||
|
||||
/// Create a TUN device with the given name.
|
||||
pub fn create_as_async(configuration: &Configuration) -> Result<AsyncDevice, error::Error> {
|
||||
let device = create(&configuration)?;
|
||||
AsyncDevice::new(device).map_err(|err| err.into())
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
#![allow(unused_variables)]
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::configuration::Configuration;
|
||||
use crate::device::Device as D;
|
||||
use crate::error::*;
|
||||
use crate::platform::posix::{self, Fd};
|
||||
|
||||
/// A TUN device for Android.
|
||||
pub struct Device {
|
||||
queue: Queue,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
/// Create a new `Device` for the given `Configuration`.
|
||||
pub fn new(config: &Configuration) -> Result<Self> {
|
||||
let fd = match config.raw_fd {
|
||||
Some(raw_fd) => raw_fd,
|
||||
_ => return Err(Error::InvalidConfig),
|
||||
};
|
||||
let device = {
|
||||
let tun = Fd::new(fd).map_err(|_| io::Error::last_os_error())?;
|
||||
|
||||
Device {
|
||||
queue: Queue { tun: tun },
|
||||
}
|
||||
};
|
||||
Ok(device)
|
||||
}
|
||||
|
||||
/// Split the interface into a `Reader` and `Writer`.
|
||||
pub fn split(self) -> (posix::Reader, posix::Writer) {
|
||||
let fd = Arc::new(self.queue.tun);
|
||||
(posix::Reader(fd.clone()), posix::Writer(fd.clone()))
|
||||
}
|
||||
|
||||
/// Return whether the device has packet information
|
||||
pub fn has_packet_information(&self) -> bool {
|
||||
self.queue.has_packet_information()
|
||||
}
|
||||
|
||||
/// Set non-blocking mode
|
||||
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||
self.queue.set_nonblock()
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Device {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.queue.tun.read(buf)
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.queue.tun.read_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Device {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.queue.tun.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.queue.tun.flush()
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.queue.tun.write_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl D for Device {
|
||||
type Queue = Queue;
|
||||
|
||||
fn name(&self) -> &str {
|
||||
return "";
|
||||
}
|
||||
|
||||
fn set_name(&mut self, value: &str) -> Result<()> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn enabled(&mut self, value: bool) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn address(&self) -> Result<Ipv4Addr> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn set_address(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn destination(&self) -> Result<Ipv4Addr> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn set_destination(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn broadcast(&self) -> Result<Ipv4Addr> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn set_broadcast(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn netmask(&self) -> Result<Ipv4Addr> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn set_netmask(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn mtu(&self) -> Result<i32> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn set_mtu(&mut self, value: i32) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn queue(&mut self, index: usize) -> Option<&mut Self::Queue> {
|
||||
if index > 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(&mut self.queue)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Device {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.queue.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for Device {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.queue.into_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Queue {
|
||||
tun: Fd,
|
||||
}
|
||||
|
||||
impl Queue {
|
||||
pub fn has_packet_information(&self) -> bool {
|
||||
// on Android this is always the case
|
||||
false
|
||||
}
|
||||
|
||||
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||
self.tun.set_nonblock()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Queue {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.tun.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for Queue {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.tun.into_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Queue {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.tun.read(buf)
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.tun.read_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Queue {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.tun.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.tun.flush()
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.tun.write_vectored(bufs)
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
//! Android specific functionality.
|
||||
|
||||
mod device;
|
||||
pub use self::device::{Device, Queue};
|
||||
|
||||
use crate::configuration::Configuration as C;
|
||||
use crate::error::*;
|
||||
|
||||
/// Android-only interface configuration.
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
pub struct Configuration {}
|
||||
|
||||
/// Create a TUN device with the given name.
|
||||
pub fn create(configuration: &C) -> Result<Device> {
|
||||
Device::new(&configuration)
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
#![allow(unused_variables)]
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::configuration::Configuration;
|
||||
use crate::device::Device as D;
|
||||
use crate::error::*;
|
||||
use crate::platform::posix::{self, Fd};
|
||||
|
||||
/// A TUN device for iOS.
|
||||
pub struct Device {
|
||||
queue: Queue,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
/// Create a new `Device` for the given `Configuration`.
|
||||
pub fn new(config: &Configuration) -> Result<Self> {
|
||||
let fd = match config.raw_fd {
|
||||
Some(raw_fd) => raw_fd,
|
||||
_ => return Err(Error::InvalidConfig),
|
||||
};
|
||||
let mut device = unsafe {
|
||||
let tun = Fd::new(fd).map_err(|_| io::Error::last_os_error())?;
|
||||
|
||||
Device {
|
||||
queue: Queue { tun: tun },
|
||||
}
|
||||
};
|
||||
Ok(device)
|
||||
}
|
||||
|
||||
/// Split the interface into a `Reader` and `Writer`.
|
||||
pub fn split(self) -> (posix::Reader, posix::Writer) {
|
||||
let fd = Arc::new(self.queue.tun);
|
||||
(posix::Reader(fd.clone()), posix::Writer(fd.clone()))
|
||||
}
|
||||
|
||||
/// Return whether the device has packet information
|
||||
pub fn has_packet_information(&self) -> bool {
|
||||
self.queue.has_packet_information()
|
||||
}
|
||||
|
||||
/// Set non-blocking mode
|
||||
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||
self.queue.set_nonblock()
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Device {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.queue.tun.read(buf)
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.queue.tun.read_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Device {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.queue.tun.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.queue.tun.flush()
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.queue.tun.write_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl D for Device {
|
||||
type Queue = Queue;
|
||||
|
||||
fn name(&self) -> &str {
|
||||
return "";
|
||||
}
|
||||
|
||||
fn set_name(&mut self, value: &str) -> Result<()> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn enabled(&mut self, value: bool) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn address(&self) -> Result<Ipv4Addr> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn set_address(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn destination(&self) -> Result<Ipv4Addr> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn set_destination(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn broadcast(&self) -> Result<Ipv4Addr> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn set_broadcast(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn netmask(&self) -> Result<Ipv4Addr> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn set_netmask(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn mtu(&self) -> Result<i32> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn set_mtu(&mut self, value: i32) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn queue(&mut self, index: usize) -> Option<&mut Self::Queue> {
|
||||
if index > 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(&mut self.queue)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Device {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.queue.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for Device {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.queue.into_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Queue {
|
||||
tun: Fd,
|
||||
}
|
||||
|
||||
impl Queue {
|
||||
pub fn has_packet_information(&self) -> bool {
|
||||
// on ios this is always the case
|
||||
true
|
||||
}
|
||||
|
||||
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||
self.tun.set_nonblock()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Queue {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.tun.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for Queue {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.tun.into_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Queue {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.tun.read(buf)
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.tun.read_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Queue {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.tun.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.tun.flush()
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.tun.write_vectored(bufs)
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
//! iOS specific functionality.
|
||||
|
||||
mod device;
|
||||
pub use self::device::{Device, Queue};
|
||||
|
||||
use crate::configuration::Configuration as C;
|
||||
use crate::error::*;
|
||||
|
||||
/// iOS-only interface configuration.
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
pub struct Configuration {}
|
||||
|
||||
/// Create a TUN device with the given name.
|
||||
pub fn create(configuration: &C) -> Result<Device> {
|
||||
Device::new(&configuration)
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
use std::net::{SocketAddr, UdpSocket};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use chrono::Local;
|
||||
|
||||
use crate::DEVICE_LIST;
|
||||
use crate::error::*;
|
||||
use crate::handle::DIRECT_ROUTE_TABLE;
|
||||
use crate::protocol::{control_packet, NetPacket, Protocol, Version};
|
||||
use crate::protocol::control_packet::PingPacket;
|
||||
|
||||
pub fn handle_loop(udp: UdpSocket, server_addr: SocketAddr) -> Result<()> {
|
||||
const INTERVAL: u64 = 3000;
|
||||
const MAX_INTERVAL: i64 = 3000 * 3;
|
||||
let mut buf = [0u8; (4 + 8 + 4)];
|
||||
let mut net_packet = NetPacket::new(&mut buf)?;
|
||||
net_packet.set_version(Version::V1);
|
||||
net_packet.set_protocol(Protocol::Control);
|
||||
net_packet.set_transport_protocol(control_packet::Protocol::Ping.into());
|
||||
net_packet.set_ttl(255);
|
||||
loop {
|
||||
let current_time = Local::now().timestamp_millis();
|
||||
{
|
||||
let mut ping = PingPacket::new(net_packet.payload_mut())?;
|
||||
ping.set_time(current_time);
|
||||
let epoch = { DEVICE_LIST.lock().0 };
|
||||
ping.set_epoch(epoch);
|
||||
}
|
||||
let _ = udp.send_to(net_packet.buffer(), server_addr);
|
||||
for x in DIRECT_ROUTE_TABLE.iter() {
|
||||
let virtual_ip = x.key().clone();
|
||||
let route = x.value().clone();
|
||||
drop(x);
|
||||
if current_time - route.recv_time <= MAX_INTERVAL {
|
||||
let _ = udp.send_to(net_packet.buffer(), route.address);
|
||||
} else {
|
||||
DIRECT_ROUTE_TABLE.remove_if(&virtual_ip, |_, route| {
|
||||
current_time - route.recv_time <= MAX_INTERVAL
|
||||
});
|
||||
}
|
||||
}
|
||||
thread::sleep(Duration::from_millis(INTERVAL));
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
use std::net::{ Ipv4Addr, SocketAddr};
|
||||
use std::sync::atomic::AtomicI64;
|
||||
use std::time::Duration;
|
||||
|
||||
use chrono::Local;
|
||||
use dashmap::DashMap;
|
||||
use lazy_static::lazy_static;
|
||||
use moka::sync::Cache;
|
||||
use parking_lot::{const_mutex, Mutex};
|
||||
|
||||
use crate::proto::message::NatType;
|
||||
|
||||
pub mod heartbeat_handler;
|
||||
pub mod punch_handler;
|
||||
pub mod registration_handler;
|
||||
pub mod tun_handler;
|
||||
pub mod udp_recv_handler;
|
||||
lazy_static! {
|
||||
/// 0. 机器纪元,每一次上线或者下线都会增1,由服务端维护,用于感知网络中机器变化
|
||||
/// 服务端和客户端的不一致,则服务端会推送新的设备列表
|
||||
/// 1. 网络中的虚拟ip列表
|
||||
pub static ref DEVICE_LIST:Mutex<(u32,Vec<Ipv4Addr>)> = const_mutex((0,Vec::new()));
|
||||
/// 服务器延迟
|
||||
pub static ref SERVER_RT:AtomicI64 = AtomicI64::new(-1);
|
||||
/// id
|
||||
pub static ref ID:AtomicI64 = AtomicI64::new(0);
|
||||
/// 直连路由表
|
||||
pub static ref DIRECT_ROUTE_TABLE:DashMap<Ipv4Addr,Route> = DashMap::new();
|
||||
/// 地址映射
|
||||
pub static ref ADDR_TABLE:Cache<SocketAddr,Ipv4Addr> = Cache::builder()
|
||||
.time_to_idle(Duration::from_secs(60*5)).build();
|
||||
/// 当前设备的nat信息
|
||||
pub static ref NAT_INFO:Mutex<Option<NatInfo>> = const_mutex(None);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NatInfo {
|
||||
public_ips: Vec<u32>,
|
||||
public_port: u16,
|
||||
public_port_range: u16,
|
||||
nat_type: NatType,
|
||||
}
|
||||
|
||||
impl NatInfo {
|
||||
pub fn new(public_ips: Vec<u32>,
|
||||
public_port: u16,
|
||||
public_port_range: u16,
|
||||
nat_type: NatType, ) -> Self {
|
||||
Self {
|
||||
public_ips,
|
||||
public_port,
|
||||
public_port_range,
|
||||
nat_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 初始化nat信息
|
||||
pub fn init_nat_info(public_ip: u32, public_port: u16) {
|
||||
match crate::nat::check::public_ip_list() {
|
||||
Ok((nat_type, ips, port_range)) => {
|
||||
let mut public_ips = Vec::new();
|
||||
public_ips.push(public_ip);
|
||||
for ip in ips {
|
||||
let ip = u32::from_be_bytes(ip.octets());
|
||||
if ip != public_ip {
|
||||
public_ips.push(ip);
|
||||
}
|
||||
}
|
||||
let nat_info = NatInfo::new(public_ips,
|
||||
public_port,
|
||||
port_range, nat_type);
|
||||
// println!("nat信息:{:?}",nat_info);
|
||||
let mut nat_info_lock = NAT_INFO.lock();
|
||||
nat_info_lock.replace(nat_info);
|
||||
}
|
||||
Err(e) => {
|
||||
println!("获取nat数据失败,将无法进行udp打洞:{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CurrentDeviceInfo {
|
||||
pub(crate) virtual_ip: Ipv4Addr,
|
||||
pub(crate) virtual_gateway: Ipv4Addr,
|
||||
pub(crate) virtual_netmask: Ipv4Addr,
|
||||
//网络地址
|
||||
pub(crate) virtual_network: Ipv4Addr,
|
||||
//直接广播地址
|
||||
pub(crate) broadcast_address: Ipv4Addr,
|
||||
//链接的服务器地址
|
||||
pub(crate) connect_server: SocketAddr,
|
||||
}
|
||||
|
||||
impl CurrentDeviceInfo {
|
||||
pub fn new(virtual_ip: Ipv4Addr, virtual_gateway: Ipv4Addr, virtual_netmask: Ipv4Addr, connect_server: SocketAddr) -> Self {
|
||||
let broadcast_address = (!u32::from_be_bytes(virtual_netmask.octets()))
|
||||
| u32::from_be_bytes(virtual_gateway.octets());
|
||||
let broadcast_address = Ipv4Addr::from(broadcast_address);
|
||||
let virtual_network = u32::from_be_bytes(virtual_netmask.octets())
|
||||
& u32::from_be_bytes(virtual_gateway.octets());
|
||||
let virtual_network = Ipv4Addr::from(virtual_network);
|
||||
Self {
|
||||
virtual_ip,
|
||||
virtual_netmask,
|
||||
virtual_gateway,
|
||||
virtual_network,
|
||||
broadcast_address,
|
||||
connect_server,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct Route {
|
||||
pub(crate) address: SocketAddr,
|
||||
//用心跳探测延迟,收包时更新
|
||||
pub(crate) delay: i64,
|
||||
//收包时更新,如果太久没有收到消息则剔除
|
||||
pub(crate) recv_time: i64,
|
||||
}
|
||||
|
||||
impl Route {
|
||||
pub fn new(address: SocketAddr) -> Self {
|
||||
Self {
|
||||
address,
|
||||
delay: -1,
|
||||
recv_time: Local::now().timestamp_millis(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,354 +0,0 @@
|
||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use crossbeam::channel::{Receiver, RecvTimeoutError, Sender, SendError, TrySendError};
|
||||
use dashmap::DashMap;
|
||||
use lazy_static::lazy_static;
|
||||
use protobuf::Message;
|
||||
|
||||
use crate::{CurrentDeviceInfo, DEVICE_LIST, NAT_INFO, NatInfo};
|
||||
use crate::error::*;
|
||||
use crate::handle::DIRECT_ROUTE_TABLE;
|
||||
use crate::proto::message::{NatType, Punch, Step};
|
||||
use crate::protocol::{control_packet, NetPacket, Protocol, turn_packet, Version};
|
||||
use crate::protocol::control_packet::PunchRequestPacket;
|
||||
use crate::protocol::turn_packet::TurnPacket;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref STEP_MAP:DashMap<Ipv4Addr,Step> = DashMap::new();
|
||||
}
|
||||
/// 每一种类型一个通道,减少相互干扰
|
||||
pub fn bounded() -> (PunchSender, ConeReceiver, ReqSymmetricReceiver, ResSymmetricReceiver) {
|
||||
let (cone_sender, cone_receiver) = crossbeam::channel::bounded(3);
|
||||
let (req_symmetric_sender, req_symmetric_receiver) = crossbeam::channel::bounded(1);
|
||||
let (res_symmetric_sender, res_symmetric_receiver) = crossbeam::channel::bounded(1);
|
||||
(PunchSender::new(cone_sender, req_symmetric_sender, res_symmetric_sender),
|
||||
ConeReceiver(cone_receiver), ReqSymmetricReceiver(req_symmetric_receiver),
|
||||
ResSymmetricReceiver(res_symmetric_receiver))
|
||||
}
|
||||
|
||||
pub struct ConeReceiver(Receiver<Punch>);
|
||||
|
||||
pub struct ReqSymmetricReceiver(Receiver<Punch>);
|
||||
|
||||
pub struct ResSymmetricReceiver(Receiver<Punch>);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PunchSender {
|
||||
cone_sender: Sender<Punch>,
|
||||
req_symmetric_sender: Sender<Punch>,
|
||||
res_symmetric_sender: Sender<Punch>,
|
||||
}
|
||||
|
||||
impl PunchSender {
|
||||
pub fn new(cone_sender: Sender<Punch>,
|
||||
req_symmetric_sender: Sender<Punch>,
|
||||
res_symmetric_sender: Sender<Punch>, ) -> Self {
|
||||
Self {
|
||||
cone_sender,
|
||||
req_symmetric_sender,
|
||||
res_symmetric_sender,
|
||||
}
|
||||
}
|
||||
pub fn send(&self, punch: Punch) -> std::result::Result<(), SendError<Punch>> {
|
||||
match punch.nat_type.enum_value_or_default() {
|
||||
NatType::Symmetric => {
|
||||
if punch.reply {
|
||||
// 为true表示回应,也就是主动发起的打洞操作
|
||||
self.res_symmetric_sender.send(punch)
|
||||
} else {
|
||||
self.req_symmetric_sender.send(punch)
|
||||
}
|
||||
}
|
||||
NatType::Cone => {
|
||||
self.cone_sender.send(punch)
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn try_send(&self, punch: Punch) -> std::result::Result<(), TrySendError<Punch>> {
|
||||
match punch.nat_type.enum_value_or_default() {
|
||||
NatType::Symmetric => {
|
||||
if punch.reply {
|
||||
// 为true表示回应,也就是主动发起的打洞操作
|
||||
self.res_symmetric_sender.try_send(punch)
|
||||
} else {
|
||||
self.req_symmetric_sender.try_send(punch)
|
||||
}
|
||||
}
|
||||
NatType::Cone => {
|
||||
self.cone_sender.try_send(punch)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle(udp: &UdpSocket, punch_list: Vec<Punch>, buf: &[u8]) -> Result<()> {
|
||||
let mut counter = 0u64;
|
||||
for punch in punch_list {
|
||||
let dest = Ipv4Addr::from(punch.virtual_ip);
|
||||
if DIRECT_ROUTE_TABLE.contains_key(&dest) {
|
||||
continue;
|
||||
}
|
||||
// println!("punch {:?}", punch);
|
||||
match punch.nat_type.enum_value_or_default() {
|
||||
NatType::Symmetric => {
|
||||
match punch.step.enum_value_or_default() {
|
||||
Step::Step1 | Step::Step2 | Step::Step3 => {
|
||||
//预测范围发送
|
||||
for pub_ip in punch.public_ip_list {
|
||||
let pub_ip = Ipv4Addr::from(pub_ip);
|
||||
for range in 0..punch.public_port_range + 1 {
|
||||
let right_port = ((punch.public_port + range) & 0xFFFF) as u16;
|
||||
let left_port = ((0xFFFF + punch.public_port - range) & 0xFFFF) as u16;
|
||||
if right_port != 0 {
|
||||
// println!("{:?}", SocketAddr::V4(SocketAddrV4::new(pub_ip, right_port)));
|
||||
udp.send_to(
|
||||
buf,
|
||||
SocketAddr::V4(SocketAddrV4::new(pub_ip, right_port)),
|
||||
)?;
|
||||
select_sleep(&mut counter);
|
||||
}
|
||||
if left_port != 0 && range != 0 {
|
||||
// println!("{:?}", SocketAddr::V4(SocketAddrV4::new(pub_ip, right_port)));
|
||||
if left_port == right_port {
|
||||
break;
|
||||
}
|
||||
udp.send_to(
|
||||
buf,
|
||||
SocketAddr::V4(SocketAddrV4::new(pub_ip, left_port)),
|
||||
)?;
|
||||
select_sleep(&mut counter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Step::Step4 => {
|
||||
//全范围发送
|
||||
for pub_ip in punch.public_ip_list {
|
||||
let pub_ip = Ipv4Addr::from(pub_ip);
|
||||
for port in 1..0xFFFF {
|
||||
udp.send_to(
|
||||
buf,
|
||||
SocketAddr::V4(SocketAddrV4::new(pub_ip, port)),
|
||||
)?;
|
||||
select_sleep(&mut counter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
NatType::Cone => {
|
||||
for pub_ip in punch.public_ip_list {
|
||||
udp.send_to(
|
||||
buf,
|
||||
SocketAddr::V4(SocketAddrV4::new(
|
||||
Ipv4Addr::from(pub_ip),
|
||||
punch.public_port as u16,
|
||||
)),
|
||||
)?;
|
||||
select_sleep(&mut counter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 给对称nat发送打洞数据包
|
||||
pub fn req_symmetric_handle_loop(
|
||||
receiver: ReqSymmetricReceiver,
|
||||
udp: UdpSocket,
|
||||
cur_info: CurrentDeviceInfo,
|
||||
) -> Result<()> {
|
||||
let receiver = receiver.0;
|
||||
handle_loop(receiver, udp, cur_info)
|
||||
}
|
||||
|
||||
/// 给对称nat发送打洞数据包,处理主动发起的打洞操作
|
||||
pub fn res_symmetric_handle_loop(
|
||||
receiver: ResSymmetricReceiver,
|
||||
udp: UdpSocket,
|
||||
cur_info: CurrentDeviceInfo,
|
||||
) -> Result<()> {
|
||||
let receiver = receiver.0;
|
||||
let mut buf = [0u8; 12];
|
||||
let mut packet = NetPacket::new(&mut buf)?;
|
||||
packet.set_version(Version::V1);
|
||||
packet.set_ttl(255);
|
||||
packet.set_protocol(Protocol::Control);
|
||||
packet.set_transport_protocol(control_packet::Protocol::PunchRequest.into());
|
||||
{
|
||||
let mut punch_packet = PunchRequestPacket::new(packet.payload_mut())?;
|
||||
punch_packet.set_source(cur_info.virtual_ip);
|
||||
}
|
||||
loop {
|
||||
match receiver.recv_timeout(Duration::from_secs(30)) {
|
||||
Ok(punch) => {
|
||||
let mut list = Vec::new();
|
||||
list.push(punch);
|
||||
loop {
|
||||
match receiver.try_recv() {
|
||||
Ok(punch) => {
|
||||
list.push(punch);
|
||||
}
|
||||
Err(_) => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for punch in &list {
|
||||
let dest = Ipv4Addr::from(punch.virtual_ip);
|
||||
match punch.step.enum_value_or_default() {
|
||||
Step::Step1 => {
|
||||
STEP_MAP.insert(dest, Step::Step2);
|
||||
}
|
||||
Step::Step2 => {
|
||||
STEP_MAP.insert(dest, Step::Step3);
|
||||
}
|
||||
Step::Step3 => {
|
||||
STEP_MAP.insert(dest, Step::Step4);
|
||||
}
|
||||
Step::Step4 => {
|
||||
STEP_MAP.insert(dest, Step::Step1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Err(e) = handle(&udp, list, packet.buffer()) {
|
||||
println!("{:?}", e);
|
||||
}
|
||||
}
|
||||
Err(RecvTimeoutError::Timeout) => {
|
||||
punch_request_handle(&udp, &cur_info)?;
|
||||
}
|
||||
Err(_) => {
|
||||
return Err(Error::Stop("打洞线程通道关闭".to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 给锥形nat发送打洞数据包
|
||||
pub fn cone_handle_loop(
|
||||
receiver: ConeReceiver,
|
||||
udp: UdpSocket,
|
||||
cur_info: CurrentDeviceInfo,
|
||||
) -> Result<()> {
|
||||
let receiver = receiver.0;
|
||||
handle_loop(receiver, udp, cur_info)
|
||||
}
|
||||
|
||||
pub fn handle_loop(
|
||||
receiver: Receiver<Punch>,
|
||||
udp: UdpSocket,
|
||||
cur_info: CurrentDeviceInfo,
|
||||
) -> Result<()> {
|
||||
let mut buf = [0u8; 12];
|
||||
let mut packet = NetPacket::new(&mut buf)?;
|
||||
packet.set_version(Version::V1);
|
||||
packet.set_ttl(255);
|
||||
packet.set_protocol(Protocol::Control);
|
||||
packet.set_transport_protocol(control_packet::Protocol::PunchRequest.into());
|
||||
{
|
||||
let mut punch_packet = PunchRequestPacket::new(packet.payload_mut())?;
|
||||
punch_packet.set_source(cur_info.virtual_ip);
|
||||
}
|
||||
loop {
|
||||
match receiver.recv() {
|
||||
Ok(punch) => {
|
||||
let mut list = Vec::new();
|
||||
list.push(punch);
|
||||
loop {
|
||||
match receiver.try_recv() {
|
||||
Ok(punch) => {
|
||||
list.push(punch);
|
||||
}
|
||||
Err(_) => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Err(e) = handle(&udp, list, packet.buffer()) {
|
||||
println!("{:?}", e);
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
return Err(Error::Stop("打洞线程通道关闭".to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn select_sleep(counter: &mut u64) {
|
||||
*counter += 1;
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
// if *counter > 1 {
|
||||
// if cone_nat {
|
||||
// thread::sleep(Duration::from_millis(2));
|
||||
// } else {
|
||||
// if (*counter) & 10 == 10 {
|
||||
// thread::sleep(Duration::from_millis(1));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
fn punch_request_handle(udp: &UdpSocket, cur_info: &CurrentDeviceInfo) -> Result<()> {
|
||||
let nat_info_lock = NAT_INFO.lock();
|
||||
let nat_info = nat_info_lock.clone();
|
||||
drop(nat_info_lock);
|
||||
if let Some(nat_info) = nat_info {
|
||||
if let Err(e) = send_punch(&udp,
|
||||
&cur_info,
|
||||
nat_info) {
|
||||
println!("发送打洞数据失败 :{:?}", e);
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::Stop("未初始化nat信息".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn send_punch(udp: &UdpSocket, cur_info: &CurrentDeviceInfo, nat_info: NatInfo) -> Result<()> {
|
||||
let lock = DEVICE_LIST.lock();
|
||||
let list = lock.1.clone();
|
||||
drop(lock);
|
||||
for ip in list {
|
||||
//只向ip比自己大的发起打洞,避免双方同时发起打洞浪费流量
|
||||
if ip > cur_info.virtual_ip && !DIRECT_ROUTE_TABLE.contains_key(&ip) {
|
||||
let step = if let Some(step) = STEP_MAP.get(&ip) {
|
||||
*step
|
||||
} else {
|
||||
Step::Step1
|
||||
};
|
||||
let bytes = punch_packet(cur_info.virtual_ip,
|
||||
nat_info.clone(), ip, step)?;
|
||||
udp.send_to(&bytes, cur_info.connect_server)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn punch_packet(virtual_ip: Ipv4Addr, nat_info: NatInfo, dest: Ipv4Addr, step: Step) -> Result<Vec<u8>> {
|
||||
let mut punch_reply = Punch::new();
|
||||
punch_reply.reply = false;
|
||||
punch_reply.virtual_ip = u32::from_be_bytes(virtual_ip.octets());
|
||||
punch_reply.step = protobuf::EnumOrUnknown::new(step);
|
||||
punch_reply.public_ip_list = nat_info.public_ips;
|
||||
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(nat_info.nat_type);
|
||||
let bytes = punch_reply.write_to_bytes()?;
|
||||
let mut net_packet = NetPacket::new(vec![0u8; 4 + 8 + 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.set_ttl(255);
|
||||
let mut turn_packet = TurnPacket::new(net_packet.payload_mut())?;
|
||||
turn_packet.set_source(virtual_ip);
|
||||
turn_packet.set_destination(dest);
|
||||
turn_packet.set_payload(&bytes);
|
||||
Ok(net_packet.into_buffer())
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
use std::io;
|
||||
use std::net::{SocketAddr, UdpSocket};
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
||||
use std::time::Duration;
|
||||
|
||||
use chrono::Local;
|
||||
use parking_lot::RwLock;
|
||||
use protobuf::Message;
|
||||
|
||||
use crate::error::*;
|
||||
use crate::proto::message::{RegistrationRequest, RegistrationResponse};
|
||||
use crate::protocol::{error_packet, NetPacket, Protocol, service_packet, Version};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref REQUEST:RwLock<Option<(String,String)>> = parking_lot::const_rwlock(None);
|
||||
static ref REGISTRATION_TIME:AtomicI64=AtomicI64::new(0);
|
||||
}
|
||||
|
||||
///向中继服务器注册,token标识一个虚拟网关,mac_address防止多次注册时得到的ip不一致
|
||||
pub fn registration(
|
||||
udp: &UdpSocket,
|
||||
server_address: SocketAddr,
|
||||
token: String,
|
||||
mac_address: String,
|
||||
) -> Result<RegistrationResponse> {
|
||||
// todo 和服务器通信加密
|
||||
let request_packet = registration_request_packet(token.clone(), mac_address.clone())?;
|
||||
let buf = request_packet.buffer();
|
||||
let mut counter = 0;
|
||||
let mut recv_buf = [0u8; 10240];
|
||||
udp.set_read_timeout(Some(Duration::from_millis(500)))?;
|
||||
loop {
|
||||
counter += 1;
|
||||
if counter & 10 == 10 {
|
||||
return Err(Error::Stop("注册请求超时".to_string()));
|
||||
}
|
||||
udp.send_to(buf, server_address)?;
|
||||
let (len, addr) = match udp.recv_from(&mut recv_buf) {
|
||||
Ok(ok) => ok,
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::WouldBlock || e.kind() == io::ErrorKind::TimedOut {
|
||||
continue;
|
||||
}
|
||||
return Err(Error::Io(e));
|
||||
}
|
||||
};
|
||||
if server_address != addr {
|
||||
continue;
|
||||
}
|
||||
let net_packet = NetPacket::new(&recv_buf[..len])?;
|
||||
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())?;
|
||||
let _ = REQUEST.write().replace((token, mac_address));
|
||||
udp.set_read_timeout(None)?;
|
||||
return Ok(response);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Protocol::Error => {
|
||||
match error_packet::Protocol::from(net_packet.transport_protocol()) {
|
||||
error_packet::Protocol::TokenError => {
|
||||
return Err(Error::Stop("token错误".to_string()));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn registration_request_packet(token: String, mac_address: String) -> Result<NetPacket<Vec<u8>>> {
|
||||
let mut request = RegistrationRequest::new();
|
||||
request.token = token;
|
||||
request.mac_address = mac_address;
|
||||
let bytes = request.write_to_bytes()?;
|
||||
let buf = vec![0u8; 4 + 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.set_ttl(255);
|
||||
net_packet.set_payload(&bytes);
|
||||
Ok(net_packet)
|
||||
}
|
||||
|
||||
pub fn fast_registration(udp: &UdpSocket, server_address: SocketAddr) -> Result<()> {
|
||||
let last = REGISTRATION_TIME.load(Ordering::Relaxed);
|
||||
let new = Local::now().timestamp_millis();
|
||||
if new - last < 2000
|
||||
|| REGISTRATION_TIME
|
||||
.compare_exchange(last, new, Ordering::Relaxed, Ordering::Relaxed)
|
||||
.is_err()
|
||||
{
|
||||
//短时间不重复注册
|
||||
return Ok(());
|
||||
}
|
||||
let lock = REQUEST.read();
|
||||
let option = lock.clone();
|
||||
drop(lock);
|
||||
if let Some((token, mac_address)) = option {
|
||||
let request_packet = registration_request_packet(token, mac_address)?;
|
||||
udp.send_to(request_packet.buffer(), server_address)?;
|
||||
REGISTRATION_TIME.store(Local::now().timestamp_millis(), Ordering::Relaxed);
|
||||
return Ok(());
|
||||
}
|
||||
return Err(Error::Stop("注册信息不存在".to_string()));
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
/// 接收tun数据,并且转发到udp上
|
||||
use std::net::{IpAddr, Ipv4Addr, UdpSocket};
|
||||
|
||||
use chrono::Local;
|
||||
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::{CurrentDeviceInfo, DIRECT_ROUTE_TABLE};
|
||||
use crate::protocol::{NetPacket, Protocol, Version};
|
||||
use crate::protocol::turn_packet::TurnPacket;
|
||||
use crate::tun_device::TunReader;
|
||||
|
||||
/// 是否在一个网段
|
||||
fn check_dest(dest: Ipv4Addr, cur_info: &CurrentDeviceInfo) -> bool {
|
||||
u32::from_be_bytes(dest.octets()) & u32::from_be_bytes(cur_info.virtual_netmask.octets())
|
||||
== u32::from_be_bytes(cur_info.virtual_network.octets())
|
||||
}
|
||||
|
||||
fn icmp(udp: &UdpSocket, 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();
|
||||
let mut addr = udp.local_addr()?;
|
||||
addr.set_ip(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
|
||||
udp.send_to(ipv4_packet.buffer, addr)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn handle(
|
||||
udp: &UdpSocket,
|
||||
data: &mut [u8],
|
||||
cur_info: &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 != cur_info.virtual_ip || !check_dest(dest_ip, &cur_info) {
|
||||
return Ok(());
|
||||
}
|
||||
if src_ip == dest_ip {
|
||||
return icmp(&udp, ipv4_packet);
|
||||
}
|
||||
let mut ipv4_turn_packet = TurnPacket::new(net_packet.payload_mut())?;
|
||||
ipv4_turn_packet.set_source(src_ip);
|
||||
ipv4_turn_packet.set_destination(dest_ip);
|
||||
ipv4_turn_packet.set_payload(ipv4_packet.buffer);
|
||||
//优先发到直连到地址
|
||||
if let Some(route) = DIRECT_ROUTE_TABLE.get(&dest_ip) {
|
||||
let current_time = Local::now().timestamp_millis();
|
||||
if current_time - route.recv_time < 3_000 {
|
||||
udp.send_to(&net_packet.buffer()[..(4 + 8 + data_len)], route.address)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
udp.send_to(&net_packet.buffer()[..(4 + 8 + data_len)], cur_info.connect_server)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn handle_loop(
|
||||
udp: UdpSocket,
|
||||
tun_reader: TunReader,
|
||||
cur_info: CurrentDeviceInfo,
|
||||
) -> 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(255);
|
||||
loop {
|
||||
let mut data = tun_reader.next()?;
|
||||
match handle(&udp, data.bytes_mut(), &cur_info, &mut net_packet) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
println!("{:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(unix))]
|
||||
pub fn handle_loop(
|
||||
udp: UdpSocket,
|
||||
mut tun_reader: TunReader,
|
||||
cur_info: CurrentDeviceInfo,
|
||||
) -> 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(0);
|
||||
net_packet.set_ttl(255);
|
||||
let mut buf = [0u8; 1500];
|
||||
loop {
|
||||
let data = tun_reader.read(&mut buf)?;
|
||||
match handle(&udp, data, &cur_info, &mut net_packet) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
println!("{:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,299 +0,0 @@
|
||||
use std::net::{Ipv4Addr, SocketAddr, UdpSocket};
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use chrono::Local;
|
||||
use crossbeam::channel::{Receiver, Sender, TrySendError};
|
||||
use packet::icmp::{icmp, Kind};
|
||||
use packet::ip::ipv4;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
use protobuf::Message;
|
||||
|
||||
use crate::CurrentDeviceInfo;
|
||||
use crate::error::*;
|
||||
use crate::handle::{ADDR_TABLE, DEVICE_LIST, DIRECT_ROUTE_TABLE, NAT_INFO, Route, SERVER_RT};
|
||||
use crate::handle::punch_handler::PunchSender;
|
||||
use crate::handle::registration_handler::fast_registration;
|
||||
use crate::proto::message::{DeviceList, Punch, RegistrationResponse};
|
||||
use crate::protocol::{control_packet, NetPacket, Protocol, service_packet, turn_packet, Version};
|
||||
use crate::protocol::control_packet::{ControlPacket, PunchResponsePacket};
|
||||
use crate::protocol::error_packet::InErrorPacket;
|
||||
use crate::protocol::turn_packet::TurnPacket;
|
||||
use crate::tun_device::TunWriter;
|
||||
|
||||
pub fn recv_loop(
|
||||
udp: UdpSocket,
|
||||
server_addr: SocketAddr,
|
||||
other_sender: Sender<(SocketAddr, Vec<u8>)>,
|
||||
mut tun_writer: TunWriter,
|
||||
current_device: CurrentDeviceInfo,
|
||||
) -> Result<()> {
|
||||
let mut buf = [0u8; 65536];
|
||||
let mut local_addr = udp.local_addr()?;
|
||||
local_addr.set_ip(std::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
|
||||
loop {
|
||||
match udp.recv_from(&mut buf) {
|
||||
Ok((len, addr)) => {
|
||||
if addr == local_addr {
|
||||
//本地的包直接再发到网卡,这个主要用于处理当前虚拟ip的icmp ping
|
||||
if let Ok(ip) = IpV4Packet::new(&buf[..len]) {
|
||||
if ip.destination_ip() == current_device.virtual_ip {
|
||||
let _ = tun_writer.write(&buf[..len]);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
match recv_handle(
|
||||
&udp,
|
||||
addr,
|
||||
&mut buf[..len],
|
||||
&server_addr,
|
||||
&other_sender,
|
||||
&mut tun_writer,
|
||||
¤t_device,
|
||||
) {
|
||||
Ok(_) => {}
|
||||
Err(Error::Stop(str)) => {
|
||||
return Err(Error::Stop(str));
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("{:?}", e);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn recv_handle(
|
||||
udp: &UdpSocket,
|
||||
recv_addr: SocketAddr,
|
||||
buf: &mut [u8],
|
||||
_server_addr: &SocketAddr,
|
||||
other_sender: &Sender<(SocketAddr, Vec<u8>)>,
|
||||
tun_writer: &mut TunWriter,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
) -> Result<()> {
|
||||
let mut net_packet = NetPacket::new(buf)?;
|
||||
match net_packet.protocol() {
|
||||
Protocol::Ipv4Turn => {
|
||||
let mut ipv4_turn_packet = TurnPacket::new(net_packet.payload_mut())?;
|
||||
let source = ipv4_turn_packet.source();
|
||||
let destination = ipv4_turn_packet.destination();
|
||||
let mut ipv4 = IpV4Packet::new(ipv4_turn_packet.payload_mut())?;
|
||||
if ipv4.source_ip() == source
|
||||
&& ipv4.destination_ip() == destination
|
||||
&& current_device.virtual_ip == ipv4.destination_ip()
|
||||
{
|
||||
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();
|
||||
ipv4_turn_packet.set_source(destination);
|
||||
ipv4_turn_packet.set_destination(source);
|
||||
udp.send_to(net_packet.buffer(), recv_addr)?;
|
||||
} else {
|
||||
tun_writer.write(ipv4_turn_packet.payload())?;
|
||||
}
|
||||
} else {
|
||||
tun_writer.write(ipv4_turn_packet.payload())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Protocol::UnKnow(_) => {}
|
||||
_ => {
|
||||
//发送到子线程处理
|
||||
let v = net_packet.buffer().to_vec();
|
||||
match other_sender.try_send((recv_addr, v)) {
|
||||
Ok(_) => {}
|
||||
Err(TrySendError::Disconnected(_)) => {
|
||||
return Err(Error::Stop("处理线程停止".to_string()));
|
||||
}
|
||||
Err(e) => {
|
||||
println!("子线程处理 :{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn other_loop(
|
||||
udp: UdpSocket,
|
||||
receiver: Receiver<(SocketAddr, Vec<u8>)>,
|
||||
current_device: CurrentDeviceInfo,
|
||||
sender: PunchSender,
|
||||
) -> Result<()> {
|
||||
loop {
|
||||
let (peer_addr, buf) = receiver.recv()?;
|
||||
match other_handle(&udp, buf, peer_addr, ¤t_device, &sender) {
|
||||
Ok(_) => {}
|
||||
Err(Error::Stop(str)) => {
|
||||
return Err(Error::Stop(str));
|
||||
}
|
||||
Err(e) => {
|
||||
println!("{:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn other_handle(
|
||||
udp: &UdpSocket,
|
||||
buf: Vec<u8>,
|
||||
peer_addr: SocketAddr,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
sender: &PunchSender,
|
||||
) -> Result<()> {
|
||||
let server_addr = current_device.connect_server;
|
||||
let mut net_packet = NetPacket::new(buf)?;
|
||||
match net_packet.protocol() {
|
||||
Protocol::Service => {
|
||||
if peer_addr != current_device.connect_server {
|
||||
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())?;
|
||||
crate::handle::init_nat_info(response.public_ip, response.public_port as u16);
|
||||
//todo 重连之后ip可能会发生改变(目前2分钟内未重连则会释放ip),需要更新本地ip(或者保证重连ip不变)
|
||||
}
|
||||
service_packet::Protocol::UpdateDeviceList => {
|
||||
let device_list = DeviceList::parse_from_bytes(net_packet.payload())?;
|
||||
let ip_list: Vec<Ipv4Addr> = device_list
|
||||
.virtual_ip_list
|
||||
.iter()
|
||||
.map(|ip| Ipv4Addr::from(*ip))
|
||||
.collect();
|
||||
let mut dev = DEVICE_LIST.lock();
|
||||
if dev.0 < device_list.epoch || device_list.epoch - dev.0 > u32::MAX >> 2 {
|
||||
dev.0 = device_list.epoch;
|
||||
dev.1 = ip_list;
|
||||
}
|
||||
}
|
||||
service_packet::Protocol::UnKnow(_) => {}
|
||||
}
|
||||
}
|
||||
Protocol::Error => {
|
||||
match InErrorPacket::new(net_packet.transport_protocol(), net_packet.payload())? {
|
||||
InErrorPacket::TokenError => {
|
||||
if server_addr == peer_addr {
|
||||
//停止整个应用
|
||||
return Err(Error::Stop("token无效".to_string()));
|
||||
}
|
||||
}
|
||||
InErrorPacket::Disconnect => {
|
||||
if server_addr == peer_addr {
|
||||
fast_registration(&udp, server_addr)?;
|
||||
}
|
||||
}
|
||||
InErrorPacket::OtherError(e) => {
|
||||
println!("{:?}", e.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
Protocol::Control => {
|
||||
match ControlPacket::new(net_packet.transport_protocol(), net_packet.payload())? {
|
||||
ControlPacket::PingPacket(ping) => {
|
||||
net_packet.set_transport_protocol(control_packet::Protocol::Pong.into());
|
||||
udp.send_to(&net_packet.buffer()[..12], peer_addr)?;
|
||||
}
|
||||
ControlPacket::PongPacket(pong_packet) => {
|
||||
let current_time = Local::now().timestamp_millis();
|
||||
let rt = current_time - pong_packet.time();
|
||||
if rt >= 0 {
|
||||
if peer_addr == server_addr {
|
||||
SERVER_RT.store(rt, Ordering::Relaxed)
|
||||
} else {
|
||||
//其他设备
|
||||
if let Some(virtual_ip) = ADDR_TABLE.get(&peer_addr) {
|
||||
if let Some(mut info) = DIRECT_ROUTE_TABLE.get_mut(&virtual_ip) {
|
||||
info.delay = rt;
|
||||
info.recv_time = current_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ControlPacket::PunchRequest(punch_request) => {
|
||||
// println!("打洞请求:{:?}", punch_request);
|
||||
let src = punch_request.source();
|
||||
drop(punch_request);
|
||||
//回应
|
||||
let mut punch_response = PunchResponsePacket::new(net_packet.payload_mut())?;
|
||||
punch_response.set_source(current_device.virtual_ip);
|
||||
net_packet.set_transport_protocol(control_packet::Protocol::PunchResponse.into());
|
||||
udp.send_to(net_packet.buffer(), peer_addr)?;
|
||||
let route = Route::new(peer_addr);
|
||||
DIRECT_ROUTE_TABLE.insert(src, route);
|
||||
ADDR_TABLE.insert(peer_addr, src);
|
||||
}
|
||||
ControlPacket::PunchResponse(punch_response) => {
|
||||
// println!("打洞响应:{:?}", punch_response);
|
||||
let route = Route::new(peer_addr);
|
||||
DIRECT_ROUTE_TABLE.insert(punch_response.source(), route);
|
||||
ADDR_TABLE.insert(peer_addr, punch_response.source());
|
||||
}
|
||||
}
|
||||
}
|
||||
Protocol::Ipv4Turn => {}
|
||||
Protocol::OtherTurn => {
|
||||
let turn_packet = TurnPacket::new(net_packet.payload())?;
|
||||
// println!("{:?}",turn_packet);
|
||||
let src = turn_packet.source();
|
||||
let dest = turn_packet.destination();
|
||||
if dest == current_device.virtual_ip {
|
||||
match turn_packet::Protocol::from(net_packet.transport_protocol()) {
|
||||
turn_packet::Protocol::Punch => {
|
||||
let punch = Punch::parse_from_bytes(turn_packet.payload())?;
|
||||
if punch.virtual_ip.to_be_bytes() == src.octets() {
|
||||
if !punch.reply {
|
||||
let mut punch_reply = Punch::new();
|
||||
punch_reply.reply = true;
|
||||
punch_reply.virtual_ip = u32::from_be_bytes(current_device.virtual_ip.octets());
|
||||
punch_reply.step = punch.step;
|
||||
if let Err(_) = sender.try_send(punch) {
|
||||
return Ok(());
|
||||
}
|
||||
let nat_info = NAT_INFO.lock();
|
||||
if let Some(info) = nat_info.as_ref() {
|
||||
punch_reply.public_ip_list = info.public_ips.clone();
|
||||
punch_reply.public_port = info.public_port as u32;
|
||||
punch_reply.public_port_range = info.public_port_range as u32;
|
||||
punch_reply.nat_type = protobuf::EnumOrUnknown::new(info.nat_type);
|
||||
drop(nat_info);
|
||||
let bytes = punch_reply.write_to_bytes()?;
|
||||
let mut net_packet = NetPacket::new(vec![0u8; 4 + 8 + 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.set_ttl(255);
|
||||
let mut turn_packet = TurnPacket::new(net_packet.payload_mut())?;
|
||||
turn_packet.set_source(current_device.virtual_ip);
|
||||
turn_packet.set_destination(src);
|
||||
turn_packet.set_payload(&bytes);
|
||||
udp.send_to(net_packet.buffer(), peer_addr)?;
|
||||
}
|
||||
} else {
|
||||
let _ = sender.try_send(punch);
|
||||
}
|
||||
}
|
||||
}
|
||||
turn_packet::Protocol::UnKnow(_) => {}
|
||||
}
|
||||
} else {
|
||||
panic!("ip")
|
||||
}
|
||||
}
|
||||
Protocol::UnKnow(p) => {
|
||||
println!("未知协议:{}", p)
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
-248
@@ -1,248 +0,0 @@
|
||||
use std::{io, thread};
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use clap::Parser;
|
||||
use console::style;
|
||||
|
||||
use crate::handle::{CurrentDeviceInfo, DEVICE_LIST, DIRECT_ROUTE_TABLE, NAT_INFO, NatInfo, SERVER_RT};
|
||||
use crate::handle::registration_handler::registration;
|
||||
use crate::tun_device::create_tun;
|
||||
|
||||
pub mod tun_device;
|
||||
pub mod nat;
|
||||
pub mod error;
|
||||
pub mod handle;
|
||||
pub mod proto;
|
||||
pub mod protocol;
|
||||
#[cfg(windows)]
|
||||
pub mod admin_check;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author = "Lu Beilin", version, about = "一个虚拟网络工具,启动后会获取一个ip,相同token下的设备之间可以用ip直接通信")]
|
||||
struct Args {
|
||||
/// 32位字符
|
||||
/// 相同token的设备之间才能通信。
|
||||
/// 建议使用uuid保证唯一性。
|
||||
/// 32-bit characters.
|
||||
/// Only devices with the same token can communicate with each other.
|
||||
/// It is recommended to use uuid to ensure uniqueness
|
||||
#[arg(short, long)]
|
||||
token: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
#[cfg(windows)]
|
||||
if !admin_check::is_app_elevated() {
|
||||
let args: Vec<_> = std::env::args().collect();
|
||||
println!("{}", style("正在启动管理员权限执行...").red());
|
||||
if let Some(absolute_path) = std::env::current_exe()
|
||||
.ok()
|
||||
.and_then(|p| p.to_str().map(|p| p.to_string()))
|
||||
{
|
||||
let _ = runas::Command::new(&absolute_path).args(&args[1..]).status()
|
||||
.expect("failed to execute");
|
||||
} else {
|
||||
panic!("failed to execute")
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#[cfg(any(unix))]
|
||||
if sudo::RunningAs::Root != sudo::check() {
|
||||
println!("{}", style("需要使用root权限执行...").red());
|
||||
sudo::escalate_if_needed().unwrap();
|
||||
}
|
||||
|
||||
println!("{}", style("启动服务...").green());
|
||||
|
||||
let token = args.token;
|
||||
// let d = Local::now().timestamp().to_string();
|
||||
let mac_address = mac_address::get_mac_address().unwrap().unwrap().to_string();
|
||||
let server_address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(43, 139, 56, 10)), 29876);
|
||||
// let server_address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127,0,0,1)), 29876);
|
||||
let mut port = 101 as u16;
|
||||
let udp = loop {
|
||||
match UdpSocket::bind(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from(0), port))) {
|
||||
Ok(udp) => {
|
||||
break udp;
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::AddrInUse {
|
||||
port += 1;
|
||||
} else {
|
||||
println!("创建udp失败:{:?}", e);
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
//注册
|
||||
let response = registration(&udp, server_address, token, mac_address).unwrap();
|
||||
{
|
||||
let ip_list = response
|
||||
.virtual_ip_list
|
||||
.iter()
|
||||
.map(|ip| Ipv4Addr::from(*ip))
|
||||
.collect();
|
||||
let mut dev = DEVICE_LIST.lock();
|
||||
dev.0 = response.epoch;
|
||||
dev.1 = ip_list;
|
||||
}
|
||||
let virtual_ip = Ipv4Addr::from(response.virtual_ip);
|
||||
let virtual_gateway = Ipv4Addr::from(response.virtual_gateway);
|
||||
let virtual_netmask = Ipv4Addr::from(response.virtual_netmask);
|
||||
println!("virtual_gateway:{:?}", virtual_gateway);
|
||||
println!("virtual_netmask:{:?}", virtual_netmask);
|
||||
println!("当前设备ip(virtual_ip):{}", style(virtual_ip).green());
|
||||
//心跳线程
|
||||
{
|
||||
let udp = udp.try_clone().unwrap();
|
||||
let _ = thread::spawn(move || {
|
||||
if let Err(e) = handle::heartbeat_handler::handle_loop(udp, server_address) {
|
||||
println!("心跳线程停止:{:?}", e);
|
||||
}
|
||||
std::process::exit(1);
|
||||
});
|
||||
}
|
||||
//初始化nat数据
|
||||
handle::init_nat_info(response.public_ip, response.public_port as u16);
|
||||
// tun服务
|
||||
let (tun_writer, tun_reader) =
|
||||
create_tun(virtual_ip, virtual_netmask, virtual_gateway).unwrap();
|
||||
// 打洞数据通道
|
||||
let (punch_sender, cone_receiver, req_symmetric_receiver, res_symmetric_receiver) = handle::punch_handler::bounded();
|
||||
//udp数据处理
|
||||
{
|
||||
// 低优先级的udp数据通道
|
||||
let (sender, receiver) = crossbeam::channel::bounded(100);
|
||||
let udp1 = udp.try_clone().unwrap();
|
||||
let _ = thread::spawn(move || {
|
||||
let current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, server_address);
|
||||
if let Err(e) = handle::udp_recv_handler::recv_loop(
|
||||
udp1,
|
||||
server_address,
|
||||
sender,
|
||||
tun_writer,
|
||||
current_device,
|
||||
) {
|
||||
println!("udp数据处理线程停止:{:?}", e);
|
||||
}
|
||||
std::process::exit(1);
|
||||
});
|
||||
let udp1 = udp.try_clone().unwrap();
|
||||
let _ = thread::spawn(move || {
|
||||
let current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, server_address);
|
||||
if let Err(e) = handle::udp_recv_handler::other_loop(udp1, receiver, current_device, punch_sender) {
|
||||
println!("udp数据处理线程停止:{:?}", e);
|
||||
}
|
||||
std::process::exit(1);
|
||||
});
|
||||
}
|
||||
//打洞处理
|
||||
{
|
||||
let udp1 = udp.try_clone().unwrap();
|
||||
let _ = thread::spawn(move || {
|
||||
let current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, server_address);
|
||||
if let Err(e) = handle::punch_handler::cone_handle_loop(cone_receiver, udp1, current_device) {
|
||||
println!("打洞响应线程停止:{:?}", e);
|
||||
}
|
||||
});
|
||||
let udp1 = udp.try_clone().unwrap();
|
||||
let _ = thread::spawn(move || {
|
||||
let current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, server_address);
|
||||
if let Err(e) = handle::punch_handler::req_symmetric_handle_loop(req_symmetric_receiver, udp1, current_device) {
|
||||
println!("打洞触发线程停止:{:?}", e);
|
||||
}
|
||||
});
|
||||
let udp1 = udp.try_clone().unwrap();
|
||||
let _ = thread::spawn(move || {
|
||||
let current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, server_address);
|
||||
if let Err(e) = handle::punch_handler::res_symmetric_handle_loop(res_symmetric_receiver, udp1, current_device) {
|
||||
println!("打洞触发线程停止:{:?}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
//tun数据处理
|
||||
{
|
||||
let udp = udp.try_clone().unwrap();
|
||||
let _ = thread::spawn(move || {
|
||||
let current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, server_address);
|
||||
if let Err(e) = handle::tun_handler::handle_loop(udp, tun_reader, current_device) {
|
||||
println!("tun数据处理线程停止:{:?}", e);
|
||||
}
|
||||
std::process::exit(1);
|
||||
});
|
||||
}
|
||||
use console::Term;
|
||||
let term = Term::stdout();
|
||||
let current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, server_address);
|
||||
loop {
|
||||
println!("{}", style("Please enter the command (Usage: list,status,exit,help):").color256(102));
|
||||
match term.read_line() {
|
||||
Ok(cmd) => {
|
||||
command(cmd.trim(), ¤t_device);
|
||||
}
|
||||
Err(e) => {
|
||||
println!("read_line:{:?}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn command(cmd: &str, current_device: &CurrentDeviceInfo) {
|
||||
match cmd {
|
||||
"list" => {
|
||||
let server_delay = SERVER_RT.load(Ordering::Relaxed);
|
||||
let device_list_lock = DEVICE_LIST.lock();
|
||||
let (_epoch, device_list) = device_list_lock.clone();
|
||||
drop(device_list_lock);
|
||||
if device_list.is_empty() {
|
||||
println!("No other devices found");
|
||||
return;
|
||||
}
|
||||
for ip in device_list {
|
||||
if let Some(route_ref) = DIRECT_ROUTE_TABLE.get(&ip) {
|
||||
let str = if route_ref.value().delay >= 0 {
|
||||
format!("{}(p2p delay:{}ms)", ip, route_ref.value().delay)
|
||||
} else {
|
||||
format!("{}(p2p)", ip)
|
||||
};
|
||||
drop(route_ref);
|
||||
println!("{}", style(str).green());
|
||||
} else {
|
||||
let str = if server_delay >= 0 {
|
||||
format!("{}(relay delay:{}ms)", ip, server_delay * 2)
|
||||
} else {
|
||||
format!("{}(relay)", ip)
|
||||
};
|
||||
println!("{}", style(str).blue());
|
||||
}
|
||||
}
|
||||
}
|
||||
"status" => {
|
||||
let server_delay = SERVER_RT.load(Ordering::Relaxed);
|
||||
println!("Virtual ip:{}", style(current_device.virtual_ip).green());
|
||||
println!("Virtual gateway:{}", style(current_device.virtual_gateway).green());
|
||||
println!("Relay server :{}", style(current_device.connect_server).green());
|
||||
if server_delay >= 0 {
|
||||
println!("Delay of relay server :{}", style(server_delay).green());
|
||||
}
|
||||
}
|
||||
"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" => {
|
||||
std::process::exit(1);
|
||||
}
|
||||
_ => {
|
||||
println!("command {} not fount. ", style(cmd).red());
|
||||
println!("Try to enter: '{}'", style("help").green());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
pub mod check;
|
||||
@@ -1,223 +0,0 @@
|
||||
use std::fmt;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::error::*;
|
||||
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||
pub enum Protocol {
|
||||
Ping,
|
||||
Pong,
|
||||
PunchRequest,
|
||||
PunchResponse,
|
||||
UnKnow(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for Protocol {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
1 => Protocol::Ping,
|
||||
2 => Protocol::Pong,
|
||||
3 => Protocol::PunchRequest,
|
||||
4 => Protocol::PunchResponse,
|
||||
val => Protocol::UnKnow(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for Protocol {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
Protocol::Ping => 1,
|
||||
Protocol::Pong => 2,
|
||||
Protocol::PunchRequest => 3,
|
||||
Protocol::PunchResponse => 4,
|
||||
Protocol::UnKnow(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ControlPacket<B> {
|
||||
PingPacket(PingPacket<B>),
|
||||
PongPacket(PongPacket<B>),
|
||||
PunchRequest(PunchRequestPacket<B>),
|
||||
PunchResponse(PunchResponsePacket<B>),
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> ControlPacket<B> {
|
||||
pub fn new(protocol: u8, buffer: B) -> Result<ControlPacket<B>> {
|
||||
match Protocol::from(protocol) {
|
||||
Protocol::Ping => Ok(ControlPacket::PingPacket(PingPacket::new(buffer)?)),
|
||||
Protocol::Pong => Ok(ControlPacket::PongPacket(PongPacket::new(buffer)?)),
|
||||
Protocol::PunchRequest => Ok(ControlPacket::PunchRequest(PunchRequestPacket::new(
|
||||
buffer,
|
||||
)?)),
|
||||
Protocol::PunchResponse => Ok(ControlPacket::PunchResponse(PunchResponsePacket::new(
|
||||
buffer,
|
||||
)?)),
|
||||
Protocol::UnKnow(_) => Err(Error::NotSupport),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 网络探针
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct PingPacket<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct PongPacket<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
|
||||
impl<B: AsRef<[u8]>> PingPacket<B> {
|
||||
pub fn new(buffer: B) -> Result<PingPacket<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
if len != 8 + 4 {
|
||||
return Err(Error::InvalidPacket);
|
||||
}
|
||||
Ok(PingPacket { buffer })
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> PingPacket<B> {
|
||||
pub fn time(&self) -> i64 {
|
||||
i64::from_be_bytes(self.buffer.as_ref()[..8].try_into().unwrap())
|
||||
}
|
||||
pub fn epoch(&self) -> u32 {
|
||||
u32::from_be_bytes(self.buffer.as_ref()[8..12].try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> PingPacket<B> {
|
||||
pub fn set_time(&mut self, time: i64) {
|
||||
self.buffer.as_mut()[..8].copy_from_slice(&time.to_be_bytes())
|
||||
}
|
||||
pub fn set_epoch(&mut self, epoch: u32) {
|
||||
self.buffer.as_mut()[8..12].copy_from_slice(&epoch.to_be_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for PingPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("PingPacket")
|
||||
.field("time", &self.time())
|
||||
.field("epoch", &self.epoch())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> PongPacket<B> {
|
||||
pub fn new(buffer: B) -> Result<PongPacket<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
if len != 8 {
|
||||
return Err(Error::InvalidPacket);
|
||||
}
|
||||
Ok(PongPacket { buffer })
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> PongPacket<B> {
|
||||
pub fn time(&self) -> i64 {
|
||||
i64::from_be_bytes(self.buffer.as_ref()[..8].try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> PongPacket<B> {
|
||||
pub fn set_time(&mut self, time: i64) {
|
||||
self.buffer.as_mut()[..8].copy_from_slice(&time.to_be_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for PongPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("PongPacket")
|
||||
.field("time", &self.time())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
pub type TurnPongPacket<B> = TurnPingPacket<B>;
|
||||
|
||||
/// 探测目标延迟
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct TurnPingPacket<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> TurnPingPacket<B> {
|
||||
pub fn new(buffer: B) -> Result<TurnPingPacket<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
if len != 16 {
|
||||
return Err(Error::InvalidPacket);
|
||||
}
|
||||
Ok(TurnPingPacket { buffer })
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> TurnPingPacket<B> {
|
||||
// pub fn source(&self) -> Ipv4Addr {
|
||||
// let tmp:[u8;4] = self.buffer.as_ref()[..4].try_into().unwrap();
|
||||
// Ipv4Addr::from(tmp)
|
||||
// }
|
||||
// pub fn destination(&self) -> Ipv4Addr {
|
||||
// let tmp:[u8;4] = self.buffer.as_ref()[4..8].try_into().unwrap();
|
||||
// Ipv4Addr::from(tmp)
|
||||
// }
|
||||
pub fn time(&self) -> i64 {
|
||||
i64::from_be_bytes(self.buffer.as_ref()[8..].try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> TurnPingPacket<B> {
|
||||
pub fn set_source(&mut self, source: Ipv4Addr) {
|
||||
self.buffer.as_mut()[..4].copy_from_slice(&source.octets());
|
||||
}
|
||||
pub fn set_destination(&mut self, destination: Ipv4Addr) {
|
||||
self.buffer.as_mut()[4..8].copy_from_slice(&destination.octets());
|
||||
}
|
||||
pub fn set_time(&mut self, time: i64) {
|
||||
self.buffer.as_mut()[8..].copy_from_slice(&time.to_be_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
pub type PunchResponsePacket<B> = PunchPacket<B>;
|
||||
pub type PunchRequestPacket<B> = PunchPacket<B>;
|
||||
|
||||
/// nat穿透
|
||||
#[derive(Clone)]
|
||||
pub struct PunchPacket<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> PunchPacket<B> {
|
||||
pub fn new(buffer: B) -> Result<PunchPacket<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
if len != 8 {
|
||||
return Err(Error::InvalidPacket);
|
||||
}
|
||||
Ok(Self { buffer })
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> PunchPacket<B> {
|
||||
pub fn source(&self) -> Ipv4Addr {
|
||||
let tmp: [u8; 4] = self.buffer.as_ref()[..4].try_into().unwrap();
|
||||
Ipv4Addr::from(tmp)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> PunchPacket<B> {
|
||||
pub fn set_source(&mut self, source: Ipv4Addr) {
|
||||
self.buffer.as_mut()[..4].copy_from_slice(&source.octets());
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for PunchPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("PunchPacket")
|
||||
.field("source", &self.source())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
use std::fmt;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::error::*;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
pub enum Protocol {
|
||||
Punch,
|
||||
UnKnow(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for Protocol {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
1 => Protocol::Punch,
|
||||
val => Protocol::UnKnow(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for Protocol {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
Protocol::Punch => 1,
|
||||
Protocol::UnKnow(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TurnPacket<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> TurnPacket<B> {
|
||||
pub fn new(buffer: B) -> Result<TurnPacket<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
if len <= 8 {
|
||||
return Err(Error::InvalidPacket);
|
||||
}
|
||||
Ok(Self { buffer })
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> TurnPacket<B> {
|
||||
pub fn source(&self) -> Ipv4Addr {
|
||||
let tmp: [u8; 4] = self.buffer.as_ref()[..4].try_into().unwrap();
|
||||
Ipv4Addr::from(tmp)
|
||||
}
|
||||
pub fn destination(&self) -> Ipv4Addr {
|
||||
let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap();
|
||||
Ipv4Addr::from(tmp)
|
||||
}
|
||||
pub fn payload(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[8..]
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> TurnPacket<B> {
|
||||
pub fn payload_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.buffer.as_mut()[8..]
|
||||
}
|
||||
pub fn set_source(&mut self, source: Ipv4Addr) {
|
||||
self.buffer.as_mut()[..4].copy_from_slice(&source.octets());
|
||||
}
|
||||
pub fn set_destination(&mut self, destination: Ipv4Addr) {
|
||||
self.buffer.as_mut()[4..8].copy_from_slice(&destination.octets());
|
||||
}
|
||||
pub fn set_payload(&mut self, payload: &[u8]) {
|
||||
self.buffer.as_mut()[8..payload.len() + 8].copy_from_slice(payload)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for TurnPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("TurnPacket")
|
||||
.field("source", &self.source())
|
||||
.field("destination", &self.destination())
|
||||
.field("payload", &self.payload())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
use std::io;
|
||||
use std::io::{Error, Read, Write};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::os::unix::process::CommandExt;
|
||||
use std::process::Command;
|
||||
|
||||
use bytes::BufMut;
|
||||
use tun::Device;
|
||||
use tun::platform::posix::{Reader, Writer};
|
||||
|
||||
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();
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
config.platform(|config| {
|
||||
config.packet_information(true);
|
||||
});
|
||||
|
||||
let dev = tun::create(&config).unwrap();
|
||||
|
||||
// let up_eth_str: String = format!("ifconfig utun3 {:?} {:?} up ", 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(crate::error::Error::Stop(format!("设置地址失败:{:?}", up_eth_out)));
|
||||
// }
|
||||
// println!("{:?}", 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(crate::error::Error::Stop(format!("设置路由失败:{:?}", if_config_out)));
|
||||
}
|
||||
// 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 (reader, writer) = dev.split();
|
||||
Ok((
|
||||
TunWriter(writer, packet_information),
|
||||
TunReader(reader, packet_information),
|
||||
))
|
||||
}
|
||||
|
||||
pub struct TunReader(Reader, bool);
|
||||
|
||||
impl TunReader {
|
||||
pub fn read<'a>(&'a mut 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 struct TunWriter(Writer, bool);
|
||||
|
||||
impl TunWriter {
|
||||
pub fn write(&mut 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 main1() {
|
||||
// loop {
|
||||
// let len = reader.read(&mut buffer).unwrap();
|
||||
// println!("{:?}", &buffer[..len]);
|
||||
// match ip::Packet::new(&buffer[4..len]) {
|
||||
// Ok(ip::Packet::V4(pkt)) => {
|
||||
// match icmp::Packet::new(pkt.payload()) {
|
||||
// Ok(icmp) => {
|
||||
// match icmp.echo() {
|
||||
// Ok(icmp) => {
|
||||
// println!("{:?}", icmp);
|
||||
// let reply = ip::v4::Builder::default()
|
||||
// .id(0x42)
|
||||
// .unwrap()
|
||||
// .ttl(64)
|
||||
// .unwrap()
|
||||
// .source(pkt.destination())
|
||||
// .unwrap()
|
||||
// .destination(pkt.source())
|
||||
// .unwrap()
|
||||
// .icmp()
|
||||
// .unwrap()
|
||||
// .echo()
|
||||
// .unwrap()
|
||||
// .reply()
|
||||
// .unwrap()
|
||||
// .identifier(icmp.identifier())
|
||||
// .unwrap()
|
||||
// .sequence(icmp.sequence())
|
||||
// .unwrap()
|
||||
// .payload(icmp.payload())
|
||||
// .unwrap()
|
||||
// .build()
|
||||
// .unwrap();
|
||||
// let l = reply.len();
|
||||
// &mut buffer[4..(l + 4)].copy_from_slice(&reply);
|
||||
// // writer.write_all(&buffer[..4]).unwrap();
|
||||
// writer.write_all(&buffer[..(l + 4)]).unwrap();
|
||||
// }
|
||||
// Err(_) => {}
|
||||
// }
|
||||
// }
|
||||
// Err(_) => {}
|
||||
// }
|
||||
// }
|
||||
// _ => {}
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
@@ -1,132 +0,0 @@
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use libloading::Library;
|
||||
use wintun::{Adapter, Packet, Session};
|
||||
|
||||
use crate::error::*;
|
||||
|
||||
pub struct TunWriter(Arc<Session>);
|
||||
|
||||
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 struct TunReader(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 create_tun(
|
||||
address: Ipv4Addr,
|
||||
netmask: Ipv4Addr,
|
||||
gateway: Ipv4Addr,
|
||||
) -> 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(Error::Stop(format!("{:?}", e)));
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
println!("{}", console::style("wintun.dll not found").red());
|
||||
return Err(Error::Stop(format!("{:?}", e)));
|
||||
}
|
||||
}
|
||||
};
|
||||
let adapter = match Adapter::open(&win_tun, "Demo") {
|
||||
Ok(a) => a,
|
||||
Err(_) => match Adapter::create(&win_tun, "Example", "Demo", None) {
|
||||
Ok(adapter) => adapter,
|
||||
|
||||
Err(e) => return Err(Error::Stop(format!("{:?}", e))),
|
||||
},
|
||||
};
|
||||
let index = adapter.get_adapter_index().unwrap();
|
||||
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,
|
||||
);
|
||||
// println!("{}", set_mtu);
|
||||
// println!("{}", set_metric);
|
||||
// println!("{}", set_address);
|
||||
// 执行网卡初始化命令
|
||||
let out = std::process::Command::new("cmd")
|
||||
.arg("/C")
|
||||
.arg(set_mtu)
|
||||
.output()
|
||||
.unwrap();
|
||||
if !out.status.success() {
|
||||
return Err(Error::Stop(format!("设置mtu失败:{:?}", out)));
|
||||
}
|
||||
let out = std::process::Command::new("cmd")
|
||||
.arg("/C")
|
||||
.arg(set_metric)
|
||||
.output()
|
||||
.unwrap();
|
||||
if !out.status.success() {
|
||||
return Err(Error::Stop(format!("设置接口跃点失败:{:?}", out)));
|
||||
}
|
||||
let out = std::process::Command::new("cmd")
|
||||
.arg("/C")
|
||||
.arg(set_address)
|
||||
.output()
|
||||
.unwrap();
|
||||
if !out.status.success() {
|
||||
return Err(Error::Stop(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
|
||||
);
|
||||
// println!("{}", set_route);
|
||||
// 执行添加路由命令
|
||||
let out = std::process::Command::new("cmd")
|
||||
.arg("/C")
|
||||
.arg(set_route)
|
||||
.output()
|
||||
.unwrap();
|
||||
if !out.status.success() {
|
||||
return Err(Error::Stop(format!("添加路由失败:{:?}", out)));
|
||||
}
|
||||
let session = Arc::new(adapter.start_session(wintun::MAX_RING_CAPACITY).unwrap());
|
||||
let reader_session = session.clone();
|
||||
Ok((TunWriter(session), TunReader(reader_session)))
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
[package]
|
||||
name = "switch-desktop"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
switch = {path="../switch"}
|
||||
mac_address = "1.1.4"
|
||||
clap = { version = "4.0.32", features = ["derive"] }
|
||||
console = "0.15.2"
|
||||
dirs = "4.0.0"
|
||||
log = "0.4.17"
|
||||
log4rs = "1.2.0"
|
||||
#tokio = { version = "1.24.1", features = ["full"] }
|
||||
chrono = "0.4.23"
|
||||
|
||||
serde = "1.0"
|
||||
serde_yaml = "0.9"
|
||||
serde_json = "1.0.94"
|
||||
crossbeam = "0.8.2"
|
||||
lazy_static = "1.4.0"
|
||||
parking_lot = "0.12.1"
|
||||
|
||||
fs2 = "0.4.3"
|
||||
|
||||
os_info = "3.5.1"
|
||||
[target.'cfg(any(target_os = "linux",target_os = "macos"))'.dependencies]
|
||||
sudo = "0.6.0"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
winapi = { version = "0.3.9", features = ["handleapi", "processthreadsapi", "winnt", "securitybaseapi", "impl-default"] }
|
||||
#runas = "0.2.1"
|
||||
windows-service = "0.5.0"
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
use std::io;
|
||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::command::entity::{DeviceItem, RouteItem, Status};
|
||||
|
||||
pub struct CommandClient {
|
||||
udp: UdpSocket,
|
||||
}
|
||||
|
||||
impl CommandClient {
|
||||
pub fn new() -> io::Result<Self> {
|
||||
let port = crate::config::read_command_port()?;
|
||||
let udp = UdpSocket::bind("127.0.0.1:0")?;
|
||||
udp.set_read_timeout(Some(Duration::from_secs(2)))?;
|
||||
udp.connect(SocketAddr::V4(SocketAddrV4::new(
|
||||
Ipv4Addr::new(127, 0, 0, 1),
|
||||
port,
|
||||
)))?;
|
||||
Ok(Self { udp })
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandClient {
|
||||
pub fn list(&self) -> io::Result<Vec<DeviceItem>> {
|
||||
self.udp.send(b"list")?;
|
||||
let mut buf = [0; 10240];
|
||||
let len = self.udp.recv(&mut buf)?;
|
||||
match serde_json::from_slice::<Vec<DeviceItem>>(&buf[..len]) {
|
||||
Ok(val) => {
|
||||
Ok(val)
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{:?}",e);
|
||||
Err(io::Error::new(io::ErrorKind::Other, "data error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn route(&self) -> io::Result<Vec<RouteItem>> {
|
||||
self.udp.send(b"route")?;
|
||||
let mut buf = [0; 10240];
|
||||
let len = self.udp.recv(&mut buf)?;
|
||||
match serde_json::from_slice::<Vec<RouteItem>>(&buf[..len]) {
|
||||
Ok(val) => {
|
||||
Ok(val)
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{:?}",e);
|
||||
Err(io::Error::new(io::ErrorKind::Other, "data error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn status(&self) -> io::Result<Status> {
|
||||
self.udp.send(b"status")?;
|
||||
let mut buf = [0; 10240];
|
||||
let len = self.udp.recv(&mut buf)?;
|
||||
match serde_json::from_slice::<Status>(&buf[..len]) {
|
||||
Ok(val) => {
|
||||
Ok(val)
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{:?},{:?}",&buf[..len],e);
|
||||
Err(io::Error::new(io::ErrorKind::Other, "data error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(any(unix))]
|
||||
pub fn stop(&self) -> io::Result<String> {
|
||||
self.udp.send(b"stop")?;
|
||||
let mut buf = [0; 10240];
|
||||
let len = self.udp.recv(&mut buf)?;
|
||||
Ok(String::from_utf8(buf[..len].to_vec()).unwrap())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Status {
|
||||
pub name: String,
|
||||
pub virtual_ip: String,
|
||||
pub virtual_gateway: String,
|
||||
pub virtual_netmask: String,
|
||||
pub connect_status: String,
|
||||
pub relay_server: String,
|
||||
pub nat_type: String,
|
||||
pub public_ips: String,
|
||||
pub local_ip: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct RouteItem {
|
||||
pub destination: String,
|
||||
pub next_hop: String,
|
||||
pub metric: String,
|
||||
pub rt: String,
|
||||
pub interface: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct DeviceItem {
|
||||
pub name: String,
|
||||
pub virtual_ip: String,
|
||||
pub nat_type: String,
|
||||
pub public_ips: String,
|
||||
pub local_ip: String,
|
||||
pub nat_traversal_type: String,
|
||||
pub rt: String,
|
||||
pub status: String,
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
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(())
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
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)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
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(())
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
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"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
use console::{style, Style};
|
||||
|
||||
use crate::command::entity::{DeviceItem, RouteItem, Status};
|
||||
|
||||
pub mod table;
|
||||
|
||||
pub fn console_status(status: Status) {
|
||||
println!("Name: {}", style(status.name).green());
|
||||
println!("Virtual ip: {}", style(status.virtual_ip).green());
|
||||
println!("Virtual gateway: {}", style(status.virtual_gateway).green());
|
||||
println!("Virtual netmask: {}", style(status.virtual_netmask).green());
|
||||
println!("Connection status: {}", style(status.connect_status).green());
|
||||
println!("NAT type: {}", style(status.nat_type).green());
|
||||
println!("Relay server: {}", style(status.relay_server).green());
|
||||
println!("Public ips: {}", style(status.public_ips).green());
|
||||
println!("Local ip: {}", style(status.local_ip).green());
|
||||
}
|
||||
|
||||
pub fn console_route_table(mut list: Vec<RouteItem>) {
|
||||
if list.is_empty() {
|
||||
println!("No route found");
|
||||
return;
|
||||
}
|
||||
list.sort_by(|t1, t2| t1.destination.cmp(&t2.destination));
|
||||
let mut out_list = Vec::with_capacity(list.len());
|
||||
|
||||
out_list.push(vec![("Destination".to_string(), Style::new()),
|
||||
("Next Hop".to_string(), Style::new()),
|
||||
("Metric".to_string(), Style::new()),
|
||||
("Rt".to_string(), Style::new()),
|
||||
("Interface".to_string(), Style::new()), ]);
|
||||
for item in list {
|
||||
out_list.push(vec![(item.destination, Style::new().green()),
|
||||
(item.next_hop, Style::new().green()),
|
||||
(item.metric, Style::new().green()),
|
||||
(item.rt, Style::new().green()),
|
||||
(item.interface, Style::new().green())]);
|
||||
}
|
||||
|
||||
table::println_table(out_list)
|
||||
}
|
||||
|
||||
pub fn console_device_list(mut list: Vec<DeviceItem>) {
|
||||
if list.is_empty() {
|
||||
println!("No other devices found");
|
||||
return;
|
||||
}
|
||||
list.sort_by(|t1, t2| t1.virtual_ip.cmp(&t2.virtual_ip));
|
||||
list.sort_by(|t1, t2| t1.status.cmp(&t2.status));
|
||||
let mut out_list = Vec::with_capacity(list.len());
|
||||
//表头
|
||||
out_list.push(vec![("Name".to_string(), Style::new()),
|
||||
("Virtual Ip".to_string(), Style::new()),
|
||||
("Status".to_string(), Style::new()),
|
||||
("P2P/Relay".to_string(), Style::new()),
|
||||
("Rt".to_string(), Style::new())]);
|
||||
for item in list {
|
||||
if &item.status == "Online" {
|
||||
if &item.nat_traversal_type == "p2p" {
|
||||
out_list.push(vec![(item.name, Style::new().green()),
|
||||
(item.virtual_ip, Style::new().green()),
|
||||
(item.status, Style::new().green()),
|
||||
(item.nat_traversal_type, Style::new().green()),
|
||||
(item.rt, Style::new().green())]);
|
||||
} else {
|
||||
out_list.push(vec![(item.name, Style::new().yellow()),
|
||||
(item.virtual_ip, Style::new().yellow()),
|
||||
(item.status, Style::new().yellow()),
|
||||
(item.nat_traversal_type, Style::new().yellow()),
|
||||
(item.rt, Style::new().yellow())]);
|
||||
}
|
||||
} else {
|
||||
out_list.push(vec![(item.name, Style::new().color256(102)),
|
||||
(item.virtual_ip, Style::new().color256(102)),
|
||||
(item.status, Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102))]);
|
||||
}
|
||||
}
|
||||
table::println_table(out_list)
|
||||
}
|
||||
|
||||
pub fn console_device_list_all(mut list: Vec<DeviceItem>) {
|
||||
if list.is_empty() {
|
||||
println!("No other devices found");
|
||||
return;
|
||||
}
|
||||
list.sort_by(|t1, t2| t1.virtual_ip.cmp(&t2.virtual_ip));
|
||||
list.sort_by(|t1, t2| t1.status.cmp(&t2.status));
|
||||
let mut out_list = Vec::with_capacity(list.len());
|
||||
//表头
|
||||
out_list.push(vec![("Name".to_string(), Style::new()),
|
||||
("Virtual Ip".to_string(), Style::new()),
|
||||
("Status".to_string(), Style::new()),
|
||||
("NAT Type".to_string(), Style::new()),
|
||||
("Public Ips".to_string(), Style::new()),
|
||||
("Local Ip".to_string(), Style::new()),
|
||||
("P2P/Relay".to_string(), Style::new()),
|
||||
("Rt".to_string(), Style::new())]);
|
||||
for item in list {
|
||||
if &item.status == "Online" {
|
||||
if &item.nat_traversal_type == "p2p" {
|
||||
out_list.push(vec![(item.name, Style::new().green()),
|
||||
(item.virtual_ip, Style::new().green()),
|
||||
(item.status, Style::new().green()),
|
||||
(item.nat_traversal_type, Style::new().green()),
|
||||
(item.rt, Style::new().green()),
|
||||
(item.nat_type, Style::new().green()),
|
||||
(item.public_ips, Style::new().green()),
|
||||
(item.local_ip, Style::new().green())]);
|
||||
} else {
|
||||
out_list.push(vec![(item.name, Style::new().yellow()),
|
||||
(item.virtual_ip, Style::new().yellow()),
|
||||
(item.status, Style::new().yellow()),
|
||||
(item.nat_traversal_type, Style::new().yellow()),
|
||||
(item.rt, Style::new().yellow()),
|
||||
(item.nat_type, Style::new().yellow()),
|
||||
(item.public_ips, Style::new().yellow()),
|
||||
(item.local_ip, Style::new().yellow()), ]);
|
||||
}
|
||||
} else {
|
||||
out_list.push(vec![(item.name, Style::new().color256(102)),
|
||||
(item.virtual_ip, Style::new().color256(102)),
|
||||
(item.status, Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)), ]);
|
||||
}
|
||||
}
|
||||
table::println_table(out_list)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
use console::Style;
|
||||
|
||||
pub fn println_table(table: Vec<Vec<(String, Style)>>) {
|
||||
if table.is_empty() {
|
||||
return;
|
||||
}
|
||||
let mut width_list = vec![0; table[0].len()];
|
||||
for in_list in table.iter() {
|
||||
for (index, (item, _)) in in_list.iter().enumerate() {
|
||||
let width = console::measure_text_width(item) + 6;
|
||||
if width_list[index] < width {
|
||||
width_list[index] = width;
|
||||
}
|
||||
}
|
||||
}
|
||||
for in_list in table {
|
||||
for (col, (item, style)) in in_list.iter().enumerate() {
|
||||
let str = format!("{:1$}", item, width_list[col]);
|
||||
print!("{}", style.apply_to(str));
|
||||
}
|
||||
println!()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
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(())
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
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(())
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
// #[macro_use]
|
||||
// extern crate windows_service;
|
||||
|
||||
use std::ffi::OsString;
|
||||
use std::net::ToSocketAddrs;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use windows_service::{define_windows_service, service_control_handler, service_dispatcher};
|
||||
use windows_service::service::{
|
||||
ServiceControl, ServiceControlAccept, ServiceExitCode, ServiceState, ServiceStatus,
|
||||
};
|
||||
use windows_service::service_control_handler::ServiceControlHandlerResult;
|
||||
|
||||
use switch::core::{Config, Switch};
|
||||
|
||||
use crate::config;
|
||||
use crate::windows::config::read_config;
|
||||
use crate::windows::SERVICE_NAME;
|
||||
|
||||
define_windows_service!(ffi_service_main, switch_service_main);
|
||||
pub fn switch_service_main(arguments: Vec<OsString>) {
|
||||
if !arguments.is_empty() {
|
||||
if let Some(str) = arguments[0].to_str() {
|
||||
if str == "log" {
|
||||
let _ = config::log_config::log_service_init();
|
||||
}
|
||||
}
|
||||
}
|
||||
thread::spawn(|| match service_main() {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn service_main() -> windows_service::Result<()> {
|
||||
let parker = crossbeam::sync::Parker::new();
|
||||
let un_parker = parker.unparker().clone();
|
||||
let event_handler = move |control_event| -> ServiceControlHandlerResult {
|
||||
match control_event {
|
||||
// Notifies a service to report its current status information to the service
|
||||
// control manager. Always return NoError even if not implemented.
|
||||
ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
|
||||
|
||||
// Handle stop
|
||||
ServiceControl::Stop => {
|
||||
un_parker.unpark();
|
||||
log::info!("handler 服务停止");
|
||||
ServiceControlHandlerResult::NoError
|
||||
}
|
||||
_ => ServiceControlHandlerResult::NotImplemented,
|
||||
}
|
||||
};
|
||||
|
||||
// Register system service event handler.
|
||||
// The returned status handle should be used to report service status changes to the system.
|
||||
let status_handle =
|
||||
service_control_handler::register(SERVICE_NAME, event_handler)?;
|
||||
|
||||
// Tell the system that service is running
|
||||
status_handle.set_service_status(ServiceStatus {
|
||||
service_type: crate::windows::SERVICE_TYPE,
|
||||
current_state: ServiceState::Running,
|
||||
controls_accepted: ServiceControlAccept::STOP,
|
||||
exit_code: ServiceExitCode::Win32(0),
|
||||
checkpoint: 0,
|
||||
wait_hint: Duration::default(),
|
||||
process_id: None,
|
||||
})?;
|
||||
match start_switch() {
|
||||
Ok(switch) => {
|
||||
parker.park();
|
||||
if let Err(e) = switch.stop() {
|
||||
log::warn!("switch stop:{:?}",e)
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{:?}",e);
|
||||
}
|
||||
}
|
||||
status_handle.set_service_status(ServiceStatus {
|
||||
service_type: crate::windows::SERVICE_TYPE,
|
||||
current_state: ServiceState::Stopped,
|
||||
controls_accepted: ServiceControlAccept::empty(),
|
||||
exit_code: ServiceExitCode::Win32(0),
|
||||
checkpoint: 0,
|
||||
wait_hint: Duration::default(),
|
||||
process_id: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn start_switch() -> switch::Result<Arc<Switch>> {
|
||||
if let Some(config) = read_config() {
|
||||
let device_id = config.device_id;
|
||||
if device_id.trim().is_empty() {
|
||||
return Err(switch::error::Error::Stop("Device id error".to_string()));
|
||||
}
|
||||
let server_address = if let Some(server_address) = config.server
|
||||
.to_socket_addrs()?
|
||||
.next() {
|
||||
server_address
|
||||
} else {
|
||||
return Err(switch::error::Error::Stop("server address error".to_string()));
|
||||
};
|
||||
let nat_test_server = config.nat_test_server.iter()
|
||||
.flat_map(|a| a.to_socket_addrs())
|
||||
.flatten()
|
||||
.collect::<Vec<_>>();
|
||||
if nat_test_server.is_empty() {
|
||||
return Err(switch::error::Error::Stop("nat test server address error".to_string()));
|
||||
}
|
||||
let config = Config::new(
|
||||
config.token,
|
||||
device_id,
|
||||
config.name,
|
||||
server_address,
|
||||
nat_test_server);
|
||||
let switch = Switch::start(config)?;
|
||||
log::info!("switch-service服务启动");
|
||||
let switch = Arc::new(switch);
|
||||
let command_server = crate::command::server::CommandServer::new();
|
||||
let switch1 = switch.clone();
|
||||
thread::spawn(move || {
|
||||
if let Err(e) = config::update_pid(std::process::id()) {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
if let Err(e) = command_server.start(switch1) {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
});
|
||||
Ok(switch)
|
||||
} else {
|
||||
Err(switch::error::Error::Stop("配置文件为空".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start() {
|
||||
log::info!("以服务的方式启动");
|
||||
service_dispatcher::start(SERVICE_NAME, ffi_service_main).unwrap();
|
||||
}
|
||||
@@ -1,76 +1,76 @@
|
||||
/// 使用 https://github.com/spa5k/is_sudo/blob/main/src/window.rs
|
||||
use std::io::Error;
|
||||
use std::ptr;
|
||||
|
||||
use winapi::um::handleapi::CloseHandle;
|
||||
use winapi::um::processthreadsapi::{GetCurrentProcess, OpenProcessToken};
|
||||
use winapi::um::securitybaseapi::GetTokenInformation;
|
||||
use winapi::um::winnt::{HANDLE, TOKEN_ELEVATION, TOKEN_QUERY, TokenElevation};
|
||||
|
||||
// Use std::io::Error::last_os_error for errors.
|
||||
// NOTE: For this example I'm simple passing on the OS error.
|
||||
// However, customising the error could provide more context
|
||||
|
||||
/// Returns true if the current process has admin rights, otherwise false.
|
||||
pub fn is_app_elevated() -> bool {
|
||||
_is_app_elevated().unwrap_or(false)
|
||||
}
|
||||
|
||||
/// On success returns a bool indicating if the current process has admin rights.
|
||||
/// Otherwise returns an OS error.
|
||||
///
|
||||
/// This is unlikely to fail but if it does it's even more unlikely that you have admin permissions anyway.
|
||||
/// Therefore the public function above simply eats the error and returns a bool.
|
||||
fn _is_app_elevated() -> Result<bool, Error> {
|
||||
let token = QueryAccessToken::from_current_process()?;
|
||||
token.is_elevated()
|
||||
}
|
||||
|
||||
/// A safe wrapper around querying Windows access tokens.
|
||||
pub struct QueryAccessToken(HANDLE);
|
||||
|
||||
impl QueryAccessToken {
|
||||
pub fn from_current_process() -> Result<Self, Error> {
|
||||
unsafe {
|
||||
let mut handle: HANDLE = ptr::null_mut();
|
||||
let result = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut handle);
|
||||
|
||||
if result != 0 {
|
||||
Ok(Self(handle))
|
||||
} else {
|
||||
Err(Error::last_os_error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// On success returns a bool indicating if the access token has elevated privilidges.
|
||||
/// Otherwise returns an OS error.
|
||||
pub fn is_elevated(&self) -> Result<bool, Error> {
|
||||
unsafe {
|
||||
let mut elevation = TOKEN_ELEVATION::default();
|
||||
let size = std::mem::size_of::<TOKEN_ELEVATION>() as u32;
|
||||
let mut ret_size = size;
|
||||
// The weird looking repetition of `as *mut _` is casting the reference to a c_void pointer.
|
||||
if GetTokenInformation(
|
||||
self.0,
|
||||
TokenElevation,
|
||||
&mut elevation as *mut _ as *mut _,
|
||||
size,
|
||||
&mut ret_size,
|
||||
) != 0
|
||||
{
|
||||
Ok(elevation.TokenIsElevated != 0)
|
||||
} else {
|
||||
Err(Error::last_os_error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for QueryAccessToken {
|
||||
fn drop(&mut self) {
|
||||
if !self.0.is_null() {
|
||||
unsafe { CloseHandle(self.0) };
|
||||
}
|
||||
}
|
||||
}
|
||||
/// 使用 https://github.com/spa5k/is_sudo/blob/main/src/window.rs
|
||||
use std::io::Error;
|
||||
use std::ptr;
|
||||
|
||||
use winapi::um::handleapi::CloseHandle;
|
||||
use winapi::um::processthreadsapi::{GetCurrentProcess, OpenProcessToken};
|
||||
use winapi::um::securitybaseapi::GetTokenInformation;
|
||||
use winapi::um::winnt::{TokenElevation, HANDLE, TOKEN_ELEVATION, TOKEN_QUERY};
|
||||
|
||||
// Use std::io::Error::last_os_error for errors.
|
||||
// NOTE: For this example I'm simple passing on the OS error.
|
||||
// However, customising the error could provide more context
|
||||
|
||||
/// Returns true if the current process has admin rights, otherwise false.
|
||||
pub fn is_app_elevated() -> bool {
|
||||
_is_app_elevated().unwrap_or(false)
|
||||
}
|
||||
|
||||
/// On success returns a bool indicating if the current process has admin rights.
|
||||
/// Otherwise returns an OS error.
|
||||
///
|
||||
/// This is unlikely to fail but if it does it's even more unlikely that you have admin permissions anyway.
|
||||
/// Therefore the public function above simply eats the error and returns a bool.
|
||||
fn _is_app_elevated() -> Result<bool, Error> {
|
||||
let token = QueryAccessToken::from_current_process()?;
|
||||
token.is_elevated()
|
||||
}
|
||||
|
||||
/// A safe wrapper around querying Windows access tokens.
|
||||
pub struct QueryAccessToken(HANDLE);
|
||||
|
||||
impl QueryAccessToken {
|
||||
pub fn from_current_process() -> Result<Self, Error> {
|
||||
unsafe {
|
||||
let mut handle: HANDLE = ptr::null_mut();
|
||||
let result = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut handle);
|
||||
|
||||
if result != 0 {
|
||||
Ok(Self(handle))
|
||||
} else {
|
||||
Err(Error::last_os_error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// On success returns a bool indicating if the access token has elevated privilidges.
|
||||
/// Otherwise returns an OS error.
|
||||
pub fn is_elevated(&self) -> Result<bool, Error> {
|
||||
unsafe {
|
||||
let mut elevation = TOKEN_ELEVATION::default();
|
||||
let size = std::mem::size_of::<TOKEN_ELEVATION>() as u32;
|
||||
let mut ret_size = size;
|
||||
// The weird looking repetition of `as *mut _` is casting the reference to a c_void pointer.
|
||||
if GetTokenInformation(
|
||||
self.0,
|
||||
TokenElevation,
|
||||
&mut elevation as *mut _ as *mut _,
|
||||
size,
|
||||
&mut ret_size,
|
||||
) != 0
|
||||
{
|
||||
Ok(elevation.TokenIsElevated != 0)
|
||||
} else {
|
||||
Err(Error::last_os_error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for QueryAccessToken {
|
||||
fn drop(&mut self) {
|
||||
if !self.0.is_null() {
|
||||
unsafe { CloseHandle(self.0) };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
[package]
|
||||
name = "switch"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
packet = { path = "./packet" }
|
||||
p2p_channel = { path = "./p2p_channel" }
|
||||
bytes = "1.3.0"
|
||||
log = "0.4.17"
|
||||
libc = "0.2.137"
|
||||
|
||||
dashmap = "5.4.0"
|
||||
crossbeam = "0.8.2"
|
||||
crossbeam-skiplist = "0.1"
|
||||
parking_lot = "0.12.1"
|
||||
|
||||
rsa = "0.7.2"
|
||||
rand = "0.8.5"
|
||||
sha2 = { version = "0.10.6", features = ["oid"] }
|
||||
|
||||
thiserror = "1.0.37"
|
||||
chrono = "0.4.23"
|
||||
#lazy_static = "1.4.0"
|
||||
#moka = "0.9.6"
|
||||
protobuf = "3.2.0"
|
||||
#local-ip-address = "0.4.9"
|
||||
|
||||
#mio = {version = "0.8.6",features = ["os-poll", "net"]}
|
||||
#tokio = { version = "1.24.1", features = ["full"] }
|
||||
[target.'cfg(any(unix))'.dependencies]
|
||||
tun = { path = "./rust-tun" }
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
wintun = { path = "./wintun" }
|
||||
libloading = "0.7.4"
|
||||
|
||||
[build-dependencies]
|
||||
protobuf-codegen = "3.2.0"
|
||||
protoc-bin-vendored = "3.0.0"
|
||||
Submodule
+1
Submodule switch/p2p_channel added at 9d2e02f629
@@ -133,7 +133,6 @@ fn u32c(x: u8, y: u8) -> u32 {
|
||||
((x as u32) << 8) | y as u32
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -1 +1 @@
|
||||
pub mod udp;
|
||||
pub mod udp;
|
||||
@@ -1,40 +1,41 @@
|
||||
syntax = "proto3";
|
||||
message RegistrationRequest{
|
||||
string token = 1;
|
||||
string mac_address = 2;
|
||||
}
|
||||
|
||||
message RegistrationResponse{
|
||||
fixed32 virtual_ip = 1;
|
||||
fixed32 virtual_gateway = 2;
|
||||
fixed32 virtual_netmask = 3;
|
||||
uint32 epoch = 4;
|
||||
repeated fixed32 virtual_ip_list = 5;
|
||||
fixed32 public_ip = 6;
|
||||
uint32 public_port = 7;
|
||||
}
|
||||
|
||||
message DeviceList{
|
||||
uint32 epoch = 1;
|
||||
repeated fixed32 virtual_ip_list = 2;
|
||||
}
|
||||
|
||||
message Punch{
|
||||
fixed32 virtual_ip = 1;
|
||||
repeated fixed32 public_ip_list = 2;
|
||||
uint32 public_port = 3;
|
||||
uint32 public_port_range = 4;
|
||||
NatType nat_type = 5;
|
||||
bool reply = 6;
|
||||
Step step = 7;
|
||||
}
|
||||
enum NatType{
|
||||
Symmetric = 0;
|
||||
Cone = 1;
|
||||
}
|
||||
enum Step{
|
||||
Step1 = 0;
|
||||
Step2 = 1;
|
||||
Step3 = 2;
|
||||
Step4 = 3;
|
||||
syntax = "proto3";
|
||||
message RegistrationRequest{
|
||||
string token = 1;
|
||||
string device_id = 2;
|
||||
string name = 3;
|
||||
bool is_fast = 4;
|
||||
}
|
||||
|
||||
message RegistrationResponse{
|
||||
fixed32 virtual_ip = 1;
|
||||
fixed32 virtual_gateway = 2;
|
||||
fixed32 virtual_netmask = 3;
|
||||
uint32 epoch = 4;
|
||||
repeated DeviceInfo device_info_list = 5;
|
||||
fixed32 public_ip = 6;
|
||||
uint32 public_port = 7;
|
||||
}
|
||||
message DeviceInfo{
|
||||
string name = 1;
|
||||
fixed32 virtual_ip = 2;
|
||||
uint32 device_status = 3;
|
||||
}
|
||||
|
||||
message DeviceList{
|
||||
uint32 epoch = 1;
|
||||
repeated DeviceInfo device_info_list = 2;
|
||||
}
|
||||
|
||||
message PunchInfo{
|
||||
repeated fixed32 public_ip_list = 2;
|
||||
uint32 public_port = 3;
|
||||
uint32 public_port_range = 4;
|
||||
PunchNatType nat_type = 5;
|
||||
bool reply = 6;
|
||||
fixed32 local_ip = 7;
|
||||
uint32 local_port = 8;
|
||||
}
|
||||
enum PunchNatType{
|
||||
Symmetric = 0;
|
||||
Cone = 1;
|
||||
}
|
||||
@@ -15,31 +15,11 @@ libc = "0.2"
|
||||
thiserror = "1"
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "ios", target_os = "android"))'.dependencies]
|
||||
tokio = { version = "1", features = ["net", "macros"], optional = true }
|
||||
tokio-util = { version = "0.6", features = ["codec"], optional = true }
|
||||
bytes = { version = "1", optional = true }
|
||||
byteorder = { version = "1", optional = true }
|
||||
# This is only for the `ready` macro.
|
||||
futures-core = { version = "0.3", optional = true }
|
||||
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies]
|
||||
ioctl = { version = "0.6", package = "ioctl-sys" }
|
||||
|
||||
[dev-dependencies]
|
||||
packet = "0.1"
|
||||
futures = "0.3"
|
||||
|
||||
[features]
|
||||
async = ["tokio", "tokio-util", "bytes", "byteorder", "futures-core"]
|
||||
|
||||
[[example]]
|
||||
name = "read-async"
|
||||
required-features = [ "async", "tokio/rt-multi-thread" ]
|
||||
|
||||
[[example]]
|
||||
name = "read-async-codec"
|
||||
required-features = [ "async", "tokio/rt-multi-thread" ]
|
||||
|
||||
[[example]]
|
||||
name = "ping-tun"
|
||||
required-features = [ "async", "tokio/rt-multi-thread" ]
|
||||
@@ -12,15 +12,14 @@
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::io::{Read, Write};
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::configuration::Configuration;
|
||||
use crate::error::*;
|
||||
|
||||
/// A TUN device.
|
||||
pub trait Device: Read + Write {
|
||||
type Queue: Read + Write;
|
||||
pub trait Device {
|
||||
type Queue ;
|
||||
|
||||
/// Reconfigure the device.
|
||||
fn configure(&mut self, config: &Configuration) -> Result<()> {
|
||||
@@ -91,5 +90,5 @@ pub trait Device: Read + Write {
|
||||
fn set_mtu(&mut self, value: i32) -> Result<()>;
|
||||
|
||||
/// Get a device queue.
|
||||
fn queue(&mut self, index: usize) -> Option<&mut Self::Queue>;
|
||||
fn queue(&self, index: usize) -> Option<&Self::Queue>;
|
||||
}
|
||||
@@ -27,27 +27,6 @@ pub use crate::configuration::{Configuration, Layer};
|
||||
pub mod platform;
|
||||
pub use crate::platform::create;
|
||||
|
||||
#[cfg(all(
|
||||
feature = "async",
|
||||
any(
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "android"
|
||||
)
|
||||
))]
|
||||
pub mod r#async;
|
||||
#[cfg(all(
|
||||
feature = "async",
|
||||
any(
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "android"
|
||||
)
|
||||
))]
|
||||
pub use r#async::*;
|
||||
|
||||
pub fn configure() -> Configuration {
|
||||
Configuration::default()
|
||||
}
|
||||
+43
-116
@@ -13,10 +13,10 @@
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::io::{self, Read, Write};
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
use std::vec::Vec;
|
||||
@@ -92,7 +92,7 @@ impl Device {
|
||||
}
|
||||
|
||||
queues.push(Queue {
|
||||
tun,
|
||||
tun: Arc::new(tun),
|
||||
pi_enabled: config.platform.packet_information,
|
||||
});
|
||||
}
|
||||
@@ -126,45 +126,40 @@ impl Device {
|
||||
req
|
||||
}
|
||||
|
||||
/// Make the device persistent.
|
||||
pub fn persist(&mut self) -> Result<()> {
|
||||
unsafe {
|
||||
if tunsetpersist(self.as_raw_fd(), &1) < 0 {
|
||||
Err(io::Error::last_os_error().into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
// /// Make the device persistent.
|
||||
// pub fn persist(&mut self) -> Result<()> {
|
||||
// unsafe {
|
||||
// if tunsetpersist(self.as_raw_fd(), &1) < 0 {
|
||||
// Err(io::Error::last_os_error().into())
|
||||
// } else {
|
||||
// Ok(())
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/// Set the owner of the device.
|
||||
pub fn user(&mut self, value: i32) -> Result<()> {
|
||||
unsafe {
|
||||
if tunsetowner(self.as_raw_fd(), &value) < 0 {
|
||||
Err(io::Error::last_os_error().into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the group of the device.
|
||||
pub fn group(&mut self, value: i32) -> Result<()> {
|
||||
unsafe {
|
||||
if tunsetgroup(self.as_raw_fd(), &value) < 0 {
|
||||
Err(io::Error::last_os_error().into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn split(mut self) -> (posix::Reader, posix::Writer) {
|
||||
let queue = self.queues.swap_remove(0);
|
||||
let fd = Arc::new(queue.tun);
|
||||
(posix::Reader(fd.clone()), posix::Writer(fd.clone()))
|
||||
}
|
||||
// /// Set the owner of the device.
|
||||
// pub fn user(&mut self, value: i32) -> Result<()> {
|
||||
// unsafe {
|
||||
// if tunsetowner(self.as_raw_fd(), &value) < 0 {
|
||||
// Err(io::Error::last_os_error().into())
|
||||
// } else {
|
||||
// Ok(())
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /// Set the group of the device.
|
||||
// pub fn group(&mut self, value: i32) -> Result<()> {
|
||||
// unsafe {
|
||||
// if tunsetgroup(self.as_raw_fd(), &value) < 0 {
|
||||
// Err(io::Error::last_os_error().into())
|
||||
// } else {
|
||||
// Ok(())
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
/// Return whether the device has packet information
|
||||
pub fn has_packet_information(&mut self) -> bool {
|
||||
pub fn has_packet_information(&self) -> bool {
|
||||
self.queues[0].has_packet_information()
|
||||
}
|
||||
|
||||
@@ -174,30 +169,6 @@ impl Device {
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Device {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.queues[0].read(buf)
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.queues[0].read_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Device {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.queues[0].write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.queues[0].flush()
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.queues[0].write_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl D for Device {
|
||||
type Queue = Queue;
|
||||
|
||||
@@ -377,73 +348,29 @@ impl D for Device {
|
||||
}
|
||||
}
|
||||
|
||||
fn queue(&mut self, index: usize) -> Option<&mut Self::Queue> {
|
||||
self.queues.get_mut(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Device {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.queues[0].as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for Device {
|
||||
fn into_raw_fd(mut self) -> RawFd {
|
||||
// It is Ok to swap the first queue with the last one, because the self will be dropped afterwards
|
||||
let queue = self.queues.swap_remove(0);
|
||||
queue.into_raw_fd()
|
||||
fn queue(&self, index: usize) -> Option<&Self::Queue> {
|
||||
self.queues.get(index)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Queue {
|
||||
tun: Fd,
|
||||
tun: Arc<Fd>,
|
||||
pi_enabled: bool,
|
||||
}
|
||||
|
||||
impl Queue {
|
||||
pub fn has_packet_information(&mut self) -> bool {
|
||||
pub fn has_packet_information(&self) -> bool {
|
||||
self.pi_enabled
|
||||
}
|
||||
|
||||
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||
self.tun.set_nonblock()
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Queue {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.tun.read(buf)
|
||||
pub fn reader(&self) -> posix::Reader {
|
||||
posix::Reader(self.tun.clone())
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.tun.read_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Queue {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.tun.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.tun.flush()
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.tun.write_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Queue {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.tun.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for Queue {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.tun.into_raw_fd()
|
||||
pub fn writer(&self) -> posix::Writer {
|
||||
posix::Writer(self.tun.clone())
|
||||
}
|
||||
}
|
||||
|
||||
+85
-78
@@ -14,15 +14,15 @@
|
||||
#![allow(unused_variables)]
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use libc;
|
||||
use libc::{c_char, c_uint, c_void, sockaddr, socklen_t, AF_INET, SOCK_DGRAM};
|
||||
use libc::{AF_INET, c_char, c_uint, c_void, SOCK_DGRAM, sockaddr, socklen_t};
|
||||
|
||||
use crate::configuration::{Configuration, Layer};
|
||||
use crate::device::Device as D;
|
||||
@@ -121,7 +121,7 @@ impl Device {
|
||||
name: CStr::from_ptr(name.as_ptr() as *const c_char)
|
||||
.to_string_lossy()
|
||||
.into(),
|
||||
queue: Queue { tun: tun },
|
||||
queue: Queue { tun: Arc::new(tun) },
|
||||
ctl: ctl,
|
||||
}
|
||||
};
|
||||
@@ -165,11 +165,11 @@ impl Device {
|
||||
}
|
||||
}
|
||||
|
||||
/// Split the interface into a `Reader` and `Writer`.
|
||||
pub fn split(self) -> (posix::Reader, posix::Writer) {
|
||||
let fd = Arc::new(self.queue.tun);
|
||||
(posix::Reader(fd.clone()), posix::Writer(fd.clone()))
|
||||
}
|
||||
// /// Split the interface into a `Reader` and `Writer`.
|
||||
// pub fn split(self) -> (posix::Reader, posix::Writer) {
|
||||
// let fd = Arc::new(self.queue.tun);
|
||||
// (posix::Reader(fd.clone()), posix::Writer(fd.clone()))
|
||||
// }
|
||||
|
||||
/// Return whether the device has packet information
|
||||
pub fn has_packet_information(&self) -> bool {
|
||||
@@ -182,29 +182,29 @@ impl Device {
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Device {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.queue.tun.read(buf)
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.queue.tun.read_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Device {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.queue.tun.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.queue.tun.flush()
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.queue.tun.write_vectored(bufs)
|
||||
}
|
||||
}
|
||||
// impl Read for Device {
|
||||
// fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
// self.queue.tun.read(buf)
|
||||
// }
|
||||
//
|
||||
// fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
// self.queue.tun.read_vectored(bufs)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl Write for Device {
|
||||
// fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
// self.queue.tun.write(buf)
|
||||
// }
|
||||
//
|
||||
// fn flush(&mut self) -> io::Result<()> {
|
||||
// self.queue.tun.flush()
|
||||
// }
|
||||
//
|
||||
// fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
// self.queue.tun.write_vectored(bufs)
|
||||
// }
|
||||
// }
|
||||
|
||||
impl D for Device {
|
||||
type Queue = Queue;
|
||||
@@ -365,29 +365,29 @@ impl D for Device {
|
||||
}
|
||||
}
|
||||
|
||||
fn queue(&mut self, index: usize) -> Option<&mut Self::Queue> {
|
||||
fn queue(&self, index: usize) -> Option<&Self::Queue> {
|
||||
if index > 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(&mut self.queue)
|
||||
Some(&self.queue)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Device {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.queue.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for Device {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.queue.into_raw_fd()
|
||||
}
|
||||
}
|
||||
// impl AsRawFd for Device {
|
||||
// fn as_raw_fd(&self) -> RawFd {
|
||||
// self.queue.as_raw_fd()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl IntoRawFd for Device {
|
||||
// fn into_raw_fd(self) -> RawFd {
|
||||
// self.queue.into_raw_fd()
|
||||
// }
|
||||
// }
|
||||
|
||||
pub struct Queue {
|
||||
tun: Fd,
|
||||
tun: Arc<Fd>,
|
||||
}
|
||||
|
||||
impl Queue {
|
||||
@@ -399,40 +399,47 @@ impl Queue {
|
||||
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||
self.tun.set_nonblock()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Queue {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.tun.as_raw_fd()
|
||||
pub fn reader(&self) -> posix::Reader {
|
||||
posix::Reader(self.tun.clone())
|
||||
}
|
||||
pub fn writer(&self) -> posix::Writer {
|
||||
posix::Writer(self.tun.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for Queue {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.tun.into_raw_fd()
|
||||
}
|
||||
}
|
||||
// impl AsRawFd for Queue {
|
||||
// fn as_raw_fd(&self) -> RawFd {
|
||||
// self.tun.as_raw_fd()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl IntoRawFd for Queue {
|
||||
// fn into_raw_fd(self) -> RawFd {
|
||||
// self.tun.into_raw_fd()
|
||||
// }
|
||||
// }
|
||||
|
||||
impl Read for Queue {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.tun.read(buf)
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.tun.read_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Queue {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.tun.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.tun.flush()
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.tun.write_vectored(bufs)
|
||||
}
|
||||
}
|
||||
// impl Read for Queue {
|
||||
// fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
// self.tun.read(buf)
|
||||
// }
|
||||
//
|
||||
// fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
// self.tun.read_vectored(bufs)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl Write for Queue {
|
||||
// fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
// self.tun.write(buf)
|
||||
// }
|
||||
//
|
||||
// fn flush(&mut self) -> io::Result<()> {
|
||||
// self.tun.flush()
|
||||
// }
|
||||
//
|
||||
// fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
// self.tun.write_vectored(bufs)
|
||||
// }
|
||||
// }
|
||||
+32
-17
@@ -12,22 +12,24 @@
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::os::unix::io::{AsRawFd,RawFd};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::platform::posix::Fd;
|
||||
use libc;
|
||||
|
||||
/// Read-only end for a file descriptor.
|
||||
#[derive(Clone)]
|
||||
pub struct Reader(pub(crate) Arc<Fd>);
|
||||
|
||||
/// Write-only end for a file descriptor.
|
||||
#[derive(Clone)]
|
||||
pub struct Writer(pub(crate) Arc<Fd>);
|
||||
|
||||
impl Read for Reader {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
impl Reader {
|
||||
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let amount = libc::read(self.0.as_raw_fd(), buf.as_mut_ptr() as *mut _, buf.len());
|
||||
|
||||
@@ -39,7 +41,7 @@ impl Read for Reader {
|
||||
}
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
pub fn read_vectored(&self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let mut msg: libc::msghdr = mem::zeroed();
|
||||
// msg.msg_name: NULL
|
||||
@@ -57,8 +59,8 @@ impl Read for Reader {
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Writer {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
impl Writer {
|
||||
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let amount = libc::write(self.0.as_raw_fd(), buf.as_ptr() as *const _, buf.len());
|
||||
|
||||
@@ -70,11 +72,8 @@ impl Write for Writer {
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
pub fn write_vectored(&self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let mut msg: libc::msghdr = mem::zeroed();
|
||||
// msg.msg_name = NULL
|
||||
@@ -90,6 +89,22 @@ impl Write for Writer {
|
||||
Ok(n as usize)
|
||||
}
|
||||
}
|
||||
pub fn write_all(&self, mut buf: &[u8]) -> io::Result<()> {
|
||||
while !buf.is_empty() {
|
||||
match self.write(buf) {
|
||||
Ok(0) => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::WriteZero,
|
||||
"failed to write whole buffer",
|
||||
));
|
||||
}
|
||||
Ok(n) => buf = &buf[n..],
|
||||
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Reader {
|
||||
@@ -97,9 +112,9 @@ 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 {
|
||||
// self.0.as_raw_fd()
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1,140 @@
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,8 @@ pub enum Error {
|
||||
NotSupport,
|
||||
#[error("Stop")]
|
||||
Stop(String),
|
||||
#[error("Warn")]
|
||||
Warn(String),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
@@ -0,0 +1,117 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
use std::net::{Ipv4Addr, SocketAddr};
|
||||
|
||||
pub mod heartbeat_handler;
|
||||
pub mod punch_handler;
|
||||
pub mod registration_handler;
|
||||
pub mod tun_handler;
|
||||
pub mod recv_handler;
|
||||
|
||||
/// 是否在一个网段
|
||||
fn check_dest(dest: Ipv4Addr, virtual_netmask: Ipv4Addr, virtual_network: Ipv4Addr) -> bool {
|
||||
u32::from_be_bytes(dest.octets()) & u32::from_be_bytes(virtual_netmask.octets())
|
||||
== u32::from_be_bytes(virtual_network.octets())
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct PeerDeviceInfo {
|
||||
pub virtual_ip: Ipv4Addr,
|
||||
pub name: String,
|
||||
pub status: PeerDeviceStatus,
|
||||
}
|
||||
|
||||
impl PeerDeviceInfo {
|
||||
pub fn new(virtual_ip: Ipv4Addr, name: String, status: u8) -> Self {
|
||||
Self {
|
||||
virtual_ip,
|
||||
name,
|
||||
status: PeerDeviceStatus::from(status),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum PeerDeviceStatus {
|
||||
Online,
|
||||
Offline,
|
||||
}
|
||||
|
||||
impl Into<u8> for PeerDeviceStatus {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
PeerDeviceStatus::Online => 0,
|
||||
PeerDeviceStatus::Offline => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for PeerDeviceStatus {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
0 => PeerDeviceStatus::Online,
|
||||
_ => PeerDeviceStatus::Offline,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum ConnectStatus {
|
||||
Connecting,
|
||||
Connected,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct CurrentDeviceInfo {
|
||||
virtual_ip: Ipv4Addr,
|
||||
pub virtual_gateway: Ipv4Addr,
|
||||
pub virtual_netmask: Ipv4Addr,
|
||||
//网络地址
|
||||
pub virtual_network: Ipv4Addr,
|
||||
//直接广播地址
|
||||
pub broadcast_address: Ipv4Addr,
|
||||
//链接的服务器地址
|
||||
pub connect_server: SocketAddr,
|
||||
}
|
||||
|
||||
impl CurrentDeviceInfo {
|
||||
pub fn new(
|
||||
virtual_ip: Ipv4Addr,
|
||||
virtual_gateway: Ipv4Addr,
|
||||
virtual_netmask: Ipv4Addr,
|
||||
connect_server: SocketAddr,
|
||||
) -> Self {
|
||||
let broadcast_address = (!u32::from_be_bytes(virtual_netmask.octets()))
|
||||
| u32::from_be_bytes(virtual_gateway.octets());
|
||||
let broadcast_address = Ipv4Addr::from(broadcast_address);
|
||||
let virtual_network = u32::from_be_bytes(virtual_netmask.octets())
|
||||
& u32::from_be_bytes(virtual_gateway.octets());
|
||||
let virtual_network = Ipv4Addr::from(virtual_network);
|
||||
Self {
|
||||
virtual_ip,
|
||||
virtual_netmask,
|
||||
virtual_gateway,
|
||||
virtual_network,
|
||||
broadcast_address,
|
||||
connect_server,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn virtual_ip(&self) -> Ipv4Addr {
|
||||
self.virtual_ip
|
||||
}
|
||||
#[inline]
|
||||
pub fn virtual_gateway(&self) -> Ipv4Addr {
|
||||
self.virtual_gateway
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
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())
|
||||
}
|
||||
@@ -0,0 +1,393 @@
|
||||
use std::{io, thread};
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
use std::sync::Arc;
|
||||
|
||||
use chrono::Local;
|
||||
use crossbeam::atomic::AtomicCell;
|
||||
use crossbeam_skiplist::SkipMap;
|
||||
use parking_lot::Mutex;
|
||||
use protobuf::Message;
|
||||
|
||||
use p2p_channel::channel::{Channel, Route, RouteKey};
|
||||
use p2p_channel::punch::NatInfo;
|
||||
use packet::icmp::{icmp, Kind};
|
||||
use packet::ip::ipv4;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::handle::{check_dest, ConnectStatus, CurrentDeviceInfo, PeerDeviceInfo};
|
||||
use crate::handle::registration_handler::Register;
|
||||
use crate::nat;
|
||||
use crate::nat::NatTest;
|
||||
use crate::proto::message::{DeviceList, PunchInfo, PunchNatType, RegistrationResponse};
|
||||
use crate::protocol::{control_packet, MAX_TTL, NetPacket, Protocol, service_packet, turn_packet, Version};
|
||||
use crate::protocol::control_packet::ControlPacket;
|
||||
use crate::protocol::error_packet::InErrorPacket;
|
||||
use crate::tun_device::TunWriter;
|
||||
|
||||
pub fn start(mut handler: RecvHandler) {
|
||||
thread::Builder::new().name("udp-recv-handler".into()).spawn(move || {
|
||||
let mut buf = [0; 4096];
|
||||
loop {
|
||||
match handler.channel.recv_from(&mut buf, None) {
|
||||
Ok((len, route)) => {
|
||||
if let Err(e) = handler.handle(&mut buf[..len], &route) {
|
||||
log::warn!("数据处理失败:{:?},e:{:?}",route,e);
|
||||
if let Error::Stop(_) = e {
|
||||
let _ = handler.channel.close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("{:?}",e);
|
||||
// 检查关闭状态
|
||||
if handler.channel.is_close() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}).unwrap();
|
||||
}
|
||||
|
||||
pub struct RecvHandler {
|
||||
channel: Channel<Ipv4Addr>,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
register: Arc<Register>,
|
||||
nat_test: NatTest,
|
||||
tun_writer: TunWriter,
|
||||
connect_status: Arc<AtomicCell<ConnectStatus>>,
|
||||
peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>>,
|
||||
}
|
||||
|
||||
impl RecvHandler {
|
||||
pub fn new(channel: Channel<Ipv4Addr>,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
register: Arc<Register>,
|
||||
nat_test: NatTest,
|
||||
tun_writer: TunWriter,
|
||||
connect_status: Arc<AtomicCell<ConnectStatus>>,
|
||||
peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
channel,
|
||||
current_device,
|
||||
device_list,
|
||||
register,
|
||||
nat_test,
|
||||
tun_writer,
|
||||
connect_status,
|
||||
peer_nat_info_map,
|
||||
}
|
||||
}
|
||||
pub fn try_clone(&self) -> io::Result<Self> {
|
||||
Ok(Self {
|
||||
channel: self.channel.try_clone()?,
|
||||
current_device: self.current_device.clone(),
|
||||
device_list: self.device_list.clone(),
|
||||
register: self.register.clone(),
|
||||
nat_test: self.nat_test.clone(),
|
||||
tun_writer: self.tun_writer.clone(),
|
||||
connect_status: self.connect_status.clone(),
|
||||
peer_nat_info_map: self.peer_nat_info_map.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl RecvHandler {
|
||||
fn handle(&self, buf: &mut [u8], route_key: &RouteKey) -> crate::Result<()> {
|
||||
let mut net_packet = NetPacket::new(buf)?;
|
||||
if net_packet.ttl() == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
let source = net_packet.source();
|
||||
let current_device = self.current_device.load();
|
||||
if source == current_device.virtual_ip() {
|
||||
return Ok(());
|
||||
}
|
||||
let destination = net_packet.destination();
|
||||
if !destination.is_broadcast() && destination != current_device.broadcast_address
|
||||
&& current_device.virtual_ip() != destination && self.connect_status.load() == ConnectStatus::Connected {
|
||||
if !check_dest(source, current_device.virtual_netmask, current_device.virtual_network) {
|
||||
log::warn!("转发数据,源地址错误:{:?},当前网络:{:?},route_key:{:?}",source,current_device.virtual_network,route_key);
|
||||
return Ok(());
|
||||
}
|
||||
if !check_dest(destination, current_device.virtual_netmask, current_device.virtual_network) {
|
||||
log::warn!("转发数据,目的地址错误:{:?},当前网络:{:?},route_key:{:?}",destination,current_device.virtual_network,route_key);
|
||||
return Ok(());
|
||||
}
|
||||
let ttl = net_packet.ttl();
|
||||
if ttl > 1 {
|
||||
// 转发
|
||||
net_packet.set_ttl(ttl - 1);
|
||||
if let Some(route) = self.channel.route(&destination) {
|
||||
if route.metric <= net_packet.ttl() {
|
||||
self.channel.send_to_route(net_packet.buffer(), &route.route_key())?;
|
||||
}
|
||||
} else if (ttl > 2 || destination == current_device.virtual_gateway())
|
||||
&& source != current_device.virtual_gateway() {
|
||||
//网关默认要转发一次,生存时间不够的发到网关也会被丢弃
|
||||
self.channel.send_to_addr(net_packet.buffer(), current_device.connect_server)?;
|
||||
}
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
match net_packet.protocol() {
|
||||
Protocol::Ipv4Turn => {
|
||||
let mut ipv4 = IpV4Packet::new(net_packet.payload_mut())?;
|
||||
if ipv4.protocol() == ipv4::protocol::Protocol::Icmp {
|
||||
let mut icmp_packet = icmp::IcmpPacket::new(ipv4.payload_mut())?;
|
||||
if icmp_packet.kind() == Kind::EchoRequest {
|
||||
//开启ping
|
||||
icmp_packet.set_kind(Kind::EchoReply);
|
||||
icmp_packet.update_checksum();
|
||||
ipv4.set_source_ip(destination);
|
||||
ipv4.set_destination_ip(source);
|
||||
ipv4.update_checksum();
|
||||
net_packet.set_source(destination);
|
||||
net_packet.set_destination(source);
|
||||
self.channel.send_to_route(net_packet.buffer(), route_key)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
self.tun_writer.write(net_packet.payload())?;
|
||||
}
|
||||
Protocol::Service => {
|
||||
self.service(current_device, source, net_packet, route_key)?;
|
||||
}
|
||||
Protocol::Error => {
|
||||
self.error(current_device, source, net_packet, route_key)?;
|
||||
}
|
||||
Protocol::Control => {
|
||||
self.control(current_device, source, net_packet, route_key)?;
|
||||
}
|
||||
Protocol::OtherTurn => {
|
||||
self.other_turn(current_device, source, net_packet, route_key)?;
|
||||
}
|
||||
Protocol::UnKnow(e) => {
|
||||
log::info!("不支持的协议:{}",e);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn service(&self, current_device: CurrentDeviceInfo, source: Ipv4Addr, net_packet: NetPacket<&mut [u8]>, route_key: &RouteKey) -> crate::Result<()> {
|
||||
if route_key.addr != current_device.connect_server || source != current_device.virtual_gateway() {
|
||||
return Ok(());
|
||||
}
|
||||
match service_packet::Protocol::from(net_packet.transport_protocol()) {
|
||||
service_packet::Protocol::RegistrationRequest => {}
|
||||
service_packet::Protocol::RegistrationResponse => {
|
||||
let response = RegistrationResponse::parse_from_bytes(net_packet.payload())?;
|
||||
let local_port = self.channel.local_addr()?.port();
|
||||
let local_ip = nat::local_ip()?;
|
||||
let nat_info = self.nat_test.re_test(Ipv4Addr::from(response.public_ip), response.public_port as u16, local_ip, local_port);
|
||||
self.channel.set_nat_type(nat_info.nat_type)?;
|
||||
let new_ip = Ipv4Addr::from(response.virtual_ip);
|
||||
let current_ip = current_device.virtual_ip();
|
||||
if current_ip != new_ip {
|
||||
// ip发生变化
|
||||
log::info!("ip发生变化,old_ip:{:?},new_ip:{:?}",current_ip,new_ip);
|
||||
let old_netmask = current_device.virtual_netmask;
|
||||
let old_gateway = current_device.virtual_gateway();
|
||||
let virtual_ip = Ipv4Addr::from(response.virtual_ip);
|
||||
let virtual_gateway = Ipv4Addr::from(response.virtual_gateway);
|
||||
let virtual_netmask = Ipv4Addr::from(response.virtual_netmask);
|
||||
self.tun_writer.change_ip(virtual_ip, virtual_netmask, virtual_gateway, old_netmask, old_gateway)?;
|
||||
let new_current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway,
|
||||
virtual_netmask, current_device.connect_server);
|
||||
if let Err(e) = self.current_device.compare_exchange(current_device, new_current_device) {
|
||||
log::warn!("替换失败:{:?}",e);
|
||||
}
|
||||
}
|
||||
self.connect_status.store(ConnectStatus::Connected);
|
||||
}
|
||||
service_packet::Protocol::PollDeviceList => {}
|
||||
service_packet::Protocol::PushDeviceList => {
|
||||
let device_list_t = DeviceList::parse_from_bytes(net_packet.payload())?;
|
||||
let ip_list = device_list_t
|
||||
.device_info_list
|
||||
.into_iter()
|
||||
.map(|info| {
|
||||
PeerDeviceInfo::new(
|
||||
Ipv4Addr::from(info.virtual_ip),
|
||||
info.name,
|
||||
info.device_status as u8,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let mut dev = self.device_list.lock();
|
||||
if dev.0 != device_list_t.epoch as u16 {
|
||||
dev.0 = device_list_t.epoch as u16;
|
||||
dev.1 = ip_list;
|
||||
}
|
||||
}
|
||||
service_packet::Protocol::UnKnow(u) => {
|
||||
log::warn!("未知服务协议:{}",u);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn error(&self, current_device: CurrentDeviceInfo, source: Ipv4Addr, net_packet: NetPacket<&mut [u8]>, route_key: &RouteKey) -> crate::Result<()> {
|
||||
if route_key.addr != current_device.connect_server || source != current_device.virtual_gateway() {
|
||||
return Ok(());
|
||||
}
|
||||
match InErrorPacket::new(net_packet.transport_protocol(), net_packet.payload())? {
|
||||
InErrorPacket::TokenError => {
|
||||
return Err(Error::Stop("Token error".to_string()));
|
||||
}
|
||||
InErrorPacket::Disconnect => {
|
||||
self.connect_status.store(ConnectStatus::Connecting);
|
||||
self.register.fast_register()?;
|
||||
}
|
||||
InErrorPacket::AddressExhausted => {
|
||||
//地址用尽
|
||||
return Err(Error::Stop("IP address has been exhausted".to_string()));
|
||||
}
|
||||
InErrorPacket::OtherError(e) => {
|
||||
log::error!("OtherError {:?}", e.message());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn control(&self, current_device: CurrentDeviceInfo, source: Ipv4Addr, mut net_packet: NetPacket<&mut [u8]>, route_key: &RouteKey) -> crate::Result<()> {
|
||||
match ControlPacket::new(net_packet.transport_protocol(), net_packet.payload())? {
|
||||
ControlPacket::PingPacket(_) => {
|
||||
let metric = net_packet.source_ttl() - net_packet.ttl() + 1;
|
||||
net_packet.set_transport_protocol(control_packet::Protocol::Pong.into());
|
||||
net_packet.set_source(current_device.virtual_ip());
|
||||
net_packet.set_destination(source);
|
||||
net_packet.first_set_ttl(MAX_TTL);
|
||||
self.channel.send_to_route(net_packet.buffer(), route_key)?;
|
||||
if metric == 1 {
|
||||
if let Some(current_route) = self.channel.route(&source) {
|
||||
if current_route.metric > 1 {
|
||||
let route = Route::from(*route_key, 1, -1);
|
||||
self.channel.add_route(source, route);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ControlPacket::PongPacket(pong_packet) => {
|
||||
let current_time = Local::now().timestamp_millis() as u16;
|
||||
if current_time < pong_packet.time() {
|
||||
return Ok(());
|
||||
}
|
||||
let rt = (current_time - pong_packet.time()) as i64;
|
||||
let metric = net_packet.source_ttl() - net_packet.ttl() + 1;
|
||||
if let Some(current_route) = self.channel.route(&source) {
|
||||
if ¤t_route.route_key() == route_key {
|
||||
self.channel.update_route(&source, metric, rt);
|
||||
} else if current_route.metric >= metric && current_route.rt > rt {
|
||||
let route = Route::from(*route_key, metric, rt);
|
||||
self.channel.add_route(source, route);
|
||||
}
|
||||
} else {
|
||||
let route = Route::from(*route_key, metric, rt);
|
||||
self.channel.add_route(source, route);
|
||||
}
|
||||
if route_key.addr == current_device.connect_server && source == current_device.virtual_gateway() {
|
||||
let epoch = self.device_list.lock().0;
|
||||
if pong_packet.epoch() != epoch {
|
||||
let mut poll_device = NetPacket::new([0; 12])?;
|
||||
poll_device.set_source(current_device.virtual_ip());
|
||||
poll_device.set_destination(source);
|
||||
poll_device.set_version(Version::V1);
|
||||
poll_device.first_set_ttl(MAX_TTL);
|
||||
poll_device.set_protocol(Protocol::Service);
|
||||
poll_device.set_transport_protocol(service_packet::Protocol::PollDeviceList.into());
|
||||
self.channel.send_to_route(poll_device.buffer(), route_key)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
ControlPacket::PunchRequest => {
|
||||
// log::info!("PunchRequest route_key:{:?}",route_key);
|
||||
//回应
|
||||
net_packet.set_transport_protocol(control_packet::Protocol::PunchResponse.into());
|
||||
net_packet.set_source(current_device.virtual_ip());
|
||||
net_packet.set_destination(source);
|
||||
net_packet.first_set_ttl(1);
|
||||
self.channel.send_to_route(net_packet.buffer(), route_key)?;
|
||||
let route = Route::from(*route_key, 1, -1);
|
||||
self.channel.add_route(source, route);
|
||||
}
|
||||
ControlPacket::PunchResponse => {
|
||||
// log::info!("PunchResponse route_key:{:?}",route_key);
|
||||
let route = Route::from(*route_key, 1, -1);
|
||||
self.channel.add_route(net_packet.source(), route);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn other_turn(&self, current_device: CurrentDeviceInfo, source: Ipv4Addr, net_packet: NetPacket<&mut [u8]>, route_key: &RouteKey) -> crate::Result<()> {
|
||||
match turn_packet::Protocol::from(net_packet.transport_protocol()) {
|
||||
turn_packet::Protocol::Punch => {
|
||||
let punch_info = PunchInfo::parse_from_bytes(net_packet.payload())?;
|
||||
let public_ips = punch_info.public_ip_list.
|
||||
iter().map(|v| { IpAddr::from(v.to_be_bytes()) }).collect();
|
||||
let peer_nat_info = NatInfo::new(public_ips,
|
||||
punch_info.public_port as u16,
|
||||
punch_info.public_port_range as u16,
|
||||
IpAddr::from(punch_info.local_ip.to_be_bytes()),
|
||||
punch_info.local_port as u16,
|
||||
punch_info.nat_type.enum_value_or_default().into());
|
||||
self.peer_nat_info_map.insert(source, peer_nat_info.clone());
|
||||
if !punch_info.reply {
|
||||
let mut punch_reply = PunchInfo::new();
|
||||
punch_reply.reply = true;
|
||||
let nat_info = self.nat_test.nat_info();
|
||||
punch_reply.public_ip_list = nat_info.public_ips.iter().map(|i| {
|
||||
match i {
|
||||
IpAddr::V4(ip) => u32::from_be_bytes(ip.octets()),
|
||||
IpAddr::V6(_) => 0
|
||||
}
|
||||
}).collect();
|
||||
punch_reply.public_port = nat_info.public_port as u32;
|
||||
punch_reply.public_port_range = nat_info.public_port_range as u32;
|
||||
punch_reply.nat_type =
|
||||
protobuf::EnumOrUnknown::new(PunchNatType::from(nat_info.nat_type));
|
||||
punch_reply.local_ip = match nat_info.local_ip {
|
||||
IpAddr::V4(ip) => u32::from_be_bytes(ip.octets()),
|
||||
IpAddr::V6(_) => 0
|
||||
};
|
||||
punch_reply.local_port = nat_info.local_port as u32;
|
||||
let bytes = punch_reply.write_to_bytes()?;
|
||||
let mut net_packet =
|
||||
NetPacket::new(vec![0u8; 12 + bytes.len()])?;
|
||||
net_packet.set_version(Version::V1);
|
||||
net_packet.set_protocol(Protocol::OtherTurn);
|
||||
net_packet.set_transport_protocol(
|
||||
turn_packet::Protocol::Punch.into(),
|
||||
);
|
||||
net_packet.first_set_ttl(MAX_TTL);
|
||||
net_packet.set_source(current_device.virtual_ip());
|
||||
net_packet.set_destination(source);
|
||||
net_packet.set_payload(&bytes);
|
||||
if !peer_nat_info.local_ip.is_unspecified() && peer_nat_info.local_port != 0 {
|
||||
let mut packet = NetPacket::new([0u8; 12])?;
|
||||
packet.set_version(Version::V1);
|
||||
packet.first_set_ttl(1);
|
||||
packet.set_protocol(Protocol::Control);
|
||||
packet.set_transport_protocol(control_packet::Protocol::PunchRequest.into());
|
||||
packet.set_source(current_device.virtual_ip());
|
||||
packet.set_destination(source);
|
||||
let _ = self.channel.send_to_addr(packet.buffer(), SocketAddr::new(peer_nat_info.local_ip, peer_nat_info.local_port));
|
||||
}
|
||||
if let Err(e) = self.channel.punch(source, peer_nat_info) {
|
||||
log::warn!("发送到打洞通道失败 {:?}",e);
|
||||
return Ok(());
|
||||
}
|
||||
self.channel.send_to_route(net_packet.buffer(), route_key)?;
|
||||
} else {
|
||||
let _ = self.channel.punch(source, peer_nat_info);
|
||||
}
|
||||
}
|
||||
turn_packet::Protocol::UnKnow(e) => {
|
||||
log::warn!("不支持的转发协议 {:?},source:{:?}",e,source);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
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(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
use crate::error::Error;
|
||||
|
||||
|
||||
pub use p2p_channel::channel::{Route, RouteKey};
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
pub mod error;
|
||||
pub mod handle;
|
||||
pub mod nat;
|
||||
pub mod proto;
|
||||
pub mod protocol;
|
||||
pub mod tun_device;
|
||||
pub mod core;
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::{io, thread};
|
||||
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::proto::message::NatType;
|
||||
|
||||
// #[derive(Debug, Copy, Clone, PartialEq)]
|
||||
// pub enum NatType {
|
||||
@@ -21,7 +21,7 @@ use crate::proto::message::NatType;
|
||||
// }
|
||||
|
||||
/// 返回所有公网ip和端口变化范围
|
||||
pub fn public_ip_list() -> io::Result<(NatType, Vec<Ipv4Addr>, u16)> {
|
||||
pub fn public_ip_list(addrs: &Vec<SocketAddr>) -> io::Result<(NatType, Vec<Ipv4Addr>, u16)> {
|
||||
let mut hash_set = HashSet::new();
|
||||
let mut max_port_range = 0;
|
||||
let mut nat_type = NatType::Cone;
|
||||
@@ -41,7 +41,7 @@ pub fn public_ip_list() -> io::Result<(NatType, Vec<Ipv4Addr>, u16)> {
|
||||
}
|
||||
}
|
||||
};
|
||||
let (set, min_port, max_port) = public_ip_list_(&udp)?;
|
||||
let (set, min_port, max_port) = public_ip_list_(&udp, addrs)?;
|
||||
drop(udp);
|
||||
let port_range = max_port - min_port;
|
||||
//有多个ip或者端口有变化,说明是对称nat
|
||||
@@ -69,19 +69,21 @@ pub fn public_ip_list() -> io::Result<(NatType, Vec<Ipv4Addr>, u16)> {
|
||||
/// - 电信4g:对称网络只有一个ip 公网端口比较连续
|
||||
/// - 综上:客户端使用小端口,针对对称网络 尝试所有ip 公网端口+-变化量的范围
|
||||
/// - 打通概率 移动宽带=电信宽带>联调宽带>电信4g>移动4g>>联调4g
|
||||
pub fn public_ip_list_(udp: &UdpSocket) -> io::Result<(HashSet<Ipv4Addr>, u16, u16)> {
|
||||
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];
|
||||
let _ = udp.send_to(b"NatTest", "nat1.wherewego.top:35061")?;
|
||||
let _ = udp.send_to(b"NatTest", "nat1.wherewego.top:35062")?;
|
||||
let _ = udp.send_to(b"NatTest", "nat2.wherewego.top:35061")?;
|
||||
let _ = udp.send_to(b"NatTest", "nat2.wherewego.top:35062")?;
|
||||
for addr in addrs {
|
||||
let _ = udp.send_to(b"NatTest", addr)?;
|
||||
}
|
||||
let mut hash_set = HashSet::new();
|
||||
let mut count = 0;
|
||||
let mut min_port = 65535;
|
||||
let mut max_port = 0;
|
||||
for _ in 0..4 {
|
||||
for _ in 0..addrs.len() {
|
||||
if let Ok(len) = udp.recv(&mut buf) {
|
||||
if len != 16 || &buf[..10] != &b"NatType213"[..] {
|
||||
continue;
|
||||
@@ -94,7 +96,6 @@ pub fn public_ip_list_(udp: &UdpSocket) -> io::Result<(HashSet<Ipv4Addr>, u16, u
|
||||
max_port = port;
|
||||
}
|
||||
let ip = Ipv4Addr::new(buf[10], buf[11], buf[12], buf[13]);
|
||||
// println!("pub {:?}:{}", ip, port);
|
||||
hash_set.insert(ip);
|
||||
count += 1;
|
||||
}
|
||||
@@ -148,9 +149,23 @@ pub fn nat_test_() -> io::Result<NatType> {
|
||||
}
|
||||
Ok(NatType::Cone)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nat_test_run(){
|
||||
fn nat_test_run() {
|
||||
let udp = UdpSocket::bind("0.0.0.0:101").unwrap();
|
||||
let print = public_ip_list_(&udp).unwrap();
|
||||
println!("{:?}",print);
|
||||
}
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs, UdpSocket};
|
||||
let addrs = vec![
|
||||
"nat1.wherewego.top:35062"
|
||||
.to_socket_addrs()
|
||||
.unwrap()
|
||||
.next()
|
||||
.unwrap(),
|
||||
"nat2.wherewego.top:35062"
|
||||
.to_socket_addrs()
|
||||
.unwrap()
|
||||
.next()
|
||||
.unwrap(),
|
||||
];
|
||||
let print = public_ip_list_(&udp, &addrs).unwrap();
|
||||
println!("{:?}", print);
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
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,
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -31,8 +31,12 @@ pub struct RegistrationRequest {
|
||||
// message fields
|
||||
// @@protoc_insertion_point(field:RegistrationRequest.token)
|
||||
pub token: ::std::string::String,
|
||||
// @@protoc_insertion_point(field:RegistrationRequest.mac_address)
|
||||
pub mac_address: ::std::string::String,
|
||||
// @@protoc_insertion_point(field:RegistrationRequest.device_id)
|
||||
pub device_id: ::std::string::String,
|
||||
// @@protoc_insertion_point(field:RegistrationRequest.name)
|
||||
pub name: ::std::string::String,
|
||||
// @@protoc_insertion_point(field:RegistrationRequest.is_fast)
|
||||
pub is_fast: bool,
|
||||
// special fields
|
||||
// @@protoc_insertion_point(special_field:RegistrationRequest.special_fields)
|
||||
pub special_fields: ::protobuf::SpecialFields,
|
||||
@@ -50,7 +54,7 @@ impl RegistrationRequest {
|
||||
}
|
||||
|
||||
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
|
||||
let mut fields = ::std::vec::Vec::with_capacity(2);
|
||||
let mut fields = ::std::vec::Vec::with_capacity(4);
|
||||
let mut oneofs = ::std::vec::Vec::with_capacity(0);
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"token",
|
||||
@@ -58,9 +62,19 @@ impl RegistrationRequest {
|
||||
|m: &mut RegistrationRequest| { &mut m.token },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"mac_address",
|
||||
|m: &RegistrationRequest| { &m.mac_address },
|
||||
|m: &mut RegistrationRequest| { &mut m.mac_address },
|
||||
"device_id",
|
||||
|m: &RegistrationRequest| { &m.device_id },
|
||||
|m: &mut RegistrationRequest| { &mut m.device_id },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"name",
|
||||
|m: &RegistrationRequest| { &m.name },
|
||||
|m: &mut RegistrationRequest| { &mut m.name },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"is_fast",
|
||||
|m: &RegistrationRequest| { &m.is_fast },
|
||||
|m: &mut RegistrationRequest| { &mut m.is_fast },
|
||||
));
|
||||
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<RegistrationRequest>(
|
||||
"RegistrationRequest",
|
||||
@@ -84,7 +98,13 @@ impl ::protobuf::Message for RegistrationRequest {
|
||||
self.token = is.read_string()?;
|
||||
},
|
||||
18 => {
|
||||
self.mac_address = is.read_string()?;
|
||||
self.device_id = is.read_string()?;
|
||||
},
|
||||
26 => {
|
||||
self.name = is.read_string()?;
|
||||
},
|
||||
32 => {
|
||||
self.is_fast = is.read_bool()?;
|
||||
},
|
||||
tag => {
|
||||
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
|
||||
@@ -101,8 +121,14 @@ impl ::protobuf::Message for RegistrationRequest {
|
||||
if !self.token.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(1, &self.token);
|
||||
}
|
||||
if !self.mac_address.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(2, &self.mac_address);
|
||||
if !self.device_id.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(2, &self.device_id);
|
||||
}
|
||||
if !self.name.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(3, &self.name);
|
||||
}
|
||||
if self.is_fast != 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);
|
||||
@@ -113,8 +139,14 @@ impl ::protobuf::Message for RegistrationRequest {
|
||||
if !self.token.is_empty() {
|
||||
os.write_string(1, &self.token)?;
|
||||
}
|
||||
if !self.mac_address.is_empty() {
|
||||
os.write_string(2, &self.mac_address)?;
|
||||
if !self.device_id.is_empty() {
|
||||
os.write_string(2, &self.device_id)?;
|
||||
}
|
||||
if !self.name.is_empty() {
|
||||
os.write_string(3, &self.name)?;
|
||||
}
|
||||
if self.is_fast != false {
|
||||
os.write_bool(4, self.is_fast)?;
|
||||
}
|
||||
os.write_unknown_fields(self.special_fields.unknown_fields())?;
|
||||
::std::result::Result::Ok(())
|
||||
@@ -134,14 +166,18 @@ impl ::protobuf::Message for RegistrationRequest {
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.token.clear();
|
||||
self.mac_address.clear();
|
||||
self.device_id.clear();
|
||||
self.name.clear();
|
||||
self.is_fast = false;
|
||||
self.special_fields.clear();
|
||||
}
|
||||
|
||||
fn default_instance() -> &'static RegistrationRequest {
|
||||
static instance: RegistrationRequest = RegistrationRequest {
|
||||
token: ::std::string::String::new(),
|
||||
mac_address: ::std::string::String::new(),
|
||||
device_id: ::std::string::String::new(),
|
||||
name: ::std::string::String::new(),
|
||||
is_fast: false,
|
||||
special_fields: ::protobuf::SpecialFields::new(),
|
||||
};
|
||||
&instance
|
||||
@@ -177,8 +213,8 @@ pub struct RegistrationResponse {
|
||||
pub virtual_netmask: u32,
|
||||
// @@protoc_insertion_point(field:RegistrationResponse.epoch)
|
||||
pub epoch: u32,
|
||||
// @@protoc_insertion_point(field:RegistrationResponse.virtual_ip_list)
|
||||
pub virtual_ip_list: ::std::vec::Vec<u32>,
|
||||
// @@protoc_insertion_point(field:RegistrationResponse.device_info_list)
|
||||
pub device_info_list: ::std::vec::Vec<DeviceInfo>,
|
||||
// @@protoc_insertion_point(field:RegistrationResponse.public_ip)
|
||||
pub public_ip: u32,
|
||||
// @@protoc_insertion_point(field:RegistrationResponse.public_port)
|
||||
@@ -223,9 +259,9 @@ impl RegistrationResponse {
|
||||
|m: &mut RegistrationResponse| { &mut m.epoch },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>(
|
||||
"virtual_ip_list",
|
||||
|m: &RegistrationResponse| { &m.virtual_ip_list },
|
||||
|m: &mut RegistrationResponse| { &mut m.virtual_ip_list },
|
||||
"device_info_list",
|
||||
|m: &RegistrationResponse| { &m.device_info_list },
|
||||
|m: &mut RegistrationResponse| { &mut m.device_info_list },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"public_ip",
|
||||
@@ -268,10 +304,7 @@ impl ::protobuf::Message for RegistrationResponse {
|
||||
self.epoch = is.read_uint32()?;
|
||||
},
|
||||
42 => {
|
||||
is.read_repeated_packed_fixed32_into(&mut self.virtual_ip_list)?;
|
||||
},
|
||||
45 => {
|
||||
self.virtual_ip_list.push(is.read_fixed32()?);
|
||||
self.device_info_list.push(is.read_message()?);
|
||||
},
|
||||
53 => {
|
||||
self.public_ip = is.read_fixed32()?;
|
||||
@@ -303,7 +336,10 @@ impl ::protobuf::Message for RegistrationResponse {
|
||||
if self.epoch != 0 {
|
||||
my_size += ::protobuf::rt::uint32_size(4, self.epoch);
|
||||
}
|
||||
my_size += 5 * self.virtual_ip_list.len() as u64;
|
||||
for value in &self.device_info_list {
|
||||
let len = value.compute_size();
|
||||
my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len;
|
||||
};
|
||||
if self.public_ip != 0 {
|
||||
my_size += 1 + 4;
|
||||
}
|
||||
@@ -328,8 +364,8 @@ impl ::protobuf::Message for RegistrationResponse {
|
||||
if self.epoch != 0 {
|
||||
os.write_uint32(4, self.epoch)?;
|
||||
}
|
||||
for v in &self.virtual_ip_list {
|
||||
os.write_fixed32(5, *v)?;
|
||||
for v in &self.device_info_list {
|
||||
::protobuf::rt::write_message_field_with_cached_size(5, v, os)?;
|
||||
};
|
||||
if self.public_ip != 0 {
|
||||
os.write_fixed32(6, self.public_ip)?;
|
||||
@@ -358,7 +394,7 @@ impl ::protobuf::Message for RegistrationResponse {
|
||||
self.virtual_gateway = 0;
|
||||
self.virtual_netmask = 0;
|
||||
self.epoch = 0;
|
||||
self.virtual_ip_list.clear();
|
||||
self.device_info_list.clear();
|
||||
self.public_ip = 0;
|
||||
self.public_port = 0;
|
||||
self.special_fields.clear();
|
||||
@@ -370,7 +406,7 @@ impl ::protobuf::Message for RegistrationResponse {
|
||||
virtual_gateway: 0,
|
||||
virtual_netmask: 0,
|
||||
epoch: 0,
|
||||
virtual_ip_list: ::std::vec::Vec::new(),
|
||||
device_info_list: ::std::vec::Vec::new(),
|
||||
public_ip: 0,
|
||||
public_port: 0,
|
||||
special_fields: ::protobuf::SpecialFields::new(),
|
||||
@@ -396,14 +432,172 @@ impl ::protobuf::reflect::ProtobufValue for RegistrationResponse {
|
||||
type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
|
||||
}
|
||||
|
||||
#[derive(PartialEq,Clone,Default,Debug)]
|
||||
// @@protoc_insertion_point(message:DeviceInfo)
|
||||
pub struct DeviceInfo {
|
||||
// message fields
|
||||
// @@protoc_insertion_point(field:DeviceInfo.name)
|
||||
pub name: ::std::string::String,
|
||||
// @@protoc_insertion_point(field:DeviceInfo.virtual_ip)
|
||||
pub virtual_ip: u32,
|
||||
// @@protoc_insertion_point(field:DeviceInfo.device_status)
|
||||
pub device_status: u32,
|
||||
// special fields
|
||||
// @@protoc_insertion_point(special_field:DeviceInfo.special_fields)
|
||||
pub special_fields: ::protobuf::SpecialFields,
|
||||
}
|
||||
|
||||
impl<'a> ::std::default::Default for &'a DeviceInfo {
|
||||
fn default() -> &'a DeviceInfo {
|
||||
<DeviceInfo as ::protobuf::Message>::default_instance()
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceInfo {
|
||||
pub fn new() -> DeviceInfo {
|
||||
::std::default::Default::default()
|
||||
}
|
||||
|
||||
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
|
||||
let mut fields = ::std::vec::Vec::with_capacity(3);
|
||||
let mut oneofs = ::std::vec::Vec::with_capacity(0);
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"name",
|
||||
|m: &DeviceInfo| { &m.name },
|
||||
|m: &mut DeviceInfo| { &mut m.name },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"virtual_ip",
|
||||
|m: &DeviceInfo| { &m.virtual_ip },
|
||||
|m: &mut DeviceInfo| { &mut m.virtual_ip },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"device_status",
|
||||
|m: &DeviceInfo| { &m.device_status },
|
||||
|m: &mut DeviceInfo| { &mut m.device_status },
|
||||
));
|
||||
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<DeviceInfo>(
|
||||
"DeviceInfo",
|
||||
fields,
|
||||
oneofs,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::Message for DeviceInfo {
|
||||
const NAME: &'static str = "DeviceInfo";
|
||||
|
||||
fn is_initialized(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
|
||||
while let Some(tag) = is.read_raw_tag_or_eof()? {
|
||||
match tag {
|
||||
10 => {
|
||||
self.name = is.read_string()?;
|
||||
},
|
||||
21 => {
|
||||
self.virtual_ip = is.read_fixed32()?;
|
||||
},
|
||||
24 => {
|
||||
self.device_status = is.read_uint32()?;
|
||||
},
|
||||
tag => {
|
||||
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
|
||||
},
|
||||
};
|
||||
}
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
|
||||
// Compute sizes of nested messages
|
||||
#[allow(unused_variables)]
|
||||
fn compute_size(&self) -> u64 {
|
||||
let mut my_size = 0;
|
||||
if !self.name.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(1, &self.name);
|
||||
}
|
||||
if self.virtual_ip != 0 {
|
||||
my_size += 1 + 4;
|
||||
}
|
||||
if self.device_status != 0 {
|
||||
my_size += ::protobuf::rt::uint32_size(3, self.device_status);
|
||||
}
|
||||
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
|
||||
self.special_fields.cached_size().set(my_size as u32);
|
||||
my_size
|
||||
}
|
||||
|
||||
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
|
||||
if !self.name.is_empty() {
|
||||
os.write_string(1, &self.name)?;
|
||||
}
|
||||
if self.virtual_ip != 0 {
|
||||
os.write_fixed32(2, self.virtual_ip)?;
|
||||
}
|
||||
if self.device_status != 0 {
|
||||
os.write_uint32(3, self.device_status)?;
|
||||
}
|
||||
os.write_unknown_fields(self.special_fields.unknown_fields())?;
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
|
||||
fn special_fields(&self) -> &::protobuf::SpecialFields {
|
||||
&self.special_fields
|
||||
}
|
||||
|
||||
fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
|
||||
&mut self.special_fields
|
||||
}
|
||||
|
||||
fn new() -> DeviceInfo {
|
||||
DeviceInfo::new()
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.name.clear();
|
||||
self.virtual_ip = 0;
|
||||
self.device_status = 0;
|
||||
self.special_fields.clear();
|
||||
}
|
||||
|
||||
fn default_instance() -> &'static DeviceInfo {
|
||||
static instance: DeviceInfo = DeviceInfo {
|
||||
name: ::std::string::String::new(),
|
||||
virtual_ip: 0,
|
||||
device_status: 0,
|
||||
special_fields: ::protobuf::SpecialFields::new(),
|
||||
};
|
||||
&instance
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::MessageFull for DeviceInfo {
|
||||
fn descriptor() -> ::protobuf::reflect::MessageDescriptor {
|
||||
static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new();
|
||||
descriptor.get(|| file_descriptor().message_by_package_relative_name("DeviceInfo").unwrap()).clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for DeviceInfo {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
::protobuf::text_format::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::reflect::ProtobufValue for DeviceInfo {
|
||||
type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
|
||||
}
|
||||
|
||||
#[derive(PartialEq,Clone,Default,Debug)]
|
||||
// @@protoc_insertion_point(message:DeviceList)
|
||||
pub struct DeviceList {
|
||||
// message fields
|
||||
// @@protoc_insertion_point(field:DeviceList.epoch)
|
||||
pub epoch: u32,
|
||||
// @@protoc_insertion_point(field:DeviceList.virtual_ip_list)
|
||||
pub virtual_ip_list: ::std::vec::Vec<u32>,
|
||||
// @@protoc_insertion_point(field:DeviceList.device_info_list)
|
||||
pub device_info_list: ::std::vec::Vec<DeviceInfo>,
|
||||
// special fields
|
||||
// @@protoc_insertion_point(special_field:DeviceList.special_fields)
|
||||
pub special_fields: ::protobuf::SpecialFields,
|
||||
@@ -429,9 +623,9 @@ impl DeviceList {
|
||||
|m: &mut DeviceList| { &mut m.epoch },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>(
|
||||
"virtual_ip_list",
|
||||
|m: &DeviceList| { &m.virtual_ip_list },
|
||||
|m: &mut DeviceList| { &mut m.virtual_ip_list },
|
||||
"device_info_list",
|
||||
|m: &DeviceList| { &m.device_info_list },
|
||||
|m: &mut DeviceList| { &mut m.device_info_list },
|
||||
));
|
||||
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<DeviceList>(
|
||||
"DeviceList",
|
||||
@@ -455,10 +649,7 @@ impl ::protobuf::Message for DeviceList {
|
||||
self.epoch = is.read_uint32()?;
|
||||
},
|
||||
18 => {
|
||||
is.read_repeated_packed_fixed32_into(&mut self.virtual_ip_list)?;
|
||||
},
|
||||
21 => {
|
||||
self.virtual_ip_list.push(is.read_fixed32()?);
|
||||
self.device_info_list.push(is.read_message()?);
|
||||
},
|
||||
tag => {
|
||||
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
|
||||
@@ -475,7 +666,10 @@ impl ::protobuf::Message for DeviceList {
|
||||
if self.epoch != 0 {
|
||||
my_size += ::protobuf::rt::uint32_size(1, self.epoch);
|
||||
}
|
||||
my_size += 5 * self.virtual_ip_list.len() as u64;
|
||||
for value in &self.device_info_list {
|
||||
let len = value.compute_size();
|
||||
my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len;
|
||||
};
|
||||
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
|
||||
self.special_fields.cached_size().set(my_size as u32);
|
||||
my_size
|
||||
@@ -485,8 +679,8 @@ impl ::protobuf::Message for DeviceList {
|
||||
if self.epoch != 0 {
|
||||
os.write_uint32(1, self.epoch)?;
|
||||
}
|
||||
for v in &self.virtual_ip_list {
|
||||
os.write_fixed32(2, *v)?;
|
||||
for v in &self.device_info_list {
|
||||
::protobuf::rt::write_message_field_with_cached_size(2, v, os)?;
|
||||
};
|
||||
os.write_unknown_fields(self.special_fields.unknown_fields())?;
|
||||
::std::result::Result::Ok(())
|
||||
@@ -506,14 +700,14 @@ impl ::protobuf::Message for DeviceList {
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.epoch = 0;
|
||||
self.virtual_ip_list.clear();
|
||||
self.device_info_list.clear();
|
||||
self.special_fields.clear();
|
||||
}
|
||||
|
||||
fn default_instance() -> &'static DeviceList {
|
||||
static instance: DeviceList = DeviceList {
|
||||
epoch: 0,
|
||||
virtual_ip_list: ::std::vec::Vec::new(),
|
||||
device_info_list: ::std::vec::Vec::new(),
|
||||
special_fields: ::protobuf::SpecialFields::new(),
|
||||
};
|
||||
&instance
|
||||
@@ -538,87 +732,87 @@ impl ::protobuf::reflect::ProtobufValue for DeviceList {
|
||||
}
|
||||
|
||||
#[derive(PartialEq,Clone,Default,Debug)]
|
||||
// @@protoc_insertion_point(message:Punch)
|
||||
pub struct Punch {
|
||||
// @@protoc_insertion_point(message:PunchInfo)
|
||||
pub struct PunchInfo {
|
||||
// message fields
|
||||
// @@protoc_insertion_point(field:Punch.virtual_ip)
|
||||
pub virtual_ip: u32,
|
||||
// @@protoc_insertion_point(field:Punch.public_ip_list)
|
||||
// @@protoc_insertion_point(field:PunchInfo.public_ip_list)
|
||||
pub public_ip_list: ::std::vec::Vec<u32>,
|
||||
// @@protoc_insertion_point(field:Punch.public_port)
|
||||
// @@protoc_insertion_point(field:PunchInfo.public_port)
|
||||
pub public_port: u32,
|
||||
// @@protoc_insertion_point(field:Punch.public_port_range)
|
||||
// @@protoc_insertion_point(field:PunchInfo.public_port_range)
|
||||
pub public_port_range: u32,
|
||||
// @@protoc_insertion_point(field:Punch.nat_type)
|
||||
pub nat_type: ::protobuf::EnumOrUnknown<NatType>,
|
||||
// @@protoc_insertion_point(field:Punch.reply)
|
||||
// @@protoc_insertion_point(field:PunchInfo.nat_type)
|
||||
pub nat_type: ::protobuf::EnumOrUnknown<PunchNatType>,
|
||||
// @@protoc_insertion_point(field:PunchInfo.reply)
|
||||
pub reply: bool,
|
||||
// @@protoc_insertion_point(field:Punch.step)
|
||||
pub step: ::protobuf::EnumOrUnknown<Step>,
|
||||
// @@protoc_insertion_point(field:PunchInfo.local_ip)
|
||||
pub local_ip: u32,
|
||||
// @@protoc_insertion_point(field:PunchInfo.local_port)
|
||||
pub local_port: u32,
|
||||
// special fields
|
||||
// @@protoc_insertion_point(special_field:Punch.special_fields)
|
||||
// @@protoc_insertion_point(special_field:PunchInfo.special_fields)
|
||||
pub special_fields: ::protobuf::SpecialFields,
|
||||
}
|
||||
|
||||
impl<'a> ::std::default::Default for &'a Punch {
|
||||
fn default() -> &'a Punch {
|
||||
<Punch as ::protobuf::Message>::default_instance()
|
||||
impl<'a> ::std::default::Default for &'a PunchInfo {
|
||||
fn default() -> &'a PunchInfo {
|
||||
<PunchInfo as ::protobuf::Message>::default_instance()
|
||||
}
|
||||
}
|
||||
|
||||
impl Punch {
|
||||
pub fn new() -> Punch {
|
||||
impl PunchInfo {
|
||||
pub fn new() -> PunchInfo {
|
||||
::std::default::Default::default()
|
||||
}
|
||||
|
||||
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
|
||||
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::<_, _>(
|
||||
"virtual_ip",
|
||||
|m: &Punch| { &m.virtual_ip },
|
||||
|m: &mut Punch| { &mut m.virtual_ip },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>(
|
||||
"public_ip_list",
|
||||
|m: &Punch| { &m.public_ip_list },
|
||||
|m: &mut Punch| { &mut m.public_ip_list },
|
||||
|m: &PunchInfo| { &m.public_ip_list },
|
||||
|m: &mut PunchInfo| { &mut m.public_ip_list },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"public_port",
|
||||
|m: &Punch| { &m.public_port },
|
||||
|m: &mut Punch| { &mut m.public_port },
|
||||
|m: &PunchInfo| { &m.public_port },
|
||||
|m: &mut PunchInfo| { &mut m.public_port },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"public_port_range",
|
||||
|m: &Punch| { &m.public_port_range },
|
||||
|m: &mut Punch| { &mut m.public_port_range },
|
||||
|m: &PunchInfo| { &m.public_port_range },
|
||||
|m: &mut PunchInfo| { &mut m.public_port_range },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"nat_type",
|
||||
|m: &Punch| { &m.nat_type },
|
||||
|m: &mut Punch| { &mut m.nat_type },
|
||||
|m: &PunchInfo| { &m.nat_type },
|
||||
|m: &mut PunchInfo| { &mut m.nat_type },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"reply",
|
||||
|m: &Punch| { &m.reply },
|
||||
|m: &mut Punch| { &mut m.reply },
|
||||
|m: &PunchInfo| { &m.reply },
|
||||
|m: &mut PunchInfo| { &mut m.reply },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"step",
|
||||
|m: &Punch| { &m.step },
|
||||
|m: &mut Punch| { &mut m.step },
|
||||
"local_ip",
|
||||
|m: &PunchInfo| { &m.local_ip },
|
||||
|m: &mut PunchInfo| { &mut m.local_ip },
|
||||
));
|
||||
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<Punch>(
|
||||
"Punch",
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"local_port",
|
||||
|m: &PunchInfo| { &m.local_port },
|
||||
|m: &mut PunchInfo| { &mut m.local_port },
|
||||
));
|
||||
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<PunchInfo>(
|
||||
"PunchInfo",
|
||||
fields,
|
||||
oneofs,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::Message for Punch {
|
||||
const NAME: &'static str = "Punch";
|
||||
impl ::protobuf::Message for PunchInfo {
|
||||
const NAME: &'static str = "PunchInfo";
|
||||
|
||||
fn is_initialized(&self) -> bool {
|
||||
true
|
||||
@@ -627,9 +821,6 @@ impl ::protobuf::Message for Punch {
|
||||
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
|
||||
while let Some(tag) = is.read_raw_tag_or_eof()? {
|
||||
match tag {
|
||||
13 => {
|
||||
self.virtual_ip = is.read_fixed32()?;
|
||||
},
|
||||
18 => {
|
||||
is.read_repeated_packed_fixed32_into(&mut self.public_ip_list)?;
|
||||
},
|
||||
@@ -648,8 +839,11 @@ impl ::protobuf::Message for Punch {
|
||||
48 => {
|
||||
self.reply = is.read_bool()?;
|
||||
},
|
||||
56 => {
|
||||
self.step = is.read_enum_or_unknown()?;
|
||||
61 => {
|
||||
self.local_ip = is.read_fixed32()?;
|
||||
},
|
||||
64 => {
|
||||
self.local_port = is.read_uint32()?;
|
||||
},
|
||||
tag => {
|
||||
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
|
||||
@@ -663,9 +857,6 @@ impl ::protobuf::Message for Punch {
|
||||
#[allow(unused_variables)]
|
||||
fn compute_size(&self) -> u64 {
|
||||
let mut my_size = 0;
|
||||
if self.virtual_ip != 0 {
|
||||
my_size += 1 + 4;
|
||||
}
|
||||
my_size += 5 * self.public_ip_list.len() as u64;
|
||||
if self.public_port != 0 {
|
||||
my_size += ::protobuf::rt::uint32_size(3, self.public_port);
|
||||
@@ -673,14 +864,17 @@ impl ::protobuf::Message for Punch {
|
||||
if self.public_port_range != 0 {
|
||||
my_size += ::protobuf::rt::uint32_size(4, self.public_port_range);
|
||||
}
|
||||
if self.nat_type != ::protobuf::EnumOrUnknown::new(NatType::Symmetric) {
|
||||
if self.nat_type != ::protobuf::EnumOrUnknown::new(PunchNatType::Symmetric) {
|
||||
my_size += ::protobuf::rt::int32_size(5, self.nat_type.value());
|
||||
}
|
||||
if self.reply != false {
|
||||
my_size += 1 + 1;
|
||||
}
|
||||
if self.step != ::protobuf::EnumOrUnknown::new(Step::Step1) {
|
||||
my_size += ::protobuf::rt::int32_size(7, self.step.value());
|
||||
if self.local_ip != 0 {
|
||||
my_size += 1 + 4;
|
||||
}
|
||||
if self.local_port != 0 {
|
||||
my_size += ::protobuf::rt::uint32_size(8, self.local_port);
|
||||
}
|
||||
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
|
||||
self.special_fields.cached_size().set(my_size as u32);
|
||||
@@ -688,9 +882,6 @@ impl ::protobuf::Message for Punch {
|
||||
}
|
||||
|
||||
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
|
||||
if self.virtual_ip != 0 {
|
||||
os.write_fixed32(1, self.virtual_ip)?;
|
||||
}
|
||||
for v in &self.public_ip_list {
|
||||
os.write_fixed32(2, *v)?;
|
||||
};
|
||||
@@ -700,14 +891,17 @@ impl ::protobuf::Message for Punch {
|
||||
if self.public_port_range != 0 {
|
||||
os.write_uint32(4, self.public_port_range)?;
|
||||
}
|
||||
if self.nat_type != ::protobuf::EnumOrUnknown::new(NatType::Symmetric) {
|
||||
if self.nat_type != ::protobuf::EnumOrUnknown::new(PunchNatType::Symmetric) {
|
||||
os.write_enum(5, ::protobuf::EnumOrUnknown::value(&self.nat_type))?;
|
||||
}
|
||||
if self.reply != false {
|
||||
os.write_bool(6, self.reply)?;
|
||||
}
|
||||
if self.step != ::protobuf::EnumOrUnknown::new(Step::Step1) {
|
||||
os.write_enum(7, ::protobuf::EnumOrUnknown::value(&self.step))?;
|
||||
if self.local_ip != 0 {
|
||||
os.write_fixed32(7, self.local_ip)?;
|
||||
}
|
||||
if self.local_port != 0 {
|
||||
os.write_uint32(8, self.local_port)?;
|
||||
}
|
||||
os.write_unknown_fields(self.special_fields.unknown_fields())?;
|
||||
::std::result::Result::Ok(())
|
||||
@@ -721,87 +915,87 @@ impl ::protobuf::Message for Punch {
|
||||
&mut self.special_fields
|
||||
}
|
||||
|
||||
fn new() -> Punch {
|
||||
Punch::new()
|
||||
fn new() -> PunchInfo {
|
||||
PunchInfo::new()
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.virtual_ip = 0;
|
||||
self.public_ip_list.clear();
|
||||
self.public_port = 0;
|
||||
self.public_port_range = 0;
|
||||
self.nat_type = ::protobuf::EnumOrUnknown::new(NatType::Symmetric);
|
||||
self.nat_type = ::protobuf::EnumOrUnknown::new(PunchNatType::Symmetric);
|
||||
self.reply = false;
|
||||
self.step = ::protobuf::EnumOrUnknown::new(Step::Step1);
|
||||
self.local_ip = 0;
|
||||
self.local_port = 0;
|
||||
self.special_fields.clear();
|
||||
}
|
||||
|
||||
fn default_instance() -> &'static Punch {
|
||||
static instance: Punch = Punch {
|
||||
virtual_ip: 0,
|
||||
fn default_instance() -> &'static PunchInfo {
|
||||
static instance: PunchInfo = PunchInfo {
|
||||
public_ip_list: ::std::vec::Vec::new(),
|
||||
public_port: 0,
|
||||
public_port_range: 0,
|
||||
nat_type: ::protobuf::EnumOrUnknown::from_i32(0),
|
||||
reply: false,
|
||||
step: ::protobuf::EnumOrUnknown::from_i32(0),
|
||||
local_ip: 0,
|
||||
local_port: 0,
|
||||
special_fields: ::protobuf::SpecialFields::new(),
|
||||
};
|
||||
&instance
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::MessageFull for Punch {
|
||||
impl ::protobuf::MessageFull for PunchInfo {
|
||||
fn descriptor() -> ::protobuf::reflect::MessageDescriptor {
|
||||
static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new();
|
||||
descriptor.get(|| file_descriptor().message_by_package_relative_name("Punch").unwrap()).clone()
|
||||
descriptor.get(|| file_descriptor().message_by_package_relative_name("PunchInfo").unwrap()).clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for Punch {
|
||||
impl ::std::fmt::Display for PunchInfo {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
::protobuf::text_format::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::reflect::ProtobufValue for Punch {
|
||||
impl ::protobuf::reflect::ProtobufValue for PunchInfo {
|
||||
type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)]
|
||||
// @@protoc_insertion_point(enum:NatType)
|
||||
pub enum NatType {
|
||||
// @@protoc_insertion_point(enum_value:NatType.Symmetric)
|
||||
// @@protoc_insertion_point(enum:PunchNatType)
|
||||
pub enum PunchNatType {
|
||||
// @@protoc_insertion_point(enum_value:PunchNatType.Symmetric)
|
||||
Symmetric = 0,
|
||||
// @@protoc_insertion_point(enum_value:NatType.Cone)
|
||||
// @@protoc_insertion_point(enum_value:PunchNatType.Cone)
|
||||
Cone = 1,
|
||||
}
|
||||
|
||||
impl ::protobuf::Enum for NatType {
|
||||
const NAME: &'static str = "NatType";
|
||||
impl ::protobuf::Enum for PunchNatType {
|
||||
const NAME: &'static str = "PunchNatType";
|
||||
|
||||
fn value(&self) -> i32 {
|
||||
*self as i32
|
||||
}
|
||||
|
||||
fn from_i32(value: i32) -> ::std::option::Option<NatType> {
|
||||
fn from_i32(value: i32) -> ::std::option::Option<PunchNatType> {
|
||||
match value {
|
||||
0 => ::std::option::Option::Some(NatType::Symmetric),
|
||||
1 => ::std::option::Option::Some(NatType::Cone),
|
||||
0 => ::std::option::Option::Some(PunchNatType::Symmetric),
|
||||
1 => ::std::option::Option::Some(PunchNatType::Cone),
|
||||
_ => ::std::option::Option::None
|
||||
}
|
||||
}
|
||||
|
||||
const VALUES: &'static [NatType] = &[
|
||||
NatType::Symmetric,
|
||||
NatType::Cone,
|
||||
const VALUES: &'static [PunchNatType] = &[
|
||||
PunchNatType::Symmetric,
|
||||
PunchNatType::Cone,
|
||||
];
|
||||
}
|
||||
|
||||
impl ::protobuf::EnumFull for NatType {
|
||||
impl ::protobuf::EnumFull for PunchNatType {
|
||||
fn enum_descriptor() -> ::protobuf::reflect::EnumDescriptor {
|
||||
static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::Lazy::new();
|
||||
descriptor.get(|| file_descriptor().enum_by_package_relative_name("NatType").unwrap()).clone()
|
||||
descriptor.get(|| file_descriptor().enum_by_package_relative_name("PunchNatType").unwrap()).clone()
|
||||
}
|
||||
|
||||
fn descriptor(&self) -> ::protobuf::reflect::EnumValueDescriptor {
|
||||
@@ -810,100 +1004,41 @@ impl ::protobuf::EnumFull for NatType {
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::default::Default for NatType {
|
||||
impl ::std::default::Default for PunchNatType {
|
||||
fn default() -> Self {
|
||||
NatType::Symmetric
|
||||
PunchNatType::Symmetric
|
||||
}
|
||||
}
|
||||
|
||||
impl NatType {
|
||||
impl PunchNatType {
|
||||
fn generated_enum_descriptor_data() -> ::protobuf::reflect::GeneratedEnumDescriptorData {
|
||||
::protobuf::reflect::GeneratedEnumDescriptorData::new::<NatType>("NatType")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)]
|
||||
// @@protoc_insertion_point(enum:Step)
|
||||
pub enum Step {
|
||||
// @@protoc_insertion_point(enum_value:Step.Step1)
|
||||
Step1 = 0,
|
||||
// @@protoc_insertion_point(enum_value:Step.Step2)
|
||||
Step2 = 1,
|
||||
// @@protoc_insertion_point(enum_value:Step.Step3)
|
||||
Step3 = 2,
|
||||
// @@protoc_insertion_point(enum_value:Step.Step4)
|
||||
Step4 = 3,
|
||||
}
|
||||
|
||||
impl ::protobuf::Enum for Step {
|
||||
const NAME: &'static str = "Step";
|
||||
|
||||
fn value(&self) -> i32 {
|
||||
*self as i32
|
||||
}
|
||||
|
||||
fn from_i32(value: i32) -> ::std::option::Option<Step> {
|
||||
match value {
|
||||
0 => ::std::option::Option::Some(Step::Step1),
|
||||
1 => ::std::option::Option::Some(Step::Step2),
|
||||
2 => ::std::option::Option::Some(Step::Step3),
|
||||
3 => ::std::option::Option::Some(Step::Step4),
|
||||
_ => ::std::option::Option::None
|
||||
}
|
||||
}
|
||||
|
||||
const VALUES: &'static [Step] = &[
|
||||
Step::Step1,
|
||||
Step::Step2,
|
||||
Step::Step3,
|
||||
Step::Step4,
|
||||
];
|
||||
}
|
||||
|
||||
impl ::protobuf::EnumFull for Step {
|
||||
fn enum_descriptor() -> ::protobuf::reflect::EnumDescriptor {
|
||||
static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::Lazy::new();
|
||||
descriptor.get(|| file_descriptor().enum_by_package_relative_name("Step").unwrap()).clone()
|
||||
}
|
||||
|
||||
fn descriptor(&self) -> ::protobuf::reflect::EnumValueDescriptor {
|
||||
let index = *self as usize;
|
||||
Self::enum_descriptor().value_by_index(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::default::Default for Step {
|
||||
fn default() -> Self {
|
||||
Step::Step1
|
||||
}
|
||||
}
|
||||
|
||||
impl Step {
|
||||
fn generated_enum_descriptor_data() -> ::protobuf::reflect::GeneratedEnumDescriptorData {
|
||||
::protobuf::reflect::GeneratedEnumDescriptorData::new::<Step>("Step")
|
||||
::protobuf::reflect::GeneratedEnumDescriptorData::new::<PunchNatType>("PunchNatType")
|
||||
}
|
||||
}
|
||||
|
||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
||||
\n\rmessage.proto\"L\n\x13RegistrationRequest\x12\x14\n\x05token\x18\x01\
|
||||
\x20\x01(\tR\x05token\x12\x1f\n\x0bmac_address\x18\x02\x20\x01(\tR\nmacA\
|
||||
ddress\"\x83\x02\n\x14RegistrationResponse\x12\x1d\n\nvirtual_ip\x18\x01\
|
||||
\x20\x01(\x07R\tvirtualIp\x12'\n\x0fvirtual_gateway\x18\x02\x20\x01(\x07\
|
||||
R\x0evirtualGateway\x12'\n\x0fvirtual_netmask\x18\x03\x20\x01(\x07R\x0ev\
|
||||
irtualNetmask\x12\x14\n\x05epoch\x18\x04\x20\x01(\rR\x05epoch\x12&\n\x0f\
|
||||
virtual_ip_list\x18\x05\x20\x03(\x07R\rvirtualIpList\x12\x1b\n\tpublic_i\
|
||||
p\x18\x06\x20\x01(\x07R\x08publicIp\x12\x1f\n\x0bpublic_port\x18\x07\x20\
|
||||
\x01(\rR\npublicPort\"J\n\nDeviceList\x12\x14\n\x05epoch\x18\x01\x20\x01\
|
||||
(\rR\x05epoch\x12&\n\x0fvirtual_ip_list\x18\x02\x20\x03(\x07R\rvirtualIp\
|
||||
List\"\xef\x01\n\x05Punch\x12\x1d\n\nvirtual_ip\x18\x01\x20\x01(\x07R\tv\
|
||||
irtualIp\x12$\n\x0epublic_ip_list\x18\x02\x20\x03(\x07R\x0cpublicIpList\
|
||||
\x12\x1f\n\x0bpublic_port\x18\x03\x20\x01(\rR\npublicPort\x12*\n\x11publ\
|
||||
ic_port_range\x18\x04\x20\x01(\rR\x0fpublicPortRange\x12#\n\x08nat_type\
|
||||
\x18\x05\x20\x01(\x0e2\x08.NatTypeR\x07natType\x12\x14\n\x05reply\x18\
|
||||
\x06\x20\x01(\x08R\x05reply\x12\x19\n\x04step\x18\x07\x20\x01(\x0e2\x05.\
|
||||
StepR\x04step*\"\n\x07NatType\x12\r\n\tSymmetric\x10\0\x12\x08\n\x04Cone\
|
||||
\x10\x01*2\n\x04Step\x12\t\n\x05Step1\x10\0\x12\t\n\x05Step2\x10\x01\x12\
|
||||
\t\n\x05Step3\x10\x02\x12\t\n\x05Step4\x10\x03b\x06proto3\
|
||||
\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\
|
||||
";
|
||||
|
||||
/// `FileDescriptorProto` object which was a source for this generated file
|
||||
@@ -921,14 +1056,14 @@ pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor {
|
||||
file_descriptor.get(|| {
|
||||
let generated_file_descriptor = generated_file_descriptor_lazy.get(|| {
|
||||
let mut deps = ::std::vec::Vec::with_capacity(0);
|
||||
let mut messages = ::std::vec::Vec::with_capacity(4);
|
||||
let mut messages = ::std::vec::Vec::with_capacity(5);
|
||||
messages.push(RegistrationRequest::generated_message_descriptor_data());
|
||||
messages.push(RegistrationResponse::generated_message_descriptor_data());
|
||||
messages.push(DeviceInfo::generated_message_descriptor_data());
|
||||
messages.push(DeviceList::generated_message_descriptor_data());
|
||||
messages.push(Punch::generated_message_descriptor_data());
|
||||
let mut enums = ::std::vec::Vec::with_capacity(2);
|
||||
enums.push(NatType::generated_enum_descriptor_data());
|
||||
enums.push(Step::generated_enum_descriptor_data());
|
||||
messages.push(PunchInfo::generated_message_descriptor_data());
|
||||
let mut enums = ::std::vec::Vec::with_capacity(1);
|
||||
enums.push(PunchNatType::generated_enum_descriptor_data());
|
||||
::protobuf::reflect::GeneratedFileDescriptor::new_generated(
|
||||
file_descriptor_proto(),
|
||||
deps,
|
||||
@@ -0,0 +1,110 @@
|
||||
use std::{fmt, io};
|
||||
|
||||
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||
pub enum Protocol {
|
||||
/// ping请求
|
||||
/*
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| time | echo |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
Ping,
|
||||
/// 维持连接,内容同ping
|
||||
Pong,
|
||||
/// 打洞请求
|
||||
PunchRequest,
|
||||
/// 打洞响应
|
||||
PunchResponse,
|
||||
UnKnow(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for Protocol {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
1 => Protocol::Ping,
|
||||
2 => Protocol::Pong,
|
||||
3 => Protocol::PunchRequest,
|
||||
4 => Protocol::PunchResponse,
|
||||
val => Protocol::UnKnow(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for Protocol {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
Protocol::Ping => 1,
|
||||
Protocol::Pong => 2,
|
||||
Protocol::PunchRequest => 3,
|
||||
Protocol::PunchResponse => 4,
|
||||
Protocol::UnKnow(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ControlPacket<B> {
|
||||
PingPacket(PingPacket<B>),
|
||||
PongPacket(PongPacket<B>),
|
||||
PunchRequest,
|
||||
PunchResponse,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> ControlPacket<B> {
|
||||
pub fn new(protocol: u8, buffer: B) -> io::Result<ControlPacket<B>> {
|
||||
match Protocol::from(protocol) {
|
||||
Protocol::Ping => Ok(ControlPacket::PingPacket(PingPacket::new(buffer)?)),
|
||||
Protocol::Pong => Ok(ControlPacket::PongPacket(PongPacket::new(buffer)?)),
|
||||
Protocol::PunchRequest => Ok(ControlPacket::PunchRequest),
|
||||
Protocol::PunchResponse => Ok(ControlPacket::PunchResponse),
|
||||
Protocol::UnKnow(_) => Err(io::Error::new(io::ErrorKind::InvalidData, "Unsupported")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 网络探针
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct PingPacket<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
type PongPacket<B> = PingPacket<B>;
|
||||
|
||||
impl<B: AsRef<[u8]>> PingPacket<B> {
|
||||
pub fn new(buffer: B) -> io::Result<PingPacket<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
if len != 4 {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData, "len != 4"));
|
||||
}
|
||||
Ok(PingPacket { buffer })
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> PingPacket<B> {
|
||||
pub fn time(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[..2].try_into().unwrap())
|
||||
}
|
||||
pub fn epoch(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> PingPacket<B> {
|
||||
pub fn set_time(&mut self, time: u16) {
|
||||
self.buffer.as_mut()[..2].copy_from_slice(&time.to_be_bytes())
|
||||
}
|
||||
pub fn set_epoch(&mut self, epoch: u16) {
|
||||
self.buffer.as_mut()[2..4].copy_from_slice(&epoch.to_be_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for PingPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("PingPacket")
|
||||
.field("time", &self.time())
|
||||
.field("epoch", &self.epoch())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ use crate::error::*;
|
||||
pub enum Protocol {
|
||||
TokenError,
|
||||
Disconnect,
|
||||
AddressExhausted,
|
||||
Other(u8),
|
||||
}
|
||||
|
||||
@@ -12,6 +13,7 @@ impl From<u8> for Protocol {
|
||||
match value {
|
||||
1 => Self::TokenError,
|
||||
2 => Self::Disconnect,
|
||||
3 => Self::AddressExhausted,
|
||||
val => Self::Other(val),
|
||||
}
|
||||
}
|
||||
@@ -22,6 +24,7 @@ impl Into<u8> for Protocol {
|
||||
match self {
|
||||
Protocol::TokenError => 1,
|
||||
Protocol::Disconnect => 2,
|
||||
Protocol::AddressExhausted => 3,
|
||||
Protocol::Other(val) => val,
|
||||
}
|
||||
}
|
||||
@@ -30,6 +33,7 @@ impl Into<u8> for Protocol {
|
||||
pub enum InErrorPacket<B> {
|
||||
TokenError,
|
||||
Disconnect,
|
||||
AddressExhausted,
|
||||
OtherError(ErrorPacket<B>),
|
||||
}
|
||||
|
||||
@@ -38,6 +42,7 @@ impl<B: AsRef<[u8]>> InErrorPacket<B> {
|
||||
match Protocol::from(protocol) {
|
||||
Protocol::TokenError => Ok(InErrorPacket::TokenError),
|
||||
Protocol::Disconnect => Ok(InErrorPacket::Disconnect),
|
||||
Protocol::AddressExhausted => Ok(InErrorPacket::AddressExhausted),
|
||||
Protocol::Other(_) => Ok(InErrorPacket::OtherError(ErrorPacket::new(buffer)?)),
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,19 @@
|
||||
use std::fmt;
|
||||
use std::{fmt, io};
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::error::*;
|
||||
/*
|
||||
0 15 31
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 版本(8) | 协议(8) | 上层协议(8) | 初始ttl(4) | 生存时间(4) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 源ip地址(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 目的ip地址(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 数据体 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
pub mod control_packet;
|
||||
pub mod error_packet;
|
||||
@@ -41,6 +54,7 @@ pub enum Protocol {
|
||||
Control,
|
||||
/// 转发ipv4数据
|
||||
Ipv4Turn,
|
||||
/// 转发其他数据
|
||||
OtherTurn,
|
||||
UnKnow(u8),
|
||||
}
|
||||
@@ -71,17 +85,20 @@ impl Into<u8> for Protocol {
|
||||
}
|
||||
}
|
||||
|
||||
pub const MAX_TTL: u8 = 0b1111;
|
||||
pub const MAX_SOURCE: u8 = 0b11110000;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct NetPacket<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> NetPacket<B> {
|
||||
pub fn new(buffer: B) -> Result<NetPacket<B>> {
|
||||
pub fn new(buffer: B) -> io::Result<NetPacket<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
// 不能大于udp最大载荷长度
|
||||
if len < 4 || len > 65535 - 20 - 8 {
|
||||
return Err(Error::InvalidPacket);
|
||||
if len < 12 || len > 65535 - 20 - 8 {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData, "length overflow"));
|
||||
}
|
||||
Ok(NetPacket { buffer })
|
||||
}
|
||||
@@ -104,10 +121,21 @@ impl<B: AsRef<[u8]>> NetPacket<B> {
|
||||
self.buffer.as_ref()[2]
|
||||
}
|
||||
pub fn ttl(&self) -> u8 {
|
||||
self.buffer.as_ref()[3]
|
||||
self.buffer.as_ref()[3] & MAX_TTL
|
||||
}
|
||||
pub fn source_ttl(&self) -> u8 {
|
||||
self.buffer.as_ref()[3] >> 4
|
||||
}
|
||||
pub fn source(&self) -> Ipv4Addr {
|
||||
let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap();
|
||||
Ipv4Addr::from(tmp)
|
||||
}
|
||||
pub fn destination(&self) -> Ipv4Addr {
|
||||
let tmp: [u8; 4] = self.buffer.as_ref()[8..12].try_into().unwrap();
|
||||
Ipv4Addr::from(tmp)
|
||||
}
|
||||
pub fn payload(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[4..]
|
||||
&self.buffer.as_ref()[12..]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,14 +149,26 @@ impl<B: AsRef<[u8]> + AsMut<[u8]>> NetPacket<B> {
|
||||
pub fn set_transport_protocol(&mut self, transport_protocol: u8) {
|
||||
self.buffer.as_mut()[2] = transport_protocol;
|
||||
}
|
||||
pub fn first_set_ttl(&mut self, ttl: u8) {
|
||||
self.buffer.as_mut()[3] = ttl << 4 | ttl;
|
||||
}
|
||||
pub fn set_ttl(&mut self, ttl: u8) {
|
||||
self.buffer.as_mut()[3] = ttl;
|
||||
self.buffer.as_mut()[3] = (self.buffer.as_mut()[3] & MAX_SOURCE) | (MAX_TTL & ttl);
|
||||
}
|
||||
pub fn set_source_ttl(&mut self, source_ttl: u8) {
|
||||
self.buffer.as_mut()[3] = (source_ttl << 4) | (MAX_TTL & self.buffer.as_ref()[3]);
|
||||
}
|
||||
pub fn set_source(&mut self, source: Ipv4Addr) {
|
||||
self.buffer.as_mut()[4..8].copy_from_slice(&source.octets());
|
||||
}
|
||||
pub fn set_destination(&mut self, destination: Ipv4Addr) {
|
||||
self.buffer.as_mut()[8..12].copy_from_slice(&destination.octets());
|
||||
}
|
||||
pub fn set_payload(&mut self, payload: &[u8]) {
|
||||
self.buffer.as_mut()[4..payload.len() + 4].copy_from_slice(payload);
|
||||
self.buffer.as_mut()[12..payload.len() + 12].copy_from_slice(payload);
|
||||
}
|
||||
pub fn payload_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.buffer.as_mut()[4..]
|
||||
&mut self.buffer.as_mut()[12..]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,6 +179,9 @@ impl<B: AsRef<[u8]>> fmt::Debug for NetPacket<B> {
|
||||
.field("protocol", &self.protocol())
|
||||
.field("transport_protocol", &self.transport_protocol())
|
||||
.field("ttl", &self.ttl())
|
||||
.field("source_ttl", &self.source_ttl())
|
||||
.field("source", &self.source())
|
||||
.field("destination", &self.destination())
|
||||
.field("payload", &self.payload())
|
||||
.finish()
|
||||
}
|
||||
@@ -4,8 +4,10 @@ pub enum Protocol {
|
||||
RegistrationRequest,
|
||||
/// 注册响应
|
||||
RegistrationResponse,
|
||||
/// 更新设备列表
|
||||
UpdateDeviceList,
|
||||
/// 拉取设备列表
|
||||
PollDeviceList,
|
||||
/// 推送设备列表
|
||||
PushDeviceList,
|
||||
UnKnow(u8),
|
||||
}
|
||||
|
||||
@@ -14,7 +16,8 @@ impl From<u8> for Protocol {
|
||||
match value {
|
||||
1 => Self::RegistrationRequest,
|
||||
2 => Self::RegistrationResponse,
|
||||
3 => Self::UpdateDeviceList,
|
||||
3 => Self::PollDeviceList,
|
||||
4 => Self::PushDeviceList,
|
||||
val => Self::UnKnow(val),
|
||||
}
|
||||
}
|
||||
@@ -25,7 +28,8 @@ impl Into<u8> for Protocol {
|
||||
match self {
|
||||
Self::RegistrationRequest => 1,
|
||||
Self::RegistrationResponse => 2,
|
||||
Self::UpdateDeviceList => 3,
|
||||
Self::PollDeviceList => 3,
|
||||
Self::PushDeviceList => 4,
|
||||
Self::UnKnow(val) => val,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
pub enum Protocol {
|
||||
Punch,
|
||||
UnKnow(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for Protocol {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
1 => Protocol::Punch,
|
||||
val => Protocol::UnKnow(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for Protocol {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
Protocol::Punch => 1,
|
||||
Protocol::UnKnow(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
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),
|
||||
))
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
use std::net::Ipv4Addr;
|
||||
use std::process::Command;
|
||||
use std::io;
|
||||
use tun::Device;
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::tun_device::{TunReader, TunWriter};
|
||||
|
||||
pub fn create_tun(
|
||||
address: Ipv4Addr,
|
||||
netmask: Ipv4Addr,
|
||||
gateway: Ipv4Addr,
|
||||
) -> crate::error::Result<(TunWriter, TunReader)> {
|
||||
let mut config = tun::Configuration::default();
|
||||
|
||||
config
|
||||
.destination(gateway)
|
||||
.address(address)
|
||||
.netmask(netmask)
|
||||
.mtu(1420)
|
||||
.up();
|
||||
|
||||
let dev = tun::create(&config).unwrap();
|
||||
config_ip(dev.name(), address, netmask, gateway)?;
|
||||
// println!("{:?}", if_config_out);
|
||||
// let cmd_str: String = " ifconfig|grep flags=8051|awk -F ':' '{print $1}'|tail -1".to_string();
|
||||
//
|
||||
// let cmd_str_out = Command::new("sh")
|
||||
// .arg("-c")
|
||||
// .arg(cmd_str)
|
||||
// .output()
|
||||
// .expect("sh exec error!");
|
||||
// if !cmd_str_out.status.success(){
|
||||
// return Err(Error::Stop(format!("设置路由失败:{:?}", cmd_str_out)));
|
||||
// }
|
||||
// println!("{:?}", cmd_str_out);
|
||||
let packet_information = dev.has_packet_information();
|
||||
let queue = dev.queue(0).unwrap();
|
||||
let reader = queue.reader();
|
||||
let writer = queue.writer();
|
||||
Ok((
|
||||
TunWriter(writer, packet_information, Arc::new(Mutex::new(dev))),
|
||||
TunReader(reader, packet_information),
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn config_ip(name: &str, address: Ipv4Addr, netmask: Ipv4Addr, gateway: Ipv4Addr) -> io::Result<()> {
|
||||
let up_eth_str: String = format!("ifconfig {} {:?} {:?} up ", name, address, gateway);
|
||||
let route_add_str: String = format!(
|
||||
"sudo route -n add -net {:?} -netmask {:?} {:?}",
|
||||
address, netmask, gateway
|
||||
);
|
||||
let up_eth_out = Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(up_eth_str)
|
||||
.output()
|
||||
.expect("sh exec error!");
|
||||
if !up_eth_out.status.success() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("设置网络地址失败: {:?}", up_eth_out)));
|
||||
}
|
||||
let if_config_out = Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(route_add_str)
|
||||
.output()
|
||||
.expect("sh exec error!");
|
||||
if !if_config_out.status.success() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("添加路由失败: {:?}", if_config_out)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,14 +1,19 @@
|
||||
|
||||
#[cfg(any(unix))]
|
||||
pub use unix::create_tun;
|
||||
#[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(any(unix))]
|
||||
pub mod unix;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub mod windows;
|
||||
#[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;
|
||||
@@ -0,0 +1,76 @@
|
||||
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(());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
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(())
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
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
|
||||
@@ -0,0 +1,60 @@
|
||||
# ChangeLog
|
||||
|
||||
This format is based on [Keep a Changelog](https://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](https://semver.org).
|
||||
|
||||
## [0.2.1] - 2021-12-03
|
||||
|
||||
### Fixed
|
||||
Type in readme
|
||||
|
||||
## [0.2.0] - 2021-12-03
|
||||
|
||||
Added support for wintun 0.14.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- Wintun driver versions before `0.14` are no longer support due to beraking
|
||||
changes in the C API
|
||||
- `Adapter::create` returns a `Result<Adapter, ...>` instead of a `Result<CreateData, ...>`.
|
||||
This was done because the underlying Wintun function was changed to only return an adapter handle
|
||||
- `Adapter::create` the pool parameter was removed because it was also removed from the C function
|
||||
- `Adapter::delete` takes no parameters and returns a `Result<(), ()>`.
|
||||
The `force_close_sessions` parameter was removed because it was removed from the
|
||||
C function. Same for the bool inside the Ok(..) variant
|
||||
- `Adapter::create` and `Adapter::open` return `Arc<Adapter>` instead of `Adapter`
|
||||
- `get_running_driver_version` now returns a proper Result<Version, ()>.
|
||||
|
||||
### Added
|
||||
|
||||
- `reset_logger` function to disable logging after a logger has been set.
|
||||
|
||||
## [0.1.5] - 2021-08-27
|
||||
|
||||
### Fixed
|
||||
|
||||
- Readme on crates.io
|
||||
|
||||
## [0.1.4] - 2021-08-27
|
||||
|
||||
### Added
|
||||
- `panic_on_unsent_packets` feature flag to help in debugging ring buffer blockage issues
|
||||
|
||||
## [0.1.3] - 2021-06-28
|
||||
|
||||
### Fixed
|
||||
|
||||
- Cargo.toml metadata to include `package.metadata.docs.rs.default-target`.
|
||||
Fixes build issue on docs.rs (we can only build docs on windows, 0.1.1 doesn't work)
|
||||
|
||||
## [0.1.2] - 2021-06-28
|
||||
docs.rs testing
|
||||
|
||||
## [0.1.1] - 2021-06-28
|
||||
|
||||
- Cargo.toml metadata to build on linux
|
||||
|
||||
## [0.1.0] - 2021-06-28
|
||||
|
||||
First release with initial api
|
||||
|
||||
Generated
+427
@@ -0,0 +1,427 @@
|
||||
# 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",
|
||||
]
|
||||
@@ -0,0 +1,35 @@
|
||||
[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"
|
||||
@@ -0,0 +1,7 @@
|
||||
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.
|
||||
@@ -0,0 +1,80 @@
|
||||
# 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
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
bindgen \
|
||||
--allowlist-function "Wintun.*" \
|
||||
--allowlist-type "WINTUN_.*" \
|
||||
--dynamic-loading wintun \
|
||||
--dynamic-link-require-all \
|
||||
wintun/wintun_functions.h > src/wintun_raw.rs
|
||||
@@ -0,0 +1,345 @@
|
||||
/// 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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
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 {}
|
||||
@@ -0,0 +1,174 @@
|
||||
//! 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))
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
use crate::wintun_raw;
|
||||
use crate::Wintun;
|
||||
use log::*;
|
||||
use widestring::U16CStr;
|
||||
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
/// Sets the logger wintun will use when logging. Maps to the WintunSetLogger C function
|
||||
pub fn set_logger(wintun: &Wintun, f: wintun_raw::WINTUN_LOGGER_CALLBACK) {
|
||||
unsafe { wintun.WintunSetLogger(f) };
|
||||
}
|
||||
|
||||
pub fn reset_logger(wintun: &Wintun) {
|
||||
set_logger(wintun, None);
|
||||
}
|
||||
|
||||
static SET_LOGGER: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
/// The logger that is active by default. Logs messages to the log crate
|
||||
///
|
||||
/// # Safety
|
||||
/// `message` must be a valid pointer that points to an aligned null terminated UTF-16 string
|
||||
pub unsafe extern "C" fn default_logger(
|
||||
level: wintun_raw::WINTUN_LOGGER_LEVEL,
|
||||
_timestamp: wintun_raw::DWORD64,
|
||||
message: *const wintun_raw::WCHAR,
|
||||
) {
|
||||
//Cant wait for RFC 2585
|
||||
#[allow(unused_unsafe)]
|
||||
//Wintun will always give us a valid UTF16 null termineted string
|
||||
let msg = unsafe { U16CStr::from_ptr_str(message) };
|
||||
let utf8_msg = msg.to_string_lossy();
|
||||
match level {
|
||||
wintun_raw::WINTUN_LOGGER_LEVEL_WINTUN_LOG_INFO => info!("WinTun: {}", utf8_msg),
|
||||
wintun_raw::WINTUN_LOGGER_LEVEL_WINTUN_LOG_WARN => warn!("WinTun: {}", utf8_msg),
|
||||
wintun_raw::WINTUN_LOGGER_LEVEL_WINTUN_LOG_ERR => error!("WinTun: {}", utf8_msg),
|
||||
_ => error!("WinTun: {} (with invalid log level {})", utf8_msg, level),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_default_logger_if_unset(wintun: &Wintun) {
|
||||
if SET_LOGGER
|
||||
.compare_exchange(false, true, Ordering::SeqCst, Ordering::Relaxed)
|
||||
.is_ok()
|
||||
{
|
||||
set_logger(wintun, Some(default_logger));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,448 @@
|
||||
/* automatically generated by rust-bindgen 0.59.1 */
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct __BindgenBitfieldUnit<Storage> {
|
||||
storage: Storage,
|
||||
}
|
||||
impl<Storage> __BindgenBitfieldUnit<Storage> {
|
||||
#[inline]
|
||||
pub const fn new(storage: Storage) -> Self {
|
||||
Self { storage }
|
||||
}
|
||||
}
|
||||
impl<Storage> __BindgenBitfieldUnit<Storage>
|
||||
where
|
||||
Storage: AsRef<[u8]> + AsMut<[u8]>,
|
||||
{
|
||||
#[inline]
|
||||
pub fn get_bit(&self, index: usize) -> bool {
|
||||
debug_assert!(index / 8 < self.storage.as_ref().len());
|
||||
let byte_index = index / 8;
|
||||
let byte = self.storage.as_ref()[byte_index];
|
||||
let bit_index = if cfg!(target_endian = "big") {
|
||||
7 - (index % 8)
|
||||
} else {
|
||||
index % 8
|
||||
};
|
||||
let mask = 1 << bit_index;
|
||||
byte & mask == mask
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_bit(&mut self, index: usize, val: bool) {
|
||||
debug_assert!(index / 8 < self.storage.as_ref().len());
|
||||
let byte_index = index / 8;
|
||||
let byte = &mut self.storage.as_mut()[byte_index];
|
||||
let bit_index = if cfg!(target_endian = "big") {
|
||||
7 - (index % 8)
|
||||
} else {
|
||||
index % 8
|
||||
};
|
||||
let mask = 1 << bit_index;
|
||||
if val {
|
||||
*byte |= mask;
|
||||
} else {
|
||||
*byte &= !mask;
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 {
|
||||
debug_assert!(bit_width <= 64);
|
||||
debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
|
||||
debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
|
||||
let mut val = 0;
|
||||
for i in 0..(bit_width as usize) {
|
||||
if self.get_bit(i + bit_offset) {
|
||||
let index = if cfg!(target_endian = "big") {
|
||||
bit_width as usize - 1 - i
|
||||
} else {
|
||||
i
|
||||
};
|
||||
val |= 1 << index;
|
||||
}
|
||||
}
|
||||
val
|
||||
}
|
||||
#[inline]
|
||||
pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) {
|
||||
debug_assert!(bit_width <= 64);
|
||||
debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
|
||||
debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
|
||||
for i in 0..(bit_width as usize) {
|
||||
let mask = 1 << i;
|
||||
let val_bit_is_set = val & mask == mask;
|
||||
let index = if cfg!(target_endian = "big") {
|
||||
bit_width as usize - 1 - i
|
||||
} else {
|
||||
i
|
||||
};
|
||||
self.set_bit(index + bit_offset, val_bit_is_set);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub type wchar_t = ::std::os::raw::c_ushort;
|
||||
pub type DWORD = ::std::os::raw::c_ulong;
|
||||
pub type BOOL = ::std::os::raw::c_int;
|
||||
pub type BYTE = ::std::os::raw::c_uchar;
|
||||
pub type ULONG64 = ::std::os::raw::c_ulonglong;
|
||||
pub type DWORD64 = ::std::os::raw::c_ulonglong;
|
||||
pub type WCHAR = wchar_t;
|
||||
pub type LPCWSTR = *const WCHAR;
|
||||
pub type HANDLE = *mut ::std::os::raw::c_void;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct _GUID {
|
||||
pub Data1: ::std::os::raw::c_ulong,
|
||||
pub Data2: ::std::os::raw::c_ushort,
|
||||
pub Data3: ::std::os::raw::c_ushort,
|
||||
pub Data4: [::std::os::raw::c_uchar; 8usize],
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout__GUID() {
|
||||
assert_eq!(
|
||||
::std::mem::size_of::<_GUID>(),
|
||||
16usize,
|
||||
concat!("Size of: ", stringify!(_GUID))
|
||||
);
|
||||
assert_eq!(
|
||||
::std::mem::align_of::<_GUID>(),
|
||||
4usize,
|
||||
concat!("Alignment of ", stringify!(_GUID))
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<_GUID>())).Data1 as *const _ as usize },
|
||||
0usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(_GUID),
|
||||
"::",
|
||||
stringify!(Data1)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<_GUID>())).Data2 as *const _ as usize },
|
||||
4usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(_GUID),
|
||||
"::",
|
||||
stringify!(Data2)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<_GUID>())).Data3 as *const _ as usize },
|
||||
6usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(_GUID),
|
||||
"::",
|
||||
stringify!(Data3)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<_GUID>())).Data4 as *const _ as usize },
|
||||
8usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(_GUID),
|
||||
"::",
|
||||
stringify!(Data4)
|
||||
)
|
||||
);
|
||||
}
|
||||
pub type GUID = _GUID;
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub union _NET_LUID_LH {
|
||||
pub Value: ULONG64,
|
||||
pub Info: _NET_LUID_LH__bindgen_ty_1,
|
||||
}
|
||||
#[repr(C)]
|
||||
#[repr(align(8))]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct _NET_LUID_LH__bindgen_ty_1 {
|
||||
pub _bitfield_align_1: [u32; 0],
|
||||
pub _bitfield_1: __BindgenBitfieldUnit<[u8; 8usize]>,
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout__NET_LUID_LH__bindgen_ty_1() {
|
||||
assert_eq!(
|
||||
::std::mem::size_of::<_NET_LUID_LH__bindgen_ty_1>(),
|
||||
8usize,
|
||||
concat!("Size of: ", stringify!(_NET_LUID_LH__bindgen_ty_1))
|
||||
);
|
||||
assert_eq!(
|
||||
::std::mem::align_of::<_NET_LUID_LH__bindgen_ty_1>(),
|
||||
8usize,
|
||||
concat!("Alignment of ", stringify!(_NET_LUID_LH__bindgen_ty_1))
|
||||
);
|
||||
}
|
||||
impl _NET_LUID_LH__bindgen_ty_1 {
|
||||
#[inline]
|
||||
pub fn Reserved(&self) -> ULONG64 {
|
||||
unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 24u8) as u64) }
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_Reserved(&mut self, val: ULONG64) {
|
||||
unsafe {
|
||||
let val: u64 = ::std::mem::transmute(val);
|
||||
self._bitfield_1.set(0usize, 24u8, val as u64)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn NetLuidIndex(&self) -> ULONG64 {
|
||||
unsafe { ::std::mem::transmute(self._bitfield_1.get(24usize, 24u8) as u64) }
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_NetLuidIndex(&mut self, val: ULONG64) {
|
||||
unsafe {
|
||||
let val: u64 = ::std::mem::transmute(val);
|
||||
self._bitfield_1.set(24usize, 24u8, val as u64)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn IfType(&self) -> ULONG64 {
|
||||
unsafe { ::std::mem::transmute(self._bitfield_1.get(48usize, 16u8) as u64) }
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_IfType(&mut self, val: ULONG64) {
|
||||
unsafe {
|
||||
let val: u64 = ::std::mem::transmute(val);
|
||||
self._bitfield_1.set(48usize, 16u8, val as u64)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn new_bitfield_1(
|
||||
Reserved: ULONG64,
|
||||
NetLuidIndex: ULONG64,
|
||||
IfType: ULONG64,
|
||||
) -> __BindgenBitfieldUnit<[u8; 8usize]> {
|
||||
let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 8usize]> = Default::default();
|
||||
__bindgen_bitfield_unit.set(0usize, 24u8, {
|
||||
let Reserved: u64 = unsafe { ::std::mem::transmute(Reserved) };
|
||||
Reserved as u64
|
||||
});
|
||||
__bindgen_bitfield_unit.set(24usize, 24u8, {
|
||||
let NetLuidIndex: u64 = unsafe { ::std::mem::transmute(NetLuidIndex) };
|
||||
NetLuidIndex as u64
|
||||
});
|
||||
__bindgen_bitfield_unit.set(48usize, 16u8, {
|
||||
let IfType: u64 = unsafe { ::std::mem::transmute(IfType) };
|
||||
IfType as u64
|
||||
});
|
||||
__bindgen_bitfield_unit
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout__NET_LUID_LH() {
|
||||
assert_eq!(
|
||||
::std::mem::size_of::<_NET_LUID_LH>(),
|
||||
8usize,
|
||||
concat!("Size of: ", stringify!(_NET_LUID_LH))
|
||||
);
|
||||
assert_eq!(
|
||||
::std::mem::align_of::<_NET_LUID_LH>(),
|
||||
8usize,
|
||||
concat!("Alignment of ", stringify!(_NET_LUID_LH))
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<_NET_LUID_LH>())).Value as *const _ as usize },
|
||||
0usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(_NET_LUID_LH),
|
||||
"::",
|
||||
stringify!(Value)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(::std::ptr::null::<_NET_LUID_LH>())).Info as *const _ as usize },
|
||||
0usize,
|
||||
concat!(
|
||||
"Offset of field: ",
|
||||
stringify!(_NET_LUID_LH),
|
||||
"::",
|
||||
stringify!(Info)
|
||||
)
|
||||
);
|
||||
}
|
||||
pub type NET_LUID_LH = _NET_LUID_LH;
|
||||
pub type NET_LUID = NET_LUID_LH;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct _WINTUN_ADAPTER {
|
||||
_unused: [u8; 0],
|
||||
}
|
||||
#[doc = " A handle representing Wintun adapter"]
|
||||
pub type WINTUN_ADAPTER_HANDLE = *mut _WINTUN_ADAPTER;
|
||||
#[doc = "< Informational"]
|
||||
pub const WINTUN_LOGGER_LEVEL_WINTUN_LOG_INFO: WINTUN_LOGGER_LEVEL = 0;
|
||||
#[doc = "< Warning"]
|
||||
pub const WINTUN_LOGGER_LEVEL_WINTUN_LOG_WARN: WINTUN_LOGGER_LEVEL = 1;
|
||||
#[doc = "< Error"]
|
||||
pub const WINTUN_LOGGER_LEVEL_WINTUN_LOG_ERR: WINTUN_LOGGER_LEVEL = 2;
|
||||
#[doc = " Determines the level of logging, passed to WINTUN_LOGGER_CALLBACK."]
|
||||
pub type WINTUN_LOGGER_LEVEL = ::std::os::raw::c_int;
|
||||
#[doc = " Called by internal logger to report diagnostic messages"]
|
||||
#[doc = ""]
|
||||
#[doc = " @param Level Message level."]
|
||||
#[doc = ""]
|
||||
#[doc = " @param Timestamp Message timestamp in in 100ns intervals since 1601-01-01 UTC."]
|
||||
#[doc = ""]
|
||||
#[doc = " @param Message Message text."]
|
||||
pub type WINTUN_LOGGER_CALLBACK = ::std::option::Option<
|
||||
unsafe extern "C" fn(Level: WINTUN_LOGGER_LEVEL, Timestamp: DWORD64, Message: LPCWSTR),
|
||||
>;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct _TUN_SESSION {
|
||||
_unused: [u8; 0],
|
||||
}
|
||||
#[doc = " A handle representing Wintun session"]
|
||||
pub type WINTUN_SESSION_HANDLE = *mut _TUN_SESSION;
|
||||
extern crate libloading;
|
||||
pub struct wintun {
|
||||
__library: ::libloading::Library,
|
||||
pub WintunCreateAdapter: unsafe extern "C" fn(
|
||||
arg1: LPCWSTR,
|
||||
arg2: LPCWSTR,
|
||||
arg3: *const GUID,
|
||||
) -> WINTUN_ADAPTER_HANDLE,
|
||||
pub WintunCloseAdapter: unsafe extern "C" fn(arg1: WINTUN_ADAPTER_HANDLE),
|
||||
pub WintunOpenAdapter: unsafe extern "C" fn(arg1: LPCWSTR) -> WINTUN_ADAPTER_HANDLE,
|
||||
pub WintunGetAdapterLUID:
|
||||
unsafe extern "C" fn(arg1: WINTUN_ADAPTER_HANDLE, arg2: *mut NET_LUID),
|
||||
pub WintunGetRunningDriverVersion: unsafe extern "C" fn() -> DWORD,
|
||||
pub WintunDeleteDriver: unsafe extern "C" fn() -> BOOL,
|
||||
pub WintunSetLogger: unsafe extern "C" fn(arg1: WINTUN_LOGGER_CALLBACK),
|
||||
pub WintunStartSession:
|
||||
unsafe extern "C" fn(arg1: WINTUN_ADAPTER_HANDLE, arg2: DWORD) -> WINTUN_SESSION_HANDLE,
|
||||
pub WintunEndSession: unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE),
|
||||
pub WintunGetReadWaitEvent: unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE) -> HANDLE,
|
||||
pub WintunReceivePacket:
|
||||
unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE, arg2: *mut DWORD) -> *mut BYTE,
|
||||
pub WintunReleaseReceivePacket:
|
||||
unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE, arg2: *const BYTE),
|
||||
pub WintunAllocateSendPacket:
|
||||
unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE, arg2: DWORD) -> *mut BYTE,
|
||||
pub WintunSendPacket: unsafe extern "C" fn(arg1: WINTUN_SESSION_HANDLE, arg2: *const BYTE),
|
||||
}
|
||||
impl wintun {
|
||||
pub unsafe fn new<P>(path: P) -> Result<Self, ::libloading::Error>
|
||||
where
|
||||
P: AsRef<::std::ffi::OsStr>,
|
||||
{
|
||||
let library = ::libloading::Library::new(path)?;
|
||||
Self::from_library(library)
|
||||
}
|
||||
pub unsafe fn from_library<L>(library: L) -> Result<Self, ::libloading::Error>
|
||||
where
|
||||
L: Into<::libloading::Library>,
|
||||
{
|
||||
let __library = library.into();
|
||||
let WintunCreateAdapter = __library.get(b"WintunCreateAdapter\0").map(|sym| *sym)?;
|
||||
let WintunCloseAdapter = __library.get(b"WintunCloseAdapter\0").map(|sym| *sym)?;
|
||||
let WintunOpenAdapter = __library.get(b"WintunOpenAdapter\0").map(|sym| *sym)?;
|
||||
let WintunGetAdapterLUID = __library.get(b"WintunGetAdapterLUID\0").map(|sym| *sym)?;
|
||||
let WintunGetRunningDriverVersion = __library
|
||||
.get(b"WintunGetRunningDriverVersion\0")
|
||||
.map(|sym| *sym)?;
|
||||
let WintunDeleteDriver = __library.get(b"WintunDeleteDriver\0").map(|sym| *sym)?;
|
||||
let WintunSetLogger = __library.get(b"WintunSetLogger\0").map(|sym| *sym)?;
|
||||
let WintunStartSession = __library.get(b"WintunStartSession\0").map(|sym| *sym)?;
|
||||
let WintunEndSession = __library.get(b"WintunEndSession\0").map(|sym| *sym)?;
|
||||
let WintunGetReadWaitEvent = __library.get(b"WintunGetReadWaitEvent\0").map(|sym| *sym)?;
|
||||
let WintunReceivePacket = __library.get(b"WintunReceivePacket\0").map(|sym| *sym)?;
|
||||
let WintunReleaseReceivePacket = __library
|
||||
.get(b"WintunReleaseReceivePacket\0")
|
||||
.map(|sym| *sym)?;
|
||||
let WintunAllocateSendPacket = __library
|
||||
.get(b"WintunAllocateSendPacket\0")
|
||||
.map(|sym| *sym)?;
|
||||
let WintunSendPacket = __library.get(b"WintunSendPacket\0").map(|sym| *sym)?;
|
||||
Ok(wintun {
|
||||
__library,
|
||||
WintunCreateAdapter,
|
||||
WintunCloseAdapter,
|
||||
WintunOpenAdapter,
|
||||
WintunGetAdapterLUID,
|
||||
WintunGetRunningDriverVersion,
|
||||
WintunDeleteDriver,
|
||||
WintunSetLogger,
|
||||
WintunStartSession,
|
||||
WintunEndSession,
|
||||
WintunGetReadWaitEvent,
|
||||
WintunReceivePacket,
|
||||
WintunReleaseReceivePacket,
|
||||
WintunAllocateSendPacket,
|
||||
WintunSendPacket,
|
||||
})
|
||||
}
|
||||
pub unsafe fn WintunCreateAdapter(
|
||||
&self,
|
||||
arg1: LPCWSTR,
|
||||
arg2: LPCWSTR,
|
||||
arg3: *const GUID,
|
||||
) -> WINTUN_ADAPTER_HANDLE {
|
||||
(self.WintunCreateAdapter)(arg1, arg2, arg3)
|
||||
}
|
||||
pub unsafe fn WintunCloseAdapter(&self, arg1: WINTUN_ADAPTER_HANDLE) -> () {
|
||||
(self.WintunCloseAdapter)(arg1)
|
||||
}
|
||||
pub unsafe fn WintunOpenAdapter(&self, arg1: LPCWSTR) -> WINTUN_ADAPTER_HANDLE {
|
||||
(self.WintunOpenAdapter)(arg1)
|
||||
}
|
||||
pub unsafe fn WintunGetAdapterLUID(
|
||||
&self,
|
||||
arg1: WINTUN_ADAPTER_HANDLE,
|
||||
arg2: *mut NET_LUID,
|
||||
) -> () {
|
||||
(self.WintunGetAdapterLUID)(arg1, arg2)
|
||||
}
|
||||
pub unsafe fn WintunGetRunningDriverVersion(&self) -> DWORD {
|
||||
(self.WintunGetRunningDriverVersion)()
|
||||
}
|
||||
pub unsafe fn WintunDeleteDriver(&self) -> BOOL {
|
||||
(self.WintunDeleteDriver)()
|
||||
}
|
||||
pub unsafe fn WintunSetLogger(&self, arg1: WINTUN_LOGGER_CALLBACK) -> () {
|
||||
(self.WintunSetLogger)(arg1)
|
||||
}
|
||||
pub unsafe fn WintunStartSession(
|
||||
&self,
|
||||
arg1: WINTUN_ADAPTER_HANDLE,
|
||||
arg2: DWORD,
|
||||
) -> WINTUN_SESSION_HANDLE {
|
||||
(self.WintunStartSession)(arg1, arg2)
|
||||
}
|
||||
pub unsafe fn WintunEndSession(&self, arg1: WINTUN_SESSION_HANDLE) -> () {
|
||||
(self.WintunEndSession)(arg1)
|
||||
}
|
||||
pub unsafe fn WintunGetReadWaitEvent(&self, arg1: WINTUN_SESSION_HANDLE) -> HANDLE {
|
||||
(self.WintunGetReadWaitEvent)(arg1)
|
||||
}
|
||||
pub unsafe fn WintunReceivePacket(
|
||||
&self,
|
||||
arg1: WINTUN_SESSION_HANDLE,
|
||||
arg2: *mut DWORD,
|
||||
) -> *mut BYTE {
|
||||
(self.WintunReceivePacket)(arg1, arg2)
|
||||
}
|
||||
pub unsafe fn WintunReleaseReceivePacket(
|
||||
&self,
|
||||
arg1: WINTUN_SESSION_HANDLE,
|
||||
arg2: *const BYTE,
|
||||
) -> () {
|
||||
(self.WintunReleaseReceivePacket)(arg1, arg2)
|
||||
}
|
||||
pub unsafe fn WintunAllocateSendPacket(
|
||||
&self,
|
||||
arg1: WINTUN_SESSION_HANDLE,
|
||||
arg2: DWORD,
|
||||
) -> *mut BYTE {
|
||||
(self.WintunAllocateSendPacket)(arg1, arg2)
|
||||
}
|
||||
pub unsafe fn WintunSendPacket(&self, arg1: WINTUN_SESSION_HANDLE, arg2: *const BYTE) -> () {
|
||||
(self.WintunSendPacket)(arg1, arg2)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
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.
|
||||
@@ -0,0 +1,270 @@
|
||||
/* 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
|
||||
@@ -0,0 +1,19 @@
|
||||
// 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;
|
||||
Reference in New Issue
Block a user