diff --git a/switch-desktop/src/command/server.rs b/switch-desktop/src/command/server.rs index 530e5bf..0be90a0 100644 --- a/switch-desktop/src/command/server.rs +++ b/switch-desktop/src/command/server.rs @@ -1,6 +1,5 @@ use std::io; use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket}; - use std::sync::Arc; use console::style; @@ -54,6 +53,7 @@ impl CommandServer { } } } + fn command(cmd: &str, switch: &Switch) -> io::Result { let mut out_str = String::new(); match cmd { @@ -129,6 +129,13 @@ fn command(cmd: &str, switch: &Switch) -> io::Result { let str = format!("Delay of relay server :{}ms\n", style(server_rt).green()); out_str.push_str(&str); } + if let Some(nat_info) = switch.nat_info() { + let str = format!( + "NAT type :{}", + style(format!("{:?}", nat_info.nat_type)).green() + ); + out_str.push_str(&str); + } } "help" | "h" => { let str = format!("Options: \n"); diff --git a/switch-desktop/src/main.rs b/switch-desktop/src/main.rs index 6d2b828..b4b6d82 100644 --- a/switch-desktop/src/main.rs +++ b/switch-desktop/src/main.rs @@ -1,11 +1,12 @@ use std::io; +use std::net::ToSocketAddrs; use std::path::PathBuf; use clap::Parser; use console::style; -use switch::*; use switch::handle::{PeerDeviceStatus, RouteType}; +use switch::*; #[cfg(windows)] mod command; @@ -16,9 +17,9 @@ mod windows; #[derive(Parser, Debug)] #[command( -author = "Lu Beilin", -version, -about = "一个虚拟网络工具,启动后会获取一个ip,相同token下的设备之间可以用ip直接通信" + author = "Lu Beilin", + version, + about = "一个虚拟网络工具,启动后会获取一个ip,相同token下的设备之间可以用ip直接通信" )] struct Args { /// 32位字符 @@ -30,6 +31,7 @@ struct Args { #[arg(long)] token: String, /// 给设备一个名称,为空时默认用系统版本信息 + /// Give the device a name. If it is blank, the system version information will be used by default #[arg(long)] name: Option, } @@ -120,7 +122,10 @@ fn main() { let _ = log_init(); let args = Args::parse(); if sudo::RunningAs::Root != sudo::check() { - println!("{}", style("需要使用root权限执行...").red()); + println!( + "{}", + style("需要使用root权限执行(Need to execute with root permission)...").red() + ); sudo::escalate_if_needed().unwrap(); } println!("{}", style("starting...").green()); @@ -129,7 +134,41 @@ fn main() { pub fn start(token: String, name: Option) { let mac_address = mac_address::get_mac_address().unwrap().unwrap().to_string(); - let switch = match Config::new(token, mac_address, name, || {}) { + let server_address = "nat1.wherewego.top:29875" + .to_socket_addrs() + .unwrap() + .next() + .unwrap(); + let nat_test_server = vec![ + "nat1.wherewego.top:35061" + .to_socket_addrs() + .unwrap() + .next() + .unwrap(), + "nat1.wherewego.top:35062" + .to_socket_addrs() + .unwrap() + .next() + .unwrap(), + "nat2.wherewego.top:35061" + .to_socket_addrs() + .unwrap() + .next() + .unwrap(), + "nat2.wherewego.top:35062" + .to_socket_addrs() + .unwrap() + .next() + .unwrap(), + ]; + let switch = match Config::new( + token, + mac_address, + name, + server_address, + nat_test_server, + || {}, + ) { Ok(config) => match Switch::start(config) { Ok(switch) => switch, Err(e) => { @@ -248,6 +287,12 @@ fn command(cmd: &str, switch: &Switch) -> Result<(), ()> { if server_rt >= 0 { println!("Delay of relay server :{}ms", style(server_rt).green()); } + if let Some(nat_info) = switch.nat_info() { + println!( + "NAT type :{}", + style(format!("{:?}", nat_info.nat_type)).green() + ); + } } "help" | "h" => { println!("Options: "); diff --git a/switch-desktop/src/windows/mod.rs b/switch-desktop/src/windows/mod.rs index 1df5092..910de42 100644 --- a/switch-desktop/src/windows/mod.rs +++ b/switch-desktop/src/windows/mod.rs @@ -1,15 +1,16 @@ -use std::{io, thread}; use std::ffi::OsString; use std::path::PathBuf; use std::time::Duration; +use std::{io, thread}; use clap::Parser; use console::style; -use windows_service::Error; + use windows_service::service::{ ServiceAccess, ServiceErrorControl, ServiceInfo, ServiceStartType, ServiceState, ServiceType, }; use windows_service::service_manager::{ServiceManager, ServiceManagerAccess}; +use windows_service::Error; use crate::config; @@ -18,9 +19,9 @@ mod windows_admin_check; #[derive(Parser, Debug)] #[command( -author = "Lu Beilin", -version, -about = "一个虚拟网络工具,启动后会获取一个ip,相同token下的设备之间可以用ip直接通信" + author = "Lu Beilin", + version, + about = "一个虚拟网络工具,启动后会获取一个ip,相同token下的设备之间可以用ip直接通信" )] struct Args { /// 32位字符 @@ -32,25 +33,32 @@ struct Args { #[arg(long)] token: Option, /// 给设备一个名称,为空时默认用系统版本信息 + /// Give the device a name. If it is blank, the system version information will be used by default #[arg(long)] name: Option, /// 安装服务,安装后可以后台运行,需要指定安装路径 + /// The installation service can run in the background after installation, and the installation path needs to be specified #[arg(long)] install: Option, /// 卸载服务 + /// Uninstall service #[arg(long)] uninstall: bool, /// 启动,启动时可以附加参数 --token,如果没有token,则会读取配置文件中上一次使用的token /// 安装服务后,会以服务的方式在后台启动,此时可以关闭命令行窗口 + /// When starting, you can attach the parameter -- token. If there is no token, the last token used in the configuration file will be read. After installing the service, it will be started in the background as a service. At this time, you can close the command line window #[arg(long)] start: bool, #[arg(long)] /// 停止,安装服务后,使用 --stop停止服务 + /// Stop. After installing the service, use -- stop to stop the service stop: bool, /// 启动服务后,使用 --list 查看设备列表 + /// After starting the service, use -- list to view the device list #[arg(long)] list: bool, /// 启动服务后,使用 --status 查看设备状态 + /// After starting the service, use -- status to view the device status #[arg(long)] status: bool, } @@ -85,7 +93,10 @@ pub fn main0() { return; } if !windows_admin_check::is_app_elevated() { - println!("{}", style("请使用管理员权限运行").red()); + println!( + "{}", + style("请使用管理员权限运行(Please run with administrator privileges)").red() + ); return; } if let Some(path) = args.install { @@ -94,23 +105,23 @@ pub fn main0() { std::fs::create_dir_all(&path).unwrap(); } if !path.is_dir() { - println!("参数必须为文件目录"); + println!("参数必须为文件目录(Parameter must be a file directory)"); } else { if let Err(e) = install(path) { log::error!("{:?}", e); } else { - println!("{}", style("安装成功").green()) + println!("{}", style("安装成功(Installation succeeded)").green()) } } } else if args.uninstall { if let Err(e) = uninstall() { log::error!("{:?}", e); } else { - println!("{}", style("卸载成功").green()) + println!("{}", style("卸载成功(Uninstall succeeded)").green()) } } else if args.start { if args.token.is_none() { - println!("{}", style("需要参数 --token").red()); + println!("{}", style("需要参数(require parameters) --token").red()); } else { let token = args.token.clone().unwrap(); match service_state() { @@ -120,18 +131,18 @@ pub fn main0() { token.clone(), args.name.clone(), )) - .unwrap(); + .unwrap(); match start() { Ok(_) => { //需要检查启动状态 - println!("{}", style("启动成功").green()) + println!("{}", style("启动成功(Start successfully)").green()) } Err(e) => { log::error!("{:?}", e); } } } else { - println!("服务未停止"); + println!("服务未停止(Service not stopped)"); } } Err(e) => { @@ -142,7 +153,7 @@ pub fn main0() { //指定的服务未安装。 println!( "{}", - style("服务未安装,在当前进程启动").red() + style("服务未安装,在当前进程启动(The service is not installed and started in the current process)").red() ); crate::start(token, args.name); return; @@ -158,20 +169,23 @@ pub fn main0() { } else if args.stop { match stop() { Ok(_) => { - println!("{}", style("停止成功").green()) + println!("{}", style("停止成功(Stopped successfully)").green()) } Err(e) => { log::error!("{:?}", e); } } } else { - println!("使用参数 -h 查看帮助") + println!("使用参数 -h 查看帮助(Use the parameter - h to view help)") } pause(); } fn pause() { - println!("{}", style("按任意键退出...").green()); + println!( + "{}", + style("按任意键退出(Press any key to exit)...").green() + ); use console::Term; let term = Term::stdout(); let _ = term.read_char().unwrap(); diff --git a/switch-desktop/src/windows/service.rs b/switch-desktop/src/windows/service.rs index bdb12d1..87e0432 100644 --- a/switch-desktop/src/windows/service.rs +++ b/switch-desktop/src/windows/service.rs @@ -6,14 +6,13 @@ use std::sync::Arc; use std::thread; use std::time::Duration; +use switch::{Config, Switch}; use windows_service::service::{ ServiceControl, ServiceControlAccept, ServiceExitCode, ServiceState, ServiceStatus, }; use windows_service::service_control_handler::ServiceControlHandlerResult; use windows_service::{define_windows_service, service_control_handler, service_dispatcher}; -use switch::{Config, Switch}; - use crate::windows::config::read_config; define_windows_service!(ffi_service_main, switch_service_main); @@ -63,9 +62,43 @@ fn service_main() -> windows_service::Result<()> { if let Some(config) = read_config() { let mac_address = mac_address::get_mac_address().unwrap().unwrap().to_string(); let un_parker = parker.unparker().clone(); - match Config::new(config.token, mac_address, config.name, move || { - un_parker.unpark(); - }) { + let server_address = "nat1.wherewego.top:29875" + .to_socket_addrs() + .unwrap() + .next() + .unwrap(); + let nat_test_server = vec![ + "nat1.wherewego.top:35061" + .to_socket_addrs() + .unwrap() + .next() + .unwrap(), + "nat1.wherewego.top:35062" + .to_socket_addrs() + .unwrap() + .next() + .unwrap(), + "nat2.wherewego.top:35061" + .to_socket_addrs() + .unwrap() + .next() + .unwrap(), + "nat2.wherewego.top:35062" + .to_socket_addrs() + .unwrap() + .next() + .unwrap(), + ]; + match Config::new( + config.token, + mac_address, + config.name, + server_address, + nat_test_server, + move || { + un_parker.unpark(); + }, + ) { Ok(config) => match Switch::start(config) { Ok(switch) => { log::info!("switch-service服务启动"); diff --git a/switch-jni/src/lib.rs b/switch-jni/src/lib.rs index e069301..a43a9a4 100644 --- a/switch-jni/src/lib.rs +++ b/switch-jni/src/lib.rs @@ -1,4 +1,4 @@ -use std::net::{IpAddr, Ipv4Addr}; +use std::net::{IpAddr, Ipv4Addr, ToSocketAddrs}; use jni::errors::Error; use jni::objects::{JClass, JObject, JString, JValue}; @@ -46,7 +46,41 @@ fn start(env: &JNIEnv, config: JObject) -> Result, Error> { let token = to_string_not_null(&env, config, "token")?; let mac_address = to_string_not_null(&env, config, "macAddress")?; let name = to_string(&env, config, "name")?; - let config = match Config::new(token, mac_address, name, || {}) { + let server_address = "nat1.wherewego.top:29875" + .to_socket_addrs() + .unwrap() + .next() + .unwrap(); + let nat_test_server = vec![ + "nat1.wherewego.top:35061" + .to_socket_addrs() + .unwrap() + .next() + .unwrap(), + "nat1.wherewego.top:35062" + .to_socket_addrs() + .unwrap() + .next() + .unwrap(), + "nat2.wherewego.top:35061" + .to_socket_addrs() + .unwrap() + .next() + .unwrap(), + "nat2.wherewego.top:35062" + .to_socket_addrs() + .unwrap() + .next() + .unwrap(), + ]; + let config = match Config::new( + token, + mac_address, + name, + server_address, + nat_test_server, + || {}, + ) { Ok(config) => config, Err(e) => { env.throw_new( @@ -187,7 +221,7 @@ fn device_list(env: &JNIEnv, device_list: Vec) -> Result> = const_mutex(None); + static ref NAT_TEST_ADDRESS:Mutex> = const_mutex(Vec::new()); } #[derive(Clone, Debug, Eq, PartialEq)] pub struct PeerDeviceInfo { @@ -96,10 +97,10 @@ impl Into for ConnectStatus { #[derive(Clone, Debug)] pub struct NatInfo { - public_ips: Vec, - public_port: u16, - public_port_range: u16, - nat_type: NatType, + pub public_ips: Vec, + pub public_port: u16, + pub public_port_range: u16, + pub nat_type: NatType, } impl NatInfo { @@ -118,9 +119,14 @@ impl NatInfo { } } +pub fn init_nat_test_addr(addrs: Vec) { + NAT_TEST_ADDRESS.lock().extend_from_slice(&addrs); +} + /// 初始化nat信息 pub fn init_nat_info(public_ip: u32, public_port: u16) { - match crate::nat::check::public_ip_list() { + let addrs = NAT_TEST_ADDRESS.lock().clone(); + match crate::nat::check::public_ip_list(&addrs) { Ok((nat_type, ips, port_range)) => { let mut public_ips = Vec::new(); public_ips.push(public_ip); diff --git a/switch/src/handle/punch_handler.rs b/switch/src/handle/punch_handler.rs index b8a9770..ee362a5 100644 --- a/switch/src/handle/punch_handler.rs +++ b/switch/src/handle/punch_handler.rs @@ -132,10 +132,10 @@ fn handle( } else { punch.public_port + punch.public_port_range }; - let k = if max_port - min_port +1 > 60 { + let k = if max_port - min_port + 1 > 60 { 60 } else { - max_port - min_port +1 + max_port - min_port + 1 }; send_f(min_port as u16, max_port as u16, k as usize)?; } @@ -371,7 +371,7 @@ fn send_punch(udp: &UdpSocket, cur_info: &CurrentDeviceInfo, nat_info: NatInfo) let ip = peer_info.virtual_ip; //只向ip比自己大的发起打洞,避免双方同时发起打洞浪费流量 if ip > cur_info.virtual_ip && !DIRECT_ROUTE_TABLE.contains_key(&ip) { - log::info!("发起打洞 {:?}, peer_info:{:?}",nat_info,peer_info); + log::info!("发起打洞 {:?}, peer_info:{:?}", nat_info, peer_info); let bytes = punch_packet(cur_info.virtual_ip, nat_info.clone(), ip)?; udp.send_to(&bytes, cur_info.connect_server)?; } diff --git a/switch/src/handle/tun_handler.rs b/switch/src/handle/tun_handler.rs index 3248503..d3b20a4 100644 --- a/switch/src/handle/tun_handler.rs +++ b/switch/src/handle/tun_handler.rs @@ -148,10 +148,10 @@ pub async fn handler_start( ) where F: FnOnce() + Send + 'static, { - #[cfg(target_os = "linux")] - use std::os::unix::io::AsRawFd; #[cfg(target_os = "macos")] use std::os::fd::AsRawFd; + #[cfg(target_os = "linux")] + use std::os::unix::io::AsRawFd; let raw_fd = tun_reader.0.as_raw_fd(); tokio::spawn(async move { let _ = status_watch.changed().await; diff --git a/switch/src/lib.rs b/switch/src/lib.rs index 5f68f10..69e28b1 100644 --- a/switch/src/lib.rs +++ b/switch/src/lib.rs @@ -1,7 +1,7 @@ use std::io; -use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, ToSocketAddrs, UdpSocket}; -use std::sync::Arc; +use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket}; use std::sync::atomic::Ordering; +use std::sync::Arc; use std::time::Duration; use crossbeam::atomic::AtomicCell; @@ -11,11 +11,11 @@ use tokio::sync::watch; use error::*; -use crate::handle::{ - ApplicationStatus, ConnectStatus, CurrentDeviceInfo, DEVICE_LIST, DIRECT_ROUTE_TABLE, PeerDeviceInfo, - Route, RouteType, SERVER_RT, -}; use crate::handle::registration_handler::CONNECTION_STATUS; +use crate::handle::{ + ApplicationStatus, ConnectStatus, CurrentDeviceInfo, NatInfo, PeerDeviceInfo, Route, RouteType, + DEVICE_LIST, DIRECT_ROUTE_TABLE, NAT_INFO, SERVER_RT, +}; pub mod error; pub mod handle; @@ -29,6 +29,8 @@ pub struct Config { pub token: String, pub mac_address: String, pub name: String, + pub server_address: SocketAddr, + pub nat_test_server: Vec, pub abnormal_call: F, } @@ -37,10 +39,12 @@ impl Config { token: String, mac_address: String, name: Option, + server_address: SocketAddr, + nat_test_server: Vec, abnormal_call: F, ) -> Result - where - F: FnOnce() + Send + 'static, + where + F: FnOnce() + Send + 'static, { if token.is_empty() || token.len() > 64 { return Err(Error::Stop("token invalid".to_string())); @@ -56,6 +60,8 @@ impl Config { token, mac_address, name, + server_address, + nat_test_server, abnormal_call, }) } else { @@ -69,6 +75,8 @@ impl Config { token, mac_address, name, + server_address, + nat_test_server, abnormal_call, }) } @@ -84,8 +92,8 @@ pub struct Switch { impl Switch { pub fn start(config: Config) -> Result - where - F: FnOnce() + Send + 'static, + where + F: FnOnce() + Send + 'static, { let runtime = tokio::runtime::Builder::new_multi_thread() .enable_all() @@ -109,6 +117,9 @@ impl Switch { pub fn current_device(&self) -> &CurrentDeviceInfo { &self.current_device } + pub fn nat_info(&self) -> Option { + NAT_INFO.lock().clone() + } pub fn server_rt(&self) -> i64 { SERVER_RT.load(Ordering::Relaxed) } @@ -141,11 +152,12 @@ impl Switch { return status == ApplicationStatus::Starting; } pub async fn start_(config: Config) -> Result - where - F: FnOnce() + Send + 'static, + where + F: FnOnce() + Send + 'static, { // let server_address = "nat1.wherewego.top:29876" - let server_address = "nat1.wherewego.top:29875".to_socket_addrs().unwrap().next().unwrap(); + // let server_address = "nat1.wherewego.top:29875".to_socket_addrs().unwrap().next().unwrap(); + let server_address = config.server_address; let mut port = 101 as u16; let udp = loop { match UdpSocket::bind(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from(0), port))) { @@ -215,9 +227,10 @@ impl Switch { drop(wait_group1); }, ) - .await; + .await; } //初始化nat数据 + handle::init_nat_test_addr(config.nat_test_server); handle::init_nat_info(response.public_ip, response.public_port as u16); // tun服务 let (tun_writer, tun_reader) = @@ -249,7 +262,7 @@ impl Switch { drop(wait_group1); }, ) - .await; + .await; let udp1 = udp.try_clone()?; let wait_group1 = wait_group.clone(); let status_sender1 = status_sender.clone(); @@ -269,7 +282,7 @@ impl Switch { drop(wait_group1); }, ) - .await; + .await; } //打洞处理 { @@ -291,7 +304,7 @@ impl Switch { drop(wait_group1); }, ) - .await; + .await; let udp1 = udp.try_clone()?; let wait_group1 = wait_group.clone(); let status_sender1 = status_sender.clone(); @@ -310,7 +323,7 @@ impl Switch { drop(wait_group1); }, ) - .await; + .await; let udp1 = udp.try_clone()?; let wait_group1 = wait_group.clone(); let status_sender1 = status_sender.clone(); @@ -329,7 +342,7 @@ impl Switch { drop(wait_group1); }, ) - .await; + .await; } //tun数据处理 { @@ -350,7 +363,7 @@ impl Switch { drop(wait_group1); }, ) - .await; + .await; } Ok(Switch { current_device, diff --git a/switch/src/nat/check.rs b/switch/src/nat/check.rs index a3c059a..9fa98be 100644 --- a/switch/src/nat/check.rs +++ b/switch/src/nat/check.rs @@ -21,7 +21,7 @@ use crate::proto::message::NatType; // } /// 返回所有公网ip和端口变化范围 -pub fn public_ip_list() -> io::Result<(NatType, Vec, u16)> { +pub fn public_ip_list(addrs: &Vec) -> io::Result<(NatType, Vec, u16)> { let mut hash_set = HashSet::new(); let mut max_port_range = 0; let mut nat_type = NatType::Cone; @@ -41,7 +41,7 @@ pub fn public_ip_list() -> io::Result<(NatType, Vec, u16)> { } } }; - let (set, min_port, max_port) = public_ip_list_(&udp)?; + let (set, min_port, max_port) = public_ip_list_(&udp, addrs)?; drop(udp); let port_range = max_port - min_port; //有多个ip或者端口有变化,说明是对称nat @@ -69,19 +69,24 @@ pub fn public_ip_list() -> io::Result<(NatType, Vec, u16)> { /// - 电信4g:对称网络只有一个ip 公网端口比较连续 /// - 综上:客户端使用小端口,针对对称网络 尝试所有ip 公网端口+-变化量的范围 /// - 打通概率 移动宽带=电信宽带>联调宽带>电信4g>移动4g>>联调4g -pub fn public_ip_list_(udp: &UdpSocket) -> io::Result<(HashSet, u16, u16)> { +pub fn public_ip_list_( + udp: &UdpSocket, + addrs: &Vec, +) -> io::Result<(HashSet, u16, u16)> { // println!("local port {:?}", udp.local_addr().unwrap().port()); udp.set_read_timeout(Some(Duration::from_millis(300)))?; let mut buf = [0u8; 128]; - let _ = udp.send_to(b"NatTest", "nat1.wherewego.top:35061")?; - let _ = udp.send_to(b"NatTest", "nat1.wherewego.top:35062")?; - let _ = udp.send_to(b"NatTest", "nat2.wherewego.top:35061")?; - let _ = udp.send_to(b"NatTest", "nat2.wherewego.top:35062")?; + for addr in addrs { + let _ = udp.send_to(b"NatTest", addr)?; + } + // let _ = udp.send_to(b"NatTest", "nat1.wherewego.top:35062")?; + // let _ = udp.send_to(b"NatTest", "nat2.wherewego.top:35061")?; + // let _ = udp.send_to(b"NatTest", "nat2.wherewego.top:35062")?; let mut hash_set = HashSet::new(); let mut count = 0; let mut min_port = 65535; let mut max_port = 0; - for _ in 0..4 { + for _ in 0..addrs.len() { if let Ok(len) = udp.recv(&mut buf) { if len != 16 || &buf[..10] != &b"NatType213"[..] { continue; @@ -152,6 +157,19 @@ pub fn nat_test_() -> io::Result { #[test] fn nat_test_run() { let udp = UdpSocket::bind("0.0.0.0:101").unwrap(); - let print = public_ip_list_(&udp).unwrap(); + use std::net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs, UdpSocket}; + let addrs = vec![ + "nat1.wherewego.top:35062" + .to_socket_addrs() + .unwrap() + .next() + .unwrap(), + "nat2.wherewego.top:35062" + .to_socket_addrs() + .unwrap() + .next() + .unwrap(), + ]; + let print = public_ip_list_(&udp, &addrs).unwrap(); println!("{:?}", print); } diff --git a/switch/src/tun_device/linux.rs b/switch/src/tun_device/linux.rs index 1811bf8..d86f553 100644 --- a/switch/src/tun_device/linux.rs +++ b/switch/src/tun_device/linux.rs @@ -1,5 +1,5 @@ -use std::net::Ipv4Addr; use crate::tun_device::{TunReader, TunWriter}; +use std::net::Ipv4Addr; pub fn create_tun( address: Ipv4Addr,