1 #include "dnswriter.hh"
5 #include "packethandler.hh"
7 #include "dnspacket.hh"
8 #include "auth-caches.hh"
10 #include "dnsseckeeper.hh"
15 #include "arguments.hh"
16 #include "resolver.hh"
17 #include "dns_random.hh"
18 #include "backends/gsql/ssql.hh"
19 #include "communicator.hh"
20 #include "query-local-address.hh"
21 #include "gss_context.hh"
22 #include "auth-main.hh"
25 extern CommunicatorClass Communicator
;
27 std::mutex
PacketHandler::s_rfc2136lock
;
29 // Implement section 3.2.1 and 3.2.2 of RFC2136
30 int PacketHandler::checkUpdatePrerequisites(const DNSRecord
*rr
, DomainInfo
*di
) {
32 return RCode::FormErr
;
34 // 3.2.1 and 3.2.2 check content length.
35 if ( (rr
->d_class
== QClass::NONE
|| rr
->d_class
== QClass::ANY
) && rr
->d_clen
!= 0)
36 return RCode::FormErr
;
38 bool foundRecord
=false;
39 DNSResourceRecord rec
;
40 di
->backend
->lookup(QType(QType::ANY
), rr
->d_name
, di
->id
);
41 while(di
->backend
->get(rec
)) {
42 if (!rec
.qtype
.getCode())
44 if ((rr
->d_type
!= QType::ANY
&& rec
.qtype
== rr
->d_type
) || rr
->d_type
== QType::ANY
)
49 if (rr
->d_class
== QClass::ANY
&& !foundRecord
) {
50 if (rr
->d_type
== QType::ANY
)
51 return RCode::NXDomain
;
52 if (rr
->d_type
!= QType::ANY
)
53 return RCode::NXRRSet
;
57 if (rr
->d_class
== QClass::NONE
&& foundRecord
) {
58 if (rr
->d_type
== QType::ANY
)
59 return RCode::YXDomain
;
60 if (rr
->d_type
!= QType::ANY
)
61 return RCode::YXRRSet
;
64 return RCode::NoError
;
68 // Method implements section 3.4.1 of RFC2136
69 int PacketHandler::checkUpdatePrescan(const DNSRecord
*rr
) {
70 // The RFC stats that d_class != ZCLASS, but we only support the IN class.
71 if (rr
->d_class
!= QClass::IN
&& rr
->d_class
!= QClass::NONE
&& rr
->d_class
!= QClass::ANY
) {
72 return RCode::FormErr
;
75 QType qtype
= QType(rr
->d_type
);
77 if (!qtype
.isSupportedType()) {
78 return RCode::FormErr
;
81 if ((rr
->d_class
== QClass::NONE
|| rr
->d_class
== QClass::ANY
) && rr
->d_ttl
!= 0) {
82 return RCode::FormErr
;
85 if (rr
->d_class
== QClass::ANY
&& rr
->d_clen
!= 0) {
86 return RCode::FormErr
;
89 if (qtype
.isMetadataType()) {
90 return RCode::FormErr
;
93 if (rr
->d_class
!= QClass::ANY
&& qtype
.getCode() == QType::ANY
) {
94 return RCode::FormErr
;
97 return RCode::NoError
;
101 // Implements section 3.4.2 of RFC2136
102 uint
PacketHandler::performUpdate(const string
&msgPrefix
, const DNSRecord
*rr
, DomainInfo
*di
, bool isPresigned
, bool* narrow
, bool* haveNSEC3
, NSEC3PARAMRecordContent
*ns3pr
, bool *updatedSerial
) {
104 QType rrType
= QType(rr
->d_type
);
106 if (rrType
== QType::NSEC
|| rrType
== QType::NSEC3
) {
107 g_log
<<Logger::Warning
<<msgPrefix
<<"Trying to add/update/delete "<<rr
->d_name
<<"|"<<rrType
.toString()<<". These are generated records, ignoring!"<<endl
;
111 if (!isPresigned
&& rrType
== QType::RRSIG
) {
112 g_log
<<Logger::Warning
<<msgPrefix
<<"Trying to add/update/delete "<<rr
->d_name
<<"|"<<rrType
.toString()<<" in non-presigned zone, ignoring!"<<endl
;
116 if ((rrType
== QType::NSEC3PARAM
|| rrType
== QType::DNSKEY
) && rr
->d_name
!= di
->zone
) {
117 g_log
<<Logger::Warning
<<msgPrefix
<<"Trying to add/update/delete "<<rr
->d_name
<<"|"<<rrType
.toString()<<", "<<rrType
.toString()<<" must be at zone apex, ignoring!"<<endl
;
122 uint changedRecords
= 0;
123 DNSResourceRecord rec
;
124 vector
<DNSResourceRecord
> rrset
, recordsToDelete
;
125 set
<DNSName
> delnonterm
, insnonterm
; // used to (at the end) fix ENT records.
128 if (rr
->d_class
== QClass::IN
) { // 3.4.2.2 QClass::IN means insert or update
129 DLOG(g_log
<<msgPrefix
<<"Add/Update record (QClass == IN) "<<rr
->d_name
<<"|"<<rrType
.toString()<<endl
);
131 if (rrType
== QType::NSEC3PARAM
) {
132 g_log
<<Logger::Notice
<<msgPrefix
<<"Adding/updating NSEC3PARAM for zone, resetting ordernames."<<endl
;
134 *ns3pr
= NSEC3PARAMRecordContent(rr
->getContent()->getZoneRepresentation(), di
->zone
);
135 *narrow
= false; // adding a NSEC3 will cause narrow mode to be dropped, as you cannot specify that in a NSEC3PARAM record
136 d_dk
.setNSEC3PARAM(di
->zone
, *ns3pr
, (*narrow
));
141 if (!d_dk
.rectifyZone(di
->zone
, error
, info
, false)) {
142 throw PDNSException("Failed to rectify '" + di
->zone
.toLogString() + "': " + error
);
149 bool foundRecord
= false;
150 di
->backend
->lookup(rrType
, rr
->d_name
, di
->id
);
151 while (di
->backend
->get(rec
)) {
152 rrset
.push_back(rec
);
158 if (rrType
== QType::SOA
) { // SOA updates require the serial to be higher than the current
159 SOAData sdOld
, sdUpdate
;
160 DNSResourceRecord
*oldRec
= &rrset
.front();
161 fillSOAData(oldRec
->content
, sdOld
);
162 oldRec
->setContent(rr
->getContent()->getZoneRepresentation());
163 fillSOAData(oldRec
->content
, sdUpdate
);
164 if (rfc1982LessThan(sdOld
.serial
, sdUpdate
.serial
)) {
165 di
->backend
->replaceRRSet(di
->id
, oldRec
->qname
, oldRec
->qtype
, rrset
);
166 *updatedSerial
= true;
168 g_log
<<Logger::Notice
<<msgPrefix
<<"Replacing SOA record "<<rr
->d_name
<<"|"<<rrType
.toString()<<endl
;
170 g_log
<<Logger::Notice
<<msgPrefix
<<"Provided serial ("<<sdUpdate
.serial
<<") is older than the current serial ("<<sdOld
.serial
<<"), ignoring SOA update."<<endl
;
173 // It's not possible to have multiple CNAME's with the same NAME. So we always update.
174 } else if (rrType
== QType::CNAME
) {
175 int changedCNames
= 0;
176 for (auto& i
: rrset
) {
177 if (i
.ttl
!= rr
->d_ttl
|| i
.content
!= rr
->getContent()->getZoneRepresentation()) {
179 i
.setContent(rr
->getContent()->getZoneRepresentation());
183 if (changedCNames
> 0) {
184 di
->backend
->replaceRRSet(di
->id
, rr
->d_name
, rrType
, rrset
);
185 g_log
<<Logger::Notice
<<msgPrefix
<<"Replacing CNAME record "<<rr
->d_name
<<"|"<<rrType
.toString()<<endl
;
186 changedRecords
+= changedCNames
;
188 g_log
<<Logger::Notice
<<msgPrefix
<<"Replace for CNAME record "<<rr
->d_name
<<"|"<<rrType
.toString()<<" requested, but no changes made."<<endl
;
191 // In any other case, we must check if the TYPE and RDATA match to provide an update (which effectively means a update of TTL)
195 bool lowerCase
= false;
196 if (rrType
.getCode() == QType::PTR
||
197 rrType
.getCode() == QType::MX
||
198 rrType
.getCode() == QType::SRV
) {
201 string content
= rr
->getContent()->getZoneRepresentation();
202 if (lowerCase
) content
= toLower(content
);
203 for (auto& i
: rrset
) {
204 string icontent
= i
.getZoneRepresentation();
205 if (lowerCase
) icontent
= toLower(icontent
);
206 if (rrType
== i
.qtype
.getCode()) {
207 if (icontent
== content
) {
210 if (i
.ttl
!= rr
->d_ttl
) {
217 di
->backend
->replaceRRSet(di
->id
, rr
->d_name
, rrType
, rrset
);
218 g_log
<<Logger::Notice
<<msgPrefix
<<"Updating TTLs for "<<rr
->d_name
<<"|"<<rrType
.toString()<<endl
;
219 changedRecords
+= updateTTL
;
221 g_log
<<Logger::Notice
<<msgPrefix
<<"Replace for recordset "<<rr
->d_name
<<"|"<<rrType
.toString()<<" requested, but no changes made."<<endl
;
225 // ReplaceRRSet dumps our ordername and auth flag, so we need to correct it if we have changed records.
226 // We can take the auth flag from the first RR in the set, as the name is different, so should the auth be.
227 if (changedRecords
> 0) {
228 bool auth
= rrset
.front().auth
;
233 ordername
=DNSName(toBase32Hex(hashQNameWithSalt(*ns3pr
, rr
->d_name
)));
236 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, rr
->d_name
, DNSName(), auth
);
238 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, rr
->d_name
, ordername
, auth
);
239 if(!auth
|| rrType
== QType::DS
) {
240 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, rr
->d_name
, DNSName(), false, QType::NS
);
241 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, rr
->d_name
, DNSName(), false, QType::A
);
242 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, rr
->d_name
, DNSName(), false, QType::AAAA
);
246 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, rr
->d_name
, rr
->d_name
.makeRelative(di
->zone
), auth
);
247 if(!auth
|| rrType
== QType::DS
) {
248 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, rr
->d_name
, DNSName(), false, QType::A
);
249 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, rr
->d_name
, DNSName(), false, QType::AAAA
);
254 } // if (foundRecord)
256 // If we haven't found a record that matches, we must add it.
258 g_log
<<Logger::Notice
<<msgPrefix
<<"Adding record "<<rr
->d_name
<<"|"<<rrType
.toString()<<endl
;
259 delnonterm
.insert(rr
->d_name
); // always remove any ENT's in the place where we're going to add a record.
260 auto newRec
= DNSResourceRecord::fromWire(*rr
);
261 newRec
.domain_id
= di
->id
;
262 newRec
.auth
= (rr
->d_name
== di
->zone
|| rrType
.getCode() != QType::NS
);
263 di
->backend
->feedRecord(newRec
, DNSName());
267 // because we added a record, we need to fix DNSSEC data.
268 DNSName
shorter(rr
->d_name
);
269 bool auth
=newRec
.auth
;
270 bool fixDS
= (rrType
== QType::DS
);
272 if (di
->zone
!= shorter
) { // Everything at APEX is auth=1 && no ENT's
275 if (di
->zone
== shorter
)
278 bool foundShorter
= false;
279 di
->backend
->lookup(QType(QType::ANY
), shorter
, di
->id
);
280 while (di
->backend
->get(rec
)) {
281 if (rec
.qname
== rr
->d_name
&& rec
.qtype
== QType::DS
)
283 if (shorter
!= rr
->d_name
)
285 if (rec
.qtype
== QType::NS
) // are we inserting below a delegate?
289 if (!foundShorter
&& auth
&& shorter
!= rr
->d_name
) // haven't found any record at current level, insert ENT.
290 insnonterm
.insert(shorter
);
292 break; // if we find a shorter record, we can stop searching
293 } while(shorter
.chopOff());
300 ordername
=DNSName(toBase32Hex(hashQNameWithSalt(*ns3pr
, rr
->d_name
)));
303 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, rr
->d_name
, DNSName(), auth
);
305 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, rr
->d_name
, ordername
, auth
);
308 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, rr
->d_name
, ordername
, true, QType::DS
);
313 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, rr
->d_name
, DNSName(), false, QType::NS
);
314 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, rr
->d_name
, DNSName(), false, QType::A
);
315 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, rr
->d_name
, DNSName(), false, QType::AAAA
);
320 DNSName ordername
=rr
->d_name
.makeRelative(di
->zone
);
321 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, rr
->d_name
, ordername
, auth
);
323 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, rr
->d_name
, ordername
, true, QType::DS
);
326 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, rr
->d_name
, DNSName(), false, QType::A
);
327 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, rr
->d_name
, DNSName(), false, QType::AAAA
);
332 // If we insert an NS, all the records below it become non auth - so, we're inserting a delegate.
333 // Auth can only be false when the rr->d_name is not the zone
334 if (auth
== false && rrType
== QType::NS
) {
335 DLOG(g_log
<<msgPrefix
<<"Going to fix auth flags below "<<rr
->d_name
<<endl
);
336 insnonterm
.clear(); // No ENT's are needed below delegates (auth=0)
337 vector
<DNSName
> qnames
;
338 di
->backend
->listSubZone(rr
->d_name
, di
->id
);
339 while(di
->backend
->get(rec
)) {
340 if (rec
.qtype
.getCode() && rec
.qtype
.getCode() != QType::DS
&& rr
->d_name
!= rec
.qname
) // Skip ENT, DS and our already corrected record.
341 qnames
.push_back(rec
.qname
);
343 for(const auto & qname
: qnames
) {
347 ordername
=DNSName(toBase32Hex(hashQNameWithSalt(*ns3pr
, qname
)));
350 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, qname
, DNSName(), auth
);
352 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, qname
, ordername
, auth
);
355 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, qname
, DNSName(), false, QType::NS
);
358 DNSName ordername
=DNSName(qname
).makeRelative(di
->zone
);
359 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, qname
, ordername
, false, QType::NS
);
362 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, qname
, DNSName(), false, QType::A
);
363 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, qname
, DNSName(), false, QType::AAAA
);
367 } // rr->d_class == QClass::IN
370 // 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
371 // the code that calls this performUpdate().
372 if ((rr
->d_class
== QClass::ANY
|| rr
->d_class
== QClass::NONE
) && rrType
!= QType::SOA
) { // never delete a SOA.
373 DLOG(g_log
<<msgPrefix
<<"Deleting records: "<<rr
->d_name
<<"; QClass:"<<rr
->d_class
<<"; rrType: "<<rrType
.toString()<<endl
);
375 if (rrType
== QType::NSEC3PARAM
) {
376 g_log
<<Logger::Notice
<<msgPrefix
<<"Deleting NSEC3PARAM from zone, resetting ordernames."<<endl
;
377 if (rr
->d_class
== QClass::ANY
)
378 d_dk
.unsetNSEC3PARAM(rr
->d_name
);
379 else if (rr
->d_class
== QClass::NONE
) {
380 NSEC3PARAMRecordContent
nsec3rr(rr
->getContent()->getZoneRepresentation(), di
->zone
);
381 if (*haveNSEC3
&& ns3pr
->getZoneRepresentation() == nsec3rr
.getZoneRepresentation())
382 d_dk
.unsetNSEC3PARAM(rr
->d_name
);
388 // Update NSEC3 variables, other RR's in this update package might need them as well.
394 if (!d_dk
.rectifyZone(di
->zone
, error
, info
, false)) {
395 throw PDNSException("Failed to rectify '" + di
->zone
.toLogString() + "': " + error
);
398 } // end of NSEC3PARAM delete block
401 di
->backend
->lookup(rrType
, rr
->d_name
, di
->id
);
402 while(di
->backend
->get(rec
)) {
403 if (rr
->d_class
== QClass::ANY
) { // 3.4.2.3
404 if (rec
.qname
== di
->zone
&& (rec
.qtype
== QType::NS
|| rec
.qtype
== QType::SOA
)) // Never delete all SOA and NS's
405 rrset
.push_back(rec
);
407 recordsToDelete
.push_back(rec
);
409 if (rr
->d_class
== QClass::NONE
) { // 3.4.2.4
410 auto repr
= rec
.getZoneRepresentation();
411 if (rec
.qtype
== QType::TXT
) {
412 DLOG(g_log
<<msgPrefix
<<"Adjusting TXT content from ["<<repr
<<"]"<<endl
);
413 auto drc
= DNSRecordContent::mastermake(rec
.qtype
.getCode(), QClass::IN
, repr
);
414 auto ser
= drc
->serialize(rec
.qname
, true, true);
415 auto rc
= DNSRecordContent::deserialize(rec
.qname
, rec
.qtype
.getCode(), ser
);
416 repr
= rc
->getZoneRepresentation(true);
417 DLOG(g_log
<<msgPrefix
<<"Adjusted TXT content to ["<<repr
<<"]"<<endl
);
419 DLOG(g_log
<<msgPrefix
<<"Matching RR in RRset - (adjusted) representation from request=["<<repr
<<"], rr->getContent()->getZoneRepresentation()=["<<rr
->getContent()->getZoneRepresentation()<<"]"<<endl
);
420 if (rrType
== rec
.qtype
&& repr
== rr
->getContent()->getZoneRepresentation())
421 recordsToDelete
.push_back(rec
);
423 rrset
.push_back(rec
);
427 if (recordsToDelete
.size()) {
428 di
->backend
->replaceRRSet(di
->id
, rr
->d_name
, rrType
, rrset
);
429 g_log
<<Logger::Notice
<<msgPrefix
<<"Deleting record "<<rr
->d_name
<<"|"<<rrType
.toString()<<endl
;
430 changedRecords
+= recordsToDelete
.size();
433 // If we've removed a delegate, we need to reset ordername/auth for some records.
434 if (rrType
== QType::NS
&& rr
->d_name
!= di
->zone
) {
435 vector
<DNSName
> belowOldDelegate
, nsRecs
, updateAuthFlag
;
436 di
->backend
->listSubZone(rr
->d_name
, di
->id
);
437 while (di
->backend
->get(rec
)) {
438 if (rec
.qtype
.getCode()) // skip ENT records, they are always auth=false
439 belowOldDelegate
.push_back(rec
.qname
);
440 if (rec
.qtype
.getCode() == QType::NS
&& rec
.qname
!= rr
->d_name
)
441 nsRecs
.push_back(rec
.qname
);
444 for(auto &belowOldDel
: belowOldDelegate
)
446 bool isBelowDelegate
= false;
447 for(const auto & ns
: nsRecs
) {
448 if (ns
.isPartOf(belowOldDel
)) {
449 isBelowDelegate
=true;
453 if (!isBelowDelegate
)
454 updateAuthFlag
.push_back(belowOldDel
);
457 for (const auto &changeRec
:updateAuthFlag
) {
461 ordername
=DNSName(toBase32Hex(hashQNameWithSalt(*ns3pr
, changeRec
)));
463 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, changeRec
, ordername
, true);
466 DNSName ordername
=changeRec
.makeRelative(di
->zone
);
467 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, changeRec
, ordername
, true);
473 // We must check if we have a record below the current level and if we removed the 'last' record
474 // on that level. If so, we must insert an ENT record.
475 // 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.
476 bool foundDeeper
= false, foundOtherWithSameName
= false;
477 di
->backend
->listSubZone(rr
->d_name
, di
->id
);
478 while (di
->backend
->get(rec
)) {
479 if (rec
.qname
== rr
->d_name
&& !count(recordsToDelete
.begin(), recordsToDelete
.end(), rec
))
480 foundOtherWithSameName
= true;
481 if (rec
.qname
!= rr
->d_name
&& 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
485 if (foundDeeper
&& !foundOtherWithSameName
) {
486 insnonterm
.insert(rr
->d_name
);
487 } else if (!foundOtherWithSameName
) {
488 // If we didn't have to insert an ENT, we might have deleted a record at very deep level
489 // and we must then clean up the ENT's above the deleted record.
490 DNSName
shorter(rr
->d_name
);
491 while (shorter
!= di
->zone
) {
493 bool foundRealRR
= false;
494 bool foundEnt
= false;
496 // The reason for a listSubZone here is because might go up the tree and find the ENT of another branch
497 // consider these non ENT-records:
500 // 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.
501 // At that point we can stop deleting ENT's because the tree is in tact again.
502 di
->backend
->listSubZone(shorter
, di
->id
);
504 while (di
->backend
->get(rec
)) {
505 if (rec
.qtype
.getCode())
511 if (foundEnt
) // only delete the ENT if we actually found one.
512 delnonterm
.insert(shorter
);
517 } else { // if (recordsToDelete.size())
518 g_log
<<Logger::Notice
<<msgPrefix
<<"Deletion for record "<<rr
->d_name
<<"|"<<rrType
.toString()<<" requested, but not found."<<endl
;
520 } // (End of delete block d_class == ANY || d_class == NONE
524 //Insert and delete ENT's
525 if (insnonterm
.size() > 0 || delnonterm
.size() > 0) {
526 DLOG(g_log
<<msgPrefix
<<"Updating ENT records - "<<insnonterm
.size()<<"|"<<delnonterm
.size()<<endl
);
527 di
->backend
->updateEmptyNonTerminals(di
->id
, insnonterm
, delnonterm
, false);
528 for (const auto &i
: insnonterm
) {
534 ordername
=DNSName(toBase32Hex(hashQNameWithSalt(*ns3pr
, i
)));
535 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, i
, ordername
, true);
540 return changedRecords
;
543 int PacketHandler::forwardPacket(const string
&msgPrefix
, const DNSPacket
& p
, const DomainInfo
& di
) {
544 vector
<string
> forward
;
545 B
.getDomainMetadata(p
.qdomain
, "FORWARD-DNSUPDATE", forward
);
547 if (forward
.size() == 0 && ! ::arg().mustDo("forward-dnsupdate")) {
548 g_log
<<Logger::Notice
<<msgPrefix
<<"Not configured to forward to master, returning Refused."<<endl
;
549 return RCode::Refused
;
552 for(const auto& remote
: di
.masters
) {
553 g_log
<<Logger::Notice
<<msgPrefix
<<"Forwarding packet to master "<<remote
<<endl
;
555 if (!pdns::isQueryLocalAddressFamilyEnabled(remote
.sin4
.sin_family
)) {
558 auto local
= pdns::getQueryLocalAddress(remote
.sin4
.sin_family
, 0);
559 int sock
= makeQuerySocket(local
, false); // create TCP socket. RFC2136 section 6.2 seems to be ok with this.
561 g_log
<<Logger::Error
<<msgPrefix
<<"Error creating socket: "<<stringerror()<<endl
;
565 if( connect(sock
, (struct sockaddr
*)&remote
, remote
.getSocklen()) < 0 ) {
566 g_log
<<Logger::Error
<<msgPrefix
<<"Failed to connect to "<<remote
.toStringWithPort()<<": "<<stringerror()<<endl
;
570 catch(const PDNSException
& e
) {
571 g_log
<<Logger::Error
<<"Error closing master forwarding socket after connect() failed: "<<e
.reason
<<endl
;
576 DNSPacket
l_forwardPacket(p
);
577 l_forwardPacket
.setID(dns_random_uint16());
578 l_forwardPacket
.setRemote(&remote
);
579 uint16_t len
=htons(l_forwardPacket
.getString().length());
580 string
buffer((const char*)&len
, 2);
581 buffer
.append(l_forwardPacket
.getString());
582 if(write(sock
, buffer
.c_str(), buffer
.length()) < 0) {
583 g_log
<<Logger::Error
<<msgPrefix
<<"Unable to forward update message to "<<remote
.toStringWithPort()<<", error:"<<stringerror()<<endl
;
587 catch(const PDNSException
& e
) {
588 g_log
<<Logger::Error
<<"Error closing master forwarding socket after write() failed: "<<e
.reason
<<endl
;
593 int res
= waitForData(sock
, 10, 0);
595 g_log
<<Logger::Error
<<msgPrefix
<<"Timeout waiting for reply from master at "<<remote
.toStringWithPort()<<endl
;
599 catch(const PDNSException
& e
) {
600 g_log
<<Logger::Error
<<"Error closing master forwarding socket after a timeout occurred: "<<e
.reason
<<endl
;
605 g_log
<<Logger::Error
<<msgPrefix
<<"Error waiting for answer from master at "<<remote
.toStringWithPort()<<", error:"<<stringerror()<<endl
;
609 catch(const PDNSException
& e
) {
610 g_log
<<Logger::Error
<<"Error closing master forwarding socket after an error occurred: "<<e
.reason
<<endl
;
615 unsigned char lenBuf
[2];
617 recvRes
= recv(sock
, &lenBuf
, sizeof(lenBuf
), 0);
618 if (recvRes
< 0 || static_cast<size_t>(recvRes
) < sizeof(lenBuf
)) {
619 g_log
<<Logger::Error
<<msgPrefix
<<"Could not receive data (length) from master at "<<remote
.toStringWithPort()<<", error:"<<stringerror()<<endl
;
623 catch(const PDNSException
& e
) {
624 g_log
<<Logger::Error
<<"Error closing master forwarding socket after recv() failed: "<<e
.reason
<<endl
;
628 size_t packetLen
= lenBuf
[0]*256+lenBuf
[1];
630 buffer
.resize(packetLen
);
631 recvRes
= recv(sock
, &buffer
.at(0), packetLen
, 0);
633 g_log
<<Logger::Error
<<msgPrefix
<<"Could not receive data (dnspacket) from master at "<<remote
.toStringWithPort()<<", error:"<<stringerror()<<endl
;
637 catch(const PDNSException
& e
) {
638 g_log
<<Logger::Error
<<"Error closing master forwarding socket after recv() failed: "<<e
.reason
<<endl
;
645 catch(const PDNSException
& e
) {
646 g_log
<<Logger::Error
<<"Error closing master forwarding socket: "<<e
.reason
<<endl
;
650 MOADNSParser
mdp(false, buffer
.data(), static_cast<unsigned int>(recvRes
));
651 g_log
<<Logger::Info
<<msgPrefix
<<"Forward update message to "<<remote
.toStringWithPort()<<", result was RCode "<<mdp
.d_header
.rcode
<<endl
;
652 return mdp
.d_header
.rcode
;
655 g_log
<<Logger::Error
<<msgPrefix
<<"Failed to parse response packet from master at "<<remote
.toStringWithPort()<<endl
;
659 g_log
<<Logger::Error
<<msgPrefix
<<"Failed to forward packet to master(s). Returning ServFail."<<endl
;
660 return RCode::ServFail
;
664 int PacketHandler::processUpdate(DNSPacket
& p
) {
665 if (! ::arg().mustDo("dnsupdate"))
666 return RCode::Refused
;
668 string msgPrefix
="UPDATE (" + std::to_string(p
.d
.id
) + ") from " + p
.getRemoteString() + " for " + p
.qdomain
.toLogString() + ": ";
669 g_log
<<Logger::Info
<<msgPrefix
<<"Processing started."<<endl
;
671 // if there is policy, we delegate all checks to it
672 if (this->d_update_policy_lua
== nullptr) {
674 // Check permissions - IP based
675 vector
<string
> allowedRanges
;
676 B
.getDomainMetadata(p
.qdomain
, "ALLOW-DNSUPDATE-FROM", allowedRanges
);
677 if (! ::arg()["allow-dnsupdate-from"].empty())
678 stringtok(allowedRanges
, ::arg()["allow-dnsupdate-from"], ", \t" );
681 for(const auto& i
: allowedRanges
) {
685 if ( ! ng
.match(p
.getInnerRemote())) {
686 g_log
<<Logger::Error
<<msgPrefix
<<"Remote not listed in allow-dnsupdate-from or domainmetadata. Sending REFUSED"<<endl
;
687 return RCode::Refused
;
691 // Check permissions - TSIG based.
692 vector
<string
> tsigKeys
;
693 B
.getDomainMetadata(p
.qdomain
, "TSIG-ALLOW-DNSUPDATE", tsigKeys
);
694 if (tsigKeys
.size() > 0) {
695 bool validKey
= false;
697 TSIGRecordContent trc
;
700 if (! p
.getTSIGDetails(&trc
, &inputkey
)) {
701 g_log
<<Logger::Error
<<msgPrefix
<<"TSIG key required, but packet does not contain key. Sending REFUSED"<<endl
;
702 return RCode::Refused
;
704 #ifdef ENABLE_GSS_TSIG
705 if (g_doGssTSIG
&& p
.d_tsig_algo
== TSIG_GSS
) {
706 GssName
inputname(p
.d_peer_principal
); // match against principal since GSS requires that
707 for(const auto& key
: tsigKeys
) {
708 if (inputname
.match(key
)) {
717 for(const auto& key
: tsigKeys
) {
718 if (inputkey
== DNSName(key
)) { // because checkForCorrectTSIG has already been performed earlier on, if the name of the key matches with the domain given it is valid.
726 g_log
<<Logger::Error
<<msgPrefix
<<"TSIG key ("<<inputkey
<<") required, but no matching key found in domainmetadata, tried "<<tsigKeys
.size()<<". Sending REFUSED"<<endl
;
727 return RCode::Refused
;
731 if (tsigKeys
.size() == 0 && p
.d_havetsig
)
732 g_log
<<Logger::Warning
<<msgPrefix
<<"TSIG is provided, but domain is not secured with TSIG. Processing continues"<<endl
;
736 // RFC2136 uses the same DNS Header and Message as defined in RFC1035.
737 // This means we can use the MOADNSParser to parse the incoming packet. The result is that we have some different
738 // variable names during the use of our MOADNSParser.
739 MOADNSParser
mdp(false, p
.getString());
740 if (mdp
.d_header
.qdcount
!= 1) {
741 g_log
<<Logger::Warning
<<msgPrefix
<<"Zone Count is not 1, sending FormErr"<<endl
;
742 return RCode::FormErr
;
745 if (p
.qtype
.getCode() != QType::SOA
) { // RFC2136 2.3 - ZTYPE must be SOA
746 g_log
<<Logger::Warning
<<msgPrefix
<<"Query ZTYPE is not SOA, sending FormErr"<<endl
;
747 return RCode::FormErr
;
750 if (p
.qclass
!= QClass::IN
) {
751 g_log
<<Logger::Warning
<<msgPrefix
<<"Class is not IN, sending NotAuth"<<endl
;
752 return RCode::NotAuth
;
757 if(!B
.getDomainInfo(p
.qdomain
, di
) || !di
.backend
) {
758 g_log
<<Logger::Error
<<msgPrefix
<<"Can't determine backend for domain '"<<p
.qdomain
<<"' (or backend does not support DNS update operation)"<<endl
;
759 return RCode::NotAuth
;
762 if (di
.kind
== DomainInfo::Slave
)
763 return forwardPacket(msgPrefix
, p
, di
);
765 // Check if all the records provided are within the zone
766 for(const auto & answer
: mdp
.d_answers
) {
767 const DNSRecord
*rr
= &answer
.first
;
768 // Skip this check for other field types (like the TSIG - which is in the additional section)
769 // For a TSIG, the label is the dnskey, so it does not pass the endOn validation.
770 if (! (rr
->d_place
== DNSResourceRecord::ANSWER
|| rr
->d_place
== DNSResourceRecord::AUTHORITY
))
773 if (!rr
->d_name
.isPartOf(di
.zone
)) {
774 g_log
<<Logger::Error
<<msgPrefix
<<"Received update/record out of zone, sending NotZone."<<endl
;
775 return RCode::NotZone
;
780 std::lock_guard
<std::mutex
> l(s_rfc2136lock
); //TODO: i think this lock can be per zone, not for everything
781 g_log
<<Logger::Info
<<msgPrefix
<<"starting transaction."<<endl
;
782 if (!di
.backend
->startTransaction(p
.qdomain
, -1)) { // Not giving the domain_id means that we do not delete the existing records.
783 g_log
<<Logger::Error
<<msgPrefix
<<"Backend for domain "<<p
.qdomain
<<" does not support transaction. Can't do Update packet."<<endl
;
784 return RCode::NotImp
;
787 // 3.2.1 and 3.2.2 - Prerequisite check
788 for(const auto & answer
: mdp
.d_answers
) {
789 const DNSRecord
*rr
= &answer
.first
;
790 if (rr
->d_place
== DNSResourceRecord::ANSWER
) {
791 int res
= checkUpdatePrerequisites(rr
, &di
);
793 g_log
<<Logger::Error
<<msgPrefix
<<"Failed PreRequisites check for "<<rr
->d_name
<<", returning "<<RCode::to_s(res
)<<endl
;
794 di
.backend
->abortTransaction();
800 // 3.2.3 - Prerequisite check - this is outside of updatePrerequisitesCheck because we check an RRSet and not the RR.
801 typedef pair
<DNSName
, QType
> rrSetKey_t
;
802 typedef vector
<DNSResourceRecord
> rrVector_t
;
803 typedef std::map
<rrSetKey_t
, rrVector_t
> RRsetMap_t
;
804 RRsetMap_t preReqRRsets
;
805 for(const auto& i
: mdp
.d_answers
) {
806 const DNSRecord
* rr
= &i
.first
;
807 if (rr
->d_place
== DNSResourceRecord::ANSWER
) {
808 // Last line of 3.2.3
809 if (rr
->d_class
!= QClass::IN
&& rr
->d_class
!= QClass::NONE
&& rr
->d_class
!= QClass::ANY
)
810 return RCode::FormErr
;
812 if (rr
->d_class
== QClass::IN
) {
813 rrSetKey_t key
= {rr
->d_name
, QType(rr
->d_type
)};
814 rrVector_t
*vec
= &preReqRRsets
[key
];
815 vec
->push_back(DNSResourceRecord::fromWire(*rr
));
820 if (preReqRRsets
.size() > 0) {
821 RRsetMap_t zoneRRsets
;
822 for (auto & preReqRRset
: preReqRRsets
) {
823 rrSetKey_t rrSet
=preReqRRset
.first
;
824 rrVector_t
*vec
= &preReqRRset
.second
;
826 DNSResourceRecord rec
;
827 di
.backend
->lookup(QType(QType::ANY
), rrSet
.first
, di
.id
);
828 uint16_t foundRR
=0, matchRR
=0;
829 while (di
.backend
->get(rec
)) {
830 if (rec
.qtype
== rrSet
.second
) {
832 for(auto & rrItem
: *vec
) {
833 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.
839 if (matchRR
!= foundRR
|| foundRR
!= vec
->size()) {
840 g_log
<<Logger::Error
<<msgPrefix
<<"Failed PreRequisites check (RRs differ), returning NXRRSet"<<endl
;
841 di
.backend
->abortTransaction();
842 return RCode::NXRRSet
;
849 // 3.4 - Prescan & Add/Update/Delete records - is all done within a try block.
851 uint changedRecords
= 0;
852 // 3.4.1 - Prescan section
853 for(const auto & answer
: mdp
.d_answers
) {
854 const DNSRecord
*rr
= &answer
.first
;
855 if (rr
->d_place
== DNSResourceRecord::AUTHORITY
) {
856 int res
= checkUpdatePrescan(rr
);
858 g_log
<<Logger::Error
<<msgPrefix
<<"Failed prescan check, returning "<<res
<<endl
;
859 di
.backend
->abortTransaction();
865 bool updatedSerial
=false;
866 NSEC3PARAMRecordContent ns3pr
;
868 bool haveNSEC3
= d_dk
.getNSEC3PARAM(di
.zone
, &ns3pr
, &narrow
);
869 bool isPresigned
= d_dk
.isPresigned(di
.zone
);
870 string soaEditSetting
;
871 d_dk
.getSoaEdit(di
.zone
, soaEditSetting
);
873 // 3.4.2 - Perform the updates.
874 // There's a special condition where deleting the last NS record at zone apex is never deleted (3.4.2.4)
875 // This means we must do it outside the normal performUpdate() because that focusses only on a separate RR.
876 vector
<const DNSRecord
*> nsRRtoDelete
;
878 // Another special case is the addition of both a CNAME and a non-CNAME for the same name (#6270)
879 set
<DNSName
> cn
, nocn
;
880 for (const auto &rr
: mdp
.d_answers
) {
881 if (rr
.first
.d_place
== DNSResourceRecord::AUTHORITY
&& rr
.first
.d_class
== QClass::IN
&& rr
.first
.d_ttl
> 0) {
883 if (rr
.first
.d_type
== QType::CNAME
) {
884 cn
.insert(rr
.first
.d_name
);
885 } else if (rr
.first
.d_type
!= QType::RRSIG
) {
886 nocn
.insert(rr
.first
.d_name
);
890 for (auto const &n
: cn
) {
891 if (nocn
.count(n
) > 0) {
892 g_log
<<Logger::Error
<<msgPrefix
<<"Refusing update, found CNAME and non-CNAME addition"<<endl
;
893 di
.backend
->abortTransaction();
894 return RCode::FormErr
;
898 vector
<const DNSRecord
*> cnamesToAdd
, nonCnamesToAdd
;
899 for(const auto & answer
: mdp
.d_answers
) {
900 const DNSRecord
*rr
= &answer
.first
;
901 if (rr
->d_place
== DNSResourceRecord::AUTHORITY
) {
902 /* see if it's permitted by policy */
903 if (this->d_update_policy_lua
!= nullptr) {
904 if (this->d_update_policy_lua
->updatePolicy(rr
->d_name
, QType(rr
->d_type
), di
.zone
, p
) == false) {
905 g_log
<<Logger::Warning
<<msgPrefix
<<"Refusing update for " << rr
->d_name
<< "/" << QType(rr
->d_type
).toString() << ": Not permitted by policy"<<endl
;
908 g_log
<<Logger::Debug
<<msgPrefix
<<"Accepting update for " << rr
->d_name
<< "/" << QType(rr
->d_type
).toString() << ": Permitted by policy"<<endl
;
912 if (rr
->d_class
== QClass::NONE
&& rr
->d_type
== QType::NS
&& rr
->d_name
== di
.zone
)
913 nsRRtoDelete
.push_back(rr
);
914 else if (rr
->d_class
== QClass::IN
&& rr
->d_ttl
> 0) {
915 if (rr
->d_type
== QType::CNAME
) {
916 cnamesToAdd
.push_back(rr
);
918 nonCnamesToAdd
.push_back(rr
);
922 changedRecords
+= performUpdate(msgPrefix
, rr
, &di
, isPresigned
, &narrow
, &haveNSEC3
, &ns3pr
, &updatedSerial
);
925 for (const auto &rr
: cnamesToAdd
) {
926 DNSResourceRecord rec
;
927 di
.backend
->lookup(QType(QType::ANY
), rr
->d_name
, di
.id
);
928 while (di
.backend
->get(rec
)) {
929 if (rec
.qtype
!= QType::CNAME
&& rec
.qtype
!= QType::ENT
&& rec
.qtype
!= QType::RRSIG
) {
930 // leave database handle in a consistent state
931 while (di
.backend
->get(rec
))
933 g_log
<<Logger::Warning
<<msgPrefix
<<"Refusing update for " << rr
->d_name
<< "/" << QType(rr
->d_type
).toString() << ": Data other than CNAME exists for the same name"<<endl
;
934 di
.backend
->abortTransaction();
935 return RCode::Refused
;
938 changedRecords
+= performUpdate(msgPrefix
, rr
, &di
, isPresigned
, &narrow
, &haveNSEC3
, &ns3pr
, &updatedSerial
);
940 for (const auto &rr
: nonCnamesToAdd
) {
941 DNSResourceRecord rec
;
942 di
.backend
->lookup(QType(QType::CNAME
), rr
->d_name
, di
.id
);
943 while (di
.backend
->get(rec
)) {
944 if (rec
.qtype
== QType::CNAME
&& rr
->d_type
!= QType::RRSIG
) {
945 // leave database handle in a consistent state
946 while (di
.backend
->get(rec
))
948 g_log
<<Logger::Warning
<<msgPrefix
<<"Refusing update for " << rr
->d_name
<< "/" << QType(rr
->d_type
).toString() << ": CNAME exists for the same name"<<endl
;
949 di
.backend
->abortTransaction();
950 return RCode::Refused
;
953 changedRecords
+= performUpdate(msgPrefix
, rr
, &di
, isPresigned
, &narrow
, &haveNSEC3
, &ns3pr
, &updatedSerial
);
955 if (nsRRtoDelete
.size()) {
956 vector
<DNSResourceRecord
> nsRRInZone
;
957 DNSResourceRecord rec
;
958 di
.backend
->lookup(QType(QType::NS
), di
.zone
, di
.id
);
959 while (di
.backend
->get(rec
)) {
960 nsRRInZone
.push_back(rec
);
962 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)
963 for (auto& inZone
: nsRRInZone
) {
964 for (auto& rr
: nsRRtoDelete
) {
965 if (inZone
.getZoneRepresentation() == (rr
)->getContent()->getZoneRepresentation())
966 changedRecords
+= performUpdate(msgPrefix
, rr
, &di
, isPresigned
, &narrow
, &haveNSEC3
, &ns3pr
, &updatedSerial
);
972 // Section 3.6 - Update the SOA serial - outside of performUpdate because we do a SOA update for the complete update message
973 if (changedRecords
> 0 && !updatedSerial
) {
974 increaseSerial(msgPrefix
, &di
, soaEditSetting
, haveNSEC3
, narrow
, &ns3pr
);
978 if (changedRecords
> 0) {
979 if (!di
.backend
->commitTransaction()) {
980 g_log
<<Logger::Error
<<msgPrefix
<<"Failed to commit updates!"<<endl
;
981 return RCode::ServFail
;
984 S
.deposit("dnsupdate-changes", changedRecords
);
986 d_dk
.clearMetaCache(di
.zone
);
987 // Purge the records!
988 string
zone(di
.zone
.toString());
990 purgeAuthCaches(zone
);
993 if (di
.kind
== DomainInfo::Master
) {
994 vector
<string
> notify
;
995 B
.getDomainMetadata(p
.qdomain
, "NOTIFY-DNSUPDATE", notify
);
996 if (!notify
.empty() && notify
.front() == "1") {
997 Communicator
.notifyDomain(di
.zone
, &B
);
1001 g_log
<<Logger::Info
<<msgPrefix
<<"Update completed, "<<changedRecords
<<" changed records committed."<<endl
;
1003 //No change, no commit, we perform abort() because some backends might like this more.
1004 g_log
<<Logger::Info
<<msgPrefix
<<"Update completed, 0 changes, rolling back."<<endl
;
1005 di
.backend
->abortTransaction();
1007 return RCode::NoError
; //rfc 2136 3.4.2.5
1009 catch (SSqlException
&e
) {
1010 g_log
<<Logger::Error
<<msgPrefix
<<"Caught SSqlException: "<<e
.txtReason()<<"; Sending ServFail!"<<endl
;
1011 di
.backend
->abortTransaction();
1012 return RCode::ServFail
;
1014 catch (DBException
&e
) {
1015 g_log
<<Logger::Error
<<msgPrefix
<<"Caught DBException: "<<e
.reason
<<"; Sending ServFail!"<<endl
;
1016 di
.backend
->abortTransaction();
1017 return RCode::ServFail
;
1019 catch (PDNSException
&e
) {
1020 g_log
<<Logger::Error
<<msgPrefix
<<"Caught PDNSException: "<<e
.reason
<<"; Sending ServFail!"<<endl
;
1021 di
.backend
->abortTransaction();
1022 return RCode::ServFail
;
1024 catch(std::exception
&e
) {
1025 g_log
<<Logger::Error
<<msgPrefix
<<"Caught std:exception: "<<e
.what()<<"; Sending ServFail!"<<endl
;
1026 di
.backend
->abortTransaction();
1027 return RCode::ServFail
;
1030 g_log
<<Logger::Error
<<msgPrefix
<<"Caught unknown exception when performing update. Sending ServFail!"<<endl
;
1031 di
.backend
->abortTransaction();
1032 return RCode::ServFail
;
1036 void PacketHandler::increaseSerial(const string
&msgPrefix
, const DomainInfo
*di
, const string
& soaEditSetting
, bool haveNSEC3
, bool narrow
, const NSEC3PARAMRecordContent
*ns3pr
) {
1038 if (!di
->backend
->getSOA(di
->zone
, sd
)) {
1039 throw PDNSException("SOA-Serial update failed because there was no SOA. Wowie.");
1042 uint32_t oldSerial
= sd
.serial
;
1044 vector
<string
> soaEdit2136Setting
;
1045 B
.getDomainMetadata(di
->zone
, "SOA-EDIT-DNSUPDATE", soaEdit2136Setting
);
1046 string soaEdit2136
= "DEFAULT";
1048 if (!soaEdit2136Setting
.empty()) {
1049 soaEdit2136
= soaEdit2136Setting
[0];
1050 if (pdns_iequals(soaEdit2136
, "SOA-EDIT") || pdns_iequals(soaEdit2136
,"SOA-EDIT-INCREASE") ){
1051 if (soaEditSetting
.empty()) {
1052 g_log
<<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
;
1053 soaEdit2136
= "DEFAULT";
1055 soaEdit
= soaEditSetting
;
1059 DNSResourceRecord rr
;
1060 if (makeIncreasedSOARecord(sd
, soaEdit2136
, soaEdit
, rr
)) {
1061 di
->backend
->replaceRRSet(di
->id
, rr
.qname
, rr
.qtype
, vector
<DNSResourceRecord
>(1, rr
));
1062 g_log
<< Logger::Notice
<< msgPrefix
<< "Increasing SOA serial (" << oldSerial
<< " -> " << sd
.serial
<< ")" << endl
;
1064 //Correct ordername + auth flag
1068 ordername
= DNSName(toBase32Hex(hashQNameWithSalt(*ns3pr
, rr
.qname
)));
1070 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, rr
.qname
, ordername
, true);
1072 DNSName ordername
= rr
.qname
.makeRelative(di
->zone
);
1073 di
->backend
->updateDNSSECOrderNameAndAuth(di
->id
, rr
.qname
, ordername
, true);