]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/recursordist/rec-main.cc
Also do the compare for protobuf logger config objects.
[thirdparty/pdns.git] / pdns / recursordist / rec-main.cc
1 /*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22 #include <sys/stat.h>
23
24 #include "rec-main.hh"
25
26 #include "aggressive_nsec.hh"
27 #include "capabilities.hh"
28 #include "arguments.hh"
29 #include "dns_random.hh"
30 #include "rec_channel.hh"
31 #include "rec-tcpout.hh"
32 #include "version.hh"
33 #include "query-local-address.hh"
34 #include "validate-recursor.hh"
35 #include "pubsuffix.hh"
36 #include "opensslsigners.hh"
37 #include "ws-recursor.hh"
38 #include "rec-taskqueue.hh"
39 #include "secpoll-recursor.hh"
40 #include "logging.hh"
41
42 #ifdef NOD_ENABLED
43 #include "nod.hh"
44 #endif /* NOD_ENABLED */
45
46 #ifdef HAVE_LIBSODIUM
47 #include <sodium.h>
48 #endif
49
50 #ifdef HAVE_SYSTEMD
51 // All calls are coming form the same function, so no use for CODE_LINE, CODE_FUNC etc
52 #define SD_JOURNAL_SUPPRESS_LOCATION
53 #include <systemd/sd-daemon.h>
54 #include <systemd/sd-journal.h>
55 #endif
56
57 #ifdef HAVE_FSTRM
58 thread_local FrameStreamServersInfo t_frameStreamServersInfo;
59 thread_local FrameStreamServersInfo t_nodFrameStreamServersInfo;
60 #endif /* HAVE_FSTRM */
61
62 string g_programname = "pdns_recursor";
63 string g_pidfname;
64 RecursorControlChannel g_rcc; // only active in the handler thread
65
66 #ifdef NOD_ENABLED
67 bool g_nodEnabled;
68 DNSName g_nodLookupDomain;
69 bool g_nodLog;
70 SuffixMatchNode g_nodDomainWL;
71 std::string g_nod_pbtag;
72 bool g_udrEnabled;
73 bool g_udrLog;
74 std::string g_udr_pbtag;
75 thread_local std::shared_ptr<nod::NODDB> t_nodDBp;
76 thread_local std::shared_ptr<nod::UniqueResponseDB> t_udrDBp;
77 #endif /* NOD_ENABLED */
78
79 std::atomic<bool> statsWanted;
80 uint32_t g_disthashseed;
81 bool g_useIncomingECS;
82 NetmaskGroup g_proxyProtocolACL;
83 boost::optional<ComboAddress> g_dns64Prefix{boost::none};
84 DNSName g_dns64PrefixReverse;
85 std::shared_ptr<SyncRes::domainmap_t> g_initialDomainMap; // new threads needs this to be setup
86 std::shared_ptr<NetmaskGroup> g_initialAllowFrom; // new thread needs to be setup with this
87 std::shared_ptr<NetmaskGroup> g_initialAllowNotifyFrom; // new threads need this to be setup
88 std::shared_ptr<notifyset_t> g_initialAllowNotifyFor; // new threads need this to be setup
89 bool g_logRPZChanges{false};
90 static time_t s_statisticsInterval;
91 bool g_addExtendedResolutionDNSErrors;
92 static std::atomic<uint32_t> s_counter;
93 int g_argc;
94 char** g_argv;
95 static string s_structured_logger_backend;
96 static Logger::Urgency s_logUrgency;
97
98 std::shared_ptr<Logr::Logger> g_slogtcpin;
99 std::shared_ptr<Logr::Logger> g_slogudpin;
100 std::shared_ptr<Logr::Logger> g_slogudpout;
101
102 /* without reuseport, all listeners share the same sockets */
103 deferredAdd_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 */
109 std::vector<RecThreadInfo> RecThreadInfo::s_threadInfos;
110
111 std::unique_ptr<ProxyMapping> g_proxyMapping; // new threads needs this to be setup
112 thread_local std::unique_ptr<ProxyMapping> t_proxyMapping;
113
114 bool RecThreadInfo::s_weDistributeQueries; // if true, 1 or more threads listen on the incoming query sockets and distribute them to workers
115 unsigned int RecThreadInfo::s_numDistributorThreads;
116 unsigned int RecThreadInfo::s_numWorkerThreads;
117 thread_local unsigned int RecThreadInfo::t_id;
118
119 static std::map<unsigned int, std::set<int>> parseCPUMap(Logr::log_t log)
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()) {
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"));
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
144 auto threadId = pdns::checked_stoi<unsigned int>(headers.first);
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) {
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)));
158 }
159 }
160
161 return result;
162 }
163
164 static void setCPUMap(const std::map<unsigned int, std::set<int>>& cpusMap, unsigned int n, pthread_t tid, Logr::log_t log)
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) {
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 {
180 log->info(Logr::Info, "CPU affinity has been set", "thread", Logging::Loggable(n), "cpumap", Logging::IterLoggable(cpuMapping->second.begin(), cpuMapping->second.end()));
181 }
182 }
183 else {
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 {
192 log->error(Logr::Warning, rc, "Error setting CPU affinity", "thread", Logging::Loggable(n), "cpumap", Logging::IterLoggable(cpuMapping->second.begin(), cpuMapping->second.end()));
193 }
194 }
195 }
196
197 static void recursorThread();
198
199 void RecThreadInfo::start(unsigned int id, const string& tname, const std::map<unsigned int, std::set<int>>& cpusMap, Logr::log_t log)
200 {
201 name = tname;
202 thread = std::thread([id, tname] {
203 t_id = id;
204 const string threadPrefix = "rec/";
205 setThreadName(threadPrefix + tname);
206 recursorThread();
207 });
208 setCPUMap(cpusMap, id, thread.native_handle(), log);
209 }
210
211 int RecThreadInfo::runThreads(Logr::log_t log)
212 {
213 int ret = EXIT_SUCCESS;
214 unsigned int currentThreadId = 1;
215 const auto cpusMap = parseCPUMap(log);
216
217 if (RecThreadInfo::numDistributors() + RecThreadInfo::numWorkers() == 1) {
218 SLOG(g_log << Logger::Warning << "Operating with single distributor/worker thread" << endl,
219 log->info(Logr::Notice, "Operating with single distributor/worker thread"));
220
221 /* This thread handles the web server, carbon, statistics and the control channel */
222 auto& handlerInfo = RecThreadInfo::info(0);
223 handlerInfo.setHandler();
224 handlerInfo.start(0, "web+stat", cpusMap, log);
225 auto& taskInfo = RecThreadInfo::info(2);
226 taskInfo.setTaskThread();
227 taskInfo.start(2, "taskThread", cpusMap, log);
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()) {
264 SLOG(g_log << Logger::Warning << "Launching " << RecThreadInfo::numDistributors() << " distributor threads" << endl,
265 log->info(Logr::Notice, "Launching distributor threads", "count", Logging::Loggable(RecThreadInfo::numDistributors())));
266 for (unsigned int n = 0; n < RecThreadInfo::numDistributors(); ++n) {
267 auto& info = RecThreadInfo::info(currentThreadId);
268 info.start(currentThreadId++, "distr", cpusMap, log);
269 }
270 }
271 SLOG(g_log << Logger::Warning << "Launching " << RecThreadInfo::numWorkers() << " worker threads" << endl,
272 log->info(Logr::Notice, "Launching worker threads", "count", Logging::Loggable(RecThreadInfo::numWorkers())));
273
274 for (unsigned int n = 0; n < RecThreadInfo::numWorkers(); ++n) {
275 auto& info = RecThreadInfo::info(currentThreadId);
276 info.start(currentThreadId++, "worker", cpusMap, log);
277 }
278
279 for (unsigned int n = 0; n < RecThreadInfo::numTaskThreads(); ++n) {
280 auto& info = RecThreadInfo::info(currentThreadId);
281 info.start(currentThreadId++, "taskThread", cpusMap, log);
282 }
283
284 /* This thread handles the web server, carbon, statistics and the control channel */
285 auto& info = RecThreadInfo::info(0);
286 info.setHandler();
287 info.start(0, "web+stat", cpusMap, log);
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
299 void RecThreadInfo::makeThreadPipes(Logr::log_t log)
300 {
301 auto pipeBufferSize = ::arg().asNum("distribution-pipe-buffer-size");
302 if (pipeBufferSize > 0) {
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)));
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;
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)));
340 auto existingSize = getPipeBufferSize(threadInfo.pipes.writeQueriesToThread);
341 if (existingSize > 0) {
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)));
344 }
345 }
346 }
347
348 if (!setNonBlocking(threadInfo.pipes.writeQueriesToThread)) {
349 unixDie("Making pipe for inter-thread communications non-blocking");
350 }
351 }
352 }
353
354 ArgvMap& arg()
355 {
356 static ArgvMap theArg;
357 return theArg;
358 }
359
360 static FDMultiplexer* getMultiplexer(Logr::log_t log)
361 {
362 FDMultiplexer* ret;
363 for (const auto& i : FDMultiplexer::getMultiplexerMap()) {
364 try {
365 ret = i.second(FDMultiplexer::s_maxevents);
366 return ret;
367 }
368 catch (FDMultiplexerException& fe) {
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"));
371 }
372 catch (...) {
373 SLOG(g_log << Logger::Error << "Non-fatal error initializing possible multiplexer" << endl,
374 log->info(Logr::Error, "Non-fatal error initializing possible multiplexer"));
375 }
376 }
377 SLOG(g_log << Logger::Error << "No working multiplexer found!" << endl,
378 log->info(Logr::Error, "No working multiplexer found!"));
379 _exit(1);
380 }
381
382 static std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>> startProtobufServers(const ProtobufExportConfig& config, Logr::log_t log)
383 {
384 auto result = std::make_shared<std::vector<std::unique_ptr<RemoteLogger>>>();
385
386 for (const auto& server : config.servers) {
387 try {
388 auto logger = make_unique<RemoteLogger>(server, config.timeout, 100 * config.maxQueuedEntries, config.reconnectWaitTime, config.asyncConnect);
389 logger->setLogQueries(config.logQueries);
390 logger->setLogResponses(config.logResponses);
391 result->emplace_back(std::move(logger));
392 }
393 catch (const std::exception& e) {
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)));
396 }
397 catch (const PDNSException& e) {
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)));
400 }
401 }
402
403 return result;
404 }
405
406 bool checkProtobufExport(LocalStateHolder<LuaConfigItems>& luaconfsLocal)
407 {
408 if (!luaconfsLocal->protobufExportConfig.enabled) {
409 if (t_protobufServers.servers) {
410 t_protobufServers.servers.reset();
411 t_protobufServers.config = luaconfsLocal->protobufExportConfig;
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 */
419 if (t_protobufServers.generation < luaconfsLocal->generation && t_protobufServers.config != luaconfsLocal->protobufExportConfig) {
420
421 if (t_protobufServers.servers) {
422 t_protobufServers.servers.reset();
423 }
424 auto log = g_slog->withName("protobuf");
425 t_protobufServers.servers = startProtobufServers(luaconfsLocal->protobufExportConfig, log);
426 t_protobufServers.config = luaconfsLocal->protobufExportConfig;
427 t_protobufServers.generation = luaconfsLocal->generation;
428 }
429
430 return true;
431 }
432
433 bool checkOutgoingProtobufExport(LocalStateHolder<LuaConfigItems>& luaconfsLocal)
434 {
435 if (!luaconfsLocal->outgoingProtobufExportConfig.enabled) {
436 if (t_outgoingProtobufServers.servers) {
437 t_outgoingProtobufServers.servers.reset();
438 t_outgoingProtobufServers.config = luaconfsLocal->outgoingProtobufExportConfig;
439 }
440
441 return false;
442 }
443
444 /* if the server was not running, or if it was running according to a
445 previous configuration */
446 if (t_outgoingProtobufServers.generation < luaconfsLocal->generation && t_outgoingProtobufServers.config != luaconfsLocal->outgoingProtobufExportConfig) {
447
448 if (t_outgoingProtobufServers.servers) {
449 t_outgoingProtobufServers.servers.reset();
450 }
451 auto log = g_slog->withName("protobuf");
452 t_outgoingProtobufServers.servers = startProtobufServers(luaconfsLocal->outgoingProtobufExportConfig, log);
453 t_outgoingProtobufServers.config = luaconfsLocal->outgoingProtobufExportConfig;
454 t_outgoingProtobufServers.generation = luaconfsLocal->generation;
455 }
456
457 return true;
458 }
459
460 void 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)
461 {
462 auto log = g_slog->withName("pblq");
463
464 if (!t_protobufServers.servers) {
465 return;
466 }
467
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 }
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());
497 for (auto& server : *t_protobufServers.servers) {
498 remoteLoggerQueueData(*server, msg);
499 }
500 }
501
502 void protobufLogResponse(pdns::ProtoZero::RecMessage& message)
503 {
504 if (!t_protobufServers.servers) {
505 return;
506 }
507
508 std::string msg(message.finishAndMoveBuf());
509 for (auto& server : *t_protobufServers.servers) {
510 remoteLoggerQueueData(*server, msg);
511 }
512 }
513
514 void protobufLogResponse(const struct dnsheader* dh, LocalStateHolder<LuaConfigItems>& luaconfsLocal,
515 const RecursorPacketCache::OptPBData& pbData, const struct timeval& tv,
516 bool tcp, const ComboAddress& source, const ComboAddress& destination,
517 const ComboAddress& mappedSource,
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)
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
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 }
552 pbMessage.setMessageIdentity(uniqueId);
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);
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
579 static std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>> startFrameStreamServers(const FrameStreamExportConfig& config, Logr::log_t log)
580 {
581 auto result = std::make_shared<std::vector<std::unique_ptr<FrameStreamLogger>>>();
582
583 for (const auto& server : config.servers) {
584 try {
585 std::unordered_map<string, unsigned> options;
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;
592 FrameStreamLogger* fsl = nullptr;
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);
602 fsl->setLogNODs(config.logNODs);
603 fsl->setLogUDRs(config.logUDRs);
604 result->emplace_back(fsl);
605 }
606 catch (const std::exception& e) {
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)));
609 }
610 catch (const PDNSException& e) {
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)));
613 }
614 }
615
616 return result;
617 }
618
619 static 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
627 bool checkFrameStreamExport(LocalStateHolder<LuaConfigItems>& luaconfsLocal, const FrameStreamExportConfig& config, FrameStreamServersInfo& serverInfos)
628 {
629 if (!config.enabled) {
630 if (serverInfos.servers) {
631 // dt's take care of cleanup
632 asyncFrameStreamLoggersCleanup(std::move(serverInfos.servers));
633 serverInfos.config = config;
634 }
635
636 return false;
637 }
638
639 /* if the server was not running, or if it was running according to a previous
640 * configuration
641 */
642 if (serverInfos.generation < luaconfsLocal->generation && serverInfos.config != config) {
643 if (serverInfos.servers) {
644 // dt's take care of cleanup
645 asyncFrameStreamLoggersCleanup(std::move(serverInfos.servers));
646 }
647
648 auto dnsTapLog = g_slog->withName("dnstap");
649 serverInfos.servers = startFrameStreamServers(config, dnsTapLog);
650 serverInfos.config = config;
651 serverInfos.generation = luaconfsLocal->generation;
652 }
653
654 return true;
655 }
656
657 #endif /* HAVE_FSTRM */
658
659 static void makeControlChannelSocket(int processNum = -1)
660 {
661 string sockname = ::arg()["socket-dir"] + "/" + g_programname;
662 if (processNum >= 0)
663 sockname += "." + std::to_string(processNum);
664 sockname += ".controlsocket";
665 g_rcc.listen(sockname);
666
667 int sockowner = -1;
668 int sockgroup = -1;
669
670 if (!::arg().isEmpty("socket-group"))
671 sockgroup = ::arg().asGid("socket-group");
672 if (!::arg().isEmpty("socket-owner"))
673 sockowner = ::arg().asUid("socket-owner");
674
675 if (sockgroup > -1 || sockowner > -1) {
676 if (chown(sockname.c_str(), sockowner, sockgroup) < 0) {
677 unixDie("Failed to chown control socket");
678 }
679 }
680
681 // do mode change if socket-mode is given
682 if (!::arg().isEmpty("socket-mode")) {
683 mode_t sockmode = ::arg().asMode("socket-mode");
684 if (chmod(sockname.c_str(), sockmode) < 0) {
685 unixDie("Failed to chmod control socket");
686 }
687 }
688 }
689
690 static void writePid(Logr::log_t log)
691 {
692 if (!::arg().mustDo("write-pid"))
693 return;
694 ofstream of(g_pidfname.c_str(), std::ios_base::app);
695 if (of)
696 of << Utility::getpid() << endl;
697 else {
698 int err = errno;
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)));
701 }
702 }
703
704 static void checkSocketDir(Logr::log_t log)
705 {
706 string dir(::arg()["socket-dir"]);
707 string msg;
708
709 struct stat dirStat = {};
710 if (stat(dir.c_str(), &dirStat) == -1) {
711 msg = "it does not exist or cannot access";
712 }
713 else if (!S_ISDIR(dirStat.st_mode)) {
714 msg = "it is not a directory";
715 }
716 else if (access(dir.c_str(), R_OK | W_OK | X_OK) != 0) {
717 msg = "cannot read, write or search";
718 }
719 else {
720 return;
721 }
722 dir = ::arg()["chroot"] + dir;
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,
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)));
725 _exit(1);
726 }
727
728 #ifdef NOD_ENABLED
729 static void setupNODThread(Logr::log_t log)
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) {
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"])));
740 _exit(1);
741 }
742 if (!t_nodDBp->init()) {
743 SLOG(g_log << Logger::Error << "Could not initialize domain tracking" << endl,
744 log->info(Logr::Error, "Could not initialize domain tracking"));
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) {
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"])));
760 _exit(1);
761 }
762 if (!t_udrDBp->init()) {
763 SLOG(g_log << Logger::Error << "Could not initialize unique response tracking" << endl,
764 log->info(Logr::Error, "Could not initialize unique response tracking"));
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
773 static void parseNODIgnorelist(const std::string& wlist)
774 {
775 vector<string> parts;
776 stringtok(parts, wlist, ",; ");
777 for (const auto& a : parts) {
778 g_nodDomainWL.add(DNSName(a));
779 }
780 }
781
782 static 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
797 static void daemonize(Logr::log_t log)
798 {
799 if (fork())
800 exit(0); // bye bye
801
802 setsid();
803
804 int i = open("/dev/null", O_RDWR); /* open stdin */
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 }
810 else {
811 dup2(i, 0); /* stdin */
812 dup2(i, 1); /* stderr */
813 dup2(i, 2); /* stderr */
814 close(i);
815 }
816 }
817
818 static void termIntHandler(int)
819 {
820 doExit();
821 }
822
823 static void usr1Handler(int)
824 {
825 statsWanted = true;
826 }
827
828 static void usr2Handler(int)
829 {
830 g_quiet = !g_quiet;
831 SyncRes::setDefaultLogMode(g_quiet ? SyncRes::LogNone : SyncRes::Log);
832 ::arg().set("quiet") = g_quiet ? "" : "no";
833 }
834
835 static void checkLinuxIPv6Limits(Logr::log_t log)
836 {
837 #ifdef __linux__
838 string line;
839 if (readFileIfThere("/proc/sys/net/ipv6/route/max_size", &line)) {
840 int lim = std::stoi(line);
841 if (lim < 16384) {
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)));
844 }
845 }
846 #endif
847 }
848
849 static void checkOrFixFDS(Logr::log_t log)
850 {
851 unsigned int availFDs = getFilenumLimit();
852 unsigned int wantFDs = g_maxMThreads * RecThreadInfo::numWorkers() + 25; // even healthier margin then before
853 wantFDs += RecThreadInfo::numWorkers() * TCPOutConnectionManager::s_maxIdlePerThread;
854
855 if (wantFDs > availFDs) {
856 unsigned int hardlimit = getFilenumLimit(true);
857 if (hardlimit >= wantFDs) {
858 setFilenumLimit(wantFDs);
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)));
861 }
862 else {
863 int newval = (hardlimit - 25 - TCPOutConnectionManager::s_maxIdlePerThread) / RecThreadInfo::numWorkers();
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)));
866 g_maxMThreads = newval;
867 setFilenumLimit(hardlimit);
868 }
869 }
870 }
871
872 // static std::string s_timestampFormat = "%m-%dT%H:%M:%S";
873 static std::string s_timestampFormat = "%s";
874
875 static const char* toTimestampStringMilli(const struct timeval& tv, char* buf, size_t sz)
876 {
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 }
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
893 #ifdef HAVE_SYSTEMD
894 static void loggerSDBackend(const Logging::Entry& entry)
895 {
896 // First map SL priority to syslog's Urgency
897 Logger::Urgency u = entry.d_priority ? Logger::Urgency(entry.d_priority) : Logger::Info;
898 if (u > s_logUrgency) {
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.
901 return;
902 }
903 // We need to keep the string in mem until sd_journal_sendv has ben called
904 vector<string> strings;
905 auto appendKeyAndVal = [&strings](const string& k, const string& v) {
906 strings.emplace_back(k + "=" + v);
907 };
908 appendKeyAndVal("MESSAGE", entry.message);
909 if (entry.error) {
910 appendKeyAndVal("ERROR", entry.error.get());
911 }
912 appendKeyAndVal("LEVEL", std::to_string(entry.level));
913 appendKeyAndVal("PRIORITY", std::to_string(entry.d_priority));
914 if (entry.name) {
915 appendKeyAndVal("SUBSYSTEM", entry.name.get());
916 }
917 char timebuf[64];
918 appendKeyAndVal("TIMESTAMP", toTimestampStringMilli(entry.d_timestamp, timebuf, sizeof(timebuf)));
919 for (auto const& v : entry.values) {
920 appendKeyAndVal(toUpper(v.first), v.second);
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.
924 appendKeyAndVal("TID", std::to_string(RecThreadInfo::id()));
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
936 static void loggerBackend(const Logging::Entry& entry)
937 {
938 static thread_local std::stringstream buf;
939
940 // First map SL priority to syslog's Urgency
941 Logger::Urgency u = entry.d_priority ? Logger::Urgency(entry.d_priority) : Logger::Info;
942 if (u > s_logUrgency) {
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.
945 return;
946 }
947 buf.str("");
948 buf << "msg=" << std::quoted(entry.message);
949 if (entry.error) {
950 buf << " error=" << std::quoted(entry.error.get());
951 }
952
953 if (entry.name) {
954 buf << " subsystem=" << std::quoted(entry.name.get());
955 }
956 buf << " level=" << std::quoted(std::to_string(entry.level));
957 if (entry.d_priority) {
958 buf << " prio=" << std::quoted(Logr::Logger::toString(entry.d_priority));
959 }
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()));
963 char timebuf[64];
964 buf << " ts=" << std::quoted(toTimestampStringMilli(entry.d_timestamp, timebuf, sizeof(timebuf)));
965 for (auto const& v : entry.values) {
966 buf << " ";
967 buf << v.first << "=" << std::quoted(v.second);
968 }
969
970 g_log << u << buf.str() << endl;
971 }
972
973 static 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
981 static 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();
995 uint64_t pcSize = broadcastAccFunction<uint64_t>(pleaseGetPacketCacheSize);
996 uint64_t pcHits = broadcastAccFunction<uint64_t>(pleaseGetPacketCacheHits);
997
998 auto log = g_slog->withName("stats");
999
1000 if (g_stats.qcounter && (cacheHits + cacheMisses) && SyncRes::s_queries && SyncRes::s_outqueries) {
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: "
1009 << SyncRes::getEDNSStatusesSize() << ", non-resolving: "
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;
1016
1017 g_log << Logger::Notice << "stats: " << pcSize << " packet cache entries, " << ratePercentage(pcHits, g_stats.qcounter) << "% packet cache hits" << endl;
1018
1019 g_log << Logger::Notice << "stats: tasks pushed/expired/queuesize: " << taskPushes << '/' << taskExpired << '/' << taskSize << endl;
1020 }
1021 else {
1022 const string m = "Periodic statistics report";
1023 log->info(Logr::Info, m,
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));
1031 log->info(Logr::Info, m,
1032 "throttle-entries", Logging::Loggable(SyncRes::getThrottledServersSize()),
1033 "nsspeed-entries", Logging::Loggable(SyncRes::getNSSpeedsSize()),
1034 "failed-host-entries", Logging::Loggable(SyncRes::getFailedServersSize()),
1035 "edns-entries", Logging::Loggable(SyncRes::getEDNSStatusesSize()),
1036 "non-resolving-nameserver-entries", Logging::Loggable(SyncRes::getNonResolvingNSSize()),
1037 "saved-parent-ns-sets-entries", Logging::Loggable(SyncRes::getSaveParentsNSSetsSize()),
1038 "outqueries-per-query", Logging::Loggable(ratePercentage(SyncRes::s_outqueries, SyncRes::s_queries)));
1039 log->info(Logr::Info, m,
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)),
1045 "outgoing-timeouts", Logging::Loggable(SyncRes::s_outgoingtimeouts));
1046 log->info(Logr::Info, m,
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 }
1053 size_t idx = 0;
1054 for (const auto& threadInfo : RecThreadInfo::infos()) {
1055 if (threadInfo.isWorker()) {
1056 SLOG(g_log << Logger::Notice << "stats: thread " << idx << " has been distributed " << threadInfo.numberOfDistributedQueries << " queries" << endl,
1057 log->info(Logr::Info, "Queries handled by thread", "thread", Logging::Loggable(idx), "count", Logging::Loggable(threadInfo.numberOfDistributedQueries)));
1058 ++idx;
1059 }
1060 }
1061 time_t now = time(0);
1062 if (lastOutputTime && lastQueryCount && now != lastOutputTime) {
1063 SLOG(g_log << Logger::Notice << "stats: " << (g_stats.qcounter - lastQueryCount) / (now - lastOutputTime) << " qps (average over " << (now - lastOutputTime) << " seconds)" << endl,
1064 log->info(Logr::Info, "Periodic QPS report", "qps", Logging::Loggable((g_stats.qcounter - lastQueryCount) / (now - lastOutputTime)),
1065 "averagedOver", Logging::Loggable(now - lastOutputTime)));
1066 }
1067 lastOutputTime = now;
1068 lastQueryCount = g_stats.qcounter;
1069 }
1070 else if (statsWanted) {
1071 SLOG(g_log << Logger::Notice << "stats: no stats yet!" << endl,
1072 log->info(Logr::Notice, "No stats yet"));
1073 }
1074
1075 statsWanted = false;
1076 }
1077
1078 static std::shared_ptr<NetmaskGroup> parseACL(const std::string& aclFile, const std::string& aclSetting, Logr::log_t log)
1079 {
1080 auto result = std::make_shared<NetmaskGroup>();
1081
1082 if (!::arg()[aclFile].empty()) {
1083 string line;
1084 ifstream ifs(::arg()[aclFile].c_str());
1085 if (!ifs) {
1086 throw runtime_error("Could not open '" + ::arg()[aclFile] + "': " + stringerror());
1087 }
1088
1089 string::size_type pos;
1090 while (getline(ifs, line)) {
1091 pos = line.find('#');
1092 if (pos != string::npos)
1093 line.resize(pos);
1094 boost::trim(line);
1095 if (line.empty())
1096 continue;
1097
1098 result->addMask(line);
1099 }
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),
1102 "number", Logging::Loggable(result->size()), "file", Logging::Loggable(::arg()[aclFile])));
1103 }
1104 else if (!::arg()[aclSetting].empty()) {
1105 vector<string> ips;
1106 stringtok(ips, ::arg()[aclSetting], ", ");
1107
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 {
1121 log->info(Logr::Info, "Setting access control", "acl", Logging::Loggable(aclSetting), "addresses", Logging::IterLoggable(ips.begin(), ips.end()));
1122 }
1123 }
1124
1125 return result;
1126 }
1127
1128 static void* pleaseSupplantAllowFrom(std::shared_ptr<NetmaskGroup> ng)
1129 {
1130 t_allowFrom = ng;
1131 return nullptr;
1132 }
1133
1134 static void* pleaseSupplantAllowNotifyFrom(std::shared_ptr<NetmaskGroup> ng)
1135 {
1136 t_allowNotifyFrom = ng;
1137 return nullptr;
1138 }
1139
1140 void* pleaseSupplantAllowNotifyFor(std::shared_ptr<notifyset_t> ns)
1141 {
1142 t_allowNotifyFor = ns;
1143 return nullptr;
1144 }
1145
1146 void parseACLs()
1147 {
1148 auto log = g_slog->withName("config");
1149
1150 static bool l_initialized;
1151
1152 if (l_initialized) { // only reload configuration file on second call
1153 string configName = ::arg()["config-dir"] + "/recursor.conf";
1154 if (!::arg()["config-name"].empty()) {
1155 configName = ::arg()["config-dir"] + "/recursor-" + ::arg()["config-name"] + ".conf";
1156 }
1157 cleanSlashes(configName);
1158
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);
1163
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");
1168
1169 ::arg().preParseFile(configName.c_str(), "include-dir");
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
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 }
1183
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 }
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
1199 auto allowFrom = parseACL("allow-from-file", "allow-from", log);
1200
1201 if (allowFrom->empty()) {
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 }
1206 allowFrom = nullptr;
1207 }
1208
1209 g_initialAllowFrom = allowFrom;
1210 broadcastFunction([=] { return pleaseSupplantAllowFrom(allowFrom); });
1211
1212 auto allowNotifyFrom = parseACL("allow-notify-from-file", "allow-notify-from", log);
1213
1214 g_initialAllowNotifyFrom = allowNotifyFrom;
1215 broadcastFunction([=] { return pleaseSupplantAllowNotifyFrom(allowNotifyFrom); });
1216
1217 l_initialized = true;
1218 }
1219
1220 void 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
1226 if (RecThreadInfo::infos().empty() && RecThreadInfo::id() == 0) {
1227 /* the handler and distributors will call themselves below, but
1228 during startup we get called while g_threadInfos has not been
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;
1236 for (const auto& threadInfo : RecThreadInfo::infos()) {
1237 if (n++ == RecThreadInfo::id()) {
1238 func(); // don't write to ourselves!
1239 continue;
1240 }
1241
1242 ThreadMSG* tmsg = new ThreadMSG();
1243 tmsg->func = func;
1244 tmsg->wantAnswer = true;
1245 if (write(threadInfo.pipes.writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) {
1246 delete tmsg;
1247
1248 unixDie("write to thread pipe returned wrong size or error");
1249 }
1250
1251 string* resp = nullptr;
1252 if (read(threadInfo.pipes.readFromThread, &resp, sizeof(resp)) != sizeof(resp))
1253 unixDie("read from thread pipe returned wrong size or error");
1254
1255 if (resp) {
1256 delete resp;
1257 resp = nullptr;
1258 }
1259 // coverity[leaked_storage]
1260 }
1261 }
1262
1263 template <class T>
1264 void* voider(const std::function<T*()>& func)
1265 {
1266 return func();
1267 }
1268
1269 static vector<ComboAddress>& operator+=(vector<ComboAddress>& a, const vector<ComboAddress>& b)
1270 {
1271 a.insert(a.end(), b.begin(), b.end());
1272 return a;
1273 }
1274
1275 static vector<pair<DNSName, uint16_t>>& operator+=(vector<pair<DNSName, uint16_t>>& a, const vector<pair<DNSName, uint16_t>>& b)
1276 {
1277 a.insert(a.end(), b.begin(), b.end());
1278 return a;
1279 }
1280
1281 static 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
1290 static RemoteLoggerStats_t& operator+=(RemoteLoggerStats_t& a, const RemoteLoggerStats_t& b)
1291 {
1292 for (const auto& [key, entry] : b) {
1293 a[key] += entry;
1294 }
1295 return a;
1296 }
1297
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).
1303 template <class T>
1304 T broadcastAccFunction(const std::function<T*()>& func)
1305 {
1306 if (!RecThreadInfo::self().isHandler()) {
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
1309 _exit(1);
1310 }
1311
1312 unsigned int n = 0;
1313 T ret = T();
1314 for (const auto& threadInfo : RecThreadInfo::infos()) {
1315 if (n++ == RecThreadInfo::id()) {
1316 continue;
1317 }
1318
1319 const auto& tps = threadInfo.pipes;
1320 ThreadMSG* tmsg = new ThreadMSG();
1321 tmsg->func = [func] { return voider<T>(func); };
1322 tmsg->wantAnswer = true;
1323
1324 if (write(tps.writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) {
1325 delete tmsg;
1326 unixDie("write to thread pipe returned wrong size or error");
1327 }
1328
1329 T* resp = nullptr;
1330 if (read(tps.readFromThread, &resp, sizeof(resp)) != sizeof(resp))
1331 unixDie("read from thread pipe returned wrong size or error");
1332
1333 if (resp) {
1334 ret += *resp;
1335 delete resp;
1336 resp = nullptr;
1337 }
1338 // coverity[leaked_storage]
1339 }
1340 return ret;
1341 }
1342
1343 template string broadcastAccFunction(const std::function<string*()>& fun); // explicit instantiation
1344 template RecursorControlChannel::Answer broadcastAccFunction(const std::function<RecursorControlChannel::Answer*()>& fun); // explicit instantiation
1345 template uint64_t broadcastAccFunction(const std::function<uint64_t*()>& fun); // explicit instantiation
1346 template vector<ComboAddress> broadcastAccFunction(const std::function<vector<ComboAddress>*()>& fun); // explicit instantiation
1347 template vector<pair<DNSName, uint16_t>> broadcastAccFunction(const std::function<vector<pair<DNSName, uint16_t>>*()>& fun); // explicit instantiation
1348 template ThreadTimes broadcastAccFunction(const std::function<ThreadTimes*()>& fun);
1349 template ProxyMappingStats_t broadcastAccFunction(const std::function<ProxyMappingStats_t*()>& fun);
1350 template RemoteLoggerStats_t broadcastAccFunction(const std::function<RemoteLoggerStats_t*()>& fun);
1351
1352 static int serviceMain(int argc, char* argv[], Logr::log_t log)
1353 {
1354 g_log.setName(g_programname);
1355 g_log.disableSyslog(::arg().mustDo("disable-syslog"));
1356 g_log.setTimestamps(::arg().mustDo("log-timestamp"));
1357
1358 if (!::arg()["logging-facility"].empty()) {
1359 int val = logFacilityToLOG(::arg().asNum("logging-facility"));
1360 if (val >= 0)
1361 g_log.setFacility(val);
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 }
1366 }
1367
1368 showProductVersion();
1369
1370 g_disthashseed = dns_random(0xffffffff);
1371
1372 checkLinuxIPv6Limits(log);
1373 try {
1374 pdns::parseQueryLocalAddress(::arg()["query-local-address"]);
1375 }
1376 catch (std::exception& e) {
1377 SLOG(g_log << Logger::Error << "Assigning local query addresses: " << e.what(),
1378 log->error(Logr::Error, e.what(), "Unable to assign local query address"));
1379 exit(99);
1380 }
1381
1382 if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET)) {
1383 SyncRes::s_doIPv4 = true;
1384 SLOG(g_log << Logger::Warning << "Enabling IPv4 transport for outgoing queries" << endl,
1385 log->info(Logr::Notice, "Enabling IPv4 transport for outgoing queries"));
1386 }
1387 else {
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"));
1390 }
1391
1392 if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET6)) {
1393 SyncRes::s_doIPv6 = true;
1394 SLOG(g_log << Logger::Warning << "Enabling IPv6 transport for outgoing queries" << endl,
1395 log->info(Logr::Notice, "Enabling IPv6 transport for outgoing queries"));
1396 }
1397 else {
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"));
1400 }
1401
1402 if (!SyncRes::s_doIPv6 && !SyncRes::s_doIPv4) {
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"));
1405 exit(99);
1406 }
1407
1408 // keep this ABOVE loadRecursorLuaConfig!
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;
1419 else {
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"])));
1422 exit(1);
1423 }
1424
1425 g_signatureInceptionSkew = ::arg().asNum("signature-inception-skew");
1426 if (g_signatureInceptionSkew < 0) {
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"));
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 {
1440 ProxyMapping proxyMapping;
1441 loadRecursorLuaConfig(::arg()["lua-config-file"], delayedLuaThreads, proxyMapping);
1442 // Initial proxy mapping
1443 g_proxyMapping = proxyMapping.empty() ? nullptr : std::make_unique<ProxyMapping>(proxyMapping);
1444 }
1445 catch (PDNSException& e) {
1446 SLOG(g_log << Logger::Error << "Cannot load Lua configuration: " << e.reason << endl,
1447 log->error(Logr::Error, e.reason, "Cannot load Lua configuration"));
1448 exit(1);
1449 }
1450
1451 parseACLs();
1452 initPublicSuffixList(::arg()["public-suffix-list-file"]);
1453
1454 if (!::arg()["dont-query"].empty()) {
1455 vector<string> ips;
1456 stringtok(ips, ::arg()["dont-query"], ", ");
1457 ips.push_back("0.0.0.0");
1458 ips.push_back("::");
1459
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 {
1473 log->info(Logr::Notice, "Will not send queries to", "addresses", Logging::IterLoggable(ips.begin(), ips.end()));
1474 }
1475 }
1476
1477 /* this needs to be done before parseACLs(), which call broadcastFunction() */
1478 RecThreadInfo::setWeDistributeQueries(::arg().mustDo("pdns-distributes-queries"));
1479 if (RecThreadInfo::weDistributeQueries()) {
1480 SLOG(g_log << Logger::Warning << "PowerDNS Recursor itself will distribute queries over threads" << endl,
1481 log->info(Logr::Notice, "PowerDNS Recursor itself will distribute queries over threads"));
1482 }
1483
1484 g_outgoingEDNSBufsize = ::arg().asNum("edns-outgoing-bufsize");
1485
1486 if (::arg()["trace"] == "fail") {
1487 SyncRes::setDefaultLogMode(SyncRes::Store);
1488 }
1489 else if (::arg().mustDo("trace")) {
1490 SyncRes::setDefaultLogMode(SyncRes::Log);
1491 ::arg().set("quiet") = "no";
1492 g_quiet = false;
1493 g_dnssecLOG = true;
1494 }
1495 auto myHostname = getHostname();
1496 if (!myHostname.has_value()) {
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"));
1499 }
1500
1501 SyncRes::s_minimumTTL = ::arg().asNum("minimum-ttl-override");
1502 SyncRes::s_minimumECSTTL = ::arg().asNum("ecs-minimum-ttl-override");
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");
1507 // Cap the packetcache-servfail-ttl to the packetcache-ttl
1508 uint32_t packetCacheServFailTTL = ::arg().asNum("packetcache-servfail-ttl");
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");
1516 SyncRes::s_maxnsperresolve = ::arg().asNum("max-ns-per-resolve");
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");
1521 SyncRes::s_refresh_ttlperc = ::arg().asNum("refresh-on-ttl-perc");
1522 SyncRes::s_locked_ttlperc = ::arg().asNum("record-cache-locked-ttl-perc");
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");
1529 SyncRes::s_save_parent_ns_set = ::arg().mustDo("save-parent-ns-set");
1530 SyncRes::s_max_busy_dot_probes = ::arg().asNum("max-busy-dot-probes");
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 }
1541
1542 if (SyncRes::s_tcp_fast_open_connect) {
1543 checkFastOpenSysctl(true, log);
1544 checkTFOconnect(log);
1545 }
1546
1547 if (SyncRes::s_serverID.empty()) {
1548 SyncRes::s_serverID = myHostname.has_value() ? *myHostname : "";
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;
1572 }
1573 else if (value == "no") {
1574 SyncRes::s_hardenNXD = SyncRes::HardenNXD::No;
1575 }
1576 else if (value != "dnssec") {
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)));
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
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) {
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"])));
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) {
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"])));
1634 exit(1);
1635 }
1636 }
1637
1638 g_networkTimeoutMsec = ::arg().asNum("network-timeout");
1639
1640 std::tie(g_initialDomainMap, g_initialAllowNotifyFor) = parseZoneConfiguration();
1641
1642 g_latencyStatSize = ::arg().asNum("latency-statistic-size");
1643
1644 g_logCommonErrors = ::arg().mustDo("log-common-errors");
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 {
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"])));
1662 exit(1);
1663 }
1664 g_paddingTag = ::arg().asNum("edns-padding-tag");
1665 g_paddingOutgoing = ::arg().mustDo("edns-padding-out");
1666
1667 RecThreadInfo::setNumDistributorThreads(::arg().asNum("distributor-threads"));
1668 RecThreadInfo::setNumWorkerThreads(::arg().asNum("threads"));
1669 if (RecThreadInfo::numWorkers() < 1) {
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"));
1672 RecThreadInfo::setNumWorkerThreads(1);
1673 }
1674
1675 g_maxMThreads = ::arg().asNum("max-mthreads");
1676
1677 int64_t maxInFlight = ::arg().asNum("max-concurrent-requests-per-tcp-connection");
1678 if (maxInFlight < 1 || maxInFlight > USHRT_MAX || maxInFlight >= g_maxMThreads) {
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)"));
1681 TCPConnection::s_maxInFlight = 10;
1682 }
1683 else {
1684 TCPConnection::s_maxInFlight = maxInFlight;
1685 }
1686
1687 int64_t millis = ::arg().asNum("tcp-out-max-idle-ms");
1688 TCPOutConnectionManager::s_maxIdleTime = timeval{millis / 1000, (static_cast<suseconds_t>(millis) % 1000) * 1000};
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
1695 s_statisticsInterval = ::arg().asNum("statistics-interval");
1696
1697 g_addExtendedResolutionDNSErrors = ::arg().mustDo("extended-resolution-errors");
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 {
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"));
1706 }
1707 }
1708
1709 {
1710 SuffixMatchNode dontThrottleNames;
1711 vector<string> parts;
1712 stringtok(parts, ::arg()["dont-throttle-names"], " ,");
1713 for (const auto& p : parts) {
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"], " ,");
1721 for (const auto& p : parts) {
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"], " ,");
1731 for (const auto& p : parts) {
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()) {
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,
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"));
1745 }
1746 #endif
1747 for (const auto& p : parts) {
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
1762 g_balancingFactor = ::arg().asDouble("distribution-load-factor");
1763 if (g_balancingFactor != 0.0 && g_balancingFactor < 1.0) {
1764 g_balancingFactor = 0.0;
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"));
1767 }
1768
1769 #ifdef SO_REUSEPORT
1770 g_reusePort = ::arg().mustDo("reuseport");
1771 #endif
1772
1773 RecThreadInfo::infos().resize(RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + RecThreadInfo::numWorkers() + RecThreadInfo::numTaskThreads());
1774
1775 if (g_reusePort) {
1776 if (RecThreadInfo::weDistributeQueries()) {
1777 /* first thread is the handler, then distributors */
1778 for (unsigned int threadId = 1; threadId <= RecThreadInfo::numDistributors(); threadId++) {
1779 auto& info = RecThreadInfo::info(threadId);
1780 auto& deferredAdds = info.deferredAdds;
1781 auto& tcpSockets = info.tcpSockets;
1782 makeUDPServerSockets(deferredAdds, log);
1783 makeTCPServerSockets(deferredAdds, tcpSockets, log);
1784 }
1785 }
1786 else {
1787 /* first thread is the handler, there is no distributor here and workers are accepting queries */
1788 for (unsigned int threadId = 1; threadId <= RecThreadInfo::numWorkers(); threadId++) {
1789 auto& info = RecThreadInfo::info(threadId);
1790 auto& deferredAdds = info.deferredAdds;
1791 auto& tcpSockets = info.tcpSockets;
1792 makeUDPServerSockets(deferredAdds, log);
1793 makeTCPServerSockets(deferredAdds, tcpSockets, log);
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 */
1801 makeUDPServerSockets(g_deferredAdds, log);
1802 makeTCPServerSockets(g_deferredAdds, tcpSockets, log);
1803
1804 /* every listener (so distributor if g_weDistributeQueries, workers otherwise)
1805 needs to listen to the shared sockets */
1806 if (RecThreadInfo::weDistributeQueries()) {
1807 /* first thread is the handler, then distributors */
1808 for (unsigned int threadId = 1; threadId <= RecThreadInfo::numDistributors(); threadId++) {
1809 RecThreadInfo::info(threadId).tcpSockets = tcpSockets;
1810 }
1811 }
1812 else {
1813 /* first thread is the handler, there is no distributor here and workers are accepting queries */
1814 for (unsigned int threadId = 1; threadId <= RecThreadInfo::numWorkers(); threadId++) {
1815 RecThreadInfo::info(threadId).tcpSockets = tcpSockets;
1816 }
1817 }
1818 }
1819
1820 #ifdef NOD_ENABLED
1821 // Setup newly observed domain globals
1822 setupNODGlobal();
1823 #endif /* NOD_ENABLED */
1824
1825 int forks;
1826 for (forks = 0; forks < ::arg().asNum("processes") - 1; ++forks) {
1827 if (!fork()) // we are child
1828 break;
1829 }
1830
1831 if (::arg().mustDo("daemon")) {
1832 SLOG(g_log << Logger::Warning << "Calling daemonize, going to background" << endl,
1833 log->info(Logr::Warning, "Calling daemonize, going to background"));
1834 g_log.toConsole(Logger::Critical);
1835 daemonize(log);
1836 }
1837 if (Utility::getpid() == 1) {
1838 /* We are running as pid 1, register sigterm and sigint handler
1839
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
1851 signal(SIGTERM, termIntHandler);
1852 signal(SIGINT, termIntHandler);
1853 }
1854
1855 signal(SIGUSR1, usr1Handler);
1856 signal(SIGUSR2, usr2Handler);
1857 signal(SIGPIPE, SIG_IGN);
1858
1859 checkOrFixFDS(log);
1860
1861 #ifdef HAVE_LIBSODIUM
1862 if (sodium_init() == -1) {
1863 SLOG(g_log << Logger::Error << "Unable to initialize sodium crypto library" << endl,
1864 log->info(Logr::Error, "Unable to initialize sodium crypto library"));
1865 exit(99);
1866 }
1867 #endif
1868
1869 openssl_thread_setup();
1870 openssl_seed();
1871 /* setup rng before chroot */
1872 dns_random_init();
1873
1874 if (::arg()["server-id"].empty()) {
1875 ::arg().set("server-id") = myHostname.has_value() ? *myHostname : "";
1876 }
1877
1878 int newgid = 0;
1879 if (!::arg()["setgid"].empty())
1880 newgid = strToGID(::arg()["setgid"]);
1881 int newuid = 0;
1882 if (!::arg()["setuid"].empty())
1883 newuid = strToUID(::arg()["setuid"]);
1884
1885 Utility::dropGroupPrivs(newuid, newgid);
1886
1887 if (!::arg()["chroot"].empty()) {
1888 #ifdef HAVE_SYSTEMD
1889 char* ns;
1890 ns = getenv("NOTIFY_SOCKET");
1891 if (ns != nullptr) {
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'"));
1894 exit(1);
1895 }
1896 #endif
1897 if (chroot(::arg()["chroot"].c_str()) < 0 || chdir("/") < 0) {
1898 int err = errno;
1899 SLOG(g_log << Logger::Error << "Unable to chroot to '" + ::arg()["chroot"] + "': " << strerror(err) << ", exiting" << endl,
1900 log->error(Logr::Error, err, "Unable to chroot", "chroot", Logging::Loggable(::arg()["chroot"])));
1901 exit(1);
1902 }
1903 else {
1904 SLOG(g_log << Logger::Info << "Chrooted to '" << ::arg()["chroot"] << "'" << endl,
1905 log->info(Logr::Info, "Chrooted", "chroot", Logging::Loggable(::arg()["chroot"])));
1906 }
1907 }
1908
1909 checkSocketDir(log);
1910
1911 g_pidfname = ::arg()["socket-dir"] + "/" + g_programname + ".pid";
1912 if (!g_pidfname.empty())
1913 unlink(g_pidfname.c_str()); // remove possible old pid file
1914 writePid(log);
1915
1916 makeControlChannelSocket(::arg().asNum("processes") > 1 ? forks : -1);
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 }
1926 catch (const std::exception& e) {
1927 SLOG(g_log << Logger::Warning << e.what() << endl,
1928 log->error(Logr::Warning, e.what(), "Could not drop capabilities"));
1929 }
1930
1931 startLuaConfigDelayedThreads(delayedLuaThreads, g_luaconfs.getCopy().generation);
1932 delayedLuaThreads.rpzPrimaryThreads.clear(); // no longer needed
1933
1934 RecThreadInfo::makeThreadPipes(log);
1935
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");
1939 g_maxUDPQueriesPerRound = ::arg().asNum("max-udp-queries-per-round");
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")) {
1954 #ifdef HAVE_NET_SNMP
1955 string setting = ::arg()["snmp-daemon-socket"];
1956 if (setting.empty()) {
1957 setting = ::arg()["snmp-master-socket"];
1958 }
1959 g_snmpAgent = std::make_shared<RecursorSNMPAgent>("recursor", setting);
1960 g_snmpAgent->run();
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
1966 }
1967
1968 int port = ::arg().asNum("udp-source-port-min");
1969 if (port < 1024 || port > 65535) {
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"));
1972 exit(99); // this isn't going to fix itself either
1973 }
1974 g_minUdpSourcePort = port;
1975 port = ::arg().asNum("udp-source-port-max");
1976 if (port < 1024 || port > 65535 || port < g_minUdpSourcePort) {
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"));
1979 exit(99); // this isn't going to fix itself either
1980 }
1981 g_maxUdpSourcePort = port;
1982 std::vector<string> parts{};
1983 stringtok(parts, ::arg()["udp-source-port-avoid"], ", ");
1984 for (const auto& part : parts) {
1985 port = std::stoi(part);
1986 if (port < 1024 || port > 65535) {
1987 SLOG(g_log << Logger::Error << "Unable to launch, udp-source-port-avoid contains an invalid port number: " << part << endl,
1988 log->info(Logr::Error, "Unable to launch, udp-source-port-avoid contains an invalid port number", "port", Logging::Loggable(part)));
1989 exit(99); // this isn't going to fix itself either
1990 }
1991 g_avoidUdpSourcePorts.insert(port);
1992 }
1993
1994 return RecThreadInfo::runThreads(log);
1995 }
1996
1997 static void handlePipeRequest(int fd, FDMultiplexer::funcparam_t& var)
1998 {
1999 ThreadMSG* tmsg = nullptr;
2000
2001 if (read(fd, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // fd == readToThread || fd == readQueriesToThread
2002 unixDie("read from thread pipe returned wrong size or error");
2003 }
2004
2005 void* resp = 0;
2006 try {
2007 resp = tmsg->func();
2008 }
2009 catch (std::exception& e) {
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 }
2014 }
2015 catch (PDNSException& e) {
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 }
2020 }
2021 if (tmsg->wantAnswer) {
2022 if (write(RecThreadInfo::self().pipes.writeFromThread, &resp, sizeof(resp)) != sizeof(resp)) {
2023 delete tmsg;
2024 unixDie("write to thread pipe returned wrong size or error");
2025 }
2026 }
2027
2028 delete tmsg;
2029 }
2030
2031 static void handleRCC(int fd, FDMultiplexer::funcparam_t& var)
2032 {
2033 auto log = g_slog->withName("control");
2034 try {
2035 FDWrapper clientfd = accept(fd, nullptr, nullptr);
2036 if (clientfd == -1) {
2037 throw PDNSException("accept failed");
2038 }
2039 string msg = g_rcc.recv(clientfd).d_str;
2040 SLOG(g_log << Logger::Info << "Received rec_control command '" << msg << "' via controlsocket" << endl,
2041 log->info(Logr::Info, "Received rec_control command via control socket", "command", Logging::Loggable(msg)));
2042
2043 RecursorControlParser rcp;
2044 RecursorControlParser::func_t* command;
2045 auto answer = rcp.getAnswer(clientfd, msg, &command);
2046
2047 g_rcc.send(clientfd, answer);
2048 command();
2049 }
2050 catch (const std::exception& e) {
2051 SLOG(g_log << Logger::Error << "Error dealing with control socket request: " << e.what() << endl,
2052 log->error(Logr::Error, e.what(), "Exception while dealing with control socket request", "exception", Logging::Loggable("std::exception")));
2053 }
2054 catch (const PDNSException& ae) {
2055 SLOG(g_log << Logger::Error << "Error dealing with control socket request: " << ae.reason << endl,
2056 log->error(Logr::Error, ae.reason, "Exception while dealing with control socket request", "exception", Logging::Loggable("PDNSException")));
2057 }
2058 }
2059
2060 class PeriodicTask
2061 {
2062 public:
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 }
2070
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 }
2080
2081 time_t getPeriod() const
2082 {
2083 return period.tv_sec;
2084 }
2085
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
2101 private:
2102 struct timeval last_run
2103 {
2104 0, 0
2105 };
2106 struct timeval period;
2107 const string name;
2108 };
2109
2110 static void houseKeeping(void*)
2111 {
2112 auto log = g_slog->withName("housekeeping");
2113 static thread_local bool t_running; // houseKeeping can get suspended in secpoll, and be restarted, which makes us do duplicate work
2114
2115 try {
2116 if (t_running) {
2117 return;
2118 }
2119 t_running = true;
2120
2121 struct timeval now;
2122 Utility::gettimeofday(&now);
2123
2124 // Below are the tasks that run for every recursorThread, including handler and taskThread
2125 if (t_packetCache) {
2126 static thread_local PeriodicTask packetCacheTask{"packetCacheTask", 5};
2127 packetCacheTask.runIfDue(now, []() {
2128 size_t sz = g_maxPacketCacheEntries / (RecThreadInfo::numWorkers() + RecThreadInfo::numDistributors());
2129 t_packetCache->setMaxSize(sz); // g_maxPacketCacheEntries might have changed by rec_control
2130 t_packetCache->doPruneTo(sz);
2131 });
2132 }
2133
2134 static thread_local PeriodicTask pruneTCPTask{"pruneTCPTask", 5};
2135 pruneTCPTask.runIfDue(now, [now]() {
2136 t_tcp_manager.cleanup(now);
2137 });
2138
2139 const auto& info = RecThreadInfo::self();
2140
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
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
2158 runTasks(10, g_logCommonErrors);
2159
2160 static PeriodicTask ztcTask{"ZTC", 60};
2161 static map<DNSName, RecZoneToCache::State> ztcStates;
2162 ztcTask.runIfDue(now, [&luaconfsLocal]() {
2163 RecZoneToCache::maintainStates(luaconfsLocal->ztcConfigs, ztcStates, luaconfsLocal->generation);
2164 for (auto& ztc : luaconfsLocal->ztcConfigs) {
2165 RecZoneToCache::ZoneToCache(ztc.second, ztcStates.at(ztc.first));
2166 }
2167 });
2168 }
2169 else if (info.isHandler()) {
2170 static PeriodicTask recordCachePruneTask{"RecordCachePruneTask", 5};
2171 recordCachePruneTask.runIfDue(now, []() {
2172 g_recCache->doPrune(g_maxCacheEntries);
2173 });
2174
2175 static PeriodicTask negCachePruneTask{"NegCachePrunteTask", 5};
2176 negCachePruneTask.runIfDue(now, []() {
2177 g_negCache->prune(g_maxCacheEntries / 10);
2178 });
2179
2180 static PeriodicTask aggrNSECPruneTask{"AggrNSECPruneTask", 5};
2181 aggrNSECPruneTask.runIfDue(now, [now]() {
2182 if (g_aggressiveNSECCache) {
2183 g_aggressiveNSECCache->prune(now.tv_sec);
2184 }
2185 });
2186
2187 static PeriodicTask pruneNSpeedTask{"pruneNSSpeedTask", 30};
2188 pruneNSpeedTask.runIfDue(now, [now]() {
2189 SyncRes::pruneNSSpeeds(now.tv_sec - 300);
2190 });
2191
2192 static PeriodicTask pruneEDNSTask{"pruneEDNSTask", 60};
2193 pruneEDNSTask.runIfDue(now, [now]() {
2194 SyncRes::pruneEDNSStatuses(now.tv_sec);
2195 });
2196
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
2204 static PeriodicTask pruneThrottledTask{"pruneThrottledTask", 5};
2205 pruneThrottledTask.runIfDue(now, [now]() {
2206 SyncRes::pruneThrottledServers(now.tv_sec);
2207 });
2208
2209 static PeriodicTask pruneFailedServersTask{"pruneFailedServerTask", 5};
2210 pruneFailedServersTask.runIfDue(now, [now]() {
2211 SyncRes::pruneFailedServers(now.tv_sec - SyncRes::s_serverdownthrottletime * 10);
2212 });
2213
2214 static PeriodicTask pruneNonResolvingTask{"pruneNonResolvingTask", 5};
2215 pruneNonResolvingTask.runIfDue(now, [now]() {
2216 SyncRes::pruneNonResolving(now.tv_sec - SyncRes::s_nonresolvingnsthrottletime);
2217 });
2218
2219 static PeriodicTask pruneSaveParentSetTask{"pruneSaveParentSetTask", 60};
2220 pruneSaveParentSetTask.runIfDue(now, [now]() {
2221 SyncRes::pruneSaveParentsNSSets(now.tv_sec);
2222 });
2223
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)};
2227 rootUpdateTask.runIfDue(now, [now, &log, minRootRefreshInterval]() {
2228 int res = SyncRes::getRootNS(now, nullptr, 0, log);
2229 if (res == 0) {
2230 // Success, go back to the defaut period
2231 rootUpdateTask.setPeriod(std::max(SyncRes::s_maxcachettl * 8 / 10, minRootRefreshInterval));
2232 const string msg = "Exception while priming the root NS zones";
2233 try {
2234 primeRootNSZones(g_dnssecmode, 0);
2235 }
2236 catch (const std::exception& e) {
2237 SLOG(g_log << Logger::Error << "Exception while priming the root NS zones: " << e.what() << endl,
2238 log->error(Logr::Error, e.what(), msg, "exception", Logging::Loggable("std::exception")));
2239 }
2240 catch (const PDNSException& e) {
2241 SLOG(g_log << Logger::Error << "Exception while priming the root NS zones: " << e.reason << endl,
2242 log->error(Logr::Error, e.reason, msg, "exception", Logging::Loggable("PDNSException")));
2243 }
2244 catch (const ImmediateServFailException& e) {
2245 SLOG(g_log << Logger::Error << "Exception while priming the root NS zones: " << e.reason << endl,
2246 log->error(Logr::Error, e.reason, msg, "exception", Logging::Loggable("ImmediateServFailException")));
2247 }
2248 catch (const PolicyHitException& e) {
2249 SLOG(g_log << Logger::Error << "Policy hit while priming the root NS zones" << endl,
2250 log->info(Logr::Error, msg, "exception", Logging::Loggable("PolicyHitException")));
2251 }
2252 catch (...) {
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"));
2255 }
2256 }
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 }
2263 });
2264
2265 static PeriodicTask secpollTask{"secpollTask", 3600};
2266 static time_t t_last_secpoll;
2267 secpollTask.runIfDue(now, [&log]() {
2268 try {
2269 doSecPoll(&t_last_secpoll, log);
2270 }
2271 catch (const std::exception& e) {
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"));
2274 }
2275 catch (const PDNSException& e) {
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"));
2278 }
2279 catch (const ImmediateServFailException& e) {
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"));
2282 }
2283 catch (const PolicyHitException& e) {
2284 SLOG(g_log << Logger::Error << "Policy hit while performing security poll" << endl,
2285 log->info(Logr::Error, "Policy hit while performing security poll"));
2286 }
2287 catch (...) {
2288 SLOG(g_log << Logger::Error << "Exception while performing security poll" << endl,
2289 log->info(Logr::Error, "Exception while performing security poll"));
2290 }
2291 });
2292
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);
2300 trustAnchorTask.runIfDue(now, [&luaconfsLocal, &log]() {
2301 if (!luaconfsLocal->trustAnchorFileInfo.fname.empty() && luaconfsLocal->trustAnchorFileInfo.interval != 0) {
2302 SLOG(g_log << Logger::Debug << "Refreshing Trust Anchors from file" << endl,
2303 log->info(Logr::Debug, "Refreshing Trust Anchors from file"));
2304 try {
2305 map<DNSName, dsmap_t> dsAnchors;
2306 if (updateTrustAnchorsFromFile(luaconfsLocal->trustAnchorFileInfo.fname, dsAnchors, log)) {
2307 g_luaconfs.modify([&dsAnchors](LuaConfigItems& lci) {
2308 lci.dsAnchors = dsAnchors;
2309 });
2310 }
2311 }
2312 catch (const PDNSException& pe) {
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"));
2315 }
2316 }
2317 });
2318 }
2319 t_running = false;
2320 }
2321 catch (const PDNSException& ae) {
2322 t_running = false;
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"));
2325 throw;
2326 }
2327 catch (...) {
2328 t_running = false;
2329 SLOG(g_log << Logger::Error << "Uncaught exception in housekeeping thread" << endl,
2330 log->info(Logr::Error, "Uncaught exception in housekeeping thread"));
2331 throw;
2332 }
2333 }
2334
2335 static void recursorThread()
2336 {
2337 auto log = g_slog->withName("runtime");
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>();
2348 if (g_proxyMapping) {
2349 t_proxyMapping = make_unique<ProxyMapping>(*g_proxyMapping);
2350 }
2351 else {
2352 t_proxyMapping = nullptr;
2353 }
2354
2355 if (threadInfo.isHandler()) {
2356 if (!primeHints()) {
2357 threadInfo.setExitCode(EXIT_FAILURE);
2358 RecursorControlChannel::stop = 1;
2359 SLOG(g_log << Logger::Critical << "Priming cache failed, stopping" << endl,
2360 log->info(Logr::Critical, "Priming cache failed, stopping"));
2361 }
2362 SLOG(g_log << Logger::Debug << "Done priming cache with root hints" << endl,
2363 log->info(Logr::Debug, "Done priming cache with root hints"));
2364 }
2365 }
2366
2367 if (!::arg().mustDo("disable-packetcache") && (threadInfo.isDistributor() || threadInfo.isWorker())) {
2368 // Only enable packet cache for thread processing queries from the outside world
2369 t_packetCache = std::make_unique<RecursorPacketCache>(g_maxPacketCacheEntries / (RecThreadInfo::numWorkers() + RecThreadInfo::numDistributors()));
2370 }
2371
2372 #ifdef NOD_ENABLED
2373 if (threadInfo.isWorker())
2374 setupNODThread(log);
2375 #endif /* NOD_ENABLED */
2376
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>();
2382 t_pdl->loadFile(::arg()["lua-dns-script"]);
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"])));
2385 }
2386 }
2387 catch (std::exception& e) {
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"])));
2390 _exit(99);
2391 }
2392 }
2393
2394 unsigned int ringsize = ::arg().asNum("stats-ringbuffer-entries") / RecThreadInfo::numWorkers();
2395 if (ringsize) {
2396 t_remotes = std::make_unique<addrringbuf_t>();
2397 if (RecThreadInfo::weDistributeQueries())
2398 t_remotes->set_capacity(::arg().asNum("stats-ringbuffer-entries") / RecThreadInfo::numDistributors());
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);
2424 #ifdef HAVE_FSTRM
2425 checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
2426 checkFrameStreamExport(luaconfsLocal, luaconfsLocal->nodFrameStreamExportConfig, t_nodFrameStreamServersInfo);
2427 #endif
2428
2429 t_fdm = unique_ptr<FDMultiplexer>(getMultiplexer(log));
2430
2431 std::unique_ptr<RecursorWebServer> rws;
2432
2433 t_fdm->addReadFD(threadInfo.pipes.readToThread, handlePipeRequest);
2434
2435 if (threadInfo.isHandler()) {
2436 if (::arg().mustDo("webserver")) {
2437 SLOG(g_log << Logger::Warning << "Enabling web server" << endl,
2438 log->info(Logr::Info, "Enabling web server"))
2439 try {
2440 rws = make_unique<RecursorWebServer>(t_fdm.get());
2441 }
2442 catch (const PDNSException& e) {
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"));
2445 _exit(99);
2446 }
2447 }
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())));
2450 }
2451 else {
2452 t_fdm->addReadFD(threadInfo.pipes.readQueriesToThread, handlePipeRequest);
2453
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 }
2460 }
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 }
2466 }
2467 }
2468 }
2469
2470 registerAllStats();
2471
2472 if (threadInfo.isHandler()) {
2473 t_fdm->addReadFD(g_rcc.d_fd, handleRCC); // control channel
2474 }
2475
2476 unsigned int maxTcpClients = ::arg().asNum("max-tcp-clients");
2477
2478 bool listenOnTCP{true};
2479
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
2485
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
2495 while (!RecursorControlChannel::stop) {
2496 while (MT->schedule(&g_now))
2497 ; // MTasker letting the mthreads do their thing
2498
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
2501 if (((threadInfo.isHandler() || threadInfo.isTaskThread()) && s_counter % 11 == 0) || s_counter % 499 == 0) {
2502 struct timeval start;
2503 Utility::gettimeofday(&start);
2504 MT->makeThread(houseKeeping, nullptr);
2505 if (!threadInfo.isTaskThread()) {
2506 struct timeval stop;
2507 Utility::gettimeofday(&stop);
2508 g_stats.maintenanceUsec += uSec(stop - start);
2509 ++g_stats.maintenanceCalls;
2510 }
2511 }
2512
2513 if (!(s_counter % 55)) {
2514 typedef vector<pair<int, FDMultiplexer::funcparam_t>> expired_t;
2515 expired_t expired = t_fdm->getTimeouts(g_now);
2516
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)
2520 SLOG(g_log << Logger::Warning << "Timeout from remote TCP client " << conn->d_remote.toStringWithPort() << endl,
2521 g_slogtcpin->info(Logr::Warning, "Timeout from remote TCP client", "remote", Logging::Loggable(conn->d_remote)));
2522 t_fdm->removeReadFD(i->first);
2523 }
2524 }
2525
2526 s_counter++;
2527
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 }
2533
2534 Utility::gettimeofday(&g_now, nullptr);
2535
2536 if ((g_now.tv_sec - last_carbon) >= carbonInterval) {
2537 MT->makeThread(doCarbonDump, 0);
2538 last_carbon = g_now.tv_sec;
2539 }
2540 }
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) {
2547 struct timeval start;
2548 Utility::gettimeofday(&start);
2549 t_pdl->maintenance();
2550 last_lua_maintenance = g_now.tv_sec;
2551 struct timeval stop;
2552 Utility::gettimeofday(&stop);
2553 g_stats.maintenanceUsec += uSec(stop - start);
2554 ++g_stats.maintenanceCalls;
2555 }
2556 }
2557 }
2558
2559 t_fdm->run(&g_now);
2560 // 'run' updates g_now for us
2561
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;
2569 }
2570 }
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;
2577 }
2578 }
2579 }
2580 }
2581 }
2582 catch (PDNSException& ae) {
2583 SLOG(g_log << Logger::Error << "Exception: " << ae.reason << endl,
2584 log->error(Logr::Error, ae.reason, "Exception in RecursorThread", "exception", Logging::Loggable("PDNSException")))
2585 }
2586 catch (std::exception& e) {
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")))
2589 }
2590 catch (...) {
2591 SLOG(g_log << Logger::Error << "any other exception in main: " << endl,
2592 log->info(Logr::Error, "Exception in RecursorThread"));
2593 }
2594 }
2595
2596 int main(int argc, char** argv)
2597 {
2598 g_argc = argc;
2599 g_argv = argv;
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
2610 ::arg().set("stack-size", "stack size per mthread") = "400000";
2611 #else
2612 ::arg().set("stack-size", "stack size per mthread") = "200000";
2613 #endif
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"
2631 #ifdef HAVE_SYSTEMD
2632 #define SYSTEMD_SETID_MSG ". When running inside systemd, use the User and Group settings in the unit-file!"
2633 SYSTEMD_SETID_MSG
2634 #endif
2635 )
2636 = "";
2637 ::arg().set("setuid", "If set, change user id to this uid for more security"
2638 #ifdef HAVE_SYSTEMD
2639 SYSTEMD_SETID_MSG
2640 #endif
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") = "";
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") = "";
2654 ::arg().set("webserver-allow-from", "Webserver access is only allowed from these subnets") = "127.0.0.1,::1";
2655 ::arg().set("webserver-loglevel", "Amount of logging in the webserver (none, normal, detailed)") = "normal";
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"
2672 #ifdef HAVE_SYSTEMD
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 }
2679 #else
2680 )
2681 = "";
2682 #endif
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";
2687 ::arg().set("max-concurrent-requests-per-tcp-connection", "Maximum number of requests handled concurrently per TCP connection") = "10";
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";
2755 ::arg().set("max-ns-per-resolve", "Maximum number of NS records to consider to resolve a name, 0 is no limit") = "13";
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") = "";
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 }
2779 std::string defaultDisabledStats = defaultAPIDisabledStats + ", cumul-clientanswers, cumul-authanswers, policy-hits, proxy-mapping-total, remote-logger-count";
2780
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;
2785
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;
2790
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";
2794
2795 ::arg().set("cpu-map", "Thread to CPU mapping, space separated thread-id=cpu1,cpu2..cpuN pairs") = "";
2796
2797 ::arg().setSwitch("log-rpz-changes", "Log additions and removals to RPZ zones at Info level") = "no";
2798
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";
2801
2802 ::arg().set("dns64-prefix", "DNS64 prefix") = "";
2803
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";
2810
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";
2816 ::arg().set("refresh-on-ttl-perc", "If a record is requested from the cache and only this % of original TTL remains, refetch") = "0";
2817 ::arg().set("record-cache-locked-ttl-perc", "Replace records in record cache only after this % of original TTL has passed") = "0";
2818
2819 ::arg().set("x-dnssec-names", "Collect DNSSEC statistics for names or suffixes in this list in separate x-dnssec counters") = "";
2820
2821 #ifdef NOD_ENABLED
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";
2835 #endif /* NOD_ENABLED */
2836
2837 ::arg().setSwitch("extended-resolution-errors", "If set, send an EDNS Extended Error extension on resolution failures, like DNSSEC validation errors") = "no";
2838
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";
2840
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") = "";
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";
2844 ::arg().setSwitch("edns-padding-out", "Whether to add EDNS padding to outgoing DoT messages") = "yes";
2845
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";
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";
2855 ::arg().set("structured-logging-backend", "Structured logging backend") = "default";
2856 ::arg().setSwitch("save-parent-ns-set", "Save parent NS set to be used if child NS set fails") = "yes";
2857 ::arg().set("max-busy-dot-probes", "Maximum number of concurrent DoT probes") = "0";
2858 ::arg().set("serve-stale-extensions", "Number of times a record's ttl is extended by 30s to be served stale") = "0";
2859
2860 ::arg().setCmd("help", "Provide a helpful message");
2861 ::arg().setCmd("version", "Print version string");
2862 ::arg().setCmd("config", "Output blank configuration. You can use --config=check to test the config file and command line arguments.");
2863 ::arg().setDefaults();
2864 g_log.toConsole(Logger::Info);
2865 ::arg().laxParse(argc, argv); // do a lax parse
2866
2867 if (::arg().mustDo("version")) {
2868 showProductVersion();
2869 showBuildConfiguration();
2870 exit(0);
2871 }
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");
2881 s_logUrgency = (Logger::Urgency)::arg().asNum("loglevel");
2882 g_slogStructured = ::arg().mustDo("structured-logging");
2883 s_structured_logger_backend = ::arg()["structured-logging-backend"];
2884
2885 if (s_logUrgency < Logger::Error) {
2886 s_logUrgency = Logger::Error;
2887 }
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
2890 }
2891 g_log.setLoglevel(s_logUrgency);
2892 g_log.toConsole(s_logUrgency);
2893
2894 string configname = ::arg()["config-dir"] + "/recursor.conf";
2895 if (!::arg()["config-name"].empty()) {
2896 configname = ::arg()["config-dir"] + "/recursor-" + ::arg()["config-name"] + ".conf";
2897 g_programname += "-" + ::arg()["config-name"];
2898 }
2899 cleanSlashes(configname);
2900
2901 if (!::arg().getCommands().empty()) {
2902 cerr << "Fatal: non-option";
2903 if (::arg().getCommands().size() > 1) {
2904 cerr << "s";
2905 }
2906 cerr << " (";
2907 bool first = true;
2908 for (const auto& c : ::arg().getCommands()) {
2909 if (!first) {
2910 cerr << ", ";
2911 }
2912 first = false;
2913 cerr << c;
2914 }
2915 cerr << ") on the command line, perhaps a '--setting=123' statement missed the '='?" << endl;
2916 exit(99);
2917 }
2918
2919 if (s_structured_logger_backend == "systemd-journal") {
2920 #ifdef HAVE_SYSTEMD
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 }
2925 #endif
2926 if (g_slog == nullptr) {
2927 cerr << "Structured logging to systemd-journal requested but it is not available" << endl;
2928 }
2929 }
2930
2931 if (g_slog == nullptr) {
2932 g_slog = Logging::Logger::create(loggerBackend);
2933 }
2934
2935 // Missing: a mechanism to call setVerbosity(x)
2936 auto startupLog = g_slog->withName("config");
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");
2940
2941 ::arg().setSLog(startupLog);
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 }
2961 else if (config == "default" || config.empty()) {
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
2985 if (!::arg().file(configname.c_str())) {
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)));
2988 }
2989
2990 // Reparse, now with config file as well
2991 ::arg().parse(argc, argv);
2992
2993 g_quiet = ::arg().mustDo("quiet");
2994 s_logUrgency = (Logger::Urgency)::arg().asNum("loglevel");
2995 g_slogStructured = ::arg().mustDo("structured-logging");
2996
2997 if (s_logUrgency < Logger::Error) {
2998 s_logUrgency = Logger::Error;
2999 }
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
3002 }
3003 g_log.setLoglevel(s_logUrgency);
3004 g_log.toConsole(s_logUrgency);
3005
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,
3008 startupLog->info(Logr::Error, "Cannot use chroot and enable the API at the same time"));
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
3019 if (::arg().asNum("threads") == 1) {
3020 if (::arg().mustDo("pdns-distributes-queries")) {
3021 SLOG(g_log << Logger::Warning << "Asked to run with pdns-distributes-queries set but no distributor threads, raising to 1" << endl,
3022 startupLog->v(1)->info("Only one thread, no need to distribute queries ourselves"));
3023 ::arg().set("pdns-distributes-queries") = "no";
3024 }
3025 }
3026
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,
3029 startupLog->v(1)->info("Asked to run with pdns-distributes-queries set but no distributor threads, raising to 1"));
3030 ::arg().set("distributor-threads") = "1";
3031 }
3032
3033 if (!::arg().mustDo("pdns-distributes-queries")) {
3034 ::arg().set("distributor-threads") = "0";
3035 }
3036
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
3040 ret = serviceMain(argc, argv, startupLog);
3041 }
3042 catch (const PDNSException& ae) {
3043 SLOG(g_log << Logger::Error << "Exception: " << ae.reason << endl,
3044 g_slog->withName("config")->error(Logr::Critical, ae.reason, "Fatal error", "exception", Logging::Loggable("PDNSException")));
3045 ret = EXIT_FAILURE;
3046 }
3047 catch (const std::exception& e) {
3048 SLOG(g_log << Logger::Error << "STL Exception: " << e.what() << endl,
3049 g_slog->withName("config")->error(Logr::Critical, e.what(), "Fatal error", "exception", Logging::Loggable("std::exception")));
3050 ret = EXIT_FAILURE;
3051 }
3052 catch (...) {
3053 SLOG(g_log << Logger::Error << "any other exception in main: " << endl,
3054 g_slog->withName("config")->info(Logr::Critical, "Fatal error"));
3055 ret = EXIT_FAILURE;
3056 }
3057
3058 return ret;
3059 }
3060
3061 static RecursorControlChannel::Answer* doReloadLuaScript()
3062 {
3063 string fname = ::arg()["lua-dns-script"];
3064 auto log = g_slog->withName("runtime")->withValues("name", Logging::Loggable(fname));
3065 try {
3066 if (fname.empty()) {
3067 t_pdl.reset();
3068 SLOG(g_log << Logger::Info << RecThreadInfo::id() << " Unloaded current lua script" << endl,
3069 log->info(Logr::Info, "Unloaded current lua script"));
3070 return new RecursorControlChannel::Answer{0, string("unloaded\n")};
3071 }
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();
3079 SLOG(g_log << Logger::Error << msg << endl,
3080 log->error(Logr::Error, ex.what(), "Retaining current script, could not read new script"));
3081 return new RecursorControlChannel::Answer{1, msg + "\n"};
3082 }
3083 }
3084 catch (std::exception& e) {
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"));
3087 return new RecursorControlChannel::Answer{1, string("retaining current script, error from '" + fname + "': " + e.what() + "\n")};
3088 }
3089
3090 SLOG(g_log << Logger::Warning << RecThreadInfo::id() << " (Re)loaded lua script from '" << fname << "'" << endl,
3091 log->info(Logr::Warning, "(Re)loaded lua script"));
3092 return new RecursorControlChannel::Answer{0, string("(re)loaded '" + fname + "'\n")};
3093 }
3094
3095 RecursorControlChannel::Answer doQueueReloadLuaScript(vector<string>::const_iterator begin, vector<string>::const_iterator end)
3096 {
3097 if (begin != end)
3098 ::arg().set("lua-dns-script") = *begin;
3099
3100 return broadcastAccFunction<RecursorControlChannel::Answer>(doReloadLuaScript);
3101 }
3102
3103 static string* pleaseUseNewTraceRegex(const std::string& newRegex)
3104 try {
3105 if (newRegex.empty()) {
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 }
3114 catch (PDNSException& ae) {
3115 return new string(ae.reason + "\n");
3116 }
3117
3118 string doTraceRegex(vector<string>::const_iterator begin, vector<string>::const_iterator end)
3119 {
3120 return broadcastAccFunction<string>([=] { return pleaseUseNewTraceRegex(begin != end ? *begin : ""); });
3121 }
3122
3123 static uint64_t* pleaseWipePacketCache(const DNSName& canon, bool subtree, uint16_t qtype)
3124 {
3125 return new uint64_t(t_packetCache ? t_packetCache->doWipePacketCache(canon, qtype, subtree) : 0);
3126 }
3127
3128 struct 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);
3134 // scanbuild complains here about an allocated function object that is being leaked. Needs investigation
3135 res.packet_count = broadcastAccFunction<uint64_t>([=] { return pleaseWipePacketCache(canon, subtree, qtype); });
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) {
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"));
3145 }
3146
3147 return res;
3148 }