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

PowerDNS / pdns / 11382357625

17 Oct 2024 09:38AM UTC coverage: 64.665% (+0.005%) from 64.66%
11382357625

Pull #14724

github

web-flow
Merge c574c0d31 into 52c38c7c5
Pull Request #14724: dnsdist: Add meson support

37153 of 88208 branches covered (42.12%)

Branch coverage included in aggregate %.

414 of 581 new or added lines in 39 files covered. (71.26%)

271 existing lines in 19 files now uncovered.

125128 of 162750 relevant lines covered (76.88%)

4774887.93 hits per line

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

40.39
/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
uint32_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
void TinyDNSBackend::getUpdatedPrimaries(vector<DomainInfo>& retDomains, std::unordered_set<DNSName>& /* catalogs */, CatalogHashMap& /* catalogHashes */)
92
{
×
93
  auto domainInfo = s_domainInfo.lock(); //TODO: We could actually lock less if we do it per suffix.
×
94
  if (!domainInfo->count(d_suffix)) {
×
95
    TDI_t tmp;
×
96
    domainInfo->emplace(d_suffix, tmp);
×
97
  }
×
98

99
  TDI_t* domains = &(*domainInfo)[d_suffix];
×
100

101
  vector<DomainInfo> allDomains;
×
102
  getAllDomains(&allDomains, true, false);
×
103
  if (domains->size() == 0 && !mustDo("notify-on-startup")) {
×
104
    for (vector<DomainInfo>::iterator di = allDomains.begin(); di != allDomains.end(); ++di) {
×
105
      di->notified_serial = 0;
×
106
    }
×
107
  }
×
108

109
  for (vector<DomainInfo>::iterator di = allDomains.begin(); di != allDomains.end(); ++di) {
×
110
    TDIByZone_t& zone_index = domains->get<tag_zone>();
×
111
    TDIByZone_t::iterator itByZone = zone_index.find(di->zone);
×
112
    if (itByZone == zone_index.end()) {
×
113
      s_lastId++;
×
114

115
      TinyDomainInfo tmp;
×
116
      tmp.zone = di->zone;
×
117
      tmp.id = s_lastId;
×
118
      tmp.notified_serial = di->serial;
×
119
      domains->insert(tmp);
×
120

121
      di->id = s_lastId;
×
122
      if (di->notified_serial > 0) {
×
123
        retDomains.push_back(*di);
×
124
      }
×
125
    }
×
126
    else {
×
127
      if (itByZone->notified_serial < di->serial) {
×
128
        di->id = itByZone->id;
×
129
        retDomains.push_back(*di);
×
130
      }
×
131
    }
×
132
  }
×
133
}
×
134

135
void TinyDNSBackend::setNotified(uint32_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* domains = &(*domainInfo)[d_suffix];
×
142
  TDIById_t& domain_index = domains->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] = *domains;
×
152
}
×
153

154
void TinyDNSBackend::getAllDomains(vector<DomainInfo>* domains, bool getSerial, bool /* include_disabled */)
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
  }
1✔
UNCOV
163
  catch (const std::exception& e) {
1✔
164
    g_log << Logger::Error << e.what() << endl;
×
165
    throw PDNSException(e.what());
×
166
  }
×
167

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

