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::net::{IpAddr, SocketAddr, SocketAddrV4, SocketAddrV6};
use std::pin::Pin;
use std::sync::Arc;
use std::time::Duration;
use tokio::net::{TcpStream, UdpSocket};
use url::{Host, Url};
@ -38,7 +39,7 @@ impl DnsResolver {
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() {
// no dns resolver specified, fall-back to default one
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(
cfg,
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(
cfg,
opts,
GenericConnector::new(TokioRuntimeProviderWithSoMark::new(so_mark)),
GenericConnector::new(TokioRuntimeProviderWithSoMark::new(proxy, so_mark)),
)))
}
}
@ -122,14 +123,16 @@ impl DnsResolver {
#[derive(Clone)]
pub struct TokioRuntimeProviderWithSoMark {
runtime: TokioRuntimeProvider,
proxy: Option<Arc<Url>>,
#[cfg(target_os = "linux")]
so_mark: Option<u32>,
}
impl TokioRuntimeProviderWithSoMark {
fn new(so_mark: Option<u32>) -> Self {
fn new(proxy: Option<Url>, so_mark: Option<u32>) -> Self {
Self {
runtime: TokioRuntimeProvider::default(),
proxy: proxy.map(Arc::new),
#[cfg(target_os = "linux")]
so_mark,
}
@ -154,12 +157,26 @@ impl RuntimeProvider for TokioRuntimeProviderWithSoMark {
#[cfg(target_os = "linux")]
let so_mark = self.so_mark;
let proxy = self.proxy.clone();
let socket = async move {
let host = match server_addr.ip() {
IpAddr::V4(addr) => Host::<String>::Ipv4(addr),
IpAddr::V6(addr) => Host::<String>::Ipv6(addr),
};
if let Some(proxy) = &proxy {
tcp::connect_with_http_proxy(
proxy,
&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
} else {
tcp::connect(
&host,
server_addr.port(),
@ -170,6 +187,7 @@ impl RuntimeProvider for TokioRuntimeProviderWithSoMark {
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
.map(|s| s.map(AsyncIoTokioAsStd))
.await
}
};
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
/// Example:
/// 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
/// For Dns over HTTPS/TLS if an HTTP proxy is configured, it will be used also
/// To use libc resolver, use
/// system://0.0.0.0
///
@ -286,7 +287,7 @@ struct Server {
/// Can be specified multiple time
/// Example:
/// 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
/// To use libc resolver, use
/// system://0.0.0.0
@ -839,24 +840,7 @@ async fn main() {
panic!("http headers file does not exists: {}", path.display());
}
}
let mut client_config = WsClientConfig {
remote_addr: TransportAddr::new(
TransportScheme::from_str(args.remote_addr.scheme()).unwrap(),
args.remote_addr.host().unwrap().to_owned(),
args.remote_addr.port_or_known_default().unwrap(),
tls,
)
.unwrap(),
socket_so_mark: args.socket_so_mark,
http_upgrade_path_prefix,
http_upgrade_credentials: args.http_upgrade_credentials,
http_headers: args.http_headers.into_iter().filter(|(k, _)| k != HOST).collect(),
http_headers_file: args.http_headers_file,
http_header_host: host_header,
timeout_connect: Duration::from_secs(10),
websocket_ping_frequency: args.websocket_ping_frequency_sec.unwrap_or(Duration::from_secs(30)),
websocket_mask_frame: args.websocket_mask_frame,
http_proxy: if let Some(proxy) = args.http_proxy {
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 {
@ -874,11 +858,29 @@ async fn main() {
Some(proxy)
} else {
None
},
};
let mut client_config = WsClientConfig {
remote_addr: TransportAddr::new(
TransportScheme::from_str(args.remote_addr.scheme()).unwrap(),
args.remote_addr.host().unwrap().to_owned(),
args.remote_addr.port_or_known_default().unwrap(),
tls,
)
.unwrap(),
socket_so_mark: args.socket_so_mark,
http_upgrade_path_prefix,
http_upgrade_credentials: args.http_upgrade_credentials,
http_headers: args.http_headers.into_iter().filter(|(k, _)| k != HOST).collect(),
http_headers_file: args.http_headers_file,
http_header_host: host_header,
timeout_connect: Duration::from_secs(10),
websocket_ping_frequency: args.websocket_ping_frequency_sec.unwrap_or(Duration::from_secs(30)),
websocket_mask_frame: args.websocket_mask_frame,
cnx_pool: 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"),
http_proxy,
};
let tls_reloader =
@ -1296,7 +1298,7 @@ async fn main() {
timeout_connect: Duration::from_secs(10),
websocket_mask_frame: args.websocket_mask_frame,
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"),
restriction_config: args.restrict_config,
};