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

PowerDNS / pdns / 18748609987

23 Oct 2025 12:38PM UTC coverage: 73.046% (+4.3%) from 68.781%
18748609987

Pull #16362

github

web-flow
Merge fb6974bbe into dec9583d8
Pull Request #16362: rec: implements new feature to only generate OpenTelemtry Traces on certain conditions

38418 of 63316 branches covered (60.68%)

Branch coverage included in aggregate %.

122 of 136 new or added lines in 11 files covered. (89.71%)

53 existing lines in 11 files now uncovered.

127614 of 163983 relevant lines covered (77.82%)

5916487.93 hits per line

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

64.95
/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 "dnssec.hh"
42
#include "rec-rust-lib/cxxsettings.hh"
43
#include "json.hh"
44
#include "rec-system-resolve.hh"
45
#include "root-dnssec.hh"
46
#include "ratelimitedlog.hh"
47
#include "rec-rust-lib/rust/web.rs.h"
48

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

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

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

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

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

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

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

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

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

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

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

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

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

143
std::unique_ptr<OpenTelemetryTraceConditions> g_OTConditions; // new threads needs this to be setup
144
thread_local std::unique_ptr<OpenTelemetryTraceConditions> t_OTConditions;
145

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

152
pdns::RateLimitedLog g_rateLimitedLogger;
153

154
static void runStartStopLua(bool start, Logr::log_t log);
155

156
static std::map<unsigned int, std::set<int>> parseCPUMap(Logr::log_t log)
157
{
185✔
158
  std::map<unsigned int, std::set<int>> result;
185✔
159

160
  const std::string value = ::arg()["cpu-map"];
185✔
161

162
  if (!value.empty() && !isSettingThreadCPUAffinitySupported()) {
185!
163
    log->info(Logr::Warning, "CPU mapping requested but not supported, skipping");
×
164
    return result;
×
165
  }
×
166

167
  std::vector<std::string> parts;
185✔
168

169
  stringtok(parts, value, " \t");
185✔
170

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

176
    try {
×
177
      auto headers = splitField(part, '=');
×
178
      boost::trim(headers.first);
×
179
      boost::trim(headers.second);
×
180

181
      auto threadId = pdns::checked_stoi<unsigned int>(headers.first);
×
182
      std::vector<std::string> cpus;
×
183

184
      stringtok(cpus, headers.second, ",");
×
185

186
      for (const auto& cpu : cpus) {
×
187
        int cpuId = std::stoi(cpu);
×
188

189
        result[threadId].insert(cpuId);
×
190
      }
×
191
    }
×
192
    catch (const std::exception& e) {
×
193
      log->error(Logr::Error, e.what(), "Error parsing cpu-map entry", "entry", Logging::Loggable(part));
×
194
    }
×
195
  }
×
196

197
  return result;
185✔
198
}
185✔
199

200
static void setCPUMap(const std::map<unsigned int, std::set<int>>& cpusMap, unsigned int n, pthread_t tid, Logr::log_t log)
201
{
960✔
202
  const auto& cpuMapping = cpusMap.find(n);
960✔
203
  if (cpuMapping == cpusMap.cend()) {
960!
204
    return;
960✔
205
  }
960✔
206
  int ret = mapThreadToCPUList(tid, cpuMapping->second);
×
207
  if (ret == 0) {
×
208
    log->info(Logr::Info, "CPU affinity has been set", "thread", Logging::Loggable(n), "cpumap", Logging::IterLoggable(cpuMapping->second.begin(), cpuMapping->second.end()));
×
209
  }
×
210
  else {
×
211
    log->error(Logr::Warning, ret, "Error setting CPU affinity", "thread", Logging::Loggable(n), "cpumap", Logging::IterLoggable(cpuMapping->second.begin(), cpuMapping->second.end()));
×
212
  }
×
213
}
×
214

215
static void recursorThread();
216

217
void RecThreadInfo::start(unsigned int tid, const string& tname, const std::map<unsigned int, std::set<int>>& cpusMap, Logr::log_t log)
218
{
960✔
219
  name = tname;
960✔
220
  thread = std::thread([tid, tname] {
960✔
221
    t_id = tid;
960✔
222
    const string threadPrefix = "rec/";
960✔
223
    setThreadName(threadPrefix + tname);
960✔
224
    recursorThread();
960✔
225
  });
960✔
226
  setCPUMap(cpusMap, tid, thread.native_handle(), log);
960✔
227
}
960✔
228

229
int RecThreadInfo::runThreads(Logr::log_t log)
230
{
185✔
231
  int ret = EXIT_SUCCESS;
185✔
232
  const auto cpusMap = parseCPUMap(log);
185✔
233

234
  if (RecThreadInfo::numDistributors() + RecThreadInfo::numUDPWorkers() == 1) {
185✔
235
    log->info(Logr::Notice, "Operating with single UDP distributor/worker thread");
3✔
236

237
    /* This thread handles the web server, carbon, statistics and the control channel */
238
    unsigned int currentThreadId = 0;
3✔
239
    auto& handlerInfo = RecThreadInfo::info(currentThreadId);
3✔
240
    handlerInfo.setHandler();
3✔
241
    handlerInfo.start(currentThreadId, "web+stat", cpusMap, log);
3✔
242

243
    // We skip the single UDP worker thread 1, it's handled after the loop and taskthreads
244
    currentThreadId = 2;
3✔
245
    for (unsigned int thread = 0; thread < RecThreadInfo::numTCPWorkers(); thread++, currentThreadId++) {
6✔
246
      auto& info = RecThreadInfo::info(currentThreadId);
3✔
247
      info.setTCPListener();
3✔
248
      info.setWorker();
3✔
249
      info.start(currentThreadId, "tcpworker", cpusMap, log);
3✔
250
    }
3✔
251

252
    for (unsigned int thread = 0; thread < RecThreadInfo::numTaskThreads(); thread++, currentThreadId++) {
6✔
253
      auto& taskInfo = RecThreadInfo::info(currentThreadId);
3✔
254
      taskInfo.setTaskThread();
3✔
255
      taskInfo.start(currentThreadId, "task", cpusMap, log);
3✔
256
    }
3✔
257

258
    if (::arg().mustDo("webserver")) {
3!
259
      serveRustWeb();
×
260
    }
×
261

262
    currentThreadId = 1;
3✔
263
    auto& info = RecThreadInfo::info(currentThreadId);
3✔
264
    info.setListener();
3✔
265
    info.setWorker();
3✔
266
    RecThreadInfo::setThreadId(currentThreadId);
3✔
267
    recursorThread();
3✔
268

269
    // Skip handler thread (it might be still handling the quit-nicely) and 1, which is actually the main thread in this case;
270
    // handler thread (0) will be handled in main().
271
    for (unsigned int thread = 2; thread < RecThreadInfo::numRecursorThreads(); thread++) {
9✔
272
      auto& tInfo = RecThreadInfo::info(thread);
6✔
273
      tInfo.thread.join();
6✔
274
      if (tInfo.exitCode != 0) {
6!
275
        ret = tInfo.exitCode;
×
276
      }
×
277
    }
6✔
278
  }
3✔
279
  else {
182✔
280
    // Setup RecThreadInfo objects
281
    unsigned int currentThreadId = 1;
182✔
282
    if (RecThreadInfo::weDistributeQueries()) {
182✔
283
      for (unsigned int thread = 0; thread < RecThreadInfo::numDistributors(); thread++, currentThreadId++) {
28✔
284
        RecThreadInfo::info(currentThreadId).setListener();
14✔
285
      }
14✔
286
    }
14✔
287
    for (unsigned int thread = 0; thread < RecThreadInfo::numUDPWorkers(); thread++, currentThreadId++) {
573✔
288
      auto& info = RecThreadInfo::info(currentThreadId);
391✔
289
      info.setListener(!RecThreadInfo::weDistributeQueries());
391✔
290
      info.setWorker();
391✔
291
    }
391✔
292
    for (unsigned int thread = 0; thread < RecThreadInfo::numTCPWorkers(); thread++, currentThreadId++) {
364✔
293
      auto& info = RecThreadInfo::info(currentThreadId);
182✔
294
      info.setTCPListener();
182✔
295
      info.setWorker();
182✔
296
    }
182✔
297
    for (unsigned int thread = 0; thread < RecThreadInfo::numTaskThreads(); thread++, currentThreadId++) {
364✔
298
      auto& info = RecThreadInfo::info(currentThreadId);
182✔
299
      info.setTaskThread();
182✔
300
    }
182✔
301

302
    // And now start the actual threads
303
    currentThreadId = 1;
182✔
304
    if (RecThreadInfo::weDistributeQueries()) {
182✔
305
      log->info(Logr::Notice, "Launching distributor threads", "count", Logging::Loggable(RecThreadInfo::numDistributors()));
14✔
306
      for (unsigned int thread = 0; thread < RecThreadInfo::numDistributors(); thread++, currentThreadId++) {
28✔
307
        auto& info = RecThreadInfo::info(currentThreadId);
14✔
308
        info.start(currentThreadId, "distr", cpusMap, log);
14✔
309
      }
14✔
310
    }
14✔
311
    log->info(Logr::Notice, "Launching worker threads", "count", Logging::Loggable(RecThreadInfo::numUDPWorkers()));
182✔
312

313
    for (unsigned int thread = 0; thread < RecThreadInfo::numUDPWorkers(); thread++, currentThreadId++) {
573✔
314
      auto& info = RecThreadInfo::info(currentThreadId);
391✔
315
      info.start(currentThreadId, "worker", cpusMap, log);
391✔
316
    }
391✔
317

318
    log->info(Logr::Notice, "Launching tcpworker threads", "count", Logging::Loggable(RecThreadInfo::numTCPWorkers()));
182✔
319

320
    for (unsigned int thread = 0; thread < RecThreadInfo::numTCPWorkers(); thread++, currentThreadId++) {
364✔
321
      auto& info = RecThreadInfo::info(currentThreadId);
182✔
322
      info.start(currentThreadId, "tcpworker", cpusMap, log);
182✔
323
    }
182✔
324

325
    for (unsigned int thread = 0; thread < RecThreadInfo::numTaskThreads(); thread++, currentThreadId++) {
364✔
326
      auto& info = RecThreadInfo::info(currentThreadId);
182✔
327
      info.start(currentThreadId, "task", cpusMap, log);
182✔
328
    }
182✔
329

330
    /* This thread handles the web server, carbon, statistics and the control channel */
331
    currentThreadId = 0;
182✔
332
    auto& info = RecThreadInfo::info(currentThreadId);
182✔
333
    info.setHandler();
182✔
334
    info.start(currentThreadId, "web+stat", cpusMap, log);
182✔
335

336
    if (::arg().mustDo("webserver")) {
182✔
337
      serveRustWeb();
24✔
338
    }
24✔
339
    for (auto& tInfo : RecThreadInfo::infos()) {
951✔
340
      // who handles the handler? the caller!
341
      if (tInfo.isHandler()) {
951✔
342
        continue;
182✔
343
      }
182✔
344
      tInfo.thread.join();
769✔
345
      if (tInfo.exitCode != 0) {
769!
346
        ret = tInfo.exitCode;
×
347
      }
×
348
    }
769✔
349
  }
182✔
350
  return ret;
185✔
351
}
185✔
352

353
void RecThreadInfo::makeThreadPipes(Logr::log_t log)
354
{
185✔
355
  auto pipeBufferSize = ::arg().asNum("distribution-pipe-buffer-size");
185✔
356
  if (pipeBufferSize > 0) {
185!
357
    log->info(Logr::Info, "Resizing the buffer of the distribution pipe", "size", Logging::Loggable(pipeBufferSize));
×
358
  }
×
359

360
  /* thread 0 is the handler / SNMP, worker threads start at 1 */
361
  for (unsigned int thread = 0; thread < numRecursorThreads(); ++thread) {
1,148✔
362
    auto& threadInfo = info(thread);
963✔
363

364
    std::array<int, 2> fileDesc{};
963✔
365
    if (pipe(fileDesc.data()) < 0) {
963!
366
      unixDie("Creating pipe for inter-thread communications");
×
367
    }
×
368

369
    threadInfo.pipes.readToThread = fileDesc[0];
963✔
370
    threadInfo.pipes.writeToThread = fileDesc[1];
963✔
371

372
    // handler thread only gets first pipe, not the others
373
    if (thread == 0) {
963✔
374
      continue;
185✔
375
    }
185✔
376

377
    if (pipe(fileDesc.data()) < 0) {
778!
378
      unixDie("Creating pipe for inter-thread communications");
×
379
    }
×
380

381
    threadInfo.pipes.readFromThread = fileDesc[0];
778✔
382
    threadInfo.pipes.writeFromThread = fileDesc[1];
778✔
383

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

388
    threadInfo.pipes.readQueriesToThread = fileDesc[0];
778✔
389
    threadInfo.pipes.writeQueriesToThread = fileDesc[1];
778✔
390

391
    if (pipeBufferSize > 0) {
778!
392
      if (!setPipeBufferSize(threadInfo.pipes.writeQueriesToThread, pipeBufferSize)) {
×
393
        int err = errno;
×
394
        log->error(Logr::Warning, err, "Error resizing the buffer of the distribution pipe for thread", "thread", Logging::Loggable(thread), "size", Logging::Loggable(pipeBufferSize));
×
395
        auto existingSize = getPipeBufferSize(threadInfo.pipes.writeQueriesToThread);
×
396
        if (existingSize > 0) {
×
397
          log->info(Logr::Warning, "The current size of the distribution pipe's buffer for thread", "thread", Logging::Loggable(thread), "size", Logging::Loggable(existingSize));
×
398
        }
×
399
      }
×
400
    }
×
401

402
    if (!setNonBlocking(threadInfo.pipes.writeQueriesToThread)) {
778!
403
      unixDie("Making pipe for inter-thread communications non-blocking");
×
404
    }
×
405
  }
778✔
406
}
185✔
407

408
ArgvMap& arg()
409
{
140,657✔
410
  static ArgvMap theArg;
140,657✔
411
  return theArg;
140,657✔
412
}
140,657✔
413

414
static FDMultiplexer* getMultiplexer(Logr::log_t log)
415
{
963✔
416
  FDMultiplexer* ret = nullptr;
963✔
417
  for (const auto& mplexer : FDMultiplexer::getMultiplexerMap()) {
963✔
418
    try {
962✔
419
      ret = mplexer.second(FDMultiplexer::s_maxevents);
962✔
420
      return ret;
962✔
421
    }
962✔
422
    catch (FDMultiplexerException& fe) {
962✔
423
      log->error(Logr::Warning, fe.what(), "Non-fatal error initializing possible multiplexer, falling back");
×
424
    }
×
425
    catch (...) {
962✔
426
      log->info(Logr::Warning, "Non-fatal error initializing possible multiplexer");
×
427
    }
×
428
  }
962✔
429
  log->info(Logr::Error, "No working multiplexer found!");
1✔
430
  _exit(1);
1✔
431
}
963✔
432

433
static std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>> startProtobufServers(const ProtobufExportConfig& config, Logr::log_t log)
434
{
110✔
435
  auto result = std::make_shared<std::vector<std::unique_ptr<RemoteLogger>>>();
110✔
436

437
  for (const auto& server : config.servers) {
219✔
438
    try {
219✔
439
      auto logger = make_unique<RemoteLogger>(server, config.timeout, 100 * config.maxQueuedEntries, config.reconnectWaitTime, config.asyncConnect);
219✔
440
      logger->setLogQueries(config.logQueries);
219✔
441
      logger->setLogResponses(config.logResponses);
219✔
442
      result->emplace_back(std::move(logger));
219✔
443
    }
219✔
444
    catch (const std::exception& e) {
219✔
445
      log->error(Logr::Error, e.what(), "Exception while starting protobuf logger", "exception", Logging::Loggable("std::exception"), "server", Logging::Loggable(server));
×
446
    }
×
447
    catch (const PDNSException& e) {
219✔
448
      log->error(Logr::Error, e.reason, "Exception while starting protobuf logger", "exception", Logging::Loggable("PDNSException"), "server", Logging::Loggable(server));
×
449
    }
×
450
  }
219✔
451

452
  return result;
111✔
453
}
110✔
454

455
bool checkProtobufExport(LocalStateHolder<LuaConfigItems>& luaconfsLocal)
456
{
10,997✔
457
  if (!luaconfsLocal->protobufExportConfig.enabled) {
11,016✔
458
    if (t_protobufServers.servers) {
10,795!
459
      t_protobufServers.servers.reset();
×
460
      t_protobufServers.config = luaconfsLocal->protobufExportConfig;
×
461
    }
×
462

463
    return false;
10,795✔
464
  }
10,795✔
465

466
  /* if the server was not running, or if it was running according to a
467
     previous configuration */
468
  if (t_protobufServers.generation < luaconfsLocal->generation && t_protobufServers.config != luaconfsLocal->protobufExportConfig) {
2,147,483,868✔
469

470
    if (t_protobufServers.servers) {
95!
471
      t_protobufServers.servers.reset();
×
472
    }
×
473
    auto log = g_slog->withName("protobuf");
95✔
474
    t_protobufServers.servers = startProtobufServers(luaconfsLocal->protobufExportConfig, log);
95✔
475
    t_protobufServers.config = luaconfsLocal->protobufExportConfig;
95✔
476
    t_protobufServers.generation = luaconfsLocal->generation;
95✔
477
  }
95✔
478

479
  return true;
2,147,483,868✔
480
}
10,997✔
481

482
bool checkOutgoingProtobufExport(LocalStateHolder<LuaConfigItems>& luaconfsLocal)
483
{
10,156✔
484
  if (!luaconfsLocal->outgoingProtobufExportConfig.enabled) {
10,156✔
485
    if (t_outgoingProtobufServers.servers) {
10,112!
486
      t_outgoingProtobufServers.servers.reset();
×
487
      t_outgoingProtobufServers.config = luaconfsLocal->outgoingProtobufExportConfig;
×
488
    }
×
489

490
    return false;
10,112✔
491
  }
10,112✔
492

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

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

506
  return true;
44✔
507
}
10,156✔
508

