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

PowerDNS / pdns / 9418798096

07 Jun 2024 02:38PM UTC coverage: 64.612% (+0.002%) from 64.61%
9418798096

Pull #14057

github

web-flow
Merge 3ef28144e into b1d2ec1c5
Pull Request #14057: Auth: fix a crash and some cleanup in the auth-catalogzone.cc

37074 of 88162 branches covered (42.05%)

Branch coverage included in aggregate %.

23 of 50 new or added lines in 4 files covered. (46.0%)

45 existing lines in 14 files now uncovered.

124373 of 161708 relevant lines covered (76.91%)

5379791.12 hits per line

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

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

24
#include "rec-main.hh"
25

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

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

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

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

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

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

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

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

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

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

123
std::shared_ptr<Logr::Logger> g_slogtcpin;
124
std::shared_ptr<Logr::Logger> g_slogudpin;
125
std::shared_ptr<Logr::Logger> g_slogudpout;
126

127
/* without reuseport, all listeners share the same sockets */
128
static deferredAdd_t s_deferredUDPadds;
129
static deferredAdd_t s_deferredTCPadds;
130

131
/* first we have the handler thread, t_id == 0 (some other
132
   helper threads like SNMP might have t_id == 0 as well)
133
   then the distributor threads if any
134
   and finally the workers */
135
std::vector<RecThreadInfo> RecThreadInfo::s_threadInfos;
136

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

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

146
static pdns::RateLimitedLog s_rateLimitedLogger;
147

