Skip to content

Client cannot resolve exit node endpoint on IPv6-only networks — hole punch never starts, registration fails with HOLEPUNCH_MISSING #42

Description

@WuestenGeeko

Describe the Bug

Summary

On a mobile network that hands out only IPv6 DNS servers, the Android client fails to resolve the exit node hostname for UDP hole punching. No hole punch packet is ever sent, so the server aborts registration with HOLEPUNCH_MISSING and the app hangs in the "registering" state indefinitely.

The same device, app version and server work fine as soon as the network provides an IPv4 DNS server — including full relay fallback.

The server's error message ("ensure your client can reach the server on UDP port 21820") is misleading here: the client never attempts to reach it, because name resolution failed first.

Environment

App Pangolin Android 0.4.1
olm version reported 0.4.1
Device Pixel 8a, Android 17, aarch64
Pangolin server 1.21.1 (enterprise build)
newt (site) 1.15.0
Exit node hostname, UDP 21820
Failing network mobile data on APN internet.v6.telekom (IPv6-only APN, Telekom Germany / its MVNOs), DNS servers 2a01:598:7ff:0:10:74:210:221 / :222
Working network same SIM on APN internet.telekom (dual-stack), or any network providing an IPv4 resolver

Hostnames, org id, keys and tokens are redacted below as pangolin.example.com, <redacted> etc.

To Reproduce

Failing case (IPv6-only DNS)

12:59:30.608  Applying system DNS: [[2a01:598:7ff:0:10:74:210:221]:53 [2a01:598:7ff:0:10:74:210:222]:53]
12:59:30.612  Created shared UDP socket on port 63563 (refcount: 2)
12:59:30.662  Got exit nodes for hole punching: [{pangolin.example.com 21820 <redacted> [1]}]
12:59:30.662  Starting hole punch for 1 exit nodes
12:59:30.662  Starting UDP hole punch to 1 exit nodes with shared bind
12:59:30.666  Failed to resolve endpoint pangolin.example.com: DNS lookup failed using all
              upstream servers: lookup pangolin.example.com on [::1]:53:
              dial udp [2a01:598:7ff:0:10:74:210:222]:53: connect: no route to host
12:59:30.666  No exit nodes available yet, waiting for nodes to be added
12:59:30.849  Websocket Connected
12:59:31.350  Sending registration message to server with public key: <redacted> and relay: false
              ... olm/wg/register repeated every 2s ...
13:00:05.556  Received olm error message: map[code:HOLEPUNCH_MISSING
              message:Unable to coordinate client P2P connection. Please ensure your client
              can reach the server on UDP port 21820 and try registering again.]

The key detail: the websocket connects to the same hostname 183 ms after the resolver failed on it. The platform resolver works; only the client's internal resolver on [::1]:53 fails.

This reproduced 8 times in a row over ~40 minutes, byte-identical each time.

Working case (IPv4 DNS available)

Same device, same app version, same server, same session of the log, ~45 minutes earlier:

12:13:21.175  Applying system DNS: [9.9.9.9:53 149.112.112.112:53]
12:13:21.639  Got exit nodes for hole punching: [{pangolin.example.com 21820 <redacted> [1]}]
12:13:21.639  Starting hole punch for 1 exit nodes          ← no resolve error
12:13:21.737  Websocket Connected
12:13:22.239  Sending registration message to server with public key: <redacted> and relay: false
12:13:22.603  The tunnel IP is: 100.90.128.2/20             ← registration completes
12:13:22.603  Set IPv4 addresses: [100.90.128.2], subnet masks: [255.255.240.0]
12:13:22.603  Set MTU: 1280
12:13:22.613  Configured peer <redacted>
12:13:33.081  Local endpoint 192.168.178.200:63131 for site 1 failed 3 times,
              falling back to public/relay logic
12:13:35.899  Rapid fallback test: site 1 unreachable on public endpoint after local
              fallback, requesting relay
12:13:35.943  Received relay-peer message: map[relayEndpoint:pangolin.example.com
              relayPort:21820 siteId:1]
12:13:35.987  Adjusted peer 1 to point to relay!
12:13:41.138  WireGuard connection to site 1 is CONNECTED

The device was not on the site's LAN in this session — the local endpoint probe failed and the client correctly fell back to relay. So relay works; the only variable between working and failing is whether an IPv4 DNS server is present.

Suspected cause

The tunnel interface is IPv4-only. Across the whole 9119-line log there is not a single Set IPv6 line; every session configures only Set IPv4 addresses: [100.90.x.x].

Android's VpnService makes IPv6 destinations unreachable (no route to host) for traffic subject to a VPN that has no IPv6 address/route. The internal DNS proxy's upstream socket appears not to be protected with VpnService.protect(), so once the TUN is established its queries to IPv6 resolvers are blackholed.

On a dual-stack network this is invisible, because the IPv4 resolver in the same list still answers. On an IPv6-only network there is no fallback and hole punching never starts.

Expected Behavior

The exit node hostname should resolve regardless of the network's address family — e.g. by protecting the resolver socket, resolving before the TUN is established, or falling back to the platform resolver (which demonstrably succeeds, as the websocket connection proves).

Additional note: the DNS setting in the app does not reach this code path

Setting a custom DNS server (9.9.9.9) in the app's settings does not help. The value never appears in the tunnel config. Across all 8 failing attempts:

UpstreamDNS:[]  PublicDNS:[]  DNS:

The only field that changed after editing the setting was TunnelDNS: true → false. So there is currently no user-side workaround within the app.

Full tunnel config of a failing session:

{Endpoint:https://pangolin.example.com ID:<redacted> Secret:<redacted>
 UserToken:<redacted> MTU:1280 DNS: UpstreamDNS:[] PublicDNS:[] InterfaceName:
 MatchDomains:[] Holepunch:true TlsClientCert: PingIntervalDuration:10s
 PingTimeoutDuration:30s OrgID:<redacted> FileDescriptorTun:146 FileDescriptorUAPI:0
 EnableUAPI:false OverrideDNS:true TunnelDNS:false ... DisableRelay:false
 PreferLocalRoutes:false}

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions