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

PowerDNS / pdns / 22225698541

20 Feb 2026 01:20PM UTC coverage: 70.913% (-0.7%) from 71.654%
22225698541

Pull #16693

github

web-flow
Merge 12ab81d89 into da25f7afd
Pull Request #16693: auth: plumbing for structured logging

45342 of 79966 branches covered (56.7%)

Branch coverage included in aggregate %.

892 of 2611 new or added lines in 70 files covered. (34.16%)

390 existing lines in 48 files now uncovered.

130608 of 168156 relevant lines covered (77.67%)

7371735.34 hits per line

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

22.37
/pdns/dynhandler.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
#ifdef HAVE_CONFIG_H
23
#include "config.h"
24
#endif
25
#include "auth-caches.hh"
26
#include "auth-querycache.hh"
27
#include "auth-packetcache.hh"
28
#include "utility.hh"
29
#include "dynhandler.hh"
30
#include "statbag.hh"
31
#include "logger.hh"
32
#include "dns.hh"
33
#include "arguments.hh"
34
#include <csignal>
35
#include "misc.hh"
36
#include "communicator.hh"
37
#include "dnsseckeeper.hh"
38
#include "nameserver.hh"
39
#include "responsestats.hh"
40
#include "ueberbackend.hh"
41
#include "auth-main.hh"
42

43
extern ResponseStats g_rs;
44

45
static bool s_pleasequit;
46
static string d_status;
47

48
bool DLQuitPlease()
49
{
×
50
  return s_pleasequit;
×
51
}
×
52

53
string DLQuitHandler(const vector<string>& parts, Utility::pid_t /* ppid */, Logr::log_t slog)
54
{
×
55
  string ret="No return value";
×
56
  if(parts[0]=="QUIT") {
×
57
    s_pleasequit=true;
×
58
    ret="Scheduling exit";
×
NEW
59
    SLOG(g_log<<Logger::Error<<"Scheduling exit on remote request"<<endl,
×
NEW
60
         slog->info(Logr::Error, "Scheduling exit on remote request"));
×
61
  }
×
62
  return ret;
×
63
}
×
64

65
static void dokill(int)
66
{
×
67
  exit(0);
×
68
}
×
69

70
string DLCurrentConfigHandler(const vector<string>& parts, Utility::pid_t /* ppid */, Logr::log_t /* slog */)
71
{
×
72
  if(parts.size() > 1) {
×
73
    if(parts.size() == 2 && parts[1] == "diff") {
×
74
      return ::arg().configstring(true, false);
×
75
    }
×
76
    return "Syntax: current-config [diff]";
×
77
  }
×
78
  return ::arg().configstring(true, true);
×
79
}
×
80

81
string DLRQuitHandler(const vector<string>& /* parts */, Utility::pid_t /* ppid */, Logr::log_t /* slog */)
82
{
×
83
  signal(SIGALRM, dokill);
×
84
  alarm(1);
×
85
  return "Exiting";
×
86
}
×
87

88
string DLPingHandler(const vector<string>& /* parts */, Utility::pid_t /* ppid */, Logr::log_t /* slog */)
89
{
×
90
  return "PONG";
×
91
}
×
92

93
string DLShowHandler(const vector<string>& parts, Utility::pid_t /* ppid */, Logr::log_t /* slog */)
94
{
2✔
95
  try {
2✔
96
    extern StatBag S;
2✔
97
    string ret("Wrong number of parameters");
2✔
98
    if (parts.size() == 2) {
2!
99
      if (parts[1] == "*")
2✔
100
        ret = S.directory();
1✔
101
      else if (parts[1].length() && parts[1][parts[1].length() - 1 ] == '*')
1!
102
        ret = S.directory(parts[1].substr(0, parts[1].length() - 1));
×
103
      else
1✔
104
        ret = S.getValueStr(parts[1]);
1✔
105
    }
2✔
106

107
    return ret;
2✔
108
  }
2✔
109
  catch (...) {
2✔
110
    return "Unknown";
×
111
  }
×
112
}
2✔
113

114
void setStatus(const string &str)
115
{
×
116
  d_status=str;
×
117
}
×
118

