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

PowerDNS / pdns / 15920796590

27 Jun 2025 07:31AM UTC coverage: 65.647% (+0.03%) from 65.614%
15920796590

Pull #15733

github

web-flow
Merge f99cd2479 into a44ba546f
Pull Request #15733: Reduce ZoneName::toString mess

41569 of 91856 branches covered (45.25%)

Branch coverage included in aggregate %.

86 of 120 new or added lines in 8 files covered. (71.67%)

53 existing lines in 12 files now uncovered.

126844 of 164688 relevant lines covered (77.02%)

5123086.44 hits per line

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

73.44
/pdns/packethandler.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 "packetcache.hh"
26
#include "utility.hh"
27
#include "base32.hh"
28
#include "base64.hh"
29
#include <string>
30
#include <sys/types.h>
31
#include <boost/algorithm/string.hpp>
32
#include "dnssecinfra.hh"
33
#include "dnsseckeeper.hh"
34
#include "dns.hh"
35
#include "dnsbackend.hh"
36
#include "ueberbackend.hh"
37
#include "dnspacket.hh"
38
#include "nameserver.hh"
39
#include "distributor.hh"
40
#include "logger.hh"
41
#include "arguments.hh"
42
#include "packethandler.hh"
43
#include "statbag.hh"
44
#include "resolver.hh"
45
#include "communicator.hh"
46
#include "dnsproxy.hh"
47
#include "version.hh"
48
#include "auth-main.hh"
49
#include "trusted-notification-proxy.hh"
50
#include "gss_context.hh"
51

52
#if 0
53
#undef DLOG
54
#define DLOG(x) x
55
#endif
56

57
AtomicCounter PacketHandler::s_count;
58
NetmaskGroup PacketHandler::s_allowNotifyFrom;
59
set<string> PacketHandler::s_forwardNotify;
60
bool PacketHandler::s_SVCAutohints{false};
61

62
extern string g_programname;
63

64
// See https://www.rfc-editor.org/rfc/rfc8078.txt and https://www.rfc-editor.org/errata/eid5049 for details
65
const std::shared_ptr<CDNSKEYRecordContent> PacketHandler::s_deleteCDNSKEYContent = std::make_shared<CDNSKEYRecordContent>("0 3 0 AA==");
66
const std::shared_ptr<CDSRecordContent> PacketHandler::s_deleteCDSContent = std::make_shared<CDSRecordContent>("0 0 0 00");
67

68
PacketHandler::PacketHandler():B(g_programname), d_dk(&B)
69
{
754✔
70
  ++s_count;
754✔
71
  d_doDNAME=::arg().mustDo("dname-processing");
754✔
72
  d_doExpandALIAS = ::arg().mustDo("expand-alias");
754✔
73
  d_doResolveAcrossZones = ::arg().mustDo("resolve-across-zones");
754✔
74
  d_logDNSDetails= ::arg().mustDo("log-dns-details");
754✔
75
  string fname= ::arg()["lua-prequery-script"];
754✔
76

77
  if(fname.empty())
754✔
78
  {
332✔
79
    d_pdl = nullptr;
332✔
80
  }
332✔
81
  else
422✔
82
  {
422✔
83
    d_pdl = std::make_unique<AuthLua4>(::arg()["lua-global-include-dir"]);
422✔
84
    d_pdl->loadFile(fname);
422✔
85
  }
422✔
86
  fname = ::arg()["lua-dnsupdate-policy-script"];
754✔
87
  if (fname.empty())
754!
88
  {
754✔
89
    d_update_policy_lua = nullptr;
754✔
90
  }
754✔
91
  else
×
92
  {
×
93
    try {
×
94
      d_update_policy_lua = std::make_unique<AuthLua4>();
×
95
      d_update_policy_lua->loadFile(fname);
×
96
    }
×
97
    catch (const std::runtime_error& e) {
×
98
      g_log<<Logger::Warning<<"Failed to load update policy - disabling: "<<e.what()<<endl;
×
99
      d_update_policy_lua = nullptr;
×
100
    }
×
101
  }
×
102
}
754✔
103

104
UeberBackend *PacketHandler::getBackend()
105
{
4,089✔
106
  return &B;
4,089✔
107
}
4,089✔
108

109
PacketHandler::~PacketHandler()
110
{
×
111
  --s_count;
×
112
  DLOG(g_log<<Logger::Error<<"PacketHandler destructor called - "<<s_count<<" left"<<endl);
×
113
}
×
114

115
/**
116
 * This adds CDNSKEY records to the answer packet. Returns true if one was added.
117
 *
118
 * @param p          Pointer to the DNSPacket containing the original question
119
 * @param r          Pointer to the DNSPacket where the records should be inserted into
120
 * @return           bool that shows if any records were added
121
**/
122
bool PacketHandler::addCDNSKEY(DNSPacket& p, std::unique_ptr<DNSPacket>& r)
123
{
1,672✔
124
  string publishCDNSKEY;
1,672✔
125
  d_dk.getPublishCDNSKEY(r->qdomainzone,publishCDNSKEY);
1,672✔
126
  if (publishCDNSKEY.empty())
1,672✔
127
    return false;
364✔
128

129
  DNSZoneRecord rr;
1,308✔
130
  rr.dr.d_type=QType::CDNSKEY;
1,308✔
131
  rr.dr.d_ttl=d_sd.minimum;
1,308✔
132
  rr.dr.d_name=p.qdomain;
1,308✔
133
  rr.auth=true;
1,308✔
134

135
  if (publishCDNSKEY == "0") { // delete DS via CDNSKEY
1,308✔
136
    rr.dr.setContent(s_deleteCDNSKEYContent);
436✔
137
    r->addRecord(std::move(rr));
436✔
138
    return true;
436✔
139
  }
436✔
140

141
  bool haveOne=false;
872✔
142
  for (const auto& value : d_dk.getEntryPoints(r->qdomainzone)) {
872✔
143
    if (!value.second.published) {
872!
144
      continue;
×
145
    }
×
146
    rr.dr.setContent(std::make_shared<DNSKEYRecordContent>(value.first.getDNSKEY()));
872✔
147
    r->addRecord(DNSZoneRecord(rr));
872✔
148
    haveOne=true;
872✔
149
  }
872✔
150

151
  if(::arg().mustDo("direct-dnskey")) {
872✔
152
    B.lookup(QType(QType::CDNSKEY), p.qdomain, d_sd.domain_id, &p);
88✔
153

154
    while(B.get(rr)) {
88!
155
      rr.dr.d_ttl=d_sd.minimum;
×
156
      r->addRecord(std::move(rr));
×
157
      haveOne=true;
×
158
    }
×
159
  }
88✔
160
  return haveOne;
872✔
161
}
1,308✔
162

163
/**
164
 * This adds DNSKEY records to the answer packet. Returns true if one was added.
165
 *
166
 * @param p          Pointer to the DNSPacket containing the original question
167
 * @param r          Pointer to the DNSPacket where the records should be inserted into
168
 * @return           bool that shows if any records were added
169
**/
170
bool PacketHandler::addDNSKEY(DNSPacket& p, std::unique_ptr<DNSPacket>& r)
171
{
23,476✔
172
  DNSZoneRecord rr;
23,476✔
173
  bool haveOne=false;
23,476✔
174

175
  for (const auto& value : d_dk.getKeys(r->qdomainzone)) {
24,247✔
176
    if (!value.second.published) {
24,147✔
177
      continue;
432✔
178
    }
432✔
179
    rr.dr.d_type=QType::DNSKEY;
23,715✔
180
    rr.dr.d_ttl=d_sd.minimum;
23,715✔
181
    rr.dr.d_name=p.qdomain;
23,715✔
182
    rr.dr.setContent(std::make_shared<DNSKEYRecordContent>(value.first.getDNSKEY()));
23,715✔
183
    rr.auth=true;
23,715✔
184
    r->addRecord(std::move(rr));
23,715✔
185
    haveOne=true;
23,715✔
186
  }
23,715✔
187

188
  if(::arg().mustDo("direct-dnskey")) {
23,476✔
189
    B.lookup(QType(QType::DNSKEY), p.qdomain, d_sd.domain_id, &p);
2,055✔
190

191
    while(B.get(rr)) {
2,055!
192
      rr.dr.d_ttl=d_sd.minimum;
×
193
      r->addRecord(std::move(rr));
×
194
      haveOne=true;
×
195
    }
×
196
  }
2,055✔
197

198
  return haveOne;
23,476✔
199
}
23,476✔
200

201
/**
202
 * This adds CDS records to the answer packet r.
203
 *
204
 * @param p   Pointer to the DNSPacket containing the original question.
205
 * @param r   Pointer to the DNSPacket where the records should be inserted into.
206
 *            used to determine record TTL.
207
 * @return    bool that shows if any records were added.
208
**/
209
bool PacketHandler::addCDS(DNSPacket& p, std::unique_ptr<DNSPacket>& r)
210
{
1,677✔
211
  string publishCDS;
1,677✔
212
  d_dk.getPublishCDS(r->qdomainzone, publishCDS);
1,677✔
213
  if (publishCDS.empty())
1,677✔
214
    return false;
365✔
215

216
  vector<string> digestAlgos;
1,312✔
217
  stringtok(digestAlgos, publishCDS, ", ");
1,312✔
218

219
  DNSZoneRecord rr;
1,312✔
220
  rr.dr.d_type=QType::CDS;
1,312✔
221
  rr.dr.d_ttl=d_sd.minimum;
1,312✔
222
  rr.dr.d_name=p.qdomain;
1,312✔
223
  rr.auth=true;
1,312✔
224

225
  if(std::find(digestAlgos.begin(), digestAlgos.end(), "0") != digestAlgos.end()) { // delete DS via CDS
1,312✔
226
    rr.dr.setContent(s_deleteCDSContent);
437✔
227
    r->addRecord(std::move(rr));
437✔
228
    return true;
437✔
229
  }
437✔
230

231
  bool haveOne=false;
875✔
232

233
  for (const auto& value : d_dk.getEntryPoints(r->qdomainzone)) {
878✔
234
    if (!value.second.published) {
878!
235
      continue;
×
236
    }
×
237
    for(auto const &digestAlgo : digestAlgos){
880✔
238
      rr.dr.setContent(std::make_shared<DSRecordContent>(makeDSFromDNSKey(p.qdomain, value.first.getDNSKEY(), pdns::checked_stoi<uint8_t>(digestAlgo))));
880✔
239
      r->addRecord(DNSZoneRecord(rr));
880✔
240
      haveOne=true;
880✔
241
    }
880✔
242
  }
878✔
243

244
  if(::arg().mustDo("direct-dnskey")) {
875✔
245
    B.lookup(QType(QType::CDS), p.qdomain, d_sd.domain_id, &p);
88✔
246

247
    while(B.get(rr)) {
88!
248
      rr.dr.d_ttl=d_sd.minimum;
×
249
      r->addRecord(std::move(rr));
×
250
      haveOne=true;
×
251
    }
×
252
  }
88✔
253

254
  return haveOne;
875✔
255
}
1,312✔
256

257
/** This adds NSEC3PARAM records. Returns true if one was added */
258
bool PacketHandler::addNSEC3PARAM(const DNSPacket& p, std::unique_ptr<DNSPacket>& r)
259
{
2,398✔
260
  DNSZoneRecord rr;
2,398✔
261

262
  NSEC3PARAMRecordContent ns3prc;
2,398✔
263
  if(d_dk.getNSEC3PARAM(r->qdomainzone, &ns3prc)) {
2,398✔
264
    rr.dr.d_type=QType::NSEC3PARAM;
1,448✔
265
    rr.dr.d_ttl=d_sd.minimum;
1,448✔
266
    rr.dr.d_name=p.qdomain;
1,448✔
267
    ns3prc.d_flags = 0; // the NSEC3PARAM 'flag' is defined to always be zero in RFC5155.
1,448✔
268
    rr.dr.setContent(std::make_shared<NSEC3PARAMRecordContent>(ns3prc));
1,448✔
269
    rr.auth = true;
1,448✔
270
    r->addRecord(std::move(rr));
1,448✔
271
    return true;
1,448✔
272
  }
1,448✔
273
  return false;
950✔
274
}
2,398✔
275

276

277
// This is our chaos class requests handler. Return 1 if content was added, 0 if it wasn't
278
int PacketHandler::doChaosRequest(const DNSPacket& p, std::unique_ptr<DNSPacket>& r, DNSName &target) const
279
{
×
280
  DNSZoneRecord rr;
×
281

282
  if(p.qtype.getCode()==QType::TXT) {
×
283
    static const DNSName versionbind("version.bind."), versionpdns("version.pdns."), idserver("id.server.");
×
284
    if (target==versionbind || target==versionpdns) {
×
285
      // modes: full, powerdns only, anonymous or custom
286
      const static string mode=::arg()["version-string"];
×
287
      string content;
×
288
      if(mode=="full")
×
289
        content=fullVersionString();
×
290
      else if(mode=="powerdns")
×
291
        content="Served by PowerDNS - https://www.powerdns.com/";
×
292
      else if(mode=="anonymous") {
×
293
        r->setRcode(RCode::ServFail);
×
294
        return 0;
×
295
      }
×
296
      else
×
297
        content=mode;
×
298
      rr.dr.setContent(DNSRecordContent::make(QType::TXT, 1, "\"" + content + "\""));
×
299
    }
×
300
    else if (target==idserver) {
×
301
      // modes: disabled, hostname or custom
302
      const static string id=::arg()["server-id"];
×
303

304
      if (id == "disabled") {
×
305
        r->setRcode(RCode::Refused);
×
306
        return 0;
×
307
      }
×
308
      string tid=id;
×
309
      if(!tid.empty() && tid[0]!='"') { // see #6010 however
×
310
        tid = "\"" + tid + "\"";
×
311
      }
×
312
      rr.dr.setContent(DNSRecordContent::make(QType::TXT, 1, tid));
×
313
    }
×
314
    else {
×
315
      r->setRcode(RCode::Refused);
×
316
      return 0;
×
317
    }
×
318

319
    rr.dr.d_ttl=5;
×
320
    rr.dr.d_name=target;
×
321
    rr.dr.d_type=QType::TXT;
×
322
    rr.dr.d_class=QClass::CHAOS;
×
323
    r->addRecord(std::move(rr));
×
324
    return 1;
×
325
  }
×
326

327
  r->setRcode(RCode::NotImp);
×
328
  return 0;
×
329
}
×
330

331
vector<DNSZoneRecord> PacketHandler::getBestReferralNS(DNSPacket& p, const DNSName &target)
332
{
83,665✔
333
  vector<DNSZoneRecord> ret;
83,665✔
334
  DNSZoneRecord rr;
83,665✔
335
  DNSName subdomain(target);
83,665✔
336
  do {
201,025✔
337
    if(subdomain == d_sd.qname()) { // stop at SOA
201,025✔
338
      break;
77,761✔
339
    }
77,761✔
340
    B.lookup(QType(QType::NS), subdomain, d_sd.domain_id, &p);
123,264✔
341
    while(B.get(rr)) {
131,902✔
342
      ret.push_back(rr); // this used to exclude auth NS records for some reason
8,638✔
343
    }
8,638✔
344
    if(!ret.empty()) {
123,264✔
345
      return ret;
5,905✔
346
    }
5,905✔
347
  } while( subdomain.chopOff() );   // 'www.powerdns.org' -> 'powerdns.org' -> 'org' -> ''
123,264✔
348
  return ret;
77,760✔
349
}
83,665✔
350

351
void PacketHandler::getBestDNAMESynth(DNSPacket& p, DNSName &target, vector<DNSZoneRecord> &ret)
352
{
15,562✔
353
  ret.clear();
15,562✔
354
  DNSZoneRecord rr;
15,562✔
355
  DNSName prefix;
15,562✔
356
  DNSName subdomain(target);
15,562✔
357
  do {
52,109✔
358
    DLOG(g_log<<"Attempting DNAME lookup for "<<subdomain<<", d_sd.qname()="<<d_sd.qname()<<endl);
52,109✔
359

360
    B.lookup(QType(QType::DNAME), subdomain, d_sd.domain_id, &p);
52,109✔
361
    while(B.get(rr)) {
53,440✔
362
      ret.push_back(rr);  // put in the original
1,331✔
363
      rr.dr.d_type = QType::CNAME;
1,331✔
364
      rr.dr.d_name = prefix + rr.dr.d_name;
1,331✔
365
      rr.dr.setContent(std::make_shared<CNAMERecordContent>(CNAMERecordContent(prefix + getRR<DNAMERecordContent>(rr.dr)->getTarget())));
1,331✔
366
      rr.auth = false; // don't sign CNAME
1,331✔
367
      target = getRR<CNAMERecordContent>(rr.dr)->getTarget();
1,331✔
368
      ret.push_back(rr);
1,331✔
369
    }
1,331✔
370
    if(!ret.empty()) {
52,109✔
371
      return;
951✔
372
    }
951✔
373
    if(subdomain.countLabels() != 0) {
51,158✔
374
      prefix.appendRawLabel(subdomain.getRawLabels()[0]); // XXX DNSName pain this feels wrong
50,028✔
375
    }
50,028✔
376
    if(subdomain == d_sd.qname()) { // stop at SOA
51,158✔
377
      break;
14,234✔
378
    }
14,234✔
379

380
  } while( subdomain.chopOff() );   // 'www.powerdns.org' -> 'powerdns.org' -> 'org' -> ''
51,158✔
381
  return;
14,611✔
382
}
15,562✔
383

384

385
// Return best matching wildcard or next closer name
386
bool PacketHandler::getBestWildcard(DNSPacket& p, const DNSName &target, DNSName &wildcard, vector<DNSZoneRecord>* ret)
387
{
23,611✔
388
  ret->clear();
23,611✔
389
  DNSZoneRecord rr;
23,611✔
390
  DNSName subdomain(target);
23,611✔
391
  bool haveSomething=false;
23,611✔
392
  bool haveCNAME = false;
23,611✔
393

394
#ifdef HAVE_LUA_RECORDS
23,611✔
395
  bool doLua=g_doLuaRecord;
23,611✔
396
  if(!doLua) {
23,611✔
397
    string val;
23,607✔
398
    d_dk.getFromMeta(d_sd.zonename, "ENABLE-LUA-RECORDS", val);
23,607✔
399
    doLua = (val=="1");
23,607✔
400
  }
23,607✔
401
#endif
23,611✔
402

403
  wildcard=subdomain;
23,611✔
404
  while( subdomain.chopOff() && !haveSomething )  {
45,493!
405
    if (subdomain.empty()) {
45,493!
406
      B.lookup(QType(QType::ANY), g_wildcarddnsname, d_sd.domain_id, &p);
×
407
    } else {
45,493✔
408
      B.lookup(QType(QType::ANY), g_wildcarddnsname+subdomain, d_sd.domain_id, &p);
45,493✔
409
    }
45,493✔
410
    while(B.get(rr)) {
56,194✔
411
      if (haveCNAME) {
10,701✔
412
        continue;
792✔
413
      }
792✔
414
#ifdef HAVE_LUA_RECORDS
9,909✔
415
      if (rr.dr.d_type == QType::LUA && !d_dk.isPresigned(d_sd.zonename)) {
9,909!
416
        if(!doLua) {
×
417
          DLOG(g_log<<"Have a wildcard Lua match, but not doing Lua record for this zone"<<endl);
×
418
          continue;
×
419
        }
×
420

421
        DLOG(g_log<<"Have a wildcard Lua match"<<endl);
×
422

423
        auto rec=getRR<LUARecordContent>(rr.dr);
×
424
        if (!rec) {
×
425
          continue;
×
426
        }
×
427
        if(rec->d_type == QType::CNAME || rec->d_type == p.qtype.getCode() || (p.qtype.getCode() == QType::ANY && rec->d_type != QType::RRSIG)) {
×
428
          //    noCache=true;
429
          DLOG(g_log<<"Executing Lua: '"<<rec->getCode()<<"'"<<endl);
×
430
          try {
×
431
            auto recvec=luaSynth(rec->getCode(), target, rr, d_sd.qname(), p, rec->d_type, s_LUA);
×
432
            for (const auto& r : recvec) {
×
433
              rr.dr.d_type = rec->d_type; // might be CNAME
×
434
              rr.dr.setContent(r);
×
435
              rr.scopeMask = p.getRealRemote().getBits(); // this makes sure answer is a specific as your question
×
436
              if (rr.dr.d_type == QType::CNAME) {
×
437
                haveCNAME = true;
×
438
                *ret = {rr};
×
439
                break;
×
440
              }
×
441
              ret->push_back(rr);
×
442
            }
×
443
          }
×
444
          catch (std::exception &e) {
×
445
            B.lookupEnd();                 // don't leave DB handle in bad state
×
446

447
            throw;
×
448
          }
×
449
        }
×
450
      }
×
451
      else
9,909✔
452
#endif
9,909✔
453
      if(rr.dr.d_type != QType::ENT && (rr.dr.d_type == p.qtype.getCode() || rr.dr.d_type == QType::CNAME || (p.qtype.getCode() == QType::ANY && rr.dr.d_type != QType::RRSIG))) {
9,909✔
454
        if (rr.dr.d_type == QType::CNAME) {
6,379✔
455
          haveCNAME = true;
2,579✔
456
          ret->clear();
2,579✔
457
        }
2,579✔
458
        ret->push_back(rr);
6,379✔
459
      }
6,379✔
460

461
      // NOLINTNEXTLINE(readability-misleading-indentation): go home, clang-tidy, you're drunk
462
      wildcard=g_wildcarddnsname+subdomain;
9,909✔
463
      haveSomething=true;
9,909✔
464
    }
9,909✔
465

466
    if ( subdomain == d_sd.qname() || haveSomething ) { // stop at SOA or result
45,493✔
467
      break;
17,620✔
468
    }
17,620✔
469

470
    B.lookup(QType(QType::ANY), subdomain, d_sd.domain_id, &p);
27,873✔
471
    if (B.get(rr)) {
27,873✔
472
      DLOG(g_log<<"No wildcard match, ancestor exists"<<endl);
5,991✔
473
      B.lookupEnd();
5,991✔
474
      break;
5,991✔
475
    }
5,991✔
476
    wildcard=subdomain;
21,882✔
477
  }
21,882✔
478

479
  return haveSomething;
23,611✔
480
}
23,611✔
481

482
DNSName PacketHandler::doAdditionalServiceProcessing(const DNSName &firstTarget, const uint16_t &qtype, std::unique_ptr<DNSPacket>& /* r */, vector<DNSZoneRecord>& extraRecords) {
682✔
483
  DNSName ret = firstTarget;
682✔
484
  size_t ctr = 5; // Max 5 SVCB Aliasforms per query
682✔
485
  bool done = false;
682✔
486
  while (!done && ctr > 0) {
1,364!
487
    DNSZoneRecord rr;
682✔
488
    done = true;
682✔
489

490
    if(!ret.isPartOf(d_sd.qname())) {
682✔
491
      continue;
341✔
492
    }
341✔
493

494
    B.lookup(QType(qtype), ret, d_sd.domain_id);
341✔
495
    while (B.get(rr)) {
682✔
496
      rr.dr.d_place = DNSResourceRecord::ADDITIONAL;
341✔
497
      switch (qtype) {
341✔
498
        case QType::SVCB: /* fall-through */
341!
499
        case QType::HTTPS: {
341!
500
          auto rrc = getRR<SVCBBaseRecordContent>(rr.dr);
341✔
501
          extraRecords.push_back(std::move(rr));
341✔
502
          ret = rrc->getTarget().isRoot() ? ret : rrc->getTarget();
341!
503
          if (rrc->getPriority() == 0) {
341!
504
            done = false;
×
505
          }
×
506
          break;
341✔
507
        }
341✔
508
        default:
×
509
          B.lookupEnd();              // don't leave DB handle in bad state
×
510

511
          throw PDNSException("Unknown type (" + QType(qtype).toString() + ") for additional service processing");
×
512
      }
341✔
513
    }
341✔
514
    ctr--;
341✔
515
  }
341✔
516
  return ret;
682✔
517
}
682✔
518

519