509
void protobufLogQuery(LocalStateHolder<LuaConfigItems>& luaconfsLocal, const boost::uuids::uuid& uniqueId, const ComboAddress& remote, const ComboAddress& local, const ComboAddress& mappedSource, const Netmask& ednssubnet, bool tcp, size_t len, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::unordered_set<std::string>& policyTags, const std::string& requestorId, const std::string& deviceId, const std::string& deviceName, const std::map<std::string, RecursorLua4::MetaValue>& meta, const boost::optional<uint32_t>& ednsVersion, const dnsheader& header, const pdns::trace::TraceID& traceID)
510
{
43✔
511
  auto log = g_slog->withName("pblq");
43✔
512

513
  if (!t_protobufServers.servers) {
43!
514
    return;
×
515
  }
×
516

517
  ComboAddress requestor;
43✔
518
  if (!luaconfsLocal->protobufExportConfig.logMappedFrom) {
43✔
519
    Netmask requestorNM(remote, remote.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
41!
520
    requestor = requestorNM.getMaskedNetwork();
41✔
521
    requestor.setPort(remote.getPort());
41✔
522
  }
41✔
523
  else {
2✔
524
    Netmask requestorNM(mappedSource, mappedSource.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
2!
525
    requestor = requestorNM.getMaskedNetwork();
2✔
526
    requestor.setPort(mappedSource.getPort());
2✔
527
  }
2✔
528

529
  pdns::ProtoZero::RecMessage msg{128, std::string::size_type(policyTags.empty() ? 0 : 64)}; // It's a guess
43✔
530
  msg.setType(pdns::ProtoZero::Message::MessageType::DNSQueryType);
43✔
531
  msg.setRequest(uniqueId, requestor, local, qname, qtype, qclass, header.id, tcp ? pdns::ProtoZero::Message::TransportProtocol::TCP : pdns::ProtoZero::Message::TransportProtocol::UDP, len);
43✔
532
  msg.setServerIdentity(SyncRes::s_serverID);
43✔
533
  msg.setEDNSSubnet(ednssubnet, ednssubnet.isIPv4() ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
43!
534
  msg.setRequestorId(requestorId);
43✔
535
  msg.setDeviceId(deviceId);
43✔
536
  msg.setDeviceName(deviceName);
43✔
537
  msg.setWorkerId(RecThreadInfo::thread_local_id());
43✔
538
  // For queries, packetCacheHit and outgoingQueries are not relevant
539

540
  if (!policyTags.empty()) {
43✔
541
    msg.addPolicyTags(policyTags);
5✔
542
  }
5✔
543
  for (const auto& mit : meta) {
43✔
544
    msg.setMeta(mit.first, mit.second.stringVal, mit.second.intVal);
2✔
545
  }
2✔
546
  msg.setHeaderFlags(*getFlagsFromDNSHeader(&header));
43✔
547
  if (ednsVersion) {
43!
548
    msg.setEDNSVersion(*ednsVersion);
43✔
549
  }
43✔
550
  if (traceID != pdns::trace::s_emptyTraceID) {
43✔
551
    msg.setOpenTelemetryTraceID(traceID);
6✔
552
  }
6✔
553

554
  std::string strMsg(msg.finishAndMoveBuf());
43✔
555
  for (auto& server : *t_protobufServers.servers) {
86✔
556
    remoteLoggerQueueData(*server, strMsg);
86✔
557
  }
86✔
558
}
43✔
559

560
void protobufLogResponse(pdns::ProtoZero::RecMessage& message)
561
{
51✔
562
  if (!t_protobufServers.servers) {
51!
563
    return;
×
564
  }
×
565

566
  std::string msg(message.finishAndMoveBuf());
51✔
567
  for (auto& server : *t_protobufServers.servers) {
102✔
568
    remoteLoggerQueueData(*server, msg);
102✔
569
  }
102✔
570
}
51✔
571

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

592
  // In response part
593
  if (g_useKernelTimestamp && tval.tv_sec != 0) {
22!
594
    pbMessage.setQueryTime(tval.tv_sec, tval.tv_usec);
×
595
  }
×
596
  else {
22✔
597
    pbMessage.setQueryTime(g_now.tv_sec, g_now.tv_usec);
22✔
598
  }
22✔
599

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

620
  pbMessage.setTime();
22✔
621
  pbMessage.setEDNSSubnet(ednssubnet.getSource(), ednssubnet.getSource().isIPv4() ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
22!
622
  pbMessage.setRequestorId(requestorId);
22✔
623
  pbMessage.setDeviceId(deviceId);
22✔
624
  pbMessage.setDeviceName(deviceName);
22✔
625
  pbMessage.setToPort(destination.getPort());
22✔
626
  pbMessage.setWorkerId(RecThreadInfo::thread_local_id());
22✔
627
  // this method is only used for PC cache hits
628
  pbMessage.setPacketCacheHit(true);
22✔
629
  // we do not set outgoingQueries, it is not relevant for PC cache hits
630

631
  for (const auto& metaItem : meta) {
22!
632
    pbMessage.setMeta(metaItem.first, metaItem.second.stringVal, metaItem.second.intVal);
×
633
  }
×
634
#ifdef NOD_ENABLED
22✔
635
  if (g_nodEnabled) {
22!
636
    pbMessage.setNewlyObservedDomain(false);
×
637
  }
×
638
#endif
22✔
639
  if (eventTrace.enabled() && SyncRes::eventTraceEnabled(SyncRes::event_trace_to_pb)) {
22!
640
    pbMessage.addEvents(eventTrace);
×
641
  }
×
642

643
  if (eventTrace.enabled() && eventTrace.getThisOTTraceEnabled() && SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) {
22!
644
    otTrace.setIDsIfNotSet();
10✔
645
    auto trace = pdns::trace::TracesData::boilerPlate("rec", eventTrace.convertToOT(otTrace),
10✔
646
                                                      {{"query.qname", {qname.toLogString()}},
10✔
647
                                                       {"query.qtype", {qtype.toString()}}},
10✔
648
                                                      SyncRes::s_serverID);
10✔
649
    pbMessage.setOpenTelemetryData(trace.encode());
10✔
650
  }
10✔
651
  if (otTrace.trace_id != pdns::trace::s_emptyTraceID) {
22✔
652
    pbMessage.setOpenTelemetryTraceID(otTrace.trace_id);
13✔
653
  }
13✔
654
  pbMessage.addPolicyTags(policyTags);
22✔
655

656
  protobufLogResponse(pbMessage);
22✔
657
}
22✔
658

659
#ifdef HAVE_FSTRM
660

661
static std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>> startFrameStreamServers(const FrameStreamExportConfig& config, Logr::log_t log)
662
{
25✔
663
  auto result = std::make_shared<std::vector<std::unique_ptr<FrameStreamLogger>>>();
25✔
664

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

696
  return result;
25✔
697
}
25✔
698

699
static void asyncFrameStreamLoggersCleanup(std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>&& servers)
700
{
×
701
  auto thread = std::thread([&] {
×
702
    servers.reset();
×
703
  });
×
704
  thread.detach();
×
705
}
×
706

707
bool checkFrameStreamExport(LocalStateHolder<LuaConfigItems>& luaconfsLocal, const FrameStreamExportConfig& config, FrameStreamServersInfo& serverInfos)
708
{
14,980✔
709
  if (!config.enabled) {
14,980✔
710
    if (serverInfos.servers) {
14,923!
711
      // dt's take care of cleanup
712
      asyncFrameStreamLoggersCleanup(std::move(serverInfos.servers));
×
713
      serverInfos.config = config;
×
714
    }
×
715

716
    return false;
14,923✔
717
  }
14,923✔
718

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

728
    auto dnsTapLog = g_slog->withName("dnstap");
25✔
729
    serverInfos.servers = startFrameStreamServers(config, dnsTapLog);
25✔
730
    serverInfos.config = config;
25✔
731
    serverInfos.generation = luaconfsLocal->generation;
25✔
732
  }
25✔
733

734
  return true;
57✔
735
}
14,980✔
736

737
#endif /* HAVE_FSTRM */
738

739
static void makeControlChannelSocket(int processNum = -1)
740
{
185✔
741
  string sockname = ::arg()["socket-dir"] + "/" + g_programname;
185✔
742
  if (processNum >= 0) {
185!
743
    sockname += "." + std::to_string(processNum);
×
744
  }
×
745
  sockname += ".controlsocket";
185✔
746
  g_rcc.listen(sockname);
185✔
747

748
  uid_t sockowner = -1;
185✔
749
  gid_t sockgroup = -1;
185✔
750

751
  if (!::arg().isEmpty("socket-group")) {
185!
752
    sockgroup = ::arg().asGid("socket-group");
×
753
  }
×
754
  if (!::arg().isEmpty("socket-owner")) {
185!
755
    sockowner = ::arg().asUid("socket-owner");
×
756
  }
×
757

758
  if (sockgroup != static_cast<gid_t>(-1) || sockowner != static_cast<uid_t>(-1)) {
185!
759
    if (chown(sockname.c_str(), sockowner, sockgroup) < 0) {
×
760
      unixDie("Failed to chown control socket");
×
761
    }
×
762
  }
×
763

764
  // do mode change if socket-mode is given
765
  if (!::arg().isEmpty("socket-mode")) {
185!
766
    mode_t sockmode = ::arg().asMode("socket-mode");
×
767
    if (chmod(sockname.c_str(), sockmode) < 0) {
×
768
      unixDie("Failed to chmod control socket");
×
769
    }
×
770
  }
×
771
}
185✔
772

773
static void writePid(Logr::log_t log)
774
{
185✔
775
  if (!::arg().mustDo("write-pid")) {
185!
776
    return;
×
777
  }
×
778
  ofstream ostr(g_pidfname.c_str(), std::ios_base::app);
185✔
779
  if (ostr) {
185!
780
    ostr << Utility::getpid() << endl;
185✔
781
  }
185✔
782
  else {
×
783
    int err = errno;
×
784
    log->error(Logr::Error, err, "Writing pid failed", "pid", Logging::Loggable(Utility::getpid()), "file", Logging::Loggable(g_pidfname));
×
785
  }
×
786
}
185✔
787

788
static void checkSocketDir(Logr::log_t log)
789
{
185✔
790
  string dir(::arg()["socket-dir"]);
185✔
791
  string msg;
185✔
792

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

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

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

869
static void parseIgnorelistFile(const std::string& fname, SuffixMatchNode& matchNode)
870
{
×
871
  string line;
×
872
  std::ifstream ignorelistFileStream(fname);
×
873
  if (!ignorelistFileStream) {
×
874
    throw ArgException(fname + " could not be opened");
×
875
  }
×
876

877
  while (getline(ignorelistFileStream, line)) {
×
878
    boost::trim(line);
×
879

880
    try {
×
881
      matchNode.add(DNSName(line));
×
882
    }
×
883
    catch (const std::exception& e) {
×
884
      g_slog->withName("config")->error(Logr::Warning, e.what(), "Ignoring line of ignorelist due to an error", "exception", Logging::Loggable("std::exception"));
×
885
    }
×
886
  }
×
887
}
×
888

889
static void setupNODGlobal()
890
{
185✔
891
  // Setup NOD subsystem
892
  g_nodEnabled = ::arg().mustDo("new-domain-tracking");
185✔
893
  g_nodLookupDomain = DNSName(::arg()["new-domain-lookup"]);
185✔
894
  g_nodLog = ::arg().mustDo("new-domain-log");
185✔
895
  parseIgnorelist(::arg()["new-domain-whitelist"], g_nodDomainWL);
185✔
896
  parseIgnorelist(::arg()["new-domain-ignore-list"], g_nodDomainWL);
185✔
897
  if (!::arg().isEmpty("new-domain-ignore-list-file")) {
185!
898
    parseIgnorelistFile(::arg()["new-domain-ignore-list-file"], g_nodDomainWL);
×
899
  }
×
900

901
  // Setup Unique DNS Response subsystem
902
  g_udrEnabled = ::arg().mustDo("unique-response-tracking");
185✔
903
  g_udrLog = ::arg().mustDo("unique-response-log");
185✔
904
  g_nod_pbtag = ::arg()["new-domain-pb-tag"];
185✔
905
  g_udr_pbtag = ::arg()["unique-response-pb-tag"];
185✔
906
  parseIgnorelist(::arg()["unique-response-ignore-list"], g_udrDomainWL);
185✔
907
  if (!::arg().isEmpty("unique-response-ignore-list-file")) {
185!
908
    parseIgnorelistFile(::arg()["unique-response-ignore-list-file"], g_udrDomainWL);
×
909
  }
×
910
}
185✔
911
#endif /* NOD_ENABLED */
912

913
static void daemonize(Logr::log_t log)
914
{
×
915
  if (auto pid = fork(); pid != 0) {
×
916
    if (pid < 0) {
×
917
      int err = errno;
×
918
      log->error(Logr::Critical, err, "Fork failed");
×
919
      exit(1); // NOLINT(concurrency-mt-unsafe)
×
920
    }
×
921
    exit(0); // NOLINT(concurrency-mt-unsafe)
×
922
  }
×
923

924
  setsid();
×
925

926
  int devNull = open("/dev/null", O_RDWR); /* open stdin */
×
927
  if (devNull < 0) {
×
928
    int err = errno;
×
929
    log->error(Logr::Critical, err, "Unable to open /dev/null");
×
930
  }
×
931
  else {
×
932
    dup2(devNull, 0); /* stdin */
×
933
    dup2(devNull, 1); /* stderr */
×
934
    dup2(devNull, 2); /* stderr */
×
935
    close(devNull);
×
936
  }
×
937
}
×
938

939
static void termIntHandler([[maybe_unused]] int arg)
940
{
×
941
  _exit(1);
×
942
}
×
943

944
static void usr1Handler([[maybe_unused]] int arg)
945
{
45✔
946
  statsWanted = true;
45✔
947
}
45✔
948

949
static void usr2Handler([[maybe_unused]] int arg)
950
{
×
951
  g_quiet = !g_quiet;
×
952
  SyncRes::setDefaultLogMode(g_quiet ? SyncRes::LogNone : SyncRes::Log);
×
953
  ::arg().set("quiet") = g_quiet ? "yes" : "no";
×
954
}
×
955

956
static void checkLinuxIPv6Limits([[maybe_unused]] Logr::log_t log)
957
{
185✔
958
#ifdef __linux__
185✔
959
  string line;
185✔
960
  if (readFileIfThere("/proc/sys/net/ipv6/route/max_size", &line)) {
185!
961
    int lim = std::stoi(line);
185✔
962
    if (lim < 16384) {
185!
963
      log->info(Logr::Error, "If using IPv6, please raise sysctl net.ipv6.route.max_size to a size >= 16384", "current", Logging::Loggable(lim));
×
964
    }
×
965
  }
185✔
966
#endif
185✔
967
}
185✔
968

969
static void checkOrFixLinuxMapCountLimits([[maybe_unused]] Logr::log_t log)
970
{
185✔
971
#ifdef __linux__
185✔
972
  string line;
185✔
973
  if (readFileIfThere("/proc/sys/vm/max_map_count", &line)) {
185!
974
    auto lim = std::stoull(line);
185✔
975
    // mthread stack use 3 maps per stack (2 guard pages + stack itself). Multiple by 4 for extra allowance.
976
    // Also add 2 for handler and task threads.
977
    auto workers = RecThreadInfo::numTCPWorkers() + RecThreadInfo::numUDPWorkers() + 2;
185✔
978
    auto mapsNeeded = 4ULL * g_maxMThreads * workers;
185✔
979
    if (lim < mapsNeeded) {
185!
980
      g_maxMThreads = static_cast<unsigned int>(lim / (4ULL * workers));
×
981
      log->info(Logr::Error, "sysctl vm.max_map_count < mapsNeeded, this may cause 'bad_alloc' exceptions, adjusting max-mthreads",
×
982
                "vm.max_map_count", Logging::Loggable(lim), "mapsNeeded", Logging::Loggable(mapsNeeded),
×
983
                "max-mthreads", Logging::Loggable(g_maxMThreads));
×
984
    }
×
985
  }
185✔
986
#endif
185✔
987
}
185✔
988

989
static void checkOrFixFDS(unsigned int listeningSockets, Logr::log_t log)
990
{
185✔
991
  const auto availFDs = getFilenumLimit();
185✔
992
  // Posix threads
993
  const auto threads = RecThreadInfo::numRecursorThreads();
185✔
994
  // We do not count the handler and task threads, they do not spawn many mthreads at once
995
  const auto workers = RecThreadInfo::numUDPWorkers() + RecThreadInfo::numTCPWorkers();
185✔
996

997
  // Static part: the FDs from the start, pipes, controlsocket, web socket, listen sockets
998
  unsigned int staticPart = 25; // general  allowance, including control socket, web, snmp
185✔
999
  // Handler thread gets one pipe, the others all of them
1000
  staticPart += 2 + (threads - 1) * (sizeof(RecThreadInfo::ThreadPipeSet) / sizeof(int)); // number of fd's in ThreadPipeSet
185✔
1001
  // listen sockets
1002
  staticPart += listeningSockets;
185✔
1003
  // Another fd per thread for poll/kqueue
1004
  staticPart += threads;
185✔
1005
  // Incoming TCP, connections are shared by threads and are kept open for a while
1006
  staticPart += g_maxTCPClients;
185✔
1007

1008
  // Dynamic parts per worker
1009
  // Each mthread uses one fd for either outgoing UDP or outgoing TCP (but not simultaneously)
1010
  unsigned int perWorker = g_maxMThreads;
185✔
1011
  // plus each worker thread can have a number of idle outgoing TCP connections
1012
  perWorker += TCPOutConnectionManager::s_maxIdlePerThread;
185✔
1013

1014
  auto wantFDs = staticPart + workers * perWorker;
185✔
1015

1016
  if (wantFDs > availFDs) {
185!
1017
    unsigned int hardlimit = getFilenumLimit(true);
×
1018
    if (staticPart >= hardlimit) {
×
1019
      log->info(Logr::Critical, "Number of available filedescriptors is lower than the minimum needed",
×
1020
                "hardlimit", Logging::Loggable(hardlimit), "minimum", Logging::Loggable(staticPart));
×
1021
      _exit(1);
×
1022
    }
×
1023
    if (hardlimit >= wantFDs) {
×
1024
      setFilenumLimit(wantFDs);
×
1025
      log->info(Logr::Warning, "Raised soft limit on number of filedescriptors to match max-mthreads and threads settings", "limit", Logging::Loggable(wantFDs));
×
1026
    }
×
1027
    else {
×
1028
      auto newval = (hardlimit - staticPart) / workers;
×
1029
      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));
×
1030
      g_maxMThreads = newval;
×
1031
      setFilenumLimit(hardlimit);
×
1032
    }
×
1033
  }
×
1034
}
185✔
1035

1036
#ifdef HAVE_SYSTEMD
1037
static void loggerSDBackend(const Logging::Entry& entry)
1038
{
×
1039
  static const set<std::string, CIStringComparePOSIX> special = {
×
1040
    "message",
×
1041
    "message_id",
×
1042
    "priority",
×
1043
    "code_file",
×
1044
    "code_line",
×
1045
    "code_func",
×
1046
    "errno",
×
1047
    "invocation_id",
×
1048
    "user_invocation_id",
×
1049
    "syslog_facility",
×
1050
    "syslog_identifier",
×
1051
    "syslog_pid",
×
1052
    "syslog_timestamp",
×
1053
    "syslog_raw",
×
1054
    "documentation",
×
1055
    "tid",
×
1056
    "unit",
×
1057
    "user_unit",
×
1058
    "object_pid"};
×
1059

1060
  // First map SL priority to syslog's Urgency
1061
  Logger::Urgency urgency = entry.d_priority != 0 ? Logger::Urgency(entry.d_priority) : Logger::Info;
×
1062
  if (urgency > s_logUrgency) {
×
1063
    // We do not log anything if the Urgency of the message is lower than the requested loglevel.
1064
    // Not that lower Urgency means higher number.
1065
    return;
×
1066
  }
×
1067
  // We need to keep the string in mem until sd_journal_sendv has ben called
1068
  vector<string> strings;
×
1069
  auto appendKeyAndVal = [&strings](const string& key, const string& value) {
×
1070
    strings.emplace_back(key + "=" + value);
×
1071
  };
×
1072
  appendKeyAndVal("MESSAGE", entry.message);
×
1073
  if (entry.error) {
×
1074
    appendKeyAndVal("ERROR", entry.error.get());
×
1075
  }
×
1076
  appendKeyAndVal("LEVEL", std::to_string(entry.level));
×
1077
  appendKeyAndVal("PRIORITY", std::to_string(entry.d_priority));
×
1078
  if (entry.name) {
×
1079
    appendKeyAndVal("SUBSYSTEM", entry.name.get());
×
1080
  }
×
1081
  std::array<char, 64> timebuf{};
×
1082
  appendKeyAndVal("TIMESTAMP", Logging::toTimestampStringMilli(entry.d_timestamp, timebuf));
×
1083
  for (const auto& value : entry.values) {
×
1084
    if (value.first.at(0) == '_' || special.count(value.first) != 0) {
×
1085
      string key{"PDNS"};
×
1086
      key.append(value.first);
×
1087
      appendKeyAndVal(toUpper(key), value.second);
×
1088
    }
×
1089
    else {
×
1090
      appendKeyAndVal(toUpper(value.first), value.second);
×
1091
    }
×
1092
  }
×
1093
  // Thread id filled in by backend, since the SL code does not know about RecursorThreads
1094
  // We use the Recursor thread, other threads get id 0. May need to revisit.
1095
  appendKeyAndVal("TID", std::to_string(RecThreadInfo::thread_local_id()));
×
1096

1097
  vector<iovec> iov;
×
1098
  iov.reserve(strings.size());
×
1099
  for (const auto& str : strings) {
×
1100
    // iovec has no 2 arg constructor, so make it explicit
1101
    iov.emplace_back(iovec{const_cast<void*>(reinterpret_cast<const void*>(str.data())), str.size()}); // NOLINT: it's the API
×
1102
  }
×
1103
  sd_journal_sendv(iov.data(), static_cast<int>(iov.size()));
×
1104
}
×
1105
#endif
1106

1107
static void loggerJSONBackend(const Logging::Entry& entry)
1108
{
×
1109
  // First map SL priority to syslog's Urgency
1110
  Logger::Urgency urg = entry.d_priority != 0 ? Logger::Urgency(entry.d_priority) : Logger::Info;
×
1111
  if (urg > s_logUrgency) {
×
1112
    // We do not log anything if the Urgency of the message is lower than the requested loglevel.
1113
    // Not that lower Urgency means higher number.
1114
    return;
×
1115
  }
×
1116

1117
  std::array<char, 64> timebuf{};
×
1118
  json11::Json::object json = {
×
1119
    {"msg", entry.message},
×
1120
    {"level", std::to_string(entry.level)},
×
1121
    // Thread id filled in by backend, since the SL code does not know about RecursorThreads
1122
    // We use the Recursor thread, other threads get id 0. May need to revisit.
1123
    {"tid", std::to_string(RecThreadInfo::thread_local_id())},
×
1124
    {"ts", Logging::toTimestampStringMilli(entry.d_timestamp, timebuf)},
×
1125
  };
×
1126

1127
  if (entry.error) {
×
1128
    json.emplace("error", entry.error.get());
×
1129
  }
×
1130

1131
  if (entry.name) {
×
1132
    json.emplace("subsystem", entry.name.get());
×
1133
  }
×
1134

1135
  if (entry.d_priority != 0) {
×
1136
    json.emplace("priority", std::to_string(entry.d_priority));
×
1137
  }
×
1138

1139
  for (auto const& value : entry.values) {
×
1140
    json.emplace(value.first, value.second);
×
1141
  }
×
1142

1143
  static thread_local std::string out;
×
1144
  out.clear();
×
1145
  json11::Json doc(std::move(json));
×
1146
  doc.dump(out);
×
1147
  cerr << out << endl;
×
1148
}
×
1149

1150
static void loggerBackend(const Logging::Entry& entry)
1151
{
8,343✔
1152
  static thread_local std::stringstream buf;
8,343✔
1153

1154
  // First map SL priority to syslog's Urgency
1155
  Logger::Urgency urg = entry.d_priority != 0 ? Logger::Urgency(entry.d_priority) : Logger::Info;
8,343✔
1156
  if (urg > s_logUrgency) {
8,343✔
1157
    // We do not log anything if the Urgency of the message is lower than the requested loglevel.
1158
    // Not that lower Urgency means higher number.
1159
    return;
229✔
1160
  }
229✔
1161
  buf.str("");
8,114✔
1162
  buf << "msg=" << std::quoted(entry.message);
8,114✔
1163
  if (entry.error) {
8,114✔
1164
    buf << " error=" << std::quoted(entry.error.get());
244✔
1165
  }
244✔
1166

1167
  if (entry.name) {
8,114✔
1168
    buf << " subsystem=" << std::quoted(entry.name.get());
8,038✔
1169
  }
8,038✔
1170
  buf << " level=" << std::quoted(std::to_string(entry.level));
8,114✔
1171
  if (entry.d_priority != 0) {
8,114✔
1172
    buf << " prio=" << std::quoted(Logr::Logger::toString(entry.d_priority));
8,084✔
1173
  }
8,084✔
1174
  // Thread id filled in by backend, since the SL code does not know about RecursorThreads
1175
  // We use the Recursor thread, other threads get id 0. May need to revisit.
1176
  buf << " tid=" << std::quoted(std::to_string(RecThreadInfo::thread_local_id()));
8,114✔
1177
  std::array<char, 64> timebuf{};
8,114✔
1178
  buf << " ts=" << std::quoted(Logging::toTimestampStringMilli(entry.d_timestamp, timebuf));
8,114✔
1179
  for (auto const& value : entry.values) {
38,919✔
1180
    buf << " ";
38,919✔
1181
    buf << value.first << "=" << std::quoted(value.second);
38,919✔
1182
  }
38,919✔
1183

1184
  g_log << urg << buf.str() << endl;
8,114✔
1185
}
8,114✔
1186

1187
static std::string ratePercentage(uint64_t nom, uint64_t denom)
1188
{
300✔
1189
  if (denom == 0) {
300!
1190
    return "0";
×
1191
  }
×
1192
  std::ostringstream str;
300✔
1193
  str << std::setprecision(2) << std::fixed << 100.0 * static_cast<double>(nom) / static_cast<double>(denom);
300✔
1194
  return str.str();
300✔
1195
}
300✔
1196

1197
static void doStats()
1198
{
60✔
1199
  static time_t lastOutputTime;
60✔
1200
  static uint64_t lastQueryCount;
60✔
1201

1202
  auto cacheHits = g_recCache->getCacheHits();
60✔
1203
  auto cacheMisses = g_recCache->getCacheMisses();
60✔
1204
  auto cacheSize = g_recCache->size();
60✔
1205
  auto rc_stats = g_recCache->stats();
60✔
1206
  auto pc_stats = g_packetCache ? g_packetCache->stats() : std::pair<uint64_t, uint64_t>{0, 0};
60!
1207
  auto rrc = ratePercentage(rc_stats.first, rc_stats.second);
60✔
1208
  auto rpc = ratePercentage(pc_stats.first, pc_stats.second);
60✔
1209
  auto negCacheSize = g_negCache->size();
60✔
1210
  auto taskPushes = getTaskPushes();
60✔
1211
  auto taskExpired = getTaskExpired();
60✔
1212
  auto taskSize = getTaskSize();
60✔
1213
  auto pcSize = g_packetCache ? g_packetCache->size() : 0;
60!
1214
  auto pcHits = g_packetCache ? g_packetCache->getHits() : 0;
60!
1215
  auto pcMisses = g_packetCache ? g_packetCache->getMisses() : 0;
60!
1216

1217
  auto qcounter = g_Counters.sum(rec::Counter::qcounter);
60✔
1218
  auto outqueries = g_Counters.sum(rec::Counter::outqueries);
60✔
1219
  auto throttledqueries = g_Counters.sum(rec::Counter::throttledqueries);
60✔
1220
  auto tcpoutqueries = g_Counters.sum(rec::Counter::tcpoutqueries);
60✔
1221
  auto dotoutqueries = g_Counters.sum(rec::Counter::dotoutqueries);
60✔
1222
  auto outgoingtimeouts = g_Counters.sum(rec::Counter::outgoingtimeouts);
60✔
1223

1224
  auto log = g_slog->withName("stats");
60✔
1225

1226
  if (qcounter > 0) {
60✔
1227
    const string report = "Periodic statistics report";
45✔
1228
    log->info(Logr::Info, report,
45✔
1229
              "questions", Logging::Loggable(qcounter),
45✔
1230
              "cache-entries", Logging::Loggable(cacheSize),
45✔
1231
              "negcache-entries", Logging::Loggable(negCacheSize),
45✔
1232
              "record-cache-hitratio-perc", Logging::Loggable(ratePercentage(cacheHits, cacheHits + cacheMisses)),
45✔
1233
              "record-cache-contended", Logging::Loggable(rc_stats.first),
45✔
1234
              "record-cache-acquired", Logging::Loggable(rc_stats.second),
45✔
1235
              "record-cache-contended-perc", Logging::Loggable(rrc));
45✔
1236
    log->info(Logr::Info, report,
45✔
1237
              "packetcache-contended", Logging::Loggable(pc_stats.first),
45✔
1238
              "packetcache-acquired", Logging::Loggable(pc_stats.second),
45✔
1239
              "packetcache-contended-perc", Logging::Loggable(rpc),
45✔
1240
              "packetcache-entries", Logging::Loggable(pcSize),
45✔
1241
              "packetcache-hitratio-perc", Logging::Loggable(ratePercentage(pcHits, pcHits + pcMisses)));
45✔
1242
    log->info(Logr::Info, report,
45✔
1243
              "throttle-entries", Logging::Loggable(SyncRes::getThrottledServersSize()),
45✔
1244
              "nsspeed-entries", Logging::Loggable(SyncRes::getNSSpeedsSize()),
45✔
1245
              "failed-host-entries", Logging::Loggable(SyncRes::getFailedServersSize()),
45✔
1246
              "edns-entries", Logging::Loggable(SyncRes::getEDNSStatusesSize()),
45✔
1247
              "non-resolving-nameserver-entries", Logging::Loggable(SyncRes::getNonResolvingNSSize()),
45✔
1248
              "saved-parent-ns-sets-entries", Logging::Loggable(SyncRes::getSaveParentsNSSetsSize()));
45✔
1249
    log->info(Logr::Info, report,
45✔
1250
              "throttled-queries-perc", Logging::Loggable(ratePercentage(throttledqueries, outqueries + throttledqueries)),
45✔
1251
              "outqueries", Logging::Loggable(outqueries),
45✔
1252
              "tcp-outqueries", Logging::Loggable(tcpoutqueries),
45✔
1253
              "dot-outqueries", Logging::Loggable(dotoutqueries),
45✔
1254
              "idle-tcpout-connections", Logging::Loggable(getCurrentIdleTCPConnections()),
45✔
1255
              "concurrent-queries", Logging::Loggable(broadcastAccFunction<uint64_t>(pleaseGetConcurrentQueries)),
45✔
1256
              "outgoing-timeouts", Logging::Loggable(outgoingtimeouts),
45✔
1257
              "outqueries-per-query-perc", Logging::Loggable(ratePercentage(outqueries, qcounter)));
45✔
1258
    log->info(Logr::Info, report,
45✔
1259
              "taskqueue-pushed", Logging::Loggable(taskPushes),
45✔
1260
              "taskqueue-expired", Logging::Loggable(taskExpired),
45✔
1261
              "taskqueue-size", Logging::Loggable(taskSize));
45✔
1262

1263
    size_t idx = 0;
45✔
1264
    for (const auto& threadInfo : RecThreadInfo::infos()) {
333✔
1265
      if (threadInfo.isWorker()) {
333✔
1266
        log->info(Logr::Info, "Queries handled by thread", "thread", Logging::Loggable(idx), "tname", Logging::Loggable(threadInfo.getName()), "count", Logging::Loggable(threadInfo.getNumberOfDistributedQueries()));
207✔
1267
        ++idx;
207✔
1268
      }
207✔
1269
    }
333✔
1270
    time_t now = time(nullptr);
45✔
1271
    if (lastOutputTime != 0 && lastQueryCount != 0 && now != lastOutputTime) {
45!
1272
      log->info(Logr::Info, "Periodic QPS report", "qps", Logging::Loggable((qcounter - lastQueryCount) / (now - lastOutputTime)),
30✔
1273
                "averagedOver", Logging::Loggable(now - lastOutputTime));
30✔
1274
    }
30✔
1275
    lastOutputTime = now;
45✔
1276
    lastQueryCount = qcounter;
45✔
1277
  }
45✔
1278
  else if (statsWanted) {
15!
1279
    log->info(Logr::Notice, "No stats yet");
×
1280
  }
×
1281

1282
  statsWanted = false;
60✔
1283
}
60✔
1284

1285
static std::shared_ptr<NetmaskGroup> parseACL(const std::string& aclFile, const std::string& aclSetting, Logr::log_t log)
1286
{
372✔
1287
  auto result = std::make_shared<NetmaskGroup>();
372✔
1288

1289
  const string file = ::arg()[aclFile];
372✔
1290

1291
  if (!file.empty()) {
372!
1292
    if (boost::ends_with(file, ".yml")) {
×
1293
      ::rust::vec<::rust::string> vec;
×
1294
      pdns::settings::rec::readYamlAllowFromFile(file, vec, log);
×
1295
      for (const auto& subnet : vec) {
×
1296
        result->addMask(string(subnet));
×
1297
      }
×
1298
    }
×
1299
    else {
×
1300
      string line;
×
1301
      ifstream ifs(file);
×
1302
      if (!ifs) {
×
1303
        int err = errno;
×
1304
        throw runtime_error("Could not open '" + file + "': " + stringerror(err));
×
1305
      }
×
1306

1307
      while (getline(ifs, line)) {
×
1308
        auto pos = line.find('#');
×
1309
        if (pos != string::npos) {
×
1310
          line.resize(pos);
×
1311
        }
×
1312
        boost::trim(line);
×
1313
        if (line.empty()) {
×
1314
          continue;
×
1315
        }
×
1316

1317
        result->addMask(line);
×
1318
      }
×
1319
    }
×
1320
    log->info(Logr::Info, "Done parsing ranges from file, will override setting", "setting", Logging::Loggable(aclSetting),
×
1321
              "number", Logging::Loggable(result->size()), "file", Logging::Loggable(file));
×
1322
  }
×
1323
  else if (!::arg()[aclSetting].empty()) {
372✔
1324
    vector<string> ips;
190✔
1325
    stringtok(ips, ::arg()[aclSetting], ", ");
190✔
1326

1327
    for (const auto& address : ips) {
1,560✔
1328
      result->addMask(address);
1,560✔
1329
    }
1,560✔
1330
    log->info(Logr::Info, "Setting access control", "acl", Logging::Loggable(aclSetting), "addresses", Logging::IterLoggable(ips.begin(), ips.end()));
190✔
1331
  }
190✔
1332

1333
  return result;
372✔
1334
}
372✔
1335

1336
static void* pleaseSupplantAllowFrom(std::shared_ptr<NetmaskGroup> nmgroup)
1337
{
190✔
1338
  t_allowFrom = std::move(nmgroup);
190✔
1339
  return nullptr;
190✔
1340
}
190✔
1341

1342
static void* pleaseSupplantAllowNotifyFrom(std::shared_ptr<NetmaskGroup> nmgroup)
1343
{
190✔
1344
  t_allowNotifyFrom = std::move(nmgroup);
190✔
1345
  return nullptr;
190✔
1346
}
190✔
1347

1348
void* pleaseSupplantAllowNotifyFor(std::shared_ptr<notifyset_t> allowNotifyFor)
1349
{
50✔
1350
  t_allowNotifyFor = std::move(allowNotifyFor);
50✔
1351
  return nullptr;
50✔
1352
}
50✔
1353

1354
static void* pleaseSupplantProxyProtocolSettings(std::shared_ptr<NetmaskGroup> acl, std::shared_ptr<std::set<ComboAddress>> except)
1355
{
190✔
1356
  t_proxyProtocolACL = std::move(acl);
190✔
1357
  t_proxyProtocolExceptions = std::move(except);
190✔
1358
  return nullptr;
190✔
1359
}
190✔
1360

1361
void parseACLs()
1362
{
186✔
1363
  auto log = g_slog->withName("config");
186✔
1364

1365
  static bool l_initialized;
186✔
1366
  const std::array<string, 6> aclNames = {
186✔
1367
    "allow-from-file",
186✔
1368
    "allow-from",
186✔
1369
    "allow-notify-from-file",
186✔
1370
    "allow-notify-from",
186✔
1371
    "proxy-protocol-from",
186✔
1372
    "proxy-protocol-exceptions"};
186✔
1373

1374
  if (l_initialized) { // only reload configuration file on second call
186✔
1375

1376
    string configName = ::arg()["config-dir"] + "/recursor";
1✔
1377
    if (!::arg()["config-name"].empty()) {
1!
1378
      configName = ::arg()["config-dir"] + "/recursor-" + ::arg()["config-name"];
×
1379
    }
×
1380
    cleanSlashes(configName);
1✔
1381

1382
    if (g_yamlSettings) {
1!
1383
      configName += g_yamlSettingsSuffix;
×
1384
      string msg;
×
1385
      pdns::rust::settings::rec::Recursorsettings settings;
×
1386
      // XXX Does ::arg()["include-dir"] have the right value, i.e. potentially overriden by command line?
1387
      auto yamlstatus = pdns::settings::rec::readYamlSettings(configName, ::arg()["include-dir"], settings, msg, log);
×
1388

1389
      switch (yamlstatus) {
×
1390
      case pdns::settings::rec::YamlSettingsStatus::CannotOpen:
×
1391
        throw runtime_error("Unable to open '" + configName + "': " + msg);
×
1392
        break;
×
1393
      case pdns::settings::rec::YamlSettingsStatus::PresentButFailed:
×
1394
        throw runtime_error("Error processing '" + configName + "': " + msg);
×
1395
        break;
×
1396
      case pdns::settings::rec::YamlSettingsStatus::OK:
×
1397
        pdns::settings::rec::processAPIDir(arg()["include-dir"], settings, log);
×
1398
        // Does *not* set include-dir
1399
        pdns::settings::rec::setArgsForACLRelatedSettings(settings);
×
1400
        break;
×
1401
      }
×
1402
    }
×
1403
    else {
1✔
1404
      configName += ".conf";
1✔
1405
      if (!::arg().preParseFile(configName, "allow-from-file")) {
1!
1406
        throw runtime_error("Unable to re-parse configuration file '" + configName + "'");
×
1407
      }
×
1408
      ::arg().preParseFile(configName, "allow-from", LOCAL_NETS);
1✔
1409

1410
      if (!::arg().preParseFile(configName, "allow-notify-from-file")) {
1!
1411
        throw runtime_error("Unable to re-parse configuration file '" + configName + "'");
×
1412
      }
×
1413
      ::arg().preParseFile(configName, "allow-notify-from");
1✔
1414
      ::arg().preParseFile(configName, "proxy-protocol-from");
1✔
1415
      ::arg().preParseFile(configName, "proxy-protocol-exceptions");
1✔
1416

1417
      ::arg().preParseFile(configName, "include-dir");
1✔
1418
      ::arg().preParse(g_argc, g_argv, "include-dir");
1✔
1419

1420
      // then process includes
1421
      std::vector<std::string> extraConfigs;
1✔
1422
      ::arg().gatherIncludes(::arg()["include-dir"], ".conf", extraConfigs);
1✔
1423

1424
      for (const std::string& fileName : extraConfigs) {
1!
1425
        for (const auto& aclName : aclNames) {
×
1426
          if (!::arg().preParseFile(fileName, aclName, ::arg()[aclName])) {
×
1427
            throw runtime_error("Unable to re-parse configuration file include '" + fileName + "'");
×
1428
          }
×
1429
        }
×
1430
      }
×
1431
    }
1✔
1432
  }
1✔
1433
  // Process command line args potentially overriding settings read from file
1434
  for (const auto& aclName : aclNames) {
1,116✔
1435
    ::arg().preParse(g_argc, g_argv, aclName);
1,116✔
1436
  }
1,116✔
1437

1438
  auto allowFrom = parseACL("allow-from-file", "allow-from", log);
186✔
1439

1440
  if (allowFrom->empty()) {
186!
1441
    if (::arg()["local-address"] != "127.0.0.1" && ::arg().asNum("local-port") == 53) {
×
1442
      log->info(Logr::Warning, "WARNING: Allowing queries from all IP addresses - this can be a security risk!");
×
1443
    }
×
1444
    allowFrom = nullptr;
×
1445
  }
×
1446

1447
  *g_initialAllowFrom.lock() = allowFrom;
186✔
1448
  // coverity[copy_constructor_call] maybe this can be avoided, but be careful as pointers get passed to other threads
1449
  broadcastFunction([=] { return pleaseSupplantAllowFrom(allowFrom); });
190✔
1450

1451
  auto allowNotifyFrom = parseACL("allow-notify-from-file", "allow-notify-from", log);
186✔
1452

1453
  *g_initialAllowNotifyFrom.lock() = allowNotifyFrom;
186✔
1454
  // coverity[copy_constructor_call] maybe this can be avoided, but be careful as pointers get passed to other threads
1455
  broadcastFunction([=] { return pleaseSupplantAllowNotifyFrom(allowNotifyFrom); });
190✔
1456

1457
  std::shared_ptr<NetmaskGroup> proxyProtocolACL;
186✔
1458
  std::shared_ptr<std::set<ComboAddress>> proxyProtocolExceptions;
186✔
1459
  if (!::arg()["proxy-protocol-from"].empty()) {
186✔
1460
    proxyProtocolACL = std::make_shared<NetmaskGroup>();
11✔
1461
    proxyProtocolACL->toMasks(::arg()["proxy-protocol-from"]);
11✔
1462

1463
    std::vector<std::string> vec;
11✔
1464
    stringtok(vec, ::arg()["proxy-protocol-exceptions"], ", ");
11✔
1465
    if (!vec.empty()) {
11✔
1466
      proxyProtocolExceptions = std::make_shared<std::set<ComboAddress>>();
1✔
1467
      for (const auto& sockAddrStr : vec) {
1✔
1468
        ComboAddress sockAddr(sockAddrStr, 53);
1✔
1469
        proxyProtocolExceptions->emplace(sockAddr);
1✔
1470
      }
1✔
1471
    }
1✔
1472
  }
11✔
1473
  g_initialProxyProtocolACL = proxyProtocolACL;
186✔
1474
  g_initialProxyProtocolExceptions = proxyProtocolExceptions;
186✔
1475

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

1479
  l_initialized = true;
186✔
1480
}
186✔
1481

1482
static std::mutex pipeBroadCastMutex{};
1483

1484
void broadcastFunction(const pipefunc_t& func)
1485
{
611✔
1486
  // we do not want the handler and web code to use pipes simultaneously
1487
  std::scoped_lock lock(pipeBroadCastMutex);
611✔
1488

1489
  /* This function might be called by the worker with t_id not inited during startup
1490
     for the initialization of ACLs and domain maps. After that it should only
1491
     be called by the handler. */
1492

1493
  if (RecThreadInfo::infos().empty() && !RecThreadInfo::is_thread_inited()) {
611!
1494
    /* the handler and  distributors will call themselves below, but
1495
       during startup we get called while g_threadInfos has not been
1496
       populated yet to update the ACL or domain maps, so we need to
1497
       handle that case.
1498
    */
1499
    func();
572✔
1500
  }
572✔
1501

1502
  unsigned int thread = 0;
611✔
1503
  for (const auto& threadInfo : RecThreadInfo::infos()) {
611✔
1504
    if (thread++ == RecThreadInfo::thread_local_id()) {
195✔
1505
      func(); // don't write to ourselves!
39✔
1506
      continue;
39✔
1507
    }
39✔
1508

1509
    ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: manual ownership handling
156✔
1510
    tmsg->func = func;
156✔
1511
    tmsg->wantAnswer = true;
156✔
1512
    if (write(threadInfo.getPipes().writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // NOLINT: sizeof correct
156!
1513
      delete tmsg; // NOLINT: manual ownership handling
×
1514

1515
      unixDie("write to thread pipe returned wrong size or error");
×
1516
    }
×
1517

1518
    string* resp = nullptr;
156✔
1519
    if (read(threadInfo.getPipes().readFromThread, &resp, sizeof(resp)) != sizeof(resp)) { // NOLINT: sizeof correct
156!
1520
      unixDie("read from thread pipe returned wrong size or error");
×
1521
    }
×
1522

1523
    if (resp != nullptr) {
156!
1524
      delete resp; // NOLINT: manual ownership handling
×
1525
      resp = nullptr;
×
1526
    }
×
1527
    // coverity[leaked_storage]
1528
  }
156✔
1529
}
611✔
1530

1531
template <class T>
1532
void* voider(const std::function<T*()>& func)
1533
{
4,404✔
1534
  return func();
4,404✔
1535
}
4,404✔
1536

1537
static vector<ComboAddress>& operator+=(vector<ComboAddress>& lhs, const vector<ComboAddress>& rhs)
1538
{
16✔
1539
  lhs.insert(lhs.end(), rhs.begin(), rhs.end());
16✔
1540
  return lhs;
16✔
1541
}
16✔
1542

1543
static vector<pair<DNSName, uint16_t>>& operator+=(vector<pair<DNSName, uint16_t>>& lhs, const vector<pair<DNSName, uint16_t>>& rhs)
1544
{
×
1545
  lhs.insert(lhs.end(), rhs.begin(), rhs.end());
×
1546
  return lhs;
×
1547
}
×
1548

1549
static ProxyMappingStats_t& operator+=(ProxyMappingStats_t& lhs, const ProxyMappingStats_t& rhs)
1550
{
396✔
1551
  for (const auto& [key, entry] : rhs) {
396!
1552
    lhs[key].netmaskMatches += entry.netmaskMatches;
×
1553
    lhs[key].suffixMatches += entry.suffixMatches;
×
1554
  }
×
1555
  return lhs;
396✔
1556
}
396✔
1557

1558
static RemoteLoggerStats_t& operator+=(RemoteLoggerStats_t& lhs, const RemoteLoggerStats_t& rhs)
1559
{
1,584✔
1560
  for (const auto& [key, entry] : rhs) {
1,584!
1561
    lhs[key] += entry;
×
1562
  }
×
1563
  return lhs;
1,584✔
1564
}
1,584✔
1565

1566
// This function should only be called by the handler and web thread to gather metrics, wipe the
1567
// cache, reload the Lua script (not the Lua config) or change the current trace regex, and by the
1568
// SNMP thread to gather metrics.  Note that this currently skips the handler, but includes the
1569
// taskThread(s).
1570
template <class T>
1571
T broadcastAccFunction(const std::function<T*()>& func)
1572
{
957✔
1573
  if (RecThreadInfo::thread_local_id() != 0) {
957!
1574
    g_slog->withName("runtime")->info(Logr::Critical, "broadcastAccFunction has been called by a worker"); // tid will be added
×
1575
    _exit(1);
×
1576
  }
×
1577

1578
  // we do not want the handler and web code to use pipes simultaneously
1579
  std::scoped_lock lock(pipeBroadCastMutex);
957✔
1580

1581
  unsigned int thread = 0;
957✔
1582
  T ret = T();
957✔
1583
  for (const auto& threadInfo : RecThreadInfo::infos()) {
5,361!
1584
    if (thread++ == RecThreadInfo::thread_local_id()) {
5,361!
1585
      continue;
957✔
1586
    }
957✔
1587

1588
    const auto& tps = threadInfo.getPipes();
4,404✔
1589
    ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: manual ownership handling
4,404✔
1590
    tmsg->func = [func] { return voider<T>(func); };
4,404✔
1591
    tmsg->wantAnswer = true;
4,404✔
1592

1593
    if (write(tps.writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // NOLINT:: sizeof correct
4,404!
1594
      delete tmsg; // NOLINT: manual ownership handling
×
1595
      unixDie("write to thread pipe returned wrong size or error");
×
1596
    }
×
1597

1598
    T* resp = nullptr;
4,404✔
1599
    if (read(tps.readFromThread, &resp, sizeof(resp)) != sizeof(resp)) // NOLINT: sizeof correct
4,404!
1600
      unixDie("read from thread pipe returned wrong size or error");
×
1601

1602
    if (resp) {
4,404!
1603
      ret += *resp;
4,404✔
1604
      delete resp; // NOLINT: manual ownership handling
4,404✔
1605
      resp = nullptr;
4,404✔
1606
    }
4,404✔
1607
    // coverity[leaked_storage]
1608
  }
4,404✔
1609
  return ret;
957✔
1610
}
957✔
1611

1612
template string broadcastAccFunction(const std::function<string*()>& fun); // explicit instantiation
1613
template RecursorControlChannel::Answer broadcastAccFunction(const std::function<RecursorControlChannel::Answer*()>& fun); // explicit instantiation
1614
template uint64_t broadcastAccFunction(const std::function<uint64_t*()>& fun); // explicit instantiation
1615
template vector<ComboAddress> broadcastAccFunction(const std::function<vector<ComboAddress>*()>& fun); // explicit instantiation
1616
template vector<pair<DNSName, uint16_t>> broadcastAccFunction(const std::function<vector<pair<DNSName, uint16_t>>*()>& fun); // explicit instantiation
1617
template ThreadTimes broadcastAccFunction(const std::function<ThreadTimes*()>& fun);
1618
template ProxyMappingStats_t broadcastAccFunction(const std::function<ProxyMappingStats_t*()>& fun);
1619
template RemoteLoggerStats_t broadcastAccFunction(const std::function<RemoteLoggerStats_t*()>& fun);
1620

1621
static int initNet(Logr::log_t log)
1622
{
185✔
1623
  checkLinuxIPv6Limits(log);
185✔
1624
  try {
185✔
1625
    pdns::parseQueryLocalAddress(::arg()["query-local-address"]);
185✔
1626
  }
185✔
1627
  catch (std::exception& e) {
185✔
1628
    log->error(Logr::Error, e.what(), "Unable to assign local query address");
×
1629
    return 99;
×
1630
  }
×
1631

1632
  if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET)) {
185✔
1633
    SyncRes::s_doIPv4 = true;
183✔
1634
    log->info(Logr::Notice, "Enabling IPv4 transport for outgoing queries");
183✔
1635
  }
183✔
1636
  else {
2✔
1637
    log->info(Logr::Warning, "NOT using IPv4 for outgoing queries - add an IPv4 address (like '0.0.0.0') to query-local-address to enable");
2✔
1638
  }
2✔
1639

1640
  if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET6)) {
185✔
1641
    SyncRes::s_doIPv6 = true;
2✔
1642
    log->info(Logr::Notice, "Enabling IPv6 transport for outgoing queries");
2✔
1643
  }
