Skip to content

🐛 [Bug]: getaddrinfo fails while tethered when no Wi-Fi service is active (scutil DNS entry is Supplemental-only) #11

Open

Description

@prodzaha

Summary

While USB tethering is active and Wi-Fi is disconnected, most applications cannot resolve any hostname:

  • curl -I https://example.com -> curl: (6) Could not resolve host
  • Node / OpenCode / browsers -> getaddrinfo ENOTFOUND
  • python3 -c "import socket; print(socket.gethostbyname('example.com'))" -> socket.gaierror: [Errno 8]

At the same time direct DNS works fine through the tunnel (nslookup example.com 8.8.8.8, dig example.com), so routing and the tunnel are fine — only the macOS system resolver is broken. Resolution works again as soon as Wi-Fi is associated with any network.

Environment

Root cause (analysis)

internal/tun/utun_darwin.go SetDNS writes via scutil:

d.init
d.add ServerAddresses * 8.8.8.8 10.213.203.218
d.add SupplementalMatchDomains * ""
d.add SupplementalMatchOrders * 10
set State:/Network/Service/droidtether/DNS
set State:/Network/Global/DNS

and then overwrites State:/Network/Global/IPv4 with only Router and Interface.

On macOS 26 scutil --dns shows this as the only resolver:

resolver #1
  nameserver[0] : 8.8.8.8
  nameserver[1] : 10.213.203.218
  flags    : Supplemental
  reach    : 0x00000002 (Reachable)

Because the entry is a Supplemental resolver (and the droidtether pseudo service never becomes the primary service), getaddrinfo does not use it for ordinary lookups. With Wi-Fi off there is no other active service resolver, so all lookups fail system-wide. When Wi-Fi is associated, the DNS servers configured on the Wi-Fi service resolve queries and mask the problem.

Possibly related: State:/Network/Global/IPv4 is overwritten without PrimaryService, and the pseudo service is not added to the service order, so the resolver never becomes primary.

Steps to reproduce

  1. Connect the phone and enable USB tethering; keep Wi-Fi disconnected.
  2. Wait for Network auto-configured! in /var/log/droidtether.log.
  3. python3 -c "import socket; print(socket.gethostbyname('example.com'))" -> gaierror, while dig example.com resolves.
  4. Associate Wi-Fi with any network -> resolution starts working immediately.

Current workaround

Keep a Wi-Fi association active (any network, even one without internet) — traffic keeps flowing through the tunnel. /etc/hosts entries also work but are brittle.

Suggested fix

Make the tunnel DNS a resolver macOS actually uses for default lookups, for example:

  • register the pseudo service as PrimaryService in State:/Network/Global/IPv4, or
  • create the resolver without relying on SupplementalMatchDomains,

and restore/remove Global/DNS and Global/IPv4 when the tunnel stops.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions