2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "rec-main.hh"
26 #include "aggressive_nsec.hh"
27 #include "capabilities.hh"
28 #include "arguments.hh"
29 #include "dns_random.hh"
30 #include "rec_channel.hh"
31 #include "rec-tcpout.hh"
33 #include "query-local-address.hh"
34 #include "validate-recursor.hh"
35 #include "pubsuffix.hh"
36 #include "opensslsigners.hh"
37 #include "ws-recursor.hh"
38 #include "rec-taskqueue.hh"
39 #include "secpoll-recursor.hh"
41 #include "dnsseckeeper.hh"
42 #include "settings/cxxsettings.hh"
46 #endif /* NOD_ENABLED */
56 // All calls are coming form the same function, so no use for CODE_LINE, CODE_FUNC etc
57 #define SD_JOURNAL_SUPPRESS_LOCATION
58 #include <systemd/sd-daemon.h>
59 #include <systemd/sd-journal.h>
63 thread_local FrameStreamServersInfo t_frameStreamServersInfo
;
64 thread_local FrameStreamServersInfo t_nodFrameStreamServersInfo
;
65 #endif /* HAVE_FSTRM */
67 string g_programname
= "pdns_recursor";
69 RecursorControlChannel g_rcc
; // only active in the handler thread
70 bool g_regressionTestMode
;
75 DNSName g_nodLookupDomain
;
77 SuffixMatchNode g_nodDomainWL
;
78 std::string g_nod_pbtag
;
81 std::string g_udr_pbtag
;
82 thread_local
std::shared_ptr
<nod::NODDB
> t_nodDBp
;
83 thread_local
std::shared_ptr
<nod::UniqueResponseDB
> t_udrDBp
;
84 #endif /* NOD_ENABLED */
86 std::atomic
<bool> statsWanted
;
87 uint32_t g_disthashseed
;
88 bool g_useIncomingECS
;
89 NetmaskGroup g_proxyProtocolACL
;
90 boost::optional
<ComboAddress
> g_dns64Prefix
{boost::none
};
91 DNSName g_dns64PrefixReverse
;
92 std::shared_ptr
<SyncRes::domainmap_t
> g_initialDomainMap
; // new threads needs this to be setup
93 std::shared_ptr
<NetmaskGroup
> g_initialAllowFrom
; // new thread needs to be setup with this
94 std::shared_ptr
<NetmaskGroup
> g_initialAllowNotifyFrom
; // new threads need this to be setup
95 std::shared_ptr
<notifyset_t
> g_initialAllowNotifyFor
; // new threads need this to be setup
96 bool g_logRPZChanges
{false};
97 static time_t s_statisticsInterval
;
98 static std::atomic
<uint32_t> s_counter
;
101 static string s_structured_logger_backend
;
102 static Logger::Urgency s_logUrgency
;
104 std::shared_ptr
<Logr::Logger
> g_slogtcpin
;
105 std::shared_ptr
<Logr::Logger
> g_slogudpin
;
106 std::shared_ptr
<Logr::Logger
> g_slogudpout
;
108 /* without reuseport, all listeners share the same sockets */
109 static deferredAdd_t s_deferredUDPadds
;
110 static deferredAdd_t s_deferredTCPadds
;
112 /* first we have the handler thread, t_id == 0 (some other
113 helper threads like SNMP might have t_id == 0 as well)
114 then the distributor threads if any
115 and finally the workers */
116 std::vector
<RecThreadInfo
> RecThreadInfo::s_threadInfos
;
118 std::unique_ptr
<ProxyMapping
> g_proxyMapping
; // new threads needs this to be setup
119 thread_local
std::unique_ptr
<ProxyMapping
> t_proxyMapping
;
121 bool RecThreadInfo::s_weDistributeQueries
; // if true, 1 or more threads listen on the incoming query sockets and distribute them to workers
122 unsigned int RecThreadInfo::s_numDistributorThreads
;
123 unsigned int RecThreadInfo::s_numUDPWorkerThreads
;
124 unsigned int RecThreadInfo::s_numTCPWorkerThreads
;
125 thread_local
unsigned int RecThreadInfo::t_id
;
127 static std::map
<unsigned int, std::set
<int>> parseCPUMap(Logr::log_t log
)
129 std::map
<unsigned int, std::set
<int>> result
;
131 const std::string value
= ::arg()["cpu-map"];
133 if (!value
.empty() && !isSettingThreadCPUAffinitySupported()) {
134 SLOG(g_log
<< Logger::Warning
<< "CPU mapping requested but not supported, skipping" << endl
,
135 log
->info(Logr::Warning
, "CPU mapping requested but not supported, skipping"));
139 std::vector
<std::string
> parts
;
141 stringtok(parts
, value
, " \t");
143 for (const auto& part
: parts
) {
144 if (part
.find('=') == string::npos
) {
149 auto headers
= splitField(part
, '=');
150 boost::trim(headers
.first
);
151 boost::trim(headers
.second
);
153 auto threadId
= pdns::checked_stoi
<unsigned int>(headers
.first
);
154 std::vector
<std::string
> cpus
;
156 stringtok(cpus
, headers
.second
, ",");
158 for (const auto& cpu
: cpus
) {
159 int cpuId
= std::stoi(cpu
);
161 result
[threadId
].insert(cpuId
);
164 catch (const std::exception
& e
) {
165 SLOG(g_log
<< Logger::Error
<< "Error parsing cpu-map entry '" << part
<< "': " << e
.what() << endl
,
166 log
->error(Logr::Error
, e
.what(), "Error parsing cpu-map entry", "entry", Logging::Loggable(part
)));
173 static void setCPUMap(const std::map
<unsigned int, std::set
<int>>& cpusMap
, unsigned int n
, pthread_t tid
, Logr::log_t log
)
175 const auto& cpuMapping
= cpusMap
.find(n
);
176 if (cpuMapping
== cpusMap
.cend()) {
179 int ret
= mapThreadToCPUList(tid
, cpuMapping
->second
);
181 if (!g_slogStructured
) {
182 g_log
<< Logger::Info
<< "CPU affinity for thread " << n
<< " has been set to CPU map:";
183 for (const auto cpu
: cpuMapping
->second
) {
184 g_log
<< Logger::Info
<< " " << cpu
;
186 g_log
<< Logger::Info
<< endl
;
189 log
->info(Logr::Info
, "CPU affinity has been set", "thread", Logging::Loggable(n
), "cpumap", Logging::IterLoggable(cpuMapping
->second
.begin(), cpuMapping
->second
.end()));
193 if (!g_slogStructured
) {
194 g_log
<< Logger::Warning
<< "Error setting CPU affinity for thread " << n
<< " to CPU map:";
195 for (const auto cpu
: cpuMapping
->second
) {
196 g_log
<< Logger::Info
<< " " << cpu
;
198 g_log
<< Logger::Info
<< ' ' << stringerror(ret
) << endl
;
201 log
->error(Logr::Warning
, ret
, "Error setting CPU affinity", "thread", Logging::Loggable(n
), "cpumap", Logging::IterLoggable(cpuMapping
->second
.begin(), cpuMapping
->second
.end()));
206 static void recursorThread();
208 void RecThreadInfo::start(unsigned int tid
, const string
& tname
, const std::map
<unsigned int, std::set
<int>>& cpusMap
, Logr::log_t log
)
211 thread
= std::thread([tid
, tname
] {
213 const string threadPrefix
= "rec/";
214 setThreadName(threadPrefix
+ tname
);
217 setCPUMap(cpusMap
, tid
, thread
.native_handle(), log
);
220 int RecThreadInfo::runThreads(Logr::log_t log
)
222 int ret
= EXIT_SUCCESS
;
223 const auto cpusMap
= parseCPUMap(log
);
225 if (RecThreadInfo::numDistributors() + RecThreadInfo::numUDPWorkers() == 1) {
226 SLOG(g_log
<< Logger::Warning
<< "Operating with single UDP distributor/worker thread" << endl
,
227 log
->info(Logr::Notice
, "Operating with single UDP distributor/worker thread"));
229 /* This thread handles the web server, carbon, statistics and the control channel */
230 unsigned int currentThreadId
= 0;
231 auto& handlerInfo
= RecThreadInfo::info(currentThreadId
);
232 handlerInfo
.setHandler();
233 handlerInfo
.start(currentThreadId
, "web+stat", cpusMap
, log
);
235 // We skip the single UDP worker thread 1, it's handled after the loop and taskthreads
237 for (unsigned int thread
= 0; thread
< RecThreadInfo::numTCPWorkers(); thread
++, currentThreadId
++) {
238 auto& info
= RecThreadInfo::info(currentThreadId
);
239 info
.setTCPListener();
241 info
.start(currentThreadId
, "tcpworker", cpusMap
, log
);
244 for (unsigned int thread
= 0; thread
< RecThreadInfo::numTaskThreads(); thread
++, currentThreadId
++) {
245 auto& taskInfo
= RecThreadInfo::info(currentThreadId
);
246 taskInfo
.setTaskThread();
247 taskInfo
.start(currentThreadId
, "task", cpusMap
, log
);
251 auto& info
= RecThreadInfo::info(currentThreadId
);
254 RecThreadInfo::setThreadId(currentThreadId
);
257 for (unsigned int thread
= 0; thread
< RecThreadInfo::numRecursorThreads(); thread
++) {
261 auto& tInfo
= RecThreadInfo::info(thread
);
263 if (tInfo
.exitCode
!= 0) {
264 ret
= tInfo
.exitCode
;
269 // Setup RecThreadInfo objects
270 unsigned int currentThreadId
= 1;
271 if (RecThreadInfo::weDistributeQueries()) {
272 for (unsigned int thread
= 0; thread
< RecThreadInfo::numDistributors(); thread
++, currentThreadId
++) {
273 RecThreadInfo::info(currentThreadId
).setListener();
276 for (unsigned int thread
= 0; thread
< RecThreadInfo::numUDPWorkers(); thread
++, currentThreadId
++) {
277 auto& info
= RecThreadInfo::info(currentThreadId
);
278 info
.setListener(!RecThreadInfo::weDistributeQueries());
281 for (unsigned int thread
= 0; thread
< RecThreadInfo::numTCPWorkers(); thread
++, currentThreadId
++) {
282 auto& info
= RecThreadInfo::info(currentThreadId
);
283 info
.setTCPListener();
286 for (unsigned int thread
= 0; thread
< RecThreadInfo::numTaskThreads(); thread
++, currentThreadId
++) {
287 auto& info
= RecThreadInfo::info(currentThreadId
);
288 info
.setTaskThread();
291 // And now start the actual threads
293 if (RecThreadInfo::weDistributeQueries()) {
294 SLOG(g_log
<< Logger::Warning
<< "Launching " << RecThreadInfo::numDistributors() << " distributor threads" << endl
,
295 log
->info(Logr::Notice
, "Launching distributor threads", "count", Logging::Loggable(RecThreadInfo::numDistributors())));
296 for (unsigned int thread
= 0; thread
< RecThreadInfo::numDistributors(); thread
++, currentThreadId
++) {
297 auto& info
= RecThreadInfo::info(currentThreadId
);
298 info
.start(currentThreadId
, "distr", cpusMap
, log
);
301 SLOG(g_log
<< Logger::Warning
<< "Launching " << RecThreadInfo::numUDPWorkers() << " worker threads" << endl
,
302 log
->info(Logr::Notice
, "Launching worker threads", "count", Logging::Loggable(RecThreadInfo::numUDPWorkers())));
304 for (unsigned int thread
= 0; thread
< RecThreadInfo::numUDPWorkers(); thread
++, currentThreadId
++) {
305 auto& info
= RecThreadInfo::info(currentThreadId
);
306 info
.start(currentThreadId
, "worker", cpusMap
, log
);
309 SLOG(g_log
<< Logger::Warning
<< "Launching " << RecThreadInfo::numTCPWorkers() << " tcpworker threads" << endl
,
310 log
->info(Logr::Notice
, "Launching tcpworker threads", "count", Logging::Loggable(RecThreadInfo::numTCPWorkers())));
312 for (unsigned int thread
= 0; thread
< RecThreadInfo::numTCPWorkers(); thread
++, currentThreadId
++) {
313 auto& info
= RecThreadInfo::info(currentThreadId
);
314 info
.start(currentThreadId
, "tcpworker", cpusMap
, log
);
317 for (unsigned int thread
= 0; thread
< RecThreadInfo::numTaskThreads(); thread
++, currentThreadId
++) {
318 auto& info
= RecThreadInfo::info(currentThreadId
);
319 info
.start(currentThreadId
, "task", cpusMap
, log
);
322 /* This thread handles the web server, carbon, statistics and the control channel */
324 auto& info
= RecThreadInfo::info(currentThreadId
);
326 info
.start(currentThreadId
, "web+stat", cpusMap
, log
);
328 for (auto& tInfo
: RecThreadInfo::infos()) {
330 if (tInfo
.exitCode
!= 0) {
331 ret
= tInfo
.exitCode
;
338 void RecThreadInfo::makeThreadPipes(Logr::log_t log
)
340 auto pipeBufferSize
= ::arg().asNum("distribution-pipe-buffer-size");
341 if (pipeBufferSize
> 0) {
342 SLOG(g_log
<< Logger::Info
<< "Resizing the buffer of the distribution pipe to " << pipeBufferSize
<< endl
,
343 log
->info(Logr::Info
, "Resizing the buffer of the distribution pipe", "size", Logging::Loggable(pipeBufferSize
)));
346 /* thread 0 is the handler / SNMP, worker threads start at 1 */
347 for (unsigned int thread
= 0; thread
< numRecursorThreads(); ++thread
) {
348 auto& threadInfo
= info(thread
);
350 std::array
<int, 2> fileDesc
{};
351 if (pipe(fileDesc
.data()) < 0) {
352 unixDie("Creating pipe for inter-thread communications");
355 threadInfo
.pipes
.readToThread
= fileDesc
[0];
356 threadInfo
.pipes
.writeToThread
= fileDesc
[1];
358 // handler thread only gets first pipe, not the others
363 if (pipe(fileDesc
.data()) < 0) {
364 unixDie("Creating pipe for inter-thread communications");
367 threadInfo
.pipes
.readFromThread
= fileDesc
[0];
368 threadInfo
.pipes
.writeFromThread
= fileDesc
[1];
370 if (pipe(fileDesc
.data()) < 0) {
371 unixDie("Creating pipe for inter-thread communications");
374 threadInfo
.pipes
.readQueriesToThread
= fileDesc
[0];
375 threadInfo
.pipes
.writeQueriesToThread
= fileDesc
[1];
377 if (pipeBufferSize
> 0) {
378 if (!setPipeBufferSize(threadInfo
.pipes
.writeQueriesToThread
, pipeBufferSize
)) {
380 SLOG(g_log
<< Logger::Warning
<< "Error resizing the buffer of the distribution pipe for thread " << thread
<< " to " << pipeBufferSize
<< ": " << stringerror(err
) << endl
,
381 log
->error(Logr::Warning
, err
, "Error resizing the buffer of the distribution pipe for thread", "thread", Logging::Loggable(thread
), "size", Logging::Loggable(pipeBufferSize
)));
382 auto existingSize
= getPipeBufferSize(threadInfo
.pipes
.writeQueriesToThread
);
383 if (existingSize
> 0) {
384 SLOG(g_log
<< Logger::Warning
<< "The current size of the distribution pipe's buffer for thread " << thread
<< " is " << existingSize
<< endl
,
385 log
->info(Logr::Warning
, "The current size of the distribution pipe's buffer for thread", "thread", Logging::Loggable(thread
), "size", Logging::Loggable(existingSize
)));
390 if (!setNonBlocking(threadInfo
.pipes
.writeQueriesToThread
)) {
391 unixDie("Making pipe for inter-thread communications non-blocking");
398 static ArgvMap theArg
;
402 static FDMultiplexer
* getMultiplexer(Logr::log_t log
)
404 FDMultiplexer
* ret
= nullptr;
405 for (const auto& mplexer
: FDMultiplexer::getMultiplexerMap()) {
407 ret
= mplexer
.second(FDMultiplexer::s_maxevents
);
410 catch (FDMultiplexerException
& fe
) {
411 SLOG(g_log
<< Logger::Warning
<< "Non-fatal error initializing possible multiplexer (" << fe
.what() << "), falling back" << endl
,
412 log
->error(Logr::Warning
, fe
.what(), "Non-fatal error initializing possible multiplexer, falling back"));
415 SLOG(g_log
<< Logger::Warning
<< "Non-fatal error initializing possible multiplexer" << endl
,
416 log
->info(Logr::Warning
, "Non-fatal error initializing possible multiplexer"));
419 SLOG(g_log
<< Logger::Error
<< "No working multiplexer found!" << endl
,
420 log
->info(Logr::Error
, "No working multiplexer found!"));
424 static std::shared_ptr
<std::vector
<std::unique_ptr
<RemoteLogger
>>> startProtobufServers(const ProtobufExportConfig
& config
, Logr::log_t log
)
426 auto result
= std::make_shared
<std::vector
<std::unique_ptr
<RemoteLogger
>>>();
428 for (const auto& server
: config
.servers
) {
430 auto logger
= make_unique
<RemoteLogger
>(server
, config
.timeout
, 100 * config
.maxQueuedEntries
, config
.reconnectWaitTime
, config
.asyncConnect
);
431 logger
->setLogQueries(config
.logQueries
);
432 logger
->setLogResponses(config
.logResponses
);
433 result
->emplace_back(std::move(logger
));
435 catch (const std::exception
& e
) {
436 SLOG(g_log
<< Logger::Error
<< "Error while starting protobuf logger to '" << server
<< ": " << e
.what() << endl
,
437 log
->error(Logr::Error
, e
.what(), "Exception while starting protobuf logger", "exception", Logging::Loggable("std::exception"), "server", Logging::Loggable(server
)));
439 catch (const PDNSException
& e
) {
440 SLOG(g_log
<< Logger::Error
<< "Error while starting protobuf logger to '" << server
<< ": " << e
.reason
<< endl
,
441 log
->error(Logr::Error
, e
.reason
, "Exception while starting protobuf logger", "exception", Logging::Loggable("PDNSException"), "server", Logging::Loggable(server
)));
448 bool checkProtobufExport(LocalStateHolder
<LuaConfigItems
>& luaconfsLocal
)
450 if (!luaconfsLocal
->protobufExportConfig
.enabled
) {
451 if (t_protobufServers
.servers
) {
452 t_protobufServers
.servers
.reset();
453 t_protobufServers
.config
= luaconfsLocal
->protobufExportConfig
;
459 /* if the server was not running, or if it was running according to a
460 previous configuration */
461 if (t_protobufServers
.generation
< luaconfsLocal
->generation
&& t_protobufServers
.config
!= luaconfsLocal
->protobufExportConfig
) {
463 if (t_protobufServers
.servers
) {
464 t_protobufServers
.servers
.reset();
466 auto log
= g_slog
->withName("protobuf");
467 t_protobufServers
.servers
= startProtobufServers(luaconfsLocal
->protobufExportConfig
, log
);
468 t_protobufServers
.config
= luaconfsLocal
->protobufExportConfig
;
469 t_protobufServers
.generation
= luaconfsLocal
->generation
;
475 bool checkOutgoingProtobufExport(LocalStateHolder
<LuaConfigItems
>& luaconfsLocal
)
477 if (!luaconfsLocal
->outgoingProtobufExportConfig
.enabled
) {
478 if (t_outgoingProtobufServers
.servers
) {
479 t_outgoingProtobufServers
.servers
.reset();
480 t_outgoingProtobufServers
.config
= luaconfsLocal
->outgoingProtobufExportConfig
;
486 /* if the server was not running, or if it was running according to a
487 previous configuration */
488 if (t_outgoingProtobufServers
.generation
< luaconfsLocal
->generation
&& t_outgoingProtobufServers
.config
!= luaconfsLocal
->outgoingProtobufExportConfig
) {
490 if (t_outgoingProtobufServers
.servers
) {
491 t_outgoingProtobufServers
.servers
.reset();
493 auto log
= g_slog
->withName("protobuf");
494 t_outgoingProtobufServers
.servers
= startProtobufServers(luaconfsLocal
->outgoingProtobufExportConfig
, log
);
495 t_outgoingProtobufServers
.config
= luaconfsLocal
->outgoingProtobufExportConfig
;
496 t_outgoingProtobufServers
.generation
= luaconfsLocal
->generation
;
502 void protobufLogQuery(LocalStateHolder
<LuaConfigItems
>& luaconfsLocal
, const boost::uuids::uuid
& uniqueId
, const ComboAddress
& remote
, const ComboAddress
& local
, const ComboAddress
& mappedSource
, const Netmask
& ednssubnet
, bool tcp
, uint16_t queryID
, size_t len
, const DNSName
& qname
, uint16_t qtype
, uint16_t qclass
, const std::unordered_set
<std::string
>& policyTags
, const std::string
& requestorId
, const std::string
& deviceId
, const std::string
& deviceName
, const std::map
<std::string
, RecursorLua4::MetaValue
>& meta
)
504 auto log
= g_slog
->withName("pblq");
506 if (!t_protobufServers
.servers
) {
510 ComboAddress requestor
;
511 if (!luaconfsLocal
->protobufExportConfig
.logMappedFrom
) {
512 Netmask
requestorNM(remote
, remote
.sin4
.sin_family
== AF_INET
? luaconfsLocal
->protobufMaskV4
: luaconfsLocal
->protobufMaskV6
);
513 requestor
= requestorNM
.getMaskedNetwork();
514 requestor
.setPort(remote
.getPort());
517 Netmask
requestorNM(mappedSource
, mappedSource
.sin4
.sin_family
== AF_INET
? luaconfsLocal
->protobufMaskV4
: luaconfsLocal
->protobufMaskV6
);
518 requestor
= requestorNM
.getMaskedNetwork();
519 requestor
.setPort(mappedSource
.getPort());
522 pdns::ProtoZero::RecMessage msg
{128, std::string::size_type(policyTags
.empty() ? 0 : 64)}; // It's a guess
523 msg
.setType(pdns::ProtoZero::Message::MessageType::DNSQueryType
);
524 msg
.setRequest(uniqueId
, requestor
, local
, qname
, qtype
, qclass
, queryID
, tcp
? pdns::ProtoZero::Message::TransportProtocol::TCP
: pdns::ProtoZero::Message::TransportProtocol::UDP
, len
);
525 msg
.setServerIdentity(SyncRes::s_serverID
);
526 msg
.setEDNSSubnet(ednssubnet
, ednssubnet
.isIPv4() ? luaconfsLocal
->protobufMaskV4
: luaconfsLocal
->protobufMaskV6
);
527 msg
.setRequestorId(requestorId
);
528 msg
.setDeviceId(deviceId
);
529 msg
.setDeviceName(deviceName
);
531 if (!policyTags
.empty()) {
532 msg
.addPolicyTags(policyTags
);
534 for (const auto& mit
: meta
) {
535 msg
.setMeta(mit
.first
, mit
.second
.stringVal
, mit
.second
.intVal
);
538 std::string
strMsg(msg
.finishAndMoveBuf());
539 for (auto& server
: *t_protobufServers
.servers
) {
540 remoteLoggerQueueData(*server
, strMsg
);
544 void protobufLogResponse(pdns::ProtoZero::RecMessage
& message
)
546 if (!t_protobufServers
.servers
) {
550 std::string
msg(message
.finishAndMoveBuf());
551 for (auto& server
: *t_protobufServers
.servers
) {
552 remoteLoggerQueueData(*server
, msg
);
556 void protobufLogResponse(const struct dnsheader
* header
, LocalStateHolder
<LuaConfigItems
>& luaconfsLocal
,
557 const RecursorPacketCache::OptPBData
& pbData
, const struct timeval
& tval
,
558 bool tcp
, const ComboAddress
& source
, const ComboAddress
& destination
,
559 const ComboAddress
& mappedSource
,
560 const EDNSSubnetOpts
& ednssubnet
,
561 const boost::uuids::uuid
& uniqueId
, const string
& requestorId
, const string
& deviceId
,
562 const string
& deviceName
, const std::map
<std::string
, RecursorLua4::MetaValue
>& meta
,
563 const RecEventTrace
& eventTrace
,
564 const std::unordered_set
<std::string
>& policyTags
)
566 pdns::ProtoZero::RecMessage
pbMessage(pbData
? pbData
->d_message
: "", pbData
? pbData
->d_response
: "", 64, 10); // The extra bytes we are going to add
567 // Normally we take the immutable string from the cache and append a few values, but if it's not there (can this happen?)
568 // we start with an empty string and append the minimal
570 pbMessage
.setType(pdns::ProtoZero::Message::MessageType::DNSResponseType
);
571 pbMessage
.setServerIdentity(SyncRes::s_serverID
);
575 if (g_useKernelTimestamp
&& tval
.tv_sec
!= 0) {
576 pbMessage
.setQueryTime(tval
.tv_sec
, tval
.tv_usec
);
579 pbMessage
.setQueryTime(g_now
.tv_sec
, g_now
.tv_usec
);
583 if (!luaconfsLocal
->protobufExportConfig
.logMappedFrom
) {
584 pbMessage
.setSocketFamily(source
.sin4
.sin_family
);
585 Netmask
requestorNM(source
, source
.sin4
.sin_family
== AF_INET
? luaconfsLocal
->protobufMaskV4
: luaconfsLocal
->protobufMaskV6
);
586 const auto& requestor
= requestorNM
.getMaskedNetwork();
587 pbMessage
.setFrom(requestor
);
588 pbMessage
.setFromPort(source
.getPort());
591 pbMessage
.setSocketFamily(mappedSource
.sin4
.sin_family
);
592 Netmask
requestorNM(mappedSource
, mappedSource
.sin4
.sin_family
== AF_INET
? luaconfsLocal
->protobufMaskV4
: luaconfsLocal
->protobufMaskV6
);
593 const auto& requestor
= requestorNM
.getMaskedNetwork();
594 pbMessage
.setFrom(requestor
);
595 pbMessage
.setFromPort(mappedSource
.getPort());
597 pbMessage
.setMessageIdentity(uniqueId
);
598 pbMessage
.setTo(destination
);
599 pbMessage
.setSocketProtocol(tcp
? pdns::ProtoZero::Message::TransportProtocol::TCP
: pdns::ProtoZero::Message::TransportProtocol::UDP
);
600 pbMessage
.setId(header
->id
);
603 pbMessage
.setEDNSSubnet(ednssubnet
.source
, ednssubnet
.source
.isIPv4() ? luaconfsLocal
->protobufMaskV4
: luaconfsLocal
->protobufMaskV6
);
604 pbMessage
.setRequestorId(requestorId
);
605 pbMessage
.setDeviceId(deviceId
);
606 pbMessage
.setDeviceName(deviceName
);
607 pbMessage
.setToPort(destination
.getPort());
608 for (const auto& metaItem
: meta
) {
609 pbMessage
.setMeta(metaItem
.first
, metaItem
.second
.stringVal
, metaItem
.second
.intVal
);
613 pbMessage
.setNewlyObservedDomain(false);
616 if (eventTrace
.enabled() && (SyncRes::s_event_trace_enabled
& SyncRes::event_trace_to_pb
) != 0) {
617 pbMessage
.addEvents(eventTrace
);
619 pbMessage
.addPolicyTags(policyTags
);
621 protobufLogResponse(pbMessage
);
626 static std::shared_ptr
<std::vector
<std::unique_ptr
<FrameStreamLogger
>>> startFrameStreamServers(const FrameStreamExportConfig
& config
, Logr::log_t log
)
628 auto result
= std::make_shared
<std::vector
<std::unique_ptr
<FrameStreamLogger
>>>();
630 for (const auto& server
: config
.servers
) {
632 std::unordered_map
<string
, unsigned> options
;
633 options
["bufferHint"] = config
.bufferHint
;
634 options
["flushTimeout"] = config
.flushTimeout
;
635 options
["inputQueueSize"] = config
.inputQueueSize
;
636 options
["outputQueueSize"] = config
.outputQueueSize
;
637 options
["queueNotifyThreshold"] = config
.queueNotifyThreshold
;
638 options
["reopenInterval"] = config
.reopenInterval
;
639 unique_ptr
<FrameStreamLogger
> fsl
= nullptr;
641 ComboAddress
address(server
);
642 fsl
= make_unique
<FrameStreamLogger
>(address
.sin4
.sin_family
, address
.toStringWithPort(), true, options
);
644 catch (const PDNSException
& e
) {
645 fsl
= make_unique
<FrameStreamLogger
>(AF_UNIX
, server
, true, options
);
647 fsl
->setLogQueries(config
.logQueries
);
648 fsl
->setLogResponses(config
.logResponses
);
649 fsl
->setLogNODs(config
.logNODs
);
650 fsl
->setLogUDRs(config
.logUDRs
);
651 result
->emplace_back(std::move(fsl
));
653 catch (const std::exception
& e
) {
654 SLOG(g_log
<< Logger::Error
<< "Error while starting dnstap framestream logger to '" << server
<< ": " << e
.what() << endl
,
655 log
->error(Logr::Error
, e
.what(), "Exception while starting dnstap framestream logger", "exception", Logging::Loggable("std::exception"), "server", Logging::Loggable(server
)));
657 catch (const PDNSException
& e
) {
658 SLOG(g_log
<< Logger::Error
<< "Error while starting dnstap framestream logger to '" << server
<< ": " << e
.reason
<< endl
,
659 log
->error(Logr::Error
, e
.reason
, "Exception while starting dnstap framestream logger", "exception", Logging::Loggable("PDNSException"), "server", Logging::Loggable(server
)));
666 static void asyncFrameStreamLoggersCleanup(std::shared_ptr
<std::vector
<std::unique_ptr
<FrameStreamLogger
>>>&& servers
)
668 auto thread
= std::thread([&] {
674 bool checkFrameStreamExport(LocalStateHolder
<LuaConfigItems
>& luaconfsLocal
, const FrameStreamExportConfig
& config
, FrameStreamServersInfo
& serverInfos
)
676 if (!config
.enabled
) {
677 if (serverInfos
.servers
) {
678 // dt's take care of cleanup
679 asyncFrameStreamLoggersCleanup(std::move(serverInfos
.servers
));
680 serverInfos
.config
= config
;
686 /* if the server was not running, or if it was running according to a previous
689 if (serverInfos
.generation
< luaconfsLocal
->generation
&& serverInfos
.config
!= config
) {
690 if (serverInfos
.servers
) {
691 // dt's take care of cleanup
692 asyncFrameStreamLoggersCleanup(std::move(serverInfos
.servers
));
695 auto dnsTapLog
= g_slog
->withName("dnstap");
696 serverInfos
.servers
= startFrameStreamServers(config
, dnsTapLog
);
697 serverInfos
.config
= config
;
698 serverInfos
.generation
= luaconfsLocal
->generation
;
704 #endif /* HAVE_FSTRM */
706 static void makeControlChannelSocket(int processNum
= -1)
708 string sockname
= ::arg()["socket-dir"] + "/" + g_programname
;
709 if (processNum
>= 0) {
710 sockname
+= "." + std::to_string(processNum
);
712 sockname
+= ".controlsocket";
713 g_rcc
.listen(sockname
);
715 uid_t sockowner
= -1;
716 gid_t sockgroup
= -1;
718 if (!::arg().isEmpty("socket-group")) {
719 sockgroup
= ::arg().asGid("socket-group");
721 if (!::arg().isEmpty("socket-owner")) {
722 sockowner
= ::arg().asUid("socket-owner");
725 if (sockgroup
!= static_cast<gid_t
>(-1) || sockowner
!= static_cast<uid_t
>(-1)) {
726 if (chown(sockname
.c_str(), sockowner
, sockgroup
) < 0) {
727 unixDie("Failed to chown control socket");
731 // do mode change if socket-mode is given
732 if (!::arg().isEmpty("socket-mode")) {
733 mode_t sockmode
= ::arg().asMode("socket-mode");
734 if (chmod(sockname
.c_str(), sockmode
) < 0) {
735 unixDie("Failed to chmod control socket");
740 static void writePid(Logr::log_t log
)
742 if (!::arg().mustDo("write-pid")) {
745 ofstream
ostr(g_pidfname
.c_str(), std::ios_base::app
);
747 ostr
<< Utility::getpid() << endl
;
751 SLOG(g_log
<< Logger::Error
<< "Writing pid for " << Utility::getpid() << " to " << g_pidfname
<< " failed: " << stringerror(err
) << endl
,
752 log
->error(Logr::Error
, err
, "Writing pid failed", "pid", Logging::Loggable(Utility::getpid()), "file", Logging::Loggable(g_pidfname
)));
756 static void checkSocketDir(Logr::log_t log
)
758 string
dir(::arg()["socket-dir"]);
761 struct stat dirStat
= {};
762 if (stat(dir
.c_str(), &dirStat
) == -1) {
763 msg
= "it does not exist or cannot access";
765 else if (!S_ISDIR(dirStat
.st_mode
)) {
766 msg
= "it is not a directory";
768 else if (access(dir
.c_str(), R_OK
| W_OK
| X_OK
) != 0) {
769 msg
= "cannot read, write or search";
774 dir
= ::arg()["chroot"] + dir
;
775 SLOG(g_log
<< Logger::Error
<< "Problem with socket directory " << dir
<< ": " << msg
<< "; see https://docs.powerdns.com/recursor/upgrade.html#x-to-4-3-0" << endl
,
776 log
->error(Logr::Error
, msg
, "Problem with socket directory, see https://docs.powerdns.com/recursor/upgrade.html#x-to-4-3-0", "dir", Logging::Loggable(dir
)));
781 static void setupNODThread(Logr::log_t log
)
784 uint32_t num_cells
= ::arg().asNum("new-domain-db-size");
785 t_nodDBp
= std::make_shared
<nod::NODDB
>(num_cells
);
787 t_nodDBp
->setCacheDir(::arg()["new-domain-history-dir"]);
789 catch (const PDNSException
& e
) {
790 SLOG(g_log
<< Logger::Error
<< "new-domain-history-dir (" << ::arg()["new-domain-history-dir"] << ") is not readable or does not exist" << endl
,
791 log
->error(Logr::Error
, e
.reason
, "new-domain-history-dir is not readable or does not exists", "dir", Logging::Loggable(::arg()["new-domain-history-dir"])));
794 if (!t_nodDBp
->init()) {
795 SLOG(g_log
<< Logger::Error
<< "Could not initialize domain tracking" << endl
,
796 log
->info(Logr::Error
, "Could not initialize domain tracking"));
799 std::thread
thread(nod::NODDB::startHousekeepingThread
, t_nodDBp
, std::this_thread::get_id());
803 uint32_t num_cells
= ::arg().asNum("unique-response-db-size");
804 t_udrDBp
= std::make_shared
<nod::UniqueResponseDB
>(num_cells
);
806 t_udrDBp
->setCacheDir(::arg()["unique-response-history-dir"]);
808 catch (const PDNSException
& e
) {
809 SLOG(g_log
<< Logger::Error
<< "unique-response-history-dir (" << ::arg()["unique-response-history-dir"] << ") is not readable or does not exist" << endl
,
810 log
->info(Logr::Error
, "unique-response-history-dir is not readable or does not exist", "dir", Logging::Loggable(::arg()["unique-response-history-dir"])));
813 if (!t_udrDBp
->init()) {
814 SLOG(g_log
<< Logger::Error
<< "Could not initialize unique response tracking" << endl
,
815 log
->info(Logr::Error
, "Could not initialize unique response tracking"));
818 std::thread
thread(nod::UniqueResponseDB::startHousekeepingThread
, t_udrDBp
, std::this_thread::get_id());
823 static void parseNODIgnorelist(const std::string
& wlist
)
825 vector
<string
> parts
;
826 stringtok(parts
, wlist
, ",; ");
827 for (const auto& part
: parts
) {
828 g_nodDomainWL
.add(DNSName(part
));
832 static void setupNODGlobal()
834 // Setup NOD subsystem
835 g_nodEnabled
= ::arg().mustDo("new-domain-tracking");
836 g_nodLookupDomain
= DNSName(::arg()["new-domain-lookup"]);
837 g_nodLog
= ::arg().mustDo("new-domain-log");
838 parseNODIgnorelist(::arg()["new-domain-whitelist"]);
839 parseNODIgnorelist(::arg()["new-domain-ignore-list"]);
841 // Setup Unique DNS Response subsystem
842 g_udrEnabled
= ::arg().mustDo("unique-response-tracking");
843 g_udrLog
= ::arg().mustDo("unique-response-log");
844 g_nod_pbtag
= ::arg()["new-domain-pb-tag"];
845 g_udr_pbtag
= ::arg()["unique-response-pb-tag"];
847 #endif /* NOD_ENABLED */
849 static void daemonize(Logr::log_t log
)
851 if (auto pid
= fork(); pid
!= 0) {
854 SLOG(g_log
<< Logger::Critical
<< "Fork failed: " << stringerror(err
) << endl
,
855 log
->error(Logr::Critical
, err
, "Fork failed"));
856 exit(1); // NOLINT(concurrency-mt-unsafe)
858 exit(0); // NOLINT(concurrency-mt-unsafe)
863 int devNull
= open("/dev/null", O_RDWR
); /* open stdin */
866 SLOG(g_log
<< Logger::Critical
<< "Unable to open /dev/null: " << stringerror(err
) << endl
,
867 log
->error(Logr::Critical
, err
, "Unable to open /dev/null"));
870 dup2(devNull
, 0); /* stdin */
871 dup2(devNull
, 1); /* stderr */
872 dup2(devNull
, 2); /* stderr */
877 static void termIntHandler([[maybe_unused
]] int arg
)
882 static void usr1Handler([[maybe_unused
]] int arg
)
887 static void usr2Handler([[maybe_unused
]] int arg
)
890 SyncRes::setDefaultLogMode(g_quiet
? SyncRes::LogNone
: SyncRes::Log
);
891 ::arg().set("quiet") = g_quiet
? "yes" : "no";
894 static void checkLinuxIPv6Limits([[maybe_unused
]] Logr::log_t log
)
898 if (readFileIfThere("/proc/sys/net/ipv6/route/max_size", &line
)) {
899 int lim
= std::stoi(line
);
901 SLOG(g_log
<< Logger::Error
<< "If using IPv6, please raise sysctl net.ipv6.route.max_size, currently set to " << lim
<< " which is < 16384" << endl
,
902 log
->info(Logr::Error
, "If using IPv6, please raise sysctl net.ipv6.route.max_size to a size >= 16384", "current", Logging::Loggable(lim
)));
908 static void checkOrFixFDS(Logr::log_t log
)
910 unsigned int availFDs
= getFilenumLimit();
911 unsigned int wantFDs
= g_maxMThreads
* (RecThreadInfo::numUDPWorkers() + RecThreadInfo::numTCPWorkers()) + 25; // even healthier margin than before
912 wantFDs
+= (RecThreadInfo::numUDPWorkers() + RecThreadInfo::numTCPWorkers()) * TCPOutConnectionManager::s_maxIdlePerThread
;
914 if (wantFDs
> availFDs
) {
915 unsigned int hardlimit
= getFilenumLimit(true);
916 if (hardlimit
>= wantFDs
) {
917 setFilenumLimit(wantFDs
);
918 SLOG(g_log
<< Logger::Warning
<< "Raised soft limit on number of filedescriptors to " << wantFDs
<< " to match max-mthreads and threads settings" << endl
,
919 log
->info(Logr::Warning
, "Raised soft limit on number of filedescriptors to match max-mthreads and threads settings", "limit", Logging::Loggable(wantFDs
)));
922 auto newval
= (hardlimit
- 25 - TCPOutConnectionManager::s_maxIdlePerThread
) / (RecThreadInfo::numUDPWorkers() + RecThreadInfo::numTCPWorkers());
923 SLOG(g_log
<< Logger::Warning
<< "Insufficient number of filedescriptors available for max-mthreads*threads setting! (" << hardlimit
<< " < " << wantFDs
<< "), reducing max-mthreads to " << newval
<< endl
,
924 log
->info(Logr::Warning
, "Insufficient number of filedescriptors available for max-mthreads*threads setting! Reducing max-mthreads", "hardlimit", Logging::Loggable(hardlimit
), "want", Logging::Loggable(wantFDs
), "max-mthreads", Logging::Loggable(newval
)));
925 g_maxMThreads
= newval
;
926 setFilenumLimit(hardlimit
);
931 // static std::string s_timestampFormat = "%m-%dT%H:%M:%S";
932 static std::string s_timestampFormat
= "%s";
934 static const char* toTimestampStringMilli(const struct timeval
& tval
, std::array
<char, 64>& buf
)
937 if (s_timestampFormat
!= "%s") {
938 // strftime is not thread safe, it can access locale information
939 static std::mutex mutex
;
940 auto lock
= std::lock_guard(mutex
);
941 struct tm theTime
// clang-format insists on formatting it like this
944 len
= strftime(buf
.data(), buf
.size(), s_timestampFormat
.c_str(), localtime_r(&tval
.tv_sec
, &theTime
));
947 len
= snprintf(buf
.data(), buf
.size(), "%lld", static_cast<long long>(tval
.tv_sec
));
950 snprintf(&buf
.at(len
), buf
.size() - len
, ".%03ld", static_cast<long>(tval
.tv_usec
) / 1000);
955 static void loggerSDBackend(const Logging::Entry
& entry
)
957 static const set
<std::string
, CIStringComparePOSIX
> special
= {
966 "user_invocation_id",
978 // First map SL priority to syslog's Urgency
979 Logger::Urgency urgency
= entry
.d_priority
!= 0 ? Logger::Urgency(entry
.d_priority
) : Logger::Info
;
980 if (urgency
> s_logUrgency
) {
981 // We do not log anything if the Urgency of the message is lower than the requested loglevel.
982 // Not that lower Urgency means higher number.
985 // We need to keep the string in mem until sd_journal_sendv has ben called
986 vector
<string
> strings
;
987 auto appendKeyAndVal
= [&strings
](const string
& key
, const string
& value
) {
988 strings
.emplace_back(key
+ "=" + value
);
990 appendKeyAndVal("MESSAGE", entry
.message
);
992 appendKeyAndVal("ERROR", entry
.error
.get());
994 appendKeyAndVal("LEVEL", std::to_string(entry
.level
));
995 appendKeyAndVal("PRIORITY", std::to_string(entry
.d_priority
));
997 appendKeyAndVal("SUBSYSTEM", entry
.name
.get());
999 std::array
<char, 64> timebuf
{};
1000 appendKeyAndVal("TIMESTAMP", toTimestampStringMilli(entry
.d_timestamp
, timebuf
));
1001 for (const auto& value
: entry
.values
) {
1002 if (value
.first
.at(0) == '_' || special
.count(value
.first
) != 0) {
1004 key
.append(value
.first
);
1005 appendKeyAndVal(toUpper(key
), value
.second
);
1008 appendKeyAndVal(toUpper(value
.first
), value
.second
);
1011 // Thread id filled in by backend, since the SL code does not know about RecursorThreads
1012 // We use the Recursor thread, other threads get id 0. May need to revisit.
1013 appendKeyAndVal("TID", std::to_string(RecThreadInfo::id()));
1016 iov
.reserve(strings
.size());
1017 for (const auto& str
: strings
) {
1018 // iovec has no 2 arg constructor, so make it explicit
1019 iov
.emplace_back(iovec
{const_cast<void*>(reinterpret_cast<const void*>(str
.data())), str
.size()}); // NOLINT: it's the API
1021 sd_journal_sendv(iov
.data(), static_cast<int>(iov
.size()));
1025 static void loggerBackend(const Logging::Entry
& entry
)
1027 static thread_local
std::stringstream buf
;
1029 // First map SL priority to syslog's Urgency
1030 Logger::Urgency urg
= entry
.d_priority
!= 0 ? Logger::Urgency(entry
.d_priority
) : Logger::Info
;
1031 if (urg
> s_logUrgency
) {
1032 // We do not log anything if the Urgency of the message is lower than the requested loglevel.
1033 // Not that lower Urgency means higher number.
1037 buf
<< "msg=" << std::quoted(entry
.message
);
1039 buf
<< " error=" << std::quoted(entry
.error
.get());
1043 buf
<< " subsystem=" << std::quoted(entry
.name
.get());
1045 buf
<< " level=" << std::quoted(std::to_string(entry
.level
));
1046 if (entry
.d_priority
!= 0) {
1047 buf
<< " prio=" << std::quoted(Logr::Logger::toString(entry
.d_priority
));
1049 // Thread id filled in by backend, since the SL code does not know about RecursorThreads
1050 // We use the Recursor thread, other threads get id 0. May need to revisit.
1051 buf
<< " tid=" << std::quoted(std::to_string(RecThreadInfo::id()));
1052 std::array
<char, 64> timebuf
{};
1053 buf
<< " ts=" << std::quoted(toTimestampStringMilli(entry
.d_timestamp
, timebuf
));
1054 for (auto const& value
: entry
.values
) {
1056 buf
<< value
.first
<< "=" << std::quoted(value
.second
);
1059 g_log
<< urg
<< buf
.str() << endl
;
1062 static int ratePercentage(uint64_t nom
, uint64_t denom
)
1067 return static_cast<int>(round(100.0 * static_cast<double>(nom
) / static_cast<double>(denom
)));
1070 static void doStats()
1072 static time_t lastOutputTime
;
1073 static uint64_t lastQueryCount
;
1075 uint64_t cacheHits
= g_recCache
->getCacheHits();
1076 uint64_t cacheMisses
= g_recCache
->getCacheMisses();
1077 uint64_t cacheSize
= g_recCache
->size();
1078 auto rc_stats
= g_recCache
->stats();
1079 auto pc_stats
= g_packetCache
? g_packetCache
->stats() : std::pair
<uint64_t, uint64_t>{0, 0};
1080 double rrc
= ratePercentage(rc_stats
.first
, rc_stats
.second
);
1081 double rpc
= ratePercentage(pc_stats
.first
, pc_stats
.second
);
1082 uint64_t negCacheSize
= g_negCache
->size();
1083 auto taskPushes
= getTaskPushes();
1084 auto taskExpired
= getTaskExpired();
1085 auto taskSize
= getTaskSize();
1086 uint64_t pcSize
= g_packetCache
? g_packetCache
->size() : 0;
1087 uint64_t pcHits
= g_packetCache
? g_packetCache
->getHits() : 0;
1089 auto log
= g_slog
->withName("stats");
1091 auto qcounter
= g_Counters
.sum(rec::Counter::qcounter
);
1092 auto syncresqueries
= g_Counters
.sum(rec::Counter::syncresqueries
);
1093 auto outqueries
= g_Counters
.sum(rec::Counter::outqueries
);
1094 auto throttledqueries
= g_Counters
.sum(rec::Counter::throttledqueries
);
1095 auto tcpoutqueries
= g_Counters
.sum(rec::Counter::tcpoutqueries
);
1096 auto dotoutqueries
= g_Counters
.sum(rec::Counter::dotoutqueries
);
1097 auto outgoingtimeouts
= g_Counters
.sum(rec::Counter::outgoingtimeouts
);
1098 if (qcounter
> 0 && (cacheHits
+ cacheMisses
) > 0 && syncresqueries
> 0 && outqueries
> 0) {
1099 if (!g_slogStructured
) {
1100 g_log
<< Logger::Notice
<< "stats: " << qcounter
<< " questions, " << cacheSize
<< " cache entries, " << negCacheSize
<< " negative entries, " << ratePercentage(cacheHits
, cacheHits
+ cacheMisses
) << "% cache hits" << endl
;
1101 g_log
<< Logger::Notice
<< "stats: record cache contended/acquired " << rc_stats
.first
<< '/' << rc_stats
.second
<< " = " << rrc
<< '%' << endl
;
1102 g_log
<< Logger::Notice
<< "stats: packet cache contended/acquired " << pc_stats
.first
<< '/' << pc_stats
.second
<< " = " << rpc
<< '%' << endl
;
1104 g_log
<< Logger::Notice
<< "stats: throttle map: "
1105 << SyncRes::getThrottledServersSize() << ", ns speeds: "
1106 << SyncRes::getNSSpeedsSize() << ", failed ns: "
1107 << SyncRes::getFailedServersSize() << ", ednsmap: "
1108 << SyncRes::getEDNSStatusesSize() << ", non-resolving: "
1109 << SyncRes::getNonResolvingNSSize() << ", saved-parentsets: "
1110 << SyncRes::getSaveParentsNSSetsSize()
1112 g_log
<< Logger::Notice
<< "stats: outpacket/query ratio " << ratePercentage(outqueries
, syncresqueries
) << "%";
1113 g_log
<< Logger::Notice
<< ", " << ratePercentage(throttledqueries
, outqueries
+ throttledqueries
) << "% throttled" << endl
;
1114 g_log
<< Logger::Notice
<< "stats: " << tcpoutqueries
<< "/" << dotoutqueries
<< "/" << getCurrentIdleTCPConnections() << " outgoing tcp/dot/idle connections, " << broadcastAccFunction
<uint64_t>(pleaseGetConcurrentQueries
) << " queries running, " << outgoingtimeouts
<< " outgoing timeouts " << endl
;
1116 g_log
<< Logger::Notice
<< "stats: " << pcSize
<< " packet cache entries, " << ratePercentage(pcHits
, qcounter
) << "% packet cache hits" << endl
;
1118 g_log
<< Logger::Notice
<< "stats: tasks pushed/expired/queuesize: " << taskPushes
<< '/' << taskExpired
<< '/' << taskSize
<< endl
;
1121 const string report
= "Periodic statistics report";
1122 log
->info(Logr::Info
, report
,
1123 "questions", Logging::Loggable(qcounter
),
1124 "cache-entries", Logging::Loggable(cacheSize
),
1125 "negcache-entries", Logging::Loggable(negCacheSize
),
1126 "record-cache-hitratio-perc", Logging::Loggable(ratePercentage(cacheHits
, cacheHits
+ cacheMisses
)),
1127 "record-cache-contended", Logging::Loggable(rc_stats
.first
),
1128 "record-cache-acquired", Logging::Loggable(rc_stats
.second
),
1129 "record-cache-contended-perc", Logging::Loggable(rrc
),
1130 "packetcache-contended", Logging::Loggable(pc_stats
.first
),
1131 "packetcache-acquired", Logging::Loggable(pc_stats
.second
),
1132 "packetcache-contended-perc", Logging::Loggable(rpc
));
1133 log
->info(Logr::Info
, report
,
1134 "throttle-entries", Logging::Loggable(SyncRes::getThrottledServersSize()),
1135 "nsspeed-entries", Logging::Loggable(SyncRes::getNSSpeedsSize()),
1136 "failed-host-entries", Logging::Loggable(SyncRes::getFailedServersSize()),
1137 "edns-entries", Logging::Loggable(SyncRes::getEDNSStatusesSize()),
1138 "non-resolving-nameserver-entries", Logging::Loggable(SyncRes::getNonResolvingNSSize()),
1139 "saved-parent-ns-sets-entries", Logging::Loggable(SyncRes::getSaveParentsNSSetsSize()),
1140 "outqueries-per-query", Logging::Loggable(ratePercentage(outqueries
, syncresqueries
)));
1141 log
->info(Logr::Info
, report
,
1142 "throttled-queries-perc", Logging::Loggable(ratePercentage(throttledqueries
, outqueries
+ throttledqueries
)),
1143 "tcp-outqueries", Logging::Loggable(tcpoutqueries
),
1144 "dot-outqueries", Logging::Loggable(dotoutqueries
),
1145 "idle-tcpout-connections", Logging::Loggable(getCurrentIdleTCPConnections()),
1146 "concurrent-queries", Logging::Loggable(broadcastAccFunction
<uint64_t>(pleaseGetConcurrentQueries
)),
1147 "outgoing-timeouts", Logging::Loggable(outgoingtimeouts
));
1148 log
->info(Logr::Info
, report
,
1149 "packetcache-entries", Logging::Loggable(pcSize
),
1150 "packetcache-hitratio-perc", Logging::Loggable(ratePercentage(pcHits
, qcounter
)),
1151 "taskqueue-pushed", Logging::Loggable(taskPushes
),
1152 "taskqueue-expired", Logging::Loggable(taskExpired
),
1153 "taskqueue-size", Logging::Loggable(taskSize
));
1156 for (const auto& threadInfo
: RecThreadInfo::infos()) {
1157 if (threadInfo
.isWorker()) {
1158 SLOG(g_log
<< Logger::Notice
<< "stats: thread " << idx
<< " has been distributed " << threadInfo
.getNumberOfDistributedQueries() << " queries" << endl
,
1159 log
->info(Logr::Info
, "Queries handled by thread", "thread", Logging::Loggable(idx
), "tname", Logging::Loggable(threadInfo
.getName()), "count", Logging::Loggable(threadInfo
.getNumberOfDistributedQueries())));
1163 time_t now
= time(nullptr);
1164 if (lastOutputTime
!= 0 && lastQueryCount
!= 0 && now
!= lastOutputTime
) {
1165 SLOG(g_log
<< Logger::Notice
<< "stats: " << (qcounter
- lastQueryCount
) / (now
- lastOutputTime
) << " qps (average over " << (now
- lastOutputTime
) << " seconds)" << endl
,
1166 log
->info(Logr::Info
, "Periodic QPS report", "qps", Logging::Loggable((qcounter
- lastQueryCount
) / (now
- lastOutputTime
)),
1167 "averagedOver", Logging::Loggable(now
- lastOutputTime
)));
1169 lastOutputTime
= now
;
1170 lastQueryCount
= qcounter
;
1172 else if (statsWanted
) {
1173 SLOG(g_log
<< Logger::Notice
<< "stats: no stats yet!" << endl
,
1174 log
->info(Logr::Notice
, "No stats yet"));
1177 statsWanted
= false;
1180 static std::shared_ptr
<NetmaskGroup
> parseACL(const std::string
& aclFile
, const std::string
& aclSetting
, Logr::log_t log
)
1182 auto result
= std::make_shared
<NetmaskGroup
>();
1184 const string file
= ::arg()[aclFile
];
1186 if (!file
.empty()) {
1187 if (boost::ends_with(file
, ".yml")) {
1188 ::rust::vec
<::rust::string
> vec
;
1189 pdns::settings::rec::readYamlAllowFromFile(file
, vec
, log
);
1190 for (const auto& subnet
: vec
) {
1191 result
->addMask(string(subnet
));
1199 throw runtime_error("Could not open '" + file
+ "': " + stringerror(err
));
1202 while (getline(ifs
, line
)) {
1203 auto pos
= line
.find('#');
1204 if (pos
!= string::npos
) {
1212 result
->addMask(line
);
1215 SLOG(g_log
<< Logger::Info
<< "Done parsing " << result
->size() << " " << aclSetting
<< " ranges from file '" << file
<< "' - overriding '" << aclSetting
<< "' setting" << endl
,
1216 log
->info(Logr::Info
, "Done parsing ranges from file, will override setting", "setting", Logging::Loggable(aclSetting
),
1217 "number", Logging::Loggable(result
->size()), "file", Logging::Loggable(file
)));
1219 else if (!::arg()[aclSetting
].empty()) {
1221 stringtok(ips
, ::arg()[aclSetting
], ", ");
1223 for (const auto& address
: ips
) {
1224 result
->addMask(address
);
1226 if (!g_slogStructured
) {
1227 g_log
<< Logger::Info
<< aclSetting
<< ": ";
1228 for (auto i
= ips
.begin(); i
!= ips
.end(); ++i
) {
1229 if (i
!= ips
.begin()) {
1230 g_log
<< Logger::Info
<< ", ";
1232 g_log
<< Logger::Info
<< *i
;
1234 g_log
<< Logger::Info
<< endl
;
1237 log
->info(Logr::Info
, "Setting access control", "acl", Logging::Loggable(aclSetting
), "addresses", Logging::IterLoggable(ips
.begin(), ips
.end()));
1244 static void* pleaseSupplantAllowFrom(std::shared_ptr
<NetmaskGroup
> nmgroup
)
1246 t_allowFrom
= std::move(nmgroup
);
1250 static void* pleaseSupplantAllowNotifyFrom(std::shared_ptr
<NetmaskGroup
> nmgroup
)
1252 t_allowNotifyFrom
= std::move(nmgroup
);
1256 void* pleaseSupplantAllowNotifyFor(std::shared_ptr
<notifyset_t
> allowNotifyFor
)
1258 t_allowNotifyFor
= std::move(allowNotifyFor
);
1264 auto log
= g_slog
->withName("config");
1266 static bool l_initialized
;
1268 if (l_initialized
) { // only reload configuration file on second call
1270 string configName
= ::arg()["config-dir"] + "/recursor";
1271 if (!::arg()["config-name"].empty()) {
1272 configName
= ::arg()["config-dir"] + "/recursor-" + ::arg()["config-name"];
1274 cleanSlashes(configName
);
1276 if (g_yamlSettings
) {
1277 configName
+= ".yml";
1279 pdns::rust::settings::rec::Recursorsettings settings
;
1280 // XXX Does ::arg()["include-dir"] have the right value, i.e. potentially overriden by command line?
1281 auto yamlstatus
= pdns::settings::rec::readYamlSettings(configName
, ::arg()["include-dir"], settings
, msg
, log
);
1283 switch (yamlstatus
) {
1284 case pdns::settings::rec::YamlSettingsStatus::CannotOpen
:
1285 throw runtime_error("Unable to open '" + configName
+ "': " + msg
);
1287 case pdns::settings::rec::YamlSettingsStatus::PresentButFailed
:
1288 throw runtime_error("Error processing '" + configName
+ "': " + msg
);
1290 case pdns::settings::rec::YamlSettingsStatus::OK
:
1291 pdns::settings::rec::processAPIDir(arg()["include-dir"], settings
, log
);
1292 // Does *not* set include-dir
1293 pdns::settings::rec::setArgsForACLRelatedSettings(settings
);
1298 configName
+= ".conf";
1299 if (!::arg().preParseFile(configName
, "allow-from-file")) {
1300 throw runtime_error("Unable to re-parse configuration file '" + configName
+ "'");
1302 ::arg().preParseFile(configName
, "allow-from", LOCAL_NETS
);
1304 if (!::arg().preParseFile(configName
, "allow-notify-from-file")) {
1305 throw runtime_error("Unable to re-parse configuration file '" + configName
+ "'");
1307 ::arg().preParseFile(configName
, "allow-notify-from");
1309 ::arg().preParseFile(configName
, "include-dir");
1310 ::arg().preParse(g_argc
, g_argv
, "include-dir");
1312 // then process includes
1313 std::vector
<std::string
> extraConfigs
;
1314 ::arg().gatherIncludes(::arg()["include-dir"], ".conf", extraConfigs
);
1316 for (const std::string
& fileName
: extraConfigs
) {
1317 if (!::arg().preParseFile(fileName
, "allow-from-file", ::arg()["allow-from-file"])) {
1318 throw runtime_error("Unable to re-parse configuration file include '" + fileName
+ "'");
1320 if (!::arg().preParseFile(fileName
, "allow-from", ::arg()["allow-from"])) {
1321 throw runtime_error("Unable to re-parse configuration file include '" + fileName
+ "'");
1324 if (!::arg().preParseFile(fileName
, "allow-notify-from-file", ::arg()["allow-notify-from-file"])) {
1325 throw runtime_error("Unable to re-parse configuration file include '" + fileName
+ "'");
1327 if (!::arg().preParseFile(fileName
, "allow-notify-from", ::arg()["allow-notify-from"])) {
1328 throw runtime_error("Unable to re-parse configuration file include '" + fileName
+ "'");
1333 // Process command line args potentially overriding settings read from file
1334 ::arg().preParse(g_argc
, g_argv
, "allow-from-file");
1335 ::arg().preParse(g_argc
, g_argv
, "allow-from");
1337 ::arg().preParse(g_argc
, g_argv
, "allow-notify-from-file");
1338 ::arg().preParse(g_argc
, g_argv
, "allow-notify-from");
1340 auto allowFrom
= parseACL("allow-from-file", "allow-from", log
);
1342 if (allowFrom
->empty()) {
1343 if (::arg()["local-address"] != "127.0.0.1" && ::arg().asNum("local-port") == 53) {
1344 SLOG(g_log
<< Logger::Warning
<< "WARNING: Allowing queries from all IP addresses - this can be a security risk!" << endl
,
1345 log
->info(Logr::Warning
, "WARNING: Allowing queries from all IP addresses - this can be a security risk!"));
1347 allowFrom
= nullptr;
1350 g_initialAllowFrom
= allowFrom
;
1351 // coverity[copy_constructor_call] maybe this can be avoided, but be careful as pointers get passed to other threads
1352 broadcastFunction([=] { return pleaseSupplantAllowFrom(allowFrom
); });
1354 auto allowNotifyFrom
= parseACL("allow-notify-from-file", "allow-notify-from", log
);
1356 g_initialAllowNotifyFrom
= allowNotifyFrom
;
1357 // coverity[copy_constructor_call] maybe this can be avoided, but be careful as pointers get passed to other threads
1358 broadcastFunction([=] { return pleaseSupplantAllowNotifyFrom(allowNotifyFrom
); });
1360 l_initialized
= true;
1363 void broadcastFunction(const pipefunc_t
& func
)
1365 /* This function might be called by the worker with t_id 0 during startup
1366 for the initialization of ACLs and domain maps. After that it should only
1367 be called by the handler. */
1369 if (RecThreadInfo::infos().empty() && RecThreadInfo::id() == 0) {
1370 /* the handler and distributors will call themselves below, but
1371 during startup we get called while g_threadInfos has not been
1372 populated yet to update the ACL or domain maps, so we need to
1378 unsigned int thread
= 0;
1379 for (const auto& threadInfo
: RecThreadInfo::infos()) {
1380 if (thread
++ == RecThreadInfo::id()) {
1381 func(); // don't write to ourselves!
1385 ThreadMSG
* tmsg
= new ThreadMSG(); // NOLINT: manual ownership handling
1387 tmsg
->wantAnswer
= true;
1388 if (write(threadInfo
.getPipes().writeToThread
, &tmsg
, sizeof(tmsg
)) != sizeof(tmsg
)) { // NOLINT: sizeof correct
1389 delete tmsg
; // NOLINT: manual ownership handling
1391 unixDie("write to thread pipe returned wrong size or error");
1394 string
* resp
= nullptr;
1395 if (read(threadInfo
.getPipes().readFromThread
, &resp
, sizeof(resp
)) != sizeof(resp
)) { // NOLINT: sizeof correct
1396 unixDie("read from thread pipe returned wrong size or error");
1399 if (resp
!= nullptr) {
1400 delete resp
; // NOLINT: manual ownership handling
1403 // coverity[leaked_storage]
1408 void* voider(const std::function
<T
*()>& func
)
1413 static vector
<ComboAddress
>& operator+=(vector
<ComboAddress
>& lhs
, const vector
<ComboAddress
>& rhs
)
1415 lhs
.insert(lhs
.end(), rhs
.begin(), rhs
.end());
1419 static vector
<pair
<DNSName
, uint16_t>>& operator+=(vector
<pair
<DNSName
, uint16_t>>& lhs
, const vector
<pair
<DNSName
, uint16_t>>& rhs
)
1421 lhs
.insert(lhs
.end(), rhs
.begin(), rhs
.end());
1425 static ProxyMappingStats_t
& operator+=(ProxyMappingStats_t
& lhs
, const ProxyMappingStats_t
& rhs
)
1427 for (const auto& [key
, entry
] : rhs
) {
1428 lhs
[key
].netmaskMatches
+= entry
.netmaskMatches
;
1429 lhs
[key
].suffixMatches
+= entry
.suffixMatches
;
1434 static RemoteLoggerStats_t
& operator+=(RemoteLoggerStats_t
& lhs
, const RemoteLoggerStats_t
& rhs
)
1436 for (const auto& [key
, entry
] : rhs
) {
1442 // This function should only be called by the handler to gather
1443 // metrics, wipe the cache, reload the Lua script (not the Lua config)
1444 // or change the current trace regex, and by the SNMP thread to gather
1446 // Note that this currently skips the handler, but includes the taskThread(s).
1448 T
broadcastAccFunction(const std::function
<T
*()>& func
)
1450 if (!RecThreadInfo::self().isHandler()) {
1451 SLOG(g_log
<< Logger::Error
<< "broadcastAccFunction has been called by a worker (" << RecThreadInfo::id() << ")" << endl
,
1452 g_slog
->withName("runtime")->info(Logr::Critical
, "broadcastAccFunction has been called by a worker")); // tid will be added
1456 unsigned int thread
= 0;
1458 for (const auto& threadInfo
: RecThreadInfo::infos()) {
1459 if (thread
++ == RecThreadInfo::id()) {
1463 const auto& tps
= threadInfo
.getPipes();
1464 ThreadMSG
* tmsg
= new ThreadMSG(); // NOLINT: manual ownership handling
1465 tmsg
->func
= [func
] { return voider
<T
>(func
); };
1466 tmsg
->wantAnswer
= true;
1468 if (write(tps
.writeToThread
, &tmsg
, sizeof(tmsg
)) != sizeof(tmsg
)) { // NOLINT:: sizeof correct
1469 delete tmsg
; // NOLINT: manual ownership handling
1470 unixDie("write to thread pipe returned wrong size or error");
1474 if (read(tps
.readFromThread
, &resp
, sizeof(resp
)) != sizeof(resp
)) // NOLINT: sizeof correct
1475 unixDie("read from thread pipe returned wrong size or error");
1479 delete resp
; // NOLINT: manual ownership handling
1482 // coverity[leaked_storage]
1487 template string
broadcastAccFunction(const std::function
<string
*()>& fun
); // explicit instantiation
1488 template RecursorControlChannel::Answer
broadcastAccFunction(const std::function
<RecursorControlChannel::Answer
*()>& fun
); // explicit instantiation
1489 template uint64_t broadcastAccFunction(const std::function
<uint64_t*()>& fun
); // explicit instantiation
1490 template vector
<ComboAddress
> broadcastAccFunction(const std::function
<vector
<ComboAddress
>*()>& fun
); // explicit instantiation
1491 template vector
<pair
<DNSName
, uint16_t>> broadcastAccFunction(const std::function
<vector
<pair
<DNSName
, uint16_t>>*()>& fun
); // explicit instantiation
1492 template ThreadTimes
broadcastAccFunction(const std::function
<ThreadTimes
*()>& fun
);
1493 template ProxyMappingStats_t
broadcastAccFunction(const std::function
<ProxyMappingStats_t
*()>& fun
);
1494 template RemoteLoggerStats_t
broadcastAccFunction(const std::function
<RemoteLoggerStats_t
*()>& fun
);
1496 static int initNet(Logr::log_t log
)
1498 checkLinuxIPv6Limits(log
);
1500 pdns::parseQueryLocalAddress(::arg()["query-local-address"]);
1502 catch (std::exception
& e
) {
1503 SLOG(g_log
<< Logger::Error
<< "Assigning local query addresses: " << e
.what(),
1504 log
->error(Logr::Error
, e
.what(), "Unable to assign local query address"));
1508 if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET
)) {
1509 SyncRes::s_doIPv4
= true;
1510 SLOG(g_log
<< Logger::Warning
<< "Enabling IPv4 transport for outgoing queries" << endl
,
1511 log
->info(Logr::Notice
, "Enabling IPv4 transport for outgoing queries"));
1514 SLOG(g_log
<< Logger::Warning
<< "NOT using IPv4 for outgoing queries - add an IPv4 address (like '0.0.0.0') to query-local-address to enable" << endl
,
1515 log
->info(Logr::Warning
, "NOT using IPv4 for outgoing queries - add an IPv4 address (like '0.0.0.0') to query-local-address to enable"));
1518 if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET6
)) {
1519 SyncRes::s_doIPv6
= true;
1520 SLOG(g_log
<< Logger::Warning
<< "Enabling IPv6 transport for outgoing queries" << endl
,
1521 log
->info(Logr::Notice
, "Enabling IPv6 transport for outgoing queries"));
1524 SLOG(g_log
<< Logger::Warning
<< "NOT using IPv6 for outgoing queries - add an IPv6 address (like '::') to query-local-address to enable" << endl
,
1525 log
->info(Logr::Warning
, "NOT using IPv6 for outgoing queries - add an IPv6 address (like '::') to query-local-address to enable"));
1528 if (!SyncRes::s_doIPv6
&& !SyncRes::s_doIPv4
) {
1529 SLOG(g_log
<< Logger::Error
<< "No outgoing addresses configured! Can not continue" << endl
,
1530 log
->info(Logr::Error
, "No outgoing addresses configured! Can not continue"));
1536 static int initDNSSEC(Logr::log_t log
)
1538 if (::arg()["dnssec"] == "off") {
1539 g_dnssecmode
= DNSSECMode::Off
;
1541 else if (::arg()["dnssec"] == "process-no-validate") {
1542 g_dnssecmode
= DNSSECMode::ProcessNoValidate
;
1544 else if (::arg()["dnssec"] == "process") {
1545 g_dnssecmode
= DNSSECMode::Process
;
1547 else if (::arg()["dnssec"] == "validate") {
1548 g_dnssecmode
= DNSSECMode::ValidateAll
;
1550 else if (::arg()["dnssec"] == "log-fail") {
1551 g_dnssecmode
= DNSSECMode::ValidateForLog
;
1554 SLOG(g_log
<< Logger::Error
<< "Unknown DNSSEC mode " << ::arg()["dnssec"] << endl
,
1555 log
->info(Logr::Error
, "Unknown DNSSEC mode", "dnssec", Logging::Loggable(::arg()["dnssec"])));
1559 g_signatureInceptionSkew
= ::arg().asNum("signature-inception-skew");
1560 if (g_signatureInceptionSkew
< 0) {
1561 SLOG(g_log
<< Logger::Error
<< "A negative value for 'signature-inception-skew' is not allowed" << endl
,
1562 log
->info(Logr::Error
, "A negative value for 'signature-inception-skew' is not allowed"));
1566 g_dnssecLogBogus
= ::arg().mustDo("dnssec-log-bogus");
1567 g_maxNSEC3Iterations
= ::arg().asNum("nsec3-max-iterations");
1569 vector
<string
> nums
;
1570 bool automatic
= true;
1571 if (!::arg()["dnssec-disabled-algorithms"].empty()) {
1573 stringtok(nums
, ::arg()["dnssec-disabled-algorithms"], ", ");
1574 for (const auto& num
: nums
) {
1575 DNSCryptoKeyEngine::switchOffAlgorithm(pdns::checked_stoi
<unsigned int>(num
));
1579 for (auto algo
: {DNSSECKeeper::RSASHA1
, DNSSECKeeper::RSASHA1NSEC3SHA1
}) {
1580 if (!DNSCryptoKeyEngine::verifyOne(algo
)) {
1581 DNSCryptoKeyEngine::switchOffAlgorithm(algo
);
1582 nums
.push_back(std::to_string(algo
));
1586 if (!nums
.empty()) {
1587 if (!g_slogStructured
) {
1588 g_log
<< Logger::Warning
<< (automatic
? "Automatically" : "Manually") << " disabled DNSSEC algorithms: ";
1589 for (auto i
= nums
.begin(); i
!= nums
.end(); ++i
) {
1590 if (i
!= nums
.begin()) {
1591 g_log
<< Logger::Warning
<< ", ";
1593 g_log
<< Logger::Warning
<< *i
;
1595 g_log
<< Logger::Warning
<< endl
;
1598 log
->info(Logr::Notice
, "Disabled DNSSEC algorithms", "automatically", Logging::Loggable(automatic
), "algorithms", Logging::IterLoggable(nums
.begin(), nums
.end()));
1605 static void initDontQuery(Logr::log_t log
)
1607 if (!::arg()["dont-query"].empty()) {
1609 stringtok(ips
, ::arg()["dont-query"], ", ");
1610 ips
.emplace_back("0.0.0.0");
1611 ips
.emplace_back("::");
1613 for (const auto& anIP
: ips
) {
1614 SyncRes::addDontQuery(anIP
);
1616 if (!g_slogStructured
) {
1617 g_log
<< Logger::Warning
<< "Will not send queries to: ";
1618 for (auto i
= ips
.begin(); i
!= ips
.end(); ++i
) {
1619 if (i
!= ips
.begin()) {
1620 g_log
<< Logger::Warning
<< ", ";
1622 g_log
<< Logger::Warning
<< *i
;
1624 g_log
<< Logger::Warning
<< endl
;
1627 log
->info(Logr::Notice
, "Will not send queries to", "addresses", Logging::IterLoggable(ips
.begin(), ips
.end()));
1632 static int initSyncRes(Logr::log_t log
)
1634 SyncRes::s_minimumTTL
= ::arg().asNum("minimum-ttl-override");
1635 SyncRes::s_minimumECSTTL
= ::arg().asNum("ecs-minimum-ttl-override");
1636 SyncRes::s_maxnegttl
= ::arg().asNum("max-negative-ttl");
1637 SyncRes::s_maxbogusttl
= ::arg().asNum("max-cache-bogus-ttl");
1638 SyncRes::s_maxcachettl
= max(::arg().asNum("max-cache-ttl"), 15);
1640 SyncRes::s_packetcachettl
= ::arg().asNum("packetcache-ttl");
1641 // Cap the packetcache-servfail-ttl and packetcache-negative-ttl to packetcache-ttl
1642 SyncRes::s_packetcacheservfailttl
= std::min(static_cast<unsigned int>(::arg().asNum("packetcache-servfail-ttl")), SyncRes::s_packetcachettl
);
1643 SyncRes::s_packetcachenegativettl
= std::min(static_cast<unsigned int>(::arg().asNum("packetcache-negative-ttl")), SyncRes::s_packetcachettl
);
1645 SyncRes::s_serverdownmaxfails
= ::arg().asNum("server-down-max-fails");
1646 SyncRes::s_serverdownthrottletime
= ::arg().asNum("server-down-throttle-time");
1647 SyncRes::s_unthrottle_n
= ::arg().asNum("bypass-server-throttling-probability");
1648 SyncRes::s_nonresolvingnsmaxfails
= ::arg().asNum("non-resolving-ns-max-fails");
1649 SyncRes::s_nonresolvingnsthrottletime
= ::arg().asNum("non-resolving-ns-throttle-time");
1650 SyncRes::s_serverID
= ::arg()["server-id"];
1651 // This bound is dynamically adjusted in SyncRes, depending on qname minimization being active
1652 SyncRes::s_maxqperq
= ::arg().asNum("max-qperq");
1653 SyncRes::s_maxnsperresolve
= ::arg().asNum("max-ns-per-resolve");
1654 SyncRes::s_maxnsaddressqperq
= ::arg().asNum("max-ns-address-qperq");
1655 SyncRes::s_maxtotusec
= 1000 * ::arg().asNum("max-total-msec");
1656 SyncRes::s_maxdepth
= ::arg().asNum("max-recursion-depth");
1657 SyncRes::s_rootNXTrust
= ::arg().mustDo("root-nx-trust");
1658 SyncRes::s_refresh_ttlperc
= ::arg().asNum("refresh-on-ttl-perc");
1659 SyncRes::s_locked_ttlperc
= ::arg().asNum("record-cache-locked-ttl-perc");
1660 RecursorPacketCache::s_refresh_ttlperc
= SyncRes::s_refresh_ttlperc
;
1661 SyncRes::s_tcp_fast_open
= ::arg().asNum("tcp-fast-open");
1662 SyncRes::s_tcp_fast_open_connect
= ::arg().mustDo("tcp-fast-open-connect");
1664 SyncRes::s_dot_to_port_853
= ::arg().mustDo("dot-to-port-853");
1665 SyncRes::s_event_trace_enabled
= ::arg().asNum("event-trace-enabled");
1666 SyncRes::s_save_parent_ns_set
= ::arg().mustDo("save-parent-ns-set");
1667 SyncRes::s_max_busy_dot_probes
= ::arg().asNum("max-busy-dot-probes");
1669 uint64_t sse
= ::arg().asNum("serve-stale-extensions");
1670 if (sse
> std::numeric_limits
<uint16_t>::max()) {
1671 SLOG(g_log
<< Logger::Error
<< "Illegal serve-stale-extensions value: " << sse
<< "; range = 0..65536" << endl
,
1672 log
->info(Logr::Error
, "Illegal serve-stale-extensions value; range = 0..65536", "value", Logging::Loggable(sse
)));
1675 MemRecursorCache::s_maxServedStaleExtensions
= sse
;
1676 NegCache::s_maxServedStaleExtensions
= sse
;
1679 if (SyncRes::s_tcp_fast_open_connect
) {
1680 checkFastOpenSysctl(true, log
);
1681 checkTFOconnect(log
);
1683 SyncRes::s_ecsipv4limit
= ::arg().asNum("ecs-ipv4-bits");
1684 SyncRes::s_ecsipv6limit
= ::arg().asNum("ecs-ipv6-bits");
1685 SyncRes::clearECSStats();
1686 SyncRes::s_ecsipv4cachelimit
= ::arg().asNum("ecs-ipv4-cache-bits");
1687 SyncRes::s_ecsipv6cachelimit
= ::arg().asNum("ecs-ipv6-cache-bits");
1688 SyncRes::s_ecsipv4nevercache
= ::arg().mustDo("ecs-ipv4-never-cache");
1689 SyncRes::s_ecsipv6nevercache
= ::arg().mustDo("ecs-ipv6-never-cache");
1690 SyncRes::s_ecscachelimitttl
= ::arg().asNum("ecs-cache-limit-ttl");
1692 SyncRes::s_qnameminimization
= ::arg().mustDo("qname-minimization");
1693 SyncRes::s_minimize_one_label
= ::arg().asNum("qname-minimize-one-label");
1694 SyncRes::s_max_minimize_count
= ::arg().asNum("qname-max-minimize-count");
1696 SyncRes::s_hardenNXD
= SyncRes::HardenNXD::DNSSEC
;
1697 string value
= ::arg()["nothing-below-nxdomain"];
1698 if (value
== "yes") {
1699 SyncRes::s_hardenNXD
= SyncRes::HardenNXD::Yes
;
1701 else if (value
== "no") {
1702 SyncRes::s_hardenNXD
= SyncRes::HardenNXD::No
;
1704 else if (value
!= "dnssec") {
1705 SLOG(g_log
<< Logger::Error
<< "Unknown nothing-below-nxdomain mode: " << value
<< endl
,
1706 log
->info(Logr::Error
, "Unknown nothing-below-nxdomain mode", "mode", Logging::Loggable(value
)));
1710 if (!::arg().isEmpty("ecs-scope-zero-address")) {
1711 ComboAddress
scopeZero(::arg()["ecs-scope-zero-address"]);
1712 SyncRes::setECSScopeZeroAddress(Netmask(scopeZero
, scopeZero
.isIPv4() ? 32 : 128));
1718 auto addr
= pdns::getNonAnyQueryLocalAddress(AF_INET
);
1719 if (addr
.sin4
.sin_family
!= 0) {
1720 netmask
= Netmask(addr
, 32);
1724 addr
= pdns::getNonAnyQueryLocalAddress(AF_INET6
);
1725 if (addr
.sin4
.sin_family
!= 0) {
1726 netmask
= Netmask(addr
, 128);
1731 netmask
= Netmask(ComboAddress("127.0.0.1"), 32);
1733 SyncRes::setECSScopeZeroAddress(netmask
);
1736 SyncRes::parseEDNSSubnetAllowlist(::arg()["edns-subnet-whitelist"]);
1737 SyncRes::parseEDNSSubnetAllowlist(::arg()["edns-subnet-allow-list"]);
1738 SyncRes::parseEDNSSubnetAddFor(::arg()["ecs-add-for"]);
1739 g_useIncomingECS
= ::arg().mustDo("use-incoming-edns-subnet");
1743 static void initDistribution(Logr::log_t log
)
1745 g_balancingFactor
= ::arg().asDouble("distribution-load-factor");
1746 if (g_balancingFactor
!= 0.0 && g_balancingFactor
< 1.0) {
1747 g_balancingFactor
= 0.0;
1748 SLOG(g_log
<< Logger::Warning
<< "Asked to run with a distribution-load-factor below 1.0, disabling it instead" << endl
,
1749 log
->info(Logr::Warning
, "Asked to run with a distribution-load-factor below 1.0, disabling it instead"));
1753 g_reusePort
= ::arg().mustDo("reuseport");
1756 RecThreadInfo::infos().resize(RecThreadInfo::numRecursorThreads());
1759 unsigned int threadNum
= 1;
1760 if (RecThreadInfo::weDistributeQueries()) {
1761 /* first thread is the handler, then distributors */
1762 for (unsigned int i
= 0; i
< RecThreadInfo::numDistributors(); i
++, threadNum
++) {
1763 auto& info
= RecThreadInfo::info(threadNum
);
1764 auto& deferredAdds
= info
.getDeferredAdds();
1765 makeUDPServerSockets(deferredAdds
, log
);
1769 /* first thread is the handler, there is no distributor here and workers are accepting queries */
1770 for (unsigned int i
= 0; i
< RecThreadInfo::numUDPWorkers(); i
++, threadNum
++) {
1771 auto& info
= RecThreadInfo::info(threadNum
);
1772 auto& deferredAdds
= info
.getDeferredAdds();
1773 makeUDPServerSockets(deferredAdds
, log
);
1776 threadNum
= 1 + RecThreadInfo::numDistributors() + RecThreadInfo::numUDPWorkers();
1777 for (unsigned int i
= 0; i
< RecThreadInfo::numTCPWorkers(); i
++, threadNum
++) {
1778 auto& info
= RecThreadInfo::info(threadNum
);
1779 auto& deferredAdds
= info
.getDeferredAdds();
1780 auto& tcpSockets
= info
.getTCPSockets();
1781 makeTCPServerSockets(deferredAdds
, tcpSockets
, log
);
1785 std::set
<int> tcpSockets
;
1786 /* we don't have reuseport so we can only open one socket per
1787 listening addr:port and everyone will listen on it */
1788 makeUDPServerSockets(s_deferredUDPadds
, log
);
1789 makeTCPServerSockets(s_deferredTCPadds
, tcpSockets
, log
);
1791 // TCP queries are handled by TCP workers
1792 for (unsigned int i
= 0; i
< RecThreadInfo::numTCPWorkers(); i
++) {
1793 auto& info
= RecThreadInfo::info(i
+ 1 + RecThreadInfo::numDistributors() + RecThreadInfo::numUDPWorkers());
1794 info
.setTCPSockets(tcpSockets
);
1799 static int initForks(Logr::log_t log
)
1802 for (; forks
< ::arg().asNum("processes") - 1; ++forks
) {
1803 if (fork() == 0) { // we are child
1808 if (::arg().mustDo("daemon")) {
1809 SLOG(g_log
<< Logger::Warning
<< "Calling daemonize, going to background" << endl
,
1810 log
->info(Logr::Warning
, "Calling daemonize, going to background"));
1811 g_log
.toConsole(Logger::Critical
);
1815 if (Utility::getpid() == 1) {
1816 /* We are running as pid 1, register sigterm and sigint handler
1818 The Linux kernel will handle SIGTERM and SIGINT for all processes, except PID 1.
1819 It assumes that the processes running as pid 1 is an "init" like system.
1820 For years, this was a safe assumption, but containers change that: in
1821 most (all?) container implementations, the application itself is running
1822 as pid 1. This means that sending signals to those applications, will not
1823 be handled by default. Results might be "your container not responding
1824 when asking it to stop", or "ctrl-c not working even when the app is
1825 running in the foreground inside a container".
1827 So TL;DR: If we're running pid 1 (container), we should handle SIGTERM and SIGINT ourselves */
1829 signal(SIGTERM
, termIntHandler
);
1830 signal(SIGINT
, termIntHandler
);
1833 signal(SIGUSR1
, usr1Handler
);
1834 signal(SIGUSR2
, usr2Handler
);
1835 signal(SIGPIPE
, SIG_IGN
); // NOLINT: Posix API
1839 static int initPorts(Logr::log_t log
)
1841 int port
= ::arg().asNum("udp-source-port-min");
1842 if (port
< 1024 || port
> 65535) {
1843 SLOG(g_log
<< Logger::Error
<< "Unable to launch, udp-source-port-min is not a valid port number" << endl
,
1844 log
->info(Logr::Error
, "Unable to launch, udp-source-port-min is not a valid port number"));
1845 return 99; // this isn't going to fix itself either
1847 g_minUdpSourcePort
= port
;
1848 port
= ::arg().asNum("udp-source-port-max");
1849 if (port
< 1024 || port
> 65535 || port
< g_minUdpSourcePort
) {
1850 SLOG(g_log
<< Logger::Error
<< "Unable to launch, udp-source-port-max is not a valid port number or is smaller than udp-source-port-min" << endl
,
1851 log
->info(Logr::Error
, "Unable to launch, udp-source-port-max is not a valid port number or is smaller than udp-source-port-min"));
1852 return 99; // this isn't going to fix itself either
1854 g_maxUdpSourcePort
= port
;
1855 std::vector
<string
> parts
{};
1856 stringtok(parts
, ::arg()["udp-source-port-avoid"], ", ");
1857 for (const auto& part
: parts
) {
1858 port
= std::stoi(part
);
1859 if (port
< 1024 || port
> 65535) {
1860 SLOG(g_log
<< Logger::Error
<< "Unable to launch, udp-source-port-avoid contains an invalid port number: " << part
<< endl
,
1861 log
->info(Logr::Error
, "Unable to launch, udp-source-port-avoid contains an invalid port number", "port", Logging::Loggable(part
)));
1862 return 99; // this isn't going to fix itself either
1864 g_avoidUdpSourcePorts
.insert(port
);
1869 static void initSNMP([[maybe_unused
]] Logr::log_t log
)
1871 if (::arg().mustDo("snmp-agent")) {
1872 #ifdef HAVE_NET_SNMP
1873 string setting
= ::arg()["snmp-daemon-socket"];
1874 if (setting
.empty()) {
1875 setting
= ::arg()["snmp-master-socket"];
1877 g_snmpAgent
= std::make_shared
<RecursorSNMPAgent
>("recursor", setting
);
1880 const std::string msg
= "snmp-agent set but SNMP support not compiled in";
1881 SLOG(g_log
<< Logger::Error
<< msg
<< endl
,
1882 log
->info(Logr::Error
, msg
));
1883 #endif // HAVE_NET_SNMP
1887 static int initControl(Logr::log_t log
, uid_t newuid
, int forks
)
1889 if (!::arg()["chroot"].empty()) {
1892 ns
= getenv("NOTIFY_SOCKET");
1893 if (ns
!= nullptr) {
1894 SLOG(g_log
<< Logger::Error
<< "Unable to chroot when running from systemd. Please disable chroot= or set the 'Type' for this service to 'simple'" << endl
,
1895 log
->info(Logr::Error
, "Unable to chroot when running from systemd. Please disable chroot= or set the 'Type' for this service to 'simple'"));
1899 if (chroot(::arg()["chroot"].c_str()) < 0 || chdir("/") < 0) {
1901 SLOG(g_log
<< Logger::Error
<< "Unable to chroot to '" + ::arg()["chroot"] + "': " << stringerror(err
) << ", exiting" << endl
,
1902 log
->error(Logr::Error
, err
, "Unable to chroot", "chroot", Logging::Loggable(::arg()["chroot"])));
1905 SLOG(g_log
<< Logger::Info
<< "Chrooted to '" << ::arg()["chroot"] << "'" << endl
,
1906 log
->info(Logr::Info
, "Chrooted", "chroot", Logging::Loggable(::arg()["chroot"])));
1909 checkSocketDir(log
);
1911 g_pidfname
= ::arg()["socket-dir"] + "/" + g_programname
+ ".pid";
1912 if (!g_pidfname
.empty()) {
1913 unlink(g_pidfname
.c_str()); // remove possible old pid file
1917 makeControlChannelSocket(::arg().asNum("processes") > 1 ? forks
: -1);
1919 Utility::dropUserPrivs(newuid
);
1921 /* we might still have capabilities remaining, for example if we have been started as root
1922 without --setuid (please don't do that) or as an unprivileged user with ambient capabilities
1923 like CAP_NET_BIND_SERVICE.
1927 catch (const std::exception
& e
) {
1928 SLOG(g_log
<< Logger::Warning
<< e
.what() << endl
,
1929 log
->error(Logr::Warning
, e
.what(), "Could not drop capabilities"));
1934 static void initSuffixMatchNodes([[maybe_unused
]] Logr::log_t log
)
1937 SuffixMatchNode dontThrottleNames
;
1938 vector
<string
> parts
;
1939 stringtok(parts
, ::arg()["dont-throttle-names"], " ,");
1940 for (const auto& part
: parts
) {
1941 dontThrottleNames
.add(DNSName(part
));
1943 g_dontThrottleNames
.setState(std::move(dontThrottleNames
));
1946 NetmaskGroup dontThrottleNetmasks
;
1947 stringtok(parts
, ::arg()["dont-throttle-netmasks"], " ,");
1948 for (const auto& part
: parts
) {
1949 dontThrottleNetmasks
.addMask(Netmask(part
));
1951 g_dontThrottleNetmasks
.setState(std::move(dontThrottleNetmasks
));
1955 SuffixMatchNode xdnssecNames
;
1956 vector
<string
> parts
;
1957 stringtok(parts
, ::arg()["x-dnssec-names"], " ,");
1958 for (const auto& part
: parts
) {
1959 xdnssecNames
.add(DNSName(part
));
1961 g_xdnssec
.setState(std::move(xdnssecNames
));
1965 SuffixMatchNode dotauthNames
;
1966 vector
<string
> parts
;
1967 stringtok(parts
, ::arg()["dot-to-auth-names"], " ,");
1968 #ifndef HAVE_DNS_OVER_TLS
1969 if (!parts
.empty()) {
1970 SLOG(g_log
<< Logger::Error
<< "dot-to-auth-names setting contains names, but Recursor was built without DNS over TLS support. Setting will be ignored." << endl
,
1971 log
->info(Logr::Error
, "dot-to-auth-names setting contains names, but Recursor was built without DNS over TLS support. Setting will be ignored"));
1974 for (const auto& part
: parts
) {
1975 dotauthNames
.add(DNSName(part
));
1977 g_DoTToAuthNames
.setState(std::move(dotauthNames
));
1981 static void initCarbon()
1983 CarbonConfig config
;
1984 stringtok(config
.servers
, arg()["carbon-server"], ", ");
1985 config
.hostname
= arg()["carbon-ourname"];
1986 config
.instance_name
= arg()["carbon-instance"];
1987 config
.namespace_name
= arg()["carbon-namespace"];
1988 g_carbonConfig
.setState(std::move(config
));
1991 static int initDNS64(Logr::log_t log
)
1993 if (!::arg()["dns64-prefix"].empty()) {
1995 auto dns64Prefix
= Netmask(::arg()["dns64-prefix"]);
1996 if (dns64Prefix
.getBits() != 96) {
1997 SLOG(g_log
<< Logger::Error
<< "Invalid prefix for 'dns64-prefix', the current implementation only supports /96 prefixes: " << ::arg()["dns64-prefix"] << endl
,
1998 log
->info(Logr::Error
, "Invalid prefix for 'dns64-prefix', the current implementation only supports /96 prefixes", "prefix", Logging::Loggable(::arg()["dns64-prefix"])));
2001 g_dns64Prefix
= dns64Prefix
.getNetwork();
2002 g_dns64PrefixReverse
= reverseNameFromIP(*g_dns64Prefix
);
2003 /* /96 is 24 nibbles + 2 for "ip6.arpa." */
2004 while (g_dns64PrefixReverse
.countLabels() > 26) {
2005 g_dns64PrefixReverse
.chopOff();
2008 catch (const NetmaskException
& ne
) {
2009 SLOG(g_log
<< Logger::Error
<< "Invalid prefix '" << ::arg()["dns64-prefix"] << "' for 'dns64-prefix': " << ne
.reason
<< endl
,
2010 log
->info(Logr::Error
, "Invalid prefix", "dns64-prefix", Logging::Loggable(::arg()["dns64-prefix"])));
2017 static int serviceMain(Logr::log_t log
)
2019 g_log
.setName(g_programname
);
2020 g_log
.disableSyslog(::arg().mustDo("disable-syslog"));
2021 g_log
.setTimestamps(::arg().mustDo("log-timestamp"));
2022 g_regressionTestMode
= ::arg().mustDo("devonly-regression-test-mode");
2024 if (!::arg()["logging-facility"].empty()) {
2025 int val
= logFacilityToLOG(::arg().asNum("logging-facility"));
2027 g_log
.setFacility(val
);
2030 SLOG(g_log
<< Logger::Error
<< "Unknown logging facility " << ::arg().asNum("logging-facility") << endl
,
2031 log
->info(Logr::Error
, "Unknown logging facility", "facility", Logging::Loggable(::arg().asNum("logging-facility"))));
2035 g_disthashseed
= dns_random_uint32();
2037 int ret
= initNet(log
);
2041 // keep this ABOVE loadRecursorLuaConfig!
2042 ret
= initDNSSEC(log
);
2046 g_maxCacheEntries
= ::arg().asNum("max-cache-entries");
2048 luaConfigDelayedThreads delayedLuaThreads
;
2050 ProxyMapping proxyMapping
;
2051 loadRecursorLuaConfig(::arg()["lua-config-file"], delayedLuaThreads
, proxyMapping
);
2052 // Initial proxy mapping
2053 g_proxyMapping
= proxyMapping
.empty() ? nullptr : std::make_unique
<ProxyMapping
>(proxyMapping
);
2055 catch (PDNSException
& e
) {
2056 SLOG(g_log
<< Logger::Error
<< "Cannot load Lua configuration: " << e
.reason
<< endl
,
2057 log
->error(Logr::Error
, e
.reason
, "Cannot load Lua configuration"));
2062 initPublicSuffixList(::arg()["public-suffix-list-file"]);
2066 RecThreadInfo::setWeDistributeQueries(::arg().mustDo("pdns-distributes-queries"));
2067 if (RecThreadInfo::weDistributeQueries()) {
2068 SLOG(g_log
<< Logger::Warning
<< "PowerDNS Recursor itself will distribute queries over threads" << endl
,
2069 log
->info(Logr::Notice
, "PowerDNS Recursor itself will distribute queries over threads"));
2072 g_outgoingEDNSBufsize
= ::arg().asNum("edns-outgoing-bufsize");
2074 if (::arg()["trace"] == "fail") {
2075 SyncRes::setDefaultLogMode(SyncRes::Store
);
2077 else if (::arg().mustDo("trace")) {
2078 SyncRes::setDefaultLogMode(SyncRes::Log
);
2079 ::arg().set("quiet") = "no";
2083 ret
= initSyncRes(log
);
2088 g_proxyProtocolACL
.toMasks(::arg()["proxy-protocol-from"]);
2089 g_proxyProtocolMaximumSize
= ::arg().asNum("proxy-protocol-maximum-size");
2091 ret
= initDNS64(log
);
2095 g_networkTimeoutMsec
= ::arg().asNum("network-timeout");
2097 std::tie(g_initialDomainMap
, g_initialAllowNotifyFor
) = parseZoneConfiguration(g_yamlSettings
);
2099 g_latencyStatSize
= ::arg().asNum("latency-statistic-size");
2101 g_logCommonErrors
= ::arg().mustDo("log-common-errors");
2102 g_logRPZChanges
= ::arg().mustDo("log-rpz-changes");
2104 g_anyToTcp
= ::arg().mustDo("any-to-tcp");
2105 g_allowNoRD
= ::arg().mustDo("allow-no-rd");
2106 g_udpTruncationThreshold
= ::arg().asNum("udp-truncation-threshold");
2108 g_lowercaseOutgoing
= ::arg().mustDo("lowercase-outgoing");
2110 g_paddingFrom
.toMasks(::arg()["edns-padding-from"]);
2111 if (::arg()["edns-padding-mode"] == "always") {
2112 g_paddingMode
= PaddingMode::Always
;
2114 else if (::arg()["edns-padding-mode"] == "padded-queries-only") {
2115 g_paddingMode
= PaddingMode::PaddedQueries
;
2118 SLOG(g_log
<< Logger::Error
<< "Unknown edns-padding-mode: " << ::arg()["edns-padding-mode"] << endl
,
2119 log
->info(Logr::Error
, "Unknown edns-padding-mode", "edns-padding-mode", Logging::Loggable(::arg()["edns-padding-mode"])));
2122 g_paddingTag
= ::arg().asNum("edns-padding-tag");
2123 g_paddingOutgoing
= ::arg().mustDo("edns-padding-out");
2125 RecThreadInfo::setNumDistributorThreads(::arg().asNum("distributor-threads"));
2126 RecThreadInfo::setNumUDPWorkerThreads(::arg().asNum("threads"));
2127 if (RecThreadInfo::numUDPWorkers() < 1) {
2128 SLOG(g_log
<< Logger::Warning
<< "Asked to run with 0 threads, raising to 1 instead" << endl
,
2129 log
->info(Logr::Warning
, "Asked to run with 0 threads, raising to 1 instead"));
2130 RecThreadInfo::setNumUDPWorkerThreads(1);
2132 RecThreadInfo::setNumTCPWorkerThreads(::arg().asNum("tcp-threads"));
2133 if (RecThreadInfo::numTCPWorkers() < 1) {
2134 SLOG(g_log
<< Logger::Warning
<< "Asked to run with 0 TCP threads, raising to 1 instead" << endl
,
2135 log
->info(Logr::Warning
, "Asked to run with 0 TCP threads, raising to 1 instead"));
2136 RecThreadInfo::setNumTCPWorkerThreads(1);
2139 g_maxMThreads
= ::arg().asNum("max-mthreads");
2141 int64_t maxInFlight
= ::arg().asNum("max-concurrent-requests-per-tcp-connection");
2142 if (maxInFlight
< 1 || maxInFlight
> USHRT_MAX
|| maxInFlight
>= g_maxMThreads
) {
2143 SLOG(g_log
<< Logger::Warning
<< "Asked to run with illegal max-concurrent-requests-per-tcp-connection, setting to default (10)" << endl
,
2144 log
->info(Logr::Warning
, "Asked to run with illegal max-concurrent-requests-per-tcp-connection, setting to default (10)"));
2145 TCPConnection::s_maxInFlight
= 10;
2148 TCPConnection::s_maxInFlight
= maxInFlight
;
2151 int64_t millis
= ::arg().asNum("tcp-out-max-idle-ms");
2152 TCPOutConnectionManager::s_maxIdleTime
= timeval
{millis
/ 1000, (static_cast<suseconds_t
>(millis
) % 1000) * 1000};
2153 TCPOutConnectionManager::s_maxIdlePerAuth
= ::arg().asNum("tcp-out-max-idle-per-auth");
2154 TCPOutConnectionManager::s_maxQueries
= ::arg().asNum("tcp-out-max-queries");
2155 TCPOutConnectionManager::s_maxIdlePerThread
= ::arg().asNum("tcp-out-max-idle-per-thread");
2157 g_gettagNeedsEDNSOptions
= ::arg().mustDo("gettag-needs-edns-options");
2159 s_statisticsInterval
= ::arg().asNum("statistics-interval");
2161 SyncRes::s_addExtendedResolutionDNSErrors
= ::arg().mustDo("extended-resolution-errors");
2163 if (::arg().asNum("aggressive-nsec-cache-size") > 0) {
2164 if (g_dnssecmode
== DNSSECMode::ValidateAll
|| g_dnssecmode
== DNSSECMode::ValidateForLog
|| g_dnssecmode
== DNSSECMode::Process
) {
2165 g_aggressiveNSECCache
= make_unique
<AggressiveNSECCache
>(::arg().asNum("aggressive-nsec-cache-size"));
2168 SLOG(g_log
<< Logger::Warning
<< "Aggressive NSEC/NSEC3 caching is enabled but DNSSEC validation is not set to 'validate', 'log-fail' or 'process', ignoring" << endl
,
2169 log
->info(Logr::Warning
, "Aggressive NSEC/NSEC3 caching is enabled but DNSSEC validation is not set to 'validate', 'log-fail' or 'process', ignoring"));
2173 AggressiveNSECCache::s_maxNSEC3CommonPrefix
= static_cast<uint8_t>(std::round(std::log2(::arg().asNum("aggressive-cache-min-nsec3-hit-ratio"))));
2174 SLOG(g_log
<< Logger::Debug
<< "NSEC3 aggressive cache tuning: aggressive-cache-min-nsec3-hit-ratio: " << ::arg().asNum("aggressive-cache-min-nsec3-hit-ratio") << " max common prefix bits: " << std::to_string(AggressiveNSECCache::s_maxNSEC3CommonPrefix
) << endl
,
2175 log
->info(Logr::Debug
, "NSEC3 aggressive cache tuning", "aggressive-cache-min-nsec3-hit-ratio", Logging::Loggable(::arg().asNum("aggressive-cache-min-nsec3-hit-ratio")), "maxCommonPrefixBits", Logging::Loggable(AggressiveNSECCache::s_maxNSEC3CommonPrefix
)));
2177 initSuffixMatchNodes(log
);
2179 initDistribution(log
);
2182 // Setup newly observed domain globals
2184 #endif /* NOD_ENABLED */
2186 auto forks
= initForks(log
);
2190 #ifdef HAVE_LIBSODIUM
2191 if (sodium_init() == -1) {
2192 SLOG(g_log
<< Logger::Error
<< "Unable to initialize sodium crypto library" << endl
,
2193 log
->info(Logr::Error
, "Unable to initialize sodium crypto library"));
2198 openssl_thread_setup();
2202 if (!::arg()["setgid"].empty()) {
2203 newgid
= strToGID(::arg()["setgid"]);
2206 if (!::arg()["setuid"].empty()) {
2207 newuid
= strToUID(::arg()["setuid"]);
2210 Utility::dropGroupPrivs(newuid
, newgid
);
2212 ret
= initControl(log
, newuid
, forks
);
2217 startLuaConfigDelayedThreads(delayedLuaThreads
, g_luaconfs
.getCopy().generation
);
2218 delayedLuaThreads
.rpzPrimaryThreads
.clear(); // no longer needed
2220 RecThreadInfo::makeThreadPipes(log
);
2222 g_tcpTimeout
= ::arg().asNum("client-tcp-timeout");
2223 g_maxTCPPerClient
= ::arg().asNum("max-tcp-per-client");
2224 g_tcpMaxQueriesPerConn
= ::arg().asNum("max-tcp-queries-per-connection");
2225 g_maxUDPQueriesPerRound
= ::arg().asNum("max-udp-queries-per-round");
2227 g_useKernelTimestamp
= ::arg().mustDo("protobuf-use-kernel-timestamp");
2229 disableStats(StatComponent::API
, ::arg()["stats-api-blacklist"]);
2230 disableStats(StatComponent::Carbon
, ::arg()["stats-carbon-blacklist"]);
2231 disableStats(StatComponent::RecControl
, ::arg()["stats-rec-control-blacklist"]);
2232 disableStats(StatComponent::SNMP
, ::arg()["stats-snmp-blacklist"]);
2234 disableStats(StatComponent::API
, ::arg()["stats-api-disabled-list"]);
2235 disableStats(StatComponent::Carbon
, ::arg()["stats-carbon-disabled-list"]);
2236 disableStats(StatComponent::RecControl
, ::arg()["stats-rec-control-disabled-list"]);
2237 disableStats(StatComponent::SNMP
, ::arg()["stats-snmp-disabled-list"]);
2239 // Run before any thread doing stats related things
2244 ret
= initPorts(log
);
2249 return RecThreadInfo::runThreads(log
);
2252 static void handlePipeRequest(int fileDesc
, FDMultiplexer::funcparam_t
& /* var */)
2254 ThreadMSG
* tmsg
= nullptr;
2256 if (read(fileDesc
, &tmsg
, sizeof(tmsg
)) != sizeof(tmsg
)) { // fd == readToThread || fd == readQueriesToThread NOLINT: sizeof correct
2257 unixDie("read from thread pipe returned wrong size or error");
2260 void* resp
= nullptr;
2262 resp
= tmsg
->func();
2264 catch (std::exception
& e
) {
2265 if (g_logCommonErrors
) {
2266 SLOG(g_log
<< Logger::Error
<< "PIPE function we executed created exception: " << e
.what() << endl
, // but what if they wanted an answer.. we send 0
2267 g_slog
->withName("runtime")->error(Logr::Error
, e
.what(), "PIPE function we executed created exception", "exception", Logging::Loggable("std::exception")));
2270 catch (PDNSException
& e
) {
2271 if (g_logCommonErrors
) {
2272 SLOG(g_log
<< Logger::Error
<< "PIPE function we executed created PDNS exception: " << e
.reason
<< endl
, // but what if they wanted an answer.. we send 0
2273 g_slog
->withName("runtime")->error(Logr::Error
, e
.reason
, "PIPE function we executed created exception", "exception", Logging::Loggable("PDNSException")));
2276 if (tmsg
->wantAnswer
) {
2277 if (write(RecThreadInfo::self().getPipes().writeFromThread
, &resp
, sizeof(resp
)) != sizeof(resp
)) {
2278 delete tmsg
; // NOLINT: manual ownership handling
2279 unixDie("write to thread pipe returned wrong size or error");
2283 delete tmsg
; // NOLINT: manual ownership handling
2286 static void handleRCC(int fileDesc
, FDMultiplexer::funcparam_t
& /* var */)
2288 auto log
= g_slog
->withName("control");
2290 FDWrapper clientfd
= accept(fileDesc
, nullptr, nullptr);
2291 if (clientfd
== -1) {
2292 throw PDNSException("accept failed");
2294 string msg
= g_rcc
.recv(clientfd
).d_str
;
2295 SLOG(g_log
<< Logger::Info
<< "Received rec_control command '" << msg
<< "' via controlsocket" << endl
,
2296 log
->info(Logr::Info
, "Received rec_control command via control socket", "command", Logging::Loggable(msg
)));
2298 RecursorControlParser::func_t
* command
= nullptr;
2299 auto answer
= RecursorControlParser::getAnswer(clientfd
, msg
, &command
);
2301 g_rcc
.send(clientfd
, answer
);
2304 catch (const std::exception
& e
) {
2305 SLOG(g_log
<< Logger::Error
<< "Error dealing with control socket request: " << e
.what() << endl
,
2306 log
->error(Logr::Error
, e
.what(), "Exception while dealing with control socket request", "exception", Logging::Loggable("std::exception")));
2308 catch (const PDNSException
& ae
) {
2309 SLOG(g_log
<< Logger::Error
<< "Error dealing with control socket request: " << ae
.reason
<< endl
,
2310 log
->error(Logr::Error
, ae
.reason
, "Exception while dealing with control socket request", "exception", Logging::Loggable("PDNSException")));
2317 PeriodicTask(const string
& aName
, time_t aTime
) :
2318 period
{aTime
, 0}, name(aName
)
2321 throw PDNSException("Invalid period of periodic task " + aName
);
2325 void runIfDue(struct timeval
& now
, const std::function
<void()>& function
)
2327 if (last_run
< now
- period
) {
2329 Utility::gettimeofday(&last_run
);
2334 [[nodiscard
]] time_t getPeriod() const
2336 return period
.tv_sec
;
2339 void setPeriod(time_t newperiod
)
2341 period
.tv_sec
= newperiod
;
2344 void updateLastRun()
2346 Utility::gettimeofday(&last_run
);
2349 [[nodiscard
]] bool hasRun() const
2351 return last_run
.tv_sec
!= 0 || last_run
.tv_usec
!= 0;
2355 struct timeval last_run
2359 struct timeval period
;
2363 static void houseKeepingWork(Logr::log_t log
)
2368 Utility::gettimeofday(&now
);
2369 t_Counters
.updateSnap(now
, g_regressionTestMode
);
2371 // Below are the tasks that run for every recursorThread, including handler and taskThread
2373 static thread_local PeriodicTask pruneTCPTask
{"pruneTCPTask", 5};
2374 pruneTCPTask
.runIfDue(now
, [now
]() {
2375 t_tcp_manager
.cleanup(now
);
2378 const auto& info
= RecThreadInfo::self();
2380 // Threads handling packets process config changes in the input path, but not all threads process input packets
2381 // distr threads only process TCP, so that may not happenn very often. So do all periodically.
2382 static thread_local PeriodicTask exportConfigTask
{"exportConfigTask", 30};
2383 auto luaconfsLocal
= g_luaconfs
.getLocal();
2384 exportConfigTask
.runIfDue(now
, [&luaconfsLocal
]() {
2385 checkProtobufExport(luaconfsLocal
);
2386 checkOutgoingProtobufExport(luaconfsLocal
);
2388 checkFrameStreamExport(luaconfsLocal
, luaconfsLocal
->frameStreamExportConfig
, t_frameStreamServersInfo
);
2389 checkFrameStreamExport(luaconfsLocal
, luaconfsLocal
->nodFrameStreamExportConfig
, t_nodFrameStreamServersInfo
);
2393 // Below are the thread specific tasks for the handler and the taskThread
2394 // Likley a few handler tasks could be moved to the taskThread
2395 if (info
.isTaskThread()) {
2396 // TaskQueue is run always
2397 runTasks(10, g_logCommonErrors
);
2399 static PeriodicTask ztcTask
{"ZTC", 60};
2400 static map
<DNSName
, RecZoneToCache::State
> ztcStates
;
2401 ztcTask
.runIfDue(now
, [&luaconfsLocal
]() {
2402 RecZoneToCache::maintainStates(luaconfsLocal
->ztcConfigs
, ztcStates
, luaconfsLocal
->generation
);
2403 for (const auto& ztc
: luaconfsLocal
->ztcConfigs
) {
2404 RecZoneToCache::ZoneToCache(ztc
.second
, ztcStates
.at(ztc
.first
));
2408 else if (info
.isHandler()) {
2409 if (g_packetCache
) {
2410 static PeriodicTask packetCacheTask
{"packetCacheTask", 5};
2411 packetCacheTask
.runIfDue(now
, [now
]() {
2412 g_packetCache
->doPruneTo(now
.tv_sec
, g_maxPacketCacheEntries
);
2415 static PeriodicTask recordCachePruneTask
{"RecordCachePruneTask", 5};
2416 recordCachePruneTask
.runIfDue(now
, [now
]() {
2417 g_recCache
->doPrune(now
.tv_sec
, g_maxCacheEntries
);
2420 static PeriodicTask negCachePruneTask
{"NegCachePrunteTask", 5};
2421 negCachePruneTask
.runIfDue(now
, [now
]() {
2422 g_negCache
->prune(now
.tv_sec
, g_maxCacheEntries
/ 8);
2425 static PeriodicTask aggrNSECPruneTask
{"AggrNSECPruneTask", 5};
2426 aggrNSECPruneTask
.runIfDue(now
, [now
]() {
2427 if (g_aggressiveNSECCache
) {
2428 g_aggressiveNSECCache
->prune(now
.tv_sec
);
2432 static PeriodicTask pruneNSpeedTask
{"pruneNSSpeedTask", 30};
2433 pruneNSpeedTask
.runIfDue(now
, [now
]() {
2434 SyncRes::pruneNSSpeeds(now
.tv_sec
- 300);
2437 static PeriodicTask pruneEDNSTask
{"pruneEDNSTask", 60};
2438 pruneEDNSTask
.runIfDue(now
, [now
]() {
2439 SyncRes::pruneEDNSStatuses(now
.tv_sec
);
2442 if (SyncRes::s_max_busy_dot_probes
> 0) {
2443 static PeriodicTask pruneDoTProbeMap
{"pruneDoTProbeMapTask", 60};
2444 pruneDoTProbeMap
.runIfDue(now
, [now
]() {
2445 SyncRes::pruneDoTProbeMap(now
.tv_sec
);
2449 static PeriodicTask pruneThrottledTask
{"pruneThrottledTask", 5};
2450 pruneThrottledTask
.runIfDue(now
, [now
]() {
2451 SyncRes::pruneThrottledServers(now
.tv_sec
);
2454 static PeriodicTask pruneFailedServersTask
{"pruneFailedServerTask", 5};
2455 pruneFailedServersTask
.runIfDue(now
, [now
]() {
2456 SyncRes::pruneFailedServers(now
.tv_sec
- static_cast<time_t>(SyncRes::s_serverdownthrottletime
* 10));
2459 static PeriodicTask pruneNonResolvingTask
{"pruneNonResolvingTask", 5};
2460 pruneNonResolvingTask
.runIfDue(now
, [now
]() {
2461 SyncRes::pruneNonResolving(now
.tv_sec
- SyncRes::s_nonresolvingnsthrottletime
);
2464 static PeriodicTask pruneSaveParentSetTask
{"pruneSaveParentSetTask", 60};
2465 pruneSaveParentSetTask
.runIfDue(now
, [now
]() {
2466 SyncRes::pruneSaveParentsNSSets(now
.tv_sec
);
2469 // By default, refresh at 80% of max-cache-ttl with a minimum period of 10s
2470 const unsigned int minRootRefreshInterval
= 10;
2471 static PeriodicTask rootUpdateTask
{"rootUpdateTask", std::max(SyncRes::s_maxcachettl
* 8 / 10, minRootRefreshInterval
)};
2472 rootUpdateTask
.runIfDue(now
, [now
, &log
, minRootRefreshInterval
]() {
2474 if (!g_regressionTestMode
) {
2475 res
= SyncRes::getRootNS(now
, nullptr, 0, log
);
2478 // Success, go back to the defaut period
2479 rootUpdateTask
.setPeriod(std::max(SyncRes::s_maxcachettl
* 8 / 10, minRootRefreshInterval
));
2482 // On failure, go to the middle of the remaining period (initially 80% / 8 = 10%) and shorten the interval on each
2483 // failure by dividing the existing interval by 8, keeping the minimum interval at 10s.
2484 // So with a 1 day period and failures we'll see a refresh attempt at 69120, 69120+11520, 69120+11520+1440, ...
2485 rootUpdateTask
.setPeriod(std::max
<time_t>(rootUpdateTask
.getPeriod() / 8, minRootRefreshInterval
));
2489 static PeriodicTask secpollTask
{"secpollTask", 3600};
2490 static time_t t_last_secpoll
;
2491 secpollTask
.runIfDue(now
, [&log
]() {
2493 doSecPoll(&t_last_secpoll
, log
);
2495 catch (const std::exception
& e
) {
2496 SLOG(g_log
<< Logger::Error
<< "Exception while performing security poll: " << e
.what() << endl
,
2497 log
->error(Logr::Error
, e
.what(), "Exception while performing security poll"));
2499 catch (const PDNSException
& e
) {
2500 SLOG(g_log
<< Logger::Error
<< "Exception while performing security poll: " << e
.reason
<< endl
,
2501 log
->error(Logr::Error
, e
.reason
, "Exception while performing security poll"));
2503 catch (const ImmediateServFailException
& e
) {
2504 SLOG(g_log
<< Logger::Error
<< "Exception while performing security poll: " << e
.reason
<< endl
,
2505 log
->error(Logr::Error
, e
.reason
, "Exception while performing security poll"));
2507 catch (const PolicyHitException
& e
) {
2508 SLOG(g_log
<< Logger::Error
<< "Policy hit while performing security poll" << endl
,
2509 log
->info(Logr::Error
, "Policy hit while performing security poll"));
2512 SLOG(g_log
<< Logger::Error
<< "Exception while performing security poll" << endl
,
2513 log
->info(Logr::Error
, "Exception while performing security poll"));
2517 const time_t taInterval
= std::max(1, static_cast<int>(luaconfsLocal
->trustAnchorFileInfo
.interval
) * 3600);
2518 static PeriodicTask trustAnchorTask
{"trustAnchorTask", taInterval
};
2519 if (!trustAnchorTask
.hasRun()) {
2520 // Loading the Lua config file already "refreshed" the TAs
2521 trustAnchorTask
.updateLastRun();
2523 // interval might have ben updated
2524 trustAnchorTask
.setPeriod(taInterval
);
2525 trustAnchorTask
.runIfDue(now
, [&luaconfsLocal
, &log
]() {
2526 if (!luaconfsLocal
->trustAnchorFileInfo
.fname
.empty() && luaconfsLocal
->trustAnchorFileInfo
.interval
!= 0) {
2527 SLOG(g_log
<< Logger::Debug
<< "Refreshing Trust Anchors from file" << endl
,
2528 log
->info(Logr::Debug
, "Refreshing Trust Anchors from file"));
2530 map
<DNSName
, dsmap_t
> dsAnchors
;
2531 if (updateTrustAnchorsFromFile(luaconfsLocal
->trustAnchorFileInfo
.fname
, dsAnchors
, log
)) {
2532 g_luaconfs
.modify([&dsAnchors
](LuaConfigItems
& lci
) {
2533 lci
.dsAnchors
= dsAnchors
;
2537 catch (const PDNSException
& pe
) {
2538 SLOG(g_log
<< Logger::Error
<< "Unable to update Trust Anchors: " << pe
.reason
<< endl
,
2539 log
->error(Logr::Error
, pe
.reason
, "Unable to update Trust Anchors"));
2544 t_Counters
.updateSnap(g_regressionTestMode
);
2547 static void houseKeeping(void* /* ignored */)
2549 auto log
= g_slog
->withName("housekeeping");
2550 static thread_local
bool t_running
; // houseKeeping can get suspended in secpoll, and be restarted, which makes us do duplicate work
2557 houseKeepingWork(log
);
2560 catch (const PDNSException
& ae
) {
2562 SLOG(g_log
<< Logger::Error
<< "Fatal error in housekeeping thread: " << ae
.reason
<< endl
,
2563 log
->error(Logr::Error
, ae
.reason
, "Fatal error in housekeeping thread"));
2568 SLOG(g_log
<< Logger::Error
<< "Uncaught exception in housekeeping thread" << endl
,
2569 log
->info(Logr::Error
, "Uncaught exception in housekeeping thread"));
2574 static void runLuaMaintenance(RecThreadInfo
& threadInfo
, time_t& last_lua_maintenance
, time_t luaMaintenanceInterval
)
2576 if (t_pdl
!= nullptr) {
2577 // lua-dns-script directive is present, call the maintenance callback if needed
2578 if (threadInfo
.isWorker()) { // either UDP of TCP worker
2579 // Only on threads processing queries
2580 if (g_now
.tv_sec
- last_lua_maintenance
>= luaMaintenanceInterval
) {
2581 struct timeval start
2584 Utility::gettimeofday(&start
);
2585 t_pdl
->maintenance();
2586 last_lua_maintenance
= g_now
.tv_sec
;
2590 Utility::gettimeofday(&stop
);
2591 t_Counters
.at(rec::Counter::maintenanceUsec
) += uSec(stop
- start
);
2592 ++t_Counters
.at(rec::Counter::maintenanceCalls
);
2598 static void runTCPMaintenance(RecThreadInfo
& threadInfo
, bool& listenOnTCP
, unsigned int maxTcpClients
)
2600 if (threadInfo
.isTCPListener()) {
2602 if (TCPConnection::getCurrentConnections() > maxTcpClients
) { // shutdown, too many connections
2603 for (const auto fileDesc
: threadInfo
.getTCPSockets()) {
2604 t_fdm
->removeReadFD(fileDesc
);
2606 listenOnTCP
= false;
2610 if (TCPConnection::getCurrentConnections() <= maxTcpClients
) { // reenable
2611 for (const auto fileDesc
: threadInfo
.getTCPSockets()) {
2612 t_fdm
->addReadFD(fileDesc
, handleNewTCPQuestion
);
2620 static void recLoop()
2622 unsigned int maxTcpClients
= ::arg().asNum("max-tcp-clients");
2623 bool listenOnTCP
{true};
2624 time_t last_stat
= 0;
2625 time_t last_carbon
= 0;
2626 time_t last_lua_maintenance
= 0;
2627 time_t carbonInterval
= ::arg().asNum("carbon-interval");
2628 time_t luaMaintenanceInterval
= ::arg().asNum("lua-maintenance-interval");
2630 auto& threadInfo
= RecThreadInfo::self();
2632 while (!RecursorControlChannel::stop
) {
2633 while (g_multiTasker
->schedule(g_now
)) {
2634 ; // MTasker letting the mthreads do their thing
2637 // Use primes, it avoid not being scheduled in cases where the counter has a regular pattern.
2638 // We want to call handler thread often, it gets scheduled about 2 times per second
2639 if (((threadInfo
.isHandler() || threadInfo
.isTaskThread()) && s_counter
% 11 == 0) || s_counter
% 499 == 0) {
2640 struct timeval start
2643 Utility::gettimeofday(&start
);
2644 g_multiTasker
->makeThread(houseKeeping
, nullptr);
2645 if (!threadInfo
.isTaskThread()) {
2649 Utility::gettimeofday(&stop
);
2650 t_Counters
.at(rec::Counter::maintenanceUsec
) += uSec(stop
- start
);
2651 ++t_Counters
.at(rec::Counter::maintenanceCalls
);
2655 if (s_counter
% 55 == 0) {
2656 auto expired
= t_fdm
->getTimeouts(g_now
);
2658 for (const auto& exp
: expired
) {
2659 auto conn
= boost::any_cast
<shared_ptr
<TCPConnection
>>(exp
.second
);
2660 if (g_logCommonErrors
) {
2661 SLOG(g_log
<< Logger::Warning
<< "Timeout from remote TCP client " << conn
->d_remote
.toStringWithPort() << endl
,
2662 g_slogtcpin
->info(Logr::Warning
, "Timeout from remote TCP client", "remote", Logging::Loggable(conn
->d_remote
)));
2664 t_fdm
->removeReadFD(exp
.first
);
2670 if (threadInfo
.isHandler()) {
2671 if (statsWanted
|| (s_statisticsInterval
> 0 && (g_now
.tv_sec
- last_stat
) >= s_statisticsInterval
)) {
2673 last_stat
= g_now
.tv_sec
;
2676 Utility::gettimeofday(&g_now
, nullptr);
2678 if ((g_now
.tv_sec
- last_carbon
) >= carbonInterval
) {
2679 g_multiTasker
->makeThread(doCarbonDump
, nullptr);
2680 last_carbon
= g_now
.tv_sec
;
2683 runLuaMaintenance(threadInfo
, last_lua_maintenance
, luaMaintenanceInterval
);
2686 // 'run' updates g_now for us
2688 runTCPMaintenance(threadInfo
, listenOnTCP
, maxTcpClients
);
2692 static void recursorThread()
2694 auto log
= g_slog
->withName("runtime");
2695 t_Counters
.updateSnap(true);
2697 auto& threadInfo
= RecThreadInfo::self();
2699 SyncRes
tmp(g_now
); // make sure it allocates tsstorage before we do anything, like primeHints or so..
2700 SyncRes::setDomainMap(g_initialDomainMap
);
2701 t_allowFrom
= g_initialAllowFrom
;
2702 t_allowNotifyFrom
= g_initialAllowNotifyFrom
;
2703 t_allowNotifyFor
= g_initialAllowNotifyFor
;
2704 t_udpclientsocks
= std::make_unique
<UDPClientSocks
>();
2705 t_tcpClientCounts
= std::make_unique
<tcpClientCounts_t
>();
2706 if (g_proxyMapping
) {
2707 t_proxyMapping
= make_unique
<ProxyMapping
>(*g_proxyMapping
);
2710 t_proxyMapping
= nullptr;
2713 if (threadInfo
.isHandler()) {
2714 if (!primeHints()) {
2715 threadInfo
.setExitCode(EXIT_FAILURE
);
2716 RecursorControlChannel::stop
= true;
2717 SLOG(g_log
<< Logger::Critical
<< "Priming cache failed, stopping" << endl
,
2718 log
->info(Logr::Critical
, "Priming cache failed, stopping"));
2720 SLOG(g_log
<< Logger::Debug
<< "Done priming cache with root hints" << endl
,
2721 log
->info(Logr::Debug
, "Done priming cache with root hints"));
2726 if (threadInfo
.isWorker()) {
2727 setupNODThread(log
);
2729 #endif /* NOD_ENABLED */
2731 /* the listener threads handle TCP queries */
2732 if (threadInfo
.isWorker() || threadInfo
.isListener()) {
2734 if (!::arg()["lua-dns-script"].empty()) {
2735 t_pdl
= std::make_shared
<RecursorLua4
>();
2736 t_pdl
->loadFile(::arg()["lua-dns-script"]);
2737 SLOG(g_log
<< Logger::Warning
<< "Loaded 'lua' script from '" << ::arg()["lua-dns-script"] << "'" << endl
,
2738 log
->info(Logr::Warning
, "Loading Lua script from file", "name", Logging::Loggable(::arg()["lua-dns-script"])));
2741 catch (std::exception
& e
) {
2742 SLOG(g_log
<< Logger::Error
<< "Failed to load 'lua' script from '" << ::arg()["lua-dns-script"] << "': " << e
.what() << endl
,
2743 log
->error(Logr::Error
, e
.what(), "Failed to load Lua script from file", "name", Logging::Loggable(::arg()["lua-dns-script"])));
2748 unsigned int ringsize
= ::arg().asNum("stats-ringbuffer-entries") / RecThreadInfo::numUDPWorkers();
2749 if (ringsize
!= 0) {
2750 t_remotes
= std::make_unique
<addrringbuf_t
>();
2751 if (RecThreadInfo::weDistributeQueries()) {
2752 t_remotes
->set_capacity(::arg().asNum("stats-ringbuffer-entries") / RecThreadInfo::numDistributors());
2755 t_remotes
->set_capacity(ringsize
);
2757 t_servfailremotes
= std::make_unique
<addrringbuf_t
>();
2758 t_servfailremotes
->set_capacity(ringsize
);
2759 t_bogusremotes
= std::make_unique
<addrringbuf_t
>();
2760 t_bogusremotes
->set_capacity(ringsize
);
2761 t_largeanswerremotes
= std::make_unique
<addrringbuf_t
>();
2762 t_largeanswerremotes
->set_capacity(ringsize
);
2763 t_timeouts
= std::make_unique
<addrringbuf_t
>();
2764 t_timeouts
->set_capacity(ringsize
);
2766 t_queryring
= std::make_unique
<boost::circular_buffer
<pair
<DNSName
, uint16_t>>>();
2767 t_queryring
->set_capacity(ringsize
);
2768 t_servfailqueryring
= std::make_unique
<boost::circular_buffer
<pair
<DNSName
, uint16_t>>>();
2769 t_servfailqueryring
->set_capacity(ringsize
);
2770 t_bogusqueryring
= std::make_unique
<boost::circular_buffer
<pair
<DNSName
, uint16_t>>>();
2771 t_bogusqueryring
->set_capacity(ringsize
);
2773 g_multiTasker
= std::make_unique
<MT_t
>(::arg().asNum("stack-size"), ::arg().asNum("stack-cache-size"));
2774 threadInfo
.setMT(g_multiTasker
.get());
2776 /* start protobuf export threads if needed */
2777 auto luaconfsLocal
= g_luaconfs
.getLocal();
2778 checkProtobufExport(luaconfsLocal
);
2779 checkOutgoingProtobufExport(luaconfsLocal
);
2781 checkFrameStreamExport(luaconfsLocal
, luaconfsLocal
->frameStreamExportConfig
, t_frameStreamServersInfo
);
2782 checkFrameStreamExport(luaconfsLocal
, luaconfsLocal
->nodFrameStreamExportConfig
, t_nodFrameStreamServersInfo
);
2785 t_fdm
= unique_ptr
<FDMultiplexer
>(getMultiplexer(log
));
2787 std::unique_ptr
<RecursorWebServer
> rws
;
2789 t_fdm
->addReadFD(threadInfo
.getPipes().readToThread
, handlePipeRequest
);
2791 if (threadInfo
.isHandler()) {
2792 if (::arg().mustDo("webserver")) {
2793 SLOG(g_log
<< Logger::Warning
<< "Enabling web server" << endl
,
2794 log
->info(Logr::Info
, "Enabling web server"));
2796 rws
= make_unique
<RecursorWebServer
>(t_fdm
.get());
2798 catch (const PDNSException
& e
) {
2799 SLOG(g_log
<< Logger::Error
<< "Unable to start the internal web server: " << e
.reason
<< endl
,
2800 log
->error(Logr::Critical
, e
.reason
, "Exception while starting internal web server"));
2804 SLOG(g_log
<< Logger::Info
<< "Enabled '" << t_fdm
->getName() << "' multiplexer" << endl
,
2805 log
->info(Logr::Info
, "Enabled multiplexer", "name", Logging::Loggable(t_fdm
->getName())));
2808 t_fdm
->addReadFD(threadInfo
.getPipes().readQueriesToThread
, handlePipeRequest
);
2810 if (threadInfo
.isListener()) {
2812 /* then every listener has its own FDs */
2813 for (const auto& deferred
: threadInfo
.getDeferredAdds()) {
2814 t_fdm
->addReadFD(deferred
.first
, deferred
.second
);
2818 /* otherwise all listeners are listening on the same ones */
2819 for (const auto& deferred
: threadInfo
.isTCPListener() ? s_deferredTCPadds
: s_deferredUDPadds
) {
2820 t_fdm
->addReadFD(deferred
.first
, deferred
.second
);
2826 if (threadInfo
.isHandler()) {
2827 t_fdm
->addReadFD(g_rcc
.d_fd
, handleRCC
); // control channel
2831 if (threadInfo
.isHandler()) {
2832 // There is a race, as some threads might not be ready yet to do work.
2833 // To solve that, threads should notify RecThreadInfo they are done initializing.
2834 // But we lack a mechanism for that at this point in time.
2835 sd_notify(0, "READY=1");
2841 catch (PDNSException
& ae
) {
2842 SLOG(g_log
<< Logger::Error
<< "Exception: " << ae
.reason
<< endl
,
2843 log
->error(Logr::Error
, ae
.reason
, "Exception in RecursorThread", "exception", Logging::Loggable("PDNSException")));
2845 catch (std::exception
& e
) {
2846 SLOG(g_log
<< Logger::Error
<< "STL Exception: " << e
.what() << endl
,
2847 log
->error(Logr::Error
, e
.what(), "Exception in RecursorThread", "exception", Logging::Loggable("std::exception")));
2850 SLOG(g_log
<< Logger::Error
<< "any other exception in main: " << endl
,
2851 log
->info(Logr::Error
, "Exception in RecursorThread"));
2855 static pair
<int, bool> doYamlConfig(Logr::log_t
/* startupLog */, int argc
, char* argv
[]) // NOLINT: Posix API
2857 if (!::arg().mustDo("config")) {
2860 const string config
= ::arg()["config"];
2861 if (config
== "diff" || config
.empty()) {
2862 ::arg().parse(argc
, argv
);
2863 pdns::rust::settings::rec::Recursorsettings settings
;
2864 pdns::settings::rec::oldStyleSettingsToBridgeStruct(settings
);
2865 auto yaml
= settings
.to_yaml_string();
2866 cout
<< yaml
<< endl
;
2868 else if (config
== "default") {
2869 auto yaml
= pdns::settings::rec::defaultsToYaml();
2870 cout
<< yaml
<< endl
;
2875 static pair
<int, bool> doConfig(Logr::log_t startupLog
, const string
& configname
, int argc
, char* argv
[]) // NOLINT: Posix API
2877 if (::arg().mustDo("config")) {
2878 string config
= ::arg()["config"];
2879 if (config
== "check") {
2881 if (!::arg().file(configname
)) {
2882 SLOG(g_log
<< Logger::Warning
<< "Unable to open configuration file '" << configname
<< "'" << endl
,
2883 startupLog
->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname
)));
2886 ::arg().parse(argc
, argv
);
2889 catch (const ArgException
& argException
) {
2890 SLOG(g_log
<< Logger::Warning
<< "Unable to parse configuration file '" << configname
<< "': " << argException
.reason
<< endl
,
2891 startupLog
->error("Cannot parse configuration", "Unable to parse configuration file", "config_file", Logging::Loggable(configname
), "reason", Logging::Loggable(argException
.reason
)));
2895 else if (config
== "default" || config
.empty()) {
2896 cout
<< ::arg().configstring(false, true);
2898 else if (config
== "diff") {
2899 if (!::arg().laxFile(configname
)) {
2900 SLOG(g_log
<< Logger::Warning
<< "Unable to open configuration file '" << configname
<< "'" << endl
,
2901 startupLog
->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname
)));
2904 ::arg().laxParse(argc
, argv
);
2905 cout
<< ::arg().configstring(true, false);
2908 if (!::arg().laxFile(configname
)) {
2909 SLOG(g_log
<< Logger::Warning
<< "Unable to open configuration file '" << configname
<< "'" << endl
,
2910 startupLog
->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname
)));
2913 ::arg().laxParse(argc
, argv
);
2914 cout
<< ::arg().configstring(true, true);
2921 static void handleRuntimeDefaults(Logr::log_t log
)
2923 #if HAVE_FIBER_SANITIZER
2924 // Asan needs more stack
2925 if (::arg().asNum("stack-size") == 200000) { // the default in table.py
2926 ::arg().set("stack-size", "stack size per mthread") = "600000";
2930 const string RUNTIME
= "*runtime determined*";
2931 if (::arg()["version-string"] == RUNTIME
) { // i.e. not set explicitly
2932 ::arg().set("version-string") = fullVersionString();
2935 if (::arg()["server-id"] == RUNTIME
) { // i.e. not set explicitly
2936 auto myHostname
= getHostname();
2937 if (!myHostname
.has_value()) {
2938 SLOG(g_log
<< Logger::Warning
<< "Unable to get the hostname, NSID and id.server values will be empty" << endl
,
2939 log
->info(Logr::Warning
, "Unable to get the hostname, NSID and id.server values will be empty"));
2941 ::arg().set("server-id") = myHostname
.has_value() ? *myHostname
: "";
2944 if (::arg()["socket-dir"].empty()) {
2945 auto* runtimeDir
= getenv("RUNTIME_DIRECTORY"); // NOLINT(concurrency-mt-unsafe,cppcoreguidelines-pro-type-vararg)
2946 if (runtimeDir
!= nullptr) {
2947 ::arg().set("socket-dir") = runtimeDir
;
2951 if (::arg()["socket-dir"].empty()) {
2952 if (::arg()["chroot"].empty()) {
2953 ::arg().set("socket-dir") = std::string(LOCALSTATEDIR
) + "/pdns-recursor";
2956 ::arg().set("socket-dir") = "/";
2960 if (::arg().asNum("threads") == 1) {
2961 if (::arg().mustDo("pdns-distributes-queries")) {
2962 SLOG(g_log
<< Logger::Warning
<< "Only one thread, no need to distribute queries ourselves" << endl
,
2963 log
->info(Logr::Warning
, "Only one thread, no need to distribute queries ourselves"));
2964 ::arg().set("pdns-distributes-queries") = "no";
2968 if (::arg().mustDo("pdns-distributes-queries") && ::arg().asNum("distributor-threads") == 0) {
2969 SLOG(g_log
<< Logger::Warning
<< "Asked to run with pdns-distributes-queries set but no distributor threads, raising to 1" << endl
,
2970 log
->info(Logr::Warning
, "Asked to run with pdns-distributes-queries set but no distributor threads, raising to 1"));
2971 ::arg().set("distributor-threads") = "1";
2974 if (!::arg().mustDo("pdns-distributes-queries") && ::arg().asNum("distributor-threads") > 0) {
2975 SLOG(g_log
<< Logger::Warning
<< "Not distributing queries, setting distributor threads to 0" << endl
,
2976 log
->info(Logr::Warning
, "Not distributing queries, setting distributor threads to 0"));
2977 ::arg().set("distributor-threads") = "0";
2981 int main(int argc
, char** argv
)
2985 versionSetProduct(ProductRecursor
);
2989 int ret
= EXIT_SUCCESS
;
2992 pdns::settings::rec::defineOldStyleSettings();
2993 ::arg().setDefaults();
2994 g_log
.toConsole(Logger::Info
);
2995 ::arg().laxParse(argc
, argv
); // do a lax parse
2997 if (::arg().mustDo("version")) {
2998 showProductVersion();
2999 showBuildConfiguration();
3002 if (::arg().mustDo("help")) {
3003 cout
<< "syntax:" << endl
3005 cout
<< ::arg().helpstring(::arg()["help"]) << endl
;
3009 // Pick up options given on command line to setup logging asap.
3010 g_quiet
= ::arg().mustDo("quiet");
3011 s_logUrgency
= (Logger::Urgency
)::arg().asNum("loglevel");
3012 g_slogStructured
= ::arg().mustDo("structured-logging");
3013 s_structured_logger_backend
= ::arg()["structured-logging-backend"];
3015 if (!g_quiet
&& s_logUrgency
< Logger::Info
) { // Logger::Info=6, Logger::Debug=7
3016 s_logUrgency
= Logger::Info
; // if you do --quiet=no, you need Info to also see the query log
3018 g_log
.setLoglevel(s_logUrgency
);
3019 g_log
.toConsole(s_logUrgency
);
3020 showProductVersion();
3021 if (!g_slogStructured
) {
3022 g_log
<< Logger::Warning
<< "Disabling structured logging is deprecated, old-style logging wil be removed in a future release" << endl
;
3025 g_yamlSettings
= false;
3026 string configname
= ::arg()["config-dir"] + "/recursor";
3027 if (!::arg()["config-name"].empty()) {
3028 configname
= ::arg()["config-dir"] + "/recursor-" + ::arg()["config-name"];
3029 g_programname
+= "-" + ::arg()["config-name"];
3031 cleanSlashes(configname
);
3033 if (!::arg().getCommands().empty()) {
3034 cerr
<< "Fatal: non-option";
3035 if (::arg().getCommands().size() > 1) {
3040 for (const auto& command
: ::arg().getCommands()) {
3047 cerr
<< ") on the command line, perhaps a '--setting=123' statement missed the '='?" << endl
;
3051 if (s_structured_logger_backend
== "systemd-journal") {
3053 if (int fd
= sd_journal_stream_fd("pdns-recusor", LOG_DEBUG
, 0); fd
>= 0) {
3054 g_slog
= Logging::Logger::create(loggerSDBackend
);
3058 if (g_slog
== nullptr) {
3059 cerr
<< "Structured logging to systemd-journal requested but it is not available" << endl
;
3063 if (g_slog
== nullptr) {
3064 g_slog
= Logging::Logger::create(loggerBackend
);
3067 // Missing: a mechanism to call setVerbosity(x)
3068 auto startupLog
= g_slog
->withName("config");
3069 g_slogtcpin
= g_slog
->withName("in")->withValues("proto", Logging::Loggable("tcp"));
3070 g_slogudpin
= g_slog
->withName("in")->withValues("proto", Logging::Loggable("udp"));
3071 g_slogout
= g_slog
->withName("out");
3073 ::arg().setSLog(startupLog
);
3075 const string yamlconfigname
= configname
+ ".yml";
3077 pdns::rust::settings::rec::Recursorsettings settings
;
3078 // TODO: handle include-dir on command line
3079 auto yamlstatus
= pdns::settings::rec::readYamlSettings(yamlconfigname
, ::arg()["include-dir"], settings
, msg
, startupLog
);
3081 switch (yamlstatus
) {
3082 case pdns::settings::rec::YamlSettingsStatus::CannotOpen
:
3083 SLOG(g_log
<< Logger::Debug
<< "No YAML config found for configname '" << yamlconfigname
<< "': " << msg
<< endl
,
3084 startupLog
->error(Logr::Debug
, msg
, "No YAML config found", "configname", Logging::Loggable(yamlconfigname
)));
3086 case pdns::settings::rec::YamlSettingsStatus::PresentButFailed
:
3087 SLOG(g_log
<< Logger::Error
<< "YAML config found for configname '" << yamlconfigname
<< "' but error ocurred processing it" << endl
,
3088 startupLog
->error(Logr::Error
, msg
, "YAML config found, but error occurred processsing it", "configname", Logging::Loggable(yamlconfigname
)));
3091 case pdns::settings::rec::YamlSettingsStatus::OK
:
3092 g_yamlSettings
= true;
3093 SLOG(g_log
<< Logger::Notice
<< "YAML config found and processed for configname '" << yamlconfigname
<< "'" << endl
,
3094 startupLog
->info(Logr::Notice
, "YAML config found and processed", "configname", Logging::Loggable(yamlconfigname
)));
3095 pdns::settings::rec::processAPIDir(arg()["include-dir"], settings
, startupLog
);
3096 pdns::settings::rec::bridgeStructToOldStyleSettings(settings
);
3100 if (g_yamlSettings
) {
3101 bool mustExit
= false;
3102 std::tie(ret
, mustExit
) = doYamlConfig(startupLog
, argc
, argv
);
3103 if (ret
!= 0 || mustExit
) {
3108 if (yamlstatus
== pdns::settings::rec::YamlSettingsStatus::CannotOpen
) {
3109 configname
+= ".conf";
3110 bool mustExit
= false;
3111 std::tie(ret
, mustExit
) = doConfig(startupLog
, configname
, argc
, argv
);
3112 if (ret
!= 0 || mustExit
) {
3115 if (!::arg().file(configname
)) {
3116 SLOG(g_log
<< Logger::Warning
<< "Unable to open configuration file '" << configname
<< "'" << endl
,
3117 startupLog
->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname
)));
3121 // Reparse, now with config file as well, both for old-style as for YAML settings
3122 ::arg().parse(argc
, argv
);
3124 g_quiet
= ::arg().mustDo("quiet");
3125 s_logUrgency
= (Logger::Urgency
)::arg().asNum("loglevel");
3126 g_slogStructured
= ::arg().mustDo("structured-logging");
3128 if (s_logUrgency
< Logger::Error
) {
3129 s_logUrgency
= Logger::Error
;
3131 if (!g_quiet
&& s_logUrgency
< Logger::Info
) { // Logger::Info=6, Logger::Debug=7
3132 s_logUrgency
= Logger::Info
; // if you do --quiet=no, you need Info to also see the query log
3134 g_log
.setLoglevel(s_logUrgency
);
3135 g_log
.toConsole(s_logUrgency
);
3137 if (!::arg()["chroot"].empty() && !::arg()["api-config-dir"].empty()) {
3138 SLOG(g_log
<< Logger::Error
<< "Using chroot and enabling the API is not possible" << endl
,
3139 startupLog
->info(Logr::Error
, "Cannot use chroot and enable the API at the same time"));
3140 return EXIT_FAILURE
;
3143 handleRuntimeDefaults(startupLog
);
3145 g_recCache
= std::make_unique
<MemRecursorCache
>(::arg().asNum("record-cache-shards"));
3146 g_negCache
= std::make_unique
<NegCache
>(::arg().asNum("record-cache-shards") / 8);
3147 if (!::arg().mustDo("disable-packetcache")) {
3148 g_maxPacketCacheEntries
= ::arg().asNum("max-packetcache-entries");
3149 g_packetCache
= std::make_unique
<RecursorPacketCache
>(g_maxPacketCacheEntries
, ::arg().asNum("packetcache-shards"));
3152 ret
= serviceMain(startupLog
);
3154 catch (const PDNSException
& ae
) {
3155 SLOG(g_log
<< Logger::Error
<< "Exception: " << ae
.reason
<< endl
,
3156 g_slog
->withName("config")->error(Logr::Critical
, ae
.reason
, "Fatal error", "exception", Logging::Loggable("PDNSException")));
3159 catch (const std::exception
& e
) {
3160 SLOG(g_log
<< Logger::Error
<< "STL Exception: " << e
.what() << endl
,
3161 g_slog
->withName("config")->error(Logr::Critical
, e
.what(), "Fatal error", "exception", Logging::Loggable("std::exception")));
3165 SLOG(g_log
<< Logger::Error
<< "any other exception in main: " << endl
,
3166 g_slog
->withName("config")->info(Logr::Critical
, "Fatal error"));
3173 static RecursorControlChannel::Answer
* doReloadLuaScript()
3175 string fname
= ::arg()["lua-dns-script"];
3176 auto log
= g_slog
->withName("runtime")->withValues("name", Logging::Loggable(fname
));
3178 if (fname
.empty()) {
3180 SLOG(g_log
<< Logger::Info
<< RecThreadInfo::id() << " Unloaded current lua script" << endl
,
3181 log
->info(Logr::Info
, "Unloaded current lua script"));
3182 return new RecursorControlChannel::Answer
{0, string("unloaded\n")};
3185 t_pdl
= std::make_shared
<RecursorLua4
>();
3187 t_pdl
->loadFile(fname
);
3189 catch (std::runtime_error
& ex
) {
3190 string msg
= std::to_string(RecThreadInfo::id()) + " Retaining current script, could not read '" + fname
+ "': " + ex
.what();
3191 SLOG(g_log
<< Logger::Error
<< msg
<< endl
,
3192 log
->error(Logr::Error
, ex
.what(), "Retaining current script, could not read new script"));
3193 return new RecursorControlChannel::Answer
{1, msg
+ "\n"};
3196 catch (std::exception
& e
) {
3197 SLOG(g_log
<< Logger::Error
<< RecThreadInfo::id() << " Retaining current script, error from '" << fname
<< "': " << e
.what() << endl
,
3198 log
->error(Logr::Error
, e
.what(), "Retaining current script, error in new script"));
3199 return new RecursorControlChannel::Answer
{1, string("retaining current script, error from '" + fname
+ "': " + e
.what() + "\n")};
3202 SLOG(g_log
<< Logger::Warning
<< RecThreadInfo::id() << " (Re)loaded lua script from '" << fname
<< "'" << endl
,
3203 log
->info(Logr::Warning
, "(Re)loaded lua script"));
3204 return new RecursorControlChannel::Answer
{0, string("(re)loaded '" + fname
+ "'\n")};
3207 RecursorControlChannel::Answer
doQueueReloadLuaScript(vector
<string
>::const_iterator begin
, vector
<string
>::const_iterator end
)
3210 ::arg().set("lua-dns-script") = *begin
;
3213 return broadcastAccFunction
<RecursorControlChannel::Answer
>(doReloadLuaScript
);
3216 static string
* pleaseUseNewTraceRegex(const std::string
& newRegex
, int file
)
3219 if (newRegex
.empty()) {
3220 t_traceRegex
.reset();
3221 t_tracefd
= FDWrapper();
3222 return new string("unset\n");
3225 return new string("could not dup file\n");
3227 t_traceRegex
= std::make_shared
<Regex
>(newRegex
);
3229 return new string("ok\n"); // NOLINT(cppcoreguidelines-owning-memory): it's the API
3231 catch (const PDNSException
& ae
) {
3232 return new string(ae
.reason
+ "\n"); // NOLINT(cppcoreguidelines-owning-memory): it's the API
3236 string
doTraceRegex(FDWrapper file
, vector
<string
>::const_iterator begin
, vector
<string
>::const_iterator end
)
3238 int fileno
= dup(file
);
3239 // Potential dup failure handled in pleaseUseNewTraceRegex()
3240 return broadcastAccFunction
<string
>([=] { return pleaseUseNewTraceRegex(begin
!= end
? *begin
: "", fileno
); });
3243 struct WipeCacheResult
wipeCaches(const DNSName
& canon
, bool subtree
, uint16_t qtype
)
3245 struct WipeCacheResult res
;
3248 res
.record_count
= static_cast<int>(g_recCache
->doWipeCache(canon
, subtree
, qtype
));
3249 // scanbuild complains here about an allocated function object that is being leaked. Needs investigation
3250 if (g_packetCache
) {
3251 res
.packet_count
= static_cast<int>(g_packetCache
->doWipePacketCache(canon
, qtype
, subtree
));
3253 res
.negative_record_count
= static_cast<int>(g_negCache
->wipe(canon
, subtree
));
3254 if (g_aggressiveNSECCache
) {
3255 g_aggressiveNSECCache
->removeZoneInfo(canon
, subtree
);
3258 catch (const std::exception
& e
) {
3259 auto log
= g_slog
->withName("runtime");
3260 SLOG(g_log
<< Logger::Warning
<< ", failed: " << e
.what() << endl
,
3261 log
->error(Logr::Warning
, e
.what(), "Wipecache failed"));