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

PowerDNS / pdns / 19957761886

05 Dec 2025 08:52AM UTC coverage: 73.063% (-0.07%) from 73.131%
19957761886

push

github

web-flow
Merge pull request #16590 from rgacogne/ddist-coverity-20251204

dnsdist: Fix missed optimizations reported by Coverity in config

38511 of 63422 branches covered (60.72%)

Branch coverage included in aggregate %.

3 of 5 new or added lines in 1 file covered. (60.0%)

13941 existing lines in 122 files now uncovered.

128037 of 164529 relevant lines covered (77.82%)

5473411.95 hits per line

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

44.32
/modules/tinydnsbackend/tinydnsbackend.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 "tinydnsbackend.hh"
26
#include "pdns/misc.hh"
27
#include "pdns/dnsrecords.hh"
28
#include <utility>
29

30
static string backendname = "[TinyDNSBackend] ";
31
domainid_t TinyDNSBackend::s_lastId;
32
LockGuarded<TinyDNSBackend::TDI_suffix_t> TinyDNSBackend::s_domainInfo;
33

34
vector<string> TinyDNSBackend::getLocations()
35
{
×
36
  vector<string> ret;
×
37

38
  if (!d_dnspacket) {
×
39
    return ret;
×
40
  }
×
41

42
  //TODO: We do not have IPv6 support.
43
  Netmask remote = d_dnspacket->getRealRemote();
×
44
  if (remote.getBits() != 32) {
×
45
    return ret;
×
46
  }
×
47

48
  unsigned long addr = remote.getNetwork().sin4.sin_addr.s_addr;
×
49

50
  char key[6];
×
51
  key[0] = '\000';
×
52
  key[1] = '\045';
×
53
  key[2] = (addr) & 0xff;
×
54
  key[3] = (addr >> 8) & 0xff;
×
55
  key[4] = (addr >> 16) & 0xff;
×
56
  key[5] = (addr >> 24) & 0xff;
×
57

58
  for (int i = 4; i >= 0; i--) {
×
59
    string searchkey(key, i + 2);
×
60
    try {
×
61
      auto reader = std::make_unique<CDB>(getArg("dbfile"));
×
62
      ret = reader->findall(searchkey);
×
63
    }
×
64
    catch (const std::exception& e) {
×
65
      g_log << Logger::Error << e.what() << endl;
×
66
      throw PDNSException(e.what());
×
67
    }
×
68

69
    //Biggest item wins, so when we find something, we can jump out.
70
    if (ret.size() > 0) {
×
71
      break;
×
72
    }
×
73
  }
×
74

75
  return ret;
×
76
}
×
77

78
TinyDNSBackend::TinyDNSBackend(const string& suffix)
UNCOV
79
{
5✔
UNCOV
80
  setArgPrefix("tinydns" + suffix);
5✔
UNCOV
81
  d_suffix = suffix;
5✔
UNCOV
82
  d_locations = mustDo("locations");
5✔
UNCOV
83
  d_ignorebogus = mustDo("ignore-bogus-records");
5✔
UNCOV
84
  d_taiepoch = 4611686018427387904ULL + getArgAsNum("tai-adjust");
5✔
UNCOV
85
  d_dnspacket = NULL;
5✔
UNCOV
86
  d_cdbReader = NULL;
5✔
UNCOV
87
  d_isAxfr = false;
5✔
UNCOV
88
  d_isWildcardQuery = false;
5✔
UNCOV
89
}
5✔
90

91
TinyDNSBackend::TDI_t::iterator TinyDNSBackend::updateState(DomainInfo& domain, TDI_t* state)
UNCOV
92
{
17✔
UNCOV
93
  TDIByZone_t& zone_index = state->get<tag_zone>();
17✔
UNCOV
94
  TDIByZone_t::iterator itByZone = zone_index.find(domain.zone);
17✔
UNCOV
95
  if (itByZone != zone_index.end()) {
17!
96
    return itByZone;
×
97
  }
×
98

UNCOV
99
  TinyDomainInfo tmp;
17✔
UNCOV
100
  s_lastId++;
17✔
UNCOV
101
  tmp.zone = domain.zone;
17✔
UNCOV
102
  tmp.id = s_lastId;
17✔
UNCOV
103
  tmp.notified_serial = domain.serial;
17✔
UNCOV
104
  return state->insert(tmp).first;
17✔
UNCOV
105
}
17✔
106

107
void TinyDNSBackend::getUpdatedPrimaries(vector<DomainInfo>& retDomains, std::unordered_set<DNSName>& /* catalogs */, CatalogHashMap& /* catalogHashes */)
108
{
×
109
  bool alwaysNotify{false};
×
110
  auto domainInfo = s_domainInfo.lock(); //TODO: We could actually lock less if we do it per suffix.
×
111
  if (domainInfo->count(d_suffix) == 0) {
×
112
    // If we don't have any state yet, this is startup, check whether we need
113
    // to always notify.
114
    alwaysNotify = mustDo("notify-on-startup");
×
115
    TDI_t tmp;
×
116
    domainInfo->emplace(d_suffix, tmp);
×
117
  }
×
118

119
  TDI_t* state = &(*domainInfo)[d_suffix];
×
120

121
  vector<DomainInfo> allDomains;
×
122
  getAllDomains_locked(&allDomains, true);
×
123

124
  for (auto& domain : allDomains) {
×
125
    auto iter = updateState(domain, state);
×
126
    // Keep domain id in sync with our current state.
127
    domain.id = iter->id;
×
128
    if (alwaysNotify || iter->notified_serial < domain.serial) {
×
129
      retDomains.push_back(domain);
×
130
    }
×
131
  }
×
132
}
×
133

134
// NOLINTNEXTLINE(readability-identifier-length)
135
void TinyDNSBackend::setNotified(domainid_t id, uint32_t serial)
136
{
×
137
  auto domainInfo = s_domainInfo.lock();
×
138
  if (!domainInfo->count(d_suffix)) {
×
139
    throw PDNSException("Can't get list of domains to set the serial.");
×
140
  }
×
141
  TDI_t* state = &(*domainInfo)[d_suffix];
×
142
  TDIById_t& domain_index = state->get<tag_domainid>();
×
143
  TDIById_t::iterator itById = domain_index.find(id);
×
144
  if (itById == domain_index.end()) {
×
145
    g_log << Logger::Error << backendname << "Received updated serial(" << serial << "), but domain ID (" << id << ") is not known in this backend." << endl;
×
146
  }
×
147
  else {
×
148
    DLOG(g_log << Logger::Debug << backendname << "Setting serial for " << itById->zone << " to " << serial << endl);
×
149
    domain_index.modify(itById, TDI_SerialModifier(serial));
×
150
  }
×
151
  (*domainInfo)[d_suffix] = *state;
×
152
}
×
153

154
void TinyDNSBackend::getAllDomains_locked(vector<DomainInfo>* domains, bool getSerial)
UNCOV
155
{
1✔
UNCOV
156
  d_isAxfr = true;
1✔
UNCOV
157
  d_isGetDomains = true;
1✔
UNCOV
158
  d_dnspacket = NULL;
1✔
159

UNCOV
160
  try {
1✔
UNCOV
161
    d_cdbReader = std::make_unique<CDB>(getArg("dbfile"));
1✔
UNCOV
162
    d_currentDomain = UnknownDomainID;
1✔
UNCOV
163
  }
1✔
UNCOV
164
  catch (const std::exception& e) {
1✔
165
    g_log << Logger::Error << e.what() << endl;
×
166
    throw PDNSException(e.what());
×
167
  }
×
168

UNCOV
169
  d_cdbReader->searchAll();
1✔
UNCOV
170
  DNSResourceRecord rr;
1✔
UNCOV
171
  std::unordered_set<DNSName> dupcheck;
1✔
172

UNCOV
173
  while (get(rr)) {
18✔
UNCOV
174
    if (rr.qtype.getCode() == QType::SOA && dupcheck.insert(rr.qname).second) {
17!
UNCOV
175
      DomainInfo di;
17✔
UNCOV
176
      di.id = d_currentDomain; // Will be overridden by caller
17✔
UNCOV
177
      di.backend = this;
17✔
UNCOV
178
      di.zone = ZoneName(rr.qname);
17✔
UNCOV
179
      di.kind = DomainInfo::Primary;
17✔
UNCOV
180
      di.last_check = time(0);
17✔
181

UNCOV
182
      if (getSerial) {
17!
183
        SOAData sd;
×
184
        try {
×
185
          fillSOAData(rr.content, sd);
×
186
          di.serial = sd.serial;
×
187
        }
×
188
        catch (...) {
×
189
          di.serial = 0;
×
190
        }
×
191
      }
×
192

UNCOV
193
      di.notified_serial = di.serial;
17✔
UNCOV
194
      domains->push_back(di);
17✔
UNCOV
195
    }
17✔
UNCOV
196
  }
17✔
UNCOV
197
}
1✔
198

199
void TinyDNSBackend::getAllDomains(vector<DomainInfo>* domains, bool getSerial, bool /* include_disabled */)
UNCOV
200
{
1✔
UNCOV
201
  auto domainInfo = s_domainInfo.lock(); //TODO: We could actually lock less if we do it per suffix.
1✔
UNCOV
202
  if (domainInfo->count(d_suffix) == 0) {
1!
UNCOV
203
    TDI_t tmp;
1✔
UNCOV
204
    domainInfo->emplace(d_suffix, tmp);
1✔
UNCOV
205
  }
1✔
206

UNCOV
207
  TDI_t* state = &(*domainInfo)[d_suffix];
1✔
208

UNCOV
209
  getAllDomains_locked(domains, getSerial);
1✔
210

UNCOV
211
  for (auto& domain : *domains) {
17✔
UNCOV
212
    auto iter = updateState(domain, state);
17✔
213
    // Keep domain id in sync with our current state.
UNCOV
214
    domain.id = iter->id;
17✔
UNCOV
215
  }
17✔
UNCOV
216
}
1✔
217

218
//NOLINTNEXTLINE(readability-identifier-length)
219
bool TinyDNSBackend::getDomainInfo(const ZoneName& domain, DomainInfo& di, bool getSerial)
220
{
×
221
  auto domainInfo = s_domainInfo.lock(); //TODO: We could actually lock less if we do it per suffix.
×
222
  if (domainInfo->count(d_suffix) == 0) {
×
223
    TDI_t tmp;
×
224
    domainInfo->emplace(d_suffix, tmp);
×
225
  }
×
226

227
  TDI_t* state = &(*domainInfo)[d_suffix];
×
228

229
  vector<DomainInfo> allDomains;
×
230
  getAllDomains_locked(&allDomains, getSerial);
×
231

232
  bool found{false};
×
233
  for (auto& oneDomain : allDomains) {
×
234
    auto iter = updateState(oneDomain, state);
×
235
    if (oneDomain.zone == domain) {
×
236
      // Keep domain id in sync with our current state.
237
      oneDomain.id = iter->id;
×
238
      di = oneDomain;
×
239
      found = true;
×
240
    }
×
241
  }
×
242
  return found;
×
243
}
×
244

245
bool TinyDNSBackend::list(const ZoneName& target, domainid_t domain_id, bool /* include_disabled */)
246
{
×
247
  d_isAxfr = true;
×
248
  d_isGetDomains = false;
×
249
  string key = target.operator const DNSName&().toDNSStringLC();
×
250
  try {
×
251
    d_cdbReader = std::make_unique<CDB>(getArg("dbfile"));
×
252
    d_currentDomain = domain_id;
×
253
  }
×
254
  catch (const std::exception& e) {
×
255
    g_log << Logger::Error << e.what() << endl;
×
256
    throw PDNSException(e.what());
×
257
  }
×
258

259
  return d_cdbReader->searchSuffix(key);
×
260
}
×
261

262
void TinyDNSBackend::lookup(const QType& qtype, const DNSName& qdomain, domainid_t zoneId, DNSPacket* pkt_p)
UNCOV
263
{
218✔
UNCOV
264
  d_isAxfr = false;
218✔
UNCOV
265
  d_isGetDomains = false;
218✔
UNCOV
266
  string queryDomain = toLowerCanonic(qdomain.toString());
218✔
267

UNCOV
268
  string key = simpleCompress(queryDomain);
218✔
269

UNCOV
270
  DLOG(g_log << Logger::Debug << backendname << "[lookup] query for qtype [" << qtype.toString() << "] qdomain [" << qdomain << "]" << endl);
218✔
UNCOV
271
  DLOG(g_log << Logger::Debug << "[lookup] key [" << makeHexDump(key) << "]" << endl);
218✔
272

UNCOV
273
  d_isWildcardQuery = false;
218✔
UNCOV
274
  if (key[0] == '\001' && key[1] == '\052') {
218✔
UNCOV
275
    d_isWildcardQuery = true;
49✔
UNCOV
276
    key.erase(0, 2);
49✔
UNCOV
277
  }
49✔
278

UNCOV
279
  d_qtype = qtype;
218✔
280

UNCOV
281
  try {
218✔
UNCOV
282
    d_cdbReader = std::make_unique<CDB>(getArg("dbfile"));
218✔
UNCOV
283
    d_currentDomain = zoneId;
218✔
UNCOV
284
  }
218✔
UNCOV
285
  catch (const std::exception& e) {
218✔
286
    g_log << Logger::Error << e.what() << endl;
×
287
    throw PDNSException(e.what());
×
288
  }
×
289

UNCOV
290
  d_cdbReader->searchKey(key);
218✔
UNCOV
291
  d_dnspacket = pkt_p;
218✔
UNCOV
292
}
218✔
293

