]> git.ipfire.org Git - thirdparty/pdns.git/blame - regression-tests.recursor-dnssec/test_PacketCache.py
Another case of too early API access
[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):
34e6e52f 70 self.waitForTCPSocket("127.0.0.1", self._wsPort)
fa980c59
RG
71 # first query, no cookie
72 qname = 'a.example.'
73 query = dns.message.make_query(qname, 'A', want_dnssec=True)
74 expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
75
76 for method in ("sendUDPQuery", "sendTCPQuery"):
77 sender = getattr(self, method)
78 res = sender(query)
79 self.assertRcodeEqual(res, dns.rcode.NOERROR)
80 self.assertRRsetInAnswer(res, expected)
81
282b5da9 82 self.checkPacketCacheMetrics(0, 2)
fa980c59
RG
83
84 # we should get a hit over UDP this time
85 res = self.sendUDPQuery(query)
86 self.assertRcodeEqual(res, dns.rcode.NOERROR)
87 self.assertRRsetInAnswer(res, expected)
282b5da9
O
88 self.checkPacketCacheMetrics(1, 2)
89
90 # we should get a hit over TCP this time
91 res = self.sendTCPQuery(query)
92 self.assertRcodeEqual(res, dns.rcode.NOERROR)
93 self.assertRRsetInAnswer(res, expected)
94 self.checkPacketCacheMetrics(2, 2)
fa980c59
RG
95
96 eco1 = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
97 eco2 = cookiesoption.CookiesOption(b'deadc0de', b'deadc0de')
98 ecso1 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
99 ecso2 = clientsubnetoption.ClientSubnetOption('192.0.2.2', 32)
100
101 # we add a cookie, should not match anymore
102 query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco1])
103 res = self.sendUDPQuery(query)
104 self.assertRcodeEqual(res, dns.rcode.NOERROR)
105 self.assertRRsetInAnswer(res, expected)
282b5da9 106 self.checkPacketCacheMetrics(2, 3)
fa980c59
RG
107
108 # same cookie, should match
109 query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco1])
110 res = self.sendUDPQuery(query)
111 self.assertRcodeEqual(res, dns.rcode.NOERROR)
112 self.assertRRsetInAnswer(res, expected)
282b5da9 113 self.checkPacketCacheMetrics(3, 3)
fa980c59
RG
114
115 # different cookie, should still match
116 query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco2])
117 res = self.sendUDPQuery(query)
118 self.assertRcodeEqual(res, dns.rcode.NOERROR)
119 self.assertRRsetInAnswer(res, expected)
282b5da9 120 self.checkPacketCacheMetrics(4, 3)
fa980c59
RG
121
122 # first cookie but with an ECS option, should not match
123 query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco1, ecso1])
124 res = self.sendUDPQuery(query)
125 self.assertRcodeEqual(res, dns.rcode.NOERROR)
126 self.assertRRsetInAnswer(res, expected)
282b5da9 127 self.checkPacketCacheMetrics(4, 4)
fa980c59
RG
128
129 # different cookie but same ECS option, should match
130 query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco2, ecso1])
131 res = self.sendUDPQuery(query)
132 self.assertRcodeEqual(res, dns.rcode.NOERROR)
133 self.assertRRsetInAnswer(res, expected)
282b5da9 134 self.checkPacketCacheMetrics(5, 4)
fa980c59
RG
135
136 # first cookie but different ECS option, should still match (we ignore EDNS Client Subnet
137 # in the recursor's packet cache, but ECS-specific responses are not cached
138 query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco1, ecso2])
139 res = self.sendUDPQuery(query)
140 self.assertRcodeEqual(res, dns.rcode.NOERROR)
141 self.assertRRsetInAnswer(res, expected)
282b5da9 142 self.checkPacketCacheMetrics(6, 4)
628fcdb9
O
143
144 # NXDomain should get default packetcache TTL (10)
145 query = dns.message.make_query('nxdomain.example.', 'A', want_dnssec=True)
146 res = self.sendUDPQuery(query)
147 self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
148 self.checkPacketCacheMetrics(6, 5)
149
150 # NoData should get default packetcache TTL (10)
151 query = dns.message.make_query('a.example.', 'AAAA', want_dnssec=True)
152 res = self.sendUDPQuery(query)
153 self.assertRcodeEqual(res, dns.rcode.NOERROR)
154 self.checkPacketCacheMetrics(6, 6)
155
156 # ServFail should get ServFail TTL (5)
157 query = dns.message.make_query('f.example.', 'A', want_dnssec=True)
158 res = self.sendUDPQuery(query)
159 self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
160 self.checkPacketCacheMetrics(6, 7)
161
a4a0c6a8 162 # 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
163 rec_controlCmd = [os.environ['RECCONTROL'],
164 '--config-dir=%s' % 'configs/' + self._confdir,
165 'dump-cache', '-']
166 try:
167 ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT)
168 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))
169 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))
170 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))
171 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))
172
173 except subprocess.CalledProcessError as e:
174 print(e.output)
175 raise
176