From 466174ef88b5eacc684893f03e0235a044f26478 Mon Sep 17 00:00:00 2001 From: lubeilin <1791778603@qq.com> Date: Fri, 23 Jun 2023 15:33:08 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=9B=BD=E9=99=85=E5=8C=96?= =?UTF-8?q?=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- switch-desktop/Cargo.toml | 3 +- switch-desktop/locales/en.yml | 49 ++++++ switch-desktop/locales/zh-CN.yml | 49 ++++++ switch-desktop/src/command/server.rs | 2 +- switch-desktop/src/command_args.rs | 128 ++++++++++++++++ switch-desktop/src/config/mod.rs | 46 +++--- switch-desktop/src/i18n.rs | 211 ++++++++++++++++++++++++++ switch-desktop/src/main.rs | 42 ++--- switch-desktop/src/windows/mod.rs | 62 +++++--- switch-desktop/src/windows/service.rs | 1 - 10 files changed, 523 insertions(+), 70 deletions(-) create mode 100644 switch-desktop/locales/en.yml create mode 100644 switch-desktop/locales/zh-CN.yml create mode 100644 switch-desktop/src/command_args.rs create mode 100644 switch-desktop/src/i18n.rs diff --git a/switch-desktop/Cargo.toml b/switch-desktop/Cargo.toml index d288719..16fad16 100644 --- a/switch-desktop/Cargo.toml +++ b/switch-desktop/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "switch-desktop" -version = "1.0.5" +version = "1.0.6" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -16,6 +16,7 @@ log4rs = "1.2.0" tokio = { version = "1.28.1", features = ["full"] } chrono = "0.4.23" +rust-i18n = "1.2.2" serde = "1.0" serde_yaml = "0.9" serde_json = "1.0.94" diff --git a/switch-desktop/locales/en.yml b/switch-desktop/locales/en.yml new file mode 100644 index 0000000..831a2c3 --- /dev/null +++ b/switch-desktop/locales/en.yml @@ -0,0 +1,49 @@ +switch_about: "A virtual network tool that will obtain an ip after startup. Devices under the same token will form a virtual local area network, and can use ip to communicate directly with each other." +switch_usage: "switch-desktop.exe " +switch_start_about: "Start switch" +switch_token_help: "Using the same token, you can build a local area network. It is recommended to use a more complex token to avoid connecting to other people's local area network" +switch_name_help: "Give the device a name, the system version information will be used by default" +switch_device_id_help: "The unique identifier of the device, the ip is assigned according to the id, and the MAC address is used by default" +switch_server_help: "Registry and relay server address, public server is used by default" +switch_nat_test_server_help: "NAT detection server addresses, separated by commas" +switch_log_help: "Record the log, the output is in the '${home}/.switch_desktop' directory, it is not recommended to open it for long-term use" +switch_tap_help: "Use tap mode, tun mode will be used by default" +switch_in_ip_help: "Use when configuring point-to-network (IP proxy), --in-ip 192.168.10.0/24,10.26.0.3, which means it is allowed to receive data from the network segment 192.168.10.0/24 and forward it to 10.26.0.3" +switch_out_ip_help: "Use when configuring point-to-network, --out-ip 192.168.10.0/24,192.168.1.10, which means that the data with the target of 192.168.10.0/24 is allowed to be forwarded from the network card 192.168.1.10" +switch_config_help: "Read configuration file" +switch_stop_about: "Stop background service" +switch_route_about: "View route" +switch_list_about: "View device list" +switch_list_all_help: "View full information" +switch_status_about: "View current device information" +switch_install_about: "Install windows service" +switch_path_help: "Service installation path, it is recommended to use an empty directory" +switch_auto_help: "Service starts automatically at boot" +switch_uninstall_about: "Uninstall windows service" +switch_config_about: "Change Windows Service Configuration" + +switch_use_admin_print: "Please run with administrator privileges" +switch_use_root_print: "Please run with root privileges" +switch_service_not_start_print: "service not started" +switch_start_successfully_print: "Start successfully" +switch_start_failed_print: "Startup failed" +switch_service_not_stopped_print: "service not stopped" +switch_stopped_print: "stopped" +switch_server_already_installed_print: "service is installed" + + +switch_repeated_start_print: "cannot be restarted repeatedly" + +switch_token_not_found_print: "missing token" +switch_token_cannot_be_empty_print: "token cannot be empty" +switch_token_cannot_exceed_64_print: "token cannot exceed 64 characters" +switch_device_id_is_empty_print: "The device id cannot be empty and the length cannot be greater than 64 characters" +switch_in_ips_example_print: "in_ips Parameter error Example:--in_ip 192.168.10.0/24,10.26.0.3" +switch_out_ips_example_print: "out_ips Parameter error Example:--out_ip 192.168.10.0/24,192.168.0.5" +switch_relay_server_address_error: "Wrong relay server address" +switch_nat_test_server_address_error: "NAT detection service address error" +switch_press_any_key_to_exit: "Press any key to exit" + +switch_virtual_ip: "virtual ip" +switch_virtual_gateway: "virtual gateway" +switch_please_enter_the_command: "Please enter the command (Usage: list,route,status,exit,help):" \ No newline at end of file diff --git a/switch-desktop/locales/zh-CN.yml b/switch-desktop/locales/zh-CN.yml new file mode 100644 index 0000000..7c9e210 --- /dev/null +++ b/switch-desktop/locales/zh-CN.yml @@ -0,0 +1,49 @@ +switch_about: "一个虚拟网络工具,启动后会获取一个ip,相同token下的设备会组件虚拟局域网,之间可以用ip直接通信" +switch_usage: "switch-desktop.exe <命令>" +switch_start_about: "启动switch" +switch_token_help: "使用相同的token,就能组建一个局域网络,建议使用一个复杂一点的token,避免连到其他人的局域网中" +switch_name_help: "给设备一个名字,默认会使用系统版本信息" +switch_device_id_help: "设备唯一标识符,凭id分配ip,默认使用MAC地址" +switch_server_help: "注册和中继服务器地址,默认使用公共服务器" +switch_nat_test_server_help: "NAT探测服务器地址,使用逗号分隔" +switch_log_help: "记录日志,输出在 '${home}/.switch_desktop' 目录下,长时间使用时不建议开启" +switch_tap_help: "使用tap模式,默认会使用tun模式" +switch_in_ip_help: "配置点对网(IP代理)时使用,--in-ip 192.168.10.0/24,10.26.0.3,表示允许接收网段192.168.10.0/24的数据并转发到10.26.0.3" +switch_out_ip_help: "配置点对网时使用,--out-ip 192.168.10.0/24,192.168.1.10,表示允许目标为192.168.10.0/24的数据从网卡192.168.1.10转发出去" +switch_config_help: "读取配置文件" +switch_stop_about: "停止后台服务" +switch_route_about: "查看路由" +switch_list_about: "查看设备列表" +switch_list_all_help: "查看完整信息" +switch_status_about: "查看当前设备信息" +switch_install_about: "安装Windows服务" +switch_path_help: "服务安装路径,建议使用一个空目录" +switch_auto_help: "服务开机自启动" +switch_uninstall_about: "卸载Windows服务" +switch_config_about: "改变Windows服务配置" + +switch_use_admin_print: "请使用管理员权限运行" +switch_use_root_print: "请使用root权限运行" +switch_service_not_start_print: "服务未启动" +switch_start_successfully_print: "启动成功" +switch_start_failed_print: "启动失败" +switch_service_not_stopped_print: "服务未停止" +switch_stopped_print: "已停止" +switch_server_already_installed_print: "服务已经安装" + + +switch_repeated_start_print: "不能重复启动" + +switch_token_not_found_print: "缺少token" +switch_token_cannot_be_empty_print: "token不能为空" +switch_token_cannot_exceed_64_print: "token不能超过64个字符" +switch_device_id_is_empty_print: "设备id不能为空并且长度不能大于64字符" +switch_in_ips_example_print: "in_ips 参数错误 示例:--in_ip 192.168.10.0/24,10.26.0.3" +switch_out_ips_example_print: "out_ips 参数错误 示例:--out_ip 192.168.10.0/24,192.168.0.5" +switch_relay_server_address_error: "中继服务器地址错误" +switch_nat_test_server_address_error: "NAT检测服务地址错误" +switch_press_any_key_to_exit: "按任意键退出" + +switch_virtual_ip: "当前虚拟ip(virtual ip)" +switch_virtual_gateway: "虚拟网关(virtual gateway)" +switch_please_enter_the_command: "输入命令 (例如: list,route,status,exit,help):" \ No newline at end of file diff --git a/switch-desktop/src/command/server.rs b/switch-desktop/src/command/server.rs index 3791585..3c0fea4 100644 --- a/switch-desktop/src/command/server.rs +++ b/switch-desktop/src/command/server.rs @@ -182,7 +182,7 @@ fn command(cmd: &str, switch: &Switch) -> io::Result { "stopping".to_string() } _ => { - format!("command '{}' not fount. \n Try to enter: 'help'\n", cmd) + format!("command '{}' not found. \n Try to enter: 'help'\n", cmd) } }; Ok(out_str) diff --git a/switch-desktop/src/command_args.rs b/switch-desktop/src/command_args.rs new file mode 100644 index 0000000..7b15672 --- /dev/null +++ b/switch-desktop/src/command_args.rs @@ -0,0 +1,128 @@ +use clap::{Arg, ArgAction, Command}; +use clap::builder::BoolishValueParser; +use crate::i18n::*; + +fn common() -> Command { + Command::new("switch-desktop") + .about(switch_about()) + // .version(switch_version()) + .subcommand_required(true) + .arg_required_else_help(true) + // .author(switch_author()) + .override_usage(switch_usage()) + .subcommand( + Command::new("start") + .about(switch_start_about()) + .arg( + Arg::new("token") + .long("token") + .help(switch_token_help()) + .action(ArgAction::Set) + ) + .arg( + Arg::new("name") + .long("name") + .help(switch_name_help()) + .action(ArgAction::Set) + ) + .arg( + Arg::new("device_id") + .long("device-id") + .help(switch_device_id_help()) + .action(ArgAction::Set) + ).arg( + Arg::new("server") + .long("server") + .help(switch_server_help()) + .action(ArgAction::Set) + ).arg( + Arg::new("nat_test_server") + .long("nat-test-server") + .help(switch_nat_test_server_help()) + .action(ArgAction::Set) + ).arg( + Arg::new("log") + .long("log") + .help(switch_log_help()) + .action(ArgAction::SetTrue) + .value_parser(BoolishValueParser::new()), + ).arg( + Arg::new("tap") + .long("tap") + .help(switch_tap_help()) + .action(ArgAction::SetTrue), + ).arg( + Arg::new("in_ip") + .long("in-ip") + .help(switch_in_ip_help()) + .action(ArgAction::Append) + .num_args(1..), + ).arg( + Arg::new("out_ip") + .long("out-ip") + .help(switch_out_ip_help()) + .action(ArgAction::Append) + ).arg( + Arg::new("config") + .long("config") + .help(switch_config_help()) + .action(ArgAction::Set) + ) + , + ).subcommand( + Command::new("stop") + .about(switch_stop_about())) + .subcommand( + Command::new("route") + .about(switch_route_about())) + .subcommand(Command::new("list") + .about(switch_list_about()).arg( + Arg::new("all") + .long("all") + .short('a') + .help(switch_list_all_help()) + .action(ArgAction::SetTrue) + .value_parser(BoolishValueParser::new()), )) + .subcommand(Command::new("status") + .about(switch_status_about())) +} + +pub fn check() -> bool { + #[cfg(windows)] + let cmd = common().subcommand(Command::new("install") + .about(switch_install_about()) + .arg( + Arg::new("path") + .long("path") + .help(switch_path_help()) + .action(ArgAction::Set) + .num_args(1..)) + .arg( + Arg::new("auto") + .long("auto") + .help(switch_auto_help()) + .action(ArgAction::SetTrue) + .value_parser(BoolishValueParser::new()), )) + .subcommand(Command::new("uninstall") + .about(switch_uninstall_about())) + .subcommand(Command::new("config") + .about(switch_config_about()) + .arg( + Arg::new("auto") + .long("auto") + .help(switch_auto_help()) + .action(ArgAction::SetTrue) + .value_parser(BoolishValueParser::new()), )); + #[cfg(any(unix))] + let cmd = common(); + match cmd.try_get_matches() { + Ok(_) => { + true + } + Err(e) => { + println!("{}", e); + false + } + } +} + diff --git a/switch-desktop/src/config/mod.rs b/switch-desktop/src/config/mod.rs index 6723c2e..9480470 100644 --- a/switch-desktop/src/config/mod.rs +++ b/switch-desktop/src/config/mod.rs @@ -8,7 +8,7 @@ use lazy_static::lazy_static; use parking_lot::Mutex; use serde::{Deserialize, Serialize}; -use crate::StartArgs; +use crate::{i18n, StartArgs}; pub mod log_config; lazy_static! { @@ -25,7 +25,7 @@ pub fn set_win_server_home(home: PathBuf) { let _ = SWITCH_HOME_PATH.lock().insert(home); } -#[derive(Clone,Debug)] +#[derive(Clone, Debug)] pub struct StartConfig { pub tap: bool, pub name: String, @@ -101,14 +101,14 @@ pub fn default_config(start_args: StartArgs) -> Result { println!("use tun"); } if start_args.token.is_none() { - return Err("找不到token(Token not found)".to_string()); + return Err(i18n::switch_token_not_found_print()); } let token = start_args.token.unwrap(); if token.is_empty() { - return Err("token不能为空(Token cannot be empty)".to_string()); + return Err(i18n::switch_token_cannot_be_empty_print()); } if token.len() > 64 { - return Err("token不能超过64字符(Token cannot exceed 64 characters)".to_string()); + return Err(i18n::switch_token_cannot_exceed_64_print()); } println!("token:{:?}", token); let name = start_args.name.unwrap_or_else(|| { @@ -129,7 +129,7 @@ pub fn default_config(start_args: StartArgs) -> Result { } }); if device_id.is_empty() || device_id.len() > 64 { - return Err("设备id不能为空并且长度不能大于64字符(The device id cannot be empty and the length cannot be greater than 64 characters)".to_string()); + return Err(i18n::switch_device_id_is_empty_print()); } println!("device_id:{:?}", device_id); let in_ips = start_args.in_ip.unwrap_or_else(|| { @@ -142,13 +142,13 @@ pub fn default_config(start_args: StartArgs) -> Result { let in_ips_c = if let Ok(in_ips_c) = ips_parse(&in_ips) { in_ips_c } else { - return Err("in_ips 参数错误 示例:--in_ip 192.168.10.0/24,10.26.0.3".to_string()); + return Err(i18n::switch_in_ips_example_print()); }; println!("out_ips:{:?}", out_ips); let out_ips_c = if let Ok(out_ips_c) = ips_parse(&out_ips) { out_ips_c } else { - return Err("out_ips 参数错误 示例:--out_ip 192.168.10.0/24,192.168.0.5".to_string()); + return Err(i18n::switch_out_ips_example_print()); }; @@ -159,11 +159,11 @@ pub fn default_config(start_args: StartArgs) -> Result { if let Some(addr) = server.next() { addr } else { - return Err("中继服务器地址错误( Relay server address error)".to_string()); + return Err(i18n::switch_relay_server_address_error()); } } Err(e) => { - return Err(format!("中继服务器地址错误( Relay server address error) :{:?}", e)); + return Err(format!("{} :{:?}",i18n::switch_relay_server_address_error(), e)); } }; println!("中继服务器:{:?}", server); @@ -172,7 +172,7 @@ pub fn default_config(start_args: StartArgs) -> Result { }).split(",").flat_map(|a| a.to_socket_addrs()).flatten() .collect::>(); if nat_test_server.is_empty() { - return Err("NAT检测服务地址错误(NAT detection service address error)".to_string()); + return Err(i18n::switch_nat_test_server_address_error()); } println!("NAT探测服务器:{:?}", nat_test_server); let base_config = StartConfig { @@ -211,10 +211,10 @@ pub fn read_config_file(config_path: PathBuf) -> Result { } let token = args_config.token; if token.is_empty() { - return Err("token不能为空(Token cannot be empty)".to_string()); + return Err(i18n::switch_token_cannot_be_empty_print()); } if token.len() > 64 { - return Err("token不能超过64字符(Token cannot exceed 64 characters)".to_string()); + return Err(i18n::switch_token_cannot_exceed_64_print()); } println!("token:{:?}", token); let name = args_config.name; @@ -235,7 +235,7 @@ pub fn read_config_file(config_path: PathBuf) -> Result { } }; if device_id.is_empty() || device_id.len() > 64 { - return Err("设备id不能为空并且长度不能大于64字符(The device id cannot be empty and the length cannot be greater than 64 characters)".to_string()); + return Err(i18n::switch_device_id_is_empty_print()); } println!("device_id:{:?}", device_id); let in_ips = args_config.in_ips; @@ -244,13 +244,13 @@ pub fn read_config_file(config_path: PathBuf) -> Result { let in_ips_c = if let Ok(in_ips_c) = ips_parse(&in_ips) { in_ips_c } else { - return Err("in_ips 参数错误 示例:--in_ip 192.168.10.0/24,10.26.0.3".to_string()); + return Err(i18n::switch_in_ips_example_print()); }; println!("out_ips:{:?}", out_ips); let out_ips_c = if let Ok(out_ips_c) = ips_parse(&out_ips) { out_ips_c } else { - return Err("out_ips 参数错误 示例:--out_ip 192.168.10.0/24,192.168.0.5".to_string()); + return Err(i18n::switch_out_ips_example_print()); }; let server = match { if !args_config.server.is_empty() { @@ -264,11 +264,11 @@ pub fn read_config_file(config_path: PathBuf) -> Result { if let Some(addr) = server.next() { addr } else { - return Err("中继服务器地址错误( Relay server address error)".to_string()); + return Err(i18n::switch_relay_server_address_error()); } } Err(e) => { - return Err(format!("中继服务器地址错误( Relay server address error) :{:?}", e)); + return Err(format!("{}:{:?}",i18n::switch_relay_server_address_error(), e)); } }; println!("中继服务器:{:?}", server); @@ -279,7 +279,7 @@ pub fn read_config_file(config_path: PathBuf) -> Result { }.iter().flat_map(|a| a.to_socket_addrs()).flatten() .collect::>(); if nat_test_server.is_empty() { - return Err("NAT检测服务地址错误(NAT detection service address error)".to_string()); + return Err(i18n::switch_nat_test_server_address_error()); } println!("NAT探测服务器:{:?}", nat_test_server); let base_config = StartConfig { @@ -332,6 +332,7 @@ pub struct ArgsConfig { #[serde(default = "default_false")] pub log: bool, } + #[cfg(windows)] impl ArgsConfig { pub fn new(start_config: StartConfig) -> ArgsConfig { @@ -343,7 +344,7 @@ impl ArgsConfig { }).collect::>(); ArgsConfig { tap: start_config.tap, - version: "1.0.5".to_string(), + version: "1.0.6".to_string(), token: start_config.token.to_string(), name: start_config.name.to_string(), server: start_config.server.to_string(), @@ -357,6 +358,7 @@ impl ArgsConfig { } } } + #[cfg(windows)] fn subnet_mask_to_integer(subnet_mask: u32) -> u8 { let mut mask_bits = subnet_mask; @@ -373,7 +375,7 @@ fn default_false() -> bool { } fn default_version() -> String { - "1.0".to_string() + "1.0.6".to_string() } fn default_str() -> String { @@ -446,7 +448,7 @@ pub fn read_command_port() -> io::Result { if let Some(p) = config.command_port { Ok(p) } else { - Err(io::Error::new(io::ErrorKind::Other, "not fount config")) + Err(io::Error::new(io::ErrorKind::Other, "not found config")) } } diff --git a/switch-desktop/src/i18n.rs b/switch-desktop/src/i18n.rs new file mode 100644 index 0000000..3410ec6 --- /dev/null +++ b/switch-desktop/src/i18n.rs @@ -0,0 +1,211 @@ +#[cfg(target_os = "windows")] +fn get_default_language() -> Option { + use std::process::Command; + use std::str; + + let output = Command::new("powershell") + .arg("-Command") + .arg("[System.Globalization.CultureInfo]::CurrentCulture.Name") + .output() + .ok()?; + + let language_code = str::from_utf8(&output.stdout) + .ok()? + .trim() + .to_string(); + + Some(language_code) +} + +pub fn init() { + #[cfg(target_os = "windows")] + { + if let Some(l) = get_default_language() { + rust_i18n::set_locale(&l); + } + } +} + +pub fn switch_about() -> String { + rust_i18n::t!("switch_about") +} + +pub fn switch_usage() -> String { + rust_i18n::t!("switch_usage") +} + +pub fn switch_start_about() -> String { + rust_i18n::t!("switch_start_about") +} + +pub fn switch_token_help() -> String { + rust_i18n::t!("switch_token_help") +} + +pub fn switch_name_help() -> String { + rust_i18n::t!("switch_name_help") +} + +pub fn switch_device_id_help() -> String { + rust_i18n::t!("switch_device_id_help") +} + +pub fn switch_server_help() -> String { + rust_i18n::t!("switch_server_help") +} + +pub fn switch_nat_test_server_help() -> String { + rust_i18n::t!("switch_nat_test_server_help") +} + +pub fn switch_log_help() -> String { + rust_i18n::t!("switch_log_help") +} + +pub fn switch_tap_help() -> String { + rust_i18n::t!("switch_tap_help") +} + +pub fn switch_in_ip_help() -> String { + rust_i18n::t!("switch_in_ip_help") +} + +pub fn switch_out_ip_help() -> String { + rust_i18n::t!("switch_out_ip_help") +} + +pub fn switch_config_help() -> String { + rust_i18n::t!("switch_config_help") +} + +pub fn switch_stop_about() -> String { + rust_i18n::t!("switch_stop_about") +} + +pub fn switch_route_about() -> String { + rust_i18n::t!("switch_route_about") +} + +pub fn switch_list_about() -> String { + rust_i18n::t!("switch_list_about") +} + +pub fn switch_list_all_help() -> String { + rust_i18n::t!("switch_list_all_help") +} + +pub fn switch_status_about() -> String { + rust_i18n::t!("switch_status_about") +} + +#[cfg(windows)] +pub fn switch_install_about() -> String { + rust_i18n::t!("switch_install_about") +} + +#[cfg(windows)] +pub fn switch_path_help() -> String { + rust_i18n::t!("switch_path_help") +} + +#[cfg(windows)] +pub fn switch_auto_help() -> String { + rust_i18n::t!("switch_auto_help") +} + +#[cfg(windows)] +pub fn switch_uninstall_about() -> String { + rust_i18n::t!("switch_uninstall_about") +} + +#[cfg(windows)] +pub fn switch_config_about() -> String { + rust_i18n::t!("switch_config_about") +} + +#[cfg(windows)] +pub fn switch_use_root_print() -> String { + rust_i18n::t!("switch_use_admin_print") +} + +#[cfg(unix)] +pub fn switch_use_root_print() -> String { + rust_i18n::t!("switch_use_root_print") +} + +#[cfg(windows)] +pub fn switch_service_not_start_print() -> String { + rust_i18n::t!("switch_service_not_start_print") +} + +pub fn switch_start_successfully_print() -> String { + rust_i18n::t!("switch_start_successfully_print") +} + +#[cfg(windows)] +pub fn switch_start_failed_print() -> String { + rust_i18n::t!("switch_start_failed_print") +} + +#[cfg(windows)] +pub fn switch_service_not_stopped_print() -> String { + rust_i18n::t!("switch_service_not_stopped_print") +} + +#[cfg(windows)] +pub fn switch_server_already_installed_print() -> String { + rust_i18n::t!("switch_server_already_installed_print") +} + +pub fn switch_repeated_start_print() -> String { + rust_i18n::t!("switch_repeated_start_print") +} + +pub fn switch_stopped_print() -> String { + rust_i18n::t!("switch_stopped_print") +} + +pub fn switch_token_not_found_print() -> String { + rust_i18n::t!("switch_token_not_found_print") +} + +pub fn switch_token_cannot_be_empty_print() -> String { + rust_i18n::t!("switch_token_cannot_be_empty_print") +} + +pub fn switch_token_cannot_exceed_64_print() -> String { + rust_i18n::t!("switch_token_cannot_exceed_64_print") +} + +pub fn switch_device_id_is_empty_print() -> String { + rust_i18n::t!("switch_device_id_is_empty_print") +} + +pub fn switch_in_ips_example_print() -> String { + rust_i18n::t!("switch_in_ips_example_print") +} + +pub fn switch_out_ips_example_print() -> String { + rust_i18n::t!("switch_out_ips_example_print") +} + +pub fn switch_relay_server_address_error() -> String { + rust_i18n::t!("switch_relay_server_address_error") +} + +pub fn switch_nat_test_server_address_error() -> String { + rust_i18n::t!("switch_nat_test_server_address_error") +} + +pub fn switch_press_any_key_to_exit() -> String { + rust_i18n::t!("switch_press_any_key_to_exit") +} +pub fn switch_virtual_ip() -> String { + rust_i18n::t!("switch_virtual_ip") +} +pub fn switch_virtual_gateway() -> String { + rust_i18n::t!("switch_virtual_gateway") +} +pub fn switch_please_enter_the_command() -> String { + rust_i18n::t!("switch_please_enter_the_command") +} \ No newline at end of file diff --git a/switch-desktop/src/main.rs b/switch-desktop/src/main.rs index 8e3ec59..ec3ccab 100644 --- a/switch-desktop/src/main.rs +++ b/switch-desktop/src/main.rs @@ -11,10 +11,11 @@ mod command; mod config; #[cfg(target_os = "windows")] mod windows; - #[cfg(any(unix))] mod unix; mod console_out; +mod command_args; +mod i18n; #[derive(Parser, Debug)] #[command( @@ -60,7 +61,7 @@ enum Commands { Status, } -#[derive(Parser, Debug,Default)] +#[derive(Parser, Debug, Default)] pub struct StartArgs { /// 不超过64个字符 /// 相同token的设备之间才能通信。 @@ -101,15 +102,15 @@ pub struct StartArgs { /// 配置点对网时使用,--in-ip 192.168.10.0/24,10.26.0.3,表示允许接收网段192.168.10.0/24的数据并转发到10.26.0.3 /// Use when configuring peer-to-peer networks #[arg(long)] - in_ip:Option>, + in_ip: Option>, /// 配置点对网时使用,--out-ip 192.168.10.0/24,192.168.1.10,表示允许目标为192.168.10.0/24的数据从网卡192.168.1.10转发出去 /// Use when configuring peer-to-peer networks #[arg(long)] - out_ip:Option>, + out_ip: Option>, /// 读取配置文件 --config config_file_path /// Read configuration file #[arg(long)] - config:Option, + config: Option, } #[cfg(target_os = "windows")] @@ -134,10 +135,14 @@ pub struct ConfigArgs { auto: bool, } +#[macro_use] +extern crate rust_i18n; +i18n!("locales", fallback = "en"); + #[cfg(windows)] -#[tokio::main] -async fn main() { +fn main() { + i18n::init(); let args: Vec<_> = std::env::args().collect(); if args.len() == 3 && args[1] == windows::SERVICE_FLAG { //以服务的方式启动 @@ -145,13 +150,16 @@ async fn main() { windows::service::start(); return; } else { + if !command_args::check() { + return; + } let args = BaseArgs::parse(); if let Commands::Start(start_args) = &args.command { if start_args.log { let _ = log_init(); } } - windows::main0(args).await; + windows::main0(args); } } @@ -177,27 +185,21 @@ async fn main() { pub fn console_listen(switch: &Switch) { use console::Term; let term = Term::stdout(); - println!("{}", style("启动成功 started").green()); + println!("{}", style(i18n::switch_start_successfully_print()).green()); let current_device = switch.current_device(); - println!( - "当前虚拟ip(virtual ip): {:?}", - style(current_device.virtual_ip()).green() - ); - println!( - "虚拟网关(virtual gateway): {:?}", - style(current_device.virtual_gateway()).green() - ); + println!("{}: {:?}", i18n::switch_virtual_ip(), style(current_device.virtual_ip()).green()); + println!("{}: {:?}", i18n::switch_virtual_gateway(), style(current_device.virtual_gateway()).green()); loop { println!( "{}", - style("Please enter the command (Usage: list,status,exit,help):").color256(102) + style(i18n::switch_please_enter_the_command()).color256(102) ); match term.read_line() { Ok(cmd) => { #[cfg(unix)] if cmd.is_empty() { use libc::{STDIN_FILENO, isatty}; - if !unsafe { isatty(STDIN_FILENO) != 0 }{ + if !unsafe { isatty(STDIN_FILENO) != 0 } { return; } } @@ -252,7 +254,7 @@ fn command(cmd: &str, switch: &Switch) -> Result<(), ()> { return Err(()); } _ => { - println!("command '{}' not fount. ", style(cmd).red()); + println!("command '{}' not found. ", style(cmd).red()); println!("Try to enter: '{}'", style("help").green()); } } diff --git a/switch-desktop/src/windows/mod.rs b/switch-desktop/src/windows/mod.rs index 9b0d523..b463354 100644 --- a/switch-desktop/src/windows/mod.rs +++ b/switch-desktop/src/windows/mod.rs @@ -14,7 +14,7 @@ use windows_service::service_manager::{ServiceManager, ServiceManagerAccess}; use switch::core::{Config, Switch}; -use crate::{BaseArgs, Commands, config}; +use crate::{BaseArgs, Commands, config, i18n}; use crate::command::{command, CommandEnum}; pub mod service; @@ -28,7 +28,7 @@ fn admin_check() -> bool { if !windows_admin_check::is_app_elevated() { println!( "{}", - style("请使用管理员权限运行(Please run with administrator privileges)").red() + style(i18n::switch_use_root_print()).red() ); true } else { @@ -42,7 +42,7 @@ fn not_started() -> bool { if state == ServiceState::Running { return false; } else { - println!("服务未启动") + println!("{}",i18n::switch_service_not_start_print()) } } Err(e) => { @@ -52,7 +52,7 @@ fn not_started() -> bool { return true; } -pub async fn main0(base_args: BaseArgs) { +pub fn main0(base_args: BaseArgs) { match base_args.command { Commands::Start(args) => { if admin_check() { @@ -92,15 +92,15 @@ pub async fn main0(base_args: BaseArgs) { Ok(_) => { //需要检查启动状态 thread::sleep(Duration::from_secs(2)); - println!("{}", style("启动成功(Start successfully)").green()); + println!("{}", style(i18n::switch_start_successfully_print()).green()); } Err(e) => { log::error!("{:?}", e); - println!("{}:{}", style("启动失败").red(),e); + println!("{}:{}", style(i18n::switch_start_failed_print()).red(), e); } } } else { - println!("服务未停止(Service not stopped)"); + println!("{}",i18n::switch_service_not_stopped_print()); } } Err(e) => { @@ -130,18 +130,20 @@ pub async fn main0(base_args: BaseArgs) { } }; if lock.try_lock_exclusive().is_err() { - println!("{}", style("文件被重复打开").red()); + println!("{}", style(i18n::switch_repeated_start_print()).red()); return; } - match Switch::start(config).await { - Ok(switch) => { - crate::console_listen(&switch); + tokio::runtime::Builder::new_multi_thread().enable_all().build().unwrap().block_on(async move { + match Switch::start(config).await { + Ok(switch) => { + crate::console_listen(&switch); + } + Err(e) => { + log::error!("{:?}", e); + println!("启动switch失败:{:?}", e); + } } - Err(e) => { - log::error!("{:?}", e); - println!("启动switch失败:{:?}", e); - } - } + }); lock.unlock().unwrap(); return; } @@ -163,11 +165,11 @@ pub async fn main0(base_args: BaseArgs) { } match stop() { Ok(_) => { - println!("{}", style("停止成功(Stopped successfully)").green()) + println!("{}", style(i18n::switch_stopped_print()).green()) } Err(e) => { log::error!("{:?}", e); - println!("停止失败:{}",e); + println!("停止失败:{}", e); } } pause(); @@ -176,6 +178,10 @@ pub async fn main0(base_args: BaseArgs) { if admin_check() { return; } + if service_state().is_ok() { + println!("{}",i18n::switch_server_already_installed_print()); + return; + } let path: PathBuf = args.path.into(); if !path.exists() { std::fs::create_dir_all(&path).unwrap(); @@ -185,7 +191,7 @@ pub async fn main0(base_args: BaseArgs) { } else { if let Err(e) = install(path, args.auto) { log::error!("{:?}", e); - println!("安装失败:{}",e); + println!("安装失败:{}", e); } else { println!("{}", style("安装成功(Installation succeeded)").green()) } @@ -196,18 +202,24 @@ pub async fn main0(base_args: BaseArgs) { if admin_check() { return; } + if service_state().is_err() { + println!("服务未安装"); + } if let Err(e) = uninstall() { log::error!("{:?}", e); - println!("卸载失败:{}",e); + println!("卸载失败:{}", e); } else { println!("{}", style("卸载成功(Uninstall succeeded)").green()) } pause(); } Commands::Config(args) => { + if service_state().is_err() { + println!("服务未安装"); + } if let Err(e) = change(args.auto) { log::error!("{:?}", e); - println!("配置失败:{}",e); + println!("配置失败:{}", e); } else { println!("{}", style("配置成功(Config succeeded)").green()) } @@ -241,7 +253,7 @@ pub async fn main0(base_args: BaseArgs) { fn pause() { println!( "{}", - style("按任意键退出(Press any key to exit)...").green() + style(i18n::switch_press_any_key_to_exit()).green() ); use console::Term; let term = Term::stdout(); @@ -249,7 +261,7 @@ fn pause() { } fn install(mut path: PathBuf, auto: bool) -> Result<(), Error> { - if !path.is_absolute(){ + if !path.is_absolute() { path = path.canonicalize().unwrap(); } let manager_access = ServiceManagerAccess::CONNECT | ServiceManagerAccess::CREATE_SERVICE; @@ -259,7 +271,7 @@ fn install(mut path: PathBuf, auto: bool) -> Result<(), Error> { std::fs::copy(current_exe_path, service_path.as_path()).unwrap(); if let Err(e) = std::fs::copy("wintun.dll", path.join("wintun.dll").as_path()) { if e.kind() == io::ErrorKind::NotFound { - println!("Not fount 'wintun.dll'. Please put 'wintun.dll' in the current directory"); + println!("'wintun.dll' not found. Please put 'wintun.dll' in the current directory"); std::process::exit(0); } else { panic!("{:?}", e) @@ -318,7 +330,7 @@ fn change(auto: bool) -> Result<(), Error> { PathBuf::from(executable_path) }; let home_path = split.next().unwrap().trim(); - let launch_arguments = vec![OsString::from(SERVICE_FLAG),OsString::from(home_path)]; + let launch_arguments = vec![OsString::from(SERVICE_FLAG), OsString::from(home_path)]; let service_info = ServiceInfo { name: OsString::from(SERVICE_NAME), display_name: config.display_name, diff --git a/switch-desktop/src/windows/service.rs b/switch-desktop/src/windows/service.rs index 4f4568a..de3eb79 100644 --- a/switch-desktop/src/windows/service.rs +++ b/switch-desktop/src/windows/service.rs @@ -96,7 +96,6 @@ fn auto_config_path() -> io::Result { fn save_auto_config(start_config: config::StartConfig) -> io::Result<()> { let mut file = std::fs::File::create(auto_config_path()?)?; - log::error!("auto_config_path()? {:?}",auto_config_path()?); let config = config::ArgsConfig::new(start_config); match serde_yaml::to_string(&config) { Ok(yaml) => {