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

PowerDNS / pdns / 8248266633

12 Mar 2024 11:49AM UTC coverage: 57.922% (+4.2%) from 53.746%
8248266633

push

github

web-flow
Merge pull request #13877 from rgacogne/remove-harvestCSPFromRecs

validate: Remove unused `harvestCSPFromRecs()`

31817 of 88102 branches covered (36.11%)

Branch coverage included in aggregate %.

111130 of 158689 relevant lines covered (70.03%)

3849388.97 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)
79
{
5✔
80
  setArgPrefix("tinydns" + suffix);
5✔
81
  d_suffix = suffix;
5✔
82
  d_locations = mustDo("locations");
5✔
83
  d_ignorebogus = mustDo("ignore-bogus-records");
5✔
84
  d_taiepoch = 4611686018427387904ULL + getArgAsNum("tai-adjust");
5✔
85
  d_dnspacket = NULL;
5✔
86
  d_cdbReader = NULL;
5✔
87
  d_isAxfr = false;
5✔
88
  d_isWildcardQuery = false;
5✔
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 */)
155
{
1✔
156
  d_isAxfr = true;
1✔
157
  d_isGetDomains = true;
1✔
158
  d_dnspacket = NULL;
1✔
159

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

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

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

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

192
      di.notified_serial = di.serial;
17✔
193
      domains->push_back(di);
17✔
194
    }
17✔
195
  }
17✔
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)
215
{
213✔
216
  d_isAxfr = false;
213✔
217
  d_isGetDomains = false;
213✔
218
  string queryDomain = toLowerCanonic(qdomain.toString());
213✔
219

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

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

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

231
  d_qtype = qtype;
213✔
232

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

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

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

249
  while (d_cdbReader->readNext(record)) {
20,832✔
250
    string val = record.second;
20,618✔
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);
255
    if (key[0] == '\000' && key[1] == '\045') { // skip locations
20,618!
256
      continue;
×
257
    }
×
258

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

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

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

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

278
    if (d_isAxfr || d_qtype.getCode() == QType::ANY || rr.qtype == d_qtype) {
268!
279
      char locwild = pr.get8BitInt();
268✔
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

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();
311
      rr.qname = DNSName(key.c_str(), key.size(), 0, false);
268✔
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.
315
      rr.auth = true;
268✔
316

317
      rr.ttl = pr.get32BitInt();
268✔
318
      uint64_t timestamp = pr.get32BitInt();
268✔
319
      timestamp <<= 32;
268✔
320
      timestamp += pr.get32BitInt();
268✔
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
      }
×
337
      try {
268✔
338
        DNSRecord dr;
268✔
339
        dr.d_class = 1;
268✔
340
        dr.d_type = rr.qtype.getCode();
268✔
341
        dr.d_clen = val.size() - pr.getPosition();
268✔
342

343
        auto drc = DNSRecordContent::make(dr, pr);
268✔
344
        rr.content = drc->getZoneRepresentation();
268✔
345
        DLOG(cerr << "CONTENT: " << rr.content << endl);
268✔
346
      }
268✔
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;);
359
      return true;
268✔
360
    }
268✔
361
  } // end of while
268✔
362
  DLOG(g_log << Logger::Debug << backendname << "No more records to return." << endl);
214✔
363

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

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

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

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

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