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