• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 18743945403

23 Oct 2025 09:29AM UTC coverage: 65.845% (+0.02%) from 65.829%
18743945403

Pull #16356

github

web-flow
Merge 8a2027ef1 into efa3637e8
Pull Request #16356: auth 5.0: backport "pdnsutil: fix b2b-migrate to from sql to non-sql"

42073 of 92452 branches covered (45.51%)

Branch coverage included in aggregate %.

128008 of 165855 relevant lines covered (77.18%)

6379935.17 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

62.06
/pdns/recursordist/rec-main.cc
1
/*
2
 * This file is part of PowerDNS or dnsdist.
3
 * Copyright -- PowerDNS.COM B.V. and its contributors
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of version 2 of the GNU General Public License as
7
 * published by the Free Software Foundation.
8
 *
9
 * In addition, for the avoidance of any doubt, permission is granted to
10
 * link this program with OpenSSL and to (re)distribute the binaries
11
 * produced as the result of such linking.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
 */
22
#include <sys/stat.h>
23

24
#include "rec-main.hh"
25

26
#include "aggressive_nsec.hh"
27
#include "capabilities.hh"
28
#include "arguments.hh"
29
#include "dns_random.hh"
30
#include "rec_channel.hh"
31
#include "rec-tcpout.hh"
32
#include "version.hh"
33
#include "query-local-address.hh"
34
#include "validate-recursor.hh"
35
#include "pubsuffix.hh"
36
#include "opensslsigners.hh"
37
#include "ws-recursor.hh"
38
#include "rec-taskqueue.hh"
39
#include "secpoll-recursor.hh"
40
#include "logging.hh"
41
#include "dnsseckeeper.hh"
42
#include "rec-rust-lib/cxxsettings.hh"
43
#include "json.hh"
44
#include "rec-system-resolve.hh"
45
#include "root-dnssec.hh"
46
#include "ratelimitedlog.hh"
47
#include "rec-rust-lib/rust/web.rs.h"
48

49
#ifdef NOD_ENABLED
50
#include "nod.hh"
51
#endif /* NOD_ENABLED */
52

53
#ifdef HAVE_LIBSODIUM
54
#include <sodium.h>
55

56
#include <cstddef>
57
#include <utility>
58
#endif
59

60
#ifdef HAVE_SYSTEMD
61
// All calls are coming form the same function, so no use for CODE_LINE, CODE_FUNC etc
62
#define SD_JOURNAL_SUPPRESS_LOCATION
63
#include <systemd/sd-daemon.h>
64
#include <systemd/sd-journal.h>
65
#endif
66

67
#ifdef HAVE_FSTRM
68
thread_local FrameStreamServersInfo t_frameStreamServersInfo;
69
thread_local FrameStreamServersInfo t_nodFrameStreamServersInfo;
70
#endif /* HAVE_FSTRM */
71

72
/* g++ defines __SANITIZE_THREAD__
73
   clang++ supports the nice __has_feature(thread_sanitizer),
74
   let's merge them */
75
#if defined(__has_feature)
76
#if __has_feature(thread_sanitizer)
77
#define __SANITIZE_THREAD__ 1
78
#endif
79
#if __has_feature(address_sanitizer)
80
#define __SANITIZE_ADDRESS__ 1
81
#endif
82
#endif
83

84
string g_programname = "pdns_recursor";
85
string g_pidfname;
86
RecursorControlChannel g_rcc; // only active in the handler thread
87
bool g_regressionTestMode;
88
bool g_yamlSettings;
89
string g_yamlSettingsSuffix;
90
bool g_luaSettingsInYAML;
91

92
#ifdef NOD_ENABLED
93
bool g_nodEnabled;
94
DNSName g_nodLookupDomain;
95
bool g_nodLog;
96
SuffixMatchNode g_nodDomainWL;
97
SuffixMatchNode g_udrDomainWL;
98
std::string g_nod_pbtag;
99
bool g_udrEnabled;
100
bool g_udrLog;
101
std::string g_udr_pbtag;
102
std::unique_ptr<nod::NODDB> g_nodDBp;
103
std::unique_ptr<nod::UniqueResponseDB> g_udrDBp;
104
#endif /* NOD_ENABLED */
105

106
std::atomic<bool> statsWanted;
107
uint32_t g_disthashseed;
108
bool g_useIncomingECS;
109
static shared_ptr<NetmaskGroup> g_initialProxyProtocolACL;
110
static shared_ptr<std::set<ComboAddress>> g_initialProxyProtocolExceptions;
111
boost::optional<ComboAddress> g_dns64Prefix{boost::none};
112
DNSName g_dns64PrefixReverse;
113
unsigned int g_maxChainLength;
114
LockGuarded<std::shared_ptr<SyncRes::domainmap_t>> g_initialDomainMap; // new threads needs this to be setup
115
LockGuarded<std::shared_ptr<NetmaskGroup>> g_initialAllowFrom; // new thread needs to be setup with this
116
LockGuarded<std::shared_ptr<NetmaskGroup>> g_initialAllowNotifyFrom; // new threads need this to be setup
117
LockGuarded<std::shared_ptr<notifyset_t>> g_initialAllowNotifyFor; // new threads need this to be setup
118
bool g_logRPZChanges{false};
119
static time_t s_statisticsInterval;
120
static std::atomic<uint32_t> s_counter;
121
int g_argc;
122
char** g_argv;
123
static string s_structured_logger_backend;
124
static Logger::Urgency s_logUrgency;
125

126
std::shared_ptr<Logr::Logger> g_slogtcpin;
127
std::shared_ptr<Logr::Logger> g_slogudpin;
128
std::shared_ptr<Logr::Logger> g_slogudpout;
129

130
/* without reuseport, all listeners share the same sockets */
131
static deferredAdd_t s_deferredUDPadds;
132
static deferredAdd_t s_deferredTCPadds;
133

134
/* first we have the handler thread, t_id == 0 (threads not created as a RecursorThread have t_id = NOT_INITED)
135
   then the distributor threads if any
136
   and finally the workers */
137
std::vector<RecThreadInfo> RecThreadInfo::s_threadInfos;
138

139
std::unique_ptr<ProxyMapping> g_proxyMapping; // new threads needs this to be setup
140
thread_local std::unique_ptr<ProxyMapping> t_proxyMapping;
141

142
bool RecThreadInfo::s_weDistributeQueries; // if true, 1 or more threads listen on the incoming query sockets and distribute them to workers
143
unsigned int RecThreadInfo::s_numDistributorThreads;
144
unsigned int RecThreadInfo::s_numUDPWorkerThreads;
145
unsigned int RecThreadInfo::s_numTCPWorkerThreads;
146
thread_local unsigned int RecThreadInfo::t_id{RecThreadInfo::TID_NOT_INITED};
147

148
pdns::RateLimitedLog g_rateLimitedLogger;
149

150
static void runStartStopLua(bool start, Logr::log_t log);
151

152
static std::map<unsigned int, std::set<int>> parseCPUMap(Logr::log_t log)
153
{
175✔
154
  std::map<unsigned int, std::set<int>> result;
175✔
155

156
  const std::string value = ::arg()["cpu-map"];
175✔
157

158
  if (!value.empty() && !isSettingThreadCPUAffinitySupported()) {
175!
159
    SLOG(g_log << Logger::Warning << "CPU mapping requested but not supported, skipping" << endl,
×
160
         log->info(Logr::Warning, "CPU mapping requested but not supported, skipping"));
×
161
    return result;
×
162
  }
×
163

164
  std::vector<std::string> parts;
175✔
165

166
  stringtok(parts, value, " \t");
175✔
167

168
  for (const auto& part : parts) {
175!
169
    if (part.find('=') == string::npos) {
×
170
      continue;
×
171
    }
×
172

173
    try {
×
174
      auto headers = splitField(part, '=');
×
175
      boost::trim(headers.first);
×
176
      boost::trim(headers.second);
×
177

178
      auto threadId = pdns::checked_stoi<unsigned int>(headers.first);
×
179
      std::vector<std::string> cpus;
×
180

181
      stringtok(cpus, headers.second, ",");
×
182

183
      for (const auto& cpu : cpus) {
×
184
        int cpuId = std::stoi(cpu);
×
185

186
        result[threadId].insert(cpuId);
×
187
      }
×
188
    }
×
189
    catch (const std::exception& e) {
×
190
      SLOG(g_log << Logger::Error << "Error parsing cpu-map entry '" << part << "': " << e.what() << endl,
×
191
           log->error(Logr::Error, e.what(), "Error parsing cpu-map entry", "entry", Logging::Loggable(part)));
×
192
    }
×
193
  }
×
194

195
  return result;
175✔
196
}
175✔
197

198
static void setCPUMap(const std::map<unsigned int, std::set<int>>& cpusMap, unsigned int n, pthread_t tid, Logr::log_t log)
199
{
910✔
200
  const auto& cpuMapping = cpusMap.find(n);
910✔
201
  if (cpuMapping == cpusMap.cend()) {
910!
202
    return;
910✔
203
  }
910✔
204
  int ret = mapThreadToCPUList(tid, cpuMapping->second);
×
205
  if (ret == 0) {
×
206
    if (!g_slogStructured) {
×
207
      g_log << Logger::Info << "CPU affinity for thread " << n << " has been set to CPU map:";
×
208
      for (const auto cpu : cpuMapping->second) {
×
209
        g_log << Logger::Info << " " << cpu;
×
210
      }
×
211
      g_log << Logger::Info << endl;
×
212
    }
×
213
    else {
×
214
      log->info(Logr::Info, "CPU affinity has been set", "thread", Logging::Loggable(n), "cpumap", Logging::IterLoggable(cpuMapping->second.begin(), cpuMapping->second.end()));
×
215
    }
×
216
  }
×
217
  else {
×
218
    if (!g_slogStructured) {
×
219
      g_log << Logger::Warning << "Error setting CPU affinity for thread " << n << " to CPU map:";
×
220
      for (const auto cpu : cpuMapping->second) {
×
221
        g_log << Logger::Info << " " << cpu;
×
222
      }
×
223
      g_log << Logger::Info << ' ' << stringerror(ret) << endl;
×
224
    }
×
225
    else {
×
226
      log->error(Logr::Warning, ret, "Error setting CPU affinity", "thread", Logging::Loggable(n), "cpumap", Logging::IterLoggable(cpuMapping->second.begin(), cpuMapping->second.end()));
×
227
    }
×
228
  }
×
229
}
×
230

231
static void recursorThread();
232

233
void RecThreadInfo::start(unsigned int tid, const string& tname, const std::map<unsigned int, std::set<int>>& cpusMap, Logr::log_t log)
234
{
910✔
235
  name = tname;
910✔
236
  thread = std::thread([tid, tname] {
910✔
237
    t_id = tid;
909✔
238
    const string threadPrefix = "rec/";
909✔
239
    setThreadName(threadPrefix + tname);
909✔
240
    recursorThread();
909✔
241
  });
909✔
242
  setCPUMap(cpusMap, tid, thread.native_handle(), log);
910✔
243
}
910✔
244

245
int RecThreadInfo::runThreads(Logr::log_t log)
246
{
175✔
247
  int ret = EXIT_SUCCESS;
175✔
248
  const auto cpusMap = parseCPUMap(log);
175✔
249

250
  if (RecThreadInfo::numDistributors() + RecThreadInfo::numUDPWorkers() == 1) {
175✔
251
    SLOG(g_log << Logger::Warning << "Operating with single UDP distributor/worker thread" << endl,
3✔
252
         log->info(Logr::Notice, "Operating with single UDP distributor/worker thread"));
3✔
253

254
    /* This thread handles the web server, carbon, statistics and the control channel */
255
    unsigned int currentThreadId = 0;
3✔
256
    auto& handlerInfo = RecThreadInfo::info(currentThreadId);
3✔
257
    handlerInfo.setHandler();
3✔
258
    handlerInfo.start(currentThreadId, "web+stat", cpusMap, log);
3✔
259

260
    // We skip the single UDP worker thread 1, it's handled after the loop and taskthreads
261
    currentThreadId = 2;
3✔
262
    for (unsigned int thread = 0; thread < RecThreadInfo::numTCPWorkers(); thread++, currentThreadId++) {
6✔
263
      auto& info = RecThreadInfo::info(currentThreadId);
3✔
264
      info.setTCPListener();
3✔
265
      info.setWorker();
3✔
266
      info.start(currentThreadId, "tcpworker", cpusMap, log);
3✔
267
    }
3✔
268

269
    for (unsigned int thread = 0; thread < RecThreadInfo::numTaskThreads(); thread++, currentThreadId++) {
6✔
270
      auto& taskInfo = RecThreadInfo::info(currentThreadId);
3✔
271
      taskInfo.setTaskThread();
3✔
272
      taskInfo.start(currentThreadId, "task", cpusMap, log);
3✔
273
    }
3✔
274

275
    if (::arg().mustDo("webserver")) {
3!
276
      serveRustWeb();
×
277
    }
×
278

279
    currentThreadId = 1;
3✔
280
    auto& info = RecThreadInfo::info(currentThreadId);
3✔
281
    info.setListener();
3✔
282
    info.setWorker();
3✔
283
    RecThreadInfo::setThreadId(currentThreadId);
3✔
284
    recursorThread();
3✔
285

286
    // Skip handler thread (it might be still handling the quit-nicely) and 1, which is actually the main thread in this case;
287
    // handler thread (0) will be handled in main().
288
    for (unsigned int thread = 2; thread < RecThreadInfo::numRecursorThreads(); thread++) {
9✔
289
      auto& tInfo = RecThreadInfo::info(thread);
6✔
290
      tInfo.thread.join();
6✔
291
      if (tInfo.exitCode != 0) {
6!
292
        ret = tInfo.exitCode;
×
293
      }
×
294
    }
6✔
295
  }
3✔
296
  else {
172✔
297
    // Setup RecThreadInfo objects
298
    unsigned int currentThreadId = 1;
172✔
299
    if (RecThreadInfo::weDistributeQueries()) {
172✔
300
      for (unsigned int thread = 0; thread < RecThreadInfo::numDistributors(); thread++, currentThreadId++) {
28✔
301
        RecThreadInfo::info(currentThreadId).setListener();
14✔
302
      }
14✔
303
    }
14✔
304
    for (unsigned int thread = 0; thread < RecThreadInfo::numUDPWorkers(); thread++, currentThreadId++) {
543✔
305
      auto& info = RecThreadInfo::info(currentThreadId);
371✔
306
      info.setListener(!RecThreadInfo::weDistributeQueries());
371✔
307
      info.setWorker();
371✔
308
    }
371✔
309
    for (unsigned int thread = 0; thread < RecThreadInfo::numTCPWorkers(); thread++, currentThreadId++) {
344✔
310
      auto& info = RecThreadInfo::info(currentThreadId);
172✔
311
      info.setTCPListener();
172✔
312
      info.setWorker();
172✔
313
    }
172✔
314
    for (unsigned int thread = 0; thread < RecThreadInfo::numTaskThreads(); thread++, currentThreadId++) {
344✔
315
      auto& info = RecThreadInfo::info(currentThreadId);
172✔
316
      info.setTaskThread();
172✔
317
    }
172✔
318

319
    // And now start the actual threads
320
    currentThreadId = 1;
172✔
321
    if (RecThreadInfo::weDistributeQueries()) {
172✔
322
      SLOG(g_log << Logger::Warning << "Launching " << RecThreadInfo::numDistributors() << " distributor threads" << endl,
14✔
323
           log->info(Logr::Notice, "Launching distributor threads", "count", Logging::Loggable(RecThreadInfo::numDistributors())));
14✔
324
      for (unsigned int thread = 0; thread < RecThreadInfo::numDistributors(); thread++, currentThreadId++) {
28✔
325
        auto& info = RecThreadInfo::info(currentThreadId);
14✔
326
        info.start(currentThreadId, "distr", cpusMap, log);
14✔
327
      }
14✔
328
    }
14✔
329
    SLOG(g_log << Logger::Warning << "Launching " << RecThreadInfo::numUDPWorkers() << " worker threads" << endl,
172✔
330
         log->info(Logr::Notice, "Launching worker threads", "count", Logging::Loggable(RecThreadInfo::numUDPWorkers())));
172✔
331

332
    for (unsigned int thread = 0; thread < RecThreadInfo::numUDPWorkers(); thread++, currentThreadId++) {
543✔
333
      auto& info = RecThreadInfo::info(currentThreadId);
371✔
334
      info.start(currentThreadId, "worker", cpusMap, log);
371✔
335
    }
371✔
336

337
    SLOG(g_log << Logger::Warning << "Launching " << RecThreadInfo::numTCPWorkers() << " tcpworker threads" << endl,
172✔
338
         log->info(Logr::Notice, "Launching tcpworker threads", "count", Logging::Loggable(RecThreadInfo::numTCPWorkers())));
172✔
339

340
    for (unsigned int thread = 0; thread < RecThreadInfo::numTCPWorkers(); thread++, currentThreadId++) {
344✔
341
      auto& info = RecThreadInfo::info(currentThreadId);
172✔
342
      info.start(currentThreadId, "tcpworker", cpusMap, log);
172✔
343
    }
172✔
344

345
    for (unsigned int thread = 0; thread < RecThreadInfo::numTaskThreads(); thread++, currentThreadId++) {
344✔
346
      auto& info = RecThreadInfo::info(currentThreadId);
172✔
347
      info.start(currentThreadId, "task", cpusMap, log);
172✔
348
    }
172✔
349

350
    /* This thread handles the web server, carbon, statistics and the control channel */
351
    currentThreadId = 0;
172✔
352
    auto& info = RecThreadInfo::info(currentThreadId);
172✔
353
    info.setHandler();
172✔
354
    info.start(currentThreadId, "web+stat", cpusMap, log);
172✔
355

356
    if (::arg().mustDo("webserver")) {
172✔
357
      serveRustWeb();
20✔
358
    }
20✔
359
    for (auto& tInfo : RecThreadInfo::infos()) {
901✔
360
      // who handles the handler? the caller!
361
      if (tInfo.isHandler()) {
901✔
362
        continue;
172✔
363
      }
172✔
364
      tInfo.thread.join();
729✔
365
      if (tInfo.exitCode != 0) {
729!
366
        ret = tInfo.exitCode;
×
367
      }
×
368
    }
729✔
369
  }
172✔
370
  return ret;
175✔
371
}
175✔
372

373
void RecThreadInfo::makeThreadPipes(Logr::log_t log)
374
{
175✔
375
  auto pipeBufferSize = ::arg().asNum("distribution-pipe-buffer-size");
175✔
376
  if (pipeBufferSize > 0) {
175!
377
    SLOG(g_log << Logger::Info << "Resizing the buffer of the distribution pipe to " << pipeBufferSize << endl,
×
378
         log->info(Logr::Info, "Resizing the buffer of the distribution pipe", "size", Logging::Loggable(pipeBufferSize)));
×
379
  }
×
380

381
  /* thread 0 is the handler / SNMP, worker threads start at 1 */
382
  for (unsigned int thread = 0; thread < numRecursorThreads(); ++thread) {
1,088✔
383
    auto& threadInfo = info(thread);
913✔
384

385
    std::array<int, 2> fileDesc{};
913✔
386
    if (pipe(fileDesc.data()) < 0) {
913!
387
      unixDie("Creating pipe for inter-thread communications");
×
388
    }
×
389

390
    threadInfo.pipes.readToThread = fileDesc[0];
913✔
391
    threadInfo.pipes.writeToThread = fileDesc[1];
913✔
392

393
    // handler thread only gets first pipe, not the others
394
    if (thread == 0) {
913✔
395
      continue;
175✔
396
    }
175✔
397

398
    if (pipe(fileDesc.data()) < 0) {
738!
399
      unixDie("Creating pipe for inter-thread communications");
×
400
    }
×
401

402
    threadInfo.pipes.readFromThread = fileDesc[0];
738✔
403
    threadInfo.pipes.writeFromThread = fileDesc[1];
738✔
404

405
    if (pipe(fileDesc.data()) < 0) {
738!
406
      unixDie("Creating pipe for inter-thread communications");
×
407
    }
×
408

409
    threadInfo.pipes.readQueriesToThread = fileDesc[0];
738✔
410
    threadInfo.pipes.writeQueriesToThread = fileDesc[1];
738✔
411

412
    if (pipeBufferSize > 0) {
738!
413
      if (!setPipeBufferSize(threadInfo.pipes.writeQueriesToThread, pipeBufferSize)) {
×
414
        int err = errno;
×
415
        SLOG(g_log << Logger::Warning << "Error resizing the buffer of the distribution pipe for thread " << thread << " to " << pipeBufferSize << ": " << stringerror(err) << endl,
×
416
             log->error(Logr::Warning, err, "Error resizing the buffer of the distribution pipe for thread", "thread", Logging::Loggable(thread), "size", Logging::Loggable(pipeBufferSize)));
×
417
        auto existingSize = getPipeBufferSize(threadInfo.pipes.writeQueriesToThread);
×
418
        if (existingSize > 0) {
×
419
          SLOG(g_log << Logger::Warning << "The current size of the distribution pipe's buffer for thread " << thread << " is " << existingSize << endl,
×
420
               log->info(Logr::Warning, "The current size of the distribution pipe's buffer for thread", "thread", Logging::Loggable(thread), "size", Logging::Loggable(existingSize)));
×
421
        }
×
422
      }
×
423
    }
×
424

425
    if (!setNonBlocking(threadInfo.pipes.writeQueriesToThread)) {
738!
426
      unixDie("Making pipe for inter-thread communications non-blocking");
×
427
    }
×
428
  }
738✔
429
}
175✔
430

431
ArgvMap& arg()
432
{
116,534✔
433
  static ArgvMap theArg;
116,534✔
434
  return theArg;
116,534✔
435
}
116,534✔
436

437
static FDMultiplexer* getMultiplexer(Logr::log_t log)
438
{
913✔
439
  FDMultiplexer* ret = nullptr;
913✔
440
  for (const auto& mplexer : FDMultiplexer::getMultiplexerMap()) {
913!
441
    try {
913✔
442
      ret = mplexer.second(FDMultiplexer::s_maxevents);
913✔
443
      return ret;
913✔
444
    }
913✔
445
    catch (FDMultiplexerException& fe) {
913✔
446
      SLOG(g_log << Logger::Warning << "Non-fatal error initializing possible multiplexer (" << fe.what() << "), falling back" << endl,
×
447
           log->error(Logr::Warning, fe.what(), "Non-fatal error initializing possible multiplexer, falling back"));
×
448
    }
×
449
    catch (...) {
913✔
450
      SLOG(g_log << Logger::Warning << "Non-fatal error initializing possible multiplexer" << endl,
×
451
           log->info(Logr::Warning, "Non-fatal error initializing possible multiplexer"));
×
452
    }
×
453
  }
913✔
454
  SLOG(g_log << Logger::Error << "No working multiplexer found!" << endl,
×
455
       log->info(Logr::Error, "No working multiplexer found!"));
×
456
  _exit(1);
×
457
}
913✔
458

459
static std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>> startProtobufServers(const ProtobufExportConfig& config, Logr::log_t log)
460
{
110✔
461
  auto result = std::make_shared<std::vector<std::unique_ptr<RemoteLogger>>>();
110✔
462

463
  for (const auto& server : config.servers) {
220✔
464
    try {
220✔
465
      auto logger = make_unique<RemoteLogger>(server, config.timeout, 100 * config.maxQueuedEntries, config.reconnectWaitTime, config.asyncConnect);
220✔
466
      logger->setLogQueries(config.logQueries);
220✔
467
      logger->setLogResponses(config.logResponses);
220✔
468
      result->emplace_back(std::move(logger));
220✔
469
    }
220✔
470
    catch (const std::exception& e) {
220✔
471
      SLOG(g_log << Logger::Error << "Error while starting protobuf logger to '" << server << ": " << e.what() << endl,
×
472
           log->error(Logr::Error, e.what(), "Exception while starting protobuf logger", "exception", Logging::Loggable("std::exception"), "server", Logging::Loggable(server)));
×
473
    }
×
474
    catch (const PDNSException& e) {
220✔
475
      SLOG(g_log << Logger::Error << "Error while starting protobuf logger to '" << server << ": " << e.reason << endl,
×
476
           log->error(Logr::Error, e.reason, "Exception while starting protobuf logger", "exception", Logging::Loggable("PDNSException"), "server", Logging::Loggable(server)));
×
477
    }
×
478
  }
220✔
479

480
  return result;
110✔
481
}
110✔
482

483
bool checkProtobufExport(LocalStateHolder<LuaConfigItems>& luaconfsLocal)
484
{
10,875✔
485
  if (!luaconfsLocal->protobufExportConfig.enabled) {
10,885✔
486
    if (t_protobufServers.servers) {
10,638!
487
      t_protobufServers.servers.reset();
×
488
      t_protobufServers.config = luaconfsLocal->protobufExportConfig;
×
489
    }
×
490

491
    return false;
10,638✔
492
  }
10,638✔
493

494
  /* if the server was not running, or if it was running according to a
495
     previous configuration */
496
  if (t_protobufServers.generation < luaconfsLocal->generation && t_protobufServers.config != luaconfsLocal->protobufExportConfig) {
2,147,483,894!
497

498
    if (t_protobufServers.servers) {
95!
499
      t_protobufServers.servers.reset();
×
500
    }
×
501
    auto log = g_slog->withName("protobuf");
95✔
502
    t_protobufServers.servers = startProtobufServers(luaconfsLocal->protobufExportConfig, log);
95✔
503
    t_protobufServers.config = luaconfsLocal->protobufExportConfig;
95✔
504
    t_protobufServers.generation = luaconfsLocal->generation;
95✔
505
  }
95✔
506

507
  return true;
2,147,483,894✔
508
}
10,875✔
509

