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