From 5e48626cb99abdb72701ba592d50b537ab9ae1a9 Mon Sep 17 00:00:00 2001 From: agusti moll Date: Fri, 3 Oct 2025 18:18:10 +0200 Subject: [PATCH] add tld-dns-zone for customizing top-level domain (TLD) zone (#1436) --- easytier/locales/app.yml | 3 + easytier/src/common/config.rs | 2 + easytier/src/easytier-core.rs | 10 ++ .../instance/dns_server/client_instance.rs | 6 +- .../instance/dns_server/server_instance.rs | 7 +- easytier/src/instance/dns_server/tests.rs | 96 ++++++++++++------- easytier/src/instance/instance.rs | 2 +- easytier/src/lib.rs | 2 +- easytier/src/proto/common.proto | 3 + 9 files changed, 91 insertions(+), 40 deletions(-) diff --git a/easytier/locales/app.yml b/easytier/locales/app.yml index 542cad1..b132c3f 100644 --- a/easytier/locales/app.yml +++ b/easytier/locales/app.yml @@ -190,6 +190,9 @@ core_clap: accept_dns: en: "if true, enable magic dns. with magic dns, you can access other nodes with a domain name, e.g.: .et.net. magic dns will modify your system dns settings, enable it carefully." zh-CN: "如果为true,则启用魔法DNS。使用魔法DNS,您可以使用域名访问其他节点,例如:.et.net。魔法DNS将修改您的系统DNS设置,请谨慎启用。" + tld_dns_zone: + en: "specify the top-level domain zone for magic DNS. if not provided, defaults to the value from dns_server module (et.net.). only used when accept_dns is true." + zh-CN: "指定魔法DNS的顶级域名区域。如果未提供,默认使用dns_server模块中的值(et.net.)。仅在accept_dns为true时使用。" private_mode: en: "if true, nodes with different network names or passwords from this network are not allowed to perform handshake or relay through this node." zh-CN: "如果为true,则不允许使用了与本网络不相同的网络名称和密码的节点通过本节点进行握手或中转" diff --git a/easytier/src/common/config.rs b/easytier/src/common/config.rs index b27115f..bbc2ea7 100644 --- a/easytier/src/common/config.rs +++ b/easytier/src/common/config.rs @@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize}; use crate::{ common::stun::StunInfoCollector, + instance::dns_server::DEFAULT_ET_DNS_ZONE, proto::{ acl::Acl, common::{CompressionAlgoPb, PortForwardConfigPb, SocketType}, @@ -50,6 +51,7 @@ pub fn gen_default_flags() -> Flags { multi_thread_count: 2, encryption_algorithm: "aes-gcm".to_string(), disable_sym_hole_punching: false, + tld_dns_zone: DEFAULT_ET_DNS_ZONE.to_string(), } } diff --git a/easytier/src/easytier-core.rs b/easytier/src/easytier-core.rs index 102b55e..41ed6d0 100644 --- a/easytier/src/easytier-core.rs +++ b/easytier/src/easytier-core.rs @@ -507,6 +507,12 @@ struct NetworkOptions { )] accept_dns: Option, + #[arg( + long = "tld-dns-zone", + env = "ET_TLD_DNS_ZONE", + help = t!("core_clap.tld_dns_zone").to_string())] + tld_dns_zone: Option, + #[arg( long, env = "ET_PRIVATE_MODE", @@ -935,6 +941,10 @@ impl NetworkOptions { .enable_relay_foreign_network_kcp .unwrap_or(f.enable_relay_foreign_network_kcp); f.disable_sym_hole_punching = self.disable_sym_hole_punching.unwrap_or(false); + // Configure tld_dns_zone: use provided value if set + if let Some(tld_dns_zone) = &self.tld_dns_zone { + f.tld_dns_zone = tld_dns_zone.clone(); + } cfg.set_flags(f); if !self.exit_nodes.is_empty() { diff --git a/easytier/src/instance/dns_server/client_instance.rs b/easytier/src/instance/dns_server/client_instance.rs index eb172f8..f1d5f35 100644 --- a/easytier/src/instance/dns_server/client_instance.rs +++ b/easytier/src/instance/dns_server/client_instance.rs @@ -17,7 +17,7 @@ use crate::{ tunnel::tcp::TcpTunnelConnector, }; -use super::{DEFAULT_ET_DNS_ZONE, MAGIC_DNS_INSTANCE_ADDR}; +use super::MAGIC_DNS_INSTANCE_ADDR; pub struct MagicDnsClientInstance { rpc_client: StandAloneClient, @@ -68,9 +68,11 @@ impl MagicDnsClientInstance { ipv4_addr: ctx.get_ipv4().map(Into::into), ..Default::default() }); + // Use configured tld_dns_zone (always set by default) + let flags = ctx.config.get_flags(); let req = UpdateDnsRecordRequest { routes, - zone: DEFAULT_ET_DNS_ZONE.to_string(), + zone: flags.tld_dns_zone.clone(), }; tracing::debug!( "MagicDnsClientInstance::update_dns_task: update dns records: {:?}", diff --git a/easytier/src/instance/dns_server/server_instance.rs b/easytier/src/instance/dns_server/server_instance.rs index 9f450c0..f7789df 100644 --- a/easytier/src/instance/dns_server/server_instance.rs +++ b/easytier/src/instance/dns_server/server_instance.rs @@ -20,7 +20,6 @@ use crate::{ instance::dns_server::{ config::{Record, RecordBuilder, RecordType}, server::build_authority, - DEFAULT_ET_DNS_ZONE, }, peers::{peer_manager::PeerManager, NicPacketFilter}, proto::{ @@ -522,9 +521,11 @@ impl MagicDnsServerInstance { peer_mgr .add_nic_packet_process_pipeline(Box::new(data.clone())) .await; - + // Use configured tld_dns_zone or fall back to DEFAULT_ET_DNS_ZONE if empty + let flags = peer_mgr.get_global_ctx().config.get_flags(); + let tld_dns_zone_clone = flags.tld_dns_zone.clone(); let data_clone = data.clone(); - tokio::task::spawn_blocking(move || data_clone.do_system_config(DEFAULT_ET_DNS_ZONE)) + tokio::task::spawn_blocking(move || data_clone.do_system_config(&tld_dns_zone_clone)) .await .context("Failed to configure system")??; diff --git a/easytier/src/instance/dns_server/tests.rs b/easytier/src/instance/dns_server/tests.rs index a5ace89..f7e3a8f 100644 --- a/easytier/src/instance/dns_server/tests.rs +++ b/easytier/src/instance/dns_server/tests.rs @@ -16,7 +16,7 @@ use crate::connector::udp_hole_punch::tests::replace_stun_info_collector; use crate::instance::dns_server::runner::DnsRunner; use crate::instance::dns_server::server_instance::MagicDnsServerInstance; -use crate::instance::dns_server::DEFAULT_ET_DNS_ZONE; +use crate::instance::dns_server::{DEFAULT_ET_DNS_ZONE, MAGIC_DNS_FAKE_IP}; use crate::instance::virtual_nic::NicCtx; use crate::peers::peer_manager::{PeerManager, RouteAlgoType}; @@ -25,9 +25,27 @@ use crate::proto::api::instance::Route; use crate::proto::common::NatType; pub async fn prepare_env(dns_name: &str, tun_ip: Ipv4Inet) -> (Arc, NicCtx) { + prepare_env_with_tld_dns_zone(dns_name, tun_ip, None).await +} + +pub async fn prepare_env_with_tld_dns_zone( + dns_name: &str, + tun_ip: Ipv4Inet, + tld_dns_zone: Option<&str>, +) -> (Arc, NicCtx) { let ctx = get_mock_global_ctx(); ctx.set_hostname(dns_name.to_owned()); ctx.set_ipv4(Some(tun_ip)); + + if tld_dns_zone.is_some() { + let mut flags = ctx.config.get_flags(); + flags.accept_dns = true; // Enable DNS + if let Some(zone) = tld_dns_zone { + flags.tld_dns_zone = zone.to_string(); + } + ctx.config.set_flags(flags); + } + let (s, r) = create_packet_recv_chan(); let peer_mgr = Arc::new(PeerManager::new(RouteAlgoType::Ospf, ctx, s)); peer_mgr.run().await.unwrap(); @@ -113,41 +131,53 @@ async fn test_magic_dns_server_instance() { #[tokio::test] async fn test_magic_dns_runner() { - let tun_ip = Ipv4Inet::from_str("10.144.144.10/24").unwrap(); - let (peer_mgr, virtual_nic) = prepare_env("test1", tun_ip).await; - let tun_name = virtual_nic.ifname().await.unwrap(); - let fake_ip = Ipv4Addr::from_str("100.100.100.101").unwrap(); - let mut dns_runner = DnsRunner::new(peer_mgr, Some(tun_name), tun_ip, fake_ip); + // Test first runner with default DNS settings + { + let tun_ip = Ipv4Inet::from_str("10.144.144.10/24").unwrap(); + let (peer_mgr, virtual_nic) = prepare_env("test1", tun_ip).await; + let tun_name = virtual_nic.ifname().await.unwrap(); + let fake_ip = Ipv4Addr::from_str(MAGIC_DNS_FAKE_IP).unwrap(); + let mut dns_runner = DnsRunner::new(peer_mgr, Some(tun_name), tun_ip, fake_ip); - let cancel_token = CancellationToken::new(); - let cancel_token_clone = cancel_token.clone(); - let t = tokio::spawn(async move { - dns_runner.run(cancel_token_clone).await; - }); - tokio::time::sleep(Duration::from_secs(3)).await; - check_dns_record(&fake_ip, "test1.et.net", "10.144.144.10").await; + let cancel_token = CancellationToken::new(); + let cancel_token_clone = cancel_token.clone(); + let t = tokio::spawn(async move { + dns_runner.run(cancel_token_clone).await; + }); + tokio::time::sleep(Duration::from_secs(3)).await; - // add a new dns runner - let tun_ip2 = Ipv4Inet::from_str("10.144.144.20/24").unwrap(); - let (peer_mgr, virtual_nic) = prepare_env("test2", tun_ip2).await; - let tun_name2 = virtual_nic.ifname().await.unwrap(); - let mut dns_runner2 = DnsRunner::new(peer_mgr, Some(tun_name2), tun_ip2, fake_ip); - let cancel_token2 = CancellationToken::new(); - let cancel_token2_clone = cancel_token2.clone(); - let t2 = tokio::spawn(async move { - dns_runner2.run(cancel_token2_clone).await; - }); - tokio::time::sleep(Duration::from_secs(3)).await; - check_dns_record(&fake_ip, "test1.et.net", "10.144.144.10").await; - check_dns_record(&fake_ip, "test2.et.net", "10.144.144.20").await; + // Test default settings: query should resolve test1.et.net to tunnel IP via default fake IP + check_dns_record(&fake_ip, "test1.et.net", "10.144.144.10").await; - // stop runner 1, runner 2 will take over the dns server - cancel_token.cancel(); - t.await.unwrap(); + cancel_token.cancel(); + t.await.unwrap(); - tokio::time::sleep(Duration::from_secs(3)).await; - check_dns_record(&fake_ip, "test2.et.net", "10.144.144.20").await; + // Wait a bit for cleanup + tokio::time::sleep(Duration::from_secs(1)).await; + } - cancel_token2.cancel(); - t2.await.unwrap(); + // Test second runner with different TLD zone + { + let tun_ip = Ipv4Inet::from_str("10.144.144.20/24").unwrap(); + // NOTE: Using same fake IP to avoid system DNS configuration conflicts + let custom_tld_zone = "custom.local."; // Different TLD zone is safer + let (peer_mgr, virtual_nic) = + prepare_env_with_tld_dns_zone("test2", tun_ip, Some(custom_tld_zone)).await; + let tun_name = virtual_nic.ifname().await.unwrap(); + let fake_ip = Ipv4Addr::from_str(MAGIC_DNS_FAKE_IP).unwrap(); + let mut dns_runner = DnsRunner::new(peer_mgr, Some(tun_name), tun_ip, fake_ip); + + let cancel_token = CancellationToken::new(); + let cancel_token_clone = cancel_token.clone(); + let t = tokio::spawn(async move { + dns_runner.run(cancel_token_clone).await; + }); + tokio::time::sleep(Duration::from_secs(3)).await; + + // Test with same fake IP but different TLD zone + check_dns_record(&fake_ip, "test2.custom.local", "10.144.144.20").await; + + cancel_token.cancel(); + t.await.unwrap(); + } } diff --git a/easytier/src/instance/instance.rs b/easytier/src/instance/instance.rs index 5fab30b..dc7c8be 100644 --- a/easytier/src/instance/instance.rs +++ b/easytier/src/instance/instance.rs @@ -136,7 +136,7 @@ struct MagicDnsContainer { } // nic container will be cleared when dhcp ip changed -pub(crate) struct NicCtxContainer { +pub struct NicCtxContainer { nic_ctx: Option>, magic_dns: Option, } diff --git a/easytier/src/lib.rs b/easytier/src/lib.rs index c390977..7d8d8e7 100644 --- a/easytier/src/lib.rs +++ b/easytier/src/lib.rs @@ -7,7 +7,7 @@ use clap_complete::Generator; mod arch; mod gateway; -mod instance; +pub mod instance; mod peer_center; mod vpn_portal; diff --git a/easytier/src/proto/common.proto b/easytier/src/proto/common.proto index 1ead7a2..5915b89 100644 --- a/easytier/src/proto/common.proto +++ b/easytier/src/proto/common.proto @@ -55,6 +55,9 @@ message FlagsInConfig { // disable symmetric nat hole punching, treat symmetric as cone when enabled bool disable_sym_hole_punching = 30; + + // tld dns zone for magic dns + string tld_dns_zone = 31; } message RpcDescriptor {