支持自定义ip、服务端tcp通道、可选择禁止p2p
@@ -58,6 +58,8 @@ jobs:
|
||||
OS: ubuntu-latest
|
||||
- TARGET: x86_64-apple-darwin # tested on a mac, is not properly signed so there are security warnings
|
||||
OS: macos-latest
|
||||
- TARGET: aarch64-apple-darwin # tested on a mac, is not properly signed so there are security warnings
|
||||
OS: macos-latest
|
||||
- TARGET: i686-pc-windows-msvc # tested on a windows machine
|
||||
OS: windows-latest
|
||||
- TARGET: x86_64-pc-windows-msvc # tested on a windows machine
|
||||
@@ -65,7 +67,7 @@ jobs:
|
||||
# needs: test
|
||||
runs-on: ${{ matrix.OS }}
|
||||
env:
|
||||
NAME: switch-cmd # change with the name of your project
|
||||
NAME: vnt-cli # change with the name of your project
|
||||
TARGET: ${{ matrix.TARGET }}
|
||||
OS: ${{ matrix.OS }}
|
||||
steps:
|
||||
@@ -106,7 +108,7 @@ jobs:
|
||||
- name: Install rust target
|
||||
run: rustup target add $TARGET
|
||||
- name: Run build
|
||||
run: cargo build --package switch-cmd --release --verbose --target $TARGET
|
||||
run: cargo build --package vnt-cli --release --verbose --target $TARGET
|
||||
- name: List target
|
||||
run: find ./target
|
||||
- name: Compress
|
||||
@@ -128,7 +130,7 @@ jobs:
|
||||
- name: Archive artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: switch-cmd
|
||||
name: vnt-cli
|
||||
path: |
|
||||
./artifacts
|
||||
# deploys to github releases on tag
|
||||
@@ -140,7 +142,7 @@ jobs:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: switch-cmd
|
||||
name: vnt-cli
|
||||
path: ./artifacts
|
||||
- name: List
|
||||
run: find ./artifacts
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[workspace]
|
||||
members = ["switch","common","switch-desktop","switch-cmd","switch-jni"]
|
||||
members = ["vnt","common","vnt-cli","vnt-jni"]
|
||||
|
||||
[profile.release]
|
||||
opt-level = 'z'
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
# switch
|
||||
# Vnt
|
||||
|
||||
A virtual network tool (VPN)
|
||||
|
||||
将不同网络下的多个设备虚拟到一个局域网下
|
||||
### switch-cmd参数详解 [参数说明](https://github.com/lbl8603/switch/blob/main/switch-cmd/README.md)
|
||||
### vnt-cli参数详解 [参数说明](https://github.com/lbl8603/vnt/blob/main/vnt-cli/README.md)
|
||||
### 快速使用:
|
||||
|
||||
1. 指定一个token,在多台设备上运行该程序,例如:
|
||||
```shell
|
||||
# linux上
|
||||
root@DESKTOP-0BCHNIO:/opt# ./switch-cmd -k 123456
|
||||
root@DESKTOP-0BCHNIO:/opt# ./vnt-cli -k 123456
|
||||
# 在另一台linux上使用nohup后台运行
|
||||
root@izj6cemne76ykdzkataftfz switch# nohup ./switch-cmd -k 123456 &
|
||||
root@izj6cemne76ykdzkataftfz vnt# nohup ./vnt-cli -k 123456 &
|
||||
# windows上
|
||||
D:\switch\bin_v1>switch-cmd.exe -k 123456
|
||||
D:\vnt\bin_v1>vnt-cli.exe -k 123456
|
||||
```
|
||||
2. 可以执行info命令查看当前设备的虚拟ip
|
||||
```shell
|
||||
root@DESKTOP-0BCHNIO:/opt# ./switch-cmd --info
|
||||
root@DESKTOP-0BCHNIO:/opt# ./vnt-cli --info
|
||||
Name: Ubuntu 18.04 (bionic) [64-bit]
|
||||
Virtual ip: 10.26.0.2
|
||||
Virtual gateway: 10.26.0.1
|
||||
@@ -30,14 +30,14 @@ A virtual network tool (VPN)
|
||||
```
|
||||
3. 也可以执行list命令查看其他设备的虚拟ip
|
||||
```shell
|
||||
root@DESKTOP-0BCHNIO:/opt# ./switch-cmd --list
|
||||
root@DESKTOP-0BCHNIO:/opt# ./vnt-cli --list
|
||||
Name Virtual Ip P2P/Relay Rt Status
|
||||
Windows 10.0.22621 (Windows 11 Professional) [64-bit] 10.26.0.3 p2p 2 Online
|
||||
CentOS 7.9.2009 (Core) [64-bit] 10.26.0.4 p2p 35 Online
|
||||
```
|
||||
4. 最后可以用虚拟ip实现设备间相互访问
|
||||
|
||||
<img width="506" alt="ssh" src="https://raw.githubusercontent.com/lbl8603/switch/dev/documents/img/ssh.jpg">
|
||||
<img width="506" alt="ssh" src="https://raw.githubusercontent.com/lbl8603/vnt/dev/documents/img/ssh.jpg">
|
||||
5. 帮助,使用-h命令查看
|
||||
|
||||
### 更多玩法
|
||||
@@ -52,15 +52,15 @@ A virtual network tool (VPN)
|
||||
- token的作用是标识一个虚拟局域网,当使用公共服务器时,建议使用一个唯一值当token(比如uuid),否则有可能连接到其他人创建的虚拟局域网中
|
||||
- 默认使用公共服务器,目前的配置是2核4G 4Mbps,有需要再扩展~
|
||||
- 需要root/管理员权限
|
||||
- switch-cmd需要使用命令行运行
|
||||
- Mac和Linux下需要加可执行权限(例如:chmod +x ./switch-cmd)
|
||||
- 可以自己搭注册和中继服务器([server](https://github.com/lbl8603/switch-server))
|
||||
- vnt-cli需要使用命令行运行
|
||||
- Mac和Linux下需要加可执行权限(例如:chmod +x ./vnt-cli)
|
||||
- 可以自己搭注册和中继服务器([server](https://github.com/lbl8603/vnts))
|
||||
|
||||
### 编译
|
||||
|
||||
前提条件:安装rust编译环境([install rust](https://www.rust-lang.org/zh-CN/tools/install))
|
||||
|
||||
到项目根目录下执行 cargo build -p switch-cmd
|
||||
到项目根目录下执行 cargo build -p vnt-cli
|
||||
|
||||
### 支持平台
|
||||
|
||||
@@ -98,13 +98,13 @@ A virtual network tool (VPN)
|
||||
|
||||
##### 可能原因:
|
||||
|
||||
switch默认使用10.26.0.0/24网段,和本地网络适配器的ip冲突
|
||||
vnt默认使用10.26.0.0/24网段,和本地网络适配器的ip冲突
|
||||
|
||||
##### 解决方法:
|
||||
|
||||
1. 方法一:找到冲突的IP,将其改成别的
|
||||
2. 方法二:自建服务器,指定其他不会冲突的网段
|
||||
3. 方法三:增加参数-d <device-id> ,设置不同的id会让switch-server分配不同的IP,从而绕开有冲突的IP
|
||||
3. 方法三:增加参数-d <device-id> ,设置不同的id会让服务端分配不同的IP,从而绕开有冲突的IP
|
||||
|
||||
#### 问题2: windows系统上wintun.dll加载失败
|
||||
|
||||
@@ -113,8 +113,8 @@ switch默认使用10.26.0.0/24网段,和本地网络适配器的ip冲突
|
||||
##### 解决方法:
|
||||
1. 下载最新版的wintun.dll [下载链接](https://www.wintun.net/builds/wintun-0.14.1.zip)
|
||||
2. 解压后找到对应架构的目录,通常是amd64
|
||||
3. 将对应的wintun.dll放到和switch-cmd同目录下(或者放到C盘Windows目录下)
|
||||
4. 再次启动switch-cmd
|
||||
3. 将对应的wintun.dll放到和vnt-cli同目录下(或者放到C盘Windows目录下)
|
||||
4. 再次启动vnt-cli
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "common"
|
||||
version = "1.1.0"
|
||||
version = "1.1.1"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -38,7 +38,6 @@ pub fn get_unique_identifier() -> Option<String> {
|
||||
let identifier = result
|
||||
.lines()
|
||||
.find(|line| line.contains("IOPlatformUUID"))
|
||||
.and_then(|line| line.split('"').nth(4))
|
||||
.unwrap_or("").trim();
|
||||
if identifier.is_empty() {
|
||||
None
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
[package]
|
||||
name = "switch-desktop"
|
||||
version = "1.1.0"
|
||||
description = "switch desktop"
|
||||
authors = ["lubeilin"]
|
||||
license = ""
|
||||
repository = ""
|
||||
default-run = "switch-desktop"
|
||||
edition = "2021"
|
||||
rust-version = "1.60"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "1.4.0", features = [] }
|
||||
embed-resource = "2.2"
|
||||
|
||||
[dependencies]
|
||||
switch = {path="../switch"}
|
||||
common = {path="../common"}
|
||||
dirs = "4.0.0"
|
||||
os_info = "3.7.0"
|
||||
lazy_static = "1.4.0"
|
||||
parking_lot = "0.12.1"
|
||||
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tauri = { version = "1.4.0", features = [] }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3", features = [] }
|
||||
|
||||
[features]
|
||||
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.
|
||||
# If you use cargo directly instead of tauri's cli you can use this feature flag to switch between tauri's `dev` and `build` modes.
|
||||
# DO NOT REMOVE!!
|
||||
custom-protocol = [ "tauri/custom-protocol" ]
|
||||
@@ -1,21 +0,0 @@
|
||||
fn main() {
|
||||
if std::env::var_os("CARGO_CFG_WINDOWS").is_some() {
|
||||
let mut windows = tauri_build::WindowsAttributes::new();
|
||||
windows = windows.app_manifest(r#"
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
</assembly>
|
||||
"#);
|
||||
let attrs = tauri_build::Attributes::new().windows_attributes(windows);
|
||||
tauri_build::try_build(attrs).expect("failed to run build script");
|
||||
} else {
|
||||
tauri_build::build();
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 49 KiB |
@@ -1,9 +0,0 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn get_home() -> PathBuf {
|
||||
let home = dirs::home_dir().unwrap().join(".switch_desktop");
|
||||
if !home.exists() {
|
||||
std::fs::create_dir(&home).unwrap();
|
||||
}
|
||||
home
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use crate::config::get_home;
|
||||
|
||||
const DLL_FILE: &'static [u8] = include_bytes!("../../dll/amd64/wintun.dll");
|
||||
|
||||
pub fn load_tun_dll() -> io::Result<()> {
|
||||
let lib_path = get_home().join("lib");
|
||||
if !lib_path.exists() {
|
||||
std::fs::create_dir(&lib_path).unwrap();
|
||||
}
|
||||
let dll_path = lib_path.join("wintun.dll");
|
||||
if !dll_path.exists() {
|
||||
let mut f = File::create(&dll_path)?;
|
||||
f.write_all(DLL_FILE)?;
|
||||
f.sync_data()?;
|
||||
}
|
||||
let dll_directory = lib_path.as_os_str();
|
||||
|
||||
let dll_directory_wide: Vec<u16> = dll_directory.encode_wide().chain(Some(0)).collect();
|
||||
unsafe {
|
||||
winapi::um::winbase::SetDllDirectoryW(dll_directory_wide.as_ptr());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,225 +0,0 @@
|
||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
use std::net::{Ipv4Addr, ToSocketAddrs};
|
||||
use lazy_static::lazy_static;
|
||||
use parking_lot::Mutex;
|
||||
use common::args_parse::ips_parse;
|
||||
use switch::core::Config;
|
||||
use switch::core::{Switch, SwitchUtil};
|
||||
use switch::handle::registration_handler::ReqEnum;
|
||||
|
||||
#[cfg(windows)]
|
||||
mod load_dll;
|
||||
#[cfg(windows)]
|
||||
mod config;
|
||||
|
||||
lazy_static! {
|
||||
static ref SWITCH:Mutex<Option<Switch>> = Mutex::new(None);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
load_dll::load_tun_dll().unwrap();
|
||||
}
|
||||
tauri::Builder::default()
|
||||
.invoke_handler(tauri::generate_handler![default_value_name,default_value_device_id,connect,close,list])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn default_value_name() -> String {
|
||||
os_info::get().to_string()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn default_value_device_id() -> String {
|
||||
common::identifier::get_unique_identifier().unwrap_or(String::new())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn connect(config: ConnectConfig) -> Result<ConnectRegResponse, String> {
|
||||
let tap = config.tap;
|
||||
let token = config.token;
|
||||
let device_id = config.device_id;
|
||||
let name = config.name;
|
||||
let server_address_str = config.server_address;
|
||||
let server_address = match server_address_str.to_socket_addrs() {
|
||||
Ok(mut addr) => {
|
||||
if let Some(addr) = addr.next() {
|
||||
addr
|
||||
} else {
|
||||
return Err(String::from("server"));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(format!("server err:{}", e));
|
||||
}
|
||||
};
|
||||
let nat_test_server = config.nat_test_server.split(&[',', ' '][..]).flat_map(|a| a.to_socket_addrs()).flatten()
|
||||
.collect::<Vec<_>>();
|
||||
let in_ips = config.in_ips.split(" ").into_iter().filter(|e| !e.is_empty()).map(|e| e.to_string()).collect();
|
||||
let in_ips = match ips_parse(&in_ips) {
|
||||
Ok(in_ips) => { in_ips }
|
||||
Err(e) => {
|
||||
return Err(format!("inIps err:{}", e));
|
||||
}
|
||||
};
|
||||
let out_ips = config.out_ips.split(" ").into_iter().filter(|e| !e.is_empty()).map(|e| e.to_string()).collect();
|
||||
let out_ips = match ips_parse(&out_ips) {
|
||||
Ok(out_ips) => { out_ips }
|
||||
Err(e) => {
|
||||
return Err(format!("inIps err:{}", e));
|
||||
}
|
||||
};
|
||||
let password = if config.key.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(config.key)
|
||||
};
|
||||
let simulate_multicast = config.simulate_multicast;
|
||||
let config = Config::new(tap, token, device_id, name, server_address, server_address_str,
|
||||
nat_test_server, in_ips, out_ips,
|
||||
password, simulate_multicast, None);
|
||||
|
||||
let mut switch_util = SwitchUtil::new(config).await.unwrap();
|
||||
let mut count = 0;
|
||||
let response = loop {
|
||||
match switch_util.connect().await {
|
||||
Ok(response) => {
|
||||
break response;
|
||||
}
|
||||
Err(e) => {
|
||||
match e {
|
||||
ReqEnum::TokenError => {
|
||||
return Err("token error".to_string());
|
||||
}
|
||||
ReqEnum::AddressExhausted => {
|
||||
return Err("address exhausted".to_string());
|
||||
}
|
||||
ReqEnum::Timeout => {
|
||||
count += 1;
|
||||
if count > 3 {
|
||||
return Err("connect timeout".to_string());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
ReqEnum::ServerError(str) => {
|
||||
return Err(format!("error:{}", str));
|
||||
}
|
||||
ReqEnum::Other(str) => {
|
||||
return Err(format!("error:{}", str));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
match switch_util.create_iface() {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
return Err(format!("create net interface error:{}", e));
|
||||
}
|
||||
}
|
||||
match switch_util.build().await {
|
||||
Ok(switch) => {
|
||||
let _ = SWITCH.lock().insert(switch);
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(format!("build switch error:{}", e));
|
||||
}
|
||||
}
|
||||
Ok(ConnectRegResponse {
|
||||
virtual_ip: response.virtual_ip,
|
||||
virtual_gateway: response.virtual_gateway,
|
||||
virtual_netmask: response.virtual_netmask,
|
||||
})
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn list() -> Vec<SwitchPeerItem> {
|
||||
let mut peer_list = Vec::new();
|
||||
let mut guard = SWITCH.lock();
|
||||
match &mut *guard {
|
||||
None => {}
|
||||
Some(switch) => {
|
||||
let mut list = switch.device_list();
|
||||
let current_device = switch.current_device();
|
||||
list.sort_unstable_by_key(|v| { (v.status, v.virtual_ip) });
|
||||
for x in list {
|
||||
let item = if let Some(route) = switch.route(&x.virtual_ip) {
|
||||
let connect = if route.is_p2p() {
|
||||
"p2p".to_string()
|
||||
} else if route.addr == current_device.connect_server {
|
||||
"server relay".to_string()
|
||||
} else {
|
||||
"client relay".to_string()
|
||||
};
|
||||
SwitchPeerItem {
|
||||
name: x.name,
|
||||
virtual_ip: x.virtual_ip,
|
||||
status: format!("{:?}", x.status),
|
||||
connect,
|
||||
rt: route.rt.to_string(),
|
||||
addr: route.addr.to_string(),
|
||||
}
|
||||
} else {
|
||||
SwitchPeerItem {
|
||||
name: x.name,
|
||||
virtual_ip: x.virtual_ip,
|
||||
status: format!("{:?}", x.status),
|
||||
connect: "".to_string(),
|
||||
rt: "".to_string(),
|
||||
addr: "".to_string(),
|
||||
}
|
||||
};
|
||||
peer_list.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
peer_list
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn close() {
|
||||
let switch = SWITCH.lock().take();
|
||||
match switch {
|
||||
None => {}
|
||||
Some(mut switch) => {
|
||||
switch.stop().unwrap();
|
||||
switch.wait_stop().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
|
||||
pub struct ConnectConfig {
|
||||
pub tap: bool,
|
||||
pub token: String,
|
||||
pub device_id: String,
|
||||
pub name: String,
|
||||
pub server_address: String,
|
||||
pub nat_test_server: String,
|
||||
pub in_ips: String,
|
||||
pub out_ips: String,
|
||||
pub key: String,
|
||||
pub simulate_multicast: bool,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
|
||||
pub struct ConnectRegResponse {
|
||||
pub virtual_ip: Ipv4Addr,
|
||||
pub virtual_gateway: Ipv4Addr,
|
||||
pub virtual_netmask: Ipv4Addr,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
|
||||
pub struct SwitchPeerItem {
|
||||
pub name: String,
|
||||
pub virtual_ip: Ipv4Addr,
|
||||
pub status: String,
|
||||
pub connect: String,
|
||||
pub rt: String,
|
||||
pub addr: String,
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
{
|
||||
"build": {
|
||||
"withGlobalTauri": true,
|
||||
"beforeBuildCommand": "",
|
||||
"beforeDevCommand": "",
|
||||
"devPath": "./ui",
|
||||
"distDir": "./ui"
|
||||
},
|
||||
"package": {
|
||||
"productName": "switch-desktop",
|
||||
"version": "1.1.0"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
"all": false
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"category": "DeveloperTool",
|
||||
"copyright": "",
|
||||
"deb": {
|
||||
"depends": []
|
||||
},
|
||||
"externalBin": [],
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/[email protected]",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
],
|
||||
"identifier": "top.wherewego.switch",
|
||||
"longDescription": "",
|
||||
"macOS": {
|
||||
"entitlements": null,
|
||||
"exceptionDomain": "",
|
||||
"frameworks": [],
|
||||
"providerShortName": null,
|
||||
"signingIdentity": null
|
||||
},
|
||||
"resources": [],
|
||||
"shortDescription": "",
|
||||
"targets": "all",
|
||||
"windows": {
|
||||
"certificateThumbprint": null,
|
||||
"digestAlgorithm": "sha256",
|
||||
"timestampUrl": ""
|
||||
}
|
||||
},
|
||||
"security": {
|
||||
"csp": null
|
||||
},
|
||||
"updater": {
|
||||
"active": false
|
||||
},
|
||||
"windows": [
|
||||
{
|
||||
"fullscreen": false,
|
||||
"height": 600,
|
||||
"resizable": true,
|
||||
"title": "Switch Desktop",
|
||||
"width": 892,
|
||||
"minWidth": 892,
|
||||
"minHeight": 600,
|
||||
"center": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,681 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Switch Desktop</title>
|
||||
<script src="js/jquery-3.7.0.min.js"></script>
|
||||
<style>
|
||||
html {
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
.add {
|
||||
position: relative;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
transition: color 1s;
|
||||
border: 1px solid #616161;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
margin: 14px;
|
||||
}
|
||||
|
||||
.add:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 42px;
|
||||
width: 16px;
|
||||
border-top: 80px solid #666;
|
||||
}
|
||||
|
||||
.add:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
height: 16px;
|
||||
left: 10px;
|
||||
top: 42px;
|
||||
border-left: 80px solid #666;
|
||||
}
|
||||
|
||||
.add:hover {
|
||||
border: 1px solid #4CAF50;
|
||||
}
|
||||
|
||||
.form-row, .form-row-save {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.form-row-save {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.form-row label {
|
||||
width: 140px;
|
||||
text-align: right;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.form-row input {
|
||||
flex: 1;
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
.form-row-save .reset, .save {
|
||||
width: 100px;
|
||||
height: 36px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.form-row-save .reset {
|
||||
margin-right: 10px;
|
||||
background-color: #a1a1a1;
|
||||
}
|
||||
|
||||
.form-row-save .save {
|
||||
margin-left: auto;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#add-div {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: none;
|
||||
|
||||
}
|
||||
|
||||
#add-div-in {
|
||||
padding: 30px;
|
||||
height: 410px;
|
||||
width: 580px;
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
margin: 50px auto;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.data-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 5px;
|
||||
border-bottom: 1px solid #b1b1b1;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
span {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-weight: bold;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 10px;
|
||||
border: 1px solid #666;
|
||||
border-radius: 5px;
|
||||
width: 260px;
|
||||
height: 120px;
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.item:hover {
|
||||
border: 1px solid #4CAF50;
|
||||
background-color: #e1e1e1;
|
||||
}
|
||||
|
||||
.list {
|
||||
min-width: 880px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.left-float {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.menu {
|
||||
position: absolute;
|
||||
display: none;
|
||||
border: 1px solid #f1f1f1;
|
||||
background-color: #fff;
|
||||
box-shadow: 1px 1px 10px 5px rgba(0, 0, 0, 0.2);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.menu-items {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 90px;
|
||||
background-color: #fff;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.menu-items li::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 10px;
|
||||
width: 70px;
|
||||
height: 1px;
|
||||
background-color: #e1e1e1;
|
||||
}
|
||||
|
||||
.menu-items li {
|
||||
position: relative;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.menu-items li:hover {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
#connect-body {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #f1f1f1;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#connect-body .top {
|
||||
position: relative;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
#connect-body .bottom {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.peer-item {
|
||||
padding: 10px;
|
||||
border: 1px solid #666;
|
||||
border-radius: 5px;
|
||||
width: 260px;
|
||||
height: 120px;
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.peer-item > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 5px;
|
||||
border-bottom: 1px solid #b1b1b1;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/*#connect-body .bottom span {*/
|
||||
/* width: 140px;*/
|
||||
/*}*/
|
||||
|
||||
.connect-ip {
|
||||
color: #4CAF50;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.connect-close {
|
||||
text-align: center;
|
||||
width: 80px;
|
||||
height: 30px;
|
||||
font-size: 20px;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
background-color: chocolate;
|
||||
color: floralwhite;
|
||||
}
|
||||
|
||||
#alert-box {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: #ffffff;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
transition: opacity 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#loader {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#loader div {
|
||||
position: relative;
|
||||
margin: 100px auto;
|
||||
border: 5px solid #f3f3f3;
|
||||
border-top: 5px solid #555;
|
||||
border-radius: 50%;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
/*display: inline-block;*/
|
||||
animation: spin 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.Online {
|
||||
border: 1px solid #4CAF50;
|
||||
background-color: #e1e1e1;
|
||||
}
|
||||
.Online .peer-ip{
|
||||
color: #4CAF50;
|
||||
}
|
||||
.Offline{
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.p2p {
|
||||
color: #4CAF50;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="list">
|
||||
<div id="list-in">
|
||||
</div>
|
||||
<div id="add-button" class="add left-float"></div>
|
||||
</div>
|
||||
<div id="add-div">
|
||||
<div id="add-div-in">
|
||||
<form id="add-form">
|
||||
<div class="form-row">
|
||||
<label for="token">Token:</label>
|
||||
<input type="text" id="token" name="token" required>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="name">Name:</label>
|
||||
<input type="text" id="name" name="name" required>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="deviceId">DeviceId:</label>
|
||||
<input type="text" id="deviceId" name="device-id" required>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" id="password" name="password">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="inIps">InIps:</label>
|
||||
<input type="text" id="inIps" name="inIps"
|
||||
placeholder="192.168.10.0/24,10.26.0.3 192.168.8.0/24,10.26.0.4">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="outIps">OutIps:</label>
|
||||
<input type="text" id="outIps" name="outIps"
|
||||
placeholder="192.168.10.0/24,192.168.1.10 192.168.8.0/24,192.168.1.10">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="server">ServerAddress:</label>
|
||||
<input type="text" id="server" name="server" required value="nat1.wherewego.top:29871">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="natServer">NatServerAddress:</label>
|
||||
<input type="text" id="natServer" name="natServer" required
|
||||
value="nat1.wherewego.top:35061,nat1.wherewego.top:35062,nat2.wherewego.top:35061,nat2.wherewego.top:35062">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="simulateMulticast">SimulateMulticast:</label>
|
||||
<select id="simulateMulticast" name="simulateMulticast">
|
||||
<option value="false">false</option>
|
||||
<option value="true">true</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="tunTap">Tun/Tap:</label>
|
||||
<select id="tunTap" name="tunTap">
|
||||
<option value="tun">tun</option>
|
||||
<option value="tap">tap</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row-save">
|
||||
<input class="reset" type="reset" value="cancel">
|
||||
<input class="save" type="submit" value="save">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div id="context-menu" class="menu">
|
||||
<ul class="menu-items">
|
||||
<li id="menu-connect">connect</li>
|
||||
<li id="menu-edit">edit</li>
|
||||
<li id="menu-remove">remove</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="connect-body">
|
||||
<div class="top">
|
||||
<div>
|
||||
<label class="label">Server:</label>
|
||||
<span class="connect-server"></span>
|
||||
</div>
|
||||
<div>
|
||||
<label class="label">Token:</label>
|
||||
<span class="connect-token"></span>
|
||||
</div>
|
||||
<div>
|
||||
<label class="label">Name:</label>
|
||||
<span class="connect-name"></span>
|
||||
</div>
|
||||
<div>
|
||||
<label class="label">IP:</label>
|
||||
<span class="connect-ip"></span>
|
||||
<label class="label">Gateway:</label>
|
||||
<span class="connect-gateway"></span>
|
||||
<label class="label">Netmask:</label>
|
||||
<span class="connect-netmask"></span>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="connect-close">
|
||||
close
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div id="alert-box" class="hidden">
|
||||
<span id="alert-message"></span>
|
||||
</div>
|
||||
<div id="loader">
|
||||
<div></div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
const invoke = window.__TAURI__.invoke
|
||||
let nameDefault;
|
||||
let deviceIdDefault;
|
||||
invoke('default_value_name').then((name) => {
|
||||
$('#name').val(name);
|
||||
nameDefault = name;
|
||||
});
|
||||
invoke('default_value_device_id').then((device_id) => {
|
||||
$('#deviceId').val(device_id);
|
||||
deviceIdDefault = device_id;
|
||||
});
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
let configList = [];
|
||||
$('html').contextmenu(function (event) {
|
||||
event.preventDefault();
|
||||
})
|
||||
$('html').click(function () {
|
||||
$('#context-menu').css('display', 'none');
|
||||
})
|
||||
$('#connect-body .connect-close').click(function () {
|
||||
invoke('close').then(() => {
|
||||
stopFlushPeerList();
|
||||
$('#connect-body').css('display', 'none');
|
||||
});
|
||||
})
|
||||
let showAlertTimeout;
|
||||
|
||||
function showAlert(message, duration) {
|
||||
if (showAlertTimeout != null) {
|
||||
clearTimeout(showAlertTimeout);
|
||||
}
|
||||
$('#alert-message').text(message);
|
||||
$('#alert-box').removeClass('hidden');
|
||||
showAlertTimeout = setTimeout(function () {
|
||||
$('#alert-box').addClass('hidden');
|
||||
}, duration);
|
||||
}
|
||||
|
||||
function loadPeerList() {
|
||||
invoke('list').then((res) => {
|
||||
let html = '';
|
||||
for (let index = 0; index < res.length; index++) {
|
||||
let item = res[index];
|
||||
html += ' <div class="peer-item left-float ' + item.status + '">\n' +
|
||||
' <div>\n' +
|
||||
' <label class="label">name:</label>\n' +
|
||||
' <span>' + item.name + '</span>\n' +
|
||||
' </div>\n' +
|
||||
' <div>\n' +
|
||||
' <label class="label">ip:</label>\n' +
|
||||
' <span class="peer-ip">' + item.virtual_ip + '</span>\n' +
|
||||
' </div>\n' +
|
||||
' <div>\n' +
|
||||
' <label class="label">status:</label>\n' +
|
||||
' <span>' + item.status + '</span>\n' +
|
||||
' </div>\n' +
|
||||
' <div>\n' +
|
||||
' <label class="label">rt:</label>\n' +
|
||||
' <span>' + item.rt + '</span>\n' +
|
||||
' </div>\n' +
|
||||
' <div>\n' +
|
||||
' <label class="label">connect type:</label>\n' +
|
||||
' <span class="' + item.connect + '">' + item.connect + '</span>\n' +
|
||||
' </div>\n' +
|
||||
' </div>'
|
||||
}
|
||||
$('#connect-body .bottom').html(html);
|
||||
})
|
||||
}
|
||||
|
||||
let flushPeerListTimeout;
|
||||
|
||||
function flushPeerList(duration) {
|
||||
stopFlushPeerList();
|
||||
flushPeerListTimeout = setInterval(function () {
|
||||
loadPeerList();
|
||||
}, duration);
|
||||
}
|
||||
|
||||
function stopFlushPeerList() {
|
||||
if (flushPeerListTimeout != null) {
|
||||
clearInterval(flushPeerListTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
let listLoad = function () {
|
||||
let html = '';
|
||||
for (let index = 0; index < configList.length; index++) {
|
||||
let item = configList[index];
|
||||
html += ' <div class="item left-float" index="' + index + '" title="connect">' +
|
||||
' <div>' +
|
||||
' <div class="data-item">' +
|
||||
' <label class="label">Name:</label>' +
|
||||
' <span>' + item.name + '</span>' +
|
||||
' </div>' +
|
||||
' <div class="data-item">' +
|
||||
' <label class="label">Server:</label>' +
|
||||
' <span>' + item.server_address + '</span>' +
|
||||
' </div>' +
|
||||
' <div class="data-item">' +
|
||||
' <label class="label">Token:</label>' +
|
||||
' <span>' + item.token + '</span>' +
|
||||
' </div>' +
|
||||
// ' <div class="connect">···</div>' +
|
||||
' </div>' +
|
||||
' </div>'
|
||||
}
|
||||
$('#list-in').html(html);
|
||||
$('#list-in .item').mousedown(function (e) {
|
||||
if (3 == e.which) {
|
||||
let posX = e.clientX;
|
||||
let posY = e.clientY;
|
||||
$('#context-menu').attr('index', $(this).attr('index'));
|
||||
$('#context-menu').css('left', posX + 'px');
|
||||
$('#context-menu').css('top', posY + 'px');
|
||||
$('#context-menu').css('display', 'block');
|
||||
}
|
||||
})
|
||||
$('#list-in .item').click(function () {
|
||||
let index = parseInt($(this).attr('index'));
|
||||
let config = configList[index];
|
||||
connect(config);
|
||||
})
|
||||
}
|
||||
let connect = function (config) {
|
||||
$('#loader').css('display', 'block');
|
||||
invoke('connect', {'config': config}).then((res) => {
|
||||
$('#connect-body .connect-server').html(config.server_address);
|
||||
$('#connect-body .connect-token').html(config.token);
|
||||
$('#connect-body .connect-name').html(config.name);
|
||||
$('#connect-body .connect-ip').html(res.virtual_ip);
|
||||
$('#connect-body .connect-gateway').html(res.virtual_gateway);
|
||||
$('#connect-body .connect-netmask').html(res.virtual_netmask);
|
||||
$('#connect-body').css('display', 'block');
|
||||
$('#loader').css('display', 'none');
|
||||
loadPeerList();
|
||||
flushPeerList(2000);
|
||||
}).catch((error) => {
|
||||
$('#loader').css('display', 'none');
|
||||
showAlert(error, 2000);
|
||||
})
|
||||
}
|
||||
$('#menu-connect').click(function () {
|
||||
let index = parseInt($('#context-menu').attr('index'));
|
||||
let config = configList[index];
|
||||
connect(config);
|
||||
})
|
||||
$('#menu-remove').click(function () {
|
||||
let index = parseInt($('#context-menu').attr('index'));
|
||||
configList.splice(index, 1);
|
||||
localStorage.setItem('configList', JSON.stringify(configList));
|
||||
listLoad();
|
||||
})
|
||||
let editIndex = -1;
|
||||
$('#menu-edit').click(function () {
|
||||
let index = parseInt($('#context-menu').attr('index'));
|
||||
editIndex = index;
|
||||
$('#index').val(index);
|
||||
$('#name').val(configList[index].name);
|
||||
$('#deviceId').val(configList[index].device_id);
|
||||
$('#token').val(configList[index].token);
|
||||
$('#password').val(configList[index].key);
|
||||
$('#inIps').val(configList[index].in_ips);
|
||||
$('#inIps').val(configList[index].out_ips);
|
||||
$('#server').val(configList[index].server_address);
|
||||
$('#natServer').val(configList[index].nat_test_server);
|
||||
if (configList[index].simulate_multicast) {
|
||||
$('#simulateMulticast').val('true');
|
||||
} else {
|
||||
$('#simulateMulticast').val('false');
|
||||
}
|
||||
if (configList[index].tap) {
|
||||
$('#tunTap').val('tap');
|
||||
} else {
|
||||
$('#tunTap').val('tun');
|
||||
}
|
||||
$('#add-div').css('display', 'block');
|
||||
})
|
||||
let tmp = localStorage.getItem('configList');
|
||||
if (tmp != null) {
|
||||
configList = JSON.parse(tmp);
|
||||
listLoad();
|
||||
}
|
||||
|
||||
$('#add-button').click(function () {
|
||||
editIndex = -1;
|
||||
$('#name').val(nameDefault);
|
||||
$('#deviceId').val(deviceIdDefault);
|
||||
$('#add-div').css('display', 'block');
|
||||
})
|
||||
$('.reset').click(function () {
|
||||
$('#add-div').css('display', 'none');
|
||||
})
|
||||
$('#add-form').submit(function () {
|
||||
let token = $('#token').val();
|
||||
let name = $('#name').val();
|
||||
let deviceId = $('#deviceId').val();
|
||||
let password = $('#password').val();
|
||||
let inIps = $('#inIps').val();
|
||||
let outIps = $('#outIps').val();
|
||||
let server = $('#server').val();
|
||||
let natServer = $('#natServer').val();
|
||||
let simulateMulticast = $('#simulateMulticast').val();
|
||||
let tunTap = $('#tunTap').val();
|
||||
let config = {
|
||||
'token': token,
|
||||
'name': name,
|
||||
'device_id': deviceId,
|
||||
'in_ips': inIps,
|
||||
'out_ips': outIps,
|
||||
'server_address': server,
|
||||
'nat_test_server': natServer,
|
||||
'key': password,
|
||||
'simulate_multicast': 'true' === simulateMulticast,
|
||||
'tap': 'tap' === tunTap,
|
||||
'time': Date.now()
|
||||
}
|
||||
if (editIndex===-1) {
|
||||
let isPush = true;
|
||||
for (let index = 0; index < configList.length; index++) {
|
||||
let item = configList[index];
|
||||
if (item.token == token && item.server_address == server) {
|
||||
configList[index] = config;
|
||||
isPush = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isPush) {
|
||||
configList.push(config);
|
||||
}
|
||||
} else {
|
||||
configList[editIndex] = config;
|
||||
}
|
||||
|
||||
configList.sort(function (a, b) {
|
||||
return b.time - a.time;
|
||||
});
|
||||
listLoad();
|
||||
localStorage.setItem('configList', JSON.stringify(configList));
|
||||
$('#add-form')[0].reset();
|
||||
$('#add-div').css('display', 'none');
|
||||
return false;
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,2 +0,0 @@
|
||||
pub mod switch_util;
|
||||
pub mod switch;
|
||||
@@ -1,212 +0,0 @@
|
||||
use std::io;
|
||||
use std::net::{Ipv4Addr, SocketAddr};
|
||||
use std::time::{Duration, Instant};
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
|
||||
use protobuf::Message;
|
||||
use tokio::net::UdpSocket;
|
||||
use crate::channel::sender::ChannelSender;
|
||||
use crate::handle::PeerDeviceInfo;
|
||||
|
||||
use crate::proto::message::{RegistrationRequest, RegistrationResponse};
|
||||
use crate::protocol::error_packet::InErrorPacket;
|
||||
use crate::protocol::{service_packet, NetPacket, Protocol, Version, MAX_TTL};
|
||||
|
||||
pub enum ReqEnum {
|
||||
TokenError,
|
||||
AddressExhausted,
|
||||
Timeout,
|
||||
ServerError(String),
|
||||
Other(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RegResponse {
|
||||
pub virtual_ip: Ipv4Addr,
|
||||
pub virtual_gateway: Ipv4Addr,
|
||||
pub virtual_netmask: Ipv4Addr,
|
||||
pub epoch: u16,
|
||||
pub device_info_list: Vec<PeerDeviceInfo>,
|
||||
pub public_ip: Ipv4Addr,
|
||||
pub public_port: u16,
|
||||
}
|
||||
|
||||
///向中继服务器注册,token标识一个虚拟网关,device_id防止多次注册时得到的ip不一致
|
||||
pub async fn registration(
|
||||
main_channel: &UdpSocket,
|
||||
server_address: SocketAddr,
|
||||
token: String,
|
||||
device_id: String,
|
||||
name: String,
|
||||
) -> Result<RegResponse, ReqEnum> {
|
||||
let request_packet =
|
||||
registration_request_packet(token.clone(), device_id.clone(), name.clone(), false).unwrap();
|
||||
let buf = request_packet.buffer();
|
||||
let mut recv_buf = [0u8; 10240];
|
||||
return match main_channel.send_to(buf, server_address).await {
|
||||
Ok(_) => {
|
||||
match tokio::time::timeout(Duration::from_millis(300), main_channel.recv_from(&mut recv_buf)).await {
|
||||
Ok(rs) => {
|
||||
match rs {
|
||||
Ok((len, addr)) => {
|
||||
if server_address == addr {
|
||||
let net_packet = match NetPacket::new(&recv_buf[..len]) {
|
||||
Ok(net_packet) => {
|
||||
net_packet
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(ReqEnum::ServerError(format!("{}",e)))
|
||||
}
|
||||
};
|
||||
match net_packet.protocol() {
|
||||
Protocol::Service => {
|
||||
match service_packet::Protocol::from(net_packet.transport_protocol()) {
|
||||
service_packet::Protocol::RegistrationResponse => {
|
||||
match RegistrationResponse::parse_from_bytes(net_packet.payload()) {
|
||||
Ok(response) => {
|
||||
let device_info_list: Vec<PeerDeviceInfo> = response
|
||||
.device_info_list
|
||||
.into_iter()
|
||||
.map(|info| {
|
||||
PeerDeviceInfo::new(
|
||||
Ipv4Addr::from(info.virtual_ip),
|
||||
info.name,
|
||||
info.device_status as u8,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
Ok(RegResponse {
|
||||
virtual_ip: Ipv4Addr::from(response.virtual_ip),
|
||||
virtual_gateway: Ipv4Addr::from(response.virtual_gateway),
|
||||
virtual_netmask: Ipv4Addr::from(response.virtual_netmask),
|
||||
epoch: response.epoch as u16,
|
||||
device_info_list,
|
||||
public_ip: Ipv4Addr::from(response.public_ip),
|
||||
public_port: response.public_port as u16,
|
||||
})
|
||||
}
|
||||
Err(_) => {
|
||||
Err(ReqEnum::ServerError("invalid data".to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
Err(ReqEnum::ServerError("invalid data".to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
Protocol::Error => {
|
||||
match InErrorPacket::new(net_packet.transport_protocol(), net_packet.payload()) {
|
||||
Ok(e) => match e {
|
||||
InErrorPacket::TokenError => Err(ReqEnum::TokenError),
|
||||
InErrorPacket::Disconnect => {
|
||||
Err(ReqEnum::ServerError("disconnect".to_string()))
|
||||
}
|
||||
InErrorPacket::AddressExhausted => {
|
||||
Err(ReqEnum::AddressExhausted)
|
||||
}
|
||||
InErrorPacket::OtherError(e) => match e.message() {
|
||||
Ok(str) => {
|
||||
Err(ReqEnum::ServerError(str))
|
||||
}
|
||||
Err(e) => Err(ReqEnum::Other(format!("{}", e))),
|
||||
},
|
||||
},
|
||||
Err(e) => Err(ReqEnum::Other(format!("{}", e))),
|
||||
}
|
||||
}
|
||||
_ => Err(ReqEnum::ServerError("invalid data".to_string())),
|
||||
}
|
||||
} else {
|
||||
Err(ReqEnum::Other(format!("invalid data,from {}", addr)))
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
Err(ReqEnum::Other(format!("receiver error:{}", e)))
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
Err(ReqEnum::Timeout)
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
Err(ReqEnum::Other(format!("send error:{}", e)))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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;
|
||||
request.version = "1.1.0".to_string();
|
||||
let bytes = request.write_to_bytes()?;
|
||||
let buf = vec![0u8; 12 + bytes.len()];
|
||||
let mut net_packet = NetPacket::new(buf)?;
|
||||
net_packet.set_version(Version::V1);
|
||||
net_packet.set_protocol(Protocol::Service);
|
||||
net_packet.set_transport_protocol(service_packet::Protocol::RegistrationRequest.into());
|
||||
net_packet.first_set_ttl(MAX_TTL);
|
||||
net_packet.set_payload(&bytes);
|
||||
Ok(net_packet)
|
||||
}
|
||||
|
||||
pub struct Register {
|
||||
sender: ChannelSender,
|
||||
server_address: SocketAddr,
|
||||
token: String,
|
||||
device_id: String,
|
||||
name: String,
|
||||
time: AtomicCell<Instant>,
|
||||
}
|
||||
|
||||
impl Register {
|
||||
pub fn new(
|
||||
sender: ChannelSender,
|
||||
server_address: SocketAddr,
|
||||
token: String,
|
||||
device_id: String,
|
||||
name: String,
|
||||
) -> Self {
|
||||
Self {
|
||||
sender,
|
||||
server_address,
|
||||
token,
|
||||
device_id,
|
||||
name,
|
||||
time: AtomicCell::new(Instant::now()),
|
||||
}
|
||||
}
|
||||
pub async fn fast_register(&self) -> io::Result<()> {
|
||||
let last = self.time.load();
|
||||
if last.elapsed() < Duration::from_secs(2)
|
||||
|| self
|
||||
.time
|
||||
.compare_exchange(last, Instant::now())
|
||||
.is_err()
|
||||
{
|
||||
//短时间不重复注册
|
||||
return Ok(());
|
||||
}
|
||||
log::info!("重新连接");
|
||||
let request_packet = registration_request_packet(
|
||||
self.token.clone(),
|
||||
self.device_id.clone(),
|
||||
self.name.clone(),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
let buf = request_packet.buffer();
|
||||
self.sender.send_main(buf, self.server_address).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
[package]
|
||||
name = "switch-cmd"
|
||||
version = "1.1.0"
|
||||
name = "vnt-cli"
|
||||
version = "1.1.1"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
switch = {path="../switch"}
|
||||
vnt = {path="../vnt"}
|
||||
common = {path="../common"}
|
||||
tokio = { version = "1.28.1", features = ["full"] }
|
||||
getopts = "0.2.21"
|
||||
@@ -16,6 +16,11 @@ dirs = "4.0.0"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0.94"
|
||||
log = "0.4.17"
|
||||
[dependencies.uuid]
|
||||
version = "1.4.1"
|
||||
features = [
|
||||
"v4", # Lets you generate random UUIDs
|
||||
]
|
||||
|
||||
[target.'cfg(any(target_os = "linux",target_os = "macos"))'.dependencies]
|
||||
sudo = "0.6.0"
|
||||
@@ -24,4 +29,7 @@ sudo = "0.6.0"
|
||||
winapi = { version = "0.3.9", features = ["handleapi", "processthreadsapi", "winnt", "securitybaseapi", "impl-default"] }
|
||||
[features]
|
||||
default = []
|
||||
mini = []
|
||||
mini = []
|
||||
|
||||
[build-dependencies]
|
||||
embed-manifest = "1.4.0"
|
||||
@@ -0,0 +1,10 @@
|
||||
// use embed_manifest::{embed_manifest, new_manifest};
|
||||
// use embed_manifest::manifest::ExecutionLevel;
|
||||
|
||||
fn main() {
|
||||
////强制用管理员运行貌似体验更差了
|
||||
// if std::env::var_os("CARGO_CFG_WINDOWS").is_some() {
|
||||
// embed_manifest(new_manifest("vnt")
|
||||
// .requested_execution_level(ExecutionLevel::RequireAdministrator)).expect("unable to embed manifest file");
|
||||
// }
|
||||
}
|
||||
@@ -11,7 +11,7 @@ pub struct CommandClient {
|
||||
|
||||
impl CommandClient {
|
||||
pub fn new() -> io::Result<Self> {
|
||||
let path_buf = dirs::home_dir().unwrap().join(".switch_desktop").join("command-port");
|
||||
let path_buf = crate::app_home()?.join("command-port");
|
||||
if !path_buf.exists() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "not started"));
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::io;
|
||||
use switch::core::Switch;
|
||||
use vnt::core::Vnt;
|
||||
use crate::command::entity::{DeviceItem, RouteItem, Info};
|
||||
use crate::console_out;
|
||||
|
||||
@@ -47,11 +47,11 @@ fn command_(cmd: CommandEnum) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn command_route(switch: &Switch) -> Vec<RouteItem> {
|
||||
let route_table = switch.route_table();
|
||||
pub fn command_route(vnt: &Vnt) -> Vec<RouteItem> {
|
||||
let route_table = vnt.route_table();
|
||||
let mut route_list = Vec::with_capacity(route_table.len());
|
||||
for (destination, route) in route_table {
|
||||
let next_hop = switch.route_key(&route.route_key()).map_or(String::new(), |v| v.to_string());
|
||||
let next_hop = vnt.route_key(&route.route_key()).map_or(String::new(), |v| v.to_string());
|
||||
let metric = route.metric.to_string();
|
||||
let rt = if route.rt < 0 {
|
||||
"".to_string()
|
||||
@@ -71,13 +71,13 @@ pub fn command_route(switch: &Switch) -> Vec<RouteItem> {
|
||||
route_list
|
||||
}
|
||||
|
||||
pub fn command_list(switch: &Switch) -> Vec<DeviceItem> {
|
||||
let device_list = switch.device_list();
|
||||
pub fn command_list(vnt: &Vnt) -> Vec<DeviceItem> {
|
||||
let device_list = vnt.device_list();
|
||||
let mut list = Vec::new();
|
||||
for peer in device_list {
|
||||
let name = peer.name;
|
||||
let virtual_ip = peer.virtual_ip.to_string();
|
||||
let (nat_type, public_ips, local_ip) = if let Some(nat_info) = switch.peer_nat_info(&peer.virtual_ip) {
|
||||
let (nat_type, public_ips, local_ip) = if let Some(nat_info) = vnt.peer_nat_info(&peer.virtual_ip) {
|
||||
let nat_type = format!("{:?}", nat_info.nat_type);
|
||||
let public_ips: Vec<String> = nat_info.public_ips.iter().map(|v| v.to_string()).collect();
|
||||
let public_ips = public_ips.join(",");
|
||||
@@ -86,7 +86,7 @@ pub fn command_list(switch: &Switch) -> Vec<DeviceItem> {
|
||||
} 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, rt) = if let Some(route) = vnt.route(&peer.virtual_ip) {
|
||||
let nat_traversal_type = if route.metric == 1 { "p2p" } else { "relay" }.to_string();
|
||||
let rt = if route.rt < 0 {
|
||||
"".to_string()
|
||||
@@ -113,14 +113,14 @@ pub fn command_list(switch: &Switch) -> Vec<DeviceItem> {
|
||||
list
|
||||
}
|
||||
|
||||
pub fn command_info(switch: &Switch) -> Info {
|
||||
let current_device = switch.current_device();
|
||||
let nat_info = switch.nat_info();
|
||||
let name = switch.name().to_string();
|
||||
pub fn command_info(vnt: &Vnt) -> Info {
|
||||
let current_device = vnt.current_device();
|
||||
let nat_info = vnt.nat_info();
|
||||
let name = vnt.name().to_string();
|
||||
let virtual_ip = current_device.virtual_ip().to_string();
|
||||
let virtual_gateway = current_device.virtual_gateway().to_string();
|
||||
let virtual_netmask = current_device.virtual_netmask.to_string();
|
||||
let connect_status = format!("{:?}", switch.connection_status());
|
||||
let connect_status = format!("{:?}", vnt.connection_status());
|
||||
let relay_server = current_device.connect_server.to_string();
|
||||
let nat_type = format!("{:?}", nat_info.nat_type);
|
||||
let public_ips: Vec<String> = nat_info.public_ips.iter().map(|v| v.to_string()).collect();
|
||||
@@ -2,7 +2,7 @@ use std::io;
|
||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
use switch::core::Switch;
|
||||
use vnt::core::Vnt;
|
||||
|
||||
|
||||
pub struct CommandServer {}
|
||||
@@ -14,7 +14,7 @@ impl CommandServer {
|
||||
}
|
||||
|
||||
impl CommandServer {
|
||||
pub async fn start(self, switch: Switch) -> io::Result<()> {
|
||||
pub async fn start(self, vnt: Vnt) -> io::Result<()> {
|
||||
let mut port = 21637 as u16;
|
||||
let udp = loop {
|
||||
match UdpSocket::bind(SocketAddr::V4(SocketAddrV4::new(
|
||||
@@ -34,20 +34,17 @@ impl CommandServer {
|
||||
}
|
||||
}
|
||||
};
|
||||
let path_buf = dirs::home_dir().unwrap().join(".switch_desktop").join("command-port");
|
||||
if !path_buf.parent().unwrap().exists() {
|
||||
std::fs::create_dir_all(path_buf.parent().unwrap())?;
|
||||
}
|
||||
std::fs::write(path_buf,udp.local_addr()?.port().to_string())?;
|
||||
let path_buf = crate::app_home()?.join("command-port");
|
||||
std::fs::write(path_buf, udp.local_addr()?.port().to_string())?;
|
||||
let mut buf = [0u8; 64];
|
||||
loop {
|
||||
let (len, addr) = udp.recv_from(&mut buf).await?;
|
||||
match std::str::from_utf8(&buf[..len]) {
|
||||
Ok(cmd) => {
|
||||
if let Ok(out) = command(cmd, &switch) {
|
||||
if let Ok(out) = command(cmd, &vnt) {
|
||||
let _ = udp.send_to(out.as_bytes(), addr).await;
|
||||
if "stopped" == &out {
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,10 +58,10 @@ impl CommandServer {
|
||||
}
|
||||
|
||||
|
||||
fn command(cmd: &str, switch: &Switch) -> io::Result<String> {
|
||||
fn command(cmd: &str, vnt: &Vnt) -> io::Result<String> {
|
||||
let out_str = match cmd {
|
||||
"route" => {
|
||||
match serde_json::to_string(&crate::command::command_route(switch)) {
|
||||
match serde_json::to_string(&crate::command::command_route(vnt)) {
|
||||
Ok(str) => {
|
||||
str
|
||||
}
|
||||
@@ -74,7 +71,7 @@ fn command(cmd: &str, switch: &Switch) -> io::Result<String> {
|
||||
}
|
||||
}
|
||||
"list" => {
|
||||
match serde_json::to_string(&crate::command::command_list(switch)) {
|
||||
match serde_json::to_string(&crate::command::command_list(vnt)) {
|
||||
Ok(str) => {
|
||||
str
|
||||
}
|
||||
@@ -84,7 +81,7 @@ fn command(cmd: &str, switch: &Switch) -> io::Result<String> {
|
||||
}
|
||||
}
|
||||
"info" => {
|
||||
match serde_json::to_string(&crate::command::command_info(switch)) {
|
||||
match serde_json::to_string(&crate::command::command_info(vnt)) {
|
||||
Ok(str) => {
|
||||
str
|
||||
}
|
||||
@@ -94,7 +91,7 @@ fn command(cmd: &str, switch: &Switch) -> io::Result<String> {
|
||||
}
|
||||
}
|
||||
"stop" => {
|
||||
switch.stop()?;
|
||||
vnt.stop()?;
|
||||
"stopped".to_string()
|
||||
}
|
||||
_ => {
|
||||
@@ -1,36 +1,52 @@
|
||||
use std::net::ToSocketAddrs;
|
||||
use std::io;
|
||||
use std::net::{Ipv4Addr, ToSocketAddrs};
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
#[cfg(not(feature = "mini"))]
|
||||
use console::style;
|
||||
use getopts::Options;
|
||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||
use common::args_parse::ips_parse;
|
||||
use switch::core::{Config, SwitchUtil};
|
||||
use switch::handle::registration_handler::ReqEnum;
|
||||
use vnt::core::{Config, VntUtil};
|
||||
use vnt::handle::registration_handler::ReqEnum;
|
||||
|
||||
mod command;
|
||||
mod console_out;
|
||||
mod root_check;
|
||||
|
||||
pub fn app_home() -> io::Result<PathBuf> {
|
||||
let path = dirs::home_dir().ok_or(io::Error::new(io::ErrorKind::Other, "not home"))?.join(".vnt-cli");
|
||||
if !path.exists() {
|
||||
std::fs::create_dir_all(&path)?;
|
||||
}
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
main0().await;
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
async fn main0() {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
let program = args[0].clone();
|
||||
let mut opts = Options::new();
|
||||
opts.long_only(false);
|
||||
opts.optopt("k", "", &format!("{}", green("使用相同的token,就能组建一个局域网络".to_string())), "<token>");
|
||||
opts.optopt("n", "", "给设备一个名字", "<name>");
|
||||
opts.optopt("d", "", "设备唯一标识符,凭id分配ip", "<id>");
|
||||
#[cfg(not(feature = "mini"))]
|
||||
opts.optflag("c", "", "关闭交互式命令");
|
||||
opts.optopt("k", "", &format!("{}", green("必选,使用相同的token,就能组建一个局域网络".to_string())), "<token>");
|
||||
opts.optopt("n", "", "给设备一个名字,默认使用系统版本", "<name>");
|
||||
opts.optopt("d", "", "设备唯一标识符,不使用--ip参数时,服务端凭此参数分配ip", "<id>");
|
||||
opts.optflag("c", "", "关闭交互式命令,使用此参数禁用控制台输入");
|
||||
opts.optopt("s", "", "注册和中继服务器地址", "<server>");
|
||||
opts.optopt("e", "", "NAT探测服务器地址,使用逗号分隔", "<addr1,addr2>");
|
||||
opts.optflag("a", "", "使用tap模式,默认使用tun模式");
|
||||
opts.optmulti("i", "", "配置点对网(IP代理)时使用,-i 192.168.10.0/24,10.26.0.3,表示允许接收网段192.168.10.0/24的数据并转发到10.26.0.3", "<in-ip>");
|
||||
opts.optmulti("o", "", "配置点对网时使用,-o 192.168.10.0/24,192.168.1.10,表示允许目标为192.168.10.0/24的数据从网卡192.168.1.10转发出去", "<out-ip>");
|
||||
opts.optmulti("i", "", "配置点对网(IP代理)时使用,-i 192.168.0.0/24,10.26.0.3,表示允许接收网段192.168.0.0/24的数据并转发到10.26.0.3", "<in-ip>");
|
||||
opts.optmulti("o", "", "配置点对网时使用,-o 192.168.0.0/24,192.168.0.10,表示允许目标为192.168.0.0/24的数据从网卡192.168.0.10转发出去", "<out-ip>");
|
||||
opts.optopt("w", "", "使用该密码生成的密钥对客户端数据进行加密,并且服务端无法解密,使用相同密码的客户端才能通信", "<password>");
|
||||
opts.optflag("m", "", "模拟组播,默认情况下组播数据会被当作广播发送,开启后会模拟真实组播的数据发送");
|
||||
opts.optopt("u", "", "虚拟网卡mtu值", "<mtu>");
|
||||
opts.optflag("", "tcp", "和服务端使用tcp通信,默认使用udp,一般来说udp延迟和消耗更低");
|
||||
opts.optopt("", "ip", "指定虚拟ip,指定的ip不能和其他设备重复,必须有效并且在服务端所属网段下,默认情况由服务端分配", "<IP>");
|
||||
opts.optflag("", "relay", "仅使用服务器转发,不使用p2p,默认情况允许使用p2p");
|
||||
//"后台运行时,查看其他设备列表"
|
||||
opts.optflag("", "list", &format!("{}", yellow("后台运行时,查看其他设备列表".to_string())));
|
||||
opts.optflag("", "all", &format!("{}", yellow("后台运行时,查看其他设备完整信息".to_string())));
|
||||
@@ -52,6 +68,8 @@ async fn main() {
|
||||
}
|
||||
if !root_check::is_app_elevated() {
|
||||
println!("Please run it with administrator or root privileges");
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
sudo::escalate_if_needed().unwrap();
|
||||
return;
|
||||
}
|
||||
if matches.opt_present("list") {
|
||||
@@ -78,9 +96,19 @@ async fn main() {
|
||||
let tap = matches.opt_present("a");
|
||||
let token: String = matches.opt_get("k").unwrap().unwrap();
|
||||
let device_id = matches.opt_get_default("d", String::new()).unwrap();
|
||||
#[cfg(not(feature = "mini"))]
|
||||
let device_id = if device_id.is_empty() {
|
||||
common::identifier::get_unique_identifier().unwrap_or(String::new())
|
||||
let device_id = if device_id.is_empty() {
|
||||
if let Some(id) = common::identifier::get_unique_identifier() {
|
||||
id
|
||||
} else {
|
||||
let path_buf = app_home().unwrap().join("device-id");
|
||||
if let Ok(id) = std::fs::read_to_string(path_buf.as_path()) {
|
||||
id
|
||||
} else {
|
||||
let id = uuid::Uuid::new_v4().to_string();
|
||||
let _ = std::fs::write(path_buf, &id);
|
||||
id
|
||||
}
|
||||
}
|
||||
} else {
|
||||
device_id
|
||||
};
|
||||
@@ -91,7 +119,20 @@ async fn main() {
|
||||
}
|
||||
let name = matches.opt_get_default("n", os_info::get().to_string()).unwrap();
|
||||
let server_address_str = matches.opt_get_default("s", "nat1.wherewego.top:29871".to_string()).unwrap();
|
||||
let server_address = server_address_str.to_socket_addrs().unwrap().next().unwrap();
|
||||
let server_address = match server_address_str.to_socket_addrs() {
|
||||
Ok(mut addr) => {
|
||||
if let Some(addr) = addr.next() {
|
||||
addr
|
||||
} else {
|
||||
println!("parameter -s error .");
|
||||
return;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("parameter -s error {}.", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let nat_test_server = matches.opt_get_default("e",
|
||||
"nat1.wherewego.top:35061,nat1.wherewego.top:35062,nat2.wherewego.top:35061,nat2.wherewego.top:35062".to_string()).unwrap();
|
||||
|
||||
@@ -120,10 +161,7 @@ async fn main() {
|
||||
};
|
||||
let password: Option<String> = matches.opt_get("w").unwrap();
|
||||
let simulate_multicast = matches.opt_present("m");
|
||||
#[cfg(feature = "mini")]
|
||||
let unused_cmd = true;
|
||||
#[cfg(not(feature = "mini"))]
|
||||
let unused_cmd = matches.opt_present("c");
|
||||
let unused_cmd = matches.opt_present("c");
|
||||
let mtu: Option<String> = matches.opt_get("u").unwrap();
|
||||
let mtu = if let Some(mtu) = mtu {
|
||||
match u16::from_str(&mtu) {
|
||||
@@ -140,14 +178,24 @@ async fn main() {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let virtual_ip: Option<String> = matches.opt_get("ip").unwrap();
|
||||
let virtual_ip = virtual_ip.map(|v| Ipv4Addr::from_str(&v).expect("--ip error"));
|
||||
if let Some(virtual_ip) = virtual_ip {
|
||||
if virtual_ip.is_unspecified() || virtual_ip.is_broadcast() || virtual_ip.is_multicast() {
|
||||
println!("--ip invalid");
|
||||
return;
|
||||
}
|
||||
}
|
||||
let tcp_channel = matches.opt_present("tcp");
|
||||
let relay = matches.opt_present("relay");
|
||||
let config = Config::new(tap,
|
||||
token, device_id, name,
|
||||
server_address, server_address_str,
|
||||
nat_test_server, in_ip,
|
||||
out_ip, password, simulate_multicast, mtu);
|
||||
let mut switch_util = SwitchUtil::new(config).await.unwrap();
|
||||
out_ip, password, simulate_multicast, mtu, tcp_channel, virtual_ip, relay);
|
||||
let mut vnt_util = VntUtil::new(config).await.unwrap();
|
||||
let response = loop {
|
||||
match switch_util.connect().await {
|
||||
match vnt_util.connect().await {
|
||||
Ok(response) => {
|
||||
break response;
|
||||
}
|
||||
@@ -165,9 +213,17 @@ async fn main() {
|
||||
}
|
||||
ReqEnum::ServerError(str) => {
|
||||
println!("error:{}", str);
|
||||
continue;
|
||||
}
|
||||
ReqEnum::Other(str) => {
|
||||
println!("error:{}", str);
|
||||
continue;
|
||||
}
|
||||
ReqEnum::IpAlreadyExists => {
|
||||
println!("ip already exists");
|
||||
}
|
||||
ReqEnum::InvalidIp => {
|
||||
println!("invalid ip");
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -177,13 +233,13 @@ async fn main() {
|
||||
println!(" ====== Connect Successfully ====== ");
|
||||
println!("virtual_gateway:{}", response.virtual_gateway);
|
||||
println!("virtual_ip:{}", green(response.virtual_ip.to_string()));
|
||||
let driver_info = switch_util.create_iface().unwrap();
|
||||
let driver_info = vnt_util.create_iface().unwrap();
|
||||
println!(" ====== Create Network Interface Successfully ====== ");
|
||||
println!("name:{}", driver_info.name);
|
||||
println!("version:{}", driver_info.version);
|
||||
let mut switch = match switch_util.build().await {
|
||||
Ok(switch) => {
|
||||
switch
|
||||
let mut vnt = match vnt_util.build().await {
|
||||
Ok(vnt) => {
|
||||
vnt
|
||||
}
|
||||
Err(e) => {
|
||||
println!("error:{}", e);
|
||||
@@ -191,25 +247,21 @@ async fn main() {
|
||||
}
|
||||
};
|
||||
println!(" ====== Start Successfully ====== ");
|
||||
let switch_s = switch.clone();
|
||||
let vnt_c = vnt.clone();
|
||||
tokio::spawn(async {
|
||||
if let Err(e) = command::server::CommandServer::new().start(switch_s).await {
|
||||
if let Err(e) = command::server::CommandServer::new().start(vnt_c).await {
|
||||
println!("command error :{}", e);
|
||||
}
|
||||
});
|
||||
if unused_cmd {
|
||||
switch.wait_stop().await;
|
||||
} else {
|
||||
#[cfg(not(feature = "mini"))]
|
||||
{
|
||||
let stdin = tokio::io::stdin();
|
||||
let mut cmd = String::new();
|
||||
let mut reader = BufReader::new(stdin);
|
||||
loop {
|
||||
cmd.clear();
|
||||
println!("input:list,info,route,all,stop");
|
||||
tokio::select! {
|
||||
_ = switch.wait_stop()=>{
|
||||
if !unused_cmd {
|
||||
let stdin = tokio::io::stdin();
|
||||
let mut cmd = String::new();
|
||||
let mut reader = BufReader::new(stdin);
|
||||
loop {
|
||||
cmd.clear();
|
||||
println!("input:list,info,route,all,stop");
|
||||
tokio::select! {
|
||||
_ = vnt.wait_stop()=>{
|
||||
break;
|
||||
}
|
||||
rs = reader.read_line(&mut cmd)=>{
|
||||
@@ -220,23 +272,23 @@ async fn main() {
|
||||
}
|
||||
match cmd[..len].to_lowercase().trim() {
|
||||
"list" => {
|
||||
let list = command::command_list(&switch);
|
||||
let list = command::command_list(&vnt);
|
||||
console_out::console_device_list(list);
|
||||
}
|
||||
"info"=>{
|
||||
let info = command::command_info(&switch);
|
||||
let info = command::command_info(&vnt);
|
||||
console_out::console_info(info);
|
||||
}
|
||||
"route" =>{
|
||||
let route = command::command_route(&switch);
|
||||
let route = command::command_route(&vnt);
|
||||
console_out::console_route_table(route);
|
||||
}
|
||||
"all" =>{
|
||||
let list = command::command_list(&switch);
|
||||
let list = command::command_list(&vnt);
|
||||
console_out::console_device_list_all(list);
|
||||
}
|
||||
"stop" =>{
|
||||
let _ = switch.stop();
|
||||
let _ = vnt.stop();
|
||||
break;
|
||||
}
|
||||
_ => {
|
||||
@@ -251,16 +303,14 @@ async fn main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
switch.wait_stop().await;
|
||||
}
|
||||
}
|
||||
std::process::exit(0);
|
||||
vnt.wait_stop().await;
|
||||
}
|
||||
|
||||
fn print_usage(program: &str, opts: Options) {
|
||||
let brief = format!("Usage: {} [options]", program);
|
||||
println!("version:1.1.0");
|
||||
println!("version:1.1.1");
|
||||
println!("{}", opts.usage(&brief));
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
[package]
|
||||
name = "switch-jni"
|
||||
name = "vnt-jni"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
switch = {path="../switch"}
|
||||
vnt = {path="../vnt"}
|
||||
|
||||
jni = { version = "0.21.1", default-features = false }
|
||||
[lib]
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod vnt_util;
|
||||
pub mod vnt;
|
||||
@@ -3,39 +3,39 @@ use jni::errors::Error;
|
||||
use jni::JNIEnv;
|
||||
use jni::objects::{JClass, JObject, JValue};
|
||||
use jni::sys::{jboolean, jbyte, jint, jlong, jobject, jobjectArray, jsize};
|
||||
use switch::channel::Route;
|
||||
use switch::core::sync::SwitchSync;
|
||||
use switch::handle::PeerDeviceInfo;
|
||||
use vnt::channel::Route;
|
||||
use vnt::core::sync::VntSync;
|
||||
use vnt::handle::PeerDeviceInfo;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn Java_top_wherewego_switchjni_Switch_stop0(
|
||||
pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_Vnt_stop0(
|
||||
_env: JNIEnv,
|
||||
_class: JClass,
|
||||
raw_switch: jlong,
|
||||
raw_vnt: jlong,
|
||||
) {
|
||||
let switch = raw_switch as *mut SwitchSync;
|
||||
let _ = (&*switch).stop();
|
||||
let vnt = raw_vnt as *mut VntSync;
|
||||
let _ = (&*vnt).stop();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn Java_top_wherewego_switchjni_Switch_waitStop0(
|
||||
pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_Vnt_waitStop0(
|
||||
_env: JNIEnv,
|
||||
_class: JClass,
|
||||
raw_switch: jlong,
|
||||
raw_vnt: jlong,
|
||||
) {
|
||||
let switch = raw_switch as *mut SwitchSync;
|
||||
let _ = (&mut *switch).wait_stop();
|
||||
let vnt = raw_vnt as *mut VntSync;
|
||||
let _ = (&mut *vnt).wait_stop();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn Java_top_wherewego_switchjni_Switch_waitStopMs0(
|
||||
pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_Vnt_waitStopMs0(
|
||||
_env: JNIEnv,
|
||||
_class: JClass,
|
||||
raw_switch: jlong,
|
||||
raw_vnt: jlong,
|
||||
ms: jlong,
|
||||
) -> jboolean {
|
||||
let switch = raw_switch as *mut SwitchSync;
|
||||
if (&mut *switch).wait_stop_ms(ms as _) {
|
||||
let vnt = raw_vnt as *mut VntSync;
|
||||
if (&mut *vnt).wait_stop_ms(ms as _) {
|
||||
jni::sys::JNI_TRUE
|
||||
} else {
|
||||
jni::sys::JNI_FALSE
|
||||
@@ -43,28 +43,28 @@ pub unsafe extern "C" fn Java_top_wherewego_switchjni_Switch_waitStopMs0(
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn Java_top_wherewego_switchjni_Switch_drop0(
|
||||
pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_Vnt_drop0(
|
||||
_env: JNIEnv,
|
||||
_class: JClass,
|
||||
raw_switch: jlong,
|
||||
raw_vnt: jlong,
|
||||
) {
|
||||
let switch = raw_switch as *mut SwitchSync;
|
||||
let _ = Box::from_raw(switch).stop();
|
||||
let vnt = raw_vnt as *mut VntSync;
|
||||
let _ = Box::from_raw(vnt).stop();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn Java_top_wherewego_switchjni_Switch_list0(
|
||||
pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_Vnt_list0(
|
||||
mut env: JNIEnv,
|
||||
_class: JClass,
|
||||
raw_switch: jlong,
|
||||
raw_vnt: jlong,
|
||||
) -> jobjectArray {
|
||||
let switch = raw_switch as *mut SwitchSync;
|
||||
let switch = &mut *switch;
|
||||
let list = switch.device_list();
|
||||
let vnt = raw_vnt as *mut VntSync;
|
||||
let vnt = &mut *vnt;
|
||||
let list = vnt.device_list();
|
||||
|
||||
let arr = match env.new_object_array(
|
||||
list.len() as jsize,
|
||||
"top/wherewego/switchjni/PeerDeviceInfo",
|
||||
"top/wherewego/vnt/jni/PeerDeviceInfo",
|
||||
JObject::null(),
|
||||
) {
|
||||
Ok(arr) => { arr }
|
||||
@@ -75,7 +75,7 @@ pub unsafe extern "C" fn Java_top_wherewego_switchjni_Switch_list0(
|
||||
}
|
||||
};
|
||||
for (index, peer) in list.into_iter().enumerate() {
|
||||
let route = if let Some(route) = switch.route(&peer.virtual_ip) {
|
||||
let route = if let Some(route) = vnt.route(&peer.virtual_ip) {
|
||||
match route_parse(&mut env, route) {
|
||||
Ok(route) => {
|
||||
JObject::from_raw(route)
|
||||
@@ -113,7 +113,7 @@ fn route_parse(env: &mut JNIEnv, route: Route) -> Result<jobject, Error> {
|
||||
let metric = route.metric;
|
||||
let rt = route.rt;
|
||||
let rs = env.new_object(
|
||||
"top/wherewego/switchjni/Route",
|
||||
"top/wherewego/vnt/jni/Route",
|
||||
"(Ljava/lang/String;BI)V",
|
||||
&[JValue::Object(&env.new_string(address)?.into()),
|
||||
JValue::Byte(metric as jbyte),
|
||||
@@ -127,8 +127,8 @@ fn peer_device_info_parse(env: &mut JNIEnv, peer: PeerDeviceInfo, route: JObject
|
||||
let name = peer.name.to_string();
|
||||
let status = format!("{:?}", peer.status);
|
||||
let rs = env.new_object(
|
||||
"top/wherewego/switchjni/PeerDeviceInfo",
|
||||
"(ILjava/lang/String;Ljava/lang/String;Ltop/wherewego/switchjni/Route;)V",
|
||||
"top/wherewego/vnt/jni/PeerDeviceInfo",
|
||||
"(ILjava/lang/String;Ljava/lang/String;Ltop/wherewego/vnt/jni/Route;)V",
|
||||
&[JValue::Int(virtual_ip as jint),
|
||||
JValue::Object(&env.new_string(name)?.into()),
|
||||
JValue::Object(&env.new_string(status)?.into()),
|
||||
@@ -7,11 +7,11 @@ use jni::objects::{JClass, JObject, JString, JValue};
|
||||
use jni::sys::jboolean;
|
||||
use jni::sys::{jint, jlong, jobject};
|
||||
use jni::JNIEnv;
|
||||
use switch::core::Config;
|
||||
use switch::core::sync::SwitchUtilSync;
|
||||
use switch::handle::registration_handler::{RegResponse, ReqEnum};
|
||||
use vnt::core::Config;
|
||||
use vnt::core::sync::VntUtilSync;
|
||||
use vnt::handle::registration_handler::{RegResponse, ReqEnum};
|
||||
#[cfg(not(target_os = "android"))]
|
||||
use switch::tun_tap_device::DriverInfo;
|
||||
use vnt::tun_tap_device::DriverInfo;
|
||||
|
||||
fn to_string_not_null(env: &mut JNIEnv, config: &JObject, name: &'static str) -> Result<String, Error> {
|
||||
let value = env.get_field(config, name, "Ljava/lang/String;")?.l()?;
|
||||
@@ -49,7 +49,7 @@ fn to_string(env: &mut JNIEnv, config: &JObject, name: &str) -> Result<Option<St
|
||||
}
|
||||
}
|
||||
|
||||
fn new_sync(env: &mut JNIEnv, config: JObject) -> Result<SwitchUtilSync, Error> {
|
||||
fn new_sync(env: &mut JNIEnv, config: JObject) -> Result<VntUtilSync, Error> {
|
||||
let token = to_string_not_null(env, &config, "token")?;
|
||||
let name = to_string_not_null(env, &config, "name")?;
|
||||
let device_id = to_string_not_null(env, &config, "deviceId")?;
|
||||
@@ -78,13 +78,13 @@ fn new_sync(env: &mut JNIEnv, config: JObject) -> Result<SwitchUtilSync, Error>
|
||||
token, device_id, name,
|
||||
server_address, server_address_str,
|
||||
nat_test_server, vec![],
|
||||
vec![], password, false, None);
|
||||
match SwitchUtilSync::new(config) {
|
||||
Ok(switch_util) => {
|
||||
Ok(switch_util)
|
||||
vec![], password, false, None, false, None, false);
|
||||
match VntUtilSync::new(config) {
|
||||
Ok(vnt_util) => {
|
||||
Ok(vnt_util)
|
||||
}
|
||||
Err(e) => {
|
||||
env.throw_new("Ljava/lang/RuntimeException", format!("switch start error {}", e))
|
||||
env.throw_new("Ljava/lang/RuntimeException", format!("vnt start error {}", e))
|
||||
.expect("throw");
|
||||
return Err(Error::JavaException);
|
||||
}
|
||||
@@ -92,14 +92,14 @@ fn new_sync(env: &mut JNIEnv, config: JObject) -> Result<SwitchUtilSync, Error>
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn Java_top_wherewego_switchjni_SwitchUtil_new0(
|
||||
pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_VntUtil_new0(
|
||||
mut env: JNIEnv,
|
||||
_class: JClass,
|
||||
config: JObject,
|
||||
) -> jlong {
|
||||
match new_sync(&mut env, config) {
|
||||
Ok(switch_util) => {
|
||||
let ptr = Box::into_raw(Box::new(switch_util));
|
||||
Ok(vnt_util) => {
|
||||
let ptr = Box::into_raw(Box::new(vnt_util));
|
||||
return ptr as jlong;
|
||||
}
|
||||
Err(_) => {}
|
||||
@@ -108,43 +108,51 @@ pub unsafe extern "C" fn Java_top_wherewego_switchjni_SwitchUtil_new0(
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn Java_top_wherewego_switchjni_SwitchUtil_connect0(
|
||||
pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_VntUtil_connect0(
|
||||
mut env: JNIEnv,
|
||||
_class: JClass,
|
||||
raw_switch_util: jlong,
|
||||
raw_vnt_util: jlong,
|
||||
) -> jobject {
|
||||
let raw_switch_util = raw_switch_util as *mut SwitchUtilSync;
|
||||
match (&mut *raw_switch_util).connect() {
|
||||
let raw_vnt_util = raw_vnt_util as *mut VntUtilSync;
|
||||
match (&mut *raw_vnt_util).connect() {
|
||||
Ok(response) => {
|
||||
match reg_response(&mut env, response) {
|
||||
Ok(res) => {
|
||||
return res;
|
||||
}
|
||||
Err(e) => {
|
||||
env.throw(format!("switch connect error {}", e)).expect("throw");
|
||||
env.throw(format!("vnt connect error {}", e)).expect("throw");
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
match e {
|
||||
ReqEnum::TokenError => {
|
||||
env.throw_new("top/wherewego/switchjni/exception/TokenErrorException", "TokenError")
|
||||
env.throw_new("top/wherewego/vnt/jni/exception/TokenErrorException", "TokenError")
|
||||
.expect("throw");
|
||||
}
|
||||
ReqEnum::AddressExhausted => {
|
||||
env.throw_new("top/wherewego/switchjni/exception/AddressExhaustedException", "AddressExhausted")
|
||||
env.throw_new("top/wherewego/vnt/jni/exception/AddressExhaustedException", "AddressExhausted")
|
||||
.expect("throw");
|
||||
}
|
||||
ReqEnum::Timeout => {
|
||||
env.throw_new("top/wherewego/switchjni/exception/TimeoutException", "Timeout")
|
||||
env.throw_new("top/wherewego/vnt/jni/exception/TimeoutException", "Timeout")
|
||||
.expect("throw");
|
||||
}
|
||||
ReqEnum::ServerError(str) => {
|
||||
env.throw_new("java/lang/RuntimeException", format!("switch connect error {}", str))
|
||||
env.throw_new("java/lang/RuntimeException", format!("vnt connect error {}", str))
|
||||
.expect("throw");
|
||||
}
|
||||
ReqEnum::Other(str) => {
|
||||
env.throw_new("java/lang/RuntimeException", format!("switch connect error {}", str))
|
||||
env.throw_new("java/lang/RuntimeException", format!("vnt connect error {}", str))
|
||||
.expect("throw");
|
||||
}
|
||||
ReqEnum::IpAlreadyExists => {
|
||||
env.throw_new("top/wherewego/vnt/jni/exception/IpAlreadyExistsException", "IpAlreadyExists")
|
||||
.expect("throw");
|
||||
}
|
||||
ReqEnum::InvalidIp => {
|
||||
env.throw_new("top/wherewego/vnt/jni/exception/InvalidIpException", "InvalidIp")
|
||||
.expect("throw");
|
||||
}
|
||||
}
|
||||
@@ -155,27 +163,26 @@ pub unsafe extern "C" fn Java_top_wherewego_switchjni_SwitchUtil_connect0(
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn Java_top_wherewego_switchjni_SwitchUtil_createIface0(
|
||||
pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_VntUtil_createIface0(
|
||||
_env: JNIEnv,
|
||||
_class: JClass,
|
||||
raw_switch_util: jlong,
|
||||
raw_vnt_util: jlong,
|
||||
fd: jint,
|
||||
) {
|
||||
let raw_switch_util = raw_switch_util as *mut SwitchUtilSync;
|
||||
let raw_vnt_util = raw_vnt_util as *mut VntUtilSync;
|
||||
|
||||
(&mut *raw_switch_util).create_iface(fd as i32);
|
||||
(&mut *raw_vnt_util).create_iface(fd as i32);
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn Java_top_wherewego_switchjni_SwitchUtil_createIface0(
|
||||
pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_VntUtil_createIface0(
|
||||
mut env: JNIEnv,
|
||||
_class: JClass,
|
||||
raw_switch_util: jlong,
|
||||
raw_vnt_util: jlong,
|
||||
) -> jobject {
|
||||
let raw_switch_util = raw_switch_util as *mut SwitchUtilSync;
|
||||
#[cfg(not(target_os = "android"))]
|
||||
let rs = (&mut *raw_switch_util).create_iface();
|
||||
let raw_vnt_util = raw_vnt_util as *mut VntUtilSync;
|
||||
let rs = (&mut *raw_vnt_util).create_iface();
|
||||
match rs {
|
||||
Ok(driver_info) => {
|
||||
match driver_info_e(&mut env, driver_info) {
|
||||
@@ -183,12 +190,12 @@ pub unsafe extern "C" fn Java_top_wherewego_switchjni_SwitchUtil_createIface0(
|
||||
return res;
|
||||
}
|
||||
Err(e) => {
|
||||
env.throw(format!("switch create iface error {}", e)).expect("throw");
|
||||
env.throw(format!("vnt create iface error {}", e)).expect("throw");
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
env.throw_new("java/lang/RuntimeException", format!("switch create iface error {}", e))
|
||||
env.throw_new("java/lang/RuntimeException", format!("vnt create iface error {}", e))
|
||||
.expect("throw");
|
||||
}
|
||||
}
|
||||
@@ -196,18 +203,18 @@ pub unsafe extern "C" fn Java_top_wherewego_switchjni_SwitchUtil_createIface0(
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn Java_top_wherewego_switchjni_SwitchUtil_build0(
|
||||
pub unsafe extern "C" fn Java_top_wherewego_vnt_jni_VntUtil_build0(
|
||||
mut env: JNIEnv,
|
||||
_class: JClass,
|
||||
raw_switch_util: jlong,
|
||||
raw_vnt_util: jlong,
|
||||
) -> jlong {
|
||||
let raw_switch_util = Box::from_raw(raw_switch_util as *mut SwitchUtilSync);
|
||||
match raw_switch_util.build() {
|
||||
let raw_vnt_util = Box::from_raw(raw_vnt_util as *mut VntUtilSync);
|
||||
match raw_vnt_util.build() {
|
||||
Ok(rs) => {
|
||||
return Box::into_raw(Box::new(rs)) as jlong;
|
||||
}
|
||||
Err(e) => {
|
||||
env.throw_new("java/lang/RuntimeException", format!("switch start error:{:?}", e))
|
||||
env.throw_new("java/lang/RuntimeException", format!("vnt start error:{:?}", e))
|
||||
.expect("throw");
|
||||
}
|
||||
}
|
||||
@@ -219,7 +226,7 @@ fn reg_response(env: &mut JNIEnv, response: RegResponse) -> Result<jobject, Erro
|
||||
let virtual_gateway = u32::from(response.virtual_gateway);
|
||||
let virtual_netmask = u32::from(response.virtual_netmask);
|
||||
let response = env.new_object(
|
||||
"top/wherewego/switchjni/RegResponse",
|
||||
"top/wherewego/vnt/jni/RegResponse",
|
||||
"(III)V",
|
||||
&[JValue::Int(virtual_ip as jint),
|
||||
JValue::Int(virtual_gateway as jint),
|
||||
@@ -235,7 +242,7 @@ fn driver_info_e(env: &mut JNIEnv, driver_info: DriverInfo) -> Result<jobject, E
|
||||
let version = driver_info.version;
|
||||
let mac = driver_info.mac.unwrap_or(String::new());
|
||||
let response = env.new_object(
|
||||
"top/wherewego/switchjni/DriverInfo",
|
||||
"top/wherewego/vnt/jni/DriverInfo",
|
||||
"(ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
|
||||
&[JValue::Bool(is_tun as jboolean),
|
||||
JValue::Object(&env.new_string(name)?.into()),
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "switch"
|
||||
version = "1.1.0"
|
||||
name = "vnt"
|
||||
version = "1.1.1"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@@ -5,6 +5,8 @@ message RegistrationRequest{
|
||||
string name = 3;
|
||||
bool is_fast = 4;
|
||||
string version = 5;
|
||||
fixed32 virtual_ip = 6;
|
||||
bool allow_ip_change = 7;
|
||||
}
|
||||
|
||||
message RegistrationResponse{
|
||||
@@ -15,6 +17,7 @@ message RegistrationResponse{
|
||||
repeated DeviceInfo device_info_list = 5;
|
||||
fixed32 public_ip = 6;
|
||||
uint32 public_port = 7;
|
||||
bytes public_ipv6 = 8;
|
||||
}
|
||||
message DeviceInfo{
|
||||
string name = 1;
|
||||
@@ -35,6 +38,7 @@ message PunchInfo{
|
||||
bool reply = 6;
|
||||
fixed32 local_ip = 7;
|
||||
uint32 local_port = 8;
|
||||
repeated bytes public_ipv6_list = 9;
|
||||
}
|
||||
enum PunchNatType{
|
||||
Symmetric = 0;
|
||||
@@ -1,28 +1,33 @@
|
||||
use std::io;
|
||||
use std::net::{Ipv4Addr, SocketAddr};
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::time::Instant;
|
||||
use std::time::{Duration, Instant};
|
||||
use crossbeam_skiplist::SkipMap;
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::Mutex;
|
||||
use tokio::net::UdpSocket;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::{TcpStream, UdpSocket};
|
||||
use tokio::net::tcp::OwnedReadHalf;
|
||||
use tokio::sync::watch::{channel, Receiver, Sender};
|
||||
use crate::channel::{Route, RouteKey, Status};
|
||||
use crate::channel::punch::NatType;
|
||||
use crate::core::status::SwitchWorker;
|
||||
use crate::core::status::VntWorker;
|
||||
use crate::handle::CurrentDeviceInfo;
|
||||
use crate::handle::recv_handler::ChannelDataHandler;
|
||||
|
||||
pub struct ContextInner {
|
||||
pub(crate) lock: Mutex<()>,
|
||||
pub(crate) count: AtomicUsize,
|
||||
//udp用于打洞、服务端通信(可选)
|
||||
pub(crate) main_channel: Arc<UdpSocket>,
|
||||
//在udp的基础上,可以选择使用tcp和服务端通信
|
||||
pub(crate) main_tcp_channel: Option<tokio::sync::mpsc::Sender<Vec<u8>>>,
|
||||
pub(crate) route_table: SkipMap<Ipv4Addr, Vec<Route>>,
|
||||
pub(crate) route_table_time: SkipMap<(RouteKey, Ipv4Addr), AtomicCell<Instant>>,
|
||||
pub(crate) status_receiver: Receiver<Status>,
|
||||
pub(crate) status_sender: Sender<Status>,
|
||||
pub(crate) udp_map: SkipMap<usize, Arc<UdpSocket>>,
|
||||
pub(crate) channel_num: usize,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -31,20 +36,21 @@ pub struct Context {
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn new(main_channel: Arc<UdpSocket>, _channel_num: usize) -> Self {
|
||||
pub fn new(main_channel: Arc<UdpSocket>, main_tcp_channel: Option<tokio::sync::mpsc::Sender<Vec<u8>>>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>, _channel_num: usize) -> Self {
|
||||
//当前版本只支持一个通道
|
||||
let channel_num = 1;
|
||||
let (status_sender, status_receiver) = channel(Status::Cone);
|
||||
let inner = Arc::new(ContextInner {
|
||||
lock: Mutex::new(()),
|
||||
count: AtomicUsize::new(0),
|
||||
main_channel,
|
||||
main_tcp_channel,
|
||||
route_table: SkipMap::new(),
|
||||
route_table_time: SkipMap::new(),
|
||||
status_receiver,
|
||||
status_sender,
|
||||
udp_map: SkipMap::new(),
|
||||
channel_num,
|
||||
current_device,
|
||||
});
|
||||
Self {
|
||||
inner
|
||||
@@ -82,7 +88,30 @@ impl Context {
|
||||
self.inner.main_channel.local_addr().map(|k| k.port())
|
||||
}
|
||||
pub async fn send_main(&self, buf: &[u8], addr: SocketAddr) -> io::Result<usize> {
|
||||
self.inner.main_channel.send_to(buf, addr).await
|
||||
if let Some(sender) = &self.inner.main_tcp_channel {
|
||||
let mut vec = vec![0; 4 + buf.len()];
|
||||
vec[4..].copy_from_slice(buf);
|
||||
if sender.send(vec).await.is_ok() {
|
||||
Ok(buf.len())
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "send_main err"))
|
||||
}
|
||||
} else {
|
||||
self.inner.main_channel.send_to(buf, addr).await
|
||||
}
|
||||
}
|
||||
pub fn try_send_main(&self, buf: &[u8], addr: SocketAddr) -> io::Result<usize> {
|
||||
if let Some(sender) = &self.inner.main_tcp_channel {
|
||||
let mut vec = vec![0; 4 + buf.len()];
|
||||
vec[4..].copy_from_slice(buf);
|
||||
if sender.try_send(vec).is_ok() {
|
||||
Ok(buf.len())
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "try_send_main err"))
|
||||
}
|
||||
} else {
|
||||
self.inner.main_channel.try_send_to(buf, addr)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn send_all(&self, buf: &[u8], addr: SocketAddr) -> io::Result<()> {
|
||||
@@ -91,31 +120,36 @@ impl Context {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn try_send_main(&self, buf: &[u8], addr: SocketAddr) -> io::Result<usize> {
|
||||
self.inner.main_channel.try_send_to(buf, addr)
|
||||
}
|
||||
|
||||
pub async fn send_by_id(&self, buf: &[u8], id: &Ipv4Addr) -> io::Result<usize> {
|
||||
if let Some(v) = self.inner.route_table.get(id) {
|
||||
let route = match v.value().len() {
|
||||
0 => {
|
||||
return Err(io::Error::new(io::ErrorKind::NotFound, "route not found"));
|
||||
}
|
||||
1 => v.value()[0],
|
||||
len => v.value()[self.inner.count.fetch_add(1, Ordering::Relaxed) % len]
|
||||
};
|
||||
if v.value().is_empty() {
|
||||
return Err(io::Error::new(io::ErrorKind::NotFound, "route not found"));
|
||||
}
|
||||
let route = v.value()[0];
|
||||
drop(v);
|
||||
if !route.is_p2p() {
|
||||
if let Some(time) = self.inner.route_table_time.get(&(route.route_key(), *id)) {
|
||||
//借道传输时,长时间不通信的通道不使用
|
||||
if time.value().load().elapsed() > Duration::from_secs(3) {
|
||||
return Err(io::Error::new(io::ErrorKind::NotFound, "route time out"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(udp) = self.inner.udp_map.get(&route.index) {
|
||||
return udp.value().send_to(buf, route.addr).await;
|
||||
}
|
||||
}
|
||||
Err(io::Error::new(io::ErrorKind::NotFound, "route not found"))
|
||||
}
|
||||
|
||||
pub fn try_send_by_id(&self, buf: &[u8], id: &Ipv4Addr) -> io::Result<usize> {
|
||||
if let Some(v) = self.inner.route_table.get(id) {
|
||||
if v.value().is_empty() {
|
||||
return Err(io::Error::new(io::ErrorKind::NotFound, "route not found"));
|
||||
}
|
||||
let route = v.value()[self.inner.count.fetch_add(1, Ordering::Relaxed) % v.value().len()];
|
||||
let route = v.value()[0];
|
||||
drop(v);
|
||||
if let Some(udp) = self.inner.udp_map.get(&route.index) {
|
||||
return udp.value().try_send_to(buf, route.addr);
|
||||
@@ -124,12 +158,34 @@ impl Context {
|
||||
Err(io::Error::new(io::ErrorKind::NotFound, "route not found"))
|
||||
}
|
||||
pub async fn send_by_key(&self, buf: &[u8], route_key: &RouteKey) -> io::Result<usize> {
|
||||
if route_key.index == 0 {
|
||||
if let Some(sender) = &self.inner.main_tcp_channel {
|
||||
let mut vec = vec![0; 4 + buf.len()];
|
||||
vec[4..].copy_from_slice(buf);
|
||||
return if sender.send(vec).await.is_ok() {
|
||||
Ok(buf.len())
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "send_by_key err"))
|
||||
};
|
||||
}
|
||||
}
|
||||
if let Some(udp) = self.inner.udp_map.get(&route_key.index) {
|
||||
return udp.value().send_to(buf, route_key.addr).await;
|
||||
}
|
||||
Err(io::Error::new(io::ErrorKind::NotFound, "route not found"))
|
||||
}
|
||||
pub fn try_send_by_key(&self, buf: &[u8], route_key: &RouteKey) -> io::Result<usize> {
|
||||
if route_key.index == 0 {
|
||||
if let Some(sender) = &self.inner.main_tcp_channel {
|
||||
let mut vec = vec![0; 4 + buf.len()];
|
||||
vec[4..].copy_from_slice(buf);
|
||||
return if sender.try_send(vec).is_ok() {
|
||||
Ok(buf.len())
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "try_send_by_key err"))
|
||||
};
|
||||
}
|
||||
}
|
||||
if let Some(udp) = self.inner.udp_map.get(&route_key.index) {
|
||||
return udp.value().try_send_to(buf, route_key.addr);
|
||||
}
|
||||
@@ -280,30 +336,105 @@ impl Channel {
|
||||
|
||||
impl Channel {
|
||||
async fn handle(handler: &mut ChannelDataHandler,
|
||||
udp: &Arc<UdpSocket>,
|
||||
context: &Context,
|
||||
id: usize,
|
||||
result: io::Result<(usize, SocketAddr)>,
|
||||
buf: &mut [u8], start: usize) {
|
||||
match result {
|
||||
Ok((len, addr)) => {
|
||||
handler.handle(buf, start, start + len, RouteKey::new(id, addr), &udp, context).await;
|
||||
handler.handle(buf, start, start + len, RouteKey::new(id, addr), context).await;
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{:?}",e)
|
||||
}
|
||||
}
|
||||
}
|
||||
async fn tcp_handle(mut tcp_r: OwnedReadHalf, context: Context,
|
||||
mut handler: ChannelDataHandler, head_reserve: usize, ) -> io::Result<()> {
|
||||
let mut buf = [0; 4096];
|
||||
let addr = tcp_r.peer_addr()?;
|
||||
let key = RouteKey::new(0, addr);
|
||||
loop {
|
||||
tcp_r.read_exact(&mut buf[head_reserve..head_reserve + 4]).await?;
|
||||
let len = 4 + (((buf[head_reserve + 2] as u16) << 8) | buf[head_reserve + 3] as u16) as usize;
|
||||
tcp_r.read_exact(&mut buf[head_reserve + 4..head_reserve + len]).await?;
|
||||
handler.handle(&mut buf[4..], head_reserve, head_reserve + len - 4, key, &context).await;
|
||||
}
|
||||
}
|
||||
async fn start_tcp(mut worker: VntWorker, tcp_stream: TcpStream, mut receiver: tokio::sync::mpsc::Receiver<Vec<u8>>, context: Context, handler: ChannelDataHandler, head_reserve: usize) {
|
||||
let (tcp_r, mut tcp_w) = tcp_stream.into_split();
|
||||
{
|
||||
let context = context.clone();
|
||||
let handler = handler.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = Self::tcp_handle(tcp_r, context, handler, head_reserve).await {
|
||||
log::info!("tcp链接断开:{:?}",e);
|
||||
}
|
||||
});
|
||||
}
|
||||
loop {
|
||||
tokio::select! {
|
||||
_=worker.stop_wait()=>{
|
||||
break;
|
||||
}
|
||||
rs=receiver.recv()=>{
|
||||
if let Some(mut data) = rs{
|
||||
if data.len()<4{
|
||||
continue
|
||||
}
|
||||
let len = data.len() - 4;
|
||||
data[2] = (len >> 8) as u8;
|
||||
data[3] = (len & 0xFF) as u8;
|
||||
if let Err(e) = tcp_w.write_all(&data).await {
|
||||
if context.is_close() {
|
||||
break;
|
||||
}
|
||||
log::info!("发送失败,需要重连:{:?}",e);
|
||||
let _ = tcp_w.shutdown().await;
|
||||
match TcpStream::connect(context.inner.current_device.load().connect_server).await {
|
||||
Ok(tcp_stream) => {
|
||||
let (r, w) = tcp_stream.into_split();
|
||||
tcp_w = w;
|
||||
let context = context.clone();
|
||||
let handler = handler.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = Self::tcp_handle(r, context, handler, head_reserve).await {
|
||||
log::info!("tcp 链接断开:{:?}",e);
|
||||
}
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
log::info!("重连失败:{:?}",e);
|
||||
}
|
||||
};
|
||||
}
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
worker.stop_all();
|
||||
}
|
||||
pub async fn start(self,
|
||||
mut worker: SwitchWorker,
|
||||
mut worker: VntWorker,
|
||||
tcp: Option<(TcpStream, tokio::sync::mpsc::Receiver<Vec<u8>>)>,
|
||||
head_reserve: usize,//头部预留字节
|
||||
symmetric_channel_num: usize,//对称网络,则再加一组监听,提升打洞成功率
|
||||
relay: bool,
|
||||
) {
|
||||
let context = self.context;
|
||||
let main_channel = context.inner.main_channel.clone();
|
||||
let handler = self.handler.clone();
|
||||
if let Some((tcp_stream, receiver)) = tcp {
|
||||
tokio::spawn(Self::start_tcp(worker.worker("main_channel_tcp"), tcp_stream, receiver, context.clone(), handler.clone(), head_reserve));
|
||||
}
|
||||
tokio::spawn(Self::start_(worker.worker("main_channel_1"), context.clone(), handler.clone(), main_channel.clone(), head_reserve, true));
|
||||
tokio::spawn(Self::start_(worker.worker("main_channel_2"), context.clone(), handler, main_channel, head_reserve, true));
|
||||
// tokio::spawn(Self::start_(worker.worker("main_channel_2"), context.clone(), handler, main_channel, head_reserve, true));
|
||||
if relay {
|
||||
worker.stop_wait().await;
|
||||
return;
|
||||
}
|
||||
let mut cur_status = Status::Cone;
|
||||
let mut status_receiver = context.inner.status_receiver.clone();
|
||||
loop {
|
||||
@@ -352,7 +483,7 @@ impl Channel {
|
||||
}
|
||||
worker.stop_all();
|
||||
}
|
||||
async fn start_(mut worker: SwitchWorker, context: Context,
|
||||
async fn start_(mut worker: VntWorker, context: Context,
|
||||
mut handler: ChannelDataHandler,
|
||||
udp: Arc<UdpSocket>,
|
||||
head_reserve: usize,
|
||||
@@ -361,17 +492,17 @@ impl Channel {
|
||||
#[cfg(target_os = "windows")]
|
||||
use std::os::windows::io::AsRawSocket;
|
||||
#[cfg(target_os = "windows")]
|
||||
let id = udp.as_raw_socket() as usize;
|
||||
let id = 1 + udp.as_raw_socket() as usize;
|
||||
#[cfg(any(unix))]
|
||||
use std::os::fd::AsRawFd;
|
||||
#[cfg(any(unix))]
|
||||
let id = udp.as_raw_fd() as usize;
|
||||
let id = 1 + udp.as_raw_fd() as usize;
|
||||
context.inner.udp_map.insert(id, udp.clone());
|
||||
let mut buf = [0; 4096];
|
||||
loop {
|
||||
tokio::select! {
|
||||
rs=udp.recv_from(&mut buf[head_reserve..])=>{
|
||||
Self::handle(&mut handler,&udp,&context,id,rs,&mut buf,head_reserve).await;
|
||||
Self::handle(&mut handler,&context,id,rs,&mut buf,head_reserve).await;
|
||||
}
|
||||
changed=status_receiver.changed()=>{
|
||||
match changed {
|
||||
@@ -25,7 +25,7 @@ impl Idle {
|
||||
/// 获取空闲路由
|
||||
pub async fn next_idle(&self) -> io::Result<(Ipv4Addr, RouteKey)> {
|
||||
loop {
|
||||
let mut max = Duration::from_secs(10);
|
||||
let mut max = Duration::from_secs(0);
|
||||
for entry in self.context.inner.route_table_time.iter() {
|
||||
let last_read = entry.value().load().elapsed();
|
||||
if last_read >= self.read_idle {
|
||||
@@ -36,8 +36,10 @@ impl Idle {
|
||||
}
|
||||
}
|
||||
}
|
||||
let sleep_time = self.read_idle - max;
|
||||
tokio::time::sleep(sleep_time).await;
|
||||
if self.read_idle > max {
|
||||
let sleep_time = self.read_idle - max;
|
||||
tokio::time::sleep(sleep_time).await;
|
||||
}
|
||||
if self.context.is_close() {
|
||||
return Err(Error::new(ErrorKind::Other, "closed"));
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
use std::io;
|
||||
|
||||
use aes_gcm::{AeadInPlace, Aes128Gcm, Aes256Gcm, Key, Nonce, Tag,KeyInit};
|
||||
use aes_gcm::aead::consts::{U12, U16};
|
||||
use aes_gcm::aead::generic_array::GenericArray;
|
||||
use sha2::Digest;
|
||||
|
||||
use crate::protocol;
|
||||
use crate::protocol::{ip_turn_packet, NetPacket};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Cipher {
|
||||
AesGCM128(Aes128Gcm),
|
||||
AesGCM256(Aes256Gcm),
|
||||
None,
|
||||
}
|
||||
|
||||
impl Cipher {
|
||||
pub fn new(password: Option<String>) -> Self {
|
||||
if let Some(password) = password {
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
hasher.update(password.as_bytes());
|
||||
let key: [u8; 32] = hasher.finalize().into();
|
||||
if password.len() < 8 {
|
||||
let key: &Key<Aes128Gcm> = key[..16].into();
|
||||
Cipher::AesGCM128(Aes128Gcm::new(&key))
|
||||
} else {
|
||||
let key: &Key<Aes256Gcm> = &key.into();
|
||||
Cipher::AesGCM256(Aes256Gcm::new(&key))
|
||||
}
|
||||
} else {
|
||||
Cipher::None
|
||||
}
|
||||
}
|
||||
pub fn decrypt_ipv4(&self, net_packet: &mut NetPacket<&mut [u8]>) -> io::Result<Option<usize>> {
|
||||
match &self {
|
||||
Cipher::None => {
|
||||
return Ok(None);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if !net_packet.is_encrypt() {
|
||||
//未加密的数据直接丢弃
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "not encrypt"));
|
||||
}
|
||||
if net_packet.payload().len() < 16 {
|
||||
log::error!("数据异常,长度小于16");
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
let mut nonce = [0; 12];
|
||||
nonce[0..4].copy_from_slice(&net_packet.source().octets());
|
||||
nonce[4..8].copy_from_slice(&net_packet.destination().octets());
|
||||
nonce[8] = protocol::Protocol::IpTurn.into();
|
||||
nonce[9] = ip_turn_packet::Protocol::Ipv4.into();
|
||||
let nonce: &GenericArray<u8, U12> = Nonce::from_slice(&nonce);
|
||||
let payload_len = net_packet.payload().len() - 16;
|
||||
let tag: GenericArray<u8, U16> = Tag::clone_from_slice(&net_packet.payload()[payload_len..]);
|
||||
let rs = match &self {
|
||||
Cipher::AesGCM128(cipher) => {
|
||||
cipher.decrypt_in_place_detached(nonce, &[], &mut net_packet.payload_mut()[..payload_len], &tag)
|
||||
}
|
||||
Cipher::AesGCM256(cipher) => {
|
||||
cipher.decrypt_in_place_detached(nonce, &[], &mut net_packet.payload_mut()[..payload_len], &tag)
|
||||
}
|
||||
Cipher::None => {
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
if let Err(e) = rs {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("解密失败:{}", e)));
|
||||
}
|
||||
return Ok(Some(payload_len));
|
||||
}
|
||||
/// net_packet 必须预留足够长度
|
||||
/// data_len是有效载荷的长度
|
||||
/// 返回加密后载荷的长度
|
||||
pub fn encrypt_ipv4(&self, payload_len: usize, net_packet: &mut NetPacket<&mut [u8]>) -> io::Result<Option<usize>> {
|
||||
match &self {
|
||||
Cipher::None => {
|
||||
return Ok(None);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let mut nonce = [0; 12];
|
||||
nonce[0..4].copy_from_slice(&net_packet.source().octets());
|
||||
nonce[4..8].copy_from_slice(&net_packet.destination().octets());
|
||||
nonce[8] = protocol::Protocol::IpTurn.into();
|
||||
nonce[9] = ip_turn_packet::Protocol::Ipv4.into();
|
||||
let nonce: &GenericArray<u8, U12> = Nonce::from_slice(&nonce);
|
||||
let rs = match &self {
|
||||
Cipher::AesGCM128(cipher) => {
|
||||
cipher.encrypt_in_place_detached(nonce, &[], &mut net_packet.payload_mut()[..payload_len])
|
||||
}
|
||||
Cipher::AesGCM256(cipher) => {
|
||||
cipher.encrypt_in_place_detached(nonce, &[], &mut net_packet.payload_mut()[..payload_len])
|
||||
}
|
||||
Cipher::None => {
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
return match rs {
|
||||
Ok(tag) => {
|
||||
if tag.len() != 16 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("加密tag长度错误:{}", tag.len())));
|
||||
}
|
||||
net_packet.set_encrypt_flag(true);
|
||||
net_packet.payload_mut()[payload_len..payload_len + 16].copy_from_slice(tag.as_slice());
|
||||
Ok(Some(payload_len + 16))
|
||||
}
|
||||
Err(e) => {
|
||||
Err(io::Error::new(io::ErrorKind::Other, format!("加密失败:{}", e)))
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,10 @@ use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use aes_gcm::{Aes256Gcm, Key, KeyInit};
|
||||
use crossbeam_skiplist::SkipMap;
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::Mutex;
|
||||
use sha2::Digest;
|
||||
use tokio::net::UdpSocket;
|
||||
use tokio::net::{TcpStream, UdpSocket};
|
||||
use tokio::sync::mpsc::channel;
|
||||
|
||||
use crate::channel::{Route, RouteKey};
|
||||
@@ -16,7 +14,8 @@ use crate::channel::channel::{Channel, Context};
|
||||
use crate::channel::idle::Idle;
|
||||
use crate::channel::punch::{NatInfo, Punch};
|
||||
use crate::channel::sender::ChannelSender;
|
||||
use crate::core::status::SwitchStatusManger;
|
||||
use crate::cipher::Cipher;
|
||||
use crate::core::status::VntStatusManger;
|
||||
use crate::error::Error;
|
||||
use crate::external_route::ExternalRoute;
|
||||
use crate::handle::{ConnectStatus, CurrentDeviceInfo, heartbeat_handler, PeerDeviceInfo, punch_handler, registration_handler};
|
||||
@@ -35,11 +34,11 @@ pub mod sync;
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Switch {
|
||||
pub struct Vnt {
|
||||
name: String,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
context: Context,
|
||||
switch_status_manager: SwitchStatusManger,
|
||||
vnt_status_manager: VntStatusManger,
|
||||
device_writer: DeviceWriter,
|
||||
/// 0. 机器纪元,每一次上线或者下线都会增1,用于感知网络中机器变化
|
||||
/// 服务端和客户端的不一致,则服务端会推送新的设备列表
|
||||
@@ -50,27 +49,34 @@ pub struct Switch {
|
||||
peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>>,
|
||||
}
|
||||
|
||||
pub struct SwitchUtil {
|
||||
pub struct VntUtil {
|
||||
config: Config,
|
||||
main_channel: Arc<UdpSocket>,
|
||||
main_channel: UdpSocket,
|
||||
main_tcp_channel: Option<TcpStream>,
|
||||
response: Option<RegResponse>,
|
||||
iface: Option<(DeviceWriter, DeviceReader)>,
|
||||
}
|
||||
|
||||
impl SwitchUtil {
|
||||
pub async fn new(config: Config) -> io::Result<SwitchUtil> {
|
||||
let main_channel = Arc::new(UdpSocket::bind("0.0.0.0:0").await?);
|
||||
Ok(SwitchUtil {
|
||||
impl VntUtil {
|
||||
pub async fn new(config: Config) -> io::Result<VntUtil> {
|
||||
let main_channel = UdpSocket::bind("0.0.0.0:0").await?;
|
||||
let main_tcp_channel = if config.tcp {
|
||||
Some(TcpStream::connect(config.server_address).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(VntUtil {
|
||||
config,
|
||||
main_channel,
|
||||
main_tcp_channel,
|
||||
response: None,
|
||||
iface: None,
|
||||
})
|
||||
}
|
||||
pub async fn connect(&mut self) -> Result<RegResponse, ReqEnum> {
|
||||
match registration_handler::registration(&self.main_channel, self.config.server_address,
|
||||
match registration_handler::registration(&self.main_channel, self.main_tcp_channel.as_mut(), self.config.server_address,
|
||||
self.config.token.clone(), self.config.device_id.clone(),
|
||||
self.config.name.clone()).await {
|
||||
self.config.name.clone(), self.config.ip.unwrap_or(Ipv4Addr::UNSPECIFIED)).await {
|
||||
Ok(res) => {
|
||||
let _ = self.response.insert(res.clone());
|
||||
Ok(res)
|
||||
@@ -101,14 +107,14 @@ impl SwitchUtil {
|
||||
let device_type = if self.config.tap {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
//删除switch的tun网卡避免ip冲突,因为非正常退出会保留网卡
|
||||
//删除tun网卡避免ip冲突,因为非正常退出会保留网卡
|
||||
tun_tap_device::delete_device(tun_tap_device::DeviceType::Tun);
|
||||
}
|
||||
tun_tap_device::DeviceType::Tap
|
||||
} else {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
//删除switch的tap网卡避免ip冲突,非正常退出会保留网卡
|
||||
//删除tap网卡避免ip冲突,非正常退出会保留网卡
|
||||
tun_tap_device::delete_device(tun_tap_device::DeviceType::Tap);
|
||||
}
|
||||
tun_tap_device::DeviceType::Tun
|
||||
@@ -121,7 +127,7 @@ impl SwitchUtil {
|
||||
let _ = self.iface.insert((device_writer, device_reader));
|
||||
Ok(driver_info)
|
||||
}
|
||||
pub async fn build(self) -> crate::Result<Switch> {
|
||||
pub async fn build(self) -> crate::Result<Vnt> {
|
||||
let response = match self.response {
|
||||
None => {
|
||||
return Err(Error::Stop("response None".to_string()));
|
||||
@@ -139,16 +145,22 @@ impl SwitchUtil {
|
||||
}
|
||||
};
|
||||
let config = self.config;
|
||||
let switch_status_manager = SwitchStatusManger::new();
|
||||
let cipher = if let Some(key) = &config.key {
|
||||
let key: &Key<Aes256Gcm> = key.into();
|
||||
Some(Aes256Gcm::new(&key))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let vnt_status_manager = VntStatusManger::new();
|
||||
let cipher = Cipher::new(config.password.clone());
|
||||
let virtual_ip = response.virtual_ip;
|
||||
let virtual_gateway = response.virtual_gateway;
|
||||
let virtual_netmask = response.virtual_netmask;
|
||||
let current_device = Arc::new(AtomicCell::new(CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, config.server_address)));
|
||||
|
||||
let (cone_sender, cone_receiver) = channel(3);
|
||||
let (symmetric_sender, symmetric_receiver) = channel(2);
|
||||
let context = Context::new(self.main_channel, 1);
|
||||
let (tcp_sender, tcp) = if let Some(main_tcp_channel) = self.main_tcp_channel {
|
||||
let (tcp_sender, tcp_receiver) = channel::<Vec<u8>>(100);
|
||||
(Some(tcp_sender), Some((main_tcp_channel, tcp_receiver)))
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
let context = Context::new(Arc::new(self.main_channel), tcp_sender, current_device.clone(), 1);
|
||||
let punch = Punch::new(context.clone());
|
||||
let idle = Idle::new(Duration::from_secs(16), context.clone());
|
||||
let channel_sender = ChannelSender::new(context.clone());
|
||||
@@ -159,9 +171,7 @@ impl SwitchUtil {
|
||||
let device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>> = Arc::new(Mutex::new((response.epoch, response.device_info_list)));
|
||||
let peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>> = Arc::new(SkipMap::new());
|
||||
let connect_status = Arc::new(AtomicCell::new(ConnectStatus::Connected));
|
||||
let virtual_ip = response.virtual_ip;
|
||||
let virtual_gateway = response.virtual_gateway;
|
||||
let virtual_netmask = response.virtual_netmask;
|
||||
|
||||
|
||||
let local_ip = crate::nat::local_ip()?;
|
||||
let local_port = context.main_local_port()?;
|
||||
@@ -175,7 +185,6 @@ impl SwitchUtil {
|
||||
} else {
|
||||
Some(ExternalRoute::new(config.in_ips))
|
||||
};
|
||||
let current_device = Arc::new(AtomicCell::new(CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, config.server_address)));
|
||||
let (tcp_proxy, udp_proxy, ip_proxy_map) = if out_ips.is_empty() {
|
||||
(None, None, None)
|
||||
} else {
|
||||
@@ -190,14 +199,14 @@ impl SwitchUtil {
|
||||
};
|
||||
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
|
||||
if config.tap {
|
||||
tap_handler::start(switch_status_manager.worker("tap_handler"), channel_sender.clone(), device_reader, device_writer.clone(),
|
||||
tap_handler::start(vnt_status_manager.worker("tap_handler"), channel_sender.clone(), device_reader, device_writer.clone(),
|
||||
igmp_server.clone(), current_device.clone(), in_external_route, ip_proxy_map.clone(), cipher.clone());
|
||||
} else {
|
||||
tun_handler::start(switch_status_manager.worker("tun_handler"), channel_sender.clone(), device_reader, device_writer.clone(),
|
||||
tun_handler::start(vnt_status_manager.worker("tun_handler"), channel_sender.clone(), device_reader, device_writer.clone(),
|
||||
igmp_server.clone(), current_device.clone(), in_external_route, ip_proxy_map.clone(), cipher.clone());
|
||||
}
|
||||
#[cfg(any(target_os = "android"))]
|
||||
tun_handler::start(switch_status_manager.worker("android tun_handler"), channel_sender.clone(), device_reader, device_writer.clone(),
|
||||
tun_handler::start(vnt_status_manager.worker("android tun_handler"), channel_sender.clone(), device_reader, device_writer.clone(),
|
||||
igmp_server.clone(), current_device.clone(), in_external_route, ip_proxy_map.clone(), cipher.clone());
|
||||
|
||||
//外部数据接收处理
|
||||
@@ -205,10 +214,11 @@ impl SwitchUtil {
|
||||
register.clone(), nat_test.clone(), igmp_server,
|
||||
device_writer.clone(), connect_status.clone(),
|
||||
peer_nat_info_map.clone(), ip_proxy_map, out_external_route,
|
||||
cone_sender, symmetric_sender, cipher);
|
||||
cone_sender, symmetric_sender, cipher, config.relay);
|
||||
{
|
||||
let channel = Channel::new(context.clone(), channel_recv_handler);
|
||||
let channel_worker = switch_status_manager.worker("channel_worker");
|
||||
let channel_worker = vnt_status_manager.worker("channel_worker");
|
||||
let relay = config.relay;
|
||||
if let Some(tcp_proxy) = tcp_proxy {
|
||||
tokio::spawn(tcp_proxy.start());
|
||||
}
|
||||
@@ -216,11 +226,11 @@ impl SwitchUtil {
|
||||
tokio::spawn(udp_proxy.start());
|
||||
}
|
||||
tokio::spawn(async move {
|
||||
channel.start(channel_worker, 14, 65).await
|
||||
channel.start(channel_worker, tcp, 14, 65, relay).await
|
||||
});
|
||||
}
|
||||
{
|
||||
let other_worker = switch_status_manager.worker("punch_handler");
|
||||
let other_worker = vnt_status_manager.worker("punch_handler");
|
||||
let nat_test = nat_test.clone();
|
||||
let device_list = device_list.clone();
|
||||
let current_device = current_device.clone();
|
||||
@@ -228,18 +238,20 @@ impl SwitchUtil {
|
||||
heartbeat_handler::start_heartbeat(other_worker.worker("heartbeat"), channel_sender.clone(), device_list.clone(), current_device.clone(), config.server_address_str);
|
||||
// 空闲检查
|
||||
heartbeat_handler::start_idle(other_worker.worker("idle"), idle, channel_sender.clone());
|
||||
// 打洞处理
|
||||
punch_handler::start(other_worker.worker("cone_receiver"), cone_receiver, punch.clone(), current_device.clone());
|
||||
punch_handler::start(other_worker.worker("symmetric_receiver"), symmetric_receiver, punch, current_device.clone());
|
||||
tokio::spawn(punch_handler::start_punch(other_worker, nat_test,
|
||||
device_list, channel_sender, current_device));
|
||||
if !config.relay {
|
||||
// 打洞处理
|
||||
punch_handler::start(other_worker.worker("cone_receiver"), cone_receiver, punch.clone(), current_device.clone());
|
||||
punch_handler::start(other_worker.worker("symmetric_receiver"), symmetric_receiver, punch, current_device.clone());
|
||||
tokio::spawn(punch_handler::start_punch(other_worker, nat_test,
|
||||
device_list, channel_sender, current_device));
|
||||
}
|
||||
}
|
||||
context.switch(nat_test.nat_info().nat_type);
|
||||
Ok(Switch {
|
||||
Ok(Vnt {
|
||||
name: config.name,
|
||||
current_device,
|
||||
context,
|
||||
switch_status_manager,
|
||||
vnt_status_manager,
|
||||
device_writer,
|
||||
nat_test,
|
||||
device_list,
|
||||
@@ -249,7 +261,7 @@ impl SwitchUtil {
|
||||
}
|
||||
}
|
||||
|
||||
impl Switch {
|
||||
impl Vnt {
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
@@ -282,7 +294,7 @@ impl Switch {
|
||||
}
|
||||
pub fn stop(&self) -> io::Result<()> {
|
||||
self.context.close();
|
||||
self.switch_status_manager.stop_all();
|
||||
self.vnt_status_manager.stop_all();
|
||||
self.device_writer.close()?;
|
||||
let virtual_gateway = self.current_device.load().virtual_gateway;
|
||||
let _ = std::net::UdpSocket::bind("0.0.0.0:0")?.send_to(&[0],
|
||||
@@ -290,12 +302,12 @@ impl Switch {
|
||||
Ok(())
|
||||
}
|
||||
pub async fn wait_stop(&mut self) {
|
||||
self.switch_status_manager.wait().await;
|
||||
self.vnt_status_manager.wait().await;
|
||||
let _ = self.stop();
|
||||
}
|
||||
pub async fn wait_stop_ms(&mut self, ms: Duration) -> bool {
|
||||
tokio::select! {
|
||||
_=self.switch_status_manager.wait()=>{
|
||||
_=self.vnt_status_manager.wait()=>{
|
||||
let _ = self.stop();
|
||||
return true;
|
||||
}
|
||||
@@ -306,7 +318,7 @@ impl Switch {
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Switch {
|
||||
impl Drop for Vnt {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.stop();
|
||||
}
|
||||
@@ -323,9 +335,12 @@ pub struct Config {
|
||||
pub nat_test_server: Vec<SocketAddr>,
|
||||
pub in_ips: Vec<(u32, u32, Ipv4Addr)>,
|
||||
pub out_ips: Vec<(u32, u32, Ipv4Addr)>,
|
||||
pub key: Option<[u8; 32]>,
|
||||
pub password: Option<String>,
|
||||
pub simulate_multicast: bool,
|
||||
pub mtu: Option<u16>,
|
||||
pub tcp: bool,
|
||||
pub ip: Option<Ipv4Addr>,
|
||||
pub relay: bool,
|
||||
}
|
||||
|
||||
|
||||
@@ -337,15 +352,9 @@ impl Config {
|
||||
server_address_str: String,
|
||||
nat_test_server: Vec<SocketAddr>,
|
||||
in_ips: Vec<(u32, u32, Ipv4Addr)>, out_ips: Vec<(u32, u32, Ipv4Addr)>,
|
||||
password: Option<String>, simulate_multicast: bool, mtu: Option<u16>, ) -> Self {
|
||||
let key = if let Some(password) = password {
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
hasher.update(password.as_bytes());
|
||||
let key: [u8; 32] = hasher.finalize().into();
|
||||
Some(key)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
password: Option<String>, simulate_multicast: bool, mtu: Option<u16>, tcp: bool,
|
||||
ip: Option<Ipv4Addr>,
|
||||
relay: bool, ) -> Self {
|
||||
Self {
|
||||
tap,
|
||||
token,
|
||||
@@ -356,9 +365,12 @@ impl Config {
|
||||
nat_test_server,
|
||||
in_ips,
|
||||
out_ips,
|
||||
key,
|
||||
password,
|
||||
simulate_multicast,
|
||||
mtu,
|
||||
tcp,
|
||||
ip,
|
||||
relay,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,22 +4,22 @@ use tokio::sync::watch::{Receiver, Sender};
|
||||
use crate::util::wait::WaitGroup;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub enum SwitchStatus {
|
||||
pub enum VntStatus {
|
||||
Starting,
|
||||
Stopping,
|
||||
}
|
||||
|
||||
pub struct SwitchWorker {
|
||||
pub struct VntWorker {
|
||||
_name: String,
|
||||
wg: WaitGroup,
|
||||
status_s: Arc<Sender<SwitchStatus>>,
|
||||
status_r: Receiver<SwitchStatus>,
|
||||
status_s: Arc<Sender<VntStatus>>,
|
||||
status_r: Receiver<VntStatus>,
|
||||
}
|
||||
|
||||
impl SwitchWorker {
|
||||
impl VntWorker {
|
||||
pub fn worker(&self, name: &str) -> Self {
|
||||
self.wg.add();
|
||||
SwitchWorker {
|
||||
VntWorker {
|
||||
_name: name.to_string(),
|
||||
wg: self.wg.clone(),
|
||||
status_s: self.status_s.clone(),
|
||||
@@ -28,24 +28,24 @@ impl SwitchWorker {
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SwitchWorker {
|
||||
impl Drop for VntWorker {
|
||||
fn drop(&mut self) {
|
||||
self.wg.done();
|
||||
}
|
||||
}
|
||||
|
||||
impl SwitchWorker {
|
||||
impl VntWorker {
|
||||
pub fn stop_all(&self) {
|
||||
let _ = self.status_s.send(SwitchStatus::Stopping);
|
||||
let _ = self.status_s.send(VntStatus::Stopping);
|
||||
}
|
||||
pub async fn stop_wait(&mut self) {
|
||||
loop {
|
||||
if *self.status_r.borrow() == SwitchStatus::Stopping {
|
||||
if *self.status_r.borrow() == VntStatus::Stopping {
|
||||
return;
|
||||
}
|
||||
match self.status_r.changed().await {
|
||||
Ok(_) => {
|
||||
if *self.status_r.borrow() == SwitchStatus::Stopping {
|
||||
if *self.status_r.borrow() == VntStatus::Stopping {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -56,15 +56,15 @@ impl SwitchWorker {
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SwitchStatusManger {
|
||||
pub struct VntStatusManger {
|
||||
wg: WaitGroup,
|
||||
status_s: Arc<Sender<SwitchStatus>>,
|
||||
status_r: Receiver<SwitchStatus>,
|
||||
status_s: Arc<Sender<VntStatus>>,
|
||||
status_r: Receiver<VntStatus>,
|
||||
}
|
||||
|
||||
impl SwitchStatusManger {
|
||||
impl VntStatusManger {
|
||||
pub fn new() -> Self {
|
||||
let (status_s, status_r) = watch::channel(SwitchStatus::Starting);
|
||||
let (status_s, status_r) = watch::channel(VntStatus::Starting);
|
||||
Self {
|
||||
wg: WaitGroup::new(),
|
||||
status_s: Arc::new(status_s),
|
||||
@@ -72,14 +72,14 @@ impl SwitchStatusManger {
|
||||
}
|
||||
}
|
||||
pub fn stop_all(&self) {
|
||||
let _ = self.status_s.send(SwitchStatus::Stopping);
|
||||
let _ = self.status_s.send(VntStatus::Stopping);
|
||||
}
|
||||
pub async fn wait(&mut self) {
|
||||
self.wg.wait().await
|
||||
}
|
||||
pub fn worker(&self, name: &str) -> SwitchWorker {
|
||||
pub fn worker(&self, name: &str) -> VntWorker {
|
||||
self.wg.add();
|
||||
SwitchWorker {
|
||||
VntWorker {
|
||||
_name: name.to_string(),
|
||||
wg: self.wg.clone(),
|
||||
status_s: self.status_s.clone(),
|
||||
@@ -2,68 +2,68 @@ use std::io;
|
||||
use std::ops::Deref;
|
||||
use std::time::Duration;
|
||||
use tokio::runtime::Runtime;
|
||||
use crate::core::{Config, Switch, SwitchUtil};
|
||||
use crate::core::{Config, Vnt, VntUtil};
|
||||
use crate::handle::registration_handler::{RegResponse, ReqEnum};
|
||||
|
||||
pub struct SwitchUtilSync {
|
||||
switch_util: SwitchUtil,
|
||||
pub struct VntUtilSync {
|
||||
vnt_util: VntUtil,
|
||||
runtime: Runtime,
|
||||
}
|
||||
|
||||
pub struct SwitchSync {
|
||||
switch: Switch,
|
||||
pub struct VntSync {
|
||||
vnt: Vnt,
|
||||
runtime: Runtime,
|
||||
}
|
||||
|
||||
impl SwitchUtilSync {
|
||||
pub fn new(config: Config) -> io::Result<SwitchUtilSync> {
|
||||
impl VntUtilSync {
|
||||
pub fn new(config: Config) -> io::Result<VntUtilSync> {
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread().enable_all().build().unwrap();
|
||||
let switch_util = runtime.block_on(SwitchUtil::new(config))?;
|
||||
Ok(SwitchUtilSync {
|
||||
switch_util,
|
||||
let vnt_util = runtime.block_on(VntUtil::new(config))?;
|
||||
Ok(VntUtilSync {
|
||||
vnt_util,
|
||||
runtime,
|
||||
})
|
||||
}
|
||||
pub fn connect(&mut self) -> Result<RegResponse, ReqEnum> {
|
||||
self.runtime.block_on(self.switch_util.connect())
|
||||
self.runtime.block_on(self.vnt_util.connect())
|
||||
}
|
||||
#[cfg(any(target_os = "android"))]
|
||||
pub fn create_iface(&mut self, vpn_fd: i32) {
|
||||
self.switch_util.create_iface(vpn_fd)
|
||||
self.vnt_util.create_iface(vpn_fd)
|
||||
}
|
||||
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
|
||||
pub fn create_iface(&mut self) -> io::Result<crate::tun_tap_device::DriverInfo> {
|
||||
self.switch_util.create_iface()
|
||||
self.vnt_util.create_iface()
|
||||
}
|
||||
pub fn build(self) -> crate::Result<SwitchSync> {
|
||||
pub fn build(self) -> crate::Result<VntSync> {
|
||||
let runtime = self.runtime;
|
||||
let switch = runtime.block_on(self.switch_util.build())?;
|
||||
let vnt = runtime.block_on(self.vnt_util.build())?;
|
||||
{
|
||||
let mut switch = switch.clone();
|
||||
let mut vnt = vnt.clone();
|
||||
std::thread::spawn(move || {
|
||||
runtime.block_on(switch.wait_stop())
|
||||
runtime.block_on(vnt.wait_stop())
|
||||
});
|
||||
}
|
||||
Ok(SwitchSync {
|
||||
switch,
|
||||
Ok(VntSync {
|
||||
vnt,
|
||||
runtime: tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl SwitchSync {
|
||||
impl VntSync {
|
||||
pub fn wait_stop(&mut self) {
|
||||
self.runtime.block_on(self.switch.wait_stop())
|
||||
self.runtime.block_on(self.vnt.wait_stop())
|
||||
}
|
||||
pub fn wait_stop_ms(&mut self, ms: u64) -> bool {
|
||||
self.runtime.block_on(self.switch.wait_stop_ms(Duration::from_millis(ms)))
|
||||
self.runtime.block_on(self.vnt.wait_stop_ms(Duration::from_millis(ms)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for SwitchSync {
|
||||
type Target = Switch;
|
||||
impl Deref for VntSync {
|
||||
type Target = Vnt;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.switch
|
||||
&self.vnt
|
||||
}
|
||||
}
|
||||
@@ -9,14 +9,14 @@ use rand::prelude::SliceRandom;
|
||||
use crate::channel::idle::Idle;
|
||||
use crate::channel::Route;
|
||||
use crate::channel::sender::ChannelSender;
|
||||
use crate::core::status::SwitchWorker;
|
||||
use crate::core::status::VntWorker;
|
||||
|
||||
|
||||
use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo};
|
||||
use crate::protocol::control_packet::PingPacket;
|
||||
use crate::protocol::{control_packet, NetPacket, Protocol, Version};
|
||||
|
||||
pub fn start_idle(mut worker: SwitchWorker, idle: Idle, sender: ChannelSender) {
|
||||
pub fn start_idle(mut worker: VntWorker, idle: Idle, sender: ChannelSender) {
|
||||
tokio::spawn(async move {
|
||||
tokio::select! {
|
||||
_=worker.stop_wait()=>{
|
||||
@@ -45,7 +45,7 @@ async fn start_idle_(idle: Idle, sender: ChannelSender) -> io::Result<()> {
|
||||
}
|
||||
|
||||
pub fn start_heartbeat(
|
||||
mut worker: SwitchWorker,
|
||||
mut worker: VntWorker,
|
||||
sender: ChannelSender,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
@@ -13,9 +13,9 @@ use std::io;
|
||||
use tokio::sync::mpsc::Receiver;
|
||||
use crate::channel::punch::{NatInfo, Punch};
|
||||
use crate::channel::sender::ChannelSender;
|
||||
use crate::core::status::SwitchWorker;
|
||||
use crate::core::status::VntWorker;
|
||||
|
||||
pub fn start(mut worker: SwitchWorker, receiver: Receiver<(Ipv4Addr, NatInfo)>, punch: Punch, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) {
|
||||
pub fn start(mut worker: VntWorker, receiver: Receiver<(Ipv4Addr, NatInfo)>, punch: Punch, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) {
|
||||
tokio::spawn(async move {
|
||||
tokio::select! {
|
||||
_=start0(receiver, punch, current_device)=>{}
|
||||
@@ -54,7 +54,7 @@ async fn start_(
|
||||
}
|
||||
|
||||
pub async fn start_punch(
|
||||
mut worker: SwitchWorker,
|
||||
mut worker: VntWorker,
|
||||
nat_test: NatTest,
|
||||
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
|
||||
sender: ChannelSender,
|
||||
@@ -1,14 +1,10 @@
|
||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||
use std::sync::Arc;
|
||||
use aes_gcm::{AeadInPlace, Aes256Gcm, Nonce, Tag};
|
||||
use aes_gcm::aead::consts::{U12, U16};
|
||||
use aes_gcm::aead::generic_array::GenericArray;
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use crossbeam_skiplist::SkipMap;
|
||||
use parking_lot::Mutex;
|
||||
use protobuf::Message;
|
||||
use tokio::net::UdpSocket;
|
||||
use tokio::sync::mpsc::Sender;
|
||||
|
||||
use packet::icmp::{icmp, Kind};
|
||||
@@ -18,6 +14,7 @@ use packet::ip::ipv4::packet::IpV4Packet;
|
||||
use crate::channel::channel::Context;
|
||||
use crate::channel::punch::{NatInfo, NatType};
|
||||
use crate::channel::{Route, RouteKey};
|
||||
use crate::cipher::Cipher;
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::external_route::ExternalRoute;
|
||||
@@ -47,7 +44,8 @@ pub struct ChannelDataHandler {
|
||||
out_external_route: ExternalRoute,
|
||||
cone_sender: Sender<(Ipv4Addr, NatInfo)>,
|
||||
symmetric_sender: Sender<(Ipv4Addr, NatInfo)>,
|
||||
cipher: Option<Aes256Gcm>,
|
||||
cipher: Cipher,
|
||||
relay: bool,
|
||||
}
|
||||
|
||||
impl ChannelDataHandler {
|
||||
@@ -63,7 +61,8 @@ impl ChannelDataHandler {
|
||||
out_external_route: ExternalRoute,
|
||||
cone_sender: Sender<(Ipv4Addr, NatInfo)>,
|
||||
symmetric_sender: Sender<(Ipv4Addr, NatInfo)>,
|
||||
cipher: Option<Aes256Gcm>, ) -> Self {
|
||||
cipher: Cipher,
|
||||
relay: bool, ) -> Self {
|
||||
Self {
|
||||
current_device,
|
||||
device_list,
|
||||
@@ -78,14 +77,14 @@ impl ChannelDataHandler {
|
||||
cone_sender,
|
||||
symmetric_sender,
|
||||
cipher,
|
||||
relay,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl ChannelDataHandler {
|
||||
pub async fn handle(&mut self, buf: &mut [u8], start: usize, end: usize, route_key: RouteKey, _udp: &Arc<UdpSocket>,
|
||||
context: &Context, ) {
|
||||
pub async fn handle(&mut self, buf: &mut [u8], start: usize, end: usize, route_key: RouteKey, context: &Context) {
|
||||
assert_eq!(start, 14);
|
||||
match self.handle0(&mut buf[..end], &route_key, context).await {
|
||||
Ok(_) => {}
|
||||
@@ -104,7 +103,7 @@ impl ChannelDataHandler {
|
||||
let destination = net_packet.destination();
|
||||
let not_broadcast = !destination.is_broadcast() && !destination.is_multicast() && destination != current_device.broadcast_address;
|
||||
if current_device.virtual_ip() != destination
|
||||
&& not_broadcast
|
||||
&& not_broadcast && !destination.is_unspecified()
|
||||
&& self.connect_status.load() == ConnectStatus::Connected {
|
||||
if !check_dest(source, current_device.virtual_netmask, current_device.virtual_network) {
|
||||
log::warn!("转发数据,源地址错误:{:?},当前网络:{:?},route_key:{:?}",source,current_device.virtual_network,route_key);
|
||||
@@ -150,32 +149,8 @@ impl ChannelDataHandler {
|
||||
return Ok(());
|
||||
}
|
||||
ip_turn_packet::Protocol::Ipv4 => {
|
||||
let data = if let Some(cipher) = &self.cipher {
|
||||
if !net_packet.is_encrypt() {
|
||||
//未加密的数据之间丢弃
|
||||
return Ok(());
|
||||
}
|
||||
if net_packet.payload().len() < 16 {
|
||||
log::error!("数据异常,长度小于16");
|
||||
return Ok(());
|
||||
}
|
||||
//需要解密
|
||||
let mut nonce = [0; 12];
|
||||
nonce[0..4].copy_from_slice(&source.octets());
|
||||
nonce[4..8].copy_from_slice(&destination.octets());
|
||||
nonce[8] = Protocol::IpTurn.into();
|
||||
nonce[9] = ip_turn_packet::Protocol::Ipv4.into();
|
||||
let nonce: &GenericArray<u8, U12> = Nonce::from_slice(&nonce);
|
||||
let data_len = net_packet.payload().len() - 16;
|
||||
let tag: GenericArray<u8, U16> = Tag::clone_from_slice(&net_packet.payload()[data_len..]);
|
||||
match cipher.decrypt_in_place_detached(nonce, &[], &mut net_packet.payload_mut()[..data_len], &tag) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
log::error!("数据解密异常:{}",e);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
&mut net_packet.payload_mut()[..data_len]
|
||||
let data = if let Some(payload_len) = self.cipher.decrypt_ipv4(&mut net_packet)? {
|
||||
&mut net_packet.payload_mut()[..payload_len]
|
||||
} else {
|
||||
net_packet.payload_mut()
|
||||
};
|
||||
@@ -199,30 +174,8 @@ impl ChannelDataHandler {
|
||||
ipv4.update_checksum();
|
||||
net_packet.set_source(destination);
|
||||
net_packet.set_destination(source);
|
||||
if let Some(cipher) = &self.cipher {
|
||||
//需要加密
|
||||
let mut nonce = [0; 12];
|
||||
nonce[0..4].copy_from_slice(&destination.octets());
|
||||
nonce[4..8].copy_from_slice(&source.octets());
|
||||
nonce[8] = Protocol::IpTurn.into();
|
||||
nonce[9] = ip_turn_packet::Protocol::Ipv4.into();
|
||||
let nonce: &GenericArray<u8, U12> = Nonce::from_slice(&nonce);
|
||||
let data_len = net_packet.payload().len() - 16;
|
||||
match cipher.encrypt_in_place_detached(nonce, &[], &mut net_packet.payload_mut()[..data_len]) {
|
||||
Ok(tag) => {
|
||||
if tag.len() != 16 {
|
||||
log::error!("加密tag长度错误:{}",tag.len());
|
||||
return Ok(());
|
||||
}
|
||||
net_packet.set_encrypt_flag(true);
|
||||
net_packet.payload_mut()[data_len..data_len + 16].copy_from_slice(tag.as_slice());
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("加密失败:{}",e);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
//不管加不加密,和接收到的数据长度都一致
|
||||
let _ = self.cipher.encrypt_ipv4(net_packet.payload().len() - 16, &mut net_packet)?;
|
||||
context.send_by_key(net_packet.buffer(), route_key).await?;
|
||||
return Ok(());
|
||||
}
|
||||
@@ -311,10 +264,6 @@ impl ChannelDataHandler {
|
||||
Ok(())
|
||||
}
|
||||
async fn service(&self, context: &Context, current_device: CurrentDeviceInfo, _source: Ipv4Addr, net_packet: NetPacket<&mut [u8]>, route_key: &RouteKey) -> crate::Result<()> {
|
||||
//todo 校验来源
|
||||
if route_key.addr != current_device.connect_server {
|
||||
return Ok(());
|
||||
}
|
||||
match service_packet::Protocol::from(net_packet.transport_protocol()) {
|
||||
service_packet::Protocol::RegistrationRequest => {}
|
||||
service_packet::Protocol::RegistrationResponse => {
|
||||
@@ -378,10 +327,8 @@ impl ChannelDataHandler {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
async fn error(&self, _context: &Context, current_device: CurrentDeviceInfo, _source: Ipv4Addr, net_packet: NetPacket<&mut [u8]>, route_key: &RouteKey) -> crate::Result<()> {
|
||||
if route_key.addr != current_device.connect_server {
|
||||
return Ok(());
|
||||
}
|
||||
async fn error(&self, _context: &Context, current_device: CurrentDeviceInfo, _source: Ipv4Addr, net_packet: NetPacket<&mut [u8]>, _route_key: &RouteKey) -> crate::Result<()> {
|
||||
log::info!("current_device:{:?}",current_device);
|
||||
match InErrorPacket::new(net_packet.transport_protocol(), net_packet.payload())? {
|
||||
InErrorPacket::TokenError => {
|
||||
return Err(Error::Stop("Token error".to_string()));
|
||||
@@ -394,7 +341,7 @@ impl ChannelDataHandler {
|
||||
}
|
||||
|
||||
self.connect_status.store(ConnectStatus::Connecting);
|
||||
self.register.fast_register().await?;
|
||||
self.register.fast_register(current_device.virtual_ip).await?;
|
||||
}
|
||||
InErrorPacket::AddressExhausted => {
|
||||
//地址用尽
|
||||
@@ -403,6 +350,12 @@ impl ChannelDataHandler {
|
||||
InErrorPacket::OtherError(e) => {
|
||||
log::error!("OtherError {:?}", e.message());
|
||||
}
|
||||
InErrorPacket::IpAlreadyExists => {
|
||||
log::error!("IpAlreadyExists");
|
||||
}
|
||||
InErrorPacket::InvalidIp => {
|
||||
log::error!("InvalidIp");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -428,7 +381,7 @@ impl ChannelDataHandler {
|
||||
let rt = (current_time - pong_packet.time()) as i64;
|
||||
let route = Route::from(*route_key, metric, rt);
|
||||
context.add_route(source, route);
|
||||
if route_key.addr == current_device.connect_server && source == current_device.virtual_gateway() {
|
||||
if source == current_device.virtual_gateway() {
|
||||
let epoch = self.device_list.lock().0;
|
||||
if pong_packet.epoch() != epoch {
|
||||
let mut poll_device = NetPacket::new([0; 12])?;
|
||||
@@ -438,11 +391,14 @@ impl ChannelDataHandler {
|
||||
poll_device.first_set_ttl(MAX_TTL);
|
||||
poll_device.set_protocol(Protocol::Service);
|
||||
poll_device.set_transport_protocol(service_packet::Protocol::PollDeviceList.into());
|
||||
context.send_by_key(poll_device.buffer(), route_key).await?;
|
||||
context.send_main(poll_device.buffer(), current_device.connect_server).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
ControlPacket::PunchRequest => {
|
||||
if self.relay {
|
||||
return Ok(());
|
||||
}
|
||||
//回应
|
||||
net_packet.set_transport_protocol(control_packet::Protocol::PunchResponse.into());
|
||||
net_packet.set_source(current_device.virtual_ip());
|
||||
@@ -453,6 +409,9 @@ impl ChannelDataHandler {
|
||||
context.add_route_if_absent(source, route);
|
||||
}
|
||||
ControlPacket::PunchResponse => {
|
||||
if self.relay {
|
||||
return Ok(());
|
||||
}
|
||||
// log::info!("PunchResponse route_key:{:?}",route_key);
|
||||
let route = Route::from(*route_key, metric, 99);
|
||||
context.add_route_if_absent(source, route);
|
||||
@@ -461,6 +420,9 @@ impl ChannelDataHandler {
|
||||
Ok(())
|
||||
}
|
||||
async fn other_turn(&self, context: &Context, current_device: CurrentDeviceInfo, source: Ipv4Addr, net_packet: NetPacket<&mut [u8]>, route_key: &RouteKey) -> crate::Result<()> {
|
||||
if self.relay {
|
||||
return Ok(());
|
||||
}
|
||||
match other_turn_packet::Protocol::from(net_packet.transport_protocol()) {
|
||||
other_turn_packet::Protocol::Punch => {
|
||||
let punch_info = PunchInfo::parse_from_bytes(net_packet.payload())?;
|
||||
@@ -0,0 +1,244 @@
|
||||
use std::io;
|
||||
use std::net::{Ipv4Addr, SocketAddr};
|
||||
use std::time::{Duration, Instant};
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
|
||||
use protobuf::Message;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::{TcpStream, UdpSocket};
|
||||
use crate::channel::sender::ChannelSender;
|
||||
use crate::handle::PeerDeviceInfo;
|
||||
|
||||
use crate::proto::message::{RegistrationRequest, RegistrationResponse};
|
||||
use crate::protocol::error_packet::InErrorPacket;
|
||||
use crate::protocol::{service_packet, NetPacket, Protocol, Version, MAX_TTL};
|
||||
|
||||
pub enum ReqEnum {
|
||||
TokenError,
|
||||
AddressExhausted,
|
||||
IpAlreadyExists,
|
||||
InvalidIp,
|
||||
Timeout,
|
||||
ServerError(String),
|
||||
Other(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RegResponse {
|
||||
pub virtual_ip: Ipv4Addr,
|
||||
pub virtual_gateway: Ipv4Addr,
|
||||
pub virtual_netmask: Ipv4Addr,
|
||||
pub epoch: u16,
|
||||
pub device_info_list: Vec<PeerDeviceInfo>,
|
||||
pub public_ip: Ipv4Addr,
|
||||
pub public_port: u16,
|
||||
}
|
||||
|
||||
///向中继服务器注册,token标识一个虚拟网关,device_id防止多次注册时得到的ip不一致
|
||||
pub async fn registration(
|
||||
main_channel: &UdpSocket,
|
||||
main_tcp_channel: Option<&mut TcpStream>,
|
||||
server_address: SocketAddr,
|
||||
token: String,
|
||||
device_id: String,
|
||||
name: String,
|
||||
ip: Ipv4Addr,
|
||||
) -> Result<RegResponse, ReqEnum> {
|
||||
let request_packet =
|
||||
registration_request_packet(token.clone(), device_id.clone(), name.clone(), ip, false, false).unwrap();
|
||||
let buf = request_packet.buffer();
|
||||
let mut recv_buf = [0u8; 10240];
|
||||
let recv_buf = if let Some(main_tcp_channel) = main_tcp_channel {
|
||||
let mut vec = vec![0; 4 + buf.len()];
|
||||
let len = buf.len();
|
||||
vec[2] = (len >> 8) as u8;
|
||||
vec[3] = (len & 0xFF) as u8;
|
||||
vec[4..].copy_from_slice(buf);
|
||||
if let Err(e) = main_tcp_channel.write_all(&vec).await {
|
||||
return Err(ReqEnum::Other(format!("send error:{}", e)));
|
||||
}
|
||||
if let Err(e) = main_tcp_channel.read_exact(&mut recv_buf[..4]).await {
|
||||
return Err(ReqEnum::Other(format!("read error:{}", e)));
|
||||
}
|
||||
let len = 4 + (((recv_buf[2] as u16) << 8) | recv_buf[3] as u16) as usize;
|
||||
if let Err(e) = main_tcp_channel.read_exact(&mut recv_buf[4..len]).await {
|
||||
return Err(ReqEnum::Other(format!("read error:{}", e)));
|
||||
}
|
||||
&recv_buf[4..len]
|
||||
} else {
|
||||
if let Err(e) = main_channel.send_to(buf, server_address).await {
|
||||
return Err(ReqEnum::Other(format!("send error:{}", e)));
|
||||
}
|
||||
match tokio::time::timeout(Duration::from_millis(300), main_channel.recv_from(&mut recv_buf)).await {
|
||||
Ok(rs) => {
|
||||
match rs {
|
||||
Ok((len, addr)) => {
|
||||
if server_address != addr {
|
||||
return Err(ReqEnum::Other(format!("invalid data,from {}", addr)));
|
||||
}
|
||||
&recv_buf[..len]
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(ReqEnum::Other(format!("receiver error:{}", e)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
return Err(ReqEnum::Timeout);
|
||||
}
|
||||
}
|
||||
};
|
||||
let net_packet = match NetPacket::new(recv_buf) {
|
||||
Ok(net_packet) => {
|
||||
net_packet
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(ReqEnum::ServerError(format!("{}", e)));
|
||||
}
|
||||
};
|
||||
match net_packet.protocol() {
|
||||
Protocol::Service => {
|
||||
match service_packet::Protocol::from(net_packet.transport_protocol()) {
|
||||
service_packet::Protocol::RegistrationResponse => {
|
||||
match RegistrationResponse::parse_from_bytes(net_packet.payload()) {
|
||||
Ok(response) => {
|
||||
let device_info_list: Vec<PeerDeviceInfo> = response
|
||||
.device_info_list
|
||||
.into_iter()
|
||||
.map(|info| {
|
||||
PeerDeviceInfo::new(
|
||||
Ipv4Addr::from(info.virtual_ip),
|
||||
info.name,
|
||||
info.device_status as u8,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
Ok(RegResponse {
|
||||
virtual_ip: Ipv4Addr::from(response.virtual_ip),
|
||||
virtual_gateway: Ipv4Addr::from(response.virtual_gateway),
|
||||
virtual_netmask: Ipv4Addr::from(response.virtual_netmask),
|
||||
epoch: response.epoch as u16,
|
||||
device_info_list,
|
||||
public_ip: Ipv4Addr::from(response.public_ip),
|
||||
public_port: response.public_port as u16,
|
||||
})
|
||||
}
|
||||
Err(_) => {
|
||||
Err(ReqEnum::ServerError("invalid data".to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
Err(ReqEnum::ServerError("invalid data".to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
Protocol::Error => {
|
||||
match InErrorPacket::new(net_packet.transport_protocol(), net_packet.payload()) {
|
||||
Ok(e) => match e {
|
||||
InErrorPacket::TokenError => Err(ReqEnum::TokenError),
|
||||
InErrorPacket::Disconnect => {
|
||||
Err(ReqEnum::ServerError("disconnect".to_string()))
|
||||
}
|
||||
InErrorPacket::AddressExhausted => {
|
||||
Err(ReqEnum::AddressExhausted)
|
||||
}
|
||||
InErrorPacket::OtherError(e) => match e.message() {
|
||||
Ok(str) => {
|
||||
Err(ReqEnum::ServerError(str))
|
||||
}
|
||||
Err(e) => Err(ReqEnum::Other(format!("{}", e))),
|
||||
},
|
||||
InErrorPacket::IpAlreadyExists => {
|
||||
Err(ReqEnum::IpAlreadyExists)
|
||||
}
|
||||
InErrorPacket::InvalidIp => {
|
||||
Err(ReqEnum::InvalidIp)
|
||||
}
|
||||
},
|
||||
Err(e) => Err(ReqEnum::Other(format!("{}", e))),
|
||||
}
|
||||
}
|
||||
_ => Err(ReqEnum::ServerError("invalid data".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn registration_request_packet(
|
||||
token: String,
|
||||
device_id: String,
|
||||
name: String,
|
||||
ip: Ipv4Addr,
|
||||
is_fast: bool,
|
||||
allow_ip_change: bool,
|
||||
) -> crate::Result<NetPacket<Vec<u8>>> {
|
||||
let mut request = RegistrationRequest::new();
|
||||
request.token = token;
|
||||
request.device_id = device_id;
|
||||
request.name = name;
|
||||
request.virtual_ip = ip.into();
|
||||
request.allow_ip_change = allow_ip_change;
|
||||
request.is_fast = is_fast;
|
||||
request.version = "1.1.1".to_string();
|
||||
let bytes = request.write_to_bytes()?;
|
||||
let buf = vec![0u8; 12 + bytes.len()];
|
||||
let mut net_packet = NetPacket::new(buf)?;
|
||||
net_packet.set_version(Version::V1);
|
||||
net_packet.set_protocol(Protocol::Service);
|
||||
net_packet.set_transport_protocol(service_packet::Protocol::RegistrationRequest.into());
|
||||
net_packet.first_set_ttl(MAX_TTL);
|
||||
net_packet.set_payload(&bytes);
|
||||
Ok(net_packet)
|
||||
}
|
||||
|
||||
pub struct Register {
|
||||
sender: ChannelSender,
|
||||
server_address: SocketAddr,
|
||||
token: String,
|
||||
device_id: String,
|
||||
name: String,
|
||||
time: AtomicCell<Instant>,
|
||||
}
|
||||
|
||||
impl Register {
|
||||
pub fn new(
|
||||
sender: ChannelSender,
|
||||
server_address: SocketAddr,
|
||||
token: String,
|
||||
device_id: String,
|
||||
name: String,
|
||||
) -> Self {
|
||||
Self {
|
||||
sender,
|
||||
server_address,
|
||||
token,
|
||||
device_id,
|
||||
name,
|
||||
time: AtomicCell::new(Instant::now()),
|
||||
}
|
||||
}
|
||||
pub async fn fast_register(&self, ip: Ipv4Addr) -> io::Result<()> {
|
||||
let last = self.time.load();
|
||||
if last.elapsed() < Duration::from_secs(2)
|
||||
|| self
|
||||
.time
|
||||
.compare_exchange(last, Instant::now())
|
||||
.is_err()
|
||||
{
|
||||
//短时间不重复注册
|
||||
return Ok(());
|
||||
}
|
||||
log::info!("重新连接");
|
||||
let request_packet = registration_request_packet(
|
||||
self.token.clone(),
|
||||
self.device_id.clone(),
|
||||
self.name.clone(),
|
||||
ip,
|
||||
false,
|
||||
true,
|
||||
)
|
||||
.unwrap();
|
||||
let buf = request_packet.buffer();
|
||||
self.sender.send_main(buf, self.server_address).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,10 @@
|
||||
use std::io;
|
||||
use std::net::{Ipv4Addr, SocketAddrV4};
|
||||
use aes_gcm::{AeadInPlace, Aes256Gcm, Nonce};
|
||||
use aes_gcm::aead::consts::U12;
|
||||
use aes_gcm::aead::generic_array::GenericArray;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
use packet::ip::ipv4::protocol::Protocol;
|
||||
use packet::tcp::tcp::TcpPacket;
|
||||
use packet::udp::udp::UdpPacket;
|
||||
use crate::channel::sender::ChannelSender;
|
||||
use crate::cipher::Cipher;
|
||||
use crate::external_route::ExternalRoute;
|
||||
use crate::handle::{check_dest, CurrentDeviceInfo};
|
||||
use crate::ip_proxy::IpProxyMap;
|
||||
@@ -18,7 +15,7 @@ use crate::protocol;
|
||||
use crate::protocol::ip_turn_packet::BroadcastPacketEnd;
|
||||
|
||||
pub mod tun_handler;
|
||||
#[cfg(any(target_os = "linux", target_os = "macos",target_os = "windows"))]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
|
||||
pub mod tap_handler;
|
||||
|
||||
async fn broadcast(sender: &ChannelSender, net_packet: &mut NetPacket<&mut [u8]>, data_len: usize, current_device: &CurrentDeviceInfo) -> Result<()> {
|
||||
@@ -109,7 +106,7 @@ pub async fn base_handle(sender: &ChannelSender, buf: &mut [u8],
|
||||
mut data_len: usize,//数据总长度=ip长度+12
|
||||
igmp_server: &Option<IgmpServer>,
|
||||
current_device: CurrentDeviceInfo,
|
||||
ip_route: &Option<ExternalRoute>, proxy_map: &Option<IpProxyMap>, cipher: &Option<Aes256Gcm>) -> Result<()> {
|
||||
ip_route: &Option<ExternalRoute>, proxy_map: &Option<IpProxyMap>, cipher: &Cipher) -> Result<()> {
|
||||
let ipv4_packet = IpV4Packet::new(&buf[12..data_len])?;
|
||||
let protocol = ipv4_packet.protocol();
|
||||
let ip_head_len = ipv4_packet.header_len() as usize * 4;
|
||||
@@ -143,9 +140,8 @@ pub async fn base_handle(sender: &ChannelSender, buf: &mut [u8],
|
||||
}
|
||||
Protocol::Udp => {
|
||||
if let Some(igmp_server) = igmp_server {
|
||||
if let Some(cipher) = cipher {
|
||||
//需要加密
|
||||
encrypt(cipher, &mut data_len, &mut net_packet)?;
|
||||
if let Some(len) = cipher.encrypt_ipv4(data_len - 12, &mut net_packet)? {
|
||||
data_len = 12 + len;
|
||||
}
|
||||
multicast(igmp_server, dest_ip, sender, &mut net_packet, data_len, ¤t_device).await?;
|
||||
return Ok(());
|
||||
@@ -163,9 +159,8 @@ pub async fn base_handle(sender: &ChannelSender, buf: &mut [u8],
|
||||
if dest_ip.is_broadcast() || current_device.broadcast_address == dest_ip {
|
||||
// 广播 发送到直连目标
|
||||
if Protocol::Udp == protocol {
|
||||
if let Some(cipher) = cipher {
|
||||
//需要加密
|
||||
encrypt(cipher, &mut data_len, &mut net_packet)?;
|
||||
if let Some(len) = cipher.encrypt_ipv4(data_len - 12, &mut net_packet)? {
|
||||
data_len = 12 + len;
|
||||
}
|
||||
broadcast(sender, &mut net_packet, data_len, ¤t_device).await?;
|
||||
}
|
||||
@@ -224,9 +219,8 @@ pub async fn base_handle(sender: &ChannelSender, buf: &mut [u8],
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if let Some(cipher) = cipher {
|
||||
//需要加密
|
||||
encrypt(cipher, &mut data_len, &mut net_packet)?;
|
||||
if let Some(len) = cipher.encrypt_ipv4(data_len - 12, &mut net_packet)? {
|
||||
data_len = 12 + len;
|
||||
}
|
||||
|
||||
//优先发到直连到地址
|
||||
@@ -235,26 +229,3 @@ pub async fn base_handle(sender: &ChannelSender, buf: &mut [u8],
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
fn encrypt(cipher: &Aes256Gcm, data_len: &mut usize, net_packet: &mut NetPacket<&mut [u8]>) -> io::Result<()> {
|
||||
let mut nonce = [0; 12];
|
||||
nonce[0..4].copy_from_slice(&net_packet.source().octets());
|
||||
nonce[4..8].copy_from_slice(&net_packet.destination().octets());
|
||||
nonce[8] = protocol::Protocol::IpTurn.into();
|
||||
nonce[9] = ip_turn_packet::Protocol::Ipv4.into();
|
||||
let nonce: &GenericArray<u8, U12> = Nonce::from_slice(&nonce);
|
||||
return match cipher.encrypt_in_place_detached(nonce, &[], &mut net_packet.payload_mut()[..*data_len - 12]) {
|
||||
Ok(tag) => {
|
||||
if tag.len() != 16 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("加密tag长度错误:{}", tag.len())));
|
||||
}
|
||||
net_packet.set_encrypt_flag(true);
|
||||
net_packet.payload_mut()[*data_len - 12..*data_len - 12 + 16].copy_from_slice(tag.as_slice());
|
||||
*data_len += 16;
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
Err(io::Error::new(io::ErrorKind::Other, format!("加密失败:{}", e)))
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
use std::sync::Arc;
|
||||
use std::{io, thread};
|
||||
use aes_gcm::Aes256Gcm;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
|
||||
use packet::arp::arp::ArpPacket;
|
||||
use packet::ethernet;
|
||||
use packet::ethernet::packet::EthernetPacket;
|
||||
@@ -9,22 +10,24 @@ use packet::icmp::icmp::IcmpPacket;
|
||||
use packet::icmp::Kind;
|
||||
use packet::ip::ipv4;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
|
||||
use crate::channel::sender::ChannelSender;
|
||||
use crate::core::status::SwitchWorker;
|
||||
use crate::cipher::Cipher;
|
||||
use crate::core::status::VntWorker;
|
||||
use crate::external_route::ExternalRoute;
|
||||
use crate::handle::CurrentDeviceInfo;
|
||||
use crate::igmp_server::IgmpServer;
|
||||
use crate::ip_proxy::IpProxyMap;
|
||||
use crate::tun_tap_device::{DeviceReader, DeviceWriter};
|
||||
|
||||
pub fn start(worker: SwitchWorker, sender: ChannelSender,
|
||||
pub fn start(worker: VntWorker, sender: ChannelSender,
|
||||
device_reader: DeviceReader,
|
||||
device_writer: DeviceWriter,
|
||||
igmp_server: Option<IgmpServer>,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
ip_route: Option<ExternalRoute>,
|
||||
ip_proxy_map: Option<IpProxyMap>,
|
||||
cipher: Option<Aes256Gcm>) {
|
||||
cipher: Cipher) {
|
||||
thread::Builder::new().name("tap_handler".into()).spawn(move || {
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all().build().unwrap()
|
||||
@@ -46,7 +49,7 @@ async fn start_(sender: ChannelSender,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
ip_route: Option<ExternalRoute>,
|
||||
ip_proxy_map: Option<IpProxyMap>,
|
||||
cipher: Option<Aes256Gcm>) -> io::Result<()> {
|
||||
cipher: Cipher) -> io::Result<()> {
|
||||
let mut buf = [0; 4096];
|
||||
loop {
|
||||
//ip拆包了会直接丢弃?
|
||||
@@ -58,7 +61,7 @@ async fn start_(sender: ChannelSender,
|
||||
}
|
||||
|
||||
async fn handle(buf: &mut [u8], len: usize, igmp_server: &Option<IgmpServer>, current_device: &AtomicCell<CurrentDeviceInfo>,
|
||||
device_writer: &DeviceWriter, sender: &ChannelSender, ip_route: &Option<ExternalRoute>, proxy_map: &Option<IpProxyMap>, cipher: &Option<Aes256Gcm>) -> crate::Result<()> {
|
||||
device_writer: &DeviceWriter, sender: &ChannelSender, ip_route: &Option<ExternalRoute>, proxy_map: &Option<IpProxyMap>, cipher: &Cipher) -> crate::Result<()> {
|
||||
let mut ethernet_packet = EthernetPacket::new(&mut buf[..len])?;
|
||||
let current_device = current_device.load();
|
||||
match ethernet_packet.protocol() {
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::{io, thread};
|
||||
use std::sync::Arc;
|
||||
use aes_gcm::Aes256Gcm;
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
|
||||
@@ -9,7 +8,8 @@ use packet::icmp::icmp::IcmpPacket;
|
||||
use packet::ip::ipv4;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
use crate::channel::sender::ChannelSender;
|
||||
use crate::core::status::SwitchWorker;
|
||||
use crate::cipher::Cipher;
|
||||
use crate::core::status::VntWorker;
|
||||
|
||||
use crate::error::*;
|
||||
use crate::external_route::ExternalRoute;
|
||||
@@ -37,7 +37,7 @@ fn icmp(device_writer: &DeviceWriter, mut ipv4_packet: IpV4Packet<&mut [u8]>) ->
|
||||
/// 接收tun数据,并且转发到udp上
|
||||
#[inline]
|
||||
async fn handle(sender: &ChannelSender, data: &mut [u8], len: usize, device_writer: &DeviceWriter, igmp_server: &Option<IgmpServer>, current_device: CurrentDeviceInfo,
|
||||
ip_route: &Option<ExternalRoute>, proxy_map: &Option<IpProxyMap>, cipher: &Option<Aes256Gcm>) -> Result<()> {
|
||||
ip_route: &Option<ExternalRoute>, proxy_map: &Option<IpProxyMap>, cipher: &Cipher) -> Result<()> {
|
||||
let ipv4_packet = if let Ok(ipv4_packet) = IpV4Packet::new(&mut data[12..len]) {
|
||||
ipv4_packet
|
||||
} else {
|
||||
@@ -54,14 +54,14 @@ async fn handle(sender: &ChannelSender, data: &mut [u8], len: usize, device_writ
|
||||
return crate::handle::tun_tap::base_handle(sender, data, len, igmp_server, current_device, ip_route, proxy_map, cipher).await;
|
||||
}
|
||||
|
||||
pub fn start(worker: SwitchWorker, sender: ChannelSender,
|
||||
pub fn start(worker: VntWorker, sender: ChannelSender,
|
||||
device_reader: DeviceReader,
|
||||
device_writer: DeviceWriter,
|
||||
igmp_server: Option<IgmpServer>,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
ip_route: Option<ExternalRoute>,
|
||||
ip_proxy_map: Option<IpProxyMap>,
|
||||
cipher: Option<Aes256Gcm>) {
|
||||
cipher: Cipher) {
|
||||
thread::Builder::new().name("tun_handler".into()).spawn(move || {
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all().build().unwrap()
|
||||
@@ -82,7 +82,7 @@ async fn start_(sender: ChannelSender,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
ip_route: Option<ExternalRoute>,
|
||||
ip_proxy_map: Option<IpProxyMap>,
|
||||
cipher: Option<Aes256Gcm>) -> io::Result<()> {
|
||||
cipher: Cipher) -> io::Result<()> {
|
||||
let mut buf = [0; 4096];
|
||||
loop {
|
||||
if sender.is_close() {
|
||||
@@ -57,7 +57,7 @@ impl IcmpProxy {
|
||||
current_device,
|
||||
})
|
||||
}
|
||||
pub fn icmp_socket(&self) ->Arc<Socket>{
|
||||
pub fn icmp_socket(&self) -> Arc<Socket> {
|
||||
self.icmp_socket.clone()
|
||||
}
|
||||
pub fn start(self) {
|
||||
@@ -85,12 +85,16 @@ impl IcmpProxy {
|
||||
let dest_ip = *entry.value();
|
||||
ipv4_packet.set_destination_ip(dest_ip);
|
||||
ipv4_packet.update_checksum();
|
||||
let virtual_ip = self.current_device.load().virtual_ip();
|
||||
let current_device = self.current_device.load();
|
||||
let virtual_ip = current_device.virtual_ip();
|
||||
let connect_server = current_device.connect_server;
|
||||
net_packet.set_source(virtual_ip);
|
||||
net_packet.set_destination(dest_ip);
|
||||
let data_len = ipv4_packet.buffer.len();
|
||||
net_packet.set_payload(ipv4_packet.buffer);
|
||||
let _ = self.sender.try_send_by_id(&net_packet.buffer()[..(12 + data_len)], &dest_ip);
|
||||
if self.sender.try_send_by_id(&net_packet.buffer()[..(12 + data_len)], &dest_ip).is_err() {
|
||||
let _ = self.sender.try_send_main(&net_packet.buffer()[..(12 + data_len)], connect_server);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
@@ -14,3 +14,4 @@ pub mod tun_tap_device;
|
||||
pub mod core;
|
||||
pub mod channel;
|
||||
pub mod util;
|
||||
pub mod cipher;
|
||||
@@ -39,6 +39,10 @@ pub struct RegistrationRequest {
|
||||
pub is_fast: bool,
|
||||
// @@protoc_insertion_point(field:RegistrationRequest.version)
|
||||
pub version: ::std::string::String,
|
||||
// @@protoc_insertion_point(field:RegistrationRequest.virtual_ip)
|
||||
pub virtual_ip: u32,
|
||||
// @@protoc_insertion_point(field:RegistrationRequest.allow_ip_change)
|
||||
pub allow_ip_change: bool,
|
||||
// special fields
|
||||
// @@protoc_insertion_point(special_field:RegistrationRequest.special_fields)
|
||||
pub special_fields: ::protobuf::SpecialFields,
|
||||
@@ -56,7 +60,7 @@ impl RegistrationRequest {
|
||||
}
|
||||
|
||||
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
|
||||
let mut fields = ::std::vec::Vec::with_capacity(5);
|
||||
let mut fields = ::std::vec::Vec::with_capacity(7);
|
||||
let mut oneofs = ::std::vec::Vec::with_capacity(0);
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"token",
|
||||
@@ -83,6 +87,16 @@ impl RegistrationRequest {
|
||||
|m: &RegistrationRequest| { &m.version },
|
||||
|m: &mut RegistrationRequest| { &mut m.version },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"virtual_ip",
|
||||
|m: &RegistrationRequest| { &m.virtual_ip },
|
||||
|m: &mut RegistrationRequest| { &mut m.virtual_ip },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"allow_ip_change",
|
||||
|m: &RegistrationRequest| { &m.allow_ip_change },
|
||||
|m: &mut RegistrationRequest| { &mut m.allow_ip_change },
|
||||
));
|
||||
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<RegistrationRequest>(
|
||||
"RegistrationRequest",
|
||||
fields,
|
||||
@@ -116,6 +130,12 @@ impl ::protobuf::Message for RegistrationRequest {
|
||||
42 => {
|
||||
self.version = is.read_string()?;
|
||||
},
|
||||
53 => {
|
||||
self.virtual_ip = is.read_fixed32()?;
|
||||
},
|
||||
56 => {
|
||||
self.allow_ip_change = is.read_bool()?;
|
||||
},
|
||||
tag => {
|
||||
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
|
||||
},
|
||||
@@ -143,6 +163,12 @@ impl ::protobuf::Message for RegistrationRequest {
|
||||
if !self.version.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(5, &self.version);
|
||||
}
|
||||
if self.virtual_ip != 0 {
|
||||
my_size += 1 + 4;
|
||||
}
|
||||
if self.allow_ip_change != false {
|
||||
my_size += 1 + 1;
|
||||
}
|
||||
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
|
||||
self.special_fields.cached_size().set(my_size as u32);
|
||||
my_size
|
||||
@@ -164,6 +190,12 @@ impl ::protobuf::Message for RegistrationRequest {
|
||||
if !self.version.is_empty() {
|
||||
os.write_string(5, &self.version)?;
|
||||
}
|
||||
if self.virtual_ip != 0 {
|
||||
os.write_fixed32(6, self.virtual_ip)?;
|
||||
}
|
||||
if self.allow_ip_change != false {
|
||||
os.write_bool(7, self.allow_ip_change)?;
|
||||
}
|
||||
os.write_unknown_fields(self.special_fields.unknown_fields())?;
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
@@ -186,6 +218,8 @@ impl ::protobuf::Message for RegistrationRequest {
|
||||
self.name.clear();
|
||||
self.is_fast = false;
|
||||
self.version.clear();
|
||||
self.virtual_ip = 0;
|
||||
self.allow_ip_change = false;
|
||||
self.special_fields.clear();
|
||||
}
|
||||
|
||||
@@ -196,6 +230,8 @@ impl ::protobuf::Message for RegistrationRequest {
|
||||
name: ::std::string::String::new(),
|
||||
is_fast: false,
|
||||
version: ::std::string::String::new(),
|
||||
virtual_ip: 0,
|
||||
allow_ip_change: false,
|
||||
special_fields: ::protobuf::SpecialFields::new(),
|
||||
};
|
||||
&instance
|
||||
@@ -237,6 +273,8 @@ pub struct RegistrationResponse {
|
||||
pub public_ip: u32,
|
||||
// @@protoc_insertion_point(field:RegistrationResponse.public_port)
|
||||
pub public_port: u32,
|
||||
// @@protoc_insertion_point(field:RegistrationResponse.public_ipv6)
|
||||
pub public_ipv6: ::std::vec::Vec<u8>,
|
||||
// special fields
|
||||
// @@protoc_insertion_point(special_field:RegistrationResponse.special_fields)
|
||||
pub special_fields: ::protobuf::SpecialFields,
|
||||
@@ -254,7 +292,7 @@ impl RegistrationResponse {
|
||||
}
|
||||
|
||||
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
|
||||
let mut fields = ::std::vec::Vec::with_capacity(7);
|
||||
let mut fields = ::std::vec::Vec::with_capacity(8);
|
||||
let mut oneofs = ::std::vec::Vec::with_capacity(0);
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"virtual_ip",
|
||||
@@ -291,6 +329,11 @@ impl RegistrationResponse {
|
||||
|m: &RegistrationResponse| { &m.public_port },
|
||||
|m: &mut RegistrationResponse| { &mut m.public_port },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"public_ipv6",
|
||||
|m: &RegistrationResponse| { &m.public_ipv6 },
|
||||
|m: &mut RegistrationResponse| { &mut m.public_ipv6 },
|
||||
));
|
||||
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<RegistrationResponse>(
|
||||
"RegistrationResponse",
|
||||
fields,
|
||||
@@ -330,6 +373,9 @@ impl ::protobuf::Message for RegistrationResponse {
|
||||
56 => {
|
||||
self.public_port = is.read_uint32()?;
|
||||
},
|
||||
66 => {
|
||||
self.public_ipv6 = is.read_bytes()?;
|
||||
},
|
||||
tag => {
|
||||
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
|
||||
},
|
||||
@@ -364,6 +410,9 @@ impl ::protobuf::Message for RegistrationResponse {
|
||||
if self.public_port != 0 {
|
||||
my_size += ::protobuf::rt::uint32_size(7, self.public_port);
|
||||
}
|
||||
if !self.public_ipv6.is_empty() {
|
||||
my_size += ::protobuf::rt::bytes_size(8, &self.public_ipv6);
|
||||
}
|
||||
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
|
||||
self.special_fields.cached_size().set(my_size as u32);
|
||||
my_size
|
||||
@@ -391,6 +440,9 @@ impl ::protobuf::Message for RegistrationResponse {
|
||||
if self.public_port != 0 {
|
||||
os.write_uint32(7, self.public_port)?;
|
||||
}
|
||||
if !self.public_ipv6.is_empty() {
|
||||
os.write_bytes(8, &self.public_ipv6)?;
|
||||
}
|
||||
os.write_unknown_fields(self.special_fields.unknown_fields())?;
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
@@ -415,6 +467,7 @@ impl ::protobuf::Message for RegistrationResponse {
|
||||
self.device_info_list.clear();
|
||||
self.public_ip = 0;
|
||||
self.public_port = 0;
|
||||
self.public_ipv6.clear();
|
||||
self.special_fields.clear();
|
||||
}
|
||||
|
||||
@@ -427,6 +480,7 @@ impl ::protobuf::Message for RegistrationResponse {
|
||||
device_info_list: ::std::vec::Vec::new(),
|
||||
public_ip: 0,
|
||||
public_port: 0,
|
||||
public_ipv6: ::std::vec::Vec::new(),
|
||||
special_fields: ::protobuf::SpecialFields::new(),
|
||||
};
|
||||
&instance
|
||||
@@ -767,6 +821,8 @@ pub struct PunchInfo {
|
||||
pub local_ip: u32,
|
||||
// @@protoc_insertion_point(field:PunchInfo.local_port)
|
||||
pub local_port: u32,
|
||||
// @@protoc_insertion_point(field:PunchInfo.public_ipv6_list)
|
||||
pub public_ipv6_list: ::std::vec::Vec<::std::vec::Vec<u8>>,
|
||||
// special fields
|
||||
// @@protoc_insertion_point(special_field:PunchInfo.special_fields)
|
||||
pub special_fields: ::protobuf::SpecialFields,
|
||||
@@ -784,7 +840,7 @@ impl PunchInfo {
|
||||
}
|
||||
|
||||
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
|
||||
let mut fields = ::std::vec::Vec::with_capacity(7);
|
||||
let mut fields = ::std::vec::Vec::with_capacity(8);
|
||||
let mut oneofs = ::std::vec::Vec::with_capacity(0);
|
||||
fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>(
|
||||
"public_ip_list",
|
||||
@@ -821,6 +877,11 @@ impl PunchInfo {
|
||||
|m: &PunchInfo| { &m.local_port },
|
||||
|m: &mut PunchInfo| { &mut m.local_port },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>(
|
||||
"public_ipv6_list",
|
||||
|m: &PunchInfo| { &m.public_ipv6_list },
|
||||
|m: &mut PunchInfo| { &mut m.public_ipv6_list },
|
||||
));
|
||||
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<PunchInfo>(
|
||||
"PunchInfo",
|
||||
fields,
|
||||
@@ -863,6 +924,9 @@ impl ::protobuf::Message for PunchInfo {
|
||||
64 => {
|
||||
self.local_port = is.read_uint32()?;
|
||||
},
|
||||
74 => {
|
||||
self.public_ipv6_list.push(is.read_bytes()?);
|
||||
},
|
||||
tag => {
|
||||
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
|
||||
},
|
||||
@@ -894,6 +958,9 @@ impl ::protobuf::Message for PunchInfo {
|
||||
if self.local_port != 0 {
|
||||
my_size += ::protobuf::rt::uint32_size(8, self.local_port);
|
||||
}
|
||||
for value in &self.public_ipv6_list {
|
||||
my_size += ::protobuf::rt::bytes_size(9, &value);
|
||||
};
|
||||
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
|
||||
self.special_fields.cached_size().set(my_size as u32);
|
||||
my_size
|
||||
@@ -921,6 +988,9 @@ impl ::protobuf::Message for PunchInfo {
|
||||
if self.local_port != 0 {
|
||||
os.write_uint32(8, self.local_port)?;
|
||||
}
|
||||
for v in &self.public_ipv6_list {
|
||||
os.write_bytes(9, &v)?;
|
||||
};
|
||||
os.write_unknown_fields(self.special_fields.unknown_fields())?;
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
@@ -945,6 +1015,7 @@ impl ::protobuf::Message for PunchInfo {
|
||||
self.reply = false;
|
||||
self.local_ip = 0;
|
||||
self.local_port = 0;
|
||||
self.public_ipv6_list.clear();
|
||||
self.special_fields.clear();
|
||||
}
|
||||
|
||||
@@ -957,6 +1028,7 @@ impl ::protobuf::Message for PunchInfo {
|
||||
reply: false,
|
||||
local_ip: 0,
|
||||
local_port: 0,
|
||||
public_ipv6_list: ::std::vec::Vec::new(),
|
||||
special_fields: ::protobuf::SpecialFields::new(),
|
||||
};
|
||||
&instance
|
||||
@@ -1035,29 +1107,33 @@ impl PunchNatType {
|
||||
}
|
||||
|
||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
||||
\n\rmessage.proto\"\x8f\x01\n\x13RegistrationRequest\x12\x14\n\x05token\
|
||||
\n\rmessage.proto\"\xd6\x01\n\x13RegistrationRequest\x12\x14\n\x05token\
|
||||
\x18\x01\x20\x01(\tR\x05token\x12\x1b\n\tdevice_id\x18\x02\x20\x01(\tR\
|
||||
\x08deviceId\x12\x12\n\x04name\x18\x03\x20\x01(\tR\x04name\x12\x17\n\x07\
|
||||
is_fast\x18\x04\x20\x01(\x08R\x06isFast\x12\x18\n\x07version\x18\x05\x20\
|
||||
\x01(\tR\x07version\"\x92\x02\n\x14RegistrationResponse\x12\x1d\n\nvirtu\
|
||||
al_ip\x18\x01\x20\x01(\x07R\tvirtualIp\x12'\n\x0fvirtual_gateway\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\x05e\
|
||||
poch\x125\n\x10device_info_list\x18\x05\x20\x03(\x0b2\x0b.DeviceInfoR\
|
||||
\x0edeviceInfoList\x12\x1b\n\tpublic_ip\x18\x06\x20\x01(\x07R\x08publicI\
|
||||
p\x12\x1f\n\x0bpublic_port\x18\x07\x20\x01(\rR\npublicPort\"d\n\nDeviceI\
|
||||
nfo\x12\x12\n\x04name\x18\x01\x20\x01(\tR\x04name\x12\x1d\n\nvirtual_ip\
|
||||
\x18\x02\x20\x01(\x07R\tvirtualIp\x12#\n\rdevice_status\x18\x03\x20\x01(\
|
||||
\rR\x0cdeviceStatus\"Y\n\nDeviceList\x12\x14\n\x05epoch\x18\x01\x20\x01(\
|
||||
\rR\x05epoch\x125\n\x10device_info_list\x18\x02\x20\x03(\x0b2\x0b.Device\
|
||||
InfoR\x0edeviceInfoList\"\xf8\x01\n\tPunchInfo\x12$\n\x0epublic_ip_list\
|
||||
\x18\x02\x20\x03(\x07R\x0cpublicIpList\x12\x1f\n\x0bpublic_port\x18\x03\
|
||||
\x20\x01(\rR\npublicPort\x12*\n\x11public_port_range\x18\x04\x20\x01(\rR\
|
||||
\x0fpublicPortRange\x12(\n\x08nat_type\x18\x05\x20\x01(\x0e2\r.PunchNatT\
|
||||
ypeR\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_por\
|
||||
t\x18\x08\x20\x01(\rR\tlocalPort*'\n\x0cPunchNatType\x12\r\n\tSymmetric\
|
||||
\x10\0\x12\x08\n\x04Cone\x10\x01b\x06proto3\
|
||||
\x01(\tR\x07version\x12\x1d\n\nvirtual_ip\x18\x06\x20\x01(\x07R\tvirtual\
|
||||
Ip\x12&\n\x0fallow_ip_change\x18\x07\x20\x01(\x08R\rallowIpChange\"\xb3\
|
||||
\x02\n\x14RegistrationResponse\x12\x1d\n\nvirtual_ip\x18\x01\x20\x01(\
|
||||
\x07R\tvirtualIp\x12'\n\x0fvirtual_gateway\x18\x02\x20\x01(\x07R\x0evirt\
|
||||
ualGateway\x12'\n\x0fvirtual_netmask\x18\x03\x20\x01(\x07R\x0evirtualNet\
|
||||
mask\x12\x14\n\x05epoch\x18\x04\x20\x01(\rR\x05epoch\x125\n\x10device_in\
|
||||
fo_list\x18\x05\x20\x03(\x0b2\x0b.DeviceInfoR\x0edeviceInfoList\x12\x1b\
|
||||
\n\tpublic_ip\x18\x06\x20\x01(\x07R\x08publicIp\x12\x1f\n\x0bpublic_port\
|
||||
\x18\x07\x20\x01(\rR\npublicPort\x12\x1f\n\x0bpublic_ipv6\x18\x08\x20\
|
||||
\x01(\x0cR\npublicIpv6\"d\n\nDeviceInfo\x12\x12\n\x04name\x18\x01\x20\
|
||||
\x01(\tR\x04name\x12\x1d\n\nvirtual_ip\x18\x02\x20\x01(\x07R\tvirtualIp\
|
||||
\x12#\n\rdevice_status\x18\x03\x20\x01(\rR\x0cdeviceStatus\"Y\n\nDeviceL\
|
||||
ist\x12\x14\n\x05epoch\x18\x01\x20\x01(\rR\x05epoch\x125\n\x10device_inf\
|
||||
o_list\x18\x02\x20\x03(\x0b2\x0b.DeviceInfoR\x0edeviceInfoList\"\xa2\x02\
|
||||
\n\tPunchInfo\x12$\n\x0epublic_ip_list\x18\x02\x20\x03(\x07R\x0cpublicIp\
|
||||
List\x12\x1f\n\x0bpublic_port\x18\x03\x20\x01(\rR\npublicPort\x12*\n\x11\
|
||||
public_port_range\x18\x04\x20\x01(\rR\x0fpublicPortRange\x12(\n\x08nat_t\
|
||||
ype\x18\x05\x20\x01(\x0e2\r.PunchNatTypeR\x07natType\x12\x14\n\x05reply\
|
||||
\x18\x06\x20\x01(\x08R\x05reply\x12\x19\n\x08local_ip\x18\x07\x20\x01(\
|
||||
\x07R\x07localIp\x12\x1d\n\nlocal_port\x18\x08\x20\x01(\rR\tlocalPort\
|
||||
\x12(\n\x10public_ipv6_list\x18\t\x20\x03(\x0cR\x0epublicIpv6List*'\n\
|
||||
\x0cPunchNatType\x12\r\n\tSymmetric\x10\0\x12\x08\n\x04Cone\x10\x01b\x06\
|
||||
proto3\
|
||||
";
|
||||
|
||||
/// `FileDescriptorProto` object which was a source for this generated file
|
||||
@@ -43,7 +43,6 @@ impl Into<u8> for Protocol {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ControlPacket<B> {
|
||||
PingPacket(PingPacket<B>),
|
||||
PongPacket(PongPacket<B>),
|
||||
@@ -5,6 +5,8 @@ pub enum Protocol {
|
||||
TokenError,
|
||||
Disconnect,
|
||||
AddressExhausted,
|
||||
IpAlreadyExists,
|
||||
InvalidIp,
|
||||
Other(u8),
|
||||
}
|
||||
|
||||
@@ -14,6 +16,8 @@ impl From<u8> for Protocol {
|
||||
1 => Self::TokenError,
|
||||
2 => Self::Disconnect,
|
||||
3 => Self::AddressExhausted,
|
||||
4 => Self::IpAlreadyExists,
|
||||
5 => Self::InvalidIp,
|
||||
val => Self::Other(val),
|
||||
}
|
||||
}
|
||||
@@ -25,6 +29,8 @@ impl Into<u8> for Protocol {
|
||||
Protocol::TokenError => 1,
|
||||
Protocol::Disconnect => 2,
|
||||
Protocol::AddressExhausted => 3,
|
||||
Protocol::IpAlreadyExists => 4,
|
||||
Protocol::InvalidIp => 5,
|
||||
Protocol::Other(val) => val,
|
||||
}
|
||||
}
|
||||
@@ -34,6 +40,8 @@ pub enum InErrorPacket<B> {
|
||||
TokenError,
|
||||
Disconnect,
|
||||
AddressExhausted,
|
||||
IpAlreadyExists,
|
||||
InvalidIp,
|
||||
OtherError(ErrorPacket<B>),
|
||||
}
|
||||
|
||||
@@ -43,6 +51,8 @@ impl<B: AsRef<[u8]>> InErrorPacket<B> {
|
||||
Protocol::TokenError => Ok(InErrorPacket::TokenError),
|
||||
Protocol::Disconnect => Ok(InErrorPacket::Disconnect),
|
||||
Protocol::AddressExhausted => Ok(InErrorPacket::AddressExhausted),
|
||||
Protocol::IpAlreadyExists => Ok(InErrorPacket::IpAlreadyExists),
|
||||
Protocol::InvalidIp => Ok(InErrorPacket::InvalidIp),
|
||||
Protocol::Other(_) => Ok(InErrorPacket::OtherError(ErrorPacket::new(buffer)?)),
|
||||
}
|
||||
}
|
||||
@@ -10,9 +10,9 @@ use packet::ethernet::packet::EthernetPacket;
|
||||
use win_tun_tap::{IFace, TapDevice, TunDevice};
|
||||
use crate::tun_tap_device::{DriverInfo, DeviceType};
|
||||
|
||||
pub const TUN_INTERFACE_NAME: &str = "Switch-Tun-V1";
|
||||
pub const TUN_POOL_NAME: &str = "Switch-Tun-V1";
|
||||
pub const TAP_INTERFACE_NAME: &str = "Switch-Tap-V1";
|
||||
pub const TUN_INTERFACE_NAME: &str = "Vnt-Tun-V1";
|
||||
pub const TUN_POOL_NAME: &str = "Vnt-Tun-V1";
|
||||
pub const TAP_INTERFACE_NAME: &str = "Vnt-Tap-V1";
|
||||
|
||||
pub enum Device {
|
||||
Tun(TunDevice),
|
||||