148
static std::map<unsigned int, std::set<int>> parseCPUMap(Logr::log_t log)
149
{
148✔
150
  std::map<unsigned int, std::set<int>> result;
148✔
151

152
  const std::string value = ::arg()["cpu-map"];
148✔
153

154
  if (!value.empty() && !isSettingThreadCPUAffinitySupported()) {
148!
155
    SLOG(g_log << Logger::Warning << "CPU mapping requested but not supported, skipping" << endl,
×
156
         log->info(Logr::Warning, "CPU mapping requested but not supported, skipping"));
×
157
    return result;
×
158
  }
×
159

160
  std::vector<std::string> parts;
148✔
161

162
  stringtok(parts, value, " \t");
148✔
163

164
  for (const auto& part : parts) {
148!
165
    if (part.find('=') == string::npos) {
×
166
      continue;
×
167
    }
×
168

169
    try {
×
170
      auto headers = splitField(part, '=');
×
171
      boost::trim(headers.first);
×
172
      boost::trim(headers.second);
×
173

174
      auto threadId = pdns::checked_stoi<unsigned int>(headers.first);
×
175
      std::vector<std::string> cpus;
×
176

177
      stringtok(cpus, headers.second, ",");
×
178

179
      for (const auto& cpu : cpus) {
×
180
        int cpuId = std::stoi(cpu);
×
181

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

191
  return result;
148✔
192
}
148✔
193

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

227
static void recursorThread();
228

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

241
int RecThreadInfo::runThreads(Logr::log_t log)
242
{
148✔
243
  int ret = EXIT_SUCCESS;
148✔
244
  const auto cpusMap = parseCPUMap(log);
148✔
245

246
  if (RecThreadInfo::numDistributors() + RecThreadInfo::numUDPWorkers() == 1) {
148!
247
    SLOG(g_log << Logger::Warning << "Operating with single UDP distributor/worker thread" << endl,
×
248
         log->info(Logr::Notice, "Operating with single UDP distributor/worker thread"));
×
249

250
    /* This thread handles the web server, carbon, statistics and the control channel */
251
    unsigned int currentThreadId = 0;
×
252
    auto& handlerInfo = RecThreadInfo::info(currentThreadId);
×
253
    handlerInfo.setHandler();
×
254
    handlerInfo.start(currentThreadId, "web+stat", cpusMap, log);
×
255

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

265
    for (unsigned int thread = 0; thread < RecThreadInfo::numTaskThreads(); thread++, currentThreadId++) {
×
266
      auto& taskInfo = RecThreadInfo::info(currentThreadId);
×
267
      taskInfo.setTaskThread();
×
268
      taskInfo.start(currentThreadId, "task", cpusMap, log);
×
269
    }
×
270

271
    currentThreadId = 1;
×
272
    auto& info = RecThreadInfo::info(currentThreadId);
×
273
    info.setListener();
×
274
    info.setWorker();
×
275
    RecThreadInfo::setThreadId(currentThreadId);
×
276
    recursorThread();
×
277

278
    for (unsigned int thread = 0; thread < RecThreadInfo::numRecursorThreads(); thread++) {
×
279
      if (thread == 1) {
×
280
        continue;
×
281
      }
×
282
      auto& tInfo = RecThreadInfo::info(thread);
×
283
      tInfo.thread.join();
×
284
      if (tInfo.exitCode != 0) {
×
285
        ret = tInfo.exitCode;
×
286
      }
×
287
    }
×
288
  }
×
289
  else {
148✔
290
    // Setup RecThreadInfo objects
291
    unsigned int currentThreadId = 1;
148✔
292
    if (RecThreadInfo::weDistributeQueries()) {
148✔
293
      for (unsigned int thread = 0; thread < RecThreadInfo::numDistributors(); thread++, currentThreadId++) {
28✔
294
        RecThreadInfo::info(currentThreadId).setListener();
14✔
295
      }
14✔
296
    }
14✔
297
    for (unsigned int thread = 0; thread < RecThreadInfo::numUDPWorkers(); thread++, currentThreadId++) {
471✔
298
      auto& info = RecThreadInfo::info(currentThreadId);
323✔
299
      info.setListener(!RecThreadInfo::weDistributeQueries());
323✔
300
      info.setWorker();
323✔
301
    }
323✔
302
    for (unsigned int thread = 0; thread < RecThreadInfo::numTCPWorkers(); thread++, currentThreadId++) {
296✔
303
      auto& info = RecThreadInfo::info(currentThreadId);
148✔
304
      info.setTCPListener();
148✔
305
      info.setWorker();
148✔
306
    }
148✔
307
    for (unsigned int thread = 0; thread < RecThreadInfo::numTaskThreads(); thread++, currentThreadId++) {
296✔
308
      auto& info = RecThreadInfo::info(currentThreadId);
148✔
309
      info.setTaskThread();
148✔
310
    }
148✔
311

312
    // And now start the actual threads
313
    currentThreadId = 1;
148✔
314
    if (RecThreadInfo::weDistributeQueries()) {
148✔
315
      SLOG(g_log << Logger::Warning << "Launching " << RecThreadInfo::numDistributors() << " distributor threads" << endl,
14✔
316
           log->info(Logr::Notice, "Launching distributor threads", "count", Logging::Loggable(RecThreadInfo::numDistributors())));
14✔
317
      for (unsigned int thread = 0; thread < RecThreadInfo::numDistributors(); thread++, currentThreadId++) {
28✔
318
        auto& info = RecThreadInfo::info(currentThreadId);
14✔
319
        info.start(currentThreadId, "distr", cpusMap, log);
14✔
320
      }
14✔
321
    }
14✔
322
    SLOG(g_log << Logger::Warning << "Launching " << RecThreadInfo::numUDPWorkers() << " worker threads" << endl,
148✔
323
         log->info(Logr::Notice, "Launching worker threads", "count", Logging::Loggable(RecThreadInfo::numUDPWorkers())));
148✔
324

325
    for (unsigned int thread = 0; thread < RecThreadInfo::numUDPWorkers(); thread++, currentThreadId++) {
471✔
326
      auto& info = RecThreadInfo::info(currentThreadId);
323✔
327
      info.start(currentThreadId, "worker", cpusMap, log);
323✔
328
    }
323✔
329

330
    SLOG(g_log << Logger::Warning << "Launching " << RecThreadInfo::numTCPWorkers() << " tcpworker threads" << endl,
148✔
331
         log->info(Logr::Notice, "Launching tcpworker threads", "count", Logging::Loggable(RecThreadInfo::numTCPWorkers())));
148✔
332

333
    for (unsigned int thread = 0; thread < RecThreadInfo::numTCPWorkers(); thread++, currentThreadId++) {
296✔
334
      auto& info = RecThreadInfo::info(currentThreadId);
148✔
335
      info.start(currentThreadId, "tcpworker", cpusMap, log);
148✔
336
    }
148✔
337

338
    for (unsigned int thread = 0; thread < RecThreadInfo::numTaskThreads(); thread++, currentThreadId++) {
296✔
339
      auto& info = RecThreadInfo::info(currentThreadId);
148✔
340
      info.start(currentThreadId, "task", cpusMap, log);
148✔
341
    }
148✔
342

343
    /* This thread handles the web server, carbon, statistics and the control channel */
344
    currentThreadId = 0;
148✔
345
    auto& info = RecThreadInfo::info(currentThreadId);
148✔
346
    info.setHandler();
148✔
347
    info.start(currentThreadId, "web+stat", cpusMap, log);
148✔
348

349
    for (auto& tInfo : RecThreadInfo::infos()) {
152✔
350
      tInfo.thread.join();
152✔
351
      if (tInfo.exitCode != 0) {
152!
352
        ret = tInfo.exitCode;
×
353
      }
×
354
    }
152✔
355
  }
148✔
356
  return ret;
148✔
357
}
148✔
358

359
void RecThreadInfo::makeThreadPipes(Logr::log_t log)
360
{
148✔
361
  auto pipeBufferSize = ::arg().asNum("distribution-pipe-buffer-size");
148✔
362
  if (pipeBufferSize > 0) {
148!
363
    SLOG(g_log << Logger::Info << "Resizing the buffer of the distribution pipe to " << pipeBufferSize << endl,
×
364
         log->info(Logr::Info, "Resizing the buffer of the distribution pipe", "size", Logging::Loggable(pipeBufferSize)));
×
365
  }
×
366

367
  /* thread 0 is the handler / SNMP, worker threads start at 1 */
368
  for (unsigned int thread = 0; thread < numRecursorThreads(); ++thread) {
929✔
369
    auto& threadInfo = info(thread);
781✔
370

371
    std::array<int, 2> fileDesc{};
781✔
372
    if (pipe(fileDesc.data()) < 0) {
781!
373
      unixDie("Creating pipe for inter-thread communications");
×
374
    }
×
375

376
    threadInfo.pipes.readToThread = fileDesc[0];
781✔
377
    threadInfo.pipes.writeToThread = fileDesc[1];
781✔
378

379
    // handler thread only gets first pipe, not the others
380
    if (thread == 0) {
781✔
381
      continue;
148✔
382
    }
148✔
383

384
    if (pipe(fileDesc.data()) < 0) {
633!
385
      unixDie("Creating pipe for inter-thread communications");
×
386
    }
×
387

388
    threadInfo.pipes.readFromThread = fileDesc[0];
633✔
389
    threadInfo.pipes.writeFromThread = fileDesc[1];
633✔
390

391
    if (pipe(fileDesc.data()) < 0) {
633!
392
      unixDie("Creating pipe for inter-thread communications");
×
393
    }
×
394

395
    threadInfo.pipes.readQueriesToThread = fileDesc[0];
633✔
396
    threadInfo.pipes.writeQueriesToThread = fileDesc[1];
633✔
397

398
    if (pipeBufferSize > 0) {
633!
399
      if (!setPipeBufferSize(threadInfo.pipes.writeQueriesToThread, pipeBufferSize)) {
×
400
        int err = errno;
×
401
        SLOG(g_log << Logger::Warning << "Error resizing the buffer of the distribution pipe for thread " << thread << " to " << pipeBufferSize << ": " << stringerror(err) << endl,
×
402
             log->error(Logr::Warning, err, "Error resizing the buffer of the distribution pipe for thread", "thread", Logging::Loggable(thread), "size", Logging::Loggable(pipeBufferSize)));
×
403
        auto existingSize = getPipeBufferSize(threadInfo.pipes.writeQueriesToThread);
×
404
        if (existingSize > 0) {
×
405
          SLOG(g_log << Logger::Warning << "The current size of the distribution pipe's buffer for thread " << thread << " is " << existingSize << endl,
×
406
               log->info(Logr::Warning, "The current size of the distribution pipe's buffer for thread", "thread", Logging::Loggable(thread), "size", Logging::Loggable(existingSize)));
×
407
        }
×
408
      }
×
409
    }
×
410

411
    if (!setNonBlocking(threadInfo.pipes.writeQueriesToThread)) {
633!
412
      unixDie("Making pipe for inter-thread communications non-blocking");
×
413
    }
×
414
  }
633✔
415
}
148✔
416

417
ArgvMap& arg()
418
{
90,893✔
419
  static ArgvMap theArg;
90,893✔
420
  return theArg;
90,893✔
421
}
90,893✔
422

423
static FDMultiplexer* getMultiplexer(Logr::log_t log)
424
{
781✔
425
  FDMultiplexer* ret = nullptr;
781✔
426
  for (const auto& mplexer : FDMultiplexer::getMultiplexerMap()) {
781!
427
    try {
781✔
428
      ret = mplexer.second(FDMultiplexer::s_maxevents);
781✔
429
      return ret;
781✔
430
    }
781✔
431
    catch (FDMultiplexerException& fe) {
781✔
432
      SLOG(g_log << Logger::Warning << "Non-fatal error initializing possible multiplexer (" << fe.what() << "), falling back" << endl,
×
433
           log->error(Logr::Warning, fe.what(), "Non-fatal error initializing possible multiplexer, falling back"));
×
434
    }
×
435
    catch (...) {
781✔
436
      SLOG(g_log << Logger::Warning << "Non-fatal error initializing possible multiplexer" << endl,
×
437
           log->info(Logr::Warning, "Non-fatal error initializing possible multiplexer"));
×
438
    }
×
439
  }
781✔
UNCOV
440
  SLOG(g_log << Logger::Error << "No working multiplexer found!" << endl,
×
UNCOV
441
       log->info(Logr::Error, "No working multiplexer found!"));
×
UNCOV
442
  _exit(1);
×
443
}
781✔
444

445
static std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>> startProtobufServers(const ProtobufExportConfig& config, Logr::log_t log)
446
{
105✔
447
  auto result = std::make_shared<std::vector<std::unique_ptr<RemoteLogger>>>();
105✔
448

449
  for (const auto& server : config.servers) {
209✔
450
    try {
209✔
451
      auto logger = make_unique<RemoteLogger>(server, config.timeout, 100 * config.maxQueuedEntries, config.reconnectWaitTime, config.asyncConnect);
209✔
452
      logger->setLogQueries(config.logQueries);
209✔
453
      logger->setLogResponses(config.logResponses);
209✔
454
      result->emplace_back(std::move(logger));
209✔
455
    }
209✔
456
    catch (const std::exception& e) {
209✔
457
      SLOG(g_log << Logger::Error << "Error while starting protobuf logger to '" << server << ": " << e.what() << endl,
×
458
           log->error(Logr::Error, e.what(), "Exception while starting protobuf logger", "exception", Logging::Loggable("std::exception"), "server", Logging::Loggable(server)));
×
459
    }
×
460
    catch (const PDNSException& e) {
209✔
461
      SLOG(g_log << Logger::Error << "Error while starting protobuf logger to '" << server << ": " << e.reason << endl,
×
462
           log->error(Logr::Error, e.reason, "Exception while starting protobuf logger", "exception", Logging::Loggable("PDNSException"), "server", Logging::Loggable(server)));
×
463
    }
×
464
  }
209✔
465

466
  return result;
106✔
467
}
105✔
468

469
bool checkProtobufExport(LocalStateHolder<LuaConfigItems>& luaconfsLocal)
470
{
7,971✔
471
  if (!luaconfsLocal->protobufExportConfig.enabled) {
7,983✔
472
    if (t_protobufServers.servers) {
7,764!
473
      t_protobufServers.servers.reset();
×
474
      t_protobufServers.config = luaconfsLocal->protobufExportConfig;
×
475
    }
×
476

477
    return false;
7,764✔
478
  }
7,764✔
479

480
  /* if the server was not running, or if it was running according to a
481
     previous configuration */
482
  if (t_protobufServers.generation < luaconfsLocal->generation && t_protobufServers.config != luaconfsLocal->protobufExportConfig) {
2,147,483,866✔
483

484
    if (t_protobufServers.servers) {
89!
485
      t_protobufServers.servers.reset();
×
486
    }
×
487
    auto log = g_slog->withName("protobuf");
89✔
488
    t_protobufServers.servers = startProtobufServers(luaconfsLocal->protobufExportConfig, log);
89✔
489
    t_protobufServers.config = luaconfsLocal->protobufExportConfig;
89✔
490
    t_protobufServers.generation = luaconfsLocal->generation;
89✔
491
  }
89✔
492

493
  return true;
2,147,483,866✔
494
}
7,971✔
495

496
bool checkOutgoingProtobufExport(LocalStateHolder<LuaConfigItems>& luaconfsLocal)
497
{
7,751✔
498
  if (!luaconfsLocal->outgoingProtobufExportConfig.enabled) {
7,751✔
499
    if (t_outgoingProtobufServers.servers) {
7,706!
500
      t_outgoingProtobufServers.servers.reset();
×
501
      t_outgoingProtobufServers.config = luaconfsLocal->outgoingProtobufExportConfig;
×
502
    }
×
503

504
    return false;
7,706✔
505
  }
7,706✔
506

507
  /* if the server was not running, or if it was running according to a
508
     previous configuration */
509
  if (t_outgoingProtobufServers.generation < luaconfsLocal->generation && t_outgoingProtobufServers.config != luaconfsLocal->outgoingProtobufExportConfig) {
45!
510

511
    if (t_outgoingProtobufServers.servers) {
15!
512
      t_outgoingProtobufServers.servers.reset();
×
513
    }
×
514
    auto log = g_slog->withName("protobuf");
15✔
515
    t_outgoingProtobufServers.servers = startProtobufServers(luaconfsLocal->outgoingProtobufExportConfig, log);
15✔
516
    t_outgoingProtobufServers.config = luaconfsLocal->outgoingProtobufExportConfig;
15✔
517
    t_outgoingProtobufServers.generation = luaconfsLocal->generation;
15✔
518
  }
15✔
519

520
  return true;
45✔
521
}
7,751✔
522

523
void protobufLogQuery(LocalStateHolder<LuaConfigItems>& luaconfsLocal, const boost::uuids::uuid& uniqueId, const ComboAddress& remote, const ComboAddress& local, const ComboAddress& mappedSource, const Netmask& ednssubnet, bool tcp, uint16_t queryID, size_t len, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::unordered_set<std::string>& policyTags, const std::string& requestorId, const std::string& deviceId, const std::string& deviceName, const std::map<std::string, RecursorLua4::MetaValue>& meta)
524
{
23✔
525
  auto log = g_slog->withName("pblq");
23✔
526

527
  if (!t_protobufServers.servers) {
23!
528
    return;
×
529
  }
×
530

531
  ComboAddress requestor;
23✔
532
  if (!luaconfsLocal->protobufExportConfig.logMappedFrom) {
23✔
533
    Netmask requestorNM(remote, remote.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
21!
534
    requestor = requestorNM.getMaskedNetwork();
21✔
535
    requestor.setPort(remote.getPort());
21✔
536
  }
21✔
537
  else {
2✔
538
    Netmask requestorNM(mappedSource, mappedSource.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
2!
539
    requestor = requestorNM.getMaskedNetwork();
2✔
540
    requestor.setPort(mappedSource.getPort());
2✔
541
  }
2✔
542

543
  pdns::ProtoZero::RecMessage msg{128, std::string::size_type(policyTags.empty() ? 0 : 64)}; // It's a guess
23✔
544
  msg.setType(pdns::ProtoZero::Message::MessageType::DNSQueryType);
23✔
545
  msg.setRequest(uniqueId, requestor, local, qname, qtype, qclass, queryID, tcp ? pdns::ProtoZero::Message::TransportProtocol::TCP : pdns::ProtoZero::Message::TransportProtocol::UDP, len);
23!
546
  msg.setServerIdentity(SyncRes::s_serverID);
23✔
547
  msg.setEDNSSubnet(ednssubnet, ednssubnet.isIPv4() ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
23!
548
  msg.setRequestorId(requestorId);
23✔
549
  msg.setDeviceId(deviceId);
23✔
550
  msg.setDeviceName(deviceName);
23✔
551
  msg.setWorkerId(RecThreadInfo::id());
23✔
552
  // For queries, packetCacheHit and outgoingQueries are not relevant
553

554
  if (!policyTags.empty()) {
23✔
555
    msg.addPolicyTags(policyTags);
3✔
556
  }
3✔
557
  for (const auto& mit : meta) {
23✔
558
    msg.setMeta(mit.first, mit.second.stringVal, mit.second.intVal);
2✔
559
  }
2✔
560

561
  std::string strMsg(msg.finishAndMoveBuf());
23✔
562
  for (auto& server : *t_protobufServers.servers) {
46✔
563
    remoteLoggerQueueData(*server, strMsg);
46✔
564
  }
46✔
565
}
23✔
566

567
void protobufLogResponse(pdns::ProtoZero::RecMessage& message)
568
{
25✔
569
  if (!t_protobufServers.servers) {
25!
570
    return;
×
571
  }
×
572

573
  std::string msg(message.finishAndMoveBuf());
25✔
574
  for (auto& server : *t_protobufServers.servers) {
50✔
575
    remoteLoggerQueueData(*server, msg);
50✔
576
  }
50✔
577
}
25✔
578

579
void protobufLogResponse(const struct dnsheader* header, LocalStateHolder<LuaConfigItems>& luaconfsLocal,
580
                         const RecursorPacketCache::OptPBData& pbData, const struct timeval& tval,
581
                         bool tcp, const ComboAddress& source, const ComboAddress& destination,
582
                         const ComboAddress& mappedSource,
583
                         const EDNSSubnetOpts& ednssubnet,
584
                         const boost::uuids::uuid& uniqueId, const string& requestorId, const string& deviceId,
585
                         const string& deviceName, const std::map<std::string, RecursorLua4::MetaValue>& meta,
586
                         const RecEventTrace& eventTrace,
587
                         const std::unordered_set<std::string>& policyTags)
588
{
4✔
589
  pdns::ProtoZero::RecMessage pbMessage(pbData ? pbData->d_message : "", pbData ? pbData->d_response : "", 64, 10); // The extra bytes we are going to add
4!
590
  // Normally we take the immutable string from the cache and append a few values, but if it's not there (can this happen?)
591
  // we start with an empty string and append the minimal
592
  if (!pbData) {
4!
593
    pbMessage.setType(pdns::ProtoZero::Message::MessageType::DNSResponseType);
×
594
    pbMessage.setServerIdentity(SyncRes::s_serverID);
×
595
  }
×
596

597
  // In response part
598
  if (g_useKernelTimestamp && tval.tv_sec != 0) {
4!
599
    pbMessage.setQueryTime(tval.tv_sec, tval.tv_usec);
×
600
  }
×
601
  else {
4✔
602
    pbMessage.setQueryTime(g_now.tv_sec, g_now.tv_usec);
4✔
603
  }
4✔
604

605
  // In message part
606
  if (!luaconfsLocal->protobufExportConfig.logMappedFrom) {
4!
607
    pbMessage.setSocketFamily(source.sin4.sin_family);
4✔
608
    Netmask requestorNM(source, source.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
4!
609
    const auto& requestor = requestorNM.getMaskedNetwork();
4✔
610
    pbMessage.setFrom(requestor);
4✔
611
    pbMessage.setFromPort(source.getPort());
4✔
612
  }
4✔
613
  else {
×
614
    pbMessage.setSocketFamily(mappedSource.sin4.sin_family);
×
615
    Netmask requestorNM(mappedSource, mappedSource.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
×
616
    const auto& requestor = requestorNM.getMaskedNetwork();
×
617
    pbMessage.setFrom(requestor);
×
618
    pbMessage.setFromPort(mappedSource.getPort());
×
619
  }
×
620
  pbMessage.setMessageIdentity(uniqueId);
4✔
621
  pbMessage.setTo(destination);
4✔
622
  pbMessage.setSocketProtocol(tcp ? pdns::ProtoZero::Message::TransportProtocol::TCP : pdns::ProtoZero::Message::TransportProtocol::UDP);
4!
623
  pbMessage.setId(header->id);
4✔
624

625
  pbMessage.setTime();
4✔
626
  pbMessage.setEDNSSubnet(ednssubnet.source, ednssubnet.source.isIPv4() ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
4!
627
  pbMessage.setRequestorId(requestorId);
4✔
628
  pbMessage.setDeviceId(deviceId);
4✔
629
  pbMessage.setDeviceName(deviceName);
4✔
630
  pbMessage.setToPort(destination.getPort());
4✔
631
  pbMessage.setWorkerId(RecThreadInfo::id());
4✔
632
  // this method is only used for PC cache hits
633
  pbMessage.setPacketCacheHit(true);
4✔
634
  // we do not set outgoingQueries, it is not relevant for PC cache hits
635

636
  for (const auto& metaItem : meta) {
4!
637
    pbMessage.setMeta(metaItem.first, metaItem.second.stringVal, metaItem.second.intVal);
×
638
  }
×
639
#ifdef NOD_ENABLED
4✔
640
  if (g_nodEnabled) {
4!
641
    pbMessage.setNewlyObservedDomain(false);
×
642
  }
×
643
#endif
4✔
644
  if (eventTrace.enabled() && (SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_pb) != 0) {
4!
645
    pbMessage.addEvents(eventTrace);
×
646
  }
×
647
  pbMessage.addPolicyTags(policyTags);
4✔
648

649
  protobufLogResponse(pbMessage);
4✔
650
}
4✔
651

652
#ifdef HAVE_FSTRM
653

654
static std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>> startFrameStreamServers(const FrameStreamExportConfig& config, Logr::log_t log)
655
{
25✔
656
  auto result = std::make_shared<std::vector<std::unique_ptr<FrameStreamLogger>>>();
25✔
657

658
  for (const auto& server : config.servers) {
25✔
659
    try {
25✔
660
      std::unordered_map<string, unsigned> options;
25✔
661
      options["bufferHint"] = config.bufferHint;
25✔
662
      options["flushTimeout"] = config.flushTimeout;
25✔
663
      options["inputQueueSize"] = config.inputQueueSize;
25✔
664
      options["outputQueueSize"] = config.outputQueueSize;
25✔
665
      options["queueNotifyThreshold"] = config.queueNotifyThreshold;
25✔
666
      options["reopenInterval"] = config.reopenInterval;
25✔
667
      unique_ptr<FrameStreamLogger> fsl = nullptr;
25✔
668
      try {
25✔
669
        ComboAddress address(server);
25✔
670
        fsl = make_unique<FrameStreamLogger>(address.sin4.sin_family, address.toStringWithPort(), true, options);
25✔
671
      }
25✔
672
      catch (const PDNSException& e) {
25✔
673
        fsl = make_unique<FrameStreamLogger>(AF_UNIX, server, true, options);
25✔
674
      }
25✔
675
      fsl->setLogQueries(config.logQueries);
25✔
676
      fsl->setLogResponses(config.logResponses);
25✔
677
      fsl->setLogNODs(config.logNODs);
25✔
678
      fsl->setLogUDRs(config.logUDRs);
25✔
679
      result->emplace_back(std::move(fsl));
25✔
680
    }
25✔
681
    catch (const std::exception& e) {
25✔
682
      SLOG(g_log << Logger::Error << "Error while starting dnstap framestream logger to '" << server << ": " << e.what() << endl,
×
683
           log->error(Logr::Error, e.what(), "Exception while starting dnstap framestream logger", "exception", Logging::Loggable("std::exception"), "server", Logging::Loggable(server)));
×
684
    }
×
685
    catch (const PDNSException& e) {
25✔
686
      SLOG(g_log << Logger::Error << "Error while starting dnstap framestream logger to '" << server << ": " << e.reason << endl,
×
687
           log->error(Logr::Error, e.reason, "Exception while starting dnstap framestream logger", "exception", Logging::Loggable("PDNSException"), "server", Logging::Loggable(server)));
×
688
    }
×
689
  }
25✔
690

691
  return result;
25✔
692
}
25✔
693

694
static void asyncFrameStreamLoggersCleanup(std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>&& servers)
695
{
×
696
  auto thread = std::thread([&] {
×
697
    servers.reset();
×
698
  });
×
699
  thread.detach();
×
700
}
×
701

702
bool checkFrameStreamExport(LocalStateHolder<LuaConfigItems>& luaconfsLocal, const FrameStreamExportConfig& config, FrameStreamServersInfo& serverInfos)
703
{
11,286✔
704
  if (!config.enabled) {
11,286✔
705
    if (serverInfos.servers) {
11,237!
706
      // dt's take care of cleanup
707
      asyncFrameStreamLoggersCleanup(std::move(serverInfos.servers));
×
708
      serverInfos.config = config;
×
709
    }
×
710

711
    return false;
11,237✔
712
  }
11,237✔
713

714
  /* if the server was not running, or if it was running according to a previous
715
   * configuration
716
   */
717
  if (serverInfos.generation < luaconfsLocal->generation && serverInfos.config != config) {
49!
718
    if (serverInfos.servers) {
25!
719
      // dt's take care of cleanup
720
      asyncFrameStreamLoggersCleanup(std::move(serverInfos.servers));
×
721
    }
×
722

723
    auto dnsTapLog = g_slog->withName("dnstap");
25✔
724
    serverInfos.servers = startFrameStreamServers(config, dnsTapLog);
25✔
725
    serverInfos.config = config;
25✔
726
    serverInfos.generation = luaconfsLocal->generation;
25✔
727
  }
25✔
728

729
  return true;
49✔
730
}
11,286✔
731

732
#endif /* HAVE_FSTRM */
733

734
static void makeControlChannelSocket(int processNum = -1)
735
{
148✔
736
  string sockname = ::arg()["socket-dir"] + "/" + g_programname;
148✔
737
  if (processNum >= 0) {
148!
738
    sockname += "." + std::to_string(processNum);
×
739
  }
×
740
  sockname += ".controlsocket";
148✔
741
  g_rcc.listen(sockname);
148✔
742

743
  uid_t sockowner = -1;
148✔
744
  gid_t sockgroup = -1;
148✔
745

746
  if (!::arg().isEmpty("socket-group")) {
148!
747
    sockgroup = ::arg().asGid("socket-group");
×
748
  }
×
749
  if (!::arg().isEmpty("socket-owner")) {
148!
750
    sockowner = ::arg().asUid("socket-owner");
×
751
  }
×
752

753
  if (sockgroup != static_cast<gid_t>(-1) || sockowner != static_cast<uid_t>(-1)) {
148!
754
    if (chown(sockname.c_str(), sockowner, sockgroup) < 0) {
×
755
      unixDie("Failed to chown control socket");
×
756
    }
×
757
  }
×
758

759
  // do mode change if socket-mode is given
760
  if (!::arg().isEmpty("socket-mode")) {
148!
761
    mode_t sockmode = ::arg().asMode("socket-mode");
×
762
    if (chmod(sockname.c_str(), sockmode) < 0) {
×
763
      unixDie("Failed to chmod control socket");
×
764
    }
×
765
  }
×
766
}
148✔
767

768
static void writePid(Logr::log_t log)
769
{
148✔
770
  if (!::arg().mustDo("write-pid")) {
148!
771
    return;
×
772
  }
×
773
  ofstream ostr(g_pidfname.c_str(), std::ios_base::app);
148✔
774
  if (ostr) {
148!
775
    ostr << Utility::getpid() << endl;
148✔
776
  }
148✔
777
  else {
×
778
    int err = errno;
×
779
    SLOG(g_log << Logger::Error << "Writing pid for " << Utility::getpid() << " to " << g_pidfname << " failed: " << stringerror(err) << endl,
×
780
         log->error(Logr::Error, err, "Writing pid failed", "pid", Logging::Loggable(Utility::getpid()), "file", Logging::Loggable(g_pidfname)));
×
781
  }
×
782
}
148✔
783

784
static void checkSocketDir(Logr::log_t log)
785
{
148✔
786
  string dir(::arg()["socket-dir"]);
148✔
787
  string msg;
148✔
788

789
  struct stat dirStat = {};
148✔
790
  if (stat(dir.c_str(), &dirStat) == -1) {
148!
791
    msg = "it does not exist or cannot access";
×
792
  }
×
793
  else if (!S_ISDIR(dirStat.st_mode)) {
148!
794
    msg = "it is not a directory";
×
795
  }
×
796
  else if (access(dir.c_str(), R_OK | W_OK | X_OK) != 0) {
148!
797
    msg = "cannot read, write or search";
×
798
  }
×
799
  else {
148✔
800
    return;
148✔
801
  }
148✔
802
  dir = ::arg()["chroot"] + dir;
×
803
  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,
×
804
       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)));
×
805
  _exit(1);
×
806
}
148✔
807

808
#ifdef NOD_ENABLED
809
static void setupNODThread(Logr::log_t log)
810
{
148✔
811
  if (g_nodEnabled) {
148✔
812
    uint32_t num_cells = ::arg().asNum("new-domain-db-size");
3✔
813
    g_nodDBp = std::make_unique<nod::NODDB>(num_cells);
3✔
814
    try {
3✔
815
      g_nodDBp->setCacheDir(::arg()["new-domain-history-dir"]);
3✔
816
    }
3✔
817
    catch (const PDNSException& e) {
3✔
818
      SLOG(g_log << Logger::Error << "new-domain-history-dir (" << ::arg()["new-domain-history-dir"] << ") is not readable or does not exist" << endl,
×
819
           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"])));
×
820
      _exit(1);
×
821
    }
×
822
    if (!g_nodDBp->init()) {
3!
823
      SLOG(g_log << Logger::Error << "Could not initialize domain tracking" << endl,
×
824
           log->info(Logr::Error, "Could not initialize domain tracking"));
×
825
      _exit(1);
×
826
    }
×
827
    if (::arg().asNum("new-domain-db-snapshot-interval") > 0) {
3!
828
      g_nodDBp->setSnapshotInterval(::arg().asNum("new-domain-db-snapshot-interval"));
3✔
829
      std::thread thread([tid = std::this_thread::get_id()]() {
3✔
830
        g_nodDBp->housekeepingThread(tid);
3✔
831
      });
3✔
832
      thread.detach();
3✔
833
    }
3✔
834
  }
3✔
835
  if (g_udrEnabled) {
148✔
836
    uint32_t num_cells = ::arg().asNum("unique-response-db-size");
3✔
837
    g_udrDBp = std::make_unique<nod::UniqueResponseDB>(num_cells);
3✔
838
    try {
3✔
839
      g_udrDBp->setCacheDir(::arg()["unique-response-history-dir"]);
3✔
840
    }
3✔
841
    catch (const PDNSException& e) {
3✔
842
      SLOG(g_log << Logger::Error << "unique-response-history-dir (" << ::arg()["unique-response-history-dir"] << ") is not readable or does not exist" << endl,
×
843
           log->info(Logr::Error, "unique-response-history-dir is not readable or does not exist", "dir", Logging::Loggable(::arg()["unique-response-history-dir"])));
×
844
      _exit(1);
×
845
    }
×
846
    if (!g_udrDBp->init()) {
3!
847
      SLOG(g_log << Logger::Error << "Could not initialize unique response tracking" << endl,
×
848
           log->info(Logr::Error, "Could not initialize unique response tracking"));
×
849
      _exit(1);
×
850
    }
×
851
    if (::arg().asNum("new-domain-db-snapshot-interval") > 0) {
3!
852
      g_udrDBp->setSnapshotInterval(::arg().asNum("new-domain-db-snapshot-interval"));
3✔
853
      std::thread thread([tid = std::this_thread::get_id()]() {
3✔
854
        g_udrDBp->housekeepingThread(tid);
3✔
855
      });
3✔
856
      thread.detach();
3✔
857
    }
3✔
858
  }
3✔
859
}
148✔
860

861
static void parseNODIgnorelist(const std::string& wlist)
862
{
296✔
863
  vector<string> parts;
296✔
864
  stringtok(parts, wlist, ",; ");
296✔
865
  for (const auto& part : parts) {
296!
866
    g_nodDomainWL.add(DNSName(part));
×
867
  }
×
868
}
296✔
869

870
static void setupNODGlobal()
871
{
148✔
872
  // Setup NOD subsystem
873
  g_nodEnabled = ::arg().mustDo("new-domain-tracking");
148✔
874
  g_nodLookupDomain = DNSName(::arg()["new-domain-lookup"]);
148✔
875
  g_nodLog = ::arg().mustDo("new-domain-log");
148✔
876
  parseNODIgnorelist(::arg()["new-domain-whitelist"]);
148✔
877
  parseNODIgnorelist(::arg()["new-domain-ignore-list"]);
148✔
878

879
  // Setup Unique DNS Response subsystem
880
  g_udrEnabled = ::arg().mustDo("unique-response-tracking");
148✔
881
  g_udrLog = ::arg().mustDo("unique-response-log");
148✔
882
  g_nod_pbtag = ::arg()["new-domain-pb-tag"];
148✔
883
  g_udr_pbtag = ::arg()["unique-response-pb-tag"];
148✔
884
}
148✔
885
#endif /* NOD_ENABLED */
886

887
static void daemonize(Logr::log_t log)
888
{
×
889
  if (auto pid = fork(); pid != 0) {
×
890
    if (pid < 0) {
×
891
      int err = errno;
×
892
      SLOG(g_log << Logger::Critical << "Fork failed: " << stringerror(err) << endl,
×
893
           log->error(Logr::Critical, err, "Fork failed"));
×
894
      exit(1); // NOLINT(concurrency-mt-unsafe)
×
895
    }
×
896
    exit(0); // NOLINT(concurrency-mt-unsafe)
×
897
  }
×
898

899
  setsid();
×
900

901
  int devNull = open("/dev/null", O_RDWR); /* open stdin */
×
902
  if (devNull < 0) {
×
903
    int err = errno;
×
904
    SLOG(g_log << Logger::Critical << "Unable to open /dev/null: " << stringerror(err) << endl,
×
905
         log->error(Logr::Critical, err, "Unable to open /dev/null"));
×
906
  }
×
907
  else {
×
908
    dup2(devNull, 0); /* stdin */
×
909
    dup2(devNull, 1); /* stderr */
×
910
    dup2(devNull, 2); /* stderr */
×
911
    close(devNull);
×
912
  }
×
913
}
×
914

915
static void termIntHandler([[maybe_unused]] int arg)
916
{
147✔
917
  doExit();
147✔
918
}
147✔
919

920
static void usr1Handler([[maybe_unused]] int arg)
921
{
36✔
922
  statsWanted = true;
36✔
923
}
36✔
924

925
static void usr2Handler([[maybe_unused]] int arg)
926
{
×
927
  g_quiet = !g_quiet;
×
928
  SyncRes::setDefaultLogMode(g_quiet ? SyncRes::LogNone : SyncRes::Log);
×
929
  ::arg().set("quiet") = g_quiet ? "yes" : "no";
×
930
}
×
931

932
static void checkLinuxIPv6Limits([[maybe_unused]] Logr::log_t log)
933
{
148✔
934
#ifdef __linux__
148✔
935
  string line;
148✔
936
  if (readFileIfThere("/proc/sys/net/ipv6/route/max_size", &line)) {
148!
937
    int lim = std::stoi(line);
148✔
938
    if (lim < 16384) {
148!
939
      SLOG(g_log << Logger::Error << "If using IPv6, please raise sysctl net.ipv6.route.max_size, currently set to " << lim << " which is < 16384" << endl,
×
940
           log->info(Logr::Error, "If using IPv6, please raise sysctl net.ipv6.route.max_size to a size >= 16384", "current", Logging::Loggable(lim)));
×
941
    }
×
942
  }
148✔
943
#endif
148✔
944
}
148✔
945

946
static void checkOrFixLinuxMapCountLimits([[maybe_unused]] Logr::log_t log)
947
{
148✔
948
#ifdef __linux__
148✔
949
  string line;
148✔
950
  if (readFileIfThere("/proc/sys/vm/max_map_count", &line)) {
148!
951
    auto lim = std::stoull(line);
148✔
952
    // mthread stack use 3 maps per stack (2 guard pages + stack itself). Multiple by 4 for extra allowance.
953
    // Also add 2 for handler and task threads.
954
    auto workers = RecThreadInfo::numTCPWorkers() + RecThreadInfo::numUDPWorkers() + 2;
148✔
955
    auto mapsNeeded = 4ULL * g_maxMThreads * workers;
148✔
956
    if (lim < mapsNeeded) {
148!
957
      g_maxMThreads = static_cast<unsigned int>(lim / (4ULL * workers));
×
958
      SLOG(g_log << Logger::Error << "sysctl vm.max_map_count= <" << mapsNeeded << ", this may cause 'bad_alloc' exceptions; adjusting max-mthreads to " << g_maxMThreads << endl,
×
959
           log->info(Logr::Error, "sysctl vm.max_map_count < mapsNeeded, this may cause 'bad_alloc' exceptions, adjusting max-mthreads",
×
960
                     "vm.max_map_count", Logging::Loggable(lim), "mapsNeeded", Logging::Loggable(mapsNeeded),
×
961
                     "max-mthreads", Logging::Loggable(g_maxMThreads)));
×
962
    }
×
963
  }
148✔
964
#endif
148✔
965
}
148✔
966

967
static void checkOrFixFDS(Logr::log_t log)
968
{
148✔
969
  unsigned int availFDs = getFilenumLimit();
148✔
970
  unsigned int wantFDs = g_maxMThreads * (RecThreadInfo::numUDPWorkers() + RecThreadInfo::numTCPWorkers()) + 25; // even healthier margin than before
148✔
971
  wantFDs += (RecThreadInfo::numUDPWorkers() + RecThreadInfo::numTCPWorkers()) * TCPOutConnectionManager::s_maxIdlePerThread;
148✔
972

973
  if (wantFDs > availFDs) {
148!
974
    unsigned int hardlimit = getFilenumLimit(true);
×
975
    if (hardlimit >= wantFDs) {
×
976
      setFilenumLimit(wantFDs);
×
977
      SLOG(g_log << Logger::Warning << "Raised soft limit on number of filedescriptors to " << wantFDs << " to match max-mthreads and threads settings" << endl,
×
978
           log->info(Logr::Warning, "Raised soft limit on number of filedescriptors to match max-mthreads and threads settings", "limit", Logging::Loggable(wantFDs)));
×
979
    }
×
980
    else {
×
981
      auto newval = (hardlimit - 25 - TCPOutConnectionManager::s_maxIdlePerThread) / (RecThreadInfo::numUDPWorkers() + RecThreadInfo::numTCPWorkers());
×
982
      SLOG(g_log << Logger::Warning << "Insufficient number of filedescriptors available for max-mthreads*threads setting! (" << hardlimit << " < " << wantFDs << "), reducing max-mthreads to " << newval << endl,
×
983
           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)));
×
984
      g_maxMThreads = newval;
×
985
      setFilenumLimit(hardlimit);
×
986
    }
×
987
  }
×
988
}
148✔
989

990
#ifdef HAVE_SYSTEMD
991
static void loggerSDBackend(const Logging::Entry& entry)
992
{
×
993
  static const set<std::string, CIStringComparePOSIX> special = {
×
994
    "message",
×
995
    "message_id",
×
996
    "priority",
×
997
    "code_file",
×
998
    "code_line",
×
999
    "code_func",
×
1000
    "errno",
×
1001
    "invocation_id",
×
1002
    "user_invocation_id",
×
1003
    "syslog_facility",
×
1004
    "syslog_identifier",
×
1005
    "syslog_pid",
×
1006
    "syslog_timestamp",
×
1007
    "syslog_raw",
×
1008
    "documentation",
×
1009
    "tid",
×
1010
    "unit",
×
1011
    "user_unit",
×
1012
    "object_pid"};
×
1013

1014
  // First map SL priority to syslog's Urgency
1015
  Logger::Urgency urgency = entry.d_priority != 0 ? Logger::Urgency(entry.d_priority) : Logger::Info;
×
1016
  if (urgency > s_logUrgency) {
×
1017
    // We do not log anything if the Urgency of the message is lower than the requested loglevel.
1018
    // Not that lower Urgency means higher number.
1019
    return;
×
1020
  }
×
1021
  // We need to keep the string in mem until sd_journal_sendv has ben called
1022
  vector<string> strings;
×
1023
  auto appendKeyAndVal = [&strings](const string& key, const string& value) {
×
1024
    strings.emplace_back(key + "=" + value);
×
1025
  };
×
1026
  appendKeyAndVal("MESSAGE", entry.message);
×
1027
  if (entry.error) {
×
1028
    appendKeyAndVal("ERROR", entry.error.get());
×
1029
  }
×
1030
  appendKeyAndVal("LEVEL", std::to_string(entry.level));
×
1031
  appendKeyAndVal("PRIORITY", std::to_string(entry.d_priority));
×
1032
  if (entry.name) {
×
1033
    appendKeyAndVal("SUBSYSTEM", entry.name.get());
×
1034
  }
×
1035
  std::array<char, 64> timebuf{};
×
1036
  appendKeyAndVal("TIMESTAMP", Logging::toTimestampStringMilli(entry.d_timestamp, timebuf));
×
1037
  for (const auto& value : entry.values) {
×
1038
    if (value.first.at(0) == '_' || special.count(value.first) != 0) {
×
1039
      string key{"PDNS"};
×
1040
      key.append(value.first);
×
1041
      appendKeyAndVal(toUpper(key), value.second);
×
1042
    }
×
1043
    else {
×
1044
      appendKeyAndVal(toUpper(value.first), value.second);
×
1045
    }
×
1046
  }
×
1047
  // Thread id filled in by backend, since the SL code does not know about RecursorThreads
1048
  // We use the Recursor thread, other threads get id 0. May need to revisit.
1049
  appendKeyAndVal("TID", std::to_string(RecThreadInfo::id()));
×
1050

1051
  vector<iovec> iov;
×
1052
  iov.reserve(strings.size());
×
1053
  for (const auto& str : strings) {
×
1054
    // iovec has no 2 arg constructor, so make it explicit
1055
    iov.emplace_back(iovec{const_cast<void*>(reinterpret_cast<const void*>(str.data())), str.size()}); // NOLINT: it's the API
×
1056
  }
×
1057
  sd_journal_sendv(iov.data(), static_cast<int>(iov.size()));
×
1058
}
×
1059
#endif
1060

1061
static void loggerJSONBackend(const Logging::Entry& entry)
1062
{
×
1063
  // First map SL priority to syslog's Urgency
1064
  Logger::Urgency urg = entry.d_priority != 0 ? Logger::Urgency(entry.d_priority) : Logger::Info;
×
1065
  if (urg > s_logUrgency) {
×
1066
    // We do not log anything if the Urgency of the message is lower than the requested loglevel.
1067
    // Not that lower Urgency means higher number.
1068
    return;
×
1069
  }
×
1070

1071
  std::array<char, 64> timebuf{};
×
1072
  json11::Json::object json = {
×
1073
    {"msg", entry.message},
×
1074
    {"level", std::to_string(entry.level)},
×
1075
    // Thread id filled in by backend, since the SL code does not know about RecursorThreads
1076
    // We use the Recursor thread, other threads get id 0. May need to revisit.
1077
    {"tid", std::to_string(RecThreadInfo::id())},
×
1078
    {"ts", Logging::toTimestampStringMilli(entry.d_timestamp, timebuf)},
×
1079
  };
×
1080

1081
  if (entry.error) {
×
1082
    json.emplace("error", entry.error.get());
×
1083
  }
×
1084

1085
  if (entry.name) {
×
1086
    json.emplace("subsystem", entry.name.get());
×
1087
  }
×
1088

1089
  if (entry.d_priority != 0) {
×
1090
    json.emplace("priority", std::to_string(entry.d_priority));
×
1091
  }
×
1092

1093
  for (auto const& value : entry.values) {
×
1094
    json.emplace(value.first, value.second);
×
1095
  }
×
1096

1097
  static thread_local std::string out;
×
1098
  out.clear();
×
1099
  json11::Json doc(std::move(json));
×
1100
  doc.dump(out);
×
1101
  cerr << out << endl;
×
1102
}
×
1103

1104
static void loggerBackend(const Logging::Entry& entry)
1105
{
6,942✔
1106
  static thread_local std::stringstream buf;
6,942✔
1107

1108
  // First map SL priority to syslog's Urgency
1109
  Logger::Urgency urg = entry.d_priority != 0 ? Logger::Urgency(entry.d_priority) : Logger::Info;
6,942✔
1110
  if (urg > s_logUrgency) {
6,942✔
1111
    // We do not log anything if the Urgency of the message is lower than the requested loglevel.
1112
    // Not that lower Urgency means higher number.
1113
    return;
283✔
1114
  }
283✔
1115
  buf.str("");
6,659✔
1116
  buf << "msg=" << std::quoted(entry.message);
6,659✔
1117
  if (entry.error) {
6,659✔
1118
    buf << " error=" << std::quoted(entry.error.get());
86✔
1119
  }
86✔
1120

1121
  if (entry.name) {
6,659✔
1122
    buf << " subsystem=" << std::quoted(entry.name.get());
6,651✔
1123
  }
6,651✔
1124
  buf << " level=" << std::quoted(std::to_string(entry.level));
6,659✔
1125
  if (entry.d_priority != 0) {
6,659✔
1126
    buf << " prio=" << std::quoted(Logr::Logger::toString(entry.d_priority));
6,635✔
1127
  }
6,635✔
1128
  // Thread id filled in by backend, since the SL code does not know about RecursorThreads
1129
  // We use the Recursor thread, other threads get id 0. May need to revisit.
1130
  buf << " tid=" << std::quoted(std::to_string(RecThreadInfo::id()));
6,659✔
1131
  std::array<char, 64> timebuf{};
6,659✔
1132
  buf << " ts=" << std::quoted(Logging::toTimestampStringMilli(entry.d_timestamp, timebuf));
6,659✔
1133
  for (auto const& value : entry.values) {
31,846✔
1134
    buf << " ";
31,846✔
1135
    buf << value.first << "=" << std::quoted(value.second);
31,846✔
1136
  }
31,846✔
1137

1138
  g_log << urg << buf.str() << endl;
6,659✔
1139
}
6,659✔
1140

1141
static int ratePercentage(uint64_t nom, uint64_t denom)
1142
{
242✔
1143
  if (denom == 0) {
242!
1144
    return 0;
×
1145
  }
×
1146
  return static_cast<int>(round(100.0 * static_cast<double>(nom) / static_cast<double>(denom)));
242✔
1147
}
242✔
1148

1149
static void doStats()
1150
{
49✔
1151
  static time_t lastOutputTime;
49✔
1152
  static uint64_t lastQueryCount;
49✔
1153

1154
  uint64_t cacheHits = g_recCache->getCacheHits();
49✔
1155
  uint64_t cacheMisses = g_recCache->getCacheMisses();
49✔
1156
  uint64_t cacheSize = g_recCache->size();
49✔
1157
  auto rc_stats = g_recCache->stats();
49✔
1158
  auto pc_stats = g_packetCache ? g_packetCache->stats() : std::pair<uint64_t, uint64_t>{0, 0};
49!
1159
  double rrc = ratePercentage(rc_stats.first, rc_stats.second);
49✔
1160
  double rpc = ratePercentage(pc_stats.first, pc_stats.second);
49✔
1161
  uint64_t negCacheSize = g_negCache->size();
49✔
1162
  auto taskPushes = getTaskPushes();
49✔
1163
  auto taskExpired = getTaskExpired();
49✔
1164
  auto taskSize = getTaskSize();
49✔
1165
  uint64_t pcSize = g_packetCache ? g_packetCache->size() : 0;
49!
1166
  uint64_t pcHits = g_packetCache ? g_packetCache->getHits() : 0;
49!
1167

1168
  auto log = g_slog->withName("stats");
49✔
1169

1170
  auto qcounter = g_Counters.sum(rec::Counter::qcounter);
49✔
1171
  auto syncresqueries = g_Counters.sum(rec::Counter::syncresqueries);
49✔
1172
  auto outqueries = g_Counters.sum(rec::Counter::outqueries);
49✔
1173
  auto throttledqueries = g_Counters.sum(rec::Counter::throttledqueries);
49✔
1174
  auto tcpoutqueries = g_Counters.sum(rec::Counter::tcpoutqueries);
49✔
1175
  auto dotoutqueries = g_Counters.sum(rec::Counter::dotoutqueries);
49✔
1176
  auto outgoingtimeouts = g_Counters.sum(rec::Counter::outgoingtimeouts);
49✔
1177
  if (qcounter > 0 && (cacheHits + cacheMisses) > 0 && syncresqueries > 0 && outqueries > 0) {
49!
1178
    if (!g_slogStructured) {
36✔
1179
      g_log << Logger::Notice << "stats: " << qcounter << " questions, " << cacheSize << " cache entries, " << negCacheSize << " negative entries, " << ratePercentage(cacheHits, cacheHits + cacheMisses) << "% cache hits" << endl;
×
1180
      g_log << Logger::Notice << "stats: record cache contended/acquired " << rc_stats.first << '/' << rc_stats.second << " = " << rrc << '%' << endl;
×
1181
      g_log << Logger::Notice << "stats: packet cache contended/acquired " << pc_stats.first << '/' << pc_stats.second << " = " << rpc << '%' << endl;
×
1182

1183
      g_log << Logger::Notice << "stats: throttle map: "
×
1184
            << SyncRes::getThrottledServersSize() << ", ns speeds: "
×
1185
            << SyncRes::getNSSpeedsSize() << ", failed ns: "
×
1186
            << SyncRes::getFailedServersSize() << ", ednsmap: "
×
1187
            << SyncRes::getEDNSStatusesSize() << ", non-resolving: "
×
1188
            << SyncRes::getNonResolvingNSSize() << ", saved-parentsets: "
×
1189
            << SyncRes::getSaveParentsNSSetsSize()
×
1190
            << endl;
×
1191
      g_log << Logger::Notice << "stats: outpacket/query ratio " << ratePercentage(outqueries, syncresqueries) << "%";
×
1192
      g_log << Logger::Notice << ", " << ratePercentage(throttledqueries, outqueries + throttledqueries) << "% throttled" << endl;
×
1193
      g_log << Logger::Notice << "stats: " << tcpoutqueries << "/" << dotoutqueries << "/" << getCurrentIdleTCPConnections() << " outgoing tcp/dot/idle connections, " << broadcastAccFunction<uint64_t>(pleaseGetConcurrentQueries) << " queries running, " << outgoingtimeouts << " outgoing timeouts " << endl;
×
1194

1195
      g_log << Logger::Notice << "stats: " << pcSize << " packet cache entries, " << ratePercentage(pcHits, qcounter) << "% packet cache hits" << endl;
×
1196

1197
      g_log << Logger::Notice << "stats: tasks pushed/expired/queuesize: " << taskPushes << '/' << taskExpired << '/' << taskSize << endl;
×
1198
    }
×
1199
    else {
36✔
1200
      const string report = "Periodic statistics report";
36✔
1201
      log->info(Logr::Info, report,
36✔
1202
                "questions", Logging::Loggable(qcounter),
36✔
1203
                "cache-entries", Logging::Loggable(cacheSize),
36✔
1204
                "negcache-entries", Logging::Loggable(negCacheSize),
36✔
1205
                "record-cache-hitratio-perc", Logging::Loggable(ratePercentage(cacheHits, cacheHits + cacheMisses)),
36✔
1206
                "record-cache-contended", Logging::Loggable(rc_stats.first),
36✔
1207
                "record-cache-acquired", Logging::Loggable(rc_stats.second),
36✔
1208
                "record-cache-contended-perc", Logging::Loggable(rrc),
36✔
1209
                "packetcache-contended", Logging::Loggable(pc_stats.first),
36✔
1210
                "packetcache-acquired", Logging::Loggable(pc_stats.second),
36✔
1211
                "packetcache-contended-perc", Logging::Loggable(rpc));
36✔
1212
      log->info(Logr::Info, report,
36✔
1213
                "throttle-entries", Logging::Loggable(SyncRes::getThrottledServersSize()),
36✔
1214
                "nsspeed-entries", Logging::Loggable(SyncRes::getNSSpeedsSize()),
36✔
1215
                "failed-host-entries", Logging::Loggable(SyncRes::getFailedServersSize()),
36✔
1216
                "edns-entries", Logging::Loggable(SyncRes::getEDNSStatusesSize()),
36✔
1217
                "non-resolving-nameserver-entries", Logging::Loggable(SyncRes::getNonResolvingNSSize()),
36✔
1218
                "saved-parent-ns-sets-entries", Logging::Loggable(SyncRes::getSaveParentsNSSetsSize()),
36✔
1219
                "outqueries-per-query", Logging::Loggable(ratePercentage(outqueries, syncresqueries)));
36✔
1220
      log->info(Logr::Info, report,
36✔
1221
                "throttled-queries-perc", Logging::Loggable(ratePercentage(throttledqueries, outqueries + throttledqueries)),
36✔
1222
                "tcp-outqueries", Logging::Loggable(tcpoutqueries),
36✔
1223
                "dot-outqueries", Logging::Loggable(dotoutqueries),
36✔
1224
                "idle-tcpout-connections", Logging::Loggable(getCurrentIdleTCPConnections()),
36✔
1225
                "concurrent-queries", Logging::Loggable(broadcastAccFunction<uint64_t>(pleaseGetConcurrentQueries)),
36✔
1226
                "outgoing-timeouts", Logging::Loggable(outgoingtimeouts));
36✔
1227
      log->info(Logr::Info, report,
36✔
1228
                "packetcache-entries", Logging::Loggable(pcSize),
36✔
1229
                "packetcache-hitratio-perc", Logging::Loggable(ratePercentage(pcHits, qcounter)),
36✔
1230
                "taskqueue-pushed", Logging::Loggable(taskPushes),
36✔
1231
                "taskqueue-expired", Logging::Loggable(taskExpired),
36✔
1232
                "taskqueue-size", Logging::Loggable(taskSize));
36✔
1233
    }
36✔
1234
    size_t idx = 0;
36✔
1235
    for (const auto& threadInfo : RecThreadInfo::infos()) {
297✔
1236
      if (threadInfo.isWorker()) {
297✔
1237
        SLOG(g_log << Logger::Notice << "stats: thread " << idx << " has been distributed " << threadInfo.getNumberOfDistributedQueries() << " queries" << endl,
189✔
1238
             log->info(Logr::Info, "Queries handled by thread", "thread", Logging::Loggable(idx), "tname", Logging::Loggable(threadInfo.getName()), "count", Logging::Loggable(threadInfo.getNumberOfDistributedQueries())));
189✔
1239
        ++idx;
189✔
1240
      }
189✔
1241
    }
297✔
1242
    time_t now = time(nullptr);
36✔
1243
    if (lastOutputTime != 0 && lastQueryCount != 0 && now != lastOutputTime) {
36!
1244
      SLOG(g_log << Logger::Notice << "stats: " << (qcounter - lastQueryCount) / (now - lastOutputTime) << " qps (average over " << (now - lastOutputTime) << " seconds)" << endl,
24✔
1245
           log->info(Logr::Info, "Periodic QPS report", "qps", Logging::Loggable((qcounter - lastQueryCount) / (now - lastOutputTime)),
24✔
1246
                     "averagedOver", Logging::Loggable(now - lastOutputTime)));
24✔
1247
    }
24✔
1248
    lastOutputTime = now;
36✔
1249
    lastQueryCount = qcounter;
36✔
1250
  }
36✔
1251
  else if (statsWanted) {
13!
1252
    SLOG(g_log << Logger::Notice << "stats: no stats yet!" << endl,
×
1253
         log->info(Logr::Notice, "No stats yet"));
×
1254
  }
×
1255

1256
  statsWanted = false;
49✔
1257
}
49✔
1258

1259
static std::shared_ptr<NetmaskGroup> parseACL(const std::string& aclFile, const std::string& aclSetting, Logr::log_t log)
1260
{
304✔
1261
  auto result = std::make_shared<NetmaskGroup>();
304✔
1262

1263
  const string file = ::arg()[aclFile];
304✔
1264

1265
  if (!file.empty()) {
304✔
1266
    if (boost::ends_with(file, ".yml")) {
4!
1267
      ::rust::vec<::rust::string> vec;
4✔
1268
      pdns::settings::rec::readYamlAllowFromFile(file, vec, log);
4✔
1269
      for (const auto& subnet : vec) {
8✔
1270
        result->addMask(string(subnet));
8✔
1271
      }
8✔
1272
    }
4✔
1273
    else {
×
1274
      string line;
×
1275
      ifstream ifs(file);
×
1276
      if (!ifs) {
×
1277
        int err = errno;
×
1278
        throw runtime_error("Could not open '" + file + "': " + stringerror(err));
×
1279
      }
×
1280

1281
      while (getline(ifs, line)) {
×
1282
        auto pos = line.find('#');
×
1283
        if (pos != string::npos) {
×
1284
          line.resize(pos);
×
1285
        }
×
1286
        boost::trim(line);
×
1287
        if (line.empty()) {
×
1288
          continue;
×
1289
        }
×
1290

1291
        result->addMask(line);
×
1292
      }
×
1293
    }
×
1294
    SLOG(g_log << Logger::Info << "Done parsing " << result->size() << " " << aclSetting << " ranges from file '" << file << "' - overriding '" << aclSetting << "' setting" << endl,
4✔
1295
         log->info(Logr::Info, "Done parsing ranges from file, will override setting", "setting", Logging::Loggable(aclSetting),
4✔
1296
                   "number", Logging::Loggable(result->size()), "file", Logging::Loggable(file)));
4✔
1297
  }
4✔
1298
  else if (!::arg()[aclSetting].empty()) {
300✔
1299
    vector<string> ips;
152✔
1300
    stringtok(ips, ::arg()[aclSetting], ", ");
152✔
1301

1302
    for (const auto& address : ips) {
1,232✔
1303
      result->addMask(address);
1,232✔
1304
    }
1,232✔
1305
    if (!g_slogStructured) {
152✔
1306
      g_log << Logger::Info << aclSetting << ": ";
×
1307
      for (auto i = ips.begin(); i != ips.end(); ++i) {
×
1308
        if (i != ips.begin()) {
×
1309
          g_log << Logger::Info << ", ";
×
1310
        }
×
1311
        g_log << Logger::Info << *i;
×
1312
      }
×
1313
      g_log << Logger::Info << endl;
×
1314
    }
×
1315
    else {
152✔
1316
      log->info(Logr::Info, "Setting access control", "acl", Logging::Loggable(aclSetting), "addresses", Logging::IterLoggable(ips.begin(), ips.end()));
152✔
1317
    }
152✔
1318
  }
152✔
1319

1320
  return result;
304✔
1321
}
304✔
1322

1323
static void* pleaseSupplantAllowFrom(std::shared_ptr<NetmaskGroup> nmgroup)
1324
{
168✔
1325
  t_allowFrom = std::move(nmgroup);
168✔
1326
  return nullptr;
168✔
1327
}
168✔
1328

1329
static void* pleaseSupplantAllowNotifyFrom(std::shared_ptr<NetmaskGroup> nmgroup)
1330
{
168✔
1331
  t_allowNotifyFrom = std::move(nmgroup);
168✔
1332
  return nullptr;
168✔
1333
}
168✔
1334

1335
void* pleaseSupplantAllowNotifyFor(std::shared_ptr<notifyset_t> allowNotifyFor)
1336
{
55✔
1337
  t_allowNotifyFor = std::move(allowNotifyFor);
55✔
1338
  return nullptr;
55✔
1339
}
55✔
1340

1341
void parseACLs()
1342
{
152✔
1343
  auto log = g_slog->withName("config");
152✔
1344

1345
  static bool l_initialized;
152✔
1346

1347
  if (l_initialized) { // only reload configuration file on second call
152✔
1348

1349
    string configName = ::arg()["config-dir"] + "/recursor";
4✔
1350
    if (!::arg()["config-name"].empty()) {
4!
1351
      configName = ::arg()["config-dir"] + "/recursor-" + ::arg()["config-name"];
×
1352
    }
×
1353
    cleanSlashes(configName);
4✔
1354

1355
    if (g_yamlSettings) {
4!
1356
      configName += ".yml";
4✔
1357
      string msg;
4✔
1358
      pdns::rust::settings::rec::Recursorsettings settings;
4✔
1359
      // XXX Does ::arg()["include-dir"] have the right value, i.e. potentially overriden by command line?
1360
      auto yamlstatus = pdns::settings::rec::readYamlSettings(configName, ::arg()["include-dir"], settings, msg, log);
4✔
1361

1362
      switch (yamlstatus) {
4!
1363
      case pdns::settings::rec::YamlSettingsStatus::CannotOpen:
×
1364
        throw runtime_error("Unable to open '" + configName + "': " + msg);
×
1365
        break;
×
1366
      case pdns::settings::rec::YamlSettingsStatus::PresentButFailed:
×
1367
        throw runtime_error("Error processing '" + configName + "': " + msg);
×
1368
        break;
×
1369
      case pdns::settings::rec::YamlSettingsStatus::OK:
4!
1370
        pdns::settings::rec::processAPIDir(arg()["include-dir"], settings, log);
4✔
1371
        // Does *not* set include-dir
1372
        pdns::settings::rec::setArgsForACLRelatedSettings(settings);
4✔
1373
        break;
4✔
1374
      }
4✔
1375
    }
4✔
1376
    else {
×
1377
      configName += ".conf";
×
1378
      if (!::arg().preParseFile(configName, "allow-from-file")) {
×
1379
        throw runtime_error("Unable to re-parse configuration file '" + configName + "'");
×
1380
      }
×
1381
      ::arg().preParseFile(configName, "allow-from", LOCAL_NETS);
×
1382

1383
      if (!::arg().preParseFile(configName, "allow-notify-from-file")) {
×
1384
        throw runtime_error("Unable to re-parse configuration file '" + configName + "'");
×
1385
      }
×
1386
      ::arg().preParseFile(configName, "allow-notify-from");
×
1387

1388
      ::arg().preParseFile(configName, "include-dir");
×
1389
      ::arg().preParse(g_argc, g_argv, "include-dir");
×
1390

1391
      // then process includes
1392
      std::vector<std::string> extraConfigs;
×
1393
      ::arg().gatherIncludes(::arg()["include-dir"], ".conf", extraConfigs);
×
1394

1395
      for (const std::string& fileName : extraConfigs) {
×
1396
        if (!::arg().preParseFile(fileName, "allow-from-file", ::arg()["allow-from-file"])) {
×
1397
          throw runtime_error("Unable to re-parse configuration file include '" + fileName + "'");
×
1398
        }
×
1399
        if (!::arg().preParseFile(fileName, "allow-from", ::arg()["allow-from"])) {
×
1400
          throw runtime_error("Unable to re-parse configuration file include '" + fileName + "'");
×
1401
        }
×
1402

1403
        if (!::arg().preParseFile(fileName, "allow-notify-from-file", ::arg()["allow-notify-from-file"])) {
×
1404
          throw runtime_error("Unable to re-parse configuration file include '" + fileName + "'");
×
1405
        }
×
1406
        if (!::arg().preParseFile(fileName, "allow-notify-from", ::arg()["allow-notify-from"])) {
×
1407
          throw runtime_error("Unable to re-parse configuration file include '" + fileName + "'");
×
1408
        }
×
1409
      }
×
1410
    }
×
1411
  }
4✔
1412
  // Process command line args potentially overriding settings read from file
1413
  ::arg().preParse(g_argc, g_argv, "allow-from-file");
152✔
1414
  ::arg().preParse(g_argc, g_argv, "allow-from");
152✔
1415

1416
  ::arg().preParse(g_argc, g_argv, "allow-notify-from-file");
152✔
1417
  ::arg().preParse(g_argc, g_argv, "allow-notify-from");
152✔
1418

1419
  auto allowFrom = parseACL("allow-from-file", "allow-from", log);
152✔
1420

1421
  if (allowFrom->empty()) {
152✔
1422
    if (::arg()["local-address"] != "127.0.0.1" && ::arg().asNum("local-port") == 53) {
3!
1423
      SLOG(g_log << Logger::Warning << "WARNING: Allowing queries from all IP addresses - this can be a security risk!" << endl,
×
1424
           log->info(Logr::Warning, "WARNING: Allowing queries from all IP addresses - this can be a security risk!"));
×
1425
    }
×
1426
    allowFrom = nullptr;
3✔
1427
  }
3✔
1428

1429
  g_initialAllowFrom = allowFrom;
152✔
1430
  // coverity[copy_constructor_call] maybe this can be avoided, but be careful as pointers get passed to other threads
1431
  broadcastFunction([=] { return pleaseSupplantAllowFrom(allowFrom); });
168✔
1432

1433
  auto allowNotifyFrom = parseACL("allow-notify-from-file", "allow-notify-from", log);
152✔
1434

1435
  g_initialAllowNotifyFrom = allowNotifyFrom;
152✔
1436
  // coverity[copy_constructor_call] maybe this can be avoided, but be careful as pointers get passed to other threads
1437
  broadcastFunction([=] { return pleaseSupplantAllowNotifyFrom(allowNotifyFrom); });
168✔
1438

1439
  l_initialized = true;
152✔
1440
}
152✔
1441

1442
void broadcastFunction(const pipefunc_t& func)
1443
{
326✔
1444
  /* This function might be called by the worker with t_id 0 during startup
1445
     for the initialization of ACLs and domain maps. After that it should only
1446
     be called by the handler. */
1447

1448
  if (RecThreadInfo::infos().empty() && RecThreadInfo::id() == 0) {
326!
1449
    /* the handler and  distributors will call themselves below, but
1450
       during startup we get called while g_threadInfos has not been
1451
       populated yet to update the ACL or domain maps, so we need to
1452
       handle that case.
1453
    */
1454
    func();
296✔
1455
  }
296✔
1456

1457
  unsigned int thread = 0;
326✔
1458
  for (const auto& threadInfo : RecThreadInfo::infos()) {
444✔
1459
    if (thread++ == RecThreadInfo::id()) {
150✔
1460
      func(); // don't write to ourselves!
30✔
1461
      continue;
30✔
1462
    }
30✔
1463

1464
    ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: manual ownership handling
120✔
1465
    tmsg->func = func;
120✔
1466
    tmsg->wantAnswer = true;
120✔
1467
    if (write(threadInfo.getPipes().writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // NOLINT: sizeof correct
120!
1468
      delete tmsg; // NOLINT: manual ownership handling
×
1469

1470
      unixDie("write to thread pipe returned wrong size or error");
×
1471
    }
×
1472

1473
    string* resp = nullptr;
120✔
1474
    if (read(threadInfo.getPipes().readFromThread, &resp, sizeof(resp)) != sizeof(resp)) { // NOLINT: sizeof correct
120!
1475
      unixDie("read from thread pipe returned wrong size or error");
×
1476
    }
×
1477

1478
    if (resp != nullptr) {
120!
1479
      delete resp; // NOLINT: manual ownership handling
×
1480
      resp = nullptr;
×
1481
    }
×
1482
    // coverity[leaked_storage]
1483
  }
120✔
1484
}
326✔
1485

1486
template <class T>
1487
void* voider(const std::function<T*()>& func)
1488
{
4,773✔
1489
  return func();
4,773✔
1490
}
4,773✔
1491

1492
static vector<ComboAddress>& operator+=(vector<ComboAddress>& lhs, const vector<ComboAddress>& rhs)
1493
{
×
1494
  lhs.insert(lhs.end(), rhs.begin(), rhs.end());
×
1495
  return lhs;
×
1496
}
×
1497

1498
static vector<pair<DNSName, uint16_t>>& operator+=(vector<pair<DNSName, uint16_t>>& lhs, const vector<pair<DNSName, uint16_t>>& rhs)
1499
{
×
1500
  lhs.insert(lhs.end(), rhs.begin(), rhs.end());
×
1501
  return lhs;
×
1502
}
×
1503

1504
static ProxyMappingStats_t& operator+=(ProxyMappingStats_t& lhs, const ProxyMappingStats_t& rhs)
1505
{
404✔
1506
  for (const auto& [key, entry] : rhs) {
404!
1507
    lhs[key].netmaskMatches += entry.netmaskMatches;
×
1508
    lhs[key].suffixMatches += entry.suffixMatches;
×
1509
  }
×
1510
  return lhs;
404✔
1511
}
404✔
1512

1513
static RemoteLoggerStats_t& operator+=(RemoteLoggerStats_t& lhs, const RemoteLoggerStats_t& rhs)
1514
{
1,616✔
1515
  for (const auto& [key, entry] : rhs) {
1,616!
1516
    lhs[key] += entry;
×
1517
  }
×
1518
  return lhs;
1,616✔
1519
}
1,616✔
1520

1521
// This function should only be called by the handler to gather
1522
// metrics, wipe the cache, reload the Lua script (not the Lua config)
1523
// or change the current trace regex, and by the SNMP thread to gather
1524
// metrics.
1525
// Note that this currently skips the handler, but includes the taskThread(s).
1526
template <class T>
1527
T broadcastAccFunction(const std::function<T*()>& func)
1528
{
1,027✔
1529
  if (!RecThreadInfo::self().isHandler()) {
1,027!
1530
    SLOG(g_log << Logger::Error << "broadcastAccFunction has been called by a worker (" << RecThreadInfo::id() << ")" << endl,
×
1531
         g_slog->withName("runtime")->info(Logr::Critical, "broadcastAccFunction has been called by a worker")); // tid will be added
×
1532
    _exit(1);
×
1533
  }
×
1534

1535
  unsigned int thread = 0;
1,027✔
1536
  T ret = T();
1,027✔
1537
  for (const auto& threadInfo : RecThreadInfo::infos()) {
5,800!
1538
    if (thread++ == RecThreadInfo::id()) {
5,800!
1539
      continue;
1,027✔
1540
    }
1,027✔
1541

1542
    const auto& tps = threadInfo.getPipes();
4,773✔
1543
    ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: manual ownership handling
4,773✔
1544
    tmsg->func = [func] { return voider<T>(func); };
4,773✔
1545
    tmsg->wantAnswer = true;
4,773✔
1546

1547
    if (write(tps.writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // NOLINT:: sizeof correct
4,773!
1548
      delete tmsg; // NOLINT: manual ownership handling
×
1549
      unixDie("write to thread pipe returned wrong size or error");
×
1550
    }
×
1551

1552
    T* resp = nullptr;
4,773✔
1553
    if (read(tps.readFromThread, &resp, sizeof(resp)) != sizeof(resp)) // NOLINT: sizeof correct
4,773!
1554
      unixDie("read from thread pipe returned wrong size or error");
×
1555

1556
    if (resp) {
4,773!
1557
      ret += *resp;
4,773✔
1558
      delete resp; // NOLINT: manual ownership handling
4,773✔
1559
      resp = nullptr;
4,773✔
1560
    }
4,773✔
1561
    // coverity[leaked_storage]
1562
  }
4,773✔
1563
  return ret;
1,027✔
1564
}
1,027✔
1565

1566
template string broadcastAccFunction(const std::function<string*()>& fun); // explicit instantiation
1567
template RecursorControlChannel::Answer broadcastAccFunction(const std::function<RecursorControlChannel::Answer*()>& fun); // explicit instantiation
1568
template uint64_t broadcastAccFunction(const std::function<uint64_t*()>& fun); // explicit instantiation
1569
template vector<ComboAddress> broadcastAccFunction(const std::function<vector<ComboAddress>*()>& fun); // explicit instantiation
1570
template vector<pair<DNSName, uint16_t>> broadcastAccFunction(const std::function<vector<pair<DNSName, uint16_t>>*()>& fun); // explicit instantiation
1571
template ThreadTimes broadcastAccFunction(const std::function<ThreadTimes*()>& fun);
1572
template ProxyMappingStats_t broadcastAccFunction(const std::function<ProxyMappingStats_t*()>& fun);
1573
template RemoteLoggerStats_t broadcastAccFunction(const std::function<RemoteLoggerStats_t*()>& fun);
1574

1575
static int initNet(Logr::log_t log)
1576
{
148✔
1577
  checkLinuxIPv6Limits(log);
148✔
1578
  try {
148✔
1579
    pdns::parseQueryLocalAddress(::arg()["query-local-address"]);
148✔
1580
  }
148✔
1581
  catch (std::exception& e) {
148✔
1582
    SLOG(g_log << Logger::Error << "Assigning local query addresses: " << e.what(),
×
1583
         log->error(Logr::Error, e.what(), "Unable to assign local query address"));
×
1584
    return 99;
×
1585
  }
×
1586

1587
  if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET)) {
148✔
1588
    SyncRes::s_doIPv4 = true;
147✔
1589
    SLOG(g_log << Logger::Warning << "Enabling IPv4 transport for outgoing queries" << endl,
147✔
1590
         log->info(Logr::Notice, "Enabling IPv4 transport for outgoing queries"));
147✔
1591
  }
147✔
1592
  else {
1✔
1593
    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,
1✔
1594
         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"));
1✔
1595
  }
1✔
1596

1597
  if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET6)) {
148✔
1598
    SyncRes::s_doIPv6 = true;
1✔
1599
    SLOG(g_log << Logger::Warning << "Enabling IPv6 transport for outgoing queries" << endl,
1✔
1600
         log->info(Logr::Notice, "Enabling IPv6 transport for outgoing queries"));
1✔
1601
  }
1✔
1602
  else {
147✔
1603
    SLOG(g_log << Logger::Warning << "NOT using IPv6 for outgoing queries - add an IPv6 address (like '::') to query-local-address to enable" << endl,
147✔
1604
         log->info(Logr::Warning, "NOT using IPv6 for outgoing queries - add an IPv6 address (like '::') to query-local-address to enable"));
147✔
1605
  }
147✔
1606

1607
  if (!SyncRes::s_doIPv6 && !SyncRes::s_doIPv4) {
148!
1608
    SLOG(g_log << Logger::Error << "No outgoing addresses configured! Can not continue" << endl,
×
1609
         log->info(Logr::Error, "No outgoing addresses configured! Can not continue"));
×
1610
    return 99;
×
1611
  }
×
1612
  return 0;
148✔
1613
}
148✔
1614

1615
static int initDNSSEC(Logr::log_t log)
1616
{
148✔
1617
  if (::arg()["dnssec"] == "off") {
148✔
1618
    g_dnssecmode = DNSSECMode::Off;
2✔
1619
  }
2✔
1620
  else if (::arg()["dnssec"] == "process-no-validate") {
146✔
1621
    g_dnssecmode = DNSSECMode::ProcessNoValidate;
1✔
1622
  }
1✔
1623
  else if (::arg()["dnssec"] == "process") {
145✔
1624
    g_dnssecmode = DNSSECMode::Process;
100✔
1625
  }
100✔
1626
  else if (::arg()["dnssec"] == "validate") {
45!
1627
    g_dnssecmode = DNSSECMode::ValidateAll;
45✔
1628
  }
45✔
1629
  else if (::arg()["dnssec"] == "log-fail") {
×
1630
    g_dnssecmode = DNSSECMode::ValidateForLog;
×
1631
  }
×
1632
  else {
×
1633
    SLOG(g_log << Logger::Error << "Unknown DNSSEC mode " << ::arg()["dnssec"] << endl,
×
1634
         log->info(Logr::Error, "Unknown DNSSEC mode", "dnssec", Logging::Loggable(::arg()["dnssec"])));
×
1635
    return 1;
×
1636
  }
×
1637

1638
  g_signatureInceptionSkew = ::arg().asNum("signature-inception-skew");
148✔
1639
  if (g_signatureInceptionSkew < 0) {
148!
1640
    SLOG(g_log << Logger::Error << "A negative value for 'signature-inception-skew' is not allowed" << endl,
×
1641
         log->info(Logr::Error, "A negative value for 'signature-inception-skew' is not allowed"));
×
1642
    return 1;
×
1643
  }
×
1644

1645
  g_dnssecLogBogus = ::arg().mustDo("dnssec-log-bogus");
148✔
1646
  g_maxNSEC3Iterations = ::arg().asNum("nsec3-max-iterations");
148✔
1647
  g_maxRRSIGsPerRecordToConsider = ::arg().asNum("max-rrsigs-per-record");
148✔
1648
  g_maxNSEC3sPerRecordToConsider = ::arg().asNum("max-nsec3s-per-record");
148✔
1649
  g_maxDNSKEYsToConsider = ::arg().asNum("max-dnskeys");
148✔
1650
  g_maxDSsToConsider = ::arg().asNum("max-ds-per-zone");
148✔
1651

1652
  vector<string> nums;
148✔
1653
  bool automatic = true;
148✔
1654
  if (!::arg()["dnssec-disabled-algorithms"].empty()) {
148!
1655
    automatic = false;
×
1656
    stringtok(nums, ::arg()["dnssec-disabled-algorithms"], ", ");
×
1657
    for (const auto& num : nums) {
×
1658
      DNSCryptoKeyEngine::switchOffAlgorithm(pdns::checked_stoi<unsigned int>(num));
×
1659
    }
×
1660
  }
×
1661
  else {
148✔
1662
    for (auto algo : {DNSSECKeeper::RSASHA1, DNSSECKeeper::RSASHA1NSEC3SHA1}) {
296✔
1663
      if (!DNSCryptoKeyEngine::verifyOne(algo)) {
296!
1664
        DNSCryptoKeyEngine::switchOffAlgorithm(algo);
×
1665
        nums.push_back(std::to_string(algo));
×
1666
      }
×
1667
    }
296✔
1668
  }
148✔
1669
  if (!nums.empty()) {
148!
1670
    if (!g_slogStructured) {
×
1671
      g_log << Logger::Warning << (automatic ? "Automatically" : "Manually") << " disabled DNSSEC algorithms: ";
×
1672
      for (auto i = nums.begin(); i != nums.end(); ++i) {
×
1673
        if (i != nums.begin()) {
×
1674
          g_log << Logger::Warning << ", ";
×
1675
        }
×
1676
        g_log << Logger::Warning << *i;
×
1677
      }
×
1678
      g_log << Logger::Warning << endl;
×
1679
    }
×
1680
    else {
×
1681
      log->info(Logr::Notice, "Disabled DNSSEC algorithms", "automatically", Logging::Loggable(automatic), "algorithms", Logging::IterLoggable(nums.begin(), nums.end()));
×
1682
    }
×
1683
  }
×
1684

1685
  return 0;
148✔
1686
}
148✔
1687

1688
static void initDontQuery(Logr::log_t log)
1689
{
148✔
1690
  if (!::arg()["dont-query"].empty()) {
148✔
1691
    vector<string> ips;
14✔
1692
    stringtok(ips, ::arg()["dont-query"], ", ");
14✔
1693
    ips.emplace_back("0.0.0.0");
14✔
1694
    ips.emplace_back("::");
14✔
1695

1696
    for (const auto& anIP : ips) {
294✔
1697
      SyncRes::addDontQuery(anIP);
294✔
1698
    }
294✔
1699
    if (!g_slogStructured) {
14✔
1700
      g_log << Logger::Warning << "Will not send queries to: ";
×
1701
      for (auto i = ips.begin(); i != ips.end(); ++i) {
×
1702
        if (i != ips.begin()) {
×
1703
          g_log << Logger::Warning << ", ";
×
1704
        }
×
1705
        g_log << Logger::Warning << *i;
×
1706
      }
×
1707
      g_log << Logger::Warning << endl;
×
1708
    }
×
1709
    else {
14✔
1710
      log->info(Logr::Notice, "Will not send queries to", "addresses", Logging::IterLoggable(ips.begin(), ips.end()));
14✔
1711
    }
14✔
1712
  }
14✔
1713
}
148✔
1714

1715
static int initSyncRes(Logr::log_t log)
1716
{
148✔
1717
  SyncRes::s_minimumTTL = ::arg().asNum("minimum-ttl-override");
148✔
1718
  SyncRes::s_minimumECSTTL = ::arg().asNum("ecs-minimum-ttl-override");
148✔
1719
  SyncRes::s_maxnegttl = ::arg().asNum("max-negative-ttl");
148✔
1720
  SyncRes::s_maxbogusttl = ::arg().asNum("max-cache-bogus-ttl");
148✔
1721
  SyncRes::s_maxcachettl = max(::arg().asNum("max-cache-ttl"), 15);
148✔
1722

1723
  SyncRes::s_packetcachettl = ::arg().asNum("packetcache-ttl");
148✔
1724
  // Cap the packetcache-servfail-ttl and packetcache-negative-ttl to packetcache-ttl
1725
  SyncRes::s_packetcacheservfailttl = std::min(static_cast<unsigned int>(::arg().asNum("packetcache-servfail-ttl")), SyncRes::s_packetcachettl);
148✔
1726
  SyncRes::s_packetcachenegativettl = std::min(static_cast<unsigned int>(::arg().asNum("packetcache-negative-ttl")), SyncRes::s_packetcachettl);
148✔
1727

1728
  SyncRes::s_serverdownmaxfails = ::arg().asNum("server-down-max-fails");
148✔
1729
  SyncRes::s_serverdownthrottletime = ::arg().asNum("server-down-throttle-time");
148✔
1730
  SyncRes::s_unthrottle_n = ::arg().asNum("bypass-server-throttling-probability");
148✔
1731
  SyncRes::s_nonresolvingnsmaxfails = ::arg().asNum("non-resolving-ns-max-fails");
148✔
1732
  SyncRes::s_nonresolvingnsthrottletime = ::arg().asNum("non-resolving-ns-throttle-time");
148✔
1733
  SyncRes::s_serverID = ::arg()["server-id"];
148✔
1734
  // This bound is dynamically adjusted in SyncRes, depending on qname minimization being active
1735
  SyncRes::s_maxqperq = ::arg().asNum("max-qperq");
148✔
1736
  SyncRes::s_maxnsperresolve = ::arg().asNum("max-ns-per-resolve");
148✔
1737
  SyncRes::s_maxnsaddressqperq = ::arg().asNum("max-ns-address-qperq");
148✔
1738
  SyncRes::s_maxtotusec = 1000 * ::arg().asNum("max-total-msec");
148✔
1739
  SyncRes::s_maxdepth = ::arg().asNum("max-recursion-depth");
148✔
1740
  SyncRes::s_maxvalidationsperq = ::arg().asNum("max-signature-validations-per-query");
148✔
1741
  SyncRes::s_maxnsec3iterationsperq = ::arg().asNum("max-nsec3-hash-computations-per-query");
148✔
1742
  SyncRes::s_rootNXTrust = ::arg().mustDo("root-nx-trust");
148✔
1743
  SyncRes::s_refresh_ttlperc = ::arg().asNum("refresh-on-ttl-perc");
148✔
1744
  SyncRes::s_locked_ttlperc = ::arg().asNum("record-cache-locked-ttl-perc");
148✔
1745
  RecursorPacketCache::s_refresh_ttlperc = SyncRes::s_refresh_ttlperc;
148✔
1746
  SyncRes::s_tcp_fast_open = ::arg().asNum("tcp-fast-open");
148✔
1747
  SyncRes::s_tcp_fast_open_connect = ::arg().mustDo("tcp-fast-open-connect");
148✔
1748

1749
  SyncRes::s_dot_to_port_853 = ::arg().mustDo("dot-to-port-853");
148✔
1750
  SyncRes::s_event_trace_enabled = ::arg().asNum("event-trace-enabled");
148✔
1751
  SyncRes::s_save_parent_ns_set = ::arg().mustDo("save-parent-ns-set");
148✔
1752
  SyncRes::s_max_busy_dot_probes = ::arg().asNum("max-busy-dot-probes");
148✔
1753
  {
148✔
1754
    uint64_t sse = ::arg().asNum("serve-stale-extensions");
148✔
1755
    if (sse > std::numeric_limits<uint16_t>::max()) {
148!
1756
      SLOG(g_log << Logger::Error << "Illegal serve-stale-extensions value: " << sse << "; range = 0..65536" << endl,
×
1757
           log->info(Logr::Error, "Illegal serve-stale-extensions value; range = 0..65536", "value", Logging::Loggable(sse)));
×
1758
      return 1;
×
1759
    }
×
1760
    MemRecursorCache::s_maxServedStaleExtensions = sse;
148✔
1761
    NegCache::s_maxServedStaleExtensions = sse;
148✔
1762
  }
148✔
1763

1764
  if (SyncRes::s_tcp_fast_open_connect) {
148!
1765
    checkFastOpenSysctl(true, log);
×
1766
    checkTFOconnect(log);
×
1767
  }
×
1768
  SyncRes::s_ecsipv4limit = ::arg().asNum("ecs-ipv4-bits");
148✔
1769
  SyncRes::s_ecsipv6limit = ::arg().asNum("ecs-ipv6-bits");
148✔
1770
  SyncRes::clearECSStats();
148✔
1771
  SyncRes::s_ecsipv4cachelimit = ::arg().asNum("ecs-ipv4-cache-bits");
148✔
1772
  SyncRes::s_ecsipv6cachelimit = ::arg().asNum("ecs-ipv6-cache-bits");
148✔
1773
  SyncRes::s_ecsipv4nevercache = ::arg().mustDo("ecs-ipv4-never-cache");
148✔
1774
  SyncRes::s_ecsipv6nevercache = ::arg().mustDo("ecs-ipv6-never-cache");
148✔
1775
  SyncRes::s_ecscachelimitttl = ::arg().asNum("ecs-cache-limit-ttl");
148✔
1776

1777
  SyncRes::s_qnameminimization = ::arg().mustDo("qname-minimization");
148✔
1778
  SyncRes::s_minimize_one_label = ::arg().asNum("qname-minimize-one-label");
148✔
1779
  SyncRes::s_max_minimize_count = ::arg().asNum("qname-max-minimize-count");
148✔
1780

1781
  SyncRes::s_hardenNXD = SyncRes::HardenNXD::DNSSEC;
148✔
1782
  string value = ::arg()["nothing-below-nxdomain"];
148✔
1783
  if (value == "yes") {
148!
1784
    SyncRes::s_hardenNXD = SyncRes::HardenNXD::Yes;
×
1785
  }
×
1786
  else if (value == "no") {
148!
1787
    SyncRes::s_hardenNXD = SyncRes::HardenNXD::No;
×
1788
  }
×
1789
  else if (value != "dnssec") {
148!
1790
    SLOG(g_log << Logger::Error << "Unknown nothing-below-nxdomain mode: " << value << endl,
×
1791
         log->info(Logr::Error, "Unknown nothing-below-nxdomain mode", "mode", Logging::Loggable(value)));
×
1792
    return 1;
×
1793
  }
×
1794

1795
  if (!::arg().isEmpty("ecs-scope-zero-address")) {
148✔
1796
    ComboAddress scopeZero(::arg()["ecs-scope-zero-address"]);
4✔
1797
    SyncRes::setECSScopeZeroAddress(Netmask(scopeZero, scopeZero.isIPv4() ? 32 : 128));
4✔
1798
  }
4✔
1799
  else {
144✔
1800
    Netmask netmask;
144✔
1801
    bool done = false;
144✔
1802

1803
    auto addr = pdns::getNonAnyQueryLocalAddress(AF_INET);
144✔
1804
    if (addr.sin4.sin_family != 0) {
144!
1805
      netmask = Netmask(addr, 32);
×
1806
      done = true;
×
1807
    }
×
1808
    if (!done) {
144!
1809
      addr = pdns::getNonAnyQueryLocalAddress(AF_INET6);
144✔
1810
      if (addr.sin4.sin_family != 0) {
144✔
1811
        netmask = Netmask(addr, 128);
1✔
1812
        done = true;
1✔
1813
      }
1✔
1814
    }
144✔
1815
    if (!done) {
144✔
1816
      netmask = Netmask(ComboAddress("127.0.0.1"), 32);
143✔
1817
    }
143✔
1818
    SyncRes::setECSScopeZeroAddress(netmask);
144✔
1819
  }
144✔
1820

1821
  SyncRes::parseEDNSSubnetAllowlist(::arg()["edns-subnet-whitelist"]);
148✔
1822
  SyncRes::parseEDNSSubnetAllowlist(::arg()["edns-subnet-allow-list"]);
148✔
1823
  SyncRes::parseEDNSSubnetAddFor(::arg()["ecs-add-for"]);
148✔
1824
  g_useIncomingECS = ::arg().mustDo("use-incoming-edns-subnet");
148✔
1825
  return 0;
148✔
1826
}
148✔
1827

1828
static void initDistribution(Logr::log_t log)
1829
{
148✔
1830
  g_balancingFactor = ::arg().asDouble("distribution-load-factor");
148✔
1831
  if (g_balancingFactor != 0.0 && g_balancingFactor < 1.0) {
148!
1832
    g_balancingFactor = 0.0;
×
1833
    SLOG(g_log << Logger::Warning << "Asked to run with a distribution-load-factor below 1.0, disabling it instead" << endl,
×
1834
         log->info(Logr::Warning, "Asked to run with a distribution-load-factor below 1.0, disabling it instead"));
×
1835
  }
×
1836

1837
#ifdef SO_REUSEPORT
148✔
1838
  g_reusePort = ::arg().mustDo("reuseport");
148✔
1839
#endif
148✔
1840

1841
  RecThreadInfo::infos().resize(RecThreadInfo::numRecursorThreads());
148✔
1842

1843
  if (g_reusePort) {
148✔
1844
    unsigned int threadNum = 1;
136✔
1845
    if (RecThreadInfo::weDistributeQueries()) {
136✔
1846
      /* first thread is the handler, then distributors */
1847
      for (unsigned int i = 0; i < RecThreadInfo::numDistributors(); i++, threadNum++) {
4✔
1848
        auto& info = RecThreadInfo::info(threadNum);
2✔
1849
        auto& deferredAdds = info.getDeferredAdds();
2✔
1850
        makeUDPServerSockets(deferredAdds, log);
2✔
1851
      }
2✔
1852
    }
2✔
1853
    else {
134✔
1854
      /* first thread is the handler, there is no distributor here and workers are accepting queries */
1855
      for (unsigned int i = 0; i < RecThreadInfo::numUDPWorkers(); i++, threadNum++) {
402✔
1856
        auto& info = RecThreadInfo::info(threadNum);
268✔
1857
        auto& deferredAdds = info.getDeferredAdds();
268✔
1858
        makeUDPServerSockets(deferredAdds, log);
268✔
1859
      }
268✔
1860
    }
134✔
1861
    threadNum = 1 + RecThreadInfo::numDistributors() + RecThreadInfo::numUDPWorkers();
136✔
1862
    for (unsigned int i = 0; i < RecThreadInfo::numTCPWorkers(); i++, threadNum++) {
272✔
1863
      auto& info = RecThreadInfo::info(threadNum);
136✔
1864
      auto& deferredAdds = info.getDeferredAdds();
136✔
1865
      auto& tcpSockets = info.getTCPSockets();
136✔
1866
      makeTCPServerSockets(deferredAdds, tcpSockets, log);
136✔
1867
    }
136✔
1868
  }
136✔
1869
  else {
12✔
1870
    std::set<int> tcpSockets;
12✔
1871
    /* we don't have reuseport so we can only open one socket per
1872
       listening addr:port and everyone will listen on it */
1873
    makeUDPServerSockets(s_deferredUDPadds, log);
12✔
1874
    makeTCPServerSockets(s_deferredTCPadds, tcpSockets, log);
12✔
1875

1876
    // TCP queries are handled by TCP workers
1877
    for (unsigned int i = 0; i < RecThreadInfo::numTCPWorkers(); i++) {
24✔
1878
      auto& info = RecThreadInfo::info(i + 1 + RecThreadInfo::numDistributors() + RecThreadInfo::numUDPWorkers());
12✔
1879
      info.setTCPSockets(tcpSockets);
12✔
1880
    }
12✔
1881
  }
12✔
1882
}
148✔
1883

1884
static int initForks(Logr::log_t log)
1885
{
148✔
1886
  int forks = 0;
148✔
1887
  for (; forks < ::arg().asNum("processes") - 1; ++forks) {
148!
1888
    if (fork() == 0) { // we are child
×
1889
      break;
×
1890
    }
×
1891
  }
×
1892

1893
  if (::arg().mustDo("daemon")) {
148!
1894
    SLOG(g_log << Logger::Warning << "Calling daemonize, going to background" << endl,
×
1895
         log->info(Logr::Warning, "Calling daemonize, going to background"));
×
1896
    g_log.toConsole(Logger::Critical);
×
1897
    daemonize(log);
×
1898
  }
×
1899

1900
  if (Utility::getpid() == 1) {
148!
1901
    /* We are running as pid 1, register sigterm and sigint handler
1902

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

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

1914
    signal(SIGTERM, termIntHandler);
×
1915
    signal(SIGINT, termIntHandler);
×
1916
  }
×
1917
#if defined(__SANITIZE_THREAD__) || (defined(__SANITIZE_ADDRESS__) && defined(HAVE_LEAK_SANITIZER_INTERFACE))
148✔
1918
  // If san is wanted, we dump the info ourselves
1919
  signal(SIGTERM, termIntHandler);
148✔
1920
#endif
148✔
1921

1922
  signal(SIGUSR1, usr1Handler);
148✔
1923
  signal(SIGUSR2, usr2Handler);
148✔
1924
  signal(SIGPIPE, SIG_IGN); // NOLINT: Posix API
148✔
1925
  return forks;
148✔
1926
}
148✔
1927

1928
static int initPorts(Logr::log_t log)
1929
{
148✔
1930
  int port = ::arg().asNum("udp-source-port-min");
148✔
1931
  if (port < 1024 || port > 65535) {
148!
1932
    SLOG(g_log << Logger::Error << "Unable to launch, udp-source-port-min is not a valid port number" << endl,
×
1933
         log->info(Logr::Error, "Unable to launch, udp-source-port-min is not a valid port number"));
×
1934
    return 99; // this isn't going to fix itself either
×
1935
  }
×
1936
  g_minUdpSourcePort = port;
148✔
1937
  port = ::arg().asNum("udp-source-port-max");
148✔
1938
  if (port < 1024 || port > 65535 || port < g_minUdpSourcePort) {
148!
1939
    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,
×
1940
         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"));
×
1941
    return 99; // this isn't going to fix itself either
×
1942
  }
×
1943
  g_maxUdpSourcePort = port;
148✔
1944
  std::vector<string> parts{};
148✔
1945
  stringtok(parts, ::arg()["udp-source-port-avoid"], ", ");
148✔
1946
  for (const auto& part : parts) {
148✔
1947
    port = std::stoi(part);
148✔
1948
    if (port < 1024 || port > 65535) {
148!
1949
      SLOG(g_log << Logger::Error << "Unable to launch, udp-source-port-avoid contains an invalid port number: " << part << endl,
×
1950
           log->info(Logr::Error, "Unable to launch, udp-source-port-avoid contains an invalid port number", "port", Logging::Loggable(part)));
×
1951
      return 99; // this isn't going to fix itself either
×
1952
    }
×
1953
    g_avoidUdpSourcePorts.insert(port);
148✔
1954
  }
148✔
1955
  return 0;
148✔
1956
}
148✔
1957

1958
static void initSNMP([[maybe_unused]] Logr::log_t log)
1959
{
148✔
1960
  if (::arg().mustDo("snmp-agent")) {
148✔
1961
#ifdef HAVE_NET_SNMP
1✔
1962
    string setting = ::arg()["snmp-daemon-socket"];
1✔
1963
    if (setting.empty()) {
1!
1964
      setting = ::arg()["snmp-master-socket"];
1✔
1965
    }
1✔
1966
    g_snmpAgent = std::make_shared<RecursorSNMPAgent>("recursor", setting);
1✔
1967
    g_snmpAgent->run();
1✔
1968
#else
1969
    const std::string msg = "snmp-agent set but SNMP support not compiled in";
1970
    SLOG(g_log << Logger::Error << msg << endl,
1971
         log->info(Logr::Error, msg));
1972
#endif // HAVE_NET_SNMP
1973
  }
1✔
1974
}
148✔
1975

1976
static int initControl(Logr::log_t log, uid_t newuid, int forks)
1977
{
148✔
1978
  if (!::arg()["chroot"].empty()) {
148!
1979
#ifdef HAVE_SYSTEMD
×
1980
    char* ns;
×
1981
    ns = getenv("NOTIFY_SOCKET");
×
1982
    if (ns != nullptr) {
×
1983
      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,
×
1984
           log->info(Logr::Error, "Unable to chroot when running from systemd. Please disable chroot= or set the 'Type' for this service to 'simple'"));
×
1985
      return 1;
×
1986
    }
×
1987
#endif
×
1988
    if (chroot(::arg()["chroot"].c_str()) < 0 || chdir("/") < 0) {
×
1989
      int err = errno;
×
1990
      SLOG(g_log << Logger::Error << "Unable to chroot to '" + ::arg()["chroot"] + "': " << stringerror(err) << ", exiting" << endl,
×
1991
           log->error(Logr::Error, err, "Unable to chroot", "chroot", Logging::Loggable(::arg()["chroot"])));
×
1992
      return 1;
×
1993
    }
×
1994
    SLOG(g_log << Logger::Info << "Chrooted to '" << ::arg()["chroot"] << "'" << endl,
×
1995
         log->info(Logr::Info, "Chrooted", "chroot", Logging::Loggable(::arg()["chroot"])));
×
1996
  }
×
1997

1998
  checkSocketDir(log);
148✔
1999

2000
  g_pidfname = ::arg()["socket-dir"] + "/" + g_programname + ".pid";
148✔
2001
  if (!g_pidfname.empty()) {
148!
2002
    unlink(g_pidfname.c_str()); // remove possible old pid file
148✔
2003
  }
148✔
2004
  writePid(log);
148✔
2005

2006
  makeControlChannelSocket(::arg().asNum("processes") > 1 ? forks : -1);
148!
2007

2008
  Utility::dropUserPrivs(newuid);
148✔
2009
  try {
148✔
2010
    /* we might still have capabilities remaining, for example if we have been started as root
2011
       without --setuid (please don't do that) or as an unprivileged user with ambient capabilities
2012
       like CAP_NET_BIND_SERVICE.
2013
    */
2014
    dropCapabilities();
148✔
2015
  }
148✔
2016
  catch (const std::exception& e) {
148✔
2017
    SLOG(g_log << Logger::Warning << e.what() << endl,
×
2018
         log->error(Logr::Warning, e.what(), "Could not drop capabilities"));
×
2019
  }
×
2020
  return 0;
148✔
2021
}
148✔
2022

2023
static void initSuffixMatchNodes([[maybe_unused]] Logr::log_t log)
2024
{
148✔
2025
  {
148✔
2026
    SuffixMatchNode dontThrottleNames;
148✔
2027
    vector<string> parts;
148✔
2028
    stringtok(parts, ::arg()["dont-throttle-names"], " ,");
148✔
2029
    for (const auto& part : parts) {
148!
2030
      dontThrottleNames.add(DNSName(part));
×
2031
    }
×
2032
    g_dontThrottleNames.setState(std::move(dontThrottleNames));
148✔
2033

2034
    NetmaskGroup dontThrottleNetmasks;
148✔
2035
    dontThrottleNetmasks.toMasks(::arg()["dont-throttle-netmasks"]);
148✔
2036
    g_dontThrottleNetmasks.setState(std::move(dontThrottleNetmasks));
148✔
2037
  }
148✔
2038

2039
  {
148✔
2040
    SuffixMatchNode xdnssecNames;
148✔
2041
    vector<string> parts;
148✔
2042
    stringtok(parts, ::arg()["x-dnssec-names"], " ,");
148✔
2043
    for (const auto& part : parts) {
148!
2044
      xdnssecNames.add(DNSName(part));
×
2045
    }
×
2046
    g_xdnssec.setState(std::move(xdnssecNames));
148✔
2047
  }
148✔
2048

2049
  {
148✔
2050
    SuffixMatchNode dotauthNames;
148✔
2051
    vector<string> parts;
148✔
2052
    stringtok(parts, ::arg()["dot-to-auth-names"], " ,");
148✔
2053
#ifndef HAVE_DNS_OVER_TLS
2054
    if (!parts.empty()) {
2055
      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,
2056
           log->info(Logr::Error, "dot-to-auth-names setting contains names, but Recursor was built without DNS over TLS support. Setting will be ignored"));
2057
    }
2058
#endif
2059
    for (const auto& part : parts) {
148✔
2060
      dotauthNames.add(DNSName(part));
1✔
2061
    }
1✔
2062
    g_DoTToAuthNames.setState(std::move(dotauthNames));
148✔
2063
  }
148✔
2064
}
148✔
2065

2066
static void initCarbon()
2067
{
148✔
2068
  CarbonConfig config;
148✔
2069
  stringtok(config.servers, arg()["carbon-server"], ", ");
148✔
2070
  config.hostname = arg()["carbon-ourname"];
148✔
2071
  config.instance_name = arg()["carbon-instance"];
148✔
2072
  config.namespace_name = arg()["carbon-namespace"];
148✔
2073
  g_carbonConfig.setState(std::move(config));
148✔
2074
}
148✔
2075

2076
static int initDNS64(Logr::log_t log)
2077
{
148✔
2078
  if (!::arg()["dns64-prefix"].empty()) {
148✔
2079
    try {
3✔
2080
      auto dns64Prefix = Netmask(::arg()["dns64-prefix"]);
3✔
2081
      if (dns64Prefix.getBits() != 96) {
3!
2082
        SLOG(g_log << Logger::Error << "Invalid prefix for 'dns64-prefix', the current implementation only supports /96 prefixes: " << ::arg()["dns64-prefix"] << endl,
×
2083
             log->info(Logr::Error, "Invalid prefix for 'dns64-prefix', the current implementation only supports /96 prefixes", "prefix", Logging::Loggable(::arg()["dns64-prefix"])));
×
2084
        return 1;
×
2085
      }
×
2086
      g_dns64Prefix = dns64Prefix.getNetwork();
3✔
2087
      g_dns64PrefixReverse = reverseNameFromIP(*g_dns64Prefix);
3✔
2088
      /* /96 is 24 nibbles + 2 for "ip6.arpa." */
2089
      while (g_dns64PrefixReverse.countLabels() > 26) {
27✔
2090
        g_dns64PrefixReverse.chopOff();
24✔
2091
      }
24✔
2092
    }
3✔
2093
    catch (const NetmaskException& ne) {
3✔
2094
      SLOG(g_log << Logger::Error << "Invalid prefix '" << ::arg()["dns64-prefix"] << "' for 'dns64-prefix': " << ne.reason << endl,
×
2095
           log->info(Logr::Error, "Invalid prefix", "dns64-prefix", Logging::Loggable(::arg()["dns64-prefix"])));
×
2096
      return 1;
×
2097
    }
×
2098
  }
3✔
2099
  return 0;
148✔
2100
}
148✔
2101

2102
static int serviceMain(Logr::log_t log)
2103
{
148✔
2104
  g_log.setName(g_programname);
148✔
2105
  g_log.disableSyslog(::arg().mustDo("disable-syslog"));
148✔
2106
  g_log.setTimestamps(::arg().mustDo("log-timestamp"));
148✔
2107
  g_regressionTestMode = ::arg().mustDo("devonly-regression-test-mode");
148✔
2108

2109
  if (!::arg()["logging-facility"].empty()) {
148!
2110
    int val = logFacilityToLOG(::arg().asNum("logging-facility"));
×
2111
    if (val >= 0) {
×
2112
      g_log.setFacility(val);
×
2113
    }
×
2114
    else {
×
2115
      SLOG(g_log << Logger::Error << "Unknown logging facility " << ::arg().asNum("logging-facility") << endl,
×
2116
           log->info(Logr::Error, "Unknown logging facility", "facility", Logging::Loggable(::arg().asNum("logging-facility"))));
×
2117
    }
×
2118
  }
×
2119

2120
  g_disthashseed = dns_random_uint32();
148✔
2121

2122
  int ret = initNet(log);
148✔
2123
  if (ret != 0) {
148!
2124
    return ret;
×
2125
  }
×
2126
  // keep this ABOVE loadRecursorLuaConfig!
2127
  ret = initDNSSEC(log);
148✔
2128
  if (ret != 0) {
148!
2129
    return ret;
×
2130
  }
×
2131
  g_maxCacheEntries = ::arg().asNum("max-cache-entries");
148✔
2132

2133
  auto luaResult = luaconfig(false);
148✔
2134
  if (luaResult.d_ret != 0) {
148!
2135
    SLOG(g_log << Logger::Error << "Cannot load Lua or equivalent YAML configuration: " << luaResult.d_str << endl,
×
2136
         log->error(Logr::Error, luaResult.d_str, "Cannot load Lua or equivalent YAML configuration"));
×
2137
    return 1;
×
2138
  }
×
2139

2140
  parseACLs();
148✔
2141
  initPublicSuffixList(::arg()["public-suffix-list-file"]);
148✔
2142

2143
  initDontQuery(log);
148✔
2144

2145
  RecThreadInfo::setWeDistributeQueries(::arg().mustDo("pdns-distributes-queries"));
148✔
2146
  if (RecThreadInfo::weDistributeQueries()) {
148✔
2147
    SLOG(g_log << Logger::Warning << "PowerDNS Recursor itself will distribute queries over threads" << endl,
14✔
2148
         log->info(Logr::Notice, "PowerDNS Recursor itself will distribute queries over threads"));
14✔
2149
  }
14✔
2150

2151
  g_outgoingEDNSBufsize = ::arg().asNum("edns-outgoing-bufsize");
148✔
2152

2153
  if (::arg()["trace"] == "fail") {
148✔
2154
    SyncRes::setDefaultLogMode(SyncRes::Store);
1✔
2155
  }
1✔
2156
  else if (::arg().mustDo("trace")) {
147✔
2157
    SyncRes::setDefaultLogMode(SyncRes::Log);
133✔
2158
    ::arg().set("quiet") = "no";
133✔
2159
    g_quiet = false;
133✔
2160
  }
133✔
2161

2162
  ret = initSyncRes(log);
148✔
2163
  if (ret != 0) {
148!
2164
    return ret;
×
2165
  }
×
2166

2167
  g_proxyProtocolACL.toMasks(::arg()["proxy-protocol-from"]);
148✔
2168
  {
148✔
2169
    std::vector<std::string> vec;
148✔
2170
    stringtok(vec, ::arg()["proxy-protocol-exceptions"], ", ");
148✔
2171
    for (const auto& sockAddrStr : vec) {
148✔
2172
      ComboAddress sockAddr(sockAddrStr, 53);
1✔
2173
      g_proxyProtocolExceptions.emplace(sockAddr);
1✔
2174
    }
1✔
2175
  }
148✔
2176
  g_proxyProtocolMaximumSize = ::arg().asNum("proxy-protocol-maximum-size");
148✔
2177

2178
  ret = initDNS64(log);
148✔
2179
  if (ret != 0) {
148!
2180
    return ret;
×
2181
  }
×
2182
  g_networkTimeoutMsec = ::arg().asNum("network-timeout");
148✔
2183

2184
  std::tie(g_initialDomainMap, g_initialAllowNotifyFor) = parseZoneConfiguration(g_yamlSettings);
148✔
2185

2186
  g_latencyStatSize = ::arg().asNum("latency-statistic-size");
148✔
2187

2188
  g_logCommonErrors = ::arg().mustDo("log-common-errors");
148✔
2189
  g_logRPZChanges = ::arg().mustDo("log-rpz-changes");
148✔
2190

2191
  g_anyToTcp = ::arg().mustDo("any-to-tcp");
148✔
2192
  g_allowNoRD = ::arg().mustDo("allow-no-rd");
148✔
2193
  g_udpTruncationThreshold = ::arg().asNum("udp-truncation-threshold");
148✔
2194

2195
  g_lowercaseOutgoing = ::arg().mustDo("lowercase-outgoing");
148✔
2196

2197
  g_paddingFrom.toMasks(::arg()["edns-padding-from"]);
148✔
2198
  if (::arg()["edns-padding-mode"] == "always") {
148✔
2199
    g_paddingMode = PaddingMode::Always;
3✔
2200
  }
3✔
2201
  else if (::arg()["edns-padding-mode"] == "padded-queries-only") {
145!
2202
    g_paddingMode = PaddingMode::PaddedQueries;
145✔
2203
  }
145✔
2204
  else {
×
2205
    SLOG(g_log << Logger::Error << "Unknown edns-padding-mode: " << ::arg()["edns-padding-mode"] << endl,
×
2206
         log->info(Logr::Error, "Unknown edns-padding-mode", "edns-padding-mode", Logging::Loggable(::arg()["edns-padding-mode"])));
×
2207
    return 1;
×
2208
  }
×
2209
  g_paddingTag = ::arg().asNum("edns-padding-tag");
148✔
2210
  g_paddingOutgoing = ::arg().mustDo("edns-padding-out");
148✔
2211

2212
  RecThreadInfo::setNumDistributorThreads(::arg().asNum("distributor-threads"));
148✔
2213
  RecThreadInfo::setNumUDPWorkerThreads(::arg().asNum("threads"));
148✔
2214
  if (RecThreadInfo::numUDPWorkers() < 1) {
148!
2215
    SLOG(g_log << Logger::Warning << "Asked to run with 0 threads, raising to 1 instead" << endl,
×
2216
         log->info(Logr::Warning, "Asked to run with 0 threads, raising to 1 instead"));
×
2217
    RecThreadInfo::setNumUDPWorkerThreads(1);
×
2218
  }
×
2219
  RecThreadInfo::setNumTCPWorkerThreads(::arg().asNum("tcp-threads"));
148✔
2220
  if (RecThreadInfo::numTCPWorkers() < 1) {
148!
2221
    SLOG(g_log << Logger::Warning << "Asked to run with 0 TCP threads, raising to 1 instead" << endl,
×
2222
         log->info(Logr::Warning, "Asked to run with 0 TCP threads, raising to 1 instead"));
×
2223
    RecThreadInfo::setNumTCPWorkerThreads(1);
×
2224
  }
×
2225

2226
  g_maxMThreads = ::arg().asNum("max-mthreads");
148✔
2227

2228
  int64_t maxInFlight = ::arg().asNum("max-concurrent-requests-per-tcp-connection");
148✔
2229
  if (maxInFlight < 1 || maxInFlight > USHRT_MAX || maxInFlight >= g_maxMThreads) {
148!
2230
    SLOG(g_log << Logger::Warning << "Asked to run with illegal max-concurrent-requests-per-tcp-connection, setting to default (10)" << endl,
×
2231
         log->info(Logr::Warning, "Asked to run with illegal max-concurrent-requests-per-tcp-connection, setting to default (10)"));
×
2232
    TCPConnection::s_maxInFlight = 10;
×
2233
  }
×
2234
  else {
148✔
2235
    TCPConnection::s_maxInFlight = maxInFlight;
148✔
2236
  }
148✔
2237

2238
  int64_t millis = ::arg().asNum("tcp-out-max-idle-ms");
148✔
2239
  TCPOutConnectionManager::s_maxIdleTime = timeval{millis / 1000, (static_cast<suseconds_t>(millis) % 1000) * 1000};
148✔
2240
  TCPOutConnectionManager::s_maxIdlePerAuth = ::arg().asNum("tcp-out-max-idle-per-auth");
148✔
2241
  TCPOutConnectionManager::s_maxQueries = ::arg().asNum("tcp-out-max-queries");
148✔
2242
  TCPOutConnectionManager::s_maxIdlePerThread = ::arg().asNum("tcp-out-max-idle-per-thread");
148✔
2243

2244
  g_gettagNeedsEDNSOptions = ::arg().mustDo("gettag-needs-edns-options");
148✔
2245

2246
  s_statisticsInterval = ::arg().asNum("statistics-interval");
148✔
2247

2248
  SyncRes::s_addExtendedResolutionDNSErrors = ::arg().mustDo("extended-resolution-errors");
148✔
2249

2250
  if (::arg().asNum("aggressive-nsec-cache-size") > 0) {
148!
2251
    if (g_dnssecmode == DNSSECMode::ValidateAll || g_dnssecmode == DNSSECMode::ValidateForLog || g_dnssecmode == DNSSECMode::Process) {
148!
2252
      g_aggressiveNSECCache = make_unique<AggressiveNSECCache>(::arg().asNum("aggressive-nsec-cache-size"));
145✔
2253
    }
145✔
2254
    else {
3✔
2255
      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✔
2256
           log->info(Logr::Warning, "Aggressive NSEC/NSEC3 caching is enabled but DNSSEC validation is not set to 'validate', 'log-fail' or 'process', ignoring"));
3✔
2257
    }
3✔
2258
  }
148✔
2259

2260
  AggressiveNSECCache::s_nsec3DenialProofMaxCost = ::arg().asNum("aggressive-cache-max-nsec3-hash-cost");
148✔
2261
  AggressiveNSECCache::s_maxNSEC3CommonPrefix = static_cast<uint8_t>(std::round(std::log2(::arg().asNum("aggressive-cache-min-nsec3-hit-ratio"))));
148✔
2262
  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,
148✔
2263
       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)));
