]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/recursordist/rec-main.cc
rec: CVE-2023-50387 and CVE-2023-50868
[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 #include "settings/cxxsettings.hh"
43
44 #ifdef NOD_ENABLED
45 #include "nod.hh"
46 #endif /* NOD_ENABLED */
47
48 #ifdef HAVE_LIBSODIUM
49 #include <sodium.h>
50
51 #include <cstddef>
52 #include <utility>
53 #endif
54
55 #ifdef HAVE_SYSTEMD
56 // All calls are coming form the same function, so no use for CODE_LINE, CODE_FUNC etc
57 #define SD_JOURNAL_SUPPRESS_LOCATION
58 #include <systemd/sd-daemon.h>
59 #include <systemd/sd-journal.h>
60 #endif
61
62 #ifdef HAVE_FSTRM
63 thread_local FrameStreamServersInfo t_frameStreamServersInfo;
64 thread_local FrameStreamServersInfo t_nodFrameStreamServersInfo;
65 #endif /* HAVE_FSTRM */
66
67 string g_programname = "pdns_recursor";
68 string g_pidfname;
69 RecursorControlChannel g_rcc; // only active in the handler thread
70 bool g_regressionTestMode;
71 bool g_yamlSettings;
72
73 #ifdef NOD_ENABLED
74 bool g_nodEnabled;
75 DNSName g_nodLookupDomain;
76 bool g_nodLog;
77 SuffixMatchNode g_nodDomainWL;
78 std::string g_nod_pbtag;
79 bool g_udrEnabled;
80 bool g_udrLog;
81 std::string g_udr_pbtag;
82 thread_local std::shared_ptr<nod::NODDB> t_nodDBp;
83 thread_local std::shared_ptr<nod::UniqueResponseDB> t_udrDBp;
84 #endif /* NOD_ENABLED */
85
86 std::atomic<bool> statsWanted;
87 uint32_t g_disthashseed;
88 bool g_useIncomingECS;
89 NetmaskGroup g_proxyProtocolACL;
90 boost::optional<ComboAddress> g_dns64Prefix{boost::none};
91 DNSName g_dns64PrefixReverse;
92 std::shared_ptr<SyncRes::domainmap_t> g_initialDomainMap; // new threads needs this to be setup
93 std::shared_ptr<NetmaskGroup> g_initialAllowFrom; // new thread needs to be setup with this
94 std::shared_ptr<NetmaskGroup> g_initialAllowNotifyFrom; // new threads need this to be setup
95 std::shared_ptr<notifyset_t> g_initialAllowNotifyFor; // new threads need this to be setup
96 bool g_logRPZChanges{false};
97 static time_t s_statisticsInterval;
98 static std::atomic<uint32_t> s_counter;
99 int g_argc;
100 char** g_argv;
101 static string s_structured_logger_backend;
102 static Logger::Urgency s_logUrgency;
103
104 std::shared_ptr<Logr::Logger> g_slogtcpin;
105 std::shared_ptr<Logr::Logger> g_slogudpin;
106 std::shared_ptr<Logr::Logger> g_slogudpout;
107
108 /* without reuseport, all listeners share the same sockets */
109 static deferredAdd_t s_deferredUDPadds;
110 static deferredAdd_t s_deferredTCPadds;
111
112 /* first we have the handler thread, t_id == 0 (some other
113 helper threads like SNMP might have t_id == 0 as well)
114 then the distributor threads if any
115 and finally the workers */
116 std::vector<RecThreadInfo> RecThreadInfo::s_threadInfos;
117
118 std::unique_ptr<ProxyMapping> g_proxyMapping; // new threads needs this to be setup
119 thread_local std::unique_ptr<ProxyMapping> t_proxyMapping;
120
121 bool RecThreadInfo::s_weDistributeQueries; // if true, 1 or more threads listen on the incoming query sockets and distribute them to workers
122 unsigned int RecThreadInfo::s_numDistributorThreads;
123 unsigned int RecThreadInfo::s_numUDPWorkerThreads;
124 unsigned int RecThreadInfo::s_numTCPWorkerThreads;
125 thread_local unsigned int RecThreadInfo::t_id;
126
127 static std::map<unsigned int, std::set<int>> parseCPUMap(Logr::log_t log)
128 {
129 std::map<unsigned int, std::set<int>> result;
130
131 const std::string value = ::arg()["cpu-map"];
132
133 if (!value.empty() && !isSettingThreadCPUAffinitySupported()) {
134 SLOG(g_log << Logger::Warning << "CPU mapping requested but not supported, skipping" << endl,
135 log->info(Logr::Warning, "CPU mapping requested but not supported, skipping"));
136 return result;
137 }
138
139 std::vector<std::string> parts;
140
141 stringtok(parts, value, " \t");
142
143 for (const auto& part : parts) {
144 if (part.find('=') == string::npos) {
145 continue;
146 }
147
148 try {
149 auto headers = splitField(part, '=');
150 boost::trim(headers.first);
151 boost::trim(headers.second);
152
153 auto threadId = pdns::checked_stoi<unsigned int>(headers.first);
154 std::vector<std::string> cpus;
155
156 stringtok(cpus, headers.second, ",");
157
158 for (const auto& cpu : cpus) {
159 int cpuId = std::stoi(cpu);
160
161 result[threadId].insert(cpuId);
162 }
163 }
164 catch (const std::exception& e) {
165 SLOG(g_log << Logger::Error << "Error parsing cpu-map entry '" << part << "': " << e.what() << endl,
166 log->error(Logr::Error, e.what(), "Error parsing cpu-map entry", "entry", Logging::Loggable(part)));
167 }
168 }
169
170 return result;
171 }
172
173 static void setCPUMap(const std::map<unsigned int, std::set<int>>& cpusMap, unsigned int n, pthread_t tid, Logr::log_t log)
174 {
175 const auto& cpuMapping = cpusMap.find(n);
176 if (cpuMapping == cpusMap.cend()) {
177 return;
178 }
179 int ret = mapThreadToCPUList(tid, cpuMapping->second);
180 if (ret == 0) {
181 if (!g_slogStructured) {
182 g_log << Logger::Info << "CPU affinity for thread " << n << " has been set to CPU map:";
183 for (const auto cpu : cpuMapping->second) {
184 g_log << Logger::Info << " " << cpu;
185 }
186 g_log << Logger::Info << endl;
187 }
188 else {
189 log->info(Logr::Info, "CPU affinity has been set", "thread", Logging::Loggable(n), "cpumap", Logging::IterLoggable(cpuMapping->second.begin(), cpuMapping->second.end()));
190 }
191 }
192 else {
193 if (!g_slogStructured) {
194 g_log << Logger::Warning << "Error setting CPU affinity for thread " << n << " to CPU map:";
195 for (const auto cpu : cpuMapping->second) {
196 g_log << Logger::Info << " " << cpu;
197 }
198 g_log << Logger::Info << ' ' << stringerror(ret) << endl;
199 }
200 else {
201 log->error(Logr::Warning, ret, "Error setting CPU affinity", "thread", Logging::Loggable(n), "cpumap", Logging::IterLoggable(cpuMapping->second.begin(), cpuMapping->second.end()));
202 }
203 }
204 }
205
206 static void recursorThread();
207
208 void RecThreadInfo::start(unsigned int tid, const string& tname, const std::map<unsigned int, std::set<int>>& cpusMap, Logr::log_t log)
209 {
210 name = tname;
211 thread = std::thread([tid, tname] {
212 t_id = tid;
213 const string threadPrefix = "rec/";
214 setThreadName(threadPrefix + tname);
215 recursorThread();
216 });
217 setCPUMap(cpusMap, tid, thread.native_handle(), log);
218 }
219
220 int RecThreadInfo::runThreads(Logr::log_t log)
221 {
222 int ret = EXIT_SUCCESS;
223 const auto cpusMap = parseCPUMap(log);
224
225 if (RecThreadInfo::numDistributors() + RecThreadInfo::numUDPWorkers() == 1) {
226 SLOG(g_log << Logger::Warning << "Operating with single UDP distributor/worker thread" << endl,
227 log->info(Logr::Notice, "Operating with single UDP distributor/worker thread"));
228
229 /* This thread handles the web server, carbon, statistics and the control channel */
230 unsigned int currentThreadId = 0;
231 auto& handlerInfo = RecThreadInfo::info(currentThreadId);
232 handlerInfo.setHandler();
233 handlerInfo.start(currentThreadId, "web+stat", cpusMap, log);
234
235 // We skip the single UDP worker thread 1, it's handled after the loop and taskthreads
236 currentThreadId = 2;
237 for (unsigned int thread = 0; thread < RecThreadInfo::numTCPWorkers(); thread++, currentThreadId++) {
238 auto& info = RecThreadInfo::info(currentThreadId);
239 info.setTCPListener();
240 info.setWorker();
241 info.start(currentThreadId, "tcpworker", cpusMap, log);
242 }
243
244 for (unsigned int thread = 0; thread < RecThreadInfo::numTaskThreads(); thread++, currentThreadId++) {
245 auto& taskInfo = RecThreadInfo::info(currentThreadId);
246 taskInfo.setTaskThread();
247 taskInfo.start(currentThreadId, "task", cpusMap, log);
248 }
249
250 currentThreadId = 1;
251 auto& info = RecThreadInfo::info(currentThreadId);
252 info.setListener();
253 info.setWorker();
254 RecThreadInfo::setThreadId(currentThreadId);
255 recursorThread();
256
257 for (unsigned int thread = 0; thread < RecThreadInfo::numRecursorThreads(); thread++) {
258 if (thread == 1) {
259 continue;
260 }
261 auto& tInfo = RecThreadInfo::info(thread);
262 tInfo.thread.join();
263 if (tInfo.exitCode != 0) {
264 ret = tInfo.exitCode;
265 }
266 }
267 }
268 else {
269 // Setup RecThreadInfo objects
270 unsigned int currentThreadId = 1;
271 if (RecThreadInfo::weDistributeQueries()) {
272 for (unsigned int thread = 0; thread < RecThreadInfo::numDistributors(); thread++, currentThreadId++) {
273 RecThreadInfo::info(currentThreadId).setListener();
274 }
275 }
276 for (unsigned int thread = 0; thread < RecThreadInfo::numUDPWorkers(); thread++, currentThreadId++) {
277 auto& info = RecThreadInfo::info(currentThreadId);
278 info.setListener(!RecThreadInfo::weDistributeQueries());
279 info.setWorker();
280 }
281 for (unsigned int thread = 0; thread < RecThreadInfo::numTCPWorkers(); thread++, currentThreadId++) {
282 auto& info = RecThreadInfo::info(currentThreadId);
283 info.setTCPListener();
284 info.setWorker();
285 }
286 for (unsigned int thread = 0; thread < RecThreadInfo::numTaskThreads(); thread++, currentThreadId++) {
287 auto& info = RecThreadInfo::info(currentThreadId);
288 info.setTaskThread();
289 }
290
291 // And now start the actual threads
292 currentThreadId = 1;
293 if (RecThreadInfo::weDistributeQueries()) {
294 SLOG(g_log << Logger::Warning << "Launching " << RecThreadInfo::numDistributors() << " distributor threads" << endl,
295 log->info(Logr::Notice, "Launching distributor threads", "count", Logging::Loggable(RecThreadInfo::numDistributors())));
296 for (unsigned int thread = 0; thread < RecThreadInfo::numDistributors(); thread++, currentThreadId++) {
297 auto& info = RecThreadInfo::info(currentThreadId);
298 info.start(currentThreadId, "distr", cpusMap, log);
299 }
300 }
301 SLOG(g_log << Logger::Warning << "Launching " << RecThreadInfo::numUDPWorkers() << " worker threads" << endl,
302 log->info(Logr::Notice, "Launching worker threads", "count", Logging::Loggable(RecThreadInfo::numUDPWorkers())));
303
304 for (unsigned int thread = 0; thread < RecThreadInfo::numUDPWorkers(); thread++, currentThreadId++) {
305 auto& info = RecThreadInfo::info(currentThreadId);
306 info.start(currentThreadId, "worker", cpusMap, log);
307 }
308
309 SLOG(g_log << Logger::Warning << "Launching " << RecThreadInfo::numTCPWorkers() << " tcpworker threads" << endl,
310 log->info(Logr::Notice, "Launching tcpworker threads", "count", Logging::Loggable(RecThreadInfo::numTCPWorkers())));
311
312 for (unsigned int thread = 0; thread < RecThreadInfo::numTCPWorkers(); thread++, currentThreadId++) {
313 auto& info = RecThreadInfo::info(currentThreadId);
314 info.start(currentThreadId, "tcpworker", cpusMap, log);
315 }
316
317 for (unsigned int thread = 0; thread < RecThreadInfo::numTaskThreads(); thread++, currentThreadId++) {
318 auto& info = RecThreadInfo::info(currentThreadId);
319 info.start(currentThreadId, "task", cpusMap, log);
320 }
321
322 /* This thread handles the web server, carbon, statistics and the control channel */
323 currentThreadId = 0;
324 auto& info = RecThreadInfo::info(currentThreadId);
325 info.setHandler();
326 info.start(currentThreadId, "web+stat", cpusMap, log);
327
328 for (auto& tInfo : RecThreadInfo::infos()) {
329 tInfo.thread.join();
330 if (tInfo.exitCode != 0) {
331 ret = tInfo.exitCode;
332 }
333 }
334 }
335 return ret;
336 }
337
338 void RecThreadInfo::makeThreadPipes(Logr::log_t log)
339 {
340 auto pipeBufferSize = ::arg().asNum("distribution-pipe-buffer-size");
341 if (pipeBufferSize > 0) {
342 SLOG(g_log << Logger::Info << "Resizing the buffer of the distribution pipe to " << pipeBufferSize << endl,
343 log->info(Logr::Info, "Resizing the buffer of the distribution pipe", "size", Logging::Loggable(pipeBufferSize)));
344 }
345
346 /* thread 0 is the handler / SNMP, worker threads start at 1 */
347 for (unsigned int thread = 0; thread < numRecursorThreads(); ++thread) {
348 auto& threadInfo = info(thread);
349
350 std::array<int, 2> fileDesc{};
351 if (pipe(fileDesc.data()) < 0) {
352 unixDie("Creating pipe for inter-thread communications");
353 }
354
355 threadInfo.pipes.readToThread = fileDesc[0];
356 threadInfo.pipes.writeToThread = fileDesc[1];
357
358 // handler thread only gets first pipe, not the others
359 if (thread == 0) {
360 continue;
361 }
362
363 if (pipe(fileDesc.data()) < 0) {
364 unixDie("Creating pipe for inter-thread communications");
365 }
366
367 threadInfo.pipes.readFromThread = fileDesc[0];
368 threadInfo.pipes.writeFromThread = fileDesc[1];
369
370 if (pipe(fileDesc.data()) < 0) {
371 unixDie("Creating pipe for inter-thread communications");
372 }
373
374 threadInfo.pipes.readQueriesToThread = fileDesc[0];
375 threadInfo.pipes.writeQueriesToThread = fileDesc[1];
376
377 if (pipeBufferSize > 0) {
378 if (!setPipeBufferSize(threadInfo.pipes.writeQueriesToThread, pipeBufferSize)) {
379 int err = errno;
380 SLOG(g_log << Logger::Warning << "Error resizing the buffer of the distribution pipe for thread " << thread << " to " << pipeBufferSize << ": " << stringerror(err) << endl,
381 log->error(Logr::Warning, err, "Error resizing the buffer of the distribution pipe for thread", "thread", Logging::Loggable(thread), "size", Logging::Loggable(pipeBufferSize)));
382 auto existingSize = getPipeBufferSize(threadInfo.pipes.writeQueriesToThread);
383 if (existingSize > 0) {
384 SLOG(g_log << Logger::Warning << "The current size of the distribution pipe's buffer for thread " << thread << " is " << existingSize << endl,
385 log->info(Logr::Warning, "The current size of the distribution pipe's buffer for thread", "thread", Logging::Loggable(thread), "size", Logging::Loggable(existingSize)));
386 }
387 }
388 }
389
390 if (!setNonBlocking(threadInfo.pipes.writeQueriesToThread)) {
391 unixDie("Making pipe for inter-thread communications non-blocking");
392 }
393 }
394 }
395
396 ArgvMap& arg()
397 {
398 static ArgvMap theArg;
399 return theArg;
400 }
401
402 static FDMultiplexer* getMultiplexer(Logr::log_t log)
403 {
404 FDMultiplexer* ret = nullptr;
405 for (const auto& mplexer : FDMultiplexer::getMultiplexerMap()) {
406 try {
407 ret = mplexer.second(FDMultiplexer::s_maxevents);
408 return ret;
409 }
410 catch (FDMultiplexerException& fe) {
411 SLOG(g_log << Logger::Warning << "Non-fatal error initializing possible multiplexer (" << fe.what() << "), falling back" << endl,
412 log->error(Logr::Warning, fe.what(), "Non-fatal error initializing possible multiplexer, falling back"));
413 }
414 catch (...) {
415 SLOG(g_log << Logger::Warning << "Non-fatal error initializing possible multiplexer" << endl,
416 log->info(Logr::Warning, "Non-fatal error initializing possible multiplexer"));
417 }
418 }
419 SLOG(g_log << Logger::Error << "No working multiplexer found!" << endl,
420 log->info(Logr::Error, "No working multiplexer found!"));
421 _exit(1);
422 }
423
424 static std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>> startProtobufServers(const ProtobufExportConfig& config, Logr::log_t log)
425 {
426 auto result = std::make_shared<std::vector<std::unique_ptr<RemoteLogger>>>();
427
428 for (const auto& server : config.servers) {
429 try {
430 auto logger = make_unique<RemoteLogger>(server, config.timeout, 100 * config.maxQueuedEntries, config.reconnectWaitTime, config.asyncConnect);
431 logger->setLogQueries(config.logQueries);
432 logger->setLogResponses(config.logResponses);
433 result->emplace_back(std::move(logger));
434 }
435 catch (const std::exception& e) {
436 SLOG(g_log << Logger::Error << "Error while starting protobuf logger to '" << server << ": " << e.what() << endl,
437 log->error(Logr::Error, e.what(), "Exception while starting protobuf logger", "exception", Logging::Loggable("std::exception"), "server", Logging::Loggable(server)));
438 }
439 catch (const PDNSException& e) {
440 SLOG(g_log << Logger::Error << "Error while starting protobuf logger to '" << server << ": " << e.reason << endl,
441 log->error(Logr::Error, e.reason, "Exception while starting protobuf logger", "exception", Logging::Loggable("PDNSException"), "server", Logging::Loggable(server)));
442 }
443 }
444
445 return result;
446 }
447
448 bool checkProtobufExport(LocalStateHolder<LuaConfigItems>& luaconfsLocal)
449 {
450 if (!luaconfsLocal->protobufExportConfig.enabled) {
451 if (t_protobufServers.servers) {
452 t_protobufServers.servers.reset();
453 t_protobufServers.config = luaconfsLocal->protobufExportConfig;
454 }
455
456 return false;
457 }
458
459 /* if the server was not running, or if it was running according to a
460 previous configuration */
461 if (t_protobufServers.generation < luaconfsLocal->generation && t_protobufServers.config != luaconfsLocal->protobufExportConfig) {
462
463 if (t_protobufServers.servers) {
464 t_protobufServers.servers.reset();
465 }
466 auto log = g_slog->withName("protobuf");
467 t_protobufServers.servers = startProtobufServers(luaconfsLocal->protobufExportConfig, log);
468 t_protobufServers.config = luaconfsLocal->protobufExportConfig;
469 t_protobufServers.generation = luaconfsLocal->generation;
470 }
471
472 return true;
473 }
474
475 bool checkOutgoingProtobufExport(LocalStateHolder<LuaConfigItems>& luaconfsLocal)
476 {
477 if (!luaconfsLocal->outgoingProtobufExportConfig.enabled) {
478 if (t_outgoingProtobufServers.servers) {
479 t_outgoingProtobufServers.servers.reset();
480 t_outgoingProtobufServers.config = luaconfsLocal->outgoingProtobufExportConfig;
481 }
482
483 return false;
484 }
485
486 /* if the server was not running, or if it was running according to a
487 previous configuration */
488 if (t_outgoingProtobufServers.generation < luaconfsLocal->generation && t_outgoingProtobufServers.config != luaconfsLocal->outgoingProtobufExportConfig) {
489
490 if (t_outgoingProtobufServers.servers) {
491 t_outgoingProtobufServers.servers.reset();
492 }
493 auto log = g_slog->withName("protobuf");
494 t_outgoingProtobufServers.servers = startProtobufServers(luaconfsLocal->outgoingProtobufExportConfig, log);
495 t_outgoingProtobufServers.config = luaconfsLocal->outgoingProtobufExportConfig;
496 t_outgoingProtobufServers.generation = luaconfsLocal->generation;
497 }
498
499 return true;
500 }
501
502 void protobufLogQuery(LocalStateHolder<LuaConfigItems>& luaconfsLocal, const boost::uuids::uuid& uniqueId, const ComboAddress& remote, const ComboAddress& local, const ComboAddress& mappedSource, const Netmask& ednssubnet, bool tcp, uint16_t queryID, size_t len, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::unordered_set<std::string>& policyTags, const std::string& requestorId, const std::string& deviceId, const std::string& deviceName, const std::map<std::string, RecursorLua4::MetaValue>& meta)
503 {
504 auto log = g_slog->withName("pblq");
505
506 if (!t_protobufServers.servers) {
507 return;
508 }
509
510 ComboAddress requestor;
511 if (!luaconfsLocal->protobufExportConfig.logMappedFrom) {
512 Netmask requestorNM(remote, remote.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
513 requestor = requestorNM.getMaskedNetwork();
514 requestor.setPort(remote.getPort());
515 }
516 else {
517 Netmask requestorNM(mappedSource, mappedSource.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
518 requestor = requestorNM.getMaskedNetwork();
519 requestor.setPort(mappedSource.getPort());
520 }
521
522 pdns::ProtoZero::RecMessage msg{128, std::string::size_type(policyTags.empty() ? 0 : 64)}; // It's a guess
523 msg.setType(pdns::ProtoZero::Message::MessageType::DNSQueryType);
524 msg.setRequest(uniqueId, requestor, local, qname, qtype, qclass, queryID, tcp ? pdns::ProtoZero::Message::TransportProtocol::TCP : pdns::ProtoZero::Message::TransportProtocol::UDP, len);
525 msg.setServerIdentity(SyncRes::s_serverID);
526 msg.setEDNSSubnet(ednssubnet, ednssubnet.isIPv4() ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
527 msg.setRequestorId(requestorId);
528 msg.setDeviceId(deviceId);
529 msg.setDeviceName(deviceName);
530
531 if (!policyTags.empty()) {
532 msg.addPolicyTags(policyTags);
533 }
534 for (const auto& mit : meta) {
535 msg.setMeta(mit.first, mit.second.stringVal, mit.second.intVal);
536 }
537
538 std::string strMsg(msg.finishAndMoveBuf());
539 for (auto& server : *t_protobufServers.servers) {
540 remoteLoggerQueueData(*server, strMsg);
541 }
542 }
543
544 void protobufLogResponse(pdns::ProtoZero::RecMessage& message)
545 {
546 if (!t_protobufServers.servers) {
547 return;
548 }
549
550 std::string msg(message.finishAndMoveBuf());
551 for (auto& server : *t_protobufServers.servers) {
552 remoteLoggerQueueData(*server, msg);
553 }
554 }
555
556 void protobufLogResponse(const struct dnsheader* header, LocalStateHolder<LuaConfigItems>& luaconfsLocal,
557 const RecursorPacketCache::OptPBData& pbData, const struct timeval& tval,
558 bool tcp, const ComboAddress& source, const ComboAddress& destination,
559 const ComboAddress& mappedSource,
560 const EDNSSubnetOpts& ednssubnet,
561 const boost::uuids::uuid& uniqueId, const string& requestorId, const string& deviceId,
562 const string& deviceName, const std::map<std::string, RecursorLua4::MetaValue>& meta,
563 const RecEventTrace& eventTrace,
564 const std::unordered_set<std::string>& policyTags)
565 {
566 pdns::ProtoZero::RecMessage pbMessage(pbData ? pbData->d_message : "", pbData ? pbData->d_response : "", 64, 10); // The extra bytes we are going to add
567 // Normally we take the immutable string from the cache and append a few values, but if it's not there (can this happen?)
568 // we start with an empty string and append the minimal
569 if (!pbData) {
570 pbMessage.setType(pdns::ProtoZero::Message::MessageType::DNSResponseType);
571 pbMessage.setServerIdentity(SyncRes::s_serverID);
572 }
573
574 // In response part
575 if (g_useKernelTimestamp && tval.tv_sec != 0) {
576 pbMessage.setQueryTime(tval.tv_sec, tval.tv_usec);
577 }
578 else {
579 pbMessage.setQueryTime(g_now.tv_sec, g_now.tv_usec);
580 }
581
582 // In message part
583 if (!luaconfsLocal->protobufExportConfig.logMappedFrom) {
584 pbMessage.setSocketFamily(source.sin4.sin_family);
585 Netmask requestorNM(source, source.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
586 const auto& requestor = requestorNM.getMaskedNetwork();
587 pbMessage.setFrom(requestor);
588 pbMessage.setFromPort(source.getPort());
589 }
590 else {
591 pbMessage.setSocketFamily(mappedSource.sin4.sin_family);
592 Netmask requestorNM(mappedSource, mappedSource.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
593 const auto& requestor = requestorNM.getMaskedNetwork();
594 pbMessage.setFrom(requestor);
595 pbMessage.setFromPort(mappedSource.getPort());
596 }
597 pbMessage.setMessageIdentity(uniqueId);
598 pbMessage.setTo(destination);
599 pbMessage.setSocketProtocol(tcp ? pdns::ProtoZero::Message::TransportProtocol::TCP : pdns::ProtoZero::Message::TransportProtocol::UDP);
600 pbMessage.setId(header->id);
601
602 pbMessage.setTime();
603 pbMessage.setEDNSSubnet(ednssubnet.source, ednssubnet.source.isIPv4() ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
604 pbMessage.setRequestorId(requestorId);
605 pbMessage.setDeviceId(deviceId);
606 pbMessage.setDeviceName(deviceName);
607 pbMessage.setToPort(destination.getPort());
608 for (const auto& metaItem : meta) {
609 pbMessage.setMeta(metaItem.first, metaItem.second.stringVal, metaItem.second.intVal);
610 }
611 #ifdef NOD_ENABLED
612 if (g_nodEnabled) {
613 pbMessage.setNewlyObservedDomain(false);
614 }
615 #endif
616 if (eventTrace.enabled() && (SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_pb) != 0) {
617 pbMessage.addEvents(eventTrace);
618 }
619 pbMessage.addPolicyTags(policyTags);
620
621 protobufLogResponse(pbMessage);
622 }
623
624 #ifdef HAVE_FSTRM
625
626 static std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>> startFrameStreamServers(const FrameStreamExportConfig& config, Logr::log_t log)
627 {
628 auto result = std::make_shared<std::vector<std::unique_ptr<FrameStreamLogger>>>();
629
630 for (const auto& server : config.servers) {
631 try {
632 std::unordered_map<string, unsigned> options;
633 options["bufferHint"] = config.bufferHint;
634 options["flushTimeout"] = config.flushTimeout;
635 options["inputQueueSize"] = config.inputQueueSize;
636 options["outputQueueSize"] = config.outputQueueSize;
637 options["queueNotifyThreshold"] = config.queueNotifyThreshold;
638 options["reopenInterval"] = config.reopenInterval;
639 unique_ptr<FrameStreamLogger> fsl = nullptr;
640 try {
641 ComboAddress address(server);
642 fsl = make_unique<FrameStreamLogger>(address.sin4.sin_family, address.toStringWithPort(), true, options);
643 }
644 catch (const PDNSException& e) {
645 fsl = make_unique<FrameStreamLogger>(AF_UNIX, server, true, options);
646 }
647 fsl->setLogQueries(config.logQueries);
648 fsl->setLogResponses(config.logResponses);
649 fsl->setLogNODs(config.logNODs);
650 fsl->setLogUDRs(config.logUDRs);
651 result->emplace_back(std::move(fsl));
652 }
653 catch (const std::exception& e) {
654 SLOG(g_log << Logger::Error << "Error while starting dnstap framestream logger to '" << server << ": " << e.what() << endl,
655 log->error(Logr::Error, e.what(), "Exception while starting dnstap framestream logger", "exception", Logging::Loggable("std::exception"), "server", Logging::Loggable(server)));
656 }
657 catch (const PDNSException& e) {
658 SLOG(g_log << Logger::Error << "Error while starting dnstap framestream logger to '" << server << ": " << e.reason << endl,
659 log->error(Logr::Error, e.reason, "Exception while starting dnstap framestream logger", "exception", Logging::Loggable("PDNSException"), "server", Logging::Loggable(server)));
660 }
661 }
662
663 return result;
664 }
665
666 static void asyncFrameStreamLoggersCleanup(std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>&& servers)
667 {
668 auto thread = std::thread([&] {
669 servers.reset();
670 });
671 thread.detach();
672 }
673
674 bool checkFrameStreamExport(LocalStateHolder<LuaConfigItems>& luaconfsLocal, const FrameStreamExportConfig& config, FrameStreamServersInfo& serverInfos)
675 {
676 if (!config.enabled) {
677 if (serverInfos.servers) {
678 // dt's take care of cleanup
679 asyncFrameStreamLoggersCleanup(std::move(serverInfos.servers));
680 serverInfos.config = config;
681 }
682
683 return false;
684 }
685
686 /* if the server was not running, or if it was running according to a previous
687 * configuration
688 */
689 if (serverInfos.generation < luaconfsLocal->generation && serverInfos.config != config) {
690 if (serverInfos.servers) {
691 // dt's take care of cleanup
692 asyncFrameStreamLoggersCleanup(std::move(serverInfos.servers));
693 }
694
695 auto dnsTapLog = g_slog->withName("dnstap");
696 serverInfos.servers = startFrameStreamServers(config, dnsTapLog);
697 serverInfos.config = config;
698 serverInfos.generation = luaconfsLocal->generation;
699 }
700
701 return true;
702 }
703
704 #endif /* HAVE_FSTRM */
705
706 static void makeControlChannelSocket(int processNum = -1)
707 {
708 string sockname = ::arg()["socket-dir"] + "/" + g_programname;
709 if (processNum >= 0) {
710 sockname += "." + std::to_string(processNum);
711 }
712 sockname += ".controlsocket";
713 g_rcc.listen(sockname);
714
715 uid_t sockowner = -1;
716 gid_t sockgroup = -1;
717
718 if (!::arg().isEmpty("socket-group")) {
719 sockgroup = ::arg().asGid("socket-group");
720 }
721 if (!::arg().isEmpty("socket-owner")) {
722 sockowner = ::arg().asUid("socket-owner");
723 }
724
725 if (sockgroup != static_cast<gid_t>(-1) || sockowner != static_cast<uid_t>(-1)) {
726 if (chown(sockname.c_str(), sockowner, sockgroup) < 0) {
727 unixDie("Failed to chown control socket");
728 }
729 }
730
731 // do mode change if socket-mode is given
732 if (!::arg().isEmpty("socket-mode")) {
733 mode_t sockmode = ::arg().asMode("socket-mode");
734 if (chmod(sockname.c_str(), sockmode) < 0) {
735 unixDie("Failed to chmod control socket");
736 }
737 }
738 }
739
740 static void writePid(Logr::log_t log)
741 {
742 if (!::arg().mustDo("write-pid")) {
743 return;
744 }
745 ofstream ostr(g_pidfname.c_str(), std::ios_base::app);
746 if (ostr) {
747 ostr << Utility::getpid() << endl;
748 }
749 else {
750 int err = errno;
751 SLOG(g_log << Logger::Error << "Writing pid for " << Utility::getpid() << " to " << g_pidfname << " failed: " << stringerror(err) << endl,
752 log->error(Logr::Error, err, "Writing pid failed", "pid", Logging::Loggable(Utility::getpid()), "file", Logging::Loggable(g_pidfname)));
753 }
754 }
755
756 static void checkSocketDir(Logr::log_t log)
757 {
758 string dir(::arg()["socket-dir"]);
759 string msg;
760
761 struct stat dirStat = {};
762 if (stat(dir.c_str(), &dirStat) == -1) {
763 msg = "it does not exist or cannot access";
764 }
765 else if (!S_ISDIR(dirStat.st_mode)) {
766 msg = "it is not a directory";
767 }
768 else if (access(dir.c_str(), R_OK | W_OK | X_OK) != 0) {
769 msg = "cannot read, write or search";
770 }
771 else {
772 return;
773 }
774 dir = ::arg()["chroot"] + dir;
775 SLOG(g_log << Logger::Error << "Problem with socket directory " << dir << ": " << msg << "; see https://docs.powerdns.com/recursor/upgrade.html#x-to-4-3-0" << endl,
776 log->error(Logr::Error, msg, "Problem with socket directory, see https://docs.powerdns.com/recursor/upgrade.html#x-to-4-3-0", "dir", Logging::Loggable(dir)));
777 _exit(1);
778 }
779
780 #ifdef NOD_ENABLED
781 static void setupNODThread(Logr::log_t log)
782 {
783 if (g_nodEnabled) {
784 uint32_t num_cells = ::arg().asNum("new-domain-db-size");
785 t_nodDBp = std::make_shared<nod::NODDB>(num_cells);
786 try {
787 t_nodDBp->setCacheDir(::arg()["new-domain-history-dir"]);
788 }
789 catch (const PDNSException& e) {
790 SLOG(g_log << Logger::Error << "new-domain-history-dir (" << ::arg()["new-domain-history-dir"] << ") is not readable or does not exist" << endl,
791 log->error(Logr::Error, e.reason, "new-domain-history-dir is not readable or does not exists", "dir", Logging::Loggable(::arg()["new-domain-history-dir"])));
792 _exit(1);
793 }
794 if (!t_nodDBp->init()) {
795 SLOG(g_log << Logger::Error << "Could not initialize domain tracking" << endl,
796 log->info(Logr::Error, "Could not initialize domain tracking"));
797 _exit(1);
798 }
799 std::thread thread(nod::NODDB::startHousekeepingThread, t_nodDBp, std::this_thread::get_id());
800 thread.detach();
801 }
802 if (g_udrEnabled) {
803 uint32_t num_cells = ::arg().asNum("unique-response-db-size");
804 t_udrDBp = std::make_shared<nod::UniqueResponseDB>(num_cells);
805 try {
806 t_udrDBp->setCacheDir(::arg()["unique-response-history-dir"]);
807 }
808 catch (const PDNSException& e) {
809 SLOG(g_log << Logger::Error << "unique-response-history-dir (" << ::arg()["unique-response-history-dir"] << ") is not readable or does not exist" << endl,
810 log->info(Logr::Error, "unique-response-history-dir is not readable or does not exist", "dir", Logging::Loggable(::arg()["unique-response-history-dir"])));
811 _exit(1);
812 }
813 if (!t_udrDBp->init()) {
814 SLOG(g_log << Logger::Error << "Could not initialize unique response tracking" << endl,
815 log->info(Logr::Error, "Could not initialize unique response tracking"));
816 _exit(1);
817 }
818 std::thread thread(nod::UniqueResponseDB::startHousekeepingThread, t_udrDBp, std::this_thread::get_id());
819 thread.detach();
820 }
821 }
822
823 static void parseNODIgnorelist(const std::string& wlist)
824 {
825 vector<string> parts;
826 stringtok(parts, wlist, ",; ");
827 for (const auto& part : parts) {
828 g_nodDomainWL.add(DNSName(part));
829 }
830 }
831
832 static void setupNODGlobal()
833 {
834 // Setup NOD subsystem
835 g_nodEnabled = ::arg().mustDo("new-domain-tracking");
836 g_nodLookupDomain = DNSName(::arg()["new-domain-lookup"]);
837 g_nodLog = ::arg().mustDo("new-domain-log");
838 parseNODIgnorelist(::arg()["new-domain-whitelist"]);
839 parseNODIgnorelist(::arg()["new-domain-ignore-list"]);
840
841 // Setup Unique DNS Response subsystem
842 g_udrEnabled = ::arg().mustDo("unique-response-tracking");
843 g_udrLog = ::arg().mustDo("unique-response-log");
844 g_nod_pbtag = ::arg()["new-domain-pb-tag"];
845 g_udr_pbtag = ::arg()["unique-response-pb-tag"];
846 }
847 #endif /* NOD_ENABLED */
848
849 static void daemonize(Logr::log_t log)
850 {
851 if (auto pid = fork(); pid != 0) {
852 if (pid < 0) {
853 int err = errno;
854 SLOG(g_log << Logger::Critical << "Fork failed: " << stringerror(err) << endl,
855 log->error(Logr::Critical, err, "Fork failed"));
856 exit(1); // NOLINT(concurrency-mt-unsafe)
857 }
858 exit(0); // NOLINT(concurrency-mt-unsafe)
859 }
860
861 setsid();
862
863 int devNull = open("/dev/null", O_RDWR); /* open stdin */
864 if (devNull < 0) {
865 int err = errno;
866 SLOG(g_log << Logger::Critical << "Unable to open /dev/null: " << stringerror(err) << endl,
867 log->error(Logr::Critical, err, "Unable to open /dev/null"));
868 }
869 else {
870 dup2(devNull, 0); /* stdin */
871 dup2(devNull, 1); /* stderr */
872 dup2(devNull, 2); /* stderr */
873 close(devNull);
874 }
875 }
876
877 static void termIntHandler([[maybe_unused]] int arg)
878 {
879 doExit();
880 }
881
882 static void usr1Handler([[maybe_unused]] int arg)
883 {
884 statsWanted = true;
885 }
886
887 static void usr2Handler([[maybe_unused]] int arg)
888 {
889 g_quiet = !g_quiet;
890 SyncRes::setDefaultLogMode(g_quiet ? SyncRes::LogNone : SyncRes::Log);
891 ::arg().set("quiet") = g_quiet ? "yes" : "no";
892 }
893
894 static void checkLinuxIPv6Limits([[maybe_unused]] Logr::log_t log)
895 {
896 #ifdef __linux__
897 string line;
898 if (readFileIfThere("/proc/sys/net/ipv6/route/max_size", &line)) {
899 int lim = std::stoi(line);
900 if (lim < 16384) {
901 SLOG(g_log << Logger::Error << "If using IPv6, please raise sysctl net.ipv6.route.max_size, currently set to " << lim << " which is < 16384" << endl,
902 log->info(Logr::Error, "If using IPv6, please raise sysctl net.ipv6.route.max_size to a size >= 16384", "current", Logging::Loggable(lim)));
903 }
904 }
905 #endif
906 }
907
908 static void checkOrFixFDS(Logr::log_t log)
909 {
910 unsigned int availFDs = getFilenumLimit();
911 unsigned int wantFDs = g_maxMThreads * (RecThreadInfo::numUDPWorkers() + RecThreadInfo::numTCPWorkers()) + 25; // even healthier margin than before
912 wantFDs += (RecThreadInfo::numUDPWorkers() + RecThreadInfo::numTCPWorkers()) * TCPOutConnectionManager::s_maxIdlePerThread;
913
914 if (wantFDs > availFDs) {
915 unsigned int hardlimit = getFilenumLimit(true);
916 if (hardlimit >= wantFDs) {
917 setFilenumLimit(wantFDs);
918 SLOG(g_log << Logger::Warning << "Raised soft limit on number of filedescriptors to " << wantFDs << " to match max-mthreads and threads settings" << endl,
919 log->info(Logr::Warning, "Raised soft limit on number of filedescriptors to match max-mthreads and threads settings", "limit", Logging::Loggable(wantFDs)));
920 }
921 else {
922 auto newval = (hardlimit - 25 - TCPOutConnectionManager::s_maxIdlePerThread) / (RecThreadInfo::numUDPWorkers() + RecThreadInfo::numTCPWorkers());
923 SLOG(g_log << Logger::Warning << "Insufficient number of filedescriptors available for max-mthreads*threads setting! (" << hardlimit << " < " << wantFDs << "), reducing max-mthreads to " << newval << endl,
924 log->info(Logr::Warning, "Insufficient number of filedescriptors available for max-mthreads*threads setting! Reducing max-mthreads", "hardlimit", Logging::Loggable(hardlimit), "want", Logging::Loggable(wantFDs), "max-mthreads", Logging::Loggable(newval)));
925 g_maxMThreads = newval;
926 setFilenumLimit(hardlimit);
927 }
928 }
929 }
930
931 // static std::string s_timestampFormat = "%m-%dT%H:%M:%S";
932 static std::string s_timestampFormat = "%s";
933
934 static const char* toTimestampStringMilli(const struct timeval& tval, std::array<char, 64>& buf)
935 {
936 size_t len = 0;
937 if (s_timestampFormat != "%s") {
938 // strftime is not thread safe, it can access locale information
939 static std::mutex mutex;
940 auto lock = std::lock_guard(mutex);
941 struct tm theTime // clang-format insists on formatting it like this
942 {
943 };
944 len = strftime(buf.data(), buf.size(), s_timestampFormat.c_str(), localtime_r(&tval.tv_sec, &theTime));
945 }
946 if (len == 0) {
947 len = snprintf(buf.data(), buf.size(), "%lld", static_cast<long long>(tval.tv_sec));
948 }
949
950 snprintf(&buf.at(len), buf.size() - len, ".%03ld", static_cast<long>(tval.tv_usec) / 1000);
951 return buf.data();
952 }
953
954 #ifdef HAVE_SYSTEMD
955 static void loggerSDBackend(const Logging::Entry& entry)
956 {
957 static const set<std::string, CIStringComparePOSIX> special = {
958 "message",
959 "message_id",
960 "priority",
961 "code_file",
962 "code_line",
963 "code_func",
964 "errno",
965 "invocation_id",
966 "user_invocation_id",
967 "syslog_facility",
968 "syslog_identifier",
969 "syslog_pid",
970 "syslog_timestamp",
971 "syslog_raw",
972 "documentation",
973 "tid",
974 "unit",
975 "user_unit",
976 "object_pid"};
977
978 // First map SL priority to syslog's Urgency
979 Logger::Urgency urgency = entry.d_priority != 0 ? Logger::Urgency(entry.d_priority) : Logger::Info;
980 if (urgency > s_logUrgency) {
981 // We do not log anything if the Urgency of the message is lower than the requested loglevel.
982 // Not that lower Urgency means higher number.
983 return;
984 }
985 // We need to keep the string in mem until sd_journal_sendv has ben called
986 vector<string> strings;
987 auto appendKeyAndVal = [&strings](const string& key, const string& value) {
988 strings.emplace_back(key + "=" + value);
989 };
990 appendKeyAndVal("MESSAGE", entry.message);
991 if (entry.error) {
992 appendKeyAndVal("ERROR", entry.error.get());
993 }
994 appendKeyAndVal("LEVEL", std::to_string(entry.level));
995 appendKeyAndVal("PRIORITY", std::to_string(entry.d_priority));
996 if (entry.name) {
997 appendKeyAndVal("SUBSYSTEM", entry.name.get());
998 }
999 std::array<char, 64> timebuf{};
1000 appendKeyAndVal("TIMESTAMP", toTimestampStringMilli(entry.d_timestamp, timebuf));
1001 for (const auto& value : entry.values) {
1002 if (value.first.at(0) == '_' || special.count(value.first) != 0) {
1003 string key{"PDNS"};
1004 key.append(value.first);
1005 appendKeyAndVal(toUpper(key), value.second);
1006 }
1007 else {
1008 appendKeyAndVal(toUpper(value.first), value.second);
1009 }
1010 }
1011 // Thread id filled in by backend, since the SL code does not know about RecursorThreads
1012 // We use the Recursor thread, other threads get id 0. May need to revisit.
1013 appendKeyAndVal("TID", std::to_string(RecThreadInfo::id()));
1014
1015 vector<iovec> iov;
1016 iov.reserve(strings.size());
1017 for (const auto& str : strings) {
1018 // iovec has no 2 arg constructor, so make it explicit
1019 iov.emplace_back(iovec{const_cast<void*>(reinterpret_cast<const void*>(str.data())), str.size()}); // NOLINT: it's the API
1020 }
1021 sd_journal_sendv(iov.data(), static_cast<int>(iov.size()));
1022 }
1023 #endif
1024
1025 static void loggerBackend(const Logging::Entry& entry)
1026 {
1027 static thread_local std::stringstream buf;
1028
1029 // First map SL priority to syslog's Urgency
1030 Logger::Urgency urg = entry.d_priority != 0 ? Logger::Urgency(entry.d_priority) : Logger::Info;
1031 if (urg > s_logUrgency) {
1032 // We do not log anything if the Urgency of the message is lower than the requested loglevel.
1033 // Not that lower Urgency means higher number.
1034 return;
1035 }
1036 buf.str("");
1037 buf << "msg=" << std::quoted(entry.message);
1038 if (entry.error) {
1039 buf << " error=" << std::quoted(entry.error.get());
1040 }
1041
1042 if (entry.name) {
1043 buf << " subsystem=" << std::quoted(entry.name.get());
1044 }
1045 buf << " level=" << std::quoted(std::to_string(entry.level));
1046 if (entry.d_priority != 0) {
1047 buf << " prio=" << std::quoted(Logr::Logger::toString(entry.d_priority));
1048 }
1049 // Thread id filled in by backend, since the SL code does not know about RecursorThreads
1050 // We use the Recursor thread, other threads get id 0. May need to revisit.
1051 buf << " tid=" << std::quoted(std::to_string(RecThreadInfo::id()));
1052 std::array<char, 64> timebuf{};
1053 buf << " ts=" << std::quoted(toTimestampStringMilli(entry.d_timestamp, timebuf));
1054 for (auto const& value : entry.values) {
1055 buf << " ";
1056 buf << value.first << "=" << std::quoted(value.second);
1057 }
1058
1059 g_log << urg << buf.str() << endl;
1060 }
1061
1062 static int ratePercentage(uint64_t nom, uint64_t denom)
1063 {
1064 if (denom == 0) {
1065 return 0;
1066 }
1067 return static_cast<int>(round(100.0 * static_cast<double>(nom) / static_cast<double>(denom)));
1068 }
1069
1070 static void doStats()
1071 {
1072 static time_t lastOutputTime;
1073 static uint64_t lastQueryCount;
1074
1075 uint64_t cacheHits = g_recCache->getCacheHits();
1076 uint64_t cacheMisses = g_recCache->getCacheMisses();
1077 uint64_t cacheSize = g_recCache->size();
1078 auto rc_stats = g_recCache->stats();
1079 auto pc_stats = g_packetCache ? g_packetCache->stats() : std::pair<uint64_t, uint64_t>{0, 0};
1080 double rrc = ratePercentage(rc_stats.first, rc_stats.second);
1081 double rpc = ratePercentage(pc_stats.first, pc_stats.second);
1082 uint64_t negCacheSize = g_negCache->size();
1083 auto taskPushes = getTaskPushes();
1084 auto taskExpired = getTaskExpired();
1085 auto taskSize = getTaskSize();
1086 uint64_t pcSize = g_packetCache ? g_packetCache->size() : 0;
1087 uint64_t pcHits = g_packetCache ? g_packetCache->getHits() : 0;
1088
1089 auto log = g_slog->withName("stats");
1090
1091 auto qcounter = g_Counters.sum(rec::Counter::qcounter);
1092 auto syncresqueries = g_Counters.sum(rec::Counter::syncresqueries);
1093 auto outqueries = g_Counters.sum(rec::Counter::outqueries);
1094 auto throttledqueries = g_Counters.sum(rec::Counter::throttledqueries);
1095 auto tcpoutqueries = g_Counters.sum(rec::Counter::tcpoutqueries);
1096 auto dotoutqueries = g_Counters.sum(rec::Counter::dotoutqueries);
1097 auto outgoingtimeouts = g_Counters.sum(rec::Counter::outgoingtimeouts);
1098 if (qcounter > 0 && (cacheHits + cacheMisses) > 0 && syncresqueries > 0 && outqueries > 0) {
1099 if (!g_slogStructured) {
1100 g_log << Logger::Notice << "stats: " << qcounter << " questions, " << cacheSize << " cache entries, " << negCacheSize << " negative entries, " << ratePercentage(cacheHits, cacheHits + cacheMisses) << "% cache hits" << endl;
1101 g_log << Logger::Notice << "stats: record cache contended/acquired " << rc_stats.first << '/' << rc_stats.second << " = " << rrc << '%' << endl;
1102 g_log << Logger::Notice << "stats: packet cache contended/acquired " << pc_stats.first << '/' << pc_stats.second << " = " << rpc << '%' << endl;
1103
1104 g_log << Logger::Notice << "stats: throttle map: "
1105 << SyncRes::getThrottledServersSize() << ", ns speeds: "
1106 << SyncRes::getNSSpeedsSize() << ", failed ns: "
1107 << SyncRes::getFailedServersSize() << ", ednsmap: "
1108 << SyncRes::getEDNSStatusesSize() << ", non-resolving: "
1109 << SyncRes::getNonResolvingNSSize() << ", saved-parentsets: "
1110 << SyncRes::getSaveParentsNSSetsSize()
1111 << endl;
1112 g_log << Logger::Notice << "stats: outpacket/query ratio " << ratePercentage(outqueries, syncresqueries) << "%";
1113 g_log << Logger::Notice << ", " << ratePercentage(throttledqueries, outqueries + throttledqueries) << "% throttled" << endl;
1114 g_log << Logger::Notice << "stats: " << tcpoutqueries << "/" << dotoutqueries << "/" << getCurrentIdleTCPConnections() << " outgoing tcp/dot/idle connections, " << broadcastAccFunction<uint64_t>(pleaseGetConcurrentQueries) << " queries running, " << outgoingtimeouts << " outgoing timeouts " << endl;
1115
1116 g_log << Logger::Notice << "stats: " << pcSize << " packet cache entries, " << ratePercentage(pcHits, qcounter) << "% packet cache hits" << endl;
1117
1118 g_log << Logger::Notice << "stats: tasks pushed/expired/queuesize: " << taskPushes << '/' << taskExpired << '/' << taskSize << endl;
1119 }
1120 else {
1121 const string report = "Periodic statistics report";
1122 log->info(Logr::Info, report,
1123 "questions", Logging::Loggable(qcounter),
1124 "cache-entries", Logging::Loggable(cacheSize),
1125 "negcache-entries", Logging::Loggable(negCacheSize),
1126 "record-cache-hitratio-perc", Logging::Loggable(ratePercentage(cacheHits, cacheHits + cacheMisses)),
1127 "record-cache-contended", Logging::Loggable(rc_stats.first),
1128 "record-cache-acquired", Logging::Loggable(rc_stats.second),
1129 "record-cache-contended-perc", Logging::Loggable(rrc),
1130 "packetcache-contended", Logging::Loggable(pc_stats.first),
1131 "packetcache-acquired", Logging::Loggable(pc_stats.second),
1132 "packetcache-contended-perc", Logging::Loggable(rpc));
1133 log->info(Logr::Info, report,
1134 "throttle-entries", Logging::Loggable(SyncRes::getThrottledServersSize()),
1135 "nsspeed-entries", Logging::Loggable(SyncRes::getNSSpeedsSize()),
1136 "failed-host-entries", Logging::Loggable(SyncRes::getFailedServersSize()),
1137 "edns-entries", Logging::Loggable(SyncRes::getEDNSStatusesSize()),
1138 "non-resolving-nameserver-entries", Logging::Loggable(SyncRes::getNonResolvingNSSize()),
1139 "saved-parent-ns-sets-entries", Logging::Loggable(SyncRes::getSaveParentsNSSetsSize()),
1140 "outqueries-per-query", Logging::Loggable(ratePercentage(outqueries, syncresqueries)));
1141 log->info(Logr::Info, report,
1142 "throttled-queries-perc", Logging::Loggable(ratePercentage(throttledqueries, outqueries + throttledqueries)),
1143 "tcp-outqueries", Logging::Loggable(tcpoutqueries),
1144 "dot-outqueries", Logging::Loggable(dotoutqueries),
1145 "idle-tcpout-connections", Logging::Loggable(getCurrentIdleTCPConnections()),
1146 "concurrent-queries", Logging::Loggable(broadcastAccFunction<uint64_t>(pleaseGetConcurrentQueries)),
1147 "outgoing-timeouts", Logging::Loggable(outgoingtimeouts));
1148 log->info(Logr::Info, report,
1149 "packetcache-entries", Logging::Loggable(pcSize),
1150 "packetcache-hitratio-perc", Logging::Loggable(ratePercentage(pcHits, qcounter)),
1151 "taskqueue-pushed", Logging::Loggable(taskPushes),
1152 "taskqueue-expired", Logging::Loggable(taskExpired),
1153 "taskqueue-size", Logging::Loggable(taskSize));
1154 }
1155 size_t idx = 0;
1156 for (const auto& threadInfo : RecThreadInfo::infos()) {
1157 if (threadInfo.isWorker()) {
1158 SLOG(g_log << Logger::Notice << "stats: thread " << idx << " has been distributed " << threadInfo.getNumberOfDistributedQueries() << " queries" << endl,
1159 log->info(Logr::Info, "Queries handled by thread", "thread", Logging::Loggable(idx), "tname", Logging::Loggable(threadInfo.getName()), "count", Logging::Loggable(threadInfo.getNumberOfDistributedQueries())));
1160 ++idx;
1161 }
1162 }
1163 time_t now = time(nullptr);
1164 if (lastOutputTime != 0 && lastQueryCount != 0 && now != lastOutputTime) {
1165 SLOG(g_log << Logger::Notice << "stats: " << (qcounter - lastQueryCount) / (now - lastOutputTime) << " qps (average over " << (now - lastOutputTime) << " seconds)" << endl,
1166 log->info(Logr::Info, "Periodic QPS report", "qps", Logging::Loggable((qcounter - lastQueryCount) / (now - lastOutputTime)),
1167 "averagedOver", Logging::Loggable(now - lastOutputTime)));
1168 }
1169 lastOutputTime = now;
1170 lastQueryCount = qcounter;
1171 }
1172 else if (statsWanted) {
1173 SLOG(g_log << Logger::Notice << "stats: no stats yet!" << endl,
1174 log->info(Logr::Notice, "No stats yet"));
1175 }
1176
1177 statsWanted = false;
1178 }
1179
1180 static std::shared_ptr<NetmaskGroup> parseACL(const std::string& aclFile, const std::string& aclSetting, Logr::log_t log)
1181 {
1182 auto result = std::make_shared<NetmaskGroup>();
1183
1184 const string file = ::arg()[aclFile];
1185
1186 if (!file.empty()) {
1187 if (boost::ends_with(file, ".yml")) {
1188 ::rust::vec<::rust::string> vec;
1189 pdns::settings::rec::readYamlAllowFromFile(file, vec, log);
1190 for (const auto& subnet : vec) {
1191 result->addMask(string(subnet));
1192 }
1193 }
1194 else {
1195 string line;
1196 ifstream ifs(file);
1197 if (!ifs) {
1198 int err = errno;
1199 throw runtime_error("Could not open '" + file + "': " + stringerror(err));
1200 }
1201
1202 while (getline(ifs, line)) {
1203 auto pos = line.find('#');
1204 if (pos != string::npos) {
1205 line.resize(pos);
1206 }
1207 boost::trim(line);
1208 if (line.empty()) {
1209 continue;
1210 }
1211
1212 result->addMask(line);
1213 }
1214 }
1215 SLOG(g_log << Logger::Info << "Done parsing " << result->size() << " " << aclSetting << " ranges from file '" << file << "' - overriding '" << aclSetting << "' setting" << endl,
1216 log->info(Logr::Info, "Done parsing ranges from file, will override setting", "setting", Logging::Loggable(aclSetting),
1217 "number", Logging::Loggable(result->size()), "file", Logging::Loggable(file)));
1218 }
1219 else if (!::arg()[aclSetting].empty()) {
1220 vector<string> ips;
1221 stringtok(ips, ::arg()[aclSetting], ", ");
1222
1223 for (const auto& address : ips) {
1224 result->addMask(address);
1225 }
1226 if (!g_slogStructured) {
1227 g_log << Logger::Info << aclSetting << ": ";
1228 for (auto i = ips.begin(); i != ips.end(); ++i) {
1229 if (i != ips.begin()) {
1230 g_log << Logger::Info << ", ";
1231 }
1232 g_log << Logger::Info << *i;
1233 }
1234 g_log << Logger::Info << endl;
1235 }
1236 else {
1237 log->info(Logr::Info, "Setting access control", "acl", Logging::Loggable(aclSetting), "addresses", Logging::IterLoggable(ips.begin(), ips.end()));
1238 }
1239 }
1240
1241 return result;
1242 }
1243
1244 static void* pleaseSupplantAllowFrom(std::shared_ptr<NetmaskGroup> nmgroup)
1245 {
1246 t_allowFrom = std::move(nmgroup);
1247 return nullptr;
1248 }
1249
1250 static void* pleaseSupplantAllowNotifyFrom(std::shared_ptr<NetmaskGroup> nmgroup)
1251 {
1252 t_allowNotifyFrom = std::move(nmgroup);
1253 return nullptr;
1254 }
1255
1256 void* pleaseSupplantAllowNotifyFor(std::shared_ptr<notifyset_t> allowNotifyFor)
1257 {
1258 t_allowNotifyFor = std::move(allowNotifyFor);
1259 return nullptr;
1260 }
1261
1262 void parseACLs()
1263 {
1264 auto log = g_slog->withName("config");
1265
1266 static bool l_initialized;
1267
1268 if (l_initialized) { // only reload configuration file on second call
1269
1270 string configName = ::arg()["config-dir"] + "/recursor";
1271 if (!::arg()["config-name"].empty()) {
1272 configName = ::arg()["config-dir"] + "/recursor-" + ::arg()["config-name"];
1273 }
1274 cleanSlashes(configName);
1275
1276 if (g_yamlSettings) {
1277 configName += ".yml";
1278 string msg;
1279 pdns::rust::settings::rec::Recursorsettings settings;
1280 // XXX Does ::arg()["include-dir"] have the right value, i.e. potentially overriden by command line?
1281 auto yamlstatus = pdns::settings::rec::readYamlSettings(configName, ::arg()["include-dir"], settings, msg, log);
1282
1283 switch (yamlstatus) {
1284 case pdns::settings::rec::YamlSettingsStatus::CannotOpen:
1285 throw runtime_error("Unable to open '" + configName + "': " + msg);
1286 break;
1287 case pdns::settings::rec::YamlSettingsStatus::PresentButFailed:
1288 throw runtime_error("Error processing '" + configName + "': " + msg);
1289 break;
1290 case pdns::settings::rec::YamlSettingsStatus::OK:
1291 pdns::settings::rec::processAPIDir(arg()["include-dir"], settings, log);
1292 // Does *not* set include-dir
1293 pdns::settings::rec::setArgsForACLRelatedSettings(settings);
1294 break;
1295 }
1296 }
1297 else {
1298 configName += ".conf";
1299 if (!::arg().preParseFile(configName, "allow-from-file")) {
1300 throw runtime_error("Unable to re-parse configuration file '" + configName + "'");
1301 }
1302 ::arg().preParseFile(configName, "allow-from", LOCAL_NETS);
1303
1304 if (!::arg().preParseFile(configName, "allow-notify-from-file")) {
1305 throw runtime_error("Unable to re-parse configuration file '" + configName + "'");
1306 }
1307 ::arg().preParseFile(configName, "allow-notify-from");
1308
1309 ::arg().preParseFile(configName, "include-dir");
1310 ::arg().preParse(g_argc, g_argv, "include-dir");
1311
1312 // then process includes
1313 std::vector<std::string> extraConfigs;
1314 ::arg().gatherIncludes(::arg()["include-dir"], ".conf", extraConfigs);
1315
1316 for (const std::string& fileName : extraConfigs) {
1317 if (!::arg().preParseFile(fileName, "allow-from-file", ::arg()["allow-from-file"])) {
1318 throw runtime_error("Unable to re-parse configuration file include '" + fileName + "'");
1319 }
1320 if (!::arg().preParseFile(fileName, "allow-from", ::arg()["allow-from"])) {
1321 throw runtime_error("Unable to re-parse configuration file include '" + fileName + "'");
1322 }
1323
1324 if (!::arg().preParseFile(fileName, "allow-notify-from-file", ::arg()["allow-notify-from-file"])) {
1325 throw runtime_error("Unable to re-parse configuration file include '" + fileName + "'");
1326 }
1327 if (!::arg().preParseFile(fileName, "allow-notify-from", ::arg()["allow-notify-from"])) {
1328 throw runtime_error("Unable to re-parse configuration file include '" + fileName + "'");
1329 }
1330 }
1331 }
1332 }
1333 // Process command line args potentially overriding settings read from file
1334 ::arg().preParse(g_argc, g_argv, "allow-from-file");
1335 ::arg().preParse(g_argc, g_argv, "allow-from");
1336
1337 ::arg().preParse(g_argc, g_argv, "allow-notify-from-file");
1338 ::arg().preParse(g_argc, g_argv, "allow-notify-from");
1339
1340 auto allowFrom = parseACL("allow-from-file", "allow-from", log);
1341
1342 if (allowFrom->empty()) {
1343 if (::arg()["local-address"] != "127.0.0.1" && ::arg().asNum("local-port") == 53) {
1344 SLOG(g_log << Logger::Warning << "WARNING: Allowing queries from all IP addresses - this can be a security risk!" << endl,
1345 log->info(Logr::Warning, "WARNING: Allowing queries from all IP addresses - this can be a security risk!"));
1346 }
1347 allowFrom = nullptr;
1348 }
1349
1350 g_initialAllowFrom = allowFrom;
1351 // coverity[copy_constructor_call] maybe this can be avoided, but be careful as pointers get passed to other threads
1352 broadcastFunction([=] { return pleaseSupplantAllowFrom(allowFrom); });
1353
1354 auto allowNotifyFrom = parseACL("allow-notify-from-file", "allow-notify-from", log);
1355
1356 g_initialAllowNotifyFrom = allowNotifyFrom;
1357 // coverity[copy_constructor_call] maybe this can be avoided, but be careful as pointers get passed to other threads
1358 broadcastFunction([=] { return pleaseSupplantAllowNotifyFrom(allowNotifyFrom); });
1359
1360 l_initialized = true;
1361 }
1362
1363 void broadcastFunction(const pipefunc_t& func)
1364 {
1365 /* This function might be called by the worker with t_id 0 during startup
1366 for the initialization of ACLs and domain maps. After that it should only
1367 be called by the handler. */
1368
1369 if (RecThreadInfo::infos().empty() && RecThreadInfo::id() == 0) {
1370 /* the handler and distributors will call themselves below, but
1371 during startup we get called while g_threadInfos has not been
1372 populated yet to update the ACL or domain maps, so we need to
1373 handle that case.
1374 */
1375 func();
1376 }
1377
1378 unsigned int thread = 0;
1379 for (const auto& threadInfo : RecThreadInfo::infos()) {
1380 if (thread++ == RecThreadInfo::id()) {
1381 func(); // don't write to ourselves!
1382 continue;
1383 }
1384
1385 ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: manual ownership handling
1386 tmsg->func = func;
1387 tmsg->wantAnswer = true;
1388 if (write(threadInfo.getPipes().writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // NOLINT: sizeof correct
1389 delete tmsg; // NOLINT: manual ownership handling
1390
1391 unixDie("write to thread pipe returned wrong size or error");
1392 }
1393
1394 string* resp = nullptr;
1395 if (read(threadInfo.getPipes().readFromThread, &resp, sizeof(resp)) != sizeof(resp)) { // NOLINT: sizeof correct
1396 unixDie("read from thread pipe returned wrong size or error");
1397 }
1398
1399 if (resp != nullptr) {
1400 delete resp; // NOLINT: manual ownership handling
1401 resp = nullptr;
1402 }
1403 // coverity[leaked_storage]
1404 }
1405 }
1406
1407 template <class T>
1408 void* voider(const std::function<T*()>& func)
1409 {
1410 return func();
1411 }
1412
1413 static vector<ComboAddress>& operator+=(vector<ComboAddress>& lhs, const vector<ComboAddress>& rhs)
1414 {
1415 lhs.insert(lhs.end(), rhs.begin(), rhs.end());
1416 return lhs;
1417 }
1418
1419 static vector<pair<DNSName, uint16_t>>& operator+=(vector<pair<DNSName, uint16_t>>& lhs, const vector<pair<DNSName, uint16_t>>& rhs)
1420 {
1421 lhs.insert(lhs.end(), rhs.begin(), rhs.end());
1422 return lhs;
1423 }
1424
1425 static ProxyMappingStats_t& operator+=(ProxyMappingStats_t& lhs, const ProxyMappingStats_t& rhs)
1426 {
1427 for (const auto& [key, entry] : rhs) {
1428 lhs[key].netmaskMatches += entry.netmaskMatches;
1429 lhs[key].suffixMatches += entry.suffixMatches;
1430 }
1431 return lhs;
1432 }
1433
1434 static RemoteLoggerStats_t& operator+=(RemoteLoggerStats_t& lhs, const RemoteLoggerStats_t& rhs)
1435 {
1436 for (const auto& [key, entry] : rhs) {
1437 lhs[key] += entry;
1438 }
1439 return lhs;
1440 }
1441
1442 // This function should only be called by the handler to gather
1443 // metrics, wipe the cache, reload the Lua script (not the Lua config)
1444 // or change the current trace regex, and by the SNMP thread to gather
1445 // metrics.
1446 // Note that this currently skips the handler, but includes the taskThread(s).
1447 template <class T>
1448 T broadcastAccFunction(const std::function<T*()>& func)
1449 {
1450 if (!RecThreadInfo::self().isHandler()) {
1451 SLOG(g_log << Logger::Error << "broadcastAccFunction has been called by a worker (" << RecThreadInfo::id() << ")" << endl,
1452 g_slog->withName("runtime")->info(Logr::Critical, "broadcastAccFunction has been called by a worker")); // tid will be added
1453 _exit(1);
1454 }
1455
1456 unsigned int thread = 0;
1457 T ret = T();
1458 for (const auto& threadInfo : RecThreadInfo::infos()) {
1459 if (thread++ == RecThreadInfo::id()) {
1460 continue;
1461 }
1462
1463 const auto& tps = threadInfo.getPipes();
1464 ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: manual ownership handling
1465 tmsg->func = [func] { return voider<T>(func); };
1466 tmsg->wantAnswer = true;
1467
1468 if (write(tps.writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // NOLINT:: sizeof correct
1469 delete tmsg; // NOLINT: manual ownership handling
1470 unixDie("write to thread pipe returned wrong size or error");
1471 }
1472
1473 T* resp = nullptr;
1474 if (read(tps.readFromThread, &resp, sizeof(resp)) != sizeof(resp)) // NOLINT: sizeof correct
1475 unixDie("read from thread pipe returned wrong size or error");
1476
1477 if (resp) {
1478 ret += *resp;
1479 delete resp; // NOLINT: manual ownership handling
1480 resp = nullptr;
1481 }
1482 // coverity[leaked_storage]
1483 }
1484 return ret;
1485 }
1486
1487 template string broadcastAccFunction(const std::function<string*()>& fun); // explicit instantiation
1488 template RecursorControlChannel::Answer broadcastAccFunction(const std::function<RecursorControlChannel::Answer*()>& fun); // explicit instantiation
1489 template uint64_t broadcastAccFunction(const std::function<uint64_t*()>& fun); // explicit instantiation
1490 template vector<ComboAddress> broadcastAccFunction(const std::function<vector<ComboAddress>*()>& fun); // explicit instantiation
1491 template vector<pair<DNSName, uint16_t>> broadcastAccFunction(const std::function<vector<pair<DNSName, uint16_t>>*()>& fun); // explicit instantiation
1492 template ThreadTimes broadcastAccFunction(const std::function<ThreadTimes*()>& fun);
1493 template ProxyMappingStats_t broadcastAccFunction(const std::function<ProxyMappingStats_t*()>& fun);
1494 template RemoteLoggerStats_t broadcastAccFunction(const std::function<RemoteLoggerStats_t*()>& fun);
1495
1496 static int initNet(Logr::log_t log)
1497 {
1498 checkLinuxIPv6Limits(log);
1499 try {
1500 pdns::parseQueryLocalAddress(::arg()["query-local-address"]);
1501 }
1502 catch (std::exception& e) {
1503 SLOG(g_log << Logger::Error << "Assigning local query addresses: " << e.what(),
1504 log->error(Logr::Error, e.what(), "Unable to assign local query address"));
1505 return 99;
1506 }
1507
1508 if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET)) {
1509 SyncRes::s_doIPv4 = true;
1510 SLOG(g_log << Logger::Warning << "Enabling IPv4 transport for outgoing queries" << endl,
1511 log->info(Logr::Notice, "Enabling IPv4 transport for outgoing queries"));
1512 }
1513 else {
1514 SLOG(g_log << Logger::Warning << "NOT using IPv4 for outgoing queries - add an IPv4 address (like '0.0.0.0') to query-local-address to enable" << endl,
1515 log->info(Logr::Warning, "NOT using IPv4 for outgoing queries - add an IPv4 address (like '0.0.0.0') to query-local-address to enable"));
1516 }
1517
1518 if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET6)) {
1519 SyncRes::s_doIPv6 = true;
1520 SLOG(g_log << Logger::Warning << "Enabling IPv6 transport for outgoing queries" << endl,
1521 log->info(Logr::Notice, "Enabling IPv6 transport for outgoing queries"));
1522 }
1523 else {
1524 SLOG(g_log << Logger::Warning << "NOT using IPv6 for outgoing queries - add an IPv6 address (like '::') to query-local-address to enable" << endl,
1525 log->info(Logr::Warning, "NOT using IPv6 for outgoing queries - add an IPv6 address (like '::') to query-local-address to enable"));
1526 }
1527
1528 if (!SyncRes::s_doIPv6 && !SyncRes::s_doIPv4) {
1529 SLOG(g_log << Logger::Error << "No outgoing addresses configured! Can not continue" << endl,
1530 log->info(Logr::Error, "No outgoing addresses configured! Can not continue"));
1531 return 99;
1532 }
1533 return 0;
1534 }
1535
1536 static int initDNSSEC(Logr::log_t log)
1537 {
1538 if (::arg()["dnssec"] == "off") {
1539 g_dnssecmode = DNSSECMode::Off;
1540 }
1541 else if (::arg()["dnssec"] == "process-no-validate") {
1542 g_dnssecmode = DNSSECMode::ProcessNoValidate;
1543 }
1544 else if (::arg()["dnssec"] == "process") {
1545 g_dnssecmode = DNSSECMode::Process;
1546 }
1547 else if (::arg()["dnssec"] == "validate") {
1548 g_dnssecmode = DNSSECMode::ValidateAll;
1549 }
1550 else if (::arg()["dnssec"] == "log-fail") {
1551 g_dnssecmode = DNSSECMode::ValidateForLog;
1552 }
1553 else {
1554 SLOG(g_log << Logger::Error << "Unknown DNSSEC mode " << ::arg()["dnssec"] << endl,
1555 log->info(Logr::Error, "Unknown DNSSEC mode", "dnssec", Logging::Loggable(::arg()["dnssec"])));
1556 return 1;
1557 }
1558
1559 g_signatureInceptionSkew = ::arg().asNum("signature-inception-skew");
1560 if (g_signatureInceptionSkew < 0) {
1561 SLOG(g_log << Logger::Error << "A negative value for 'signature-inception-skew' is not allowed" << endl,
1562 log->info(Logr::Error, "A negative value for 'signature-inception-skew' is not allowed"));
1563 return 1;
1564 }
1565
1566 g_dnssecLogBogus = ::arg().mustDo("dnssec-log-bogus");
1567 g_maxNSEC3Iterations = ::arg().asNum("nsec3-max-iterations");
1568 g_maxRRSIGsPerRecordToConsider = ::arg().asNum("max-rrsigs-per-record");
1569 g_maxNSEC3sPerRecordToConsider = ::arg().asNum("max-nsec3s-per-record");
1570 g_maxDNSKEYsToConsider = ::arg().asNum("max-dnskeys");
1571 g_maxDSsToConsider = ::arg().asNum("max-ds-per-zone");
1572
1573 vector<string> nums;
1574 bool automatic = true;
1575 if (!::arg()["dnssec-disabled-algorithms"].empty()) {
1576 automatic = false;
1577 stringtok(nums, ::arg()["dnssec-disabled-algorithms"], ", ");
1578 for (const auto& num : nums) {
1579 DNSCryptoKeyEngine::switchOffAlgorithm(pdns::checked_stoi<unsigned int>(num));
1580 }
1581 }
1582 else {
1583 for (auto algo : {DNSSECKeeper::RSASHA1, DNSSECKeeper::RSASHA1NSEC3SHA1}) {
1584 if (!DNSCryptoKeyEngine::verifyOne(algo)) {
1585 DNSCryptoKeyEngine::switchOffAlgorithm(algo);
1586 nums.push_back(std::to_string(algo));
1587 }
1588 }
1589 }
1590 if (!nums.empty()) {
1591 if (!g_slogStructured) {
1592 g_log << Logger::Warning << (automatic ? "Automatically" : "Manually") << " disabled DNSSEC algorithms: ";
1593 for (auto i = nums.begin(); i != nums.end(); ++i) {
1594 if (i != nums.begin()) {
1595 g_log << Logger::Warning << ", ";
1596 }
1597 g_log << Logger::Warning << *i;
1598 }
1599 g_log << Logger::Warning << endl;
1600 }
1601 else {
1602 log->info(Logr::Notice, "Disabled DNSSEC algorithms", "automatically", Logging::Loggable(automatic), "algorithms", Logging::IterLoggable(nums.begin(), nums.end()));
1603 }
1604 }
1605
1606 return 0;
1607 }
1608
1609 static void initDontQuery(Logr::log_t log)
1610 {
1611 if (!::arg()["dont-query"].empty()) {
1612 vector<string> ips;
1613 stringtok(ips, ::arg()["dont-query"], ", ");
1614 ips.emplace_back("0.0.0.0");
1615 ips.emplace_back("::");
1616
1617 for (const auto& anIP : ips) {
1618 SyncRes::addDontQuery(anIP);
1619 }
1620 if (!g_slogStructured) {
1621 g_log << Logger::Warning << "Will not send queries to: ";
1622 for (auto i = ips.begin(); i != ips.end(); ++i) {
1623 if (i != ips.begin()) {
1624 g_log << Logger::Warning << ", ";
1625 }
1626 g_log << Logger::Warning << *i;
1627 }
1628 g_log << Logger::Warning << endl;
1629 }
1630 else {
1631 log->info(Logr::Notice, "Will not send queries to", "addresses", Logging::IterLoggable(ips.begin(), ips.end()));
1632 }
1633 }
1634 }
1635
1636 static int initSyncRes(Logr::log_t log)
1637 {
1638 SyncRes::s_minimumTTL = ::arg().asNum("minimum-ttl-override");
1639 SyncRes::s_minimumECSTTL = ::arg().asNum("ecs-minimum-ttl-override");
1640 SyncRes::s_maxnegttl = ::arg().asNum("max-negative-ttl");
1641 SyncRes::s_maxbogusttl = ::arg().asNum("max-cache-bogus-ttl");
1642 SyncRes::s_maxcachettl = max(::arg().asNum("max-cache-ttl"), 15);
1643
1644 SyncRes::s_packetcachettl = ::arg().asNum("packetcache-ttl");
1645 // Cap the packetcache-servfail-ttl and packetcache-negative-ttl to packetcache-ttl
1646 SyncRes::s_packetcacheservfailttl = std::min(static_cast<unsigned int>(::arg().asNum("packetcache-servfail-ttl")), SyncRes::s_packetcachettl);
1647 SyncRes::s_packetcachenegativettl = std::min(static_cast<unsigned int>(::arg().asNum("packetcache-negative-ttl")), SyncRes::s_packetcachettl);
1648
1649 SyncRes::s_serverdownmaxfails = ::arg().asNum("server-down-max-fails");
1650 SyncRes::s_serverdownthrottletime = ::arg().asNum("server-down-throttle-time");
1651 SyncRes::s_unthrottle_n = ::arg().asNum("bypass-server-throttling-probability");
1652 SyncRes::s_nonresolvingnsmaxfails = ::arg().asNum("non-resolving-ns-max-fails");
1653 SyncRes::s_nonresolvingnsthrottletime = ::arg().asNum("non-resolving-ns-throttle-time");
1654 SyncRes::s_serverID = ::arg()["server-id"];
1655 // This bound is dynamically adjusted in SyncRes, depending on qname minimization being active
1656 SyncRes::s_maxqperq = ::arg().asNum("max-qperq");
1657 SyncRes::s_maxnsperresolve = ::arg().asNum("max-ns-per-resolve");
1658 SyncRes::s_maxnsaddressqperq = ::arg().asNum("max-ns-address-qperq");
1659 SyncRes::s_maxtotusec = 1000 * ::arg().asNum("max-total-msec");
1660 SyncRes::s_maxdepth = ::arg().asNum("max-recursion-depth");
1661 SyncRes::s_maxvalidationsperq = ::arg().asNum("max-signature-validations-per-query");
1662 SyncRes::s_maxnsec3iterationsperq = ::arg().asNum("max-nsec3-hash-computations-per-query");
1663 SyncRes::s_rootNXTrust = ::arg().mustDo("root-nx-trust");
1664 SyncRes::s_refresh_ttlperc = ::arg().asNum("refresh-on-ttl-perc");
1665 SyncRes::s_locked_ttlperc = ::arg().asNum("record-cache-locked-ttl-perc");
1666 RecursorPacketCache::s_refresh_ttlperc = SyncRes::s_refresh_ttlperc;
1667 SyncRes::s_tcp_fast_open = ::arg().asNum("tcp-fast-open");
1668 SyncRes::s_tcp_fast_open_connect = ::arg().mustDo("tcp-fast-open-connect");
1669
1670 SyncRes::s_dot_to_port_853 = ::arg().mustDo("dot-to-port-853");
1671 SyncRes::s_event_trace_enabled = ::arg().asNum("event-trace-enabled");
1672 SyncRes::s_save_parent_ns_set = ::arg().mustDo("save-parent-ns-set");
1673 SyncRes::s_max_busy_dot_probes = ::arg().asNum("max-busy-dot-probes");
1674 {
1675 uint64_t sse = ::arg().asNum("serve-stale-extensions");
1676 if (sse > std::numeric_limits<uint16_t>::max()) {
1677 SLOG(g_log << Logger::Error << "Illegal serve-stale-extensions value: " << sse << "; range = 0..65536" << endl,
1678 log->info(Logr::Error, "Illegal serve-stale-extensions value; range = 0..65536", "value", Logging::Loggable(sse)));
1679 return 1;
1680 }
1681 MemRecursorCache::s_maxServedStaleExtensions = sse;
1682 NegCache::s_maxServedStaleExtensions = sse;
1683 }
1684
1685 if (SyncRes::s_tcp_fast_open_connect) {
1686 checkFastOpenSysctl(true, log);
1687 checkTFOconnect(log);
1688 }
1689 SyncRes::s_ecsipv4limit = ::arg().asNum("ecs-ipv4-bits");
1690 SyncRes::s_ecsipv6limit = ::arg().asNum("ecs-ipv6-bits");
1691 SyncRes::clearECSStats();
1692 SyncRes::s_ecsipv4cachelimit = ::arg().asNum("ecs-ipv4-cache-bits");
1693 SyncRes::s_ecsipv6cachelimit = ::arg().asNum("ecs-ipv6-cache-bits");
1694 SyncRes::s_ecsipv4nevercache = ::arg().mustDo("ecs-ipv4-never-cache");
1695 SyncRes::s_ecsipv6nevercache = ::arg().mustDo("ecs-ipv6-never-cache");
1696 SyncRes::s_ecscachelimitttl = ::arg().asNum("ecs-cache-limit-ttl");
1697
1698 SyncRes::s_qnameminimization = ::arg().mustDo("qname-minimization");
1699 SyncRes::s_minimize_one_label = ::arg().asNum("qname-minimize-one-label");
1700 SyncRes::s_max_minimize_count = ::arg().asNum("qname-max-minimize-count");
1701
1702 SyncRes::s_hardenNXD = SyncRes::HardenNXD::DNSSEC;
1703 string value = ::arg()["nothing-below-nxdomain"];
1704 if (value == "yes") {
1705 SyncRes::s_hardenNXD = SyncRes::HardenNXD::Yes;
1706 }
1707 else if (value == "no") {
1708 SyncRes::s_hardenNXD = SyncRes::HardenNXD::No;
1709 }
1710 else if (value != "dnssec") {
1711 SLOG(g_log << Logger::Error << "Unknown nothing-below-nxdomain mode: " << value << endl,
1712 log->info(Logr::Error, "Unknown nothing-below-nxdomain mode", "mode", Logging::Loggable(value)));
1713 return 1;
1714 }
1715
1716 if (!::arg().isEmpty("ecs-scope-zero-address")) {
1717 ComboAddress scopeZero(::arg()["ecs-scope-zero-address"]);
1718 SyncRes::setECSScopeZeroAddress(Netmask(scopeZero, scopeZero.isIPv4() ? 32 : 128));
1719 }
1720 else {
1721 Netmask netmask;
1722 bool done = false;
1723
1724 auto addr = pdns::getNonAnyQueryLocalAddress(AF_INET);
1725 if (addr.sin4.sin_family != 0) {
1726 netmask = Netmask(addr, 32);
1727 done = true;
1728 }
1729 if (!done) {
1730 addr = pdns::getNonAnyQueryLocalAddress(AF_INET6);
1731 if (addr.sin4.sin_family != 0) {
1732 netmask = Netmask(addr, 128);
1733 done = true;
1734 }
1735 }
1736 if (!done) {
1737 netmask = Netmask(ComboAddress("127.0.0.1"), 32);
1738 }
1739 SyncRes::setECSScopeZeroAddress(netmask);
1740 }
1741
1742 SyncRes::parseEDNSSubnetAllowlist(::arg()["edns-subnet-whitelist"]);
1743 SyncRes::parseEDNSSubnetAllowlist(::arg()["edns-subnet-allow-list"]);
1744 SyncRes::parseEDNSSubnetAddFor(::arg()["ecs-add-for"]);
1745 g_useIncomingECS = ::arg().mustDo("use-incoming-edns-subnet");
1746 return 0;
1747 }
1748
1749 static void initDistribution(Logr::log_t log)
1750 {
1751 g_balancingFactor = ::arg().asDouble("distribution-load-factor");
1752 if (g_balancingFactor != 0.0 && g_balancingFactor < 1.0) {
1753 g_balancingFactor = 0.0;
1754 SLOG(g_log << Logger::Warning << "Asked to run with a distribution-load-factor below 1.0, disabling it instead" << endl,
1755 log->info(Logr::Warning, "Asked to run with a distribution-load-factor below 1.0, disabling it instead"));
1756 }
1757
1758 #ifdef SO_REUSEPORT
1759 g_reusePort = ::arg().mustDo("reuseport");
1760 #endif
1761
1762 RecThreadInfo::infos().resize(RecThreadInfo::numRecursorThreads());
1763
1764 if (g_reusePort) {
1765 unsigned int threadNum = 1;
1766 if (RecThreadInfo::weDistributeQueries()) {
1767 /* first thread is the handler, then distributors */
1768 for (unsigned int i = 0; i < RecThreadInfo::numDistributors(); i++, threadNum++) {
1769 auto& info = RecThreadInfo::info(threadNum);
1770 auto& deferredAdds = info.getDeferredAdds();
1771 makeUDPServerSockets(deferredAdds, log);
1772 }
1773 }
1774 else {
1775 /* first thread is the handler, there is no distributor here and workers are accepting queries */
1776 for (unsigned int i = 0; i < RecThreadInfo::numUDPWorkers(); i++, threadNum++) {
1777 auto& info = RecThreadInfo::info(threadNum);
1778 auto& deferredAdds = info.getDeferredAdds();
1779 makeUDPServerSockets(deferredAdds, log);
1780 }
1781 }
1782 threadNum = 1 + RecThreadInfo::numDistributors() + RecThreadInfo::numUDPWorkers();
1783 for (unsigned int i = 0; i < RecThreadInfo::numTCPWorkers(); i++, threadNum++) {
1784 auto& info = RecThreadInfo::info(threadNum);
1785 auto& deferredAdds = info.getDeferredAdds();
1786 auto& tcpSockets = info.getTCPSockets();
1787 makeTCPServerSockets(deferredAdds, tcpSockets, log);
1788 }
1789 }
1790 else {
1791 std::set<int> tcpSockets;
1792 /* we don't have reuseport so we can only open one socket per
1793 listening addr:port and everyone will listen on it */
1794 makeUDPServerSockets(s_deferredUDPadds, log);
1795 makeTCPServerSockets(s_deferredTCPadds, tcpSockets, log);
1796
1797 // TCP queries are handled by TCP workers
1798 for (unsigned int i = 0; i < RecThreadInfo::numTCPWorkers(); i++) {
1799 auto& info = RecThreadInfo::info(i + 1 + RecThreadInfo::numDistributors() + RecThreadInfo::numUDPWorkers());
1800 info.setTCPSockets(tcpSockets);
1801 }
1802 }
1803 }
1804
1805 static int initForks(Logr::log_t log)
1806 {
1807 int forks = 0;
1808 for (; forks < ::arg().asNum("processes") - 1; ++forks) {
1809 if (fork() == 0) { // we are child
1810 break;
1811 }
1812 }
1813
1814 if (::arg().mustDo("daemon")) {
1815 SLOG(g_log << Logger::Warning << "Calling daemonize, going to background" << endl,
1816 log->info(Logr::Warning, "Calling daemonize, going to background"));
1817 g_log.toConsole(Logger::Critical);
1818 daemonize(log);
1819 }
1820
1821 if (Utility::getpid() == 1) {
1822 /* We are running as pid 1, register sigterm and sigint handler
1823
1824 The Linux kernel will handle SIGTERM and SIGINT for all processes, except PID 1.
1825 It assumes that the processes running as pid 1 is an "init" like system.
1826 For years, this was a safe assumption, but containers change that: in
1827 most (all?) container implementations, the application itself is running
1828 as pid 1. This means that sending signals to those applications, will not
1829 be handled by default. Results might be "your container not responding
1830 when asking it to stop", or "ctrl-c not working even when the app is
1831 running in the foreground inside a container".
1832
1833 So TL;DR: If we're running pid 1 (container), we should handle SIGTERM and SIGINT ourselves */
1834
1835 signal(SIGTERM, termIntHandler);
1836 signal(SIGINT, termIntHandler);
1837 }
1838
1839 signal(SIGUSR1, usr1Handler);
1840 signal(SIGUSR2, usr2Handler);
1841 signal(SIGPIPE, SIG_IGN); // NOLINT: Posix API
1842 return forks;
1843 }
1844
1845 static int initPorts(Logr::log_t log)
1846 {
1847 int port = ::arg().asNum("udp-source-port-min");
1848 if (port < 1024 || port > 65535) {
1849 SLOG(g_log << Logger::Error << "Unable to launch, udp-source-port-min is not a valid port number" << endl,
1850 log->info(Logr::Error, "Unable to launch, udp-source-port-min is not a valid port number"));
1851 return 99; // this isn't going to fix itself either
1852 }
1853 g_minUdpSourcePort = port;
1854 port = ::arg().asNum("udp-source-port-max");
1855 if (port < 1024 || port > 65535 || port < g_minUdpSourcePort) {
1856 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,
1857 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"));
1858 return 99; // this isn't going to fix itself either
1859 }
1860 g_maxUdpSourcePort = port;
1861 std::vector<string> parts{};
1862 stringtok(parts, ::arg()["udp-source-port-avoid"], ", ");
1863 for (const auto& part : parts) {
1864 port = std::stoi(part);
1865 if (port < 1024 || port > 65535) {
1866 SLOG(g_log << Logger::Error << "Unable to launch, udp-source-port-avoid contains an invalid port number: " << part << endl,
1867 log->info(Logr::Error, "Unable to launch, udp-source-port-avoid contains an invalid port number", "port", Logging::Loggable(part)));
1868 return 99; // this isn't going to fix itself either
1869 }
1870 g_avoidUdpSourcePorts.insert(port);
1871 }
1872 return 0;
1873 }
1874
1875 static void initSNMP([[maybe_unused]] Logr::log_t log)
1876 {
1877 if (::arg().mustDo("snmp-agent")) {
1878 #ifdef HAVE_NET_SNMP
1879 string setting = ::arg()["snmp-daemon-socket"];
1880 if (setting.empty()) {
1881 setting = ::arg()["snmp-master-socket"];
1882 }
1883 g_snmpAgent = std::make_shared<RecursorSNMPAgent>("recursor", setting);
1884 g_snmpAgent->run();
1885 #else
1886 const std::string msg = "snmp-agent set but SNMP support not compiled in";
1887 SLOG(g_log << Logger::Error << msg << endl,
1888 log->info(Logr::Error, msg));
1889 #endif // HAVE_NET_SNMP
1890 }
1891 }
1892
1893 static int initControl(Logr::log_t log, uid_t newuid, int forks)
1894 {
1895 if (!::arg()["chroot"].empty()) {
1896 #ifdef HAVE_SYSTEMD
1897 char* ns;
1898 ns = getenv("NOTIFY_SOCKET");
1899 if (ns != nullptr) {
1900 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,
1901 log->info(Logr::Error, "Unable to chroot when running from systemd. Please disable chroot= or set the 'Type' for this service to 'simple'"));
1902 return 1;
1903 }
1904 #endif
1905 if (chroot(::arg()["chroot"].c_str()) < 0 || chdir("/") < 0) {
1906 int err = errno;
1907 SLOG(g_log << Logger::Error << "Unable to chroot to '" + ::arg()["chroot"] + "': " << stringerror(err) << ", exiting" << endl,
1908 log->error(Logr::Error, err, "Unable to chroot", "chroot", Logging::Loggable(::arg()["chroot"])));
1909 return 1;
1910 }
1911 SLOG(g_log << Logger::Info << "Chrooted to '" << ::arg()["chroot"] << "'" << endl,
1912 log->info(Logr::Info, "Chrooted", "chroot", Logging::Loggable(::arg()["chroot"])));
1913 }
1914
1915 checkSocketDir(log);
1916
1917 g_pidfname = ::arg()["socket-dir"] + "/" + g_programname + ".pid";
1918 if (!g_pidfname.empty()) {
1919 unlink(g_pidfname.c_str()); // remove possible old pid file
1920 }
1921 writePid(log);
1922
1923 makeControlChannelSocket(::arg().asNum("processes") > 1 ? forks : -1);
1924
1925 Utility::dropUserPrivs(newuid);
1926 try {
1927 /* we might still have capabilities remaining, for example if we have been started as root
1928 without --setuid (please don't do that) or as an unprivileged user with ambient capabilities
1929 like CAP_NET_BIND_SERVICE.
1930 */
1931 dropCapabilities();
1932 }
1933 catch (const std::exception& e) {
1934 SLOG(g_log << Logger::Warning << e.what() << endl,
1935 log->error(Logr::Warning, e.what(), "Could not drop capabilities"));
1936 }
1937 return 0;
1938 }
1939
1940 static void initSuffixMatchNodes([[maybe_unused]] Logr::log_t log)
1941 {
1942 {
1943 SuffixMatchNode dontThrottleNames;
1944 vector<string> parts;
1945 stringtok(parts, ::arg()["dont-throttle-names"], " ,");
1946 for (const auto& part : parts) {
1947 dontThrottleNames.add(DNSName(part));
1948 }
1949 g_dontThrottleNames.setState(std::move(dontThrottleNames));
1950
1951 parts.clear();
1952 NetmaskGroup dontThrottleNetmasks;
1953 stringtok(parts, ::arg()["dont-throttle-netmasks"], " ,");
1954 for (const auto& part : parts) {
1955 dontThrottleNetmasks.addMask(Netmask(part));
1956 }
1957 g_dontThrottleNetmasks.setState(std::move(dontThrottleNetmasks));
1958 }
1959
1960 {
1961 SuffixMatchNode xdnssecNames;
1962 vector<string> parts;
1963 stringtok(parts, ::arg()["x-dnssec-names"], " ,");
1964 for (const auto& part : parts) {
1965 xdnssecNames.add(DNSName(part));
1966 }
1967 g_xdnssec.setState(std::move(xdnssecNames));
1968 }
1969
1970 {
1971 SuffixMatchNode dotauthNames;
1972 vector<string> parts;
1973 stringtok(parts, ::arg()["dot-to-auth-names"], " ,");
1974 #ifndef HAVE_DNS_OVER_TLS
1975 if (!parts.empty()) {
1976 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,
1977 log->info(Logr::Error, "dot-to-auth-names setting contains names, but Recursor was built without DNS over TLS support. Setting will be ignored"));
1978 }
1979 #endif
1980 for (const auto& part : parts) {
1981 dotauthNames.add(DNSName(part));
1982 }
1983 g_DoTToAuthNames.setState(std::move(dotauthNames));
1984 }
1985 }
1986
1987 static void initCarbon()
1988 {
1989 CarbonConfig config;
1990 stringtok(config.servers, arg()["carbon-server"], ", ");
1991 config.hostname = arg()["carbon-ourname"];
1992 config.instance_name = arg()["carbon-instance"];
1993 config.namespace_name = arg()["carbon-namespace"];
1994 g_carbonConfig.setState(std::move(config));
1995 }
1996
1997 static int initDNS64(Logr::log_t log)
1998 {
1999 if (!::arg()["dns64-prefix"].empty()) {
2000 try {
2001 auto dns64Prefix = Netmask(::arg()["dns64-prefix"]);
2002 if (dns64Prefix.getBits() != 96) {
2003 SLOG(g_log << Logger::Error << "Invalid prefix for 'dns64-prefix', the current implementation only supports /96 prefixes: " << ::arg()["dns64-prefix"] << endl,
2004 log->info(Logr::Error, "Invalid prefix for 'dns64-prefix', the current implementation only supports /96 prefixes", "prefix", Logging::Loggable(::arg()["dns64-prefix"])));
2005 return 1;
2006 }
2007 g_dns64Prefix = dns64Prefix.getNetwork();
2008 g_dns64PrefixReverse = reverseNameFromIP(*g_dns64Prefix);
2009 /* /96 is 24 nibbles + 2 for "ip6.arpa." */
2010 while (g_dns64PrefixReverse.countLabels() > 26) {
2011 g_dns64PrefixReverse.chopOff();
2012 }
2013 }
2014 catch (const NetmaskException& ne) {
2015 SLOG(g_log << Logger::Error << "Invalid prefix '" << ::arg()["dns64-prefix"] << "' for 'dns64-prefix': " << ne.reason << endl,
2016 log->info(Logr::Error, "Invalid prefix", "dns64-prefix", Logging::Loggable(::arg()["dns64-prefix"])));
2017 return 1;
2018 }
2019 }
2020 return 0;
2021 }
2022
2023 static int serviceMain(Logr::log_t log)
2024 {
2025 g_log.setName(g_programname);
2026 g_log.disableSyslog(::arg().mustDo("disable-syslog"));
2027 g_log.setTimestamps(::arg().mustDo("log-timestamp"));
2028 g_regressionTestMode = ::arg().mustDo("devonly-regression-test-mode");
2029
2030 if (!::arg()["logging-facility"].empty()) {
2031 int val = logFacilityToLOG(::arg().asNum("logging-facility"));
2032 if (val >= 0) {
2033 g_log.setFacility(val);
2034 }
2035 else {
2036 SLOG(g_log << Logger::Error << "Unknown logging facility " << ::arg().asNum("logging-facility") << endl,
2037 log->info(Logr::Error, "Unknown logging facility", "facility", Logging::Loggable(::arg().asNum("logging-facility"))));
2038 }
2039 }
2040
2041 g_disthashseed = dns_random_uint32();
2042
2043 int ret = initNet(log);
2044 if (ret != 0) {
2045 return ret;
2046 }
2047 // keep this ABOVE loadRecursorLuaConfig!
2048 ret = initDNSSEC(log);
2049 if (ret != 0) {
2050 return ret;
2051 }
2052 g_maxCacheEntries = ::arg().asNum("max-cache-entries");
2053
2054 luaConfigDelayedThreads delayedLuaThreads;
2055 try {
2056 ProxyMapping proxyMapping;
2057 loadRecursorLuaConfig(::arg()["lua-config-file"], delayedLuaThreads, proxyMapping);
2058 // Initial proxy mapping
2059 g_proxyMapping = proxyMapping.empty() ? nullptr : std::make_unique<ProxyMapping>(proxyMapping);
2060 }
2061 catch (PDNSException& e) {
2062 SLOG(g_log << Logger::Error << "Cannot load Lua configuration: " << e.reason << endl,
2063 log->error(Logr::Error, e.reason, "Cannot load Lua configuration"));
2064 return 1;
2065 }
2066
2067 parseACLs();
2068 initPublicSuffixList(::arg()["public-suffix-list-file"]);
2069
2070 initDontQuery(log);
2071
2072 RecThreadInfo::setWeDistributeQueries(::arg().mustDo("pdns-distributes-queries"));
2073 if (RecThreadInfo::weDistributeQueries()) {
2074 SLOG(g_log << Logger::Warning << "PowerDNS Recursor itself will distribute queries over threads" << endl,
2075 log->info(Logr::Notice, "PowerDNS Recursor itself will distribute queries over threads"));
2076 }
2077
2078 g_outgoingEDNSBufsize = ::arg().asNum("edns-outgoing-bufsize");
2079
2080 if (::arg()["trace"] == "fail") {
2081 SyncRes::setDefaultLogMode(SyncRes::Store);
2082 }
2083 else if (::arg().mustDo("trace")) {
2084 SyncRes::setDefaultLogMode(SyncRes::Log);
2085 ::arg().set("quiet") = "no";
2086 g_quiet = false;
2087 }
2088
2089 ret = initSyncRes(log);
2090 if (ret != 0) {
2091 return ret;
2092 }
2093
2094 g_proxyProtocolACL.toMasks(::arg()["proxy-protocol-from"]);
2095 g_proxyProtocolMaximumSize = ::arg().asNum("proxy-protocol-maximum-size");
2096
2097 ret = initDNS64(log);
2098 if (ret != 0) {
2099 return ret;
2100 }
2101 g_networkTimeoutMsec = ::arg().asNum("network-timeout");
2102
2103 std::tie(g_initialDomainMap, g_initialAllowNotifyFor) = parseZoneConfiguration(g_yamlSettings);
2104
2105 g_latencyStatSize = ::arg().asNum("latency-statistic-size");
2106
2107 g_logCommonErrors = ::arg().mustDo("log-common-errors");
2108 g_logRPZChanges = ::arg().mustDo("log-rpz-changes");
2109
2110 g_anyToTcp = ::arg().mustDo("any-to-tcp");
2111 g_allowNoRD = ::arg().mustDo("allow-no-rd");
2112 g_udpTruncationThreshold = ::arg().asNum("udp-truncation-threshold");
2113
2114 g_lowercaseOutgoing = ::arg().mustDo("lowercase-outgoing");
2115
2116 g_paddingFrom.toMasks(::arg()["edns-padding-from"]);
2117 if (::arg()["edns-padding-mode"] == "always") {
2118 g_paddingMode = PaddingMode::Always;
2119 }
2120 else if (::arg()["edns-padding-mode"] == "padded-queries-only") {
2121 g_paddingMode = PaddingMode::PaddedQueries;
2122 }
2123 else {
2124 SLOG(g_log << Logger::Error << "Unknown edns-padding-mode: " << ::arg()["edns-padding-mode"] << endl,
2125 log->info(Logr::Error, "Unknown edns-padding-mode", "edns-padding-mode", Logging::Loggable(::arg()["edns-padding-mode"])));
2126 return 1;
2127 }
2128 g_paddingTag = ::arg().asNum("edns-padding-tag");
2129 g_paddingOutgoing = ::arg().mustDo("edns-padding-out");
2130
2131 RecThreadInfo::setNumDistributorThreads(::arg().asNum("distributor-threads"));
2132 RecThreadInfo::setNumUDPWorkerThreads(::arg().asNum("threads"));
2133 if (RecThreadInfo::numUDPWorkers() < 1) {
2134 SLOG(g_log << Logger::Warning << "Asked to run with 0 threads, raising to 1 instead" << endl,
2135 log->info(Logr::Warning, "Asked to run with 0 threads, raising to 1 instead"));
2136 RecThreadInfo::setNumUDPWorkerThreads(1);
2137 }
2138 RecThreadInfo::setNumTCPWorkerThreads(::arg().asNum("tcp-threads"));
2139 if (RecThreadInfo::numTCPWorkers() < 1) {
2140 SLOG(g_log << Logger::Warning << "Asked to run with 0 TCP threads, raising to 1 instead" << endl,
2141 log->info(Logr::Warning, "Asked to run with 0 TCP threads, raising to 1 instead"));
2142 RecThreadInfo::setNumTCPWorkerThreads(1);
2143 }
2144
2145 g_maxMThreads = ::arg().asNum("max-mthreads");
2146
2147 int64_t maxInFlight = ::arg().asNum("max-concurrent-requests-per-tcp-connection");
2148 if (maxInFlight < 1 || maxInFlight > USHRT_MAX || maxInFlight >= g_maxMThreads) {
2149 SLOG(g_log << Logger::Warning << "Asked to run with illegal max-concurrent-requests-per-tcp-connection, setting to default (10)" << endl,
2150 log->info(Logr::Warning, "Asked to run with illegal max-concurrent-requests-per-tcp-connection, setting to default (10)"));
2151 TCPConnection::s_maxInFlight = 10;
2152 }
2153 else {
2154 TCPConnection::s_maxInFlight = maxInFlight;
2155 }
2156
2157 int64_t millis = ::arg().asNum("tcp-out-max-idle-ms");
2158 TCPOutConnectionManager::s_maxIdleTime = timeval{millis / 1000, (static_cast<suseconds_t>(millis) % 1000) * 1000};
2159 TCPOutConnectionManager::s_maxIdlePerAuth = ::arg().asNum("tcp-out-max-idle-per-auth");
2160 TCPOutConnectionManager::s_maxQueries = ::arg().asNum("tcp-out-max-queries");
2161 TCPOutConnectionManager::s_maxIdlePerThread = ::arg().asNum("tcp-out-max-idle-per-thread");
2162
2163 g_gettagNeedsEDNSOptions = ::arg().mustDo("gettag-needs-edns-options");
2164
2165 s_statisticsInterval = ::arg().asNum("statistics-interval");
2166
2167 SyncRes::s_addExtendedResolutionDNSErrors = ::arg().mustDo("extended-resolution-errors");
2168
2169 if (::arg().asNum("aggressive-nsec-cache-size") > 0) {
2170 if (g_dnssecmode == DNSSECMode::ValidateAll || g_dnssecmode == DNSSECMode::ValidateForLog || g_dnssecmode == DNSSECMode::Process) {
2171 g_aggressiveNSECCache = make_unique<AggressiveNSECCache>(::arg().asNum("aggressive-nsec-cache-size"));
2172 }
2173 else {
2174 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,
2175 log->info(Logr::Warning, "Aggressive NSEC/NSEC3 caching is enabled but DNSSEC validation is not set to 'validate', 'log-fail' or 'process', ignoring"));
2176 }
2177 }
2178
2179 AggressiveNSECCache::s_nsec3DenialProofMaxCost = ::arg().asNum("aggressive-cache-max-nsec3-hash-cost");
2180 AggressiveNSECCache::s_maxNSEC3CommonPrefix = static_cast<uint8_t>(std::round(std::log2(::arg().asNum("aggressive-cache-min-nsec3-hit-ratio"))));
2181 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,
2182 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)));
2183
2184 initSuffixMatchNodes(log);
2185 initCarbon();
2186 initDistribution(log);
2187
2188 #ifdef NOD_ENABLED
2189 // Setup newly observed domain globals
2190 setupNODGlobal();
2191 #endif /* NOD_ENABLED */
2192
2193 auto forks = initForks(log);
2194
2195 checkOrFixFDS(log);
2196
2197 #ifdef HAVE_LIBSODIUM
2198 if (sodium_init() == -1) {
2199 SLOG(g_log << Logger::Error << "Unable to initialize sodium crypto library" << endl,
2200 log->info(Logr::Error, "Unable to initialize sodium crypto library"));
2201 return 99;
2202 }
2203 #endif
2204
2205 openssl_thread_setup();
2206 openssl_seed();
2207
2208 gid_t newgid = 0;
2209 if (!::arg()["setgid"].empty()) {
2210 newgid = strToGID(::arg()["setgid"]);
2211 }
2212 uid_t newuid = 0;
2213 if (!::arg()["setuid"].empty()) {
2214 newuid = strToUID(::arg()["setuid"]);
2215 }
2216
2217 Utility::dropGroupPrivs(newuid, newgid);
2218
2219 ret = initControl(log, newuid, forks);
2220 if (ret != 0) {
2221 return ret;
2222 }
2223
2224 startLuaConfigDelayedThreads(delayedLuaThreads, g_luaconfs.getCopy().generation);
2225 delayedLuaThreads.rpzPrimaryThreads.clear(); // no longer needed
2226
2227 RecThreadInfo::makeThreadPipes(log);
2228
2229 g_tcpTimeout = ::arg().asNum("client-tcp-timeout");
2230 g_maxTCPPerClient = ::arg().asNum("max-tcp-per-client");
2231 g_tcpMaxQueriesPerConn = ::arg().asNum("max-tcp-queries-per-connection");
2232 g_maxUDPQueriesPerRound = ::arg().asNum("max-udp-queries-per-round");
2233
2234 g_useKernelTimestamp = ::arg().mustDo("protobuf-use-kernel-timestamp");
2235
2236 disableStats(StatComponent::API, ::arg()["stats-api-blacklist"]);
2237 disableStats(StatComponent::Carbon, ::arg()["stats-carbon-blacklist"]);
2238 disableStats(StatComponent::RecControl, ::arg()["stats-rec-control-blacklist"]);
2239 disableStats(StatComponent::SNMP, ::arg()["stats-snmp-blacklist"]);
2240
2241 disableStats(StatComponent::API, ::arg()["stats-api-disabled-list"]);
2242 disableStats(StatComponent::Carbon, ::arg()["stats-carbon-disabled-list"]);
2243 disableStats(StatComponent::RecControl, ::arg()["stats-rec-control-disabled-list"]);
2244 disableStats(StatComponent::SNMP, ::arg()["stats-snmp-disabled-list"]);
2245
2246 // Run before any thread doing stats related things
2247 registerAllStats();
2248
2249 initSNMP(log);
2250
2251 ret = initPorts(log);
2252 if (ret != 0) {
2253 return ret;
2254 }
2255
2256 return RecThreadInfo::runThreads(log);
2257 }
2258
2259 static void handlePipeRequest(int fileDesc, FDMultiplexer::funcparam_t& /* var */)
2260 {
2261 ThreadMSG* tmsg = nullptr;
2262
2263 if (read(fileDesc, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // fd == readToThread || fd == readQueriesToThread NOLINT: sizeof correct
2264 unixDie("read from thread pipe returned wrong size or error");
2265 }
2266
2267 void* resp = nullptr;
2268 try {
2269 resp = tmsg->func();
2270 }
2271 catch (std::exception& e) {
2272 if (g_logCommonErrors) {
2273 SLOG(g_log << Logger::Error << "PIPE function we executed created exception: " << e.what() << endl, // but what if they wanted an answer.. we send 0
2274 g_slog->withName("runtime")->error(Logr::Error, e.what(), "PIPE function we executed created exception", "exception", Logging::Loggable("std::exception")));
2275 }
2276 }
2277 catch (PDNSException& e) {
2278 if (g_logCommonErrors) {
2279 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
2280 g_slog->withName("runtime")->error(Logr::Error, e.reason, "PIPE function we executed created exception", "exception", Logging::Loggable("PDNSException")));
2281 }
2282 }
2283 if (tmsg->wantAnswer) {
2284 if (write(RecThreadInfo::self().getPipes().writeFromThread, &resp, sizeof(resp)) != sizeof(resp)) {
2285 delete tmsg; // NOLINT: manual ownership handling
2286 unixDie("write to thread pipe returned wrong size or error");
2287 }
2288 }
2289
2290 delete tmsg; // NOLINT: manual ownership handling
2291 }
2292
2293 static void handleRCC(int fileDesc, FDMultiplexer::funcparam_t& /* var */)
2294 {
2295 auto log = g_slog->withName("control");
2296 try {
2297 FDWrapper clientfd = accept(fileDesc, nullptr, nullptr);
2298 if (clientfd == -1) {
2299 throw PDNSException("accept failed");
2300 }
2301 string msg = g_rcc.recv(clientfd).d_str;
2302 SLOG(g_log << Logger::Info << "Received rec_control command '" << msg << "' via controlsocket" << endl,
2303 log->info(Logr::Info, "Received rec_control command via control socket", "command", Logging::Loggable(msg)));
2304
2305 RecursorControlParser::func_t* command = nullptr;
2306 auto answer = RecursorControlParser::getAnswer(clientfd, msg, &command);
2307
2308 g_rcc.send(clientfd, answer);
2309 command();
2310 }
2311 catch (const std::exception& e) {
2312 SLOG(g_log << Logger::Error << "Error dealing with control socket request: " << e.what() << endl,
2313 log->error(Logr::Error, e.what(), "Exception while dealing with control socket request", "exception", Logging::Loggable("std::exception")));
2314 }
2315 catch (const PDNSException& ae) {
2316 SLOG(g_log << Logger::Error << "Error dealing with control socket request: " << ae.reason << endl,
2317 log->error(Logr::Error, ae.reason, "Exception while dealing with control socket request", "exception", Logging::Loggable("PDNSException")));
2318 }
2319 }
2320
2321 class PeriodicTask
2322 {
2323 public:
2324 PeriodicTask(const string& aName, time_t aTime) :
2325 period{aTime, 0}, name(aName)
2326 {
2327 if (aTime <= 0) {
2328 throw PDNSException("Invalid period of periodic task " + aName);
2329 }
2330 }
2331
2332 void runIfDue(struct timeval& now, const std::function<void()>& function)
2333 {
2334 if (last_run < now - period) {
2335 function();
2336 Utility::gettimeofday(&last_run);
2337 now = last_run;
2338 }
2339 }
2340
2341 [[nodiscard]] time_t getPeriod() const
2342 {
2343 return period.tv_sec;
2344 }
2345
2346 void setPeriod(time_t newperiod)
2347 {
2348 period.tv_sec = newperiod;
2349 }
2350
2351 void updateLastRun()
2352 {
2353 Utility::gettimeofday(&last_run);
2354 }
2355
2356 [[nodiscard]] bool hasRun() const
2357 {
2358 return last_run.tv_sec != 0 || last_run.tv_usec != 0;
2359 }
2360
2361 private:
2362 struct timeval last_run
2363 {
2364 0, 0
2365 };
2366 struct timeval period;
2367 string name;
2368 };
2369
2370 static void houseKeepingWork(Logr::log_t log)
2371 {
2372 struct timeval now
2373 {
2374 };
2375 Utility::gettimeofday(&now);
2376 t_Counters.updateSnap(now, g_regressionTestMode);
2377
2378 // Below are the tasks that run for every recursorThread, including handler and taskThread
2379
2380 static thread_local PeriodicTask pruneTCPTask{"pruneTCPTask", 5};
2381 pruneTCPTask.runIfDue(now, [now]() {
2382 t_tcp_manager.cleanup(now);
2383 });
2384
2385 const auto& info = RecThreadInfo::self();
2386
2387 // Threads handling packets process config changes in the input path, but not all threads process input packets
2388 // distr threads only process TCP, so that may not happenn very often. So do all periodically.
2389 static thread_local PeriodicTask exportConfigTask{"exportConfigTask", 30};
2390 auto luaconfsLocal = g_luaconfs.getLocal();
2391 exportConfigTask.runIfDue(now, [&luaconfsLocal]() {
2392 checkProtobufExport(luaconfsLocal);
2393 checkOutgoingProtobufExport(luaconfsLocal);
2394 #ifdef HAVE_FSTRM
2395 checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
2396 checkFrameStreamExport(luaconfsLocal, luaconfsLocal->nodFrameStreamExportConfig, t_nodFrameStreamServersInfo);
2397 #endif
2398 });
2399
2400 // Below are the thread specific tasks for the handler and the taskThread
2401 // Likley a few handler tasks could be moved to the taskThread
2402 if (info.isTaskThread()) {
2403 // TaskQueue is run always
2404 runTasks(10, g_logCommonErrors);
2405
2406 static PeriodicTask ztcTask{"ZTC", 60};
2407 static map<DNSName, RecZoneToCache::State> ztcStates;
2408 ztcTask.runIfDue(now, [&luaconfsLocal]() {
2409 RecZoneToCache::maintainStates(luaconfsLocal->ztcConfigs, ztcStates, luaconfsLocal->generation);
2410 for (const auto& ztc : luaconfsLocal->ztcConfigs) {
2411 RecZoneToCache::ZoneToCache(ztc.second, ztcStates.at(ztc.first));
2412 }
2413 });
2414 }
2415 else if (info.isHandler()) {
2416 if (g_packetCache) {
2417 static PeriodicTask packetCacheTask{"packetCacheTask", 5};
2418 packetCacheTask.runIfDue(now, [now]() {
2419 g_packetCache->doPruneTo(now.tv_sec, g_maxPacketCacheEntries);
2420 });
2421 }
2422 static PeriodicTask recordCachePruneTask{"RecordCachePruneTask", 5};
2423 recordCachePruneTask.runIfDue(now, [now]() {
2424 g_recCache->doPrune(now.tv_sec, g_maxCacheEntries);
2425 });
2426
2427 static PeriodicTask negCachePruneTask{"NegCachePrunteTask", 5};
2428 negCachePruneTask.runIfDue(now, [now]() {
2429 g_negCache->prune(now.tv_sec, g_maxCacheEntries / 8);
2430 });
2431
2432 static PeriodicTask aggrNSECPruneTask{"AggrNSECPruneTask", 5};
2433 aggrNSECPruneTask.runIfDue(now, [now]() {
2434 if (g_aggressiveNSECCache) {
2435 g_aggressiveNSECCache->prune(now.tv_sec);
2436 }
2437 });
2438
2439 static PeriodicTask pruneNSpeedTask{"pruneNSSpeedTask", 30};
2440 pruneNSpeedTask.runIfDue(now, [now]() {
2441 SyncRes::pruneNSSpeeds(now.tv_sec - 300);
2442 });
2443
2444 static PeriodicTask pruneEDNSTask{"pruneEDNSTask", 60};
2445 pruneEDNSTask.runIfDue(now, [now]() {
2446 SyncRes::pruneEDNSStatuses(now.tv_sec);
2447 });
2448
2449 if (SyncRes::s_max_busy_dot_probes > 0) {
2450 static PeriodicTask pruneDoTProbeMap{"pruneDoTProbeMapTask", 60};
2451 pruneDoTProbeMap.runIfDue(now, [now]() {
2452 SyncRes::pruneDoTProbeMap(now.tv_sec);
2453 });
2454 }
2455
2456 static PeriodicTask pruneThrottledTask{"pruneThrottledTask", 5};
2457 pruneThrottledTask.runIfDue(now, [now]() {
2458 SyncRes::pruneThrottledServers(now.tv_sec);
2459 });
2460
2461 static PeriodicTask pruneFailedServersTask{"pruneFailedServerTask", 5};
2462 pruneFailedServersTask.runIfDue(now, [now]() {
2463 SyncRes::pruneFailedServers(now.tv_sec - static_cast<time_t>(SyncRes::s_serverdownthrottletime * 10));
2464 });
2465
2466 static PeriodicTask pruneNonResolvingTask{"pruneNonResolvingTask", 5};
2467 pruneNonResolvingTask.runIfDue(now, [now]() {
2468 SyncRes::pruneNonResolving(now.tv_sec - SyncRes::s_nonresolvingnsthrottletime);
2469 });
2470
2471 static PeriodicTask pruneSaveParentSetTask{"pruneSaveParentSetTask", 60};
2472 pruneSaveParentSetTask.runIfDue(now, [now]() {
2473 SyncRes::pruneSaveParentsNSSets(now.tv_sec);
2474 });
2475
2476 // By default, refresh at 80% of max-cache-ttl with a minimum period of 10s
2477 const unsigned int minRootRefreshInterval = 10;
2478 static PeriodicTask rootUpdateTask{"rootUpdateTask", std::max(SyncRes::s_maxcachettl * 8 / 10, minRootRefreshInterval)};
2479 rootUpdateTask.runIfDue(now, [now, &log, minRootRefreshInterval]() {
2480 int res = 0;
2481 if (!g_regressionTestMode) {
2482 res = SyncRes::getRootNS(now, nullptr, 0, log);
2483 }
2484 if (res == 0) {
2485 // Success, go back to the defaut period
2486 rootUpdateTask.setPeriod(std::max(SyncRes::s_maxcachettl * 8 / 10, minRootRefreshInterval));
2487 }
2488 else {
2489 // On failure, go to the middle of the remaining period (initially 80% / 8 = 10%) and shorten the interval on each
2490 // failure by dividing the existing interval by 8, keeping the minimum interval at 10s.
2491 // So with a 1 day period and failures we'll see a refresh attempt at 69120, 69120+11520, 69120+11520+1440, ...
2492 rootUpdateTask.setPeriod(std::max<time_t>(rootUpdateTask.getPeriod() / 8, minRootRefreshInterval));
2493 }
2494 });
2495
2496 static PeriodicTask secpollTask{"secpollTask", 3600};
2497 static time_t t_last_secpoll;
2498 secpollTask.runIfDue(now, [&log]() {
2499 try {
2500 doSecPoll(&t_last_secpoll, log);
2501 }
2502 catch (const std::exception& e) {
2503 SLOG(g_log << Logger::Error << "Exception while performing security poll: " << e.what() << endl,
2504 log->error(Logr::Error, e.what(), "Exception while performing security poll"));
2505 }
2506 catch (const PDNSException& e) {
2507 SLOG(g_log << Logger::Error << "Exception while performing security poll: " << e.reason << endl,
2508 log->error(Logr::Error, e.reason, "Exception while performing security poll"));
2509 }
2510 catch (const ImmediateServFailException& e) {
2511 SLOG(g_log << Logger::Error << "Exception while performing security poll: " << e.reason << endl,
2512 log->error(Logr::Error, e.reason, "Exception while performing security poll"));
2513 }
2514 catch (const PolicyHitException& e) {
2515 SLOG(g_log << Logger::Error << "Policy hit while performing security poll" << endl,
2516 log->info(Logr::Error, "Policy hit while performing security poll"));
2517 }
2518 catch (...) {
2519 SLOG(g_log << Logger::Error << "Exception while performing security poll" << endl,
2520 log->info(Logr::Error, "Exception while performing security poll"));
2521 }
2522 });
2523
2524 const time_t taInterval = std::max(1, static_cast<int>(luaconfsLocal->trustAnchorFileInfo.interval) * 3600);
2525 static PeriodicTask trustAnchorTask{"trustAnchorTask", taInterval};
2526 if (!trustAnchorTask.hasRun()) {
2527 // Loading the Lua config file already "refreshed" the TAs
2528 trustAnchorTask.updateLastRun();
2529 }
2530 // interval might have ben updated
2531 trustAnchorTask.setPeriod(taInterval);
2532 trustAnchorTask.runIfDue(now, [&luaconfsLocal, &log]() {
2533 if (!luaconfsLocal->trustAnchorFileInfo.fname.empty() && luaconfsLocal->trustAnchorFileInfo.interval != 0) {
2534 SLOG(g_log << Logger::Debug << "Refreshing Trust Anchors from file" << endl,
2535 log->info(Logr::Debug, "Refreshing Trust Anchors from file"));
2536 try {
2537 map<DNSName, dsmap_t> dsAnchors;
2538 if (updateTrustAnchorsFromFile(luaconfsLocal->trustAnchorFileInfo.fname, dsAnchors, log)) {
2539 g_luaconfs.modify([&dsAnchors](LuaConfigItems& lci) {
2540 lci.dsAnchors = dsAnchors;
2541 });
2542 }
2543 }
2544 catch (const PDNSException& pe) {
2545 SLOG(g_log << Logger::Error << "Unable to update Trust Anchors: " << pe.reason << endl,
2546 log->error(Logr::Error, pe.reason, "Unable to update Trust Anchors"));
2547 }
2548 }
2549 });
2550 }
2551 t_Counters.updateSnap(g_regressionTestMode);
2552 }
2553
2554 static void houseKeeping(void* /* ignored */)
2555 {
2556 auto log = g_slog->withName("housekeeping");
2557 static thread_local bool t_running; // houseKeeping can get suspended in secpoll, and be restarted, which makes us do duplicate work
2558
2559 try {
2560 if (t_running) {
2561 return;
2562 }
2563 t_running = true;
2564 houseKeepingWork(log);
2565 t_running = false;
2566 }
2567 catch (const PDNSException& ae) {
2568 t_running = false;
2569 SLOG(g_log << Logger::Error << "Fatal error in housekeeping thread: " << ae.reason << endl,
2570 log->error(Logr::Error, ae.reason, "Fatal error in housekeeping thread"));
2571 throw;
2572 }
2573 catch (...) {
2574 t_running = false;
2575 SLOG(g_log << Logger::Error << "Uncaught exception in housekeeping thread" << endl,
2576 log->info(Logr::Error, "Uncaught exception in housekeeping thread"));
2577 throw;
2578 }
2579 }
2580
2581 static void runLuaMaintenance(RecThreadInfo& threadInfo, time_t& last_lua_maintenance, time_t luaMaintenanceInterval)
2582 {
2583 if (t_pdl != nullptr) {
2584 // lua-dns-script directive is present, call the maintenance callback if needed
2585 if (threadInfo.isWorker()) { // either UDP of TCP worker
2586 // Only on threads processing queries
2587 if (g_now.tv_sec - last_lua_maintenance >= luaMaintenanceInterval) {
2588 struct timeval start
2589 {
2590 };
2591 Utility::gettimeofday(&start);
2592 t_pdl->maintenance();
2593 last_lua_maintenance = g_now.tv_sec;
2594 struct timeval stop
2595 {
2596 };
2597 Utility::gettimeofday(&stop);
2598 t_Counters.at(rec::Counter::maintenanceUsec) += uSec(stop - start);
2599 ++t_Counters.at(rec::Counter::maintenanceCalls);
2600 }
2601 }
2602 }
2603 }
2604
2605 static void runTCPMaintenance(RecThreadInfo& threadInfo, bool& listenOnTCP, unsigned int maxTcpClients)
2606 {
2607 if (threadInfo.isTCPListener()) {
2608 if (listenOnTCP) {
2609 if (TCPConnection::getCurrentConnections() > maxTcpClients) { // shutdown, too many connections
2610 for (const auto fileDesc : threadInfo.getTCPSockets()) {
2611 t_fdm->removeReadFD(fileDesc);
2612 }
2613 listenOnTCP = false;
2614 }
2615 }
2616 else {
2617 if (TCPConnection::getCurrentConnections() <= maxTcpClients) { // reenable
2618 for (const auto fileDesc : threadInfo.getTCPSockets()) {
2619 t_fdm->addReadFD(fileDesc, handleNewTCPQuestion);
2620 }
2621 listenOnTCP = true;
2622 }
2623 }
2624 }
2625 }
2626
2627 static void recLoop()
2628 {
2629 unsigned int maxTcpClients = ::arg().asNum("max-tcp-clients");
2630 bool listenOnTCP{true};
2631 time_t last_stat = 0;
2632 time_t last_carbon = 0;
2633 time_t last_lua_maintenance = 0;
2634 time_t carbonInterval = ::arg().asNum("carbon-interval");
2635 time_t luaMaintenanceInterval = ::arg().asNum("lua-maintenance-interval");
2636
2637 auto& threadInfo = RecThreadInfo::self();
2638
2639 while (!RecursorControlChannel::stop) {
2640 while (g_multiTasker->schedule(g_now)) {
2641 ; // MTasker letting the mthreads do their thing
2642 }
2643
2644 // Use primes, it avoid not being scheduled in cases where the counter has a regular pattern.
2645 // We want to call handler thread often, it gets scheduled about 2 times per second
2646 if (((threadInfo.isHandler() || threadInfo.isTaskThread()) && s_counter % 11 == 0) || s_counter % 499 == 0) {
2647 struct timeval start
2648 {
2649 };
2650 Utility::gettimeofday(&start);
2651 g_multiTasker->makeThread(houseKeeping, nullptr);
2652 if (!threadInfo.isTaskThread()) {
2653 struct timeval stop
2654 {
2655 };
2656 Utility::gettimeofday(&stop);
2657 t_Counters.at(rec::Counter::maintenanceUsec) += uSec(stop - start);
2658 ++t_Counters.at(rec::Counter::maintenanceCalls);
2659 }
2660 }
2661
2662 if (s_counter % 55 == 0) {
2663 auto expired = t_fdm->getTimeouts(g_now);
2664
2665 for (const auto& exp : expired) {
2666 auto conn = boost::any_cast<shared_ptr<TCPConnection>>(exp.second);
2667 if (g_logCommonErrors) {
2668 SLOG(g_log << Logger::Warning << "Timeout from remote TCP client " << conn->d_remote.toStringWithPort() << endl,
2669 g_slogtcpin->info(Logr::Warning, "Timeout from remote TCP client", "remote", Logging::Loggable(conn->d_remote)));
2670 }
2671 t_fdm->removeReadFD(exp.first);
2672 }
2673 }
2674
2675 s_counter++;
2676
2677 if (threadInfo.isHandler()) {
2678 if (statsWanted || (s_statisticsInterval > 0 && (g_now.tv_sec - last_stat) >= s_statisticsInterval)) {
2679 doStats();
2680 last_stat = g_now.tv_sec;
2681 }
2682
2683 Utility::gettimeofday(&g_now, nullptr);
2684
2685 if ((g_now.tv_sec - last_carbon) >= carbonInterval) {
2686 g_multiTasker->makeThread(doCarbonDump, nullptr);
2687 last_carbon = g_now.tv_sec;
2688 }
2689 }
2690 runLuaMaintenance(threadInfo, last_lua_maintenance, luaMaintenanceInterval);
2691
2692 t_fdm->run(&g_now);
2693 // 'run' updates g_now for us
2694
2695 runTCPMaintenance(threadInfo, listenOnTCP, maxTcpClients);
2696 }
2697 }
2698
2699 static void recursorThread()
2700 {
2701 auto log = g_slog->withName("runtime");
2702 t_Counters.updateSnap(true);
2703 try {
2704 auto& threadInfo = RecThreadInfo::self();
2705 {
2706 SyncRes tmp(g_now); // make sure it allocates tsstorage before we do anything, like primeHints or so..
2707 SyncRes::setDomainMap(g_initialDomainMap);
2708 t_allowFrom = g_initialAllowFrom;
2709 t_allowNotifyFrom = g_initialAllowNotifyFrom;
2710 t_allowNotifyFor = g_initialAllowNotifyFor;
2711 t_udpclientsocks = std::make_unique<UDPClientSocks>();
2712 t_tcpClientCounts = std::make_unique<tcpClientCounts_t>();
2713 if (g_proxyMapping) {
2714 t_proxyMapping = make_unique<ProxyMapping>(*g_proxyMapping);
2715 }
2716 else {
2717 t_proxyMapping = nullptr;
2718 }
2719
2720 if (threadInfo.isHandler()) {
2721 if (!primeHints()) {
2722 threadInfo.setExitCode(EXIT_FAILURE);
2723 RecursorControlChannel::stop = true;
2724 SLOG(g_log << Logger::Critical << "Priming cache failed, stopping" << endl,
2725 log->info(Logr::Critical, "Priming cache failed, stopping"));
2726 }
2727 SLOG(g_log << Logger::Debug << "Done priming cache with root hints" << endl,
2728 log->info(Logr::Debug, "Done priming cache with root hints"));
2729 }
2730 }
2731
2732 #ifdef NOD_ENABLED
2733 if (threadInfo.isWorker()) {
2734 setupNODThread(log);
2735 }
2736 #endif /* NOD_ENABLED */
2737
2738 /* the listener threads handle TCP queries */
2739 if (threadInfo.isWorker() || threadInfo.isListener()) {
2740 try {
2741 if (!::arg()["lua-dns-script"].empty()) {
2742 t_pdl = std::make_shared<RecursorLua4>();
2743 t_pdl->loadFile(::arg()["lua-dns-script"]);
2744 SLOG(g_log << Logger::Warning << "Loaded 'lua' script from '" << ::arg()["lua-dns-script"] << "'" << endl,
2745 log->info(Logr::Warning, "Loading Lua script from file", "name", Logging::Loggable(::arg()["lua-dns-script"])));
2746 }
2747 }
2748 catch (std::exception& e) {
2749 SLOG(g_log << Logger::Error << "Failed to load 'lua' script from '" << ::arg()["lua-dns-script"] << "': " << e.what() << endl,
2750 log->error(Logr::Error, e.what(), "Failed to load Lua script from file", "name", Logging::Loggable(::arg()["lua-dns-script"])));
2751 _exit(99);
2752 }
2753 }
2754
2755 unsigned int ringsize = ::arg().asNum("stats-ringbuffer-entries") / RecThreadInfo::numUDPWorkers();
2756 if (ringsize != 0) {
2757 t_remotes = std::make_unique<addrringbuf_t>();
2758 if (RecThreadInfo::weDistributeQueries()) {
2759 t_remotes->set_capacity(::arg().asNum("stats-ringbuffer-entries") / RecThreadInfo::numDistributors());
2760 }
2761 else {
2762 t_remotes->set_capacity(ringsize);
2763 }
2764 t_servfailremotes = std::make_unique<addrringbuf_t>();
2765 t_servfailremotes->set_capacity(ringsize);
2766 t_bogusremotes = std::make_unique<addrringbuf_t>();
2767 t_bogusremotes->set_capacity(ringsize);
2768 t_largeanswerremotes = std::make_unique<addrringbuf_t>();
2769 t_largeanswerremotes->set_capacity(ringsize);
2770 t_timeouts = std::make_unique<addrringbuf_t>();
2771 t_timeouts->set_capacity(ringsize);
2772
2773 t_queryring = std::make_unique<boost::circular_buffer<pair<DNSName, uint16_t>>>();
2774 t_queryring->set_capacity(ringsize);
2775 t_servfailqueryring = std::make_unique<boost::circular_buffer<pair<DNSName, uint16_t>>>();
2776 t_servfailqueryring->set_capacity(ringsize);
2777 t_bogusqueryring = std::make_unique<boost::circular_buffer<pair<DNSName, uint16_t>>>();
2778 t_bogusqueryring->set_capacity(ringsize);
2779 }
2780 g_multiTasker = std::make_unique<MT_t>(::arg().asNum("stack-size"), ::arg().asNum("stack-cache-size"));
2781 threadInfo.setMT(g_multiTasker.get());
2782
2783 /* start protobuf export threads if needed */
2784 auto luaconfsLocal = g_luaconfs.getLocal();
2785 checkProtobufExport(luaconfsLocal);
2786 checkOutgoingProtobufExport(luaconfsLocal);
2787 #ifdef HAVE_FSTRM
2788 checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
2789 checkFrameStreamExport(luaconfsLocal, luaconfsLocal->nodFrameStreamExportConfig, t_nodFrameStreamServersInfo);
2790 #endif
2791
2792 t_fdm = unique_ptr<FDMultiplexer>(getMultiplexer(log));
2793
2794 std::unique_ptr<RecursorWebServer> rws;
2795
2796 t_fdm->addReadFD(threadInfo.getPipes().readToThread, handlePipeRequest);
2797
2798 if (threadInfo.isHandler()) {
2799 if (::arg().mustDo("webserver")) {
2800 SLOG(g_log << Logger::Warning << "Enabling web server" << endl,
2801 log->info(Logr::Info, "Enabling web server"));
2802 try {
2803 rws = make_unique<RecursorWebServer>(t_fdm.get());
2804 }
2805 catch (const PDNSException& e) {
2806 SLOG(g_log << Logger::Error << "Unable to start the internal web server: " << e.reason << endl,
2807 log->error(Logr::Critical, e.reason, "Exception while starting internal web server"));
2808 _exit(99);
2809 }
2810 }
2811 SLOG(g_log << Logger::Info << "Enabled '" << t_fdm->getName() << "' multiplexer" << endl,
2812 log->info(Logr::Info, "Enabled multiplexer", "name", Logging::Loggable(t_fdm->getName())));
2813 }
2814 else {
2815 t_fdm->addReadFD(threadInfo.getPipes().readQueriesToThread, handlePipeRequest);
2816
2817 if (threadInfo.isListener()) {
2818 if (g_reusePort) {
2819 /* then every listener has its own FDs */
2820 for (const auto& deferred : threadInfo.getDeferredAdds()) {
2821 t_fdm->addReadFD(deferred.first, deferred.second);
2822 }
2823 }
2824 else {
2825 /* otherwise all listeners are listening on the same ones */
2826 for (const auto& deferred : threadInfo.isTCPListener() ? s_deferredTCPadds : s_deferredUDPadds) {
2827 t_fdm->addReadFD(deferred.first, deferred.second);
2828 }
2829 }
2830 }
2831 }
2832
2833 if (threadInfo.isHandler()) {
2834 t_fdm->addReadFD(g_rcc.d_fd, handleRCC); // control channel
2835 }
2836
2837 #ifdef HAVE_SYSTEMD
2838 if (threadInfo.isHandler()) {
2839 // There is a race, as some threads might not be ready yet to do work.
2840 // To solve that, threads should notify RecThreadInfo they are done initializing.
2841 // But we lack a mechanism for that at this point in time.
2842 sd_notify(0, "READY=1");
2843 }
2844 #endif
2845
2846 recLoop();
2847 }
2848 catch (PDNSException& ae) {
2849 SLOG(g_log << Logger::Error << "Exception: " << ae.reason << endl,
2850 log->error(Logr::Error, ae.reason, "Exception in RecursorThread", "exception", Logging::Loggable("PDNSException")));
2851 }
2852 catch (std::exception& e) {
2853 SLOG(g_log << Logger::Error << "STL Exception: " << e.what() << endl,
2854 log->error(Logr::Error, e.what(), "Exception in RecursorThread", "exception", Logging::Loggable("std::exception")));
2855 }
2856 catch (...) {
2857 SLOG(g_log << Logger::Error << "any other exception in main: " << endl,
2858 log->info(Logr::Error, "Exception in RecursorThread"));
2859 }
2860 }
2861
2862 static pair<int, bool> doYamlConfig(Logr::log_t /* startupLog */, int argc, char* argv[]) // NOLINT: Posix API
2863 {
2864 if (!::arg().mustDo("config")) {
2865 return {0, false};
2866 }
2867 const string config = ::arg()["config"];
2868 if (config == "diff" || config.empty()) {
2869 ::arg().parse(argc, argv);
2870 pdns::rust::settings::rec::Recursorsettings settings;
2871 pdns::settings::rec::oldStyleSettingsToBridgeStruct(settings);
2872 auto yaml = settings.to_yaml_string();
2873 cout << yaml << endl;
2874 }
2875 else if (config == "default") {
2876 auto yaml = pdns::settings::rec::defaultsToYaml();
2877 cout << yaml << endl;
2878 }
2879 return {0, true};
2880 }
2881
2882 static pair<int, bool> doConfig(Logr::log_t startupLog, const string& configname, int argc, char* argv[]) // NOLINT: Posix API
2883 {
2884 if (::arg().mustDo("config")) {
2885 string config = ::arg()["config"];
2886 if (config == "check") {
2887 try {
2888 if (!::arg().file(configname)) {
2889 SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl,
2890 startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname)));
2891 return {1, true};
2892 }
2893 ::arg().parse(argc, argv);
2894 return {0, true};
2895 }
2896 catch (const ArgException& argException) {
2897 SLOG(g_log << Logger::Warning << "Unable to parse configuration file '" << configname << "': " << argException.reason << endl,
2898 startupLog->error("Cannot parse configuration", "Unable to parse configuration file", "config_file", Logging::Loggable(configname), "reason", Logging::Loggable(argException.reason)));
2899 return {1, true};
2900 }
2901 }
2902 else if (config == "default" || config.empty()) {
2903 cout << ::arg().configstring(false, true);
2904 }
2905 else if (config == "diff") {
2906 if (!::arg().laxFile(configname)) {
2907 SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl,
2908 startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname)));
2909 return {1, true};
2910 }
2911 ::arg().laxParse(argc, argv);
2912 cout << ::arg().configstring(true, false);
2913 }
2914 else {
2915 if (!::arg().laxFile(configname)) {
2916 SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl,
2917 startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname)));
2918 return {1, true};
2919 }
2920 ::arg().laxParse(argc, argv);
2921 cout << ::arg().configstring(true, true);
2922 }
2923 return {0, true};
2924 }
2925 return {0, false};
2926 }
2927
2928 static void handleRuntimeDefaults(Logr::log_t log)
2929 {
2930 #if HAVE_FIBER_SANITIZER
2931 // Asan needs more stack
2932 if (::arg().asNum("stack-size") == 200000) { // the default in table.py
2933 ::arg().set("stack-size", "stack size per mthread") = "600000";
2934 }
2935 #endif
2936
2937 const string RUNTIME = "*runtime determined*";
2938 if (::arg()["version-string"] == RUNTIME) { // i.e. not set explicitly
2939 ::arg().set("version-string") = fullVersionString();
2940 }
2941
2942 if (::arg()["server-id"] == RUNTIME) { // i.e. not set explicitly
2943 auto myHostname = getHostname();
2944 if (!myHostname.has_value()) {
2945 SLOG(g_log << Logger::Warning << "Unable to get the hostname, NSID and id.server values will be empty" << endl,
2946 log->info(Logr::Warning, "Unable to get the hostname, NSID and id.server values will be empty"));
2947 }
2948 ::arg().set("server-id") = myHostname.has_value() ? *myHostname : "";
2949 }
2950
2951 if (::arg()["socket-dir"].empty()) {
2952 auto* runtimeDir = getenv("RUNTIME_DIRECTORY"); // NOLINT(concurrency-mt-unsafe,cppcoreguidelines-pro-type-vararg)
2953 if (runtimeDir != nullptr) {
2954 ::arg().set("socket-dir") = runtimeDir;
2955 }
2956 }
2957
2958 if (::arg()["socket-dir"].empty()) {
2959 if (::arg()["chroot"].empty()) {
2960 ::arg().set("socket-dir") = std::string(LOCALSTATEDIR) + "/pdns-recursor";
2961 }
2962 else {
2963 ::arg().set("socket-dir") = "/";
2964 }
2965 }
2966
2967 if (::arg().asNum("threads") == 1) {
2968 if (::arg().mustDo("pdns-distributes-queries")) {
2969 SLOG(g_log << Logger::Warning << "Only one thread, no need to distribute queries ourselves" << endl,
2970 log->info(Logr::Warning, "Only one thread, no need to distribute queries ourselves"));
2971 ::arg().set("pdns-distributes-queries") = "no";
2972 }
2973 }
2974
2975 if (::arg().mustDo("pdns-distributes-queries") && ::arg().asNum("distributor-threads") == 0) {
2976 SLOG(g_log << Logger::Warning << "Asked to run with pdns-distributes-queries set but no distributor threads, raising to 1" << endl,
2977 log->info(Logr::Warning, "Asked to run with pdns-distributes-queries set but no distributor threads, raising to 1"));
2978 ::arg().set("distributor-threads") = "1";
2979 }
2980
2981 if (!::arg().mustDo("pdns-distributes-queries") && ::arg().asNum("distributor-threads") > 0) {
2982 SLOG(g_log << Logger::Warning << "Not distributing queries, setting distributor threads to 0" << endl,
2983 log->info(Logr::Warning, "Not distributing queries, setting distributor threads to 0"));
2984 ::arg().set("distributor-threads") = "0";
2985 }
2986 }
2987
2988 int main(int argc, char** argv)
2989 {
2990 g_argc = argc;
2991 g_argv = argv;
2992 versionSetProduct(ProductRecursor);
2993 reportBasicTypes();
2994 reportOtherTypes();
2995
2996 int ret = EXIT_SUCCESS;
2997
2998 try {
2999 pdns::settings::rec::defineOldStyleSettings();
3000 ::arg().setDefaults();
3001 g_log.toConsole(Logger::Info);
3002 ::arg().laxParse(argc, argv); // do a lax parse
3003
3004 if (::arg().mustDo("version")) {
3005 showProductVersion();
3006 showBuildConfiguration();
3007 return 0;
3008 }
3009 if (::arg().mustDo("help")) {
3010 cout << "syntax:" << endl
3011 << endl;
3012 cout << ::arg().helpstring(::arg()["help"]) << endl;
3013 return 0;
3014 }
3015
3016 // Pick up options given on command line to setup logging asap.
3017 g_quiet = ::arg().mustDo("quiet");
3018 s_logUrgency = (Logger::Urgency)::arg().asNum("loglevel");
3019 g_slogStructured = ::arg().mustDo("structured-logging");
3020 s_structured_logger_backend = ::arg()["structured-logging-backend"];
3021
3022 if (!g_quiet && s_logUrgency < Logger::Info) { // Logger::Info=6, Logger::Debug=7
3023 s_logUrgency = Logger::Info; // if you do --quiet=no, you need Info to also see the query log
3024 }
3025 g_log.setLoglevel(s_logUrgency);
3026 g_log.toConsole(s_logUrgency);
3027 showProductVersion();
3028 if (!g_slogStructured) {
3029 g_log << Logger::Warning << "Disabling structured logging is deprecated, old-style logging wil be removed in a future release" << endl;
3030 }
3031
3032 g_yamlSettings = false;
3033 string configname = ::arg()["config-dir"] + "/recursor";
3034 if (!::arg()["config-name"].empty()) {
3035 configname = ::arg()["config-dir"] + "/recursor-" + ::arg()["config-name"];
3036 g_programname += "-" + ::arg()["config-name"];
3037 }
3038 cleanSlashes(configname);
3039
3040 if (!::arg().getCommands().empty()) {
3041 cerr << "Fatal: non-option";
3042 if (::arg().getCommands().size() > 1) {
3043 cerr << "s";
3044 }
3045 cerr << " (";
3046 bool first = true;
3047 for (const auto& command : ::arg().getCommands()) {
3048 if (!first) {
3049 cerr << ", ";
3050 }
3051 first = false;
3052 cerr << command;
3053 }
3054 cerr << ") on the command line, perhaps a '--setting=123' statement missed the '='?" << endl;
3055 return 99;
3056 }
3057
3058 if (s_structured_logger_backend == "systemd-journal") {
3059 #ifdef HAVE_SYSTEMD
3060 if (int fd = sd_journal_stream_fd("pdns-recusor", LOG_DEBUG, 0); fd >= 0) {
3061 g_slog = Logging::Logger::create(loggerSDBackend);
3062 close(fd);
3063 }
3064 #endif
3065 if (g_slog == nullptr) {
3066 cerr << "Structured logging to systemd-journal requested but it is not available" << endl;
3067 }
3068 }
3069
3070 if (g_slog == nullptr) {
3071 g_slog = Logging::Logger::create(loggerBackend);
3072 }
3073
3074 // Missing: a mechanism to call setVerbosity(x)
3075 auto startupLog = g_slog->withName("config");
3076 g_slogtcpin = g_slog->withName("in")->withValues("proto", Logging::Loggable("tcp"));
3077 g_slogudpin = g_slog->withName("in")->withValues("proto", Logging::Loggable("udp"));
3078 g_slogout = g_slog->withName("out");
3079
3080 ::arg().setSLog(startupLog);
3081
3082 const string yamlconfigname = configname + ".yml";
3083 string msg;
3084 pdns::rust::settings::rec::Recursorsettings settings;
3085 // TODO: handle include-dir on command line
3086 auto yamlstatus = pdns::settings::rec::readYamlSettings(yamlconfigname, ::arg()["include-dir"], settings, msg, startupLog);
3087
3088 switch (yamlstatus) {
3089 case pdns::settings::rec::YamlSettingsStatus::CannotOpen:
3090 SLOG(g_log << Logger::Debug << "No YAML config found for configname '" << yamlconfigname << "': " << msg << endl,
3091 startupLog->error(Logr::Debug, msg, "No YAML config found", "configname", Logging::Loggable(yamlconfigname)));
3092 break;
3093 case pdns::settings::rec::YamlSettingsStatus::PresentButFailed:
3094 SLOG(g_log << Logger::Error << "YAML config found for configname '" << yamlconfigname << "' but error ocurred processing it" << endl,
3095 startupLog->error(Logr::Error, msg, "YAML config found, but error occurred processsing it", "configname", Logging::Loggable(yamlconfigname)));
3096 return 1;
3097 break;
3098 case pdns::settings::rec::YamlSettingsStatus::OK:
3099 g_yamlSettings = true;
3100 SLOG(g_log << Logger::Notice << "YAML config found and processed for configname '" << yamlconfigname << "'" << endl,
3101 startupLog->info(Logr::Notice, "YAML config found and processed", "configname", Logging::Loggable(yamlconfigname)));
3102 pdns::settings::rec::processAPIDir(arg()["include-dir"], settings, startupLog);
3103 pdns::settings::rec::bridgeStructToOldStyleSettings(settings);
3104 break;
3105 }
3106
3107 if (g_yamlSettings) {
3108 bool mustExit = false;
3109 std::tie(ret, mustExit) = doYamlConfig(startupLog, argc, argv);
3110 if (ret != 0 || mustExit) {
3111 return ret;
3112 }
3113 }
3114
3115 if (yamlstatus == pdns::settings::rec::YamlSettingsStatus::CannotOpen) {
3116 configname += ".conf";
3117 bool mustExit = false;
3118 std::tie(ret, mustExit) = doConfig(startupLog, configname, argc, argv);
3119 if (ret != 0 || mustExit) {
3120 return ret;
3121 }
3122 if (!::arg().file(configname)) {
3123 SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl,
3124 startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname)));
3125 }
3126 }
3127
3128 // Reparse, now with config file as well, both for old-style as for YAML settings
3129 ::arg().parse(argc, argv);
3130
3131 g_quiet = ::arg().mustDo("quiet");
3132 s_logUrgency = (Logger::Urgency)::arg().asNum("loglevel");
3133 g_slogStructured = ::arg().mustDo("structured-logging");
3134
3135 if (s_logUrgency < Logger::Error) {
3136 s_logUrgency = Logger::Error;
3137 }
3138 if (!g_quiet && s_logUrgency < Logger::Info) { // Logger::Info=6, Logger::Debug=7
3139 s_logUrgency = Logger::Info; // if you do --quiet=no, you need Info to also see the query log
3140 }
3141 g_log.setLoglevel(s_logUrgency);
3142 g_log.toConsole(s_logUrgency);
3143
3144 if (!::arg()["chroot"].empty() && !::arg()["api-config-dir"].empty()) {
3145 SLOG(g_log << Logger::Error << "Using chroot and enabling the API is not possible" << endl,
3146 startupLog->info(Logr::Error, "Cannot use chroot and enable the API at the same time"));
3147 return EXIT_FAILURE;
3148 }
3149
3150 handleRuntimeDefaults(startupLog);
3151
3152 g_recCache = std::make_unique<MemRecursorCache>(::arg().asNum("record-cache-shards"));
3153 g_negCache = std::make_unique<NegCache>(::arg().asNum("record-cache-shards") / 8);
3154 if (!::arg().mustDo("disable-packetcache")) {
3155 g_maxPacketCacheEntries = ::arg().asNum("max-packetcache-entries");
3156 g_packetCache = std::make_unique<RecursorPacketCache>(g_maxPacketCacheEntries, ::arg().asNum("packetcache-shards"));
3157 }
3158
3159 ret = serviceMain(startupLog);
3160 }
3161 catch (const PDNSException& ae) {
3162 SLOG(g_log << Logger::Error << "Exception: " << ae.reason << endl,
3163 g_slog->withName("config")->error(Logr::Critical, ae.reason, "Fatal error", "exception", Logging::Loggable("PDNSException")));
3164 ret = EXIT_FAILURE;
3165 }
3166 catch (const std::exception& e) {
3167 SLOG(g_log << Logger::Error << "STL Exception: " << e.what() << endl,
3168 g_slog->withName("config")->error(Logr::Critical, e.what(), "Fatal error", "exception", Logging::Loggable("std::exception")));
3169 ret = EXIT_FAILURE;
3170 }
3171 catch (...) {
3172 SLOG(g_log << Logger::Error << "any other exception in main: " << endl,
3173 g_slog->withName("config")->info(Logr::Critical, "Fatal error"));
3174 ret = EXIT_FAILURE;
3175 }
3176
3177 return ret;
3178 }
3179
3180 static RecursorControlChannel::Answer* doReloadLuaScript()
3181 {
3182 string fname = ::arg()["lua-dns-script"];
3183 auto log = g_slog->withName("runtime")->withValues("name", Logging::Loggable(fname));
3184 try {
3185 if (fname.empty()) {
3186 t_pdl.reset();
3187 SLOG(g_log << Logger::Info << RecThreadInfo::id() << " Unloaded current lua script" << endl,
3188 log->info(Logr::Info, "Unloaded current lua script"));
3189 return new RecursorControlChannel::Answer{0, string("unloaded\n")};
3190 }
3191
3192 t_pdl = std::make_shared<RecursorLua4>();
3193 try {
3194 t_pdl->loadFile(fname);
3195 }
3196 catch (std::runtime_error& ex) {
3197 string msg = std::to_string(RecThreadInfo::id()) + " Retaining current script, could not read '" + fname + "': " + ex.what();
3198 SLOG(g_log << Logger::Error << msg << endl,
3199 log->error(Logr::Error, ex.what(), "Retaining current script, could not read new script"));
3200 return new RecursorControlChannel::Answer{1, msg + "\n"};
3201 }
3202 }
3203 catch (std::exception& e) {
3204 SLOG(g_log << Logger::Error << RecThreadInfo::id() << " Retaining current script, error from '" << fname << "': " << e.what() << endl,
3205 log->error(Logr::Error, e.what(), "Retaining current script, error in new script"));
3206 return new RecursorControlChannel::Answer{1, string("retaining current script, error from '" + fname + "': " + e.what() + "\n")};
3207 }
3208
3209 SLOG(g_log << Logger::Warning << RecThreadInfo::id() << " (Re)loaded lua script from '" << fname << "'" << endl,
3210 log->info(Logr::Warning, "(Re)loaded lua script"));
3211 return new RecursorControlChannel::Answer{0, string("(re)loaded '" + fname + "'\n")};
3212 }
3213
3214 RecursorControlChannel::Answer doQueueReloadLuaScript(vector<string>::const_iterator begin, vector<string>::const_iterator end)
3215 {
3216 if (begin != end) {
3217 ::arg().set("lua-dns-script") = *begin;
3218 }
3219
3220 return broadcastAccFunction<RecursorControlChannel::Answer>(doReloadLuaScript);
3221 }
3222
3223 static string* pleaseUseNewTraceRegex(const std::string& newRegex, int file)
3224 {
3225 try {
3226 if (newRegex.empty()) {
3227 t_traceRegex.reset();
3228 t_tracefd = FDWrapper();
3229 return new string("unset\n");
3230 }
3231 if (file == -1) {
3232 return new string("could not dup file\n");
3233 }
3234 t_traceRegex = std::make_shared<Regex>(newRegex);
3235 t_tracefd = file;
3236 return new string("ok\n"); // NOLINT(cppcoreguidelines-owning-memory): it's the API
3237 }
3238 catch (const PDNSException& ae) {
3239 return new string(ae.reason + "\n"); // NOLINT(cppcoreguidelines-owning-memory): it's the API
3240 }
3241 }
3242
3243 string doTraceRegex(FDWrapper file, vector<string>::const_iterator begin, vector<string>::const_iterator end)
3244 {
3245 int fileno = dup(file);
3246 // Potential dup failure handled in pleaseUseNewTraceRegex()
3247 return broadcastAccFunction<string>([=] { return pleaseUseNewTraceRegex(begin != end ? *begin : "", fileno); });
3248 }
3249
3250 struct WipeCacheResult wipeCaches(const DNSName& canon, bool subtree, uint16_t qtype)
3251 {
3252 struct WipeCacheResult res;
3253
3254 try {
3255 res.record_count = static_cast<int>(g_recCache->doWipeCache(canon, subtree, qtype));
3256 // scanbuild complains here about an allocated function object that is being leaked. Needs investigation
3257 if (g_packetCache) {
3258 res.packet_count = static_cast<int>(g_packetCache->doWipePacketCache(canon, qtype, subtree));
3259 }
3260 res.negative_record_count = static_cast<int>(g_negCache->wipe(canon, subtree));
3261 if (g_aggressiveNSECCache) {
3262 g_aggressiveNSECCache->removeZoneInfo(canon, subtree);
3263 }
3264 }
3265 catch (const std::exception& e) {
3266 auto log = g_slog->withName("runtime");
3267 SLOG(g_log << Logger::Warning << ", failed: " << e.what() << endl,
3268 log->error(Logr::Warning, e.what(), "Wipecache failed"));
3269 }
3270
3271 return res;
3272 }