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

PowerDNS / pdns / 12595591960

03 Jan 2025 09:27AM UTC coverage: 62.774% (+2.5%) from 60.245%
12595591960

Pull #15008

github

web-flow
Merge c2a2749d3 into 788f396a7
Pull Request #15008: Do not follow CNAME records for ANY or CNAME queries

30393 of 78644 branches covered (38.65%)

Branch coverage included in aggregate %.

105822 of 138350 relevant lines covered (76.49%)

4613078.44 hits per line

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

32.77
/pdns/recursordist/rec_channel_rec.cc
1
#ifdef HAVE_CONFIG_H
2
#include "config.h"
3
#endif
4
#include "utility.hh"
5
#include "rec_channel.hh"
6

7
#include <vector>
8
#ifdef MALLOC_TRACE
9
#include "malloctrace.hh"
10
#endif
11
#include "misc.hh"
12
#include "recursor_cache.hh"
13
#include "syncres.hh"
14
#include "negcache.hh"
15
#include <boost/format.hpp>
16
#include <boost/algorithm/string.hpp>
17

18
#include "version.hh"
19
#include <sys/types.h>
20
#include <sys/stat.h>
21
#include <fcntl.h>
22
#include "logger.hh"
23
#include "dnsparser.hh"
24
#include "arguments.hh"
25
#include <sys/resource.h>
26
#include <sys/time.h>
27
#include "lock.hh"
28
#include "rec-lua-conf.hh"
29

30
#include "aggressive_nsec.hh"
31
#include "coverage.hh"
32
#include "validate-recursor.hh"
33
#include "filterpo.hh"
34

35
#include "secpoll-recursor.hh" // IWYU pragma: keep, needed by included generated file
36
#include "pubsuffix.hh"
37
#include "namespaces.hh"
38
#include "rec-taskqueue.hh"
39
#include "rec-tcpout.hh" // IWYU pragma: keep, needed by included generated file
40
#include "rec-main.hh"
41
#include "rec-system-resolve.hh"
42

43
#include "settings/cxxsettings.hh"
44

45
/* g++ defines __SANITIZE_THREAD__
46
   clang++ supports the nice __has_feature(thread_sanitizer),
47
   let's merge them */
48
#if defined(__has_feature)
49
#if __has_feature(thread_sanitizer)
50
#define __SANITIZE_THREAD__ 1
51
#endif
52
#if __has_feature(address_sanitizer)
53
#define __SANITIZE_ADDRESS__ 1
54
#endif
55
#endif
56

57
#if defined(__SANITIZE_ADDRESS__) && defined(HAVE_LEAK_SANITIZER_INTERFACE)
58
#include <sanitizer/lsan_interface.h>
59
#endif
60

61
std::pair<std::string, std::string> PrefixDashNumberCompare::prefixAndTrailingNum(const std::string& a)
62
{
759,766✔
63
  auto i = a.length();
759,766✔
64
  if (i == 0) {
759,766!
65
    return {a, ""};
×
66
  }
×
67
  --i;
759,766✔
68
  if (!std::isdigit(a[i])) {
759,766✔
69
    return {a, ""};
563,432✔
70
  }
563,432✔
71
  while (i > 0) {
1,069,608!
72
    if (!std::isdigit(a[i])) {
1,069,608✔
73
      break;
196,334✔
74
    }
196,334✔
75
    --i;
873,274✔
76
  }
873,274✔
77
  return {a.substr(0, i + 1), a.substr(i + 1, a.size() - i - 1)};
196,334✔
78
}
759,766✔
79

80
bool PrefixDashNumberCompare::operator()(const std::string& a, const std::string& b) const
81
{
379,883✔
82
  auto [aprefix, anum] = prefixAndTrailingNum(a);
379,883✔
83
  auto [bprefix, bnum] = prefixAndTrailingNum(b);
379,883✔
84

85
  if (aprefix != bprefix || anum.length() == 0 || bnum.length() == 0) {
379,883!
86
    return a < b;
328,621✔
87
  }
328,621✔
88
  auto aa = std::stoull(anum);
51,262✔
89
  auto bb = std::stoull(bnum);
51,262✔
90
  return aa < bb;
51,262✔
91
}
379,883✔
92

93
static map<string, const uint32_t*> d_get32bitpointers;
94
static map<string, const pdns::stat_t*> d_getatomics;
95
static map<string, std::function<uint64_t()>> d_get64bitmembers;
96
static map<string, std::function<StatsMap()>> d_getmultimembers;
97

98
struct dynmetrics
99
{
100
  std::atomic<unsigned long>* d_ptr;
101
  std::string d_prometheusName;
102
};
103

104
static LockGuarded<map<string, dynmetrics>> d_dynmetrics;
105

106
static std::map<StatComponent, std::set<std::string>> s_disabledStats;
107

108
bool isStatDisabled(StatComponent component, const string& name)
109
{
152✔
110
  return s_disabledStats[component].count(name) != 0;
152✔
111
}
152✔
112

113
void disableStat(StatComponent component, const string& name)
114
{
×
115
  s_disabledStats[component].insert(name);
×
116
}
×
117

118
void disableStats(StatComponent component, const string& stats)
119
{
1,224✔
120
  std::vector<std::string> disabledStats;
1,224✔
121
  stringtok(disabledStats, stats, ", ");
1,224✔
122
  auto& map = s_disabledStats[component];
1,224✔
123
  for (const auto& st : disabledStats) {
204,102✔
124
    map.insert(st);
204,102✔
125
  }
204,102✔
126
}
1,224✔
127

128
static void addGetStat(const string& name, const uint32_t* place)
129
{
153✔
130
  if (!d_get32bitpointers.emplace(name, place).second) {
153!
131
    cerr << "addGetStat: double def " << name << endl;
×
132
    _exit(1);
×
133
  }
×
134
}
153✔
135

136
static void addGetStat(const string& name, const pdns::stat_t* place)
137
{
24,786✔
138
  if (!d_getatomics.emplace(name, place).second) {
24,786!
139
    cerr << "addGetStat: double def " << name << endl;
×
140
    _exit(1);
×
141
  }
×
142
}
24,786✔
143

144
static void addGetStat(const string& name, std::function<uint64_t()> func)
145
{
25,092✔
146
  if (!d_get64bitmembers.emplace(name, std::move(func)).second) {
25,092!
147
    cerr << "addGetStat: double def " << name << endl;
×
148
    _exit(1);
×
149
  }
×
150
}
25,092✔
151

152
static void addGetStat(const string& name, std::function<StatsMap()> func)
153
{
1,071✔
154
  if (!d_getmultimembers.emplace(name, std::move(func)).second) {
1,071!
155
    cerr << "addGetStat: double def " << name << endl;
×
156
    _exit(1);
×
157
  }
×
158
}
1,071✔
159

160
static std::string getPrometheusName(const std::string& arg)
161
{
20,258✔
162
  std::string name = arg;
20,258✔
163
  std::replace_if(
20,258✔
164
    name.begin(), name.end(), [](char c) { return !isalnum(static_cast<unsigned char>(c)); }, '_');
383,614✔
165
  return "pdns_recursor_" + name;
20,258✔
166
}
20,258✔
167

168
std::atomic<unsigned long>* getDynMetric(const std::string& str, const std::string& prometheusName)
169
{
×
170
  auto dm = d_dynmetrics.lock();
×
171
  auto f = dm->find(str);
×
172
  if (f != dm->end()) {
×
173
    return f->second.d_ptr;
×
174
  }
×
175

176
  std::string name(str);
×
177
  if (!prometheusName.empty()) {
×
178
    name = prometheusName;
×
179
  }
×
180
  else {
×
181
    name = getPrometheusName(name);
×
182
  }
×
183

184
  auto ret = dynmetrics{new std::atomic<unsigned long>(), std::move(name)};
×
185
  (*dm)[str] = ret;
×
186
  return ret.d_ptr;
×
187
}
×
188

189
static std::optional<uint64_t> get(const string& name)
190
{
600✔
191
  std::optional<uint64_t> ret;
600✔
192

193
  if (d_get32bitpointers.count(name))
600✔
194
    return *d_get32bitpointers.find(name)->second;
4✔
195
  if (d_getatomics.count(name))
596!
196
    return d_getatomics.find(name)->second->load();
×
197
  if (d_get64bitmembers.count(name))
596✔
198
    return d_get64bitmembers.find(name)->second();
527✔
199

200
  {
69✔
201
    auto dm = d_dynmetrics.lock();
69✔
202
    auto f = rplookup(*dm, name);
69✔
203
    if (f) {
69!
204
      return f->d_ptr->load();
×
205
    }
×
206
  }
69✔
207

208
  for (const auto& themultimember : d_getmultimembers) {
99✔
209
    const auto items = themultimember.second();
99✔
210
    const auto item = items.find(name);
99✔
211
    if (item != items.end()) {
99✔
212
      return std::stoull(item->second.d_value);
64✔
213
    }
64✔
214
  }
99✔
215

216
  return ret;
5✔
217
}
69✔
218

219
std::optional<uint64_t> getStatByName(const std::string& name)
220
{
596✔
221
  return get(name);
596✔
222
}
596✔
223

224
StatsMap getAllStatsMap(StatComponent component)
225
{
119✔
226
  StatsMap ret;
119✔
227
  const auto& disabledlistMap = s_disabledStats.at(component);
119✔
228

229
  for (const auto& the32bits : d_get32bitpointers) {
119✔
230
    if (disabledlistMap.count(the32bits.first) == 0) {
119!
231
      ret.emplace(the32bits.first, StatsMapEntry{getPrometheusName(the32bits.first), std::to_string(*the32bits.second)});
119✔
232
    }
119✔
233
  }
119✔
234
  for (const auto& atomic : d_getatomics) {
19,278✔
235
    if (disabledlistMap.count(atomic.first) == 0) {
19,278✔
236
      ret.emplace(atomic.first, StatsMapEntry{getPrometheusName(atomic.first), std::to_string(atomic.second->load())});
238✔
237
    }
238✔
238
  }
19,278✔
239

240
  for (const auto& the64bitmembers : d_get64bitmembers) {
19,516✔
241
    if (disabledlistMap.count(the64bitmembers.first) == 0) {
19,516✔
242
      ret.emplace(the64bitmembers.first, StatsMapEntry{getPrometheusName(the64bitmembers.first), std::to_string(the64bitmembers.second())});
19,159✔
243
    }
19,159✔
244
  }
19,516✔
245

246
  for (const auto& themultimember : d_getmultimembers) {
833✔
247
    if (disabledlistMap.count(themultimember.first) == 0) {
833✔
248
      ret.merge(themultimember.second());
643✔
249
    }
643✔
250
  }
833✔
251

252
  {
119✔
253
    for (const auto& a : *(d_dynmetrics.lock())) {
119!
254
      if (disabledlistMap.count(a.first) == 0) {
×
255
        ret.emplace(a.first, StatsMapEntry{a.second.d_prometheusName, std::to_string(*a.second.d_ptr)});
×
256
      }
×
257
    }
×
258
  }
119✔
259

260
  return ret;
119✔
261
}
119✔
262

263
static string getAllStats()
264
{
36✔
265
  auto varmap = getAllStatsMap(StatComponent::RecControl);
36✔
266
  string ret;
36✔
267
  for (const auto& tup : varmap) {
6,741✔
268
    ret += tup.first + "\t" + tup.second.d_value + "\n";
6,741✔
269
  }
6,741✔
270
  return ret;
36✔
271
}
36✔
272

273
template <typename T>
274
static string doGet(T begin, T end)
275
{
4✔
276
  string ret;
4✔
277

278
  for (T i = begin; i != end; ++i) {
8✔
279
    std::optional<uint64_t> num = get(*i);
4✔
280
    if (num)
4!
281
      ret += std::to_string(*num) + "\n";
4✔
282
    else
×
283
      ret += "UNKNOWN\n";
×
284
  }
4✔
285
  return ret;
4✔
286
}
4✔
287

288
template <typename T>
289
string static doGetParameter(T begin, T end)
290
{
×
291
  string ret;
×
292
  string parm;
×
293
  using boost::replace_all;
×
294
  for (T i = begin; i != end; ++i) {
×
295
    if (::arg().parmIsset(*i)) {
×
296
      parm = ::arg()[*i];
×
297
      replace_all(parm, "\\", "\\\\");
×
298
      replace_all(parm, "\"", "\\\"");
×
299
      replace_all(parm, "\n", "\\n");
×
300
      ret += *i + "=\"" + parm + "\"\n";
×
301
    }
×
302
    else
×
303
      ret += *i + " not known\n";
×
304
  }
×
305
  return ret;
×
306
}
×
307