148✔
2264

2265
  initSuffixMatchNodes(log);
148✔
2266
  initCarbon();
148✔
2267
  initDistribution(log);
148✔
2268

2269
#ifdef NOD_ENABLED
148✔
2270
  // Setup newly observed domain globals
2271
  setupNODGlobal();
148✔
2272
#endif /* NOD_ENABLED */
148✔
2273

2274
  auto forks = initForks(log);
148✔
2275

2276
  checkOrFixFDS(log);
148✔
2277
  checkOrFixLinuxMapCountLimits(log);
148✔
2278

2279
#ifdef HAVE_LIBSODIUM
148✔
2280
  if (sodium_init() == -1) {
148!
2281
    SLOG(g_log << Logger::Error << "Unable to initialize sodium crypto library" << endl,
×
2282
         log->info(Logr::Error, "Unable to initialize sodium crypto library"));
×
2283
    return 99;
×
2284
  }
×
2285
#endif
148✔
2286

2287
  openssl_thread_setup();
148✔
2288
  openssl_seed();
148✔
2289

2290
  gid_t newgid = 0;
148✔
2291
  if (!::arg()["setgid"].empty()) {
148!
2292
    newgid = strToGID(::arg()["setgid"]);
×
2293
  }
×
2294
  uid_t newuid = 0;
