use std::sync::Arc;
use std::time::Duration;
use crate::resolver::CoreResolverConfig;
use crate::upstream_handler::{UpstreamManager, UpstreamSpec};
use crate::error::{DnsError, Result};
use crate::dns_error;
use super::{
strategy::QueryStrategy,
engine::SmartDecisionEngine,
resolver::SmartDnsResolver,
};
#[derive(Debug, Clone, PartialEq)]
pub enum LoggerInitStrategy {
None,
Silent,
Debug,
Auto,
}
#[derive(Debug, Clone)]
pub struct DnsResolverBuilder {
config: CoreResolverConfig,
upstream_manager: UpstreamManager,
query_strategy: QueryStrategy,
enable_edns: bool,
current_region: String,
logger_init_strategy: LoggerInitStrategy,
}
impl DnsResolverBuilder {
pub fn new(
query_strategy: QueryStrategy,
enable_edns: bool,
current_region: String,
) -> Self {
let config = CoreResolverConfig::new(
query_strategy,
std::time::Duration::from_secs(5), 2, false, std::time::Duration::from_secs(300), false, std::time::Duration::from_secs(30), 53, 1, true, 4096, false, rat_logger::LevelFilter::Info, false, );
Self {
config,
upstream_manager: UpstreamManager::new(),
query_strategy,
enable_edns,
current_region,
logger_init_strategy: LoggerInitStrategy::Auto, }
}
pub fn query_strategy(mut self, strategy: QueryStrategy) -> Self {
self.query_strategy = strategy;
self
}
pub fn enable_edns(mut self, enable: bool) -> Self {
self.enable_edns = enable;
self
}
pub fn region(mut self, region: impl Into<String>) -> Self {
self.current_region = region.into();
self
}
pub fn add_udp_upstream(mut self, name: impl Into<String>, server: impl Into<String>) -> Self {
let spec = UpstreamSpec::udp(name.into(), server.into());
let _ = self.upstream_manager.add_upstream(spec); self
}
pub fn add_tcp_upstream(mut self, name: impl Into<String>, server: impl Into<String>) -> Self {
let spec = UpstreamSpec::tcp(name.into(), server.into());
if let Err(e) = self.upstream_manager.add_upstream(spec) {
dns_error!("Failed to add TCP upstream: {}", e);
}
self
}
pub fn add_doh_upstream(mut self, name: impl Into<String>, url: impl Into<String>) -> Self {
let spec = UpstreamSpec::doh(name.into(), url.into());
if let Err(e) = self.upstream_manager.add_upstream(spec) {
dns_error!("Failed to add DoH upstream: {}", e);
}
self
}
pub fn add_dot_upstream(mut self, name: impl Into<String>, server: impl Into<String>) -> Self {
let spec = UpstreamSpec::dot(name.into(), server.into());
if let Err(e) = self.upstream_manager.add_upstream(spec) {
dns_error!("Failed to add DoT upstream: {}", e);
}
self
}
pub fn add_upstream(mut self, spec: UpstreamSpec) -> Result<Self> {
self.upstream_manager.add_upstream(spec)?;
Ok(self)
}
pub fn add_upstreams(mut self, specs: Vec<UpstreamSpec>) -> Result<Self> {
for spec in specs {
self.upstream_manager.add_upstream(spec)?;
}
Ok(self)
}
pub fn with_public_dns(mut self) -> Result<Self> {
self = self.add_udp_upstream("阿里DNS", "223.5.5.5");
self = self.add_udp_upstream("腾讯DNS", "119.29.29.29");
self = self.add_udp_upstream("114DNS", "114.114.114.114");
self = self.add_udp_upstream("Google DNS", "8.8.8.8");
self = self.add_udp_upstream("Cloudflare DNS", "1.1.1.1");
self = self.add_doh_upstream("阿里DoH", "https://dns.alidns.com/dns-query");
self = self.add_doh_upstream("腾讯DoH", "https://doh.pub/dns-query");
self = self.add_doh_upstream("Cloudflare DoH", "https://cloudflare-dns.com/dns-query");
self = self.add_dot_upstream("阿里DoT", "223.5.5.5");
self = self.add_dot_upstream("腾讯DoT", "1.12.12.12");
Ok(self)
}
pub fn with_timeout(mut self, timeout: Duration) -> Self {
self.config.default_timeout = timeout;
self
}
pub fn with_round_robin_timeout(mut self, timeout: Duration) -> Self {
if matches!(self.query_strategy, QueryStrategy::RoundRobin) {
self.config.default_timeout = timeout.min(Duration::from_secs(2));
}
self
}
pub fn optimize_for_round_robin(mut self) -> Self {
if matches!(self.query_strategy, QueryStrategy::RoundRobin) {
self.config.default_timeout = Duration::from_millis(1500); self.config.enable_upstream_monitoring = true; self.config.retry_count = 1; self.config.concurrent_queries = self.config.concurrent_queries.max(4); }
self
}
pub fn with_retry_count(mut self, count: usize) -> Self {
self.config.retry_count = count;
self
}
pub fn with_cache(mut self, enable: bool) -> Self {
self.config.enable_cache = enable;
self
}
pub fn with_cache_ttl(mut self, ttl: Duration) -> Self {
self.config.max_cache_ttl = ttl;
self
}
pub fn with_upstream_monitoring(mut self, enable: bool) -> Self {
self.config.enable_upstream_monitoring = enable;
self
}
pub fn with_port(mut self, port: u16) -> Self {
self.config.port = port;
self
}
pub fn with_concurrent_queries(mut self, count: usize) -> Self {
self.config.concurrent_queries = count;
self
}
pub fn with_recursion(mut self, enable: bool) -> Self {
self.config.recursion_desired = enable;
self
}
pub fn with_buffer_size(mut self, size: usize) -> Self {
self.config.buffer_size = size;
self
}
pub fn with_log_level(mut self, level: rat_logger::LevelFilter) -> Self {
self.config.log_level = level;
self
}
pub fn with_dns_log_format(mut self, enable: bool) -> Self {
self.config.enable_dns_log_format = enable;
self
}
pub fn with_verbose_logging(mut self) -> Self {
self.config.log_level = rat_logger::LevelFilter::Debug;
self.config.enable_dns_log_format = true;
self
}
pub fn with_quiet_logging(mut self) -> Self {
self.config.log_level = rat_logger::LevelFilter::Error;
self
}
pub fn with_logger_init_strategy(mut self, strategy: LoggerInitStrategy) -> Self {
self.logger_init_strategy = strategy;
self
}
pub fn disable_logger_init(mut self) -> Self {
self.logger_init_strategy = LoggerInitStrategy::None;
self
}
pub fn with_silent_logger_init(mut self) -> Self {
self.logger_init_strategy = LoggerInitStrategy::Silent;
self
}
pub fn with_debug_logger_init(mut self) -> Self {
self.logger_init_strategy = LoggerInitStrategy::Debug;
self
}
pub fn with_auto_logger_init(mut self) -> Self {
self.logger_init_strategy = LoggerInitStrategy::Auto;
self
}
pub async fn build(self) -> Result<SmartDnsResolver> {
if self.upstream_manager.get_specs().is_empty() {
return Err(DnsError::InvalidConfig("No upstream servers configured".to_string()));
}
match self.logger_init_strategy {
LoggerInitStrategy::None => {
},
LoggerInitStrategy::Silent => {
let _ = crate::logger::init_dns_logger_silent();
},
LoggerInitStrategy::Debug => {
let _ = crate::logger::init_dns_logger(rat_logger::LevelFilter::Debug);
},
LoggerInitStrategy::Auto => {
if self.config.log_level == rat_logger::LevelFilter::Off {
let _ = crate::logger::init_dns_logger_silent();
} else if self.config.enable_dns_log_format {
let _ = crate::logger::init_dns_logger(self.config.log_level);
} else {
let _ = crate::logger::init_dns_logger(self.config.log_level);
}
},
}
for spec in self.upstream_manager.get_specs() {
if spec.name.is_empty() {
return Err(DnsError::InvalidConfig("Upstream name cannot be empty".to_string()));
}
if spec.server.is_empty() {
return Err(DnsError::InvalidConfig(
format!("Server address cannot be empty for upstream '{}'", spec.name)
));
}
}
let decision_engine = match self.query_strategy {
QueryStrategy::Smart | QueryStrategy::Fifo | QueryStrategy::RoundRobin => {
let mut engine = SmartDecisionEngine::new(self.current_region.clone());
for spec in self.upstream_manager.get_specs() {
engine.add_upstream(spec.clone()).await?;
}
Some(Arc::new(engine))
},
};
SmartDnsResolver::new(
self.config,
self.upstream_manager,
decision_engine,
self.query_strategy,
self.enable_edns,
)
}
pub fn upstream_count(&self) -> usize {
self.upstream_manager.get_specs().len()
}
pub fn current_strategy(&self) -> QueryStrategy {
self.query_strategy
}
pub fn is_edns_enabled(&self) -> bool {
self.enable_edns
}
pub fn current_region(&self) -> &str {
&self.current_region
}
pub fn upstream_manager(&self) -> &UpstreamManager {
&self.upstream_manager
}
}