From 8c5eaccb5d654eab6894dfa927a8cb148da55c82 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Mon, 25 Aug 2025 09:04:41 -0700 Subject: [PATCH] Net: replace crate::net::resolver with ngx::async_::resolver ngx's resolver started as this crate's resolver, and includes bug fixes --- Cargo.lock | 12 +-- Cargo.toml | 4 +- src/conf.rs | 2 +- src/net.rs | 1 - src/net/http.rs | 6 +- src/net/peer_conn.rs | 8 +- src/net/resolver.rs | 203 ------------------------------------------- 7 files changed, 16 insertions(+), 220 deletions(-) delete mode 100644 src/net/resolver.rs diff --git a/Cargo.lock b/Cargo.lock index 08455d6..f00a319 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,9 +28,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.21" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +checksum = "c583acf993cf4245c4acb0a2cc2ab1f9cc097de73411bb6d3647ff6af2b1013d" dependencies = [ "serde", ] @@ -484,9 +484,9 @@ dependencies = [ [[package]] name = "nginx-sys" -version = "0.5.0-beta" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c042c2e0b0bfbc6422ef53a4a8275518438b4f5447ec9fd32eb2d31f018fc492" +checksum = "3c85fc3a3b71cdb9269103e3934e989fe5c9f22a34d9068c1b23db1f0349ca79" dependencies = [ "bindgen 0.72.0", "cc", @@ -498,9 +498,9 @@ dependencies = [ [[package]] name = "ngx" -version = "0.5.0-beta" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6e2877ca7004675715d9d2ce03fa0f83b513e6b6cb80391eedc9f10cf7f4c3" +checksum = "3cafa4700c0836055c5c78ba9defc418cea187d92ac65478bb7053a5a1d39775" dependencies = [ "allocator-api2", "async-task", diff --git a/Cargo.toml b/Cargo.toml index 932b351..56a0a7a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,8 +21,8 @@ http-body-util = "0.1.3" http-serde = "2.1.1" hyper = { version = "1.6.0", features = ["client", "http1"] } libc = "0.2.174" -nginx-sys = "0.5.0-beta" -ngx = { version = "0.5.0-beta", features = ["async", "serde", "std"] } +nginx-sys = "0.5.0" +ngx = { version = "0.5.0", features = ["async", "serde", "std"] } openssl = { version = "0.10.73", features = ["bindgen"] } openssl-foreign-types = { package = "foreign-types", version = "0.3" } openssl-sys = { version = "0.9.109", features = ["bindgen"] } diff --git a/src/conf.rs b/src/conf.rs index fce646c..f191a9f 100644 --- a/src/conf.rs +++ b/src/conf.rs @@ -412,7 +412,7 @@ extern "C" fn cmd_issuer_set_external_account_key( return NGX_CONF_DUPLICATE; } - let mut pool = cf.pool(); + let pool = cf.pool(); // NGX_CONF_TAKE2 ensures that args contains 3 elements let args = cf.args(); diff --git a/src/net.rs b/src/net.rs index c7022a9..c98b61a 100644 --- a/src/net.rs +++ b/src/net.rs @@ -6,4 +6,3 @@ pub mod connection; pub mod http; pub mod peer_conn; -pub mod resolver; diff --git a/src/net/http.rs b/src/net/http.rs index 17b42e7..8a712a6 100644 --- a/src/net/http.rs +++ b/src/net/http.rs @@ -15,12 +15,12 @@ use http_body::Body; use http_body_util::BodyExt; use nginx_sys::{ngx_log_t, ngx_resolver_t, NGX_LOG_WARN}; use ngx::allocator::Box; +use ngx::async_::resolver::Resolver; use ngx::async_::spawn; use ngx::ngx_log_error; use thiserror::Error; use super::peer_conn::PeerConnection; -use super::resolver::Resolver; use crate::conf::ssl::NgxSsl; // The largest response we can reasonably expect is a certificate chain, which should not exceed @@ -65,7 +65,7 @@ pub enum HttpClientError { #[error("request error: {0}")] Http(#[from] hyper::Error), #[error("name resolution error: {0}")] - Resolver(super::resolver::Error), + Resolver(ngx::async_::resolver::Error), #[error("connection error: {0}")] Io(io::Error), #[error("invalid uri: {0}")] @@ -74,7 +74,7 @@ pub enum HttpClientError { impl From for HttpClientError { fn from(err: io::Error) -> Self { - match err.downcast::() { + match err.downcast::() { Ok(x) => Self::Resolver(x), Err(x) => Self::Io(x), } diff --git a/src/net/peer_conn.rs b/src/net/peer_conn.rs index 73984de..8e9b427 100644 --- a/src/net/peer_conn.rs +++ b/src/net/peer_conn.rs @@ -17,13 +17,13 @@ use nginx_sys::{ ngx_ssl_shutdown, ngx_ssl_t, ngx_str_t, ngx_url_t, NGX_DEFAULT_POOL_SIZE, NGX_LOG_ERR, NGX_LOG_WARN, }; +use ngx::async_::resolver::Resolver; use ngx::collections::Vec; -use ngx::core::Status; +use ngx::core::{Pool, Status}; use ngx::{ngx_log_debug, ngx_log_error}; use openssl_sys::{SSL_get_verify_result, X509_verify_cert_error_string, X509_V_OK}; use super::connection::{Connection, ConnectionLogError}; -use super::resolver::Resolver; use crate::util::OwnedPool; const ACME_DEFAULT_READ_TIMEOUT: ngx_msec_t = 60000; @@ -165,7 +165,7 @@ impl PeerConnection { url.default_port = if ssl.is_some() { 443 } else { 80 }; url.set_no_resolve(1); - let addr_vec: Vec; + let addr_vec: Vec; if Status(unsafe { nginx_sys::ngx_parse_url(self.pool.as_mut(), &mut url) }) != Status::NGX_OK @@ -183,7 +183,7 @@ impl PeerConnection { self.pc.socklen = addr.socklen; } else { addr_vec = res - .resolve(&url.host, self.pool.as_mut()) + .resolve_name(&url.host, self.pool.as_mut()) .await .map_err(io::Error::other)?; diff --git a/src/net/resolver.rs b/src/net/resolver.rs deleted file mode 100644 index 8407f69..0000000 --- a/src/net/resolver.rs +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright (c) F5, Inc. -// -// This source code is licensed under the Apache License, Version 2.0 license found in the -// LICENSE file in the root directory of this source tree. - -use core::ffi::c_void; -use core::ptr::NonNull; -use std::boxed::Box; -use std::string::{String, ToString}; - -use futures_channel::oneshot::{channel, Sender}; -use nginx_sys::{ - NGX_RESOLVE_FORMERR, NGX_RESOLVE_NOTIMP, NGX_RESOLVE_NXDOMAIN, NGX_RESOLVE_REFUSED, - NGX_RESOLVE_SERVFAIL, NGX_RESOLVE_TIMEDOUT, -}; -use ngx::{ - collections::Vec, - core::Pool, - ffi::{ - ngx_addr_t, ngx_msec_t, ngx_resolve_name, ngx_resolve_start, ngx_resolver_ctx_t, - ngx_resolver_t, ngx_str_t, - }, -}; - -/// Error type for all uses of `Resolver`. -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("No resolver configured")] - NoResolver, - #[error("{0}: resolving `{1}`")] - Resolver(ResolverError, String), - #[error("Allocation failed")] - AllocationFailed, - #[error("Unexpected error: {0}")] - Unexpected(String), -} - -/// These cases directly reflect the NGX_RESOLVE_ error codes, -/// plus a timeout, and a case for an unknown error where a known -/// NGX_RESOLVE_ should be. -#[derive(thiserror::Error, Debug)] -pub enum ResolverError { - #[error("Format error")] - Format, - #[error("Server failure")] - Server, - #[error("Host not found")] - HostNotFound, - #[error("Unimplemented")] - Unimplemented, - #[error("Operation refused")] - Refused, - #[error("Timed Out")] - TimedOut, - #[error("Unknown NGX_RESOLVE error {0}")] - Unknown(isize), -} -/// Convert from the NGX_RESOLVE_ error codes. Fails if code was success. -impl TryFrom for ResolverError { - type Error = (); - fn try_from(code: isize) -> Result { - match code as u32 { - 0 => Err(()), - NGX_RESOLVE_FORMERR => Ok(ResolverError::Format), - NGX_RESOLVE_SERVFAIL => Ok(ResolverError::Server), - NGX_RESOLVE_NXDOMAIN => Ok(ResolverError::HostNotFound), - NGX_RESOLVE_NOTIMP => Ok(ResolverError::Unimplemented), - NGX_RESOLVE_REFUSED => Ok(ResolverError::Refused), - NGX_RESOLVE_TIMEDOUT => Ok(ResolverError::TimedOut), - _ => Ok(ResolverError::Unknown(code)), - } - } -} - -type Res = Result, Error>; - -struct ResCtx<'a> { - ctx: Option<*mut ngx_resolver_ctx_t>, - sender: Option>, - pool: &'a mut Pool, -} - -impl Drop for ResCtx<'_> { - fn drop(&mut self) { - if let Some(ctx) = self.ctx.take() { - unsafe { - nginx_sys::ngx_resolve_name_done(ctx); - } - } - } -} - -fn copy_resolved_addr( - addr: *mut nginx_sys::ngx_resolver_addr_t, - pool: &mut Pool, -) -> Result { - let addr = NonNull::new(addr).ok_or(Error::Unexpected( - "null ngx_resolver_addr_t in ngx_resolver_ctx_t.addrs".to_string(), - ))?; - let addr = unsafe { addr.as_ref() }; - - let sockaddr = pool.alloc(addr.socklen as usize) as *mut nginx_sys::sockaddr; - if sockaddr.is_null() { - Err(Error::AllocationFailed)?; - } - unsafe { - addr.sockaddr - .cast::() - .copy_to_nonoverlapping(sockaddr.cast(), addr.socklen as usize) - }; - - let name = unsafe { ngx_str_t::from_bytes(pool.as_mut(), addr.name.as_bytes()) } - .ok_or(Error::AllocationFailed)?; - - Ok(ngx_addr_t { - sockaddr, - socklen: addr.socklen, - name, - }) -} - -pub struct Resolver { - resolver: NonNull, - timeout: ngx_msec_t, -} - -impl Resolver { - /// Create a new `Resolver` from existing pointer to `ngx_resolver_t` and - /// timeout. - pub fn from_resolver(resolver: NonNull, timeout: ngx_msec_t) -> Self { - Self { resolver, timeout } - } - - /// Resolve a name into a set of addresses. - /// - /// The set of addresses may not be deterministic, because the - /// implementation of the resolver may race multiple DNS requests. - pub async fn resolve(&self, name: &ngx_str_t, pool: &mut Pool) -> Res { - unsafe { - let ctx: *mut ngx_resolver_ctx_t = - ngx_resolve_start(self.resolver.as_ptr(), core::ptr::null_mut()); - if ctx.is_null() { - Err(Error::AllocationFailed)? - } - if ctx as isize == -1 { - Err(Error::NoResolver)? - } - - let (sender, receiver) = channel::(); - let rctx = Box::new(ResCtx { - ctx: Some(ctx), - sender: Some(sender), - pool, - }); - - (*ctx).name = *name; - (*ctx).timeout = self.timeout; - (*ctx).set_cancelable(1); - (*ctx).handler = Some(Self::resolve_handler); - (*ctx).data = Box::into_raw(rctx) as *mut c_void; - - let ret = ngx_resolve_name(ctx); - if ret != 0 { - Err(Error::Resolver( - ResolverError::try_from(ret).expect("nonzero, checked above"), - name.to_string(), - ))?; - } - - receiver - .await - .map_err(|_| Error::Resolver(ResolverError::TimedOut, name.to_string()))? - } - } - - unsafe extern "C" fn resolve_handler(ctx: *mut ngx_resolver_ctx_t) { - let mut rctx = *Box::from_raw((*ctx).data as *mut ResCtx); - rctx.ctx.take(); - if let Some(sender) = rctx.sender.take() { - let _ = sender.send(Self::resolve_result(ctx, rctx.pool)); - } - nginx_sys::ngx_resolve_name_done(ctx); - } - - fn resolve_result(ctx: *mut ngx_resolver_ctx_t, pool: &mut Pool) -> Res { - let ctx = unsafe { ctx.as_ref().unwrap() }; - let s = ctx.state; - if s != 0 { - Err(Error::Resolver( - ResolverError::try_from(s).expect("nonzero, checked above"), - ctx.name.to_string(), - ))?; - } - if ctx.addrs.is_null() { - Err(Error::AllocationFailed)?; - } - let mut out = Vec::new(); - for i in 0..ctx.naddrs { - out.push(copy_resolved_addr(unsafe { ctx.addrs.add(i) }, pool)?); - } - Ok(out) - } -}