308
/* Read an (open) fd from the control channel */
309
static FDWrapper
310
getfd(int s)
311
{
5✔
312
  int fd = -1;
5✔
313
  struct msghdr msg;
5✔
314
  struct cmsghdr* cmsg;
5✔
315
  union
5✔
316
  {
5✔
317
    struct cmsghdr hdr;
5✔
318
    unsigned char buf[CMSG_SPACE(sizeof(int))];
5✔
319
  } cmsgbuf;
5✔
320
  struct iovec io_vector[1];
5✔
321
  char ch;
5✔
322

323
  io_vector[0].iov_base = &ch;
5✔
324
  io_vector[0].iov_len = 1;
5✔
325

326
  memset(&msg, 0, sizeof(msg));
5✔
327
  msg.msg_control = &cmsgbuf.buf;
5✔
328
  msg.msg_controllen = sizeof(cmsgbuf.buf);
5✔
329
  msg.msg_iov = io_vector;
5✔
330
  msg.msg_iovlen = 1;
5✔
331

332
  if (recvmsg(s, &msg, 0) == -1) {
5!
333
    throw PDNSException("recvmsg");
×
334
  }
×
335
  if ((msg.msg_flags & MSG_TRUNC) || (msg.msg_flags & MSG_CTRUNC)) {
5!
336
    throw PDNSException("control message truncated");
×
337
  }
×
338
  for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
5!
339
       cmsg = CMSG_NXTHDR(&msg, cmsg)) {
5✔
340
    if (cmsg->cmsg_len == CMSG_LEN(sizeof(int)) && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
5!
341
      fd = *(int*)CMSG_DATA(cmsg);
5✔
342
      break;
5✔
343
    }
5✔
344
  }
5✔
345
  return FDWrapper(fd);
5✔
346
}
5✔
347

348
static uint64_t dumpAggressiveNSECCache(int fd)
349
{
5✔
350
  if (!g_aggressiveNSECCache) {
5!
351
    return 0;
×
352
  }
×
353

354
  int newfd = dup(fd);
5✔
355
  if (newfd == -1) {
5!
356
    return 0;
×
357
  }
×
358
  auto filePtr = pdns::UniqueFilePtr(fdopen(newfd, "w"));
5✔
359
  if (!filePtr) {
5!
360
    return 0;
×
361
  }
×
362
  fprintf(filePtr.get(), "; aggressive NSEC cache dump follows\n;\n");
5✔
363

364
  struct timeval now;
5✔
365
  Utility::gettimeofday(&now, nullptr);
5✔
366
  return g_aggressiveNSECCache->dumpToFile(filePtr, now);
5✔
367
}
5✔
368

369
static uint64_t* pleaseDumpEDNSMap(int fd)
370
{
×
371
  return new uint64_t(SyncRes::doEDNSDump(fd));
×
372
}
×
373

374
static uint64_t* pleaseDumpNSSpeeds(int fd)
375
{
×
376
  return new uint64_t(SyncRes::doDumpNSSpeeds(fd));
×
377
}
×
378

379
static uint64_t* pleaseDumpThrottleMap(int fd)
380
{
×
381
  return new uint64_t(SyncRes::doDumpThrottleMap(fd));
×
382
}
×
383

384
static uint64_t* pleaseDumpFailedServers(int fd)
385
{
×
386
  return new uint64_t(SyncRes::doDumpFailedServers(fd));
×
387
}
×
388

389
static uint64_t* pleaseDumpSavedParentNSSets(int fd)
390
{
×
391
  return new uint64_t(SyncRes::doDumpSavedParentNSSets(fd));
×
392
}
×
393

394
static uint64_t* pleaseDumpNonResolvingNS(int fd)
395
{
×
396
  return new uint64_t(SyncRes::doDumpNonResolvingNS(fd));
×
397
}
×
398

399
static uint64_t* pleaseDumpDoTProbeMap(int fd)
400
{
×
401
  return new uint64_t(SyncRes::doDumpDoTProbeMap(fd));
×
402
}
×
403

404
// Generic dump to file command
405
static RecursorControlChannel::Answer doDumpToFile(int s, uint64_t* (*function)(int s), const string& name, bool threads = true)
406
{
×
407
  auto fdw = getfd(s);
×
408

409
  if (fdw < 0) {
×
410
    return {1, name + ": error opening dump file for writing: " + stringerror() + "\n"};
×
411
  }
×
412

413
  uint64_t total = 0;
×
414
  try {
×
415
    if (threads) {
×
416
      int fd = fdw;
×
417
      total = broadcastAccFunction<uint64_t>([function, fd] { return function(fd); });
×
418
    }
×
419
    else {
×
420
      auto ret = function(fdw);
×
421
      total = *ret;
×
422
      delete ret;
×
423
    }
×
424
  }
×
425
  catch (std::exception& e) {
×
426
    return {1, name + ": error dumping data: " + string(e.what()) + "\n"};
×
427
  }
×
428
  catch (PDNSException& e) {
×
429
    return {1, name + ": error dumping data: " + e.reason + "\n"};
×
430
  }
×
431

432
  return {0, name + ": dumped " + std::to_string(total) + " records\n"};
×
433
}
×
434

435
// Does not follow the generic dump to file pattern, has a more complex lambda
436
template <typename T>
437
static RecursorControlChannel::Answer doDumpCache(int socket, T begin, T end)
438
{
5✔
439
  auto fdw = getfd(socket);
5✔
440

441
  if (fdw < 0) {
5!
442
    return {1, "Error opening dump file for writing: " + stringerror() + "\n"};
×
443
  }
×
444
  bool dumpRecordCache = true;
5✔
445
  bool dumpNegCache = true;
5✔
446
  bool dumpPacketCache = true;
5✔
447
  bool dumpAggrCache = true;
5✔
448
  if (begin != end) {
5!
449
    dumpRecordCache = false;
×
450
    dumpNegCache = false;
×
451
    dumpPacketCache = false;
×
452
    dumpAggrCache = false;
×
453
    for (auto name = begin; name != end; ++name) {
×
454
      if (*name == "r") {
×
455
        dumpRecordCache = true;
×
456
      }
×
457
      else if (*name == "n") {
×
458
        dumpNegCache = true;
×
459
      }
×
460
      else if (*name == "p") {
×
461
        dumpPacketCache = true;
×
462
      }
×
463
      else if (*name == "a") {
×
464
        dumpAggrCache = true;
×
465
      }
×
466
    }
×
467
  }
×
468
  uint64_t total = 0;
5✔
469
  try {
5✔
470
    if (dumpRecordCache) {
5!
471
      total += g_recCache->doDump(fdw, g_maxCacheEntries.load());
5✔
472
    }
5✔
473
    if (dumpNegCache) {
5!
474
      total += g_negCache->doDump(fdw, g_maxCacheEntries.load() / 8);
5✔
475
    }
5✔
476
    if (dumpPacketCache) {
5!
477
      total += g_packetCache ? g_packetCache->doDump(fdw) : 0;
5!
478
    }
5✔
479
    if (dumpAggrCache) {
5!
480
      total += dumpAggressiveNSECCache(fdw);
5✔
481
    }
5✔
482
  }
5✔
483
  catch (...) {
5✔
484
  }
×
485

486
  return {0, "dumped " + std::to_string(total) + " records\n"};
5✔
487
}
5✔
488

489
// Does not follow the generic dump to file pattern, has an argument
490
template <typename T>
491
static RecursorControlChannel::Answer doDumpRPZ(int s, T begin, T end)
492
{
×
493
  auto fdw = getfd(s);
×
494

495
  if (fdw < 0) {
×
496
    return {1, "Error opening dump file for writing: " + stringerror() + "\n"};
×
497
  }
×
498

499
  T i = begin;
×
500

501
  if (i == end) {
×
502
    return {1, "No zone name specified\n"};
×
503
  }
×
504
  string zoneName = *i;
×
505

506
  auto luaconf = g_luaconfs.getLocal();
×
507
  const auto zone = luaconf->dfe.getZone(zoneName);
×
508
  if (!zone) {
×
509
    return {1, "No RPZ zone named " + zoneName + "\n"};
×
510
  }
×
511

512
  auto filePtr = pdns::UniqueFilePtr(fdopen(fdw, "w"));
×
513
  if (!filePtr) {
×
514
    int err = errno;
×
515
    return {1, "converting file descriptor: " + stringerror(err) + "\n"};
×
516
  }
×
517

518
  zone->dump(filePtr.get());
×
519

520
  return {0, "done\n"};
×
521
}
×
522

523
template <typename T>
524
static string doWipeCache(T begin, T end, uint16_t qtype)
525
{
405✔
526
  vector<pair<DNSName, bool>> toWipe;
405✔
527
  for (T i = begin; i != end; ++i) {
810✔
528
    DNSName canon;
405✔
529
    bool subtree = false;
405✔
530

531
    try {
405✔
532
      if (boost::ends_with(*i, "$")) {
405!
533
        canon = DNSName(i->substr(0, i->size() - 1));
405✔
534
        subtree = true;
405✔
535
      }
405✔
536
      else {
×
537
        canon = DNSName(*i);
×
538
      }
×
539
    }
405✔
540
    catch (std::exception& e) {
405✔
541
      return "Error: " + std::string(e.what()) + ", nothing wiped\n";
×
542
    }
×
543
    toWipe.emplace_back(canon, subtree);
405✔
544
  }
405✔
545

546
  int count = 0, pcount = 0, countNeg = 0;
405✔
547
  for (const auto& wipe : toWipe) {
405✔
548
    try {
405✔
549
      auto res = wipeCaches(wipe.first, wipe.second, qtype);
405✔
550
      count += res.record_count;
405✔
551
      pcount += res.packet_count;
405✔
552
      countNeg += res.negative_record_count;
405✔
553
    }
405✔
554
    catch (const std::exception& e) {
405✔
555
      g_log << Logger::Warning << ", failed: " << e.what() << endl;
×
556
    }
×
557
  }
405✔
558

559
  return "wiped " + std::to_string(count) + " records, " + std::to_string(countNeg) + " negative records, " + std::to_string(pcount) + " packets\n";
405✔
560
}
405✔
561

562
template <typename T>
563
static string doSetCarbonServer(T begin, T end)
564
{
×
565
  auto config = g_carbonConfig.getCopy();
×
566
  if (begin == end) {
×
567
    config.servers.clear();
×
568
    g_carbonConfig.setState(std::move(config));
×
569
    return "cleared carbon-server setting\n";
×
570
  }
×
571

572
  string ret;
×
573
  stringtok(config.servers, *begin, ", ");
×
574
  ret = "set carbon-server to '" + *begin + "'\n";
×
575

576
  ++begin;
×
577
  if (begin != end) {
×
578
    config.hostname = *begin;
×
579
    ret += "set carbon-ourname to '" + *begin + "'\n";
×
580
  }
×
581
  else {
×
582
    g_carbonConfig.setState(std::move(config));
×
583
    return ret;
×
584
  }
×
585

586
  ++begin;
×
587
  if (begin != end) {
×
588
    config.namespace_name = *begin;
×
589
    ret += "set carbon-namespace to '" + *begin + "'\n";
×
590
  }
×
591
  else {
×
592
    g_carbonConfig.setState(std::move(config));
×
593
    return ret;
×
594
  }
×
595

596
  ++begin;
×
597
  if (begin != end) {
×
598
    config.instance_name = *begin;
×
599
    ret += "set carbon-instance to '" + *begin + "'\n";
×
600
  }
×
601

602
  g_carbonConfig.setState(std::move(config));
×
603
  return ret;
×
604
}
×
605

606
template <typename T>
607
static string doSetDnssecLogBogus(T begin, T end)
608
{
×
609
  if (checkDNSSECDisabled())
×
610
    return "DNSSEC is disabled in the configuration, not changing the Bogus logging setting\n";
×
611

612
  if (begin == end)
×
613
    return "No DNSSEC Bogus logging setting specified\n";
×
614

615
  if (pdns_iequals(*begin, "on") || pdns_iequals(*begin, "yes")) {
×
616
    if (!g_dnssecLogBogus) {
×
617
      g_log << Logger::Warning << "Enabling DNSSEC Bogus logging, requested via control channel" << endl;
×
618
      g_dnssecLogBogus = true;
×
619
      return "DNSSEC Bogus logging enabled\n";
×
620
    }
×
621
    return "DNSSEC Bogus logging was already enabled\n";
×
622
  }
×
623

624
  if (pdns_iequals(*begin, "off") || pdns_iequals(*begin, "no")) {
×
625
    if (g_dnssecLogBogus) {
×
626
      g_log << Logger::Warning << "Disabling DNSSEC Bogus logging, requested via control channel" << endl;
×
627
      g_dnssecLogBogus = false;
×
628
      return "DNSSEC Bogus logging disabled\n";
×
629
    }
×
630
    return "DNSSEC Bogus logging was already disabled\n";
×
631
  }
×
632

633
  return "Unknown DNSSEC Bogus setting: '" + *begin + "'\n";
×
634
}
×
635

