]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/rfc2136handler.cc
serialize dnsname
[thirdparty/pdns.git] / pdns / rfc2136handler.cc
CommitLineData
870a0fe4
AT
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif
f1b59a55
RA
4#include "packethandler.hh"
5#include "qtype.hh"
6#include "dnspacket.hh"
7#include "packetcache.hh"
8#include "dnsseckeeper.hh"
9#include "base64.hh"
10#include "base32.hh"
8c9bde70 11#include <boost/foreach.hpp>
f1b59a55
RA
12#include "misc.hh"
13#include "arguments.hh"
b3148887
RA
14#include "resolver.hh"
15#include "dns_random.hh"
7facf345 16#include "backends/gsql/ssql.hh"
f1b59a55
RA
17
18extern PacketCache PC;
276a5ce7 19extern StatBag S;
f1b59a55 20
ee9ef8f2
RA
21pthread_mutex_t PacketHandler::s_rfc2136lock=PTHREAD_MUTEX_INITIALIZER;
22
f1b59a55
RA
23// Implement section 3.2.1 and 3.2.2 of RFC2136
24int PacketHandler::checkUpdatePrerequisites(const DNSRecord *rr, DomainInfo *di) {
25 if (rr->d_ttl != 0)
26 return RCode::FormErr;
27
28 // 3.2.1 and 3.2.2 check content length.
29 if ( (rr->d_class == QClass::NONE || rr->d_class == QClass::ANY) && rr->d_clen != 0)
30 return RCode::FormErr;
31
6a323f63 32 string rrLabel = stripDot(rr->d_label);
f1b59a55
RA
33
34 bool foundRecord=false;
35 DNSResourceRecord rec;
6a323f63 36 di->backend->lookup(QType(QType::ANY), rrLabel);
f1b59a55
RA
37 while(di->backend->get(rec)) {
38 if (!rec.qtype.getCode())
39 continue;
40 if ((rr->d_type != QType::ANY && rec.qtype == rr->d_type) || rr->d_type == QType::ANY)
41 foundRecord=true;
42 }
43
914353ca
KM
44 // Section 3.2.1
45 if (rr->d_class == QClass::ANY && !foundRecord) {
46 if (rr->d_type == QType::ANY)
f1b59a55
RA
47 return RCode::NXDomain;
48 if (rr->d_type != QType::ANY)
49 return RCode::NXRRSet;
914353ca 50 }
f1b59a55
RA
51
52 // Section 3.2.2
53 if (rr->d_class == QClass::NONE && foundRecord) {
54 if (rr->d_type == QType::ANY)
55 return RCode::YXDomain;
56 if (rr->d_type != QType::ANY)
57 return RCode::YXRRSet;
58 }
59
60 return RCode::NoError;
61}
62
63
64// Method implements section 3.4.1 of RFC2136
65int PacketHandler::checkUpdatePrescan(const DNSRecord *rr) {
66 // The RFC stats that d_class != ZCLASS, but we only support the IN class.
914353ca 67 if (rr->d_class != QClass::IN && rr->d_class != QClass::NONE && rr->d_class != QClass::ANY)
f1b59a55
RA
68 return RCode::FormErr;
69
70 QType qtype = QType(rr->d_type);
71
72 if (! qtype.isSupportedType())
73 return RCode::FormErr;
74
75 if ((rr->d_class == QClass::NONE || rr->d_class == QClass::ANY) && rr->d_ttl != 0)
76 return RCode::FormErr;
77
78 if (rr->d_class == QClass::ANY && rr->d_clen != 0)
79 return RCode::FormErr;
914353ca 80
f1b59a55
RA
81 if (qtype.isMetadataType())
82 return RCode::FormErr;
83
84 if (rr->d_class != QClass::ANY && qtype.getCode() == QType::ANY)
85 return RCode::FormErr;
86
87 return RCode::NoError;
88}
89
6a323f63 90
f1b59a55 91// Implements section 3.4.2 of RFC2136
d0d273cf 92uint PacketHandler::performUpdate(const string &msgPrefix, const DNSRecord *rr, DomainInfo *di, bool isPresigned, bool* narrow, bool* haveNSEC3, NSEC3PARAMRecordContent *ns3pr, bool *updatedSerial) {
968de8d0 93
dff32e09
RA
94 string rrLabel = stripDot(rr->d_label);
95 rrLabel = toLower(rrLabel);
96 QType rrType = QType(rr->d_type);
97
dff32e09
RA
98 if (rrType == QType::NSEC || rrType == QType::NSEC3) {
99 L<<Logger::Warning<<msgPrefix<<"Trying to add/update/delete "<<rrLabel<<"|"<<rrType.getName()<<". These are generated records, ignoring!"<<endl;
914353ca 100 return 0;
dff32e09
RA
101 }
102
cc8df07f 103 if (!isPresigned && ((!::arg().mustDo("direct-dnskey") && rrType == QType::DNSKEY) || rrType == QType::RRSIG)) {
dff32e09
RA
104 L<<Logger::Warning<<msgPrefix<<"Trying to add/update/delete "<<rrLabel<<"|"<<rrType.getName()<<" in non-presigned zone, ignoring!"<<endl;
105 return 0;
106 }
107
968de8d0
KM
108 if ((rrType == QType::NSEC3PARAM || rrType == QType::DNSKEY) && rrLabel != di->zone) {
109 L<<Logger::Warning<<msgPrefix<<"Trying to add/update/delete "<<rrLabel<<"|"<<rrType.getName()<<", "<<rrType.getName()<<" must be at zone apex, ignoring!"<<endl;
dff32e09
RA
110 return 0;
111 }
112
968de8d0 113
d0d273cf 114 uint changedRecords = 0;
6a323f63
RA
115 DNSResourceRecord rec;
116 vector<DNSResourceRecord> rrset, recordsToDelete;
675fa24c 117 set<DNSName> delnonterm, insnonterm; // used to (at the end) fix ENT records.
6a323f63 118
f1b59a55 119
6a323f63
RA
120 if (rr->d_class == QClass::IN) { // 3.4.2.2 QClass::IN means insert or update
121 DLOG(L<<msgPrefix<<"Add/Update record (QClass == IN) "<<rrLabel<<"|"<<rrType.getName()<<endl);
f1b59a55 122
dff32e09 123 if (rrType == QType::NSEC3PARAM) {
8c9bde70 124 L<<Logger::Notice<<msgPrefix<<"Adding/updating NSEC3PARAM for zone, resetting ordernames."<<endl;
f858f843 125
dff32e09 126 NSEC3PARAMRecordContent nsec3param(rr->d_content->getZoneRepresentation(), di->zone);
f858f843 127 *narrow = false; // adding a NSEC3 will cause narrow mode to be dropped, as you cannot specify that in a NSEC3PARAM record
dff32e09 128 d_dk.setNSEC3PARAM(di->zone, nsec3param, (*narrow));
8c9bde70
KM
129
130 *haveNSEC3 = d_dk.getNSEC3PARAM(di->zone, ns3pr, narrow);
f858f843 131
dff32e09 132 vector<DNSResourceRecord> rrs;
8c9bde70 133 set<string> qnames, nssets, dssets;
f858f843 134 di->backend->list(di->zone, di->id);
dff32e09 135 while (di->backend->get(rec)) {
8c9bde70
KM
136 qnames.insert(rec.qname);
137 if(rec.qtype.getCode() == QType::NS && !pdns_iequals(rec.qname, di->zone))
138 nssets.insert(rec.qname);
139 if(rec.qtype.getCode() == QType::DS)
140 dssets.insert(rec.qname);
dff32e09 141 }
f858f843 142
8c9bde70
KM
143 string shorter, hashed;
144 BOOST_FOREACH(const string& qname, qnames) {
145 shorter = qname;
146 int ddepth = 0;
147 do {
148 if(pdns_iequals(qname, di->zone))
f858f843 149 break;
8c9bde70
KM
150 if(nssets.count(shorter))
151 ++ddepth;
152 } while(chopOff(shorter));
153
154 if (! *narrow && (ddepth == 0 || (ddepth == 1 && nssets.count(qname)))) {
1bad4190 155 hashed = toBase32Hex(hashQNameWithSalt(ns3pr->d_iterations, ns3pr->d_salt, qname));
8c9bde70
KM
156 di->backend->updateDNSSECOrderAndAuthAbsolute(di->id, qname, hashed, (ddepth == 0));
157
158 if (nssets.count(qname)) {
159 if (ns3pr->d_flags)
160 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, qname, "NS");
161 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, qname, "A");
162 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, qname, "AAAA");
f858f843 163 }
f858f843 164 } else {
8c9bde70 165 di->backend->nullifyDNSSECOrderNameAndUpdateAuth(di->id, qname, (ddepth == 0));
f858f843 166 }
8c9bde70
KM
167 if (ddepth == 1 || dssets.count(qname))
168 di->backend->setDNSSECAuthOnDsRecord(di->id, qname);
dff32e09
RA
169 }
170 return 1;
171 }
172
f858f843
RA
173
174
6a323f63
RA
175 bool foundRecord = false;
176 di->backend->lookup(rrType, rrLabel);
f1b59a55 177 while (di->backend->get(rec)) {
dff32e09
RA
178 rrset.push_back(rec);
179 foundRecord = true;
6a323f63 180 }
914353ca 181
6a323f63 182 if (foundRecord) {
73174643 183
dff32e09 184 if (rrType == QType::SOA) { // SOA updates require the serial to be higher than the current
f1b59a55 185 SOAData sdOld, sdUpdate;
6a323f63
RA
186 DNSResourceRecord *oldRec = &rrset.front();
187 fillSOAData(oldRec->content, sdOld);
188 oldRec->setContent(rr->d_content->getZoneRepresentation());
189 fillSOAData(oldRec->content, sdUpdate);
f1b59a55 190 if (rfc1982LessThan(sdOld.serial, sdUpdate.serial)) {
6a323f63 191 di->backend->replaceRRSet(di->id, oldRec->qname, oldRec->qtype, rrset);
f1b59a55 192 *updatedSerial = true;
f5059bd3 193 changedRecords++;
7facf345 194 L<<Logger::Notice<<msgPrefix<<"Replacing record "<<rrLabel<<"|"<<rrType.getName()<<endl;
73174643 195 } else {
f1b59a55 196 L<<Logger::Notice<<msgPrefix<<"Provided serial ("<<sdUpdate.serial<<") is older than the current serial ("<<sdOld.serial<<"), ignoring SOA update."<<endl;
73174643 197 }
6a323f63
RA
198
199 // It's not possible to have multiple CNAME's with the same NAME. So we always update.
200 } else if (rrType == QType::CNAME) {
73174643 201 int changedCNames = 0;
6a323f63 202 for (vector<DNSResourceRecord>::iterator i = rrset.begin(); i != rrset.end(); i++) {
73174643
RA
203 if (i->ttl != rr->d_ttl || i->content != rr->d_content->getZoneRepresentation()) {
204 i->ttl = rr->d_ttl;
205 i->setContent(rr->d_content->getZoneRepresentation());
206 changedCNames++;
207 }
208 }
209 if (changedCNames > 0) {
210 di->backend->replaceRRSet(di->id, rrLabel, rrType, rrset);
211 L<<Logger::Notice<<msgPrefix<<"Replacing record "<<rrLabel<<"|"<<rrType.getName()<<endl;
212 changedRecords += changedCNames;
213 } else {
214 L<<Logger::Notice<<msgPrefix<<"Replace for record "<<rrLabel<<"|"<<rrType.getName()<<" requested, but no changes made."<<endl;
f1b59a55 215 }
6a323f63
RA
216
217 // In any other case, we must check if the TYPE and RDATA match to provide an update (which effectily means a update of TTL)
218 } else {
73174643 219 int updateTTL=0;
6a323f63
RA
220 foundRecord = false;
221 for (vector<DNSResourceRecord>::iterator i = rrset.begin(); i != rrset.end(); i++) {
222 string content = rr->d_content->getZoneRepresentation();
223 if (rrType == i->qtype.getCode() && i->getZoneRepresentation() == content) {
73174643
RA
224 foundRecord=true;
225 if (i->ttl != rr->d_ttl) {
226 i->ttl = rr->d_ttl;
227 updateTTL++;
228 }
6a323f63
RA
229 }
230 }
73174643 231 if (updateTTL > 0) {
6a323f63 232 di->backend->replaceRRSet(di->id, rrLabel, rrType, rrset);
7facf345 233 L<<Logger::Notice<<msgPrefix<<"Replacing record "<<rrLabel<<"|"<<rrType.getName()<<endl;
73174643
RA
234 changedRecords += updateTTL;
235 } else {
236 L<<Logger::Notice<<msgPrefix<<"Replace for record "<<rrLabel<<"|"<<rrType.getName()<<" requested, but no changes made."<<endl;
7facf345 237 }
f1b59a55 238 }
6e42cac2 239
73174643 240 // ReplaceRRSet dumps our ordername and auth flag, so we need to correct it if we have changed records.
6e42cac2 241 // We can take the auth flag from the first RR in the set, as the name is different, so should the auth be.
73174643
RA
242 if (changedRecords > 0) {
243 bool auth = rrset.front().auth;
244
245 if(*haveNSEC3) {
246 string hashed;
247 if(! *narrow)
1bad4190 248 hashed=toBase32Hex(hashQNameWithSalt(ns3pr->d_iterations, ns3pr->d_salt, rrLabel));
73174643
RA
249
250 if (*narrow)
251 di->backend->nullifyDNSSECOrderNameAndUpdateAuth(di->id, rrLabel, auth);
252 else
253 di->backend->updateDNSSECOrderAndAuthAbsolute(di->id, rrLabel, hashed, auth);
254 if(!auth || rrType == QType::DS) {
255 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, rrLabel, "NS");
256 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, rrLabel, "A");
257 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, rrLabel, "AAAA");
258 }
6e42cac2 259
73174643
RA
260 } else { // NSEC
261 di->backend->updateDNSSECOrderAndAuth(di->id, di->zone, rrLabel, auth);
262 if(!auth || rrType == QType::DS) {
263 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, rrLabel, "A");
264 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, rrLabel, "AAAA");
265 }
6e42cac2
RA
266 }
267 }
268
269 } // if (foundRecord)
f1b59a55 270
6a323f63 271 // If we haven't found a record that matches, we must add it.
f1b59a55 272 if (! foundRecord) {
6a323f63
RA
273 L<<Logger::Notice<<msgPrefix<<"Adding record "<<rrLabel<<"|"<<rrType.getName()<<endl;
274 delnonterm.insert(rrLabel); // always remove any ENT's in the place where we're going to add a record.
f1b59a55
RA
275 DNSResourceRecord newRec(*rr);
276 newRec.domain_id = di->id;
f48c7915 277 newRec.auth = (rrLabel == di->zone || rrType.getCode() != QType::NS);
f1b59a55 278 di->backend->feedRecord(newRec);
b0704e0d 279 changedRecords++;
6a323f63
RA
280
281
282 // because we added a record, we need to fix DNSSEC data.
675fa24c 283 DNSName shorter(rrLabel);
163cb726 284 bool auth=newRec.auth;
6a82ce4a 285 bool fixDS = (rrType == QType::DS);
f1b59a55 286
6a82ce4a 287 if ( ! pdns_iequals(di->zone, shorter)) { // Everything at APEX is auth=1 && no ENT's
e70f2627 288 do {
6a82ce4a 289
e70f2627 290 if (pdns_iequals(di->zone, shorter))
f1b59a55 291 break;
914353ca 292
f1b59a55
RA
293 bool foundShorter = false;
294 di->backend->lookup(QType(QType::ANY), shorter);
295 while (di->backend->get(rec)) {
6a82ce4a
RA
296 if (pdns_iequals(rec.qname, rrLabel) && rec.qtype == QType::DS)
297 fixDS = true;
e70f2627
RA
298 if ( ! pdns_iequals(shorter, rrLabel) )
299 foundShorter = true;
163cb726 300 if (rec.qtype == QType::NS) // are we inserting below a delegate?
f1b59a55
RA
301 auth=false;
302 }
6a82ce4a 303
e70f2627 304 if (!foundShorter && auth && !pdns_iequals(shorter, rrLabel)) // haven't found any record at current level, insert ENT.
f1b59a55 305 insnonterm.insert(shorter);
e70f2627 306 if (foundShorter)
a95f897d 307 break; // if we find a shorter record, we can stop searching
675fa24c 308 } while(shorter.chopOff());
f1b59a55
RA
309 }
310
dff32e09 311 if(*haveNSEC3)
f1b59a55
RA
312 {
313 string hashed;
914353ca 314 if(! *narrow)
1bad4190 315 hashed=toBase32Hex(hashQNameWithSalt(ns3pr->d_iterations, ns3pr->d_salt, rrLabel));
914353ca 316
e70f2627
RA
317 if (*narrow)
318 di->backend->nullifyDNSSECOrderNameAndUpdateAuth(di->id, rrLabel, auth);
319 else
320 di->backend->updateDNSSECOrderAndAuthAbsolute(di->id, rrLabel, hashed, auth);
321
6a82ce4a
RA
322 if (fixDS)
323 di->backend->setDNSSECAuthOnDsRecord(di->id, rrLabel);
324
929eee6b 325 if(!auth)
f1b59a55 326 {
914353ca 327 if (ns3pr->d_flags)
be40c2df 328 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, rrLabel, "NS");
6a323f63
RA
329 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, rrLabel, "A");
330 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, rrLabel, "AAAA");
f1b59a55
RA
331 }
332 }
333 else // NSEC
334 {
6a323f63 335 di->backend->updateDNSSECOrderAndAuth(di->id, di->zone, rrLabel, auth);
6a82ce4a 336 if (fixDS) {
929eee6b 337 di->backend->setDNSSECAuthOnDsRecord(di->id, rrLabel);
6a82ce4a
RA
338 }
339 if(!auth) {
340 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, rrLabel, "A");
341 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, rrLabel, "AAAA");
f1b59a55
RA
342 }
343 }
6a323f63
RA
344
345
f1b59a55 346 // If we insert an NS, all the records below it become non auth - so, we're inserting a delegate.
914353ca 347 // Auth can only be false when the rrLabel is not the zone
6a323f63
RA
348 if (auth == false && rrType == QType::NS) {
349 DLOG(L<<msgPrefix<<"Going to fix auth flags below "<<rrLabel<<endl);
6a82ce4a 350 insnonterm.clear(); // No ENT's are needed below delegates (auth=0)
f1b59a55 351 vector<string> qnames;
6a323f63 352 di->backend->listSubZone(rrLabel, di->id);
f1b59a55 353 while(di->backend->get(rec)) {
6a82ce4a 354 if (rec.qtype.getCode() && rec.qtype.getCode() != QType::DS && !pdns_iequals(rrLabel, rec.qname)) // Skip ENT, DS and our already corrected record.
f1b59a55
RA
355 qnames.push_back(rec.qname);
356 }
357 for(vector<string>::const_iterator qname=qnames.begin(); qname != qnames.end(); ++qname) {
dff32e09 358 if(*haveNSEC3) {
f1b59a55 359 string hashed;
914353ca 360 if(! *narrow)
1bad4190 361 hashed=toBase32Hex(hashQNameWithSalt(ns3pr->d_iterations, ns3pr->d_salt, *qname));
914353ca 362
6a82ce4a
RA
363 if (*narrow)
364 di->backend->nullifyDNSSECOrderNameAndUpdateAuth(di->id, rrLabel, auth);
365 else
366 di->backend->updateDNSSECOrderAndAuthAbsolute(di->id, *qname, hashed, auth);
367
be40c2df
RA
368 if (ns3pr->d_flags)
369 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, *qname, "NS");
f1b59a55
RA
370 }
371 else // NSEC
372 di->backend->updateDNSSECOrderAndAuth(di->id, di->zone, *qname, auth);
373
374 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, *qname, "AAAA");
375 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, *qname, "A");
376 }
377 }
f1b59a55
RA
378 }
379 } // rr->d_class == QClass::IN
380
914353ca 381
6a323f63
RA
382 // 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
383 // the code that calls this performUpdate().
384 if ((rr->d_class == QClass::ANY || rr->d_class == QClass::NONE) && rrType != QType::SOA) { // never delete a SOA.
fdf983eb 385 DLOG(L<<msgPrefix<<"Deleting records: "<<rrLabel<<"; QClasse:"<<rr->d_class<<"; rrType: "<<rrType.getName()<<endl);
dff32e09
RA
386
387 if (rrType == QType::NSEC3PARAM) {
914353ca 388 L<<Logger::Notice<<msgPrefix<<"Deleting NSEC3PARAM from zone, resetting ordernames."<<endl;
dff32e09
RA
389 if (rr->d_class == QClass::ANY)
390 d_dk.unsetNSEC3PARAM(rrLabel);
391 else if (rr->d_class == QClass::NONE) {
392 NSEC3PARAMRecordContent nsec3rr(rr->d_content->getZoneRepresentation(), di->zone);
0bb68041 393 if (ns3pr->getZoneRepresentation() == nsec3rr.getZoneRepresentation())
dff32e09
RA
394 d_dk.unsetNSEC3PARAM(rrLabel);
395 else
396 return 0;
914353ca 397 } else
dff32e09
RA
398 return 0;
399
e3dc2b56
RA
400 // We retrieve new values, other RR's in this update package might need it as well.
401 *haveNSEC3 = d_dk.getNSEC3PARAM(di->zone, ns3pr, narrow);
402
dff32e09 403 vector<DNSResourceRecord> rrs;
b48fe0e3
KM
404 set<string> qnames, nssets, dssets, ents;
405 di->backend->list(di->zone, di->id);
b1ca7619 406 while (di->backend->get(rec)) {
b48fe0e3
KM
407 qnames.insert(rec.qname);
408 if(rec.qtype.getCode() == QType::NS && !pdns_iequals(rec.qname, di->zone))
409 nssets.insert(rec.qname);
410 if(rec.qtype.getCode() == QType::DS)
411 dssets.insert(rec.qname);
412 if(!rec.qtype.getCode())
413 ents.insert(rec.qname);
b1ca7619 414 }
b48fe0e3
KM
415
416 string shorter, hashed;
417 BOOST_FOREACH(const string& qname, qnames) {
418 shorter = qname;
419 int ddepth = 0;
420 do {
421 if(pdns_iequals(qname, di->zone))
422 break;
423 if(nssets.count(shorter))
424 ++ddepth;
425 } while(chopOff(shorter));
426
427 if (!ents.count(qname) && (ddepth == 0 || (ddepth == 1 && nssets.count(qname)))) {
428 di->backend->updateDNSSECOrderAndAuth(di->id, di->zone, qname, (ddepth == 0));
429
430 if (nssets.count(qname)) {
431 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, qname, "A");
432 di->backend->nullifyDNSSECOrderNameAndAuth(di->id, qname, "AAAA");
b1ca7619 433 }
b48fe0e3
KM
434 } else {
435 di->backend->nullifyDNSSECOrderNameAndUpdateAuth(di->id, qname, (ddepth == 0));
b1ca7619 436 }
b48fe0e3
KM
437 if (ddepth == 1 || dssets.count(qname))
438 di->backend->setDNSSECAuthOnDsRecord(di->id, qname);
dff32e09
RA
439 }
440 return 1;
589563c0 441 } // end of NSEC3PARAM delete block
dff32e09
RA
442
443
6a323f63
RA
444 di->backend->lookup(rrType, rrLabel);
445 while(di->backend->get(rec)) {
446 if (rr->d_class == QClass::ANY) { // 3.4.2.3
447 if (rec.qname == di->zone && (rec.qtype == QType::NS || rec.qtype == QType::SOA)) // Never delete all SOA and NS's
448 rrset.push_back(rec);
449 else
f1b59a55
RA
450 recordsToDelete.push_back(rec);
451 }
6a323f63
RA
452 if (rr->d_class == QClass::NONE) { // 3.4.2.4
453 if (rrType == rec.qtype && rec.getZoneRepresentation() == rr->d_content->getZoneRepresentation())
454 recordsToDelete.push_back(rec);
455 else
456 rrset.push_back(rec);
457 }
f1b59a55 458 }
589563c0
RA
459
460 if (recordsToDelete.size()) {
461 di->backend->replaceRRSet(di->id, rrLabel, rrType, rrset);
462 L<<Logger::Notice<<msgPrefix<<"Deleting record "<<rrLabel<<"|"<<rrType.getName()<<endl;
463 changedRecords += recordsToDelete.size();
f1b59a55 464
f1b59a55 465
589563c0
RA
466 // If we've removed a delegate, we need to reset ordername/auth for some records.
467 if (rrType == QType::NS && rrLabel != di->zone) {
675fa24c 468 vector<DNSName> belowOldDelegate, nsRecs, updateAuthFlag;
6a323f63 469 di->backend->listSubZone(rrLabel, di->id);
f1b59a55 470 while (di->backend->get(rec)) {
7a0a9f8a 471 if (rec.qtype.getCode()) // skip ENT records, they are always auth=false
a95f897d
RA
472 belowOldDelegate.push_back(rec.qname);
473 if (rec.qtype.getCode() == QType::NS && rec.qname != rrLabel)
474 nsRecs.push_back(rec.qname);
f1b59a55 475 }
a95f897d 476
675fa24c 477 for(auto &belowOldDel: belowOldDelegate)
a95f897d
RA
478 {
479 bool isBelowDelegate = false;
88bffdda 480 for(vector<string>::const_iterator ns=nsRecs.begin(); ns!= nsRecs.end(); ns++) {
a95f897d
RA
481 if (endsOn(*ns, *belowOldDel)) {
482 isBelowDelegate=true;
483 break;
484 }
485 }
486 if (!isBelowDelegate)
487 updateAuthFlag.push_back(*belowOldDel);
488 }
489
490 for (vector<string>::const_iterator changeRec=updateAuthFlag.begin(); changeRec!=updateAuthFlag.end(); ++changeRec) {
dff32e09 491 if(*haveNSEC3) {
f1b59a55 492 string hashed;
914353ca 493 if(! *narrow)
1bad4190 494 hashed=toBase32Hex(hashQNameWithSalt(ns3pr->d_iterations, ns3pr->d_salt, *changeRec));
914353ca 495
f1b59a55
RA
496 di->backend->updateDNSSECOrderAndAuthAbsolute(di->id, *changeRec, hashed, true);
497 }
498 else // NSEC
499 di->backend->updateDNSSECOrderAndAuth(di->id, di->zone, *changeRec, true);
500 }
501 }
f1b59a55 502
6a323f63
RA
503 // Fix ENT records.
504 // We must check if we have a record below the current level and if we removed the 'last' record
505 // on that level. If so, we must insert an ENT record.
fdf983eb 506 // We take extra care here to not 'include' the record that we just deleted. Some backends will still return it as they only reload on a commit.
9353d244 507 bool foundDeeper = false, foundOtherWithSameName = false;
6a323f63
RA
508 di->backend->listSubZone(rrLabel, di->id);
509 while (di->backend->get(rec)) {
510 if (rec.qname == rrLabel && !count(recordsToDelete.begin(), recordsToDelete.end(), rec))
9353d244 511 foundOtherWithSameName = true;
7a0a9f8a 512 if (rec.qname != rrLabel && rec.qtype.getCode() != QType::NS) //Skip NS records, as this would be a delegate that we can ignore as this does not require us to create a ENT
6a323f63
RA
513 foundDeeper = true;
514 }
f1b59a55 515
9353d244 516 if (foundDeeper && !foundOtherWithSameName) {
6a323f63 517 insnonterm.insert(rrLabel);
9353d244 518 } else if (!foundOtherWithSameName) {
6a323f63
RA
519 // If we didn't have to insert an ENT, we might have deleted a record at very deep level
520 // and we must then clean up the ENT's above the deleted record.
675fa24c 521 DNSName shorter(rrLabel);
9353d244 522 while (shorter != di->zone) {
675fa24c 523 shorter.chopOff();
9353d244 524 bool foundRealRR = false;
a95f897d 525 bool foundEnt = false;
9353d244
RA
526
527 // The reason for a listSubZone here is because might go up the tree and find the ENT of another branch
6a323f63 528 // consider these non ENT-records:
9353d244
RA
529 // b.c.d.e.test.com
530 // b.d.e.test.com
531 // if we delete b.c.d.e.test.com, we go up to d.e.test.com and then find b.d.e.test.com because that's below d.e.test.com.
6a323f63
RA
532 // At that point we can stop deleting ENT's because the tree is in tact again.
533 di->backend->listSubZone(shorter, di->id);
914353ca 534
9353d244
RA
535 while (di->backend->get(rec)) {
536 if (rec.qtype.getCode())
537 foundRealRR = true;
a95f897d
RA
538 else
539 foundEnt = true;
9353d244 540 }
3ab4cfca 541 if (!foundRealRR) {
a95f897d
RA
542 if (foundEnt) // only delete the ENT if we actually found one.
543 delnonterm.insert(shorter);
3ab4cfca 544 } else
9353d244
RA
545 break;
546 }
6a323f63 547 }
589563c0
RA
548 } else { // if (recordsToDelete.size())
549 L<<Logger::Notice<<msgPrefix<<"Deletion for record "<<rrLabel<<"|"<<rrType.getName()<<" requested, but not found."<<endl;
f1b59a55 550 }
589563c0
RA
551 } // (End of delete block d_class == ANY || d_class == NONE
552
f1b59a55 553
6a323f63
RA
554
555 //Insert and delete ENT's
556 if (insnonterm.size() > 0 || delnonterm.size() > 0) {
557 DLOG(L<<msgPrefix<<"Updating ENT records - "<<insnonterm.size()<<"|"<<delnonterm.size()<<endl);
558 di->backend->updateEmptyNonTerminals(di->id, di->zone, insnonterm, delnonterm, false);
559 for (set<string>::const_iterator i=insnonterm.begin(); i!=insnonterm.end(); i++) {
560 string hashed;
dff32e09 561 if(*haveNSEC3)
6a323f63 562 {
f1b59a55 563 string hashed;
914353ca 564 if(! *narrow)
1bad4190 565 hashed=toBase32Hex(hashQNameWithSalt(ns3pr->d_iterations, ns3pr->d_salt, *i));
0c72fa99 566 di->backend->updateDNSSECOrderAndAuthAbsolute(di->id, *i, hashed, true);
f1b59a55
RA
567 }
568 }
569 }
570
589563c0 571 return changedRecords;
f1b59a55
RA
572}
573
b3148887 574int PacketHandler::forwardPacket(const string &msgPrefix, DNSPacket *p, DomainInfo *di) {
e174b255 575 vector<string> forward;
71f758e0 576 B.getDomainMetadata(p->qdomain, "FORWARD-DNSUPDATE", forward);
e174b255 577
71f758e0 578 if (forward.size() == 0 && ! ::arg().mustDo("forward-dnsupdate")) {
d8293c79
RA
579 L<<Logger::Notice<<msgPrefix<<"Not configured to forward to master, returning Refused."<<endl;
580 return RCode::Refused;
e174b255
RA
581 }
582
b3148887 583 for(vector<string>::const_iterator master=di->masters.begin(); master != di->masters.end(); master++) {
dff32e09 584 L<<Logger::Notice<<msgPrefix<<"Forwarding packet to master "<<*master<<endl;
b3148887
RA
585 ComboAddress remote;
586 try {
cc456024 587 remote = ComboAddress(*master, 53);
b3148887
RA
588 }
589 catch (...) {
590 L<<Logger::Error<<msgPrefix<<"Failed to parse "<<*master<<" as valid remote."<<endl;
591 continue;
592 }
593
594 ComboAddress local;
595 if(remote.sin4.sin_family == AF_INET)
cc456024 596 local = ComboAddress(::arg()["query-local-address"]);
b3148887 597 else if(!::arg()["query-local-address6"].empty())
cc456024 598 local = ComboAddress(::arg()["query-local-address6"]);
b3148887 599 else
cc456024 600 local = ComboAddress("::");
b3148887
RA
601 int sock = makeQuerySocket(local, false); // create TCP socket. RFC2136 section 6.2 seems to be ok with this.
602
603 if( connect(sock, (struct sockaddr*)&remote, remote.getSocklen()) < 0 ) {
604 L<<Logger::Error<<msgPrefix<<"Failed to connect to "<<remote.toStringWithPort()<<": "<<stringerror()<<endl;
3897b9e1 605 closesocket(sock);
b3148887
RA
606 continue;
607 }
608
609 DNSPacket forwardPacket(*p);
610 forwardPacket.setID(dns_random(0xffff));
611 forwardPacket.setRemote(&remote);
612 uint16_t len=htons(forwardPacket.getString().length());
613 string buffer((const char*)&len, 2);
614 buffer.append(forwardPacket.getString());
914353ca 615 if(write(sock, buffer.c_str(), buffer.length()) < 0) {
b3148887 616 L<<Logger::Error<<msgPrefix<<"Unable to forward update message to "<<remote.toStringWithPort()<<", error:"<<stringerror()<<endl;
3897b9e1 617 closesocket(sock);
b3148887
RA
618 continue;
619 }
620
621 int res = waitForData(sock, 10, 0);
622 if (!res) {
cc456024 623 L<<Logger::Error<<msgPrefix<<"Timeout waiting for reply from master at "<<remote.toStringWithPort()<<endl;
3897b9e1 624 closesocket(sock);
b3148887
RA
625 continue;
626 }
627 if (res < 0) {
628 L<<Logger::Error<<msgPrefix<<"Error waiting for answer from master at "<<remote.toStringWithPort()<<", error:"<<stringerror()<<endl;
3897b9e1 629 closesocket(sock);
b3148887
RA
630 continue;
631 }
632
633 char lenBuf[2];
634 int recvRes;
635 recvRes = recv(sock, &lenBuf, sizeof(lenBuf), 0);
636 if (recvRes < 0) {
637 L<<Logger::Error<<msgPrefix<<"Could not receive data (length) from master at "<<remote.toStringWithPort()<<", error:"<<stringerror()<<endl;
3897b9e1 638 closesocket(sock);
b3148887
RA
639 continue;
640 }
641 int packetLen = lenBuf[0]*256+lenBuf[1];
642
643
644 char buf[packetLen];
645 recvRes = recv(sock, &buf, packetLen, 0);
646 if (recvRes < 0) {
647 L<<Logger::Error<<msgPrefix<<"Could not receive data (dnspacket) from master at "<<remote.toStringWithPort()<<", error:"<<stringerror()<<endl;
3897b9e1 648 closesocket(sock);
b3148887
RA
649 continue;
650 }
3897b9e1 651 closesocket(sock);
b3148887
RA
652
653 try {
b3148887
RA
654 MOADNSParser mdp(buf, recvRes);
655 L<<Logger::Info<<msgPrefix<<"Forward update message to "<<remote.toStringWithPort()<<", result was RCode "<<mdp.d_header.rcode<<endl;
656 return mdp.d_header.rcode;
657 }
658 catch (...) {
659 L<<Logger::Error<<msgPrefix<<"Failed to parse response packet from master at "<<remote.toStringWithPort()<<endl;
660 continue;
661 }
662 }
663 L<<Logger::Error<<msgPrefix<<"Failed to forward packet to master(s). Returning ServFail."<<endl;
664 return RCode::ServFail;
665
666}
667
f1b59a55 668int PacketHandler::processUpdate(DNSPacket *p) {
71f758e0 669 if (! ::arg().mustDo("experimental-dnsupdate"))
f1b59a55 670 return RCode::Refused;
914353ca 671
7facf345 672 string msgPrefix="UPDATE (" + itoa(p->d.id) + ") from " + p->getRemote() + " for " + p->qdomain + ": ";
f1b59a55
RA
673 L<<Logger::Info<<msgPrefix<<"Processing started."<<endl;
674
675 // Check permissions - IP based
676 vector<string> allowedRanges;
71f758e0
KM
677 B.getDomainMetadata(p->qdomain, "ALLOW-DNSUPDATE-FROM", allowedRanges);
678 if (! ::arg()["allow-dnsupdate-from"].empty())
679 stringtok(allowedRanges, ::arg()["allow-dnsupdate-from"], ", \t" );
f1b59a55
RA
680
681 NetmaskGroup ng;
682 for(vector<string>::const_iterator i=allowedRanges.begin(); i != allowedRanges.end(); i++)
683 ng.addMask(*i);
914353ca 684
f1b59a55 685 if ( ! ng.match(&p->d_remote)) {
71f758e0 686 L<<Logger::Error<<msgPrefix<<"Remote not listed in allow-dnsupdate-from or domainmetadata. Sending REFUSED"<<endl;
f1b59a55
RA
687 return RCode::Refused;
688 }
689
690
691 // Check permissions - TSIG based.
692 vector<string> tsigKeys;
71f758e0 693 B.getDomainMetadata(p->qdomain, "TSIG-ALLOW-DNSUPDATE", tsigKeys);
f1b59a55
RA
694 if (tsigKeys.size() > 0) {
695 bool validKey = false;
914353ca 696
f1b59a55
RA
697 TSIGRecordContent trc;
698 string inputkey, message;
cbd17382 699 if (! p->getTSIGDetails(&trc, &inputkey, 0)) {
f1b59a55
RA
700 L<<Logger::Error<<msgPrefix<<"TSIG key required, but packet does not contain key. Sending REFUSED"<<endl;
701 return RCode::Refused;
702 }
703
6917c321
AT
704 if (p->d_tsig_algo == TSIG_GSS) {
705 GssName inputname(p->d_peer_principal); // match against principal since GSS
706 for(vector<string>::const_iterator key=tsigKeys.begin(); key != tsigKeys.end(); key++) {
707 if (inputname.match(*key)) {
708 validKey = true;
709 break;
710 }
711 }
712 } else {
713 for(vector<string>::const_iterator key=tsigKeys.begin(); key != tsigKeys.end(); key++) {
714 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.
715 validKey=true;
716 break;
717 }
718 }
f1b59a55
RA
719 }
720
721 if (!validKey) {
722 L<<Logger::Error<<msgPrefix<<"TSIG key ("<<inputkey<<") required, but no matching key found in domainmetadata, tried "<<tsigKeys.size()<<". Sending REFUSED"<<endl;
723 return RCode::Refused;
724 }
725 }
726
727 if (tsigKeys.size() == 0 && p->d_havetsig)
728 L<<Logger::Warning<<msgPrefix<<"TSIG is provided, but domain is not secured with TSIG. Processing continues"<<endl;
729
730 // RFC2136 uses the same DNS Header and Message as defined in RFC1035.
914353ca 731 // This means we can use the MOADNSParser to parse the incoming packet. The result is that we have some different
f1b59a55
RA
732 // variable names during the use of our MOADNSParser.
733 MOADNSParser mdp(p->getString());
734 if (mdp.d_header.qdcount != 1) {
735 L<<Logger::Warning<<msgPrefix<<"Zone Count is not 1, sending FormErr"<<endl;
736 return RCode::FormErr;
914353ca 737 }
f1b59a55
RA
738
739 if (p->qtype.getCode() != QType::SOA) { // RFC2136 2.3 - ZTYPE must be SOA
740 L<<Logger::Warning<<msgPrefix<<"Query ZTYPE is not SOA, sending FormErr"<<endl;
741 return RCode::FormErr;
742 }
743
744 if (p->qclass != QClass::IN) {
745 L<<Logger::Warning<<msgPrefix<<"Class is not IN, sending NotAuth"<<endl;
746 return RCode::NotAuth;
747 }
748
749 DomainInfo di;
750 di.backend=0;
751 if(!B.getDomainInfo(p->qdomain, di) || !di.backend) {
71f758e0 752 L<<Logger::Error<<msgPrefix<<"Can't determine backend for domain '"<<p->qdomain<<"' (or backend does not support DNS update operation)"<<endl;
f1b59a55
RA
753 return RCode::NotAuth;
754 }
755
b3148887
RA
756 if (di.kind == DomainInfo::Slave)
757 return forwardPacket(msgPrefix, p, &di);
f1b59a55 758
914353ca 759 // Check if all the records provided are within the zone
f1b59a55
RA
760 for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i != mdp.d_answers.end(); ++i) {
761 const DNSRecord *rr = &i->first;
762 // Skip this check for other field types (like the TSIG - which is in the additional section)
dff32e09 763 // For a TSIG, the label is the dnskey, so it does not pass the endOn validation.
914353ca 764 if (! (rr->d_place == DNSRecord::Answer || rr->d_place == DNSRecord::Nameserver))
f1b59a55
RA
765 continue;
766
767 string label = stripDot(rr->d_label);
768
769 if (!endsOn(label, di.zone)) {
770 L<<Logger::Error<<msgPrefix<<"Received update/record out of zone, sending NotZone."<<endl;
771 return RCode::NotZone;
772 }
773 }
774
dff32e09
RA
775
776 Lock l(&s_rfc2136lock); //TODO: i think this lock can be per zone, not for everything
f1b59a55 777 L<<Logger::Info<<msgPrefix<<"starting transaction."<<endl;
dff32e09 778 if (!di.backend->startTransaction(p->qdomain, -1)) { // Not giving the domain_id means that we do not delete the existing records.
f1b59a55
RA
779 L<<Logger::Error<<msgPrefix<<"Backend for domain "<<p->qdomain<<" does not support transaction. Can't do Update packet."<<endl;
780 return RCode::NotImp;
781 }
782
914353ca 783 // 3.2.1 and 3.2.2 - Prerequisite check
f1b59a55
RA
784 for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i != mdp.d_answers.end(); ++i) {
785 const DNSRecord *rr = &i->first;
786 if (rr->d_place == DNSRecord::Answer) {
787 int res = checkUpdatePrerequisites(rr, &di);
788 if (res>0) {
789 L<<Logger::Error<<msgPrefix<<"Failed PreRequisites check, returning "<<res<<endl;
790 di.backend->abortTransaction();
791 return res;
792 }
914353ca 793 }
f1b59a55
RA
794 }
795
796 // 3.2.3 - Prerequisite check - this is outside of updatePrequisitesCheck because we check an RRSet and not the RR.
797 typedef pair<string, QType> rrSetKey_t;
798 typedef vector<DNSResourceRecord> rrVector_t;
799 typedef std::map<rrSetKey_t, rrVector_t> RRsetMap_t;
800 RRsetMap_t preReqRRsets;
801 for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i != mdp.d_answers.end(); ++i) {
802 const DNSRecord *rr = &i->first;
803 if (rr->d_place == DNSRecord::Answer) {
804 // Last line of 3.2.3
914353ca 805 if (rr->d_class != QClass::IN && rr->d_class != QClass::NONE && rr->d_class != QClass::ANY)
f1b59a55
RA
806 return RCode::FormErr;
807
808 if (rr->d_class == QClass::IN) {
a09f956a 809 rrSetKey_t key = make_pair(stripDot(rr->d_label), QType(rr->d_type));
f1b59a55
RA
810 rrVector_t *vec = &preReqRRsets[key];
811 vec->push_back(DNSResourceRecord(*rr));
812 }
813 }
814 }
815
816 if (preReqRRsets.size() > 0) {
817 RRsetMap_t zoneRRsets;
818 for (RRsetMap_t::iterator preRRSet = preReqRRsets.begin(); preRRSet != preReqRRsets.end(); ++preRRSet) {
819 rrSetKey_t rrSet=preRRSet->first;
820 rrVector_t *vec = &preRRSet->second;
821
822 DNSResourceRecord rec;
823 di.backend->lookup(QType(QType::ANY), rrSet.first);
824 uint16_t foundRR=0, matchRR=0;
825 while (di.backend->get(rec)) {
826 if (rec.qtype == rrSet.second) {
827 foundRR++;
828 for(rrVector_t::iterator rrItem=vec->begin(); rrItem != vec->end(); ++rrItem) {
829 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.
914353ca 830 if (*rrItem == rec)
f1b59a55
RA
831 matchRR++;
832 }
833 }
834 }
835 if (matchRR != foundRR || foundRR != vec->size()) {
836 L<<Logger::Error<<msgPrefix<<"Failed PreRequisites check, returning NXRRSet"<<endl;
837 di.backend->abortTransaction();
838 return RCode::NXRRSet;
839 }
840 }
841 }
842
843
844
0bb68041 845 // 3.4 - Prescan & Add/Update/Delete records - is all done within a try block.
f1b59a55 846 try {
d0d273cf 847 uint changedRecords = 0;
f1b59a55
RA
848 // 3.4.1 - Prescan section
849 for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i != mdp.d_answers.end(); ++i) {
850 const DNSRecord *rr = &i->first;
851 if (rr->d_place == DNSRecord::Nameserver) {
852 int res = checkUpdatePrescan(rr);
853 if (res>0) {
854 L<<Logger::Error<<msgPrefix<<"Failed prescan check, returning "<<res<<endl;
855 di.backend->abortTransaction();
856 return res;
857 }
858 }
859 }
860
861 bool updatedSerial=false;
862 NSEC3PARAMRecordContent ns3pr;
914353ca 863 bool narrow=false;
f1b59a55 864 bool haveNSEC3 = d_dk.getNSEC3PARAM(di.zone, &ns3pr, &narrow);
dff32e09 865 bool isPresigned = d_dk.isPresigned(di.zone);
f1b59a55 866
f1b59a55
RA
867 // 3.4.2 - Perform the updates.
868 // There's a special condition where deleting the last NS record at zone apex is never deleted (3.4.2.4)
7c696097 869 // This means we must do it outside the normal performUpdate() because that focusses only on a separate RR.
f1b59a55
RA
870 vector<const DNSRecord *> nsRRtoDelete;
871 for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i != mdp.d_answers.end(); ++i) {
872 const DNSRecord *rr = &i->first;
873 if (rr->d_place == DNSRecord::Nameserver) {
874 if (rr->d_class == QClass::NONE && rr->d_type == QType::NS && stripDot(rr->d_label) == di.zone)
875 nsRRtoDelete.push_back(rr);
876 else
d0d273cf 877 changedRecords += performUpdate(msgPrefix, rr, &di, isPresigned, &narrow, &haveNSEC3, &ns3pr, &updatedSerial);
f1b59a55
RA
878 }
879 }
880 if (nsRRtoDelete.size()) {
881 vector<DNSResourceRecord> nsRRInZone;
882 DNSResourceRecord rec;
883 di.backend->lookup(QType(QType::NS), di.zone);
884 while (di.backend->get(rec)) {
885 nsRRInZone.push_back(rec);
886 }
887 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)
888 for (vector<DNSResourceRecord>::iterator inZone=nsRRInZone.begin(); inZone != nsRRInZone.end(); inZone++) {
889 for (vector<const DNSRecord *>::iterator rr=nsRRtoDelete.begin(); rr != nsRRtoDelete.end(); rr++) {
890 if (inZone->getZoneRepresentation() == (*rr)->d_content->getZoneRepresentation())
d0d273cf 891 changedRecords += performUpdate(msgPrefix, *rr, &di, isPresigned, &narrow, &haveNSEC3, &ns3pr, &updatedSerial);
f1b59a55
RA
892 }
893 }
894 }
895 }
896
f1b59a55 897 // Section 3.6 - Update the SOA serial - outside of performUpdate because we do a SOA update for the complete update message
d0d273cf 898 if (changedRecords > 0 && !updatedSerial) {
166ac305 899 increaseSerial(msgPrefix, &di, haveNSEC3, narrow, &ns3pr);
d0d273cf 900 changedRecords++;
890c7124
RA
901 }
902
d0d273cf 903 if (changedRecords > 0) {
dce6b818
RA
904 if (!di.backend->commitTransaction()) {
905 L<<Logger::Error<<msgPrefix<<"Failed to commit updates!"<<endl;
906 return RCode::ServFail;
907 }
914353ca 908
71f758e0 909 S.deposit("dnsupdate-changes", changedRecords);
7facf345 910
dce6b818
RA
911 // Purge the records!
912 string zone(di.zone);
913 zone.append("$");
914 PC.purge(zone);
0bb68041 915
7c696097 916 L<<Logger::Info<<msgPrefix<<"Update completed, "<<changedRecords<<" changed records committed."<<endl;
dce6b818
RA
917 } else {
918 //No change, no commit, we perform abort() because some backends might like this more.
919 L<<Logger::Info<<msgPrefix<<"Update completed, 0 changes, rolling back."<<endl;
920 di.backend->abortTransaction();
921 }
0bb68041
RA
922 return RCode::NoError; //rfc 2136 3.4.2.5
923 }
924 catch (SSqlException &e) {
925 L<<Logger::Error<<msgPrefix<<"Caught SSqlException: "<<e.txtReason()<<"; Sending ServFail!"<<endl;
926 di.backend->abortTransaction();
927 return RCode::ServFail;
f1b59a55 928 }
ee9ef8f2
RA
929 catch (DBException &e) {
930 L<<Logger::Error<<msgPrefix<<"Caught DBException: "<<e.reason<<"; Sending ServFail!"<<endl;
931 di.backend->abortTransaction();
932 return RCode::ServFail;
933 }
05ff9dab
RA
934 catch (PDNSException &e) {
935 L<<Logger::Error<<msgPrefix<<"Caught PDNSException: "<<e.reason<<"; Sending ServFail!"<<endl;
f1b59a55
RA
936 di.backend->abortTransaction();
937 return RCode::ServFail;
938 }
28b66a94
KM
939 catch(std::exception &e) {
940 L<<Logger::Error<<msgPrefix<<"Caught std:exception: "<<e.what()<<"; Sending ServFail!"<<endl;
941 di.backend->abortTransaction();
942 return RCode::ServFail;
943 }
f1b59a55
RA
944 catch (...) {
945 L<<Logger::Error<<msgPrefix<<"Caught unknown exception when performing update. Sending ServFail!"<<endl;
946 di.backend->abortTransaction();
947 return RCode::ServFail;
948 }
f1b59a55
RA
949}
950
166ac305 951void PacketHandler::increaseSerial(const string &msgPrefix, const DomainInfo *di, bool haveNSEC3, bool narrow, const NSEC3PARAMRecordContent *ns3pr) {
f1b59a55 952 DNSResourceRecord rec, newRec;
166ac305 953 di->backend->lookup(QType(QType::SOA), di->zone);
f1b59a55 954 bool foundSOA=false;
166ac305 955 while (di->backend->get(rec)) {
f1b59a55
RA
956 newRec = rec;
957 foundSOA=true;
958 }
959 if (!foundSOA) {
05ff9dab 960 throw PDNSException("SOA-Serial update failed because there was no SOA. Wowie.");
f1b59a55
RA
961 }
962 SOAData soa2Update;
963 fillSOAData(rec.content, soa2Update);
890c7124 964 int oldSerial = soa2Update.serial;
f1b59a55 965
e31f9680
RA
966 if (oldSerial == 0) { // using Autoserial, leave the serial alone.
967 L<<Logger::Notice<<msgPrefix<<"AutoSerial being used, not updating SOA serial."<<endl;
968 return;
969 }
970
f1b59a55 971 vector<string> soaEdit2136Setting;
71f758e0 972 B.getDomainMetadata(di->zone, "SOA-EDIT-DNSUPDATE", soaEdit2136Setting);
f1b59a55
RA
973 string soaEdit2136 = "DEFAULT";
974 string soaEdit;
975 if (!soaEdit2136Setting.empty()) {
976 soaEdit2136 = soaEdit2136Setting[0];
977 if (pdns_iequals(soaEdit2136, "SOA-EDIT") || pdns_iequals(soaEdit2136,"SOA-EDIT-INCREASE") ){
978 vector<string> soaEditSetting;
166ac305 979 B.getDomainMetadata(di->zone, "SOA-EDIT", soaEditSetting);
f1b59a55 980 if (soaEditSetting.empty()) {
a6448d95 981 L<<Logger::Error<<msgPrefix<<"Using "<<soaEdit2136<<" for SOA-EDIT-DNSUPDATE increase on DNS update, but SOA-EDIT is not set for domain \""<< di->zone <<"\". Using DEFAULT for SOA-EDIT-DNSUPDATE"<<endl;
f1b59a55
RA
982 soaEdit2136 = "DEFAULT";
983 } else
984 soaEdit = soaEditSetting[0];
985 }
986 }
987
effecfa7 988 soa2Update.serial = calculateIncreaseSOA(soa2Update, soaEdit2136, soaEdit);
f1b59a55
RA
989
990 newRec.content = serializeSOAData(soa2Update);
3e0ab216
RA
991 vector<DNSResourceRecord> rrset;
992 rrset.push_back(newRec);
166ac305 993 di->backend->replaceRRSet(di->id, newRec.qname, newRec.qtype, rrset);
dff32e09 994 L<<Logger::Notice<<msgPrefix<<"Increasing SOA serial ("<<oldSerial<<" -> "<<soa2Update.serial<<")"<<endl;
166ac305
RA
995
996 //Correct ordername + auth flag
c1c3e5a4
RA
997 if (haveNSEC3 && narrow)
998 di->backend->nullifyDNSSECOrderNameAndUpdateAuth(di->id, newRec.qname, true);
999 else if (haveNSEC3) {
166ac305 1000 string hashed;
c1c3e5a4 1001 if (!narrow)
1bad4190 1002 hashed = toBase32Hex(hashQNameWithSalt(ns3pr->d_iterations, ns3pr->d_salt, newRec.qname));
c1c3e5a4 1003
166ac305
RA
1004 di->backend->updateDNSSECOrderAndAuthAbsolute(di->id, newRec.qname, hashed, true);
1005 }
1006 else // NSEC
1007 di->backend->updateDNSSECOrderAndAuth(di->id, di->zone, newRec.qname, true);
166ac305 1008}