2✔
1644
  else {
183✔
1645
    log->info(Logr::Warning, "NOT using IPv6 for outgoing queries - add an IPv6 address (like '::') to query-local-address to enable");
183✔
1646
  }
183✔
1647

1648
  if (!SyncRes::s_doIPv6 && !SyncRes::s_doIPv4) {
185!
1649
    log->info(Logr::Error, "No outgoing addresses configured! Can not continue");
×
1650
    return 99;
×
1651
  }
×
1652
  return 0;
185✔
1653
}
185✔
1654

1655
static int initDNSSEC(Logr::log_t log)
1656
{
185✔
1657
  if (::arg()["dnssec"] == "off") {
185✔
1658
    g_dnssecmode = DNSSECMode::Off;
6✔
1659
  }
6✔
1660
  else if (::arg()["dnssec"] == "process-no-validate") {
179✔
1661
    g_dnssecmode = DNSSECMode::ProcessNoValidate;
1✔
1662
  }
1✔
1663
  else if (::arg()["dnssec"] == "process") {
178✔
1664
    g_dnssecmode = DNSSECMode::Process;
120✔
1665
  }
120✔
1666
  else if (::arg()["dnssec"] == "validate") {
58!
1667
    g_dnssecmode = DNSSECMode::ValidateAll;
58✔
1668
  }
58✔
1669
  else if (::arg()["dnssec"] == "log-fail") {
×
1670
    g_dnssecmode = DNSSECMode::ValidateForLog;
×
1671
  }
×
1672
  else {
×
1673
    log->info(Logr::Error, "Unknown DNSSEC mode", "dnssec", Logging::Loggable(::arg()["dnssec"]));
×
1674
    return 1;
×
1675
  }
×
1676

1677
  {
185✔
1678
    auto value = ::arg().asNum("signature-inception-skew");
185✔
1679
    if (value < 0) {
185!
1680
      log->info(Logr::Error, "A negative value for 'signature-inception-skew' is not allowed");
×
1681
      return 1;
×
1682
    }
×
1683
    g_signatureInceptionSkew = value;
185✔
1684
  }
185✔
1685

1686
  g_dnssecLogBogus = ::arg().mustDo("dnssec-log-bogus");
×
1687
  g_maxNSEC3Iterations = ::arg().asNum("nsec3-max-iterations");
185✔
1688
  g_maxRRSIGsPerRecordToConsider = ::arg().asNum("max-rrsigs-per-record");
185✔
1689
  g_maxNSEC3sPerRecordToConsider = ::arg().asNum("max-nsec3s-per-record");
185✔
1690
  g_maxDNSKEYsToConsider = ::arg().asNum("max-dnskeys");
185✔
1691
  g_maxDSsToConsider = ::arg().asNum("max-ds-per-zone");
185✔
1692

1693
  vector<string> nums;
185✔
1694
  bool automatic = true;
185✔
1695
  if (!::arg()["dnssec-disabled-algorithms"].empty()) {
185!
1696
    automatic = false;
×
1697
    stringtok(nums, ::arg()["dnssec-disabled-algorithms"], ", ");
×
1698
    for (const auto& num : nums) {
×
1699
      DNSCryptoKeyEngine::switchOffAlgorithm(pdns::checked_stoi<unsigned int>(num));
×
1700
    }
×
1701
  }
×
1702
  else {
185✔
1703
    for (auto algo : {DNSSEC::RSASHA1, DNSSEC::RSASHA1NSEC3SHA1}) {
370✔
1704
      if (!DNSCryptoKeyEngine::verifyOne(algo)) {
370!
1705
        DNSCryptoKeyEngine::switchOffAlgorithm(algo);
×
1706
        nums.push_back(std::to_string(algo));
×
1707
      }
×
1708
    }
370✔
1709
  }
185✔
1710
  if (!nums.empty()) {
185!
1711
    log->info(Logr::Notice, "Disabled DNSSEC algorithms", "automatically", Logging::Loggable(automatic), "algorithms", Logging::IterLoggable(nums.begin(), nums.end()));
×
1712
  }
×
1713

1714
  return 0;
185✔
1715
}
185✔
1716

