diff --git a/easytier/src/arch/windows.rs b/easytier/src/arch/windows.rs index 1c7799c..4dae5c8 100644 --- a/easytier/src/arch/windows.rs +++ b/easytier/src/arch/windows.rs @@ -82,7 +82,7 @@ pub fn find_interface_index(iface_name: &str) -> io::Result { tracing::error!("Failed to find interface index for {}", iface_name); Err(io::Error::new( io::ErrorKind::NotFound, - format!("{}", iface_name), + iface_name.to_string(), )) } diff --git a/easytier/src/common/config.rs b/easytier/src/common/config.rs index bbd4b40..364d672 100644 --- a/easytier/src/common/config.rs +++ b/easytier/src/common/config.rs @@ -199,6 +199,9 @@ pub trait ConfigLoader: Send + Sync { fn get_udp_whitelist(&self) -> Vec; fn set_udp_whitelist(&self, whitelist: Vec); + fn get_stun_servers(&self) -> Vec; + fn set_stun_servers(&self, servers: Vec); + fn dump(&self) -> String; } @@ -407,6 +410,7 @@ struct Config { tcp_whitelist: Option>, udp_whitelist: Option>, + stun_servers: Option>, } #[derive(Debug, Clone)] @@ -786,6 +790,19 @@ impl ConfigLoader for TomlConfigLoader { self.config.lock().unwrap().udp_whitelist = Some(whitelist); } + fn get_stun_servers(&self) -> Vec { + self.config + .lock() + .unwrap() + .stun_servers + .clone() + .unwrap_or_default() + } + + fn set_stun_servers(&self, servers: Vec) { + self.config.lock().unwrap().stun_servers = Some(servers); + } + fn dump(&self) -> String { let default_flags_json = serde_json::to_string(&gen_default_flags()).unwrap(); let default_flags_hashmap = @@ -816,6 +833,39 @@ impl ConfigLoader for TomlConfigLoader { pub mod tests { use super::*; + #[test] + fn test_stun_servers_config() { + let config = TomlConfigLoader::default(); + let stun_servers = config.get_stun_servers(); + assert!(stun_servers.is_empty()); + + // Test setting custom stun servers + let custom_servers = vec!["txt:stun.easytier.cn".to_string()]; + config.set_stun_servers(custom_servers.clone()); + + let retrieved_servers = config.get_stun_servers(); + assert_eq!(retrieved_servers, custom_servers); + } + + #[test] + fn test_stun_servers_toml_parsing() { + let config_str = r#" +instance_name = "test" +stun_servers = [ + "stun.l.google.com:19302", + "stun1.l.google.com:19302", + "txt:stun.easytier.cn" +]"#; + + let config = TomlConfigLoader::new_from_str(config_str).unwrap(); + let stun_servers = config.get_stun_servers(); + + assert_eq!(stun_servers.len(), 3); + assert_eq!(stun_servers[0], "stun.l.google.com:19302"); + assert_eq!(stun_servers[1], "stun1.l.google.com:19302"); + assert_eq!(stun_servers[2], "txt:stun.easytier.cn"); + } + #[tokio::test] async fn full_example_test() { let config_str = r#" diff --git a/easytier/src/common/global_ctx.rs b/easytier/src/common/global_ctx.rs index 2c5e2cb..f91d42b 100644 --- a/easytier/src/common/global_ctx.rs +++ b/easytier/src/common/global_ctx.rs @@ -112,7 +112,12 @@ impl GlobalCtx { let (event_bus, _) = tokio::sync::broadcast::channel(8); - let stun_info_collection = Arc::new(StunInfoCollector::new_with_default_servers()); + let stun_servers = config_fs.get_stun_servers(); + let stun_info_collection = Arc::new(if stun_servers.is_empty() { + StunInfoCollector::new_with_default_servers() + } else { + StunInfoCollector::new(stun_servers) + }); let enable_exit_node = config_fs.get_flags().enable_exit_node || cfg!(target_env = "ohos"); let proxy_forward_by_system = config_fs.get_flags().proxy_forward_by_system; diff --git a/easytier/src/common/ifcfg/win/types.rs b/easytier/src/common/ifcfg/win/types.rs index ee030c4..dae0be9 100644 --- a/easytier/src/common/ifcfg/win/types.rs +++ b/easytier/src/common/ifcfg/win/types.rs @@ -43,10 +43,9 @@ pub fn convert_ipv4addr_to_inaddr(ip: &Ipv4Addr) -> winapi::shared::inaddr::in_a pub fn convert_ipv6addr_to_inaddr(ip: &Ipv6Addr) -> winapi::shared::in6addr::in6_addr { let mut winaddr = winapi::shared::in6addr::in6_addr::default(); let octets = ip.octets(); - for i in 0..octets.len() { - unsafe { winaddr.u.Byte_mut()[i] = octets[i] }; + for (i, &octet) in octets.iter().enumerate() { + unsafe { winaddr.u.Byte_mut()[i] = octet }; } - winaddr } diff --git a/easytier/src/common/ifcfg/windows.rs b/easytier/src/common/ifcfg/windows.rs index 3d5d27d..078219e 100644 --- a/easytier/src/common/ifcfg/windows.rs +++ b/easytier/src/common/ifcfg/windows.rs @@ -35,7 +35,7 @@ fn format_win_error(error: u32) -> String { null_mut(), error, 0, - buffer.as_mut_ptr() as *mut u16, + buffer.as_mut_ptr(), size, null_mut(), ); @@ -43,9 +43,7 @@ fn format_win_error(error: u32) -> String { let str_end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); format!( "{} (code: {})", - String::from_utf16_lossy(&buffer[..str_end]) - .trim() - .to_string(), + String::from_utf16_lossy(&buffer[..str_end]).trim(), error ) } diff --git a/easytier/src/common/stun.rs b/easytier/src/common/stun.rs index e9763b5..ec12ad3 100644 --- a/easytier/src/common/stun.rs +++ b/easytier/src/common/stun.rs @@ -736,8 +736,8 @@ impl StunInfoCollector { } pub fn get_default_servers() -> Vec { - // NOTICE: we may need to choose stun stun server based on geo location - // stun server cross nation may return a external ip address with high latency and loss rate + // NOTICE: we may need to choose stun server based on geolocation + // stun server cross nation may return an external ip address with high latency and loss rate [ "txt:stun.easytier.cn", "stun.miwifi.com", diff --git a/easytier/src/easytier-cli.rs b/easytier/src/easytier-cli.rs index 286d8a6..471574d 100644 --- a/easytier/src/easytier-cli.rs +++ b/easytier/src/easytier-cli.rs @@ -2060,12 +2060,12 @@ mod win_service_manager { let service = self .service_manager .create_service(&service_info, ServiceAccess::ALL_ACCESS) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + .map_err(io::Error::other)?; if let Some(s) = description { service .set_description(s.clone()) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + .map_err(io::Error::other)?; } if let Some(work_dir) = ctx.working_directory { @@ -2079,33 +2079,27 @@ mod win_service_manager { let service = self .service_manager .open_service(ctx.label.to_qualified_name(), ServiceAccess::ALL_ACCESS) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + .map_err(io::Error::other)?; - service - .delete() - .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) + service.delete().map_err(io::Error::other) } fn start(&self, ctx: ServiceStartCtx) -> io::Result<()> { let service = self .service_manager .open_service(ctx.label.to_qualified_name(), ServiceAccess::ALL_ACCESS) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + .map_err(io::Error::other)?; - service - .start(&[] as &[&OsStr]) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) + service.start(&[] as &[&OsStr]).map_err(io::Error::other) } fn stop(&self, ctx: ServiceStopCtx) -> io::Result<()> { let service = self .service_manager .open_service(ctx.label.to_qualified_name(), ServiceAccess::ALL_ACCESS) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + .map_err(io::Error::other)?; - _ = service - .stop() - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + _ = service.stop().map_err(io::Error::other)?; Ok(()) } @@ -2117,10 +2111,7 @@ mod win_service_manager { fn set_level(&mut self, level: ServiceLevel) -> io::Result<()> { match level { ServiceLevel::System => Ok(()), - _ => Err(io::Error::new( - io::ErrorKind::Other, - "Unsupported service level", - )), + _ => Err(io::Error::other("Unsupported service level")), } } @@ -2136,13 +2127,11 @@ mod win_service_manager { return Ok(ServiceStatus::NotInstalled); } } - return Err(io::Error::new(io::ErrorKind::Other, e)); + return Err(io::Error::other(e)); } }; - let status = service - .query_status() - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + let status = service.query_status().map_err(io::Error::other)?; match status.current_state { windows_service::service::ServiceState::Stopped => Ok(ServiceStatus::Stopped(None)), diff --git a/easytier/src/easytier-core.rs b/easytier/src/easytier-core.rs index 3b4f982..f8f6760 100644 --- a/easytier/src/easytier-core.rs +++ b/easytier/src/easytier-core.rs @@ -555,6 +555,15 @@ struct NetworkOptions { default_missing_value = "true" )] enable_relay_foreign_network_kcp: Option, + + #[arg( + long, + env = "ET_STUN_SERVERS", + value_delimiter = ',', + help = t!("core_clap.stun_servers").to_string(), + num_args = 0.. + )] + stun_servers: Option>, } #[derive(Parser, Debug)] @@ -924,6 +933,10 @@ impl NetworkOptions { old_udp_whitelist.extend(self.udp_whitelist.clone()); cfg.set_udp_whitelist(old_udp_whitelist); + if let Some(stun_servers) = &self.stun_servers { + cfg.set_stun_servers(stun_servers.clone()); + } + Ok(()) } } diff --git a/easytier/src/instance/dns_server/system_config/windows.rs b/easytier/src/instance/dns_server/system_config/windows.rs index f626cae..94f014d 100644 --- a/easytier/src/instance/dns_server/system_config/windows.rs +++ b/easytier/src/instance/dns_server/system_config/windows.rs @@ -39,7 +39,7 @@ impl InterfaceControl { if matches!(e.kind(), io::ErrorKind::NotFound) { Ok(()) // 忽略不存在的值 } else { - Err(e.into()) + Err(e) } } } @@ -106,10 +106,7 @@ impl InterfaceControl { .output() .expect("failed to execute process"); if !output.status.success() { - return Err(io::Error::new( - io::ErrorKind::Other, - "Failed to flush DNS cache", - )); + return Err(io::Error::other("Failed to flush DNS cache")); } Ok(()) } @@ -122,10 +119,7 @@ impl InterfaceControl { .output() .expect("failed to execute process"); if !output.status.success() { - return Err(io::Error::new( - io::ErrorKind::Other, - "Failed to register DNS", - )); + return Err(io::Error::other("Failed to register DNS")); } Ok(()) } diff --git a/easytier/src/instance/virtual_nic.rs b/easytier/src/instance/virtual_nic.rs index 44b32eb..cb4d94f 100644 --- a/easytier/src/instance/virtual_nic.rs +++ b/easytier/src/instance/virtual_nic.rs @@ -399,7 +399,7 @@ impl VirtualNic { Err(e) => { println!("Failed to add Easytier to firewall allowlist, Subnet proxy and KCP proxy may not work properly. error: {}", e); println!("You can add firewall rules manually, or use --use-smoltcp to run with user-space TCP/IP stack."); - println!(""); + println!(); } } @@ -409,7 +409,7 @@ impl VirtualNic { } if !dev_name.is_empty() { - config.tun_name(format!("{}", dev_name)); + config.tun_name(&dev_name); } else { use rand::distributions::Distribution as _; let c = crate::arch::windows::interface_count()?; diff --git a/easytier/src/utils.rs b/easytier/src/utils.rs index abb878a..b623c32 100644 --- a/easytier/src/utils.rs +++ b/easytier/src/utils.rs @@ -117,7 +117,7 @@ pub fn utf8_or_gbk_to_string(s: &[u8]) -> String { utf8_str } else { // 如果解码失败,则尝试使用GBK解码 - if let Ok(gbk_str) = GBK.decode(&s, DecoderTrap::Strict) { + if let Ok(gbk_str) = GBK.decode(s, DecoderTrap::Strict) { gbk_str } else { String::from_utf8_lossy(s).to_string()