2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "rec-main.hh"
26 #include "aggressive_nsec.hh"
27 #include "capabilities.hh"
28 #include "arguments.hh"
29 #include "dns_random.hh"
30 #include "rec_channel.hh"
31 #include "rec-tcpout.hh"
33 #include "query-local-address.hh"
34 #include "validate-recursor.hh"
35 #include "pubsuffix.hh"
36 #include "opensslsigners.hh"
37 #include "ws-recursor.hh"
38 #include "rec-taskqueue.hh"
39 #include "secpoll-recursor.hh"
41 #include "dnsseckeeper.hh"
45 #endif /* NOD_ENABLED */
55 // All calls are coming form the same function, so no use for CODE_LINE, CODE_FUNC etc
56 #define SD_JOURNAL_SUPPRESS_LOCATION
57 #include <systemd/sd-daemon.h>
58 #include <systemd/sd-journal.h>
62 thread_local FrameStreamServersInfo t_frameStreamServersInfo
;
63 thread_local FrameStreamServersInfo t_nodFrameStreamServersInfo
;
64 #endif /* HAVE_FSTRM */
66 string g_programname
= "pdns_recursor";
68 RecursorControlChannel g_rcc
; // only active in the handler thread
69 bool g_regressionTestMode
;
73 DNSName g_nodLookupDomain
;
75 SuffixMatchNode g_nodDomainWL
;
76 std::string g_nod_pbtag
;
79 std::string g_udr_pbtag
;
80 thread_local
std::shared_ptr
<nod::NODDB
> t_nodDBp
;
81 thread_local
std::shared_ptr
<nod::UniqueResponseDB
> t_udrDBp
;
82 #endif /* NOD_ENABLED */
84 std::atomic
<bool> statsWanted
;
85 uint32_t g_disthashseed
;
86 bool g_useIncomingECS
;
87 NetmaskGroup g_proxyProtocolACL
;
88 boost::optional
<ComboAddress
> g_dns64Prefix
{boost::none
};
89 DNSName g_dns64PrefixReverse
;
90 std::shared_ptr
<SyncRes::domainmap_t
> g_initialDomainMap
; // new threads needs this to be setup
91 std::shared_ptr
<NetmaskGroup
> g_initialAllowFrom
; // new thread needs to be setup with this
92 std::shared_ptr
<NetmaskGroup
> g_initialAllowNotifyFrom
; // new threads need this to be setup
93 std::shared_ptr
<notifyset_t
> g_initialAllowNotifyFor
; // new threads need this to be setup
94 bool g_logRPZChanges
{false};
95 static time_t s_statisticsInterval
;
96 static std::atomic
<uint32_t> s_counter
;
99 static string s_structured_logger_backend
;
100 static Logger::Urgency s_logUrgency
;
102 std::shared_ptr
<Logr::Logger
> g_slogtcpin
;
103 std::shared_ptr
<Logr::Logger
> g_slogudpin
;
104 std::shared_ptr
<Logr::Logger
> g_slogudpout
;
106 /* without reuseport, all listeners share the same sockets */
107 deferredAdd_t g_deferredAdds
;
109 /* first we have the handler thread, t_id == 0 (some other
110 helper threads like SNMP might have t_id == 0 as well)
111 then the distributor threads if any
112 and finally the workers */
113 std::vector
<RecThreadInfo
> RecThreadInfo::s_threadInfos
;
115 std::unique_ptr
<ProxyMapping
> g_proxyMapping
; // new threads needs this to be setup
116 thread_local
std::unique_ptr
<ProxyMapping
> t_proxyMapping
;
118 bool RecThreadInfo::s_weDistributeQueries
; // if true, 1 or more threads listen on the incoming query sockets and distribute them to workers
119 unsigned int RecThreadInfo::s_numDistributorThreads
;
120 unsigned int RecThreadInfo::s_numWorkerThreads
;
121 thread_local
unsigned int RecThreadInfo::t_id
;
123 static std::map
<unsigned int, std::set
<int>> parseCPUMap(Logr::log_t log
)
125 std::map
<unsigned int, std::set
<int>> result
;
127 const std::string value
= ::arg()["cpu-map"];
129 if (!value
.empty() && !isSettingThreadCPUAffinitySupported()) {
130 SLOG(g_log
<< Logger::Warning
<< "CPU mapping requested but not supported, skipping" << endl
,
131 log
->info(Logr::Warning
, "CPU mapping requested but not supported, skipping"));
135 std::vector
<std::string
> parts
;
137 stringtok(parts
, value
, " \t");
139 for (const auto& part
: parts
) {
140 if (part
.find('=') == string::npos
) {
145 auto headers
= splitField(part
, '=');
146 boost::trim(headers
.first
);
147 boost::trim(headers
.second
);
149 auto threadId
= pdns::checked_stoi
<unsigned int>(headers
.first
);
150 std::vector
<std::string
> cpus
;
152 stringtok(cpus
, headers
.second
, ",");
154 for (const auto& cpu
: cpus
) {
155 int cpuId
= std::stoi(cpu
);
157 result
[threadId
].insert(cpuId
);
160 catch (const std::exception
& e
) {
161 SLOG(g_log
<< Logger::Error
<< "Error parsing cpu-map entry '" << part
<< "': " << e
.what() << endl
,
162 log
->error(Logr::Error
, e
.what(), "Error parsing cpu-map entry", "entry", Logging::Loggable(part
)));
169 static void setCPUMap(const std::map
<unsigned int, std::set
<int>>& cpusMap
, unsigned int n
, pthread_t tid
, Logr::log_t log
)
171 const auto& cpuMapping
= cpusMap
.find(n
);
172 if (cpuMapping
== cpusMap
.cend()) {
175 int ret
= mapThreadToCPUList(tid
, cpuMapping
->second
);
177 if (!g_slogStructured
) {
178 g_log
<< Logger::Info
<< "CPU affinity for thread " << n
<< " has been set to CPU map:";
179 for (const auto cpu
: cpuMapping
->second
) {
180 g_log
<< Logger::Info
<< " " << cpu
;
182 g_log
<< Logger::Info
<< endl
;
185 log
->info(Logr::Info
, "CPU affinity has been set", "thread", Logging::Loggable(n
), "cpumap", Logging::IterLoggable(cpuMapping
->second
.begin(), cpuMapping
->second
.end()));
189 if (!g_slogStructured
) {
190 g_log
<< Logger::Warning
<< "Error setting CPU affinity for thread " << n
<< " to CPU map:";
191 for (const auto cpu
: cpuMapping
->second
) {
192 g_log
<< Logger::Info
<< " " << cpu
;
194 g_log
<< Logger::Info
<< ' ' << stringerror(ret
) << endl
;
197 log
->error(Logr::Warning
, ret
, "Error setting CPU affinity", "thread", Logging::Loggable(n
), "cpumap", Logging::IterLoggable(cpuMapping
->second
.begin(), cpuMapping
->second
.end()));
202 static void recursorThread();
204 void RecThreadInfo::start(unsigned int tid
, const string
& tname
, const std::map
<unsigned int, std::set
<int>>& cpusMap
, Logr::log_t log
)
207 thread
= std::thread([tid
, tname
] {
209 const string threadPrefix
= "rec/";
210 setThreadName(threadPrefix
+ tname
);
213 setCPUMap(cpusMap
, tid
, thread
.native_handle(), log
);
216 int RecThreadInfo::runThreads(Logr::log_t log
)
218 int ret
= EXIT_SUCCESS
;
219 unsigned int currentThreadId
= 1;
220 const auto cpusMap
= parseCPUMap(log
);
222 if (RecThreadInfo::numDistributors() + RecThreadInfo::numWorkers() == 1) {
223 SLOG(g_log
<< Logger::Warning
<< "Operating with single distributor/worker thread" << endl
,
224 log
->info(Logr::Notice
, "Operating with single distributor/worker thread"));
226 /* This thread handles the web server, carbon, statistics and the control channel */
227 auto& handlerInfo
= RecThreadInfo::info(0);
228 handlerInfo
.setHandler();
229 handlerInfo
.start(0, "web+stat", cpusMap
, log
);
230 auto& taskInfo
= RecThreadInfo::info(2);
231 taskInfo
.setTaskThread();
232 taskInfo
.start(2, "task", cpusMap
, log
);
234 auto& info
= RecThreadInfo::info(currentThreadId
);
237 RecThreadInfo::setThreadId(currentThreadId
++);
240 handlerInfo
.thread
.join();
241 if (handlerInfo
.exitCode
!= 0) {
242 ret
= handlerInfo
.exitCode
;
244 taskInfo
.thread
.join();
245 if (taskInfo
.exitCode
!= 0) {
246 ret
= taskInfo
.exitCode
;
250 // Setup RecThreadInfo objects
251 unsigned int tmp
= currentThreadId
;
252 if (RecThreadInfo::weDistributeQueries()) {
253 for (unsigned int thread
= 0; thread
< RecThreadInfo::numDistributors(); ++thread
) {
254 RecThreadInfo::info(tmp
++).setListener();
257 for (unsigned int thread
= 0; thread
< RecThreadInfo::numWorkers(); ++thread
) {
258 auto& info
= RecThreadInfo::info(tmp
++);
259 info
.setListener(!RecThreadInfo::weDistributeQueries());
262 for (unsigned int thread
= 0; thread
< RecThreadInfo::numTaskThreads(); ++thread
) {
263 auto& info
= RecThreadInfo::info(tmp
++);
264 info
.setTaskThread();
267 // And now start the actual threads
268 if (RecThreadInfo::weDistributeQueries()) {
269 SLOG(g_log
<< Logger::Warning
<< "Launching " << RecThreadInfo::numDistributors() << " distributor threads" << endl
,
270 log
->info(Logr::Notice
, "Launching distributor threads", "count", Logging::Loggable(RecThreadInfo::numDistributors())));
271 for (unsigned int thread
= 0; thread
< RecThreadInfo::numDistributors(); ++thread
) {
272 auto& info
= RecThreadInfo::info(currentThreadId
);
273 info
.start(currentThreadId
++, "distr", cpusMap
, log
);
276 SLOG(g_log
<< Logger::Warning
<< "Launching " << RecThreadInfo::numWorkers() << " worker threads" << endl
,
277 log
->info(Logr::Notice
, "Launching worker threads", "count", Logging::Loggable(RecThreadInfo::numWorkers())));
279 for (unsigned int thread
= 0; thread
< RecThreadInfo::numWorkers(); ++thread
) {
280 auto& info
= RecThreadInfo::info(currentThreadId
);
281 info
.start(currentThreadId
++, "worker", cpusMap
, log
);
284 for (unsigned int thread
= 0; thread
< RecThreadInfo::numTaskThreads(); ++thread
) {
285 auto& info
= RecThreadInfo::info(currentThreadId
);
286 info
.start(currentThreadId
++, "task", cpusMap
, log
);
289 /* This thread handles the web server, carbon, statistics and the control channel */
290 auto& info
= RecThreadInfo::info(0);
292 info
.start(0, "web+stat", cpusMap
, log
);
294 for (auto& tInfo
: RecThreadInfo::infos()) {
296 if (tInfo
.exitCode
!= 0) {
297 ret
= tInfo
.exitCode
;
304 void RecThreadInfo::makeThreadPipes(Logr::log_t log
)
306 auto pipeBufferSize
= ::arg().asNum("distribution-pipe-buffer-size");
307 if (pipeBufferSize
> 0) {
308 SLOG(g_log
<< Logger::Info
<< "Resizing the buffer of the distribution pipe to " << pipeBufferSize
<< endl
,
309 log
->info(Logr::Info
, "Resizing the buffer of the distribution pipe", "size", Logging::Loggable(pipeBufferSize
)));
312 /* thread 0 is the handler / SNMP, worker threads start at 1 */
313 for (unsigned int thread
= 0; thread
< numRecursorThreads(); ++thread
) {
314 auto& threadInfo
= info(thread
);
316 std::array
<int, 2> fileDesc
{};
317 if (pipe(fileDesc
.data()) < 0) {
318 unixDie("Creating pipe for inter-thread communications");
321 threadInfo
.pipes
.readToThread
= fileDesc
[0];
322 threadInfo
.pipes
.writeToThread
= fileDesc
[1];
324 // handler thread only gets first pipe, not the others
329 if (pipe(fileDesc
.data()) < 0) {
330 unixDie("Creating pipe for inter-thread communications");
333 threadInfo
.pipes
.readFromThread
= fileDesc
[0];
334 threadInfo
.pipes
.writeFromThread
= fileDesc
[1];
336 if (pipe(fileDesc
.data()) < 0) {
337 unixDie("Creating pipe for inter-thread communications");
340 threadInfo
.pipes
.readQueriesToThread
= fileDesc
[0];
341 threadInfo
.pipes
.writeQueriesToThread
= fileDesc
[1];
343 if (pipeBufferSize
> 0) {
344 if (!setPipeBufferSize(threadInfo
.pipes
.writeQueriesToThread
, pipeBufferSize
)) {
346 SLOG(g_log
<< Logger::Warning
<< "Error resizing the buffer of the distribution pipe for thread " << thread
<< " to " << pipeBufferSize
<< ": " << stringerror(err
) << endl
,
347 log
->error(Logr::Warning
, err
, "Error resizing the buffer of the distribution pipe for thread", "thread", Logging::Loggable(thread
), "size", Logging::Loggable(pipeBufferSize
)));
348 auto existingSize
= getPipeBufferSize(threadInfo
.pipes
.writeQueriesToThread
);
349 if (existingSize
> 0) {
350 SLOG(g_log
<< Logger::Warning
<< "The current size of the distribution pipe's buffer for thread " << thread
<< " is " << existingSize
<< endl
,
351 log
->info(Logr::Warning
, "The current size of the distribution pipe's buffer for thread", "thread", Logging::Loggable(thread
), "size", Logging::Loggable(existingSize
)));
356 if (!setNonBlocking(threadInfo
.pipes
.writeQueriesToThread
)) {
357 unixDie("Making pipe for inter-thread communications non-blocking");
364 static ArgvMap theArg
;
368 static FDMultiplexer
* getMultiplexer(Logr::log_t log
)
370 FDMultiplexer
* ret
= nullptr;
371 for (const auto& mplexer
: FDMultiplexer::getMultiplexerMap()) {
373 ret
= mplexer
.second(FDMultiplexer::s_maxevents
);
376 catch (FDMultiplexerException
& fe
) {
377 SLOG(g_log
<< Logger::Warning
<< "Non-fatal error initializing possible multiplexer (" << fe
.what() << "), falling back" << endl
,
378 log
->error(Logr::Warning
, fe
.what(), "Non-fatal error initializing possible multiplexer, falling back"));
381 SLOG(g_log
<< Logger::Warning
<< "Non-fatal error initializing possible multiplexer" << endl
,
382 log
->info(Logr::Warning
, "Non-fatal error initializing possible multiplexer"));
385 SLOG(g_log
<< Logger::Error
<< "No working multiplexer found!" << endl
,
386 log
->info(Logr::Error
, "No working multiplexer found!"));
390 static std::shared_ptr
<std::vector
<std::unique_ptr
<RemoteLogger
>>> startProtobufServers(const ProtobufExportConfig
& config
, Logr::log_t log
)
392 auto result
= std::make_shared
<std::vector
<std::unique_ptr
<RemoteLogger
>>>();
394 for (const auto& server
: config
.servers
) {
396 auto logger
= make_unique
<RemoteLogger
>(server
, config
.timeout
, 100 * config
.maxQueuedEntries
, config
.reconnectWaitTime
, config
.asyncConnect
);
397 logger
->setLogQueries(config
.logQueries
);
398 logger
->setLogResponses(config
.logResponses
);
399 result
->emplace_back(std::move(logger
));
401 catch (const std::exception
& e
) {
402 SLOG(g_log
<< Logger::Error
<< "Error while starting protobuf logger to '" << server
<< ": " << e
.what() << endl
,
403 log
->error(Logr::Error
, e
.what(), "Exception while starting protobuf logger", "exception", Logging::Loggable("std::exception"), "server", Logging::Loggable(server
)));
405 catch (const PDNSException
& e
) {
406 SLOG(g_log
<< Logger::Error
<< "Error while starting protobuf logger to '" << server
<< ": " << e
.reason
<< endl
,
407 log
->error(Logr::Error
, e
.reason
, "Exception while starting protobuf logger", "exception", Logging::Loggable("PDNSException"), "server", Logging::Loggable(server
)));
414 bool checkProtobufExport(LocalStateHolder
<LuaConfigItems
>& luaconfsLocal
)
416 if (!luaconfsLocal
->protobufExportConfig
.enabled
) {
417 if (t_protobufServers
.servers
) {
418 t_protobufServers
.servers
.reset();
419 t_protobufServers
.config
= luaconfsLocal
->protobufExportConfig
;
425 /* if the server was not running, or if it was running according to a
426 previous configuration */
427 if (t_protobufServers
.generation
< luaconfsLocal
->generation
&& t_protobufServers
.config
!= luaconfsLocal
->protobufExportConfig
) {
429 if (t_protobufServers
.servers
) {
430 t_protobufServers
.servers
.reset();
432 auto log
= g_slog
->withName("protobuf");
433 t_protobufServers
.servers
= startProtobufServers(luaconfsLocal
->protobufExportConfig
, log
);
434 t_protobufServers
.config
= luaconfsLocal
->protobufExportConfig
;
435 t_protobufServers
.generation
= luaconfsLocal
->generation
;
441 bool checkOutgoingProtobufExport(LocalStateHolder
<LuaConfigItems
>& luaconfsLocal
)
443 if (!luaconfsLocal
->outgoingProtobufExportConfig
.enabled
) {
444 if (t_outgoingProtobufServers
.servers
) {
445 t_outgoingProtobufServers
.servers
.reset();
446 t_outgoingProtobufServers
.config
= luaconfsLocal
->outgoingProtobufExportConfig
;
452 /* if the server was not running, or if it was running according to a
453 previous configuration */
454 if (t_outgoingProtobufServers
.generation
< luaconfsLocal
->generation
&& t_outgoingProtobufServers
.config
!= luaconfsLocal
->outgoingProtobufExportConfig
) {
456 if (t_outgoingProtobufServers
.servers
) {
457 t_outgoingProtobufServers
.servers
.reset();
459 auto log
= g_slog
->withName("protobuf");
460 t_outgoingProtobufServers
.servers
= startProtobufServers(luaconfsLocal
->outgoingProtobufExportConfig
, log
);
461 t_outgoingProtobufServers
.config
= luaconfsLocal
->outgoingProtobufExportConfig
;
462 t_outgoingProtobufServers
.generation
= luaconfsLocal
->generation
;
468 void protobufLogQuery(LocalStateHolder
<LuaConfigItems
>& luaconfsLocal
, const boost::uuids::uuid
& uniqueId
, const ComboAddress
& remote
, const ComboAddress
& local
, const ComboAddress
& mappedSource
, const Netmask
& ednssubnet
, bool tcp
, uint16_t queryID
, size_t len
, const DNSName
& qname
, uint16_t qtype
, uint16_t qclass
, const std::unordered_set
<std::string
>& policyTags
, const std::string
& requestorId
, const std::string
& deviceId
, const std::string
& deviceName
, const std::map
<std::string
, RecursorLua4::MetaValue
>& meta
)
470 auto log
= g_slog
->withName("pblq");
472 if (!t_protobufServers
.servers
) {
476 ComboAddress requestor
;
477 if (!luaconfsLocal
->protobufExportConfig
.logMappedFrom
) {
478 Netmask
requestorNM(remote
, remote
.sin4
.sin_family
== AF_INET
? luaconfsLocal
->protobufMaskV4
: luaconfsLocal
->protobufMaskV6
);
479 requestor
= requestorNM
.getMaskedNetwork();
480 requestor
.setPort(remote
.getPort());
483 Netmask
requestorNM(mappedSource
, mappedSource
.sin4
.sin_family
== AF_INET
? luaconfsLocal
->protobufMaskV4
: luaconfsLocal
->protobufMaskV6
);
484 requestor
= requestorNM
.getMaskedNetwork();
485 requestor
.setPort(mappedSource
.getPort());
488 pdns::ProtoZero::RecMessage msg
{128, std::string::size_type(policyTags
.empty() ? 0 : 64)}; // It's a guess
489 msg
.setType(pdns::ProtoZero::Message::MessageType::DNSQueryType
);
490 msg
.setRequest(uniqueId
, requestor
, local
, qname
, qtype
, qclass
, queryID
, tcp
? pdns::ProtoZero::Message::TransportProtocol::TCP
: pdns::ProtoZero::Message::TransportProtocol::UDP
, len
);
491 msg
.setServerIdentity(SyncRes::s_serverID
);
492 msg
.setEDNSSubnet(ednssubnet
, ednssubnet
.isIPv4() ? luaconfsLocal
->protobufMaskV4
: luaconfsLocal
->protobufMaskV6
);
493 msg
.setRequestorId(requestorId
);
494 msg
.setDeviceId(deviceId
);
495 msg
.setDeviceName(deviceName
);
497 if (!policyTags
.empty()) {
498 msg
.addPolicyTags(policyTags
);
500 for (const auto& mit
: meta
) {
501 msg
.setMeta(mit
.first
, mit
.second
.stringVal
, mit
.second
.intVal
);
504 std::string
strMsg(msg
.finishAndMoveBuf());
505 for (auto& server
: *t_protobufServers
.servers
) {
506 remoteLoggerQueueData(*server
, strMsg
);
510 void protobufLogResponse(pdns::ProtoZero::RecMessage
& message
)
512 if (!t_protobufServers
.servers
) {
516 std::string
msg(message
.finishAndMoveBuf());
517 for (auto& server
: *t_protobufServers
.servers
) {
518 remoteLoggerQueueData(*server
, msg
);
522 void protobufLogResponse(const struct dnsheader
* header
, LocalStateHolder
<LuaConfigItems
>& luaconfsLocal
,
523 const RecursorPacketCache::OptPBData
& pbData
, const struct timeval
& tval
,
524 bool tcp
, const ComboAddress
& source
, const ComboAddress
& destination
,
525 const ComboAddress
& mappedSource
,
526 const EDNSSubnetOpts
& ednssubnet
,
527 const boost::uuids::uuid
& uniqueId
, const string
& requestorId
, const string
& deviceId
,
528 const string
& deviceName
, const std::map
<std::string
, RecursorLua4::MetaValue
>& meta
,
529 const RecEventTrace
& eventTrace
)
531 pdns::ProtoZero::RecMessage
pbMessage(pbData
? pbData
->d_message
: "", pbData
? pbData
->d_response
: "", 64, 10); // The extra bytes we are going to add
532 // Normally we take the immutable string from the cache and append a few values, but if it's not there (can this happen?)
533 // we start with an empty string and append the minimal
535 pbMessage
.setType(pdns::ProtoZero::Message::MessageType::DNSResponseType
);
536 pbMessage
.setServerIdentity(SyncRes::s_serverID
);
540 if (g_useKernelTimestamp
&& tval
.tv_sec
!= 0) {
541 pbMessage
.setQueryTime(tval
.tv_sec
, tval
.tv_usec
);
544 pbMessage
.setQueryTime(g_now
.tv_sec
, g_now
.tv_usec
);
548 if (!luaconfsLocal
->protobufExportConfig
.logMappedFrom
) {
549 Netmask
requestorNM(source
, source
.sin4
.sin_family
== AF_INET
? luaconfsLocal
->protobufMaskV4
: luaconfsLocal
->protobufMaskV6
);
550 auto requestor
= requestorNM
.getMaskedNetwork();
551 pbMessage
.setFrom(requestor
);
552 pbMessage
.setFromPort(source
.getPort());
555 Netmask
requestorNM(mappedSource
, mappedSource
.sin4
.sin_family
== AF_INET
? luaconfsLocal
->protobufMaskV4
: luaconfsLocal
->protobufMaskV6
);
556 auto requestor
= requestorNM
.getMaskedNetwork();
557 pbMessage
.setFrom(requestor
);
558 pbMessage
.setFromPort(mappedSource
.getPort());
560 pbMessage
.setMessageIdentity(uniqueId
);
561 pbMessage
.setTo(destination
);
562 pbMessage
.setSocketProtocol(tcp
? pdns::ProtoZero::Message::TransportProtocol::TCP
: pdns::ProtoZero::Message::TransportProtocol::UDP
);
563 pbMessage
.setId(header
->id
);
566 pbMessage
.setEDNSSubnet(ednssubnet
.source
, ednssubnet
.source
.isIPv4() ? luaconfsLocal
->protobufMaskV4
: luaconfsLocal
->protobufMaskV6
);
567 pbMessage
.setRequestorId(requestorId
);
568 pbMessage
.setDeviceId(deviceId
);
569 pbMessage
.setDeviceName(deviceName
);
570 pbMessage
.setToPort(destination
.getPort());
571 for (const auto& metaItem
: meta
) {
572 pbMessage
.setMeta(metaItem
.first
, metaItem
.second
.stringVal
, metaItem
.second
.intVal
);
576 pbMessage
.setNewlyObservedDomain(false);
579 if (eventTrace
.enabled() && (SyncRes::s_event_trace_enabled
& SyncRes::event_trace_to_pb
) != 0) {
580 pbMessage
.addEvents(eventTrace
);
582 protobufLogResponse(pbMessage
);
587 static std::shared_ptr
<std::vector
<std::unique_ptr
<FrameStreamLogger
>>> startFrameStreamServers(const FrameStreamExportConfig
& config
, Logr::log_t log
)
589 auto result
= std::make_shared
<std::vector
<std::unique_ptr
<FrameStreamLogger
>>>();
591 for (const auto& server
: config
.servers
) {
593 std::unordered_map
<string
, unsigned> options
;
594 options
["bufferHint"] = config
.bufferHint
;
595 options
["flushTimeout"] = config
.flushTimeout
;
596 options
["inputQueueSize"] = config
.inputQueueSize
;
597 options
["outputQueueSize"] = config
.outputQueueSize
;
598 options
["queueNotifyThreshold"] = config
.queueNotifyThreshold
;
599 options
["reopenInterval"] = config
.reopenInterval
;
600 unique_ptr
<FrameStreamLogger
> fsl
= nullptr;
602 ComboAddress
address(server
);
603 fsl
= make_unique
<FrameStreamLogger
>(address
.sin4
.sin_family
, address
.toStringWithPort(), true, options
);
605 catch (const PDNSException
& e
) {
606 fsl
= make_unique
<FrameStreamLogger
>(AF_UNIX
, server
, true, options
);
608 fsl
->setLogQueries(config
.logQueries
);
609 fsl
->setLogResponses(config
.logResponses
);
610 fsl
->setLogNODs(config
.logNODs
);
611 fsl
->setLogUDRs(config
.logUDRs
);
612 result
->emplace_back(std::move(fsl
));
614 catch (const std::exception
& e
) {
615 SLOG(g_log
<< Logger::Error
<< "Error while starting dnstap framestream logger to '" << server
<< ": " << e
.what() << endl
,
616 log
->error(Logr::Error
, e
.what(), "Exception while starting dnstap framestream logger", "exception", Logging::Loggable("std::exception"), "server", Logging::Loggable(server
)));
618 catch (const PDNSException
& e
) {
619 SLOG(g_log
<< Logger::Error
<< "Error while starting dnstap framestream logger to '" << server
<< ": " << e
.reason
<< endl
,
620 log
->error(Logr::Error
, e
.reason
, "Exception while starting dnstap framestream logger", "exception", Logging::Loggable("PDNSException"), "server", Logging::Loggable(server
)));
627 static void asyncFrameStreamLoggersCleanup(std::shared_ptr
<std::vector
<std::unique_ptr
<FrameStreamLogger
>>>&& servers
)
629 auto thread
= std::thread([&] {
635 bool checkFrameStreamExport(LocalStateHolder
<LuaConfigItems
>& luaconfsLocal
, const FrameStreamExportConfig
& config
, FrameStreamServersInfo
& serverInfos
)
637 if (!config
.enabled
) {
638 if (serverInfos
.servers
) {
639 // dt's take care of cleanup
640 asyncFrameStreamLoggersCleanup(std::move(serverInfos
.servers
));
641 serverInfos
.config
= config
;
647 /* if the server was not running, or if it was running according to a previous
650 if (serverInfos
.generation
< luaconfsLocal
->generation
&& serverInfos
.config
!= config
) {
651 if (serverInfos
.servers
) {
652 // dt's take care of cleanup
653 asyncFrameStreamLoggersCleanup(std::move(serverInfos
.servers
));
656 auto dnsTapLog
= g_slog
->withName("dnstap");
657 serverInfos
.servers
= startFrameStreamServers(config
, dnsTapLog
);
658 serverInfos
.config
= config
;
659 serverInfos
.generation
= luaconfsLocal
->generation
;
665 #endif /* HAVE_FSTRM */
667 static void makeControlChannelSocket(int processNum
= -1)
669 string sockname
= ::arg()["socket-dir"] + "/" + g_programname
;
670 if (processNum
>= 0) {
671 sockname
+= "." + std::to_string(processNum
);
673 sockname
+= ".controlsocket";
674 g_rcc
.listen(sockname
);
676 uid_t sockowner
= -1;
677 gid_t sockgroup
= -1;
679 if (!::arg().isEmpty("socket-group")) {
680 sockgroup
= ::arg().asGid("socket-group");
682 if (!::arg().isEmpty("socket-owner")) {
683 sockowner
= ::arg().asUid("socket-owner");
686 if (sockgroup
!= static_cast<gid_t
>(-1) || sockowner
!= static_cast<uid_t
>(-1)) {
687 if (chown(sockname
.c_str(), sockowner
, sockgroup
) < 0) {
688 unixDie("Failed to chown control socket");
692 // do mode change if socket-mode is given
693 if (!::arg().isEmpty("socket-mode")) {
694 mode_t sockmode
= ::arg().asMode("socket-mode");
695 if (chmod(sockname
.c_str(), sockmode
) < 0) {
696 unixDie("Failed to chmod control socket");
701 static void writePid(Logr::log_t log
)
703 if (!::arg().mustDo("write-pid")) {
706 ofstream
ostr(g_pidfname
.c_str(), std::ios_base::app
);
708 ostr
<< Utility::getpid() << endl
;
712 SLOG(g_log
<< Logger::Error
<< "Writing pid for " << Utility::getpid() << " to " << g_pidfname
<< " failed: " << stringerror(err
) << endl
,
713 log
->error(Logr::Error
, err
, "Writing pid failed", "pid", Logging::Loggable(Utility::getpid()), "file", Logging::Loggable(g_pidfname
)));
717 static void checkSocketDir(Logr::log_t log
)
719 string
dir(::arg()["socket-dir"]);
722 struct stat dirStat
= {};
723 if (stat(dir
.c_str(), &dirStat
) == -1) {
724 msg
= "it does not exist or cannot access";
726 else if (!S_ISDIR(dirStat
.st_mode
)) {
727 msg
= "it is not a directory";
729 else if (access(dir
.c_str(), R_OK
| W_OK
| X_OK
) != 0) {
730 msg
= "cannot read, write or search";
735 dir
= ::arg()["chroot"] + dir
;
736 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
,
737 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
)));
742 static void setupNODThread(Logr::log_t log
)
745 uint32_t num_cells
= ::arg().asNum("new-domain-db-size");
746 t_nodDBp
= std::make_shared
<nod::NODDB
>(num_cells
);
748 t_nodDBp
->setCacheDir(::arg()["new-domain-history-dir"]);
750 catch (const PDNSException
& e
) {
751 SLOG(g_log
<< Logger::Error
<< "new-domain-history-dir (" << ::arg()["new-domain-history-dir"] << ") is not readable or does not exist" << endl
,
752 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"])));
755 if (!t_nodDBp
->init()) {
756 SLOG(g_log
<< Logger::Error
<< "Could not initialize domain tracking" << endl
,
757 log
->info(Logr::Error
, "Could not initialize domain tracking"));
760 std::thread
thread(nod::NODDB::startHousekeepingThread
, t_nodDBp
, std::this_thread::get_id());
764 uint32_t num_cells
= ::arg().asNum("unique-response-db-size");
765 t_udrDBp
= std::make_shared
<nod::UniqueResponseDB
>(num_cells
);
767 t_udrDBp
->setCacheDir(::arg()["unique-response-history-dir"]);
769 catch (const PDNSException
& e
) {
770 SLOG(g_log
<< Logger::Error
<< "unique-response-history-dir (" << ::arg()["unique-response-history-dir"] << ") is not readable or does not exist" << endl
,
771 log
->info(Logr::Error
, "unique-response-history-dir is not readable or does not exist", "dir", Logging::Loggable(::arg()["unique-response-history-dir"])));
774 if (!t_udrDBp
->init()) {
775 SLOG(g_log
<< Logger::Error
<< "Could not initialize unique response tracking" << endl
,
776 log
->info(Logr::Error
, "Could not initialize unique response tracking"));
779 std::thread
thread(nod::UniqueResponseDB::startHousekeepingThread
, t_udrDBp
, std::this_thread::get_id());
784 static void parseNODIgnorelist(const std::string
& wlist
)
786 vector
<string
> parts
;
787 stringtok(parts
, wlist
, ",; ");
788 for (const auto& part
: parts
) {
789 g_nodDomainWL
.add(DNSName(part
));
793 static void setupNODGlobal()
795 // Setup NOD subsystem
796 g_nodEnabled
= ::arg().mustDo("new-domain-tracking");
797 g_nodLookupDomain
= DNSName(::arg()["new-domain-lookup"]);
798 g_nodLog
= ::arg().mustDo("new-domain-log");
799 parseNODIgnorelist(::arg()["new-domain-whitelist"]);
800 parseNODIgnorelist(::arg()["new-domain-ignore-list"]);
802 // Setup Unique DNS Response subsystem
803 g_udrEnabled
= ::arg().mustDo("unique-response-tracking");
804 g_udrLog
= ::arg().mustDo("unique-response-log");
805 g_nod_pbtag
= ::arg()["new-domain-pb-tag"];
806 g_udr_pbtag
= ::arg()["unique-response-pb-tag"];
808 #endif /* NOD_ENABLED */
810 static void daemonize(Logr::log_t log
)
812 if (auto pid
= fork(); pid
!= 0) {
815 SLOG(g_log
<< Logger::Critical
<< "Fork failed: " << stringerror(err
) << endl
,
816 log
->error(Logr::Critical
, err
, "Fork failed"));
817 exit(1); // NOLINT(concurrency-mt-unsafe)
819 exit(0); // NOLINT(concurrency-mt-unsafe)
824 int devNull
= open("/dev/null", O_RDWR
); /* open stdin */
827 SLOG(g_log
<< Logger::Critical
<< "Unable to open /dev/null: " << stringerror(err
) << endl
,
828 log
->error(Logr::Critical
, err
, "Unable to open /dev/null"));
831 dup2(devNull
, 0); /* stdin */
832 dup2(devNull
, 1); /* stderr */
833 dup2(devNull
, 2); /* stderr */
838 static void termIntHandler([[maybe_unused
]] int arg
)
843 static void usr1Handler([[maybe_unused
]] int arg
)
848 static void usr2Handler([[maybe_unused
]] int arg
)
851 SyncRes::setDefaultLogMode(g_quiet
? SyncRes::LogNone
: SyncRes::Log
);
852 ::arg().set("quiet") = g_quiet
? "" : "no";
855 static void checkLinuxIPv6Limits([[maybe_unused
]] Logr::log_t log
)
859 if (readFileIfThere("/proc/sys/net/ipv6/route/max_size", &line
)) {
860 int lim
= std::stoi(line
);
862 SLOG(g_log
<< Logger::Error
<< "If using IPv6, please raise sysctl net.ipv6.route.max_size, currently set to " << lim
<< " which is < 16384" << endl
,
863 log
->info(Logr::Error
, "If using IPv6, please raise sysctl net.ipv6.route.max_size to a size >= 16384", "current", Logging::Loggable(lim
)));
869 static void checkOrFixFDS(Logr::log_t log
)
871 unsigned int availFDs
= getFilenumLimit();
872 unsigned int wantFDs
= g_maxMThreads
* RecThreadInfo::numWorkers() + 25; // even healthier margin then before
873 wantFDs
+= RecThreadInfo::numWorkers() * TCPOutConnectionManager::s_maxIdlePerThread
;
875 if (wantFDs
> availFDs
) {
876 unsigned int hardlimit
= getFilenumLimit(true);
877 if (hardlimit
>= wantFDs
) {
878 setFilenumLimit(wantFDs
);
879 SLOG(g_log
<< Logger::Warning
<< "Raised soft limit on number of filedescriptors to " << wantFDs
<< " to match max-mthreads and threads settings" << endl
,
880 log
->info(Logr::Warning
, "Raised soft limit on number of filedescriptors to match max-mthreads and threads settings", "limit", Logging::Loggable(wantFDs
)));
883 auto newval
= (hardlimit
- 25 - TCPOutConnectionManager::s_maxIdlePerThread
) / RecThreadInfo::numWorkers();
884 SLOG(g_log
<< Logger::Warning
<< "Insufficient number of filedescriptors available for max-mthreads*threads setting! (" << hardlimit
<< " < " << wantFDs
<< "), reducing max-mthreads to " << newval
<< endl
,
885 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
)));
886 g_maxMThreads
= newval
;
887 setFilenumLimit(hardlimit
);
892 // static std::string s_timestampFormat = "%m-%dT%H:%M:%S";
893 static std::string s_timestampFormat
= "%s";
895 static const char* toTimestampStringMilli(const struct timeval
& tval
, std::array
<char, 64>& buf
)
898 if (s_timestampFormat
!= "%s") {
899 // strftime is not thread safe, it can access locale information
900 static std::mutex mutex
;
901 auto lock
= std::lock_guard(mutex
);
902 struct tm theTime
// clang-format insists on formatting it like this
905 len
= strftime(buf
.data(), buf
.size(), s_timestampFormat
.c_str(), localtime_r(&tval
.tv_sec
, &theTime
));
908 len
= snprintf(buf
.data(), buf
.size(), "%lld", static_cast<long long>(tval
.tv_sec
));
911 snprintf(&buf
.at(len
), buf
.size() - len
, ".%03ld", static_cast<long>(tval
.tv_usec
) / 1000);
916 static void loggerSDBackend(const Logging::Entry
& entry
)
918 static const set
<std::string
, CIStringComparePOSIX
> special
= {
927 "user_invocation_id",
939 // First map SL priority to syslog's Urgency
940 Logger::Urgency urgency
= entry
.d_priority
!= 0 ? Logger::Urgency(entry
.d_priority
) : Logger::Info
;
941 if (urgency
> s_logUrgency
) {
942 // We do not log anything if the Urgency of the message is lower than the requested loglevel.
943 // Not that lower Urgency means higher number.
946 // We need to keep the string in mem until sd_journal_sendv has ben called
947 vector
<string
> strings
;
948 auto appendKeyAndVal
= [&strings
](const string
& key
, const string
& value
) {
949 strings
.emplace_back(key
+ "=" + value
);
951 appendKeyAndVal("MESSAGE", entry
.message
);
953 appendKeyAndVal("ERROR", entry
.error
.get());
955 appendKeyAndVal("LEVEL", std::to_string(entry
.level
));
956 appendKeyAndVal("PRIORITY", std::to_string(entry
.d_priority
));
958 appendKeyAndVal("SUBSYSTEM", entry
.name
.get());
960 std::array
<char, 64> timebuf
{};
961 appendKeyAndVal("TIMESTAMP", toTimestampStringMilli(entry
.d_timestamp
, timebuf
));
962 for (const auto& value
: entry
.values
) {
963 if (value
.first
.at(0) == '_' || special
.count(value
.first
) != 0) {
965 key
.append(value
.first
);
966 appendKeyAndVal(toUpper(key
), value
.second
);
969 appendKeyAndVal(toUpper(value
.first
), value
.second
);
972 // Thread id filled in by backend, since the SL code does not know about RecursorThreads
973 // We use the Recursor thread, other threads get id 0. May need to revisit.
974 appendKeyAndVal("TID", std::to_string(RecThreadInfo::id()));
977 iov
.reserve(strings
.size());
978 for (const auto& str
: strings
) {
979 // iovec has no 2 arg constructor, so make it explicit
980 iov
.emplace_back(iovec
{const_cast<void*>(reinterpret_cast<const void*>(str
.data())), str
.size()}); // NOLINT: it's the API
982 sd_journal_sendv(iov
.data(), static_cast<int>(iov
.size()));
986 static void loggerBackend(const Logging::Entry
& entry
)
988 static thread_local
std::stringstream buf
;
990 // First map SL priority to syslog's Urgency
991 Logger::Urgency urg
= entry
.d_priority
!= 0 ? Logger::Urgency(entry
.d_priority
) : Logger::Info
;
992 if (urg
> s_logUrgency
) {
993 // We do not log anything if the Urgency of the message is lower than the requested loglevel.
994 // Not that lower Urgency means higher number.
998 buf
<< "msg=" << std::quoted(entry
.message
);
1000 buf
<< " error=" << std::quoted(entry
.error
.get());
1004 buf
<< " subsystem=" << std::quoted(entry
.name
.get());
1006 buf
<< " level=" << std::quoted(std::to_string(entry
.level
));
1007 if (entry
.d_priority
!= 0) {
1008 buf
<< " prio=" << std::quoted(Logr::Logger::toString(entry
.d_priority
));
1010 // Thread id filled in by backend, since the SL code does not know about RecursorThreads
1011 // We use the Recursor thread, other threads get id 0. May need to revisit.
1012 buf
<< " tid=" << std::quoted(std::to_string(RecThreadInfo::id()));
1013 std::array
<char, 64> timebuf
{};
1014 buf
<< " ts=" << std::quoted(toTimestampStringMilli(entry
.d_timestamp
, timebuf
));
1015 for (auto const& value
: entry
.values
) {
1017 buf
<< value
.first
<< "=" << std::quoted(value
.second
);
1020 g_log
<< urg
<< buf
.str() << endl
;
1023 static int ratePercentage(uint64_t nom
, uint64_t denom
)
1028 return static_cast<int>(round(100.0 * static_cast<double>(nom
) / static_cast<double>(denom
)));
1031 static void doStats()
1033 static time_t lastOutputTime
;
1034 static uint64_t lastQueryCount
;
1036 uint64_t cacheHits
= g_recCache
->cacheHits
;
1037 uint64_t cacheMisses
= g_recCache
->cacheMisses
;
1038 uint64_t cacheSize
= g_recCache
->size();
1039 auto rc_stats
= g_recCache
->stats();
1040 auto pc_stats
= g_packetCache
? g_packetCache
->stats() : std::pair
<uint64_t, uint64_t>{0, 0};
1041 double rrc
= ratePercentage(rc_stats
.first
, rc_stats
.second
);
1042 double rpc
= ratePercentage(pc_stats
.first
, pc_stats
.second
);
1043 uint64_t negCacheSize
= g_negCache
->size();
1044 auto taskPushes
= getTaskPushes();
1045 auto taskExpired
= getTaskExpired();
1046 auto taskSize
= getTaskSize();
1047 uint64_t pcSize
= g_packetCache
? g_packetCache
->size() : 0;
1048 uint64_t pcHits
= g_packetCache
? g_packetCache
->getHits() : 0;
1050 auto log
= g_slog
->withName("stats");
1052 auto qcounter
= g_Counters
.sum(rec::Counter::qcounter
);
1053 auto syncresqueries
= g_Counters
.sum(rec::Counter::syncresqueries
);
1054 auto outqueries
= g_Counters
.sum(rec::Counter::outqueries
);
1055 auto throttledqueries
= g_Counters
.sum(rec::Counter::throttledqueries
);
1056 auto tcpoutqueries
= g_Counters
.sum(rec::Counter::tcpoutqueries
);
1057 auto dotoutqueries
= g_Counters
.sum(rec::Counter::dotoutqueries
);
1058 auto outgoingtimeouts
= g_Counters
.sum(rec::Counter::outgoingtimeouts
);
1059 if (qcounter
> 0 && (cacheHits
+ cacheMisses
) > 0 && syncresqueries
> 0 && outqueries
> 0) {
1060 if (!g_slogStructured
) {
1061 g_log
<< Logger::Notice
<< "stats: " << qcounter
<< " questions, " << cacheSize
<< " cache entries, " << negCacheSize
<< " negative entries, " << ratePercentage(cacheHits
, cacheHits
+ cacheMisses
) << "% cache hits" << endl
;
1062 g_log
<< Logger::Notice
<< "stats: record cache contended/acquired " << rc_stats
.first
<< '/' << rc_stats
.second
<< " = " << rrc
<< '%' << endl
;
1063 g_log
<< Logger::Notice
<< "stats: packet cache contended/acquired " << pc_stats
.first
<< '/' << pc_stats
.second
<< " = " << rpc
<< '%' << endl
;
1065 g_log
<< Logger::Notice
<< "stats: throttle map: "
1066 << SyncRes::getThrottledServersSize() << ", ns speeds: "
1067 << SyncRes::getNSSpeedsSize() << ", failed ns: "
1068 << SyncRes::getFailedServersSize() << ", ednsmap: "
1069 << SyncRes::getEDNSStatusesSize() << ", non-resolving: "
1070 << SyncRes::getNonResolvingNSSize() << ", saved-parentsets: "
1071 << SyncRes::getSaveParentsNSSetsSize()
1073 g_log
<< Logger::Notice
<< "stats: outpacket/query ratio " << ratePercentage(outqueries
, syncresqueries
) << "%";
1074 g_log
<< Logger::Notice
<< ", " << ratePercentage(throttledqueries
, outqueries
+ throttledqueries
) << "% throttled" << endl
;
1075 g_log
<< Logger::Notice
<< "stats: " << tcpoutqueries
<< "/" << dotoutqueries
<< "/" << getCurrentIdleTCPConnections() << " outgoing tcp/dot/idle connections, " << broadcastAccFunction
<uint64_t>(pleaseGetConcurrentQueries
) << " queries running, " << outgoingtimeouts
<< " outgoing timeouts " << endl
;
1077 g_log
<< Logger::Notice
<< "stats: " << pcSize
<< " packet cache entries, " << ratePercentage(pcHits
, qcounter
) << "% packet cache hits" << endl
;
1079 g_log
<< Logger::Notice
<< "stats: tasks pushed/expired/queuesize: " << taskPushes
<< '/' << taskExpired
<< '/' << taskSize
<< endl
;
1082 const string report
= "Periodic statistics report";
1083 log
->info(Logr::Info
, report
,
1084 "questions", Logging::Loggable(qcounter
),
1085 "cache-entries", Logging::Loggable(cacheSize
),
1086 "negcache-entries", Logging::Loggable(negCacheSize
),
1087 "record-cache-hitratio-perc", Logging::Loggable(ratePercentage(cacheHits
, cacheHits
+ cacheMisses
)),
1088 "record-cache-contended", Logging::Loggable(rc_stats
.first
),
1089 "record-cache-acquired", Logging::Loggable(rc_stats
.second
),
1090 "record-cache-contended-perc", Logging::Loggable(rrc
),
1091 "packetcache-contended", Logging::Loggable(pc_stats
.first
),
1092 "packetcache-acquired", Logging::Loggable(pc_stats
.second
),
1093 "packetcache-contended-perc", Logging::Loggable(rpc
));
1094 log
->info(Logr::Info
, report
,
1095 "throttle-entries", Logging::Loggable(SyncRes::getThrottledServersSize()),
1096 "nsspeed-entries", Logging::Loggable(SyncRes::getNSSpeedsSize()),
1097 "failed-host-entries", Logging::Loggable(SyncRes::getFailedServersSize()),
1098 "edns-entries", Logging::Loggable(SyncRes::getEDNSStatusesSize()),
1099 "non-resolving-nameserver-entries", Logging::Loggable(SyncRes::getNonResolvingNSSize()),
1100 "saved-parent-ns-sets-entries", Logging::Loggable(SyncRes::getSaveParentsNSSetsSize()),
1101 "outqueries-per-query", Logging::Loggable(ratePercentage(outqueries
, syncresqueries
)));
1102 log
->info(Logr::Info
, report
,
1103 "throttled-queries-perc", Logging::Loggable(ratePercentage(throttledqueries
, outqueries
+ throttledqueries
)),
1104 "tcp-outqueries", Logging::Loggable(tcpoutqueries
),
1105 "dot-outqueries", Logging::Loggable(dotoutqueries
),
1106 "idle-tcpout-connections", Logging::Loggable(getCurrentIdleTCPConnections()),
1107 "concurrent-queries", Logging::Loggable(broadcastAccFunction
<uint64_t>(pleaseGetConcurrentQueries
)),
1108 "outgoing-timeouts", Logging::Loggable(outgoingtimeouts
));
1109 log
->info(Logr::Info
, report
,
1110 "packetcache-entries", Logging::Loggable(pcSize
),
1111 "packetcache-hitratio-perc", Logging::Loggable(ratePercentage(pcHits
, qcounter
)),
1112 "taskqueue-pushed", Logging::Loggable(taskPushes
),
1113 "taskqueue-expired", Logging::Loggable(taskExpired
),
1114 "taskqueue-size", Logging::Loggable(taskSize
));
1117 for (const auto& threadInfo
: RecThreadInfo::infos()) {
1118 if (threadInfo
.isWorker()) {
1119 SLOG(g_log
<< Logger::Notice
<< "stats: thread " << idx
<< " has been distributed " << threadInfo
.numberOfDistributedQueries
<< " queries" << endl
,
1120 log
->info(Logr::Info
, "Queries handled by thread", "thread", Logging::Loggable(idx
), "count", Logging::Loggable(threadInfo
.numberOfDistributedQueries
)));
1124 time_t now
= time(nullptr);
1125 if (lastOutputTime
!= 0 && lastQueryCount
!= 0 && now
!= lastOutputTime
) {
1126 SLOG(g_log
<< Logger::Notice
<< "stats: " << (qcounter
- lastQueryCount
) / (now
- lastOutputTime
) << " qps (average over " << (now
- lastOutputTime
) << " seconds)" << endl
,
1127 log
->info(Logr::Info
, "Periodic QPS report", "qps", Logging::Loggable((qcounter
- lastQueryCount
) / (now
- lastOutputTime
)),
1128 "averagedOver", Logging::Loggable(now
- lastOutputTime
)));
1130 lastOutputTime
= now
;
1131 lastQueryCount
= qcounter
;
1133 else if (statsWanted
) {
1134 SLOG(g_log
<< Logger::Notice
<< "stats: no stats yet!" << endl
,
1135 log
->info(Logr::Notice
, "No stats yet"));
1138 statsWanted
= false;
1141 static std::shared_ptr
<NetmaskGroup
> parseACL(const std::string
& aclFile
, const std::string
& aclSetting
, Logr::log_t log
)
1143 auto result
= std::make_shared
<NetmaskGroup
>();
1145 if (!::arg()[aclFile
].empty()) {
1147 ifstream
ifs(::arg()[aclFile
].c_str());
1149 throw runtime_error("Could not open '" + ::arg()[aclFile
] + "': " + stringerror());
1152 while (getline(ifs
, line
)) {
1153 auto pos
= line
.find('#');
1154 if (pos
!= string::npos
) {
1162 result
->addMask(line
);
1164 SLOG(g_log
<< Logger::Info
<< "Done parsing " << result
->size() << " " << aclSetting
<< " ranges from file '" << ::arg()[aclFile
] << "' - overriding '" << aclSetting
<< "' setting" << endl
,
1165 log
->info(Logr::Info
, "Done parsing ranges from file, will override setting", "setting", Logging::Loggable(aclSetting
),
1166 "number", Logging::Loggable(result
->size()), "file", Logging::Loggable(::arg()[aclFile
])));
1168 else if (!::arg()[aclSetting
].empty()) {
1170 stringtok(ips
, ::arg()[aclSetting
], ", ");
1172 for (const auto& address
: ips
) {
1173 result
->addMask(address
);
1175 if (!g_slogStructured
) {
1176 g_log
<< Logger::Info
<< aclSetting
<< ": ";
1177 for (auto i
= ips
.begin(); i
!= ips
.end(); ++i
) {
1178 if (i
!= ips
.begin()) {
1179 g_log
<< Logger::Info
<< ", ";
1181 g_log
<< Logger::Info
<< *i
;
1183 g_log
<< Logger::Info
<< endl
;
1186 log
->info(Logr::Info
, "Setting access control", "acl", Logging::Loggable(aclSetting
), "addresses", Logging::IterLoggable(ips
.begin(), ips
.end()));
1193 static void* pleaseSupplantAllowFrom(std::shared_ptr
<NetmaskGroup
> nmgroup
)
1195 t_allowFrom
= std::move(nmgroup
);
1199 static void* pleaseSupplantAllowNotifyFrom(std::shared_ptr
<NetmaskGroup
> nmgroup
)
1201 t_allowNotifyFrom
= std::move(nmgroup
);
1205 void* pleaseSupplantAllowNotifyFor(std::shared_ptr
<notifyset_t
> allowNotifyFor
)
1207 t_allowNotifyFor
= std::move(allowNotifyFor
);
1213 auto log
= g_slog
->withName("config");
1215 static bool l_initialized
;
1217 if (l_initialized
) { // only reload configuration file on second call
1218 string configName
= ::arg()["config-dir"] + "/recursor.conf";
1219 if (!::arg()["config-name"].empty()) {
1220 configName
= ::arg()["config-dir"] + "/recursor-" + ::arg()["config-name"] + ".conf";
1222 cleanSlashes(configName
);
1224 if (!::arg().preParseFile(configName
.c_str(), "allow-from-file")) {
1225 throw runtime_error("Unable to re-parse configuration file '" + configName
+ "'");
1227 ::arg().preParseFile(configName
.c_str(), "allow-from", LOCAL_NETS
);
1229 if (!::arg().preParseFile(configName
.c_str(), "allow-notify-from-file")) {
1230 throw runtime_error("Unable to re-parse configuration file '" + configName
+ "'");
1232 ::arg().preParseFile(configName
.c_str(), "allow-notify-from");
1234 ::arg().preParseFile(configName
.c_str(), "include-dir");
1235 ::arg().preParse(g_argc
, g_argv
, "include-dir");
1237 // then process includes
1238 std::vector
<std::string
> extraConfigs
;
1239 ::arg().gatherIncludes(extraConfigs
);
1241 for (const std::string
& fileName
: extraConfigs
) {
1242 if (!::arg().preParseFile(fileName
.c_str(), "allow-from-file", ::arg()["allow-from-file"])) {
1243 throw runtime_error("Unable to re-parse configuration file include '" + fileName
+ "'");
1245 if (!::arg().preParseFile(fileName
.c_str(), "allow-from", ::arg()["allow-from"])) {
1246 throw runtime_error("Unable to re-parse configuration file include '" + fileName
+ "'");
1249 if (!::arg().preParseFile(fileName
.c_str(), "allow-notify-from-file", ::arg()["allow-notify-from-file"])) {
1250 throw runtime_error("Unable to re-parse configuration file include '" + fileName
+ "'");
1252 if (!::arg().preParseFile(fileName
.c_str(), "allow-notify-from", ::arg()["allow-notify-from"])) {
1253 throw runtime_error("Unable to re-parse configuration file include '" + fileName
+ "'");
1257 ::arg().preParse(g_argc
, g_argv
, "allow-from-file");
1258 ::arg().preParse(g_argc
, g_argv
, "allow-from");
1260 ::arg().preParse(g_argc
, g_argv
, "allow-notify-from-file");
1261 ::arg().preParse(g_argc
, g_argv
, "allow-notify-from");
1264 auto allowFrom
= parseACL("allow-from-file", "allow-from", log
);
1266 if (allowFrom
->empty()) {
1267 if (::arg()["local-address"] != "127.0.0.1" && ::arg().asNum("local-port") == 53) {
1268 SLOG(g_log
<< Logger::Warning
<< "WARNING: Allowing queries from all IP addresses - this can be a security risk!" << endl
,
1269 log
->info(Logr::Warning
, "WARNING: Allowing queries from all IP addresses - this can be a security risk!"));
1271 allowFrom
= nullptr;
1274 g_initialAllowFrom
= allowFrom
;
1275 broadcastFunction([=] { return pleaseSupplantAllowFrom(allowFrom
); });
1277 auto allowNotifyFrom
= parseACL("allow-notify-from-file", "allow-notify-from", log
);
1279 g_initialAllowNotifyFrom
= allowNotifyFrom
;
1280 broadcastFunction([=] { return pleaseSupplantAllowNotifyFrom(allowNotifyFrom
); });
1282 l_initialized
= true;
1285 void broadcastFunction(const pipefunc_t
& func
)
1287 /* This function might be called by the worker with t_id 0 during startup
1288 for the initialization of ACLs and domain maps. After that it should only
1289 be called by the handler. */
1291 if (RecThreadInfo::infos().empty() && RecThreadInfo::id() == 0) {
1292 /* the handler and distributors will call themselves below, but
1293 during startup we get called while g_threadInfos has not been
1294 populated yet to update the ACL or domain maps, so we need to
1300 unsigned int thread
= 0;
1301 for (const auto& threadInfo
: RecThreadInfo::infos()) {
1302 if (thread
++ == RecThreadInfo::id()) {
1303 func(); // don't write to ourselves!
1307 ThreadMSG
* tmsg
= new ThreadMSG(); // NOLINT: manual ownership handling
1309 tmsg
->wantAnswer
= true;
1310 if (write(threadInfo
.pipes
.writeToThread
, &tmsg
, sizeof(tmsg
)) != sizeof(tmsg
)) { // NOLINT: sizeof correct
1311 delete tmsg
; // NOLINT: manual ownership handling
1313 unixDie("write to thread pipe returned wrong size or error");
1316 string
* resp
= nullptr;
1317 if (read(threadInfo
.pipes
.readFromThread
, &resp
, sizeof(resp
)) != sizeof(resp
)) { // NOLINT: sizeof correct
1318 unixDie("read from thread pipe returned wrong size or error");
1321 if (resp
!= nullptr) {
1322 delete resp
; // NOLINT: manual ownership handling
1325 // coverity[leaked_storage]
1330 void* voider(const std::function
<T
*()>& func
)
1335 static vector
<ComboAddress
>& operator+=(vector
<ComboAddress
>& lhs
, const vector
<ComboAddress
>& rhs
)
1337 lhs
.insert(lhs
.end(), rhs
.begin(), rhs
.end());
1341 static vector
<pair
<DNSName
, uint16_t>>& operator+=(vector
<pair
<DNSName
, uint16_t>>& lhs
, const vector
<pair
<DNSName
, uint16_t>>& rhs
)
1343 lhs
.insert(lhs
.end(), rhs
.begin(), rhs
.end());
1347 static ProxyMappingStats_t
& operator+=(ProxyMappingStats_t
& lhs
, const ProxyMappingStats_t
& rhs
)
1349 for (const auto& [key
, entry
] : rhs
) {
1350 lhs
[key
].netmaskMatches
+= entry
.netmaskMatches
;
1351 lhs
[key
].suffixMatches
+= entry
.suffixMatches
;
1356 static RemoteLoggerStats_t
& operator+=(RemoteLoggerStats_t
& lhs
, const RemoteLoggerStats_t
& rhs
)
1358 for (const auto& [key
, entry
] : rhs
) {
1364 // This function should only be called by the handler to gather
1365 // metrics, wipe the cache, reload the Lua script (not the Lua config)
1366 // or change the current trace regex, and by the SNMP thread to gather
1368 // Note that this currently skips the handler, but includes the taskThread(s).
1370 T
broadcastAccFunction(const std::function
<T
*()>& func
)
1372 if (!RecThreadInfo::self().isHandler()) {
1373 SLOG(g_log
<< Logger::Error
<< "broadcastAccFunction has been called by a worker (" << RecThreadInfo::id() << ")" << endl
,
1374 g_slog
->withName("runtime")->info(Logr::Critical
, "broadcastAccFunction has been called by a worker")); // tid will be added
1378 unsigned int thread
= 0;
1380 for (const auto& threadInfo
: RecThreadInfo::infos()) {
1381 if (thread
++ == RecThreadInfo::id()) {
1385 const auto& tps
= threadInfo
.pipes
;
1386 ThreadMSG
* tmsg
= new ThreadMSG(); // NOLINT: manual ownership handling
1387 tmsg
->func
= [func
] { return voider
<T
>(func
); };
1388 tmsg
->wantAnswer
= true;
1390 if (write(tps
.writeToThread
, &tmsg
, sizeof(tmsg
)) != sizeof(tmsg
)) { // NOLINT:: sizeof correct
1391 delete tmsg
; // NOLINT: manual ownership handling
1392 unixDie("write to thread pipe returned wrong size or error");
1396 if (read(tps
.readFromThread
, &resp
, sizeof(resp
)) != sizeof(resp
)) // NOLINT: sizeof correct
1397 unixDie("read from thread pipe returned wrong size or error");
1401 delete resp
; // NOLINT: manual ownership handling
1404 // coverity[leaked_storage]
1409 template string
broadcastAccFunction(const std::function
<string
*()>& fun
); // explicit instantiation
1410 template RecursorControlChannel::Answer
broadcastAccFunction(const std::function
<RecursorControlChannel::Answer
*()>& fun
); // explicit instantiation
1411 template uint64_t broadcastAccFunction(const std::function
<uint64_t*()>& fun
); // explicit instantiation
1412 template vector
<ComboAddress
> broadcastAccFunction(const std::function
<vector
<ComboAddress
>*()>& fun
); // explicit instantiation
1413 template vector
<pair
<DNSName
, uint16_t>> broadcastAccFunction(const std::function
<vector
<pair
<DNSName
, uint16_t>>*()>& fun
); // explicit instantiation
1414 template ThreadTimes
broadcastAccFunction(const std::function
<ThreadTimes
*()>& fun
);
1415 template ProxyMappingStats_t
broadcastAccFunction(const std::function
<ProxyMappingStats_t
*()>& fun
);
1416 template RemoteLoggerStats_t
broadcastAccFunction(const std::function
<RemoteLoggerStats_t
*()>& fun
);
1418 static int initNet(Logr::log_t log
)
1420 checkLinuxIPv6Limits(log
);
1422 pdns::parseQueryLocalAddress(::arg()["query-local-address"]);
1424 catch (std::exception
& e
) {
1425 SLOG(g_log
<< Logger::Error
<< "Assigning local query addresses: " << e
.what(),
1426 log
->error(Logr::Error
, e
.what(), "Unable to assign local query address"));
1430 if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET
)) {
1431 SyncRes::s_doIPv4
= true;
1432 SLOG(g_log
<< Logger::Warning
<< "Enabling IPv4 transport for outgoing queries" << endl
,
1433 log
->info(Logr::Notice
, "Enabling IPv4 transport for outgoing queries"));
1436 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
,
1437 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"));
1440 if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET6
)) {
1441 SyncRes::s_doIPv6
= true;
1442 SLOG(g_log
<< Logger::Warning
<< "Enabling IPv6 transport for outgoing queries" << endl
,
1443 log
->info(Logr::Notice
, "Enabling IPv6 transport for outgoing queries"));
1446 SLOG(g_log
<< Logger::Warning
<< "NOT using IPv6 for outgoing queries - add an IPv6 address (like '::') to query-local-address to enable" << endl
,
1447 log
->info(Logr::Warning
, "NOT using IPv6 for outgoing queries - add an IPv6 address (like '::') to query-local-address to enable"));
1450 if (!SyncRes::s_doIPv6
&& !SyncRes::s_doIPv4
) {
1451 SLOG(g_log
<< Logger::Error
<< "No outgoing addresses configured! Can not continue" << endl
,
1452 log
->info(Logr::Error
, "No outgoing addresses configured! Can not continue"));
1458 static int initDNSSEC(Logr::log_t log
)
1460 if (::arg()["dnssec"] == "off") {
1461 g_dnssecmode
= DNSSECMode::Off
;
1463 else if (::arg()["dnssec"] == "process-no-validate") {
1464 g_dnssecmode
= DNSSECMode::ProcessNoValidate
;
1466 else if (::arg()["dnssec"] == "process") {
1467 g_dnssecmode
= DNSSECMode::Process
;
1469 else if (::arg()["dnssec"] == "validate") {
1470 g_dnssecmode
= DNSSECMode::ValidateAll
;
1472 else if (::arg()["dnssec"] == "log-fail") {
1473 g_dnssecmode
= DNSSECMode::ValidateForLog
;
1476 SLOG(g_log
<< Logger::Error
<< "Unknown DNSSEC mode " << ::arg()["dnssec"] << endl
,
1477 log
->info(Logr::Error
, "Unknown DNSSEC mode", "dnssec", Logging::Loggable(::arg()["dnssec"])));
1481 g_signatureInceptionSkew
= ::arg().asNum("signature-inception-skew");
1482 if (g_signatureInceptionSkew
< 0) {
1483 SLOG(g_log
<< Logger::Error
<< "A negative value for 'signature-inception-skew' is not allowed" << endl
,
1484 log
->info(Logr::Error
, "A negative value for 'signature-inception-skew' is not allowed"));
1488 g_dnssecLogBogus
= ::arg().mustDo("dnssec-log-bogus");
1489 g_maxNSEC3Iterations
= ::arg().asNum("nsec3-max-iterations");
1491 vector
<string
> nums
;
1492 bool automatic
= true;
1493 if (!::arg()["dnssec-disabled-algorithms"].empty()) {
1495 stringtok(nums
, ::arg()["dnssec-disabled-algorithms"], ", ");
1496 for (const auto& num
: nums
) {
1497 DNSCryptoKeyEngine::switchOffAlgorithm(pdns::checked_stoi
<unsigned int>(num
));
1501 for (auto algo
: {DNSSECKeeper::RSASHA1
, DNSSECKeeper::RSASHA1NSEC3SHA1
}) {
1502 if (!DNSCryptoKeyEngine::verifyOne(algo
)) {
1503 DNSCryptoKeyEngine::switchOffAlgorithm(algo
);
1504 nums
.push_back(std::to_string(algo
));
1508 if (!nums
.empty()) {
1509 if (!g_slogStructured
) {
1510 g_log
<< Logger::Warning
<< (automatic
? "Automatically" : "Manually") << " disabled DNSSEC algorithms: ";
1511 for (auto i
= nums
.begin(); i
!= nums
.end(); ++i
) {
1512 if (i
!= nums
.begin()) {
1513 g_log
<< Logger::Warning
<< ", ";
1515 g_log
<< Logger::Warning
<< *i
;
1517 g_log
<< Logger::Warning
<< endl
;
1520 log
->info(Logr::Notice
, "Disabled DNSSEC algorithms", "automatically", Logging::Loggable(automatic
), "algorithms", Logging::IterLoggable(nums
.begin(), nums
.end()));
1527 static void initDontQuery(Logr::log_t log
)
1529 if (!::arg()["dont-query"].empty()) {
1531 stringtok(ips
, ::arg()["dont-query"], ", ");
1532 ips
.emplace_back("0.0.0.0");
1533 ips
.emplace_back("::");
1535 for (const auto& anIP
: ips
) {
1536 SyncRes::addDontQuery(anIP
);
1538 if (!g_slogStructured
) {
1539 g_log
<< Logger::Warning
<< "Will not send queries to: ";
1540 for (auto i
= ips
.begin(); i
!= ips
.end(); ++i
) {
1541 if (i
!= ips
.begin()) {
1542 g_log
<< Logger::Warning
<< ", ";
1544 g_log
<< Logger::Warning
<< *i
;
1546 g_log
<< Logger::Warning
<< endl
;
1549 log
->info(Logr::Notice
, "Will not send queries to", "addresses", Logging::IterLoggable(ips
.begin(), ips
.end()));
1554 static int initSyncRes(Logr::log_t log
, const std::optional
<std::string
>& myHostname
)
1556 SyncRes::s_minimumTTL
= ::arg().asNum("minimum-ttl-override");
1557 SyncRes::s_minimumECSTTL
= ::arg().asNum("ecs-minimum-ttl-override");
1558 SyncRes::s_maxnegttl
= ::arg().asNum("max-negative-ttl");
1559 SyncRes::s_maxbogusttl
= ::arg().asNum("max-cache-bogus-ttl");
1560 SyncRes::s_maxcachettl
= max(::arg().asNum("max-cache-ttl"), 15);
1562 SyncRes::s_packetcachettl
= ::arg().asNum("packetcache-ttl");
1563 // Cap the packetcache-servfail-ttl and packetcache-negative-ttl to packetcache-ttl
1564 SyncRes::s_packetcacheservfailttl
= std::min(static_cast<unsigned int>(::arg().asNum("packetcache-servfail-ttl")), SyncRes::s_packetcachettl
);
1565 SyncRes::s_packetcachenegativettl
= std::min(static_cast<unsigned int>(::arg().asNum("packetcache-negative-ttl")), SyncRes::s_packetcachettl
);
1567 SyncRes::s_serverdownmaxfails
= ::arg().asNum("server-down-max-fails");
1568 SyncRes::s_serverdownthrottletime
= ::arg().asNum("server-down-throttle-time");
1569 SyncRes::s_nonresolvingnsmaxfails
= ::arg().asNum("non-resolving-ns-max-fails");
1570 SyncRes::s_nonresolvingnsthrottletime
= ::arg().asNum("non-resolving-ns-throttle-time");
1571 SyncRes::s_serverID
= ::arg()["server-id"];
1572 SyncRes::s_maxqperq
= ::arg().asNum("max-qperq");
1573 SyncRes::s_maxnsperresolve
= ::arg().asNum("max-ns-per-resolve");
1574 SyncRes::s_maxnsaddressqperq
= ::arg().asNum("max-ns-address-qperq");
1575 SyncRes::s_maxtotusec
= 1000 * ::arg().asNum("max-total-msec");
1576 SyncRes::s_maxdepth
= ::arg().asNum("max-recursion-depth");
1577 SyncRes::s_rootNXTrust
= ::arg().mustDo("root-nx-trust");
1578 SyncRes::s_refresh_ttlperc
= ::arg().asNum("refresh-on-ttl-perc");
1579 SyncRes::s_locked_ttlperc
= ::arg().asNum("record-cache-locked-ttl-perc");
1580 RecursorPacketCache::s_refresh_ttlperc
= SyncRes::s_refresh_ttlperc
;
1581 SyncRes::s_tcp_fast_open
= ::arg().asNum("tcp-fast-open");
1582 SyncRes::s_tcp_fast_open_connect
= ::arg().mustDo("tcp-fast-open-connect");
1584 SyncRes::s_dot_to_port_853
= ::arg().mustDo("dot-to-port-853");
1585 SyncRes::s_event_trace_enabled
= ::arg().asNum("event-trace-enabled");
1586 SyncRes::s_save_parent_ns_set
= ::arg().mustDo("save-parent-ns-set");
1587 SyncRes::s_max_busy_dot_probes
= ::arg().asNum("max-busy-dot-probes");
1589 uint64_t sse
= ::arg().asNum("serve-stale-extensions");
1590 if (sse
> std::numeric_limits
<uint16_t>::max()) {
1591 SLOG(g_log
<< Logger::Error
<< "Illegal serve-stale-extensions value: " << sse
<< "; range = 0..65536" << endl
,
1592 log
->info(Logr::Error
, "Illegal serve-stale-extensions value; range = 0..65536", "value", Logging::Loggable(sse
)));
1595 MemRecursorCache::s_maxServedStaleExtensions
= sse
;
1596 NegCache::s_maxServedStaleExtensions
= sse
;
1599 if (SyncRes::s_tcp_fast_open_connect
) {
1600 checkFastOpenSysctl(true, log
);
1601 checkTFOconnect(log
);
1604 if (SyncRes::s_serverID
.empty()) {
1605 SyncRes::s_serverID
= myHostname
.has_value() ? *myHostname
: "";
1608 SyncRes::s_ecsipv4limit
= ::arg().asNum("ecs-ipv4-bits");
1609 SyncRes::s_ecsipv6limit
= ::arg().asNum("ecs-ipv6-bits");
1610 SyncRes::clearECSStats();
1611 SyncRes::s_ecsipv4cachelimit
= ::arg().asNum("ecs-ipv4-cache-bits");
1612 SyncRes::s_ecsipv6cachelimit
= ::arg().asNum("ecs-ipv6-cache-bits");
1613 SyncRes::s_ecsipv4nevercache
= ::arg().mustDo("ecs-ipv4-never-cache");
1614 SyncRes::s_ecsipv6nevercache
= ::arg().mustDo("ecs-ipv6-never-cache");
1615 SyncRes::s_ecscachelimitttl
= ::arg().asNum("ecs-cache-limit-ttl");
1617 SyncRes::s_qnameminimization
= ::arg().mustDo("qname-minimization");
1619 if (SyncRes::s_qnameminimization
) {
1620 // With an empty cache, a rev ipv6 query with dnssec enabled takes
1621 // almost 100 queries. Default maxqperq is 60.
1622 SyncRes::s_maxqperq
= std::max(SyncRes::s_maxqperq
, static_cast<unsigned int>(100));
1625 SyncRes::s_hardenNXD
= SyncRes::HardenNXD::DNSSEC
;
1626 string value
= ::arg()["nothing-below-nxdomain"];
1627 if (value
== "yes") {
1628 SyncRes::s_hardenNXD
= SyncRes::HardenNXD::Yes
;
1630 else if (value
== "no") {
1631 SyncRes::s_hardenNXD
= SyncRes::HardenNXD::No
;
1633 else if (value
!= "dnssec") {
1634 SLOG(g_log
<< Logger::Error
<< "Unknown nothing-below-nxdomain mode: " << value
<< endl
,
1635 log
->info(Logr::Error
, "Unknown nothing-below-nxdomain mode", "mode", Logging::Loggable(value
)));
1639 if (!::arg().isEmpty("ecs-scope-zero-address")) {
1640 ComboAddress
scopeZero(::arg()["ecs-scope-zero-address"]);
1641 SyncRes::setECSScopeZeroAddress(Netmask(scopeZero
, scopeZero
.isIPv4() ? 32 : 128));
1647 auto addr
= pdns::getNonAnyQueryLocalAddress(AF_INET
);
1648 if (addr
.sin4
.sin_family
!= 0) {
1649 netmask
= Netmask(addr
, 32);
1653 addr
= pdns::getNonAnyQueryLocalAddress(AF_INET6
);
1654 if (addr
.sin4
.sin_family
!= 0) {
1655 netmask
= Netmask(addr
, 128);
1660 netmask
= Netmask(ComboAddress("127.0.0.1"), 32);
1662 SyncRes::setECSScopeZeroAddress(netmask
);
1665 SyncRes::parseEDNSSubnetAllowlist(::arg()["edns-subnet-whitelist"]);
1666 SyncRes::parseEDNSSubnetAllowlist(::arg()["edns-subnet-allow-list"]);
1667 SyncRes::parseEDNSSubnetAddFor(::arg()["ecs-add-for"]);
1668 g_useIncomingECS
= ::arg().mustDo("use-incoming-edns-subnet");
1672 static void initDistribution(Logr::log_t log
)
1674 g_balancingFactor
= ::arg().asDouble("distribution-load-factor");
1675 if (g_balancingFactor
!= 0.0 && g_balancingFactor
< 1.0) {
1676 g_balancingFactor
= 0.0;
1677 SLOG(g_log
<< Logger::Warning
<< "Asked to run with a distribution-load-factor below 1.0, disabling it instead" << endl
,
1678 log
->info(Logr::Warning
, "Asked to run with a distribution-load-factor below 1.0, disabling it instead"));
1682 g_reusePort
= ::arg().mustDo("reuseport");
1685 RecThreadInfo::infos().resize(RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + RecThreadInfo::numWorkers() + RecThreadInfo::numTaskThreads());
1688 if (RecThreadInfo::weDistributeQueries()) {
1689 /* first thread is the handler, then distributors */
1690 for (unsigned int threadId
= 1; threadId
<= RecThreadInfo::numDistributors(); threadId
++) {
1691 auto& info
= RecThreadInfo::info(threadId
);
1692 auto& deferredAdds
= info
.deferredAdds
;
1693 auto& tcpSockets
= info
.tcpSockets
;
1694 makeUDPServerSockets(deferredAdds
, log
);
1695 makeTCPServerSockets(deferredAdds
, tcpSockets
, log
);
1699 /* first thread is the handler, there is no distributor here and workers are accepting queries */
1700 for (unsigned int threadId
= 1; threadId
<= RecThreadInfo::numWorkers(); threadId
++) {
1701 auto& info
= RecThreadInfo::info(threadId
);
1702 auto& deferredAdds
= info
.deferredAdds
;
1703 auto& tcpSockets
= info
.tcpSockets
;
1704 makeUDPServerSockets(deferredAdds
, log
);
1705 makeTCPServerSockets(deferredAdds
, tcpSockets
, log
);
1710 std::set
<int> tcpSockets
;
1711 /* we don't have reuseport so we can only open one socket per
1712 listening addr:port and everyone will listen on it */
1713 makeUDPServerSockets(g_deferredAdds
, log
);
1714 makeTCPServerSockets(g_deferredAdds
, tcpSockets
, log
);
1716 /* every listener (so distributor if g_weDistributeQueries, workers otherwise)
1717 needs to listen to the shared sockets */
1718 if (RecThreadInfo::weDistributeQueries()) {
1719 /* first thread is the handler, then distributors */
1720 for (unsigned int threadId
= 1; threadId
<= RecThreadInfo::numDistributors(); threadId
++) {
1721 RecThreadInfo::info(threadId
).tcpSockets
= tcpSockets
;
1725 /* first thread is the handler, there is no distributor here and workers are accepting queries */
1726 for (unsigned int threadId
= 1; threadId
<= RecThreadInfo::numWorkers(); threadId
++) {
1727 RecThreadInfo::info(threadId
).tcpSockets
= tcpSockets
;
1733 static int initForks(Logr::log_t log
)
1736 for (; forks
< ::arg().asNum("processes") - 1; ++forks
) {
1737 if (fork() == 0) { // we are child
1742 if (::arg().mustDo("daemon")) {
1743 SLOG(g_log
<< Logger::Warning
<< "Calling daemonize, going to background" << endl
,
1744 log
->info(Logr::Warning
, "Calling daemonize, going to background"));
1745 g_log
.toConsole(Logger::Critical
);
1749 if (Utility::getpid() == 1) {
1750 /* We are running as pid 1, register sigterm and sigint handler
1752 The Linux kernel will handle SIGTERM and SIGINT for all processes, except PID 1.
1753 It assumes that the processes running as pid 1 is an "init" like system.
1754 For years, this was a safe assumption, but containers change that: in
1755 most (all?) container implementations, the application itself is running
1756 as pid 1. This means that sending signals to those applications, will not
1757 be handled by default. Results might be "your container not responding
1758 when asking it to stop", or "ctrl-c not working even when the app is
1759 running in the foreground inside a container".
1761 So TL;DR: If we're running pid 1 (container), we should handle SIGTERM and SIGINT ourselves */
1763 signal(SIGTERM
, termIntHandler
);
1764 signal(SIGINT
, termIntHandler
);
1767 signal(SIGUSR1
, usr1Handler
);
1768 signal(SIGUSR2
, usr2Handler
);
1769 signal(SIGPIPE
, SIG_IGN
); // NOLINT: Posix API
1773 static int initPorts(Logr::log_t log
)
1775 int port
= ::arg().asNum("udp-source-port-min");
1776 if (port
< 1024 || port
> 65535) {
1777 SLOG(g_log
<< Logger::Error
<< "Unable to launch, udp-source-port-min is not a valid port number" << endl
,
1778 log
->info(Logr::Error
, "Unable to launch, udp-source-port-min is not a valid port number"));
1779 return 99; // this isn't going to fix itself either
1781 g_minUdpSourcePort
= port
;
1782 port
= ::arg().asNum("udp-source-port-max");
1783 if (port
< 1024 || port
> 65535 || port
< g_minUdpSourcePort
) {
1784 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
,
1785 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"));
1786 return 99; // this isn't going to fix itself either
1788 g_maxUdpSourcePort
= port
;
1789 std::vector
<string
> parts
{};
1790 stringtok(parts
, ::arg()["udp-source-port-avoid"], ", ");
1791 for (const auto& part
: parts
) {
1792 port
= std::stoi(part
);
1793 if (port
< 1024 || port
> 65535) {
1794 SLOG(g_log
<< Logger::Error
<< "Unable to launch, udp-source-port-avoid contains an invalid port number: " << part
<< endl
,
1795 log
->info(Logr::Error
, "Unable to launch, udp-source-port-avoid contains an invalid port number", "port", Logging::Loggable(part
)));
1796 return 99; // this isn't going to fix itself either
1798 g_avoidUdpSourcePorts
.insert(port
);
1803 static void initSNMP([[maybe_unused
]] Logr::log_t log
)
1805 if (::arg().mustDo("snmp-agent")) {
1806 #ifdef HAVE_NET_SNMP
1807 string setting
= ::arg()["snmp-daemon-socket"];
1808 if (setting
.empty()) {
1809 setting
= ::arg()["snmp-master-socket"];
1811 g_snmpAgent
= std::make_shared
<RecursorSNMPAgent
>("recursor", setting
);
1814 const std::string msg
= "snmp-agent set but SNMP support not compiled in";
1815 SLOG(g_log
<< Logger::Error
<< msg
<< endl
,
1816 log
->info(Logr::Error
, msg
));
1817 #endif // HAVE_NET_SNMP
1821 static int initControl(Logr::log_t log
, uid_t newuid
, int forks
)
1823 if (!::arg()["chroot"].empty()) {
1826 ns
= getenv("NOTIFY_SOCKET");
1827 if (ns
!= nullptr) {
1828 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
,
1829 log
->info(Logr::Error
, "Unable to chroot when running from systemd. Please disable chroot= or set the 'Type' for this service to 'simple'"));
1833 if (chroot(::arg()["chroot"].c_str()) < 0 || chdir("/") < 0) {
1835 SLOG(g_log
<< Logger::Error
<< "Unable to chroot to '" + ::arg()["chroot"] + "': " << stringerror(err
) << ", exiting" << endl
,
1836 log
->error(Logr::Error
, err
, "Unable to chroot", "chroot", Logging::Loggable(::arg()["chroot"])));
1839 SLOG(g_log
<< Logger::Info
<< "Chrooted to '" << ::arg()["chroot"] << "'" << endl
,
1840 log
->info(Logr::Info
, "Chrooted", "chroot", Logging::Loggable(::arg()["chroot"])));
1843 checkSocketDir(log
);
1845 g_pidfname
= ::arg()["socket-dir"] + "/" + g_programname
+ ".pid";
1846 if (!g_pidfname
.empty()) {
1847 unlink(g_pidfname
.c_str()); // remove possible old pid file
1851 makeControlChannelSocket(::arg().asNum("processes") > 1 ? forks
: -1);
1853 Utility::dropUserPrivs(newuid
);
1855 /* we might still have capabilities remaining, for example if we have been started as root
1856 without --setuid (please don't do that) or as an unprivileged user with ambient capabilities
1857 like CAP_NET_BIND_SERVICE.
1861 catch (const std::exception
& e
) {
1862 SLOG(g_log
<< Logger::Warning
<< e
.what() << endl
,
1863 log
->error(Logr::Warning
, e
.what(), "Could not drop capabilities"));
1868 static void initSuffixMatchNodes([[maybe_unused
]] Logr::log_t log
)
1871 SuffixMatchNode dontThrottleNames
;
1872 vector
<string
> parts
;
1873 stringtok(parts
, ::arg()["dont-throttle-names"], " ,");
1874 for (const auto& part
: parts
) {
1875 dontThrottleNames
.add(DNSName(part
));
1877 g_dontThrottleNames
.setState(std::move(dontThrottleNames
));
1880 NetmaskGroup dontThrottleNetmasks
;
1881 stringtok(parts
, ::arg()["dont-throttle-netmasks"], " ,");
1882 for (const auto& part
: parts
) {
1883 dontThrottleNetmasks
.addMask(Netmask(part
));
1885 g_dontThrottleNetmasks
.setState(std::move(dontThrottleNetmasks
));
1889 SuffixMatchNode xdnssecNames
;
1890 vector
<string
> parts
;
1891 stringtok(parts
, ::arg()["x-dnssec-names"], " ,");
1892 for (const auto& part
: parts
) {
1893 xdnssecNames
.add(DNSName(part
));
1895 g_xdnssec
.setState(std::move(xdnssecNames
));
1899 SuffixMatchNode dotauthNames
;
1900 vector
<string
> parts
;
1901 stringtok(parts
, ::arg()["dot-to-auth-names"], " ,");
1902 #ifndef HAVE_DNS_OVER_TLS
1904 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
,
1905 log
->info(Logr::Error
, "dot-to-auth-names setting contains names, but Recursor was built without DNS over TLS support. Setting will be ignored"));
1908 for (const auto& part
: parts
) {
1909 dotauthNames
.add(DNSName(part
));
1911 g_DoTToAuthNames
.setState(std::move(dotauthNames
));
1915 static void initCarbon()
1917 CarbonConfig config
;
1918 stringtok(config
.servers
, arg()["carbon-server"], ", ");
1919 config
.hostname
= arg()["carbon-ourname"];
1920 config
.instance_name
= arg()["carbon-instance"];
1921 config
.namespace_name
= arg()["carbon-namespace"];
1922 g_carbonConfig
.setState(std::move(config
));
1925 static int initDNS64(Logr::log_t log
)
1927 if (!::arg()["dns64-prefix"].empty()) {
1929 auto dns64Prefix
= Netmask(::arg()["dns64-prefix"]);
1930 if (dns64Prefix
.getBits() != 96) {
1931 SLOG(g_log
<< Logger::Error
<< "Invalid prefix for 'dns64-prefix', the current implementation only supports /96 prefixes: " << ::arg()["dns64-prefix"] << endl
,
1932 log
->info(Logr::Error
, "Invalid prefix for 'dns64-prefix', the current implementation only supports /96 prefixes", "prefix", Logging::Loggable(::arg()["dns64-prefix"])));
1935 g_dns64Prefix
= dns64Prefix
.getNetwork();
1936 g_dns64PrefixReverse
= reverseNameFromIP(*g_dns64Prefix
);
1937 /* /96 is 24 nibbles + 2 for "ip6.arpa." */
1938 while (g_dns64PrefixReverse
.countLabels() > 26) {
1939 g_dns64PrefixReverse
.chopOff();
1942 catch (const NetmaskException
& ne
) {
1943 SLOG(g_log
<< Logger::Error
<< "Invalid prefix '" << ::arg()["dns64-prefix"] << "' for 'dns64-prefix': " << ne
.reason
<< endl
,
1944 log
->info(Logr::Error
, "Invalid prefix", "dns64-prefix", Logging::Loggable(::arg()["dns64-prefix"])));
1951 static int serviceMain(Logr::log_t log
)
1953 g_log
.setName(g_programname
);
1954 g_log
.disableSyslog(::arg().mustDo("disable-syslog"));
1955 g_log
.setTimestamps(::arg().mustDo("log-timestamp"));
1956 g_regressionTestMode
= ::arg().mustDo("devonly-regression-test-mode");
1958 if (!::arg()["logging-facility"].empty()) {
1959 int val
= logFacilityToLOG(::arg().asNum("logging-facility"));
1961 g_log
.setFacility(val
);
1964 SLOG(g_log
<< Logger::Error
<< "Unknown logging facility " << ::arg().asNum("logging-facility") << endl
,
1965 log
->info(Logr::Error
, "Unknown logging facility", "facility", Logging::Loggable(::arg().asNum("logging-facility"))));
1969 showProductVersion();
1971 g_disthashseed
= dns_random_uint32();
1973 int ret
= initNet(log
);
1977 // keep this ABOVE loadRecursorLuaConfig!
1978 ret
= initDNSSEC(log
);
1982 g_maxCacheEntries
= ::arg().asNum("max-cache-entries");
1984 luaConfigDelayedThreads delayedLuaThreads
;
1986 ProxyMapping proxyMapping
;
1987 loadRecursorLuaConfig(::arg()["lua-config-file"], delayedLuaThreads
, proxyMapping
);
1988 // Initial proxy mapping
1989 g_proxyMapping
= proxyMapping
.empty() ? nullptr : std::make_unique
<ProxyMapping
>(proxyMapping
);
1991 catch (PDNSException
& e
) {
1992 SLOG(g_log
<< Logger::Error
<< "Cannot load Lua configuration: " << e
.reason
<< endl
,
1993 log
->error(Logr::Error
, e
.reason
, "Cannot load Lua configuration"));
1998 initPublicSuffixList(::arg()["public-suffix-list-file"]);
2002 RecThreadInfo::setWeDistributeQueries(::arg().mustDo("pdns-distributes-queries"));
2003 if (RecThreadInfo::weDistributeQueries()) {
2004 SLOG(g_log
<< Logger::Warning
<< "PowerDNS Recursor itself will distribute queries over threads" << endl
,
2005 log
->info(Logr::Notice
, "PowerDNS Recursor itself will distribute queries over threads"));
2008 g_outgoingEDNSBufsize
= ::arg().asNum("edns-outgoing-bufsize");
2010 if (::arg()["trace"] == "fail") {
2011 SyncRes::setDefaultLogMode(SyncRes::Store
);
2013 else if (::arg().mustDo("trace")) {
2014 SyncRes::setDefaultLogMode(SyncRes::Log
);
2015 ::arg().set("quiet") = "no";
2018 auto myHostname
= getHostname();
2019 if (!myHostname
.has_value()) {
2020 SLOG(g_log
<< Logger::Warning
<< "Unable to get the hostname, NSID and id.server values will be empty" << endl
,
2021 log
->info(Logr::Warning
, "Unable to get the hostname, NSID and id.server values will be empty"));
2024 ret
= initSyncRes(log
, myHostname
);
2029 g_proxyProtocolACL
.toMasks(::arg()["proxy-protocol-from"]);
2030 g_proxyProtocolMaximumSize
= ::arg().asNum("proxy-protocol-maximum-size");
2032 ret
= initDNS64(log
);
2036 g_networkTimeoutMsec
= ::arg().asNum("network-timeout");
2038 std::tie(g_initialDomainMap
, g_initialAllowNotifyFor
) = parseZoneConfiguration();
2040 g_latencyStatSize
= ::arg().asNum("latency-statistic-size");
2042 g_logCommonErrors
= ::arg().mustDo("log-common-errors");
2043 g_logRPZChanges
= ::arg().mustDo("log-rpz-changes");
2045 g_anyToTcp
= ::arg().mustDo("any-to-tcp");
2046 g_udpTruncationThreshold
= ::arg().asNum("udp-truncation-threshold");
2048 g_lowercaseOutgoing
= ::arg().mustDo("lowercase-outgoing");
2050 g_paddingFrom
.toMasks(::arg()["edns-padding-from"]);
2051 if (::arg()["edns-padding-mode"] == "always") {
2052 g_paddingMode
= PaddingMode::Always
;
2054 else if (::arg()["edns-padding-mode"] == "padded-queries-only") {
2055 g_paddingMode
= PaddingMode::PaddedQueries
;
2058 SLOG(g_log
<< Logger::Error
<< "Unknown edns-padding-mode: " << ::arg()["edns-padding-mode"] << endl
,
2059 log
->info(Logr::Error
, "Unknown edns-padding-mode", "edns-padding-mode", Logging::Loggable(::arg()["edns-padding-mode"])));
2062 g_paddingTag
= ::arg().asNum("edns-padding-tag");
2063 g_paddingOutgoing
= ::arg().mustDo("edns-padding-out");
2065 RecThreadInfo::setNumDistributorThreads(::arg().asNum("distributor-threads"));
2066 RecThreadInfo::setNumWorkerThreads(::arg().asNum("threads"));
2067 if (RecThreadInfo::numWorkers() < 1) {
2068 SLOG(g_log
<< Logger::Warning
<< "Asked to run with 0 threads, raising to 1 instead" << endl
,
2069 log
->info(Logr::Warning
, "Asked to run with 0 threads, raising to 1 instead"));
2070 RecThreadInfo::setNumWorkerThreads(1);
2073 g_maxMThreads
= ::arg().asNum("max-mthreads");
2075 int64_t maxInFlight
= ::arg().asNum("max-concurrent-requests-per-tcp-connection");
2076 if (maxInFlight
< 1 || maxInFlight
> USHRT_MAX
|| maxInFlight
>= g_maxMThreads
) {
2077 SLOG(g_log
<< Logger::Warning
<< "Asked to run with illegal max-concurrent-requests-per-tcp-connection, setting to default (10)" << endl
,
2078 log
->info(Logr::Warning
, "Asked to run with illegal max-concurrent-requests-per-tcp-connection, setting to default (10)"));
2079 TCPConnection::s_maxInFlight
= 10;
2082 TCPConnection::s_maxInFlight
= maxInFlight
;
2085 int64_t millis
= ::arg().asNum("tcp-out-max-idle-ms");
2086 TCPOutConnectionManager::s_maxIdleTime
= timeval
{millis
/ 1000, (static_cast<suseconds_t
>(millis
) % 1000) * 1000};
2087 TCPOutConnectionManager::s_maxIdlePerAuth
= ::arg().asNum("tcp-out-max-idle-per-auth");
2088 TCPOutConnectionManager::s_maxQueries
= ::arg().asNum("tcp-out-max-queries");
2089 TCPOutConnectionManager::s_maxIdlePerThread
= ::arg().asNum("tcp-out-max-idle-per-thread");
2091 g_gettagNeedsEDNSOptions
= ::arg().mustDo("gettag-needs-edns-options");
2093 s_statisticsInterval
= ::arg().asNum("statistics-interval");
2095 SyncRes::s_addExtendedResolutionDNSErrors
= ::arg().mustDo("extended-resolution-errors");
2097 if (::arg().asNum("aggressive-nsec-cache-size") > 0) {
2098 if (g_dnssecmode
== DNSSECMode::ValidateAll
|| g_dnssecmode
== DNSSECMode::ValidateForLog
|| g_dnssecmode
== DNSSECMode::Process
) {
2099 g_aggressiveNSECCache
= make_unique
<AggressiveNSECCache
>(::arg().asNum("aggressive-nsec-cache-size"));
2102 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
,
2103 log
->info(Logr::Warning
, "Aggressive NSEC/NSEC3 caching is enabled but DNSSEC validation is not set to 'validate', 'log-fail' or 'process', ignoring"));
2107 AggressiveNSECCache::s_maxNSEC3CommonPrefix
= static_cast<uint8_t>(std::round(std::log2(::arg().asNum("aggressive-cache-min-nsec3-hit-ratio"))));
2108 SLOG(g_log
<< Logger::Debug
<< "NSEC3 aggressive cache tuning: aggressive-cache-min-nsec3-hit-ratio: " << ::arg().asNum("aggressive-cache-min-nsec3-hit-ratio") << " max common prefix bits: " << std::to_string(AggressiveNSECCache::s_maxNSEC3CommonPrefix
) << endl
,
2109 log
->info(Logr::Debug
, "NSEC3 aggressive cache tuning", "aggressive-cache-min-nsec3-hit-ratio", Logging::Loggable(::arg().asNum("aggressive-cache-min-nsec3-hit-ratio")), "maxCommonPrefixBits", Logging::Loggable(AggressiveNSECCache::s_maxNSEC3CommonPrefix
)));
2111 initSuffixMatchNodes(log
);
2113 initDistribution(log
);
2116 // Setup newly observed domain globals
2118 #endif /* NOD_ENABLED */
2120 auto forks
= initForks(log
);
2124 #ifdef HAVE_LIBSODIUM
2125 if (sodium_init() == -1) {
2126 SLOG(g_log
<< Logger::Error
<< "Unable to initialize sodium crypto library" << endl
,
2127 log
->info(Logr::Error
, "Unable to initialize sodium crypto library"));
2132 openssl_thread_setup();
2134 /* setup rng before chroot */
2137 if (::arg()["server-id"].empty()) {
2138 ::arg().set("server-id") = myHostname
.has_value() ? *myHostname
: "";
2142 if (!::arg()["setgid"].empty()) {
2143 newgid
= strToGID(::arg()["setgid"]);
2146 if (!::arg()["setuid"].empty()) {
2147 newuid
= strToUID(::arg()["setuid"]);
2150 Utility::dropGroupPrivs(newuid
, newgid
);
2152 ret
= initControl(log
, newuid
, forks
);
2157 startLuaConfigDelayedThreads(delayedLuaThreads
, g_luaconfs
.getCopy().generation
);
2158 delayedLuaThreads
.rpzPrimaryThreads
.clear(); // no longer needed
2160 RecThreadInfo::makeThreadPipes(log
);
2162 g_tcpTimeout
= ::arg().asNum("client-tcp-timeout");
2163 g_maxTCPPerClient
= ::arg().asNum("max-tcp-per-client");
2164 g_tcpMaxQueriesPerConn
= ::arg().asNum("max-tcp-queries-per-connection");
2165 g_maxUDPQueriesPerRound
= ::arg().asNum("max-udp-queries-per-round");
2167 g_useKernelTimestamp
= ::arg().mustDo("protobuf-use-kernel-timestamp");
2169 disableStats(StatComponent::API
, ::arg()["stats-api-blacklist"]);
2170 disableStats(StatComponent::Carbon
, ::arg()["stats-carbon-blacklist"]);
2171 disableStats(StatComponent::RecControl
, ::arg()["stats-rec-control-blacklist"]);
2172 disableStats(StatComponent::SNMP
, ::arg()["stats-snmp-blacklist"]);
2174 disableStats(StatComponent::API
, ::arg()["stats-api-disabled-list"]);
2175 disableStats(StatComponent::Carbon
, ::arg()["stats-carbon-disabled-list"]);
2176 disableStats(StatComponent::RecControl
, ::arg()["stats-rec-control-disabled-list"]);
2177 disableStats(StatComponent::SNMP
, ::arg()["stats-snmp-disabled-list"]);
2179 // Run before any thread doing stats related things
2184 ret
= initPorts(log
);
2189 return RecThreadInfo::runThreads(log
);
2192 static void handlePipeRequest(int fileDesc
, FDMultiplexer::funcparam_t
& /* var */)
2194 ThreadMSG
* tmsg
= nullptr;
2196 if (read(fileDesc
, &tmsg
, sizeof(tmsg
)) != sizeof(tmsg
)) { // fd == readToThread || fd == readQueriesToThread NOLINT: sizeof correct
2197 unixDie("read from thread pipe returned wrong size or error");
2200 void* resp
= nullptr;
2202 resp
= tmsg
->func();
2204 catch (std::exception
& e
) {
2205 if (g_logCommonErrors
) {
2206 SLOG(g_log
<< Logger::Error
<< "PIPE function we executed created exception: " << e
.what() << endl
, // but what if they wanted an answer.. we send 0
2207 g_slog
->withName("runtime")->error(Logr::Error
, e
.what(), "PIPE function we executed created exception", "exception", Logging::Loggable("std::exception")));
2210 catch (PDNSException
& e
) {
2211 if (g_logCommonErrors
) {
2212 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
2213 g_slog
->withName("runtime")->error(Logr::Error
, e
.reason
, "PIPE function we executed created exception", "exception", Logging::Loggable("PDNSException")));
2216 if (tmsg
->wantAnswer
) {
2217 if (write(RecThreadInfo::self().pipes
.writeFromThread
, &resp
, sizeof(resp
)) != sizeof(resp
)) {
2218 delete tmsg
; // NOLINT: manual ownership handling
2219 unixDie("write to thread pipe returned wrong size or error");
2223 delete tmsg
; // NOLINT: manual ownership handling
2226 static void handleRCC(int fileDesc
, FDMultiplexer::funcparam_t
& /* var */)
2228 auto log
= g_slog
->withName("control");
2230 FDWrapper clientfd
= accept(fileDesc
, nullptr, nullptr);
2231 if (clientfd
== -1) {
2232 throw PDNSException("accept failed");
2234 string msg
= g_rcc
.recv(clientfd
).d_str
;
2235 SLOG(g_log
<< Logger::Info
<< "Received rec_control command '" << msg
<< "' via controlsocket" << endl
,
2236 log
->info(Logr::Info
, "Received rec_control command via control socket", "command", Logging::Loggable(msg
)));
2238 RecursorControlParser rcp
;
2239 RecursorControlParser::func_t
* command
= nullptr;
2240 auto answer
= rcp
.getAnswer(clientfd
, msg
, &command
);
2242 g_rcc
.send(clientfd
, answer
);
2245 catch (const std::exception
& e
) {
2246 SLOG(g_log
<< Logger::Error
<< "Error dealing with control socket request: " << e
.what() << endl
,
2247 log
->error(Logr::Error
, e
.what(), "Exception while dealing with control socket request", "exception", Logging::Loggable("std::exception")));
2249 catch (const PDNSException
& ae
) {
2250 SLOG(g_log
<< Logger::Error
<< "Error dealing with control socket request: " << ae
.reason
<< endl
,
2251 log
->error(Logr::Error
, ae
.reason
, "Exception while dealing with control socket request", "exception", Logging::Loggable("PDNSException")));
2258 PeriodicTask(const string
& aName
, time_t aTime
) :
2259 period
{aTime
, 0}, name(aName
)
2262 throw PDNSException("Invalid period of periodic task " + aName
);
2266 void runIfDue(struct timeval
& now
, const std::function
<void()>& function
)
2268 if (last_run
< now
- period
) {
2269 // cerr << RecThreadInfo::id() << ' ' << name << ' ' << now.tv_sec << '.' << now.tv_usec << " running" << endl;
2271 Utility::gettimeofday(&last_run
);
2276 [[nodiscard
]] time_t getPeriod() const
2278 return period
.tv_sec
;
2281 void setPeriod(time_t newperiod
)
2283 period
.tv_sec
= newperiod
;
2286 void updateLastRun()
2288 Utility::gettimeofday(&last_run
);
2291 [[nodiscard
]] bool hasRun() const
2293 return last_run
.tv_sec
!= 0 || last_run
.tv_usec
!= 0;
2297 struct timeval last_run
2301 struct timeval period
;
2305 static void houseKeepingWork(Logr::log_t log
)
2310 Utility::gettimeofday(&now
);
2311 t_Counters
.updateSnap(now
, g_regressionTestMode
);
2313 // Below are the tasks that run for every recursorThread, including handler and taskThread
2315 static thread_local PeriodicTask pruneTCPTask
{"pruneTCPTask", 5};
2316 pruneTCPTask
.runIfDue(now
, [now
]() {
2317 t_tcp_manager
.cleanup(now
);
2320 const auto& info
= RecThreadInfo::self();
2322 // Threads handling packets process config changes in the input path, but not all threads process input packets
2323 // distr threads only process TCP, so that may not happenn very often. So do all periodically.
2324 static thread_local PeriodicTask exportConfigTask
{"exportConfigTask", 30};
2325 auto luaconfsLocal
= g_luaconfs
.getLocal();
2326 exportConfigTask
.runIfDue(now
, [&luaconfsLocal
]() {
2327 checkProtobufExport(luaconfsLocal
);
2328 checkOutgoingProtobufExport(luaconfsLocal
);
2330 checkFrameStreamExport(luaconfsLocal
, luaconfsLocal
->frameStreamExportConfig
, t_frameStreamServersInfo
);
2331 checkFrameStreamExport(luaconfsLocal
, luaconfsLocal
->nodFrameStreamExportConfig
, t_nodFrameStreamServersInfo
);
2335 // Below are the thread specific tasks for the handler and the taskThread
2336 // Likley a few handler tasks could be moved to the taskThread
2337 if (info
.isTaskThread()) {
2338 // TaskQueue is run always
2339 runTasks(10, g_logCommonErrors
);
2341 static PeriodicTask ztcTask
{"ZTC", 60};
2342 static map
<DNSName
, RecZoneToCache::State
> ztcStates
;
2343 ztcTask
.runIfDue(now
, [&luaconfsLocal
]() {
2344 RecZoneToCache::maintainStates(luaconfsLocal
->ztcConfigs
, ztcStates
, luaconfsLocal
->generation
);
2345 for (const auto& ztc
: luaconfsLocal
->ztcConfigs
) {
2346 RecZoneToCache::ZoneToCache(ztc
.second
, ztcStates
.at(ztc
.first
));
2350 else if (info
.isHandler()) {
2351 if (g_packetCache
) {
2352 static PeriodicTask packetCacheTask
{"packetCacheTask", 5};
2353 packetCacheTask
.runIfDue(now
, []() {
2354 g_packetCache
->doPruneTo(g_maxPacketCacheEntries
);
2357 static PeriodicTask recordCachePruneTask
{"RecordCachePruneTask", 5};
2358 recordCachePruneTask
.runIfDue(now
, []() {
2359 g_recCache
->doPrune(g_maxCacheEntries
);
2362 static PeriodicTask negCachePruneTask
{"NegCachePrunteTask", 5};
2363 negCachePruneTask
.runIfDue(now
, []() {
2364 g_negCache
->prune(g_maxCacheEntries
/ 8);
2367 static PeriodicTask aggrNSECPruneTask
{"AggrNSECPruneTask", 5};
2368 aggrNSECPruneTask
.runIfDue(now
, [now
]() {
2369 if (g_aggressiveNSECCache
) {
2370 g_aggressiveNSECCache
->prune(now
.tv_sec
);
2374 static PeriodicTask pruneNSpeedTask
{"pruneNSSpeedTask", 30};
2375 pruneNSpeedTask
.runIfDue(now
, [now
]() {
2376 SyncRes::pruneNSSpeeds(now
.tv_sec
- 300);
2379 static PeriodicTask pruneEDNSTask
{"pruneEDNSTask", 60};
2380 pruneEDNSTask
.runIfDue(now
, [now
]() {
2381 SyncRes::pruneEDNSStatuses(now
.tv_sec
);
2384 if (SyncRes::s_max_busy_dot_probes
> 0) {
2385 static PeriodicTask pruneDoTProbeMap
{"pruneDoTProbeMapTask", 60};
2386 pruneDoTProbeMap
.runIfDue(now
, [now
]() {
2387 SyncRes::pruneDoTProbeMap(now
.tv_sec
);
2391 static PeriodicTask pruneThrottledTask
{"pruneThrottledTask", 5};
2392 pruneThrottledTask
.runIfDue(now
, [now
]() {
2393 SyncRes::pruneThrottledServers(now
.tv_sec
);
2396 static PeriodicTask pruneFailedServersTask
{"pruneFailedServerTask", 5};
2397 pruneFailedServersTask
.runIfDue(now
, [now
]() {
2398 SyncRes::pruneFailedServers(now
.tv_sec
- static_cast<time_t>(SyncRes::s_serverdownthrottletime
* 10));
2401 static PeriodicTask pruneNonResolvingTask
{"pruneNonResolvingTask", 5};
2402 pruneNonResolvingTask
.runIfDue(now
, [now
]() {
2403 SyncRes::pruneNonResolving(now
.tv_sec
- SyncRes::s_nonresolvingnsthrottletime
);
2406 static PeriodicTask pruneSaveParentSetTask
{"pruneSaveParentSetTask", 60};
2407 pruneSaveParentSetTask
.runIfDue(now
, [now
]() {
2408 SyncRes::pruneSaveParentsNSSets(now
.tv_sec
);
2411 // By default, refresh at 80% of max-cache-ttl with a minimum period of 10s
2412 const unsigned int minRootRefreshInterval
= 10;
2413 static PeriodicTask rootUpdateTask
{"rootUpdateTask", std::max(SyncRes::s_maxcachettl
* 8 / 10, minRootRefreshInterval
)};
2414 rootUpdateTask
.runIfDue(now
, [now
, &log
, minRootRefreshInterval
]() {
2416 if (!g_regressionTestMode
) {
2417 res
= SyncRes::getRootNS(now
, nullptr, 0, log
);
2420 // Success, go back to the defaut period
2421 rootUpdateTask
.setPeriod(std::max(SyncRes::s_maxcachettl
* 8 / 10, minRootRefreshInterval
));
2424 // On failure, go to the middle of the remaining period (initially 80% / 8 = 10%) and shorten the interval on each
2425 // failure by dividing the existing interval by 8, keeping the minimum interval at 10s.
2426 // So with a 1 day period and failures we'll see a refresh attempt at 69120, 69120+11520, 69120+11520+1440, ...
2427 rootUpdateTask
.setPeriod(std::max
<time_t>(rootUpdateTask
.getPeriod() / 8, minRootRefreshInterval
));
2431 static PeriodicTask secpollTask
{"secpollTask", 3600};
2432 static time_t t_last_secpoll
;
2433 secpollTask
.runIfDue(now
, [&log
]() {
2435 doSecPoll(&t_last_secpoll
, log
);
2437 catch (const std::exception
& e
) {
2438 SLOG(g_log
<< Logger::Error
<< "Exception while performing security poll: " << e
.what() << endl
,
2439 log
->error(Logr::Error
, e
.what(), "Exception while performing security poll"));
2441 catch (const PDNSException
& e
) {
2442 SLOG(g_log
<< Logger::Error
<< "Exception while performing security poll: " << e
.reason
<< endl
,
2443 log
->error(Logr::Error
, e
.reason
, "Exception while performing security poll"));
2445 catch (const ImmediateServFailException
& e
) {
2446 SLOG(g_log
<< Logger::Error
<< "Exception while performing security poll: " << e
.reason
<< endl
,
2447 log
->error(Logr::Error
, e
.reason
, "Exception while performing security poll"));
2449 catch (const PolicyHitException
& e
) {
2450 SLOG(g_log
<< Logger::Error
<< "Policy hit while performing security poll" << endl
,
2451 log
->info(Logr::Error
, "Policy hit while performing security poll"));
2454 SLOG(g_log
<< Logger::Error
<< "Exception while performing security poll" << endl
,
2455 log
->info(Logr::Error
, "Exception while performing security poll"));
2459 const time_t taInterval
= std::max(1, static_cast<int>(luaconfsLocal
->trustAnchorFileInfo
.interval
) * 3600);
2460 static PeriodicTask trustAnchorTask
{"trustAnchorTask", taInterval
};
2461 if (!trustAnchorTask
.hasRun()) {
2462 // Loading the Lua config file already "refreshed" the TAs
2463 trustAnchorTask
.updateLastRun();
2465 // interval might have ben updated
2466 trustAnchorTask
.setPeriod(taInterval
);
2467 trustAnchorTask
.runIfDue(now
, [&luaconfsLocal
, &log
]() {
2468 if (!luaconfsLocal
->trustAnchorFileInfo
.fname
.empty() && luaconfsLocal
->trustAnchorFileInfo
.interval
!= 0) {
2469 SLOG(g_log
<< Logger::Debug
<< "Refreshing Trust Anchors from file" << endl
,
2470 log
->info(Logr::Debug
, "Refreshing Trust Anchors from file"));
2472 map
<DNSName
, dsmap_t
> dsAnchors
;
2473 if (updateTrustAnchorsFromFile(luaconfsLocal
->trustAnchorFileInfo
.fname
, dsAnchors
, log
)) {
2474 g_luaconfs
.modify([&dsAnchors
](LuaConfigItems
& lci
) {
2475 lci
.dsAnchors
= dsAnchors
;
2479 catch (const PDNSException
& pe
) {
2480 SLOG(g_log
<< Logger::Error
<< "Unable to update Trust Anchors: " << pe
.reason
<< endl
,
2481 log
->error(Logr::Error
, pe
.reason
, "Unable to update Trust Anchors"));
2486 t_Counters
.updateSnap(g_regressionTestMode
);
2489 static void houseKeeping(void* /* ignored */)
2491 auto log
= g_slog
->withName("housekeeping");
2492 static thread_local
bool t_running
; // houseKeeping can get suspended in secpoll, and be restarted, which makes us do duplicate work
2499 houseKeepingWork(log
);
2502 catch (const PDNSException
& ae
) {
2504 SLOG(g_log
<< Logger::Error
<< "Fatal error in housekeeping thread: " << ae
.reason
<< endl
,
2505 log
->error(Logr::Error
, ae
.reason
, "Fatal error in housekeeping thread"));
2510 SLOG(g_log
<< Logger::Error
<< "Uncaught exception in housekeeping thread" << endl
,
2511 log
->info(Logr::Error
, "Uncaught exception in housekeeping thread"));
2516 static void runLuaMaintenance(RecThreadInfo
& threadInfo
, time_t& last_lua_maintenance
, time_t luaMaintenanceInterval
)
2518 if (t_pdl
!= nullptr) {
2519 // lua-dns-script directive is present, call the maintenance callback if needed
2520 /* remember that the listener threads handle TCP queries */
2521 if (threadInfo
.isWorker() || threadInfo
.isListener()) {
2522 // Only on threads processing queries
2523 if (g_now
.tv_sec
- last_lua_maintenance
>= luaMaintenanceInterval
) {
2524 struct timeval start
2527 Utility::gettimeofday(&start
);
2528 t_pdl
->maintenance();
2529 last_lua_maintenance
= g_now
.tv_sec
;
2533 Utility::gettimeofday(&stop
);
2534 t_Counters
.at(rec::Counter::maintenanceUsec
) += uSec(stop
- start
);
2535 ++t_Counters
.at(rec::Counter::maintenanceCalls
);
2541 static void runTCPMaintenance(RecThreadInfo
& threadInfo
, bool& listenOnTCP
, unsigned int maxTcpClients
)
2543 if (threadInfo
.isListener()) {
2545 if (TCPConnection::getCurrentConnections() > maxTcpClients
) { // shutdown, too many connections
2546 for (const auto fileDesc
: threadInfo
.tcpSockets
) {
2547 t_fdm
->removeReadFD(fileDesc
);
2549 listenOnTCP
= false;
2553 if (TCPConnection::getCurrentConnections() <= maxTcpClients
) { // reenable
2554 for (const auto fileDesc
: threadInfo
.tcpSockets
) {
2555 t_fdm
->addReadFD(fileDesc
, handleNewTCPQuestion
);
2563 static void recLoop()
2565 unsigned int maxTcpClients
= ::arg().asNum("max-tcp-clients");
2566 bool listenOnTCP
{true};
2567 time_t last_stat
= 0;
2568 time_t last_carbon
= 0;
2569 time_t last_lua_maintenance
= 0;
2570 time_t carbonInterval
= ::arg().asNum("carbon-interval");
2571 time_t luaMaintenanceInterval
= ::arg().asNum("lua-maintenance-interval");
2573 auto& threadInfo
= RecThreadInfo::self();
2575 while (!RecursorControlChannel::stop
) {
2576 while (g_multiTasker
->schedule(&g_now
)) {
2577 ; // MTasker letting the mthreads do their thing
2580 // Use primes, it avoid not being scheduled in cases where the counter has a regular pattern.
2581 // We want to call handler thread often, it gets scheduled about 2 times per second
2582 if (((threadInfo
.isHandler() || threadInfo
.isTaskThread()) && s_counter
% 11 == 0) || s_counter
% 499 == 0) {
2583 struct timeval start
2586 Utility::gettimeofday(&start
);
2587 g_multiTasker
->makeThread(houseKeeping
, nullptr);
2588 if (!threadInfo
.isTaskThread()) {
2592 Utility::gettimeofday(&stop
);
2593 t_Counters
.at(rec::Counter::maintenanceUsec
) += uSec(stop
- start
);
2594 ++t_Counters
.at(rec::Counter::maintenanceCalls
);
2598 if (s_counter
% 55 == 0) {
2599 auto expired
= t_fdm
->getTimeouts(g_now
);
2601 for (const auto& exp
: expired
) {
2602 auto conn
= boost::any_cast
<shared_ptr
<TCPConnection
>>(exp
.second
);
2603 if (g_logCommonErrors
) {
2604 SLOG(g_log
<< Logger::Warning
<< "Timeout from remote TCP client " << conn
->d_remote
.toStringWithPort() << endl
,
2605 g_slogtcpin
->info(Logr::Warning
, "Timeout from remote TCP client", "remote", Logging::Loggable(conn
->d_remote
)));
2607 t_fdm
->removeReadFD(exp
.first
);
2613 if (threadInfo
.isHandler()) {
2614 if (statsWanted
|| (s_statisticsInterval
> 0 && (g_now
.tv_sec
- last_stat
) >= s_statisticsInterval
)) {
2616 last_stat
= g_now
.tv_sec
;
2619 Utility::gettimeofday(&g_now
, nullptr);
2621 if ((g_now
.tv_sec
- last_carbon
) >= carbonInterval
) {
2622 g_multiTasker
->makeThread(doCarbonDump
, nullptr);
2623 last_carbon
= g_now
.tv_sec
;
2626 runLuaMaintenance(threadInfo
, last_lua_maintenance
, luaMaintenanceInterval
);
2629 // 'run' updates g_now for us
2631 runTCPMaintenance(threadInfo
, listenOnTCP
, maxTcpClients
);
2635 static void recursorThread()
2637 auto log
= g_slog
->withName("runtime");
2638 t_Counters
.updateSnap(true);
2640 auto& threadInfo
= RecThreadInfo::self();
2642 SyncRes
tmp(g_now
); // make sure it allocates tsstorage before we do anything, like primeHints or so..
2643 SyncRes::setDomainMap(g_initialDomainMap
);
2644 t_allowFrom
= g_initialAllowFrom
;
2645 t_allowNotifyFrom
= g_initialAllowNotifyFrom
;
2646 t_allowNotifyFor
= g_initialAllowNotifyFor
;
2647 t_udpclientsocks
= std::make_unique
<UDPClientSocks
>();
2648 t_tcpClientCounts
= std::make_unique
<tcpClientCounts_t
>();
2649 if (g_proxyMapping
) {
2650 t_proxyMapping
= make_unique
<ProxyMapping
>(*g_proxyMapping
);
2653 t_proxyMapping
= nullptr;
2656 if (threadInfo
.isHandler()) {
2657 if (!primeHints()) {
2658 threadInfo
.setExitCode(EXIT_FAILURE
);
2659 RecursorControlChannel::stop
= true;
2660 SLOG(g_log
<< Logger::Critical
<< "Priming cache failed, stopping" << endl
,
2661 log
->info(Logr::Critical
, "Priming cache failed, stopping"));
2663 SLOG(g_log
<< Logger::Debug
<< "Done priming cache with root hints" << endl
,
2664 log
->info(Logr::Debug
, "Done priming cache with root hints"));
2669 if (threadInfo
.isWorker()) {
2670 setupNODThread(log
);
2672 #endif /* NOD_ENABLED */
2674 /* the listener threads handle TCP queries */
2675 if (threadInfo
.isWorker() || threadInfo
.isListener()) {
2677 if (!::arg()["lua-dns-script"].empty()) {
2678 t_pdl
= std::make_shared
<RecursorLua4
>();
2679 t_pdl
->loadFile(::arg()["lua-dns-script"]);
2680 SLOG(g_log
<< Logger::Warning
<< "Loaded 'lua' script from '" << ::arg()["lua-dns-script"] << "'" << endl
,
2681 log
->info(Logr::Warning
, "Loading Lua script from file", "name", Logging::Loggable(::arg()["lua-dns-script"])));
2684 catch (std::exception
& e
) {
2685 SLOG(g_log
<< Logger::Error
<< "Failed to load 'lua' script from '" << ::arg()["lua-dns-script"] << "': " << e
.what() << endl
,
2686 log
->error(Logr::Error
, e
.what(), "Failed to load Lua script from file", "name", Logging::Loggable(::arg()["lua-dns-script"])));
2691 unsigned int ringsize
= ::arg().asNum("stats-ringbuffer-entries") / RecThreadInfo::numWorkers();
2692 if (ringsize
!= 0) {
2693 t_remotes
= std::make_unique
<addrringbuf_t
>();
2694 if (RecThreadInfo::weDistributeQueries()) {
2695 t_remotes
->set_capacity(::arg().asNum("stats-ringbuffer-entries") / RecThreadInfo::numDistributors());
2698 t_remotes
->set_capacity(ringsize
);
2700 t_servfailremotes
= std::make_unique
<addrringbuf_t
>();
2701 t_servfailremotes
->set_capacity(ringsize
);
2702 t_bogusremotes
= std::make_unique
<addrringbuf_t
>();
2703 t_bogusremotes
->set_capacity(ringsize
);
2704 t_largeanswerremotes
= std::make_unique
<addrringbuf_t
>();
2705 t_largeanswerremotes
->set_capacity(ringsize
);
2706 t_timeouts
= std::make_unique
<addrringbuf_t
>();
2707 t_timeouts
->set_capacity(ringsize
);
2709 t_queryring
= std::make_unique
<boost::circular_buffer
<pair
<DNSName
, uint16_t>>>();
2710 t_queryring
->set_capacity(ringsize
);
2711 t_servfailqueryring
= std::make_unique
<boost::circular_buffer
<pair
<DNSName
, uint16_t>>>();
2712 t_servfailqueryring
->set_capacity(ringsize
);
2713 t_bogusqueryring
= std::make_unique
<boost::circular_buffer
<pair
<DNSName
, uint16_t>>>();
2714 t_bogusqueryring
->set_capacity(ringsize
);
2716 g_multiTasker
= std::make_unique
<MT_t
>(::arg().asNum("stack-size"), ::arg().asNum("stack-cache-size"));
2717 threadInfo
.mt
= g_multiTasker
.get();
2719 /* start protobuf export threads if needed */
2720 auto luaconfsLocal
= g_luaconfs
.getLocal();
2721 checkProtobufExport(luaconfsLocal
);
2722 checkOutgoingProtobufExport(luaconfsLocal
);
2724 checkFrameStreamExport(luaconfsLocal
, luaconfsLocal
->frameStreamExportConfig
, t_frameStreamServersInfo
);
2725 checkFrameStreamExport(luaconfsLocal
, luaconfsLocal
->nodFrameStreamExportConfig
, t_nodFrameStreamServersInfo
);
2728 t_fdm
= unique_ptr
<FDMultiplexer
>(getMultiplexer(log
));
2730 std::unique_ptr
<RecursorWebServer
> rws
;
2732 t_fdm
->addReadFD(threadInfo
.pipes
.readToThread
, handlePipeRequest
);
2734 if (threadInfo
.isHandler()) {
2735 if (::arg().mustDo("webserver")) {
2736 SLOG(g_log
<< Logger::Warning
<< "Enabling web server" << endl
,
2737 log
->info(Logr::Info
, "Enabling web server"))
2739 rws
= make_unique
<RecursorWebServer
>(t_fdm
.get());
2741 catch (const PDNSException
& e
) {
2742 SLOG(g_log
<< Logger::Error
<< "Unable to start the internal web server: " << e
.reason
<< endl
,
2743 log
->error(Logr::Critical
, e
.reason
, "Exception while starting internal web server"));
2747 SLOG(g_log
<< Logger::Info
<< "Enabled '" << t_fdm
->getName() << "' multiplexer" << endl
,
2748 log
->info(Logr::Info
, "Enabled multiplexer", "name", Logging::Loggable(t_fdm
->getName())));
2751 t_fdm
->addReadFD(threadInfo
.pipes
.readQueriesToThread
, handlePipeRequest
);
2753 if (threadInfo
.isListener()) {
2755 /* then every listener has its own FDs */
2756 for (const auto& deferred
: threadInfo
.deferredAdds
) {
2757 t_fdm
->addReadFD(deferred
.first
, deferred
.second
);
2761 /* otherwise all listeners are listening on the same ones */
2762 for (const auto& deferred
: g_deferredAdds
) {
2763 t_fdm
->addReadFD(deferred
.first
, deferred
.second
);
2769 if (threadInfo
.isHandler()) {
2770 t_fdm
->addReadFD(g_rcc
.d_fd
, handleRCC
); // control channel
2774 if (threadInfo
.isHandler()) {
2775 // There is a race, as some threads might not be ready yet to do work.
2776 // To solve that, threads should notify RecThreadInfo they are done initializing.
2777 // But we lack a mechanism for that at this point in time.
2778 sd_notify(0, "READY=1");
2784 catch (PDNSException
& ae
) {
2785 SLOG(g_log
<< Logger::Error
<< "Exception: " << ae
.reason
<< endl
,
2786 log
->error(Logr::Error
, ae
.reason
, "Exception in RecursorThread", "exception", Logging::Loggable("PDNSException")))
2788 catch (std::exception
& e
) {
2789 SLOG(g_log
<< Logger::Error
<< "STL Exception: " << e
.what() << endl
,
2790 log
->error(Logr::Error
, e
.what(), "Exception in RecursorThread", "exception", Logging::Loggable("std::exception")))
2793 SLOG(g_log
<< Logger::Error
<< "any other exception in main: " << endl
,
2794 log
->info(Logr::Error
, "Exception in RecursorThread"));
2798 static void initArgs()
2800 #if HAVE_FIBER_SANITIZER
2801 // Asan needs more stack
2802 ::arg().set("stack-size", "stack size per mthread") = "600000";
2804 ::arg().set("stack-size", "stack size per mthread") = "200000";
2806 ::arg().set("stack-cache-size", "Size of the stack cache, per mthread") = "100";
2807 // This mode forces metrics snap updates and disable root-refresh, to get consistent counters
2808 ::arg().setSwitch("devonly-regression-test-mode", "internal use only") = "no";
2809 ::arg().set("soa-minimum-ttl", "Don't change") = "0";
2810 ::arg().set("no-shuffle", "Don't change") = "off";
2811 ::arg().set("local-port", "port to listen on") = "53";
2812 ::arg().set("local-address", "IP addresses to listen on, separated by spaces or commas. Also accepts ports.") = "127.0.0.1";
2813 ::arg().setSwitch("non-local-bind", "Enable binding to non-local addresses by using FREEBIND / BINDANY socket options") = "no";
2814 ::arg().set("trace", "if we should output heaps of logging. set to 'fail' to only log failing domains") = "off";
2815 ::arg().set("dnssec", "DNSSEC mode: off/process-no-validate/process (default)/log-fail/validate") = "process";
2816 ::arg().set("dnssec-log-bogus", "Log DNSSEC bogus validations") = "no";
2817 ::arg().set("signature-inception-skew", "Allow the signature inception to be off by this number of seconds") = "60";
2818 ::arg().set("dnssec-disabled-algorithms", "List of DNSSEC algorithm numbers that are considered unsupported") = "";
2819 ::arg().set("daemon", "Operate as a daemon") = "no";
2820 ::arg().setSwitch("write-pid", "Write a PID file") = "yes";
2821 ::arg().set("loglevel", "Amount of logging. Higher is more. Do not set below 3") = "6";
2822 ::arg().set("disable-syslog", "Disable logging to syslog, useful when running inside a supervisor that logs stdout") = "no";
2823 ::arg().set("log-timestamp", "Print timestamps in log lines, useful to disable when running with a tool that timestamps stdout already") = "yes";
2824 ::arg().set("log-common-errors", "If we should log rather common errors") = "no";
2825 ::arg().set("chroot", "switch to chroot jail") = "";
2826 ::arg().set("setgid", "If set, change group id to this gid for more security"
2828 #define SYSTEMD_SETID_MSG ". When running inside systemd, use the User and Group settings in the unit-file!"
2833 ::arg().set("setuid", "If set, change user id to this uid for more security"
2839 ::arg().set("network-timeout", "Wait this number of milliseconds for network i/o") = "1500";
2840 ::arg().set("threads", "Launch this number of threads") = "2";
2841 ::arg().set("distributor-threads", "Launch this number of distributor threads, distributing queries to other threads") = "0";
2842 ::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!
2843 ::arg().set("config-name", "Name of this virtual configuration - will rename the binary image") = "";
2844 ::arg().set("api-config-dir", "Directory where REST API stores config and zones") = "";
2845 ::arg().set("api-key", "Static pre-shared authentication key for access to the REST API") = "";
2846 ::arg().setSwitch("webserver", "Start a webserver (for REST API)") = "no";
2847 ::arg().set("webserver-address", "IP Address of webserver to listen on") = "127.0.0.1";
2848 ::arg().set("webserver-port", "Port of webserver to listen on") = "8082";
2849 ::arg().set("webserver-password", "Password required for accessing the webserver") = "";
2850 ::arg().set("webserver-allow-from", "Webserver access is only allowed from these subnets") = "127.0.0.1,::1";
2851 ::arg().set("webserver-loglevel", "Amount of logging in the webserver (none, normal, detailed)") = "normal";
2852 ::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";
2853 ::arg().set("carbon-ourname", "If set, overrides our reported hostname for carbon stats") = "";
2854 ::arg().set("carbon-server", "If set, send metrics in carbon (graphite) format to this server IP address") = "";
2855 ::arg().set("carbon-interval", "Number of seconds between carbon (graphite) updates") = "30";
2856 ::arg().set("carbon-namespace", "If set overwrites the first part of the carbon string") = "pdns";
2857 ::arg().set("carbon-instance", "If set overwrites the instance name default") = "recursor";
2859 ::arg().set("statistics-interval", "Number of seconds between printing of recursor statistics, 0 to disable") = "1800";
2860 ::arg().set("quiet", "Suppress logging of questions and answers") = "";
2861 ::arg().set("logging-facility", "Facility to log messages as. 0 corresponds to local0") = "";
2862 ::arg().set("config-dir", "Location of configuration directory (recursor.conf)") = SYSCONFDIR
;
2863 ::arg().set("socket-owner", "Owner of socket") = "";
2864 ::arg().set("socket-group", "Group of socket") = "";
2865 ::arg().set("socket-mode", "Permissions for socket") = "";
2867 ::arg().set("socket-dir", string("Where the controlsocket will live, ") + LOCALSTATEDIR
+ "/pdns-recursor when unset and not chrooted"
2869 + ". Set to the RUNTIME_DIRECTORY environment variable when that variable has a value (e.g. under systemd).")
2871 auto* runtimeDir
= getenv("RUNTIME_DIRECTORY"); // NOLINT(concurrency-mt-unsafe,cppcoreguidelines-pro-type-vararg)
2872 if (runtimeDir
!= nullptr) {
2873 ::arg().set("socket-dir") = runtimeDir
;
2879 ::arg().set("query-local-address", "Source IP address for sending queries") = "0.0.0.0";
2880 ::arg().set("client-tcp-timeout", "Timeout in seconds when talking to TCP clients") = "2";
2881 ::arg().set("max-mthreads", "Maximum number of simultaneous Mtasker threads") = "2048";
2882 ::arg().set("max-tcp-clients", "Maximum number of simultaneous TCP clients") = "128";
2883 ::arg().set("max-concurrent-requests-per-tcp-connection", "Maximum number of requests handled concurrently per TCP connection") = "10";
2884 ::arg().set("server-down-max-fails", "Maximum number of consecutive timeouts (and unreachables) to mark a server as down ( 0 => disabled )") = "64";
2885 ::arg().set("server-down-throttle-time", "Number of seconds to throttle all queries to a server after being marked as down") = "60";
2886 ::arg().set("dont-throttle-names", "Do not throttle nameservers with this name or suffix") = "";
2887 ::arg().set("dont-throttle-netmasks", "Do not throttle nameservers with this IP netmask") = "";
2888 ::arg().set("non-resolving-ns-max-fails", "Number of failed address resolves of a nameserver to start throttling it, 0 is disabled") = "5";
2889 ::arg().set("non-resolving-ns-throttle-time", "Number of seconds to throttle a nameserver with a name failing to resolve") = "60";
2891 ::arg().set("hint-file", "If set, load root hints from this file") = "";
2892 ::arg().set("max-cache-entries", "If set, maximum number of entries in the main cache") = "1000000";
2893 ::arg().set("max-negative-ttl", "maximum number of seconds to keep a negative cached entry in memory") = "3600";
2894 ::arg().set("max-cache-bogus-ttl", "maximum number of seconds to keep a Bogus (positive or negative) cached entry in memory") = "3600";
2895 ::arg().set("max-cache-ttl", "maximum number of seconds to keep a cached entry in memory") = "86400";
2896 ::arg().set("packetcache-ttl", "maximum number of seconds to keep a cached entry in packetcache") = "86400";
2897 ::arg().set("max-packetcache-entries", "maximum number of entries to keep in the packetcache") = "500000";
2898 ::arg().set("packetcache-servfail-ttl", "maximum number of seconds to keep a cached servfail entry in packetcache") = "60";
2899 ::arg().set("packetcache-negative-ttl", "maximum number of seconds to keep a cached NxDomain or NoData entry in packetcache") = "60";
2900 ::arg().set("server-id", "Returned when queried for 'id.server' TXT or NSID, defaults to hostname, set custom or 'disabled'") = "";
2901 ::arg().set("stats-ringbuffer-entries", "maximum number of packets to store statistics for") = "10000";
2902 ::arg().set("version-string", "string reported on version.pdns or version.bind") = fullVersionString();
2903 ::arg().set("allow-from", "If set, only allow these comma separated netmasks to recurse") = LOCAL_NETS
;
2904 ::arg().set("allow-from-file", "If set, load allowed netmasks from this file") = "";
2905 ::arg().set("allow-notify-for", "If set, NOTIFY requests for these zones will be allowed") = "";
2906 ::arg().set("allow-notify-for-file", "If set, load NOTIFY-allowed zones from this file") = "";
2907 ::arg().set("allow-notify-from", "If set, NOTIFY requests from these comma separated netmasks will be allowed") = "";
2908 ::arg().set("allow-notify-from-file", "If set, load NOTIFY-allowed netmasks from this file") = "";
2909 ::arg().set("entropy-source", "If set, read entropy from this file") = "/dev/urandom";
2910 ::arg().set("dont-query", "If set, do not query these netmasks for DNS data") = DONT_QUERY
;
2911 ::arg().set("max-tcp-per-client", "If set, maximum number of TCP sessions per client (IP address)") = "0";
2912 ::arg().set("max-tcp-queries-per-connection", "If set, maximum number of TCP queries in a TCP connection") = "0";
2913 ::arg().set("spoof-nearmiss-max", "If non-zero, assume spoofing after this many near misses") = "1";
2914 ::arg().set("single-socket", "If set, only use a single socket for outgoing queries") = "off";
2915 ::arg().set("auth-zones", "Zones for which we have authoritative data, comma separated domain=file pairs ") = "";
2916 ::arg().set("lua-config-file", "More powerful configuration options") = "";
2917 ::arg().setSwitch("allow-trust-anchor-query", "Allow queries for trustanchor.server CH TXT and negativetrustanchor.server CH TXT") = "no";
2919 ::arg().set("forward-zones", "Zones for which we forward queries, comma separated domain=ip pairs") = "";
2920 ::arg().set("forward-zones-recurse", "Zones for which we forward queries with recursion bit, comma separated domain=ip pairs") = "";
2921 ::arg().set("forward-zones-file", "File with (+)domain=ip pairs for forwarding") = "";
2922 ::arg().set("export-etc-hosts", "If we should serve up contents from /etc/hosts") = "off";
2923 ::arg().set("export-etc-hosts-search-suffix", "Also serve up the contents of /etc/hosts with this suffix") = "";
2924 ::arg().set("etc-hosts-file", "Path to 'hosts' file") = "/etc/hosts";
2925 ::arg().set("serve-rfc1918", "If we should be authoritative for RFC 1918 private IP space") = "yes";
2926 ::arg().set("lua-dns-script", "Filename containing an optional 'lua' script that will be used to modify dns answers") = "";
2927 ::arg().set("lua-maintenance-interval", "Number of seconds between calls to the lua user defined maintenance() function") = "1";
2928 ::arg().set("latency-statistic-size", "Number of latency values to calculate the qa-latency average") = "10000";
2929 ::arg().setSwitch("disable-packetcache", "Disable packetcache") = "no";
2930 ::arg().set("ecs-ipv4-bits", "Number of bits of IPv4 address to pass for EDNS Client Subnet") = "24";
2931 ::arg().set("ecs-ipv4-cache-bits", "Maximum number of bits of IPv4 mask to cache ECS response") = "24";
2932 ::arg().set("ecs-ipv6-bits", "Number of bits of IPv6 address to pass for EDNS Client Subnet") = "56";
2933 ::arg().set("ecs-ipv6-cache-bits", "Maximum number of bits of IPv6 mask to cache ECS response") = "56";
2934 ::arg().setSwitch("ecs-ipv4-never-cache", "If we should never cache IPv4 ECS responses") = "no";
2935 ::arg().setSwitch("ecs-ipv6-never-cache", "If we should never cache IPv6 ECS responses") = "no";
2936 ::arg().set("ecs-minimum-ttl-override", "The minimum TTL for records in ECS-specific answers") = "1";
2937 ::arg().set("ecs-cache-limit-ttl", "Minimum TTL to cache ECS response") = "0";
2938 ::arg().set("edns-subnet-whitelist", "List of netmasks and domains that we should enable EDNS subnet for (deprecated)") = "";
2939 ::arg().set("edns-subnet-allow-list", "List of netmasks and domains that we should enable EDNS subnet for") = "";
2940 ::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
;
2941 ::arg().set("ecs-scope-zero-address", "Address to send to allow-listed authoritative servers for incoming queries with ECS prefix-length source of 0") = "";
2942 ::arg().setSwitch("use-incoming-edns-subnet", "Pass along received EDNS Client Subnet information") = "no";
2943 ::arg().setSwitch("pdns-distributes-queries", "If PowerDNS itself should distribute queries over threads") = "no";
2944 ::arg().setSwitch("root-nx-trust", "If set, believe that an NXDOMAIN from the root means the TLD does not exist") = "yes";
2945 ::arg().setSwitch("any-to-tcp", "Answer ANY queries with tc=1, shunting to TCP") = "no";
2946 ::arg().setSwitch("lowercase-outgoing", "Force outgoing questions to lowercase") = "no";
2947 ::arg().setSwitch("gettag-needs-edns-options", "If EDNS Options should be extracted before calling the gettag() hook") = "no";
2948 ::arg().set("udp-truncation-threshold", "Maximum UDP response size before we truncate") = "1232";
2949 ::arg().set("edns-outgoing-bufsize", "Outgoing EDNS buffer size") = "1232";
2950 ::arg().set("minimum-ttl-override", "The minimum TTL") = "1";
2951 ::arg().set("max-qperq", "Maximum outgoing queries per query") = "60";
2952 ::arg().set("max-ns-per-resolve", "Maximum number of NS records to consider to resolve a name, 0 is no limit") = "13";
2953 ::arg().set("max-ns-address-qperq", "Maximum outgoing NS address queries per query") = "10";
2954 ::arg().set("max-total-msec", "Maximum total wall-clock time per query in milliseconds, 0 for unlimited") = "7000";
2955 ::arg().set("max-recursion-depth", "Maximum number of internal recursion calls per query, 0 for unlimited") = "16";
2956 ::arg().set("max-udp-queries-per-round", "Maximum number of UDP queries processed per recvmsg() round, before returning back to normal processing") = "10000";
2957 ::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)") = "";
2958 ::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";
2960 ::arg().set("include-dir", "Include *.conf files from this directory") = "";
2961 ::arg().set("security-poll-suffix", "Domain name from which to query security update notifications") = "secpoll.powerdns.com.";
2964 ::arg().setSwitch("reuseport", "Enable SO_REUSEPORT allowing multiple recursors processes to listen to 1 address") = "yes";
2966 ::arg().setSwitch("reuseport", "Enable SO_REUSEPORT allowing multiple recursors processes to listen to 1 address") = "no";
2968 ::arg().setSwitch("snmp-agent", "If set, register as an SNMP agent") = "no";
2969 ::arg().set("snmp-master-socket", "If set and snmp-agent is set, the socket to use to register to the SNMP daemon (deprecated)") = "";
2970 ::arg().set("snmp-daemon-socket", "If set and snmp-agent is set, the socket to use to register to the SNMP daemon") = "";
2972 std::string defaultAPIDisabledStats
= "cache-bytes, packetcache-bytes, special-memory-usage";
2973 for (size_t idx
= 0; idx
< 32; idx
++) {
2974 defaultAPIDisabledStats
+= ", ecs-v4-response-bits-" + std::to_string(idx
+ 1);
2976 for (size_t idx
= 0; idx
< 128; idx
++) {
2977 defaultAPIDisabledStats
+= ", ecs-v6-response-bits-" + std::to_string(idx
+ 1);
2979 std::string defaultDisabledStats
= defaultAPIDisabledStats
+ ", cumul-clientanswers, cumul-authanswers, policy-hits, proxy-mapping-total, remote-logger-count";
2981 ::arg().set("stats-api-blacklist", "List of statistics that are disabled when retrieving the complete list of statistics via the API (deprecated)") = defaultAPIDisabledStats
;
2982 ::arg().set("stats-carbon-blacklist", "List of statistics that are prevented from being exported via Carbon (deprecated)") = defaultDisabledStats
;
2983 ::arg().set("stats-rec-control-blacklist", "List of statistics that are prevented from being exported via rec_control get-all (deprecated)") = defaultDisabledStats
;
2984 ::arg().set("stats-snmp-blacklist", "List of statistics that are prevented from being exported via SNMP (deprecated)") = defaultDisabledStats
;
2986 ::arg().set("stats-api-disabled-list", "List of statistics that are disabled when retrieving the complete list of statistics via the API") = defaultAPIDisabledStats
;
2987 ::arg().set("stats-carbon-disabled-list", "List of statistics that are prevented from being exported via Carbon") = defaultDisabledStats
;
2988 ::arg().set("stats-rec-control-disabled-list", "List of statistics that are prevented from being exported via rec_control get-all") = defaultDisabledStats
;
2989 ::arg().set("stats-snmp-disabled-list", "List of statistics that are prevented from being exported via SNMP") = defaultDisabledStats
;
2991 ::arg().set("tcp-fast-open", "Enable TCP Fast Open support on the listening sockets, using the supplied numerical value as the queue size") = "0";
2992 ::arg().set("tcp-fast-open-connect", "Enable TCP Fast Open support on outgoing sockets") = "no";
2993 ::arg().set("nsec3-max-iterations", "Maximum number of iterations allowed for an NSEC3 record") = "150";
2995 ::arg().set("cpu-map", "Thread to CPU mapping, space separated thread-id=cpu1,cpu2..cpuN pairs") = "";
2997 ::arg().setSwitch("log-rpz-changes", "Log additions and removals to RPZ zones at Info level") = "no";
2999 ::arg().set("proxy-protocol-from", "A Proxy Protocol header is only allowed from these subnets") = "";
3000 ::arg().set("proxy-protocol-maximum-size", "The maximum size of a proxy protocol payload, including the TLV values") = "512";
3002 ::arg().set("dns64-prefix", "DNS64 prefix") = "";
3004 ::arg().set("udp-source-port-min", "Minimum UDP port to bind on") = "1024";
3005 ::arg().set("udp-source-port-max", "Maximum UDP port to bind on") = "65535";
3006 ::arg().set("udp-source-port-avoid", "List of comma separated UDP port number to avoid") = "11211";
3007 ::arg().set("rng", "Specify random number generator to use. Valid values are auto,sodium,openssl,getrandom,arc4random,urandom.") = "auto";
3008 ::arg().set("public-suffix-list-file", "Path to the Public Suffix List file, if any") = "";
3009 ::arg().set("distribution-load-factor", "The load factor used when PowerDNS is distributing queries to worker threads") = "0.0";
3011 ::arg().setSwitch("qname-minimization", "Use Query Name Minimization") = "yes";
3012 ::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";
3013 ::arg().set("max-generate-steps", "Maximum number of $GENERATE steps when loading a zone from a file") = "0";
3014 ::arg().set("max-include-depth", "Maximum nested $INCLUDE depth when loading a zone from a file") = "20";
3016 ::arg().set("record-cache-shards", "Number of shards in the record cache") = "1024";
3017 ::arg().set("packetcache-shards", "Number of shards in the packet cache") = "1024";
3019 ::arg().set("refresh-on-ttl-perc", "If a record is requested from the cache and only this % of original TTL remains, refetch") = "0";
3020 ::arg().set("record-cache-locked-ttl-perc", "Replace records in record cache only after this % of original TTL has passed") = "0";
3022 ::arg().set("x-dnssec-names", "Collect DNSSEC statistics for names or suffixes in this list in separate x-dnssec counters") = "";
3025 ::arg().set("new-domain-tracking", "Track newly observed domains (i.e. never seen before).") = "no";
3026 ::arg().set("new-domain-log", "Log newly observed domains.") = "yes";
3027 ::arg().set("new-domain-lookup", "Perform a DNS lookup newly observed domains as a subdomain of the configured domain") = "";
3028 ::arg().set("new-domain-history-dir", "Persist new domain tracking data here to persist between restarts") = string(NODCACHEDIR
) + "/nod";
3029 ::arg().set("new-domain-whitelist", "List of domains (and implicitly all subdomains) which will never be considered a new domain (deprecated)") = "";
3030 ::arg().set("new-domain-ignore-list", "List of domains (and implicitly all subdomains) which will never be considered a new domain") = "";
3031 ::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";
3032 ::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";
3033 ::arg().set("unique-response-tracking", "Track unique responses (tuple of query name, type and RR).") = "no";
3034 ::arg().set("unique-response-log", "Log unique responses") = "yes";
3035 ::arg().set("unique-response-history-dir", "Persist unique response tracking data here to persist between restarts") = string(NODCACHEDIR
) + "/udr";
3036 ::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";
3037 ::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";
3038 #endif /* NOD_ENABLED */
3040 ::arg().setSwitch("extended-resolution-errors", "If set, send an EDNS Extended Error extension on resolution failures, like DNSSEC validation errors") = "no";
3042 ::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";
3043 ::arg().set("aggressive-cache-min-nsec3-hit-ratio", "The minimum expected hit ratio to store NSEC3 records into the aggressive cache") = "2000";
3045 ::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") = "";
3046 ::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";
3047 ::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";
3048 ::arg().setSwitch("edns-padding-out", "Whether to add EDNS padding to outgoing DoT messages") = "yes";
3050 ::arg().setSwitch("dot-to-port-853", "Force DoT connection to target port 853 if DoT compiled in") = "yes";
3051 ::arg().set("dot-to-auth-names", "Use DoT to authoritative servers with these names or suffixes") = "";
3052 ::arg().set("event-trace-enabled", "If set, event traces are collected and send out via protobuf logging (1), logfile (2) or both(3)") = "0";
3054 ::arg().set("tcp-out-max-idle-ms", "Time TCP/DoT connections are left idle in milliseconds or 0 if no limit") = "10000";
3055 ::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";
3056 ::arg().set("tcp-out-max-queries", "Maximum total number of queries per TCP/DoT connection, 0 means no limit") = "0";
3057 ::arg().set("tcp-out-max-idle-per-thread", "Maximum number of idle TCP/DoT connections per thread") = "100";
3058 ::arg().setSwitch("structured-logging", "Prefer structured logging") = "yes";
3059 ::arg().set("structured-logging-backend", "Structured logging backend") = "default";
3060 ::arg().setSwitch("save-parent-ns-set", "Save parent NS set to be used if child NS set fails") = "yes";
3061 ::arg().set("max-busy-dot-probes", "Maximum number of concurrent DoT probes") = "0";
3062 ::arg().set("serve-stale-extensions", "Number of times a record's ttl is extended by 30s to be served stale") = "0";
3064 ::arg().setCmd("help", "Provide a helpful message");
3065 ::arg().setCmd("version", "Print version string");
3066 ::arg().setCmd("config", "Output blank configuration. You can use --config=check to test the config file and command line arguments.");
3067 ::arg().setDefaults();
3068 g_log
.toConsole(Logger::Info
);
3071 static pair
<int, bool> doConfig(Logr::log_t startupLog
, const string
& configname
, int argc
, char* argv
[]) // NOLINT: Posix API
3073 if (::arg().mustDo("config")) {
3074 string config
= ::arg()["config"];
3075 if (config
== "check") {
3077 if (!::arg().file(configname
.c_str())) {
3078 SLOG(g_log
<< Logger::Warning
<< "Unable to open configuration file '" << configname
<< "'" << endl
,
3079 startupLog
->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname
)));
3082 ::arg().parse(argc
, argv
);
3085 catch (const ArgException
& argException
) {
3086 SLOG(g_log
<< Logger::Warning
<< "Unable to parse configuration file '" << configname
<< "': " << argException
.reason
<< endl
,
3087 startupLog
->error("Cannot parse configuration", "Unable to parse configuration file", "config_file", Logging::Loggable(configname
), "reason", Logging::Loggable(argException
.reason
)));
3091 else if (config
== "default" || config
.empty()) {
3092 cout
<< ::arg().configstring(false, true);
3094 else if (config
== "diff") {
3095 if (!::arg().laxFile(configname
.c_str())) {
3096 SLOG(g_log
<< Logger::Warning
<< "Unable to open configuration file '" << configname
<< "'" << endl
,
3097 startupLog
->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname
)));
3100 ::arg().laxParse(argc
, argv
);
3101 cout
<< ::arg().configstring(true, false);
3104 if (!::arg().laxFile(configname
.c_str())) {
3105 SLOG(g_log
<< Logger::Warning
<< "Unable to open configuration file '" << configname
<< "'" << endl
,
3106 startupLog
->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname
)));
3109 ::arg().laxParse(argc
, argv
);
3110 cout
<< ::arg().configstring(true, true);
3117 int main(int argc
, char** argv
)
3122 versionSetProduct(ProductRecursor
);
3126 int ret
= EXIT_SUCCESS
;
3130 ::arg().laxParse(argc
, argv
); // do a lax parse
3132 if (::arg().mustDo("version")) {
3133 showProductVersion();
3134 showBuildConfiguration();
3137 if (::arg().mustDo("help")) {
3138 cout
<< "syntax:" << endl
3140 cout
<< ::arg().helpstring(::arg()["help"]) << endl
;
3144 // Pick up options given on command line to setup logging asap.
3145 g_quiet
= ::arg().mustDo("quiet");
3146 s_logUrgency
= (Logger::Urgency
)::arg().asNum("loglevel");
3147 g_slogStructured
= ::arg().mustDo("structured-logging");
3148 s_structured_logger_backend
= ::arg()["structured-logging-backend"];
3150 if (s_logUrgency
< Logger::Error
) {
3151 s_logUrgency
= Logger::Error
;
3153 if (!g_quiet
&& s_logUrgency
< Logger::Info
) { // Logger::Info=6, Logger::Debug=7
3154 s_logUrgency
= Logger::Info
; // if you do --quiet=no, you need Info to also see the query log
3156 g_log
.setLoglevel(s_logUrgency
);
3157 g_log
.toConsole(s_logUrgency
);
3159 string configname
= ::arg()["config-dir"] + "/recursor.conf";
3160 if (!::arg()["config-name"].empty()) {
3161 configname
= ::arg()["config-dir"] + "/recursor-" + ::arg()["config-name"] + ".conf";
3162 g_programname
+= "-" + ::arg()["config-name"];
3164 cleanSlashes(configname
);
3166 if (!::arg().getCommands().empty()) {
3167 cerr
<< "Fatal: non-option";
3168 if (::arg().getCommands().size() > 1) {
3173 for (const auto& command
: ::arg().getCommands()) {
3180 cerr
<< ") on the command line, perhaps a '--setting=123' statement missed the '='?" << endl
;
3184 if (s_structured_logger_backend
== "systemd-journal") {
3186 if (int fd
= sd_journal_stream_fd("pdns-recusor", LOG_DEBUG
, 0); fd
>= 0) {
3187 g_slog
= Logging::Logger::create(loggerSDBackend
);
3191 if (g_slog
== nullptr) {
3192 cerr
<< "Structured logging to systemd-journal requested but it is not available" << endl
;
3196 if (g_slog
== nullptr) {
3197 g_slog
= Logging::Logger::create(loggerBackend
);
3200 // Missing: a mechanism to call setVerbosity(x)
3201 auto startupLog
= g_slog
->withName("config");
3202 g_slogtcpin
= g_slog
->withName("in")->withValues("proto", Logging::Loggable("tcp"));
3203 g_slogudpin
= g_slog
->withName("in")->withValues("proto", Logging::Loggable("udp"));
3204 g_slogout
= g_slog
->withName("out");
3206 ::arg().setSLog(startupLog
);
3208 bool mustExit
= false;
3209 std::tie(ret
, mustExit
) = doConfig(startupLog
, configname
, argc
, argv
);
3210 if (ret
!= 0 || mustExit
) {
3214 if (!::arg().file(configname
.c_str())) {
3215 SLOG(g_log
<< Logger::Warning
<< "Unable to open configuration file '" << configname
<< "'" << endl
,
3216 startupLog
->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname
)));
3219 // Reparse, now with config file as well
3220 ::arg().parse(argc
, argv
);
3222 g_quiet
= ::arg().mustDo("quiet");
3223 s_logUrgency
= (Logger::Urgency
)::arg().asNum("loglevel");
3224 g_slogStructured
= ::arg().mustDo("structured-logging");
3226 if (s_logUrgency
< Logger::Error
) {
3227 s_logUrgency
= Logger::Error
;
3229 if (!g_quiet
&& s_logUrgency
< Logger::Info
) { // Logger::Info=6, Logger::Debug=7
3230 s_logUrgency
= Logger::Info
; // if you do --quiet=no, you need Info to also see the query log
3232 g_log
.setLoglevel(s_logUrgency
);
3233 g_log
.toConsole(s_logUrgency
);
3235 if (!::arg()["chroot"].empty() && !::arg()["api-config-dir"].empty()) {
3236 SLOG(g_log
<< Logger::Error
<< "Using chroot and enabling the API is not possible" << endl
,
3237 startupLog
->info(Logr::Error
, "Cannot use chroot and enable the API at the same time"));
3238 return EXIT_FAILURE
;
3241 if (::arg()["socket-dir"].empty()) {
3242 if (::arg()["chroot"].empty()) {
3243 ::arg().set("socket-dir") = std::string(LOCALSTATEDIR
) + "/pdns-recursor";
3246 ::arg().set("socket-dir") = "/";
3250 if (::arg().asNum("threads") == 1) {
3251 if (::arg().mustDo("pdns-distributes-queries")) {
3252 SLOG(g_log
<< Logger::Warning
<< "Asked to run with pdns-distributes-queries set but no distributor threads, raising to 1" << endl
,
3253 startupLog
->v(1)->info("Only one thread, no need to distribute queries ourselves"));
3254 ::arg().set("pdns-distributes-queries") = "no";
3258 if (::arg().mustDo("pdns-distributes-queries") && ::arg().asNum("distributor-threads") <= 0) {
3259 SLOG(g_log
<< Logger::Warning
<< "Asked to run with pdns-distributes-queries set but no distributor threads, raising to 1" << endl
,
3260 startupLog
->v(1)->info("Asked to run with pdns-distributes-queries set but no distributor threads, raising to 1"));
3261 ::arg().set("distributor-threads") = "1";
3264 if (!::arg().mustDo("pdns-distributes-queries")) {
3265 ::arg().set("distributor-threads") = "0";
3268 g_recCache
= std::make_unique
<MemRecursorCache
>(::arg().asNum("record-cache-shards"));
3269 g_negCache
= std::make_unique
<NegCache
>(::arg().asNum("record-cache-shards") / 8);
3270 if (!::arg().mustDo("disable-packetcache")) {
3271 g_maxPacketCacheEntries
= ::arg().asNum("max-packetcache-entries");
3272 g_packetCache
= std::make_unique
<RecursorPacketCache
>(g_maxPacketCacheEntries
, ::arg().asNum("packetcache-shards"));
3275 ret
= serviceMain(startupLog
);
3277 catch (const PDNSException
& ae
) {
3278 SLOG(g_log
<< Logger::Error
<< "Exception: " << ae
.reason
<< endl
,
3279 g_slog
->withName("config")->error(Logr::Critical
, ae
.reason
, "Fatal error", "exception", Logging::Loggable("PDNSException")));
3282 catch (const std::exception
& e
) {
3283 SLOG(g_log
<< Logger::Error
<< "STL Exception: " << e
.what() << endl
,
3284 g_slog
->withName("config")->error(Logr::Critical
, e
.what(), "Fatal error", "exception", Logging::Loggable("std::exception")));
3288 SLOG(g_log
<< Logger::Error
<< "any other exception in main: " << endl
,
3289 g_slog
->withName("config")->info(Logr::Critical
, "Fatal error"));
3296 static RecursorControlChannel::Answer
* doReloadLuaScript()
3298 string fname
= ::arg()["lua-dns-script"];
3299 auto log
= g_slog
->withName("runtime")->withValues("name", Logging::Loggable(fname
));
3301 if (fname
.empty()) {
3303 SLOG(g_log
<< Logger::Info
<< RecThreadInfo::id() << " Unloaded current lua script" << endl
,
3304 log
->info(Logr::Info
, "Unloaded current lua script"));
3305 return new RecursorControlChannel::Answer
{0, string("unloaded\n")};
3308 t_pdl
= std::make_shared
<RecursorLua4
>();
3310 t_pdl
->loadFile(fname
);
3312 catch (std::runtime_error
& ex
) {
3313 string msg
= std::to_string(RecThreadInfo::id()) + " Retaining current script, could not read '" + fname
+ "': " + ex
.what();
3314 SLOG(g_log
<< Logger::Error
<< msg
<< endl
,
3315 log
->error(Logr::Error
, ex
.what(), "Retaining current script, could not read new script"));
3316 return new RecursorControlChannel::Answer
{1, msg
+ "\n"};
3319 catch (std::exception
& e
) {
3320 SLOG(g_log
<< Logger::Error
<< RecThreadInfo::id() << " Retaining current script, error from '" << fname
<< "': " << e
.what() << endl
,
3321 log
->error(Logr::Error
, e
.what(), "Retaining current script, error in new script"));
3322 return new RecursorControlChannel::Answer
{1, string("retaining current script, error from '" + fname
+ "': " + e
.what() + "\n")};
3325 SLOG(g_log
<< Logger::Warning
<< RecThreadInfo::id() << " (Re)loaded lua script from '" << fname
<< "'" << endl
,
3326 log
->info(Logr::Warning
, "(Re)loaded lua script"));
3327 return new RecursorControlChannel::Answer
{0, string("(re)loaded '" + fname
+ "'\n")};
3330 RecursorControlChannel::Answer
doQueueReloadLuaScript(vector
<string
>::const_iterator begin
, vector
<string
>::const_iterator end
)
3333 ::arg().set("lua-dns-script") = *begin
;
3336 return broadcastAccFunction
<RecursorControlChannel::Answer
>(doReloadLuaScript
);
3339 static string
* pleaseUseNewTraceRegex(const std::string
& newRegex
, int file
)
3342 if (newRegex
.empty()) {
3343 t_traceRegex
.reset();
3344 t_tracefd
= FDWrapper();
3345 return new string("unset\n");
3348 return new string("could not dup file\n");
3350 t_traceRegex
= std::make_shared
<Regex
>(newRegex
);
3352 return new string("ok\n");
3354 catch (const PDNSException
& ae
) {
3355 return new string(ae
.reason
+ "\n");
3359 string
doTraceRegex(FDWrapper file
, vector
<string
>::const_iterator begin
, vector
<string
>::const_iterator end
)
3361 int fileno
= dup(file
);
3362 // Potential dup failure handled in pleaseUseNewTraceRegex()
3363 return broadcastAccFunction
<string
>([=] { return pleaseUseNewTraceRegex(begin
!= end
? *begin
: "", fileno
); });
3366 struct WipeCacheResult
wipeCaches(const DNSName
& canon
, bool subtree
, uint16_t qtype
)
3368 struct WipeCacheResult res
;
3371 res
.record_count
= static_cast<int>(g_recCache
->doWipeCache(canon
, subtree
, qtype
));
3372 // scanbuild complains here about an allocated function object that is being leaked. Needs investigation
3373 if (g_packetCache
) {
3374 res
.packet_count
= static_cast<int>(g_packetCache
->doWipePacketCache(canon
, qtype
, subtree
));
3376 res
.negative_record_count
= static_cast<int>(g_negCache
->wipe(canon
, subtree
));
3377 if (g_aggressiveNSECCache
) {
3378 g_aggressiveNSECCache
->removeZoneInfo(canon
, subtree
);
3381 catch (const std::exception
& e
) {
3382 auto log
= g_slog
->withName("runtime");
3383 SLOG(g_log
<< Logger::Warning
<< ", failed: " << e
.what() << endl
,
3384 log
->error(Logr::Warning
, e
.what(), "Wipecache failed"));