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