Dont use libc dns resolver by default

+ By default libc dns resolution is blocking.
    Which force async runtime to spawn blocking thread for it
    which lead to heavy memory usage
This commit is contained in:
Σrebe - Romain GERARD 2024-01-02 19:32:47 +01:00
parent 5ae552f713
commit b705484d9f
No known key found for this signature in database
GPG key ID: 7A42B4B97E0332F4
5 changed files with 59 additions and 35 deletions

View file

@ -150,6 +150,8 @@ Options:
dns://1.1.1.1 for using udp dns://1.1.1.1 for using udp
dns+https://1.1.1.1 for using dns over HTTPS dns+https://1.1.1.1 for using dns over HTTPS
dns+tls://8.8.8.8 for using dns over TLS dns+tls://8.8.8.8 for using dns over TLS
To use libc resolver, use
system://0.0.0.0
-r, --restrict-http-upgrade-path-prefix <RESTRICT_HTTP_UPGRADE_PATH_PREFIX> -r, --restrict-http-upgrade-path-prefix <RESTRICT_HTTP_UPGRADE_PATH_PREFIX>
Server will only accept connection from if this specific path prefix is used during websocket upgrade. Server will only accept connection from if this specific path prefix is used during websocket upgrade.
Useful if you specify in the client a custom path prefix and you want the server to only allow this one. Useful if you specify in the client a custom path prefix and you want the server to only allow this one.

View file

@ -10,7 +10,7 @@ make_release $VERSION $FORCE="":
git add Cargo.* git add Cargo.*
git commit -m 'Bump version v'$VERSION git commit -m 'Bump version v'$VERSION
git tag $FORCE v$VERSION -m 'version v'$VERSION git tag $FORCE v$VERSION -m 'version v'$VERSION
git push git push $FORCE
git push $FORCE origin v$VERSION git push $FORCE origin v$VERSION
@just docker_release v$VERSION @just docker_release v$VERSION

View file