148✔
2295
  if (!::arg()["setuid"].empty()) {
148!
2296
    newuid = strToUID(::arg()["setuid"]);
×
2297
  }
×
2298

2299
  Utility::dropGroupPrivs(newuid, newgid);
148✔
2300

2301
  ret = initControl(log, newuid, forks);
148✔
2302
  if (ret != 0) {
148!
2303
    return ret;
×
2304
  }
×
2305

2306
  {
148✔
2307
    auto lci = g_luaconfs.getCopy();
148✔
2308
    startLuaConfigDelayedThreads(lci.rpzs, lci.generation);
148✔
2309
  }
148✔
2310

2311
  RecThreadInfo::makeThreadPipes(log);
148✔
2312

2313
  g_tcpTimeout = ::arg().asNum("client-tcp-timeout");
148✔
2314
  g_maxTCPPerClient = ::arg().asNum("max-tcp-per-client");
148✔
2315
  g_tcpMaxQueriesPerConn = ::arg().asNum("max-tcp-queries-per-connection");
148✔
2316
  g_maxUDPQueriesPerRound = ::arg().asNum("max-udp-queries-per-round");
148✔
2317

2318
  g_useKernelTimestamp = ::arg().mustDo("protobuf-use-kernel-timestamp");
148✔
2319
  g_maxChainLength = ::arg().asNum("max-chain-length");