520
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
521
void PacketHandler::doAdditionalProcessing(DNSPacket& p, std::unique_ptr<DNSPacket>& r)
522
{
108,782✔
523
  DNSName content;
108,782✔
524
  DNSZoneRecord dzr;
108,782✔
525
  std::unordered_set<DNSName> lookup;
108,782✔
526
  vector<DNSZoneRecord> extraRecords;
108,782✔
527
  const auto& rrs = r->getRRS();
108,782✔
528

529
  lookup.reserve(rrs.size());
108,782✔
530
  for(auto& rr : rrs) {
222,577✔
531
    if(rr.dr.d_place != DNSResourceRecord::ADDITIONAL) {
222,578✔
532
      content.clear();
222,572✔
533
      switch(rr.dr.d_type) {
222,572✔
534
        case QType::NS:
15,126✔
535
          content=getRR<NSRecordContent>(rr.dr)->getNS();
15,126✔
536
          break;
15,126✔
537
        case QType::MX:
5,780✔
538
          content=getRR<MXRecordContent>(rr.dr)->d_mxname;
5,780✔
539
          break;
5,780✔
540
        case QType::SRV:
1,879✔
541
          content=getRR<SRVRecordContent>(rr.dr)->d_target;
1,879✔
542
          break;
1,879✔
543
        case QType::SVCB: /* fall-through */
1,705✔
544
        case QType::HTTPS: {
1,705!
545
          auto rrc = getRR<SVCBBaseRecordContent>(rr.dr);
1,705✔
546
          content = rrc->getTarget();
1,705✔
547
          if (content.isRoot()) {
1,705✔
548
            content = rr.dr.d_name;
1,023✔
549
          }
1,023✔
550
          if (rrc->getPriority() == 0) {
1,705✔
551
            content = doAdditionalServiceProcessing(content, rr.dr.d_type, r, extraRecords);
682✔
552
          }
682✔
553
          break;
1,705✔
554
        }
1,705✔
555
        case QType::NAPTR: {
2,046✔
556
          auto naptrContent = getRR<NAPTRRecordContent>(rr.dr);
2,046✔
557
          auto flags = naptrContent->getFlags();
2,046✔
558
          toLowerInPlace(flags);
2,046✔
559
          if (flags.find('a') != string::npos) {
2,046✔
560
            content = naptrContent->getReplacement();
682✔
561
            DLOG(g_log<<Logger::Debug<<"adding NAPTR replacement 'a'="<<content<<endl);
682✔
562
          }
682✔
563
          else if (flags.find('s') != string::npos) {
1,364✔
564
            content = naptrContent->getReplacement();
682✔
565
            DLOG(g_log<<Logger::Debug<<"adding NAPTR replacement 's'="<<content<<endl);
682✔
566
            B.lookup(QType(QType::SRV), content, d_sd.domain_id, &p);
682✔
567
            while(B.get(dzr)) {
2,046✔
568
              content=getRR<SRVRecordContent>(dzr.dr)->d_target;
1,364✔
569
              if(content.isPartOf(d_sd.qname())) {
1,364!
570
                lookup.emplace(content);
1,364✔
571
              }
1,364✔
572
              dzr.dr.d_place=DNSResourceRecord::ADDITIONAL;
1,364✔
573
              extraRecords.emplace_back(std::move(dzr));
1,364✔
574
            }
1,364✔
575
            content.clear();
682✔
576
          }
682✔
577
          break;
2,046✔
578
        }
1,705✔
579
        default:
196,047✔
580
          continue;
196,047✔
581
      }
222,572✔
582
      if(!content.empty() && content.isPartOf(d_sd.qname())) {
26,536✔
583
        lookup.emplace(content);
19,146✔
584
      }
19,146✔
585
    }
26,536✔
586
  }
222,577✔
587

588
  for(auto& rr : extraRecords) {
108,793✔
589
    r->addRecord(std::move(rr));
1,705✔
590
  }
1,705✔
591
  extraRecords.clear();
108,793✔
592
  // TODO should we have a setting to do this?
593
  for (auto &rec : r->getServiceRecords()) {
108,793✔
594
    // Process auto hints
595
    auto rrc = getRR<SVCBBaseRecordContent>(rec->dr);
2,046✔
596
    DNSName target = rrc->getTarget().isRoot() ? rec->dr.d_name : rrc->getTarget();
2,046✔
597

598
    if (rrc->hasParam(SvcParam::ipv4hint) && rrc->autoHint(SvcParam::ipv4hint)) {
2,046!
599
      auto newRRC = rrc->clone();
×
600
      if (!newRRC) {
×
601
        continue;
×
602
      }
×
603
      if (s_SVCAutohints) {
×
604
        auto hints = getIPAddressFor(target, QType::A);
×
605
        if (hints.size() == 0) {
×
606
          newRRC->removeParam(SvcParam::ipv4hint);
×
607
        } else {
×
608
          newRRC->setHints(SvcParam::ipv4hint, hints);
×
609
        }
×
610
      } else {
×
611
        newRRC->removeParam(SvcParam::ipv4hint);
×
612
      }
×
613
      rrc = newRRC;
×
614
      rec->dr.setContent(std::move(newRRC));
×
615
    }
×
616

617
    if (rrc->hasParam(SvcParam::ipv6hint) && rrc->autoHint(SvcParam::ipv6hint)) {
2,046!
618
      auto newRRC = rrc->clone();
×
619
      if (!newRRC) {
×
620
        continue;
×
621
      }
×
622
      if (s_SVCAutohints) {
×
623
        auto hints = getIPAddressFor(target, QType::AAAA);
×
624
        if (hints.size() == 0) {
×
625
          newRRC->removeParam(SvcParam::ipv6hint);
×
626
        } else {
×
627
          newRRC->setHints(SvcParam::ipv6hint, hints);
×
628
        }
×
629
      } else {
×
630
        newRRC->removeParam(SvcParam::ipv6hint);
×
631
      }
×
632
      rec->dr.setContent(std::move(newRRC));
×
633
    }
×
634
  }
2,046✔
635

636
  for(const auto& name : lookup) {
108,793✔
637
    B.lookup(QType(QType::ANY), name, d_sd.domain_id, &p);
18,464✔
638
    while(B.get(dzr)) {
87,458✔
639
      if(dzr.dr.d_type == QType::A || dzr.dr.d_type == QType::AAAA) {
68,994✔
640
        dzr.dr.d_place=DNSResourceRecord::ADDITIONAL;
61,623✔
641
        r->addRecord(std::move(dzr));
61,623✔
642
      }
61,623✔
643
    }
68,994✔
644
  }
18,464✔
645
}
108,793✔
646

647
vector<ComboAddress> PacketHandler::getIPAddressFor(const DNSName &target, const uint16_t qtype) {
×
648
  vector<ComboAddress> ret;
×
649
  if (qtype != QType::A && qtype != QType::AAAA) {
×
650
    return ret;
×
651
  }
×
652
  B.lookup(qtype, target, d_sd.domain_id);
×
653
  DNSZoneRecord rr;
×
654
  while (B.get(rr)) {
×
655
    if (qtype == QType::AAAA) {
×
656
      auto aaaarrc = getRR<AAAARecordContent>(rr.dr);
×
657
      ret.push_back(aaaarrc->getCA());
×
658
    } else if (qtype == QType::A) {
×
659
      auto arrc = getRR<ARecordContent>(rr.dr);
×
660
      ret.push_back(arrc->getCA());
×
661
    }
×
662
  }
×
663
  return ret;
×
664
}
×
665

666
void PacketHandler::emitNSEC(std::unique_ptr<DNSPacket>& r, const DNSName& name, const DNSName& next, int mode)
667
{
19,925✔
668
  NSECRecordContent nrc;
19,925✔
669
  nrc.d_next = next;
19,925✔
670

671
  nrc.set(QType::NSEC);
19,925✔
672
  nrc.set(QType::RRSIG);
19,925✔
673
  if(d_sd.qname() == name) {
19,925✔
674
    nrc.set(QType::SOA); // 1dfd8ad SOA can live outside the records table
7,525✔
675
    if(!d_dk.isPresigned(d_sd.zonename)) {
7,525✔
676
      auto keyset = d_dk.getKeys(d_sd.zonename);
6,554✔
677
      for(const auto& value: keyset) {
6,554✔
678
        if (value.second.published) {
6,554✔
679
          nrc.set(QType::DNSKEY);
6,506✔
680
          string publishCDNSKEY;
6,506✔
681
          d_dk.getPublishCDNSKEY(d_sd.zonename, publishCDNSKEY);
6,506✔
682
          if (! publishCDNSKEY.empty())
6,506✔
683
            nrc.set(QType::CDNSKEY);
564✔
684
          string publishCDS;
6,506✔
685
          d_dk.getPublishCDS(d_sd.zonename, publishCDS);
6,506✔
686
          if (! publishCDS.empty())
6,506✔
687
            nrc.set(QType::CDS);
564✔
688
          break;
6,506✔
689
        }
6,506✔
690
      }
6,554✔
691
    }
6,554✔
692
  }
7,525✔
693

694
  DNSZoneRecord rr;
19,925✔
695
#ifdef HAVE_LUA_RECORDS
19,925✔
696
  bool first{true};
19,925✔
697
  bool doLua{false};
19,925✔
698
#endif
19,925✔
699

700
  B.lookup(QType(QType::ANY), name, d_sd.domain_id);
19,925✔
701
  while(B.get(rr)) {
80,048✔
702
#ifdef HAVE_LUA_RECORDS
60,123✔
703
    if (rr.dr.d_type == QType::LUA && first && !d_dk.isPresigned(d_sd.zonename)) {
60,123!
704
      first = false;
×
705
      doLua = g_doLuaRecord;
×
706
      if (!doLua) {
×
707
        string val;
×
708
        d_dk.getFromMeta(d_sd.zonename, "ENABLE-LUA-RECORDS", val);
×
709
        doLua = (val == "1");
×
710
      }
×
711
    }
×
712

713
    if (rr.dr.d_type == QType::LUA && doLua) {
60,123!
714
      nrc.set(getRR<LUARecordContent>(rr.dr)->d_type);
×
715
    }
×
716
    else
60,123✔
717
#endif
60,123✔
718
    if (d_doExpandALIAS && rr.dr.d_type == QType::ALIAS) {
60,123✔
719
      // Set the A and AAAA in the NSEC bitmap so aggressive NSEC
720
      // does not falsely deny the type for this name.
721
      // This does NOT add the ALIAS to the bitmap, as that record cannot
722
      // be requested.
723
      if (!d_dk.isPresigned(d_sd.zonename)) {
100!
724
        nrc.set(QType::A);
100✔
725
        nrc.set(QType::AAAA);
100✔
726
      }
100✔
727
    }
100✔
728
    else if((rr.dr.d_type == QType::DNSKEY || rr.dr.d_type == QType::CDS || rr.dr.d_type == QType::CDNSKEY) && !d_dk.isPresigned(d_sd.zonename) && !::arg().mustDo("direct-dnskey")) {
60,023!
729
      continue;
×
730
    }
×
731
    else if(rr.dr.d_type == QType::NS || rr.auth) {
60,023✔
732
      nrc.set(rr.dr.d_type);
59,488✔
733
    }
59,488✔
734
  }
60,123✔
735

736
  rr.dr.d_name = name;
19,925✔
737
  rr.dr.d_ttl = d_sd.getNegativeTTL();
19,925✔
738
  rr.dr.d_type = QType::NSEC;
19,925✔
739
  rr.dr.setContent(std::make_shared<NSECRecordContent>(std::move(nrc)));
19,925✔
740
  rr.dr.d_place = (mode == 5 ) ? DNSResourceRecord::ANSWER: DNSResourceRecord::AUTHORITY;
19,925✔
741
  rr.auth = true;
19,925✔
742

743
  r->addRecord(std::move(rr));
19,925✔
744
}
19,925✔
745

746
void PacketHandler::emitNSEC3(std::unique_ptr<DNSPacket>& r, const NSEC3PARAMRecordContent& ns3prc, const DNSName& name, const string& namehash, const string& nexthash, int mode)
747
{
43,669✔
748
  NSEC3RecordContent n3rc;
43,669✔
749
  n3rc.d_algorithm = ns3prc.d_algorithm;
43,669✔
750
  n3rc.d_flags = ns3prc.d_flags;
43,669✔
751
  n3rc.d_iterations = ns3prc.d_iterations;
43,669✔
752
  n3rc.d_salt = ns3prc.d_salt;
43,669✔
753
  n3rc.d_nexthash = nexthash;
43,669✔
754

755
  DNSZoneRecord rr;
43,669✔
756

757
  if(!name.empty()) {
43,669✔
758
    if (d_sd.qname() == name) {
38,794✔
759
      n3rc.set(QType::SOA); // 1dfd8ad SOA can live outside the records table
12,715✔
760
      n3rc.set(QType::NSEC3PARAM);
12,715✔
761
      if(!d_dk.isPresigned(d_sd.zonename)) {
12,715✔
762
        auto keyset = d_dk.getKeys(d_sd.zonename);
10,476✔
763
        for(const auto& value: keyset) {
10,476✔
764
          if (value.second.published) {
10,476✔
765
            n3rc.set(QType::DNSKEY);
10,380✔
766
            string publishCDNSKEY;
10,380✔
767
            d_dk.getPublishCDNSKEY(d_sd.zonename, publishCDNSKEY);
10,380✔
768
            if (! publishCDNSKEY.empty())
10,380✔
769
              n3rc.set(QType::CDNSKEY);
576✔
770
            string publishCDS;
10,380✔
771
            d_dk.getPublishCDS(d_sd.zonename, publishCDS);
10,380✔
772
            if (! publishCDS.empty())
10,380✔
773
              n3rc.set(QType::CDS);
576✔
774
            break;
10,380✔
775
          }
10,380✔
776
        }
10,476✔
777
      }
10,476✔
778
    }
12,715✔
779

780
#ifdef HAVE_LUA_RECORDS
38,794✔
781
    bool first{true};
38,794✔
782
    bool doLua{false};
38,794✔
783
#endif
38,794✔
784

785
    B.lookup(QType(QType::ANY), name, d_sd.domain_id);
38,794✔
786
    while(B.get(rr)) {
145,149✔
787
#ifdef HAVE_LUA_RECORDS
106,355✔
788
      if (rr.dr.d_type == QType::LUA && first && !d_dk.isPresigned(d_sd.zonename)) {
106,355!
789
        first = false;
×
790
        doLua = g_doLuaRecord;
×
791
        if (!doLua) {
×
792
          string val;
×
793
          d_dk.getFromMeta(d_sd.zonename, "ENABLE-LUA-RECORDS", val);
×
794
          doLua = (val == "1");
×
795
        }
×
796
      }
×
797

798
      if (rr.dr.d_type == QType::LUA && doLua) {
106,355!
799
        n3rc.set(getRR<LUARecordContent>(rr.dr)->d_type);
×
800
      }
×
801
      else
106,355✔
802
#endif
106,355✔
803
      if (d_doExpandALIAS && rr.dr.d_type == QType::ALIAS) {
106,355✔
804
        // Set the A and AAAA in the NSEC3 bitmap so aggressive NSEC
805
        // does not falsely deny the type for this name.
806
        // This does NOT add the ALIAS to the bitmap, as that record cannot
807
        // be requested.
808
        if (!d_dk.isPresigned(d_sd.zonename)) {
105!
809
          n3rc.set(QType::A);
105✔
810
          n3rc.set(QType::AAAA);
105✔
811
        }
105✔
812
      }
105✔
813
      else if((rr.dr.d_type == QType::DNSKEY || rr.dr.d_type == QType::CDS || rr.dr.d_type == QType::CDNSKEY) && !d_dk.isPresigned(d_sd.zonename) && !::arg().mustDo("direct-dnskey")) {
106,250!
814
        continue;
×
815
      }
×
816
      else if(rr.dr.d_type && (rr.dr.d_type == QType::NS || rr.auth)) {
106,250✔
817
          // skip empty non-terminals
818
          n3rc.set(rr.dr.d_type);
99,902✔
819
      }
99,902✔
820
    }
106,355✔
821
  }
38,794✔
822

823
  const auto numberOfTypesSet = n3rc.numberOfTypesSet();
43,669✔
824
  if (numberOfTypesSet != 0 && !(numberOfTypesSet == 1 && n3rc.isSet(QType::NS))) {
43,669✔
825
    n3rc.set(QType::RRSIG);
31,180✔
826
  }
31,180✔
827

828
  rr.dr.d_name = DNSName(toBase32Hex(namehash))+d_sd.qname();
43,669✔
829
  rr.dr.d_ttl = d_sd.getNegativeTTL();
43,669✔
830
  rr.dr.d_type=QType::NSEC3;
43,669✔
831
  rr.dr.setContent(std::make_shared<NSEC3RecordContent>(std::move(n3rc)));
43,669✔
832
  rr.dr.d_place = (mode == 5 ) ? DNSResourceRecord::ANSWER: DNSResourceRecord::AUTHORITY;
43,669!
833
  rr.auth = true;
43,669✔
834

835
  r->addRecord(std::move(rr));
43,669✔
836
}
43,669✔
837

