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