510
bool checkOutgoingProtobufExport(LocalStateHolder<LuaConfigItems>& luaconfsLocal)
511
{
10,033✔
512
  if (!luaconfsLocal->outgoingProtobufExportConfig.enabled) {
10,033✔
513
    if (t_outgoingProtobufServers.servers) {
9,989!
514
      t_outgoingProtobufServers.servers.reset();
×
515
      t_outgoingProtobufServers.config = luaconfsLocal->outgoingProtobufExportConfig;
×
516
    }
×
517

518
    return false;
9,989✔
519
  }
9,989✔
520

521
  /* if the server was not running, or if it was running according to a
522
     previous configuration */
523
  if (t_outgoingProtobufServers.generation < luaconfsLocal->generation && t_outgoingProtobufServers.config != luaconfsLocal->outgoingProtobufExportConfig) {
44!
524

525
    if (t_outgoingProtobufServers.servers) {
15!
526
      t_outgoingProtobufServers.servers.reset();
×
527
    }
×
528
    auto log = g_slog->withName("protobuf");
15✔
529
    t_outgoingProtobufServers.servers = startProtobufServers(luaconfsLocal->outgoingProtobufExportConfig, log);
15✔
530
    t_outgoingProtobufServers.config = luaconfsLocal->outgoingProtobufExportConfig;
15✔
531
    t_outgoingProtobufServers.generation = luaconfsLocal->generation;
15✔
532
  }
15✔
533

534
  return true;
44✔
535
}
10,033✔
536

537
void protobufLogQuery(LocalStateHolder<LuaConfigItems>& luaconfsLocal, const boost::uuids::uuid& uniqueId, const ComboAddress& remote, const ComboAddress& local, const ComboAddress& mappedSource, const Netmask& ednssubnet, bool tcp, 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, const boost::optional<uint32_t>& ednsVersion, const dnsheader& header)
538
{
26✔
539
  auto log = g_slog->withName("pblq");
26✔
540

541
  if (!t_protobufServers.servers) {
26!
542
    return;
×
543
  }
×
544

545
  ComboAddress requestor;
26✔
546
  if (!luaconfsLocal->protobufExportConfig.logMappedFrom) {
26✔
547
    Netmask requestorNM(remote, remote.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
24!
548
    requestor = requestorNM.getMaskedNetwork();
24✔
549
    requestor.setPort(remote.getPort());
24✔
550
  }
24✔
551
  else {
2✔
552
    Netmask requestorNM(mappedSource, mappedSource.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
2!
553
    requestor = requestorNM.getMaskedNetwork();
2✔
554
    requestor.setPort(mappedSource.getPort());
2✔
555
  }
2✔
556

557
  pdns::ProtoZero::RecMessage msg{128, std::string::size_type(policyTags.empty() ? 0 : 64)}; // It's a guess
26✔
558
  msg.setType(pdns::ProtoZero::Message::MessageType::DNSQueryType);
26✔
559
  msg.setRequest(uniqueId, requestor, local, qname, qtype, qclass, header.id, tcp ? pdns::ProtoZero::Message::TransportProtocol::TCP : pdns::ProtoZero::Message::TransportProtocol::UDP, len);
26✔
560
  msg.setServerIdentity(SyncRes::s_serverID);
26✔
561
  msg.setEDNSSubnet(ednssubnet, ednssubnet.isIPv4() ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
26!
562
  msg.setRequestorId(requestorId);
26✔
563
  msg.setDeviceId(deviceId);
26✔
564
  msg.setDeviceName(deviceName);
26✔
565
  msg.setWorkerId(RecThreadInfo::thread_local_id());
26✔
566
  // For queries, packetCacheHit and outgoingQueries are not relevant
567

568
  if (!policyTags.empty()) {
26✔
569
    msg.addPolicyTags(policyTags);
5✔
570
  }
5✔
571
  for (const auto& mit : meta) {
26✔
572
    msg.setMeta(mit.first, mit.second.stringVal, mit.second.intVal);
2✔
573
  }
2✔
574
  msg.setHeaderFlags(*getFlagsFromDNSHeader(&header));
26✔
575
  if (ednsVersion) {
26!
576
    msg.setEDNSVersion(*ednsVersion);
26✔
577
  }
26✔
578

579
  std::string strMsg(msg.finishAndMoveBuf());
26✔
580
  for (auto& server : *t_protobufServers.servers) {
52✔
581
    remoteLoggerQueueData(*server, strMsg);
52✔
582
  }
52✔
583
}
26✔
584

585
void protobufLogResponse(pdns::ProtoZero::RecMessage& message)
586
{
34✔
587
  if (!t_protobufServers.servers) {
34!
588
    return;
×
589
  }
×
590

591
  std::string msg(message.finishAndMoveBuf());
34✔
592
  for (auto& server : *t_protobufServers.servers) {
68✔
593
    remoteLoggerQueueData(*server, msg);
68✔
594
  }
68✔
595
}
34✔
596

597
void protobufLogResponse(const DNSName& qname, QType qtype,
598
                         const struct dnsheader* header, LocalStateHolder<LuaConfigItems>& luaconfsLocal,
599
                         const RecursorPacketCache::OptPBData& pbData, const struct timeval& tval,
600
                         bool tcp, const ComboAddress& source, const ComboAddress& destination,
601
                         const ComboAddress& mappedSource,
602
                         const EDNSSubnetOpts& ednssubnet,
603
                         const boost::uuids::uuid& uniqueId, const string& requestorId, const string& deviceId,
604
                         const string& deviceName, const std::map<std::string, RecursorLua4::MetaValue>& meta,
605
                         const RecEventTrace& eventTrace,
606
                         pdns::trace::InitialSpanInfo& otTrace,
607
                         const std::unordered_set<std::string>& policyTags)
608
{
9✔
609
  pdns::ProtoZero::RecMessage pbMessage(pbData ? pbData->d_message : "", pbData ? pbData->d_response : "", 64, 10); // The extra bytes we are going to add
9!
610
  // Normally we take the immutable string from the cache and append a few values, but if it's not there (can this happen?)
611
  // we start with an empty string and append the minimal
612
  if (!pbData) {
9!
613
    pbMessage.setType(pdns::ProtoZero::Message::MessageType::DNSResponseType);
×
614
    pbMessage.setServerIdentity(SyncRes::s_serverID);
×
615
  }
×
616

617
  // In response part
618
  if (g_useKernelTimestamp && tval.tv_sec != 0) {
9!
619
    pbMessage.setQueryTime(tval.tv_sec, tval.tv_usec);
×
620
  }
×
621
  else {
9✔
622
    pbMessage.setQueryTime(g_now.tv_sec, g_now.tv_usec);
9✔
623
  }
9✔
624

625
  // In message part
626
  if (!luaconfsLocal->protobufExportConfig.logMappedFrom) {
9!
627
    pbMessage.setSocketFamily(source.sin4.sin_family);
9✔
628
    Netmask requestorNM(source, source.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
9!
629
    const auto& requestor = requestorNM.getMaskedNetwork();
9✔
630
    pbMessage.setFrom(requestor);
9✔
631
    pbMessage.setFromPort(source.getPort());
9✔
632
  }
9✔
633
  else {
×
634
    pbMessage.setSocketFamily(mappedSource.sin4.sin_family);
×
635
    Netmask requestorNM(mappedSource, mappedSource.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
×
636
    const auto& requestor = requestorNM.getMaskedNetwork();
×
637
    pbMessage.setFrom(requestor);
×
638
    pbMessage.setFromPort(mappedSource.getPort());
×
639
  }
×
640
  pbMessage.setMessageIdentity(uniqueId);
9✔
641
  pbMessage.setTo(destination);
9✔
642
  pbMessage.setSocketProtocol(tcp ? pdns::ProtoZero::Message::TransportProtocol::TCP : pdns::ProtoZero::Message::TransportProtocol::UDP);
9✔
643
  pbMessage.setId(header->id);
9✔
644

645
  pbMessage.setTime();
9✔
646
  pbMessage.setEDNSSubnet(ednssubnet.getSource(), ednssubnet.getSource().isIPv4() ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
9!
647
  pbMessage.setRequestorId(requestorId);
9✔
648
  pbMessage.setDeviceId(deviceId);
9✔
649
  pbMessage.setDeviceName(deviceName);
9✔
650
  pbMessage.setToPort(destination.getPort());
9✔
651
  pbMessage.setWorkerId(RecThreadInfo::thread_local_id());
9✔
652
  // this method is only used for PC cache hits
653
  pbMessage.setPacketCacheHit(true);
9✔
654
  // we do not set outgoingQueries, it is not relevant for PC cache hits
655

656
  for (const auto& metaItem : meta) {
9!
657
    pbMessage.setMeta(metaItem.first, metaItem.second.stringVal, metaItem.second.intVal);
×
658
  }
×
659
#ifdef NOD_ENABLED
9✔
660
  if (g_nodEnabled) {
9!
661
    pbMessage.setNewlyObservedDomain(false);
×
662
  }
×
663
#endif
9✔
664
  if (eventTrace.enabled() && (SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_pb) != 0) {
9!
665
    pbMessage.addEvents(eventTrace);
×
666
  }
×
667
  if (eventTrace.enabled() && (SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_ot) != 0) {
9!
668
    auto trace = pdns::trace::TracesData::boilerPlate("rec", qname.toLogString() + '/' + qtype.toString(), eventTrace.convertToOT(otTrace));
×
669
    pbMessage.setOpenTelemetryData(trace.encode());
×
670
  }
×
671
  pbMessage.addPolicyTags(policyTags);
9✔
672

673
  protobufLogResponse(pbMessage);
9✔
674
}
9✔
675

676
#ifdef HAVE_FSTRM
677

678
static std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>> startFrameStreamServers(const FrameStreamExportConfig& config, Logr::log_t log)
679
{
25✔
680
  auto result = std::make_shared<std::vector<std::unique_ptr<FrameStreamLogger>>>();
25✔
681

682
  for (const auto& server : config.servers) {
25✔
683
    try {
25✔
684
      std::unordered_map<string, unsigned> options;
25✔
685
      options["bufferHint"] = config.bufferHint;
25✔
686
      options["flushTimeout"] = config.flushTimeout;
25✔
687
      options["inputQueueSize"] = config.inputQueueSize;
25✔
688
      options["outputQueueSize"] = config.outputQueueSize;
25✔
689
      options["queueNotifyThreshold"] = config.queueNotifyThreshold;
25✔
690
      options["reopenInterval"] = config.reopenInterval;
25✔
691
      unique_ptr<FrameStreamLogger> fsl = nullptr;
25✔
692
      try {
25✔
693
        ComboAddress address(server);
25✔
694
        fsl = make_unique<FrameStreamLogger>(address.sin4.sin_family, address.toStringWithPort(), true, options);
25✔
695
      }
25✔
696
      catch (const PDNSException& e) {
25✔
697
        fsl = make_unique<FrameStreamLogger>(AF_UNIX, server, true, options);
25✔
698
      }
25✔
699
      fsl->setLogQueries(config.logQueries);
25✔
700
      fsl->setLogResponses(config.logResponses);
25✔
701
      fsl->setLogNODs(config.logNODs);
25✔
702
      fsl->setLogUDRs(config.logUDRs);
25✔
703
      result->emplace_back(std::move(fsl));
25✔
704
    }
25✔
705
    catch (const std::exception& e) {
25✔
706
      SLOG(g_log << Logger::Error << "Error while starting dnstap framestream logger to '" << server << ": " << e.what() << endl,
×
707
           log->error(Logr::Error, e.what(), "Exception while starting dnstap framestream logger", "exception", Logging::Loggable("std::exception"), "server", Logging::Loggable(server)));
×
708
    }
×
709
    catch (const PDNSException& e) {
25✔
710
      SLOG(g_log << Logger::Error << "Error while starting dnstap framestream logger to '" << server << ": " << e.reason << endl,
×
711
           log->error(Logr::Error, e.reason, "Exception while starting dnstap framestream logger", "exception", Logging::Loggable("PDNSException"), "server", Logging::Loggable(server)));
×
712
    }
×
713
  }
25✔
714

715
  return result;
25✔
716
}
25✔
717

718
static void asyncFrameStreamLoggersCleanup(std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>&& servers)
719
{
×
720
  auto thread = std::thread([&] {
×
721
    servers.reset();
×
722
  });
×
723
  thread.detach();
×
724
}
×
725

726
bool checkFrameStreamExport(LocalStateHolder<LuaConfigItems>& luaconfsLocal, const FrameStreamExportConfig& config, FrameStreamServersInfo& serverInfos)
727
{
14,777✔
728
  if (!config.enabled) {
14,777✔
729
    if (serverInfos.servers) {
14,720!
730
      // dt's take care of cleanup
731
      asyncFrameStreamLoggersCleanup(std::move(serverInfos.servers));
×
732
      serverInfos.config = config;
×
733
    }
×
734

735
    return false;
14,720✔
736
  }
14,720✔
737

738
  /* if the server was not running, or if it was running according to a previous
739
   * configuration
740
   */
741
  if (serverInfos.generation < luaconfsLocal->generation && serverInfos.config != config) {
57!
742
    if (serverInfos.servers) {
25!
743
      // dt's take care of cleanup
744
      asyncFrameStreamLoggersCleanup(std::move(serverInfos.servers));
×
745
    }
×
746

747
    auto dnsTapLog = g_slog->withName("dnstap");
25✔
748
    serverInfos.servers = startFrameStreamServers(config, dnsTapLog);
25✔
749
    serverInfos.config = config;
25✔
750
    serverInfos.generation = luaconfsLocal->generation;
25✔
751
  }
25✔
752

753
  return true;
57✔
754
}
14,777✔
755

756
#endif /* HAVE_FSTRM */
757

758
static void makeControlChannelSocket(int processNum = -1)
759
{
175✔
760
  string sockname = ::arg()["socket-dir"] + "/" + g_programname;
175✔
761
  if (processNum >= 0) {
175!
762
    sockname += "." + std::to_string(processNum);
×
763
  }
×
764
  sockname += ".controlsocket";
175✔
765
  g_rcc.listen(sockname);
175✔
766

767
  uid_t sockowner = -1;
175✔
768
  gid_t sockgroup = -1;
175✔
769

770
  if (!::arg().isEmpty("socket-group")) {
175!
771
    sockgroup = ::arg().asGid("socket-group");
×
772
  }
×
773
  if (!::arg().isEmpty("socket-owner")) {
175!
774
    sockowner = ::arg().asUid("socket-owner");
×
775
  }
×
776

777
  if (sockgroup != static_cast<gid_t>(-1) || sockowner != static_cast<uid_t>(-1)) {
175!
778
    if (chown(sockname.c_str(), sockowner, sockgroup) < 0) {
×
779
      unixDie("Failed to chown control socket");
×
780
    }
×
781
  }
×
782

783
  // do mode change if socket-mode is given
784
  if (!::arg().isEmpty("socket-mode")) {
175!
785
    mode_t sockmode = ::arg().asMode("socket-mode");
×
786
    if (chmod(sockname.c_str(), sockmode) < 0) {
×
787
      unixDie("Failed to chmod control socket");
×
788
    }
×
789
  }
×
790
}
175✔
791

792
static void writePid(Logr::log_t log)
793
{
175✔
794
  if (!::arg().mustDo("write-pid")) {
175!
795
    return;
×
796
  }
×
797
  ofstream ostr(g_pidfname.c_str(), std::ios_base::app);
175✔
798
  if (ostr) {
175!
799
    ostr << Utility::getpid() << endl;
175✔
800
  }
175✔
801
  else {
×
802
    int err = errno;
×
803
    SLOG(g_log << Logger::Error << "Writing pid for " << Utility::getpid() << " to " << g_pidfname << " failed: " << stringerror(err) << endl,
×
804
         log->error(Logr::Error, err, "Writing pid failed", "pid", Logging::Loggable(Utility::getpid()), "file", Logging::Loggable(g_pidfname)));
×
805
  }
×
806
}
175✔
807

808
static void checkSocketDir(Logr::log_t log)
809
{
175✔
810
  string dir(::arg()["socket-dir"]);
175✔
811
  string msg;
175✔
812

813
  struct stat dirStat = {};
175✔
814
  if (stat(dir.c_str(), &dirStat) == -1) {
175!
815
    msg = "it does not exist or cannot access";
×
816
  }
×
817
  else if (!S_ISDIR(dirStat.st_mode)) {
175!
818
    msg = "it is not a directory";
×
819
  }
×
820
  else if (access(dir.c_str(), R_OK | W_OK | X_OK) != 0) {
175!
821
    msg = "cannot read, write or search";
×
822
  }
×
823
  else {
175✔
824
    return;
175✔
825
  }
175✔
826
  dir = ::arg()["chroot"] + dir;
×
827
  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,
×
828
       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)));
×
829
  _exit(1);
×
830
}
175✔
831

832
#ifdef NOD_ENABLED
833
static void setupNODThread(Logr::log_t log)
834
{
175✔
835
  if (g_nodEnabled) {
175✔
836
    uint32_t num_cells = ::arg().asNum("new-domain-db-size");
3✔
837
    g_nodDBp = std::make_unique<nod::NODDB>(num_cells);
3✔
838
    try {
3✔
839
      g_nodDBp->setCacheDir(::arg()["new-domain-history-dir"]);
3✔
840
    }
3✔
841
    catch (const PDNSException& e) {
3✔
842
      SLOG(g_log << Logger::Error << "new-domain-history-dir (" << ::arg()["new-domain-history-dir"] << ") is not readable or does not exist" << endl,
×
843
           log->error(Logr::Error, e.reason, "new-domain-history-dir is not readable or does not exists", "dir", Logging::Loggable(::arg()["new-domain-history-dir"])));
×
844
      _exit(1);
×
845
    }
×
846
    if (!g_nodDBp->init()) {
3!
847
      SLOG(g_log << Logger::Error << "Could not initialize domain tracking" << endl,
×
848
           log->info(Logr::Error, "Could not initialize domain tracking"));
×
849
      _exit(1);
×
850
    }
×
851
    if (::arg().asNum("new-domain-db-snapshot-interval") > 0) {
3!
852
      g_nodDBp->setSnapshotInterval(::arg().asNum("new-domain-db-snapshot-interval"));
3✔
853
      std::thread thread([tid = std::this_thread::get_id()]() {
3✔
854
        g_nodDBp->housekeepingThread(tid);
3✔
855
      });
3✔
856
      thread.detach();
3✔
857
    }
3✔
858
  }
3✔
859
  if (g_udrEnabled) {
175✔
860
    uint32_t num_cells = ::arg().asNum("unique-response-db-size");
3✔
861
    g_udrDBp = std::make_unique<nod::UniqueResponseDB>(num_cells);
3✔
862
    try {
3✔
863
      g_udrDBp->setCacheDir(::arg()["unique-response-history-dir"]);
3✔
864
    }
3✔
865
    catch (const PDNSException& e) {
3✔
866
      SLOG(g_log << Logger::Error << "unique-response-history-dir (" << ::arg()["unique-response-history-dir"] << ") is not readable or does not exist" << endl,
×
867
           log->info(Logr::Error, "unique-response-history-dir is not readable or does not exist", "dir", Logging::Loggable(::arg()["unique-response-history-dir"])));
×
868
      _exit(1);
×
869
    }
×
870
    if (!g_udrDBp->init()) {
3!
871
      SLOG(g_log << Logger::Error << "Could not initialize unique response tracking" << endl,
×
872
           log->info(Logr::Error, "Could not initialize unique response tracking"));
×
873
      _exit(1);
×
874
    }
×
875
    if (::arg().asNum("new-domain-db-snapshot-interval") > 0) {
3!
876
      g_udrDBp->setSnapshotInterval(::arg().asNum("new-domain-db-snapshot-interval"));
3✔
877
      std::thread thread([tid = std::this_thread::get_id()]() {
3✔
878
        g_udrDBp->housekeepingThread(tid);
3✔
879
      });
3✔
880
      thread.detach();
3✔
881
    }
3✔
882
  }
3✔
883
}
175✔
884

885
static void parseIgnorelist(const std::string& wlist, SuffixMatchNode& matchNode)
886
{
525✔
887
  vector<string> parts;
525✔
888
  stringtok(parts, wlist, ",; ");
525✔
889
  for (const auto& part : parts) {
525!
890
    matchNode.add(DNSName(part));
×
891
  }
×
892
}
525✔
893

894
static void parseIgnorelistFile(const std::string& fname, SuffixMatchNode& matchNode)
895
{
×
896
  string line;
×
897
  std::ifstream ignorelistFileStream(fname);
×
898
  if (!ignorelistFileStream) {
×
899
    throw ArgException(fname + " could not be opened");
×
900
  }
×
901

902
  while (getline(ignorelistFileStream, line)) {
×
903
    boost::trim(line);
×
904

905
    try {
×
906
      matchNode.add(DNSName(line));
×
907
    }
×
908
    catch (const std::exception& e) {
×
909
      SLOG(g_log << Logger::Warning << "Ignoring line of ignorelist due to an error: " << e.what() << endl,
×
910
           g_slog->withName("config")->error(Logr::Warning, e.what(), "Ignoring line of ignorelist due to an error", "exception", Logging::Loggable("std::exception")));
×
911
    }
×
912
  }
×
913
}
×
914

915
static void setupNODGlobal()
916
{
175✔
917
  // Setup NOD subsystem
918
  g_nodEnabled = ::arg().mustDo("new-domain-tracking");
175✔
919
  g_nodLookupDomain = DNSName(::arg()["new-domain-lookup"]);
175✔
920
  g_nodLog = ::arg().mustDo("new-domain-log");
175✔
921
  parseIgnorelist(::arg()["new-domain-whitelist"], g_nodDomainWL);
175✔
922
  parseIgnorelist(::arg()["new-domain-ignore-list"], g_nodDomainWL);
175✔
923
  if (!::arg().isEmpty("new-domain-ignore-list-file")) {
175!
924
    parseIgnorelistFile(::arg()["new-domain-ignore-list-file"], g_nodDomainWL);
×
925
  }
×
926

927
  // Setup Unique DNS Response subsystem
928
  g_udrEnabled = ::arg().mustDo("unique-response-tracking");
175✔
929
  g_udrLog = ::arg().mustDo("unique-response-log");
175✔
930
  g_nod_pbtag = ::arg()["new-domain-pb-tag"];
175✔
931
  g_udr_pbtag = ::arg()["unique-response-pb-tag"];
175✔
932
  parseIgnorelist(::arg()["unique-response-ignore-list"], g_udrDomainWL);
175✔
933
  if (!::arg().isEmpty("unique-response-ignore-list-file")) {
175!
934
    parseIgnorelistFile(::arg()["unique-response-ignore-list-file"], g_udrDomainWL);
×
935
  }
×
936
}
175✔
937
#endif /* NOD_ENABLED */
938

939
static void daemonize(Logr::log_t log)
940
{
×
941
  if (auto pid = fork(); pid != 0) {
×
942
    if (pid < 0) {
×
943
      int err = errno;
×
944
      SLOG(g_log << Logger::Critical << "Fork failed: " << stringerror(err) << endl,
×
945
           log->error(Logr::Critical, err, "Fork failed"));
×
946
      exit(1); // NOLINT(concurrency-mt-unsafe)
×
947
    }
×
948
    exit(0); // NOLINT(concurrency-mt-unsafe)
×
949
  }
×
950

951
  setsid();
×
952

953
  int devNull = open("/dev/null", O_RDWR); /* open stdin */
×
954
  if (devNull < 0) {
×
955
    int err = errno;
×
956
    SLOG(g_log << Logger::Critical << "Unable to open /dev/null: " << stringerror(err) << endl,
×
957
         log->error(Logr::Critical, err, "Unable to open /dev/null"));
×
958
  }
×
959
  else {
×
960
    dup2(devNull, 0); /* stdin */
×
961
    dup2(devNull, 1); /* stderr */
×
962
    dup2(devNull, 2); /* stderr */
×
963
    close(devNull);
×
964
  }
×
965
}
×
966

967
static void termIntHandler([[maybe_unused]] int arg)
968
{
×
969
  _exit(1);
×
970
}
×
971

972
static void usr1Handler([[maybe_unused]] int arg)
973
{
45✔
974
  statsWanted = true;
45✔
975
}
45✔
976

977
static void usr2Handler([[maybe_unused]] int arg)
978
{
×
979
  g_quiet = !g_quiet;
×
980
  SyncRes::setDefaultLogMode(g_quiet ? SyncRes::LogNone : SyncRes::Log);
×
981
  ::arg().set("quiet") = g_quiet ? "yes" : "no";
×
982
}
×
983

984
static void checkLinuxIPv6Limits([[maybe_unused]] Logr::log_t log)
985
{
175✔
986
#ifdef __linux__
175✔
987
  string line;
175✔
988
  if (readFileIfThere("/proc/sys/net/ipv6/route/max_size", &line)) {
175!
989
    int lim = std::stoi(line);
175✔
990
    if (lim < 16384) {
175!
991
      SLOG(g_log << Logger::Error << "If using IPv6, please raise sysctl net.ipv6.route.max_size, currently set to " << lim << " which is < 16384" << endl,
×
992
           log->info(Logr::Error, "If using IPv6, please raise sysctl net.ipv6.route.max_size to a size >= 16384", "current", Logging::Loggable(lim)));
×
993
    }
×
994
  }
175✔
995
#endif
175✔
996
}
175✔
997

998
static void checkOrFixLinuxMapCountLimits([[maybe_unused]] Logr::log_t log)
999
{
175✔
1000
#ifdef __linux__
175✔
1001
  string line;
175✔
1002
  if (readFileIfThere("/proc/sys/vm/max_map_count", &line)) {
175!
1003
    auto lim = std::stoull(line);
175✔
1004
    // mthread stack use 3 maps per stack (2 guard pages + stack itself). Multiple by 4 for extra allowance.
1005
    // Also add 2 for handler and task threads.
1006
    auto workers = RecThreadInfo::numTCPWorkers() + RecThreadInfo::numUDPWorkers() + 2;
175✔
1007
    auto mapsNeeded = 4ULL * g_maxMThreads * workers;
175✔
1008
    if (lim < mapsNeeded) {
175!
1009
      g_maxMThreads = static_cast<unsigned int>(lim / (4ULL * workers));
×
1010
      SLOG(g_log << Logger::Error << "sysctl vm.max_map_count= <" << mapsNeeded << ", this may cause 'bad_alloc' exceptions; adjusting max-mthreads to " << g_maxMThreads << endl,
×
1011
           log->info(Logr::Error, "sysctl vm.max_map_count < mapsNeeded, this may cause 'bad_alloc' exceptions, adjusting max-mthreads",
×
1012
                     "vm.max_map_count", Logging::Loggable(lim), "mapsNeeded", Logging::Loggable(mapsNeeded),
×
1013
                     "max-mthreads", Logging::Loggable(g_maxMThreads)));
×
1014
    }
×
1015
  }
175✔
1016
#endif
175✔
1017
}
175✔
1018

1019
static void checkOrFixFDS(unsigned int listeningSockets, Logr::log_t log)
1020
{
175✔
1021
  const auto availFDs = getFilenumLimit();
175✔
1022
  // Posix threads
1023
  const auto threads = RecThreadInfo::numRecursorThreads();
175✔
1024
  // We do not count the handler and task threads, they do not spawn many mthreads at once
1025
  const auto workers = RecThreadInfo::numUDPWorkers() + RecThreadInfo::numTCPWorkers();
175✔
1026

1027
  // Static part: the FDs from the start, pipes, controlsocket, web socket, listen sockets
1028
  unsigned int staticPart = 25; // general  allowance, including control socket, web, snmp
175✔
1029
  // Handler thread gets one pipe, the others all of them
1030
  staticPart += 2 + (threads - 1) * (sizeof(RecThreadInfo::ThreadPipeSet) / sizeof(int)); // number of fd's in ThreadPipeSet
175✔
1031
  // listen sockets
1032
  staticPart += listeningSockets;
175✔
1033
  // Another fd per thread for poll/kqueue
1034
  staticPart += threads;
175✔
1035
  // Incoming TCP, connections are shared by threads and are kept open for a while
1036
  staticPart += g_maxTCPClients;
175✔
1037

1038
  // Dynamic parts per worker
1039
  // Each mthread uses one fd for either outgoing UDP or outgoing TCP (but not simultaneously)
1040
  unsigned int perWorker = g_maxMThreads;
175✔
1041
  // plus each worker thread can have a number of idle outgoing TCP connections
1042
  perWorker += TCPOutConnectionManager::s_maxIdlePerThread;
175✔
1043

1044
  auto wantFDs = staticPart + workers * perWorker;
175✔
1045

1046
  if (wantFDs > availFDs) {
175!
1047
    unsigned int hardlimit = getFilenumLimit(true);
×
1048
    if (staticPart >= hardlimit) {
×
1049
      log->info(Logr::Critical, "Number of available filedescriptors is lower than the minimum needed",
×
1050
                "hardlimit", Logging::Loggable(hardlimit), "minimum", Logging::Loggable(staticPart));
×
1051
      _exit(1);
×
1052
    }
×
1053
    if (hardlimit >= wantFDs) {
×
1054
      setFilenumLimit(wantFDs);
×
1055
      log->info(Logr::Warning, "Raised soft limit on number of filedescriptors to match max-mthreads and threads settings", "limit", Logging::Loggable(wantFDs));
×
1056
    }
×
1057
    else {
×
1058
      auto newval = (hardlimit - staticPart) / workers;
×
1059
      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));
×
1060
      g_maxMThreads = newval;
×
1061
      setFilenumLimit(hardlimit);
×
1062
    }
×
1063
  }
×
1064
}
175✔
1065

1066
#ifdef HAVE_SYSTEMD
1067
static void loggerSDBackend(const Logging::Entry& entry)
1068
{
×
1069
  static const set<std::string, CIStringComparePOSIX> special = {
×
1070
    "message",
×
1071
    "message_id",
×
1072
    "priority",
×
1073
    "code_file",
×
1074
    "code_line",
×
1075
    "code_func",
×
1076
    "errno",
×
1077
    "invocation_id",
×
1078
    "user_invocation_id",
×
1079
    "syslog_facility",
×
1080
    "syslog_identifier",
×
1081
    "syslog_pid",
×
1082
    "syslog_timestamp",
×
1083
    "syslog_raw",
×
1084
    "documentation",
×
1085
    "tid",
×
1086
    "unit",
×
1087
    "user_unit",
×
1088
    "object_pid"};
×
1089

1090
  // First map SL priority to syslog's Urgency
1091
  Logger::Urgency urgency = entry.d_priority != 0 ? Logger::Urgency(entry.d_priority) : Logger::Info;
×
1092
  if (urgency > s_logUrgency) {
×
1093
    // We do not log anything if the Urgency of the message is lower than the requested loglevel.
1094
    // Not that lower Urgency means higher number.
1095
    return;
×
1096
  }
×
1097
  // We need to keep the string in mem until sd_journal_sendv has ben called
1098
  vector<string> strings;
×
1099
  auto appendKeyAndVal = [&strings](const string& key, const string& value) {
×
1100
    strings.emplace_back(key + "=" + value);
×
1101
  };
×
1102
  appendKeyAndVal("MESSAGE", entry.message);
×
1103
  if (entry.error) {
×
1104
    appendKeyAndVal("ERROR", entry.error.get());
×
1105
  }
×
1106
  appendKeyAndVal("LEVEL", std::to_string(entry.level));
×
1107
  appendKeyAndVal("PRIORITY", std::to_string(entry.d_priority));
×
1108
  if (entry.name) {
×
1109
    appendKeyAndVal("SUBSYSTEM", entry.name.get());
×
1110
  }
×
1111
  std::array<char, 64> timebuf{};
×
1112
  appendKeyAndVal("TIMESTAMP", Logging::toTimestampStringMilli(entry.d_timestamp, timebuf));
×
1113
  for (const auto& value : entry.values) {
×
1114
    if (value.first.at(0) == '_' || special.count(value.first) != 0) {
×
1115
      string key{"PDNS"};
×
1116
      key.append(value.first);
×
1117
      appendKeyAndVal(toUpper(key), value.second);
×
1118
    }
×
1119
    else {
×
1120
      appendKeyAndVal(toUpper(value.first), value.second);
×
1121
    }
×
1122
  }
×
1123
  // Thread id filled in by backend, since the SL code does not know about RecursorThreads
1124
  // We use the Recursor thread, other threads get id 0. May need to revisit.
1125
  appendKeyAndVal("TID", std::to_string(RecThreadInfo::thread_local_id()));
×
1126

1127
  vector<iovec> iov;
×
1128
  iov.reserve(strings.size());
×
1129
  for (const auto& str : strings) {
×
1130
    // iovec has no 2 arg constructor, so make it explicit
1131
    iov.emplace_back(iovec{const_cast<void*>(reinterpret_cast<const void*>(str.data())), str.size()}); // NOLINT: it's the API
×
1132
  }
×
1133
  sd_journal_sendv(iov.data(), static_cast<int>(iov.size()));
×
1134
}
×
1135
#endif
1136

1137
static void loggerJSONBackend(const Logging::Entry& entry)
1138
{
×
1139
  // First map SL priority to syslog's Urgency
1140
  Logger::Urgency urg = entry.d_priority != 0 ? Logger::Urgency(entry.d_priority) : Logger::Info;
×
1141
  if (urg > s_logUrgency) {
×
1142
    // We do not log anything if the Urgency of the message is lower than the requested loglevel.
1143
    // Not that lower Urgency means higher number.
1144
    return;
×
1145
  }
×
1146

1147
  std::array<char, 64> timebuf{};
×
1148
  json11::Json::object json = {
×
1149
    {"msg", entry.message},
×
1150
    {"level", std::to_string(entry.level)},
×
1151
    // Thread id filled in by backend, since the SL code does not know about RecursorThreads
1152
    // We use the Recursor thread, other threads get id 0. May need to revisit.
1153
    {"tid", std::to_string(RecThreadInfo::thread_local_id())},
×
1154
    {"ts", Logging::toTimestampStringMilli(entry.d_timestamp, timebuf)},
×
1155
  };
×
1156

1157
  if (entry.error) {
×
1158
    json.emplace("error", entry.error.get());
×
1159
  }
×
1160

1161
  if (entry.name) {
×
1162
    json.emplace("subsystem", entry.name.get());
×
1163
  }
×
1164

1165
  if (entry.d_priority != 0) {
×
1166
    json.emplace("priority", std::to_string(entry.d_priority));
×
1167
  }
×
1168

1169
  for (auto const& value : entry.values) {
×
1170
    json.emplace(value.first, value.second);
×
1171
  }
×
1172

1173
  static thread_local std::string out;
×
1174
  out.clear();
×
1175
  json11::Json doc(std::move(json));
×
1176
  doc.dump(out);
×
1177
  cerr << out << endl;
×
1178
}
×
1179

1180
static void loggerBackend(const Logging::Entry& entry)
1181
{
7,910✔
1182
  static thread_local std::stringstream buf;
7,910✔
1183

1184
  // First map SL priority to syslog's Urgency
1185
  Logger::Urgency urg = entry.d_priority != 0 ? Logger::Urgency(entry.d_priority) : Logger::Info;
7,910✔
1186
  if (urg > s_logUrgency) {
7,910✔
1187
    // We do not log anything if the Urgency of the message is lower than the requested loglevel.
1188
    // Not that lower Urgency means higher number.
1189
    return;
230✔
1190
  }
230✔
1191
  buf.str("");
7,680✔
1192
  buf << "msg=" << std::quoted(entry.message);
7,680✔
1193
  if (entry.error) {
7,680✔
1194
    buf << " error=" << std::quoted(entry.error.get());
247✔
1195
  }
247✔
1196

1197
  if (entry.name) {
7,680✔
1198
    buf << " subsystem=" << std::quoted(entry.name.get());
7,668✔
1199
  }
7,668✔
1200
  buf << " level=" << std::quoted(std::to_string(entry.level));
7,680✔
1201
  if (entry.d_priority != 0) {
7,680✔
1202
    buf << " prio=" << std::quoted(Logr::Logger::toString(entry.d_priority));
7,650✔
1203
  }
7,650✔
1204
  // Thread id filled in by backend, since the SL code does not know about RecursorThreads
1205
  // We use the Recursor thread, other threads get id 0. May need to revisit.
1206
  buf << " tid=" << std::quoted(std::to_string(RecThreadInfo::thread_local_id()));
7,680✔
1207
  std::array<char, 64> timebuf{};
7,680✔
1208
  buf << " ts=" << std::quoted(Logging::toTimestampStringMilli(entry.d_timestamp, timebuf));
7,680✔
1209
  for (auto const& value : entry.values) {
36,142✔
1210
    buf << " ";
36,142✔
1211
    buf << value.first << "=" << std::quoted(value.second);
36,142✔
1212
  }
36,142✔
1213

1214
  g_log << urg << buf.str() << endl;
7,680✔
1215
}
7,680✔
1216

1217
static std::string ratePercentage(uint64_t nom, uint64_t denom)
1218
{
300✔
1219
  if (denom == 0) {
300!
1220
    return "0";
×
1221
  }
×
1222
  std::ostringstream str;
300✔
1223
  str << std::setprecision(2) << std::fixed << 100.0 * static_cast<double>(nom) / static_cast<double>(denom);
300✔
1224
  return str.str();
300✔
1225
}
300✔
1226

1227
static void doStats()
1228
{
60✔
1229
  static time_t lastOutputTime;
60✔
1230
  static uint64_t lastQueryCount;
60✔
1231

1232
  auto cacheHits = g_recCache->getCacheHits();
60✔
1233
  auto cacheMisses = g_recCache->getCacheMisses();
60✔
1234
  auto cacheSize = g_recCache->size();
60✔
1235
  auto rc_stats = g_recCache->stats();
60✔
1236
  auto pc_stats = g_packetCache ? g_packetCache->stats() : std::pair<uint64_t, uint64_t>{0, 0};
60!
1237
  auto rrc = ratePercentage(rc_stats.first, rc_stats.second);
60✔
1238
  auto rpc = ratePercentage(pc_stats.first, pc_stats.second);
60✔
1239
  auto negCacheSize = g_negCache->size();
60✔
1240
  auto taskPushes = getTaskPushes();
60✔
1241
  auto taskExpired = getTaskExpired();
60✔
1242
  auto taskSize = getTaskSize();
60✔
1243
  auto pcSize = g_packetCache ? g_packetCache->size() : 0;
60!
1244
  auto pcHits = g_packetCache ? g_packetCache->getHits() : 0;
60!
1245
  auto pcMisses = g_packetCache ? g_packetCache->getMisses() : 0;
60!
1246

1247
  auto qcounter = g_Counters.sum(rec::Counter::qcounter);
60✔
1248
  auto outqueries = g_Counters.sum(rec::Counter::outqueries);
60✔
1249
  auto throttledqueries = g_Counters.sum(rec::Counter::throttledqueries);
60✔
1250
  auto tcpoutqueries = g_Counters.sum(rec::Counter::tcpoutqueries);
60✔
1251
  auto dotoutqueries = g_Counters.sum(rec::Counter::dotoutqueries);
60✔
1252
  auto outgoingtimeouts = g_Counters.sum(rec::Counter::outgoingtimeouts);
60✔
1253

1254
  auto log = g_slog->withName("stats");
60✔
1255

1256
  if (qcounter > 0) {
60✔
1257
    const string report = "Periodic statistics report";
45✔
1258
    log->info(Logr::Info, report,
45✔
1259
              "questions", Logging::Loggable(qcounter),
45✔
1260
              "cache-entries", Logging::Loggable(cacheSize),
45✔
1261
              "negcache-entries", Logging::Loggable(negCacheSize),
45✔
1262
              "record-cache-hitratio-perc", Logging::Loggable(ratePercentage(cacheHits, cacheHits + cacheMisses)),
45✔
1263
              "record-cache-contended", Logging::Loggable(rc_stats.first),
45✔
1264
              "record-cache-acquired", Logging::Loggable(rc_stats.second),
45✔
1265
              "record-cache-contended-perc", Logging::Loggable(rrc));
45✔
1266
    log->info(Logr::Info, report,
45✔
1267
              "packetcache-contended", Logging::Loggable(pc_stats.first),
45✔
1268
              "packetcache-acquired", Logging::Loggable(pc_stats.second),
45✔
1269
              "packetcache-contended-perc", Logging::Loggable(rpc),
45✔
1270
              "packetcache-entries", Logging::Loggable(pcSize),
45✔
1271
              "packetcache-hitratio-perc", Logging::Loggable(ratePercentage(pcHits, pcHits + pcMisses)));
45✔
1272
    log->info(Logr::Info, report,
45✔
1273
              "throttle-entries", Logging::Loggable(SyncRes::getThrottledServersSize()),
45✔
1274
              "nsspeed-entries", Logging::Loggable(SyncRes::getNSSpeedsSize()),
45✔
1275
              "failed-host-entries", Logging::Loggable(SyncRes::getFailedServersSize()),
45✔
1276
              "edns-entries", Logging::Loggable(SyncRes::getEDNSStatusesSize()),
45✔
1277
              "non-resolving-nameserver-entries", Logging::Loggable(SyncRes::getNonResolvingNSSize()),
45✔
1278
              "saved-parent-ns-sets-entries", Logging::Loggable(SyncRes::getSaveParentsNSSetsSize()));
45✔
1279
    log->info(Logr::Info, report,
45✔
1280
              "throttled-queries-perc", Logging::Loggable(ratePercentage(throttledqueries, outqueries + throttledqueries)),
45✔
1281
              "outqueries", Logging::Loggable(outqueries),
45✔
1282
              "tcp-outqueries", Logging::Loggable(tcpoutqueries),
45✔
1283
              "dot-outqueries", Logging::Loggable(dotoutqueries),
45✔
1284
              "idle-tcpout-connections", Logging::Loggable(getCurrentIdleTCPConnections()),
45✔
1285
              "concurrent-queries", Logging::Loggable(broadcastAccFunction<uint64_t>(pleaseGetConcurrentQueries)),
45✔
1286
              "outgoing-timeouts", Logging::Loggable(outgoingtimeouts),
45✔
1287
              "outqueries-per-query-perc", Logging::Loggable(ratePercentage(outqueries, qcounter)));
45✔
1288
    log->info(Logr::Info, report,
45✔
1289
              "taskqueue-pushed", Logging::Loggable(taskPushes),
45✔
1290
              "taskqueue-expired", Logging::Loggable(taskExpired),
45✔
1291
              "taskqueue-size", Logging::Loggable(taskSize));
45✔
1292

1293
    size_t idx = 0;
45✔
1294
    for (const auto& threadInfo : RecThreadInfo::infos()) {
333✔
1295
      if (threadInfo.isWorker()) {
333✔
1296
        log->info(Logr::Info, "Queries handled by thread", "thread", Logging::Loggable(idx), "tname", Logging::Loggable(threadInfo.getName()), "count", Logging::Loggable(threadInfo.getNumberOfDistributedQueries()));
207✔
1297
        ++idx;
207✔
1298
      }
207✔
1299
    }
333✔
1300
    time_t now = time(nullptr);
45✔
1301
    if (lastOutputTime != 0 && lastQueryCount != 0 && now != lastOutputTime) {
45!
1302
      log->info(Logr::Info, "Periodic QPS report", "qps", Logging::Loggable((qcounter - lastQueryCount) / (now - lastOutputTime)),
30✔
1303
                "averagedOver", Logging::Loggable(now - lastOutputTime));
30✔
1304
    }
30✔
1305
    lastOutputTime = now;
45✔
1306
    lastQueryCount = qcounter;
45✔
1307
  }
45✔
1308
  else if (statsWanted) {
15!
1309
    log->info(Logr::Notice, "No stats yet");
×
1310
  }
×
1311

1312
  statsWanted = false;
60✔
1313
}
60✔
1314

1315
static std::shared_ptr<NetmaskGroup> parseACL(const std::string& aclFile, const std::string& aclSetting, Logr::log_t log)
1316
{
352✔
1317
  auto result = std::make_shared<NetmaskGroup>();
352✔
1318

1319
  const string file = ::arg()[aclFile];
352✔
1320

1321
  if (!file.empty()) {
352!
1322
    if (boost::ends_with(file, ".yml")) {
×
1323
      ::rust::vec<::rust::string> vec;
×
1324
      pdns::settings::rec::readYamlAllowFromFile(file, vec, log);
×
1325
      for (const auto& subnet : vec) {
×
1326
        result->addMask(string(subnet));
×
1327
      }
×
1328
    }
×
1329
    else {
×
1330
      string line;
×
1331
      ifstream ifs(file);
×
1332
      if (!ifs) {
×
1333
        int err = errno;
×
1334
        throw runtime_error("Could not open '" + file + "': " + stringerror(err));
×
1335
      }
×
1336

1337
      while (getline(ifs, line)) {
×
1338
        auto pos = line.find('#');
×
1339
        if (pos != string::npos) {
×
1340
          line.resize(pos);
×
1341
        }
×
1342
        boost::trim(line);
×
1343
        if (line.empty()) {
×
1344
          continue;
×
1345
        }
×
1346

1347
        result->addMask(line);
×
1348
      }
×
1349
    }
×
1350
    SLOG(g_log << Logger::Info << "Done parsing " << result->size() << " " << aclSetting << " ranges from file '" << file << "' - overriding '" << aclSetting << "' setting" << endl,
×
1351
         log->info(Logr::Info, "Done parsing ranges from file, will override setting", "setting", Logging::Loggable(aclSetting),
×
1352
                   "number", Logging::Loggable(result->size()), "file", Logging::Loggable(file)));
×
1353
  }
×
1354
  else if (!::arg()[aclSetting].empty()) {
352✔
1355
    vector<string> ips;
180✔
1356
    stringtok(ips, ::arg()[aclSetting], ", ");
180✔
1357

1358
    for (const auto& address : ips) {
1,470✔
1359
      result->addMask(address);
1,470✔
1360
    }
1,470✔
1361
    if (!g_slogStructured) {
180✔
1362
      g_log << Logger::Info << aclSetting << ": ";
×
1363
      for (auto i = ips.begin(); i != ips.end(); ++i) {
×
1364
        if (i != ips.begin()) {
×
1365
          g_log << Logger::Info << ", ";
×
1366
        }
×
1367
        g_log << Logger::Info << *i;
×
1368
      }
×
1369
      g_log << Logger::Info << endl;
×
1370
    }
×
1371
    else {
180✔
1372
      log->info(Logr::Info, "Setting access control", "acl", Logging::Loggable(aclSetting), "addresses", Logging::IterLoggable(ips.begin(), ips.end()));
180✔
1373
    }
180✔
1374
  }
180✔
1375

1376
  return result;
352✔
1377
}
352✔
1378

1379
static void* pleaseSupplantAllowFrom(std::shared_ptr<NetmaskGroup> nmgroup)
1380
{
180✔
1381
  t_allowFrom = std::move(nmgroup);
180✔
1382
  return nullptr;
180✔
1383
}
180✔
1384

1385
static void* pleaseSupplantAllowNotifyFrom(std::shared_ptr<NetmaskGroup> nmgroup)
1386
{
180✔
1387
  t_allowNotifyFrom = std::move(nmgroup);
180✔
1388
  return nullptr;
180✔
1389
}
180✔
1390

1391
void* pleaseSupplantAllowNotifyFor(std::shared_ptr<notifyset_t> allowNotifyFor)
1392
{
50✔
1393
  t_allowNotifyFor = std::move(allowNotifyFor);
50✔
1394
  return nullptr;
50✔
1395
}
50✔
1396

1397
static void* pleaseSupplantProxyProtocolSettings(std::shared_ptr<NetmaskGroup> acl, std::shared_ptr<std::set<ComboAddress>> except)
1398
{
180✔
1399
  t_proxyProtocolACL = std::move(acl);
180✔
1400
  t_proxyProtocolExceptions = std::move(except);
180✔
1401
  return nullptr;
180✔
1402
}
180✔
1403

1404
void parseACLs()
1405
{
176✔
1406
  auto log = g_slog->withName("config");
176✔
1407

1408
  static bool l_initialized;
176✔
1409
  const std::array<string, 6> aclNames = {
176✔
1410
    "allow-from-file",
176✔
1411
    "allow-from",
176✔
1412
    "allow-notify-from-file",
176✔
1413
    "allow-notify-from",
176✔
1414
    "proxy-protocol-from",
176✔
1415
    "proxy-protocol-exceptions"};
176✔
1416

1417
  if (l_initialized) { // only reload configuration file on second call
176✔
1418

1419
    string configName = ::arg()["config-dir"] + "/recursor";
1✔
1420
    if (!::arg()["config-name"].empty()) {
1!
1421
      configName = ::arg()["config-dir"] + "/recursor-" + ::arg()["config-name"];
×
1422
    }
×
1423
    cleanSlashes(configName);
1✔
1424

1425
    if (g_yamlSettings) {
1!
1426
      configName += g_yamlSettingsSuffix;
×
1427
      string msg;
×
1428
      pdns::rust::settings::rec::Recursorsettings settings;
×
1429
      // XXX Does ::arg()["include-dir"] have the right value, i.e. potentially overriden by command line?
1430
      auto yamlstatus = pdns::settings::rec::readYamlSettings(configName, ::arg()["include-dir"], settings, msg, log);
×
1431

1432
      switch (yamlstatus) {
×
1433
      case pdns::settings::rec::YamlSettingsStatus::CannotOpen:
×
1434
        throw runtime_error("Unable to open '" + configName + "': " + msg);
×
1435
        break;
×
1436
      case pdns::settings::rec::YamlSettingsStatus::PresentButFailed:
×
1437
        throw runtime_error("Error processing '" + configName + "': " + msg);
×
1438
        break;
×
1439
      case pdns::settings::rec::YamlSettingsStatus::OK:
×
1440
        pdns::settings::rec::processAPIDir(arg()["include-dir"], settings, log);
×
1441
        // Does *not* set include-dir
1442
        pdns::settings::rec::setArgsForACLRelatedSettings(settings);
×
1443
        break;
×
1444
      }
×
1445
    }
×
1446
    else {
1✔
1447
      configName += ".conf";
1✔
1448
      if (!::arg().preParseFile(configName, "allow-from-file")) {
1!
1449
        throw runtime_error("Unable to re-parse configuration file '" + configName + "'");
×
1450
      }
×
1451
      ::arg().preParseFile(configName, "allow-from", LOCAL_NETS);
1✔
1452

1453
      if (!::arg().preParseFile(configName, "allow-notify-from-file")) {
1!
1454
        throw runtime_error("Unable to re-parse configuration file '" + configName + "'");
×
1455
      }
×
1456
      ::arg().preParseFile(configName, "allow-notify-from");
1✔
1457
      ::arg().preParseFile(configName, "proxy-protocol-from");
1✔
1458
      ::arg().preParseFile(configName, "proxy-protocol-exceptions");
1✔
1459

1460
      ::arg().preParseFile(configName, "include-dir");
1✔
1461
      ::arg().preParse(g_argc, g_argv, "include-dir");
1✔
1462

1463
      // then process includes
1464
      std::vector<std::string> extraConfigs;
1✔
1465
      ::arg().gatherIncludes(::arg()["include-dir"], ".conf", extraConfigs);
1✔
1466

1467
      for (const std::string& fileName : extraConfigs) {
1!
1468
        for (const auto& aclName : aclNames) {
×
1469
          if (!::arg().preParseFile(fileName, aclName, ::arg()[aclName])) {
×
1470
            throw runtime_error("Unable to re-parse configuration file include '" + fileName + "'");
×
1471
          }
×
1472
        }
×
1473
      }
×
1474
    }
1✔
1475
  }
1✔
1476
  // Process command line args potentially overriding settings read from file
1477
  for (const auto& aclName : aclNames) {
1,056✔
1478
    ::arg().preParse(g_argc, g_argv, aclName);
1,056✔
1479
  }
1,056✔
1480

1481
  auto allowFrom = parseACL("allow-from-file", "allow-from", log);
176✔
1482

1483
  if (allowFrom->empty()) {
176!
1484
    if (::arg()["local-address"] != "127.0.0.1" && ::arg().asNum("local-port") == 53) {
×
1485
      SLOG(g_log << Logger::Warning << "WARNING: Allowing queries from all IP addresses - this can be a security risk!" << endl,
×
1486
           log->info(Logr::Warning, "WARNING: Allowing queries from all IP addresses - this can be a security risk!"));
×
1487
    }
×
1488
    allowFrom = nullptr;
×
1489
  }
×
1490

1491
  *g_initialAllowFrom.lock() = allowFrom;
176✔
1492
  // coverity[copy_constructor_call] maybe this can be avoided, but be careful as pointers get passed to other threads
1493
  broadcastFunction([=] { return pleaseSupplantAllowFrom(allowFrom); });
180✔
1494

1495
  auto allowNotifyFrom = parseACL("allow-notify-from-file", "allow-notify-from", log);
176✔
1496

1497
  *g_initialAllowNotifyFrom.lock() = allowNotifyFrom;
176✔
1498
  // coverity[copy_constructor_call] maybe this can be avoided, but be careful as pointers get passed to other threads
1499
  broadcastFunction([=] { return pleaseSupplantAllowNotifyFrom(allowNotifyFrom); });
180✔
1500

1501
  std::shared_ptr<NetmaskGroup> proxyProtocolACL;
176✔
1502
  std::shared_ptr<std::set<ComboAddress>> proxyProtocolExceptions;
176✔
1503
  if (!::arg()["proxy-protocol-from"].empty()) {
176✔
1504
    proxyProtocolACL = std::make_shared<NetmaskGroup>();
11✔
1505
    proxyProtocolACL->toMasks(::arg()["proxy-protocol-from"]);
11✔
1506

1507
    std::vector<std::string> vec;
11✔
1508
    stringtok(vec, ::arg()["proxy-protocol-exceptions"], ", ");
11✔
1509
    if (!vec.empty()) {
11✔
1510
      proxyProtocolExceptions = std::make_shared<std::set<ComboAddress>>();
1✔
1511
      for (const auto& sockAddrStr : vec) {
1✔
1512
        ComboAddress sockAddr(sockAddrStr, 53);
1✔
1513
        proxyProtocolExceptions->emplace(sockAddr);
1✔
1514
      }
1✔
1515
    }
1✔
1516
  }
11✔
1517
  g_initialProxyProtocolACL = proxyProtocolACL;
176✔
1518
  g_initialProxyProtocolExceptions = proxyProtocolExceptions;
176✔
1519

1520
  // coverity[copy_constructor_call] maybe this can be avoided, but be careful as pointers get passed to other threads
1521
  broadcastFunction([=] { return pleaseSupplantProxyProtocolSettings(proxyProtocolACL, proxyProtocolExceptions); });
180✔
1522

1523
  l_initialized = true;
176✔
1524
}
176✔
1525

1526
static std::mutex pipeBroadCastMutex{};
1527

1528
void broadcastFunction(const pipefunc_t& func)
1529
{
565✔
1530
  // we do not want the handler and web code to use pipes simultaneously
1531
  std::scoped_lock lock(pipeBroadCastMutex);
565✔
1532

1533
  /* This function might be called by the worker with t_id not inited during startup
1534
     for the initialization of ACLs and domain maps. After that it should only
1535
     be called by the handler. */
1536

1537
  if (RecThreadInfo::infos().empty() && !RecThreadInfo::is_thread_inited()) {
565!
1538
    /* the handler and  distributors will call themselves below, but
1539
       during startup we get called while g_threadInfos has not been
1540
       populated yet to update the ACL or domain maps, so we need to
1541
       handle that case.
1542
    */
1543
    func();
542✔
1544
  }
542✔
1545

1546
  unsigned int thread = 0;
565✔
1547
  for (const auto& threadInfo : RecThreadInfo::infos()) {
565✔
1548
    if (thread++ == RecThreadInfo::thread_local_id()) {
115✔
1549
      func(); // don't write to ourselves!
23✔
1550
      continue;
23✔
1551
    }
23✔
1552

1553
    ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: manual ownership handling
92✔
1554
    tmsg->func = func;
92✔
1555
    tmsg->wantAnswer = true;
92✔
1556
    if (write(threadInfo.getPipes().writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // NOLINT: sizeof correct
92!
1557
      delete tmsg; // NOLINT: manual ownership handling
×
1558

1559
      unixDie("write to thread pipe returned wrong size or error");
×
1560
    }
×
1561

1562
    string* resp = nullptr;
92✔
1563
    if (read(threadInfo.getPipes().readFromThread, &resp, sizeof(resp)) != sizeof(resp)) { // NOLINT: sizeof correct
92!
1564
      unixDie("read from thread pipe returned wrong size or error");
×
1565
    }
×
1566

1567
    if (resp != nullptr) {
92!
1568
      delete resp; // NOLINT: manual ownership handling
×
1569
      resp = nullptr;
×
1570
    }
×
1571
    // coverity[leaked_storage]
1572
  }
92✔
1573
}
565✔
1574

1575
template <class T>
1576
void* voider(const std::function<T*()>& func)
1577
{
4,276✔
1578
  return func();
4,276✔
1579
}
4,276✔
1580

1581
static vector<ComboAddress>& operator+=(vector<ComboAddress>& lhs, const vector<ComboAddress>& rhs)
1582
{
16✔
1583
  lhs.insert(lhs.end(), rhs.begin(), rhs.end());
16✔
1584
  return lhs;
16✔
1585
}
16✔
1586

1587
static vector<pair<DNSName, uint16_t>>& operator+=(vector<pair<DNSName, uint16_t>>& lhs, const vector<pair<DNSName, uint16_t>>& rhs)
1588
{
×
1589
  lhs.insert(lhs.end(), rhs.begin(), rhs.end());
×
1590
  return lhs;
×
1591
}
×
1592

1593
static ProxyMappingStats_t& operator+=(ProxyMappingStats_t& lhs, const ProxyMappingStats_t& rhs)
1594
{
380✔
1595
  for (const auto& [key, entry] : rhs) {
380!
1596
    lhs[key].netmaskMatches += entry.netmaskMatches;
×
1597
    lhs[key].suffixMatches += entry.suffixMatches;
×
1598
  }
×
1599
  return lhs;
380✔
1600
}
380✔
1601

1602
static RemoteLoggerStats_t& operator+=(RemoteLoggerStats_t& lhs, const RemoteLoggerStats_t& rhs)
1603
{
1,520✔
1604
  for (const auto& [key, entry] : rhs) {
1,520!
1605
    lhs[key] += entry;
×
1606
  }
×
1607
  return lhs;
1,520✔
1608
}
1,520✔
1609

1610
// This function should only be called by the handler and web thread to gather metrics, wipe the
1611
// cache, reload the Lua script (not the Lua config) or change the current trace regex, and by the
1612
// SNMP thread to gather metrics.  Note that this currently skips the handler, but includes the
1613
// taskThread(s).
1614
template <class T>
1615
T broadcastAccFunction(const std::function<T*()>& func)
1616
{
925✔
1617
  if (RecThreadInfo::thread_local_id() != 0) {
925!
1618
    g_slog->withName("runtime")->info(Logr::Critical, "broadcastAccFunction has been called by a worker"); // tid will be added
×
1619
    _exit(1);
×
1620
  }
×
1621

1622
  // we do not want the handler and web code to use pipes simultaneously
1623
  std::scoped_lock lock(pipeBroadCastMutex);
925✔
1624

1625
  unsigned int thread = 0;
925✔
1626
  T ret = T();
925✔
1627
  for (const auto& threadInfo : RecThreadInfo::infos()) {
5,201!
1628
    if (thread++ == RecThreadInfo::thread_local_id()) {
5,201!
1629
      continue;
925✔
1630
    }
925✔
1631

1632
    const auto& tps = threadInfo.getPipes();
4,276✔
1633
    ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: manual ownership handling
4,276✔
1634
    tmsg->func = [func] { return voider<T>(func); };
4,276✔
1635
    tmsg->wantAnswer = true;
4,276✔
1636

1637
    if (write(tps.writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // NOLINT:: sizeof correct
4,276!
1638
      delete tmsg; // NOLINT: manual ownership handling
×
1639
      unixDie("write to thread pipe returned wrong size or error");
×
1640
    }
×
1641

1642
    T* resp = nullptr;
4,276✔
1643
    if (read(tps.readFromThread, &resp, sizeof(resp)) != sizeof(resp)) // NOLINT: sizeof correct
4,276!
1644
      unixDie("read from thread pipe returned wrong size or error");
×
1645

1646
    if (resp) {
4,276!
1647
      ret += *resp;
4,276✔
1648
      delete resp; // NOLINT: manual ownership handling
4,276✔
1649
      resp = nullptr;
4,276✔
1650
    }
4,276✔
1651
    // coverity[leaked_storage]
1652
  }
4,276✔
1653
  return ret;
925✔
1654
}
925✔
1655

1656
template string broadcastAccFunction(const std::function<string*()>& fun); // explicit instantiation
1657
template RecursorControlChannel::Answer broadcastAccFunction(const std::function<RecursorControlChannel::Answer*()>& fun); // explicit instantiation
1658
template uint64_t broadcastAccFunction(const std::function<uint64_t*()>& fun); // explicit instantiation
1659
template vector<ComboAddress> broadcastAccFunction(const std::function<vector<ComboAddress>*()>& fun); // explicit instantiation
1660
template vector<pair<DNSName, uint16_t>> broadcastAccFunction(const std::function<vector<pair<DNSName, uint16_t>>*()>& fun); // explicit instantiation
1661
template ThreadTimes broadcastAccFunction(const std::function<ThreadTimes*()>& fun);
1662
template ProxyMappingStats_t broadcastAccFunction(const std::function<ProxyMappingStats_t*()>& fun);
1663
template RemoteLoggerStats_t broadcastAccFunction(const std::function<RemoteLoggerStats_t*()>& fun);
1664

1665
static int initNet(Logr::log_t log)
1666
{
175✔
1667
  checkLinuxIPv6Limits(log);
175✔
1668
  try {
175✔
1669
    pdns::parseQueryLocalAddress(::arg()["query-local-address"]);
175✔
1670
  }
175✔
1671
  catch (std::exception& e) {
175✔
1672
    SLOG(g_log << Logger::Error << "Assigning local query addresses: " << e.what(),
×
1673
         log->error(Logr::Error, e.what(), "Unable to assign local query address"));
×
1674
    return 99;
×
1675
  }
×
1676

1677
  if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET)) {
175✔
1678
    SyncRes::s_doIPv4 = true;
173✔
1679
    SLOG(g_log << Logger::Warning << "Enabling IPv4 transport for outgoing queries" << endl,
173✔
1680
         log->info(Logr::Notice, "Enabling IPv4 transport for outgoing queries"));
173✔
1681
  }
173✔
1682
  else {
2✔
1683
    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,
2✔
1684
         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"));
2✔
1685
  }
2✔
1686

1687
  if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET6)) {
175✔
1688
    SyncRes::s_doIPv6 = true;
2✔
1689
    SLOG(g_log << Logger::Warning << "Enabling IPv6 transport for outgoing queries" << endl,
2✔
1690
         log->info(Logr::Notice, "Enabling IPv6 transport for outgoing queries"));
2✔
1691
  }
2✔
1692
  else {
173✔
1693
    SLOG(g_log << Logger::Warning << "NOT using IPv6 for outgoing queries - add an IPv6 address (like '::') to query-local-address to enable" << endl,
173✔
1694
         log->info(Logr::Warning, "NOT using IPv6 for outgoing queries - add an IPv6 address (like '::') to query-local-address to enable"));
173✔
1695
  }
173✔
1696

1697
  if (!SyncRes::s_doIPv6 && !SyncRes::s_doIPv4) {
175!
1698
    SLOG(g_log << Logger::Error << "No outgoing addresses configured! Can not continue" << endl,
×
1699
         log->info(Logr::Error, "No outgoing addresses configured! Can not continue"));
×
1700
    return 99;
×
1701
  }
×
1702
  return 0;
175✔
1703
}
175✔
1704

1705
static int initDNSSEC(Logr::log_t log)
1706
{
175✔
1707
  if (::arg()["dnssec"] == "off") {
175✔
1708
    g_dnssecmode = DNSSECMode::Off;
2✔
1709
  }
2✔
1710
  else if (::arg()["dnssec"] == "process-no-validate") {
173✔
1711
    g_dnssecmode = DNSSECMode::ProcessNoValidate;
1✔
1712
  }
1✔
1713
  else if (::arg()["dnssec"] == "process") {
172✔
1714
    g_dnssecmode = DNSSECMode::Process;
119✔
1715
  }
119✔
1716
  else if (::arg()["dnssec"] == "validate") {
53!
1717
    g_dnssecmode = DNSSECMode::ValidateAll;
53✔
1718
  }
53✔
1719
  else if (::arg()["dnssec"] == "log-fail") {
×
1720
    g_dnssecmode = DNSSECMode::ValidateForLog;
×
1721
  }
×
1722
  else {
×
1723
    SLOG(g_log << Logger::Error << "Unknown DNSSEC mode " << ::arg()["dnssec"] << endl,
×
1724
         log->info(Logr::Error, "Unknown DNSSEC mode", "dnssec", Logging::Loggable(::arg()["dnssec"])));
×
1725
    return 1;
×
1726
  }
×
1727

1728
  {
175✔
1729
    auto value = ::arg().asNum("signature-inception-skew");
175✔
1730
    if (value < 0) {
175!
1731
      log->info(Logr::Error, "A negative value for 'signature-inception-skew' is not allowed");
×
1732
      return 1;
×
1733
    }
×
1734
    g_signatureInceptionSkew = value;
175✔
1735
  }
175✔
1736

1737
  g_dnssecLogBogus = ::arg().mustDo("dnssec-log-bogus");
×
1738
  g_maxNSEC3Iterations = ::arg().asNum("nsec3-max-iterations");
175✔
1739
  g_maxRRSIGsPerRecordToConsider = ::arg().asNum("max-rrsigs-per-record");
175✔
1740
  g_maxNSEC3sPerRecordToConsider = ::arg().asNum("max-nsec3s-per-record");
175✔
1741
  g_maxDNSKEYsToConsider = ::arg().asNum("max-dnskeys");
175✔
1742
  g_maxDSsToConsider = ::arg().asNum("max-ds-per-zone");
175✔
1743

1744
  vector<string> nums;
175✔
1745
  bool automatic = true;
175✔
1746
  if (!::arg()["dnssec-disabled-algorithms"].empty()) {
175!
1747
    automatic = false;
×
1748
    stringtok(nums, ::arg()["dnssec-disabled-algorithms"], ", ");
×
1749
    for (const auto& num : nums) {
×
1750
      DNSCryptoKeyEngine::switchOffAlgorithm(pdns::checked_stoi<unsigned int>(num));
×
1751
    }
×
1752
  }
×
1753
  else {
175✔
1754
    for (auto algo : {DNSSECKeeper::RSASHA1, DNSSECKeeper::RSASHA1NSEC3SHA1}) {
350✔
1755
      if (!DNSCryptoKeyEngine::verifyOne(algo)) {
350!
1756
        DNSCryptoKeyEngine::switchOffAlgorithm(algo);
×
1757
        nums.push_back(std::to_string(algo));
×
1758
      }
×
1759
    }
350✔
1760
  }
175✔
1761
  if (!nums.empty()) {
175!
1762
    if (!g_slogStructured) {
×
1763
      g_log << Logger::Warning << (automatic ? "Automatically" : "Manually") << " disabled DNSSEC algorithms: ";
×
1764
      for (auto i = nums.begin(); i != nums.end(); ++i) {
×
1765
        if (i != nums.begin()) {
×
1766
          g_log << Logger::Warning << ", ";
×
1767
        }
×
1768
        g_log << Logger::Warning << *i;
×
1769
      }
×
1770
      g_log << Logger::Warning << endl;
×
1771
    }
×
1772
    else {
×
1773
      log->info(Logr::Notice, "Disabled DNSSEC algorithms", "automatically", Logging::Loggable(automatic), "algorithms", Logging::IterLoggable(nums.begin(), nums.end()));
×
1774
    }
×
1775
  }
×
1776

1777
  return 0;
175✔
1778
}
175✔
1779

1780
static void initDontQuery(Logr::log_t log)
1781
{
175✔
1782
  if (!::arg()["dont-query"].empty()) {
175✔
1783
    vector<string> ips;
16✔
1784
    stringtok(ips, ::arg()["dont-query"], ", ");
16✔
1785
    ips.emplace_back("0.0.0.0");
16✔
1786
    ips.emplace_back("::");
16✔
1787

1788
    for (const auto& anIP : ips) {
336✔
1789
      SyncRes::addDontQuery(anIP);
336✔
1790
    }
336✔
1791
    if (!g_slogStructured) {
16✔
1792
      g_log << Logger::Warning << "Will not send queries to: ";
×
1793
      for (auto i = ips.begin(); i != ips.end(); ++i) {
×
1794
        if (i != ips.begin()) {
×
1795
          g_log << Logger::Warning << ", ";
×
1796
        }
×
1797
        g_log << Logger::Warning << *i;
×
1798
      }
×
1799
      g_log << Logger::Warning << endl;
×
1800
    }
×
1801
    else {
16✔
1802
      log->info(Logr::Notice, "Will not send queries to", "addresses", Logging::IterLoggable(ips.begin(), ips.end()));
16✔
1803
    }
16✔
1804
  }
16✔
1805
}
175✔
1806

1807
static int initSyncRes(Logr::log_t log)
1808
{
175✔
1809
  SyncRes::s_minimumTTL = ::arg().asNum("minimum-ttl-override");
175✔
1810
  SyncRes::s_minimumECSTTL = ::arg().asNum("ecs-minimum-ttl-override");
175✔
1811
  SyncRes::s_maxnegttl = ::arg().asNum("max-negative-ttl");
175✔
1812
  SyncRes::s_maxbogusttl = ::arg().asNum("max-cache-bogus-ttl");
175✔
1813
  SyncRes::s_maxcachettl = max(::arg().asNum("max-cache-ttl"), 15);
175✔
1814

1815
  SyncRes::s_packetcachettl = ::arg().asNum("packetcache-ttl");
175✔
1816
  // Cap the packetcache-servfail-ttl and packetcache-negative-ttl to packetcache-ttl
1817
  SyncRes::s_packetcacheservfailttl = std::min(static_cast<unsigned int>(::arg().asNum("packetcache-servfail-ttl")), SyncRes::s_packetcachettl);
175✔
1818
  SyncRes::s_packetcachenegativettl = std::min(static_cast<unsigned int>(::arg().asNum("packetcache-negative-ttl")), SyncRes::s_packetcachettl);
175✔
1819

1820
  SyncRes::s_serverdownmaxfails = ::arg().asNum("server-down-max-fails");
175✔
1821
  SyncRes::s_serverdownthrottletime = ::arg().asNum("server-down-throttle-time");
175✔
1822
  SyncRes::s_unthrottle_n = ::arg().asNum("bypass-server-throttling-probability");
175✔
1823
  SyncRes::s_nonresolvingnsmaxfails = ::arg().asNum("non-resolving-ns-max-fails");
175✔
1824
  SyncRes::s_nonresolvingnsthrottletime = ::arg().asNum("non-resolving-ns-throttle-time");
175✔
1825
  SyncRes::s_serverID = ::arg()["server-id"];
175✔
1826
  // This bound is dynamically adjusted in SyncRes, depending on qname minimization being active
1827
  SyncRes::s_maxqperq = ::arg().asNum("max-qperq");
175✔
1828
  SyncRes::s_maxnsperresolve = ::arg().asNum("max-ns-per-resolve");
175✔
1829
  SyncRes::s_maxnsaddressqperq = ::arg().asNum("max-ns-address-qperq");
175✔
1830
  SyncRes::s_maxtotusec = 1000 * ::arg().asNum("max-total-msec");
175✔
1831
  SyncRes::s_maxdepth = ::arg().asNum("max-recursion-depth");
175✔
1832
  SyncRes::s_maxvalidationsperq = ::arg().asNum("max-signature-validations-per-query");
175✔
1833
  SyncRes::s_maxnsec3iterationsperq = ::arg().asNum("max-nsec3-hash-computations-per-query");
175✔
1834
  SyncRes::s_rootNXTrust = ::arg().mustDo("root-nx-trust");
175✔
1835
  SyncRes::s_refresh_ttlperc = ::arg().asNum("refresh-on-ttl-perc");
175✔
1836
  SyncRes::s_locked_ttlperc = ::arg().asNum("record-cache-locked-ttl-perc");
175✔
1837
  RecursorPacketCache::s_refresh_ttlperc = SyncRes::s_refresh_ttlperc;
175✔
1838
  SyncRes::s_tcp_fast_open = ::arg().asNum("tcp-fast-open");
175✔
1839
  SyncRes::s_tcp_fast_open_connect = ::arg().mustDo("tcp-fast-open-connect");
175✔
1840

1841
  SyncRes::s_dot_to_port_853 = ::arg().mustDo("dot-to-port-853");
175✔
1842
  SyncRes::s_event_trace_enabled = ::arg().asNum("event-trace-enabled");
175✔
1843
  SyncRes::s_save_parent_ns_set = ::arg().mustDo("save-parent-ns-set");
175✔
1844
  SyncRes::s_max_busy_dot_probes = ::arg().asNum("max-busy-dot-probes");
175✔
1845
  SyncRes::s_max_CNAMES_followed = ::arg().asNum("max-cnames-followed");
175✔
1846
  {
175✔
1847
    uint64_t sse = ::arg().asNum("serve-stale-extensions");
175✔
1848
    if (sse > std::numeric_limits<uint16_t>::max()) {
175!
1849
      SLOG(g_log << Logger::Error << "Illegal serve-stale-extensions value: " << sse << "; range = 0..65536" << endl,
×
1850
           log->info(Logr::Error, "Illegal serve-stale-extensions value; range = 0..65536", "value", Logging::Loggable(sse)));
×
1851
      return 1;
×
1852
    }
×
1853
    MemRecursorCache::s_maxServedStaleExtensions = sse;
175✔
1854
    NegCache::s_maxServedStaleExtensions = sse;
175✔
1855
  }
175✔
1856
  MemRecursorCache::s_maxRRSetSize = ::arg().asNum("max-rrset-size");
×
1857
  MemRecursorCache::s_limitQTypeAny = ::arg().mustDo("limit-qtype-any");
175✔
1858

1859
  if (SyncRes::s_tcp_fast_open_connect) {
175!
1860
    checkFastOpenSysctl(true, log);
×
1861
    checkTFOconnect(log);
×
1862
  }
×
1863
  SyncRes::s_ecsipv4limit = ::arg().asNum("ecs-ipv4-bits");
175✔
1864
  SyncRes::s_ecsipv6limit = ::arg().asNum("ecs-ipv6-bits");
175✔
1865
  SyncRes::clearECSStats();
175✔
1866
  SyncRes::s_ecsipv4cachelimit = ::arg().asNum("ecs-ipv4-cache-bits");
175✔
1867
  SyncRes::s_ecsipv6cachelimit = ::arg().asNum("ecs-ipv6-cache-bits");
175✔
1868
  SyncRes::s_ecsipv4nevercache = ::arg().mustDo("ecs-ipv4-never-cache");
175✔
1869
  SyncRes::s_ecsipv6nevercache = ::arg().mustDo("ecs-ipv6-never-cache");
175✔
1870
  SyncRes::s_ecscachelimitttl = ::arg().asNum("ecs-cache-limit-ttl");
175✔
1871

1872
  SyncRes::s_qnameminimization = ::arg().mustDo("qname-minimization");
175✔
1873
  SyncRes::s_minimize_one_label = ::arg().asNum("qname-minimize-one-label");
175✔
1874
  SyncRes::s_max_minimize_count = ::arg().asNum("qname-max-minimize-count");
175✔
1875

1876
  SyncRes::s_hardenNXD = SyncRes::HardenNXD::DNSSEC;
175✔
1877
  string value = ::arg()["nothing-below-nxdomain"];
175✔
1878
  if (value == "yes") {
175!
1879
    SyncRes::s_hardenNXD = SyncRes::HardenNXD::Yes;
×
1880
  }
×
1881
  else if (value == "no") {
175!
1882
    SyncRes::s_hardenNXD = SyncRes::HardenNXD::No;
×
1883
  }
×
1884
  else if (value != "dnssec") {
175!
1885
    SLOG(g_log << Logger::Error << "Unknown nothing-below-nxdomain mode: " << value << endl,
×
1886
         log->info(Logr::Error, "Unknown nothing-below-nxdomain mode", "mode", Logging::Loggable(value)));
×
1887
    return 1;
×
1888
  }
×
1889

1890
  if (!::arg().isEmpty("ecs-scope-zero-address")) {
175✔
1891
    ComboAddress scopeZero(::arg()["ecs-scope-zero-address"]);
8✔
1892
    SyncRes::setECSScopeZeroAddress(Netmask(scopeZero, scopeZero.isIPv4() ? 32 : 128));
8✔
1893
  }
8✔
1894
  else {
167✔
1895
    Netmask netmask;
167✔
1896
    bool done = false;
167✔
1897

1898
    auto addr = pdns::getNonAnyQueryLocalAddress(AF_INET);
167✔
1899
    if (addr.sin4.sin_family != 0) {
167!
1900
      netmask = Netmask(addr, 32);
×
1901
      done = true;
×
1902
    }
×
1903
    if (!done) {
167!
1904
      addr = pdns::getNonAnyQueryLocalAddress(AF_INET6);
167✔
1905
      if (addr.sin4.sin_family != 0) {
167✔
1906
        netmask = Netmask(addr, 128);
2✔
1907
        done = true;
2✔
1908
      }
2✔
1909
    }
167✔
1910
    if (!done) {
167✔
1911
      netmask = Netmask(ComboAddress("127.0.0.1"), 32);
165✔
1912
    }
165✔
1913
    SyncRes::setECSScopeZeroAddress(netmask);
167✔
1914
  }
167✔
1915

1916
  SyncRes::parseEDNSSubnetAllowlist(::arg()["edns-subnet-whitelist"]);
175✔
1917
  SyncRes::parseEDNSSubnetAllowlist(::arg()["edns-subnet-allow-list"]);
175✔
1918
  SyncRes::parseEDNSSubnetAddFor(::arg()["ecs-add-for"]);
175✔
1919
  g_useIncomingECS = ::arg().mustDo("use-incoming-edns-subnet");
175✔
1920
  return 0;
175✔
1921
}
175✔
1922

1923
static unsigned int initDistribution(Logr::log_t log)
1924
{
175✔
1925
  unsigned int count = 0;
175✔
1926
  g_balancingFactor = ::arg().asDouble("distribution-load-factor");
175✔
1927
  if (g_balancingFactor != 0.0 && g_balancingFactor < 1.0) {
175!
1928
    g_balancingFactor = 0.0;
×
1929
    SLOG(g_log << Logger::Warning << "Asked to run with a distribution-load-factor below 1.0, disabling it instead" << endl,
×
1930
         log->info(Logr::Warning, "Asked to run with a distribution-load-factor below 1.0, disabling it instead"));
×
1931
  }
×
1932

1933
#ifdef SO_REUSEPORT
175✔
1934
  g_reusePort = ::arg().mustDo("reuseport");
175✔
1935
#endif
175✔
1936

1937
  RecThreadInfo::resize(RecThreadInfo::numRecursorThreads());
175✔
1938

1939
  if (g_reusePort) {
175✔
1940
    unsigned int threadNum = 1;
160✔
1941
    if (RecThreadInfo::weDistributeQueries()) {
160✔
1942
      /* first thread is the handler, then distributors */
1943
      for (unsigned int i = 0; i < RecThreadInfo::numDistributors(); i++, threadNum++) {
4✔
1944
        auto& info = RecThreadInfo::info(threadNum);
2✔
1945
        auto& deferredAdds = info.getDeferredAdds();
2✔
1946
        // The two last arguments to make{UDP,TCP}ServerSockets are used for logging purposes only, same for calls below
1947
        count += makeUDPServerSockets(deferredAdds, log, i == RecThreadInfo::numDistributors() - 1, RecThreadInfo::numDistributors());
2✔
1948
      }
2✔
1949
    }
2✔
1950
    else {
158✔
1951
      /* first thread is the handler, there is no distributor here and workers are accepting queries */
1952
      for (unsigned int i = 0; i < RecThreadInfo::numUDPWorkers(); i++, threadNum++) {
474✔
1953
        auto& info = RecThreadInfo::info(threadNum);
316✔
1954
        auto& deferredAdds = info.getDeferredAdds();
316✔
1955
        count += makeUDPServerSockets(deferredAdds, log, i == RecThreadInfo::numUDPWorkers() - 1, RecThreadInfo::numUDPWorkers());
316✔
1956
      }
316✔
1957
    }
158✔
1958
    threadNum = 1 + RecThreadInfo::numDistributors() + RecThreadInfo::numUDPWorkers();
160✔
1959
    for (unsigned int i = 0; i < RecThreadInfo::numTCPWorkers(); i++, threadNum++) {
320✔
1960
      auto& info = RecThreadInfo::info(threadNum);
160✔
1961
      auto& deferredAdds = info.getDeferredAdds();
160✔
1962
      auto& tcpSockets = info.getTCPSockets();
160✔
1963
      count += makeTCPServerSockets(deferredAdds, tcpSockets, log, i == RecThreadInfo::numTCPWorkers() - 1, RecThreadInfo::numTCPWorkers());
160✔
1964
    }
160✔
1965
  }
160✔
1966
  else {
15✔
1967
    std::set<int> tcpSockets;
15✔
1968
    /* we don't have reuseport so we can only open one socket per
1969
       listening addr:port and everyone will listen on it */
1970
    count += makeUDPServerSockets(s_deferredUDPadds, log, true, 1);
15✔
1971
    count += makeTCPServerSockets(s_deferredTCPadds, tcpSockets, log, true, 1);
15✔
1972

1973
    // TCP queries are handled by TCP workers
1974
    for (unsigned int i = 0; i < RecThreadInfo::numTCPWorkers(); i++) {
30✔
1975
      auto& info = RecThreadInfo::info(i + 1 + RecThreadInfo::numDistributors() + RecThreadInfo::numUDPWorkers());
15✔
1976
      info.setTCPSockets(tcpSockets);
15✔
1977
    }
15✔
1978
  }
15✔
1979
  return count;
175✔
1980
}
175✔
1981

1982
static int initForks(Logr::log_t log)
1983
{
175✔
1984
  int forks = 0;
175✔
1985
  for (; forks < ::arg().asNum("processes") - 1; ++forks) {
175!
1986
    if (fork() == 0) { // we are child
×
1987
      break;
×
1988
    }
×
1989
  }
×
1990

1991
  if (::arg().mustDo("daemon")) {
175!
1992
    SLOG(g_log << Logger::Warning << "Calling daemonize, going to background" << endl,
×
1993
         log->info(Logr::Warning, "Calling daemonize, going to background"));
×
1994
    g_log.toConsole(Logger::Critical);
×
1995
    daemonize(log);
×
1996
  }
×
1997

1998
  if (Utility::getpid() == 1) {
175!
1999
    /* We are running as pid 1, register sigterm and sigint handler
2000

2001
      The Linux kernel will handle SIGTERM and SIGINT for all processes, except PID 1.
2002
      It assumes that the processes running as pid 1 is an "init" like system.
2003
      For years, this was a safe assumption, but containers change that: in
2004
      most (all?) container implementations, the application itself is running
2005
      as pid 1. This means that sending signals to those applications, will not
2006
      be handled by default. Results might be "your container not responding
2007
      when asking it to stop", or "ctrl-c not working even when the app is
2008
      running in the foreground inside a container".
2009

2010
      So TL;DR: If we're running pid 1 (container), we should handle SIGTERM and SIGINT ourselves */
2011

2012
    signal(SIGTERM, termIntHandler);
×
2013
    signal(SIGINT, termIntHandler);
×
2014
  }
×
2015

2016
  signal(SIGUSR1, usr1Handler);
175✔
2017
  signal(SIGUSR2, usr2Handler);
175✔
2018
  signal(SIGPIPE, SIG_IGN); // NOLINT: Posix API
175✔
2019
  return forks;
175✔
2020
}
175✔
2021

2022
static int initPorts(Logr::log_t log)
2023
{
175✔
2024
  int port = ::arg().asNum("udp-source-port-min");
175✔
2025
  if (port < 1024 || port > 65535) {
175!
2026
    SLOG(g_log << Logger::Error << "Unable to launch, udp-source-port-min is not a valid port number" << endl,
×
2027
         log->info(Logr::Error, "Unable to launch, udp-source-port-min is not a valid port number"));
×
2028
    return 99; // this isn't going to fix itself either
×
2029
  }
×
2030
  g_minUdpSourcePort = port;
175✔
2031
  port = ::arg().asNum("udp-source-port-max");
175✔
2032
  if (port < 1024 || port > 65535 || port < g_minUdpSourcePort) {
175!
2033
    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,
×
2034
         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"));
×
2035
    return 99; // this isn't going to fix itself either
×
2036
  }
×
2037
  g_maxUdpSourcePort = port;
175✔
2038
  std::vector<string> parts{};
175✔
2039
  stringtok(parts, ::arg()["udp-source-port-avoid"], ", ");
175✔
2040
  for (const auto& part : parts) {
350✔
2041
    port = std::stoi(part);
350✔
2042
    if (port < 1024 || port > 65535) {
350!
2043
      SLOG(g_log << Logger::Error << "Unable to launch, udp-source-port-avoid contains an invalid port number: " << part << endl,
×
2044
           log->info(Logr::Error, "Unable to launch, udp-source-port-avoid contains an invalid port number", "port", Logging::Loggable(part)));
×
2045
      return 99; // this isn't going to fix itself either
×
2046
    }
×
2047
    g_avoidUdpSourcePorts.insert(port);
350✔
2048
  }
350✔
2049
  return 0;
175✔
2050
}
175✔
2051

2052
static void initSNMP([[maybe_unused]] Logr::log_t log)
2053
{
175✔
2054
  if (::arg().mustDo("snmp-agent")) {
175✔
2055
#ifdef HAVE_NET_SNMP
1✔
2056
    string setting = ::arg()["snmp-daemon-socket"];
1✔
2057
    if (setting.empty()) {
1!
2058
      setting = ::arg()["snmp-master-socket"];
1✔
2059
    }
1✔
2060
    g_snmpAgent = std::make_shared<RecursorSNMPAgent>("recursor", setting);
1✔
2061
    g_snmpAgent->run();
1✔
2062
#else
2063
    const std::string msg = "snmp-agent set but SNMP support not compiled in";
2064
    SLOG(g_log << Logger::Error << msg << endl,
2065
         log->info(Logr::Error, msg));
2066
#endif // HAVE_NET_SNMP
2067
  }
1✔
2068
}
175✔
2069

2070
static int initControl(Logr::log_t log, uid_t newuid, int forks)
2071
{
175✔
2072
  if (!::arg()["chroot"].empty()) {
175!
2073
#ifdef HAVE_SYSTEMD
×
2074
    char* ns;
×
2075
    ns = getenv("NOTIFY_SOCKET");
×
2076
    if (ns != nullptr) {
×
2077
      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,
×
2078
           log->info(Logr::Error, "Unable to chroot when running from systemd. Please disable chroot= or set the 'Type' for this service to 'simple'"));
×
2079
      return 1;
×
2080
    }
×
2081
#endif
×
2082
    if (chroot(::arg()["chroot"].c_str()) < 0 || chdir("/") < 0) {
×
2083
      int err = errno;
×
2084
      SLOG(g_log << Logger::Error << "Unable to chroot to '" + ::arg()["chroot"] + "': " << stringerror(err) << ", exiting" << endl,
×
2085
           log->error(Logr::Error, err, "Unable to chroot", "chroot", Logging::Loggable(::arg()["chroot"])));
×
2086
      return 1;
×
2087
    }
×
2088
    SLOG(g_log << Logger::Info << "Chrooted to '" << ::arg()["chroot"] << "'" << endl,
×
2089
         log->info(Logr::Info, "Chrooted", "chroot", Logging::Loggable(::arg()["chroot"])));
×
2090
  }
×
2091

2092
  checkSocketDir(log);
175✔
2093

2094
  g_pidfname = ::arg()["socket-dir"] + "/" + g_programname + ".pid";
175✔
2095
  if (!g_pidfname.empty()) {
175!
2096
    unlink(g_pidfname.c_str()); // remove possible old pid file
175✔
2097
  }
175✔
2098
  writePid(log);
175✔
2099

2100
  makeControlChannelSocket(::arg().asNum("processes") > 1 ? forks : -1);
175!
2101

2102
  Utility::dropUserPrivs(newuid);
175✔
2103
  try {
175✔
2104
    /* we might still have capabilities remaining, for example if we have been started as root
2105
       without --setuid (please don't do that) or as an unprivileged user with ambient capabilities
2106
       like CAP_NET_BIND_SERVICE.
2107
    */
2108
    dropCapabilities();
175✔
2109
  }
175✔
2110
  catch (const std::exception& e) {
175✔
2111
    SLOG(g_log << Logger::Warning << e.what() << endl,
×
2112
         log->error(Logr::Warning, e.what(), "Could not drop capabilities"));
×
2113
  }
×
2114
  return 0;
175✔
2115
}
175✔
2116

2117
static void initSuffixMatchNodes([[maybe_unused]] Logr::log_t log)
2118
{
175✔
2119
  {
175✔
2120
    SuffixMatchNode dontThrottleNames;
175✔
2121
    vector<string> parts;
175✔
2122
    stringtok(parts, ::arg()["dont-throttle-names"], " ,");
175✔
2123
    for (const auto& part : parts) {
175!
2124
      dontThrottleNames.add(DNSName(part));
×
2125
    }
×
2126
    g_dontThrottleNames.setState(std::move(dontThrottleNames));
175✔
2127

2128
    NetmaskGroup dontThrottleNetmasks;
175✔
2129
    dontThrottleNetmasks.toMasks(::arg()["dont-throttle-netmasks"]);
175✔
2130
    g_dontThrottleNetmasks.setState(std::move(dontThrottleNetmasks));
175✔
2131
  }
175✔
2132

2133
  {
175✔
2134
    SuffixMatchNode xdnssecNames;
175✔
2135
    vector<string> parts;
175✔
2136
    stringtok(parts, ::arg()["x-dnssec-names"], " ,");
175✔
2137
    for (const auto& part : parts) {
175!
2138
      xdnssecNames.add(DNSName(part));
×
2139
    }
×
2140
    g_xdnssec.setState(std::move(xdnssecNames));
175✔
2141
  }
175✔
2142

2143
  {
175✔
2144
    SuffixMatchNode dotauthNames;
175✔
2145
    vector<string> parts;
175✔
2146
    stringtok(parts, ::arg()["dot-to-auth-names"], " ,");
175✔
2147
#ifndef HAVE_DNS_OVER_TLS
2148
    if (!parts.empty()) {
2149
      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,
2150
           log->info(Logr::Error, "dot-to-auth-names setting contains names, but Recursor was built without DNS over TLS support. Setting will be ignored"));
2151
    }
2152
#endif
2153
    for (const auto& part : parts) {
175✔
2154
      dotauthNames.add(DNSName(part));
1✔
2155
    }
1✔
2156
    g_DoTToAuthNames.setState(std::move(dotauthNames));
175✔
2157
  }
175✔
2158
}
175✔
2159

2160
static void initCarbon()
2161
{
175✔
2162
  CarbonConfig config;
175✔
2163
  stringtok(config.servers, arg()["carbon-server"], ", ");
175✔
2164
  config.hostname = arg()["carbon-ourname"];
175✔
2165
  config.instance_name = arg()["carbon-instance"];
175✔
2166
  config.namespace_name = arg()["carbon-namespace"];
175✔
2167
  g_carbonConfig.setState(std::move(config));
175✔
2168
}
175✔
2169

2170
static int initDNS64(Logr::log_t log)
2171
{
175✔
2172
  if (!::arg()["dns64-prefix"].empty()) {
175✔
2173
    try {
3✔
2174
      auto dns64Prefix = Netmask(::arg()["dns64-prefix"]);
3✔
2175
      if (dns64Prefix.getBits() != 96) {
3!
2176
        SLOG(g_log << Logger::Error << "Invalid prefix for 'dns64-prefix', the current implementation only supports /96 prefixes: " << ::arg()["dns64-prefix"] << endl,
×
2177
             log->info(Logr::Error, "Invalid prefix for 'dns64-prefix', the current implementation only supports /96 prefixes", "prefix", Logging::Loggable(::arg()["dns64-prefix"])));
×
2178
        return 1;
×
2179
      }
×
2180
      g_dns64Prefix = dns64Prefix.getNetwork();
3✔
2181
      g_dns64PrefixReverse = reverseNameFromIP(*g_dns64Prefix);
3✔
2182
      /* /96 is 24 nibbles + 2 for "ip6.arpa." */
2183
      while (g_dns64PrefixReverse.countLabels() > 26) {
27✔
2184
        g_dns64PrefixReverse.chopOff();
24✔
2185
      }
24✔
2186
    }
3✔
2187
    catch (const NetmaskException& ne) {
3✔
2188
      SLOG(g_log << Logger::Error << "Invalid prefix '" << ::arg()["dns64-prefix"] << "' for 'dns64-prefix': " << ne.reason << endl,
×
2189
           log->info(Logr::Error, "Invalid prefix", "dns64-prefix", Logging::Loggable(::arg()["dns64-prefix"])));
×
2190
      return 1;
×
2191
    }
×
2192
  }
3✔
2193
  return 0;
175✔
2194
}
175✔
2195

2196
static int serviceMain(Logr::log_t log)
2197
{
175✔
2198
  g_log.setName(g_programname);
175✔
2199
  g_log.disableSyslog(::arg().mustDo("disable-syslog"));
175✔
2200
  g_log.setTimestamps(::arg().mustDo("log-timestamp"));
175✔
2201
  g_regressionTestMode = ::arg().mustDo("devonly-regression-test-mode");
175✔
2202

2203
  if (!::arg()["logging-facility"].empty()) {
175!
2204
    int val = logFacilityToLOG(::arg().asNum("logging-facility"));
×
2205
    if (val >= 0) {
×
2206
      g_log.setFacility(val);
×
2207
    }
×
2208
    else {
×
2209
      SLOG(g_log << Logger::Error << "Unknown logging facility " << ::arg().asNum("logging-facility") << endl,
×
2210
           log->info(Logr::Error, "Unknown logging facility", "facility", Logging::Loggable(::arg().asNum("logging-facility"))));
×
2211
    }
×
2212
  }
×
2213

2214
  g_disthashseed = dns_random_uint32();
175✔
2215

2216
  int ret = initNet(log);
175✔
2217
  if (ret != 0) {
175!
2218
    return ret;
×
2219
  }
×
2220
  // keep this ABOVE loadRecursorLuaConfig!
2221
  ret = initDNSSEC(log);
175✔
2222
  if (ret != 0) {
175!
2223
    return ret;
×
2224
  }
×
2225
  g_maxCacheEntries = ::arg().asNum("max-cache-entries");
175✔
2226

2227
  auto luaResult = luaconfig(false);
175✔
2228
  if (luaResult.d_ret != 0) {
175!
2229
    SLOG(g_log << Logger::Error << "Cannot load Lua or equivalent YAML configuration: " << luaResult.d_str << endl,
×
2230
         log->error(Logr::Error, luaResult.d_str, "Cannot load Lua or equivalent YAML configuration"));
×
2231
    return 1;
×
2232
  }
×
2233

2234
  parseACLs();
175✔
2235
  initPublicSuffixList(::arg()["public-suffix-list-file"]);
175✔
2236

2237
  initDontQuery(log);
175✔
2238

2239
  RecThreadInfo::setWeDistributeQueries(::arg().mustDo("pdns-distributes-queries"));
175✔
2240
  if (RecThreadInfo::weDistributeQueries()) {
175✔
2241
    SLOG(g_log << Logger::Warning << "PowerDNS Recursor itself will distribute queries over threads" << endl,
14✔
2242
         log->info(Logr::Notice, "PowerDNS Recursor itself will distribute queries over threads"));
14✔
2243
  }
14✔
2244

2245
  g_outgoingEDNSBufsize = ::arg().asNum("edns-outgoing-bufsize");
175✔
2246

2247
  if (::arg()["trace"] == "fail") {
175✔
2248
    SyncRes::setDefaultLogMode(SyncRes::Store);
1✔
2249
  }
1✔
2250
  else if (::arg().mustDo("trace")) {
174✔
2251
    SyncRes::setDefaultLogMode(SyncRes::Log);
155✔
2252
    ::arg().set("quiet") = "no";
155✔
2253
    g_quiet = false;
155✔
2254
  }
155✔
2255

2256
  ret = initSyncRes(log);
175✔
2257
  if (ret != 0) {
175!
2258
    return ret;
×
2259
  }
×
2260

2261
  if (!::arg()["proxy-protocol-from"].empty()) {
175✔
2262
    g_initialProxyProtocolACL = std::make_shared<NetmaskGroup>();
10✔
2263
    g_initialProxyProtocolACL->toMasks(::arg()["proxy-protocol-from"]);
10✔
2264

2265
    std::vector<std::string> vec;
10✔
2266
    stringtok(vec, ::arg()["proxy-protocol-exceptions"], ", ");
10✔
2267
    if (!vec.empty()) {
10✔
2268
      g_initialProxyProtocolExceptions = std::make_shared<std::set<ComboAddress>>();
1✔
2269
      for (const auto& sockAddrStr : vec) {
1✔
2270
        ComboAddress sockAddr(sockAddrStr, 53);
1✔
2271
        g_initialProxyProtocolExceptions->emplace(sockAddr);
1✔
2272
      }
1✔
2273
    }
1✔
2274
  }
10✔
2275
  g_proxyProtocolMaximumSize = ::arg().asNum("proxy-protocol-maximum-size");
175✔
2276

2277
  ret = initDNS64(log);
175✔
2278
  if (ret != 0) {
175!
2279
    return ret;
×
2280
  }
×
2281
  g_networkTimeoutMsec = ::arg().asNum("network-timeout");
175✔
2282

2283
  { // Reduce scope of locks (otherwise Coverity induces from this line the global vars below should be
175✔
2284
    // protected by a mutex)
2285
    std::tie(*g_initialDomainMap.lock(), *g_initialAllowNotifyFor.lock()) = parseZoneConfiguration(g_yamlSettings);
175✔
2286
  }
175✔
2287

2288
  g_latencyStatSize = ::arg().asNum("latency-statistic-size");
175✔
2289

2290
  g_logCommonErrors = ::arg().mustDo("log-common-errors");
175✔
2291
  g_logRPZChanges = ::arg().mustDo("log-rpz-changes");
175✔
2292

2293
  g_anyToTcp = ::arg().mustDo("any-to-tcp");
175✔
2294
  g_allowNoRD = ::arg().mustDo("allow-no-rd");
175✔
2295
  g_udpTruncationThreshold = ::arg().asNum("udp-truncation-threshold");
175✔
2296

2297
  g_lowercaseOutgoing = ::arg().mustDo("lowercase-outgoing");
175✔
2298

2299
  g_paddingFrom.toMasks(::arg()["edns-padding-from"]);
175✔
2300
  if (::arg()["edns-padding-mode"] == "always") {
175✔
2301
    g_paddingMode = PaddingMode::Always;
3✔
2302
  }
3✔
2303
  else if (::arg()["edns-padding-mode"] == "padded-queries-only") {
172!
2304
    g_paddingMode = PaddingMode::PaddedQueries;
172✔
2305
  }
172✔
2306
  else {
×
2307
    SLOG(g_log << Logger::Error << "Unknown edns-padding-mode: " << ::arg()["edns-padding-mode"] << endl,
×
2308
         log->info(Logr::Error, "Unknown edns-padding-mode", "edns-padding-mode", Logging::Loggable(::arg()["edns-padding-mode"])));
×
2309
    return 1;
×
2310
  }
×
2311
  g_paddingTag = ::arg().asNum("edns-padding-tag");
175✔
2312
  g_paddingOutgoing = ::arg().mustDo("edns-padding-out");
175✔
2313
  g_ECSHardening = ::arg().mustDo("edns-subnet-harden");
175✔
2314

2315
  RecThreadInfo::setNumDistributorThreads(::arg().asNum("distributor-threads"));
175✔
2316
  RecThreadInfo::setNumUDPWorkerThreads(::arg().asNum("threads"));
175✔
2317
  if (RecThreadInfo::numUDPWorkers() < 1) {
175!
2318
    SLOG(g_log << Logger::Warning << "Asked to run with 0 threads, raising to 1 instead" << endl,
×
2319
         log->info(Logr::Warning, "Asked to run with 0 threads, raising to 1 instead"));
×
2320
    RecThreadInfo::setNumUDPWorkerThreads(1);
×
2321
  }
×
2322
  RecThreadInfo::setNumTCPWorkerThreads(::arg().asNum("tcp-threads"));
175✔
2323
  if (RecThreadInfo::numTCPWorkers() < 1) {
175!
2324
    SLOG(g_log << Logger::Warning << "Asked to run with 0 TCP threads, raising to 1 instead" << endl,
×
2325
         log->info(Logr::Warning, "Asked to run with 0 TCP threads, raising to 1 instead"));
×
2326
    RecThreadInfo::setNumTCPWorkerThreads(1);
×
2327
  }
×
2328

2329
  g_maxMThreads = ::arg().asNum("max-mthreads");
175✔
2330

2331
  int64_t maxInFlight = ::arg().asNum("max-concurrent-requests-per-tcp-connection");
175✔
2332
  if (maxInFlight < 1 || maxInFlight > USHRT_MAX || maxInFlight >= g_maxMThreads) {
175!
2333
    SLOG(g_log << Logger::Warning << "Asked to run with illegal max-concurrent-requests-per-tcp-connection, setting to default (10)" << endl,
×
2334
         log->info(Logr::Warning, "Asked to run with illegal max-concurrent-requests-per-tcp-connection, setting to default (10)"));
×
2335
    TCPConnection::s_maxInFlight = 10;
×
2336
  }
×
2337
  else {
175✔
2338
    TCPConnection::s_maxInFlight = maxInFlight;
175✔
2339
  }
175✔
2340

2341
  int64_t millis = ::arg().asNum("tcp-out-max-idle-ms");
175✔
2342
  TCPOutConnectionManager::s_maxIdleTime = timeval{millis / 1000, (static_cast<suseconds_t>(millis) % 1000) * 1000};
175✔
2343
  TCPOutConnectionManager::s_maxIdlePerAuth = ::arg().asNum("tcp-out-max-idle-per-auth");
175✔
2344
  TCPOutConnectionManager::s_maxQueries = ::arg().asNum("tcp-out-max-queries");
175✔
2345
  TCPOutConnectionManager::s_maxIdlePerThread = ::arg().asNum("tcp-out-max-idle-per-thread");
175✔
2346

2347
  g_gettagNeedsEDNSOptions = ::arg().mustDo("gettag-needs-edns-options");
175✔
2348

2349
  s_statisticsInterval = ::arg().asNum("statistics-interval");
175✔
2350

2351
  SyncRes::s_addExtendedResolutionDNSErrors = ::arg().mustDo("extended-resolution-errors");
175✔
2352

2353
  if (::arg().asNum("aggressive-nsec-cache-size") > 0) {
175!
2354
    if (g_dnssecmode == DNSSECMode::ValidateAll || g_dnssecmode == DNSSECMode::ValidateForLog || g_dnssecmode == DNSSECMode::Process) {
175!
2355
      g_aggressiveNSECCache = make_unique<AggressiveNSECCache>(::arg().asNum("aggressive-nsec-cache-size"));
172✔
2356
    }
172✔
2357
    else {
3✔
2358
      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,
3✔
2359
           log->info(Logr::Warning, "Aggressive NSEC/NSEC3 caching is enabled but DNSSEC validation is not set to 'validate', 'log-fail' or 'process', ignoring"));
3✔
2360
    }
3✔
2361
  }
