[PR #422] fix: hostname resolution via OS resolver + multi-homed DTLS listener #414

Open
opened 2026-05-05 22:18:35 -06:00 by gitea-mirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/feschber/lan-mouse/pull/422
Author: @jondkinney
Created: 5/6/2026
Status: 🔄 Open

Base: mainHead: split/03-network


📝 Commits (3)

  • dd6ccad fix(dns): resolve hostnames via the OS resolver instead of pure DNS
  • cd43ed4 fix(listen): bind one DTLS listener per local IPv4 address
  • 70a6e2c fix(listen): periodic reconciliation drops stale per-IP listeners

📊 Changes

5 files changed (+626 additions, -284 deletions)

View changed files

📝 Cargo.lock (+268 -204)
📝 Cargo.toml (+2 -1)
📝 src/dns.rs (+21 -11)
📝 src/listen.rs (+335 -65)
📝 src/service.rs (+0 -3)

📄 Description

Summary

Two unrelated-but-bundled fixes for "lan-mouse should Just Work on real LANs" — one for hostname resolution, one for multi-homed hosts. Together they remove the most common need for the manual ips=[…] workaround.

Hostname resolution via the OS resolver (9ce3847)

hickory_resolver::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. 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; lookup failures surface as io::Error, already covered by ServiceError::Io.

Multi-homed DTLS listener (2c7ce2e / 4c80ed0)

When a host has two interfaces on the same subnet (macOS Wi-Fi en0 + USB-C dock en7 both on 192.168.1.0/24), a single 0.0.0.0:port DTLS listener silently breaks for peers that dial the non-routed IP: the kernel sources its reply from the routing table's preferred interface, so the reply's src-IP doesn't match the 4-tuple the peer expects, and webrtc-dtls drops the packet.

Replace the single 0.0.0.0 bind with one Listener per local IPv4 address (loopback + link-local skipped), each socket bound to a specific IP so the kernel uses that IP as source — symmetric replies guaranteed regardless of the routing table. An if-watch supervisor task adds/drops listener slots dynamically on interface up/down; plugging a dock or toggling Wi-Fi no longer requires a lan-mouse restart.

The supervisor also runs a 30-second reconciliation tick that diffs the live getifaddrs set against the listeners HashMap. if-watch on macOS uses Network.framework, which doesn't reliably fire IfEvent::Down when an interface is administratively disabled (e.g. user toggles Wi-Fi off in System Settings); the polling backup catches whatever the event stream misses, both adds and drops.

Falls back to a single 0.0.0.0 bind only if interface enumeration or every per-IP bind fails — preserves single-NIC behavior and ensures we never silently fail to listen.

Removes the previous user-facing workaround of forcing ips = ["192.168.1.88"] on the peer.

Test plan

  • JKMBP-M4-Max.local resolves on a fresh network without any DNS-server config
  • Multi-homed Mac (Wi-Fi + Ethernet on same subnet): peer can dial either IP and the DTLS handshake completes
  • Toggling Wi-Fi off in macOS System Settings: the listener slot drops within ~30s (reconciliation tick) without restart
  • Plugging in a USB-C dock: the new IP gets a listener slot via if-watch
  • Single-NIC machines fall back to the existing 0.0.0.0 bind (no behavior change)

Split out from #418, the umbrella PR collecting ~10 independent feature areas. This PR contains the hostname-resolution and multi-homed-listener subsets. See #418 for the full picture.

Stack overview

These PRs are split out from #418 and stack in this order:

  1. #420 — cursor sync + wall-press + host-lock + slider/UI
  2. #421 — peer version exchange
  3. #422 — hostname resolver + multi-homed DTLS listener
  4. #423 — mDNS-SD service-order discovery
  5. #424 — macOS QoL + UI polish
  6. #425 — scroll forwarding
  7. #426 — GUI singleton

Each PR's branch builds on the previous one, so until earlier PRs are merged the cumulative diff against main includes all preceding work. Reviewing in order is easiest.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/feschber/lan-mouse/pull/422 **Author:** [@jondkinney](https://github.com/jondkinney) **Created:** 5/6/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `split/03-network` --- ### 📝 Commits (3) - [`dd6ccad`](https://github.com/feschber/lan-mouse/commit/dd6ccad017a7d2ee10afcbadb3afa41a97bb7cf2) fix(dns): resolve hostnames via the OS resolver instead of pure DNS - [`cd43ed4`](https://github.com/feschber/lan-mouse/commit/cd43ed424f3dd78f14c1904f50cd8d14834f4e66) fix(listen): bind one DTLS listener per local IPv4 address - [`70a6e2c`](https://github.com/feschber/lan-mouse/commit/70a6e2c3a8937b9c14b619426fce9967dbd49bc2) fix(listen): periodic reconciliation drops stale per-IP listeners ### 📊 Changes **5 files changed** (+626 additions, -284 deletions) <details> <summary>View changed files</summary> 📝 `Cargo.lock` (+268 -204) 📝 `Cargo.toml` (+2 -1) 📝 `src/dns.rs` (+21 -11) 📝 `src/listen.rs` (+335 -65) 📝 `src/service.rs` (+0 -3) </details> ### 📄 Description ## Summary Two unrelated-but-bundled fixes for "lan-mouse should Just Work on real LANs" — one for hostname resolution, one for multi-homed hosts. Together they remove the most common need for the manual `ips=[…]` workaround. ## Hostname resolution via the OS resolver (`9ce3847`) `hickory_resolver::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`. 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; lookup failures surface as `io::Error`, already covered by `ServiceError::Io`. ## Multi-homed DTLS listener (`2c7ce2e` / `4c80ed0`) When a host has two interfaces on the same subnet (macOS Wi-Fi `en0` + USB-C dock `en7` both on `192.168.1.0/24`), a single `0.0.0.0:port` DTLS listener silently breaks for peers that dial the non-routed IP: the kernel sources its reply from the routing table's preferred interface, so the reply's src-IP doesn't match the 4-tuple the peer expects, and `webrtc-dtls` drops the packet. Replace the single `0.0.0.0` bind with one `Listener` per local IPv4 address (loopback + link-local skipped), each socket bound to a specific IP so the kernel uses *that* IP as source — symmetric replies guaranteed regardless of the routing table. An `if-watch` supervisor task adds/drops listener slots dynamically on interface up/down; plugging a dock or toggling Wi-Fi no longer requires a lan-mouse restart. The supervisor also runs a **30-second reconciliation tick** that diffs the live `getifaddrs` set against the listeners HashMap. `if-watch` on macOS uses Network.framework, which doesn't reliably fire `IfEvent::Down` when an interface is administratively disabled (e.g. user toggles Wi-Fi off in System Settings); the polling backup catches whatever the event stream misses, both adds and drops. Falls back to a single `0.0.0.0` bind only if interface enumeration or every per-IP bind fails — preserves single-NIC behavior and ensures we never silently fail to listen. Removes the previous user-facing workaround of forcing `ips = ["192.168.1.88"]` on the peer. ## Test plan - [x] `JKMBP-M4-Max.local` resolves on a fresh network without any DNS-server config - [x] Multi-homed Mac (Wi-Fi + Ethernet on same subnet): peer can dial either IP and the DTLS handshake completes - [x] Toggling Wi-Fi off in macOS System Settings: the listener slot drops within ~30s (reconciliation tick) without restart - [x] Plugging in a USB-C dock: the new IP gets a listener slot via `if-watch` - [x] Single-NIC machines fall back to the existing `0.0.0.0` bind (no behavior change) --- Split out from #418, the umbrella PR collecting ~10 independent feature areas. This PR contains the hostname-resolution and multi-homed-listener subsets. See #418 for the full picture. ## Stack overview These PRs are split out from #418 and stack in this order: 1. #420 — cursor sync + wall-press + host-lock + slider/UI 2. #421 — peer version exchange 3. #422 — hostname resolver + multi-homed DTLS listener 4. #423 — mDNS-SD service-order discovery 5. #424 — macOS QoL + UI polish 6. #425 — scroll forwarding 7. #426 — GUI singleton Each PR's branch builds on the previous one, so until earlier PRs are merged the cumulative diff against `main` includes all preceding work. Reviewing in order is easiest. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
gitea-mirror added the
pull-request
label 2026-05-05 22:18:35 -06:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: github-starred/lan-mouse#414
No description provided.