From eeaa1cb6a26c50e64f10a300f031780171e1536a Mon Sep 17 00:00:00 2001 From: Otto Moerbeek Date: Tue, 11 Jan 2022 11:37:17 +0100 Subject: [PATCH] Split out rec-main.cc --- pdns/pdns_recursor.cc | 2894 ++------------------------------- pdns/recursordist/rec-main.cc | 2569 +++++++++++++++++++++++++++++ pdns/recursordist/rec-main.hh | 173 +- 3 files changed, 2842 insertions(+), 2794 deletions(-) diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index 5853c1339b..94fc94fc73 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -22,112 +22,32 @@ #include "rec-main.hh" -#include -#include -#include -#ifdef HAVE_BOOST_CONTAINER_FLAT_SET_HPP -#include -#endif -#include "ws-recursor.hh" -#include -#include "threadname.hh" -#include "recpacketcache.hh" -#include "utility.hh" -#include "dns_random.hh" -#ifdef HAVE_LIBSODIUM -#include -#endif -#include "opensslsigners.hh" -#include -#include -#include -#include -#include -#include "recursor_cache.hh" -#include "cachecleaner.hh" -#include -#include -#include -#include "misc.hh" -#include "mtasker.hh" -#include #include "arguments.hh" -#include "syncres.hh" -#include -#include -#include "sortlist.hh" -#include "sstuff.hh" -#include -#include -#include -#include -#include -#ifdef MALLOC_TRACE -#include "malloctrace.hh" -#endif -#include -#include "aggressive_nsec.hh" -#include "capabilities.hh" -#include "dnsparser.hh" -#include "dnswriter.hh" -#include "dnsrecords.hh" -#include "zoneparser-tng.hh" -#include "rec_channel.hh" -#include "logger.hh" -#include "logging.hh" -#include "iputils.hh" -#include "mplexer.hh" -#include "config.h" -#include "lua-recursor4.hh" -#include "version.hh" -#include "responsestats.hh" -#include "secpoll-recursor.hh" -#include "dnsname.hh" -#include "filterpo.hh" -#include "rpzloader.hh" -#include "validate-recursor.hh" -#include "rec-lua-conf.hh" -#include "ednsoptions.hh" +#include "dns_random.hh" #include "ednsextendederror.hh" #include "ednspadding.hh" -#include "gettime.hh" -#include "proxy-protocol.hh" -#include "pubsuffix.hh" -#include "shuffle.hh" -#ifdef NOD_ENABLED -#include "nod.hh" -#include "logging.hh" -#endif /* NOD_ENABLED */ #include "query-local-address.hh" -#include "rec-tcpout.hh" - -#include "rec-snmp.hh" #include "rec-taskqueue.hh" +#include "responsestats.hh" +#include "shuffle.hh" +#include "validate-recursor.hh" +#include "xpf.hh" #ifdef HAVE_SYSTEMD #include #endif -#include "namespaces.hh" - -#include "uuid-utils.hh" -#include "rec-protozero.hh" - -#include "xpf.hh" -#include "rec-eventtrace.hh" +#ifdef NOD_ENABLED +#include "nod.hh" +#include "logging.hh" +#endif /* NOD_ENABLED */ thread_local std::shared_ptr t_pdl; thread_local unsigned int t_id = 0; -static thread_local std::shared_ptr t_traceRegex; +thread_local std::shared_ptr t_traceRegex; thread_local std::shared_ptr>> t_protobufServers{nullptr}; -static thread_local uint64_t t_protobufServersGeneration; thread_local std::shared_ptr>> t_outgoingProtobufServers{nullptr}; -static thread_local uint64_t t_outgoingProtobufServersGeneration; -#ifdef HAVE_FSTRM -static thread_local std::shared_ptr>> t_frameStreamServers{nullptr}; -static thread_local uint64_t t_frameStreamServersGeneration; -#endif /* HAVE_FSTRM */ thread_local std::unique_ptr MT; // the big MTasker std::unique_ptr g_recCache; @@ -140,121 +60,40 @@ thread_local std::unique_ptr > > thread_local std::shared_ptr t_allowFrom; thread_local std::shared_ptr t_allowNotifyFrom; thread_local std::shared_ptr t_allowNotifyFor; -#ifdef NOD_ENABLED -thread_local std::shared_ptr t_nodDBp; -thread_local std::shared_ptr t_udrDBp; -#endif /* NOD_ENABLED */ __thread struct timeval g_now; // timestamp, updated (too) frequently -// for communicating with our threads -// effectively readonly after startup -struct RecThreadInfo -{ - struct ThreadPipeSet - { - int writeToThread{-1}; - int readToThread{-1}; - int writeFromThread{-1}; - int readFromThread{-1}; - int writeQueriesToThread{-1}; // this one is non-blocking - int readQueriesToThread{-1}; - }; - - /* FD corresponding to TCP sockets this thread is listening - on. - These FDs are also in deferredAdds when we have one - socket per listener, and in g_deferredAdds instead. */ - std::set tcpSockets; - /* FD corresponding to listening sockets if we have one socket per - listener (with reuseport), otherwise all listeners share the - same FD and g_deferredAdds is then used instead */ - deferredAdd_t deferredAdds; - struct ThreadPipeSet pipes; - std::thread thread; - MT_t* mt{nullptr}; - uint64_t numberOfDistributedQueries{0}; - int exitCode{0}; - /* handle the web server, carbon, statistics and the control channel */ - bool isHandler{false}; - /* accept incoming queries (and distributes them to the workers if pdns-distributes-queries is set) */ - bool isListener{false}; - /* process queries */ - bool isWorker{false}; -}; - -/* first we have the handler thread, t_id == 0 (some other - helper threads like SNMP might have t_id == 0 as well) - then the distributor threads if any - and finally the workers */ -static std::vector s_threadInfos; -/* without reuseport, all listeners share the same sockets */ -static deferredAdd_t g_deferredAdds; typedef map listenSocketsAddresses_t; // is shared across all threads right now -enum class PaddingMode { Always, PaddedQueries }; static listenSocketsAddresses_t g_listenSocketsAddresses; // is shared across all threads right now static set g_fromtosockets; // listen sockets that use 'sendfromto()' mechanism (without actually using sendfromto()) -static std::atomic counter; -static std::shared_ptr g_initialDomainMap; // new threads needs this to be setup -static std::shared_ptr g_initialAllowFrom; // new thread needs to be setup with this -static std::shared_ptr g_initialAllowNotifyFrom; // new threads need this to be setup -static std::shared_ptr g_initialAllowNotifyFor; // new threads need this to be setup NetmaskGroup g_XPFAcl; -static NetmaskGroup g_proxyProtocolACL; NetmaskGroup g_paddingFrom; -static boost::optional g_dns64Prefix{boost::none}; -static DNSName g_dns64PrefixReverse; size_t g_proxyProtocolMaximumSize; -static size_t s_maxUDPQueriesPerRound; -static uint64_t g_latencyStatSize; -static uint32_t g_disthashseed; +size_t s_maxUDPQueriesPerRound; unsigned int g_maxMThreads; -static unsigned int g_numDistributorThreads; -static unsigned int g_numWorkerThreads; unsigned int g_paddingTag; -static uint16_t g_udpTruncationThreshold; -static uint16_t g_xpfRRCode{0}; -static PaddingMode g_paddingMode; -static std::atomic statsWanted; +PaddingMode g_paddingMode; +uint16_t g_udpTruncationThreshold; std::atomic g_quiet; bool g_logCommonErrors; -static bool g_weDistributeQueries; // if true, 1 or more threads listen on the incoming query sockets and distribute them to workers bool g_reusePort{false}; bool g_gettagNeedsEDNSOptions{false}; -static time_t g_statisticsInterval; -static bool g_useIncomingECS; -static bool g_useKernelTimestamp; +bool g_useKernelTimestamp; std::atomic g_maxCacheEntries, g_maxPacketCacheEntries; -#ifdef NOD_ENABLED -static bool g_nodEnabled; -static DNSName g_nodLookupDomain; -static bool g_nodLog; -static SuffixMatchNode g_nodDomainWL; -static std::string g_nod_pbtag; -static bool g_udrEnabled; -static bool g_udrLog; -static std::string g_udr_pbtag; -#endif /* NOD_ENABLED */ #ifdef HAVE_BOOST_CONTAINER_FLAT_SET_HPP -static boost::container::flat_set s_avoidUdpSourcePorts; +boost::container::flat_set s_avoidUdpSourcePorts; #else -static std::set s_avoidUdpSourcePorts; +std::set s_avoidUdpSourcePorts; #endif -static uint16_t s_minUdpSourcePort; -static uint16_t s_maxUdpSourcePort; -static double s_balancingFactor; -static bool s_addExtendedResolutionDNSErrors; +uint16_t s_minUdpSourcePort; +uint16_t s_maxUdpSourcePort; +double s_balancingFactor; -RecursorControlChannel s_rcc; // only active in the handler thread RecursorStats g_stats; -string s_programname="pdns_recursor"; -string s_pidfname; bool g_lowercaseOutgoing; unsigned int g_networkTimeoutMsec; -unsigned int g_numThreads; uint16_t g_outgoingEDNSBufsize; -bool g_logRPZChanges{false}; // Used in Syncres to counts DNSSEC stats for names in a different "universe" GlobalStateHolder g_xdnssec; @@ -262,118 +101,9 @@ GlobalStateHolder g_xdnssec; GlobalStateHolder g_dontThrottleNames; GlobalStateHolder g_dontThrottleNetmasks; GlobalStateHolder g_DoTToAuthNames; +uint64_t g_latencyStatSize; -#define LOCAL_NETS "127.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 169.254.0.0/16, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fc00::/7, fe80::/10" -#define LOCAL_NETS_INVERSE "!127.0.0.0/8, !10.0.0.0/8, !100.64.0.0/10, !169.254.0.0/16, !192.168.0.0/16, !172.16.0.0/12, !::1/128, !fc00::/7, !fe80::/10" -// Bad Nets taken from both: -// http://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml -// and -// http://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml -// where such a network may not be considered a valid destination -#define BAD_NETS "0.0.0.0/8, 192.0.0.0/24, 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24, 240.0.0.0/4, ::/96, ::ffff:0:0/96, 100::/64, 2001:db8::/32" -#define DONT_QUERY LOCAL_NETS ", " BAD_NETS - -struct ThreadMSG -{ - pipefunc_t func; - bool wantAnswer; -}; - - -ArgvMap &arg() -{ - static ArgvMap theArg; - return theArg; -} - - -static bool isDistributorThread() -{ - if (t_id == 0) { - return false; - } - - return g_weDistributeQueries && s_threadInfos.at(t_id).isListener; -} - -static bool isHandlerThread() -{ - if (t_id == 0) { - return true; - } - - return s_threadInfos.at(t_id).isHandler; -} - - -static void handleGenUDPQueryResponse(int fd, FDMultiplexer::funcparam_t& var) -{ - std::shared_ptr pident = boost::any_cast>(var); - PacketBuffer resp; - resp.resize(512); - ComboAddress fromaddr; - socklen_t addrlen = sizeof(fromaddr); - - ssize_t ret = recvfrom(fd, resp.data(), resp.size(), 0, (sockaddr *)&fromaddr, &addrlen); - if (fromaddr != pident->remote) { - g_log<remote.toStringWithPort()<<"), discarding"<removeReadFD(fd); - if(ret >= 0) { - MT->sendEvent(pident, &resp); - } - else { - PacketBuffer empty; - MT->sendEvent(pident, &empty); - // cerr<<"Had some kind of error: "< pident = std::make_shared(); - pident->fd = s.getHandle(); - pident->remote = dest; - pident->type = 0; - t_fdm->addReadFD(s.getHandle(), handleGenUDPQueryResponse, pident); - - PacketBuffer data; - int ret=MT->waitEvent(pident, &data, g_networkTimeoutMsec); - - if (!ret || ret==-1) { // timeout - t_fdm->removeReadFD(s.getHandle()); - } - else if(data.empty()) {// error, EOF or other - // we could special case this - return data; - } - return data; -} - -static void handleUDPServerResponse(int fd, FDMultiplexer::funcparam_t&); - -// you can ask this class for a UDP socket to send a query from -// this socket is not yours, don't even think about deleting it -// but after you call 'returnSocket' on it, don't assume anything anymore -class UDPClientSocks -{ - unsigned int d_numsocks; -public: - UDPClientSocks() : d_numsocks(0) - { - } - - LWResult::Result getSocket(const ComboAddress& toaddr, int* fd) +LWResult::Result UDPClientSocks::getSocket(const ComboAddress& toaddr, int* fd) { *fd = makeClientSocket(toaddr.sin4.sin_family); if(*fd < 0) { // temporary error - receive exception otherwise @@ -401,7 +131,7 @@ public: } // return a socket to the pool, or simply erase it - void returnSocket(int fd) +void UDPClientSocks::returnSocket(int fd) { try { t_fdm->removeReadFD(fd); @@ -420,10 +150,9 @@ public: --d_numsocks; } -private: // returns -1 for errors which might go away, throws for ones that won't - static int makeClientSocket(int family) +int UDPClientSocks::makeClientSocket(int family) { int ret = socket(family, SOCK_DGRAM, 0); // turns out that setting CLO_EXEC and NONBLOCK from here is not a performance win on Linux (oddly enough) @@ -473,9 +202,66 @@ private: } return ret; } -}; -static thread_local std::unique_ptr t_udpclientsocks; + +static void handleGenUDPQueryResponse(int fd, FDMultiplexer::funcparam_t& var) +{ + std::shared_ptr pident = boost::any_cast>(var); + PacketBuffer resp; + resp.resize(512); + ComboAddress fromaddr; + socklen_t addrlen = sizeof(fromaddr); + + ssize_t ret = recvfrom(fd, resp.data(), resp.size(), 0, (sockaddr *)&fromaddr, &addrlen); + if (fromaddr != pident->remote) { + g_log<remote.toStringWithPort()<<"), discarding"<removeReadFD(fd); + if(ret >= 0) { + MT->sendEvent(pident, &resp); + } + else { + PacketBuffer empty; + MT->sendEvent(pident, &empty); + // cerr<<"Had some kind of error: "< pident = std::make_shared(); + pident->fd = s.getHandle(); + pident->remote = dest; + pident->type = 0; + t_fdm->addReadFD(s.getHandle(), handleGenUDPQueryResponse, pident); + + PacketBuffer data; + int ret=MT->waitEvent(pident, &data, g_networkTimeoutMsec); + + if (!ret || ret==-1) { // timeout + t_fdm->removeReadFD(s.getHandle()); + } + else if(data.empty()) {// error, EOF or other + // we could special case this + return data; + } + return data; +} + +static void handleUDPServerResponse(int fd, FDMultiplexer::funcparam_t&); + + +thread_local std::unique_ptr t_udpclientsocks; /* these two functions are used by LWRes */ LWResult::Result asendto(const char *data, size_t len, int flags, @@ -567,19 +353,6 @@ LWResult::Result arecvfrom(PacketBuffer& packet, int flags, const ComboAddress& return ret == 0 ? LWResult::Result::Timeout : LWResult::Result::PermanentError; } -static void writePid(void) -{ - if(!::arg().mustDo("write-pid")) - return; - ofstream of(s_pidfname.c_str(), std::ios_base::app); - if(of) - of<< Utility::getpid() <& luaconfsLocal, const boost::uuids::uuid& uniqueId, const ComboAddress& remote, const ComboAddress& local, const Netmask& ednssubnet, bool tcp, uint16_t id, size_t len, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::unordered_set& policyTags, const std::string& requestorId, const std::string& deviceId, const std::string& deviceName, const std::map& meta) -{ - if (!t_protobufServers) { - return; - } - - Netmask requestorNM(remote, remote.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6); - ComboAddress requestor = requestorNM.getMaskedNetwork(); - requestor.setPort(remote.getPort()); - - pdns::ProtoZero::RecMessage m{128, std::string::size_type(policyTags.empty() ? 0 : 64)}; // It's a guess - m.setType(pdns::ProtoZero::Message::MessageType::DNSQueryType); - m.setRequest(uniqueId, requestor, local, qname, qtype, qclass, id, tcp ? pdns::ProtoZero::Message::TransportProtocol::TCP : pdns::ProtoZero::Message::TransportProtocol::UDP, len); - m.setServerIdentity(SyncRes::s_serverID); - m.setEDNSSubnet(ednssubnet, ednssubnet.isIPv4() ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6); - m.setRequestorId(requestorId); - m.setDeviceId(deviceId); - m.setDeviceName(deviceName); - - if (!policyTags.empty()) { - m.addPolicyTags(policyTags); - } - for (const auto& mit : meta) { - m.setMeta(mit.first, mit.second.stringVal, mit.second.intVal); - } - - std::string msg(m.finishAndMoveBuf()); - for (auto& server : *t_protobufServers) { - server->queueData(msg); - } -} - -void protobufLogResponse(pdns::ProtoZero::RecMessage& message) -{ - if (!t_protobufServers) { - return; - } - - std::string msg(message.finishAndMoveBuf()); - for (auto& server : *t_protobufServers) { - server->queueData(msg); - } -} - -void protobufLogResponse(const struct dnsheader* dh, LocalStateHolder& luaconfsLocal, - const RecursorPacketCache::OptPBData& pbData, const struct timeval& tv, - bool tcp, const ComboAddress& source, const ComboAddress& destination, - const EDNSSubnetOpts& ednssubnet, - const boost::uuids::uuid& uniqueId, const string& requestorId, const string& deviceId, - const string& deviceName, const std::map& meta, - const RecEventTrace& eventTrace) -{ - pdns::ProtoZero::RecMessage pbMessage(pbData ? pbData->d_message : "", pbData ? pbData->d_response : "", 64, 10); // The extra bytes we are going to add - // Normally we take the immutable string from the cache and append a few values, but if it's not there (can this happen?) - // we start with an empty string and append the minimal - if (!pbData) { - pbMessage.setType(pdns::ProtoZero::Message::MessageType::DNSResponseType); - pbMessage.setServerIdentity(SyncRes::s_serverID); - } - - // In response part - if (g_useKernelTimestamp && tv.tv_sec) { - pbMessage.setQueryTime(tv.tv_sec, tv.tv_usec); - } - else { - pbMessage.setQueryTime(g_now.tv_sec, g_now.tv_usec); - } - - // In message part - Netmask requestorNM(source, source.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6); - ComboAddress requestor = requestorNM.getMaskedNetwork(); - pbMessage.setMessageIdentity(uniqueId); - pbMessage.setFrom(requestor); - pbMessage.setTo(destination); - pbMessage.setSocketProtocol(tcp ? pdns::ProtoZero::Message::TransportProtocol::TCP : pdns::ProtoZero::Message::TransportProtocol::UDP); - pbMessage.setId(dh->id); - - pbMessage.setTime(); - pbMessage.setEDNSSubnet(ednssubnet.source, ednssubnet.source.isIPv4() ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6); - pbMessage.setRequestorId(requestorId); - pbMessage.setDeviceId(deviceId); - pbMessage.setDeviceName(deviceName); - pbMessage.setFromPort(source.getPort()); - pbMessage.setToPort(destination.getPort()); - for (const auto& m : meta) { - pbMessage.setMeta(m.first, m.second.stringVal, m.second.intVal); - } -#ifdef NOD_ENABLED - if (g_nodEnabled) { - pbMessage.setNewlyObservedDomain(false); - } -#endif - if (eventTrace.enabled() && SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_pb) { - pbMessage.addEvents(eventTrace); - } - protobufLogResponse(pbMessage); -} /** * Chases the CNAME provided by the PolicyCustom RPZ policy. @@ -876,185 +552,33 @@ static PolicyResult handlePolicyHit(const DNSFilterEngine::Policy& appliedPolicy return PolicyResult::NoAction; } -static std::shared_ptr>> startProtobufServers(const ProtobufExportConfig& config) -{ - auto result = std::make_shared>>(); - - for (const auto& server : config.servers) { - try { - auto logger = make_unique(server, config.timeout, 100*config.maxQueuedEntries, config.reconnectWaitTime, config.asyncConnect); - logger->setLogQueries(config.logQueries); - logger->setLogResponses(config.logResponses); - result->emplace_back(std::move(logger)); - } - catch(const std::exception& e) { - g_log<& luaconfsLocal) +#ifdef NOD_ENABLED +static bool nodCheckNewDomain(const shared_ptr& nodlogger, const DNSName& dname) { - if (!luaconfsLocal->protobufExportConfig.enabled) { - if (t_protobufServers) { - for (auto& server : *t_protobufServers) { - server->stop(); - } - t_protobufServers.reset(); - } - - return false; - } - - /* if the server was not running, or if it was running according to a - previous configuration */ - if (!t_protobufServers || - t_protobufServersGeneration < luaconfsLocal->generation) { - - if (t_protobufServers) { - for (auto& server : *t_protobufServers) { - server->stop(); + bool ret = false; + // First check the (sub)domain isn't ignored for NOD purposes + if (!g_nodDomainWL.check(dname)) { + // Now check the NODDB (note this is probabilistic so can have FNs/FPs) + if (t_nodDBp && t_nodDBp->isNewDomain(dname)) { + if (g_nodLog) { + // This should probably log to a dedicated log file + SLOG(g_log<protobufExportConfig); - t_protobufServersGeneration = luaconfsLocal->generation; } - - return true; + return ret; } -static bool checkOutgoingProtobufExport(LocalStateHolder& luaconfsLocal) +static void sendNODLookup(const shared_ptr& nodlogger, const DNSName& dname) { - if (!luaconfsLocal->outgoingProtobufExportConfig.enabled) { - if (t_outgoingProtobufServers) { - for (auto& server : *t_outgoingProtobufServers) { - server->stop(); - } - } - t_outgoingProtobufServers.reset(); - - return false; - } - - /* if the server was not running, or if it was running according to a - previous configuration */ - if (!t_outgoingProtobufServers || - t_outgoingProtobufServersGeneration < luaconfsLocal->generation) { - - if (t_outgoingProtobufServers) { - for (auto& server : *t_outgoingProtobufServers) { - server->stop(); - } - } - t_outgoingProtobufServers.reset(); - - t_outgoingProtobufServers = startProtobufServers(luaconfsLocal->outgoingProtobufExportConfig); - t_outgoingProtobufServersGeneration = luaconfsLocal->generation; - } - - return true; -} - -#ifdef HAVE_FSTRM - -static std::shared_ptr>> startFrameStreamServers(const FrameStreamExportConfig& config) -{ - auto result = std::make_shared>>(); - - for (const auto& server : config.servers) { - try { - std::unordered_map options; - options["bufferHint"] = config.bufferHint; - options["flushTimeout"] = config.flushTimeout; - options["inputQueueSize"] = config.inputQueueSize; - options["outputQueueSize"] = config.outputQueueSize; - options["queueNotifyThreshold"] = config.queueNotifyThreshold; - options["reopenInterval"] = config.reopenInterval; - FrameStreamLogger *fsl = nullptr; - try { - ComboAddress address(server); - fsl = new FrameStreamLogger(address.sin4.sin_family, address.toStringWithPort(), true, options); - } - catch (const PDNSException& e) { - fsl = new FrameStreamLogger(AF_UNIX, server, true, options); - } - fsl->setLogQueries(config.logQueries); - fsl->setLogResponses(config.logResponses); - result->emplace_back(fsl); - } - catch(const std::exception& e) { - g_log<& luaconfsLocal) -{ - if (!luaconfsLocal->frameStreamExportConfig.enabled) { - if (t_frameStreamServers) { - // dt's take care of cleanup - t_frameStreamServers.reset(); - } - - return false; - } - - /* if the server was not running, or if it was running according to a - previous configuration */ - if (!t_frameStreamServers || - t_frameStreamServersGeneration < luaconfsLocal->generation) { - - if (t_frameStreamServers) { - // dt's take care of cleanup - t_frameStreamServers.reset(); - } - - t_frameStreamServers = startFrameStreamServers(luaconfsLocal->frameStreamExportConfig); - t_frameStreamServersGeneration = luaconfsLocal->generation; - } - - return true; -} -#endif /* HAVE_FSTRM */ - -#ifdef NOD_ENABLED -static bool nodCheckNewDomain(const shared_ptr& nodlogger, const DNSName& dname) -{ - bool ret = false; - // First check the (sub)domain isn't ignored for NOD purposes - if (!g_nodDomainWL.check(dname)) { - // Now check the NODDB (note this is probabilistic so can have FNs/FPs) - if (t_nodDBp && t_nodDBp->isNewDomain(dname)) { - if (g_nodLog) { - // This should probably log to a dedicated log file - SLOG(g_log<& nodlogger, const DNSName& dname) -{ - if (!(g_nodLookupDomain.isRoot())) { - // Send a DNS A query to .g_nodLookupDomain - DNSName qname; - try { - qname = dname + g_nodLookupDomain; + if (!(g_nodLookupDomain.isRoot())) { + // Send a DNS A query to .g_nodLookupDomain + DNSName qname; + try { + qname = dname + g_nodLookupDomain; } catch(const std::range_error &e) { nodlogger->v(10)->error(Logr::Error, "DNSName too long", "Unable to send NOD lookup"); @@ -2116,36 +1640,6 @@ void startDoResolve(void *p) g_stats.maxMThreadStackUsage = max(MT->getMaxStackUsage(), g_stats.maxMThreadStackUsage.load()); } -static void makeControlChannelSocket(int processNum=-1) -{ - string sockname=::arg()["socket-dir"]+"/"+s_programname; - if(processNum >= 0) - sockname += "."+std::to_string(processNum); - sockname+=".controlsocket"; - s_rcc.listen(sockname); - - int sockowner = -1; - int sockgroup = -1; - - if (!::arg().isEmpty("socket-group")) - sockgroup=::arg().asGid("socket-group"); - if (!::arg().isEmpty("socket-owner")) - sockowner=::arg().asUid("socket-owner"); - - if (sockgroup > -1 || sockowner > -1) { - if(chown(sockname.c_str(), sockowner, sockgroup) < 0) { - unixDie("Failed to chown control socket"); - } - } - - // do mode change if socket-mode is given - if(!::arg().isEmpty("socket-mode")) { - mode_t sockmode=::arg().asMode("socket-mode"); - if(chmod(sockname.c_str(), sockmode) < 0) { - unixDie("Failed to chmod control socket"); - } - } -} void getQNameAndSubnet(const std::string& question, DNSName* dnsname, uint16_t* qtype, uint16_t* qclass, bool& foundECS, EDNSSubnetOpts* ednssubnet, EDNSOptionViewMap* options, @@ -2744,7 +2238,7 @@ static void handleNewUDPQuestion(int fd, FDMultiplexer::funcparam_t& var) } } -static void makeUDPServerSockets(deferredAdd_t& deferredAdds) +void makeUDPServerSockets(deferredAdd_t& deferredAdds) { int one=1; vectorlocals; @@ -2843,339 +2337,7 @@ static void makeUDPServerSockets(deferredAdd_t& deferredAdds) } } -static void daemonize(void) -{ - if(fork()) - exit(0); // bye bye - - setsid(); - - int i=open("/dev/null",O_RDWR); /* open stdin */ - if(i < 0) - g_log<cacheHits; - uint64_t cacheMisses = g_recCache->cacheMisses; - uint64_t cacheSize = g_recCache->size(); - auto rc_stats = g_recCache->stats(); - double r = rc_stats.second == 0 ? 0.0 : (100.0 * rc_stats.first / rc_stats.second); - uint64_t negCacheSize = g_negCache->size(); - auto taskPushes = getTaskPushes(); - auto taskExpired = getTaskExpired(); - auto taskSize = getTaskSize(); - - if(g_stats.qcounter && (cacheHits + cacheMisses) && SyncRes::s_queries && SyncRes::s_outqueries) { - g_log<(pleaseGetThrottleSize) <<", ns speeds: " - << broadcastAccFunction(pleaseGetNsSpeedsSize)<<", failed ns: " - << broadcastAccFunction(pleaseGetFailedServersSize)<<", ednsmap: " - <(pleaseGetEDNSStatusesSize)<(pleaseGetConcurrentQueries)<<" queries running, "<(pleaseGetPacketCacheSize); - uint64_t pcHits = broadcastAccFunction(pleaseGetPacketCacheHits); - g_log<trustAnchorFileInfo.fname.empty() && luaconfsLocal->trustAnchorFileInfo.interval != 0) { - // Loading the Lua config file already "refreshed" the TAs - last_trustAnchorUpdate = g_now.tv_sec + luaconfsLocal->trustAnchorFileInfo.interval * 3600; - } - - try { - if(s_running) { - return; - } - s_running=true; - - runTaskOnce(g_logCommonErrors); - - struct timeval now, past; - Utility::gettimeofday(&now, nullptr); - past = now; - past.tv_sec -= 5; - if (last_prune < past) { - t_packetCache->doPruneTo(g_maxPacketCacheEntries / (g_numWorkerThreads + g_numDistributorThreads)); - - time_t limit; - if(!((cleanCounter++)%40)) { // this is a full scan! - limit=now.tv_sec-300; - SyncRes::pruneNSSpeeds(limit); - } - limit = now.tv_sec - SyncRes::s_serverdownthrottletime * 10; - SyncRes::pruneFailedServers(limit); - limit = now.tv_sec - 2*3600; - SyncRes::pruneEDNSStatuses(limit); - SyncRes::pruneThrottledServers(); - SyncRes::pruneNonResolving(now.tv_sec - SyncRes::s_nonresolvingnsthrottletime); - Utility::gettimeofday(&last_prune, nullptr); - t_tcp_manager.cleanup(now); - } - - if(isHandlerThread()) { - if (now.tv_sec - last_RC_prune > 5) { - g_recCache->doPrune(g_maxCacheEntries); - g_negCache->prune(g_maxCacheEntries / 10); - if (g_aggressiveNSECCache) { - g_aggressiveNSECCache->prune(now.tv_sec); - } - last_RC_prune = now.tv_sec; - } - // Divide by 12 to get the original 2 hour cycle if s_maxcachettl is default (1 day) - if (now.tv_sec - last_rootupdate > max(SyncRes::s_maxcachettl / 12, 10U)) { - int res = SyncRes::getRootNS(g_now, nullptr, 0); - if (!res) { - last_rootupdate=now.tv_sec; - try { - primeRootNSZones(g_dnssecmode != DNSSECMode::Off, 0); - } - catch (const std::exception& e) { - g_log<= 3600) { - try { - doSecPoll(&last_secpoll); - } - catch (const std::exception& e) - { - g_log<trustAnchorFileInfo.fname.empty() && luaconfsLocal->trustAnchorFileInfo.interval != 0 && - g_now.tv_sec - last_trustAnchorUpdate >= (luaconfsLocal->trustAnchorFileInfo.interval * 3600)) { - g_log< dsAnchors; - if (updateTrustAnchorsFromFile(luaconfsLocal->trustAnchorFileInfo.fname, dsAnchors)) { - g_luaconfs.modify([&dsAnchors](LuaConfigItems& lci) { - lci.dsAnchors = dsAnchors; - }); - } - last_trustAnchorUpdate = now.tv_sec; - } catch (const PDNSException &pe) { - g_log< 0) { - g_log< 0) { - if (!setPipeBufferSize(threadInfos.pipes.writeQueriesToThread, pipeBufferSize)) { - int err = errno; - g_log< 0) { - g_log<func = func; - tmsg->wantAnswer = true; - if(write(threadInfo.pipes.writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { - delete tmsg; - - unixDie("write to thread pipe returned wrong size or error"); - } - - string* resp = nullptr; - if(read(threadInfo.pipes.readFromThread, &resp, sizeof(resp)) != sizeof(resp)) - unixDie("read from thread pipe returned wrong size or error"); - if(resp) { - delete resp; - resp = nullptr; - } - } -} static bool trySendingQueryToWorker(unsigned int target, ThreadMSG* tmsg) { @@ -3278,126 +2440,6 @@ void distributeAsyncFunction(const string& packet, const pipefunc_t& func) } } -static void handlePipeRequest(int fd, FDMultiplexer::funcparam_t& var) -{ - ThreadMSG* tmsg = nullptr; - - if(read(fd, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // fd == readToThread || fd == readQueriesToThread - unixDie("read from thread pipe returned wrong size or error"); - } - - void *resp=0; - try { - resp = tmsg->func(); - } - catch(std::exception& e) { - if(g_logCommonErrors) - g_log<wantAnswer) { - const auto& threadInfo = s_threadInfos.at(t_id); - if(write(threadInfo.pipes.writeFromThread, &resp, sizeof(resp)) != sizeof(resp)) { - delete tmsg; - unixDie("write to thread pipe returned wrong size or error"); - } - } - - delete tmsg; -} - -template void *voider(const boost::function& func) -{ - return func(); -} - -static vector& operator+=(vector&a, const vector& b) -{ - a.insert(a.end(), b.begin(), b.end()); - return a; -} - -static vector >& operator+=(vector >&a, const vector >& b) -{ - a.insert(a.end(), b.begin(), b.end()); - return a; -} - -/* - This function should only be called by the handler to gather metrics, wipe the cache, - reload the Lua script (not the Lua config) or change the current trace regex, - and by the SNMP thread to gather metrics. */ -template T broadcastAccFunction(const boost::function& func) -{ - if (!isHandlerThread()) { - g_log<func = [func]{ return voider(func); }; - tmsg->wantAnswer = true; - - if(write(tps.writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { - delete tmsg; - unixDie("write to thread pipe returned wrong size or error"); - } - - T* resp = nullptr; - if(read(tps.readFromThread, &resp, sizeof(resp)) != sizeof(resp)) - unixDie("read from thread pipe returned wrong size or error"); - - if(resp) { - ret += *resp; - delete resp; - resp = nullptr; - } - } - return ret; -} - -template string broadcastAccFunction(const boost::function& fun); // explicit instantiation -template RecursorControlChannel::Answer broadcastAccFunction(const boost::function& fun); // explicit instantiation -template uint64_t broadcastAccFunction(const boost::function& fun); // explicit instantiation -template vector broadcastAccFunction(const boost::function *()>& fun); // explicit instantiation -template vector > broadcastAccFunction(const boost::function > *()>& fun); // explicit instantiation -template ThreadTimes broadcastAccFunction(const boost::function& fun); - -static void handleRCC(int fd, FDMultiplexer::funcparam_t& var) -{ - try { - FDWrapper clientfd = accept(fd, nullptr, nullptr); - if (clientfd == -1) { - throw PDNSException("accept failed"); - } - string msg = s_rcc.recv(clientfd).d_str; - g_log << Logger::Info << "Received rec_control command '" << msg << "' via controlsocket" << endl; - - RecursorControlParser rcp; - RecursorControlParser::func_t* command; - auto answer = rcp.getAnswer(clientfd, msg, &command); - - s_rcc.send(clientfd, answer); - command(); - } - catch(const std::exception& e) { - g_log<(); - int err = t_pdl->loadFile(fname); - if (err != 0) { - string msg = std::to_string(t_id) + " Retaining current script, could not read '" + fname + "': " + stringerror(err); - g_log<::const_iterator begin, vector::const_iterator end) -{ - if(begin != end) - ::arg().set("lua-dns-script") = *begin; - - return broadcastAccFunction(doReloadLuaScript); -} - -static string* pleaseUseNewTraceRegex(const std::string& newRegex) -try -{ - if(newRegex.empty()) { - t_traceRegex.reset(); - return new string("unset\n"); - } - else { - t_traceRegex = std::make_shared(newRegex); - return new string("ok\n"); - } -} -catch(PDNSException& ae) -{ - return new string(ae.reason+"\n"); -} - -string doTraceRegex(vector::const_iterator begin, vector::const_iterator end) -{ - return broadcastAccFunction([=]{ return pleaseUseNewTraceRegex(begin!=end ? *begin : ""); }); -} - -static void checkLinuxIPv6Limits() -{ -#ifdef __linux__ - string line; - if(readFileIfThere("/proc/sys/net/ipv6/route/max_size", &line)) { - int lim=std::stoi(line); - if(lim < 16384) { - g_log< availFDs) { - unsigned int hardlimit= getFilenumLimit(true); - if(hardlimit >= wantFDs) { - setFilenumLimit(wantFDs); - g_log< ng) -{ - t_allowFrom = ng; - return nullptr; -} - -static void* pleaseSupplantAllowNotifyFrom(std::shared_ptr ng) -{ - t_allowNotifyFrom = ng; - return nullptr; -} - -void* pleaseSupplantAllowNotifyFor(std::shared_ptr ns) -{ - t_allowNotifyFor = ns; - return nullptr; -} - -int g_argc; -char** g_argv; - -static std::shared_ptr parseACL(const std::string& aclFile, const std::string& aclSetting) -{ - auto result = std::make_shared(); - - if(!::arg()[aclFile].empty()) { - string line; - ifstream ifs(::arg()[aclFile].c_str()); - if(!ifs) { - throw runtime_error("Could not open '"+::arg()[aclFile]+"': "+stringerror()); - } - - string::size_type pos; - while(getline(ifs,line)) { - pos=line.find('#'); - if(pos!=string::npos) - line.resize(pos); - boost::trim(line); - if(line.empty()) - continue; - - result->addMask(line); - } - g_log<size()<<" "< ips; - stringtok(ips, ::arg()[aclSetting], ", "); - - g_log<::const_iterator i = ips.begin(); i!= ips.end(); ++i) { - result->addMask(*i); - if(i!=ips.begin()) - g_log< extraConfigs; - ::arg().gatherIncludes(extraConfigs); - - for(const std::string& fn : extraConfigs) { - if(!::arg().preParseFile(fn.c_str(), "allow-from-file", ::arg()["allow-from-file"])) - throw runtime_error("Unable to re-parse configuration file include '"+fn+"'"); - if(!::arg().preParseFile(fn.c_str(), "allow-from", ::arg()["allow-from"])) - throw runtime_error("Unable to re-parse configuration file include '"+fn+"'"); - - if(!::arg().preParseFile(fn.c_str(), "allow-notify-from-file", ::arg()["allow-notify-from-file"])) - throw runtime_error("Unable to re-parse configuration file include '"+fn+"'"); - if(!::arg().preParseFile(fn.c_str(), "allow-notify-from", ::arg()["allow-notify-from"])) - throw runtime_error("Unable to re-parse configuration file include '"+fn+"'"); - } - - ::arg().preParse(g_argc, g_argv, "allow-from-file"); - ::arg().preParse(g_argc, g_argv, "allow-from"); - - ::arg().preParse(g_argc, g_argv, "allow-notify-from-file"); - ::arg().preParse(g_argc, g_argv, "allow-notify-from"); - } - - auto allowFrom = parseACL("allow-from-file", "allow-from"); - - if(allowFrom->size() == 0) { - if(::arg()["local-address"]!="127.0.0.1" && ::arg().asNum("local-port")==53) - g_log< > parseCPUMap() -{ - std::map > result; - - const std::string value = ::arg()["cpu-map"]; - - if (!value.empty() && !isSettingThreadCPUAffinitySupported()) { - g_log< parts; - - stringtok(parts, value, " \t"); - - for(const auto& part : parts) { - if (part.find('=') == string::npos) - continue; - - try { - auto headers = splitField(part, '='); - boost::trim(headers.first); - boost::trim(headers.second); - - unsigned int threadId = pdns_stou(headers.first); - std::vector cpus; - - stringtok(cpus, headers.second, ","); - - for(const auto& cpu : cpus) { - int cpuId = std::stoi(cpu); - - result[threadId].insert(cpuId); - } - } - catch(const std::exception& e) { - g_log< >& cpusMap, unsigned int n, pthread_t tid) -{ - const auto& cpuMapping = cpusMap.find(n); - if (cpuMapping != cpusMap.cend()) { - int rc = mapThreadToCPUList(tid, cpuMapping->second); - if (rc == 0) { - g_log<second) { - g_log<second) { - g_log<(num_cells); - try { - t_nodDBp->setCacheDir(::arg()["new-domain-history-dir"]); - } - catch (const PDNSException& e) { - g_log<init()) { - g_log<(num_cells); - try { - t_udrDBp->setCacheDir(::arg()["unique-response-history-dir"]); - } - catch (const PDNSException& e) { - g_log<init()) { - g_log< parts; - stringtok(parts, wlist, ",; "); - for(const auto& a : parts) { - g_nodDomainWL.add(DNSName(a)); - } -} - -static void setupNODGlobal() -{ - // Setup NOD subsystem - g_nodEnabled = ::arg().mustDo("new-domain-tracking"); - g_nodLookupDomain = DNSName(::arg()["new-domain-lookup"]); - g_nodLog = ::arg().mustDo("new-domain-log"); - parseNODIgnorelist(::arg()["new-domain-whitelist"]); - parseNODIgnorelist(::arg()["new-domain-ignore-list"]); - - // Setup Unique DNS Response subsystem - g_udrEnabled = ::arg().mustDo("unique-response-tracking"); - g_udrLog = ::arg().mustDo("unique-response-log"); -} -#endif /* NOD_ENABLED */ - -static void checkSocketDir(void) -{ - struct stat st; - string dir(::arg()["socket-dir"]); - string msg; - - if (stat(dir.c_str(), &st) == -1) { - msg = "it does not exist or cannot access"; - } - else if (!S_ISDIR(st.st_mode)) { - msg = "it is not a directory"; - } - else if (access(dir.c_str(), R_OK | W_OK | X_OK) != 0) { - msg = "cannot read, write or search"; - } else { - return; - } - g_log << Logger::Error << "Problem with socket directory " << dir << ": " << msg << "; see https://docs.powerdns.com/recursor/upgrade.html#x-to-4-3-0" << endl; - _exit(1); -} - -static uint64_t* pleaseWipePacketCache(const DNSName& canon, bool subtree, uint16_t qtype) -{ - return new uint64_t(t_packetCache->doWipePacketCache(canon, qtype, subtree)); -} - -struct WipeCacheResult wipeCaches(const DNSName& canon, bool subtree, uint16_t qtype) -{ - struct WipeCacheResult res; - - try { - res.record_count = g_recCache->doWipeCache(canon, subtree, qtype); - res.packet_count = broadcastAccFunction([=]{ return pleaseWipePacketCache(canon, subtree, qtype);}); - res.negative_record_count = g_negCache->wipe(canon, subtree); - if (g_aggressiveNSECCache) { - g_aggressiveNSECCache->removeZoneInfo(canon, subtree); - } - } - catch (const std::exception& e) { - g_log<= 0) - g_log.setFacility(val); - else - g_log< ips; - stringtok(ips, ::arg()["dont-query"], ", "); - ips.push_back("0.0.0.0"); - ips.push_back("::"); - - g_log<::const_iterator i = ips.begin(); i!= ips.end(); ++i) { - SyncRes::addDontQuery(*i); - if(i!=ips.begin()) - g_log< SyncRes::s_packetcachettl) ? SyncRes::s_packetcachettl : packetCacheServFailTTL; - SyncRes::s_serverdownmaxfails=::arg().asNum("server-down-max-fails"); - SyncRes::s_serverdownthrottletime=::arg().asNum("server-down-throttle-time"); - SyncRes::s_nonresolvingnsmaxfails=::arg().asNum("non-resolving-ns-max-fails"); - SyncRes::s_nonresolvingnsthrottletime=::arg().asNum("non-resolving-ns-throttle-time"); - SyncRes::s_serverID=::arg()["server-id"]; - SyncRes::s_maxqperq=::arg().asNum("max-qperq"); - SyncRes::s_maxnsaddressqperq=::arg().asNum("max-ns-address-qperq"); - SyncRes::s_maxtotusec=1000*::arg().asNum("max-total-msec"); - SyncRes::s_maxdepth=::arg().asNum("max-recursion-depth"); - SyncRes::s_rootNXTrust = ::arg().mustDo( "root-nx-trust"); - SyncRes::s_refresh_ttlperc = ::arg().asNum("refresh-on-ttl-perc"); - RecursorPacketCache::s_refresh_ttlperc = SyncRes::s_refresh_ttlperc; - SyncRes::s_tcp_fast_open = ::arg().asNum("tcp-fast-open"); - SyncRes::s_tcp_fast_open_connect = ::arg().mustDo("tcp-fast-open-connect"); - - SyncRes::s_dot_to_port_853 = ::arg().mustDo("dot-to-port-853"); - SyncRes::s_event_trace_enabled = ::arg().asNum("event-trace-enabled"); - - if (SyncRes::s_tcp_fast_open_connect) { - checkFastOpenSysctl(true); - checkTFOconnect(); - } - - if(SyncRes::s_serverID.empty()) { - SyncRes::s_serverID = myHostname; - } - - SyncRes::s_ecsipv4limit = ::arg().asNum("ecs-ipv4-bits"); - SyncRes::s_ecsipv6limit = ::arg().asNum("ecs-ipv6-bits"); - SyncRes::clearECSStats(); - SyncRes::s_ecsipv4cachelimit = ::arg().asNum("ecs-ipv4-cache-bits"); - SyncRes::s_ecsipv6cachelimit = ::arg().asNum("ecs-ipv6-cache-bits"); - SyncRes::s_ecsipv4nevercache = ::arg().mustDo("ecs-ipv4-never-cache"); - SyncRes::s_ecsipv6nevercache = ::arg().mustDo("ecs-ipv6-never-cache"); - SyncRes::s_ecscachelimitttl = ::arg().asNum("ecs-cache-limit-ttl"); - - SyncRes::s_qnameminimization = ::arg().mustDo("qname-minimization"); - - if (SyncRes::s_qnameminimization) { - // With an empty cache, a rev ipv6 query with dnssec enabled takes - // almost 100 queries. Default maxqperq is 60. - SyncRes::s_maxqperq = std::max(SyncRes::s_maxqperq, static_cast(100)); - } - - SyncRes::s_hardenNXD = SyncRes::HardenNXD::DNSSEC; - string value = ::arg()["nothing-below-nxdomain"]; - if (value == "yes") { - SyncRes::s_hardenNXD = SyncRes::HardenNXD::Yes; - } else if (value == "no") { - SyncRes::s_hardenNXD = SyncRes::HardenNXD::No; - } else if (value != "dnssec") { - g_log << Logger::Error << "Unknown nothing-below-nxdomain mode: " << value << endl; - exit(1); - } - - if (!::arg().isEmpty("ecs-scope-zero-address")) { - ComboAddress scopeZero(::arg()["ecs-scope-zero-address"]); - SyncRes::setECSScopeZeroAddress(Netmask(scopeZero, scopeZero.isIPv4() ? 32 : 128)); - } - else { - Netmask nm; - bool done = false; - - auto addr = pdns::getNonAnyQueryLocalAddress(AF_INET); - if (addr.sin4.sin_family != 0) { - nm = Netmask(addr, 32); - done = true; - } - if (!done) { - addr = pdns::getNonAnyQueryLocalAddress(AF_INET6); - if (addr.sin4.sin_family != 0) { - nm = Netmask(addr, 128); - done = true; - } - } - if (!done) { - nm = Netmask(ComboAddress("127.0.0.1"), 32); - } - SyncRes::setECSScopeZeroAddress(nm); - } - - SyncRes::parseEDNSSubnetAllowlist(::arg()["edns-subnet-whitelist"]); - SyncRes::parseEDNSSubnetAllowlist(::arg()["edns-subnet-allow-list"]); - SyncRes::parseEDNSSubnetAddFor(::arg()["ecs-add-for"]); - g_useIncomingECS = ::arg().mustDo("use-incoming-edns-subnet"); - - g_XPFAcl.toMasks(::arg()["xpf-allow-from"]); - g_xpfRRCode = ::arg().asNum("xpf-rr-code"); - - g_proxyProtocolACL.toMasks(::arg()["proxy-protocol-from"]); - g_proxyProtocolMaximumSize = ::arg().asNum("proxy-protocol-maximum-size"); - - if (!::arg()["dns64-prefix"].empty()) { - try { - auto dns64Prefix = Netmask(::arg()["dns64-prefix"]); - if (dns64Prefix.getBits() != 96) { - g_log << Logger::Error << "Invalid prefix for 'dns64-prefix', the current implementation only supports /96 prefixes: " << ::arg()["dns64-prefix"] << endl; - exit(1); - } - g_dns64Prefix = dns64Prefix.getNetwork(); - g_dns64PrefixReverse = reverseNameFromIP(*g_dns64Prefix); - /* /96 is 24 nibbles + 2 for "ip6.arpa." */ - while (g_dns64PrefixReverse.countLabels() > 26) { - g_dns64PrefixReverse.chopOff(); - } - } - catch (const NetmaskException& ne) { - g_log << Logger::Error << "Invalid prefix '" << ::arg()["dns64-prefix"] << "' for 'dns64-prefix': " << ne.reason << endl; - exit(1); - } - } - - g_networkTimeoutMsec = ::arg().asNum("network-timeout"); - - std::tie(g_initialDomainMap, g_initialAllowNotifyFor) = parseZoneConfiguration(); - - g_latencyStatSize=::arg().asNum("latency-statistic-size"); - - g_logCommonErrors=::arg().mustDo("log-common-errors"); - g_logRPZChanges = ::arg().mustDo("log-rpz-changes"); - - g_anyToTcp = ::arg().mustDo("any-to-tcp"); - g_udpTruncationThreshold = ::arg().asNum("udp-truncation-threshold"); - - g_lowercaseOutgoing = ::arg().mustDo("lowercase-outgoing"); - - g_paddingFrom.toMasks(::arg()["edns-padding-from"]); - if (::arg()["edns-padding-mode"] == "always") { - g_paddingMode = PaddingMode::Always; - } - else if (::arg()["edns-padding-mode"] == "padded-queries-only") { - g_paddingMode = PaddingMode::PaddedQueries; - } - else { - g_log << Logger::Error << "Unknown edns-padding-mode: " << ::arg()["edns-padding-mode"] << endl; - exit(1); - } - g_paddingTag = ::arg().asNum("edns-padding-tag"); - - g_numDistributorThreads = ::arg().asNum("distributor-threads"); - g_numWorkerThreads = ::arg().asNum("threads"); - if (g_numWorkerThreads < 1) { - g_log< USHRT_MAX || maxInFlight >= g_maxMThreads) { - g_log<(millis) % 1000) * 1000 }; - TCPOutConnectionManager::s_maxIdlePerAuth = ::arg().asNum("tcp-out-max-idle-per-auth"); - TCPOutConnectionManager::s_maxQueries = ::arg().asNum("tcp-out-max-queries"); - TCPOutConnectionManager::s_maxIdlePerThread = ::arg().asNum("tcp-out-max-idle-per-thread"); - - g_gettagNeedsEDNSOptions = ::arg().mustDo("gettag-needs-edns-options"); - - g_statisticsInterval = ::arg().asNum("statistics-interval"); - - s_addExtendedResolutionDNSErrors = ::arg().mustDo("extended-resolution-errors"); - - if (::arg().asNum("aggressive-nsec-cache-size") > 0) { - if (g_dnssecmode == DNSSECMode::ValidateAll || g_dnssecmode == DNSSECMode::ValidateForLog || g_dnssecmode == DNSSECMode::Process) { - g_aggressiveNSECCache = make_unique(::arg().asNum("aggressive-nsec-cache-size")); - } - else { - g_log< parts; - stringtok(parts, ::arg()["dont-throttle-names"], " ,"); - for (const auto &p : parts) { - dontThrottleNames.add(DNSName(p)); - } - g_dontThrottleNames.setState(std::move(dontThrottleNames)); - - parts.clear(); - NetmaskGroup dontThrottleNetmasks; - stringtok(parts, ::arg()["dont-throttle-netmasks"], " ,"); - for (const auto &p : parts) { - dontThrottleNetmasks.addMask(Netmask(p)); - } - g_dontThrottleNetmasks.setState(std::move(dontThrottleNetmasks)); - } - - { - SuffixMatchNode xdnssecNames; - vector parts; - stringtok(parts, ::arg()["x-dnssec-names"], " ,"); - for (const auto &p : parts) { - xdnssecNames.add(DNSName(p)); - } - g_xdnssec.setState(std::move(xdnssecNames)); - } - - { - SuffixMatchNode dotauthNames; - vector parts; - stringtok(parts, ::arg()["dot-to-auth-names"], " ,"); -#ifndef HAVE_DNS_OVER_TLS - if (parts.size()) { - g_log << Logger::Error << "dot-to-auth-names setting contains names, but Recursor was built without DNS over TLS support. Setting will be ignored."< tcpSockets; - /* we don't have reuseport so we can only open one socket per - listening addr:port and everyone will listen on it */ - makeUDPServerSockets(g_deferredAdds); - makeTCPServerSockets(g_deferredAdds, tcpSockets); - - /* every listener (so distributor if g_weDistributeQueries, workers otherwise) - needs to listen to the shared sockets */ - if (g_weDistributeQueries) { - /* first thread is the handler, then distributors */ - for (unsigned int threadId = 1; threadId <= g_numDistributorThreads; threadId++) { - s_threadInfos.at(threadId).tcpSockets = tcpSockets; - } - } - else { - /* first thread is the handler, there is no distributor here and workers are accepting queries */ - for (unsigned int threadId = 1; threadId <= g_numWorkerThreads; threadId++) { - s_threadInfos.at(threadId).tcpSockets = tcpSockets; - } - } - } - -#ifdef NOD_ENABLED - // Setup newly observed domain globals - setupNODGlobal(); -#endif /* NOD_ENABLED */ - - int forks; - for(forks = 0; forks < ::arg().asNum("processes") - 1; ++forks) { - if(!fork()) // we are child - break; - } - - if(::arg().mustDo("daemon")) { - g_log< 1 ? forks : -1); - - Utility::dropUserPrivs(newuid); - try { - /* we might still have capabilities remaining, for example if we have been started as root - without --setuid (please don't do that) or as an unprivileged user with ambient capabilities - like CAP_NET_BIND_SERVICE. - */ - dropCapabilities(); - } - catch(const std::exception& e) { - g_log<("recursor", setting); - g_snmpAgent->run(); - } - - int port = ::arg().asNum("udp-source-port-min"); - if(port < 1024 || port > 65535){ - g_log< 65535 || port < s_minUdpSourcePort){ - g_log< parts {}; - stringtok(parts, ::arg()["udp-source-port-avoid"], ", "); - for (const auto &part : parts) - { - port = std::stoi(part); - if(port < 1024 || port > 65535){ - g_log<(); - t_tcpClientCounts = std::make_unique(); - - if (threadInfo.isHandler) { - if (!primeHints()) { - threadInfo.exitCode = EXIT_FAILURE; - RecursorControlChannel::stop = 1; - g_log<(); - -#ifdef NOD_ENABLED - if (threadInfo.isWorker) - setupNODThread(); -#endif /* NOD_ENABLED */ - - /* the listener threads handle TCP queries */ - if(threadInfo.isWorker || threadInfo.isListener) { - try { - if(!::arg()["lua-dns-script"].empty()) { - t_pdl = std::make_shared(); - t_pdl->loadFile(::arg()["lua-dns-script"]); - g_log<(); - if(g_weDistributeQueries) - t_remotes->set_capacity(::arg().asNum("stats-ringbuffer-entries") / g_numDistributorThreads); - else - t_remotes->set_capacity(ringsize); - t_servfailremotes = std::make_unique(); - t_servfailremotes->set_capacity(ringsize); - t_bogusremotes = std::make_unique(); - t_bogusremotes->set_capacity(ringsize); - t_largeanswerremotes = std::make_unique(); - t_largeanswerremotes->set_capacity(ringsize); - t_timeouts = std::make_unique(); - t_timeouts->set_capacity(ringsize); - - t_queryring = std::make_unique>>(); - t_queryring->set_capacity(ringsize); - t_servfailqueryring = std::make_unique>>(); - t_servfailqueryring->set_capacity(ringsize); - t_bogusqueryring = std::make_unique>>(); - t_bogusqueryring->set_capacity(ringsize); - } - MT = std::make_unique(::arg().asNum("stack-size")); - threadInfo.mt = MT.get(); - - /* start protobuf export threads if needed */ - auto luaconfsLocal = g_luaconfs.getLocal(); - checkProtobufExport(luaconfsLocal); - checkOutgoingProtobufExport(luaconfsLocal); -#ifdef HAVE_FSTRM - checkFrameStreamExport(luaconfsLocal); -#endif - - PacketID pident; - - t_fdm=getMultiplexer(); - - RecursorWebServer *rws = nullptr; - - t_fdm->addReadFD(threadInfo.pipes.readToThread, handlePipeRequest); - - if(threadInfo.isHandler) { - if(::arg().mustDo("webserver")) { - g_log<getName() << "' multiplexer"<addReadFD(threadInfo.pipes.readQueriesToThread, handlePipeRequest); - - if (threadInfo.isListener) { - if (g_reusePort) { - /* then every listener has its own FDs */ - for(const auto& deferred : threadInfo.deferredAdds) { - t_fdm->addReadFD(deferred.first, deferred.second); - } - } - else { - /* otherwise all listeners are listening on the same ones */ - for(const auto& deferred : g_deferredAdds) { - t_fdm->addReadFD(deferred.first, deferred.second); - } - } - } - } - - registerAllStats(); - - if(threadInfo.isHandler) { - t_fdm->addReadFD(s_rcc.d_fd, handleRCC); // control channel - } - - unsigned int maxTcpClients=::arg().asNum("max-tcp-clients"); - - bool listenOnTCP(true); - - time_t last_stat = 0; - time_t last_carbon=0, last_lua_maintenance=0; - time_t carbonInterval=::arg().asNum("carbon-interval"); - time_t luaMaintenanceInterval=::arg().asNum("lua-maintenance-interval"); - counter.store(0); // used to periodically execute certain tasks - - while (!RecursorControlChannel::stop) { - while(MT->schedule(&g_now)); // MTasker letting the mthreads do their thing - - // Use primes, it avoid not being scheduled in cases where the counter has a regular pattern. - // We want to call handler thread often, it gets scheduled about 2 times per second - if ((threadInfo.isHandler && counter % 11 == 0) || counter % 499 == 0) { - MT->makeThread(houseKeeping, 0); - } - - if(!(counter%55)) { - typedef vector > expired_t; - expired_t expired=t_fdm->getTimeouts(g_now); - - for(expired_t::iterator i=expired.begin() ; i != expired.end(); ++i) { - shared_ptr conn=boost::any_cast >(i->second); - if(g_logCommonErrors) - g_log<d_remote.toStringWithPort() <removeReadFD(i->first); - } - } - - counter++; - - if(threadInfo.isHandler) { - if(statsWanted || (g_statisticsInterval > 0 && (g_now.tv_sec - last_stat) >= g_statisticsInterval)) { - doStats(); - last_stat = g_now.tv_sec; - } - - Utility::gettimeofday(&g_now, nullptr); - - if((g_now.tv_sec - last_carbon) >= carbonInterval) { - MT->makeThread(doCarbonDump, 0); - last_carbon = g_now.tv_sec; - } - } - if (t_pdl != nullptr) { - // lua-dns-script directive is present, call the maintenance callback if needed - /* remember that the listener threads handle TCP queries */ - if (threadInfo.isWorker || threadInfo.isListener) { - // Only on threads processing queries - if(g_now.tv_sec - last_lua_maintenance >= luaMaintenanceInterval) { - t_pdl->maintenance(); - last_lua_maintenance = g_now.tv_sec; - } - } - } - - t_fdm->run(&g_now); - // 'run' updates g_now for us - - if(threadInfo.isListener) { - if(listenOnTCP) { - if(TCPConnection::getCurrentConnections() > maxTcpClients) { // shutdown, too many connections - for(const auto fd : threadInfo.tcpSockets) { - t_fdm->removeReadFD(fd); - } - listenOnTCP=false; - } - } - else { - if(TCPConnection::getCurrentConnections() <= maxTcpClients) { // reenable - for(const auto fd : threadInfo.tcpSockets) { - t_fdm->addReadFD(fd, handleNewTCPQuestion); - } - listenOnTCP=true; - } - } - } - } - delete rws; - delete t_fdm; - return 0; -} -catch(PDNSException &ae) { - g_log<(tv.tv_sec)); - } - - snprintf(buf + len, sz - len, ".%03ld", static_cast(tv.tv_usec) / 1000); - return buf; -} - -static void loggerBackend(const Logging::Entry& entry) { - static thread_local std::stringstream buf; - - buf.str(""); - buf << "msg=" << std::quoted(entry.message); - if (entry.error) { - buf << " oserror=" << std::quoted(entry.error.get()); - } - - if (entry.name) { - buf << " subsystem=" << std::quoted(entry.name.get()); - } - buf << " level=" << entry.level; - if (entry.d_priority) { - buf << " prio=" << static_cast(entry.d_priority); - } - char timebuf[64]; - buf << " ts=" << std::quoted(toTimestampStringMilli(entry.d_timestamp, timebuf, sizeof(timebuf))); - for (auto const& v: entry.values) { - buf << " "; - buf << v.first << "=" << std::quoted(v.second); - } - Logger::Urgency u = entry.d_priority ? Logger::Urgency(entry.d_priority) : Logger::Info; - g_log << u << buf.str() << endl; -} - - -int main(int argc, char **argv) -{ - g_argc = argc; - g_argv = argv; - g_stats.startupTime=time(0); - Utility::srandom(); - versionSetProduct(ProductRecursor); - reportBasicTypes(); - reportOtherTypes(); - - int ret = EXIT_SUCCESS; - - try { -#if HAVE_FIBER_SANITIZER - // Asan needs more stack - ::arg().set("stack-size","stack size per mthread")="400000"; -#else - ::arg().set("stack-size","stack size per mthread")="200000"; -#endif - ::arg().set("soa-minimum-ttl","Don't change")="0"; - ::arg().set("no-shuffle","Don't change")="off"; - ::arg().set("local-port","port to listen on")="53"; - ::arg().set("local-address","IP addresses to listen on, separated by spaces or commas. Also accepts ports.")="127.0.0.1"; - ::arg().setSwitch("non-local-bind", "Enable binding to non-local addresses by using FREEBIND / BINDANY socket options")="no"; - ::arg().set("trace","if we should output heaps of logging. set to 'fail' to only log failing domains")="off"; - ::arg().set("dnssec", "DNSSEC mode: off/process-no-validate/process (default)/log-fail/validate")="process"; - ::arg().set("dnssec-log-bogus", "Log DNSSEC bogus validations")="no"; - ::arg().set("signature-inception-skew", "Allow the signature inception to be off by this number of seconds")="60"; - ::arg().set("daemon","Operate as a daemon")="no"; - ::arg().setSwitch("write-pid","Write a PID file")="yes"; - ::arg().set("loglevel","Amount of logging. Higher is more. Do not set below 3")="6"; - ::arg().set("disable-syslog","Disable logging to syslog, useful when running inside a supervisor that logs stdout")="no"; - ::arg().set("log-timestamp","Print timestamps in log lines, useful to disable when running with a tool that timestamps stdout already")="yes"; - ::arg().set("log-common-errors","If we should log rather common errors")="no"; - ::arg().set("chroot","switch to chroot jail")=""; - ::arg().set("setgid","If set, change group id to this gid for more security" -#ifdef HAVE_SYSTEMD -#define SYSTEMD_SETID_MSG ". When running inside systemd, use the User and Group settings in the unit-file!" - SYSTEMD_SETID_MSG -#endif - )=""; - ::arg().set("setuid","If set, change user id to this uid for more security" -#ifdef HAVE_SYSTEMD - SYSTEMD_SETID_MSG -#endif - )=""; - ::arg().set("network-timeout", "Wait this number of milliseconds for network i/o")="1500"; - ::arg().set("threads", "Launch this number of threads")="2"; - ::arg().set("distributor-threads", "Launch this number of distributor threads, distributing queries to other threads")="0"; - ::arg().set("processes", "Launch this number of processes (EXPERIMENTAL, DO NOT CHANGE)")="1"; // if we un-experimental this, need to fix openssl rand seeding for multiple PIDs! - ::arg().set("config-name","Name of this virtual configuration - will rename the binary image")=""; - ::arg().set("api-config-dir", "Directory where REST API stores config and zones") = ""; - ::arg().set("api-key", "Static pre-shared authentication key for access to the REST API") = ""; - ::arg().setSwitch("webserver", "Start a webserver (for REST API)") = "no"; - ::arg().set("webserver-address", "IP Address of webserver to listen on") = "127.0.0.1"; - ::arg().set("webserver-port", "Port of webserver to listen on") = "8082"; - ::arg().set("webserver-password", "Password required for accessing the webserver") = ""; - ::arg().set("webserver-allow-from","Webserver access is only allowed from these subnets")="127.0.0.1,::1"; - ::arg().set("webserver-loglevel", "Amount of logging in the webserver (none, normal, detailed)") = "normal"; - ::arg().setSwitch("webserver-hash-plaintext-credentials","Whether to hash passwords and api keys supplied in plaintext, to prevent keeping the plaintext version in memory at runtime")="no"; - ::arg().set("carbon-ourname", "If set, overrides our reported hostname for carbon stats")=""; - ::arg().set("carbon-server", "If set, send metrics in carbon (graphite) format to this server IP address")=""; - ::arg().set("carbon-interval", "Number of seconds between carbon (graphite) updates")="30"; - ::arg().set("carbon-namespace", "If set overwrites the first part of the carbon string")="pdns"; - ::arg().set("carbon-instance", "If set overwrites the instance name default")="recursor"; - - ::arg().set("statistics-interval", "Number of seconds between printing of recursor statistics, 0 to disable")="1800"; - ::arg().set("quiet","Suppress logging of questions and answers")=""; - ::arg().set("logging-facility","Facility to log messages as. 0 corresponds to local0")=""; - ::arg().set("config-dir","Location of configuration directory (recursor.conf)")=SYSCONFDIR; - ::arg().set("socket-owner","Owner of socket")=""; - ::arg().set("socket-group","Group of socket")=""; - ::arg().set("socket-mode", "Permissions for socket")=""; - - ::arg().set("socket-dir",string("Where the controlsocket will live, ")+LOCALSTATEDIR+"/pdns-recursor when unset and not chrooted" -#ifdef HAVE_SYSTEMD - + ". Set to the RUNTIME_DIRECTORY environment variable when that variable has a value (e.g. under systemd).")=""; - auto runtimeDir = getenv("RUNTIME_DIRECTORY"); - if (runtimeDir != nullptr) { - ::arg().set("socket-dir") = runtimeDir; - } -#else - )=""; -#endif - ::arg().set("query-local-address","Source IP address for sending queries")="0.0.0.0"; - ::arg().set("client-tcp-timeout","Timeout in seconds when talking to TCP clients")="2"; - ::arg().set("max-mthreads", "Maximum number of simultaneous Mtasker threads")="2048"; - ::arg().set("max-tcp-clients","Maximum number of simultaneous TCP clients")="128"; - ::arg().set("max-concurrent-requests-per-tcp-connection", "Maximum number of requests handled concurrently per TCP connection") = "10"; - ::arg().set("server-down-max-fails","Maximum number of consecutive timeouts (and unreachables) to mark a server as down ( 0 => disabled )")="64"; - ::arg().set("server-down-throttle-time","Number of seconds to throttle all queries to a server after being marked as down")="60"; - ::arg().set("dont-throttle-names", "Do not throttle nameservers with this name or suffix")=""; - ::arg().set("dont-throttle-netmasks", "Do not throttle nameservers with this IP netmask")=""; - ::arg().set("non-resolving-ns-max-fails", "Number of failed address resolves of a nameserver to start throttling it, 0 is disabled")="5"; - ::arg().set("non-resolving-ns-throttle-time", "Number of seconds to throttle a nameserver with a name failing to resolve")="60"; - - ::arg().set("hint-file", "If set, load root hints from this file")=""; - ::arg().set("max-cache-entries", "If set, maximum number of entries in the main cache")="1000000"; - ::arg().set("max-negative-ttl", "maximum number of seconds to keep a negative cached entry in memory")="3600"; - ::arg().set("max-cache-bogus-ttl", "maximum number of seconds to keep a Bogus (positive or negative) cached entry in memory")="3600"; - ::arg().set("max-cache-ttl", "maximum number of seconds to keep a cached entry in memory")="86400"; - ::arg().set("packetcache-ttl", "maximum number of seconds to keep a cached entry in packetcache")="3600"; - ::arg().set("max-packetcache-entries", "maximum number of entries to keep in the packetcache")="500000"; - ::arg().set("packetcache-servfail-ttl", "maximum number of seconds to keep a cached servfail entry in packetcache")="60"; - ::arg().set("server-id", "Returned when queried for 'id.server' TXT or NSID, defaults to hostname, set custom or 'disabled'")=""; - ::arg().set("stats-ringbuffer-entries", "maximum number of packets to store statistics for")="10000"; - ::arg().set("version-string", "string reported on version.pdns or version.bind")=fullVersionString(); - ::arg().set("allow-from", "If set, only allow these comma separated netmasks to recurse")=LOCAL_NETS; - ::arg().set("allow-from-file", "If set, load allowed netmasks from this file")=""; - ::arg().set("allow-notify-for", "If set, NOTIFY requests for these zones will be allowed")=""; - ::arg().set("allow-notify-for-file", "If set, load NOTIFY-allowed zones from this file")=""; - ::arg().set("allow-notify-from", "If set, NOTIFY requests from these comma separated netmasks will be allowed")=""; - ::arg().set("allow-notify-from-file", "If set, load NOTIFY-allowed netmasks from this file")=""; - ::arg().set("entropy-source", "If set, read entropy from this file")="/dev/urandom"; - ::arg().set("dont-query", "If set, do not query these netmasks for DNS data")=DONT_QUERY; - ::arg().set("max-tcp-per-client", "If set, maximum number of TCP sessions per client (IP address)")="0"; - ::arg().set("max-tcp-queries-per-connection", "If set, maximum number of TCP queries in a TCP connection")="0"; - ::arg().set("spoof-nearmiss-max", "If non-zero, assume spoofing after this many near misses")="1"; - ::arg().set("single-socket", "If set, only use a single socket for outgoing queries")="off"; - ::arg().set("auth-zones", "Zones for which we have authoritative data, comma separated domain=file pairs ")=""; - ::arg().set("lua-config-file", "More powerful configuration options")=""; - ::arg().setSwitch("allow-trust-anchor-query", "Allow queries for trustanchor.server CH TXT and negativetrustanchor.server CH TXT")="no"; - - ::arg().set("forward-zones", "Zones for which we forward queries, comma separated domain=ip pairs")=""; - ::arg().set("forward-zones-recurse", "Zones for which we forward queries with recursion bit, comma separated domain=ip pairs")=""; - ::arg().set("forward-zones-file", "File with (+)domain=ip pairs for forwarding")=""; - ::arg().set("export-etc-hosts", "If we should serve up contents from /etc/hosts")="off"; - ::arg().set("export-etc-hosts-search-suffix", "Also serve up the contents of /etc/hosts with this suffix")=""; - ::arg().set("etc-hosts-file", "Path to 'hosts' file")="/etc/hosts"; - ::arg().set("serve-rfc1918", "If we should be authoritative for RFC 1918 private IP space")="yes"; - ::arg().set("lua-dns-script", "Filename containing an optional 'lua' script that will be used to modify dns answers")=""; - ::arg().set("lua-maintenance-interval", "Number of seconds between calls to the lua user defined maintenance() function")="1"; - ::arg().set("latency-statistic-size","Number of latency values to calculate the qa-latency average")="10000"; - ::arg().setSwitch( "disable-packetcache", "Disable packetcache" )= "no"; - ::arg().set("ecs-ipv4-bits", "Number of bits of IPv4 address to pass for EDNS Client Subnet")="24"; - ::arg().set("ecs-ipv4-cache-bits", "Maximum number of bits of IPv4 mask to cache ECS response")="24"; - ::arg().set("ecs-ipv6-bits", "Number of bits of IPv6 address to pass for EDNS Client Subnet")="56"; - ::arg().set("ecs-ipv6-cache-bits", "Maximum number of bits of IPv6 mask to cache ECS response")="56"; - ::arg().setSwitch("ecs-ipv4-never-cache", "If we should never cache IPv4 ECS responses")="no"; - ::arg().setSwitch("ecs-ipv6-never-cache", "If we should never cache IPv6 ECS responses")="no"; - ::arg().set("ecs-minimum-ttl-override", "The minimum TTL for records in ECS-specific answers")="1"; - ::arg().set("ecs-cache-limit-ttl", "Minimum TTL to cache ECS response")="0"; - ::arg().set("edns-subnet-whitelist", "List of netmasks and domains that we should enable EDNS subnet for (deprecated)")=""; - ::arg().set("edns-subnet-allow-list", "List of netmasks and domains that we should enable EDNS subnet for")=""; - ::arg().set("ecs-add-for", "List of client netmasks for which EDNS Client Subnet will be added")="0.0.0.0/0, ::/0, " LOCAL_NETS_INVERSE; - ::arg().set("ecs-scope-zero-address", "Address to send to allow-listed authoritative servers for incoming queries with ECS prefix-length source of 0")=""; - ::arg().setSwitch( "use-incoming-edns-subnet", "Pass along received EDNS Client Subnet information")="no"; - ::arg().setSwitch( "pdns-distributes-queries", "If PowerDNS itself should distribute queries over threads")="yes"; - ::arg().setSwitch( "root-nx-trust", "If set, believe that an NXDOMAIN from the root means the TLD does not exist")="yes"; - ::arg().setSwitch( "any-to-tcp","Answer ANY queries with tc=1, shunting to TCP" )="no"; - ::arg().setSwitch( "lowercase-outgoing","Force outgoing questions to lowercase")="no"; - ::arg().setSwitch("gettag-needs-edns-options", "If EDNS Options should be extracted before calling the gettag() hook")="no"; - ::arg().set("udp-truncation-threshold", "Maximum UDP response size before we truncate")="1232"; - ::arg().set("edns-outgoing-bufsize", "Outgoing EDNS buffer size")="1232"; - ::arg().set("minimum-ttl-override", "The minimum TTL")="1"; - ::arg().set("max-qperq", "Maximum outgoing queries per query")="60"; - ::arg().set("max-ns-address-qperq", "Maximum outgoing NS address queries per query")="10"; - ::arg().set("max-total-msec", "Maximum total wall-clock time per query in milliseconds, 0 for unlimited")="7000"; - ::arg().set("max-recursion-depth", "Maximum number of internal recursion calls per query, 0 for unlimited")="40"; - ::arg().set("max-udp-queries-per-round", "Maximum number of UDP queries processed per recvmsg() round, before returning back to normal processing")="10000"; - ::arg().set("protobuf-use-kernel-timestamp", "Compute the latency of queries in protobuf messages by using the timestamp set by the kernel when the query was received (when available)")=""; - ::arg().set("distribution-pipe-buffer-size", "Size in bytes of the internal buffer of the pipe used by the distributor to pass incoming queries to a worker thread")="0"; - - ::arg().set("include-dir","Include *.conf files from this directory")=""; - ::arg().set("security-poll-suffix","Domain name from which to query security update notifications")="secpoll.powerdns.com."; - - ::arg().setSwitch("reuseport","Enable SO_REUSEPORT allowing multiple recursors processes to listen to 1 address")="no"; - - ::arg().setSwitch("snmp-agent", "If set, register as an SNMP agent")="no"; - ::arg().set("snmp-master-socket", "If set and snmp-agent is set, the socket to use to register to the SNMP daemon (deprecated)")=""; - ::arg().set("snmp-daemon-socket", "If set and snmp-agent is set, the socket to use to register to the SNMP daemon")=""; - - std::string defaultAPIDisabledStats = "cache-bytes, packetcache-bytes, special-memory-usage"; - for (size_t idx = 0; idx < 32; idx++) { - defaultAPIDisabledStats += ", ecs-v4-response-bits-" + std::to_string(idx + 1); - } - for (size_t idx = 0; idx < 128; idx++) { - defaultAPIDisabledStats += ", ecs-v6-response-bits-" + std::to_string(idx + 1); - } - std::string defaultDisabledStats = defaultAPIDisabledStats + ", cumul-clientanswers, cumul-authanswers, policy-hits"; - - ::arg().set("stats-api-blacklist", "List of statistics that are disabled when retrieving the complete list of statistics via the API (deprecated)")=defaultAPIDisabledStats; - ::arg().set("stats-carbon-blacklist", "List of statistics that are prevented from being exported via Carbon (deprecated)")=defaultDisabledStats; - ::arg().set("stats-rec-control-blacklist", "List of statistics that are prevented from being exported via rec_control get-all (deprecated)")=defaultDisabledStats; - ::arg().set("stats-snmp-blacklist", "List of statistics that are prevented from being exported via SNMP (deprecated)")=defaultDisabledStats; - - ::arg().set("stats-api-disabled-list", "List of statistics that are disabled when retrieving the complete list of statistics via the API")=defaultAPIDisabledStats; - ::arg().set("stats-carbon-disabled-list", "List of statistics that are prevented from being exported via Carbon")=defaultDisabledStats; - ::arg().set("stats-rec-control-disabled-list", "List of statistics that are prevented from being exported via rec_control get-all")=defaultDisabledStats; - ::arg().set("stats-snmp-disabled-list", "List of statistics that are prevented from being exported via SNMP")=defaultDisabledStats; - - ::arg().set("tcp-fast-open", "Enable TCP Fast Open support on the listening sockets, using the supplied numerical value as the queue size")="0"; - ::arg().set("tcp-fast-open-connect", "Enable TCP Fast Open support on outgoing sockets")="no"; - ::arg().set("nsec3-max-iterations", "Maximum number of iterations allowed for an NSEC3 record")="150"; - - ::arg().set("cpu-map", "Thread to CPU mapping, space separated thread-id=cpu1,cpu2..cpuN pairs")=""; - - ::arg().setSwitch("log-rpz-changes", "Log additions and removals to RPZ zones at Info level")="no"; - - ::arg().set("xpf-allow-from","XPF information is only processed from these subnets")=""; - ::arg().set("xpf-rr-code","XPF option code to use")="0"; - - ::arg().set("proxy-protocol-from", "A Proxy Protocol header is only allowed from these subnets")=""; - ::arg().set("proxy-protocol-maximum-size", "The maximum size of a proxy protocol payload, including the TLV values")="512"; - - ::arg().set("dns64-prefix", "DNS64 prefix")=""; - - ::arg().set("udp-source-port-min", "Minimum UDP port to bind on")="1024"; - ::arg().set("udp-source-port-max", "Maximum UDP port to bind on")="65535"; - ::arg().set("udp-source-port-avoid", "List of comma separated UDP port number to avoid")="11211"; - ::arg().set("rng", "Specify random number generator to use. Valid values are auto,sodium,openssl,getrandom,arc4random,urandom.")="auto"; - ::arg().set("public-suffix-list-file", "Path to the Public Suffix List file, if any")=""; - ::arg().set("distribution-load-factor", "The load factor used when PowerDNS is distributing queries to worker threads")="0.0"; - - ::arg().setSwitch("qname-minimization", "Use Query Name Minimization")="yes"; - ::arg().setSwitch("nothing-below-nxdomain", "When an NXDOMAIN exists in cache for a name with fewer labels than the qname, send NXDOMAIN without doing a lookup (see RFC 8020)")="dnssec"; - ::arg().set("max-generate-steps", "Maximum number of $GENERATE steps when loading a zone from a file")="0"; - ::arg().set("max-include-depth", "Maximum nested $INCLUDE depth when loading a zone from a file")="20"; - ::arg().set("record-cache-shards", "Number of shards in the record cache")="1024"; - ::arg().set("refresh-on-ttl-perc", "If a record is requested from the cache and only this % of original TTL remains, refetch") = "0"; - - ::arg().set("x-dnssec-names", "Collect DNSSEC statistics for names or suffixes in this list in separate x-dnssec counters")=""; - -#ifdef NOD_ENABLED - ::arg().set("new-domain-tracking", "Track newly observed domains (i.e. never seen before).")="no"; - ::arg().set("new-domain-log", "Log newly observed domains.")="yes"; - ::arg().set("new-domain-lookup", "Perform a DNS lookup newly observed domains as a subdomain of the configured domain")=""; - ::arg().set("new-domain-history-dir", "Persist new domain tracking data here to persist between restarts")=string(NODCACHEDIR)+"/nod"; - ::arg().set("new-domain-whitelist", "List of domains (and implicitly all subdomains) which will never be considered a new domain (deprecated)")=""; - ::arg().set("new-domain-ignore-list", "List of domains (and implicitly all subdomains) which will never be considered a new domain")=""; - ::arg().set("new-domain-db-size", "Size of the DB used to track new domains in terms of number of cells. Defaults to 67108864")="67108864"; - ::arg().set("new-domain-pb-tag", "If protobuf is configured, the tag to use for messages containing newly observed domains. Defaults to 'pdns-nod'")="pdns-nod"; - ::arg().set("unique-response-tracking", "Track unique responses (tuple of query name, type and RR).")="no"; - ::arg().set("unique-response-log", "Log unique responses")="yes"; - ::arg().set("unique-response-history-dir", "Persist unique response tracking data here to persist between restarts")=string(NODCACHEDIR)+"/udr"; - ::arg().set("unique-response-db-size", "Size of the DB used to track unique responses in terms of number of cells. Defaults to 67108864")="67108864"; - ::arg().set("unique-response-pb-tag", "If protobuf is configured, the tag to use for messages containing unique DNS responses. Defaults to 'pdns-udr'")="pdns-udr"; -#endif /* NOD_ENABLED */ - - ::arg().setSwitch("extended-resolution-errors", "If set, send an EDNS Extended Error extension on resolution failures, like DNSSEC validation errors")="no"; - - ::arg().set("aggressive-nsec-cache-size", "The number of records to cache in the aggressive cache. If set to a value greater than 0, and DNSSEC processing or validation is enabled, the recursor will cache NSEC and NSEC3 records to generate negative answers, as defined in rfc8198")="100000"; - - ::arg().set("edns-padding-from", "List of netmasks (proxy IP in case of XPF or proxy-protocol presence, client IP otherwise) for which EDNS padding will be enabled in responses, provided that 'edns-padding-mode' applies")=""; - ::arg().set("edns-padding-mode", "Whether to add EDNS padding to all responses ('always') or only to responses for queries containing the EDNS padding option ('padded-queries-only', the default). In both modes, padding will only be added to responses for queries coming from `edns-padding-from`_ sources")="padded-queries-only"; - ::arg().set("edns-padding-tag", "Packetcache tag associated to responses sent with EDNS padding, to prevent sending these to clients for which padding is not enabled.")="7830"; - - ::arg().setSwitch("dot-to-port-853", "Force DoT connection to target port 853 if DoT compiled in")="yes"; - ::arg().set("dot-to-auth-names", "Use DoT to authoritative servers with these names or suffixes")=""; - ::arg().set("event-trace-enabled", "If set, event traces are collected and send out via protobuf logging (1), logfile (2) or both(3)")="0"; - - ::arg().set("tcp-out-max-idle-ms", "Time TCP/DoT connections are left idle in milliseconds or 0 if no limit") = "10000"; - ::arg().set("tcp-out-max-idle-per-auth", "Maximum number of idle TCP/DoT connections to a specific IP per thread, 0 means do not keep idle connections open") = "10"; - ::arg().set("tcp-out-max-queries", "Maximum total number of queries per TCP/DoT connection, 0 means no limit") = "0"; - ::arg().set("tcp-out-max-idle-per-thread", "Maximum number of idle TCP/DoT connections per thread") = "100"; - ::arg().setSwitch("structured-logging", "Prefer structured logging") = "yes"; - - ::arg().setCmd("help","Provide a helpful message"); - ::arg().setCmd("version","Print version string"); - ::arg().setCmd("config","Output blank configuration"); - ::arg().setDefaults(); - g_log.toConsole(Logger::Info); - ::arg().laxParse(argc,argv); // do a lax parse - - if(::arg().mustDo("version")) { - showProductVersion(); - showBuildConfiguration(); - exit(0); - } - - string configname=::arg()["config-dir"]+"/recursor.conf"; - if(::arg()["config-name"]!="") { - configname=::arg()["config-dir"]+"/recursor-"+::arg()["config-name"]+".conf"; - s_programname+="-"+::arg()["config-name"]; - } - cleanSlashes(configname); - - if(!::arg().getCommands().empty()) { - cerr<<"Fatal: non-option"; - if (::arg().getCommands().size() > 1) { - cerr<<"s"; - } - cerr<<" ("; - bool first = true; - for (const auto& c : ::arg().getCommands()) { - if (!first) { - cerr<<", "; - } - first = false; - cerr<error("No such file", "Unable to parse configuration file", "config_file", Logging::Loggable(configname))); - } - - ::arg().parse(argc,argv); - - if( !::arg()["chroot"].empty() && !::arg()["api-config-dir"].empty() ) { - SLOG(g_log<info("Cannot use chroot and enable the API at the same time")); - exit(EXIT_FAILURE); - } - - if (::arg()["socket-dir"].empty()) { - if (::arg()["chroot"].empty()) - ::arg().set("socket-dir") = std::string(LOCALSTATEDIR) + "/pdns-recursor"; - else - ::arg().set("socket-dir") = "/"; - } - - if(::arg().asNum("threads")==1) { - if (::arg().mustDo("pdns-distributes-queries")) { - SLOG(g_log<v(1)->info("Only one thread, no need to distribute queries ourselves")); - ::arg().set("pdns-distributes-queries")="no"; - } - } - - if(::arg().mustDo("pdns-distributes-queries") && ::arg().asNum("distributor-threads") <= 0) { - SLOG(g_log<v(1)->info("Asked to run with pdns-distributes-queries set but no distributor threads, raising to 1")); - ::arg().set("distributor-threads")="1"; - } - - if (!::arg().mustDo("pdns-distributes-queries")) { - ::arg().set("distributor-threads")="0"; - } - - if(::arg().mustDo("help")) { - cout<<"syntax:"<(::arg().asNum("record-cache-shards")); - g_negCache = std::make_unique(::arg().asNum("record-cache-shards")); - - g_quiet=::arg().mustDo("quiet"); - Logger::Urgency logUrgency = (Logger::Urgency)::arg().asNum("loglevel"); - - if (logUrgency < Logger::Error) - logUrgency = Logger::Error; - if(!g_quiet && logUrgency < Logger::Info) { // Logger::Info=6, Logger::Debug=7 - logUrgency = Logger::Info; // if you do --quiet=no, you need Info to also see the query log - } - g_log.setLoglevel(logUrgency); - g_log.toConsole(logUrgency); - - ret = serviceMain(argc, argv); - } - catch(PDNSException &ae) { - g_log< #include "rec-main.hh" +#include "aggressive_nsec.hh" +#include "capabilities.hh" +#include "arguments.hh" +#include "dns_random.hh" +#include "rec_channel.hh" +#include "rec-tcpout.hh" +#include "version.hh" +#include "query-local-address.hh" +#include "validate-recursor.hh" +#include "pubsuffix.hh" +#include "opensslsigners.hh" +#include "threadname.hh" +#include "ws-recursor.hh" +#include "rec-taskqueue.hh" +#include "secpoll-recursor.hh" + +#ifdef NOD_ENABLED +#include "nod.hh" +#include "logging.hh" +#endif /* NOD_ENABLED */ + +#ifdef HAVE_LIBSODIUM +#include +#endif + +#ifdef HAVE_SYSTEMD +#include +#endif + +static thread_local uint64_t t_protobufServersGeneration; +static thread_local uint64_t t_outgoingProtobufServersGeneration; + +#ifdef HAVE_FSTRM +thread_local std::shared_ptr>> t_frameStreamServers{nullptr}; +thread_local uint64_t t_frameStreamServersGeneration; +#endif /* HAVE_FSTRM */ + +string s_programname="pdns_recursor"; +string s_pidfname; +RecursorControlChannel s_rcc; // only active in the handler thread + +#ifdef NOD_ENABLED +bool g_nodEnabled; +DNSName g_nodLookupDomain; +bool g_nodLog; +SuffixMatchNode g_nodDomainWL; +std::string g_nod_pbtag; +bool g_udrEnabled; +bool g_udrLog; +std::string g_udr_pbtag; +thread_local std::shared_ptr t_nodDBp; +thread_local std::shared_ptr t_udrDBp; +#endif /* NOD_ENABLED */ + +std::atomic statsWanted; +unsigned int g_numWorkerThreads; +uint32_t g_disthashseed; +bool g_weDistributeQueries; // if true, 1 or more threads listen on the incoming query sockets and distribute them to workers +bool g_useIncomingECS; +uint16_t g_xpfRRCode{0}; +NetmaskGroup g_proxyProtocolACL; +boost::optional g_dns64Prefix{boost::none}; +DNSName g_dns64PrefixReverse; +std::shared_ptr g_initialDomainMap; // new threads needs this to be setup +std::shared_ptr g_initialAllowFrom; // new thread needs to be setup with this +std::shared_ptr g_initialAllowNotifyFrom; // new threads need this to be setup +std::shared_ptr g_initialAllowNotifyFor; // new threads need this to be setup +bool g_logRPZChanges{false}; +unsigned int g_numDistributorThreads; +unsigned int g_numThreads; +static time_t g_statisticsInterval; +bool s_addExtendedResolutionDNSErrors; +static std::atomic counter; +int g_argc; +char** g_argv; + +/* without reuseport, all listeners share the same sockets */ +deferredAdd_t g_deferredAdds; + +/* first we have the handler thread, t_id == 0 (some other + helper threads like SNMP might have t_id == 0 as well) + then the distributor threads if any + and finally the workers */ +std::vector s_threadInfos; + +ArgvMap &arg() +{ + static ArgvMap theArg; + return theArg; +} + +static FDMultiplexer* getMultiplexer() +{ + FDMultiplexer* ret; + for(const auto& i : FDMultiplexer::getMultiplexerMap()) { + try { + ret=i.second(); + return ret; + } + catch(FDMultiplexerException &fe) { + g_log<>> startProtobufServers(const ProtobufExportConfig& config) +{ + auto result = std::make_shared>>(); + + for (const auto& server : config.servers) { + try { + auto logger = make_unique(server, config.timeout, 100*config.maxQueuedEntries, config.reconnectWaitTime, config.asyncConnect); + logger->setLogQueries(config.logQueries); + logger->setLogResponses(config.logResponses); + result->emplace_back(std::move(logger)); + } + catch(const std::exception& e) { + g_log<& luaconfsLocal) +{ + if (!luaconfsLocal->protobufExportConfig.enabled) { + if (t_protobufServers) { + for (auto& server : *t_protobufServers) { + server->stop(); + } + t_protobufServers.reset(); + } + + return false; + } + + /* if the server was not running, or if it was running according to a + previous configuration */ + if (!t_protobufServers || + t_protobufServersGeneration < luaconfsLocal->generation) { + + if (t_protobufServers) { + for (auto& server : *t_protobufServers) { + server->stop(); + } + } + t_protobufServers.reset(); + + t_protobufServers = startProtobufServers(luaconfsLocal->protobufExportConfig); + t_protobufServersGeneration = luaconfsLocal->generation; + } + + return true; +} + +bool checkOutgoingProtobufExport(LocalStateHolder& luaconfsLocal) +{ + if (!luaconfsLocal->outgoingProtobufExportConfig.enabled) { + if (t_outgoingProtobufServers) { + for (auto& server : *t_outgoingProtobufServers) { + server->stop(); + } + } + t_outgoingProtobufServers.reset(); + + return false; + } + + /* if the server was not running, or if it was running according to a + previous configuration */ + if (!t_outgoingProtobufServers || + t_outgoingProtobufServersGeneration < luaconfsLocal->generation) { + + if (t_outgoingProtobufServers) { + for (auto& server : *t_outgoingProtobufServers) { + server->stop(); + } + } + t_outgoingProtobufServers.reset(); + + t_outgoingProtobufServers = startProtobufServers(luaconfsLocal->outgoingProtobufExportConfig); + t_outgoingProtobufServersGeneration = luaconfsLocal->generation; + } + + return true; +} + +void protobufLogQuery(LocalStateHolder& luaconfsLocal, const boost::uuids::uuid& uniqueId, const ComboAddress& remote, const ComboAddress& local, const Netmask& ednssubnet, bool tcp, uint16_t id, size_t len, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::unordered_set& policyTags, const std::string& requestorId, const std::string& deviceId, const std::string& deviceName, const std::map& meta) +{ + if (!t_protobufServers) { + return; + } + + Netmask requestorNM(remote, remote.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6); + ComboAddress requestor = requestorNM.getMaskedNetwork(); + requestor.setPort(remote.getPort()); + + pdns::ProtoZero::RecMessage m{128, std::string::size_type(policyTags.empty() ? 0 : 64)}; // It's a guess + m.setType(pdns::ProtoZero::Message::MessageType::DNSQueryType); + m.setRequest(uniqueId, requestor, local, qname, qtype, qclass, id, tcp ? pdns::ProtoZero::Message::TransportProtocol::TCP : pdns::ProtoZero::Message::TransportProtocol::UDP, len); + m.setServerIdentity(SyncRes::s_serverID); + m.setEDNSSubnet(ednssubnet, ednssubnet.isIPv4() ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6); + m.setRequestorId(requestorId); + m.setDeviceId(deviceId); + m.setDeviceName(deviceName); + + if (!policyTags.empty()) { + m.addPolicyTags(policyTags); + } + for (const auto& mit : meta) { + m.setMeta(mit.first, mit.second.stringVal, mit.second.intVal); + } + + std::string msg(m.finishAndMoveBuf()); + for (auto& server : *t_protobufServers) { + server->queueData(msg); + } +} + +void protobufLogResponse(pdns::ProtoZero::RecMessage& message) +{ + if (!t_protobufServers) { + return; + } + + std::string msg(message.finishAndMoveBuf()); + for (auto& server : *t_protobufServers) { + server->queueData(msg); + } +} + +void protobufLogResponse(const struct dnsheader* dh, LocalStateHolder& luaconfsLocal, + const RecursorPacketCache::OptPBData& pbData, const struct timeval& tv, + bool tcp, const ComboAddress& source, const ComboAddress& destination, + const EDNSSubnetOpts& ednssubnet, + const boost::uuids::uuid& uniqueId, const string& requestorId, const string& deviceId, + const string& deviceName, const std::map& meta, + const RecEventTrace& eventTrace) +{ + pdns::ProtoZero::RecMessage pbMessage(pbData ? pbData->d_message : "", pbData ? pbData->d_response : "", 64, 10); // The extra bytes we are going to add + // Normally we take the immutable string from the cache and append a few values, but if it's not there (can this happen?) + // we start with an empty string and append the minimal + if (!pbData) { + pbMessage.setType(pdns::ProtoZero::Message::MessageType::DNSResponseType); + pbMessage.setServerIdentity(SyncRes::s_serverID); + } + + // In response part + if (g_useKernelTimestamp && tv.tv_sec) { + pbMessage.setQueryTime(tv.tv_sec, tv.tv_usec); + } + else { + pbMessage.setQueryTime(g_now.tv_sec, g_now.tv_usec); + } + + // In message part + Netmask requestorNM(source, source.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6); + ComboAddress requestor = requestorNM.getMaskedNetwork(); + pbMessage.setMessageIdentity(uniqueId); + pbMessage.setFrom(requestor); + pbMessage.setTo(destination); + pbMessage.setSocketProtocol(tcp ? pdns::ProtoZero::Message::TransportProtocol::TCP : pdns::ProtoZero::Message::TransportProtocol::UDP); + pbMessage.setId(dh->id); + + pbMessage.setTime(); + pbMessage.setEDNSSubnet(ednssubnet.source, ednssubnet.source.isIPv4() ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6); + pbMessage.setRequestorId(requestorId); + pbMessage.setDeviceId(deviceId); + pbMessage.setDeviceName(deviceName); + pbMessage.setFromPort(source.getPort()); + pbMessage.setToPort(destination.getPort()); + for (const auto& m : meta) { + pbMessage.setMeta(m.first, m.second.stringVal, m.second.intVal); + } +#ifdef NOD_ENABLED + if (g_nodEnabled) { + pbMessage.setNewlyObservedDomain(false); + } +#endif + if (eventTrace.enabled() && SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_pb) { + pbMessage.addEvents(eventTrace); + } + protobufLogResponse(pbMessage); +} + +#ifdef HAVE_FSTRM + +static std::shared_ptr>> startFrameStreamServers(const FrameStreamExportConfig& config) +{ + auto result = std::make_shared>>(); + + for (const auto& server : config.servers) { + try { + std::unordered_map options; + options["bufferHint"] = config.bufferHint; + options["flushTimeout"] = config.flushTimeout; + options["inputQueueSize"] = config.inputQueueSize; + options["outputQueueSize"] = config.outputQueueSize; + options["queueNotifyThreshold"] = config.queueNotifyThreshold; + options["reopenInterval"] = config.reopenInterval; + FrameStreamLogger *fsl = nullptr; + try { + ComboAddress address(server); + fsl = new FrameStreamLogger(address.sin4.sin_family, address.toStringWithPort(), true, options); + } + catch (const PDNSException& e) { + fsl = new FrameStreamLogger(AF_UNIX, server, true, options); + } + fsl->setLogQueries(config.logQueries); + fsl->setLogResponses(config.logResponses); + result->emplace_back(fsl); + } + catch(const std::exception& e) { + g_log<& luaconfsLocal) +{ + if (!luaconfsLocal->frameStreamExportConfig.enabled) { + if (t_frameStreamServers) { + // dt's take care of cleanup + t_frameStreamServers.reset(); + } + + return false; + } + + /* if the server was not running, or if it was running according to a + previous configuration */ + if (!t_frameStreamServers || + t_frameStreamServersGeneration < luaconfsLocal->generation) { + + if (t_frameStreamServers) { + // dt's take care of cleanup + t_frameStreamServers.reset(); + } + + t_frameStreamServers = startFrameStreamServers(luaconfsLocal->frameStreamExportConfig); + t_frameStreamServersGeneration = luaconfsLocal->generation; + } + + return true; +} +#endif /* HAVE_FSTRM */ + +static void makeControlChannelSocket(int processNum=-1) +{ + string sockname=::arg()["socket-dir"]+"/"+s_programname; + if(processNum >= 0) + sockname += "."+std::to_string(processNum); + sockname+=".controlsocket"; + s_rcc.listen(sockname); + + int sockowner = -1; + int sockgroup = -1; + + if (!::arg().isEmpty("socket-group")) + sockgroup=::arg().asGid("socket-group"); + if (!::arg().isEmpty("socket-owner")) + sockowner=::arg().asUid("socket-owner"); + + if (sockgroup > -1 || sockowner > -1) { + if(chown(sockname.c_str(), sockowner, sockgroup) < 0) { + unixDie("Failed to chown control socket"); + } + } + + // do mode change if socket-mode is given + if(!::arg().isEmpty("socket-mode")) { + mode_t sockmode=::arg().asMode("socket-mode"); + if(chmod(sockname.c_str(), sockmode) < 0) { + unixDie("Failed to chmod control socket"); + } + } +} + +static void writePid(void) +{ + if(!::arg().mustDo("write-pid")) + return; + ofstream of(s_pidfname.c_str(), std::ios_base::app); + if(of) + of<< Utility::getpid() < > parseCPUMap() +{ + std::map > result; + + const std::string value = ::arg()["cpu-map"]; + + if (!value.empty() && !isSettingThreadCPUAffinitySupported()) { + g_log< parts; + + stringtok(parts, value, " \t"); + + for(const auto& part : parts) { + if (part.find('=') == string::npos) + continue; + + try { + auto headers = splitField(part, '='); + boost::trim(headers.first); + boost::trim(headers.second); + + unsigned int threadId = pdns_stou(headers.first); + std::vector cpus; + + stringtok(cpus, headers.second, ","); + + for(const auto& cpu : cpus) { + int cpuId = std::stoi(cpu); + + result[threadId].insert(cpuId); + } + } + catch(const std::exception& e) { + g_log< >& cpusMap, unsigned int n, pthread_t tid) +{ + const auto& cpuMapping = cpusMap.find(n); + if (cpuMapping != cpusMap.cend()) { + int rc = mapThreadToCPUList(tid, cpuMapping->second); + if (rc == 0) { + g_log<second) { + g_log<second) { + g_log<(num_cells); + try { + t_nodDBp->setCacheDir(::arg()["new-domain-history-dir"]); + } + catch (const PDNSException& e) { + g_log<init()) { + g_log<(num_cells); + try { + t_udrDBp->setCacheDir(::arg()["unique-response-history-dir"]); + } + catch (const PDNSException& e) { + g_log<init()) { + g_log< parts; + stringtok(parts, wlist, ",; "); + for(const auto& a : parts) { + g_nodDomainWL.add(DNSName(a)); + } +} + +static void setupNODGlobal() +{ + // Setup NOD subsystem + g_nodEnabled = ::arg().mustDo("new-domain-tracking"); + g_nodLookupDomain = DNSName(::arg()["new-domain-lookup"]); + g_nodLog = ::arg().mustDo("new-domain-log"); + parseNODIgnorelist(::arg()["new-domain-whitelist"]); + parseNODIgnorelist(::arg()["new-domain-ignore-list"]); + + // Setup Unique DNS Response subsystem + g_udrEnabled = ::arg().mustDo("unique-response-tracking"); + g_udrLog = ::arg().mustDo("unique-response-log"); +} +#endif /* NOD_ENABLED */ + +static void daemonize(void) +{ + if(fork()) + exit(0); // bye bye + + setsid(); + + int i=open("/dev/null",O_RDWR); /* open stdin */ + if(i < 0) + g_log< availFDs) { + unsigned int hardlimit= getFilenumLimit(true); + if(hardlimit >= wantFDs) { + setFilenumLimit(wantFDs); + g_log<(tv.tv_sec)); + } + + snprintf(buf + len, sz - len, ".%03ld", static_cast(tv.tv_usec) / 1000); + return buf; +} + +static void loggerBackend(const Logging::Entry& entry) { + static thread_local std::stringstream buf; + + buf.str(""); + buf << "msg=" << std::quoted(entry.message); + if (entry.error) { + buf << " oserror=" << std::quoted(entry.error.get()); + } + + if (entry.name) { + buf << " subsystem=" << std::quoted(entry.name.get()); + } + buf << " level=" << entry.level; + if (entry.d_priority) { + buf << " prio=" << static_cast(entry.d_priority); + } + char timebuf[64]; + buf << " ts=" << std::quoted(toTimestampStringMilli(entry.d_timestamp, timebuf, sizeof(timebuf))); + for (auto const& v: entry.values) { + buf << " "; + buf << v.first << "=" << std::quoted(v.second); + } + Logger::Urgency u = entry.d_priority ? Logger::Urgency(entry.d_priority) : Logger::Info; + g_log << u << buf.str() << endl; +} +void makeThreadPipes() +{ + auto pipeBufferSize = ::arg().asNum("distribution-pipe-buffer-size"); + if (pipeBufferSize > 0) { + g_log< 0) { + if (!setPipeBufferSize(threadInfos.pipes.writeQueriesToThread, pipeBufferSize)) { + int err = errno; + g_log< 0) { + g_log<cacheHits; + uint64_t cacheMisses = g_recCache->cacheMisses; + uint64_t cacheSize = g_recCache->size(); + auto rc_stats = g_recCache->stats(); + double r = rc_stats.second == 0 ? 0.0 : (100.0 * rc_stats.first / rc_stats.second); + uint64_t negCacheSize = g_negCache->size(); + auto taskPushes = getTaskPushes(); + auto taskExpired = getTaskExpired(); + auto taskSize = getTaskSize(); + + if(g_stats.qcounter && (cacheHits + cacheMisses) && SyncRes::s_queries && SyncRes::s_outqueries) { + g_log<(pleaseGetThrottleSize) <<", ns speeds: " + << broadcastAccFunction(pleaseGetNsSpeedsSize)<<", failed ns: " + << broadcastAccFunction(pleaseGetFailedServersSize)<<", ednsmap: " + <(pleaseGetEDNSStatusesSize)<(pleaseGetConcurrentQueries)<<" queries running, "<(pleaseGetPacketCacheSize); + uint64_t pcHits = broadcastAccFunction(pleaseGetPacketCacheHits); + g_log< parseACL(const std::string& aclFile, const std::string& aclSetting) +{ + auto result = std::make_shared(); + + if(!::arg()[aclFile].empty()) { + string line; + ifstream ifs(::arg()[aclFile].c_str()); + if(!ifs) { + throw runtime_error("Could not open '"+::arg()[aclFile]+"': "+stringerror()); + } + + string::size_type pos; + while(getline(ifs,line)) { + pos=line.find('#'); + if(pos!=string::npos) + line.resize(pos); + boost::trim(line); + if(line.empty()) + continue; + + result->addMask(line); + } + g_log<size()<<" "< ips; + stringtok(ips, ::arg()[aclSetting], ", "); + + g_log<::const_iterator i = ips.begin(); i!= ips.end(); ++i) { + result->addMask(*i); + if(i!=ips.begin()) + g_log< ng) +{ + t_allowFrom = ng; + return nullptr; +} + +static void* pleaseSupplantAllowNotifyFrom(std::shared_ptr ng) +{ + t_allowNotifyFrom = ng; + return nullptr; +} + +void* pleaseSupplantAllowNotifyFor(std::shared_ptr ns) +{ + t_allowNotifyFor = ns; + return nullptr; +} + +void parseACLs() +{ + static bool l_initialized; + + if(l_initialized) { // only reload configuration file on second call + string configname=::arg()["config-dir"]+"/recursor.conf"; + if(::arg()["config-name"]!="") { + configname=::arg()["config-dir"]+"/recursor-"+::arg()["config-name"]+".conf"; + } + cleanSlashes(configname); + + if(!::arg().preParseFile(configname.c_str(), "allow-from-file")) + throw runtime_error("Unable to re-parse configuration file '"+configname+"'"); + ::arg().preParseFile(configname.c_str(), "allow-from", LOCAL_NETS); + + if(!::arg().preParseFile(configname.c_str(), "allow-notify-from-file")) + throw runtime_error("Unable to re-parse configuration file '"+configname+"'"); + ::arg().preParseFile(configname.c_str(), "allow-notify-from"); + + ::arg().preParseFile(configname.c_str(), "include-dir"); + ::arg().preParse(g_argc, g_argv, "include-dir"); + + // then process includes + std::vector extraConfigs; + ::arg().gatherIncludes(extraConfigs); + + for(const std::string& fn : extraConfigs) { + if(!::arg().preParseFile(fn.c_str(), "allow-from-file", ::arg()["allow-from-file"])) + throw runtime_error("Unable to re-parse configuration file include '"+fn+"'"); + if(!::arg().preParseFile(fn.c_str(), "allow-from", ::arg()["allow-from"])) + throw runtime_error("Unable to re-parse configuration file include '"+fn+"'"); + + if(!::arg().preParseFile(fn.c_str(), "allow-notify-from-file", ::arg()["allow-notify-from-file"])) + throw runtime_error("Unable to re-parse configuration file include '"+fn+"'"); + if(!::arg().preParseFile(fn.c_str(), "allow-notify-from", ::arg()["allow-notify-from"])) + throw runtime_error("Unable to re-parse configuration file include '"+fn+"'"); + } + + ::arg().preParse(g_argc, g_argv, "allow-from-file"); + ::arg().preParse(g_argc, g_argv, "allow-from"); + + ::arg().preParse(g_argc, g_argv, "allow-notify-from-file"); + ::arg().preParse(g_argc, g_argv, "allow-notify-from"); + } + + auto allowFrom = parseACL("allow-from-file", "allow-from"); + + if(allowFrom->size() == 0) { + if(::arg()["local-address"]!="127.0.0.1" && ::arg().asNum("local-port")==53) + g_log<func = func; + tmsg->wantAnswer = true; + if(write(threadInfo.pipes.writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { + delete tmsg; + + unixDie("write to thread pipe returned wrong size or error"); + } + + string* resp = nullptr; + if(read(threadInfo.pipes.readFromThread, &resp, sizeof(resp)) != sizeof(resp)) + unixDie("read from thread pipe returned wrong size or error"); + + if(resp) { + delete resp; + resp = nullptr; + } + } +} + +template void *voider(const boost::function& func) +{ + return func(); +} + +static vector& operator+=(vector&a, const vector& b) +{ + a.insert(a.end(), b.begin(), b.end()); + return a; +} + +static vector >& operator+=(vector >&a, const vector >& b) +{ + a.insert(a.end(), b.begin(), b.end()); + return a; +} + +/* + This function should only be called by the handler to gather metrics, wipe the cache, + reload the Lua script (not the Lua config) or change the current trace regex, + and by the SNMP thread to gather metrics. */ +template T broadcastAccFunction(const boost::function& func) +{ + if (!isHandlerThread()) { + g_log<func = [func]{ return voider(func); }; + tmsg->wantAnswer = true; + + if(write(tps.writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { + delete tmsg; + unixDie("write to thread pipe returned wrong size or error"); + } + + T* resp = nullptr; + if(read(tps.readFromThread, &resp, sizeof(resp)) != sizeof(resp)) + unixDie("read from thread pipe returned wrong size or error"); + + if(resp) { + ret += *resp; + delete resp; + resp = nullptr; + } + } + return ret; +} + +template string broadcastAccFunction(const boost::function& fun); // explicit instantiation +template RecursorControlChannel::Answer broadcastAccFunction(const boost::function& fun); // explicit instantiation +template uint64_t broadcastAccFunction(const boost::function& fun); // explicit instantiation +template vector broadcastAccFunction(const boost::function *()>& fun); // explicit instantiation +template vector > broadcastAccFunction(const boost::function > *()>& fun); // explicit instantiation +template ThreadTimes broadcastAccFunction(const boost::function& fun); + +static int serviceMain(int argc, char*argv[]) +{ + int ret = EXIT_SUCCESS; + + g_slogStructured = ::arg().mustDo("structured-logging"); + + g_log.setName(s_programname); + g_log.disableSyslog(::arg().mustDo("disable-syslog")); + g_log.setTimestamps(::arg().mustDo("log-timestamp")); + + if(!::arg()["logging-facility"].empty()) { + int val=logFacilityToLOG(::arg().asNum("logging-facility") ); + if(val >= 0) + g_log.setFacility(val); + else + g_log< ips; + stringtok(ips, ::arg()["dont-query"], ", "); + ips.push_back("0.0.0.0"); + ips.push_back("::"); + + g_log<::const_iterator i = ips.begin(); i!= ips.end(); ++i) { + SyncRes::addDontQuery(*i); + if(i!=ips.begin()) + g_log< SyncRes::s_packetcachettl) ? SyncRes::s_packetcachettl : packetCacheServFailTTL; + SyncRes::s_serverdownmaxfails=::arg().asNum("server-down-max-fails"); + SyncRes::s_serverdownthrottletime=::arg().asNum("server-down-throttle-time"); + SyncRes::s_nonresolvingnsmaxfails=::arg().asNum("non-resolving-ns-max-fails"); + SyncRes::s_nonresolvingnsthrottletime=::arg().asNum("non-resolving-ns-throttle-time"); + SyncRes::s_serverID=::arg()["server-id"]; + SyncRes::s_maxqperq=::arg().asNum("max-qperq"); + SyncRes::s_maxnsaddressqperq=::arg().asNum("max-ns-address-qperq"); + SyncRes::s_maxtotusec=1000*::arg().asNum("max-total-msec"); + SyncRes::s_maxdepth=::arg().asNum("max-recursion-depth"); + SyncRes::s_rootNXTrust = ::arg().mustDo( "root-nx-trust"); + SyncRes::s_refresh_ttlperc = ::arg().asNum("refresh-on-ttl-perc"); + RecursorPacketCache::s_refresh_ttlperc = SyncRes::s_refresh_ttlperc; + SyncRes::s_tcp_fast_open = ::arg().asNum("tcp-fast-open"); + SyncRes::s_tcp_fast_open_connect = ::arg().mustDo("tcp-fast-open-connect"); + + SyncRes::s_dot_to_port_853 = ::arg().mustDo("dot-to-port-853"); + SyncRes::s_event_trace_enabled = ::arg().asNum("event-trace-enabled"); + + if (SyncRes::s_tcp_fast_open_connect) { + checkFastOpenSysctl(true); + checkTFOconnect(); + } + + if(SyncRes::s_serverID.empty()) { + SyncRes::s_serverID = myHostname; + } + + SyncRes::s_ecsipv4limit = ::arg().asNum("ecs-ipv4-bits"); + SyncRes::s_ecsipv6limit = ::arg().asNum("ecs-ipv6-bits"); + SyncRes::clearECSStats(); + SyncRes::s_ecsipv4cachelimit = ::arg().asNum("ecs-ipv4-cache-bits"); + SyncRes::s_ecsipv6cachelimit = ::arg().asNum("ecs-ipv6-cache-bits"); + SyncRes::s_ecsipv4nevercache = ::arg().mustDo("ecs-ipv4-never-cache"); + SyncRes::s_ecsipv6nevercache = ::arg().mustDo("ecs-ipv6-never-cache"); + SyncRes::s_ecscachelimitttl = ::arg().asNum("ecs-cache-limit-ttl"); + + SyncRes::s_qnameminimization = ::arg().mustDo("qname-minimization"); + + if (SyncRes::s_qnameminimization) { + // With an empty cache, a rev ipv6 query with dnssec enabled takes + // almost 100 queries. Default maxqperq is 60. + SyncRes::s_maxqperq = std::max(SyncRes::s_maxqperq, static_cast(100)); + } + + SyncRes::s_hardenNXD = SyncRes::HardenNXD::DNSSEC; + string value = ::arg()["nothing-below-nxdomain"]; + if (value == "yes") { + SyncRes::s_hardenNXD = SyncRes::HardenNXD::Yes; + } else if (value == "no") { + SyncRes::s_hardenNXD = SyncRes::HardenNXD::No; + } else if (value != "dnssec") { + g_log << Logger::Error << "Unknown nothing-below-nxdomain mode: " << value << endl; + exit(1); + } + + if (!::arg().isEmpty("ecs-scope-zero-address")) { + ComboAddress scopeZero(::arg()["ecs-scope-zero-address"]); + SyncRes::setECSScopeZeroAddress(Netmask(scopeZero, scopeZero.isIPv4() ? 32 : 128)); + } + else { + Netmask nm; + bool done = false; + + auto addr = pdns::getNonAnyQueryLocalAddress(AF_INET); + if (addr.sin4.sin_family != 0) { + nm = Netmask(addr, 32); + done = true; + } + if (!done) { + addr = pdns::getNonAnyQueryLocalAddress(AF_INET6); + if (addr.sin4.sin_family != 0) { + nm = Netmask(addr, 128); + done = true; + } + } + if (!done) { + nm = Netmask(ComboAddress("127.0.0.1"), 32); + } + SyncRes::setECSScopeZeroAddress(nm); + } + + SyncRes::parseEDNSSubnetAllowlist(::arg()["edns-subnet-whitelist"]); + SyncRes::parseEDNSSubnetAllowlist(::arg()["edns-subnet-allow-list"]); + SyncRes::parseEDNSSubnetAddFor(::arg()["ecs-add-for"]); + g_useIncomingECS = ::arg().mustDo("use-incoming-edns-subnet"); + + g_XPFAcl.toMasks(::arg()["xpf-allow-from"]); + g_xpfRRCode = ::arg().asNum("xpf-rr-code"); + + g_proxyProtocolACL.toMasks(::arg()["proxy-protocol-from"]); + g_proxyProtocolMaximumSize = ::arg().asNum("proxy-protocol-maximum-size"); + + if (!::arg()["dns64-prefix"].empty()) { + try { + auto dns64Prefix = Netmask(::arg()["dns64-prefix"]); + if (dns64Prefix.getBits() != 96) { + g_log << Logger::Error << "Invalid prefix for 'dns64-prefix', the current implementation only supports /96 prefixes: " << ::arg()["dns64-prefix"] << endl; + exit(1); + } + g_dns64Prefix = dns64Prefix.getNetwork(); + g_dns64PrefixReverse = reverseNameFromIP(*g_dns64Prefix); + /* /96 is 24 nibbles + 2 for "ip6.arpa." */ + while (g_dns64PrefixReverse.countLabels() > 26) { + g_dns64PrefixReverse.chopOff(); + } + } + catch (const NetmaskException& ne) { + g_log << Logger::Error << "Invalid prefix '" << ::arg()["dns64-prefix"] << "' for 'dns64-prefix': " << ne.reason << endl; + exit(1); + } + } + + g_networkTimeoutMsec = ::arg().asNum("network-timeout"); + + std::tie(g_initialDomainMap, g_initialAllowNotifyFor) = parseZoneConfiguration(); + + g_latencyStatSize=::arg().asNum("latency-statistic-size"); + + g_logCommonErrors=::arg().mustDo("log-common-errors"); + g_logRPZChanges = ::arg().mustDo("log-rpz-changes"); + + g_anyToTcp = ::arg().mustDo("any-to-tcp"); + g_udpTruncationThreshold = ::arg().asNum("udp-truncation-threshold"); + + g_lowercaseOutgoing = ::arg().mustDo("lowercase-outgoing"); + + g_paddingFrom.toMasks(::arg()["edns-padding-from"]); + if (::arg()["edns-padding-mode"] == "always") { + g_paddingMode = PaddingMode::Always; + } + else if (::arg()["edns-padding-mode"] == "padded-queries-only") { + g_paddingMode = PaddingMode::PaddedQueries; + } + else { + g_log << Logger::Error << "Unknown edns-padding-mode: " << ::arg()["edns-padding-mode"] << endl; + exit(1); + } + g_paddingTag = ::arg().asNum("edns-padding-tag"); + + g_numDistributorThreads = ::arg().asNum("distributor-threads"); + g_numWorkerThreads = ::arg().asNum("threads"); + if (g_numWorkerThreads < 1) { + g_log< USHRT_MAX || maxInFlight >= g_maxMThreads) { + g_log<(millis) % 1000) * 1000 }; + TCPOutConnectionManager::s_maxIdlePerAuth = ::arg().asNum("tcp-out-max-idle-per-auth"); + TCPOutConnectionManager::s_maxQueries = ::arg().asNum("tcp-out-max-queries"); + TCPOutConnectionManager::s_maxIdlePerThread = ::arg().asNum("tcp-out-max-idle-per-thread"); + + g_gettagNeedsEDNSOptions = ::arg().mustDo("gettag-needs-edns-options"); + + g_statisticsInterval = ::arg().asNum("statistics-interval"); + + s_addExtendedResolutionDNSErrors = ::arg().mustDo("extended-resolution-errors"); + + if (::arg().asNum("aggressive-nsec-cache-size") > 0) { + if (g_dnssecmode == DNSSECMode::ValidateAll || g_dnssecmode == DNSSECMode::ValidateForLog || g_dnssecmode == DNSSECMode::Process) { + g_aggressiveNSECCache = make_unique(::arg().asNum("aggressive-nsec-cache-size")); + } + else { + g_log< parts; + stringtok(parts, ::arg()["dont-throttle-names"], " ,"); + for (const auto &p : parts) { + dontThrottleNames.add(DNSName(p)); + } + g_dontThrottleNames.setState(std::move(dontThrottleNames)); + + parts.clear(); + NetmaskGroup dontThrottleNetmasks; + stringtok(parts, ::arg()["dont-throttle-netmasks"], " ,"); + for (const auto &p : parts) { + dontThrottleNetmasks.addMask(Netmask(p)); + } + g_dontThrottleNetmasks.setState(std::move(dontThrottleNetmasks)); + } + + { + SuffixMatchNode xdnssecNames; + vector parts; + stringtok(parts, ::arg()["x-dnssec-names"], " ,"); + for (const auto &p : parts) { + xdnssecNames.add(DNSName(p)); + } + g_xdnssec.setState(std::move(xdnssecNames)); + } + + { + SuffixMatchNode dotauthNames; + vector parts; + stringtok(parts, ::arg()["dot-to-auth-names"], " ,"); +#ifndef HAVE_DNS_OVER_TLS + if (parts.size()) { + g_log << Logger::Error << "dot-to-auth-names setting contains names, but Recursor was built without DNS over TLS support. Setting will be ignored."< tcpSockets; + /* we don't have reuseport so we can only open one socket per + listening addr:port and everyone will listen on it */ + makeUDPServerSockets(g_deferredAdds); + makeTCPServerSockets(g_deferredAdds, tcpSockets); + + /* every listener (so distributor if g_weDistributeQueries, workers otherwise) + needs to listen to the shared sockets */ + if (g_weDistributeQueries) { + /* first thread is the handler, then distributors */ + for (unsigned int threadId = 1; threadId <= g_numDistributorThreads; threadId++) { + s_threadInfos.at(threadId).tcpSockets = tcpSockets; + } + } + else { + /* first thread is the handler, there is no distributor here and workers are accepting queries */ + for (unsigned int threadId = 1; threadId <= g_numWorkerThreads; threadId++) { + s_threadInfos.at(threadId).tcpSockets = tcpSockets; + } + } + } + +#ifdef NOD_ENABLED + // Setup newly observed domain globals + setupNODGlobal(); +#endif /* NOD_ENABLED */ + + int forks; + for(forks = 0; forks < ::arg().asNum("processes") - 1; ++forks) { + if(!fork()) // we are child + break; + } + + if(::arg().mustDo("daemon")) { + g_log< 1 ? forks : -1); + + Utility::dropUserPrivs(newuid); + try { + /* we might still have capabilities remaining, for example if we have been started as root + without --setuid (please don't do that) or as an unprivileged user with ambient capabilities + like CAP_NET_BIND_SERVICE. + */ + dropCapabilities(); + } + catch(const std::exception& e) { + g_log<("recursor", setting); + g_snmpAgent->run(); + } + + int port = ::arg().asNum("udp-source-port-min"); + if(port < 1024 || port > 65535){ + g_log< 65535 || port < s_minUdpSourcePort){ + g_log< parts {}; + stringtok(parts, ::arg()["udp-source-port-avoid"], ", "); + for (const auto &part : parts) + { + port = std::stoi(part); + if(port < 1024 || port > 65535){ + g_log<func(); + } + catch(std::exception& e) { + if(g_logCommonErrors) + g_log<wantAnswer) { + const auto& threadInfo = s_threadInfos.at(t_id); + if(write(threadInfo.pipes.writeFromThread, &resp, sizeof(resp)) != sizeof(resp)) { + delete tmsg; + unixDie("write to thread pipe returned wrong size or error"); + } + } + + delete tmsg; +} + +static void handleRCC(int fd, FDMultiplexer::funcparam_t& var) +{ + try { + FDWrapper clientfd = accept(fd, nullptr, nullptr); + if (clientfd == -1) { + throw PDNSException("accept failed"); + } + string msg = s_rcc.recv(clientfd).d_str; + g_log << Logger::Info << "Received rec_control command '" << msg << "' via controlsocket" << endl; + + RecursorControlParser rcp; + RecursorControlParser::func_t* command; + auto answer = rcp.getAnswer(clientfd, msg, &command); + + s_rcc.send(clientfd, answer); + command(); + } + catch(const std::exception& e) { + g_log<trustAnchorFileInfo.fname.empty() && luaconfsLocal->trustAnchorFileInfo.interval != 0) { + // Loading the Lua config file already "refreshed" the TAs + last_trustAnchorUpdate = g_now.tv_sec + luaconfsLocal->trustAnchorFileInfo.interval * 3600; + } + + try { + if(s_running) { + return; + } + s_running=true; + + runTaskOnce(g_logCommonErrors); + + struct timeval now, past; + Utility::gettimeofday(&now, nullptr); + past = now; + past.tv_sec -= 5; + if (last_prune < past) { + t_packetCache->doPruneTo(g_maxPacketCacheEntries / (g_numWorkerThreads + g_numDistributorThreads)); + + time_t limit; + if(!((cleanCounter++)%40)) { // this is a full scan! + limit=now.tv_sec-300; + SyncRes::pruneNSSpeeds(limit); + } + limit = now.tv_sec - SyncRes::s_serverdownthrottletime * 10; + SyncRes::pruneFailedServers(limit); + limit = now.tv_sec - 2*3600; + SyncRes::pruneEDNSStatuses(limit); + SyncRes::pruneThrottledServers(); + SyncRes::pruneNonResolving(now.tv_sec - SyncRes::s_nonresolvingnsthrottletime); + Utility::gettimeofday(&last_prune, nullptr); + t_tcp_manager.cleanup(now); + } + + if(isHandlerThread()) { + if (now.tv_sec - last_RC_prune > 5) { + g_recCache->doPrune(g_maxCacheEntries); + g_negCache->prune(g_maxCacheEntries / 10); + if (g_aggressiveNSECCache) { + g_aggressiveNSECCache->prune(now.tv_sec); + } + last_RC_prune = now.tv_sec; + } + // Divide by 12 to get the original 2 hour cycle if s_maxcachettl is default (1 day) + if (now.tv_sec - last_rootupdate > max(SyncRes::s_maxcachettl / 12, 10U)) { + int res = SyncRes::getRootNS(g_now, nullptr, 0); + if (!res) { + last_rootupdate=now.tv_sec; + try { + primeRootNSZones(g_dnssecmode != DNSSECMode::Off, 0); + } + catch (const std::exception& e) { + g_log<= 3600) { + try { + doSecPoll(&last_secpoll); + } + catch (const std::exception& e) + { + g_log<trustAnchorFileInfo.fname.empty() && luaconfsLocal->trustAnchorFileInfo.interval != 0 && + g_now.tv_sec - last_trustAnchorUpdate >= (luaconfsLocal->trustAnchorFileInfo.interval * 3600)) { + g_log< dsAnchors; + if (updateTrustAnchorsFromFile(luaconfsLocal->trustAnchorFileInfo.fname, dsAnchors)) { + g_luaconfs.modify([&dsAnchors](LuaConfigItems& lci) { + lci.dsAnchors = dsAnchors; + }); + } + last_trustAnchorUpdate = now.tv_sec; + } catch (const PDNSException &pe) { + g_log<(); + t_tcpClientCounts = std::make_unique(); + + if (threadInfo.isHandler) { + if (!primeHints()) { + threadInfo.exitCode = EXIT_FAILURE; + RecursorControlChannel::stop = 1; + g_log<(); + +#ifdef NOD_ENABLED + if (threadInfo.isWorker) + setupNODThread(); +#endif /* NOD_ENABLED */ + + /* the listener threads handle TCP queries */ + if(threadInfo.isWorker || threadInfo.isListener) { + try { + if(!::arg()["lua-dns-script"].empty()) { + t_pdl = std::make_shared(); + t_pdl->loadFile(::arg()["lua-dns-script"]); + g_log<(); + if(g_weDistributeQueries) + t_remotes->set_capacity(::arg().asNum("stats-ringbuffer-entries") / g_numDistributorThreads); + else + t_remotes->set_capacity(ringsize); + t_servfailremotes = std::make_unique(); + t_servfailremotes->set_capacity(ringsize); + t_bogusremotes = std::make_unique(); + t_bogusremotes->set_capacity(ringsize); + t_largeanswerremotes = std::make_unique(); + t_largeanswerremotes->set_capacity(ringsize); + t_timeouts = std::make_unique(); + t_timeouts->set_capacity(ringsize); + + t_queryring = std::make_unique>>(); + t_queryring->set_capacity(ringsize); + t_servfailqueryring = std::make_unique>>(); + t_servfailqueryring->set_capacity(ringsize); + t_bogusqueryring = std::make_unique>>(); + t_bogusqueryring->set_capacity(ringsize); + } + MT = std::make_unique(::arg().asNum("stack-size")); + threadInfo.mt = MT.get(); + + /* start protobuf export threads if needed */ + auto luaconfsLocal = g_luaconfs.getLocal(); + checkProtobufExport(luaconfsLocal); + checkOutgoingProtobufExport(luaconfsLocal); +#ifdef HAVE_FSTRM + checkFrameStreamExport(luaconfsLocal); +#endif + + PacketID pident; + + t_fdm=getMultiplexer(); + + RecursorWebServer *rws = nullptr; + + t_fdm->addReadFD(threadInfo.pipes.readToThread, handlePipeRequest); + + if(threadInfo.isHandler) { + if(::arg().mustDo("webserver")) { + g_log<getName() << "' multiplexer"<addReadFD(threadInfo.pipes.readQueriesToThread, handlePipeRequest); + + if (threadInfo.isListener) { + if (g_reusePort) { + /* then every listener has its own FDs */ + for(const auto& deferred : threadInfo.deferredAdds) { + t_fdm->addReadFD(deferred.first, deferred.second); + } + } + else { + /* otherwise all listeners are listening on the same ones */ + for(const auto& deferred : g_deferredAdds) { + t_fdm->addReadFD(deferred.first, deferred.second); + } + } + } + } + + registerAllStats(); + + if(threadInfo.isHandler) { + t_fdm->addReadFD(s_rcc.d_fd, handleRCC); // control channel + } + + unsigned int maxTcpClients=::arg().asNum("max-tcp-clients"); + + bool listenOnTCP(true); + + time_t last_stat = 0; + time_t last_carbon=0, last_lua_maintenance=0; + time_t carbonInterval=::arg().asNum("carbon-interval"); + time_t luaMaintenanceInterval=::arg().asNum("lua-maintenance-interval"); + counter.store(0); // used to periodically execute certain tasks + + while (!RecursorControlChannel::stop) { + while(MT->schedule(&g_now)); // MTasker letting the mthreads do their thing + + // Use primes, it avoid not being scheduled in cases where the counter has a regular pattern. + // We want to call handler thread often, it gets scheduled about 2 times per second + if ((threadInfo.isHandler && counter % 11 == 0) || counter % 499 == 0) { + MT->makeThread(houseKeeping, 0); + } + + if(!(counter%55)) { + typedef vector > expired_t; + expired_t expired=t_fdm->getTimeouts(g_now); + + for(expired_t::iterator i=expired.begin() ; i != expired.end(); ++i) { + shared_ptr conn=boost::any_cast >(i->second); + if(g_logCommonErrors) + g_log<d_remote.toStringWithPort() <removeReadFD(i->first); + } + } + + counter++; + + if(threadInfo.isHandler) { + if(statsWanted || (g_statisticsInterval > 0 && (g_now.tv_sec - last_stat) >= g_statisticsInterval)) { + doStats(); + last_stat = g_now.tv_sec; + } + + Utility::gettimeofday(&g_now, nullptr); + + if((g_now.tv_sec - last_carbon) >= carbonInterval) { + MT->makeThread(doCarbonDump, 0); + last_carbon = g_now.tv_sec; + } + } + if (t_pdl != nullptr) { + // lua-dns-script directive is present, call the maintenance callback if needed + /* remember that the listener threads handle TCP queries */ + if (threadInfo.isWorker || threadInfo.isListener) { + // Only on threads processing queries + if(g_now.tv_sec - last_lua_maintenance >= luaMaintenanceInterval) { + t_pdl->maintenance(); + last_lua_maintenance = g_now.tv_sec; + } + } + } + + t_fdm->run(&g_now); + // 'run' updates g_now for us + + if(threadInfo.isListener) { + if(listenOnTCP) { + if(TCPConnection::getCurrentConnections() > maxTcpClients) { // shutdown, too many connections + for(const auto fd : threadInfo.tcpSockets) { + t_fdm->removeReadFD(fd); + } + listenOnTCP=false; + } + } + else { + if(TCPConnection::getCurrentConnections() <= maxTcpClients) { // reenable + for(const auto fd : threadInfo.tcpSockets) { + t_fdm->addReadFD(fd, handleNewTCPQuestion); + } + listenOnTCP=true; + } + } + } + } + delete rws; + delete t_fdm; + return 0; +} +catch(PDNSException &ae) { + g_log< disabled )")="64"; + ::arg().set("server-down-throttle-time","Number of seconds to throttle all queries to a server after being marked as down")="60"; + ::arg().set("dont-throttle-names", "Do not throttle nameservers with this name or suffix")=""; + ::arg().set("dont-throttle-netmasks", "Do not throttle nameservers with this IP netmask")=""; + ::arg().set("non-resolving-ns-max-fails", "Number of failed address resolves of a nameserver to start throttling it, 0 is disabled")="5"; + ::arg().set("non-resolving-ns-throttle-time", "Number of seconds to throttle a nameserver with a name failing to resolve")="60"; + + ::arg().set("hint-file", "If set, load root hints from this file")=""; + ::arg().set("max-cache-entries", "If set, maximum number of entries in the main cache")="1000000"; + ::arg().set("max-negative-ttl", "maximum number of seconds to keep a negative cached entry in memory")="3600"; + ::arg().set("max-cache-bogus-ttl", "maximum number of seconds to keep a Bogus (positive or negative) cached entry in memory")="3600"; + ::arg().set("max-cache-ttl", "maximum number of seconds to keep a cached entry in memory")="86400"; + ::arg().set("packetcache-ttl", "maximum number of seconds to keep a cached entry in packetcache")="3600"; + ::arg().set("max-packetcache-entries", "maximum number of entries to keep in the packetcache")="500000"; + ::arg().set("packetcache-servfail-ttl", "maximum number of seconds to keep a cached servfail entry in packetcache")="60"; + ::arg().set("server-id", "Returned when queried for 'id.server' TXT or NSID, defaults to hostname, set custom or 'disabled'")=""; + ::arg().set("stats-ringbuffer-entries", "maximum number of packets to store statistics for")="10000"; + ::arg().set("version-string", "string reported on version.pdns or version.bind")=fullVersionString(); + ::arg().set("allow-from", "If set, only allow these comma separated netmasks to recurse")=LOCAL_NETS; + ::arg().set("allow-from-file", "If set, load allowed netmasks from this file")=""; + ::arg().set("allow-notify-for", "If set, NOTIFY requests for these zones will be allowed")=""; + ::arg().set("allow-notify-for-file", "If set, load NOTIFY-allowed zones from this file")=""; + ::arg().set("allow-notify-from", "If set, NOTIFY requests from these comma separated netmasks will be allowed")=""; + ::arg().set("allow-notify-from-file", "If set, load NOTIFY-allowed netmasks from this file")=""; + ::arg().set("entropy-source", "If set, read entropy from this file")="/dev/urandom"; + ::arg().set("dont-query", "If set, do not query these netmasks for DNS data")=DONT_QUERY; + ::arg().set("max-tcp-per-client", "If set, maximum number of TCP sessions per client (IP address)")="0"; + ::arg().set("max-tcp-queries-per-connection", "If set, maximum number of TCP queries in a TCP connection")="0"; + ::arg().set("spoof-nearmiss-max", "If non-zero, assume spoofing after this many near misses")="1"; + ::arg().set("single-socket", "If set, only use a single socket for outgoing queries")="off"; + ::arg().set("auth-zones", "Zones for which we have authoritative data, comma separated domain=file pairs ")=""; + ::arg().set("lua-config-file", "More powerful configuration options")=""; + ::arg().setSwitch("allow-trust-anchor-query", "Allow queries for trustanchor.server CH TXT and negativetrustanchor.server CH TXT")="no"; + + ::arg().set("forward-zones", "Zones for which we forward queries, comma separated domain=ip pairs")=""; + ::arg().set("forward-zones-recurse", "Zones for which we forward queries with recursion bit, comma separated domain=ip pairs")=""; + ::arg().set("forward-zones-file", "File with (+)domain=ip pairs for forwarding")=""; + ::arg().set("export-etc-hosts", "If we should serve up contents from /etc/hosts")="off"; + ::arg().set("export-etc-hosts-search-suffix", "Also serve up the contents of /etc/hosts with this suffix")=""; + ::arg().set("etc-hosts-file", "Path to 'hosts' file")="/etc/hosts"; + ::arg().set("serve-rfc1918", "If we should be authoritative for RFC 1918 private IP space")="yes"; + ::arg().set("lua-dns-script", "Filename containing an optional 'lua' script that will be used to modify dns answers")=""; + ::arg().set("lua-maintenance-interval", "Number of seconds between calls to the lua user defined maintenance() function")="1"; + ::arg().set("latency-statistic-size","Number of latency values to calculate the qa-latency average")="10000"; + ::arg().setSwitch( "disable-packetcache", "Disable packetcache" )= "no"; + ::arg().set("ecs-ipv4-bits", "Number of bits of IPv4 address to pass for EDNS Client Subnet")="24"; + ::arg().set("ecs-ipv4-cache-bits", "Maximum number of bits of IPv4 mask to cache ECS response")="24"; + ::arg().set("ecs-ipv6-bits", "Number of bits of IPv6 address to pass for EDNS Client Subnet")="56"; + ::arg().set("ecs-ipv6-cache-bits", "Maximum number of bits of IPv6 mask to cache ECS response")="56"; + ::arg().setSwitch("ecs-ipv4-never-cache", "If we should never cache IPv4 ECS responses")="no"; + ::arg().setSwitch("ecs-ipv6-never-cache", "If we should never cache IPv6 ECS responses")="no"; + ::arg().set("ecs-minimum-ttl-override", "The minimum TTL for records in ECS-specific answers")="1"; + ::arg().set("ecs-cache-limit-ttl", "Minimum TTL to cache ECS response")="0"; + ::arg().set("edns-subnet-whitelist", "List of netmasks and domains that we should enable EDNS subnet for (deprecated)")=""; + ::arg().set("edns-subnet-allow-list", "List of netmasks and domains that we should enable EDNS subnet for")=""; + ::arg().set("ecs-add-for", "List of client netmasks for which EDNS Client Subnet will be added")="0.0.0.0/0, ::/0, " LOCAL_NETS_INVERSE; + ::arg().set("ecs-scope-zero-address", "Address to send to allow-listed authoritative servers for incoming queries with ECS prefix-length source of 0")=""; + ::arg().setSwitch( "use-incoming-edns-subnet", "Pass along received EDNS Client Subnet information")="no"; + ::arg().setSwitch( "pdns-distributes-queries", "If PowerDNS itself should distribute queries over threads")="yes"; + ::arg().setSwitch( "root-nx-trust", "If set, believe that an NXDOMAIN from the root means the TLD does not exist")="yes"; + ::arg().setSwitch( "any-to-tcp","Answer ANY queries with tc=1, shunting to TCP" )="no"; + ::arg().setSwitch( "lowercase-outgoing","Force outgoing questions to lowercase")="no"; + ::arg().setSwitch("gettag-needs-edns-options", "If EDNS Options should be extracted before calling the gettag() hook")="no"; + ::arg().set("udp-truncation-threshold", "Maximum UDP response size before we truncate")="1232"; + ::arg().set("edns-outgoing-bufsize", "Outgoing EDNS buffer size")="1232"; + ::arg().set("minimum-ttl-override", "The minimum TTL")="1"; + ::arg().set("max-qperq", "Maximum outgoing queries per query")="60"; + ::arg().set("max-ns-address-qperq", "Maximum outgoing NS address queries per query")="10"; + ::arg().set("max-total-msec", "Maximum total wall-clock time per query in milliseconds, 0 for unlimited")="7000"; + ::arg().set("max-recursion-depth", "Maximum number of internal recursion calls per query, 0 for unlimited")="40"; + ::arg().set("max-udp-queries-per-round", "Maximum number of UDP queries processed per recvmsg() round, before returning back to normal processing")="10000"; + ::arg().set("protobuf-use-kernel-timestamp", "Compute the latency of queries in protobuf messages by using the timestamp set by the kernel when the query was received (when available)")=""; + ::arg().set("distribution-pipe-buffer-size", "Size in bytes of the internal buffer of the pipe used by the distributor to pass incoming queries to a worker thread")="0"; + + ::arg().set("include-dir","Include *.conf files from this directory")=""; + ::arg().set("security-poll-suffix","Domain name from which to query security update notifications")="secpoll.powerdns.com."; + + ::arg().setSwitch("reuseport","Enable SO_REUSEPORT allowing multiple recursors processes to listen to 1 address")="no"; + + ::arg().setSwitch("snmp-agent", "If set, register as an SNMP agent")="no"; + ::arg().set("snmp-master-socket", "If set and snmp-agent is set, the socket to use to register to the SNMP daemon (deprecated)")=""; + ::arg().set("snmp-daemon-socket", "If set and snmp-agent is set, the socket to use to register to the SNMP daemon")=""; + + std::string defaultAPIDisabledStats = "cache-bytes, packetcache-bytes, special-memory-usage"; + for (size_t idx = 0; idx < 32; idx++) { + defaultAPIDisabledStats += ", ecs-v4-response-bits-" + std::to_string(idx + 1); + } + for (size_t idx = 0; idx < 128; idx++) { + defaultAPIDisabledStats += ", ecs-v6-response-bits-" + std::to_string(idx + 1); + } + std::string defaultDisabledStats = defaultAPIDisabledStats + ", cumul-clientanswers, cumul-authanswers, policy-hits"; + + ::arg().set("stats-api-blacklist", "List of statistics that are disabled when retrieving the complete list of statistics via the API (deprecated)")=defaultAPIDisabledStats; + ::arg().set("stats-carbon-blacklist", "List of statistics that are prevented from being exported via Carbon (deprecated)")=defaultDisabledStats; + ::arg().set("stats-rec-control-blacklist", "List of statistics that are prevented from being exported via rec_control get-all (deprecated)")=defaultDisabledStats; + ::arg().set("stats-snmp-blacklist", "List of statistics that are prevented from being exported via SNMP (deprecated)")=defaultDisabledStats; + + ::arg().set("stats-api-disabled-list", "List of statistics that are disabled when retrieving the complete list of statistics via the API")=defaultAPIDisabledStats; + ::arg().set("stats-carbon-disabled-list", "List of statistics that are prevented from being exported via Carbon")=defaultDisabledStats; + ::arg().set("stats-rec-control-disabled-list", "List of statistics that are prevented from being exported via rec_control get-all")=defaultDisabledStats; + ::arg().set("stats-snmp-disabled-list", "List of statistics that are prevented from being exported via SNMP")=defaultDisabledStats; + + ::arg().set("tcp-fast-open", "Enable TCP Fast Open support on the listening sockets, using the supplied numerical value as the queue size")="0"; + ::arg().set("tcp-fast-open-connect", "Enable TCP Fast Open support on outgoing sockets")="no"; + ::arg().set("nsec3-max-iterations", "Maximum number of iterations allowed for an NSEC3 record")="150"; + + ::arg().set("cpu-map", "Thread to CPU mapping, space separated thread-id=cpu1,cpu2..cpuN pairs")=""; + + ::arg().setSwitch("log-rpz-changes", "Log additions and removals to RPZ zones at Info level")="no"; + + ::arg().set("xpf-allow-from","XPF information is only processed from these subnets")=""; + ::arg().set("xpf-rr-code","XPF option code to use")="0"; + + ::arg().set("proxy-protocol-from", "A Proxy Protocol header is only allowed from these subnets")=""; + ::arg().set("proxy-protocol-maximum-size", "The maximum size of a proxy protocol payload, including the TLV values")="512"; + + ::arg().set("dns64-prefix", "DNS64 prefix")=""; + + ::arg().set("udp-source-port-min", "Minimum UDP port to bind on")="1024"; + ::arg().set("udp-source-port-max", "Maximum UDP port to bind on")="65535"; + ::arg().set("udp-source-port-avoid", "List of comma separated UDP port number to avoid")="11211"; + ::arg().set("rng", "Specify random number generator to use. Valid values are auto,sodium,openssl,getrandom,arc4random,urandom.")="auto"; + ::arg().set("public-suffix-list-file", "Path to the Public Suffix List file, if any")=""; + ::arg().set("distribution-load-factor", "The load factor used when PowerDNS is distributing queries to worker threads")="0.0"; + + ::arg().setSwitch("qname-minimization", "Use Query Name Minimization")="yes"; + ::arg().setSwitch("nothing-below-nxdomain", "When an NXDOMAIN exists in cache for a name with fewer labels than the qname, send NXDOMAIN without doing a lookup (see RFC 8020)")="dnssec"; + ::arg().set("max-generate-steps", "Maximum number of $GENERATE steps when loading a zone from a file")="0"; + ::arg().set("max-include-depth", "Maximum nested $INCLUDE depth when loading a zone from a file")="20"; + ::arg().set("record-cache-shards", "Number of shards in the record cache")="1024"; + ::arg().set("refresh-on-ttl-perc", "If a record is requested from the cache and only this % of original TTL remains, refetch") = "0"; + + ::arg().set("x-dnssec-names", "Collect DNSSEC statistics for names or suffixes in this list in separate x-dnssec counters")=""; + +#ifdef NOD_ENABLED + ::arg().set("new-domain-tracking", "Track newly observed domains (i.e. never seen before).")="no"; + ::arg().set("new-domain-log", "Log newly observed domains.")="yes"; + ::arg().set("new-domain-lookup", "Perform a DNS lookup newly observed domains as a subdomain of the configured domain")=""; + ::arg().set("new-domain-history-dir", "Persist new domain tracking data here to persist between restarts")=string(NODCACHEDIR)+"/nod"; + ::arg().set("new-domain-whitelist", "List of domains (and implicitly all subdomains) which will never be considered a new domain (deprecated)")=""; + ::arg().set("new-domain-ignore-list", "List of domains (and implicitly all subdomains) which will never be considered a new domain")=""; + ::arg().set("new-domain-db-size", "Size of the DB used to track new domains in terms of number of cells. Defaults to 67108864")="67108864"; + ::arg().set("new-domain-pb-tag", "If protobuf is configured, the tag to use for messages containing newly observed domains. Defaults to 'pdns-nod'")="pdns-nod"; + ::arg().set("unique-response-tracking", "Track unique responses (tuple of query name, type and RR).")="no"; + ::arg().set("unique-response-log", "Log unique responses")="yes"; + ::arg().set("unique-response-history-dir", "Persist unique response tracking data here to persist between restarts")=string(NODCACHEDIR)+"/udr"; + ::arg().set("unique-response-db-size", "Size of the DB used to track unique responses in terms of number of cells. Defaults to 67108864")="67108864"; + ::arg().set("unique-response-pb-tag", "If protobuf is configured, the tag to use for messages containing unique DNS responses. Defaults to 'pdns-udr'")="pdns-udr"; +#endif /* NOD_ENABLED */ + + ::arg().setSwitch("extended-resolution-errors", "If set, send an EDNS Extended Error extension on resolution failures, like DNSSEC validation errors")="no"; + + ::arg().set("aggressive-nsec-cache-size", "The number of records to cache in the aggressive cache. If set to a value greater than 0, and DNSSEC processing or validation is enabled, the recursor will cache NSEC and NSEC3 records to generate negative answers, as defined in rfc8198")="100000"; + + ::arg().set("edns-padding-from", "List of netmasks (proxy IP in case of XPF or proxy-protocol presence, client IP otherwise) for which EDNS padding will be enabled in responses, provided that 'edns-padding-mode' applies")=""; + ::arg().set("edns-padding-mode", "Whether to add EDNS padding to all responses ('always') or only to responses for queries containing the EDNS padding option ('padded-queries-only', the default). In both modes, padding will only be added to responses for queries coming from `edns-padding-from`_ sources")="padded-queries-only"; + ::arg().set("edns-padding-tag", "Packetcache tag associated to responses sent with EDNS padding, to prevent sending these to clients for which padding is not enabled.")="7830"; + + ::arg().setSwitch("dot-to-port-853", "Force DoT connection to target port 853 if DoT compiled in")="yes"; + ::arg().set("dot-to-auth-names", "Use DoT to authoritative servers with these names or suffixes")=""; + ::arg().set("event-trace-enabled", "If set, event traces are collected and send out via protobuf logging (1), logfile (2) or both(3)")="0"; + + ::arg().set("tcp-out-max-idle-ms", "Time TCP/DoT connections are left idle in milliseconds or 0 if no limit") = "10000"; + ::arg().set("tcp-out-max-idle-per-auth", "Maximum number of idle TCP/DoT connections to a specific IP per thread, 0 means do not keep idle connections open") = "10"; + ::arg().set("tcp-out-max-queries", "Maximum total number of queries per TCP/DoT connection, 0 means no limit") = "0"; + ::arg().set("tcp-out-max-idle-per-thread", "Maximum number of idle TCP/DoT connections per thread") = "100"; + ::arg().setSwitch("structured-logging", "Prefer structured logging") = "yes"; + + ::arg().setCmd("help","Provide a helpful message"); + ::arg().setCmd("version","Print version string"); + ::arg().setCmd("config","Output blank configuration"); + ::arg().setDefaults(); + g_log.toConsole(Logger::Info); + ::arg().laxParse(argc,argv); // do a lax parse + + if(::arg().mustDo("version")) { + showProductVersion(); + showBuildConfiguration(); + exit(0); + } + + string configname=::arg()["config-dir"]+"/recursor.conf"; + if(::arg()["config-name"]!="") { + configname=::arg()["config-dir"]+"/recursor-"+::arg()["config-name"]+".conf"; + s_programname+="-"+::arg()["config-name"]; + } + cleanSlashes(configname); + + if(!::arg().getCommands().empty()) { + cerr<<"Fatal: non-option"; + if (::arg().getCommands().size() > 1) { + cerr<<"s"; + } + cerr<<" ("; + bool first = true; + for (const auto& c : ::arg().getCommands()) { + if (!first) { + cerr<<", "; + } + first = false; + cerr<error("No such file", "Unable to parse configuration file", "config_file", Logging::Loggable(configname))); + } + + ::arg().parse(argc,argv); + + if( !::arg()["chroot"].empty() && !::arg()["api-config-dir"].empty() ) { + SLOG(g_log<info("Cannot use chroot and enable the API at the same time")); + exit(EXIT_FAILURE); + } + + if (::arg()["socket-dir"].empty()) { + if (::arg()["chroot"].empty()) + ::arg().set("socket-dir") = std::string(LOCALSTATEDIR) + "/pdns-recursor"; + else + ::arg().set("socket-dir") = "/"; + } + + if(::arg().asNum("threads")==1) { + if (::arg().mustDo("pdns-distributes-queries")) { + SLOG(g_log<v(1)->info("Only one thread, no need to distribute queries ourselves")); + ::arg().set("pdns-distributes-queries")="no"; + } + } + + if(::arg().mustDo("pdns-distributes-queries") && ::arg().asNum("distributor-threads") <= 0) { + SLOG(g_log<v(1)->info("Asked to run with pdns-distributes-queries set but no distributor threads, raising to 1")); + ::arg().set("distributor-threads")="1"; + } + + if (!::arg().mustDo("pdns-distributes-queries")) { + ::arg().set("distributor-threads")="0"; + } + + if(::arg().mustDo("help")) { + cout<<"syntax:"<(::arg().asNum("record-cache-shards")); + g_negCache = std::make_unique(::arg().asNum("record-cache-shards")); + + g_quiet=::arg().mustDo("quiet"); + Logger::Urgency logUrgency = (Logger::Urgency)::arg().asNum("loglevel"); + + if (logUrgency < Logger::Error) + logUrgency = Logger::Error; + if(!g_quiet && logUrgency < Logger::Info) { // Logger::Info=6, Logger::Debug=7 + logUrgency = Logger::Info; // if you do --quiet=no, you need Info to also see the query log + } + g_log.setLoglevel(logUrgency); + g_log.toConsole(logUrgency); + + ret = serviceMain(argc, argv); + } + catch(PDNSException &ae) { + g_log<(); + int err = t_pdl->loadFile(fname); + if (err != 0) { + string msg = std::to_string(t_id) + " Retaining current script, could not read '" + fname + "': " + stringerror(err); + g_log<::const_iterator begin, vector::const_iterator end) +{ + if(begin != end) + ::arg().set("lua-dns-script") = *begin; + + return broadcastAccFunction(doReloadLuaScript); +} + +static string* pleaseUseNewTraceRegex(const std::string& newRegex) +try +{ + if(newRegex.empty()) { + t_traceRegex.reset(); + return new string("unset\n"); + } + else { + t_traceRegex = std::make_shared(newRegex); + return new string("ok\n"); + } +} +catch(PDNSException& ae) +{ + return new string(ae.reason+"\n"); +} + +string doTraceRegex(vector::const_iterator begin, vector::const_iterator end) +{ + return broadcastAccFunction([=]{ return pleaseUseNewTraceRegex(begin!=end ? *begin : ""); }); +} + + +static uint64_t* pleaseWipePacketCache(const DNSName& canon, bool subtree, uint16_t qtype) +{ + return new uint64_t(t_packetCache->doWipePacketCache(canon, qtype, subtree)); +} + +struct WipeCacheResult wipeCaches(const DNSName& canon, bool subtree, uint16_t qtype) +{ + struct WipeCacheResult res; + + try { + res.record_count = g_recCache->doWipeCache(canon, subtree, qtype); + res.packet_count = broadcastAccFunction([=]{ return pleaseWipePacketCache(canon, subtree, qtype);}); + res.negative_record_count = g_negCache->wipe(canon, subtree); + if (g_aggressiveNSECCache) { + g_aggressiveNSECCache->removeZoneInfo(canon, subtree); + } + } + catch (const std::exception& e) { + g_log< +#endif //! used to send information to a newborn mthread @@ -123,11 +132,36 @@ struct DNSComboWriter { std::map d_meta; }; +extern thread_local FDMultiplexer* t_fdm; +extern uint16_t s_minUdpSourcePort; +extern uint16_t s_maxUdpSourcePort; + +// you can ask this class for a UDP socket to send a query from +// this socket is not yours, don't even think about deleting it +// but after you call 'returnSocket' on it, don't assume anything anymore +class UDPClientSocks +{ + unsigned int d_numsocks; +public: + UDPClientSocks() : d_numsocks(0) + { + } + + LWResult::Result getSocket(const ComboAddress& toaddr, int* fd); + + // return a socket to the pool, or simply erase it + void returnSocket(int fd); +private: + + // returns -1 for errors which might go away, throws for ones that won't + static int makeClientSocket(int family); +}; + +enum class PaddingMode { Always, PaddedQueries }; typedef MTasker, PacketBuffer, PacketIDCompare> MT_t; extern thread_local std::unique_ptr MT; // the big MTasker -extern thread_local FDMultiplexer* t_fdm; extern bool g_logCommonErrors; extern size_t g_proxyProtocolMaximumSize; extern std::atomic g_quiet; @@ -137,6 +171,7 @@ extern thread_local std::shared_ptr t_pdl; extern bool g_gettagNeedsEDNSOptions; extern NetmaskGroup g_paddingFrom; extern unsigned int g_paddingTag; +extern PaddingMode g_paddingMode; extern thread_local std::shared_ptr>> t_outgoingProtobufServers; extern unsigned int g_maxMThreads; extern bool g_reusePort; @@ -144,11 +179,65 @@ extern bool g_anyToTcp; extern size_t g_tcpMaxQueriesPerConn; extern unsigned int g_maxTCPPerClient; extern int g_tcpTimeout; +extern uint16_t g_udpTruncationThreshold; +extern double s_balancingFactor; +extern size_t s_maxUDPQueriesPerRound; +extern bool g_useKernelTimestamp; +extern thread_local std::shared_ptr t_allowFrom; +extern thread_local std::shared_ptr t_allowNotifyFrom; +extern thread_local std::shared_ptr t_allowNotifyFor; +extern thread_local std::unique_ptr t_udpclientsocks; +extern bool g_weDistributeQueries; // if true, 1 or more threads listen on the incoming query sockets and distribute them to workers +extern bool g_useIncomingECS; +extern boost::optional g_dns64Prefix; +extern DNSName g_dns64PrefixReverse; +extern uint64_t g_latencyStatSize; +extern bool s_addExtendedResolutionDNSErrors; +extern uint16_t g_xpfRRCode; +extern NetmaskGroup g_proxyProtocolACL; +extern std::atomic statsWanted; +extern unsigned int g_numDistributorThreads; +extern unsigned int g_numWorkerThreads; +extern uint32_t g_disthashseed; +extern int g_argc; +extern char** g_argv; +extern std::shared_ptr g_initialDomainMap; // new threads needs this to be setup +extern std::shared_ptr g_initialAllowFrom; // new thread needs to be setup with this +extern std::shared_ptr g_initialAllowNotifyFrom; // new threads need this to be setup +extern std::shared_ptr g_initialAllowNotifyFor; // new threads need this to be setup +extern thread_local std::shared_ptr t_traceRegex; + +#ifdef NOD_ENABLED +extern bool g_nodEnabled; +extern DNSName g_nodLookupDomain; +extern bool g_nodLog; +extern SuffixMatchNode g_nodDomainWL; +extern std::string g_nod_pbtag; +extern bool g_udrEnabled; +extern bool g_udrLog; +extern std::string g_udr_pbtag; +extern thread_local std::shared_ptr t_nodDBp; +extern thread_local std::shared_ptr t_udrDBp; +#endif + +#ifdef HAVE_FSTRM +extern thread_local std::shared_ptr>> t_frameStreamServers; +extern thread_local uint64_t t_frameStreamServersGeneration; +#endif /* HAVE_FSTRM */ + +#ifdef HAVE_BOOST_CONTAINER_FLAT_SET_HPP +extern boost::container::flat_set s_avoidUdpSourcePorts; +#else +extern std::set s_avoidUdpSourcePorts; +#endif + +/* without reuseport, all listeners share the same sockets */ +typedef vector > > deferredAdd_t; +extern deferredAdd_t g_deferredAdds; typedef map tcpClientCounts_t; extern thread_local std::unique_ptr t_tcpClientCounts; -typedef vector > > deferredAdd_t; inline MT_t* getMT() { @@ -191,8 +280,75 @@ static bool sendResponseOverTCP(const std::unique_ptr& dc, const return hadError; } +// for communicating with our threads +// effectively readonly after startup +struct RecThreadInfo +{ + struct ThreadPipeSet + { + int writeToThread{-1}; + int readToThread{-1}; + int writeFromThread{-1}; + int readFromThread{-1}; + int writeQueriesToThread{-1}; // this one is non-blocking + int readQueriesToThread{-1}; + }; + + /* FD corresponding to TCP sockets this thread is listening + on. + These FDs are also in deferredAdds when we have one + socket per listener, and in g_deferredAdds instead. */ + std::set tcpSockets; + /* FD corresponding to listening sockets if we have one socket per + listener (with reuseport), otherwise all listeners share the + same FD and g_deferredAdds is then used instead */ + deferredAdd_t deferredAdds; + struct ThreadPipeSet pipes; + std::thread thread; + MT_t* mt{nullptr}; + uint64_t numberOfDistributedQueries{0}; + int exitCode{0}; + /* handle the web server, carbon, statistics and the control channel */ + bool isHandler{false}; + /* accept incoming queries (and distributes them to the workers if pdns-distributes-queries is set) */ + bool isListener{false}; + /* process queries */ + bool isWorker{false}; +}; + +struct ThreadMSG +{ + pipefunc_t func; + bool wantAnswer; +}; + +/* first we have the handler thread, t_id == 0 (some other + helper threads like SNMP might have t_id == 0 as well) + then the distributor threads if any + and finally the workers */ +extern std::vector s_threadInfos; + +inline bool isDistributorThread() +{ + if (t_id == 0) { + return false; + } + + return g_weDistributeQueries && s_threadInfos.at(t_id).isListener; +} + +inline bool isHandlerThread() +{ + if (t_id == 0) { + return true; + } + + return s_threadInfos.at(t_id).isHandler; +} + PacketBuffer GenUDPQueryResponse(const ComboAddress& dest, const string& query); bool checkProtobufExport(LocalStateHolder& luaconfsLocal); +bool checkOutgoingProtobufExport(LocalStateHolder& luaconfsLocal); bool checkFrameStreamExport(LocalStateHolder& luaconfsLocal); void getQNameAndSubnet(const std::string& question, DNSName* dnsname, uint16_t* qtype, uint16_t* qclass, bool& foundECS, EDNSSubnetOpts* ednssubnet, EDNSOptionViewMap* options, @@ -220,3 +376,16 @@ void checkFastOpenSysctl(bool active); void checkTFOconnect(); void makeTCPServerSockets(deferredAdd_t& deferredAdds, std::set& tcpSockets); void handleNewTCPQuestion(int fd, FDMultiplexer::funcparam_t& ); + +void makeUDPServerSockets(deferredAdd_t& deferredAdds); +void* recursorThread(unsigned int n, const string& threadName); + +#define LOCAL_NETS "127.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 169.254.0.0/16, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fc00::/7, fe80::/10" +#define LOCAL_NETS_INVERSE "!127.0.0.0/8, !10.0.0.0/8, !100.64.0.0/10, !169.254.0.0/16, !192.168.0.0/16, !172.16.0.0/12, !::1/128, !fc00::/7, !fe80::/10" +// Bad Nets taken from both: +// http://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml +// and +// http://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml +// where such a network may not be considered a valid destination +#define BAD_NETS "0.0.0.0/8, 192.0.0.0/24, 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24, 240.0.0.0/4, ::/96, ::ffff:0:0/96, 100::/64, 2001:db8::/32" +#define DONT_QUERY LOCAL_NETS ", " BAD_NETS -- 2.47.2