175✔
2362

2363
  AggressiveNSECCache::s_nsec3DenialProofMaxCost = ::arg().asNum("aggressive-cache-max-nsec3-hash-cost");
175✔
2364
  AggressiveNSECCache::s_maxNSEC3CommonPrefix = static_cast<uint8_t>(std::round(std::log2(::arg().asNum("aggressive-cache-min-nsec3-hit-ratio"))));
175✔
2365
  SLOG(g_log << Logger::Debug << "NSEC3 aggressive cache tuning: aggressive-cache-min-nsec3-hit-ratio: " << ::arg().asNum("aggressive-cache-min-nsec3-hit-ratio") << " max common prefix bits: " << std::to_string(AggressiveNSECCache::s_maxNSEC3CommonPrefix) << endl,
175✔
2366
       log->info(Logr::Debug, "NSEC3 aggressive cache tuning", "aggressive-cache-min-nsec3-hit-ratio", Logging::Loggable(::arg().asNum("aggressive-cache-min-nsec3-hit-ratio")), "maxCommonPrefixBits", Logging::Loggable(AggressiveNSECCache::s_maxNSEC3CommonPrefix)));
175✔
2367

2368
  initSuffixMatchNodes(log);
175✔
2369
  initCarbon();
175✔
2370
  auto listeningSockets = initDistribution(log);
175✔
2371

2372
#ifdef NOD_ENABLED
175✔
2373
  // Setup newly observed domain globals
2374
  setupNODGlobal();
175✔
2375
#endif /* NOD_ENABLED */
175✔
2376

2377
  auto forks = initForks(log);
175✔
2378

2379
  g_tcpTimeout = ::arg().asNum("client-tcp-timeout");
175✔
2380
  g_maxTCPClients = ::arg().asNum("max-tcp-clients");
175✔
2381
  g_maxTCPPerClient = ::arg().asNum("max-tcp-per-client");
175✔
2382
  g_tcpMaxQueriesPerConn = ::arg().asNum("max-tcp-queries-per-connection");
175✔
2383
  g_maxUDPQueriesPerRound = ::arg().asNum("max-udp-queries-per-round");
175✔
2384

2385
  g_useKernelTimestamp = ::arg().mustDo("protobuf-use-kernel-timestamp");
175✔
2386
  g_maxChainLength = ::arg().asNum("max-chain-length");
175✔
2387

2388
  checkOrFixFDS(listeningSockets, log);
175✔
2389
  checkOrFixLinuxMapCountLimits(log);
175✔
2390

2391
#ifdef HAVE_LIBSODIUM
175✔
2392
  if (sodium_init() == -1) {
175!
2393
    SLOG(g_log << Logger::Error << "Unable to initialize sodium crypto library" << endl,
×
2394
         log->info(Logr::Error, "Unable to initialize sodium crypto library"));
×
2395
    return 99;
×
2396
  }
×
2397
#endif
175✔
2398

2399
  openssl_thread_setup();
175✔
2400
  openssl_seed();
175✔
2401

2402
  gid_t newgid = 0;
175✔
2403
  if (!::arg()["setgid"].empty()) {
175!
2404
    newgid = strToGID(::arg()["setgid"]);
×
2405
  }
×
2406
  uid_t newuid = 0;
175✔
2407
  if (!::arg()["setuid"].empty()) {
175!
2408
    newuid = strToUID(::arg()["setuid"]);
×
2409
  }
×
2410

2411
  Utility::dropGroupPrivs(newuid, newgid);
175✔
2412

2413
  ret = initControl(log, newuid, forks);
175✔
2414
  if (ret != 0) {
175!
2415
    return ret;
×
2416
  }
×
2417

2418
  {
175✔
2419
    auto lci = g_luaconfs.getCopy();
175✔
2420
    startLuaConfigDelayedThreads(lci, lci.generation);
175✔
2421
  }
175✔
2422

2423
  RecThreadInfo::makeThreadPipes(log);
175✔
2424

2425
  disableStats(StatComponent::API, ::arg()["stats-api-blacklist"]);
175✔
2426
  disableStats(StatComponent::Carbon, ::arg()["stats-carbon-blacklist"]);
175✔
2427
  disableStats(StatComponent::RecControl, ::arg()["stats-rec-control-blacklist"]);
175✔
2428
  disableStats(StatComponent::SNMP, ::arg()["stats-snmp-blacklist"]);
175✔
2429

2430
  disableStats(StatComponent::API, ::arg()["stats-api-disabled-list"]);
175✔
2431
  disableStats(StatComponent::Carbon, ::arg()["stats-carbon-disabled-list"]);
175✔
2432
  disableStats(StatComponent::RecControl, ::arg()["stats-rec-control-disabled-list"]);
175✔
2433
  disableStats(StatComponent::SNMP, ::arg()["stats-snmp-disabled-list"]);
175✔
2434

2435
  // Run before any thread doing stats related things
2436
  registerAllStats();
175✔
2437

2438
  initSNMP(log);
175✔
2439

2440
  ret = initPorts(log);
175✔
2441
  if (ret != 0) {
175!
2442
    return ret;
×
2443
  }
×
2444

2445
#ifdef NOD_ENABLED
175✔
2446
  setupNODThread(log);
175✔
2447
#endif /* NOD_ENABLED */
175✔
2448

2449
  runStartStopLua(true, log);
175✔
2450
  ret = RecThreadInfo::runThreads(log);
175✔
2451
  runStartStopLua(false, log);
175✔
2452
  return ret;
175✔
2453
}
175✔
2454