UNCOV
172
  while (get(rr)) {
18✔
UNCOV
173
    if (rr.qtype.getCode() == QType::SOA && dupcheck.insert(rr.qname).second) {
17!
UNCOV
174
      DomainInfo di;
17✔
UNCOV
175
      di.id = -1; //TODO: Check if this is ok.
17✔
UNCOV
176
      di.backend = this;
17✔
UNCOV
177
      di.zone = rr.qname;
17✔
UNCOV
178
      di.kind = DomainInfo::Primary;
17✔
UNCOV
179
      di.last_check = time(0);
17✔
180

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

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

198
bool TinyDNSBackend::list(const DNSName& target, int /* domain_id */, bool /* include_disabled */)
199
{
×
200
  d_isAxfr = true;
×
201
  d_isGetDomains = false;
×
202
  string key = target.toDNSStringLC();
×
203
  try {
×
204
    d_cdbReader = std::make_unique<CDB>(getArg("dbfile"));
×
205
  }
×
206
  catch (const std::exception& e) {
×
207
    g_log << Logger::Error << e.what() << endl;
×
208
    throw PDNSException(e.what());
×
209
  }
×
210

211
  return d_cdbReader->searchSuffix(key);
×
212
}
×
213

214
void TinyDNSBackend::lookup(const QType& qtype, const DNSName& qdomain, int /* zoneId */, DNSPacket* pkt_p)
UNCOV
215
{
213✔
UNCOV
216
  d_isAxfr = false;
213✔
UNCOV
217
  d_isGetDomains = false;
213✔
UNCOV
218
  string queryDomain = toLowerCanonic(qdomain.toString());
213✔
219

UNCOV
220
  string key = simpleCompress(queryDomain);
213✔
221

UNCOV
222
  DLOG(g_log << Logger::Debug << backendname << "[lookup] query for qtype [" << qtype.toString() << "] qdomain [" << qdomain << "]" << endl);
213✔
UNCOV
223
  DLOG(g_log << Logger::Debug << "[lookup] key [" << makeHexDump(key) << "]" << endl);
213✔
224

UNCOV
225
  d_isWildcardQuery = false;
213✔
UNCOV
226
  if (key[0] == '\001' && key[1] == '\052') {
213✔
UNCOV
227
    d_isWildcardQuery = true;
49✔
UNCOV
228
    key.erase(0, 2);
49✔
UNCOV
229
  }
49✔
230

UNCOV
231
  d_qtype = qtype;
213✔
232

UNCOV
233
  try {
213✔
UNCOV
234
    d_cdbReader = std::make_unique<CDB>(getArg("dbfile"));
213✔
UNCOV
235
  }
213✔
UNCOV
236
  catch (const std::exception& e) {
213✔
237
    g_log << Logger::Error << e.what() << endl;
×
238
    throw PDNSException(e.what());
×
239
  }
×
240

UNCOV
241
  d_cdbReader->searchKey(key);
213✔
UNCOV
242
  d_dnspacket = pkt_p;
213✔
UNCOV
243
}
213✔
244

245
bool TinyDNSBackend::get(DNSResourceRecord& rr)
UNCOV
246
{
482✔
UNCOV
247
  pair<string, string> record;
482✔
248

UNCOV
249
  while (d_cdbReader->readNext(record)) {
20,832✔
UNCOV
250
    string val = record.second;
20,618✔
UNCOV
251
    string key = record.first;
20,618✔
252

253
    //DLOG(g_log<<Logger::Debug<<"[GET] Key: "<<makeHexDump(key)<<endl);
254
    //DLOG(g_log<<Logger::Debug<<"[GET] Val: "<<makeHexDump(val)<<endl);
UNCOV
255
    if (key[0] == '\000' && key[1] == '\045') { // skip locations
20,618!
256
      continue;
×
257
    }
×
258

UNCOV
259
    if (!d_isAxfr) {
20,618✔
260
      // If we have a wildcard query, but the record we got is not a wildcard, we skip.
UNCOV
261
      if (d_isWildcardQuery && val[2] != '\052' && val[2] != '\053') {
290!
UNCOV
262
        continue;
28✔
UNCOV
263
      }
28✔
264

265
      // If it is NOT a wildcard query, but we do find a wildcard record, we skip it.
UNCOV
266
      if (!d_isWildcardQuery && (val[2] == '\052' || val[2] == '\053')) {
262!
UNCOV
267
        continue;
11✔
UNCOV
268
      }
11✔
UNCOV
269
    }
262✔
270

UNCOV
271
    PacketReader pr(val, 0);
20,579✔
UNCOV
272
    rr.qtype = QType(pr.get16BitInt());
20,579✔
273

UNCOV
274
    if (d_isGetDomains && rr.qtype != QType::SOA) {
20,579✔
UNCOV
275
      continue;
20,311✔
UNCOV
276
    }
20,311✔
277

UNCOV
278
    if (d_isAxfr || d_qtype.getCode() == QType::ANY || rr.qtype == d_qtype) {
268!
UNCOV
279
      char locwild = pr.get8BitInt();
268✔
UNCOV
280
      if (locwild != '\075' && (locwild == '\076' || locwild == '\053')) {
268!
281
        if (d_isAxfr && d_locations) { // We skip records with a location in AXFR, unless we disable locations.
×
282
          continue;
×
283
        }
×
284
        char recloc[2];
×
285
        recloc[0] = pr.get8BitInt();
×
286
        recloc[1] = pr.get8BitInt();
×
287

288
        if (d_locations) {
×
289
          bool foundLocation = false;
×
290
          vector<string> locations = getLocations();
×
291
          while (locations.size() > 0) {
×
292
            string locId = locations.back();
×
293
            locations.pop_back();
×
294

295
            if (recloc[0] == locId[0] && recloc[1] == locId[1]) {
×
296
              foundLocation = true;
×
297
              break;
×
298
            }
×
299
          }
×
300
          if (!foundLocation) {
×
301
            continue;
×
302
          }
×
303
        }
×
304
      }
×
305

UNCOV
306
      if (d_isAxfr && (val[2] == '\052' || val[2] == '\053')) { // Keys are not stored with wildcard character, with AXFR we need to add that.
268!
307
        key.insert(0, 1, '\052');
×
308
        key.insert(0, 1, '\001');
×
309
      }
×
310
      // rr.qname.clear();
UNCOV
311
      rr.qname = DNSName(key.c_str(), key.size(), 0, false);
268✔
UNCOV
312
      rr.domain_id = -1;
268✔
313
      // 11:13.21 <@ahu> IT IS ALWAYS AUTH --- well not really because we are just a backend :-)
314
      // We could actually do NSEC3-NARROW DNSSEC according to Habbie, if we do, we need to change something here.
UNCOV
315
      rr.auth = true;
268✔
316

UNCOV
317
      rr.ttl = pr.get32BitInt();
268✔
UNCOV
318
      uint64_t timestamp = pr.get32BitInt();
268✔
UNCOV
319
      timestamp <<= 32;
268✔
UNCOV
320
      timestamp += pr.get32BitInt();
268✔
UNCOV
321
      if (timestamp) {
268!
322
        uint64_t now = d_taiepoch + time(NULL);
×
323
        if (rr.ttl == 0) {
×
324
          if (timestamp < now) {
×
325
            continue;
×
326
          }
×
327
          rr.ttl = timestamp - now;
×
328
          if (rr.ttl <= 2)
×
329
            rr.ttl = 2;
×
330
          if (rr.ttl >= 3600)
×
331
            rr.ttl = 3600;
×
332
        }
×
333
        else if (now <= timestamp) {
×
334
          continue;
×
335
        }
×
336
      }
×
UNCOV
337
      try {
268✔
UNCOV
338
        DNSRecord dr;
268✔
UNCOV
339
        dr.d_class = 1;
268✔
UNCOV
340
        dr.d_type = rr.qtype.getCode();
268✔
UNCOV
341
        dr.d_clen = val.size() - pr.getPosition();
268✔
342

UNCOV
343
        auto drc = DNSRecordContent::make(dr, pr);
268✔
UNCOV
344
        rr.content = drc->getZoneRepresentation();
268✔
UNCOV
345
        DLOG(cerr << "CONTENT: " << rr.content << endl);
268✔
UNCOV
346
      }
268✔
UNCOV
347
      catch (...) {
268✔
348
        g_log << Logger::Error << backendname << "Failed to parse record content for " << rr.qname << " with type " << rr.qtype.toString();
×
349
        if (d_ignorebogus || d_isGetDomains) {
×
350
          g_log << ". Ignoring!" << endl;
×
351
          continue;
×
352
        }
×
353
        else {
×
354
          g_log << ". Erroring out!" << endl;
×
355
          throw;
×
356
        }
×
357
      }
×
358
      //      DLOG(g_log<<Logger::Debug<<backendname<<"Returning ["<<rr.content<<"] for ["<<rr.qname<<"] of RecordType ["<<rr.qtype.toString()<<"]"<<endl;);
UNCOV
359
      return true;
268✔
UNCOV
360
    }
268✔
UNCOV
361
  } // end of while
268✔
UNCOV
362
  DLOG(g_log << Logger::Debug << backendname << "No more records to return." << endl);
214✔
363

UNCOV
364
  d_cdbReader = nullptr;
214✔
UNCOV
365
  return false;
214✔
UNCOV
366
}
482✔
367

368
// boilerplate
369
class TinyDNSFactory : public BackendFactory
370
{
371
public:
372
  TinyDNSFactory() :
373
    BackendFactory("tinydns") {}
3,469✔
374

375
  void declareArguments(const string& suffix = "") override
UNCOV
376
  {
1✔
UNCOV
377
    declare(suffix, "notify-on-startup", "Tell the TinyDNSBackend to notify all the secondary nameservers on startup. Default is no.", "no");
1✔
UNCOV
378
    declare(suffix, "dbfile", "Location of the cdb data file", "data.cdb");
1✔
UNCOV
379
    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
380
    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
381
    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
382
  }
1✔
383

384
  DNSBackend* make(const string& suffix = "") override
UNCOV
385
  {
5✔
UNCOV
386
    return new TinyDNSBackend(suffix);
5✔
UNCOV
387
  }
5✔
388
};
389

390
// boilerplate
391
class TinyDNSLoader
392
{
393
public:
394
  TinyDNSLoader()
395
  {
3,469✔
396
    BackendMakers().report(std::make_unique<TinyDNSFactory>());
3,469✔
397
    g_log << Logger::Info << "[tinydnsbackend] This is the tinydns backend version " VERSION
3,469✔
398
#ifndef REPRODUCIBLE
3,469✔
399
          << " (" __DATE__ " " __TIME__ ")"
3,469✔
400
#endif
3,469✔
401
          << " reporting" << endl;
3,469✔
402
  }
3,469✔
403
};
404

405
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