hijack-dns captures non-DNS (STUN) packets that reuse a DNS query's UDP source port → "process DNS packet: unpack request: bad rdata"
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 38.1k
- Forks
- 4.6k
- Avg merge
- 19d 15h
- Merged PRs (30d)
- 1
Description
Update (revised report): my original title/description attributed this to a single-packet DNS sniffer false positive. After capturing the full UDP flow, the real root cause is UDP source-port reuse causing non-DNS packets to be glued to a hijacked-DNS session. Revised below.
Operating system
Linux
System version
Debian 13 (trixie), kernel 6.12.90+deb13.1-amd64, x86_64
Installation type
Original sing-box Command Line
Version
sing-box version 1.13.12
Environment: go1.25.9 linux/amd64
Tags: with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,with_naive_outbound,badlinkname,tfogo_checklinkname0,with_musl
Revision: 1086ab2563320e0da0c23b3a491d8dfa0939dff4
CGO: enabled
Description
On a TUN gateway with hijack-dns, I get a continuous stream of:
router: process DNS packet: unpack request: bad question name: dns: bad rdata
router: process DNS packet: unpack request: ... overflow unpacking uint16
router: process DNS packet: ... buffer size too small
Full packet capture + debug logs show the cause is UDP source-port reuse, not a per-packet sniffer false positive:
A LAN client (an iPhone running a live-streaming app with P2P/PCDN) reuses a single UDP source port. It first sends one DNS query to :53, then reuses the same source port to send STUN packets to other ports (:3478, etc.). sing-box tracks the TUN UDP session by source port and makes the hijack decision on the first packet, so the whole session is locked to hijack-dns; every subsequent STUN packet on that source port is fed into the hijacked DNS session and fails to unpack.
This still happens with the hijack rule reduced to { "port": 53 } only (no protocol: dns), because the first packet genuinely targets :53.
Evidence
One erroring UDP session (connection id 4017931420, source port 57688) over ~70s — its packets grouped by destination port and payload header:
| dst port | packets | payload bytes [4:6] | meaning |
|---|---|---|---|
| 53 | 1 | 0001 |
normal DNS query (QDCOUNT=1) — the first packet |
| 63335 | 84 | 2112 |
STUN (Magic Cookie 0x2112A442) |
| 3478 | 5 | 2112 |
STUN |
The session logged 89 bad rdata errors ≈ the 89 STUN packets.
Debug log for this session — the hijack is decided on the first packet via port=53:
INFO [4017931420] inbound/tun[tun-in]: inbound packet connection from 172.17.1.18:57688
INFO [4017931420] inbound/tun[tun-in]: inbound packet connection to 198.18.2.2:53
DEBUG [4017931420] router: sniffed packet protocol: dns
DEBUG [4017931420] router: match[1] port=53 => hijack-dns
A captured STUN packet on this same source port (dst :3478):
IP 172.17.1.18.57688 > x.x.x.x.3478: UDP, length 20
payload: 0001 0000 2112 a442 <12-byte transaction id>
^^^^ STUN Message Type (Binding Request)
^^^^^^^^ STUN Magic Cookie (RFC 5389)
When this is handed to the DNS module, the STUN header is read as a DNS message (2112 → QDCOUNT = 8466), so it tries to parse 8466 questions out of a 20-byte packet → bad rdata / overflow unpacking uint16 / buffer size too small.
Why config workarounds do not help
- Removing
protocol: dnsfrom the hijack rule (keeping onlyport: 53): no effect (verified) — the first packet really targets:53. - Adding
{ "protocol": "stun", "action": "reject" }: no effect — the STUN packets never undergo independent route evaluation; they are glued to the already-hijacked DNS session and go straight into the DNS module.
Side effect
The hijacked STUN packets are dropped on unpack failure, so the client's NAT-traversal probes do not complete — this can degrade P2P for the app, on top of spamming the log.
Reproduction
- TUN inbound,
hijack-dnsmatchingport 53(with or withoutprotocol: dns). - A client behind the gateway reuses one UDP source port to send a DNS query to
:53and then STUN to another port (any WebRTC / P2P / live-streaming NAT traversal can do this). - sing-box locks the source-port UDP session to
hijack-dnson the first (DNS) packet; subsequent STUN packets on the same source port are processed as DNS and fail to unpack → repeated errors.
Suggested fix
Within a hijacked-DNS UDP session, packets that fail to parse as DNS should be passed through / re-routed instead of dropped with an error; and/or hijack-dns session tracking should key on the full 5-tuple and re-evaluate per packet, so a source port later reused for STUN is not captured by a prior DNS session.
Related
Same symptom as #3531 (errors while a game/app generates NAT-traversal traffic), closed as stale for lack of a capture. This report provides the capture and identifies source-port reuse as the cause.
Configuration
De-identified minimal excerpt (the errors persist with this reduced rule set):
{
"inbounds": [
{ "type": "tun", "tag": "tun-in", "auto_route": true, "auto_redirect": true, "stack": "mixed" }
],
"route": {
"rules": [
{ "action": "sniff" },
{ "port": 53, "action": "hijack-dns" }
]
}
}
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the TUN UDP session tracking and hijack-dns routing path described in the report, then reproduce the source-port reuse sequence with a DNS query followed by STUN packets. Trace how subsequent packets reach the DNS unpacking path. Done means STUN traffic is not treated as DNS or dropped, while the initial DNS packet is still hijacked correctly and the repeated unpack errors stop.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- networking
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100