]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/recursordist/rec-main.cc
a435f3a0d37a8f61e30382c8283be9ef26397f84
[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
1569 vector<string> nums;
1570 bool automatic = true;
1571 if (!::arg()["dnssec-disabled-algorithms"].empty()) {
1572 automatic = false;
1573 stringtok(nums, ::arg()["dnssec-disabled-algorithms"], ", ");
1574 for (const auto& num : nums) {
1575 DNSCryptoKeyEngine::switchOffAlgorithm(pdns::checked_stoi<unsigned int>(num));
1576 }
1577 }
1578 else {
1579 for (auto algo : {DNSSECKeeper::RSASHA1, DNSSECKeeper::RSASHA1NSEC3SHA1}) {
1580 if (!DNSCryptoKeyEngine::verifyOne(algo)) {
1581 DNSCryptoKeyEngine::switchOffAlgorithm(algo);
1582 nums.push_back(std::to_string(algo));
1583 }
1584 }
1585 }
1586 if (!nums.empty()) {
1587 if (!g_slogStructured) {
1588 g_log << Logger::Warning << (automatic ? "Automatically" : "Manually") << " disabled DNSSEC algorithms: ";
1589 for (auto i = nums.begin(); i != nums.end(); ++i) {
1590 if (i != nums.begin()) {
1591 g_log << Logger::Warning << ", ";
1592 }
1593 g_log << Logger::Warning << *i;
1594 }
1595 g_log << Logger::Warning << endl;
1596 }
1597 else {
1598 log->info(Logr::Notice, "Disabled DNSSEC algorithms", "automatically", Logging::Loggable(automatic), "algorithms", Logging::IterLoggable(nums.begin(), nums.end()));
1599 }
1600 }
1601
1602 return 0;
1603 }
1604
1605 static void initDontQuery(Logr::log_t log)
1606 {
1607 if (!::arg()["dont-query"].empty()) {
1608 vector<string> ips;
1609 stringtok(ips, ::arg()["dont-query"], ", ");
1610 ips.emplace_back("0.0.0.0");
1611 ips.emplace_back("::");
1612
1613 for (const auto& anIP : ips) {
1614 SyncRes::addDontQuery(anIP);
1615 }
1616 if (!g_slogStructured) {
1617 g_log << Logger::Warning << "Will not send queries to: ";
1618 for (auto i = ips.begin(); i != ips.end(); ++i) {
1619 if (i != ips.begin()) {
1620 g_log << Logger::Warning << ", ";
1621 }
1622 g_log << Logger::Warning << *i;
1623 }
1624 g_log << Logger::Warning << endl;
1625 }
1626 else {
1627 log->info(Logr::Notice, "Will not send queries to", "addresses", Logging::IterLoggable(ips.begin(), ips.end()));
1628 }
1629 }
1630 }
1631
1632 static int initSyncRes(Logr::log_t log)
1633 {
1634 SyncRes::s_minimumTTL = ::arg().asNum("minimum-ttl-override");
1635 SyncRes::s_minimumECSTTL = ::arg().asNum("ecs-minimum-ttl-override");
1636 SyncRes::s_maxnegttl = ::arg().asNum("max-negative-ttl");
1637 SyncRes::s_maxbogusttl = ::arg().asNum("max-cache-bogus-ttl");
1638 SyncRes::s_maxcachettl = max(::arg().asNum("max-cache-ttl"), 15);
1639
1640 SyncRes::s_packetcachettl = ::arg().asNum("packetcache-ttl");
1641 // Cap the packetcache-servfail-ttl and packetcache-negative-ttl to packetcache-ttl
1642 SyncRes::s_packetcacheservfailttl = std::min(static_cast<unsigned int>(::arg().asNum("packetcache-servfail-ttl")), SyncRes::s_packetcachettl);
1643 SyncRes::s_packetcachenegativettl = std::min(static_cast<unsigned int>(::arg().asNum("packetcache-negative-ttl")), SyncRes::s_packetcachettl);
1644
1645 SyncRes::s_serverdownmaxfails = ::arg().asNum("server-down-max-fails");
1646 SyncRes::s_serverdownthrottletime = ::arg().asNum("server-down-throttle-time");
1647 SyncRes::s_unthrottle_n = ::arg().asNum("bypass-server-throttling-probability");
1648 SyncRes::s_nonresolvingnsmaxfails = ::arg().asNum("non-resolving-ns-max-fails");
1649 SyncRes::s_nonresolvingnsthrottletime = ::arg().asNum("non-resolving-ns-throttle-time");
1650 SyncRes::s_serverID = ::arg()["server-id"];
1651 // This bound is dynamically adjusted in SyncRes, depending on qname minimization being active
1652 SyncRes::s_maxqperq = ::arg().asNum("max-qperq");
1653 SyncRes::s_maxnsperresolve = ::arg().asNum("max-ns-per-resolve");
1654 SyncRes::s_maxnsaddressqperq = ::arg().asNum("max-ns-address-qperq");
1655 SyncRes::s_maxtotusec = 1000 * ::arg().asNum("max-total-msec");
1656 SyncRes::s_maxdepth = ::arg().asNum("max-recursion-depth");
1657 SyncRes::s_rootNXTrust = ::arg().mustDo("root-nx-trust");
1658 SyncRes::s_refresh_ttlperc = ::arg().asNum("refresh-on-ttl-perc");
1659 SyncRes::s_locked_ttlperc = ::arg().asNum("record-cache-locked-ttl-perc");
1660 RecursorPacketCache::s_refresh_ttlperc = SyncRes::s_refresh_ttlperc;
1661 SyncRes::s_tcp_fast_open = ::arg().asNum("tcp-fast-open");
1662 SyncRes::s_tcp_fast_open_connect = ::arg().mustDo("tcp-fast-open-connect");
1663
1664 SyncRes::s_dot_to_port_853 = ::arg().mustDo("dot-to-port-853");
1665 SyncRes::s_event_trace_enabled = ::arg().asNum("event-trace-enabled");
1666 SyncRes::s_save_parent_ns_set = ::arg().mustDo("save-parent-ns-set");
1667 SyncRes::s_max_busy_dot_probes = ::arg().asNum("max-busy-dot-probes");
1668 {
1669 uint64_t sse = ::arg().asNum("serve-stale-extensions");
1670 if (sse > std::numeric_limits<uint16_t>::max()) {
1671 SLOG(g_log << Logger::Error << "Illegal serve-stale-extensions value: " << sse << "; range = 0..65536" << endl,
1672 log->info(Logr::Error, "Illegal serve-stale-extensions value; range = 0..65536", "value", Logging::Loggable(sse)));
1673 return 1;
1674 }
1675 MemRecursorCache::s_maxServedStaleExtensions = sse;
1676 NegCache::s_maxServedStaleExtensions = sse;
1677 }
1678
1679 if (SyncRes::s_tcp_fast_open_connect) {
1680 checkFastOpenSysctl(true, log);
1681 checkTFOconnect(log);
1682 }
1683 SyncRes::s_ecsipv4limit = ::arg().asNum("ecs-ipv4-bits");
1684 SyncRes::s_ecsipv6limit = ::arg().asNum("ecs-ipv6-bits");
1685 SyncRes::clearECSStats();
1686 SyncRes::s_ecsipv4cachelimit = ::arg().asNum("ecs-ipv4-cache-bits");
1687 SyncRes::s_ecsipv6cachelimit = ::arg().asNum("ecs-ipv6-cache-bits");
1688 SyncRes::s_ecsipv4nevercache = ::arg().mustDo("ecs-ipv4-never-cache");
1689 SyncRes::s_ecsipv6nevercache = ::arg().mustDo("ecs-ipv6-never-cache");
1690 SyncRes::s_ecscachelimitttl = ::arg().asNum("ecs-cache-limit-ttl");
1691
1692 SyncRes::s_qnameminimization = ::arg().mustDo("qname-minimization");
1693 SyncRes::s_minimize_one_label = ::arg().asNum("qname-minimize-one-label");
1694 SyncRes::s_max_minimize_count = ::arg().asNum("qname-max-minimize-count");
1695
1696 SyncRes::s_hardenNXD = SyncRes::HardenNXD::DNSSEC;
1697 string value = ::arg()["nothing-below-nxdomain"];
1698 if (value == "yes") {
1699 SyncRes::s_hardenNXD = SyncRes::HardenNXD::Yes;
1700 }
1701 else if (value == "no") {
1702 SyncRes::s_hardenNXD = SyncRes::HardenNXD::No;
1703 }
1704 else if (value != "dnssec") {
1705 SLOG(g_log << Logger::Error << "Unknown nothing-below-nxdomain mode: " << value << endl,
1706 log->info(Logr::Error, "Unknown nothing-below-nxdomain mode", "mode", Logging::Loggable(value)));
1707 return 1;
1708 }
1709
1710 if (!::arg().isEmpty("ecs-scope-zero-address")) {
1711 ComboAddress scopeZero(::arg()["ecs-scope-zero-address"]);
1712 SyncRes::setECSScopeZeroAddress(Netmask(scopeZero, scopeZero.isIPv4() ? 32 : 128));
1713 }
1714 else {
1715 Netmask netmask;
1716 bool done = false;
1717
1718 auto addr = pdns::getNonAnyQueryLocalAddress(AF_INET);
1719 if (addr.sin4.sin_family != 0) {
1720 netmask = Netmask(addr, 32);
1721 done = true;
1722 }
1723 if (!done) {
1724 addr = pdns::getNonAnyQueryLocalAddress(AF_INET6);
1725 if (addr.sin4.sin_family != 0) {
1726 netmask = Netmask(addr, 128);
1727 done = true;
1728 }
1729 }
1730 if (!done) {
1731 netmask = Netmask(ComboAddress("127.0.0.1"), 32);
1732 }
1733 SyncRes::setECSScopeZeroAddress(netmask);
1734 }
1735
1736 SyncRes::parseEDNSSubnetAllowlist(::arg()["edns-subnet-whitelist"]);
1737 SyncRes::parseEDNSSubnetAllowlist(::arg()["edns-subnet-allow-list"]);
1738 SyncRes::parseEDNSSubnetAddFor(::arg()["ecs-add-for"]);
1739 g_useIncomingECS = ::arg().mustDo("use-incoming-edns-subnet");
1740 return 0;
1741 }
1742
1743 static void initDistribution(Logr::log_t log)
1744 {
1745 g_balancingFactor = ::arg().asDouble("distribution-load-factor");
1746 if (g_balancingFactor != 0.0 && g_balancingFactor < 1.0) {
1747 g_balancingFactor = 0.0;
1748 SLOG(g_log << Logger::Warning << "Asked to run with a distribution-load-factor below 1.0, disabling it instead" << endl,
1749 log->info(Logr::Warning, "Asked to run with a distribution-load-factor below 1.0, disabling it instead"));
1750 }
1751
1752 #ifdef SO_REUSEPORT
1753 g_reusePort = ::arg().mustDo("reuseport");
1754 #endif
1755
1756 RecThreadInfo::infos().resize(RecThreadInfo::numRecursorThreads());
1757
1758 if (g_reusePort) {
1759 unsigned int threadNum = 1;
1760 if (RecThreadInfo::weDistributeQueries()) {
1761 /* first thread is the handler, then distributors */
1762 for (unsigned int i = 0; i < RecThreadInfo::numDistributors(); i++, threadNum++) {
1763 auto& info = RecThreadInfo::info(threadNum);
1764 auto& deferredAdds = info.getDeferredAdds();
1765 makeUDPServerSockets(deferredAdds, log);
1766 }
1767 }
1768 else {
1769 /* first thread is the handler, there is no distributor here and workers are accepting queries */
1770 for (unsigned int i = 0; i < RecThreadInfo::numUDPWorkers(); i++, threadNum++) {
1771 auto& info = RecThreadInfo::info(threadNum);
1772 auto& deferredAdds = info.getDeferredAdds();
1773 makeUDPServerSockets(deferredAdds, log);
1774 }
1775 }
1776 threadNum = 1 + RecThreadInfo::numDistributors() + RecThreadInfo::numUDPWorkers();
1777 for (unsigned int i = 0; i < RecThreadInfo::numTCPWorkers(); i++, threadNum++) {
1778 auto& info = RecThreadInfo::info(threadNum);
1779 auto& deferredAdds = info.getDeferredAdds();
1780 auto& tcpSockets = info.getTCPSockets();
1781 makeTCPServerSockets(deferredAdds, tcpSockets, log);
1782 }
1783 }
1784 else {
1785 std::set<int> tcpSockets;
1786 /* we don't have reuseport so we can only open one socket per
1787 listening addr:port and everyone will listen on it */
1788 makeUDPServerSockets(s_deferredUDPadds, log);
1789 makeTCPServerSockets(s_deferredTCPadds, tcpSockets, log);
1790
1791 // TCP queries are handled by TCP workers
1792 for (unsigned int i = 0; i < RecThreadInfo::numTCPWorkers(); i++) {
1793 auto& info = RecThreadInfo::info(i + 1 + RecThreadInfo::numDistributors() + RecThreadInfo::numUDPWorkers());
1794 info.setTCPSockets(tcpSockets);
1795 }
1796 }
1797 }
1798
1799 static int initForks(Logr::log_t log)
1800 {
1801 int forks = 0;
1802 for (; forks < ::arg().asNum("processes") - 1; ++forks) {
1803 if (fork() == 0) { // we are child
1804 break;
1805 }
1806 }
1807
1808 if (::arg().mustDo("daemon")) {
1809 SLOG(g_log << Logger::Warning << "Calling daemonize, going to background" << endl,
1810 log->info(Logr::Warning, "Calling daemonize, going to background"));
1811 g_log.toConsole(Logger::Critical);
1812 daemonize(log);
1813 }
1814
1815 if (Utility::getpid() == 1) {
1816 /* We are running as pid 1, register sigterm and sigint handler
1817
1818 The Linux kernel will handle SIGTERM and SIGINT for all processes, except PID 1.
1819 It assumes that the processes running as pid 1 is an "init" like system.
1820 For years, this was a safe assumption, but containers change that: in
1821 most (all?) container implementations, the application itself is running
1822 as pid 1. This means that sending signals to those applications, will not
1823 be handled by default. Results might be "your container not responding
1824 when asking it to stop", or "ctrl-c not working even when the app is
1825 running in the foreground inside a container".
1826
1827 So TL;DR: If we're running pid 1 (container), we should handle SIGTERM and SIGINT ourselves */
1828
1829 signal(SIGTERM, termIntHandler);
1830 signal(SIGINT, termIntHandler);
1831 }
1832
1833 signal(SIGUSR1, usr1Handler);
1834 signal(SIGUSR2, usr2Handler);
1835 signal(SIGPIPE, SIG_IGN); // NOLINT: Posix API
1836 return forks;
1837 }
1838
1839 static int initPorts(Logr::log_t log)
1840 {
1841 int port = ::arg().asNum("udp-source-port-min");
1842 if (port < 1024 || port > 65535) {
1843 SLOG(g_log << Logger::Error << "Unable to launch, udp-source-port-min is not a valid port number" << endl,
1844 log->info(Logr::Error, "Unable to launch, udp-source-port-min is not a valid port number"));
1845 return 99; // this isn't going to fix itself either
1846 }
1847 g_minUdpSourcePort = port;
1848 port = ::arg().asNum("udp-source-port-max");
1849 if (port < 1024 || port > 65535 || port < g_minUdpSourcePort) {
1850 SLOG(g_log << Logger::Error << "Unable to launch, udp-source-port-max is not a valid port number or is smaller than udp-source-port-min" << endl,
1851 log->info(Logr::Error, "Unable to launch, udp-source-port-max is not a valid port number or is smaller than udp-source-port-min"));
1852 return 99; // this isn't going to fix itself either
1853 }
1854 g_maxUdpSourcePort = port;
1855 std::vector<string> parts{};
1856 stringtok(parts, ::arg()["udp-source-port-avoid"], ", ");
1857 for (const auto& part : parts) {
1858 port = std::stoi(part);
1859 if (port < 1024 || port > 65535) {
1860 SLOG(g_log << Logger::Error << "Unable to launch, udp-source-port-avoid contains an invalid port number: " << part << endl,
1861 log->info(Logr::Error, "Unable to launch, udp-source-port-avoid contains an invalid port number", "port", Logging::Loggable(part)));
1862 return 99; // this isn't going to fix itself either
1863 }
1864 g_avoidUdpSourcePorts.insert(port);
1865 }
1866 return 0;
1867 }
1868
1869 static void initSNMP([[maybe_unused]] Logr::log_t log)
1870 {
1871 if (::arg().mustDo("snmp-agent")) {
1872 #ifdef HAVE_NET_SNMP
1873 string setting = ::arg()["snmp-daemon-socket"];
1874 if (setting.empty()) {
1875 setting = ::arg()["snmp-master-socket"];
1876 }
1877 g_snmpAgent = std::make_shared<RecursorSNMPAgent>("recursor", setting);
1878 g_snmpAgent->run();
1879 #else
1880 const std::string msg = "snmp-agent set but SNMP support not compiled in";
1881 SLOG(g_log << Logger::Error << msg << endl,
1882 log->info(Logr::Error, msg));
1883 #endif // HAVE_NET_SNMP
1884 }
1885 }
1886
1887 static int initControl(Logr::log_t log, uid_t newuid, int forks)
1888 {
1889 if (!::arg()["chroot"].empty()) {
1890 #ifdef HAVE_SYSTEMD
1891 char* ns;
1892 ns = getenv("NOTIFY_SOCKET");
1893 if (ns != nullptr) {
1894 SLOG(g_log << Logger::Error << "Unable to chroot when running from systemd. Please disable chroot= or set the 'Type' for this service to 'simple'" << endl,
1895 log->info(Logr::Error, "Unable to chroot when running from systemd. Please disable chroot= or set the 'Type' for this service to 'simple'"));
1896 return 1;
1897 }
1898 #endif
1899 if (chroot(::arg()["chroot"].c_str()) < 0 || chdir("/") < 0) {
1900 int err = errno;
1901 SLOG(g_log << Logger::Error << "Unable to chroot to '" + ::arg()["chroot"] + "': " << stringerror(err) << ", exiting" << endl,
1902 log->error(Logr::Error, err, "Unable to chroot", "chroot", Logging::Loggable(::arg()["chroot"])));
1903 return 1;
1904 }
1905 SLOG(g_log << Logger::Info << "Chrooted to '" << ::arg()["chroot"] << "'" << endl,
1906 log->info(Logr::Info, "Chrooted", "chroot", Logging::Loggable(::arg()["chroot"])));
1907 }
1908
1909 checkSocketDir(log);
1910
1911 g_pidfname = ::arg()["socket-dir"] + "/" + g_programname + ".pid";
1912 if (!g_pidfname.empty()) {
1913 unlink(g_pidfname.c_str()); // remove possible old pid file
1914 }
1915 writePid(log);
1916
1917 makeControlChannelSocket(::arg().asNum("processes") > 1 ? forks : -1);
1918
1919 Utility::dropUserPrivs(newuid);
1920 try {
1921 /* we might still have capabilities remaining, for example if we have been started as root
1922 without --setuid (please don't do that) or as an unprivileged user with ambient capabilities
1923 like CAP_NET_BIND_SERVICE.
1924 */
1925 dropCapabilities();
1926 }
1927 catch (const std::exception& e) {
1928 SLOG(g_log << Logger::Warning << e.what() << endl,
1929 log->error(Logr::Warning, e.what(), "Could not drop capabilities"));
1930 }
1931 return 0;
1932 }
1933
1934 static void initSuffixMatchNodes([[maybe_unused]] Logr::log_t log)
1935 {
1936 {
1937 SuffixMatchNode dontThrottleNames;
1938 vector<string> parts;
1939 stringtok(parts, ::arg()["dont-throttle-names"], " ,");
1940 for (const auto& part : parts) {
1941 dontThrottleNames.add(DNSName(part));
1942 }
1943 g_dontThrottleNames.setState(std::move(dontThrottleNames));
1944
1945 parts.clear();
1946 NetmaskGroup dontThrottleNetmasks;
1947 stringtok(parts, ::arg()["dont-throttle-netmasks"], " ,");
1948 for (const auto& part : parts) {
1949 dontThrottleNetmasks.addMask(Netmask(part));
1950 }
1951 g_dontThrottleNetmasks.setState(std::move(dontThrottleNetmasks));
1952 }
1953
1954 {
1955 SuffixMatchNode xdnssecNames;
1956 vector<string> parts;
1957 stringtok(parts, ::arg()["x-dnssec-names"], " ,");
1958 for (const auto& part : parts) {
1959 xdnssecNames.add(DNSName(part));
1960 }
1961 g_xdnssec.setState(std::move(xdnssecNames));
1962 }
1963
1964 {
1965 SuffixMatchNode dotauthNames;
1966 vector<string> parts;
1967 stringtok(parts, ::arg()["dot-to-auth-names"], " ,");
1968 #ifndef HAVE_DNS_OVER_TLS
1969 if (!parts.empty()) {
1970 SLOG(g_log << Logger::Error << "dot-to-auth-names setting contains names, but Recursor was built without DNS over TLS support. Setting will be ignored." << endl,
1971 log->info(Logr::Error, "dot-to-auth-names setting contains names, but Recursor was built without DNS over TLS support. Setting will be ignored"));
1972 }
1973 #endif
1974 for (const auto& part : parts) {
1975 dotauthNames.add(DNSName(part));
1976 }
1977 g_DoTToAuthNames.setState(std::move(dotauthNames));
1978 }
1979 }
1980
1981 static void initCarbon()
1982 {
1983 CarbonConfig config;
1984 stringtok(config.servers, arg()["carbon-server"], ", ");
1985 config.hostname = arg()["carbon-ourname"];
1986 config.instance_name = arg()["carbon-instance"];
1987 config.namespace_name = arg()["carbon-namespace"];
1988 g_carbonConfig.setState(std::move(config));
1989 }
1990
1991 static int initDNS64(Logr::log_t log)
1992 {
1993 if (!::arg()["dns64-prefix"].empty()) {
1994 try {
1995 auto dns64Prefix = Netmask(::arg()["dns64-prefix"]);
1996 if (dns64Prefix.getBits() != 96) {
1997 SLOG(g_log << Logger::Error << "Invalid prefix for 'dns64-prefix', the current implementation only supports /96 prefixes: " << ::arg()["dns64-prefix"] << endl,
1998 log->info(Logr::Error, "Invalid prefix for 'dns64-prefix', the current implementation only supports /96 prefixes", "prefix", Logging::Loggable(::arg()["dns64-prefix"])));
1999 return 1;
2000 }
2001 g_dns64Prefix = dns64Prefix.getNetwork();
2002 g_dns64PrefixReverse = reverseNameFromIP(*g_dns64Prefix);
2003 /* /96 is 24 nibbles + 2 for "ip6.arpa." */
2004 while (g_dns64PrefixReverse.countLabels() > 26) {
2005 g_dns64PrefixReverse.chopOff();
2006 }
2007 }
2008 catch (const NetmaskException& ne) {
2009 SLOG(g_log << Logger::Error << "Invalid prefix '" << ::arg()["dns64-prefix"] << "' for 'dns64-prefix': " << ne.reason << endl,
2010 log->info(Logr::Error, "Invalid prefix", "dns64-prefix", Logging::Loggable(::arg()["dns64-prefix"])));
2011 return 1;
2012 }
2013 }
2014 return 0;
2015 }
2016
2017 static int serviceMain(Logr::log_t log)
2018 {
2019 g_log.setName(g_programname);
2020 g_log.disableSyslog(::arg().mustDo("disable-syslog"));
2021 g_log.setTimestamps(::arg().mustDo("log-timestamp"));
2022 g_regressionTestMode = ::arg().mustDo("devonly-regression-test-mode");
2023
2024 if (!::arg()["logging-facility"].empty()) {
2025 int val = logFacilityToLOG(::arg().asNum("logging-facility"));
2026 if (val >= 0) {
2027 g_log.setFacility(val);
2028 }
2029 else {
2030 SLOG(g_log << Logger::Error << "Unknown logging facility " << ::arg().asNum("logging-facility") << endl,
2031 log->info(Logr::Error, "Unknown logging facility", "facility", Logging::Loggable(::arg().asNum("logging-facility"))));
2032 }
2033 }
2034
2035 g_disthashseed = dns_random_uint32();
2036
2037 int ret = initNet(log);
2038 if (ret != 0) {
2039 return ret;
2040 }
2041 // keep this ABOVE loadRecursorLuaConfig!
2042 ret = initDNSSEC(log);
2043 if (ret != 0) {
2044 return ret;
2045 }
2046 g_maxCacheEntries = ::arg().asNum("max-cache-entries");
2047
2048 luaConfigDelayedThreads delayedLuaThreads;
2049 try {
2050 ProxyMapping proxyMapping;
2051 loadRecursorLuaConfig(::arg()["lua-config-file"], delayedLuaThreads, proxyMapping);
2052 // Initial proxy mapping
2053 g_proxyMapping = proxyMapping.empty() ? nullptr : std::make_unique<ProxyMapping>(proxyMapping);
2054 }
2055 catch (PDNSException& e) {
2056 SLOG(g_log << Logger::Error << "Cannot load Lua configuration: " << e.reason << endl,
2057 log->error(Logr::Error, e.reason, "Cannot load Lua configuration"));
2058 return 1;
2059 }
2060
2061 parseACLs();
2062 initPublicSuffixList(::arg()["public-suffix-list-file"]);
2063
2064 initDontQuery(log);
2065
2066 RecThreadInfo::setWeDistributeQueries(::arg().mustDo("pdns-distributes-queries"));
2067 if (RecThreadInfo::weDistributeQueries()) {
2068 SLOG(g_log << Logger::Warning << "PowerDNS Recursor itself will distribute queries over threads" << endl,
2069 log->info(Logr::Notice, "PowerDNS Recursor itself will distribute queries over threads"));
2070 }
2071
2072 g_outgoingEDNSBufsize = ::arg().asNum("edns-outgoing-bufsize");
2073
2074 if (::arg()["trace"] == "fail") {
2075 SyncRes::setDefaultLogMode(SyncRes::Store);
2076 }
2077 else if (::arg().mustDo("trace")) {
2078 SyncRes::setDefaultLogMode(SyncRes::Log);
2079 ::arg().set("quiet") = "no";
2080 g_quiet = false;
2081 }
2082
2083 ret = initSyncRes(log);
2084 if (ret != 0) {
2085 return ret;
2086 }
2087
2088 g_proxyProtocolACL.toMasks(::arg()["proxy-protocol-from"]);
2089 g_proxyProtocolMaximumSize = ::arg().asNum("proxy-protocol-maximum-size");
2090
2091 ret = initDNS64(log);
2092 if (ret != 0) {
2093 return ret;
2094 }
2095 g_networkTimeoutMsec = ::arg().asNum("network-timeout");
2096
2097 std::tie(g_initialDomainMap, g_initialAllowNotifyFor) = parseZoneConfiguration(g_yamlSettings);
2098
2099 g_latencyStatSize = ::arg().asNum("latency-statistic-size");
2100
2101 g_logCommonErrors = ::arg().mustDo("log-common-errors");
2102 g_logRPZChanges = ::arg().mustDo("log-rpz-changes");
2103
2104 g_anyToTcp = ::arg().mustDo("any-to-tcp");
2105 g_allowNoRD = ::arg().mustDo("allow-no-rd");
2106 g_udpTruncationThreshold = ::arg().asNum("udp-truncation-threshold");
2107
2108 g_lowercaseOutgoing = ::arg().mustDo("lowercase-outgoing");
2109
2110 g_paddingFrom.toMasks(::arg()["edns-padding-from"]);
2111 if (::arg()["edns-padding-mode"] == "always") {
2112 g_paddingMode = PaddingMode::Always;
2113 }
2114 else if (::arg()["edns-padding-mode"] == "padded-queries-only") {
2115 g_paddingMode = PaddingMode::PaddedQueries;
2116 }
2117 else {
2118 SLOG(g_log << Logger::Error << "Unknown edns-padding-mode: " << ::arg()["edns-padding-mode"] << endl,
2119 log->info(Logr::Error, "Unknown edns-padding-mode", "edns-padding-mode", Logging::Loggable(::arg()["edns-padding-mode"])));
2120 return 1;
2121 }
2122 g_paddingTag = ::arg().asNum("edns-padding-tag");
2123 g_paddingOutgoing = ::arg().mustDo("edns-padding-out");
2124
2125 RecThreadInfo::setNumDistributorThreads(::arg().asNum("distributor-threads"));
2126 RecThreadInfo::setNumUDPWorkerThreads(::arg().asNum("threads"));
2127 if (RecThreadInfo::numUDPWorkers() < 1) {
2128 SLOG(g_log << Logger::Warning << "Asked to run with 0 threads, raising to 1 instead" << endl,
2129 log->info(Logr::Warning, "Asked to run with 0 threads, raising to 1 instead"));
2130 RecThreadInfo::setNumUDPWorkerThreads(1);
2131 }
2132 RecThreadInfo::setNumTCPWorkerThreads(::arg().asNum("tcp-threads"));
2133 if (RecThreadInfo::numTCPWorkers() < 1) {
2134 SLOG(g_log << Logger::Warning << "Asked to run with 0 TCP threads, raising to 1 instead" << endl,
2135 log->info(Logr::Warning, "Asked to run with 0 TCP threads, raising to 1 instead"));
2136 RecThreadInfo::setNumTCPWorkerThreads(1);
2137 }
2138
2139 g_maxMThreads = ::arg().asNum("max-mthreads");
2140
2141 int64_t maxInFlight = ::arg().asNum("max-concurrent-requests-per-tcp-connection");
2142 if (maxInFlight < 1 || maxInFlight > USHRT_MAX || maxInFlight >= g_maxMThreads) {
2143 SLOG(g_log << Logger::Warning << "Asked to run with illegal max-concurrent-requests-per-tcp-connection, setting to default (10)" << endl,
2144 log->info(Logr::Warning, "Asked to run with illegal max-concurrent-requests-per-tcp-connection, setting to default (10)"));
2145 TCPConnection::s_maxInFlight = 10;
2146 }
2147 else {
2148 TCPConnection::s_maxInFlight = maxInFlight;
2149 }
2150
2151 int64_t millis = ::arg().asNum("tcp-out-max-idle-ms");
2152 TCPOutConnectionManager::s_maxIdleTime = timeval{millis / 1000, (static_cast<suseconds_t>(millis) % 1000) * 1000};
2153 TCPOutConnectionManager::s_maxIdlePerAuth = ::arg().asNum("tcp-out-max-idle-per-auth");
2154 TCPOutConnectionManager::s_maxQueries = ::arg().asNum("tcp-out-max-queries");
2155 TCPOutConnectionManager::s_maxIdlePerThread = ::arg().asNum("tcp-out-max-idle-per-thread");
2156
2157 g_gettagNeedsEDNSOptions = ::arg().mustDo("gettag-needs-edns-options");
2158
2159 s_statisticsInterval = ::arg().asNum("statistics-interval");
2160
2161 SyncRes::s_addExtendedResolutionDNSErrors = ::arg().mustDo("extended-resolution-errors");
2162
2163 if (::arg().asNum("aggressive-nsec-cache-size") > 0) {
2164 if (g_dnssecmode == DNSSECMode::ValidateAll || g_dnssecmode == DNSSECMode::ValidateForLog || g_dnssecmode == DNSSECMode::Process) {
2165 g_aggressiveNSECCache = make_unique<AggressiveNSECCache>(::arg().asNum("aggressive-nsec-cache-size"));
2166 }
2167 else {
2168 SLOG(g_log << Logger::Warning << "Aggressive NSEC/NSEC3 caching is enabled but DNSSEC validation is not set to 'validate', 'log-fail' or 'process', ignoring" << endl,
2169 log->info(Logr::Warning, "Aggressive NSEC/NSEC3 caching is enabled but DNSSEC validation is not set to 'validate', 'log-fail' or 'process', ignoring"));
2170 }
2171 }
2172
2173 AggressiveNSECCache::s_maxNSEC3CommonPrefix = static_cast<uint8_t>(std::round(std::log2(::arg().asNum("aggressive-cache-min-nsec3-hit-ratio"))));
2174 SLOG(g_log << Logger::Debug << "NSEC3 aggressive cache tuning: aggressive-cache-min-nsec3-hit-ratio: " << ::arg().asNum("aggressive-cache-min-nsec3-hit-ratio") << " max common prefix bits: " << std::to_string(AggressiveNSECCache::s_maxNSEC3CommonPrefix) << endl,
2175 log->info(Logr::Debug, "NSEC3 aggressive cache tuning", "aggressive-cache-min-nsec3-hit-ratio", Logging::Loggable(::arg().asNum("aggressive-cache-min-nsec3-hit-ratio")), "maxCommonPrefixBits", Logging::Loggable(AggressiveNSECCache::s_maxNSEC3CommonPrefix)));
2176
2177 initSuffixMatchNodes(log);
2178 initCarbon();
2179 initDistribution(log);
2180
2181 #ifdef NOD_ENABLED
2182 // Setup newly observed domain globals
2183 setupNODGlobal();
2184 #endif /* NOD_ENABLED */
2185
2186 auto forks = initForks(log);
2187
2188 checkOrFixFDS(log);
2189
2190 #ifdef HAVE_LIBSODIUM
2191 if (sodium_init() == -1) {
2192 SLOG(g_log << Logger::Error << "Unable to initialize sodium crypto library" << endl,
2193 log->info(Logr::Error, "Unable to initialize sodium crypto library"));
2194 return 99;
2195 }
2196 #endif
2197
2198 openssl_thread_setup();
2199 openssl_seed();
2200
2201 gid_t newgid = 0;
2202 if (!::arg()["setgid"].empty()) {
2203 newgid = strToGID(::arg()["setgid"]);
2204 }
2205 uid_t newuid = 0;
2206 if (!::arg()["setuid"].empty()) {
2207 newuid = strToUID(::arg()["setuid"]);
2208 }
2209
2210 Utility::dropGroupPrivs(newuid, newgid);
2211
2212 ret = initControl(log, newuid, forks);
2213 if (ret != 0) {
2214 return ret;
2215 }
2216
2217 startLuaConfigDelayedThreads(delayedLuaThreads, g_luaconfs.getCopy().generation);
2218 delayedLuaThreads.rpzPrimaryThreads.clear(); // no longer needed
2219
2220 RecThreadInfo::makeThreadPipes(log);
2221
2222 g_tcpTimeout = ::arg().asNum("client-tcp-timeout");
2223 g_maxTCPPerClient = ::arg().asNum("max-tcp-per-client");
2224 g_tcpMaxQueriesPerConn = ::arg().asNum("max-tcp-queries-per-connection");
2225 g_maxUDPQueriesPerRound = ::arg().asNum("max-udp-queries-per-round");
2226
2227 g_useKernelTimestamp = ::arg().mustDo("protobuf-use-kernel-timestamp");
2228
2229 disableStats(StatComponent::API, ::arg()["stats-api-blacklist"]);
2230 disableStats(StatComponent::Carbon, ::arg()["stats-carbon-blacklist"]);
2231 disableStats(StatComponent::RecControl, ::arg()["stats-rec-control-blacklist"]);
2232 disableStats(StatComponent::SNMP, ::arg()["stats-snmp-blacklist"]);
2233
2234 disableStats(StatComponent::API, ::arg()["stats-api-disabled-list"]);
2235 disableStats(StatComponent::Carbon, ::arg()["stats-carbon-disabled-list"]);
2236 disableStats(StatComponent::RecControl, ::arg()["stats-rec-control-disabled-list"]);
2237 disableStats(StatComponent::SNMP, ::arg()["stats-snmp-disabled-list"]);
2238
2239 // Run before any thread doing stats related things
2240 registerAllStats();
2241
2242 initSNMP(log);
2243
2244 ret = initPorts(log);
2245 if (ret != 0) {
2246 return ret;
2247 }
2248
2249 return RecThreadInfo::runThreads(log);
2250 }
2251
2252 static void handlePipeRequest(int fileDesc, FDMultiplexer::funcparam_t& /* var */)
2253 {
2254 ThreadMSG* tmsg = nullptr;
2255
2256 if (read(fileDesc, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // fd == readToThread || fd == readQueriesToThread NOLINT: sizeof correct
2257 unixDie("read from thread pipe returned wrong size or error");
2258 }
2259
2260 void* resp = nullptr;
2261 try {
2262 resp = tmsg->func();
2263 }
2264 catch (std::exception& e) {
2265 if (g_logCommonErrors) {
2266 SLOG(g_log << Logger::Error << "PIPE function we executed created exception: " << e.what() << endl, // but what if they wanted an answer.. we send 0
2267 g_slog->withName("runtime")->error(Logr::Error, e.what(), "PIPE function we executed created exception", "exception", Logging::Loggable("std::exception")));
2268 }
2269 }
2270 catch (PDNSException& e) {
2271 if (g_logCommonErrors) {
2272 SLOG(g_log << Logger::Error << "PIPE function we executed created PDNS exception: " << e.reason << endl, // but what if they wanted an answer.. we send 0
2273 g_slog->withName("runtime")->error(Logr::Error, e.reason, "PIPE function we executed created exception", "exception", Logging::Loggable("PDNSException")));
2274 }
2275 }
2276 if (tmsg->wantAnswer) {
2277 if (write(RecThreadInfo::self().getPipes().writeFromThread, &resp, sizeof(resp)) != sizeof(resp)) {
2278 delete tmsg; // NOLINT: manual ownership handling
2279 unixDie("write to thread pipe returned wrong size or error");
2280 }
2281 }
2282
2283 delete tmsg; // NOLINT: manual ownership handling
2284 }
2285
2286 static void handleRCC(int fileDesc, FDMultiplexer::funcparam_t& /* var */)
2287 {
2288 auto log = g_slog->withName("control");
2289 try {
2290 FDWrapper clientfd = accept(fileDesc, nullptr, nullptr);
2291 if (clientfd == -1) {
2292 throw PDNSException("accept failed");
2293 }
2294 string msg = g_rcc.recv(clientfd).d_str;
2295 SLOG(g_log << Logger::Info << "Received rec_control command '" << msg << "' via controlsocket" << endl,
2296 log->info(Logr::Info, "Received rec_control command via control socket", "command", Logging::Loggable(msg)));
2297
2298 RecursorControlParser::func_t* command = nullptr;
2299 auto answer = RecursorControlParser::getAnswer(clientfd, msg, &command);
2300
2301 g_rcc.send(clientfd, answer);
2302 command();
2303 }
2304 catch (const std::exception& e) {
2305 SLOG(g_log << Logger::Error << "Error dealing with control socket request: " << e.what() << endl,
2306 log->error(Logr::Error, e.what(), "Exception while dealing with control socket request", "exception", Logging::Loggable("std::exception")));
2307 }
2308 catch (const PDNSException& ae) {
2309 SLOG(g_log << Logger::Error << "Error dealing with control socket request: " << ae.reason << endl,
2310 log->error(Logr::Error, ae.reason, "Exception while dealing with control socket request", "exception", Logging::Loggable("PDNSException")));
2311 }
2312 }
2313
2314 class PeriodicTask
2315 {
2316 public:
2317 PeriodicTask(const string& aName, time_t aTime) :
2318 period{aTime, 0}, name(aName)
2319 {
2320 if (aTime <= 0) {
2321 throw PDNSException("Invalid period of periodic task " + aName);
2322 }
2323 }
2324
2325 void runIfDue(struct timeval& now, const std::function<void()>& function)
2326 {
2327 if (last_run < now - period) {
2328 function();
2329 Utility::gettimeofday(&last_run);
2330 now = last_run;
2331 }
2332 }
2333
2334 [[nodiscard]] time_t getPeriod() const
2335 {
2336 return period.tv_sec;
2337 }
2338
2339 void setPeriod(time_t newperiod)
2340 {
2341 period.tv_sec = newperiod;
2342 }
2343
2344 void updateLastRun()
2345 {
2346 Utility::gettimeofday(&last_run);
2347 }
2348
2349 [[nodiscard]] bool hasRun() const
2350 {
2351 return last_run.tv_sec != 0 || last_run.tv_usec != 0;
2352 }
2353
2354 private:
2355 struct timeval last_run
2356 {
2357 0, 0
2358 };
2359 struct timeval period;
2360 string name;
2361 };
2362
2363 static void houseKeepingWork(Logr::log_t log)
2364 {
2365 struct timeval now
2366 {
2367 };
2368 Utility::gettimeofday(&now);
2369 t_Counters.updateSnap(now, g_regressionTestMode);
2370
2371 // Below are the tasks that run for every recursorThread, including handler and taskThread
2372
2373 static thread_local PeriodicTask pruneTCPTask{"pruneTCPTask", 5};
2374 pruneTCPTask.runIfDue(now, [now]() {
2375 t_tcp_manager.cleanup(now);
2376 });
2377
2378 const auto& info = RecThreadInfo::self();
2379
2380 // Threads handling packets process config changes in the input path, but not all threads process input packets
2381 // distr threads only process TCP, so that may not happenn very often. So do all periodically.
2382 static thread_local PeriodicTask exportConfigTask{"exportConfigTask", 30};
2383 auto luaconfsLocal = g_luaconfs.getLocal();
2384 exportConfigTask.runIfDue(now, [&luaconfsLocal]() {
2385 checkProtobufExport(luaconfsLocal);
2386 checkOutgoingProtobufExport(luaconfsLocal);
2387 #ifdef HAVE_FSTRM
2388 checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
2389 checkFrameStreamExport(luaconfsLocal, luaconfsLocal->nodFrameStreamExportConfig, t_nodFrameStreamServersInfo);
2390 #endif
2391 });
2392
2393 // Below are the thread specific tasks for the handler and the taskThread
2394 // Likley a few handler tasks could be moved to the taskThread
2395 if (info.isTaskThread()) {
2396 // TaskQueue is run always
2397 runTasks(10, g_logCommonErrors);
2398
2399 static PeriodicTask ztcTask{"ZTC", 60};
2400 static map<DNSName, RecZoneToCache::State> ztcStates;
2401 ztcTask.runIfDue(now, [&luaconfsLocal]() {
2402 RecZoneToCache::maintainStates(luaconfsLocal->ztcConfigs, ztcStates, luaconfsLocal->generation);
2403 for (const auto& ztc : luaconfsLocal->ztcConfigs) {
2404 RecZoneToCache::ZoneToCache(ztc.second, ztcStates.at(ztc.first));
2405 }
2406 });
2407 }
2408 else if (info.isHandler()) {
2409 if (g_packetCache) {
2410 static PeriodicTask packetCacheTask{"packetCacheTask", 5};
2411 packetCacheTask.runIfDue(now, [now]() {
2412 g_packetCache->doPruneTo(now.tv_sec, g_maxPacketCacheEntries);
2413 });
2414 }
2415 static PeriodicTask recordCachePruneTask{"RecordCachePruneTask", 5};
2416 recordCachePruneTask.runIfDue(now, [now]() {
2417 g_recCache->doPrune(now.tv_sec, g_maxCacheEntries);
2418 });
2419
2420 static PeriodicTask negCachePruneTask{"NegCachePrunteTask", 5};
2421 negCachePruneTask.runIfDue(now, [now]() {
2422 g_negCache->prune(now.tv_sec, g_maxCacheEntries / 8);
2423 });
2424
2425 static PeriodicTask aggrNSECPruneTask{"AggrNSECPruneTask", 5};
2426 aggrNSECPruneTask.runIfDue(now, [now]() {
2427 if (g_aggressiveNSECCache) {
2428 g_aggressiveNSECCache->prune(now.tv_sec);
2429 }
2430 });
2431
2432 static PeriodicTask pruneNSpeedTask{"pruneNSSpeedTask", 30};
2433 pruneNSpeedTask.runIfDue(now, [now]() {
2434 SyncRes::pruneNSSpeeds(now.tv_sec - 300);
2435 });
2436
2437 static PeriodicTask pruneEDNSTask{"pruneEDNSTask", 60};
2438 pruneEDNSTask.runIfDue(now, [now]() {
2439 SyncRes::pruneEDNSStatuses(now.tv_sec);
2440 });
2441
2442 if (SyncRes::s_max_busy_dot_probes > 0) {
2443 static PeriodicTask pruneDoTProbeMap{"pruneDoTProbeMapTask", 60};
2444 pruneDoTProbeMap.runIfDue(now, [now]() {
2445 SyncRes::pruneDoTProbeMap(now.tv_sec);
2446 });
2447 }
2448
2449 static PeriodicTask pruneThrottledTask{"pruneThrottledTask", 5};
2450 pruneThrottledTask.runIfDue(now, [now]() {
2451 SyncRes::pruneThrottledServers(now.tv_sec);
2452 });
2453
2454 static PeriodicTask pruneFailedServersTask{"pruneFailedServerTask", 5};
2455 pruneFailedServersTask.runIfDue(now, [now]() {
2456 SyncRes::pruneFailedServers(now.tv_sec - static_cast<time_t>(SyncRes::s_serverdownthrottletime * 10));
2457 });
2458
2459 static PeriodicTask pruneNonResolvingTask{"pruneNonResolvingTask", 5};
2460 pruneNonResolvingTask.runIfDue(now, [now]() {
2461 SyncRes::pruneNonResolving(now.tv_sec - SyncRes::s_nonresolvingnsthrottletime);
2462 });
2463
2464 static PeriodicTask pruneSaveParentSetTask{"pruneSaveParentSetTask", 60};
2465 pruneSaveParentSetTask.runIfDue(now, [now]() {
2466 SyncRes::pruneSaveParentsNSSets(now.tv_sec);
2467 });
2468
2469 // By default, refresh at 80% of max-cache-ttl with a minimum period of 10s
2470 const unsigned int minRootRefreshInterval = 10;
2471 static PeriodicTask rootUpdateTask{"rootUpdateTask", std::max(SyncRes::s_maxcachettl * 8 / 10, minRootRefreshInterval)};
2472 rootUpdateTask.runIfDue(now, [now, &log, minRootRefreshInterval]() {
2473 int res = 0;
2474 if (!g_regressionTestMode) {
2475 res = SyncRes::getRootNS(now, nullptr, 0, log);
2476 }
2477 if (res == 0) {
2478 // Success, go back to the defaut period
2479 rootUpdateTask.setPeriod(std::max(SyncRes::s_maxcachettl * 8 / 10, minRootRefreshInterval));
2480 }
2481 else {
2482 // On failure, go to the middle of the remaining period (initially 80% / 8 = 10%) and shorten the interval on each
2483 // failure by dividing the existing interval by 8, keeping the minimum interval at 10s.
2484 // So with a 1 day period and failures we'll see a refresh attempt at 69120, 69120+11520, 69120+11520+1440, ...
2485 rootUpdateTask.setPeriod(std::max<time_t>(rootUpdateTask.getPeriod() / 8, minRootRefreshInterval));
2486 }
2487 });
2488
2489 static PeriodicTask secpollTask{"secpollTask", 3600};
2490 static time_t t_last_secpoll;
2491 secpollTask.runIfDue(now, [&log]() {
2492 try {
2493 doSecPoll(&t_last_secpoll, log);
2494 }
2495 catch (const std::exception& e) {
2496 SLOG(g_log << Logger::Error << "Exception while performing security poll: " << e.what() << endl,
2497 log->error(Logr::Error, e.what(), "Exception while performing security poll"));
2498 }
2499 catch (const PDNSException& e) {
2500 SLOG(g_log << Logger::Error << "Exception while performing security poll: " << e.reason << endl,
2501 log->error(Logr::Error, e.reason, "Exception while performing security poll"));
2502 }
2503 catch (const ImmediateServFailException& e) {
2504 SLOG(g_log << Logger::Error << "Exception while performing security poll: " << e.reason << endl,
2505 log->error(Logr::Error, e.reason, "Exception while performing security poll"));
2506 }
2507 catch (const PolicyHitException& e) {
2508 SLOG(g_log << Logger::Error << "Policy hit while performing security poll" << endl,
2509 log->info(Logr::Error, "Policy hit while performing security poll"));
2510 }
2511 catch (...) {
2512 SLOG(g_log << Logger::Error << "Exception while performing security poll" << endl,
2513 log->info(Logr::Error, "Exception while performing security poll"));
2514 }
2515 });
2516
2517 const time_t taInterval = std::max(1, static_cast<int>(luaconfsLocal->trustAnchorFileInfo.interval) * 3600);
2518 static PeriodicTask trustAnchorTask{"trustAnchorTask", taInterval};
2519 if (!trustAnchorTask.hasRun()) {
2520 // Loading the Lua config file already "refreshed" the TAs
2521 trustAnchorTask.updateLastRun();
2522 }
2523 // interval might have ben updated
2524 trustAnchorTask.setPeriod(taInterval);
2525 trustAnchorTask.runIfDue(now, [&luaconfsLocal, &log]() {
2526 if (!luaconfsLocal->trustAnchorFileInfo.fname.empty() && luaconfsLocal->trustAnchorFileInfo.interval != 0) {
2527 SLOG(g_log << Logger::Debug << "Refreshing Trust Anchors from file" << endl,
2528 log->info(Logr::Debug, "Refreshing Trust Anchors from file"));
2529 try {
2530 map<DNSName, dsmap_t> dsAnchors;
2531 if (updateTrustAnchorsFromFile(luaconfsLocal->trustAnchorFileInfo.fname, dsAnchors, log)) {
2532 g_luaconfs.modify([&dsAnchors](LuaConfigItems& lci) {
2533 lci.dsAnchors = dsAnchors;
2534 });
2535 }
2536 }
2537 catch (const PDNSException& pe) {
2538 SLOG(g_log << Logger::Error << "Unable to update Trust Anchors: " << pe.reason << endl,
2539 log->error(Logr::Error, pe.reason, "Unable to update Trust Anchors"));
2540 }
2541 }
2542 });
2543 }
2544 t_Counters.updateSnap(g_regressionTestMode);
2545 }
2546
2547 static void houseKeeping(void* /* ignored */)
2548 {
2549 auto log = g_slog->withName("housekeeping");
2550 static thread_local bool t_running; // houseKeeping can get suspended in secpoll, and be restarted, which makes us do duplicate work
2551
2552 try {
2553 if (t_running) {
2554 return;
2555 }
2556 t_running = true;
2557 houseKeepingWork(log);
2558 t_running = false;
2559 }
2560 catch (const PDNSException& ae) {
2561 t_running = false;
2562 SLOG(g_log << Logger::Error << "Fatal error in housekeeping thread: " << ae.reason << endl,
2563 log->error(Logr::Error, ae.reason, "Fatal error in housekeeping thread"));
2564 throw;
2565 }
2566 catch (...) {
2567 t_running = false;
2568 SLOG(g_log << Logger::Error << "Uncaught exception in housekeeping thread" << endl,
2569 log->info(Logr::Error, "Uncaught exception in housekeeping thread"));
2570 throw;
2571 }
2572 }
2573
2574 static void runLuaMaintenance(RecThreadInfo& threadInfo, time_t& last_lua_maintenance, time_t luaMaintenanceInterval)
2575 {
2576 if (t_pdl != nullptr) {
2577 // lua-dns-script directive is present, call the maintenance callback if needed
2578 if (threadInfo.isWorker()) { // either UDP of TCP worker
2579 // Only on threads processing queries
2580 if (g_now.tv_sec - last_lua_maintenance >= luaMaintenanceInterval) {
2581 struct timeval start
2582 {
2583 };
2584 Utility::gettimeofday(&start);
2585 t_pdl->maintenance();
2586 last_lua_maintenance = g_now.tv_sec;
2587 struct timeval stop
2588 {
2589 };
2590 Utility::gettimeofday(&stop);
2591 t_Counters.at(rec::Counter::maintenanceUsec) += uSec(stop - start);
2592 ++t_Counters.at(rec::Counter::maintenanceCalls);
2593 }
2594 }
2595 }
2596 }
2597
2598 static void runTCPMaintenance(RecThreadInfo& threadInfo, bool& listenOnTCP, unsigned int maxTcpClients)
2599 {
2600 if (threadInfo.isTCPListener()) {
2601 if (listenOnTCP) {
2602 if (TCPConnection::getCurrentConnections() > maxTcpClients) { // shutdown, too many connections
2603 for (const auto fileDesc : threadInfo.getTCPSockets()) {
2604 t_fdm->removeReadFD(fileDesc);
2605 }
2606 listenOnTCP = false;
2607 }
2608 }
2609 else {
2610 if (TCPConnection::getCurrentConnections() <= maxTcpClients) { // reenable
2611 for (const auto fileDesc : threadInfo.getTCPSockets()) {
2612 t_fdm->addReadFD(fileDesc, handleNewTCPQuestion);
2613 }
2614 listenOnTCP = true;
2615 }
2616 }
2617 }
2618 }
2619
2620 static void recLoop()
2621 {
2622 unsigned int maxTcpClients = ::arg().asNum("max-tcp-clients");
2623 bool listenOnTCP{true};
2624 time_t last_stat = 0;
2625 time_t last_carbon = 0;
2626 time_t last_lua_maintenance = 0;
2627 time_t carbonInterval = ::arg().asNum("carbon-interval");
2628 time_t luaMaintenanceInterval = ::arg().asNum("lua-maintenance-interval");
2629
2630 auto& threadInfo = RecThreadInfo::self();
2631
2632 while (!RecursorControlChannel::stop) {
2633 while (g_multiTasker->schedule(g_now)) {
2634 ; // MTasker letting the mthreads do their thing
2635 }
2636
2637 // Use primes, it avoid not being scheduled in cases where the counter has a regular pattern.
2638 // We want to call handler thread often, it gets scheduled about 2 times per second
2639 if (((threadInfo.isHandler() || threadInfo.isTaskThread()) && s_counter % 11 == 0) || s_counter % 499 == 0) {
2640 struct timeval start
2641 {
2642 };
2643 Utility::gettimeofday(&start);
2644 g_multiTasker->makeThread(houseKeeping, nullptr);
2645 if (!threadInfo.isTaskThread()) {
2646 struct timeval stop
2647 {
2648 };
2649 Utility::gettimeofday(&stop);
2650 t_Counters.at(rec::Counter::maintenanceUsec) += uSec(stop - start);
2651 ++t_Counters.at(rec::Counter::maintenanceCalls);
2652 }
2653 }
2654
2655 if (s_counter % 55 == 0) {
2656 auto expired = t_fdm->getTimeouts(g_now);
2657
2658 for (const auto& exp : expired) {
2659 auto conn = boost::any_cast<shared_ptr<TCPConnection>>(exp.second);
2660 if (g_logCommonErrors) {
2661 SLOG(g_log << Logger::Warning << "Timeout from remote TCP client " << conn->d_remote.toStringWithPort() << endl,
2662 g_slogtcpin->info(Logr::Warning, "Timeout from remote TCP client", "remote", Logging::Loggable(conn->d_remote)));
2663 }
2664 t_fdm->removeReadFD(exp.first);
2665 }
2666 }
2667
2668 s_counter++;
2669
2670 if (threadInfo.isHandler()) {
2671 if (statsWanted || (s_statisticsInterval > 0 && (g_now.tv_sec - last_stat) >= s_statisticsInterval)) {
2672 doStats();
2673 last_stat = g_now.tv_sec;
2674 }
2675
2676 Utility::gettimeofday(&g_now, nullptr);
2677
2678 if ((g_now.tv_sec - last_carbon) >= carbonInterval) {
2679 g_multiTasker->makeThread(doCarbonDump, nullptr);
2680 last_carbon = g_now.tv_sec;
2681 }
2682 }
2683 runLuaMaintenance(threadInfo, last_lua_maintenance, luaMaintenanceInterval);
2684
2685 t_fdm->run(&g_now);
2686 // 'run' updates g_now for us
2687
2688 runTCPMaintenance(threadInfo, listenOnTCP, maxTcpClients);
2689 }
2690 }
2691
2692 static void recursorThread()
2693 {
2694 auto log = g_slog->withName("runtime");
2695 t_Counters.updateSnap(true);
2696 try {
2697 auto& threadInfo = RecThreadInfo::self();
2698 {
2699 SyncRes tmp(g_now); // make sure it allocates tsstorage before we do anything, like primeHints or so..
2700 SyncRes::setDomainMap(g_initialDomainMap);
2701 t_allowFrom = g_initialAllowFrom;
2702 t_allowNotifyFrom = g_initialAllowNotifyFrom;
2703 t_allowNotifyFor = g_initialAllowNotifyFor;
2704 t_udpclientsocks = std::make_unique<UDPClientSocks>();
2705 t_tcpClientCounts = std::make_unique<tcpClientCounts_t>();
2706 if (g_proxyMapping) {
2707 t_proxyMapping = make_unique<ProxyMapping>(*g_proxyMapping);
2708 }
2709 else {
2710 t_proxyMapping = nullptr;
2711 }
2712
2713 if (threadInfo.isHandler()) {
2714 if (!primeHints()) {
2715 threadInfo.setExitCode(EXIT_FAILURE);
2716 RecursorControlChannel::stop = true;
2717 SLOG(g_log << Logger::Critical << "Priming cache failed, stopping" << endl,
2718 log->info(Logr::Critical, "Priming cache failed, stopping"));
2719 }
2720 SLOG(g_log << Logger::Debug << "Done priming cache with root hints" << endl,
2721 log->info(Logr::Debug, "Done priming cache with root hints"));
2722 }
2723 }
2724
2725 #ifdef NOD_ENABLED
2726 if (threadInfo.isWorker()) {
2727 setupNODThread(log);
2728 }
2729 #endif /* NOD_ENABLED */
2730
2731 /* the listener threads handle TCP queries */
2732 if (threadInfo.isWorker() || threadInfo.isListener()) {
2733 try {
2734 if (!::arg()["lua-dns-script"].empty()) {
2735 t_pdl = std::make_shared<RecursorLua4>();
2736 t_pdl->loadFile(::arg()["lua-dns-script"]);
2737 SLOG(g_log << Logger::Warning << "Loaded 'lua' script from '" << ::arg()["lua-dns-script"] << "'" << endl,
2738 log->info(Logr::Warning, "Loading Lua script from file", "name", Logging::Loggable(::arg()["lua-dns-script"])));
2739 }
2740 }
2741 catch (std::exception& e) {
2742 SLOG(g_log << Logger::Error << "Failed to load 'lua' script from '" << ::arg()["lua-dns-script"] << "': " << e.what() << endl,
2743 log->error(Logr::Error, e.what(), "Failed to load Lua script from file", "name", Logging::Loggable(::arg()["lua-dns-script"])));
2744 _exit(99);
2745 }
2746 }
2747
2748 unsigned int ringsize = ::arg().asNum("stats-ringbuffer-entries") / RecThreadInfo::numUDPWorkers();
2749 if (ringsize != 0) {
2750 t_remotes = std::make_unique<addrringbuf_t>();
2751 if (RecThreadInfo::weDistributeQueries()) {
2752 t_remotes->set_capacity(::arg().asNum("stats-ringbuffer-entries") / RecThreadInfo::numDistributors());
2753 }
2754 else {
2755 t_remotes->set_capacity(ringsize);
2756 }
2757 t_servfailremotes = std::make_unique<addrringbuf_t>();
2758 t_servfailremotes->set_capacity(ringsize);
2759 t_bogusremotes = std::make_unique<addrringbuf_t>();
2760 t_bogusremotes->set_capacity(ringsize);
2761 t_largeanswerremotes = std::make_unique<addrringbuf_t>();
2762 t_largeanswerremotes->set_capacity(ringsize);
2763 t_timeouts = std::make_unique<addrringbuf_t>();
2764 t_timeouts->set_capacity(ringsize);
2765
2766 t_queryring = std::make_unique<boost::circular_buffer<pair<DNSName, uint16_t>>>();
2767 t_queryring->set_capacity(ringsize);
2768 t_servfailqueryring = std::make_unique<boost::circular_buffer<pair<DNSName, uint16_t>>>();
2769 t_servfailqueryring->set_capacity(ringsize);
2770 t_bogusqueryring = std::make_unique<boost::circular_buffer<pair<DNSName, uint16_t>>>();
2771 t_bogusqueryring->set_capacity(ringsize);
2772 }
2773 g_multiTasker = std::make_unique<MT_t>(::arg().asNum("stack-size"), ::arg().asNum("stack-cache-size"));
2774 threadInfo.setMT(g_multiTasker.get());
2775
2776 /* start protobuf export threads if needed */
2777 auto luaconfsLocal = g_luaconfs.getLocal();
2778 checkProtobufExport(luaconfsLocal);
2779 checkOutgoingProtobufExport(luaconfsLocal);
2780 #ifdef HAVE_FSTRM
2781 checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
2782 checkFrameStreamExport(luaconfsLocal, luaconfsLocal->nodFrameStreamExportConfig, t_nodFrameStreamServersInfo);
2783 #endif
2784
2785 t_fdm = unique_ptr<FDMultiplexer>(getMultiplexer(log));
2786
2787 std::unique_ptr<RecursorWebServer> rws;
2788
2789 t_fdm->addReadFD(threadInfo.getPipes().readToThread, handlePipeRequest);
2790
2791 if (threadInfo.isHandler()) {
2792 if (::arg().mustDo("webserver")) {
2793 SLOG(g_log << Logger::Warning << "Enabling web server" << endl,
2794 log->info(Logr::Info, "Enabling web server"));
2795 try {
2796 rws = make_unique<RecursorWebServer>(t_fdm.get());
2797 }
2798 catch (const PDNSException& e) {
2799 SLOG(g_log << Logger::Error << "Unable to start the internal web server: " << e.reason << endl,
2800 log->error(Logr::Critical, e.reason, "Exception while starting internal web server"));
2801 _exit(99);
2802 }
2803 }
2804 SLOG(g_log << Logger::Info << "Enabled '" << t_fdm->getName() << "' multiplexer" << endl,
2805 log->info(Logr::Info, "Enabled multiplexer", "name", Logging::Loggable(t_fdm->getName())));
2806 }
2807 else {
2808 t_fdm->addReadFD(threadInfo.getPipes().readQueriesToThread, handlePipeRequest);
2809
2810 if (threadInfo.isListener()) {
2811 if (g_reusePort) {
2812 /* then every listener has its own FDs */
2813 for (const auto& deferred : threadInfo.getDeferredAdds()) {
2814 t_fdm->addReadFD(deferred.first, deferred.second);
2815 }
2816 }
2817 else {
2818 /* otherwise all listeners are listening on the same ones */
2819 for (const auto& deferred : threadInfo.isTCPListener() ? s_deferredTCPadds : s_deferredUDPadds) {
2820 t_fdm->addReadFD(deferred.first, deferred.second);
2821 }
2822 }
2823 }
2824 }
2825
2826 if (threadInfo.isHandler()) {
2827 t_fdm->addReadFD(g_rcc.d_fd, handleRCC); // control channel
2828 }
2829
2830 #ifdef HAVE_SYSTEMD
2831 if (threadInfo.isHandler()) {
2832 // There is a race, as some threads might not be ready yet to do work.
2833 // To solve that, threads should notify RecThreadInfo they are done initializing.
2834 // But we lack a mechanism for that at this point in time.
2835 sd_notify(0, "READY=1");
2836 }
2837 #endif
2838
2839 recLoop();
2840 }
2841 catch (PDNSException& ae) {
2842 SLOG(g_log << Logger::Error << "Exception: " << ae.reason << endl,
2843 log->error(Logr::Error, ae.reason, "Exception in RecursorThread", "exception", Logging::Loggable("PDNSException")));
2844 }
2845 catch (std::exception& e) {
2846 SLOG(g_log << Logger::Error << "STL Exception: " << e.what() << endl,
2847 log->error(Logr::Error, e.what(), "Exception in RecursorThread", "exception", Logging::Loggable("std::exception")));
2848 }
2849 catch (...) {
2850 SLOG(g_log << Logger::Error << "any other exception in main: " << endl,
2851 log->info(Logr::Error, "Exception in RecursorThread"));
2852 }
2853 }
2854
2855 static pair<int, bool> doYamlConfig(Logr::log_t /* startupLog */, int argc, char* argv[]) // NOLINT: Posix API
2856 {
2857 if (!::arg().mustDo("config")) {
2858 return {0, false};
2859 }
2860 const string config = ::arg()["config"];
2861 if (config == "diff" || config.empty()) {
2862 ::arg().parse(argc, argv);
2863 pdns::rust::settings::rec::Recursorsettings settings;
2864 pdns::settings::rec::oldStyleSettingsToBridgeStruct(settings);
2865 auto yaml = settings.to_yaml_string();
2866 cout << yaml << endl;
2867 }
2868 else if (config == "default") {
2869 auto yaml = pdns::settings::rec::defaultsToYaml();
2870 cout << yaml << endl;
2871 }
2872 return {0, true};
2873 }
2874
2875 static pair<int, bool> doConfig(Logr::log_t startupLog, const string& configname, int argc, char* argv[]) // NOLINT: Posix API
2876 {
2877 if (::arg().mustDo("config")) {
2878 string config = ::arg()["config"];
2879 if (config == "check") {
2880 try {
2881 if (!::arg().file(configname)) {
2882 SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl,
2883 startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname)));
2884 return {1, true};
2885 }
2886 ::arg().parse(argc, argv);
2887 return {0, true};
2888 }
2889 catch (const ArgException& argException) {
2890 SLOG(g_log << Logger::Warning << "Unable to parse configuration file '" << configname << "': " << argException.reason << endl,
2891 startupLog->error("Cannot parse configuration", "Unable to parse configuration file", "config_file", Logging::Loggable(configname), "reason", Logging::Loggable(argException.reason)));
2892 return {1, true};
2893 }
2894 }
2895 else if (config == "default" || config.empty()) {
2896 cout << ::arg().configstring(false, true);
2897 }
2898 else if (config == "diff") {
2899 if (!::arg().laxFile(configname)) {
2900 SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl,
2901 startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname)));
2902 return {1, true};
2903 }
2904 ::arg().laxParse(argc, argv);
2905 cout << ::arg().configstring(true, false);
2906 }
2907 else {
2908 if (!::arg().laxFile(configname)) {
2909 SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl,
2910 startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname)));
2911 return {1, true};
2912 }
2913 ::arg().laxParse(argc, argv);
2914 cout << ::arg().configstring(true, true);
2915 }
2916 return {0, true};
2917 }
2918 return {0, false};
2919 }
2920
2921 static void handleRuntimeDefaults(Logr::log_t log)
2922 {
2923 #if HAVE_FIBER_SANITIZER
2924 // Asan needs more stack
2925 if (::arg().asNum("stack-size") == 200000) { // the default in table.py
2926 ::arg().set("stack-size", "stack size per mthread") = "600000";
2927 }
2928 #endif
2929
2930 const string RUNTIME = "*runtime determined*";
2931 if (::arg()["version-string"] == RUNTIME) { // i.e. not set explicitly
2932 ::arg().set("version-string") = fullVersionString();
2933 }
2934
2935 if (::arg()["server-id"] == RUNTIME) { // i.e. not set explicitly
2936 auto myHostname = getHostname();
2937 if (!myHostname.has_value()) {
2938 SLOG(g_log << Logger::Warning << "Unable to get the hostname, NSID and id.server values will be empty" << endl,
2939 log->info(Logr::Warning, "Unable to get the hostname, NSID and id.server values will be empty"));
2940 }
2941 ::arg().set("server-id") = myHostname.has_value() ? *myHostname : "";
2942 }
2943
2944 if (::arg()["socket-dir"].empty()) {
2945 auto* runtimeDir = getenv("RUNTIME_DIRECTORY"); // NOLINT(concurrency-mt-unsafe,cppcoreguidelines-pro-type-vararg)
2946 if (runtimeDir != nullptr) {
2947 ::arg().set("socket-dir") = runtimeDir;
2948 }
2949 }
2950
2951 if (::arg()["socket-dir"].empty()) {
2952 if (::arg()["chroot"].empty()) {
2953 ::arg().set("socket-dir") = std::string(LOCALSTATEDIR) + "/pdns-recursor";
2954 }
2955 else {
2956 ::arg().set("socket-dir") = "/";
2957 }
2958 }
2959
2960 if (::arg().asNum("threads") == 1) {
2961 if (::arg().mustDo("pdns-distributes-queries")) {
2962 SLOG(g_log << Logger::Warning << "Only one thread, no need to distribute queries ourselves" << endl,
2963 log->info(Logr::Warning, "Only one thread, no need to distribute queries ourselves"));
2964 ::arg().set("pdns-distributes-queries") = "no";
2965 }
2966 }
2967
2968 if (::arg().mustDo("pdns-distributes-queries") && ::arg().asNum("distributor-threads") == 0) {
2969 SLOG(g_log << Logger::Warning << "Asked to run with pdns-distributes-queries set but no distributor threads, raising to 1" << endl,
2970 log->info(Logr::Warning, "Asked to run with pdns-distributes-queries set but no distributor threads, raising to 1"));
2971 ::arg().set("distributor-threads") = "1";
2972 }
2973
2974 if (!::arg().mustDo("pdns-distributes-queries") && ::arg().asNum("distributor-threads") > 0) {
2975 SLOG(g_log << Logger::Warning << "Not distributing queries, setting distributor threads to 0" << endl,
2976 log->info(Logr::Warning, "Not distributing queries, setting distributor threads to 0"));
2977 ::arg().set("distributor-threads") = "0";
2978 }
2979 }
2980
2981 int main(int argc, char** argv)
2982 {
2983 g_argc = argc;
2984 g_argv = argv;
2985 versionSetProduct(ProductRecursor);
2986 reportBasicTypes();
2987 reportOtherTypes();
2988
2989 int ret = EXIT_SUCCESS;
2990
2991 try {
2992 pdns::settings::rec::defineOldStyleSettings();
2993 ::arg().setDefaults();
2994 g_log.toConsole(Logger::Info);
2995 ::arg().laxParse(argc, argv); // do a lax parse
2996
2997 if (::arg().mustDo("version")) {
2998 showProductVersion();
2999 showBuildConfiguration();
3000 return 0;
3001 }
3002 if (::arg().mustDo("help")) {
3003 cout << "syntax:" << endl
3004 << endl;
3005 cout << ::arg().helpstring(::arg()["help"]) << endl;
3006 return 0;
3007 }
3008
3009 // Pick up options given on command line to setup logging asap.
3010 g_quiet = ::arg().mustDo("quiet");
3011 s_logUrgency = (Logger::Urgency)::arg().asNum("loglevel");
3012 g_slogStructured = ::arg().mustDo("structured-logging");
3013 s_structured_logger_backend = ::arg()["structured-logging-backend"];
3014
3015 if (!g_quiet && s_logUrgency < Logger::Info) { // Logger::Info=6, Logger::Debug=7
3016 s_logUrgency = Logger::Info; // if you do --quiet=no, you need Info to also see the query log
3017 }
3018 g_log.setLoglevel(s_logUrgency);
3019 g_log.toConsole(s_logUrgency);
3020 showProductVersion();
3021 if (!g_slogStructured) {
3022 g_log << Logger::Warning << "Disabling structured logging is deprecated, old-style logging wil be removed in a future release" << endl;
3023 }
3024
3025 g_yamlSettings = false;
3026 string configname = ::arg()["config-dir"] + "/recursor";
3027 if (!::arg()["config-name"].empty()) {
3028 configname = ::arg()["config-dir"] + "/recursor-" + ::arg()["config-name"];
3029 g_programname += "-" + ::arg()["config-name"];
3030 }
3031 cleanSlashes(configname);
3032
3033 if (!::arg().getCommands().empty()) {
3034 cerr << "Fatal: non-option";
3035 if (::arg().getCommands().size() > 1) {
3036 cerr << "s";
3037 }
3038 cerr << " (";
3039 bool first = true;
3040 for (const auto& command : ::arg().getCommands()) {
3041 if (!first) {
3042 cerr << ", ";
3043 }
3044 first = false;
3045 cerr << command;
3046 }
3047 cerr << ") on the command line, perhaps a '--setting=123' statement missed the '='?" << endl;
3048 return 99;
3049 }
3050
3051 if (s_structured_logger_backend == "systemd-journal") {
3052 #ifdef HAVE_SYSTEMD
3053 if (int fd = sd_journal_stream_fd("pdns-recusor", LOG_DEBUG, 0); fd >= 0) {
3054 g_slog = Logging::Logger::create(loggerSDBackend);
3055 close(fd);
3056 }
3057 #endif
3058 if (g_slog == nullptr) {
3059 cerr << "Structured logging to systemd-journal requested but it is not available" << endl;
3060 }
3061 }
3062
3063 if (g_slog == nullptr) {
3064 g_slog = Logging::Logger::create(loggerBackend);
3065 }
3066
3067 // Missing: a mechanism to call setVerbosity(x)
3068 auto startupLog = g_slog->withName("config");
3069 g_slogtcpin = g_slog->withName("in")->withValues("proto", Logging::Loggable("tcp"));
3070 g_slogudpin = g_slog->withName("in")->withValues("proto", Logging::Loggable("udp"));
3071 g_slogout = g_slog->withName("out");
3072
3073 ::arg().setSLog(startupLog);
3074
3075 const string yamlconfigname = configname + ".yml";
3076 string msg;
3077 pdns::rust::settings::rec::Recursorsettings settings;
3078 // TODO: handle include-dir on command line
3079 auto yamlstatus = pdns::settings::rec::readYamlSettings(yamlconfigname, ::arg()["include-dir"], settings, msg, startupLog);
3080
3081 switch (yamlstatus) {
3082 case pdns::settings::rec::YamlSettingsStatus::CannotOpen:
3083 SLOG(g_log << Logger::Debug << "No YAML config found for configname '" << yamlconfigname << "': " << msg << endl,
3084 startupLog->error(Logr::Debug, msg, "No YAML config found", "configname", Logging::Loggable(yamlconfigname)));
3085 break;
3086 case pdns::settings::rec::YamlSettingsStatus::PresentButFailed:
3087 SLOG(g_log << Logger::Error << "YAML config found for configname '" << yamlconfigname << "' but error ocurred processing it" << endl,
3088 startupLog->error(Logr::Error, msg, "YAML config found, but error occurred processsing it", "configname", Logging::Loggable(yamlconfigname)));
3089 return 1;
3090 break;
3091 case pdns::settings::rec::YamlSettingsStatus::OK:
3092 g_yamlSettings = true;
3093 SLOG(g_log << Logger::Notice << "YAML config found and processed for configname '" << yamlconfigname << "'" << endl,
3094 startupLog->info(Logr::Notice, "YAML config found and processed", "configname", Logging::Loggable(yamlconfigname)));
3095 pdns::settings::rec::processAPIDir(arg()["include-dir"], settings, startupLog);
3096 pdns::settings::rec::bridgeStructToOldStyleSettings(settings);
3097 break;
3098 }
3099
3100 if (g_yamlSettings) {
3101 bool mustExit = false;
3102 std::tie(ret, mustExit) = doYamlConfig(startupLog, argc, argv);
3103 if (ret != 0 || mustExit) {
3104 return ret;
3105 }
3106 }
3107
3108 if (yamlstatus == pdns::settings::rec::YamlSettingsStatus::CannotOpen) {
3109 configname += ".conf";
3110 bool mustExit = false;
3111 std::tie(ret, mustExit) = doConfig(startupLog, configname, argc, argv);
3112 if (ret != 0 || mustExit) {
3113 return ret;
3114 }
3115 if (!::arg().file(configname)) {
3116 SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl,
3117 startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname)));
3118 }
3119 }
3120
3121 // Reparse, now with config file as well, both for old-style as for YAML settings
3122 ::arg().parse(argc, argv);
3123
3124 g_quiet = ::arg().mustDo("quiet");
3125 s_logUrgency = (Logger::Urgency)::arg().asNum("loglevel");
3126 g_slogStructured = ::arg().mustDo("structured-logging");
3127
3128 if (s_logUrgency < Logger::Error) {
3129 s_logUrgency = Logger::Error;
3130 }
3131 if (!g_quiet && s_logUrgency < Logger::Info) { // Logger::Info=6, Logger::Debug=7
3132 s_logUrgency = Logger::Info; // if you do --quiet=no, you need Info to also see the query log
3133 }
3134 g_log.setLoglevel(s_logUrgency);
3135 g_log.toConsole(s_logUrgency);
3136
3137 if (!::arg()["chroot"].empty() && !::arg()["api-config-dir"].empty()) {
3138 SLOG(g_log << Logger::Error << "Using chroot and enabling the API is not possible" << endl,
3139 startupLog->info(Logr::Error, "Cannot use chroot and enable the API at the same time"));
3140 return EXIT_FAILURE;
3141 }
3142
3143 handleRuntimeDefaults(startupLog);
3144
3145 g_recCache = std::make_unique<MemRecursorCache>(::arg().asNum("record-cache-shards"));
3146 g_negCache = std::make_unique<NegCache>(::arg().asNum("record-cache-shards") / 8);
3147 if (!::arg().mustDo("disable-packetcache")) {
3148 g_maxPacketCacheEntries = ::arg().asNum("max-packetcache-entries");
3149 g_packetCache = std::make_unique<RecursorPacketCache>(g_maxPacketCacheEntries, ::arg().asNum("packetcache-shards"));
3150 }
3151
3152 ret = serviceMain(startupLog);
3153 }
3154 catch (const PDNSException& ae) {
3155 SLOG(g_log << Logger::Error << "Exception: " << ae.reason << endl,
3156 g_slog->withName("config")->error(Logr::Critical, ae.reason, "Fatal error", "exception", Logging::Loggable("PDNSException")));
3157 ret = EXIT_FAILURE;
3158 }
3159 catch (const std::exception& e) {
3160 SLOG(g_log << Logger::Error << "STL Exception: " << e.what() << endl,
3161 g_slog->withName("config")->error(Logr::Critical, e.what(), "Fatal error", "exception", Logging::Loggable("std::exception")));
3162 ret = EXIT_FAILURE;
3163 }
3164 catch (...) {
3165 SLOG(g_log << Logger::Error << "any other exception in main: " << endl,
3166 g_slog->withName("config")->info(Logr::Critical, "Fatal error"));
3167 ret = EXIT_FAILURE;
3168 }
3169
3170 return ret;
3171 }
3172
3173 static RecursorControlChannel::Answer* doReloadLuaScript()
3174 {
3175 string fname = ::arg()["lua-dns-script"];
3176 auto log = g_slog->withName("runtime")->withValues("name", Logging::Loggable(fname));
3177 try {
3178 if (fname.empty()) {
3179 t_pdl.reset();
3180 SLOG(g_log << Logger::Info << RecThreadInfo::id() << " Unloaded current lua script" << endl,
3181 log->info(Logr::Info, "Unloaded current lua script"));
3182 return new RecursorControlChannel::Answer{0, string("unloaded\n")};
3183 }
3184
3185 t_pdl = std::make_shared<RecursorLua4>();
3186 try {
3187 t_pdl->loadFile(fname);
3188 }
3189 catch (std::runtime_error& ex) {
3190 string msg = std::to_string(RecThreadInfo::id()) + " Retaining current script, could not read '" + fname + "': " + ex.what();
3191 SLOG(g_log << Logger::Error << msg << endl,
3192 log->error(Logr::Error, ex.what(), "Retaining current script, could not read new script"));
3193 return new RecursorControlChannel::Answer{1, msg + "\n"};
3194 }
3195 }
3196 catch (std::exception& e) {
3197 SLOG(g_log << Logger::Error << RecThreadInfo::id() << " Retaining current script, error from '" << fname << "': " << e.what() << endl,
3198 log->error(Logr::Error, e.what(), "Retaining current script, error in new script"));
3199 return new RecursorControlChannel::Answer{1, string("retaining current script, error from '" + fname + "': " + e.what() + "\n")};
3200 }
3201
3202 SLOG(g_log << Logger::Warning << RecThreadInfo::id() << " (Re)loaded lua script from '" << fname << "'" << endl,
3203 log->info(Logr::Warning, "(Re)loaded lua script"));
3204 return new RecursorControlChannel::Answer{0, string("(re)loaded '" + fname + "'\n")};
3205 }
3206
3207 RecursorControlChannel::Answer doQueueReloadLuaScript(vector<string>::const_iterator begin, vector<string>::const_iterator end)
3208 {
3209 if (begin != end) {
3210 ::arg().set("lua-dns-script") = *begin;
3211 }
3212
3213 return broadcastAccFunction<RecursorControlChannel::Answer>(doReloadLuaScript);
3214 }
3215
3216 static string* pleaseUseNewTraceRegex(const std::string& newRegex, int file)
3217 {
3218 try {
3219 if (newRegex.empty()) {
3220 t_traceRegex.reset();
3221 t_tracefd = FDWrapper();
3222 return new string("unset\n");
3223 }
3224 if (file == -1) {
3225 return new string("could not dup file\n");
3226 }
3227 t_traceRegex = std::make_shared<Regex>(newRegex);
3228 t_tracefd = file;
3229 return new string("ok\n"); // NOLINT(cppcoreguidelines-owning-memory): it's the API
3230 }
3231 catch (const PDNSException& ae) {
3232 return new string(ae.reason + "\n"); // NOLINT(cppcoreguidelines-owning-memory): it's the API
3233 }
3234 }
3235
3236 string doTraceRegex(FDWrapper file, vector<string>::const_iterator begin, vector<string>::const_iterator end)
3237 {
3238 int fileno = dup(file);
3239 // Potential dup failure handled in pleaseUseNewTraceRegex()
3240 return broadcastAccFunction<string>([=] { return pleaseUseNewTraceRegex(begin != end ? *begin : "", fileno); });
3241 }
3242
3243 struct WipeCacheResult wipeCaches(const DNSName& canon, bool subtree, uint16_t qtype)
3244 {
3245 struct WipeCacheResult res;
3246
3247 try {
3248 res.record_count = static_cast<int>(g_recCache->doWipeCache(canon, subtree, qtype));
3249 // scanbuild complains here about an allocated function object that is being leaked. Needs investigation
3250 if (g_packetCache) {
3251 res.packet_count = static_cast<int>(g_packetCache->doWipePacketCache(canon, qtype, subtree));
3252 }
3253 res.negative_record_count = static_cast<int>(g_negCache->wipe(canon, subtree));
3254 if (g_aggressiveNSECCache) {
3255 g_aggressiveNSECCache->removeZoneInfo(canon, subtree);
3256 }
3257 }
3258 catch (const std::exception& e) {
3259 auto log = g_slog->withName("runtime");
3260 SLOG(g_log << Logger::Warning << ", failed: " << e.what() << endl,
3261 log->error(Logr::Warning, e.what(), "Wipecache failed"));
3262 }
3263
3264 return res;
3265 }