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"
44 #endif /* NOD_ENABLED */
51 // All calls are coming form the same function, so no use for CODE_LINE, CODE_FUNC etc
52 #define SD_JOURNAL_SUPPRESS_LOCATION
53 #include <systemd/sd-daemon.h>
54 #include <systemd/sd-journal.h>
58 thread_local FrameStreamServersInfo t_frameStreamServersInfo
;
59 thread_local FrameStreamServersInfo t_nodFrameStreamServersInfo
;
60 #endif /* HAVE_FSTRM */
62 string g_programname
= "pdns_recursor";
64 RecursorControlChannel g_rcc
; // only active in the handler thread
68 DNSName g_nodLookupDomain
;
70 SuffixMatchNode g_nodDomainWL
;
71 std::string g_nod_pbtag
;
74 std::string g_udr_pbtag
;
75 thread_local
std::shared_ptr
<nod::NODDB
> t_nodDBp
;
76 thread_local
std::shared_ptr
<nod::UniqueResponseDB
> t_udrDBp
;
77 #endif /* NOD_ENABLED */
79 std::atomic
<bool> statsWanted
;
80 uint32_t g_disthashseed
;
81 bool g_useIncomingECS
;
82 NetmaskGroup g_proxyProtocolACL
;
83 boost::optional
<ComboAddress
> g_dns64Prefix
{boost::none
};
84 DNSName g_dns64PrefixReverse
;
85 std::shared_ptr
<SyncRes::domainmap_t
> g_initialDomainMap
; // new threads needs this to be setup
86 std::shared_ptr
<NetmaskGroup
> g_initialAllowFrom
; // new thread needs to be setup with this
87 std::shared_ptr
<NetmaskGroup
> g_initialAllowNotifyFrom
; // new threads need this to be setup
88 std::shared_ptr
<notifyset_t
> g_initialAllowNotifyFor
; // new threads need this to be setup
89 bool g_logRPZChanges
{false};
90 static time_t s_statisticsInterval
;
91 bool g_addExtendedResolutionDNSErrors
;
92 static std::atomic
<uint32_t> s_counter
;
95 static string s_structured_logger_backend
;
96 static Logger::Urgency s_logUrgency
;
98 std::shared_ptr
<Logr::Logger
> g_slogtcpin
;
99 std::shared_ptr
<Logr::Logger
> g_slogudpin
;
100 std::shared_ptr
<Logr::Logger
> g_slogudpout
;
102 /* without reuseport, all listeners share the same sockets */
103 deferredAdd_t g_deferredAdds
;
105 /* first we have the handler thread, t_id == 0 (some other
106 helper threads like SNMP might have t_id == 0 as well)
107 then the distributor threads if any
108 and finally the workers */
109 std::vector
<RecThreadInfo
> RecThreadInfo::s_threadInfos
;
111 std::unique_ptr
<ProxyMapping
> g_proxyMapping
; // new threads needs this to be setup
112 thread_local
std::unique_ptr
<ProxyMapping
> t_proxyMapping
;
114 bool RecThreadInfo::s_weDistributeQueries
; // if true, 1 or more threads listen on the incoming query sockets and distribute them to workers
115 unsigned int RecThreadInfo::s_numDistributorThreads
;
116 unsigned int RecThreadInfo::s_numWorkerThreads
;
117 thread_local
unsigned int RecThreadInfo::t_id
;
119 static std::map
<unsigned int, std::set
<int>> parseCPUMap(Logr::log_t log
)
121 std::map
<unsigned int, std::set
<int>> result
;
123 const std::string value
= ::arg()["cpu-map"];
125 if (!value
.empty() && !isSettingThreadCPUAffinitySupported()) {
126 SLOG(g_log
<< Logger::Warning
<< "CPU mapping requested but not supported, skipping" << endl
,
127 log
->info(Logr::Warning
, "CPU mapping requested but not supported, skipping"));
131 std::vector
<std::string
> parts
;
133 stringtok(parts
, value
, " \t");
135 for (const auto& part
: parts
) {
136 if (part
.find('=') == string::npos
)
140 auto headers
= splitField(part
, '=');
141 boost::trim(headers
.first
);
142 boost::trim(headers
.second
);
144 auto threadId
= pdns::checked_stoi
<unsigned int>(headers
.first
);
145 std::vector
<std::string
> cpus
;
147 stringtok(cpus
, headers
.second
, ",");
149 for (const auto& cpu
: cpus
) {
150 int cpuId
= std::stoi(cpu
);
152 result
[threadId
].insert(cpuId
);
155 catch (const std::exception
& e
) {
156 SLOG(g_log
<< Logger::Error
<< "Error parsing cpu-map entry '" << part
<< "': " << e
.what() << endl
,
157 log
->error(Logr::Error
, e
.what(), "Error parsing cpu-map entry", "entry", Logging::Loggable(part
)));
164 static void setCPUMap(const std::map
<unsigned int, std::set
<int>>& cpusMap
, unsigned int n
, pthread_t tid
, Logr::log_t log
)
166 const auto& cpuMapping
= cpusMap
.find(n
);
167 if (cpuMapping
== cpusMap
.cend()) {
170 int rc
= mapThreadToCPUList(tid
, cpuMapping
->second
);
172 if (!g_slogStructured
) {
173 g_log
<< Logger::Info
<< "CPU affinity for thread " << n
<< " has been set to CPU map:";
174 for (const auto cpu
: cpuMapping
->second
) {
175 g_log
<< Logger::Info
<< " " << cpu
;
177 g_log
<< Logger::Info
<< endl
;
180 log
->info(Logr::Info
, "CPU affinity has been set", "thread", Logging::Loggable(n
), "cpumap", Logging::IterLoggable(cpuMapping
->second
.begin(), cpuMapping
->second
.end()));
184 if (!g_slogStructured
) {
185 g_log
<< Logger::Warning
<< "Error setting CPU affinity for thread " << n
<< " to CPU map:";
186 for (const auto cpu
: cpuMapping
->second
) {
187 g_log
<< Logger::Info
<< " " << cpu
;
189 g_log
<< Logger::Info
<< ' ' << strerror(rc
) << endl
;
192 log
->error(Logr::Warning
, rc
, "Error setting CPU affinity", "thread", Logging::Loggable(n
), "cpumap", Logging::IterLoggable(cpuMapping
->second
.begin(), cpuMapping
->second
.end()));
197 static void recursorThread();
199 void RecThreadInfo::start(unsigned int id
, const string
& tname
, const std::map
<unsigned int, std::set
<int>>& cpusMap
, Logr::log_t log
)
202 thread
= std::thread([id
, tname
] {
204 const string threadPrefix
= "rec/";
205 setThreadName(threadPrefix
+ tname
);
208 setCPUMap(cpusMap
, id
, thread
.native_handle(), log
);
211 int RecThreadInfo::runThreads(Logr::log_t log
)
213 int ret
= EXIT_SUCCESS
;
214 unsigned int currentThreadId
= 1;
215 const auto cpusMap
= parseCPUMap(log
);
217 if (RecThreadInfo::numDistributors() + RecThreadInfo::numWorkers() == 1) {
218 SLOG(g_log
<< Logger::Warning
<< "Operating with single distributor/worker thread" << endl
,
219 log
->info(Logr::Notice
, "Operating with single distributor/worker thread"));
221 /* This thread handles the web server, carbon, statistics and the control channel */
222 auto& handlerInfo
= RecThreadInfo::info(0);
223 handlerInfo
.setHandler();
224 handlerInfo
.start(0, "web+stat", cpusMap
, log
);
225 auto& taskInfo
= RecThreadInfo::info(2);
226 taskInfo
.setTaskThread();
227 taskInfo
.start(2, "taskThread", cpusMap
, log
);
229 auto& info
= RecThreadInfo::info(currentThreadId
);
232 info
.setThreadId(currentThreadId
++);
235 handlerInfo
.thread
.join();
236 if (handlerInfo
.exitCode
!= 0) {
237 ret
= handlerInfo
.exitCode
;
239 taskInfo
.thread
.join();
240 if (taskInfo
.exitCode
!= 0) {
241 ret
= taskInfo
.exitCode
;
245 // Setup RecThreadInfo objects
246 unsigned int tmp
= currentThreadId
;
247 if (RecThreadInfo::weDistributeQueries()) {
248 for (unsigned int n
= 0; n
< RecThreadInfo::numDistributors(); ++n
) {
249 RecThreadInfo::info(tmp
++).setListener();
252 for (unsigned int n
= 0; n
< RecThreadInfo::numWorkers(); ++n
) {
253 auto& info
= RecThreadInfo::info(tmp
++);
254 info
.setListener(!RecThreadInfo::weDistributeQueries());
257 for (unsigned int n
= 0; n
< RecThreadInfo::numTaskThreads(); ++n
) {
258 auto& info
= RecThreadInfo::info(tmp
++);
259 info
.setTaskThread();
262 // And now start the actual threads
263 if (RecThreadInfo::weDistributeQueries()) {
264 SLOG(g_log
<< Logger::Warning
<< "Launching " << RecThreadInfo::numDistributors() << " distributor threads" << endl
,
265 log
->info(Logr::Notice
, "Launching distributor threads", "count", Logging::Loggable(RecThreadInfo::numDistributors())));
266 for (unsigned int n
= 0; n
< RecThreadInfo::numDistributors(); ++n
) {
267 auto& info
= RecThreadInfo::info(currentThreadId
);
268 info
.start(currentThreadId
++, "distr", cpusMap
, log
);
271 SLOG(g_log
<< Logger::Warning
<< "Launching " << RecThreadInfo::numWorkers() << " worker threads" << endl
,
272 log
->info(Logr::Notice
, "Launching worker threads", "count", Logging::Loggable(RecThreadInfo::numWorkers())));
274 for (unsigned int n
= 0; n
< RecThreadInfo::numWorkers(); ++n
) {
275 auto& info
= RecThreadInfo::info(currentThreadId
);
276 info
.start(currentThreadId
++, "worker", cpusMap
, log
);
279 for (unsigned int n
= 0; n
< RecThreadInfo::numTaskThreads(); ++n
) {
280 auto& info
= RecThreadInfo::info(currentThreadId
);
281 info
.start(currentThreadId
++, "taskThread", cpusMap
, log
);
284 /* This thread handles the web server, carbon, statistics and the control channel */
285 auto& info
= RecThreadInfo::info(0);
287 info
.start(0, "web+stat", cpusMap
, log
);
289 for (auto& ti
: RecThreadInfo::infos()) {
291 if (ti
.exitCode
!= 0) {
299 void RecThreadInfo::makeThreadPipes(Logr::log_t log
)
301 auto pipeBufferSize
= ::arg().asNum("distribution-pipe-buffer-size");
302 if (pipeBufferSize
> 0) {
303 SLOG(g_log
<< Logger::Info
<< "Resizing the buffer of the distribution pipe to " << pipeBufferSize
<< endl
,
304 log
->info(Logr::Info
, "Resizing the buffer of the distribution pipe", "size", Logging::Loggable(pipeBufferSize
)));
307 /* thread 0 is the handler / SNMP, worker threads start at 1 */
308 for (unsigned int n
= 0; n
< numRecursorThreads(); ++n
) {
309 auto& threadInfo
= info(n
);
313 unixDie("Creating pipe for inter-thread communications");
315 threadInfo
.pipes
.readToThread
= fd
[0];
316 threadInfo
.pipes
.writeToThread
= fd
[1];
318 // handler thread only gets first pipe, not the others
324 unixDie("Creating pipe for inter-thread communications");
326 threadInfo
.pipes
.readFromThread
= fd
[0];
327 threadInfo
.pipes
.writeFromThread
= fd
[1];
330 unixDie("Creating pipe for inter-thread communications");
332 threadInfo
.pipes
.readQueriesToThread
= fd
[0];
333 threadInfo
.pipes
.writeQueriesToThread
= fd
[1];
335 if (pipeBufferSize
> 0) {
336 if (!setPipeBufferSize(threadInfo
.pipes
.writeQueriesToThread
, pipeBufferSize
)) {
338 SLOG(g_log
<< Logger::Warning
<< "Error resizing the buffer of the distribution pipe for thread " << n
<< " to " << pipeBufferSize
<< ": " << strerror(err
) << endl
,
339 log
->error(Logr::Warning
, err
, "Error resizing the buffer of the distribution pipe for thread", "thread", Logging::Loggable(n
), "size", Logging::Loggable(pipeBufferSize
)));
340 auto existingSize
= getPipeBufferSize(threadInfo
.pipes
.writeQueriesToThread
);
341 if (existingSize
> 0) {
342 SLOG(g_log
<< Logger::Warning
<< "The current size of the distribution pipe's buffer for thread " << n
<< " is " << existingSize
<< endl
,
343 log
->info(Logr::Warning
, "The current size of the distribution pipe's buffer for thread", "thread", Logging::Loggable(n
), "size", Logging::Loggable(existingSize
)));
348 if (!setNonBlocking(threadInfo
.pipes
.writeQueriesToThread
)) {
349 unixDie("Making pipe for inter-thread communications non-blocking");
356 static ArgvMap theArg
;
360 static FDMultiplexer
* getMultiplexer(Logr::log_t log
)
363 for (const auto& i
: FDMultiplexer::getMultiplexerMap()) {
365 ret
= i
.second(FDMultiplexer::s_maxevents
);
368 catch (FDMultiplexerException
& fe
) {
369 SLOG(g_log
<< Logger::Error
<< "Non-fatal error initializing possible multiplexer (" << fe
.what() << "), falling back" << endl
,
370 log
->error(Logr::Error
, fe
.what(), "Non-fatal error initializing possible multiplexer, falling back"));
373 SLOG(g_log
<< Logger::Error
<< "Non-fatal error initializing possible multiplexer" << endl
,
374 log
->info(Logr::Error
, "Non-fatal error initializing possible multiplexer"));
377 SLOG(g_log
<< Logger::Error
<< "No working multiplexer found!" << endl
,
378 log
->info(Logr::Error
, "No working multiplexer found!"));
382 static std::shared_ptr
<std::vector
<std::unique_ptr
<RemoteLogger
>>> startProtobufServers(const ProtobufExportConfig
& config
, Logr::log_t log
)
384 auto result
= std::make_shared
<std::vector
<std::unique_ptr
<RemoteLogger
>>>();
386 for (const auto& server
: config
.servers
) {
388 auto logger
= make_unique
<RemoteLogger
>(server
, config
.timeout
, 100 * config
.maxQueuedEntries
, config
.reconnectWaitTime
, config
.asyncConnect
);
389 logger
->setLogQueries(config
.logQueries
);
390 logger
->setLogResponses(config
.logResponses
);
391 result
->emplace_back(std::move(logger
));
393 catch (const std::exception
& e
) {
394 SLOG(g_log
<< Logger::Error
<< "Error while starting protobuf logger to '" << server
<< ": " << e
.what() << endl
,
395 log
->error(Logr::Error
, e
.what(), "Exception while starting protobuf logger", "exception", Logging::Loggable("std::exception"), "server", Logging::Loggable(server
)));
397 catch (const PDNSException
& e
) {
398 SLOG(g_log
<< Logger::Error
<< "Error while starting protobuf logger to '" << server
<< ": " << e
.reason
<< endl
,
399 log
->error(Logr::Error
, e
.reason
, "Exception while starting protobuf logger", "exception", Logging::Loggable("PDNSException"), "server", Logging::Loggable(server
)));
406 bool checkProtobufExport(LocalStateHolder
<LuaConfigItems
>& luaconfsLocal
)
408 if (!luaconfsLocal
->protobufExportConfig
.enabled
) {
409 if (t_protobufServers
.servers
) {
410 t_protobufServers
.servers
.reset();
411 t_protobufServers
.config
= luaconfsLocal
->protobufExportConfig
;
417 /* if the server was not running, or if it was running according to a
418 previous configuration */
419 if (t_protobufServers
.generation
< luaconfsLocal
->generation
&& t_protobufServers
.config
!= luaconfsLocal
->protobufExportConfig
) {
421 if (t_protobufServers
.servers
) {
422 t_protobufServers
.servers
.reset();
424 auto log
= g_slog
->withName("protobuf");
425 t_protobufServers
.servers
= startProtobufServers(luaconfsLocal
->protobufExportConfig
, log
);
426 t_protobufServers
.config
= luaconfsLocal
->protobufExportConfig
;
427 t_protobufServers
.generation
= luaconfsLocal
->generation
;
433 bool checkOutgoingProtobufExport(LocalStateHolder
<LuaConfigItems
>& luaconfsLocal
)
435 if (!luaconfsLocal
->outgoingProtobufExportConfig
.enabled
) {
436 if (t_outgoingProtobufServers
.servers
) {
437 t_outgoingProtobufServers
.servers
.reset();
438 t_outgoingProtobufServers
.config
= luaconfsLocal
->outgoingProtobufExportConfig
;
444 /* if the server was not running, or if it was running according to a
445 previous configuration */
446 if (t_outgoingProtobufServers
.generation
< luaconfsLocal
->generation
&& t_outgoingProtobufServers
.config
!= luaconfsLocal
->outgoingProtobufExportConfig
) {
448 if (t_outgoingProtobufServers
.servers
) {
449 t_outgoingProtobufServers
.servers
.reset();
451 auto log
= g_slog
->withName("protobuf");
452 t_outgoingProtobufServers
.servers
= startProtobufServers(luaconfsLocal
->outgoingProtobufExportConfig
, log
);
453 t_outgoingProtobufServers
.config
= luaconfsLocal
->outgoingProtobufExportConfig
;
454 t_outgoingProtobufServers
.generation
= luaconfsLocal
->generation
;
460 void protobufLogQuery(LocalStateHolder
<LuaConfigItems
>& luaconfsLocal
, const boost::uuids::uuid
& uniqueId
, const ComboAddress
& remote
, const ComboAddress
& local
, const ComboAddress
& mappedRemote
, const Netmask
& ednssubnet
, bool tcp
, uint16_t id
, 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
)
462 auto log
= g_slog
->withName("pblq");
464 if (!t_protobufServers
.servers
) {
468 ComboAddress requestor
;
469 if (!luaconfsLocal
->protobufExportConfig
.logMappedFrom
) {
470 Netmask
requestorNM(remote
, remote
.sin4
.sin_family
== AF_INET
? luaconfsLocal
->protobufMaskV4
: luaconfsLocal
->protobufMaskV6
);
471 requestor
= requestorNM
.getMaskedNetwork();
472 requestor
.setPort(remote
.getPort());
475 Netmask
requestorNM(mappedRemote
, mappedRemote
.sin4
.sin_family
== AF_INET
? luaconfsLocal
->protobufMaskV4
: luaconfsLocal
->protobufMaskV6
);
476 requestor
= requestorNM
.getMaskedNetwork();
477 requestor
.setPort(mappedRemote
.getPort());
480 pdns::ProtoZero::RecMessage m
{128, std::string::size_type(policyTags
.empty() ? 0 : 64)}; // It's a guess
481 m
.setType(pdns::ProtoZero::Message::MessageType::DNSQueryType
);
482 m
.setRequest(uniqueId
, requestor
, local
, qname
, qtype
, qclass
, id
, tcp
? pdns::ProtoZero::Message::TransportProtocol::TCP
: pdns::ProtoZero::Message::TransportProtocol::UDP
, len
);
483 m
.setServerIdentity(SyncRes::s_serverID
);
484 m
.setEDNSSubnet(ednssubnet
, ednssubnet
.isIPv4() ? luaconfsLocal
->protobufMaskV4
: luaconfsLocal
->protobufMaskV6
);
485 m
.setRequestorId(requestorId
);
486 m
.setDeviceId(deviceId
);
487 m
.setDeviceName(deviceName
);
489 if (!policyTags
.empty()) {
490 m
.addPolicyTags(policyTags
);
492 for (const auto& mit
: meta
) {
493 m
.setMeta(mit
.first
, mit
.second
.stringVal
, mit
.second
.intVal
);
496 std::string
msg(m
.finishAndMoveBuf());
497 for (auto& server
: *t_protobufServers
.servers
) {
498 remoteLoggerQueueData(*server
, msg
);
502 void protobufLogResponse(pdns::ProtoZero::RecMessage
& message
)
504 if (!t_protobufServers
.servers
) {
508 std::string
msg(message
.finishAndMoveBuf());
509 for (auto& server
: *t_protobufServers
.servers
) {
510 remoteLoggerQueueData(*server
, msg
);
514 void protobufLogResponse(const struct dnsheader
* dh
, LocalStateHolder
<LuaConfigItems
>& luaconfsLocal
,
515 const RecursorPacketCache::OptPBData
& pbData
, const struct timeval
& tv
,
516 bool tcp
, const ComboAddress
& source
, const ComboAddress
& destination
,
517 const ComboAddress
& mappedSource
,
518 const EDNSSubnetOpts
& ednssubnet
,
519 const boost::uuids::uuid
& uniqueId
, const string
& requestorId
, const string
& deviceId
,
520 const string
& deviceName
, const std::map
<std::string
, RecursorLua4::MetaValue
>& meta
,
521 const RecEventTrace
& eventTrace
)
523 pdns::ProtoZero::RecMessage
pbMessage(pbData
? pbData
->d_message
: "", pbData
? pbData
->d_response
: "", 64, 10); // The extra bytes we are going to add
524 // Normally we take the immutable string from the cache and append a few values, but if it's not there (can this happen?)
525 // we start with an empty string and append the minimal
527 pbMessage
.setType(pdns::ProtoZero::Message::MessageType::DNSResponseType
);
528 pbMessage
.setServerIdentity(SyncRes::s_serverID
);
532 if (g_useKernelTimestamp
&& tv
.tv_sec
) {
533 pbMessage
.setQueryTime(tv
.tv_sec
, tv
.tv_usec
);
536 pbMessage
.setQueryTime(g_now
.tv_sec
, g_now
.tv_usec
);
540 if (!luaconfsLocal
->protobufExportConfig
.logMappedFrom
) {
541 Netmask
requestorNM(source
, source
.sin4
.sin_family
== AF_INET
? luaconfsLocal
->protobufMaskV4
: luaconfsLocal
->protobufMaskV6
);
542 auto requestor
= requestorNM
.getMaskedNetwork();
543 pbMessage
.setFrom(requestor
);
544 pbMessage
.setFromPort(source
.getPort());
547 Netmask
requestorNM(mappedSource
, mappedSource
.sin4
.sin_family
== AF_INET
? luaconfsLocal
->protobufMaskV4
: luaconfsLocal
->protobufMaskV6
);
548 auto requestor
= requestorNM
.getMaskedNetwork();
549 pbMessage
.setFrom(requestor
);
550 pbMessage
.setFromPort(mappedSource
.getPort());
552 pbMessage
.setMessageIdentity(uniqueId
);
553 pbMessage
.setTo(destination
);
554 pbMessage
.setSocketProtocol(tcp
? pdns::ProtoZero::Message::TransportProtocol::TCP
: pdns::ProtoZero::Message::TransportProtocol::UDP
);
555 pbMessage
.setId(dh
->id
);
558 pbMessage
.setEDNSSubnet(ednssubnet
.source
, ednssubnet
.source
.isIPv4() ? luaconfsLocal
->protobufMaskV4
: luaconfsLocal
->protobufMaskV6
);
559 pbMessage
.setRequestorId(requestorId
);
560 pbMessage
.setDeviceId(deviceId
);
561 pbMessage
.setDeviceName(deviceName
);
562 pbMessage
.setToPort(destination
.getPort());
563 for (const auto& m
: meta
) {
564 pbMessage
.setMeta(m
.first
, m
.second
.stringVal
, m
.second
.intVal
);
568 pbMessage
.setNewlyObservedDomain(false);
571 if (eventTrace
.enabled() && SyncRes::s_event_trace_enabled
& SyncRes::event_trace_to_pb
) {
572 pbMessage
.addEvents(eventTrace
);
574 protobufLogResponse(pbMessage
);
579 static std::shared_ptr
<std::vector
<std::unique_ptr
<FrameStreamLogger
>>> startFrameStreamServers(const FrameStreamExportConfig
& config
, Logr::log_t log
)
581 auto result
= std::make_shared
<std::vector
<std::unique_ptr
<FrameStreamLogger
>>>();
583 for (const auto& server
: config
.servers
) {
585 std::unordered_map
<string
, unsigned> options
;
586 options
["bufferHint"] = config
.bufferHint
;
587 options
["flushTimeout"] = config
.flushTimeout
;
588 options
["inputQueueSize"] = config
.inputQueueSize
;
589 options
["outputQueueSize"] = config
.outputQueueSize
;
590 options
["queueNotifyThreshold"] = config
.queueNotifyThreshold
;
591 options
["reopenInterval"] = config
.reopenInterval
;
592 FrameStreamLogger
* fsl
= nullptr;
594 ComboAddress
address(server
);
595 fsl
= new FrameStreamLogger(address
.sin4
.sin_family
, address
.toStringWithPort(), true, options
);
597 catch (const PDNSException
& e
) {
598 fsl
= new FrameStreamLogger(AF_UNIX
, server
, true, options
);
600 fsl
->setLogQueries(config
.logQueries
);
601 fsl
->setLogResponses(config
.logResponses
);
602 fsl
->setLogNODs(config
.logNODs
);
603 fsl
->setLogUDRs(config
.logUDRs
);
604 result
->emplace_back(fsl
);
606 catch (const std::exception
& e
) {
607 SLOG(g_log
<< Logger::Error
<< "Error while starting dnstap framestream logger to '" << server
<< ": " << e
.what() << endl
,
608 log
->error(Logr::Error
, e
.what(), "Exception while starting dnstap framestream logger", "exception", Logging::Loggable("std::exception"), "server", Logging::Loggable(server
)));
610 catch (const PDNSException
& e
) {
611 SLOG(g_log
<< Logger::Error
<< "Error while starting dnstap framestream logger to '" << server
<< ": " << e
.reason
<< endl
,
612 log
->error(Logr::Error
, e
.reason
, "Exception while starting dnstap framestream logger", "exception", Logging::Loggable("PDNSException"), "server", Logging::Loggable(server
)));
619 static void asyncFrameStreamLoggersCleanup(std::shared_ptr
<std::vector
<std::unique_ptr
<FrameStreamLogger
>>>&& servers
)
621 auto thread
= std::thread([&] {
627 bool checkFrameStreamExport(LocalStateHolder
<LuaConfigItems
>& luaconfsLocal
, const FrameStreamExportConfig
& config
, FrameStreamServersInfo
& serverInfos
)
629 if (!config
.enabled
) {
630 if (serverInfos
.servers
) {
631 // dt's take care of cleanup
632 asyncFrameStreamLoggersCleanup(std::move(serverInfos
.servers
));
633 serverInfos
.config
= config
;
639 /* if the server was not running, or if it was running according to a previous
642 if (serverInfos
.generation
< luaconfsLocal
->generation
&& serverInfos
.config
!= config
) {
643 if (serverInfos
.servers
) {
644 // dt's take care of cleanup
645 asyncFrameStreamLoggersCleanup(std::move(serverInfos
.servers
));
648 auto dnsTapLog
= g_slog
->withName("dnstap");
649 serverInfos
.servers
= startFrameStreamServers(config
, dnsTapLog
);
650 serverInfos
.config
= config
;
651 serverInfos
.generation
= luaconfsLocal
->generation
;
657 #endif /* HAVE_FSTRM */
659 static void makeControlChannelSocket(int processNum
= -1)
661 string sockname
= ::arg()["socket-dir"] + "/" + g_programname
;
663 sockname
+= "." + std::to_string(processNum
);
664 sockname
+= ".controlsocket";
665 g_rcc
.listen(sockname
);
670 if (!::arg().isEmpty("socket-group"))
671 sockgroup
= ::arg().asGid("socket-group");
672 if (!::arg().isEmpty("socket-owner"))
673 sockowner
= ::arg().asUid("socket-owner");
675 if (sockgroup
> -1 || sockowner
> -1) {
676 if (chown(sockname
.c_str(), sockowner
, sockgroup
) < 0) {
677 unixDie("Failed to chown control socket");
681 // do mode change if socket-mode is given
682 if (!::arg().isEmpty("socket-mode")) {
683 mode_t sockmode
= ::arg().asMode("socket-mode");
684 if (chmod(sockname
.c_str(), sockmode
) < 0) {
685 unixDie("Failed to chmod control socket");
690 static void writePid(Logr::log_t log
)
692 if (!::arg().mustDo("write-pid"))
694 ofstream
of(g_pidfname
.c_str(), std::ios_base::app
);
696 of
<< Utility::getpid() << endl
;
699 SLOG(g_log
<< Logger::Error
<< "Writing pid for " << Utility::getpid() << " to " << g_pidfname
<< " failed: " << stringerror(err
) << endl
,
700 log
->error(Logr::Error
, err
, "Writing pid failed", "pid", Logging::Loggable(Utility::getpid()), "file", Logging::Loggable(g_pidfname
)));
704 static void checkSocketDir(Logr::log_t log
)
706 string
dir(::arg()["socket-dir"]);
709 struct stat dirStat
= {};
710 if (stat(dir
.c_str(), &dirStat
) == -1) {
711 msg
= "it does not exist or cannot access";
713 else if (!S_ISDIR(dirStat
.st_mode
)) {
714 msg
= "it is not a directory";
716 else if (access(dir
.c_str(), R_OK
| W_OK
| X_OK
) != 0) {
717 msg
= "cannot read, write or search";
722 dir
= ::arg()["chroot"] + dir
;
723 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
,
724 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
)));
729 static void setupNODThread(Logr::log_t log
)
732 uint32_t num_cells
= ::arg().asNum("new-domain-db-size");
733 t_nodDBp
= std::make_shared
<nod::NODDB
>(num_cells
);
735 t_nodDBp
->setCacheDir(::arg()["new-domain-history-dir"]);
737 catch (const PDNSException
& e
) {
738 SLOG(g_log
<< Logger::Error
<< "new-domain-history-dir (" << ::arg()["new-domain-history-dir"] << ") is not readable or does not exist" << endl
,
739 log
->error(Logr::Error
, e
.reason
, "new-domain-history-dir is not readbale or does not exists", "dir", Logging::Loggable(::arg()["new-domain-history-dir"])));
742 if (!t_nodDBp
->init()) {
743 SLOG(g_log
<< Logger::Error
<< "Could not initialize domain tracking" << endl
,
744 log
->info(Logr::Error
, "Could not initialize domain tracking"));
747 std::thread
t(nod::NODDB::startHousekeepingThread
, t_nodDBp
, std::this_thread::get_id());
749 g_nod_pbtag
= ::arg()["new-domain-pb-tag"];
752 uint32_t num_cells
= ::arg().asNum("unique-response-db-size");
753 t_udrDBp
= std::make_shared
<nod::UniqueResponseDB
>(num_cells
);
755 t_udrDBp
->setCacheDir(::arg()["unique-response-history-dir"]);
757 catch (const PDNSException
& e
) {
758 SLOG(g_log
<< Logger::Error
<< "unique-response-history-dir (" << ::arg()["unique-response-history-dir"] << ") is not readable or does not exist" << endl
,
759 log
->info(Logr::Error
, "unique-response-history-dir is not readable or does not exist", "dir", Logging::Loggable(::arg()["unique-response-history-dir"])));
762 if (!t_udrDBp
->init()) {
763 SLOG(g_log
<< Logger::Error
<< "Could not initialize unique response tracking" << endl
,
764 log
->info(Logr::Error
, "Could not initialize unique response tracking"));
767 std::thread
t(nod::UniqueResponseDB::startHousekeepingThread
, t_udrDBp
, std::this_thread::get_id());
769 g_udr_pbtag
= ::arg()["unique-response-pb-tag"];
773 static void parseNODIgnorelist(const std::string
& wlist
)
775 vector
<string
> parts
;
776 stringtok(parts
, wlist
, ",; ");
777 for (const auto& a
: parts
) {
778 g_nodDomainWL
.add(DNSName(a
));
782 static void setupNODGlobal()
784 // Setup NOD subsystem
785 g_nodEnabled
= ::arg().mustDo("new-domain-tracking");
786 g_nodLookupDomain
= DNSName(::arg()["new-domain-lookup"]);
787 g_nodLog
= ::arg().mustDo("new-domain-log");
788 parseNODIgnorelist(::arg()["new-domain-whitelist"]);
789 parseNODIgnorelist(::arg()["new-domain-ignore-list"]);
791 // Setup Unique DNS Response subsystem
792 g_udrEnabled
= ::arg().mustDo("unique-response-tracking");
793 g_udrLog
= ::arg().mustDo("unique-response-log");
795 #endif /* NOD_ENABLED */
797 static void daemonize(Logr::log_t log
)
804 int i
= open("/dev/null", O_RDWR
); /* open stdin */
807 SLOG(g_log
<< Logger::Critical
<< "Unable to open /dev/null: " << stringerror(err
) << endl
,
808 log
->error(Logr::Critical
, err
, "Unable to open /dev/null"));
811 dup2(i
, 0); /* stdin */
812 dup2(i
, 1); /* stderr */
813 dup2(i
, 2); /* stderr */
818 static void termIntHandler(int)
823 static void usr1Handler(int)
828 static void usr2Handler(int)
831 SyncRes::setDefaultLogMode(g_quiet
? SyncRes::LogNone
: SyncRes::Log
);
832 ::arg().set("quiet") = g_quiet
? "" : "no";
835 static void checkLinuxIPv6Limits(Logr::log_t log
)
839 if (readFileIfThere("/proc/sys/net/ipv6/route/max_size", &line
)) {
840 int lim
= std::stoi(line
);
842 SLOG(g_log
<< Logger::Error
<< "If using IPv6, please raise sysctl net.ipv6.route.max_size, currently set to " << lim
<< " which is < 16384" << endl
,
843 log
->info(Logr::Error
, "If using IPv6, please raise sysctl net.ipv6.route.max_size to a size >= 16384", "current", Logging::Loggable(lim
)));
849 static void checkOrFixFDS(Logr::log_t log
)
851 unsigned int availFDs
= getFilenumLimit();
852 unsigned int wantFDs
= g_maxMThreads
* RecThreadInfo::numWorkers() + 25; // even healthier margin then before
853 wantFDs
+= RecThreadInfo::numWorkers() * TCPOutConnectionManager::s_maxIdlePerThread
;
855 if (wantFDs
> availFDs
) {
856 unsigned int hardlimit
= getFilenumLimit(true);
857 if (hardlimit
>= wantFDs
) {
858 setFilenumLimit(wantFDs
);
859 SLOG(g_log
<< Logger::Warning
<< "Raised soft limit on number of filedescriptors to " << wantFDs
<< " to match max-mthreads and threads settings" << endl
,
860 log
->info(Logr::Warning
, "Raised soft limit on number of filedescriptors to match max-mthreads and threads settings", "limit", Logging::Loggable(wantFDs
)));
863 int newval
= (hardlimit
- 25 - TCPOutConnectionManager::s_maxIdlePerThread
) / RecThreadInfo::numWorkers();
864 SLOG(g_log
<< Logger::Warning
<< "Insufficient number of filedescriptors available for max-mthreads*threads setting! (" << hardlimit
<< " < " << wantFDs
<< "), reducing max-mthreads to " << newval
<< endl
,
865 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
)));
866 g_maxMThreads
= newval
;
867 setFilenumLimit(hardlimit
);
872 // static std::string s_timestampFormat = "%m-%dT%H:%M:%S";
873 static std::string s_timestampFormat
= "%s";
875 static const char* toTimestampStringMilli(const struct timeval
& tv
, char* buf
, size_t sz
)
878 if (s_timestampFormat
!= "%s") {
879 // strftime is not thread safe, it can access locale information
881 auto lock
= std::lock_guard(m
);
883 len
= strftime(buf
, sz
, s_timestampFormat
.c_str(), localtime_r(&tv
.tv_sec
, &tm
));
886 len
= snprintf(buf
, sz
, "%lld", static_cast<long long>(tv
.tv_sec
));
889 snprintf(buf
+ len
, sz
- len
, ".%03ld", static_cast<long>(tv
.tv_usec
) / 1000);
894 static void loggerSDBackend(const Logging::Entry
& entry
)
896 // First map SL priority to syslog's Urgency
897 Logger::Urgency u
= entry
.d_priority
? Logger::Urgency(entry
.d_priority
) : Logger::Info
;
898 if (u
> s_logUrgency
) {
899 // We do not log anything if the Urgency of the message is lower than the requested loglevel.
900 // Not that lower Urgency means higher number.
903 // We need to keep the string in mem until sd_journal_sendv has ben called
904 vector
<string
> strings
;
905 auto appendKeyAndVal
= [&strings
](const string
& k
, const string
& v
) {
906 strings
.emplace_back(k
+ "=" + v
);
908 appendKeyAndVal("MESSAGE", entry
.message
);
910 appendKeyAndVal("ERROR", entry
.error
.get());
912 appendKeyAndVal("LEVEL", std::to_string(entry
.level
));
913 appendKeyAndVal("PRIORITY", std::to_string(entry
.d_priority
));
915 appendKeyAndVal("SUBSYSTEM", entry
.name
.get());
918 appendKeyAndVal("TIMESTAMP", toTimestampStringMilli(entry
.d_timestamp
, timebuf
, sizeof(timebuf
)));
919 for (auto const& v
: entry
.values
) {
920 appendKeyAndVal(toUpper(v
.first
), v
.second
);
922 // Thread id filled in by backend, since the SL code does not know about RecursorThreads
923 // We use the Recursor thread, other threads get id 0. May need to revisit.
924 appendKeyAndVal("TID", std::to_string(RecThreadInfo::id()));
927 iov
.reserve(strings
.size());
928 for (const auto& s
: strings
) {
929 // iovec has no 2 arg constructor, so make it explicit
930 iov
.emplace_back(iovec
{const_cast<void*>(reinterpret_cast<const void*>(s
.data())), s
.size()});
932 sd_journal_sendv(iov
.data(), static_cast<int>(iov
.size()));
936 static void loggerBackend(const Logging::Entry
& entry
)
938 static thread_local
std::stringstream buf
;
940 // First map SL priority to syslog's Urgency
941 Logger::Urgency u
= entry
.d_priority
? Logger::Urgency(entry
.d_priority
) : Logger::Info
;
942 if (u
> s_logUrgency
) {
943 // We do not log anything if the Urgency of the message is lower than the requested loglevel.
944 // Not that lower Urgency means higher number.
948 buf
<< "msg=" << std::quoted(entry
.message
);
950 buf
<< " error=" << std::quoted(entry
.error
.get());
954 buf
<< " subsystem=" << std::quoted(entry
.name
.get());
956 buf
<< " level=" << std::quoted(std::to_string(entry
.level
));
957 if (entry
.d_priority
) {
958 buf
<< " prio=" << std::quoted(Logr::Logger::toString(entry
.d_priority
));
960 // Thread id filled in by backend, since the SL code does not know about RecursorThreads
961 // We use the Recursor thread, other threads get id 0. May need to revisit.
962 buf
<< " tid=" << std::quoted(std::to_string(RecThreadInfo::id()));
964 buf
<< " ts=" << std::quoted(toTimestampStringMilli(entry
.d_timestamp
, timebuf
, sizeof(timebuf
)));
965 for (auto const& v
: entry
.values
) {
967 buf
<< v
.first
<< "=" << std::quoted(v
.second
);
970 g_log
<< u
<< buf
.str() << endl
;
973 static int ratePercentage(uint64_t nom
, uint64_t denom
)
978 return round(100.0 * nom
/ denom
);
981 static void doStats(void)
983 static time_t lastOutputTime
;
984 static uint64_t lastQueryCount
;
986 uint64_t cacheHits
= g_recCache
->cacheHits
;
987 uint64_t cacheMisses
= g_recCache
->cacheMisses
;
988 uint64_t cacheSize
= g_recCache
->size();
989 auto rc_stats
= g_recCache
->stats();
990 double r
= rc_stats
.second
== 0 ? 0.0 : (100.0 * rc_stats
.first
/ rc_stats
.second
);
991 uint64_t negCacheSize
= g_negCache
->size();
992 auto taskPushes
= getTaskPushes();
993 auto taskExpired
= getTaskExpired();
994 auto taskSize
= getTaskSize();
995 uint64_t pcSize
= broadcastAccFunction
<uint64_t>(pleaseGetPacketCacheSize
);
996 uint64_t pcHits
= broadcastAccFunction
<uint64_t>(pleaseGetPacketCacheHits
);
998 auto log
= g_slog
->withName("stats");
1000 if (g_stats
.qcounter
&& (cacheHits
+ cacheMisses
) && SyncRes::s_queries
&& SyncRes::s_outqueries
) {
1001 if (!g_slogStructured
) {
1002 g_log
<< Logger::Notice
<< "stats: " << g_stats
.qcounter
<< " questions, " << cacheSize
<< " cache entries, " << negCacheSize
<< " negative entries, " << ratePercentage(cacheHits
, cacheHits
+ cacheMisses
) << "% cache hits" << endl
;
1003 g_log
<< Logger::Notice
<< "stats: cache contended/acquired " << rc_stats
.first
<< '/' << rc_stats
.second
<< " = " << r
<< '%' << endl
;
1005 g_log
<< Logger::Notice
<< "stats: throttle map: "
1006 << SyncRes::getThrottledServersSize() << ", ns speeds: "
1007 << SyncRes::getNSSpeedsSize() << ", failed ns: "
1008 << SyncRes::getFailedServersSize() << ", ednsmap: "
1009 << SyncRes::getEDNSStatusesSize() << ", non-resolving: "
1010 << SyncRes::getNonResolvingNSSize() << ", saved-parentsets: "
1011 << SyncRes::getSaveParentsNSSetsSize()
1013 g_log
<< Logger::Notice
<< "stats: outpacket/query ratio " << ratePercentage(SyncRes::s_outqueries
, SyncRes::s_queries
) << "%";
1014 g_log
<< Logger::Notice
<< ", " << ratePercentage(SyncRes::s_throttledqueries
, SyncRes::s_outqueries
+ SyncRes::s_throttledqueries
) << "% throttled" << endl
;
1015 g_log
<< Logger::Notice
<< "stats: " << SyncRes::s_tcpoutqueries
<< "/" << SyncRes::s_dotoutqueries
<< "/" << getCurrentIdleTCPConnections() << " outgoing tcp/dot/idle connections, " << broadcastAccFunction
<uint64_t>(pleaseGetConcurrentQueries
) << " queries running, " << SyncRes::s_outgoingtimeouts
<< " outgoing timeouts " << endl
;
1017 g_log
<< Logger::Notice
<< "stats: " << pcSize
<< " packet cache entries, " << ratePercentage(pcHits
, g_stats
.qcounter
) << "% packet cache hits" << endl
;
1019 g_log
<< Logger::Notice
<< "stats: tasks pushed/expired/queuesize: " << taskPushes
<< '/' << taskExpired
<< '/' << taskSize
<< endl
;
1022 const string m
= "Periodic statistics report";
1023 log
->info(Logr::Info
, m
,
1024 "questions", Logging::Loggable(g_stats
.qcounter
),
1025 "cache-entries", Logging::Loggable(cacheSize
),
1026 "negcache-entries", Logging::Loggable(negCacheSize
),
1027 "record-cache-hitratio-perc", Logging::Loggable(ratePercentage(cacheHits
, cacheHits
+ cacheMisses
)),
1028 "record-cache-contended", Logging::Loggable(rc_stats
.first
),
1029 "record-cache-acquired", Logging::Loggable(rc_stats
.second
),
1030 "record-cache-contended-perc", Logging::Loggable(r
));
1031 log
->info(Logr::Info
, m
,
1032 "throttle-entries", Logging::Loggable(SyncRes::getThrottledServersSize()),
1033 "nsspeed-entries", Logging::Loggable(SyncRes::getNSSpeedsSize()),
1034 "failed-host-entries", Logging::Loggable(SyncRes::getFailedServersSize()),
1035 "edns-entries", Logging::Loggable(SyncRes::getEDNSStatusesSize()),
1036 "non-resolving-nameserver-entries", Logging::Loggable(SyncRes::getNonResolvingNSSize()),
1037 "saved-parent-ns-sets-entries", Logging::Loggable(SyncRes::getSaveParentsNSSetsSize()),
1038 "outqueries-per-query", Logging::Loggable(ratePercentage(SyncRes::s_outqueries
, SyncRes::s_queries
)));
1039 log
->info(Logr::Info
, m
,
1040 "throttled-queries-perc", Logging::Loggable(ratePercentage(SyncRes::s_throttledqueries
, SyncRes::s_outqueries
+ SyncRes::s_throttledqueries
)),
1041 "tcp-outqueries", Logging::Loggable(SyncRes::s_tcpoutqueries
),
1042 "dot-outqueries", Logging::Loggable(SyncRes::s_dotoutqueries
),
1043 "idle-tcpout-connections", Logging::Loggable(getCurrentIdleTCPConnections()),
1044 "concurrent-queries", Logging::Loggable(broadcastAccFunction
<uint64_t>(pleaseGetConcurrentQueries
)),
1045 "outgoing-timeouts", Logging::Loggable(SyncRes::s_outgoingtimeouts
));
1046 log
->info(Logr::Info
, m
,
1047 "packetcache-entries", Logging::Loggable(pcSize
),
1048 "packetcache-hitratio-perc", Logging::Loggable(ratePercentage(pcHits
, g_stats
.qcounter
)),
1049 "taskqueue-pushed", Logging::Loggable(taskPushes
),
1050 "taskqueue-expired", Logging::Loggable(taskExpired
),
1051 "taskqueue-size", Logging::Loggable(taskSize
));
1054 for (const auto& threadInfo
: RecThreadInfo::infos()) {
1055 if (threadInfo
.isWorker()) {
1056 SLOG(g_log
<< Logger::Notice
<< "stats: thread " << idx
<< " has been distributed " << threadInfo
.numberOfDistributedQueries
<< " queries" << endl
,
1057 log
->info(Logr::Info
, "Queries handled by thread", "thread", Logging::Loggable(idx
), "count", Logging::Loggable(threadInfo
.numberOfDistributedQueries
)));
1061 time_t now
= time(0);
1062 if (lastOutputTime
&& lastQueryCount
&& now
!= lastOutputTime
) {
1063 SLOG(g_log
<< Logger::Notice
<< "stats: " << (g_stats
.qcounter
- lastQueryCount
) / (now
- lastOutputTime
) << " qps (average over " << (now
- lastOutputTime
) << " seconds)" << endl
,
1064 log
->info(Logr::Info
, "Periodic QPS report", "qps", Logging::Loggable((g_stats
.qcounter
- lastQueryCount
) / (now
- lastOutputTime
)),
1065 "averagedOver", Logging::Loggable(now
- lastOutputTime
)));
1067 lastOutputTime
= now
;
1068 lastQueryCount
= g_stats
.qcounter
;
1070 else if (statsWanted
) {
1071 SLOG(g_log
<< Logger::Notice
<< "stats: no stats yet!" << endl
,
1072 log
->info(Logr::Notice
, "No stats yet"));
1075 statsWanted
= false;
1078 static std::shared_ptr
<NetmaskGroup
> parseACL(const std::string
& aclFile
, const std::string
& aclSetting
, Logr::log_t log
)
1080 auto result
= std::make_shared
<NetmaskGroup
>();
1082 if (!::arg()[aclFile
].empty()) {
1084 ifstream
ifs(::arg()[aclFile
].c_str());
1086 throw runtime_error("Could not open '" + ::arg()[aclFile
] + "': " + stringerror());
1089 string::size_type pos
;
1090 while (getline(ifs
, line
)) {
1091 pos
= line
.find('#');
1092 if (pos
!= string::npos
)
1098 result
->addMask(line
);
1100 SLOG(g_log
<< Logger::Info
<< "Done parsing " << result
->size() << " " << aclSetting
<< " ranges from file '" << ::arg()[aclFile
] << "' - overriding '" << aclSetting
<< "' setting" << endl
,
1101 log
->info(Logr::Info
, "Done parsing ranges from file, will override setting", "setting", Logging::Loggable(aclSetting
),
1102 "number", Logging::Loggable(result
->size()), "file", Logging::Loggable(::arg()[aclFile
])));
1104 else if (!::arg()[aclSetting
].empty()) {
1106 stringtok(ips
, ::arg()[aclSetting
], ", ");
1108 for (const auto& i
: ips
) {
1111 if (!g_slogStructured
) {
1112 g_log
<< Logger::Info
<< aclSetting
<< ": ";
1113 for (vector
<string
>::const_iterator i
= ips
.begin(); i
!= ips
.end(); ++i
) {
1114 if (i
!= ips
.begin())
1115 g_log
<< Logger::Info
<< ", ";
1116 g_log
<< Logger::Info
<< *i
;
1118 g_log
<< Logger::Info
<< endl
;
1121 log
->info(Logr::Info
, "Setting access control", "acl", Logging::Loggable(aclSetting
), "addresses", Logging::IterLoggable(ips
.begin(), ips
.end()));
1128 static void* pleaseSupplantAllowFrom(std::shared_ptr
<NetmaskGroup
> ng
)
1134 static void* pleaseSupplantAllowNotifyFrom(std::shared_ptr
<NetmaskGroup
> ng
)
1136 t_allowNotifyFrom
= ng
;
1140 void* pleaseSupplantAllowNotifyFor(std::shared_ptr
<notifyset_t
> ns
)
1142 t_allowNotifyFor
= ns
;
1148 auto log
= g_slog
->withName("config");
1150 static bool l_initialized
;
1152 if (l_initialized
) { // only reload configuration file on second call
1153 string configName
= ::arg()["config-dir"] + "/recursor.conf";
1154 if (!::arg()["config-name"].empty()) {
1155 configName
= ::arg()["config-dir"] + "/recursor-" + ::arg()["config-name"] + ".conf";
1157 cleanSlashes(configName
);
1159 if (!::arg().preParseFile(configName
.c_str(), "allow-from-file")) {
1160 throw runtime_error("Unable to re-parse configuration file '" + configName
+ "'");
1162 ::arg().preParseFile(configName
.c_str(), "allow-from", LOCAL_NETS
);
1164 if (!::arg().preParseFile(configName
.c_str(), "allow-notify-from-file")) {
1165 throw runtime_error("Unable to re-parse configuration file '" + configName
+ "'");
1167 ::arg().preParseFile(configName
.c_str(), "allow-notify-from");
1169 ::arg().preParseFile(configName
.c_str(), "include-dir");
1170 ::arg().preParse(g_argc
, g_argv
, "include-dir");
1172 // then process includes
1173 std::vector
<std::string
> extraConfigs
;
1174 ::arg().gatherIncludes(extraConfigs
);
1176 for (const std::string
& fileName
: extraConfigs
) {
1177 if (!::arg().preParseFile(fileName
.c_str(), "allow-from-file", ::arg()["allow-from-file"])) {
1178 throw runtime_error("Unable to re-parse configuration file include '" + fileName
+ "'");
1180 if (!::arg().preParseFile(fileName
.c_str(), "allow-from", ::arg()["allow-from"])) {
1181 throw runtime_error("Unable to re-parse configuration file include '" + fileName
+ "'");
1184 if (!::arg().preParseFile(fileName
.c_str(), "allow-notify-from-file", ::arg()["allow-notify-from-file"])) {
1185 throw runtime_error("Unable to re-parse configuration file include '" + fileName
+ "'");
1187 if (!::arg().preParseFile(fileName
.c_str(), "allow-notify-from", ::arg()["allow-notify-from"])) {
1188 throw runtime_error("Unable to re-parse configuration file include '" + fileName
+ "'");
1192 ::arg().preParse(g_argc
, g_argv
, "allow-from-file");
1193 ::arg().preParse(g_argc
, g_argv
, "allow-from");
1195 ::arg().preParse(g_argc
, g_argv
, "allow-notify-from-file");
1196 ::arg().preParse(g_argc
, g_argv
, "allow-notify-from");
1199 auto allowFrom
= parseACL("allow-from-file", "allow-from", log
);
1201 if (allowFrom
->empty()) {
1202 if (::arg()["local-address"] != "127.0.0.1" && ::arg().asNum("local-port") == 53) {
1203 SLOG(g_log
<< Logger::Warning
<< "WARNING: Allowing queries from all IP addresses - this can be a security risk!" << endl
,
1204 log
->info(Logr::Warning
, "WARNING: Allowing queries from all IP addresses - this can be a security risk!"));
1206 allowFrom
= nullptr;
1209 g_initialAllowFrom
= allowFrom
;
1210 broadcastFunction([=] { return pleaseSupplantAllowFrom(allowFrom
); });
1212 auto allowNotifyFrom
= parseACL("allow-notify-from-file", "allow-notify-from", log
);
1214 g_initialAllowNotifyFrom
= allowNotifyFrom
;
1215 broadcastFunction([=] { return pleaseSupplantAllowNotifyFrom(allowNotifyFrom
); });
1217 l_initialized
= true;
1220 void broadcastFunction(const pipefunc_t
& func
)
1222 /* This function might be called by the worker with t_id 0 during startup
1223 for the initialization of ACLs and domain maps. After that it should only
1224 be called by the handler. */
1226 if (RecThreadInfo::infos().empty() && RecThreadInfo::id() == 0) {
1227 /* the handler and distributors will call themselves below, but
1228 during startup we get called while g_threadInfos has not been
1229 populated yet to update the ACL or domain maps, so we need to
1236 for (const auto& threadInfo
: RecThreadInfo::infos()) {
1237 if (n
++ == RecThreadInfo::id()) {
1238 func(); // don't write to ourselves!
1242 ThreadMSG
* tmsg
= new ThreadMSG();
1244 tmsg
->wantAnswer
= true;
1245 if (write(threadInfo
.pipes
.writeToThread
, &tmsg
, sizeof(tmsg
)) != sizeof(tmsg
)) {
1248 unixDie("write to thread pipe returned wrong size or error");
1251 string
* resp
= nullptr;
1252 if (read(threadInfo
.pipes
.readFromThread
, &resp
, sizeof(resp
)) != sizeof(resp
))
1253 unixDie("read from thread pipe returned wrong size or error");
1259 // coverity[leaked_storage]
1264 void* voider(const std::function
<T
*()>& func
)
1269 static vector
<ComboAddress
>& operator+=(vector
<ComboAddress
>& a
, const vector
<ComboAddress
>& b
)
1271 a
.insert(a
.end(), b
.begin(), b
.end());
1275 static vector
<pair
<DNSName
, uint16_t>>& operator+=(vector
<pair
<DNSName
, uint16_t>>& a
, const vector
<pair
<DNSName
, uint16_t>>& b
)
1277 a
.insert(a
.end(), b
.begin(), b
.end());
1281 static ProxyMappingStats_t
& operator+=(ProxyMappingStats_t
& a
, const ProxyMappingStats_t
& b
)
1283 for (const auto& [key
, entry
] : b
) {
1284 a
[key
].netmaskMatches
+= entry
.netmaskMatches
;
1285 a
[key
].suffixMatches
+= entry
.suffixMatches
;
1290 static RemoteLoggerStats_t
& operator+=(RemoteLoggerStats_t
& a
, const RemoteLoggerStats_t
& b
)
1292 for (const auto& [key
, entry
] : b
) {
1298 // This function should only be called by the handler to gather
1299 // metrics, wipe the cache, reload the Lua script (not the Lua config)
1300 // or change the current trace regex, and by the SNMP thread to gather
1302 // Note that this currently skips the handler, but includes the taskThread(s).
1304 T
broadcastAccFunction(const std::function
<T
*()>& func
)
1306 if (!RecThreadInfo::self().isHandler()) {
1307 SLOG(g_log
<< Logger::Error
<< "broadcastAccFunction has been called by a worker (" << RecThreadInfo::id() << ")" << endl
,
1308 g_slog
->withName("runtime")->info(Logr::Critical
, "broadcastAccFunction has been called by a worker")); // tid will be added
1314 for (const auto& threadInfo
: RecThreadInfo::infos()) {
1315 if (n
++ == RecThreadInfo::id()) {
1319 const auto& tps
= threadInfo
.pipes
;
1320 ThreadMSG
* tmsg
= new ThreadMSG();
1321 tmsg
->func
= [func
] { return voider
<T
>(func
); };
1322 tmsg
->wantAnswer
= true;
1324 if (write(tps
.writeToThread
, &tmsg
, sizeof(tmsg
)) != sizeof(tmsg
)) {
1326 unixDie("write to thread pipe returned wrong size or error");
1330 if (read(tps
.readFromThread
, &resp
, sizeof(resp
)) != sizeof(resp
))
1331 unixDie("read from thread pipe returned wrong size or error");
1338 // coverity[leaked_storage]
1343 template string
broadcastAccFunction(const std::function
<string
*()>& fun
); // explicit instantiation
1344 template RecursorControlChannel::Answer
broadcastAccFunction(const std::function
<RecursorControlChannel::Answer
*()>& fun
); // explicit instantiation
1345 template uint64_t broadcastAccFunction(const std::function
<uint64_t*()>& fun
); // explicit instantiation
1346 template vector
<ComboAddress
> broadcastAccFunction(const std::function
<vector
<ComboAddress
>*()>& fun
); // explicit instantiation
1347 template vector
<pair
<DNSName
, uint16_t>> broadcastAccFunction(const std::function
<vector
<pair
<DNSName
, uint16_t>>*()>& fun
); // explicit instantiation
1348 template ThreadTimes
broadcastAccFunction(const std::function
<ThreadTimes
*()>& fun
);
1349 template ProxyMappingStats_t
broadcastAccFunction(const std::function
<ProxyMappingStats_t
*()>& fun
);
1350 template RemoteLoggerStats_t
broadcastAccFunction(const std::function
<RemoteLoggerStats_t
*()>& fun
);
1352 static int serviceMain(int argc
, char* argv
[], Logr::log_t log
)
1354 g_log
.setName(g_programname
);
1355 g_log
.disableSyslog(::arg().mustDo("disable-syslog"));
1356 g_log
.setTimestamps(::arg().mustDo("log-timestamp"));
1358 if (!::arg()["logging-facility"].empty()) {
1359 int val
= logFacilityToLOG(::arg().asNum("logging-facility"));
1361 g_log
.setFacility(val
);
1363 SLOG(g_log
<< Logger::Error
<< "Unknown logging facility " << ::arg().asNum("logging-facility") << endl
,
1364 log
->info(Logr::Error
, "Unknown logging facility", "facility", Logging::Loggable(::arg().asNum("logging-facility"))));
1368 showProductVersion();
1370 g_disthashseed
= dns_random(0xffffffff);
1372 checkLinuxIPv6Limits(log
);
1374 pdns::parseQueryLocalAddress(::arg()["query-local-address"]);
1376 catch (std::exception
& e
) {
1377 SLOG(g_log
<< Logger::Error
<< "Assigning local query addresses: " << e
.what(),
1378 log
->error(Logr::Error
, e
.what(), "Unable to assign local query address"));
1382 if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET
)) {
1383 SyncRes::s_doIPv4
= true;
1384 SLOG(g_log
<< Logger::Warning
<< "Enabling IPv4 transport for outgoing queries" << endl
,
1385 log
->info(Logr::Notice
, "Enabling IPv4 transport for outgoing queries"));
1388 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
,
1389 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"));
1392 if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET6
)) {
1393 SyncRes::s_doIPv6
= true;
1394 SLOG(g_log
<< Logger::Warning
<< "Enabling IPv6 transport for outgoing queries" << endl
,
1395 log
->info(Logr::Notice
, "Enabling IPv6 transport for outgoing queries"));
1398 SLOG(g_log
<< Logger::Warning
<< "NOT using IPv6 for outgoing queries - add an IPv6 address (like '::') to query-local-address to enable" << endl
,
1399 log
->info(Logr::Warning
, "NOT using IPv6 for outgoing queries - add an IPv6 address (like '::') to query-local-address to enable"));
1402 if (!SyncRes::s_doIPv6
&& !SyncRes::s_doIPv4
) {
1403 SLOG(g_log
<< Logger::Error
<< "No outgoing addresses configured! Can not continue" << endl
,
1404 log
->info(Logr::Error
, "No outgoing addresses configured! Can not continue"));
1408 // keep this ABOVE loadRecursorLuaConfig!
1409 if (::arg()["dnssec"] == "off")
1410 g_dnssecmode
= DNSSECMode::Off
;
1411 else if (::arg()["dnssec"] == "process-no-validate")
1412 g_dnssecmode
= DNSSECMode::ProcessNoValidate
;
1413 else if (::arg()["dnssec"] == "process")
1414 g_dnssecmode
= DNSSECMode::Process
;
1415 else if (::arg()["dnssec"] == "validate")
1416 g_dnssecmode
= DNSSECMode::ValidateAll
;
1417 else if (::arg()["dnssec"] == "log-fail")
1418 g_dnssecmode
= DNSSECMode::ValidateForLog
;
1420 SLOG(g_log
<< Logger::Error
<< "Unknown DNSSEC mode " << ::arg()["dnssec"] << endl
,
1421 log
->info(Logr::Error
, "Unknown DNSSEC mode", "dnssec", Logging::Loggable(::arg()["dnssec"])));
1425 g_signatureInceptionSkew
= ::arg().asNum("signature-inception-skew");
1426 if (g_signatureInceptionSkew
< 0) {
1427 SLOG(g_log
<< Logger::Error
<< "A negative value for 'signature-inception-skew' is not allowed" << endl
,
1428 log
->info(Logr::Error
, "A negative value for 'signature-inception-skew' is not allowed"));
1432 g_dnssecLogBogus
= ::arg().mustDo("dnssec-log-bogus");
1433 g_maxNSEC3Iterations
= ::arg().asNum("nsec3-max-iterations");
1435 g_maxCacheEntries
= ::arg().asNum("max-cache-entries");
1436 g_maxPacketCacheEntries
= ::arg().asNum("max-packetcache-entries");
1438 luaConfigDelayedThreads delayedLuaThreads
;
1440 ProxyMapping proxyMapping
;
1441 loadRecursorLuaConfig(::arg()["lua-config-file"], delayedLuaThreads
, proxyMapping
);
1442 // Initial proxy mapping
1443 g_proxyMapping
= proxyMapping
.empty() ? nullptr : std::make_unique
<ProxyMapping
>(proxyMapping
);
1445 catch (PDNSException
& e
) {
1446 SLOG(g_log
<< Logger::Error
<< "Cannot load Lua configuration: " << e
.reason
<< endl
,
1447 log
->error(Logr::Error
, e
.reason
, "Cannot load Lua configuration"));
1452 initPublicSuffixList(::arg()["public-suffix-list-file"]);
1454 if (!::arg()["dont-query"].empty()) {
1456 stringtok(ips
, ::arg()["dont-query"], ", ");
1457 ips
.push_back("0.0.0.0");
1458 ips
.push_back("::");
1460 for (const auto& ip
: ips
) {
1461 SyncRes::addDontQuery(ip
);
1463 if (!g_slogStructured
) {
1464 g_log
<< Logger::Warning
<< "Will not send queries to: ";
1465 for (vector
<string
>::const_iterator i
= ips
.begin(); i
!= ips
.end(); ++i
) {
1466 if (i
!= ips
.begin())
1467 g_log
<< Logger::Warning
<< ", ";
1468 g_log
<< Logger::Warning
<< *i
;
1470 g_log
<< Logger::Warning
<< endl
;
1473 log
->info(Logr::Notice
, "Will not send queries to", "addresses", Logging::IterLoggable(ips
.begin(), ips
.end()));
1477 /* this needs to be done before parseACLs(), which call broadcastFunction() */
1478 RecThreadInfo::setWeDistributeQueries(::arg().mustDo("pdns-distributes-queries"));
1479 if (RecThreadInfo::weDistributeQueries()) {
1480 SLOG(g_log
<< Logger::Warning
<< "PowerDNS Recursor itself will distribute queries over threads" << endl
,
1481 log
->info(Logr::Notice
, "PowerDNS Recursor itself will distribute queries over threads"));
1484 g_outgoingEDNSBufsize
= ::arg().asNum("edns-outgoing-bufsize");
1486 if (::arg()["trace"] == "fail") {
1487 SyncRes::setDefaultLogMode(SyncRes::Store
);
1489 else if (::arg().mustDo("trace")) {
1490 SyncRes::setDefaultLogMode(SyncRes::Log
);
1491 ::arg().set("quiet") = "no";
1495 auto myHostname
= getHostname();
1496 if (!myHostname
.has_value()) {
1497 SLOG(g_log
<< Logger::Warning
<< "Unable to get the hostname, NSID and id.server values will be empty" << endl
,
1498 log
->info(Logr::Warning
, "Unable to get the hostname, NSID and id.server values will be empty"));
1501 SyncRes::s_minimumTTL
= ::arg().asNum("minimum-ttl-override");
1502 SyncRes::s_minimumECSTTL
= ::arg().asNum("ecs-minimum-ttl-override");
1503 SyncRes::s_maxnegttl
= ::arg().asNum("max-negative-ttl");
1504 SyncRes::s_maxbogusttl
= ::arg().asNum("max-cache-bogus-ttl");
1505 SyncRes::s_maxcachettl
= max(::arg().asNum("max-cache-ttl"), 15);
1506 SyncRes::s_packetcachettl
= ::arg().asNum("packetcache-ttl");
1507 // Cap the packetcache-servfail-ttl to the packetcache-ttl
1508 uint32_t packetCacheServFailTTL
= ::arg().asNum("packetcache-servfail-ttl");
1509 SyncRes::s_packetcacheservfailttl
= (packetCacheServFailTTL
> SyncRes::s_packetcachettl
) ? SyncRes::s_packetcachettl
: packetCacheServFailTTL
;
1510 SyncRes::s_serverdownmaxfails
= ::arg().asNum("server-down-max-fails");
1511 SyncRes::s_serverdownthrottletime
= ::arg().asNum("server-down-throttle-time");
1512 SyncRes::s_nonresolvingnsmaxfails
= ::arg().asNum("non-resolving-ns-max-fails");
1513 SyncRes::s_nonresolvingnsthrottletime
= ::arg().asNum("non-resolving-ns-throttle-time");
1514 SyncRes::s_serverID
= ::arg()["server-id"];
1515 SyncRes::s_maxqperq
= ::arg().asNum("max-qperq");
1516 SyncRes::s_maxnsperresolve
= ::arg().asNum("max-ns-per-resolve");
1517 SyncRes::s_maxnsaddressqperq
= ::arg().asNum("max-ns-address-qperq");
1518 SyncRes::s_maxtotusec
= 1000 * ::arg().asNum("max-total-msec");
1519 SyncRes::s_maxdepth
= ::arg().asNum("max-recursion-depth");
1520 SyncRes::s_rootNXTrust
= ::arg().mustDo("root-nx-trust");
1521 SyncRes::s_refresh_ttlperc
= ::arg().asNum("refresh-on-ttl-perc");
1522 SyncRes::s_locked_ttlperc
= ::arg().asNum("record-cache-locked-ttl-perc");
1523 RecursorPacketCache::s_refresh_ttlperc
= SyncRes::s_refresh_ttlperc
;
1524 SyncRes::s_tcp_fast_open
= ::arg().asNum("tcp-fast-open");
1525 SyncRes::s_tcp_fast_open_connect
= ::arg().mustDo("tcp-fast-open-connect");
1527 SyncRes::s_dot_to_port_853
= ::arg().mustDo("dot-to-port-853");
1528 SyncRes::s_event_trace_enabled
= ::arg().asNum("event-trace-enabled");
1529 SyncRes::s_save_parent_ns_set
= ::arg().mustDo("save-parent-ns-set");
1530 SyncRes::s_max_busy_dot_probes
= ::arg().asNum("max-busy-dot-probes");
1532 uint64_t sse
= ::arg().asNum("serve-stale-extensions");
1533 if (sse
> std::numeric_limits
<uint16_t>::max()) {
1534 SLOG(g_log
<< Logger::Error
<< "Illegal serve-stale-extensions value: " << sse
<< "; range = 0..65536" << endl
,
1535 log
->info(Logr::Error
, "Illegal serve-stale-extensions value; range = 0..65536", "value", Logging::Loggable(sse
)));
1538 MemRecursorCache::s_maxServedStaleExtensions
= sse
;
1539 NegCache::s_maxServedStaleExtensions
= sse
;
1542 if (SyncRes::s_tcp_fast_open_connect
) {
1543 checkFastOpenSysctl(true, log
);
1544 checkTFOconnect(log
);
1547 if (SyncRes::s_serverID
.empty()) {
1548 SyncRes::s_serverID
= myHostname
.has_value() ? *myHostname
: "";
1551 SyncRes::s_ecsipv4limit
= ::arg().asNum("ecs-ipv4-bits");
1552 SyncRes::s_ecsipv6limit
= ::arg().asNum("ecs-ipv6-bits");
1553 SyncRes::clearECSStats();
1554 SyncRes::s_ecsipv4cachelimit
= ::arg().asNum("ecs-ipv4-cache-bits");
1555 SyncRes::s_ecsipv6cachelimit
= ::arg().asNum("ecs-ipv6-cache-bits");
1556 SyncRes::s_ecsipv4nevercache
= ::arg().mustDo("ecs-ipv4-never-cache");
1557 SyncRes::s_ecsipv6nevercache
= ::arg().mustDo("ecs-ipv6-never-cache");
1558 SyncRes::s_ecscachelimitttl
= ::arg().asNum("ecs-cache-limit-ttl");
1560 SyncRes::s_qnameminimization
= ::arg().mustDo("qname-minimization");
1562 if (SyncRes::s_qnameminimization
) {
1563 // With an empty cache, a rev ipv6 query with dnssec enabled takes
1564 // almost 100 queries. Default maxqperq is 60.
1565 SyncRes::s_maxqperq
= std::max(SyncRes::s_maxqperq
, static_cast<unsigned int>(100));
1568 SyncRes::s_hardenNXD
= SyncRes::HardenNXD::DNSSEC
;
1569 string value
= ::arg()["nothing-below-nxdomain"];
1570 if (value
== "yes") {
1571 SyncRes::s_hardenNXD
= SyncRes::HardenNXD::Yes
;
1573 else if (value
== "no") {
1574 SyncRes::s_hardenNXD
= SyncRes::HardenNXD::No
;
1576 else if (value
!= "dnssec") {
1577 SLOG(g_log
<< Logger::Error
<< "Unknown nothing-below-nxdomain mode: " << value
<< endl
,
1578 log
->info(Logr::Error
, "Unknown nothing-below-nxdomain mode", "mode", Logging::Loggable(value
)));
1582 if (!::arg().isEmpty("ecs-scope-zero-address")) {
1583 ComboAddress
scopeZero(::arg()["ecs-scope-zero-address"]);
1584 SyncRes::setECSScopeZeroAddress(Netmask(scopeZero
, scopeZero
.isIPv4() ? 32 : 128));
1590 auto addr
= pdns::getNonAnyQueryLocalAddress(AF_INET
);
1591 if (addr
.sin4
.sin_family
!= 0) {
1592 nm
= Netmask(addr
, 32);
1596 addr
= pdns::getNonAnyQueryLocalAddress(AF_INET6
);
1597 if (addr
.sin4
.sin_family
!= 0) {
1598 nm
= Netmask(addr
, 128);
1603 nm
= Netmask(ComboAddress("127.0.0.1"), 32);
1605 SyncRes::setECSScopeZeroAddress(nm
);
1608 SyncRes::parseEDNSSubnetAllowlist(::arg()["edns-subnet-whitelist"]);
1609 SyncRes::parseEDNSSubnetAllowlist(::arg()["edns-subnet-allow-list"]);
1610 SyncRes::parseEDNSSubnetAddFor(::arg()["ecs-add-for"]);
1611 g_useIncomingECS
= ::arg().mustDo("use-incoming-edns-subnet");
1613 g_proxyProtocolACL
.toMasks(::arg()["proxy-protocol-from"]);
1614 g_proxyProtocolMaximumSize
= ::arg().asNum("proxy-protocol-maximum-size");
1616 if (!::arg()["dns64-prefix"].empty()) {
1618 auto dns64Prefix
= Netmask(::arg()["dns64-prefix"]);
1619 if (dns64Prefix
.getBits() != 96) {
1620 SLOG(g_log
<< Logger::Error
<< "Invalid prefix for 'dns64-prefix', the current implementation only supports /96 prefixes: " << ::arg()["dns64-prefix"] << endl
,
1621 log
->info(Logr::Error
, "Invalid prefix for 'dns64-prefix', the current implementation only supports /96 prefixes", "prefix", Logging::Loggable(::arg()["dns64-prefix"])));
1624 g_dns64Prefix
= dns64Prefix
.getNetwork();
1625 g_dns64PrefixReverse
= reverseNameFromIP(*g_dns64Prefix
);
1626 /* /96 is 24 nibbles + 2 for "ip6.arpa." */
1627 while (g_dns64PrefixReverse
.countLabels() > 26) {
1628 g_dns64PrefixReverse
.chopOff();
1631 catch (const NetmaskException
& ne
) {
1632 SLOG(g_log
<< Logger::Error
<< "Invalid prefix '" << ::arg()["dns64-prefix"] << "' for 'dns64-prefix': " << ne
.reason
<< endl
,
1633 log
->info(Logr::Error
, "Invalid prefix", "dns64-prefix", Logging::Loggable(::arg()["dns64-prefix"])));
1638 g_networkTimeoutMsec
= ::arg().asNum("network-timeout");
1640 std::tie(g_initialDomainMap
, g_initialAllowNotifyFor
) = parseZoneConfiguration();
1642 g_latencyStatSize
= ::arg().asNum("latency-statistic-size");
1644 g_logCommonErrors
= ::arg().mustDo("log-common-errors");
1645 g_logRPZChanges
= ::arg().mustDo("log-rpz-changes");
1647 g_anyToTcp
= ::arg().mustDo("any-to-tcp");
1648 g_udpTruncationThreshold
= ::arg().asNum("udp-truncation-threshold");
1650 g_lowercaseOutgoing
= ::arg().mustDo("lowercase-outgoing");
1652 g_paddingFrom
.toMasks(::arg()["edns-padding-from"]);
1653 if (::arg()["edns-padding-mode"] == "always") {
1654 g_paddingMode
= PaddingMode::Always
;
1656 else if (::arg()["edns-padding-mode"] == "padded-queries-only") {
1657 g_paddingMode
= PaddingMode::PaddedQueries
;
1660 SLOG(g_log
<< Logger::Error
<< "Unknown edns-padding-mode: " << ::arg()["edns-padding-mode"] << endl
,
1661 log
->info(Logr::Error
, "Unknown edns-padding-mode", "edns-padding-mode", Logging::Loggable(::arg()["edns-padding-mode"])));
1664 g_paddingTag
= ::arg().asNum("edns-padding-tag");
1665 g_paddingOutgoing
= ::arg().mustDo("edns-padding-out");
1667 RecThreadInfo::setNumDistributorThreads(::arg().asNum("distributor-threads"));
1668 RecThreadInfo::setNumWorkerThreads(::arg().asNum("threads"));
1669 if (RecThreadInfo::numWorkers() < 1) {
1670 SLOG(g_log
<< Logger::Warning
<< "Asked to run with 0 threads, raising to 1 instead" << endl
,
1671 log
->info(Logr::Warning
, "Asked to run with 0 threads, raising to 1 instead"));
1672 RecThreadInfo::setNumWorkerThreads(1);
1675 g_maxMThreads
= ::arg().asNum("max-mthreads");
1677 int64_t maxInFlight
= ::arg().asNum("max-concurrent-requests-per-tcp-connection");
1678 if (maxInFlight
< 1 || maxInFlight
> USHRT_MAX
|| maxInFlight
>= g_maxMThreads
) {
1679 SLOG(g_log
<< Logger::Warning
<< "Asked to run with illegal max-concurrent-requests-per-tcp-connection, setting to default (10)" << endl
,
1680 log
->info(Logr::Warning
, "Asked to run with illegal max-concurrent-requests-per-tcp-connection, setting to default (10)"));
1681 TCPConnection::s_maxInFlight
= 10;
1684 TCPConnection::s_maxInFlight
= maxInFlight
;
1687 int64_t millis
= ::arg().asNum("tcp-out-max-idle-ms");
1688 TCPOutConnectionManager::s_maxIdleTime
= timeval
{millis
/ 1000, (static_cast<suseconds_t
>(millis
) % 1000) * 1000};
1689 TCPOutConnectionManager::s_maxIdlePerAuth
= ::arg().asNum("tcp-out-max-idle-per-auth");
1690 TCPOutConnectionManager::s_maxQueries
= ::arg().asNum("tcp-out-max-queries");
1691 TCPOutConnectionManager::s_maxIdlePerThread
= ::arg().asNum("tcp-out-max-idle-per-thread");
1693 g_gettagNeedsEDNSOptions
= ::arg().mustDo("gettag-needs-edns-options");
1695 s_statisticsInterval
= ::arg().asNum("statistics-interval");
1697 g_addExtendedResolutionDNSErrors
= ::arg().mustDo("extended-resolution-errors");
1699 if (::arg().asNum("aggressive-nsec-cache-size") > 0) {
1700 if (g_dnssecmode
== DNSSECMode::ValidateAll
|| g_dnssecmode
== DNSSECMode::ValidateForLog
|| g_dnssecmode
== DNSSECMode::Process
) {
1701 g_aggressiveNSECCache
= make_unique
<AggressiveNSECCache
>(::arg().asNum("aggressive-nsec-cache-size"));
1704 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
,
1705 log
->info(Logr::Warning
, "Aggressive NSEC/NSEC3 caching is enabled but DNSSEC validation is not set to 'validate', 'log-fail' or 'process', ignoring"));
1710 SuffixMatchNode dontThrottleNames
;
1711 vector
<string
> parts
;
1712 stringtok(parts
, ::arg()["dont-throttle-names"], " ,");
1713 for (const auto& p
: parts
) {
1714 dontThrottleNames
.add(DNSName(p
));
1716 g_dontThrottleNames
.setState(std::move(dontThrottleNames
));
1719 NetmaskGroup dontThrottleNetmasks
;
1720 stringtok(parts
, ::arg()["dont-throttle-netmasks"], " ,");
1721 for (const auto& p
: parts
) {
1722 dontThrottleNetmasks
.addMask(Netmask(p
));
1724 g_dontThrottleNetmasks
.setState(std::move(dontThrottleNetmasks
));
1728 SuffixMatchNode xdnssecNames
;
1729 vector
<string
> parts
;
1730 stringtok(parts
, ::arg()["x-dnssec-names"], " ,");
1731 for (const auto& p
: parts
) {
1732 xdnssecNames
.add(DNSName(p
));
1734 g_xdnssec
.setState(std::move(xdnssecNames
));
1738 SuffixMatchNode dotauthNames
;
1739 vector
<string
> parts
;
1740 stringtok(parts
, ::arg()["dot-to-auth-names"], " ,");
1741 #ifndef HAVE_DNS_OVER_TLS
1743 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
,
1744 log
->info(Logr::Error
, "dot-to-auth-names setting contains names, but Recursor was built without DNS over TLS support. Setting will be ignored"));
1747 for (const auto& p
: parts
) {
1748 dotauthNames
.add(DNSName(p
));
1750 g_DoTToAuthNames
.setState(std::move(dotauthNames
));
1754 CarbonConfig config
;
1755 stringtok(config
.servers
, arg()["carbon-server"], ", ");
1756 config
.hostname
= arg()["carbon-ourname"];
1757 config
.instance_name
= arg()["carbon-instance"];
1758 config
.namespace_name
= arg()["carbon-namespace"];
1759 g_carbonConfig
.setState(std::move(config
));
1762 g_balancingFactor
= ::arg().asDouble("distribution-load-factor");
1763 if (g_balancingFactor
!= 0.0 && g_balancingFactor
< 1.0) {
1764 g_balancingFactor
= 0.0;
1765 SLOG(g_log
<< Logger::Warning
<< "Asked to run with a distribution-load-factor below 1.0, disabling it instead" << endl
,
1766 log
->info(Logr::Warning
, "Asked to run with a distribution-load-factor below 1.0, disabling it instead"));
1770 g_reusePort
= ::arg().mustDo("reuseport");
1773 RecThreadInfo::infos().resize(RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + RecThreadInfo::numWorkers() + RecThreadInfo::numTaskThreads());
1776 if (RecThreadInfo::weDistributeQueries()) {
1777 /* first thread is the handler, then distributors */
1778 for (unsigned int threadId
= 1; threadId
<= RecThreadInfo::numDistributors(); threadId
++) {
1779 auto& info
= RecThreadInfo::info(threadId
);
1780 auto& deferredAdds
= info
.deferredAdds
;
1781 auto& tcpSockets
= info
.tcpSockets
;
1782 makeUDPServerSockets(deferredAdds
, log
);
1783 makeTCPServerSockets(deferredAdds
, tcpSockets
, log
);
1787 /* first thread is the handler, there is no distributor here and workers are accepting queries */
1788 for (unsigned int threadId
= 1; threadId
<= RecThreadInfo::numWorkers(); threadId
++) {
1789 auto& info
= RecThreadInfo::info(threadId
);
1790 auto& deferredAdds
= info
.deferredAdds
;
1791 auto& tcpSockets
= info
.tcpSockets
;
1792 makeUDPServerSockets(deferredAdds
, log
);
1793 makeTCPServerSockets(deferredAdds
, tcpSockets
, log
);
1798 std::set
<int> tcpSockets
;
1799 /* we don't have reuseport so we can only open one socket per
1800 listening addr:port and everyone will listen on it */
1801 makeUDPServerSockets(g_deferredAdds
, log
);
1802 makeTCPServerSockets(g_deferredAdds
, tcpSockets
, log
);
1804 /* every listener (so distributor if g_weDistributeQueries, workers otherwise)
1805 needs to listen to the shared sockets */
1806 if (RecThreadInfo::weDistributeQueries()) {
1807 /* first thread is the handler, then distributors */
1808 for (unsigned int threadId
= 1; threadId
<= RecThreadInfo::numDistributors(); threadId
++) {
1809 RecThreadInfo::info(threadId
).tcpSockets
= tcpSockets
;
1813 /* first thread is the handler, there is no distributor here and workers are accepting queries */
1814 for (unsigned int threadId
= 1; threadId
<= RecThreadInfo::numWorkers(); threadId
++) {
1815 RecThreadInfo::info(threadId
).tcpSockets
= tcpSockets
;
1821 // Setup newly observed domain globals
1823 #endif /* NOD_ENABLED */
1826 for (forks
= 0; forks
< ::arg().asNum("processes") - 1; ++forks
) {
1827 if (!fork()) // we are child
1831 if (::arg().mustDo("daemon")) {
1832 SLOG(g_log
<< Logger::Warning
<< "Calling daemonize, going to background" << endl
,
1833 log
->info(Logr::Warning
, "Calling daemonize, going to background"));
1834 g_log
.toConsole(Logger::Critical
);
1837 if (Utility::getpid() == 1) {
1838 /* We are running as pid 1, register sigterm and sigint handler
1840 The Linux kernel will handle SIGTERM and SIGINT for all processes, except PID 1.
1841 It assumes that the processes running as pid 1 is an "init" like system.
1842 For years, this was a safe assumption, but containers change that: in
1843 most (all?) container implementations, the application itself is running
1844 as pid 1. This means that sending signals to those applications, will not
1845 be handled by default. Results might be "your container not responding
1846 when asking it to stop", or "ctrl-c not working even when the app is
1847 running in the foreground inside a container".
1849 So TL;DR: If we're running pid 1 (container), we should handle SIGTERM and SIGINT ourselves */
1851 signal(SIGTERM
, termIntHandler
);
1852 signal(SIGINT
, termIntHandler
);
1855 signal(SIGUSR1
, usr1Handler
);
1856 signal(SIGUSR2
, usr2Handler
);
1857 signal(SIGPIPE
, SIG_IGN
);
1861 #ifdef HAVE_LIBSODIUM
1862 if (sodium_init() == -1) {
1863 SLOG(g_log
<< Logger::Error
<< "Unable to initialize sodium crypto library" << endl
,
1864 log
->info(Logr::Error
, "Unable to initialize sodium crypto library"));
1869 openssl_thread_setup();
1871 /* setup rng before chroot */
1874 if (::arg()["server-id"].empty()) {
1875 ::arg().set("server-id") = myHostname
.has_value() ? *myHostname
: "";
1879 if (!::arg()["setgid"].empty())
1880 newgid
= strToGID(::arg()["setgid"]);
1882 if (!::arg()["setuid"].empty())
1883 newuid
= strToUID(::arg()["setuid"]);
1885 Utility::dropGroupPrivs(newuid
, newgid
);
1887 if (!::arg()["chroot"].empty()) {
1890 ns
= getenv("NOTIFY_SOCKET");
1891 if (ns
!= nullptr) {
1892 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
,
1893 log
->info(Logr::Error
, "Unable to chroot when running from systemd. Please disable chroot= or set the 'Type' for this service to 'simple'"));
1897 if (chroot(::arg()["chroot"].c_str()) < 0 || chdir("/") < 0) {
1899 SLOG(g_log
<< Logger::Error
<< "Unable to chroot to '" + ::arg()["chroot"] + "': " << strerror(err
) << ", exiting" << endl
,
1900 log
->error(Logr::Error
, err
, "Unable to chroot", "chroot", Logging::Loggable(::arg()["chroot"])));
1904 SLOG(g_log
<< Logger::Info
<< "Chrooted to '" << ::arg()["chroot"] << "'" << endl
,
1905 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
1916 makeControlChannelSocket(::arg().asNum("processes") > 1 ? forks
: -1);
1918 Utility::dropUserPrivs(newuid
);
1920 /* we might still have capabilities remaining, for example if we have been started as root
1921 without --setuid (please don't do that) or as an unprivileged user with ambient capabilities
1922 like CAP_NET_BIND_SERVICE.
1926 catch (const std::exception
& e
) {
1927 SLOG(g_log
<< Logger::Warning
<< e
.what() << endl
,
1928 log
->error(Logr::Warning
, e
.what(), "Could not drop capabilities"));
1931 startLuaConfigDelayedThreads(delayedLuaThreads
, g_luaconfs
.getCopy().generation
);
1932 delayedLuaThreads
.rpzPrimaryThreads
.clear(); // no longer needed
1934 RecThreadInfo::makeThreadPipes(log
);
1936 g_tcpTimeout
= ::arg().asNum("client-tcp-timeout");
1937 g_maxTCPPerClient
= ::arg().asNum("max-tcp-per-client");
1938 g_tcpMaxQueriesPerConn
= ::arg().asNum("max-tcp-queries-per-connection");
1939 g_maxUDPQueriesPerRound
= ::arg().asNum("max-udp-queries-per-round");
1941 g_useKernelTimestamp
= ::arg().mustDo("protobuf-use-kernel-timestamp");
1943 disableStats(StatComponent::API
, ::arg()["stats-api-blacklist"]);
1944 disableStats(StatComponent::Carbon
, ::arg()["stats-carbon-blacklist"]);
1945 disableStats(StatComponent::RecControl
, ::arg()["stats-rec-control-blacklist"]);
1946 disableStats(StatComponent::SNMP
, ::arg()["stats-snmp-blacklist"]);
1948 disableStats(StatComponent::API
, ::arg()["stats-api-disabled-list"]);
1949 disableStats(StatComponent::Carbon
, ::arg()["stats-carbon-disabled-list"]);
1950 disableStats(StatComponent::RecControl
, ::arg()["stats-rec-control-disabled-list"]);
1951 disableStats(StatComponent::SNMP
, ::arg()["stats-snmp-disabled-list"]);
1953 if (::arg().mustDo("snmp-agent")) {
1954 #ifdef HAVE_NET_SNMP
1955 string setting
= ::arg()["snmp-daemon-socket"];
1956 if (setting
.empty()) {
1957 setting
= ::arg()["snmp-master-socket"];
1959 g_snmpAgent
= std::make_shared
<RecursorSNMPAgent
>("recursor", setting
);
1962 const std::string msg
= "snmp-agent set but SNMP support not compiled in";
1963 SLOG(g_log
<< Logger::Error
<< msg
<< endl
,
1964 log
->info(Logr::Error
, msg
));
1965 #endif // HAVE_NET_SNMP
1968 int port
= ::arg().asNum("udp-source-port-min");
1969 if (port
< 1024 || port
> 65535) {
1970 SLOG(g_log
<< Logger::Error
<< "Unable to launch, udp-source-port-min is not a valid port number" << endl
,
1971 log
->info(Logr::Error
, "Unable to launch, udp-source-port-min is not a valid port number"));
1972 exit(99); // this isn't going to fix itself either
1974 g_minUdpSourcePort
= port
;
1975 port
= ::arg().asNum("udp-source-port-max");
1976 if (port
< 1024 || port
> 65535 || port
< g_minUdpSourcePort
) {
1977 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
,
1978 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"));
1979 exit(99); // this isn't going to fix itself either
1981 g_maxUdpSourcePort
= port
;
1982 std::vector
<string
> parts
{};
1983 stringtok(parts
, ::arg()["udp-source-port-avoid"], ", ");
1984 for (const auto& part
: parts
) {
1985 port
= std::stoi(part
);
1986 if (port
< 1024 || port
> 65535) {
1987 SLOG(g_log
<< Logger::Error
<< "Unable to launch, udp-source-port-avoid contains an invalid port number: " << part
<< endl
,
1988 log
->info(Logr::Error
, "Unable to launch, udp-source-port-avoid contains an invalid port number", "port", Logging::Loggable(part
)));
1989 exit(99); // this isn't going to fix itself either
1991 g_avoidUdpSourcePorts
.insert(port
);
1994 return RecThreadInfo::runThreads(log
);
1997 static void handlePipeRequest(int fd
, FDMultiplexer::funcparam_t
& var
)
1999 ThreadMSG
* tmsg
= nullptr;
2001 if (read(fd
, &tmsg
, sizeof(tmsg
)) != sizeof(tmsg
)) { // fd == readToThread || fd == readQueriesToThread
2002 unixDie("read from thread pipe returned wrong size or error");
2007 resp
= tmsg
->func();
2009 catch (std::exception
& e
) {
2010 if (g_logCommonErrors
) {
2011 SLOG(g_log
<< Logger::Error
<< "PIPE function we executed created exception: " << e
.what() << endl
, // but what if they wanted an answer.. we send 0
2012 g_slog
->withName("runtime")->error(Logr::Error
, e
.what(), "PIPE function we executed created exception", "exception", Logging::Loggable("std::exception")));
2015 catch (PDNSException
& e
) {
2016 if (g_logCommonErrors
) {
2017 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
2018 g_slog
->withName("runtime")->error(Logr::Error
, e
.reason
, "PIPE function we executed created exception", "exception", Logging::Loggable("PDNSException")));
2021 if (tmsg
->wantAnswer
) {
2022 if (write(RecThreadInfo::self().pipes
.writeFromThread
, &resp
, sizeof(resp
)) != sizeof(resp
)) {
2024 unixDie("write to thread pipe returned wrong size or error");
2031 static void handleRCC(int fd
, FDMultiplexer::funcparam_t
& var
)
2033 auto log
= g_slog
->withName("control");
2035 FDWrapper clientfd
= accept(fd
, nullptr, nullptr);
2036 if (clientfd
== -1) {
2037 throw PDNSException("accept failed");
2039 string msg
= g_rcc
.recv(clientfd
).d_str
;
2040 SLOG(g_log
<< Logger::Info
<< "Received rec_control command '" << msg
<< "' via controlsocket" << endl
,
2041 log
->info(Logr::Info
, "Received rec_control command via control socket", "command", Logging::Loggable(msg
)));
2043 RecursorControlParser rcp
;
2044 RecursorControlParser::func_t
* command
;
2045 auto answer
= rcp
.getAnswer(clientfd
, msg
, &command
);
2047 g_rcc
.send(clientfd
, answer
);
2050 catch (const std::exception
& e
) {
2051 SLOG(g_log
<< Logger::Error
<< "Error dealing with control socket request: " << e
.what() << endl
,
2052 log
->error(Logr::Error
, e
.what(), "Exception while dealing with control socket request", "exception", Logging::Loggable("std::exception")));
2054 catch (const PDNSException
& ae
) {
2055 SLOG(g_log
<< Logger::Error
<< "Error dealing with control socket request: " << ae
.reason
<< endl
,
2056 log
->error(Logr::Error
, ae
.reason
, "Exception while dealing with control socket request", "exception", Logging::Loggable("PDNSException")));
2063 PeriodicTask(const string
& n
, time_t p
) :
2064 period
{p
, 0}, name(n
)
2067 throw PDNSException("Invalid period of periodic task " + n
);
2071 void runIfDue(struct timeval
& now
, const std::function
<void()>& f
)
2073 if (last_run
< now
- period
) {
2074 // cerr << RecThreadInfo::id() << ' ' << name << ' ' << now.tv_sec << '.' << now.tv_usec << " running" << endl;
2076 Utility::gettimeofday(&last_run
);
2081 time_t getPeriod() const
2083 return period
.tv_sec
;
2086 void setPeriod(time_t p
)
2091 void updateLastRun()
2093 Utility::gettimeofday(&last_run
);
2098 return last_run
.tv_sec
!= 0 || last_run
.tv_usec
!= 0;
2102 struct timeval last_run
2106 struct timeval period
;
2110 static void houseKeeping(void*)
2112 auto log
= g_slog
->withName("housekeeping");
2113 static thread_local
bool t_running
; // houseKeeping can get suspended in secpoll, and be restarted, which makes us do duplicate work
2122 Utility::gettimeofday(&now
);
2124 // Below are the tasks that run for every recursorThread, including handler and taskThread
2125 if (t_packetCache
) {
2126 static thread_local PeriodicTask packetCacheTask
{"packetCacheTask", 5};
2127 packetCacheTask
.runIfDue(now
, []() {
2128 size_t sz
= g_maxPacketCacheEntries
/ (RecThreadInfo::numWorkers() + RecThreadInfo::numDistributors());
2129 t_packetCache
->setMaxSize(sz
); // g_maxPacketCacheEntries might have changed by rec_control
2130 t_packetCache
->doPruneTo(sz
);
2134 static thread_local PeriodicTask pruneTCPTask
{"pruneTCPTask", 5};
2135 pruneTCPTask
.runIfDue(now
, [now
]() {
2136 t_tcp_manager
.cleanup(now
);
2139 const auto& info
= RecThreadInfo::self();
2141 // Threads handling packets process config changes in the input path, but not all threads process input packets
2142 // distr threads only process TCP, so that may not happenn very often. So do all periodically.
2143 static thread_local PeriodicTask exportConfigTask
{"exportConfigTask", 30};
2144 auto luaconfsLocal
= g_luaconfs
.getLocal();
2145 exportConfigTask
.runIfDue(now
, [&luaconfsLocal
]() {
2146 checkProtobufExport(luaconfsLocal
);
2147 checkOutgoingProtobufExport(luaconfsLocal
);
2149 checkFrameStreamExport(luaconfsLocal
, luaconfsLocal
->frameStreamExportConfig
, t_frameStreamServersInfo
);
2150 checkFrameStreamExport(luaconfsLocal
, luaconfsLocal
->nodFrameStreamExportConfig
, t_nodFrameStreamServersInfo
);
2154 // Below are the thread specific tasks for the handler and the taskThread
2155 // Likley a few handler tasks could be moved to the taskThread
2156 if (info
.isTaskThread()) {
2157 // TaskQueue is run always
2158 runTasks(10, g_logCommonErrors
);
2160 static PeriodicTask ztcTask
{"ZTC", 60};
2161 static map
<DNSName
, RecZoneToCache::State
> ztcStates
;
2162 ztcTask
.runIfDue(now
, [&luaconfsLocal
]() {
2163 RecZoneToCache::maintainStates(luaconfsLocal
->ztcConfigs
, ztcStates
, luaconfsLocal
->generation
);
2164 for (auto& ztc
: luaconfsLocal
->ztcConfigs
) {
2165 RecZoneToCache::ZoneToCache(ztc
.second
, ztcStates
.at(ztc
.first
));
2169 else if (info
.isHandler()) {
2170 static PeriodicTask recordCachePruneTask
{"RecordCachePruneTask", 5};
2171 recordCachePruneTask
.runIfDue(now
, []() {
2172 g_recCache
->doPrune(g_maxCacheEntries
);
2175 static PeriodicTask negCachePruneTask
{"NegCachePrunteTask", 5};
2176 negCachePruneTask
.runIfDue(now
, []() {
2177 g_negCache
->prune(g_maxCacheEntries
/ 10);
2180 static PeriodicTask aggrNSECPruneTask
{"AggrNSECPruneTask", 5};
2181 aggrNSECPruneTask
.runIfDue(now
, [now
]() {
2182 if (g_aggressiveNSECCache
) {
2183 g_aggressiveNSECCache
->prune(now
.tv_sec
);
2187 static PeriodicTask pruneNSpeedTask
{"pruneNSSpeedTask", 30};
2188 pruneNSpeedTask
.runIfDue(now
, [now
]() {
2189 SyncRes::pruneNSSpeeds(now
.tv_sec
- 300);
2192 static PeriodicTask pruneEDNSTask
{"pruneEDNSTask", 60};
2193 pruneEDNSTask
.runIfDue(now
, [now
]() {
2194 SyncRes::pruneEDNSStatuses(now
.tv_sec
);
2197 if (SyncRes::s_max_busy_dot_probes
> 0) {
2198 static PeriodicTask pruneDoTProbeMap
{"pruneDoTProbeMapTask", 60};
2199 pruneDoTProbeMap
.runIfDue(now
, [now
]() {
2200 SyncRes::pruneDoTProbeMap(now
.tv_sec
);
2204 static PeriodicTask pruneThrottledTask
{"pruneThrottledTask", 5};
2205 pruneThrottledTask
.runIfDue(now
, [now
]() {
2206 SyncRes::pruneThrottledServers(now
.tv_sec
);
2209 static PeriodicTask pruneFailedServersTask
{"pruneFailedServerTask", 5};
2210 pruneFailedServersTask
.runIfDue(now
, [now
]() {
2211 SyncRes::pruneFailedServers(now
.tv_sec
- SyncRes::s_serverdownthrottletime
* 10);
2214 static PeriodicTask pruneNonResolvingTask
{"pruneNonResolvingTask", 5};
2215 pruneNonResolvingTask
.runIfDue(now
, [now
]() {
2216 SyncRes::pruneNonResolving(now
.tv_sec
- SyncRes::s_nonresolvingnsthrottletime
);
2219 static PeriodicTask pruneSaveParentSetTask
{"pruneSaveParentSetTask", 60};
2220 pruneSaveParentSetTask
.runIfDue(now
, [now
]() {
2221 SyncRes::pruneSaveParentsNSSets(now
.tv_sec
);
2224 // By default, refresh at 80% of max-cache-ttl with a minimum period of 10s
2225 const unsigned int minRootRefreshInterval
= 10;
2226 static PeriodicTask rootUpdateTask
{"rootUpdateTask", std::max(SyncRes::s_maxcachettl
* 8 / 10, minRootRefreshInterval
)};
2227 rootUpdateTask
.runIfDue(now
, [now
, &log
, minRootRefreshInterval
]() {
2228 int res
= SyncRes::getRootNS(now
, nullptr, 0, log
);
2230 // Success, go back to the defaut period
2231 rootUpdateTask
.setPeriod(std::max(SyncRes::s_maxcachettl
* 8 / 10, minRootRefreshInterval
));
2232 const string msg
= "Exception while priming the root NS zones";
2234 primeRootNSZones(g_dnssecmode
, 0);
2236 catch (const std::exception
& e
) {
2237 SLOG(g_log
<< Logger::Error
<< "Exception while priming the root NS zones: " << e
.what() << endl
,
2238 log
->error(Logr::Error
, e
.what(), msg
, "exception", Logging::Loggable("std::exception")));
2240 catch (const PDNSException
& e
) {
2241 SLOG(g_log
<< Logger::Error
<< "Exception while priming the root NS zones: " << e
.reason
<< endl
,
2242 log
->error(Logr::Error
, e
.reason
, msg
, "exception", Logging::Loggable("PDNSException")));
2244 catch (const ImmediateServFailException
& e
) {
2245 SLOG(g_log
<< Logger::Error
<< "Exception while priming the root NS zones: " << e
.reason
<< endl
,
2246 log
->error(Logr::Error
, e
.reason
, msg
, "exception", Logging::Loggable("ImmediateServFailException")));
2248 catch (const PolicyHitException
& e
) {
2249 SLOG(g_log
<< Logger::Error
<< "Policy hit while priming the root NS zones" << endl
,
2250 log
->info(Logr::Error
, msg
, "exception", Logging::Loggable("PolicyHitException")));
2253 SLOG(g_log
<< Logger::Error
<< "Exception while priming the root NS zones" << endl
,
2254 log
->info(Logr::Error
, "Exception while priming the root NS zones"));
2258 // On failure, go to the middle of the remaining period (initially 80% / 8 = 10%) and shorten the interval on each
2259 // failure by dividing the existing interval by 8, keeping the minimum interval at 10s.
2260 // So with a 1 day period and failures we'll see a refresh attempt at 69120, 69120+11520, 69120+11520+1440, ...
2261 rootUpdateTask
.setPeriod(std::max
<time_t>(rootUpdateTask
.getPeriod() / 8, minRootRefreshInterval
));
2265 static PeriodicTask secpollTask
{"secpollTask", 3600};
2266 static time_t t_last_secpoll
;
2267 secpollTask
.runIfDue(now
, [&log
]() {
2269 doSecPoll(&t_last_secpoll
, log
);
2271 catch (const std::exception
& e
) {
2272 SLOG(g_log
<< Logger::Error
<< "Exception while performing security poll: " << e
.what() << endl
,
2273 log
->error(Logr::Error
, e
.what(), "Exception while performing security poll"));
2275 catch (const PDNSException
& e
) {
2276 SLOG(g_log
<< Logger::Error
<< "Exception while performing security poll: " << e
.reason
<< endl
,
2277 log
->error(Logr::Error
, e
.reason
, "Exception while performing security poll"));
2279 catch (const ImmediateServFailException
& e
) {
2280 SLOG(g_log
<< Logger::Error
<< "Exception while performing security poll: " << e
.reason
<< endl
,
2281 log
->error(Logr::Error
, e
.reason
, "Exception while performing security poll"));
2283 catch (const PolicyHitException
& e
) {
2284 SLOG(g_log
<< Logger::Error
<< "Policy hit while performing security poll" << endl
,
2285 log
->info(Logr::Error
, "Policy hit while performing security poll"));
2288 SLOG(g_log
<< Logger::Error
<< "Exception while performing security poll" << endl
,
2289 log
->info(Logr::Error
, "Exception while performing security poll"));
2293 static PeriodicTask trustAnchorTask
{"trustAnchorTask", std::max(1U, luaconfsLocal
->trustAnchorFileInfo
.interval
) * 3600};
2294 if (!trustAnchorTask
.hasRun()) {
2295 // Loading the Lua config file already "refreshed" the TAs
2296 trustAnchorTask
.updateLastRun();
2298 // interval might have ben updated
2299 trustAnchorTask
.setPeriod(std::max(1U, luaconfsLocal
->trustAnchorFileInfo
.interval
) * 3600);
2300 trustAnchorTask
.runIfDue(now
, [&luaconfsLocal
, &log
]() {
2301 if (!luaconfsLocal
->trustAnchorFileInfo
.fname
.empty() && luaconfsLocal
->trustAnchorFileInfo
.interval
!= 0) {
2302 SLOG(g_log
<< Logger::Debug
<< "Refreshing Trust Anchors from file" << endl
,
2303 log
->info(Logr::Debug
, "Refreshing Trust Anchors from file"));
2305 map
<DNSName
, dsmap_t
> dsAnchors
;
2306 if (updateTrustAnchorsFromFile(luaconfsLocal
->trustAnchorFileInfo
.fname
, dsAnchors
, log
)) {
2307 g_luaconfs
.modify([&dsAnchors
](LuaConfigItems
& lci
) {
2308 lci
.dsAnchors
= dsAnchors
;
2312 catch (const PDNSException
& pe
) {
2313 SLOG(g_log
<< Logger::Error
<< "Unable to update Trust Anchors: " << pe
.reason
<< endl
,
2314 log
->error(Logr::Error
, pe
.reason
, "Unable to update Trust Anchors"));
2321 catch (const PDNSException
& ae
) {
2323 SLOG(g_log
<< Logger::Error
<< "Fatal error in housekeeping thread: " << ae
.reason
<< endl
,
2324 log
->error(Logr::Error
, ae
.reason
, "Fatal error in housekeeping thread"));
2329 SLOG(g_log
<< Logger::Error
<< "Uncaught exception in housekeeping thread" << endl
,
2330 log
->info(Logr::Error
, "Uncaught exception in housekeeping thread"));
2335 static void recursorThread()
2337 auto log
= g_slog
->withName("runtime");
2339 auto& threadInfo
= RecThreadInfo::self();
2341 SyncRes
tmp(g_now
); // make sure it allocates tsstorage before we do anything, like primeHints or so..
2342 SyncRes::setDomainMap(g_initialDomainMap
);
2343 t_allowFrom
= g_initialAllowFrom
;
2344 t_allowNotifyFrom
= g_initialAllowNotifyFrom
;
2345 t_allowNotifyFor
= g_initialAllowNotifyFor
;
2346 t_udpclientsocks
= std::make_unique
<UDPClientSocks
>();
2347 t_tcpClientCounts
= std::make_unique
<tcpClientCounts_t
>();
2348 if (g_proxyMapping
) {
2349 t_proxyMapping
= make_unique
<ProxyMapping
>(*g_proxyMapping
);
2352 t_proxyMapping
= nullptr;
2355 if (threadInfo
.isHandler()) {
2356 if (!primeHints()) {
2357 threadInfo
.setExitCode(EXIT_FAILURE
);
2358 RecursorControlChannel::stop
= 1;
2359 SLOG(g_log
<< Logger::Critical
<< "Priming cache failed, stopping" << endl
,
2360 log
->info(Logr::Critical
, "Priming cache failed, stopping"));
2362 SLOG(g_log
<< Logger::Debug
<< "Done priming cache with root hints" << endl
,
2363 log
->info(Logr::Debug
, "Done priming cache with root hints"));
2367 if (!::arg().mustDo("disable-packetcache") && (threadInfo
.isDistributor() || threadInfo
.isWorker())) {
2368 // Only enable packet cache for thread processing queries from the outside world
2369 t_packetCache
= std::make_unique
<RecursorPacketCache
>(g_maxPacketCacheEntries
/ (RecThreadInfo::numWorkers() + RecThreadInfo::numDistributors()));
2373 if (threadInfo
.isWorker())
2374 setupNODThread(log
);
2375 #endif /* NOD_ENABLED */
2377 /* the listener threads handle TCP queries */
2378 if (threadInfo
.isWorker() || threadInfo
.isListener()) {
2380 if (!::arg()["lua-dns-script"].empty()) {
2381 t_pdl
= std::make_shared
<RecursorLua4
>();
2382 t_pdl
->loadFile(::arg()["lua-dns-script"]);
2383 SLOG(g_log
<< Logger::Warning
<< "Loaded 'lua' script from '" << ::arg()["lua-dns-script"] << "'" << endl
,
2384 log
->info(Logr::Warning
, "Loading Lua script from file", "name", Logging::Loggable(::arg()["lua-dns-script"])));
2387 catch (std::exception
& e
) {
2388 SLOG(g_log
<< Logger::Error
<< "Failed to load 'lua' script from '" << ::arg()["lua-dns-script"] << "': " << e
.what() << endl
,
2389 log
->error(Logr::Error
, e
.what(), "Failed to load Lua script from file", "name", Logging::Loggable(::arg()["lua-dns-script"])));
2394 unsigned int ringsize
= ::arg().asNum("stats-ringbuffer-entries") / RecThreadInfo::numWorkers();
2396 t_remotes
= std::make_unique
<addrringbuf_t
>();
2397 if (RecThreadInfo::weDistributeQueries())
2398 t_remotes
->set_capacity(::arg().asNum("stats-ringbuffer-entries") / RecThreadInfo::numDistributors());
2400 t_remotes
->set_capacity(ringsize
);
2401 t_servfailremotes
= std::make_unique
<addrringbuf_t
>();
2402 t_servfailremotes
->set_capacity(ringsize
);
2403 t_bogusremotes
= std::make_unique
<addrringbuf_t
>();
2404 t_bogusremotes
->set_capacity(ringsize
);
2405 t_largeanswerremotes
= std::make_unique
<addrringbuf_t
>();
2406 t_largeanswerremotes
->set_capacity(ringsize
);
2407 t_timeouts
= std::make_unique
<addrringbuf_t
>();
2408 t_timeouts
->set_capacity(ringsize
);
2410 t_queryring
= std::make_unique
<boost::circular_buffer
<pair
<DNSName
, uint16_t>>>();
2411 t_queryring
->set_capacity(ringsize
);
2412 t_servfailqueryring
= std::make_unique
<boost::circular_buffer
<pair
<DNSName
, uint16_t>>>();
2413 t_servfailqueryring
->set_capacity(ringsize
);
2414 t_bogusqueryring
= std::make_unique
<boost::circular_buffer
<pair
<DNSName
, uint16_t>>>();
2415 t_bogusqueryring
->set_capacity(ringsize
);
2417 MT
= std::make_unique
<MT_t
>(::arg().asNum("stack-size"));
2418 threadInfo
.mt
= MT
.get();
2420 /* start protobuf export threads if needed */
2421 auto luaconfsLocal
= g_luaconfs
.getLocal();
2422 checkProtobufExport(luaconfsLocal
);
2423 checkOutgoingProtobufExport(luaconfsLocal
);
2425 checkFrameStreamExport(luaconfsLocal
, luaconfsLocal
->frameStreamExportConfig
, t_frameStreamServersInfo
);
2426 checkFrameStreamExport(luaconfsLocal
, luaconfsLocal
->nodFrameStreamExportConfig
, t_nodFrameStreamServersInfo
);
2429 t_fdm
= unique_ptr
<FDMultiplexer
>(getMultiplexer(log
));
2431 std::unique_ptr
<RecursorWebServer
> rws
;
2433 t_fdm
->addReadFD(threadInfo
.pipes
.readToThread
, handlePipeRequest
);
2435 if (threadInfo
.isHandler()) {
2436 if (::arg().mustDo("webserver")) {
2437 SLOG(g_log
<< Logger::Warning
<< "Enabling web server" << endl
,
2438 log
->info(Logr::Info
, "Enabling web server"))
2440 rws
= make_unique
<RecursorWebServer
>(t_fdm
.get());
2442 catch (const PDNSException
& e
) {
2443 SLOG(g_log
<< Logger::Error
<< "Unable to start the internal web server: " << e
.reason
<< endl
,
2444 log
->error(Logr::Critical
, e
.reason
, "Exception while starting internal web server"));
2448 SLOG(g_log
<< Logger::Info
<< "Enabled '" << t_fdm
->getName() << "' multiplexer" << endl
,
2449 log
->info(Logr::Info
, "Enabled multiplexer", "name", Logging::Loggable(t_fdm
->getName())));
2452 t_fdm
->addReadFD(threadInfo
.pipes
.readQueriesToThread
, handlePipeRequest
);
2454 if (threadInfo
.isListener()) {
2456 /* then every listener has its own FDs */
2457 for (const auto& deferred
: threadInfo
.deferredAdds
) {
2458 t_fdm
->addReadFD(deferred
.first
, deferred
.second
);
2462 /* otherwise all listeners are listening on the same ones */
2463 for (const auto& deferred
: g_deferredAdds
) {
2464 t_fdm
->addReadFD(deferred
.first
, deferred
.second
);
2472 if (threadInfo
.isHandler()) {
2473 t_fdm
->addReadFD(g_rcc
.d_fd
, handleRCC
); // control channel
2476 unsigned int maxTcpClients
= ::arg().asNum("max-tcp-clients");
2478 bool listenOnTCP
{true};
2480 time_t last_stat
= 0;
2481 time_t last_carbon
= 0, last_lua_maintenance
= 0;
2482 time_t carbonInterval
= ::arg().asNum("carbon-interval");
2483 time_t luaMaintenanceInterval
= ::arg().asNum("lua-maintenance-interval");
2484 s_counter
.store(0); // used to periodically execute certain tasks
2487 if (threadInfo
.isHandler()) {
2488 // There is a race, as some threads might not be ready yet to do work.
2489 // To solve that, threads should notify RecThreadInfo they are done initializing.
2490 // But we lack a mechanism for that at this point in time.
2491 sd_notify(0, "READY=1");
2495 while (!RecursorControlChannel::stop
) {
2496 while (MT
->schedule(&g_now
))
2497 ; // MTasker letting the mthreads do their thing
2499 // Use primes, it avoid not being scheduled in cases where the counter has a regular pattern.
2500 // We want to call handler thread often, it gets scheduled about 2 times per second
2501 if (((threadInfo
.isHandler() || threadInfo
.isTaskThread()) && s_counter
% 11 == 0) || s_counter
% 499 == 0) {
2502 struct timeval start
;
2503 Utility::gettimeofday(&start
);
2504 MT
->makeThread(houseKeeping
, nullptr);
2505 if (!threadInfo
.isTaskThread()) {
2506 struct timeval stop
;
2507 Utility::gettimeofday(&stop
);
2508 g_stats
.maintenanceUsec
+= uSec(stop
- start
);
2509 ++g_stats
.maintenanceCalls
;
2513 if (!(s_counter
% 55)) {
2514 typedef vector
<pair
<int, FDMultiplexer::funcparam_t
>> expired_t
;
2515 expired_t expired
= t_fdm
->getTimeouts(g_now
);
2517 for (expired_t::iterator i
= expired
.begin(); i
!= expired
.end(); ++i
) {
2518 shared_ptr
<TCPConnection
> conn
= boost::any_cast
<shared_ptr
<TCPConnection
>>(i
->second
);
2519 if (g_logCommonErrors
)
2520 SLOG(g_log
<< Logger::Warning
<< "Timeout from remote TCP client " << conn
->d_remote
.toStringWithPort() << endl
,
2521 g_slogtcpin
->info(Logr::Warning
, "Timeout from remote TCP client", "remote", Logging::Loggable(conn
->d_remote
)));
2522 t_fdm
->removeReadFD(i
->first
);
2528 if (threadInfo
.isHandler()) {
2529 if (statsWanted
|| (s_statisticsInterval
> 0 && (g_now
.tv_sec
- last_stat
) >= s_statisticsInterval
)) {
2531 last_stat
= g_now
.tv_sec
;
2534 Utility::gettimeofday(&g_now
, nullptr);
2536 if ((g_now
.tv_sec
- last_carbon
) >= carbonInterval
) {
2537 MT
->makeThread(doCarbonDump
, 0);
2538 last_carbon
= g_now
.tv_sec
;
2541 if (t_pdl
!= nullptr) {
2542 // lua-dns-script directive is present, call the maintenance callback if needed
2543 /* remember that the listener threads handle TCP queries */
2544 if (threadInfo
.isWorker() || threadInfo
.isListener()) {
2545 // Only on threads processing queries
2546 if (g_now
.tv_sec
- last_lua_maintenance
>= luaMaintenanceInterval
) {
2547 struct timeval start
;
2548 Utility::gettimeofday(&start
);
2549 t_pdl
->maintenance();
2550 last_lua_maintenance
= g_now
.tv_sec
;
2551 struct timeval stop
;
2552 Utility::gettimeofday(&stop
);
2553 g_stats
.maintenanceUsec
+= uSec(stop
- start
);
2554 ++g_stats
.maintenanceCalls
;
2560 // 'run' updates g_now for us
2562 if (threadInfo
.isListener()) {
2564 if (TCPConnection::getCurrentConnections() > maxTcpClients
) { // shutdown, too many connections
2565 for (const auto fd
: threadInfo
.tcpSockets
) {
2566 t_fdm
->removeReadFD(fd
);
2568 listenOnTCP
= false;
2572 if (TCPConnection::getCurrentConnections() <= maxTcpClients
) { // reenable
2573 for (const auto fd
: threadInfo
.tcpSockets
) {
2574 t_fdm
->addReadFD(fd
, handleNewTCPQuestion
);
2582 catch (PDNSException
& ae
) {
2583 SLOG(g_log
<< Logger::Error
<< "Exception: " << ae
.reason
<< endl
,
2584 log
->error(Logr::Error
, ae
.reason
, "Exception in RecursorThread", "exception", Logging::Loggable("PDNSException")))
2586 catch (std::exception
& e
) {
2587 SLOG(g_log
<< Logger::Error
<< "STL Exception: " << e
.what() << endl
,
2588 log
->error(Logr::Error
, e
.what(), "Exception in RecursorThread", "exception", Logging::Loggable("std::exception")))
2591 SLOG(g_log
<< Logger::Error
<< "any other exception in main: " << endl
,
2592 log
->info(Logr::Error
, "Exception in RecursorThread"));
2596 int main(int argc
, char** argv
)
2601 versionSetProduct(ProductRecursor
);
2605 int ret
= EXIT_SUCCESS
;
2608 #if HAVE_FIBER_SANITIZER
2609 // Asan needs more stack
2610 ::arg().set("stack-size", "stack size per mthread") = "400000";
2612 ::arg().set("stack-size", "stack size per mthread") = "200000";
2614 ::arg().set("soa-minimum-ttl", "Don't change") = "0";
2615 ::arg().set("no-shuffle", "Don't change") = "off";
2616 ::arg().set("local-port", "port to listen on") = "53";
2617 ::arg().set("local-address", "IP addresses to listen on, separated by spaces or commas. Also accepts ports.") = "127.0.0.1";
2618 ::arg().setSwitch("non-local-bind", "Enable binding to non-local addresses by using FREEBIND / BINDANY socket options") = "no";
2619 ::arg().set("trace", "if we should output heaps of logging. set to 'fail' to only log failing domains") = "off";
2620 ::arg().set("dnssec", "DNSSEC mode: off/process-no-validate/process (default)/log-fail/validate") = "process";
2621 ::arg().set("dnssec-log-bogus", "Log DNSSEC bogus validations") = "no";
2622 ::arg().set("signature-inception-skew", "Allow the signature inception to be off by this number of seconds") = "60";
2623 ::arg().set("daemon", "Operate as a daemon") = "no";
2624 ::arg().setSwitch("write-pid", "Write a PID file") = "yes";
2625 ::arg().set("loglevel", "Amount of logging. Higher is more. Do not set below 3") = "6";
2626 ::arg().set("disable-syslog", "Disable logging to syslog, useful when running inside a supervisor that logs stdout") = "no";
2627 ::arg().set("log-timestamp", "Print timestamps in log lines, useful to disable when running with a tool that timestamps stdout already") = "yes";
2628 ::arg().set("log-common-errors", "If we should log rather common errors") = "no";
2629 ::arg().set("chroot", "switch to chroot jail") = "";
2630 ::arg().set("setgid", "If set, change group id to this gid for more security"
2632 #define SYSTEMD_SETID_MSG ". When running inside systemd, use the User and Group settings in the unit-file!"
2637 ::arg().set("setuid", "If set, change user id to this uid for more security"
2643 ::arg().set("network-timeout", "Wait this number of milliseconds for network i/o") = "1500";
2644 ::arg().set("threads", "Launch this number of threads") = "2";
2645 ::arg().set("distributor-threads", "Launch this number of distributor threads, distributing queries to other threads") = "0";
2646 ::arg().set("processes", "Launch this number of processes (EXPERIMENTAL, DO NOT CHANGE)") = "1"; // if we un-experimental this, need to fix openssl rand seeding for multiple PIDs!
2647 ::arg().set("config-name", "Name of this virtual configuration - will rename the binary image") = "";
2648 ::arg().set("api-config-dir", "Directory where REST API stores config and zones") = "";
2649 ::arg().set("api-key", "Static pre-shared authentication key for access to the REST API") = "";
2650 ::arg().setSwitch("webserver", "Start a webserver (for REST API)") = "no";
2651 ::arg().set("webserver-address", "IP Address of webserver to listen on") = "127.0.0.1";
2652 ::arg().set("webserver-port", "Port of webserver to listen on") = "8082";
2653 ::arg().set("webserver-password", "Password required for accessing the webserver") = "";
2654 ::arg().set("webserver-allow-from", "Webserver access is only allowed from these subnets") = "127.0.0.1,::1";
2655 ::arg().set("webserver-loglevel", "Amount of logging in the webserver (none, normal, detailed)") = "normal";
2656 ::arg().setSwitch("webserver-hash-plaintext-credentials", "Whether to hash passwords and api keys supplied in plaintext, to prevent keeping the plaintext version in memory at runtime") = "no";
2657 ::arg().set("carbon-ourname", "If set, overrides our reported hostname for carbon stats") = "";
2658 ::arg().set("carbon-server", "If set, send metrics in carbon (graphite) format to this server IP address") = "";
2659 ::arg().set("carbon-interval", "Number of seconds between carbon (graphite) updates") = "30";
2660 ::arg().set("carbon-namespace", "If set overwrites the first part of the carbon string") = "pdns";
2661 ::arg().set("carbon-instance", "If set overwrites the instance name default") = "recursor";
2663 ::arg().set("statistics-interval", "Number of seconds between printing of recursor statistics, 0 to disable") = "1800";
2664 ::arg().set("quiet", "Suppress logging of questions and answers") = "";
2665 ::arg().set("logging-facility", "Facility to log messages as. 0 corresponds to local0") = "";
2666 ::arg().set("config-dir", "Location of configuration directory (recursor.conf)") = SYSCONFDIR
;
2667 ::arg().set("socket-owner", "Owner of socket") = "";
2668 ::arg().set("socket-group", "Group of socket") = "";
2669 ::arg().set("socket-mode", "Permissions for socket") = "";
2671 ::arg().set("socket-dir", string("Where the controlsocket will live, ") + LOCALSTATEDIR
+ "/pdns-recursor when unset and not chrooted"
2673 + ". Set to the RUNTIME_DIRECTORY environment variable when that variable has a value (e.g. under systemd).")
2675 auto runtimeDir
= getenv("RUNTIME_DIRECTORY");
2676 if (runtimeDir
!= nullptr) {
2677 ::arg().set("socket-dir") = runtimeDir
;
2683 ::arg().set("query-local-address", "Source IP address for sending queries") = "0.0.0.0";
2684 ::arg().set("client-tcp-timeout", "Timeout in seconds when talking to TCP clients") = "2";
2685 ::arg().set("max-mthreads", "Maximum number of simultaneous Mtasker threads") = "2048";
2686 ::arg().set("max-tcp-clients", "Maximum number of simultaneous TCP clients") = "128";
2687 ::arg().set("max-concurrent-requests-per-tcp-connection", "Maximum number of requests handled concurrently per TCP connection") = "10";
2688 ::arg().set("server-down-max-fails", "Maximum number of consecutive timeouts (and unreachables) to mark a server as down ( 0 => disabled )") = "64";
2689 ::arg().set("server-down-throttle-time", "Number of seconds to throttle all queries to a server after being marked as down") = "60";
2690 ::arg().set("dont-throttle-names", "Do not throttle nameservers with this name or suffix") = "";
2691 ::arg().set("dont-throttle-netmasks", "Do not throttle nameservers with this IP netmask") = "";
2692 ::arg().set("non-resolving-ns-max-fails", "Number of failed address resolves of a nameserver to start throttling it, 0 is disabled") = "5";
2693 ::arg().set("non-resolving-ns-throttle-time", "Number of seconds to throttle a nameserver with a name failing to resolve") = "60";
2695 ::arg().set("hint-file", "If set, load root hints from this file") = "";
2696 ::arg().set("max-cache-entries", "If set, maximum number of entries in the main cache") = "1000000";
2697 ::arg().set("max-negative-ttl", "maximum number of seconds to keep a negative cached entry in memory") = "3600";
2698 ::arg().set("max-cache-bogus-ttl", "maximum number of seconds to keep a Bogus (positive or negative) cached entry in memory") = "3600";
2699 ::arg().set("max-cache-ttl", "maximum number of seconds to keep a cached entry in memory") = "86400";
2700 ::arg().set("packetcache-ttl", "maximum number of seconds to keep a cached entry in packetcache") = "3600";
2701 ::arg().set("max-packetcache-entries", "maximum number of entries to keep in the packetcache") = "500000";
2702 ::arg().set("packetcache-servfail-ttl", "maximum number of seconds to keep a cached servfail entry in packetcache") = "60";
2703 ::arg().set("server-id", "Returned when queried for 'id.server' TXT or NSID, defaults to hostname, set custom or 'disabled'") = "";
2704 ::arg().set("stats-ringbuffer-entries", "maximum number of packets to store statistics for") = "10000";
2705 ::arg().set("version-string", "string reported on version.pdns or version.bind") = fullVersionString();
2706 ::arg().set("allow-from", "If set, only allow these comma separated netmasks to recurse") = LOCAL_NETS
;
2707 ::arg().set("allow-from-file", "If set, load allowed netmasks from this file") = "";
2708 ::arg().set("allow-notify-for", "If set, NOTIFY requests for these zones will be allowed") = "";
2709 ::arg().set("allow-notify-for-file", "If set, load NOTIFY-allowed zones from this file") = "";
2710 ::arg().set("allow-notify-from", "If set, NOTIFY requests from these comma separated netmasks will be allowed") = "";
2711 ::arg().set("allow-notify-from-file", "If set, load NOTIFY-allowed netmasks from this file") = "";
2712 ::arg().set("entropy-source", "If set, read entropy from this file") = "/dev/urandom";
2713 ::arg().set("dont-query", "If set, do not query these netmasks for DNS data") = DONT_QUERY
;
2714 ::arg().set("max-tcp-per-client", "If set, maximum number of TCP sessions per client (IP address)") = "0";
2715 ::arg().set("max-tcp-queries-per-connection", "If set, maximum number of TCP queries in a TCP connection") = "0";
2716 ::arg().set("spoof-nearmiss-max", "If non-zero, assume spoofing after this many near misses") = "1";
2717 ::arg().set("single-socket", "If set, only use a single socket for outgoing queries") = "off";
2718 ::arg().set("auth-zones", "Zones for which we have authoritative data, comma separated domain=file pairs ") = "";
2719 ::arg().set("lua-config-file", "More powerful configuration options") = "";
2720 ::arg().setSwitch("allow-trust-anchor-query", "Allow queries for trustanchor.server CH TXT and negativetrustanchor.server CH TXT") = "no";
2722 ::arg().set("forward-zones", "Zones for which we forward queries, comma separated domain=ip pairs") = "";
2723 ::arg().set("forward-zones-recurse", "Zones for which we forward queries with recursion bit, comma separated domain=ip pairs") = "";
2724 ::arg().set("forward-zones-file", "File with (+)domain=ip pairs for forwarding") = "";
2725 ::arg().set("export-etc-hosts", "If we should serve up contents from /etc/hosts") = "off";
2726 ::arg().set("export-etc-hosts-search-suffix", "Also serve up the contents of /etc/hosts with this suffix") = "";
2727 ::arg().set("etc-hosts-file", "Path to 'hosts' file") = "/etc/hosts";
2728 ::arg().set("serve-rfc1918", "If we should be authoritative for RFC 1918 private IP space") = "yes";
2729 ::arg().set("lua-dns-script", "Filename containing an optional 'lua' script that will be used to modify dns answers") = "";
2730 ::arg().set("lua-maintenance-interval", "Number of seconds between calls to the lua user defined maintenance() function") = "1";
2731 ::arg().set("latency-statistic-size", "Number of latency values to calculate the qa-latency average") = "10000";
2732 ::arg().setSwitch("disable-packetcache", "Disable packetcache") = "no";
2733 ::arg().set("ecs-ipv4-bits", "Number of bits of IPv4 address to pass for EDNS Client Subnet") = "24";
2734 ::arg().set("ecs-ipv4-cache-bits", "Maximum number of bits of IPv4 mask to cache ECS response") = "24";
2735 ::arg().set("ecs-ipv6-bits", "Number of bits of IPv6 address to pass for EDNS Client Subnet") = "56";
2736 ::arg().set("ecs-ipv6-cache-bits", "Maximum number of bits of IPv6 mask to cache ECS response") = "56";
2737 ::arg().setSwitch("ecs-ipv4-never-cache", "If we should never cache IPv4 ECS responses") = "no";
2738 ::arg().setSwitch("ecs-ipv6-never-cache", "If we should never cache IPv6 ECS responses") = "no";
2739 ::arg().set("ecs-minimum-ttl-override", "The minimum TTL for records in ECS-specific answers") = "1";
2740 ::arg().set("ecs-cache-limit-ttl", "Minimum TTL to cache ECS response") = "0";
2741 ::arg().set("edns-subnet-whitelist", "List of netmasks and domains that we should enable EDNS subnet for (deprecated)") = "";
2742 ::arg().set("edns-subnet-allow-list", "List of netmasks and domains that we should enable EDNS subnet for") = "";
2743 ::arg().set("ecs-add-for", "List of client netmasks for which EDNS Client Subnet will be added") = "0.0.0.0/0, ::/0, " LOCAL_NETS_INVERSE
;
2744 ::arg().set("ecs-scope-zero-address", "Address to send to allow-listed authoritative servers for incoming queries with ECS prefix-length source of 0") = "";
2745 ::arg().setSwitch("use-incoming-edns-subnet", "Pass along received EDNS Client Subnet information") = "no";
2746 ::arg().setSwitch("pdns-distributes-queries", "If PowerDNS itself should distribute queries over threads") = "yes";
2747 ::arg().setSwitch("root-nx-trust", "If set, believe that an NXDOMAIN from the root means the TLD does not exist") = "yes";
2748 ::arg().setSwitch("any-to-tcp", "Answer ANY queries with tc=1, shunting to TCP") = "no";
2749 ::arg().setSwitch("lowercase-outgoing", "Force outgoing questions to lowercase") = "no";
2750 ::arg().setSwitch("gettag-needs-edns-options", "If EDNS Options should be extracted before calling the gettag() hook") = "no";
2751 ::arg().set("udp-truncation-threshold", "Maximum UDP response size before we truncate") = "1232";
2752 ::arg().set("edns-outgoing-bufsize", "Outgoing EDNS buffer size") = "1232";
2753 ::arg().set("minimum-ttl-override", "The minimum TTL") = "1";
2754 ::arg().set("max-qperq", "Maximum outgoing queries per query") = "60";
2755 ::arg().set("max-ns-per-resolve", "Maximum number of NS records to consider to resolve a name, 0 is no limit") = "13";
2756 ::arg().set("max-ns-address-qperq", "Maximum outgoing NS address queries per query") = "10";
2757 ::arg().set("max-total-msec", "Maximum total wall-clock time per query in milliseconds, 0 for unlimited") = "7000";
2758 ::arg().set("max-recursion-depth", "Maximum number of internal recursion calls per query, 0 for unlimited") = "40";
2759 ::arg().set("max-udp-queries-per-round", "Maximum number of UDP queries processed per recvmsg() round, before returning back to normal processing") = "10000";
2760 ::arg().set("protobuf-use-kernel-timestamp", "Compute the latency of queries in protobuf messages by using the timestamp set by the kernel when the query was received (when available)") = "";
2761 ::arg().set("distribution-pipe-buffer-size", "Size in bytes of the internal buffer of the pipe used by the distributor to pass incoming queries to a worker thread") = "0";
2763 ::arg().set("include-dir", "Include *.conf files from this directory") = "";
2764 ::arg().set("security-poll-suffix", "Domain name from which to query security update notifications") = "secpoll.powerdns.com.";
2766 ::arg().setSwitch("reuseport", "Enable SO_REUSEPORT allowing multiple recursors processes to listen to 1 address") = "no";
2768 ::arg().setSwitch("snmp-agent", "If set, register as an SNMP agent") = "no";
2769 ::arg().set("snmp-master-socket", "If set and snmp-agent is set, the socket to use to register to the SNMP daemon (deprecated)") = "";
2770 ::arg().set("snmp-daemon-socket", "If set and snmp-agent is set, the socket to use to register to the SNMP daemon") = "";
2772 std::string defaultAPIDisabledStats
= "cache-bytes, packetcache-bytes, special-memory-usage";
2773 for (size_t idx
= 0; idx
< 32; idx
++) {
2774 defaultAPIDisabledStats
+= ", ecs-v4-response-bits-" + std::to_string(idx
+ 1);
2776 for (size_t idx
= 0; idx
< 128; idx
++) {
2777 defaultAPIDisabledStats
+= ", ecs-v6-response-bits-" + std::to_string(idx
+ 1);
2779 std::string defaultDisabledStats
= defaultAPIDisabledStats
+ ", cumul-clientanswers, cumul-authanswers, policy-hits, proxy-mapping-total, remote-logger-count";
2781 ::arg().set("stats-api-blacklist", "List of statistics that are disabled when retrieving the complete list of statistics via the API (deprecated)") = defaultAPIDisabledStats
;
2782 ::arg().set("stats-carbon-blacklist", "List of statistics that are prevented from being exported via Carbon (deprecated)") = defaultDisabledStats
;
2783 ::arg().set("stats-rec-control-blacklist", "List of statistics that are prevented from being exported via rec_control get-all (deprecated)") = defaultDisabledStats
;
2784 ::arg().set("stats-snmp-blacklist", "List of statistics that are prevented from being exported via SNMP (deprecated)") = defaultDisabledStats
;
2786 ::arg().set("stats-api-disabled-list", "List of statistics that are disabled when retrieving the complete list of statistics via the API") = defaultAPIDisabledStats
;
2787 ::arg().set("stats-carbon-disabled-list", "List of statistics that are prevented from being exported via Carbon") = defaultDisabledStats
;
2788 ::arg().set("stats-rec-control-disabled-list", "List of statistics that are prevented from being exported via rec_control get-all") = defaultDisabledStats
;
2789 ::arg().set("stats-snmp-disabled-list", "List of statistics that are prevented from being exported via SNMP") = defaultDisabledStats
;
2791 ::arg().set("tcp-fast-open", "Enable TCP Fast Open support on the listening sockets, using the supplied numerical value as the queue size") = "0";
2792 ::arg().set("tcp-fast-open-connect", "Enable TCP Fast Open support on outgoing sockets") = "no";
2793 ::arg().set("nsec3-max-iterations", "Maximum number of iterations allowed for an NSEC3 record") = "150";
2795 ::arg().set("cpu-map", "Thread to CPU mapping, space separated thread-id=cpu1,cpu2..cpuN pairs") = "";
2797 ::arg().setSwitch("log-rpz-changes", "Log additions and removals to RPZ zones at Info level") = "no";
2799 ::arg().set("proxy-protocol-from", "A Proxy Protocol header is only allowed from these subnets") = "";
2800 ::arg().set("proxy-protocol-maximum-size", "The maximum size of a proxy protocol payload, including the TLV values") = "512";
2802 ::arg().set("dns64-prefix", "DNS64 prefix") = "";
2804 ::arg().set("udp-source-port-min", "Minimum UDP port to bind on") = "1024";
2805 ::arg().set("udp-source-port-max", "Maximum UDP port to bind on") = "65535";
2806 ::arg().set("udp-source-port-avoid", "List of comma separated UDP port number to avoid") = "11211";
2807 ::arg().set("rng", "Specify random number generator to use. Valid values are auto,sodium,openssl,getrandom,arc4random,urandom.") = "auto";
2808 ::arg().set("public-suffix-list-file", "Path to the Public Suffix List file, if any") = "";
2809 ::arg().set("distribution-load-factor", "The load factor used when PowerDNS is distributing queries to worker threads") = "0.0";
2811 ::arg().setSwitch("qname-minimization", "Use Query Name Minimization") = "yes";
2812 ::arg().setSwitch("nothing-below-nxdomain", "When an NXDOMAIN exists in cache for a name with fewer labels than the qname, send NXDOMAIN without doing a lookup (see RFC 8020)") = "dnssec";
2813 ::arg().set("max-generate-steps", "Maximum number of $GENERATE steps when loading a zone from a file") = "0";
2814 ::arg().set("max-include-depth", "Maximum nested $INCLUDE depth when loading a zone from a file") = "20";
2815 ::arg().set("record-cache-shards", "Number of shards in the record cache") = "1024";
2816 ::arg().set("refresh-on-ttl-perc", "If a record is requested from the cache and only this % of original TTL remains, refetch") = "0";
2817 ::arg().set("record-cache-locked-ttl-perc", "Replace records in record cache only after this % of original TTL has passed") = "0";
2819 ::arg().set("x-dnssec-names", "Collect DNSSEC statistics for names or suffixes in this list in separate x-dnssec counters") = "";
2822 ::arg().set("new-domain-tracking", "Track newly observed domains (i.e. never seen before).") = "no";
2823 ::arg().set("new-domain-log", "Log newly observed domains.") = "yes";
2824 ::arg().set("new-domain-lookup", "Perform a DNS lookup newly observed domains as a subdomain of the configured domain") = "";
2825 ::arg().set("new-domain-history-dir", "Persist new domain tracking data here to persist between restarts") = string(NODCACHEDIR
) + "/nod";
2826 ::arg().set("new-domain-whitelist", "List of domains (and implicitly all subdomains) which will never be considered a new domain (deprecated)") = "";
2827 ::arg().set("new-domain-ignore-list", "List of domains (and implicitly all subdomains) which will never be considered a new domain") = "";
2828 ::arg().set("new-domain-db-size", "Size of the DB used to track new domains in terms of number of cells. Defaults to 67108864") = "67108864";
2829 ::arg().set("new-domain-pb-tag", "If protobuf is configured, the tag to use for messages containing newly observed domains. Defaults to 'pdns-nod'") = "pdns-nod";
2830 ::arg().set("unique-response-tracking", "Track unique responses (tuple of query name, type and RR).") = "no";
2831 ::arg().set("unique-response-log", "Log unique responses") = "yes";
2832 ::arg().set("unique-response-history-dir", "Persist unique response tracking data here to persist between restarts") = string(NODCACHEDIR
) + "/udr";
2833 ::arg().set("unique-response-db-size", "Size of the DB used to track unique responses in terms of number of cells. Defaults to 67108864") = "67108864";
2834 ::arg().set("unique-response-pb-tag", "If protobuf is configured, the tag to use for messages containing unique DNS responses. Defaults to 'pdns-udr'") = "pdns-udr";
2835 #endif /* NOD_ENABLED */
2837 ::arg().setSwitch("extended-resolution-errors", "If set, send an EDNS Extended Error extension on resolution failures, like DNSSEC validation errors") = "no";
2839 ::arg().set("aggressive-nsec-cache-size", "The number of records to cache in the aggressive cache. If set to a value greater than 0, and DNSSEC processing or validation is enabled, the recursor will cache NSEC and NSEC3 records to generate negative answers, as defined in rfc8198") = "100000";
2841 ::arg().set("edns-padding-from", "List of netmasks (proxy IP in case of proxy-protocol presence, client IP otherwise) for which EDNS padding will be enabled in responses, provided that 'edns-padding-mode' applies") = "";
2842 ::arg().set("edns-padding-mode", "Whether to add EDNS padding to all responses ('always') or only to responses for queries containing the EDNS padding option ('padded-queries-only', the default). In both modes, padding will only be added to responses for queries coming from `edns-padding-from`_ sources") = "padded-queries-only";
2843 ::arg().set("edns-padding-tag", "Packetcache tag associated to responses sent with EDNS padding, to prevent sending these to clients for which padding is not enabled.") = "7830";
2844 ::arg().setSwitch("edns-padding-out", "Whether to add EDNS padding to outgoing DoT messages") = "yes";
2846 ::arg().setSwitch("dot-to-port-853", "Force DoT connection to target port 853 if DoT compiled in") = "yes";
2847 ::arg().set("dot-to-auth-names", "Use DoT to authoritative servers with these names or suffixes") = "";
2848 ::arg().set("event-trace-enabled", "If set, event traces are collected and send out via protobuf logging (1), logfile (2) or both(3)") = "0";
2850 ::arg().set("tcp-out-max-idle-ms", "Time TCP/DoT connections are left idle in milliseconds or 0 if no limit") = "10000";
2851 ::arg().set("tcp-out-max-idle-per-auth", "Maximum number of idle TCP/DoT connections to a specific IP per thread, 0 means do not keep idle connections open") = "10";
2852 ::arg().set("tcp-out-max-queries", "Maximum total number of queries per TCP/DoT connection, 0 means no limit") = "0";
2853 ::arg().set("tcp-out-max-idle-per-thread", "Maximum number of idle TCP/DoT connections per thread") = "100";
2854 ::arg().setSwitch("structured-logging", "Prefer structured logging") = "yes";
2855 ::arg().set("structured-logging-backend", "Structured logging backend") = "default";
2856 ::arg().setSwitch("save-parent-ns-set", "Save parent NS set to be used if child NS set fails") = "yes";
2857 ::arg().set("max-busy-dot-probes", "Maximum number of concurrent DoT probes") = "0";
2858 ::arg().set("serve-stale-extensions", "Number of times a record's ttl is extended by 30s to be served stale") = "0";
2860 ::arg().setCmd("help", "Provide a helpful message");
2861 ::arg().setCmd("version", "Print version string");
2862 ::arg().setCmd("config", "Output blank configuration. You can use --config=check to test the config file and command line arguments.");
2863 ::arg().setDefaults();
2864 g_log
.toConsole(Logger::Info
);
2865 ::arg().laxParse(argc
, argv
); // do a lax parse
2867 if (::arg().mustDo("version")) {
2868 showProductVersion();
2869 showBuildConfiguration();
2872 if (::arg().mustDo("help")) {
2873 cout
<< "syntax:" << endl
2875 cout
<< ::arg().helpstring(::arg()["help"]) << endl
;
2879 // Pick up options given on command line to setup logging asap.
2880 g_quiet
= ::arg().mustDo("quiet");
2881 s_logUrgency
= (Logger::Urgency
)::arg().asNum("loglevel");
2882 g_slogStructured
= ::arg().mustDo("structured-logging");
2883 s_structured_logger_backend
= ::arg()["structured-logging-backend"];
2885 if (s_logUrgency
< Logger::Error
) {
2886 s_logUrgency
= Logger::Error
;
2888 if (!g_quiet
&& s_logUrgency
< Logger::Info
) { // Logger::Info=6, Logger::Debug=7
2889 s_logUrgency
= Logger::Info
; // if you do --quiet=no, you need Info to also see the query log
2891 g_log
.setLoglevel(s_logUrgency
);
2892 g_log
.toConsole(s_logUrgency
);
2894 string configname
= ::arg()["config-dir"] + "/recursor.conf";
2895 if (!::arg()["config-name"].empty()) {
2896 configname
= ::arg()["config-dir"] + "/recursor-" + ::arg()["config-name"] + ".conf";
2897 g_programname
+= "-" + ::arg()["config-name"];
2899 cleanSlashes(configname
);
2901 if (!::arg().getCommands().empty()) {
2902 cerr
<< "Fatal: non-option";
2903 if (::arg().getCommands().size() > 1) {
2908 for (const auto& c
: ::arg().getCommands()) {
2915 cerr
<< ") on the command line, perhaps a '--setting=123' statement missed the '='?" << endl
;
2919 if (s_structured_logger_backend
== "systemd-journal") {
2921 if (int fd
= sd_journal_stream_fd("pdns-recusor", LOG_DEBUG
, 0); fd
>= 0) {
2922 g_slog
= Logging::Logger::create(loggerSDBackend
);
2926 if (g_slog
== nullptr) {
2927 cerr
<< "Structured logging to systemd-journal requested but it is not available" << endl
;
2931 if (g_slog
== nullptr) {
2932 g_slog
= Logging::Logger::create(loggerBackend
);
2935 // Missing: a mechanism to call setVerbosity(x)
2936 auto startupLog
= g_slog
->withName("config");
2937 g_slogtcpin
= g_slog
->withName("in")->withValues("proto", Logging::Loggable("tcp"));
2938 g_slogudpin
= g_slog
->withName("in")->withValues("proto", Logging::Loggable("udp"));
2939 g_slogout
= g_slog
->withName("out");
2941 ::arg().setSLog(startupLog
);
2943 if (::arg().mustDo("config")) {
2944 string config
= ::arg()["config"];
2945 if (config
== "check") {
2947 if (!::arg().file(configname
.c_str())) {
2948 SLOG(g_log
<< Logger::Warning
<< "Unable to open configuration file '" << configname
<< "'" << endl
,
2949 startupLog
->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname
)));
2952 ::arg().parse(argc
, argv
);
2955 catch (const ArgException
& argException
) {
2956 SLOG(g_log
<< Logger::Warning
<< "Unable to parse configuration file '" << configname
<< "': " << argException
.reason
<< endl
,
2957 startupLog
->error("Cannot parse configuration", "Unable to parse configuration file", "config_file", Logging::Loggable(configname
), "reason", Logging::Loggable(argException
.reason
)));
2961 else if (config
== "default" || config
.empty()) {
2962 cout
<< ::arg().configstring(false, true);
2964 else if (config
== "diff") {
2965 if (!::arg().laxFile(configname
.c_str())) {
2966 SLOG(g_log
<< Logger::Warning
<< "Unable to open configuration file '" << configname
<< "'" << endl
,
2967 startupLog
->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname
)));
2970 ::arg().laxParse(argc
, argv
);
2971 cout
<< ::arg().configstring(true, false);
2974 if (!::arg().laxFile(configname
.c_str())) {
2975 SLOG(g_log
<< Logger::Warning
<< "Unable to open configuration file '" << configname
<< "'" << endl
,
2976 startupLog
->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname
)));
2979 ::arg().laxParse(argc
, argv
);
2980 cout
<< ::arg().configstring(true, true);
2985 if (!::arg().file(configname
.c_str())) {
2986 SLOG(g_log
<< Logger::Warning
<< "Unable to open configuration file '" << configname
<< "'" << endl
,
2987 startupLog
->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname
)));
2990 // Reparse, now with config file as well
2991 ::arg().parse(argc
, argv
);
2993 g_quiet
= ::arg().mustDo("quiet");
2994 s_logUrgency
= (Logger::Urgency
)::arg().asNum("loglevel");
2995 g_slogStructured
= ::arg().mustDo("structured-logging");
2997 if (s_logUrgency
< Logger::Error
) {
2998 s_logUrgency
= Logger::Error
;
3000 if (!g_quiet
&& s_logUrgency
< Logger::Info
) { // Logger::Info=6, Logger::Debug=7
3001 s_logUrgency
= Logger::Info
; // if you do --quiet=no, you need Info to also see the query log
3003 g_log
.setLoglevel(s_logUrgency
);
3004 g_log
.toConsole(s_logUrgency
);
3006 if (!::arg()["chroot"].empty() && !::arg()["api-config-dir"].empty()) {
3007 SLOG(g_log
<< Logger::Error
<< "Using chroot and enabling the API is not possible" << endl
,
3008 startupLog
->info(Logr::Error
, "Cannot use chroot and enable the API at the same time"));
3012 if (::arg()["socket-dir"].empty()) {
3013 if (::arg()["chroot"].empty())
3014 ::arg().set("socket-dir") = std::string(LOCALSTATEDIR
) + "/pdns-recursor";
3016 ::arg().set("socket-dir") = "/";
3019 if (::arg().asNum("threads") == 1) {
3020 if (::arg().mustDo("pdns-distributes-queries")) {
3021 SLOG(g_log
<< Logger::Warning
<< "Asked to run with pdns-distributes-queries set but no distributor threads, raising to 1" << endl
,
3022 startupLog
->v(1)->info("Only one thread, no need to distribute queries ourselves"));
3023 ::arg().set("pdns-distributes-queries") = "no";
3027 if (::arg().mustDo("pdns-distributes-queries") && ::arg().asNum("distributor-threads") <= 0) {
3028 SLOG(g_log
<< Logger::Warning
<< "Asked to run with pdns-distributes-queries set but no distributor threads, raising to 1" << endl
,
3029 startupLog
->v(1)->info("Asked to run with pdns-distributes-queries set but no distributor threads, raising to 1"));
3030 ::arg().set("distributor-threads") = "1";
3033 if (!::arg().mustDo("pdns-distributes-queries")) {
3034 ::arg().set("distributor-threads") = "0";
3037 g_recCache
= std::make_unique
<MemRecursorCache
>(::arg().asNum("record-cache-shards"));
3038 g_negCache
= std::make_unique
<NegCache
>(::arg().asNum("record-cache-shards"));
3040 ret
= serviceMain(argc
, argv
, startupLog
);
3042 catch (const PDNSException
& ae
) {
3043 SLOG(g_log
<< Logger::Error
<< "Exception: " << ae
.reason
<< endl
,
3044 g_slog
->withName("config")->error(Logr::Critical
, ae
.reason
, "Fatal error", "exception", Logging::Loggable("PDNSException")));
3047 catch (const std::exception
& e
) {
3048 SLOG(g_log
<< Logger::Error
<< "STL Exception: " << e
.what() << endl
,
3049 g_slog
->withName("config")->error(Logr::Critical
, e
.what(), "Fatal error", "exception", Logging::Loggable("std::exception")));
3053 SLOG(g_log
<< Logger::Error
<< "any other exception in main: " << endl
,
3054 g_slog
->withName("config")->info(Logr::Critical
, "Fatal error"));
3061 static RecursorControlChannel::Answer
* doReloadLuaScript()
3063 string fname
= ::arg()["lua-dns-script"];
3064 auto log
= g_slog
->withName("runtime")->withValues("name", Logging::Loggable(fname
));
3066 if (fname
.empty()) {
3068 SLOG(g_log
<< Logger::Info
<< RecThreadInfo::id() << " Unloaded current lua script" << endl
,
3069 log
->info(Logr::Info
, "Unloaded current lua script"));
3070 return new RecursorControlChannel::Answer
{0, string("unloaded\n")};
3073 t_pdl
= std::make_shared
<RecursorLua4
>();
3075 t_pdl
->loadFile(fname
);
3077 catch (std::runtime_error
& ex
) {
3078 string msg
= std::to_string(RecThreadInfo::id()) + " Retaining current script, could not read '" + fname
+ "': " + ex
.what();
3079 SLOG(g_log
<< Logger::Error
<< msg
<< endl
,
3080 log
->error(Logr::Error
, ex
.what(), "Retaining current script, could not read new script"));
3081 return new RecursorControlChannel::Answer
{1, msg
+ "\n"};
3084 catch (std::exception
& e
) {
3085 SLOG(g_log
<< Logger::Error
<< RecThreadInfo::id() << " Retaining current script, error from '" << fname
<< "': " << e
.what() << endl
,
3086 log
->error(Logr::Error
, e
.what(), "Retaining current script, error in new script"));
3087 return new RecursorControlChannel::Answer
{1, string("retaining current script, error from '" + fname
+ "': " + e
.what() + "\n")};
3090 SLOG(g_log
<< Logger::Warning
<< RecThreadInfo::id() << " (Re)loaded lua script from '" << fname
<< "'" << endl
,
3091 log
->info(Logr::Warning
, "(Re)loaded lua script"));
3092 return new RecursorControlChannel::Answer
{0, string("(re)loaded '" + fname
+ "'\n")};
3095 RecursorControlChannel::Answer
doQueueReloadLuaScript(vector
<string
>::const_iterator begin
, vector
<string
>::const_iterator end
)
3098 ::arg().set("lua-dns-script") = *begin
;
3100 return broadcastAccFunction
<RecursorControlChannel::Answer
>(doReloadLuaScript
);
3103 static string
* pleaseUseNewTraceRegex(const std::string
& newRegex
)
3105 if (newRegex
.empty()) {
3106 t_traceRegex
.reset();
3107 return new string("unset\n");
3110 t_traceRegex
= std::make_shared
<Regex
>(newRegex
);
3111 return new string("ok\n");
3114 catch (PDNSException
& ae
) {
3115 return new string(ae
.reason
+ "\n");
3118 string
doTraceRegex(vector
<string
>::const_iterator begin
, vector
<string
>::const_iterator end
)
3120 return broadcastAccFunction
<string
>([=] { return pleaseUseNewTraceRegex(begin
!= end
? *begin
: ""); });
3123 static uint64_t* pleaseWipePacketCache(const DNSName
& canon
, bool subtree
, uint16_t qtype
)
3125 return new uint64_t(t_packetCache
? t_packetCache
->doWipePacketCache(canon
, qtype
, subtree
) : 0);
3128 struct WipeCacheResult
wipeCaches(const DNSName
& canon
, bool subtree
, uint16_t qtype
)
3130 struct WipeCacheResult res
;
3133 res
.record_count
= g_recCache
->doWipeCache(canon
, subtree
, qtype
);
3134 // scanbuild complains here about an allocated function object that is being leaked. Needs investigation
3135 res
.packet_count
= broadcastAccFunction
<uint64_t>([=] { return pleaseWipePacketCache(canon
, subtree
, qtype
); });
3136 res
.negative_record_count
= g_negCache
->wipe(canon
, subtree
);
3137 if (g_aggressiveNSECCache
) {
3138 g_aggressiveNSECCache
->removeZoneInfo(canon
, subtree
);
3141 catch (const std::exception
& e
) {
3142 auto log
= g_slog
->withName("runtime");
3143 SLOG(g_log
<< Logger::Warning
<< ", failed: " << e
.what() << endl
,
3144 log
->error(Logr::Warning
, e
.what(), "Wipecache failed"));