636
template <typename T>
637
static string doAddNTA(T begin, T end)
638
{
×
639
  if (checkDNSSECDisabled())
×
640
    return "DNSSEC is disabled in the configuration, not adding a Negative Trust Anchor\n";
×
641

642
  if (begin == end)
×
643
    return "No NTA specified, doing nothing\n";
×
644

645
  DNSName who;
×
646
  try {
×
647
    who = DNSName(*begin);
×
648
  }
×
649
  catch (std::exception& e) {
×
650
    string ret("Can't add Negative Trust Anchor: ");
×
651
    ret += e.what();
×
652
    ret += "\n";
×
653
    return ret;
×
654
  }
×
655
  begin++;
×
656

657
  string why("");
×
658
  while (begin != end) {
×
659
    why += *begin;
×
660
    begin++;
×
661
    if (begin != end)
×
662
      why += " ";
×
663
  }
×
664
  g_log << Logger::Warning << "Adding Negative Trust Anchor for " << who << " with reason '" << why << "', requested via control channel" << endl;
×
665
  g_luaconfs.modify([who, why](LuaConfigItems& lci) {
×
666
    lci.negAnchors[who] = why;
×
667
  });
×
668
  try {
×
669
    wipeCaches(who, true, 0xffff);
×
670
  }
×
671
  catch (std::exception& e) {
×
672
    g_log << Logger::Warning << ", failed: " << e.what() << endl;
×
673
    return "Unable to clear caches while adding Negative Trust Anchor for " + who.toStringRootDot() + ": " + e.what() + "\n";
×
674
  }
×
675
  return "Added Negative Trust Anchor for " + who.toLogString() + " with reason '" + why + "'\n";
×
676
}
×
677

678
template <typename T>
679
static string doClearNTA(T begin, T end)
680
{
×
681
  if (checkDNSSECDisabled())
×
682
    return "DNSSEC is disabled in the configuration, not removing a Negative Trust Anchor\n";
×
683

684
  if (begin == end)
×
685
    return "No Negative Trust Anchor specified, doing nothing.\n";
×
686

687
  if (begin + 1 == end && *begin == "*") {
×
688
    g_log << Logger::Warning << "Clearing all Negative Trust Anchors, requested via control channel" << endl;
×
689
    g_luaconfs.modify([](LuaConfigItems& lci) {
×
690
      lci.negAnchors.clear();
×
691
    });
×
692
    return "Cleared all Negative Trust Anchors.\n";
×
693
  }
×
694

695
  vector<DNSName> toRemove;
×
696
  DNSName who;
×
697
  while (begin != end) {
×
698
    if (*begin == "*")
×
699
      return "Don't mix all Negative Trust Anchor removal with multiple Negative Trust Anchor removal. Nothing removed\n";
×
700
    try {
×
701
      who = DNSName(*begin);
×
702
    }
×
703
    catch (std::exception& e) {
×
704
      string ret("Error: ");
×
705
      ret += e.what();
×
706
      ret += ". No Negative Anchors removed\n";
×
707
      return ret;
×
708
    }
×
709
    toRemove.push_back(who);
×
710
    begin++;
×
711
  }
×
712

713
  string removed("");
×
714
  bool first(true);
×
715
  try {
×
716
    for (auto const& entry : toRemove) {
×
717
      g_log << Logger::Warning << "Clearing Negative Trust Anchor for " << entry << ", requested via control channel" << endl;
×
718
      g_luaconfs.modify([entry](LuaConfigItems& lci) {
×
719
        lci.negAnchors.erase(entry);
×
720
      });
×
721
      wipeCaches(entry, true, 0xffff);
×
722
      if (!first) {
×
723
        first = false;
×
724
        removed += ",";
×
725
      }
×
726
      removed += " " + entry.toStringRootDot();
×
727
    }
×
728
  }
×
729
  catch (std::exception& e) {
×
730
    g_log << Logger::Warning << ", failed: " << e.what() << endl;
×
731
    return "Unable to clear caches while clearing Negative Trust Anchor for " + who.toStringRootDot() + ": " + e.what() + "\n";
×
732
  }
×
733

734
  return "Removed Negative Trust Anchors for " + removed + "\n";
×
735
}
×
736

737
static string getNTAs()
738
{
×
739
  if (checkDNSSECDisabled())
×
740
    return "DNSSEC is disabled in the configuration\n";
×
741

742
  string ret("Configured Negative Trust Anchors:\n");
×
743
  auto luaconf = g_luaconfs.getLocal();
×
744
  for (const auto& negAnchor : luaconf->negAnchors)
×
745
    ret += negAnchor.first.toLogString() + "\t" + negAnchor.second + "\n";
×
746
  return ret;
×
747
}
×
748

749
template <typename T>
750
static string doAddTA(T begin, T end)
751
{
×
752
  if (checkDNSSECDisabled())
×
753
    return "DNSSEC is disabled in the configuration, not adding a Trust Anchor\n";
×
754

755
  if (begin == end)
×
756
    return "No TA specified, doing nothing\n";
×
757

758
  DNSName who;
×
759
  try {
×
760
    who = DNSName(*begin);
×
761
  }
×
762
  catch (std::exception& e) {
×
763
    string ret("Can't add Trust Anchor: ");
×
764
    ret += e.what();
×
765
    ret += "\n";
×
766
    return ret;
×
767
  }
×
768
  begin++;
×
769

770
  string what("");
×
771
  while (begin != end) {
×
772
    what += *begin + " ";
×
773
    begin++;
×
774
  }
×
775

776
  try {
×
777
    g_log << Logger::Warning << "Adding Trust Anchor for " << who << " with data '" << what << "', requested via control channel";
×
778
    g_luaconfs.modify([who, what](LuaConfigItems& lci) {
×
779
      auto ds = std::dynamic_pointer_cast<DSRecordContent>(DSRecordContent::make(what));
×
780
      lci.dsAnchors[who].insert(*ds);
×
781
    });
×
782
    wipeCaches(who, true, 0xffff);
×
783
    g_log << Logger::Warning << endl;
×
784
    return "Added Trust Anchor for " + who.toStringRootDot() + " with data " + what + "\n";
×
785
  }
×
786
  catch (std::exception& e) {
×
787
    g_log << Logger::Warning << ", failed: " << e.what() << endl;
×
788
    return "Unable to add Trust Anchor for " + who.toStringRootDot() + ": " + e.what() + "\n";
×
789
  }
×
790
}
×
791

792
template <typename T>
793
static string doClearTA(T begin, T end)
794
{
×
795
  if (checkDNSSECDisabled())
×
796
    return "DNSSEC is disabled in the configuration, not removing a Trust Anchor\n";
×
797

798
  if (begin == end)
×
799
    return "No Trust Anchor to clear\n";
×
800

801
  vector<DNSName> toRemove;
×
802
  DNSName who;
×
803
  while (begin != end) {
×
804
    try {
×
805
      who = DNSName(*begin);
×
806
    }
×
807
    catch (std::exception& e) {
×
808
      string ret("Error: ");
×
809
      ret += e.what();
×
810
      ret += ". No Anchors removed\n";
×
811
      return ret;
×
812
    }
×
813
    if (who.isRoot())
×
814
      return "Refusing to remove root Trust Anchor, no Anchors removed\n";
×
815
    toRemove.push_back(who);
×
816
    begin++;
×
817
  }
×
818

819
  string removed("");
×
820
  bool first(true);
×
821
  try {
×
822
    for (auto const& entry : toRemove) {
×
823
      g_log << Logger::Warning << "Removing Trust Anchor for " << entry << ", requested via control channel" << endl;
×
824
      g_luaconfs.modify([entry](LuaConfigItems& lci) {
×
825
        lci.dsAnchors.erase(entry);
×
826
      });
×
827
      wipeCaches(entry, true, 0xffff);
×
828
      if (!first) {
×
829
        first = false;
×
830
        removed += ",";
×
831
      }
×
832
      removed += " " + entry.toStringRootDot();
×
833
    }
×
834
  }
×
835
  catch (std::exception& e) {
×
836
    g_log << Logger::Warning << ", failed: " << e.what() << endl;
×
837
    return "Unable to clear caches while clearing Trust Anchor for " + who.toStringRootDot() + ": " + e.what() + "\n";
×
838
  }
×
839

840
  return "Removed Trust Anchor(s) for" + removed + "\n";
×
841
}
×
842

843
static string getTAs()
844
{
1✔
845
  if (checkDNSSECDisabled())
1!
846
    return "DNSSEC is disabled in the configuration\n";
×
847

848
  string ret("Configured Trust Anchors:\n");
1✔
849
  auto luaconf = g_luaconfs.getLocal();
1✔
850
  for (const auto& anchor : luaconf->dsAnchors) {
1✔
851
    ret += anchor.first.toLogString() + "\n";
1✔
852
    for (const auto& e : anchor.second) {
2✔
853
      ret += "\t\t" + e.getZoneRepresentation() + "\n";
2✔
854
    }
2✔
855
  }
1✔
856

857
  return ret;
1✔
858
}
1✔
859

860
template <typename T>
861
static string setMinimumTTL(T begin, T end)
862
{
×
863
  if (end - begin != 1)
×
864
    return "Need to supply new minimum TTL number\n";
×
865
  try {
×
866
    pdns::checked_stoi_into(SyncRes::s_minimumTTL, *begin);
×
867
    return "New minimum TTL: " + std::to_string(SyncRes::s_minimumTTL) + "\n";
×
868
  }
×
869
  catch (const std::exception& e) {
×
870
    return "Error parsing the new minimum TTL number: " + std::string(e.what()) + "\n";
×
871
  }
×
872
}
×
873

874
template <typename T>
875
static string setMinimumECSTTL(T begin, T end)
876
{
×
877
  if (end - begin != 1)
×
878
    return "Need to supply new ECS minimum TTL number\n";
×
879
  try {
×
880
    pdns::checked_stoi_into(SyncRes::s_minimumECSTTL, *begin);
×
881
    return "New minimum ECS TTL: " + std::to_string(SyncRes::s_minimumECSTTL) + "\n";
×
882
  }
×
883
  catch (const std::exception& e) {
×
884
    return "Error parsing the new ECS minimum TTL number: " + std::string(e.what()) + "\n";
×
885
  }
×
886
}
×
887

888
template <typename T>
889
static string setMaxCacheEntries(T begin, T end)
890
{
×
891
  if (end - begin != 1)
×
892
    return "Need to supply new cache size\n";
×
893
  try {
×
894
    g_maxCacheEntries = pdns::checked_stoi<uint32_t>(*begin);
×
895
    return "New max cache entries: " + std::to_string(g_maxCacheEntries) + "\n";
×
896
  }
×
897
  catch (const std::exception& e) {
×
898
    return "Error parsing the new cache size: " + std::string(e.what()) + "\n";
×
899
  }
×
900
}
×
901

902
template <typename T>
903
static string setMaxPacketCacheEntries(T begin, T end)
904
{
×
905
  if (end - begin != 1)
×
906
    return "Need to supply new packet cache size\n";
×
907
  if (::arg().mustDo("disable-packetcache")) {
×
908
    return "Packet cache is disabled\n";
×
909
  }
×
910
  try {
×
911
    g_maxPacketCacheEntries = pdns::checked_stoi<uint32_t>(*begin);
×
912
    g_packetCache->setMaxSize(g_maxPacketCacheEntries);
×
913
    return "New max packetcache entries: " + std::to_string(g_maxPacketCacheEntries) + "\n";
×
914
  }
×
915
  catch (const std::exception& e) {
×
916
    return "Error parsing the new packet cache size: " + std::string(e.what()) + "\n";
×
917
  }
×
918
}
×
919

920
template <typename T>
921
static RecursorControlChannel::Answer setAggrNSECCacheSize(T begin, T end)
922
{
×
923
  if (end - begin != 1) {
×
924
    return {1, "Need to supply new aggressive NSEC cache size\n"};
×
925
  }
×
926
  if (!g_aggressiveNSECCache) {
×
927
    return {1, "Aggressive NSEC cache is disabled by startup config\n"};
×
928
  }
×
929
  try {
×
930
    auto newmax = pdns::checked_stoi<uint64_t>(*begin);
×
931
    g_aggressiveNSECCache->setMaxEntries(newmax);
×
932
    return {0, "New aggressive NSEC cache size: " + std::to_string(newmax) + "\n"};
×
933
  }
×
934
  catch (const std::exception& e) {
×
935
    return {1, "Error parsing the new aggressive NSEC cache size: " + std::string(e.what()) + "\n"};
×
936
  }
×
937
}
×
938

939
static uint64_t getSysTimeMsec()
940
{
123✔
941
  struct rusage ru;
123✔
942
  getrusage(RUSAGE_SELF, &ru);
123✔
943
  return (ru.ru_stime.tv_sec * 1000ULL + ru.ru_stime.tv_usec / 1000);
123✔
944
}
123✔
945

946
static uint64_t getUserTimeMsec()
947
{
123✔
948
  struct rusage ru;
123✔
949
  getrusage(RUSAGE_SELF, &ru);
123✔
950
  return (ru.ru_utime.tv_sec * 1000ULL + ru.ru_utime.tv_usec / 1000);
123✔
951
}
123✔
952

953
/* This is a pretty weird set of functions. To get per-thread cpu usage numbers,
954
   we have to ask a thread over a pipe. We could do so surgically, so if you want to know about
955
   thread 3, we pick pipe 3, but we lack that infrastructure.
956

957
   We can however ask "execute this function on all threads and add up the results".
958
   This is what the first function does using a custom object ThreadTimes, which if you add
959
   to each other keeps filling the first one with CPU usage numbers
960
*/
961