838
/*
839
   mode 0 = No Data Responses, QTYPE is not DS
840
   mode 1 = No Data Responses, QTYPE is DS
841
   mode 2 = Wildcard No Data Responses
842
   mode 3 = Wildcard Answer Responses
843
   mode 4 = Name Error Responses
844
   mode 5 = Direct NSEC request
845
*/
846
void PacketHandler::addNSECX(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const DNSName& target, const DNSName& wildcard, int mode)
847
{
43,318✔
848
  NSEC3PARAMRecordContent ns3rc;
43,318✔
849
  bool narrow = false;
43,318✔
850
  if(d_dk.getNSEC3PARAM(d_sd.zonename, &ns3rc, &narrow))  {
43,318✔
851
    if (mode != 5) // no direct NSEC3 queries, rfc5155 7.2.8
28,146✔
852
      addNSEC3(p, r, target, wildcard, ns3rc, narrow, mode);
26,336✔
853
  }
28,146✔
854
  else {
15,172✔
855
    addNSEC(p, r, target, wildcard, mode);
15,172✔
856
  }
15,172✔
857
}
43,318✔
858

859
bool PacketHandler::getNSEC3Hashes(bool narrow, const std::string& hashed, bool decrement, DNSName& unhashed, std::string& before, std::string& after, int mode)
860
{
44,658✔
861
  bool ret;
44,658✔
862
  if(narrow) { // nsec3-narrow
44,658✔
863
    ret=true;
11,153✔
864
    before=hashed;
11,153✔
865
    if(decrement) {
11,153✔
866
      decrementHash(before);
4,871✔
867
      unhashed.clear();
4,871✔
868
    }
4,871✔
869
    after=hashed;
11,153✔
870
    incrementHash(after);
11,153✔
871
  }
11,153✔
872
  else {
33,505✔
873
    DNSName hashedName = DNSName(toBase32Hex(hashed));
33,505✔
874
    DNSName beforeName, afterName;
33,505✔
875
    if (!decrement && mode >= 2)
33,505✔
876
      beforeName = hashedName;
6,012✔
877
    ret=d_sd.db->getBeforeAndAfterNamesAbsolute(d_sd.domain_id, hashedName, unhashed, beforeName, afterName);
33,505✔
878
    before=fromBase32Hex(beforeName.toString());
33,505✔
879
    after=fromBase32Hex(afterName.toString());
33,505✔
880
  }
33,505✔
881
  return ret;
44,658✔
882
}
44,658✔
883

884
void PacketHandler::addNSEC3(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const DNSName& target, const DNSName& wildcard, const NSEC3PARAMRecordContent& ns3rc, bool narrow, int mode)
885
{
26,334✔
886
  DLOG(g_log<<"addNSEC3() mode="<<mode<<" auth="<<d_sd.qname()<<" target="<<target<<" wildcard="<<wildcard<<endl);
26,334✔
887

888
  if (d_sd.db == nullptr) {
26,334✔
889
    if(!B.getSOAUncached(d_sd.zonename, d_sd)) {
26!
890
      DLOG(g_log<<"Could not get SOA for domain"<<endl);
×
891
      return;
×
892
    }
×
893
  }
26✔
894

895
  if (!d_sd.db->doesDNSSEC() && !narrow) {
26,334!
896
    // We are in a configuration where the zone is primarily served by a
897
    // non-DNSSEC-capable backend, but DNSSEC keys have been added to the
898
    // zone in a second, DNSSEC-capable backend, which caused d_dnssec to
899
    // be set to true. While it would be nice to support such a zone
900
    // configuration, we don't. Log a warning and skip DNSSEC processing.
901
    g_log << Logger::Notice << "Backend for zone '" << d_sd.qname() << "' does not support DNSSEC operation, not adding NSEC3 hashes" << endl;
×
902
    return;
×
903
  }
×
904

905
  bool doNextcloser = false;
26,334✔
906
  string before, after, hashed;
26,334✔
907
  DNSName unhashed, closest;
26,334✔
908

909
  if (mode == 2 || mode == 3 || mode == 4) {
26,334✔
910
    closest=wildcard;
11,626✔
911
    closest.chopOff();
11,626✔
912
  } else
11,626✔
913
    closest=target;
14,708✔
914

915
  // add matching NSEC3 RR
916
  if (mode != 3) {
26,334✔
917
    unhashed=(mode == 0 || mode == 1 || mode == 5) ? target : closest;
22,882!
918
    hashed=hashQNameWithSalt(ns3rc, unhashed);
22,882✔
919
    DLOG(g_log<<"1 hash: "<<toBase32Hex(hashed)<<" "<<unhashed<<endl);
22,882✔
920

921
    getNSEC3Hashes(narrow, hashed, false, unhashed, before, after, mode);
22,882✔
922

923
    if (((mode == 0 && ns3rc.d_flags) ||  mode == 1) && (hashed != before)) {
22,882✔
924
      DLOG(g_log<<"No matching NSEC3, do closest (provable) encloser"<<endl);
989✔
925

926
      bool doBreak = false;
989✔
927
      DNSZoneRecord rr;
989✔
928
      while( closest.chopOff() && (closest != d_sd.qname()))  { // stop at SOA
1,068!
929
        B.lookup(QType(QType::ANY), closest, d_sd.domain_id, &p);
259✔
930
        while(B.get(rr))
518✔
931
          if (rr.auth)
259✔
932
            doBreak = true;
180✔
933
        if(doBreak)
259✔
934
          break;
180✔
935
      }
259✔
936
      doNextcloser = true;
989✔
937
      unhashed=closest;
989✔
938
      hashed=hashQNameWithSalt(ns3rc, unhashed);
989✔
939
      DLOG(g_log<<"1 hash: "<<toBase32Hex(hashed)<<" "<<unhashed<<endl);
989✔
940

941
      getNSEC3Hashes(narrow, hashed, false, unhashed, before, after);
989✔
942
    }
989✔
943

944
    if (!after.empty()) {
22,882!
945
      DLOG(g_log<<"Done calling for matching, hashed: '"<<toBase32Hex(hashed)<<"' before='"<<toBase32Hex(before)<<"', after='"<<toBase32Hex(after)<<"'"<<endl);
22,882✔
946
      emitNSEC3(r, ns3rc, unhashed, before, after, mode);
22,882✔
947
    }
22,882✔
948
  }
22,882✔
949

950
  // add covering NSEC3 RR
951
  if ((mode >= 2 && mode <= 4) || doNextcloser) {
26,334!
952
    DNSName next(target);
12,615✔
953
    do {
18,002✔
954
      unhashed=next;
18,002✔
955
    }
18,002✔
956
    while( next.chopOff() && !(next==closest));
18,002!
957

958
    hashed=hashQNameWithSalt(ns3rc, unhashed);
12,615✔
959
    DLOG(g_log<<"2 hash: "<<toBase32Hex(hashed)<<" "<<unhashed<<endl);
12,615✔
960

961
    getNSEC3Hashes(narrow, hashed, true, unhashed, before, after);
12,615✔
962
    DLOG(g_log<<"Done calling for covering, hashed: '"<<toBase32Hex(hashed)<<"' before='"<<toBase32Hex(before)<<"', after='"<<toBase32Hex(after)<<"'"<<endl);
12,615✔
963
    emitNSEC3( r, ns3rc, unhashed, before, after, mode);
12,615✔
964
  }
12,615✔
965

966
  // wildcard denial
967
  if (mode == 2 || mode == 4) {
26,334✔
968
    unhashed=g_wildcarddnsname+closest;
8,172✔
969

970
    hashed=hashQNameWithSalt(ns3rc, unhashed);
8,172✔
971
    DLOG(g_log<<"3 hash: "<<toBase32Hex(hashed)<<" "<<unhashed<<endl);
8,172✔
972

973
    getNSEC3Hashes(narrow, hashed, (mode != 2), unhashed, before, after);
8,172✔
974
    DLOG(g_log<<"Done calling for '*', hashed: '"<<toBase32Hex(hashed)<<"' before='"<<toBase32Hex(before)<<"', after='"<<toBase32Hex(after)<<"'"<<endl);
8,172✔
975
    emitNSEC3( r, ns3rc, unhashed, before, after, mode);
8,172✔
976
  }
8,172✔
977
}
26,334✔
978

979
void PacketHandler::addNSEC(DNSPacket& /* p */, std::unique_ptr<DNSPacket>& r, const DNSName& target, const DNSName& wildcard, int mode)
980
{
15,423✔
981
  DLOG(g_log<<"addNSEC() mode="<<mode<<" auth="<<d_sd.qname()<<" target="<<target<<" wildcard="<<wildcard<<endl);
15,423✔
982

983
  if (d_sd.db == nullptr) {
15,423✔
984
    if(!B.getSOAUncached(d_sd.zonename, d_sd)) {
20!
985
      DLOG(g_log<<"Could not get SOA for domain"<<endl);
×
986
      return;
×
987
    }
×
988
  }
20✔
989

990
  if (!d_sd.db->doesDNSSEC()) {
15,423!
991
    // We are in a configuration where the zone is primarily served by a
992
    // non-DNSSEC-capable backend, but DNSSEC keys have been added to the
993
    // zone in a second, DNSSEC-capable backend, which caused d_dnssec to
994
    // be set to true. While it would be nice to support such a zone
995
    // configuration, we don't. Log a warning and skip DNSSEC processing.
996
    g_log << Logger::Notice << "Backend for zone '" << d_sd.qname() << "' does not support DNSSEC operation, not adding NSEC records" << endl;
×
997
    return;
×
998
  }
×
999

1000
  DNSName before,after;
15,423✔
1001
  d_sd.db->getBeforeAndAfterNames(d_sd.domain_id, d_sd.zonename, target, before, after);
15,423✔
1002
  if (mode != 5 || before == target)
15,423✔
1003
    emitNSEC(r, before, after, mode);
15,302✔
1004

1005
  if (mode == 2 || mode == 4) {
15,423✔
1006
    // wildcard NO-DATA or wildcard denial
1007
    before.clear();
4,623✔
1008
    DNSName closest(wildcard);
4,623✔
1009
    if (mode == 4) {
4,623✔
1010
      closest.chopOff();
4,103✔
1011
      closest.prependRawLabel("*");
4,103✔
1012
    }
4,103✔
1013
    d_sd.db->getBeforeAndAfterNames(d_sd.domain_id, d_sd.zonename, closest, before, after);
4,623✔
1014
    emitNSEC(r, before, after, mode);
4,623✔
1015
  }
4,623✔
1016
  return;
15,423✔
1017
}
15,423✔
1018

1019
/* Semantics:
1020

1021
- only one backend owns the SOA of a zone
1022
- only one AXFR per zone at a time - double startTransaction should fail
1023
- backends implement transaction semantics
1024

1025
How BindBackend implements this:
1026
   startTransaction makes a file
1027
   feedRecord sends everything to that file
1028
   commitTransaction moves that file atomically over the regular file, and triggers a reload
1029
   abortTransaction removes the file
1030

1031
How SQL backends implement this:
1032
   startTransaction starts a sql transaction, which also deletes all records if requested
1033
   feedRecord is an insert statement
1034
   commitTransaction commits the transaction
1035
   abortTransaction aborts it
1036

1037
*/
1038

1039
int PacketHandler::tryAutoPrimary(const DNSPacket& p, const DNSName& tsigkeyname)
1040
{
4✔
1041
  if(p.d_tcp)
4!
1042
  {
×
1043
    // do it right now if the client is TCP
1044
    // rarely happens
1045
    return tryAutoPrimarySynchronous(p, tsigkeyname);
×
1046
  }
×
1047
  else
4✔
1048
  {
4✔
1049
    // queue it if the client is on UDP
1050
    Communicator.addTryAutoPrimaryRequest(p);
4✔
1051
    return 0;
4✔
1052
  }
4✔
1053
}
4✔
1054

