|
|
|
@ -102,7 +102,7 @@ Disallow: /
|
|
|
|
|
return [output]
|
|
|
|
|
|
|
|
|
|
def query(self, start_response, path, client, format="HTML", alt_resolver=None,
|
|
|
|
|
do_dnssec=False, tcp=False, edns_size=default_edns_size,
|
|
|
|
|
do_dnssec=False, tcp=False, cd=False, edns_size=default_edns_size,
|
|
|
|
|
reverse=False):
|
|
|
|
|
""" path must starts with a /, then the domain name then an
|
|
|
|
|
(optional) / followed by the QTYPE """
|
|
|
|
@ -181,12 +181,6 @@ Disallow: /
|
|
|
|
|
output, plaintype)
|
|
|
|
|
return [output]
|
|
|
|
|
# Pseudo-qtype ADDR is handled specially later
|
|
|
|
|
# Alas, dnspython does not react properly to QTYPE=ANY :-(
|
|
|
|
|
if qtype == "ANY":
|
|
|
|
|
output = "dnspython does not support ANY queries, sorry\n"
|
|
|
|
|
send_response(start_response, '400 Bad record type', output,
|
|
|
|
|
plaintype)
|
|
|
|
|
return [output]
|
|
|
|
|
if not domain.endswith('.'):
|
|
|
|
|
domain += '.'
|
|
|
|
|
if domain == 'root.':
|
|
|
|
@ -210,22 +204,22 @@ Disallow: /
|
|
|
|
|
formatter = Formatter.XmlFormatter(domain)
|
|
|
|
|
self.resolver.reset()
|
|
|
|
|
if do_dnssec:
|
|
|
|
|
self.resolver.use_edns(0, edns_size)
|
|
|
|
|
self.resolver.set_edns(dnssec=True)
|
|
|
|
|
if alt_resolver:
|
|
|
|
|
self.resolver.set_nameservers([alt_resolver,])
|
|
|
|
|
query_start = datetime.now()
|
|
|
|
|
if qtype != "ADDR":
|
|
|
|
|
answer = self.resolver.query(qdomain, qtype, tcp=tcp)
|
|
|
|
|
answer = self.resolver.query(qdomain, qtype, tcp=tcp, cd=cd)
|
|
|
|
|
else:
|
|
|
|
|
# TODO CRIT refaire completement
|
|
|
|
|
try:
|
|
|
|
|
answers = self.resolver.query(qdomain, "A", tcp=tcp)
|
|
|
|
|
answer = self.resolver.query(qdomain, "A", tcp=tcp, cd=cd)
|
|
|
|
|
except dns.resolver.NoAnswer:
|
|
|
|
|
answer = None
|
|
|
|
|
try:
|
|
|
|
|
answers = self.resolver.query(qdomain, "AAAA", tcp=tcp)
|
|
|
|
|
if answer is not None:
|
|
|
|
|
answer.rrsets.append(answers.rrset)
|
|
|
|
|
answer_bis = self.resolver.query(qdomain, "AAAA", tcp=tcp, cd=cd)
|
|
|
|
|
if answer_bis is not None:
|
|
|
|
|
for rrset in answer_bis.answer:
|
|
|
|
|
answer.answer.append(rrset)
|
|
|
|
|
except dns.resolver.NoAnswer:
|
|
|
|
|
pass
|
|
|
|
|
# TODO: what if flags are different with A and AAAA? (Should not happen)
|
|
|
|
@ -241,42 +235,43 @@ Disallow: /
|
|
|
|
|
formatter.format(answer, qtype, answer.flags, self)
|
|
|
|
|
output = formatter.result(self)
|
|
|
|
|
send_response(start_response, '200 OK', output, mtype)
|
|
|
|
|
except dns.resolver.NXDOMAIN:
|
|
|
|
|
except Resolver.UnknownRRtype:
|
|
|
|
|
output = "Record type %s does not exist\n" % qtype
|
|
|
|
|
output = output.encode(self.encoding)
|
|
|
|
|
send_response(start_response, '400 Unknown record type', output,
|
|
|
|
|
plaintype)
|
|
|
|
|
except Resolver.NoSuchDomainName:
|
|
|
|
|
output = "Domain %s does not exist\n" % domain
|
|
|
|
|
output = output.encode(self.encoding)
|
|
|
|
|
# TODO send back HTML if this is the expected format
|
|
|
|
|
send_response(start_response, '404 No such domain', output, plaintype)
|
|
|
|
|
except dns.resolver.NoNameservers:
|
|
|
|
|
output = "No working server for domain %s\n" % domain
|
|
|
|
|
except Resolver.Refused:
|
|
|
|
|
output = "Refusal to answer for all name servers for %s\n" % domain
|
|
|
|
|
output = output.encode(self.encoding)
|
|
|
|
|
# TODO send back HTML if this is the expected format
|
|
|
|
|
send_response(start_response, '404 No such domain', output, plaintype)
|
|
|
|
|
except dns.resolver.Timeout: # dnspython seems to raise this, not only when
|
|
|
|
|
# there is an actual timeout, but also when all the authoritative
|
|
|
|
|
# servers reply REFUSED.
|
|
|
|
|
output = "No server reply for domain %s\n" % domain
|
|
|
|
|
send_response(start_response, '403 Refused', output, plaintype)
|
|
|
|
|
except Resolver.Servfail:
|
|
|
|
|
output = "Server failure for all name servers for %s (may be a DNSSEC validation error)\n" % domain
|
|
|
|
|
output = output.encode(self.encoding)
|
|
|
|
|
send_response(start_response, '504 Servfail', output, plaintype)
|
|
|
|
|
except Resolver.Timeout:
|
|
|
|
|
output = "No server replies for domain %s\n" % domain
|
|
|
|
|
output = output.encode(self.encoding)
|
|
|
|
|
# TODO send back HTML if this is the expected format. In
|
|
|
|
|
# that case, do not serialize output.
|
|
|
|
|
send_response(start_response, '504 Timeout', output,
|
|
|
|
|
"text/plain")
|
|
|
|
|
except dns.rdatatype.UnknownRdatatype:
|
|
|
|
|
output = "Record type %s does not exist\n" % qtype
|
|
|
|
|
except Resolver.NoPositiveAnswer:
|
|
|
|
|
output = "No server replies for domain %s\n" % domain
|
|
|
|
|
output = output.encode(self.encoding)
|
|
|
|
|
send_response(start_response, '400 Unknown record type', output,
|
|
|
|
|
plaintype)
|
|
|
|
|
except dns.resolver.NoAnswer: # TODO: use raise_on_no_answer=False in query() ?
|
|
|
|
|
# It appeared apparently only with dnspython 1.9.
|
|
|
|
|
query_end = datetime.now()
|
|
|
|
|
self.delay = query_end - query_start
|
|
|
|
|
formatter.format(None, qtype, 0, self)
|
|
|
|
|
output = formatter.result(self)
|
|
|
|
|
send_response(start_response, '200 OK', output, mtype)
|
|
|
|
|
# TODO: other exceptions, specially SERVFAIL (for instance
|
|
|
|
|
# with bogus DNSSEC like reverseddates-A.test.dnssec-tools.org
|
|
|
|
|
# (see issue #4). dnspython apparently returns Timeout :-(
|
|
|
|
|
# Fixing this will probably require to switch to the low-level
|
|
|
|
|
# interface of DNS Python. See issue #3.
|
|
|
|
|
# TODO send back HTML if this is the expected format. In
|
|
|
|
|
# that case, do not serialize output.
|
|
|
|
|
send_response(start_response, '504 No positive answer', output,
|
|
|
|
|
"text/plain")
|
|
|
|
|
except Resolver.UnknownError as code:
|
|
|
|
|
output = "Unknown error %s resolving %s\n" % (dns.rcode.to_text(int(str(code))), domain)
|
|
|
|
|
output = output.encode(self.encoding)
|
|
|
|
|
# TODO send back HTML if this is the expected format
|
|
|
|
|
send_response(start_response, '500 Unknown server error', output, plaintype)
|
|
|
|
|
return [output]
|
|
|
|
|
|
|
|
|
|
def application(self, environ, start_response):
|
|
|
|
@ -305,11 +300,20 @@ Disallow: /
|
|
|
|
|
dotcp = queries.get("tcp", '')
|
|
|
|
|
tcp = not(len(dotcp) == 0 or dotcp[0] == "0" or \
|
|
|
|
|
dotcp[0].lower() == "false" or dotcp[0] == "")
|
|
|
|
|
# TODO: CD bit. See issue #4
|
|
|
|
|
|
|
|
|
|
docd = queries.get("cd", '')
|
|
|
|
|
cd = not(len(docd) == 0 or docd[0] == "0" or \
|
|
|
|
|
docd[0].lower() == "false" or docd[0] == "")
|
|
|
|
|
doreverse = queries.get("reverse", '')
|
|
|
|
|
reverse = not(len(doreverse) == 0 or doreverse[0] == "0" or \
|
|
|
|
|
doreverse[0].lower() == "false" or doreverse[0] == "")
|
|
|
|
|
buffersize = int(queries.get("buffersize", [default_edns_size])[0])
|
|
|
|
|
if cd:
|
|
|
|
|
if not do_dnssec:
|
|
|
|
|
output = "Incompatible arguments"
|
|
|
|
|
send_response(start_response, '400 CD is meaningful only for DNSSEC',
|
|
|
|
|
output, plaintype)
|
|
|
|
|
return [output]
|
|
|
|
|
if buffersize == 0:
|
|
|
|
|
if do_dnssec:
|
|
|
|
|
output = "Buffer size = 0"
|
|
|
|
@ -342,7 +346,7 @@ Disallow: /
|
|
|
|
|
pure_path = path[len(self.base_url):]
|
|
|
|
|
# TODO: content negotiation? Find the output format from Accept headers?
|
|
|
|
|
return self.query(start_response, pure_path, client, format, resolver,
|
|
|
|
|
do_dnssec, tcp, edns_size, reverse)
|
|
|
|
|
do_dnssec, tcp, cd, edns_size, reverse)
|
|
|
|
|
else:
|
|
|
|
|
return self.default(start_response, path)
|
|
|
|
|
|
|
|
|
|