From 7840f30b64dc57d42a0fdecfc5798654528aec12 Mon Sep 17 00:00:00 2001 From: lubeilin <1791778603@qq.com> Date: Sun, 19 Mar 2023 20:11:51 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +- switch-desktop/Cargo.toml | 2 +- switch-desktop/src/config/mod.rs | 7 +- switch-desktop/src/console_out/mod.rs | 103 +++++++++++++++++++----- switch-desktop/src/console_out/table.rs | 18 ++--- switch-desktop/src/main.rs | 28 ++++--- switch-desktop/src/unix/mod.rs | 30 ++++--- switch-desktop/src/windows/mod.rs | 33 ++++---- switch-desktop/src/windows/service.rs | 9 ++- switch/p2p_channel | 2 +- switch/src/core/mod.rs | 10 +-- switch/src/handle/heartbeat_handler.rs | 2 +- switch/src/handle/recv_handler.rs | 15 ++-- switch/src/nat/mod.rs | 9 +++ 14 files changed, 174 insertions(+), 99 deletions(-) diff --git a/README.md b/README.md index 9ea4ae7..af3106c 100644 --- a/README.md +++ b/README.md @@ -47,16 +47,17 @@ 1. 和远程桌面(如mstsc)搭配,超低延迟的体验 2. 安装samba服务,共享磁盘 -3. 搭配nginx,在公网访问本地文件 +3. 搭配公网服务器nginx反向代理,在公网访问本地文件 ### 使用须知 - token的作用是标识一个虚拟局域网,当使用公共服务器时,建议使用一个唯一值当token(比如uuid),否则有可能连接到其他人创建的虚拟局域网中 +- 建议指定deviceId,默认使用MAC地址,在某些环境下可能发生变化 - 公共服务器目前的配置是2核4G 4Mbps,有需要再扩展~ - 需要root/管理员权限 - 使用命令行运行 - Mac和Linux下需要加可执行权限(例如:chmod +x ./switch-macos) - +- 自己搭注册和中继服务器(https://github.com/lbl8603/switch-server) ### 编译 前提条件:安装rust编译环境(https://www.rust-lang.org/zh-CN/tools/install) diff --git a/switch-desktop/Cargo.toml b/switch-desktop/Cargo.toml index 9d4adc5..bf154e5 100644 --- a/switch-desktop/Cargo.toml +++ b/switch-desktop/Cargo.toml @@ -23,7 +23,7 @@ crossbeam = "0.8.2" lazy_static = "1.4.0" parking_lot = "0.12.1" -fd-lock = "3.0.10" +fs2 = "0.4.3" os_info = "3.5.1" [target.'cfg(any(target_os = "linux",target_os = "macos"))'.dependencies] diff --git a/switch-desktop/src/config/mod.rs b/switch-desktop/src/config/mod.rs index 588ddda..17c6271 100644 --- a/switch-desktop/src/config/mod.rs +++ b/switch-desktop/src/config/mod.rs @@ -156,10 +156,9 @@ impl ArgsConfig { } } } -use fd_lock::RwLock; -pub fn lock_config() -> io::Result> { - let config_path = SWITCH_HOME_PATH.lock().clone().unwrap().join("config"); - Ok(RwLock::new(File::open(config_path)?)) +pub fn lock_file() -> io::Result { + let path = SWITCH_HOME_PATH.lock().clone().unwrap().join(".lock"); + Ok(File::create(path)?) } pub fn save_config(config: ArgsConfig) -> io::Result<()> { diff --git a/switch-desktop/src/console_out/mod.rs b/switch-desktop/src/console_out/mod.rs index 514e0ce..83f55d6 100644 --- a/switch-desktop/src/console_out/mod.rs +++ b/switch-desktop/src/console_out/mod.rs @@ -1,4 +1,4 @@ -use console::style; +use console::{style, Style}; use crate::command::entity::{DeviceItem, RouteItem, Status}; @@ -16,51 +16,118 @@ pub fn console_status(status: Status) { println!("Local ip: {}", style(status.local_ip).green()); } -pub fn console_route_table(list: Vec) { +pub fn console_route_table(mut list: Vec) { if list.is_empty() { println!("No route found"); return; } + list.sort_by(|t1, t2| t1.destination.cmp(&t2.destination)); let mut out_list = Vec::with_capacity(list.len()); - //表头 - out_list.push(vec!["Destination".to_string(), "Next Hop".to_string(), "Metric".to_string(), - "Rt".to_string(), "Interface".to_string()]); + + out_list.push(vec![("Destination".to_string(), Style::new()), + ("Next Hop".to_string(), Style::new()), + ("Metric".to_string(), Style::new()), + ("Rt".to_string(), Style::new()), + ("Interface".to_string(), Style::new()), ]); for item in list { - out_list.push(vec![item.destination, item.next_hop, item.metric, - item.rt, item.interface]); + out_list.push(vec![(item.destination, Style::new().green()), + (item.next_hop, Style::new().green()), + (item.metric, Style::new().green()), + (item.rt, Style::new().green()), + (item.interface, Style::new().green())]); } + table::println_table(out_list) } -pub fn console_device_list(list: Vec) { +pub fn console_device_list(mut list: Vec) { if list.is_empty() { println!("No other devices found"); return; } + list.sort_by(|t1, t2| t1.virtual_ip.cmp(&t2.virtual_ip)); + list.sort_by(|t1, t2| t1.status.cmp(&t2.status)); let mut out_list = Vec::with_capacity(list.len()); //表头 - out_list.push(vec!["Name".to_string(), "Virtual Ip".to_string(), "P2P/Relay".to_string(), "Rt".to_string(), "Status".to_string()]); + out_list.push(vec![("Name".to_string(), Style::new()), + ("Virtual Ip".to_string(), Style::new()), + ("Status".to_string(), Style::new()), + ("P2P/Relay".to_string(), Style::new()), + ("Rt".to_string(), Style::new())]); for item in list { - out_list.push(vec![item.name, item.virtual_ip, item.nat_traversal_type, - item.rt, item.status]); + 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())]); + } 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)), + (item.virtual_ip, Style::new().color256(102)), + (item.status, Style::new().color256(102)), + ("".to_string(), Style::new().color256(102)), + ("".to_string(), Style::new().color256(102))]); + } } table::println_table(out_list) } -pub fn console_device_list_all(list: Vec) { +pub fn console_device_list_all(mut list: Vec) { if list.is_empty() { println!("No other devices found"); return; } + list.sort_by(|t1, t2| t1.virtual_ip.cmp(&t2.virtual_ip)); + list.sort_by(|t1, t2| t1.status.cmp(&t2.status)); let mut out_list = Vec::with_capacity(list.len()); //表头 - out_list.push(vec!["Name".to_string(), "Virtual Ip".to_string(), "NAT Type".to_string(), - "Public Ips".to_string(), "Local Ip".to_string(), "P2P/Relay".to_string(), - "Rt".to_string(), "Status".to_string()]); + out_list.push(vec![("Name".to_string(), Style::new()), + ("Virtual Ip".to_string(), Style::new()), + ("Status".to_string(), Style::new()), + ("NAT Type".to_string(), Style::new()), + ("Public Ips".to_string(), Style::new()), + ("Local Ip".to_string(), Style::new()), + ("P2P/Relay".to_string(), Style::new()), + ("Rt".to_string(), Style::new())]); for item in list { - out_list.push(vec![item.name, item.virtual_ip, item.nat_type, - item.public_ips, item.local_ip, item.nat_traversal_type, - item.rt, item.status]); + 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()), + (item.nat_type, Style::new().green()), + (item.public_ips, Style::new().green()), + (item.local_ip, 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()), + (item.nat_type, Style::new().yellow()), + (item.public_ips, Style::new().yellow()), + (item.local_ip, Style::new().yellow()), ]); + } + } else { + out_list.push(vec![(item.name, Style::new().color256(102)), + (item.virtual_ip, Style::new().color256(102)), + (item.status, Style::new().color256(102)), + ("".to_string(), Style::new().color256(102)), + ("".to_string(), Style::new().color256(102)), + ("".to_string(), Style::new().color256(102)), + ("".to_string(), Style::new().color256(102)), + ("".to_string(), Style::new().color256(102)), ]); + } } table::println_table(out_list) } \ No newline at end of file diff --git a/switch-desktop/src/console_out/table.rs b/switch-desktop/src/console_out/table.rs index a7d325e..c117b95 100644 --- a/switch-desktop/src/console_out/table.rs +++ b/switch-desktop/src/console_out/table.rs @@ -1,29 +1,23 @@ -use console::style; +use console::Style; -pub fn println_table(table: Vec>) { +pub fn println_table(table: Vec>) { if table.is_empty() { return; } let mut width_list = vec![0; table[0].len()]; for in_list in table.iter() { - for (index, item) in in_list.iter().enumerate() { + for (index, (item, _)) in in_list.iter().enumerate() { let width = console::measure_text_width(item) + 6; if width_list[index] < width { width_list[index] = width; } } } - let mut head = true; for in_list in table { - for (index, item) in in_list.iter().enumerate() { - if head { - print!("{item:width$}", item = item, width = width_list[index]); - } else { - let str = format!("{item:width$}", item = item, width = width_list[index]); - print!("{}", style(str).green()); - } + for (col, (item, style)) in in_list.iter().enumerate() { + let str = format!("{:1$}", item, width_list[col]); + print!("{}", style.apply_to(str)); } - head = false; println!() } } \ No newline at end of file diff --git a/switch-desktop/src/main.rs b/switch-desktop/src/main.rs index 00a17fe..4fa2d65 100644 --- a/switch-desktop/src/main.rs +++ b/switch-desktop/src/main.rs @@ -4,8 +4,6 @@ use console::style; use switch::core::Switch; use crate::config::log_config::log_init; -#[cfg(target_os = "windows")] -use crate::config::log_config::log_service_init; mod command; mod config; @@ -86,13 +84,14 @@ pub struct StartArgs { /// NAT detection service address. Use comma to separate #[arg(long)] nat_test_server: Option, - /// 命令服务,开启后可以在其他进程使用 route、list等命令查看信息 - /// 程序使用后台运行时需要增加此参数 - /// Command service. After it is enabled, you can use route, list and other commands in other processes to view information. - /// This parameter needs to be added when the program is running in the background + /// 关闭命令服务,关闭后不能在其他进程直接使用route、list等命令查看信息 + /// Turn off the command service. After turning off, you cannot directly use the route, list and other commands to view information in other processes #[cfg(any(unix))] #[arg(long)] - command_server: bool, + off_command_server: bool, + /// 记录日志,输出在 home/.switch 目录下,长时间使用时不建议开启 + /// Output the log in the "home/.switch" directory + log: bool, } #[cfg(target_os = "windows")] @@ -124,16 +123,17 @@ fn main() { if args.len() == 3 && args[1] == windows::SERVICE_FLAG { //以服务的方式启动 config::set_home(std::path::PathBuf::from(&args[2])); - let _ = log_service_init(); - log::info!("config {:?}", std::path::PathBuf::from(&args[2])); - log::info!("config {:?}", config::read_config()); windows::service::start(); return; } else { let home = dirs::home_dir().unwrap().join(".switch"); config::set_home(home); - let _ = log_init(); let args = BaseArgs::parse(); + if let Commands::Start(start_args) = &args.command { + if start_args.log { + let _ = log_init(); + } + } windows::main0(args); } } @@ -142,8 +142,12 @@ fn main() { fn main() { let home = dirs::home_dir().unwrap().join(".switch"); config::set_home(home); - let _ = log_init(); let args = BaseArgs::parse(); + if let Commands::Start(start_args) = &args.command { + if start_args.log { + let _ = log_init(); + } + } unix::main0(args); } diff --git a/switch-desktop/src/unix/mod.rs b/switch-desktop/src/unix/mod.rs index dd0880a..f94a764 100644 --- a/switch-desktop/src/unix/mod.rs +++ b/switch-desktop/src/unix/mod.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use console::style; +use fs2::FileExt; use switch::core::{Config, Switch}; @@ -11,7 +12,7 @@ use crate::command::{command, CommandEnum}; pub fn main0(base_args: BaseArgs) { match base_args.command { Commands::Start(args) => { - let open_command_server = args.command_server; + let off_command_server = args.off_command_server; match config::default_config(args) { Ok(start_config) => { if sudo::RunningAs::Root != sudo::check() { @@ -37,7 +38,7 @@ pub fn main0(base_args: BaseArgs) { nat_test_server, start_config.device_id.clone(), ); - let mut lock = match config::lock_config() { + let lock = match config::lock_file() { Ok(lock) => { lock } @@ -46,17 +47,13 @@ pub fn main0(base_args: BaseArgs) { return; } }; - let lock_guard = match lock.try_write() { - Ok(lock) => { - lock - } - Err(_) => { - println!("{}", style("文件被重复打开").red()); - return; - } - }; + if lock.try_lock_exclusive().is_err() { + println!("{}", style("文件被重复打开").red()); + return; + } if let Err(e) = config::save_config(args_config) { log::error!("{:?}",e); + lock.unlock().unwrap(); return; } let switch = match Switch::start(config) { @@ -65,12 +62,16 @@ pub fn main0(base_args: BaseArgs) { } Err(e) => { log::error!("{:?}", e); + lock.unlock().unwrap(); return; } }; let switch = Arc::new(switch); let command_server = crate::command::server::CommandServer::new(); - if open_command_server { + if off_command_server { + crate::console_listen(&switch); + log::info!("前台任务结束"); + } else { if let Err(e) = config::update_pid(std::process::id()) { log::error!("{:?}", e); } @@ -86,11 +87,8 @@ pub fn main0(base_args: BaseArgs) { } else { log::info!("后台任务结束"); } - } else { - crate::console_listen(&switch); - log::info!("前台任务结束"); } - drop(lock_guard) + lock.unlock().unwrap(); } Err(e) => { log::error!("{:?}", e); diff --git a/switch-desktop/src/windows/mod.rs b/switch-desktop/src/windows/mod.rs index 7b8106a..4683816 100644 --- a/switch-desktop/src/windows/mod.rs +++ b/switch-desktop/src/windows/mod.rs @@ -5,6 +5,7 @@ use std::path::PathBuf; use std::time::Duration; use console::style; +use fs2::FileExt; use windows_service::Error; use windows_service::service::{ ServiceAccess, ServiceErrorControl, ServiceInfo, ServiceStartType, ServiceState, ServiceType, @@ -61,6 +62,7 @@ pub fn main0(base_args: BaseArgs) { // 允许应用通过防火墙 let _udp = UdpSocket::bind("0.0.0.0:0").unwrap(); } + let out_log = args.log; match config::default_config(args) { Ok(start_config) => { match service_state() { @@ -76,7 +78,7 @@ pub fn main0(base_args: BaseArgs) { log::error!("{:?}",e); return; } - match start() { + match start(out_log) { Ok(_) => { //需要检查启动状态 thread::sleep(Duration::from_secs(2)); @@ -107,22 +109,19 @@ pub fn main0(base_args: BaseArgs) { start_config.server, start_config.nat_test_server, ); - let mut lock = match config::lock_config() { - Ok(lock) => lock, + let lock = match config::lock_file() { + Ok(lock) => { + lock + } Err(e) => { log::error!("{:?}",e); return; } }; - let lock_guard = match lock.try_write() { - Ok(lock) => { - lock - } - Err(_) => { - println!("{}", style("程序文件被重复打开").red()); - return; - } - }; + if lock.try_lock_exclusive().is_err() { + println!("{}", style("文件被重复打开").red()); + return; + } match Switch::start(config) { Ok(switch) => { crate::console_listen(&switch); @@ -131,7 +130,7 @@ pub fn main0(base_args: BaseArgs) { log::error!("{:?}", e); } } - drop(lock_guard); + lock.unlock().unwrap(); return; } } @@ -329,11 +328,15 @@ fn uninstall() -> Result<(), Error> { Ok(()) } -fn start() -> Result<(), Error> { +fn start(out_log: bool) -> Result<(), Error> { let manager_access = ServiceManagerAccess::CONNECT; let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?; let service = service_manager.open_service(SERVICE_NAME, ServiceAccess::START)?; - service.start(&[""]) + if out_log { + service.start(&["log"]) + } else { + service.start(&[""]) + } } fn service_state() -> Result { diff --git a/switch-desktop/src/windows/service.rs b/switch-desktop/src/windows/service.rs index 66a102c..40dc4fc 100644 --- a/switch-desktop/src/windows/service.rs +++ b/switch-desktop/src/windows/service.rs @@ -20,7 +20,14 @@ use crate::windows::config::read_config; use crate::windows::SERVICE_NAME; define_windows_service!(ffi_service_main, switch_service_main); -pub fn switch_service_main(_arguments: Vec) { +pub fn switch_service_main(arguments: Vec) { + if !arguments.is_empty() { + if let Some(str) = arguments[0].to_str() { + if str == "log" { + let _ = config::log_config::log_service_init(); + } + } + } thread::spawn(|| match service_main() { Ok(_) => {} Err(e) => { diff --git a/switch/p2p_channel b/switch/p2p_channel index d75c050..a9c49f7 160000 --- a/switch/p2p_channel +++ b/switch/p2p_channel @@ -1 +1 @@ -Subproject commit d75c05048298dfc35d0502d8944cba1c4859fe48 +Subproject commit a9c49f79c66ecbe6a4286b9b6862c3488f730b13 diff --git a/switch/src/core/mod.rs b/switch/src/core/mod.rs index 322d212..bafaa3f 100644 --- a/switch/src/core/mod.rs +++ b/switch/src/core/mod.rs @@ -38,14 +38,10 @@ impl Switch { let virtual_gateway = Ipv4Addr::from(response.virtual_gateway); let virtual_netmask = Ipv4Addr::from(response.virtual_netmask); let current_device = Arc::new(AtomicCell::new(CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, config.server_address))); - let local_addr = channel.local_addr()?; - let local_ip = if local_addr.ip().is_unspecified() { - local_ip_address::local_ip().unwrap_or(local_addr.ip()) - } else { - local_addr.ip() - }; + let local_ip = crate::nat::local_ip()?; + let local_port = channel.local_addr()?.port(); // NAT检测 - let nat_test = NatTest::new(config.nat_test_server.clone(), Ipv4Addr::from(response.public_ip), response.public_port as u16, local_ip, local_addr.port()); + let nat_test = NatTest::new(config.nat_test_server.clone(), Ipv4Addr::from(response.public_ip), response.public_port as u16, local_ip, local_port); // tun通道 let (tun_writer, tun_reader) = tun_device::create_tun(virtual_ip, virtual_netmask, virtual_gateway)?; diff --git a/switch/src/handle/heartbeat_handler.rs b/switch/src/handle/heartbeat_handler.rs index 7f91fcc..5a4df8a 100644 --- a/switch/src/handle/heartbeat_handler.rs +++ b/switch/src/handle/heartbeat_handler.rs @@ -59,7 +59,7 @@ fn start_heartbeat_(sender: Sender, device_list: Arc> = None; let peer_list = device_list.lock().1.clone(); for peer in peer_list { diff --git a/switch/src/handle/recv_handler.rs b/switch/src/handle/recv_handler.rs index a158e86..9388ce8 100644 --- a/switch/src/handle/recv_handler.rs +++ b/switch/src/handle/recv_handler.rs @@ -17,6 +17,7 @@ use packet::ip::ipv4::packet::IpV4Packet; use crate::error::Error; use crate::handle::{check_dest, ConnectStatus, CurrentDeviceInfo, PeerDeviceInfo}; use crate::handle::registration_handler::Register; +use crate::nat; use crate::nat::NatTest; use crate::proto::message::{DeviceList, PunchInfo, PunchNatType, RegistrationResponse}; use crate::protocol::{control_packet, MAX_TTL, NetPacket, Protocol, service_packet, turn_packet, Version}; @@ -179,13 +180,9 @@ impl RecvHandler { service_packet::Protocol::RegistrationRequest => {} service_packet::Protocol::RegistrationResponse => { let response = RegistrationResponse::parse_from_bytes(net_packet.payload())?; - let local_addr = self.channel.local_addr()?; - let local_ip = if local_addr.ip().is_unspecified() { - local_ip_address::local_ip().unwrap_or(local_addr.ip()) - } else { - local_addr.ip() - }; - let nat_info = self.nat_test.re_test(Ipv4Addr::from(response.public_ip), response.public_port as u16, local_ip, local_addr.port()); + let local_port = self.channel.local_addr()?.port(); + let local_ip = nat::local_ip()?; + let nat_info = self.nat_test.re_test(Ipv4Addr::from(response.public_ip), response.public_port as u16, local_ip, local_port); self.channel.set_nat_type(nat_info.nat_type)?; let new_ip = Ipv4Addr::from(response.virtual_ip); let current_ip = current_device.virtual_ip(); @@ -296,7 +293,7 @@ impl RecvHandler { } } ControlPacket::PunchRequest => { - log::info!("PunchRequest route_key:{:?}",route_key); + // log::info!("PunchRequest route_key:{:?}",route_key); //回应 net_packet.set_transport_protocol(control_packet::Protocol::PunchResponse.into()); net_packet.set_source(current_device.virtual_ip()); @@ -307,7 +304,7 @@ impl RecvHandler { self.channel.add_route(source, route); } ControlPacket::PunchResponse => { - log::info!("PunchResponse route_key:{:?}",route_key); + // log::info!("PunchResponse route_key:{:?}",route_key); let route = Route::from(*route_key, 1, -1); self.channel.add_route(net_packet.source(), route); } diff --git a/switch/src/nat/mod.rs b/switch/src/nat/mod.rs index 2a3b03b..81c3dfb 100644 --- a/switch/src/nat/mod.rs +++ b/switch/src/nat/mod.rs @@ -1,3 +1,4 @@ +use std::io; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::sync::Arc; use parking_lot::Mutex; @@ -6,6 +7,14 @@ use crate::proto::message::PunchNatType; pub mod check; +use std::net::UdpSocket; + +pub fn local_ip() -> io::Result { + let socket = UdpSocket::bind("0.0.0.0:0")?; + socket.connect("8.8.8.8:80")?; + let addr = socket.local_addr()?; + Ok(addr.ip()) +} #[derive(Clone)] pub struct NatTest {