From 4c22f8addfaf7309c2108d7b1c1a08dea21ea243 Mon Sep 17 00:00:00 2001 From: kadinho Date: Sun, 11 Oct 2026 15:02:22 +0100 Subject: [PATCH] Make the guide gateway IPv4-only to stop IPv6 stalls Serve only A/ANY queries; answer AAAA and all other record types with NOERROR and no data, for overrides and forwarded names alike. The console LAN has no IPv6 route, so returning a real AAAA made clients stall on an unreachable address. This fixes the PS5 Payload Manager looping on "synchronizing repositories": its dual-stack catalog host itsplk.github.io handed back an IPv6 address and the manager's libcurl download (no timeout) hung on it indefinitely. Co-Authored-By: Claude Opus 4.8 --- docker/guide_gateway.py | 39 +++++++++++++++++++++++---------------- docs/HANDOFF.md | 11 +++++++++++ 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/docker/guide_gateway.py b/docker/guide_gateway.py index b24e56c..2601287 100644 --- a/docker/guide_gateway.py +++ b/docker/guide_gateway.py @@ -218,30 +218,37 @@ def dns_response(packet, use_tcp=False): return struct.pack("!HHHHHH", transaction_id, response_flags | 1, 0, 0, 0, 0) question = packet[12:question_end] + is_exception = match_any(name, EXCEPTIONS) + address = None if is_exception else override_for(name) + + # Default-deny: a name matching no rule returns NXDOMAIN. + if not is_exception and address is None: + return struct.pack("!HHHHHH", transaction_id, response_flags | 3, 1, 0, 0, 0) + question + + noerror_nodata = struct.pack("!HHHHHH", transaction_id, response_flags, 1, 0, 0, 0) + question + + # IPv4-only resolver. Only A (and ANY) queries are served; AAAA and any other + # record type get NOERROR with no data. The console LAN has no IPv6 route, so + # handing back an AAAA record would make clients stall on an unreachable IPv6 + # address -- this is what left the PS5 Payload Manager looping on "synchronizing + # repositories" for the dual-stack itsplk.github.io host. + if qclass != 1 or qtype not in (1, 255): + return noerror_nodata # Exceptions bypass overrides and are forwarded upstream (nanoDNS semantics). - if match_any(name, EXCEPTIONS): + if is_exception: try: return forward_query(packet, use_tcp=use_tcp) except (OSError, ValueError) as error: print(f"DNS upstream failure for {name}: {error}", flush=True) return error_response(packet, 2) # SERVFAIL - address = override_for(name) - if address is None: - # Default-deny: a name matching no rule returns NXDOMAIN. - return struct.pack("!HHHHHH", transaction_id, response_flags | 3, 1, 0, 0, 0) + question - - if qclass == 1 and qtype in (1, 255): - answer = ( - b"\xc0\x0c" - + struct.pack("!HHIH", 1, 1, 60, 4) - + address.packed - ) - return struct.pack("!HHHHHH", transaction_id, response_flags, 1, 1, 0, 0) + question + answer - - # NOERROR with no data prevents IPv6 or unsupported record fallback. - return struct.pack("!HHHHHH", transaction_id, response_flags, 1, 0, 0, 0) + question + answer = ( + b"\xc0\x0c" + + struct.pack("!HHIH", 1, 1, 60, 4) + + address.packed + ) + return struct.pack("!HHHHHH", transaction_id, response_flags, 1, 1, 0, 0) + question + answer class ThreadingUDPServer(socketserver.ThreadingUDPServer): diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index 512635a..f97dae5 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -207,6 +207,17 @@ overrides (spoof) → NXDOMAIN. Unlike upstream nanoDNS, a name matching no rule blocked by default. The PS5 sends DNS only to `net-utils01`; that host must be allowed to reach the configured upstream resolver on UDP/TCP 53. +The gateway is **IPv4-only**: it serves only `A` (and `ANY`) queries. `AAAA` and +any other record type return NOERROR with no data, for both overrides and +forwarded names. The console LAN has no IPv6 route, so returning a real `AAAA` +would make clients stall on an unreachable IPv6 address. This specifically fixed +the PS5 Payload Manager looping forever on "synchronizing repositories": its +catalog host `itsplk.github.io` is dual-stack, the console tried the IPv6 address, +and the manager's libcurl download has no timeout, so the hung connection never +returned. The official Payload Manager repository needs only `itsplk.github.io` +(catalog), `github.com` (payload `url`s) and `release-assets.githubusercontent.com` +(release-download redirect target), all already in `[exceptions]`. + The gateway additionally restricts which source clients it answers, via `[general] allowed_clients`. It defaults to the console VLAN `192.168.40.0/28`, the server VLAN `192.168.20.0/24`, and the trusted management VLAN