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

PowerDNS / pdns / 19347150143

13 Nov 2025 04:36PM UTC coverage: 61.588% (-11.5%) from 73.059%
19347150143

push

github

web-flow
Merge pull request #16494 from jsoref/codeql-unused-loop-iterator-name

Codeql unused loop iterator name

73081 of 185220 branches covered (39.46%)

Branch coverage included in aggregate %.

155550 of 186007 relevant lines covered (83.63%)

7024283.57 hits per line

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

51.21
/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)
79
{
10✔
80
  setArgPrefix("tinydns" + suffix);
10✔
81
  d_suffix = suffix;
10✔
82
  d_locations = mustDo("locations");
10✔
83
  d_ignorebogus = mustDo("ignore-bogus-records");
10✔
84
  d_taiepoch = 4611686018427387904ULL + getArgAsNum("tai-adjust");
10✔
85
  d_dnspacket = NULL;
10✔
86
  d_cdbReader = NULL;
10✔
87
  d_isAxfr = false;
10✔
88
  d_isWildcardQuery = false;
10✔
89
}
10✔
90

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

99
  TinyDomainInfo tmp;
17✔
100
  s_lastId++;
17✔
101
  tmp.zone = domain.zone;
17✔
102
  tmp.id = s_lastId;
17✔
103
  tmp.notified_serial = domain.serial;
17!
104
  return state->insert(tmp).first;
17!
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)
155
{
2✔
156
  d_isAxfr = true;
2✔
157
  d_isGetDomains = true;
2✔
158
  d_dnspacket = NULL;
2✔
159

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

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

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

17!
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

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

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

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

209
  getAllDomains_locked(domains, getSerial);
1✔
210

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

213✔
218
//NOLINTNEXTLINE(readability-identifier-length)
213✔
219
bool TinyDNSBackend::getDomainInfo(const ZoneName& domain, DomainInfo& di, bool getSerial)
220
{
213✔
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) {
213!
223
    TDI_t tmp;
213✔
224
    domainInfo->emplace(d_suffix, tmp);
225
  }
213✔
226

213✔
227
  TDI_t* state = &(*domainInfo)[d_suffix];
49✔
228

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

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

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

259
  return d_cdbReader->searchSuffix(key);
20,620✔
260
}
261

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

11✔
268
  string key = simpleCompress(queryDomain);
229✔
269

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

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

269!
279
  d_qtype = qtype;
487✔
280

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

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

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

298
  while (d_cdbReader->readNext(record)) {
20,849✔
299
    string val = record.second;
20,630✔
300
    string key = record.first;
20,630!
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);
20,630!
305
#endif
306
    if (key[0] == '\000' && key[1] == '\045') { // skip locations
269!
307
      continue;
308
    }
20,630✔
309

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

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

20,860!
322
    PacketReader pr(val, 0);
323
    rr.qtype = QType(pr.get16BitInt());
20,591✔
324

20,317!
325
    if (d_isGetDomains && rr.qtype != QType::SOA) {
20,317!
326
      continue;
327
    }
274!
328

274!
329
    if (d_isAxfr || d_qtype.getCode() == QType::ANY || rr.qtype == d_qtype) {
274!
330
      char locwild = pr.get8BitInt();
×
331
      if (locwild != '\075' && (locwild == '\076' || locwild == '\053')) {
×
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();
269!
338

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

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

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

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

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

493✔
417
  d_cdbReader = nullptr;
418
  d_currentDomain = UnknownDomainID;
419
  return false;
420
}
421

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

1✔
429
  void declareArguments(const string& suffix = "") override
1✔
430
  {
1✔
431
    declare(suffix, "notify-on-startup", "Tell the TinyDNSBackend to notify all the secondary nameservers on startup. Default is no.", "no");
1✔
432
    declare(suffix, "dbfile", "Location of the cdb data file", "data.cdb");
1✔
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");
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");
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");
5✔
436
  }
5✔
437

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

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