Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0b0c61a03 | ||
|
|
b0501db4e5 | ||
|
|
465a5c75ae | ||
|
|
978460aca8 | ||
|
|
4b86752c79 | ||
|
|
bd61cad7b6 | ||
|
|
40cbd2e26b | ||
|
|
2cab580b4e | ||
|
|
3d243fb01d | ||
|
|
ca4e8d14f0 | ||
|
|
9c098c55c9 |
@@ -3,7 +3,9 @@
|
||||
A virtual network tool (VPN)
|
||||
|
||||
将不同网络下的多个设备虚拟到一个局域网下
|
||||
|
||||
### vnt-cli参数详解 [参数说明](https://github.com/lbl8603/vnt/blob/main/vnt-cli/README.md)
|
||||
|
||||
### 快速使用:
|
||||
|
||||
1. 指定一个token,在多台设备上运行该程序,例如:
|
||||
@@ -61,8 +63,27 @@ A virtual network tool (VPN)
|
||||
|
||||
前提条件:安装rust编译环境([install rust](https://www.rust-lang.org/zh-CN/tools/install))
|
||||
|
||||
```
|
||||
到项目根目录下执行 cargo build -p vnt-cli
|
||||
|
||||
也可按需编译,将得到更小的二进制文件,使用--no-default-features排除默认features
|
||||
|
||||
cargo build -p vnt-cli --no-default-features
|
||||
```
|
||||
|
||||
features说明
|
||||
|
||||
| feature | 说明 | 是否默认 |
|
||||
|------------------|----------------------|------|
|
||||
| openssl | 使用openssl中的aes_ecb算法 | 否 |
|
||||
| openssl-vendored | 从源码编译openssl | 否 |
|
||||
| ring-cipher | 使用ring中的aes_gcm算法 | 否 |
|
||||
| aes_cbc | 支持aes_cbc加密 | 是 |
|
||||
| aes_ecb | 支持aes_ecb加密 | 是 |
|
||||
| aes_gcm | 支持aes_gcm加密 | 是 |
|
||||
| aes_ecb | 支持aes_ecb加密 | 是 |
|
||||
| server_encrypt | 支持服务端加密 | 是 |
|
||||
|
||||
### 支持平台
|
||||
|
||||
- Mac
|
||||
@@ -86,9 +107,11 @@ A virtual network tool (VPN)
|
||||
- p2p组播/广播
|
||||
- 客户端数据加密
|
||||
- 服务端数据加密
|
||||
|
||||
### 结构
|
||||
|
||||
<details> <summary>展开</summary>
|
||||
|
||||
|
||||
<pre>
|
||||
|
||||
0 15 31
|
||||
@@ -121,8 +144,9 @@ A virtual network tool (VPN)
|
||||
- 支持Ipv6(1.2.2已支持客户端之间的ipv6,待支持客户端和服务端之间的ipv6通信)
|
||||
|
||||
### 常见问题
|
||||
|
||||
<details> <summary>展开</summary>
|
||||
|
||||
|
||||
#### 问题1: 设置网络地址失败
|
||||
|
||||
##### 可能原因:
|
||||
@@ -138,26 +162,35 @@ vnt默认使用10.26.0.0/24网段,和本地网络适配器的ip冲突
|
||||
#### 问题2: windows系统上wintun.dll加载失败
|
||||
|
||||
##### 可能原因:
|
||||
|
||||
没有下载wintun.dll 或者使用的wintun.dll有问题
|
||||
|
||||
##### 解决方法:
|
||||
|
||||
1. 下载最新版的wintun.dll [下载链接](https://www.wintun.net/builds/wintun-0.14.1.zip)
|
||||
2. 解压后找到对应架构的目录,通常是amd64
|
||||
3. 将对应的wintun.dll放到和vnt-cli同目录下(或者放到C盘Windows目录下)
|
||||
4. 再次启动vnt-cli
|
||||
|
||||
|
||||
#### 问题3: 丢包严重,或是不能正常组网通信
|
||||
|
||||
##### 可能原因:
|
||||
|
||||
某些宽带下(比如广电宽带)UDP丢包严重
|
||||
|
||||
##### 解决方法:
|
||||
|
||||
1. 使用TCP模式中继转发(vnt-cli增加--tcp参数)
|
||||
2. 如果p2p后效果很差,可以选择禁用p2p(vnt-cli增加--relay参数)
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
### 交流群
|
||||
|
||||
QQ:1034868233
|
||||
|
||||
### 其他
|
||||
|
||||
可使用社区小伙伴搭建的中继服务器
|
||||
|
||||
1. -s vnt.8443.eu.org:29871
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "common"
|
||||
version = "1.2.4"
|
||||
version = "1.2.5"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
+10
-5
@@ -1,20 +1,21 @@
|
||||
[package]
|
||||
name = "vnt-cli"
|
||||
version = "1.2.4"
|
||||
version = "1.2.5"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
vnt = { path = "../vnt", package = "vnt", optional = true }
|
||||
vnt = { path = "../vnt", package = "vnt",default-features = false }
|
||||
common = { path = "../common" }
|
||||
tokio = { version = "1.32.0", features = ["full"] }
|
||||
getopts = "0.2.21"
|
||||
console = "0.15.2"
|
||||
os_info = "3.7.0"
|
||||
dirs = "4.0.0"
|
||||
dirs = "5.0.1"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0.94"
|
||||
serde_yaml = "0.9.25"
|
||||
log = "0.4.17"
|
||||
log4rs = "1.2.0"
|
||||
[dependencies.uuid]
|
||||
@@ -30,11 +31,15 @@ sudo = "0.6.0"
|
||||
winapi = { version = "0.3.9", features = ["handleapi", "processthreadsapi", "winnt", "securitybaseapi", "impl-default"] }
|
||||
|
||||
[features]
|
||||
default = ["vnt"]
|
||||
default = ["server_encrypt","aes_gcm","aes_cbc","aes_ecb","sm4_cbc"]
|
||||
openssl = ["vnt/openssl"]
|
||||
openssl-vendored = ["vnt/openssl-vendored"]
|
||||
ring-cipher = ["vnt/ring-cipher"]
|
||||
|
||||
aes_cbc=["vnt/aes_cbc"]
|
||||
aes_ecb=["vnt/aes_ecb"]
|
||||
sm4_cbc=["vnt/sm4_cbc"]
|
||||
aes_gcm=["vnt/aes_gcm"]
|
||||
server_encrypt=["vnt/server_encrypt"]
|
||||
|
||||
[build-dependencies]
|
||||
embed-manifest = "1.4.0"
|
||||
+47
-5
@@ -29,10 +29,10 @@
|
||||
|
||||
提升通信安全性,使用该密码生成的密钥对客户端数据进行加密,并且服务端无法解密(包括中继数据)。使用相同密码的客户端才能通信
|
||||
|
||||
| 密码位数 | 加密算法 |
|
||||
|---------|-------|
|
||||
| 小于8 | AES128-GCM
|
||||
| 大于等于8 | AES256-GCM |
|
||||
| 密码位数 | 加密算法 |
|
||||
|-------|------------|
|
||||
| 小于8 | AES128-GCM |
|
||||
| 大于等于8 | AES256-GCM |
|
||||
|
||||
### -W
|
||||
开启和服务端通信的数据加密,采用rsa+aes256gcm加密客户端和服务端之间通信的数据,可以避免token泄漏、中间人攻击
|
||||
@@ -59,7 +59,8 @@
|
||||
### --par `<parallel>`
|
||||
任务并行度(必须为正整数),默认值为1,该值表示处理网卡读写的任务数,组网设备数较多、处理延迟较大时可适当调大此值
|
||||
### --model `<model>`
|
||||
加密模式,可选值 aes_gcm/aes_cbc/aes_ecb,默认使用aes_gcm,通常情况aes_gcm安全性高、aes_ecb性能更好
|
||||
加密模式,可选值 aes_gcm/aes_cbc/aes_ecb/sm4_cbc,默认使用aes_gcm,通常情况aes_gcm安全性高、aes_ecb性能更好,但是在低性能设备上sm4_cbc也许速度会更快;
|
||||
|
||||
|
||||
| 密码位数 | model | 加密算法 |
|
||||
|-------|---------|------------|
|
||||
@@ -69,12 +70,53 @@
|
||||
| `>=`8 | aes_cbc | AES256-CBC |
|
||||
| 1~8位 | aes_ecb | AES128-ECB |
|
||||
| `>=`8 | aes_ecb | AES256-ECB |
|
||||
| `>0` | sm4_cbc | SM4-CBC |
|
||||
### --finger
|
||||
|
||||
开启数据指纹校验,可增加安全性,如果服务端开启指纹校验,则客户端也必须开启,开启会损耗一部分性能
|
||||
|
||||
注意:默认情况下服务端不会对中转的数据做校验,如果要对中转的数据做校验,则需要客户端、服务端都开启此参数
|
||||
### --punch `<punch>`
|
||||
取值ipv4/ipv6,选择只使用ipv4打洞或者只使用ipv6打洞,默认两则都会使用
|
||||
### --port `<port>`
|
||||
取值0~65535,指定本地监听的端口,默认取随机端口
|
||||
### -f `<conf>`
|
||||
指定配置文件
|
||||
配置文件采用yaml格式,可参考:
|
||||
```yaml
|
||||
# 全部参数
|
||||
tap: false #是否使用tap
|
||||
token: xxx #组网token
|
||||
device_id: xxx #当前设备id
|
||||
name: windows 11 #当前设备名称
|
||||
server_address: ip:port #注册和中继服务器
|
||||
stun_server: #stun服务器
|
||||
- stun1.l.google.com:19302
|
||||
- stun2.l.google.com:19302
|
||||
in_ips: #代理ip入站
|
||||
- 192.168.1.0/24,10.26.0.3
|
||||
out_ips: #代理ip出站
|
||||
- 0.0.0.0/0
|
||||
password: xxx #密码
|
||||
simulate_multicast: false #模拟组播
|
||||
mtu: 1420 #mtu
|
||||
tcp: false #tcp模式
|
||||
ip: 10.26.0.2 #指定虚拟ip
|
||||
relay: false #中继模式
|
||||
server_encrypt: true #服务端加密
|
||||
parallel: 1 #任务并行度
|
||||
cipher_model: aes_gcm #客户端加密算法
|
||||
finger: false #关闭数据指纹
|
||||
punch_model: ipv4 #打洞模式
|
||||
port: 0 #使用随机端口
|
||||
cmd: false #关闭控制台输入
|
||||
```
|
||||
|
||||
或者需要哪个配置就加哪个,当然token是必须的
|
||||
```yaml
|
||||
# 部分参数
|
||||
token: xxx #组网token
|
||||
```
|
||||
### --relay
|
||||
禁用p2p,在网络环境很差时,只使用服务器中转效果可能更好(可以配合--tcp参数一起使用)
|
||||
### --list
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
use std::io;
|
||||
use std::net::{Ipv4Addr, ToSocketAddrs};
|
||||
use std::str::FromStr;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use vnt::channel::punch::PunchModel;
|
||||
use vnt::cipher::CipherModel;
|
||||
use vnt::core::Config;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(default)]
|
||||
pub struct FileConfig {
|
||||
pub tap: bool,
|
||||
pub token: String,
|
||||
pub device_id: String,
|
||||
pub name: String,
|
||||
pub server_address: String,
|
||||
pub stun_server: Vec<String>,
|
||||
pub in_ips: Vec<String>,
|
||||
pub out_ips: Vec<String>,
|
||||
pub password: Option<String>,
|
||||
pub simulate_multicast: bool,
|
||||
pub mtu: Option<u16>,
|
||||
pub tcp: bool,
|
||||
pub ip: Option<String>,
|
||||
pub relay: bool,
|
||||
pub server_encrypt: bool,
|
||||
pub parallel: usize,
|
||||
pub cipher_model: String,
|
||||
pub finger: bool,
|
||||
pub punch_model: String,
|
||||
pub port: u16,
|
||||
pub cmd: bool,
|
||||
}
|
||||
|
||||
impl Default for FileConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
tap: false,
|
||||
token: "".to_string(),
|
||||
device_id: get_device_id(),
|
||||
name: os_info::get().to_string(),
|
||||
server_address: "nat1.wherewego.top:29872".to_string(),
|
||||
stun_server: vec![
|
||||
"stun1.l.google.com:19302".to_string(),
|
||||
"stun2.l.google.com:19302".to_string(),
|
||||
"stun.qq.com:3478".to_string(),
|
||||
],
|
||||
in_ips: vec![],
|
||||
out_ips: vec![],
|
||||
password: None,
|
||||
simulate_multicast: false,
|
||||
mtu: None,
|
||||
tcp: false,
|
||||
ip: None,
|
||||
relay: false,
|
||||
server_encrypt: false,
|
||||
parallel: 1,
|
||||
cipher_model: "aes_gcm".to_string(),
|
||||
finger: false,
|
||||
punch_model: "".to_string(),
|
||||
port: 0,
|
||||
cmd: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_config(file_path: &str) -> io::Result<(Config, bool)> {
|
||||
let conf = std::fs::read_to_string(file_path)?;
|
||||
let file_conf = match serde_yaml::from_str::<FileConfig>(&conf) {
|
||||
Ok(val) => val,
|
||||
Err(e) => {
|
||||
log::error!("{:?}", e);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, format!("{}", e)));
|
||||
}
|
||||
};
|
||||
if file_conf.token.is_empty() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "token is_empty"));
|
||||
}
|
||||
let server_address = match file_conf.server_address.to_socket_addrs() {
|
||||
Ok(mut addr) => {
|
||||
if let Some(addr) = addr.next() {
|
||||
addr
|
||||
} else {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("server_address {:?} error", &file_conf.server_address),
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("server_address {:?} error:{}", &file_conf.server_address, e),
|
||||
));
|
||||
}
|
||||
};
|
||||
let in_ips = match common::args_parse::ips_parse(&file_conf.in_ips) {
|
||||
Ok(in_ips) => in_ips,
|
||||
Err(e) => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("in_ips {:?} error:{}", &file_conf.in_ips, e),
|
||||
));
|
||||
}
|
||||
};
|
||||
let out_ips = match common::args_parse::out_ips_parse(&file_conf.out_ips) {
|
||||
Ok(out_ips) => out_ips,
|
||||
Err(e) => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("out_ips {:?} error:{}", &file_conf.out_ips, e),
|
||||
));
|
||||
}
|
||||
};
|
||||
let virtual_ip = match file_conf.ip.clone().map(|v| Ipv4Addr::from_str(&v)) {
|
||||
None => None,
|
||||
Some(r) => Some(r.map_err(|e| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("ip {:?} error:{}", &file_conf.ip, e),
|
||||
)
|
||||
})?),
|
||||
};
|
||||
|
||||
let cipher_model = CipherModel::from_str(&file_conf.cipher_model)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
|
||||
let punch_model = PunchModel::from_str(&file_conf.punch_model)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
let config = Config::new(
|
||||
file_conf.tap,
|
||||
file_conf.token,
|
||||
file_conf.device_id,
|
||||
file_conf.name,
|
||||
server_address,
|
||||
file_conf.server_address,
|
||||
file_conf.stun_server,
|
||||
in_ips,
|
||||
out_ips,
|
||||
file_conf.password,
|
||||
file_conf.simulate_multicast,
|
||||
file_conf.mtu,
|
||||
file_conf.tcp,
|
||||
virtual_ip,
|
||||
file_conf.relay,
|
||||
file_conf.server_encrypt,
|
||||
file_conf.parallel,
|
||||
cipher_model,
|
||||
file_conf.finger,
|
||||
punch_model,
|
||||
file_conf.port,
|
||||
);
|
||||
Ok((config, file_conf.cmd))
|
||||
}
|
||||
|
||||
pub fn get_device_id() -> String {
|
||||
if let Some(id) = common::identifier::get_unique_identifier() {
|
||||
id
|
||||
} else {
|
||||
let path_buf = crate::app_home().unwrap().join("device-id");
|
||||
if let Ok(id) = std::fs::read_to_string(path_buf.as_path()) {
|
||||
id
|
||||
} else {
|
||||
let id = uuid::Uuid::new_v4().to_string();
|
||||
let _ = std::fs::write(path_buf, &id);
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
+198
-143
@@ -16,6 +16,7 @@ use vnt::handle::handshake_handler::HandshakeEnum;
|
||||
use vnt::handle::registration_handler::ReqEnum;
|
||||
|
||||
mod command;
|
||||
mod config;
|
||||
mod console_out;
|
||||
mod root_check;
|
||||
|
||||
@@ -54,12 +55,10 @@ fn main() {
|
||||
opts.optopt("", "thread", "线程数(必须为正整数)", "<thread>");
|
||||
opts.optopt("", "model", "加密模式", "<model>");
|
||||
opts.optflag("", "finger", "指纹校验");
|
||||
opts.optopt(
|
||||
"",
|
||||
"punch",
|
||||
"取值ipv4/ipv6,表示仅使用ipv4或ipv6打洞",
|
||||
"<punch>",
|
||||
);
|
||||
opts.optopt("", "punch", "取值ipv4/ipv6", "<punch>");
|
||||
opts.optopt("", "port", "监听的端口", "<port>");
|
||||
opts.optflag("", "cmd", "开启窗口输入");
|
||||
opts.optopt("f", "", "配置文件", "<conf>");
|
||||
//"后台运行时,查看其他设备列表"
|
||||
opts.optflag("", "list", "后台运行时,查看其他设备列表");
|
||||
opts.optflag("", "all", "后台运行时,查看其他设备完整信息");
|
||||
@@ -101,156 +100,192 @@ fn main() {
|
||||
command::command(command::CommandEnum::All);
|
||||
return;
|
||||
}
|
||||
if !matches.opt_present("k") {
|
||||
print_usage(&program, opts);
|
||||
println!("parameter -k not found .");
|
||||
return;
|
||||
}
|
||||
let tap = matches.opt_present("a");
|
||||
let token: String = matches.opt_get("k").unwrap().unwrap();
|
||||
let device_id = matches.opt_get_default("d", String::new()).unwrap();
|
||||
let device_id = if device_id.is_empty() {
|
||||
if let Some(id) = common::identifier::get_unique_identifier() {
|
||||
id
|
||||
} else {
|
||||
let path_buf = app_home().unwrap().join("device-id");
|
||||
if let Ok(id) = std::fs::read_to_string(path_buf.as_path()) {
|
||||
id
|
||||
} else {
|
||||
let id = uuid::Uuid::new_v4().to_string();
|
||||
let _ = std::fs::write(path_buf, &id);
|
||||
id
|
||||
}
|
||||
}
|
||||
} else {
|
||||
device_id
|
||||
};
|
||||
if device_id.is_empty() {
|
||||
print_usage(&program, opts);
|
||||
println!("parameter -d not found .");
|
||||
return;
|
||||
}
|
||||
let name = matches
|
||||
.opt_get_default("n", os_info::get().to_string())
|
||||
.unwrap();
|
||||
let server_address_str = matches
|
||||
.opt_get_default("s", "nat1.wherewego.top:29872".to_string())
|
||||
.unwrap();
|
||||
let server_address = match server_address_str.to_socket_addrs() {
|
||||
Ok(mut addr) => {
|
||||
if let Some(addr) = addr.next() {
|
||||
addr
|
||||
} else {
|
||||
println!("parameter '-s {}' error .", server_address_str);
|
||||
let conf = matches.opt_str("f");
|
||||
let (config, cmd) = if conf.is_some() {
|
||||
match config::read_config(&conf.unwrap()) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
println!("conf err {}", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("parameter '-s {}' error {}.", server_address_str, e);
|
||||
} else {
|
||||
if !matches.opt_present("k") {
|
||||
print_usage(&program, opts);
|
||||
println!("parameter -k not found .");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let mut stun_server = matches.opt_strs("e");
|
||||
if stun_server.is_empty() {
|
||||
stun_server.push("stun1.l.google.com:19302".to_string());
|
||||
stun_server.push("stun2.l.google.com:19302".to_string());
|
||||
stun_server.push("stun.qq.com:3478".to_string());
|
||||
}
|
||||
let tap = matches.opt_present("a");
|
||||
let token: String = matches.opt_get("k").unwrap().unwrap();
|
||||
let device_id = matches.opt_get_default("d", String::new()).unwrap();
|
||||
let device_id = if device_id.is_empty() {
|
||||
config::get_device_id()
|
||||
} else {
|
||||
device_id
|
||||
};
|
||||
if device_id.is_empty() {
|
||||
print_usage(&program, opts);
|
||||
println!("parameter -d not found .");
|
||||
return;
|
||||
}
|
||||
let name = matches
|
||||
.opt_get_default("n", os_info::get().to_string())
|
||||
.unwrap();
|
||||
let server_address_str = matches
|
||||
.opt_get_default("s", "nat1.wherewego.top:29872".to_string())
|
||||
.unwrap();
|
||||
let server_address = match server_address_str.to_socket_addrs() {
|
||||
Ok(mut addr) => {
|
||||
if let Some(addr) = addr.next() {
|
||||
addr
|
||||
} else {
|
||||
println!("parameter '-s {}' error .", server_address_str);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("parameter '-s {}' error {}.", server_address_str, e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let mut stun_server = matches.opt_strs("e");
|
||||
if stun_server.is_empty() {
|
||||
stun_server.push("stun1.l.google.com:19302".to_string());
|
||||
stun_server.push("stun2.l.google.com:19302".to_string());
|
||||
stun_server.push("stun.qq.com:3478".to_string());
|
||||
}
|
||||
|
||||
let in_ip = matches.opt_strs("i");
|
||||
let in_ip = match ips_parse(&in_ip) {
|
||||
Ok(in_ip) => in_ip,
|
||||
Err(e) => {
|
||||
print_usage(&program, opts);
|
||||
println!();
|
||||
println!("-i: {:?} {}", in_ip, e);
|
||||
println!("example: -i 192.168.0.0/24,10.26.0.3");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let out_ip = matches.opt_strs("o");
|
||||
let out_ip = match out_ips_parse(&out_ip) {
|
||||
Ok(out_ip) => out_ip,
|
||||
Err(e) => {
|
||||
print_usage(&program, opts);
|
||||
println!();
|
||||
println!("-o: {:?} {}", out_ip, e);
|
||||
println!("example: -o 0.0.0.0/0");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let password: Option<String> = matches.opt_get("w").unwrap();
|
||||
let server_encrypt = matches.opt_present("W");
|
||||
let simulate_multicast = matches.opt_present("m");
|
||||
let unused_cmd = matches.opt_present("c");
|
||||
let mtu: Option<String> = matches.opt_get("u").unwrap();
|
||||
let mtu = if let Some(mtu) = mtu {
|
||||
match u16::from_str(&mtu) {
|
||||
Ok(mtu) => Some(mtu),
|
||||
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!("'-u {}' {}", mtu, e);
|
||||
println!("-i: {:?} {}", in_ip, e);
|
||||
println!("example: -i 192.168.0.0/24,10.26.0.3");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let out_ip = matches.opt_strs("o");
|
||||
let out_ip = match out_ips_parse(&out_ip) {
|
||||
Ok(out_ip) => out_ip,
|
||||
Err(e) => {
|
||||
print_usage(&program, opts);
|
||||
println!();
|
||||
println!("-o: {:?} {}", out_ip, e);
|
||||
println!("example: -o 0.0.0.0/0");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let password: Option<String> = matches.opt_get("w").unwrap();
|
||||
let server_encrypt = matches.opt_present("W");
|
||||
#[cfg(not(feature = "server_encrypt"))]
|
||||
{
|
||||
if server_encrypt {
|
||||
println!("Server encryption not supported");
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let virtual_ip: Option<String> = matches.opt_get("ip").unwrap();
|
||||
let virtual_ip =
|
||||
virtual_ip.map(|v| Ipv4Addr::from_str(&v).expect(&format!("'--ip {}' error", v)));
|
||||
if let Some(virtual_ip) = virtual_ip {
|
||||
if virtual_ip.is_unspecified() || virtual_ip.is_broadcast() || virtual_ip.is_multicast() {
|
||||
println!("'--ip {}' invalid", virtual_ip);
|
||||
let simulate_multicast = matches.opt_present("m");
|
||||
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 {}' {}", mtu, e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let virtual_ip: Option<String> = matches.opt_get("ip").unwrap();
|
||||
let virtual_ip =
|
||||
virtual_ip.map(|v| Ipv4Addr::from_str(&v).expect(&format!("'--ip {}' error", v)));
|
||||
if let Some(virtual_ip) = virtual_ip {
|
||||
if virtual_ip.is_unspecified() || virtual_ip.is_broadcast() || virtual_ip.is_multicast()
|
||||
{
|
||||
println!("'--ip {}' invalid", virtual_ip);
|
||||
return;
|
||||
}
|
||||
}
|
||||
let tcp_channel = matches.opt_present("tcp");
|
||||
let relay = matches.opt_present("relay");
|
||||
let parallel = matches.opt_get::<usize>("par").unwrap().unwrap_or(1);
|
||||
if parallel == 0 {
|
||||
println!("'--par {}' invalid", parallel);
|
||||
return;
|
||||
}
|
||||
}
|
||||
let tcp_channel = matches.opt_present("tcp");
|
||||
let relay = matches.opt_present("relay");
|
||||
let parallel = matches.opt_get::<usize>("par").unwrap().unwrap_or(1);
|
||||
if parallel == 0 {
|
||||
println!("'--par {}' invalid", parallel);
|
||||
return;
|
||||
}
|
||||
|
||||
let cipher_model = match matches.opt_get::<CipherModel>("model") {
|
||||
Ok(model) => model.unwrap_or(CipherModel::AesGcm),
|
||||
Err(e) => {
|
||||
println!("'--model ' invalid,{}", e);
|
||||
return;
|
||||
}
|
||||
let cipher_model = match matches.opt_get::<CipherModel>("model") {
|
||||
Ok(model) => {
|
||||
#[cfg(not(any(
|
||||
feature = "aes_gcm",
|
||||
feature = "server_encrypt",
|
||||
feature = "aes_cbc",
|
||||
feature = "aes_ecb",
|
||||
feature = "sm4_cbc"
|
||||
)))]
|
||||
{
|
||||
if password.is_some() && model.is_none() {
|
||||
println!("Encryption not supported");
|
||||
return;
|
||||
}
|
||||
}
|
||||
#[cfg(not(any(feature = "aes_gcm", feature = "server_encrypt")))]
|
||||
{
|
||||
if password.is_some() && model.is_none() {
|
||||
println!("'--model ' undefined");
|
||||
return;
|
||||
}
|
||||
model.unwrap_or(CipherModel::None)
|
||||
}
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
model.unwrap_or(CipherModel::AesGcm)
|
||||
}
|
||||
Err(e) => {
|
||||
println!("'--model ' invalid,{}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let finger = matches.opt_present("finger");
|
||||
let punch_model = matches
|
||||
.opt_get::<PunchModel>("punch")
|
||||
.unwrap()
|
||||
.unwrap_or(PunchModel::All);
|
||||
let port = matches.opt_get::<u16>("port").unwrap_or(None).unwrap_or(0);
|
||||
let cmd = matches.opt_present("cmd");
|
||||
let config = Config::new(
|
||||
tap,
|
||||
token,
|
||||
device_id,
|
||||
name,
|
||||
server_address,
|
||||
server_address_str,
|
||||
stun_server,
|
||||
in_ip,
|
||||
out_ip,
|
||||
password,
|
||||
simulate_multicast,
|
||||
mtu,
|
||||
tcp_channel,
|
||||
virtual_ip,
|
||||
relay,
|
||||
server_encrypt,
|
||||
parallel,
|
||||
cipher_model,
|
||||
finger,
|
||||
punch_model,
|
||||
port,
|
||||
);
|
||||
(config, cmd)
|
||||
};
|
||||
|
||||
let finger = matches.opt_present("finger");
|
||||
let punch_model = matches
|
||||
.opt_get::<PunchModel>("punch")
|
||||
.unwrap()
|
||||
.unwrap_or(PunchModel::All);
|
||||
println!("version {}", vnt::VNT_VERSION);
|
||||
let config = Config::new(
|
||||
tap,
|
||||
token,
|
||||
device_id,
|
||||
name,
|
||||
server_address,
|
||||
server_address_str,
|
||||
stun_server,
|
||||
in_ip,
|
||||
out_ip,
|
||||
password,
|
||||
simulate_multicast,
|
||||
mtu,
|
||||
tcp_channel,
|
||||
virtual_ip,
|
||||
relay,
|
||||
server_encrypt,
|
||||
parallel,
|
||||
cipher_model,
|
||||
finger,
|
||||
punch_model,
|
||||
);
|
||||
main0(config, !unused_cmd);
|
||||
|
||||
main0(config, cmd);
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
@@ -438,28 +473,48 @@ fn print_usage(program: &str, _opts: Options) {
|
||||
println!("Options:");
|
||||
println!(
|
||||
" -k <token> {}",
|
||||
green("必选,使用相同的token,就能组建一个局域网络".to_string())
|
||||
green("使用相同的token,就能组建一个局域网络".to_string())
|
||||
);
|
||||
println!(" -n <name> 给设备一个名字,便于区分不同设备,默认使用系统版本");
|
||||
println!(" -d <id> 设备唯一标识符,不使用--ip参数时,服务端凭此参数分配虚拟ip");
|
||||
println!(" -c 关闭交互式命令,使用此参数禁用控制台输入");
|
||||
println!(" -s <server> 注册和中继服务器地址");
|
||||
println!(" -e <stun-server> stun服务器,用于探测NAT类型,可多次指定,如-e addr1 -e addr2");
|
||||
println!(" -a 使用tap模式,默认使用tun模式");
|
||||
println!(" -i <in-ip> 配置点对网(IP代理)时使用,-i 192.168.0.0/24,10.26.0.3表示允许接收网段192.168.0.0/24的数据");
|
||||
println!(" 并转发到10.26.0.3,可指定多个网段");
|
||||
println!(" -o <out-ip> 配置点对网时使用,-o 192.168.0.0/24表示允许将数据转发到192.168.0.0/24,可指定多个网段");
|
||||
println!(" -w <password> 使用该密码生成的密钥对客户端数据进行加密,并且服务端无法解密,使用相同密码的客户端才能通信");
|
||||
let mut enums = String::new();
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
enums.push_str("/aes_gcm");
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
enums.push_str("/aes_cbc");
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
enums.push_str("/aes_ecb");
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
enums.push_str("/sm4_cbc");
|
||||
if !enums.is_empty() {
|
||||
println!(" -w <password> 使用该密码生成的密钥对客户端数据进行加密,并且服务端无法解密,使用相同密码的客户端才能通信");
|
||||
}
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
println!(" -W 加密当前客户端和服务端通信的数据,请留意服务端指纹是否正确");
|
||||
println!(" -m 模拟组播,默认情况下组播数据会被当作广播发送,开启后会模拟真实组播的数据发送");
|
||||
println!(" -u <mtu> 自定义mtu(不加密默认为1450,加密默认为1410)");
|
||||
println!(" -f <conf_file> 读取配置文件中的配置");
|
||||
|
||||
println!(" --tcp 和服务端使用tcp通信,默认使用udp,遇到udp qos时可指定使用tcp");
|
||||
println!(" --ip <ip> 指定虚拟ip,指定的ip不能和其他设备重复,必须有效并且在服务端所属网段下,默认情况由服务端分配");
|
||||
println!(" --relay 仅使用服务器转发,不使用p2p,默认情况允许使用p2p");
|
||||
println!(" --par <parallel> 任务并行度(必须为正整数),默认值为1");
|
||||
println!(" --model <model> 加密模式(默认aes_gcm),可选值aes_gcm/aes_cbc/aes_ecb,一般来说性能:aes_ecb>aes_cbc>aes_gcm");
|
||||
if !enums.is_empty() {
|
||||
println!(
|
||||
" --model <model> 加密模式(默认aes_gcm),可选值{}",
|
||||
&enums[1..]
|
||||
);
|
||||
}
|
||||
println!(" --finger 增加数据指纹校验,可增加安全性,如果服务端开启指纹校验,则客户端也必须开启");
|
||||
println!(" --punch <punch> 取值ipv4/ipv6,ipv4表示仅使用ipv4打洞");
|
||||
println!(" --port <port> 取值0~65535,指定本地监听的端口,默认取随机端口");
|
||||
println!(" --cmd 开启交互式命令,使用此参数开启控制台输入");
|
||||
|
||||
println!();
|
||||
println!(
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "vnt-jni"
|
||||
version = "1.2.4"
|
||||
version = "1.2.5"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -68,6 +68,7 @@ fn new_sync(env: &mut JNIEnv, config: JObject) -> Result<VntUtilSync, Error> {
|
||||
let finger = env.get_field(&config, "finger", "Z")?.z()?;
|
||||
let in_ips = to_string(env, &config, "inIps")?;
|
||||
let out_ips = to_string(env, &config, "outIps")?;
|
||||
let port = env.get_field(&config, "port", "I")?.i()? as u16;
|
||||
let in_ips = if let Some(in_ips) = in_ips {
|
||||
let in_ips: Vec<&str> = in_ips.split("\n").collect();
|
||||
let in_ips = in_ips.iter().map(|v| v.to_string()).collect();
|
||||
@@ -149,6 +150,7 @@ fn new_sync(env: &mut JNIEnv, config: JObject) -> Result<VntUtilSync, Error> {
|
||||
cipher_model,
|
||||
finger,
|
||||
PunchModel::All,
|
||||
port,
|
||||
);
|
||||
match VntUtilSync::new(config) {
|
||||
Ok(vnt_util) => Ok(vnt_util),
|
||||
|
||||
+13
-9
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "vnt"
|
||||
version = "1.2.4"
|
||||
version = "1.2.5"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@@ -20,16 +20,16 @@ thiserror = "1.0.37"
|
||||
protobuf = "3.2.0"
|
||||
socket2 = { version = "0.5.2", features = ["all"] }
|
||||
tokio = { version = "1.32.0", features = ["full"] }
|
||||
aes-gcm = { version = "0.10.2" }
|
||||
aes-gcm = { version = "0.10.2",optional = true }
|
||||
ring = { version = "0.16.20", optional = true }
|
||||
cbc = "0.1.2"
|
||||
ecb = "0.1.2"
|
||||
cbc = {version = "0.1.2",optional = true}
|
||||
ecb = {version = "0.1.2",optional = true}
|
||||
aes = "0.8.3"
|
||||
stun-format = { version = "1.0.1", features = ["fmt", "rfc3489"] }
|
||||
rsa = { version = "0.7.2", features = [] }
|
||||
spki = { version = "0.6.0", features = ["fingerprint", "alloc"] }
|
||||
rsa = { version = "0.7.2", features = [] ,optional = true}
|
||||
spki = { version = "0.6.0", features = ["fingerprint", "alloc"] ,optional = true}
|
||||
openssl-sys = { git = "https://github.com/lbl8603/rust-openssl" ,optional = true}
|
||||
|
||||
libsm = {git="https://github.com/lbl8603/libsm" ,optional = true}
|
||||
|
||||
[target.'cfg(any(target_os = "linux",target_os = "macos"))'.dependencies]
|
||||
tun = { path = "./rust-tun" }
|
||||
@@ -44,10 +44,14 @@ protobuf-codegen = "3.2.0"
|
||||
protoc-bin-vendored = "3.0.0"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
default = ["server_encrypt","aes_gcm","aes_cbc","aes_ecb","sm4_cbc"]
|
||||
openssl = ["openssl-sys"]
|
||||
# 从源码编译
|
||||
openssl-vendored = ["openssl-sys/vendored"]
|
||||
ring-cipher = ["ring"]
|
||||
|
||||
aes_cbc=["cbc"]
|
||||
aes_ecb=["ecb"]
|
||||
sm4_cbc=["libsm"]
|
||||
aes_gcm=["aes-gcm"]
|
||||
server_encrypt =["rsa","spki"]
|
||||
|
||||
|
||||
@@ -51,10 +51,6 @@ impl AesEcbCipher {
|
||||
//未加密的数据直接丢弃
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "not encrypt"));
|
||||
}
|
||||
if net_packet.payload().len() < 16 {
|
||||
log::error!("数据异常,长度{}小于{}", net_packet.payload().len(), 16);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
|
||||
if let Some(finger) = &self.finger {
|
||||
let mut nonce_raw = [0; 12];
|
||||
@@ -75,6 +71,10 @@ impl AesEcbCipher {
|
||||
}
|
||||
net_packet.set_data_len(net_packet.data_len() - finger.len())?;
|
||||
}
|
||||
if net_packet.payload().len() < 16 {
|
||||
log::error!("数据异常,长度{}小于{}", net_packet.payload().len(), 16);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
let mut out = [0u8; 1024 * 5];
|
||||
let rs = match self.key {
|
||||
AesEcbEnum::AES128ECB(key) => Aes128EcbDec::new(&key.into())
|
||||
@@ -111,7 +111,7 @@ impl AesEcbCipher {
|
||||
}
|
||||
Err(e) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("解密失败:{}", e),
|
||||
format!("aes_ecb解密失败:{}", e),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@@ -154,7 +154,7 @@ impl AesEcbCipher {
|
||||
}
|
||||
Err(e) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("加密失败:{}", e),
|
||||
format!("aes_ecb加密失败:{}", e),
|
||||
)),
|
||||
};
|
||||
}
|
||||
@@ -164,6 +164,8 @@ impl AesEcbCipher {
|
||||
fn test_aes_ecb() {
|
||||
let d = AesEcbCipher::new_128([0; 16], Some(Finger::new("123")));
|
||||
let mut p = NetPacket::new_encrypt([0; 100]).unwrap();
|
||||
let src = p.buffer().to_vec();
|
||||
d.encrypt_ipv4(&mut p).unwrap();
|
||||
d.decrypt_ipv4(&mut p).unwrap();
|
||||
assert_eq!(p.buffer(), &src)
|
||||
}
|
||||
|
||||
+95
-13
@@ -1,23 +1,37 @@
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
#[cfg(not(any(feature = "openssl-vendored", feature = "openssl")))]
|
||||
use crate::cipher::aes_ecb::AesEcbCipher;
|
||||
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
use crate::cipher::aes_cbc::AesCbcCipher;
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
#[cfg(not(feature = "ring-cipher"))]
|
||||
use crate::cipher::aes_gcm_cipher::AesGcmCipher;
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
#[cfg(any(feature = "openssl-vendored", feature = "openssl"))]
|
||||
use crate::cipher::openssl_aes_ecb::AesEcbCipher;
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
#[cfg(feature = "ring-cipher")]
|
||||
use crate::cipher::ring_aes_gcm_cipher::AesGcmCipher;
|
||||
use crate::cipher::{aes_cbc, Finger};
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
use crate::cipher::sm4_cbc::Sm4CbcCipher;
|
||||
use crate::cipher::Finger;
|
||||
use crate::protocol::NetPacket;
|
||||
use aes_cbc::AesCbcCipher;
|
||||
use sha2::Digest;
|
||||
use std::io;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
pub enum CipherModel {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
AesGcm,
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
AesCbc,
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
AesEcb,
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
Sm4Cbc,
|
||||
None,
|
||||
}
|
||||
|
||||
impl FromStr for CipherModel {
|
||||
@@ -25,19 +39,45 @@ impl FromStr for CipherModel {
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.to_lowercase().trim() {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
"aes_gcm" => Ok(CipherModel::AesGcm),
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
"aes_cbc" => Ok(CipherModel::AesCbc),
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
"aes_ecb" => Ok(CipherModel::AesEcb),
|
||||
_ => Err(format!("not match '{}', enum:aes_gcm/aes_cbc/aes_ecb", s)),
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
"sm4_cbc" => Ok(CipherModel::Sm4Cbc),
|
||||
_ => {
|
||||
let mut enums = String::new();
|
||||
#[cfg(feature = "aes_gcm")]
|
||||
enums.push_str("/aes_gcm");
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
enums.push_str("/aes_cbc");
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
enums.push_str("/aes_ecb");
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
enums.push_str("/sm4_cbc");
|
||||
let str = if enums.is_empty() {
|
||||
"no encrypt"
|
||||
} else {
|
||||
&enums[1..]
|
||||
};
|
||||
Err(format!("not match '{}', enum:{}", s, str))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Cipher {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
AesGcm((AesGcmCipher, Vec<u8>)),
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
AesCbc(AesCbcCipher),
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
AesEcb(AesEcbCipher),
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
Sm4Cbc(Sm4CbcCipher),
|
||||
None,
|
||||
}
|
||||
|
||||
@@ -53,6 +93,7 @@ impl Cipher {
|
||||
hasher.update(password.as_bytes());
|
||||
let key: [u8; 32] = hasher.finalize().into();
|
||||
match model {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
CipherModel::AesGcm => {
|
||||
if password.len() < 8 {
|
||||
let aes = AesGcmCipher::new_128(key[..16].try_into().unwrap(), finger);
|
||||
@@ -62,6 +103,7 @@ impl Cipher {
|
||||
Cipher::AesGcm((aes, key.to_vec()))
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
CipherModel::AesCbc => {
|
||||
if password.len() < 8 {
|
||||
let aes = AesCbcCipher::new_128(key[..16].try_into().unwrap(), finger);
|
||||
@@ -71,6 +113,7 @@ impl Cipher {
|
||||
Cipher::AesCbc(aes)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
CipherModel::AesEcb => {
|
||||
if password.len() < 8 {
|
||||
let aes = AesEcbCipher::new_128(key[..16].try_into().unwrap(), finger);
|
||||
@@ -80,6 +123,12 @@ impl Cipher {
|
||||
Cipher::AesEcb(aes)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
CipherModel::Sm4Cbc => {
|
||||
let aes = Sm4CbcCipher::new_128(key[..16].try_into().unwrap(), finger);
|
||||
Cipher::Sm4Cbc(aes)
|
||||
}
|
||||
CipherModel::None => Cipher::None,
|
||||
}
|
||||
} else {
|
||||
Cipher::None
|
||||
@@ -88,10 +137,12 @@ impl Cipher {
|
||||
pub fn new_key(key: [u8; 32], token: String) -> io::Result<Self> {
|
||||
let finger = Some(Finger::new(&token));
|
||||
match key.len() {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
16 => {
|
||||
let aes = AesGcmCipher::new_128(key[..16].try_into().unwrap(), finger);
|
||||
Ok(Cipher::AesGcm((aes, key[..16].to_vec())))
|
||||
}
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
32 => {
|
||||
let aes = AesGcmCipher::new_256(key, finger);
|
||||
Ok(Cipher::AesGcm((aes, key.to_vec())))
|
||||
@@ -104,9 +155,14 @@ impl Cipher {
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> io::Result<()> {
|
||||
match self {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
Cipher::AesGcm((aes_gcm, _)) => aes_gcm.decrypt_ipv4(net_packet),
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
Cipher::AesCbc(aes_cbc) => aes_cbc.decrypt_ipv4(net_packet),
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
Cipher::AesEcb(aes_ecb) => aes_ecb.decrypt_ipv4(net_packet),
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
Cipher::Sm4Cbc(sm4_cbc) => sm4_cbc.decrypt_ipv4(net_packet),
|
||||
Cipher::None => {
|
||||
if net_packet.is_encrypt() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "not key"));
|
||||
@@ -120,30 +176,56 @@ impl Cipher {
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> io::Result<()> {
|
||||
match self {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
Cipher::AesGcm((aes_gcm, _)) => aes_gcm.encrypt_ipv4(net_packet),
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
Cipher::AesCbc(aes_cbc) => aes_cbc.encrypt_ipv4(net_packet),
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
Cipher::AesEcb(aes_ecb) => aes_ecb.encrypt_ipv4(net_packet),
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
Cipher::Sm4Cbc(sm4_cbc) => sm4_cbc.encrypt_ipv4(net_packet),
|
||||
Cipher::None => Ok(()),
|
||||
}
|
||||
}
|
||||
pub fn check_finger<B: AsRef<[u8]>>(&self, net_packet: &NetPacket<B>) -> io::Result<()> {
|
||||
let finger = match self {
|
||||
Cipher::AesGcm((aes_gcm, _)) => aes_gcm.finger.as_ref(),
|
||||
Cipher::AesCbc(aes_cbc) => aes_cbc.finger.as_ref(),
|
||||
Cipher::AesEcb(aes_ecb) => aes_ecb.finger.as_ref(),
|
||||
Cipher::None => None,
|
||||
};
|
||||
if let Some(finger) = finger {
|
||||
finger.check_finger(net_packet)
|
||||
} else {
|
||||
Ok(())
|
||||
match self {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
Cipher::AesGcm((aes_gcm, _)) => aes_gcm
|
||||
.finger
|
||||
.as_ref()
|
||||
.map(|f| f.check_finger(net_packet))
|
||||
.unwrap_or(Ok(())),
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
Cipher::AesCbc(aes_cbc) => aes_cbc
|
||||
.finger
|
||||
.as_ref()
|
||||
.map(|f| f.check_finger(net_packet))
|
||||
.unwrap_or(Ok(())),
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
Cipher::AesEcb(aes_ecb) => aes_ecb
|
||||
.finger
|
||||
.as_ref()
|
||||
.map(|f| f.check_finger(net_packet))
|
||||
.unwrap_or(Ok(())),
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
Cipher::Sm4Cbc(sm4_cbc) => sm4_cbc
|
||||
.finger
|
||||
.as_ref()
|
||||
.map(|f| f.check_finger(net_packet))
|
||||
.unwrap_or(Ok(())),
|
||||
Cipher::None => Ok(()),
|
||||
}
|
||||
}
|
||||
pub fn key(&self) -> Option<&[u8]> {
|
||||
match self {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
Cipher::AesGcm((_, key)) => Some(key),
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
Cipher::AesCbc(aes_cbc) => Some(aes_cbc.key()),
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
Cipher::AesEcb(aes_ecb) => Some(aes_ecb.key()),
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
Cipher::Sm4Cbc(sm4_cbc) => Some(sm4_cbc.key()),
|
||||
Cipher::None => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
mod aes_cbc;
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
#[cfg(not(any(feature = "openssl-vendored", feature = "openssl")))]
|
||||
mod aes_ecb;
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
#[cfg(not(feature = "ring-cipher"))]
|
||||
mod aes_gcm_cipher;
|
||||
mod cipher;
|
||||
mod finger;
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
#[cfg(any(feature = "openssl-vendored", feature = "openssl"))]
|
||||
mod openssl_aes_ecb;
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
#[cfg(feature = "ring-cipher")]
|
||||
mod ring_aes_gcm_cipher;
|
||||
mod rsa_cipher;
|
||||
|
||||
#[cfg(any(feature = "openssl-vendored", feature = "openssl"))]
|
||||
mod openssl_aes_ecb;
|
||||
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
mod sm4_cbc;
|
||||
pub use cipher::Cipher;
|
||||
pub use cipher::CipherModel;
|
||||
pub use finger::Finger;
|
||||
|
||||
@@ -105,10 +105,6 @@ impl AesEcbCipher {
|
||||
//未加密的数据直接丢弃
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "not encrypt"));
|
||||
}
|
||||
if net_packet.payload().len() < 16 {
|
||||
log::error!("数据异常,长度{}小于{}", net_packet.payload().len(), 16);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
|
||||
if let Some(finger) = &self.finger {
|
||||
let mut nonce_raw = [0; 12];
|
||||
@@ -129,6 +125,10 @@ impl AesEcbCipher {
|
||||
}
|
||||
net_packet.set_data_len(net_packet.data_len() - finger.len())?;
|
||||
}
|
||||
if net_packet.payload().len() < 16 {
|
||||
log::error!("数据异常,长度{}小于{}", net_packet.payload().len(), 16);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
let input = net_packet.payload();
|
||||
let mut out = [0u8; 1024 * 5];
|
||||
let mut out_len = 0;
|
||||
|
||||
@@ -1,23 +1,32 @@
|
||||
use crate::protocol::body::{RsaSecretBody, RSA_ENCRYPTION_RESERVED};
|
||||
use crate::protocol::NetPacket;
|
||||
use rand::Rng;
|
||||
use rsa::pkcs8::der::Decode;
|
||||
use rsa::{PublicKey, RsaPublicKey};
|
||||
use sha2::Digest;
|
||||
use spki::{DecodePublicKey, EncodePublicKey};
|
||||
use std::io;
|
||||
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
use crate::protocol::body::{RsaSecretBody, RSA_ENCRYPTION_RESERVED};
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
use rand::Rng;
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
use rsa::pkcs8::der::Decode;
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
use rsa::{PublicKey, RsaPublicKey};
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
use sha2::Digest;
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
use spki::{DecodePublicKey, EncodePublicKey};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RsaCipher {
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
inner: Inner,
|
||||
}
|
||||
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
#[derive(Clone)]
|
||||
struct Inner {
|
||||
public_key: RsaPublicKey,
|
||||
}
|
||||
|
||||
impl RsaCipher {
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
pub fn new(der: &[u8]) -> io::Result<Self> {
|
||||
match RsaPublicKey::from_public_key_der(der) {
|
||||
Ok(public_key) => {
|
||||
@@ -30,6 +39,11 @@ impl RsaCipher {
|
||||
)),
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "server_encrypt"))]
|
||||
pub fn new(_der: &[u8]) -> io::Result<Self> {
|
||||
unimplemented!()
|
||||
}
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
pub fn finger(&self) -> io::Result<String> {
|
||||
match self.inner.public_key.to_public_key_der() {
|
||||
Ok(der) => match rsa::pkcs8::SubjectPublicKeyInfo::from_der(der.as_bytes()) {
|
||||
@@ -51,9 +65,21 @@ impl RsaCipher {
|
||||
)),
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "server_encrypt"))]
|
||||
pub fn finger(&self) -> io::Result<String> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl RsaCipher {
|
||||
#[cfg(not(feature = "server_encrypt"))]
|
||||
pub fn encrypt<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
_net_packet: &mut NetPacket<B>,
|
||||
) -> io::Result<NetPacket<Vec<u8>>> {
|
||||
unimplemented!()
|
||||
}
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
/// net_packet 必须预留足够长度
|
||||
pub fn encrypt<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
use crate::cipher::Finger;
|
||||
use crate::protocol::{NetPacket, HEAD_LEN};
|
||||
use libsm::sm4::cipher_mode::CipherMode;
|
||||
use libsm::sm4::Sm4CipherMode;
|
||||
use rand::RngCore;
|
||||
use std::io;
|
||||
|
||||
pub struct Sm4CbcCipher {
|
||||
key: [u8; 16],
|
||||
pub(crate) cipher: Sm4CipherMode,
|
||||
pub(crate) finger: Option<Finger>,
|
||||
}
|
||||
|
||||
impl Clone for Sm4CbcCipher {
|
||||
fn clone(&self) -> Self {
|
||||
let cipher = Sm4CipherMode::new(&self.key, CipherMode::Cbc).unwrap();
|
||||
Self {
|
||||
key: self.key,
|
||||
cipher,
|
||||
finger: self.finger.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sm4CbcCipher {
|
||||
pub fn key(&self) -> &[u8] {
|
||||
&self.key
|
||||
}
|
||||
}
|
||||
|
||||
impl Sm4CbcCipher {
|
||||
pub fn new_128(key: [u8; 16], finger: Option<Finger>) -> Self {
|
||||
let cipher = Sm4CipherMode::new(&key, CipherMode::Cbc).unwrap();
|
||||
Self {
|
||||
key,
|
||||
cipher,
|
||||
finger,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> io::Result<()> {
|
||||
if !net_packet.is_encrypt() {
|
||||
//未加密的数据直接丢弃
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "not encrypt"));
|
||||
}
|
||||
|
||||
if let Some(finger) = &self.finger {
|
||||
let mut nonce_raw = [0; 12];
|
||||
nonce_raw[0..4].copy_from_slice(&net_packet.source().octets());
|
||||
nonce_raw[4..8].copy_from_slice(&net_packet.destination().octets());
|
||||
nonce_raw[8] = net_packet.protocol().into();
|
||||
nonce_raw[9] = net_packet.transport_protocol();
|
||||
nonce_raw[10] = net_packet.is_gateway() as u8;
|
||||
nonce_raw[11] = net_packet.source_ttl();
|
||||
let len = net_packet.payload().len();
|
||||
if len < 12 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "payload len <12"));
|
||||
}
|
||||
let secret_body = &net_packet.payload()[..len - 12];
|
||||
let finger = finger.calculate_finger(&nonce_raw, secret_body);
|
||||
if &finger != &net_packet.payload()[len - 12..] {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "finger err"));
|
||||
}
|
||||
net_packet.set_data_len(net_packet.data_len() - finger.len())?;
|
||||
}
|
||||
let payload = net_packet.payload();
|
||||
let len = payload.len();
|
||||
if len < 16 || len > 1024 * 4 {
|
||||
log::error!("数据异常,长度{}小于16或大于4096", len);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
let mut out = [0u8; 1024 * 4];
|
||||
let data = &payload[..len - 16];
|
||||
let iv = &payload[len - 16..];
|
||||
match self.cipher.decrypt(data, iv, &mut out) {
|
||||
Ok(len) => {
|
||||
let src_net_packet = NetPacket::new(&out[..len])?;
|
||||
if src_net_packet.source() != net_packet.source() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
if src_net_packet.destination() != net_packet.destination() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
if src_net_packet.protocol() != net_packet.protocol() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
if src_net_packet.transport_protocol() != net_packet.transport_protocol() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
if src_net_packet.is_gateway() != net_packet.is_gateway() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
if src_net_packet.source_ttl() != net_packet.source_ttl() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
net_packet.set_data_len(len)?;
|
||||
net_packet.set_payload(src_net_packet.payload())?;
|
||||
net_packet.set_encrypt_flag(false);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("sm4_cbc解密失败:{}", e),
|
||||
)),
|
||||
}
|
||||
}
|
||||
/// net_packet 必须预留足够长度
|
||||
/// data_len是有效载荷的长度
|
||||
pub fn encrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> io::Result<()> {
|
||||
let mut out = [0u8; 1024 * 4];
|
||||
let mut iv = [0u8; 16];
|
||||
rand::thread_rng().fill_bytes(&mut iv);
|
||||
if net_packet.data_len() > 1024 * 4 - 32 {
|
||||
log::error!(
|
||||
"数据异常,长度{}大于1024 * 4 - 32",
|
||||
net_packet.buffer().len()
|
||||
);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "data err"));
|
||||
}
|
||||
match self.cipher.encrypt(net_packet.buffer(), &iv, &mut out) {
|
||||
Ok(len) => {
|
||||
net_packet.set_data_len(HEAD_LEN + len + 16)?;
|
||||
net_packet.payload_mut()[..len].copy_from_slice(&out[..len]);
|
||||
net_packet.payload_mut()[len..].copy_from_slice(&iv);
|
||||
if let Some(finger) = &self.finger {
|
||||
let mut nonce_raw = [0; 12];
|
||||
nonce_raw[0..4].copy_from_slice(&net_packet.source().octets());
|
||||
nonce_raw[4..8].copy_from_slice(&net_packet.destination().octets());
|
||||
nonce_raw[8] = net_packet.protocol().into();
|
||||
nonce_raw[9] = net_packet.transport_protocol();
|
||||
nonce_raw[10] = net_packet.is_gateway() as u8;
|
||||
nonce_raw[11] = net_packet.source_ttl();
|
||||
let finger = finger.calculate_finger(&nonce_raw, net_packet.payload());
|
||||
let src_data_len = net_packet.data_len();
|
||||
//设置实际长度
|
||||
net_packet.set_data_len(src_data_len + finger.len())?;
|
||||
|
||||
net_packet.buffer_mut()[src_data_len..].copy_from_slice(&finger);
|
||||
}
|
||||
net_packet.set_encrypt_flag(true);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("sm4_cbc加密失败:{}", e),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sm4_ecb() {
|
||||
let d = Sm4CbcCipher::new_128([0; 16], Some(Finger::new("123")));
|
||||
let mut p = NetPacket::new_encrypt([1; 1024]).unwrap();
|
||||
let src = p.buffer().to_vec();
|
||||
d.encrypt_ipv4(&mut p).unwrap();
|
||||
d.decrypt_ipv4(&mut p).unwrap();
|
||||
assert_eq!(p.buffer(), &src);
|
||||
let d = Sm4CbcCipher::new_128([0; 16], None);
|
||||
let mut p = NetPacket::new_encrypt([1; 102]).unwrap();
|
||||
let src = p.buffer().to_vec();
|
||||
d.encrypt_ipv4(&mut p).unwrap();
|
||||
d.decrypt_ipv4(&mut p).unwrap();
|
||||
assert_eq!(p.buffer(), &src)
|
||||
}
|
||||
+5
-2
@@ -69,11 +69,11 @@ pub struct VntUtil {
|
||||
impl VntUtil {
|
||||
pub async fn new(config: Config) -> io::Result<VntUtil> {
|
||||
//单个udp用同步的性能更好,但是代理和多端口监听用异步更方便,这里将两者结合起来
|
||||
let main_channel = UdpSocket::bind("0.0.0.0:0")?;
|
||||
let main_channel = UdpSocket::bind(format!("0.0.0.0:{}", config.port))?;
|
||||
main_channel.set_write_timeout(Some(Duration::from_secs(5)))?;
|
||||
main_channel.set_read_timeout(Some(Duration::from_secs(2)))?;
|
||||
let main_channel_ipv6 = if config.punch_model != PunchModel::IPv4 {
|
||||
match UdpSocket::bind("[::]:0") {
|
||||
match UdpSocket::bind(format!("[::]:{}", config.port)) {
|
||||
Ok(main_channel_ipv6) => {
|
||||
main_channel_ipv6.set_write_timeout(Some(Duration::from_secs(5)))?;
|
||||
Some(main_channel_ipv6)
|
||||
@@ -577,6 +577,7 @@ pub struct Config {
|
||||
pub cipher_model: CipherModel,
|
||||
pub finger: bool,
|
||||
pub punch_model: PunchModel,
|
||||
pub port: u16,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@@ -601,6 +602,7 @@ impl Config {
|
||||
cipher_model: CipherModel,
|
||||
finger: bool,
|
||||
punch_model: PunchModel,
|
||||
port: u16,
|
||||
) -> Self {
|
||||
for x in stun_server.iter_mut() {
|
||||
if !x.contains(":") {
|
||||
@@ -628,6 +630,7 @@ impl Config {
|
||||
cipher_model,
|
||||
finger,
|
||||
punch_model,
|
||||
port,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
use crate::error::Error;
|
||||
pub const VNT_VERSION: &'static str = "1.2.4.4";
|
||||
pub const VNT_VERSION: &'static str = "1.2.5";
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
pub mod channel;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::{fmt, io};
|
||||
|
||||
pub const ENCRYPTION_RESERVED: usize = 32 + 12;
|
||||
pub const ENCRYPTION_RESERVED: usize = 16 + 32 + 12;
|
||||
pub const AES_GCM_ENCRYPTION_RESERVED: usize = 32;
|
||||
pub const RSA_ENCRYPTION_RESERVED: usize = 32;
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ impl<B: AsRef<[u8]>> NetPacket<B> {
|
||||
}
|
||||
/// 网关通信的标识
|
||||
pub fn is_gateway(&self) -> bool {
|
||||
self.buffer.as_ref()[0] & 0x50 == 0x50
|
||||
self.buffer.as_ref()[0] & 0x40 == 0x40
|
||||
}
|
||||
pub fn version(&self) -> Version {
|
||||
Version::from(self.buffer.as_ref()[0] & 0x0F)
|
||||
@@ -198,6 +198,7 @@ impl<B: AsRef<[u8]> + AsMut<[u8]>> NetPacket<B> {
|
||||
}
|
||||
pub fn set_gateway_flag(&mut self, is_gateway: bool) {
|
||||
if is_gateway {
|
||||
// 后面的版本再改为0x40,改了之后不兼容1.2.5之前的版本
|
||||
self.buffer.as_mut()[0] = self.buffer.as_ref()[0] | 0x50
|
||||
} else {
|
||||
self.buffer.as_mut()[0] = self.buffer.as_ref()[0] & 0xBF
|
||||
|
||||
Reference in New Issue
Block a user