feat(dns): Use HTTP proxy if configured for Dns over HTTPS/TLS

This commit is contained in:
Σrebe - Romain GERARD 2024-06-26 13:27:36 +02:00
parent ef1ca16e4a
commit 15db9358a0
No known key found for this signature in database
GPG key ID: 7A42B4B97E0332F4
2 changed files with 57 additions and 37 deletions

View file

@ -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)

View file

@ -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,
}; };