optout.example. 3600 IN NS ns1.optout.example.
optout.example. 3600 IN DS 59332 13 1 e664f886ae1b5df03d918bc1217d22afc29925b9
ns1.optout.example. 3600 IN A {prefix}.14
+
+insecure-formerr.example. 3600 IN NS ns1.insecure-formerr.example.
+ns1.insecure-formerr.example. 3600 IN A {prefix}.2
""",
'secure.example': """
secure.example. 3600 IN SOA {soa}
*.cnamewildcard.secure.example. 3600 IN CNAME host1.secure.example.
*.cnamewildcardnxdomain.secure.example. 3600 IN CNAME doesntexist.secure.example.
+
+cname-to-formerr.secure.example. 3600 IN CNAME host1.insecure-formerr.example.
""",
'bogus.example': """
bogus.example. 3600 IN SOA {soa}
roothints.write(cls._roothints)
conf.write("hint-file=%s\n" % roothintspath)
+ @classmethod
+ def startResponders(cls):
+ pass
+
@classmethod
def startRecursor(cls, confdir, port):
print("Launching pdns_recursor..")
recursorcmd = [os.environ['PDNSRECURSOR'],
'--config-dir=%s' % confdir,
- '--local-port=%s' % port]
+ '--local-port=%s' % port,
+ '--security-poll-suffix=']
print(' '.join(recursorcmd))
logFile = os.path.join(confdir, 'recursor.log')
@classmethod
def setUpClass(cls):
cls.setUpSockets()
+
+ cls.startResponders()
+
confdir = os.path.join('configs', cls._confdir)
cls.createConfigDir(confdir)
cls.generateAllAuthConfig(confdir)
def tearDownClass(cls):
cls.tearDownRecursor()
cls.tearDownAuth()
+ cls.tearDownResponders()
+
+ @classmethod
+ def tearDownResponders(cls):
+ pass
@classmethod
def tearDownAuth(cls):
if e.errno != errno.ESRCH:
raise
-
@classmethod
def sendUDPQuery(cls, query, timeout=2.0):
if timeout:
--- /dev/null
+import dns
+import socket
+import copy
+from recursortests import RecursorTest
+from twisted.internet.protocol import DatagramProtocol
+from twisted.internet import reactor
+import threading
+
+class testInterop(RecursorTest):
+ _confdir = 'Interop'
+
+ _config_template = """dnssec=validate"""
+
+ def testFORMERR(self):
+ """
+ #3841, when we encounter a server that does not understands OPT records
+ (or something else), we don't retry without EDNS in dnssec=validate mode
+ """
+ expected = dns.rrset.from_text('host1.insecure-formerr.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
+
+ query = dns.message.make_query('cname-to-formerr.secure.example.', 'A')
+ res = self.sendUDPQuery(query)
+
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+
+ @classmethod
+ def startResponders(cls):
+ print("Launching responders..")
+
+ address = cls._PREFIX + '.2'
+ port = 53
+
+ reactor.listenUDP(port, UDPResponder(), interface=address)
+
+ cls._UDPResponder = threading.Thread(name='UDP Responder', target=reactor.run, args=(False,))
+ cls._UDPResponder.setDaemon(True)
+ cls._UDPResponder.start()
+
+ @classmethod
+ def tearDownResponders(cls):
+ reactor.stop()
+
+class UDPResponder(DatagramProtocol):
+ def datagramReceived(self, datagram, address):
+ request = dns.message.from_wire(datagram)
+
+ response = dns.message.make_response(request)
+ response.flags = dns.flags.AA + dns.flags.QR
+
+ if request.edns != -1:
+ response.set_rcode(dns.rcode.FORMERR)
+ response.edns = -1
+ response.additional = []
+ else:
+ answer = dns.rrset.from_text('host1.insecure-formerr.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
+ response.answer.append(answer)
+
+ self.transport.write(response.to_wire(), address)