2455
static void handlePipeRequest(int fileDesc, FDMultiplexer::funcparam_t& /* var */)
2456
{
7,973✔
2457
  ThreadMSG* tmsg = nullptr;
7,973✔
2458

2459
  if (read(fileDesc, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // fd == readToThread || fd == readQueriesToThread NOLINT: sizeof correct
7,973!
2460
    unixDie("read from thread pipe returned wrong size or error");
×
2461
  }
×
2462

2463
  void* resp = nullptr;
7,973✔
2464
  try {
7,973✔
2465
    resp = tmsg->func();
7,973✔
2466
  }
7,973✔
2467
  catch (const PDNSException& pdnsException) {
7,973✔
2468
    g_rateLimitedLogger.log(g_slog->withName("runtime"), "PIPE function", pdnsException);
×
2469
  }
×
2470
  catch (const MOADNSException& moadnsexception) {
7,973✔
2471
    if (g_logCommonErrors) {
×
2472
      g_slog->withName("runtime")->error(moadnsexception.what(), "PIPE function created an exception", "excepion", Logging::Loggable("MOADNSException"));
×
2473
    }
×
2474
  }
×
2475
  catch (const std::exception& stdException) {
7,973✔
2476
    g_rateLimitedLogger.log(g_slog->withName("runtime"), "PIPE function", stdException);
×
2477
  }
×
2478
  catch (...) {
7,973✔
2479
    g_rateLimitedLogger.log(g_slog->withName("runtime"), "PIPE function");
×
2480
  }
×
2481
  if (tmsg->wantAnswer) {
7,976✔
2482
    if (write(RecThreadInfo::self().getPipes().writeFromThread, &resp, sizeof(resp)) != sizeof(resp)) {
4,368!
2483
      delete tmsg; // NOLINT: manual ownership handling
×
2484
      unixDie("write to thread pipe returned wrong size or error");
×
2485
    }
×
2486
  }
4,368✔
2487

2488
  delete tmsg; // NOLINT: manual ownership handling
7,966✔
2489
}
7,966✔
2490

2491
static void handleRCC(int fileDesc, FDMultiplexer::funcparam_t& /* var */)
2492
{
652✔
2493
  auto log = g_slog->withName("control");
652✔
2494
  try {
652✔
2495
    FDWrapper clientfd = accept(fileDesc, nullptr, nullptr);
652✔
2496
    if (clientfd == -1) {
652!
2497
      throw PDNSException("accept failed");
×
2498
    }
×
2499
    string msg = g_rcc.recv(clientfd).d_str;
652✔
2500
    SLOG(g_log << Logger::Info << "Received rec_control command '" << msg << "' via controlsocket" << endl,
652✔
2501
         log->info(Logr::Info, "Received rec_control command via control socket", "command", Logging::Loggable(msg)));
652✔
2502

2503
    RecursorControlParser::func_t* command = nullptr;
652✔
2504
    auto answer = RecursorControlParser::getAnswer(clientfd, msg, &command);
652✔
2505

2506
    if (command != doExitNicely) {
652✔
2507
      g_rcc.send(clientfd, answer);
477✔
2508
    }
477✔
2509
    command();
652✔
2510
    if (command == doExitNicely) {
652✔
2511
      g_rcc.send(clientfd, answer);
175✔
2512
    }
175✔
2513
  }
652✔
2514
  catch (const std::exception& e) {
652✔
2515
    SLOG(g_log << Logger::Error << "Error dealing with control socket request: " << e.what() << endl,
×
2516
         log->error(Logr::Error, e.what(), "Exception while dealing with control socket request", "exception", Logging::Loggable("std::exception")));
×
2517
  }
×
2518
  catch (const PDNSException& ae) {
652✔
2519
    SLOG(g_log << Logger::Error << "Error dealing with control socket request: " << ae.reason << endl,
×
2520
         log->error(Logr::Error, ae.reason, "Exception while dealing with control socket request", "exception", Logging::Loggable("PDNSException")));
×
2521
  }
×
2522
}
652✔
2523

2524
class PeriodicTask
2525
{
2526
public:
2527
  PeriodicTask(const string& aName, time_t aTime) :
2528
    period{aTime, 0}, name(aName)
2529
  {
2,880✔
2530
    if (aTime <= 0) {
2,880!
2531
      throw PDNSException("Invalid period of periodic task " + aName);
×
2532
    }
×
2533
  }
2,880✔
2534

2535
  void runIfDue(struct timeval& now, const std::function<void()>& function)
2536
  {
5,144✔
2537
    if (last_run < now - period) {
5,144✔
2538
      function();
3,066✔
2539
      Utility::gettimeofday(&last_run);
3,066✔
2540
      now = last_run;
3,066✔
2541
    }
3,066✔
2542
  }
5,144✔
2543

2544
  [[nodiscard]] time_t getPeriod() const
2545
  {
49✔
2546
    return period.tv_sec;
49✔
2547
  }
49✔
2548

2549
  void setPeriod(time_t newperiod)
2550
  {
334✔
2551
    period.tv_sec = newperiod;
334✔
2552
  }
334✔
2553

2554
  void updateLastRun()
2555
  {
109✔
2556
    Utility::gettimeofday(&last_run);
109✔
2557
  }
109✔
2558

2559
  [[nodiscard]] bool hasRun() const
2560
  {
221✔
2561
    return last_run.tv_sec != 0 || last_run.tv_usec != 0;
221!
2562
  }
221✔
2563

2564
private:
2565
  struct timeval last_run{
2566
    0, 0};
2567
  struct timeval period;
2568
  string name;
2569
};
2570

2571
static void houseKeepingWork(Logr::log_t log)
2572
{
994✔
2573
  struct timeval now{};
994✔
2574
  Utility::gettimeofday(&now);
994✔
2575
  t_Counters.updateSnap(now, g_regressionTestMode);
994✔
2576

2577
  // Below are the tasks that run for every recursorThread, including handler and taskThread
2578

2579
  static thread_local PeriodicTask pruneTCPTask{"pruneTCPTask", 5};
994✔
2580
  pruneTCPTask.runIfDue(now, [now]() {
994✔
2581
    t_tcp_manager.cleanup(now);
759✔
2582
  });
759✔
2583

2584
  const auto& info = RecThreadInfo::self();
994✔
2585

2586
  // Threads handling packets process config changes in the input path, but not all threads process input packets
2587
  // distr threads only process TCP, so that may not happenn very often. So do all periodically.
2588
  static thread_local PeriodicTask exportConfigTask{"exportConfigTask", 30};
994✔
2589
  auto luaconfsLocal = g_luaconfs.getLocal();
994✔
2590
  exportConfigTask.runIfDue(now, [&luaconfsLocal]() {
994✔
2591
    checkProtobufExport(luaconfsLocal);
676✔
2592
    checkOutgoingProtobufExport(luaconfsLocal);
676✔
2593
#ifdef HAVE_FSTRM
676✔
2594
    checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
676✔
2595
    checkFrameStreamExport(luaconfsLocal, luaconfsLocal->nodFrameStreamExportConfig, t_nodFrameStreamServersInfo);
676✔
2596
#endif
676✔
2597
  });
676✔
2598

2599
  // Below are the thread specific tasks for the handler and the taskThread
2600
  // Likley a few handler tasks could be moved to the taskThread
2601
  if (info.isTaskThread()) {
994✔
2602
    // TaskQueue is run always
2603
    runTasks(10, g_logCommonErrors);
292✔
2604

2605
    static PeriodicTask ztcTask{"ZTC", 60};
292✔
2606
    static map<DNSName, RecZoneToCache::State> ztcStates;
292✔
2607
    ztcTask.runIfDue(now, [&luaconfsLocal]() {
292✔
2608
      RecZoneToCache::maintainStates(luaconfsLocal->ztcConfigs, ztcStates, luaconfsLocal->generation);
114✔
2609
      for (const auto& ztc : luaconfsLocal->ztcConfigs) {
114✔
2610
        RecZoneToCache::ZoneToCache(ztc.second, ztcStates.at(ztc.first));
1✔
2611
      }
1✔
2612
    });
114✔
2613
  }
292✔
2614
  else if (info.isHandler()) {
702✔
2615
    if (g_packetCache) {
221✔
2616
      static PeriodicTask packetCacheTask{"packetCacheTask", 5};
210✔
2617
      packetCacheTask.runIfDue(now, [now]() {
210✔
2618
        g_packetCache->doPruneTo(now.tv_sec, g_maxPacketCacheEntries);
133✔
2619
      });
133✔
2620
    }
210✔
2621
    static PeriodicTask recordCachePruneTask{"RecordCachePruneTask", 5};
221✔
2622
    recordCachePruneTask.runIfDue(now, [now]() {
221✔
2623
      g_recCache->doPrune(now.tv_sec, g_maxCacheEntries);
139✔
2624
    });
139✔
2625

2626
    static PeriodicTask negCachePruneTask{"NegCachePrunteTask", 5};
221✔
2627
    negCachePruneTask.runIfDue(now, [now]() {
221✔
2628
      g_negCache->prune(now.tv_sec, g_maxCacheEntries / 8);
139✔
2629
    });
139✔
2630

2631
    static PeriodicTask aggrNSECPruneTask{"AggrNSECPruneTask", 5};
221✔
2632
    aggrNSECPruneTask.runIfDue(now, [now]() {
221✔
2633
      if (g_aggressiveNSECCache) {
139✔
2634
        g_aggressiveNSECCache->prune(now.tv_sec);
135✔
2635
      }
135✔
2636
    });
139✔
2637

2638
    static PeriodicTask pruneNSpeedTask{"pruneNSSpeedTask", 30};
221✔
2639
    pruneNSpeedTask.runIfDue(now, [now]() {
221✔
2640
      SyncRes::pruneNSSpeeds(now.tv_sec - 300);
109✔
2641
    });
109✔
2642

2643
    static PeriodicTask pruneEDNSTask{"pruneEDNSTask", 60};
221✔
2644
    pruneEDNSTask.runIfDue(now, [now]() {
221✔
2645
      SyncRes::pruneEDNSStatuses(now.tv_sec);
109✔
2646
    });
109✔
2647

2648
    if (SyncRes::s_max_busy_dot_probes > 0) {
221!
2649
      static PeriodicTask pruneDoTProbeMap{"pruneDoTProbeMapTask", 60};
×
2650
      pruneDoTProbeMap.runIfDue(now, [now]() {
×
2651
        SyncRes::pruneDoTProbeMap(now.tv_sec);
×
2652
      });
×
2653
    }
×
2654

2655
    static PeriodicTask pruneThrottledTask{"pruneThrottledTask", 5};
221✔
2656
    pruneThrottledTask.runIfDue(now, [now]() {
221✔
2657
      SyncRes::pruneThrottledServers(now.tv_sec);
139✔
2658
    });
139✔
2659

2660
    static PeriodicTask pruneFailedServersTask{"pruneFailedServerTask", 5};
221✔
2661
    pruneFailedServersTask.runIfDue(now, [now]() {
221✔
2662
      SyncRes::pruneFailedServers(now.tv_sec - static_cast<time_t>(SyncRes::s_serverdownthrottletime * 10));
139✔
2663
    });
139✔
2664

2665
    static PeriodicTask pruneNonResolvingTask{"pruneNonResolvingTask", 5};
221✔
2666
    pruneNonResolvingTask.runIfDue(now, [now]() {
221✔
2667
      SyncRes::pruneNonResolving(now.tv_sec - SyncRes::s_nonresolvingnsthrottletime);
139✔
2668
    });
139✔
2669

2670
    static PeriodicTask pruneSaveParentSetTask{"pruneSaveParentSetTask", 60};
221✔
2671
    pruneSaveParentSetTask.runIfDue(now, [now]() {
221✔
2672
      SyncRes::pruneSaveParentsNSSets(now.tv_sec);
109✔
2673
    });
109✔
2674

2675
    // By default, refresh at 80% of max-cache-ttl with a minimum period of 10s
2676
    const unsigned int minRootRefreshInterval = 10;
221✔
2677
    static PeriodicTask rootUpdateTask{"rootUpdateTask", std::max(SyncRes::s_maxcachettl * 8 / 10, minRootRefreshInterval)};
221✔
2678
    rootUpdateTask.runIfDue(now, [now, &log, minRootRefreshInterval]() {
221✔
2679
      int res = 0;
113✔
2680
      if (!g_regressionTestMode) {
113✔
2681
        res = SyncRes::getRootNS(now, nullptr, 0, log);
107✔
2682
      }
107✔
2683
      if (res == 0) {
113✔
2684
        // Success, go back to the defaut period
2685
        rootUpdateTask.setPeriod(std::max(SyncRes::s_maxcachettl * 8 / 10, minRootRefreshInterval));
64✔
2686
      }
64✔
2687
      else {
49✔
2688
        // On failure, go to the middle of the remaining period (initially 80% / 8 = 10%) and shorten the interval on each
2689
        // failure by dividing the existing interval by 8, keeping the minimum interval at 10s.
2690
        // So with a 1 day period and failures we'll see a refresh attempt at 69120, 69120+11520, 69120+11520+1440, ...
2691
        rootUpdateTask.setPeriod(std::max<time_t>(rootUpdateTask.getPeriod() / 8, minRootRefreshInterval));
49✔
2692
      }
49✔
2693
    });
113✔
2694

2695
    static PeriodicTask secpollTask{"secpollTask", 3600};
221✔
2696
    static time_t t_last_secpoll;
221✔
2697
    secpollTask.runIfDue(now, [&log]() {
221✔
2698
      try {
109✔
2699
        doSecPoll(&t_last_secpoll, log);
109✔
2700
      }
109✔
2701
      catch (const std::exception& e) {
109✔
2702
        SLOG(g_log << Logger::Error << "Exception while performing security poll: " << e.what() << endl,
×
2703
             log->error(Logr::Error, e.what(), "Exception while performing security poll"));
×
2704
      }
×
2705
      catch (const PDNSException& e) {
109✔
2706
        SLOG(g_log << Logger::Error << "Exception while performing security poll: " << e.reason << endl,
×
2707
             log->error(Logr::Error, e.reason, "Exception while performing security poll"));
×
2708
      }
×
2709
      catch (const ImmediateServFailException& e) {
109✔
2710
        SLOG(g_log << Logger::Error << "Exception while performing security poll: " << e.reason << endl,
×
2711
             log->error(Logr::Error, e.reason, "Exception while performing security poll"));
×
2712
      }
×
2713
      catch (const PolicyHitException& e) {
109✔
2714
        SLOG(g_log << Logger::Error << "Policy hit while performing security poll" << endl,
×
2715
             log->info(Logr::Error, "Policy hit while performing security poll"));
×
2716
      }
×
2717
      catch (...) {
109✔
2718
        SLOG(g_log << Logger::Error << "Exception while performing security poll" << endl,
×
2719
             log->info(Logr::Error, "Exception while performing security poll"));
×
2720
      }
×
2721
    });
109✔
2722

2723
    const time_t taInterval = std::max(1, static_cast<int>(luaconfsLocal->trustAnchorFileInfo.interval) * 3600);
221✔
2724
    static PeriodicTask trustAnchorTask{"trustAnchorTask", taInterval};
221✔
2725
    if (!trustAnchorTask.hasRun()) {
221✔
2726
      // Loading the Lua config file already "refreshed" the TAs
2727
      trustAnchorTask.updateLastRun();
109✔
2728
    }
109✔
2729
    // interval might have ben updated
2730
    trustAnchorTask.setPeriod(taInterval);
221✔
2731
    trustAnchorTask.runIfDue(now, [&luaconfsLocal, &log]() {
221✔
2732
      if (!luaconfsLocal->trustAnchorFileInfo.fname.empty() && luaconfsLocal->trustAnchorFileInfo.interval != 0) {
×
2733
        SLOG(g_log << Logger::Debug << "Refreshing Trust Anchors from file" << endl,
×
2734
             log->info(Logr::Debug, "Refreshing Trust Anchors from file"));
×
2735
        try {
×
2736
          map<DNSName, dsset_t> dsAnchors;
×
2737
          if (updateTrustAnchorsFromFile(luaconfsLocal->trustAnchorFileInfo.fname, dsAnchors, log)) {
×
2738
            g_luaconfs.modify([&dsAnchors](LuaConfigItems& lci) {
×
2739
              lci.dsAnchors = dsAnchors;
×
2740
            });
×
2741
          }
×
2742
        }
×
2743
        catch (const PDNSException& pe) {
×
2744
          SLOG(g_log << Logger::Error << "Unable to update Trust Anchors: " << pe.reason << endl,
×
2745
               log->error(Logr::Error, pe.reason, "Unable to update Trust Anchors"));
×
2746
        }
×
2747
      }
×
2748
    });
×
2749
  }
221✔
2750
  t_Counters.updateSnap(g_regressionTestMode);
994✔
2751
}
994✔
2752

2753
static void houseKeeping(void* /* ignored */)
2754
{
1,011✔
2755
  auto log = g_slog->withName("housekeeping");
1,011✔
2756
  static thread_local bool t_running; // houseKeeping can get suspended in secpoll, and be restarted, which makes us do duplicate work
1,011✔
2757

2758
  try {
1,011✔
2759
    if (t_running) {
1,011✔
2760
      return;
18✔
2761
    }
18✔
2762
    t_running = true;
993✔
2763
    houseKeepingWork(log);
993✔
2764
    t_running = false;
993✔
2765
  }
993✔
2766
  catch (const PDNSException& ae) {
1,011✔
2767
    t_running = false;
×
2768
    SLOG(g_log << Logger::Error << "Fatal error in housekeeping thread: " << ae.reason << endl,
×
2769
         log->error(Logr::Error, ae.reason, "Fatal error in housekeeping thread"));
×
2770
    throw;
×
2771
  }
×
2772
  catch (...) {
1,011✔
2773
    t_running = false;
×
2774
    SLOG(g_log << Logger::Error << "Uncaught exception in housekeeping thread" << endl,
×
2775
         log->info(Logr::Error, "Uncaught exception in housekeeping thread"));
×
2776
    throw;
×
2777
  }
×
2778
}
1,011✔
2779

2780
static void runLuaMaintenance(RecThreadInfo& threadInfo, time_t& last_lua_maintenance, time_t luaMaintenanceInterval)
2781
{
25,582✔
2782
  if (t_pdl != nullptr) {
25,582✔
2783
    // lua-dns-script directive is present, call the maintenance callback if needed
2784
    if (threadInfo.isWorker()) { // either UDP of TCP worker
2,244✔
2785
      // Only on threads processing queries
2786
      if (g_now.tv_sec - last_lua_maintenance >= luaMaintenanceInterval) {
2,222✔
2787
        struct timeval start{};
436✔
2788
        Utility::gettimeofday(&start);
436✔
2789
        t_pdl->maintenance();
436✔
2790
        last_lua_maintenance = g_now.tv_sec;
436✔
2791
        struct timeval stop{};
436✔
2792
        Utility::gettimeofday(&stop);
436✔
2793
        t_Counters.at(rec::Counter::maintenanceUsec) += uSec(stop - start);
436✔
2794
        ++t_Counters.at(rec::Counter::maintenanceCalls);
436✔
2795
      }
436✔
2796
    }
2,222✔
2797
  }
2,244✔
2798
}
25,582✔
2799

2800
static void recLoop()
2801
{
913✔
2802
  time_t last_stat = 0;
913✔
2803
  time_t last_carbon = 0;
913✔
2804
  time_t last_lua_maintenance = 0;
913✔
2805
  time_t carbonInterval = ::arg().asNum("carbon-interval");
913✔
2806
  time_t luaMaintenanceInterval = ::arg().asNum("lua-maintenance-interval");
913✔
2807

2808
  auto& threadInfo = RecThreadInfo::self();
913✔
2809

2810
  while (!RecursorControlChannel::stop) {
26,325✔
2811
    try {
25,466✔
2812
      while (g_multiTasker->schedule(g_now)) {
34,026✔
2813
        ; // MTasker letting the mthreads do their thing
8,560✔
2814
      }
8,560✔
2815

2816
      // Use primes, it avoid not being scheduled in cases where the counter has a regular pattern.
2817
      // We want to call handler thread often, it gets scheduled about 2 times per second
2818
      if (((threadInfo.isHandler() || threadInfo.isTaskThread()) && s_counter % 11 == 0) || s_counter % 499 == 0) {
25,481✔
2819
        timeval start{};
1,201✔
2820
        Utility::gettimeofday(&start);
1,201✔
2821
        g_multiTasker->makeThread(houseKeeping, nullptr);
1,201✔
2822
        if (!threadInfo.isTaskThread()) {
1,201✔
2823
          timeval stop{};
836✔
2824
          Utility::gettimeofday(&stop);
836✔
2825
          t_Counters.at(rec::Counter::maintenanceUsec) += uSec(stop - start);
836✔
2826
          ++t_Counters.at(rec::Counter::maintenanceCalls);
836✔
2827
        }
836✔
2828
      }
1,201✔
2829

2830
      if (s_counter % 55 == 0) {
25,466✔
2831
        auto expired = t_fdm->getTimeouts(g_now);
562✔
2832

2833
        for (const auto& exp : expired) {
562!
2834
          auto conn = boost::any_cast<shared_ptr<TCPConnection>>(exp.second);
×
2835
          if (g_logCommonErrors) {
×
2836
            SLOG(g_log << Logger::Warning << "Timeout from remote TCP client " << conn->d_remote.toStringWithPort() << endl,
×
2837
                 g_slogtcpin->info(Logr::Warning, "Timeout from remote TCP client", "remote", Logging::Loggable(conn->d_remote)));
×
2838
          }
×
2839
          t_fdm->removeReadFD(exp.first);
×
2840
        }
×
2841
      }
562✔
2842

2843
      s_counter++;
25,466✔
2844

2845
      if (threadInfo.isHandler()) {
25,466✔
2846
        if (statsWanted || (s_statisticsInterval > 0 && (g_now.tv_sec - last_stat) >= s_statisticsInterval)) {
1,930✔
2847
          doStats();
60✔
2848
          last_stat = g_now.tv_sec;
60✔
2849
        }
60✔
2850

2851
        Utility::gettimeofday(&g_now, nullptr);
1,930✔
2852

2853
        if ((g_now.tv_sec - last_carbon) >= carbonInterval) {
1,930✔
2854
          g_multiTasker->makeThread(doCarbonDump, nullptr);
176✔
2855
          last_carbon = g_now.tv_sec;
176✔
2856
        }
176✔
2857
      }
1,930✔
2858
      runLuaMaintenance(threadInfo, last_lua_maintenance, luaMaintenanceInterval);
25,466✔
2859

2860
      auto timeoutUsec = g_multiTasker->nextWaiterDelayUsec(500000);
25,466✔
2861
      t_fdm->run(&g_now, static_cast<int>(timeoutUsec / 1000));
25,466✔
2862
      // 'run' updates g_now for us
2863
    }
25,466✔
2864
    catch (const PDNSException& pdnsException) {
25,466✔
2865
      g_rateLimitedLogger.log(g_slog->withName("runtime"), "recLoop", pdnsException);
×
2866
    }
×
2867
    catch (const std::exception& stdException) {
25,466✔
2868
      g_rateLimitedLogger.log(g_slog->withName("runtime"), "recLoop", stdException);
×
2869
    }
×
2870
    catch (...) {
25,466✔
2871
      g_rateLimitedLogger.log(g_slog->withName("runtime"), "recLoop");
×
2872
    }
×
2873
  }
25,466✔
2874
}
913✔
2875

2876
static void recursorThread()
2877
{
913✔
2878
  auto log = g_slog->withName("runtime");
913✔
2879
  t_Counters.updateSnap(true);
913✔
2880
  try {
913✔
2881
    auto& threadInfo = RecThreadInfo::self();
913✔
2882
    {
913✔
2883
      SyncRes tmp(g_now); // make sure it allocates tsstorage before we do anything, like primeHints or so..
913✔
2884
      SyncRes::setDomainMap(*g_initialDomainMap.lock());
913✔
2885
      t_allowFrom = *g_initialAllowFrom.lock();
913✔
2886
      t_allowNotifyFrom = *g_initialAllowNotifyFrom.lock();
913✔
2887
      t_allowNotifyFor = *g_initialAllowNotifyFor.lock();
913✔
2888
      t_proxyProtocolACL = g_initialProxyProtocolACL;
913✔
2889
      t_proxyProtocolExceptions = g_initialProxyProtocolExceptions;
913✔
2890
      t_udpclientsocks = std::make_unique<UDPClientSocks>();
913✔
2891
      if (g_proxyMapping) {
913✔
2892
        t_proxyMapping = make_unique<ProxyMapping>(*g_proxyMapping);
30✔
2893
      }
30✔
2894
      else {
883✔
2895
        t_proxyMapping = nullptr;
883✔
2896
      }
883✔
2897

2898
      if (threadInfo.isHandler()) {
913✔
2899
        if (!primeHints()) {
175!
2900
          threadInfo.setExitCode(EXIT_FAILURE);
×
2901
          RecursorControlChannel::stop = true;
×
2902
          SLOG(g_log << Logger::Critical << "Priming cache failed, stopping" << endl,
×
2903
               log->info(Logr::Critical, "Priming cache failed, stopping"));
×
2904
        }
×
2905
        SLOG(g_log << Logger::Debug << "Done priming cache with root hints" << endl,
175✔
2906
             log->info(Logr::Debug, "Done priming cache with root hints"));
175✔
2907
      }
175✔
2908
    }
913✔
2909

2910
    /* the listener threads handle TCP queries */
2911
    if (threadInfo.isWorker() || threadInfo.isListener()) {
913✔
2912
      try {
563✔
2913
        if (!::arg()["lua-dns-script"].empty()) {
563✔
2914
          t_pdl = std::make_shared<RecursorLua4>();
125✔
2915
          t_pdl->loadFile(::arg()["lua-dns-script"]);
125✔
2916
          SLOG(g_log << Logger::Warning << "Loaded 'lua' script from '" << ::arg()["lua-dns-script"] << "'" << endl,
125✔
2917
               log->info(Logr::Warning, "Loading Lua script from file", "name", Logging::Loggable(::arg()["lua-dns-script"])));
125✔
2918
        }
125✔
2919
      }
563✔
2920
      catch (std::exception& e) {
563✔
2921
        SLOG(g_log << Logger::Error << "Failed to load 'lua' script from '" << ::arg()["lua-dns-script"] << "': " << e.what() << endl,
×
2922
             log->error(Logr::Error, e.what(), "Failed to load Lua script from file", "name", Logging::Loggable(::arg()["lua-dns-script"])));
×
2923
        _exit(99);
×
2924
      }
×
2925
    }
563✔
2926

2927
    if (unsigned int ringsize = ::arg().asNum("stats-ringbuffer-entries") / RecThreadInfo::numUDPWorkers(); ringsize != 0) {
913!
2928
      t_remotes = std::make_unique<addrringbuf_t>();
913✔
2929
      if (RecThreadInfo::weDistributeQueries()) {
913✔
2930
        t_remotes->set_capacity(::arg().asNum("stats-ringbuffer-entries") / RecThreadInfo::numDistributors());
111✔
2931
      }
111✔
2932
      else {
802✔
2933
        t_remotes->set_capacity(ringsize);
802✔
2934
      }
802✔
2935
      t_servfailremotes = std::make_unique<addrringbuf_t>();
913✔
2936
      t_servfailremotes->set_capacity(ringsize);
913✔
2937
      t_bogusremotes = std::make_unique<addrringbuf_t>();
913✔
2938
      t_bogusremotes->set_capacity(ringsize);
913✔
2939
      t_largeanswerremotes = std::make_unique<addrringbuf_t>();
913✔
2940
      t_largeanswerremotes->set_capacity(ringsize);
913✔
2941
      t_timeouts = std::make_unique<addrringbuf_t>();
913✔
2942
      t_timeouts->set_capacity(ringsize);
913✔
2943

2944
      t_queryring = std::make_unique<boost::circular_buffer<pair<DNSName, uint16_t>>>();
913✔
2945
      t_queryring->set_capacity(ringsize);
913✔
2946
      t_servfailqueryring = std::make_unique<boost::circular_buffer<pair<DNSName, uint16_t>>>();
913✔
2947
      t_servfailqueryring->set_capacity(ringsize);
913✔
2948
      t_bogusqueryring = std::make_unique<boost::circular_buffer<pair<DNSName, uint16_t>>>();
913✔
2949
      t_bogusqueryring->set_capacity(ringsize);
913✔
2950
    }
913✔
2951
    g_multiTasker = std::make_unique<MT_t>(::arg().asNum("stack-size"), ::arg().asNum("stack-cache-size"));
913✔
2952
    threadInfo.setMT(g_multiTasker.get());
913✔
2953

2954
    {
913✔
2955
      /* start protobuf export threads if needed, don't keep a ref to lua config around */
2956
      auto luaconfsLocal = g_luaconfs.getLocal();
913✔
2957
      checkProtobufExport(luaconfsLocal);
913✔
2958
      checkOutgoingProtobufExport(luaconfsLocal);
913✔
2959
#ifdef HAVE_FSTRM
913✔
2960
      checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
913✔
2961
      checkFrameStreamExport(luaconfsLocal, luaconfsLocal->nodFrameStreamExportConfig, t_nodFrameStreamServersInfo);
913✔
2962
#endif
913✔
2963
      for (const auto& rpz : luaconfsLocal->rpzs) {
913✔
2964
        string name = rpz.polName.empty() ? (rpz.zoneXFRParams.primaries.empty() ? "rpzFile" : rpz.zoneXFRParams.name) : rpz.polName;
85!
2965
        t_Counters.at(rec::PolicyNameHits::policyName).counts[name] = 0;
85✔
2966
      }
85✔
2967
    }
913✔
2968

2969
    t_fdm = unique_ptr<FDMultiplexer>(getMultiplexer(log));
913✔
2970
    t_fdm->addReadFD(threadInfo.getPipes().readToThread, handlePipeRequest);
913✔
2971

2972
    if (threadInfo.isHandler()) {
913✔
2973
      SLOG(g_log << Logger::Info << "Enabled '" << t_fdm->getName() << "' multiplexer" << endl,
175✔
2974
           log->info(Logr::Info, "Enabled multiplexer", "name", Logging::Loggable(t_fdm->getName())));
175✔
2975
    }
175✔
2976
    else {
738✔
2977
      t_fdm->addReadFD(threadInfo.getPipes().readQueriesToThread, handlePipeRequest);
738✔
2978

2979
      if (threadInfo.isListener()) {
738✔
2980
        if (g_reusePort) {
508✔
2981
          /* then every listener has its own FDs */
2982
          for (const auto& deferred : threadInfo.getDeferredAdds()) {
484✔
2983
            t_fdm->addReadFD(deferred.first, deferred.second);
484✔
2984
          }
484✔
2985
        }
478✔
2986
        else {
30✔
2987
          /* otherwise all listeners are listening on the same ones */
2988
          for (const auto& deferred : threadInfo.isTCPListener() ? s_deferredTCPadds : s_deferredUDPadds) {
60✔
2989
            t_fdm->addReadFD(deferred.first, deferred.second);
60✔
2990
          }
60✔
2991
        }
30✔
2992
      }
508✔
2993
    }
738✔
2994

2995
    if (threadInfo.isHandler()) {
913✔
2996
      t_fdm->addReadFD(g_rcc.getDescriptor(), handleRCC); // control channel
175✔
2997
    }
175✔
2998

2999
#ifdef HAVE_SYSTEMD
913✔
3000
    if (threadInfo.isHandler()) {
913✔
3001
      // There is a race, as some threads might not be ready yet to do work.
3002
      // To solve that, threads should notify RecThreadInfo they are done initializing.
3003
      // But we lack a mechanism for that at this point in time.
3004
      sd_notify(0, "READY=1");
175✔
3005
    }
175✔
3006
#endif
913✔
3007

3008
    recLoop();
913✔
3009
  }
913✔
3010
  catch (const PDNSException& ae) {
913✔
3011
    SLOG(g_log << Logger::Error << "Exception: " << ae.reason << endl,
×
3012
         log->error(Logr::Error, ae.reason, "Exception in RecursorThread", "exception", Logging::Loggable("PDNSException")));
×
3013
  }
×
3014
  catch (const std::exception& e) {
913✔
3015
    SLOG(g_log << Logger::Error << "STL Exception: " << e.what() << endl,
×
3016
         log->error(Logr::Error, e.what(), "Exception in RecursorThread", "exception", Logging::Loggable("std::exception")));
×
3017
  }
×
3018
  catch (...) {
913✔
3019
    SLOG(g_log << Logger::Error << "any other exception in main: " << endl,
×
3020
         log->info(Logr::Error, "Exception in RecursorThread"));
×
3021
  }
×
3022
}
913✔
3023

3024
static pair<int, bool> doYamlConfig(int argc, char* argv[], const pdns::rust::settings::rec::Recursorsettings& settings) // NOLINT: Posix API
3025
{
7✔
3026
  if (!::arg().mustDo("config")) {
7!
3027
    return {0, false};
7✔
3028
  }
7✔
3029
  const string config = ::arg()["config"];
×
3030
  if (config == "diff" || config.empty()) {
×
3031
    ::arg().parse(argc, argv);
×
3032
    ProxyMapping proxyMapping;
×
3033
    LuaConfigItems lci;
×
3034
    pdns::settings::rec::fromBridgeStructToLuaConfig(settings, lci, proxyMapping);
×
3035
    auto yaml = settings.to_yaml_string();
×
3036
    cout << yaml << endl;
×
3037
  }
×
3038
  else if (config == "default") {
×
3039
    auto yaml = pdns::settings::rec::defaultsToYaml();
×
3040
    cout << yaml << endl;
×
3041
  }
×
3042
  else if (config == "check") {
×
3043
    // Kinda redundant, if we came here we already read and checked the config....x
3044
  }
×
3045
  return {0, true};
×
3046
}
7✔
3047

3048
static pair<int, bool> doConfig(Logr::log_t startupLog, const string& configname, int argc, char* argv[]) // NOLINT: Posix API
3049
{
168✔
3050
  if (::arg().mustDo("config")) {
168!
3051
    string config = ::arg()["config"];
×
3052
    if (config == "check") {
×
3053
      try {
×
3054
        if (!::arg().file(configname)) {
×
3055
          SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl,
×
3056
               startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname)));
×
3057
          return {1, true};
×
3058
        }
×
3059
        ::arg().parse(argc, argv);
×
3060
        return {0, true};
×
3061
      }
×
3062
      catch (const ArgException& argException) {
×
3063
        SLOG(g_log << Logger::Warning << "Unable to parse configuration file '" << configname << "': " << argException.reason << endl,
×
3064
             startupLog->error("Cannot parse configuration", "Unable to parse configuration file", "config_file", Logging::Loggable(configname), "reason", Logging::Loggable(argException.reason)));
×
3065
        return {1, true};
×
3066
      }
×
3067
    }
×
3068
    else if (config == "default" || config.empty()) {
×
3069
      auto yaml = pdns::settings::rec::defaultsToYaml();
×
3070
      cout << yaml << endl;
×
3071
    }
×
3072
    else if (config == "diff") {
×
3073
      if (!::arg().laxFile(configname)) {
×
3074
        SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl,
×
3075
             startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname)));
×
3076
        return {1, true};
×
3077
      }
×
3078
      ::arg().laxParse(argc, argv);
×
3079
      cout << ::arg().configstring(true, false);
×
3080
    }
×
3081
    else {
×
3082
      if (!::arg().laxFile(configname)) {
×
3083
        SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl,
×
3084
             startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname)));
×
3085
        return {1, true};
×
3086
      }
×
3087
      ::arg().laxParse(argc, argv);
×
3088
      cout << ::arg().configstring(true, true);
×
3089
    }
×
3090
    return {0, true};
×
3091
  }
×
3092
  return {0, false};
168✔
3093
}
168✔
3094

3095
LockGuarded<pdns::rust::settings::rec::Recursorsettings> g_yamlStruct;
3096

3097
static void runStartStopLua(bool start, Logr::log_t log)
3098
{
350✔
3099
  auto settings = g_yamlStruct.lock();
350✔
3100
  const auto& script = settings->recursor.lua_start_stop_script;
350✔
3101
  if (script.empty()) {
350!
3102
    return;
350✔
3103
  }
350✔
3104
  auto lua = std::make_shared<RecursorLua4>();
×
3105
  lua->runStartStopFunction(std::string(script), start, log);
×
3106
}
×
3107

3108
static void handleRuntimeDefaults(Logr::log_t log)
3109
{
175✔
3110
#ifdef HAVE_FIBER_SANITIZER
175✔
3111
  // Asan needs more stack
3112
  if (::arg().asNum("stack-size") == 200000) { // the default in table.py
175!
3113
    ::arg().set("stack-size", "stack size per mthread") = "600000";
175✔
3114
  }
175✔
3115
#endif
175✔
3116

3117
  const string RUNTIME = "*runtime determined*";
175✔
3118
  if (::arg()["version-string"] == RUNTIME) { // i.e. not set explicitly
175✔
3119
    ::arg().set("version-string") = fullVersionString();
174✔
3120
  }
174✔
3121

3122
  if (::arg()["server-id"] == RUNTIME) { // i.e. not set explicitly
175✔
3123
    auto myHostname = getHostname();
174✔
3124
    if (!myHostname.has_value()) {
174!
3125
      SLOG(g_log << Logger::Warning << "Unable to get the hostname, NSID and id.server values will be empty" << endl,
×
3126
           log->info(Logr::Warning, "Unable to get the hostname, NSID and id.server values will be empty"));
×
3127
    }
×
3128
    ::arg().set("server-id") = myHostname.has_value() ? *myHostname : "";
174!
3129
  }
174✔
3130

3131
  if (::arg()["socket-dir"].empty()) {
175!
3132
    auto* runtimeDir = getenv("RUNTIME_DIRECTORY"); // NOLINT(concurrency-mt-unsafe,cppcoreguidelines-pro-type-vararg)
×
3133
    if (runtimeDir != nullptr) {
×
3134
      ::arg().set("socket-dir") = runtimeDir;
×
3135
    }
×
3136
  }
×
3137

3138
  if (::arg()["socket-dir"].empty()) {
175!
3139
    if (::arg()["chroot"].empty()) {
×
3140
      ::arg().set("socket-dir") = std::string(LOCALSTATEDIR) + "/pdns-recursor";
×
3141
    }
×
3142
    else {
×
3143
      ::arg().set("socket-dir") = "/";
×
3144
    }
×
3145
  }
×
3146

3147
  if (::arg().asNum("threads") == 1) {
175✔
3148
    if (::arg().mustDo("pdns-distributes-queries")) {
3!
3149
      SLOG(g_log << Logger::Warning << "Only one thread, no need to distribute queries ourselves" << endl,
3✔
3150
           log->info(Logr::Warning, "Only one thread, no need to distribute queries ourselves"));
3✔
3151
      ::arg().set("pdns-distributes-queries") = "no";
3✔
3152
    }
3✔
3153
  }
3✔
3154

3155
  if (::arg().mustDo("pdns-distributes-queries") && ::arg().asNum("distributor-threads") == 0) {
175!
3156
    SLOG(g_log << Logger::Warning << "Asked to run with pdns-distributes-queries set but no distributor threads, raising to 1" << endl,
14✔
3157
         log->info(Logr::Warning, "Asked to run with pdns-distributes-queries set but no distributor threads, raising to 1"));
14✔
3158
    ::arg().set("distributor-threads") = "1";
14✔
3159
  }
14✔
3160

3161
  if (!::arg().mustDo("pdns-distributes-queries") && ::arg().asNum("distributor-threads") > 0) {
175!
3162
    SLOG(g_log << Logger::Warning << "Not distributing queries, setting distributor threads to 0" << endl,
×
3163
         log->info(Logr::Warning, "Not distributing queries, setting distributor threads to 0"));
×
3164
    ::arg().set("distributor-threads") = "0";
×
3165
  }
×
3166
}
175✔
3167

3168
static void setupLogging(const string& logname)
3169
{
175✔
3170
  if (logname == "systemd-journal") {
175!
3171
#ifdef HAVE_SYSTEMD
×
3172
    if (int fileDesc = sd_journal_stream_fd("pdns-recusor", LOG_DEBUG, 0); fileDesc >= 0) {
×
3173
      g_slog = Logging::Logger::create(loggerSDBackend);
×
3174
      close(fileDesc);
×
3175
    }
×
3176
#endif
×
3177
    if (g_slog == nullptr) {
×
3178
      cerr << "Requested structured logging to systemd-journal, but it is not available" << endl;
×
3179
    }
×
3180
  }
×
3181
  else if (logname == "json") {
175!
3182
    g_slog = Logging::Logger::create(loggerJSONBackend);
×
3183
    if (g_slog == nullptr) {
×
3184
      cerr << "JSON logging requested but it is not available" << endl;
×
3185
    }
×
3186
  }
×
3187

3188
  if (g_slog == nullptr) {
175!
3189
    g_slog = Logging::Logger::create(loggerBackend);
175✔
3190
  }
175✔
3191
}
175✔
3192

3193
DoneRunning g_doneRunning;
3194