962
static ThreadTimes* pleaseGetThreadCPUMsec()
963
{
321✔
964
  uint64_t ret = 0;
321✔
965
#ifdef RUSAGE_THREAD
321✔
966
  struct rusage ru;
321✔
967
  getrusage(RUSAGE_THREAD, &ru);
321✔
968
  ret = (ru.ru_utime.tv_sec * 1000ULL + ru.ru_utime.tv_usec / 1000);
321✔
969
  ret += (ru.ru_stime.tv_sec * 1000ULL + ru.ru_stime.tv_usec / 1000);
321✔
970
#endif
321✔
971
  return new ThreadTimes{ret, vector<uint64_t>()};
321✔
972
}
321✔
973

974
/* Next up, when you want msec data for a specific thread, we check
975
   if we recently executed pleaseGetThreadCPUMsec. If we didn't we do so
976
   now and consult all threads.
977

978
   We then answer you from the (re)fresh(ed) ThreadTimes.
979
*/
980
static uint64_t doGetThreadCPUMsec(int n)
981
{
613✔
982
  static std::mutex s_mut;
613✔
983
  static time_t last = 0;
613✔
984
  static ThreadTimes tt;
613✔
985

986
  std::lock_guard<std::mutex> l(s_mut);
613✔
987
  if (last != time(nullptr)) {
613✔
988
    tt = broadcastAccFunction<ThreadTimes>(pleaseGetThreadCPUMsec);
51✔
989
    last = time(nullptr);
51✔
990
  }
51✔
991

992
  return tt.times.at(n);
613✔
993
}
613✔
994

995
static ProxyMappingStats_t* pleaseGetProxyMappingStats()
996
{
344✔
997
  auto ret = new ProxyMappingStats_t;
344✔
998
  if (t_proxyMapping) {
344!
999
    for (const auto& [key, entry] : *t_proxyMapping) {
×
1000
      ret->emplace(key, ProxyMappingCounts{entry.stats.netmaskMatches, entry.stats.suffixMatches});
×
1001
    }
×
1002
  }
×
1003
  return ret;
344✔
1004
}
344✔
1005

1006
static RemoteLoggerStats_t* pleaseGetRemoteLoggerStats()
1007
{
344✔
1008
  auto ret = make_unique<RemoteLoggerStats_t>();
344✔
1009

1010
  if (t_protobufServers.servers) {
344!
1011
    for (const auto& server : *t_protobufServers.servers) {
×
1012
      ret->emplace(server->address(), server->getStats());
×
1013
    }
×
1014
  }
×
1015
  return ret.release();
344✔
1016
}
344✔
1017

1018
static string doGetProxyMappingStats()
1019
{
×
1020
  ostringstream ret;
×
1021
  ret << "subnet\t\t\tmatches\tsuffixmatches" << endl;
×
1022
  auto proxyMappingStats = broadcastAccFunction<ProxyMappingStats_t>(pleaseGetProxyMappingStats);
×
1023
  for (const auto& [key, entry] : proxyMappingStats) {
×
1024
    ret << key.toString() << '\t' << entry.netmaskMatches << '\t' << entry.suffixMatches << endl;
×
1025
  }
×
1026
  return ret.str();
×
1027
}
×
1028

1029
static RemoteLoggerStats_t* pleaseGetOutgoingRemoteLoggerStats()
1030
{
344✔
1031
  auto ret = make_unique<RemoteLoggerStats_t>();
344✔
1032

1033
  if (t_outgoingProtobufServers.servers) {
344!
1034
    for (const auto& server : *t_outgoingProtobufServers.servers) {
×
1035
      ret->emplace(server->address(), server->getStats());
×
1036
    }
×
1037
  }
×
1038
  return ret.release();
344✔
1039
}
344✔
1040

1041
#ifdef HAVE_FSTRM
1042
static RemoteLoggerStats_t* pleaseGetFramestreamLoggerStats()
1043
{
344✔
1044
  auto ret = make_unique<RemoteLoggerStats_t>();
344✔
1045

1046
  if (t_frameStreamServersInfo.servers) {
344!
1047
    for (const auto& server : *t_frameStreamServersInfo.servers) {
×
1048
      ret->emplace(server->address(), server->getStats());
×
1049
    }
×
1050
  }
×
1051
  return ret.release();
344✔
1052
}
344✔
1053

1054
static RemoteLoggerStats_t* pleaseGetNODFramestreamLoggerStats()
1055
{
344✔
1056
  auto ret = make_unique<RemoteLoggerStats_t>();
344✔
1057

1058
  if (t_nodFrameStreamServersInfo.servers) {
344!
1059
    for (const auto& server : *t_nodFrameStreamServersInfo.servers) {
×
1060
      ret->emplace(server->address(), server->getStats());
×
1061
    }
×
1062
  }
×
1063
  return ret.release();
344✔
1064
}
344✔
1065
#endif
1066

1067
static void remoteLoggerStats(const string& type, const RemoteLoggerStats_t& stats, ostringstream& outpustStream)
1068
{
×
1069
  if (stats.empty()) {
×
1070
    return;
×
1071
  }
×
1072
  for (const auto& [key, entry] : stats) {
×
1073
    outpustStream << entry.d_queued << '\t' << entry.d_pipeFull << '\t' << entry.d_tooLarge << '\t' << entry.d_otherError << '\t' << key << '\t' << type << endl;
×
1074
  }
×
1075
}
×
1076

1077
static string getRemoteLoggerStats()
1078
{
×
1079
  ostringstream outputStream;
×
1080
  outputStream << "Queued\tPipe-\tToo-\tOther-\tAddress\tType" << endl;
×
1081
  outputStream << "\tFull\tLarge\terror" << endl;
×
1082
  auto stats = broadcastAccFunction<RemoteLoggerStats_t>(pleaseGetRemoteLoggerStats);
×
1083
  remoteLoggerStats("protobuf", stats, outputStream);
×
1084
  stats = broadcastAccFunction<RemoteLoggerStats_t>(pleaseGetOutgoingRemoteLoggerStats);
×
1085
  remoteLoggerStats("outgoingProtobuf", stats, outputStream);
×
1086
#ifdef HAVE_FSTRM
×
1087
  stats = broadcastAccFunction<RemoteLoggerStats_t>(pleaseGetFramestreamLoggerStats);
×
1088
  remoteLoggerStats("dnstapFrameStream", stats, outputStream);
×
1089
  stats = broadcastAccFunction<RemoteLoggerStats_t>(pleaseGetNODFramestreamLoggerStats);
×
1090
  remoteLoggerStats("dnstapNODFrameStream", stats, outputStream);
×
1091
#endif
×
1092
  return outputStream.str();
×
1093
}
×
1094

1095
static string* pleaseGetCurrentQueries()
1096
{
×
1097
  ostringstream ostr;
×
1098
  struct timeval now;
×
1099
  gettimeofday(&now, 0);
×
1100

1101
  ostr << getMT()->getWaiters().size() << " currently outstanding questions\n";
×
1102

1103
  boost::format fmt("%1% %|40t|%2% %|47t|%3% %|63t|%4% %|68t|%5% %|78t|%6%\n");
×
1104

1105
  ostr << (fmt % "qname" % "qtype" % "remote" % "tcp" % "chained" % "spent(ms)");
×
1106
  unsigned int n = 0;
×
1107
  for (const auto& mthread : getMT()->getWaiters()) {
×
1108
    const std::shared_ptr<PacketID>& pident = mthread.key;
×
1109
    const double spent = g_networkTimeoutMsec - (DiffTime(now, mthread.ttd) * 1000);
×
1110
    ostr << (fmt
×
1111
             % pident->domain.toLogString() /* ?? */ % DNSRecordContent::NumberToType(pident->type)
×
1112
             % pident->remote.toString() % (pident->tcpsock ? 'Y' : 'n')
×
1113
             % (pident->fd == -1 ? 'Y' : 'n')
×
1114
             % (spent > 0 ? spent : '0'));
×
1115
    ++n;
×
1116
    if (n >= 100)
×
1117
      break;
×
1118
  }
×
1119
  ostr << " - done\n";
×
1120
  return new string(ostr.str());
×
1121
}
×
1122

1123
static string doCurrentQueries()
1124
{
×
1125
  return broadcastAccFunction<string>(pleaseGetCurrentQueries);
×
1126
}
×
1127

1128
static uint64_t getNegCacheSize()
1129
{
123✔
1130
  return g_negCache->size();
123✔
1131
}
123✔
1132

1133
uint64_t* pleaseGetConcurrentQueries()
1134
{
870✔
1135
  return new uint64_t(getMT() ? getMT()->numProcesses() : 0);
870!
1136
}
870✔
1137

1138
static uint64_t getConcurrentQueries()
1139
{
123✔
1140
  return broadcastAccFunction<uint64_t>(pleaseGetConcurrentQueries);
123✔
1141
}
123✔
1142

1143
static uint64_t doGetCacheSize()
1144
{
123✔
1145
  return g_recCache->size();
123✔
1146
}
123✔
1147

1148
static uint64_t doGetCacheBytes()
1149
{
×
1150
  return g_recCache->bytes();
×
1151
}
×
1152

1153
static uint64_t doGetMallocated()
1154
{
123✔
1155
  // this turned out to be broken
1156
  /*  struct mallinfo mi = mallinfo();
1157
  return mi.uordblks; */
1158
  return 0;
123✔
1159
}
123✔
1160

1161
static StatsMap toStatsMap(const string& name, const pdns::Histogram& histogram)
1162
{
86✔
1163
  const auto& data = histogram.getCumulativeBuckets();
86✔
1164
  const string pbasename = getPrometheusName(name);
86✔
1165
  StatsMap entries;
86✔
1166
  char buf[32];
86✔
1167

1168
  for (const auto& bucket : data) {
1,720✔
1169
    snprintf(buf, sizeof(buf), "%g", bucket.d_boundary / 1e6);
1,720✔
1170
    std::string pname = pbasename + "seconds_bucket{" + "le=\"" + (bucket.d_boundary == std::numeric_limits<uint64_t>::max() ? "+Inf" : buf) + "\"}";
1,720✔
1171
    entries.emplace(bucket.d_name, StatsMapEntry{std::move(pname), std::to_string(bucket.d_count)});
1,720✔
1172
  }
1,720✔
1173

1174
  snprintf(buf, sizeof(buf), "%g", histogram.getSum() / 1e6);
86✔
1175
  entries.emplace(name + "sum", StatsMapEntry{pbasename + "seconds_sum", buf});
86✔
1176
  entries.emplace(name + "count", StatsMapEntry{pbasename + "seconds_count", std::to_string(data.back().d_count)});
86✔
1177

1178
  return entries;
86✔
1179
}
86✔
1180

1181
static StatsMap toStatsMap(const string& name, const pdns::Histogram& histogram4, const pdns::Histogram& histogram6)
1182
{
86✔
1183
  const string pbasename = getPrometheusName(name);
86✔
1184
  StatsMap entries;
86✔
1185
  char buf[32];
86✔
1186
  std::string pname;
86✔
1187

1188
  const auto& data4 = histogram4.getCumulativeBuckets();
86✔
1189
  for (const auto& bucket : data4) {
1,204✔
1190
    snprintf(buf, sizeof(buf), "%g", bucket.d_boundary / 1e6);
1,204✔
1191
    pname = pbasename + "seconds_bucket{ipversion=\"v4\",le=\"" + (bucket.d_boundary == std::numeric_limits<uint64_t>::max() ? "+Inf" : buf) + "\"}";
1,204✔
1192
    entries.emplace(bucket.d_name + "4", StatsMapEntry{pname, std::to_string(bucket.d_count)});
1,204✔
1193
  }
1,204✔
1194
  snprintf(buf, sizeof(buf), "%g", histogram4.getSum() / 1e6);
86✔
1195
  entries.emplace(name + "sum4", StatsMapEntry{pbasename + "seconds_sum{ipversion=\"v4\"}", buf});
86✔
1196
  entries.emplace(name + "count4", StatsMapEntry{pbasename + "seconds_count{ipversion=\"v4\"}", std::to_string(data4.back().d_count)});
86✔
1197

1198
  const auto& data6 = histogram6.getCumulativeBuckets();
86✔
1199
  for (const auto& bucket : data6) {
1,204✔
1200
    snprintf(buf, sizeof(buf), "%g", bucket.d_boundary / 1e6);
1,204✔
1201
    pname = pbasename + "seconds_bucket{ipversion=\"v6\",le=\"" + (bucket.d_boundary == std::numeric_limits<uint64_t>::max() ? "+Inf" : buf) + "\"}";
1,204✔
1202
    entries.emplace(bucket.d_name + "6", StatsMapEntry{pname, std::to_string(bucket.d_count)});
1,204✔
1203
  }
1,204✔
1204
  snprintf(buf, sizeof(buf), "%g", histogram6.getSum() / 1e6);
86✔
1205
  entries.emplace(name + "sum6", StatsMapEntry{pbasename + "seconds_sum{ipversion=\"v6\"}", buf});
86✔
1206
  entries.emplace(name + "count6", StatsMapEntry{pbasename + "seconds_count{ipversion=\"v6\"}", std::to_string(data6.back().d_count)});
86✔
1207

1208
  return entries;
86✔
1209
}
86✔
1210

