diff --git a/src/dns.rs b/src/dns.rs index b36df8d..6a7a2ea 100644 --- a/src/dns.rs +++ b/src/dns.rs @@ -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) -> anyhow::Result { + pub fn new_from_urls(resolvers: &[Url], proxy: Option, so_mark: Option) -> anyhow::Result { 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>, #[cfg(target_os = "linux")] so_mark: Option, } impl TokioRuntimeProviderWithSoMark { - fn new(so_mark: Option) -> Self { + fn new(proxy: Option, so_mark: Option) -> Self { Self { runtime: TokioRuntimeProvider::default(), + proxy: proxy.map(Arc::new), #[cfg(target_os = "linux")] so_mark, } @@ -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::::Ipv4(addr), IpAddr::V6(addr) => Host::::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) diff --git a/src/main.rs b/src/main.rs index ce2ec63..a85c09d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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,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(), @@ -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 = @@ -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, };