From b87a05b45793fe33c4e968b67dcf3322069b7e21 Mon Sep 17 00:00:00 2001 From: fanyang Date: Tue, 2 Sep 2025 21:46:37 +0800 Subject: [PATCH] refactor: update custom STUN server settings (#1310) * refactor: update global context STUN server initialization Modified global context initialization to use a single StunInfoCollector instance with properly configured IPv4 and IPv6 servers instead of creating separate instances. feat: add IPv6 STUN server configuration support Added interface methods and config struct fields to support both IPv4 and IPv6 STUN server configuration. Modified getter and setter methods to handle Option> type for both server types. feat: enhance StunInfoCollector with IPv6 support Updated StunInfoCollector to support both IPv4 and IPv6 STUN servers. Added new constructor that accepts both server types and methods to set them independently. feat: add CLI argument for IPv6 STUN servers Added command line argument support for configuring IPv6 STUN servers. Updated configuration setup to handle both IPv4 and IPv6 STUN server settings. docs: add localization for STUN server configuration Added English and Chinese localization strings for the new STUN server configuration options, including both IPv4 and IPv6 variants. --- easytier/locales/app.yml | 6 +++++ easytier/src/common/config.rs | 39 ++++++++++++++++------------ easytier/src/common/global_ctx.rs | 23 +++++++++++----- easytier/src/common/ifcfg/darwin.rs | 7 +---- easytier/src/common/network.rs | 8 ++---- easytier/src/common/stun.rs | 16 +++++++++--- easytier/src/easytier-core.rs | 29 +++++++++++++++++++-- easytier/src/gateway/kcp_proxy.rs | 2 +- easytier/src/gateway/quic_proxy.rs | 8 +++--- easytier/src/instance/virtual_nic.rs | 7 ++--- 10 files changed, 95 insertions(+), 50 deletions(-) diff --git a/easytier/locales/app.yml b/easytier/locales/app.yml index e7817ef..9a4c60c 100644 --- a/easytier/locales/app.yml +++ b/easytier/locales/app.yml @@ -208,6 +208,12 @@ core_clap: enable_relay_foreign_network_kcp: en: "if true, allow relay kcp packets from foreign network. default is false (not forward foreign network kcp packets)" zh-CN: "如果为true,则作为共享节点时也可以转发其他网络的 KCP 数据包。默认值为false(不转发)" + stun_servers: + en: "Override default STUN servers; If configured but empty, STUN servers are not used" + zh-CN: "覆盖内置的默认 STUN server 列表;如果设置了但是为空,则不使用 STUN servers;如果没设置,则使用默认 STUN server 列表" + stun_servers_v6: + en: "Override default STUN servers, IPv6; If configured but empty, IPv6 STUN servers are not used" + zh-CN: "覆盖内置的默认 IPv6 STUN server 列表;如果设置了但是为空,则不使用 IPv6 STUN servers;如果没设置,则使用默认 IPv6 STUN server 列表" core_app: panic_backtrace_save: diff --git a/easytier/src/common/config.rs b/easytier/src/common/config.rs index 2382c28..0e20548 100644 --- a/easytier/src/common/config.rs +++ b/easytier/src/common/config.rs @@ -200,8 +200,11 @@ 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 get_stun_servers(&self) -> Option>; + fn set_stun_servers(&self, servers: Option>); + + fn get_stun_servers_v6(&self) -> Option>; + fn set_stun_servers_v6(&self, servers: Option>); fn dump(&self) -> String; } @@ -374,7 +377,7 @@ impl From for PortForwardConfigPb { } } -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] struct Config { netns: Option, hostname: Option, @@ -412,6 +415,7 @@ struct Config { tcp_whitelist: Option>, udp_whitelist: Option>, stun_servers: Option>, + stun_servers_v6: Option>, } #[derive(Debug, Clone)] @@ -791,17 +795,20 @@ 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 get_stun_servers(&self) -> Option> { + self.config.lock().unwrap().stun_servers.clone() } - fn set_stun_servers(&self, servers: Vec) { - self.config.lock().unwrap().stun_servers = Some(servers); + fn set_stun_servers(&self, servers: Option>) { + self.config.lock().unwrap().stun_servers = servers; + } + + fn get_stun_servers_v6(&self) -> Option> { + self.config.lock().unwrap().stun_servers_v6.clone() + } + + fn set_stun_servers_v6(&self, servers: Option>) { + self.config.lock().unwrap().stun_servers_v6 = servers; } fn dump(&self) -> String { @@ -838,14 +845,14 @@ pub mod tests { fn test_stun_servers_config() { let config = TomlConfigLoader::default(); let stun_servers = config.get_stun_servers(); - assert!(stun_servers.is_empty()); + assert!(stun_servers.is_none()); // Test setting custom stun servers let custom_servers = vec!["txt:stun.easytier.cn".to_string()]; - config.set_stun_servers(custom_servers.clone()); + config.set_stun_servers(Some(custom_servers.clone())); let retrieved_servers = config.get_stun_servers(); - assert_eq!(retrieved_servers, custom_servers); + assert_eq!(retrieved_servers.unwrap(), custom_servers); } #[test] @@ -859,7 +866,7 @@ stun_servers = [ ]"#; let config = TomlConfigLoader::new_from_str(config_str).unwrap(); - let stun_servers = config.get_stun_servers(); + let stun_servers = config.get_stun_servers().unwrap(); assert_eq!(stun_servers.len(), 3); assert_eq!(stun_servers[0], "stun.l.google.com:19302"); diff --git a/easytier/src/common/global_ctx.rs b/easytier/src/common/global_ctx.rs index 19539ae..a6f0b97 100644 --- a/easytier/src/common/global_ctx.rs +++ b/easytier/src/common/global_ctx.rs @@ -114,12 +114,21 @@ impl GlobalCtx { let (event_bus, _) = tokio::sync::broadcast::channel(8); - let stun_servers = config_fs.get_stun_servers(); - let stun_info_collection = Arc::new(if stun_servers.is_empty() { - StunInfoCollector::new_with_default_servers() + let stun_info_collector = StunInfoCollector::new_with_default_servers(); + + if let Some(stun_servers) = config_fs.get_stun_servers() { + stun_info_collector.set_stun_servers(stun_servers); } else { - StunInfoCollector::new(stun_servers) - }); + stun_info_collector.set_stun_servers(Vec::new()); + } + + if let Some(stun_servers) = config_fs.get_stun_servers_v6() { + stun_info_collector.set_stun_servers_v6(stun_servers); + } else { + stun_info_collector.set_stun_servers_v6(Vec::new()); + } + + let stun_info_collector = Arc::new(stun_info_collector); 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; @@ -145,12 +154,12 @@ impl GlobalCtx { ip_collector: Mutex::new(Some(Arc::new(IPCollector::new( net_ns, - stun_info_collection.clone(), + stun_info_collector.clone(), )))), hostname: Mutex::new(hostname), - stun_info_collection: Mutex::new(stun_info_collection), + stun_info_collection: Mutex::new(stun_info_collector), running_listeners: Mutex::new(Vec::new()), diff --git a/easytier/src/common/ifcfg/darwin.rs b/easytier/src/common/ifcfg/darwin.rs index 3c75153..4394005 100644 --- a/easytier/src/common/ifcfg/darwin.rs +++ b/easytier/src/common/ifcfg/darwin.rs @@ -71,12 +71,7 @@ impl IfConfiguerTrait for MacIfConfiger { run_shell_cmd(format!("ifconfig {} inet delete", name).as_str()).await } else { run_shell_cmd( - format!( - "ifconfig {} inet {} delete", - name, - ip.unwrap().address().to_string() - ) - .as_str(), + format!("ifconfig {} inet {} delete", name, ip.unwrap().address()).as_str(), ) .await } diff --git a/easytier/src/common/network.rs b/easytier/src/common/network.rs index 73dd97c..e66e788 100644 --- a/easytier/src/common/network.rs +++ b/easytier/src/common/network.rs @@ -65,7 +65,7 @@ impl InterfaceFilter { async fn is_interface_physical(&self) -> bool { let interface_name = &self.iface.name; let output = tokio::process::Command::new("networksetup") - .args(&["-listallhardwareports"]) + .args(["-listallhardwareports"]) .output() .await .expect("Failed to execute command"); @@ -79,11 +79,7 @@ impl InterfaceFilter { if line.contains("Device:") && line.contains(interface_name) { let next_line = lines[i + 1]; - if next_line.contains("Virtual Interface") { - return false; - } else { - return true; - } + return !next_line.contains("Virtual Interface"); } } diff --git a/easytier/src/common/stun.rs b/easytier/src/common/stun.rs index ec12ad3..f586ed4 100644 --- a/easytier/src/common/stun.rs +++ b/easytier/src/common/stun.rs @@ -718,10 +718,10 @@ impl StunInfoCollectorTrait for StunInfoCollector { } impl StunInfoCollector { - pub fn new(stun_servers: Vec) -> Self { + pub fn new(stun_servers: Vec, stun_servers_v6: Vec) -> Self { Self { stun_servers: Arc::new(RwLock::new(stun_servers)), - stun_servers_v6: Arc::new(RwLock::new(Self::get_default_servers_v6())), + stun_servers_v6: Arc::new(RwLock::new(stun_servers_v6)), udp_nat_test_result: Arc::new(RwLock::new(None)), public_ipv6: Arc::new(AtomicCell::new(None)), nat_test_result_time: Arc::new(AtomicCell::new(Local::now())), @@ -732,7 +732,17 @@ impl StunInfoCollector { } pub fn new_with_default_servers() -> Self { - Self::new(Self::get_default_servers()) + Self::new(Self::get_default_servers(), Self::get_default_servers_v6()) + } + + pub fn set_stun_servers(&self, stun_servers: Vec) { + let mut g = self.stun_servers.write().unwrap(); + *g = stun_servers; + } + + pub fn set_stun_servers_v6(&self, stun_servers_v6: Vec) { + let mut g = self.stun_servers_v6.write().unwrap(); + *g = stun_servers_v6; } pub fn get_default_servers() -> Vec { diff --git a/easytier/src/easytier-core.rs b/easytier/src/easytier-core.rs index c7616f1..1f70387 100644 --- a/easytier/src/easytier-core.rs +++ b/easytier/src/easytier-core.rs @@ -25,7 +25,7 @@ use easytier::{ constants::EASYTIER_VERSION, global_ctx::GlobalCtx, set_default_machine_id, - stun::MockStunInfoCollector, + stun::{MockStunInfoCollector, StunInfoCollector}, }, connector::create_connector_by_url, instance_manager::NetworkInstanceManager, @@ -572,6 +572,15 @@ struct NetworkOptions { num_args = 0.. )] stun_servers: Option>, + + #[arg( + long, + env = "ET_STUN_SERVERS_V6", + value_delimiter = ',', + help = t!("core_clap.stun_servers_v6").to_string(), + num_args = 0.. + )] + stun_servers_v6: Option>, } #[derive(Parser, Debug)] @@ -943,7 +952,23 @@ impl NetworkOptions { cfg.set_udp_whitelist(old_udp_whitelist); if let Some(stun_servers) = &self.stun_servers { - cfg.set_stun_servers(stun_servers.clone()); + if stun_servers.is_empty() { + cfg.set_stun_servers(None); + } else { + cfg.set_stun_servers(Some(stun_servers.clone())); + } + } else { + cfg.set_stun_servers(Some(StunInfoCollector::get_default_servers())); + } + + if let Some(stun_servers) = &self.stun_servers_v6 { + if stun_servers.is_empty() { + cfg.set_stun_servers_v6(None); + } else { + cfg.set_stun_servers_v6(Some(stun_servers.clone())); + } + } else { + cfg.set_stun_servers_v6(Some(StunInfoCollector::get_default_servers_v6())); } Ok(()) diff --git a/easytier/src/gateway/kcp_proxy.rs b/easytier/src/gateway/kcp_proxy.rs index aeff9d5..9c2f8ca 100644 --- a/easytier/src/gateway/kcp_proxy.rs +++ b/easytier/src/gateway/kcp_proxy.rs @@ -457,7 +457,7 @@ impl KcpProxyDst { global_ctx: ArcGlobalCtx, proxy_entries: Arc>, cidr_set: Arc, - route: Arc<(dyn crate::peers::route_trait::Route + Send + Sync + 'static)>, + route: Arc, ) -> Result<()> { let mut conn_data = kcp_stream.conn_data().clone(); let parsed_conn_data = KcpConnData::decode(&mut conn_data) diff --git a/easytier/src/gateway/quic_proxy.rs b/easytier/src/gateway/quic_proxy.rs index ce95f91..8cd6c60 100644 --- a/easytier/src/gateway/quic_proxy.rs +++ b/easytier/src/gateway/quic_proxy.rs @@ -252,13 +252,13 @@ pub struct QUICProxyDst { endpoint: Arc, proxy_entries: Arc>, tasks: Arc>>, - route: Arc<(dyn crate::peers::route_trait::Route + Send + Sync + 'static)>, + route: Arc, } impl QUICProxyDst { pub fn new( global_ctx: ArcGlobalCtx, - route: Arc<(dyn crate::peers::route_trait::Route + Send + Sync + 'static)>, + route: Arc, ) -> Result { let _g = global_ctx.net_ns.guard(); let (endpoint, _) = make_server_endpoint("0.0.0.0:0".parse().unwrap()) @@ -324,7 +324,7 @@ impl QUICProxyDst { ctx: Arc, cidr_set: Arc, proxy_entries: Arc>, - route: Arc<(dyn crate::peers::route_trait::Route + Send + Sync + 'static)>, + route: Arc, ) { let remote_addr = conn.remote_address(); defer!( @@ -368,7 +368,7 @@ impl QUICProxyDst { cidr_set: Arc, proxy_entry_key: SocketAddr, proxy_entries: Arc>, - route: Arc<(dyn crate::peers::route_trait::Route + Send + Sync + 'static)>, + route: Arc, ) -> Result<(QUICStream, TcpStream, ProxyAclHandler)> { let conn = incoming.await.with_context(|| "accept failed")?; let addr = conn.remote_address(); diff --git a/easytier/src/instance/virtual_nic.rs b/easytier/src/instance/virtual_nic.rs index 2aa3fd8..84bdfd0 100644 --- a/easytier/src/instance/virtual_nic.rs +++ b/easytier/src/instance/virtual_nic.rs @@ -127,10 +127,7 @@ impl PacketProtocol { match self { PacketProtocol::IPv4 => Ok(libc::PF_INET as u16), PacketProtocol::IPv6 => Ok(libc::PF_INET6 as u16), - PacketProtocol::Other(_) => Err(io::Error::new( - io::ErrorKind::Other, - "neither an IPv4 nor IPv6 packet", - )), + PacketProtocol::Other(_) => Err(io::Error::other("neither an IPv4 nor IPv6 packet")), } } @@ -904,7 +901,7 @@ impl NicCtx { // remove the 10.0.0.0/24 route (which is added by rust-tun by default) let _ = nic .ifcfg - .remove_ipv4_route(&nic.ifname(), "10.0.0.0".parse().unwrap(), 24) + .remove_ipv4_route(nic.ifname(), "10.0.0.0".parse().unwrap(), 24) .await; }