add tld-dns-zone for customizing top-level domain (TLD) zone (#1436)

This commit is contained in:
agusti moll
2025-10-03 18:18:10 +02:00
committed by GitHub
parent ad7dc3a129
commit 5e48626cb9
9 changed files with 91 additions and 40 deletions

View File

@@ -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(),
}
}

View File

@@ -507,6 +507,12 @@ struct NetworkOptions {
)]
accept_dns: Option<bool>,
#[arg(
long = "tld-dns-zone",
env = "ET_TLD_DNS_ZONE",
help = t!("core_clap.tld_dns_zone").to_string())]
tld_dns_zone: Option<String>,
#[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() {

View File

@@ -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<TcpTunnelConnector>,
@@ -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: {:?}",

View File

@@ -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")??;

View File

@@ -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<PeerManager>, 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<PeerManager>, 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();
}
}

View File

@@ -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<Box<dyn Any + 'static + Send>>,
magic_dns: Option<MagicDnsContainer>,
}

View File

@@ -7,7 +7,7 @@ use clap_complete::Generator;
mod arch;
mod gateway;
mod instance;
pub mod instance;
mod peer_center;
mod vpn_portal;

View File

@@ -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 {