1717
static void initDontQuery(Logr::log_t log)
1718
{
185✔
1719
  if (!::arg()["dont-query"].empty()) {
185✔
1720
    vector<string> ips;
16✔
1721
    stringtok(ips, ::arg()["dont-query"], ", ");
16✔
1722
    ips.emplace_back("0.0.0.0");
16✔
1723
    ips.emplace_back("::");
16✔
1724

1725
    for (const auto& anIP : ips) {
336✔
1726
      SyncRes::addDontQuery(anIP);
336✔
1727
    }
336✔
1728
    log->info(Logr::Notice, "Will not send queries to", "addresses", Logging::IterLoggable(ips.begin(), ips.end()));
16✔
1729
  }
16✔
1730
}
185✔
1731

1732
static int initSyncRes(Logr::log_t log)
1733
{
185✔
1734
  SyncRes::s_minimumTTL = ::arg().asNum("minimum-ttl-override");
185✔
1735
  SyncRes::s_minimumECSTTL = ::arg().asNum("ecs-minimum-ttl-override");
185✔
1736
  SyncRes::s_maxnegttl = ::arg().asNum("max-negative-ttl");
185✔
1737
  SyncRes::s_maxbogusttl = ::arg().asNum("max-cache-bogus-ttl");
185✔
1738
  SyncRes::s_maxcachettl = max(::arg().asNum("max-cache-ttl"), 15);
185✔
1739

1740
  SyncRes::s_packetcachettl = ::arg().asNum("packetcache-ttl");
185✔
1741
  // Cap the packetcache-servfail-ttl and packetcache-negative-ttl to packetcache-ttl
1742
  SyncRes::s_packetcacheservfailttl = std::min(static_cast<unsigned int>(::arg().asNum("packetcache-servfail-ttl")), SyncRes::s_packetcachettl);
185✔
1743
  SyncRes::s_packetcachenegativettl = std::min(static_cast<unsigned int>(::arg().asNum("packetcache-negative-ttl")), SyncRes::s_packetcachettl);
185✔
1744

1745
  SyncRes::s_serverdownmaxfails = ::arg().asNum("server-down-max-fails");
185✔
1746
  SyncRes::s_serverdownthrottletime = ::arg().asNum("server-down-throttle-time");
185✔
1747
  SyncRes::s_unthrottle_n = ::arg().asNum("bypass-server-throttling-probability");
185✔
1748
  SyncRes::s_nonresolvingnsmaxfails = ::arg().asNum("non-resolving-ns-max-fails");
185✔
1749
  SyncRes::s_nonresolvingnsthrottletime = ::arg().asNum("non-resolving-ns-throttle-time");
185✔
1750
  SyncRes::s_serverID = ::arg()["server-id"];
185✔
1751
  // This bound is dynamically adjusted in SyncRes, depending on qname minimization being active
1752
  SyncRes::s_maxqperq = ::arg().asNum("max-qperq");
185✔
1753
  SyncRes::s_maxnsperresolve = ::arg().asNum("max-ns-per-resolve");
185✔
1754
  SyncRes::s_maxnsaddressqperq = ::arg().asNum("max-ns-address-qperq");
185✔
1755
  SyncRes::s_maxtotusec = 1000 * ::arg().asNum("max-total-msec");
185✔
1756
  SyncRes::s_maxdepth = ::arg().asNum("max-recursion-depth");
185✔
1757
  SyncRes::s_maxvalidationsperq = ::arg().asNum("max-signature-validations-per-query");
185✔
1758
  SyncRes::s_maxnsec3iterationsperq = ::arg().asNum("max-nsec3-hash-computations-per-query");
185✔
1759
  SyncRes::s_rootNXTrust = ::arg().mustDo("root-nx-trust");
185✔
1760
  SyncRes::s_refresh_ttlperc = ::arg().asNum("refresh-on-ttl-perc");
185✔
1761
  SyncRes::s_locked_ttlperc = ::arg().asNum("record-cache-locked-ttl-perc");
185✔
1762
  RecursorPacketCache::s_refresh_ttlperc = SyncRes::s_refresh_ttlperc;
185✔
1763
  SyncRes::s_tcp_fast_open = ::arg().asNum("tcp-fast-open");
185✔
1764
  SyncRes::s_tcp_fast_open_connect = ::arg().mustDo("tcp-fast-open-connect");
185✔
1765

1766
  SyncRes::s_dot_to_port_853 = ::arg().mustDo("dot-to-port-853");
185✔
1767
  SyncRes::s_event_trace_enabled = ::arg().asNum("event-trace-enabled");
185✔
1768
  SyncRes::s_save_parent_ns_set = ::arg().mustDo("save-parent-ns-set");
185✔
1769
  SyncRes::s_max_busy_dot_probes = ::arg().asNum("max-busy-dot-probes");
185✔
1770
  SyncRes::s_max_CNAMES_followed = ::arg().asNum("max-cnames-followed");
185✔
1771
  {
185✔
1772
    uint64_t sse = ::arg().asNum("serve-stale-extensions");
185✔
1773
    if (sse > std::numeric_limits<uint16_t>::max()) {
185!
1774
      log->info(Logr::Error, "Illegal serve-stale-extensions value; range = 0..65536", "value", Logging::Loggable(sse));
×
1775
      return 1;
×
1776
    }
×
1777
    MemRecursorCache::s_maxServedStaleExtensions = sse;
185✔
1778
    NegCache::s_maxServedStaleExtensions = sse;
185✔
1779
  }
185✔
1780
  MemRecursorCache::s_maxRRSetSize = ::arg().asNum("max-rrset-size");
×
1781
  MemRecursorCache::s_limitQTypeAny = ::arg().mustDo("limit-qtype-any");
185✔
1782

1783
  if (SyncRes::s_tcp_fast_open_connect) {
185!
1784
    checkFastOpenSysctl(true, log);
×
1785
    checkTFOconnect(log);
×
1786
  }
×
1787
  SyncRes::s_ecsipv4limit = ::arg().asNum("ecs-ipv4-bits");
185✔
1788
  SyncRes::s_ecsipv6limit = ::arg().asNum("ecs-ipv6-bits");
185✔
1789
  SyncRes::clearECSStats();
185✔
1790
  SyncRes::s_ecsipv4cachelimit = ::arg().asNum("ecs-ipv4-cache-bits");
185✔
1791
  SyncRes::s_ecsipv6cachelimit = ::arg().asNum("ecs-ipv6-cache-bits");
185✔
1792
  SyncRes::s_ecsipv4nevercache = ::arg().mustDo("ecs-ipv4-never-cache");
185✔
1793
  SyncRes::s_ecsipv6nevercache = ::arg().mustDo("ecs-ipv6-never-cache");
185✔
1794
  SyncRes::s_ecscachelimitttl = ::arg().asNum("ecs-cache-limit-ttl");
185✔
1795

1796
  SyncRes::s_qnameminimization = ::arg().mustDo("qname-minimization");
185✔
1797
  SyncRes::s_minimize_one_label = ::arg().asNum("qname-minimize-one-label");
185✔
1798
  SyncRes::s_max_minimize_count = ::arg().asNum("qname-max-minimize-count");
185✔
1799

1800
  SyncRes::s_hardenNXD = SyncRes::HardenNXD::DNSSEC;
185✔
1801
  string value = ::arg()["nothing-below-nxdomain"];
185✔
1802
  if (value == "yes") {
185!
1803
    SyncRes::s_hardenNXD = SyncRes::HardenNXD::Yes;
×
1804
  }
×
1805
  else if (value == "no") {
185!
1806
    SyncRes::s_hardenNXD = SyncRes::HardenNXD::No;
×
1807
  }
×
1808
  else if (value != "dnssec") {
185!
1809
    log->info(Logr::Error, "Unknown nothing-below-nxdomain mode", "mode", Logging::Loggable(value));
×
1810
    return 1;
×
1811
  }
×
1812

1813
  if (!::arg().isEmpty("ecs-scope-zero-address")) {
185✔
1814
    ComboAddress scopeZero(::arg()["ecs-scope-zero-address"]);
8✔
1815
    SyncRes::setECSScopeZeroAddress(Netmask(scopeZero, scopeZero.isIPv4() ? 32 : 128));
8✔
1816
  }
8✔
1817
  else {
177✔
1818
    Netmask netmask;
177✔
1819
    bool done = false;
177✔
1820

1821
    auto addr = pdns::getNonAnyQueryLocalAddress(AF_INET);
177✔
1822
    if (addr.sin4.sin_family != 0) {
177!
1823
      netmask = Netmask(addr, 32);
×
1824
      done = true;
×
1825
    }
×
1826
    if (!done) {
177!
1827
      addr = pdns::getNonAnyQueryLocalAddress(AF_INET6);
177✔
1828
      if (addr.sin4.sin_family != 0) {
177✔
1829
        netmask = Netmask(addr, 128);
2✔
1830
        done = true;
2✔
1831
      }
2✔
1832
    }
177✔
1833
    if (!done) {
177✔
1834
      netmask = Netmask(ComboAddress("127.0.0.1"), 32);
175✔
1835
    }
175✔
1836
    SyncRes::setECSScopeZeroAddress(netmask);
177✔
1837
  }
177✔
1838

1839
  SyncRes::parseEDNSSubnetAllowlist(::arg()["edns-subnet-whitelist"]);
185✔
1840
  SyncRes::parseEDNSSubnetAllowlist(::arg()["edns-subnet-allow-list"]);
185✔
1841
  SyncRes::parseEDNSSubnetAddFor(::arg()["ecs-add-for"]);
185✔
1842
  g_useIncomingECS = ::arg().mustDo("use-incoming-edns-subnet");
185✔
1843
  return 0;
185✔
1844
}
185✔
1845

1846
static unsigned int initDistribution(Logr::log_t log)
1847
{
185✔
1848
  unsigned int count = 0;
185✔
1849
  g_balancingFactor = ::arg().asDouble("distribution-load-factor");
185✔
1850
  if (g_balancingFactor != 0.0 && g_balancingFactor < 1.0) {
185!
1851
    g_balancingFactor = 0.0;
×
1852
    log->info(Logr::Warning, "Asked to run with a distribution-load-factor below 1.0, disabling it instead");
×
1853
  }
×
1854

1855
#ifdef SO_REUSEPORT
185✔
1856
  g_reusePort = ::arg().mustDo("reuseport");
185✔
1857
#endif
185✔
1858

1859
  RecThreadInfo::resize(RecThreadInfo::numRecursorThreads());
185✔
1860

1861
  if (g_reusePort) {
185✔
1862
    unsigned int threadNum = 1;
170✔
1863
    if (RecThreadInfo::weDistributeQueries()) {
170✔
1864
      /* first thread is the handler, then distributors */
1865
      for (unsigned int i = 0; i < RecThreadInfo::numDistributors(); i++, threadNum++) {
4✔
1866
        auto& info = RecThreadInfo::info(threadNum);
2✔
1867
        auto& deferredAdds = info.getDeferredAdds();
2✔
1868
        // The two last arguments to make{UDP,TCP}ServerSockets are used for logging purposes only, same for calls below
1869
        count += makeUDPServerSockets(deferredAdds, log, i == RecThreadInfo::numDistributors() - 1, RecThreadInfo::numDistributors());
2✔
1870
      }
2✔
1871
    }
2✔
1872
    else {
168✔
1873
      /* first thread is the handler, there is no distributor here and workers are accepting queries */
1874
      for (unsigned int i = 0; i < RecThreadInfo::numUDPWorkers(); i++, threadNum++) {
504✔
1875
        auto& info = RecThreadInfo::info(threadNum);
336✔
1876
        auto& deferredAdds = info.getDeferredAdds();
336✔
1877
        count += makeUDPServerSockets(deferredAdds, log, i == RecThreadInfo::numUDPWorkers() - 1, RecThreadInfo::numUDPWorkers());
336✔
1878
      }
336✔
1879
    }
168✔
1880
    threadNum = 1 + RecThreadInfo::numDistributors() + RecThreadInfo::numUDPWorkers();
170✔
1881
    for (unsigned int i = 0; i < RecThreadInfo::numTCPWorkers(); i++, threadNum++) {
340✔
1882
      auto& info = RecThreadInfo::info(threadNum);
170✔
1883
      auto& deferredAdds = info.getDeferredAdds();
170✔
1884
      auto& tcpSockets = info.getTCPSockets();
170✔
1885
      count += makeTCPServerSockets(deferredAdds, tcpSockets, log, i == RecThreadInfo::numTCPWorkers() - 1, RecThreadInfo::numTCPWorkers());
170✔
1886
    }
170✔
1887
  }
170✔
1888
  else {
15✔
1889
    std::set<int> tcpSockets;
15✔
1890
    /* we don't have reuseport so we can only open one socket per
1891
       listening addr:port and everyone will listen on it */
1892
    count += makeUDPServerSockets(s_deferredUDPadds, log, true, 1);
15✔
1893
    count += makeTCPServerSockets(s_deferredTCPadds, tcpSockets, log, true, 1);
15✔
1894

1895
    // TCP queries are handled by TCP workers
1896
    for (unsigned int i = 0; i < RecThreadInfo::numTCPWorkers(); i++) {
30✔
1897
      auto& info = RecThreadInfo::info(i + 1 + RecThreadInfo::numDistributors() + RecThreadInfo::numUDPWorkers());
15✔
1898
      info.setTCPSockets(tcpSockets);
15✔
1899
    }
15✔
1900
  }
15✔
1901
  return count;
185✔
1902
}
185✔
1903

