diff --git a/src/local/mod.rs b/src/local/mod.rs index 1ebf4d4..83324b9 100644 --- a/src/local/mod.rs +++ b/src/local/mod.rs @@ -30,7 +30,8 @@ //! the client-gone teardown. //! //! Resolution is the provider's job (ADR-012): domain targets/destinations -//! resolve here, at the seam — the ssh `-D` semantic. +//! resolve here, at the seam — the ssh `-D` semantic, via the +//! non-blocking [`tokio::net::lookup_host`] resolver. use std::io::ErrorKind; use std::net::SocketAddr; @@ -49,19 +50,22 @@ use crate::{ /// Resolve a [`TargetAddr`] to a `SocketAddr` (egress-side DNS; the /// ssh `-D` semantic — ADR-012: resolution is the provider's job). IP -/// targets resolve directly; domain targets resolve via the blocking -/// resolver (`std::net::ToSocketAddrs` — the POC #2 `LocalEgress` -/// shape; the first address wins). -pub(crate) fn resolve_target(addr: &TargetAddr) -> Result { +/// targets resolve directly; domain targets resolve via +/// [`tokio::net::lookup_host`] (the non-blocking resolver — review 003 +/// S-1's blocking-`to_socket_addrs` residue retired; the first address +/// wins, upstream parity). +pub(crate) async fn resolve_target(addr: &TargetAddr) -> Result { match addr { TargetAddr::Ip(sa) => Ok(*sa), TargetAddr::Domain(domain, port) => { - use std::net::ToSocketAddrs; - (domain.as_str(), *port) - .to_socket_addrs() + let addrs = tokio::net::lookup_host((domain.as_str(), *port)) + .await .map_err(|e| EgressError::Unreachable(format!("resolve {domain}: {e}")))? .next() - .ok_or_else(|| EgressError::Unreachable(format!("resolve {domain}: no addresses"))) + .ok_or_else(|| { + EgressError::Unreachable(format!("resolve {domain}: no addresses")) + })?; + Ok(addrs) } } } @@ -96,13 +100,10 @@ pub fn tcp_dial() -> DialFn { async fn local_tcp_dial(target: TargetAddr) -> Result { let addrs: Vec = match &target { TargetAddr::Ip(sa) => vec![*sa], - TargetAddr::Domain(domain, port) => { - use std::net::ToSocketAddrs; - (domain.as_str(), *port) - .to_socket_addrs() - .map_err(|e| SocksDialError::HostUnreachable(format!("resolve {domain}: {e}")))? - .collect() - } + TargetAddr::Domain(domain, port) => tokio::net::lookup_host((domain.as_str(), *port)) + .await + .map_err(|e| SocksDialError::HostUnreachable(format!("resolve {domain}: {e}")))? + .collect(), }; if addrs.is_empty() { return Err(SocksDialError::HostUnreachable(format!( @@ -176,7 +177,7 @@ impl Egress for LocalEgress { fn send(&mut self, addr: TargetAddr, payload: Bytes) -> BoxFuture<'_, Result<(), EgressError>> { let sock = Arc::clone(&self.sock); Box::pin(async move { - let dest = resolve_target(&addr)?; + let dest = resolve_target(&addr).await?; sock.send_to(&payload, dest) .await .map(|_| ())