mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-09-03 09:35:41 +00:00
remove old magic-dns
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<TcpTunnelConnector>,
|
||||
rpc_stub: Option<Box<dyn MagicDnsServerRpc<Controller = BaseController> + Send>>,
|
||||
peer_mgr: Arc<PeerManager>,
|
||||
tasks: JoinSet<()>,
|
||||
}
|
||||
|
||||
impl MagicDnsClientInstance {
|
||||
pub async fn new(peer_mgr: Arc<PeerManager>) -> Result<Self, anyhow::Error> {
|
||||
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::<MagicDnsServerRpcClientFactory<BaseController>>("".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<PeerManager>,
|
||||
rpc_stub: Box<dyn MagicDnsServerRpc<Controller = BaseController> + 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<IpAddr>,
|
||||
}
|
||||
|
||||
impl RunConfig {
|
||||
pub fn general(&self) -> &GeneralConfig {
|
||||
&self.general
|
||||
}
|
||||
|
||||
pub fn zones(&self) -> &Zone {
|
||||
&self.zones
|
||||
}
|
||||
|
||||
pub fn excluded_forward_nameservers(&self) -> &Vec<IpAddr> {
|
||||
&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<String>,
|
||||
|
||||
#[builder(setter(into, strip_option), default = None)]
|
||||
listen_udp: Option<String>,
|
||||
}
|
||||
|
||||
impl GeneralConfig {
|
||||
pub fn listen_tcp(&self) -> &Option<String> {
|
||||
&self.listen_tcp
|
||||
}
|
||||
|
||||
pub fn listen_udp(&self) -> &Option<String> {
|
||||
&self.listen_udp
|
||||
}
|
||||
}
|
||||
|
||||
pub type Zone = HashMap<String, Vec<Record>>; // 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<rr::Name> {
|
||||
let name = rr::Name::from_str(self.name.as_str())?;
|
||||
Ok(name)
|
||||
}
|
||||
|
||||
fn rr_type(&self) -> rr::RecordType {
|
||||
self.rr_type
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Record> for rr::Record {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: Record) -> Result<Self, Self::Error> {
|
||||
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<Self, Self::Error> {
|
||||
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::<Vec<_>>();
|
||||
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::<RunConfig>(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(())
|
||||
}
|
||||
}
|
||||
@@ -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.";
|
||||
@@ -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<MagicDnsClientInstance>,
|
||||
server: Option<MagicDnsServerInstance>,
|
||||
peer_mgr: Arc<PeerManager>,
|
||||
tun_dev: Option<String>,
|
||||
tun_inet: Ipv4Inet,
|
||||
fake_ip: Ipv4Addr,
|
||||
}
|
||||
|
||||
impl DnsRunner {
|
||||
pub fn new(
|
||||
peer_mgr: Arc<PeerManager>,
|
||||
tun_dev: Option<String>,
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<CatalogRequestHandler>,
|
||||
catalog: Arc<RwLock<Catalog>>,
|
||||
general_config: GeneralConfig,
|
||||
udp_local_addr: Option<SocketAddr>,
|
||||
tcp_local_addr: Option<SocketAddr>,
|
||||
tasks: JoinSet<()>,
|
||||
}
|
||||
|
||||
struct CatalogRequestHandler {
|
||||
catalog: Arc<RwLock<Catalog>>,
|
||||
}
|
||||
|
||||
impl CatalogRequestHandler {
|
||||
fn new(catalog: Arc<RwLock<Catalog>>) -> 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<R: ResponseHandler>(
|
||||
&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<InMemoryAuthority> {
|
||||
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<Self> {
|
||||
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::<Vec<_>>()
|
||||
.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<SocketAddr> {
|
||||
self.udp_local_addr
|
||||
}
|
||||
|
||||
pub fn tcp_local_addr(&self) -> Option<SocketAddr> {
|
||||
self.tcp_local_addr
|
||||
}
|
||||
|
||||
pub async fn register_udp_socket(&mut self, address: String) -> Result<SocketAddr> {
|
||||
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<dyn AuthorityObject>) {
|
||||
self.catalog.write().await.upsert(name, vec![authority]);
|
||||
}
|
||||
|
||||
pub async fn remove(&self, name: &LowerName) -> Option<Vec<Arc<dyn AuthorityObject>>> {
|
||||
self.catalog.write().await.remove(name)
|
||||
}
|
||||
|
||||
pub async fn update<R: ResponseHandler>(
|
||||
&self,
|
||||
update: &Request,
|
||||
response_edns: Option<Edns>,
|
||||
response_handle: R,
|
||||
) -> io::Result<ResponseInfo> {
|
||||
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<R: ResponseHandler>(
|
||||
&self,
|
||||
request: &Request,
|
||||
response_edns: Option<Edns>,
|
||||
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(())
|
||||
}
|
||||
}
|
||||
@@ -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<String>,
|
||||
tun_ip: Ipv4Addr,
|
||||
fake_ip: Ipv4Addr,
|
||||
my_peer_id: PeerId,
|
||||
|
||||
// zone -> (tunnel remote addr -> route)
|
||||
route_infos: DashMap<String, MultiMap<url::Url, Route>>,
|
||||
|
||||
system_config: Option<Box<dyn SystemConfigurator>>,
|
||||
}
|
||||
|
||||
impl MagicDnsServerInstanceData {
|
||||
pub async fn update_dns_records<'a, T: Iterator<Item = &'a Route>>(
|
||||
&self,
|
||||
routes: T,
|
||||
zone: &str,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let mut records: Vec<Record> = 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<HandshakeResponse> {
|
||||
Ok(Default::default())
|
||||
}
|
||||
|
||||
async fn heartbeat(
|
||||
&self,
|
||||
_ctrl: Self::Controller,
|
||||
_input: Void,
|
||||
) -> crate::proto::rpc_types::error::Result<Void> {
|
||||
Ok(Default::default())
|
||||
}
|
||||
|
||||
async fn update_dns_record(
|
||||
&self,
|
||||
ctrl: Self::Controller,
|
||||
input: UpdateDnsRecordRequest,
|
||||
) -> crate::proto::rpc_types::error::Result<Void> {
|
||||
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<GetDnsRecordResponse> {
|
||||
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<Mutex<Vec<u8>>>,
|
||||
}
|
||||
|
||||
trait RecordIter<'a>: Iterator<Item = &'a hickory_proto::rr::Record> + Send + 'a {}
|
||||
impl<'a, T> RecordIter<'a> for T where T: Iterator<Item = &'a hickory_proto::rr::Record> + 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<ResponseInfo> {
|
||||
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<TunnelInfo>,
|
||||
) -> Result<Option<TunnelInfo>, anyhow::Error> {
|
||||
tracing::info!(?tunnel_info, "New client connected");
|
||||
Ok(tunnel_info)
|
||||
}
|
||||
|
||||
async fn on_client_disconnected(&self, tunnel_info: Option<TunnelInfo>) {
|
||||
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<TcpTunnelListener>,
|
||||
pub(super) data: Arc<MagicDnsServerInstanceData>,
|
||||
peer_mgr: Arc<PeerManager>,
|
||||
tun_inet: Ipv4Inet,
|
||||
}
|
||||
|
||||
fn get_system_config(
|
||||
_tun_name: Option<&str>,
|
||||
) -> Result<Option<Box<dyn SystemConfigurator>>, 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<PeerManager>,
|
||||
tun_dev: Option<String>,
|
||||
tun_inet: Ipv4Inet,
|
||||
fake_ip: Ipv4Addr,
|
||||
) -> Result<Self, anyhow::Error> {
|
||||
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");
|
||||
}
|
||||
}
|
||||
@@ -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<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.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::<Ipv4Addr>().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::<Vec<_>>();
|
||||
|
||||
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));
|
||||
}
|
||||
@@ -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<Box<dyn Any + 'static + Send>>,
|
||||
#[cfg(feature = "magic-dns")]
|
||||
magic_dns: Option<MagicDnsContainer>,
|
||||
}
|
||||
|
||||
#[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<DnsRunner>) -> 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<T: 'static + Send>(ctx: T) -> Self {
|
||||
Self {
|
||||
nic_ctx: Some(Box::new(ctx)),
|
||||
#[cfg(feature = "magic-dns")]
|
||||
magic_dns: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tun")]
|
||||
type ArcNicCtx = Arc<Mutex<Option<NicCtxContainer>>>;
|
||||
type ArcNicCtx = Arc<Mutex<Option<Box<dyn Any + 'static + Send>>>>;
|
||||
|
||||
pub struct InstanceRpcServerHook {
|
||||
rpc_portal_whitelist: Vec<IpCidr>,
|
||||
@@ -666,16 +607,6 @@ impl Instance {
|
||||
arc_nic_ctx: ArcNicCtx,
|
||||
packet_recv: Arc<Mutex<PacketRecvChanReceiver>>,
|
||||
) {
|
||||
#[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<PeerManager>,
|
||||
tun_dev: Option<String>,
|
||||
tun_ip: Ipv4Inet,
|
||||
) -> Option<DnsRunner> {
|
||||
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<DnsRunner>,
|
||||
) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
pub mod dns_server;
|
||||
#[allow(clippy::module_inception)]
|
||||
pub mod instance;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user