1904
static int initForks(Logr::log_t log)
1905
{
185✔
1906
  int forks = 0;
185✔
1907
  for (; forks < ::arg().asNum("processes") - 1; ++forks) {
185!
1908
    if (fork() == 0) { // we are child
×
1909
      break;
×
1910
    }
×
1911
  }
×
1912

1913
  if (::arg().mustDo("daemon")) {
185!
1914
    log->info(Logr::Warning, "Calling daemonize, going to background");
×
1915
    g_log.toConsole(Logger::Critical);
×
1916
    daemonize(log);
×
1917
  }
×
1918

1919
  if (Utility::getpid() == 1) {
185!
1920
    /* We are running as pid 1, register sigterm and sigint handler
1921

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

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

1933
    signal(SIGTERM, termIntHandler);
×
1934
    signal(SIGINT, termIntHandler);
×
1935
  }
×
1936

1937
  signal(SIGUSR1, usr1Handler);
185✔
1938
  signal(SIGUSR2, usr2Handler);
185✔
1939
  signal(SIGPIPE, SIG_IGN); // NOLINT: Posix API
185✔
1940
  return forks;
185✔
1941
}
185✔
1942

1943
static int initPorts(Logr::log_t log)
1944
{
185✔
1945
  int port = ::arg().asNum("udp-source-port-min");
185✔
1946
  if (port < 1024 || port > 65535) {
185!
1947
    log->info(Logr::Error, "Unable to launch, udp-source-port-min is not a valid port number");
×
1948
    return 99; // this isn't going to fix itself either
×
1949
  }
×
1950
  g_minUdpSourcePort = port;
185✔
1951
  port = ::arg().asNum("udp-source-port-max");
185✔
1952
  if (port < 1024 || port > 65535 || port < g_minUdpSourcePort) {
185!
1953
    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");
×
1954
    return 99; // this isn't going to fix itself either
×
1955
  }
×
1956
  g_maxUdpSourcePort = port;
185✔
1957
  std::vector<string> parts{};
185✔
1958
  stringtok(parts, ::arg()["udp-source-port-avoid"], ", ");
185✔
1959
  for (const auto& part : parts) {
370✔
1960
    port = std::stoi(part);
370✔
1961
    if (port < 1024 || port > 65535) {
370!
1962
      log->info(Logr::Error, "Unable to launch, udp-source-port-avoid contains an invalid port number", "port", Logging::Loggable(part));
×
1963
      return 99; // this isn't going to fix itself either
×
1964
    }
×
1965
    g_avoidUdpSourcePorts.insert(port);
370✔
1966
  }
370✔
1967
  return 0;
185✔
1968
}
185✔
1969

1970
static void initSNMP([[maybe_unused]] Logr::log_t log)
1971
{
185✔
1972
  if (::arg().mustDo("snmp-agent")) {
185✔
1973
#ifdef HAVE_NET_SNMP
1✔
1974
    string setting = ::arg()["snmp-daemon-socket"];
1✔
1975
    if (setting.empty()) {
1!
1976
      setting = ::arg()["snmp-master-socket"];
1✔
1977
    }
1✔
1978
    g_snmpAgent = std::make_shared<RecursorSNMPAgent>("recursor", setting);
1✔
1979
    g_snmpAgent->run();
1✔
1980
#else
1981
    const std::string msg = "snmp-agent set but SNMP support not compiled in";
1982
    log->info(Logr::Error, msg);
1983
#endif // HAVE_NET_SNMP
1984
  }
1✔
1985
}
185✔
1986

1987
static int initControl(Logr::log_t log, uid_t newuid, int forks)
1988
{
185✔
1989
  if (!::arg()["chroot"].empty()) {
185!
1990
#ifdef HAVE_SYSTEMD
×
1991
    char* ns;
×
1992
    ns = getenv("NOTIFY_SOCKET");
×
1993
    if (ns != nullptr) {
×
1994
      log->info(Logr::Error, "Unable to chroot when running from systemd. Please disable chroot= or set the 'Type' for this service to 'simple'");
×
1995
      return 1;
×
1996
    }
×
1997
#endif
×
1998
    if (chroot(::arg()["chroot"].c_str()) < 0 || chdir("/") < 0) {
×
1999
      int err = errno;
×
2000
      log->error(Logr::Error, err, "Unable to chroot", "chroot", Logging::Loggable(::arg()["chroot"]));
×
2001
      return 1;
×
2002
    }
×
2003
    log->info(Logr::Info, "Chrooted", "chroot", Logging::Loggable(::arg()["chroot"]));
×
2004
  }
×
2005

2006
  checkSocketDir(log);
185✔
2007

2008
  g_pidfname = ::arg()["socket-dir"] + "/" + g_programname + ".pid";
185✔
2009
  if (!g_pidfname.empty()) {
185!
2010
    unlink(g_pidfname.c_str()); // remove possible old pid file
185✔
2011
  }
185✔
2012
  writePid(log);
185✔
2013

2014
  makeControlChannelSocket(::arg().asNum("processes") > 1 ? forks : -1);
185!
2015

2016
  Utility::dropUserPrivs(newuid);
185✔
2017
  try {
185✔
2018
    /* we might still have capabilities remaining, for example if we have been started as root
2019
       without --setuid (please don't do that) or as an unprivileged user with ambient capabilities
2020
       like CAP_NET_BIND_SERVICE.
2021
    */
2022
    dropCapabilities();
185✔
2023
  }
185✔
2024
  catch (const std::exception& e) {
185✔
2025
    log->error(Logr::Warning, e.what(), "Could not drop capabilities");
×
2026
  }
×
2027
  return 0;
185✔
2028
}
185✔
2029

2030
static void initSuffixMatchNodes([[maybe_unused]] Logr::log_t log)
2031
{
185✔
2032
  {
185✔
2033
    SuffixMatchNode dontThrottleNames;
185✔
2034
    vector<string> parts;
185✔
2035
    stringtok(parts, ::arg()["dont-throttle-names"], " ,");
185✔
2036
    for (const auto& part : parts) {
185!
2037
      dontThrottleNames.add(DNSName(part));
×
2038
    }
×
2039
    g_dontThrottleNames.setState(std::move(dontThrottleNames));
185✔
2040

2041
    NetmaskGroup dontThrottleNetmasks;
185✔
2042
    dontThrottleNetmasks.toMasks(::arg()["dont-throttle-netmasks"]);
185✔
2043
    g_dontThrottleNetmasks.setState(std::move(dontThrottleNetmasks));
185✔
2044
  }
185✔
2045

2046
  {
185✔
2047
    SuffixMatchNode xdnssecNames;
185✔
2048
    vector<string> parts;
185✔
2049
    stringtok(parts, ::arg()["x-dnssec-names"], " ,");
185✔
2050
    for (const auto& part : parts) {
185!
2051
      xdnssecNames.add(DNSName(part));
×
2052
    }
×
2053
    g_xdnssec.setState(std::move(xdnssecNames));
185✔
2054
  }
185✔
2055

2056
  {
185✔
2057
    SuffixMatchNode dotauthNames;
185✔
2058
    vector<string> parts;
185✔
2059
    stringtok(parts, ::arg()["dot-to-auth-names"], " ,");
185✔
2060
#ifndef HAVE_DNS_OVER_TLS
2061
    if (!parts.empty()) {
2062
      log->info(Logr::Error, "dot-to-auth-names setting contains names, but Recursor was built without DNS over TLS support. Setting will be ignored");
2063
    }
2064
#endif
2065
    for (const auto& part : parts) {
185✔
2066
      dotauthNames.add(DNSName(part));
7✔
2067
    }
7✔
2068
    g_DoTToAuthNames.setState(std::move(dotauthNames));
185✔
2069
  }
185✔
2070
}
185✔
2071

2072
static void initCarbon()
2073
{
185✔
2074
  CarbonConfig config;
185✔
2075
  stringtok(config.servers, arg()["carbon-server"], ", ");
185✔
2076
  config.hostname = arg()["carbon-ourname"];
185✔
2077
  config.instance_name = arg()["carbon-instance"];
185✔
2078
  config.namespace_name = arg()["carbon-namespace"];
185✔
2079
  g_carbonConfig.setState(std::move(config));
185✔
2080
}
185✔
2081

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

2106
static int serviceMain(Logr::log_t log)
2107
{
185✔
2108
  g_log.setName(g_programname);
185✔
2109
  g_log.disableSyslog(::arg().mustDo("disable-syslog"));
185✔
2110
  g_log.setTimestamps(::arg().mustDo("log-timestamp"));
185✔
2111
  g_regressionTestMode = ::arg().mustDo("devonly-regression-test-mode");
185✔
2112

2113
  if (!::arg()["logging-facility"].empty()) {
185!
2114
    int val = logFacilityToLOG(::arg().asNum("logging-facility"));
×
2115
    if (val >= 0) {
×
2116
      g_log.setFacility(val);
×
2117
    }
×
2118
    else {
×
2119
      log->info(Logr::Error, "Unknown logging facility", "facility", Logging::Loggable(::arg().asNum("logging-facility")));
×
2120
    }
×
2121
  }
×
2122

2123
  g_disthashseed = dns_random_uint32();
185✔
2124

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

2136
  auto luaResult = luaconfig(false);
185✔
2137
  if (luaResult.d_ret != 0) {
185!
2138
    log->error(Logr::Error, luaResult.d_str, "Cannot load Lua or equivalent YAML configuration");
×
2139
    return 1;
×
2140
  }
×
2141

2142
  parseACLs();
185✔
2143
  initPublicSuffixList(::arg()["public-suffix-list-file"]);
185✔
2144

2145
  initDontQuery(log);
185✔
2146

2147
  RecThreadInfo::setWeDistributeQueries(::arg().mustDo("pdns-distributes-queries"));
185✔
2148
  if (RecThreadInfo::weDistributeQueries()) {
185✔
2149
    log->info(Logr::Notice, "PowerDNS Recursor itself will distribute queries over threads");
14✔
2150
  }
14✔
2151

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

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

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

2168
  if (!::arg()["proxy-protocol-from"].empty()) {
185✔
2169
    g_initialProxyProtocolACL = std::make_shared<NetmaskGroup>();
10✔
2170
    g_initialProxyProtocolACL->toMasks(::arg()["proxy-protocol-from"]);
10✔
2171

2172
    std::vector<std::string> vec;
10✔
2173
    stringtok(vec, ::arg()["proxy-protocol-exceptions"], ", ");
10✔
2174
    if (!vec.empty()) {
10✔
2175
      g_initialProxyProtocolExceptions = std::make_shared<std::set<ComboAddress>>();
1✔
2176
      for (const auto& sockAddrStr : vec) {
1✔
2177
        ComboAddress sockAddr(sockAddrStr, 53);
1✔
2178
        g_initialProxyProtocolExceptions->emplace(sockAddr);
1✔
2179
      }
1✔
2180
    }
1✔
2181
  }
10✔
2182
  g_proxyProtocolMaximumSize = ::arg().asNum("proxy-protocol-maximum-size");
185✔
2183

2184
  ret = initDNS64(log);
185✔
2185
  if (ret != 0) {
185!
2186
    return ret;
×
2187
  }
×
2188
  g_networkTimeoutMsec = ::arg().asNum("network-timeout");
185✔
2189

2190
  { // Reduce scope of locks (otherwise Coverity induces from this line the global vars below should be
185✔
2191
    // protected by a mutex)
2192
    std::tie(*g_initialDomainMap.lock(), *g_initialAllowNotifyFor.lock()) = parseZoneConfiguration(g_yamlSettings);
185✔
2193
  }
185✔
2194

2195
  g_latencyStatSize = ::arg().asNum("latency-statistic-size");
185✔
2196

2197
  g_logCommonErrors = ::arg().mustDo("log-common-errors");
185✔
2198
  g_logRPZChanges = ::arg().mustDo("log-rpz-changes");
185✔
2199

2200
  g_anyToTcp = ::arg().mustDo("any-to-tcp");
185✔
2201
  g_allowNoRD = ::arg().mustDo("allow-no-rd");
185✔
2202
  g_udpTruncationThreshold = ::arg().asNum("udp-truncation-threshold");
185✔
2203

2204
  g_lowercaseOutgoing = ::arg().mustDo("lowercase-outgoing");
185✔
2205

2206
  g_paddingFrom.toMasks(::arg()["edns-padding-from"]);
185✔
2207
  if (::arg()["edns-padding-mode"] == "always") {
185✔
2208
    g_paddingMode = PaddingMode::Always;
3✔
2209
  }
3✔
2210
  else if (::arg()["edns-padding-mode"] == "padded-queries-only") {
182!
2211
    g_paddingMode = PaddingMode::PaddedQueries;
182✔
2212
  }
182✔
2213
  else {
×
2214
    log->info(Logr::Error, "Unknown edns-padding-mode", "edns-padding-mode", Logging::Loggable(::arg()["edns-padding-mode"]));
×
2215
    return 1;
×
2216
  }
×
2217
  g_paddingTag = ::arg().asNum("edns-padding-tag");
185✔
2218
  g_paddingOutgoing = ::arg().mustDo("edns-padding-out");
185✔
2219
  g_ECSHardening = ::arg().mustDo("edns-subnet-harden");
185✔
2220

2221
  // Ignong errors return value, as YAML parsing already checked the format of the entries.
2222
  enableOutgoingCookies(::arg().mustDo("outgoing-cookies"), ::arg()["outgoing-cookies-unsupported"]);
185✔
2223

2224
  RecThreadInfo::setNumDistributorThreads(::arg().asNum("distributor-threads"));
185✔
2225
  RecThreadInfo::setNumUDPWorkerThreads(::arg().asNum("threads"));
185✔
2226
  if (RecThreadInfo::numUDPWorkers() < 1) {
185!
2227
    log->info(Logr::Warning, "Asked to run with 0 threads, raising to 1 instead");
×
2228
    RecThreadInfo::setNumUDPWorkerThreads(1);
×
2229
  }
×
2230
  RecThreadInfo::setNumTCPWorkerThreads(::arg().asNum("tcp-threads"));
185✔
2231
  if (RecThreadInfo::numTCPWorkers() < 1) {
185!
2232
    log->info(Logr::Warning, "Asked to run with 0 TCP threads, raising to 1 instead");
×
2233
    RecThreadInfo::setNumTCPWorkerThreads(1);
×
2234
  }
×
2235

2236
  g_maxMThreads = ::arg().asNum("max-mthreads");
185✔
2237

2238
  int64_t maxInFlight = ::arg().asNum("max-concurrent-requests-per-tcp-connection");
185✔
2239
  if (maxInFlight < 1 || maxInFlight > USHRT_MAX || maxInFlight >= g_maxMThreads) {
185!
2240
    log->info(Logr::Warning, "Asked to run with illegal max-concurrent-requests-per-tcp-connection, setting to default (10)");
×
2241
    TCPConnection::s_maxInFlight = 10;
×
2242
  }
×
2243
  else {
185✔
2244
    TCPConnection::s_maxInFlight = maxInFlight;
185✔
2245
  }
185✔
2246

2247
  int64_t millis = ::arg().asNum("tcp-out-max-idle-ms");
185✔
2248
  TCPOutConnectionManager::s_maxIdleTime = timeval{millis / 1000, (static_cast<suseconds_t>(millis) % 1000) * 1000};
185✔
2249
  TCPOutConnectionManager::s_maxIdlePerAuth = ::arg().asNum("tcp-out-max-idle-per-auth");
185✔
2250
  TCPOutConnectionManager::s_maxQueries = ::arg().asNum("tcp-out-max-queries");
185✔
2251
  TCPOutConnectionManager::s_maxIdlePerThread = ::arg().asNum("tcp-out-max-idle-per-thread");
185✔
2252

2253
  g_gettagNeedsEDNSOptions = ::arg().mustDo("gettag-needs-edns-options");
185✔
2254

2255
  s_statisticsInterval = ::arg().asNum("statistics-interval");
185✔
2256

2257
  SyncRes::s_addExtendedResolutionDNSErrors = ::arg().mustDo("extended-resolution-errors");
185✔
2258

2259
  if (::arg().asNum("aggressive-nsec-cache-size") > 0) {
185!
2260
    if (g_dnssecmode == DNSSECMode::ValidateAll || g_dnssecmode == DNSSECMode::ValidateForLog || g_dnssecmode == DNSSECMode::Process) {
185!
2261
      g_aggressiveNSECCache = make_unique<AggressiveNSECCache>(::arg().asNum("aggressive-nsec-cache-size"));
178✔
2262
    }
178✔
2263
    else {
7✔
2264
      log->info(Logr::Warning, "Aggressive NSEC/NSEC3 caching is enabled but DNSSEC validation is not set to 'validate', 'log-fail' or 'process', ignoring");
7✔
2265
    }
7✔
2266
  }
185✔
2267

2268
  AggressiveNSECCache::s_nsec3DenialProofMaxCost = ::arg().asNum("aggressive-cache-max-nsec3-hash-cost");
185✔
2269
  AggressiveNSECCache::s_maxNSEC3CommonPrefix = static_cast<uint8_t>(std::round(std::log2(::arg().asNum("aggressive-cache-min-nsec3-hit-ratio"))));
185✔
2270
  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));
185✔
2271

2272
  initSuffixMatchNodes(log);
185✔
2273
  initCarbon();
185✔
2274
  auto listeningSockets = initDistribution(log);
185✔
2275

2276
#ifdef NOD_ENABLED
185✔
2277
  // Setup newly observed domain globals
2278
  setupNODGlobal();
185✔
2279
#endif /* NOD_ENABLED */
185✔
2280

2281
  auto forks = initForks(log);
185✔
2282

2283
  g_tcpTimeout = ::arg().asNum("client-tcp-timeout");
185✔
2284
  g_maxTCPClients = ::arg().asNum("max-tcp-clients");
185✔
2285
  g_maxTCPPerClient = ::arg().asNum("max-tcp-per-client");
185✔
2286
  g_tcpMaxQueriesPerConn = ::arg().asNum("max-tcp-queries-per-connection");
185✔
2287
  g_maxUDPQueriesPerRound = ::arg().asNum("max-udp-queries-per-round");
185✔
2288

2289
  g_useKernelTimestamp = ::arg().mustDo("protobuf-use-kernel-timestamp");
185✔
2290
  g_maxChainLength = ::arg().asNum("max-chain-length");
185✔
2291

2292
  checkOrFixFDS(listeningSockets, log);
185✔
2293
  checkOrFixLinuxMapCountLimits(log);
185✔
2294

2295
#ifdef HAVE_LIBSODIUM
185✔
2296
  if (sodium_init() == -1) {
185!
2297
    log->info(Logr::Error, "Unable to initialize sodium crypto library");
×
2298
    return 99;
×
2299
  }
×
2300
#endif
185✔
2301

2302
  openssl_thread_setup();
185✔
2303
  openssl_seed();
185✔
2304

2305
  gid_t newgid = 0;
185✔
2306
  if (!::arg()["setgid"].empty()) {
185!
2307
    newgid = strToGID(::arg()["setgid"]);
×
2308
  }
×
2309
  uid_t newuid = 0;
185✔
2310
  if (!::arg()["setuid"].empty()) {
185!
2311
    newuid = strToUID(::arg()["setuid"]);
×
2312
  }
×
2313

2314
  Utility::dropGroupPrivs(newuid, newgid);
185✔
2315

2316
  ret = initControl(log, newuid, forks);
185✔
2317
  if (ret != 0) {
185!
2318
    return ret;
×
2319
  }
×
2320

2321
  {
185✔
2322
    auto lci = g_luaconfs.getCopy();
185✔
2323
    startLuaConfigDelayedThreads(lci, lci.generation);
185✔
2324
  }
185✔
2325

2326
  RecThreadInfo::makeThreadPipes(log);
185✔
2327

2328
  disableStats(StatComponent::API, ::arg()["stats-api-blacklist"]);
185✔
2329
  disableStats(StatComponent::Carbon, ::arg()["stats-carbon-blacklist"]);
185✔
2330
  disableStats(StatComponent::RecControl, ::arg()["stats-rec-control-blacklist"]);
