Skip to content

Commit

Permalink
feat(dns): Use HTTP proxy if configured for Dns over HTTPS/TLS
Browse files Browse the repository at this point in the history
  • Loading branch information
erebe committed Jun 26, 2024
1 parent ef1ca16 commit 15db935
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 37 deletions.
46 changes: 32 additions & 14 deletions src/dns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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 {
Expand All @@ -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)),
)));
};

Expand Down Expand Up @@ -114,22 +115,24 @@ impl DnsResolver {
Ok(Self::TrustDns(AsyncResolver::new(
cfg,
opts,
GenericConnector::new(TokioRuntimeProviderWithSoMark::new(so_mark)),
GenericConnector::new(TokioRuntimeProviderWithSoMark::new(proxy, so_mark)),
)))
}
}

#[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 {

Check warning on line 132 in src/dns.rs

View workflow job for this annotation

GitHub Actions / Build - MacOS x86_64

unused variable: `so_mark`

Check warning on line 132 in src/dns.rs

View workflow job for this annotation

GitHub Actions / Build - MacOS aarch64

unused variable: `so_mark`

Check warning on line 132 in src/dns.rs

View workflow job for this annotation

GitHub Actions / Build - Freebsd x86_64

unused variable: `so_mark`

Check warning on line 132 in src/dns.rs

View workflow job for this annotation

GitHub Actions / Build - Freebsd x86

unused variable: `so_mark`

Check warning on line 132 in src/dns.rs

View workflow job for this annotation

GitHub Actions / Build - Android armv7

unused variable: `so_mark`

Check warning on line 132 in src/dns.rs

View workflow job for this annotation

GitHub Actions / Build - Android aarch64

unused variable: `so_mark`

Check warning on line 132 in src/dns.rs

View workflow job for this annotation

GitHub Actions / Build - Windows x86_64

unused variable: `so_mark`

Check warning on line 132 in src/dns.rs

View workflow job for this annotation

GitHub Actions / Build - Windows x86

unused variable: `so_mark`
Self {
runtime: TokioRuntimeProvider::default(),
proxy: proxy.map(Arc::new),
#[cfg(target_os = "linux")]
so_mark,
}
Expand All @@ -154,22 +157,37 @@ 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),
};

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
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(),
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)
Expand Down
48 changes: 25 additions & 23 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
///
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -839,6 +840,25 @@ async fn main() {
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 {
remote_addr: TransportAddr::new(
TransportScheme::from_str(args.remote_addr.scheme()).unwrap(),
Expand All @@ -856,29 +876,11 @@ async fn main() {
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 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,
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 =
Expand Down Expand Up @@ -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,
};
Expand Down

0 comments on commit 15db935

Please sign in to comment.