1055
int PacketHandler::tryAutoPrimarySynchronous(const DNSPacket& p, const DNSName& tsigkeyname)
1056
{
4✔
1057
  ComboAddress remote = p.getInnerRemote();
4✔
1058
  if(p.hasEDNSSubnet() && pdns::isAddressTrustedNotificationProxy(remote)) {
4!
1059
    remote = p.getRealRemote().getNetwork();
×
1060
  }
×
1061
  else {
4✔
1062
    remote = p.getInnerRemote();
4✔
1063
  }
4✔
1064
  remote.setPort(53);
4✔
1065

1066
  Resolver::res_t nsset;
4✔
1067
  try {
4✔
1068
    Resolver resolver;
4✔
1069
    uint32_t theirserial;
4✔
1070
    resolver.getSoaSerial(remote, p.qdomain, &theirserial);
4✔
1071
    resolver.resolve(remote, p.qdomain, QType::NS, &nsset);
4✔
1072
  }
4✔
1073
  catch(ResolverException &re) {
4✔
1074
    g_log<<Logger::Error<<"Error resolving SOA or NS for "<<p.qdomain<<" at: "<< remote <<": "<<re.reason<<endl;
×
1075
    return RCode::ServFail;
×
1076
  }
×
1077

1078
  // check if the returned records are NS records
1079
  bool haveNS=false;
4✔
1080
  for(const auto& ns: nsset) {
8✔
1081
    if(ns.qtype==QType::NS)
8!
1082
      haveNS=true;
8✔
1083
  }
8✔
1084

1085
  if(!haveNS) {
4!
1086
    g_log << Logger::Error << "While checking for autoprimary, did not find NS for " << p.qdomain << " at: " << remote << endl;
×
1087
    return RCode::ServFail;
×
1088
  }
×
1089

1090
  string nameserver, account;
4✔
1091
  DNSBackend *db;
4✔
1092

1093
  if (!::arg().mustDo("allow-unsigned-autoprimary") && tsigkeyname.empty()) {
4!
1094
    g_log << Logger::Error << "Received unsigned NOTIFY for " << p.qdomain << " from potential autoprimary " << remote << ". Refusing." << endl;
×
1095
    return RCode::Refused;
×
1096
  }
×
1097

1098
  ZoneName zonename(p.qdomain);
4✔
1099
  if (!B.autoPrimaryBackend(remote.toString(), zonename, nsset, &nameserver, &account, &db)) {
4✔
1100
    g_log << Logger::Error << "Unable to find backend willing to host " << p.qdomain << " for potential autoprimary " << remote << ". Remote nameservers: " << endl;
2✔
1101
    for(const auto& rr: nsset) {
4✔
1102
      if(rr.qtype==QType::NS)
4!
1103
        g_log<<Logger::Error<<rr.content<<endl;
4✔
1104
    }
4✔
1105
    return RCode::Refused;
2✔
1106
  }
2✔
1107
  try {
2✔
1108
    db->createSecondaryDomain(remote.toString(), zonename, nameserver, account);
2✔
1109
    DomainInfo di;
2✔
1110
    if (!db->getDomainInfo(zonename, di, false)) {
2!
1111
      g_log << Logger::Error << "Failed to create " << zonename << " for potential autoprimary " << remote << endl;
×
1112
      return RCode::ServFail;
×
1113
    }
×
1114
    g_zoneCache.add(zonename, di.id);
2✔
1115
    if (tsigkeyname.empty() == false) {
2✔
1116
      vector<string> meta;
1✔
1117
      meta.push_back(tsigkeyname.toStringNoDot());
1✔
1118
      db->setDomainMetadata(zonename, "AXFR-MASTER-TSIG", meta);
1✔
1119
    }
1✔
1120
  }
2✔
1121
  catch(PDNSException& ae) {
2✔
1122
    g_log << Logger::Error << "Database error trying to create " << zonename << " for potential autoprimary " << remote << ": " << ae.reason << endl;
×
1123
    return RCode::ServFail;
×
1124
  }
×
1125
  g_log << Logger::Warning << "Created new secondary zone '" << zonename << "' from autoprimary " << remote << endl;
2✔
1126
  return RCode::NoError;
2✔
1127
}
2✔
1128

1129
int PacketHandler::processNotify(const DNSPacket& p)
1130
{
7✔
1131
  ZoneName zonename(p.qdomain);
7✔
1132
  /* now what?
1133
     was this notification from an approved address?
1134
     was this notification approved by TSIG?
1135
     We determine our internal SOA id (via UeberBackend)
1136
     We determine the SOA at our (known) primary
1137
     if primary is higher -> do stuff
1138
  */
1139

1140
  g_log<<Logger::Debug<<"Received NOTIFY for "<<zonename<<" from "<<p.getRemoteString()<<endl;
7✔
1141

1142
  if(!::arg().mustDo("secondary") && s_forwardNotify.empty()) {
7!
1143
    g_log << Logger::Warning << "Received NOTIFY for " << zonename << " from " << p.getRemoteString() << " but secondary support is disabled in the configuration" << endl;
×
1144
    return RCode::Refused;
×
1145
  }
×
1146

1147
  // Sender verification
1148
  //
1149
  if(!s_allowNotifyFrom.match(p.getInnerRemote()) || p.d_havetsig) {
7!
1150
    if (p.d_havetsig && p.getTSIGKeyname().empty() == false) {
3!
1151
        g_log<<Logger::Notice<<"Received secure NOTIFY for "<<zonename<<" from "<<p.getRemoteString()<<", with TSIG key '"<<p.getTSIGKeyname()<<"'"<<endl;
3✔
1152
    } else {
3✔
UNCOV
1153
      g_log<<Logger::Warning<<"Received NOTIFY for "<<zonename<<" from "<<p.getRemoteString()<<" but the remote is not providing a TSIG key or in allow-notify-from (Refused)"<<endl;
×
UNCOV
1154
      return RCode::Refused;
×
UNCOV
1155
    }
×
1156
  }
3✔
1157

1158
  if ((!::arg().mustDo("allow-unsigned-notify") && !p.d_havetsig) || p.d_havetsig) {
7✔
1159
    if (!p.d_havetsig) {
4✔
1160
      g_log<<Logger::Warning<<"Received unsigned NOTIFY for "<<zonename<<" from "<<p.getRemoteString()<<" while a TSIG key was required (Refused)"<<endl;
1✔
1161
      return RCode::Refused;
1✔
1162
    }
1✔
1163
    vector<string> meta;
3✔
1164
    if (B.getDomainMetadata(zonename,"AXFR-MASTER-TSIG",meta) && !meta.empty()) {
3!
1165
      DNSName expected{meta[0]};
1✔
1166
      if (p.getTSIGKeyname() != expected) {
1!
1167
        g_log<<Logger::Warning<<"Received secure NOTIFY for "<<zonename<<" from "<<p.getRemoteString()<<": expected TSIG key '"<<expected<<"', got '"<<p.getTSIGKeyname()<<"' (Refused)"<<endl;
×
1168
        return RCode::Refused;
×
1169
      }
×
1170
    }
1✔
1171
  }
3✔
1172

1173
  // Domain verification
1174
  //
1175
  DomainInfo di;
6✔
1176
  if(!B.getDomainInfo(zonename, di, false) || di.backend == nullptr) {
6!
1177
    if(::arg().mustDo("autosecondary")) {
4!
1178
      g_log << Logger::Warning << "Received NOTIFY for " << zonename << " from " << p.getRemoteString() << " for which we are not authoritative, trying autoprimary" << endl;
4✔
1179
      return tryAutoPrimary(p, p.getTSIGKeyname());
4✔
1180
    }
4✔
1181
    g_log<<Logger::Notice<<"Received NOTIFY for "<<zonename<<" from "<<p.getRemoteString()<<" for which we are not authoritative (Refused)"<<endl;
×
1182
    return RCode::Refused;
×
1183
  }
4✔
1184

1185
  if(pdns::isAddressTrustedNotificationProxy(p.getInnerRemote())) {
2!
1186
    if (di.primaries.empty()) {
×
1187
      g_log << Logger::Warning << "Received NOTIFY for " << zonename << " from trusted-notification-proxy " << p.getRemoteString() << ", zone does not have any primaries defined (Refused)" << endl;
×
1188
      return RCode::Refused;
×
1189
    }
×
1190
    g_log<<Logger::Notice<<"Received NOTIFY for "<<zonename<<" from trusted-notification-proxy "<<p.getRemoteString()<<endl;
×
1191
  }
×
1192
  else if (::arg().mustDo("primary") && di.isPrimaryType()) {
2!
1193
    g_log << Logger::Warning << "Received NOTIFY for " << zonename << " from " << p.getRemoteString() << " but we are primary (Refused)" << endl;
×
1194
    return RCode::Refused;
×
1195
  }
×
1196
  else if (!di.isPrimary(p.getInnerRemote())) {
2!
1197
    g_log << Logger::Warning << "Received NOTIFY for " << zonename << " from " << p.getRemoteString() << " which is not a primary (Refused)" << endl;
×
1198
    return RCode::Refused;
×
1199
  }
×
1200

1201
  if(!s_forwardNotify.empty()) {
2!
1202
    set<string> forwardNotify(s_forwardNotify);
×
1203
    for(const auto & j : forwardNotify) {
×
1204
      g_log<<Logger::Notice<<"Relaying notification of domain "<<zonename<<" from "<<p.getRemoteString()<<" to "<<j<<endl;
×
1205
      Communicator.notify(zonename,j);
×
1206
    }
×
1207
  }
×
1208

1209
  if(::arg().mustDo("secondary")) {
2!
1210
    g_log<<Logger::Notice<<"Received NOTIFY for "<<zonename<<" from "<<p.getRemoteString()<<" - queueing check"<<endl;
2✔
1211
    di.receivedNotify = true;
2✔
1212
    Communicator.addSecondaryCheckRequest(di, p.getInnerRemote());
2✔
1213
  }
2✔
1214
  return 0;
2✔
1215
}
2✔
1216

1217
static bool validDNSName(const DNSName& name)
1218
{
112,818✔
1219
  if (!g_8bitDNS) {
112,821✔
1220
    return name.has8bitBytes() == false;
112,819✔
1221
  }
112,819✔
1222
  return true;
4,294,967,296✔
1223
}
112,818✔
1224

1225
std::unique_ptr<DNSPacket> PacketHandler::question(DNSPacket& p)
1226
{
96,661✔
1227
  std::unique_ptr<DNSPacket> ret{nullptr};
96,661✔
1228

1229
  if(d_pdl)
96,661✔
1230
  {
77,808✔
1231
    ret=d_pdl->prequery(p);
77,808✔
1232
    if(ret)
77,808!
1233
      return ret;
×
1234
  }
77,808✔
1235

1236
  if(p.d.rd) {
96,661!
1237
    static AtomicCounter &rdqueries=*S.getPointer("rd-queries");
×
1238
    rdqueries++;
×
1239
  }
×
1240

1241
  return doQuestion(p);
96,661✔
1242
}
96,661✔
1243

1244

1245
void PacketHandler::makeNXDomain(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const DNSName& target, const DNSName& wildcard)
1246
{
13,989✔
1247
  DNSZoneRecord rr;
13,989✔
1248
  rr=makeEditedDNSZRFromSOAData(d_dk, d_sd, DNSResourceRecord::AUTHORITY);
13,989✔
1249
  rr.dr.d_ttl=d_sd.getNegativeTTL();
13,989✔
1250
  r->addRecord(std::move(rr));
13,989✔
1251

1252
  if(d_dnssec) {
13,989✔
1253
    addNSECX(p, r, target, wildcard, 4);
11,191✔
1254
  }
11,191✔
1255

1256
  r->setRcode(RCode::NXDomain);
13,989✔
1257
}
13,989✔
1258

1259
void PacketHandler::makeNOError(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const DNSName& target, const DNSName& wildcard, int mode)
1260
{
25,425✔
1261
  DNSZoneRecord rr;
25,425✔
1262
  rr=makeEditedDNSZRFromSOAData(d_dk, d_sd, DNSResourceRecord::AUTHORITY);
25,425✔
1263
  rr.dr.d_ttl=d_sd.getNegativeTTL();
25,425✔
1264
  r->addRecord(std::move(rr));
25,425✔
1265

1266
  if(d_dnssec) {
25,425✔
1267
    addNSECX(p, r, target, wildcard, mode);
21,394✔
1268
  }
21,394✔
1269

1270
  S.inc("noerror-packets");
25,425✔
1271
  S.ringAccount("noerror-queries", p.qdomain, p.qtype);
25,425✔
1272
}
25,425✔
1273

1274

1275
bool PacketHandler::addDSforNS(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const DNSName& dsname)
1276
{
4,929✔
1277
  //cerr<<"Trying to find a DS for '"<<dsname<<"', domain_id = "<<d_sd.domain_id<<endl;
1278
  B.lookup(QType(QType::DS), dsname, d_sd.domain_id, &p);
4,929✔
1279
  DNSZoneRecord rr;
4,929✔
1280
  bool gotOne=false;
4,929✔
1281
  while(B.get(rr)) {
6,563✔
1282
    gotOne=true;
1,634✔
1283
    rr.dr.d_place = DNSResourceRecord::AUTHORITY;
1,634✔
1284
    r->addRecord(std::move(rr));
1,634✔
1285
  }
1,634✔
1286
  return gotOne;
4,929✔
1287
}
4,929✔
1288

1289
bool PacketHandler::tryReferral(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const DNSName &target, bool retargeted)
1290
{
83,664✔
1291
  vector<DNSZoneRecord> rrset = getBestReferralNS(p, target);
83,664✔
1292
  if(rrset.empty())
83,664✔
1293
    return false;
77,760✔
1294

1295
  DNSName name = rrset.begin()->dr.d_name;
5,904✔
1296
  for(auto& rr: rrset) {
8,638✔
1297
    rr.dr.d_place=DNSResourceRecord::AUTHORITY;
8,638✔
1298
    r->addRecord(std::move(rr));
8,638✔
1299
  }
8,638✔
1300
  if(!retargeted)
5,904✔
1301
    r->setA(false);
5,636✔
1302

1303
  if(d_dk.isSecuredZone(d_sd.zonename) && !addDSforNS(p, r, name) && d_dnssec) {
5,904✔
1304
    addNSECX(p, r, name, DNSName(), 1);
2,765✔
1305
  }
2,765✔
1306

1307
  return true;
5,904✔
1308
}
83,664✔
1309

