mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-10-14 16:36:16 -08:00
* Add shared virtual NIC core with per-member routing Share a named TUN across instances while keeping unnamed devices dedicated. Track member addresses, routes, and MTU, and reject overlapping destinations except the common Magic DNS host route. Dispatch packets between the physical TUN and member tunnels. Keep IPv4 and ordinary IPv6 source translation for mobile wrong-source traffic. Update platform configuration and runtime host integration, with unit and network namespace coverage for routing and lifecycle. Use smoltcp for packet parsing and checksum updates. Keep core CI matrix jobs independent. Reserve native inbound flows before source translations allocate their wire tuples. Keep active reservations under table pressure and leave forwarded subnet traffic outside the local flow table. Complete interface claim updates on the shared runtime and retain member registrations until the transaction commits. Keep address, route, and MTU cleanup ordered after cancelled callers, including device initialization. Project mobile VPN addresses, routes, and DNS from admitted native members. Prefer unchanged attached claims and report conflicting members individually. Apply each member's complete initial claims before opening the shared fd, so conflicts leave healthy members usable. Cover inbound IPv4/IPv6 collisions, cancellation during native changes and dispatcher acknowledgments, admission priority, and atomic mobile claim rejection with regression tests. Use scoped TestNetwork fixtures for shared TUN bridge setup and cleanup. Resolve public IPv6 namespace aliases through the lab's actual names so the new tests remain isolated under parallel upstream test runs. * Configure one mobile VPN from running TUN instances Use admitted native members to configure VpnService and attach its single fd to the core shared TUN. Wait for attachment completion and record the actual attached members before refreshing VPN state. Report rejected members in the GUI and preserve the VPN for unchanged healthy members. Reconcile ordinary configuration notifications without invalidating an arbitrary member. Invalidate attachments on instance lifecycle changes, including restarts that reuse an instance ID. Forward public IPv6 changes alongside proxy route changes to refresh the platform VPN configuration. Exclude confirmed IPv4-less members from readiness gating while retaining retries for unavailable status and pending addresses. Quick Settings stops all running TUN members and restores that group on the next start, skipping removed configurations and no_tun networks. Keep the FFI set_tun_fd call scoped to one instance and report attachment errors. Cover mobile configuration, member rejection and replacement, attachment completion, collection notifications, IPv4-less members, and tile stop/start restoration in GUI regression tests. Validate with 36 mobile VPN tests, vue-tsc --noEmit, Android GUI cargo check, workspace clippy, and shared TUN network namespace tests. * fix(tun): preserve shared NIC ownership and failure invariants Keep mobile address and route claims in dispatcher tables without applying desktop interface configuration to synthetic TUN names. Limit the Magic DNS overlap exemption to route claims and remove the shared route retry that bypassed claim accounting after invalidation. Invalidate and stop dispatchers on failed mobile TUN replacement or I/O. Bound TUN writes so stalled devices release pending source updates and member cleanup instead of retaining an unusable shared dispatcher. Treat missing Darwin and Windows addresses and routes as successful deletions while preserving permission and other configuration errors. Add regressions for claim conflicts, mobile configuration, failed TUN replacement, stalled writes, and platform deletion error handling. * fix(tun): forward single-member shared NIC traffic unchanged Deliver TUN packets directly to the sole attached member without destination matching or creating source translations. Keep native flow reservations so a later member cannot collide with existing connections, and continue applying mappings created before other members leave. Reuse flow preparation with the original source address. Preserve the desktop subnet forwarding bypass, including when the flow table is full. IPv4 fragments and IPv6 extension headers remain unchanged on flows that need no translation. Cover unknown sources and destinations, first and later IP fragments, native flows established before another member joins, and retained translations after returning to one member. Document the forwarding behavior and retain rejection coverage for multiple attached members. * fix(tun): preserve wildcard UDP replies on shared member ports Mark flow reservations created by incoming traffic separately from outgoing mappings. When a wildcard UDP socket replies with another member's source address, reuse the incoming reservation after source correction and preserve its service port and inbound destination. Keep exact outgoing mappings and outgoing flow collision handling ahead of this rule. Incoming UDP sessions take priority when packet tuples cannot distinguish a reply from a new client using identical endpoints. Cover IPv4 and IPv6 replies, checksum updates, repeated inbound traffic, existing outgoing mappings, TCP collisions, and single-to-shared member transitions with regression tests. * test(tun): wait for IPv6 activation in mobile configuration test Assert that the configured IPv6 address is registered before forwarding starts, then wait up to five seconds for the kernel to make it usable. Netlink acknowledges address creation before asynchronous IPv6 activation completes, so an immediate bind can fail under CI parallelism. Disable automatic link-local address generation in the test namespace to keep startup IPv6 control traffic out of member-routing assertions. Validation: the original test failed 40 of 320 runs at 32-way parallelism. The updated test passed all 320 runs; workspace fmt and clippy also pass.
515 lines
17 KiB
Rust
515 lines
17 KiB
Rust
// 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::{
|
|
MAGIC_DNS_INSTANCE_SOCKET_ADDR,
|
|
config::{GeneralConfigBuilder, RunConfigBuilder},
|
|
server::Server,
|
|
system_config::{OSConfig, SystemConfig},
|
|
};
|
|
use crate::{
|
|
common::{
|
|
error::Error as EtError,
|
|
global_ctx::ArcGlobalCtx,
|
|
ifcfg::{IfConfiger, IfConfiguerTrait},
|
|
netns::NetNS,
|
|
},
|
|
instance::dns_server::{
|
|
config::{Record, RecordBuilder, RecordType},
|
|
server::build_authority,
|
|
},
|
|
proto::{
|
|
common::{TunnelInfo, Void},
|
|
magic_dns::{
|
|
DnsRecord, DnsRecordA, DnsRecordList, GetDnsRecordResponse, HandshakeRequest,
|
|
HandshakeResponse, MagicDnsServerRpc, MagicDnsServerRpcServer, UpdateDnsRecordRequest,
|
|
dns_record::{self},
|
|
},
|
|
rpc::standalone::{
|
|
RpcServerHook, RuntimeRpcListener, StandAloneServer, runtime_rpc_listener,
|
|
},
|
|
rpc_types::controller::{BaseController, Controller},
|
|
},
|
|
};
|
|
use anyhow::Context;
|
|
use cidr::Ipv4Inet;
|
|
use easytier_core::gateway::magic_dns::{
|
|
MagicDnsQuery, MagicDnsQueryResolver, MagicDnsRecordStore, MagicDnsResolverRegistration,
|
|
MagicDnsRoute,
|
|
};
|
|
use easytier_core::instance::CorePacketPlane;
|
|
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 std::sync::Mutex;
|
|
use std::{collections::BTreeMap, io, net::Ipv4Addr, str::FromStr, sync::Arc, time::Duration};
|
|
|
|
pub(super) struct MagicDnsServerInstanceData {
|
|
dns_server: Server,
|
|
tun_dev: Option<String>,
|
|
net_ns: NetNS,
|
|
fake_ip: Ipv4Addr,
|
|
manage_fake_ip_route: bool,
|
|
route_store: MagicDnsRecordStore,
|
|
record_apply: tokio::sync::Mutex<()>,
|
|
|
|
system_config: Option<Box<dyn SystemConfig>>,
|
|
}
|
|
|
|
impl MagicDnsServerInstanceData {
|
|
pub async fn update_dns_records<'a, T: Iterator<Item = &'a MagicDnsRoute>>(
|
|
&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 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) {
|
|
let snapshot = self.route_store.snapshot();
|
|
for (zone, routes) in &snapshot.zones {
|
|
if let Err(e) = self.update_dns_records(routes.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::<&MagicDnsRoute>(), 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(&OSConfig {
|
|
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 _apply = self.record_apply.lock().await;
|
|
let zone = input.zone.clone();
|
|
let remote_addr: url::Url = remote_addr.clone().into();
|
|
let routes = input
|
|
.routes
|
|
.into_iter()
|
|
.map(|route| MagicDnsRoute {
|
|
hostname: route.hostname,
|
|
ipv4_addr: route.ipv4_addr.unwrap_or_default().address.map(Into::into),
|
|
})
|
|
.collect();
|
|
let zone_removed =
|
|
self.route_store
|
|
.replace_client_routes(zone.clone(), remote_addr.to_string(), routes);
|
|
|
|
if zone_removed {
|
|
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 (zone, routes) in self.route_store.snapshot().zones {
|
|
let mut dns_records = DnsRecordList::default();
|
|
for route in routes {
|
|
dns_records.records.push(DnsRecord {
|
|
record: Some(dns_record::Record::A(DnsRecordA {
|
|
name: format!("{}.{}", route.hostname, zone),
|
|
value: route.ipv4_addr.map(Into::into),
|
|
ttl: 1,
|
|
})),
|
|
});
|
|
}
|
|
ret.insert(zone, 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 {
|
|
async fn resolve_query_inner(&self, query: MagicDnsQuery) -> Option<Vec<u8>> {
|
|
let request = Request::new(
|
|
MessageRequest::from_bytes(&query.payload).ok()?,
|
|
query.source,
|
|
hickory_proto::xfer::Protocol::Udp,
|
|
);
|
|
let response = Arc::new(Mutex::new(Vec::with_capacity(512)));
|
|
|
|
self.dns_server
|
|
.read_catalog()
|
|
.await
|
|
.handle_request(
|
|
&request,
|
|
ResponseWrapper {
|
|
response: response.clone(),
|
|
},
|
|
)
|
|
.await;
|
|
|
|
Arc::into_inner(response)?.into_inner().ok()
|
|
}
|
|
}
|
|
|
|
#[async_trait::async_trait]
|
|
impl MagicDnsQueryResolver for MagicDnsServerInstanceData {
|
|
async fn resolve(&self, query: MagicDnsQuery) -> Option<Vec<u8>> {
|
|
self.resolve_query_inner(query).await
|
|
}
|
|
}
|
|
|
|
#[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 _apply = self.record_apply.lock().await;
|
|
let remote_addr: url::Url = remote_addr.into();
|
|
for zone in self.route_store.remove_client(remote_addr.as_ref()) {
|
|
self.keep_zone_authoritative(&zone).await;
|
|
}
|
|
self.update().await;
|
|
}
|
|
}
|
|
|
|
pub struct MagicDnsServerInstance {
|
|
_rpc_server: StandAloneServer<RuntimeRpcListener>,
|
|
pub(super) data: Arc<MagicDnsServerInstanceData>,
|
|
packet_filter: MagicDnsResolverRegistration,
|
|
tun_inet: Ipv4Inet,
|
|
}
|
|
|
|
fn get_system_config(
|
|
_tun_name: Option<&str>,
|
|
) -> Result<Option<Box<dyn SystemConfig>>, anyhow::Error> {
|
|
#[cfg(target_os = "windows")]
|
|
{
|
|
use super::system_config::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 super::system_config::darwin::DarwinConfigurator;
|
|
return Ok(Some(Box::new(DarwinConfigurator::new())));
|
|
}
|
|
|
|
#[allow(unreachable_code)]
|
|
Ok(None)
|
|
}
|
|
|
|
impl MagicDnsServerInstance {
|
|
async fn add_fake_ip_route(
|
|
tun_dev_name: &str,
|
|
fake_ip: Ipv4Addr,
|
|
net_ns: &NetNS,
|
|
cost: Option<i32>,
|
|
) -> Result<(), anyhow::Error> {
|
|
let ifcfg = IfConfiger::default();
|
|
let _guard = net_ns.guard();
|
|
match ifcfg.add_ipv4_route(tun_dev_name, fake_ip, 32, cost).await {
|
|
Err(EtError::IOError(err)) if err.kind() == io::ErrorKind::AlreadyExists => {
|
|
ifcfg.remove_ipv4_route(tun_dev_name, fake_ip, 32).await?;
|
|
ifcfg
|
|
.add_ipv4_route(tun_dev_name, fake_ip, 32, cost)
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
ret => ret.map_err(Into::into),
|
|
}
|
|
}
|
|
|
|
async fn remove_fake_ip_route(tun_dev_name: &str, fake_ip: Ipv4Addr, net_ns: &NetNS) {
|
|
let ifcfg = IfConfiger::default();
|
|
let _guard = net_ns.guard();
|
|
if let Err(err) = ifcfg.remove_ipv4_route(tun_dev_name, fake_ip, 32).await {
|
|
tracing::warn!(
|
|
?err,
|
|
?tun_dev_name,
|
|
?fake_ip,
|
|
"remove magic dns route failed"
|
|
);
|
|
}
|
|
}
|
|
|
|
pub(crate) async fn new(
|
|
packet_plane: Arc<CorePacketPlane>,
|
|
global_ctx: ArcGlobalCtx,
|
|
tun_dev: Option<String>,
|
|
tun_inet: Ipv4Inet,
|
|
fake_ip: Ipv4Addr,
|
|
) -> Result<Self, anyhow::Error> {
|
|
Self::new_inner(packet_plane, global_ctx, tun_dev, tun_inet, fake_ip, true).await
|
|
}
|
|
|
|
pub(crate) async fn new_with_external_fake_ip_route(
|
|
packet_plane: Arc<CorePacketPlane>,
|
|
global_ctx: ArcGlobalCtx,
|
|
tun_dev: Option<String>,
|
|
tun_inet: Ipv4Inet,
|
|
fake_ip: Ipv4Addr,
|
|
) -> Result<Self, anyhow::Error> {
|
|
Self::new_inner(packet_plane, global_ctx, tun_dev, tun_inet, fake_ip, false).await
|
|
}
|
|
|
|
async fn new_inner(
|
|
packet_plane: Arc<CorePacketPlane>,
|
|
global_ctx: ArcGlobalCtx,
|
|
tun_dev: Option<String>,
|
|
tun_inet: Ipv4Inet,
|
|
fake_ip: Ipv4Addr,
|
|
manage_fake_ip_route: bool,
|
|
) -> Result<Self, anyhow::Error> {
|
|
let tcp_listener = runtime_rpc_listener(MAGIC_DNS_INSTANCE_SOCKET_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 manage_fake_ip_route
|
|
&& !tun_inet.contains(&fake_ip)
|
|
&& let Some(tun_dev_name) = &tun_dev
|
|
{
|
|
let cost = if cfg!(target_os = "windows") {
|
|
Some(4)
|
|
} else {
|
|
None
|
|
};
|
|
Self::add_fake_ip_route(tun_dev_name, fake_ip, &global_ctx.net_ns, cost).await?;
|
|
}
|
|
|
|
let data = Arc::new(MagicDnsServerInstanceData {
|
|
dns_server,
|
|
tun_dev: tun_dev.clone(),
|
|
net_ns: global_ctx.net_ns.clone(),
|
|
fake_ip,
|
|
manage_fake_ip_route,
|
|
route_store: MagicDnsRecordStore::default(),
|
|
record_apply: tokio::sync::Mutex::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());
|
|
|
|
// Use configured tld_dns_zone or fall back to DEFAULT_ET_DNS_ZONE if empty
|
|
let flags = global_ctx.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")??;
|
|
|
|
// Install the resolver only after all fallible initialization has
|
|
// completed, so construction failure cannot leave a managed pipeline
|
|
// registration that never reaches async cleanup.
|
|
let packet_filter = packet_plane
|
|
.register_magic_dns_resolver(fake_ip, data.clone())
|
|
.await;
|
|
|
|
Ok(Self {
|
|
_rpc_server: rpc_server,
|
|
data,
|
|
packet_filter,
|
|
tun_inet,
|
|
})
|
|
}
|
|
|
|
pub async fn clean_env(&self) {
|
|
if let Some(configer) = &self.data.system_config {
|
|
let ret = configer.close();
|
|
if let Err(e) = ret {
|
|
tracing::error!("Failed to close system config: {:?}", e);
|
|
}
|
|
}
|
|
|
|
if self.data.manage_fake_ip_route
|
|
&& !self.tun_inet.contains(&self.data.fake_ip)
|
|
&& let Some(tun_dev_name) = &self.data.tun_dev
|
|
{
|
|
Self::remove_fake_ip_route(tun_dev_name, self.data.fake_ip, &self.data.net_ns).await;
|
|
}
|
|
|
|
self.packet_filter.close().await;
|
|
}
|
|
}
|
|
|
|
impl Drop for MagicDnsServerInstance {
|
|
fn drop(&mut self) {
|
|
println!("MagicDnsServerInstance dropped");
|
|
}
|
|
}
|