We used to check the netmask for all entries for a qname
if at least one of them was a subnet specific one. Since an empty
`Netmask` doesn't match anything, we would effectively ignore every
non subnet specific entries if we had at least one subnet specific
one.
This caused a very hard to reproduce issue with for example
f.root-servers.net that includes an EDNS Client Subnet option in its
answer for `NS .` if the query has an EDNS Client Subnet option.
This caused the recursor to cache a subnet specific entry for `NS .`.
When that entry expired, we retrieved and cached a non subnet specific
one, but that new one was ignored as long as the subnet specific
was not expunged from the cache.
Under certain circumstances that could cause a root refresh loop
using a lot of stack memory.
(cherry picked from commit
65fdd185f4930f685b87340d29535f40d8b52fb3)
if(res)
res->clear();
- bool haveSubnetSpecific=false;
if(d_cachecache.first!=d_cachecache.second) {
- for(cache_t::const_iterator i=d_cachecache.first; i != d_cachecache.second; ++i) {
- if(!i->d_netmask.empty()) {
- // cout<<"Had a subnet specific hit: "<<i->d_netmask.toString()<<", query was for "<<who.toString()<<": match "<<i->d_netmask.match(who)<<endl;
- haveSubnetSpecific=true;
- }
- }
for(cache_t::const_iterator i=d_cachecache.first; i != d_cachecache.second; ++i)
if(i->d_ttd > now && ((i->d_qtype == qt.getCode() || qt.getCode()==QType::ANY ||
(qt.getCode()==QType::ADDR && (i->d_qtype == QType::A || i->d_qtype == QType::AAAA) ))
- && (!haveSubnetSpecific || i->d_netmask.match(who)))
+ && (i->d_netmask.empty() || i->d_netmask.match(who)))
) {
ttd = i->d_ttd;