3195
int main(int argc, char** argv)
3196
{
177✔
3197
  g_argc = argc;
177✔
3198
  g_argv = argv;
177✔
3199
  versionSetProduct(ProductRecursor);
177✔
3200
  reportAllTypes();
177✔
3201

3202
  int ret = EXIT_SUCCESS;
177✔
3203

3204
  try {
177✔
3205
    pdns::settings::rec::defineOldStyleSettings();
177✔
3206
    ::arg().setDefaults();
177✔
3207
    g_log.toConsole(Logger::Info);
177✔
3208
    ::arg().laxParse(argc, argv); // do a lax parse
177✔
3209

3210
    if (::arg().mustDo("version")) {
177✔
3211
      cout << getProductVersion();
2✔
3212
      cout << getBuildConfiguration();
2✔
3213
      return 0;
2✔
3214
    }
2✔
3215
    if (::arg().mustDo("help")) {
175!
3216
      cout << "syntax:" << endl
×
3217
           << endl;
×
3218
      cout << ::arg().helpstring(::arg()["help"]) << endl;
×
3219
      return 0;
×
3220
    }
×
3221

3222
    // Pick up options given on command line to setup logging asap.
3223
    g_quiet = ::arg().mustDo("quiet");
175✔
3224
    s_logUrgency = (Logger::Urgency)::arg().asNum("loglevel");
175✔
3225
    s_structured_logger_backend = ::arg()["structured-logging-backend"];
175✔
3226

3227
    if (!g_quiet && s_logUrgency < Logger::Info) { // Logger::Info=6, Logger::Debug=7
175!
3228
      s_logUrgency = Logger::Info; // if you do --quiet=no, you need Info to also see the query log
×
3229
    }
×
3230
    g_log.setLoglevel(s_logUrgency);
175✔
3231
    g_log.toConsole(s_logUrgency);
175✔
3232

3233
    for (const string& line : getProductVersionLines()) {
525✔
3234
      g_log << Logger::Info << line << endl;
525✔
3235
    }
525✔
3236
    if (!::arg().mustDo("structured-logging")) {
175!
3237
      g_log << Logger::Error << "Disabling structured logging is not supported anymore" << endl;
×
3238
    }
×
3239

3240
    g_yamlSettings = false;
175✔
3241
    string configname = ::arg()["config-dir"] + "/recursor";
175✔
3242
    if (!::arg()["config-name"].empty()) {
175!
3243
      configname = ::arg()["config-dir"] + "/recursor-" + ::arg()["config-name"];
×
3244
      g_programname += "-" + ::arg()["config-name"];
×
3245
    }
×
3246
    cleanSlashes(configname);
175✔
3247

3248
    if (!::arg().getCommands().empty()) {
175!
3249
      cerr << "Fatal: non-option";
×
3250
      if (::arg().getCommands().size() > 1) {
×
3251
        cerr << "s";
×
3252
      }
×
3253
      cerr << " (";
×
3254
      bool first = true;
×
3255
      for (const auto& command : ::arg().getCommands()) {
×
3256
        if (!first) {
×
3257
          cerr << ", ";
×
3258
        }
×
3259
        first = false;
×
3260
        cerr << command;
×
3261
      }
×
3262
      cerr << ") on the command line, perhaps a '--setting=123' statement missed the '='?" << endl;
×
3263
      return 99;
×
3264
    }
×
3265

3266
    setupLogging(s_structured_logger_backend);
175✔
3267

3268
    // Missing: a mechanism to call setVerbosity(x)
3269
    auto startupLog = g_slog->withName("config");
175✔
3270
    g_slogtcpin = g_slog->withName("in")->withValues("proto", Logging::Loggable("tcp"));
175✔
3271
    g_slogudpin = g_slog->withName("in")->withValues("proto", Logging::Loggable("udp"));
175✔
3272
    g_slogout = g_slog->withName("out");
175✔
3273

3274
    ::arg().setSLog(startupLog);
175✔
3275

3276
    string yamlconfigname;
175✔
3277
    pdns::rust::settings::rec::Recursorsettings settings;
175✔
3278
    pdns::settings::rec::YamlSettingsStatus yamlstatus{};
175✔
3279

3280
    for (const string suffix : {".yml", ".conf"}) {
343✔
3281
      yamlconfigname = configname + suffix;
343✔
3282
      yamlstatus = pdns::settings::rec::tryReadYAML(yamlconfigname, true, g_yamlSettings, g_luaSettingsInYAML, settings, startupLog);
343✔
3283
      if (yamlstatus == pdns::settings::rec::YamlSettingsStatus::OK) {
343✔
3284
        g_yamlSettingsSuffix = suffix;
7✔
3285
        break;
7✔
3286
      }
7✔
3287
      if (suffix == ".yml" && yamlstatus == pdns::settings::rec::PresentButFailed) {
336!
3288
        return 1;
×
3289
      }
×
3290
    }
336✔
3291

3292
    if (g_yamlSettings) {
175✔
3293
      bool mustExit = false;
7✔
3294
      std::tie(ret, mustExit) = doYamlConfig(argc, argv, settings);
7✔
3295
      if (ret != 0 || mustExit) {
7!
3296
        return ret;
×
3297
      }
×
3298
    }
7✔
3299
    if (yamlstatus == pdns::settings::rec::YamlSettingsStatus::OK) {
175✔
3300
      auto lock = g_yamlStruct.lock();
7✔
3301
      *lock = std::move(settings);
7✔
3302
    }
7✔
3303
    else {
168✔
3304
      configname += ".conf";
168✔
3305
      startupLog->info(Logr::Warning, "Trying to read YAML from .yml or .conf failed, falling back to old-style config read", "configname", Logging::Loggable(configname));
168✔
3306
      bool mustExit = false;
168✔
3307
      std::tie(ret, mustExit) = doConfig(startupLog, configname, argc, argv);
168✔
3308
      if (ret != 0 || mustExit) {
168!
3309
        return ret;
×
3310
      }
×
3311
      if (!::arg().file(configname)) {
168✔
3312
        SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl,
15✔
3313
             startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname)));
15✔
3314
      }
15✔
3315
      else {
153✔
3316
        if (!::arg().mustDo("enable-old-settings")) {
153!
3317
          startupLog->info(Logr::Error, "Old-style settings syntax not supported by default anymore", "configname", Logging::Loggable(configname));
×
3318
          startupLog->info(Logr::Error, "Convert to YAML settings. If not feasible use --enable-old-settings on the command line. This option will be removed in a future release.");
×
3319
          return EXIT_FAILURE;
×
3320
        }
×
3321
        startupLog->info(Logr::Warning, "Convert to YAML settings. The --enable-old-settings option on the command line will be removed in a future release.");
153✔
3322
      }
153✔
3323
    }
168✔
3324

3325
    // Reparse, now with config file as well, both for old-style as for YAML settings
3326
    ::arg().parse(argc, argv);
175✔
3327

3328
    g_quiet = ::arg().mustDo("quiet");
175✔
3329
    s_logUrgency = (Logger::Urgency)::arg().asNum("loglevel");
175✔
3330

3331
    if (s_logUrgency < Logger::Error) {
175!
3332
      s_logUrgency = Logger::Error;
×
3333
    }
×
3334
    if (!g_quiet && s_logUrgency < Logger::Info) { // Logger::Info=6, Logger::Debug=7
175!
3335
      s_logUrgency = Logger::Info; // if you do --quiet=no, you need Info to also see the query log
×
3336
    }
×
3337
    g_log.setLoglevel(s_logUrgency);
175✔
3338
    g_log.toConsole(s_logUrgency);
175✔
3339

3340
    if (!::arg()["chroot"].empty() && !::arg()["api-config-dir"].empty()) {
175!
3341
      SLOG(g_log << Logger::Error << "Using chroot and enabling the API is not possible" << endl,
×
3342
           startupLog->info(Logr::Error, "Cannot use chroot and enable the API at the same time"));
×
3343
      return EXIT_FAILURE;
×
3344
    }
×
3345

3346
    handleRuntimeDefaults(startupLog);
175✔
3347

3348
    if (auto ttl = ::arg().asNum("system-resolver-ttl"); ttl != 0) {
175✔
3349
      time_t interval = ttl;
2✔
3350
      if (::arg().asNum("system-resolver-interval") != 0) {
2!
3351
        interval = ::arg().asNum("system-resolver-interval");
×
3352
      }
×
3353
      bool selfResolveCheck = ::arg().mustDo("system-resolver-self-resolve-check");
2✔
3354
      // Cannot use SyncRes::s_serverID, it is not set yet
3355
      pdns::RecResolve::setInstanceParameters(arg()["server-id"], ttl, interval, selfResolveCheck, []() { reloadZoneConfiguration(g_yamlSettings); });
2✔
3356
    }
2✔
3357

3358
    g_recCache = std::make_unique<MemRecursorCache>(::arg().asNum("record-cache-shards"));
175✔
3359
    g_negCache = std::make_unique<NegCache>(::arg().asNum("record-cache-shards") / 8);
175✔
3360
    if (!::arg().mustDo("disable-packetcache")) {
175✔
3361
      g_maxPacketCacheEntries = ::arg().asNum("max-packetcache-entries");
168✔
3362
      g_packetCache = std::make_unique<RecursorPacketCache>(g_maxPacketCacheEntries, ::arg().asNum("packetcache-shards"));
168✔
3363
    }
168✔
3364

3365
    ret = serviceMain(startupLog);
175✔
3366
    {
175✔
3367
      std::scoped_lock lock(g_doneRunning.mutex);
175✔
3368
      g_doneRunning.done = true;
175✔
3369
      g_doneRunning.condVar.notify_one();
175✔
3370
    }
175✔
3371
    RecThreadInfo::joinThread0();
175✔
3372
  }
175✔
3373
  catch (const PDNSException& ae) {
177✔
3374
    SLOG(g_log << Logger::Error << "Exception: " << ae.reason << endl,
×
3375
         g_slog->withName("config")->error(Logr::Critical, ae.reason, "Fatal error", "exception", Logging::Loggable("PDNSException")));
×
3376
    ret = EXIT_FAILURE;
×
3377
  }
×
3378
  catch (const std::exception& e) {
177✔
3379
    SLOG(g_log << Logger::Error << "STL Exception: " << e.what() << endl,
×
3380
         g_slog->withName("config")->error(Logr::Critical, e.what(), "Fatal error", "exception", Logging::Loggable("std::exception")));
×
3381
    ret = EXIT_FAILURE;
×
3382
  }
×
3383
  catch (...) {
177✔
3384
    SLOG(g_log << Logger::Error << "any other exception in main: " << endl,
×
3385
         g_slog->withName("config")->info(Logr::Critical, "Fatal error"));
×
3386
    ret = EXIT_FAILURE;
×
3387
  }
×
3388

3389
  return ret;
175✔
3390
}
177✔
3391

3392
static RecursorControlChannel::Answer* doReloadLuaScript()
3393
{
×
3394
  string fname = ::arg()["lua-dns-script"];
×
3395
  auto log = g_slog->withName("runtime")->withValues("name", Logging::Loggable(fname));
×
3396
  try {
×
3397
    if (fname.empty()) {
×
3398
      t_pdl.reset();
×
3399
      SLOG(g_log << Logger::Info << RecThreadInfo::id() << " Unloaded current lua script" << endl,
×
3400
           log->info(Logr::Info, "Unloaded current lua script"));
×
3401
      return new RecursorControlChannel::Answer{0, string("unloaded\n")};
×
3402
    }
×
3403

3404
    t_pdl = std::make_shared<RecursorLua4>();
×
3405
    try {
×
3406
      t_pdl->loadFile(fname);
×
3407
    }
×
3408
    catch (std::runtime_error& ex) {
×
3409
      string msg = std::to_string(RecThreadInfo::thread_local_id()) + " Retaining current script, could not read '" + fname + "': " + ex.what();
×
3410
      SLOG(g_log << Logger::Error << msg << endl,
×
3411
           log->error(Logr::Error, ex.what(), "Retaining current script, could not read new script"));
×
3412
      return new RecursorControlChannel::Answer{1, msg + "\n"};
×
3413
    }
×
3414
  }
×
3415
  catch (std::exception& e) {
×
3416
    SLOG(g_log << Logger::Error << RecThreadInfo::thread_local_id() << " Retaining current script, error from '" << fname << "': " << e.what() << endl,
×
3417
         log->error(Logr::Error, e.what(), "Retaining current script, error in new script"));
×
3418
    return new RecursorControlChannel::Answer{1, string("retaining current script, error from '" + fname + "': " + e.what() + "\n")};
×
3419
  }
×
3420

3421
  SLOG(g_log << Logger::Warning << RecThreadInfo::id() << " (Re)loaded lua script from '" << fname << "'" << endl,
×
3422
       log->info(Logr::Warning, "(Re)loaded lua script"));
×
3423
  return new RecursorControlChannel::Answer{0, string("(re)loaded '" + fname + "'\n")};
×
3424
}
×
3425

3426
RecursorControlChannel::Answer doQueueReloadLuaScript(vector<string>::const_iterator begin, vector<string>::const_iterator end)
3427
{
×
3428
  if (begin != end) {
×
3429
    ::arg().set("lua-dns-script") = *begin;
×
3430
  }
×
3431

3432
  return broadcastAccFunction<RecursorControlChannel::Answer>(doReloadLuaScript);
×
3433
}
×
3434

3435
static string* pleaseUseNewTraceRegex(const std::string& newRegex, int file)
3436
{
×
3437
  try {
×
3438
    if (newRegex.empty()) {
×
3439
      t_traceRegex.reset();
×
3440
      t_tracefd = FDWrapper();
×
3441
      return new string("unset\n");
×
3442
    }
×
3443
    if (file == -1) {
×
3444
      return new string("could not dup file\n");
×
3445
    }
×
3446
    t_traceRegex = std::make_shared<Regex>(newRegex);
×
3447
    t_tracefd = file;
×
3448
    return new string("ok\n"); // NOLINT(cppcoreguidelines-owning-memory): it's the API
×
3449
  }
×
3450
  catch (const PDNSException& ae) {
×
3451
    return new string(ae.reason + "\n"); // NOLINT(cppcoreguidelines-owning-memory): it's the API
×
3452
  }
×
3453
}
×
3454

3455
string doTraceRegex(FDWrapper file, vector<string>::const_iterator begin, vector<string>::const_iterator end)
3456
{
×
3457
  int fileno = dup(file);
×
3458
  // Potential dup failure handled in pleaseUseNewTraceRegex()
3459
  return broadcastAccFunction<string>([=] { return pleaseUseNewTraceRegex(begin != end ? *begin : "", fileno); });
×
3460
}
×
3461

3462
struct WipeCacheResult wipeCaches(const DNSName& canon, bool subtree, uint16_t qtype)
3463
{
756✔
3464
  struct WipeCacheResult res;
756✔
3465

3466
  try {
756✔
3467
    res.record_count = static_cast<int>(g_recCache->doWipeCache(canon, subtree, qtype));
756✔
3468
    // scanbuild complains here about an allocated function object that is being leaked. Needs investigation
3469
    if (g_packetCache) {
756✔
3470
      res.packet_count = static_cast<int>(g_packetCache->doWipePacketCache(canon, qtype, subtree));
405✔
3471
    }
405✔
3472
    res.negative_record_count = static_cast<int>(g_negCache->wipe(canon, subtree));
756✔
3473
    if (g_aggressiveNSECCache) {
756✔
3474
      g_aggressiveNSECCache->removeZoneInfo(canon, subtree);
588✔
3475
    }
588✔
3476
  }
756✔
3477
  catch (const std::exception& e) {
756✔
3478
    auto log = g_slog->withName("runtime");
×
3479
    SLOG(g_log << Logger::Warning << ", failed: " << e.what() << endl,
×
3480
         log->error(Logr::Warning, e.what(), "Wipecache failed"));
×
3481
  }
×
3482

3483
  return res;
756✔
3484
}
756✔
3485

3486
void startLuaConfigDelayedThreads(const LuaConfigItems& luaConfig, uint64_t generation)
3487
{
175✔
3488
  for (const auto& rpzPrimary : luaConfig.rpzs) {
175✔
3489
    if (rpzPrimary.zoneXFRParams.primaries.empty()) {
17✔
3490
      continue;
15✔
3491
    }
15✔
3492
    try {
2✔
3493
      // RPZIXTracker uses call by value for its args. That is essential, since we want copies so
3494
      // that RPZIXFRTracker gets values with the proper lifetime.
3495
      std::thread theThread(RPZIXFRTracker, rpzPrimary, generation);
2✔
3496
      theThread.detach();
2✔
3497
    }
2✔
3498
    catch (const std::exception& e) {
2✔
3499
      SLOG(g_log << Logger::Error << "Problem starting RPZIXFRTracker thread: " << e.what() << endl,
×
3500
           g_slog->withName("rpz")->error(Logr::Error, e.what(), "Exception starting RPZIXFRTracker thread", "exception", Logging::Loggable("std::exception")));
×
3501
      exit(1); // NOLINT(concurrency-mt-unsafe)
×
3502
    }
×
3503
    catch (const PDNSException& e) {
2✔
3504
      SLOG(g_log << Logger::Error << "Problem starting RPZIXFRTracker thread: " << e.reason << endl,
×
3505
           g_slog->withName("rpz")->error(Logr::Error, e.reason, "Exception starting RPZIXFRTracker thread", "exception", Logging::Loggable("PDNSException")));
×
3506
      exit(1); // NOLINT(concurrency-mt-unsafe)
×
3507
    }
×
3508
  }
2✔
3509
  for (const auto& fcz : luaConfig.catalogzones) {
175✔
3510
    if (fcz.d_params.primaries.empty()) {
1!
3511
      continue;
×
3512
    }
×
3513
    try {
1✔
3514
      // ZoneXFRTracker uses call by value for its args. That is essential, since we want copies so
3515
      // that ZoneXFRTracker gets values with the proper lifetime.
3516
      std::thread theThread(FWCatZoneXFR::zoneXFRTracker, fcz.d_params, generation);
1✔
3517
      theThread.detach();
1✔
3518
    }
1✔
3519
    catch (const std::exception& e) {
1✔
3520
      SLOG(g_log << Logger::Error << "Problem starting ZoneIXFRTracker thread: " << e.what() << endl,
×
3521
           g_slog->withName("zone")->error(Logr::Error, e.what(), "Exception starting ZoneXFRTracker thread", "exception", Logging::Loggable("std::exception")));
×
3522
      exit(1); // NOLINT(concurrency-mt-unsafe)
×
3523
    }
×
3524
    catch (const PDNSException& e) {
1✔
3525
      SLOG(g_log << Logger::Error << "Problem starting ZoneIXFRTracker thread: " << e.reason << endl,
×
3526
           g_slog->withName("zone")->error(Logr::Error, e.reason, "Exception starting ZoneXFRTracker thread", "exception", Logging::Loggable("PDNSException")));
×
3527
      exit(1); // NOLINT(concurrency-mt-unsafe)
×
3528
    }
×
3529
  }
1✔
3530
}
175✔
3531

3532
static void* pleaseInitPolCounts(const string& name)
3533
{
17✔
3534
  if (t_Counters.at(rec::PolicyNameHits::policyName).counts.count(name) == 0) {
17!
3535
    t_Counters.at(rec::PolicyNameHits::policyName).counts[name] = 0;
17✔
3536
  }
17✔
3537
  return nullptr;
17✔
3538
}
17✔
3539

3540
static bool activateRPZFile(const RPZTrackerParams& params, LuaConfigItems& lci, shared_ptr<DNSFilterEngine::Zone>& zone)
3541
{
15✔
3542
  auto log = lci.d_slog->withValues("file", Logging::Loggable(params.zoneXFRParams.name));
15✔
3543

3544
  zone->setName(params.polName.empty() ? "rpzFile" : params.polName);
15!
3545
  try {
15✔
3546
    SLOG(g_log << Logger::Warning << "Loading RPZ from file '" << params.name << "'" << endl,
15✔
3547
         log->info(Logr::Info, "Loading RPZ from file"));
15✔
3548
    loadRPZFromFile(params.zoneXFRParams.name, zone, params.defpol, params.defpolOverrideLocal, params.maxTTL);
15✔
3549
    SLOG(g_log << Logger::Warning << "Done loading RPZ from file '" << params.name << "'" << endl,
15✔
3550
         log->info(Logr::Info, "Done loading RPZ from file"));
15✔
3551
  }
15✔
3552
  catch (const std::exception& e) {
15✔
3553
    SLOG(g_log << Logger::Error << "Unable to load RPZ zone from '" << params.name << "': " << e.what() << endl,
×
3554
         log->error(Logr::Error, e.what(), "Exception while loading RPZ zone from file"));
×
3555
    zone->clear();
×
3556
    return false;
×
3557
  }
×
3558
  return true;
15✔
3559
}
15✔
3560

3561
static void activateRPZPrimary(RPZTrackerParams& params, LuaConfigItems& lci, shared_ptr<DNSFilterEngine::Zone>& zone, const DNSName& domain)
3562
{
2✔
3563
  auto log = lci.d_slog->withValues("seedfile", Logging::Loggable(params.seedFileName), "zone", Logging::Loggable(params.zoneXFRParams.name));
2✔
3564

3565
  if (!params.seedFileName.empty()) {
2!
3566
    SLOG(g_log << Logger::Info << "Pre-loading RPZ zone " << params.name << " from seed file '" << params.seedFileName << "'" << endl,
×
3567
         log->info(Logr::Info, "Pre-loading RPZ zone from seed file"));
×
3568
    try {
×
3569
      params.zoneXFRParams.soaRecordContent = loadRPZFromFile(params.seedFileName, zone, params.defpol, params.defpolOverrideLocal, params.maxTTL);
×
3570

3571
      if (zone->getDomain() != domain) {
×
3572
        throw PDNSException("The RPZ zone " + params.zoneXFRParams.name + " loaded from the seed file (" + zone->getDomain().toString() + ") does not match the one passed in parameter (" + domain.toString() + ")");
×
3573
      }
×
3574

3575
      if (params.zoneXFRParams.soaRecordContent == nullptr) {
×
3576
        throw PDNSException("The RPZ zone " + params.zoneXFRParams.name + " loaded from the seed file (" + zone->getDomain().toString() + ") has no SOA record");
×
3577
      }
×
3578
    }
×
3579
    catch (const PDNSException& e) {
×
3580
      SLOG(g_log << Logger::Warning << "Unable to pre-load RPZ zone " << params.name << " from seed file '" << params.seedFileName << "': " << e.reason << endl,
×
3581
           log->error(Logr::Warning, e.reason, "Exception while pre-loading RPZ zone", "exception", Logging::Loggable("PDNSException")));
×
3582
      zone->clear();
×
3583
    }
×
3584
    catch (const std::exception& e) {
×
3585
      SLOG(g_log << Logger::Warning << "Unable to pre-load RPZ zone " << params.name << " from seed file '" << params.seedFileName << "': " << e.what() << endl,
×
3586
           log->error(Logr::Warning, e.what(), "Exception while pre-loading RPZ zone", "exception", Logging::Loggable("std::exception")));
×
3587
      zone->clear();
×
3588
    }
×
3589
  }
×
3590
}
2✔
3591

3592
static void activateRPZs(LuaConfigItems& lci)
3593
{
157✔
3594
  for (auto& params : lci.rpzs) {
157✔
3595
    auto zone = std::make_shared<DNSFilterEngine::Zone>();
17✔
3596
    if (params.zoneXFRParams.zoneSizeHint != 0) {
17!
3597
      zone->reserve(params.zoneXFRParams.zoneSizeHint);
×
3598
    }
×
3599
    if (!params.tags.empty()) {
17✔
3600
      std::unordered_set<std::string> tags;
1✔
3601
      for (const auto& tag : params.tags) {
2✔
3602
        tags.emplace(tag);
2✔
3603
      }
2✔
3604
      zone->setTags(tags);
1✔
3605
    }
1✔
3606
    zone->setPolicyOverridesGettag(params.defpolOverrideLocal);
17✔
3607
    if (params.extendedErrorCode != std::numeric_limits<uint32_t>::max()) {
17✔
3608
      zone->setExtendedErrorCode(params.extendedErrorCode);
1✔
3609
      if (!params.extendedErrorExtra.empty()) {
1!
3610
        zone->setExtendedErrorExtra(params.extendedErrorExtra);
1✔
3611
      }
1✔
3612
    }
1✔
3613
    zone->setIncludeSOA(params.includeSOA);
17✔
3614
    zone->setIgnoreDuplicates(params.ignoreDuplicates);
17✔
3615

3616
    if (params.zoneXFRParams.primaries.empty()) {
17✔
3617
      if (activateRPZFile(params, lci, zone)) {
15!
3618
        lci.dfe.addZone(zone);
15✔
3619
      }
15✔
3620
    }
15✔
3621
    else {
2✔
3622
      DNSName domain(params.zoneXFRParams.name);
2✔
3623
      zone->setDomain(domain);
2✔
3624
      zone->setName(params.polName.empty() ? params.zoneXFRParams.name : params.polName);
2!
3625
      params.zoneXFRParams.zoneIdx = lci.dfe.addZone(zone);
2✔
3626
      activateRPZPrimary(params, lci, zone, domain);
2✔
3627
    }
2✔
3628
    broadcastFunction([name = zone->getName()] { return pleaseInitPolCounts(name); });
17✔
3629
  }
17✔
3630
}
157✔
3631

3632
static void activateForwardingCatalogZones(LuaConfigItems& lci)
3633
{
157✔
3634
  size_t idx = 0;
157✔
3635
  for (auto& fcz : lci.catalogzones) {
157✔
3636

3637
    auto& params = fcz.d_params;
1✔
3638
    params.zoneIdx = idx++;
1✔
3639
    auto zone = std::make_shared<CatalogZone>();
1✔
3640
    // zoneSizeHint ignored
3641
    zone->setName(DNSName(params.name));
1✔
3642
    fcz.d_catz = std::move(zone);
1✔
3643
  }
1✔
3644
}
157✔
3645

3646
void activateLuaConfig(LuaConfigItems& lci)
3647
{
157✔
3648
  if (!lci.trustAnchorFileInfo.fname.empty()) {
157✔
3649
    warnIfDNSSECDisabled("Warning: reading Trust Anchors from file, but dnssec is set to 'off'!");
1✔
3650
    updateTrustAnchorsFromFile(lci.trustAnchorFileInfo.fname, lci.dsAnchors, lci.d_slog);
1✔
3651
  }
1✔
3652
  if (lci.dsAnchors.size() > rootDSs.size()) {
157!
3653
    warnIfDNSSECDisabled("Warning: adding Trust Anchor for DNSSEC, but dnssec is set to 'off'!");
×
3654
  }
×
3655
  if (!lci.negAnchors.empty()) {
157✔
3656
    warnIfDNSSECDisabled("Warning: adding Negative Trust Anchor for DNSSEC, but dnssec is set to 'off'!");
2✔
3657
  }
2✔
3658
  activateRPZs(lci);
157✔
3659
  activateForwardingCatalogZones(lci);
157✔
3660
  g_luaconfs.setState(lci);
157✔
3661
}
157✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc