diff --git a/vnt-cli/src/config/mod.rs b/vnt-cli/src/config/mod.rs new file mode 100644 index 0000000..5d53cf0 --- /dev/null +++ b/vnt-cli/src/config/mod.rs @@ -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, + pub in_ips: Vec, + pub out_ips: Vec, + pub password: Option, + pub simulate_multicast: bool, + pub mtu: Option, + pub tcp: bool, + pub ip: Option, + 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::(&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 + } + } +} diff --git a/vnt-cli/src/main.rs b/vnt-cli/src/main.rs index b875091..c4515f2 100644 --- a/vnt-cli/src/main.rs +++ b/vnt-cli/src/main.rs @@ -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", "线程数(必须为正整数)", ""); opts.optopt("", "model", "加密模式", ""); opts.optflag("", "finger", "指纹校验"); - opts.optopt( - "", - "punch", - "取值ipv4/ipv6,表示仅使用ipv4或ipv6打洞", - "", - ); + opts.optopt("", "punch", "取值ipv4/ipv6", ""); + opts.optopt("", "port", "监听的端口", ""); + opts.optflag("", "cmd", "开启窗口输入"); + opts.optopt("f", "", "配置文件", ""); //"后台运行时,查看其他设备列表" opts.optflag("", "list", "后台运行时,查看其他设备列表"); opts.optflag("", "all", "后台运行时,查看其他设备完整信息"); @@ -101,156 +100,161 @@ 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 = 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 = 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 = matches.opt_get("w").unwrap(); + let server_encrypt = matches.opt_present("W"); + let simulate_multicast = matches.opt_present("m"); + let mtu: Option = 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 = 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; } } - } else { - None - }; - let virtual_ip: Option = 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 tcp_channel = matches.opt_present("tcp"); + let relay = matches.opt_present("relay"); + let parallel = matches.opt_get::("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::("par").unwrap().unwrap_or(1); - if parallel == 0 { - println!("'--par {}' invalid", parallel); - return; - } - let cipher_model = match matches.opt_get::("model") { - Ok(model) => model.unwrap_or(CipherModel::AesGcm), - Err(e) => { - println!("'--model ' invalid,{}", e); - return; - } + let cipher_model = match matches.opt_get::("model") { + Ok(model) => model.unwrap_or(CipherModel::AesGcm), + Err(e) => { + println!("'--model ' invalid,{}", e); + return; + } + }; + + let finger = matches.opt_present("finger"); + let punch_model = matches + .opt_get::("punch") + .unwrap() + .unwrap_or(PunchModel::All); + let port = matches.opt_get::("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::("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,11 +442,10 @@ fn print_usage(program: &str, _opts: Options) { println!("Options:"); println!( " -k {}", - green("必选,使用相同的token,就能组建一个局域网络".to_string()) + green("使用相同的token,就能组建一个局域网络".to_string()) ); println!(" -n 给设备一个名字,便于区分不同设备,默认使用系统版本"); println!(" -d 设备唯一标识符,不使用--ip参数时,服务端凭此参数分配虚拟ip"); - println!(" -c 关闭交互式命令,使用此参数禁用控制台输入"); println!(" -s 注册和中继服务器地址"); println!(" -e stun服务器,用于探测NAT类型,可多次指定,如-e addr1 -e addr2"); println!(" -a 使用tap模式,默认使用tun模式"); @@ -453,13 +456,17 @@ fn print_usage(program: &str, _opts: Options) { println!(" -W 加密当前客户端和服务端通信的数据,请留意服务端指纹是否正确"); println!(" -m 模拟组播,默认情况下组播数据会被当作广播发送,开启后会模拟真实组播的数据发送"); println!(" -u 自定义mtu(不加密默认为1450,加密默认为1410)"); + println!(" -f 读取配置文件中的配置"); + println!(" --tcp 和服务端使用tcp通信,默认使用udp,遇到udp qos时可指定使用tcp"); println!(" --ip 指定虚拟ip,指定的ip不能和其他设备重复,必须有效并且在服务端所属网段下,默认情况由服务端分配"); println!(" --relay 仅使用服务器转发,不使用p2p,默认情况允许使用p2p"); println!(" --par 任务并行度(必须为正整数),默认值为1"); - println!(" --model 加密模式(默认aes_gcm),可选值aes_gcm/aes_cbc/aes_ecb,一般来说性能:aes_ecb>aes_cbc>aes_gcm"); + println!(" --model 加密模式(默认aes_gcm),可选值aes_gcm/aes_cbc/aes_ecb/sm4_cbc"); println!(" --finger 增加数据指纹校验,可增加安全性,如果服务端开启指纹校验,则客户端也必须开启"); println!(" --punch 取值ipv4/ipv6,ipv4表示仅使用ipv4打洞"); + println!(" --port 取值0~65535,指定本地监听的端口,默认取随机端口"); + println!(" --cmd 开启交互式命令,使用此参数开启控制台输入"); println!(); println!( diff --git a/vnt-jni/src/vnt_util.rs b/vnt-jni/src/vnt_util.rs index 59f6c1a..60ead60 100644 --- a/vnt-jni/src/vnt_util.rs +++ b/vnt-jni/src/vnt_util.rs @@ -68,6 +68,7 @@ fn new_sync(env: &mut JNIEnv, config: JObject) -> Result { 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 { cipher_model, finger, PunchModel::All, + port, ); match VntUtilSync::new(config) { Ok(vnt_util) => Ok(vnt_util), diff --git a/vnt/src/core/mod.rs b/vnt/src/core/mod.rs index 348b825..fb16c36 100644 --- a/vnt/src/core/mod.rs +++ b/vnt/src/core/mod.rs @@ -69,11 +69,11 @@ pub struct VntUtil { impl VntUtil { pub async fn new(config: Config) -> io::Result { //单个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, } } }