1310
void PacketHandler::completeANYRecords(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const DNSName &target)
1311
{
2,859✔
1312
  addNSECX(p, r, target, DNSName(), 5);
2,859✔
1313
  if(d_sd.qname() == p.qdomain) {
2,859✔
1314
    if(!d_dk.isPresigned(d_sd.zonename)) {
1,457✔
1315
      addDNSKEY(p, r);
1,132✔
1316
      addCDNSKEY(p, r);
1,132✔
1317
      addCDS(p, r);
1,132✔
1318
    }
1,132✔
1319
    addNSEC3PARAM(p, r);
1,457✔
1320
  }
1,457✔
1321
}
2,859✔
1322

1323
bool PacketHandler::tryDNAME(DNSPacket& p, std::unique_ptr<DNSPacket>& r, DNSName &target)
1324
{
15,570✔
1325
  if(!d_doDNAME)
15,570✔
1326
    return false;
6✔
1327
  DLOG(g_log<<Logger::Warning<<"Let's try DNAME.."<<endl);
15,564✔
1328
  vector<DNSZoneRecord> rrset;
15,564✔
1329
  try {
15,564✔
1330
    getBestDNAMESynth(p, target, rrset);
15,564✔
1331
    if(!rrset.empty()) {
15,564✔
1332
      for (auto& record : rrset) {
1,902✔
1333
        record.dr.d_place = DNSResourceRecord::ANSWER;
1,902✔
1334
        r->addRecord(std::move(record));
1,902✔
1335
      }
1,902✔
1336
      return true;
951✔
1337
    }
951✔
1338
  } catch (const std::range_error &e) {
15,564✔
1339
    // Add the DNAME regardless, but throw to let the caller know we could not
1340
    // synthesize a CNAME
1341
    if(!rrset.empty()) {
380!
1342
      for (auto& record : rrset) {
380✔
1343
        record.dr.d_place = DNSResourceRecord::ANSWER;
380✔
1344
        r->addRecord(std::move(record));
380✔
1345
      }
380✔
1346
    }
380✔
1347
    throw e;
380✔
1348
  }
380✔
1349
  return false;
14,231✔
1350
}
15,564✔
1351
bool PacketHandler::tryWildcard(DNSPacket& p, std::unique_ptr<DNSPacket>& r, DNSName &target, DNSName &wildcard, bool& retargeted, bool& nodata)
1352
{
23,610✔
1353
  retargeted = nodata = false;
23,610✔
1354
  DNSName bestmatch;
23,610✔
1355

1356
  vector<DNSZoneRecord> rrset;
23,610✔
1357
  if(!getBestWildcard(p, target, wildcard, &rrset))
23,610✔
1358
    return false;
15,570✔
1359

1360
  if(rrset.empty()) {
8,040✔
1361
    DLOG(g_log<<"Wildcard matched something, but not of the correct type"<<endl);
1,887✔
1362
    nodata=true;
1,887✔
1363
  }
1,887✔
1364
  else {
6,153✔
1365
    bestmatch = target;
6,153✔
1366
    for(auto& rr: rrset) {
6,154✔
1367
      rr.wildcardname = rr.dr.d_name;
6,153✔
1368
      rr.dr.d_name = bestmatch;
6,153✔
1369

1370
      if(rr.dr.d_type == QType::CNAME)  {
6,153✔
1371
        retargeted=true;
2,579✔
1372
        target=getRR<CNAMERecordContent>(rr.dr)->getTarget();
2,579✔
1373
      }
2,579✔
1374

1375
      rr.dr.d_place=DNSResourceRecord::ANSWER;
6,153✔
1376
      r->addRecord(std::move(rr));
6,153✔
1377
    }
6,153✔
1378
  }
6,153✔
1379
  if(d_dnssec && !nodata) {
8,040✔
1380
    addNSECX(p, r, bestmatch, wildcard, 3);
5,109✔
1381
  }
5,109✔
1382

1383
  return true;
8,040✔
1384
}
23,610✔
1385

1386
//! Called by the Distributor to ask a question. Returns 0 in case of an error
1387
std::unique_ptr<DNSPacket> PacketHandler::doQuestion(DNSPacket& pkt)
1388
{
112,884✔
1389
  bool noCache=false;
112,884✔
1390

1391
  if(pkt.d.qr) { // QR bit from dns packet (thanks RA from N)
112,884!
1392
    if(d_logDNSDetails) {
×
1393
      g_log<<Logger::Error<<"Received an answer (non-query) packet from "<<pkt.getRemoteString()<<", dropping"<<endl;
×
1394
    }
×
1395
    S.inc("corrupt-packets");
×
1396
    S.ringAccount("remotes-corrupt", pkt.getInnerRemote());
×
1397
    return nullptr;
×
1398
  }
×
1399

1400
  if(pkt.d.tc) { // truncated query. MOADNSParser would silently parse this packet in an incomplete way.
112,884!
1401
    if(d_logDNSDetails) {
×
1402
      g_log<<Logger::Error<<"Received truncated query packet from "<<pkt.getRemoteString()<<", dropping"<<endl;
×
1403
    }
×
1404
    S.inc("corrupt-packets");
×
1405
    S.ringAccount("remotes-corrupt", pkt.getInnerRemote());
×
1406
    return nullptr;
×
1407
  }
×
1408

1409
  if (pkt.hasEDNS()) {
112,884✔
1410
    if(pkt.getEDNSVersion() > 0) {
104,017✔
1411
      auto resp = pkt.replyPacket();
1✔
1412
      // PacketWriter::addOpt will take care of setting this correctly in the packet
1413
      resp->setEDNSRcode(ERCode::BADVERS);
1✔
1414
      return resp;
1✔
1415
    }
1✔
1416
    if (pkt.hasEDNSCookie()) {
104,016!
1417
      if (!pkt.hasWellFormedEDNSCookie()) {
×
1418
        auto resp = pkt.replyPacket();
×
1419
        resp->setRcode(RCode::FormErr);
×
1420
        return resp;
×
1421
      }
×
1422
      if (!pkt.hasValidEDNSCookie() && !pkt.d_tcp) {
×
1423
        auto resp = pkt.replyPacket();
×
1424
        resp->setEDNSRcode(ERCode::BADCOOKIE);
×
1425
        return resp;
×
1426
      }
×
1427
    }
×
1428
  }
104,016✔
1429

1430
  if(pkt.d_havetsig) {
112,883✔
1431
    DNSName tsigkeyname;
15✔
1432
    string secret;
15✔
1433
    TSIGRecordContent trc;
15✔
1434
    if (!checkForCorrectTSIG(pkt, &tsigkeyname, &secret, &trc)) {
15!
1435
      auto resp=pkt.replyPacket();  // generate an empty reply packet
×
1436
      if(d_logDNSDetails) {
×
1437
        g_log<<Logger::Error<<"Received a TSIG signed message with a non-validating key"<<endl;
×
1438
      }
×
1439
      // RFC3007 describes that a non-secure message should be sending Refused for DNS Updates
1440
      if (pkt.d.opcode == Opcode::Update) {
×
1441
        resp->setRcode(RCode::Refused);
×
1442
      }
×
1443
      else {
×
1444
        resp->setRcode(RCode::NotAuth);
×
1445
      }
×
1446
      return resp;
×
1447
    }
×
1448
    getTSIGHashEnum(trc.d_algoName, pkt.d_tsig_algo);
15✔
1449
#ifdef ENABLE_GSS_TSIG
15✔
1450
    if (g_doGssTSIG && pkt.d_tsig_algo == TSIG_GSS) {
15!
1451
      GssContext gssctx(tsigkeyname);
×
1452
      if (!gssctx.getPeerPrincipal(pkt.d_peer_principal)) {
×
1453
        g_log<<Logger::Warning<<"Failed to extract peer principal from GSS context with keyname '"<<tsigkeyname<<"'"<<endl;
×
1454
      }
×
1455
    }
×
1456
#endif
15✔
1457
    pkt.setTSIGDetails(trc, tsigkeyname, secret, trc.d_mac); // this will get copied by replyPacket()
15✔
1458
    noCache=true;
15✔
1459
  }
15✔
1460

1461
  if (pkt.qtype == QType::TKEY) {
112,883✔
1462
    auto resp=pkt.replyPacket();  // generate an empty reply packet, possibly with TSIG details inside
72✔
1463
    this->tkeyHandler(pkt, resp);
72✔
1464
    return resp;
72✔
1465
  }
72✔
1466

1467
  try {
112,811✔
1468

1469
    // XXX FIXME do this in DNSPacket::parse ?
1470

1471
    if(!validDNSName(pkt.qdomain)) {
112,811!
1472
      if(d_logDNSDetails) {
×
1473
        g_log<<Logger::Error<<"Received a malformed qdomain from "<<pkt.getRemoteString()<<", '"<<pkt.qdomain<<"': sending servfail"<<endl;
×
1474
      }
×
1475
      S.inc("corrupt-packets");
×
1476
      S.ringAccount("remotes-corrupt", pkt.getInnerRemote());
×
1477
      S.inc("servfail-packets");
×
1478
      auto resp=pkt.replyPacket();  // generate an empty reply packet
×
1479
      resp->setRcode(RCode::ServFail);
×
1480
      return resp;
×
1481
    }
×
1482

1483
    using opcodeHandler = std::unique_ptr<DNSPacket> (PacketHandler::*)(DNSPacket&, bool);
112,811✔
1484
    const static std::array<opcodeHandler, 16> opcodeHandlers = {
112,811✔
1485
      &PacketHandler::opcodeQuery,
112,811✔
1486
      &PacketHandler::opcodeNotImplemented,
112,811✔
1487
      &PacketHandler::opcodeNotImplemented,
112,811✔
1488
      &PacketHandler::opcodeNotImplemented,
112,811✔
1489
      &PacketHandler::opcodeNotify,
112,811✔
1490
      &PacketHandler::opcodeUpdate,
112,811✔
1491
      &PacketHandler::opcodeNotImplemented,
112,811✔
1492
      &PacketHandler::opcodeNotImplemented,
112,811✔
1493

1494
      &PacketHandler::opcodeNotImplemented,
112,811✔
1495
      &PacketHandler::opcodeNotImplemented,
112,811✔
1496
      &PacketHandler::opcodeNotImplemented,
112,811✔
1497
      &PacketHandler::opcodeNotImplemented,
112,811✔
1498
      &PacketHandler::opcodeNotImplemented,
112,811✔
1499
      &PacketHandler::opcodeNotImplemented,
112,811✔
1500
      &PacketHandler::opcodeNotImplemented,
112,811✔
1501
      &PacketHandler::opcodeNotImplemented
112,811✔
1502
    };
112,811✔
1503

1504
    return (this->*(opcodeHandlers.at(pkt.d.opcode)))(pkt, noCache);
112,811✔
1505
  }
112,811✔
1506
  catch(const DBException &e) {
112,811✔
1507
    g_log<<Logger::Error<<"Backend reported condition which prevented lookup ("+e.reason+") sending out servfail"<<endl;
×
1508
    auto resp=pkt.replyPacket(); // generate an empty reply packet
×
1509
    resp->setRcode(RCode::ServFail);
×
1510
    S.inc("servfail-packets");
×
1511
    S.ringAccount("servfail-queries", pkt.qdomain, pkt.qtype);
×
1512
    return resp;
×
1513
  }
×
1514
  catch(const PDNSException &e) {
112,811✔
1515
    g_log<<Logger::Error<<"Backend reported permanent error which prevented lookup ("+e.reason+"), aborting"<<endl;
×
1516
    throw; // we WANT to die at this point
×
1517
  }
×
1518
  catch(const std::exception &e) {
112,811✔
1519
    g_log<<Logger::Error<<"Exception building answer packet for "<<pkt.qdomain<<"/"<<pkt.qtype.toString()<<" ("<<e.what()<<") sending out servfail"<<endl;
×
1520
    auto resp=pkt.replyPacket(); // generate an empty reply packet
×
1521
    resp->setRcode(RCode::ServFail);
×
1522
    S.inc("servfail-packets");
×
1523
    S.ringAccount("servfail-queries", pkt.qdomain, pkt.qtype);
×
1524
    return resp;
×
1525
  }
×
1526
}
112,811✔
1527