294
bool TinyDNSBackend::get(DNSResourceRecord& rr)
UNCOV
295
{
495✔
UNCOV
296
  pair<string, string> record;
495✔
297

UNCOV
298
  while (d_cdbReader->readNext(record)) {
20,853✔
UNCOV
299
    string val = record.second;
20,634✔
UNCOV
300
    string key = record.first;
20,634✔
301

302
#if 0
303
    DLOG(g_log<<Logger::Debug<<"[GET] Key: "<<makeHexDump(key)<<endl);
304
    DLOG(g_log<<Logger::Debug<<"[GET] Val: "<<makeHexDump(val)<<endl);
305
#endif
UNCOV
306
    if (key[0] == '\000' && key[1] == '\045') { // skip locations
20,634!
307
      continue;
×
308
    }
×
309

UNCOV
310
    if (!d_isAxfr) {
20,634✔
311
      // If we have a wildcard query, but the record we got is not a wildcard, we skip.
UNCOV
312
      if (d_isWildcardQuery && val[2] != '\052' && val[2] != '\053') {
298!
UNCOV
313
        continue;
28✔
UNCOV
314
      }
28✔
315

316
      // If it is NOT a wildcard query, but we do find a wildcard record, we skip it.
UNCOV
317
      if (!d_isWildcardQuery && (val[2] == '\052' || val[2] == '\053')) {
270!
UNCOV
318
        continue;
11✔
UNCOV
319
      }
11✔
UNCOV
320
    }
270✔
321

UNCOV
322
    PacketReader pr(val, 0);
20,595✔
UNCOV
323
    rr.qtype = QType(pr.get16BitInt());
20,595✔
324

UNCOV
325
    if (d_isGetDomains && rr.qtype != QType::SOA) {
20,595✔
UNCOV
326
      continue;
20,319✔
UNCOV
327
    }
20,319✔
328

UNCOV
329
    if (d_isAxfr || d_qtype.getCode() == QType::ANY || rr.qtype == d_qtype) {
276!
UNCOV
330
      char locwild = pr.get8BitInt();
276✔
UNCOV
331
      if (locwild != '\075' && (locwild == '\076' || locwild == '\053')) {
276!
332
        if (d_isAxfr && d_locations) { // We skip records with a location in AXFR, unless we disable locations.
×
333
          continue;
×
334
        }
×
335
        char recloc[2];
×
336
        recloc[0] = pr.get8BitInt();
×
337
        recloc[1] = pr.get8BitInt();
×
338

339
        if (d_locations) {
×
340
          bool foundLocation = false;
×
341
          vector<string> locations = getLocations();
×
342
          while (locations.size() > 0) {
×
343
            string locId = locations.back();
×
344
            locations.pop_back();
×
345

346
            if (recloc[0] == locId[0] && recloc[1] == locId[1]) {
×
347
              foundLocation = true;
×
348
              break;
×
349
            }
×
350
          }
×
351
          if (!foundLocation) {
×
352
            continue;
×
353
          }
×
354
        }
×
355
      }
×
356

UNCOV
357
      if (d_isAxfr && (val[2] == '\052' || val[2] == '\053')) { // Keys are not stored with wildcard character, with AXFR we need to add that.
276!
358
        key.insert(0, 1, '\052');
×
359
        key.insert(0, 1, '\001');
×
360
      }
×
361
      // rr.qname.clear();
UNCOV
362
      rr.qname = DNSName(key.c_str(), key.size(), 0, false);
276✔
UNCOV
363
      rr.domain_id = d_currentDomain;
276✔
364
      // 11:13.21 <@ahu> IT IS ALWAYS AUTH --- well not really because we are just a backend :-)
365
      // We could actually do NSEC3-NARROW DNSSEC according to Habbie, if we do, we need to change something here.
UNCOV
366
      rr.auth = true;
276✔
367

UNCOV
368
      rr.ttl = pr.get32BitInt();
276✔
UNCOV
369
      uint64_t timestamp = pr.get32BitInt();
276✔
UNCOV
370
      timestamp <<= 32;
276✔
UNCOV
371
      timestamp += pr.get32BitInt();
276✔
UNCOV
372
      if (timestamp) {
276!
373
        uint64_t now = d_taiepoch + time(NULL);
×
374
        if (rr.ttl == 0) {
×
375
          if (timestamp < now) {
×
376
            continue;
×
377
          }
×
378
          rr.ttl = timestamp - now;
×
379
          if (rr.ttl <= 2)
×
380
            rr.ttl = 2;
×
381
          if (rr.ttl >= 3600)
×
382
            rr.ttl = 3600;
×
383
        }
×
384
        else if (now <= timestamp) {
×
385
          continue;
×
386
        }
×
387
      }
×
UNCOV
388
      try {
276✔
UNCOV
389
        DNSRecord dr;
276✔
UNCOV
390
        dr.d_class = 1;
276✔
UNCOV
391
        dr.d_type = rr.qtype.getCode();
276✔
UNCOV
392
        dr.d_clen = val.size() - pr.getPosition();
276✔
393

UNCOV
394
        auto drc = DNSRecordContent::make(dr, pr);
276✔
UNCOV
395
        rr.content = drc->getZoneRepresentation();
276✔
UNCOV
396
        DLOG(cerr << "CONTENT: " << rr.content << endl);
276✔
UNCOV
397
      }
276✔
UNCOV
398
      catch (...) {
276✔
399
        g_log << Logger::Error << backendname << "Failed to parse record content for " << rr.qname << " with type " << rr.qtype.toString();
×
400
        if (d_ignorebogus || d_isGetDomains) {
×
401
          g_log << ". Ignoring!" << endl;
×
402
          continue;
×
403
        }
×
404
        else {
×
405
          g_log << ". Erroring out!" << endl;
×
406
          throw;
×
407
        }
×
408
      }
×
409
#if 0
410
      DLOG(g_log<<Logger::Debug<<backendname<<"Returning ["<<rr.content<<"] for ["<<rr.qname<<"] of RecordType ["<<rr.qtype.toString()<<"]"<<endl;);
411
#endif
UNCOV
412
      return true;
276✔
UNCOV
413
    }
276✔
UNCOV
414
  } // end of while
276✔
UNCOV
415
  DLOG(g_log << Logger::Debug << backendname << "No more records to return." << endl);
219✔
416

UNCOV
417
  d_cdbReader = nullptr;
219✔
UNCOV
418
  d_currentDomain = UnknownDomainID;
219✔
UNCOV
419
  return false;
219✔
UNCOV
420
}
495✔
421

422
// boilerplate
423
class TinyDNSFactory : public BackendFactory
424
{
425
public:
426
  TinyDNSFactory() :
427
    BackendFactory("tinydns") {}
5,904✔
428

429
  void declareArguments(const string& suffix = "") override
UNCOV
430
  {
1✔
UNCOV
431
    declare(suffix, "notify-on-startup", "Tell the TinyDNSBackend to notify all the secondary nameservers on startup. Default is no.", "no");
1✔
UNCOV
432
    declare(suffix, "dbfile", "Location of the cdb data file", "data.cdb");
1✔
UNCOV
433
    declare(suffix, "tai-adjust", "This adjusts the TAI value if timestamps are used. These seconds will be added to the start point (1970) and will allow you to adjust for leap seconds. The default is 11.", "11");
1✔
UNCOV
434
    declare(suffix, "locations", "Enable or Disable location support in the backend. Changing the value to 'no' will make the backend ignore the locations. This then returns all records!", "yes");
1✔
UNCOV
435
    declare(suffix, "ignore-bogus-records", "The data.cdb file might have some incorrect record data, this causes PowerDNS to fail, where tinydns would send out truncated data. This option makes powerdns ignore that data!", "no");
1✔
UNCOV
436
  }
1✔
437

438
  DNSBackend* make(const string& suffix = "") override
UNCOV
439
  {
5✔
UNCOV
440
    return new TinyDNSBackend(suffix);
5✔
UNCOV
441
  }
5✔
442
};
443

444
// boilerplate
445
class TinyDNSLoader
446
{
447
public:
448
  TinyDNSLoader()
449
  {
5,904✔
450
    BackendMakers().report(std::make_unique<TinyDNSFactory>());
5,904✔
451
    g_log << Logger::Info << "[tinydnsbackend] This is the tinydns backend version " VERSION
5,904✔
452
#ifndef REPRODUCIBLE
5,904✔
453
          << " (" __DATE__ " " __TIME__ ")"
5,904✔
454
#endif
5,904✔
455
          << " reporting" << endl;
5,904✔
456
  }
5,904✔
457
};
458

459
static TinyDNSLoader tinydnsloader;
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