feat: Allow to specify DNS resolver on client too - fix #290 for windows
This commit is contained in:
parent
ae54cd549a
commit
880aa257a1
2 changed files with 74 additions and 53 deletions
58
src/dns.rs
58
src/dns.rs
|
@ -1,5 +1,9 @@
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use hickory_resolver::config::{NameServerConfig, ResolverConfig, ResolverOpts};
|
||||||
use hickory_resolver::TokioAsyncResolver;
|
use hickory_resolver::TokioAsyncResolver;
|
||||||
|
use log::warn;
|
||||||
use std::net::{IpAddr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
use std::net::{IpAddr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
||||||
|
use url::{Host, Url};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum DnsResolver {
|
pub enum DnsResolver {
|
||||||
|
@ -24,4 +28,58 @@ impl DnsResolver {
|
||||||
|
|
||||||
Ok(addrs)
|
Ok(addrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_from_urls(resolvers: &[Url]) -> anyhow::Result<Self> {
|
||||||
|
if resolvers.is_empty() {
|
||||||
|
// no dns resolver specified, fall-back to default one
|
||||||
|
let Ok((cfg, mut opts)) = hickory_resolver::system_conf::read_system_conf() else {
|
||||||
|
warn!("Fall-backing to system dns resolver. You should consider specifying a dns resolver. To avoid performance issue");
|
||||||
|
return Ok(Self::System);
|
||||||
|
};
|
||||||
|
|
||||||
|
opts.timeout = std::time::Duration::from_secs(1);
|
||||||
|
// Windows end-up with too many dns resolvers, which causes a performance issue
|
||||||
|
// https://github.com/hickory-dns/hickory-dns/issues/1968
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
opts.num_concurrent_reqs = 20;
|
||||||
|
}
|
||||||
|
return Ok(Self::TrustDns(hickory_resolver::AsyncResolver::tokio(cfg, opts)));
|
||||||
|
};
|
||||||
|
|
||||||
|
// if one is specified as system, use the default one from libc
|
||||||
|
if resolvers.iter().any(|r| r.scheme() == "system") {
|
||||||
|
return Ok(Self::System);
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, use the specified resolvers
|
||||||
|
let mut cfg = ResolverConfig::new();
|
||||||
|
for resolver in resolvers.iter() {
|
||||||
|
let (protocol, port) = match resolver.scheme() {
|
||||||
|
"dns" => (hickory_resolver::config::Protocol::Udp, resolver.port().unwrap_or(53)),
|
||||||
|
"dns+https" => (hickory_resolver::config::Protocol::Https, resolver.port().unwrap_or(443)),
|
||||||
|
"dns+tls" => (hickory_resolver::config::Protocol::Tls, resolver.port().unwrap_or(853)),
|
||||||
|
_ => return Err(anyhow!("invalid protocol for dns resolver")),
|
||||||
|
};
|
||||||
|
let host = resolver
|
||||||
|
.host()
|
||||||
|
.ok_or_else(|| anyhow!("Invalid dns resolver host: {}", resolver))?;
|
||||||
|
let sock = match host {
|
||||||
|
Host::Domain(host) => match Host::parse(host) {
|
||||||
|
Ok(Host::Ipv4(ip)) => SocketAddr::V4(SocketAddrV4::new(ip, port)),
|
||||||
|
Ok(Host::Ipv6(ip)) => SocketAddr::V6(SocketAddrV6::new(ip, port, 0, 0)),
|
||||||
|
Ok(Host::Domain(_)) | Err(_) => {
|
||||||
|
return Err(anyhow!("Dns resolver must be an ip address, got {}", host));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Host::Ipv4(ip) => SocketAddr::V4(SocketAddrV4::new(ip, port)),
|
||||||
|
Host::Ipv6(ip) => SocketAddr::V6(SocketAddrV6::new(ip, port, 0, 0)),
|
||||||
|
};
|
||||||
|
cfg.add_name_server(NameServerConfig::new(sock, protocol))
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut opts = ResolverOpts::default();
|
||||||
|
opts.timeout = std::time::Duration::from_secs(1);
|
||||||
|
Ok(Self::TrustDns(hickory_resolver::AsyncResolver::tokio(cfg, opts)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
69
src/main.rs
69
src/main.rs
|
@ -16,10 +16,9 @@ use anyhow::anyhow;
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use futures_util::{stream, TryStreamExt};
|
use futures_util::{stream, TryStreamExt};
|
||||||
use hickory_resolver::config::{NameServerConfig, ResolverConfig, ResolverOpts};
|
|
||||||
use hyper::header::HOST;
|
use hyper::header::HOST;
|
||||||
use hyper::http::{HeaderName, HeaderValue};
|
use hyper::http::{HeaderName, HeaderValue};
|
||||||
use log::{debug, warn};
|
use log::debug;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -245,6 +244,18 @@ struct Client {
|
||||||
/// The certificate will be automatically reloaded if it changes
|
/// The certificate will be automatically reloaded if it changes
|
||||||
#[arg(long, value_name = "FILE_PATH", verbatim_doc_comment)]
|
#[arg(long, value_name = "FILE_PATH", verbatim_doc_comment)]
|
||||||
tls_private_key: Option<PathBuf>,
|
tls_private_key: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// Dns resolver to use to lookup ips of domain name. Can be specified multiple time
|
||||||
|
/// Example:
|
||||||
|
/// dns://1.1.1.1 for using udp
|
||||||
|
/// dns+https://1.1.1.1 for using dns over HTTPS
|
||||||
|
/// dns+tls://8.8.8.8 for using dns over TLS
|
||||||
|
/// To use libc resolver, use
|
||||||
|
/// system://0.0.0.0
|
||||||
|
///
|
||||||
|
/// **WARN** On windows you may want to specify explicitly the DNS resolver to avoid excessive DNS queries
|
||||||
|
#[arg(long, verbatim_doc_comment)]
|
||||||
|
dns_resolver: Vec<Url>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(clap::Args, Debug)]
|
#[derive(clap::Args, Debug)]
|
||||||
|
@ -280,7 +291,7 @@ struct Server {
|
||||||
/// To use libc resolver, use
|
/// To use libc resolver, use
|
||||||
/// system://0.0.0.0
|
/// system://0.0.0.0
|
||||||
#[arg(long, verbatim_doc_comment)]
|
#[arg(long, verbatim_doc_comment)]
|
||||||
dns_resolver: Option<Vec<Url>>,
|
dns_resolver: Vec<Url>,
|
||||||
|
|
||||||
/// Server will only accept connection from the specified tunnel information.
|
/// Server will only accept connection from the specified tunnel information.
|
||||||
/// Can be specified multiple time
|
/// Can be specified multiple time
|
||||||
|
@ -866,12 +877,7 @@ async fn main() {
|
||||||
},
|
},
|
||||||
cnx_pool: None,
|
cnx_pool: None,
|
||||||
tls_reloader: None,
|
tls_reloader: None,
|
||||||
dns_resolver: if let Ok(resolver) = hickory_resolver::AsyncResolver::tokio_from_system_conf() {
|
dns_resolver: DnsResolver::new_from_urls(&args.dns_resolver).expect("cannot create dns resolver"),
|
||||||
DnsResolver::TrustDns(resolver)
|
|
||||||
} else {
|
|
||||||
debug!("Fall-backing to system dns resolver");
|
|
||||||
DnsResolver::System
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let tls_reloader =
|
let tls_reloader =
|
||||||
|
@ -1257,49 +1263,6 @@ async fn main() {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let dns_resolver = match args.dns_resolver {
|
|
||||||
None => {
|
|
||||||
if let Ok(resolver) = hickory_resolver::AsyncResolver::tokio_from_system_conf() {
|
|
||||||
DnsResolver::TrustDns(resolver)
|
|
||||||
} else {
|
|
||||||
warn!("Fall-backing to system dns resolver. You should consider specifying a dns resolver. To avoid performance issue");
|
|
||||||
DnsResolver::System
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(resolvers) => {
|
|
||||||
if resolvers.iter().any(|r| r.scheme() == "system") {
|
|
||||||
DnsResolver::System
|
|
||||||
} else {
|
|
||||||
let mut cfg = ResolverConfig::new();
|
|
||||||
for resolver in resolvers {
|
|
||||||
let (protocol, port) = match resolver.scheme() {
|
|
||||||
"dns" => (hickory_resolver::config::Protocol::Udp, resolver.port().unwrap_or(53)),
|
|
||||||
"dns+https" => {
|
|
||||||
(hickory_resolver::config::Protocol::Https, resolver.port().unwrap_or(443))
|
|
||||||
}
|
|
||||||
"dns+tls" => (hickory_resolver::config::Protocol::Tls, resolver.port().unwrap_or(853)),
|
|
||||||
_ => panic!("invalid protocol for dns resolver"),
|
|
||||||
};
|
|
||||||
let sock = match resolver.host().unwrap() {
|
|
||||||
Host::Domain(host) => match Host::parse(host) {
|
|
||||||
Ok(Host::Ipv4(ip)) => SocketAddr::V4(SocketAddrV4::new(ip, port)),
|
|
||||||
Ok(Host::Ipv6(ip)) => SocketAddr::V6(SocketAddrV6::new(ip, port, 0, 0)),
|
|
||||||
Ok(Host::Domain(_)) | Err(_) => {
|
|
||||||
panic!("Dns resolver must be an ip address, got {}", host)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Host::Ipv4(ip) => SocketAddr::V4(SocketAddrV4::new(ip, port)),
|
|
||||||
Host::Ipv6(ip) => SocketAddr::V6(SocketAddrV6::new(ip, port, 0, 0)),
|
|
||||||
};
|
|
||||||
cfg.add_name_server(NameServerConfig::new(sock, protocol))
|
|
||||||
}
|
|
||||||
|
|
||||||
let opts = ResolverOpts::default();
|
|
||||||
DnsResolver::TrustDns(hickory_resolver::AsyncResolver::tokio(cfg, opts))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let restrictions = if let Some(path) = &args.restrict_config {
|
let restrictions = if let Some(path) = &args.restrict_config {
|
||||||
RestrictionsRules::from_config_file(path).expect("Cannot parse restriction file")
|
RestrictionsRules::from_config_file(path).expect("Cannot parse restriction file")
|
||||||
} else {
|
} else {
|
||||||
|
@ -1332,7 +1295,7 @@ async fn main() {
|
||||||
timeout_connect: Duration::from_secs(10),
|
timeout_connect: Duration::from_secs(10),
|
||||||
websocket_mask_frame: args.websocket_mask_frame,
|
websocket_mask_frame: args.websocket_mask_frame,
|
||||||
tls: tls_config,
|
tls: tls_config,
|
||||||
dns_resolver,
|
dns_resolver: DnsResolver::new_from_urls(&args.dns_resolver).expect("Cannot create DNS resolver"),
|
||||||
restriction_config: args.restrict_config,
|
restriction_config: args.restrict_config,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue