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