Summary
Legacy unicast responses (RFC 6762 §6.7) are mostly implemented — the question
section is echoed, the cache-flush bit is cleared, and the reply is correctly
unicast to the querier's ephemeral port — but two requirements of the same
section are not met:
- The Query Identifier is not copied into the response (a MUST). Responses
always carry ID 0.
- Record TTLs are not capped to 10 seconds (a SHOULD). The full multicast
TTLs (4500 / 120) are sent.
The practical consequence of (1) is that standard DNS tooling refuses the
reply, which makes an mdns-sd-based responder hard to debug in the field:
$ dig +short @192.168.88.47 -p 5353 _bos._sub._http._tcp.local PTR
;; Warning: ID mismatch: expected ID 16033, got 0
;; communications error to 192.168.88.47#5353: timed out
;; no servers could be reached
Reproduction
Against any host running an mdns-sd responder (here one advertising
_http._tcp with a _bos subtype), sending a query from an ephemeral port:
import socket, struct
QID = 1234
def enc(n):
return b"".join(bytes([len(l)]) + l.encode() for l in n.rstrip(".").split(".")) + b"\x00"
pkt = struct.pack("!HHHHHH", QID, 0, 1, 0, 0, 0) + enc("_http._tcp.local") + struct.pack("!HH", 12, 1)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM); s.settimeout(4)
s.sendto(pkt, ("<responder-ip>", 5353))
d, _ = s.recvfrom(9000)
print("response id:", struct.unpack("!H", d[:2])[0])
Observed on mdns-sd 0.21.0:
query id sent : 0x04d2 (1234)
response id : 0x0000 (0)
flags : 0x8400
counts : questions=1 answers=1 auth=0 addl=4 (question echoed: True)
record TTLs:
type=12 ttl=4500 cache_flush=False
Expected per §6.7: response id == 1234, and TTLs of 10 seconds.
Where it comes from
DnsOutgoing::new hardcodes the header ID and exposes no way to set it
(src/dns_parser.rs, id: 0), so every outgoing message — including the
legacy unicast response built in handle_query
(DnsOutgoing::new(FLAGS_QR_RESPONSE | FLAGS_AA)) — goes out with ID 0.
The value needed is already parsed and available: DnsIncoming carries an
id: u16 field. So the fix looks like threading an id into DnsOutgoing
(or a set_id) and setting it only on the legacy unicast path, where
unicast_dest.is_some() — it must stay 0 for multicast responses.
The TTL cap would fit naturally in the same branch, alongside the existing
out.clear_cache_flush_bits() call.
Notes
I'm happy to put a PR together for either or both if you consider this in
scope — wanted to check first, since I may be missing a reason the ID is
deliberately left at 0.
Version: mdns-sd 0.21.0, responder on Linux (armv7, kernel 5.10), querier on
x86-64 Linux.
Summary
Legacy unicast responses (RFC 6762 §6.7) are mostly implemented — the question
section is echoed, the cache-flush bit is cleared, and the reply is correctly
unicast to the querier's ephemeral port — but two requirements of the same
section are not met:
always carry ID 0.
TTLs (4500 / 120) are sent.
The practical consequence of (1) is that standard DNS tooling refuses the
reply, which makes an mdns-sd-based responder hard to debug in the field:
Reproduction
Against any host running an mdns-sd responder (here one advertising
_http._tcpwith a_bossubtype), sending a query from an ephemeral port:Observed on mdns-sd 0.21.0:
Expected per §6.7:
response id == 1234, and TTLs of 10 seconds.Where it comes from
DnsOutgoing::newhardcodes the header ID and exposes no way to set it(
src/dns_parser.rs,id: 0), so every outgoing message — including thelegacy unicast response built in
handle_query(
DnsOutgoing::new(FLAGS_QR_RESPONSE | FLAGS_AA)) — goes out with ID 0.The value needed is already parsed and available:
DnsIncomingcarries anid: u16field. So the fix looks like threading an id intoDnsOutgoing(or a
set_id) and setting it only on the legacy unicast path, whereunicast_dest.is_some()— it must stay 0 for multicast responses.The TTL cap would fit naturally in the same branch, alongside the existing
out.clear_cache_flush_bits()call.Notes
I'm happy to put a PR together for either or both if you consider this in
scope — wanted to check first, since I may be missing a reason the ID is
deliberately left at 0.
Version: mdns-sd 0.21.0, responder on Linux (armv7, kernel 5.10), querier on
x86-64 Linux.