148✔
2320

2321
  disableStats(StatComponent::API, ::arg()["stats-api-blacklist"]);
148✔
2322
  disableStats(StatComponent::Carbon, ::arg()["stats-carbon-blacklist"]);
148✔
2323
  disableStats(StatComponent::RecControl, ::arg()["stats-rec-control-blacklist"]);
148✔
2324
  disableStats(StatComponent::SNMP, ::arg()["stats-snmp-blacklist"]);
148✔
2325

2326
  disableStats(StatComponent::API, ::arg()["stats-api-disabled-list"]);
148✔
2327
  disableStats(StatComponent::Carbon, ::arg()["stats-carbon-disabled-list"]);
148✔
2328
  disableStats(StatComponent::RecControl, ::arg()["stats-rec-control-disabled-list"]);
148✔
2329
  disableStats(StatComponent::SNMP, ::arg()["stats-snmp-disabled-list"]);
148✔
2330

2331
  // Run before any thread doing stats related things
2332
  registerAllStats();
148✔
2333

2334
  initSNMP(log);
148✔
2335

2336
  ret = initPorts(log);
148✔
2337
  if (ret != 0) {
148!
2338
    return ret;
×
2339
  }
×
2340

2341
#ifdef NOD_ENABLED
148✔
2342
  setupNODThread(log);
148✔
2343
#endif /* NOD_ENABLED */
148✔
2344

2345
  return RecThreadInfo::runThreads(log);
148✔
2346
}
148✔
2347

2348
static void handlePipeRequest(int fileDesc, FDMultiplexer::funcparam_t& /* var */)
2349
{
8,490✔
2350
  ThreadMSG* tmsg = nullptr;
8,490✔
2351

2352
  if (read(fileDesc, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // fd == readToThread || fd == readQueriesToThread NOLINT: sizeof correct
8,490!
2353
    unixDie("read from thread pipe returned wrong size or error");
×
2354
  }
×
2355

2356
  void* resp = nullptr;
8,490✔
2357
  try {
8,490✔
2358
    resp = tmsg->func();
8,490✔
2359
  }
8,490✔
2360
  catch (const PDNSException& pdnsException) {
8,490✔
2361
    s_rateLimitedLogger.log(g_slog->withName("runtime"), "PIPE function", pdnsException);
×
2362
  }
×
2363
  catch (const std::exception& stdException) {
8,490✔
2364
    s_rateLimitedLogger.log(g_slog->withName("runtime"), "PIPE function", stdException);
×
2365
  }
×
2366
  catch (...) {
8,490✔
2367
    s_rateLimitedLogger.log(g_slog->withName("runtime"), "PIPE function");
×
2368
  }
×
2369
  if (tmsg->wantAnswer) {
8,497✔
2370
    if (write(RecThreadInfo::self().getPipes().writeFromThread, &resp, sizeof(resp)) != sizeof(resp)) {
4,893!
2371
      delete tmsg; // NOLINT: manual ownership handling
×
2372
      unixDie("write to thread pipe returned wrong size or error");
×
2373
    }
×
2374
  }
4,893✔
2375

2376
  delete tmsg; // NOLINT: manual ownership handling
8,495✔
2377
}
8,495✔
2378

2379
static void handleRCC(int fileDesc, FDMultiplexer::funcparam_t& /* var */)
2380
{
464✔
2381
  auto log = g_slog->withName("control");
464✔
2382
  try {
464✔
2383
    FDWrapper clientfd = accept(fileDesc, nullptr, nullptr);
464✔
2384
    if (clientfd == -1) {
464!
2385
      throw PDNSException("accept failed");
×
2386
    }
×
2387
    string msg = g_rcc.recv(clientfd).d_str;
464✔
2388
    SLOG(g_log << Logger::Info << "Received rec_control command '" << msg << "' via controlsocket" << endl,
464✔
2389
         log->info(Logr::Info, "Received rec_control command via control socket", "command", Logging::Loggable(msg)));
464✔
2390

2391
    RecursorControlParser::func_t* command = nullptr;
464✔
2392
    auto answer = RecursorControlParser::getAnswer(clientfd, msg, &command);
464✔
2393

2394
    g_rcc.send(clientfd, answer);
464✔
2395
    command();
464✔
2396
  }
464✔
2397
  catch (const std::exception& e) {
464✔
2398
    SLOG(g_log << Logger::Error << "Error dealing with control socket request: " << e.what() << endl,
×
2399
         log->error(Logr::Error, e.what(), "Exception while dealing with control socket request", "exception", Logging::Loggable("std::exception")));
×
2400
  }
×
2401
  catch (const PDNSException& ae) {
464✔
2402
    SLOG(g_log << Logger::Error << "Error dealing with control socket request: " << ae.reason << endl,
×
2403
         log->error(Logr::Error, ae.reason, "Exception while dealing with control socket request", "exception", Logging::Loggable("PDNSException")));
×
2404
  }
×
2405
}
464✔
2406

2407
class PeriodicTask
2408
{
2409
public:
2410
  PeriodicTask(const string& aName, time_t aTime) :
2411
    period{aTime, 0}, name(aName)
2412
  {
2,398✔
2413
    if (aTime <= 0) {
2,398!
2414
      throw PDNSException("Invalid period of periodic task " + aName);
×
2415
    }
×
2416
  }
2,398✔
2417

2418
  void runIfDue(struct timeval& now, const std::function<void()>& function)
2419
  {
4,729✔
2420
    if (last_run < now - period) {
4,729✔
2421
      function();
2,600✔
2422
      Utility::gettimeofday(&last_run);
2,600✔
2423
      now = last_run;
2,600✔
2424
    }
2,600✔
2425
  }
4,729✔
2426

2427
  [[nodiscard]] time_t getPeriod() const
2428
  {
28✔
2429
    return period.tv_sec;
28✔
2430
  }
28✔
2431

2432
  void setPeriod(time_t newperiod)
2433
  {
285✔
2434
    period.tv_sec = newperiod;
285✔
2435
  }
285✔
2436

2437
  void updateLastRun()
2438
  {
82✔
2439
    Utility::gettimeofday(&last_run);
82✔
2440
  }
82✔
2441

2442
  [[nodiscard]] bool hasRun() const
2443
  {
201✔
2444
    return last_run.tv_sec != 0 || last_run.tv_usec != 0;
201!
2445
  }
201✔
2446

2447
private:
2448
  struct timeval last_run
2449
  {
2450
    0, 0
2451
  };
2452
  struct timeval period;
2453
  string name;
2454
};
2455

2456
static void houseKeepingWork(Logr::log_t log)
2457
{
910✔
2458
  struct timeval now
910✔
2459
  {
910✔
2460
  };
910✔
2461
  Utility::gettimeofday(&now);
910✔
2462
  t_Counters.updateSnap(now, g_regressionTestMode);
910✔
2463

2464
  // Below are the tasks that run for every recursorThread, including handler and taskThread
2465

2466
  static thread_local PeriodicTask pruneTCPTask{"pruneTCPTask", 5};
910✔
2467
  pruneTCPTask.runIfDue(now, [now]() {
910✔
2468
    t_tcp_manager.cleanup(now);
674✔
2469
  });
674✔
2470

2471
  const auto& info = RecThreadInfo::self();
910✔
2472

2473
  // Threads handling packets process config changes in the input path, but not all threads process input packets
2474
  // distr threads only process TCP, so that may not happenn very often. So do all periodically.
2475
  static thread_local PeriodicTask exportConfigTask{"exportConfigTask", 30};
910✔
2476
  auto luaconfsLocal = g_luaconfs.getLocal();
910✔
2477
  exportConfigTask.runIfDue(now, [&luaconfsLocal]() {
910✔
2478
    checkProtobufExport(luaconfsLocal);
600✔
2479
    checkOutgoingProtobufExport(luaconfsLocal);
600✔
2480
#ifdef HAVE_FSTRM
600✔
2481
    checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
600✔
2482
    checkFrameStreamExport(luaconfsLocal, luaconfsLocal->nodFrameStreamExportConfig, t_nodFrameStreamServersInfo);
600✔
2483
#endif
600✔
2484
  });
600✔
2485

2486
  // Below are the thread specific tasks for the handler and the taskThread
2487
  // Likley a few handler tasks could be moved to the taskThread
2488
  if (info.isTaskThread()) {
910✔
2489
    // TaskQueue is run always
2490
    runTasks(10, g_logCommonErrors);
309✔
2491

2492
    static PeriodicTask ztcTask{"ZTC", 60};
309✔
2493
    static map<DNSName, RecZoneToCache::State> ztcStates;
309✔
2494
    ztcTask.runIfDue(now, [&luaconfsLocal]() {
309✔
2495
      RecZoneToCache::maintainStates(luaconfsLocal->ztcConfigs, ztcStates, luaconfsLocal->generation);
136✔
2496
      for (const auto& ztc : luaconfsLocal->ztcConfigs) {
136✔
2497
        RecZoneToCache::ZoneToCache(ztc.second, ztcStates.at(ztc.first));
1✔
2498
      }
1✔
2499
    });
136✔
2500
  }
309✔
2501
  else if (info.isHandler()) {
601✔
2502
    if (g_packetCache) {
201✔
2503
      static PeriodicTask packetCacheTask{"packetCacheTask", 5};
189✔
2504
      packetCacheTask.runIfDue(now, [now]() {
189✔
2505
        g_packetCache->doPruneTo(now.tv_sec, g_maxPacketCacheEntries);
106✔
2506
      });
106✔
2507
    }
189✔
2508
    static PeriodicTask recordCachePruneTask{"RecordCachePruneTask", 5};
201✔
2509
    recordCachePruneTask.runIfDue(now, [now]() {
201✔
2510
      g_recCache->doPrune(now.tv_sec, g_maxCacheEntries);
112✔
2511
    });
112✔
2512

2513
    static PeriodicTask negCachePruneTask{"NegCachePrunteTask", 5};
201✔
2514
    negCachePruneTask.runIfDue(now, [now]() {
201✔
2515
      g_negCache->prune(now.tv_sec, g_maxCacheEntries / 8);
112✔
2516
    });
112✔
2517

2518
    static PeriodicTask aggrNSECPruneTask{"AggrNSECPruneTask", 5};
201✔
2519
    aggrNSECPruneTask.runIfDue(now, [now]() {
201✔
2520
      if (g_aggressiveNSECCache) {
112✔
2521
        g_aggressiveNSECCache->prune(now.tv_sec);
108✔
2522
      }
108✔
2523
    });
112✔
2524

2525
    static PeriodicTask pruneNSpeedTask{"pruneNSSpeedTask", 30};
201✔
2526
    pruneNSpeedTask.runIfDue(now, [now]() {
201✔
2527
      SyncRes::pruneNSSpeeds(now.tv_sec - 300);
83✔
2528
    });
83✔
2529

2530
    static PeriodicTask pruneEDNSTask{"pruneEDNSTask", 60};
201✔
2531
    pruneEDNSTask.runIfDue(now, [now]() {
201✔
2532
      SyncRes::pruneEDNSStatuses(now.tv_sec);
82✔
2533
    });
82✔
2534

2535
    if (SyncRes::s_max_busy_dot_probes > 0) {
201!
2536
      static PeriodicTask pruneDoTProbeMap{"pruneDoTProbeMapTask", 60};
×
2537
      pruneDoTProbeMap.runIfDue(now, [now]() {
×
2538
        SyncRes::pruneDoTProbeMap(now.tv_sec);
×
2539
      });
×
2540
    }
×
2541

2542
    static PeriodicTask pruneThrottledTask{"pruneThrottledTask", 5};
201✔
2543
    pruneThrottledTask.runIfDue(now, [now]() {
201✔
2544
      SyncRes::pruneThrottledServers(now.tv_sec);
112✔
2545
    });
112✔
2546

2547
    static PeriodicTask pruneFailedServersTask{"pruneFailedServerTask", 5};
201✔
2548
    pruneFailedServersTask.runIfDue(now, [now]() {
201✔
2549
      SyncRes::pruneFailedServers(now.tv_sec - static_cast<time_t>(SyncRes::s_serverdownthrottletime * 10));
112✔
2550
    });
112✔
2551

2552
    static PeriodicTask pruneNonResolvingTask{"pruneNonResolvingTask", 5};
201✔
2553
    pruneNonResolvingTask.runIfDue(now, [now]() {
201✔
2554
      SyncRes::pruneNonResolving(now.tv_sec - SyncRes::s_nonresolvingnsthrottletime);
112✔
2555
    });
112✔
2556

2557
    static PeriodicTask pruneSaveParentSetTask{"pruneSaveParentSetTask", 60};
201✔
2558
    pruneSaveParentSetTask.runIfDue(now, [now]() {
201✔
2559
      SyncRes::pruneSaveParentsNSSets(now.tv_sec);
82✔
2560
    });
82✔
2561

2562
    // By default, refresh at 80% of max-cache-ttl with a minimum period of 10s
2563
    const unsigned int minRootRefreshInterval = 10;
201✔
2564
    static PeriodicTask rootUpdateTask{"rootUpdateTask", std::max(SyncRes::s_maxcachettl * 8 / 10, minRootRefreshInterval)};
201✔
2565
    rootUpdateTask.runIfDue(now, [now, &log, minRootRefreshInterval]() {
201✔
2566
      int res = 0;
84✔
2567
      if (!g_regressionTestMode) {
84✔
2568
        res = SyncRes::getRootNS(now, nullptr, 0, log);
80✔
2569
      }
80✔
2570
      if (res == 0) {
84✔
2571
        // Success, go back to the defaut period
2572
        rootUpdateTask.setPeriod(std::max(SyncRes::s_maxcachettl * 8 / 10, minRootRefreshInterval));
56✔
2573
      }
56✔
2574
      else {
28✔
2575
        // On failure, go to the middle of the remaining period (initially 80% / 8 = 10%) and shorten the interval on each
2576
        // failure by dividing the existing interval by 8, keeping the minimum interval at 10s.
2577
        // So with a 1 day period and failures we'll see a refresh attempt at 69120, 69120+11520, 69120+11520+1440, ...
2578
        rootUpdateTask.setPeriod(std::max<time_t>(rootUpdateTask.getPeriod() / 8, minRootRefreshInterval));
28✔
2579
      }
28✔
2580
    });
84✔
2581

2582
    static PeriodicTask secpollTask{"secpollTask", 3600};
201✔
2583
    static time_t t_last_secpoll;
201✔
2584
    secpollTask.runIfDue(now, [&log]() {
201✔
2585
      try {
82✔
2586
        doSecPoll(&t_last_secpoll, log);
82✔
2587
      }
82✔
2588
      catch (const std::exception& e) {
82✔
2589
        SLOG(g_log << Logger::Error << "Exception while performing security poll: " << e.what() << endl,
×
2590
             log->error(Logr::Error, e.what(), "Exception while performing security poll"));
×
2591
      }
×
2592
      catch (const PDNSException& e) {
82✔
2593
        SLOG(g_log << Logger::Error << "Exception while performing security poll: " << e.reason << endl,
×
2594
             log->error(Logr::Error, e.reason, "Exception while performing security poll"));
×
2595
      }
×
2596
      catch (const ImmediateServFailException& e) {
82✔
2597
        SLOG(g_log << Logger::Error << "Exception while performing security poll: " << e.reason << endl,
×
2598
             log->error(Logr::Error, e.reason, "Exception while performing security poll"));
×
2599
      }
×
2600
      catch (const PolicyHitException& e) {
82✔
2601
        SLOG(g_log << Logger::Error << "Policy hit while performing security poll" << endl,
×
2602
             log->info(Logr::Error, "Policy hit while performing security poll"));
×
2603
      }
×
2604
      catch (...) {
82✔
2605
        SLOG(g_log << Logger::Error << "Exception while performing security poll" << endl,
×
2606
             log->info(Logr::Error, "Exception while performing security poll"));
×
2607
      }
×
2608
    });
82✔
2609

2610
    const time_t taInterval = std::max(1, static_cast<int>(luaconfsLocal->trustAnchorFileInfo.interval) * 3600);
201✔
2611
    static PeriodicTask trustAnchorTask{"trustAnchorTask", taInterval};
201✔
2612
    if (!trustAnchorTask.hasRun()) {
201✔
2613
      // Loading the Lua config file already "refreshed" the TAs
2614
      trustAnchorTask.updateLastRun();
82✔
2615
    }
82✔
2616
    // interval might have ben updated
2617
    trustAnchorTask.setPeriod(taInterval);
201✔
2618
    trustAnchorTask.runIfDue(now, [&luaconfsLocal, &log]() {
201✔
2619
      if (!luaconfsLocal->trustAnchorFileInfo.fname.empty() && luaconfsLocal->trustAnchorFileInfo.interval != 0) {
×
2620
        SLOG(g_log << Logger::Debug << "Refreshing Trust Anchors from file" << endl,
×
2621
             log->info(Logr::Debug, "Refreshing Trust Anchors from file"));
×
2622
        try {
×
2623
          map<DNSName, dsset_t> dsAnchors;
×
2624
          if (updateTrustAnchorsFromFile(luaconfsLocal->trustAnchorFileInfo.fname, dsAnchors, log)) {
×
2625
            g_luaconfs.modify([&dsAnchors](LuaConfigItems& lci) {
×
2626
              lci.dsAnchors = dsAnchors;
×
2627
            });
×
2628
          }
×
2629
        }
×
2630
        catch (const PDNSException& pe) {
×
2631
          SLOG(g_log << Logger::Error << "Unable to update Trust Anchors: " << pe.reason << endl,
×
2632
               log->error(Logr::Error, pe.reason, "Unable to update Trust Anchors"));
×
2633
        }
×
2634
      }
×
2635
    });
×
2636
  }
201✔
2637
  t_Counters.updateSnap(g_regressionTestMode);
910✔
2638
}
910✔
2639

2640
static void houseKeeping(void* /* ignored */)
2641
{
939✔
2642
  auto log = g_slog->withName("housekeeping");
939✔
2643
  static thread_local bool t_running; // houseKeeping can get suspended in secpoll, and be restarted, which makes us do duplicate work
939✔
2644

2645
  try {
939✔
2646
    if (t_running) {
939✔
2647
      return;
30✔
2648
    }
30✔
2649
    t_running = true;
909✔
2650
    houseKeepingWork(log);
909✔
2651
    t_running = false;
909✔
2652
  }
909✔
2653
  catch (const PDNSException& ae) {
939✔
2654
    t_running = false;
×
2655
    SLOG(g_log << Logger::Error << "Fatal error in housekeeping thread: " << ae.reason << endl,
×
2656
         log->error(Logr::Error, ae.reason, "Fatal error in housekeeping thread"));
×
2657
    throw;
×
2658
  }
×
2659
  catch (...) {
939✔
2660
    t_running = false;
×
2661
    SLOG(g_log << Logger::Error << "Uncaught exception in housekeeping thread" << endl,
×
2662
         log->info(Logr::Error, "Uncaught exception in housekeeping thread"));
×
2663
    throw;
×
2664
  }
×
2665
}
939✔
2666

2667
static void runLuaMaintenance(RecThreadInfo& threadInfo, time_t& last_lua_maintenance, time_t luaMaintenanceInterval)
2668
{
22,789✔
2669
  if (t_pdl != nullptr) {
22,789✔
2670
    // lua-dns-script directive is present, call the maintenance callback if needed
2671
    if (threadInfo.isWorker()) { // either UDP of TCP worker
2,121✔
2672
      // Only on threads processing queries
2673
      if (g_now.tv_sec - last_lua_maintenance >= luaMaintenanceInterval) {
2,100✔
2674
        struct timeval start
457✔
2675
        {
457✔
2676
        };
457✔
2677
        Utility::gettimeofday(&start);
457✔
2678
        t_pdl->maintenance();
457✔
2679
        last_lua_maintenance = g_now.tv_sec;
457✔
2680
        struct timeval stop
457✔
2681
        {
457✔
2682
        };
457✔
2683
        Utility::gettimeofday(&stop);
457✔
2684
        t_Counters.at(rec::Counter::maintenanceUsec) += uSec(stop - start);
457✔
2685
        ++t_Counters.at(rec::Counter::maintenanceCalls);
457✔
2686
      }
457✔
2687
    }
2,100✔
2688
  }
2,121✔
2689
}
22,789✔
2690

2691
static void runTCPMaintenance(RecThreadInfo& threadInfo, bool& listenOnTCP, unsigned int maxTcpClients)
2692
{
21,907✔
2693
  if (threadInfo.isTCPListener()) {
21,907✔
2694
    if (listenOnTCP) {
3,654!
2695
      if (TCPConnection::getCurrentConnections() > maxTcpClients) { // shutdown, too many connections
3,654!
2696
        for (const auto fileDesc : threadInfo.getTCPSockets()) {
×
2697
          t_fdm->removeReadFD(fileDesc);
×
2698
        }
×
2699
        listenOnTCP = false;
×
2700
      }
×
2701
    }
3,654✔
2702
    else {
×
2703
      if (TCPConnection::getCurrentConnections() <= maxTcpClients) { // reenable
×
2704
        for (const auto fileDesc : threadInfo.getTCPSockets()) {
×
2705
          t_fdm->addReadFD(fileDesc, handleNewTCPQuestion);
×
2706
        }
×
2707
        listenOnTCP = true;
×
2708
      }
×
2709
    }
×
2710
  }
3,654✔
2711
}
21,907✔
2712

2713
static void recLoop()
2714
{
780✔
2715
  unsigned int maxTcpClients = ::arg().asNum("max-tcp-clients");
780✔
2716
  bool listenOnTCP{true};
780✔
2717
  time_t last_stat = 0;
780✔
2718
  time_t last_carbon = 0;
780✔
2719
  time_t last_lua_maintenance = 0;
780✔
2720
  time_t carbonInterval = ::arg().asNum("carbon-interval");
780✔
2721
  time_t luaMaintenanceInterval = ::arg().asNum("lua-maintenance-interval");
780✔
2722

2723
  auto& threadInfo = RecThreadInfo::self();
780✔
2724

2725
  while (!RecursorControlChannel::stop) {
22,809✔
2726
    try {
22,787✔
2727
      while (g_multiTasker->schedule(g_now)) {
29,495✔
2728
        ; // MTasker letting the mthreads do their thing
6,708✔
2729
      }
6,708✔
2730

2731
      // Use primes, it avoid not being scheduled in cases where the counter has a regular pattern.
2732
      // We want to call handler thread often, it gets scheduled about 2 times per second
2733
      if (((threadInfo.isHandler() || threadInfo.isTaskThread()) && s_counter % 11 == 0) || s_counter % 499 == 0) {
22,787✔
2734
        timeval start{};
1,013✔
2735
        Utility::gettimeofday(&start);
1,013✔
2736
        g_multiTasker->makeThread(houseKeeping, nullptr);
1,013✔
2737
        if (!threadInfo.isTaskThread()) {
1,013✔
2738
          timeval stop{};
684✔
2739
          Utility::gettimeofday(&stop);
684✔
2740
          t_Counters.at(rec::Counter::maintenanceUsec) += uSec(stop - start);
684✔
2741
          ++t_Counters.at(rec::Counter::maintenanceCalls);
684✔
2742
        }
684✔
2743
      }
1,013✔
2744

2745
      if (s_counter % 55 == 0) {
22,787✔
2746
        auto expired = t_fdm->getTimeouts(g_now);
498✔
2747

2748
        for (const auto& exp : expired) {
498!
2749
          auto conn = boost::any_cast<shared_ptr<TCPConnection>>(exp.second);
×
2750
          if (g_logCommonErrors) {
×
2751
            SLOG(g_log << Logger::Warning << "Timeout from remote TCP client " << conn->d_remote.toStringWithPort() << endl,
×
2752
                 g_slogtcpin->info(Logr::Warning, "Timeout from remote TCP client", "remote", Logging::Loggable(conn->d_remote)));
×
2753
          }
×
2754
          t_fdm->removeReadFD(exp.first);
×
2755
        }
×
2756
      }
498✔
2757

2758
      s_counter++;
22,787✔
2759

2760
      if (threadInfo.isHandler()) {
22,787✔
2761
        if (statsWanted || (s_statisticsInterval > 0 && (g_now.tv_sec - last_stat) >= s_statisticsInterval)) {
1,856✔
2762
          doStats();
49✔
2763
          last_stat = g_now.tv_sec;
49✔
2764
        }
49✔
2765

2766
        Utility::gettimeofday(&g_now, nullptr);
1,856✔
2767

2768
        if ((g_now.tv_sec - last_carbon) >= carbonInterval) {
1,856✔
2769
          g_multiTasker->makeThread(doCarbonDump, nullptr);
150✔
2770
          last_carbon = g_now.tv_sec;
150✔
2771
        }
150✔
2772
      }
1,856✔
2773
      runLuaMaintenance(threadInfo, last_lua_maintenance, luaMaintenanceInterval);
22,787✔
2774

2775
      auto timeoutUsec = g_multiTasker->nextWaiterDelayUsec(500000);
22,787✔
2776
      t_fdm->run(&g_now, static_cast<int>(timeoutUsec / 1000));
22,787✔
2777
      // 'run' updates g_now for us
2778

2779
      runTCPMaintenance(threadInfo, listenOnTCP, maxTcpClients);
22,787✔
2780
    }
22,787✔
2781
    catch (const PDNSException& pdnsException) {
22,787✔
2782
      s_rateLimitedLogger.log(g_slog->withName("runtime"), "recLoop", pdnsException);
×
2783
    }
×
2784
    catch (const std::exception& stdException) {
22,787✔
2785
      s_rateLimitedLogger.log(g_slog->withName("runtime"), "recLoop", stdException);
×
2786
    }
×
2787
    catch (...) {
22,787✔
2788
      s_rateLimitedLogger.log(g_slog->withName("runtime"), "recLoop");
×
2789
    }
×
2790
  }
22,787✔
2791
}
780✔
2792

2793
static void recursorThread()
2794
{
781✔
2795
  auto log = g_slog->withName("runtime");
781✔
2796
  t_Counters.updateSnap(true);
781✔
2797
  try {
781✔
2798
    auto& threadInfo = RecThreadInfo::self();
781✔
2799
    {
781✔
2800
      SyncRes tmp(g_now); // make sure it allocates tsstorage before we do anything, like primeHints or so..
781✔
2801
      SyncRes::setDomainMap(g_initialDomainMap);
781✔
2802
      t_allowFrom = g_initialAllowFrom;
781✔
2803
      t_allowNotifyFrom = g_initialAllowNotifyFrom;
781✔
2804
      t_allowNotifyFor = g_initialAllowNotifyFor;
781✔
2805
      t_udpclientsocks = std::make_unique<UDPClientSocks>();
781✔
2806
      t_tcpClientCounts = std::make_unique<tcpClientCounts_t>();
781✔
2807
      if (g_proxyMapping) {
781✔
2808
        t_proxyMapping = make_unique<ProxyMapping>(*g_proxyMapping);
30✔
2809
      }
30✔
2810
      else {
751✔
2811
        t_proxyMapping = nullptr;
751✔
2812
      }
751✔
2813

2814
      if (threadInfo.isHandler()) {
781✔
2815
        if (!primeHints()) {
148!
2816
          threadInfo.setExitCode(EXIT_FAILURE);
×
2817
          RecursorControlChannel::stop = true;
×
2818
          SLOG(g_log << Logger::Critical << "Priming cache failed, stopping" << endl,
×
2819
               log->info(Logr::Critical, "Priming cache failed, stopping"));
×
2820
        }
×
2821
        SLOG(g_log << Logger::Debug << "Done priming cache with root hints" << endl,
148✔
2822
             log->info(Logr::Debug, "Done priming cache with root hints"));
148✔
2823
      }
148✔
2824
    }
781✔
2825

2826
    /* the listener threads handle TCP queries */
2827
    if (threadInfo.isWorker() || threadInfo.isListener()) {
781✔
2828
      try {
483✔
2829
        if (!::arg()["lua-dns-script"].empty()) {
483✔
2830
          t_pdl = std::make_shared<RecursorLua4>();
118✔
2831
          t_pdl->loadFile(::arg()["lua-dns-script"]);
118✔
2832
          SLOG(g_log << Logger::Warning << "Loaded 'lua' script from '" << ::arg()["lua-dns-script"] << "'" << endl,
118✔
2833
               log->info(Logr::Warning, "Loading Lua script from file", "name", Logging::Loggable(::arg()["lua-dns-script"])));
118✔
2834
        }
118✔
2835
      }
483✔
2836
      catch (std::exception& e) {
483✔
2837
        SLOG(g_log << Logger::Error << "Failed to load 'lua' script from '" << ::arg()["lua-dns-script"] << "': " << e.what() << endl,
×
2838
             log->error(Logr::Error, e.what(), "Failed to load Lua script from file", "name", Logging::Loggable(::arg()["lua-dns-script"])));
×
2839
        _exit(99);
×
2840
      }
×
2841
    }
483✔
2842

2843
    if (unsigned int ringsize = ::arg().asNum("stats-ringbuffer-entries") / RecThreadInfo::numUDPWorkers(); ringsize != 0) {
781✔
2844
      t_remotes = std::make_unique<addrringbuf_t>();
781✔
2845
      if (RecThreadInfo::weDistributeQueries()) {
781✔
2846
        t_remotes->set_capacity(::arg().asNum("stats-ringbuffer-entries") / RecThreadInfo::numDistributors());
111✔
2847
      }
111✔
2848
      else {
670✔
2849
        t_remotes->set_capacity(ringsize);
670✔
2850
      }
670✔
2851
      t_servfailremotes = std::make_unique<addrringbuf_t>();
781✔
2852
      t_servfailremotes->set_capacity(ringsize);
781✔
2853
      t_bogusremotes = std::make_unique<addrringbuf_t>();
781✔
2854
      t_bogusremotes->set_capacity(ringsize);
781✔
2855
      t_largeanswerremotes = std::make_unique<addrringbuf_t>();
781✔
2856
      t_largeanswerremotes->set_capacity(ringsize);
781✔
2857
      t_timeouts = std::make_unique<addrringbuf_t>();
781✔
2858
      t_timeouts->set_capacity(ringsize);
781✔
2859

2860
      t_queryring = std::make_unique<boost::circular_buffer<pair<DNSName, uint16_t>>>();
781✔
2861
      t_queryring->set_capacity(ringsize);
781✔
2862
      t_servfailqueryring = std::make_unique<boost::circular_buffer<pair<DNSName, uint16_t>>>();
781✔
2863
      t_servfailqueryring->set_capacity(ringsize);
781✔
2864
      t_bogusqueryring = std::make_unique<boost::circular_buffer<pair<DNSName, uint16_t>>>();
781✔
2865
      t_bogusqueryring->set_capacity(ringsize);
781✔
2866
    }
781✔
2867
    g_multiTasker = std::make_unique<MT_t>(::arg().asNum("stack-size"), ::arg().asNum("stack-cache-size"));
780✔
2868
    threadInfo.setMT(g_multiTasker.get());
780✔
2869

2870
    {
780✔
2871
      /* start protobuf export threads if needed, don;'t keep a ref to lua config around */
2872
      auto luaconfsLocal = g_luaconfs.getLocal();
780✔
2873
      checkProtobufExport(luaconfsLocal);
780✔
2874
      checkOutgoingProtobufExport(luaconfsLocal);
780✔
2875
#ifdef HAVE_FSTRM
780✔
2876
      checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
780✔
2877
      checkFrameStreamExport(luaconfsLocal, luaconfsLocal->nodFrameStreamExportConfig, t_nodFrameStreamServersInfo);
780✔
2878
#endif
780✔
2879
    }
780✔
2880

2881
    t_fdm = unique_ptr<FDMultiplexer>(getMultiplexer(log));
780✔
2882

2883
    std::unique_ptr<RecursorWebServer> rws;
780✔
2884

2885
    t_fdm->addReadFD(threadInfo.getPipes().readToThread, handlePipeRequest);
780✔
2886

2887
    if (threadInfo.isHandler()) {
780✔
2888
      if (::arg().mustDo("webserver")) {
148✔
2889
        SLOG(g_log << Logger::Warning << "Enabling web server" << endl,
12✔
2890
             log->info(Logr::Info, "Enabling web server"));
12✔
2891
        try {
12✔
2892
          rws = make_unique<RecursorWebServer>(t_fdm.get());
12✔
2893
        }
12✔
2894
        catch (const PDNSException& e) {
12✔
2895
          SLOG(g_log << Logger::Error << "Unable to start the internal web server: " << e.reason << endl,
×
2896
               log->error(Logr::Critical, e.reason, "Exception while starting internal web server"));
×
2897
          _exit(99);
×
2898
        }
×
2899
      }
12✔
2900
      SLOG(g_log << Logger::Info << "Enabled '" << t_fdm->getName() << "' multiplexer" << endl,
148✔
2901
           log->info(Logr::Info, "Enabled multiplexer", "name", Logging::Loggable(t_fdm->getName())));
148✔
2902
    }
148✔
2903
    else {
632✔
2904
      t_fdm->addReadFD(threadInfo.getPipes().readQueriesToThread, handlePipeRequest);
632✔
2905

2906
      if (threadInfo.isListener()) {
632✔
2907
        if (g_reusePort) {
430✔
2908
          /* then every listener has its own FDs */
2909
          for (const auto& deferred : threadInfo.getDeferredAdds()) {
407✔
2910
            t_fdm->addReadFD(deferred.first, deferred.second);
407✔
2911
          }
407✔
2912
        }
403✔
2913
        else {
27✔
2914
          /* otherwise all listeners are listening on the same ones */
2915
          for (const auto& deferred : threadInfo.isTCPListener() ? s_deferredTCPadds : s_deferredUDPadds) {
27✔
2916
            t_fdm->addReadFD(deferred.first, deferred.second);
24✔
2917
          }
24✔
2918
        }
27✔
2919
      }
430✔
2920
    }
632✔
2921

2922
    if (threadInfo.isHandler()) {
780✔
2923
      t_fdm->addReadFD(g_rcc.d_fd, handleRCC); // control channel
148✔
2924
    }
148✔
2925

2926
#ifdef HAVE_SYSTEMD
780✔
2927
    if (threadInfo.isHandler()) {
780✔
2928
      // There is a race, as some threads might not be ready yet to do work.
2929
      // To solve that, threads should notify RecThreadInfo they are done initializing.
2930
      // But we lack a mechanism for that at this point in time.
2931
      sd_notify(0, "READY=1");
148✔
2932
    }
148✔
2933
#endif
780✔
2934

2935
    recLoop();
780✔
2936
  }
780✔
2937
  catch (const PDNSException& ae) {
781✔
2938
    SLOG(g_log << Logger::Error << "Exception: " << ae.reason << endl,
×
2939
         log->error(Logr::Error, ae.reason, "Exception in RecursorThread", "exception", Logging::Loggable("PDNSException")));
×
2940
  }
×
2941
  catch (const std::exception& e) {
781✔
2942
    SLOG(g_log << Logger::Error << "STL Exception: " << e.what() << endl,
×
2943
         log->error(Logr::Error, e.what(), "Exception in RecursorThread", "exception", Logging::Loggable("std::exception")));
×
2944
  }
×
2945
  catch (...) {
781✔
2946
    SLOG(g_log << Logger::Error << "any other exception in main: " << endl,
×
2947
         log->info(Logr::Error, "Exception in RecursorThread"));
×
2948
  }
×
2949
}
781✔
2950

2951
static pair<int, bool> doYamlConfig(int argc, char* argv[], const pdns::rust::settings::rec::Recursorsettings& settings) // NOLINT: Posix API
2952
{
5✔
2953
  if (!::arg().mustDo("config")) {
5!
2954
    return {0, false};
5✔
2955
  }
5✔
2956
  const string config = ::arg()["config"];
×
2957
  if (config == "diff" || config.empty()) {
×
2958
    ::arg().parse(argc, argv);
×
2959
    ProxyMapping proxyMapping;
×
2960
    LuaConfigItems lci;
×
2961
    pdns::settings::rec::fromBridgeStructToLuaConfig(settings, lci, proxyMapping);
×
2962
    auto yaml = settings.to_yaml_string();
×
2963
    cout << yaml << endl;
×
2964
  }
×
2965
  else if (config == "default") {
×
2966
    auto yaml = pdns::settings::rec::defaultsToYaml();
×
2967
    cout << yaml << endl;
×
2968
  }
×
2969
  else if (config == "check") {
×
2970
    // Kinda redundant, if we came here we already read and checked the config....x
2971
  }
×
2972
  return {0, true};
×
2973
}
5✔
2974

2975
static pair<int, bool> doConfig(Logr::log_t startupLog, const string& configname, int argc, char* argv[]) // NOLINT: Posix API
2976
{
143✔
2977
  if (::arg().mustDo("config")) {
143!
2978
    string config = ::arg()["config"];
×
2979
    if (config == "check") {
×
2980
      try {
×
2981
        if (!::arg().file(configname)) {
×
2982
          SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl,
×
2983
               startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname)));
×
2984
          return {1, true};
×
2985
        }
×
2986
        ::arg().parse(argc, argv);
×
2987
        return {0, true};
×
2988
      }
×
2989
      catch (const ArgException& argException) {
×
2990
        SLOG(g_log << Logger::Warning << "Unable to parse configuration file '" << configname << "': " << argException.reason << endl,
×
2991
             startupLog->error("Cannot parse configuration", "Unable to parse configuration file", "config_file", Logging::Loggable(configname), "reason", Logging::Loggable(argException.reason)));
×
2992
        return {1, true};
×
2993
      }
×
2994
    }
