local: the non-blocking resolver — tokio lookup_host replaces the blocking to_socket_addrs

- resolve_target + local_tcp_dial resolve via tokio::net::lookup_host
  (the blocking std resolver parked the pump on resolver latency —
  the review 003 S-1-adjacent residue); the module doc's blocking
  caveat retired
- semantics unchanged: the dial keeps its try-all-addresses loop, the
  egress keeps first-address (upstream parity)

Verification: cargo test --features local (15 suites green), clippy
--all-targets --all-features -D warnings clean, fmt clean.
This commit is contained in:
glm-5.3-flash committed 2026-09-28 09:13:40 +00:00
1 parent 4d9796be64
commit 086aeb4135
1 file changed
+18 -17
+18 -17
View File
@@ -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<SocketAddr, EgressError> {
/// 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<SocketAddr, EgressError> {
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<TargetHandle, SocksDialError> {
let addrs: Vec<SocketAddr> = 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(|_| ())