]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/recursordist/rec-main.cc
f46d32eab1c66527f98ef6d301a52ab566e2b316
[thirdparty/pdns.git] / pdns / recursordist / rec-main.cc
1 /*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
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.
8 *
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.
12 *
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.
17 *
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.
21 */
22 #include <sys/stat.h>
23
24 #include "rec-main.hh"
25
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"
32 #include "version.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"
40 #include "logging.hh"
41 #include "dnsseckeeper.hh"
42
43 #ifdef NOD_ENABLED
44 #include "nod.hh"
45 #endif /* NOD_ENABLED */
46
47 #ifdef HAVE_LIBSODIUM
48 #include <sodium.h>
49
50 #include <cstddef>
51 #include <utility>
52 #endif
53
54 #ifdef HAVE_SYSTEMD
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>
59 #endif
60
61 #ifdef HAVE_FSTRM
62 thread_local FrameStreamServersInfo t_frameStreamServersInfo;
63 thread_local FrameStreamServersInfo t_nodFrameStreamServersInfo;
64 #endif /* HAVE_FSTRM */
65
66 string g_programname = "pdns_recursor";
67 string g_pidfname;
68 RecursorControlChannel g_rcc; // only active in the handler thread
69 bool g_regressionTestMode;
70
71 #ifdef NOD_ENABLED
72 bool g_nodEnabled;
73 DNSName g_nodLookupDomain;
74 bool g_nodLog;
75 SuffixMatchNode g_nodDomainWL;
76 std::string g_nod_pbtag;
77 bool g_udrEnabled;
78 bool g_udrLog;
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 */
83
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;
97 int g_argc;
98 char** g_argv;
99 static string s_structured_logger_backend;
100 static Logger::Urgency s_logUrgency;
101
102 std::shared_ptr<Logr::Logger> g_slogtcpin;
103 std::shared_ptr<Logr::Logger> g_slogudpin;
104 std::shared_ptr<Logr::Logger> g_slogudpout;
105
106 /* without reuseport, all listeners share the same sockets */
107 deferredAdd_t g_deferredAdds;
108
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;
114
115 std::unique_ptr<ProxyMapping> g_proxyMapping; // new threads needs this to be setup
116 thread_local std::unique_ptr<ProxyMapping> t_proxyMapping;
117
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;
122
123 static std::map<unsigned int, std::set<int>> parseCPUMap(Logr::log_t log)
124 {
125 std::map<unsigned int, std::set<int>> result;
126
127 const std::string value = ::arg()["cpu-map"];
128
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"));
132 return result;
133 }
134
135 std::vector<std::string> parts;
136
137 stringtok(parts, value, " \t");
138
139 for (const auto& part : parts) {
140 if (part.find('=') == string::npos) {
141 continue;
142 }
143
144 try {
145 auto headers = splitField(part, '=');
146 boost::trim(headers.first);
147 boost::trim(headers.second);
148
149 auto threadId = pdns::checked_stoi<unsigned int>(headers.first);
150 std::vector<std::string> cpus;
151
152 stringtok(cpus, headers.second, ",");
153
154 for (const auto& cpu : cpus) {
155 int cpuId = std::stoi(cpu);
156
157 result[threadId].insert(cpuId);
158 }
159 }
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)));
163 }
164 }
165
166 return result;
167 }
168
169 static void setCPUMap(const std::map<unsigned int, std::set<int>>& cpusMap, unsigned int n, pthread_t tid, Logr::log_t log)
170 {
171 const auto& cpuMapping = cpusMap.find(n);
172 if (cpuMapping == cpusMap.cend()) {
173 return;
174 }
175 int ret = mapThreadToCPUList(tid, cpuMapping->second);
176 if (ret == 0) {
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;
181 }
182 g_log << Logger::Info << endl;
183 }
184 else {
185 log->info(Logr::Info, "CPU affinity has been set", "thread", Logging::Loggable(n), "cpumap", Logging::IterLoggable(cpuMapping->second.begin(), cpuMapping->second.end()));
186 }
187 }
188 else {
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;
193 }
194 g_log << Logger::Info << ' ' << stringerror(ret) << endl;
195 }
196 else {
197 log->error(Logr::Warning, ret, "Error setting CPU affinity", "thread", Logging::Loggable(n), "cpumap", Logging::IterLoggable(cpuMapping->second.begin(), cpuMapping->second.end()));
198 }
199 }
200 }
201
202 static void recursorThread();
203
204 void RecThreadInfo::start(unsigned int tid, const string& tname, const std::map<unsigned int, std::set<int>>& cpusMap, Logr::log_t log)
205 {
206 name = tname;
207 thread = std::thread([tid, tname] {
208 t_id = tid;
209 const string threadPrefix = "rec/";
210 setThreadName(threadPrefix + tname);
211 recursorThread();
212 });
213 setCPUMap(cpusMap, tid, thread.native_handle(), log);
214 }
215
216 int RecThreadInfo::runThreads(Logr::log_t log)
217 {
218 int ret = EXIT_SUCCESS;
219 unsigned int currentThreadId = 1;
220 const auto cpusMap = parseCPUMap(log);
221
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"));
225
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);
233
234 auto& info = RecThreadInfo::info(currentThreadId);
235 info.setListener();
236 info.setWorker();
237 RecThreadInfo::setThreadId(currentThreadId++);
238 recursorThread();
239
240 handlerInfo.thread.join();
241 if (handlerInfo.exitCode != 0) {
242 ret = handlerInfo.exitCode;
243 }
244 taskInfo.thread.join();
245 if (taskInfo.exitCode != 0) {
246 ret = taskInfo.exitCode;
247 }
248 }
249 else {
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();
255 }
256 }
257 for (unsigned int thread = 0; thread < RecThreadInfo::numWorkers(); ++thread) {
258 auto& info = RecThreadInfo::info(tmp++);
259 info.setListener(!RecThreadInfo::weDistributeQueries());
260 info.setWorker();
261 }
262 for (unsigned int thread = 0; thread < RecThreadInfo::numTaskThreads(); ++thread) {
263 auto& info = RecThreadInfo::info(tmp++);
264 info.setTaskThread();
265 }
266
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);
274 }
275 }
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())));
278
279 for (unsigned int thread = 0; thread < RecThreadInfo::numWorkers(); ++thread) {
280 auto& info = RecThreadInfo::info(currentThreadId);
281 info.start(currentThreadId++, "worker", cpusMap, log);
282 }
283
284 for (unsigned int thread = 0; thread < RecThreadInfo::numTaskThreads(); ++thread) {
285 auto& info = RecThreadInfo::info(currentThreadId);
286 info.start(currentThreadId++, "task", cpusMap, log);
287 }
288
289 /* This thread handles the web server, carbon, statistics and the control channel */
290 auto& info = RecThreadInfo::info(0);
291 info.setHandler();
292 info.start(0, "web+stat", cpusMap, log);
293
294 for (auto& tInfo : RecThreadInfo::infos()) {
295 tInfo.thread.join();
296 if (tInfo.exitCode != 0) {
297 ret = tInfo.exitCode;
298 }
299 }
300 }
301 return ret;
302 }
303
304 void RecThreadInfo::makeThreadPipes(Logr::log_t log)
305 {
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)));
310 }
311
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);
315
316 std::array<int, 2> fileDesc{};
317 if (pipe(fileDesc.data()) < 0) {
318 unixDie("Creating pipe for inter-thread communications");
319 }
320
321 threadInfo.pipes.readToThread = fileDesc[0];
322 threadInfo.pipes.writeToThread = fileDesc[1];
323
324 // handler thread only gets first pipe, not the others
325 if (thread == 0) {
326 continue;
327 }
328
329 if (pipe(fileDesc.data()) < 0) {
330 unixDie("Creating pipe for inter-thread communications");
331 }
332
333 threadInfo.pipes.readFromThread = fileDesc[0];
334 threadInfo.pipes.writeFromThread = fileDesc[1];
335
336 if (pipe(fileDesc.data()) < 0) {
337 unixDie("Creating pipe for inter-thread communications");
338 }
339
340 threadInfo.pipes.readQueriesToThread = fileDesc[0];
341 threadInfo.pipes.writeQueriesToThread = fileDesc[1];
342
343 if (pipeBufferSize > 0) {
344 if (!setPipeBufferSize(threadInfo.pipes.writeQueriesToThread, pipeBufferSize)) {
345 int err = errno;
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)));
352 }
353 }
354 }
355
356 if (!setNonBlocking(threadInfo.pipes.writeQueriesToThread)) {
357 unixDie("Making pipe for inter-thread communications non-blocking");
358 }
359 }
360 }
361
362 ArgvMap& arg()
363 {
364 static ArgvMap theArg;
365 return theArg;
366 }
367
368 static FDMultiplexer* getMultiplexer(Logr::log_t log)
369 {
370 FDMultiplexer* ret = nullptr;
371 for (const auto& mplexer : FDMultiplexer::getMultiplexerMap()) {
372 try {
373 ret = mplexer.second(FDMultiplexer::s_maxevents);
374 return ret;
375 }
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"));
379 }
380 catch (...) {
381 SLOG(g_log << Logger::Warning << "Non-fatal error initializing possible multiplexer" << endl,
382 log->info(Logr::Warning, "Non-fatal error initializing possible multiplexer"));
383 }
384 }
385 SLOG(g_log << Logger::Error << "No working multiplexer found!" << endl,
386 log->info(Logr::Error, "No working multiplexer found!"));
387 _exit(1);
388 }
389
390 static std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>> startProtobufServers(const ProtobufExportConfig& config, Logr::log_t log)
391 {
392 auto result = std::make_shared<std::vector<std::unique_ptr<RemoteLogger>>>();
393
394 for (const auto& server : config.servers) {
395 try {
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));
400 }
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)));
404 }
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)));
408 }
409 }
410
411 return result;
412 }
413
414 bool checkProtobufExport(LocalStateHolder<LuaConfigItems>& luaconfsLocal)
415 {
416 if (!luaconfsLocal->protobufExportConfig.enabled) {
417 if (t_protobufServers.servers) {
418 t_protobufServers.servers.reset();
419 t_protobufServers.config = luaconfsLocal->protobufExportConfig;
420 }
421
422 return false;
423 }
424
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) {
428
429 if (t_protobufServers.servers) {
430 t_protobufServers.servers.reset();
431 }
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;
436 }
437
438 return true;
439 }
440
441 bool checkOutgoingProtobufExport(LocalStateHolder<LuaConfigItems>& luaconfsLocal)
442 {
443 if (!luaconfsLocal->outgoingProtobufExportConfig.enabled) {
444 if (t_outgoingProtobufServers.servers) {
445 t_outgoingProtobufServers.servers.reset();
446 t_outgoingProtobufServers.config = luaconfsLocal->outgoingProtobufExportConfig;
447 }
448
449 return false;
450 }
451
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) {
455
456 if (t_outgoingProtobufServers.servers) {
457 t_outgoingProtobufServers.servers.reset();
458 }
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;
463 }
464
465 return true;
466 }
467
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)
469 {
470 auto log = g_slog->withName("pblq");
471
472 if (!t_protobufServers.servers) {
473 return;
474 }
475
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());
481 }
482 else {
483 Netmask requestorNM(mappedSource, mappedSource.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
484 requestor = requestorNM.getMaskedNetwork();
485 requestor.setPort(mappedSource.getPort());
486 }
487
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);
496
497 if (!policyTags.empty()) {
498 msg.addPolicyTags(policyTags);
499 }
500 for (const auto& mit : meta) {
501 msg.setMeta(mit.first, mit.second.stringVal, mit.second.intVal);
502 }
503
504 std::string strMsg(msg.finishAndMoveBuf());
505 for (auto& server : *t_protobufServers.servers) {
506 remoteLoggerQueueData(*server, strMsg);
507 }
508 }
509
510 void protobufLogResponse(pdns::ProtoZero::RecMessage& message)
511 {
512 if (!t_protobufServers.servers) {
513 return;
514 }
515
516 std::string msg(message.finishAndMoveBuf());
517 for (auto& server : *t_protobufServers.servers) {
518 remoteLoggerQueueData(*server, msg);
519 }
520 }
521
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)
530 {
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
534 if (!pbData) {
535 pbMessage.setType(pdns::ProtoZero::Message::MessageType::DNSResponseType);
536 pbMessage.setServerIdentity(SyncRes::s_serverID);
537 }
538
539 // In response part
540 if (g_useKernelTimestamp && tval.tv_sec != 0) {
541 pbMessage.setQueryTime(tval.tv_sec, tval.tv_usec);
542 }
543 else {
544 pbMessage.setQueryTime(g_now.tv_sec, g_now.tv_usec);
545 }
546
547 // In message part
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());
553 }
554 else {
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());
559 }
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);
564
565 pbMessage.setTime();
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);
573 }
574 #ifdef NOD_ENABLED
575 if (g_nodEnabled) {
576 pbMessage.setNewlyObservedDomain(false);
577 }
578 #endif
579 if (eventTrace.enabled() && (SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_pb) != 0) {
580 pbMessage.addEvents(eventTrace);
581 }
582 protobufLogResponse(pbMessage);
583 }
584
585 #ifdef HAVE_FSTRM
586
587 static std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>> startFrameStreamServers(const FrameStreamExportConfig& config, Logr::log_t log)
588 {
589 auto result = std::make_shared<std::vector<std::unique_ptr<FrameStreamLogger>>>();
590
591 for (const auto& server : config.servers) {
592 try {
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;
601 try {
602 ComboAddress address(server);
603 fsl = make_unique<FrameStreamLogger>(address.sin4.sin_family, address.toStringWithPort(), true, options);
604 }
605 catch (const PDNSException& e) {
606 fsl = make_unique<FrameStreamLogger>(AF_UNIX, server, true, options);
607 }
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));
613 }
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)));
617 }
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)));
621 }
622 }
623
624 return result;
625 }
626
627 static void asyncFrameStreamLoggersCleanup(std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>&& servers)
628 {
629 auto thread = std::thread([&] {
630 servers.reset();
631 });
632 thread.detach();
633 }
634
635 bool checkFrameStreamExport(LocalStateHolder<LuaConfigItems>& luaconfsLocal, const FrameStreamExportConfig& config, FrameStreamServersInfo& serverInfos)
636 {
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;
642 }
643
644 return false;
645 }
646
647 /* if the server was not running, or if it was running according to a previous
648 * configuration
649 */
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));
654 }
655
656 auto dnsTapLog = g_slog->withName("dnstap");
657 serverInfos.servers = startFrameStreamServers(config, dnsTapLog);
658 serverInfos.config = config;
659 serverInfos.generation = luaconfsLocal->generation;
660 }
661
662 return true;
663 }
664
665 #endif /* HAVE_FSTRM */
666
667 static void makeControlChannelSocket(int processNum = -1)
668 {
669 string sockname = ::arg()["socket-dir"] + "/" + g_programname;
670 if (processNum >= 0) {
671 sockname += "." + std::to_string(processNum);
672 }
673 sockname += ".controlsocket";
674 g_rcc.listen(sockname);
675
676 uid_t sockowner = -1;
677 gid_t sockgroup = -1;
678
679 if (!::arg().isEmpty("socket-group")) {
680 sockgroup = ::arg().asGid("socket-group");
681 }
682 if (!::arg().isEmpty("socket-owner")) {
683 sockowner = ::arg().asUid("socket-owner");
684 }
685
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");
689 }
690 }
691
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");
697 }
698 }
699 }
700
701 static void writePid(Logr::log_t log)
702 {
703 if (!::arg().mustDo("write-pid")) {
704 return;
705 }
706 ofstream ostr(g_pidfname.c_str(), std::ios_base::app);
707 if (ostr) {
708 ostr << Utility::getpid() << endl;
709 }
710 else {
711 int err = errno;
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)));
714 }
715 }
716
717 static void checkSocketDir(Logr::log_t log)
718 {
719 string dir(::arg()["socket-dir"]);
720 string msg;
721
722 struct stat dirStat = {};
723 if (stat(dir.c_str(), &dirStat) == -1) {
724 msg = "it does not exist or cannot access";
725 }
726 else if (!S_ISDIR(dirStat.st_mode)) {
727 msg = "it is not a directory";
728 }
729 else if (access(dir.c_str(), R_OK | W_OK | X_OK) != 0) {
730 msg = "cannot read, write or search";
731 }
732 else {
733 return;
734 }
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)));
738 _exit(1);
739 }
740
741 #ifdef NOD_ENABLED
742 static void setupNODThread(Logr::log_t log)
743 {
744 if (g_nodEnabled) {
745 uint32_t num_cells = ::arg().asNum("new-domain-db-size");
746 t_nodDBp = std::make_shared<nod::NODDB>(num_cells);
747 try {
748 t_nodDBp->setCacheDir(::arg()["new-domain-history-dir"]);
749 }
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"])));
753 _exit(1);
754 }
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"));
758 _exit(1);
759 }
760 std::thread thread(nod::NODDB::startHousekeepingThread, t_nodDBp, std::this_thread::get_id());
761 thread.detach();
762 }
763 if (g_udrEnabled) {
764 uint32_t num_cells = ::arg().asNum("unique-response-db-size");
765 t_udrDBp = std::make_shared<nod::UniqueResponseDB>(num_cells);
766 try {
767 t_udrDBp->setCacheDir(::arg()["unique-response-history-dir"]);
768 }
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"])));
772 _exit(1);
773 }
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"));
777 _exit(1);
778 }
779 std::thread thread(nod::UniqueResponseDB::startHousekeepingThread, t_udrDBp, std::this_thread::get_id());
780 thread.detach();
781 }
782 }
783
784 static void parseNODIgnorelist(const std::string& wlist)
785 {
786 vector<string> parts;
787 stringtok(parts, wlist, ",; ");
788 for (const auto& part : parts) {
789 g_nodDomainWL.add(DNSName(part));
790 }
791 }
792
793 static void setupNODGlobal()
794 {
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"]);
801
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"];
807 }
808 #endif /* NOD_ENABLED */
809
810 static void daemonize(Logr::log_t log)
811 {
812 if (auto pid = fork(); pid != 0) {
813 if (pid < 0) {
814 int err = errno;
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)
818 }
819 exit(0); // NOLINT(concurrency-mt-unsafe)
820 }
821
822 setsid();
823
824 int devNull = open("/dev/null", O_RDWR); /* open stdin */
825 if (devNull < 0) {
826 int err = errno;
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"));
829 }
830 else {
831 dup2(devNull, 0); /* stdin */
832 dup2(devNull, 1); /* stderr */
833 dup2(devNull, 2); /* stderr */
834 close(devNull);
835 }
836 }
837
838 static void termIntHandler([[maybe_unused]] int arg)
839 {
840 doExit();
841 }
842
843 static void usr1Handler([[maybe_unused]] int arg)
844 {
845 statsWanted = true;
846 }
847
848 static void usr2Handler([[maybe_unused]] int arg)
849 {
850 g_quiet = !g_quiet;
851 SyncRes::setDefaultLogMode(g_quiet ? SyncRes::LogNone : SyncRes::Log);
852 ::arg().set("quiet") = g_quiet ? "" : "no";
853 }
854
855 static void checkLinuxIPv6Limits([[maybe_unused]] Logr::log_t log)
856 {
857 #ifdef __linux__
858 string line;
859 if (readFileIfThere("/proc/sys/net/ipv6/route/max_size", &line)) {
860 int lim = std::stoi(line);
861 if (lim < 16384) {
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)));
864 }
865 }
866 #endif
867 }
868
869 static void checkOrFixFDS(Logr::log_t log)
870 {
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;
874
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)));
881 }
882 else {
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);
888 }
889 }
890 }
891
892 // static std::string s_timestampFormat = "%m-%dT%H:%M:%S";
893 static std::string s_timestampFormat = "%s";
894
895 static const char* toTimestampStringMilli(const struct timeval& tval, std::array<char, 64>& buf)
896 {
897 size_t len = 0;
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
903 {
904 };
905 len = strftime(buf.data(), buf.size(), s_timestampFormat.c_str(), localtime_r(&tval.tv_sec, &theTime));
906 }
907 if (len == 0) {
908 len = snprintf(buf.data(), buf.size(), "%lld", static_cast<long long>(tval.tv_sec));
909 }
910
911 snprintf(&buf.at(len), buf.size() - len, ".%03ld", static_cast<long>(tval.tv_usec) / 1000);
912 return buf.data();
913 }
914
915 #ifdef HAVE_SYSTEMD
916 static void loggerSDBackend(const Logging::Entry& entry)
917 {
918 static const set<std::string, CIStringComparePOSIX> special = {
919 "message",
920 "message_id",
921 "priority",
922 "code_file",
923 "code_line",
924 "code_func",
925 "errno",
926 "invocation_id",
927 "user_invocation_id",
928 "syslog_facility",
929 "syslog_identifier",
930 "syslog_pid",
931 "syslog_timestamp",
932 "syslog_raw",
933 "documentation",
934 "tid",
935 "unit",
936 "user_unit",
937 "object_pid"};
938
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.
944 return;
945 }
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);
950 };
951 appendKeyAndVal("MESSAGE", entry.message);
952 if (entry.error) {
953 appendKeyAndVal("ERROR", entry.error.get());
954 }
955 appendKeyAndVal("LEVEL", std::to_string(entry.level));
956 appendKeyAndVal("PRIORITY", std::to_string(entry.d_priority));
957 if (entry.name) {
958 appendKeyAndVal("SUBSYSTEM", entry.name.get());
959 }
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) {
964 string key{"PDNS"};
965 key.append(value.first);
966 appendKeyAndVal(toUpper(key), value.second);
967 }
968 else {
969 appendKeyAndVal(toUpper(value.first), value.second);
970 }
971 }
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()));
975
976 vector<iovec> iov;
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
981 }
982 sd_journal_sendv(iov.data(), static_cast<int>(iov.size()));
983 }
984 #endif
985
986 static void loggerBackend(const Logging::Entry& entry)
987 {
988 static thread_local std::stringstream buf;
989
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.
995 return;
996 }
997 buf.str("");
998 buf << "msg=" << std::quoted(entry.message);
999 if (entry.error) {
1000 buf << " error=" << std::quoted(entry.error.get());
1001 }
1002
1003 if (entry.name) {
1004 buf << " subsystem=" << std::quoted(entry.name.get());
1005 }
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));
1009 }
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) {
1016 buf << " ";
1017 buf << value.first << "=" << std::quoted(value.second);
1018 }
1019
1020 g_log << urg << buf.str() << endl;
1021 }
1022
1023 static int ratePercentage(uint64_t nom, uint64_t denom)
1024 {
1025 if (denom == 0) {
1026 return 0;
1027 }
1028 return static_cast<int>(round(100.0 * static_cast<double>(nom) / static_cast<double>(denom)));
1029 }
1030
1031 static void doStats()
1032 {
1033 static time_t lastOutputTime;
1034 static uint64_t lastQueryCount;
1035
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;
1049
1050 auto log = g_slog->withName("stats");
1051
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;
1064
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()
1072 << endl;
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;
1076
1077 g_log << Logger::Notice << "stats: " << pcSize << " packet cache entries, " << ratePercentage(pcHits, qcounter) << "% packet cache hits" << endl;
1078
1079 g_log << Logger::Notice << "stats: tasks pushed/expired/queuesize: " << taskPushes << '/' << taskExpired << '/' << taskSize << endl;
1080 }
1081 else {
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));
1115 }
1116 size_t idx = 0;
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)));
1121 ++idx;
1122 }
1123 }
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)));
1129 }
1130 lastOutputTime = now;
1131 lastQueryCount = qcounter;
1132 }
1133 else if (statsWanted) {
1134 SLOG(g_log << Logger::Notice << "stats: no stats yet!" << endl,
1135 log->info(Logr::Notice, "No stats yet"));
1136 }
1137
1138 statsWanted = false;
1139 }
1140
1141 static std::shared_ptr<NetmaskGroup> parseACL(const std::string& aclFile, const std::string& aclSetting, Logr::log_t log)
1142 {
1143 auto result = std::make_shared<NetmaskGroup>();
1144
1145 if (!::arg()[aclFile].empty()) {
1146 string line;
1147 ifstream ifs(::arg()[aclFile].c_str());
1148 if (!ifs) {
1149 throw runtime_error("Could not open '" + ::arg()[aclFile] + "': " + stringerror());
1150 }
1151
1152 while (getline(ifs, line)) {
1153 auto pos = line.find('#');
1154 if (pos != string::npos) {
1155 line.resize(pos);
1156 }
1157 boost::trim(line);
1158 if (line.empty()) {
1159 continue;
1160 }
1161
1162 result->addMask(line);
1163 }
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])));
1167 }
1168 else if (!::arg()[aclSetting].empty()) {
1169 vector<string> ips;
1170 stringtok(ips, ::arg()[aclSetting], ", ");
1171
1172 for (const auto& address : ips) {
1173 result->addMask(address);
1174 }
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 << ", ";
1180 }
1181 g_log << Logger::Info << *i;
1182 }
1183 g_log << Logger::Info << endl;
1184 }
1185 else {
1186 log->info(Logr::Info, "Setting access control", "acl", Logging::Loggable(aclSetting), "addresses", Logging::IterLoggable(ips.begin(), ips.end()));
1187 }
1188 }
1189
1190 return result;
1191 }
1192
1193 static void* pleaseSupplantAllowFrom(std::shared_ptr<NetmaskGroup> nmgroup)
1194 {
1195 t_allowFrom = std::move(nmgroup);
1196 return nullptr;
1197 }
1198
1199 static void* pleaseSupplantAllowNotifyFrom(std::shared_ptr<NetmaskGroup> nmgroup)
1200 {
1201 t_allowNotifyFrom = std::move(nmgroup);
1202 return nullptr;
1203 }
1204
1205 void* pleaseSupplantAllowNotifyFor(std::shared_ptr<notifyset_t> allowNotifyFor)
1206 {
1207 t_allowNotifyFor = std::move(allowNotifyFor);
1208 return nullptr;
1209 }
1210
1211 void parseACLs()
1212 {
1213 auto log = g_slog->withName("config");
1214
1215 static bool l_initialized;
1216
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";
1221 }
1222 cleanSlashes(configName);
1223
1224 if (!::arg().preParseFile(configName.c_str(), "allow-from-file")) {
1225 throw runtime_error("Unable to re-parse configuration file '" + configName + "'");
1226 }
1227 ::arg().preParseFile(configName.c_str(), "allow-from", LOCAL_NETS);
1228
1229 if (!::arg().preParseFile(configName.c_str(), "allow-notify-from-file")) {
1230 throw runtime_error("Unable to re-parse configuration file '" + configName + "'");
1231 }
1232 ::arg().preParseFile(configName.c_str(), "allow-notify-from");
1233
1234 ::arg().preParseFile(configName.c_str(), "include-dir");
1235 ::arg().preParse(g_argc, g_argv, "include-dir");
1236
1237 // then process includes
1238 std::vector<std::string> extraConfigs;
1239 ::arg().gatherIncludes(extraConfigs);
1240
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 + "'");
1244 }
1245 if (!::arg().preParseFile(fileName.c_str(), "allow-from", ::arg()["allow-from"])) {
1246 throw runtime_error("Unable to re-parse configuration file include '" + fileName + "'");
1247 }
1248
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 + "'");
1251 }
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 + "'");
1254 }
1255 }
1256
1257 ::arg().preParse(g_argc, g_argv, "allow-from-file");
1258 ::arg().preParse(g_argc, g_argv, "allow-from");
1259
1260 ::arg().preParse(g_argc, g_argv, "allow-notify-from-file");
1261 ::arg().preParse(g_argc, g_argv, "allow-notify-from");
1262 }
1263
1264 auto allowFrom = parseACL("allow-from-file", "allow-from", log);
1265
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!"));
1270 }
1271 allowFrom = nullptr;
1272 }
1273
1274 g_initialAllowFrom = allowFrom;
1275 broadcastFunction([=] { return pleaseSupplantAllowFrom(allowFrom); });
1276
1277 auto allowNotifyFrom = parseACL("allow-notify-from-file", "allow-notify-from", log);
1278
1279 g_initialAllowNotifyFrom = allowNotifyFrom;
1280 broadcastFunction([=] { return pleaseSupplantAllowNotifyFrom(allowNotifyFrom); });
1281
1282 l_initialized = true;
1283 }
1284
1285 void broadcastFunction(const pipefunc_t& func)
1286 {
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. */
1290
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
1295 handle that case.
1296 */
1297 func();
1298 }
1299
1300 unsigned int thread = 0;
1301 for (const auto& threadInfo : RecThreadInfo::infos()) {
1302 if (thread++ == RecThreadInfo::id()) {
1303 func(); // don't write to ourselves!
1304 continue;
1305 }
1306
1307 ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: manual ownership handling
1308 tmsg->func = func;
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
1312
1313 unixDie("write to thread pipe returned wrong size or error");
1314 }
1315
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");
1319 }
1320
1321 if (resp != nullptr) {
1322 delete resp; // NOLINT: manual ownership handling
1323 resp = nullptr;
1324 }
1325 // coverity[leaked_storage]
1326 }
1327 }
1328
1329 template <class T>
1330 void* voider(const std::function<T*()>& func)
1331 {
1332 return func();
1333 }
1334
1335 static vector<ComboAddress>& operator+=(vector<ComboAddress>& lhs, const vector<ComboAddress>& rhs)
1336 {
1337 lhs.insert(lhs.end(), rhs.begin(), rhs.end());
1338 return lhs;
1339 }
1340
1341 static vector<pair<DNSName, uint16_t>>& operator+=(vector<pair<DNSName, uint16_t>>& lhs, const vector<pair<DNSName, uint16_t>>& rhs)
1342 {
1343 lhs.insert(lhs.end(), rhs.begin(), rhs.end());
1344 return lhs;
1345 }
1346
1347 static ProxyMappingStats_t& operator+=(ProxyMappingStats_t& lhs, const ProxyMappingStats_t& rhs)
1348 {
1349 for (const auto& [key, entry] : rhs) {
1350 lhs[key].netmaskMatches += entry.netmaskMatches;
1351 lhs[key].suffixMatches += entry.suffixMatches;
1352 }
1353 return lhs;
1354 }
1355
1356 static RemoteLoggerStats_t& operator+=(RemoteLoggerStats_t& lhs, const RemoteLoggerStats_t& rhs)
1357 {
1358 for (const auto& [key, entry] : rhs) {
1359 lhs[key] += entry;
1360 }
1361 return lhs;
1362 }
1363
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
1367 // metrics.
1368 // Note that this currently skips the handler, but includes the taskThread(s).
1369 template <class T>
1370 T broadcastAccFunction(const std::function<T*()>& func)
1371 {
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
1375 _exit(1);
1376 }
1377
1378 unsigned int thread = 0;
1379 T ret = T();
1380 for (const auto& threadInfo : RecThreadInfo::infos()) {
1381 if (thread++ == RecThreadInfo::id()) {
1382 continue;
1383 }
1384
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;
1389
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");
1393 }
1394
1395 T* resp = nullptr;
1396 if (read(tps.readFromThread, &resp, sizeof(resp)) != sizeof(resp)) // NOLINT: sizeof correct
1397 unixDie("read from thread pipe returned wrong size or error");
1398
1399 if (resp) {
1400 ret += *resp;
1401 delete resp; // NOLINT: manual ownership handling
1402 resp = nullptr;
1403 }
1404 // coverity[leaked_storage]
1405 }
1406 return ret;
1407 }
1408
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);
1417
1418 static int initNet(Logr::log_t log)
1419 {
1420 checkLinuxIPv6Limits(log);
1421 try {
1422 pdns::parseQueryLocalAddress(::arg()["query-local-address"]);
1423 }
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"));
1427 return 99;
1428 }
1429
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"));
1434 }
1435 else {
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"));
1438 }
1439
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"));
1444 }
1445 else {
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"));
1448 }
1449
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"));
1453 return 99;
1454 }
1455 return 0;
1456 }
1457
1458 static int initDNSSEC(Logr::log_t log)
1459 {
1460 if (::arg()["dnssec"] == "off") {
1461 g_dnssecmode = DNSSECMode::Off;
1462 }
1463 else if (::arg()["dnssec"] == "process-no-validate") {
1464 g_dnssecmode = DNSSECMode::ProcessNoValidate;
1465 }
1466 else if (::arg()["dnssec"] == "process") {
1467 g_dnssecmode = DNSSECMode::Process;
1468 }
1469 else if (::arg()["dnssec"] == "validate") {
1470 g_dnssecmode = DNSSECMode::ValidateAll;
1471 }
1472 else if (::arg()["dnssec"] == "log-fail") {
1473 g_dnssecmode = DNSSECMode::ValidateForLog;
1474 }
1475 else {
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"])));
1478 return 1;
1479 }
1480
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"));
1485 return 1;
1486 }
1487
1488 g_dnssecLogBogus = ::arg().mustDo("dnssec-log-bogus");
1489 g_maxNSEC3Iterations = ::arg().asNum("nsec3-max-iterations");
1490
1491 vector<string> nums;
1492 bool automatic = true;
1493 if (!::arg()["dnssec-disabled-algorithms"].empty()) {
1494 automatic = false;
1495 stringtok(nums, ::arg()["dnssec-disabled-algorithms"], ", ");
1496 for (const auto& num : nums) {
1497 DNSCryptoKeyEngine::switchOffAlgorithm(pdns::checked_stoi<unsigned int>(num));
1498 }
1499 }
1500 else {
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));
1505 }
1506 }
1507 }
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 << ", ";
1514 }
1515 g_log << Logger::Warning << *i;
1516 }
1517 g_log << Logger::Warning << endl;
1518 }
1519 else {
1520 log->info(Logr::Notice, "Disabled DNSSEC algorithms", "automatically", Logging::Loggable(automatic), "algorithms", Logging::IterLoggable(nums.begin(), nums.end()));
1521 }
1522 }
1523
1524 return 0;
1525 }
1526
1527 static void initDontQuery(Logr::log_t log)
1528 {
1529 if (!::arg()["dont-query"].empty()) {
1530 vector<string> ips;
1531 stringtok(ips, ::arg()["dont-query"], ", ");
1532 ips.emplace_back("0.0.0.0");
1533 ips.emplace_back("::");
1534
1535 for (const auto& anIP : ips) {
1536 SyncRes::addDontQuery(anIP);
1537 }
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 << ", ";
1543 }
1544 g_log << Logger::Warning << *i;
1545 }
1546 g_log << Logger::Warning << endl;
1547 }
1548 else {
1549 log->info(Logr::Notice, "Will not send queries to", "addresses", Logging::IterLoggable(ips.begin(), ips.end()));
1550 }
1551 }
1552 }
1553
1554 static int initSyncRes(Logr::log_t log, const std::optional<std::string>& myHostname)
1555 {
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);
1561
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);
1566
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");
1583
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");
1588 {
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)));
1593 return 1;
1594 }
1595 MemRecursorCache::s_maxServedStaleExtensions = sse;
1596 NegCache::s_maxServedStaleExtensions = sse;
1597 }
1598
1599 if (SyncRes::s_tcp_fast_open_connect) {
1600 checkFastOpenSysctl(true, log);
1601 checkTFOconnect(log);
1602 }
1603
1604 if (SyncRes::s_serverID.empty()) {
1605 SyncRes::s_serverID = myHostname.has_value() ? *myHostname : "";
1606 }
1607
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");
1616
1617 SyncRes::s_qnameminimization = ::arg().mustDo("qname-minimization");
1618
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));
1623 }
1624
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;
1629 }
1630 else if (value == "no") {
1631 SyncRes::s_hardenNXD = SyncRes::HardenNXD::No;
1632 }
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)));
1636 return 1;
1637 }
1638
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));
1642 }
1643 else {
1644 Netmask netmask;
1645 bool done = false;
1646
1647 auto addr = pdns::getNonAnyQueryLocalAddress(AF_INET);
1648 if (addr.sin4.sin_family != 0) {
1649 netmask = Netmask(addr, 32);
1650 done = true;
1651 }
1652 if (!done) {
1653 addr = pdns::getNonAnyQueryLocalAddress(AF_INET6);
1654 if (addr.sin4.sin_family != 0) {
1655 netmask = Netmask(addr, 128);
1656 done = true;
1657 }
1658 }
1659 if (!done) {
1660 netmask = Netmask(ComboAddress("127.0.0.1"), 32);
1661 }
1662 SyncRes::setECSScopeZeroAddress(netmask);
1663 }
1664
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");
1669 return 0;
1670 }
1671
1672 static void initDistribution(Logr::log_t log)
1673 {
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"));
1679 }
1680
1681 #ifdef SO_REUSEPORT
1682 g_reusePort = ::arg().mustDo("reuseport");
1683 #endif
1684
1685 RecThreadInfo::infos().resize(RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + RecThreadInfo::numWorkers() + RecThreadInfo::numTaskThreads());
1686
1687 if (g_reusePort) {
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);
1696 }
1697 }
1698 else {
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);
1706 }
1707 }
1708 }
1709 else {
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);
1715
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;
1722 }
1723 }
1724 else {
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;
1728 }
1729 }
1730 }
1731 }
1732
1733 static int initForks(Logr::log_t log)
1734 {
1735 int forks = 0;
1736 for (; forks < ::arg().asNum("processes") - 1; ++forks) {
1737 if (fork() == 0) { // we are child
1738 break;
1739 }
1740 }
1741
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);
1746 daemonize(log);
1747 }
1748
1749 if (Utility::getpid() == 1) {
1750 /* We are running as pid 1, register sigterm and sigint handler
1751
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".
1760
1761 So TL;DR: If we're running pid 1 (container), we should handle SIGTERM and SIGINT ourselves */
1762
1763 signal(SIGTERM, termIntHandler);
1764 signal(SIGINT, termIntHandler);
1765 }
1766
1767 signal(SIGUSR1, usr1Handler);
1768 signal(SIGUSR2, usr2Handler);
1769 signal(SIGPIPE, SIG_IGN); // NOLINT: Posix API
1770 return forks;
1771 }
1772
1773 static int initPorts(Logr::log_t log)
1774 {
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
1780 }
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
1787 }
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
1797 }
1798 g_avoidUdpSourcePorts.insert(port);
1799 }
1800 return 0;
1801 }
1802
1803 static void initSNMP([[maybe_unused]] Logr::log_t log)
1804 {
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"];
1810 }
1811 g_snmpAgent = std::make_shared<RecursorSNMPAgent>("recursor", setting);
1812 g_snmpAgent->run();
1813 #else
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
1818 }
1819 }
1820
1821 static int initControl(Logr::log_t log, uid_t newuid, int forks)
1822 {
1823 if (!::arg()["chroot"].empty()) {
1824 #ifdef HAVE_SYSTEMD
1825 char* ns;
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'"));
1830 return 1;
1831 }
1832 #endif
1833 if (chroot(::arg()["chroot"].c_str()) < 0 || chdir("/") < 0) {
1834 int err = errno;
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"])));
1837 return 1;
1838 }
1839 SLOG(g_log << Logger::Info << "Chrooted to '" << ::arg()["chroot"] << "'" << endl,
1840 log->info(Logr::Info, "Chrooted", "chroot", Logging::Loggable(::arg()["chroot"])));
1841 }
1842
1843 checkSocketDir(log);
1844
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
1848 }
1849 writePid(log);
1850
1851 makeControlChannelSocket(::arg().asNum("processes") > 1 ? forks : -1);
1852
1853 Utility::dropUserPrivs(newuid);
1854 try {
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.
1858 */
1859 dropCapabilities();
1860 }
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"));
1864 }
1865 return 0;
1866 }
1867
1868 static void initSuffixMatchNodes([[maybe_unused]] Logr::log_t log)
1869 {
1870 {
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));
1876 }
1877 g_dontThrottleNames.setState(std::move(dontThrottleNames));
1878
1879 parts.clear();
1880 NetmaskGroup dontThrottleNetmasks;
1881 stringtok(parts, ::arg()["dont-throttle-netmasks"], " ,");
1882 for (const auto& part : parts) {
1883 dontThrottleNetmasks.addMask(Netmask(part));
1884 }
1885 g_dontThrottleNetmasks.setState(std::move(dontThrottleNetmasks));
1886 }
1887
1888 {
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));
1894 }
1895 g_xdnssec.setState(std::move(xdnssecNames));
1896 }
1897
1898 {
1899 SuffixMatchNode dotauthNames;
1900 vector<string> parts;
1901 stringtok(parts, ::arg()["dot-to-auth-names"], " ,");
1902 #ifndef HAVE_DNS_OVER_TLS
1903 if (parts.size()) {
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"));
1906 }
1907 #endif
1908 for (const auto& part : parts) {
1909 dotauthNames.add(DNSName(part));
1910 }
1911 g_DoTToAuthNames.setState(std::move(dotauthNames));
1912 }
1913 }
1914
1915 static void initCarbon()
1916 {
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));
1923 }
1924
1925 static int initDNS64(Logr::log_t log)
1926 {
1927 if (!::arg()["dns64-prefix"].empty()) {
1928 try {
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"])));
1933 return 1;
1934 }
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();
1940 }
1941 }
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"])));
1945 return 1;
1946 }
1947 }
1948 return 0;
1949 }
1950
1951 static int serviceMain(Logr::log_t log)
1952 {
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");
1957
1958 if (!::arg()["logging-facility"].empty()) {
1959 int val = logFacilityToLOG(::arg().asNum("logging-facility"));
1960 if (val >= 0) {
1961 g_log.setFacility(val);
1962 }
1963 else {
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"))));
1966 }
1967 }
1968
1969 showProductVersion();
1970
1971 g_disthashseed = dns_random_uint32();
1972
1973 int ret = initNet(log);
1974 if (ret != 0) {
1975 return ret;
1976 }
1977 // keep this ABOVE loadRecursorLuaConfig!
1978 ret = initDNSSEC(log);
1979 if (ret != 0) {
1980 return ret;
1981 }
1982 g_maxCacheEntries = ::arg().asNum("max-cache-entries");
1983
1984 luaConfigDelayedThreads delayedLuaThreads;
1985 try {
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);
1990 }
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"));
1994 return 1;
1995 }
1996
1997 parseACLs();
1998 initPublicSuffixList(::arg()["public-suffix-list-file"]);
1999
2000 initDontQuery(log);
2001
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"));
2006 }
2007
2008 g_outgoingEDNSBufsize = ::arg().asNum("edns-outgoing-bufsize");
2009
2010 if (::arg()["trace"] == "fail") {
2011 SyncRes::setDefaultLogMode(SyncRes::Store);
2012 }
2013 else if (::arg().mustDo("trace")) {
2014 SyncRes::setDefaultLogMode(SyncRes::Log);
2015 ::arg().set("quiet") = "no";
2016 g_quiet = false;
2017 }
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"));
2022 }
2023
2024 ret = initSyncRes(log, myHostname);
2025 if (ret != 0) {
2026 return ret;
2027 }
2028
2029 g_proxyProtocolACL.toMasks(::arg()["proxy-protocol-from"]);
2030 g_proxyProtocolMaximumSize = ::arg().asNum("proxy-protocol-maximum-size");
2031
2032 ret = initDNS64(log);
2033 if (ret != 0) {
2034 return ret;
2035 }
2036 g_networkTimeoutMsec = ::arg().asNum("network-timeout");
2037
2038 std::tie(g_initialDomainMap, g_initialAllowNotifyFor) = parseZoneConfiguration();
2039
2040 g_latencyStatSize = ::arg().asNum("latency-statistic-size");
2041
2042 g_logCommonErrors = ::arg().mustDo("log-common-errors");
2043 g_logRPZChanges = ::arg().mustDo("log-rpz-changes");
2044
2045 g_anyToTcp = ::arg().mustDo("any-to-tcp");
2046 g_udpTruncationThreshold = ::arg().asNum("udp-truncation-threshold");
2047
2048 g_lowercaseOutgoing = ::arg().mustDo("lowercase-outgoing");
2049
2050 g_paddingFrom.toMasks(::arg()["edns-padding-from"]);
2051 if (::arg()["edns-padding-mode"] == "always") {
2052 g_paddingMode = PaddingMode::Always;
2053 }
2054 else if (::arg()["edns-padding-mode"] == "padded-queries-only") {
2055 g_paddingMode = PaddingMode::PaddedQueries;
2056 }
2057 else {
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"])));
2060 return 1;
2061 }
2062 g_paddingTag = ::arg().asNum("edns-padding-tag");
2063 g_paddingOutgoing = ::arg().mustDo("edns-padding-out");
2064
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);
2071 }
2072
2073 g_maxMThreads = ::arg().asNum("max-mthreads");
2074
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;
2080 }
2081 else {
2082 TCPConnection::s_maxInFlight = maxInFlight;
2083 }
2084
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");
2090
2091 g_gettagNeedsEDNSOptions = ::arg().mustDo("gettag-needs-edns-options");
2092
2093 s_statisticsInterval = ::arg().asNum("statistics-interval");
2094
2095 SyncRes::s_addExtendedResolutionDNSErrors = ::arg().mustDo("extended-resolution-errors");
2096
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"));
2100 }
2101 else {
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"));
2104 }
2105 }
2106
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)));
2110
2111 initSuffixMatchNodes(log);
2112 initCarbon();
2113 initDistribution(log);
2114
2115 #ifdef NOD_ENABLED
2116 // Setup newly observed domain globals
2117 setupNODGlobal();
2118 #endif /* NOD_ENABLED */
2119
2120 auto forks = initForks(log);
2121
2122 checkOrFixFDS(log);
2123
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"));
2128 return 99;
2129 }
2130 #endif
2131
2132 openssl_thread_setup();
2133 openssl_seed();
2134 /* setup rng before chroot */
2135 dns_random_init();
2136
2137 if (::arg()["server-id"].empty()) {
2138 ::arg().set("server-id") = myHostname.has_value() ? *myHostname : "";
2139 }
2140
2141 gid_t newgid = 0;
2142 if (!::arg()["setgid"].empty()) {
2143 newgid = strToGID(::arg()["setgid"]);
2144 }
2145 uid_t newuid = 0;
2146 if (!::arg()["setuid"].empty()) {
2147 newuid = strToUID(::arg()["setuid"]);
2148 }
2149
2150 Utility::dropGroupPrivs(newuid, newgid);
2151
2152 ret = initControl(log, newuid, forks);
2153 if (ret != 0) {
2154 return ret;
2155 }
2156
2157 startLuaConfigDelayedThreads(delayedLuaThreads, g_luaconfs.getCopy().generation);
2158 delayedLuaThreads.rpzPrimaryThreads.clear(); // no longer needed
2159
2160 RecThreadInfo::makeThreadPipes(log);
2161
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");
2166
2167 g_useKernelTimestamp = ::arg().mustDo("protobuf-use-kernel-timestamp");
2168
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"]);
2173
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"]);
2178
2179 // Run before any thread doing stats related things
2180 registerAllStats();
2181
2182 initSNMP(log);
2183
2184 ret = initPorts(log);
2185 if (ret != 0) {
2186 return ret;
2187 }
2188
2189 return RecThreadInfo::runThreads(log);
2190 }
2191
2192 static void handlePipeRequest(int fileDesc, FDMultiplexer::funcparam_t& /* var */)
2193 {
2194 ThreadMSG* tmsg = nullptr;
2195
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");
2198 }
2199
2200 void* resp = nullptr;
2201 try {
2202 resp = tmsg->func();
2203 }
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")));
2208 }
2209 }
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")));
2214 }
2215 }
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");
2220 }
2221 }
2222
2223 delete tmsg; // NOLINT: manual ownership handling
2224 }
2225
2226 static void handleRCC(int fileDesc, FDMultiplexer::funcparam_t& /* var */)
2227 {
2228 auto log = g_slog->withName("control");
2229 try {
2230 FDWrapper clientfd = accept(fileDesc, nullptr, nullptr);
2231 if (clientfd == -1) {
2232 throw PDNSException("accept failed");
2233 }
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)));
2237
2238 RecursorControlParser rcp;
2239 RecursorControlParser::func_t* command = nullptr;
2240 auto answer = rcp.getAnswer(clientfd, msg, &command);
2241
2242 g_rcc.send(clientfd, answer);
2243 command();
2244 }
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")));
2248 }
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")));
2252 }
2253 }
2254
2255 class PeriodicTask
2256 {
2257 public:
2258 PeriodicTask(const string& aName, time_t aTime) :
2259 period{aTime, 0}, name(aName)
2260 {
2261 if (aTime <= 0) {
2262 throw PDNSException("Invalid period of periodic task " + aName);
2263 }
2264 }
2265
2266 void runIfDue(struct timeval& now, const std::function<void()>& function)
2267 {
2268 if (last_run < now - period) {
2269 // cerr << RecThreadInfo::id() << ' ' << name << ' ' << now.tv_sec << '.' << now.tv_usec << " running" << endl;
2270 function();
2271 Utility::gettimeofday(&last_run);
2272 now = last_run;
2273 }
2274 }
2275
2276 [[nodiscard]] time_t getPeriod() const
2277 {
2278 return period.tv_sec;
2279 }
2280
2281 void setPeriod(time_t newperiod)
2282 {
2283 period.tv_sec = newperiod;
2284 }
2285
2286 void updateLastRun()
2287 {
2288 Utility::gettimeofday(&last_run);
2289 }
2290
2291 [[nodiscard]] bool hasRun() const
2292 {
2293 return last_run.tv_sec != 0 || last_run.tv_usec != 0;
2294 }
2295
2296 private:
2297 struct timeval last_run
2298 {
2299 0, 0
2300 };
2301 struct timeval period;
2302 const string name;
2303 };
2304
2305 static void houseKeepingWork(Logr::log_t log)
2306 {
2307 struct timeval now
2308 {
2309 };
2310 Utility::gettimeofday(&now);
2311 t_Counters.updateSnap(now, g_regressionTestMode);
2312
2313 // Below are the tasks that run for every recursorThread, including handler and taskThread
2314
2315 static thread_local PeriodicTask pruneTCPTask{"pruneTCPTask", 5};
2316 pruneTCPTask.runIfDue(now, [now]() {
2317 t_tcp_manager.cleanup(now);
2318 });
2319
2320 const auto& info = RecThreadInfo::self();
2321
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);
2329 #ifdef HAVE_FSTRM
2330 checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
2331 checkFrameStreamExport(luaconfsLocal, luaconfsLocal->nodFrameStreamExportConfig, t_nodFrameStreamServersInfo);
2332 #endif
2333 });
2334
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);
2340
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));
2347 }
2348 });
2349 }
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);
2355 });
2356 }
2357 static PeriodicTask recordCachePruneTask{"RecordCachePruneTask", 5};
2358 recordCachePruneTask.runIfDue(now, []() {
2359 g_recCache->doPrune(g_maxCacheEntries);
2360 });
2361
2362 static PeriodicTask negCachePruneTask{"NegCachePrunteTask", 5};
2363 negCachePruneTask.runIfDue(now, []() {
2364 g_negCache->prune(g_maxCacheEntries / 8);
2365 });
2366
2367 static PeriodicTask aggrNSECPruneTask{"AggrNSECPruneTask", 5};
2368 aggrNSECPruneTask.runIfDue(now, [now]() {
2369 if (g_aggressiveNSECCache) {
2370 g_aggressiveNSECCache->prune(now.tv_sec);
2371 }
2372 });
2373
2374 static PeriodicTask pruneNSpeedTask{"pruneNSSpeedTask", 30};
2375 pruneNSpeedTask.runIfDue(now, [now]() {
2376 SyncRes::pruneNSSpeeds(now.tv_sec - 300);
2377 });
2378
2379 static PeriodicTask pruneEDNSTask{"pruneEDNSTask", 60};
2380 pruneEDNSTask.runIfDue(now, [now]() {
2381 SyncRes::pruneEDNSStatuses(now.tv_sec);
2382 });
2383
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);
2388 });
2389 }
2390
2391 static PeriodicTask pruneThrottledTask{"pruneThrottledTask", 5};
2392 pruneThrottledTask.runIfDue(now, [now]() {
2393 SyncRes::pruneThrottledServers(now.tv_sec);
2394 });
2395
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));
2399 });
2400
2401 static PeriodicTask pruneNonResolvingTask{"pruneNonResolvingTask", 5};
2402 pruneNonResolvingTask.runIfDue(now, [now]() {
2403 SyncRes::pruneNonResolving(now.tv_sec - SyncRes::s_nonresolvingnsthrottletime);
2404 });
2405
2406 static PeriodicTask pruneSaveParentSetTask{"pruneSaveParentSetTask", 60};
2407 pruneSaveParentSetTask.runIfDue(now, [now]() {
2408 SyncRes::pruneSaveParentsNSSets(now.tv_sec);
2409 });
2410
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]() {
2415 int res = 0;
2416 if (!g_regressionTestMode) {
2417 res = SyncRes::getRootNS(now, nullptr, 0, log);
2418 }
2419 if (res == 0) {
2420 // Success, go back to the defaut period
2421 rootUpdateTask.setPeriod(std::max(SyncRes::s_maxcachettl * 8 / 10, minRootRefreshInterval));
2422 }
2423 else {
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));
2428 }
2429 });
2430
2431 static PeriodicTask secpollTask{"secpollTask", 3600};
2432 static time_t t_last_secpoll;
2433 secpollTask.runIfDue(now, [&log]() {
2434 try {
2435 doSecPoll(&t_last_secpoll, log);
2436 }
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"));
2440 }
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"));
2444 }
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"));
2448 }
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"));
2452 }
2453 catch (...) {
2454 SLOG(g_log << Logger::Error << "Exception while performing security poll" << endl,
2455 log->info(Logr::Error, "Exception while performing security poll"));
2456 }
2457 });
2458
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();
2464 }
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"));
2471 try {
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;
2476 });
2477 }
2478 }
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"));
2482 }
2483 }
2484 });
2485 }
2486 t_Counters.updateSnap(g_regressionTestMode);
2487 }
2488
2489 static void houseKeeping(void* /* ignored */)
2490 {
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
2493
2494 try {
2495 if (t_running) {
2496 return;
2497 }
2498 t_running = true;
2499 houseKeepingWork(log);
2500 t_running = false;
2501 }
2502 catch (const PDNSException& ae) {
2503 t_running = false;
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"));
2506 throw;
2507 }
2508 catch (...) {
2509 t_running = false;
2510 SLOG(g_log << Logger::Error << "Uncaught exception in housekeeping thread" << endl,
2511 log->info(Logr::Error, "Uncaught exception in housekeeping thread"));
2512 throw;
2513 }
2514 }
2515
2516 static void runLuaMaintenance(RecThreadInfo& threadInfo, time_t& last_lua_maintenance, time_t luaMaintenanceInterval)
2517 {
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
2525 {
2526 };
2527 Utility::gettimeofday(&start);
2528 t_pdl->maintenance();
2529 last_lua_maintenance = g_now.tv_sec;
2530 struct timeval stop
2531 {
2532 };
2533 Utility::gettimeofday(&stop);
2534 t_Counters.at(rec::Counter::maintenanceUsec) += uSec(stop - start);
2535 ++t_Counters.at(rec::Counter::maintenanceCalls);
2536 }
2537 }
2538 }
2539 }
2540
2541 static void runTCPMaintenance(RecThreadInfo& threadInfo, bool& listenOnTCP, unsigned int maxTcpClients)
2542 {
2543 if (threadInfo.isListener()) {
2544 if (listenOnTCP) {
2545 if (TCPConnection::getCurrentConnections() > maxTcpClients) { // shutdown, too many connections
2546 for (const auto fileDesc : threadInfo.tcpSockets) {
2547 t_fdm->removeReadFD(fileDesc);
2548 }
2549 listenOnTCP = false;
2550 }
2551 }
2552 else {
2553 if (TCPConnection::getCurrentConnections() <= maxTcpClients) { // reenable
2554 for (const auto fileDesc : threadInfo.tcpSockets) {
2555 t_fdm->addReadFD(fileDesc, handleNewTCPQuestion);
2556 }
2557 listenOnTCP = true;
2558 }
2559 }
2560 }
2561 }
2562
2563 static void recLoop()
2564 {
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");
2572
2573 auto& threadInfo = RecThreadInfo::self();
2574
2575 while (!RecursorControlChannel::stop) {
2576 while (g_multiTasker->schedule(&g_now)) {
2577 ; // MTasker letting the mthreads do their thing
2578 }
2579
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
2584 {
2585 };
2586 Utility::gettimeofday(&start);
2587 g_multiTasker->makeThread(houseKeeping, nullptr);
2588 if (!threadInfo.isTaskThread()) {
2589 struct timeval stop
2590 {
2591 };
2592 Utility::gettimeofday(&stop);
2593 t_Counters.at(rec::Counter::maintenanceUsec) += uSec(stop - start);
2594 ++t_Counters.at(rec::Counter::maintenanceCalls);
2595 }
2596 }
2597
2598 if (s_counter % 55 == 0) {
2599 auto expired = t_fdm->getTimeouts(g_now);
2600
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)));
2606 }
2607 t_fdm->removeReadFD(exp.first);
2608 }
2609 }
2610
2611 s_counter++;
2612
2613 if (threadInfo.isHandler()) {
2614 if (statsWanted || (s_statisticsInterval > 0 && (g_now.tv_sec - last_stat) >= s_statisticsInterval)) {
2615 doStats();
2616 last_stat = g_now.tv_sec;
2617 }
2618
2619 Utility::gettimeofday(&g_now, nullptr);
2620
2621 if ((g_now.tv_sec - last_carbon) >= carbonInterval) {
2622 g_multiTasker->makeThread(doCarbonDump, nullptr);
2623 last_carbon = g_now.tv_sec;
2624 }
2625 }
2626 runLuaMaintenance(threadInfo, last_lua_maintenance, luaMaintenanceInterval);
2627
2628 t_fdm->run(&g_now);
2629 // 'run' updates g_now for us
2630
2631 runTCPMaintenance(threadInfo, listenOnTCP, maxTcpClients);
2632 }
2633 }
2634
2635 static void recursorThread()
2636 {
2637 auto log = g_slog->withName("runtime");
2638 t_Counters.updateSnap(true);
2639 try {
2640 auto& threadInfo = RecThreadInfo::self();
2641 {
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);
2651 }
2652 else {
2653 t_proxyMapping = nullptr;
2654 }
2655
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"));
2662 }
2663 SLOG(g_log << Logger::Debug << "Done priming cache with root hints" << endl,
2664 log->info(Logr::Debug, "Done priming cache with root hints"));
2665 }
2666 }
2667
2668 #ifdef NOD_ENABLED
2669 if (threadInfo.isWorker()) {
2670 setupNODThread(log);
2671 }
2672 #endif /* NOD_ENABLED */
2673
2674 /* the listener threads handle TCP queries */
2675 if (threadInfo.isWorker() || threadInfo.isListener()) {
2676 try {
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"])));
2682 }
2683 }
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"])));
2687 _exit(99);
2688 }
2689 }
2690
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());
2696 }
2697 else {
2698 t_remotes->set_capacity(ringsize);
2699 }
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);
2708
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);
2715 }
2716 g_multiTasker = std::make_unique<MT_t>(::arg().asNum("stack-size"), ::arg().asNum("stack-cache-size"));
2717 threadInfo.mt = g_multiTasker.get();
2718
2719 /* start protobuf export threads if needed */
2720 auto luaconfsLocal = g_luaconfs.getLocal();
2721 checkProtobufExport(luaconfsLocal);
2722 checkOutgoingProtobufExport(luaconfsLocal);
2723 #ifdef HAVE_FSTRM
2724 checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
2725 checkFrameStreamExport(luaconfsLocal, luaconfsLocal->nodFrameStreamExportConfig, t_nodFrameStreamServersInfo);
2726 #endif
2727
2728 t_fdm = unique_ptr<FDMultiplexer>(getMultiplexer(log));
2729
2730 std::unique_ptr<RecursorWebServer> rws;
2731
2732 t_fdm->addReadFD(threadInfo.pipes.readToThread, handlePipeRequest);
2733
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"))
2738 try {
2739 rws = make_unique<RecursorWebServer>(t_fdm.get());
2740 }
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"));
2744 _exit(99);
2745 }
2746 }
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())));
2749 }
2750 else {
2751 t_fdm->addReadFD(threadInfo.pipes.readQueriesToThread, handlePipeRequest);
2752
2753 if (threadInfo.isListener()) {
2754 if (g_reusePort) {
2755 /* then every listener has its own FDs */
2756 for (const auto& deferred : threadInfo.deferredAdds) {
2757 t_fdm->addReadFD(deferred.first, deferred.second);
2758 }
2759 }
2760 else {
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);
2764 }
2765 }
2766 }
2767 }
2768
2769 if (threadInfo.isHandler()) {
2770 t_fdm->addReadFD(g_rcc.d_fd, handleRCC); // control channel
2771 }
2772
2773 #ifdef HAVE_SYSTEMD
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");
2779 }
2780 #endif
2781
2782 recLoop();
2783 }
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")))
2787 }
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")))
2791 }
2792 catch (...) {
2793 SLOG(g_log << Logger::Error << "any other exception in main: " << endl,
2794 log->info(Logr::Error, "Exception in RecursorThread"));
2795 }
2796 }
2797
2798 static void initArgs()
2799 {
2800 #if HAVE_FIBER_SANITIZER
2801 // Asan needs more stack
2802 ::arg().set("stack-size", "stack size per mthread") = "600000";
2803 #else
2804 ::arg().set("stack-size", "stack size per mthread") = "200000";
2805 #endif
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"
2827 #ifdef HAVE_SYSTEMD
2828 #define SYSTEMD_SETID_MSG ". When running inside systemd, use the User and Group settings in the unit-file!"
2829 SYSTEMD_SETID_MSG
2830 #endif
2831 )
2832 = "";
2833 ::arg().set("setuid", "If set, change user id to this uid for more security"
2834 #ifdef HAVE_SYSTEMD
2835 SYSTEMD_SETID_MSG
2836 #endif
2837 )
2838 = "";
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";
2858
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") = "";
2866
2867 ::arg().set("socket-dir", string("Where the controlsocket will live, ") + LOCALSTATEDIR + "/pdns-recursor when unset and not chrooted"
2868 #ifdef HAVE_SYSTEMD
2869 + ". Set to the RUNTIME_DIRECTORY environment variable when that variable has a value (e.g. under systemd).")
2870 = "";
2871 auto* runtimeDir = getenv("RUNTIME_DIRECTORY"); // NOLINT(concurrency-mt-unsafe,cppcoreguidelines-pro-type-vararg)
2872 if (runtimeDir != nullptr) {
2873 ::arg().set("socket-dir") = runtimeDir;
2874 }
2875 #else
2876 )
2877 = "";
2878 #endif
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";
2890
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";
2918
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";
2959
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.";
2962
2963 #ifdef SO_REUSEPORT
2964 ::arg().setSwitch("reuseport", "Enable SO_REUSEPORT allowing multiple recursors processes to listen to 1 address") = "yes";
2965 #else
2966 ::arg().setSwitch("reuseport", "Enable SO_REUSEPORT allowing multiple recursors processes to listen to 1 address") = "no";
2967 #endif
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") = "";
2971
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);
2975 }
2976 for (size_t idx = 0; idx < 128; idx++) {
2977 defaultAPIDisabledStats += ", ecs-v6-response-bits-" + std::to_string(idx + 1);
2978 }
2979 std::string defaultDisabledStats = defaultAPIDisabledStats + ", cumul-clientanswers, cumul-authanswers, policy-hits, proxy-mapping-total, remote-logger-count";
2980
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;
2985
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;
2990
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";
2994
2995 ::arg().set("cpu-map", "Thread to CPU mapping, space separated thread-id=cpu1,cpu2..cpuN pairs") = "";
2996
2997 ::arg().setSwitch("log-rpz-changes", "Log additions and removals to RPZ zones at Info level") = "no";
2998
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";
3001
3002 ::arg().set("dns64-prefix", "DNS64 prefix") = "";
3003
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";
3010
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";
3015
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";
3018
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";
3021
3022 ::arg().set("x-dnssec-names", "Collect DNSSEC statistics for names or suffixes in this list in separate x-dnssec counters") = "";
3023
3024 #ifdef NOD_ENABLED
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 */
3039
3040 ::arg().setSwitch("extended-resolution-errors", "If set, send an EDNS Extended Error extension on resolution failures, like DNSSEC validation errors") = "no";
3041
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";
3044
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";
3049
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";
3053
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";
3063
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);
3069 }
3070
3071 static pair<int, bool> doConfig(Logr::log_t startupLog, const string& configname, int argc, char* argv[]) // NOLINT: Posix API
3072 {
3073 if (::arg().mustDo("config")) {
3074 string config = ::arg()["config"];
3075 if (config == "check") {
3076 try {
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)));
3080 return {1, true};
3081 }
3082 ::arg().parse(argc, argv);
3083 return {0, true};
3084 }
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)));
3088 return {1, true};
3089 }
3090 }
3091 else if (config == "default" || config.empty()) {
3092 cout << ::arg().configstring(false, true);
3093 }
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)));
3098 return {1, true};
3099 }
3100 ::arg().laxParse(argc, argv);
3101 cout << ::arg().configstring(true, false);
3102 }
3103 else {
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)));
3107 return {1, true};
3108 }
3109 ::arg().laxParse(argc, argv);
3110 cout << ::arg().configstring(true, true);
3111 }
3112 return {0, true};
3113 }
3114 return {0, false};
3115 }
3116
3117 int main(int argc, char** argv)
3118 {
3119 g_argc = argc;
3120 g_argv = argv;
3121 Utility::srandom();
3122 versionSetProduct(ProductRecursor);
3123 reportBasicTypes();
3124 reportOtherTypes();
3125
3126 int ret = EXIT_SUCCESS;
3127
3128 try {
3129 initArgs();
3130 ::arg().laxParse(argc, argv); // do a lax parse
3131
3132 if (::arg().mustDo("version")) {
3133 showProductVersion();
3134 showBuildConfiguration();
3135 return 0;
3136 }
3137 if (::arg().mustDo("help")) {
3138 cout << "syntax:" << endl
3139 << endl;
3140 cout << ::arg().helpstring(::arg()["help"]) << endl;
3141 return 0;
3142 }
3143
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"];
3149
3150 if (s_logUrgency < Logger::Error) {
3151 s_logUrgency = Logger::Error;
3152 }
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
3155 }
3156 g_log.setLoglevel(s_logUrgency);
3157 g_log.toConsole(s_logUrgency);
3158
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"];
3163 }
3164 cleanSlashes(configname);
3165
3166 if (!::arg().getCommands().empty()) {
3167 cerr << "Fatal: non-option";
3168 if (::arg().getCommands().size() > 1) {
3169 cerr << "s";
3170 }
3171 cerr << " (";
3172 bool first = true;
3173 for (const auto& command : ::arg().getCommands()) {
3174 if (!first) {
3175 cerr << ", ";
3176 }
3177 first = false;
3178 cerr << command;
3179 }
3180 cerr << ") on the command line, perhaps a '--setting=123' statement missed the '='?" << endl;
3181 return 99;
3182 }
3183
3184 if (s_structured_logger_backend == "systemd-journal") {
3185 #ifdef HAVE_SYSTEMD
3186 if (int fd = sd_journal_stream_fd("pdns-recusor", LOG_DEBUG, 0); fd >= 0) {
3187 g_slog = Logging::Logger::create(loggerSDBackend);
3188 close(fd);
3189 }
3190 #endif
3191 if (g_slog == nullptr) {
3192 cerr << "Structured logging to systemd-journal requested but it is not available" << endl;
3193 }
3194 }
3195
3196 if (g_slog == nullptr) {
3197 g_slog = Logging::Logger::create(loggerBackend);
3198 }
3199
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");
3205
3206 ::arg().setSLog(startupLog);
3207
3208 bool mustExit = false;
3209 std::tie(ret, mustExit) = doConfig(startupLog, configname, argc, argv);
3210 if (ret != 0 || mustExit) {
3211 return ret;
3212 }
3213
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)));
3217 }
3218
3219 // Reparse, now with config file as well
3220 ::arg().parse(argc, argv);
3221
3222 g_quiet = ::arg().mustDo("quiet");
3223 s_logUrgency = (Logger::Urgency)::arg().asNum("loglevel");
3224 g_slogStructured = ::arg().mustDo("structured-logging");
3225
3226 if (s_logUrgency < Logger::Error) {
3227 s_logUrgency = Logger::Error;
3228 }
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
3231 }
3232 g_log.setLoglevel(s_logUrgency);
3233 g_log.toConsole(s_logUrgency);
3234
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;
3239 }
3240
3241 if (::arg()["socket-dir"].empty()) {
3242 if (::arg()["chroot"].empty()) {
3243 ::arg().set("socket-dir") = std::string(LOCALSTATEDIR) + "/pdns-recursor";
3244 }
3245 else {
3246 ::arg().set("socket-dir") = "/";
3247 }
3248 }
3249
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";
3255 }
3256 }
3257
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";
3262 }
3263
3264 if (!::arg().mustDo("pdns-distributes-queries")) {
3265 ::arg().set("distributor-threads") = "0";
3266 }
3267
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"));
3273 }
3274
3275 ret = serviceMain(startupLog);
3276 }
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")));
3280 ret = EXIT_FAILURE;
3281 }
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")));
3285 ret = EXIT_FAILURE;
3286 }
3287 catch (...) {
3288 SLOG(g_log << Logger::Error << "any other exception in main: " << endl,
3289 g_slog->withName("config")->info(Logr::Critical, "Fatal error"));
3290 ret = EXIT_FAILURE;
3291 }
3292
3293 return ret;
3294 }
3295
3296 static RecursorControlChannel::Answer* doReloadLuaScript()
3297 {
3298 string fname = ::arg()["lua-dns-script"];
3299 auto log = g_slog->withName("runtime")->withValues("name", Logging::Loggable(fname));
3300 try {
3301 if (fname.empty()) {
3302 t_pdl.reset();
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")};
3306 }
3307
3308 t_pdl = std::make_shared<RecursorLua4>();
3309 try {
3310 t_pdl->loadFile(fname);
3311 }
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"};
3317 }
3318 }
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")};
3323 }
3324
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")};
3328 }
3329
3330 RecursorControlChannel::Answer doQueueReloadLuaScript(vector<string>::const_iterator begin, vector<string>::const_iterator end)
3331 {
3332 if (begin != end) {
3333 ::arg().set("lua-dns-script") = *begin;
3334 }
3335
3336 return broadcastAccFunction<RecursorControlChannel::Answer>(doReloadLuaScript);
3337 }
3338
3339 static string* pleaseUseNewTraceRegex(const std::string& newRegex, int file)
3340 {
3341 try {
3342 if (newRegex.empty()) {
3343 t_traceRegex.reset();
3344 t_tracefd = FDWrapper();
3345 return new string("unset\n");
3346 }
3347 if (file == -1) {
3348 return new string("could not dup file\n");
3349 }
3350 t_traceRegex = std::make_shared<Regex>(newRegex);
3351 t_tracefd = file;
3352 return new string("ok\n");
3353 }
3354 catch (const PDNSException& ae) {
3355 return new string(ae.reason + "\n");
3356 }
3357 }
3358
3359 string doTraceRegex(FDWrapper file, vector<string>::const_iterator begin, vector<string>::const_iterator end)
3360 {
3361 int fileno = dup(file);
3362 // Potential dup failure handled in pleaseUseNewTraceRegex()
3363 return broadcastAccFunction<string>([=] { return pleaseUseNewTraceRegex(begin != end ? *begin : "", fileno); });
3364 }
3365
3366 struct WipeCacheResult wipeCaches(const DNSName& canon, bool subtree, uint16_t qtype)
3367 {
3368 struct WipeCacheResult res;
3369
3370 try {
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));
3375 }
3376 res.negative_record_count = static_cast<int>(g_negCache->wipe(canon, subtree));
3377 if (g_aggressiveNSECCache) {
3378 g_aggressiveNSECCache->removeZoneInfo(canon, subtree);
3379 }
3380 }
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"));
3385 }
3386
3387 return res;
3388 }