×
2995
    else if (config == "default" || config.empty()) {
×
2996
      cout << ::arg().configstring(false, true);
×
2997
    }
×
2998
    else if (config == "diff") {
×
2999
      if (!::arg().laxFile(configname)) {
×
3000
        SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl,
×
3001
             startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname)));
×
3002
        return {1, true};
×
3003
      }
×
3004
      ::arg().laxParse(argc, argv);
×
3005
      cout << ::arg().configstring(true, false);
×
3006
    }
×
3007
    else {
×
3008
      if (!::arg().laxFile(configname)) {
×
3009
        SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl,
×
3010
             startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname)));
×
3011
        return {1, true};
×
3012
      }
×
3013
      ::arg().laxParse(argc, argv);
×
3014
      cout << ::arg().configstring(true, true);
×
3015
    }
×
3016
    return {0, true};
×
3017
  }
×
3018
  return {0, false};
143✔
3019
}
143✔
3020

3021
LockGuarded<pdns::rust::settings::rec::Recursorsettings> g_yamlStruct;
3022

3023
static void handleRuntimeDefaults(Logr::log_t log)
3024
{
148✔
3025
#ifdef HAVE_FIBER_SANITIZER
148✔
3026
  // Asan needs more stack
3027
  if (::arg().asNum("stack-size") == 200000) { // the default in table.py
148!
3028
    ::arg().set("stack-size", "stack size per mthread") = "600000";
148✔
3029
  }
148✔
3030
#endif
148✔
3031

3032
  const string RUNTIME = "*runtime determined*";
148✔
3033
  if (::arg()["version-string"] == RUNTIME) { // i.e. not set explicitly
148✔
3034
    ::arg().set("version-string") = fullVersionString();
147✔
3035
  }
147✔
3036

3037
  if (::arg()["server-id"] == RUNTIME) { // i.e. not set explicitly
148✔
3038
    auto myHostname = getHostname();
147✔
3039
    if (!myHostname.has_value()) {
147!
3040
      SLOG(g_log << Logger::Warning << "Unable to get the hostname, NSID and id.server values will be empty" << endl,
×
3041
           log->info(Logr::Warning, "Unable to get the hostname, NSID and id.server values will be empty"));
×
3042
    }
×
3043
    ::arg().set("server-id") = myHostname.has_value() ? *myHostname : "";
147!
3044
  }
147✔
3045

3046
  if (::arg()["socket-dir"].empty()) {
148!
3047
    auto* runtimeDir = getenv("RUNTIME_DIRECTORY"); // NOLINT(concurrency-mt-unsafe,cppcoreguidelines-pro-type-vararg)
×
3048
    if (runtimeDir != nullptr) {
×
3049
      ::arg().set("socket-dir") = runtimeDir;
×
3050
    }
×
3051
  }
×
3052

3053
  if (::arg()["socket-dir"].empty()) {
148!
3054
    if (::arg()["chroot"].empty()) {
×
3055
      ::arg().set("socket-dir") = std::string(LOCALSTATEDIR) + "/pdns-recursor";
×
3056
    }
×
3057
    else {
×
3058
      ::arg().set("socket-dir") = "/";
×
3059
    }
×
3060
  }
×
3061

3062
  if (::arg().asNum("threads") == 1) {
148!
3063
    if (::arg().mustDo("pdns-distributes-queries")) {
×
3064
      SLOG(g_log << Logger::Warning << "Only one thread, no need to distribute queries ourselves" << endl,
×
3065
           log->info(Logr::Warning, "Only one thread, no need to distribute queries ourselves"));
×
3066
      ::arg().set("pdns-distributes-queries") = "no";
×
3067
    }
×
3068
  }
×
3069

3070
  if (::arg().mustDo("pdns-distributes-queries") && ::arg().asNum("distributor-threads") == 0) {
148!
3071
    SLOG(g_log << Logger::Warning << "Asked to run with pdns-distributes-queries set but no distributor threads, raising to 1" << endl,
14✔
3072
         log->info(Logr::Warning, "Asked to run with pdns-distributes-queries set but no distributor threads, raising to 1"));
14✔
3073
    ::arg().set("distributor-threads") = "1";
14✔
3074
  }
14✔
3075

3076
  if (!::arg().mustDo("pdns-distributes-queries") && ::arg().asNum("distributor-threads") > 0) {
148!
3077
    SLOG(g_log << Logger::Warning << "Not distributing queries, setting distributor threads to 0" << endl,
×
3078
         log->info(Logr::Warning, "Not distributing queries, setting distributor threads to 0"));
×
3079
    ::arg().set("distributor-threads") = "0";
×
3080
  }
×
3081
}
148✔
3082