119
string DLStatusHandler(const vector<string>& /* parts */, Utility::pid_t ppid, Logr::log_t /* slog */)
120
{
×
121
  ostringstream os;
×
122
  os<<ppid<<": "<<d_status;
×
123
  return os.str();
×
124
}
×
125

126
string DLUptimeHandler(const vector<string>& /* parts */, Utility::pid_t /* ppid */, Logr::log_t /* slog */)
127
{
×
128
  ostringstream os;
×
129
  os<<humanDuration(time(nullptr)-g_starttime);
×
130
  return os.str();
×
131
}
×
132

133
string DLPurgeHandler(const vector<string>& parts, Utility::pid_t /* ppid */, Logr::log_t slog)
134
{
6✔
135
  ostringstream os;
6✔
136
  int ret=0;
6✔
137
  bool clearAllDNSSECcaches{false};
6✔
138

139
  if(parts.size()>1) {
6!
140
    for (vector<string>::const_iterator i=++parts.begin();i<parts.end();++i) {
12✔
141
      SLOG(g_log<<Logger::Warning<<"Cache clear request for '"<<*i<<"' received from operator"<<endl,
6!
142
           slog->info(Logr::Warning, "Cache clear request received from operator", "request", Logging::Loggable(*i)));
6✔
143
      ret+=purgeAuthCaches(*i);
6✔
144
      if (!boost::ends_with(*i, "$")) {
6!
145
        if (!clearAllDNSSECcaches) {
6!
146
          DNSSECKeeper::clearCaches(ZoneName(*i));
6✔
147
        }
6✔
148
      }
6✔
149
      else {
×
150
        clearAllDNSSECcaches = true; // at least we do what we promise.. and a bit more!
×
151
      }
×
152
    }
6✔
153
  }
6✔
154
  else {
×
NEW
155
    SLOG(g_log<<Logger::Warning<<"Cache clear request received from operator"<<endl,
×
NEW
156
         slog->info(Logr::Warning, "Cache clear request received from operator"));
×
157
    ret = purgeAuthCaches();
×
158
    clearAllDNSSECcaches = true;
×
159
  }
×
160

161
  if (clearAllDNSSECcaches) {
6!
162
    DNSSECKeeper::clearAllCaches();
×
163
  }
×
164

165
  os<<ret;
6✔
166
  return os.str();
6✔
167
}
6✔
168

169
string DLCCHandler(const vector<string>& /* parts */, Utility::pid_t /* ppid */, Logr::log_t /* slog */)
170
{
×
171
  extern AuthPacketCache PC;
×
172
  extern AuthQueryCache QC;
×
173
  map<char,uint64_t> counts=QC.getCounts();
×
174
  uint64_t packetEntries = PC.size();
×
175
  ostringstream os;
×
176
  bool first=true;
×
177
  for(map<char,uint64_t>::const_iterator i=counts.begin();i!=counts.end();++i) {
×
178
    if(!first)
×
179
      os<<", ";
×
180
    first=false;
×
181

182
    if(i->first=='!')
×
183
      os<<"negative queries: ";
×
184
    else if(i->first=='Q')
×
185
      os<<"queries: ";
×
186
    else
×
187
      os<<"unknown: ";
×
188

189
    os<<i->second;
×
190
  }
×
191
  if(!first)
×
192
    os<<", ";
×
193
  os<<"packets: "<<packetEntries;
×
194

195
  return os.str();
×
196
}
×
197

198
string DLQTypesHandler(const vector<string>& /* parts */, Utility::pid_t /* ppid */, Logr::log_t /* slog */)
199
{
×
200
  return g_rs.getQTypeReport();
×
201
}
×
202

203
string DLRSizesHandler(const vector<string>& /* parts */, Utility::pid_t /* ppid */, Logr::log_t /* slog */)
204
{
×
205
  typedef map<uint16_t, uint64_t> respsizes_t;
×
206
  respsizes_t respsizes = g_rs.getSizeResponseCounts();
×
207
  ostringstream os;
×
208
  boost::format fmt("%d\t%d\n");
×
209
  for(const respsizes_t::value_type& val :  respsizes) {
×
210
    os << (fmt % val.first % val.second).str();
×
211
  }
×
212
  return os.str();
×
213
}
×
214

