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