]>
Commit | Line | Data |
---|---|---|
86c152f2 BH |
1 | /* |
2 | PowerDNS Versatile Database Driven Nameserver | |
7e2e4419 | 3 | Copyright (C) 2003 - 2015 PowerDNS.COM BV |
86c152f2 BH |
4 | |
5 | This program is free software; you can redistribute it and/or modify | |
710af846 | 6 | it under the terms of the GNU General Public License version 2 as published |
36c5ee42 | 7 | by the Free Software Foundation |
86c152f2 | 8 | |
f782fe38 MH |
9 | Additionally, the license of this program contains a special |
10 | exception which allows to distribute the program in binary form when | |
11 | it is linked against OpenSSL. | |
12 | ||
86c152f2 BH |
13 | This program is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with this program; if not, write to the Free Software | |
06bd9ccf | 20 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
86c152f2 | 21 | */ |
caa6eefa | 22 | |
870a0fe4 AT |
23 | #ifdef HAVE_CONFIG_H |
24 | #include "config.h" | |
25 | #endif | |
c9f52071 | 26 | #include <boost/algorithm/string.hpp> |
21f0f88b | 27 | #include <boost/foreach.hpp> |
3457a2a0 | 28 | #include "lua-recursor.hh" |
caa6eefa | 29 | #include "utility.hh" |
288f4aa9 | 30 | #include "syncres.hh" |
86c152f2 BH |
31 | #include <iostream> |
32 | #include <map> | |
57769f13 | 33 | #include "dnsrecords.hh" |
86c152f2 | 34 | #include <algorithm> |
afbe2787 | 35 | #include <set> |
86c152f2 BH |
36 | #include <cerrno> |
37 | #include <cstdio> | |
38 | #include <cstdlib> | |
86c152f2 | 39 | #include <utility> |
3de83124 | 40 | #include <deque> |
c836dc19 | 41 | #include "logger.hh" |
20177d1d | 42 | #include "misc.hh" |
86c152f2 BH |
43 | #include "arguments.hh" |
44 | #include "lwres.hh" | |
eefd15f9 | 45 | #include "recursor_cache.hh" |
ea634573 | 46 | #include "dnsparser.hh" |
51e2144e | 47 | #include "dns_random.hh" |
bb4bdbaf | 48 | #include "lock.hh" |
376effcf | 49 | #include "ednssubnet.hh" |
c2567ad1 | 50 | #include "cachecleaner.hh" |
eefd15f9 | 51 | |
ac0e821b | 52 | __thread SyncRes::StaticStorage* t_sstorage; |
bb4bdbaf | 53 | |
a9af3782 | 54 | unsigned int SyncRes::s_maxnegttl; |
c3e753c7 | 55 | unsigned int SyncRes::s_maxcachettl; |
1051f8a9 BH |
56 | unsigned int SyncRes::s_packetcachettl; |
57 | unsigned int SyncRes::s_packetcacheservfailttl; | |
628e2c7b PA |
58 | unsigned int SyncRes::s_serverdownmaxfails; |
59 | unsigned int SyncRes::s_serverdownthrottletime; | |
75ba907b | 60 | uint64_t SyncRes::s_queries; |
61 | uint64_t SyncRes::s_outgoingtimeouts; | |
7b75810e | 62 | uint64_t SyncRes::s_outgoing4timeouts; |
63 | uint64_t SyncRes::s_outgoing6timeouts; | |
75ba907b | 64 | uint64_t SyncRes::s_outqueries; |
65 | uint64_t SyncRes::s_tcpoutqueries; | |
66 | uint64_t SyncRes::s_throttledqueries; | |
67 | uint64_t SyncRes::s_dontqueries; | |
68 | uint64_t SyncRes::s_nodelegated; | |
69 | uint64_t SyncRes::s_unreachables; | |
aadceba8 | 70 | unsigned int SyncRes::s_minimumTTL; |
996c89cc | 71 | bool SyncRes::s_doIPv6; |
1051f8a9 | 72 | bool SyncRes::s_nopacketcache; |
01402d56 | 73 | bool SyncRes::s_rootNXTrust; |
173d790e | 74 | unsigned int SyncRes::s_maxqperq; |
9de3e034 | 75 | unsigned int SyncRes::s_maxtotusec; |
a9af3782 | 76 | string SyncRes::s_serverID; |
77499b05 | 77 | SyncRes::LogMode SyncRes::s_lm; |
c836dc19 | 78 | |
77499b05 | 79 | #define LOG(x) if(d_lm == Log) { L <<Logger::Warning << x; } else if(d_lm == Store) { d_trace << x; } |
728485ca | 80 | |
4bfae16d | 81 | bool SyncRes::s_noEDNS; |
3de83124 | 82 | |
7b75810e | 83 | void accountAuthLatency(int usec, int family) |
11adfdd3 | 84 | { |
7b75810e | 85 | if(family == AF_INET) { |
86 | if(usec < 1000) | |
87 | g_stats.auth4Answers0_1++; | |
88 | else if(usec < 10000) | |
89 | g_stats.auth4Answers1_10++; | |
90 | else if(usec < 100000) | |
91 | g_stats.auth4Answers10_100++; | |
92 | else if(usec < 1000000) | |
93 | g_stats.auth4Answers100_1000++; | |
94 | else | |
95 | g_stats.auth4AnswersSlow++; | |
96 | } else { | |
97 | if(usec < 1000) | |
98 | g_stats.auth6Answers0_1++; | |
99 | else if(usec < 10000) | |
100 | g_stats.auth6Answers1_10++; | |
101 | else if(usec < 100000) | |
102 | g_stats.auth6Answers10_100++; | |
103 | else if(usec < 1000000) | |
104 | g_stats.auth6Answers100_1000++; | |
105 | else | |
106 | g_stats.auth6AnswersSlow++; | |
107 | } | |
108 | ||
11adfdd3 | 109 | } |
110 | ||
4465e941 | 111 | |
ac0e821b | 112 | SyncRes::SyncRes(const struct timeval& now) : d_outqueries(0), d_tcpoutqueries(0), d_throttledqueries(0), d_timeouts(0), d_unreachables(0), |
57769f13 | 113 | d_totUsec(0), d_doDNSSEC(false), d_now(now), |
114 | d_cacheonly(false), d_nocache(false), d_doEDNS0(false), d_lm(s_lm) | |
232f0877 | 115 | |
ac0e821b BH |
116 | { |
117 | if(!t_sstorage) { | |
118 | t_sstorage = new StaticStorage(); | |
119 | } | |
120 | } | |
121 | ||
728485ca | 122 | /** everything begins here - this is the entry point just after receiving a packet */ |
e325f20c | 123 | int SyncRes::beginResolve(const DNSName &qname, const QType &qtype, uint16_t qclass, vector<DNSRecord>&ret) |
728485ca | 124 | { |
c836dc19 | 125 | s_queries++; |
3762e821 | 126 | d_wasVariable=false; |
710af846 PL |
127 | |
128 | if( (qtype.getCode() == QType::AXFR)) | |
693dbe65 | 129 | return -1; |
710af846 | 130 | |
8171ab83 | 131 | static const DNSName arpa("1.0.0.127.in-addr.arpa."), localhost("localhost."), |
132 | versionbind("version.bind."), idserver("id.server."), versionpdns("version.pdns."); | |
133 | ||
134 | if( (qtype.getCode()==QType::PTR && qname==arpa) || | |
135 | (qtype.getCode()==QType::A && qname==localhost)) { | |
31ad43ab | 136 | ret.clear(); |
e325f20c | 137 | DNSRecord dr; |
138 | dr.d_name=qname; | |
e693ff5a | 139 | dr.d_place = DNSResourceRecord::ANSWER; |
e325f20c | 140 | dr.d_type=qtype.getCode(); |
141 | dr.d_class=QClass::IN; | |
142 | dr.d_ttl=86400; | |
31ad43ab | 143 | if(qtype.getCode()==QType::PTR) |
e325f20c | 144 | dr.d_content=shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(QType::PTR, 1, "localhost.")); |
31ad43ab | 145 | else |
e325f20c | 146 | dr.d_content=shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(QType::A, 1, "127.0.0.1")); |
147 | ret.push_back(dr); | |
31ad43ab BH |
148 | return 0; |
149 | } | |
150 | ||
703761cc | 151 | if(qclass==QClass::CHAOS && qtype.getCode()==QType::TXT && |
8171ab83 | 152 | (qname==versionbind || qname==idserver || qname==versionpdns ) |
a9af3782 BH |
153 | ) { |
154 | ret.clear(); | |
e325f20c | 155 | DNSRecord dr; |
156 | dr.d_name=qname; | |
157 | dr.d_type=qtype.getCode(); | |
158 | dr.d_class=qclass; | |
159 | dr.d_ttl=86400; | |
e693ff5a | 160 | dr.d_place = DNSResourceRecord::ANSWER; |
8171ab83 | 161 | if(qname==versionbind || qname==versionpdns) |
e325f20c | 162 | dr.d_content=shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(QType::TXT, 3, "\""+::arg()["version-string"]+"\"")); |
a9af3782 | 163 | else |
e325f20c | 164 | dr.d_content=shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(QType::TXT, 3, "\""+s_serverID+"\"")); |
165 | ||
166 | ret.push_back(dr); | |
a9af3782 BH |
167 | return 0; |
168 | } | |
710af846 | 169 | |
703761cc KM |
170 | if(qclass==QClass::ANY) |
171 | qclass=QClass::IN; | |
172 | else if(qclass!=QClass::IN) | |
9a97cc5c | 173 | return -1; |
710af846 | 174 | |
31ad43ab | 175 | set<GetBestNSAnswer> beenthere; |
809fe23f | 176 | int res=doResolve(qname, qtype, ret, 0, beenthere); |
728485ca BH |
177 | return res; |
178 | } | |
afbe2787 | 179 | |
ab5c053d | 180 | //! This is the 'out of band resolver', in other words, the authoritative server |
e325f20c | 181 | bool SyncRes::doOOBResolve(const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, int depth, int& res) |
e93c956b | 182 | { |
5605c067 | 183 | string prefix; |
77499b05 | 184 | if(doLog()) { |
5605c067 BH |
185 | prefix=d_prefix; |
186 | prefix.append(depth, ' '); | |
187 | } | |
188 | ||
c5c066bf PD |
189 | LOG(prefix<<qname.toString()<<": checking auth storage for '"<<qname.toString()<<"|"<<qtype.getName()<<"'"<<endl); |
190 | DNSName authdomain(qname); | |
5605c067 BH |
191 | |
192 | domainmap_t::const_iterator iter=getBestAuthZone(&authdomain); | |
49a699c4 | 193 | if(iter==t_sstorage->domainmap->end()) { |
c5c066bf | 194 | LOG(prefix<<qname.toString()<<": auth storage has no zone for this query!"<<endl); |
5605c067 BH |
195 | return false; |
196 | } | |
c5c066bf | 197 | LOG(prefix<<qname.toString()<<": auth storage has data, zone='"<<authdomain.toString()<<"'"<<endl); |
5605c067 BH |
198 | pair<AuthDomain::records_t::const_iterator, AuthDomain::records_t::const_iterator> range; |
199 | ||
200 | range=iter->second.d_records.equal_range(tie(qname)); // partial lookup | |
7bddb139 | 201 | |
5605c067 BH |
202 | ret.clear(); |
203 | AuthDomain::records_t::const_iterator ziter; | |
9e9844e2 | 204 | bool somedata=false; |
5605c067 | 205 | for(ziter=range.first; ziter!=range.second; ++ziter) { |
9e9844e2 | 206 | somedata=true; |
e325f20c | 207 | if(qtype.getCode()==QType::ANY || ziter->d_type==qtype.getCode() || ziter->d_type==QType::CNAME) // let rest of nameserver do the legwork on this one |
5605c067 BH |
208 | ret.push_back(*ziter); |
209 | } | |
9bc8c14c | 210 | if(!ret.empty()) { |
c5c066bf | 211 | LOG(prefix<<qname.toString()<<": exact match in zone '"<<authdomain.toString()<<"'"<<endl); |
9bc8c14c BH |
212 | res=0; |
213 | return true; | |
5605c067 | 214 | } |
9e9844e2 | 215 | if(somedata) { |
c5c066bf | 216 | LOG(prefix<<qname.toString()<<": found record in '"<<authdomain.toString()<<"', but nothing of the right type, sending SOA"<<endl); |
e325f20c | 217 | ziter=iter->second.d_records.find(boost::make_tuple(authdomain, QType::SOA)); |
9e9844e2 | 218 | if(ziter!=iter->second.d_records.end()) { |
e325f20c | 219 | DNSRecord dr=*ziter; |
e693ff5a | 220 | dr.d_place=DNSResourceRecord::AUTHORITY; |
e325f20c | 221 | ret.push_back(dr); |
9e9844e2 BH |
222 | } |
223 | else | |
c5c066bf | 224 | LOG(prefix<<qname.toString()<<": can't find SOA record '"<<authdomain.toString()<<"' in our zone!"<<endl); |
9e9844e2 BH |
225 | res=RCode::NoError; |
226 | return true; | |
227 | } | |
5605c067 | 228 | |
c5c066bf PD |
229 | LOG(prefix<<qname.toString()<<": nothing found so far in '"<<authdomain.toString()<<"', trying wildcards"<<endl); |
230 | DNSName wcarddomain(qname); | |
e325f20c | 231 | while(wcarddomain != iter->first && wcarddomain.chopOff()) { |
c5c066bf | 232 | LOG(prefix<<qname.toString()<<": trying '*."+wcarddomain.toString()+"' in "<<authdomain.toString()<<endl); |
3ddb9247 | 233 | range=iter->second.d_records.equal_range(boost::make_tuple(DNSName("*")+wcarddomain)); |
0d1e259a BH |
234 | if(range.first==range.second) |
235 | continue; | |
236 | ||
237 | for(ziter=range.first; ziter!=range.second; ++ziter) { | |
e325f20c | 238 | DNSRecord dr=*ziter; |
239 | if(dr.d_type == qtype.getCode() || qtype.getCode() == QType::ANY) { | |
240 | dr.d_name = qname; | |
e693ff5a | 241 | dr.d_place=DNSResourceRecord::ANSWER; |
e325f20c | 242 | ret.push_back(dr); |
0d1e259a BH |
243 | } |
244 | } | |
c5c066bf | 245 | LOG(prefix<<qname.toString()<<": in '"<<authdomain.toString()<<"', had wildcard match on '*."+wcarddomain.toString()+"'"<<endl); |
0d1e259a BH |
246 | res=RCode::NoError; |
247 | return true; | |
248 | } | |
249 | ||
c5c066bf | 250 | DNSName nsdomain(qname); |
5605c067 | 251 | |
e325f20c | 252 | while(nsdomain.chopOff() && nsdomain != iter->first) { |
253 | range=iter->second.d_records.equal_range(boost::make_tuple(nsdomain,QType::NS)); | |
5605c067 BH |
254 | if(range.first==range.second) |
255 | continue; | |
256 | ||
257 | for(ziter=range.first; ziter!=range.second; ++ziter) { | |
e325f20c | 258 | DNSRecord dr=*ziter; |
e693ff5a | 259 | dr.d_place=DNSResourceRecord::AUTHORITY; |
e325f20c | 260 | ret.push_back(dr); |
5605c067 BH |
261 | } |
262 | } | |
3ddb9247 | 263 | if(ret.empty()) { |
c5c066bf | 264 | LOG(prefix<<qname.toString()<<": no NS match in zone '"<<authdomain.toString()<<"' either, handing out SOA"<<endl); |
e325f20c | 265 | ziter=iter->second.d_records.find(boost::make_tuple(authdomain, QType::SOA)); |
5605c067 | 266 | if(ziter!=iter->second.d_records.end()) { |
e325f20c | 267 | DNSRecord dr=*ziter; |
e693ff5a | 268 | dr.d_place=DNSResourceRecord::AUTHORITY; |
e325f20c | 269 | ret.push_back(dr); |
5605c067 | 270 | } |
e325f20c | 271 | else { |
c5c066bf | 272 | LOG(prefix<<qname.toString()<<": can't find SOA record '"<<authdomain.toString()<<"' in our zone!"<<endl); |
e325f20c | 273 | } |
5605c067 BH |
274 | res=RCode::NXDomain; |
275 | } | |
710af846 | 276 | else |
5605c067 BH |
277 | res=0; |
278 | ||
9e9844e2 | 279 | return true; |
e93c956b BH |
280 | } |
281 | ||
ff1872cf BH |
282 | void SyncRes::doEDNSDumpAndClose(int fd) |
283 | { | |
284 | FILE* fp=fdopen(fd, "w"); | |
285 | fprintf(fp,"IP Address\tMode\tMode last updated at\n"); | |
57769f13 | 286 | for(const auto& eds : t_sstorage->ednsstatus) { |
287 | fprintf(fp, "%s\t%d\t%s", eds.first.toString().c_str(), (int)eds.second.mode, ctime(&eds.second.modeSetAt)); | |
ff1872cf | 288 | } |
bb4bdbaf | 289 | |
ff1872cf BH |
290 | fclose(fp); |
291 | } | |
292 | ||
376effcf | 293 | int SyncRes::asyncresolveWrapper(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, struct timeval* now, boost::optional<Netmask>& srcmask, LWResult* res) |
81883dcc BH |
294 | { |
295 | /* what is your QUEST? | |
57769f13 | 296 | the goal is to get as many remotes as possible on the highest level of EDNS support |
81883dcc BH |
297 | The levels are: |
298 | ||
81883dcc | 299 | 0) UNKNOWN Unknown state |
57769f13 | 300 | 1) EDNS: Honors EDNS0 |
301 | 2) EDNSIGNORANT: Ignores EDNS0, gives replies without EDNS0 | |
302 | 3) NOEDNS: Generates FORMERR on EDNS queries | |
81883dcc BH |
303 | |
304 | Everybody starts out assumed to be '0'. | |
57769f13 | 305 | If '0', send out EDNS0 |
306 | If you FORMERR us, go to '3', | |
307 | If no EDNS in response, go to '2' | |
308 | If '1', send out EDNS0 | |
309 | If FORMERR, downgrade to 3 | |
310 | If '2', keep on including EDNS0, see what happens | |
81883dcc | 311 | Same behaviour as 0 |
57769f13 | 312 | If '3', send bare queries |
81883dcc BH |
313 | */ |
314 | ||
57769f13 | 315 | g_stats.noEdnsOutQueries++; |
316 | ||
bb4bdbaf | 317 | SyncRes::EDNSStatus* ednsstatus; |
57769f13 | 318 | ednsstatus = &t_sstorage->ednsstatus[ip]; // does this include port? |
81883dcc | 319 | |
bb4bdbaf BH |
320 | if(ednsstatus->modeSetAt && ednsstatus->modeSetAt + 3600 < d_now.tv_sec) { |
321 | *ednsstatus=SyncRes::EDNSStatus(); | |
77499b05 | 322 | // cerr<<"Resetting EDNS Status for "<<ip.toString()<<endl); |
81883dcc BH |
323 | } |
324 | ||
bb4bdbaf | 325 | SyncRes::EDNSStatus::EDNSMode& mode=ednsstatus->mode; |
81883dcc BH |
326 | SyncRes::EDNSStatus::EDNSMode oldmode = mode; |
327 | int EDNSLevel=0; | |
328 | ||
329 | int ret; | |
ff1872cf | 330 | for(int tries = 0; tries < 3; ++tries) { |
e325f20c | 331 | // cerr<<"Remote '"<<ip.toString()<<"' currently in mode "<<mode<<endl; |
57769f13 | 332 | |
333 | if(mode==EDNSStatus::UNKNOWN || mode==EDNSStatus::EDNSOK || mode==EDNSStatus::EDNSIGNORANT) | |
81883dcc | 334 | EDNSLevel = 1; |
81883dcc BH |
335 | else if(mode==EDNSStatus::NOEDNS) { |
336 | g_stats.noEdnsOutQueries++; | |
337 | EDNSLevel = 0; | |
338 | } | |
57769f13 | 339 | |
376effcf | 340 | ret=asyncresolve(ip, domain, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, res); |
57769f13 | 341 | |
81883dcc | 342 | if(ret == 0 || ret < 0) { |
e325f20c | 343 | // cerr<< (ret < 0 ? "Transport error" : "Timeout")<<" for query to "<<ip.toString()<<" for '"<<domain.toString()<<"' (ret="<<ret<<"), no change in mode"<<endl; |
81883dcc BH |
344 | return ret; |
345 | } | |
57769f13 | 346 | else if(mode==EDNSStatus::UNKNOWN || mode==EDNSStatus::EDNSOK || mode == EDNSStatus::EDNSIGNORANT ) { |
347 | if(res->d_rcode == RCode::FormErr || res->d_rcode == RCode::NotImp) { | |
e325f20c | 348 | // cerr<<"Downgrading to NOEDNS because of "<<RCode::to_s(res->d_rcode)<<" for query to "<<ip.toString()<<" for '"<<domain.toString()<<"'"<<endl; |
57769f13 | 349 | mode = EDNSStatus::NOEDNS; |
4957a608 | 350 | continue; |
81883dcc | 351 | } |
81883dcc | 352 | else if(!res->d_haveEDNS) { |
4957a608 BH |
353 | if(mode != EDNSStatus::EDNSIGNORANT) { |
354 | mode = EDNSStatus::EDNSIGNORANT; | |
e325f20c | 355 | // cerr<<"We find that "<<ip.toString()<<" is an EDNS-ignorer for '"<<domain.toString()<<"', moving to mode 3"<<endl; |
57769f13 | 356 | } |
81883dcc | 357 | } |
57769f13 | 358 | else { |
359 | mode = EDNSStatus::EDNSOK; | |
e325f20c | 360 | // cerr<<"We find that "<<ip.toString()<<" is EDNS OK!"<<endl; |
81883dcc | 361 | } |
57769f13 | 362 | |
81883dcc BH |
363 | } |
364 | if(oldmode != mode) | |
bb4bdbaf | 365 | ednsstatus->modeSetAt=d_now.tv_sec; |
e325f20c | 366 | // cerr<<"Result: ret="<<ret<<", EDNS-level: "<<EDNSLevel<<", haveEDNS: "<<res->d_haveEDNS<<", new mode: "<<mode<<endl; |
81883dcc BH |
367 | return ret; |
368 | } | |
369 | return ret; | |
370 | } | |
371 | ||
e325f20c | 372 | int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, int depth, set<GetBestNSAnswer>& beenthere) |
afbe2787 | 373 | { |
ded77b10 | 374 | string prefix; |
77499b05 | 375 | if(doLog()) { |
ded77b10 BH |
376 | prefix=d_prefix; |
377 | prefix.append(depth, ' '); | |
378 | } | |
710af846 | 379 | |
f4df5e89 | 380 | int res=0; |
52683ca3 | 381 | if(!(d_nocache && qtype.getCode()==QType::NS && qname.isRoot())) { |
115d07ad | 382 | if(d_cacheonly) { // very limited OOB support |
263f6a5a | 383 | LWResult lwr; |
c5c066bf PD |
384 | LOG(prefix<<qname.toString()<<": Recursion not requested for '"<<qname.toString()<<"|"<<qtype.getName()<<"', peeking at auth/forward zones"<<endl); |
385 | DNSName authname(qname); | |
115d07ad | 386 | domainmap_t::const_iterator iter=getBestAuthZone(&authname); |
49a699c4 | 387 | if(iter != t_sstorage->domainmap->end()) { |
4957a608 BH |
388 | const vector<ComboAddress>& servers = iter->second.d_servers; |
389 | if(servers.empty()) { | |
390 | ret.clear(); | |
391 | doOOBResolve(qname, qtype, ret, depth, res); | |
392 | return res; | |
393 | } | |
394 | else { | |
395 | const ComboAddress remoteIP = servers.front(); | |
c5c066bf | 396 | LOG(prefix<<qname.toString()<<": forwarding query to hardcoded nameserver '"<< remoteIP.toStringWithPort()<<"' for zone '"<<authname.toString()<<"'"<<endl); |
4957a608 | 397 | |
376effcf | 398 | boost::optional<Netmask> nm; |
399 | res=asyncresolveWrapper(remoteIP, qname, qtype.getCode(), false, false, &d_now, nm, &lwr); | |
4957a608 BH |
400 | // filter out the good stuff from lwr.result() |
401 | ||
48b560ea | 402 | for(const auto& rec : lwr.d_records) { |
e693ff5a | 403 | if(rec.d_place == DNSResourceRecord::ANSWER) |
e325f20c | 404 | ret.push_back(rec); |
4957a608 BH |
405 | } |
406 | return res; | |
407 | } | |
115d07ad BH |
408 | } |
409 | } | |
410 | ||
c836dc19 BH |
411 | if(doCNAMECacheCheck(qname,qtype,ret,depth,res)) // will reroute us if needed |
412 | return res; | |
710af846 | 413 | |
c836dc19 BH |
414 | if(doCacheCheck(qname,qtype,ret,depth,res)) // we done |
415 | return res; | |
416 | } | |
afbe2787 | 417 | |
115d07ad | 418 | if(d_cacheonly) |
c836dc19 | 419 | return 0; |
728485ca | 420 | |
c5c066bf | 421 | LOG(prefix<<qname.toString()<<": No cache hit for '"<<qname.toString()<<"|"<<qtype.getName()<<"', trying to find an appropriate NS record"<<endl); |
710af846 | 422 | |
c5c066bf | 423 | DNSName subdomain(qname); |
728485ca | 424 | |
c5c066bf | 425 | set<DNSName> nsset; |
7305df82 | 426 | bool flawedNSSet=false; |
97df07f8 PD |
427 | |
428 | // the two retries allow getBestNSNamesFromCache&co to reprime the root | |
429 | // hints, in case they ever go missing | |
bdf40704 | 430 | for(int tries=0;tries<2 && nsset.empty();++tries) { |
891fbf88 | 431 | subdomain=getBestNSNamesFromCache(subdomain, qtype, nsset, &flawedNSSet, depth, beenthere); // pass beenthere to both occasions |
bdf40704 BH |
432 | } |
433 | ||
7305df82 | 434 | if(!(res=doResolveAt(nsset, subdomain, flawedNSSet, qname, qtype, ret, depth, beenthere))) |
728485ca | 435 | return 0; |
3ddb9247 | 436 | |
c5c066bf | 437 | LOG(prefix<<qname.toString()<<": failed (res="<<res<<")"<<endl); |
e325f20c | 438 | ; |
20177d1d | 439 | return res<0 ? RCode::ServFail : res; |
afbe2787 BH |
440 | } |
441 | ||
c2567ad1 | 442 | #if 0 |
a67dd0cf | 443 | // for testing purposes |
fdf05fd4 BH |
444 | static bool ipv6First(const ComboAddress& a, const ComboAddress& b) |
445 | { | |
446 | return !(a.sin4.sin_family < a.sin4.sin_family); | |
447 | } | |
c2567ad1 | 448 | #endif |
fdf05fd4 | 449 | |
21f0f88b | 450 | /** This function explicitly goes out for A or AAAA addresses |
996c89cc | 451 | */ |
c5c066bf | 452 | vector<ComboAddress> SyncRes::getAddrs(const DNSName &qname, int depth, set<GetBestNSAnswer>& beenthere) |
75b49099 | 453 | { |
e325f20c | 454 | typedef vector<DNSRecord> res_t; |
bfea0d0b | 455 | res_t res; |
75b49099 | 456 | |
996c89cc BH |
457 | typedef vector<ComboAddress> ret_t; |
458 | ret_t ret; | |
75b49099 | 459 | |
d96e88da | 460 | QType type; |
92011b8f | 461 | |
462 | for(int j=1; j<2+s_doIPv6; j++) | |
d96e88da | 463 | { |
76c01aec | 464 | bool done=false; |
76c01aec PD |
465 | switch(j) { |
466 | case 0: | |
467 | type = QType::ANY; | |
468 | break; | |
469 | case 1: | |
470 | type = QType::A; | |
471 | break; | |
472 | case 2: | |
473 | type = QType::AAAA; | |
474 | break; | |
475 | } | |
d96e88da | 476 | |
891fbf88 | 477 | if(!doResolve(qname, type, res,depth+1, beenthere) && !res.empty()) { // this consults cache, OR goes out |
d96e88da | 478 | for(res_t::const_iterator i=res.begin(); i!= res.end(); ++i) { |
e325f20c | 479 | if(i->d_type == QType::A || i->d_type == QType::AAAA) { |
480 | if(auto rec = std::dynamic_pointer_cast<ARecordContent>(i->d_content)) | |
481 | ret.push_back(rec->getCA(53)); | |
482 | else if(auto rec = std::dynamic_pointer_cast<AAAARecordContent>(i->d_content)) | |
483 | ret.push_back(rec->getCA(53)); | |
92011b8f | 484 | done=true; |
d96e88da | 485 | } |
42724edf | 486 | } |
f4df5e89 | 487 | } |
710af846 | 488 | if(done) { |
60c9a54f | 489 | if(j==1 && s_doIPv6) { // we got an A record, see if we have some AAAA lying around |
e325f20c | 490 | vector<DNSRecord> cset; |
376effcf | 491 | if(t_RC->get(d_now.tv_sec, qname, QType(QType::AAAA), &cset, d_requestor) > 0) { |
e325f20c | 492 | for(auto k=cset.cbegin();k!=cset.cend();++k) { |
493 | if(k->d_ttl > (unsigned int)d_now.tv_sec ) { | |
494 | ComboAddress ca=std::dynamic_pointer_cast<AAAARecordContent>(k->d_content)->getCA(53); | |
495 | ret.push_back(ca); | |
60c9a54f | 496 | } |
497 | } | |
498 | } | |
499 | } | |
500 | break; | |
501 | } | |
bfea0d0b | 502 | } |
710af846 | 503 | |
996c89cc | 504 | if(ret.size() > 1) { |
51e2144e | 505 | random_shuffle(ret.begin(), ret.end(), dns_random); |
996c89cc | 506 | |
ae4d8cf1 | 507 | // move 'best' address for this nameserver name up front |
5ea6f7de | 508 | nsspeeds_t::iterator best = t_sstorage->nsSpeeds.find(qname); |
996c89cc | 509 | |
49a699c4 | 510 | if(best != t_sstorage->nsSpeeds.end()) |
710af846 | 511 | for(ret_t::iterator i=ret.begin(); i != ret.end(); ++i) { |
4957a608 BH |
512 | if(*i==best->second.d_best) { // got the fastest one |
513 | if(i!=ret.begin()) { | |
514 | *i=*ret.begin(); | |
515 | *ret.begin()=best->second.d_best; | |
516 | } | |
517 | break; | |
518 | } | |
996c89cc BH |
519 | } |
520 | } | |
fdf05fd4 | 521 | |
728485ca | 522 | return ret; |
75b49099 BH |
523 | } |
524 | ||
e325f20c | 525 | void SyncRes::getBestNSFromCache(const DNSName &qname, const QType& qtype, vector<DNSRecord>& bestns, bool* flawedNSSet, int depth, set<GetBestNSAnswer>& beenthere) |
86c152f2 | 526 | { |
c5c066bf PD |
527 | string prefix; |
528 | DNSName subdomain(qname); | |
77499b05 | 529 | if(doLog()) { |
ded77b10 BH |
530 | prefix=d_prefix; |
531 | prefix.append(depth, ' '); | |
532 | } | |
75b49099 | 533 | bestns.clear(); |
2b1b4054 | 534 | bool brokeloop; |
75b49099 | 535 | do { |
2b1b4054 | 536 | brokeloop=false; |
c5c066bf | 537 | LOG(prefix<<qname.toString()<<": Checking if we have NS in cache for '"<<subdomain.toString()<<"'"<<endl); |
e325f20c | 538 | vector<DNSRecord> ns; |
7305df82 | 539 | *flawedNSSet = false; |
376effcf | 540 | if(t_RC->get(d_now.tv_sec, subdomain, QType(QType::NS), &ns, d_requestor) > 0) { |
e325f20c | 541 | for(auto k=ns.cbegin();k!=ns.cend(); ++k) { |
542 | if(k->d_ttl > (unsigned int)d_now.tv_sec ) { | |
543 | vector<DNSRecord> aset; | |
4957a608 | 544 | |
e325f20c | 545 | const DNSRecord& dr=*k; |
546 | auto nrr = std::dynamic_pointer_cast<NSRecordContent>(dr.d_content); | |
547 | if(!nrr->getNS().isPartOf(subdomain) || t_RC->get(d_now.tv_sec, nrr->getNS(), s_doIPv6 ? QType(QType::ADDR) : QType(QType::A), | |
376effcf | 548 | doLog() ? &aset : 0, d_requestor) > 5) { |
e325f20c | 549 | bestns.push_back(dr); |
550 | LOG(prefix<<qname.toString()<<": NS (with ip, or non-glue) in cache for '"<<subdomain.toString()<<"' -> '"<<nrr->getNS()<<"'"<<endl); | |
551 | LOG(prefix<<qname.toString()<<": within bailiwick: "<< nrr->getNS().isPartOf(subdomain)); | |
4957a608 | 552 | if(!aset.empty()) { |
e325f20c | 553 | LOG(", in cache, ttl="<<(unsigned int)(((time_t)aset.begin()->d_ttl- d_now.tv_sec ))<<endl); |
4957a608 BH |
554 | } |
555 | else { | |
77499b05 | 556 | LOG(", not in cache / did not look at cache"<<endl); |
4957a608 BH |
557 | } |
558 | } | |
559 | else { | |
560 | *flawedNSSet=true; | |
e325f20c | 561 | LOG(prefix<<qname<<": NS in cache for '"<<subdomain<<"', but needs glue ("<<nrr->getNS()<<") which we miss or is expired"<<endl); |
4957a608 BH |
562 | } |
563 | } | |
afbe2787 | 564 | } |
75b49099 | 565 | if(!bestns.empty()) { |
4957a608 | 566 | GetBestNSAnswer answer; |
891fbf88 | 567 | answer.qname=qname; |
568 | answer.qtype=qtype.getCode(); | |
e325f20c | 569 | for(const auto& dr : bestns) |
d076a012 | 570 | answer.bestns.insert(make_pair(dr.d_name, std::dynamic_pointer_cast<NSRecordContent>(dr.d_content)->getNS())); |
891fbf88 | 571 | |
4957a608 | 572 | if(beenthere.count(answer)) { |
2b1b4054 | 573 | brokeloop=true; |
c5c066bf | 574 | LOG(prefix<<qname.toString()<<": We have NS in cache for '"<<subdomain.toString()<<"' but part of LOOP (already seen "<<answer.qname.toString()<<")! Trying less specific NS"<<endl); |
e325f20c | 575 | ; |
77499b05 BH |
576 | if(doLog()) |
577 | for( set<GetBestNSAnswer>::const_iterator j=beenthere.begin();j!=beenthere.end();++j) { | |
2b1b4054 | 578 | bool neo = !(*j< answer || answer<*j); |
c5c066bf | 579 | LOG(prefix<<qname.toString()<<": beenthere"<<(neo?"*":"")<<": "<<j->qname.toString()<<"|"<<DNSRecordContent::NumberToType(j->qtype)<<" ("<<(unsigned int)j->bestns.size()<<")"<<endl); |
77499b05 | 580 | } |
4957a608 BH |
581 | bestns.clear(); |
582 | } | |
583 | else { | |
6576051d | 584 | beenthere.insert(answer); |
c5c066bf | 585 | LOG(prefix<<qname.toString()<<": We have NS in cache for '"<<subdomain.toString()<<"' (flawedNSSet="<<*flawedNSSet<<")"<<endl); |
4957a608 BH |
586 | return; |
587 | } | |
75b49099 | 588 | } |
afbe2787 | 589 | } |
c5c066bf | 590 | LOG(prefix<<qname.toString()<<": no valid/useful NS in cache for '"<<subdomain.toString()<<"'"<<endl); |
e325f20c | 591 | ; |
52683ca3 | 592 | if(subdomain.isRoot() && !brokeloop) { |
3ddb9247 | 593 | primeHints(); |
c5c066bf | 594 | LOG(prefix<<qname.toString()<<": reprimed the root"<<endl); |
6576051d | 595 | } |
c5c066bf | 596 | }while(subdomain.chopOff()); |
75b49099 BH |
597 | } |
598 | ||
c5c066bf | 599 | SyncRes::domainmap_t::const_iterator SyncRes::getBestAuthZone(DNSName* qname) |
5605c067 BH |
600 | { |
601 | SyncRes::domainmap_t::const_iterator ret; | |
602 | do { | |
49a699c4 | 603 | ret=t_sstorage->domainmap->find(*qname); |
710af846 | 604 | if(ret!=t_sstorage->domainmap->end()) |
5605c067 | 605 | break; |
c5c066bf | 606 | }while(qname->chopOff()); |
5605c067 BH |
607 | return ret; |
608 | } | |
288f4aa9 | 609 | |
7bf26383 | 610 | /** doesn't actually do the work, leaves that to getBestNSFromCache */ |
c5c066bf | 611 | DNSName SyncRes::getBestNSNamesFromCache(const DNSName &qname, const QType& qtype, set<DNSName>& nsset, bool* flawedNSSet, int depth, set<GetBestNSAnswer>&beenthere) |
75b49099 | 612 | { |
c5c066bf PD |
613 | DNSName subdomain(qname); |
614 | DNSName authdomain(qname); | |
3ddb9247 | 615 | |
5605c067 | 616 | domainmap_t::const_iterator iter=getBestAuthZone(&authdomain); |
49a699c4 | 617 | if(iter!=t_sstorage->domainmap->end()) { |
2e5ae2b2 | 618 | if( iter->second.d_servers.empty() ) |
54313dbc | 619 | nsset.insert(DNSName()); // this gets picked up in doResolveAt, if empty it means "we are auth", otherwise it denotes a forward |
2e5ae2b2 BH |
620 | else { |
621 | for(vector<ComboAddress>::const_iterator server=iter->second.d_servers.begin(); server != iter->second.d_servers.end(); ++server) | |
8171ab83 | 622 | // nsset.insert((iter->second.d_rdForward ? "+" : "-") + server->toStringWithPort()); // add a '+' if the rd bit should be set |
e325f20c | 623 | // XXX this doesn't work, nsset can't contain a port number, or a plus etc! DNSNAME PAIN |
8171ab83 | 624 | abort(); |
2e5ae2b2 BH |
625 | } |
626 | ||
5605c067 BH |
627 | return authdomain; |
628 | } | |
629 | ||
e325f20c | 630 | vector<DNSRecord> bestns; |
891fbf88 | 631 | getBestNSFromCache(subdomain, qtype, bestns, flawedNSSet, depth, beenthere); |
75b49099 | 632 | |
e325f20c | 633 | for(auto k=bestns.cbegin() ; k != bestns.cend(); ++k) { |
634 | nsset.insert(std::dynamic_pointer_cast<NSRecordContent>(k->d_content)->getNS()); | |
635 | if(k==bestns.cbegin()) | |
636 | subdomain=k->d_name; | |
86c152f2 | 637 | } |
75b49099 | 638 | return subdomain; |
afbe2787 BH |
639 | } |
640 | ||
e325f20c | 641 | bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector<DNSRecord>& ret, int depth, int &res) |
afbe2787 | 642 | { |
ded77b10 | 643 | string prefix; |
77499b05 | 644 | if(doLog()) { |
710af846 | 645 | prefix=d_prefix; |
ded77b10 BH |
646 | prefix.append(depth, ' '); |
647 | } | |
36f5e3db | 648 | |
40de2910 | 649 | if((depth>9 && d_outqueries>10 && d_throttledqueries>5) || depth > 15) { |
c5c066bf | 650 | LOG(prefix<<qname.toString()<<": recursing (CNAME or other indirection) too deep, depth="<<depth<<endl); |
c6644fc5 BH |
651 | res=RCode::ServFail; |
652 | return true; | |
653 | } | |
3ddb9247 | 654 | |
c5c066bf | 655 | LOG(prefix<<qname.toString()<<": Looking for CNAME cache hit of '"<<(qname.toString()+"|CNAME")<<"'"<<endl); |
e325f20c | 656 | vector<DNSRecord> cset; |
1f77f479 | 657 | vector<std::shared_ptr<RRSIGRecordContent>> signatures; |
376effcf | 658 | if(t_RC->get(d_now.tv_sec, qname,QType(QType::CNAME), &cset, d_requestor, &signatures) > 0) { |
36c5ee42 | 659 | |
e325f20c | 660 | for(auto j=cset.cbegin() ; j != cset.cend() ; ++j) { |
661 | if(j->d_ttl>(unsigned int) d_now.tv_sec) { | |
662 | LOG(prefix<<qname.toString()<<": Found cache CNAME hit for '"<< (qname.toString()+"|CNAME") <<"' to '"<<j->d_content->getZoneRepresentation()<<"'"<<endl); | |
663 | DNSRecord dr=*j; | |
664 | dr.d_ttl-=d_now.tv_sec; | |
665 | ret.push_back(dr); | |
1f77f479 | 666 | |
667 | for(const auto& signature : signatures) { | |
668 | DNSRecord dr; | |
669 | dr.d_type=QType::RRSIG; | |
670 | dr.d_name=qname; | |
671 | dr.d_ttl=j->d_ttl - d_now.tv_sec; | |
672 | dr.d_content=signature; | |
e693ff5a | 673 | dr.d_place=DNSResourceRecord::ANSWER; |
1f77f479 | 674 | dr.d_class=1; |
675 | ret.push_back(dr); | |
676 | } | |
677 | ||
4957a608 BH |
678 | if(!(qtype==QType(QType::CNAME))) { // perhaps they really wanted a CNAME! |
679 | set<GetBestNSAnswer>beenthere; | |
e325f20c | 680 | res=doResolve(std::dynamic_pointer_cast<CNAMERecordContent>(j->d_content)->getTarget(), qtype, ret, depth+1, beenthere); |
4957a608 BH |
681 | } |
682 | else | |
683 | res=0; | |
684 | return true; | |
ac539791 BH |
685 | } |
686 | } | |
afbe2787 | 687 | } |
c5c066bf | 688 | LOG(prefix<<qname.toString()<<": No CNAME cache hit of '"<< (qname.toString()+"|CNAME") <<"' found"<<endl); |
75b49099 BH |
689 | return false; |
690 | } | |
691 | ||
8171ab83 | 692 | static const DNSName getLastLabel(const DNSName& qname) |
01402d56 | 693 | { |
8171ab83 | 694 | DNSName ret(qname); |
695 | ret.trimToLabels(1); | |
696 | return ret; | |
01402d56 | 697 | } |
bb4bdbaf | 698 | |
e325f20c | 699 | |
700 | bool SyncRes::doCacheCheck(const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, int depth, int &res) | |
75b49099 | 701 | { |
fd8bc993 | 702 | bool giveNegative=false; |
710af846 | 703 | |
be718669 | 704 | string prefix; |
77499b05 | 705 | if(doLog()) { |
ded77b10 BH |
706 | prefix=d_prefix; |
707 | prefix.append(depth, ' '); | |
708 | } | |
afbe2787 | 709 | |
c5c066bf | 710 | DNSName sqname(qname); |
288f4aa9 | 711 | QType sqt(qtype); |
092f210a | 712 | uint32_t sttl=0; |
0ad83dab | 713 | // cout<<"Lookup for '"<<qname.toString()<<"|"<<qtype.getName()<<"' -> "<<getLastLabel(qname)<<endl; |
3ddb9247 | 714 | |
01402d56 | 715 | pair<negcache_t::const_iterator, negcache_t::const_iterator> range; |
716 | QType qtnull(0); | |
710af846 PL |
717 | |
718 | if(s_rootNXTrust && | |
719 | (range.first=t_sstorage->negcache.find(tie(getLastLabel(qname), qtnull))) != t_sstorage->negcache.end() && | |
52683ca3 | 720 | range.first->d_qname.isRoot() && (uint32_t)d_now.tv_sec < range.first->d_ttd ) { |
01402d56 | 721 | sttl=range.first->d_ttd - d_now.tv_sec; |
3ddb9247 | 722 | |
c5c066bf | 723 | LOG(prefix<<qname.toString()<<": Entire name '"<<qname.toString()<<"', is negatively cached via '"<<range.first->d_name.toString()<<"' & '"<<range.first->d_qname.toString()<<"' for another "<<sttl<<" seconds"<<endl); |
3ddb9247 | 724 | res = RCode::NXDomain; |
01402d56 | 725 | sqname=range.first->d_qname; |
726 | sqt=QType::SOA; | |
727 | moveCacheItemToBack(t_sstorage->negcache, range.first); | |
710af846 | 728 | |
01402d56 | 729 | giveNegative=true; |
730 | } | |
731 | else { | |
732 | range=t_sstorage->negcache.equal_range(tie(qname)); | |
733 | negcache_t::iterator ni; | |
734 | for(ni=range.first; ni != range.second; ni++) { | |
735 | // we have something | |
736 | if(ni->d_qtype.getCode() == 0 || ni->d_qtype == qtype) { | |
737 | res=0; | |
738 | if((uint32_t)d_now.tv_sec < ni->d_ttd) { | |
739 | sttl=ni->d_ttd - d_now.tv_sec; | |
740 | if(ni->d_qtype.getCode()) { | |
e8b23f3b | 741 | LOG(prefix<<qname.toString()<<": "<<qtype.getName()<<" is negatively cached via '"<<ni->d_qname.toString()<<"' for another "<<sttl<<" seconds"<<endl); |
01402d56 | 742 | res = RCode::NoError; |
743 | } | |
744 | else { | |
e8b23f3b | 745 | LOG(prefix<<qname.toString()<<": Entire name '"<<qname.toString()<<"', is negatively cached via '"<<ni->d_qname.toString()<<"' for another "<<sttl<<" seconds"<<endl); |
3ddb9247 | 746 | res= RCode::NXDomain; |
01402d56 | 747 | } |
748 | giveNegative=true; | |
749 | sqname=ni->d_qname; | |
750 | sqt=QType::SOA; | |
751 | moveCacheItemToBack(t_sstorage->negcache, ni); | |
752 | break; | |
753 | } | |
754 | else { | |
0ad83dab | 755 | LOG(prefix<<qname.toString()<<": Entire name '"<<qname.toString()<<"' or type was negatively cached, but entry expired"<<endl); |
01402d56 | 756 | moveCacheItemToFront(t_sstorage->negcache, ni); |
757 | } | |
38e22b5a | 758 | } |
fd8bc993 BH |
759 | } |
760 | } | |
e325f20c | 761 | vector<DNSRecord> cset; |
75b49099 | 762 | bool found=false, expired=false; |
57769f13 | 763 | vector<std::shared_ptr<RRSIGRecordContent>> signatures; |
764 | uint32_t ttl=0; | |
376effcf | 765 | if(t_RC->get(d_now.tv_sec, sqname, sqt, &cset, d_requestor, d_doDNSSEC ? &signatures : 0) > 0) { |
c5c066bf | 766 | LOG(prefix<<sqname.toString()<<": Found cache hit for "<<sqt.getName()<<": "); |
e325f20c | 767 | for(auto j=cset.cbegin() ; j != cset.cend() ; ++j) { |
768 | LOG(j->d_content->getZoneRepresentation()); | |
769 | if(j->d_ttl>(unsigned int) d_now.tv_sec) { | |
770 | DNSRecord dr=*j; | |
771 | ttl = (dr.d_ttl-=d_now.tv_sec); | |
4957a608 | 772 | if(giveNegative) { |
e693ff5a | 773 | dr.d_place=DNSResourceRecord::AUTHORITY; |
e325f20c | 774 | dr.d_ttl=sttl; |
4957a608 | 775 | } |
e325f20c | 776 | ret.push_back(dr); |
777 | LOG("[ttl="<<dr.d_ttl<<"] "); | |
4957a608 | 778 | found=true; |
ac539791 | 779 | } |
75b49099 | 780 | else { |
77499b05 | 781 | LOG("[expired] "); |
4957a608 | 782 | expired=true; |
75b49099 | 783 | } |
afbe2787 | 784 | } |
57769f13 | 785 | |
786 | for(const auto& signature : signatures) { | |
e325f20c | 787 | DNSRecord dr; |
788 | dr.d_type=QType::RRSIG; | |
789 | dr.d_name=sqname; | |
790 | dr.d_ttl=ttl; | |
791 | dr.d_content=signature; | |
e693ff5a | 792 | dr.d_place=DNSResourceRecord::ANSWER; |
e325f20c | 793 | dr.d_class=1; |
794 | ret.push_back(dr); | |
57769f13 | 795 | } |
ac539791 | 796 | |
77499b05 | 797 | LOG(endl); |
f4df5e89 | 798 | if(found && !expired) { |
f15a5b2a | 799 | if(!giveNegative) |
4957a608 | 800 | res=0; |
75b49099 | 801 | return true; |
f4df5e89 | 802 | } |
75b49099 | 803 | else |
c5c066bf | 804 | LOG(prefix<<qname.toString()<<": cache had only stale entries"<<endl); |
afbe2787 | 805 | } |
f4df5e89 | 806 | |
75b49099 BH |
807 | return false; |
808 | } | |
afbe2787 | 809 | |
e8b23f3b | 810 | bool SyncRes::moreSpecificThan(const DNSName& a, const DNSName &b) |
75b49099 | 811 | { |
6a1010f7 | 812 | return (a.isPartOf(b) && a.countLabels() > b.countLabels()); |
afbe2787 BH |
813 | } |
814 | ||
d8d0bb8f | 815 | struct speedOrder |
eefd15f9 | 816 | { |
3ddb9247 PD |
817 | speedOrder(map<DNSName,double> &speeds) : d_speeds(speeds) {} |
818 | bool operator()(const DNSName &a, const DNSName &b) const | |
c3d9d009 BH |
819 | { |
820 | return d_speeds[a] < d_speeds[b]; | |
c3d9d009 | 821 | } |
3ddb9247 | 822 | map<DNSName, double>& d_speeds; |
c3d9d009 BH |
823 | }; |
824 | ||
e8b23f3b | 825 | inline vector<DNSName> SyncRes::shuffleInSpeedOrder(set<DNSName> &tnameservers, const string &prefix) |
afbe2787 | 826 | { |
e8b23f3b | 827 | vector<DNSName> rnameservers; |
5ea6f7de | 828 | rnameservers.reserve(tnameservers.size()); |
e8b23f3b PD |
829 | for(const auto& tns:tnameservers) { |
830 | rnameservers.push_back(tns); | |
21f0f88b | 831 | } |
e8b23f3b | 832 | map<DNSName, double> speeds; |
461df9d2 | 833 | |
e8b23f3b | 834 | for(const auto& val: rnameservers) { |
79b8cdcc | 835 | double speed; |
21f0f88b BH |
836 | speed=t_sstorage->nsSpeeds[val].get(&d_now); |
837 | speeds[val]=speed; | |
eefd15f9 | 838 | } |
51e2144e | 839 | random_shuffle(rnameservers.begin(),rnameservers.end(), dns_random); |
996c89cc BH |
840 | speedOrder so(speeds); |
841 | stable_sort(rnameservers.begin(),rnameservers.end(), so); | |
710af846 | 842 | |
77499b05 BH |
843 | if(doLog()) { |
844 | LOG(prefix<<"Nameservers: "); | |
e325f20c | 845 | for(vector<DNSName>::const_iterator i=rnameservers.begin();i!=rnameservers.end();++i) { |
3ddb9247 | 846 | if(i!=rnameservers.begin()) { |
77499b05 BH |
847 | LOG(", "); |
848 | if(!((i-rnameservers.begin())%3)) { | |
849 | LOG(endl<<prefix<<" "); | |
850 | } | |
d8d0bb8f | 851 | } |
34dcd30c | 852 | LOG((i->empty() ? string("<empty>") : i->toString())<<"(" << (boost::format("%0.2f") % (speeds[*i]/1000.0)).str() <<"ms)"); |
d8d0bb8f | 853 | } |
77499b05 | 854 | LOG(endl); |
d8d0bb8f | 855 | } |
728485ca | 856 | return rnameservers; |
afbe2787 BH |
857 | } |
858 | ||
bf7e4a70 BH |
859 | static bool magicAddrMatch(const QType& query, const QType& answer) |
860 | { | |
861 | if(query.getCode() != QType::ADDR) | |
862 | return false; | |
863 | return answer.getCode() == QType::A || answer.getCode() == QType::AAAA; | |
864 | } | |
7738a23f | 865 | |
ac539791 | 866 | /** returns -1 in case of no results, rcode otherwise */ |
3ddb9247 | 867 | int SyncRes::doResolveAt(set<DNSName> nameservers, DNSName auth, bool flawedNSSet, const DNSName &qname, const QType &qtype, |
e325f20c | 868 | vector<DNSRecord>&ret, |
232f0877 | 869 | int depth, set<GetBestNSAnswer>&beenthere) |
86c152f2 | 870 | { |
ded77b10 | 871 | string prefix; |
77499b05 | 872 | if(doLog()) { |
ded77b10 BH |
873 | prefix=d_prefix; |
874 | prefix.append(depth, ' '); | |
875 | } | |
3ddb9247 | 876 | |
c5c066bf | 877 | LOG(prefix<<qname.toString()<<": Cache consultations done, have "<<(unsigned int)nameservers.size()<<" NS to contact"<<endl); |
afbe2787 BH |
878 | |
879 | for(;;) { // we may get more specific nameservers | |
e8b23f3b | 880 | vector<DNSName > rnameservers = shuffleInSpeedOrder(nameservers, doLog() ? (prefix+qname.toString()+": ") : string() ); |
3ddb9247 PD |
881 | |
882 | for(vector<DNSName >::const_iterator tns=rnameservers.begin();;++tns) { | |
728485ca | 883 | if(tns==rnameservers.end()) { |
e8b23f3b | 884 | LOG(prefix<<qname.toString()<<": Failed to resolve via any of the "<<(unsigned int)rnameservers.size()<<" offered NS at level '"<<auth.toString()<<"'"<<endl); |
8171ab83 | 885 | if(auth!=DNSName() && flawedNSSet) { |
0ad83dab | 886 | LOG(prefix<<qname.toString()<<": Ageing nameservers for level '"<<auth.toString()<<"', next query might succeed"<<endl); |
e325f20c | 887 | |
49a699c4 | 888 | if(t_RC->doAgeCache(d_now.tv_sec, auth, QType::NS, 10)) |
4957a608 BH |
889 | g_stats.nsSetInvalidations++; |
890 | } | |
891 | return -1; | |
afbe2787 | 892 | } |
21f0f88b | 893 | // this line needs to identify the 'self-resolving' behaviour, but we get it wrong now |
e325f20c | 894 | if(qname == *tns && qtype.getCode()==QType::A && rnameservers.size() > (unsigned)(1+1*s_doIPv6)) { |
b9e96b63 | 895 | LOG(prefix<<qname.toString()<<": Not using NS to resolve itself! ("<<(1+tns-rnameservers.begin())<<"/"<<rnameservers.size()<<")"<<endl); |
4957a608 | 896 | continue; |
20177d1d | 897 | } |
5605c067 | 898 | |
996c89cc | 899 | typedef vector<ComboAddress> remoteIPs_t; |
5605c067 | 900 | remoteIPs_t remoteIPs; |
bfea0d0b | 901 | remoteIPs_t::const_iterator remoteIP; |
5c633640 | 902 | bool doTCP=false; |
5605c067 | 903 | int resolveret; |
1c21f389 | 904 | bool pierceDontQuery=false; |
c1d73d94 | 905 | bool sendRDQuery=false; |
376effcf | 906 | boost::optional<Netmask> ednsmask; |
263f6a5a | 907 | LWResult lwr; |
5ea6f7de | 908 | if(tns->empty()) { |
c5c066bf | 909 | LOG(prefix<<qname.toString()<<": Domain is out-of-band"<<endl); |
e325f20c | 910 | doOOBResolve(qname, qtype, lwr.d_records, depth, lwr.d_rcode); |
4957a608 BH |
911 | lwr.d_tcbit=false; |
912 | lwr.d_aabit=true; | |
5605c067 BH |
913 | } |
914 | else { | |
3ddb9247 | 915 | LOG(prefix<<qname.toString()<<": Trying to resolve NS '"<<tns->toString()<< "' ("<<1+tns-rnameservers.begin()<<"/"<<(unsigned int)rnameservers.size()<<")"<<endl); |
e325f20c | 916 | ; |
4957a608 | 917 | |
39ec5d29 | 918 | // XXX NEED TO HANDLE OTHER POLICY KINDS HERE! |
919 | if(g_dfe.getProcessingPolicy(*tns).d_kind != DNSFilterEngine::PolicyKind::NoAction) | |
644dd1da | 920 | throw ImmediateServFailException("Dropped because of policy"); |
921 | ||
5ea6f7de | 922 | if(!isCanonical(*tns)) { |
c5c066bf | 923 | LOG(prefix<<qname.toString()<<": Domain has hardcoded nameserver(s)"<<endl); |
4957a608 | 924 | |
3ddb9247 | 925 | string txtAddr = tns->toString(); |
5ea6f7de | 926 | if(!tns->empty()) { |
4957a608 BH |
927 | sendRDQuery = txtAddr[0] == '+'; |
928 | txtAddr=txtAddr.c_str()+1; | |
929 | } | |
930 | ComboAddress addr=parseIPAndPort(txtAddr, 53); | |
710af846 | 931 | |
4957a608 BH |
932 | remoteIPs.push_back(addr); |
933 | pierceDontQuery=true; | |
934 | } | |
935 | else { | |
76f190f2 | 936 | remoteIPs=getAddrs(*tns, depth+2, beenthere); |
4957a608 BH |
937 | pierceDontQuery=false; |
938 | } | |
939 | ||
940 | if(remoteIPs.empty()) { | |
3ddb9247 | 941 | LOG(prefix<<qname.toString()<<": Failed to get IP for NS "<<tns->toString()<<", trying next if available"<<endl); |
4957a608 BH |
942 | flawedNSSet=true; |
943 | continue; | |
944 | } | |
945 | else { | |
21f0f88b | 946 | |
e8b23f3b | 947 | LOG(prefix<<qname.toString()<<": Resolved '"+auth.toString()+"' NS "<<tns->toString()<<" to: "); |
4957a608 | 948 | for(remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) { |
77499b05 BH |
949 | if(remoteIP != remoteIPs.begin()) { |
950 | LOG(", "); | |
951 | } | |
952 | LOG(remoteIP->toString()); | |
4957a608 | 953 | } |
77499b05 | 954 | LOG(endl); |
4957a608 BH |
955 | |
956 | } | |
957 | ||
958 | for(remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) { | |
c5c066bf | 959 | LOG(prefix<<qname.toString()<<": Trying IP "<< remoteIP->toStringWithPort() <<", asking '"<<qname.toString()<<"|"<<qtype.getName()<<"'"<<endl); |
4957a608 | 960 | extern NetmaskGroup* g_dontQuery; |
710af846 | 961 | |
41ea0e50 | 962 | if(t_sstorage->throttle.shouldThrottle(d_now.tv_sec, boost::make_tuple(*remoteIP, "", 0))) { |
c5c066bf | 963 | LOG(prefix<<qname.toString()<<": server throttled "<<endl); |
628e2c7b PA |
964 | s_throttledqueries++; d_throttledqueries++; |
965 | continue; | |
966 | } | |
41ea0e50 | 967 | else if(t_sstorage->throttle.shouldThrottle(d_now.tv_sec, boost::make_tuple(*remoteIP, qname, qtype.getCode()))) { |
c5c066bf | 968 | LOG(prefix<<qname.toString()<<": query throttled "<<endl); |
4957a608 BH |
969 | s_throttledqueries++; d_throttledqueries++; |
970 | continue; | |
710af846 | 971 | } |
4957a608 | 972 | else if(!pierceDontQuery && g_dontQuery && g_dontQuery->match(&*remoteIP)) { |
c5c066bf | 973 | LOG(prefix<<qname.toString()<<": not sending query to " << remoteIP->toString() << ", blocked by 'dont-query' setting" << endl); |
66e0b6ea | 974 | s_dontqueries++; |
4957a608 BH |
975 | continue; |
976 | } | |
977 | else { | |
978 | s_outqueries++; d_outqueries++; | |
0ad83dab | 979 | if(d_outqueries + d_throttledqueries > s_maxqperq) throw ImmediateServFailException("more than "+lexical_cast<string>(s_maxqperq)+" (max-qperq) queries sent while resolving "+qname.toString()); |
4957a608 BH |
980 | TryTCP: |
981 | if(doTCP) { | |
c5c066bf | 982 | LOG(prefix<<qname.toString()<<": using TCP with "<< remoteIP->toStringWithPort() <<endl); |
4957a608 BH |
983 | s_tcpoutqueries++; d_tcpoutqueries++; |
984 | } | |
9de3e034 | 985 | |
3ddb9247 | 986 | if(s_maxtotusec && d_totUsec > s_maxtotusec) |
0ad83dab | 987 | throw ImmediateServFailException("Too much time waiting for "+qname.toString()+"|"+qtype.getName()+", timeouts: "+boost::lexical_cast<string>(d_timeouts) +", throttles: "+boost::lexical_cast<string>(d_throttledqueries) + ", queries: "+lexical_cast<string>(d_outqueries)+", "+lexical_cast<string>(d_totUsec/1000)+"msec"); |
9de3e034 | 988 | |
e325f20c | 989 | if(d_pdl && d_pdl->preoutquery(*remoteIP, d_requestor, qname, qtype, lwr.d_records, resolveret)) { |
c5c066bf | 990 | LOG(prefix<<qname.toString()<<": query handled by Lua"<<endl); |
3457a2a0 | 991 | } |
376effcf | 992 | else { |
993 | ednsmask=getEDNSSubnetMask(d_requestor, qname, *remoteIP); | |
710af846 | 994 | resolveret=asyncresolveWrapper(*remoteIP, qname, qtype.getCode(), |
376effcf | 995 | doTCP, sendRDQuery, &d_now, ednsmask, &lwr); // <- we go out on the wire! |
376effcf | 996 | } |
3457a2a0 | 997 | if(resolveret==-3) |
998 | throw ImmediateServFailException("Query killed by policy"); | |
999 | ||
9de3e034 | 1000 | d_totUsec += lwr.d_usec; |
7b75810e | 1001 | accountAuthLatency(lwr.d_usec, remoteIP->sin4.sin_family); |
30b13ef7 | 1002 | if(resolveret != 1) { |
4957a608 | 1003 | if(resolveret==0) { |
0ad83dab | 1004 | LOG(prefix<<qname.toString()<<": timeout resolving after "<<lwr.d_usec/1000.0<<"msec "<< (doTCP ? "over TCP" : "")<<endl); |
232f0877 CH |
1005 | d_timeouts++; |
1006 | s_outgoingtimeouts++; | |
7b75810e | 1007 | if(remoteIP->sin4.sin_family == AF_INET) |
1008 | s_outgoing4timeouts++; | |
1009 | else | |
1010 | s_outgoing6timeouts++; | |
4957a608 BH |
1011 | } |
1012 | else if(resolveret==-2) { | |
0ad83dab | 1013 | LOG(prefix<<qname.toString()<<": hit a local resource limit resolving"<< (doTCP ? " over TCP" : "")<<", probable error: "<<stringerror()<<endl); |
232f0877 | 1014 | g_stats.resourceLimits++; |
4957a608 BH |
1015 | } |
1016 | else { | |
232f0877 | 1017 | s_unreachables++; d_unreachables++; |
0ad83dab | 1018 | LOG(prefix<<qname.toString()<<": error resolving"<< (doTCP ? " over TCP" : "") <<", possible error: "<<strerror(errno)<< endl); |
4957a608 | 1019 | } |
710af846 | 1020 | |
4957a608 | 1021 | if(resolveret!=-2) { // don't account for resource limits, they are our own fault |
30b13ef7 | 1022 | t_sstorage->nsSpeeds[*tns].submit(*remoteIP, 1000000, &d_now); // 1 sec |
710af846 | 1023 | |
30b13ef7 | 1024 | // code below makes sure we don't filter COM or the root |
e8b23f3b | 1025 | if (s_serverdownmaxfails > 0 && (auth != DNSName(".")) && t_sstorage->fails.incr(*remoteIP) >= s_serverdownmaxfails) { |
66bf2e76 | 1026 | LOG(prefix<<qname.toString()<<": Max fails reached resolving on "<< remoteIP->toString() <<". Going full throttle for "<< s_serverdownthrottletime <<" seconds" <<endl); |
41ea0e50 | 1027 | t_sstorage->throttle.throttle(d_now.tv_sec, boost::make_tuple(*remoteIP, "", 0), s_serverdownthrottletime, 10000); // mark server as down |
628e2c7b | 1028 | } else if(resolveret==-1) |
41ea0e50 | 1029 | t_sstorage->throttle.throttle(d_now.tv_sec, boost::make_tuple(*remoteIP, qname, qtype.getCode()), 60, 100); // unreachable, 1 minute or 100 queries |
232f0877 | 1030 | else |
41ea0e50 | 1031 | t_sstorage->throttle.throttle(d_now.tv_sec, boost::make_tuple(*remoteIP, qname, qtype.getCode()), 10, 5); // timeout |
4957a608 BH |
1032 | } |
1033 | continue; | |
1034 | } | |
352b4183 | 1035 | |
76f190f2 | 1036 | // if(d_timeouts + 0.5*d_throttledqueries > 6.0 && d_timeouts > 2) throw ImmediateServFailException("Too much work resolving "+qname+"|"+qtype.getName()+", timeouts: "+boost::lexical_cast<string>(d_timeouts) +", throttles: "+boost::lexical_cast<string>(d_throttledqueries)); |
92011b8f | 1037 | |
352b4183 | 1038 | if(lwr.d_rcode==RCode::ServFail || lwr.d_rcode==RCode::Refused) { |
3ddb9247 | 1039 | LOG(prefix<<qname.toString()<<": "<<tns->toString()<<" returned a "<< (lwr.d_rcode==RCode::ServFail ? "ServFail" : "Refused") << ", trying sibling IP or NS"<<endl); |
41ea0e50 | 1040 | t_sstorage->throttle.throttle(d_now.tv_sec,boost::make_tuple(*remoteIP, qname, qtype.getCode()),60,3); // servfail or refused |
352b4183 PD |
1041 | continue; |
1042 | } | |
710af846 | 1043 | |
628e2c7b PA |
1044 | if(s_serverdownmaxfails > 0) |
1045 | t_sstorage->fails.clear(*remoteIP); | |
1046 | ||
4957a608 BH |
1047 | break; // this IP address worked! |
1048 | wasLame:; // well, it didn't | |
3ddb9247 | 1049 | LOG(prefix<<qname.toString()<<": status=NS "<<tns->toString()<<" ("<< remoteIP->toString() <<") is lame for '"<<auth.toString()<<"', trying sibling IP or NS"<<endl); |
41ea0e50 | 1050 | t_sstorage->throttle.throttle(d_now.tv_sec, boost::make_tuple(*remoteIP, qname, qtype.getCode()), 60, 100); // lame |
4957a608 BH |
1051 | } |
1052 | } | |
710af846 | 1053 | |
4957a608 | 1054 | if(remoteIP == remoteIPs.end()) // we tried all IP addresses, none worked |
710af846 PL |
1055 | continue; |
1056 | ||
4957a608 BH |
1057 | if(lwr.d_tcbit) { |
1058 | if(!doTCP) { | |
1059 | doTCP=true; | |
0ad83dab | 1060 | LOG(prefix<<qname.toString()<<": truncated bit set, retrying via TCP"<<endl); |
4957a608 BH |
1061 | goto TryTCP; |
1062 | } | |
0ad83dab | 1063 | LOG(prefix<<qname.toString()<<": truncated bit set, over TCP?"<<endl); |
4957a608 BH |
1064 | return RCode::ServFail; |
1065 | } | |
e325f20c | 1066 | LOG(prefix<<qname.toString()<<": Got "<<(unsigned int)lwr.d_records.size()<<" answers from "<<tns->toString()<<" ("<< remoteIP->toString() <<"), rcode="<<lwr.d_rcode<<" ("<<RCode::to_s(lwr.d_rcode)<<"), aa="<<lwr.d_aabit<<", in "<<lwr.d_usec/1000<<"ms"<<endl); |
4957a608 BH |
1067 | |
1068 | /* // for you IPv6 fanatics :-) | |
1069 | if(remoteIP->sin4.sin_family==AF_INET6) | |
1070 | lwr.d_usec/=3; | |
1071 | */ | |
232f0877 | 1072 | // cout<<"msec: "<<lwr.d_usec/1000.0<<", "<<g_avgLatency/1000.0<<'\n'; |
4957a608 | 1073 | |
49a699c4 | 1074 | t_sstorage->nsSpeeds[*tns].submit(*remoteIP, lwr.d_usec, &d_now); |
20177d1d | 1075 | } |
20177d1d | 1076 | |
aadceba8 | 1077 | if(s_minimumTTL) { |
48b560ea | 1078 | for(auto& rec : lwr.d_records) { |
e325f20c | 1079 | rec.d_ttl = max(rec.d_ttl, s_minimumTTL); |
aadceba8 | 1080 | } |
1081 | } | |
1082 | ||
57769f13 | 1083 | struct CachePair |
1084 | { | |
e325f20c | 1085 | vector<DNSRecord> records; |
57769f13 | 1086 | vector<shared_ptr<RRSIGRecordContent>> signatures; |
1087 | }; | |
6c674e9a | 1088 | struct CacheKey |
1089 | { | |
1090 | DNSName name; | |
1091 | uint16_t type; | |
1092 | DNSResourceRecord::Place place; | |
1093 | bool operator<(const CacheKey& rhs) const { | |
1094 | return tie(name, type) < tie(rhs.name, rhs.type); | |
1095 | } | |
1096 | }; | |
1097 | typedef map<CacheKey, CachePair> tcache_t; | |
1ac4e536 BH |
1098 | tcache_t tcache; |
1099 | ||
57769f13 | 1100 | if(d_doDNSSEC) { |
1101 | for(const auto& rec : lwr.d_records) { | |
e325f20c | 1102 | if(rec.d_type == QType::RRSIG) { |
1103 | auto rrsig = std::dynamic_pointer_cast<RRSIGRecordContent>(rec.d_content); | |
1104 | // cerr<<"Got an RRSIG for "<<DNSRecordContent::NumberToType(rrsig->d_type)<<" with name '"<<rec.d_name<<"'"<<endl; | |
6c674e9a | 1105 | tcache[{rec.d_name, rrsig->d_type, rec.d_place}].signatures.push_back(rrsig); |
57769f13 | 1106 | } |
1107 | } | |
1108 | } | |
1109 | ||
728485ca | 1110 | // reap all answers from this packet that are acceptable |
e325f20c | 1111 | for(auto& rec : lwr.d_records) { |
1112 | if(rec.d_type == QType::OPT) { | |
1f77f479 | 1113 | LOG(prefix<<qname.toString()<<": OPT answer '"<<rec.d_name<<"' from '"<<auth.toString()<<"' nameservers" <<endl); |
4957a608 BH |
1114 | continue; |
1115 | } | |
e325f20c | 1116 | LOG(prefix<<qname.toString()<<": accept answer '"<<rec.d_name<<"|"<<DNSRecordContent::NumberToType(rec.d_type)<<"|"<<rec.d_content->getZoneRepresentation()<<"' from '"<<auth.toString()<<"' nameservers? "<<(int)rec.d_place<<" "); |
1117 | if(rec.d_type == QType::ANY) { | |
77499b05 | 1118 | LOG("NO! - we don't accept 'ANY' data"<<endl); |
4957a608 BH |
1119 | continue; |
1120 | } | |
3ddb9247 | 1121 | |
58889ee6 PL |
1122 | // Check if we are authoritative for a zone in this answer |
1123 | if (!t_sstorage->domainmap->empty()) { | |
e325f20c | 1124 | DNSName tmp_qname(rec.d_name); |
58889ee6 PL |
1125 | auto auth_domain_iter=getBestAuthZone(&tmp_qname); |
1126 | if(auth_domain_iter!=t_sstorage->domainmap->end()) { | |
1127 | if (auth_domain_iter->first != auth) { | |
1128 | LOG("NO! - we are authoritative for the zone "<<auth_domain_iter->first.toString()<<endl); | |
1129 | continue; | |
1130 | } else { | |
1131 | // ugly... | |
1132 | LOG("YES! - This answer was retrieved from the local auth store"<<endl); | |
1133 | } | |
1134 | } | |
1135 | } | |
1136 | ||
1137 | ||
e325f20c | 1138 | if(rec.d_name.isPartOf(auth)) { |
756e82cf | 1139 | if(lwr.d_aabit && lwr.d_rcode==RCode::NoError && rec.d_place==DNSResourceRecord::ANSWER && g_delegationOnly.check(auth)) { |
77499b05 | 1140 | LOG("NO! Is from delegation-only zone"<<endl); |
4957a608 BH |
1141 | s_nodelegated++; |
1142 | return RCode::NXDomain; | |
1143 | } | |
e325f20c | 1144 | else if(rec.d_type == QType::RRSIG) { |
57769f13 | 1145 | LOG("RRSIG - separate"<<endl); |
1146 | } | |
4957a608 | 1147 | else { |
77499b05 | 1148 | LOG("YES!"<<endl); |
4957a608 | 1149 | |
e325f20c | 1150 | rec.d_ttl=min(s_maxcachettl, rec.d_ttl); |
710af846 | 1151 | |
e325f20c | 1152 | DNSRecord dr(rec); |
e693ff5a | 1153 | dr.d_place=DNSResourceRecord::ANSWER; |
4957a608 | 1154 | |
e325f20c | 1155 | dr.d_ttl += d_now.tv_sec; |
6c674e9a | 1156 | tcache[{rec.d_name,rec.d_type,rec.d_place}].records.push_back(dr); |
4957a608 | 1157 | } |
710af846 | 1158 | } |
4957a608 | 1159 | else |
77499b05 | 1160 | LOG("NO!"<<endl); |
86c152f2 | 1161 | } |
710af846 | 1162 | |
728485ca | 1163 | // supplant |
60b859b9 | 1164 | for(tcache_t::iterator i=tcache.begin();i!=tcache.end();++i) { |
57769f13 | 1165 | if(i->second.records.size() > 1) { // need to group the ttl to be the minimum of the RRSET (RFC 2181, 5.2) |
331c187c | 1166 | uint32_t lowestTTL=std::numeric_limits<uint32_t>::max(); |
57769f13 | 1167 | for(auto& record : i->second.records) |
e325f20c | 1168 | lowestTTL=min(lowestTTL, record.d_ttl); |
4957a608 | 1169 | |
57769f13 | 1170 | for(auto& record : i->second.records) |
e325f20c | 1171 | *const_cast<uint32_t*>(&record.d_ttl)=lowestTTL; // boom |
4957a608 BH |
1172 | } |
1173 | ||
e325f20c | 1174 | // cout<<"Have "<<i->second.records.size()<<" records and "<<i->second.signatures.size()<<" signatures for "<<i->first.first.toString(); |
1175 | // cout<<'|'<<DNSRecordContent::NumberToType(i->first.second.getCode())<<endl; | |
6c674e9a | 1176 | t_RC->replace(d_now.tv_sec, i->first.name, QType(i->first.type), i->second.records, i->second.signatures, lwr.d_aabit, i->first.place == DNSResourceRecord::ANSWER ? ednsmask : boost::optional<Netmask>()); |
3762e821 | 1177 | if(i->first.place == DNSResourceRecord::ANSWER && ednsmask) |
1178 | d_wasVariable=true; | |
288f4aa9 | 1179 | } |
3ddb9247 | 1180 | set<DNSName> nsset; |
0ad83dab | 1181 | LOG(prefix<<qname.toString()<<": determining status after receiving this packet"<<endl); |
728485ca | 1182 | |
57769f13 | 1183 | bool done=false, realreferral=false, negindic=false, sawDS=false; |
7e2e4419 | 1184 | DNSName newauth, soaname; |
1185 | DNSName newtarget; | |
57769f13 | 1186 | |
e325f20c | 1187 | for(auto& rec : lwr.d_records) { |
e693ff5a | 1188 | if(rec.d_place==DNSResourceRecord::AUTHORITY && rec.d_type==QType::SOA && |
c32f9f94 | 1189 | lwr.d_rcode==RCode::NXDomain && qname.isPartOf(rec.d_name) && rec.d_name.isPartOf(auth)) { |
34dcd30c | 1190 | LOG(prefix<<qname.toString()<<": got negative caching indication for name '"<<qname.toString()+"' (accept="<<rec.d_name.isPartOf(auth)<<"), newtarget='"<<(newtarget.empty()?string("<empty>"):newtarget.toString())<<"'"<<endl); |
3ddb9247 | 1191 | |
e325f20c | 1192 | rec.d_ttl = min(rec.d_ttl, s_maxnegttl); |
ae14c1f3 | 1193 | if(newtarget.empty()) // only add a SOA if we're not going anywhere after this |
e325f20c | 1194 | ret.push_back(rec); |
061d67cb | 1195 | if(!wasVariable()) { |
1196 | NegCacheEntry ne; | |
1197 | ||
1198 | ne.d_qname=rec.d_name; | |
1199 | ne.d_ttd=d_now.tv_sec + rec.d_ttl; | |
1200 | ne.d_name=qname; | |
1201 | ne.d_qtype=QType(0); // this encodes 'whole record' | |
1202 | ||
01402d56 | 1203 | replacing_insert(t_sstorage->negcache, ne); |
061d67cb | 1204 | if(s_rootNXTrust && auth.isRoot()) { |
1205 | ne.d_name = getLastLabel(ne.d_name); | |
1206 | replacing_insert(t_sstorage->negcache, ne); | |
1207 | } | |
01402d56 | 1208 | } |
710af846 | 1209 | |
4957a608 BH |
1210 | negindic=true; |
1211 | } | |
e693ff5a | 1212 | else if(rec.d_place==DNSResourceRecord::ANSWER && rec.d_name == qname && rec.d_type==QType::CNAME && (!(qtype==QType(QType::CNAME)))) { |
e325f20c | 1213 | ret.push_back(rec); |
d076a012 | 1214 | newtarget=std::dynamic_pointer_cast<CNAMERecordContent>(rec.d_content)->getTarget(); |
4957a608 | 1215 | } |
e693ff5a | 1216 | else if(d_doDNSSEC && (rec.d_type==QType::RRSIG || rec.d_type==QType::NSEC || rec.d_type==QType::NSEC3) && rec.d_place==DNSResourceRecord::ANSWER){ |
8ed7b45e | 1217 | if(rec.d_type != QType::RRSIG || rec.d_name == qname) |
1218 | ret.push_back(rec); // enjoy your DNSSEC | |
57769f13 | 1219 | } |
7c696097 | 1220 | // for ANY answers we *must* have an authoritative answer, unless we are forwarding recursively |
e693ff5a | 1221 | else if(rec.d_place==DNSResourceRecord::ANSWER && rec.d_name == qname && |
232f0877 | 1222 | ( |
e325f20c | 1223 | rec.d_type==qtype.getCode() || (lwr.d_aabit && (qtype==QType(QType::ANY) || magicAddrMatch(qtype, QType(rec.d_type)) ) ) || sendRDQuery |
710af846 PL |
1224 | ) |
1225 | ) | |
4957a608 | 1226 | { |
3ddb9247 | 1227 | |
e325f20c | 1228 | LOG(prefix<<qname.toString()<<": answer is in: resolved to '"<< rec.d_content->getZoneRepresentation()<<"|"<<DNSRecordContent::NumberToType(rec.d_type)<<"'"<<endl); |
4957a608 BH |
1229 | |
1230 | done=true; | |
e325f20c | 1231 | ret.push_back(rec); |
4957a608 | 1232 | } |
e693ff5a | 1233 | else if(rec.d_place==DNSResourceRecord::AUTHORITY && qname.isPartOf(rec.d_name) && rec.d_type==QType::NS) { |
e325f20c | 1234 | if(moreSpecificThan(rec.d_name,auth)) { |
1235 | newauth=rec.d_name; | |
1236 | LOG(prefix<<qname.toString()<<": got NS record '"<<rec.d_name.toString()<<"' -> '"<<rec.d_content->getZoneRepresentation()<<"'"<<endl); | |
4957a608 BH |
1237 | realreferral=true; |
1238 | } | |
e325f20c | 1239 | else { |
1240 | LOG(prefix<<qname.toString()<<": got upwards/level NS record '"<<rec.d_name.toString()<<"' -> '"<<rec.d_content->getZoneRepresentation()<<"', had '"<<auth.toString()<<"'"<<endl); | |
1241 | } | |
d076a012 | 1242 | nsset.insert(std::dynamic_pointer_cast<NSRecordContent>(rec.d_content)->getNS()); |
4957a608 | 1243 | } |
c32f9f94 | 1244 | else if(rec.d_place==DNSResourceRecord::AUTHORITY && qname.isPartOf(rec.d_name) && rec.d_type==QType::DS) { |
e325f20c | 1245 | LOG(prefix<<qname.toString()<<": got DS record '"<<rec.d_name.toString()<<"' -> '"<<rec.d_content->getZoneRepresentation()<<"'"<<endl); |
57769f13 | 1246 | sawDS=true; |
1247 | } | |
c32f9f94 | 1248 | else if(!done && rec.d_place==DNSResourceRecord::AUTHORITY && qname.isPartOf(rec.d_name) && rec.d_type==QType::SOA && |
4957a608 | 1249 | lwr.d_rcode==RCode::NoError) { |
e8b23f3b | 1250 | LOG(prefix<<qname.toString()<<": got negative caching indication for '"<< (qname.toString()+"|"+qtype.getName()+"'") <<endl); |
3ddb9247 | 1251 | |
bac969f0 | 1252 | if(!newtarget.empty()) { |
e8b23f3b | 1253 | LOG(prefix<<qname.toString()<<": Hang on! Got a redirect to '"<<newtarget.toString()<<"' already"<<endl); |
bac969f0 BH |
1254 | } |
1255 | else { | |
e325f20c | 1256 | rec.d_ttl = min(s_maxnegttl, rec.d_ttl); |
1257 | ret.push_back(rec); | |
061d67cb | 1258 | if(!wasVariable()) { |
1259 | NegCacheEntry ne; | |
1260 | ne.d_qname=rec.d_name; | |
1261 | ne.d_ttd=d_now.tv_sec + rec.d_ttl; | |
1262 | ne.d_name=qname; | |
1263 | ne.d_qtype=qtype; | |
1264 | if(qtype.getCode()) { // prevents us from blacking out a whole domain | |
1265 | replacing_insert(t_sstorage->negcache, ne); | |
1266 | } | |
1267 | } | |
bac969f0 | 1268 | negindic=true; |
4957a608 | 1269 | } |
4957a608 | 1270 | } |
86c152f2 | 1271 | } |
86c152f2 | 1272 | |
3ddb9247 | 1273 | if(done){ |
0ad83dab | 1274 | LOG(prefix<<qname.toString()<<": status=got results, this level of recursion done"<<endl); |
4957a608 | 1275 | return 0; |
ac539791 | 1276 | } |
c6644fc5 | 1277 | if(!newtarget.empty()) { |
e325f20c | 1278 | if(newtarget == qname) { |
0ad83dab | 1279 | LOG(prefix<<qname.toString()<<": status=got a CNAME referral to self, returning SERVFAIL"<<endl); |
4957a608 BH |
1280 | return RCode::ServFail; |
1281 | } | |
1282 | if(depth > 10) { | |
0ad83dab | 1283 | LOG(prefix<<qname.toString()<<": status=got a CNAME referral, but recursing too deep, returning SERVFAIL"<<endl); |
4957a608 BH |
1284 | return RCode::ServFail; |
1285 | } | |
e8b23f3b | 1286 | LOG(prefix<<qname.toString()<<": status=got a CNAME referral, starting over with "<<newtarget.toString()<<endl); |
4957a608 BH |
1287 | |
1288 | set<GetBestNSAnswer> beenthere2; | |
1289 | return doResolve(newtarget, qtype, ret, depth + 1, beenthere2); | |
c6644fc5 | 1290 | } |
331c187c | 1291 | if(lwr.d_rcode==RCode::NXDomain) { |
0ad83dab | 1292 | LOG(prefix<<qname.toString()<<": status=NXDOMAIN, we are done "<<(negindic ? "(have negative SOA)" : "")<<endl); |
331c187c BH |
1293 | return RCode::NXDomain; |
1294 | } | |
6ffd6bad | 1295 | if(nsset.empty() && !lwr.d_rcode && (negindic || lwr.d_aabit)) { |
0ad83dab | 1296 | LOG(prefix<<qname.toString()<<": status=noerror, other types may exist, but we are done "<<(negindic ? "(have negative SOA) " : "")<<(lwr.d_aabit ? "(have aa bit) " : "")<<endl); |
4957a608 | 1297 | return 0; |
caa6eefa | 1298 | } |
728485ca | 1299 | else if(realreferral) { |
0ad83dab | 1300 | LOG(prefix<<qname.toString()<<": status=did not resolve, got "<<(unsigned int)nsset.size()<<" NS, looping to them"<<endl); |
57769f13 | 1301 | if(sawDS) { |
1302 | t_sstorage->dnssecmap[newauth]=true; | |
e325f20c | 1303 | /* for(const auto& e : t_sstorage->dnssecmap) |
7e2e4419 | 1304 | cout<<e.first.toString()<<' '; |
e325f20c | 1305 | cout<<endl;*/ |
57769f13 | 1306 | } |
4957a608 BH |
1307 | auth=newauth; |
1308 | nameservers=nsset; | |
710af846 | 1309 | break; |
728485ca | 1310 | } |
5ea6f7de | 1311 | else if(isCanonical(*tns)) { // means: not OOB (I think) |
4957a608 | 1312 | goto wasLame; |
86c152f2 BH |
1313 | } |
1314 | } | |
86c152f2 | 1315 | } |
ac539791 | 1316 | return -1; |
86c152f2 BH |
1317 | } |
1318 | ||
bd53ea9d | 1319 | |
8ce79a22 | 1320 | // used by PowerDNSLua - note that this neglects to add the packet count & statistics back to pdns_ercursor.cc |
e325f20c | 1321 | int directResolve(const std::string& qname, const QType& qtype, int qclass, vector<DNSRecord>& ret) |
bd53ea9d PD |
1322 | { |
1323 | struct timeval now; | |
1324 | gettimeofday(&now, 0); | |
710af846 | 1325 | |
bd53ea9d | 1326 | SyncRes sr(now); |
8171ab83 | 1327 | int res = sr.beginResolve(DNSName(qname), QType(qtype), qclass, ret); // DNSName conversion XXX |
e325f20c | 1328 | |
bd53ea9d PD |
1329 | return res; |
1330 | } |