2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2002-2012 PowerDNS.COM BV
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2
7 as published by the Free Software Foundation
9 Additionally, the license of this program contains a special
10 exception which allows to distribute the program in binary form when
11 it is linked against OpenSSL.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 #include <boost/algorithm/string.hpp>
26 #include <boost/scoped_array.hpp>
27 #include "auth-packetcache.hh"
29 #include "threadname.hh"
30 #include "dnssecinfra.hh"
31 #include "dnsseckeeper.hh"
36 #include <sys/types.h>
37 #include <netinet/tcp.h>
40 #include "tcpreceiver.hh"
46 #include "ueberbackend.hh"
47 #include "dnspacket.hh"
48 #include "nameserver.hh"
49 #include "distributor.hh"
52 #include "arguments.hh"
54 #include "auth-main.hh"
55 #include "packethandler.hh"
57 #include "communicator.hh"
58 #include "namespaces.hh"
59 #include "signingpipe.hh"
60 #include "stubresolver.hh"
61 #include "proxy-protocol.hh"
62 #include "noinitvector.hh"
63 #include "gss_context.hh"
64 #include "pdnsexception.hh"
65 extern AuthPacketCache PC
;
70 \brief This file implements the tcpreceiver that receives and answers questions over TCP/IP
73 std::unique_ptr
<Semaphore
> TCPNameserver::d_connectionroom_sem
{nullptr};
74 LockGuarded
<std::unique_ptr
<PacketHandler
>> TCPNameserver::s_P
{nullptr};
75 unsigned int TCPNameserver::d_maxTCPConnections
= 0;
76 NetmaskGroup
TCPNameserver::d_ng
;
77 size_t TCPNameserver::d_maxTransactionsPerConn
;
78 size_t TCPNameserver::d_maxConnectionsPerClient
;
79 unsigned int TCPNameserver::d_idleTimeout
;
80 unsigned int TCPNameserver::d_maxConnectionDuration
;
81 LockGuarded
<std::map
<ComboAddress
,size_t,ComboAddress::addressOnlyLessThan
>> TCPNameserver::s_clientsCount
;
83 void TCPNameserver::go()
85 g_log
<<Logger::Error
<<"Creating backend connection for TCP"<<endl
;
88 *(s_P
.lock()) = make_unique
<PacketHandler
>();
90 catch(PDNSException
&ae
) {
91 g_log
<<Logger::Error
<<"TCP server is unable to launch backends - will try again when questions come in: "<<ae
.reason
<<endl
;
94 std::thread
th([this](){thread();});
98 // throws PDNSException if things didn't go according to plan, returns 0 if really 0 bytes were read
99 static int readnWithTimeout(int fd
, void* buffer
, unsigned int n
, unsigned int idleTimeout
, bool throwOnEOF
=true, unsigned int totalTimeout
=0)
101 unsigned int bytes
=n
;
102 char *ptr
= (char*)buffer
;
105 unsigned int remainingTotal
= totalTimeout
;
107 start
= time(nullptr);
110 ret
=read(fd
, ptr
, bytes
);
113 ret
=waitForData(fd
, (totalTimeout
== 0 || idleTimeout
<= remainingTotal
) ? idleTimeout
: remainingTotal
);
115 throw NetworkError("Waiting for data read");
117 throw NetworkError("Timeout reading data");
121 throw NetworkError("Reading data: "+stringerror());
124 if(!throwOnEOF
&& n
== bytes
)
127 throw NetworkError("Did not fulfill read from TCP due to EOF");
133 time_t now
= time(nullptr);
134 const auto elapsed
= now
- start
;
135 if (elapsed
>= static_cast<time_t>(remainingTotal
)) {
136 throw NetworkError("Timeout while reading data");
140 remainingTotal
-= elapsed
;
148 static void writenWithTimeout(int fd
, const void *buffer
, unsigned int n
, unsigned int idleTimeout
)
150 unsigned int bytes
=n
;
151 const char *ptr
= (char*)buffer
;
154 ret
=write(fd
, ptr
, bytes
);
157 ret
=waitForRWData(fd
, false, idleTimeout
, 0);
159 throw NetworkError("Waiting for data write");
161 throw NetworkError("Timeout writing data");
165 throw NetworkError("Writing data: "+stringerror());
168 throw NetworkError("Did not fulfill TCP write due to EOF");
176 void TCPNameserver::sendPacket(std::unique_ptr
<DNSPacket
>& p
, int outsock
, bool last
)
178 uint16_t len
=htons(p
->getString(true).length());
180 // this also calls p->getString; call it after our explicit call so throwsOnTruncation=true is honoured
181 g_rs
.submitResponse(*p
, false, last
);
183 string
buffer((const char*)&len
, 2);
184 buffer
.append(p
->getString());
185 writenWithTimeout(outsock
, buffer
.c_str(), buffer
.length(), d_idleTimeout
);
189 void TCPNameserver::getQuestion(int fd
, char *mesg
, int pktlen
, const ComboAddress
&remote
, unsigned int totalTime
)
192 readnWithTimeout(fd
, mesg
, pktlen
, d_idleTimeout
, true, totalTime
);
194 catch(NetworkError
& ae
) {
195 throw NetworkError("Error reading DNS data from TCP client "+remote
.toString()+": "+ae
.what());
198 static bool maxConnectionDurationReached(unsigned int maxConnectionDuration
, time_t start
, unsigned int& remainingTime
)
200 if (maxConnectionDuration
) {
201 time_t elapsed
= time(nullptr) - start
;
202 if (elapsed
>= maxConnectionDuration
) {
206 remainingTime
= static_cast<unsigned int>(maxConnectionDuration
- elapsed
);
212 void TCPNameserver::decrementClientCount(const ComboAddress
& remote
)
214 if (d_maxConnectionsPerClient
) {
215 auto count
= s_clientsCount
.lock();
216 auto it
= count
->find(remote
);
217 if (it
== count
->end()) {
218 // this is worrying, but nothing we can do at this point
222 if (it
->second
== 0) {
228 void TCPNameserver::doConnection(int fd
)
230 setThreadName("pdns/tcpConnect");
231 std::unique_ptr
<DNSPacket
> packet
;
232 ComboAddress remote
, accountremote
;
233 socklen_t remotelen
=sizeof(remote
);
234 size_t transactions
= 0;
236 if (d_maxConnectionDuration
) {
237 start
= time(nullptr);
240 if(getpeername(fd
, (struct sockaddr
*)&remote
, &remotelen
) < 0) {
241 g_log
<<Logger::Warning
<<"Received question from socket which had no remote address, dropping ("<<stringerror()<<")"<<endl
;
242 d_connectionroom_sem
->post();
246 catch(const PDNSException
& e
) {
247 g_log
<<Logger::Error
<<"Error closing TCP socket: "<<e
.reason
<<endl
;
255 boost::scoped_array
<char> mesg(new char[mesgsize
]);
256 std::optional
<ComboAddress
> inner_remote
;
257 bool inner_tcp
= false;
259 DLOG(g_log
<<"TCP Connection accepted on fd "<<fd
<<endl
);
260 bool logDNSQueries
= ::arg().mustDo("log-dns-queries");
261 if (g_proxyProtocolACL
.match(remote
)) {
262 unsigned int remainingTime
= 0;
263 PacketBuffer proxyData
;
264 proxyData
.reserve(g_proxyProtocolMaximumSize
);
267 // this for-loop ends by throwing, or by having gathered a complete proxy header
269 used
= isProxyHeaderComplete(proxyData
);
271 ssize_t origsize
= proxyData
.size();
272 proxyData
.resize(origsize
+ -used
);
273 if (maxConnectionDurationReached(d_maxConnectionDuration
, start
, remainingTime
)) {
274 throw NetworkError("Error reading PROXYv2 header from TCP client "+remote
.toString()+": maximum TCP connection duration exceeded");
278 readnWithTimeout(fd
, &proxyData
[origsize
], -used
, d_idleTimeout
, true, remainingTime
);
280 catch(NetworkError
& ae
) {
281 throw NetworkError("Error reading PROXYv2 header from TCP client "+remote
.toString()+": "+ae
.what());
284 else if (used
== 0) {
285 throw NetworkError("Error reading PROXYv2 header from TCP client "+remote
.toString()+": PROXYv2 header was invalid");
287 else if (static_cast<size_t>(used
) > g_proxyProtocolMaximumSize
) {
288 throw NetworkError("Error reading PROXYv2 header from TCP client "+remote
.toString()+": PROXYv2 header too big");
290 else { // used > 0 && used <= g_proxyProtocolMaximumSize
294 ComboAddress psource
, pdestination
;
295 bool proxyProto
, tcp
;
296 std::vector
<ProxyProtocolValue
> ppvalues
;
298 used
= parseProxyHeader(proxyData
, proxyProto
, psource
, pdestination
, tcp
, ppvalues
);
300 throw NetworkError("Error reading PROXYv2 header from TCP client "+remote
.toString()+": PROXYv2 header was invalid");
302 if (static_cast<size_t>(used
) > g_proxyProtocolMaximumSize
) {
303 throw NetworkError("Error reading PROXYv2 header from TCP client "+remote
.toString()+": PROXYv2 header was oversized");
305 inner_remote
= psource
;
307 accountremote
= psource
;
310 accountremote
= remote
;
314 unsigned int remainingTime
= 0;
316 if (d_maxTransactionsPerConn
&& transactions
> d_maxTransactionsPerConn
) {
317 g_log
<< Logger::Notice
<<"TCP Remote "<< remote
<<" exceeded the number of transactions per connection, dropping.";
320 if (maxConnectionDurationReached(d_maxConnectionDuration
, start
, remainingTime
)) {
321 g_log
<< Logger::Notice
<<"TCP Remote "<< remote
<<" exceeded the maximum TCP connection duration, dropping.";
326 if(!readnWithTimeout(fd
, &pktlen
, 2, d_idleTimeout
, false, remainingTime
))
329 pktlen
=ntohs(pktlen
);
331 // this check will always be false *if* no one touches
332 // the mesg array. pktlen can be maximum of 65535 as
333 // it is 2 byte unsigned variable. In getQuestion, we
334 // write to 0 up to pktlen-1 so 65535 is just right.
336 // do not remove this check as it will catch if someone
337 // decreases the mesg buffer size for some reason.
338 if(pktlen
> mesgsize
) {
339 g_log
<<Logger::Warning
<<"Received an overly large question from "<<remote
.toString()<<", dropping"<<endl
;
343 if (maxConnectionDurationReached(d_maxConnectionDuration
, start
, remainingTime
)) {
344 g_log
<< Logger::Notice
<<"TCP Remote "<< remote
<<" exceeded the maximum TCP connection duration, dropping.";
348 getQuestion(fd
, mesg
.get(), pktlen
, remote
, remainingTime
);
349 S
.inc("tcp-queries");
350 if (accountremote
.sin4
.sin_family
== AF_INET6
)
351 S
.inc("tcp6-queries");
353 S
.inc("tcp4-queries");
355 packet
=make_unique
<DNSPacket
>(true);
356 packet
->setRemote(&remote
);
359 packet
->d_inner_remote
= inner_remote
;
360 packet
->d_tcp
= inner_tcp
;
362 packet
->setSocket(fd
);
363 if(packet
->parse(mesg
.get(), pktlen
)<0)
366 if (packet
->hasEDNSCookie())
367 S
.inc("tcp-cookie-queries");
369 if(packet
->qtype
.getCode()==QType::AXFR
) {
371 doAXFR(packet
->qdomain
, packet
, fd
);
375 if(packet
->qtype
.getCode()==QType::IXFR
) {
381 std::unique_ptr
<DNSPacket
> reply
;
382 auto cached
= make_unique
<DNSPacket
>(false);
384 g_log
<< Logger::Notice
<<"TCP Remote "<< packet
->getRemoteString() <<" wants '" << packet
->qdomain
<<"|"<<packet
->qtype
.toString() <<
385 "', do = " <<packet
->d_dnssecOk
<<", bufsize = "<< packet
->getMaxReplyLen();
389 if(packet
->couldBeCached() && PC
.get(*packet
, *cached
)) { // short circuit - does the PacketCache recognize this question?
391 g_log
<<": packetcache HIT"<<endl
;
392 cached
->setRemote(&packet
->d_remote
);
393 cached
->d_inner_remote
= packet
->d_inner_remote
;
394 cached
->d
.id
=packet
->d
.id
;
395 cached
->d
.rd
=packet
->d
.rd
; // copy in recursion desired bit
396 cached
->commitD(); // commit d to the packet inlined
398 sendPacket(cached
, fd
); // presigned, don't do it again
402 g_log
<<": packetcache MISS"<<endl
;
409 auto packetHandler
= s_P
.lock();
410 if (!*packetHandler
) {
411 g_log
<<Logger::Warning
<<"TCP server is without backend connections, launching"<<endl
;
412 *packetHandler
= make_unique
<PacketHandler
>();
415 reply
= (*packetHandler
)->doQuestion(*packet
); // we really need to ask the backend :-)
418 if(!reply
) // unable to write an answer?
421 sendPacket(reply
, fd
);
422 #ifdef ENABLE_GSS_TSIG
424 packet
->cleanupGSS(reply
->d
.rcode
);
429 catch(PDNSException
&ae
) {
430 s_P
.lock()->reset(); // on next call, backend will be recycled
431 g_log
<< Logger::Error
<< "TCP Connection Thread for client " << remote
<< " failed, cycling backend: " << ae
.reason
<< endl
;
433 catch(NetworkError
&e
) {
434 g_log
<< Logger::Info
<< "TCP Connection Thread for client " << remote
<< " died because of network error: " << e
.what() << endl
;
437 catch(std::exception
&e
) {
438 s_P
.lock()->reset(); // on next call, backend will be recycled
439 g_log
<< Logger::Error
<< "TCP Connection Thread for client " << remote
<< " died because of STL error, cycling backend: " << e
.what() << endl
;
443 s_P
.lock()->reset(); // on next call, backend will be recycled
444 g_log
<< Logger::Error
<< "TCP Connection Thread for client " << remote
<< " caught unknown exception, cycling backend." << endl
;
446 d_connectionroom_sem
->post();
451 catch(const PDNSException
& e
) {
452 g_log
<< Logger::Error
<< "Error closing TCP socket for client " << remote
<< ": " << e
.reason
<< endl
;
454 decrementClientCount(remote
);
458 bool TCPNameserver::canDoAXFR(std::unique_ptr
<DNSPacket
>& q
, bool isAXFR
, std::unique_ptr
<PacketHandler
>& packetHandler
)
460 if(::arg().mustDo("disable-axfr"))
463 string logPrefix
=string(isAXFR
? "A" : "I")+"XFR-out zone '"+q
->qdomain
.toLogString()+"', client '"+q
->getInnerRemote().toStringWithPort()+"', ";
465 if(q
->d_havetsig
) { // if you have one, it must be good
466 TSIGRecordContent trc
;
469 if(!q
->checkForCorrectTSIG(packetHandler
->getBackend(), &keyname
, &secret
, &trc
)) {
472 getTSIGHashEnum(trc
.d_algoName
, q
->d_tsig_algo
);
473 #ifdef ENABLE_GSS_TSIG
474 if (g_doGssTSIG
&& q
->d_tsig_algo
== TSIG_GSS
) {
475 GssContext
gssctx(keyname
);
476 if (!gssctx
.getPeerPrincipal(q
->d_peer_principal
)) {
477 g_log
<<Logger::Warning
<<"Failed to extract peer principal from GSS context with keyname '"<<keyname
<<"'"<<endl
;
483 DNSSECKeeper
dk(packetHandler
->getBackend());
484 #ifdef ENABLE_GSS_TSIG
485 if (g_doGssTSIG
&& q
->d_tsig_algo
== TSIG_GSS
) {
486 vector
<string
> princs
;
487 packetHandler
->getBackend()->getDomainMetadata(q
->qdomain
, "GSS-ALLOW-AXFR-PRINCIPAL", princs
);
488 for(const std::string
& princ
: princs
) {
489 if (q
->d_peer_principal
== princ
) {
490 g_log
<<Logger::Warning
<<"AXFR of domain '"<<q
->qdomain
<<"' allowed: TSIG signed request with authorized principal '"<<q
->d_peer_principal
<<"' and algorithm 'gss-tsig'"<<endl
;
494 g_log
<<Logger::Warning
<<"AXFR of domain '"<<q
->qdomain
<<"' denied: TSIG signed request with principal '"<<q
->d_peer_principal
<<"' and algorithm 'gss-tsig' is not permitted"<<endl
;
498 if(!dk
.TSIGGrantsAccess(q
->qdomain
, keyname
)) {
499 g_log
<<Logger::Warning
<<logPrefix
<<"denied: key with name '"<<keyname
<<"' and algorithm '"<<getTSIGAlgoName(q
->d_tsig_algo
)<<"' does not grant access"<<endl
;
503 g_log
<<Logger::Notice
<<logPrefix
<<"allowed: TSIG signed request with authorized key '"<<keyname
<<"' and algorithm '"<<getTSIGAlgoName(q
->d_tsig_algo
)<<"'"<<endl
;
508 // cerr<<"checking allow-axfr-ips"<<endl;
509 if(!(::arg()["allow-axfr-ips"].empty()) && d_ng
.match( q
->getInnerRemote() )) {
510 g_log
<<Logger::Notice
<<logPrefix
<<"allowed: client IP is in allow-axfr-ips"<<endl
;
516 // cerr<<"doing per-zone-axfr-acls"<<endl;
518 if(packetHandler
->getBackend()->getSOAUncached(q
->qdomain
,sd
)) {
519 // cerr<<"got backend and SOA"<<endl;
521 packetHandler
->getBackend()->getDomainMetadata(q
->qdomain
, "ALLOW-AXFR-FROM", acl
);
522 for (const auto & i
: acl
) {
523 // cerr<<"matching against "<<*i<<endl;
524 if(pdns_iequals(i
, "AUTO-NS")) {
525 // cerr<<"AUTO-NS magic please!"<<endl;
527 DNSResourceRecord rr
;
530 sd
.db
->lookup(QType(QType::NS
), q
->qdomain
, sd
.domain_id
);
531 while (sd
.db
->get(rr
)) {
532 nsset
.insert(DNSName(rr
.content
));
534 for(const auto & j
: nsset
) {
535 vector
<string
> nsips
=fns
.lookup(j
, packetHandler
->getBackend());
536 for(const auto & nsip
: nsips
) {
537 // cerr<<"got "<<*k<<" from AUTO-NS"<<endl;
538 if(nsip
== q
->getInnerRemote().toString())
540 // cerr<<"got AUTO-NS hit"<<endl;
541 g_log
<<Logger::Notice
<<logPrefix
<<"allowed: client IP is in NSset"<<endl
;
549 Netmask nm
= Netmask(i
);
550 if(nm
.match( q
->getInnerRemote() ))
552 g_log
<<Logger::Notice
<<logPrefix
<<"allowed: client IP is in per-zone ACL"<<endl
;
553 // cerr<<"hit!"<<endl;
560 extern CommunicatorClass Communicator
;
562 if(Communicator
.justNotified(q
->qdomain
, q
->getInnerRemote().toString())) { // we just notified this ip
563 g_log
<<Logger::Notice
<<logPrefix
<<"allowed: client IP is from recently notified secondary"<<endl
;
567 g_log
<<Logger::Warning
<<logPrefix
<<"denied: client IP has no permission"<<endl
;
579 static std::unique_ptr
<DNSPacket
> getFreshAXFRPacket(std::unique_ptr
<DNSPacket
>& q
)
581 std::unique_ptr
<DNSPacket
> ret
= std::unique_ptr
<DNSPacket
>(q
->replyPacket());
582 ret
->setCompress(false);
583 ret
->d_dnssecOk
=false; // RFC 5936, 2.2.5
590 /** do the actual zone transfer. Return 0 in case of error, 1 in case of success */
591 int TCPNameserver::doAXFR(const DNSName
&target
, std::unique_ptr
<DNSPacket
>& q
, int outsock
) // NOLINT(readability-function-cognitive-complexity)
593 string logPrefix
="AXFR-out zone '"+target
.toLogString()+"', client '"+q
->getRemoteStringWithPort()+"', ";
595 std::unique_ptr
<DNSPacket
> outpacket
= getFreshAXFRPacket(q
);
597 outpacket
->d_dnssecOk
=true; // RFC 5936, 2.2.5 'SHOULD'
599 g_log
<<Logger::Warning
<<logPrefix
<<"transfer initiated"<<endl
;
601 // determine if zone exists and AXFR is allowed using existing backend before spawning a new backend.
604 auto packetHandler
= s_P
.lock();
605 DLOG(g_log
<<logPrefix
<<"looking for SOA"<<endl
); // find domain_id via SOA and list complete domain. No SOA, no AXFR
606 if(!*packetHandler
) {
607 g_log
<<Logger::Warning
<<"TCP server is without backend connections in doAXFR, launching"<<endl
;
608 *packetHandler
= make_unique
<PacketHandler
>();
611 // canDoAXFR does all the ACL checks, and has the if(disable-axfr) shortcut, call it first.
612 if (!canDoAXFR(q
, true, *packetHandler
)) {
613 g_log
<<Logger::Warning
<<logPrefix
<<"failed: client may not request AXFR"<<endl
;
614 outpacket
->setRcode(RCode::NotAuth
);
615 sendPacket(outpacket
,outsock
);
619 if (!(*packetHandler
)->getBackend()->getSOAUncached(target
, sd
)) {
620 g_log
<<Logger::Warning
<<logPrefix
<<"failed: not authoritative"<<endl
;
621 outpacket
->setRcode(RCode::NotAuth
);
622 sendPacket(outpacket
,outsock
);
628 if(!db
.getSOAUncached(target
, sd
)) {
629 g_log
<<Logger::Warning
<<logPrefix
<<"failed: not authoritative in second instance"<<endl
;
630 outpacket
->setRcode(RCode::NotAuth
);
631 sendPacket(outpacket
,outsock
);
635 bool securedZone
= false;
636 bool presignedZone
= false;
637 bool NSEC3Zone
= false;
641 bool isCatalogZone
= sd
.db
->getDomainInfo(target
, di
, false) && di
.isCatalogType();
643 NSEC3PARAMRecordContent ns3pr
;
645 DNSSECKeeper
dk(&db
);
646 DNSSECKeeper::clearCaches(target
);
647 if (!isCatalogZone
) {
648 securedZone
= dk
.isSecuredZone(target
);
649 presignedZone
= dk
.isPresigned(target
);
652 if(securedZone
&& dk
.getNSEC3PARAM(target
, &ns3pr
, &narrow
)) {
655 g_log
<<Logger::Warning
<<logPrefix
<<"failed: not doing AXFR of an NSEC3 narrow zone"<<endl
;
656 outpacket
->setRcode(RCode::Refused
);
657 sendPacket(outpacket
,outsock
);
662 TSIGRecordContent trc
;
666 bool haveTSIGDetails
= q
->getTSIGDetails(&trc
, &tsigkeyname
);
668 if(haveTSIGDetails
&& !tsigkeyname
.empty()) {
670 DNSName algorithm
=trc
.d_algoName
;
671 if (algorithm
== DNSName("hmac-md5.sig-alg.reg.int"))
672 algorithm
= DNSName("hmac-md5");
673 if (algorithm
!= DNSName("gss-tsig")) {
674 if(!db
.getTSIGKey(tsigkeyname
, algorithm
, tsig64
)) {
675 g_log
<<Logger::Warning
<<logPrefix
<<"TSIG key not found"<<endl
;
678 if (B64Decode(tsig64
, tsigsecret
) == -1) {
679 g_log
<<Logger::Error
<<logPrefix
<<"unable to Base-64 decode TSIG key '"<<tsigkeyname
<<"'"<<endl
;
686 // SOA *must* go out first, our signing pipe might reorder
687 DLOG(g_log
<<logPrefix
<<"sending out SOA"<<endl
);
688 DNSZoneRecord soa
= makeEditedDNSZRFromSOAData(dk
, sd
);
689 outpacket
->addRecord(DNSZoneRecord(soa
));
690 if(securedZone
&& !presignedZone
) {
691 set
<DNSName
> authSet
;
692 authSet
.insert(target
);
693 addRRSigs(dk
, db
, authSet
, outpacket
->getRRS());
696 if(haveTSIGDetails
&& !tsigkeyname
.empty())
697 outpacket
->setTSIGDetails(trc
, tsigkeyname
, tsigsecret
, trc
.d_mac
); // first answer is 'normal'
699 sendPacket(outpacket
, outsock
, false);
701 trc
.d_mac
= outpacket
->d_trc
.d_mac
;
702 outpacket
= getFreshAXFRPacket(q
);
706 vector
<DNSZoneRecord
> zrrs
;
708 zrr
.dr
.d_name
= target
;
709 zrr
.dr
.d_ttl
= sd
.minimum
;
711 if(securedZone
&& !presignedZone
) { // this is where the DNSKEYs, CDNSKEYs and CDSs go in
712 bool doCDNSKEY
= true, doCDS
= true;
713 string publishCDNSKEY
, publishCDS
;
714 dk
.getPublishCDNSKEY(q
->qdomain
, publishCDNSKEY
);
715 dk
.getPublishCDS(q
->qdomain
, publishCDS
);
717 set
<uint32_t> entryPointIds
;
718 DNSSECKeeper::keyset_t entryPoints
= dk
.getEntryPoints(target
);
719 for (auto const& value
: entryPoints
) {
720 entryPointIds
.insert(value
.second
.id
);
723 DNSSECKeeper::keyset_t keys
= dk
.getKeys(target
);
724 for(const DNSSECKeeper::keyset_t::value_type
& value
: keys
) {
725 if (!value
.second
.published
) {
728 zrr
.dr
.d_type
= QType::DNSKEY
;
729 zrr
.dr
.setContent(std::make_shared
<DNSKEYRecordContent
>(value
.first
.getDNSKEY()));
730 DNSName keyname
= NSEC3Zone
? DNSName(toBase32Hex(hashQNameWithSalt(ns3pr
, zrr
.dr
.d_name
))) : zrr
.dr
.d_name
;
733 // generate CDS and CDNSKEY records
734 if(doCDNSKEY
&& entryPointIds
.count(value
.second
.id
) > 0){
735 if(!publishCDNSKEY
.empty()) {
736 zrr
.dr
.d_type
=QType::CDNSKEY
;
737 if (publishCDNSKEY
== "0") {
739 zrr
.dr
.setContent(PacketHandler::s_deleteCDNSKEYContent
);
742 zrr
.dr
.setContent(std::make_shared
<DNSKEYRecordContent
>(value
.first
.getDNSKEY()));
747 if(doCDS
&& !publishCDS
.empty()){
748 zrr
.dr
.d_type
=QType::CDS
;
749 vector
<string
> digestAlgos
;
750 stringtok(digestAlgos
, publishCDS
, ", ");
751 if(std::find(digestAlgos
.begin(), digestAlgos
.end(), "0") != digestAlgos
.end()) {
753 zrr
.dr
.setContent(PacketHandler::s_deleteCDSContent
);
756 for(auto const &digestAlgo
: digestAlgos
) {
757 zrr
.dr
.setContent(std::make_shared
<DSRecordContent
>(makeDSFromDNSKey(target
, value
.first
.getDNSKEY(), pdns::checked_stoi
<uint8_t>(digestAlgo
))));
767 if(NSEC3Zone
) { // now stuff in the NSEC3PARAM
768 uint8_t flags
= ns3pr
.d_flags
;
769 zrr
.dr
.d_type
= QType::NSEC3PARAM
;
771 zrr
.dr
.setContent(std::make_shared
<NSEC3PARAMRecordContent
>(ns3pr
));
772 ns3pr
.d_flags
= flags
;
773 DNSName keyname
= DNSName(toBase32Hex(hashQNameWithSalt(ns3pr
, zrr
.dr
.d_name
)));
777 const bool rectify
= !(presignedZone
|| ::arg().mustDo("disable-axfr-rectify"));
778 set
<DNSName
> qnames
, nsset
, terms
;
780 // Catalog zone start
781 if (di
.kind
== DomainInfo::Producer
) {
782 // Ignore all records except NS at apex
783 sd
.db
->lookup(QType::NS
, target
, di
.id
);
784 while (sd
.db
->get(zrr
)) {
785 zrrs
.emplace_back(zrr
);
788 zrr
.dr
.d_name
= target
;
790 zrr
.dr
.d_type
= QType::NS
;
791 zrr
.dr
.setContent(std::make_shared
<NSRecordContent
>("invalid."));
792 zrrs
.emplace_back(zrr
);
795 zrrs
.emplace_back(CatalogInfo::getCatalogVersionRecord(target
));
797 vector
<CatalogInfo
> members
;
798 sd
.db
->getCatalogMembers(target
, members
, CatalogInfo::CatalogType::Producer
);
799 for (const auto& ci
: members
) {
800 ci
.toDNSZoneRecords(target
, zrrs
);
802 if (members
.empty()) {
803 g_log
<< Logger::Warning
<< logPrefix
<< "catalog zone '" << target
<< "' has no members" << endl
;
809 // now start list zone
810 if (!sd
.db
->list(target
, sd
.domain_id
, isCatalogZone
)) {
811 g_log
<<Logger::Error
<<logPrefix
<<"backend signals error condition, aborting AXFR"<<endl
;
812 outpacket
->setRcode(RCode::ServFail
);
813 sendPacket(outpacket
,outsock
);
817 while(sd
.db
->get(zrr
)) {
818 if (!presignedZone
) {
819 if (zrr
.dr
.d_type
== QType::RRSIG
) {
822 if (zrr
.dr
.d_type
== QType::DNSKEY
|| zrr
.dr
.d_type
== QType::CDNSKEY
|| zrr
.dr
.d_type
== QType::CDS
) {
823 if(!::arg().mustDo("direct-dnskey")) {
826 zrr
.dr
.d_ttl
= sd
.minimum
;
830 zrr
.dr
.d_name
.makeUsLowerCase();
831 if(zrr
.dr
.d_name
.isPartOf(target
)) {
832 if (zrr
.dr
.d_type
== QType::ALIAS
&& (::arg().mustDo("outgoing-axfr-expand-alias") || ::arg()["outgoing-axfr-expand-alias"] == "ignore-errors")) {
833 vector
<DNSZoneRecord
> ips
;
834 int ret1
= stubDoResolve(getRR
<ALIASRecordContent
>(zrr
.dr
)->getContent(), QType::A
, ips
);
835 int ret2
= stubDoResolve(getRR
<ALIASRecordContent
>(zrr
.dr
)->getContent(), QType::AAAA
, ips
);
836 if (ret1
!= RCode::NoError
|| ret2
!= RCode::NoError
) {
837 if (::arg()["outgoing-axfr-expand-alias"] == "ignore-errors") {
838 if (ret1
!= RCode::NoError
) {
839 g_log
<< Logger::Error
<< logPrefix
<< zrr
.dr
.d_name
.toLogString() << ": error resolving A record for ALIAS target " << zrr
.dr
.getContent()->getZoneRepresentation() << ", continuing AXFR" << endl
;
841 if (ret2
!= RCode::NoError
) {
842 g_log
<< Logger::Error
<< logPrefix
<< zrr
.dr
.d_name
.toLogString() << ": error resolving AAAA record for ALIAS target " << zrr
.dr
.getContent()->getZoneRepresentation() << ", continuing AXFR" << endl
;
846 g_log
<< Logger::Warning
<< logPrefix
<< zrr
.dr
.d_name
.toLogString() << ": error resolving for ALIAS " << zrr
.dr
.getContent()->getZoneRepresentation() << ", aborting AXFR" << endl
;
847 outpacket
->setRcode(RCode::ServFail
);
848 sendPacket(outpacket
, outsock
);
852 for (auto& ip
: ips
) {
853 zrr
.dr
.d_type
= ip
.dr
.d_type
;
854 zrr
.dr
.setContent(ip
.dr
.getContent());
862 qnames
.insert(zrr
.dr
.d_name
);
863 if(zrr
.dr
.d_type
== QType::NS
&& zrr
.dr
.d_name
!=target
)
864 nsset
.insert(zrr
.dr
.d_name
);
866 // remove existing ents
873 g_log
<<Logger::Warning
<<logPrefix
<<"zone contains out-of-zone data '"<<zrr
.dr
.d_name
<<"|"<<DNSRecordContent::NumberToType(zrr
.dr
.d_type
)<<"', ignoring"<<endl
;
877 for (auto& loopRR
: zrrs
) {
878 if ((loopRR
.dr
.d_type
== QType::SVCB
|| loopRR
.dr
.d_type
== QType::HTTPS
)) {
879 // Process auto hints
880 // TODO this is an almost copy of the code in the packethandler
881 auto rrc
= getRR
<SVCBBaseRecordContent
>(loopRR
.dr
);
882 if (rrc
== nullptr) {
885 auto newRRC
= rrc
->clone();
889 DNSName svcTarget
= newRRC
->getTarget().isRoot() ? loopRR
.dr
.d_name
: newRRC
->getTarget();
890 if (newRRC
->autoHint(SvcParam::ipv4hint
)) {
891 sd
.db
->lookup(QType::A
, svcTarget
, sd
.domain_id
);
892 vector
<ComboAddress
> hints
;
894 while (sd
.db
->get(rr
)) {
895 auto arrc
= getRR
<ARecordContent
>(rr
.dr
);
896 hints
.push_back(arrc
->getCA());
898 if (hints
.size() == 0) {
899 newRRC
->removeParam(SvcParam::ipv4hint
);
901 newRRC
->setHints(SvcParam::ipv4hint
, hints
);
905 if (newRRC
->autoHint(SvcParam::ipv6hint
)) {
906 sd
.db
->lookup(QType::AAAA
, svcTarget
, sd
.domain_id
);
907 vector
<ComboAddress
> hints
;
909 while (sd
.db
->get(rr
)) {
910 auto arrc
= getRR
<AAAARecordContent
>(rr
.dr
);
911 hints
.push_back(arrc
->getCA());
913 if (hints
.size() == 0) {
914 newRRC
->removeParam(SvcParam::ipv6hint
);
916 newRRC
->setHints(SvcParam::ipv6hint
, hints
);
920 loopRR
.dr
.setContent(std::move(newRRC
));
924 // Group records by name and type, signpipe stumbles over interrupted rrsets
925 if(securedZone
&& !presignedZone
) {
926 sort(zrrs
.begin(), zrrs
.end(), [](const DNSZoneRecord
& a
, const DNSZoneRecord
& b
) {
927 return std::tie(a
.dr
.d_name
, a
.dr
.d_type
) < std::tie(b
.dr
.d_name
, b
.dr
.d_type
);
933 for(DNSZoneRecord
&loopZRR
: zrrs
) {
935 if (loopZRR
.dr
.d_type
!= QType::NS
|| loopZRR
.dr
.d_name
!=target
) {
936 DNSName
shorter(loopZRR
.dr
.d_name
);
938 if (shorter
==target
) // apex is always auth
940 if(nsset
.count(shorter
) && !(loopZRR
.dr
.d_name
==shorter
&& loopZRR
.dr
.d_type
== QType::DS
)) {
944 } while(shorter
.chopOff());
949 // ents are only required for NSEC3 zones
950 uint32_t maxent
= ::arg().asNum("max-ent-entries");
951 set
<DNSName
> nsec3set
, nonterm
;
952 for (auto &loopZRR
: zrrs
) {
954 DNSName shorter
= loopZRR
.dr
.d_name
;
955 if (shorter
!= target
&& shorter
.chopOff() && shorter
!= target
) {
957 if(nsset
.count(shorter
)) {
961 } while(shorter
.chopOff() && shorter
!= target
);
963 shorter
= loopZRR
.dr
.d_name
;
964 if(!skip
&& (loopZRR
.dr
.d_type
!= QType::NS
|| !ns3pr
.d_flags
)) {
966 if(!nsec3set
.count(shorter
)) {
967 nsec3set
.insert(shorter
);
969 } while(shorter
!= target
&& shorter
.chopOff());
973 for(DNSZoneRecord
&loopZRR
: zrrs
) {
974 DNSName
shorter(loopZRR
.dr
.d_name
);
975 while(shorter
!= target
&& shorter
.chopOff()) {
976 if(!qnames
.count(shorter
) && !nonterm
.count(shorter
) && nsec3set
.count(shorter
)) {
978 g_log
<<Logger::Warning
<<logPrefix
<<"zone has too many empty non terminals, aborting AXFR"<<endl
;
979 outpacket
->setRcode(RCode::ServFail
);
980 sendPacket(outpacket
,outsock
);
983 nonterm
.insert(shorter
);
989 for(const auto& nt
: nonterm
) {
990 DNSZoneRecord tempRR
;
992 tempRR
.dr
.d_type
=QType::ENT
;
994 zrrs
.push_back(tempRR
);
1001 /* now write all other records */
1003 typedef map
<DNSName
, NSECXEntry
, CanonDNSNameCompare
> nsecxrepo_t
;
1004 nsecxrepo_t nsecxrepo
;
1006 ChunkedSigningPipe
csp(target
, (securedZone
&& !presignedZone
), ::arg().asNum("signing-threads", 1), ::arg().mustDo("workaround-11804") ? 1 : 100);
1012 for(DNSZoneRecord
&loopZRR
: zrrs
) {
1013 if(securedZone
&& (loopZRR
.auth
|| loopZRR
.dr
.d_type
== QType::NS
)) {
1014 if (NSEC3Zone
|| loopZRR
.dr
.d_type
) {
1015 if (presignedZone
&& NSEC3Zone
&& loopZRR
.dr
.d_type
== QType::RRSIG
&& getRR
<RRSIGRecordContent
>(loopZRR
.dr
)->d_type
== QType::NSEC3
) {
1016 keyname
= loopZRR
.dr
.d_name
.makeRelative(sd
.qname
);
1018 keyname
= NSEC3Zone
? DNSName(toBase32Hex(hashQNameWithSalt(ns3pr
, loopZRR
.dr
.d_name
))) : loopZRR
.dr
.d_name
;
1020 NSECXEntry
& ne
= nsecxrepo
[keyname
];
1021 ne
.d_ttl
= sd
.getNegativeTTL();
1022 ne
.d_auth
= (ne
.d_auth
|| loopZRR
.auth
|| (NSEC3Zone
&& (!ns3pr
.d_flags
)));
1023 if (loopZRR
.dr
.d_type
&& loopZRR
.dr
.d_type
!= QType::RRSIG
) {
1024 ne
.d_set
.set(loopZRR
.dr
.d_type
);
1029 if (!loopZRR
.dr
.d_type
)
1030 continue; // skip empty non-terminals
1032 if(loopZRR
.dr
.d_type
== QType::SOA
)
1033 continue; // skip SOA - would indicate end of AXFR
1035 if(csp
.submit(loopZRR
)) {
1037 outpacket
->getRRS() = csp
.getChunk();
1038 if(!outpacket
->getRRS().empty()) {
1039 if(haveTSIGDetails
&& !tsigkeyname
.empty())
1040 outpacket
->setTSIGDetails(trc
, tsigkeyname
, tsigsecret
, trc
.d_mac
, true);
1041 sendPacket(outpacket
, outsock
, false);
1042 trc
.d_mac
=outpacket
->d_trc
.d_mac
;
1043 outpacket
=getFreshAXFRPacket(q
);
1051 udiff=dt.udiffNoReset();
1052 cerr<<"Starting NSEC: "<<csp.d_signed/(udiff/1000000.0)<<" sigs/s, "<<csp.d_signed<<" / "<<udiff/1000000.0<<endl;
1053 cerr<<"Outstanding: "<<csp.d_outstanding<<", "<<csp.d_queued - csp.d_signed << endl;
1054 cerr<<"Ready for consumption: "<<csp.getReady()<<endl;
1058 for(nsecxrepo_t::const_iterator iter
= nsecxrepo
.begin(); iter
!= nsecxrepo
.end(); ++iter
) {
1059 if(iter
->second
.d_auth
) {
1060 NSEC3RecordContent n3rc
;
1061 n3rc
.set(iter
->second
.d_set
);
1062 const auto numberOfTypesSet
= n3rc
.numberOfTypesSet();
1063 if (numberOfTypesSet
!= 0 && (numberOfTypesSet
!= 1 || !n3rc
.isSet(QType::NS
))) {
1064 n3rc
.set(QType::RRSIG
);
1066 n3rc
.d_salt
= ns3pr
.d_salt
;
1067 n3rc
.d_flags
= ns3pr
.d_flags
;
1068 n3rc
.d_iterations
= ns3pr
.d_iterations
;
1069 n3rc
.d_algorithm
= DNSSECKeeper::DIGEST_SHA1
; // SHA1, fixed in PowerDNS for now
1070 nsecxrepo_t::const_iterator inext
= iter
;
1072 if(inext
== nsecxrepo
.end())
1073 inext
= nsecxrepo
.begin();
1074 while(!inext
->second
.d_auth
&& inext
!= iter
)
1077 if(inext
== nsecxrepo
.end())
1078 inext
= nsecxrepo
.begin();
1080 n3rc
.d_nexthash
= fromBase32Hex(inext
->first
.toStringNoDot());
1081 zrr
.dr
.d_name
= iter
->first
+sd
.qname
;
1083 zrr
.dr
.d_ttl
= sd
.getNegativeTTL();
1084 zrr
.dr
.setContent(std::make_shared
<NSEC3RecordContent
>(std::move(n3rc
)));
1085 zrr
.dr
.d_type
= QType::NSEC3
;
1086 zrr
.dr
.d_place
= DNSResourceRecord::ANSWER
;
1088 if(csp
.submit(zrr
)) {
1090 outpacket
->getRRS() = csp
.getChunk();
1091 if(!outpacket
->getRRS().empty()) {
1092 if(haveTSIGDetails
&& !tsigkeyname
.empty())
1093 outpacket
->setTSIGDetails(trc
, tsigkeyname
, tsigsecret
, trc
.d_mac
, true);
1094 sendPacket(outpacket
, outsock
, false);
1095 trc
.d_mac
=outpacket
->d_trc
.d_mac
;
1096 outpacket
=getFreshAXFRPacket(q
);
1105 else for(nsecxrepo_t::const_iterator iter
= nsecxrepo
.begin(); iter
!= nsecxrepo
.end(); ++iter
) {
1106 NSECRecordContent nrc
;
1107 nrc
.set(iter
->second
.d_set
);
1108 nrc
.set(QType::RRSIG
);
1109 nrc
.set(QType::NSEC
);
1111 if(boost::next(iter
) != nsecxrepo
.end())
1112 nrc
.d_next
= boost::next(iter
)->first
;
1114 nrc
.d_next
=nsecxrepo
.begin()->first
;
1115 zrr
.dr
.d_name
= iter
->first
;
1117 zrr
.dr
.d_ttl
= sd
.getNegativeTTL();
1118 zrr
.dr
.setContent(std::make_shared
<NSECRecordContent
>(std::move(nrc
)));
1119 zrr
.dr
.d_type
= QType::NSEC
;
1120 zrr
.dr
.d_place
= DNSResourceRecord::ANSWER
;
1122 if(csp
.submit(zrr
)) {
1124 outpacket
->getRRS() = csp
.getChunk();
1125 if(!outpacket
->getRRS().empty()) {
1126 if(haveTSIGDetails
&& !tsigkeyname
.empty())
1127 outpacket
->setTSIGDetails(trc
, tsigkeyname
, tsigsecret
, trc
.d_mac
, true);
1128 sendPacket(outpacket
, outsock
, false);
1129 trc
.d_mac
=outpacket
->d_trc
.d_mac
;
1130 outpacket
=getFreshAXFRPacket(q
);
1139 udiff=dt.udiffNoReset();
1140 cerr<<"Flushing pipe: "<<csp.d_signed/(udiff/1000000.0)<<" sigs/s, "<<csp.d_signed<<" / "<<udiff/1000000.0<<endl;
1141 cerr<<"Outstanding: "<<csp.d_outstanding<<", "<<csp.d_queued - csp.d_signed << endl;
1142 cerr<<"Ready for consumption: "<<csp.getReady()<<endl;
1145 outpacket
->getRRS() = csp
.getChunk(true); // flush the pipe
1146 if(!outpacket
->getRRS().empty()) {
1147 if(haveTSIGDetails
&& !tsigkeyname
.empty())
1148 outpacket
->setTSIGDetails(trc
, tsigkeyname
, tsigsecret
, trc
.d_mac
, true); // first answer is 'normal'
1150 sendPacket(outpacket
, outsock
, false);
1152 catch (PDNSException
& pe
) {
1153 throw PDNSException("during axfr-out of "+target
.toString()+", this happened: "+pe
.reason
);
1155 trc
.d_mac
=outpacket
->d_trc
.d_mac
;
1156 outpacket
=getFreshAXFRPacket(q
);
1162 udiff
=dt
.udiffNoReset();
1164 g_log
<<Logger::Debug
<<logPrefix
<<"done signing: "<<csp
.d_signed
/(udiff
/1000000.0)<<" sigs/s, "<<endl
;
1166 DLOG(g_log
<<logPrefix
<<"done writing out records"<<endl
);
1167 /* and terminate with yet again the SOA record */
1168 outpacket
=getFreshAXFRPacket(q
);
1169 outpacket
->addRecord(std::move(soa
));
1170 if(haveTSIGDetails
&& !tsigkeyname
.empty())
1171 outpacket
->setTSIGDetails(trc
, tsigkeyname
, tsigsecret
, trc
.d_mac
, true);
1173 sendPacket(outpacket
, outsock
);
1175 DLOG(g_log
<<logPrefix
<<"last packet - close"<<endl
);
1176 g_log
<<Logger::Notice
<<logPrefix
<<"AXFR finished"<<endl
;
1181 int TCPNameserver::doIXFR(std::unique_ptr
<DNSPacket
>& q
, int outsock
)
1183 string logPrefix
="IXFR-out zone '"+q
->qdomain
.toLogString()+"', client '"+q
->getRemoteStringWithPort()+"', ";
1185 std::unique_ptr
<DNSPacket
> outpacket
=getFreshAXFRPacket(q
);
1187 outpacket
->d_dnssecOk
=true; // RFC 5936, 2.2.5 'SHOULD'
1189 uint32_t serial
= 0;
1190 MOADNSParser
mdp(false, q
->getString());
1191 for(const auto & answer
: mdp
.d_answers
) {
1192 const DNSRecord
*rr
= &answer
.first
;
1193 if (rr
->d_type
== QType::SOA
&& rr
->d_place
== DNSResourceRecord::AUTHORITY
) {
1194 vector
<string
>parts
;
1195 stringtok(parts
, rr
->getContent()->getZoneRepresentation());
1196 if (parts
.size() >= 3) {
1198 pdns::checked_stoi_into(serial
, parts
[2]);
1200 catch(const std::out_of_range
& oor
) {
1201 g_log
<<Logger::Warning
<<logPrefix
<<"invalid serial in IXFR query"<<endl
;
1202 outpacket
->setRcode(RCode::FormErr
);
1203 sendPacket(outpacket
,outsock
);
1207 g_log
<<Logger::Warning
<<logPrefix
<<"no serial in IXFR query"<<endl
;
1208 outpacket
->setRcode(RCode::FormErr
);
1209 sendPacket(outpacket
,outsock
);
1212 } else if (rr
->d_type
!= QType::TSIG
&& rr
->d_type
!= QType::OPT
) {
1213 g_log
<<Logger::Warning
<<logPrefix
<<"additional records in IXFR query, type: "<<QType(rr
->d_type
).toString()<<endl
;
1214 outpacket
->setRcode(RCode::FormErr
);
1215 sendPacket(outpacket
,outsock
);
1220 g_log
<<Logger::Warning
<<logPrefix
<<"transfer initiated with serial "<<serial
<<endl
;
1222 // determine if zone exists, XFR is allowed, and if IXFR can proceed using existing backend before spawning a new backend.
1225 bool serialPermitsIXFR
;
1227 auto packetHandler
= s_P
.lock();
1228 DLOG(g_log
<<logPrefix
<<"Looking for SOA"<<endl
); // find domain_id via SOA and list complete domain. No SOA, no IXFR
1229 if(!*packetHandler
) {
1230 g_log
<<Logger::Warning
<<"TCP server is without backend connections in doIXFR, launching"<<endl
;
1231 *packetHandler
= make_unique
<PacketHandler
>();
1234 // canDoAXFR does all the ACL checks, and has the if(disable-axfr) shortcut, call it first.
1235 if(!canDoAXFR(q
, false, *packetHandler
) || !(*packetHandler
)->getBackend()->getSOAUncached(q
->qdomain
, sd
)) {
1236 g_log
<<Logger::Warning
<<logPrefix
<<"failed: not authoritative"<<endl
;
1237 outpacket
->setRcode(RCode::NotAuth
);
1238 sendPacket(outpacket
,outsock
);
1242 DNSSECKeeper
dk((*packetHandler
)->getBackend());
1243 DNSSECKeeper::clearCaches(q
->qdomain
);
1244 bool narrow
= false;
1245 securedZone
= dk
.isSecuredZone(q
->qdomain
);
1246 if(dk
.getNSEC3PARAM(q
->qdomain
, nullptr, &narrow
)) {
1248 g_log
<<Logger::Warning
<<logPrefix
<<"not doing IXFR of an NSEC3 narrow zone"<<endl
;
1249 outpacket
->setRcode(RCode::Refused
);
1250 sendPacket(outpacket
,outsock
);
1255 serialPermitsIXFR
= !rfc1982LessThan(serial
, calculateEditSOA(sd
.serial
, dk
, sd
.qname
));
1258 if (serialPermitsIXFR
) {
1259 DNSName target
= q
->qdomain
;
1260 TSIGRecordContent trc
;
1261 DNSName tsigkeyname
;
1265 DNSSECKeeper
dk(&db
);
1267 bool haveTSIGDetails
= q
->getTSIGDetails(&trc
, &tsigkeyname
);
1269 if(haveTSIGDetails
&& !tsigkeyname
.empty()) {
1271 DNSName algorithm
=trc
.d_algoName
; // FIXME400: was toLowerCanonic, compare output
1272 if (algorithm
== DNSName("hmac-md5.sig-alg.reg.int"))
1273 algorithm
= DNSName("hmac-md5");
1274 if (!db
.getTSIGKey(tsigkeyname
, algorithm
, tsig64
)) {
1275 g_log
<< Logger::Error
<< "TSIG key '" << tsigkeyname
<< "' for domain '" << target
<< "' not found" << endl
;
1278 if (B64Decode(tsig64
, tsigsecret
) == -1) {
1279 g_log
<<Logger::Error
<<logPrefix
<<"unable to Base-64 decode TSIG key '"<<tsigkeyname
<<"'"<<endl
;
1284 // SOA *must* go out first, our signing pipe might reorder
1285 DLOG(g_log
<<logPrefix
<<"sending out SOA"<<endl
);
1286 DNSZoneRecord soa
= makeEditedDNSZRFromSOAData(dk
, sd
);
1287 outpacket
->addRecord(std::move(soa
));
1288 if(securedZone
&& outpacket
->d_dnssecOk
) {
1289 set
<DNSName
> authSet
;
1290 authSet
.insert(target
);
1291 addRRSigs(dk
, db
, authSet
, outpacket
->getRRS());
1294 if(haveTSIGDetails
&& !tsigkeyname
.empty())
1295 outpacket
->setTSIGDetails(trc
, tsigkeyname
, tsigsecret
, trc
.d_mac
); // first answer is 'normal'
1297 sendPacket(outpacket
, outsock
);
1299 g_log
<<Logger::Notice
<<logPrefix
<<"IXFR finished"<<endl
;
1304 g_log
<<Logger::Notice
<<logPrefix
<<"IXFR fallback to AXFR"<<endl
;
1305 return doAXFR(q
->qdomain
, q
, outsock
);
1308 TCPNameserver::~TCPNameserver() = default;
1309 TCPNameserver::TCPNameserver()
1311 d_maxTransactionsPerConn
= ::arg().asNum("max-tcp-transactions-per-conn");
1312 d_idleTimeout
= ::arg().asNum("tcp-idle-timeout");
1313 d_maxConnectionDuration
= ::arg().asNum("max-tcp-connection-duration");
1314 d_maxConnectionsPerClient
= ::arg().asNum("max-tcp-connections-per-client");
1316 // sem_init(&d_connectionroom_sem,0,::arg().asNum("max-tcp-connections"));
1317 d_connectionroom_sem
= make_unique
<Semaphore
>( ::arg().asNum( "max-tcp-connections" ));
1318 d_maxTCPConnections
= ::arg().asNum( "max-tcp-connections" );
1320 vector
<string
>locals
;
1321 stringtok(locals
,::arg()["local-address"]," ,");
1323 throw PDNSException("No local addresses specified");
1325 d_ng
.toMasks(::arg()["allow-axfr-ips"] );
1327 signal(SIGPIPE
,SIG_IGN
);
1329 for(auto const &laddr
: locals
) {
1330 ComboAddress
local(laddr
, ::arg().asNum("local-port"));
1332 int s
=socket(local
.sin4
.sin_family
, SOCK_STREAM
, 0);
1334 throw PDNSException("Unable to acquire TCP socket: "+stringerror());
1338 if(setsockopt(s
, SOL_SOCKET
,SO_REUSEADDR
, (char*)&tmp
, sizeof tmp
) < 0) {
1339 g_log
<<Logger::Error
<<"Setsockopt failed"<<endl
;
1343 if (::arg().asNum("tcp-fast-open") > 0) {
1345 int fastOpenQueueSize
= ::arg().asNum("tcp-fast-open");
1346 if (setsockopt(s
, IPPROTO_TCP
, TCP_FASTOPEN
, &fastOpenQueueSize
, sizeof fastOpenQueueSize
) < 0) {
1347 g_log
<<Logger::Error
<<"Failed to enable TCP Fast Open for listening socket "<<local
.toStringWithPort()<<": "<<stringerror()<<endl
;
1350 g_log
<<Logger::Warning
<<"TCP Fast Open configured but not supported for listening socket"<<endl
;
1354 if(::arg().mustDo("non-local-bind"))
1355 Utility::setBindAny(local
.sin4
.sin_family
, s
);
1357 if(local
.isIPv6() && setsockopt(s
, IPPROTO_IPV6
, IPV6_V6ONLY
, &tmp
, sizeof(tmp
)) < 0) {
1358 g_log
<<Logger::Error
<<"Failed to set IPv6 socket to IPv6 only, continuing anyhow: "<<stringerror()<<endl
;
1361 if(::bind(s
, (sockaddr
*)&local
, local
.getSocklen())<0) {
1364 if( err
== EADDRNOTAVAIL
&& ! ::arg().mustDo("local-address-nonexist-fail") ) {
1365 g_log
<<Logger::Error
<<"Address " << local
.toString() << " does not exist on this server - skipping TCP bind" << endl
;
1368 g_log
<<Logger::Error
<<"Unable to bind to TCP socket " << local
.toStringWithPort() << ": "<<stringerror(err
)<<endl
;
1369 throw PDNSException("Unable to bind to TCP socket");
1374 g_log
<<Logger::Error
<<"TCP server bound to "<<local
.toStringWithPort()<<endl
;
1375 d_sockets
.push_back(s
);
1377 memset(&pfd
, 0, sizeof(pfd
));
1379 pfd
.events
= POLLIN
;
1380 d_prfds
.push_back(pfd
);
1385 //! Start of TCP operations thread, we launch a new thread for each incoming TCP question
1386 void TCPNameserver::thread()
1388 setThreadName("pdns/tcpnameser");
1392 ComboAddress remote
;
1393 Utility::socklen_t addrlen
=remote
.getSocklen();
1395 int ret
=poll(&d_prfds
[0], d_prfds
.size(), -1); // blocks, forever if need be
1400 for(const pollfd
& pfd
: d_prfds
) {
1401 if(pfd
.revents
& POLLIN
) {
1403 remote
.sin4
.sin_family
= AF_INET6
;
1404 addrlen
=remote
.getSocklen();
1406 if((fd
=accept(sock
, (sockaddr
*)&remote
, &addrlen
))<0) {
1408 g_log
<<Logger::Error
<<"TCP question accept error: "<<stringerror(err
)<<endl
;
1411 g_log
<<Logger::Error
<<"TCP handler out of filedescriptors, exiting, won't recover from this"<<endl
;
1416 if (d_maxConnectionsPerClient
) {
1417 auto clientsCount
= s_clientsCount
.lock();
1418 if ((*clientsCount
)[remote
] >= d_maxConnectionsPerClient
) {
1419 g_log
<<Logger::Notice
<<"Limit of simultaneous TCP connections per client reached for "<< remote
<<", dropping"<<endl
;
1423 (*clientsCount
)[remote
]++;
1426 d_connectionroom_sem
->wait(); // blocks if no connections are available
1429 d_connectionroom_sem
->getValue( &room
);
1431 g_log
<<Logger::Warning
<<"Limit of simultaneous TCP connections reached - raise max-tcp-connections"<<endl
;
1434 std::thread
connThread(doConnection
, fd
);
1435 connThread
.detach();
1437 catch (std::exception
& e
) {
1438 g_log
<<Logger::Error
<<"Error creating thread: "<<e
.what()<<endl
;
1439 d_connectionroom_sem
->post();
1441 decrementClientCount(remote
);
1448 catch(PDNSException
&AE
) {
1449 g_log
<<Logger::Error
<<"TCP Nameserver thread dying because of fatal error: "<<AE
.reason
<<endl
;
1452 g_log
<<Logger::Error
<<"TCPNameserver dying because of an unexpected fatal error"<<endl
;
1454 _exit(1); // take rest of server with us
1458 unsigned int TCPNameserver::numTCPConnections()
1461 d_connectionroom_sem
->getValue( &room
);
1462 return d_maxTCPConnections
- room
;