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<decltype(elapsed
)>(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
) {
370 doAXFR(packet
->qdomain
, packet
, fd
);
374 if(packet
->qtype
.getCode()==QType::IXFR
) {
379 std::unique_ptr
<DNSPacket
> reply
;
380 auto cached
= make_unique
<DNSPacket
>(false);
382 g_log
<< Logger::Notice
<<"TCP Remote "<< packet
->getRemoteString() <<" wants '" << packet
->qdomain
<<"|"<<packet
->qtype
.toString() <<
383 "', do = " <<packet
->d_dnssecOk
<<", bufsize = "<< packet
->getMaxReplyLen();
387 if(packet
->couldBeCached() && PC
.get(*packet
, *cached
)) { // short circuit - does the PacketCache recognize this question?
389 g_log
<<": packetcache HIT"<<endl
;
390 cached
->setRemote(&packet
->d_remote
);
391 cached
->d_inner_remote
= packet
->d_inner_remote
;
392 cached
->d
.id
=packet
->d
.id
;
393 cached
->d
.rd
=packet
->d
.rd
; // copy in recursion desired bit
394 cached
->commitD(); // commit d to the packet inlined
396 sendPacket(cached
, fd
); // presigned, don't do it again
400 g_log
<<": packetcache MISS"<<endl
;
407 auto packetHandler
= s_P
.lock();
408 if (!*packetHandler
) {
409 g_log
<<Logger::Warning
<<"TCP server is without backend connections, launching"<<endl
;
410 *packetHandler
= make_unique
<PacketHandler
>();
413 reply
= (*packetHandler
)->doQuestion(*packet
); // we really need to ask the backend :-)
416 if(!reply
) // unable to write an answer?
419 sendPacket(reply
, fd
);
420 #ifdef ENABLE_GSS_TSIG
422 packet
->cleanupGSS(reply
->d
.rcode
);
427 catch(PDNSException
&ae
) {
428 s_P
.lock()->reset(); // on next call, backend will be recycled
429 g_log
<< Logger::Error
<< "TCP Connection Thread for client " << remote
<< " failed, cycling backend: " << ae
.reason
<< endl
;
431 catch(NetworkError
&e
) {
432 g_log
<< Logger::Info
<< "TCP Connection Thread for client " << remote
<< " died because of network error: " << e
.what() << endl
;
435 catch(std::exception
&e
) {
436 s_P
.lock()->reset(); // on next call, backend will be recycled
437 g_log
<< Logger::Error
<< "TCP Connection Thread for client " << remote
<< " died because of STL error, cycling backend: " << e
.what() << endl
;
441 s_P
.lock()->reset(); // on next call, backend will be recycled
442 g_log
<< Logger::Error
<< "TCP Connection Thread for client " << remote
<< " caught unknown exception, cycling backend." << endl
;
444 d_connectionroom_sem
->post();
449 catch(const PDNSException
& e
) {
450 g_log
<< Logger::Error
<< "Error closing TCP socket for client " << remote
<< ": " << e
.reason
<< endl
;
452 decrementClientCount(remote
);
456 bool TCPNameserver::canDoAXFR(std::unique_ptr
<DNSPacket
>& q
, bool isAXFR
, std::unique_ptr
<PacketHandler
>& packetHandler
)
458 if(::arg().mustDo("disable-axfr"))
461 string logPrefix
=string(isAXFR
? "A" : "I")+"XFR-out zone '"+q
->qdomain
.toLogString()+"', client '"+q
->getInnerRemote().toStringWithPort()+"', ";
463 if(q
->d_havetsig
) { // if you have one, it must be good
464 TSIGRecordContent trc
;
467 if(!q
->checkForCorrectTSIG(packetHandler
->getBackend(), &keyname
, &secret
, &trc
)) {
470 getTSIGHashEnum(trc
.d_algoName
, q
->d_tsig_algo
);
471 #ifdef ENABLE_GSS_TSIG
472 if (g_doGssTSIG
&& q
->d_tsig_algo
== TSIG_GSS
) {
473 GssContext
gssctx(keyname
);
474 if (!gssctx
.getPeerPrincipal(q
->d_peer_principal
)) {
475 g_log
<<Logger::Warning
<<"Failed to extract peer principal from GSS context with keyname '"<<keyname
<<"'"<<endl
;
481 DNSSECKeeper
dk(packetHandler
->getBackend());
482 #ifdef ENABLE_GSS_TSIG
483 if (g_doGssTSIG
&& q
->d_tsig_algo
== TSIG_GSS
) {
484 vector
<string
> princs
;
485 packetHandler
->getBackend()->getDomainMetadata(q
->qdomain
, "GSS-ALLOW-AXFR-PRINCIPAL", princs
);
486 for(const std::string
& princ
: princs
) {
487 if (q
->d_peer_principal
== princ
) {
488 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
;
492 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
;
496 if(!dk
.TSIGGrantsAccess(q
->qdomain
, keyname
)) {
497 g_log
<<Logger::Warning
<<logPrefix
<<"denied: key with name '"<<keyname
<<"' and algorithm '"<<getTSIGAlgoName(q
->d_tsig_algo
)<<"' does not grant access"<<endl
;
501 g_log
<<Logger::Notice
<<logPrefix
<<"allowed: TSIG signed request with authorized key '"<<keyname
<<"' and algorithm '"<<getTSIGAlgoName(q
->d_tsig_algo
)<<"'"<<endl
;
506 // cerr<<"checking allow-axfr-ips"<<endl;
507 if(!(::arg()["allow-axfr-ips"].empty()) && d_ng
.match( q
->getInnerRemote() )) {
508 g_log
<<Logger::Notice
<<logPrefix
<<"allowed: client IP is in allow-axfr-ips"<<endl
;
514 // cerr<<"doing per-zone-axfr-acls"<<endl;
516 if(packetHandler
->getBackend()->getSOAUncached(q
->qdomain
,sd
)) {
517 // cerr<<"got backend and SOA"<<endl;
519 packetHandler
->getBackend()->getDomainMetadata(q
->qdomain
, "ALLOW-AXFR-FROM", acl
);
520 for (const auto & i
: acl
) {
521 // cerr<<"matching against "<<*i<<endl;
522 if(pdns_iequals(i
, "AUTO-NS")) {
523 // cerr<<"AUTO-NS magic please!"<<endl;
525 DNSResourceRecord rr
;
528 sd
.db
->lookup(QType(QType::NS
), q
->qdomain
, sd
.domain_id
);
529 while (sd
.db
->get(rr
)) {
530 nsset
.insert(DNSName(rr
.content
));
532 for(const auto & j
: nsset
) {
533 vector
<string
> nsips
=fns
.lookup(j
, packetHandler
->getBackend());
534 for(const auto & nsip
: nsips
) {
535 // cerr<<"got "<<*k<<" from AUTO-NS"<<endl;
536 if(nsip
== q
->getInnerRemote().toString())
538 // cerr<<"got AUTO-NS hit"<<endl;
539 g_log
<<Logger::Notice
<<logPrefix
<<"allowed: client IP is in NSset"<<endl
;
547 Netmask nm
= Netmask(i
);
548 if(nm
.match( q
->getInnerRemote() ))
550 g_log
<<Logger::Notice
<<logPrefix
<<"allowed: client IP is in per-zone ACL"<<endl
;
551 // cerr<<"hit!"<<endl;
558 extern CommunicatorClass Communicator
;
560 if(Communicator
.justNotified(q
->qdomain
, q
->getInnerRemote().toString())) { // we just notified this ip
561 g_log
<<Logger::Notice
<<logPrefix
<<"allowed: client IP is from recently notified secondary"<<endl
;
565 g_log
<<Logger::Warning
<<logPrefix
<<"denied: client IP has no permission"<<endl
;
577 static std::unique_ptr
<DNSPacket
> getFreshAXFRPacket(std::unique_ptr
<DNSPacket
>& q
)
579 std::unique_ptr
<DNSPacket
> ret
= std::unique_ptr
<DNSPacket
>(q
->replyPacket());
580 ret
->setCompress(false);
581 ret
->d_dnssecOk
=false; // RFC 5936, 2.2.5
588 /** do the actual zone transfer. Return 0 in case of error, 1 in case of success */
589 int TCPNameserver::doAXFR(const DNSName
&target
, std::unique_ptr
<DNSPacket
>& q
, int outsock
) // NOLINT(readability-function-cognitive-complexity)
591 string logPrefix
="AXFR-out zone '"+target
.toLogString()+"', client '"+q
->getRemoteStringWithPort()+"', ";
593 std::unique_ptr
<DNSPacket
> outpacket
= getFreshAXFRPacket(q
);
595 outpacket
->d_dnssecOk
=true; // RFC 5936, 2.2.5 'SHOULD'
597 g_log
<<Logger::Warning
<<logPrefix
<<"transfer initiated"<<endl
;
599 // determine if zone exists and AXFR is allowed using existing backend before spawning a new backend.
602 auto packetHandler
= s_P
.lock();
603 DLOG(g_log
<<logPrefix
<<"looking for SOA"<<endl
); // find domain_id via SOA and list complete domain. No SOA, no AXFR
604 if(!*packetHandler
) {
605 g_log
<<Logger::Warning
<<"TCP server is without backend connections in doAXFR, launching"<<endl
;
606 *packetHandler
= make_unique
<PacketHandler
>();
609 // canDoAXFR does all the ACL checks, and has the if(disable-axfr) shortcut, call it first.
610 if (!canDoAXFR(q
, true, *packetHandler
)) {
611 g_log
<<Logger::Warning
<<logPrefix
<<"failed: client may not request AXFR"<<endl
;
612 outpacket
->setRcode(RCode::NotAuth
);
613 sendPacket(outpacket
,outsock
);
617 if (!(*packetHandler
)->getBackend()->getSOAUncached(target
, sd
)) {
618 g_log
<<Logger::Warning
<<logPrefix
<<"failed: not authoritative"<<endl
;
619 outpacket
->setRcode(RCode::NotAuth
);
620 sendPacket(outpacket
,outsock
);
626 if(!db
.getSOAUncached(target
, sd
)) {
627 g_log
<<Logger::Warning
<<logPrefix
<<"failed: not authoritative in second instance"<<endl
;
628 outpacket
->setRcode(RCode::NotAuth
);
629 sendPacket(outpacket
,outsock
);
633 bool securedZone
= false;
634 bool presignedZone
= false;
635 bool NSEC3Zone
= false;
639 bool isCatalogZone
= sd
.db
->getDomainInfo(target
, di
, false) && di
.isCatalogType();
641 NSEC3PARAMRecordContent ns3pr
;
643 DNSSECKeeper
dk(&db
);
644 DNSSECKeeper::clearCaches(target
);
645 if (!isCatalogZone
) {
646 securedZone
= dk
.isSecuredZone(target
);
647 presignedZone
= dk
.isPresigned(target
);
650 if(securedZone
&& dk
.getNSEC3PARAM(target
, &ns3pr
, &narrow
)) {
653 g_log
<<Logger::Warning
<<logPrefix
<<"failed: not doing AXFR of an NSEC3 narrow zone"<<endl
;
654 outpacket
->setRcode(RCode::Refused
);
655 sendPacket(outpacket
,outsock
);
660 TSIGRecordContent trc
;
664 bool haveTSIGDetails
= q
->getTSIGDetails(&trc
, &tsigkeyname
);
666 if(haveTSIGDetails
&& !tsigkeyname
.empty()) {
668 DNSName algorithm
=trc
.d_algoName
;
669 if (algorithm
== DNSName("hmac-md5.sig-alg.reg.int"))
670 algorithm
= DNSName("hmac-md5");
671 if (algorithm
!= DNSName("gss-tsig")) {
672 if(!db
.getTSIGKey(tsigkeyname
, algorithm
, tsig64
)) {
673 g_log
<<Logger::Warning
<<logPrefix
<<"TSIG key not found"<<endl
;
676 if (B64Decode(tsig64
, tsigsecret
) == -1) {
677 g_log
<<Logger::Error
<<logPrefix
<<"unable to Base-64 decode TSIG key '"<<tsigkeyname
<<"'"<<endl
;
684 // SOA *must* go out first, our signing pipe might reorder
685 DLOG(g_log
<<logPrefix
<<"sending out SOA"<<endl
);
686 DNSZoneRecord soa
= makeEditedDNSZRFromSOAData(dk
, sd
);
687 outpacket
->addRecord(DNSZoneRecord(soa
));
688 if(securedZone
&& !presignedZone
) {
689 set
<DNSName
> authSet
;
690 authSet
.insert(target
);
691 addRRSigs(dk
, db
, authSet
, outpacket
->getRRS());
694 if(haveTSIGDetails
&& !tsigkeyname
.empty())
695 outpacket
->setTSIGDetails(trc
, tsigkeyname
, tsigsecret
, trc
.d_mac
); // first answer is 'normal'
697 sendPacket(outpacket
, outsock
, false);
699 trc
.d_mac
= outpacket
->d_trc
.d_mac
;
700 outpacket
= getFreshAXFRPacket(q
);
704 vector
<DNSZoneRecord
> zrrs
;
706 zrr
.dr
.d_name
= target
;
707 zrr
.dr
.d_ttl
= sd
.minimum
;
709 if(securedZone
&& !presignedZone
) { // this is where the DNSKEYs, CDNSKEYs and CDSs go in
710 bool doCDNSKEY
= true, doCDS
= true;
711 string publishCDNSKEY
, publishCDS
;
712 dk
.getPublishCDNSKEY(q
->qdomain
, publishCDNSKEY
);
713 dk
.getPublishCDS(q
->qdomain
, publishCDS
);
715 set
<uint32_t> entryPointIds
;
716 DNSSECKeeper::keyset_t entryPoints
= dk
.getEntryPoints(target
);
717 for (auto const& value
: entryPoints
) {
718 entryPointIds
.insert(value
.second
.id
);
721 DNSSECKeeper::keyset_t keys
= dk
.getKeys(target
);
722 for(const DNSSECKeeper::keyset_t::value_type
& value
: keys
) {
723 if (!value
.second
.published
) {
726 zrr
.dr
.d_type
= QType::DNSKEY
;
727 zrr
.dr
.setContent(std::make_shared
<DNSKEYRecordContent
>(value
.first
.getDNSKEY()));
728 DNSName keyname
= NSEC3Zone
? DNSName(toBase32Hex(hashQNameWithSalt(ns3pr
, zrr
.dr
.d_name
))) : zrr
.dr
.d_name
;
731 // generate CDS and CDNSKEY records
732 if(doCDNSKEY
&& entryPointIds
.count(value
.second
.id
) > 0){
733 if(!publishCDNSKEY
.empty()) {
734 zrr
.dr
.d_type
=QType::CDNSKEY
;
735 if (publishCDNSKEY
== "0") {
737 zrr
.dr
.setContent(PacketHandler::s_deleteCDNSKEYContent
);
740 zrr
.dr
.setContent(std::make_shared
<DNSKEYRecordContent
>(value
.first
.getDNSKEY()));
745 if(doCDS
&& !publishCDS
.empty()){
746 zrr
.dr
.d_type
=QType::CDS
;
747 vector
<string
> digestAlgos
;
748 stringtok(digestAlgos
, publishCDS
, ", ");
749 if(std::find(digestAlgos
.begin(), digestAlgos
.end(), "0") != digestAlgos
.end()) {
751 zrr
.dr
.setContent(PacketHandler::s_deleteCDSContent
);
754 for(auto const &digestAlgo
: digestAlgos
) {
755 zrr
.dr
.setContent(std::make_shared
<DSRecordContent
>(makeDSFromDNSKey(target
, value
.first
.getDNSKEY(), pdns::checked_stoi
<uint8_t>(digestAlgo
))));
765 if(NSEC3Zone
) { // now stuff in the NSEC3PARAM
766 uint8_t flags
= ns3pr
.d_flags
;
767 zrr
.dr
.d_type
= QType::NSEC3PARAM
;
769 zrr
.dr
.setContent(std::make_shared
<NSEC3PARAMRecordContent
>(ns3pr
));
770 ns3pr
.d_flags
= flags
;
771 DNSName keyname
= DNSName(toBase32Hex(hashQNameWithSalt(ns3pr
, zrr
.dr
.d_name
)));
775 const bool rectify
= !(presignedZone
|| ::arg().mustDo("disable-axfr-rectify"));
776 set
<DNSName
> qnames
, nsset
, terms
;
778 // Catalog zone start
779 if (di
.kind
== DomainInfo::Producer
) {
780 // Ignore all records except NS at apex
781 sd
.db
->lookup(QType::NS
, target
, di
.id
);
782 while (sd
.db
->get(zrr
)) {
783 zrrs
.emplace_back(zrr
);
786 zrr
.dr
.d_name
= target
;
788 zrr
.dr
.d_type
= QType::NS
;
789 zrr
.dr
.setContent(std::make_shared
<NSRecordContent
>("invalid."));
790 zrrs
.emplace_back(zrr
);
793 zrrs
.emplace_back(CatalogInfo::getCatalogVersionRecord(target
));
795 vector
<CatalogInfo
> members
;
796 sd
.db
->getCatalogMembers(target
, members
, CatalogInfo::CatalogType::Producer
);
797 for (const auto& ci
: members
) {
798 ci
.toDNSZoneRecords(target
, zrrs
);
800 if (members
.empty()) {
801 g_log
<< Logger::Warning
<< logPrefix
<< "catalog zone '" << target
<< "' has no members" << endl
;
807 // now start list zone
808 if (!sd
.db
->list(target
, sd
.domain_id
, isCatalogZone
)) {
809 g_log
<<Logger::Error
<<logPrefix
<<"backend signals error condition, aborting AXFR"<<endl
;
810 outpacket
->setRcode(RCode::ServFail
);
811 sendPacket(outpacket
,outsock
);
815 while(sd
.db
->get(zrr
)) {
816 if (!presignedZone
) {
817 if (zrr
.dr
.d_type
== QType::RRSIG
) {
820 if (zrr
.dr
.d_type
== QType::DNSKEY
|| zrr
.dr
.d_type
== QType::CDNSKEY
|| zrr
.dr
.d_type
== QType::CDS
) {
821 if(!::arg().mustDo("direct-dnskey")) {
824 zrr
.dr
.d_ttl
= sd
.minimum
;
828 zrr
.dr
.d_name
.makeUsLowerCase();
829 if(zrr
.dr
.d_name
.isPartOf(target
)) {
830 if (zrr
.dr
.d_type
== QType::ALIAS
&& (::arg().mustDo("outgoing-axfr-expand-alias") || ::arg()["outgoing-axfr-expand-alias"] == "ignore-errors")) {
831 vector
<DNSZoneRecord
> ips
;
832 int ret1
= stubDoResolve(getRR
<ALIASRecordContent
>(zrr
.dr
)->getContent(), QType::A
, ips
);
833 int ret2
= stubDoResolve(getRR
<ALIASRecordContent
>(zrr
.dr
)->getContent(), QType::AAAA
, ips
);
834 if (ret1
!= RCode::NoError
|| ret2
!= RCode::NoError
) {
835 if (::arg()["outgoing-axfr-expand-alias"] == "ignore-errors") {
836 if (ret1
!= RCode::NoError
) {
837 g_log
<< Logger::Error
<< logPrefix
<< zrr
.dr
.d_name
.toLogString() << ": error resolving A record for ALIAS target " << zrr
.dr
.getContent()->getZoneRepresentation() << ", continuing AXFR" << endl
;
839 if (ret2
!= RCode::NoError
) {
840 g_log
<< Logger::Error
<< logPrefix
<< zrr
.dr
.d_name
.toLogString() << ": error resolving AAAA record for ALIAS target " << zrr
.dr
.getContent()->getZoneRepresentation() << ", continuing AXFR" << endl
;
844 g_log
<< Logger::Warning
<< logPrefix
<< zrr
.dr
.d_name
.toLogString() << ": error resolving for ALIAS " << zrr
.dr
.getContent()->getZoneRepresentation() << ", aborting AXFR" << endl
;
845 outpacket
->setRcode(RCode::ServFail
);
846 sendPacket(outpacket
, outsock
);
850 for (auto& ip
: ips
) {
851 zrr
.dr
.d_type
= ip
.dr
.d_type
;
852 zrr
.dr
.setContent(ip
.dr
.getContent());
860 qnames
.insert(zrr
.dr
.d_name
);
861 if(zrr
.dr
.d_type
== QType::NS
&& zrr
.dr
.d_name
!=target
)
862 nsset
.insert(zrr
.dr
.d_name
);
864 // remove existing ents
871 g_log
<<Logger::Warning
<<logPrefix
<<"zone contains out-of-zone data '"<<zrr
.dr
.d_name
<<"|"<<DNSRecordContent::NumberToType(zrr
.dr
.d_type
)<<"', ignoring"<<endl
;
875 for (auto& loopRR
: zrrs
) {
876 if ((loopRR
.dr
.d_type
== QType::SVCB
|| loopRR
.dr
.d_type
== QType::HTTPS
)) {
877 // Process auto hints
878 // TODO this is an almost copy of the code in the packethandler
879 auto rrc
= getRR
<SVCBBaseRecordContent
>(loopRR
.dr
);
880 if (rrc
== nullptr) {
883 auto newRRC
= rrc
->clone();
887 DNSName svcTarget
= newRRC
->getTarget().isRoot() ? loopRR
.dr
.d_name
: newRRC
->getTarget();
888 if (newRRC
->autoHint(SvcParam::ipv4hint
)) {
889 sd
.db
->lookup(QType::A
, svcTarget
, sd
.domain_id
);
890 vector
<ComboAddress
> hints
;
892 while (sd
.db
->get(rr
)) {
893 auto arrc
= getRR
<ARecordContent
>(rr
.dr
);
894 hints
.push_back(arrc
->getCA());
896 if (hints
.size() == 0) {
897 newRRC
->removeParam(SvcParam::ipv4hint
);
899 newRRC
->setHints(SvcParam::ipv4hint
, hints
);
903 if (newRRC
->autoHint(SvcParam::ipv6hint
)) {
904 sd
.db
->lookup(QType::AAAA
, svcTarget
, sd
.domain_id
);
905 vector
<ComboAddress
> hints
;
907 while (sd
.db
->get(rr
)) {
908 auto arrc
= getRR
<AAAARecordContent
>(rr
.dr
);
909 hints
.push_back(arrc
->getCA());
911 if (hints
.size() == 0) {
912 newRRC
->removeParam(SvcParam::ipv6hint
);
914 newRRC
->setHints(SvcParam::ipv6hint
, hints
);
918 loopRR
.dr
.setContent(std::move(newRRC
));
922 // Group records by name and type, signpipe stumbles over interrupted rrsets
923 if(securedZone
&& !presignedZone
) {
924 sort(zrrs
.begin(), zrrs
.end(), [](const DNSZoneRecord
& a
, const DNSZoneRecord
& b
) {
925 return std::tie(a
.dr
.d_name
, a
.dr
.d_type
) < std::tie(b
.dr
.d_name
, b
.dr
.d_type
);
931 for(DNSZoneRecord
&loopZRR
: zrrs
) {
933 if (loopZRR
.dr
.d_type
!= QType::NS
|| loopZRR
.dr
.d_name
!=target
) {
934 DNSName
shorter(loopZRR
.dr
.d_name
);
936 if (shorter
==target
) // apex is always auth
938 if(nsset
.count(shorter
) && !(loopZRR
.dr
.d_name
==shorter
&& loopZRR
.dr
.d_type
== QType::DS
)) {
942 } while(shorter
.chopOff());
947 // ents are only required for NSEC3 zones
948 uint32_t maxent
= ::arg().asNum("max-ent-entries");
949 set
<DNSName
> nsec3set
, nonterm
;
950 for (auto &loopZRR
: zrrs
) {
952 DNSName shorter
= loopZRR
.dr
.d_name
;
953 if (shorter
!= target
&& shorter
.chopOff() && shorter
!= target
) {
955 if(nsset
.count(shorter
)) {
959 } while(shorter
.chopOff() && shorter
!= target
);
961 shorter
= loopZRR
.dr
.d_name
;
962 if(!skip
&& (loopZRR
.dr
.d_type
!= QType::NS
|| !ns3pr
.d_flags
)) {
964 if(!nsec3set
.count(shorter
)) {
965 nsec3set
.insert(shorter
);
967 } while(shorter
!= target
&& shorter
.chopOff());
971 for(DNSZoneRecord
&loopZRR
: zrrs
) {
972 DNSName
shorter(loopZRR
.dr
.d_name
);
973 while(shorter
!= target
&& shorter
.chopOff()) {
974 if(!qnames
.count(shorter
) && !nonterm
.count(shorter
) && nsec3set
.count(shorter
)) {
976 g_log
<<Logger::Warning
<<logPrefix
<<"zone has too many empty non terminals, aborting AXFR"<<endl
;
977 outpacket
->setRcode(RCode::ServFail
);
978 sendPacket(outpacket
,outsock
);
981 nonterm
.insert(shorter
);
987 for(const auto& nt
: nonterm
) {
988 DNSZoneRecord tempRR
;
990 tempRR
.dr
.d_type
=QType::ENT
;
992 zrrs
.push_back(tempRR
);
999 /* now write all other records */
1001 typedef map
<DNSName
, NSECXEntry
, CanonDNSNameCompare
> nsecxrepo_t
;
1002 nsecxrepo_t nsecxrepo
;
1004 ChunkedSigningPipe
csp(target
, (securedZone
&& !presignedZone
), ::arg().asNum("signing-threads", 1), ::arg().mustDo("workaround-11804") ? 1 : 100);
1010 for(DNSZoneRecord
&loopZRR
: zrrs
) {
1011 if(securedZone
&& (loopZRR
.auth
|| loopZRR
.dr
.d_type
== QType::NS
)) {
1012 if (NSEC3Zone
|| loopZRR
.dr
.d_type
) {
1013 if (presignedZone
&& NSEC3Zone
&& loopZRR
.dr
.d_type
== QType::RRSIG
&& getRR
<RRSIGRecordContent
>(loopZRR
.dr
)->d_type
== QType::NSEC3
) {
1014 keyname
= loopZRR
.dr
.d_name
.makeRelative(sd
.qname
);
1016 keyname
= NSEC3Zone
? DNSName(toBase32Hex(hashQNameWithSalt(ns3pr
, loopZRR
.dr
.d_name
))) : loopZRR
.dr
.d_name
;
1018 NSECXEntry
& ne
= nsecxrepo
[keyname
];
1019 ne
.d_ttl
= sd
.getNegativeTTL();
1020 ne
.d_auth
= (ne
.d_auth
|| loopZRR
.auth
|| (NSEC3Zone
&& (!ns3pr
.d_flags
)));
1021 if (loopZRR
.dr
.d_type
&& loopZRR
.dr
.d_type
!= QType::RRSIG
) {
1022 ne
.d_set
.set(loopZRR
.dr
.d_type
);
1027 if (!loopZRR
.dr
.d_type
)
1028 continue; // skip empty non-terminals
1030 if(loopZRR
.dr
.d_type
== QType::SOA
)
1031 continue; // skip SOA - would indicate end of AXFR
1033 if(csp
.submit(loopZRR
)) {
1035 outpacket
->getRRS() = csp
.getChunk();
1036 if(!outpacket
->getRRS().empty()) {
1037 if(haveTSIGDetails
&& !tsigkeyname
.empty())
1038 outpacket
->setTSIGDetails(trc
, tsigkeyname
, tsigsecret
, trc
.d_mac
, true);
1039 sendPacket(outpacket
, outsock
, false);
1040 trc
.d_mac
=outpacket
->d_trc
.d_mac
;
1041 outpacket
=getFreshAXFRPacket(q
);
1049 udiff=dt.udiffNoReset();
1050 cerr<<"Starting NSEC: "<<csp.d_signed/(udiff/1000000.0)<<" sigs/s, "<<csp.d_signed<<" / "<<udiff/1000000.0<<endl;
1051 cerr<<"Outstanding: "<<csp.d_outstanding<<", "<<csp.d_queued - csp.d_signed << endl;
1052 cerr<<"Ready for consumption: "<<csp.getReady()<<endl;
1056 for(nsecxrepo_t::const_iterator iter
= nsecxrepo
.begin(); iter
!= nsecxrepo
.end(); ++iter
) {
1057 if(iter
->second
.d_auth
) {
1058 NSEC3RecordContent n3rc
;
1059 n3rc
.set(iter
->second
.d_set
);
1060 const auto numberOfTypesSet
= n3rc
.numberOfTypesSet();
1061 if (numberOfTypesSet
!= 0 && (numberOfTypesSet
!= 1 || !n3rc
.isSet(QType::NS
))) {
1062 n3rc
.set(QType::RRSIG
);
1064 n3rc
.d_salt
= ns3pr
.d_salt
;
1065 n3rc
.d_flags
= ns3pr
.d_flags
;
1066 n3rc
.d_iterations
= ns3pr
.d_iterations
;
1067 n3rc
.d_algorithm
= DNSSECKeeper::DIGEST_SHA1
; // SHA1, fixed in PowerDNS for now
1068 nsecxrepo_t::const_iterator inext
= iter
;
1070 if(inext
== nsecxrepo
.end())
1071 inext
= nsecxrepo
.begin();
1072 while(!inext
->second
.d_auth
&& inext
!= iter
)
1075 if(inext
== nsecxrepo
.end())
1076 inext
= nsecxrepo
.begin();
1078 n3rc
.d_nexthash
= fromBase32Hex(inext
->first
.toStringNoDot());
1079 zrr
.dr
.d_name
= iter
->first
+sd
.qname
;
1081 zrr
.dr
.d_ttl
= sd
.getNegativeTTL();
1082 zrr
.dr
.setContent(std::make_shared
<NSEC3RecordContent
>(std::move(n3rc
)));
1083 zrr
.dr
.d_type
= QType::NSEC3
;
1084 zrr
.dr
.d_place
= DNSResourceRecord::ANSWER
;
1086 if(csp
.submit(zrr
)) {
1088 outpacket
->getRRS() = csp
.getChunk();
1089 if(!outpacket
->getRRS().empty()) {
1090 if(haveTSIGDetails
&& !tsigkeyname
.empty())
1091 outpacket
->setTSIGDetails(trc
, tsigkeyname
, tsigsecret
, trc
.d_mac
, true);
1092 sendPacket(outpacket
, outsock
, false);
1093 trc
.d_mac
=outpacket
->d_trc
.d_mac
;
1094 outpacket
=getFreshAXFRPacket(q
);
1103 else for(nsecxrepo_t::const_iterator iter
= nsecxrepo
.begin(); iter
!= nsecxrepo
.end(); ++iter
) {
1104 NSECRecordContent nrc
;
1105 nrc
.set(iter
->second
.d_set
);
1106 nrc
.set(QType::RRSIG
);
1107 nrc
.set(QType::NSEC
);
1109 if(boost::next(iter
) != nsecxrepo
.end())
1110 nrc
.d_next
= boost::next(iter
)->first
;
1112 nrc
.d_next
=nsecxrepo
.begin()->first
;
1113 zrr
.dr
.d_name
= iter
->first
;
1115 zrr
.dr
.d_ttl
= sd
.getNegativeTTL();
1116 zrr
.dr
.setContent(std::make_shared
<NSECRecordContent
>(std::move(nrc
)));
1117 zrr
.dr
.d_type
= QType::NSEC
;
1118 zrr
.dr
.d_place
= DNSResourceRecord::ANSWER
;
1120 if(csp
.submit(zrr
)) {
1122 outpacket
->getRRS() = csp
.getChunk();
1123 if(!outpacket
->getRRS().empty()) {
1124 if(haveTSIGDetails
&& !tsigkeyname
.empty())
1125 outpacket
->setTSIGDetails(trc
, tsigkeyname
, tsigsecret
, trc
.d_mac
, true);
1126 sendPacket(outpacket
, outsock
, false);
1127 trc
.d_mac
=outpacket
->d_trc
.d_mac
;
1128 outpacket
=getFreshAXFRPacket(q
);
1137 udiff=dt.udiffNoReset();
1138 cerr<<"Flushing pipe: "<<csp.d_signed/(udiff/1000000.0)<<" sigs/s, "<<csp.d_signed<<" / "<<udiff/1000000.0<<endl;
1139 cerr<<"Outstanding: "<<csp.d_outstanding<<", "<<csp.d_queued - csp.d_signed << endl;
1140 cerr<<"Ready for consumption: "<<csp.getReady()<<endl;
1143 outpacket
->getRRS() = csp
.getChunk(true); // flush the pipe
1144 if(!outpacket
->getRRS().empty()) {
1145 if(haveTSIGDetails
&& !tsigkeyname
.empty())
1146 outpacket
->setTSIGDetails(trc
, tsigkeyname
, tsigsecret
, trc
.d_mac
, true); // first answer is 'normal'
1148 sendPacket(outpacket
, outsock
, false);
1150 catch (PDNSException
& pe
) {
1151 throw PDNSException("during axfr-out of "+target
.toString()+", this happened: "+pe
.reason
);
1153 trc
.d_mac
=outpacket
->d_trc
.d_mac
;
1154 outpacket
=getFreshAXFRPacket(q
);
1160 udiff
=dt
.udiffNoReset();
1162 g_log
<<Logger::Debug
<<logPrefix
<<"done signing: "<<csp
.d_signed
/(udiff
/1000000.0)<<" sigs/s, "<<endl
;
1164 DLOG(g_log
<<logPrefix
<<"done writing out records"<<endl
);
1165 /* and terminate with yet again the SOA record */
1166 outpacket
=getFreshAXFRPacket(q
);
1167 outpacket
->addRecord(std::move(soa
));
1168 if(haveTSIGDetails
&& !tsigkeyname
.empty())
1169 outpacket
->setTSIGDetails(trc
, tsigkeyname
, tsigsecret
, trc
.d_mac
, true);
1171 sendPacket(outpacket
, outsock
);
1173 DLOG(g_log
<<logPrefix
<<"last packet - close"<<endl
);
1174 g_log
<<Logger::Notice
<<logPrefix
<<"AXFR finished"<<endl
;
1179 int TCPNameserver::doIXFR(std::unique_ptr
<DNSPacket
>& q
, int outsock
)
1181 string logPrefix
="IXFR-out zone '"+q
->qdomain
.toLogString()+"', client '"+q
->getRemoteStringWithPort()+"', ";
1183 std::unique_ptr
<DNSPacket
> outpacket
=getFreshAXFRPacket(q
);
1185 outpacket
->d_dnssecOk
=true; // RFC 5936, 2.2.5 'SHOULD'
1187 uint32_t serial
= 0;
1188 MOADNSParser
mdp(false, q
->getString());
1189 for(const auto & answer
: mdp
.d_answers
) {
1190 const DNSRecord
*rr
= &answer
.first
;
1191 if (rr
->d_type
== QType::SOA
&& rr
->d_place
== DNSResourceRecord::AUTHORITY
) {
1192 vector
<string
>parts
;
1193 stringtok(parts
, rr
->getContent()->getZoneRepresentation());
1194 if (parts
.size() >= 3) {
1196 pdns::checked_stoi_into(serial
, parts
[2]);
1198 catch(const std::out_of_range
& oor
) {
1199 g_log
<<Logger::Warning
<<logPrefix
<<"invalid serial in IXFR query"<<endl
;
1200 outpacket
->setRcode(RCode::FormErr
);
1201 sendPacket(outpacket
,outsock
);
1205 g_log
<<Logger::Warning
<<logPrefix
<<"no serial in IXFR query"<<endl
;
1206 outpacket
->setRcode(RCode::FormErr
);
1207 sendPacket(outpacket
,outsock
);
1210 } else if (rr
->d_type
!= QType::TSIG
&& rr
->d_type
!= QType::OPT
) {
1211 g_log
<<Logger::Warning
<<logPrefix
<<"additional records in IXFR query, type: "<<QType(rr
->d_type
).toString()<<endl
;
1212 outpacket
->setRcode(RCode::FormErr
);
1213 sendPacket(outpacket
,outsock
);
1218 g_log
<<Logger::Warning
<<logPrefix
<<"transfer initiated with serial "<<serial
<<endl
;
1220 // determine if zone exists, XFR is allowed, and if IXFR can proceed using existing backend before spawning a new backend.
1223 bool serialPermitsIXFR
;
1225 auto packetHandler
= s_P
.lock();
1226 DLOG(g_log
<<logPrefix
<<"Looking for SOA"<<endl
); // find domain_id via SOA and list complete domain. No SOA, no IXFR
1227 if(!*packetHandler
) {
1228 g_log
<<Logger::Warning
<<"TCP server is without backend connections in doIXFR, launching"<<endl
;
1229 *packetHandler
= make_unique
<PacketHandler
>();
1232 // canDoAXFR does all the ACL checks, and has the if(disable-axfr) shortcut, call it first.
1233 if(!canDoAXFR(q
, false, *packetHandler
) || !(*packetHandler
)->getBackend()->getSOAUncached(q
->qdomain
, sd
)) {
1234 g_log
<<Logger::Warning
<<logPrefix
<<"failed: not authoritative"<<endl
;
1235 outpacket
->setRcode(RCode::NotAuth
);
1236 sendPacket(outpacket
,outsock
);
1240 DNSSECKeeper
dk((*packetHandler
)->getBackend());
1241 DNSSECKeeper::clearCaches(q
->qdomain
);
1242 bool narrow
= false;
1243 securedZone
= dk
.isSecuredZone(q
->qdomain
);
1244 if(dk
.getNSEC3PARAM(q
->qdomain
, nullptr, &narrow
)) {
1246 g_log
<<Logger::Warning
<<logPrefix
<<"not doing IXFR of an NSEC3 narrow zone"<<endl
;
1247 outpacket
->setRcode(RCode::Refused
);
1248 sendPacket(outpacket
,outsock
);
1253 serialPermitsIXFR
= !rfc1982LessThan(serial
, calculateEditSOA(sd
.serial
, dk
, sd
.qname
));
1256 if (serialPermitsIXFR
) {
1257 DNSName target
= q
->qdomain
;
1258 TSIGRecordContent trc
;
1259 DNSName tsigkeyname
;
1263 DNSSECKeeper
dk(&db
);
1265 bool haveTSIGDetails
= q
->getTSIGDetails(&trc
, &tsigkeyname
);
1267 if(haveTSIGDetails
&& !tsigkeyname
.empty()) {
1269 DNSName algorithm
=trc
.d_algoName
; // FIXME400: was toLowerCanonic, compare output
1270 if (algorithm
== DNSName("hmac-md5.sig-alg.reg.int"))
1271 algorithm
= DNSName("hmac-md5");
1272 if (!db
.getTSIGKey(tsigkeyname
, algorithm
, tsig64
)) {
1273 g_log
<< Logger::Error
<< "TSIG key '" << tsigkeyname
<< "' for domain '" << target
<< "' not found" << endl
;
1276 if (B64Decode(tsig64
, tsigsecret
) == -1) {
1277 g_log
<<Logger::Error
<<logPrefix
<<"unable to Base-64 decode TSIG key '"<<tsigkeyname
<<"'"<<endl
;
1282 // SOA *must* go out first, our signing pipe might reorder
1283 DLOG(g_log
<<logPrefix
<<"sending out SOA"<<endl
);
1284 DNSZoneRecord soa
= makeEditedDNSZRFromSOAData(dk
, sd
);
1285 outpacket
->addRecord(std::move(soa
));
1286 if(securedZone
&& outpacket
->d_dnssecOk
) {
1287 set
<DNSName
> authSet
;
1288 authSet
.insert(target
);
1289 addRRSigs(dk
, db
, authSet
, outpacket
->getRRS());
1292 if(haveTSIGDetails
&& !tsigkeyname
.empty())
1293 outpacket
->setTSIGDetails(trc
, tsigkeyname
, tsigsecret
, trc
.d_mac
); // first answer is 'normal'
1295 sendPacket(outpacket
, outsock
);
1297 g_log
<<Logger::Notice
<<logPrefix
<<"IXFR finished"<<endl
;
1302 g_log
<<Logger::Notice
<<logPrefix
<<"IXFR fallback to AXFR"<<endl
;
1303 return doAXFR(q
->qdomain
, q
, outsock
);
1306 TCPNameserver::~TCPNameserver()
1310 TCPNameserver::TCPNameserver()
1312 d_maxTransactionsPerConn
= ::arg().asNum("max-tcp-transactions-per-conn");
1313 d_idleTimeout
= ::arg().asNum("tcp-idle-timeout");
1314 d_maxConnectionDuration
= ::arg().asNum("max-tcp-connection-duration");
1315 d_maxConnectionsPerClient
= ::arg().asNum("max-tcp-connections-per-client");
1317 // sem_init(&d_connectionroom_sem,0,::arg().asNum("max-tcp-connections"));
1318 d_connectionroom_sem
= make_unique
<Semaphore
>( ::arg().asNum( "max-tcp-connections" ));
1319 d_maxTCPConnections
= ::arg().asNum( "max-tcp-connections" );
1321 vector
<string
>locals
;
1322 stringtok(locals
,::arg()["local-address"]," ,");
1324 throw PDNSException("No local addresses specified");
1326 d_ng
.toMasks(::arg()["allow-axfr-ips"] );
1328 signal(SIGPIPE
,SIG_IGN
);
1330 for(auto const &laddr
: locals
) {
1331 ComboAddress
local(laddr
, ::arg().asNum("local-port"));
1333 int s
=socket(local
.sin4
.sin_family
, SOCK_STREAM
, 0);
1335 throw PDNSException("Unable to acquire TCP socket: "+stringerror());
1339 if(setsockopt(s
, SOL_SOCKET
,SO_REUSEADDR
, (char*)&tmp
, sizeof tmp
) < 0) {
1340 g_log
<<Logger::Error
<<"Setsockopt failed"<<endl
;
1344 if (::arg().asNum("tcp-fast-open") > 0) {
1346 int fastOpenQueueSize
= ::arg().asNum("tcp-fast-open");
1347 if (setsockopt(s
, IPPROTO_TCP
, TCP_FASTOPEN
, &fastOpenQueueSize
, sizeof fastOpenQueueSize
) < 0) {
1348 g_log
<<Logger::Error
<<"Failed to enable TCP Fast Open for listening socket "<<local
.toStringWithPort()<<": "<<stringerror()<<endl
;
1351 g_log
<<Logger::Warning
<<"TCP Fast Open configured but not supported for listening socket"<<endl
;
1355 if(::arg().mustDo("non-local-bind"))
1356 Utility::setBindAny(local
.sin4
.sin_family
, s
);
1358 if(local
.isIPv6() && setsockopt(s
, IPPROTO_IPV6
, IPV6_V6ONLY
, &tmp
, sizeof(tmp
)) < 0) {
1359 g_log
<<Logger::Error
<<"Failed to set IPv6 socket to IPv6 only, continuing anyhow: "<<stringerror()<<endl
;
1362 if(::bind(s
, (sockaddr
*)&local
, local
.getSocklen())<0) {
1365 if( err
== EADDRNOTAVAIL
&& ! ::arg().mustDo("local-address-nonexist-fail") ) {
1366 g_log
<<Logger::Error
<<"Address " << local
.toString() << " does not exist on this server - skipping TCP bind" << endl
;
1369 g_log
<<Logger::Error
<<"Unable to bind to TCP socket " << local
.toStringWithPort() << ": "<<stringerror(err
)<<endl
;
1370 throw PDNSException("Unable to bind to TCP socket");
1375 g_log
<<Logger::Error
<<"TCP server bound to "<<local
.toStringWithPort()<<endl
;
1376 d_sockets
.push_back(s
);
1378 memset(&pfd
, 0, sizeof(pfd
));
1380 pfd
.events
= POLLIN
;
1381 d_prfds
.push_back(pfd
);
1386 //! Start of TCP operations thread, we launch a new thread for each incoming TCP question
1387 void TCPNameserver::thread()
1389 setThreadName("pdns/tcpnameser");
1393 ComboAddress remote
;
1394 Utility::socklen_t addrlen
=remote
.getSocklen();
1396 int ret
=poll(&d_prfds
[0], d_prfds
.size(), -1); // blocks, forever if need be
1401 for(const pollfd
& pfd
: d_prfds
) {
1402 if(pfd
.revents
& POLLIN
) {
1404 remote
.sin4
.sin_family
= AF_INET6
;
1405 addrlen
=remote
.getSocklen();
1407 if((fd
=accept(sock
, (sockaddr
*)&remote
, &addrlen
))<0) {
1409 g_log
<<Logger::Error
<<"TCP question accept error: "<<stringerror(err
)<<endl
;
1412 g_log
<<Logger::Error
<<"TCP handler out of filedescriptors, exiting, won't recover from this"<<endl
;
1417 if (d_maxConnectionsPerClient
) {
1418 auto clientsCount
= s_clientsCount
.lock();
1419 if ((*clientsCount
)[remote
] >= d_maxConnectionsPerClient
) {
1420 g_log
<<Logger::Notice
<<"Limit of simultaneous TCP connections per client reached for "<< remote
<<", dropping"<<endl
;
1424 (*clientsCount
)[remote
]++;
1427 d_connectionroom_sem
->wait(); // blocks if no connections are available
1430 d_connectionroom_sem
->getValue( &room
);
1432 g_log
<<Logger::Warning
<<"Limit of simultaneous TCP connections reached - raise max-tcp-connections"<<endl
;
1435 std::thread
connThread(doConnection
, fd
);
1436 connThread
.detach();
1438 catch (std::exception
& e
) {
1439 g_log
<<Logger::Error
<<"Error creating thread: "<<e
.what()<<endl
;
1440 d_connectionroom_sem
->post();
1442 decrementClientCount(remote
);
1449 catch(PDNSException
&AE
) {
1450 g_log
<<Logger::Error
<<"TCP Nameserver thread dying because of fatal error: "<<AE
.reason
<<endl
;
1453 g_log
<<Logger::Error
<<"TCPNameserver dying because of an unexpected fatal error"<<endl
;
1455 _exit(1); // take rest of server with us
1459 unsigned int TCPNameserver::numTCPConnections()
1462 d_connectionroom_sem
->getValue( &room
);
1463 return d_maxTCPConnections
- room
;