1211
static StatsMap toAuthRCodeStatsMap(const string& name)
1212
{
188✔
1213
  const string pbasename = getPrometheusName(name);
188✔
1214
  StatsMap entries;
188✔
1215

1216
  uint8_t n = 0;
188✔
1217
  auto rcodes = g_Counters.sum(rec::RCode::auth).rcodeCounters;
188✔
1218
  for (const auto& entry : rcodes) {
3,008✔
1219
    const auto key = RCode::to_short_s(n);
3,008✔
1220
    std::string pname = pbasename + "{rcode=\"" + key + "\"}";
3,008✔
1221
    entries.emplace("auth-" + key + "-answers", StatsMapEntry{std::move(pname), std::to_string(entry)});
3,008✔
1222
    n++;
3,008✔
1223
  }
3,008✔
1224
  return entries;
188✔
1225
}
188✔
1226

1227
static StatsMap toCPUStatsMap(const string& name)
1228
{
124✔
1229
  const string pbasename = getPrometheusName(name);
124✔
1230
  StatsMap entries;
124✔
1231

1232
  // Handler is not reported
1233
  for (unsigned int n = 0; n < RecThreadInfo::numRecursorThreads() - 1; ++n) {
737✔
1234
    uint64_t tm = doGetThreadCPUMsec(n);
613✔
1235
    std::string pname = pbasename + "{thread=\"" + std::to_string(n) + "\"}";
613✔
1236
    entries.emplace(name + "-thread-" + std::to_string(n), StatsMapEntry{std::move(pname), std::to_string(tm)});
613✔
1237
  }
613✔
1238
  return entries;
124✔
1239
}
124✔
1240

1241
static StatsMap toRPZStatsMap(const string& name, const std::unordered_map<std::string, uint64_t>& map)
1242
{
86✔
1243
  const string pbasename = getPrometheusName(name);
86✔
1244
  StatsMap entries;
86✔
1245

1246
  uint64_t total = 0;
86✔
1247
  for (const auto& entry : map) {
86!
1248
    const auto& key = entry.first;
×
1249
    auto count = entry.second;
×
1250
    std::string sname, pname;
×
1251
    if (key.empty()) {
×
1252
      sname = name + "-filter";
×
1253
      pname = pbasename + "{type=\"filter\"}";
×
1254
    }
×
1255
    else {
×
1256
      sname = name + "-rpz-" + key;
×
1257
      pname = pbasename + "{type=\"rpz\",policyname=\"" + key + "\"}";
×
1258
    }
×
1259
    entries.emplace(sname, StatsMapEntry{std::move(pname), std::to_string(count)});
×
1260
    total += count;
×
1261
  }
×
1262
  entries.emplace(name, StatsMapEntry{pbasename, std::to_string(total)});
86✔
1263
  return entries;
86✔
1264
}
86✔
1265

1266
static StatsMap toProxyMappingStatsMap(const string& name)
1267
{
86✔
1268
  const string pbasename = getPrometheusName(name);
86✔
1269
  StatsMap entries;
86✔
1270

1271
  auto proxyMappingStats = broadcastAccFunction<ProxyMappingStats_t>(pleaseGetProxyMappingStats);
86✔
1272
  size_t count = 0;
86✔
1273
  for (const auto& [key, entry] : proxyMappingStats) {
86!
1274
    auto keyname = pbasename + "{netmask=\"" + key.toString() + "\",count=\"";
×
1275
    auto sname1 = name + "-n-" + std::to_string(count);
×
1276
    auto pname1 = keyname + "netmaskmatches\"}";
×
1277
    entries.emplace(sname1, StatsMapEntry{std::move(pname1), std::to_string(entry.netmaskMatches)});
×
1278
    auto sname2 = name + "-s-" + std::to_string(count);
×
1279
    auto pname2 = keyname + "suffixmatches\"}";
×
1280
    entries.emplace(sname2, StatsMapEntry{std::move(pname2), std::to_string(entry.suffixMatches)});
×
1281
    count++;
×
1282
  }
×
1283
  return entries;
86✔
1284
}
86✔
1285

1286
static StatsMap toRemoteLoggerStatsMap(const string& name)
1287
{
86✔
1288
  const auto pbasename = getPrometheusName(name);
86✔
1289
  StatsMap entries;
86✔
1290

1291
  std::vector<std::pair<RemoteLoggerStats_t, std::string>> list;
86✔
1292
  auto stats1 = broadcastAccFunction<RemoteLoggerStats_t>(pleaseGetRemoteLoggerStats);
86✔
1293
  list.emplace_back(stats1, "protobuf");
86✔
1294
  auto stats2 = broadcastAccFunction<RemoteLoggerStats_t>(pleaseGetOutgoingRemoteLoggerStats);
86✔
1295
  list.emplace_back(stats2, "outgoingProtobuf");
86✔
1296
#ifdef HAVE_FSTRM
86✔
1297
  auto stats3 = broadcastAccFunction<RemoteLoggerStats_t>(pleaseGetFramestreamLoggerStats);
86✔
1298
  list.emplace_back(stats3, "dnstapFrameStream");
86✔
1299
  auto stats4 = broadcastAccFunction<RemoteLoggerStats_t>(pleaseGetNODFramestreamLoggerStats);
86✔
1300
  list.emplace_back(stats4, "dnstapNODFrameStream");
86✔
1301
#endif
86✔
1302
  uint64_t count = 0;
86✔
1303
  for (const auto& [stats, type] : list) {
344✔
1304
    for (const auto& [key, entry] : stats) {
344!
1305
      auto keyname = pbasename + "{address=\"" + key + "\",type=\"" + type + "\",count=\"";
×
1306
      auto sname1 = name + "-q-" + std::to_string(count);
×
1307
      auto pname1 = keyname + "queued\"}";
×
1308
      entries.emplace(sname1, StatsMapEntry{std::move(pname1), std::to_string(entry.d_queued)});
×
1309
      auto sname2 = name + "-p-" + std::to_string(count);
×
1310
      auto pname2 = keyname + "pipeFull\"}";
×
1311
      entries.emplace(sname2, StatsMapEntry{std::move(pname2), std::to_string(entry.d_pipeFull)});
×
1312
      auto sname3 = name + "-t-" + std::to_string(count);
×
1313
      auto pname3 = keyname + "tooLarge\"}";
×
1314
      entries.emplace(sname3, StatsMapEntry{std::move(pname3), std::to_string(entry.d_tooLarge)});
×
1315
      auto sname4 = name + "-o-" + std::to_string(count);
×
1316
      auto pname4 = keyname + "otherError\"}";
×
1317
      entries.emplace(sname4, StatsMapEntry{std::move(pname4), std::to_string(entry.d_otherError)});
×
1318
      ++count;
×
1319
    }
×
1320
  }
344✔
1321
  return entries;
86✔
1322
}
86✔
1323

1324
static time_t s_startupTime = time(nullptr);
1325

1326
static void registerAllStats1()
1327
{
153✔
1328
#include "rec-metrics-gen.h"
153!
1329

1330
  /* make sure that the ECS stats are properly initialized */
1331
  SyncRes::clearECSStats();
153✔
1332
  for (size_t idx = 0; idx < SyncRes::s_ecsResponsesBySubnetSize4.size(); idx++) {
5,049✔
1333
    const std::string name = "ecs-v4-response-bits-" + std::to_string(idx + 1);
4,896✔
1334
    addGetStat(name, &(SyncRes::s_ecsResponsesBySubnetSize4.at(idx)));
4,896✔
1335
  }
4,896✔
1336
  for (size_t idx = 0; idx < SyncRes::s_ecsResponsesBySubnetSize6.size(); idx++) {
19,737✔
1337
    const std::string name = "ecs-v6-response-bits-" + std::to_string(idx + 1);
19,584✔
1338
    addGetStat(name, &(SyncRes::s_ecsResponsesBySubnetSize6.at(idx)));
19,584✔
1339
  }
19,584✔
1340

1341
  addGetStat("cumul-clientanswers", []() {
156✔
1342
    return toStatsMap(t_Counters.at(rec::Histogram::cumulativeAnswers).getName(), g_Counters.sum(rec::Histogram::cumulativeAnswers));
86✔
1343
  });
86✔
1344
  addGetStat("cumul-authanswers", []() {
156✔
1345
    return toStatsMap(t_Counters.at(rec::Histogram::cumulativeAuth4Answers).getName(), g_Counters.sum(rec::Histogram::cumulativeAuth4Answers), g_Counters.sum(rec::Histogram::cumulativeAuth6Answers));
86✔
1346
  });
86✔
1347
  addGetStat("policy-hits", []() {
156✔
1348
    return toRPZStatsMap("policy-hits", g_Counters.sum(rec::PolicyNameHits::policyName).counts);
86✔
1349
  });
86✔
1350
  addGetStat("proxy-mapping-total", []() {
156✔
1351
    return toProxyMappingStatsMap("proxy-mapping-total");
86✔
1352
  });
86✔
1353
  addGetStat("auth-rcode-answers", []() {
188✔
1354
    return toAuthRCodeStatsMap("auth-rcode-answers");
188✔
1355
  });
188✔
1356
}
153✔
1357

1358
void registerAllStats()
1359
{
153✔
1360
  try {
153✔
1361
    registerAllStats1();
153✔
1362
  }
153✔
1363
  catch (...) {
153✔
1364
    g_log << Logger::Critical << "Could not add stat entries" << endl;
×
1365
    exit(1);
×
1366
  }
×
1367
}
153✔
1368

1369
static auto clearLuaScript()
1370
{
152✔
1371
  vector<string> empty;
152✔
1372
  empty.emplace_back();
152✔
1373
  return doQueueReloadLuaScript(empty.begin(), empty.end());
152✔
1374
}
152✔
1375

1376
void doExitGeneric(bool nicely)
1377
{
153✔
1378
#if defined(__SANITIZE_THREAD__)
1379
  _exit(0); // regression test check for exit 0
1380
#endif
1381
  g_log << Logger::Error << "Exiting on user request" << endl;
153✔
1382
  g_rcc.~RecursorControlChannel();
153✔
1383

1384
  if (!g_pidfname.empty()) {
153!
1385
    unlink(g_pidfname.c_str()); // we can at least try..
153✔
1386
  }
153✔
1387

1388
  if (nicely) {
153✔
1389
    RecursorControlChannel::stop = true;
1✔
1390
  }
1✔
1391
  else {
152✔
1392
#if defined(__SANITIZE_ADDRESS__) && defined(HAVE_LEAK_SANITIZER_INTERFACE)
152✔
1393
    clearLuaScript();
152✔
1394
    pdns::coverage::dumpCoverageData();
152✔
1395
    __lsan_do_leak_check();
152✔
1396
    _exit(0); // let the regression test distinguish between leaks and no leaks as __lsan_do_leak_check() exits 1 on leaks
152✔
1397
#else
1398
    pdns::coverage::dumpCoverageData();
1399
    _exit(1); // for historic reasons we exit 1
1400
#endif
1401
  }
152✔
1402
}
153✔
1403

1404
void doExit()
1405
{
152✔
1406
  doExitGeneric(false);
152✔
1407
}
152✔
1408

1409
void doExitNicely()
1410
{
1✔
1411
  doExitGeneric(true);
1✔
1412
}
1✔
1413

1414
vector<pair<DNSName, uint16_t>>* pleaseGetQueryRing()
1415
{
×
1416
  typedef pair<DNSName, uint16_t> query_t;
×
1417
  vector<query_t>* ret = new vector<query_t>();
×
1418
  if (!t_queryring)
×
1419
    return ret;
×
1420
  ret->reserve(t_queryring->size());
×
1421

1422
  for (const query_t& q : *t_queryring) {
×
1423
    ret->push_back(q);
×
1424
  }
×
1425
  return ret;
×
1426
}
×
1427
vector<pair<DNSName, uint16_t>>* pleaseGetServfailQueryRing()
1428
{
×
1429
  typedef pair<DNSName, uint16_t> query_t;
×
1430
  vector<query_t>* ret = new vector<query_t>();
×
1431
  if (!t_servfailqueryring)
×
1432
    return ret;
×
1433
  ret->reserve(t_servfailqueryring->size());
×
1434
  for (const query_t& q : *t_servfailqueryring) {
×
1435
    ret->push_back(q);
×
1436
  }
×
1437
  return ret;
×
1438
}
×
1439
vector<pair<DNSName, uint16_t>>* pleaseGetBogusQueryRing()
1440
{
×
1441
  typedef pair<DNSName, uint16_t> query_t;
×
1442
  vector<query_t>* ret = new vector<query_t>();
×
1443
  if (!t_bogusqueryring)
×
1444
    return ret;
×
1445
  ret->reserve(t_bogusqueryring->size());
×
1446
  for (const query_t& q : *t_bogusqueryring) {
×
1447
    ret->push_back(q);
×
1448
  }
×
1449
  return ret;
×
1450
}
×
1451

1452
typedef std::function<vector<ComboAddress>*()> pleaseremotefunc_t;
1453
typedef std::function<vector<pair<DNSName, uint16_t>>*()> pleasequeryfunc_t;
1454