@ -13,6 +13,7 @@ use futures_util::{stream, TryStreamExt};
use hickory_resolver::config::{NameServerConfig, ResolverConfig, ResolverOpts}; use hickory_resolver::config::{NameServerConfig, ResolverConfig, ResolverOpts};
use hyper::header::HOST; use hyper::header::HOST;
use hyper::http::{HeaderName, HeaderValue}; use hyper::http::{HeaderName, HeaderValue};
use log::{debug, warn};
use parking_lot::Mutex; use parking_lot::Mutex;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
@ -202,6 +203,8 @@ struct Server {
/// dns://1.1.1.1 for using udp /// dns://1.1.1.1 for using udp
/// dns+https://1.1.1.1 for using dns over HTTPS /// dns+https://1.1.1.1 for using dns over HTTPS
/// dns+tls://8.8.8.8 for using dns over TLS /// dns+tls://8.8.8.8 for using dns over TLS
/// To use libc resolver, use
/// system://0.0.0.0
#[arg(long, verbatim_doc_comment)] #[arg(long, verbatim_doc_comment)]
dns_resolver: Option<Vec<Url>>, dns_resolver: Option<Vec<Url>>,
@ -519,7 +522,7 @@ impl Debug for WsServerConfig {
} }
} }
#[derive(Clone, Debug)] #[derive(Clone)]
pub struct WsClientConfig { pub struct WsClientConfig {
pub remote_addr: (Host<String>, u16), pub remote_addr: (Host<String>, u16),
pub socket_so_mark: Option<u32>, pub socket_so_mark: Option<u32>,
@ -533,6 +536,7 @@ pub struct WsClientConfig {
pub websocket_mask_frame: bool, pub websocket_mask_frame: bool,
pub http_proxy: Option<Url>, pub http_proxy: Option<Url>,
cnx_pool: Option<bb8::Pool<WsClientConfig>>, cnx_pool: Option<bb8::Pool<WsClientConfig>>,
pub dns_resolver: DnsResolver,
} }
impl WsClientConfig { impl WsClientConfig {
@ -626,6 +630,12 @@ async fn main() {
websocket_mask_frame: args.websocket_mask_frame, websocket_mask_frame: args.websocket_mask_frame,
http_proxy: args.http_proxy, http_proxy: args.http_proxy,
cnx_pool: None, cnx_pool: None,
dns_resolver: if let Ok(resolver) = hickory_resolver::AsyncResolver::tokio_from_system_conf() {
DnsResolver::TrustDns(resolver)
} else {
debug!("Fall-backing to system dns resolver");
DnsResolver::System
},
}; };
let pool = bb8::Pool::builder() let pool = bb8::Pool::builder()
@ -654,7 +664,7 @@ async fn main() {
remote.1, remote.1,
cfg.socket_so_mark, cfg.socket_so_mark,
cfg.timeout_connect, cfg.timeout_connect,
&DnsResolver::System, &cfg.dns_resolver,
) )
.await .await
}; };
@ -673,7 +683,7 @@ async fn main() {
let cfg = client_config.clone(); let cfg = client_config.clone();
let remote = tunnel.remote.clone(); let remote = tunnel.remote.clone();
let connect_to_dest = |_| async { let connect_to_dest = |_| async {
udp::connect(&remote.0, remote.1, cfg.timeout_connect, &DnsResolver::System).await udp::connect(&remote.0, remote.1, cfg.timeout_connect, &cfg.dns_resolver).await
}; };
if let Err(err) = if let Err(err) =
@ -690,9 +700,8 @@ async fn main() {
let connect_to_dest = |remote: (Host, u16)| { let connect_to_dest = |remote: (Host, u16)| {
let so_mark = cfg.socket_so_mark; let so_mark = cfg.socket_so_mark;
let timeout = cfg.timeout_connect; let timeout = cfg.timeout_connect;
async move { let dns_resolver = &cfg.dns_resolver;
tcp::connect(&remote.0, remote.1, so_mark, timeout, &DnsResolver::System).await async move { tcp::connect(&remote.0, remote.1, so_mark, timeout, dns_resolver).await }
}
}; };
if let Err(err) = if let Err(err) =
@ -841,32 +850,45 @@ async fn main() {
}; };
let dns_resolver = match args.dns_resolver { let dns_resolver = match args.dns_resolver {
None => DnsResolver::System, None => {
Some(resolvers) => { if let Ok(resolver) = hickory_resolver::AsyncResolver::tokio_from_system_conf() {
let mut cfg = ResolverConfig::new(); DnsResolver::TrustDns(resolver)
for resolver in resolvers { } else {
let (protocol, port) = match resolver.scheme() { warn!("Fall-backing to system dns resolver. You should consider specifying a dns resolver. To avoid performance issue");
"dns" => (hickory_resolver::config::Protocol::Udp, resolver.port().unwrap_or(53)), DnsResolver::System
"dns+https" => (hickory_resolver::config::Protocol::Https, resolver.port().unwrap_or(853)),
"dns+tls" => (hickory_resolver::config::Protocol::Tls, resolver.port().unwrap_or(12)),
_ => panic!("invalid protocol for dns resolver"),
};
let sock = match resolver.host().unwrap() {
Host::Domain(host) => match Host::parse(host) {
Ok(Host::Ipv4(ip)) => SocketAddr::V4(SocketAddrV4::new(ip, port)),
Ok(Host::Ipv6(ip)) => SocketAddr::V6(SocketAddrV6::new(ip, port, 0, 0)),
Ok(Host::Domain(_)) | Err(_) => {
panic!("Dns resolver must be an ip address, got {}", host)
}
},
Host::Ipv4(ip) => SocketAddr::V4(SocketAddrV4::new(ip, port)),
Host::Ipv6(ip) => SocketAddr::V6(SocketAddrV6::new(ip, port, 0, 0)),
};
cfg.add_name_server(NameServerConfig::new(sock, protocol))
} }
}
Some(resolvers) => {
if resolvers.iter().any(|r| r.scheme() == "system") {
DnsResolver::System
} else {
let mut cfg = ResolverConfig::new();
for resolver in resolvers {
let (protocol, port) = match resolver.scheme() {
"dns" => (hickory_resolver::config::Protocol::Udp, resolver.port().unwrap_or(53)),
"dns+https" => {
(hickory_resolver::config::Protocol::Https, resolver.port().unwrap_or(443))
}
"dns+tls" => (hickory_resolver::config::Protocol::Tls, resolver.port().unwrap_or(853)),
_ => panic!("invalid protocol for dns resolver"),
};
let sock = match resolver.host().unwrap() {
Host::Domain(host) => match Host::parse(host) {
Ok(Host::Ipv4(ip)) => SocketAddr::V4(SocketAddrV4::new(ip, port)),
Ok(Host::Ipv6(ip)) => SocketAddr::V6(SocketAddrV6::new(ip, port, 0, 0)),
Ok(Host::Domain(_)) | Err(_) => {
panic!("Dns resolver must be an ip address, got {}", host)
}
},
Host::Ipv4(ip) => SocketAddr::V4(SocketAddrV4::new(ip, port)),
Host::Ipv6(ip) => SocketAddr::V6(SocketAddrV6::new(ip, port, 0, 0)),
};
cfg.add_name_server(NameServerConfig::new(sock, protocol))
}
let opts = ResolverOpts::default(); let opts = ResolverOpts::default();
DnsResolver::TrustDns(hickory_resolver::AsyncResolver::tokio(cfg, opts)) DnsResolver::TrustDns(hickory_resolver::AsyncResolver::tokio(cfg, opts))
}
} }
}; };
let server_config = WsServerConfig { let server_config = WsServerConfig {

View file

@ -102,11 +102,12 @@ pub async fn connect_with_http_proxy(
port: u16, port: u16,
so_mark: Option<u32>, so_mark: Option<u32>,
connect_timeout: Duration, connect_timeout: Duration,
dns_resolver: &DnsResolver,
) -> Result<TcpStream, anyhow::Error> { ) -> Result<TcpStream, anyhow::Error> {
let proxy_host = proxy.host().context("Cannot parse proxy host")?.to_owned(); let proxy_host = proxy.host().context("Cannot parse proxy host")?.to_owned();
let proxy_port = proxy.port_or_known_default().unwrap_or(80); let proxy_port = proxy.port_or_known_default().unwrap_or(80);
let mut socket = connect(&proxy_host, proxy_port, so_mark, connect_timeout, &DnsResolver::System).await?; let mut socket = connect(&proxy_host, proxy_port, so_mark, connect_timeout, dns_resolver).await?;
info!("Connected to http proxy {}:{}", proxy_host, proxy_port); info!("Connected to http proxy {}:{}", proxy_host, proxy_port);
let authorization = if let Some((user, password)) = proxy.password().map(|p| (proxy.username(), p)) { let authorization = if let Some((user, password)) = proxy.password().map(|p| (proxy.username(), p)) {

View file

@ -3,7 +3,6 @@ mod io;
pub mod server; pub mod server;
mod tls_reloader; mod tls_reloader;
use crate::dns::DnsResolver;
use crate::{tcp, tls, LocalProtocol, LocalToRemote, WsClientConfig}; use crate::{tcp, tls, LocalProtocol, LocalToRemote, WsClientConfig};
use async_trait::async_trait; use async_trait::async_trait;
use bb8::ManageConnection; use bb8::ManageConnection;
@ -127,9 +126,9 @@ impl ManageConnection for WsClientConfig {
let timeout = self.timeout_connect; let timeout = self.timeout_connect;
let tcp_stream = if let Some(http_proxy) = &self.http_proxy { let tcp_stream = if let Some(http_proxy) = &self.http_proxy {
tcp::connect_with_http_proxy(http_proxy, host, *port, so_mark, timeout).await? tcp::connect_with_http_proxy(http_proxy, host, *port, so_mark, timeout, &self.dns_resolver).await?
} else { } else {
tcp::connect(host, *port, so_mark, timeout, &DnsResolver::System).await? tcp::connect(host, *port, so_mark, timeout, &self.dns_resolver).await?
}; };
match &self.tls { match &self.tls {