]> git.ipfire.org Git - thirdparty/pdns.git/blame - regression-tests.recursor-dnssec/test_PacketCache.py
Merge pull request #13509 from rgacogne/ddist-teeaction-proxyprotocol
[thirdparty/pdns.git] / regression-tests.recursor-dnssec / test_PacketCache.py
CommitLineData
fa980c59
RG
1import clientsubnetoption
2import cookiesoption
3import dns
4import os
5import requests
628fcdb9 6import subprocess
fa980c59
RG
7
8from recursortests import RecursorTest
9
10class PacketCacheRecursorTest(RecursorTest):
11
5b4650e2
PL
12 _auth_zones = {
13 '8': {'threads': 1,
14 'zones': ['ROOT']}
15 }
16
fa980c59
RG
17 _confdir = 'PacketCache'
18 _wsPort = 8042
19 _wsTimeout = 2
20 _wsPassword = 'secretpassword'
21 _apiKey = 'secretapikey'
22 _config_template = """
628fcdb9 23 packetcache-ttl=10
91505918 24 packetcache-negative-ttl=8
628fcdb9 25 packetcache-servfail-ttl=5
fa980c59
RG
26 auth-zones=example=configs/%s/example.zone
27 webserver=yes
28 webserver-port=%d
29 webserver-address=127.0.0.1
30 webserver-password=%s
31 api-key=%s
32 """ % (_confdir, _wsPort, _wsPassword, _apiKey)
33
34 @classmethod
35 def generateRecursorConfig(cls, confdir):
36 authzonepath = os.path.join(confdir, 'example.zone')
37 with open(authzonepath, 'w') as authzone:
38 authzone.write("""$ORIGIN example.
39@ 3600 IN SOA {soa}
40a 3600 IN A 192.0.2.42
41b 3600 IN A 192.0.2.42
42c 3600 IN A 192.0.2.42
43d 3600 IN A 192.0.2.42
44e 3600 IN A 192.0.2.42
628fcdb9 45f 3600 IN CNAME f ; CNAME loop: dirty trick to get a ServFail in an authzone
fa980c59
RG
46""".format(soa=cls._SOA))
47 super(PacketCacheRecursorTest, cls).generateRecursorConfig(confdir)
48
fa980c59 49 def checkPacketCacheMetrics(self, expectedHits, expectedMisses):
f20bacd2 50 self.waitForTCPSocket("127.0.0.1", self._wsPort)
fa980c59
RG
51 headers = {'x-api-key': self._apiKey}
52 url = 'http://127.0.0.1:' + str(self._wsPort) + '/api/v1/servers/localhost/statistics'
53 r = requests.get(url, headers=headers, timeout=self._wsTimeout)
54 self.assertTrue(r)
4bfebc93 55 self.assertEqual(r.status_code, 200)
fa980c59
RG
56 self.assertTrue(r.json())
57 content = r.json()
58 foundHits = False
59 foundMisses = True
60 for entry in content:
61 if entry['name'] == 'packetcache-hits':
62 foundHits = True
4bfebc93 63 self.assertEqual(int(entry['value']), expectedHits)
fa980c59
RG
64 elif entry['name'] == 'packetcache-misses':
65 foundMisses = True
4bfebc93 66 self.assertEqual(int(entry['value']), expectedMisses)
fa980c59
RG
67
68 self.assertTrue(foundHits)
69 self.assertTrue(foundMisses)
70
71 def testPacketCache(self):
34e6e52f 72 self.waitForTCPSocket("127.0.0.1", self._wsPort)
fa980c59
RG
73 # first query, no cookie
74 qname = 'a.example.'
75 query = dns.message.make_query(qname, 'A', want_dnssec=True)
76 expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
77
78 for method in ("sendUDPQuery", "sendTCPQuery"):
79 sender = getattr(self, method)
80 res = sender(query)
81 self.assertRcodeEqual(res, dns.rcode.NOERROR)
82 self.assertRRsetInAnswer(res, expected)
83
282b5da9 84 self.checkPacketCacheMetrics(0, 2)
fa980c59
RG
85
86 # we should get a hit over UDP this time
87 res = self.sendUDPQuery(query)
88 self.assertRcodeEqual(res, dns.rcode.NOERROR)
89 self.assertRRsetInAnswer(res, expected)
282b5da9
O
90 self.checkPacketCacheMetrics(1, 2)
91
92 # we should get a hit over TCP this time
93 res = self.sendTCPQuery(query)
94 self.assertRcodeEqual(res, dns.rcode.NOERROR)
95 self.assertRRsetInAnswer(res, expected)
96 self.checkPacketCacheMetrics(2, 2)
fa980c59
RG
97
98 eco1 = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
99 eco2 = cookiesoption.CookiesOption(b'deadc0de', b'deadc0de')
100 ecso1 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
101 ecso2 = clientsubnetoption.ClientSubnetOption('192.0.2.2', 32)
102
103 # we add a cookie, should not match anymore
104 query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco1])
105 res = self.sendUDPQuery(query)
106 self.assertRcodeEqual(res, dns.rcode.NOERROR)
107 self.assertRRsetInAnswer(res, expected)
282b5da9 108 self.checkPacketCacheMetrics(2, 3)
fa980c59
RG
109
110 # same cookie, should match
111 query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco1])
112 res = self.sendUDPQuery(query)
113 self.assertRcodeEqual(res, dns.rcode.NOERROR)
114 self.assertRRsetInAnswer(res, expected)
282b5da9 115 self.checkPacketCacheMetrics(3, 3)
fa980c59
RG
116
117 # different cookie, should still match
118 query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco2])
119 res = self.sendUDPQuery(query)
120 self.assertRcodeEqual(res, dns.rcode.NOERROR)
121 self.assertRRsetInAnswer(res, expected)
282b5da9 122 self.checkPacketCacheMetrics(4, 3)
fa980c59
RG
123
124 # first cookie but with an ECS option, should not match
125 query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco1, ecso1])
126 res = self.sendUDPQuery(query)
127 self.assertRcodeEqual(res, dns.rcode.NOERROR)
128 self.assertRRsetInAnswer(res, expected)
282b5da9 129 self.checkPacketCacheMetrics(4, 4)
fa980c59
RG
130
131 # different cookie but same ECS option, should match
132 query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco2, ecso1])
133 res = self.sendUDPQuery(query)
134 self.assertRcodeEqual(res, dns.rcode.NOERROR)
135 self.assertRRsetInAnswer(res, expected)
282b5da9 136 self.checkPacketCacheMetrics(5, 4)
fa980c59
RG
137
138 # first cookie but different ECS option, should still match (we ignore EDNS Client Subnet
139 # in the recursor's packet cache, but ECS-specific responses are not cached
140 query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco1, ecso2])
141 res = self.sendUDPQuery(query)
142 self.assertRcodeEqual(res, dns.rcode.NOERROR)
143 self.assertRRsetInAnswer(res, expected)
282b5da9 144 self.checkPacketCacheMetrics(6, 4)
628fcdb9 145
91505918 146 # NXDomain should get negative packetcache TTL (8)
628fcdb9
O
147 query = dns.message.make_query('nxdomain.example.', 'A', want_dnssec=True)
148 res = self.sendUDPQuery(query)
149 self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
150 self.checkPacketCacheMetrics(6, 5)
151
91505918 152 # NoData should get negative packetcache TTL (8)
628fcdb9
O
153 query = dns.message.make_query('a.example.', 'AAAA', want_dnssec=True)
154 res = self.sendUDPQuery(query)
155 self.assertRcodeEqual(res, dns.rcode.NOERROR)
156 self.checkPacketCacheMetrics(6, 6)
157
158 # ServFail should get ServFail TTL (5)
159 query = dns.message.make_query('f.example.', 'A', want_dnssec=True)
160 res = self.sendUDPQuery(query)
161 self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
162 self.checkPacketCacheMetrics(6, 7)
163
a4a0c6a8 164 # We peek into the cache to check TTLs and allow TTLs to be one lower than inserted since the clock might have ticked
628fcdb9
O
165 rec_controlCmd = [os.environ['RECCONTROL'],
166 '--config-dir=%s' % 'configs/' + self._confdir,
167 'dump-cache', '-']
168 try:
169 ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT)
170 self.assertTrue((b"a.example. 10 A ; tag 0 udp\n" in ret) or (b"a.example. 9 A ; tag 0 udp\n" in ret))
91505918
OM
171 self.assertTrue((b"nxdomain.example. 8 A ; tag 0 udp\n" in ret) or (b"nxdomain.example. 7 A ; tag 0 udp\n" in ret))
172 self.assertTrue((b"a.example. 8 AAAA ; tag 0 udp\n" in ret) or (b"a.example. 7 AAAA ; tag 0 udp\n" in ret))
628fcdb9
O
173 self.assertTrue((b"f.example. 5 A ; tag 0 udp\n" in ret) or (b"f.example. 4 A ; tag 0 udp\n" in ret))
174
175 except subprocess.CalledProcessError as e:
176 print(e.output)
177 raise
178