185✔
2331
  disableStats(StatComponent::SNMP, ::arg()["stats-snmp-blacklist"]);
185✔
2332

2333
  disableStats(StatComponent::API, ::arg()["stats-api-disabled-list"]);
185✔
2334
  disableStats(StatComponent::Carbon, ::arg()["stats-carbon-disabled-list"]);
185✔
2335
  disableStats(StatComponent::RecControl, ::arg()["stats-rec-control-disabled-list"]);
185✔
2336
  disableStats(StatComponent::SNMP, ::arg()["stats-snmp-disabled-list"]);
185✔
2337

2338
  // Run before any thread doing stats related things
2339
  registerAllStats();
185✔
2340

2341
  initSNMP(log);
185✔
2342

2343
  ret = initPorts(log);
185✔
2344
  if (ret != 0) {
185!
2345
    return ret;
×
2346
  }
×
2347

2348
#ifdef NOD_ENABLED
185✔
2349
  setupNODThread(log);
185✔
2350
#endif /* NOD_ENABLED */
185✔
2351

2352
  runStartStopLua(true, log);
185✔
2353
  ret = RecThreadInfo::runThreads(log);
185✔
2354
  runStartStopLua(false, log);
185✔
2355
  return ret;
185✔
2356
}
185✔
2357

2358
static void handlePipeRequest(int fileDesc, FDMultiplexer::funcparam_t& /* var */)
2359
{
8,138✔
2360
  ThreadMSG* tmsg = nullptr;
8,138✔
2361

2362
  if (read(fileDesc, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // fd == readToThread || fd == readQueriesToThread NOLINT: sizeof correct
8,138!
2363
    unixDie("read from thread pipe returned wrong size or error");
×
2364
  }
×
2365

2366
  void* resp = nullptr;
8,138✔
2367
  try {
8,138✔
2368
    resp = tmsg->func();
8,138✔
2369
  }
8,138✔
2370
  catch (const PDNSException& pdnsException) {
8,138✔
2371
    g_rateLimitedLogger.log(g_slog->withName("runtime"), "PIPE function", pdnsException);
×
2372
  }
×
2373
  catch (const MOADNSException& moadnsexception) {
8,138✔
2374
    if (g_logCommonErrors) {
×
2375
      g_slog->withName("runtime")->error(moadnsexception.what(), "PIPE function created an exception", "excepion", Logging::Loggable("MOADNSException"));
×
2376
    }
×
2377
  }
×
2378
  catch (const std::exception& stdException) {
8,138✔
2379
    g_rateLimitedLogger.log(g_slog->withName("runtime"), "PIPE function", stdException);
×
2380
  }
×
2381
  catch (...) {
8,138✔
2382
    g_rateLimitedLogger.log(g_slog->withName("runtime"), "PIPE function");
×
2383
  }
×
2384
  if (tmsg->wantAnswer) {
8,157✔
2385
    if (write(RecThreadInfo::self().getPipes().writeFromThread, &resp, sizeof(resp)) != sizeof(resp)) {
4,560!
2386
      delete tmsg; // NOLINT: manual ownership handling
×
2387
      unixDie("write to thread pipe returned wrong size or error");
×
2388
    }
×
2389
  }
4,560✔
2390

2391
  delete tmsg; // NOLINT: manual ownership handling
8,153✔
2392
}
8,153✔
2393

2394
static void handleRCC(int fileDesc, FDMultiplexer::funcparam_t& /* var */)
2395
{
715✔
2396
  auto log = g_slog->withName("control");
715✔
2397
  try {
715✔
2398
    FDWrapper clientfd = accept(fileDesc, nullptr, nullptr);
715✔
2399
    if (clientfd == -1) {
715!
2400
      throw PDNSException("accept failed");
×
2401
    }
×
2402
    string msg = g_rcc.recv(clientfd).d_str;
715✔
2403
    log->info(Logr::Info, "Received rec_control command via control socket", "command", Logging::Loggable(msg));
715✔
2404

2405
    RecursorControlParser::func_t* command = nullptr;
715✔
2406
    auto answer = RecursorControlParser::getAnswer(clientfd, msg, &command);
715✔
2407

2408
    if (command != doExitNicely) {
715✔
2409
      g_rcc.send(clientfd, answer);
530✔
2410
    }
530✔
2411
    command();
715✔
2412
    if (command == doExitNicely) {
715✔
2413
      g_rcc.send(clientfd, answer);
185✔
2414
    }
185✔
2415
  }
715✔
2416
  catch (const std::exception& e) {
715✔
2417
    log->error(Logr::Error, e.what(), "Exception while dealing with control socket request", "exception", Logging::Loggable("std::exception"));
×
2418
  }
×
2419
  catch (const PDNSException& ae) {
715✔
2420
    log->error(Logr::Error, ae.reason, "Exception while dealing with control socket request", "exception", Logging::Loggable("PDNSException"));
×
2421
  }
×
2422
}
715✔
2423

2424
class PeriodicTask
2425
{
2426
public:
2427
  PeriodicTask(const string& aName, time_t aTime) :
2428
    period{aTime, 0}, name(aName)
2429
  {
2,805✔
2430
    if (aTime <= 0) {
2,805!
2431
      throw PDNSException("Invalid period of periodic task " + aName);
×
2432
    }
×
2433
  }
2,805✔
2434

2435
  void runIfDue(struct timeval& now, const std::function<void()>& function)
2436
  {
5,408✔
2437
    if (last_run < now - period) {
5,408✔
2438
      function();
3,020✔
2439
      Utility::gettimeofday(&last_run);
3,020✔
2440
      now = last_run;
3,020✔
2441
    }
3,020✔
2442
  }
5,408✔
2443

2444
  [[nodiscard]] time_t getPeriod() const
2445
  {
47✔
2446
    return period.tv_sec;
47✔
2447
  }
47✔
2448

2449
  void setPeriod(time_t newperiod)
2450
  {
325✔
2451
    period.tv_sec = newperiod;
325✔
2452
  }
325✔
2453

2454
  void updateLastRun()
2455
  {
99✔
2456
    Utility::gettimeofday(&last_run);
99✔
2457
  }
99✔
2458

2459
  [[nodiscard]] bool hasRun() const
2460
  {
225✔
2461
    return last_run.tv_sec != 0 || last_run.tv_usec != 0;
225!
2462
  }
225✔
2463

2464
private:
2465
  struct timeval last_run{
2466
    0, 0};
2467
  struct timeval period;
2468
  string name;
2469
};
2470

2471
static void houseKeepingWork(Logr::log_t log)
2472
{
989✔
2473
  struct timeval now{};
989✔
2474
  Utility::gettimeofday(&now);
989✔
2475
  t_Counters.updateSnap(now, g_regressionTestMode);
989✔
2476

2477
  // Below are the tasks that run for every recursorThread, including handler and taskThread
2478

2479
  static thread_local PeriodicTask pruneTCPTask{"pruneTCPTask", 5};
989✔
2480
  pruneTCPTask.runIfDue(now, [now]() {
989✔
2481
    t_tcp_manager.cleanup(now);
737✔
2482
  });
737✔
2483

2484
  const auto& info = RecThreadInfo::self();
989✔
2485

2486
  // Threads handling packets process config changes in the input path, but not all threads process input packets
2487
  // distr threads only process TCP, so that may not happenn very often. So do all periodically.
2488
  static thread_local PeriodicTask exportConfigTask{"exportConfigTask", 30};
989✔
2489
  auto luaconfsLocal = g_luaconfs.getLocal();
989✔
2490
  exportConfigTask.runIfDue(now, [&luaconfsLocal]() {
989✔
2491
    checkProtobufExport(luaconfsLocal);
657✔
2492
    checkOutgoingProtobufExport(luaconfsLocal);
657✔
2493
#ifdef HAVE_FSTRM
657✔
2494
    checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
657✔
2495
    checkFrameStreamExport(luaconfsLocal, luaconfsLocal->nodFrameStreamExportConfig, t_nodFrameStreamServersInfo);
657✔
2496
#endif
657✔
2497
  });
657✔
2498

2499
  // Below are the thread specific tasks for the handler and the taskThread
2500
  // Likley a few handler tasks could be moved to the taskThread
2501
  if (info.isTaskThread()) {
989✔
2502
    // TaskQueue is run always
2503
    runTasks(10, g_logCommonErrors);
298✔
2504

2505
    static PeriodicTask ztcTask{"ZTC", 60};
298✔
2506
    static map<DNSName, RecZoneToCache::State> ztcStates;
298✔
2507
    ztcTask.runIfDue(now, [&luaconfsLocal]() {
298✔
2508
      RecZoneToCache::maintainStates(luaconfsLocal->ztcConfigs, ztcStates, luaconfsLocal->generation);
107✔
2509
      for (const auto& ztc : luaconfsLocal->ztcConfigs) {
107✔
2510
        RecZoneToCache::ZoneToCache(ztc.second, ztcStates.at(ztc.first));
1✔
2511
      }
1✔
2512
    });
107✔
2513
  }
298✔
2514
  else if (info.isHandler()) {
691✔
2515
    if (g_packetCache) {
225✔
2516
      static PeriodicTask packetCacheTask{"packetCacheTask", 5};
212✔
2517
      packetCacheTask.runIfDue(now, [now]() {
212✔
2518
        g_packetCache->doPruneTo(now.tv_sec, g_maxPacketCacheEntries);
126✔
2519
      });
126✔
2520
    }
212✔
2521
    static PeriodicTask recordCachePruneTask{"RecordCachePruneTask", 5};
225✔
2522
    recordCachePruneTask.runIfDue(now, [now]() {
225✔
2523
      g_recCache->doPrune(now.tv_sec, g_maxCacheEntries);
133✔
2524
    });
133✔
2525

2526
    static PeriodicTask negCachePruneTask{"NegCachePrunteTask", 5};
225✔
2527
    negCachePruneTask.runIfDue(now, [now]() {
225✔
2528
      g_negCache->prune(now.tv_sec, g_maxCacheEntries / 8);
133✔
2529
    });
133✔
2530

2531
    static PeriodicTask aggrNSECPruneTask{"AggrNSECPruneTask", 5};
225✔
2532
    aggrNSECPruneTask.runIfDue(now, [now]() {
225✔
2533
      if (g_aggressiveNSECCache) {
133✔
2534
        g_aggressiveNSECCache->prune(now.tv_sec);
129✔
2535
      }
129✔
2536
    });
133✔
2537

2538
    static PeriodicTask pruneNSpeedTask{"pruneNSSpeedTask", 30};
225✔
2539
    pruneNSpeedTask.runIfDue(now, [now]() {
225✔
2540
      SyncRes::pruneNSSpeeds(now.tv_sec - 300);
99✔
2541
    });
99✔
2542

2543
    static PeriodicTask pruneEDNSTask{"pruneEDNSTask", 60};
225✔
2544
    pruneEDNSTask.runIfDue(now, [now]() {
225✔
2545
      SyncRes::pruneEDNSStatuses(now.tv_sec);
99✔
2546
    });
99✔
2547

2548
    if (SyncRes::s_max_busy_dot_probes > 0) {
225!
2549
      static PeriodicTask pruneDoTProbeMap{"pruneDoTProbeMapTask", 60};
×
2550
      pruneDoTProbeMap.runIfDue(now, [now]() {
×
2551
        SyncRes::pruneDoTProbeMap(now.tv_sec);
×
2552
      });
×
2553
    }
×
2554

2555
    static PeriodicTask pruneThrottledTask{"pruneThrottledTask", 5};
225✔
2556
    pruneThrottledTask.runIfDue(now, [now]() {
225✔
2557
      SyncRes::pruneThrottledServers(now.tv_sec);
133✔
2558
    });
133✔
2559

2560
    static PeriodicTask pruneFailedServersTask{"pruneFailedServerTask", 5};
225✔
2561
    pruneFailedServersTask.runIfDue(now, [now]() {
225✔
2562
      SyncRes::pruneFailedServers(now.tv_sec - static_cast<time_t>(SyncRes::s_serverdownthrottletime * 10));
133✔
2563
    });
133✔
2564

2565
    static PeriodicTask pruneNonResolvingTask{"pruneNonResolvingTask", 5};
225✔
2566
    pruneNonResolvingTask.runIfDue(now, [now]() {
225✔
2567
      SyncRes::pruneNonResolving(now.tv_sec - SyncRes::s_nonresolvingnsthrottletime);
133✔
2568
    });
133✔
2569

2570
    static PeriodicTask pruneSaveParentSetTask{"pruneSaveParentSetTask", 60};
225✔
2571
    pruneSaveParentSetTask.runIfDue(now, [now]() {
225✔
2572
      SyncRes::pruneSaveParentsNSSets(now.tv_sec);
99✔
2573
    });
99✔
2574

2575
    static PeriodicTask pruneCookiesTask{"pruneCookiesTask", 30};
225✔
2576
    pruneCookiesTask.runIfDue(now, [now]() {
225✔
2577
      pruneCookies(now.tv_sec - 3000);
99✔
2578
    });
99✔
2579

2580
    // By default, refresh at 80% of max-cache-ttl with a minimum period of 10s
2581
    const unsigned int minRootRefreshInterval = 10;
225✔
2582
    static PeriodicTask rootUpdateTask{"rootUpdateTask", std::max(SyncRes::s_maxcachettl * 8 / 10, minRootRefreshInterval)};
225✔
2583
    rootUpdateTask.runIfDue(now, [now, &log, minRootRefreshInterval]() {
225✔
2584
      int res = 0;
100✔
2585
      if (!g_regressionTestMode) {
100✔
2586
        res = SyncRes::getRootNS(now, nullptr, 0, log);
87✔
2587
      }
87✔
2588
      if (res == 0) {
100✔
2589
        // Success, go back to the defaut period
2590
        rootUpdateTask.setPeriod(std::max(SyncRes::s_maxcachettl * 8 / 10, minRootRefreshInterval));
53✔
2591
      }
53✔
2592
      else {
47✔
2593
        // On failure, go to the middle of the remaining period (initially 80% / 8 = 10%) and shorten the interval on each
2594
        // failure by dividing the existing interval by 8, keeping the minimum interval at 10s.
2595
        // So with a 1 day period and failures we'll see a refresh attempt at 69120, 69120+11520, 69120+11520+1440, ...
2596
        rootUpdateTask.setPeriod(std::max<time_t>(rootUpdateTask.getPeriod() / 8, minRootRefreshInterval));
47✔
2597
      }
47✔
2598
    });
100✔
2599

2600
    static PeriodicTask secpollTask{"secpollTask", 3600};
225✔
2601
    static time_t t_last_secpoll;
225✔
2602
    secpollTask.runIfDue(now, [&log]() {
225✔
2603
      try {
99✔
2604
        doSecPoll(&t_last_secpoll, log);
99✔
2605
      }
99✔
2606
      catch (const std::exception& e) {
99✔
2607
        log->error(Logr::Error, e.what(), "Exception while performing security poll");
×
2608
      }
×
2609
      catch (const PDNSException& e) {
99✔
2610
        log->error(Logr::Error, e.reason, "Exception while performing security poll");
×
2611
      }
×
2612
      catch (const ImmediateServFailException& e) {
99✔
2613
        log->error(Logr::Error, e.reason, "Exception while performing security poll");
×
2614
      }
×
2615
      catch (const PolicyHitException& e) {
99✔
2616
        log->info(Logr::Error, "Policy hit while performing security poll");
×
2617
      }
×
2618
      catch (...) {
99✔
2619
        log->info(Logr::Error, "Exception while performing security poll");
×
2620
      }
×
2621
    });
99✔
2622

2623
    const time_t taInterval = std::max(1, static_cast<int>(luaconfsLocal->trustAnchorFileInfo.interval) * 3600);
225✔
2624
    static PeriodicTask trustAnchorTask{"trustAnchorTask", taInterval};
225✔
2625
    if (!trustAnchorTask.hasRun()) {
225✔
2626
      // Loading the Lua config file already "refreshed" the TAs
2627
      trustAnchorTask.updateLastRun();
99✔
2628
    }
99✔
2629
    // interval might have ben updated
2630
    trustAnchorTask.setPeriod(taInterval);
225✔
2631
    trustAnchorTask.runIfDue(now, [&luaconfsLocal, &log]() {
225✔
2632
      if (!luaconfsLocal->trustAnchorFileInfo.fname.empty() && luaconfsLocal->trustAnchorFileInfo.interval != 0) {
×
2633
        log->info(Logr::Debug, "Refreshing Trust Anchors from file");
×
2634
        try {
×
2635
          map<DNSName, dsset_t> dsAnchors;
×
2636
          if (updateTrustAnchorsFromFile(luaconfsLocal->trustAnchorFileInfo.fname, dsAnchors, log)) {
×
2637
            g_luaconfs.modify([&dsAnchors](LuaConfigItems& lci) {
×
2638
              lci.dsAnchors = dsAnchors;
×
2639
            });
×
2640
          }
×
2641
        }
×
2642
        catch (const PDNSException& pe) {
×
2643
          log->error(Logr::Error, pe.reason, "Unable to update Trust Anchors");
×
2644
        }
×
2645
      }
×
2646
    });
×
2647
  }
225✔
2648
  t_Counters.updateSnap(g_regressionTestMode);
989✔
2649
}
989✔
2650

2651
static void houseKeeping(void* /* ignored */)
2652
{
995✔
2653
  auto log = g_slog->withName("housekeeping");
995✔
2654
  static thread_local bool t_running; // houseKeeping can get suspended in secpoll, and be restarted, which makes us do duplicate work
995✔
2655

2656
  try {
995✔
2657
    if (t_running) {
995✔
2658
      return;
9✔
2659
    }
9✔
2660
    t_running = true;
986✔
2661
    houseKeepingWork(log);
986✔
2662
    t_running = false;
986✔
2663
  }
986✔
2664
  catch (const PDNSException& ae) {
995✔
2665
    t_running = false;
×
2666
    log->error(Logr::Error, ae.reason, "Fatal error in housekeeping thread");
×
2667
    throw;
×
2668
  }
×
2669
  catch (...) {
995✔
2670
    t_running = false;
×
2671
    log->info(Logr::Error, "Uncaught exception in housekeeping thread");
×
2672
    throw;
×
2673
  }
×
2674
}
995✔
2675

2676
static void runLuaMaintenance(RecThreadInfo& threadInfo, time_t& last_lua_maintenance, time_t luaMaintenanceInterval)
2677
{
24,352✔
2678
  if (t_pdl != nullptr) {
24,352✔
2679
    // lua-dns-script directive is present, call the maintenance callback if needed
2680
    if (threadInfo.isWorker()) { // either UDP of TCP worker
2,352✔
2681
      // Only on threads processing queries
2682
      if (g_now.tv_sec - last_lua_maintenance >= luaMaintenanceInterval) {
2,329✔
2683
        struct timeval start{};
390✔
2684
        Utility::gettimeofday(&start);
390✔
2685
        t_pdl->maintenance();
390✔
2686
        last_lua_maintenance = g_now.tv_sec;
390✔
2687
        struct timeval stop{};
390✔
2688
        Utility::gettimeofday(&stop);
390✔
2689
        t_Counters.at(rec::Counter::maintenanceUsec) += uSec(stop - start);
390✔
2690
        ++t_Counters.at(rec::Counter::maintenanceCalls);
390✔
2691
      }
390✔
2692
    }
2,329✔
2693
  }
2,352✔
2694
}
24,352✔
2695

2696
static void recLoop()
2697
{
963✔
2698
  time_t last_stat = 0;
963✔
2699
  time_t last_carbon = 0;
963✔
2700
  time_t last_lua_maintenance = 0;
963✔
2701
  time_t carbonInterval = ::arg().asNum("carbon-interval");
963✔
2702
  time_t luaMaintenanceInterval = ::arg().asNum("lua-maintenance-interval");
963✔
2703

2704
  auto& threadInfo = RecThreadInfo::self();
963✔
2705

2706
  while (!RecursorControlChannel::stop) {
25,300✔
2707
    try {
24,283✔
2708
      while (g_multiTasker->schedule(g_now)) {
32,890✔
2709
        ; // MTasker letting the mthreads do their thing
8,607✔
2710
      }
8,607✔
2711

2712
      // Use primes, it avoid not being scheduled in cases where the counter has a regular pattern.
2713
      // We want to call handler thread often, it gets scheduled about 2 times per second
2714
      if (((threadInfo.isHandler() || threadInfo.isTaskThread()) && s_counter % 11 == 0) || s_counter % 499 == 0) {
24,302✔
2715
        timeval start{};
1,227✔
2716
        Utility::gettimeofday(&start);
1,227✔
2717
        g_multiTasker->makeThread(houseKeeping, nullptr);
1,227✔
2718
        if (!threadInfo.isTaskThread()) {
1,227✔
2719
          timeval stop{};
852✔
2720
          Utility::gettimeofday(&stop);
852✔
2721
          t_Counters.at(rec::Counter::maintenanceUsec) += uSec(stop - start);
852✔
2722
          ++t_Counters.at(rec::Counter::maintenanceCalls);
852✔
2723
        }
852✔
2724
      }
1,227✔
2725

2726
      if (s_counter % 55 == 0) {
24,283✔
2727
        auto expired = t_fdm->getTimeouts(g_now);
552✔
2728

2729
        for (const auto& exp : expired) {
552!
2730
          auto conn = boost::any_cast<shared_ptr<TCPConnection>>(exp.second);
×
2731
          if (g_logCommonErrors) {
×
2732
            g_slogtcpin->info(Logr::Warning, "Timeout from remote TCP client", "remote", Logging::Loggable(conn->d_remote));
×
2733
          }
×
2734
          t_fdm->removeReadFD(exp.first);
×
2735
        }
×
2736
      }
552✔
2737

2738
      s_counter++;
24,283✔
2739

2740
      if (threadInfo.isHandler()) {
24,283✔
2741
        if (statsWanted || (s_statisticsInterval > 0 && (g_now.tv_sec - last_stat) >= s_statisticsInterval)) {
1,935✔
2742
          doStats();
60✔
2743
          last_stat = g_now.tv_sec;
60✔
2744
        }
60✔
2745

2746
        Utility::gettimeofday(&g_now, nullptr);
1,935✔
2747

2748
        if ((g_now.tv_sec - last_carbon) >= carbonInterval) {
1,935✔
2749
          g_multiTasker->makeThread(doCarbonDump, nullptr);
186✔
2750
          last_carbon = g_now.tv_sec;
186✔
2751
        }
186✔
2752
      }
1,935✔
2753
      runLuaMaintenance(threadInfo, last_lua_maintenance, luaMaintenanceInterval);
24,283✔
2754

2755
      auto timeoutUsec = g_multiTasker->nextWaiterDelayUsec(500000);
24,283✔
2756
      t_fdm->run(&g_now, static_cast<int>(timeoutUsec / 1000));
24,283✔
2757
      // 'run' updates g_now for us
2758
    }
24,283✔
2759
    catch (const PDNSException& pdnsException) {
24,283✔
2760
      g_rateLimitedLogger.log(g_slog->withName("runtime"), "recLoop", pdnsException);
×
2761
    }
×
2762
    catch (const std::exception& stdException) {
24,283✔
2763
      g_rateLimitedLogger.log(g_slog->withName("runtime"), "recLoop", stdException);
×
2764
    }
×
2765
    catch (...) {
24,283✔
2766
      g_rateLimitedLogger.log(g_slog->withName("runtime"), "recLoop");
×
2767
    }
×
2768
  }
24,283✔
2769
}
963✔
2770

2771
static void recursorThread()
2772
{
963✔
2773
  auto log = g_slog->withName("runtime");
963✔
2774
  t_Counters.updateSnap(true);
963✔
2775
  try {
963✔
2776
    auto& threadInfo = RecThreadInfo::self();
963✔
2777
    {
963✔
2778
      SyncRes tmp(g_now); // make sure it allocates tsstorage before we do anything, like primeHints or so..
963✔
2779
      SyncRes::setDomainMap(*g_initialDomainMap.lock());
963✔
2780
      t_allowFrom = *g_initialAllowFrom.lock();
963✔
2781
      t_allowNotifyFrom = *g_initialAllowNotifyFrom.lock();
963✔
2782
      t_allowNotifyFor = *g_initialAllowNotifyFor.lock();
963✔
2783
      t_proxyProtocolACL = g_initialProxyProtocolACL;
963✔
2784
      t_proxyProtocolExceptions = g_initialProxyProtocolExceptions;
963✔
2785
      t_udpclientsocks = std::make_unique<UDPClientSocks>();
963✔
2786
      if (g_proxyMapping) {
963✔
2787
        t_proxyMapping = make_unique<ProxyMapping>(*g_proxyMapping);
30✔
2788
      }
30✔
2789
      else {
933✔
2790
        t_proxyMapping = nullptr;
933✔
2791
      }
933✔
2792
      if (g_OTConditions) {
963✔
2793
        t_OTConditions = make_unique<OpenTelemetryTraceConditions>(*g_OTConditions);
5✔
2794
      }
5✔
2795
      else {
958✔
2796
        t_OTConditions = nullptr;
958✔
2797
      }
958✔
2798
      if (threadInfo.isHandler()) {
963✔
2799
        if (!primeHints()) {
185!
2800
          threadInfo.setExitCode(EXIT_FAILURE);
×
2801
          RecursorControlChannel::stop = true;
×
2802
          log->info(Logr::Critical, "Priming cache failed, stopping");
×
2803
        }
×
2804
        log->info(Logr::Debug, "Done priming cache with root hints");
185✔
2805
      }
185✔
2806
    }
963✔
2807

2808
    /* the listener threads handle TCP queries */
2809
    if (threadInfo.isWorker() || threadInfo.isListener()) {
963✔
2810
      try {
593✔
2811
        if (!::arg()["lua-dns-script"].empty()) {
593✔
2812
          t_pdl = std::make_shared<RecursorLua4>();
125✔
2813
          t_pdl->loadFile(::arg()["lua-dns-script"]);
125✔
2814
          log->info(Logr::Warning, "Loading Lua script from file", "name", Logging::Loggable(::arg()["lua-dns-script"]));
125✔
2815
        }
125✔
2816
      }
593✔
2817
      catch (std::exception& e) {
593✔
2818
        log->error(Logr::Error, e.what(), "Failed to load Lua script from file", "name", Logging::Loggable(::arg()["lua-dns-script"]));
×
2819
        _exit(99);
×
2820
      }
×
2821
    }
593✔
2822

2823
    if (unsigned int ringsize = ::arg().asNum("stats-ringbuffer-entries") / RecThreadInfo::numUDPWorkers(); ringsize != 0) {
963!
2824
      t_remotes = std::make_unique<addrringbuf_t>();
963✔
2825
      if (RecThreadInfo::weDistributeQueries()) {
963✔
2826
        t_remotes->set_capacity(::arg().asNum("stats-ringbuffer-entries") / RecThreadInfo::numDistributors());
111✔
2827
      }
111✔
2828
      else {
852✔
2829
        t_remotes->set_capacity(ringsize);
852✔
2830
      }
852✔
2831
      t_servfailremotes = std::make_unique<addrringbuf_t>();
963✔
2832
      t_servfailremotes->set_capacity(ringsize);
963✔
2833
      t_bogusremotes = std::make_unique<addrringbuf_t>();
963✔
2834
      t_bogusremotes->set_capacity(ringsize);
963✔
2835
      t_largeanswerremotes = std::make_unique<addrringbuf_t>();
963✔
2836
      t_largeanswerremotes->set_capacity(ringsize);
963✔
2837
      t_timeouts = std::make_unique<addrringbuf_t>();
963✔
2838
      t_timeouts->set_capacity(ringsize);
963✔
2839

2840
      t_queryring = std::make_unique<boost::circular_buffer<pair<DNSName, uint16_t>>>();
963✔
2841
      t_queryring->set_capacity(ringsize);
963✔
2842
      t_servfailqueryring = std::make_unique<boost::circular_buffer<pair<DNSName, uint16_t>>>();
963✔
2843
      t_servfailqueryring->set_capacity(ringsize);
963✔
2844
      t_bogusqueryring = std::make_unique<boost::circular_buffer<pair<DNSName, uint16_t>>>();
963✔
2845
      t_bogusqueryring->set_capacity(ringsize);
963✔
2846
    }
963✔
2847
    g_multiTasker = std::make_unique<MT_t>(::arg().asNum("stack-size"), ::arg().asNum("stack-cache-size"));
963✔
2848
    threadInfo.setMT(g_multiTasker.get());
963✔
2849

2850
    {
963✔
2851
      /* start protobuf export threads if needed, don't keep a ref to lua config around */
2852
      auto luaconfsLocal = g_luaconfs.getLocal();
963✔
2853
      checkProtobufExport(luaconfsLocal);
963✔
2854
      checkOutgoingProtobufExport(luaconfsLocal);
963✔
2855
#ifdef HAVE_FSTRM
963✔
2856
      checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
963✔
2857
      checkFrameStreamExport(luaconfsLocal, luaconfsLocal->nodFrameStreamExportConfig, t_nodFrameStreamServersInfo);
963✔
2858
#endif
963✔
2859
      for (const auto& rpz : luaconfsLocal->rpzs) {
963✔
2860
        string name = rpz.polName.empty() ? (rpz.zoneXFRParams.primaries.empty() ? "rpzFile" : rpz.zoneXFRParams.name) : rpz.polName;
85!
2861
        t_Counters.at(rec::PolicyNameHits::policyName).counts[name] = 0;
85✔
2862
      }
85✔
2863
    }
963✔
2864

2865
    t_fdm = unique_ptr<FDMultiplexer>(getMultiplexer(log));
963✔
2866
    t_fdm->addReadFD(threadInfo.getPipes().readToThread, handlePipeRequest);
963✔
2867

2868
    if (threadInfo.isHandler()) {
963✔
2869
      log->info(Logr::Info, "Enabled multiplexer", "name", Logging::Loggable(t_fdm->getName()));
185✔
2870
    }
185✔
2871
    else {
778✔
2872
      t_fdm->addReadFD(threadInfo.getPipes().readQueriesToThread, handlePipeRequest);
778✔
2873

2874
      if (threadInfo.isListener()) {
778✔
2875
        if (g_reusePort) {
538✔
2876
          /* then every listener has its own FDs */
2877
          for (const auto& deferred : threadInfo.getDeferredAdds()) {
513✔
2878
            t_fdm->addReadFD(deferred.first, deferred.second);
513✔
2879
          }
513✔
2880
        }
506✔
2881
        else {
32✔
2882
          /* otherwise all listeners are listening on the same ones */
2883
          for (const auto& deferred : threadInfo.isTCPListener() ? s_deferredTCPadds : s_deferredUDPadds) {
62✔
2884
            t_fdm->addReadFD(deferred.first, deferred.second);
60✔
2885
          }
60✔
2886
        }
32✔
2887
      }
538✔
2888
    }
778✔
2889

2890
    if (threadInfo.isHandler()) {
963✔
2891
      t_fdm->addReadFD(g_rcc.getDescriptor(), handleRCC); // control channel
185✔
2892
    }
185✔
2893

2894
#ifdef HAVE_SYSTEMD
963✔
2895
    if (threadInfo.isHandler()) {
963✔
2896
      // There is a race, as some threads might not be ready yet to do work.
2897
      // To solve that, threads should notify RecThreadInfo they are done initializing.
2898
      // But we lack a mechanism for that at this point in time.
2899
      sd_notify(0, "READY=1");
185✔
2900
    }
185✔
2901
#endif
963✔
2902

2903
    recLoop();
963✔
2904
  }
963✔
2905
  catch (const PDNSException& ae) {
963✔
2906
    log->error(Logr::Error, ae.reason, "Exception in RecursorThread", "exception", Logging::Loggable("PDNSException"));
×
2907
  }
×
2908
  catch (const std::exception& e) {
963✔
2909
    log->error(Logr::Error, e.what(), "Exception in RecursorThread", "exception", Logging::Loggable("std::exception"));
×
2910
  }
×
2911
  catch (...) {
963✔
2912
    log->info(Logr::Error, "Exception in RecursorThread");
×
2913
  }
×
2914
}
963✔
2915

2916
static pair<int, bool> doYamlConfig(int argc, char* argv[], const pdns::rust::settings::rec::Recursorsettings& settings) // NOLINT: Posix API
2917
{
18✔
2918
  if (!::arg().mustDo("config")) {
18!
2919
    return {0, false};
18✔
2920
  }
18✔
2921
  const string config = ::arg()["config"];
×
2922
  if (config == "diff" || config.empty()) {
×
2923
    ::arg().parse(argc, argv);
×
2924
    ProxyMapping proxyMapping;
×
2925
    LuaConfigItems lci;
×
NEW
2926
    OpenTelemetryTraceConditions conditions;
×
NEW
2927
    pdns::settings::rec::fromBridgeStructToLuaConfig(settings, lci, proxyMapping, conditions);
×
2928
    auto yaml = settings.to_yaml_string();
×
2929
    cout << yaml << endl;
×
2930
  }
×
2931
  else if (config == "default") {
×
2932
    auto yaml = pdns::settings::rec::defaultsToYaml();
×
2933
    cout << yaml << endl;
×
2934
  }
×
2935
  else if (config == "check") {
×
2936
    // Kinda redundant, if we came here we already read and checked the config....x
2937
  }
×
2938
  return {0, true};
×
2939
}
18✔
2940

2941
static pair<int, bool> doConfig(Logr::log_t startupLog, const string& configname, int argc, char* argv[]) // NOLINT: Posix API
2942
{
167✔
2943
  if (::arg().mustDo("config")) {
167!
2944
    string config = ::arg()["config"];
×
2945
    if (config == "check") {
×
2946
      try {
×
2947
        if (!::arg().file(configname)) {
×
2948
          startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname));
×
2949
          return {1, true};
×
2950
        }
×
2951
        ::arg().parse(argc, argv);
×
2952
        return {0, true};
×
2953
      }
×
2954
      catch (const ArgException& argException) {
×
2955
        startupLog->error("Cannot parse configuration", "Unable to parse configuration file", "config_file", Logging::Loggable(configname), "reason", Logging::Loggable(argException.reason));
×
2956
        return {1, true};
×
2957
      }
×
2958
    }
×
2959
    else if (config == "default" || config.empty()) {
×
2960
      auto yaml = pdns::settings::rec::defaultsToYaml();
×
2961
      cout << yaml << endl;
×
2962
    }
×
2963
    else if (config == "diff") {
×
2964
      if (!::arg().laxFile(configname)) {
×
2965
        startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname));
×
2966
        return {1, true};
×
2967
      }