3083
static void setupLogging(const string& logname)
3084
{
148✔
3085
  if (logname == "systemd-journal") {
148!
3086
#ifdef HAVE_SYSTEMD
×
3087
    if (int fileDesc = sd_journal_stream_fd("pdns-recusor", LOG_DEBUG, 0); fileDesc >= 0) {
×
3088
      g_slog = Logging::Logger::create(loggerSDBackend);
×
3089
      close(fileDesc);
×
3090
    }
×
3091
#endif
×
3092
    if (g_slog == nullptr) {
×
3093
      cerr << "Requested structured logging to systemd-journal, but it is not available" << endl;
×
3094
    }
×
3095
  }
×
3096
  else if (logname == "json") {
148!
3097
    g_slog = Logging::Logger::create(loggerJSONBackend);
×
3098
    if (g_slog == nullptr) {
×
3099
      cerr << "JSON logging requested but it is not available" << endl;
×
3100
    }
×
3101
  }
×
3102

3103
  if (g_slog == nullptr) {
148!
3104
    g_slog = Logging::Logger::create(loggerBackend);
148✔
3105
  }
148✔
3106
}
148✔
3107

3108
int main(int argc, char** argv)
3109
{
150✔
3110
  g_argc = argc;
150✔
3111
  g_argv = argv;
150✔
3112
  versionSetProduct(ProductRecursor);
150✔
3113
  reportBasicTypes();
150✔
3114
  reportOtherTypes();
150✔
3115

3116
  int ret = EXIT_SUCCESS;
150✔
3117

3118
  try {
150✔
3119
    pdns::settings::rec::defineOldStyleSettings();
150✔
3120
    ::arg().setDefaults();
150✔
3121
    g_log.toConsole(Logger::Info);
150✔
3122
    ::arg().laxParse(argc, argv); // do a lax parse
150✔
3123

3124
    if (::arg().mustDo("version")) {
150✔
3125
      showProductVersion();
2✔
3126
      showBuildConfiguration();
2✔
3127
      return 0;
2✔
3128
    }
2✔
3129
    if (::arg().mustDo("help")) {
148!
3130
      cout << "syntax:" << endl
3131
           << endl;
3132
      cout << ::arg().helpstring(::arg()["help"]) << endl;
3133
      return 0;
3134
    }
3135

3136
    // Pick up options given on command line to setup logging asap.
3137
    g_quiet = ::arg().mustDo("quiet");
148✔
3138
    s_logUrgency = (Logger::Urgency)::arg().asNum("loglevel");
148✔
3139
    s_structured_logger_backend = ::arg()["structured-logging-backend"];
148✔
3140

3141
    if (!g_quiet && s_logUrgency < Logger::Info) { // Logger::Info=6, Logger::Debug=7
148!
3142
      s_logUrgency = Logger::Info; // if you do --quiet=no, you need Info to also see the query log
3143
    }
3144
    g_log.setLoglevel(s_logUrgency);
148✔
3145
    g_log.toConsole(s_logUrgency);
148✔
3146
    showProductVersion();
148✔
3147
    if (!::arg().mustDo("structured-logging")) {
148!
3148
      g_log << Logger::Error << "Disabling structured logging is not supported anymore" << endl;
3149
    }
3150

3151
    g_yamlSettings = false;
148✔
3152
    string configname = ::arg()["config-dir"] + "/recursor";
148✔
3153
    if (!::arg()["config-name"].empty()) {
148!
3154
      configname = ::arg()["config-dir"] + "/recursor-" + ::arg()["config-name"];
3155
      g_programname += "-" + ::arg()["config-name"];
3156
    }
3157
    cleanSlashes(configname);
148✔
3158

3159
    if (!::arg().getCommands().empty()) {
148!
3160
      cerr << "Fatal: non-option";
3161
      if (::arg().getCommands().size() > 1) {
×
3162
        cerr << "s";
3163
      }
3164
      cerr << " (";
3165
      bool first = true;
3166
      for (const auto& command : ::arg().getCommands()) {
×
3167
        if (!first) {
×
3168
          cerr << ", ";
3169
        }
3170
        first = false;
3171
        cerr << command;
3172
      }
3173
      cerr << ") on the command line, perhaps a '--setting=123' statement missed the '='?" << endl;
3174
      return 99;
3175
    }
3176

3177
    setupLogging(s_structured_logger_backend);
148✔
3178

3179
    // Missing: a mechanism to call setVerbosity(x)
3180
    auto startupLog = g_slog->withName("config");
148✔
3181
    g_slogtcpin = g_slog->withName("in")->withValues("proto", Logging::Loggable("tcp"));
148✔
3182
    g_slogudpin = g_slog->withName("in")->withValues("proto", Logging::Loggable("udp"));
148✔
3183
    g_slogout = g_slog->withName("out");
148✔
3184

3185
    ::arg().setSLog(startupLog);
148✔
3186

3187
    const string yamlconfigname = configname + ".yml";
148✔
3188
    pdns::rust::settings::rec::Recursorsettings settings;
148✔
3189
    auto yamlstatus = pdns::settings::rec::tryReadYAML(yamlconfigname, true, g_yamlSettings, g_luaSettingsInYAML, settings, startupLog);
148✔
3190
    if (yamlstatus == pdns::settings::rec::PresentButFailed) {
148!
3191
      return 1;
3192
    }
3193

3194
    if (g_yamlSettings) {
148✔
3195
      bool mustExit = false;
5✔
3196
      std::tie(ret, mustExit) = doYamlConfig(argc, argv, settings);
5✔
3197
      if (ret != 0 || mustExit) {
5!
3198
        return ret;
3199
      }
3200
    }
5✔
3201
    if (yamlstatus == pdns::settings::rec::YamlSettingsStatus::OK) {
148✔
3202
      auto lock = g_yamlStruct.lock();
5✔
3203
      *lock = std::move(settings);
5✔
3204
    }
5✔
3205
    if (yamlstatus == pdns::settings::rec::YamlSettingsStatus::CannotOpen) {
148✔
3206
      configname += ".conf";
143✔
3207
      bool mustExit = false;
143✔
3208
      std::tie(ret, mustExit) = doConfig(startupLog, configname, argc, argv);
143✔
3209
      if (ret != 0 || mustExit) {
143!
3210
        return ret;
3211
      }
3212
      if (!::arg().file(configname)) {
143✔
3213
        SLOG(g_log << Logger::Warning << "Unable to open configuration file '" << configname << "'" << endl,
12✔
3214
             startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname)));
12✔
3215
      }
12✔
3216
    }
143✔
3217

3218
    // Reparse, now with config file as well, both for old-style as for YAML settings
3219
    ::arg().parse(argc, argv);
148✔
3220

3221
    g_quiet = ::arg().mustDo("quiet");
148✔
3222
    s_logUrgency = (Logger::Urgency)::arg().asNum("loglevel");
148✔
3223

3224
    if (s_logUrgency < Logger::Error) {
148!
3225
      s_logUrgency = Logger::Error;
3226
    }
3227
    if (!g_quiet && s_logUrgency < Logger::Info) { // Logger::Info=6, Logger::Debug=7
148!
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);
148✔
3231
    g_log.toConsole(s_logUrgency);
148✔
3232

3233
    if (!::arg()["chroot"].empty() && !::arg()["api-config-dir"].empty()) {
148!
3234
      SLOG(g_log << Logger::Error << "Using chroot and enabling the API is not possible" << endl,
3235
           startupLog->info(Logr::Error, "Cannot use chroot and enable the API at the same time"));
3236
      return EXIT_FAILURE;
3237
    }
3238

3239
    handleRuntimeDefaults(startupLog);
148✔
3240

3241
    if (auto ttl = ::arg().asNum("system-resolver-ttl"); ttl != 0) {
148✔
3242
      time_t interval = ttl;
1✔
3243
      if (::arg().asNum("system-resolver-interval") != 0) {
1!
3244
        interval = ::arg().asNum("system-resolver-interval");
3245
      }
3246
      bool selfResolveCheck = ::arg().mustDo("system-resolver-self-resolve-check");
1✔
3247
      // Cannot use SyncRes::s_serverID, it is not set yet
3248
      pdns::RecResolve::setInstanceParameters(arg()["server-id"], ttl, interval, selfResolveCheck, []() { reloadZoneConfiguration(g_yamlSettings); });
1✔
3249
    }
1✔
3250

3251
    g_recCache = std::make_unique<MemRecursorCache>(::arg().asNum("record-cache-shards"));
148✔
3252
    g_negCache = std::make_unique<NegCache>(::arg().asNum("record-cache-shards") / 8);
148✔
3253
    if (!::arg().mustDo("disable-packetcache")) {
148✔
3254
      g_maxPacketCacheEntries = ::arg().asNum("max-packetcache-entries");
142✔
3255
      g_packetCache = std::make_unique<RecursorPacketCache>(g_maxPacketCacheEntries, ::arg().asNum("packetcache-shards"));
142✔
3256
    }
142✔
3257

3258
    ret = serviceMain(startupLog);
148✔
3259
  }
148✔
3260
  catch (const PDNSException& ae) {
150✔
3261
    SLOG(g_log << Logger::Error << "Exception: " << ae.reason << endl,
3262
         g_slog->withName("config")->error(Logr::Critical, ae.reason, "Fatal error", "exception", Logging::Loggable("PDNSException")));
3263
    ret = EXIT_FAILURE;
3264
  }
3265
  catch (const std::exception& e) {
150✔
3266
    SLOG(g_log << Logger::Error << "STL Exception: " << e.what() << endl,
3267
         g_slog->withName("config")->error(Logr::Critical, e.what(), "Fatal error", "exception", Logging::Loggable("std::exception")));
3268
    ret = EXIT_FAILURE;
3269
  }
3270
  catch (...) {
150✔
3271
    SLOG(g_log << Logger::Error << "any other exception in main: " << endl,
3272
         g_slog->withName("config")->info(Logr::Critical, "Fatal error"));
3273
    ret = EXIT_FAILURE;
3274
  }
3275

3276
  return ret;
1✔
3277
}
150✔
3278

3279
static RecursorControlChannel::Answer* doReloadLuaScript()
3280
{
629✔
3281
  string fname = ::arg()["lua-dns-script"];
629✔
3282
  auto log = g_slog->withName("runtime")->withValues("name", Logging::Loggable(fname));
629✔
3283
  try {
629✔
3284
    if (fname.empty()) {
629!
3285
      t_pdl.reset();
629✔
3286
      SLOG(g_log << Logger::Info << RecThreadInfo::id() << " Unloaded current lua script" << endl,
629✔
3287
           log->info(Logr::Info, "Unloaded current lua script"));
629✔
3288
      return new RecursorControlChannel::Answer{0, string("unloaded\n")};
629✔
3289
    }
629✔
3290

3291
    t_pdl = std::make_shared<RecursorLua4>();
×
3292
    try {
×
3293
      t_pdl->loadFile(fname);
×
3294
    }
×
3295
    catch (std::runtime_error& ex) {
×
3296
      string msg = std::to_string(RecThreadInfo::id()) + " Retaining current script, could not read '" + fname + "': " + ex.what();
×
3297
      SLOG(g_log << Logger::Error << msg << endl,
×
3298
           log->error(Logr::Error, ex.what(), "Retaining current script, could not read new script"));
×
3299
      return new RecursorControlChannel::Answer{1, msg + "\n"};
×
3300
    }
×
3301
  }
×
3302
  catch (std::exception& e) {
629✔
3303
    SLOG(g_log << Logger::Error << RecThreadInfo::id() << " Retaining current script, error from '" << fname << "': " << e.what() << endl,
×
3304
         log->error(Logr::Error, e.what(), "Retaining current script, error in new script"));
×
3305
    return new RecursorControlChannel::Answer{1, string("retaining current script, error from '" + fname + "': " + e.what() + "\n")};
×
3306
  }
×
3307

3308
  SLOG(g_log << Logger::Warning << RecThreadInfo::id() << " (Re)loaded lua script from '" << fname << "'" << endl,
×
3309
       log->info(Logr::Warning, "(Re)loaded lua script"));
×
3310
  return new RecursorControlChannel::Answer{0, string("(re)loaded '" + fname + "'\n")};
×
3311
}
629✔
3312

3313
RecursorControlChannel::Answer doQueueReloadLuaScript(vector<string>::const_iterator begin, vector<string>::const_iterator end)
3314
{
147✔
3315
  if (begin != end) {
147!
3316
    ::arg().set("lua-dns-script") = *begin;
147✔
3317
  }
147✔
3318

3319
  return broadcastAccFunction<RecursorControlChannel::Answer>(doReloadLuaScript);
147✔
3320
}
147✔
3321

3322
static string* pleaseUseNewTraceRegex(const std::string& newRegex, int file)
3323
{
×
3324
  try {
×
3325
    if (newRegex.empty()) {
×
3326
      t_traceRegex.reset();
×
3327
      t_tracefd = FDWrapper();
×
3328
      return new string("unset\n");
×
3329
    }
×
3330
    if (file == -1) {
×
3331
      return new string("could not dup file\n");
×
3332
    }
×
3333
    t_traceRegex = std::make_shared<Regex>(newRegex);
×
3334
    t_tracefd = file;
×
3335
    return new string("ok\n"); // NOLINT(cppcoreguidelines-owning-memory): it's the API
×
3336
  }
×
3337
  catch (const PDNSException& ae) {
×
3338
    return new string(ae.reason + "\n"); // NOLINT(cppcoreguidelines-owning-memory): it's the API
×
3339
  }
×
3340
}
×
3341

3342
string doTraceRegex(FDWrapper file, vector<string>::const_iterator begin, vector<string>::const_iterator end)
3343
{
×
3344
  int fileno = dup(file);
×
3345
  // Potential dup failure handled in pleaseUseNewTraceRegex()
3346
  return broadcastAccFunction<string>([=] { return pleaseUseNewTraceRegex(begin != end ? *begin : "", fileno); });
×
3347
}
×
3348

3349
struct WipeCacheResult wipeCaches(const DNSName& canon, bool subtree, uint16_t qtype)
3350
{
693✔
3351
  struct WipeCacheResult res;
693✔
3352

3353
  try {
693✔
3354
    res.record_count = static_cast<int>(g_recCache->doWipeCache(canon, subtree, qtype));
693✔
3355
    // scanbuild complains here about an allocated function object that is being leaked. Needs investigation
3356
    if (g_packetCache) {
693✔
3357
      res.packet_count = static_cast<int>(g_packetCache->doWipePacketCache(canon, qtype, subtree));
691✔
3358
    }
691✔
3359
    res.negative_record_count = static_cast<int>(g_negCache->wipe(canon, subtree));
693✔
3360
    if (g_aggressiveNSECCache) {
693✔
3361
      g_aggressiveNSECCache->removeZoneInfo(canon, subtree);
525✔
3362
    }
525✔
3363
  }
693✔
3364
  catch (const std::exception& e) {
693✔
3365
    auto log = g_slog->withName("runtime");
×
3366
    SLOG(g_log << Logger::Warning << ", failed: " << e.what() << endl,
×
3367
         log->error(Logr::Warning, e.what(), "Wipecache failed"));
×
3368
  }
×
3369

3370
  return res;
693✔
3371
}
693✔
3372

3373
void startLuaConfigDelayedThreads(const vector<RPZTrackerParams>& rpzs, uint64_t generation)
3374
{
148✔
3375
  for (const auto& rpzPrimary : rpzs) {
148✔
3376
    if (rpzPrimary.primaries.empty()) {
17✔
3377
      continue;
15✔
3378
    }
15✔
3379
    try {
2✔
3380
      // RPZIXTracker uses call by value for its args. That is essential, since we want copies so
3381
      // that RPZIXFRTracker gets values with the proper lifetime.
3382
      std::thread theThread(RPZIXFRTracker, rpzPrimary, generation);
2✔
3383
      theThread.detach();
2✔
3384
    }
2✔
3385
    catch (const std::exception& e) {
2✔
3386
      SLOG(g_log << Logger::Error << "Problem starting RPZIXFRTracker thread: " << e.what() << endl,
×
3387
           g_slog->withName("rpz")->error(Logr::Error, e.what(), "Exception starting RPZIXFRTracker thread", "exception", Logging::Loggable("std::exception")));
×
3388
      exit(1); // NOLINT(concurrency-mt-unsafe)
×
3389
    }
×
3390
    catch (const PDNSException& e) {
2✔
3391
      SLOG(g_log << Logger::Error << "Problem starting RPZIXFRTracker thread: " << e.reason << endl,
×
3392
           g_slog->withName("rpz")->error(Logr::Error, e.reason, "Exception starting RPZIXFRTracker thread", "exception", Logging::Loggable("PDNSException")));
×
3393
      exit(1); // NOLINT(concurrency-mt-unsafe)
×
3394
    }
×
3395
  }
2✔
3396
}
148✔
3397

3398
static void activateRPZFile(const RPZTrackerParams& params, LuaConfigItems& lci, shared_ptr<DNSFilterEngine::Zone>& zone)
3399
{
15✔
3400
  auto log = lci.d_slog->withValues("file", Logging::Loggable(params.name));
15✔
3401

3402
  zone->setName(params.polName);
15✔
3403
  try {
15✔
3404
    SLOG(g_log << Logger::Warning << "Loading RPZ from file '" << params.name << "'" << endl,
15✔
3405
         log->info(Logr::Info, "Loading RPZ from file"));
15✔
3406
    loadRPZFromFile(params.name, zone, params.defpol, params.defpolOverrideLocal, params.maxTTL);
15✔
3407
    SLOG(g_log << Logger::Warning << "Done loading RPZ from file '" << params.name << "'" << endl,
15✔
3408
         log->info(Logr::Info, "Done loading RPZ from file"));
15✔
3409
  }
15✔
3410
  catch (const std::exception& e) {
15✔
3411
    SLOG(g_log << Logger::Error << "Unable to load RPZ zone from '" << params.name << "': " << e.what() << endl,
×
3412
         log->error(Logr::Error, e.what(), "Exception while loading RPZ zone from file"));
×
3413
  }
×
3414
}
15✔
3415

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

3420
  if (!params.seedFileName.empty()) {
2!
3421
    SLOG(g_log << Logger::Info << "Pre-loading RPZ zone " << params.name << " from seed file '" << params.seedFileName << "'" << endl,
×
3422
         log->info(Logr::Info, "Pre-loading RPZ zone from seed file"));
×
3423
    try {
×
3424
      params.soaRecordContent = loadRPZFromFile(params.seedFileName, zone, params.defpol, params.defpolOverrideLocal, params.maxTTL);
×
3425

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

3430
      if (params.soaRecordContent == nullptr) {
×
3431
        throw PDNSException("The RPZ zone " + params.name + " loaded from the seed file (" + zone->getDomain().toString() + ") has no SOA record");
×
3432
      }
×
3433
    }
×
3434
    catch (const PDNSException& e) {
×
3435
      SLOG(g_log << Logger::Warning << "Unable to pre-load RPZ zone " << params.name << " from seed file '" << params.seedFileName << "': " << e.reason << endl,
×
3436
           log->error(Logr::Warning, e.reason, "Exception while pre-loading RPZ zone", "exception", Logging::Loggable("PDNSException")));
×
3437
      zone->clear();
×
3438
    }
×
3439
    catch (const std::exception& e) {
×
3440
      SLOG(g_log << Logger::Warning << "Unable to pre-load RPZ zone " << params.name << " from seed file '" << params.seedFileName << "': " << e.what() << endl,
×
3441
           log->error(Logr::Warning, e.what(), "Exception while pre-loading RPZ zone", "exception", Logging::Loggable("std::exception")));
×
3442
      zone->clear();
×
3443
    }
×
3444
  }
×
3445
}
2✔
3446

3447
static void activateRPZs(LuaConfigItems& lci)
3448
{
148✔
3449
  for (auto& params : lci.rpzs) {
148✔
3450
    auto zone = std::make_shared<DNSFilterEngine::Zone>();
17✔
3451
    if (params.zoneSizeHint != 0) {
17!
3452
      zone->reserve(params.zoneSizeHint);
×
3453
    }
×
3454
    if (!params.tags.empty()) {
17✔
3455
      std::unordered_set<std::string> tags;
1✔
3456
      for (const auto& tag : params.tags) {
2✔
3457
        tags.emplace(tag);
2✔
3458
      }
2✔
3459
      zone->setTags(tags);
1✔
3460
    }
1✔
3461
    zone->setPolicyOverridesGettag(params.defpolOverrideLocal);
17✔
3462
    if (params.extendedErrorCode != std::numeric_limits<uint32_t>::max()) {
17✔
3463
      zone->setExtendedErrorCode(params.extendedErrorCode);
1✔
3464
      if (!params.extendedErrorExtra.empty()) {
1!
3465
        zone->setExtendedErrorExtra(params.extendedErrorExtra);
1✔
3466
      }
1✔
3467
    }
1✔
3468
    zone->setIncludeSOA(params.includeSOA);
17✔
3469
    zone->setIgnoreDuplicates(params.ignoreDuplicates);
17✔
3470

3471
    if (params.primaries.empty()) {
17✔
3472
      activateRPZFile(params, lci, zone);
15✔
3473
      lci.dfe.addZone(zone);
15✔
3474
    }
15✔
3475
    else {
2✔
3476
      DNSName domain(params.name);
2✔
3477
      zone->setDomain(domain);
2✔
3478
      zone->setName(params.polName);
2✔
3479
      params.zoneIdx = lci.dfe.addZone(zone);
2✔
3480
      activateRPZPrimary(params, lci, zone, domain);
2✔
3481
    }
2✔
3482
  }
17✔
3483
}
148✔
3484

3485
void activateLuaConfig(LuaConfigItems& lci)
3486
{
148✔
3487
  if (!lci.trustAnchorFileInfo.fname.empty()) {
148✔
3488
    warnIfDNSSECDisabled("Warning: reading Trust Anchors from file, but dnssec is set to 'off'!");
1✔
3489
    updateTrustAnchorsFromFile(lci.trustAnchorFileInfo.fname, lci.dsAnchors, lci.d_slog);
1✔
3490
  }
1✔
3491
  if (lci.dsAnchors.size() > rootDSs.size()) {
148✔
3492
    warnIfDNSSECDisabled("Warning: adding Trust Anchor for DNSSEC, but dnssec is set to 'off'!");
2✔
3493
  }
2✔
3494
  if (!lci.negAnchors.empty()) {
148✔
3495
    warnIfDNSSECDisabled("Warning: adding Negative Trust Anchor for DNSSEC, but dnssec is set to 'off'!");
2✔
3496
  }
2✔
3497
  activateRPZs(lci);
148✔
3498
  g_luaconfs.setState(lci);
148✔
3499
}
148✔
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