Add negative response handling (RFC 2308)

Synthesize an SOA record for the zone, return it for direct SOA
queries, and include it in the authority section of empty responses
so resolvers can negative-cache properly. RCODE stays NOERROR rather
than NXDOMAIN, since mDNS can't reliably prove non-existence.
This commit is contained in:
2026-08-05 11:01:18 +02:00
parent be2477d326
commit fc8b0bfffd
+14 -7
View File
@@ -14,8 +14,15 @@ domain = sys.argv[1]
port = int(sys.argv[2])
ttl = 120
timeout = 2
negative_ttl = 10
ipv6_ula_network = ipaddress.ip_network('fc00::/7')
def soa_record():
return dns.RRHeader(name=domain, type=dns.SOA, ttl=negative_ttl, payload=dns.Record_SOA(
mname=domain, rname="hostmaster." + domain,
serial=1, refresh=1200, retry=180, expire=1209600, minimum=negative_ttl
))
class DynamicResolver(object):
def __init__(self):
self.zeroconf = Zeroconf(ip_version=IPVersion.All)
@@ -28,7 +35,7 @@ class DynamicResolver(object):
def _doDynamicResponse(self, query):
if query.type == dns.SOA:
return defer.succeed(([], [], []))
return defer.succeed(([soa_record()], [], []))
localname = str(query.name)[:-len(domain)] + "local."
@@ -50,18 +57,18 @@ class DynamicResolver(object):
#txt_ans, _, _ = txt(service)
#srv_ans, _, a_ans = srv(service)
#additional += a_ans + txt_ans + srv_ans
return answers, [], additional
return answers, ([] if answers else [soa_record()]), additional
def txt(localname):
if localname.endswith('._device-info._tcp.local.'):
info = ServiceInfo(localname, localname)
info.request(self.zeroconf, timeout*1000)
if not info.text:
return [], [], []
return [], [soa_record()], []
else:
info = self.zeroconf.get_service_info(localname, localname, timeout*1000)
if info is None:
return [], [], []
return [], [soa_record()], []
order = []
i = 0
@@ -81,7 +88,7 @@ class DynamicResolver(object):
def srv(localname):
info = self.zeroconf.get_service_info(localname, localname, timeout*1000)
if info is None:
return [], [], []
return [], [soa_record()], []
answers = [dns.RRHeader(name=localname[:-6] + domain, ttl=ttl, type=dns.SRV, payload=dns.Record_SRV(
info.priority, info.weight, info.port, info.server[:-6] + domain
@@ -128,7 +135,7 @@ class DynamicResolver(object):
addr
)) for addr in l.addrs]
return answers, [], []
return answers, ([] if answers else [soa_record()]), []
d = defer.Deferred()
@@ -146,7 +153,7 @@ class DynamicResolver(object):
return d
else:
print("Unsupported request", query)
d.callback(([], [], []))
d.callback(([], [soa_record()], []))
return d
def query(self, query, timeout=None):