×
2968
      ::arg().laxParse(argc, argv);
×
2969
      cout << ::arg().configstring(true, false);
×
2970
    }
×
2971
    else {
×
2972
      if (!::arg().laxFile(configname)) {
×
2973
        startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname));
×
2974
        return {1, true};
×
2975
      }
×
2976
      ::arg().laxParse(argc, argv);
×
2977
      cout << ::arg().configstring(true, true);
×
2978
    }
×
2979
    return {0, true};
×
2980
  }
×
2981
  return {0, false};
167✔
2982
}
167✔
2983

2984
LockGuarded<pdns::rust::settings::rec::Recursorsettings> g_yamlStruct;
2985

2986
static void runStartStopLua(bool start, Logr::log_t log)
2987
{
370✔
2988
  auto settings = g_yamlStruct.lock();
370✔
2989
  const auto& script = settings->recursor.lua_start_stop_script;
370✔
2990
  if (script.empty()) {
370!
2991
    return;
370✔
2992
  }
370✔
2993
  auto lua = std::make_shared<RecursorLua4>();
×
2994
  lua->runStartStopFunction(std::string(script), start, log);
×
2995
}
×
2996

2997
static void handleRuntimeDefaults(Logr::log_t log)
2998
{
185✔
2999
#ifdef HAVE_FIBER_SANITIZER
185✔
3000
  // Asan needs more stack
3001
  if (::arg().asNum("stack-size") == 200000) { // the default in table.py
185!
3002
    ::arg().set("stack-size", "stack size per mthread") = "600000";
185✔
3003
  }
185✔
3004
#endif
185✔
3005

3006
  const string RUNTIME = "*runtime determined*";
185✔
3007
  if (::arg()["version-string"] == RUNTIME) { // i.e. not set explicitly
185✔
3008
    ::arg().set("version-string") = fullVersionString();
184✔
3009
  }
184✔
3010

3011
  if (::arg()["server-id"] == RUNTIME) { // i.e. not set explicitly
185✔
3012
    auto myHostname = getHostname();
184✔
3013
    if (!myHostname.has_value()) {
184!
3014
      log->info(Logr::Warning, "Unable to get the hostname, NSID and id.server values will be empty");
×
3015
    }
×
3016
    ::arg().set("server-id") = myHostname.has_value() ? *myHostname : "";
184!
3017
  }
184✔
3018

3019
  if (::arg()["socket-dir"].empty()) {
185!
3020
    auto* runtimeDir = getenv("RUNTIME_DIRECTORY"); // NOLINT(concurrency-mt-unsafe,cppcoreguidelines-pro-type-vararg)
×
3021
    if (runtimeDir != nullptr) {
×
3022
      ::arg().set("socket-dir") = runtimeDir;
×
3023
    }
×
3024
  }
×
3025

3026
  if (::arg()["socket-dir"].empty()) {
185!
3027
    if (::arg()["chroot"].empty()) {
×
3028
      ::arg().set("socket-dir") = std::string(LOCALSTATEDIR) + "/pdns-recursor";
×
3029
    }
×
3030
    else {
×
3031
      ::arg().set("socket-dir") = "/";
×
3032
    }
×
3033
  }
×
3034

3035
  if (::arg().asNum("threads") == 1) {
185✔
3036
    if (::arg().mustDo("pdns-distributes-queries")) {
3!
3037
      log->info(Logr::Warning, "Only one thread, no need to distribute queries ourselves");
3✔
3038
      ::arg().set("pdns-distributes-queries") = "no";
3✔
3039
    }
3✔
3040
  }
3✔
3041

3042
  if (::arg().mustDo("pdns-distributes-queries") && ::arg().asNum("distributor-threads") == 0) {
185!
3043
    log->info(Logr::Warning, "Asked to run with pdns-distributes-queries set but no distributor threads, raising to 1");
14✔
3044
    ::arg().set("distributor-threads") = "1";
14✔
3045
  }
14✔
3046

3047
  if (!::arg().mustDo("pdns-distributes-queries") && ::arg().asNum("distributor-threads") > 0) {
185!
3048
    log->info(Logr::Warning, "Not distributing queries, setting distributor threads to 0");
×
3049
    ::arg().set("distributor-threads") = "0";
×
3050
  }
×
3051
}
185✔
3052

3053
static void setupLogging(const string& logname)
3054
{
185✔
3055
  if (logname == "systemd-journal") {
185!
3056
#ifdef HAVE_SYSTEMD
×
3057
    if (int fileDesc = sd_journal_stream_fd("pdns-recusor", LOG_DEBUG, 0); fileDesc >= 0) {
×
3058
      g_slog = Logging::Logger::create(loggerSDBackend);
×
3059
      close(fileDesc);
×
3060
    }
×
3061
#endif
×
3062
    if (g_slog == nullptr) {
×
3063
      cerr << "Requested structured logging to systemd-journal, but it is not available" << endl;
×
3064
    }
×
3065
  }
×
3066
  else if (logname == "json") {
185!
3067
    g_slog = Logging::Logger::create(loggerJSONBackend);
×
3068
    if (g_slog == nullptr) {
×
3069
      cerr << "JSON logging requested but it is not available" << endl;
×
3070
    }
×
3071
  }
×
3072

3073
  if (g_slog == nullptr) {
185!
3074
    g_slog = Logging::Logger::create(loggerBackend);
185✔
3075
  }
185✔
3076
}
185✔
3077

3078
DoneRunning g_doneRunning;
3079

