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 <[email protected]>
This commit is contained in:
2026-10-11 15:02:22 +01:00
co-authored by Claude Opus 4.8
parent 58d9b0adaf
commit 4c22f8addf
2 changed files with 34 additions and 16 deletions
+23 -16
View File
@@ -218,30 +218,37 @@ def dns_response(packet, use_tcp=False):
return struct.pack("!HHHHHH", transaction_id, response_flags | 1, 0, 0, 0, 0) return struct.pack("!HHHHHH", transaction_id, response_flags | 1, 0, 0, 0, 0)
question = packet[12:question_end] 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). # Exceptions bypass overrides and are forwarded upstream (nanoDNS semantics).
if match_any(name, EXCEPTIONS): if is_exception:
try: try:
return forward_query(packet, use_tcp=use_tcp) return forward_query(packet, use_tcp=use_tcp)
except (OSError, ValueError) as error: except (OSError, ValueError) as error:
print(f"DNS upstream failure for {name}: {error}", flush=True) print(f"DNS upstream failure for {name}: {error}", flush=True)
return error_response(packet, 2) # SERVFAIL return error_response(packet, 2) # SERVFAIL
address = override_for(name) answer = (
if address is None: b"\xc0\x0c"
# Default-deny: a name matching no rule returns NXDOMAIN. + struct.pack("!HHIH", 1, 1, 60, 4)
return struct.pack("!HHHHHH", transaction_id, response_flags | 3, 1, 0, 0, 0) + question + address.packed
)
if qclass == 1 and qtype in (1, 255): return struct.pack("!HHHHHH", transaction_id, response_flags, 1, 1, 0, 0) + question + answer
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
class ThreadingUDPServer(socketserver.ThreadingUDPServer): class ThreadingUDPServer(socketserver.ThreadingUDPServer):
+11
View File
@@ -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 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. 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 The gateway additionally restricts which source clients it answers, via
`[general] allowed_clients`. It defaults to the console VLAN `192.168.40.0/28`, `[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 the server VLAN `192.168.20.0/24`, and the trusted management VLAN