From a42592ab055e2fdad6b6545de5a849a3fefeeff1 Mon Sep 17 00:00:00 2001 From: Jon Kinney Date: Mon, 4 May 2026 23:31:34 -0500 Subject: [PATCH] fix(dns): resolve hostnames via the OS resolver instead of pure DNS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hickory_resolver's TokioResolver only consults /etc/resolv.conf and queries upstream DNS servers — which means it can't see /etc/hosts, mDNS (Avahi/Bonjour), NetBIOS, or anything else in the system's full name-resolution stack. On a typical home LAN there's no DNS server that knows about peer machine names, so users had to fall back to typing IP addresses, which broke the moment they moved their setup to a different network. Swap to tokio::net::lookup_host, which calls getaddrinfo (or GetAddrInfoEx on Windows). That walks /etc/nsswitch.conf on Linux (picking up Avahi-resolved .local names, /etc/hosts, and DNS), uses Bonjour for .local on macOS, and the full Windows resolver on Windows. A Bonjour hostname like "JKMBP-M4-Max.local" now resolves on every modern network without explicit configuration; the user can carry their two machines between LANs and the connection still finds them. Drop the hickory-resolver dependency entirely — it's no longer needed. ServiceError::Dns also goes away; lookup failures surface as io::Error which is already covered by ServiceError::Io. Co-Authored-By: Claude Opus 4.7 (1M context) --- Cargo.lock | 217 +++---------------------------------------------- Cargo.toml | 1 - src/dns.rs | 32 +++++--- src/service.rs | 3 - 4 files changed, 34 insertions(+), 219 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 64c4f36..3d6734a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -549,30 +549,6 @@ dependencies = [ "libc", ] -[[package]] -name = "critical-section" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" - -[[package]] -name = "crossbeam-channel" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -586,7 +562,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", - "rand_core 0.6.4", + "rand_core", "subtle", "zeroize", ] @@ -598,7 +574,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", - "rand_core 0.6.4", + "rand_core", "typenum", ] @@ -735,7 +711,7 @@ dependencies = [ "hkdf", "pem-rfc7468", "pkcs8", - "rand_core 0.6.4", + "rand_core", "sec1", "subtle", "zeroize", @@ -747,18 +723,6 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" -[[package]] -name = "enum-as-inner" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "enumflags2" version = "0.7.12" @@ -852,7 +816,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ - "rand_core 0.6.4", + "rand_core", "subtle", ] @@ -1281,7 +1245,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", - "rand_core 0.6.4", + "rand_core", "subtle", ] @@ -1395,52 +1359,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "hickory-proto" -version = "0.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502" -dependencies = [ - "async-trait", - "cfg-if", - "data-encoding", - "enum-as-inner", - "futures-channel", - "futures-io", - "futures-util", - "idna", - "ipnet", - "once_cell", - "rand 0.9.2", - "ring", - "thiserror 2.0.18", - "tinyvec", - "tokio", - "tracing", - "url", -] - -[[package]] -name = "hickory-resolver" -version = "0.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc62a9a99b0bfb44d2ab95a7208ac952d31060efc16241c87eaf36406fecf87a" -dependencies = [ - "cfg-if", - "futures-util", - "hickory-proto", - "ipconfig", - "moka", - "once_cell", - "parking_lot", - "rand 0.9.2", - "resolv-conf", - "smallvec", - "thiserror 2.0.18", - "tokio", - "tracing", -] - [[package]] name = "hkdf" version = "0.12.4" @@ -1713,19 +1631,6 @@ dependencies = [ "thiserror 2.0.18", ] -[[package]] -name = "ipconfig" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d40460c0ce33d6ce4b0630ad68ff63d6661961c48b6dba35e5a4d81cfb48222" -dependencies = [ - "socket2", - "widestring", - "windows-registry", - "windows-result 0.4.1", - "windows-sys 0.61.2", -] - [[package]] name = "ipnet" version = "2.12.0" @@ -1845,7 +1750,6 @@ dependencies = [ "clap", "env_logger", "futures", - "hickory-resolver", "input-capture", "input-emulation", "input-event", @@ -2096,23 +2000,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "moka" -version = "0.12.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957228ad12042ee839f93c8f257b62b4c0ab5eaae1d4fa60de53b27c9d7c5046" -dependencies = [ - "crossbeam-channel", - "crossbeam-epoch", - "crossbeam-utils", - "equivalent", - "parking_lot", - "portable-atomic", - "smallvec", - "tagptr", - "uuid", -] - [[package]] name = "nix" version = "0.26.4" @@ -2242,10 +2129,6 @@ name = "once_cell" version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" -dependencies = [ - "critical-section", - "portable-atomic", -] [[package]] name = "once_cell_polyfill" @@ -2530,18 +2413,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.5", + "rand_chacha", + "rand_core", ] [[package]] @@ -2551,17 +2424,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.5", + "rand_core", ] [[package]] @@ -2573,15 +2436,6 @@ dependencies = [ "getrandom 0.2.17", ] -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] - [[package]] name = "rcgen" version = "0.13.2" @@ -2644,12 +2498,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "resolv-conf" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" - [[package]] name = "rfc6979" version = "0.4.0" @@ -2929,7 +2777,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest", - "rand_core 0.6.4", + "rand_core", ] [[package]] @@ -3017,12 +2865,6 @@ dependencies = [ "version-compare", ] -[[package]] -name = "tagptr" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" - [[package]] name = "target-lexicon" version = "0.13.3" @@ -3125,21 +2967,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "tinyvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" version = "1.51.1" @@ -3417,7 +3244,6 @@ version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" dependencies = [ - "getrandom 0.4.2", "js-sys", "serde_core", "wasm-bindgen", @@ -3658,8 +3484,8 @@ dependencies = [ "p384", "pem", "portable-atomic", - "rand 0.8.5", - "rand_core 0.6.4", + "rand", + "rand_core", "rcgen", "ring", "rustls", @@ -3690,18 +3516,12 @@ dependencies = [ "log", "nix", "portable-atomic", - "rand 0.8.5", + "rand", "thiserror 1.0.69", "tokio", "winapi", ] -[[package]] -name = "widestring" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" - [[package]] name = "winapi" version = "0.3.9" @@ -3836,17 +3656,6 @@ dependencies = [ "windows-link 0.1.3", ] -[[package]] -name = "windows-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" -dependencies = [ - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", -] - [[package]] name = "windows-result" version = "0.3.4" @@ -4186,7 +3995,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ "curve25519-dalek", - "rand_core 0.6.4", + "rand_core", "serde", "zeroize", ] diff --git a/Cargo.toml b/Cargo.toml index 3db40d7..666bebd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,6 @@ lan-mouse-ipc = { path = "lan-mouse-ipc", version = "0.3.0" } lan-mouse-proto = { path = "lan-mouse-proto", version = "0.3.0" } shadow-rs = { version = "1.2.0", features = ["metadata"] } -hickory-resolver = "0.25.2" toml = "0.8" toml_edit = { version = "0.22", features = ["serde"] } serde = { version = "1.0", features = ["derive"] } diff --git a/src/dns.rs b/src/dns.rs index 60bc100..b1d6d42 100644 --- a/src/dns.rs +++ b/src/dns.rs @@ -1,9 +1,9 @@ -use std::{collections::HashMap, net::IpAddr}; +use std::{collections::HashMap, io, net::IpAddr}; use local_channel::mpsc::{Receiver, Sender, channel}; +use tokio::net::lookup_host; use tokio::task::{JoinHandle, spawn_local}; -use hickory_resolver::{ResolveError, TokioResolver}; use tokio_util::sync::CancellationToken; use lan_mouse_ipc::ClientHandle; @@ -22,11 +22,10 @@ struct DnsRequest { pub(crate) enum DnsEvent { Resolving(ClientHandle), - Resolved(ClientHandle, String, Result, ResolveError>), + Resolved(ClientHandle, String, io::Result>), } struct DnsTask { - resolver: TokioResolver, request_rx: Receiver, event_tx: Sender, cancellation_token: CancellationToken, @@ -34,14 +33,12 @@ struct DnsTask { } impl DnsResolver { - pub(crate) fn new() -> Result { - let resolver = TokioResolver::builder_tokio()?.build(); + pub(crate) fn new() -> io::Result { let (request_tx, request_rx) = channel(); let (event_tx, event_rx) = channel(); let cancellation_token = CancellationToken::new(); let dns_task = DnsTask { active_tasks: Default::default(), - resolver, request_rx, event_tx, cancellation_token: cancellation_token.clone(), @@ -97,15 +94,13 @@ impl DnsTask { /* spawn task for dns request */ let event_tx = self.event_tx.clone(); - let resolver = self.resolver.clone(); let cancellation_token = self.cancellation_token.clone(); let task = tokio::task::spawn_local(async move { tokio::select! { - ips = resolver.lookup_ip(&hostname) => { - let ips = ips.map(|ips| ips.iter().collect::>()); + result = resolve_hostname(&hostname) => { event_tx - .send(DnsEvent::Resolved(handle, hostname, ips)) + .send(DnsEvent::Resolved(handle, hostname, result)) .expect("channel closed"); } _ = cancellation_token.cancelled() => {}, @@ -115,3 +110,18 @@ impl DnsTask { } } } + +/// Resolve `hostname` via the operating system's full name-resolution +/// stack (`getaddrinfo` on Unix, GetAddrInfoEx on Windows). This walks +/// `/etc/nsswitch.conf` on Linux — picking up mDNS via Avahi, /etc/hosts, +/// and DNS — and uses Bonjour for `.local` names on macOS. Pure-DNS +/// resolvers like hickory miss all of those, which is why a Bonjour +/// hostname (e.g. `JKMBP-M4-Max.local`) wouldn't resolve before. +/// +/// Port `0` is a placeholder — `lookup_host` requires `host:port` but we +/// only care about the IPs at this stage; the actual port is appended at +/// connection time. +async fn resolve_hostname(hostname: &str) -> io::Result> { + let addrs = lookup_host((hostname, 0)).await?; + Ok(addrs.map(|sa| sa.ip()).collect()) +} diff --git a/src/service.rs b/src/service.rs index 46f3693..bf782c7 100644 --- a/src/service.rs +++ b/src/service.rs @@ -9,7 +9,6 @@ use crate::{ listen::{LanMouseListener, ListenerCreationError}, }; use futures::StreamExt; -use hickory_resolver::ResolveError; use lan_mouse_ipc::{ AsyncFrontendListener, ClientHandle, FrontendEvent, FrontendRequest, IpcError, IpcListenerCreationError, Position, Status, @@ -26,8 +25,6 @@ use tokio::{process::Command, signal, sync::Notify}; #[derive(Debug, Error)] pub enum ServiceError { - #[error(transparent)] - Dns(#[from] ResolveError), #[error(transparent)] IpcListen(#[from] IpcListenerCreationError), #[error(transparent)]