mirror of
https://mirror.suhoan.cn/https://github.com/EasyTier/EasyTier.git
synced 2025-12-12 20:57:26 +08:00
Magic DNS and easytier-web improvements (#856)
1. dns add macos system config 2. allow easytier-web serve dashboard and api in same port
This commit is contained in:
@@ -50,6 +50,7 @@ use crate::{
|
||||
use super::{
|
||||
config::{GeneralConfigBuilder, RunConfigBuilder},
|
||||
server::Server,
|
||||
system_config::{OSConfig, SystemConfig},
|
||||
MAGIC_DNS_INSTANCE_ADDR,
|
||||
};
|
||||
|
||||
@@ -64,6 +65,8 @@ pub(super) struct MagicDnsServerInstanceData {
|
||||
|
||||
// zone -> (tunnel remote addr -> route)
|
||||
route_infos: DashMap<String, MultiMap<url::Url, Route>>,
|
||||
|
||||
system_config: Option<Box<dyn SystemConfig>>,
|
||||
}
|
||||
|
||||
impl MagicDnsServerInstanceData {
|
||||
@@ -128,14 +131,14 @@ impl MagicDnsServerInstanceData {
|
||||
}
|
||||
}
|
||||
|
||||
fn do_system_config(&self, _zone: &str) -> Result<(), anyhow::Error> {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
use super::system_config::windows::WindowsDNSManager;
|
||||
let cfg = WindowsDNSManager::new(self.tun_dev.as_ref().unwrap())?;
|
||||
cfg.set_primary_dns(&[self.fake_ip.clone().into()], &[_zone.to_string()])?;
|
||||
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(())
|
||||
}
|
||||
}
|
||||
@@ -323,6 +326,26 @@ pub struct MagicDnsServerInstance {
|
||||
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(target_os = "macos")]
|
||||
{
|
||||
use super::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>,
|
||||
@@ -364,12 +387,14 @@ impl MagicDnsServerInstance {
|
||||
|
||||
let data = Arc::new(MagicDnsServerInstanceData {
|
||||
dns_server,
|
||||
tun_dev,
|
||||
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(data.clone()), "");
|
||||
@@ -393,6 +418,13 @@ impl MagicDnsServerInstance {
|
||||
}
|
||||
|
||||
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.tun_inet.contains(&self.data.fake_ip) && self.data.tun_dev.is_some() {
|
||||
let ifcfg = IfConfiger {};
|
||||
let _ = ifcfg
|
||||
|
||||
135
easytier/src/instance/dns_server/system_config/darwin.rs
Normal file
135
easytier/src/instance/dns_server/system_config/darwin.rs
Normal file
@@ -0,0 +1,135 @@
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
fs::{self, OpenOptions},
|
||||
io::{self, Write},
|
||||
os::unix::fs::PermissionsExt,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use super::{OSConfig, SystemConfig};
|
||||
|
||||
const MAC_RESOLVER_FILE_HEADER: &str = "# Added by easytier\n";
|
||||
const ETC_RESOLVER: &str = "/etc/resolver";
|
||||
const ETC_RESOLV_CONF: &str = "/etc/resolv.conf";
|
||||
|
||||
pub struct DarwinConfigurator {}
|
||||
|
||||
impl DarwinConfigurator {
|
||||
pub fn new() -> Self {
|
||||
DarwinConfigurator {}
|
||||
}
|
||||
|
||||
pub fn do_close(&self) -> io::Result<()> {
|
||||
self.remove_resolver_files(|_| true)
|
||||
}
|
||||
|
||||
pub fn supports_split_dns(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub fn do_set_dns(&self, cfg: &OSConfig) -> io::Result<()> {
|
||||
fs::create_dir_all(ETC_RESOLVER)?;
|
||||
let mut keep = HashSet::new();
|
||||
|
||||
// 写 search.easytier 文件
|
||||
if !cfg.search_domains.is_empty() {
|
||||
let search_file = "search.easytier";
|
||||
keep.insert(search_file.to_string());
|
||||
let mut content = String::from(MAC_RESOLVER_FILE_HEADER);
|
||||
content.push_str("search");
|
||||
for domain in &cfg.search_domains {
|
||||
content.push(' ');
|
||||
content.push_str(domain.trim_end_matches('.'));
|
||||
}
|
||||
content.push('\n');
|
||||
Self::write_resolver_file(search_file, &content)?;
|
||||
}
|
||||
|
||||
// 写 match_domains 文件
|
||||
let mut ns_content = String::from(MAC_RESOLVER_FILE_HEADER);
|
||||
for ns in &cfg.nameservers {
|
||||
ns_content.push_str(&format!("nameserver {}\n", ns));
|
||||
}
|
||||
for domain in &cfg.match_domains {
|
||||
let file_base = domain.trim_end_matches('.');
|
||||
keep.insert(file_base.to_string());
|
||||
Self::write_resolver_file(file_base, &ns_content)?;
|
||||
}
|
||||
// 删除未保留的 resolver 文件
|
||||
self.remove_resolver_files(|domain| !keep.contains(domain))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_resolver_file(file_name: &str, content: &str) -> io::Result<()> {
|
||||
let path = Path::new(ETC_RESOLVER).join(file_name);
|
||||
let mut file = OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.write(true)
|
||||
.open(&path)?;
|
||||
file.set_permissions(fs::Permissions::from_mode(0o644))?;
|
||||
file.write_all(content.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove_resolver_files<F>(&self, should_delete: F) -> io::Result<()>
|
||||
where
|
||||
F: Fn(&str) -> bool,
|
||||
{
|
||||
let entries = match fs::read_dir(ETC_RESOLVER) {
|
||||
Ok(e) => e,
|
||||
Err(e) if e.kind() == io::ErrorKind::NotFound => return Ok(()),
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
for entry in entries {
|
||||
let entry = entry?;
|
||||
let file_type = entry.file_type()?;
|
||||
if !file_type.is_file() {
|
||||
continue;
|
||||
}
|
||||
let name = entry.file_name();
|
||||
let name_str = name.to_string_lossy();
|
||||
if !should_delete(&name_str) {
|
||||
continue;
|
||||
}
|
||||
let full_path = entry.path();
|
||||
let content = fs::read_to_string(&full_path)?;
|
||||
if !content.starts_with(MAC_RESOLVER_FILE_HEADER) {
|
||||
continue;
|
||||
}
|
||||
fs::remove_file(&full_path)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SystemConfig for DarwinConfigurator {
|
||||
fn set_dns(&self, cfg: &OSConfig) -> io::Result<()> {
|
||||
self.do_set_dns(cfg)
|
||||
}
|
||||
|
||||
fn close(&self) -> io::Result<()> {
|
||||
self.do_close()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn set_dns_test() -> io::Result<()> {
|
||||
let config = OSConfig {
|
||||
nameservers: vec!["8.8.8.8".into()],
|
||||
search_domains: vec!["example.com".into()],
|
||||
match_domains: vec!["test.local".into()],
|
||||
};
|
||||
let configurator = DarwinConfigurator::new();
|
||||
|
||||
configurator.set_dns(&config)?;
|
||||
configurator.close()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -3,3 +3,18 @@ pub mod linux;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub mod windows;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub mod darwin;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct OSConfig {
|
||||
pub nameservers: Vec<String>,
|
||||
pub search_domains: Vec<String>,
|
||||
pub match_domains: Vec<String>,
|
||||
}
|
||||
|
||||
pub trait SystemConfig: Send + Sync {
|
||||
fn set_dns(&self, cfg: &OSConfig) -> std::io::Result<()>;
|
||||
fn close(&self) -> std::io::Result<()>;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ use winreg::RegKey;
|
||||
|
||||
use crate::common::ifcfg::RegistryManager;
|
||||
|
||||
use super::{OSConfig, SystemConfig};
|
||||
|
||||
pub fn is_windows_10_or_better() -> io::Result<bool> {
|
||||
let hklm = winreg::enums::HKEY_LOCAL_MACHINE;
|
||||
let key_path = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
|
||||
@@ -150,6 +152,23 @@ impl WindowsDNSManager {
|
||||
}
|
||||
}
|
||||
|
||||
impl SystemConfig for WindowsDNSManager {
|
||||
fn set_dns(&self, cfg: &OSConfig) -> io::Result<()> {
|
||||
self.set_primary_dns(
|
||||
&cfg.nameservers
|
||||
.iter()
|
||||
.map(|s| s.parse::<IpAddr>().unwrap())
|
||||
.collect::<Vec<_>>(),
|
||||
&cfg.match_domains,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn close(&self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use cidr::Ipv4Inet;
|
||||
|
||||
Reference in New Issue
Block a user