From d9d211c5a4a84d220b6e43271d4d9f878df43aa6 Mon Sep 17 00:00:00 2001 From: Luna Yao <40349250+ZnqbuZ@users.noreply.github.com> Date: Sun, 29 Mar 2026 11:43:20 +0200 Subject: [PATCH] remove old magic-dns --- easytier/src/common/config.rs | 3 +- easytier/src/core.rs | 4 - easytier/src/dns/node.rs | 1 - .../instance/dns_server/client_instance.rs | 111 ---- easytier/src/instance/dns_server/config.rs | 193 ------ easytier/src/instance/dns_server/mod.rs | 19 - easytier/src/instance/dns_server/runner.rs | 93 --- easytier/src/instance/dns_server/server.rs | 338 ---------- .../instance/dns_server/server_instance.rs | 611 ------------------ easytier/src/instance/dns_server/tests.rs | 327 ---------- easytier/src/instance/instance.rs | 130 +--- easytier/src/instance/mod.rs | 1 - easytier/src/proto/common.proto | 2 +- 13 files changed, 7 insertions(+), 1826 deletions(-) delete mode 100644 easytier/src/instance/dns_server/client_instance.rs delete mode 100644 easytier/src/instance/dns_server/config.rs delete mode 100644 easytier/src/instance/dns_server/mod.rs delete mode 100644 easytier/src/instance/dns_server/runner.rs delete mode 100644 easytier/src/instance/dns_server/server.rs delete mode 100644 easytier/src/instance/dns_server/server_instance.rs delete mode 100644 easytier/src/instance/dns_server/tests.rs diff --git a/easytier/src/common/config.rs b/easytier/src/common/config.rs index 1f8ef94d..d8bdc3cf 100644 --- a/easytier/src/common/config.rs +++ b/easytier/src/common/config.rs @@ -17,7 +17,6 @@ use tokio::io::AsyncReadExt as _; use crate::{ common::stun::StunInfoCollector, dns::config::DnsConfig, - instance::dns_server::DEFAULT_ET_DNS_ZONE, proto::{ acl::Acl, common::{CompressionAlgoPb, PortForwardConfigPb, SecureModeConfig, SocketType}, @@ -66,7 +65,7 @@ pub fn gen_default_flags() -> Flags { multi_thread_count: 2, encryption_algorithm: EncryptionAlgorithm::default().to_string(), disable_sym_hole_punching: false, - tld_dns_zone: DEFAULT_ET_DNS_ZONE.to_string(), + tld_dns_zone: "".to_string(), quic_listen_port: u32::MAX, need_p2p: false, diff --git a/easytier/src/core.rs b/easytier/src/core.rs index f3453e7e..7b494e92 100644 --- a/easytier/src/core.rs +++ b/easytier/src/core.rs @@ -1080,10 +1080,6 @@ impl NetworkOptions { .enable_relay_foreign_network_quic .unwrap_or(f.enable_relay_foreign_network_quic); 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/dns/node.rs b/easytier/src/dns/node.rs index f480da29..ade588b7 100644 --- a/easytier/src/dns/node.rs +++ b/easytier/src/dns/node.rs @@ -15,7 +15,6 @@ use tokio::sync::{broadcast, Notify}; use tokio::task::JoinSet; use tokio::time::{sleep, sleep_until, Instant}; use uuid::Uuid; -use crate::instance::instance::ArcNicCtx; #[derive(Debug)] pub struct DnsNode { diff --git a/easytier/src/instance/dns_server/client_instance.rs b/easytier/src/instance/dns_server/client_instance.rs deleted file mode 100644 index f1d5f35b..00000000 --- a/easytier/src/instance/dns_server/client_instance.rs +++ /dev/null @@ -1,111 +0,0 @@ -use std::{sync::Arc, time::Duration}; - -use tokio::task::JoinSet; - -use crate::{ - peers::peer_manager::PeerManager, - proto::{ - api::instance::Route, - common::Void, - magic_dns::{ - HandshakeRequest, MagicDnsServerRpc, MagicDnsServerRpcClientFactory, - UpdateDnsRecordRequest, - }, - rpc_impl::standalone::StandAloneClient, - rpc_types::controller::BaseController, - }, - tunnel::tcp::TcpTunnelConnector, -}; - -use super::MAGIC_DNS_INSTANCE_ADDR; - -pub struct MagicDnsClientInstance { - rpc_client: StandAloneClient, - rpc_stub: Option + Send>>, - peer_mgr: Arc, - tasks: JoinSet<()>, -} - -impl MagicDnsClientInstance { - pub async fn new(peer_mgr: Arc) -> Result { - let tcp_connector = TcpTunnelConnector::new(MAGIC_DNS_INSTANCE_ADDR.parse().unwrap()); - let mut rpc_client = StandAloneClient::new(tcp_connector); - let rpc_stub = rpc_client - .scoped_client::>("".to_string()) - .await?; - Ok(MagicDnsClientInstance { - rpc_client, - rpc_stub: Some(rpc_stub), - peer_mgr, - tasks: JoinSet::new(), - }) - } - - async fn update_dns_task( - peer_mgr: Arc, - rpc_stub: Box + Send>, - ) -> Result<(), anyhow::Error> { - let mut prev_last_update = None; - rpc_stub - .handshake(BaseController::default(), HandshakeRequest::default()) - .await?; - loop { - rpc_stub - .heartbeat(BaseController::default(), Void::default()) - .await?; - - let last_update = peer_mgr.get_route_peer_info_last_update_time().await; - if Some(last_update) == prev_last_update { - tokio::time::sleep(Duration::from_millis(500)).await; - continue; - } - - let mut routes = peer_mgr.list_routes().await; - // add self as a route - let ctx = peer_mgr.get_global_ctx(); - routes.push(Route { - hostname: ctx.get_hostname(), - 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: flags.tld_dns_zone.clone(), - }; - tracing::debug!( - "MagicDnsClientInstance::update_dns_task: update dns records: {:?}", - req - ); - rpc_stub - .update_dns_record(BaseController::default(), req) - .await?; - - let last_update_after_rpc = peer_mgr.get_route_peer_info_last_update_time().await; - if last_update_after_rpc == last_update { - prev_last_update = Some(last_update); - } - } - } - - pub async fn run_and_wait(&mut self) { - let rpc_stub = self.rpc_stub.take().unwrap(); - let peer_mgr = self.peer_mgr.clone(); - self.tasks.spawn(async move { - let ret = Self::update_dns_task(peer_mgr, rpc_stub).await; - if let Err(e) = ret { - tracing::error!("MagicDnsServerInstanceData::run_and_wait: {:?}", e); - } - }); - - tokio::select! { - _ = self.tasks.join_next() => { - tracing::warn!("MagicDnsServerInstanceData::run_and_wait: dns record update task exited"); - } - _ = self.rpc_client.wait() => { - tracing::warn!("MagicDnsServerInstanceData::run_and_wait: rpc client exited"); - } - } - } -} diff --git a/easytier/src/instance/dns_server/config.rs b/easytier/src/instance/dns_server/config.rs deleted file mode 100644 index d00f8a37..00000000 --- a/easytier/src/instance/dns_server/config.rs +++ /dev/null @@ -1,193 +0,0 @@ -use hickory_proto::rr; -use hickory_proto::rr::RData; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::net::{IpAddr, Ipv4Addr}; -use std::str::FromStr; -use std::time::Duration; - -#[derive(Serialize, Deserialize, Debug, Clone, derive_builder::Builder)] -pub struct RunConfig { - general: GeneralConfig, - - #[builder(default = HashMap::new())] - zones: Zone, - - #[builder(default = Vec::new())] - #[serde(default)] - excluded_forward_nameservers: Vec, -} - -impl RunConfig { - pub fn general(&self) -> &GeneralConfig { - &self.general - } - - pub fn zones(&self) -> &Zone { - &self.zones - } - - pub fn excluded_forward_nameservers(&self) -> &Vec { - &self.excluded_forward_nameservers - } -} - -#[derive(Serialize, Deserialize, Debug, Clone, derive_builder::Builder)] -pub struct GeneralConfig { - #[builder(setter(into, strip_option), default = None)] - listen_tcp: Option, - - #[builder(setter(into, strip_option), default = None)] - listen_udp: Option, -} - -impl GeneralConfig { - pub fn listen_tcp(&self) -> &Option { - &self.listen_tcp - } - - pub fn listen_udp(&self) -> &Option { - &self.listen_udp - } -} - -pub type Zone = HashMap>; // domain -> records - -pub type RecordType = rr::RecordType; - -#[derive(Serialize, Deserialize, Debug, Clone, derive_builder::Builder)] -pub struct Record { - #[serde(rename = "type")] - rr_type: RecordType, - - name: String, - value: String, - - #[serde(with = "humantime_serde")] - ttl: Duration, -} - -impl Record { - pub fn name(&self) -> anyhow::Result { - let name = rr::Name::from_str(self.name.as_str())?; - Ok(name) - } - - fn rr_type(&self) -> rr::RecordType { - self.rr_type - } -} - -impl TryFrom for rr::Record { - type Error = anyhow::Error; - - fn try_from(value: Record) -> Result { - let r: rr::Record = (&value).try_into()?; - Ok(r) - } -} - -impl TryFrom<&Record> for rr::Record { - type Error = anyhow::Error; - - fn try_from(value: &Record) -> Result { - let name = value.name()?; - let mut record = Self::update0(name, value.ttl.as_secs() as u32, value.rr_type()); - record.set_dns_class(rr::DNSClass::IN); - match value.rr_type { - RecordType::A => { - let addr: Ipv4Addr = value.value.parse()?; - record.set_data(RData::A(rr::rdata::a::A(addr))); - } - RecordType::SOA => { - let soa = value.value.split_whitespace().collect::>(); - if soa.len() != 7 { - return Err(anyhow::anyhow!("invalid SOA record")); - } - let mname = rr::Name::from_str(soa[0])?; - let rname = rr::Name::from_str(soa[1])?; - let serial: u32 = soa[2].parse()?; - let refresh: u32 = soa[3].parse()?; - let retry: u32 = soa[4].parse()?; - let expire: u32 = soa[5].parse()?; - let minimum: u32 = soa[6].parse()?; - record.set_data(RData::SOA(rr::rdata::soa::SOA::new( - mname, - rname, - serial, - refresh.try_into().unwrap(), - retry.try_into().unwrap(), - expire.try_into().unwrap(), - minimum, - ))); - } - _ => todo!(), - } - Ok(record) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use anyhow::anyhow; - - #[tokio::test] - async fn it_works() -> anyhow::Result<()> { - let text = r#" -[general] -listen_tcp = "127.0.0.1:5300" -listen_udp = "127.0.0.1:5353" - -[[zones."et.internal"]] -type = "A" -name = "www" -value = "123.123.123.123" -ttl = "60s" - -[[zones."et.top"]] -type = "A" -name = "@" -value = "100.100.100.100" -ttl = "61s" - -"#; - - let config = toml::from_str::(text)?; - assert_eq!( - config.general.listen_tcp().clone().unwrap(), - "127.0.0.1:5300" - ); - assert_eq!( - config.general.listen_udp().clone().unwrap(), - "127.0.0.1:5353" - ); - assert_eq!(config.zones.len(), 2); - - let (domain, records) = config - .zones - .get_key_value("et.internal") - .ok_or(anyhow!("et.internal not found"))?; - assert_eq!(domain, "et.internal"); - assert_eq!(records.len(), 1); - let record = &records[0]; - assert_eq!(record.rr_type, RecordType::A); - assert_eq!(record.name, "www"); - assert_eq!(record.value, "123.123.123.123"); - assert_eq!(record.ttl.as_secs(), 60); - - let (domain, records) = config - .zones - .get_key_value("et.top") - .ok_or(anyhow!("et.top not found"))?; - assert_eq!(domain, "et.top"); - assert_eq!(records.len(), 1); - let record = &records[0]; - assert_eq!(record.rr_type, RecordType::A); - assert_eq!(record.name, "@"); - assert_eq!(record.value, "100.100.100.100"); - assert_eq!(record.ttl.as_secs(), 61); - - Ok(()) - } -} diff --git a/easytier/src/instance/dns_server/mod.rs b/easytier/src/instance/dns_server/mod.rs deleted file mode 100644 index 64144c5b..00000000 --- a/easytier/src/instance/dns_server/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -// This module is copy and modified from https://github.com/fanyang89/libdns -#[cfg(feature = "magic-dns")] -pub(crate) mod config; -#[cfg(feature = "magic-dns")] -pub(crate) mod server; - -#[cfg(feature = "magic-dns")] -pub mod client_instance; -#[cfg(feature = "magic-dns")] -pub mod runner; -#[cfg(feature = "magic-dns")] -pub mod server_instance; - -#[cfg(all(test, feature = "tun", feature = "magic-dns"))] -pub mod tests; - -pub static MAGIC_DNS_INSTANCE_ADDR: &str = "tcp://127.0.0.1:49813"; -pub static MAGIC_DNS_FAKE_IP: &str = "100.100.100.101"; -pub static DEFAULT_ET_DNS_ZONE: &str = "et.net."; diff --git a/easytier/src/instance/dns_server/runner.rs b/easytier/src/instance/dns_server/runner.rs deleted file mode 100644 index e777ca04..00000000 --- a/easytier/src/instance/dns_server/runner.rs +++ /dev/null @@ -1,93 +0,0 @@ -use cidr::Ipv4Inet; -use tokio_util::sync::CancellationToken; - -use crate::peers::peer_manager::PeerManager; -use std::{net::Ipv4Addr, sync::Arc, time::Duration}; - -use super::{client_instance::MagicDnsClientInstance, server_instance::MagicDnsServerInstance}; - -static DEFAULT_ET_DNS_ZONE: &str = "et.net."; - -pub struct DnsRunner { - client: Option, - server: Option, - peer_mgr: Arc, - tun_dev: Option, - tun_inet: Ipv4Inet, - fake_ip: Ipv4Addr, -} - -impl DnsRunner { - pub fn new( - peer_mgr: Arc, - tun_dev: Option, - tun_inet: Ipv4Inet, - fake_ip: Ipv4Addr, - ) -> Self { - Self { - client: None, - server: None, - peer_mgr, - tun_dev, - tun_inet, - fake_ip, - } - } - - async fn clean_env(&mut self) { - if let Some(server) = self.server.take() { - server.clean_env().await; - } - self.client.take(); - } - - async fn run_once(&mut self) -> anyhow::Result<()> { - // try server first - match MagicDnsServerInstance::new( - self.peer_mgr.clone(), - self.tun_dev.clone(), - self.tun_inet, - self.fake_ip, - ) - .await - { - Ok(server) => { - self.server = Some(server); - tracing::info!("DnsRunner::run_once: server started"); - } - Err(e) => { - tracing::error!("DnsRunner::run_once: {:?}", e); - } - } - - // every runner must run a client - let client = MagicDnsClientInstance::new(self.peer_mgr.clone()).await?; - self.client = Some(client); - self.client.as_mut().unwrap().run_and_wait().await; - - Err(anyhow::anyhow!("Client instance exit")) - } - - pub async fn run(&mut self, canel_token: CancellationToken) { - loop { - tracing::info!("DnsRunner::run: start"); - tokio::select! { - _ = canel_token.cancelled() => { - self.clean_env().await; - tracing::info!("DnsRunner::run: cancelled"); - return; - } - - ret = self.run_once() => { - self.clean_env().await; - if let Err(e) = ret { - tracing::error!("DnsRunner::run: {:?}", e); - } else { - tracing::info!("DnsRunner::run: unexpected exit, server may be down"); - } - tokio::time::sleep(Duration::from_millis(500)).await; - } - } - } - } -} diff --git a/easytier/src/instance/dns_server/server.rs b/easytier/src/instance/dns_server/server.rs deleted file mode 100644 index fc812ff6..00000000 --- a/easytier/src/instance/dns_server/server.rs +++ /dev/null @@ -1,338 +0,0 @@ -use anyhow::{Context, Result}; -use hickory_proto::op::Edns; -use hickory_proto::rr; -use hickory_proto::rr::LowerName; -use hickory_resolver::config::ResolverOpts; -use hickory_resolver::name_server::TokioConnectionProvider; -use hickory_resolver::system_conf::read_system_conf; -use hickory_server::authority::{AuthorityObject, Catalog, ZoneType}; -use hickory_server::server::{Request, RequestHandler, ResponseHandler, ResponseInfo}; -use hickory_server::store::forwarder::ForwardConfig; -use hickory_server::store::{forwarder::ForwardAuthority, in_memory::InMemoryAuthority}; -use hickory_server::ServerFuture; -use std::io; -use std::net::SocketAddr; -use std::str::FromStr; -use std::sync::Arc; -use std::time::Duration; -use tokio::net::{TcpListener, UdpSocket}; -use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; -use tokio::task::JoinSet; - -use crate::common::dns::get_default_resolver_config; - -use super::config::{GeneralConfig, Record, RunConfig}; - -pub struct Server { - server: ServerFuture, - catalog: Arc>, - general_config: GeneralConfig, - udp_local_addr: Option, - tcp_local_addr: Option, - tasks: JoinSet<()>, -} - -struct CatalogRequestHandler { - catalog: Arc>, -} - -impl CatalogRequestHandler { - fn new(catalog: Arc>) -> CatalogRequestHandler { - // let system_conf = read_system_conf(); - // let recursor = match system_conf { - // Ok((conf, _)) => RecursorBuilder::default().build(conf), - // Err(_) => RecursorBuilder::default().build(get_default_resolver_config()), - // } - // // policy is security unware, this will never return an error - // .unwrap(); - - Self { catalog } - } -} - -#[async_trait::async_trait] -impl RequestHandler for CatalogRequestHandler { - async fn handle_request( - &self, - request: &Request, - response_handle: R, - ) -> ResponseInfo { - self.catalog - .read() - .await - .handle_request(request, response_handle) - .await - } -} - -pub fn build_authority(domain: &str, records: &[Record]) -> Result { - let zone = rr::Name::from_str(domain)?; - let mut authority = InMemoryAuthority::empty(zone, ZoneType::Primary, false); - for record in records.iter() { - let r = record.try_into()?; - authority.upsert_mut(r, 0); - } - Ok(authority) -} - -impl Server { - pub fn new(config: RunConfig) -> Self { - Self::try_new(config).unwrap() - } - - fn try_new(config: RunConfig) -> Result { - let mut catalog = Catalog::new(); - for (domain, records) in config.zones().iter() { - let zone = rr::Name::from_str(domain.as_str())?; - let authroty = build_authority(domain, records)?; - catalog.upsert(zone.clone().into(), vec![Arc::new(authroty)]); - } - - // use forwarder authority for the root zone - let system_conf = - read_system_conf().unwrap_or((get_default_resolver_config(), ResolverOpts::default())); - let forward_config = ForwardConfig { - name_servers: system_conf - .0 - .name_servers() - .iter() - .filter(|&x| { - !config - .excluded_forward_nameservers() - .contains(&x.socket_addr.ip()) - }) - .cloned() - .collect::>() - .into(), - options: Some(system_conf.1), - }; - let auth = ForwardAuthority::builder_with_config( - forward_config, - TokioConnectionProvider::default(), - ) - .build() - .unwrap(); - - catalog.upsert(rr::Name::from_str(".")?.into(), vec![Arc::new(auth)]); - - let catalog = Arc::new(RwLock::new(catalog)); - let handler = CatalogRequestHandler::new(catalog.clone()); - let server = ServerFuture::new(handler); - - Ok(Self { - server, - catalog, - general_config: config.general().clone(), - udp_local_addr: None, - tcp_local_addr: None, - tasks: JoinSet::new(), - }) - } - - pub fn udp_local_addr(&self) -> Option { - self.udp_local_addr - } - - pub fn tcp_local_addr(&self) -> Option { - self.tcp_local_addr - } - - pub async fn register_udp_socket(&mut self, address: String) -> Result { - let bind_addr = SocketAddr::from_str(&address) - .with_context(|| format!("DNS Server failed to parse address {}", address))?; - let socket = socket2::Socket::new( - socket2::Domain::IPV4, - socket2::Type::DGRAM, - Some(socket2::Protocol::UDP), - ) - .with_context(|| { - format!( - "DNS Server failed to create UDP socket for address {}", - address - ) - })?; - socket2::SockRef::from(&socket) - .set_reuse_address(true) - .with_context(|| { - format!( - "DNS Server failed to set reuse address on socket {}", - address - ) - })?; - socket.bind(&bind_addr.into()).with_context(|| { - format!("DNS Server failed to bind socket to address {}", bind_addr) - })?; - socket - .set_nonblocking(true) - .with_context(|| "DNS Server failed to set socket to non-blocking".to_string())?; - let socket = UdpSocket::from_std(socket.into()).with_context(|| { - format!( - "DNS Server failed to convert socket to UdpSocket for address {}", - address - ) - })?; - - let local_addr = socket - .local_addr() - .with_context(|| "DNS Server failed to get local address".to_string())?; - self.server.register_socket(socket); - - Ok(local_addr) - } - - pub async fn run(&mut self) -> Result<()> { - if let Some(address) = self.general_config.listen_tcp() { - let tcp_listener = TcpListener::bind(address.clone()) - .await - .with_context(|| format!("DNS Server failed to bind TCP address {}", address))?; - self.tcp_local_addr = Some(tcp_listener.local_addr()?); - self.server - .register_listener(tcp_listener, Duration::from_secs(5)); - } - - if let Some(address) = self.general_config.listen_udp() { - let local_addr = self.register_udp_socket(address.clone()).await?; - self.udp_local_addr = Some(local_addr); - }; - - Ok(()) - } - - pub async fn shutdown(&mut self) -> Result<()> { - self.server.shutdown_gracefully().await?; - Ok(()) - } - - pub async fn upsert(&self, name: LowerName, authority: Arc) { - self.catalog.write().await.upsert(name, vec![authority]); - } - - pub async fn remove(&self, name: &LowerName) -> Option>> { - self.catalog.write().await.remove(name) - } - - pub async fn update( - &self, - update: &Request, - response_edns: Option, - response_handle: R, - ) -> io::Result { - self.catalog - .write() - .await - .update(update, response_edns, response_handle) - .await - } - - pub async fn contains(&self, name: &LowerName) -> bool { - self.catalog.read().await.contains(name) - } - - pub async fn lookup( - &self, - request: &Request, - response_edns: Option, - response_handle: R, - ) -> ResponseInfo { - self.catalog - .read() - .await - .lookup(request, response_edns, response_handle) - .await - } - - pub async fn read_catalog(&self) -> RwLockReadGuard<'_, Catalog> { - self.catalog.read().await - } - - pub async fn write_catalog(&self) -> RwLockWriteGuard<'_, Catalog> { - self.catalog.write().await - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::instance::dns_server::config::{ - GeneralConfigBuilder, RecordBuilder, RecordType, RunConfigBuilder, - }; - use anyhow::Result; - use hickory_client::client::{Client, ClientHandle}; - use hickory_proto::rr; - use hickory_proto::runtime::TokioRuntimeProvider; - use hickory_proto::udp::UdpClientStream; - use maplit::hashmap; - use std::time::Duration; - - #[tokio::test] - async fn it_works() -> Result<()> { - let mut server = Server::new( - RunConfigBuilder::default() - .general(GeneralConfigBuilder::default().build()?) - .build()?, - ); - server.run().await?; - server.shutdown().await?; - Ok(()) - } - - #[tokio::test] - async fn can_resolve_records() -> Result<()> { - let configured_record = RecordBuilder::default() - .rr_type(RecordType::A) - .name("www.et.internal.".to_string()) - .value("123.123.123.123".to_string()) - .ttl(Duration::from_secs(60)) - .build()?; - let configured_record2 = RecordBuilder::default() - .rr_type(RecordType::A) - .name("中文.et.internal.".to_string()) - .value("123.123.123.123".to_string()) - .ttl(Duration::from_secs(60)) - .build()?; - let soa_record = RecordBuilder::default() - .rr_type(RecordType::SOA) - .name("et.internal.".to_string()) - .value( - "ns.et.internal. hostmaster.et.internal. 2023101001 7200 3600 1209600 86400" - .to_string(), - ) - .ttl(Duration::from_secs(60)) - .build()?; - let config = RunConfigBuilder::default() - .general( - GeneralConfigBuilder::default() - .listen_udp("127.0.0.1:0") - .build()?, - ) - .zones(hashmap! { - "et.internal.".to_string() => vec![configured_record.clone(), soa_record.clone(), configured_record2.clone()], - }) - .build()?; - - let mut server = Server::new(config); - server.run().await?; - - let local_addr = server.udp_local_addr().unwrap(); - let stream = UdpClientStream::builder(local_addr, TokioRuntimeProvider::default()).build(); - let (mut client, background) = Client::connect(stream).await?; - let background_task = tokio::spawn(background); - let response = client - .query( - rr::Name::from_str("www.et.internal")?, - rr::DNSClass::IN, - rr::RecordType::A, - ) - .await?; - drop(background_task); - - println!("Response: {:?}", response); - - assert_eq!(response.answers().len(), 1); - let expected_record: rr::Record = configured_record.try_into()?; - assert_eq!(response.answers().first().unwrap(), &expected_record); - - server.shutdown().await?; - Ok(()) - } -} diff --git a/easytier/src/instance/dns_server/server_instance.rs b/easytier/src/instance/dns_server/server_instance.rs deleted file mode 100644 index 2b00636d..00000000 --- a/easytier/src/instance/dns_server/server_instance.rs +++ /dev/null @@ -1,611 +0,0 @@ -// single-instance server in one machine, every easytier instance that has ip address and tun device will try to create a server instance. - -// magic dns client will connect to this server to update the dns records. -// magic dns server will add the dns server ip address to the tun device, and forward the dns request to the dns server - -// magic dns client will establish a long live tcp connection to the magic dns server, and when the server stops or crashes, -// all the clients will exit and let the easytier instance to launch a new server instance. - -use super::{ - config::{GeneralConfigBuilder, RunConfigBuilder}, - server::Server, - MAGIC_DNS_INSTANCE_ADDR, -}; -use crate::dns::system::{SystemConfig, SystemConfigurator}; -use crate::{ - common::{ - ifcfg::{IfConfiger, IfConfiguerTrait}, - PeerId, - }, - instance::dns_server::{ - config::{Record, RecordBuilder, RecordType}, - server::build_authority, - }, - peers::{peer_manager::PeerManager, NicPacketFilter}, - proto::{ - api::instance::Route, - common::{TunnelInfo, Void}, - magic_dns::{ - dns_record::{self}, - DnsRecord, DnsRecordA, DnsRecordList, GetDnsRecordResponse, HandshakeRequest, - HandshakeResponse, MagicDnsServerRpc, MagicDnsServerRpcServer, UpdateDnsRecordRequest, - }, - rpc_impl::standalone::{RpcServerHook, StandAloneServer}, - rpc_types::controller::{BaseController, Controller}, - }, - tunnel::{packet_def::ZCPacket, tcp::TcpTunnelListener}, -}; -use anyhow::Context; -use cidr::Ipv4Inet; -use dashmap::DashMap; -use hickory_proto::rr::LowerName; -use hickory_proto::serialize::binary::{BinDecodable, BinEncoder}; -use hickory_server::authority::{MessageRequest, MessageResponse}; -use hickory_server::server::{Request, RequestHandler, ResponseHandler, ResponseInfo}; -use multimap::MultiMap; -use pnet::packet::icmp::{IcmpTypes, MutableIcmpPacket}; -use pnet::packet::ipv4::Ipv4Packet; -use pnet::packet::udp::UdpPacket; -use pnet::packet::{ - icmp, - ip::IpNextHeaderProtocols, - ipv4::{self, MutableIpv4Packet}, - udp::{self, MutableUdpPacket}, - MutablePacket, Packet, -}; -use std::net::{SocketAddr, SocketAddrV4}; -use std::sync::Mutex; -use std::{collections::BTreeMap, io, net::Ipv4Addr, str::FromStr, sync::Arc, time::Duration}; - -static NIC_PIPELINE_NAME: &str = "magic_dns_server"; - -pub(super) struct MagicDnsServerInstanceData { - dns_server: Server, - tun_dev: Option, - tun_ip: Ipv4Addr, - fake_ip: Ipv4Addr, - my_peer_id: PeerId, - - // zone -> (tunnel remote addr -> route) - route_infos: DashMap>, - - system_config: Option>, -} - -impl MagicDnsServerInstanceData { - pub async fn update_dns_records<'a, T: Iterator>( - &self, - routes: T, - zone: &str, - ) -> Result<(), anyhow::Error> { - let mut records: Vec = vec![]; - for route in routes { - if route.hostname.is_empty() { - continue; - } - - let Some(ipv4_addr) = route.ipv4_addr.unwrap_or_default().address else { - continue; - }; - - let record = RecordBuilder::default() - .rr_type(RecordType::A) - .name(format!("{}.{}", route.hostname, zone)) - .value(ipv4_addr.to_string()) - .ttl(Duration::from_secs(1)) - .build()?; - - // check record name valid for dns - if let Err(e) = record.name() { - tracing::error!("Invalid subdomain label: {}", e); - continue; - } - - records.push(record); - } - - let soa_record = RecordBuilder::default() - .rr_type(RecordType::SOA) - .name(zone.to_string()) - .value(format!( - "ns.{} hostmaster.{} 2023101001 7200 3600 1209600 86400", - zone, zone - )) - .ttl(Duration::from_secs(60)) - .build()?; - records.push(soa_record); - - let authority = build_authority(zone, &records)?; - - self.dns_server - .upsert( - LowerName::from_str(zone) - .with_context(|| "Invalid zone name, expect format like \"et.net.\"")?, - Arc::new(authority), - ) - .await; - - tracing::debug!("Updated DNS records for zone {}: {:?}", zone, records); - - Ok(()) - } - - pub async fn update(&self) { - for item in self.route_infos.iter() { - let zone = item.key(); - let route_iter = item.value().flat_iter().map(|x| x.1); - if let Err(e) = self.update_dns_records(route_iter, zone).await { - tracing::error!("Failed to update DNS records for zone {}: {:?}", zone, e); - } - } - } - - async fn keep_zone_authoritative(&self, zone: &str) { - if let Err(e) = self - .update_dns_records(std::iter::empty::<&Route>(), zone) - .await - { - tracing::error!( - "Failed to keep DNS zone {} authoritative after route prune: {:?}", - zone, - e - ); - } - } - - fn do_system_config(&self, zone: &str) -> Result<(), anyhow::Error> { - if let Some(c) = &self.system_config { - c.set_dns(&SystemConfig { - nameservers: vec![self.fake_ip.to_string()], - search_domains: vec![zone.to_string()], - match_domains: vec![zone.to_string()], - })?; - } - Ok(()) - } -} - -#[async_trait::async_trait] -impl MagicDnsServerRpc for MagicDnsServerInstanceData { - type Controller = BaseController; - async fn handshake( - &self, - _ctrl: Self::Controller, - _input: HandshakeRequest, - ) -> crate::proto::rpc_types::error::Result { - Ok(Default::default()) - } - - async fn heartbeat( - &self, - _ctrl: Self::Controller, - _input: Void, - ) -> crate::proto::rpc_types::error::Result { - Ok(Default::default()) - } - - async fn update_dns_record( - &self, - ctrl: Self::Controller, - input: UpdateDnsRecordRequest, - ) -> crate::proto::rpc_types::error::Result { - let Some(tunnel_info) = ctrl.get_tunnel_info() else { - return Err(anyhow::anyhow!("No tunnel info").into()); - }; - let Some(remote_addr) = &tunnel_info.remote_addr else { - return Err(anyhow::anyhow!("No remote addr").into()); - }; - let zone = input.zone.clone(); - let remote_addr: url::Url = remote_addr.clone().into(); - let mut zone_removed = false; - - if let Some(mut routes_by_addr) = self.route_infos.get_mut(&zone) { - routes_by_addr.remove(&remote_addr); - if !input.routes.is_empty() { - routes_by_addr.insert_many(remote_addr, input.routes); - } - zone_removed = routes_by_addr.is_empty(); - } else if !input.routes.is_empty() { - let mut routes_by_addr = MultiMap::new(); - routes_by_addr.insert_many(remote_addr, input.routes); - self.route_infos.insert(zone.clone(), routes_by_addr); - } - - if zone_removed { - self.route_infos.remove(&zone); - self.keep_zone_authoritative(&zone).await; - } - - self.update().await; - Ok(Default::default()) - } - - async fn get_dns_record( - &self, - _ctrl: Self::Controller, - _input: Void, - ) -> crate::proto::rpc_types::error::Result { - let mut ret = BTreeMap::new(); - for item in self.route_infos.iter() { - let zone = item.key(); - let routes = item.value(); - let mut dns_records = DnsRecordList::default(); - for route in routes.iter().map(|x| x.1) { - dns_records.records.push(DnsRecord { - record: Some(dns_record::Record::A(DnsRecordA { - name: format!("{}.{}", route.hostname, zone), - value: route.ipv4_addr.unwrap_or_default().address, - ttl: 1, - })), - }); - } - ret.insert(zone.clone(), dns_records); - } - Ok(GetDnsRecordResponse { records: ret }) - } -} - -// This should only be used for UDP response. -// For other protocols, the variable `max_size` in `send_response` should be u16::MAX. -#[derive(Clone)] -struct ResponseWrapper { - response: Arc>>, -} - -trait RecordIter<'a>: Iterator + Send + 'a {} -impl<'a, T> RecordIter<'a> for T where T: Iterator + Send + 'a {} - -#[async_trait::async_trait] -impl ResponseHandler for ResponseWrapper { - async fn send_response<'a>( - &mut self, - response: MessageResponse< - '_, - 'a, - impl RecordIter<'a>, - impl RecordIter<'a>, - impl RecordIter<'a>, - impl RecordIter<'a>, - >, - ) -> io::Result { - let mut buffer = self - .response - .lock() - .map_err(|_| io::Error::other("lock poisoned"))?; - - let mut encoder = BinEncoder::new(&mut buffer); - - // `max_size` should be u16::MAX for protocol other than UDP. - let max_size = if let Some(edns) = response.get_edns() { - edns.max_payload() - } else { - hickory_proto::udp::MAX_RECEIVE_BUFFER_SIZE as u16 - }; - - encoder.set_max_size(max_size); - response - .destructive_emit(&mut encoder) - .map_err(io::Error::other) - } -} - -impl MagicDnsServerInstanceData { - /// Replace content of incoming UDP DNS request and ICMP echo request packet with reply data, - /// and swap source and destination IP addresses to send it back. - async fn handle_ip_packet(&self, zc_packet: &mut ZCPacket) -> Option<()> { - let (ip_header_length, ip_protocol, src_ip, dst_ip) = { - let ip_packet = Ipv4Packet::new(zc_packet.payload())?; - - if ip_packet.get_version() != 4 { - return None; - } - - ( - ip_packet.get_header_length() as usize * 4, - ip_packet.get_next_level_protocol(), - ip_packet.get_source(), - ip_packet.get_destination(), - ) - }; - - if dst_ip != self.fake_ip { - return None; - } - - match ip_protocol { - IpNextHeaderProtocols::Udp => { - self.handle_udp_packet(zc_packet, ip_header_length, src_ip, dst_ip) - .await?; - } - IpNextHeaderProtocols::Icmp => { - self.handle_icmp_packet(zc_packet, ip_header_length)?; - } - _ => { - return None; - } - } - - let mut ip_packet = MutableIpv4Packet::new(zc_packet.mut_payload())?; - ip_packet.set_source(dst_ip); - ip_packet.set_destination(src_ip); - - ip_packet.set_checksum(ipv4::checksum(&ip_packet.to_immutable())); - - zc_packet.mut_peer_manager_header().unwrap().to_peer_id = self.my_peer_id.into(); - - Some(()) - } - - /// Extract the DNS request message and send it to the hickory-dns server instance. - /// Replace the content of the UDP packet with the response message. - async fn handle_udp_packet( - &self, - zc_packet: &mut ZCPacket, - ip_header_length: usize, - src_ip: Ipv4Addr, - dst_ip: Ipv4Addr, - ) -> Option<()> { - let (src_port, dst_port, request, request_length) = { - let udp_packet = UdpPacket::new(&zc_packet.payload()[ip_header_length..])?; - - let src_port = udp_packet.get_source(); - let dst_port = udp_packet.get_destination(); - - // Remove this to support any UDP port - if dst_port != 53 { - return None; - } - - let request_payload = udp_packet.payload(); - - ( - src_port, - dst_port, - Request::new( - MessageRequest::from_bytes(request_payload).ok()?, - SocketAddr::from(SocketAddrV4::new(src_ip, src_port)), - hickory_proto::xfer::Protocol::Udp, - ), - request_payload.len(), - ) - }; - - let response_payload = { - let response_payload_arc = Arc::new(Mutex::new(Vec::with_capacity(512))); - - self.dns_server - .read_catalog() - .await - .handle_request( - &request, - ResponseWrapper { - response: response_payload_arc.clone(), - }, - ) - .await; - - Arc::into_inner(response_payload_arc)?.into_inner().ok()? - }; - - let response_length = response_payload.len(); - let delta_length = response_length as isize - request_length as isize; - - let inner_length = (zc_packet.buf_len() as isize + delta_length) as usize; - if zc_packet.mut_inner().capacity() < inner_length { - let header_length = inner_length - response_length; - zc_packet.mut_inner().truncate(header_length); - } - zc_packet.mut_inner().resize(inner_length, 0); - - let mut ip_packet = MutableIpv4Packet::new(zc_packet.mut_payload())?; - - let ip_length = (ip_packet.get_total_length() as isize + delta_length) as u16; - ip_packet.set_total_length(ip_length); - - let mut udp_packet = MutableUdpPacket::new(ip_packet.payload_mut())?; - - let udp_length = (udp_packet.get_length() as isize + delta_length) as u16; - udp_packet.set_length(udp_length); - - udp_packet.set_source(dst_port); - udp_packet.set_destination(src_port); - - udp_packet.payload_mut().copy_from_slice(&response_payload); - - udp_packet.set_checksum(udp::ipv4_checksum( - &udp_packet.to_immutable(), - &dst_ip, - &src_ip, - )); - - Some(()) - } - - fn handle_icmp_packet(&self, zc_packet: &mut ZCPacket, ip_header_length: usize) -> Option<()> { - let mut icmp_packet = - MutableIcmpPacket::new(&mut zc_packet.mut_payload()[ip_header_length..])?; - - if icmp_packet.get_icmp_type() != IcmpTypes::EchoRequest { - return None; - } - - icmp_packet.set_icmp_type(IcmpTypes::EchoReply); - icmp_packet.set_checksum(icmp::checksum(&icmp_packet.to_immutable())); - - Some(()) - } -} - -#[async_trait::async_trait] -impl NicPacketFilter for MagicDnsServerInstanceData { - async fn try_process_packet_from_nic(&self, zc_packet: &mut ZCPacket) -> bool { - self.handle_ip_packet(zc_packet).await.is_some() - } - - fn id(&self) -> String { - NIC_PIPELINE_NAME.to_string() - } -} - -#[async_trait::async_trait] -impl RpcServerHook for MagicDnsServerInstanceData { - async fn on_new_client( - &self, - tunnel_info: Option, - ) -> Result, anyhow::Error> { - tracing::info!(?tunnel_info, "New client connected"); - Ok(tunnel_info) - } - - async fn on_client_disconnected(&self, tunnel_info: Option) { - tracing::info!(?tunnel_info, "Client disconnected"); - let Some(tunnel_info) = tunnel_info else { - return; - }; - let Some(remote_addr) = tunnel_info.remote_addr else { - return; - }; - let remote_addr = remote_addr.into(); - let mut removed_zones = vec![]; - for mut item in self.route_infos.iter_mut() { - item.value_mut().remove(&remote_addr); - if item.value().is_empty() { - removed_zones.push(item.key().clone()); - } - } - for zone in &removed_zones { - self.route_infos.remove(zone); - } - for zone in removed_zones { - self.keep_zone_authoritative(&zone).await; - } - self.update().await; - } -} - -pub struct MagicDnsServerInstance { - rpc_server: StandAloneServer, - pub(super) data: Arc, - peer_mgr: Arc, - tun_inet: Ipv4Inet, -} - -fn get_system_config( - _tun_name: Option<&str>, -) -> Result>, anyhow::Error> { - #[cfg(target_os = "windows")] - { - use crate::dns::system::windows::WindowsDNSManager; - let tun_name = _tun_name.ok_or_else(|| anyhow::anyhow!("No tun name"))?; - return Ok(Some(Box::new(WindowsDNSManager::new(tun_name)?))); - } - - #[cfg(all(target_os = "macos", not(feature = "macos-ne")))] - { - use crate::dns::system_config::darwin::DarwinConfigurator; - return Ok(Some(Box::new(DarwinConfigurator::new()))); - } - - #[allow(unreachable_code)] - Ok(None) -} - -impl MagicDnsServerInstance { - pub async fn new( - peer_mgr: Arc, - tun_dev: Option, - tun_inet: Ipv4Inet, - fake_ip: Ipv4Addr, - ) -> Result { - let tcp_listener = TcpTunnelListener::new(MAGIC_DNS_INSTANCE_ADDR.parse()?); - let mut rpc_server = StandAloneServer::new(tcp_listener); - rpc_server.serve().await?; - - let dns_config = RunConfigBuilder::default() - .general(GeneralConfigBuilder::default().build()?) - .excluded_forward_nameservers(vec![fake_ip.into()]) - .build()?; - let mut dns_server = Server::new(dns_config); - dns_server.run().await?; - - if !tun_inet.contains(&fake_ip) { - if let Some(tun_dev_name) = &tun_dev { - let cost = if cfg!(target_os = "windows") { - Some(4) - } else { - None - }; - let ifcfg = IfConfiger {}; - ifcfg - .add_ipv4_route(tun_dev_name, fake_ip, 32, cost) - .await?; - } - } - - let data = Arc::new(MagicDnsServerInstanceData { - dns_server, - tun_dev: tun_dev.clone(), - tun_ip: tun_inet.address(), - fake_ip, - my_peer_id: peer_mgr.my_peer_id(), - route_infos: DashMap::new(), - system_config: get_system_config(tun_dev.as_deref())?, - }); - - rpc_server - .registry() - .register(MagicDnsServerRpcServer::new_arc(data.clone()), ""); - rpc_server.set_hook(data.clone()); - - 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(); - - data.update_dns_records(std::iter::empty(), &tld_dns_zone_clone) - .await - .context("Failed to initialize DNS zone")?; - - let data_clone = data.clone(); - tokio::task::spawn_blocking(move || data_clone.do_system_config(&tld_dns_zone_clone)) - .await - .context("Failed to configure system")??; - - Ok(Self { - rpc_server, - data, - peer_mgr, - tun_inet, - }) - } - - pub async fn clean_env(&self) { - if let Some(configer) = &self.data.system_config { - let ret = configer.clean(); - if let Err(e) = ret { - tracing::error!("Failed to close system config: {:?}", e); - } - if !self.tun_inet.contains(&self.data.fake_ip) { - if let Some(tun_dev_name) = &self.data.tun_dev { - let ifcfg = IfConfiger {}; - let _ = ifcfg - .remove_ipv4_route(tun_dev_name, self.data.fake_ip, 32) - .await; - } - } - } - - let _ = self - .peer_mgr - .remove_nic_packet_process_pipeline(NIC_PIPELINE_NAME.to_string()) - .await; - } -} - -impl Drop for MagicDnsServerInstance { - fn drop(&mut self) { - println!("MagicDnsServerInstance dropped"); - } -} diff --git a/easytier/src/instance/dns_server/tests.rs b/easytier/src/instance/dns_server/tests.rs deleted file mode 100644 index 2e9bb2e7..00000000 --- a/easytier/src/instance/dns_server/tests.rs +++ /dev/null @@ -1,327 +0,0 @@ -use std::net::{Ipv4Addr, SocketAddr}; -use std::str::FromStr as _; -use std::sync::Arc; -use std::time::Duration; - -use cidr::Ipv4Inet; -use hickory_client::client::{Client, ClientHandle as _}; -use hickory_proto::rr; -use hickory_proto::runtime::TokioRuntimeProvider; -use hickory_proto::udp::UdpClientStream; -use tokio::sync::Notify; -use tokio_util::sync::CancellationToken; - -use crate::common::global_ctx::tests::get_mock_global_ctx; -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, MAGIC_DNS_FAKE_IP}; -use crate::instance::virtual_nic::NicCtx; -use crate::peers::peer_manager::{PeerManager, RouteAlgoType}; - -use crate::peers::create_packet_recv_chan; -use crate::proto::api::instance::Route; -use crate::proto::common::NatType; -use crate::proto::magic_dns::{MagicDnsServerRpc as _, UpdateDnsRecordRequest}; -use crate::proto::rpc_types::controller::{BaseController, Controller as _}; - -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.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(); - replace_stun_info_collector(peer_mgr.clone(), NatType::PortRestricted); - - let r = Arc::new(tokio::sync::Mutex::new(r)); - let mut virtual_nic = NicCtx::new( - peer_mgr.get_global_ctx(), - &peer_mgr, - r, - Arc::new(Notify::new()), - ); - virtual_nic.run(Some(tun_ip), None).await.unwrap(); - - (peer_mgr, virtual_nic) -} - -pub async fn check_dns_record(fake_ip: &Ipv4Addr, domain: &str, expected_ip: &str) { - let stream = UdpClientStream::builder( - SocketAddr::new((*fake_ip).into(), 53), - TokioRuntimeProvider::default(), - ) - .build(); - let (mut client, background) = Client::connect(stream).await.unwrap(); - let background_task = tokio::spawn(background); - let response = client - .query( - rr::Name::from_str(domain).unwrap(), - rr::DNSClass::IN, - rr::RecordType::A, - ) - .await - .unwrap_or_else(|e| panic!("DNS query failed unexpectedly for domain '{domain}': {e}")); - background_task.abort(); - let _ = background_task.await; - - println!("Response: {:?}", response); - - assert_eq!(response.answers().len(), 1, "{:?}", response.answers()); - let resp = response.answers().first().unwrap(); - assert_eq!( - resp.clone().into_parts().rdata.into_a().unwrap().0, - expected_ip.parse::().unwrap() - ); -} - -pub async fn check_dns_record_missing(fake_ip: &Ipv4Addr, domain: &str) { - let stream = UdpClientStream::builder( - SocketAddr::new((*fake_ip).into(), 53), - TokioRuntimeProvider::default(), - ) - .build(); - let (mut client, background) = Client::connect(stream).await.unwrap(); - let background_task = tokio::spawn(background); - let response = client - .query( - rr::Name::from_str(domain).unwrap(), - rr::DNSClass::IN, - rr::RecordType::A, - ) - .await - .unwrap_or_else(|e| { - panic!("DNS query for missing record failed unexpectedly for domain '{domain}': {e}") - }); - background_task.abort(); - let _ = background_task.await; - assert!(response.answers().is_empty(), "{:?}", response.answers()); -} - -#[tokio::test] -async fn test_magic_dns_server_instance() { - 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 dns_server_inst = - MagicDnsServerInstance::new(peer_mgr.clone(), Some(tun_name), tun_ip, fake_ip) - .await - .unwrap(); - - let routes = [ - Route { - hostname: "test1".to_string(), - ipv4_addr: Some(Ipv4Inet::from_str("8.8.8.8/24").unwrap().into()), - ..Default::default() - }, - Route { - hostname: "中文".to_string(), - ipv4_addr: Some(Ipv4Inet::from_str("8.8.8.8/24").unwrap().into()), - ..Default::default() - }, - Route { - hostname: ".invalid".to_string(), - ipv4_addr: Some(Ipv4Inet::from_str("8.8.8.8/24").unwrap().into()), - ..Default::default() - }, - ]; - dns_server_inst - .data - .update_dns_records(routes.iter(), DEFAULT_ET_DNS_ZONE) - .await - .unwrap(); - - check_dns_record(&fake_ip, "test1.et.net", "8.8.8.8").await; - check_dns_record(&fake_ip, "中文.et.net", "8.8.8.8").await; -} - -#[tokio::test] -async fn test_magic_dns_runner() { - // 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; - - // 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; - - cancel_token.cancel(); - t.await.unwrap(); - - // Wait a bit for cleanup - tokio::time::sleep(Duration::from_secs(1)).await; - } - - // 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(); - } -} - -#[tokio::test] -async fn test_magic_dns_update_replaces_records_for_same_client() { - let tun_ip = Ipv4Inet::from_str("100.100.100.0/24").unwrap(); - let ctx = get_mock_global_ctx(); - ctx.set_hostname("test1".to_string()); - ctx.set_ipv4(Some(tun_ip)); - - let (s, _r) = create_packet_recv_chan(); - let peer_mgr = Arc::new(PeerManager::new(RouteAlgoType::Ospf, ctx, s)); - peer_mgr.run().await.unwrap(); - replace_stun_info_collector(peer_mgr.clone(), NatType::PortRestricted); - - let fake_ip = Ipv4Addr::from_str(MAGIC_DNS_FAKE_IP).unwrap(); - let dns_server_inst = MagicDnsServerInstance::new(peer_mgr.clone(), None, tun_ip, fake_ip) - .await - .unwrap(); - - let mut ctrl = BaseController::default(); - ctrl.set_tunnel_info(Some(crate::proto::common::TunnelInfo { - tunnel_type: "tcp".to_string(), - local_addr: None, - remote_addr: Some(crate::proto::common::Url { - url: "tcp://127.0.0.1:54321".to_string(), - }), - resolved_remote_addr: None, - })); - - dns_server_inst - .data - .update_dns_record( - ctrl.clone(), - UpdateDnsRecordRequest { - zone: DEFAULT_ET_DNS_ZONE.to_string(), - routes: vec![Route { - hostname: "test1".to_string(), - ipv4_addr: Some(Ipv4Inet::from_str("8.8.8.8/32").unwrap().into()), - ..Default::default() - }], - }, - ) - .await - .unwrap(); - - dns_server_inst - .data - .update_dns_record( - ctrl, - UpdateDnsRecordRequest { - zone: DEFAULT_ET_DNS_ZONE.to_string(), - routes: vec![Route { - hostname: "test1".to_string(), - ipv4_addr: Some(Ipv4Inet::from_str("1.1.1.1/32").unwrap().into()), - ..Default::default() - }], - }, - ) - .await - .unwrap(); - - let dns_records = dns_server_inst - .data - .get_dns_record( - BaseController::default(), - crate::proto::common::Void::default(), - ) - .await - .unwrap(); - let zone_records = dns_records.records.get(DEFAULT_ET_DNS_ZONE).unwrap(); - let a_records = zone_records - .records - .iter() - .filter_map(|record| match record.record.as_ref() { - Some(crate::proto::magic_dns::dns_record::Record::A(a)) - if a.name == "test1.et.net." => - { - Some(a) - } - _ => None, - }) - .collect::>(); - - assert_eq!(a_records.len(), 1, "{a_records:?}"); - let resolved_ip = Ipv4Addr::from(a_records[0].value.unwrap_or_default()); - assert_eq!(resolved_ip, Ipv4Addr::new(1, 1, 1, 1)); - - let mut ctrl = BaseController::default(); - ctrl.set_tunnel_info(Some(crate::proto::common::TunnelInfo { - tunnel_type: "tcp".to_string(), - local_addr: None, - remote_addr: Some(crate::proto::common::Url { - url: "tcp://127.0.0.1:54321".to_string(), - }), - resolved_remote_addr: None, - })); - - dns_server_inst - .data - .update_dns_record( - ctrl, - UpdateDnsRecordRequest { - zone: DEFAULT_ET_DNS_ZONE.to_string(), - routes: vec![], - }, - ) - .await - .unwrap(); - - let dns_records = dns_server_inst - .data - .get_dns_record( - BaseController::default(), - crate::proto::common::Void::default(), - ) - .await - .unwrap(); - assert!(!dns_records.records.contains_key(DEFAULT_ET_DNS_ZONE)); -} diff --git a/easytier/src/instance/instance.rs b/easytier/src/instance/instance.rs index e46da50d..b56fe007 100644 --- a/easytier/src/instance/instance.rs +++ b/easytier/src/instance/instance.rs @@ -14,8 +14,6 @@ use futures::FutureExt; use tokio::sync::{Mutex, Notify}; #[cfg(feature = "tun")] use tokio::{sync::oneshot, task::JoinSet}; -#[cfg(feature = "magic-dns")] -use tokio_util::sync::CancellationToken; use crate::common::acl_processor::AclRuleBuilder; use crate::common::config::ConfigLoader; @@ -62,8 +60,6 @@ use crate::rpc_service::InstanceRpcService; use crate::utils::weak_upgrade; use crate::vpn_portal::{self, VpnPortal}; -#[cfg(feature = "magic-dns")] -use super::dns_server::{runner::DnsRunner, MAGIC_DNS_FAKE_IP}; use super::listeners::ListenerManager; #[cfg(feature = "socks5")] @@ -133,63 +129,8 @@ impl IpProxy { #[cfg(feature = "tun")] type NicCtx = super::virtual_nic::NicCtx; -#[cfg(feature = "magic-dns")] -struct MagicDnsContainer { - dns_runner_task: ScopedTask<()>, - dns_runner_cancel_token: CancellationToken, -} - -// nic container will be cleared when dhcp ip changed #[cfg(feature = "tun")] -pub struct NicCtxContainer { - nic_ctx: Option>, - #[cfg(feature = "magic-dns")] - magic_dns: Option, -} - -#[cfg(feature = "tun")] -impl NicCtxContainer { - #[cfg(not(feature = "magic-dns"))] - fn new(nic_ctx: NicCtx) -> Self { - Self { - nic_ctx: Some(Box::new(nic_ctx)), - } - } - - #[cfg(feature = "magic-dns")] - fn new(nic_ctx: NicCtx, dns_runner: Option) -> Self { - if let Some(mut dns_runner) = dns_runner { - let token = CancellationToken::new(); - let token_clone = token.clone(); - let task = tokio::spawn(async move { - let _ = dns_runner.run(token_clone).await; - }); - Self { - nic_ctx: Some(Box::new(nic_ctx)), - magic_dns: Some(MagicDnsContainer { - dns_runner_task: task.into(), - dns_runner_cancel_token: token, - }), - } - } else { - Self { - nic_ctx: Some(Box::new(nic_ctx)), - magic_dns: None, - } - } - } - - fn new_with_any(ctx: T) -> Self { - Self { - nic_ctx: Some(Box::new(ctx)), - #[cfg(feature = "magic-dns")] - magic_dns: None, - } - } -} - -#[cfg(feature = "tun")] -type ArcNicCtx = Arc>>; +type ArcNicCtx = Arc>>>; pub struct InstanceRpcServerHook { rpc_portal_whitelist: Vec, @@ -666,16 +607,6 @@ impl Instance { arc_nic_ctx: ArcNicCtx, packet_recv: Arc>, ) { - #[cfg(feature = "magic-dns")] - if let Some(old_ctx) = arc_nic_ctx.lock().await.take() { - if let Some(dns_runner) = old_ctx.magic_dns { - dns_runner.dns_runner_cancel_token.cancel(); - tracing::debug!("cancelling dns runner task"); - let ret = dns_runner.dns_runner_task.await; - tracing::debug!("dns runner task cancelled, ret: {:?}", ret); - } - }; - let mut tasks = JoinSet::new(); tasks.spawn(async move { let mut packet_recv = packet_recv.lock().await; @@ -683,46 +614,15 @@ impl Instance { tracing::trace!("packet consumed by mock nic ctx: {:?}", packet); } }); - arc_nic_ctx - .lock() - .await - .replace(NicCtxContainer::new_with_any(tasks)); + arc_nic_ctx.lock().await.replace(Box::new(tasks)); tracing::debug!("nic ctx cleared."); } - #[cfg(feature = "magic-dns")] - fn create_magic_dns_runner( - peer_mgr: Arc, - tun_dev: Option, - tun_ip: Ipv4Inet, - ) -> Option { - let ctx = peer_mgr.get_global_ctx(); - if !ctx.config.get_flags().accept_dns { - return None; - } - - let runner = DnsRunner::new( - peer_mgr, - tun_dev, - tun_ip, - MAGIC_DNS_FAKE_IP.parse().unwrap(), - ); - Some(runner) - } - #[cfg(feature = "tun")] - async fn use_new_nic_ctx( - arc_nic_ctx: ArcNicCtx, - nic_ctx: NicCtx, - #[cfg(feature = "magic-dns")] magic_dns: Option, - ) { + async fn use_new_nic_ctx(arc_nic_ctx: ArcNicCtx, nic_ctx: NicCtx) { let mut g = arc_nic_ctx.lock().await; - *g = Some(NicCtxContainer::new( - nic_ctx, - #[cfg(feature = "magic-dns")] - magic_dns, - )); + *g = Some(Box::new(nic_ctx)); tracing::debug!("nic ctx updated."); } @@ -826,15 +726,7 @@ impl Instance { global_ctx_c.set_ipv4(None); continue; } - #[cfg(feature = "magic-dns")] - let ifname = new_nic_ctx.ifname().await; - Self::use_new_nic_ctx( - nic_ctx.clone(), - new_nic_ctx, - #[cfg(feature = "magic-dns")] - Self::create_magic_dns_runner(peer_manager_c.clone(), ifname, ip), - ) - .await; + Self::use_new_nic_ctx(nic_ctx.clone(), new_nic_ctx).await; } current_dhcp_ip = Some(ip); @@ -895,18 +787,6 @@ impl Instance { continue; } - // Create Magic DNS runner only if we have IPv4 - #[cfg(feature = "magic-dns")] - { - let ifname = new_nic_ctx.ifname().await; - let dns_runner = if let Some(ipv4) = ipv4_addr { - Self::create_magic_dns_runner(peer_mgr, ifname, ipv4) - } else { - None - }; - Self::use_new_nic_ctx(nic_ctx.clone(), new_nic_ctx, dns_runner).await; - } - #[cfg(not(feature = "magic-dns"))] Self::use_new_nic_ctx(nic_ctx.clone(), new_nic_ctx).await; } diff --git a/easytier/src/instance/mod.rs b/easytier/src/instance/mod.rs index d50ad23e..97a9e517 100644 --- a/easytier/src/instance/mod.rs +++ b/easytier/src/instance/mod.rs @@ -1,4 +1,3 @@ -pub mod dns_server; #[allow(clippy::module_inception)] pub mod instance; diff --git a/easytier/src/proto/common.proto b/easytier/src/proto/common.proto index 91e33d35..8a025d35 100644 --- a/easytier/src/proto/common.proto +++ b/easytier/src/proto/common.proto @@ -65,7 +65,7 @@ message FlagsInConfig { bool disable_sym_hole_punching = 30; // tld dns zone for magic dns - string tld_dns_zone = 31; + string tld_dns_zone = 31 [deprecated = true]; bool p2p_only = 32;