]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/rfc2136handler.cc
Remove non-needed check on rrset size
[thirdparty/pdns.git] / pdns / rfc2136handler.cc
CommitLineData
f1b59a55
RA
1#include "packethandler.hh"
2#include "qtype.hh"
3#include "dnspacket.hh"
4#include "packetcache.hh"
5#include "dnsseckeeper.hh"
6#include "base64.hh"
7#include "base32.hh"
8#include "misc.hh"
9#include "arguments.hh"
10
11extern PacketCache PC;
12
13// Implement section 3.2.1 and 3.2.2 of RFC2136
14int PacketHandler::checkUpdatePrerequisites(const DNSRecord *rr, DomainInfo *di) {
15 if (rr->d_ttl != 0)
16 return RCode::FormErr;
17
18 // 3.2.1 and 3.2.2 check content length.
19 if ( (rr->d_class == QClass::NONE || rr->d_class == QClass::ANY) && rr->d_clen != 0)
20 return RCode::FormErr;
21
6a323f63 22 string rrLabel = stripDot(rr->d_label);
f1b59a55
RA
23
24 bool foundRecord=false;
25 DNSResourceRecord rec;
6a323f63 26 di->backend->lookup(QType(QType::ANY), rrLabel);
f1b59a55
RA
27 while(di->backend->get(rec)) {
28 if (!rec.qtype.getCode())
29 continue;
30 if ((rr->d_type != QType::ANY && rec.qtype == rr->d_type) || rr->d_type == QType::ANY)
31 foundRecord=true;
32 }
33
34 // Section 3.2.1
35 if (rr->d_class == QClass::ANY && !foundRecord) {
36 if (rr->d_type == QType::ANY)
37 return RCode::NXDomain;
38 if (rr->d_type != QType::ANY)
39 return RCode::NXRRSet;
40 }
41
42 // Section 3.2.2
43 if (rr->d_class == QClass::NONE && foundRecord) {
44 if (rr->d_type == QType::ANY)
45 return RCode::YXDomain;
46 if (rr->d_type != QType::ANY)
47 return RCode::YXRRSet;
48 }
49
50 return RCode::NoError;
51}
52
53
54// Method implements section 3.4.1 of RFC2136
55int PacketHandler::checkUpdatePrescan(const DNSRecord *rr) {
56 // The RFC stats that d_class != ZCLASS, but we only support the IN class.
57 if (rr->d_class != QClass::IN && rr->d_class != QClass::NONE && rr->d_class != QClass::ANY)
58 return RCode::FormErr;
59
60 QType qtype = QType(rr->d_type);
61
62 if (! qtype.isSupportedType())
63 return RCode::FormErr;
64
65 if ((rr->d_class == QClass::NONE || rr->d_class == QClass::ANY) && rr->d_ttl != 0)
66 return RCode::FormErr;
67
68 if (rr->d_class == QClass::ANY && rr->d_clen != 0)
69 return RCode::FormErr;
70
71 if (qtype.isMetadataType())
72 return RCode::FormErr;
73
74 if (rr->d_class != QClass::ANY && qtype.getCode() == QType::ANY)
75 return RCode::FormErr;
76
77 return RCode::NoError;
78}
79
6a323f63 80
f1b59a55
RA
81// Implements section 3.4.2 of RFC2136
82uint16_t PacketHandler::performUpdate(const string &msgPrefix, const DNSRecord *rr, DomainInfo *di, bool narrow, bool haveNSEC3, const NSEC3PARAMRecordContent *ns3pr, bool *updatedSerial) {
6a323f63
RA
83 uint16_t addedRecords = 0, updatedRecords = 0;
84 DNSResourceRecord rec;
85 vector<DNSResourceRecord> rrset, recordsToDelete;
86 set<string> delnonterm, insnonterm; // used to (at the end) fix ENT records.
87
88 string rrLabel = stripDot(rr->d_label);
89 QType rrType = QType(rr->d_type);
f1b59a55 90
6a323f63
RA
91 if (rr->d_class == QClass::IN) { // 3.4.2.2 QClass::IN means insert or update
92 DLOG(L<<msgPrefix<<"Add/Update record (QClass == IN) "<<rrLabel<<"|"<<rrType.getName()<<endl);
f1b59a55 93
6a323f63
RA
94 bool foundRecord = false;
95 di->backend->lookup(rrType, rrLabel);
f1b59a55 96 while (di->backend->get(rec)) {
6a323f63 97 rrset.push_back(rec);
f1b59a55 98 foundRecord = true;
6a323f63
RA
99 }
100
101
102 if (foundRecord) {
103
104 // SOA updates require the serial to be updated.
105 if (rrType == QType::SOA) {
f1b59a55 106 SOAData sdOld, sdUpdate;
6a323f63
RA
107 DNSResourceRecord *oldRec = &rrset.front();
108 fillSOAData(oldRec->content, sdOld);
109 oldRec->setContent(rr->d_content->getZoneRepresentation());
110 fillSOAData(oldRec->content, sdUpdate);
f1b59a55 111 if (rfc1982LessThan(sdOld.serial, sdUpdate.serial)) {
6a323f63
RA
112 updatedRecords++;
113 di->backend->replaceRRSet(di->id, oldRec->qname, oldRec->qtype, rrset);
f1b59a55
RA
114 *updatedSerial = true;
115 }
116 else
117 L<<Logger::Notice<<msgPrefix<<"Provided serial ("<<sdUpdate.serial<<") is older than the current serial ("<<sdOld.serial<<"), ignoring SOA update."<<endl;
6a323f63
RA
118
119 // It's not possible to have multiple CNAME's with the same NAME. So we always update.
120 } else if (rrType == QType::CNAME) {
121 for (vector<DNSResourceRecord>::iterator i = rrset.begin(); i != rrset.end(); i++) {
122 i->ttl = rr->d_ttl;
123 i->setContent(rr->d_content->getZoneRepresentation());
124 updatedRecords++;
f1b59a55 125 }
6a323f63
RA
126 di->backend->replaceRRSet(di->id, rrLabel, rrType, rrset);
127
128 // In any other case, we must check if the TYPE and RDATA match to provide an update (which effectily means a update of TTL)
129 } else {
130 foundRecord = false;
131 for (vector<DNSResourceRecord>::iterator i = rrset.begin(); i != rrset.end(); i++) {
132 string content = rr->d_content->getZoneRepresentation();
133 if (rrType == i->qtype.getCode() && i->getZoneRepresentation() == content) {
134 foundRecord = true;
135 i->ttl = rr->d_ttl;
136 updatedRecords++;
137 }
138 }
139 if (foundRecord)
140 di->backend->replaceRRSet(di->id, rrLabel, rrType, rrset);
f1b59a55
RA
141 }
142 }
f1b59a55 143
6a323f63 144 // If we haven't found a record that matches, we must add it.
f1b59a55 145 if (! foundRecord) {
6a323f63
RA
146 L<<Logger::Notice<<msgPrefix<<"Adding record "<<rrLabel<<"|"<<rrType.getName()<<endl;
147 delnonterm.insert(rrLabel); // always remove any ENT's in the place where we're going to add a record.
f1b59a55
RA
148 DNSResourceRecord newRec(*rr);
149 newRec.domain_id = di->id;
f1b59a55 150 di->backend->feedRecord(newRec);
6a323f63
RA
151 addedRecords++;
152
153
154 // because we added a record, we need to fix DNSSEC data.
155 string shorter(rrLabel);
f1b59a55
RA
156 bool auth=true;
157
158 set<string> insnonterm;
6a323f63 159 if (shorter != di->zone && rrType != QType::DS) {
f1b59a55
RA
160 do {
161 if (shorter == di->zone)
162 break;
163
164 bool foundShorter = false;
165 di->backend->lookup(QType(QType::ANY), shorter);
166 while (di->backend->get(rec)) {
6a323f63 167 if (rec.qname != rrLabel)
f1b59a55
RA
168 foundShorter = true;
169 if (rec.qtype == QType::NS)
170 auth=false;
171 }
6a323f63 172 if (!foundShorter && shorter != rrLabel && shorter != di->zone)
f1b59a55
RA
173 insnonterm.insert(shorter);
174
175 } while(chopOff(shorter));
176 }
177
f1b59a55
RA
178 if(haveNSEC3)
179 {
180 string hashed;
181 if(!narrow)
6a323f63 182 hashed=toLower(toBase32Hex(hashQNameWithSalt(ns3pr->d_iterations, ns3pr->d_salt, rrLabel)));
f1b59a55 183
6a323f63
RA
184 di->backend->updateDNSSECOrderAndAuthAbsolute(di->id, rrLabel, hashed, auth);
185 if(!auth || rrType == QType::DS)
f1b59a55 186 {
6a323f63
RA
187 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, rrLabel, "NS");
188 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, rrLabel, "A");
189 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, rrLabel, "AAAA");
f1b59a55
RA
190 }
191 }
192 else // NSEC
193 {
6a323f63
RA
194 di->backend->updateDNSSECOrderAndAuth(di->id, di->zone, rrLabel, auth);
195 if(!auth || rrType == QType::DS)
f1b59a55 196 {
6a323f63
RA
197 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, rrLabel, "A");
198 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, rrLabel, "AAAA");
f1b59a55
RA
199 }
200 }
6a323f63
RA
201
202
f1b59a55 203 // If we insert an NS, all the records below it become non auth - so, we're inserting a delegate.
6a323f63
RA
204 // Auth can only be false when the rrLabel is not the zone
205 if (auth == false && rrType == QType::NS) {
206 DLOG(L<<msgPrefix<<"Going to fix auth flags below "<<rrLabel<<endl);
f1b59a55 207 vector<string> qnames;
6a323f63 208 di->backend->listSubZone(rrLabel, di->id);
f1b59a55
RA
209 while(di->backend->get(rec)) {
210 if (rec.qtype.getCode() && rec.qtype.getCode() != QType::DS) // Skip ENT and DS records.
211 qnames.push_back(rec.qname);
212 }
213 for(vector<string>::const_iterator qname=qnames.begin(); qname != qnames.end(); ++qname) {
214 if(haveNSEC3) {
215 string hashed;
216 if(!narrow)
217 hashed=toLower(toBase32Hex(hashQNameWithSalt(ns3pr->d_iterations, ns3pr->d_salt, *qname)));
218
219 di->backend->updateDNSSECOrderAndAuthAbsolute(di->id, *qname, hashed, auth);
220 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, *qname, "NS");
221 }
222 else // NSEC
223 di->backend->updateDNSSECOrderAndAuth(di->id, di->zone, *qname, auth);
224
225 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, *qname, "AAAA");
226 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, *qname, "A");
227 }
228 }
f1b59a55
RA
229 }
230 } // rr->d_class == QClass::IN
231
6a323f63
RA
232
233 // Delete records - section 3.4.2.3 and 3.4.2.4 with the exception of the 'always leave 1 NS rule' as that's handled by
234 // the code that calls this performUpdate().
235 if ((rr->d_class == QClass::ANY || rr->d_class == QClass::NONE) && rrType != QType::SOA) { // never delete a SOA.
236 DLOG(L<<msgPrefix<<"Deleting records QClasse:"<<rr->d_class<<"; rrType: "<<rrType.getName()<<endl);
237 di->backend->lookup(rrType, rrLabel);
238 while(di->backend->get(rec)) {
239 if (rr->d_class == QClass::ANY) { // 3.4.2.3
240 if (rec.qname == di->zone && (rec.qtype == QType::NS || rec.qtype == QType::SOA)) // Never delete all SOA and NS's
241 rrset.push_back(rec);
242 else
f1b59a55
RA
243 recordsToDelete.push_back(rec);
244 }
6a323f63
RA
245 if (rr->d_class == QClass::NONE) { // 3.4.2.4
246 if (rrType == rec.qtype && rec.getZoneRepresentation() == rr->d_content->getZoneRepresentation())
247 recordsToDelete.push_back(rec);
248 else
249 rrset.push_back(rec);
250 }
f1b59a55 251
f1b59a55 252 }
6a323f63 253 di->backend->replaceRRSet(di->id, rrLabel, rrType, rrset);
f1b59a55 254
f1b59a55 255
6a323f63
RA
256 if (recordsToDelete.size()) {
257 // If we remove an NS which is not at apex of the zone, we need to make everthing below it auth=true as those now are not delegated anymore.
258 if (rrType == QType::NS && rrLabel != di->zone) {
f1b59a55 259 vector<string> changeAuth;
6a323f63 260 di->backend->listSubZone(rrLabel, di->id);
f1b59a55 261 while (di->backend->get(rec)) {
6a323f63 262 if (rec.qtype.getCode()) // skip ENT records, they are always false.
f1b59a55
RA
263 changeAuth.push_back(rec.qname);
264 }
265 for (vector<string>::const_iterator changeRec=changeAuth.begin(); changeRec!=changeAuth.end(); ++changeRec) {
266 if(haveNSEC3) {
267 string hashed;
268 if(!narrow)
269 hashed=toLower(toBase32Hex(hashQNameWithSalt(ns3pr->d_iterations, ns3pr->d_salt, *changeRec)));
270
271 di->backend->updateDNSSECOrderAndAuthAbsolute(di->id, *changeRec, hashed, true);
272 }
273 else // NSEC
274 di->backend->updateDNSSECOrderAndAuth(di->id, di->zone, *changeRec, true);
275 }
276 }
f1b59a55 277
6a323f63
RA
278 // Fix ENT records.
279 // We must check if we have a record below the current level and if we removed the 'last' record
280 // on that level. If so, we must insert an ENT record.
281 // We take extra care here to not 'include' the record that we just deleted. Some backends will still return it.
282 bool foundDeeper = false, foundOther = false;
283 di->backend->listSubZone(rrLabel, di->id);
284 while (di->backend->get(rec)) {
285 if (rec.qname == rrLabel && !count(recordsToDelete.begin(), recordsToDelete.end(), rec))
286 foundOther = true;
287 if (rec.qname != rrLabel)
288 foundDeeper = true;
289 }
f1b59a55 290
6a323f63
RA
291 if (foundDeeper && !foundOther) {
292 insnonterm.insert(rrLabel);
293 } else if (!foundOther) {
294 // If we didn't have to insert an ENT, we might have deleted a record at very deep level
295 // and we must then clean up the ENT's above the deleted record.
296 string shorter(rrLabel);
297 do {
298 bool foundRealRR=false;
299 if (shorter == di->zone)
300 break;
301 // The reason for a listSubZone here is because might go up the tree and find the root ENT of another branch
302 // consider these non ENT-records:
303 // a.b.c.d.e.test.com
304 // a.b.d.e.test.com
305 // if we delete a.b.c.d.e.test.com, we go up to d.e.test.com and then find a.b.d.e.test.com
306 // At that point we can stop deleting ENT's because the tree is in tact again.
307 di->backend->listSubZone(shorter, di->id);
308 while (di->backend->get(rec)) {
309 if (rec.qtype.getCode())
310 foundRealRR=true;
311 }
312 if (!foundRealRR)
313 delnonterm.insert(shorter);
314 else
315 break; // we found a real record - tree is ok again.
316 }while(chopOff(shorter));
317 }
f1b59a55 318 }
6a323f63 319 }
f1b59a55 320
6a323f63
RA
321
322 //Insert and delete ENT's
323 if (insnonterm.size() > 0 || delnonterm.size() > 0) {
324 DLOG(L<<msgPrefix<<"Updating ENT records - "<<insnonterm.size()<<"|"<<delnonterm.size()<<endl);
325 di->backend->updateEmptyNonTerminals(di->id, di->zone, insnonterm, delnonterm, false);
326 for (set<string>::const_iterator i=insnonterm.begin(); i!=insnonterm.end(); i++) {
327 string hashed;
328 if(haveNSEC3)
329 {
f1b59a55 330 string hashed;
6a323f63
RA
331 if(!narrow)
332 hashed=toLower(toBase32Hex(hashQNameWithSalt(ns3pr->d_iterations, ns3pr->d_salt, *i)));
333 di->backend->updateDNSSECOrderAndAuthAbsolute(di->id, *i, hashed, false);
f1b59a55
RA
334 }
335 }
336 }
337
6a323f63 338 return recordsToDelete.size() + addedRecords + updatedRecords;
f1b59a55
RA
339}
340
341int PacketHandler::processUpdate(DNSPacket *p) {
342 if (::arg().mustDo("disable-rfc2136"))
343 return RCode::Refused;
344
345 string msgPrefix="UPDATE from " + p->getRemote() + " for " + p->qdomain + ": ";
346 L<<Logger::Info<<msgPrefix<<"Processing started."<<endl;
347
348 // Check permissions - IP based
349 vector<string> allowedRanges;
350 B.getDomainMetadata(p->qdomain, "ALLOW-2136-FROM", allowedRanges);
351 if (! ::arg()["allow-2136-from"].empty())
352 stringtok(allowedRanges, ::arg()["allow-2136-from"], ", \t" );
353
354 NetmaskGroup ng;
355 for(vector<string>::const_iterator i=allowedRanges.begin(); i != allowedRanges.end(); i++)
356 ng.addMask(*i);
357
358 if ( ! ng.match(&p->d_remote)) {
359 L<<Logger::Error<<msgPrefix<<"Remote not listed in allow-2136-from or domainmetadata. Sending REFUSED"<<endl;
360 return RCode::Refused;
361 }
362
363
364 // Check permissions - TSIG based.
365 vector<string> tsigKeys;
366 B.getDomainMetadata(p->qdomain, "TSIG-ALLOW-2136", tsigKeys);
367 if (tsigKeys.size() > 0) {
368 bool validKey = false;
369
370 TSIGRecordContent trc;
371 string inputkey, message;
372 if (! p->getTSIGDetails(&trc, &inputkey, &message)) {
373 L<<Logger::Error<<msgPrefix<<"TSIG key required, but packet does not contain key. Sending REFUSED"<<endl;
374 return RCode::Refused;
375 }
376
377 for(vector<string>::const_iterator key=tsigKeys.begin(); key != tsigKeys.end(); key++) {
378 if (inputkey == *key) // because checkForCorrectTSIG has already been performed earlier on, if the names of the ky match with the domain given. THis is valid.
379 validKey=true;
380 }
381
382 if (!validKey) {
383 L<<Logger::Error<<msgPrefix<<"TSIG key ("<<inputkey<<") required, but no matching key found in domainmetadata, tried "<<tsigKeys.size()<<". Sending REFUSED"<<endl;
384 return RCode::Refused;
385 }
386 }
387
388 if (tsigKeys.size() == 0 && p->d_havetsig)
389 L<<Logger::Warning<<msgPrefix<<"TSIG is provided, but domain is not secured with TSIG. Processing continues"<<endl;
390
391 // RFC2136 uses the same DNS Header and Message as defined in RFC1035.
392 // This means we can use the MOADNSParser to parse the incoming packet. The result is that we have some different
393 // variable names during the use of our MOADNSParser.
394 MOADNSParser mdp(p->getString());
395 if (mdp.d_header.qdcount != 1) {
396 L<<Logger::Warning<<msgPrefix<<"Zone Count is not 1, sending FormErr"<<endl;
397 return RCode::FormErr;
398 }
399
400 if (p->qtype.getCode() != QType::SOA) { // RFC2136 2.3 - ZTYPE must be SOA
401 L<<Logger::Warning<<msgPrefix<<"Query ZTYPE is not SOA, sending FormErr"<<endl;
402 return RCode::FormErr;
403 }
404
405 if (p->qclass != QClass::IN) {
406 L<<Logger::Warning<<msgPrefix<<"Class is not IN, sending NotAuth"<<endl;
407 return RCode::NotAuth;
408 }
409
410 DomainInfo di;
411 di.backend=0;
412 if(!B.getDomainInfo(p->qdomain, di) || !di.backend) {
413 L<<Logger::Error<<msgPrefix<<"Can't determine backend for domain '"<<p->qdomain<<"' (or backend does not support RFC2136 operation)"<<endl;
414 return RCode::NotAuth;
415 }
416
417 if (di.kind == DomainInfo::Slave) { //TODO: We do not support the forwarding to master stuff.. which we should ;-)
418 L<<Logger::Error<<msgPrefix<<"We are slave for the domain and do not support forwarding to master, sending NotImp"<<endl;
419 return RCode::NotImp;
420 }
421
422 // Check if all the records provided are within the zone
423 for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i != mdp.d_answers.end(); ++i) {
424 const DNSRecord *rr = &i->first;
425 // Skip this check for other field types (like the TSIG - which is in the additional section)
426 // For a TSIG, the label is the dnskey.
427 if (! (rr->d_place == DNSRecord::Answer || rr->d_place == DNSRecord::Nameserver))
428 continue;
429
430 string label = stripDot(rr->d_label);
431
432 if (!endsOn(label, di.zone)) {
433 L<<Logger::Error<<msgPrefix<<"Received update/record out of zone, sending NotZone."<<endl;
434 return RCode::NotZone;
435 }
436 }
437
438 //TODO: Start a lock here, to make section 3.7 correct???
439 L<<Logger::Info<<msgPrefix<<"starting transaction."<<endl;
440 if (!di.backend->startTransaction(p->qdomain, -1)) { // Not giving the domain_id means that we do not delete the records.
441 L<<Logger::Error<<msgPrefix<<"Backend for domain "<<p->qdomain<<" does not support transaction. Can't do Update packet."<<endl;
442 return RCode::NotImp;
443 }
444
445 // 3.2.1 and 3.2.2 - Prerequisite check
446 for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i != mdp.d_answers.end(); ++i) {
447 const DNSRecord *rr = &i->first;
448 if (rr->d_place == DNSRecord::Answer) {
449 int res = checkUpdatePrerequisites(rr, &di);
450 if (res>0) {
451 L<<Logger::Error<<msgPrefix<<"Failed PreRequisites check, returning "<<res<<endl;
452 di.backend->abortTransaction();
453 return res;
454 }
455 }
456 }
457
458 // 3.2.3 - Prerequisite check - this is outside of updatePrequisitesCheck because we check an RRSet and not the RR.
459 typedef pair<string, QType> rrSetKey_t;
460 typedef vector<DNSResourceRecord> rrVector_t;
461 typedef std::map<rrSetKey_t, rrVector_t> RRsetMap_t;
462 RRsetMap_t preReqRRsets;
463 for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i != mdp.d_answers.end(); ++i) {
464 const DNSRecord *rr = &i->first;
465 if (rr->d_place == DNSRecord::Answer) {
466 // Last line of 3.2.3
467 if (rr->d_class != QClass::IN && rr->d_class != QClass::NONE && rr->d_class != QClass::ANY)
468 return RCode::FormErr;
469
470 if (rr->d_class == QClass::IN) {
471 rrSetKey_t key = make_pair(stripDot(rr->d_label), rr->d_type);
472 rrVector_t *vec = &preReqRRsets[key];
473 vec->push_back(DNSResourceRecord(*rr));
474 }
475 }
476 }
477
478 if (preReqRRsets.size() > 0) {
479 RRsetMap_t zoneRRsets;
480 for (RRsetMap_t::iterator preRRSet = preReqRRsets.begin(); preRRSet != preReqRRsets.end(); ++preRRSet) {
481 rrSetKey_t rrSet=preRRSet->first;
482 rrVector_t *vec = &preRRSet->second;
483
484 DNSResourceRecord rec;
485 di.backend->lookup(QType(QType::ANY), rrSet.first);
486 uint16_t foundRR=0, matchRR=0;
487 while (di.backend->get(rec)) {
488 if (rec.qtype == rrSet.second) {
489 foundRR++;
490 for(rrVector_t::iterator rrItem=vec->begin(); rrItem != vec->end(); ++rrItem) {
491 rrItem->ttl = rec.ttl; // The compare one line below also compares TTL, so we make them equal because TTL is not user within prerequisite checks.
492 if (*rrItem == rec)
493 matchRR++;
494 }
495 }
496 }
497 if (matchRR != foundRR || foundRR != vec->size()) {
498 L<<Logger::Error<<msgPrefix<<"Failed PreRequisites check, returning NXRRSet"<<endl;
499 di.backend->abortTransaction();
500 return RCode::NXRRSet;
501 }
502 }
503 }
504
505
506
507 // 3.4 - Prescan & Add/Update/Delete records
508 uint16_t changedRecords = 0;
509 try {
510
511 // 3.4.1 - Prescan section
512 for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i != mdp.d_answers.end(); ++i) {
513 const DNSRecord *rr = &i->first;
514 if (rr->d_place == DNSRecord::Nameserver) {
515 int res = checkUpdatePrescan(rr);
516 if (res>0) {
517 L<<Logger::Error<<msgPrefix<<"Failed prescan check, returning "<<res<<endl;
518 di.backend->abortTransaction();
519 return res;
520 }
521 }
522 }
523
524 bool updatedSerial=false;
525 NSEC3PARAMRecordContent ns3pr;
526 bool narrow;
527 bool haveNSEC3 = d_dk.getNSEC3PARAM(di.zone, &ns3pr, &narrow);
528
529 // We get all the before/after fields before doing anything to the db.
530 // We can't do this inside performUpdate() because when we remove a delegate, the before/after result is different to what it should be
531 // to purge the cache correctly - One update/delete might cause a before/after to be created which is before/after the original before/after.
532 vector< pair<string, string> > beforeAfterSet;
6a323f63 533 /*if (!haveNSEC3) {
f1b59a55
RA
534 for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i != mdp.d_answers.end(); ++i) {
535 const DNSRecord *rr = &i->first;
536 if (rr->d_place == DNSRecord::Nameserver) {
537 string before, after;
6a323f63 538 di.backend->getBeforeAndAfterNames(di.id, di.zone, stripDot(rr->d_label), before, after, (rr->d_class != QClass::IN));
f1b59a55
RA
539 beforeAfterSet.push_back(make_pair(before, after));
540 }
541 }
6a323f63 542 }*/
f1b59a55
RA
543
544 // 3.4.2 - Perform the updates.
545 // There's a special condition where deleting the last NS record at zone apex is never deleted (3.4.2.4)
546 // This means we must do it outside the normal performUpdate() because that focusses only on a seperate RR.
547 vector<const DNSRecord *> nsRRtoDelete;
548 for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i != mdp.d_answers.end(); ++i) {
549 const DNSRecord *rr = &i->first;
550 if (rr->d_place == DNSRecord::Nameserver) {
551 if (rr->d_class == QClass::NONE && rr->d_type == QType::NS && stripDot(rr->d_label) == di.zone)
552 nsRRtoDelete.push_back(rr);
553 else
554 changedRecords += performUpdate(msgPrefix, rr, &di, narrow, haveNSEC3, &ns3pr, &updatedSerial);
555 }
556 }
557 if (nsRRtoDelete.size()) {
558 vector<DNSResourceRecord> nsRRInZone;
559 DNSResourceRecord rec;
560 di.backend->lookup(QType(QType::NS), di.zone);
561 while (di.backend->get(rec)) {
562 nsRRInZone.push_back(rec);
563 }
564 if (nsRRInZone.size() > nsRRtoDelete.size()) { // only delete if the NS's we delete are less then what we have in the zone (3.4.2.4)
565 for (vector<DNSResourceRecord>::iterator inZone=nsRRInZone.begin(); inZone != nsRRInZone.end(); inZone++) {
566 for (vector<const DNSRecord *>::iterator rr=nsRRtoDelete.begin(); rr != nsRRtoDelete.end(); rr++) {
567 if (inZone->getZoneRepresentation() == (*rr)->d_content->getZoneRepresentation())
568 changedRecords += performUpdate(msgPrefix, *rr, &di, narrow, haveNSEC3, &ns3pr, &updatedSerial);
569 }
570 }
571 }
572 }
573
f1b59a55 574 // Purge the records!
6a323f63
RA
575 string zone(di.zone);
576 zone.append("$");
577 PC.purge(zone); // For NSEC3, nuke the complete zone.
578/*
f1b59a55
RA
579 if (changedRecords > 0) {
580 if (haveNSEC3) {
581 string zone(di.zone);
582 zone.append("$");
583 PC.purge(zone); // For NSEC3, nuke the complete zone.
584 } else {
585 //for(vector< pair<string, string> >::const_iterator i=beforeAfterSet.begin(); i != beforeAfterSet.end(); i++)
586 //PC.purgeRange(i->first, i->second, di.zone);
587 }
588 }
6a323f63 589*/
f1b59a55
RA
590 // Section 3.6 - Update the SOA serial - outside of performUpdate because we do a SOA update for the complete update message
591 if (changedRecords > 0 && !updatedSerial)
592 increaseSerial(msgPrefix, di);
593 }
594 catch (AhuException &e) {
595 L<<Logger::Error<<msgPrefix<<"Caught AhuException: "<<e.reason<<"; Sending ServFail!"<<endl;
596 di.backend->abortTransaction();
597 return RCode::ServFail;
598 }
599 catch (...) {
600 L<<Logger::Error<<msgPrefix<<"Caught unknown exception when performing update. Sending ServFail!"<<endl;
601 di.backend->abortTransaction();
602 return RCode::ServFail;
603 }
604
605 if (!di.backend->commitTransaction()) {
606 L<<Logger::Error<<msgPrefix<<"Failed to commit update for domain "<<di.zone<<"!"<<endl;
607 return RCode::ServFail;
608 }
609
610 L<<Logger::Info<<msgPrefix<<"Update completed, "<<changedRecords<<" changed records commited."<<endl;
611 return RCode::NoError; //rfc 2136 3.4.2.5
612}
613
614void PacketHandler::increaseSerial(const string &msgPrefix, const DomainInfo& di) {
615 DNSResourceRecord rec, newRec;
616 di.backend->lookup(QType(QType::SOA), di.zone);
617 bool foundSOA=false;
618 while (di.backend->get(rec)) {
619 newRec = rec;
620 foundSOA=true;
621 }
622 if (!foundSOA) {
623 throw AhuException("SOA-Serial update failed because there was no SOA. Wowie.");
624 }
625 SOAData soa2Update;
626 fillSOAData(rec.content, soa2Update);
627
628 vector<string> soaEdit2136Setting;
629 B.getDomainMetadata(di.zone, "SOA-EDIT-2136", soaEdit2136Setting);
630 string soaEdit2136 = "DEFAULT";
631 string soaEdit;
632 if (!soaEdit2136Setting.empty()) {
633 soaEdit2136 = soaEdit2136Setting[0];
634 if (pdns_iequals(soaEdit2136, "SOA-EDIT") || pdns_iequals(soaEdit2136,"SOA-EDIT-INCREASE") ){
635 vector<string> soaEditSetting;
636 B.getDomainMetadata(di.zone, "SOA-EDIT", soaEditSetting);
637 if (soaEditSetting.empty()) {
638 L<<Logger::Error<<msgPrefix<<"Using "<<soaEdit2136<<" for SOA-EDIT-2136 increase on RFC2136, but SOA-EDIT is not set for domain. Using DEFAULT for SOA-EDIT-2136"<<endl;
639 soaEdit2136 = "DEFAULT";
640 } else
641 soaEdit = soaEditSetting[0];
642 }
643 }
644
645
646 if (pdns_iequals(soaEdit2136, "INCREASE"))
647 soa2Update.serial++;
648 else if (pdns_iequals(soaEdit2136, "SOA-EDIT-INCREASE")) {
649 uint32_t newSer = calculateEditSOA(soa2Update, soaEdit);
650 if (newSer <= soa2Update.serial)
651 soa2Update.serial++;
652 else
653 soa2Update.serial = newSer;
654 } else if (pdns_iequals(soaEdit2136, "SOA-EDIT"))
655 soa2Update.serial = calculateEditSOA(soa2Update, soaEdit);
656 else if (pdns_iequals(soaEdit2136, "EPOCH"))
657 soa2Update.serial = time(0);
658 else {
659 time_t now = time(0);
660 struct tm tm;
661 localtime_r(&now, &tm);
662 boost::format fmt("%04d%02d%02d%02d");
663 string newserdate=(fmt % (tm.tm_year+1900) % (tm.tm_mon +1 )% tm.tm_mday % 1).str();
664 uint32_t newser = atol(newserdate.c_str());
665 if (newser <= soa2Update.serial)
666 soa2Update.serial++;
667 else
668 soa2Update.serial = newser;
669 }
670
671
672 newRec.content = serializeSOAData(soa2Update);
673 //di.backend->updateRecord(rec, newRec);
674 PC.purge(newRec.qname);
675}