[pbs-devel] [POC proxmox-backup 4/4] client: http: Use custom resolver for statically linked binary

Christian Ebner c.ebner at proxmox.com
Sat Sep 28 11:42:56 CEST 2024


The dependency on the `getaddrinfo` based `GaiResolver` used by
default for the `HttpClient` is not suitable for the statically
linked binary of the `proxmox-backup-client`, because of the
dependency on glibc NSS libraries, as described in glibc's FAQs [0].

As a workaround, conditionally compile the binary using the `hickory-dns`
resolver.

[0] https://sourceware.org/glibc/wiki/FAQ#Even_statically_linked_programs_need_some_shared_libraries_which_is_not_acceptable_for_me.__What_can_I_do.3F

Suggested-by: Thomas Lamprecht <t.lamprecht at proxmox.com>
Signed-off-by: Christian Ebner <c.ebner at proxmox.com>
---
 Cargo.toml                    |  6 +++
 pbs-client/Cargo.toml         |  1 +
 pbs-client/src/http_client.rs | 81 +++++++++++++++++++++++++++++++++--
 3 files changed, 84 insertions(+), 4 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index fc1409d66..792226258 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -160,6 +160,7 @@ walkdir = "2"
 xdg = "2.2"
 zstd = { version = "0.12", features = [ "bindgen" ] }
 zstd-safe = "6.0"
+hickory-resolver = { git = "https://github.com/hickory-dns/hickory-dns", tag = "v0.24.1", default-features = false }
 
 [dependencies]
 anyhow.workspace = true
@@ -254,6 +255,11 @@ proxmox-rrd-api-types.workspace = true
 # Local path overrides
 # NOTE: You must run `cargo update` after changing this for it to take effect!
 [patch.crates-io]
+hickory-proto = { git = "https://github.com/hickory-dns/hickory-dns", tag = "v0.24.1" }
+ipconfig = { git = "https://github.com/liranringel/ipconfig", tag = "0.3.2" }
+widestring = { git = "https://github.com/starkat99/widestring-rs", tag = "v1.1.0" }
+windows-sys = { git = "https://github.com/microsoft/windows-rs", tag = "0.48.0" }
+winreg = { git = "https://github.com/gentoo90/winreg-rs", tag = "v0.50.0" }
 
 #proxmox-apt = { path = "../proxmox/proxmox-apt" }
 #proxmox-apt-api-types = { path = "../proxmox/proxmox-apt-api-types" }
diff --git a/pbs-client/Cargo.toml b/pbs-client/Cargo.toml
index 00c18b83a..785e7c4de 100644
--- a/pbs-client/Cargo.toml
+++ b/pbs-client/Cargo.toml
@@ -29,6 +29,7 @@ tokio = { workspace = true, features = [ "fs", "signal" ] }
 tokio-stream.workspace = true
 tower-service.workspace = true
 xdg.workspace = true
+hickory-resolver = { workspace = true, features = [ "system-config", "tokio-runtime" ] }
 
 pathpatterns.workspace = true
 
diff --git a/pbs-client/src/http_client.rs b/pbs-client/src/http_client.rs
index 8ae5edaa0..cdec8e42a 100644
--- a/pbs-client/src/http_client.rs
+++ b/pbs-client/src/http_client.rs
@@ -7,6 +7,8 @@ use futures::*;
 use http::header::HeaderValue;
 use http::Uri;
 use http::{Request, Response};