1455
vector<ComboAddress>* pleaseGetRemotes()
1456
{
16✔
1457
  vector<ComboAddress>* ret = new vector<ComboAddress>();
16✔
1458
  if (!t_remotes)
16!
1459
    return ret;
×
1460

1461
  ret->reserve(t_remotes->size());
16✔
1462
  for (const ComboAddress& ca : *t_remotes) {
58✔
1463
    ret->push_back(ca);
58✔
1464
  }
58✔
1465
  return ret;
16✔
1466
}
16✔
1467

1468
vector<ComboAddress>* pleaseGetServfailRemotes()
1469
{
×
1470
  vector<ComboAddress>* ret = new vector<ComboAddress>();
×
1471
  if (!t_servfailremotes)
×
1472
    return ret;
×
1473
  ret->reserve(t_servfailremotes->size());
×
1474
  for (const ComboAddress& ca : *t_servfailremotes) {
×
1475
    ret->push_back(ca);
×
1476
  }
×
1477
  return ret;
×
1478
}
×
1479

1480
vector<ComboAddress>* pleaseGetBogusRemotes()
1481
{
×
1482
  vector<ComboAddress>* ret = new vector<ComboAddress>();
×
1483
  if (!t_bogusremotes)
×
1484
    return ret;
×
1485
  ret->reserve(t_bogusremotes->size());
×
1486
  for (const ComboAddress& ca : *t_bogusremotes) {
×
1487
    ret->push_back(ca);
×
1488
  }
×
1489
  return ret;
×
1490
}
×
1491

1492
vector<ComboAddress>* pleaseGetLargeAnswerRemotes()
1493
{
×
1494
  vector<ComboAddress>* ret = new vector<ComboAddress>();
×
1495
  if (!t_largeanswerremotes)
×
1496
    return ret;
×
1497
  ret->reserve(t_largeanswerremotes->size());
×
1498
  for (const ComboAddress& ca : *t_largeanswerremotes) {
×
1499
    ret->push_back(ca);
×
1500
  }
×
1501
  return ret;
×
1502
}
×
1503

1504
vector<ComboAddress>* pleaseGetTimeouts()
1505
{
×
1506
  vector<ComboAddress>* ret = new vector<ComboAddress>();
×
1507
  if (!t_timeouts)
×
1508
    return ret;
×
1509
  ret->reserve(t_timeouts->size());
×
1510
  for (const ComboAddress& ca : *t_timeouts) {
×
1511
    ret->push_back(ca);
×
1512
  }
×
1513
  return ret;
×
1514
}
×
1515

1516
static string doGenericTopRemotes(const pleaseremotefunc_t& func)
1517
{
×
1518
  auto remotes = broadcastAccFunction<vector<ComboAddress>>(func);
×
1519
  const unsigned int total = remotes.size();
×
1520
  if (total == 0) {
×
1521
    return "No qualifying data available\n";
×
1522
  }
×
1523

1524
  std::map<ComboAddress, unsigned int, ComboAddress::addressOnlyLessThan> counts;
×
1525
  for (const auto& address : remotes) {
×
1526
    counts[address]++;
×
1527
  }
×
1528

1529
  std::multimap<unsigned int, ComboAddress> rcounts;
×
1530
  for (const auto& count : counts) {
×
1531
    rcounts.emplace(count.second, count.first);
×
1532
  }
×
1533

1534
  ostringstream ret;
×
1535
  ret << "Over last " << total << " entries:\n";
×
1536
  boost::format fmt("%.02f%%\t%s\n");
×
1537
  unsigned int limit = 0;
×
1538
  unsigned int accounted = 0;
×
1539
  for (auto i = rcounts.rbegin(); i != rcounts.rend() && limit < 20; ++i, ++limit) {
×
1540
    ret << fmt % (100.0 * i->first / total) % i->second.toString();
×
1541
    accounted += i->first;
×
1542
  }
×
1543
  ret << '\n'
×
1544
      << fmt % (100.0 * (total - accounted) / total) % "rest";
×
1545
  return ret.str();
×
1546
}
×
1547

1548
// XXX DNSName Pain - this function should benefit from native DNSName methods
1549
DNSName getRegisteredName(const DNSName& dom)
1550
{
×
1551
  auto parts = dom.getRawLabels();
×
1552
  if (parts.size() <= 2)
×
1553
    return dom;
×
1554
  reverse(parts.begin(), parts.end());
×
1555
  for (string& str : parts) {
×
1556
    str = toLower(str);
×
1557
  };
×
1558

1559
  // uk co migweb
1560
  string last;
×
1561
  while (!parts.empty()) {
×
1562
    if (parts.size() == 1 || binary_search(g_pubs.begin(), g_pubs.end(), parts)) {
×
1563

1564
      string ret = last;
×
1565
      if (!ret.empty())
×
1566
        ret += ".";
×
1567

1568
      for (auto p = parts.crbegin(); p != parts.crend(); ++p) {
×
1569
        ret += (*p) + ".";
×
1570
      }
×
1571
      return DNSName(ret);
×
1572
    }
×
1573

1574
    last = parts[parts.size() - 1];
×
1575
    parts.resize(parts.size() - 1);
×
1576
  }
×
1577
  return DNSName("??");
×
1578
}
×
1579

1580
static DNSName nopFilter(const DNSName& name)
1581
{
×
1582
  return name;
×
1583
}
×
1584

1585
static string doGenericTopQueries(const pleasequeryfunc_t& func, const std::function<DNSName(const DNSName&)>& filter = nopFilter)
1586
{
×
1587
  using query_t = pair<DNSName, uint16_t>;
×
1588
  auto queries = broadcastAccFunction<vector<query_t>>(func);
×
1589
  const unsigned int total = queries.size();
×
1590
  if (total == 0) {
×
1591
    return "No qualifying data available\n";
×
1592
  }
×
1593

1594
  map<query_t, unsigned int> counts;
×
1595
  for (const auto& query : queries) {
×
1596
    counts[pair(filter(query.first), query.second)]++;
×
1597
  }
×
1598

1599
  std::multimap<unsigned int, query_t> rcounts;
×
1600
  for (const auto& count : counts) {
×
1601
    rcounts.emplace(count.second, count.first);
×
1602
  }
×
1603

1604
  ostringstream ret;
×
1605
  ret << "Over last " << total << " entries:\n";
×
1606
  boost::format fmt("%.02f%%\t%s\n");
×
1607
  unsigned int limit = 0;
×
1608
  unsigned int accounted = 0;
×
1609
  for (auto i = rcounts.rbegin(); i != rcounts.rend() && limit < 20; ++i, ++limit) {
×
1610
    ret << fmt % (100.0 * i->first / total) % (i->second.first.toLogString() + "|" + DNSRecordContent::NumberToType(i->second.second));
×
1611
    accounted += i->first;
×
1612
  }
×
1613
  ret << '\n'
×
1614
      << fmt % (100.0 * (total - accounted) / total) % "rest";
×
1615

1616
  return ret.str();
×
1617
}
×
1618

1619
static string* nopFunction()
1620
{
87✔
1621
  return new string("pong " + RecThreadInfo::self().getName() + '\n');
87✔
1622
}
87✔
1623

1624
static string getDontThrottleNames()
1625
{
×
1626
  auto dtn = g_dontThrottleNames.getLocal();
×
1627
  return dtn->toString() + "\n";
×
1628
}
×
1629

1630
static string getDontThrottleNetmasks()
1631
{
×
1632
  auto dtn = g_dontThrottleNetmasks.getLocal();
×
1633
  return dtn->toString() + "\n";
×
1634
}
×
1635

1636
template <typename T>
1637
static string addDontThrottleNames(T begin, T end)
1638
{
×
1639
  if (begin == end) {
×
1640
    return "No names specified, keeping existing list\n";
×
1641
  }
×
1642
  vector<DNSName> toAdd;
×
1643
  while (begin != end) {
×
1644
    try {
×
1645
      auto d = DNSName(*begin);
×
1646
      toAdd.push_back(d);
×
1647
    }
×
1648
    catch (const std::exception& e) {
×
1649
      return "Problem parsing '" + *begin + "': " + e.what() + ", nothing added\n";
×
1650
    }
×
1651
    begin++;
×
1652
  }
×
1653

1654
  string ret = "Added";
×
1655
  auto dnt = g_dontThrottleNames.getCopy();
×
1656
  bool first = true;
×
1657
  for (auto const& d : toAdd) {
×
1658
    if (!first) {
×
1659
      ret += ",";
×
1660
    }
×
1661
    first = false;
×
1662
    ret += " " + d.toLogString();
×
1663
    dnt.add(d);
×
1664
  }
×
1665

1666
  g_dontThrottleNames.setState(std::move(dnt));
×
1667

1668
  ret += " to the list of nameservers that may not be throttled";
×
1669
  g_log << Logger::Info << ret << ", requested via control channel" << endl;
×
1670
  return ret + "\n";
×
1671
}
×
1672

1673
template <typename T>
1674
static string addDontThrottleNetmasks(T begin, T end)
1675
{
×
1676
  if (begin == end) {
×
1677
    return "No netmasks specified, keeping existing list\n";
×
1678
  }
×
1679
  vector<Netmask> toAdd;
×
1680
  while (begin != end) {
×
1681
    try {
×
1682
      auto n = Netmask(*begin);
×
1683
      toAdd.push_back(n);
×
1684
    }
×
1685
    catch (const std::exception& e) {
×
1686
      return "Problem parsing '" + *begin + "': " + e.what() + ", nothing added\n";
×
1687
    }
×
1688
    catch (const PDNSException& e) {
×
1689
      return "Problem parsing '" + *begin + "': " + e.reason + ", nothing added\n";
×
1690
    }
×
1691
    begin++;
×
1692
  }
×
1693

1694
  string ret = "Added";
×
1695
  auto dnt = g_dontThrottleNetmasks.getCopy();
×
1696
  bool first = true;
×
1697
  for (auto const& t : toAdd) {
×
1698
    if (!first) {
×
1699
      ret += ",";
×
1700
    }
×
1701
    first = false;
×
1702
    ret += " " + t.toString();
×
1703
    dnt.addMask(t);
×
1704
  }
×
1705

1706
  g_dontThrottleNetmasks.setState(std::move(dnt));
×
1707

1708
  ret += " to the list of nameserver netmasks that may not be throttled";
×
1709
  g_log << Logger::Info << ret << ", requested via control channel" << endl;
×
1710
  return ret + "\n";
×
1711
}
×
1712

1713
template <typename T>
1714
static string clearDontThrottleNames(T begin, T end)
1715
{
×
1716
  if (begin == end)
×
1717
    return "No names specified, doing nothing.\n";
×
1718

1719
  if (begin + 1 == end && *begin == "*") {
×
1720
    SuffixMatchNode smn;
×
1721
    g_dontThrottleNames.setState(std::move(smn));
×
1722
    string ret = "Cleared list of nameserver names that may not be throttled";
×
1723
    g_log << Logger::Warning << ret << ", requested via control channel" << endl;
×
1724
    return ret + "\n";
×
1725
  }
×
1726

1727
  vector<DNSName> toRemove;
×
1728
  while (begin != end) {
×
1729
    try {
×
1730
      if (*begin == "*") {
×
1731
        return "Please don't mix '*' with other names, nothing removed\n";
×
1732
      }
×
1733
      toRemove.push_back(DNSName(*begin));
×
1734
    }
×
1735
    catch (const std::exception& e) {
×
1736
      return "Problem parsing '" + *begin + "': " + e.what() + ", nothing removed\n";
×
1737
    }
×
1738
    begin++;
×
1739
  }
×
1740

1741
  string ret = "Removed";
×
1742
  bool first = true;
×
1743
  auto dnt = g_dontThrottleNames.getCopy();
×
1744
  for (const auto& name : toRemove) {
×
1745
    if (!first) {
×
1746
      ret += ",";
×
1747
    }
×
1748
    first = false;
×
1749
    ret += " " + name.toLogString();
×
1750
    dnt.remove(name);
×
1751
  }
×
1752

1753
  g_dontThrottleNames.setState(std::move(dnt));
×
1754

1755
  ret += " from the list of nameservers that may not be throttled";
×
1756
  g_log << Logger::Info << ret << ", requested via control channel" << endl;
×
1757
  return ret + "\n";
×
1758
}
×
1759

