]>
git.ipfire.org Git - thirdparty/pdns.git/blob - regression-tests.recursor-dnssec/test_PacketCache.py
1 import clientsubnetoption
8 from recursortests
import RecursorTest
10 class PacketCacheRecursorTest(RecursorTest
):
17 _confdir
= 'PacketCache'
20 _wsPassword
= 'secretpassword'
21 _apiKey
= 'secretapikey'
22 _config_template
= """
24 packetcache-negative-ttl=8
25 packetcache-servfail-ttl=5
26 auth-zones=example=configs/%s/example.zone
29 webserver-address=127.0.0.1
32 """ % (_confdir
, _wsPort
, _wsPassword
, _apiKey
)
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.
40 a 3600 IN A 192.0.2.42
41 b 3600 IN A 192.0.2.42
42 c 3600 IN A 192.0.2.42
43 d 3600 IN A 192.0.2.42
44 e 3600 IN A 192.0.2.42
45 f 3600 IN CNAME f ; CNAME loop: dirty trick to get a ServFail in an authzone
46 """.format(soa
=cls
._SOA
))
47 super(PacketCacheRecursorTest
, cls
).generateRecursorConfig(confdir
)
49 def checkPacketCacheMetrics(self
, expectedHits
, expectedMisses
):
50 self
.waitForTCPSocket("127.0.0.1", self
._wsPort
)
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
)
55 self
.assertEqual(r
.status_code
, 200)
56 self
.assertTrue(r
.json())
61 if entry
['name'] == 'packetcache-hits':
63 self
.assertEqual(int(entry
['value']), expectedHits
)
64 elif entry
['name'] == 'packetcache-misses':
66 self
.assertEqual(int(entry
['value']), expectedMisses
)
68 self
.assertTrue(foundHits
)
69 self
.assertTrue(foundMisses
)
71 def testPacketCache(self
):
72 self
.waitForTCPSocket("127.0.0.1", self
._wsPort
)
73 # first query, no cookie
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')
78 for method
in ("sendUDPQuery", "sendTCPQuery"):
79 sender
= getattr(self
, method
)
81 self
.assertRcodeEqual(res
, dns
.rcode
.NOERROR
)
82 self
.assertRRsetInAnswer(res
, expected
)
84 self
.checkPacketCacheMetrics(0, 2)
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
)
90 self
.checkPacketCacheMetrics(1, 2)
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)
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)
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
)
108 self
.checkPacketCacheMetrics(2, 3)
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
)
115 self
.checkPacketCacheMetrics(3, 3)
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
)
122 self
.checkPacketCacheMetrics(4, 3)
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
)
129 self
.checkPacketCacheMetrics(4, 4)
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
)
136 self
.checkPacketCacheMetrics(5, 4)
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
)
144 self
.checkPacketCacheMetrics(6, 4)
146 # NXDomain should get negative packetcache TTL (8)
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)
152 # NoData should get negative packetcache TTL (8)
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)
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)
164 # We peek into the cache to check TTLs and allow TTLs to be one lower than inserted since the clock might have ticked
165 rec_controlCmd
= [os
.environ
['RECCONTROL'],
166 '--config-dir=%s' % 'configs/' + self
._confdir
,
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
))
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
))
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
))
175 except subprocess
.CalledProcessError
as e
: