增加服务端加密、完善客户端加密

This commit is contained in:
lubeilin
2023-08-27 11:58:32 +08:00
parent 81764433d8
commit 0472b1590e
42 changed files with 2546 additions and 887 deletions
+2
View File
@@ -31,4 +31,6 @@ pub struct DeviceItem {
pub nat_traversal_type: String,
pub rt: String,
pub status: String,
pub client_secret: bool,
pub current_client_secret:bool,
}
+4
View File
@@ -75,6 +75,7 @@ pub fn command_list(vnt: &Vnt) -> Vec<DeviceItem> {
let info = vnt.current_device();
let device_list = vnt.device_list();
let mut list = Vec::new();
let current_client_secret = vnt.client_encrypt();
for peer in device_list {
let name = peer.name;
let virtual_ip = peer.virtual_ip.to_string();
@@ -105,6 +106,7 @@ pub fn command_list(vnt: &Vnt) -> Vec<DeviceItem> {
("relay".to_string(), "".to_string())
};
let status = format!("{:?}", peer.status);
let client_secret = peer.client_secret;
let item = DeviceItem {
name,
virtual_ip,
@@ -114,6 +116,8 @@ pub fn command_list(vnt: &Vnt) -> Vec<DeviceItem> {
nat_traversal_type,
rt,
status,
client_secret,
current_client_secret,
};
list.push(item);
}
+5 -21
View File
@@ -1,5 +1,5 @@
use std::io;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::io::Write;
use tokio::net::UdpSocket;
use vnt::core::Vnt;
@@ -15,27 +15,11 @@ impl CommandServer {
impl CommandServer {
pub async fn start(self, vnt: Vnt) -> io::Result<()> {
let mut port = 21637 as u16;
let udp = loop {
match UdpSocket::bind(SocketAddr::V4(SocketAddrV4::new(
Ipv4Addr::new(127, 0, 0, 1),
port,
))).await {
Ok(udp) => {
break udp;
}
Err(e) => {
if e.kind() == io::ErrorKind::AddrInUse {
port += 1;
} else {
log::error!("创建udp失败 {:?}", e);
return Err(e);
}
}
}
};
let udp = UdpSocket::bind("127.0.0.1:0").await?;
let path_buf = crate::app_home()?.join("command-port");
std::fs::write(path_buf, udp.local_addr()?.port().to_string())?;
let mut file = std::fs::File::create(path_buf)?;
file.write_all(udp.local_addr()?.port().to_string().as_bytes())?;
file.sync_all()?;
let mut buf = [0u8; 64];
loop {
let (len, addr) = udp.recv_from(&mut buf).await?;
+20 -11
View File
@@ -56,18 +56,27 @@ pub fn console_device_list(mut list: Vec<DeviceItem>) {
("Rt".to_string(), Style::new())]);
for item in list {
if &item.status == "Online" {
if &item.nat_traversal_type == "p2p" {
out_list.push(vec![(item.name, Style::new().green()),
(item.virtual_ip, Style::new().green()),
(item.status, Style::new().green()),
(item.nat_traversal_type, Style::new().green()),
(item.rt, Style::new().green())]);
if item.client_secret != item.current_client_secret {
//加密状态不一致,无法通信的
out_list.push(vec![(item.name, Style::new().red()),
(item.virtual_ip, Style::new().red()),
(item.status, Style::new().red()),
("".to_string(), Style::new().red()),
("".to_string(), Style::new().red())]);
} else {
out_list.push(vec![(item.name, Style::new().yellow()),
(item.virtual_ip, Style::new().yellow()),
(item.status, Style::new().yellow()),
(item.nat_traversal_type, Style::new().yellow()),
(item.rt, Style::new().yellow())]);
if &item.nat_traversal_type == "p2p" {
out_list.push(vec![(item.name, Style::new().green()),
(item.virtual_ip, Style::new().green()),
(item.status, Style::new().green()),
(item.nat_traversal_type, Style::new().green()),
(item.rt, Style::new().green())]);
} else {
out_list.push(vec![(item.name, Style::new().yellow()),
(item.virtual_ip, Style::new().yellow()),
(item.status, Style::new().yellow()),
(item.nat_traversal_type, Style::new().yellow()),
(item.rt, Style::new().yellow())]);
}
}
} else {
out_list.push(vec![(item.name, Style::new().color256(102)),
+1 -1
View File
@@ -7,7 +7,7 @@ pub fn println_table(table: Vec<Vec<(String, Style)>>) {
let mut width_list = vec![0; table[0].len()];
for in_list in table.iter() {
for (index, (item, _)) in in_list.iter().enumerate() {
let width = console::measure_text_width(item) + 6;
let width = console::measure_text_width(item) + 4;
if width_list[index] < width {
width_list[index] = width;
}
+177 -68
View File
@@ -2,11 +2,17 @@ use std::io;
use std::net::{Ipv4Addr, ToSocketAddrs};
use std::path::PathBuf;
use std::str::FromStr;
use console::style;
use getopts::Options;
use tokio::io::{AsyncBufReadExt, BufReader};
use tokio::signal;
#[cfg(unix)]
use tokio::signal::unix::{signal, SignalKind};
use common::args_parse::{ips_parse, out_ips_parse};
use vnt::core::{Config, VntUtil};
use vnt::core::{Config, Vnt, VntUtil};
use vnt::handle::handshake_handler::HandshakeEnum;
use vnt::handle::registration_handler::ReqEnum;
mod command;
@@ -28,24 +34,26 @@ async fn main() {
}
async fn main0() {
let _ = log4rs::init_file("log4rs.yaml", Default::default());
let args: Vec<String> = std::env::args().collect();
let program = args[0].clone();
let mut opts = Options::new();
opts.optopt("k", "", "必选,使用相同的token,就能组建一个局域网络", "<token>");
opts.optopt("n", "", "设备一个名字,便于区分不同设备,默认使用系统版本", "<name>");
opts.optopt("d", "", "设备唯一标识符,不使用--ip参数时,服务端凭此参数分配虚拟ip", "<id>");
opts.optflag("c", "", "关闭交互式命令,使用此参数禁用控制台输入");
opts.optopt("k", "", "组网标识", "<token>");
opts.optopt("n", "", "设备名称", "<name>");
opts.optopt("d", "", "设备标识", "<id>");
opts.optflag("c", "", "关闭交互式命令");
opts.optopt("s", "", "注册和中继服务器地址", "<server>");
opts.optmulti("e", "", "stun服务器,用于探测NAT类型,可多次指定,如-e addr1 -e addr2", "<stun-server>");
opts.optflag("a", "", "使用tap模式,默认使用tun模式");
opts.optmulti("i", "", "配置点对网(IP代理)时使用,-i 192.168.0.0/24,10.26.0.3 \n表示允许接收网段192.168.0.0/24的数据并转发到10.26.0.3,可指定多个网段", "<in-ip>");
opts.optmulti("o", "", "配置点对网时使用,-o 192.168.0.0/24 \n表示允许将数据转发到192.168.0.0/24,可指定多个网段", "<out-ip>");
opts.optopt("w", "", "使用该密码生成的密钥对客户端数据进行加密,并且服务端无法解密,使用相同密码的客户端才能通信", "<password>");
opts.optflag("m", "", "模拟组播,默认情况下组播数据会被当作广播发送,开启后会模拟真实组播的数据发送");
opts.optmulti("e", "", "stun服务器", "<stun-server>");
opts.optflag("a", "", "使用tap模式");
opts.optmulti("i", "", "配置点对网(IP代理)入站时使用", "<in-ip>");
opts.optmulti("o", "", "配置点对网出站时使用", "<out-ip>");
opts.optopt("w", "", "客户端加密", "<password>");
opts.optflag("W", "", "服务端加密");
opts.optflag("m", "", "模拟组播");
opts.optopt("u", "", "自定义mtu(默认为1430)", "<mtu>");
opts.optflag("", "tcp", "和服务端使用tcp通信,默认使用udp,一般来说udp延迟和消耗更低");
opts.optopt("", "ip", "指定虚拟ip,指定的ip不能和其他设备重复,必须有效并且在服务端所属网段下,默认情况由服务端分配", "<IP>");
opts.optflag("", "relay", "仅使用服务器转发,不使用p2p,默认情况允许使用p2p");
opts.optflag("", "tcp", "tcp");
opts.optopt("", "ip", "指定虚拟ip", "<IP>");
opts.optflag("", "relay", "仅使用服务器转发");
//"后台运行时,查看其他设备列表"
opts.optflag("", "list", "后台运行时,查看其他设备列表");
opts.optflag("", "all", "后台运行时,查看其他设备完整信息");
@@ -117,7 +125,7 @@ async fn main0() {
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_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() {
@@ -162,6 +170,7 @@ async fn main0() {
}
};
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();
@@ -190,45 +199,101 @@ async fn main0() {
}
let tcp_channel = matches.opt_present("tcp");
let relay = matches.opt_present("relay");
println!("version 1.2.0");
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);
out_ip, password, simulate_multicast, mtu,
tcp_channel, virtual_ip, relay, server_encrypt);
let mut vnt_util = VntUtil::new(config).await.unwrap();
let mut conn_count = 0;
let response = loop {
match vnt_util.connect().await {
if conn_count > 0 {
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
}
conn_count += 1;
if let Err(e) = vnt_util.connect().await {
println!("connect server failed {}", e);
return;
}
match vnt_util.handshake().await {
Ok(response) => {
break response;
if server_encrypt {
let finger = response.unwrap().finger().unwrap();
println!("{}{}", green("server fingerprint:".to_string()), finger);
match vnt_util.secret_handshake().await {
Ok(_) => {}
Err(e) => {
match e {
HandshakeEnum::NotSecret => {}
HandshakeEnum::KeyError => {}
HandshakeEnum::Timeout => {
println!("handshake timeout")
}
HandshakeEnum::ServerError(str) => {
println!("error:{}", str);
}
HandshakeEnum::Other(str) => {
println!("error:{}", str);
}
}
continue;
}
}
}
match vnt_util.register().await {
Ok(response) => {
break response;
}
Err(e) => {
match e {
ReqEnum::TokenError => {
println!("token error");
return;
}
ReqEnum::AddressExhausted => {
println!("address exhausted");
return;
}
ReqEnum::Timeout => {
println!("timeout...");
}
ReqEnum::ServerError(str) => {
println!("error:{}", str);
}
ReqEnum::Other(str) => {
println!("error:{}", str);
}
ReqEnum::IpAlreadyExists => {
println!("ip already exists");
return;
}
ReqEnum::InvalidIp => {
println!("invalid ip");
return;
}
}
}
}
}
Err(e) => {
match e {
ReqEnum::TokenError => {
println!("token error");
HandshakeEnum::NotSecret => {
println!("The server does not support encryption");
return;
}
ReqEnum::AddressExhausted => {
println!("address exhausted");
HandshakeEnum::KeyError => {}
HandshakeEnum::Timeout => {
println!("handshake timeout")
}
ReqEnum::Timeout => {
println!("timeout...");
continue;
}
ReqEnum::ServerError(str) => {
HandshakeEnum::ServerError(str) => {
println!("error:{}", str);
continue;
}
ReqEnum::Other(str) => {
HandshakeEnum::Other(str) => {
println!("error:{}", str);
continue;
}
ReqEnum::IpAlreadyExists => {
println!("ip already exists");
}
ReqEnum::InvalidIp => {
println!("invalid ip");
}
}
return;
}
}
};
@@ -259,44 +324,56 @@ async fn main0() {
let stdin = tokio::io::stdin();
let mut cmd = String::new();
let mut reader = BufReader::new(stdin);
#[cfg(unix)]
let mut sigterm = signal(SignalKind::terminate()).expect("Error setting SIGTERM handler");
loop {
cmd.clear();
println!("input:list,info,route,all,stop");
#[cfg(unix)]
tokio::select! {
_ = vnt.wait_stop()=>{
break;
}
_ = signal::ctrl_c()=>{
let _ = vnt.stop();
vnt.wait_stop_ms(std::time::Duration::from_secs(3)).await;
return;
}
_ = sigterm.recv()=>{
let _ = vnt.stop();
vnt.wait_stop_ms(std::time::Duration::from_secs(3)).await;
return;
}
rs = reader.read_line(&mut cmd)=>{
match rs {
Ok(len) => {
if len ==0 {
if !command(&cmd[..len],&vnt){
break;
}
match cmd[..len].to_lowercase().trim() {
"list" => {
let list = command::command_list(&vnt);
console_out::console_device_list(list);
}
"info"=>{
let info = command::command_info(&vnt);
console_out::console_info(info);
}
"route" =>{
let route = command::command_route(&vnt);
console_out::console_route_table(route);
}
"all" =>{
let list = command::command_list(&vnt);
console_out::console_device_list_all(list);
}
"stop" =>{
let _ = vnt.stop();
break;
}
_ => {
}
}
Err(e) => {
println!("input err:{}",e);
break;
}
}
}
}
#[cfg(windows)]
tokio::select! {
_ = vnt.wait_stop()=>{
break;
}
_ = signal::ctrl_c()=>{
let _ = vnt.stop();
vnt.wait_stop_ms(std::time::Duration::from_secs(3)).await;
return;
}
rs = reader.read_line(&mut cmd)=>{
match rs {
Ok(len) => {
if !command(&cmd[..len],&vnt){
break;
}
println!();
}
Err(e) => {
println!("input err:{}",e);
@@ -310,9 +387,40 @@ async fn main0() {
vnt.wait_stop().await;
}
fn command(cmd: &str, vnt: &Vnt) -> bool {
if cmd.is_empty() {
return false;
}
match cmd.to_lowercase().trim() {
"list" => {
let list = command::command_list(&vnt);
console_out::console_device_list(list);
}
"info" => {
let info = command::command_info(&vnt);
console_out::console_info(info);
}
"route" => {
let route = command::command_route(&vnt);
console_out::console_route_table(route);
}
"all" => {
let list = command::command_list(&vnt);
console_out::console_device_list_all(list);
}
"stop" => {
let _ = vnt.stop();
return false;
}
_ => {}
}
println!();
return true;
}
fn print_usage(program: &str, _opts: Options) {
println!("Usage: {} [options]", program);
println!("version:1.1.2");
println!("version:1.2.0");
println!("Options:");
println!(" -k <token> {}", green("必选,使用相同的token,就能组建一个局域网络".to_string()));
println!(" -n <name> 给设备一个名字,便于区分不同设备,默认使用系统版本");
@@ -325,17 +433,18 @@ fn print_usage(program: &str, _opts: Options) {
println!(" 并转发到10.26.0.3,可指定多个网段");
println!(" -o <out-ip> 配置点对网时使用,-o 192.168.0.0/24表示允许将数据转发到192.168.0.0/24,可指定多个网段");
println!(" -w <password> 使用该密码生成的密钥对客户端数据进行加密,并且服务端无法解密,使用相同密码的客户端才能通信");
println!(" -W 加密当前客户端和服务端通信的数据,请留意服务端指纹是否正确");
println!(" -m 模拟组播,默认情况下组播数据会被当作广播发送,开启后会模拟真实组播的数据发送");
println!(" -u <mtu> 自定义mtu(默认为1430)");
println!(" --tcp 和服务端使用tcp通信,默认使用udp,遇到udp qos时可指定使用tcp");
println!(" --ip <IP> 指定虚拟ip,指定的ip不能和其他设备重复,必须有效并且在服务端所属网段下,默认情况由服务端分配");
println!(" --relay 仅使用服务器转发,不使用p2p,默认情况允许使用p2p");
println!();
println!(" --list {}",yellow("后台运行时,查看其他设备列表".to_string()));
println!(" --all {}",yellow("后台运行时,查看其他设备完整信息".to_string()));
println!(" --info {}",yellow("后台运行时,查看当前设备信息".to_string()));
println!(" --route {}",yellow("后台运行时,查看数据转发路径".to_string()));
println!(" --stop {}",yellow("停止后台运行".to_string()));
println!(" --list {}", yellow("后台运行时,查看其他设备列表".to_string()));
println!(" --all {}", yellow("后台运行时,查看其他设备完整信息".to_string()));
println!(" --info {}", yellow("后台运行时,查看当前设备信息".to_string()));
println!(" --route {}", yellow("后台运行时,查看数据转发路径".to_string()));
println!(" --stop {}", yellow("停止后台运行".to_string()));
println!(" -h, --help 帮助");
}