1760
template <typename T>
1761
static string clearDontThrottleNetmasks(T begin, T end)
1762
{
×
1763
  if (begin == end)
×
1764
    return "No netmasks specified, doing nothing.\n";
×
1765

1766
  if (begin + 1 == end && *begin == "*") {
×
1767
    auto nmg = g_dontThrottleNetmasks.getCopy();
×
1768
    nmg.clear();
×
1769
    g_dontThrottleNetmasks.setState(std::move(nmg));
×
1770

1771
    string ret = "Cleared list of nameserver addresses that may not be throttled";
×
1772
    g_log << Logger::Warning << ret << ", requested via control channel" << endl;
×
1773
    return ret + "\n";
×
1774
  }
×
1775

1776
  std::vector<Netmask> toRemove;
×
1777
  while (begin != end) {
×
1778
    try {
×
1779
      if (*begin == "*") {
×
1780
        return "Please don't mix '*' with other netmasks, nothing removed\n";
×
1781
      }
×
1782
      auto n = Netmask(*begin);
×
1783
      toRemove.push_back(n);
×
1784
    }
×
1785
    catch (const std::exception& e) {
×
1786
      return "Problem parsing '" + *begin + "': " + e.what() + ", nothing added\n";
×
1787
    }
×
1788
    catch (const PDNSException& e) {
×
1789
      return "Problem parsing '" + *begin + "': " + e.reason + ", nothing added\n";
×
1790
    }
×
1791
    begin++;
×
1792
  }
×
1793

1794
  string ret = "Removed";
×
1795
  bool first = true;
×
1796
  auto dnt = g_dontThrottleNetmasks.getCopy();
×
1797
  for (const auto& mask : toRemove) {
×
1798
    if (!first) {
×
1799
      ret += ",";
×
1800
    }
×
1801
    first = false;
×
1802
    ret += " " + mask.toString();
×
1803
    dnt.deleteMask(mask);
×
1804
  }
×
1805

1806
  g_dontThrottleNetmasks.setState(std::move(dnt));
×
1807

1808
  ret += " from the list of nameservers that may not be throttled";
×
1809
  g_log << Logger::Info << ret << ", requested via control channel" << endl;
×
1810
  return ret + "\n";
×
1811
}
×
1812

1813
template <typename T>
1814
static string setEventTracing(T begin, T end)
1815
{
×
1816
  if (begin == end) {
×
1817
    return "No event trace enabled value specified\n";
×
1818
  }
×
1819
  try {
×
1820
    pdns::checked_stoi_into(SyncRes::s_event_trace_enabled, *begin);
×
1821
    return "New event trace enabled value: " + std::to_string(SyncRes::s_event_trace_enabled) + "\n";
×
1822
  }
×
1823
  catch (const std::exception& e) {
×
1824
    return "Error parsing the new event trace enabled value: " + std::string(e.what()) + "\n";
×
1825
  }
×
1826
}
×
1827

1828
static void* pleaseSupplantProxyMapping(const ProxyMapping& pm)
1829
{
×
1830
  if (pm.empty()) {
×
1831
    t_proxyMapping = nullptr;
×
1832
  }
×
1833
  else {
×
1834
    // Copy the existing stats values, for the new config items also present in the old
1835
    auto newmapping = make_unique<ProxyMapping>();
×
1836
    for (const auto& [nm, entry] : pm) {
×
1837
      auto& newentry = newmapping->insert(nm);
×
1838
      newentry.second = entry;
×
1839
      if (t_proxyMapping) {
×
1840
        if (const auto* existing = t_proxyMapping->lookup(nm); existing != nullptr) {
×
1841
          newentry.second.stats = existing->second.stats;
×
1842
        }
×
1843
      }
×
1844
    }
×
1845
    t_proxyMapping = std::move(newmapping);
×
1846
  }
×
1847
  return nullptr;
×
1848
}
×
1849

1850
static RecursorControlChannel::Answer help()
1851
{
×
1852
  return {0,
×
1853
          "add-dont-throttle-names [N...]   add names that are not allowed to be throttled\n"
×
1854
          "add-dont-throttle-netmasks [N...]\n"
×
1855
          "                                 add netmasks that are not allowed to be throttled\n"
×
1856
          "add-nta DOMAIN [REASON]          add a Negative Trust Anchor for DOMAIN with the comment REASON\n"
×
1857
          "add-ta DOMAIN DSRECORD           add a Trust Anchor for DOMAIN with data DSRECORD\n"
×
1858
          "current-queries                  show currently active queries\n"
×
1859
          "clear-dont-throttle-names [N...] remove names that are not allowed to be throttled. If N is '*', remove all\n"
×
1860
          "clear-dont-throttle-netmasks [N...]\n"
×
1861
          "                                 remove netmasks that are not allowed to be throttled. If N is '*', remove all\n"
×
1862
          "clear-nta [DOMAIN]...            Clear the Negative Trust Anchor for DOMAINs, if no DOMAIN is specified, remove all\n"
×
1863
          "clear-ta [DOMAIN]...             Clear the Trust Anchor for DOMAINs\n"
×
1864
          "dump-cache <filename> [type...]  dump cache contents to the named file, type is r, n, p or a\n"
×
1865
          "dump-dot-probe-map <filename>    dump the contents of the DoT probe map to the named file\n"
×
1866
          "dump-edns [status] <filename>    dump EDNS status to the named file\n"
×
1867
          "dump-failedservers <filename>    dump the failed servers to the named file\n"
×
1868
          "dump-non-resolving <filename>    dump non-resolving nameservers addresses to the named file\n"
×
1869
          "dump-nsspeeds <filename>         dump nsspeeds statistics to the named file\n"
×
1870
          "dump-saved-parent-ns-sets <filename>\n"
×
1871
          "                                 dump saved parent ns sets that were successfully used as fallback\n"
×
1872
          "dump-rpz <zone name> <filename>  dump the content of a RPZ zone to the named file\n"
×
1873
          "dump-throttlemap <filename>      dump the contents of the throttle map to the named file\n"
×
1874
          "get [key1] [key2] ..             get specific statistics\n"
×
1875
          "get-all                          get all statistics\n"
×
1876
          "get-dont-throttle-names          get the list of names that are not allowed to be throttled\n"
×
1877
          "get-dont-throttle-netmasks       get the list of netmasks that are not allowed to be throttled\n"
×
1878
          "get-ntas                         get all configured Negative Trust Anchors\n"
×
1879
          "get-tas                          get all configured Trust Anchors\n"
×
1880
          "get-parameter [key1] [key2] ..   get configuration parameters\n"
×
1881
          "get-proxymapping-stats           get proxy mapping statistics\n"
×
1882
          "get-qtypelist                    get QType statistics\n"
×
1883
          "                                 notice: queries from cache aren't being counted yet\n"
×
1884
          "get-remotelogger-stats           get remote logger statistics\n"
×
1885
          "hash-password [work-factor]      ask for a password then return the hashed version\n"
×
1886
          "help                             get this list (from the running recursor)\n"
×
1887
          "list-dnssec-algos                list supported DNSSEC algorithms\n"
×
1888
          "ping                             check that all threads are alive\n"
×
1889
          "quit                             stop the recursor daemon\n"
×
1890
          "quit-nicely                      stop the recursor daemon nicely\n"
×
1891
          "reload-acls                      reload ACLS\n"
×
1892
          "reload-lua-script [filename]     (re)load Lua script\n"
×
1893
          "reload-yaml                      Reload runtime settable parts of YAML settings\n"
×
1894
          "reload-lua-config [filename]     (re)load Lua configuration file or equivalent YAML clauses\n"
×
1895
          "reload-zones                     reload all auth and forward zones\n"
×
1896
          "set-ecs-minimum-ttl value        set ecs-minimum-ttl-override\n"
×
1897
          "set-max-aggr-nsec-cache-size value set new maximum aggressive NSEC cache size\n"
×
1898
          "set-max-cache-entries value      set new maximum record cache size\n"
×
1899
          "set-max-packetcache-entries val  set new maximum packet cache size\n"
×
1900
          "set-minimum-ttl value            set minimum-ttl-override\n"
×
1901
          "set-carbon-server                set a carbon server for telemetry\n"
×
1902
          "set-dnssec-log-bogus SETTING     enable (SETTING=yes) or disable (SETTING=no) logging of DNSSEC validation failures\n"
×
1903
          "set-event-trace-enabled SETTING  set logging of event trace messages, 0 = disabled, 1 = protobuf, 2 = log file, 3 = both\n"
×
1904
          "show-yaml [file]                 show yaml config derived from old-style config\n"
×
1905
          "trace-regex [regex file]         emit resolution trace for matching queries (no arguments clears tracing)\n"
×
1906
          "top-largeanswer-remotes          show top remotes receiving large answers\n"
×
1907
          "top-queries                      show top queries\n"
×
1908
          "top-pub-queries                  show top queries grouped by public suffix list\n"
×
1909
          "top-remotes                      show top remotes\n"
×
1910
          "top-timeouts                     show top downstream timeouts\n"
×
1911
          "top-servfail-queries             show top queries receiving servfail answers\n"
×
1912
          "top-bogus-queries                show top queries validating as bogus\n"
×
1913
          "top-pub-servfail-queries         show top queries receiving servfail answers grouped by public suffix list\n"
×
1914
          "top-pub-bogus-queries            show top queries validating as bogus grouped by public suffix list\n"
×
1915
          "top-servfail-remotes             show top remotes receiving servfail answers\n"
×
1916
          "top-bogus-remotes                show top remotes receiving bogus answers\n"
×
1917
          "unload-lua-script                unload Lua script\n"
×
1918
          "version                          return version number of running Recursor\n"
×
1919
          "wipe-cache domain0 [domain1] ..  wipe domain data from cache\n"
×
1920
          "wipe-cache-typed type domain0 [domain1] ..  wipe domain data with qtype from cache\n"};
×
1921
}
×
1922

1923
RecursorControlChannel::Answer luaconfig(bool broadcast)
1924
{
153✔
1925
  ProxyMapping proxyMapping;
153✔
1926
  LuaConfigItems lci;
153✔
1927
  lci.d_slog = g_slog;
153✔
1928
  extern std::unique_ptr<ProxyMapping> g_proxyMapping;
153✔
1929
  if (!g_luaSettingsInYAML) {
153✔
1930
    try {
150✔
1931
      if (::arg()["lua-config-file"].empty()) {
150✔
1932
        return {0, "No Lua or corresponding YAML configuration active\n"};
16✔
1933
      }
16✔
1934
      loadRecursorLuaConfig(::arg()["lua-config-file"], proxyMapping, lci);
134✔
1935
      activateLuaConfig(lci);
134✔
1936
      lci = g_luaconfs.getCopy();
134✔
1937
      if (broadcast) {
134!
1938
        startLuaConfigDelayedThreads(lci, lci.generation);
×
1939
        broadcastFunction([=] { return pleaseSupplantProxyMapping(proxyMapping); });
×
1940
      }
×
1941
      else {
134✔
1942
        // Initial proxy mapping
1943
        g_proxyMapping = proxyMapping.empty() ? nullptr : std::make_unique<ProxyMapping>(proxyMapping);
134✔
1944
      }
134✔
1945
      if (broadcast) {
134!
1946
        SLOG(g_log << Logger::Notice << "Reloaded Lua configuration file '" << ::arg()["lua-config-file"] << "', requested via control channel" << endl,
×
1947
             g_slog->withName("config")->info(Logr::Info, "Reloaded"));
×
1948
      }
×
1949
      return {0, "Reloaded Lua configuration file '" + ::arg()["lua-config-file"] + "'\n"};
134✔
1950
    }
150✔
1951
    catch (std::exception& e) {
150✔
1952
      return {1, "Unable to load Lua script from '" + ::arg()["lua-config-file"] + "': " + e.what() + "\n"};
×
1953
    }
×
1954
    catch (const PDNSException& e) {
150✔
1955
      return {1, "Unable to load Lua script from '" + ::arg()["lua-config-file"] + "': " + e.reason + "\n"};
×
1956
    }
×
1957
  }
150✔
1958
  try {
3✔
1959
    string configname = ::arg()["config-dir"] + "/recursor";
3✔
1960
    if (!::arg()["config-name"].empty()) {
3!
1961
      configname = ::arg()["config-dir"] + "/recursor-" + ::arg()["config-name"];
×
1962
    }
×
1963
    bool dummy1{};
3✔
1964
    bool dummy2{};
3✔
1965
    pdns::rust::settings::rec::Recursorsettings settings;
3✔
1966
    auto yamlstat = pdns::settings::rec::tryReadYAML(configname + g_yamlSettingsSuffix, false, dummy1, dummy2, settings, g_slog);
3✔
1967
    if (yamlstat != pdns::settings::rec::YamlSettingsStatus::OK) {
3!
1968
      return {1, "Not reloading dynamic part of YAML configuration\n"};
×
1969
    }
×
1970
    auto generation = g_luaconfs.getLocal()->generation;
3✔
1971
    lci.generation = generation + 1;
3✔
1972
    pdns::settings::rec::fromBridgeStructToLuaConfig(settings, lci, proxyMapping);
3✔
1973
    activateLuaConfig(lci);
3✔
1974
    lci = g_luaconfs.getCopy();
3✔
1975
    if (broadcast) {
3!
1976
      startLuaConfigDelayedThreads(lci, lci.generation);
×
1977
      broadcastFunction([pmap = std::move(proxyMapping)] { return pleaseSupplantProxyMapping(pmap); });
×
1978
    }
×
1979
    else {
3✔
1980
      // Initial proxy mapping
1981
      g_proxyMapping = proxyMapping.empty() ? nullptr : std::make_unique<ProxyMapping>(proxyMapping);
3!
1982
    }
3✔
1983

1984
    return {0, "Reloaded dynamic part of YAML configuration\n"};
3✔
1985
  }
3✔
1986
  catch (std::exception& e) {
3✔
1987
    return {1, "Unable to reload dynamic YAML changes: " + std::string(e.what()) + "\n"};
×
1988
  }
×
1989
  catch (const PDNSException& e) {
3✔
1990
    return {1, "Unable to reload dynamic YAML changes: " + e.reason + "\n"};
×
1991
  }
×
1992
}
3✔
1993

1994
template <typename T>
1995
static RecursorControlChannel::Answer luaconfig(T begin, T end)
1996
{
×
1997
  if (begin != end) {
×
1998
    if (g_luaSettingsInYAML) {
×
1999
      return {1, "Unable to reload Lua script from '" + *begin + "' as there is no active Lua configuration\n"};
×
2000
    }
×
2001
    ::arg().set("lua-config-file") = *begin;
×
2002
  }
×
2003
  return luaconfig(true);
×
2004
}
×
2005

2006
static RecursorControlChannel::Answer reloadACLs()
2007
{
×
2008
  if (!::arg()["chroot"].empty()) {
×
2009
    g_log << Logger::Error << "Unable to reload ACL when chroot()'ed, requested via control channel" << endl;
×
2010
    return {1, "Unable to reload ACL when chroot()'ed, please restart\n"};
×
2011
  }
×
2012

2013
  try {
×
2014
    parseACLs();
×
2015
  }
×
2016
  catch (std::exception& e) {
×
2017
    g_log << Logger::Error << "Reloading ACLs failed (Exception: " << e.what() << ")" << endl;
×
2018
    return {1, e.what() + string("\n")};
×
2019
  }
×
2020
  catch (PDNSException& ae) {
×
2021
    g_log << Logger::Error << "Reloading ACLs failed (PDNSException: " << ae.reason << ")" << endl;
×
2022
    return {1, ae.reason + string("\n")};
×
2023
  }
×
2024
  return {0, "ok\n"};
×
2025
}
×
2026

2027
static std::string reloadZoneConfigurationWithSysResolveReset()
2028
{
×
2029
  auto& sysResolver = pdns::RecResolve::getInstance();
×
2030
  sysResolver.stopRefresher();
×
2031
  sysResolver.wipe();
×
2032
  auto ret = reloadZoneConfiguration(g_yamlSettings);
×
2033
  sysResolver.startRefresher();
×
2034
  return ret;
×
2035
}
×
2036

2037
RecursorControlChannel::Answer RecursorControlParser::getAnswer(int socket, const string& question, RecursorControlParser::func_t** command)
2038
{
464✔
2039
  *command = nop;
464✔
2040
  vector<string> words;
464✔
2041
  stringtok(words, question);
464✔
2042

2043
  if (words.empty()) {
464!
2044
    return {1, "invalid command\n"};
×
2045
  }
×
2046

2047
  string cmd = toLower(words[0]);
464✔
2048
  auto begin = words.begin() + 1;
464✔
2049
  auto end = words.end();
464✔
2050

2051
  // should probably have a smart dispatcher here, like auth has
2052
  if (cmd == "help") {
464!
2053
    return help();
×
2054
  }
×
2055
  if (cmd == "get-all") {
464✔
2056
    return {0, getAllStats()};
36✔
2057
  }
36✔
2058
  if (cmd == "get") {
428✔
2059
    return {0, doGet(begin, end)};
4✔
2060
  }
4✔
2061
  if (cmd == "get-parameter") {
424!
2062
    return {0, doGetParameter(begin, end)};
×
2063
  }
×
2064
  if (cmd == "quit") {
424!
2065
    *command = &doExit;
×
2066
    return {0, "bye\n"};
×
2067
  }
×
2068
  if (cmd == "version") {
424!
2069
    return {0, getPDNSVersion() + "\n"};
×
2070
  }
×
2071
  if (cmd == "quit-nicely") {
424✔
2072
    *command = &doExitNicely;
1✔
2073
    return {0, "bye nicely\n"};
1✔
2074
  }
1✔
2075
  if (cmd == "dump-cache") {
423✔
2076
    return doDumpCache(socket, begin, end);
5✔
2077
  }
5✔
2078
  if (cmd == "dump-dot-probe-map") {
418!
2079
    return doDumpToFile(socket, pleaseDumpDoTProbeMap, cmd, false);
×
2080
  }
×
2081
  if (cmd == "dump-ednsstatus" || cmd == "dump-edns") {
418!
2082
    return doDumpToFile(socket, pleaseDumpEDNSMap, cmd, false);
×
2083
  }
×
2084
  if (cmd == "dump-nsspeeds") {
418!
2085
    return doDumpToFile(socket, pleaseDumpNSSpeeds, cmd, false);
×
2086
  }
×
2087
  if (cmd == "dump-failedservers") {
418!
2088
    return doDumpToFile(socket, pleaseDumpFailedServers, cmd, false);
×
2089
  }
×
2090
  if (cmd == "dump-saved-parent-ns-sets") {
418!
2091
    return doDumpToFile(socket, pleaseDumpSavedParentNSSets, cmd, false);
×
2092
  }
×
2093
  if (cmd == "dump-rpz") {
418!
2094
    return doDumpRPZ(socket, begin, end);
×
2095
  }
×
2096
  if (cmd == "dump-throttlemap") {
418!
2097
    return doDumpToFile(socket, pleaseDumpThrottleMap, cmd, false);
×
2098
  }
×
2099
  if (cmd == "dump-non-resolving") {
418!
2100
    return doDumpToFile(socket, pleaseDumpNonResolvingNS, cmd, false);
×
2101
  }
×
2102
  if (cmd == "wipe-cache" || cmd == "flushname") {
418!
2103
    return {0, doWipeCache(begin, end, 0xffff)};
405✔
2104
  }
405✔
2105
  if (cmd == "wipe-cache-typed") {
13!
2106
    if (begin == end) {
×
2107
      return {1, "Need a qtype\n"};
×
2108
    }
×
2109
    uint16_t qtype = QType::chartocode(begin->c_str());
×
2110
    if (qtype == 0) {
×
2111
      return {1, "Unknown qtype " + *begin + "\n"};
×
2112
    }
×
2113
    ++begin;
×
2114
    return {0, doWipeCache(begin, end, qtype)};
×
2115
  }
×
2116
  if (cmd == "reload-lua-script") {
13!
2117
    return doQueueReloadLuaScript(begin, end);
×
2118
  }
×
2119
  if (cmd == "reload-lua-config") {
13!
2120
    return luaconfig(begin, end);
×
2121
  }
×
2122
  if (cmd == "reload-yaml") {
13!
2123
    return luaconfig(begin, end);
×
2124
  }
×
2125
  if (cmd == "set-carbon-server") {
13!
2126
    return {0, doSetCarbonServer(begin, end)};
×
2127
  }
×
2128
  if (cmd == "trace-regex") {
13!
2129
    return {0, doTraceRegex(begin == end ? FDWrapper(-1) : getfd(socket), begin, end)};
×
2130
  }
×
2131
  if (cmd == "unload-lua-script") {
13!
2132
    return clearLuaScript();
×
2133
  }
×
2134
  if (cmd == "reload-acls") {
13!
2135
    return reloadACLs();
×
2136
  }
×
2137
  if (cmd == "top-remotes") {
13!
2138
    return {0, doGenericTopRemotes(pleaseGetRemotes)};
×
2139
  }
×
2140
  if (cmd == "top-queries") {
13!
2141
    return {0, doGenericTopQueries(pleaseGetQueryRing)};
×
2142
  }
×
2143
  if (cmd == "top-pub-queries") {
13!
2144
    return {0, doGenericTopQueries(pleaseGetQueryRing, getRegisteredName)};
×
2145
  }
×
2146
  if (cmd == "top-servfail-queries") {
13!
2147
    return {0, doGenericTopQueries(pleaseGetServfailQueryRing)};
×
2148
  }
×
2149
  if (cmd == "top-pub-servfail-queries") {
13!
2150
    return {0, doGenericTopQueries(pleaseGetServfailQueryRing, getRegisteredName)};
×
2151
  }
×
2152
  if (cmd == "top-bogus-queries") {
13!
2153
    return {0, doGenericTopQueries(pleaseGetBogusQueryRing)};
×
2154
  }
×
2155
  if (cmd == "top-pub-bogus-queries") {
13!
2156
    return {0, doGenericTopQueries(pleaseGetBogusQueryRing, getRegisteredName)};
×
2157
  }
×
2158
  if (cmd == "top-servfail-remotes") {
13!
2159
    return {0, doGenericTopRemotes(pleaseGetServfailRemotes)};
×
2160
  }
×
2161
  if (cmd == "top-bogus-remotes") {
13!
2162
    return {0, doGenericTopRemotes(pleaseGetBogusRemotes)};
×
2163
  }
×
2164
  if (cmd == "top-largeanswer-remotes") {
13!
2165
    return {0, doGenericTopRemotes(pleaseGetLargeAnswerRemotes)};
×
2166
  }
×
2167
  if (cmd == "top-timeouts") {
13!
2168
    return {0, doGenericTopRemotes(pleaseGetTimeouts)};
×
2169
  }
×
2170
  if (cmd == "current-queries") {
13!
2171
    return {0, doCurrentQueries()};
×
2172
  }
×
2173
  if (cmd == "ping") {
13✔
2174
    return {0, broadcastAccFunction<string>(nopFunction)};
12✔
2175
  }
12✔
2176
  if (cmd == "reload-zones") {
1!
2177
    if (!::arg()["chroot"].empty()) {
×
2178
      g_log << Logger::Error << "Unable to reload zones and forwards when chroot()'ed, requested via control channel" << endl;
×
2179
      return {1, "Unable to reload zones and forwards when chroot()'ed, please restart\n"};
×
2180
    }
×
2181
    return {0, reloadZoneConfigurationWithSysResolveReset()};
×
2182
  }
×
2183
  if (cmd == "set-ecs-minimum-ttl") {
1!
2184
    return {0, setMinimumECSTTL(begin, end)};
×
2185
  }
×
2186
  if (cmd == "set-max-cache-entries") {
1!
2187
    return {0, setMaxCacheEntries(begin, end)};
×
2188
  }
×
2189
  if (cmd == "set-max-packetcache-entries") {
1!
2190
    return {0, setMaxPacketCacheEntries(begin, end)};
×
2191
  }
×
2192
  if (cmd == "set-minimum-ttl") {
1!
2193
    return {0, setMinimumTTL(begin, end)};
×
2194
  }
×
2195
  if (cmd == "get-qtypelist") {
1!
2196
    return {0, g_Counters.sum(rec::ResponseStats::responseStats).getQTypeReport()};
×
2197
  }
×
2198
  if (cmd == "add-nta") {
1!
2199
    return {0, doAddNTA(begin, end)};
×
2200
  }
×
2201
  if (cmd == "clear-nta") {
1!
2202
    return {0, doClearNTA(begin, end)};
×
2203
  }
×
2204
  if (cmd == "get-ntas") {
1!
2205
    return {0, getNTAs()};
×
2206
  }
×
2207
  if (cmd == "add-ta") {
1!
2208
    return {0, doAddTA(begin, end)};
×
2209
  }
×
2210
  if (cmd == "clear-ta") {
1!
2211
    return {0, doClearTA(begin, end)};
×
2212
  }
×
2213
  if (cmd == "get-tas") {
1!
2214
    return {0, getTAs()};
1✔
2215
  }
1✔
2216
  if (cmd == "set-dnssec-log-bogus") {
×
2217
    return {0, doSetDnssecLogBogus(begin, end)};
×
2218
  }
×
2219
  if (cmd == "get-dont-throttle-names") {
×
2220
    return {0, getDontThrottleNames()};
×
2221
  }
×
2222
  if (cmd == "get-dont-throttle-netmasks") {
×
2223
    return {0, getDontThrottleNetmasks()};
×
2224
  }
×
2225
  if (cmd == "add-dont-throttle-names") {
×
2226
    return {0, addDontThrottleNames(begin, end)};
×
2227
  }
×
2228
  if (cmd == "add-dont-throttle-netmasks") {
×
2229
    return {0, addDontThrottleNetmasks(begin, end)};
×
2230
  }
×
2231
  if (cmd == "clear-dont-throttle-names") {
×
2232
    return {0, clearDontThrottleNames(begin, end)};
×
2233
  }
×
2234
  if (cmd == "clear-dont-throttle-netmasks") {
×
2235
    return {0, clearDontThrottleNetmasks(begin, end)};
×
2236
  }
×
2237
  if (cmd == "set-event-trace-enabled") {
×
2238
    return {0, setEventTracing(begin, end)};
×
2239
  }
×
2240
  if (cmd == "get-proxymapping-stats") {
×
2241
    return {0, doGetProxyMappingStats()};
×
2242
  }
×
2243
  if (cmd == "get-remotelogger-stats") {
×
2244
    return {0, getRemoteLoggerStats()};
×
2245
  }
×
2246
  if (cmd == "list-dnssec-algos") {
×
2247
    return {0, DNSCryptoKeyEngine::listSupportedAlgoNames()};
×
2248
  }
×
2249
  if (cmd == "set-aggr-nsec-cache-size") {
×
2250
    return setAggrNSECCacheSize(begin, end);
×
2251
  }
×
2252

2253
  return {1, "Unknown command '" + cmd + "', try 'help'\n"};
×
2254
}
×
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