+#[cfg(not(target_feature = "crt-static"))]
+use hyper::client::connect::dns::GaiResolver;
 use hyper::client::{Client, HttpConnector};
 use hyper::Body;
 use openssl::{
@@ -32,6 +34,74 @@ use pbs_api_types::{Authid, RateLimitConfig, Userid};
 use super::pipe_to_stream::PipeToSendStream;
 use super::PROXMOX_BACKUP_TCP_KEEPALIVE_TIME;
 
+#[cfg(not(target_feature = "crt-static"))]
+type DnsResolver = GaiResolver;
+
+#[cfg(target_feature = "crt-static")]
+type DnsResolver = resolver::HickoryDnsResolver;
+
+#[cfg(target_feature = "crt-static")]
+mod resolver {
+    use std::net::SocketAddr;
+    use std::pin::Pin;
+    use std::sync::Arc;
+    use std::task::{Context, Poll};
+
+    use futures::Future;
+    use hickory_resolver::error::ResolveError;
+    use hickory_resolver::lookup_ip::LookupIpIntoIter;
+    use hickory_resolver::TokioAsyncResolver;
+    use hyper::client::connect::dns::Name;
+    use tower_service::Service;
+
+    pub(crate) struct SocketAddrIter {
+        inner: LookupIpIntoIter,
+    }
+
+    impl Iterator for SocketAddrIter {
+        type Item = SocketAddr;
+
+        fn next(&mut self) -> Option<Self::Item> {
+            self.inner.next().map(|ip_addr| SocketAddr::new(ip_addr, 0))
+        }
+    }
+
+    #[derive(Clone)]
+    pub(crate) struct HickoryDnsResolver {
+        inner: Arc<TokioAsyncResolver>,
+    }
+
+    impl HickoryDnsResolver {
+        pub(crate) fn new() -> Self {
+            Self {
+                inner: Arc::new(TokioAsyncResolver::tokio_from_system_conf().unwrap()),
+            }
+        }
+    }
+
+    impl Service<Name> for HickoryDnsResolver {
+        type Response = SocketAddrIter;
+        type Error = ResolveError;
+        type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
+
+        fn poll_ready(&mut self, _ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+            Poll::Ready(Ok(()))
+        }
+
+        fn call(&mut self, name: Name) -> Self::Future {
+            let inner = self.inner.clone();
+            Box::pin(async move {
+                inner
+                    .lookup_ip(name.as_str())
+                    .await
+                    .map(|r| SocketAddrIter {
+                        inner: r.into_iter(),
+                    })
+            })
+        }
+    }
+}
+
 /// Timeout used for several HTTP operations that are expected to finish quickly but may block in
 /// certain error conditions. Keep it generous, to avoid false-positive under high load.
 const HTTP_TIMEOUT: Duration = Duration::from_secs(2 * 60);
@@ -133,7 +203,7 @@ impl Default for HttpClientOptions {
 
 /// HTTP(S) API client
 pub struct HttpClient {
-    client: Client<HttpsConnector>,
+    client: Client<HttpsConnector<DnsResolver>>,
     server: String,
     port: u16,
     fingerprint: Arc<Mutex<Option<String>>>,
@@ -364,7 +434,8 @@ impl HttpClient {
             ssl_connector_builder.set_verify(openssl::ssl::SslVerifyMode::NONE);
         }
 
-        let mut httpc = HttpConnector::new();
+        let resolver = DnsResolver::new();
+        let mut httpc = HttpConnector::new_with_resolver(resolver);
         httpc.set_nodelay(true); // important for h2 download performance!
         httpc.enforce_http(false); // we want https...
 
@@ -525,7 +596,9 @@ impl HttpClient {
             _options: options,
         })
     }
+}
 
+impl HttpClient {
     /// Login
     ///
     /// Login is done on demand, so this is only required if you need
@@ -814,7 +887,7 @@ impl HttpClient {
     }
 
     async fn credentials(
-        client: Client<HttpsConnector>,
+        client: Client<HttpsConnector<DnsResolver>>,
         server: String,
         port: u16,
         username: Userid,
@@ -859,7 +932,7 @@ impl HttpClient {
     }
 
     async fn api_request(
-        client: Client<HttpsConnector>,
+        client: Client<HttpsConnector<DnsResolver>>,
         req: Request<Body>,
     ) -> Result<Value, Error> {
         Self::api_response(
-- 
2.39.5





More information about the pbs-devel mailing list