215
string DLRemotesHandler(const vector<string>& /* parts */, Utility::pid_t /* ppid */, Logr::log_t /* slog */)
216
{
×
217
  extern StatBag S;
×
218
  typedef vector<pair<string, unsigned int> > totals_t;
×
219
  totals_t totals = S.getRing("remotes");
×
220
  string ret;
×
221
  boost::format fmt("%s\t%d\n");
×
222
  for(totals_t::value_type& val :  totals) {
×
223
    ret += (fmt % val.first % val.second).str();
×
224
  }
×
225
  return ret;
×
226
}
×
227

228
string DLSettingsHandler(const vector<string>& parts, Utility::pid_t /* ppid */, Logr::log_t slog)
229
{
×
230
  static const char *whitelist[]={"query-logging",nullptr};
×
231
  const char **p;
×
232

233
  if(parts.size()!=3) {
×
234
    return "Syntax: set variable value";
×
235
  }
×
236

237
  for(p=whitelist;*p;p++)
×
238
    if(*p==parts[1])
×
239
      break;
×
240
  if(*p) {
×
241
    ::arg().set(parts[1])=parts[2];
×
NEW
242
    SLOG(g_log<<Logger::Warning<<"Configuration change for setting '"<<parts[1]<<"' to value '"<<parts[2]<<"' received from operator"<<endl,
×
NEW
243
         slog->info(Logr::Warning, "Configuration change received from operator", "setting", Logging::Loggable(parts[1]), "new value", Logging::Loggable(parts[2])));
×
244
    return "done";
×
245
  }
×
246
  else
×
247
    return "This setting cannot be changed at runtime, or no such setting";
×
248

249
}
×
250

251
string DLVersionHandler(const vector<string>& /* parts */, Utility::pid_t /* ppid */, Logr::log_t /* slog */)
252
{
×
253
  return VERSION;
×
254
}
×
255

256
string DLNotifyRetrieveHandler(const vector<string>& parts, Utility::pid_t /* ppid */, Logr::log_t slog)
257
{
24✔
258
  extern CommunicatorClass Communicator;
24✔
259
  ostringstream os;
24✔
260
  if(parts.size()!=2 && parts.size()!=3)
24!
261
    return "syntax: retrieve zone [ip]";
×
262

263
  ZoneName domain;
24✔
264
  try {
24✔
265
    domain = ZoneName(parts[1]);
24✔
266
  } catch (...) {
24✔
267
    return "Failed to parse '" + parts[1] + "' as a valid zone name";
×
268
  }
×
269

270
  ComboAddress primary_ip;
24✔
271
  bool override_primary = false;
24✔
272
  if (parts.size() == 3) {
24!
273
    try {
×
274
      primary_ip = ComboAddress{parts[2], 53};
×
275
    } catch (...) {
×
276
      return "Invalid primary address";
×
277
    }
×
278
    override_primary = true;
×
279
  }
×
280

281
  DomainInfo di;
24✔
282
  UeberBackend B;
24✔
283
  if(!B.getDomainInfo(domain, di)) {
24✔
284
    return " Zone '" + domain.toString() + "' unknown";
12✔
285
  }
12✔
286

287
  if (override_primary) {
12!
288
    di.primaries.clear();
×
289
    di.primaries.push_back(primary_ip);
×
290
  }
×
291

292
  if (!override_primary && (!di.isSecondaryType() || di.primaries.empty()))
12!
293
    return "Zone '" + domain.toString() + "' is not a secondary/consumer zone (or has no primary defined)";
×
294

295
  shuffle(di.primaries.begin(), di.primaries.end(), pdns::dns_random_engine());
12✔
296
  const auto& primary = di.primaries.front();
12✔
297
  Communicator.addSuckRequest(domain, primary, SuckRequest::PdnsControl, override_primary);
12✔
298
  SLOG(g_log << Logger::Warning << "Retrieval request for zone '" << domain << "' from primary '" << primary << "' received from operator" << endl,
12!
299
       slog->info(Logr::Warning, "Retrieval request received from operator", "zone", Logging::Loggable(domain), "primary", Logging::Loggable(primary)));
12✔
300
  return "Added retrieval request for '" + domain.toLogString() + "' from primary " + primary.toLogString();
12✔
301
}
12✔
302

303
string DLNotifyHostHandler(const vector<string>& parts, Utility::pid_t /* ppid */, Logr::log_t slog)
304
{
×
305
  extern CommunicatorClass Communicator;
×
306
  ostringstream os;
×
307
  if(parts.size()!=3)
×
308
    return "syntax: notify-host zone ip";
×
309
  if(!::arg().mustDo("primary") && !(::arg().mustDo("secondary") && ::arg().mustDo("secondary-do-renotify")))
×
310
    return "PowerDNS not configured as primary, or secondary with re-notifications";
×
311

312
  ZoneName domain;
×
313
  try {
×
314
    domain = ZoneName(parts[1]);
×
315
  } catch (...) {
×
316
    return "Failed to parse '" + parts[1] + "' as a valid zone name";
×
317
  }
×
318

319
  try {
×
320
    ComboAddress ca(parts[2]);
×
321
  } catch(...)
×
322
  {
×
323
    return "Unable to convert '"+parts[2]+"' to an IP address";
×
324
  }
×
325

NEW
326
  SLOG(g_log << Logger::Warning << "Notification request to host " << parts[2] << " for zone '" << domain << "' received from operator" << endl,
×
NEW
327
       slog->info(Logr::Warning, "Notification request received from operator", "host", Logging::Loggable(parts[2]), "zone", Logging::Loggable(domain)));
×
328
  Communicator.notify(domain, parts[2]);
×
329
  return "Added to queue";
×
330
}
×
331

332
string DLNotifyHandler(const vector<string>& parts, Utility::pid_t /* ppid */, Logr::log_t slog)
333
{
2✔
334
  extern CommunicatorClass Communicator;
2✔
335
  UeberBackend B;
2✔
336
  if(parts.size()!=2)
2!
337
    return "syntax: notify zone";
×
338
  if(!::arg().mustDo("primary") && !(::arg().mustDo("secondary") && ::arg().mustDo("secondary-do-renotify")))
2!
339
    return "PowerDNS not configured as primary (primary), or secondary (secondary) with re-notifications";
×
340
  SLOG(g_log << Logger::Warning << "Notification request for zone '" << parts[1] << "' received from operator" << endl,
2!
341
       slog->info(Logr::Warning, "Notification request received from operator", "zone", Logging::Loggable(parts[1])));
2✔
342

343
  if (parts[1] == "*") {
2!
344
    vector<DomainInfo> domains;
×
345
    B.getAllDomains(&domains, true, false);
×
346

347
    int total = 0;
×
348
    int notified = 0;
×
349
    for (const auto& di : domains) {
×
350
      if (di.kind != DomainInfo::Native) { // Primary and secondary if secondary-do-renotify is enabled
×
351
        total++;
×
352
        if(Communicator.notifyDomain(di.zone, &B))
×
353
          notified++;
×
354
      }
×
355
    }
×
356

357
    if (total != notified)
×
358
      return std::to_string(notified)+" out of "+std::to_string(total)+" zones added to queue - see log";
×
359
    return "Added " + std::to_string(total) + " MASTER/SLAVE/PRODUCER/CONSUMER zones to queue";
×
360
  } else {
2✔
361
    ZoneName domain;
2✔
362
    try {
2✔
363
      domain = ZoneName(parts[1]);
2✔
364
    } catch (...) {
2✔
365
      return "Failed to parse '" + parts[1] + "' as a valid zone name";
×
366
    }
×
367
    if(!Communicator.notifyDomain(domain, &B)) {
2!
368
      return "Failed to add " + domain.toLogString() + " to the queue - see log";
×
369
    }
×
370
    return "Added " + domain.toLogString() + " to queue";
2✔
371
  }
2✔
372
}
2✔
373

374
string DLRediscoverHandler(const vector<string>& /* parts */, Utility::pid_t /* ppid */, Logr::log_t slog)
375
{
×
376
  UeberBackend B;
×
377
  try {
×
NEW
378
    SLOG(g_log<<Logger::Error<<"Rediscovery was requested"<<endl,
×
NEW
379
         slog->info(Logr::Warning, "Rediscovery was requested"));
×
380
    string status="Ok";
×
381
    B.rediscover(&status);
×
382
    return status;
×
383
  }
×
384
  catch(PDNSException &ae) {
×
385
    return ae.reason;
×
386
  }
×
387

388
}
×
389

390
string DLReloadHandler(const vector<string>& /* parts */, Utility::pid_t /* ppid */, Logr::log_t slog)
391
{
×
392
  UeberBackend B;
×
393
  B.reload();
×
NEW
394
  SLOG(g_log<<Logger::Error<<"Reload was requested"<<endl,
×
NEW
395
       slog->info(Logr::Warning, "Reload was requested"));
×
396
  return "Ok";
×
397
}
×
398

399
string DLListZones(const vector<string>& parts, Utility::pid_t /* ppid */, Logr::log_t slog)
400
{
×
401
  UeberBackend B;
×
NEW
402
  SLOG(g_log<<Logger::Notice<<"Received request to list zones."<<endl,
×
NEW
403
       slog->info(Logr::Notice, "Received request to list zones."));
×
404
  vector<DomainInfo> domains;
×
405
  B.getAllDomains(&domains, false, false);
×
406
  ostringstream ret;
×
407
  DomainInfo::DomainKind kind;
×
408
  if (parts.size() > 1) {
×
409
    kind = DomainInfo::stringToKind(parts.at(1));
×
410
  }
×
411
  else {
×
412
    kind = DomainInfo::All;
×
413
  }
×
414

415
  int count = 0;
×
416

417
  for (const auto& di: domains) {
×
418
    if (di.kind == kind || kind == DomainInfo::All) {
×
419
      ret<<di.zone.toString()<<endl;
×
420
      count++;
×
421
    }
×
422
  }
×
423

424
  ret << DomainInfo::getKindString(kind) << " zonecount: " << count;
×
425

426
  return ret.str();
×
427
}
×
428

429
string DLFlushHandler(const vector<string>& /*parts*/, Utility::pid_t /*ppid*/, Logr::log_t slog)
430
{
×
431
  UeberBackend B; // NOLINT(readability-identifier-length)
×
432
  B.flush();
×
NEW
433
  SLOG(g_log<<Logger::Error<<"Backend flush was requested"<<endl,
×
NEW
434
       slog->info(Logr::Warning, "Backend flush was requested"));
×
435
  return "Ok";
×
436
}
×
437

438
#ifdef HAVE_P11KIT1
439
extern bool PKCS11ModuleSlotLogin(const std::string& module, const string& tokenId, const std::string& pin);
440
#endif
441

442
string DLTokenLogin([[maybe_unused]] const vector<string>& parts, [[maybe_unused]] Utility::pid_t /* ppid */, Logr::log_t /* slog */)
443
{
×
444
#ifndef HAVE_P11KIT1
445
  return "PKCS#11 support not compiled in";
446
#else
447
  if (parts.size() != 4) {
×
448
    return "invalid number of parameters, needs 4, got " + std::to_string(parts.size());
×
449
  }
×
450

451
  if (PKCS11ModuleSlotLogin(parts[1], parts[2], parts[3])) {
×
452
    return "logged in";
×
453
  } else {
×
454
    return "could not log in, check logs";
×
455
  }
×
456
#endif
×
457
}
×
458

459
string DLSuckRequests(const vector<string>& /* parts */, Utility::pid_t /* ppid */, Logr::log_t /* slog */)
460
{
×
461
  string ret;
×
462
  for (auto const &d: Communicator.getSuckRequests()) {
×
463
    ret += d.first.toString() + " " + d.second.toString() + "\n";
×
464
  }
×
465
  return ret;
×
466
}
×
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

© 2026 Coveralls, Inc