]>
Commit | Line | Data |
---|---|---|
3696224d BH |
1 | /* |
2 | PowerDNS Versatile Database Driven Nameserver | |
fc396d56 | 3 | Copyright (C) 2002-2012 PowerDNS.COM BV |
3696224d BH |
4 | |
5 | This program is free software; you can redistribute it and/or modify | |
68dae32c KM |
6 | it under the terms of the GNU General Public License version 2 as |
7 | published by the Free Software Foundation; | |
3696224d | 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 | ||
3696224d 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 | |
20 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
21 | */ | |
870a0fe4 AT |
22 | #ifdef HAVE_CONFIG_H |
23 | #include "config.h" | |
24 | #endif | |
3696224d BH |
25 | #include "packetcache.hh" |
26 | #include "utility.hh" | |
d7652f3a BH |
27 | #include "dnssecinfra.hh" |
28 | #include "dnsseckeeper.hh" | |
29 | #include "base32.hh" | |
3696224d BH |
30 | #include <errno.h> |
31 | #include "communicator.hh" | |
32 | #include <set> | |
33 | #include <boost/utility.hpp> | |
34 | #include "dnsbackend.hh" | |
35 | #include "ueberbackend.hh" | |
36 | #include "packethandler.hh" | |
37 | #include "resolver.hh" | |
38 | #include "logger.hh" | |
39 | #include "dns.hh" | |
40 | #include "arguments.hh" | |
3696224d | 41 | #include "packetcache.hh" |
fa8fd4d2 | 42 | |
29b92d6f | 43 | #include "base64.hh" |
3696224d | 44 | #include "inflighter.cc" |
5704e107 | 45 | #include "lua-auth.hh" |
3696224d | 46 | #include "namespaces.hh" |
7108e055 | 47 | #include "common_startup.hh" |
e23622a7 BH |
48 | #include <boost/scoped_ptr.hpp> |
49 | using boost::scoped_ptr; | |
3696224d | 50 | |
fab71044 | 51 | |
7abbc40f | 52 | void CommunicatorClass::addSuckRequest(const DNSName &domain, const string &master) |
3696224d BH |
53 | { |
54 | Lock l(&d_lock); | |
3696224d BH |
55 | SuckRequest sr; |
56 | sr.domain = domain; | |
57 | sr.master = master; | |
dbcb3066 | 58 | pair<UniQueue::iterator, bool> res; |
a71bee29 PD |
59 | |
60 | res=d_suckdomains.push_back(sr); | |
68dae32c | 61 | |
dbcb3066 | 62 | if(res.second) { |
7f3d870e | 63 | d_suck_sem.post(); |
dbcb3066 | 64 | } |
3696224d BH |
65 | } |
66 | ||
7abbc40f | 67 | void CommunicatorClass::suck(const DNSName &domain,const string &remote) |
3696224d | 68 | { |
f43c4448 | 69 | L<<Logger::Error<<"Initiating transfer of '"<<domain<<"' from remote '"<<remote<<"'"<<endl; |
295c4a00 | 70 | UeberBackend B; // fresh UeberBackend |
3696224d BH |
71 | |
72 | DomainInfo di; | |
73 | di.backend=0; | |
376ec278 | 74 | bool transaction=false; |
3696224d | 75 | try { |
295c4a00 | 76 | DNSSECKeeper dk (&B); // reuse our UeberBackend copy for DNSSECKeeper |
a1282cdd | 77 | |
295c4a00 | 78 | if(!B.getDomainInfo(domain, di) || !di.backend) { // di.backend and B are mostly identical |
f43c4448 | 79 | L<<Logger::Error<<"Can't determine backend for domain '"<<domain<<"'"<<endl; |
3696224d BH |
80 | return; |
81 | } | |
53568203 KM |
82 | uint32_t domain_id=di.id; |
83 | ||
3696224d | 84 | |
7abbc40f PD |
85 | DNSName tsigkeyname, tsigalgorithm; |
86 | string tsigsecret; | |
7597b3cf | 87 | if(dk.getTSIGForAccess(domain, remote, &tsigkeyname)) { |
29b92d6f | 88 | string tsigsecret64; |
295c4a00 | 89 | if(B.getTSIGKey(tsigkeyname, &tsigalgorithm, &tsigsecret64)) { |
a1467662 | 90 | B64Decode(tsigsecret64, tsigsecret); |
53568203 | 91 | } else { |
f43c4448 | 92 | L<<Logger::Error<<"TSIG key '"<<tsigkeyname<<"' for domain '"<<domain<<"' not found"<<endl; |
53568203 | 93 | return; |
a1467662 | 94 | } |
29b92d6f | 95 | } |
53568203 KM |
96 | |
97 | ||
5704e107 | 98 | scoped_ptr<AuthLua> pdl; |
e23622a7 | 99 | vector<string> scripts; |
295c4a00 | 100 | if(B.getDomainMetadata(domain, "LUA-AXFR-SCRIPT", scripts) && !scripts.empty()) { |
b2aefbf9 | 101 | try { |
5704e107 | 102 | pdl.reset(new AuthLua(scripts[0])); |
f43c4448 | 103 | L<<Logger::Info<<"Loaded Lua script '"<<scripts[0]<<"' to edit the incoming AXFR of '"<<domain<<"'"<<endl; |
b2aefbf9 BH |
104 | } |
105 | catch(std::exception& e) { | |
f43c4448 | 106 | L<<Logger::Error<<"Failed to load Lua editing script '"<<scripts[0]<<"' for incoming AXFR of '"<<domain<<"': "<<e.what()<<endl; |
b2aefbf9 BH |
107 | return; |
108 | } | |
e23622a7 | 109 | } |
53568203 | 110 | |
fc396d56 BH |
111 | vector<string> localaddr; |
112 | ComboAddress laddr; | |
295c4a00 | 113 | if(B.getDomainMetadata(domain, "AXFR-SOURCE", localaddr) && !localaddr.empty()) { |
fc396d56 BH |
114 | try { |
115 | laddr = ComboAddress(localaddr[0]); | |
f43c4448 | 116 | L<<Logger::Info<<"AXFR source for domain '"<<domain<<"' set to "<<localaddr[0]<<endl; |
fc396d56 BH |
117 | } |
118 | catch(std::exception& e) { | |
f43c4448 | 119 | L<<Logger::Error<<"Failed to load AXFR source '"<<localaddr[0]<<"' for incoming AXFR of '"<<domain<<"': "<<e.what()<<endl; |
fc396d56 BH |
120 | return; |
121 | } | |
122 | } else { | |
53568203 | 123 | laddr.sin4.sin_family = 0; |
8c949c52 | 124 | } |
fc396d56 | 125 | |
53568203 KM |
126 | bool hadDnssecZone = false; |
127 | bool hadPresigned = false; | |
128 | bool hadNSEC3 = false; | |
129 | NSEC3PARAMRecordContent ns3pr, hadNs3pr; | |
130 | bool isNarrow, hadNarrow=false; | |
131 | ||
132 | if(dk.isSecuredZone(domain)) { | |
133 | hadDnssecZone=true; | |
134 | hadPresigned=dk.isPresigned(domain); | |
135 | if (dk.getNSEC3PARAM(domain, &ns3pr, &isNarrow)) { | |
136 | hadNSEC3 = true; | |
137 | hadNs3pr = ns3pr; | |
138 | hadNarrow = isNarrow; | |
139 | } | |
140 | } | |
141 | ||
53568203 KM |
142 | bool isDnssecZone = false; |
143 | bool isPresigned = false; | |
144 | bool isNSEC3 = false; | |
145 | bool optOutFlag = false; | |
146 | ||
376ec278 | 147 | bool first=true; |
82fb5386 | 148 | bool firstNSEC3=true; |
53568203 | 149 | unsigned int soa_serial = 0; |
7abbc40f | 150 | set<DNSName> nsset, qnames, secured; |
53568203 KM |
151 | vector<DNSResourceRecord> rrs; |
152 | ||
153 | ComboAddress raddr(remote, 53); | |
7abbc40f | 154 | AXFRRetriever retriever(raddr, domain, tsigkeyname, tsigalgorithm, tsigsecret, (laddr.sin4.sin_family == 0) ? NULL : &laddr); |
53568203 | 155 | Resolver::res_t recs; |
0c01dd7c | 156 | while(retriever.getChunk(recs)) { |
3696224d | 157 | if(first) { |
f43c4448 | 158 | L<<Logger::Error<<"AXFR started for '"<<domain<<"'"<<endl; |
4957a608 | 159 | first=false; |
3696224d | 160 | } |
f9cf6d92 | 161 | |
3696224d | 162 | for(Resolver::res_t::iterator i=recs.begin();i!=recs.end();++i) { |
7597b3cf | 163 | if(i->qtype.getCode() == QType::OPT || i->qtype.getCode() == QType::TSIG) // ignore EDNS0 & TSIG |
25aeec36 | 164 | continue; |
f9cf6d92 | 165 | |
7abbc40f | 166 | if(!i->qname.isPartOf(domain)) { |
f43c4448 | 167 | L<<Logger::Error<<"Remote "<<remote<<" tried to sneak in out-of-zone data '"<<i->qname<<"'|"<<i->qtype.getName()<<" during AXFR of zone '"<<domain<<"', ignoring"<<endl; |
f9cf6d92 | 168 | continue; |
207f9ea1 | 169 | } |
f9cf6d92 | 170 | |
68fa524b KM |
171 | vector<DNSResourceRecord> out; |
172 | if(!pdl || !pdl->axfrfilter(raddr, domain, *i, out)) { | |
173 | out.push_back(*i); | |
a1282cdd | 174 | } |
b5baefaf | 175 | |
ef7cd021 | 176 | for(DNSResourceRecord& rr : out) { |
68fa524b KM |
177 | switch(rr.qtype.getCode()) { |
178 | case QType::NSEC3PARAM: { | |
179 | ns3pr = NSEC3PARAMRecordContent(rr.content); | |
180 | isDnssecZone = isNSEC3 = true; | |
181 | isNarrow = false; | |
182 | continue; | |
183 | } | |
184 | case QType::NSEC3: { | |
185 | NSEC3RecordContent ns3rc(rr.content); | |
186 | if (firstNSEC3) { | |
187 | isDnssecZone = isPresigned = true; | |
188 | firstNSEC3 = false; | |
189 | } else if (optOutFlag != (ns3rc.d_flags & 1)) | |
190 | throw PDNSException("Zones with a mixture of Opt-Out NSEC3 RRs and non-Opt-Out NSEC3 RRs are not supported."); | |
191 | optOutFlag = ns3rc.d_flags & 1; | |
e325f20c | 192 | if (ns3rc.d_set.count(QType::NS) && !(rr.qname==domain)) |
290a083d | 193 | secured.insert(DNSName(toLower(makeRelative(rr.qname.toString(), domain.toString())))); // XXX DNSName pain |
68fa524b KM |
194 | continue; |
195 | } | |
196 | case QType::NSEC: { | |
197 | isDnssecZone = isPresigned = true; | |
198 | continue; | |
199 | } | |
200 | case QType::SOA: { | |
201 | if(soa_serial != 0) | |
202 | continue; //skip the last SOA | |
203 | SOAData sd; | |
204 | fillSOAData(rr.content,sd); | |
205 | soa_serial = sd.serial; | |
206 | break; | |
207 | } | |
208 | case QType::NS: { | |
e325f20c | 209 | if(rr.qname!=domain) |
68fa524b KM |
210 | nsset.insert(rr.qname); |
211 | break; | |
212 | } | |
213 | default: | |
214 | break; | |
215 | } | |
f9cf6d92 | 216 | |
68fa524b | 217 | qnames.insert(rr.qname); |
f9cf6d92 | 218 | |
68fa524b KM |
219 | rr.domain_id=domain_id; |
220 | rrs.push_back(rr); | |
e23622a7 | 221 | } |
3696224d BH |
222 | } |
223 | } | |
68fa524b | 224 | |
53568203 KM |
225 | if(isNSEC3) { |
226 | ns3pr.d_flags = optOutFlag ? 1 : 0; | |
227 | } | |
ff473027 | 228 | |
53568203 KM |
229 | |
230 | if(!isPresigned) { | |
231 | DNSSECKeeper::keyset_t keys = dk.getKeys(domain); | |
232 | if(!keys.empty()) { | |
233 | isDnssecZone = true; | |
234 | isNSEC3 = hadNSEC3; | |
235 | ns3pr = hadNs3pr; | |
236 | optOutFlag = (hadNs3pr.d_flags & 1); | |
237 | isNarrow = hadNarrow; | |
238 | } | |
239 | } | |
240 | ||
241 | ||
242 | if(isDnssecZone) { | |
243 | if(!isNSEC3) | |
f9cf6d92 | 244 | L<<Logger::Info<<"Adding NSEC ordering information"<<endl; |
53568203 | 245 | else if(!isNarrow) |
f43c4448 | 246 | L<<Logger::Info<<"Adding NSEC3 hashed ordering information for '"<<domain<<"'"<<endl; |
f9cf6d92 KM |
247 | else |
248 | L<<Logger::Info<<"Erasing NSEC3 ordering since we are narrow, only setting 'auth' fields"<<endl; | |
249 | } | |
250 | ||
b5baefaf | 251 | |
53568203 | 252 | transaction=di.backend->startTransaction(domain, domain_id); |
f43c4448 | 253 | L<<Logger::Error<<"Transaction started for '"<<domain<<"'"<<endl; |
f9cf6d92 | 254 | |
7f2aa497 | 255 | // update the presigned flag and NSEC3PARAM |
53568203 KM |
256 | if (isDnssecZone) { |
257 | // update presigned if there was a change | |
258 | if (isPresigned && !hadPresigned) { | |
7f2aa497 KM |
259 | // zone is now presigned |
260 | dk.setPresigned(domain); | |
53568203 KM |
261 | } else if (hadPresigned && !isPresigned) { |
262 | // zone is no longer presigned | |
263 | dk.unsetPresigned(domain); | |
7f2aa497 | 264 | } |
53568203 KM |
265 | // update NSEC3PARAM |
266 | if (isNSEC3) { | |
267 | // zone is NSEC3, only update if there was a change | |
268 | if (!hadNSEC3 || (hadNarrow != isNarrow) || | |
269 | (ns3pr.d_algorithm != hadNs3pr.d_algorithm) || | |
270 | (ns3pr.d_flags != hadNs3pr.d_flags) || | |
271 | (ns3pr.d_iterations != hadNs3pr.d_iterations) || | |
272 | (ns3pr.d_salt != hadNs3pr.d_salt)) { | |
273 | dk.setNSEC3PARAM(domain, ns3pr, isNarrow); | |
7f2aa497 | 274 | } |
53568203 KM |
275 | } else if (hadNSEC3 ) { |
276 | // zone is no longer NSEC3 | |
277 | dk.unsetNSEC3PARAM(domain); | |
278 | } | |
279 | } else if (hadDnssecZone) { | |
280 | // zone is no longer signed | |
281 | if (hadPresigned) { | |
282 | // remove presigned | |
283 | dk.unsetPresigned(domain); | |
284 | } | |
285 | if (hadNSEC3) { | |
286 | // unset NSEC3PARAM | |
287 | dk.unsetNSEC3PARAM(domain); | |
7f2aa497 | 288 | } |
7f2aa497 KM |
289 | } |
290 | ||
f9cf6d92 | 291 | bool doent=true; |
b5baefaf | 292 | uint32_t maxent = ::arg().asNum("max-ent-entries"); |
7abbc40f PD |
293 | string ordername; |
294 | DNSName shorter; | |
295 | set<DNSName> rrterm; | |
296 | map<DNSName,bool> nonterm; | |
b5baefaf | 297 | |
53568203 | 298 | |
ef7cd021 | 299 | for(DNSResourceRecord& rr : rrs) { |
27045410 | 300 | |
53568203 KM |
301 | if(!isPresigned) { |
302 | if (rr.qtype.getCode() == QType::RRSIG) | |
303 | continue; | |
304 | if(isDnssecZone && rr.qtype.getCode() == QType::DNSKEY && !::arg().mustDo("direct-dnskey")) | |
305 | continue; | |
306 | } | |
307 | ||
f9cf6d92 KM |
308 | // Figure out auth and ents |
309 | rr.auth=true; | |
310 | shorter=rr.qname; | |
311 | rrterm.clear(); | |
312 | do { | |
313 | if(doent) { | |
e5c7239c | 314 | if (!qnames.count(shorter)) |
f9cf6d92 | 315 | rrterm.insert(shorter); |
d7652f3a | 316 | } |
e5c7239c | 317 | if(nsset.count(shorter) && rr.qtype.getCode() != QType::DS) |
f9cf6d92 | 318 | rr.auth=false; |
e5c7239c | 319 | |
e325f20c | 320 | if (shorter==domain) // stop at apex |
f9cf6d92 | 321 | break; |
7abbc40f | 322 | }while(shorter.chopOff()); |
b8adb30d | 323 | |
e5c7239c KM |
324 | // Insert ents |
325 | if(doent && !rrterm.empty()) { | |
326 | bool auth; | |
327 | if (!rr.auth && rr.qtype.getCode() == QType::NS) { | |
26752fd7 | 328 | if (isNSEC3) |
28e2e78e | 329 | ordername=toBase32Hex(hashQNameWithSalt(ns3pr, rr.qname)); |
290a083d | 330 | auth=(!isNSEC3 || !optOutFlag || secured.count(DNSName(ordername))); |
e5c7239c KM |
331 | } else |
332 | auth=rr.auth; | |
333 | ||
7abbc40f | 334 | for(const auto &nt: rrterm){ |
e5c7239c | 335 | if (!nonterm.count(nt)) |
7abbc40f | 336 | nonterm.insert(pair<DNSName, bool>(nt, auth)); |
e5c7239c KM |
337 | else if (auth) |
338 | nonterm[nt]=true; | |
339 | } | |
340 | ||
f9cf6d92 | 341 | if(nonterm.size() > maxent) { |
f43c4448 | 342 | L<<Logger::Error<<"AXFR zone "<<domain<<" has too many empty non terminals."<<endl; |
f9cf6d92 KM |
343 | nonterm.clear(); |
344 | doent=false; | |
27045410 | 345 | } |
d7652f3a | 346 | } |
f9cf6d92 KM |
347 | |
348 | // RRSIG is always auth, even inside a delegation | |
349 | if (rr.qtype.getCode() == QType::RRSIG) | |
350 | rr.auth=true; | |
351 | ||
352 | // Add ordername and insert record | |
53568203 KM |
353 | if (isDnssecZone && rr.qtype.getCode() != QType::RRSIG) { |
354 | if (isNSEC3) { | |
f9cf6d92 | 355 | // NSEC3 |
28e2e78e | 356 | ordername=toBase32Hex(hashQNameWithSalt(ns3pr, rr.qname)); |
290a083d | 357 | if(!isNarrow && (rr.auth || (rr.qtype.getCode() == QType::NS && (!optOutFlag || secured.count(DNSName(ordername)))))) { |
f9cf6d92 KM |
358 | di.backend->feedRecord(rr, &ordername); |
359 | } else | |
360 | di.backend->feedRecord(rr); | |
361 | } else { | |
362 | // NSEC | |
363 | if (rr.auth || rr.qtype.getCode() == QType::NS) { | |
7abbc40f | 364 | ordername=toLower(labelReverse(makeRelative(rr.qname.toString(), domain.toString()))); |
f9cf6d92 KM |
365 | di.backend->feedRecord(rr, &ordername); |
366 | } else | |
367 | di.backend->feedRecord(rr); | |
368 | } | |
369 | } else | |
370 | di.backend->feedRecord(rr); | |
d7652f3a | 371 | } |
b5baefaf | 372 | |
f9cf6d92 KM |
373 | // Insert empty non-terminals |
374 | if(doent && !nonterm.empty()) { | |
53568203 | 375 | if (isNSEC3) { |
28e2e78e | 376 | di.backend->feedEnts3(domain_id, domain, nonterm, ns3pr, isNarrow); |
f9cf6d92 KM |
377 | } else |
378 | di.backend->feedEnts(domain_id, nonterm); | |
b5baefaf PD |
379 | } |
380 | ||
14b7e03b | 381 | di.backend->commitTransaction(); |
376ec278 | 382 | transaction = false; |
14b7e03b | 383 | di.backend->setFresh(domain_id); |
7abbc40f | 384 | PC.purge(domain.toString()+"$"); |
14b7e03b | 385 | |
a1282cdd | 386 | |
207f9ea1 | 387 | L<<Logger::Error<<"AXFR done for '"<<domain<<"', zone committed with serial number "<<soa_serial<<endl; |
8de9c054 BH |
388 | if(::arg().mustDo("slave-renotify")) |
389 | notifyDomain(domain); | |
3696224d BH |
390 | } |
391 | catch(DBException &re) { | |
290a083d | 392 | L<<Logger::Error<<"Unable to feed record during incoming AXFR of '" << domain<<"': "<<re.reason<<endl; |
376ec278 | 393 | if(di.backend && transaction) { |
3696224d BH |
394 | L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl; |
395 | di.backend->abortTransaction(); | |
396 | } | |
397 | } | |
75ccb5b9 | 398 | catch(MOADNSException &re) { |
290a083d | 399 | L<<Logger::Error<<"Unable to parse record during incoming AXFR of '"<<domain<<"' (MOADNSException): "<<re.what()<<endl; |
376ec278 | 400 | if(di.backend && transaction) { |
2714db17 BH |
401 | L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl; |
402 | di.backend->abortTransaction(); | |
403 | } | |
404 | } | |
405 | catch(std::exception &re) { | |
290a083d | 406 | L<<Logger::Error<<"Unable to parse record during incoming AXFR of '"<<domain<<"' (std::exception): "<<re.what()<<endl; |
376ec278 | 407 | if(di.backend && transaction) { |
75ccb5b9 BH |
408 | L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl; |
409 | di.backend->abortTransaction(); | |
410 | } | |
411 | } | |
3696224d | 412 | catch(ResolverException &re) { |
290a083d | 413 | L<<Logger::Error<<"Unable to AXFR zone '"<<domain<<"' from remote '"<<remote<<"' (resolver): "<<re.reason<<endl; |
376ec278 | 414 | if(di.backend && transaction) { |
3696224d | 415 | L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl; |
7f450125 PD |
416 | di.backend->abortTransaction(); |
417 | } | |
418 | } | |
3f81d239 | 419 | catch(PDNSException &ae) { |
290a083d | 420 | L<<Logger::Error<<"Unable to AXFR zone '"<<domain<<"' from remote '"<<remote<<"' (PDNSException): "<<ae.reason<<endl; |
376ec278 | 421 | if(di.backend && transaction) { |
7f450125 | 422 | L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl; |
3696224d BH |
423 | di.backend->abortTransaction(); |
424 | } | |
425 | } | |
426 | } | |
7597b3cf | 427 | namespace { |
3696224d | 428 | struct QueryInfo |
7597b3cf BH |
429 | { |
430 | struct timeval query_ttd; | |
431 | uint16_t id; | |
432 | }; | |
433 | ||
434 | struct DomainNotificationInfo | |
435 | { | |
436 | DomainInfo di; | |
437 | bool dnssecOk; | |
95302209 | 438 | ComboAddress localaddr; |
561434a6 PD |
439 | DNSName tsigkeyname, tsigalgname; |
440 | string tsigsecret; | |
7597b3cf BH |
441 | }; |
442 | } | |
443 | ||
3696224d BH |
444 | |
445 | struct SlaveSenderReceiver | |
446 | { | |
561434a6 | 447 | typedef pair<DNSName, uint16_t> Identifier; |
53568203 | 448 | |
54b2edb4 BH |
449 | struct Answer { |
450 | uint32_t theirSerial; | |
451 | uint32_t theirInception; | |
452 | uint32_t theirExpire; | |
453 | }; | |
53568203 | 454 | |
54b2edb4 | 455 | map<uint32_t, Answer> d_freshness; |
53568203 | 456 | |
3696224d BH |
457 | SlaveSenderReceiver() |
458 | { | |
3696224d | 459 | } |
53568203 | 460 | |
c0a5fc34 | 461 | void deliverTimeout(const Identifier& i) |
0c01dd7c BH |
462 | { |
463 | } | |
53568203 | 464 | |
7597b3cf | 465 | Identifier send(DomainNotificationInfo& dni) |
3696224d | 466 | { |
7597b3cf | 467 | random_shuffle(dni.di.masters.begin(), dni.di.masters.end()); |
0c01dd7c | 468 | try { |
7597b3cf | 469 | ComboAddress remote(*dni.di.masters.begin()); |
95302209 | 470 | if (dni.localaddr.sin4.sin_family == 0) { |
53568203 KM |
471 | return make_pair(dni.di.zone, |
472 | d_resolver.sendResolve(ComboAddress(*dni.di.masters.begin(), 53), | |
7abbc40f | 473 | dni.di.zone, |
53568203 | 474 | QType::SOA, |
95302209 AT |
475 | dni.dnssecOk, dni.tsigkeyname, dni.tsigalgname, dni.tsigsecret) |
476 | ); | |
477 | } else { | |
478 | return make_pair(dni.di.zone, | |
479 | d_resolver.sendResolve(ComboAddress(*dni.di.masters.begin(), 53), dni.localaddr, | |
7abbc40f | 480 | dni.di.zone, |
95302209 AT |
481 | QType::SOA, |
482 | dni.dnssecOk, dni.tsigkeyname, dni.tsigalgname, dni.tsigsecret) | |
483 | ); | |
484 | } | |
0c01dd7c | 485 | } |
3f81d239 | 486 | catch(PDNSException& e) { |
7abbc40f | 487 | throw runtime_error("While attempting to query freshness of '"+dni.di.zone.toString()+"': "+e.reason); |
0c01dd7c | 488 | } |
3696224d | 489 | } |
68dae32c | 490 | |
3696224d BH |
491 | bool receive(Identifier& id, Answer& a) |
492 | { | |
54b2edb4 | 493 | if(d_resolver.tryGetSOASerial(&id.first, &a.theirSerial, &a.theirInception, &a.theirExpire, &id.second)) { |
3696224d BH |
494 | return 1; |
495 | } | |
496 | return 0; | |
497 | } | |
68dae32c | 498 | |
7597b3cf | 499 | void deliverAnswer(DomainNotificationInfo& dni, const Answer& a, unsigned int usec) |
3696224d | 500 | { |
7597b3cf | 501 | d_freshness[dni.di.id]=a; |
3696224d | 502 | } |
68dae32c | 503 | |
3696224d | 504 | Resolver d_resolver; |
3696224d BH |
505 | }; |
506 | ||
7f3d870e BH |
507 | void CommunicatorClass::addSlaveCheckRequest(const DomainInfo& di, const ComboAddress& remote) |
508 | { | |
509 | Lock l(&d_lock); | |
90c9a70e BH |
510 | DomainInfo ours = di; |
511 | ours.backend = 0; | |
512 | d_tocheck.insert(ours); | |
7f3d870e BH |
513 | d_any_sem.post(); // kick the loop! |
514 | } | |
515 | ||
7108e055 PD |
516 | void CommunicatorClass::addTrySuperMasterRequest(DNSPacket *p) |
517 | { | |
518 | Lock l(&d_lock); | |
519 | DNSPacket ours = *p; | |
520 | d_potentialsupermasters.push_back(ours); | |
521 | d_any_sem.post(); // kick the loop! | |
522 | } | |
523 | ||
3696224d BH |
524 | void CommunicatorClass::slaveRefresh(PacketHandler *P) |
525 | { | |
56a4f068 AT |
526 | // not unless we are slave |
527 | if (!::arg().mustDo("slave")) return; | |
528 | ||
3971cf53 | 529 | UeberBackend *B=P->getBackend(); |
16e654fa | 530 | vector<DomainInfo> rdomains; |
a71bee29 | 531 | vector<DomainNotificationInfo> sdomains; // the bool is for 'presigned' |
7108e055 | 532 | vector<DNSPacket> trysuperdomains; |
68dae32c | 533 | |
7f3d870e BH |
534 | { |
535 | Lock l(&d_lock); | |
536 | rdomains.insert(rdomains.end(), d_tocheck.begin(), d_tocheck.end()); | |
537 | d_tocheck.clear(); | |
7108e055 PD |
538 | trysuperdomains.insert(trysuperdomains.end(), d_potentialsupermasters.begin(), d_potentialsupermasters.end()); |
539 | d_potentialsupermasters.clear(); | |
7f3d870e | 540 | } |
68dae32c | 541 | |
ef7cd021 | 542 | for(DNSPacket& dp : trysuperdomains) { |
7108e055 PD |
543 | int res; |
544 | res=P->trySuperMasterSynchronous(&dp); | |
545 | if(res>=0) { | |
546 | DNSPacket *r=dp.replyPacket(); | |
547 | r->setRcode(res); | |
548 | r->setOpcode(Opcode::Notify); | |
549 | N->send(r); | |
550 | delete r; | |
551 | } | |
552 | } | |
553 | ||
7f3d870e BH |
554 | if(rdomains.empty()) // if we have priority domains, check them first |
555 | B->getUnfreshSlaveInfos(&rdomains); | |
68dae32c | 556 | |
936eb34a | 557 | DNSSECKeeper dk(B); // NOW HEAR THIS! This DK uses our B backend, so no interleaved access! |
dbcb3066 BH |
558 | { |
559 | Lock l(&d_lock); | |
dbcb3066 BH |
560 | domains_by_name_t& nameindex=boost::multi_index::get<IDTag>(d_suckdomains); |
561 | ||
ef7cd021 | 562 | for(DomainInfo& di : rdomains) { |
95302209 | 563 | std::vector<std::string> localaddr; |
dbcb3066 BH |
564 | SuckRequest sr; |
565 | sr.domain=di.zone; | |
566 | if(di.masters.empty()) // slave domains w/o masters are ignored | |
567 | continue; | |
568 | // remove unfresh domains already queued for AXFR, no sense polling them again | |
569 | sr.master=*di.masters.begin(); | |
a71bee29 | 570 | if(nameindex.count(sr)) { |
dbcb3066 | 571 | continue; |
a71bee29 | 572 | } |
7597b3cf BH |
573 | DomainNotificationInfo dni; |
574 | dni.di=di; | |
575 | dni.dnssecOk = dk.isPresigned(di.zone); | |
68dae32c | 576 | |
7597b3cf BH |
577 | if(dk.getTSIGForAccess(di.zone, sr.master, &dni.tsigkeyname)) { |
578 | string secret64; | |
579 | B->getTSIGKey(dni.tsigkeyname, &dni.tsigalgname, &secret64); | |
580 | B64Decode(secret64, dni.tsigsecret); | |
581 | } | |
95302209 AT |
582 | |
583 | localaddr.clear(); | |
584 | // check for AXFR-SOURCE | |
585 | if(B->getDomainMetadata(di.zone, "AXFR-SOURCE", localaddr) && !localaddr.empty()) { | |
586 | try { | |
587 | dni.localaddr = ComboAddress(localaddr[0]); | |
588 | L<<Logger::Info<<"Freshness check source (AXFR-SOURCE) for domain '"<<di.zone<<"' set to "<<localaddr[0]<<endl; | |
589 | } | |
590 | catch(std::exception& e) { | |
591 | L<<Logger::Error<<"Failed to load freshness check source '"<<localaddr[0]<<"' for '"<<di.zone<<"': "<<e.what()<<endl; | |
592 | return; | |
593 | } | |
594 | } else { | |
595 | dni.localaddr.sin4.sin_family = 0; | |
596 | } | |
597 | ||
7597b3cf | 598 | sdomains.push_back(dni); |
dbcb3066 | 599 | } |
dbcb3066 | 600 | } |
68dae32c | 601 | |
3696224d BH |
602 | if(sdomains.empty()) |
603 | { | |
dbcb3066 BH |
604 | if(d_slaveschanged) { |
605 | Lock l(&d_lock); | |
606 | L<<Logger::Warning<<"No new unfresh slave domains, "<<d_suckdomains.size()<<" queued for AXFR already"<<endl; | |
607 | } | |
608 | d_slaveschanged = !rdomains.empty(); | |
3696224d BH |
609 | return; |
610 | } | |
dbcb3066 BH |
611 | else { |
612 | Lock l(&d_lock); | |
3696224d BH |
613 | L<<Logger::Warning<<sdomains.size()<<" slave domain"<<(sdomains.size()>1 ? "s" : "")<<" need"<< |
614 | (sdomains.size()>1 ? "" : "s")<< | |
dbcb3066 BH |
615 | " checking, "<<d_suckdomains.size()<<" queued for AXFR"<<endl; |
616 | } | |
68dae32c | 617 | |
3696224d | 618 | SlaveSenderReceiver ssr; |
68dae32c | 619 | |
7597b3cf | 620 | Inflighter<vector<DomainNotificationInfo>, SlaveSenderReceiver> ifl(sdomains, ssr); |
68dae32c | 621 | |
3696224d BH |
622 | ifl.d_maxInFlight = 200; |
623 | ||
624 | for(;;) { | |
625 | try { | |
626 | ifl.run(); | |
627 | break; | |
628 | } | |
dbcb3066 | 629 | catch(std::exception& e) { |
3696224d BH |
630 | L<<Logger::Error<<"While checking domain freshness: " << e.what()<<endl; |
631 | } | |
68dae32c | 632 | catch(PDNSException &re) { |
3696224d BH |
633 | L<<Logger::Error<<"While checking domain freshness: " << re.reason<<endl; |
634 | } | |
635 | } | |
0c01dd7c | 636 | L<<Logger::Warning<<"Received serial number updates for "<<ssr.d_freshness.size()<<" zones, had "<<ifl.getTimeouts()<<" timeouts"<<endl; |
16e654fa | 637 | |
7597b3cf | 638 | typedef DomainNotificationInfo val_t; |
ef7cd021 | 639 | for(val_t& val : sdomains) { |
7597b3cf | 640 | DomainInfo& di(val.di); |
f9fa0e2d PD |
641 | // might've come from the packethandler |
642 | if(!di.backend && !B->getDomainInfo(di.zone, di)) { | |
f43c4448 | 643 | L<<Logger::Warning<<"Ignore domain "<< di.zone<<" since it has been removed from our backend"<<endl; |
232f0877 | 644 | continue; |
f9fa0e2d | 645 | } |
68dae32c KM |
646 | |
647 | if(!ssr.d_freshness.count(di.id)) | |
3696224d | 648 | continue; |
54b2edb4 | 649 | uint32_t theirserial = ssr.d_freshness[di.id].theirSerial, ourserial = di.serial; |
68dae32c | 650 | |
da1a6059 | 651 | if(rfc1982LessThan(theirserial, ourserial) && ourserial != 0) { |
8e90e428 | 652 | L<<Logger::Error<<"Domain '"<<di.zone<<"' more recent than master, our serial " << ourserial << " > their serial "<< theirserial << endl; |
3696224d BH |
653 | di.backend->setFresh(di.id); |
654 | } | |
655 | else if(theirserial == ourserial) { | |
54b2edb4 | 656 | if(!dk.isPresigned(di.zone)) { |
1d3fae1b | 657 | L<<Logger::Info<<"Domain '"<< di.zone<<"' is fresh (not presigned, no RRSIG check)"<<endl; |
54b2edb4 BH |
658 | di.backend->setFresh(di.id); |
659 | } | |
660 | else { | |
936eb34a | 661 | B->lookup(QType(QType::RRSIG), di.zone); // can't use DK before we are done with this lookup! |
54b2edb4 BH |
662 | DNSResourceRecord rr; |
663 | uint32_t maxExpire=0, maxInception=0; | |
664 | while(B->get(rr)) { | |
665 | RRSIGRecordContent rrc(rr.content); | |
666 | if(rrc.d_type == QType::SOA) { | |
667 | maxInception = std::max(maxInception, rrc.d_siginception); | |
668 | maxExpire = std::max(maxExpire, rrc.d_sigexpire); | |
669 | } | |
670 | } | |
671 | if(maxInception == ssr.d_freshness[di.id].theirInception && maxExpire == ssr.d_freshness[di.id].theirExpire) { | |
f43c4448 | 672 | L<<Logger::Info<<"Domain '"<< di.zone<<"' is fresh and apex RRSIGs match"<<endl; |
54b2edb4 BH |
673 | di.backend->setFresh(di.id); |
674 | } | |
675 | else { | |
8e90e428 | 676 | L<<Logger::Warning<<"Domain '"<< di.zone<<"' is fresh, but RRSIGS differ, so DNSSEC stale"<<endl; |
54b2edb4 BH |
677 | addSuckRequest(di.zone, *di.masters.begin()); |
678 | } | |
679 | } | |
3696224d BH |
680 | } |
681 | else { | |
8e90e428 | 682 | L<<Logger::Warning<<"Domain '"<< di.zone<<"' is stale, master serial "<<theirserial<<", our serial "<< ourserial <<endl; |
3696224d | 683 | addSuckRequest(di.zone, *di.masters.begin()); |
3696224d BH |
684 | } |
685 | } | |
68dae32c | 686 | } |
3696224d | 687 | |
bd53ea9d PD |
688 | // stub for PowerDNSLua linking |
689 | int directResolve(const std::string& qname, const QType& qtype, int qclass, vector<DNSResourceRecord>& ret) | |
690 | { | |
691 | return -1; | |
692 | } | |
693 | ||
694 |