Compare commits

...
16 Commits
Author SHA1 Message Date
lubeilin 6cf71c8068 Merge remote-tracking branch 'origin/main' 2023-07-18 23:32:34 +08:00
lubeilin 8e556a20ee 打包排除源码 2023-07-18 23:32:10 +08:00
lbl8603 5f28a5044a Update README.md 2023-07-18 22:27:22 +08:00
lbl8603 16d295bf32 Update README.md 2023-07-18 20:42:09 +08:00
lbl8603 32163e077e Update README.md 2023-07-18 17:37:17 +08:00
lbl8603 6a5a40a7d3 Update README.md 2023-07-18 17:19:34 +08:00
lbl8603 161ecc865c Update README.md 2023-07-18 17:17:30 +08:00
lubeilin 1a5d40ff2d 修复mac端问题 2023-07-17 23:04:21 +08:00
lubeilin 6b988e0612 1.1.0 2023-07-17 22:56:37 +08:00
lubeilin 8d44934382 修改后台命令 2023-07-17 08:42:42 +08:00
lubeilin 6b140b0f71 增加桌面端 2023-07-17 01:32:34 +08:00
lubeilin c2b7b02f3f 调整jni模块、优化cmd模块展示 2023-07-17 01:31:05 +08:00
lubeilin 24140c2145 增加精简客户端模块,适用于服务端、路由器 2023-07-05 23:38:02 +08:00
lubeilin a6050e5f59 增加jni模块 2023-07-05 23:37:30 +08:00
lubeilin 50e97fd95f 增加安卓端支持、优化广播、增加停止监听 2023-07-05 23:36:41 +08:00
lubeilin 890e5f7391 默认使用广播代替组播、精简依赖 2023-06-29 23:14:59 +08:00
97 changed files with 3391 additions and 2911 deletions
+6 -6
View File
@@ -65,7 +65,7 @@ jobs:
# needs: test
runs-on: ${{ matrix.OS }}
env:
NAME: switch-desktop # change with the name of your project
NAME: switch-cmd # change with the name of your project
TARGET: ${{ matrix.TARGET }}
OS: ${{ matrix.OS }}
steps:
@@ -106,7 +106,7 @@ jobs:
- name: Install rust target
run: rustup target add $TARGET
- name: Run build
run: cargo build --package switch-desktop --release --verbose --target $TARGET
run: cargo build --package switch-cmd --release --verbose --target $TARGET
- name: List target
run: find ./target
- name: Compress
@@ -123,12 +123,12 @@ jobs:
else
TAG=$GITHUB_SHA
fi
mv ./target/$TARGET/release/$EXEC ./$EXEC
tar -czf ./artifacts/$NAME-$TARGET-$TAG.tar.gz $EXEC
mv ./target/$TARGET/release/$EXEC ./artifacts/$EXEC
tar -czf ./artifacts/$NAME-$TARGET-$TAG.tar.gz -C ./artifacts $EXEC
- name: Archive artifact
uses: actions/upload-artifact@v2
with:
name: switch-desktop
name: switch-cmd
path: |
./artifacts
# deploys to github releases on tag
@@ -140,7 +140,7 @@ jobs:
- name: Download artifacts
uses: actions/download-artifact@v2
with:
name: switch-desktop
name: switch-cmd
path: ./artifacts
- name: List
run: find ./artifacts
+1 -1
View File
@@ -1,5 +1,5 @@
[workspace]
members = ["switch","switch-desktop"]
members = ["switch","common","switch-desktop","switch-cmd","switch-jni"]
[profile.release]
opt-level = 'z'
+40 -33
View File
@@ -1,23 +1,23 @@
# switch
A virtual network tool (VPN)
A virtual network tool (VPN)
将不同网络下的多个设备虚拟到一个局域网下
### switch-cmd参数详解 [参数说明](https://github.com/lbl8603/switch/blob/main/switch-cmd/README.md)
### 快速使用:
1. 指定一个token,在多台设备上运行该程序,例如:
```shell
# linux上
root@DESKTOP-0BCHNIO:/opt# ./switch-desktop start --token 123456
root@DESKTOP-0BCHNIO:/opt# ./switch-cmd -k 123456
# 在另一台linux上使用nohup后台运行
[root@izj6cemne76ykdzkataftfz switch]# nohup ./switch-desktop start --token 123456 &
root@izj6cemne76ykdzkataftfz switch# nohup ./switch-cmd -k 123456 &
# windows上
D:\switch\bin_v1>switch-desktop.exe start --token 123456
D:\switch\bin_v1>switch-cmd.exe -k 123456
```
2. 可以执行status命令查看当前设备的虚拟ip
2. 可以执行info命令查看当前设备的虚拟ip
```shell
root@DESKTOP-0BCHNIO:/opt# ./switch-desktop status
root@DESKTOP-0BCHNIO:/opt# ./switch-cmd --info
Name: Ubuntu 18.04 (bionic) [64-bit]
Virtual ip: 10.26.0.2
Virtual gateway: 10.26.0.1
@@ -30,17 +30,13 @@
```
3. 也可以执行list命令查看其他设备的虚拟ip
```shell
root@DESKTOP-0BCHNIO:/opt# ./switch-desktop list
root@DESKTOP-0BCHNIO:/opt# ./switch-cmd --list
Name Virtual Ip P2P/Relay Rt Status
Windows 10.0.22621 (Windows 11 Professional) [64-bit] 10.26.0.3 p2p 2 Online
CentOS 7.9.2009 (Core) [64-bit] 10.26.0.4 p2p 35 Online
```
4. 最后可以用虚拟ip实现设备间相互访问
1. ping
<img width="506" alt="ping" src="https://raw.githubusercontent.com/lbl8603/switch/dev/documents/img/ping.jpg">
2. ssh
<img width="506" alt="ssh" src="https://raw.githubusercontent.com/lbl8603/switch/dev/documents/img/ssh.jpg">
5. 帮助,使用-h命令查看
@@ -49,50 +45,61 @@
1. 和远程桌面(如mstsc)搭配,超低延迟的体验
2. 安装samba服务,共享磁盘
3. 搭配公网服务器nginx反向代理,在公网访问本地文件
4. 点对网(结合启动参数'--in-ip'和'--out-ip')
4. 点对网,访问内网其他机器、IP代理(结合启动参数'-i'和'-o')
### 使用须知
- token的作用是标识一个虚拟局域网,当使用公共服务器时,建议使用一个唯一值当token(比如uuid),否则有可能连接到其他人创建的虚拟局域网中
- 建议指定deviceId,默认使用MAC地址,在某些环境下可能发生变化
- 公共服务器目前的配置是2核4G 4Mbps,有需要再扩展~
- 默认使用公共服务器,目前的配置是2核4G 4Mbps,有需要再扩展~
- 需要root/管理员权限
- 使用命令行运行
- Mac和Linux下需要加可执行权限(例如:chmod +x ./switch-macos)
- 自己搭注册和中继服务器(https://github.com/lbl8603/switch-server)
- switch-cmd需要使用命令行运行
- Mac和Linux下需要加可执行权限(例如:chmod +x ./switch-cmd)
- 可以自己搭注册和中继服务器([server](https://github.com/lbl8603/switch-server))
### 编译
前提条件:安装rust编译环境(https://www.rust-lang.org/zh-CN/tools/install)
到项目根目录下执行 cargo build -p switch-desktop
前提条件:安装rust编译环境([install rust](https://www.rust-lang.org/zh-CN/tools/install))
到项目根目录下执行 cargo build -p switch-cmd
### 支持平台
- Mac
- Linux
- Windows
- 使用tun网卡 依赖wintun.dll(https://www.wintun.net/ )(将dll放到同目录下,建议使用版本0.14.1)
- 使用tap网卡 依赖tap-windows(https://build.openvpn.net/downloads/releases/ )(建议使用版本9.24.7)
- 使用tun网卡 依赖wintun.dll([win-tun](https://www.wintun.net/))(将dll放到同目录下,建议使用版本0.14.1)
- 使用tap网卡 依赖tap-windows([win-tap](https://build.openvpn.net/downloads/releases/))(建议使用版本9.24.7)
- Android
- [SwitchApp](https://github.com/lbl8603/SwitchApp)
### 特性
- IP层数据转发
- tun虚拟网卡
- tap虚拟网卡
- tun虚拟网卡
- tap虚拟网卡
- NAT穿透
- 点对点穿透
- 服务端中继转发
- 客户端中继转发
- 点对点穿透
- 服务端中继转发
- 客户端中继转发
- IP代理
- p2p组播/广播
- 客户端数据加密
### Todo
- 支持安卓
- 服务端数据加密
- 支持Ipv6
### 常见问题
#### 问题1: 设置网络地址失败
##### 可能原因:
switch默认使用10.26.0.0/24网段,和本地网络适配器的ip冲突
##### 解决方法:
1. 方法一:找到冲突的IP,将其改成别的
2. 方法二:自建服务器,指定其他不会冲突的网段
3. 方法三:增加参数--device-id,设置不同的id会让switch-server分配不同的IP,从而绕开有冲突的IP
3. 方法三:增加参数-d <device-id> ,设置不同的id会让switch-server分配不同的IP,从而绕开有冲突的IP
+8
View File
@@ -0,0 +1,8 @@
[package]
name = "common"
version = "1.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+50
View File
@@ -0,0 +1,50 @@
use std::net::Ipv4Addr;
pub fn ips_parse(ips: &Vec<String>) -> Result<Vec<(u32, u32, Ipv4Addr)>, String> {
let mut in_ips_c = vec![];
for x in ips {
let mut split = x.split(",");
let net = if let Some(net) = split.next() {
net
} else {
return Err("ipv4/mask,ipv4".to_string());
};
let ip = if let Some(ip) = split.next() {
ip
} else {
return Err("ipv4/mask,ipv4".to_string());
};
let ip = if let Ok(ip) = ip.parse::<Ipv4Addr>() {
ip
} else {
return Err("not ipv4".to_string());
};
let mut split = net.split("/");
let dest = if let Some(dest) = split.next() {
dest
} else {
return Err("no ipv4/mask".to_string());
};
let mask = if let Some(mask) = split.next() {
mask
} else {
return Err("no netmask".to_string());
};
let dest = if let Ok(dest) = dest.parse::<Ipv4Addr>() {
dest
} else {
return Err("not ipv4".to_string());
};
let mask = if let Ok(m) = mask.parse::<u32>() {
let mut mask = 0 as u32;
for i in 0..m {
mask = mask | (1 << (31 - i));
}
mask
} else {
return Err("not netmask".to_string());
};
in_ips_c.push((u32::from_be_bytes(dest.octets()), mask, ip));
}
Ok(in_ips_c)
}
+69
View File
@@ -0,0 +1,69 @@
use std::process::Command;
#[cfg(target_os = "windows")]
pub fn get_unique_identifier() -> Option<String> {
use std::os::windows::process::CommandExt;
let output = match Command::new("wmic")
.creation_flags(0x08000000)
.args(&["csproduct", "get", "UUID"])
.output() {
Ok(output) => { output }
Err(_) => {
return None;
}
};
let result = String::from_utf8_lossy(&output.stdout);
let identifier = result.lines().nth(1).unwrap_or("").trim();
if identifier.is_empty() {
None
} else {
Some(identifier.to_string())
}
}
#[cfg(target_os = "macos")]
pub fn get_unique_identifier() -> Option<String> {
let output = match Command::new("ioreg")
.args(&["-rd1", "-c", "IOPlatformExpertDevice"])
.output() {
Ok(output) => { output }
Err(_) => {
return None;
}
};
let result = String::from_utf8_lossy(&output.stdout);
let identifier = result
.lines()
.find(|line| line.contains("IOPlatformUUID"))
.and_then(|line| line.split('"').nth(4))
.unwrap_or("").trim();
if identifier.is_empty() {
None
} else {
Some(identifier.to_string())
}
}
#[cfg(target_os = "linux")]
pub fn get_unique_identifier() -> Option<String> {
let output = match Command::new("dmidecode")
.arg("-s")
.arg("system-uuid")
.output() {
Ok(output) => { output }
Err(_) => {
return None;
}
};
let result = String::from_utf8_lossy(&output.stdout);
let identifier = result.trim().to_string();
if identifier.is_empty() {
None
} else {
Some(identifier.to_string())
}
}
+2
View File
@@ -0,0 +1,2 @@
pub mod identifier;
pub mod args_parse;
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

+27
View File
@@ -0,0 +1,27 @@
[package]
name = "switch-cmd"
version = "1.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
switch = {path="../switch"}
common = {path="../common"}
tokio = { version = "1.28.1", features = ["full"] }
getopts = "0.2.21"
console = "0.15.2"
os_info = "3.7.0"
dirs = "4.0.0"
serde = "1.0"
serde_json = "1.0.94"
log = "0.4.17"
[target.'cfg(any(target_os = "linux",target_os = "macos"))'.dependencies]
sudo = "0.6.0"
[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3.9", features = ["handleapi", "processthreadsapi", "winnt", "securitybaseapi", "impl-default"] }
[features]
default = []
mini = []
+40
View File
@@ -0,0 +1,40 @@
## 模块介绍
体积小,可以在服务器、路由器等环境使用
## 详细参数说明
### -k
一个虚拟局域网的标识,在同一服务器下,相同token的设备会组建一个局域网
### -n
设备名称,方便区分不同设备
### -d
设备id,每台设备的唯一标识,注意不要重复
### -c
关闭控制台交互式命令,后台运行时可以加此参数
### -s
注册和中继服务器地址,注册和转发数据
### -e
探测客户端NAT类型,不同类型有不同的打洞策略
### -a
加了此参数表示使用tap网卡,默认使用tun网卡,tun网卡效率更高
### -i、-o
配置点对网(IP代理)时使用,例如A(虚拟ip:10.26.0.2)通过B(虚拟ip:10.26.0.3,本地出口ip:192.168.0.10)访问C(目标网段192.168.0.0/24)
则在A配置 **-i 192.168.10.0/24,10.26.0.3** ,表示将192.168.10.0/24网段的数据都转发到10.26.0.3节点
在B配置 **-o 192.168.10.0/24,192.168.0.10** ,表示允许将192.168.10.0/24的数据从网卡192.168.0.10转发出去
### -w
提升通信安全性,使用该密码生成的密钥对客户端数据进行加密,并且服务端无法解密。使用相同密码的客户端才能通信
### -m
模拟组播,高频使用组播通信时,可以尝试开启此参数,默认情况下会把组播当作广播发给所有节点
默认情况(组播当广播发送):稳定性好,使用组播频率低时更省流量
模拟组播:高频使用组播时防止广播泛洪,客户端和中继服务器会维护组播成员等信息,注意使用此选项时,虚拟网内所有成员都需要开启此选项
### -u
设置虚拟网卡的mtu值,大多数情况下使用默认值效率会更高,也可根据实际情况微调这个值,默认值为1430
@@ -1,8 +1,9 @@
use std::io;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
use std::str::FromStr;
use std::time::Duration;
use crate::command::entity::{DeviceItem, RouteItem, Status};
use crate::command::entity::{DeviceItem, RouteItem, Info};
pub struct CommandClient {
udp: UdpSocket,
@@ -10,7 +11,17 @@ pub struct CommandClient {
impl CommandClient {
pub fn new() -> io::Result<Self> {
let port = crate::config::read_command_port()?;
let path_buf = dirs::home_dir().unwrap().join(".switch_desktop").join("command-port");
if !path_buf.exists() {
return Err(io::Error::new(io::ErrorKind::Other, "not started"));
}
let port = std::fs::read_to_string(path_buf)?;
let port = match u16::from_str(&port) {
Ok(port) => { port }
Err(_) => {
return Err(io::Error::new(io::ErrorKind::Other, "'command-port' file error"));
}
};
let udp = UdpSocket::bind("127.0.0.1:0")?;
udp.set_read_timeout(Some(Duration::from_secs(2)))?;
udp.connect(SocketAddr::V4(SocketAddrV4::new(
@@ -50,11 +61,11 @@ impl CommandClient {
}
}
}
pub fn status(&self) -> io::Result<Status> {
self.udp.send(b"status")?;
pub fn info(&self) -> io::Result<Info> {
self.udp.send(b"info")?;
let mut buf = [0; 10240];
let len = self.udp.recv(&mut buf)?;
match serde_json::from_slice::<Status>(&buf[..len]) {
match serde_json::from_slice::<Info>(&buf[..len]) {
Ok(val) => {
Ok(val)
}
@@ -64,7 +75,6 @@ impl CommandClient {
}
}
}
#[cfg(any(unix))]
pub fn stop(&self) -> io::Result<String> {
self.udp.send(b"stop")?;
let mut buf = [0; 10240];
@@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct Status {
pub struct Info {
pub name: String,
pub virtual_ip: String,
pub virtual_gateway: String,
@@ -1,56 +1,50 @@
use std::io;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
use std::sync::Arc;
use switch::core::Switch;
use crate::command::entity::{DeviceItem, RouteItem, Status};
use crate::command::entity::{DeviceItem, RouteItem, Info};
use crate::console_out;
pub mod client;
pub mod server;
pub mod entity;
pub struct CommandServer {}
pub enum CommandEnum {
Route,
List,
All,
Info,
Stop,
}
impl CommandServer {
pub fn new() -> Self {
Self {}
pub fn command(cmd: CommandEnum) {
if let Err(e) = command_(cmd) {
println!("cmd: {}", e);
}
}
impl CommandServer {
pub fn start(&self, switch: Arc<Switch>) -> io::Result<()> {
let mut port = 21637 as u16;
let udp = loop {
match UdpSocket::bind(SocketAddr::V4(SocketAddrV4::new(
Ipv4Addr::new(127, 0, 0, 1),
port,
))) {
Ok(udp) => {
break udp;
}
Err(e) => {
if e.kind() == io::ErrorKind::AddrInUse {
port += 1;
} else {
log::error!("创建udp失败 {:?}", e);
return Err(e);
}
}
}
};
crate::config::update_command_port(port)?;
let mut buf = [0u8; 64];
loop {
let (len, addr) = udp.recv_from(&mut buf)?;
match std::str::from_utf8(&buf[..len]) {
Ok(cmd) => {
if let Ok(out) = command(cmd, &switch) {
udp.send_to(out.as_bytes(), addr)?;
}
}
Err(e) => {
log::warn!("{:?}", e);
}
}
fn command_(cmd: CommandEnum) -> io::Result<()> {
let command_client = client::CommandClient::new()?;
match cmd {
CommandEnum::Route => {
let list = command_client.route()?;
console_out::console_route_table(list);
}
CommandEnum::List => {
let list = command_client.list()?;
console_out::console_device_list(list);
}
CommandEnum::All => {
let list = command_client.list()?;
console_out::console_device_list_all(list);
}
CommandEnum::Info => {
let info = command_client.info()?;
console_out::console_info(info);
}
CommandEnum::Stop => {
command_client.stop()?;
}
}
Ok(())
}
pub fn command_route(switch: &Switch) -> Vec<RouteItem> {
@@ -119,7 +113,7 @@ pub fn command_list(switch: &Switch) -> Vec<DeviceItem> {
list
}
pub fn command_status(switch: &Switch) -> Status {
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();
@@ -132,7 +126,7 @@ pub fn command_status(switch: &Switch) -> Status {
let public_ips: Vec<String> = nat_info.public_ips.iter().map(|v| v.to_string()).collect();
let public_ips = public_ips.join(",");
let local_ip = nat_info.local_ip.to_string();
Status {
Info {
name,
virtual_ip,
virtual_gateway,
@@ -145,45 +139,3 @@ pub fn command_status(switch: &Switch) -> Status {
}
}
fn command(cmd: &str, switch: &Switch) -> io::Result<String> {
let out_str = match cmd {
"route" => {
match serde_json::to_string(&command_route(switch)) {
Ok(str) => {
str
}
Err(e) => {
format!("{:?}", e)
}
}
}
"list" => {
match serde_json::to_string(&command_list(switch)) {
Ok(str) => {
str
}
Err(e) => {
format!("{:?}", e)
}
}
}
"status" => {
match serde_json::to_string(&command_status(switch)) {
Ok(str) => {
str
}
Err(e) => {
format!("{:?}", e)
}
}
}
"stop" => {
switch.stop()?;
"stopping".to_string()
}
_ => {
format!("command '{}' not found. \n Try to enter: 'help'\n", cmd)
}
};
Ok(out_str)
}
+105
View File
@@ -0,0 +1,105 @@
use std::io;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use tokio::net::UdpSocket;
use switch::core::Switch;
pub struct CommandServer {}
impl CommandServer {
pub fn new() -> Self {
Self {}
}
}
impl CommandServer {
pub async fn start(self, switch: Switch) -> io::Result<()> {
let mut port = 21637 as u16;
let udp = loop {
match UdpSocket::bind(SocketAddr::V4(SocketAddrV4::new(
Ipv4Addr::new(127, 0, 0, 1),
port,
))).await {
Ok(udp) => {
break udp;
}
Err(e) => {
if e.kind() == io::ErrorKind::AddrInUse {
port += 1;
} else {
log::error!("创建udp失败 {:?}", e);
return Err(e);
}
}
}
};
let path_buf = 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 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) {
let _ = udp.send_to(out.as_bytes(), addr).await;
if "stopped" == &out {
break;
}
}
}
Err(e) => {
log::warn!("{:?}", e);
}
}
}
Ok(())
}
}
fn command(cmd: &str, switch: &Switch) -> io::Result<String> {
let out_str = match cmd {
"route" => {
match serde_json::to_string(&crate::command::command_route(switch)) {
Ok(str) => {
str
}
Err(e) => {
format!("{:?}", e)
}
}
}
"list" => {
match serde_json::to_string(&crate::command::command_list(switch)) {
Ok(str) => {
str
}
Err(e) => {
format!("{:?}", e)
}
}
}
"info" => {
match serde_json::to_string(&crate::command::command_info(switch)) {
Ok(str) => {
str
}
Err(e) => {
format!("{:?}", e)
}
}
}
"stop" => {
switch.stop()?;
"stopped".to_string()
}
_ => {
format!("command '{}' not found. \n Try to enter: 'help'\n", cmd)
}
};
Ok(out_str)
}
@@ -1,10 +1,10 @@
use console::{style, Style};
use crate::command::entity::{DeviceItem, RouteItem, Status};
use crate::command::entity::{DeviceItem, RouteItem, Info};
pub mod table;
pub fn console_status(status: Status) {
pub fn console_info(status: Info) {
println!("Name: {}", style(status.name).green());
println!("Virtual ip: {}", style(status.virtual_ip).green());
println!("Virtual gateway: {}", style(status.virtual_gateway).green());
+274
View File
@@ -0,0 +1,274 @@
use std::net::ToSocketAddrs;
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;
mod command;
mod console_out;
mod root_check;
#[tokio::main]
async fn main() {
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("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.optopt("w", "", "使用该密码生成的密钥对客户端数据进行加密,并且服务端无法解密,使用相同密码的客户端才能通信", "<password>");
opts.optflag("m", "", "模拟组播,默认情况下组播数据会被当作广播发送,开启后会模拟真实组播的数据发送");
opts.optopt("u", "", "虚拟网卡mtu值", "<mtu>");
//"后台运行时,查看其他设备列表"
opts.optflag("", "list", &format!("{}", yellow("后台运行时,查看其他设备列表".to_string())));
opts.optflag("", "all", &format!("{}", yellow("后台运行时,查看其他设备完整信息".to_string())));
opts.optflag("", "info", &format!("{}", yellow("后台运行时,查看当前设备信息".to_string())));
opts.optflag("", "route", &format!("{}", yellow("后台运行时,查看数据转发路径".to_string())));
opts.optflag("", "stop", &format!("{}", yellow("停止后台运行".to_string())));
opts.optflag("h", "help", "帮助");
let matches = match opts.parse(&args[1..]) {
Ok(m) => { m }
Err(f) => {
print_usage(&program, opts);
println!("{}", f.to_string());
return;
}
};
if matches.opt_present("h") || args.len() == 1 {
print_usage(&program, opts);
return;
}
if !root_check::is_app_elevated() {
println!("Please run it with administrator or root privileges");
return;
}
if matches.opt_present("list") {
command::command(command::CommandEnum::List);
return;
} else if matches.opt_present("info") {
command::command(command::CommandEnum::Info);
return;
} else if matches.opt_present("stop") {
command::command(command::CommandEnum::Stop);
return;
} else if matches.opt_present("route") {
command::command(command::CommandEnum::Route);
return;
} else if matches.opt_present("all") {
command::command(command::CommandEnum::All);
return;
}
if !matches.opt_present("k") {
print_usage(&program, opts);
println!("parameter -k not found .");
return;
}
let tap = matches.opt_present("a");
let token: String = matches.opt_get("k").unwrap().unwrap();
let device_id = matches.opt_get_default("d", String::new()).unwrap();
#[cfg(not(feature = "mini"))]
let device_id = if device_id.is_empty() {
common::identifier::get_unique_identifier().unwrap_or(String::new())
} else {
device_id
};
if device_id.is_empty() {
print_usage(&program, opts);
println!("parameter -d not found .");
return;
}
let name = matches.opt_get_default("n", os_info::get().to_string()).unwrap();
let server_address_str = matches.opt_get_default("s", "nat1.wherewego.top:29871".to_string()).unwrap();
let server_address = server_address_str.to_socket_addrs().unwrap().next().unwrap();
let nat_test_server = matches.opt_get_default("e",
"nat1.wherewego.top:35061,nat1.wherewego.top:35062,nat2.wherewego.top:35061,nat2.wherewego.top:35062".to_string()).unwrap();
let nat_test_server = nat_test_server.split(",").flat_map(|a| a.to_socket_addrs()).flatten()
.collect::<Vec<_>>();
let in_ip = matches.opt_strs("i");
let in_ip = match ips_parse(&in_ip) {
Ok(in_ip) => { in_ip }
Err(e) => {
print_usage(&program, opts);
println!();
println!("-i {}", e);
return;
}
};
let out_ip = matches.opt_strs("o");
let out_ip = match ips_parse(&out_ip) {
Ok(out_ip) => { out_ip }
Err(e) => {
print_usage(&program, opts);
println!();
println!("-o {}", e);
return;
}
};
let password: Option<String> = matches.opt_get("w").unwrap();
let simulate_multicast = matches.opt_present("m");
#[cfg(feature = "mini")]
let unused_cmd = true;
#[cfg(not(feature = "mini"))]
let unused_cmd = matches.opt_present("c");
let mtu: Option<String> = matches.opt_get("u").unwrap();
let mtu = if let Some(mtu) = mtu {
match u16::from_str(&mtu) {
Ok(mtu) => {
Some(mtu)
}
Err(e) => {
print_usage(&program, opts);
println!();
println!("-u {}", e);
return;
}
}
} else {
None
};
let 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();
let response = loop {
match switch_util.connect().await {
Ok(response) => {
break response;
}
Err(e) => {
match e {
ReqEnum::TokenError => {
println!("token error");
}
ReqEnum::AddressExhausted => {
println!("address exhausted");
}
ReqEnum::Timeout => {
println!("timeout...");
continue;
}
ReqEnum::ServerError(str) => {
println!("error:{}", str);
}
ReqEnum::Other(str) => {
println!("error:{}", str);
}
}
return;
}
}
};
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();
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
}
Err(e) => {
println!("error:{}", e);
return;
}
};
println!(" ====== Start Successfully ====== ");
let switch_s = switch.clone();
tokio::spawn(async {
if let Err(e) = command::server::CommandServer::new().start(switch_s).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()=>{
break;
}
rs = reader.read_line(&mut cmd)=>{
match rs {
Ok(len) => {
if len ==0 {
break;
}
match cmd[..len].to_lowercase().trim() {
"list" => {
let list = command::command_list(&switch);
console_out::console_device_list(list);
}
"info"=>{
let info = command::command_info(&switch);
console_out::console_info(info);
}
"route" =>{
let route = command::command_route(&switch);
console_out::console_route_table(route);
}
"all" =>{
let list = command::command_list(&switch);
console_out::console_device_list_all(list);
}
"stop" =>{
let _ = switch.stop();
break;
}
_ => {
}
}
println!();
}
Err(e) => {
println!("input err:{}",e);
break;
}
}
}
}
}
switch.wait_stop().await;
}
}
std::process::exit(0);
}
fn print_usage(program: &str, opts: Options) {
let brief = format!("Usage: {} [options]", program);
println!("version:1.1.0");
println!("{}", opts.usage(&brief));
}
fn green(str: String) -> impl std::fmt::Display {
style(str).green()
}
fn yellow(str: String) -> impl std::fmt::Display {
style(str).yellow()
}
+11
View File
@@ -0,0 +1,11 @@
#[cfg(target_os = "windows")]
mod windows;
#[cfg(target_os = "windows")]
pub use windows::is_app_elevated;
#[cfg(any(target_os = "linux", target_os = "macos"))]
mod unix;
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub use unix::is_app_elevated;
+3
View File
@@ -0,0 +1,3 @@
pub fn is_app_elevated() -> bool {
sudo::RunningAs::Root == sudo::check()
}
+23 -24
View File
@@ -1,38 +1,37 @@
[package]
name = "switch-desktop"
version = "1.0.6"
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"}
mac_address = "1.1.4"
clap = { version = "4.0.32", features = ["derive"] }
console = "0.15.2"
common = {path="../common"}
dirs = "4.0.0"
log = "0.4.17"
log4rs = "1.2.0"
tokio = { version = "1.28.1", features = ["full"] }
chrono = "0.4.23"
rust-i18n = "1.2.2"
serde = "1.0"
serde_yaml = "0.9"
serde_json = "1.0.94"
crossbeam = "0.8.2"
os_info = "3.7.0"
lazy_static = "1.4.0"
parking_lot = "0.12.1"
fs2 = "0.4.3"
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.4.0", features = [] }
os_info = "3.5.1"
[target.'cfg(any(target_os = "linux",target_os = "macos"))'.dependencies]
sudo = "0.6.0"
libc = "0.2"
[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3.9", features = ["handleapi", "processthreadsapi", "winnt", "securitybaseapi", "impl-default"] }
#runas = "0.2.1"
windows-service = "0.6.0"
[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" ]
View File
+21
View File
@@ -0,0 +1,21 @@
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();
}
}
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

-50
View File
@@ -1,50 +0,0 @@
switch_about: "A virtual network tool that will obtain an ip after startup. Devices under the same token will form a virtual local area network, and can use ip to communicate directly with each other."
switch_usage: "switch-desktop.exe <COMMAND>"
switch_start_about: "Start switch"
switch_token_help: "Using the same token, you can build a local area network. It is recommended to use a more complex token to avoid connecting to other people's local area network"
switch_name_help: "Give the device a name, the system version information will be used by default"
switch_device_id_help: "The unique identifier of the device, the ip is assigned according to the id, and the MAC address is used by default"
switch_server_help: "Registry and relay server address, public server is used by default"
switch_nat_test_server_help: "NAT detection server addresses, separated by commas"
switch_log_help: "Record the log, the output is in the '${home}/.switch_desktop' directory, it is not recommended to open it for long-term use"
switch_tap_help: "Use tap mode, tun mode will be used by default"
switch_in_ip_help: "Use when configuring point-to-network (IP proxy), --in-ip 192.168.10.0/24,10.26.0.3, which means it is allowed to receive data from the network segment 192.168.10.0/24 and forward it to 10.26.0.3"
switch_out_ip_help: "Use when configuring point-to-network, --out-ip 192.168.10.0/24,192.168.1.10, which means that the data with the target of 192.168.10.0/24 is allowed to be forwarded from the network card 192.168.1.10"
switch_password_help: "Client Data Encryption"
switch_config_help: "Read configuration file"
switch_stop_about: "Stop background service"
switch_route_about: "View route"
switch_list_about: "View device list"
switch_list_all_help: "View full information"
switch_status_about: "View current device information"
switch_install_about: "Install windows service"
switch_path_help: "Service installation path, it is recommended to use an empty directory"
switch_auto_help: "Service starts automatically at boot"
switch_uninstall_about: "Uninstall windows service"
switch_config_about: "Change Windows Service Configuration"
switch_use_admin_print: "Please run with administrator privileges"
switch_use_root_print: "Please run with root privileges"
switch_service_not_start_print: "service not started"
switch_start_successfully_print: "Start successfully"
switch_start_failed_print: "Startup failed"
switch_service_not_stopped_print: "service not stopped"
switch_stopped_print: "stopped"
switch_server_already_installed_print: "service is installed"
switch_repeated_start_print: "cannot be restarted repeatedly"
switch_token_not_found_print: "missing token"
switch_token_cannot_be_empty_print: "token cannot be empty"
switch_token_cannot_exceed_64_print: "token cannot exceed 64 characters"
switch_device_id_is_empty_print: "The device id cannot be empty and the length cannot be greater than 64 characters"
switch_in_ips_example_print: "in_ips Parameter error Example:--in_ip 192.168.10.0/24,10.26.0.3"
switch_out_ips_example_print: "out_ips Parameter error Example:--out_ip 192.168.10.0/24,192.168.0.5"
switch_relay_server_address_error: "Wrong relay server address"
switch_nat_test_server_address_error: "NAT detection service address error"
switch_press_any_key_to_exit: "Press any key to exit"
switch_virtual_ip: "virtual ip"
switch_virtual_gateway: "virtual gateway"
switch_please_enter_the_command: "Please enter the command (Usage: list,route,status,exit,help):"
-50
View File
@@ -1,50 +0,0 @@
switch_about: "一个虚拟网络工具,启动后会获取一个ip,相同token下的设备会组件虚拟局域网,之间可以用ip直接通信"
switch_usage: "switch-desktop.exe <命令>"
switch_start_about: "启动switch"
switch_token_help: "使用相同的token,就能组建一个局域网络,建议使用一个复杂一点的token,避免连到其他人的局域网中"
switch_name_help: "给设备一个名字,默认会使用系统版本信息"
switch_device_id_help: "设备唯一标识符,凭id分配ip,默认使用MAC地址"
switch_server_help: "注册和中继服务器地址,默认使用公共服务器"
switch_nat_test_server_help: "NAT探测服务器地址,使用逗号分隔"
switch_log_help: "记录日志,输出在 '${home}/.switch_desktop' 目录下,长时间使用时不建议开启"
switch_tap_help: "使用tap模式,默认会使用tun模式"
switch_in_ip_help: "配置点对网(IP代理)时使用,--in-ip 192.168.10.0/24,10.26.0.3,表示允许接收网段192.168.10.0/24的数据并转发到10.26.0.3"
switch_out_ip_help: "配置点对网时使用,--out-ip 192.168.10.0/24,192.168.1.10,表示允许目标为192.168.10.0/24的数据从网卡192.168.1.10转发出去"
switch_password_help: "使用该密码生成的密钥对客户端数据进行加密,并且服务端无法解密。使用相同密码的客户端才能通信"
switch_config_help: "读取配置文件"
switch_stop_about: "停止后台服务"
switch_route_about: "查看路由"
switch_list_about: "查看设备列表"
switch_list_all_help: "查看完整信息"
switch_status_about: "查看当前设备信息"
switch_install_about: "安装Windows服务"
switch_path_help: "服务安装路径,建议使用一个空目录"
switch_auto_help: "服务开机自启动"
switch_uninstall_about: "卸载Windows服务"
switch_config_about: "改变Windows服务配置"
switch_use_admin_print: "请使用管理员权限运行"
switch_use_root_print: "请使用root权限运行"
switch_service_not_start_print: "服务未启动"
switch_start_successfully_print: "启动成功"
switch_start_failed_print: "启动失败"
switch_service_not_stopped_print: "服务未停止"
switch_stopped_print: "已停止"
switch_server_already_installed_print: "服务已经安装"
switch_repeated_start_print: "不能重复启动"
switch_token_not_found_print: "缺少token"
switch_token_cannot_be_empty_print: "token不能为空"
switch_token_cannot_exceed_64_print: "token不能超过64个字符"
switch_device_id_is_empty_print: "设备id不能为空并且长度不能大于64字符"
switch_in_ips_example_print: "in_ips 参数错误 示例:--in_ip 192.168.10.0/24,10.26.0.3"
switch_out_ips_example_print: "out_ips 参数错误 示例:--out_ip 192.168.10.0/24,192.168.0.5"
switch_relay_server_address_error: "中继服务器地址错误"
switch_nat_test_server_address_error: "NAT检测服务地址错误"
switch_press_any_key_to_exit: "按任意键退出"
switch_virtual_ip: "当前虚拟ip(virtual ip)"
switch_virtual_gateway: "虚拟网关(virtual gateway)"
switch_please_enter_the_command: "输入命令 (例如: list,route,status,exit,help):"
-59
View File
@@ -1,59 +0,0 @@
use std::io;
use console::style;
use crate::console_out;
pub mod client;
pub mod server;
pub mod entity;
pub enum CommandEnum {
Route,
List,
ListAll,
Status,
#[cfg(any(unix))]
Stop,
}
pub fn command(cmd: CommandEnum) {
if let Err(e) = command_(cmd) {
println!("{}:{:?}", style("连接后台服务错误(Connection background service error)").red(), e);
}
}
fn command_(cmd: CommandEnum) -> io::Result<()> {
match client::CommandClient::new() {
Ok(command_client) => {
match cmd {
CommandEnum::Route => {
let list = command_client.route()?;
console_out::console_route_table(list);
}
CommandEnum::List => {
let list = command_client.list()?;
console_out::console_device_list(list);
}
CommandEnum::ListAll => {
let list = command_client.list()?;
console_out::console_device_list_all(list);
}
CommandEnum::Status => {
let status = command_client.status()?;
console_out::console_status(status);
}
#[cfg(any(unix))]
CommandEnum::Stop => {
command_client.stop()?;
}
}
}
Err(e) => {
log::error!("{:?}",e);
println!(
"{}:{:?}",
style("连接后台服务错误(Connection background service error)").red(), e
);
}
};
Ok(())
}
-133
View File
@@ -1,133 +0,0 @@
use clap::{Arg, ArgAction, Command};
use clap::builder::BoolishValueParser;
use crate::i18n::*;
fn common() -> Command {
Command::new("switch-desktop")
.about(switch_about())
// .version(switch_version())
.subcommand_required(true)
.arg_required_else_help(true)
// .author(switch_author())
.override_usage(switch_usage())
.subcommand(
Command::new("start")
.about(switch_start_about())
.arg(
Arg::new("token")
.long("token")
.help(switch_token_help())
.action(ArgAction::Set)
)
.arg(
Arg::new("name")
.long("name")
.help(switch_name_help())
.action(ArgAction::Set)
)
.arg(
Arg::new("device_id")
.long("device-id")
.help(switch_device_id_help())
.action(ArgAction::Set)
).arg(
Arg::new("server")
.long("server")
.help(switch_server_help())
.action(ArgAction::Set)
).arg(
Arg::new("nat_test_server")
.long("nat-test-server")
.help(switch_nat_test_server_help())
.action(ArgAction::Set)
).arg(
Arg::new("log")
.long("log")
.help(switch_log_help())
.action(ArgAction::SetTrue)
.value_parser(BoolishValueParser::new()),
).arg(
Arg::new("tap")
.long("tap")
.help(switch_tap_help())
.action(ArgAction::SetTrue),
).arg(
Arg::new("in_ip")
.long("in-ip")
.help(switch_in_ip_help())
.action(ArgAction::Append)
.num_args(1..),
).arg(
Arg::new("out_ip")
.long("out-ip")
.help(switch_out_ip_help())
.action(ArgAction::Append)
).arg(
Arg::new("password")
.long("password")
.help(switch_password_help())
.action(ArgAction::Set)
).arg(
Arg::new("config")
.long("config")
.help(switch_config_help())
.action(ArgAction::Set)
)
,
).subcommand(
Command::new("stop")
.about(switch_stop_about()))
.subcommand(
Command::new("route")
.about(switch_route_about()))
.subcommand(Command::new("list")
.about(switch_list_about()).arg(
Arg::new("all")
.long("all")
.short('a')
.help(switch_list_all_help())
.action(ArgAction::SetTrue)
.value_parser(BoolishValueParser::new()), ))
.subcommand(Command::new("status")
.about(switch_status_about()))
}
pub fn check() -> bool {
#[cfg(windows)]
let cmd = common().subcommand(Command::new("install")
.about(switch_install_about())
.arg(
Arg::new("path")
.long("path")
.help(switch_path_help())
.action(ArgAction::Set)
.num_args(1..))
.arg(
Arg::new("auto")
.long("auto")
.help(switch_auto_help())
.action(ArgAction::SetTrue)
.value_parser(BoolishValueParser::new()), ))
.subcommand(Command::new("uninstall")
.about(switch_uninstall_about()))
.subcommand(Command::new("config")
.about(switch_config_about())
.arg(
Arg::new("auto")
.long("auto")
.help(switch_auto_help())
.action(ArgAction::SetTrue)
.value_parser(BoolishValueParser::new()), ));
#[cfg(any(unix))]
let cmd = common();
match cmd.try_get_matches() {
Ok(_) => {
true
}
Err(e) => {
println!("{}", e);
false
}
}
}
-45
View File
@@ -1,45 +0,0 @@
use std::io;
use std::path::PathBuf;
use crate::config::get_home;
#[cfg(target_os = "windows")]
pub fn log_service_init() -> io::Result<()> {
log_init_(crate::config::get_win_server_home().join("switch-service.log"))
}
pub fn log_init() -> io::Result<()> {
log_init_(get_home().join("switch-desktop.log"))
}
fn log_init_(file_name: PathBuf) -> io::Result<()> {
let stderr = log4rs::append::console::ConsoleAppender::builder()
.target(log4rs::append::console::Target::Stderr)
.build();
let logfile = log4rs::append::file::FileAppender::builder()
// Pattern: https://docs.rs/log4rs/*/log4rs/encode/pattern/index.html
.encoder(Box::new(log4rs::encode::pattern::PatternEncoder::new(
"{d(%+)(utc)} [{f}:{L}] {h({l})} {M}:{m}{n}\n",
)))
.build(file_name)?;
match log4rs::Config::builder()
.appender(log4rs::config::Appender::builder().build("logfile", Box::new(logfile)))
.appender(
log4rs::config::Appender::builder()
.filter(Box::new(log4rs::filter::threshold::ThresholdFilter::new(
log::LevelFilter::Error,
)))
.build("stderr", Box::new(stderr)),
)
.build(
log4rs::config::Root::builder()
.appender("logfile")
.appender("stderr")
.build(log::LevelFilter::Info),
) {
Ok(config) => {
let _ = log4rs::init_config(config);
}
Err(_) => {}
}
Ok(())
}
-500
View File
@@ -1,509 +1,9 @@
use std::fs::{File, OpenOptions};
use std::io;
use std::io::{Read, Write};
use std::net::{Ipv4Addr, SocketAddr, ToSocketAddrs};
use std::path::PathBuf;
use lazy_static::lazy_static;
use parking_lot::Mutex;
use serde::{Deserialize, Serialize};
use crate::{i18n, StartArgs};
pub mod log_config;
lazy_static! {
pub static ref SWITCH_HOME_PATH: Mutex<Option<PathBuf>> = Mutex::new(None);
}
#[cfg(windows)]
pub fn get_win_server_home() -> PathBuf {
SWITCH_HOME_PATH.lock().as_ref().unwrap().clone()
}
#[cfg(windows)]
pub fn set_win_server_home(home: PathBuf) {
let _ = SWITCH_HOME_PATH.lock().insert(home);
}
#[derive(Clone, Debug)]
pub struct StartConfig {
pub tap: bool,
pub name: String,
pub token: String,
pub server: SocketAddr,
pub nat_test_server: Vec<SocketAddr>,
pub device_id: String,
pub in_ips: Vec<(u32, u32, Ipv4Addr)>,
pub out_ips: Vec<(u32, u32, Ipv4Addr)>,
#[cfg(any(unix))]
pub off_command_server: bool,
pub log: bool,
pub password: Option<String>,
}
fn ips_parse(ips: &Vec<String>) -> Result<Vec<(u32, u32, Ipv4Addr)>, String> {
let mut in_ips_c = vec![];
for x in ips {
let mut split = x.split(",");
let net = if let Some(net) = split.next() {
net
} else {
return Err("参数错误".to_string());
};
let ip = if let Some(ip) = split.next() {
ip
} else {
return Err("参数错误".to_string());
};
let ip = if let Ok(ip) = ip.parse::<Ipv4Addr>() {
ip
} else {
return Err("参数错误".to_string());
};
let mut split = net.split("/");
let dest = if let Some(dest) = split.next() {
dest
} else {
return Err("参数错误".to_string());
};
let mask = if let Some(mask) = split.next() {
mask
} else {
return Err("参数错误".to_string());
};
let dest = if let Ok(dest) = dest.parse::<Ipv4Addr>() {
dest
} else {
return Err("参数错误".to_string());
};
let mask = if let Ok(m) = mask.parse::<u32>() {
let mut mask = 0 as u32;
for i in 0..m {
mask = mask | (1 << (31 - i));
}
mask
} else {
return Err("参数错误".to_string());
};
in_ips_c.push((u32::from_be_bytes(dest.octets()), mask, ip));
}
Ok(in_ips_c)
}
pub fn default_config(start_args: StartArgs) -> Result<StartConfig, String> {
println!("========参数配置========");
if start_args.log {
println!("print log");
}
let tap = start_args.tap;
if tap {
println!("use tap");
} else {
println!("use tun");
}
if start_args.token.is_none() {
return Err(i18n::switch_token_not_found_print());
}
let token = start_args.token.unwrap();
if token.is_empty() {
return Err(i18n::switch_token_cannot_be_empty_print());
}
if token.len() > 64 {
return Err(i18n::switch_token_cannot_exceed_64_print());
}
println!("token:{:?}", token);
let name = start_args.name.unwrap_or_else(|| {
os_info::get().to_string()
});
let name = name.trim();
let name = if name.len() > 64 {
name[..64].to_string()
} else {
name.to_string()
};
println!("name:{:?}", name);
let device_id = start_args.device_id.unwrap_or_else(|| {
if let Ok(Some(mac_address)) = mac_address::get_mac_address() {
mac_address.to_string()
} else {
"".to_string()
}
});
if device_id.is_empty() || device_id.len() > 64 {
return Err(i18n::switch_device_id_is_empty_print());
}
println!("device_id:{:?}", device_id);
let in_ips = start_args.in_ip.unwrap_or_else(|| {
vec![]
});
let out_ips = start_args.out_ip.unwrap_or_else(|| {
vec![]
});
println!("in_ips:{:?}", in_ips);
let in_ips_c = if let Ok(in_ips_c) = ips_parse(&in_ips) {
in_ips_c
} else {
return Err(i18n::switch_in_ips_example_print());
};
println!("out_ips:{:?}", out_ips);
let out_ips_c = if let Ok(out_ips_c) = ips_parse(&out_ips) {
out_ips_c
} else {
return Err(i18n::switch_out_ips_example_print());
};
let server = match start_args.server.unwrap_or_else(|| {
"nat1.wherewego.top:29871".to_string()
}).to_socket_addrs() {
Ok(mut server) => {
if let Some(addr) = server.next() {
addr
} else {
return Err(i18n::switch_relay_server_address_error());
}
}
Err(e) => {
return Err(format!("{} :{:?}", i18n::switch_relay_server_address_error(), e));
}
};
println!("中继服务器:{:?}", server);
let nat_test_server = start_args.nat_test_server.unwrap_or_else(|| {
"nat1.wherewego.top:35061,nat1.wherewego.top:35062,nat2.wherewego.top:35061,nat2.wherewego.top:35062".to_string()
}).split(",").flat_map(|a| a.to_socket_addrs()).flatten()
.collect::<Vec<_>>();
if nat_test_server.is_empty() {
return Err(i18n::switch_nat_test_server_address_error());
}
println!("NAT探测服务器:{:?}", nat_test_server);
let base_config = StartConfig {
tap,
name,
token,
server,
nat_test_server,
device_id,
in_ips: in_ips_c,
out_ips: out_ips_c,
#[cfg(any(unix))]
off_command_server: start_args.off_command_server,
log: start_args.log,
password: start_args.password,
};
println!("========参数配置========");
Ok(base_config)
}
pub fn read_config_file(config_path: PathBuf) -> Result<StartConfig, String> {
println!("========读取配置文件========");
let args_config = if let Ok(config) = read_config(config_path) {
config
} else {
return Err("读取配置文件失败".to_string());
};
let log = args_config.log;
if log {
println!("print log");
}
let tap = args_config.tap;
if tap {
println!("use tap");
} else {
println!("use tun");
}
let token = args_config.token;
if token.is_empty() {
return Err(i18n::switch_token_cannot_be_empty_print());
}
if token.len() > 64 {
return Err(i18n::switch_token_cannot_exceed_64_print());
}
println!("token:{:?}", token);
let name = args_config.name;
let name = name.trim();
let name = if name.len() > 64 {
name[..64].to_string()
} else {
name.to_string()
};
println!("name:{:?}", name);
let device_id = if !args_config.device_id.is_empty() {
args_config.device_id
} else {
if let Ok(Some(mac_address)) = mac_address::get_mac_address() {
mac_address.to_string()
} else {
"".to_string()
}
};
if device_id.is_empty() || device_id.len() > 64 {
return Err(i18n::switch_device_id_is_empty_print());
}
println!("device_id:{:?}", device_id);
let in_ips = args_config.in_ips;
let out_ips = args_config.out_ips;
println!("in_ips:{:?}", in_ips);
let in_ips_c = if let Ok(in_ips_c) = ips_parse(&in_ips) {
in_ips_c
} else {
return Err(i18n::switch_in_ips_example_print());
};
println!("out_ips:{:?}", out_ips);
let out_ips_c = if let Ok(out_ips_c) = ips_parse(&out_ips) {
out_ips_c
} else {
return Err(i18n::switch_out_ips_example_print());
};
let server = match {
if !args_config.server.is_empty() {
args_config.server
} else {
"nat1.wherewego.top:29871".to_string()
}
}.to_socket_addrs()
{
Ok(mut server) => {
if let Some(addr) = server.next() {
addr
} else {
return Err(i18n::switch_relay_server_address_error());
}
}
Err(e) => {
return Err(format!("{}:{:?}", i18n::switch_relay_server_address_error(), e));
}
};
println!("中继服务器:{:?}", server);
let nat_test_server = if args_config.nat_test_server.is_empty() {
vec!["nat1.wherewego.top:35061".to_string(), "nat1.wherewego.top:35062".to_string(), "nat2.wherewego.top:35061".to_string(), "nat2.wherewego.top:35062".to_string()]
} else {
args_config.nat_test_server
}.iter().flat_map(|a| a.to_socket_addrs()).flatten()
.collect::<Vec<_>>();
if nat_test_server.is_empty() {
return Err(i18n::switch_nat_test_server_address_error());
}
println!("NAT探测服务器:{:?}", nat_test_server);
let base_config = StartConfig {
tap,
name,
token,
server,
nat_test_server,
device_id,
in_ips: in_ips_c,
out_ips: out_ips_c,
#[cfg(any(unix))]
off_command_server: args_config.off_command_server,
log,
password:args_config.password
};
println!("========参数配置========");
Ok(base_config)
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RuntimeData {
#[serde(default = "default_pid")]
pub pid: u32,
pub command_port: Option<u16>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ArgsConfig {
#[serde(default = "default_false")]
pub tap: bool,
#[serde(default = "default_version")]
pub version: String,
#[serde(default = "default_str")]
pub token: String,
#[serde(default = "default_str")]
pub name: String,
#[serde(default = "default_str")]
pub server: String,
#[serde(default = "default_vec")]
pub nat_test_server: Vec<String>,
#[serde(default = "default_str")]
pub device_id: String,
#[serde(default = "default_vec")]
pub in_ips: Vec<String>,
#[serde(default = "default_vec")]
pub out_ips: Vec<String>,
#[cfg(any(unix))]
#[serde(default = "default_false")]
pub off_command_server: bool,
#[serde(default = "default_false")]
pub log: bool,
pub password: Option<String>,
}
#[cfg(windows)]
impl ArgsConfig {
pub fn new(start_config: StartConfig) -> ArgsConfig {
let in_ips = start_config.in_ips.iter().map(|(ip, mask, dest)| {
format!("{}/{},{}", Ipv4Addr::from(*ip), subnet_mask_to_integer(*mask), dest)
}).collect::<Vec<String>>();
let out_ips = start_config.out_ips.iter().map(|(ip, mask, dest)| {
format!("{}/{},{}", Ipv4Addr::from(*ip), subnet_mask_to_integer(*mask), dest)
}).collect::<Vec<String>>();
ArgsConfig {
tap: start_config.tap,
version: "1.0.6".to_string(),
token: start_config.token.to_string(),
name: start_config.name.to_string(),
server: start_config.server.to_string(),
nat_test_server: start_config.nat_test_server.iter().map(|v| v.to_string()).collect(),
device_id: start_config.device_id,
in_ips,
out_ips,
log: start_config.log,
#[cfg(any(unix))]
off_command_server: start_config.off_command_server,
password: start_config.password,
}
}
}
#[cfg(windows)]
fn subnet_mask_to_integer(subnet_mask: u32) -> u8 {
let mut mask_bits = subnet_mask;
let mut num_bits = 0;
while mask_bits != 0 {
num_bits += 1;
mask_bits <<= 1;
}
num_bits as u8
}
fn default_false() -> bool {
false
}
fn default_version() -> String {
"1.0.6".to_string()
}
fn default_str() -> String {
"".to_string()
}
fn default_vec() -> Vec<String> {
vec![]
}
fn default_pid() -> u32 {
0
}
// impl ArgsConfig {
// pub fn new(tap: bool, token: String, name: String, server: SocketAddr,
// nat_test_server: &Vec<SocketAddr>, device_id: String,
// in_ips: Vec<(u32, u32, Ipv4Addr)>, out_ips: Vec<(u32, u32, Ipv4Addr)>, ) -> Self {
//
// Self {
// tap,
// version: "1.0".to_string(),
// token,
// name,
// command_port: None,
// server: server.to_string(),
// nat_test_server: nat_test_server.iter().map(|v| v.to_string()).collect::<Vec<String>>(),
// device_id,
// pid: 0,
// }
// }
// }
pub fn lock_file() -> io::Result<File> {
let path = get_home().join(".lock");
let file = File::create(path)?;
file.sync_all()?;
Ok(file)
}
fn save_runtime_data(config: RuntimeData) -> io::Result<()> {
let config_path = get_runtime_data_path();
let str = serde_yaml::to_string(&config).unwrap();
let mut file = File::create(config_path)?;
file.write_all(str.as_bytes())?;
file.sync_all()
}
pub fn update_pid(pid: u32) -> io::Result<()> {
let mut config = read_runtime_data()?;
config.pid = pid;
return save_runtime_data(config);
}
#[cfg(any(unix))]
pub fn read_pid() -> io::Result<u32> {
let config = read_runtime_data()?;
Ok(config.pid)
}
pub fn update_command_port(port: u16) -> io::Result<()> {
let mut config = read_runtime_data()?;
config.command_port = Some(port);
return save_runtime_data(config);
}
pub fn read_command_port() -> io::Result<u16> {
let config = read_runtime_data()?;
if let Some(p) = config.command_port {
Ok(p)
} else {
Err(io::Error::new(io::ErrorKind::Other, "not found config"))
}
}
pub fn get_home() -> PathBuf {
#[cfg(windows)]
{
if let Some(path) = SWITCH_HOME_PATH.lock().as_ref() {
return path.clone();
}
}
let home = dirs::home_dir().unwrap().join(".switch_desktop");
if !home.exists() {
std::fs::create_dir(&home).unwrap();
}
home
}
pub fn get_runtime_data_path() -> PathBuf {
let home = get_home();
home.join(".data")
}
fn read_runtime_data() -> io::Result<RuntimeData> {
let config_path = get_runtime_data_path();
let mut file = if config_path.exists() {
File::open(config_path)?
} else {
OpenOptions::new().read(true).write(true).truncate(false).create(true).open(config_path)?
};
let mut str = String::new();
file.read_to_string(&mut str)?;
match serde_yaml::from_str::<RuntimeData>(&str) {
Ok(config) => Ok(config),
Err(e) => {
log::warn!("{:?}", e);
Err(io::Error::new(io::ErrorKind::Other, "config error"))
}
}
}
fn read_config(config_path: PathBuf) -> io::Result<ArgsConfig> {
let mut file = File::open(config_path)?;
let mut str = String::new();
file.read_to_string(&mut str)?;
match serde_yaml::from_str::<ArgsConfig>(&str) {
Ok(config) => Ok(config),
Err(e) => {
log::warn!("{:?}", e);
Err(io::Error::new(io::ErrorKind::Other, "config error"))
}
}
}
-214
View File
@@ -1,214 +0,0 @@
#[cfg(target_os = "windows")]
fn get_default_language() -> Option<String> {
use std::process::Command;
use std::str;
let output = Command::new("powershell")
.arg("-Command")
.arg("[System.Globalization.CultureInfo]::CurrentCulture.Name")
.output()
.ok()?;
let language_code = str::from_utf8(&output.stdout)
.ok()?
.trim()
.to_string();
Some(language_code)
}
pub fn init() {
#[cfg(target_os = "windows")]
{
if let Some(l) = get_default_language() {
rust_i18n::set_locale(&l);
}
}
}
pub fn switch_about() -> String {
rust_i18n::t!("switch_about")
}
pub fn switch_usage() -> String {
rust_i18n::t!("switch_usage")
}
pub fn switch_start_about() -> String {
rust_i18n::t!("switch_start_about")
}
pub fn switch_token_help() -> String {
rust_i18n::t!("switch_token_help")
}
pub fn switch_name_help() -> String {
rust_i18n::t!("switch_name_help")
}
pub fn switch_device_id_help() -> String {
rust_i18n::t!("switch_device_id_help")
}
pub fn switch_server_help() -> String {
rust_i18n::t!("switch_server_help")
}
pub fn switch_nat_test_server_help() -> String {
rust_i18n::t!("switch_nat_test_server_help")
}
pub fn switch_log_help() -> String {
rust_i18n::t!("switch_log_help")
}
pub fn switch_tap_help() -> String {
rust_i18n::t!("switch_tap_help")
}
pub fn switch_in_ip_help() -> String {
rust_i18n::t!("switch_in_ip_help")
}
pub fn switch_out_ip_help() -> String {
rust_i18n::t!("switch_out_ip_help")
}
pub fn switch_password_help() -> String {
rust_i18n::t!("switch_password_help")
}
pub fn switch_config_help() -> String {
rust_i18n::t!("switch_config_help")
}
pub fn switch_stop_about() -> String {
rust_i18n::t!("switch_stop_about")
}
pub fn switch_route_about() -> String {
rust_i18n::t!("switch_route_about")
}
pub fn switch_list_about() -> String {
rust_i18n::t!("switch_list_about")
}
pub fn switch_list_all_help() -> String {
rust_i18n::t!("switch_list_all_help")
}
pub fn switch_status_about() -> String {
rust_i18n::t!("switch_status_about")
}
#[cfg(windows)]
pub fn switch_install_about() -> String {
rust_i18n::t!("switch_install_about")
}
#[cfg(windows)]
pub fn switch_path_help() -> String {
rust_i18n::t!("switch_path_help")
}
#[cfg(windows)]
pub fn switch_auto_help() -> String {
rust_i18n::t!("switch_auto_help")
}
#[cfg(windows)]
pub fn switch_uninstall_about() -> String {
rust_i18n::t!("switch_uninstall_about")
}
#[cfg(windows)]
pub fn switch_config_about() -> String {
rust_i18n::t!("switch_config_about")
}
#[cfg(windows)]
pub fn switch_use_root_print() -> String {
rust_i18n::t!("switch_use_admin_print")
}
#[cfg(unix)]
pub fn switch_use_root_print() -> String {
rust_i18n::t!("switch_use_root_print")
}
#[cfg(windows)]
pub fn switch_service_not_start_print() -> String {
rust_i18n::t!("switch_service_not_start_print")
}
pub fn switch_start_successfully_print() -> String {
rust_i18n::t!("switch_start_successfully_print")
}
#[cfg(windows)]
pub fn switch_start_failed_print() -> String {
rust_i18n::t!("switch_start_failed_print")
}
#[cfg(windows)]
pub fn switch_service_not_stopped_print() -> String {
rust_i18n::t!("switch_service_not_stopped_print")
}
#[cfg(windows)]
pub fn switch_server_already_installed_print() -> String {
rust_i18n::t!("switch_server_already_installed_print")
}
pub fn switch_repeated_start_print() -> String {
rust_i18n::t!("switch_repeated_start_print")
}
pub fn switch_stopped_print() -> String {
rust_i18n::t!("switch_stopped_print")
}
pub fn switch_token_not_found_print() -> String {
rust_i18n::t!("switch_token_not_found_print")
}
pub fn switch_token_cannot_be_empty_print() -> String {
rust_i18n::t!("switch_token_cannot_be_empty_print")
}
pub fn switch_token_cannot_exceed_64_print() -> String {
rust_i18n::t!("switch_token_cannot_exceed_64_print")
}
pub fn switch_device_id_is_empty_print() -> String {
rust_i18n::t!("switch_device_id_is_empty_print")
}
pub fn switch_in_ips_example_print() -> String {
rust_i18n::t!("switch_in_ips_example_print")
}
pub fn switch_out_ips_example_print() -> String {
rust_i18n::t!("switch_out_ips_example_print")
}
pub fn switch_relay_server_address_error() -> String {
rust_i18n::t!("switch_relay_server_address_error")
}
pub fn switch_nat_test_server_address_error() -> String {
rust_i18n::t!("switch_nat_test_server_address_error")
}
pub fn switch_press_any_key_to_exit() -> String {
rust_i18n::t!("switch_press_any_key_to_exit")
}
pub fn switch_virtual_ip() -> String {
rust_i18n::t!("switch_virtual_ip")
}
pub fn switch_virtual_gateway() -> String {
rust_i18n::t!("switch_virtual_gateway")
}
pub fn switch_please_enter_the_command() -> String {
rust_i18n::t!("switch_please_enter_the_command")
}
+27
View File
@@ -0,0 +1,27 @@
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(())
}
+206 -246
View File
@@ -1,265 +1,225 @@
use std::thread;
use std::time::Duration;
use clap::{Parser, Subcommand};
use console::style;
use switch::core::Switch;
use crate::config::log_config::log_init;
mod command;
mod config;
#[cfg(target_os = "windows")]
mod windows;
#[cfg(any(unix))]
mod unix;
mod console_out;
mod command_args;
mod i18n;
#[derive(Parser, Debug)]
#[command(
author = "Lu Beilin",
version,
about = "一个虚拟网络工具,启动后会获取一个ip,相同token下的设备之间可以用ip直接通信"
)]
pub struct BaseArgs {
#[clap(subcommand)]
command: Commands,
}
#[derive(Subcommand, Debug)]
enum Commands {
/// 启动
Start(StartArgs),
/// 停止后台服务
Stop,
/// 安装服务
/// Install service
#[cfg(target_os = "windows")]
Install(InstallArgs),
/// 卸载服务
/// Uninstall service
#[cfg(target_os = "windows")]
Uninstall,
/// 配置
#[cfg(target_os = "windows")]
Config(ConfigArgs),
/// 查看路由
/// View route
Route,
/// 查看设备列表
/// View device list
List {
/// 查看所有
#[arg(short, long)]
all: bool
},
/// 查看设备当前状态
/// View the current status of the device
Status,
}
#[derive(Parser, Debug, Default)]
pub struct StartArgs {
/// 不超过64个字符
/// 相同token的设备之间才能通信。
/// 建议使用uuid保证唯一性。
/// No more than 64 characters
/// Only devices with the same token can communicate with each other.
/// It is recommended to use uuid to ensure uniqueness
#[arg(long)]
token: Option<String>,
/// 给设备一个名称,为空时默认用系统版本信息
/// Give the device a name. If it is blank, the system version information will be used by default
#[arg(long, action)]
name: Option<String>,
/// 设备唯一标识,为空时默认使用MAC地址,不超过64个字符
/// Unique identification of the device. If it is blank, the MAC address is used by default. No more than 64 characters
#[arg(long)]
device_id: Option<String>,
/// 注册和中继服务器地址
/// Register and relay server address
#[arg(long)]
server: Option<String>,
/// NAT检测服务地址,使用逗号分隔
/// NAT detection service address. Use comma to separate
#[arg(long)]
nat_test_server: Option<String>,
/// 关闭命令服务,关闭后不能在其他进程直接使用route、list等命令查看信息
/// Turn off the command service. After turning off, you cannot directly use the route, list and other commands to view information in other processes
#[cfg(any(unix))]
#[arg(long)]
off_command_server: bool,
/// 记录日志,输出在 home/.switch_desktop 目录下,长时间使用时不建议开启
/// Output the log in the "home/.switch_desktop" directory
#[arg(long)]
log: bool,
/// 使用tap网卡
#[arg(long)]
tap: bool,
/// 配置点对网时使用,--in-ip 192.168.10.0/24,10.26.0.3,表示允许接收网段192.168.10.0/24的数据并转发到10.26.0.3
/// Use when configuring peer-to-peer networks
#[arg(long)]
in_ip: Option<Vec<String>>,
/// 配置点对网时使用,--out-ip 192.168.10.0/24,192.168.1.10,表示允许目标为192.168.10.0/24的数据从网卡192.168.1.10转发出去
/// Use when configuring peer-to-peer networks
#[arg(long)]
out_ip: Option<Vec<String>>,
/// 客户端数据加密
#[arg(long)]
password:Option<String>,
/// 读取配置文件 --config config_file_path
/// Read configuration file
#[arg(long)]
config: Option<String>,
}
#[cfg(target_os = "windows")]
#[derive(Parser, Debug)]
pub struct InstallArgs {
/// 安装路径
/// Service installation path
#[arg(long)]
path: String,
/// 服务开机自启动
/// Autostart on system startup
#[arg(long)]
auto: bool,
}
#[cfg(target_os = "windows")]
#[derive(Parser, Debug)]
pub struct ConfigArgs {
/// 服务开机自启动
/// Autostart on system startup
#[arg(long)]
auto: bool,
}
#[macro_use]
extern crate rust_i18n;
i18n!("locales", fallback = "en");
// 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() {
i18n::init();
let args: Vec<_> = std::env::args().collect();
if args.len() == 3 && args[1] == windows::SERVICE_FLAG {
//以服务的方式启动
config::set_win_server_home(std::path::PathBuf::from(&args[2]));
windows::service::start();
return;
} else {
if !command_args::check() {
return;
}
let args = BaseArgs::parse();
if let Commands::Start(start_args) = &args.command {
if start_args.log {
let _ = log_init();
#[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"));
}
}
windows::main0(args);
}
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
#[tokio::main]
async fn main() {
if sudo::RunningAs::Root != sudo::check() {
println!(
"{}",
style("需要使用root权限执行(Need to execute with root permission)...").red()
);
sudo::escalate_if_needed().unwrap();
}
let args = BaseArgs::parse();
if let Commands::Start(start_args) = &args.command {
if start_args.log {
let _ = log_init();
Err(e) => {
return Err(format!("server err:{}", e));
}
}
unix::main0(args).await;
}
};
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);
pub fn console_listen(switch: &Switch) {
use console::Term;
let term = Term::stdout();
println!("{}", style(i18n::switch_start_successfully_print()).green());
let current_device = switch.current_device();
println!("{}: {:?}", i18n::switch_virtual_ip(), style(current_device.virtual_ip()).green());
println!("{}: {:?}", i18n::switch_virtual_gateway(), style(current_device.virtual_gateway()).green());
loop {
println!(
"{}",
style(i18n::switch_please_enter_the_command()).color256(102)
);
match term.read_line() {
Ok(cmd) => {
#[cfg(unix)]
if cmd.is_empty() {
use libc::{STDIN_FILENO, isatty};
if !unsafe { isatty(STDIN_FILENO) != 0 } {
return;
}
}
if command(cmd.trim(), &switch).is_err() {
println!("{}", style("stopping").red());
if let Err(e) = switch.stop() {
println!("stop:{:?}", e);
}
thread::sleep(Duration::from_secs(2));
break;
}
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) => {
log::error!("read_line:{:?}", e);
println!("{}", style("stopping...").red());
if let Err(e) = switch.stop() {
log::error!("stop:{:?}", 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));
}
}
thread::sleep(Duration::from_secs(1));
break;
}
}
};
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);
}
}
}
println!("{}", style("stopped").red());
peer_list
}
fn command(cmd: &str, switch: &Switch) -> Result<(), ()> {
match cmd {
"route" => {
let list = command::server::command_route(switch);
console_out::console_route_table(list);
}
"list" => {
let list = command::server::command_list(switch);
console_out::console_device_list(list);
}
"status" => {
let status = command::server::command_status(switch);
console_out::console_status(status);
}
"help" | "h" => {
println!("Options: ");
println!(
"{} , Query the virtual IP of other devices",
style("list").green()
);
println!("{} , View current device status", style("status").green());
println!("{} , Exit the program", style("exit").green());
}
"exit" => {
return Err(());
}
_ => {
println!("command '{}' not found. ", style(cmd).red());
println!("Try to enter: '{}'", style("help").green());
#[tauri::command]
async fn close() {
let switch = SWITCH.lock().take();
match switch {
None => {}
Some(mut switch) => {
switch.stop().unwrap();
switch.wait_stop().await;
}
}
Ok(())
}
#[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,
}
-131
View File
@@ -1,131 +0,0 @@
use std::sync::Arc;
use console::style;
use fs2::FileExt;
use switch::core::{Config, Switch};
use crate::{BaseArgs, Commands, config};
use crate::command::{command, CommandEnum};
pub async fn main0(base_args: BaseArgs) {
match base_args.command {
Commands::Start(args) => {
let start_config = if let Some(config_path) = &args.config {
match config::read_config_file(config_path.into()) {
Ok(start_config) => {
start_config
}
Err(e) => {
println!("{}", style(&e).red());
log::error!("{:?}", e);
return;
}
}
} else {
match config::default_config(args) {
Ok(start_config) => {
start_config
}
Err(e) => {
println!("{}", style(&e).red());
log::error!("{:?}", e);
return;
}
}
};
let off_command_server = start_config.off_command_server;
let config = Config::new(
start_config.tap,
start_config.token.clone(),
start_config.device_id.clone(),
start_config.name.clone(),
start_config.server,
start_config.nat_test_server.clone(),
start_config.in_ips.clone(),
start_config.out_ips.clone(),
start_config.password.clone(),
);
let lock = match config::lock_file() {
Ok(lock) => {
lock
}
Err(e) => {
log::error!("{:?}",e);
println!("文件锁定失败:{:?}", e);
return;
}
};
if lock.try_lock_exclusive().is_err() {
println!("{}", style("文件被重复打开").red());
return;
}
let switch = match Switch::start(config).await {
Ok(switch) => {
switch
}
Err(e) => {
log::error!("{:?}", e);
println!("启动switch失败:{:?}", e);
lock.unlock().unwrap();
return;
}
};
let switch = Arc::new(switch);
let command_server = crate::command::server::CommandServer::new();
if off_command_server {
crate::console_listen(&switch);
log::info!("前台任务结束");
} else {
if let Err(e) = config::update_pid(std::process::id()) {
log::error!("{:?}", e);
}
let switch1 = switch.clone();
let handle = std::thread::Builder::new().name("cmd-server".into()).spawn(move || {
if let Err(e) = command_server.start(switch1) {
log::error!("{:?}", e);
}
}).unwrap();
crate::console_listen(&switch);
if let Err(e) = handle.join() {
log::error!("后台任务异常{:?}",e);
} else {
log::info!("后台任务结束");
}
}
lock.unlock().unwrap();
}
Commands::Stop => {
command(CommandEnum::Stop);
if let Ok(pid) = config::read_pid() {
if pid != 0 {
let kill_cmd = format!("kill {}", pid);
let kill_out = std::process::Command::new("sh")
.arg("-c")
.arg(&kill_cmd)
.output()
.expect("sh exec error!");
if !kill_out.status.success() {
println!("cmd:{:?},err:{:?}", kill_cmd, kill_out);
return;
}
}
}
println!("stopped")
}
Commands::Route => {
command(CommandEnum::Route);
}
Commands::List { all } => {
if all {
command(CommandEnum::ListAll);
} else {
command(CommandEnum::List);
}
}
Commands::Status => {
command(CommandEnum::Status);
}
}
}
-392
View File
@@ -1,392 +0,0 @@
use std::{io, thread};
use std::ffi::OsString;
use std::net::UdpSocket;
use std::path::PathBuf;
use std::time::Duration;
use console::style;
use fs2::FileExt;
use windows_service::Error;
use windows_service::service::{
ServiceAccess, ServiceErrorControl, ServiceInfo, ServiceStartType, ServiceState, ServiceType,
};
use windows_service::service_manager::{ServiceManager, ServiceManagerAccess};
use switch::core::{Config, Switch};
use crate::{BaseArgs, Commands, config, i18n};
use crate::command::{command, CommandEnum};
pub mod service;
mod windows_admin_check;
pub const SERVICE_FLAG: &'static str = "start_switch_service_v1_";
pub const SERVICE_NAME: &'static str = "switch-service-v1";
pub const SERVICE_TYPE: ServiceType = ServiceType::OWN_PROCESS;
fn admin_check() -> bool {
if !windows_admin_check::is_app_elevated() {
println!(
"{}",
style(i18n::switch_use_root_print()).red()
);
true
} else {
false
}
}
fn not_started() -> bool {
match service_state() {
Ok(state) => {
if state == ServiceState::Running {
return false;
} else {
println!("{}", i18n::switch_service_not_start_print())
}
}
Err(e) => {
println!("{:?}", e);
}
}
return true;
}
pub fn main0(base_args: BaseArgs) {
match base_args.command {
Commands::Start(args) => {
if admin_check() {
return;
}
{
// 允许应用通过防火墙
let _udp = UdpSocket::bind("0.0.0.0:0").unwrap();
}
let start_config = if let Some(config_path) = &args.config {
match config::read_config_file(config_path.into()) {
Ok(start_config) => {
start_config
}
Err(e) => {
println!("{}", style(&e).red());
log::error!("{:?}", e);
return;
}
}
} else {
match config::default_config(args) {
Ok(start_config) => {
start_config
}
Err(e) => {
println!("{}", style(&e).red());
log::error!("{:?}", e);
return;
}
}
};
match service_state() {
Ok(state) => {
if state == ServiceState::Stopped {
match start() {
Ok(_) => {
//需要检查启动状态
thread::sleep(Duration::from_secs(2));
println!("{}", style(i18n::switch_start_successfully_print()).green());
}
Err(e) => {
log::error!("{:?}", e);
println!("{}:{}", style(i18n::switch_start_failed_print()).red(), e);
}
}
} else {
println!("{}", i18n::switch_service_not_stopped_print());
}
}
Err(e) => {
match e {
Error::Winapi(ref e) => {
if let Some(code) = e.raw_os_error() {
if code == 1060 {
//指定的服务未安装。
let config = Config::new(
start_config.tap,
start_config.token,
start_config.device_id,
start_config.name,
start_config.server,
start_config.nat_test_server,
start_config.in_ips,
start_config.out_ips,
start_config.password,
);
let lock = match config::lock_file() {
Ok(lock) => {
lock
}
Err(e) => {
log::error!("文件锁定失败:{:?}",e);
println!("文件锁定失败:{:?}", e);
return;
}
};
if lock.try_lock_exclusive().is_err() {
println!("{}", style(i18n::switch_repeated_start_print()).red());
return;
}
tokio::runtime::Builder::new_multi_thread().enable_all().build().unwrap().block_on(async move {
match Switch::start(config).await {
Ok(switch) => {
crate::console_listen(&switch);
}
Err(e) => {
log::error!("{:?}", e);
println!("启动switch失败:{:?}", e);
}
}
});
lock.unlock().unwrap();
return;
}
}
}
_ => {}
}
println!("{:?}", e);
}
};
pause();
}
Commands::Stop => {
if not_started() {
return;
}
if admin_check() {
return;
}
match stop() {
Ok(_) => {
println!("{}", style(i18n::switch_stopped_print()).green())
}
Err(e) => {
log::error!("{:?}", e);
println!("停止失败:{}", e);
}
}
pause();
}
Commands::Install(args) => {
if admin_check() {
return;
}
if service_state().is_ok() {
println!("{}", i18n::switch_server_already_installed_print());
return;
}
let path: PathBuf = args.path.into();
if !path.exists() {
std::fs::create_dir_all(&path).unwrap();
}
if !path.is_dir() {
println!("参数必须为文件目录(Parameter must be a file directory)");
} else {
if let Err(e) = install(path, args.auto) {
log::error!("{:?}", e);
println!("安装失败:{}", e);
} else {
println!("{}", style("安装成功(Installation succeeded)").green())
}
}
pause();
}
Commands::Uninstall => {
if admin_check() {
return;
}
if service_state().is_err() {
println!("服务未安装");
}
if let Err(e) = uninstall() {
log::error!("{:?}", e);
println!("卸载失败:{}", e);
} else {
println!("{}", style("卸载成功(Uninstall succeeded)").green())
}
pause();
}
Commands::Config(args) => {
if service_state().is_err() {
println!("服务未安装");
}
if let Err(e) = change(args.auto) {
log::error!("{:?}", e);
println!("配置失败:{}", e);
} else {
println!("{}", style("配置成功(Config succeeded)").green())
}
pause();
}
Commands::Route => {
if not_started() {
return;
}
command(CommandEnum::Route);
}
Commands::List { all } => {
if not_started() {
return;
}
if all {
command(CommandEnum::ListAll);
} else {
command(CommandEnum::List);
}
}
Commands::Status => {
if not_started() {
return;
}
command(CommandEnum::Status);
}
}
}
fn pause() {
println!(
"{}",
style(i18n::switch_press_any_key_to_exit()).green()
);
use console::Term;
let term = Term::stdout();
let _ = term.read_char().unwrap();
}
fn install(mut path: PathBuf, auto: bool) -> Result<(), Error> {
if !path.is_absolute() {
path = path.canonicalize().unwrap();
}
let manager_access = ServiceManagerAccess::CONNECT | ServiceManagerAccess::CREATE_SERVICE;
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
let current_exe_path = std::env::current_exe().unwrap();
let service_path = path.join("switch-service-v1.exe");
std::fs::copy(current_exe_path, service_path.as_path()).unwrap();
if let Err(e) = std::fs::copy("wintun.dll", path.join("wintun.dll").as_path()) {
if e.kind() == io::ErrorKind::NotFound {
println!("'wintun.dll' not found. Please put 'wintun.dll' in the current directory");
std::process::exit(0);
} else {
panic!("{:?}", e)
}
}
let mut launch_arguments = Vec::new();
launch_arguments.push(OsString::from(SERVICE_FLAG));
launch_arguments.push(OsString::from(
config::get_home().to_str().unwrap(),
));
let start_type = if auto {
ServiceStartType::AutoStart
} else {
ServiceStartType::OnDemand
};
let service_info = ServiceInfo {
name: OsString::from(SERVICE_NAME),
display_name: OsString::from("switch service v1"),
service_type: SERVICE_TYPE,
start_type,
error_control: ServiceErrorControl::Normal,
executable_path: service_path.into(),
launch_arguments,
dependencies: vec![],
account_name: None, // run as System
account_password: None,
};
let service = service_manager.create_service(&service_info, ServiceAccess::CHANGE_CONFIG)?;
service.set_description("A VPN")?;
Ok(())
}
fn change(auto: bool) -> Result<(), Error> {
let manager_access = ServiceManagerAccess::CONNECT;
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
let service_access = ServiceAccess::QUERY_CONFIG | ServiceAccess::CHANGE_CONFIG;
let service = service_manager.open_service(SERVICE_NAME, service_access)?;
let config = service.query_config()?;
let start_type = if auto {
ServiceStartType::AutoStart
} else {
ServiceStartType::OnDemand
};
let executable_path = config.executable_path.to_string_lossy().to_string();
let executable_path = if executable_path.starts_with('"') && executable_path.ends_with('"') {
&executable_path[1..executable_path.len() - 1]
} else {
&executable_path
};
let mut split = executable_path.split(SERVICE_FLAG);
let executable_path = split.next().unwrap().trim();
let executable_path = if executable_path.starts_with('"') && executable_path.ends_with('"') {
PathBuf::from(&executable_path[1..executable_path.len() - 1])
} else {
PathBuf::from(executable_path)
};
let home_path = split.next().unwrap().trim();
let launch_arguments = vec![OsString::from(SERVICE_FLAG), OsString::from(home_path)];
let service_info = ServiceInfo {
name: OsString::from(SERVICE_NAME),
display_name: config.display_name,
service_type: SERVICE_TYPE,
start_type,
error_control: config.error_control,
executable_path,
launch_arguments,
dependencies: config.dependencies,
account_name: None, // run as System
account_password: None,
};
service.change_config(&service_info)?;
Ok(())
}
fn uninstall() -> Result<(), Error> {
let manager_access = ServiceManagerAccess::CONNECT;
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
let service_access = ServiceAccess::QUERY_STATUS | ServiceAccess::STOP | ServiceAccess::DELETE;
let service = service_manager.open_service(SERVICE_NAME, service_access)?;
let service_status = service.query_status()?;
if service_status.current_state != ServiceState::Stopped {
service.stop()?;
// Wait for service to stop
thread::sleep(Duration::from_secs(1));
}
service.delete()?;
Ok(())
}
fn start() -> Result<(), Error> {
let manager_access = ServiceManagerAccess::CONNECT;
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
let service = service_manager.open_service(SERVICE_NAME, ServiceAccess::START)?;
let args: Vec<_> = std::env::args().collect();
service.start(&args[1..])
}
fn service_state() -> Result<ServiceState, Error> {
let manager_access = ServiceManagerAccess::CONNECT;
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
let service_access = ServiceAccess::QUERY_STATUS;
let service = service_manager.open_service(SERVICE_NAME, service_access)?;
let service_status = service.query_status()?;
return Ok(service_status.current_state);
}
fn stop() -> Result<(), Error> {
let manager_access = ServiceManagerAccess::CONNECT;
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
let service = service_manager.open_service(SERVICE_NAME, ServiceAccess::STOP)?;
service.stop()?;
Ok(())
}
-201
View File
@@ -1,201 +0,0 @@
// #[macro_use]
// extern crate windows_service;
use std::ffi::OsString;
use std::sync::Arc;
use std::io;
use std::io::Write;
use std::path::PathBuf;
use std::time::Duration;
use clap::Parser;
use windows_service::{define_windows_service, service_control_handler, service_dispatcher};
use windows_service::service::{
ServiceControl, ServiceControlAccept, ServiceExitCode, ServiceState, ServiceStatus,
};
use windows_service::service_control_handler::ServiceControlHandlerResult;
use switch::core::{Config, Switch};
use crate::{BaseArgs, Commands, config};
use crate::windows::SERVICE_NAME;
define_windows_service!(ffi_service_main, switch_service_main);
pub fn switch_service_main(arguments: Vec<OsString>) {
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
match service_main(arguments).await {
Ok(_) => {}
Err(e) => {
log::error!("启动服务失败:{:?}",e);
}
}
})
}
async fn service_main(arguments: Vec<OsString>) -> windows_service::Result<()> {
let parker = crossbeam::sync::Parker::new();
let un_parker = parker.unparker().clone();
let event_handler = move |control_event| -> ServiceControlHandlerResult {
match control_event {
// Notifies a service to report its current status information to the service
// control manager. Always return NoError even if not implemented.
ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
// Handle stop
ServiceControl::Stop => {
un_parker.unpark();
log::info!("handler 服务停止");
ServiceControlHandlerResult::NoError
}
_ => ServiceControlHandlerResult::NotImplemented,
}
};
// Register system service event handler.
// The returned status handle should be used to report service status changes to the system.
let status_handle =
service_control_handler::register(SERVICE_NAME, event_handler)?;
// Tell the system that service is running
status_handle.set_service_status(ServiceStatus {
service_type: crate::windows::SERVICE_TYPE,
current_state: ServiceState::Running,
controls_accepted: ServiceControlAccept::STOP,
exit_code: ServiceExitCode::Win32(0),
checkpoint: 0,
wait_hint: Duration::default(),
process_id: None,
})?;
match start_switch(arguments).await {
Ok(_) => {
parker.park();
}
Err(e) => {
log::error!("服务启动失败 {:?}",e);
}
}
status_handle.set_service_status(ServiceStatus {
service_type: crate::windows::SERVICE_TYPE,
current_state: ServiceState::Stopped,
controls_accepted: ServiceControlAccept::empty(),
exit_code: ServiceExitCode::Win32(0),
checkpoint: 0,
wait_hint: Duration::default(),
process_id: None,
})
}
fn auto_config_path() -> io::Result<PathBuf> {
Ok(config::get_win_server_home().join("auto_config.yaml"))
}
fn save_auto_config(start_config: config::StartConfig) -> io::Result<()> {
let mut file = std::fs::File::create(auto_config_path()?)?;
let config = config::ArgsConfig::new(start_config);
match serde_yaml::to_string(&config) {
Ok(yaml) => {
file.write_all(yaml.as_bytes())
}
Err(e) => {
Err(io::Error::new(io::ErrorKind::Other, format!("{:?}", e)))
}
}
}
async fn start_switch(arguments: Vec<OsString>) -> switch::Result<()> {
let start_config = match BaseArgs::try_parse_from(arguments) {
Ok(args) => {
match args.command {
Commands::Start(args) => {
if args.log {
let _ = config::log_config::log_service_init();
}
if let Some(config_path) = &args.config {
match config::read_config_file(config_path.into()) {
Ok(start_config) => {
if let Err(e) = save_auto_config(start_config.clone()) {
log::warn!("配置文件保存失败:{:?}",e);
}
start_config
}
Err(e) => {
log::error!("{:?}", e);
return Err(switch::error::Error::Stop(e));
}
}
} else {
match config::default_config(args) {
Ok(start_config) => {
if let Err(e) = save_auto_config(start_config.clone()) {
log::warn!("配置文件保存失败:{:?}",e);
}
start_config
}
Err(e) => {
log::error!("{:?}", e);
return Err(switch::error::Error::Stop(e));
}
}
}
}
_ => {
return Err(switch::error::Error::Stop("配置文件错误".to_string()));
}
}
}
Err(_) => {
match config::read_config_file(auto_config_path()?) {
Ok(start_config) => {
if start_config.log {
let _ = config::log_config::log_service_init();
}
start_config
}
Err(e) => {
return Err(switch::error::Error::Stop(e));
}
}
}
};
let config = Config::new(
start_config.tap,
start_config.token,
start_config.device_id,
start_config.name,
start_config.server,
start_config.nat_test_server,
start_config.in_ips,
start_config.out_ips,
start_config.password,
);
log::info!("switch-service服务启动");
tokio::spawn(async move {
match Switch::start(config).await {
Ok(switch) => {
let switch = Arc::new(switch);
let command_server = crate::command::server::CommandServer::new();
if let Err(e) = config::update_pid(std::process::id()) {
log::error!("{:?}", e);
}
if let Err(e) = command_server.start(switch) {
log::error!("{:?}", e);
}
}
Err(e) => {
log::error!("{:?}", e);
}
};
});
Ok(())
}
pub fn start() {
log::info!("以服务的方式启动");
service_dispatcher::start(SERVICE_NAME, ffi_service_main).unwrap();
}
+69
View File
@@ -0,0 +1,69 @@
{
"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
}
]
}
}
+681
View File
@@ -0,0 +1,681 @@
<!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>
File diff suppressed because one or more lines are too long
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "switch-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"}
jni = { version = "0.21.1", default-features = false }
[lib]
crate-type = ["staticlib", "cdylib"]
+1
View File
@@ -0,0 +1 @@
## 提供给安卓端使用
+2
View File
@@ -0,0 +1,2 @@
pub mod switch_util;
pub mod switch;
+138
View File
@@ -0,0 +1,138 @@
use std::ptr;
use jni::errors::Error;
use jni::JNIEnv;
use jni::objects::{JClass, JObject, JValue};
use jni::sys::{jboolean, jbyte, jint, jlong, jobject, jobjectArray, jsize};
use switch::channel::Route;
use switch::core::sync::SwitchSync;
use switch::handle::PeerDeviceInfo;
#[no_mangle]
pub unsafe extern "C" fn Java_top_wherewego_switchjni_Switch_stop0(
_env: JNIEnv,
_class: JClass,
raw_switch: jlong,
) {
let switch = raw_switch as *mut SwitchSync;
let _ = (&*switch).stop();
}
#[no_mangle]
pub unsafe extern "C" fn Java_top_wherewego_switchjni_Switch_waitStop0(
_env: JNIEnv,
_class: JClass,
raw_switch: jlong,
) {
let switch = raw_switch as *mut SwitchSync;
let _ = (&mut *switch).wait_stop();
}
#[no_mangle]
pub unsafe extern "C" fn Java_top_wherewego_switchjni_Switch_waitStopMs0(
_env: JNIEnv,
_class: JClass,
raw_switch: jlong,
ms: jlong,
) -> jboolean {
let switch = raw_switch as *mut SwitchSync;
if (&mut *switch).wait_stop_ms(ms as _) {
jni::sys::JNI_TRUE
} else {
jni::sys::JNI_FALSE
}
}
#[no_mangle]
pub unsafe extern "C" fn Java_top_wherewego_switchjni_Switch_drop0(
_env: JNIEnv,
_class: JClass,
raw_switch: jlong,
) {
let switch = raw_switch as *mut SwitchSync;
let _ = Box::from_raw(switch).stop();
}
#[no_mangle]
pub unsafe extern "C" fn Java_top_wherewego_switchjni_Switch_list0(
mut env: JNIEnv,
_class: JClass,
raw_switch: jlong,
) -> jobjectArray {
let switch = raw_switch as *mut SwitchSync;
let switch = &mut *switch;
let list = switch.device_list();
let arr = match env.new_object_array(
list.len() as jsize,
"top/wherewego/switchjni/PeerDeviceInfo",
JObject::null(),
) {
Ok(arr) => { arr }
Err(e) => {
env.throw_new("java/lang/RuntimeException", format!("error:{:?}", e))
.expect("throw");
return ptr::null_mut();
}
};
for (index, peer) in list.into_iter().enumerate() {
let route = if let Some(route) = switch.route(&peer.virtual_ip) {
match route_parse(&mut env, route) {
Ok(route) => {
JObject::from_raw(route)
}
Err(_) => {
JObject::null()
}
}
} else {
JObject::null()
};
match peer_device_info_parse(&mut env, peer, route) {
Ok(peer) => {
match env.set_object_array_element(&arr, index as jsize, JObject::from_raw(peer)) {
Ok(_) => {}
Err(e) => {
env.throw_new("java/lang/RuntimeException", format!("error:{:?}", e))
.expect("throw");
return ptr::null_mut();
}
}
}
Err(e) => {
env.throw_new("java/lang/RuntimeException", format!("error:{:?}", e))
.expect("throw");
return ptr::null_mut();
}
}
}
arr.as_raw()
}
fn route_parse(env: &mut JNIEnv, route: Route) -> Result<jobject, Error> {
let address = route.addr.to_string();
let metric = route.metric;
let rt = route.rt;
let rs = env.new_object(
"top/wherewego/switchjni/Route",
"(Ljava/lang/String;BI)V",
&[JValue::Object(&env.new_string(address)?.into()),
JValue::Byte(metric as jbyte),
JValue::Int(rt as jint)],
)?;
Ok(rs.as_raw())
}
fn peer_device_info_parse(env: &mut JNIEnv, peer: PeerDeviceInfo, route: JObject) -> Result<jobject, Error> {
let virtual_ip = u32::from(peer.virtual_ip);
let name = peer.name.to_string();
let status = format!("{:?}", peer.status);
let rs = env.new_object(
"top/wherewego/switchjni/PeerDeviceInfo",
"(ILjava/lang/String;Ljava/lang/String;Ltop/wherewego/switchjni/Route;)V",
&[JValue::Int(virtual_ip as jint),
JValue::Object(&env.new_string(name)?.into()),
JValue::Object(&env.new_string(status)?.into()),
JValue::Object(&route)],
)?;
Ok(rs.as_raw())
}
+246
View File
@@ -0,0 +1,246 @@
use std::net::ToSocketAddrs;
use std::ptr;
use jni::errors::Error;
use jni::objects::{JClass, JObject, JString, JValue};
#[cfg(not(target_os = "android"))]
use jni::sys::jboolean;
use jni::sys::{jint, jlong, jobject};
use jni::JNIEnv;
use switch::core::Config;
use switch::core::sync::SwitchUtilSync;
use switch::handle::registration_handler::{RegResponse, ReqEnum};
#[cfg(not(target_os = "android"))]
use switch::tun_tap_device::DriverInfo;
fn to_string_not_null(env: &mut JNIEnv, config: &JObject, name: &'static str) -> Result<String, Error> {
let value = env.get_field(config, name, "Ljava/lang/String;")?.l()?;
if value.is_null() {
env.throw_new("Ljava/lang/NullPointerException", name)
.expect("throw");
return Err(Error::NullPtr(name));
}
let binding = JString::from(value);
let value = env.get_string(binding.as_ref())?;
match value.to_str() {
Ok(value) => Ok(value.to_string()),
Err(_) => {
env.throw_new("Ljava/lang/RuntimeException", "not utf-8")
.expect("throw");
return Err(Error::JavaException);
}
}
}
fn to_string(env: &mut JNIEnv, config: &JObject, name: &str) -> Result<Option<String>, Error> {
let value = env.get_field(config, name, "Ljava/lang/String;")?.l()?;
if value.is_null() {
return Ok(None);
}
let tmp = JString::from(value);
let value = env.get_string(tmp.as_ref())?;
match value.to_str() {
Ok(value) => Ok(Some(value.to_string())),
Err(_) => {
env.throw_new("Ljava/lang/RuntimeException", "not utf-8")
.expect("throw");
return Err(Error::JavaException);
}
}
}
fn new_sync(env: &mut JNIEnv, config: JObject) -> Result<SwitchUtilSync, Error> {
let token = to_string_not_null(env, &config, "token")?;
let name = to_string_not_null(env, &config, "name")?;
let device_id = to_string_not_null(env, &config, "deviceId")?;
let password = to_string(env, &config, "password")?;
let server_address_str = to_string_not_null(env, &config, "server")?;
let nat_test_server = to_string_not_null(env, &config, "natTestServer")?;
let server_address = match server_address_str.to_socket_addrs() {
Ok(mut rs) => {
if let Some(addr) = rs.next() {
addr
} else {
env.throw_new("Ljava/lang/RuntimeException", "server address err")
.expect("throw");
return Err(Error::JavaException);
}
}
Err(e) => {
env.throw_new("Ljava/lang/RuntimeException", format!("server address {}", e))
.expect("throw");
return Err(Error::JavaException);
}
};
let nat_test_server = nat_test_server.split(",").flat_map(|a| a.trim().to_socket_addrs()).flatten()
.collect::<Vec<_>>();
let config = Config::new(false,
token, device_id, name,
server_address, server_address_str,
nat_test_server, vec![],
vec![], password, false, None);
match SwitchUtilSync::new(config) {
Ok(switch_util) => {
Ok(switch_util)
}
Err(e) => {
env.throw_new("Ljava/lang/RuntimeException", format!("switch start error {}", e))
.expect("throw");
return Err(Error::JavaException);
}
}
}
#[no_mangle]
pub unsafe extern "C" fn Java_top_wherewego_switchjni_SwitchUtil_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));
return ptr as jlong;
}
Err(_) => {}
}
return 0;
}
#[no_mangle]
pub unsafe extern "C" fn Java_top_wherewego_switchjni_SwitchUtil_connect0(
mut env: JNIEnv,
_class: JClass,
raw_switch_util: jlong,
) -> jobject {
let raw_switch_util = raw_switch_util as *mut SwitchUtilSync;
match (&mut *raw_switch_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");
}
}
}
Err(e) => {
match e {
ReqEnum::TokenError => {
env.throw_new("top/wherewego/switchjni/exception/TokenErrorException", "TokenError")
.expect("throw");
}
ReqEnum::AddressExhausted => {
env.throw_new("top/wherewego/switchjni/exception/AddressExhaustedException", "AddressExhausted")
.expect("throw");
}
ReqEnum::Timeout => {
env.throw_new("top/wherewego/switchjni/exception/TimeoutException", "Timeout")
.expect("throw");
}
ReqEnum::ServerError(str) => {
env.throw_new("java/lang/RuntimeException", format!("switch connect error {}", str))
.expect("throw");
}
ReqEnum::Other(str) => {
env.throw_new("java/lang/RuntimeException", format!("switch connect error {}", str))
.expect("throw");
}
}
}
}
return ptr::null_mut();
}
#[cfg(target_os = "android")]
#[no_mangle]
pub unsafe extern "C" fn Java_top_wherewego_switchjni_SwitchUtil_createIface0(
_env: JNIEnv,
_class: JClass,
raw_switch_util: jlong,
fd: jint,
) {
let raw_switch_util = raw_switch_util as *mut SwitchUtilSync;
(&mut *raw_switch_util).create_iface(fd as i32);
}
#[cfg(not(target_os = "android"))]
#[no_mangle]
pub unsafe extern "C" fn Java_top_wherewego_switchjni_SwitchUtil_createIface0(
mut env: JNIEnv,
_class: JClass,
raw_switch_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();
match rs {
Ok(driver_info) => {
match driver_info_e(&mut env, driver_info) {
Ok(res) => {
return res;
}
Err(e) => {
env.throw(format!("switch create iface error {}", e)).expect("throw");
}
}
}
Err(e) => {
env.throw_new("java/lang/RuntimeException", format!("switch create iface error {}", e))
.expect("throw");
}
}
return ptr::null_mut();
}
#[no_mangle]
pub unsafe extern "C" fn Java_top_wherewego_switchjni_SwitchUtil_build0(
mut env: JNIEnv,
_class: JClass,
raw_switch_util: jlong,
) -> jlong {
let raw_switch_util = Box::from_raw(raw_switch_util as *mut SwitchUtilSync);
match raw_switch_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))
.expect("throw");
}
}
return 0;
}
fn reg_response(env: &mut JNIEnv, response: RegResponse) -> Result<jobject, Error> {
let virtual_ip = u32::from(response.virtual_ip);
let virtual_gateway = u32::from(response.virtual_gateway);
let virtual_netmask = u32::from(response.virtual_netmask);
let response = env.new_object(
"top/wherewego/switchjni/RegResponse",
"(III)V",
&[JValue::Int(virtual_ip as jint),
JValue::Int(virtual_gateway as jint),
JValue::Int(virtual_netmask as jint)],
)?;
Ok(response.into_raw())
}
#[cfg(not(target_os = "android"))]
fn driver_info_e(env: &mut JNIEnv, driver_info: DriverInfo) -> Result<jobject, Error> {
let is_tun = driver_info.device_type.is_tun();
let name = driver_info.name;
let version = driver_info.version;
let mac = driver_info.mac.unwrap_or(String::new());
let response = env.new_object(
"top/wherewego/switchjni/DriverInfo",
"(ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
&[JValue::Bool(is_tun as jboolean),
JValue::Object(&env.new_string(name)?.into()),
JValue::Object(&env.new_string(version)?.into()),
JValue::Object(&env.new_string(mac)?.into()), ],
)?;
Ok(response.into_raw())
}
+4 -11
View File
@@ -1,6 +1,6 @@
[package]
name = "switch"
version = "1.0.6"
version = "1.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -10,26 +10,18 @@ packet = { path = "./packet" }
bytes = "1.3.0"
log = "0.4.17"
libc = "0.2.137"
dashmap = "5.4.0"
crossbeam = "0.8.2"
crossbeam-utils = "0.8"
crossbeam-skiplist = "0.1"
parking_lot = "0.12.1"
rsa = "0.7.2"
rand = "0.8.5"
sha2 = { version = "0.10.6", features = ["oid"] }
aes-gcm = "0.10.2"
thiserror = "1.0.37"
chrono = "0.4.23"
#lazy_static = "1.4.0"
moka = "0.9.6"
protobuf = "3.2.0"
#local-ip-address = "0.4.9"
socket2 ={ version = "0.5.2", features = ["all"] }
tokio = { version = "1.28.1", features = ["full"] }
[target.'cfg(any(unix))'.dependencies]
[target.'cfg(any(target_os = "linux",target_os = "macos"))'.dependencies]
tun = { path = "./rust-tun" }
[target.'cfg(target_os = "windows")'.dependencies]
@@ -40,3 +32,4 @@ libloading = "0.7.4"
[build-dependencies]
protobuf-codegen = "3.2.0"
protoc-bin-vendored = "3.0.0"
+5 -5
View File
@@ -107,11 +107,11 @@ impl<B: AsRef<[u8]>> UdpPacket<B> {
}
}
impl<B: AsRef<[u8]> + AsMut<[u8]>> UdpPacket<B> {
fn header_mut(&mut self) -> &mut [u8] {
&mut self.buffer.as_mut()[..8]
}
}
// impl<B: AsRef<[u8]> + AsMut<[u8]>> UdpPacket<B> {
// fn header_mut(&mut self) -> &mut [u8] {
// &mut self.buffer.as_mut()[..8]
// }
// }
impl<B: AsRef<[u8]> + AsMut<[u8]>> UdpPacket<B> {
/// 设置源端口
-9
View File
@@ -27,15 +27,6 @@ pub mod macos;
#[cfg(target_os = "macos")]
pub use self::macos::{create, Configuration, Device, Queue};
#[cfg(target_os = "ios")]
pub mod ios;
#[cfg(target_os = "ios")]
pub use self::ios::{create, Configuration, Device, Queue};
#[cfg(target_os = "android")]
pub mod android;
#[cfg(target_os = "android")]
pub use self::android::{create, Configuration, Device, Queue};
#[cfg(test)]
mod test {
+143 -135
View File
@@ -1,27 +1,33 @@
use std::io;
use std::net::{Ipv4Addr, SocketAddr};
use std::sync::Arc;
use std::sync::atomic::{AtomicI64, AtomicUsize, Ordering};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Instant;
use crossbeam_skiplist::SkipMap;
use dashmap::DashMap;
use crossbeam_utils::atomic::AtomicCell;
use parking_lot::Mutex;
use tokio::net::UdpSocket;
use tokio::sync::Notify;
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::handle::recv_handler::ChannelDataHandler;
pub struct ContextInner {
pub(crate) lock: Mutex<()>,
pub(crate) count: AtomicUsize,
pub(crate) main_channel: Arc<UdpSocket>,
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,
}
#[derive(Clone)]
pub struct Context {
pub(crate) count: Arc<AtomicUsize>,
pub(crate) main_channel: Arc<UdpSocket>,
pub(crate) route_table: Arc<DashMap<Ipv4Addr, Vec<Route>>>,
pub(crate) route_table_time: Arc<SkipMap<(RouteKey, Ipv4Addr), AtomicI64>>,
pub(crate) status_receiver: Receiver<Status>,
pub(crate) status_sender: Arc<Sender<Status>>,
pub(crate) udp_map: Arc<SkipMap<usize, Arc<UdpSocket>>>,
pub(crate) channel_num: usize,
pub(crate) notify: Arc<Notify>,
pub(crate) inner: Arc<ContextInner>,
}
impl Context {
@@ -29,30 +35,32 @@ impl Context {
//当前版本只支持一个通道
let channel_num = 1;
let (status_sender, status_receiver) = channel(Status::Cone);
let status_sender = Arc::new(status_sender);
Self {
count: Arc::new(AtomicUsize::new(0)),
let inner = Arc::new(ContextInner {
lock: Mutex::new(()),
count: AtomicUsize::new(0),
main_channel,
route_table: Arc::new(DashMap::with_capacity(16)),
route_table_time: Arc::new(SkipMap::new()),
route_table: SkipMap::new(),
route_table_time: SkipMap::new(),
status_receiver,
status_sender,
udp_map: Arc::new(SkipMap::new()),
udp_map: SkipMap::new(),
channel_num,
notify: Arc::new(Notify::new()),
});
Self {
inner
}
}
}
impl Context {
pub fn is_close(&self) -> bool {
*self.status_receiver.borrow() == Status::Close
*self.inner.status_receiver.borrow() == Status::Close
}
pub fn is_cone(&self) -> bool {
*self.status_receiver.borrow() == Status::Cone
*self.inner.status_receiver.borrow() == Status::Cone
}
pub fn close(&self) {
let _ = self.status_sender.send(Status::Close);
let _ = self.inner.status_sender.send(Status::Close);
}
pub fn switch(&self, nat_type: NatType) {
match nat_type {
@@ -65,62 +73,64 @@ impl Context {
}
}
pub fn switch_to_cone(&self) {
let _ = self.status_sender.send(Status::Cone);
let _ = self.inner.status_sender.send(Status::Cone);
}
pub fn switch_to_symmetric(&self) {
let _ = self.status_sender.send(Status::Symmetric);
let _ = self.inner.status_sender.send(Status::Symmetric);
}
pub fn main_local_port(&self) -> io::Result<u16> {
self.main_channel.local_addr().map(|k| k.port())
self.inner.main_channel.local_addr().map(|k| k.port())
}
pub async fn send_main(&self, buf: &[u8], addr: SocketAddr) -> io::Result<usize> {
self.main_channel.send_to(buf, addr).await
self.inner.main_channel.send_to(buf, addr).await
}
pub(crate) async fn send_all(&self, buf: &[u8], addr: SocketAddr) -> io::Result<()> {
for udp in self.udp_map.iter() {
for udp in self.inner.udp_map.iter() {
udp.value().send_to(buf, addr).await?;
}
Ok(())
}
pub fn try_send_main(&self, buf: &[u8], addr: SocketAddr) -> io::Result<usize> {
self.main_channel.try_send_to(buf, addr)
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.route_table.get(id) {
let route = match v.len() {
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[0],
len => &v[self.count.fetch_add(1, Ordering::Relaxed) % len]
1 => v.value()[0],
len => v.value()[self.inner.count.fetch_add(1, Ordering::Relaxed) % len]
};
if let Some(udp) = self.udp_map.get(&route.index) {
drop(v);
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.route_table.get(id) {
if v.is_empty() {
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[self.count.fetch_add(1, Ordering::Relaxed) % v.len()];
if let Some(udp) = self.udp_map.get(&route.index) {
let route = v.value()[self.inner.count.fetch_add(1, Ordering::Relaxed) % v.value().len()];
drop(v);
if let Some(udp) = self.inner.udp_map.get(&route.index) {
return udp.value().try_send_to(buf, route.addr);
}
}
Err(io::Error::new(io::ErrorKind::NotFound, "route not found"))
}
pub async fn send_by_key(&self, buf: &[u8], route_key: &RouteKey) -> io::Result<usize> {
if let Some(udp) = self.udp_map.get(&route_key.index) {
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 let Some(udp) = self.udp_map.get(&route_key.index) {
if let Some(udp) = self.inner.udp_map.get(&route_key.index) {
return udp.value().try_send_to(buf, route_key.addr);
}
Err(io::Error::new(io::ErrorKind::NotFound, "route not found"))
@@ -133,9 +143,14 @@ impl Context {
}
fn add_route_(&self, id: Ipv4Addr, route: Route, only_if_absent: bool) {
let key = route.route_key();
let mut ref_mut = self.route_table.entry(id.clone()).or_insert(Vec::with_capacity(4));
let guard = self.inner.lock.lock();
let mut list = if let Some(entry) = self.inner.route_table.get(&id) {
entry.value().clone()
} else {
Vec::with_capacity(4)
};
let mut exist = false;
for x in ref_mut.iter_mut() {
for x in list.iter_mut() {
if x.metric < route.metric {
//不能比当前的路径更长
return;
@@ -150,37 +165,40 @@ impl Context {
break;
}
}
if !exist {
if exist {
list.sort_by_key(|k| k.sort_key());
} else {
if route.metric == 1 {
//添加了直连的则排除非直连的
ref_mut.retain(|k| k.metric == 1);
list.retain(|k| k.metric == 1);
}
ref_mut.push(route);
let max_len = self.channel_num;
if ref_mut.len() > max_len {
ref_mut.sort_by_key(|k| k.sort_key());
ref_mut.truncate(max_len);
list.push(route);
list.sort_by_key(|k| k.sort_key());
let max_len = self.inner.channel_num + 1;
if list.len() > max_len {
list.truncate(max_len);
}
}
self.route_table_time.insert((key, id), AtomicI64::new(chrono::Local::now().timestamp_millis()));
self.notify.notify_one();
self.inner.route_table.insert(id, list);
self.inner.route_table_time.insert((key, id), AtomicCell::new(Instant::now()));
drop(guard);
}
pub fn route(&self, id: &Ipv4Addr) -> Option<Vec<Route>> {
if let Some(v) = self.route_table.get(id) {
if let Some(v) = self.inner.route_table.get(id) {
Some(v.value().clone())
} else {
None
}
}
pub fn route_one(&self, id: &Ipv4Addr) -> Option<Route> {
if let Some(v) = self.route_table.get(id) {
if let Some(v) = self.inner.route_table.get(id) {
v.value().iter().max_by_key(|k| k.sort_key()).map(|k| *k)
} else {
None
}
}
pub fn route_to_id(&self, route_key: &RouteKey) -> Option<Ipv4Addr> {
for x in self.route_table_time.iter() {
for x in self.inner.route_table_time.iter() {
if &x.key().0 == route_key {
return Some(x.key().1);
}
@@ -188,20 +206,20 @@ impl Context {
None
}
pub fn need_punch(&self, id: &Ipv4Addr) -> bool {
if let Some(v) = self.route_table.get(id) {
if v.iter().filter(|k| k.is_p2p()).count() >= self.channel_num {
if let Some(v) = self.inner.route_table.get(id) {
if v.value().iter().filter(|k| k.is_p2p()).count() >= self.inner.channel_num {
return false;
}
}
true
}
pub fn route_table(&self) -> Vec<(Ipv4Addr, Vec<Route>)> {
self.route_table.iter().map(|k| (k.key().clone(), k.value().clone())).collect()
self.inner.route_table.iter().map(|k| (k.key().clone(), k.value().clone())).collect()
}
pub fn route_table_one(&self) -> Vec<(Ipv4Addr, Route)> {
let mut v = Vec::with_capacity(8);
for x in self.route_table.iter() {
if let Some(route) = x.value().iter().max_by_key(|k| k.sort_key()) {
for x in self.inner.route_table.iter() {
if let Some(route) = x.value().first() {
v.push((*x.key(), *route));
}
}
@@ -209,8 +227,8 @@ impl Context {
}
pub fn direct_route_table_one(&self) -> Vec<(Ipv4Addr, Route)> {
let mut v = Vec::with_capacity(8);
for x in self.route_table.iter() {
if let Some(route) = x.value().iter().max_by_key(|k| k.sort_key()) {
for x in self.inner.route_table.iter() {
if let Some(route) = x.value().first() {
if route.metric == 1 {
v.push((*x.key(), *route));
}
@@ -219,21 +237,28 @@ impl Context {
v
}
pub fn remove_route_all(&self, id: &Ipv4Addr) {
if let Some((_, v)) = self.route_table.remove(id) {
for x in v {
self.route_table_time.remove(&(x.route_key(), id.clone()));
let guard = self.inner.lock.lock();
if let Some(v) = self.inner.route_table.remove(id) {
for x in v.value() {
self.inner.route_table_time.remove(&(x.route_key(), *id));
}
}
drop(guard);
}
pub fn remove_route(&self, id: &Ipv4Addr, route_key: RouteKey) {
if let Some(mut v) = self.route_table.get_mut(id) {
v.retain(|x| x.route_key() != route_key);
self.route_table_time.remove(&(route_key, id.clone()));
let guard = self.inner.lock.lock();
if let Some(v) = self.inner.route_table.get(id) {
let mut routes = v.value().clone();
drop(v);
routes.retain(|x| x.route_key() != route_key);
self.inner.route_table.insert(*id, routes);
self.inner.route_table_time.remove(&(route_key, *id));
}
drop(guard);
}
pub fn update_read_time(&self, id: &Ipv4Addr, route_key: &RouteKey) {
if let Some(time) = self.route_table_time.get(&(*route_key, *id)) {
time.value().store(chrono::Local::now().timestamp_millis(), Ordering::Relaxed);
if let Some(time) = self.inner.route_table_time.get(&(*route_key, *id)) {
time.value().store(Instant::now());
}
}
}
@@ -270,57 +295,69 @@ impl Channel {
}
}
pub async fn start(self,
mut worker: SwitchWorker,
head_reserve: usize,//头部预留字节
symmetric_channel_num: usize,//对称网络,则再加一组监听,提升打洞成功率
) {
let mut context = self.context;
let main_channel = context.main_channel.clone();
let context = self.context;
let main_channel = context.inner.main_channel.clone();
let handler = self.handler.clone();
tokio::spawn(Self::start_(context.clone(), handler, main_channel, head_reserve, true));
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));
let mut cur_status = Status::Cone;
let mut status_receiver = context.inner.status_receiver.clone();
loop {
match context.status_receiver.changed().await {
Ok(_) => {
match *context.status_receiver.borrow() {
Status::Cone => {
cur_status = Status::Cone;
}
Status::Symmetric => {
if cur_status == Status::Symmetric {
continue;
}
cur_status = Status::Symmetric;
for _ in 0..symmetric_channel_num {
match UdpSocket::bind("0.0.0.0:0").await {
Ok(udp) => {
let udp = Arc::new(udp);
let context = context.clone();
let handler = self.handler.clone();
tokio::spawn(Self::start_(context, handler, udp, head_reserve, false));
tokio::select! {
_=worker.stop_wait()=>{
break;
}
rs=status_receiver.changed()=>{
match rs {
Ok(_) => {
let s = status_receiver.borrow().clone();
match s {
Status::Cone => {
cur_status = Status::Cone;
}
Status::Symmetric => {
if cur_status == Status::Symmetric {
continue;
}
Err(e) => {
log::error!("{}",e);
cur_status = Status::Symmetric;
for _ in 0..symmetric_channel_num {
match UdpSocket::bind("0.0.0.0:0").await {
Ok(udp) => {
let udp = Arc::new(udp);
let context = context.clone();
let handler = self.handler.clone();
tokio::spawn(Self::start_(worker.worker("symmetric_channel"),context, handler, udp, head_reserve, false));
}
Err(e) => {
log::error!("{}",e);
}
}
}
}
Status::Close => {
break;
}
}
}
Status::Close => {
Err(_) => {
break;
}
}
}
Err(_) => {
break;
}
}
}
worker.stop_all();
}
async fn start_(context: Context,
async fn start_(mut worker: SwitchWorker, context: Context,
mut handler: ChannelDataHandler,
udp: Arc<UdpSocket>,
head_reserve: usize,
is_core: bool) {
let mut status_receiver = context.status_receiver.clone();
let mut status_receiver = context.inner.status_receiver.clone();
#[cfg(target_os = "windows")]
use std::os::windows::io::AsRawSocket;
#[cfg(target_os = "windows")]
@@ -329,8 +366,8 @@ impl Channel {
use std::os::fd::AsRawFd;
#[cfg(any(unix))]
let id = udp.as_raw_fd() as usize;
context.udp_map.insert(id, udp.clone());
let mut buf = [0; 65536];
context.inner.udp_map.insert(id, udp.clone());
let mut buf = [0; 4096];
loop {
tokio::select! {
rs=udp.recv_from(&mut buf[head_reserve..])=>{
@@ -356,43 +393,14 @@ impl Channel {
}
}
}
_=worker.stop_wait()=>{
break;
}
}
}
context.udp_map.remove(&id);
context.inner.udp_map.remove(&id);
if is_core {
worker.stop_all();
}
}
}
// pub async fn start<H: ChannelDataHandler + Clone>(mut handler: H,
// mut status_receiver: Receiver<Status>,
// head_reserve: usize,
// core_channel_num: usize,
// symmetric_channel_num: usize) -> io::Result<()> {
// for _ in 0..core_channel_num {
// let d = channel(1);
// }
// let udp = UdpSocket::bind("0.0.0.0:0").await?;
// let d = status_receiver.changed().await;
// match d {
// Ok(_) => {
// match *status_receiver.borrow() {
// Status::Cone => {}
// Status::Symmetric => {}
// Status::Close => {}
// }
// }
// Err(_) => {}
// }
// let mut buf = [0; 65546];
// let result = udp.recv_from(&mut buf[head_reserve..]).await;
// match result {
// Ok((len, addr)) => {}
// Err(e) => {}
// }
// Ok(())
// }
//
// pub struct Channel<H: ChannelDataHandler + Clone> {
// handler: H,
//
// }
+12 -28
View File
@@ -1,19 +1,18 @@
use std::io;
use std::io::{Error, ErrorKind};
use std::net::Ipv4Addr;
use std::sync::atomic::Ordering;
use std::time::Duration;
use crate::channel::channel::Context;
use crate::channel::RouteKey;
pub struct Idle {
read_idle: i64,
read_idle: Duration,
context: Context,
}
impl Idle {
pub fn new(read_idle: i64,
pub fn new(read_idle: Duration,
context: Context, ) -> Self {
Self {
read_idle,
@@ -26,34 +25,19 @@ impl Idle {
/// 获取空闲路由
pub async fn next_idle(&self) -> io::Result<(Ipv4Addr, RouteKey)> {
loop {
let now = chrono::Local::now().timestamp_millis();
let last_read_idle = now - self.read_idle;
let mut min = i64::MAX;
for entry in self.context.route_table_time.iter() {
let mut is_read_idle = false;
if self.read_idle > 0 {
let last_read = entry.value().load(Ordering::Relaxed);
if last_read < last_read_idle {
is_read_idle = true;
} else {
if min > last_read {
min = last_read;
}
let mut max = Duration::from_secs(10);
for entry in self.context.inner.route_table_time.iter() {
let last_read = entry.value().load().elapsed();
if last_read >= self.read_idle {
return Ok((entry.key().1.clone(), entry.key().0.clone()));
} else {
if max < last_read {
max = last_read;
}
}
if is_read_idle {
return Ok((entry.key().1.clone(), entry.key().0.clone()));
}
}
if self.context.route_table_time.is_empty() {
self.context.notify.notified().await;
} else {
let sleep_time = chrono::Local::now().timestamp_millis() - min;
if sleep_time > 0 {
tokio::time::sleep(Duration::from_millis(sleep_time as u64)).await;
// let _ = tokio::time::timeout(Duration::from_millis(sleep_time as u64), self.context.notify.notified()).await;
}
}
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"));
}
+221 -79
View File
@@ -1,34 +1,45 @@
use std::{io, thread};
use std::net::{Ipv4Addr, SocketAddr};
use std::io;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::sync::Arc;
use aes_gcm::{Aes256Gcm, Key, KeyInit};
use std::time::Duration;
use crossbeam::atomic::AtomicCell;
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::sync::mpsc::channel;
use crate::channel::{Route, RouteKey};
use crate::channel::channel::{Channel, Context};
use crate::channel::idle::Idle;
use crate::channel::punch::{NatInfo, Punch};
use crate::channel::{Route, RouteKey};
use crate::channel::sender::ChannelSender;
use crate::core::status::SwitchStatusManger;
use crate::error::Error;
use crate::external_route::ExternalRoute;
use crate::handle::{ConnectStatus, CurrentDeviceInfo, heartbeat_handler, PeerDeviceInfo, punch_handler, registration_handler};
use crate::handle::recv_handler::ChannelDataHandler;
use crate::handle::tun_tap::{tap_handler, tun_handler};
use crate::handle::registration_handler::{RegResponse, ReqEnum};
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
use crate::handle::tun_tap::tap_handler;
use crate::handle::tun_tap::tun_handler;
use crate::igmp_server::IgmpServer;
use crate::nat::NatTest;
use crate::tun_tap_device;
use crate::tun_tap_device::DeviceWriter;
use crate::tun_tap_device::{DeviceReader, DeviceWriter};
pub mod status;
pub mod sync;
#[derive(Clone)]
pub struct Switch {
name: String,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
context: Context,
switch_status_manager: SwitchStatusManger,
device_writer: DeviceWriter,
/// 0. 机器纪元,每一次上线或者下线都会增1,用于感知网络中机器变化
/// 服务端和客户端的不一致,则服务端会推送新的设备列表
@@ -39,97 +50,196 @@ pub struct Switch {
peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>>,
}
impl Switch {
pub async fn start(config: Config) -> crate::Result<Switch> {
log::info!("config:{:?}",config);
let cipher = if let Some(key) = &config.key {
let key: &Key<Aes256Gcm> = key.into();
Some(Aes256Gcm::new(&key))
} else {
None
};
pub struct SwitchUtil {
config: Config,
main_channel: Arc<UdpSocket>,
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?);
let response = registration_handler::registration(&main_channel, config.server_address, config.token.clone(), config.device_id.clone(), config.name.clone()).await?;
let (cone_sender, cone_receiver) = channel(3);
let (symmetric_sender, symmetric_receiver) = channel(2);
let context = Context::new(main_channel, 1);
let punch = Punch::new(context.clone());
let idle = Idle::new(16000, context.clone());
let channel_sender = ChannelSender::new(context.clone());
let register = Arc::new(registration_handler::Register::new(channel_sender.clone(), config.server_address, config.token.clone(), config.device_id.clone(), config.name.clone()));
let device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>> = Arc::new(Mutex::new((0, Vec::new())));
let peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>> = Arc::new(SkipMap::new());
let connect_status = Arc::new(AtomicCell::new(ConnectStatus::Connected));
let virtual_ip = Ipv4Addr::from(response.virtual_ip);
let virtual_gateway = Ipv4Addr::from(response.virtual_gateway);
let virtual_netmask = Ipv4Addr::from(response.virtual_netmask);
let local_ip = crate::nat::local_ip()?;
let local_port = context.main_local_port()?;
// NAT检测
let nat_test = NatTest::new(config.nat_test_server.clone(), Ipv4Addr::from(response.public_ip), response.public_port as u16, local_ip, local_port);
let in_ips = config.in_ips.iter().map(|(dest, mask, _)| { (Ipv4Addr::from(*dest & *mask), Ipv4Addr::from(*mask)) }).collect::<Vec<(Ipv4Addr, Ipv4Addr)>>();
let out_ips = config.out_ips.iter().map(|(_, _, ip)| *ip).collect::<Vec<Ipv4Addr>>();
let out_external_route = ExternalRoute::new(config.out_ips);
let in_external_route = ExternalRoute::new(config.in_ips);
let current_device = Arc::new(AtomicCell::new(CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, config.server_address)));
let ip_proxy_map = crate::ip_proxy::init_proxy(channel_sender.clone(), out_ips, current_device.clone()).await?;
let (device_writer, igmp_server) = if config.tap {
Ok(SwitchUtil {
config,
main_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,
self.config.token.clone(), self.config.device_id.clone(),
self.config.name.clone()).await {
Ok(res) => {
let _ = self.response.insert(res.clone());
Ok(res)
}
Err(e) => {
Err(e)
}
}
}
#[cfg(any(target_os = "android"))]
pub fn create_iface(&mut self, vpn_fd: i32) {
let (device_writer, device_reader) = tun_tap_device::create(vpn_fd);
let _ = self.iface.insert((device_writer, device_reader));
}
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
pub fn create_iface(&mut self) -> io::Result<tun_tap_device::DriverInfo> {
if self.iface.is_some() {
return Err(io::Error::from(io::ErrorKind::AlreadyExists));
}
let response = match &self.response {
None => {
return Err(io::Error::from(io::ErrorKind::AlreadyExists));
}
Some(res) => {
res
}
};
let device_type = if self.config.tap {
#[cfg(windows)]
{
//删除switch的tun网卡避免ip冲突,因为非正常退出会保留网卡
tun_tap_device::delete_device(tun_tap_device::DeviceType::Tap);
tun_tap_device::delete_device(tun_tap_device::DeviceType::Tun);
}
let (tap_writer, tap_reader) = tun_tap_device::create_device(tun_tap_device::DeviceType::Tap, virtual_ip, virtual_netmask, virtual_gateway, in_ips)?;
let igmp_server = IgmpServer::new(tap_writer.clone());
//tap数据处理
tap_handler::start(channel_sender.clone(), tap_reader.clone(), tap_writer.clone(),
igmp_server.clone(), current_device.clone(), in_external_route, ip_proxy_map.clone(), cipher.clone());
(tap_writer, igmp_server)
tun_tap_device::DeviceType::Tap
} else {
#[cfg(windows)]
{
//删除switch的tap网卡避免ip冲突,非正常退出会保留网卡
tun_tap_device::delete_device(tun_tap_device::DeviceType::Tap);
}
// tun通道
let (tun_writer, tun_reader) = tun_tap_device::create_device(tun_tap_device::DeviceType::Tun, virtual_ip, virtual_netmask, virtual_gateway, in_ips)?;
let igmp_server = IgmpServer::new(tun_writer.clone());
//tun数据接收处理
tun_handler::start(channel_sender.clone(), tun_reader.clone(), tun_writer.clone(),
igmp_server.clone(), current_device.clone(), in_external_route, ip_proxy_map.clone(), cipher.clone());
(tun_writer, igmp_server)
tun_tap_device::DeviceType::Tun
};
let mtu = self.config.mtu.unwrap_or(1430);
let in_ips = self.config.in_ips.iter().map(|(dest, mask, _)| { (Ipv4Addr::from(*dest & *mask), Ipv4Addr::from(*mask)) }).collect::<Vec<(Ipv4Addr, Ipv4Addr)>>();
let (device_writer, device_reader, driver_info) = tun_tap_device::create_device(device_type, response.virtual_ip,
response.virtual_netmask, response.virtual_gateway, in_ips, mtu)?;
let _ = self.iface.insert((device_writer, device_reader));
Ok(driver_info)
}
pub async fn build(self) -> crate::Result<Switch> {
let response = match self.response {
None => {
return Err(Error::Stop("response None".to_string()));
}
Some(res) => {
res
}
};
let (device_writer, device_reader) = match self.iface {
None => {
return Err(Error::Stop("iface None".to_string()));
}
Some(res) => {
res
}
};
let config = self.config;
let 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 (cone_sender, cone_receiver) = channel(3);
let (symmetric_sender, symmetric_receiver) = channel(2);
let context = Context::new(self.main_channel, 1);
let punch = Punch::new(context.clone());
let idle = Idle::new(Duration::from_secs(16), context.clone());
let channel_sender = ChannelSender::new(context.clone());
let register = Arc::new(registration_handler::Register::new(channel_sender.clone(),
config.server_address, config.token.clone(),
config.device_id.clone(), config.name.clone()));
let device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>> = Arc::new(Mutex::new((response.epoch, response.device_info_list)));
let peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>> = Arc::new(SkipMap::new());
let connect_status = Arc::new(AtomicCell::new(ConnectStatus::Connected));
let 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()?;
// NAT检测
let nat_test = NatTest::new(config.nat_test_server.clone(), response.public_ip, response.public_port, local_ip, local_port);
let out_ips = config.out_ips.iter().map(|(_, _, ip)| *ip).collect::<Vec<Ipv4Addr>>();
let out_external_route = ExternalRoute::new(config.out_ips);
let in_external_route = if config.in_ips.is_empty() {
None
} else {
Some(ExternalRoute::new(config.in_ips))
};
let 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 {
let (tcp_proxy, udp_proxy, ip_proxy_map) = crate::ip_proxy::init_proxy(channel_sender.clone(), out_ips, current_device.clone()).await?;
(Some(tcp_proxy), Some(udp_proxy), Some(ip_proxy_map))
};
let igmp_server = if config.simulate_multicast {
Some(IgmpServer::new(device_writer.clone()))
} else {
None
};
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
if config.tap {
tap_handler::start(switch_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(),
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(),
igmp_server.clone(), current_device.clone(), in_external_route, ip_proxy_map.clone(), cipher.clone());
//外部数据接收处理
let channel_recv_handler = ChannelDataHandler::new(current_device.clone(), device_list.clone(),
register.clone(), nat_test.clone(), igmp_server,
device_writer.clone(), connect_status.clone(),
peer_nat_info_map.clone(), ip_proxy_map, out_external_route,
cone_sender, symmetric_sender, cipher);
let channel = Channel::new(context.clone(), channel_recv_handler);
thread::spawn(move || {
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build().unwrap()
.block_on(channel.start(14, 60));
});
{
let channel = Channel::new(context.clone(), channel_recv_handler);
let channel_worker = switch_status_manager.worker("channel_worker");
if let Some(tcp_proxy) = tcp_proxy {
tokio::spawn(tcp_proxy.start());
}
if let Some(udp_proxy) = udp_proxy {
tokio::spawn(udp_proxy.start());
}
tokio::spawn(async move {
channel.start(channel_worker, 14, 65).await
});
}
{
let other_worker = switch_status_manager.worker("punch_handler");
let nat_test = nat_test.clone();
let device_list = device_list.clone();
let current_device = current_device.clone();
// 定时心跳
heartbeat_handler::start_heartbeat(other_worker.worker("heartbeat"), channel_sender.clone(), device_list.clone(), current_device.clone(), config.server_address_str);
// 空闲检查
heartbeat_handler::start_idle(other_worker.worker("idle"), idle, channel_sender.clone());
// 打洞处理
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);
// 定时心跳
heartbeat_handler::start_heartbeat(channel_sender.clone(), device_list.clone(), current_device.clone()).await;
// 空闲检查
heartbeat_handler::start_idle(idle, channel_sender.clone()).await;
// 打洞处理
punch_handler::start(cone_receiver, punch.clone(), current_device.clone()).await;
punch_handler::start(symmetric_receiver, punch, current_device.clone()).await;
punch_handler::start_punch(nat_test.clone(), device_list.clone(), channel_sender.clone(), current_device.clone()).await;
log::info!("switch启动成功");
Ok(Switch {
name: config.name,
current_device,
context,
switch_status_manager,
device_writer,
nat_test,
device_list,
@@ -172,9 +282,34 @@ impl Switch {
}
pub fn stop(&self) -> io::Result<()> {
self.context.close();
self.switch_status_manager.stop_all();
self.device_writer.close()?;
let virtual_gateway = self.current_device.load().virtual_gateway;
let _ = std::net::UdpSocket::bind("0.0.0.0:0")?.send_to(&[0],
SocketAddr::V4(SocketAddrV4::new(virtual_gateway, 10000)));
Ok(())
}
pub async fn wait_stop(&mut self) {
self.switch_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()=>{
let _ = self.stop();
return true;
}
_=tokio::time::sleep(ms)=>{
return false;
}
}
}
}
impl Drop for Switch {
fn drop(&mut self) {
let _ = self.stop();
}
}
#[derive(Clone, Debug)]
@@ -184,21 +319,25 @@ pub struct Config {
pub device_id: String,
pub name: String,
pub server_address: SocketAddr,
pub server_address_str: String,
pub nat_test_server: Vec<SocketAddr>,
pub in_ips: Vec<(u32, u32, Ipv4Addr)>,
pub out_ips: Vec<(u32, u32, Ipv4Addr)>,
pub key: Option<[u8; 32]>,
pub simulate_multicast: bool,
pub mtu: Option<u16>,
}
use sha2::Digest;
impl Config {
pub fn new(tap: bool, token: String,
device_id: String,
name: String,
server_address: SocketAddr,
server_address_str: String,
nat_test_server: Vec<SocketAddr>,
in_ips: Vec<(u32, u32, Ipv4Addr)>, out_ips: Vec<(u32, u32, Ipv4Addr)>, password: Option<String>, ) -> Self {
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());
@@ -213,10 +352,13 @@ impl Config {
device_id,
name,
server_address,
server_address_str,
nat_test_server,
in_ips,
out_ips,
key,
simulate_multicast,
mtu,
}
}
}
+89
View File
@@ -0,0 +1,89 @@
use std::sync::Arc;
use tokio::sync::watch;
use tokio::sync::watch::{Receiver, Sender};
use crate::util::wait::WaitGroup;
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum SwitchStatus {
Starting,
Stopping,
}
pub struct SwitchWorker {
_name: String,
wg: WaitGroup,
status_s: Arc<Sender<SwitchStatus>>,
status_r: Receiver<SwitchStatus>,
}
impl SwitchWorker {
pub fn worker(&self, name: &str) -> Self {
self.wg.add();
SwitchWorker {
_name: name.to_string(),
wg: self.wg.clone(),
status_s: self.status_s.clone(),
status_r: self.status_r.clone(),
}
}
}
impl Drop for SwitchWorker {
fn drop(&mut self) {
self.wg.done();
}
}
impl SwitchWorker {
pub fn stop_all(&self) {
let _ = self.status_s.send(SwitchStatus::Stopping);
}
pub async fn stop_wait(&mut self) {
loop {
if *self.status_r.borrow() == SwitchStatus::Stopping {
return;
}
match self.status_r.changed().await {
Ok(_) => {
if *self.status_r.borrow() == SwitchStatus::Stopping {
return;
}
}
Err(_) => { return; }
}
}
}
}
#[derive(Clone)]
pub struct SwitchStatusManger {
wg: WaitGroup,
status_s: Arc<Sender<SwitchStatus>>,
status_r: Receiver<SwitchStatus>,
}
impl SwitchStatusManger {
pub fn new() -> Self {
let (status_s, status_r) = watch::channel(SwitchStatus::Starting);
Self {
wg: WaitGroup::new(),
status_s: Arc::new(status_s),
status_r,
}
}
pub fn stop_all(&self) {
let _ = self.status_s.send(SwitchStatus::Stopping);
}
pub async fn wait(&mut self) {
self.wg.wait().await
}
pub fn worker(&self, name: &str) -> SwitchWorker {
self.wg.add();
SwitchWorker {
_name: name.to_string(),
wg: self.wg.clone(),
status_s: self.status_s.clone(),
status_r: self.status_r.clone(),
}
}
}
+69
View File
@@ -0,0 +1,69 @@
use std::io;
use std::ops::Deref;
use std::time::Duration;
use tokio::runtime::Runtime;
use crate::core::{Config, Switch, SwitchUtil};
use crate::handle::registration_handler::{RegResponse, ReqEnum};
pub struct SwitchUtilSync {
switch_util: SwitchUtil,
runtime: Runtime,
}
pub struct SwitchSync {
switch: Switch,
runtime: Runtime,
}
impl SwitchUtilSync {
pub fn new(config: Config) -> io::Result<SwitchUtilSync> {
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,
runtime,
})
}
pub fn connect(&mut self) -> Result<RegResponse, ReqEnum> {
self.runtime.block_on(self.switch_util.connect())
}
#[cfg(any(target_os = "android"))]
pub fn create_iface(&mut self, vpn_fd: i32) {
self.switch_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()
}
pub fn build(self) -> crate::Result<SwitchSync> {
let runtime = self.runtime;
let switch = runtime.block_on(self.switch_util.build())?;
{
let mut switch = switch.clone();
std::thread::spawn(move || {
runtime.block_on(switch.wait_stop())
});
}
Ok(SwitchSync {
switch,
runtime: tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap(),
})
}
}
impl SwitchSync {
pub fn wait_stop(&mut self) {
self.runtime.block_on(self.switch.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)))
}
}
impl Deref for SwitchSync {
type Target = Switch;
fn deref(&self) -> &Self::Target {
&self.switch
}
}
-3
View File
@@ -1,14 +1,11 @@
use std::io;
use crossbeam::channel::RecvError;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("Io error")]
Io(#[from] io::Error),
#[error("Channel error")]
Channel(#[from] RecvError),
#[error("Protobuf error")]
Protobuf(#[from] protobuf::Error),
#[error("Invalid packet")]
+4 -3
View File
@@ -1,21 +1,22 @@
use std::net::Ipv4Addr;
use std::sync::Arc;
// 目标ip,子网掩码,网关
#[derive(Clone)]
pub struct ExternalRoute {
route_table: Vec<(u32, u32, Ipv4Addr)>,
route_table: Arc<Vec<(u32, u32, Ipv4Addr)>>,
}
impl ExternalRoute {
pub fn new(route_table: Vec<(u32, u32, Ipv4Addr)>) -> Self {
Self {
route_table
route_table:Arc::new(route_table)
}
}
pub fn route(&self, ip: &Ipv4Addr) -> Option<Ipv4Addr> {
let ip = u32::from_be_bytes(ip.octets());
for (dest, mask, gateway) in &self.route_table {
for (dest, mask, gateway) in self.route_table.iter() {
if *mask & ip == *mask & *dest {
return Some(*gateway);
}
+81 -43
View File
@@ -1,29 +1,34 @@
use std::net::Ipv4Addr;
use std::net::{Ipv4Addr, ToSocketAddrs};
use std::sync::Arc;
use std::time::Duration;
use std::io;
use chrono::Local;
use crossbeam::atomic::AtomicCell;
use crossbeam_utils::atomic::AtomicCell;
use parking_lot::Mutex;
use rand::prelude::SliceRandom;
use crate::channel::idle::Idle;
use crate::channel::Route;
use crate::channel::sender::ChannelSender;
use crate::core::status::SwitchWorker;
use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo};
use crate::protocol::control_packet::PingPacket;
use crate::protocol::{control_packet, NetPacket, Protocol, Version};
pub async fn start_idle(idle: Idle, sender: ChannelSender) {
pub fn start_idle(mut worker: SwitchWorker, idle: Idle, sender: ChannelSender) {
tokio::spawn(async move {
match start_idle_(idle, sender).await {
Ok(_) => {}
Err(e) => {
log::warn!("空闲检测任务停止:{:?}", e);
tokio::select! {
_=worker.stop_wait()=>{
return;
}
rs=start_idle_(idle, sender)=>{
if let Err(e) = rs {
log::warn!("空闲检测任务停止:{:?}", e);
}
}
}
worker.stop_all();
});
}
@@ -39,20 +44,30 @@ async fn start_idle_(idle: Idle, sender: ChannelSender) -> io::Result<()> {
}
}
pub async fn start_heartbeat(
pub fn start_heartbeat(
mut worker: SwitchWorker,
sender: ChannelSender,
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
server_address_str: String,
) {
tokio::spawn(async move {
if let Err(e) = start_heartbeat_(sender, device_list, current_device).await {
log::warn!("心跳任务停止:{:?}", e);
tokio::select! {
_=worker.stop_wait()=>{
return;
}
rs=start_heartbeat_(sender, device_list, current_device,server_address_str)=>{
if let Err(e) = rs {
log::warn!("心跳任务停止:{:?}", e);
}
}
}
worker.stop_all();
});
}
fn set_now_time(packet: &mut NetPacket<[u8; 16]>) -> io::Result<()> {
let current_time = Local::now().timestamp_millis() as u16;
let current_time = crate::handle::now_time() as u16;
let mut ping = PingPacket::new(packet.payload_mut())?;
ping.set_time(current_time);
Ok(())
@@ -62,6 +77,7 @@ async fn start_heartbeat_(
sender: ChannelSender,
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
server_address_str: String,
) -> io::Result<()> {
let mut net_packet = NetPacket::new([0u8; 16])?;
net_packet.set_version(Version::V1);
@@ -71,59 +87,81 @@ async fn start_heartbeat_(
net_packet.first_set_ttl(2);
let mut count = 0;
loop {
let current_device = current_device.load();
net_packet.set_source(current_device.virtual_ip());
if sender.is_close() {
return Ok(());
}
let mut current_dev = current_device.load();
if count % 6 == 0 {
if let Ok(mut addr) = server_address_str.to_socket_addrs() {
if let Some(addr) = addr.next() {
if addr != current_dev.connect_server {
let mut tmp = current_dev.clone();
tmp.connect_server = addr;
if current_device.compare_exchange(current_dev, tmp).is_ok() {
current_dev.connect_server = addr;
}
}
}
}
}
net_packet.set_source(current_dev.virtual_ip());
{
let mut ping = PingPacket::new(net_packet.payload_mut())?;
let epoch = { device_list.lock().0 };
ping.set_epoch(epoch);
}
set_now_time(&mut net_packet)?;
net_packet.set_destination(current_device.virtual_gateway());
if let Err(e) = sender.send_main(net_packet.buffer(), current_device.connect_server).await
net_packet.set_destination(current_dev.virtual_gateway());
if let Err(e) = sender.send_main(net_packet.buffer(), current_dev.connect_server).await
{
log::warn!(
"connect_server:{:?},e:{:?}",
current_device.connect_server,
current_dev.connect_server,
e
);
}
if count < 7 || count % 7 == 0 {
let mut route_list: Option<Vec<(Ipv4Addr, Vec<Route>)>> = None;
let peer_list = {device_list.lock().1.clone()};
let peer_list = { device_list.lock().1.clone() };
for peer in peer_list {
if peer.virtual_ip == current_dev.virtual_ip {
continue;
}
set_now_time(&mut net_packet)?;
net_packet.set_destination(peer.virtual_ip);
if sender
.send_by_id(net_packet.buffer(), &peer.virtual_ip).await
.is_err()
{
//没有路由则发送到网关
let _ = sender.try_send_main(net_packet.buffer(), current_device.connect_server);
//再随机发送到其他地址,看有没有客户端符合转发条件
let route_list = route_list.get_or_insert_with(|| {
let mut l = sender.route_table();
l.shuffle(&mut rand::thread_rng());
l
});
let mut num = 0;
'a: for (peer_ip, route_list) in route_list.iter() {
for route in route_list {
if peer_ip != &peer.virtual_ip && route.metric == 1 {
set_now_time(&mut net_packet)?;
let _ = sender.try_send_by_key(net_packet.buffer(), &route.route_key());
num += 1;
break;
}
if num >= 3 {
break 'a;
}
if let Some(route) = sender.route_one(&peer.virtual_ip) {
let _ = sender.send_by_key(net_packet.buffer(), &route.route_key()).await;
if route.is_p2p() {
continue;
}
} else {
//没有直连路由则发送到网关
let _ = sender.send_main(net_packet.buffer(), current_dev.connect_server).await;
continue;
}
//再随机发送到其他地址,看有没有客户端符合转发条件
let route_list = route_list.get_or_insert_with(|| {
let mut l = sender.route_table();
l.shuffle(&mut rand::thread_rng());
l
});
let mut num = 0;
'a: for (peer_ip, route_list) in route_list.iter() {
for route in route_list {
if peer_ip != &peer.virtual_ip && route.is_p2p() {
set_now_time(&mut net_packet)?;
let _ = sender.try_send_by_key(net_packet.buffer(), &route.route_key());
num += 1;
break;
}
if num >= 3 {
break 'a;
}
}
}
tokio::time::sleep(Duration::from_millis(1)).await;
}
} else {
for (peer_ip, route_list) in sender.route_table().iter() {
set_now_time(&mut net_packet)?;
+10 -1
View File
@@ -6,6 +6,15 @@ pub mod recv_handler;
pub mod registration_handler;
pub mod tun_tap;
pub fn now_time() -> u64 {
let now = std::time::SystemTime::now();
if let Ok(timestamp) = now.duration_since(std::time::UNIX_EPOCH) {
timestamp.as_secs() * 1000 + u64::from(timestamp.subsec_millis())
} else {
0
}
}
/// 是否在一个网段
fn check_dest(dest: Ipv4Addr, virtual_netmask: Ipv4Addr, virtual_network: Ipv4Addr) -> bool {
u32::from_be_bytes(dest.octets()) & u32::from_be_bytes(virtual_netmask.octets())
@@ -29,7 +38,7 @@ impl PeerDeviceInfo {
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[derive(Copy, Clone, Debug, Eq, PartialEq,Ord, PartialOrd)]
pub enum PeerDeviceStatus {
Online,
Offline,
+49 -36
View File
@@ -2,7 +2,7 @@ use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo};
use crate::nat::NatTest;
use crate::proto::message::{PunchInfo, PunchNatType};
use crate::protocol::{control_packet, other_turn_packet, NetPacket, Protocol, Version, MAX_TTL};
use crossbeam::atomic::AtomicCell;
use crossbeam_utils::atomic::AtomicCell;
use parking_lot::Mutex;
use protobuf::Message;
use rand::prelude::SliceRandom;
@@ -13,10 +13,17 @@ use std::io;
use tokio::sync::mpsc::Receiver;
use crate::channel::punch::{NatInfo, Punch};
use crate::channel::sender::ChannelSender;
use crate::core::status::SwitchWorker;
pub async fn start(receiver: Receiver<(Ipv4Addr, NatInfo)>, punch: Punch, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) {
pub fn start(mut worker: SwitchWorker, receiver: Receiver<(Ipv4Addr, NatInfo)>, punch: Punch, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) {
tokio::spawn(async move {
start0(receiver, punch, current_device).await;
tokio::select! {
_=start0(receiver, punch, current_device)=>{}
_=worker.stop_wait()=>{
return;
}
}
worker.stop_all();
});
}
@@ -47,56 +54,62 @@ async fn start_(
}
pub async fn start_punch(
mut worker: SwitchWorker,
nat_test: NatTest,
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
sender: ChannelSender,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
) {
tokio::spawn(async move {
if let Err(e) = start_punch_(nat_test, device_list, sender, current_device).await {
log::warn!("打洞处理任务停止 {:?}", e);
}
});
}
async fn start_punch_(
nat_test: NatTest,
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
sender: ChannelSender,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
) -> crate::Result<()> {
let mut num = 0;
let sleep_time = [3, 5, 7, 11, 13, 17, 19, 23, 29];
loop {
if sender.is_close() {
return Ok(());
break;
}
let current_device = current_device.load();
let nat_info = nat_test.nat_info();
{
let mut list = device_list.lock().clone().1;
list.shuffle(&mut rand::thread_rng());
let mut count = 0;
for info in list {
if info.virtual_ip <= current_device.virtual_ip {
continue;
tokio::select! {
rs= start_punch_(Duration::from_secs(sleep_time[num % sleep_time.len()]),&nat_test, &device_list, &sender, &current_device)=>{
if let Err(e) = rs {
log::warn!("打洞处理任务异常 {:?}", e);
}
if !sender.need_punch(&info.virtual_ip) {
continue;
}
count += 1;
if count > 2 {
break;
}
let buf = punch_packet(current_device.virtual_ip(), &nat_info, info.virtual_ip)?;
sender.send_main(&buf, current_device.connect_server).await?;
}
_=worker.stop_wait()=>{
break;
}
}
num += 1;
tokio::time::sleep(Duration::from_secs(sleep_time[num % sleep_time.len()])).await;
}
}
async fn start_punch_(
sleep_time: Duration,
nat_test: &NatTest,
device_list: &Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
sender: &ChannelSender,
current_device: &Arc<AtomicCell<CurrentDeviceInfo>>,
) -> crate::Result<()> {
let current_device = current_device.load();
let nat_info = nat_test.nat_info();
let mut list = device_list.lock().clone().1;
list.shuffle(&mut rand::thread_rng());
let mut count = 0;
for info in list {
if info.virtual_ip <= current_device.virtual_ip {
continue;
}
if !sender.need_punch(&info.virtual_ip) {
continue;
}
count += 1;
if count > 2 {
break;
}
let buf = punch_packet(current_device.virtual_ip(), &nat_info, info.virtual_ip)?;
let _ = sender.send_main(&buf, current_device.connect_server).await;
}
tokio::time::sleep(sleep_time).await;
Ok(())
}
pub fn punch_packet(
virtual_ip: Ipv4Addr,
nat_info: &NatInfo,
+74 -60
View File
@@ -4,8 +4,7 @@ use aes_gcm::{AeadInPlace, Aes256Gcm, Nonce, Tag};
use aes_gcm::aead::consts::{U12, U16};
use aes_gcm::aead::generic_array::GenericArray;
use chrono::Local;
use crossbeam::atomic::AtomicCell;
use crossbeam_utils::atomic::AtomicCell;
use crossbeam_skiplist::SkipMap;
use parking_lot::Mutex;
use protobuf::Message;
@@ -22,7 +21,7 @@ use crate::channel::{Route, RouteKey};
use crate::error::Error;
use crate::external_route::ExternalRoute;
use crate::handle::{check_dest, ConnectStatus, CurrentDeviceInfo, PeerDeviceInfo};
use crate::handle::{check_dest, ConnectStatus, CurrentDeviceInfo, PeerDeviceInfo, PeerDeviceStatus};
use crate::handle::registration_handler::Register;
use crate::igmp_server::IgmpServer;
use crate::ip_proxy::IpProxyMap;
@@ -40,11 +39,11 @@ pub struct ChannelDataHandler {
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
register: Arc<Register>,
nat_test: NatTest,
igmp_server: IgmpServer,
igmp_server: Option<IgmpServer>,
device_writer: DeviceWriter,
connect_status: Arc<AtomicCell<ConnectStatus>>,
peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>>,
ip_proxy_map: IpProxyMap,
ip_proxy_map: Option<IpProxyMap>,
out_external_route: ExternalRoute,
cone_sender: Sender<(Ipv4Addr, NatInfo)>,
symmetric_sender: Sender<(Ipv4Addr, NatInfo)>,
@@ -56,11 +55,11 @@ impl ChannelDataHandler {
device_list: Arc<Mutex<(u16, Vec<PeerDeviceInfo>)>>,
register: Arc<Register>,
nat_test: NatTest,
igmp_server: IgmpServer,
igmp_server: Option<IgmpServer>,
device_writer: DeviceWriter,
connect_status: Arc<AtomicCell<ConnectStatus>>,
peer_nat_info_map: Arc<SkipMap<Ipv4Addr, NatInfo>>,
ip_proxy_map: IpProxyMap,
ip_proxy_map: Option<IpProxyMap>,
out_external_route: ExternalRoute,
cone_sender: Sender<(Ipv4Addr, NatInfo)>,
symmetric_sender: Sender<(Ipv4Addr, NatInfo)>,
@@ -103,8 +102,9 @@ impl ChannelDataHandler {
let source = net_packet.source();
let current_device = self.current_device.load();
let destination = net_packet.destination();
let not_broadcast = !destination.is_broadcast() && !destination.is_multicast() && destination != current_device.broadcast_address;
if current_device.virtual_ip() != destination
&& !destination.is_broadcast() && !destination.is_multicast() && destination != current_device.broadcast_address
&& not_broadcast
&& 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);
@@ -141,9 +141,11 @@ impl ChannelDataHandler {
}
}
ip_turn_packet::Protocol::Igmp => {
let ipv4 = IpV4Packet::new(net_packet.payload())?;
if ipv4.protocol() == ipv4::protocol::Protocol::Igmp {
self.igmp_server.handle(ipv4.payload(), source)?;
if let Some(igmp_server) = &self.igmp_server {
let ipv4 = IpV4Packet::new(net_packet.payload())?;
if ipv4.protocol() == ipv4::protocol::Protocol::Igmp {
igmp_server.handle(ipv4.payload(), source)?;
}
}
return Ok(());
}
@@ -180,7 +182,9 @@ impl ChannelDataHandler {
let mut ipv4 = IpV4Packet::new(data)?;
match ipv4.protocol() {
ipv4::protocol::Protocol::Igmp => {
self.igmp_server.handle(ipv4.payload(), source)?;
if let Some(igmp_server) = &self.igmp_server {
igmp_server.handle(ipv4.payload(), source)?;
}
return Ok(());
}
ipv4::protocol::Protocol::Icmp => {
@@ -226,51 +230,53 @@ impl ChannelDataHandler {
}
_ => {}
}
if ipv4.destination_ip() != destination {
if let Some(gate_way) = self.out_external_route.route(&ipv4.destination_ip()) {
match ipv4.protocol() {
ipv4::protocol::Protocol::Tcp => {
let dest_ip = ipv4.destination_ip();
//转发到代理目标地址
let mut tcp_packet = packet::tcp::tcp::TcpPacket::new(source, destination, ipv4.payload_mut())?;
let source_port = tcp_packet.source_port();
let dest_port = tcp_packet.destination_port();
tcp_packet.set_destination_port(self.ip_proxy_map.tcp_proxy_port);
tcp_packet.update_checksum();
ipv4.set_destination_ip(destination);
ipv4.update_checksum();
self.ip_proxy_map.tcp_proxy_map.insert(SocketAddrV4::new(source, source_port),
(SocketAddrV4::new(gate_way, 0), SocketAddrV4::new(dest_ip, dest_port)));
}
ipv4::protocol::Protocol::Udp => {
let dest_ip = ipv4.destination_ip();
//转发到代理目标地址
let mut udp_packet = packet::udp::udp::UdpPacket::new(source, destination, ipv4.payload_mut())?;
let source_port = udp_packet.source_port();
let dest_port = udp_packet.destination_port();
udp_packet.set_destination_port(self.ip_proxy_map.udp_proxy_port);
udp_packet.update_checksum();
ipv4.set_destination_ip(destination);
ipv4.update_checksum();
self.ip_proxy_map.udp_proxy_map.insert(SocketAddrV4::new(source, source_port),
(SocketAddrV4::new(gate_way, 0), SocketAddrV4::new(dest_ip, dest_port)));
}
ipv4::protocol::Protocol::Icmp => {
let dest_ip = ipv4.destination_ip();
//转发到代理目标地址
let icmp_packet = icmp::IcmpPacket::new(ipv4.payload())?;
match icmp_packet.header_other() {
HeaderOther::Identifier(id, seq) => {
self.ip_proxy_map.icmp_proxy_map.insert((dest_ip, id, seq), source);
self.ip_proxy_map.send_icmp(ipv4.payload(), &gate_way, &dest_ip)?;
}
_ => {
return Ok(());
if not_broadcast && ipv4.destination_ip() != destination {
if let Some(ip_proxy_map) = &self.ip_proxy_map {
if let Some(gate_way) = self.out_external_route.route(&ipv4.destination_ip()) {
match ipv4.protocol() {
ipv4::protocol::Protocol::Tcp => {
let dest_ip = ipv4.destination_ip();
//转发到代理目标地址
let mut tcp_packet = packet::tcp::tcp::TcpPacket::new(source, destination, ipv4.payload_mut())?;
let source_port = tcp_packet.source_port();
let dest_port = tcp_packet.destination_port();
tcp_packet.set_destination_port(ip_proxy_map.tcp_proxy_port);
tcp_packet.update_checksum();
ipv4.set_destination_ip(destination);
ipv4.update_checksum();
ip_proxy_map.tcp_proxy_map.insert(SocketAddrV4::new(source, source_port),
(SocketAddrV4::new(gate_way, 0), SocketAddrV4::new(dest_ip, dest_port)));
}
ipv4::protocol::Protocol::Udp => {
let dest_ip = ipv4.destination_ip();
//转发到代理目标地址
let mut udp_packet = packet::udp::udp::UdpPacket::new(source, destination, ipv4.payload_mut())?;
let source_port = udp_packet.source_port();
let dest_port = udp_packet.destination_port();
udp_packet.set_destination_port(ip_proxy_map.udp_proxy_port);
udp_packet.update_checksum();
ipv4.set_destination_ip(destination);
ipv4.update_checksum();
ip_proxy_map.udp_proxy_map.insert(SocketAddrV4::new(source, source_port),
(SocketAddrV4::new(gate_way, 0), SocketAddrV4::new(dest_ip, dest_port)));
}
ipv4::protocol::Protocol::Icmp => {
let dest_ip = ipv4.destination_ip();
//转发到代理目标地址
let icmp_packet = icmp::IcmpPacket::new(ipv4.payload())?;
match icmp_packet.header_other() {
HeaderOther::Identifier(id, seq) => {
ip_proxy_map.icmp_proxy_map.insert((dest_ip, id, seq), source);
ip_proxy_map.send_icmp(ipv4.payload(), &gate_way, &dest_ip)?;
}
_ => {
return Ok(());
}
}
}
}
_ => {
return Ok(());
_ => {
return Ok(());
}
}
}
}
@@ -323,11 +329,14 @@ impl ChannelDataHandler {
if current_ip != new_ip {
// ip发生变化
log::info!("ip发生变化,old_ip:{:?},new_ip:{:?}",current_ip,new_ip);
let old_netmask = current_device.virtual_netmask;
let old_gateway = current_device.virtual_gateway();
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
let old_netmask = current_device.virtual_netmask;
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
let old_gateway = current_device.virtual_gateway();
let virtual_ip = Ipv4Addr::from(response.virtual_ip);
let virtual_gateway = Ipv4Addr::from(response.virtual_gateway);
let virtual_netmask = Ipv4Addr::from(response.virtual_netmask);
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
self.device_writer.change_ip(virtual_ip, virtual_netmask, virtual_gateway, old_netmask, old_gateway)?;
let new_current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway,
virtual_netmask, current_device.connect_server);
@@ -340,7 +349,7 @@ impl ChannelDataHandler {
service_packet::Protocol::PollDeviceList => {}
service_packet::Protocol::PushDeviceList => {
let device_list_t = DeviceList::parse_from_bytes(net_packet.payload())?;
let ip_list = device_list_t
let ip_list: Vec<PeerDeviceInfo> = device_list_t
.device_info_list
.into_iter()
.map(|info| {
@@ -351,6 +360,12 @@ impl ChannelDataHandler {
)
})
.collect();
let route = Route::from(*route_key, 2, 99);
for x in &ip_list {
if x.status == PeerDeviceStatus::Online {
context.add_route_if_absent(x.virtual_ip, route);
}
}
let mut dev = self.device_list.lock();
if dev.0 != device_list_t.epoch as u16 {
dev.0 = device_list_t.epoch as u16;
@@ -406,7 +421,7 @@ impl ChannelDataHandler {
}
ControlPacket::PongPacket(pong_packet) => {
context.update_read_time(&source, route_key);
let current_time = Local::now().timestamp_millis() as u16;
let current_time = crate::handle::now_time() as u16;
if current_time < pong_packet.time() {
return Ok(());
}
@@ -428,7 +443,6 @@ impl ChannelDataHandler {
}
}
ControlPacket::PunchRequest => {
// log::info!("PunchRequest route_key:{:?}",route_key);
//回应
net_packet.set_transport_protocol(control_packet::Protocol::PunchResponse.into());
net_packet.set_source(current_device.virtual_ip());
+109 -67
View File
@@ -1,18 +1,36 @@
use std::io;
use std::net::SocketAddr;
use std::sync::atomic::{AtomicI64, Ordering};
use std::time::Duration;
use std::net::{Ipv4Addr, SocketAddr};
use std::time::{Duration, Instant};
use crossbeam_utils::atomic::AtomicCell;
use chrono::Local;
use protobuf::Message;
use tokio::net::UdpSocket;
use crate::channel::sender::ChannelSender;
use crate::handle::PeerDeviceInfo;
use crate::error::*;
use crate::proto::message::{RegistrationRequest, RegistrationResponse};
use crate::protocol::error_packet::InErrorPacket;
use crate::protocol::{service_packet, NetPacket, Protocol, Version, MAX_TTL};
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,
@@ -20,77 +38,102 @@ pub async fn registration(
token: String,
device_id: String,
name: String,
) -> Result<RegistrationResponse> {
) -> Result<RegResponse, ReqEnum> {
let request_packet =
registration_request_packet(token.clone(), device_id.clone(), name.clone(), false)?;
registration_request_packet(token.clone(), device_id.clone(), name.clone(), false).unwrap();
let buf = request_packet.buffer();
let mut recv_buf = [0u8; 10240];
let mut count = 0;
loop {
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 = NetPacket::new(&recv_buf[..len])?;
match net_packet.protocol() {
Protocol::Service => {
match service_packet::Protocol::from(net_packet.transport_protocol()) {
service_packet::Protocol::RegistrationResponse => {
let response = RegistrationResponse::parse_from_bytes(net_packet.payload())?;
return Ok(response);
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()))
}
}
_ => println!("响应数据错误"),
}
_ => {
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 => return Err(Error::Stop("token错误".to_string())),
InErrorPacket::Disconnect => {
println!("断开连接");
}
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))
}
InErrorPacket::AddressExhausted => {
println!("地址用尽");
log::warn!("地址用尽");
}
InErrorPacket::OtherError(e) => match e.message() {
Ok(str) => {
println!("其他异常:{:?}", str);
log::warn!("其他异常{:?}",str);
}
Err(e) => println!("其他异常:{:?}", e),
},
Err(e) => Err(ReqEnum::Other(format!("{}", e))),
},
Err(e) => println!("数据解析异常:{:?}", e),
}
},
Err(e) => Err(ReqEnum::Other(format!("{}", e))),
}
_ => println!("响应数据错误"),
};
}
_ => Err(ReqEnum::ServerError("invalid data".to_string())),
}
}
Err(e) => {
println!("接收服务器数据失败:{:?}", e);
log::warn!("接收服务器数据失败:{:?}",e);
} else {
Err(ReqEnum::Other(format!("invalid data,from {}", addr)))
}
}
}
Err(_) => {
println!("接收超时");
log::warn!("接收超时");
Err(e) => {
Err(ReqEnum::Other(format!("receiver error:{}", e)))
}
}
}
}
Err(e) => {
println!("发送数据到服务器失败:{:?}", e);
log::warn!("发送数据到服务器失败:{:?}",e);
Err(_) => {
Err(ReqEnum::Timeout)
}
}
}
count += 1;
println!("重试中(retrying)...");
std::thread::sleep(Duration::from_secs(count % 10 + 1));
Err(e) => {
Err(ReqEnum::Other(format!("send error:{}", e)))
}
};
}
@@ -105,7 +148,7 @@ fn registration_request_packet(
request.device_id = device_id;
request.name = name;
request.is_fast = is_fast;
request.version = "1.0.6".to_string();
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)?;
@@ -123,7 +166,7 @@ pub struct Register {
token: String,
device_id: String,
name: String,
time: AtomicI64,
time: AtomicCell<Instant>,
}
impl Register {
@@ -140,16 +183,15 @@ impl Register {
token,
device_id,
name,
time: AtomicI64::new(0),
time: AtomicCell::new(Instant::now()),
}
}
pub async fn fast_register(&self) -> io::Result<()> {
let last = self.time.load(Ordering::Relaxed);
let new = Local::now().timestamp_millis();
if new - last < 1000
let last = self.time.load();
if last.elapsed() < Duration::from_secs(2)
|| self
.time
.compare_exchange(last, new, Ordering::Relaxed, Ordering::Relaxed)
.compare_exchange(last, Instant::now())
.is_err()
{
//短时间不重复注册
+104 -59
View File
@@ -18,16 +18,33 @@ use crate::protocol;
use crate::protocol::ip_turn_packet::BroadcastPacketEnd;
pub mod tun_handler;
#[cfg(any(target_os = "linux", target_os = "macos",target_os = "windows"))]
pub mod tap_handler;
async fn broadcast(sender: &ChannelSender, net_packet: &mut NetPacket<&mut [u8]>, data_len: usize, current_device: &CurrentDeviceInfo) -> Result<()> {
let mut peer_ips = Vec::with_capacity(8);
let vec = sender.direct_route_table_one();
let vec = sender.route_table_one();
let mut relay_count = 0;
const MAX_COUNT: usize = u8::MAX as usize;
for (peer_ip, route) in vec {
if sender.send_by_key(&net_packet.buffer()[..data_len], &route.route_key()).await.is_ok() {
if peer_ip == current_device.virtual_gateway {
continue;
}
if peer_ips.len() == MAX_COUNT {
break;
}
if route.is_p2p()
&& sender.send_by_key(&net_packet.buffer()[..data_len], &route.route_key()).await.is_ok() {
peer_ips.push(peer_ip);
} else {
relay_count += 1;
}
}
if relay_count == 0 && !peer_ips.is_empty() && peer_ips.len() != MAX_COUNT {
//不需要转发
return Ok(());
}
if peer_ips.is_empty() {
sender.send_main(&net_packet.buffer()[..data_len], current_device.connect_server).await?;
} else {
@@ -44,20 +61,32 @@ async fn broadcast(sender: &ChannelSender, net_packet: &mut NetPacket<&mut [u8]>
async fn multicast(igmp_server: &IgmpServer, multicast_addr: Ipv4Addr, sender: &ChannelSender, net_packet: &mut NetPacket<&mut [u8]>, data_len: usize, current_device: &CurrentDeviceInfo) -> Result<()> {
let mut peer_ips = Vec::with_capacity(8);
let vec = sender.direct_route_table_one();
let vec = sender.route_table_one();
let mut relay_count = 0;
const MAX_COUNT: usize = u8::MAX as usize;
if let Some(members) = igmp_server.load(&multicast_addr) {
for (peer_ip, route) in vec {
let is_send = {members.read().is_send(&peer_ip)};
if peer_ip == current_device.virtual_gateway {
continue;
}
let is_send = { members.read().is_send(&peer_ip) };
if is_send {
if sender.send_by_key(&net_packet.buffer()[..data_len], &route.route_key()).await.is_ok() {
if peer_ips.len() == MAX_COUNT {
break;
}
if route.is_p2p()
&& sender.send_by_key(&net_packet.buffer()[..data_len], &route.route_key()).await.is_ok() {
peer_ips.push(peer_ip);
if peer_ips.len() == u8::MAX as usize {
break;
}
} else {
relay_count += 1;
}
}
}
}
if relay_count == 0 && !peer_ips.is_empty() && peer_ips.len() != MAX_COUNT {
//不需要转发
return Ok(());
}
if peer_ips.is_empty() {
sender.send_main(&net_packet.buffer()[..data_len], current_device.connect_server).await?;
} else {
@@ -78,9 +107,9 @@ async fn multicast(igmp_server: &IgmpServer, multicast_addr: Ipv4Addr, sender: &
#[inline]
pub async fn base_handle(sender: &ChannelSender, buf: &mut [u8],
mut data_len: usize,//数据总长度=ip长度+12
igmp_server: &IgmpServer,
igmp_server: &Option<IgmpServer>,
current_device: CurrentDeviceInfo,
ip_route: &ExternalRoute, proxy_map: &IpProxyMap, cipher: &Option<Aes256Gcm>) -> Result<()> {
ip_route: &Option<ExternalRoute>, proxy_map: &Option<IpProxyMap>, cipher: &Option<Aes256Gcm>) -> 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;
@@ -102,11 +131,33 @@ pub async fn base_handle(sender: &ChannelSender, buf: &mut [u8],
return Ok(());
}
if dest_ip.is_multicast() {
if protocol == Protocol::Igmp {
net_packet.set_transport_protocol(ip_turn_packet::Protocol::Igmp.into());
//发送到服务端
sender.send_main(&net_packet.buffer()[..data_len], current_device.connect_server).await?;
return Ok(());
match protocol {
Protocol::Igmp => {
if igmp_server.is_some() {
net_packet.set_transport_protocol(ip_turn_packet::Protocol::Igmp.into());
//发送到服务端
net_packet.set_destination(current_device.virtual_gateway);
sender.send_main(&net_packet.buffer()[..data_len], current_device.connect_server).await?;
}
return Ok(());
}
Protocol::Udp => {
if let Some(igmp_server) = igmp_server {
if let Some(cipher) = cipher {
//需要加密
encrypt(cipher, &mut data_len, &mut net_packet)?;
}
multicast(igmp_server, dest_ip, sender, &mut net_packet, data_len, &current_device).await?;
return Ok(());
} else {
//当广播
dest_ip = Ipv4Addr::BROADCAST;
net_packet.set_destination(dest_ip);
}
}
_ => {
return Ok(());
}
}
}
if dest_ip.is_broadcast() || current_device.broadcast_address == dest_ip {
@@ -119,17 +170,9 @@ pub async fn base_handle(sender: &ChannelSender, buf: &mut [u8],
broadcast(sender, &mut net_packet, data_len, &current_device).await?;
}
return Ok(());
} else if dest_ip.is_multicast() {
if protocol == Protocol::Udp {
if let Some(cipher) = cipher {
//需要加密
encrypt(cipher, &mut data_len, &mut net_packet)?;
}
multicast(igmp_server, dest_ip, sender, &mut net_packet, data_len, &current_device).await?;
}
return Ok(());
} else {
if !check_dest(dest_ip, current_device.virtual_netmask, current_device.virtual_network) {
}
if !check_dest(dest_ip, current_device.virtual_netmask, current_device.virtual_network) {
if let Some(ip_route) = ip_route {
if let Some(r_dest_ip) = ip_route.route(&dest_ip) {
//路由的目标不能是自己
if r_dest_ip == src_ip {
@@ -142,41 +185,43 @@ pub async fn base_handle(sender: &ChannelSender, buf: &mut [u8],
return Ok(());
}
} else {
match protocol {
Protocol::Tcp => {
let dest_addr = {
let tcp_packet = TcpPacket::new(src_ip, dest_ip, &mut net_packet.buffer_mut()[12 + ip_head_len..data_len])?;
SocketAddrV4::new(dest_ip, tcp_packet.destination_port())
};
if let Some(entry) = proxy_map.tcp_proxy_map.get(&dest_addr) {
let source_addr = entry.value().1;
let source_ip = *source_addr.ip();
let mut tcp_packet = TcpPacket::new(source_ip, dest_ip, &mut net_packet.buffer_mut()[12 + ip_head_len..data_len])?;
tcp_packet.set_source_port(source_addr.port());
tcp_packet.update_checksum();
let mut ipv4_packet = IpV4Packet::new(&mut net_packet.buffer_mut()[12..data_len])?;
ipv4_packet.set_source_ip(source_ip);
ipv4_packet.update_checksum();
}
return Ok(());
}
} else if let Some(proxy_map) = proxy_map {
match protocol {
Protocol::Tcp => {
let dest_addr = {
let tcp_packet = TcpPacket::new(src_ip, dest_ip, &mut net_packet.buffer_mut()[12 + ip_head_len..data_len])?;
SocketAddrV4::new(dest_ip, tcp_packet.destination_port())
};
if let Some(entry) = proxy_map.tcp_proxy_map.get(&dest_addr) {
let source_addr = entry.value().1;
let source_ip = *source_addr.ip();
let mut tcp_packet = TcpPacket::new(source_ip, dest_ip, &mut net_packet.buffer_mut()[12 + ip_head_len..data_len])?;
tcp_packet.set_source_port(source_addr.port());
tcp_packet.update_checksum();
let mut ipv4_packet = IpV4Packet::new(&mut net_packet.buffer_mut()[12..data_len])?;
ipv4_packet.set_source_ip(source_ip);
ipv4_packet.update_checksum();
}
Protocol::Udp => {
let dest_addr = {
let udp_packet = UdpPacket::new(src_ip, dest_ip, &mut net_packet.buffer_mut()[12 + ip_head_len..data_len])?;
SocketAddrV4::new(dest_ip, udp_packet.destination_port())
};
if let Some(entry) = proxy_map.udp_proxy_map.get(&dest_addr) {
let source_addr = entry.value().1;
let source_ip = *source_addr.ip();
let mut udp_packet = UdpPacket::new(source_ip, dest_ip, &mut net_packet.buffer_mut()[12 + ip_head_len..data_len])?;
udp_packet.set_source_port(source_addr.port());
udp_packet.update_checksum();
let mut ipv4_packet = IpV4Packet::new(&mut net_packet.buffer_mut()[12..data_len])?;
ipv4_packet.set_source_ip(source_ip);
ipv4_packet.update_checksum();
}
}
_ => {}
}
Protocol::Udp => {
let dest_addr = {
let udp_packet = UdpPacket::new(src_ip, dest_ip, &mut net_packet.buffer_mut()[12 + ip_head_len..data_len])?;
SocketAddrV4::new(dest_ip, udp_packet.destination_port())
};
if let Some(entry) = proxy_map.udp_proxy_map.get(&dest_addr) {
let source_addr = entry.value().1;
let source_ip = *source_addr.ip();
let mut udp_packet = UdpPacket::new(source_ip, dest_ip, &mut net_packet.buffer_mut()[12 + ip_head_len..data_len])?;
udp_packet.set_source_port(source_addr.port());
udp_packet.update_checksum();
let mut ipv4_packet = IpV4Packet::new(&mut net_packet.buffer_mut()[12..data_len])?;
ipv4_packet.set_source_ip(source_ip);
ipv4_packet.update_checksum();
}
}
_ => {}
}
}
if let Some(cipher) = cipher {
+15 -13
View File
@@ -1,7 +1,7 @@
use std::sync::Arc;
use std::{io, thread};
use aes_gcm::Aes256Gcm;
use crossbeam::atomic::AtomicCell;
use crossbeam_utils::atomic::AtomicCell;
use packet::arp::arp::ArpPacket;
use packet::ethernet;
use packet::ethernet::packet::EthernetPacket;
@@ -10,21 +10,22 @@ 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::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(sender: ChannelSender,
pub fn start(worker: SwitchWorker, sender: ChannelSender,
device_reader: DeviceReader,
device_writer: DeviceWriter,
igmp_server: IgmpServer,
igmp_server: Option<IgmpServer>,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
ip_route: ExternalRoute,
ip_proxy_map: IpProxyMap,
ip_route: Option<ExternalRoute>,
ip_proxy_map: Option<IpProxyMap>,
cipher: Option<Aes256Gcm>) {
thread::Builder::new().name("tap-handler".into()).spawn(move || {
thread::Builder::new().name("tap_handler".into()).spawn(move || {
tokio::runtime::Builder::new_current_thread()
.enable_all().build().unwrap()
.block_on(async move {
@@ -33,6 +34,7 @@ pub fn start(sender: ChannelSender,
current_device, ip_route, ip_proxy_map, cipher).await {
log::warn!("tap:{:?}",e);
}
worker.stop_all();
});
}).unwrap();
}
@@ -40,23 +42,23 @@ pub fn start(sender: ChannelSender,
async fn start_(sender: ChannelSender,
device_reader: DeviceReader,
device_writer: DeviceWriter,
igmp_server: IgmpServer,
igmp_server: Option<IgmpServer>,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
ip_route: ExternalRoute,
ip_proxy_map: IpProxyMap,
ip_route: Option<ExternalRoute>,
ip_proxy_map: Option<IpProxyMap>,
cipher: Option<Aes256Gcm>) -> io::Result<()> {
let mut buf = [0; 2048];
let mut buf = [0; 4096];
loop {
//ip拆包了会直接丢弃?
let len = device_reader.read(&mut buf)?;
if let Err(e) = handle(&mut buf, len, &igmp_server, &current_device, &device_writer, &sender, &ip_route, &ip_proxy_map, &cipher).await {
log::error!("tap handle{:?}",e);
log::warn!("tap handle{:?}",e);
}
}
}
async fn handle(buf: &mut [u8], len: usize, igmp_server: &IgmpServer, current_device: &AtomicCell<CurrentDeviceInfo>,
device_writer: &DeviceWriter, sender: &ChannelSender, ip_route: &ExternalRoute, proxy_map: &IpProxyMap, cipher: &Option<Aes256Gcm>) -> crate::Result<()> {
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<()> {
let mut ethernet_packet = EthernetPacket::new(&mut buf[..len])?;
let current_device = current_device.load();
match ethernet_packet.protocol() {
+24 -16
View File
@@ -2,13 +2,14 @@ use std::{io, thread};
use std::sync::Arc;
use aes_gcm::Aes256Gcm;
use crossbeam::atomic::AtomicCell;
use crossbeam_utils::atomic::AtomicCell;
use packet::icmp::Kind;
use packet::icmp::icmp::IcmpPacket;
use packet::ip::ipv4;
use packet::ip::ipv4::packet::IpV4Packet;
use crate::channel::sender::ChannelSender;
use crate::core::status::SwitchWorker;
use crate::error::*;
use crate::external_route::ExternalRoute;
@@ -35,8 +36,8 @@ 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: &IgmpServer, current_device: CurrentDeviceInfo,
ip_route: &ExternalRoute, proxy_map: &IpProxyMap,cipher: &Option<Aes256Gcm>) -> Result<()> {
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<()> {
let ipv4_packet = if let Ok(ipv4_packet) = IpV4Packet::new(&mut data[12..len]) {
ipv4_packet
} else {
@@ -50,40 +51,47 @@ async fn handle(sender: &ChannelSender, data: &mut [u8], len: usize, device_writ
if src_ip == dest_ip {
return icmp(&device_writer, ipv4_packet);
}
return crate::handle::tun_tap::base_handle(sender, data, len, igmp_server, current_device, ip_route, proxy_map,cipher).await;
return crate::handle::tun_tap::base_handle(sender, data, len, igmp_server, current_device, ip_route, proxy_map, cipher).await;
}
pub fn start(sender: ChannelSender,
pub fn start(worker: SwitchWorker, sender: ChannelSender,
device_reader: DeviceReader,
device_writer: DeviceWriter,
igmp_server: IgmpServer,
igmp_server: Option<IgmpServer>,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
ip_route: ExternalRoute,
ip_proxy_map: IpProxyMap,
ip_route: Option<ExternalRoute>,
ip_proxy_map: Option<IpProxyMap>,
cipher: Option<Aes256Gcm>) {
thread::Builder::new().name("tun-handler".into()).spawn(move || {
thread::Builder::new().name("tun_handler".into()).spawn(move || {
tokio::runtime::Builder::new_current_thread()
.enable_all().build().unwrap()
.block_on(async move {
if let Err(e) = start_(sender, device_reader, device_writer, igmp_server, current_device, ip_route, ip_proxy_map,cipher).await {
log::warn!("tun:{:?}",e);
if let Err(e) = start_(sender, device_reader, &device_writer, igmp_server, current_device, ip_route, ip_proxy_map, cipher).await {
log::warn!("stop:{}",e);
}
let _ = device_writer.close();
worker.stop_all();
})
}).unwrap();
}
async fn start_(sender: ChannelSender,
device_reader: DeviceReader,
device_writer: DeviceWriter,
igmp_server: IgmpServer,
device_writer: &DeviceWriter,
igmp_server: Option<IgmpServer>,
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
ip_route: ExternalRoute,
ip_proxy_map: IpProxyMap,
ip_route: Option<ExternalRoute>,
ip_proxy_map: Option<IpProxyMap>,
cipher: Option<Aes256Gcm>) -> io::Result<()> {
let mut buf = [0; 4096];
loop {
if sender.is_close() {
return Ok(());
}
let len = device_reader.read(&mut buf[12..])? + 12;
match handle(&sender, &mut buf, len, &device_writer, &igmp_server, current_device.load(), &ip_route, &ip_proxy_map,&cipher).await {
#[cfg(any(target_os = "macos"))]
let mut buf = &mut buf[4..];
match handle(&sender, &mut buf, len, device_writer, &igmp_server, current_device.load(), &ip_route, &ip_proxy_map, &cipher).await {
Ok(_) => {}
Err(e) => {
log::warn!("{:?}", e)
+40 -52
View File
@@ -1,8 +1,8 @@
use std::collections::{HashMap, HashSet};
use std::net::Ipv4Addr;
use std::sync::Arc;
use std::time::Duration;
use moka::sync::Cache;
use std::time::{Duration, Instant};
use crossbeam_skiplist::SkipMap;
use parking_lot::RwLock;
use packet::igmp::igmp_v2::IgmpV2Packet;
use packet::igmp::igmp_v3::{IgmpV3QueryPacket, IgmpV3RecordType, IgmpV3ReportPacket};
@@ -15,7 +15,7 @@ use crate::tun_tap_device::DeviceWriter;
#[derive(Clone, Debug)]
pub struct Multicast {
//成员虚拟ip
members: HashSet<Ipv4Addr>,
members: HashMap<Ipv4Addr, Instant>,
//是否是过滤模式
//成员过滤或包含的源ip
map: HashMap<Ipv4Addr, (bool, HashSet<Ipv4Addr>)>,
@@ -29,7 +29,7 @@ impl Multicast {
}
}
pub fn is_send(&self, ip: &Ipv4Addr) -> bool {
if self.members.contains(ip) {
if self.members.contains_key(ip) {
if let Some((is_include, set)) = self.map.get(ip) {
if *is_include {
set.contains(ip)
@@ -47,30 +47,13 @@ impl Multicast {
#[derive(Clone)]
pub struct IgmpServer {
multicast: Cache<Ipv4Addr, Arc<RwLock<Multicast>>>,
members: Cache<(Ipv4Addr, Ipv4Addr), ()>,
multicast: Arc<SkipMap<Ipv4Addr, Arc<RwLock<Multicast>>>>,
}
impl IgmpServer {
pub fn new(device_writer: DeviceWriter) -> Self {
let multicast: Cache<Ipv4Addr, Arc<RwLock<Multicast>>> = Cache::builder()
.time_to_idle(Duration::from_secs(30 * 60)).build();
let m = multicast.clone();
let members: Cache<(Ipv4Addr, Ipv4Addr), ()> = Cache::builder()
.time_to_idle(Duration::from_secs(20 * 60)).eviction_listener(move |k: Arc<(Ipv4Addr, Ipv4Addr)>, _, cause| {
if cause == moka::notification::RemovalCause::Replaced {
return;
}
log::info!("MULTICAST_MEMBER eviction {:?}", k);
if let Some(v) = m.get(&k.0) {
let mut lock = v.write();
lock.members.remove(&k.1);
lock.map.remove(&k.1);
}
}).build();
let multicast: Arc<SkipMap<Ipv4Addr, Arc<RwLock<Multicast>>>> = Arc::new(SkipMap::new());
std::thread::spawn(move || {
//定时发送query,启动时20秒一次,连发3次,之后125秒一次
let mut count = 0;
//预留以太网帧头和ip头
let mut buf = [0; 14 + 24 + 12];
let dest = Ipv4Addr::new(224, 0, 0, 1);
@@ -96,31 +79,42 @@ impl IgmpServer {
{
let mut igmp_query = IgmpV3QueryPacket::unchecked(&mut buf[14 + 24..]);
igmp_query.set_igmp_type();
igmp_query.set_max_resp_code(100);
igmp_query.set_max_resp_code(50);
igmp_query.set_group_address(Ipv4Addr::UNSPECIFIED);
igmp_query.set_qrv(2);
igmp_query.set_qqic(125);
igmp_query.set_qqic(10);
igmp_query.update_checksum();
}
loop {
let _ = device_writer.write_ipv4(&mut buf);
if count < 3 {
count += 1;
std::thread::sleep(Duration::from_secs(20))
} else {
std::thread::sleep(Duration::from_secs(125))
}
std::thread::sleep(Duration::from_secs(20))
}
});
Self {
multicast,
members,
}
}
pub fn load(&self, multicast_addr: &Ipv4Addr) -> Option<Arc<RwLock<Multicast>>> {
self.multicast.get(multicast_addr)
if let Some(entry) = self.multicast.get(multicast_addr) {
Some(entry.value().clone())
} else {
None
}
}
pub fn handle(&self, buf: &[u8], source: Ipv4Addr) -> crate::Result<()> {
for x in self.multicast.iter() {
let mut list = Vec::new();
let mut write_guard = x.value().write();
for (ip, time) in &write_guard.members {
if time.elapsed() > Duration::from_secs(30) {
list.push(*ip);
}
}
for ip in list {
write_guard.members.remove(&ip);
write_guard.map.remove(&ip);
}
}
match IgmpType::from(buf[0]) {
IgmpType::Query => {}
IgmpType::ReportV1 | IgmpType::ReportV2 => {
@@ -130,13 +124,11 @@ impl IgmpServer {
if !multicast_addr.is_multicast() {
return Ok(());
}
let multi = self.multicast.get_with(multicast_addr, || {
let multi = self.multicast.get_or_insert_with(multicast_addr, || {
Arc::new(RwLock::new(Multicast::new()))
});
let mut guard = multi.write();
guard.members.insert(source);
drop(guard);
self.members.insert((multicast_addr, source), ());
let mut guard = multi.value().write();
guard.members.insert(source, Instant::now());
}
IgmpType::LeaveV2 => {
//退出组播
@@ -145,7 +137,11 @@ impl IgmpServer {
if !multicast_addr.is_multicast() {
return Ok(());
}
self.members.invalidate(&(multicast_addr, source));
if let Some(entry) = self.multicast.get(&multicast_addr) {
let mut guard = entry.value().write();
guard.map.remove(&source);
guard.members.remove(&source);
}
}
IgmpType::ReportV3 => {
let report = IgmpV3ReportPacket::new(buf)?;
@@ -155,10 +151,10 @@ impl IgmpServer {
if !multicast_addr.is_multicast() {
return Ok(());
}
let multi = self.multicast.get_with(multicast_addr, || {
let multi = self.multicast.get_or_insert_with(multicast_addr, || {
Arc::new(RwLock::new(Multicast::new()))
});
let mut guard = multi.write();
let mut guard = multi.value().write();
match group_record.record_type() {
IgmpV3RecordType::ModeIsInclude | IgmpV3RecordType::ChangeToIncludeMode => {
@@ -169,10 +165,8 @@ impl IgmpServer {
guard.map.remove(&source);
}
Some(src) => {
guard.members.insert(source);
guard.members.insert(source, Instant::now());
guard.map.insert(source, (true, HashSet::from_iter(src)));
drop(guard);
self.members.insert((multicast_addr, source), ());
}
}
}
@@ -181,16 +175,14 @@ impl IgmpServer {
match group_record.source_addresses() {
None => {
//接收所有
guard.members.insert(source);
guard.members.insert(source, Instant::now());
guard.map.remove(&source);
}
Some(src) => {
guard.members.insert(source);
guard.members.insert(source, Instant::now());
guard.map.insert(source, (false, HashSet::from_iter(src)));
}
}
drop(guard);
self.members.insert((multicast_addr, source), ());
}
IgmpV3RecordType::AllowNewSources => {
//在已有源的基础上,接收目标源,如果是排除模式,则删除;是包含模式则添加
@@ -211,8 +203,6 @@ impl IgmpServer {
}
}
}
drop(guard);
self.members.insert((multicast_addr, source), ());
}
IgmpV3RecordType::BlockOldSources => {
//在已有源的基础上,不接收目标源
@@ -233,8 +223,6 @@ impl IgmpServer {
}
}
}
drop(guard);
self.members.insert((multicast_addr, source), ());
}
IgmpV3RecordType::Unknown(_) => {}
}
+2 -2
View File
@@ -2,7 +2,7 @@ use std::io;
use std::mem::MaybeUninit;
use std::net::{IpAddr, Ipv4Addr, SocketAddrV4};
use std::sync::Arc;
use crossbeam::atomic::AtomicCell;
use crossbeam_utils::atomic::AtomicCell;
use crossbeam_skiplist::SkipMap;
use socket2::{Domain, SockAddr, Socket, Type};
@@ -68,7 +68,7 @@ impl IcmpProxy {
net_packet.set_version(Version::V1);
net_packet.set_protocol(Protocol::IpTurn);
net_packet.set_transport_protocol(ipv4::protocol::Protocol::Icmp.into());
net_packet.set_ttl(MAX_TTL);
net_packet.first_set_ttl(MAX_TTL);
loop {
match self.recv(data) {
Ok((len, peer_ip)) => {
+10 -23
View File
@@ -2,7 +2,7 @@ use std::{io, thread};
use std::collections::HashMap;
use std::net::{Ipv4Addr, SocketAddrV4};
use std::sync::Arc;
use crossbeam::atomic::AtomicCell;
use crossbeam_utils::atomic::AtomicCell;
use crossbeam_skiplist::SkipMap;
use socket2::{SockAddr, Socket};
use tokio::net::{TcpListener, UdpSocket};
@@ -45,30 +45,17 @@ impl IpProxyMap {
}
}
pub async fn init_proxy(sender: ChannelSender, bind_ips: Vec<Ipv4Addr>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) -> io::Result<IpProxyMap> {
pub async fn init_proxy(sender: ChannelSender, bind_ips: Vec<Ipv4Addr>, current_device: Arc<AtomicCell<CurrentDeviceInfo>>) -> io::Result<(TcpProxy, UdpProxy, IpProxyMap)> {
let mut icmp_sockets = HashMap::new();
let tcp_proxy_map: Arc<SkipMap<SocketAddrV4, (SocketAddrV4, SocketAddrV4)>> = Arc::new(SkipMap::new());
let udp_proxy_map: Arc<SkipMap<SocketAddrV4, (SocketAddrV4, SocketAddrV4)>> = Arc::new(SkipMap::new());
let icmp_proxy_map: Arc<SkipMap<(Ipv4Addr, u16, u16), Ipv4Addr>> = Arc::new(SkipMap::new());
let (tcp_proxy_port, udp_proxy_port) = if !bind_ips.is_empty() {
let tcp_listener = TcpListener::bind("0.0.0.0:0").await?;
let udp_socket = UdpSocket::bind("0.0.0.0:0").await?;
let tcp_proxy_port = tcp_listener.local_addr()?.port();
let udp_proxy_port = udp_socket.local_addr()?.port();
let tcp_proxy_map = tcp_proxy_map.clone();
tokio::spawn(async {
let tcp_proxy = TcpProxy::new(tcp_listener, tcp_proxy_map);
tcp_proxy.start().await
});
let udp_proxy_map = udp_proxy_map.clone();
tokio::spawn(async {
let udp_proxy = UdpProxy::new(udp_socket, udp_proxy_map);
udp_proxy.start().await
});
(tcp_proxy_port, udp_proxy_port)
} else {
(0, 0)
};
let tcp_listener = TcpListener::bind("0.0.0.0:0").await?;
let udp_socket = UdpSocket::bind("0.0.0.0:0").await?;
let tcp_proxy_port = tcp_listener.local_addr()?.port();
let udp_proxy_port = udp_socket.local_addr()?.port();
let tcp_proxy = TcpProxy::new(tcp_listener, tcp_proxy_map.clone());
let udp_proxy = UdpProxy::new(udp_socket, udp_proxy_map.clone());
for ip in bind_ips {
let addr = SocketAddrV4::new(ip, 0);
let icmp_proxy_map = icmp_proxy_map.clone();
@@ -79,12 +66,12 @@ pub async fn init_proxy(sender: ChannelSender, bind_ips: Vec<Ipv4Addr>, current_
});
}
Ok(IpProxyMap {
Ok((tcp_proxy, udp_proxy, IpProxyMap {
tcp_proxy_port,
udp_proxy_port,
tcp_proxy_map,
udp_proxy_map,
icmp_proxy_map,
icmp_sockets,
})
}))
}
+2 -1
View File
@@ -6,7 +6,6 @@ use tokio::net::{TcpListener, TcpStream};
pub struct TcpProxy {
tcp_listener: TcpListener,
// todo 怎么过期
map: Arc<SkipMap<SocketAddrV4, (SocketAddrV4, SocketAddrV4)>>,
}
@@ -34,6 +33,7 @@ impl TcpProxy {
continue;
}
};
let map = map.clone();
tokio::spawn(async move {
match proxy(tcp_stream, peer_tcp_stream).await {
Ok(_) => {}
@@ -41,6 +41,7 @@ impl TcpProxy {
log::warn!("tcp代理异常:{:?},来源:{},目标:{}",e,src_addr,dest_addr);
}
}
map.remove(&sender_addr);
});
}
}
+2 -1
View File
@@ -8,7 +8,6 @@ use tokio::net::UdpSocket;
/// 一个udp代理,作用是利用系统协议栈,将udp数据报解析出来再转发到目的地址
pub struct UdpProxy {
udp_socket: Arc<UdpSocket>,
// todo 过期处理
map: Arc<SkipMap<SocketAddrV4, (SocketAddrV4, SocketAddrV4)>>,
}
@@ -61,6 +60,7 @@ async fn start0(buf: &[u8], sender_addr: SocketAddrV4, inner_map: &Arc<SkipMap<S
let inner_map = inner_map.clone();
inner_map.insert(sender_addr, peer_udp_socket.clone());
let udp_socket = udp_socket.clone();
let map = map.clone();
tokio::spawn(async move {
let mut buf = [0u8; 65536];
loop {
@@ -90,6 +90,7 @@ async fn start0(buf: &[u8], sender_addr: SocketAddrV4, inner_map: &Arc<SkipMap<S
}
}
inner_map.remove(&sender_addr);
map.remove(&sender_addr);
});
}
Ok(())
+1
View File
@@ -13,3 +13,4 @@ pub mod igmp_server;
pub mod tun_tap_device;
pub mod core;
pub mod channel;
pub mod util;
-15
View File
@@ -4,20 +4,6 @@ use std::time::Duration;
use std::{io, thread};
use crate::channel::punch::NatType;
// #[derive(Debug, Copy, Clone, PartialEq)]
// pub enum NatType {
// Symmetric,
// Cone,
// }
//
// impl Into<u8> for NatType {
// fn into(self) -> u8 {
// match self {
// NatType::Symmetric => 0,
// NatType::Cone => 1,
// }
// }
// }
/// 返回所有公网ip和端口变化范围
pub fn public_ip_list(addrs: &Vec<SocketAddr>) -> io::Result<(NatType, Vec<Ipv4Addr>, u16)> {
@@ -72,7 +58,6 @@ pub fn public_ip_list_(
udp: &UdpSocket,
addrs: &Vec<SocketAddr>,
) -> io::Result<(HashSet<Ipv4Addr>, u16, u16)> {
// println!("local port {:?}", udp.local_addr().unwrap().port());
udp.set_read_timeout(Some(Duration::from_millis(300)))?;
let mut buf = [0u8; 128];
for addr in addrs {
+48
View File
@@ -0,0 +1,48 @@
use std::io;
use std::os::unix::io::RawFd;
#[derive(Clone)]
pub struct DeviceWriter(RawFd);
pub struct DeviceReader(RawFd);
impl DeviceWriter {
pub fn write_ipv4_tun(&self, buf: &[u8]) -> io::Result<()> {
unsafe {
let amount = libc::write(self.0, buf.as_ptr() as *const _, buf.len());
if amount < 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
}
///写入ipv4数据,为了兼容其他代码,头部空了14个字节
pub fn write_ipv4(&self, buf: &[u8]) -> io::Result<()> {
let buf = &buf[14..];
self.write_ipv4_tun(buf)
}
pub fn close(&self) -> io::Result<()> {
// unsafe {
// libc::close(self.0);
// }
Ok(())
}
}
impl DeviceReader {
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
unsafe {
let amount = libc::read(self.0, buf.as_mut_ptr() as *mut _, buf.len());
if amount < 0 {
return Err(io::Error::last_os_error());
}
Ok(amount as usize)
}
}
}
pub fn create(fd: i32) -> (DeviceWriter, DeviceReader) {
(DeviceWriter(fd as _), DeviceReader(fd as _))
}
+12 -8
View File
@@ -1,11 +1,11 @@
use std::io;
use std::net::Ipv4Addr;
use crate::tun_tap_device::{DeviceReader, DeviceType, DeviceWriter};
use crate::tun_tap_device::{DeviceReader, DeviceType, DeviceWriter, DriverInfo};
use tun::Device;
use parking_lot::Mutex;
use std::process::Command;
use std::sync::Arc;
use crate::tun_tap_device::unix::DeviceW;
use crate::tun_tap_device::linux_mac::DeviceW;
impl DeviceWriter {
pub fn change_ip(&self, address: Ipv4Addr, netmask: Ipv4Addr,
@@ -15,7 +15,6 @@ impl DeviceWriter {
.destination(gateway)
.address(address)
.netmask(netmask)
.mtu(1420)
// .queues(2)
.up();
let mut dev = self.lock.lock();
@@ -56,15 +55,15 @@ pub fn create_device(device_type: DeviceType,
netmask: Ipv4Addr,
gateway: Ipv4Addr,
in_ips: Vec<(Ipv4Addr, Ipv4Addr)>,
) -> io::Result<(DeviceWriter, DeviceReader)> {
println!("========网卡配置========");
mtu: u16,
) -> io::Result<(DeviceWriter, DeviceReader,DriverInfo)> {
let mut config = tun::Configuration::default();
config
.destination(gateway)
.address(address)
.netmask(netmask)
.mtu(1420)
.mtu(mtu.into())
// .queues(2) 用多个队列有兼容性问题
.up();
match device_type {
@@ -79,7 +78,6 @@ pub fn create_device(device_type: DeviceType,
let reader = queue.reader();
let writer = queue.writer();
let name = dev.name();
println!("name:{:?}", name);
for (address, netmask) in &in_ips {
add_route(name, *address, *netmask)?;
}
@@ -111,10 +109,16 @@ pub fn create_device(device_type: DeviceType,
DeviceW::Tap((writer, mac))
}
};
println!("========TUN网卡配置========");
let driver_info = DriverInfo {
device_type,
name:name.to_string(),
version:String::new(),
mac: None,
};
Ok((
DeviceWriter::new(device_w, Arc::new(Mutex::new(dev)), in_ips, address, packet_information),
DeviceReader::new(reader),
driver_info,
))
}
@@ -5,10 +5,9 @@ use bytes::BufMut;
use tun::platform::posix::{Reader, Writer};
use std::net::Ipv4Addr;
use std::os::unix::io::AsRawFd;
use crossbeam::atomic::AtomicCell;
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(any(target_os = "linux"))]
use tun::platform::linux::Device;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[cfg(any(target_os = "macos"))]
use tun::platform::macos::Device;
use parking_lot::Mutex;
use packet::ethernet;
@@ -38,17 +37,15 @@ pub struct DeviceWriter {
writer: DeviceW,
pub lock: Arc<Mutex<Device>>,
pub in_ips: Vec<(Ipv4Addr, Ipv4Addr)>,
ip: Arc<AtomicCell<Ipv4Addr>>,
packet_information: bool,
}
impl DeviceWriter {
pub fn new(writer: DeviceW,lock: Arc<Mutex<Device>>, in_ips: Vec<(Ipv4Addr, Ipv4Addr)>, ip: Ipv4Addr, packet_information: bool) -> Self {
pub fn new(writer: DeviceW,lock: Arc<Mutex<Device>>, in_ips: Vec<(Ipv4Addr, Ipv4Addr)>, _ip: Ipv4Addr, packet_information: bool) -> Self {
Self {
writer,
lock,
in_ips,
ip: Arc::new(AtomicCell::new(ip)),
packet_information,
}
}
@@ -107,9 +104,6 @@ impl DeviceWriter {
}
}
}
pub fn ip(&self) -> Ipv4Addr {
self.ip.load()
}
pub fn close(&self) -> io::Result<()> {
unsafe {
match &self.writer {
@@ -128,7 +122,6 @@ impl DeviceWriter {
}
}
#[derive(Clone)]
pub struct DeviceReader(Reader);
impl DeviceReader {
+13 -9
View File
@@ -1,11 +1,11 @@
use std::io;
use std::net::Ipv4Addr;
use crate::tun_tap_device::{DeviceReader, DeviceType, DeviceWriter};
use crate::tun_tap_device::{DeviceReader, DeviceType, DeviceWriter, DriverInfo};
use tun::Device;
use parking_lot::Mutex;
use std::process::Command;
use std::sync::Arc;
use crate::tun_tap_device::unix::DeviceW;
use crate::tun_tap_device::linux_mac::DeviceW;
impl DeviceWriter {
pub fn change_ip(&self, address: Ipv4Addr, netmask: Ipv4Addr,
@@ -15,7 +15,6 @@ impl DeviceWriter {
.destination(gateway)
.address(address)
.netmask(netmask)
.mtu(1420)
.up();
let mut dev = self.lock.lock();
if let Err(e) = dev.configure(&config) {
@@ -42,21 +41,21 @@ pub fn create_device(device_type: DeviceType,
netmask: Ipv4Addr,
gateway: Ipv4Addr,
in_ips: Vec<(Ipv4Addr, Ipv4Addr)>,
) -> io::Result<(DeviceWriter, DeviceReader)> {
mtu: u16,
) -> io::Result<(DeviceWriter, DeviceReader, DriverInfo)> {
match device_type {
DeviceType::Tun => {}
DeviceType::Tap => {
unimplemented!()
}
}
println!("========TUN网卡配置========");
let mut config = tun::Configuration::default();
config
.destination(gateway)
.address(address)
.netmask(netmask)
.mtu(1420)
.mtu(mtu.into())
.up();
let dev = tun::create(&config).unwrap();
@@ -74,17 +73,22 @@ pub fn create_device(device_type: DeviceType,
let queue = dev.queue(0).unwrap();
let reader = queue.reader();
let writer = queue.writer();
println!("name:{:?}", name);
println!("========TUN网卡配置========");
let driver_info = DriverInfo {
device_type,
name: name.to_string(),
version: String::new(),
mac: None,
};
Ok((
DeviceWriter::new(DeviceW::Tun(writer), Arc::new(Mutex::new(dev)), in_ips, address, packet_information),
DeviceReader::new(reader),
driver_info
))
}
fn add_route(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> {
let route_add_str: String = format!(
"sudo route -n add -net {:?}/{:?} -interface {}",
"route -n add {} -netmask {} -interface {}",
address, netmask, name
);
let route_add_out = Command::new("sh")
+32 -11
View File
@@ -1,19 +1,25 @@
#[cfg(target_os = "windows")]
pub mod windows;
#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod linux;
mod windows;
#[cfg(any(target_os = "linux"))]
mod linux;
#[cfg(target_os = "macos")]
pub mod mac;
#[cfg(any(unix))]
pub mod unix;
mod mac;
#[cfg(any(target_os = "linux", target_os = "macos"))]
mod linux_mac;
#[cfg(target_os = "android")]
mod android;
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(any(target_os = "linux"))]
pub use linux::create_device;
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(any(target_os = "linux"))]
pub use linux::delete_device;
#[cfg(any(unix))]
pub use unix::{DeviceWriter, DeviceReader};
#[cfg(target_os = "android")]
pub use android::create;
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub use linux_mac::{DeviceWriter, DeviceReader};
#[cfg(target_os = "android")]
pub use android::{DeviceWriter, DeviceReader};
#[cfg(target_os = "macos")]
pub use mac::create_device;
#[cfg(target_os = "macos")]
@@ -26,7 +32,22 @@ pub use windows::delete_device;
#[cfg(target_os = "windows")]
pub use windows::{DeviceWriter, DeviceReader};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum DeviceType {
Tun,
Tap,
}
impl DeviceType {
pub fn is_tun(&self) -> bool {
*self == DeviceType::Tun
}
}
#[derive(Clone)]
pub struct DriverInfo {
pub device_type: DeviceType,
pub name: String,
pub version: String,
pub mac: Option<String>,
}
+37 -30
View File
@@ -1,14 +1,14 @@
use std::{io, thread};
use std::net::Ipv4Addr;
use std::os::windows::process::CommandExt;
use std::sync::Arc;
use std::time::Duration;
use crossbeam::atomic::AtomicCell;
use libloading::Library;
use parking_lot::Mutex;
use packet::ethernet;
use packet::ethernet::packet::EthernetPacket;
use win_tun_tap::{IFace, TapDevice, TunDevice};
use crate::tun_tap_device::DeviceType;
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";
@@ -37,16 +37,14 @@ pub struct DeviceWriter {
device: Arc<Device>,
lock: Arc<Mutex<()>>,
in_ips: Vec<(Ipv4Addr, Ipv4Addr)>,
ip: Arc<AtomicCell<Ipv4Addr>>,
}
impl DeviceWriter {
pub fn new(device: Arc<Device>, in_ips: Vec<(Ipv4Addr, Ipv4Addr)>, ip: Ipv4Addr) -> Self {
pub fn new(device: Arc<Device>, in_ips: Vec<(Ipv4Addr, Ipv4Addr)>, _ip: Ipv4Addr) -> Self {
Self {
device,
lock: Arc::new(Default::default()),
in_ips,
ip: Arc::new(AtomicCell::new(ip)),
}
}
}
@@ -120,7 +118,6 @@ impl DeviceWriter {
log::warn!("{:?}", e);
}
dev.set_ip(address, netmask)?;
self.ip.store(address);
for (address, netmask) in &self.in_ips {
dev.add_route(*address, *netmask, gateway, 1)?;
}
@@ -132,9 +129,6 @@ impl DeviceWriter {
delete_cache();
Ok(())
}
pub fn ip(&self) -> Ipv4Addr {
self.ip.load()
}
pub fn close(&self) -> io::Result<()> {
match self.device.as_ref() {
Device::Tun(dev) => {
@@ -161,7 +155,6 @@ fn dest(ip: Ipv4Addr, mask: Ipv4Addr) -> Ipv4Addr {
])
}
#[derive(Clone)]
pub struct DeviceReader {
device: Arc<Device>,
}
@@ -199,9 +192,9 @@ fn create_tun(
netmask: Ipv4Addr,
gateway: Ipv4Addr,
in_ips: Vec<(Ipv4Addr, Ipv4Addr)>,
) -> io::Result<(DeviceWriter, DeviceReader)> {
mtu: u16,
) -> io::Result<(DeviceWriter, DeviceReader, DriverInfo)> {
unsafe {
println!("========TUN网卡配置========");
match Library::new("wintun.dll") {
Ok(lib) => match TunDevice::delete_for_name(lib, TUN_INTERFACE_NAME) {
Ok(_) => {
@@ -210,7 +203,6 @@ fn create_tun(
Err(_) => {}
},
Err(e) => {
log::error!("wintun.dll not found");
return Err(io::Error::new(
io::ErrorKind::Other,
format!("wintun.dll not found {:?}", e),
@@ -240,11 +232,11 @@ fn create_tun(
}
}
};
println!("name:{:?}", tun_device.get_name()?);
println!("version:{:?}", tun_device.version()?);
let name = tun_device.get_name()?;
let version = format!("{:?}", tun_device.version()?);
tun_device.set_ip(address, netmask)?;
tun_device.set_metric(1)?;
tun_device.set_mtu(1420)?;
tun_device.set_mtu(mtu)?;
// ip代理路由
for (address, netmask) in &in_ips {
tun_device.add_route(*address, *netmask, gateway, 1)?;
@@ -256,22 +248,30 @@ fn create_tun(
tun_device.add_route(Ipv4Addr::from([224, 0, 0, 0]), Ipv4Addr::from([240, 0, 0, 0]), gateway, 1)?;
delete_cache();
let device = Arc::new(Device::Tun(tun_device));
println!("========TUN网卡配置========");
let driver_info = DriverInfo {
device_type: DeviceType::Tun,
name,
version,
mac: None,
};
Ok((
DeviceWriter::new(device.clone(), in_ips, address),
DeviceReader::new(device),
driver_info
))
}
}
fn delete_cache(){
fn delete_cache() {
//清除路由缓存
let delete_cache = "netsh interface ip delete destinationcache";
let out = std::process::Command::new("cmd")
.creation_flags(0x08000000)
.arg("/C")
.arg(delete_cache)
.output()
.unwrap();
if !out.status.success(){
if !out.status.success() {
log::warn!("删除缓存失败:{:?}",out);
}
}
@@ -293,8 +293,8 @@ fn create_tap(
netmask: Ipv4Addr,
gateway: Ipv4Addr,
in_ips: Vec<(Ipv4Addr, Ipv4Addr)>,
) -> io::Result<(DeviceWriter, DeviceReader)> {
println!("========TAP网卡配置========");
mtu: u16,
) -> io::Result<(DeviceWriter, DeviceReader, DriverInfo)> {
let tap_device = match TapDevice::open(TAP_INTERFACE_NAME) {
Ok(tap_device) => tap_device,
Err(e) => {
@@ -305,12 +305,12 @@ fn create_tap(
}
};
let mac = tap_device.get_mac()?;
println!("name:{:?}", tap_device.get_name()?);
println!("version:{:x?}", tap_device.get_version()?);
println!("mac:{:x?}", mac);
let name = tap_device.get_name()?;
let version = format!("{:?}", tap_device.get_version()?);
let mac_str = format!("mac:{:x?}", mac);
tap_device.set_ip(address, netmask)?;
tap_device.set_metric(1)?;
tap_device.set_mtu(1420)?;
tap_device.set_mtu(mtu)?;
tap_device.set_status(true)?;
tap_device.add_route(address, netmask, gateway, 1)?;
for (address, netmask) in &in_ips {
@@ -321,10 +321,16 @@ fn create_tap(
tap_device.add_route(Ipv4Addr::from([224, 0, 0, 0]), Ipv4Addr::from([240, 0, 0, 0]), gateway, 1)?;
delete_cache();
let tap = Arc::new(Device::Tap((tap_device, mac)));
println!("========TAP网卡配置========");
let driver_info = DriverInfo {
device_type: DeviceType::Tap,
name,
version,
mac: Some(mac_str),
};
Ok((
DeviceWriter::new(tap.clone(), in_ips, address),
DeviceReader::new(tap)
DeviceReader::new(tap),
driver_info
))
}
@@ -341,13 +347,14 @@ fn delete_tap() {
pub fn create_device(device_type: DeviceType, address: Ipv4Addr,
netmask: Ipv4Addr,
gateway: Ipv4Addr,
in_ips: Vec<(Ipv4Addr, Ipv4Addr)>, ) -> io::Result<(DeviceWriter, DeviceReader)> {
in_ips: Vec<(Ipv4Addr, Ipv4Addr)>,
mtu: u16) -> io::Result<(DeviceWriter, DeviceReader, DriverInfo)> {
match device_type {
DeviceType::Tun => {
create_tun(address, netmask, gateway, in_ips)
create_tun(address, netmask, gateway, in_ips, mtu)
}
DeviceType::Tap => {
create_tap(address, netmask, gateway, in_ips)
create_tap(address, netmask, gateway, in_ips, mtu)
}
}
}
+1
View File
@@ -0,0 +1 @@
pub mod wait;
+44
View File
@@ -0,0 +1,44 @@
use std::sync::Arc;
use std::sync::atomic::{AtomicIsize, Ordering};
use tokio::sync::watch::{channel, Receiver, Sender};
#[derive(Clone)]
pub struct WaitGroup {
count: Arc<AtomicIsize>,
receiver: Receiver<usize>,
sender: Arc<Sender<usize>>,
}
impl WaitGroup {
pub fn new() -> Self {
let (sender, receiver) = channel(1);
Self {
count: Arc::new(Default::default()),
receiver,
sender: Arc::new(sender),
}
}
pub fn add(&self) {
let _ = self.count.fetch_add(1, Ordering::Relaxed);
}
pub fn done(&self) {
let i = self.count.fetch_sub(1, Ordering::Relaxed);
if i == 1 {
let _ = self.sender.send(0);
}
}
pub async fn wait(&mut self) {
loop {
if 0 == *self.receiver.borrow() {
return;
}
if self.receiver.changed().await.is_ok() {
if 0 == *self.receiver.borrow() {
return;
}
} else {
return;
}
}
}
}
+5
View File
@@ -1,10 +1,12 @@
use std::io;
use std::net::Ipv4Addr;
use std::os::windows::process::CommandExt;
/// 设置网卡名称
pub fn set_interface_name(old_name: &str, new_name: &str) -> io::Result<()> {
let cmd = format!(" netsh interface set interface name={:?} newname={:?}", old_name, new_name);
let out = std::process::Command::new("cmd")
.creation_flags(0x08000000) //winapi-0.3.9/src/um/winbase.rs:283
.arg("/C")
.arg(&cmd)
.output()?;
@@ -21,6 +23,7 @@ pub fn set_interface_ip(index: u32, address: &Ipv4Addr, netmask: &Ipv4Addr) -> i
index, address, netmask,
);
let out = std::process::Command::new("cmd")
.creation_flags(0x08000000)
.arg("/C")
.arg(&set_address)
.output()?;
@@ -37,6 +40,7 @@ pub fn set_interface_mtu(index: u32, mtu: u16) -> io::Result<()> {
index, mtu
);
let out = std::process::Command::new("cmd")
.creation_flags(0x08000000)
.arg("/C")
.arg(&set_mtu)
.output()?;
@@ -49,6 +53,7 @@ pub fn set_interface_mtu(index: u32, mtu: u16) -> io::Result<()> {
pub fn set_interface_metric(index: u32, metric: u16) -> io::Result<()> {
let set_metric = format!("netsh interface ip set interface {} metric={}", index,metric);
let out = std::process::Command::new("cmd")
.creation_flags(0x08000000)
.arg("/C")
.arg(&set_metric)
.output()?;
+3
View File
@@ -1,5 +1,6 @@
use std::io;
use std::net::Ipv4Addr;
use std::os::windows::process::CommandExt;
/// 添加路由
pub fn add_route(index: u32, dest: Ipv4Addr,
@@ -11,6 +12,7 @@ pub fn add_route(index: u32, dest: Ipv4Addr,
);
// 执行添加路由命令
let out = std::process::Command::new("cmd")
.creation_flags(0x08000000)
.arg("/C")
.arg(&set_route)
.output()
@@ -33,6 +35,7 @@ pub fn delete_route(index: u32, dest: Ipv4Addr, netmask: Ipv4Addr, gateway: Ipv4
);
// 删除路由
let out = std::process::Command::new("cmd")
.creation_flags(0x08000000)
.arg("/C")
.arg(delete_route)
.output()