1528
bool PacketHandler::opcodeQueryInner(DNSPacket& pkt, queryState &state)
1529
{
111,548✔
1530
  state.r=pkt.replyPacket();  // generate an empty reply packet, possibly with TSIG details inside
111,548✔
1531

1532
  // g_log<<Logger::Warning<<"Query for '"<<pkt.qdomain<<"' "<<pkt.qtype.toString()<<" from "<<pkt.getRemoteString()<< " (tcp="<<pkt.d_tcp<<")"<<endl;
1533

1534
  if(pkt.qtype.getCode()==QType::IXFR) {
111,548!
1535
    state.r->setRcode(RCode::Refused);
×
1536
    return false;
×
1537
  }
×
1538

1539
  state.target=pkt.qdomain;
111,548✔
1540

1541
  // catch chaos qclass requests
1542
  if(pkt.qclass == QClass::CHAOS) {
111,548!
1543
    return doChaosRequest(pkt,state.r,state.target) != 0;
×
1544
  }
×
1545

1546
  // we only know about qclass IN (and ANY), send Refused for everything else.
1547
  if(pkt.qclass != QClass::IN && pkt.qclass!=QClass::ANY) {
111,548!
1548
    state.r->setRcode(RCode::Refused);
×
1549
    return false;
×
1550
  }
×
1551

1552
  // send TC for udp ANY query if any-to-tcp is enabled.
1553
  if(pkt.qtype.getCode() == QType::ANY && !pkt.d_tcp && g_anyToTcp) {
111,548✔
1554
    state.r->d.tc = 1;
2,118✔
1555
    state.r->commitD();
2,118✔
1556
    return false;
2,118✔
1557
  }
2,118✔
1558

1559
  // for qclass ANY the response should never be authoritative unless the response covers all classes.
1560
  if(pkt.qclass==QClass::ANY) {
109,430!
1561
    state.r->setA(false);
×
1562
  }
×
1563

1564
  int retargetcount{0};
109,430✔
1565
  while (true) {
121,555✔
1566
    state.retargeted = false;
121,553✔
1567
    bool result = opcodeQueryInner2(pkt, state, retargetcount != 0);
121,553✔
1568
    if (!state.retargeted) {
121,553✔
1569
      return result;
109,113✔
1570
    }
109,113✔
1571
    retargetcount++;
12,440✔
1572
    if (retargetcount > 10) {
12,440✔
1573
      g_log<<Logger::Warning<<"Abort CNAME chain resolution after "<<--retargetcount<<" redirects, sending out servfail. Initial query: '"<<pkt.qdomain<<"'"<<endl;
322✔
1574
      state.r=pkt.replyPacket();
322✔
1575
      state.r->setRcode(RCode::ServFail);
322✔
1576
      return false;
322✔
1577
    }
322✔
1578
  }
12,440✔
1579
}
109,430✔
1580

1581
// NOLINTNEXTLINE(readability-function-cognitive-complexity): TODO continue splitting this into smaller pieces
1582
bool PacketHandler::opcodeQueryInner2(DNSPacket& pkt, queryState &state, bool retargeted)
1583
{
121,558✔
1584
  DNSZoneRecord zrr;
121,558✔
1585
#ifdef HAVE_LUA_RECORDS
121,558✔
1586
  bool doLua=g_doLuaRecord;
121,558✔
1587
#endif
121,558✔
1588

1589
  if (retargeted && !d_doResolveAcrossZones && !state.target.isPartOf(state.r->qdomainzone)) {
121,558!
1590
    // We are following a retarget outside the initial zone (and do not need to check getAuth to know this). Config asked us not to do that.
1591
    // This is a performance optimization, the generic case is checked after getAuth below.
1592
    return true;
×
1593
  }
×
1594

1595
  if(!B.getAuth(ZoneName(state.target), pkt.qtype, &d_sd, pkt.getRealRemote(), true, &pkt)) {
121,558✔
1596
    DLOG(g_log<<Logger::Error<<"We have no authority over zone '"<<state.target<<"'"<<endl);
2,671✔
1597
    if (!retargeted) {
2,671✔
1598
      state.r->setA(false); // drop AA if we never had a SOA in the first place
715✔
1599
      state.r->setRcode(RCode::Refused); // send REFUSED - but only on empty 'no idea'
715✔
1600
    }
715✔
1601
    return true;
2,671✔
1602
  }
2,671✔
1603
  DLOG(g_log<<Logger::Error<<"We have authority, zone='"<<d_sd.qname()<<"', id="<<d_sd.domain_id<<", zonename="<<d_sd.zonename<<endl);
118,887✔
1604

1605
  if (!retargeted) {
118,887✔
1606
    state.r->qdomainzone = d_sd.zonename;
108,712✔
1607
  } else if (!d_doResolveAcrossZones && state.r->qdomainzone.operator const DNSName&() != d_sd.qname()) {
108,712!
1608
    // We are following a retarget outside the initial zone. Config asked us not to do that.
1609
    return true;
×
1610
  }
×
1611

1612
  state.authSet.insert(d_sd.zonename);
118,887✔
1613
  d_dnssec=(pkt.d_dnssecOk && d_dk.isSecuredZone(d_sd.zonename));
118,887✔
1614
  state.doSigs |= d_dnssec;
118,887✔
1615

1616
  if(d_sd.qname()==pkt.qdomain) {
118,887✔
1617
    if(!d_dk.isPresigned(d_sd.zonename)) {
44,931✔
1618
      switch (pkt.qtype.getCode()) {
40,129✔
1619
      case QType::DNSKEY:
22,344✔
1620
        if(addDNSKEY(pkt, state.r)) {
22,344✔
1621
          return true;
21,775✔
1622
        }
21,775✔
1623
        break;
569✔
1624
      case QType::CDNSKEY:
641✔
1625
        if(addCDNSKEY(pkt,state.r)) {
540!
1626
          return true;
540✔
1627
        }
540✔
1628
        break;
×
1629
      case QType::CDS:
545✔
1630
        if(addCDS(pkt,state.r)) {
545✔
1631
          return true;
544✔
1632
        }
544✔
1633
        break;
1✔
1634
      }
40,129✔
1635
    }
40,129✔
1636
    if(pkt.qtype.getCode() == QType::NSEC3PARAM) {
22,071✔
1637
      if(addNSEC3PARAM(pkt,state.r)) {
941✔
1638
        return true;
480✔
1639
      }
480✔
1640
    }
941✔
1641
  }
22,071✔
1642

1643
  if(pkt.qtype.getCode() == QType::SOA && d_sd.qname()==pkt.qdomain) {
95,547✔
1644
    zrr=makeEditedDNSZRFromSOAData(d_dk, d_sd);
1,067✔
1645
    state.r->addRecord(std::move(zrr));
1,067✔
1646
    return true;
1,067✔
1647
  }
1,067✔
1648

1649
  // this TRUMPS a cname!
1650
  if(d_dnssec && pkt.qtype.getCode() == QType::NSEC && !d_dk.getNSEC3PARAM(d_sd.zonename, nullptr)) {
94,480✔
1651
    addNSEC(pkt, state.r, state.target, DNSName(), 5);
251✔
1652
    if (!state.r->isEmpty()) {
251✔
1653
      return true;
170✔
1654
    }
170✔
1655
  }
251✔
1656

1657
  // this TRUMPS a cname!
1658
  if(pkt.qtype.getCode() == QType::RRSIG) {
94,310✔
1659
    g_log<<Logger::Info<<"Direct RRSIG query for "<<state.target<<" from "<<pkt.getRemoteString()<<endl;
592✔
1660
    state.r->setRcode(RCode::Refused);
592✔
1661
    return true;
592✔
1662
  }
592✔
1663

1664
  DLOG(g_log<<"Checking for referrals first, unless this is a DS query"<<endl);
93,718✔
1665
  if(pkt.qtype.getCode() != QType::DS && tryReferral(pkt, state.r, state.target, retargeted)) {
93,718✔
1666
    return true;
4,780✔
1667
  }
4,780✔
1668

1669
  DLOG(g_log<<"Got no referrals, trying ANY"<<endl);
88,938✔
1670

1671
#ifdef HAVE_LUA_RECORDS
88,938✔
1672
  if(!doLua) {
88,939✔
1673
    string val;
88,939✔
1674
    d_dk.getFromMeta(d_sd.zonename, "ENABLE-LUA-RECORDS", val);
88,939✔
1675
    doLua = (val=="1");
88,939✔
1676
  }
88,939✔
1677
#endif
88,938✔
1678

1679
  // see what we get..
1680
  B.lookup(QType(QType::ANY), state.target, d_sd.domain_id, &pkt);
88,938✔
1681
  vector<DNSZoneRecord> rrset;
88,938✔
1682
  DNSName haveAlias;
88,938✔
1683
  uint8_t aliasScopeMask = 0;
88,938✔
1684
  bool weDone{false};
88,938✔
1685
  bool weRedirected{false};
88,938✔
1686
  bool weHaveUnauth{false};
88,938✔
1687

1688
  while(B.get(zrr)) {
311,829✔
1689
#ifdef HAVE_LUA_RECORDS
222,891✔
1690
    if (zrr.dr.d_type == QType::LUA && !d_dk.isPresigned(d_sd.zonename)) {
222,891!
1691
      if(!doLua) {
×
1692
        continue;
×
1693
      }
×
1694
      auto rec=getRR<LUARecordContent>(zrr.dr);
×
1695
      if (!rec) {
×
1696
        continue;
×
1697
      }
×
1698
      if(rec->d_type == QType::CNAME || rec->d_type == pkt.qtype.getCode() || (pkt.qtype.getCode() == QType::ANY && rec->d_type != QType::RRSIG)) {
×
1699
        state.noCache=true;
×
1700
        try {
×
1701
          auto recvec=luaSynth(rec->getCode(), state.target, zrr, d_sd.qname(), pkt, rec->d_type, s_LUA);
×
1702
          if(!recvec.empty()) {
×
1703
            for (const auto& r_it : recvec) {
×
1704
              zrr.dr.d_type = rec->d_type; // might be CNAME
×
1705
              zrr.dr.setContent(r_it);
×
1706
              zrr.scopeMask = pkt.getRealRemote().getBits(); // this makes sure answer is a specific as your question
×
1707
              rrset.push_back(zrr);
×
1708
            }
×
1709
            if(rec->d_type == QType::CNAME && pkt.qtype.getCode() != QType::CNAME) {
×
1710
              weRedirected = true;
×
1711
            }
×
1712
            else {
×
1713
              weDone = true;
×
1714
            }
×
1715
          }
×
1716
        }
×
1717
        catch(std::exception &e) {
×
1718
          B.lookupEnd();              // don't leave DB handle in bad state
×
1719

1720
          state.r=pkt.replyPacket();
×
1721
          state.r->setRcode(RCode::ServFail);
×
1722
          return false;
×
1723
        }
×
1724
      }
×
1725
    }
×
1726
#endif
222,891✔
1727
    //cerr<<"got content: ["<<zrr.content<<"]"<<endl;
1728
    if (!d_dnssec && pkt.qtype.getCode() == QType::ANY && (zrr.dr.d_type == QType:: DNSKEY || zrr.dr.d_type == QType::NSEC3PARAM)) {
222,891!
1729
      continue; // Don't send dnssec info.
×
1730
    }
×
1731
    if (zrr.dr.d_type == QType::RRSIG) { // RRSIGS are added later any way.
222,891✔
1732
      continue; // TODO: this actually means addRRSig should check if the RRSig is already there
38,605✔
1733
    }
38,605✔
1734

1735
    // cerr<<"Auth: "<<zrr.auth<<", "<<(zrr.dr.d_type == pkt.qtype)<<", "<<zrr.dr.d_type.toString()<<endl;
1736
    if((pkt.qtype.getCode() == QType::ANY || zrr.dr.d_type == pkt.qtype.getCode()) && zrr.auth) {
184,286✔
1737
      weDone=true;
71,150✔
1738
    }
71,150✔
1739
    // the line below fakes 'unauth NS' for delegations for non-DNSSEC backends.
1740
    if((zrr.dr.d_type == pkt.qtype.getCode() && !zrr.auth) || (zrr.dr.d_type == QType::NS && (!zrr.auth || !(d_sd.qname()==zrr.dr.d_name)))) {
184,286✔
1741
      weHaveUnauth=true;
3,785✔
1742
    }
3,785✔
1743

1744
    if(zrr.dr.d_type == QType::CNAME && pkt.qtype.getCode() != QType::CNAME) {
184,286✔
1745
      weRedirected=true;
8,906✔
1746
    }
8,906✔
1747

1748
    if (DP && zrr.dr.d_type == QType::ALIAS && (pkt.qtype.getCode() == QType::A || pkt.qtype.getCode() == QType::AAAA || pkt.qtype.getCode() == QType::ANY) && !d_dk.isPresigned(d_sd.zonename)) {
184,286!
1749
      if (!d_doExpandALIAS) {
334!
1750
        g_log<<Logger::Info<<"ALIAS record found for "<<state.target<<", but ALIAS expansion is disabled."<<endl;
×
1751
        continue;
×
1752
      }
×
1753
      haveAlias=getRR<ALIASRecordContent>(zrr.dr)->getContent();
334✔
1754
      aliasScopeMask=zrr.scopeMask;
334✔
1755
    }
334✔
1756

1757
    // Filter out all SOA's and add them in later
1758
    if(zrr.dr.d_type == QType::SOA) {
184,286✔
1759
      continue;
20,276✔
1760
    }
20,276✔
1761

1762
    rrset.push_back(zrr);
164,010✔
1763
  }
164,010✔
1764

1765
  /* Add in SOA if required */
1766
  if(state.target==d_sd.qname()) {
88,938✔
1767
      zrr=makeEditedDNSZRFromSOAData(d_dk, d_sd);
20,270✔
1768
      rrset.push_back(std::move(zrr));
20,270✔
1769
  }
20,270✔
1770

1771
  DLOG(g_log<<"After first ANY query for '"<<state.target<<"', id="<<d_sd.domain_id<<": weDone="<<weDone<<", weHaveUnauth="<<weHaveUnauth<<", weRedirected="<<weRedirected<<", haveAlias='"<<haveAlias<<"'"<<endl);
88,938✔
1772
  if(pkt.qtype.getCode() == QType::DS && weHaveUnauth &&  !weDone && !weRedirected) {
88,938!
1773
    DLOG(g_log<<"Q for DS of a name for which we do have NS, but for which we don't have DS; need to provide an AUTH answer that shows we don't"<<endl);
915✔
1774
    makeNOError(pkt, state.r, state.target, DNSName(), 1);
915✔
1775
    return true;
915✔
1776
  }
915✔
1777

1778
  if(!haveAlias.empty() && (!weDone || pkt.qtype.getCode() == QType::ANY)) {
88,023!
1779
    DLOG(g_log<<Logger::Warning<<"Found nothing that matched for '"<<state.target<<"', but did get alias to '"<<haveAlias<<"', referring"<<endl);
334✔
1780
    DP->completePacket(state.r, haveAlias, state.target, aliasScopeMask);
334✔
1781
    state.r = nullptr;
334✔
1782
    return false;
334✔
1783
  }
334✔
1784

1785
  // referral for DS query
1786
  if(pkt.qtype.getCode() == QType::DS) {
87,689✔
1787
    DLOG(g_log<<"Qtype is DS"<<endl);
10,742✔
1788
    bool doReferral = true;
10,742✔
1789
    if(d_dk.doesDNSSEC()) {
10,742✔
1790
      for(auto& loopRR: rrset) {
10,786✔
1791
        // In a dnssec capable backend auth=true means, there is no delegation at
1792
        // or above this qname in this zone (for DS queries). Without a delegation,
1793
        // at or above this level, it is pointless to search for referrals.
1794
        if(loopRR.auth) {
10,496✔
1795
          doReferral = false;
9,046✔
1796
          break;
9,046✔
1797
        }
9,046✔
1798
      }
10,496✔
1799
    } else {
10,511✔
1800
      for(auto& loopRR: rrset) {
404✔
1801
        // In a non dnssec capable backend auth is always true, so our only option
1802
        // is, always look for referrals. Unless there is a direct match for DS.
1803
        if(loopRR.dr.d_type == QType::DS) {
404✔
1804
          doReferral = false;
96✔
1805
          break;
96✔
1806
        }
96✔
1807
      }
404✔
1808
    }
314✔
1809
    if(doReferral) {
10,742✔
1810
      DLOG(g_log<<"DS query found no direct result, trying referral now"<<endl);
1,600✔
1811
      if(tryReferral(pkt, state.r, state.target, retargeted)) {
1,600✔
1812
        DLOG(g_log<<"Got referral for DS query"<<endl);
1,125✔
1813
        return true;
1,125✔
1814
      }
1,125✔
1815
    }
1,600✔
1816
  }
10,742✔
1817

1818
  if(rrset.empty()) {
86,564✔
1819
    DLOG(g_log<<Logger::Warning<<"Found nothing in the by-name ANY, but let's try wildcards.."<<endl);
23,607✔
1820
    bool wereRetargeted{false};
23,607✔
1821
    bool nodata{false};
23,607✔
1822
    DNSName wildcard;
23,607✔
1823
    if(tryWildcard(pkt, state.r, state.target, wildcard, wereRetargeted, nodata)) {
23,607✔
1824
      if(wereRetargeted) {
8,040✔
1825
        if (!retargeted) {
2,579✔
1826
          state.r->qdomainwild=std::move(wildcard);
1,155✔
1827
        }
1,155✔
1828
        state.retargeted = true;
2,579✔
1829
        return true;
2,579✔
1830
      }
2,579✔
1831
      if(nodata) {
5,461✔
1832
        makeNOError(pkt, state.r, state.target, wildcard, 2);
1,887✔
1833
      }
1,887✔
1834

1835
      return true;
5,461✔
1836
    }
8,040✔
1837
    try {
15,567✔
1838
      if (tryDNAME(pkt, state.r, state.target)) {
15,567✔
1839
        state.retargeted = true;
951✔
1840
        return true;
951✔
1841
      }
951✔
1842
    } catch (const std::range_error &e) {
15,567✔
1843
      // We couldn't make a CNAME.....
1844
      state.r->setRcode(RCode::YXDomain);
380✔
1845
      return true;
380✔
1846
    }
380✔
1847

1848
    if (!(((pkt.qtype.getCode() == QType::CNAME) || (pkt.qtype.getCode() == QType::ANY)) && retargeted)) {
14,240✔
1849
      makeNXDomain(pkt, state.r, state.target, wildcard);
13,986✔
1850
    }
13,986✔
1851

1852
    return true;
14,240✔
1853
  }
15,567✔
1854

1855
  if(weRedirected) {
62,957✔
1856
    for(auto& loopRR: rrset) {
8,906!
1857
      if(loopRR.dr.d_type == QType::CNAME) {
8,906!
1858
        state.r->addRecord(DNSZoneRecord(loopRR));
8,906✔
1859
        state.target = getRR<CNAMERecordContent>(loopRR.dr)->getTarget();
8,906✔
1860
        state.retargeted = true;
8,906✔
1861
        return true;
8,906✔
1862
      }
8,906✔
1863
    }
8,906✔
1864
  }
8,906✔
1865
  else if(weDone) {
54,051✔
1866
    bool haveRecords = false;
32,038✔
1867
    bool presigned = d_dk.isPresigned(d_sd.zonename);
32,038✔
1868
    for(const auto& loopRR: rrset) {
105,338✔
1869
      if (loopRR.dr.d_type == QType::ENT) {
105,338✔
1870
        continue;
617✔
1871
      }
617✔
1872
      if (loopRR.dr.d_type == QType::ALIAS && d_doExpandALIAS && !presigned) {
104,721!
1873
        continue;
×
1874
      }
×
1875
#ifdef HAVE_LUA_RECORDS
104,721✔
1876
      if (loopRR.dr.d_type == QType::LUA && !presigned) {
104,721!
1877
        continue;
×
1878
      }
×
1879
#endif
104,721✔
1880
      if ((pkt.qtype.getCode() == QType::ANY || loopRR.dr.d_type == pkt.qtype.getCode()) && loopRR.auth) {
104,721✔
1881
        state.r->addRecord(DNSZoneRecord(loopRR));
70,033✔
1882
        haveRecords = true;
70,033✔
1883
      }
70,033✔
1884
    }
104,721✔
1885

1886
    if (haveRecords) {
32,038✔
1887
      if(d_dnssec && pkt.qtype.getCode() == QType::ANY) {
31,421✔
1888
        completeANYRecords(pkt, state.r, state.target);
2,859✔
1889
      }
2,859✔
1890
    }
31,421✔
1891
    else {
617✔
1892
      makeNOError(pkt, state.r, state.target, DNSName(), 0);
617✔
1893
    }
617✔
1894

1895
    return true;
32,038✔
1896
  }
32,038✔
1897
  else if(weHaveUnauth) {
22,013✔
1898
    DLOG(g_log<<"Have unauth data, so need to hunt for best NS records"<<endl);
4✔
1899
    if (tryReferral(pkt, state.r, state.target, retargeted)) {
4!
1900
      return true;
×
1901
    }
×
1902
    // check whether this could be fixed easily
1903
    // if (*(rrset.back().dr.d_name.rbegin()) == '.') {
1904
    //      g_log<<Logger::Error<<"Should not get here ("<<pkt.qdomain<<"|"<<pkt.qtype.toString()<<"): you have a trailing dot, this could be the problem (or run pdnsutil rectify-zone " <<d_sd.qname()<<")"<<endl;
1905
    // } else {
1906
         g_log<<Logger::Error<<"Should not get here ("<<pkt.qdomain<<"|"<<pkt.qtype.toString()<<"): please run pdnsutil rectify-zone "<<d_sd.qname()<<endl;
4✔
1907
    // }
1908
  }
4✔
1909
  else {
22,009✔
1910
    DLOG(g_log<<"Have some data, but not the right data"<<endl);
22,009✔
1911
    makeNOError(pkt, state.r, state.target, DNSName(), 0);
22,009✔
1912
  }
22,009✔
1913
  return true;
22,013✔
1914
}
62,957✔
1915

1916
std::unique_ptr<DNSPacket> PacketHandler::opcodeQuery(DNSPacket& pkt, bool noCache)
1917
{
111,560✔
1918
  queryState state;
111,560✔
1919
  state.noCache = noCache;
111,560✔
1920

1921
  if (opcodeQueryInner(pkt, state)) {
111,560✔
1922
    doAdditionalProcessing(pkt, state.r);
108,788✔
1923

1924
    // now that all processing is done, span and view may have been set, so we copy them
1925
    state.r->d_span = pkt.d_span;
108,788✔
1926
    state.r->d_view = pkt.d_view;
108,788✔
1927

1928
    for(const auto& loopRR: state.r->getRRS()) {
285,846✔
1929
      if (loopRR.scopeMask != 0) {
285,835✔
1930
        state.noCache=true;
214✔
1931
        break;
214✔
1932
      }
214✔
1933
    }
285,835✔
1934
    if (state.doSigs) {
108,788✔
1935
      addRRSigs(d_dk, B, state.authSet, state.r->getRRS(), &pkt);
88,984✔
1936
    }
88,984✔
1937

1938
    if (PC.enabled() && !state.noCache && pkt.couldBeCached()) {
108,788!
1939
      PC.insert(pkt, *state.r, state.r->getMinTTL(), pkt.d_view); // in the packet cache
14,944✔
1940
    }
14,944✔
1941
  }
108,788✔
1942

1943
  return std::move(state.r);
111,560✔
1944
}
111,560✔
1945

1946
std::unique_ptr<DNSPacket> PacketHandler::opcodeNotify(DNSPacket& pkt, bool /* noCache */)
1947
{
7✔
1948
  S.inc("incoming-notifications");
7✔
1949
  int res=processNotify(pkt);
7✔
1950
  if(res>=0) {
7!
1951
    auto resp=pkt.replyPacket();  // generate an empty reply packet
7✔
1952
    resp->setRcode(res);
7✔
1953
    resp->setOpcode(Opcode::Notify);
7✔
1954
    return resp;
7✔
1955
  }
7✔
1956
  return nullptr;
×
1957
}
7✔
1958

1959
std::unique_ptr<DNSPacket> PacketHandler::opcodeUpdate(DNSPacket& pkt, bool /* noCache */)
1960
{
1,252✔
1961
  if (g_views) {
1,252!
1962
    // Make this variant-aware without performing the complete UeberBackend::getAuth work
1963
    g_zoneCache.setZoneVariant(pkt);
×
1964
  }
×
1965
  else {
1,252✔
1966
    pkt.qdomainzone = ZoneName(pkt.qdomain);
1,252✔
1967
  }
1,252✔
1968

1969
  S.inc("dnsupdate-queries");
1,252✔
1970
  int res=processUpdate(pkt);
1,252✔
1971
  if (res == RCode::Refused) {
1,252✔
1972
    S.inc("dnsupdate-refused");
24✔
1973
  }
24✔
1974
  else if (res != RCode::ServFail) {
1,228!
1975
    S.inc("dnsupdate-answers");
1,228✔
1976
  }
1,228✔
1977
  auto resp=pkt.replyPacket();  // generate an empty reply packet
1,252✔
1978
  resp->setRcode(res);
1,252✔
1979
  resp->setOpcode(Opcode::Update);
1,252✔
1980
  return resp;
1,252✔
1981
}
1,252✔
1982

1983
// NOLINTNEXTLINE(readability-convert-member-functions-to-static): can't make it static as it is used through member method pointer in doQuestion()
1984
std::unique_ptr<DNSPacket> PacketHandler::opcodeNotImplemented(DNSPacket& pkt, bool /* noCache */)
1985
{
×
1986
  g_log<<Logger::Error<<"Received an unknown opcode "<<pkt.d.opcode<<" from "<<pkt.getRemoteString()<<" for "<<pkt.qdomain<<endl;
×
1987

1988
  auto resp=pkt.replyPacket();  // generate an empty reply packet
×
1989
  resp->setRcode(RCode::NotImp);
×
1990
  return resp;
×
1991
}
×
1992

1993
bool PacketHandler::checkForCorrectTSIG(const DNSPacket& packet, DNSName* tsigkeyname, string* secret, TSIGRecordContent* tsigContent)
1994
{
254✔
1995
  uint16_t tsigPos{0};
254✔
1996

1997
  if (!packet.getTSIGDetails(tsigContent, tsigkeyname, &tsigPos)) {
254!
1998
    return false;
×
1999
  }
×
2000

2001
  TSIGTriplet tsigTriplet;
254✔
2002
  tsigTriplet.name = *tsigkeyname;
254✔
2003
  tsigTriplet.algo = tsigContent->d_algoName;
254✔
2004
  if (tsigTriplet.algo == DNSName("hmac-md5.sig-alg.reg.int")) {
254✔
2005
    tsigTriplet.algo = DNSName("hmac-md5");
182✔
2006
  }
182✔
2007

2008
  if (tsigTriplet.algo != DNSName("gss-tsig")) {
254!
2009
    string secret64;
254✔
2010
    if (!B.getTSIGKey(*tsigkeyname, tsigTriplet.algo, secret64)) {
254!
2011
      g_log << Logger::Error << "Packet for domain '" << packet.qdomain << "' denied: can't find TSIG key with name '" << *tsigkeyname << "' and algorithm '" << tsigTriplet.algo << "'" << endl;
×
2012
      return false;
×
2013
    }
×
2014
    B64Decode(secret64, *secret);
254✔
2015
    tsigTriplet.secret = *secret;
254✔
2016
  }
254✔
2017

2018
  try {
254✔
2019
    return packet.validateTSIG(tsigTriplet, *tsigContent, "", tsigContent->d_mac, false);
254✔
2020
  }
254✔
2021
  catch(const std::runtime_error& err) {
254✔
2022
    g_log<<Logger::Error<<"Packet for '"<<packet.qdomain<<"' denied: "<<err.what()<<endl;
144✔
2023
    return false;
144✔
2024
  }
144✔
2025
}
254✔
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

© 2025 Coveralls, Inc