3080
int main(int argc, char** argv)
3081
{
187✔
3082
  g_argc = argc;
187✔
3083
  g_argv = argv;
187✔
3084
  versionSetProduct(ProductRecursor);
187✔
3085
  reportAllTypes();
187✔
3086

3087
  int ret = EXIT_SUCCESS;
187✔
3088

3089
  try {
187✔
3090
    pdns::settings::rec::defineOldStyleSettings();
187✔
3091
    ::arg().setDefaults();
187✔
3092
    g_log.toConsole(Logger::Info);
187✔
3093
    ::arg().laxParse(argc, argv); // do a lax parse
187✔
3094

3095
    if (::arg().mustDo("version")) {
187✔
3096
      cout << getProductVersion();
2✔
3097
      cout << getBuildConfiguration();
2✔
3098
      return 0;
2✔
3099
    }
2✔
3100
    if (::arg().mustDo("help")) {
185!
3101
      cout << "syntax:" << endl
×
3102
           << endl;
×
3103
      cout << ::arg().helpstring(::arg()["help"]) << endl;
×
3104
      return 0;
×
3105
    }
×
3106

3107
    // Pick up options given on command line to setup logging asap.
3108
    g_quiet = ::arg().mustDo("quiet");
185✔
3109
    s_logUrgency = (Logger::Urgency)::arg().asNum("loglevel");
185✔
3110
    s_structured_logger_backend = ::arg()["structured-logging-backend"];
185✔
3111

3112
    if (!g_quiet && s_logUrgency < Logger::Info) { // Logger::Info=6, Logger::Debug=7
185!
3113
      s_logUrgency = Logger::Info; // if you do --quiet=no, you need Info to also see the query log
×
3114
    }
×
3115
    g_log.setLoglevel(s_logUrgency);
185✔
3116
    g_log.toConsole(s_logUrgency);
185✔
3117

3118
    for (const string& line : getProductVersionLines()) {
555✔
3119
      g_log << Logger::Info << line << endl;
555✔
3120
    }
555✔
3121
    if (!::arg().mustDo("structured-logging")) {
185!
3122
      g_log << Logger::Error << "Disabling structured logging is not supported anymore" << endl;
×
3123
    }
×
3124

3125
    g_yamlSettings = false;
185✔
3126
    string configname = ::arg()["config-dir"] + "/recursor";
185✔
3127
    if (!::arg()["config-name"].empty()) {
185!
3128
      configname = ::arg()["config-dir"] + "/recursor-" + ::arg()["config-name"];
×
3129
      g_programname += "-" + ::arg()["config-name"];
×
3130
    }
×
3131
    cleanSlashes(configname);
185✔
3132

3133
    if (!::arg().getCommands().empty()) {
185!
3134
      cerr << "Fatal: non-option";
×
3135
      if (::arg().getCommands().size() > 1) {
×
3136
        cerr << "s";
×
3137
      }
×
3138
      cerr << " (";
×
3139
      bool first = true;
×
3140
      for (const auto& command : ::arg().getCommands()) {
×
3141
        if (!first) {
×
3142
          cerr << ", ";
×
3143
        }
×
3144
        first = false;
×
3145
        cerr << command;
×
3146
      }
×
3147
      cerr << ") on the command line, perhaps a '--setting=123' statement missed the '='?" << endl;
×
3148
      return 99;
×
3149
    }
×
3150

3151
    setupLogging(s_structured_logger_backend);
185✔
3152

3153
    // Missing: a mechanism to call setVerbosity(x)
3154
    auto startupLog = g_slog->withName("config");
185✔
3155
    g_slogtcpin = g_slog->withName("in")->withValues("proto", Logging::Loggable("tcp"));
185✔
3156
    g_slogudpin = g_slog->withName("in")->withValues("proto", Logging::Loggable("udp"));
185✔
3157
    g_slogout = g_slog->withName("out");
185✔
3158

3159
    ::arg().setSLog(startupLog);
185✔
3160

3161
    string yamlconfigname;
185✔
3162
    pdns::rust::settings::rec::Recursorsettings settings;
185✔
3163
    pdns::settings::rec::YamlSettingsStatus yamlstatus{};
185✔
3164

3165
    for (const string suffix : {".yml", ".conf"}) {
352✔
3166
      yamlconfigname = configname + suffix;
352✔
3167
      yamlstatus = pdns::settings::rec::tryReadYAML(yamlconfigname, true, g_yamlSettings, g_luaSettingsInYAML, settings, startupLog);
352✔
3168
      if (yamlstatus == pdns::settings::rec::YamlSettingsStatus::OK) {
352✔
3169
        g_yamlSettingsSuffix = suffix;
18✔
3170
        break;
18✔
3171
      }
18✔
3172
      if (suffix == ".yml" && yamlstatus == pdns::settings::rec::PresentButFailed) {
334!
3173
        return 1;
×
3174
      }
×
3175
    }
334✔
3176

3177
    if (g_yamlSettings) {
185✔
3178
      bool mustExit = false;
18✔
3179
      std::tie(ret, mustExit) = doYamlConfig(argc, argv, settings);
18✔
3180
      if (ret != 0 || mustExit) {
18!
3181
        return ret;
×
3182
      }
×
3183
    }
18✔
3184
    if (yamlstatus == pdns::settings::rec::YamlSettingsStatus::OK) {
185✔
3185
      auto lock = g_yamlStruct.lock();
18✔
3186
      *lock = std::move(settings);
18✔
3187
    }
18✔
3188
    else {
167✔
3189
      configname += ".conf";
167✔
3190
      startupLog->info(Logr::Warning, "Trying to read YAML from .yml or .conf failed, falling back to old-style config read", "configname", Logging::Loggable(configname));
167✔
3191
      bool mustExit = false;
167✔
3192
      std::tie(ret, mustExit) = doConfig(startupLog, configname, argc, argv);
167✔
3193
      if (ret != 0 || mustExit) {
167!
3194
        return ret;
×
3195
      }
×
3196
      if (!::arg().file(configname)) {
167✔
3197
        startupLog->error("No such file", "Unable to open configuration file", "config_file", Logging::Loggable(configname));
15✔
3198
      }
15✔
3199
      else {
152✔
3200
        if (!::arg().mustDo("enable-old-settings")) {
152!
3201
          startupLog->info(Logr::Error, "Old-style settings syntax not supported by default anymore", "configname", Logging::Loggable(configname));
×
3202
          startupLog->info(Logr::Error, "Convert to YAML settings. If not feasible use --enable-old-settings on the command line. This option will be removed in a future release.");
×
3203
          return EXIT_FAILURE;
×
3204
        }
×
3205
        startupLog->info(Logr::Warning, "Convert to YAML settings. The --enable-old-settings option on the command line will be removed in a future release.");
152✔
3206
      }
152✔
3207
    }
167✔
3208

3209
    // Reparse, now with config file as well, both for old-style as for YAML settings
3210
    ::arg().parse(argc, argv);
185✔
3211

3212
    g_quiet = ::arg().mustDo("quiet");
185✔
3213
    s_logUrgency = (Logger::Urgency)::arg().asNum("loglevel");
185✔
3214

3215
    if (s_logUrgency < Logger::Error) {
185!
3216
      s_logUrgency = Logger::Error;
×
3217
    }
×
3218
    if (!g_quiet && s_logUrgency < Logger::Info) { // Logger::Info=6, Logger::Debug=7
185!
3219
      s_logUrgency = Logger::Info; // if you do --quiet=no, you need Info to also see the query log
×
3220
    }
×
3221
    g_log.setLoglevel(s_logUrgency);
185✔
3222
    g_log.toConsole(s_logUrgency);
185✔
3223

3224
    if (!::arg()["chroot"].empty() && !::arg()["api-config-dir"].empty()) {
185!
3225
      startupLog->info(Logr::Error, "Cannot use chroot and enable the API at the same time");
×
3226
      return EXIT_FAILURE;
×
3227
    }
×
3228

3229
    handleRuntimeDefaults(startupLog);
185✔
3230

3231
    if (auto ttl = ::arg().asNum("system-resolver-ttl"); ttl != 0) {
185✔
3232
      time_t interval = ttl;
2✔
3233
      if (::arg().asNum("system-resolver-interval") != 0) {
2!
3234
        interval = ::arg().asNum("system-resolver-interval");
×
3235
      }
×
3236
      bool selfResolveCheck = ::arg().mustDo("system-resolver-self-resolve-check");
2✔
3237
      // Cannot use SyncRes::s_serverID, it is not set yet
3238
      pdns::RecResolve::setInstanceParameters(arg()["server-id"], ttl, interval, selfResolveCheck, []() { reloadZoneConfiguration(g_yamlSettings); });
2✔
3239
    }
2✔
3240

3241
    g_recCache = std::make_unique<MemRecursorCache>(::arg().asNum("record-cache-shards"));
185✔
3242
    g_negCache = std::make_unique<NegCache>(::arg().asNum("record-cache-shards") / 8);
185✔
3243
    if (!::arg().mustDo("disable-packetcache")) {
185✔
3244
      g_maxPacketCacheEntries = ::arg().asNum("max-packetcache-entries");
177✔
3245
      g_packetCache = std::make_unique<RecursorPacketCache>(g_maxPacketCacheEntries, ::arg().asNum("packetcache-shards"));
177✔
3246
    }
177✔
3247

3248
    ret = serviceMain(startupLog);
185✔
3249
    {
185✔
3250
      std::scoped_lock lock(g_doneRunning.mutex);
185✔
3251
      g_doneRunning.done = true;
185✔
3252
      g_doneRunning.condVar.notify_one();
185✔
3253
    }
185✔
3254
    RecThreadInfo::joinThread0();
185✔
3255
  }
185✔
3256
  catch (const PDNSException& ae) {
187✔
3257
    g_slog->withName("config")->error(Logr::Critical, ae.reason, "Fatal error", "exception", Logging::Loggable("PDNSException"));
×
3258
    ret = EXIT_FAILURE;
×
3259
  }
×
3260
  catch (const std::exception& e) {
187✔
3261
    g_slog->withName("config")->error(Logr::Critical, e.what(), "Fatal error", "exception", Logging::Loggable("std::exception"));
×
3262
    ret = EXIT_FAILURE;
×
3263
  }
×
3264
  catch (...) {
187✔
3265
    g_slog->withName("config")->info(Logr::Critical, "Fatal error");
×
3266
    ret = EXIT_FAILURE;
×
3267
  }
×
3268

3269
  return ret;
185✔
3270
}
187✔
3271

3272
static RecursorControlChannel::Answer* doReloadLuaScript()
3273
{
×
3274
  string fname = ::arg()["lua-dns-script"];
×
3275
  auto log = g_slog->withName("runtime")->withValues("name", Logging::Loggable(fname));
×
3276
  try {
×
3277
    if (fname.empty()) {
×
3278
      t_pdl.reset();
×
3279
      log->info(Logr::Info, "Unloaded current lua script");
×
3280
      return new RecursorControlChannel::Answer{0, string("unloaded\n")};
×
3281
    }
×
3282

3283
    t_pdl = std::make_shared<RecursorLua4>();
×
3284
    try {
×
3285
      t_pdl->loadFile(fname);
×
3286
    }
×
3287
    catch (std::runtime_error& ex) {
×
3288
      string msg = std::to_string(RecThreadInfo::thread_local_id()) + " Retaining current script, could not read '" + fname + "': " + ex.what();
×
3289
      log->error(Logr::Error, ex.what(), "Retaining current script, could not read new script");
×
3290
      return new RecursorControlChannel::Answer{1, msg + "\n"};
×
3291
    }
×
3292
  }
×
3293
  catch (std::exception& e) {
×
3294
    log->error(Logr::Error, e.what(), "Retaining current script, error in new script");
×
3295
    return new RecursorControlChannel::Answer{1, string("retaining current script, error from '" + fname + "': " + e.what() + "\n")};
×
3296
  }
×
3297

3298
  log->info(Logr::Warning, "(Re)loaded lua script");
×
3299
  return new RecursorControlChannel::Answer{0, string("(re)loaded '" + fname + "'\n")};
×
3300
}
×
3301

3302
RecursorControlChannel::Answer doQueueReloadLuaScript(vector<string>::const_iterator begin, vector<string>::const_iterator end)
3303
{
×
3304
  if (begin != end) {
×
3305
    ::arg().set("lua-dns-script") = *begin;
×
3306
  }
×
3307

3308
  return broadcastAccFunction<RecursorControlChannel::Answer>(doReloadLuaScript);
×
3309
}
×
3310

3311
static string* pleaseUseNewTraceRegex(const std::string& newRegex, int file)
3312
{
×
3313
  try {
×
3314
    if (newRegex.empty()) {
×
3315
      t_traceRegex.reset();
×
3316
      t_tracefd = FDWrapper();
×
3317
      return new string("unset\n");
×
3318
    }
×
3319
    if (file == -1) {
×
3320
      return new string("could not dup file\n");
×
3321
    }
×
3322
    t_traceRegex = std::make_shared<Regex>(newRegex);
×
3323
    t_tracefd = file;
×
3324
    return new string("ok\n"); // NOLINT(cppcoreguidelines-owning-memory): it's the API
×
3325
  }
×
3326
  catch (const PDNSException& ae) {
×
3327
    return new string(ae.reason + "\n"); // NOLINT(cppcoreguidelines-owning-memory): it's the API
×
3328
  }
×
3329
}
×
3330

3331
string doTraceRegex(FDWrapper file, vector<string>::const_iterator begin, vector<string>::const_iterator end)
3332
{
×
3333
  int fileno = dup(file);
×
3334
  // Potential dup failure handled in pleaseUseNewTraceRegex()
3335
  return broadcastAccFunction<string>([=] { return pleaseUseNewTraceRegex(begin != end ? *begin : "", fileno); });
×
3336
}
×
3337

3338
WipeCacheResult wipeCaches(const DNSName& canon, bool subtree, uint16_t qtype)
3339
{
756✔
3340
  WipeCacheResult res;
756✔
3341

3342
  try {
756✔
3343
    res.record_count = static_cast<int>(g_recCache->doWipeCache(canon, subtree, qtype));
756✔
3344
    res.negative_record_count = static_cast<int>(g_negCache->wipe(canon, subtree));
756✔
3345
    if (g_aggressiveNSECCache) {
756✔
3346
      g_aggressiveNSECCache->removeZoneInfo(canon, subtree);
588✔
3347
    }
588✔
3348
    /* we need to do the packet cache last otherwise we could re-cache something that was just cleared:
3349
     - remove entry from the packet cache
3350
     - query comes in for the removed entry
3351
     - existing entry found in positive, negative or aggressive NSEC cache
3352
     - resulting packet inserted into the packet cache
3353
     - entry removed from the positive, negative or aggressive NSEC3 cache
3354
    */
3355
    if (g_packetCache) {
756✔
3356
      res.packet_count = static_cast<int>(g_packetCache->doWipePacketCache(canon, qtype, subtree));
405✔
3357
    }
405✔
3358
  }
756✔
3359
  catch (const std::exception& e) {
756✔
3360
    auto log = g_slog->withName("runtime");
×
3361
    log->error(Logr::Warning, e.what(), "Wipecache failed");
×
3362
  }
×
3363

3364
  return res;
756✔
3365
}
756✔
3366

3367
void startLuaConfigDelayedThreads(const LuaConfigItems& luaConfig, uint64_t generation)
3368
{
193✔
3369
  for (const auto& rpzPrimary : luaConfig.rpzs) {
193✔
3370
    if (rpzPrimary.zoneXFRParams.primaries.empty()) {
17✔
3371
      continue;
15✔
3372
    }
15✔
3373
    try {
2✔
3374
      // RPZIXTracker uses call by value for its args. That is essential, since we want copies so
3375
      // that RPZIXFRTracker gets values with the proper lifetime.
3376
      std::thread theThread(RPZIXFRTracker, rpzPrimary, generation);
2✔
3377
      theThread.detach();
2✔
3378
    }
2✔
3379
    catch (const std::exception& e) {
2✔
3380
      g_slog->withName("rpz")->error(Logr::Error, e.what(), "Exception starting RPZIXFRTracker thread", "exception", Logging::Loggable("std::exception"));
×
3381
      exit(1); // NOLINT(concurrency-mt-unsafe)
×
3382
    }
×
3383
    catch (const PDNSException& e) {
2✔
3384
      g_slog->withName("rpz")->error(Logr::Error, e.reason, "Exception starting RPZIXFRTracker thread", "exception", Logging::Loggable("PDNSException"));
×
3385
      exit(1); // NOLINT(concurrency-mt-unsafe)
×
3386
    }
×
3387
  }
2✔
3388
  for (const auto& fcz : luaConfig.catalogzones) {
193✔
3389
    if (fcz.d_params.primaries.empty()) {
1!
3390
      continue;
×
3391
    }
×
3392
    try {
1✔
3393
      // ZoneXFRTracker uses call by value for its args. That is essential, since we want copies so
3394
      // that ZoneXFRTracker gets values with the proper lifetime.
3395
      std::thread theThread(FWCatZoneXFR::zoneXFRTracker, fcz.d_params, generation);
1✔
3396
      theThread.detach();
1✔
3397
    }
1✔
3398
    catch (const std::exception& e) {
1✔
3399
      g_slog->withName("zone")->error(Logr::Error, e.what(), "Exception starting ZoneXFRTracker thread", "exception", Logging::Loggable("std::exception"));
×
3400
      exit(1); // NOLINT(concurrency-mt-unsafe)
×
3401
    }
×
3402
    catch (const PDNSException& e) {
1✔
3403
      g_slog->withName("zone")->error(Logr::Error, e.reason, "Exception starting ZoneXFRTracker thread", "exception", Logging::Loggable("PDNSException"));
×
3404
      exit(1); // NOLINT(concurrency-mt-unsafe)
×
3405
    }
×
3406
  }
1✔
3407
}
193✔
3408

3409
static void* pleaseInitPolCounts(const string& name)
3410
{
17✔
3411
  if (t_Counters.at(rec::PolicyNameHits::policyName).counts.count(name) == 0) {
17!
3412
    t_Counters.at(rec::PolicyNameHits::policyName).counts[name] = 0;
17✔
3413
  }
17✔
3414
  return nullptr;
17✔
3415
}
17✔
3416

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

3421
  zone->setName(params.polName.empty() ? "rpzFile" : params.polName);
15!
3422
  try {
15✔
3423
    log->info(Logr::Info, "Loading RPZ from file");
15✔
3424
    loadRPZFromFile(params.zoneXFRParams.name, zone, params.defpol, params.defpolOverrideLocal, params.maxTTL);
15✔
3425
    log->info(Logr::Info, "Done loading RPZ from file");
15✔
3426
  }
15✔
3427
  catch (const std::exception& e) {
15✔
3428
    log->error(Logr::Error, e.what(), "Exception while loading RPZ zone from file");
×
3429
    zone->clear();
×
3430
    return false;
×
3431
  }
×
3432
  return true;
15✔
3433
}
15✔
3434

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

3439
  if (!params.seedFileName.empty()) {
2!
3440
    log->info(Logr::Info, "Pre-loading RPZ zone from seed file");
×
3441
    try {
×
3442
      params.zoneXFRParams.soaRecordContent = loadRPZFromFile(params.seedFileName, zone, params.defpol, params.defpolOverrideLocal, params.maxTTL);
×
3443

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

3448
      if (params.zoneXFRParams.soaRecordContent == nullptr) {
×
3449
        throw PDNSException("The RPZ zone " + params.zoneXFRParams.name + " loaded from the seed file (" + zone->getDomain().toString() + ") has no SOA record");
×
3450
      }
×
3451
    }
×
3452
    catch (const PDNSException& e) {
×
3453
      log->error(Logr::Warning, e.reason, "Exception while pre-loading RPZ zone", "exception", Logging::Loggable("PDNSException"));
×
3454
      zone->clear();
×
3455
    }
×
3456
    catch (const std::exception& e) {
×
3457
      log->error(Logr::Warning, e.what(), "Exception while pre-loading RPZ zone", "exception", Logging::Loggable("std::exception"));
×
3458
      zone->clear();
×
3459
    }
×
3460
  }
×
3461
}
2✔
3462

3463
static void activateRPZs(LuaConfigItems& lci)
3464
{
175✔
3465
  for (auto& params : lci.rpzs) {
175✔
3466
    auto zone = std::make_shared<DNSFilterEngine::Zone>();
17✔
3467
    if (params.zoneXFRParams.zoneSizeHint != 0) {
17!
3468
      zone->reserve(params.zoneXFRParams.zoneSizeHint);
×
3469
    }
×
3470
    if (!params.tags.empty()) {
17✔
3471
      std::unordered_set<std::string> tags;
1✔
3472
      for (const auto& tag : params.tags) {
2✔
3473
        tags.emplace(tag);
2✔
3474
      }
2✔
3475
      zone->setTags(tags);
1✔
3476
    }
1✔
3477
    zone->setPolicyOverridesGettag(params.defpolOverrideLocal);
17✔
3478
    if (params.extendedErrorCode != std::numeric_limits<uint32_t>::max()) {
17✔
3479
      zone->setExtendedErrorCode(params.extendedErrorCode);
2✔
3480
      if (!params.extendedErrorExtra.empty()) {
2!
3481
        zone->setExtendedErrorExtra(params.extendedErrorExtra);
2✔
3482
      }
2✔
3483
    }
2✔
3484
    zone->setIncludeSOA(params.includeSOA);
17✔
3485
    zone->setIgnoreDuplicates(params.ignoreDuplicates);
17✔
3486

3487
    if (params.zoneXFRParams.primaries.empty()) {
17✔
3488
      if (activateRPZFile(params, lci, zone)) {
15!
3489
        lci.dfe.addZone(zone);
15✔
3490
      }
15✔
3491
    }
15✔
3492
    else {
2✔
3493
      DNSName domain(params.zoneXFRParams.name);
2✔
3494
      zone->setDomain(domain);
2✔
3495
      zone->setName(params.polName.empty() ? params.zoneXFRParams.name : params.polName);
2!
3496
      params.zoneXFRParams.zoneIdx = lci.dfe.addZone(zone);
2✔
3497
      activateRPZPrimary(params, lci, zone, domain);
2✔
3498
    }
2✔
3499
    broadcastFunction([name = zone->getName()] { return pleaseInitPolCounts(name); });
17✔
3500
  }
17✔
3501
}
175✔
3502

3503
static void activateForwardingCatalogZones(LuaConfigItems& lci)
3504
{
175✔
3505
  size_t idx = 0;
175✔
3506
  for (auto& fcz : lci.catalogzones) {
175✔
3507

3508
    auto& params = fcz.d_params;
1✔
3509
    params.zoneIdx = idx++;
1✔
3510
    auto zone = std::make_shared<CatalogZone>();
1✔
3511
    // zoneSizeHint ignored
3512
    zone->setName(DNSName(params.name));
1✔
3513
    fcz.d_catz = std::move(zone);
1✔
3514
  }
1✔
3515
}
175✔
3516

3517
void activateLuaConfig(LuaConfigItems& lci)
3518
{
175✔
3519
  if (!lci.trustAnchorFileInfo.fname.empty()) {
175✔
3520
    warnIfDNSSECDisabled("Warning: reading Trust Anchors from file, but dnssec is set to 'off'!");
1✔
3521
    updateTrustAnchorsFromFile(lci.trustAnchorFileInfo.fname, lci.dsAnchors, lci.d_slog);
1✔
3522
  }
1✔
3523
  if (lci.dsAnchors.size() > rootDSs.size()) {
175!
3524
    warnIfDNSSECDisabled("Warning: adding Trust Anchor for DNSSEC, but dnssec is set to 'off'!");
×
3525
  }
×
3526
  if (!lci.negAnchors.empty()) {
175✔
3527
    warnIfDNSSECDisabled("Warning: adding Negative Trust Anchor for DNSSEC, but dnssec is set to 'off'!");
2✔
3528
  }
2✔
3529
  activateRPZs(lci);
175✔
3530
  activateForwardingCatalogZones(lci);
175✔
3531
  g_luaconfs.setState(lci);
175✔
3532
}
175✔
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