feat(dns): Use HTTP proxy if configured for Dns over HTTPS/TLS
This commit is contained in:
parent
ef1ca16e4a
commit
15db9358a0
2 changed files with 57 additions and 37 deletions
46
src/dns.rs
46
src/dns.rs
|
@ -10,6 +10,7 @@ use log::warn;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::net::{IpAddr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
use std::net::{IpAddr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::net::{TcpStream, UdpSocket};
|
use tokio::net::{TcpStream, UdpSocket};
|
||||||
use url::{Host, Url};
|
use url::{Host, Url};
|
||||||
|
@ -38,7 +39,7 @@ impl DnsResolver {
|
||||||
Ok(addrs)
|
Ok(addrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_from_urls(resolvers: &[Url], so_mark: Option<u32>) -> anyhow::Result<Self> {
|
pub fn new_from_urls(resolvers: &[Url], proxy: Option<Url>, so_mark: Option<u32>) -> anyhow::Result<Self> {
|
||||||
if resolvers.is_empty() {
|
if resolvers.is_empty() {
|
||||||
// no dns resolver specified, fall-back to default one
|
// no dns resolver specified, fall-back to default one
|
||||||
let Ok((cfg, mut opts)) = hickory_resolver::system_conf::read_system_conf() else {
|
let Ok((cfg, mut opts)) = hickory_resolver::system_conf::read_system_conf() else {
|
||||||
|
@ -57,7 +58,7 @@ impl DnsResolver {
|
||||||
return Ok(Self::TrustDns(AsyncResolver::new(
|
return Ok(Self::TrustDns(AsyncResolver::new(
|
||||||
cfg,
|
cfg,
|
||||||
opts,
|
opts,
|
||||||
GenericConnector::new(TokioRuntimeProviderWithSoMark::new(so_mark)),
|
GenericConnector::new(TokioRuntimeProviderWithSoMark::new(proxy, so_mark)),
|
||||||
)));
|
)));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -114,7 +115,7 @@ impl DnsResolver {
|
||||||
Ok(Self::TrustDns(AsyncResolver::new(
|
Ok(Self::TrustDns(AsyncResolver::new(
|
||||||
cfg,
|
cfg,
|
||||||
opts,
|
opts,
|
||||||
GenericConnector::new(TokioRuntimeProviderWithSoMark::new(so_mark)),
|
GenericConnector::new(TokioRuntimeProviderWithSoMark::new(proxy, so_mark)),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,14 +123,16 @@ impl DnsResolver {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct TokioRuntimeProviderWithSoMark {
|
pub struct TokioRuntimeProviderWithSoMark {
|
||||||
runtime: TokioRuntimeProvider,
|
runtime: TokioRuntimeProvider,
|
||||||
|
proxy: Option<Arc<Url>>,
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
so_mark: Option<u32>,
|
so_mark: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TokioRuntimeProviderWithSoMark {
|
impl TokioRuntimeProviderWithSoMark {
|
||||||
fn new(so_mark: Option<u32>) -> Self {
|
fn new(proxy: Option<Url>, so_mark: Option<u32>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
runtime: TokioRuntimeProvider::default(),
|
runtime: TokioRuntimeProvider::default(),
|
||||||
|
proxy: proxy.map(Arc::new),
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
so_mark,
|
so_mark,
|
||||||
}
|
}
|
||||||
|
@ -154,22 +157,37 @@ impl RuntimeProvider for TokioRuntimeProviderWithSoMark {
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
let so_mark = self.so_mark;
|
let so_mark = self.so_mark;
|
||||||
|
let proxy = self.proxy.clone();
|
||||||
let socket = async move {
|
let socket = async move {
|
||||||
let host = match server_addr.ip() {
|
let host = match server_addr.ip() {
|
||||||
IpAddr::V4(addr) => Host::<String>::Ipv4(addr),
|
IpAddr::V4(addr) => Host::<String>::Ipv4(addr),
|
||||||
IpAddr::V6(addr) => Host::<String>::Ipv6(addr),
|
IpAddr::V6(addr) => Host::<String>::Ipv6(addr),
|
||||||
};
|
};
|
||||||
|
|
||||||
tcp::connect(
|
if let Some(proxy) = &proxy {
|
||||||
&host,
|
tcp::connect_with_http_proxy(
|
||||||
server_addr.port(),
|
proxy,
|
||||||
so_mark,
|
&host,
|
||||||
Duration::from_secs(10),
|
server_addr.port(),
|
||||||
&DnsResolver::System, // not going to be used as host is directly an ip address
|
so_mark,
|
||||||
)
|
Duration::from_secs(10),
|
||||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
|
&DnsResolver::System, // not going to be used as host is directly an ip address
|
||||||
.map(|s| s.map(AsyncIoTokioAsStd))
|
)
|
||||||
.await
|
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
|
||||||
|
.map(|s| s.map(AsyncIoTokioAsStd))
|
||||||
|
.await
|
||||||
|
} else {
|
||||||
|
tcp::connect(
|
||||||
|
&host,
|
||||||
|
server_addr.port(),
|
||||||
|
so_mark,
|
||||||
|
Duration::from_secs(10),
|
||||||
|
&DnsResolver::System, // not going to be used as host is directly an ip address
|
||||||
|
)
|
||||||
|
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
|
||||||
|
.map(|s| s.map(AsyncIoTokioAsStd))
|
||||||
|
.await
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Box::pin(socket)
|
Box::pin(socket)
|
||||||
|
|
48
src/main.rs
48
src/main.rs
|
@ -248,8 +248,9 @@ struct Client {
|
||||||
/// Dns resolver to use to lookup ips of domain name. Can be specified multiple time
|
/// Dns resolver to use to lookup ips of domain name. Can be specified multiple time
|
||||||
/// Example:
|
/// Example:
|
||||||
/// dns://1.1.1.1 for using udp
|
/// dns://1.1.1.1 for using udp
|
||||||
/// dns+https://1.1.1.1?sni=loudflare-dns.com for using dns over HTTPS
|
/// dns+https://1.1.1.1?sni=cloudflare-dns.com for using dns over HTTPS
|
||||||
/// dns+tls://8.8.8.8?sni=dns.google for using dns over TLS
|
/// dns+tls://8.8.8.8?sni=dns.google for using dns over TLS
|
||||||
|
/// For Dns over HTTPS/TLS if an HTTP proxy is configured, it will be used also
|
||||||
/// To use libc resolver, use
|
/// To use libc resolver, use
|
||||||
/// system://0.0.0.0
|
/// system://0.0.0.0
|
||||||
///
|
///
|
||||||
|
@ -286,7 +287,7 @@ struct Server {
|
||||||
/// Can be specified multiple time
|
/// Can be specified multiple time
|
||||||
/// Example:
|
/// Example:
|
||||||
/// dns://1.1.1.1 for using udp
|
/// dns://1.1.1.1 for using udp
|
||||||
/// dns+https://1.1.1.1?sni=loudflare-dns.com for using dns over HTTPS
|
/// dns+https://1.1.1.1?sni=cloudflare-dns.com for using dns over HTTPS
|
||||||
/// dns+tls://8.8.8.8?sni=dns.google for using dns over TLS
|
/// dns+tls://8.8.8.8?sni=dns.google for using dns over TLS
|
||||||
/// To use libc resolver, use
|
/// To use libc resolver, use
|
||||||
/// system://0.0.0.0
|
/// system://0.0.0.0
|
||||||
|
@ -839,6 +840,25 @@ async fn main() {
|
||||||
panic!("http headers file does not exists: {}", path.display());
|
panic!("http headers file does not exists: {}", path.display());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let http_proxy = if let Some(proxy) = args.http_proxy {
|
||||||
|
let mut proxy = if proxy.starts_with("http://") {
|
||||||
|
Url::parse(&proxy).expect("Invalid http proxy url")
|
||||||
|
} else {
|
||||||
|
Url::parse(&format!("http://{}", proxy)).expect("Invalid http proxy url")
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(login) = args.http_proxy_login {
|
||||||
|
proxy.set_username(login.as_str()).expect("Cannot set http proxy login");
|
||||||
|
}
|
||||||
|
if let Some(password) = args.http_proxy_password {
|
||||||
|
proxy
|
||||||
|
.set_password(Some(password.as_str()))
|
||||||
|
.expect("Cannot set http proxy password");
|
||||||
|
}
|
||||||
|
Some(proxy)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
let mut client_config = WsClientConfig {
|
let mut client_config = WsClientConfig {
|
||||||
remote_addr: TransportAddr::new(
|
remote_addr: TransportAddr::new(
|
||||||
TransportScheme::from_str(args.remote_addr.scheme()).unwrap(),
|
TransportScheme::from_str(args.remote_addr.scheme()).unwrap(),
|
||||||
|
@ -856,29 +876,11 @@ async fn main() {
|
||||||
timeout_connect: Duration::from_secs(10),
|
timeout_connect: Duration::from_secs(10),
|
||||||
websocket_ping_frequency: args.websocket_ping_frequency_sec.unwrap_or(Duration::from_secs(30)),
|
websocket_ping_frequency: args.websocket_ping_frequency_sec.unwrap_or(Duration::from_secs(30)),
|
||||||
websocket_mask_frame: args.websocket_mask_frame,
|
websocket_mask_frame: args.websocket_mask_frame,
|
||||||
http_proxy: if let Some(proxy) = args.http_proxy {
|
|
||||||
let mut proxy = if proxy.starts_with("http://") {
|
|
||||||
Url::parse(&proxy).expect("Invalid http proxy url")
|
|
||||||
} else {
|
|
||||||
Url::parse(&format!("http://{}", proxy)).expect("Invalid http proxy url")
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(login) = args.http_proxy_login {
|
|
||||||
proxy.set_username(login.as_str()).expect("Cannot set http proxy login");
|
|
||||||
}
|
|
||||||
if let Some(password) = args.http_proxy_password {
|
|
||||||
proxy
|
|
||||||
.set_password(Some(password.as_str()))
|
|
||||||
.expect("Cannot set http proxy password");
|
|
||||||
}
|
|
||||||
Some(proxy)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
cnx_pool: None,
|
cnx_pool: None,
|
||||||
tls_reloader: None,
|
tls_reloader: None,
|
||||||
dns_resolver: DnsResolver::new_from_urls(&args.dns_resolver, args.socket_so_mark)
|
dns_resolver: DnsResolver::new_from_urls(&args.dns_resolver, http_proxy.clone(), args.socket_so_mark)
|
||||||
.expect("cannot create dns resolver"),
|
.expect("cannot create dns resolver"),
|
||||||
|
http_proxy,
|
||||||
};
|
};
|
||||||
|
|
||||||
let tls_reloader =
|
let tls_reloader =
|
||||||
|
@ -1296,7 +1298,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: DnsResolver::new_from_urls(&args.dns_resolver, args.socket_so_mark)
|
dns_resolver: DnsResolver::new_from_urls(&args.dns_resolver, None, args.socket_so_mark)
|
||||||
.expect("Cannot create DNS resolver"),
|
.expect("Cannot create DNS resolver"),
|
||||||
restriction_config: args.restrict_config,
|
restriction_config: args.restrict_config,
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue