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

PowerDNS / pdns / 19765062687

28 Nov 2025 01:22PM UTC coverage: 73.135% (+0.03%) from 73.105%
19765062687

Pull #16574

github

web-flow
Merge cc585a1f8 into 8b4e32eff
Pull Request #16574: Improve GitHub error highlighting

38564 of 63428 branches covered (60.8%)

Branch coverage included in aggregate %.

128155 of 164534 relevant lines covered (77.89%)

6255827.11 hits per line

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

72.59
/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
  {
753✔
89
    d_update_policy_lua = nullptr;
753✔
90
  }
753✔
91
  else
1✔
92
  {
1✔
93
    try {
1✔
94
      d_update_policy_lua = std::make_unique<AuthLua4>();
1✔
95
      d_update_policy_lua->loadFile(fname);
1✔
96
    }
1✔
97
    catch (const std::runtime_error& e) {
1✔
98
      g_log<<Logger::Warning<<"Failed to load update policy - disabling: "<<e.what()<<endl;
×
99
      d_update_policy_lua = nullptr;
×
100
    }
×
101
  }
1✔
102
}
754✔
103

104
UeberBackend *PacketHandler::getBackend()
105
{
4,087✔
106
  return &B;
4,087✔
107
}
4,087✔
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
 * @param sd         SOAData of the zone from which CDNSKEY contents are taken (default: d_sd)
121
 * @return           bool that shows if any records were added
122
**/
123
bool PacketHandler::addCDNSKEY(DNSPacket& p, std::unique_ptr<DNSPacket>& r)
124
{
1,736✔
125
  return addCDNSKEY(p, r, d_sd);
1,736✔
126
}
1,736✔
127
bool PacketHandler::addCDNSKEY(DNSPacket& p, std::unique_ptr<DNSPacket>& r, SOAData &sd) // NOLINT(readability-identifier-length)
128
{
1,736✔
129
  string publishCDNSKEY;
1,736✔
130
  d_dk.getPublishCDNSKEY(sd.zonename,publishCDNSKEY);
1,736✔
131
  if (publishCDNSKEY.empty())
1,736✔
132
    return false;
332✔
133

134
  DNSZoneRecord rr;
1,404✔
135
  rr.dr.d_type=QType::CDNSKEY;
1,404✔
136
  rr.dr.d_ttl=sd.minimum;
1,404✔
137
  rr.dr.d_name=p.qdomain;
1,404✔
138
  rr.auth=true;
1,404✔
139

140
  if (publishCDNSKEY == "0") { // delete DS via CDNSKEY
1,404✔
141
    rr.dr.setContent(s_deleteCDNSKEYContent);
468✔
142
    r->addRecord(std::move(rr));
468✔
143
    return true;
468✔
144
  }
468✔
145

146
  bool haveOne=false;
936✔
147
  for (const auto& value : d_dk.getEntryPoints(sd.zonename)) {
936✔
148
    if (!value.second.published) {
936!
149
      continue;
×
150
    }
×
151
    rr.dr.setContent(std::make_shared<DNSKEYRecordContent>(value.first.getDNSKEY()));
936✔
152
    r->addRecord(DNSZoneRecord(rr));
936✔
153
    haveOne=true;
936✔
154
  }
936✔
155

156
  if(::arg().mustDo("direct-dnskey")) {
936✔
157
    B.lookup(QType(QType::CDNSKEY), sd.qname(), sd.domain_id, &p);
88✔
158

159
    while(B.get(rr)) {
88!
160
      rr.dr.d_ttl=sd.minimum;
×
161
      rr.dr.d_name=p.qdomain;
×
162
      r->addRecord(std::move(rr));
×
163
      haveOne=true;
×
164
    }
×
165
  }
88✔
166
  return haveOne;
936✔
167
}
1,404✔
168

169
/**
170
 * This adds DNSKEY records to the answer packet. Returns true if one was added.
171
 *
172
 * @param p          Pointer to the DNSPacket containing the original question
173
 * @param r          Pointer to the DNSPacket where the records should be inserted into
174
 * @return           bool that shows if any records were added
175
**/
176
bool PacketHandler::addDNSKEY(DNSPacket& p, std::unique_ptr<DNSPacket>& r)
177
{
27,254✔
178
  DNSZoneRecord rr;
27,254✔
179
  bool haveOne=false;
27,254✔
180

181
  for (const auto& value : d_dk.getKeys(r->qdomainzone)) {
28,057✔
182
    if (!value.second.published) {
27,957✔
183
      continue;
432✔
184
    }
432✔
185
    rr.dr.d_type=QType::DNSKEY;
27,525✔
186
    rr.dr.d_ttl=d_sd.minimum;
27,525✔
187
    rr.dr.d_name=p.qdomain;
27,525✔
188
    rr.dr.setContent(std::make_shared<DNSKEYRecordContent>(value.first.getDNSKEY()));
27,525✔
189
    rr.auth=true;
27,525✔
190
    r->addRecord(std::move(rr));
27,525✔
191
    haveOne=true;
27,525✔
192
  }
27,525✔
193

194
  if(::arg().mustDo("direct-dnskey")) {
27,254✔
195
    B.lookup(QType(QType::DNSKEY), p.qdomain, d_sd.domain_id, &p);
2,055✔
196

197
    while(B.get(rr)) {
2,055!
198
      rr.dr.d_ttl=d_sd.minimum;
×
199
      r->addRecord(std::move(rr));
×
200
      haveOne=true;
×
201
    }
×
202
  }
2,055✔
203

204
  return haveOne;
27,254✔
205
}
27,254✔
206

207
/**
208
 * This adds CDS records to the answer packet r.
209
 *
210
 * @param p   Pointer to the DNSPacket containing the original question.
211
 * @param r   Pointer to the DNSPacket where the records should be inserted into.
212
 *            used to determine record TTL.
213
 * @param sd  SOAData of the zone from which CDS contents are taken (default: d_sd)
214
 * @return    bool that shows if any records were added.
215
**/
216
bool PacketHandler::addCDS(DNSPacket& p, std::unique_ptr<DNSPacket>& r) // NOLINT(readability-identifier-length)
217
{
1,741✔
218
  return addCDS(p, r, d_sd);
1,741✔
219
}
1,741✔
220
bool PacketHandler::addCDS(DNSPacket& p, std::unique_ptr<DNSPacket>& r, SOAData &sd) // NOLINT(readability-identifier-length)
221
{
1,741✔
222
  string publishCDS;
1,741✔
223
  d_dk.getPublishCDS(sd.zonename, publishCDS);
1,741✔
224
  if (publishCDS.empty())
1,741✔
225
    return false;
333✔
226

227
  vector<string> digestAlgos;
1,408✔
228
  stringtok(digestAlgos, publishCDS, ", ");
1,408✔
229

230
  DNSZoneRecord rr;
1,408✔
231
  rr.dr.d_type=QType::CDS;
1,408✔
232
  rr.dr.d_ttl=sd.minimum;
1,408✔
233
  rr.dr.d_name=p.qdomain;
1,408✔
234
  rr.auth=true;
1,408✔
235

236
  if(std::find(digestAlgos.begin(), digestAlgos.end(), "0") != digestAlgos.end()) { // delete DS via CDS
1,408✔
237
    rr.dr.setContent(s_deleteCDSContent);
469✔
238
    r->addRecord(std::move(rr));
469✔
239
    return true;
469✔
240
  }
469✔
241

242
  bool haveOne=false;
939✔
243

244
  for (const auto& value : d_dk.getEntryPoints(sd.zonename)) {
942✔
245
    if (!value.second.published) {
942!
246
      continue;
×
247
    }
×
248
    for(auto const &digestAlgo : digestAlgos){
944✔
249
      rr.dr.setContent(std::make_shared<DSRecordContent>(makeDSFromDNSKey(sd.qname(), value.first.getDNSKEY(), pdns::checked_stoi<uint8_t>(digestAlgo))));
944✔
250
      r->addRecord(DNSZoneRecord(rr));
944✔
251
      haveOne=true;
944✔
252
    }
944✔
253
  }
942✔
254

255
  if(::arg().mustDo("direct-dnskey")) {
939✔
256
    B.lookup(QType(QType::CDS), sd.qname(), sd.domain_id, &p);
88✔
257

258
    while(B.get(rr)) {
88!
259
      rr.dr.d_ttl=sd.minimum;
×
260
      rr.dr.d_name=p.qdomain;
×
261
      r->addRecord(std::move(rr));
×
262
      haveOne=true;
×
263
    }
×
264
  }
88✔
265

266
  return haveOne;
939✔
267
}
1,408✔
268

269
/** This adds NSEC3PARAM records. Returns true if one was added */
270
bool PacketHandler::addNSEC3PARAM(const DNSPacket& p, std::unique_ptr<DNSPacket>& r)
271
{
2,698✔
272
  DNSZoneRecord rr;
2,698✔
273

274
  NSEC3PARAMRecordContent ns3prc;
2,698✔
275
  if(d_dk.getNSEC3PARAM(r->qdomainzone, &ns3prc)) {
2,698✔
276
    rr.dr.d_type=QType::NSEC3PARAM;
1,630✔
277
    rr.dr.d_ttl=d_sd.minimum;
1,630✔
278
    rr.dr.d_name=p.qdomain;
1,630✔
279
    ns3prc.d_flags = 0; // the NSEC3PARAM 'flag' is defined to always be zero in RFC5155.
1,630✔
280
    rr.dr.setContent(std::make_shared<NSEC3PARAMRecordContent>(ns3prc));
1,630✔
281
    rr.auth = true;
1,630✔
282
    r->addRecord(std::move(rr));
1,630✔
283
    return true;
1,630✔
284
  }
1,630✔
285
  return false;
1,068✔
286
}
2,698✔
287

288

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

294
  if(p.qtype.getCode()==QType::TXT) {
×
295
    static const DNSName versionbind("version.bind."), versionpdns("version.pdns."), idserver("id.server.");
×
296
    if (target==versionbind || target==versionpdns) {
×
297
      // modes: full, powerdns only, anonymous or custom
298
      const static string mode=::arg()["version-string"];
×
299
      string content;
×
300
      if(mode=="full")
×
301
        content=fullVersionString();
×
302
      else if(mode=="powerdns")
×
303
        content="Served by PowerDNS - https://www.powerdns.com/";
×
304
      else if(mode=="anonymous") {
×
305
        r->setRcode(RCode::ServFail);
×
306
        return 0;
×
307
      }
×
308
      else
×
309
        content=mode;
×
310
      rr.dr.setContent(DNSRecordContent::make(QType::TXT, 1, "\"" + content + "\""));
×
311
    }
×
312
    else if (target==idserver) {
×
313
      // modes: disabled, hostname or custom
314
      const static string id=::arg()["server-id"];
×
315

316
      if (id == "disabled") {
×
317
        r->setRcode(RCode::Refused);
×
318
        return 0;
×
319
      }
×
320
      string tid=id;
×
321
      if(!tid.empty() && tid[0]!='"') { // see #6010 however
×
322
        tid = "\"" + tid + "\"";
×
323
      }
×
324
      rr.dr.setContent(DNSRecordContent::make(QType::TXT, 1, tid));
×
325
    }
×
326
    else {
×
327
      r->setRcode(RCode::Refused);
×
328
      return 0;
×
329
    }
×
330

331
    rr.dr.d_ttl=5;
×
332
    rr.dr.d_name=target;
×
333
    rr.dr.d_type=QType::TXT;
×
334
    rr.dr.d_class=QClass::CHAOS;
×
335
    r->addRecord(std::move(rr));
×
336
    return 1;
×
337
  }
×
338

339
  r->setRcode(RCode::NotImp);
×
340
  return 0;
×
341
}
×
342

343
vector<DNSZoneRecord> PacketHandler::getBestReferralNS(DNSPacket& p, const DNSName &target)
344
{
93,822✔
345
  vector<DNSZoneRecord> ret;
93,822✔
346
  DNSZoneRecord rr;
93,822✔
347
  DNSName subdomain(target);
93,822✔
348
  do {
225,546✔
349
    if(subdomain == d_sd.qname()) { // stop at SOA
225,546✔
350
      break;
87,752✔
351
    }
87,752✔
352
    B.lookup(QType(QType::NS), subdomain, d_sd.domain_id, &p);
137,794✔
353
    while(B.get(rr)) {
146,604✔
354
      ret.push_back(rr); // this used to exclude auth NS records for some reason
8,810✔
355
    }
8,810✔
356
    if(!ret.empty()) {
137,794✔
357
      return ret;
6,077✔
358
    }
6,077✔
359
  } while( subdomain.chopOff() );   // 'www.powerdns.org' -> 'powerdns.org' -> 'org' -> ''
137,794✔
360
  return ret;
87,745✔
361
}
93,822✔
362

363
void PacketHandler::getBestDNAMESynth(DNSPacket& p, DNSName &target, vector<DNSZoneRecord> &ret)
364
{
19,847✔
365
  ret.clear();
19,847✔
366
  DNSZoneRecord rr;
19,847✔
367
  DNSName prefix;
19,847✔
368
  DNSName subdomain(target);
19,847✔
369
  do {
65,126✔
370
    DLOG(g_log<<"Attempting DNAME lookup for "<<subdomain<<", d_sd.qname()="<<d_sd.qname()<<endl);
65,126✔
371

372
    B.lookup(QType(QType::DNAME), subdomain, d_sd.domain_id, &p);
65,126✔
373
    while(B.get(rr)) {
66,457✔
374
      ret.push_back(rr);  // put in the original
1,331✔
375
      rr.dr.d_type = QType::CNAME;
1,331✔
376
      rr.dr.d_name = prefix + rr.dr.d_name;
1,331✔
377
      rr.dr.setContent(std::make_shared<CNAMERecordContent>(CNAMERecordContent(prefix + getRR<DNAMERecordContent>(rr.dr)->getTarget())));
1,331✔
378
      rr.auth = false; // don't sign CNAME
1,331✔
379
      target = getRR<CNAMERecordContent>(rr.dr)->getTarget();
1,331✔
380
      ret.push_back(rr);
1,331✔
381
    }
1,331✔
382
    if(!ret.empty()) {
65,126✔
383
      return;
951✔
384
    }
951✔
385
    if(subdomain.hasLabels()) {
64,175✔
386
      prefix.appendRawLabel(subdomain.getRawLabel(0)); // XXX DNSName pain this feels wrong
63,022✔
387
    }
63,022✔
388
    if(subdomain == d_sd.qname()) { // stop at SOA
64,175✔
389
      break;
18,520✔
390
    }
18,520✔
391

392
  } while( subdomain.chopOff() );   // 'www.powerdns.org' -> 'powerdns.org' -> 'org' -> ''
64,175✔
393
  return;
18,896✔
394
}
19,847✔
395

396

397
// Return best matching wildcard or next closer name
398
bool PacketHandler::getBestWildcard(DNSPacket& p, const DNSName &target, DNSName &wildcard, vector<DNSZoneRecord>* ret)
399
{
28,023✔
400
  ret->clear();
28,023✔
401
  DNSZoneRecord rr;
28,023✔
402
  DNSName subdomain(target);
28,023✔
403
  bool haveSomething=false;
28,023✔
404
  bool haveCNAME = false;
28,023✔
405

406
#ifdef HAVE_LUA_RECORDS
28,023✔
407
  bool doLua = doLuaRecords();
28,023✔
408
#endif
28,023✔
409

410
  wildcard=subdomain;
28,023✔
411
  while( subdomain.chopOff() && !haveSomething )  {
50,651✔
412
    if (subdomain.empty()) {
50,650!
413
      B.lookup(QType(QType::ANY), g_wildcarddnsname, d_sd.domain_id, &p);
×
414
    } else {
50,650✔
415
      B.lookup(QType(QType::ANY), g_wildcarddnsname+subdomain, d_sd.domain_id, &p);
50,650✔
416
    }
50,650✔
417
    while(B.get(rr)) {
60,694✔
418
      if (haveCNAME) {
10,634✔
419
        // No need to look any further
420
        B.lookupEnd();
590✔
421
        break;
590✔
422
      }
590✔
423
#ifdef HAVE_LUA_RECORDS
10,044✔
424
      if (rr.dr.d_type == QType::LUA && !isPresigned()) {
10,044!
425
        if(!doLua) {
×
426
          DLOG(g_log<<"Have a wildcard Lua match, but not doing Lua record for this zone"<<endl);
×
427
          continue;
×
428
        }
×
429

430
        DLOG(g_log<<"Have a wildcard Lua match"<<endl);
×
431

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

456
            throw;
×
457
          }
×
458
        }
×
459
      }
×
460
      else
10,044✔
461
#endif
10,044✔
462
      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))) {
10,044✔
463
        if (rr.dr.d_type == QType::CNAME) {
6,495✔
464
          haveCNAME = true;
2,579✔
465
          ret->clear();
2,579✔
466
        }
2,579✔
467
        ret->push_back(rr);
6,495✔
468
      }
6,495✔
469

470
      // NOLINTNEXTLINE(readability-misleading-indentation): go home, clang-tidy, you're drunk
471
      wildcard=g_wildcarddnsname+subdomain;
10,044✔
472
      haveSomething=true;
10,044✔
473
    }
10,044✔
474

475
    if ( subdomain == d_sd.qname() || haveSomething ) { // stop at SOA or result
50,650✔
476
      break;
19,025✔
477
    }
19,025✔
478

479
    B.lookup(QType(QType::ANY), subdomain, d_sd.domain_id, &p);
31,625✔
480
    if (B.get(rr)) {
31,625✔
481
      DLOG(g_log<<"No wildcard match, ancestor exists"<<endl);
9,003✔
482
      B.lookupEnd();
9,003✔
483
      break;
9,003✔
484
    }
9,003✔
485
    wildcard=subdomain;
22,622✔
486
  }
22,622✔
487

488
  return haveSomething;
28,023✔
489
}
28,023✔
490

491
DNSName PacketHandler::doAdditionalServiceProcessing(const DNSName &firstTarget, const uint16_t &qtype, std::unique_ptr<DNSPacket>& /* r */, vector<DNSZoneRecord>& extraRecords) {
682✔
492
  DNSName ret = firstTarget;
682✔
493
  size_t ctr = 5; // Max 5 SVCB Aliasforms per query
682✔
494
  bool done = false;
682✔
495
  while (!done && ctr > 0) {
1,364!
496
    DNSZoneRecord rr;
682✔
497
    done = true;
682✔
498

499
    if(!ret.isPartOf(d_sd.qname())) {
682✔
500
      continue;
341✔
501
    }
341✔
502

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

520
          throw PDNSException("Unknown type (" + QType(qtype).toString() + ") for additional service processing");
×
521
      }
341✔
522
    }
341✔
523
    ctr--;
341✔
524
  }
341✔
525
  return ret;
682✔
526
}
682✔
527

528

529
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
530
void PacketHandler::doAdditionalProcessing(DNSPacket& p, std::unique_ptr<DNSPacket>& r)
531
{
124,353✔
532
  DNSName content;
124,353✔
533
  DNSZoneRecord dzr;
124,353✔
534
  std::unordered_set<DNSName> lookup;
124,353✔
535
  vector<DNSZoneRecord> extraRecords;
124,353✔
536
  const auto& rrs = r->getRRS();
124,353✔
537

538
  lookup.reserve(rrs.size());
124,353✔
539
  for(auto& rr : rrs) {
251,889✔
540
    if(rr.dr.d_place != DNSResourceRecord::ADDITIONAL) {
251,891✔
541
      content.clear();
251,885✔
542
      switch(rr.dr.d_type) {
251,885✔
543
        case QType::NS:
15,846✔
544
          content=getRR<NSRecordContent>(rr.dr)->getNS();
15,846✔
545
          break;
15,846✔
546
        case QType::MX:
6,808✔
547
          content=getRR<MXRecordContent>(rr.dr)->d_mxname;
6,808✔
548
          break;
6,808✔
549
        case QType::SRV:
2,023✔
550
          content=getRR<SRVRecordContent>(rr.dr)->d_target;
2,023✔
551
          break;
2,023✔
552
        case QType::SVCB: /* fall-through */
2,387✔
553
        case QType::HTTPS: {
2,387!
554
          auto rrc = getRR<SVCBBaseRecordContent>(rr.dr);
2,387✔
555
          content = rrc->getTarget();
2,387✔
556
          if (content.isRoot()) {
2,387✔
557
            content = rr.dr.d_name;
1,705✔
558
          }
1,705✔
559
          if (rrc->getPriority() == 0) {
2,387✔
560
            content = doAdditionalServiceProcessing(content, rr.dr.d_type, r, extraRecords);
682✔
561
          }
682✔
562
          break;
2,387✔
563
        }
2,387✔
564
        case QType::NAPTR: {
2,046✔
565
          auto naptrContent = getRR<NAPTRRecordContent>(rr.dr);
2,046✔
566
          auto flags = naptrContent->getFlags();
2,046✔
567
          toLowerInPlace(flags);
2,046✔
568
          if (flags.find('a') != string::npos) {
2,046✔
569
            content = naptrContent->getReplacement();
682✔
570
            DLOG(g_log<<Logger::Debug<<"adding NAPTR replacement 'a'="<<content<<endl);
682✔
571
          }
682✔
572
          else if (flags.find('s') != string::npos) {
1,364✔
573
            content = naptrContent->getReplacement();
682✔
574
            DLOG(g_log<<Logger::Debug<<"adding NAPTR replacement 's'="<<content<<endl);
682✔
575
            B.lookup(QType(QType::SRV), content, d_sd.domain_id, &p);
682✔
576
            while(B.get(dzr)) {
2,046✔
577
              content=getRR<SRVRecordContent>(dzr.dr)->d_target;
1,364✔
578
              if(content.isPartOf(d_sd.qname())) {
1,364!
579
                lookup.emplace(content);
1,364✔
580
              }
1,364✔
581
              dzr.dr.d_place=DNSResourceRecord::ADDITIONAL;
1,364✔
582
              extraRecords.emplace_back(std::move(dzr));
1,364✔
583
            }
1,364✔
584
            content.clear();
682✔
585
          }
682✔
586
          break;
2,046✔
587
        }
2,387✔
588
        default:
222,787✔
589
          continue;
222,787✔
590
      }
251,885✔
591
      if(!content.empty() && content.isPartOf(d_sd.qname())) {
29,110✔
592
        lookup.emplace(content);
21,518✔
593
      }
21,518✔
594
    }
29,110✔
595
  }
251,889✔
596

597
  for(auto& rr : extraRecords) {
124,365✔
598
    r->addRecord(std::move(rr));
1,705✔
599
  }
1,705✔
600
  extraRecords.clear();
124,365✔
601
  // TODO should we have a setting to do this?
602
  for (auto &rec : r->getServiceRecords()) {
124,365✔
603
    // Process auto hints
604
    auto rrc = getRR<SVCBBaseRecordContent>(rec->dr);
2,728✔
605
    DNSName target = rrc->getTarget().isRoot() ? rec->dr.d_name : rrc->getTarget();
2,728✔
606

607
    if (rrc->hasParam(SvcParam::ipv4hint) && rrc->autoHint(SvcParam::ipv4hint)) {
2,728!
608
      auto newRRC = rrc->clone();
×
609
      if (!newRRC) {
×
610
        continue;
×
611
      }
×
612
      if (s_SVCAutohints) {
×
613
        auto hints = getIPAddressFor(target, QType::A);
×
614
        if (hints.size() == 0) {
×
615
          newRRC->removeParam(SvcParam::ipv4hint);
×
616
        } else {
×
617
          newRRC->setHints(SvcParam::ipv4hint, hints);
×
618
        }
×
619
      } else {
×
620
        newRRC->removeParam(SvcParam::ipv4hint);
×
621
      }
×
622
      rrc = newRRC;
×
623
      rec->dr.setContent(std::move(newRRC));
×
624
    }
×
625

626
    if (rrc->hasParam(SvcParam::ipv6hint) && rrc->autoHint(SvcParam::ipv6hint)) {
2,728!
627
      auto newRRC = rrc->clone();
×
628
      if (!newRRC) {
×
629
        continue;
×
630
      }
×
631
      if (s_SVCAutohints) {
×
632
        auto hints = getIPAddressFor(target, QType::AAAA);
×
633
        if (hints.size() == 0) {
×
634
          newRRC->removeParam(SvcParam::ipv6hint);
×
635
        } else {
×
636
          newRRC->setHints(SvcParam::ipv6hint, hints);
×
637
        }
×
638
      } else {
×
639
        newRRC->removeParam(SvcParam::ipv6hint);
×
640
      }
×
641
      rec->dr.setContent(std::move(newRRC));
×
642
    }
×
643
  }
2,728✔
644

645
  for(const auto& name : lookup) {
124,365✔
646
    B.lookup(QType(QType::ANY), name, d_sd.domain_id, &p);
20,154✔
647
    while(B.get(dzr)) {
91,444✔
648
      if(dzr.dr.d_type == QType::A || dzr.dr.d_type == QType::AAAA) {
71,290✔
649
        dzr.dr.d_place=DNSResourceRecord::ADDITIONAL;
63,237✔
650
        r->addRecord(std::move(dzr));
63,237✔
651
      }
63,237✔
652
    }
71,290✔
653
  }
20,154✔
654
}
124,365✔
655

656
vector<ComboAddress> PacketHandler::getIPAddressFor(const DNSName &target, const uint16_t qtype) {
×
657
  vector<ComboAddress> ret;
×
658
  if (qtype != QType::A && qtype != QType::AAAA) {
×
659
    return ret;
×
660
  }
×
661
  B.lookup(qtype, target, d_sd.domain_id);
×
662
  DNSZoneRecord rr;
×
663
  while (B.get(rr)) {
×
664
    if (qtype == QType::AAAA) {
×
665
      auto aaaarrc = getRR<AAAARecordContent>(rr.dr);
×
666
      ret.push_back(aaaarrc->getCA());
×
667
    } else if (qtype == QType::A) {
×
668
      auto arrc = getRR<ARecordContent>(rr.dr);
×
669
      ret.push_back(arrc->getCA());
×
670
    }
×
671
  }
×
672
  return ret;
×
673
}
×
674

675
void PacketHandler::emitNSEC(std::unique_ptr<DNSPacket>& r, const DNSName& name, const DNSName& next, int mode)
676
{
22,779✔
677
  NSECRecordContent nrc;
22,779✔
678
  nrc.d_next = next;
22,779✔
679

680
  nrc.set(QType::NSEC);
22,779✔
681
  nrc.set(QType::RRSIG);
22,779✔
682
  if(d_sd.qname() == name) {
22,779✔
683
    nrc.set(QType::SOA); // 1dfd8ad SOA can live outside the records table
8,345✔
684
    if(!isPresigned()) {
8,345✔
685
      auto keyset = d_dk.getKeys(d_sd.zonename);
7,374✔
686
      for(const auto& value: keyset) {
7,374✔
687
        if (value.second.published) {
7,374✔
688
          nrc.set(QType::DNSKEY);
7,325✔
689
          string publishCDNSKEY;
7,325✔
690
          d_dk.getPublishCDNSKEY(d_sd.zonename, publishCDNSKEY);
7,325✔
691
          if (! publishCDNSKEY.empty())
7,325✔
692
            nrc.set(QType::CDNSKEY);
588✔
693
          string publishCDS;
7,325✔
694
          d_dk.getPublishCDS(d_sd.zonename, publishCDS);
7,325✔
695
          if (! publishCDS.empty())
7,325✔
696
            nrc.set(QType::CDS);
588✔
697
          break;
7,325✔
698
        }
7,325✔
699
      }
7,374✔
700
    }
7,374✔
701
  }
8,345✔
702

703
  DNSZoneRecord rr;
22,779✔
704
#ifdef HAVE_LUA_RECORDS
22,779✔
705
  bool first{true};
22,779✔
706
  bool doLua{false};
22,779✔
707
#endif
22,779✔
708

709
  B.lookup(QType(QType::ANY), name, d_sd.domain_id);
22,779✔
710
  while(B.get(rr)) {
89,678✔
711
#ifdef HAVE_LUA_RECORDS
66,899✔
712
    if (rr.dr.d_type == QType::LUA && first && !isPresigned()) {
66,899!
713
      first = false;
×
714
      doLua = doLuaRecords();
×
715
    }
×
716

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

740
  rr.dr.d_name = name;
22,779✔
741
  rr.dr.d_ttl = d_sd.getNegativeTTL();
22,779✔
742
  rr.dr.d_type = QType::NSEC;
22,779✔
743
  rr.dr.setContent(std::make_shared<NSECRecordContent>(std::move(nrc)));
22,779✔
744
  rr.dr.d_place = (mode == 5 ) ? DNSResourceRecord::ANSWER: DNSResourceRecord::AUTHORITY;
22,779✔
745
  rr.auth = true;
22,779✔
746

747
  r->addRecord(std::move(rr));
22,779✔
748
}
22,779✔
749

750
void PacketHandler::emitNSEC3(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const NSEC3PARAMRecordContent& ns3prc, const DNSName& name, const string& namehash, const string& nexthash, int mode) // NOLINT(readability-identifier-length)
751
{
53,139✔
752
  NSEC3RecordContent n3rc;
53,139✔
753
  n3rc.d_algorithm = ns3prc.d_algorithm;
53,139✔
754
  n3rc.d_flags = ns3prc.d_flags;
53,139✔
755
  n3rc.d_iterations = ns3prc.d_iterations;
53,139✔
756
  n3rc.d_salt = ns3prc.d_salt;
53,139✔
757
  n3rc.d_nexthash = nexthash;
53,139✔
758

759
  DNSZoneRecord rr;
53,139✔
760

761
  if(!name.empty()) {
53,139✔
762
    if (d_sd.qname() == name) {
46,671✔
763
      n3rc.set(QType::SOA); // 1dfd8ad SOA can live outside the records table
14,624✔
764
      n3rc.set(QType::NSEC3PARAM);
14,624✔
765
      if(!isPresigned()) {
14,624✔
766
        auto keyset = d_dk.getKeys(d_sd.zonename);
12,389✔
767
        for(const auto& value: keyset) {
12,389✔
768
          if (value.second.published) {
12,388✔
769
            n3rc.set(QType::DNSKEY);
12,292✔
770
            string publishCDNSKEY;
12,292✔
771
            d_dk.getPublishCDNSKEY(d_sd.zonename, publishCDNSKEY);
12,292✔
772
            if (! publishCDNSKEY.empty())
12,292✔
773
              n3rc.set(QType::CDNSKEY);
576✔
774
            string publishCDS;
12,292✔
775
            d_dk.getPublishCDS(d_sd.zonename, publishCDS);
12,292✔
776
            if (! publishCDS.empty())
12,292✔
777
              n3rc.set(QType::CDS);
576✔
778
            break;
12,292✔
779
          }
12,292✔
780
        }
12,388✔
781
      }
12,389✔
782
    } else if(mode == 6) {
32,047!
783
      if (p.qtype.getCode() != QType::CDS) {
×
784
        n3rc.set(QType::CDS);
×
785
      }
×
786
      if (p.qtype.getCode() != QType::CDNSKEY) {
×
787
        n3rc.set(QType::CDNSKEY);
×
788
      }
×
789
    }
×
790

791
#ifdef HAVE_LUA_RECORDS
46,671✔
792
    bool first{true};
46,671✔
793
    bool doLua{false};
46,671✔
794
#endif
46,671✔
795

796
    B.lookup(QType(QType::ANY), name, d_sd.domain_id);
46,671✔
797
    while(B.get(rr)) {
168,208✔
798
#ifdef HAVE_LUA_RECORDS
121,537✔
799
      if (rr.dr.d_type == QType::LUA && first && !isPresigned()) {
121,537!
800
        first = false;
×
801
        doLua = doLuaRecords();
×
802
      }
×
803

804
      if (rr.dr.d_type == QType::LUA && doLua) {
121,537!
805
        n3rc.set(getRR<LUARecordContent>(rr.dr)->d_type);
×
806
      }
×
807
      else
121,537✔
808
#endif
121,537✔
809
      if (d_doExpandALIAS && rr.dr.d_type == QType::ALIAS) {
121,537✔
810
        // Set the A and AAAA in the NSEC3 bitmap so aggressive NSEC
811
        // does not falsely deny the type for this name.
812
        // This does NOT add the ALIAS to the bitmap, as that record cannot
813
        // be requested.
814
        if (!isPresigned()) {
105!
815
          n3rc.set(QType::A);
105✔
816
          n3rc.set(QType::AAAA);
105✔
817
        }
105✔
818
      }
105✔
819
      else if((rr.dr.d_type == QType::DNSKEY || rr.dr.d_type == QType::CDS || rr.dr.d_type == QType::CDNSKEY) && !isPresigned() && !::arg().mustDo("direct-dnskey")) {
121,432!
820
        continue;
×
821
      }
×
822
      else if(rr.dr.d_type && (rr.dr.d_type == QType::NS || rr.auth)) {
121,432✔
823
          // skip empty non-terminals
824
          n3rc.set(rr.dr.d_type);
112,862✔
825
      }
112,862✔
826
    }
121,537✔
827
  }
46,671✔
828

829
  const auto numberOfTypesSet = n3rc.numberOfTypesSet();
53,139✔
830
  if (numberOfTypesSet != 0 && !(numberOfTypesSet == 1 && n3rc.isSet(QType::NS))) {
53,139✔
831
    n3rc.set(QType::RRSIG);
36,732✔
832
  }
36,732✔
833

834
  rr.dr.d_name = DNSName(toBase32Hex(namehash))+d_sd.qname();
53,139✔
835
  rr.dr.d_ttl = d_sd.getNegativeTTL();
53,139✔
836
  rr.dr.d_type=QType::NSEC3;
53,139✔
837
  rr.dr.setContent(std::make_shared<NSEC3RecordContent>(std::move(n3rc)));
53,139✔
838
  rr.dr.d_place = (mode == 5 ) ? DNSResourceRecord::ANSWER: DNSResourceRecord::AUTHORITY;
53,139!
839
  rr.auth = true;
53,139✔
840

841
  r->addRecord(std::move(rr));
53,139✔
842
}
53,139✔
843

844
/*
845
   mode 0 = No Data Responses, QTYPE is not DS
846
   mode 1 = No Data Responses, QTYPE is DS
847
   mode 2 = Wildcard No Data Responses
848
   mode 3 = Wildcard Answer Responses
849
   mode 4 = Name Error Responses
850
   mode 5 = Direct NSEC request
851
   mode 6 = Authenticated DNSSEC bootstrapping (RFC 9615)
852
*/
853
void PacketHandler::addNSECX(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const DNSName& target, const DNSName& wildcard, int mode)
854
{
50,569✔
855
  NSEC3PARAMRecordContent ns3rc;
50,569✔
856
  bool narrow = false;
50,569✔
857
  if(d_dk.getNSEC3PARAM(d_sd.zonename, &ns3rc, &narrow))  {
50,569✔
858
    if (mode != 5) // no direct NSEC3 queries, rfc5155 7.2.8
33,425✔
859
      addNSEC3(p, r, target, wildcard, ns3rc, narrow, mode);
30,734✔
860
  }
33,425✔
861
  else {
17,144✔
862
    addNSEC(p, r, target, wildcard, mode);
17,144✔
863
  }
17,144✔
864
}
50,569✔
865

866
bool PacketHandler::getNSEC3Hashes(bool narrow, const std::string& hashed, bool decrement, DNSName& unhashed, std::string& before, std::string& after, int mode)
867
{
54,171✔
868
  bool ret;
54,171✔
869
  if(narrow) { // nsec3-narrow
54,171✔
870
    ret=true;
14,123✔
871
    before=hashed;
14,123✔
872
    if(decrement) {
14,123✔
873
      decrementHash(before);
6,468✔
874
      unhashed.clear();
6,468✔
875
    }
6,468✔
876
    after=hashed;
14,123✔
877
    incrementHash(after);
14,123✔
878
  }
14,123✔
879
  else {
40,048✔
880
    DNSName hashedName = DNSName(toBase32Hex(hashed));
40,048✔
881
    DNSName beforeName, afterName;
40,048✔
882
    if (!decrement && mode >= 2)
40,048✔
883
      beforeName = hashedName;
7,736✔
884
    ret=d_sd.db->getBeforeAndAfterNamesAbsolute(d_sd.domain_id, hashedName, unhashed, beforeName, afterName);
40,048✔
885
    before=fromBase32Hex(beforeName.toString());
40,048✔
886
    after=fromBase32Hex(afterName.toString());
40,048✔
887
  }
40,048✔
888
  return ret;
54,171✔
889
}
54,171✔
890

891
void PacketHandler::addNSEC3(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const DNSName& target, const DNSName& wildcard, const NSEC3PARAMRecordContent& ns3rc, bool narrow, int mode)
892
{
30,734✔
893
  DLOG(g_log<<"addNSEC3() mode="<<mode<<" auth="<<d_sd.qname()<<" target="<<target<<" wildcard="<<wildcard<<endl);
30,734✔
894

895
  if (d_sd.db == nullptr) {
30,734✔
896
    if(!B.getSOAUncached(d_sd.zonename, d_sd)) {
26!
897
      DLOG(g_log<<"Could not get SOA for domain"<<endl);
×
898
      return;
×
899
    }
×
900
  }
26✔
901

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

912
  bool doNextcloser = false;
30,734✔
913
  string before, after, hashed;
30,734✔
914
  DNSName unhashed, closest;
30,734✔
915

916
  if (mode == 2 || mode == 3 || mode == 4) {
30,734✔
917
    closest=wildcard;
14,200✔
918
    closest.chopOff();
14,200✔
919
  } else
14,200✔
920
    closest=target;
16,534✔
921

922
  // add matching NSEC3 RR
923
  if (mode != 3) {
30,734✔
924
    unhashed=(mode == 0 || mode == 1 || mode == 5) ? target : closest;
27,220!
925
    hashed=hashQNameWithSalt(ns3rc, unhashed);
27,220✔
926
    DLOG(g_log<<"1 hash: "<<toBase32Hex(hashed)<<" "<<unhashed<<endl);
27,220✔
927

928
    getNSEC3Hashes(narrow, hashed, false, unhashed, before, after, mode);
27,220✔
929

930
    if (((mode == 0 && ns3rc.d_flags) ||  mode == 1) && (hashed != before)) {
27,220✔
931
      DLOG(g_log<<"No matching NSEC3, do closest (provable) encloser"<<endl);
1,033✔
932

933
      bool doBreak = false;
1,033✔
934
      DNSZoneRecord rr;
1,033✔
935
      while( closest.chopOff() && (closest != d_sd.qname()))  { // stop at SOA
1,112!
936
        B.lookup(QType(QType::ANY), closest, d_sd.domain_id, &p);
295✔
937
        while (B.get(rr)) {
374✔
938
          if (rr.auth) {
295✔
939
            B.lookupEnd();
216✔
940
            doBreak = true;
216✔
941
            break;
216✔
942
          }
216✔
943
        }
295✔
944
        if(doBreak)
295✔
945
          break;
216✔
946
      }
295✔
947
      doNextcloser = true;
1,033✔
948
      unhashed=closest;
1,033✔
949
      hashed=hashQNameWithSalt(ns3rc, unhashed);
1,033✔
950
      DLOG(g_log<<"1 hash: "<<toBase32Hex(hashed)<<" "<<unhashed<<endl);
1,033✔
951

952
      getNSEC3Hashes(narrow, hashed, false, unhashed, before, after);
1,033✔
953
    }
1,033✔
954

955
    if (!after.empty()) {
27,220!
956
      DLOG(g_log<<"Done calling for matching, hashed: '"<<toBase32Hex(hashed)<<"' before='"<<toBase32Hex(before)<<"', after='"<<toBase32Hex(after)<<"'"<<endl);
27,220✔
957
      emitNSEC3(p, r, ns3rc, unhashed, before, after, mode);
27,220✔
958
    }
27,220✔
959
  }
27,220✔
960

961
  // add covering NSEC3 RR
962
  if ((mode >= 2 && mode <= 4) || doNextcloser) {
30,734!
963
    DNSName next(target);
15,233✔
964
    do {
21,076✔
965
      unhashed=next;
21,076✔
966
    }
21,076✔
967
    while( next.chopOff() && !(next==closest));
21,076!
968

969
    hashed=hashQNameWithSalt(ns3rc, unhashed);
15,233✔
970
    DLOG(g_log<<"2 hash: "<<toBase32Hex(hashed)<<" "<<unhashed<<endl);
15,233✔
971

972
    getNSEC3Hashes(narrow, hashed, true, unhashed, before, after);
15,233✔
973
    DLOG(g_log<<"Done calling for covering, hashed: '"<<toBase32Hex(hashed)<<"' before='"<<toBase32Hex(before)<<"', after='"<<toBase32Hex(after)<<"'"<<endl);
15,233✔
974
    emitNSEC3(p, r, ns3rc, unhashed, before, after, mode);
15,233✔
975
  }
15,233✔
976

977
  // wildcard denial
978
  if (mode == 2 || mode == 4) {
30,734✔
979
    unhashed=g_wildcarddnsname+closest;
10,686✔
980

981
    hashed=hashQNameWithSalt(ns3rc, unhashed);
10,686✔
982
    DLOG(g_log<<"3 hash: "<<toBase32Hex(hashed)<<" "<<unhashed<<endl);
10,686✔
983

984
    getNSEC3Hashes(narrow, hashed, (mode != 2), unhashed, before, after);
10,686✔
985
    DLOG(g_log<<"Done calling for '*', hashed: '"<<toBase32Hex(hashed)<<"' before='"<<toBase32Hex(before)<<"', after='"<<toBase32Hex(after)<<"'"<<endl);
10,686✔
986
    emitNSEC3(p, r, ns3rc, unhashed, before, after, mode);
10,686✔
987
  }
10,686✔
988
}
30,734✔
989

990
void PacketHandler::addNSEC(DNSPacket& /* p */, std::unique_ptr<DNSPacket>& r, const DNSName& target, const DNSName& wildcard, int mode)
991
{
17,395✔
992
  DLOG(g_log<<"addNSEC() mode="<<mode<<" auth="<<d_sd.qname()<<" target="<<target<<" wildcard="<<wildcard<<endl);
17,395✔
993

994
  if (d_sd.db == nullptr) {
17,395✔
995
    if(!B.getSOAUncached(d_sd.zonename, d_sd)) {
20!
996
      DLOG(g_log<<"Could not get SOA for domain"<<endl);
×
997
      return;
×
998
    }
×
999
  }
20✔
1000

1001
  if (!d_sd.db->doesDNSSEC()) {
17,395!
1002
    // We are in a configuration where the zone is primarily served by a
1003
    // non-DNSSEC-capable backend, but DNSSEC keys have been added to the
1004
    // zone in a second, DNSSEC-capable backend, which caused d_dnssec to
1005
    // be set to true. While it would be nice to support such a zone
1006
    // configuration, we don't. Log a warning and skip DNSSEC processing.
1007
    g_log << Logger::Notice << "Backend for zone '" << d_sd.qname() << "' does not support DNSSEC operation, not adding NSEC records" << endl;
×
1008
    return;
×
1009
  }
×
1010

1011
  DNSName before,after;
17,395✔
1012
  d_sd.db->getBeforeAndAfterNames(d_sd.domain_id, d_sd.zonename, target, before, after);
17,395✔
1013
  if (mode != 5 || before == target)
17,395✔
1014
    emitNSEC(r, before, after, mode);
17,276✔
1015

1016
  if (mode == 2 || mode == 4) {
17,395✔
1017
    // wildcard NO-DATA or wildcard denial
1018
    before.clear();
5,503✔
1019
    DNSName closest(wildcard);
5,503✔
1020
    if (mode == 4) {
5,503✔
1021
      closest.chopOff();
4,979✔
1022
      closest.prependRawLabel("*");
4,979✔
1023
    }
4,979✔
1024
    d_sd.db->getBeforeAndAfterNames(d_sd.domain_id, d_sd.zonename, closest, before, after);
5,503✔
1025
    emitNSEC(r, before, after, mode);
5,503✔
1026
  }
5,503✔
1027
  return;
17,395✔
1028
}
17,395✔
1029

1030
/* Semantics:
1031

1032
- only one backend owns the SOA of a zone
1033
- only one AXFR per zone at a time - double startTransaction should fail
1034
- backends implement transaction semantics
1035

1036
How BindBackend implements this:
1037
   startTransaction makes a file
1038
   feedRecord sends everything to that file
1039
   commitTransaction moves that file atomically over the regular file, and triggers a reload
1040
   abortTransaction removes the file
1041

1042
How SQL backends implement this:
1043
   startTransaction starts a sql transaction, which also deletes all records if requested
1044
   feedRecord is an insert statement
1045
   commitTransaction commits the transaction
1046
   abortTransaction aborts it
1047

1048
*/
1049

1050
int PacketHandler::tryAutoPrimary(const DNSPacket& p, const DNSName& tsigkeyname)
1051
{
4✔
1052
  if(p.d_tcp)
4!
1053
  {
×
1054
    // do it right now if the client is TCP
1055
    // rarely happens
1056
    return tryAutoPrimarySynchronous(p, tsigkeyname);
×
1057
  }
×
1058
  else
4✔
1059
  {
4✔
1060
    // queue it if the client is on UDP
1061
    Communicator.addTryAutoPrimaryRequest(p);
4✔
1062
    return 0;
4✔
1063
  }
4✔
1064
}
4✔
1065

1066
int PacketHandler::tryAutoPrimarySynchronous(const DNSPacket& p, const DNSName& tsigkeyname)
1067
{
4✔
1068
  ComboAddress remote = p.getInnerRemote();
4✔
1069
  if(p.hasEDNSSubnet() && pdns::isAddressTrustedNotificationProxy(remote)) {
4!
1070
    remote = p.getRealRemote().getNetwork();
×
1071
  }
×
1072
  else {
4✔
1073
    remote = p.getInnerRemote();
4✔
1074
  }
4✔
1075
  remote.setPort(53);
4✔
1076

1077
  Resolver::res_t nsset;
4✔
1078
  try {
4✔
1079
    Resolver resolver;
4✔
1080
    uint32_t theirserial;
4✔
1081
    resolver.getSoaSerial(remote, p.qdomain, &theirserial);
4✔
1082
    resolver.resolve(remote, p.qdomain, QType::NS, &nsset);
4✔
1083
  }
4✔
1084
  catch(ResolverException &re) {
4✔
1085
    g_log<<Logger::Error<<"Error resolving SOA or NS for "<<p.qdomain<<" at: "<< remote <<": "<<re.reason<<endl;
×
1086
    return RCode::ServFail;
×
1087
  }
×
1088

1089
  // check if the returned records are NS records
1090
  bool haveNS=false;
4✔
1091
  for(const auto& ns: nsset) {
8✔
1092
    if(ns.qtype==QType::NS)
8!
1093
      haveNS=true;
8✔
1094
  }
8✔
1095

1096
  if(!haveNS) {
4!
1097
    g_log << Logger::Error << "While checking for autoprimary, did not find NS for " << p.qdomain << " at: " << remote << endl;
×
1098
    return RCode::ServFail;
×
1099
  }
×
1100

1101
  string nameserver, account;
4✔
1102
  DNSBackend *db;
4✔
1103

1104
  if (!::arg().mustDo("allow-unsigned-autoprimary") && tsigkeyname.empty()) {
4!
1105
    g_log << Logger::Error << "Received unsigned NOTIFY for " << p.qdomain << " from potential autoprimary " << remote << ". Refusing." << endl;
×
1106
    return RCode::Refused;
×
1107
  }
×
1108

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

1140
int PacketHandler::processNotify(const DNSPacket& p)
1141
{
7✔
1142
  ZoneName zonename(p.qdomain);
7✔
1143
  /* now what?
1144
     was this notification from an approved address?
1145
     was this notification approved by TSIG?
1146
     We determine our internal SOA id (via UeberBackend)
1147
     We determine the SOA at our (known) primary
1148
     if primary is higher -> do stuff
1149
  */
1150

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

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

1158
  // Sender verification
1159
  //
1160
  if(!s_allowNotifyFrom.match(p.getInnerRemote()) || p.d_havetsig) {
7!
1161
    if (p.d_havetsig && p.getTSIGKeyname().empty() == false) {
3!
1162
        g_log<<Logger::Notice<<"Received secure NOTIFY for "<<zonename<<" from "<<p.getRemoteString()<<", with TSIG key '"<<p.getTSIGKeyname()<<"'"<<endl;
3✔
1163
    } else {
3✔
1164
      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;
×
1165
      return RCode::Refused;
×
1166
    }
×
1167
  }
3✔
1168

1169
  if ((!::arg().mustDo("allow-unsigned-notify") && !p.d_havetsig) || p.d_havetsig) {
7✔
1170
    if (!p.d_havetsig) {
4✔
1171
      g_log<<Logger::Warning<<"Received unsigned NOTIFY for "<<zonename<<" from "<<p.getRemoteString()<<" while a TSIG key was required (Refused)"<<endl;
1✔
1172
      return RCode::Refused;
1✔
1173
    }
1✔
1174
    vector<string> meta;
3✔
1175
    if (B.getDomainMetadata(zonename,"AXFR-MASTER-TSIG",meta) && !meta.empty()) {
3!
1176
      DNSName expected{meta[0]};
1✔
1177
      if (p.getTSIGKeyname() != expected) {
1!
1178
        g_log<<Logger::Warning<<"Received secure NOTIFY for "<<zonename<<" from "<<p.getRemoteString()<<": expected TSIG key '"<<expected<<"', got '"<<p.getTSIGKeyname()<<"' (Refused)"<<endl;
×
1179
        return RCode::Refused;
×
1180
      }
×
1181
    }
1✔
1182
  }
3✔
1183

1184
  // Domain verification
1185
  //
1186
  DomainInfo di;
6✔
1187
  if(!B.getDomainInfo(zonename, di, false) || di.backend == nullptr) {
6!
1188
    if(::arg().mustDo("autosecondary")) {
4!
1189
      g_log << Logger::Warning << "Received NOTIFY for " << zonename << " from " << p.getRemoteString() << " for which we are not authoritative, trying autoprimary" << endl;
4✔
1190
      return tryAutoPrimary(p, p.getTSIGKeyname());
4✔
1191
    }
4✔
1192
    g_log<<Logger::Notice<<"Received NOTIFY for "<<zonename<<" from "<<p.getRemoteString()<<" for which we are not authoritative (Refused)"<<endl;
×
1193
    return RCode::Refused;
×
1194
  }
4✔
1195

1196
  if(pdns::isAddressTrustedNotificationProxy(p.getInnerRemote())) {
2!
1197
    if (di.primaries.empty()) {
×
1198
      g_log << Logger::Warning << "Received NOTIFY for " << zonename << " from trusted-notification-proxy " << p.getRemoteString() << ", zone does not have any primaries defined (Refused)" << endl;
×
1199
      return RCode::Refused;
×
1200
    }
×
1201
    g_log<<Logger::Notice<<"Received NOTIFY for "<<zonename<<" from trusted-notification-proxy "<<p.getRemoteString()<<endl;
×
1202
  }
×
1203
  else if (::arg().mustDo("primary") && di.isPrimaryType()) {
2!
1204
    g_log << Logger::Warning << "Received NOTIFY for " << zonename << " from " << p.getRemoteString() << " but we are primary (Refused)" << endl;
×
1205
    return RCode::Refused;
×
1206
  }
×
1207
  else if (!di.isPrimary(p.getInnerRemote())) {
2!
1208
    g_log << Logger::Warning << "Received NOTIFY for " << zonename << " from " << p.getRemoteString() << " which is not a primary (Refused)" << endl;
×
1209
    return RCode::Refused;
×
1210
  }
×
1211

1212
  if(!s_forwardNotify.empty()) {
2!
1213
    set<string> forwardNotify(s_forwardNotify);
×
1214
    for(const auto & j : forwardNotify) {
×
1215
      g_log<<Logger::Notice<<"Relaying notification of domain "<<zonename<<" from "<<p.getRemoteString()<<" to "<<j<<endl;
×
1216
      Communicator.notify(zonename,j);
×
1217
    }
×
1218
  }
×
1219

1220
  if(::arg().mustDo("secondary")) {
2!
1221
    g_log<<Logger::Notice<<"Received NOTIFY for "<<zonename<<" from "<<p.getRemoteString()<<" - queueing check"<<endl;
2✔
1222
    di.receivedNotify = true;
2✔
1223
    Communicator.addSecondaryCheckRequest(di, p.getInnerRemote());
2✔
1224
  }
2✔
1225
  return 0;
2✔
1226
}
2✔
1227

1228
static bool validDNSName(const DNSName& name)
1229
{
129,014✔
1230
  if (!g_8bitDNS) {
129,019✔
1231
    return name.has8bitBytes() == false;
129,018✔
1232
  }
129,018✔
1233
  return true;
8,589,934,588✔
1234
}
129,014✔
1235

1236
std::unique_ptr<DNSPacket> PacketHandler::question(DNSPacket& p)
1237
{
111,479✔
1238
  std::unique_ptr<DNSPacket> ret{nullptr};
111,479✔
1239

1240
  if(d_pdl)
111,479✔
1241
  {
92,645✔
1242
    ret=d_pdl->prequery(p);
92,645✔
1243
    if(ret)
92,645!
1244
      return ret;
×
1245
  }
92,645✔
1246

1247
  if(p.d.rd) {
111,479!
1248
    static AtomicCounter &rdqueries=*S.getPointer("rd-queries");
×
1249
    rdqueries++;
×
1250
  }
×
1251

1252
  return doQuestion(p);
111,479✔
1253
}
111,479✔
1254

1255

1256
void PacketHandler::makeNXDomain(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const DNSName& target, const DNSName& wildcard)
1257
{
18,270✔
1258
  DNSZoneRecord rr;
18,270✔
1259
  rr=makeEditedDNSZRFromSOAData(d_dk, d_sd, DNSResourceRecord::AUTHORITY);
18,270✔
1260
  rr.dr.d_ttl=d_sd.getNegativeTTL();
18,270✔
1261
  r->addRecord(std::move(rr));
18,270✔
1262

1263
  if(d_dnssec) {
18,270✔
1264
    addNSECX(p, r, target, wildcard, 4);
14,567✔
1265
  }
14,567✔
1266

1267
  r->setRcode(RCode::NXDomain);
18,270✔
1268
}
18,270✔
1269

1270
void PacketHandler::makeNOError(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const DNSName& target, const DNSName& wildcard, int mode)
1271
{
28,298✔
1272
  DNSZoneRecord rr;
28,298✔
1273
  rr=makeEditedDNSZRFromSOAData(d_dk, d_sd, DNSResourceRecord::AUTHORITY);
28,298✔
1274
  rr.dr.d_ttl=d_sd.getNegativeTTL();
28,298✔
1275
  r->addRecord(std::move(rr));
28,298✔
1276

1277
  if(d_dnssec) {
28,298✔
1278
    addNSECX(p, r, target, wildcard, mode);
23,862✔
1279
  }
23,862✔
1280

1281
  S.inc("noerror-packets");
28,298✔
1282
  S.ringAccount("noerror-queries", p.qdomain, p.qtype);
28,298✔
1283
}
28,298✔
1284

1285

1286
bool PacketHandler::addDSforNS(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const DNSName& dsname)
1287
{
5,073✔
1288
  //cerr<<"Trying to find a DS for '"<<dsname<<"', domain_id = "<<d_sd.domain_id<<endl;
1289
  B.lookup(QType(QType::DS), dsname, d_sd.domain_id, &p);
5,073✔
1290
  DNSZoneRecord rr;
5,073✔
1291
  bool gotOne=false;
5,073✔
1292
  while(B.get(rr)) {
6,707✔
1293
    gotOne=true;
1,634✔
1294
    rr.dr.d_place = DNSResourceRecord::AUTHORITY;
1,634✔
1295
    r->addRecord(std::move(rr));
1,634✔
1296
  }
1,634✔
1297
  return gotOne;
5,073✔
1298
}
5,073✔
1299

1300
bool PacketHandler::tryReferral(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const DNSName &target, bool retargeted)
1301
{
93,823✔
1302
  vector<DNSZoneRecord> rrset = getBestReferralNS(p, target);
93,823✔
1303
  if(rrset.empty())
93,823✔
1304
    return false;
87,747✔
1305

1306
  DNSName name = rrset.begin()->dr.d_name;
6,076✔
1307
  for(auto& rr: rrset) {
8,810✔
1308
    rr.dr.d_place=DNSResourceRecord::AUTHORITY;
8,810✔
1309
    r->addRecord(std::move(rr));
8,810✔
1310
  }
8,810✔
1311
  if(!retargeted)
6,076✔
1312
    r->setA(false);
5,808✔
1313

1314
  if(isSecuredZone() && !addDSforNS(p, r, name) && d_dnssec) {
6,076✔
1315
    addNSECX(p, r, name, DNSName(), 1);
2,909✔
1316
  }
2,909✔
1317

1318
  return true;
6,076✔
1319
}
93,823✔
1320

1321
void PacketHandler::completeANYRecords(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const DNSName &target)
1322
{
4,043✔
1323
  addNSECX(p, r, target, DNSName(), 5);
4,043✔
1324
  if(d_sd.qname() == p.qdomain) {
4,043✔
1325
    if(!isPresigned()) {
1,521✔
1326
      addDNSKEY(p, r);
1,196✔
1327
      addCDNSKEY(p, r);
1,196✔
1328
      addCDS(p, r);
1,196✔
1329
    }
1,196✔
1330
    addNSEC3PARAM(p, r);
1,521✔
1331
  }
1,521✔
1332
}
4,043✔
1333

1334
bool PacketHandler::tryAuthSignal(DNSPacket& p, std::unique_ptr<DNSPacket>& r, DNSName &target) // NOLINT(readability-identifier-length)
1335
{
18,521✔
1336
  DLOG(g_log<<Logger::Warning<<"Let's try authenticated DNSSEC bootstrapping (RFC 9615) ..."<<endl);
18,521✔
1337
  if(!d_sd.zonename.operator const DNSName&().hasLabels() || !pdns_iequals(d_sd.zonename.operator const DNSName&().getRawLabel(0), "_signal") || !d_dk.isSignalingZone(d_sd.zonename)) {
18,526!
1338
    return false;
18,523✔
1339
  }
18,523✔
1340

1341
  // Check that we're doing online signing in narrow mode (as we don't know next owner names)
1342
  if(!isSecuredZone() || isPresigned()) {
6,442,450,943!
1343
    g_log << Logger::Warning << "Signaling zone '" << d_sd.zonename << "' must be secured (but not presigned!); synthesis disabled (" << target << "/" << p.qtype << " from " << p.getRemoteString() << ")" << endl;
×
1344
    return false;
×
1345
  }
×
1346
  bool narrow{false};
6,442,450,943✔
1347
  if (!d_dk.getNSEC3PARAM(d_sd.zonename, nullptr, &narrow) || !narrow) {
6,442,450,943!
1348
    g_log << Logger::Warning << "Signaling zone '" << d_sd.zonename << "' must use NSEC3 narrow; synthesis disabled (" << target << "/" << p.qtype << ")" << " from " << p.getRemoteString() << ")" << endl;
×
1349
    return false;
×
1350
  }
×
1351

1352
  // Check for prefix mismatch
1353
  if(!target.hasLabels() || !pdns_iequals(target.getRawLabel(0), "_dsboot")) {
6,442,450,943!
1354
    makeNOError(p, r, target, DNSName(), 0); // could be ENT
×
1355
    return true;
×
1356
  }
×
1357

1358
  // Check for qtype match
1359
  if(!(p.qtype.getCode() == QType::CDS || p.qtype.getCode() == QType::CDNSKEY)) {
6,442,450,943!
1360
    // We don't know at this point whether CDS/CDNSKEY exist, so let's add both to DoE type map
1361
    makeNOError(p, r, target, DNSName(), 6);
×
1362
    return true;
×
1363
  }
×
1364

1365
  // Extract target zone name and fetch zone
1366
  SOAData zone_sd;
6,442,450,943✔
1367
  ZoneName zone = ZoneName(target.makeRelative(d_sd.zonename));
6,442,450,943✔
1368
  zone.chopOff();
6,442,450,943✔
1369
  // Zone must exist, but need not be secured (could be using front-sign setup)
1370
  if(!B.getAuth(zone, p.qtype, &zone_sd, p.getRealRemote()) || zone_sd.zonename != zone) {
6,442,450,943!
1371
    return false; // Bootstrap target zone unknown, NXDOMAIN at _dsboot is OK
×
1372
  }
×
1373

1374
  // Insert synthetic response
1375
  bool haveOne = false;
6,442,450,943✔
1376
  bool autoPublish = !d_dk.isPresigned(zone);
6,442,450,943✔
1377
  std::string val;
6,442,450,943✔
1378
  if(p.qtype.getCode() == QType::CDS) {
6,442,450,943!
1379
    d_dk.getPublishCDS(zone, val);
×
1380
    autoPublish &= !val.empty();
×
1381
    if(autoPublish) {
×
1382
      haveOne = addCDS(p, r, zone_sd);
×
1383
    }
×
1384
  } else if(p.qtype.getCode() == QType::CDNSKEY) {
6,442,450,943!
1385
    d_dk.getPublishCDNSKEY(zone, val);
×
1386
    autoPublish &= !val.empty();
×
1387
    if(autoPublish) {
×
1388
      haveOne = addCDNSKEY(p, r, zone_sd);
×
1389
    }
×
1390
  }
×
1391
  if(!autoPublish) {
6,442,450,943!
1392
      DNSZoneRecord rec;
×
1393
      B.lookup(p.qtype.getCode(), DNSName(zone), zone_sd.domain_id, &p);
×
1394
      while(B.get(rec)) {
×
1395
        rec.dr.d_name = p.qdomain;
×
1396
        r->addRecord(std::move(rec));
×
1397
        haveOne=true;
×
1398
      }
×
1399
    }
×
1400
  if(!haveOne) {
6,442,450,943!
1401
    makeNOError(p, r, target, DNSName(), 6); // other type might exist
×
1402
  }
×
1403
  return true;
6,442,450,943✔
1404
}
6,442,450,943✔
1405

1406
bool PacketHandler::tryDNAME(DNSPacket& p, std::unique_ptr<DNSPacket>& r, DNSName &target)
1407
{
19,852✔
1408
  if(!d_doDNAME)
19,852✔
1409
    return false;
6✔
1410
  DLOG(g_log<<Logger::Warning<<"Let's try DNAME.."<<endl);
19,846✔
1411
  vector<DNSZoneRecord> rrset;
19,846✔
1412
  try {
19,846✔
1413
    getBestDNAMESynth(p, target, rrset);
19,846✔
1414
    if(!rrset.empty()) {
19,846✔
1415
      for (auto& record : rrset) {
1,902✔
1416
        record.dr.d_place = DNSResourceRecord::ANSWER;
1,902✔
1417
        r->addRecord(std::move(record));
1,902✔
1418
      }
1,902✔
1419
      return true;
951✔
1420
    }
951✔
1421
  } catch (const std::range_error &e) {
19,846✔
1422
    // Add the DNAME regardless, but throw to let the caller know we could not
1423
    // synthesize a CNAME
1424
    if(!rrset.empty()) {
380!
1425
      for (auto& record : rrset) {
380✔
1426
        record.dr.d_place = DNSResourceRecord::ANSWER;
380✔
1427
        r->addRecord(std::move(record));
380✔
1428
      }
380✔
1429
    }
380✔
1430
    throw e;
380✔
1431
  }
380✔
1432
  return false;
18,516✔
1433
}
19,846✔
1434
bool PacketHandler::tryWildcard(DNSPacket& p, std::unique_ptr<DNSPacket>& r, DNSName &target, DNSName &wildcard, bool& retargeted, bool& nodata)
1435
{
28,024✔
1436
  retargeted = nodata = false;
28,024✔
1437
  DNSName bestmatch;
28,024✔
1438

1439
  vector<DNSZoneRecord> rrset;
28,024✔
1440
  if(!getBestWildcard(p, target, wildcard, &rrset))
28,024✔
1441
    return false;
19,853✔
1442

1443
  if(rrset.empty()) {
8,171✔
1444
    DLOG(g_log<<"Wildcard matched something, but not of the correct type"<<endl);
1,907✔
1445
    nodata=true;
1,907✔
1446
  }
1,907✔
1447
  else {
6,264✔
1448
    bestmatch = target;
6,264✔
1449
    for(auto& rr: rrset) {
6,266✔
1450
      rr.wildcardname = rr.dr.d_name;
6,265✔
1451
      rr.dr.d_name = bestmatch;
6,265✔
1452

1453
      if(rr.dr.d_type == QType::CNAME)  {
6,265✔
1454
        retargeted=true;
2,579✔
1455
        target=getRR<CNAMERecordContent>(rr.dr)->getTarget();
2,579✔
1456
      }
2,579✔
1457

1458
      rr.dr.d_place=DNSResourceRecord::ANSWER;
6,265✔
1459
      r->addRecord(std::move(rr));
6,265✔
1460
    }
6,265✔
1461
  }
6,264✔
1462
  if(d_dnssec && !nodata) {
8,171✔
1463
    addNSECX(p, r, bestmatch, wildcard, 3);
5,189✔
1464
  }
5,189✔
1465

1466
  return true;
8,171✔
1467
}
28,024✔
1468

1469
//! Called by the Distributor to ask a question. Returns 0 in case of an error
1470
std::unique_ptr<DNSPacket> PacketHandler::doQuestion(DNSPacket& pkt)
1471
{
129,096✔
1472
  bool noCache=false;
129,096✔
1473

1474
  if(pkt.d.qr) { // QR bit from dns packet (thanks RA from N)
129,096!
1475
    if(d_logDNSDetails) {
×
1476
      g_log<<Logger::Error<<"Received an answer (non-query) packet from "<<pkt.getRemoteString()<<", dropping"<<endl;
×
1477
    }
×
1478
    S.inc("corrupt-packets");
×
1479
    S.ringAccount("remotes-corrupt", pkt.getInnerRemote());
×
1480
    return nullptr;
×
1481
  }
×
1482

1483
  if(pkt.d.tc) { // truncated query. MOADNSParser would silently parse this packet in an incomplete way.
129,096!
1484
    if(d_logDNSDetails) {
×
1485
      g_log<<Logger::Error<<"Received truncated query packet from "<<pkt.getRemoteString()<<", dropping"<<endl;
×
1486
    }
×
1487
    S.inc("corrupt-packets");
×
1488
    S.ringAccount("remotes-corrupt", pkt.getInnerRemote());
×
1489
    return nullptr;
×
1490
  }
×
1491

1492
  if (pkt.hasEDNS()) {
129,096✔
1493
    if(pkt.getEDNSVersion() > 0) {
118,243✔
1494
      auto resp = pkt.replyPacket();
1✔
1495
      // PacketWriter::addOpt will take care of setting this correctly in the packet
1496
      resp->setEDNSRcode(ERCode::BADVERS);
1✔
1497
      return resp;
1✔
1498
    }
1✔
1499
    if (pkt.hasEDNSCookie()) {
118,242!
1500
      if (!pkt.hasWellFormedEDNSCookie()) {
×
1501
        auto resp = pkt.replyPacket();
×
1502
        resp->setRcode(RCode::FormErr);
×
1503
        return resp;
×
1504
      }
×
1505
      if (!pkt.hasValidEDNSCookie() && !pkt.d_tcp) {
×
1506
        auto resp = pkt.replyPacket();
×
1507
        resp->setEDNSRcode(ERCode::BADCOOKIE);
×
1508
        return resp;
×
1509
      }
×
1510
    }
×
1511
  }
118,242✔
1512

1513
  if(pkt.d_havetsig) {
129,095✔
1514
    DNSName tsigkeyname;
15✔
1515
    string secret;
15✔
1516
    TSIGRecordContent trc;
15✔
1517
    if (!checkForCorrectTSIG(pkt, &tsigkeyname, &secret, &trc)) {
15!
1518
      auto resp=pkt.replyPacket();  // generate an empty reply packet
×
1519
      if(d_logDNSDetails) {
×
1520
        g_log<<Logger::Error<<"Received a TSIG signed message with a non-validating key"<<endl;
×
1521
      }
×
1522
      // RFC3007 describes that a non-secure message should be sending Refused for DNS Updates
1523
      if (pkt.d.opcode == Opcode::Update) {
×
1524
        resp->setRcode(RCode::Refused);
×
1525
      }
×
1526
      else {
×
1527
        resp->setRcode(RCode::NotAuth);
×
1528
      }
×
1529
      return resp;
×
1530
    }
×
1531
    getTSIGHashEnum(trc.d_algoName, pkt.d_tsig_algo);
15✔
1532
#ifdef ENABLE_GSS_TSIG
15✔
1533
    if (g_doGssTSIG && pkt.d_tsig_algo == TSIG_GSS) {
15!
1534
      GssContext gssctx(tsigkeyname);
×
1535
      if (!gssctx.getPeerPrincipal(pkt.d_peer_principal)) {
×
1536
        g_log<<Logger::Warning<<"Failed to extract peer principal from GSS context with keyname '"<<tsigkeyname<<"'"<<endl;
×
1537
      }
×
1538
    }
×
1539
#endif
15✔
1540
    pkt.setTSIGDetails(trc, tsigkeyname, secret, trc.d_mac); // this will get copied by replyPacket()
15✔
1541
    noCache=true;
15✔
1542
  }
15✔
1543

1544
  if (pkt.qtype == QType::TKEY) {
129,095✔
1545
    auto resp=pkt.replyPacket();  // generate an empty reply packet, possibly with TSIG details inside
72✔
1546
    this->tkeyHandler(pkt, resp);
72✔
1547
    return resp;
72✔
1548
  }
72✔
1549

1550
  try {
129,023✔
1551

1552
    // XXX FIXME do this in DNSPacket::parse ?
1553

1554
    if(!validDNSName(pkt.qdomain)) {
129,023!
1555
      if(d_logDNSDetails) {
×
1556
        g_log<<Logger::Error<<"Received a malformed qdomain from "<<pkt.getRemoteString()<<", '"<<pkt.qdomain<<"': sending servfail"<<endl;
×
1557
      }
×
1558
      S.inc("corrupt-packets");
×
1559
      S.ringAccount("remotes-corrupt", pkt.getInnerRemote());
×
1560
      S.inc("servfail-packets");
×
1561
      auto resp=pkt.replyPacket();  // generate an empty reply packet
×
1562
      resp->setRcode(RCode::ServFail);
×
1563
      return resp;
×
1564
    }
×
1565

1566
    using opcodeHandler = std::unique_ptr<DNSPacket> (PacketHandler::*)(DNSPacket&, bool);
129,023✔
1567
    const static std::array<opcodeHandler, 16> opcodeHandlers = {
129,023✔
1568
      &PacketHandler::opcodeQuery,
129,023✔
1569
      &PacketHandler::opcodeNotImplemented,
129,023✔
1570
      &PacketHandler::opcodeNotImplemented,
129,023✔
1571
      &PacketHandler::opcodeNotImplemented,
129,023✔
1572
      &PacketHandler::opcodeNotify,
129,023✔
1573
      &PacketHandler::opcodeUpdate,
129,023✔
1574
      &PacketHandler::opcodeNotImplemented,
129,023✔
1575
      &PacketHandler::opcodeNotImplemented,
129,023✔
1576

1577
      &PacketHandler::opcodeNotImplemented,
129,023✔
1578
      &PacketHandler::opcodeNotImplemented,
129,023✔
1579
      &PacketHandler::opcodeNotImplemented,
129,023✔
1580
      &PacketHandler::opcodeNotImplemented,
129,023✔
1581
      &PacketHandler::opcodeNotImplemented,
129,023✔
1582
      &PacketHandler::opcodeNotImplemented,
129,023✔
1583
      &PacketHandler::opcodeNotImplemented,
129,023✔
1584
      &PacketHandler::opcodeNotImplemented
129,023✔
1585
    };
129,023✔
1586

1587
    return (this->*(opcodeHandlers.at(pkt.d.opcode)))(pkt, noCache);
129,023✔
1588
  }
129,023✔
1589
  catch(const DBException &e) {
129,023✔
1590
    g_log<<Logger::Error<<"Backend reported condition which prevented lookup ("+e.reason+") sending out servfail"<<endl;
×
1591
    auto resp=pkt.replyPacket(); // generate an empty reply packet
×
1592
    resp->setRcode(RCode::ServFail);
×
1593
    S.inc("servfail-packets");
×
1594
    S.ringAccount("servfail-queries", pkt.qdomain, pkt.qtype);
×
1595
    return resp;
×
1596
  }
×
1597
  catch(const PDNSException &e) {
129,023✔
1598
    g_log<<Logger::Error<<"Backend reported permanent error which prevented lookup ("+e.reason+"), aborting"<<endl;
×
1599
    throw; // we WANT to die at this point
×
1600
  }
×
1601
  catch(const std::exception &e) {
129,023✔
1602
    g_log<<Logger::Error<<"Exception building answer packet for "<<pkt.qdomain<<"/"<<pkt.qtype.toString()<<" ("<<e.what()<<") sending out servfail"<<endl;
×
1603
    auto resp=pkt.replyPacket(); // generate an empty reply packet
×
1604
    resp->setRcode(RCode::ServFail);
×
1605
    S.inc("servfail-packets");
×
1606
    S.ringAccount("servfail-queries", pkt.qdomain, pkt.qtype);
×
1607
    return resp;
×
1608
  }
×
1609
}
129,023✔
1610

1611
bool PacketHandler::opcodeQueryInner(DNSPacket& pkt, queryState &state)
1612
{
126,718✔
1613
  state.r=pkt.replyPacket();  // generate an empty reply packet, possibly with TSIG details inside
126,718✔
1614

1615
#if 0
1616
  g_log<<Logger::Warning<<"Query for '"<<pkt.qdomain<<"' "<<pkt.qtype.toString()<<" from "<<pkt.getRemoteString()<< " (tcp="<<pkt.d_tcp<<")"<<endl;
1617
#endif
1618

1619
  if(pkt.qtype.getCode()==QType::IXFR) {
126,718!
1620
    state.r->setRcode(RCode::Refused);
×
1621
    return false;
×
1622
  }
×
1623

1624
  state.target=pkt.qdomain;
126,718✔
1625

1626
  // catch chaos qclass requests
1627
  if(pkt.qclass == QClass::CHAOS) {
126,718!
1628
    return doChaosRequest(pkt,state.r,state.target) != 0;
×
1629
  }
×
1630

1631
  // we only know about qclass IN (and ANY), send Refused for everything else.
1632
  if(pkt.qclass != QClass::IN && pkt.qclass!=QClass::ANY) {
126,718!
1633
    state.r->setRcode(RCode::Refused);
×
1634
    return false;
×
1635
  }
×
1636

1637
  // send TC for udp ANY query if any-to-tcp is enabled.
1638
  if(pkt.qtype.getCode() == QType::ANY && !pkt.d_tcp && g_anyToTcp) {
126,718✔
1639
    state.r->d.tc = 1;
1,716✔
1640
    state.r->commitD();
1,716✔
1641
    return false;
1,716✔
1642
  }
1,716✔
1643

1644
  // for qclass ANY the response should never be authoritative unless the response covers all classes.
1645
  if(pkt.qclass==QClass::ANY) {
125,002!
1646
    state.r->setA(false);
×
1647
  }
×
1648

1649
  int retargetcount{0};
125,002✔
1650
  while (true) {
137,272✔
1651
    state.retargeted = false;
137,266✔
1652
    bool result = opcodeQueryInner2(pkt, state, retargetcount != 0);
137,266✔
1653
    if (!state.retargeted) {
137,266✔
1654
      return result;
124,688✔
1655
    }
124,688✔
1656
    retargetcount++;
12,578✔
1657
    if (retargetcount > 10) {
12,578✔
1658
      g_log<<Logger::Warning<<"Abort CNAME chain resolution after "<<--retargetcount<<" redirects, sending out servfail. Initial query: '"<<pkt.qdomain<<"'"<<endl;
322✔
1659
      state.r=pkt.replyPacket();
322✔
1660
      state.r->setRcode(RCode::ServFail);
322✔
1661
      return false;
322✔
1662
    }
322✔
1663
  }
12,578✔
1664
}
125,002✔
1665

1666
// NOLINTNEXTLINE(readability-function-cognitive-complexity): TODO continue splitting this into smaller pieces
1667
bool PacketHandler::opcodeQueryInner2(DNSPacket& pkt, queryState &state, bool retargeted)
1668
{
137,263✔
1669
  DNSZoneRecord zrr;
137,263✔
1670

1671
  if (retargeted && !d_doResolveAcrossZones && !state.target.isPartOf(state.r->qdomainzone)) {
137,263!
1672
    // 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.
1673
    // This is a performance optimization, the generic case is checked after getAuth below.
1674
    return true;
×
1675
  }
×
1676

1677
  // Reset possibly dangling data associated to d_sd.
1678
  d_ispresigned.reset();
137,263✔
1679
  d_issecuredzone.reset();
137,263✔
1680

1681
  if(!B.getAuth(ZoneName(state.target), pkt.qtype, &d_sd, pkt.getRealRemote(), true, &pkt)) {
137,263✔
1682
    DLOG(g_log<<Logger::Error<<"We have no authority over zone '"<<state.target<<"'"<<endl);
2,813✔
1683
    if (!retargeted) {
2,813✔
1684
      state.r->setA(false); // drop AA if we never had a SOA in the first place
761✔
1685
      state.r->setRcode(RCode::Refused); // send REFUSED - but only on empty 'no idea'
761✔
1686
    }
761✔
1687
    return true;
2,813✔
1688
  }
2,813✔
1689
  DLOG(g_log<<Logger::Error<<"We have authority, zone='"<<d_sd.qname()<<"', id="<<d_sd.domain_id<<", zonename="<<d_sd.zonename<<endl);
134,450✔
1690

1691
  if (!retargeted) {
134,450✔
1692
    state.r->qdomainzone = d_sd.zonename;
124,242✔
1693
  } else if (!d_doResolveAcrossZones && state.r->qdomainzone.operator const DNSName&() != d_sd.qname()) {
124,242!
1694
    // We are following a retarget outside the initial zone. Config asked us not to do that.
1695
    return true;
×
1696
  }
×
1697

1698
  state.authSet.insert(d_sd.zonename);
134,450✔
1699
  d_dnssec=(pkt.d_dnssecOk && isSecuredZone());
134,450✔
1700
  state.doSigs |= d_dnssec;
134,450✔
1701

1702
  if(d_sd.qname()==pkt.qdomain) {
134,450✔
1703
    if(!isPresigned()) {
51,761✔
1704
      switch (pkt.qtype.getCode()) {
46,958✔
1705
      case QType::DNSKEY:
26,058✔
1706
        if(addDNSKEY(pkt, state.r)) {
26,058✔
1707
          return true;
25,489✔
1708
        }
25,489✔
1709
        break;
569✔
1710
      case QType::CDNSKEY:
641✔
1711
        if(addCDNSKEY(pkt,state.r)) {
540!
1712
          return true;
540✔
1713
        }
540✔
1714
        break;
×
1715
      case QType::CDS:
545✔
1716
        if(addCDS(pkt,state.r)) {
545✔
1717
          return true;
544✔
1718
        }
544✔
1719
        break;
1✔
1720
      }
46,958✔
1721
    }
46,958✔
1722
    if(pkt.qtype.getCode() == QType::NSEC3PARAM) {
25,187✔
1723
      if(addNSEC3PARAM(pkt,state.r)) {
1,177✔
1724
        return true;
620✔
1725
      }
620✔
1726
    }
1,177✔
1727
  }
25,187✔
1728

1729
  if(pkt.qtype.getCode() == QType::SOA && d_sd.qname()==pkt.qdomain) {
107,256✔
1730
    zrr=makeEditedDNSZRFromSOAData(d_dk, d_sd);
1,433✔
1731
    state.r->addRecord(std::move(zrr));
1,433✔
1732
    return true;
1,433✔
1733
  }
1,433✔
1734

1735
  // this TRUMPS a cname!
1736
  if(d_dnssec && pkt.qtype.getCode() == QType::NSEC && !d_dk.getNSEC3PARAM(d_sd.zonename, nullptr)) {
105,823✔
1737
    addNSEC(pkt, state.r, state.target, DNSName(), 5);
251✔
1738
    if (!state.r->isEmpty()) {
251✔
1739
      return true;
170✔
1740
    }
170✔
1741
  }
251✔
1742

1743
  // this TRUMPS a cname!
1744
  if(pkt.qtype.getCode() == QType::RRSIG) {
105,653✔
1745
    g_log<<Logger::Info<<"Direct RRSIG query for "<<state.target<<" from "<<pkt.getRemoteString()<<endl;
592✔
1746
    state.r->setRcode(RCode::Refused);
592✔
1747
    return true;
592✔
1748
  }
592✔
1749

1750
  DLOG(g_log<<"Checking for referrals first, unless this is a DS query"<<endl);
105,061✔
1751
  if(pkt.qtype.getCode() != QType::DS && tryReferral(pkt, state.r, state.target, retargeted)) {
105,061✔
1752
    return true;
4,952✔
1753
  }
4,952✔
1754

1755
  DLOG(g_log<<"Got no referrals, trying ANY"<<endl);
100,109✔
1756

1757
#ifdef HAVE_LUA_RECORDS
100,109✔
1758
  bool doLua = doLuaRecords();
100,109✔
1759
#endif
100,109✔
1760

1761
  // see what we get..
1762
  B.lookup(QType(QType::ANY), state.target, d_sd.domain_id, &pkt);
100,109✔
1763
  vector<DNSZoneRecord> rrset;
100,109✔
1764
  DNSName haveAlias;
100,109✔
1765
  uint8_t aliasScopeMask = 0;
100,109✔
1766
  bool weDone{false};
100,109✔
1767
  bool weRedirected{false};
100,109✔
1768
  bool weHaveUnauth{false};
100,109✔
1769

1770
  while(B.get(zrr)) {
341,875✔
1771
#ifdef HAVE_LUA_RECORDS
241,766✔
1772
    if (zrr.dr.d_type == QType::LUA && !isPresigned()) {
241,766!
1773
      if(!doLua) {
×
1774
        continue;
×
1775
      }
×
1776
      auto rec=getRR<LUARecordContent>(zrr.dr);
×
1777
      if (!rec) {
×
1778
        continue;
×
1779
      }
×
1780
      if(rec->d_type == QType::CNAME || rec->d_type == pkt.qtype.getCode() || (pkt.qtype.getCode() == QType::ANY && rec->d_type != QType::RRSIG)) {
×
1781
        state.noCache=true;
×
1782
        try {
×
1783
          auto recvec=luaSynth(rec->getCode(), state.target, zrr, d_sd.qname(), pkt, rec->d_type, s_LUA);
×
1784
          if(!recvec.empty()) {
×
1785
            for (const auto& r_it : recvec) {
×
1786
              zrr.dr.d_type = rec->d_type; // might be CNAME
×
1787
              zrr.dr.setContent(r_it);
×
1788
              zrr.scopeMask = pkt.getRealRemote().getBits(); // this makes sure answer is a specific as your question
×
1789
              rrset.push_back(zrr);
×
1790
            }
×
1791
            if(rec->d_type == QType::CNAME && pkt.qtype.getCode() != QType::CNAME) {
×
1792
              weRedirected = true;
×
1793
            }
×
1794
            else {
×
1795
              weDone = true;
×
1796
            }
×
1797
          }
×
1798
        }
×
1799
        catch(std::exception &e) {
×
1800
          B.lookupEnd();              // don't leave DB handle in bad state
×
1801

1802
          state.r=pkt.replyPacket();
×
1803
          state.r->setRcode(RCode::ServFail);
×
1804
          return false;
×
1805
        }
×
1806
      }
×
1807
    }
×
1808
#endif
241,766✔
1809
    //cerr<<"got content: ["<<zrr.content<<"]"<<endl;
1810
    if (!d_dnssec && pkt.qtype.getCode() == QType::ANY && (zrr.dr.d_type == QType:: DNSKEY || zrr.dr.d_type == QType::NSEC3PARAM)) {
241,766!
1811
      continue; // Don't send dnssec info.
×
1812
    }
×
1813
    if (zrr.dr.d_type == QType::RRSIG) { // RRSIGS are added later any way.
241,766✔
1814
      continue; // TODO: this actually means addRRSig should check if the RRSig is already there
38,605✔
1815
    }
38,605✔
1816

1817
    // cerr<<"Auth: "<<zrr.auth<<", "<<(zrr.dr.d_type == pkt.qtype)<<", "<<zrr.dr.d_type.toString()<<endl;
1818
    if((pkt.qtype.getCode() == QType::ANY || zrr.dr.d_type == pkt.qtype.getCode()) && zrr.auth) {
203,161✔
1819
      weDone=true;
77,236✔
1820
    }
77,236✔
1821
    // the line below fakes 'unauth NS' for delegations for non-DNSSEC backends.
1822
    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)))) {
203,161✔
1823
      weHaveUnauth=true;
3,849✔
1824
    }
3,849✔
1825

1826
    if(zrr.dr.d_type == QType::CNAME && pkt.qtype.getCode() != QType::CNAME) {
203,161✔
1827
      weRedirected=true;
9,052✔
1828
    }
9,052✔
1829

1830
    if (DP && zrr.dr.d_type == QType::ALIAS && (pkt.qtype.getCode() == QType::A || pkt.qtype.getCode() == QType::AAAA || pkt.qtype.getCode() == QType::ANY) && !isPresigned()) {
203,161!
1831
      if (!d_doExpandALIAS) {
334!
1832
        g_log<<Logger::Info<<"ALIAS record found for "<<state.target<<", but ALIAS expansion is disabled."<<endl;
×
1833
        continue;
×
1834
      }
×
1835
      haveAlias=getRR<ALIASRecordContent>(zrr.dr)->getContent();
334✔
1836
      aliasScopeMask=zrr.scopeMask;
334✔
1837
    }
334✔
1838

1839
    // Filter out all SOA's and add them in later
1840
    if(zrr.dr.d_type == QType::SOA) {
203,161✔
1841
      continue;
22,886✔
1842
    }
22,886✔
1843

1844
    rrset.push_back(zrr);
180,275✔
1845
  }
180,275✔
1846

1847
  /* Add in SOA if required */
1848
  if(state.target==d_sd.qname()) {
100,109✔
1849
      zrr=makeEditedDNSZRFromSOAData(d_dk, d_sd);
22,880✔
1850
      rrset.push_back(std::move(zrr));
22,880✔
1851
  }
22,880✔
1852

1853
  DLOG(g_log<<"After first ANY query for '"<<state.target<<"', id="<<d_sd.domain_id<<": weDone="<<weDone<<", weHaveUnauth="<<weHaveUnauth<<", weRedirected="<<weRedirected<<", haveAlias='"<<haveAlias<<"'"<<endl);
100,109✔
1854
  if(pkt.qtype.getCode() == QType::DS && weHaveUnauth &&  !weDone && !weRedirected) {
100,109!
1855
    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);
947✔
1856
    makeNOError(pkt, state.r, state.target, DNSName(), 1);
947✔
1857
    return true;
947✔
1858
  }
947✔
1859

1860
  if(!haveAlias.empty() && (!weDone || pkt.qtype.getCode() == QType::ANY)) {
99,162!
1861
    DLOG(g_log<<Logger::Warning<<"Found nothing that matched for '"<<state.target<<"', but did get alias to '"<<haveAlias<<"', referring"<<endl);
334✔
1862
    DP->completePacket(state.r, haveAlias, state.target, aliasScopeMask);
334✔
1863
    state.r = nullptr;
334✔
1864
    return false;
334✔
1865
  }
334✔
1866

1867
  // referral for DS query
1868
  if(pkt.qtype.getCode() == QType::DS) {
98,828✔
1869
    DLOG(g_log<<"Qtype is DS"<<endl);
11,902✔
1870
    bool doReferral = true;
11,902✔
1871
    if(d_dk.doesDNSSEC()) {
11,902✔
1872
      for(auto& loopRR: rrset) {
11,952✔
1873
        // In a dnssec capable backend auth=true means, there is no delegation at
1874
        // or above this qname in this zone (for DS queries). Without a delegation,
1875
        // at or above this level, it is pointless to search for referrals.
1876
        if(loopRR.auth) {
11,667✔
1877
          doReferral = false;
10,206✔
1878
          break;
10,206✔
1879
        }
10,206✔
1880
      }
11,667✔
1881
    } else {
11,671✔
1882
      for(auto& loopRR: rrset) {
404✔
1883
        // In a non dnssec capable backend auth is always true, so our only option
1884
        // is, always look for referrals. Unless there is a direct match for DS.
1885
        if(loopRR.dr.d_type == QType::DS) {
404✔
1886
          doReferral = false;
96✔
1887
          break;
96✔
1888
        }
96✔
1889
      }
404✔
1890
    }
314✔
1891
    if(doReferral) {
11,902✔
1892
      DLOG(g_log<<"DS query found no direct result, trying referral now"<<endl);
1,600✔
1893
      if(tryReferral(pkt, state.r, state.target, retargeted)) {
1,600✔
1894
        DLOG(g_log<<"Got referral for DS query"<<endl);
1,125✔
1895
        return true;
1,125✔
1896
      }
1,125✔
1897
    }
1,600✔
1898
  }
11,902✔
1899

1900
  if(rrset.empty()) {
97,703✔
1901
    DLOG(g_log<<Logger::Warning<<"Found nothing in the by-name ANY, but let's try wildcards.."<<endl);
28,019✔
1902
    bool wereRetargeted{false};
28,019✔
1903
    bool nodata{false};
28,019✔
1904
    DNSName wildcard;
28,019✔
1905
    if(tryWildcard(pkt, state.r, state.target, wildcard, wereRetargeted, nodata)) {
28,019✔
1906
      if(wereRetargeted) {
8,172✔
1907
        if (!retargeted) {
2,579✔
1908
          state.r->qdomainwild=std::move(wildcard);
1,155✔
1909
        }
1,155✔
1910
        state.retargeted = true;
2,579✔
1911
        return true;
2,579✔
1912
      }
2,579✔
1913
      if(nodata) {
5,593✔
1914
        makeNOError(pkt, state.r, state.target, wildcard, 2);
1,907✔
1915
      }
1,907✔
1916

1917
      return true;
5,593✔
1918
    }
8,172✔
1919
    try {
19,847✔
1920
      if (tryDNAME(pkt, state.r, state.target)) {
19,847✔
1921
        state.retargeted = true;
951✔
1922
        return true;
951✔
1923
      }
951✔
1924
    } catch (const std::range_error &e) {
19,847✔
1925
      // We couldn't make a CNAME.....
1926
      state.r->setRcode(RCode::YXDomain);
380✔
1927
      return true;
380✔
1928
    }
380✔
1929
    if(tryAuthSignal(pkt, state.r, state.target)) {
18,523!
1930
      return true;
×
1931
    }
×
1932

1933
    if (!(((pkt.qtype.getCode() == QType::CNAME) || (pkt.qtype.getCode() == QType::ANY)) && retargeted)) {
18,523✔
1934
      makeNXDomain(pkt, state.r, state.target, wildcard);
18,265✔
1935
    }
18,265✔
1936

1937
    return true;
18,523✔
1938
  }
18,523✔
1939

1940
  if(weRedirected) {
69,684✔
1941
    for(auto& loopRR: rrset) {
9,052!
1942
      if(loopRR.dr.d_type == QType::CNAME) {
9,052!
1943
        state.r->addRecord(DNSZoneRecord(loopRR));
9,052✔
1944
        state.target = getRR<CNAMERecordContent>(loopRR.dr)->getTarget();
9,052✔
1945
        state.retargeted = true;
9,052✔
1946
        return true;
9,052✔
1947
      }
9,052✔
1948
    }
9,052✔
1949
  }
9,052✔
1950
  else if(weDone) {
60,632✔
1951
    bool haveRecords = false;
36,030✔
1952
    bool presigned = isPresigned();
36,030✔
1953
    for(const auto& loopRR: rrset) {
114,972✔
1954
      if (loopRR.dr.d_type == QType::ENT) {
114,972✔
1955
        continue;
841✔
1956
      }
841✔
1957
      if (loopRR.dr.d_type == QType::ALIAS && d_doExpandALIAS && !presigned) {
114,131!
1958
        continue;
×
1959
      }
×
1960
#ifdef HAVE_LUA_RECORDS
114,131✔
1961
      if (loopRR.dr.d_type == QType::LUA && !presigned) {
114,131!
1962
        continue;
×
1963
      }
×
1964
#endif
114,131✔
1965
      if ((pkt.qtype.getCode() == QType::ANY || loopRR.dr.d_type == pkt.qtype.getCode()) && loopRR.auth) {
114,131✔
1966
        state.r->addRecord(DNSZoneRecord(loopRR));
75,895✔
1967
        haveRecords = true;
75,895✔
1968
      }
75,895✔
1969
    }
114,131✔
1970

1971
    if (haveRecords) {
36,030✔
1972
      if(d_dnssec && pkt.qtype.getCode() == QType::ANY) {
35,189✔
1973
        completeANYRecords(pkt, state.r, state.target);
4,043✔
1974
      }
4,043✔
1975
    }
35,189✔
1976
    else {
841✔
1977
      makeNOError(pkt, state.r, state.target, DNSName(), 0);
841✔
1978
    }
841✔
1979

1980
    return true;
36,030✔
1981
  }
36,030✔
1982
  else if(weHaveUnauth) {
24,602✔
1983
    DLOG(g_log<<"Have unauth data, so need to hunt for best NS records"<<endl);
4✔
1984
    if (tryReferral(pkt, state.r, state.target, retargeted)) {
4!
1985
      return true;
×
1986
    }
×
1987
    // check whether this could be fixed easily
1988
#if 0
1989
    if (*(rrset.back().dr.d_name.getStorage().rbegin()) == '.') {
1990
      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 zone rectify " <<d_sd.qname()<<"')"<<endl;
1991
    } else {
1992
      g_log<<Logger::Error<<"Should not get here ("<<pkt.qdomain<<"|"<<pkt.qtype.toString()<<"): please run 'pdnsutil zone rectify "<<d_sd.qname()<<"'"<<endl;
1993
    }
1994
#endif
1995
  }
4✔
1996
  else {
24,598✔
1997
    DLOG(g_log<<"Have some data, but not the right data"<<endl);
24,598✔
1998
    makeNOError(pkt, state.r, state.target, DNSName(), 0);
24,598✔
1999
  }
24,598✔
2000
  return true;
24,602✔
2001
}
69,684✔
2002

2003
std::unique_ptr<DNSPacket> PacketHandler::opcodeQuery(DNSPacket& pkt, bool noCache)
2004
{
126,720✔
2005
  queryState state;
126,720✔
2006
  state.noCache = noCache;
126,720✔
2007

2008
  if (opcodeQueryInner(pkt, state)) {
126,720✔
2009
    doAdditionalProcessing(pkt, state.r);
124,357✔
2010

2011
    // now that all processing is done, span and view may have been set, so we copy them
2012
    state.r->d_span = pkt.d_span;
124,357✔
2013
    state.r->d_view = pkt.d_view;
124,357✔
2014

2015
    for(const auto& loopRR: state.r->getRRS()) {
316,784✔
2016
      if (loopRR.scopeMask != 0) {
316,773✔
2017
        state.noCache=true;
214✔
2018
        break;
214✔
2019
      }
214✔
2020
    }
316,773✔
2021
    if (state.doSigs) {
124,357✔
2022
      addRRSigs(d_dk, B, state.authSet, state.r->getRRS(), &pkt);
101,973✔
2023
    }
101,973✔
2024

2025
    if (PC.enabled() && !state.noCache && pkt.couldBeCached()) {
124,357!
2026
      PC.insert(pkt, *state.r, state.r->getMinTTL(), pkt.d_view); // in the packet cache
14,938✔
2027
    }
14,938✔
2028
  }
124,357✔
2029

2030
  return std::move(state.r);
126,720✔
2031
}
126,720✔
2032

2033
std::unique_ptr<DNSPacket> PacketHandler::opcodeNotify(DNSPacket& pkt, bool /* noCache */)
2034
{
7✔
2035
  S.inc("incoming-notifications");
7✔
2036
  int res=processNotify(pkt);
7✔
2037
  if(res>=0) {
7!
2038
    auto resp=pkt.replyPacket();  // generate an empty reply packet
7✔
2039
    resp->setRcode(res);
7✔
2040
    resp->setOpcode(Opcode::Notify);
7✔
2041
    return resp;
7✔
2042
  }
7✔
2043
  return nullptr;
×
2044
}
7✔
2045

2046
std::unique_ptr<DNSPacket> PacketHandler::opcodeUpdate(DNSPacket& pkt, bool /* noCache */)
2047
{
2,294✔
2048
  if (g_views) {
2,294✔
2049
    // Make this variant-aware without performing the complete UeberBackend::getAuth work
2050
    g_zoneCache.setZoneVariant(pkt);
1,042✔
2051
  }
1,042✔
2052
  else {
1,252✔
2053
    pkt.qdomainzone = ZoneName(pkt.qdomain);
1,252✔
2054
  }
1,252✔
2055

2056
  S.inc("dnsupdate-queries");
2,294✔
2057
  int res=processUpdate(pkt);
2,294✔
2058
  if (res == RCode::Refused) {
2,294✔
2059
    S.inc("dnsupdate-refused");
44✔
2060
  }
44✔
2061
  else if (res != RCode::ServFail) {
2,250!
2062
    S.inc("dnsupdate-answers");
2,250✔
2063
  }
2,250✔
2064
  auto resp=pkt.replyPacket();  // generate an empty reply packet
2,294✔
2065
  resp->setRcode(res);
2,294✔
2066
  resp->setOpcode(Opcode::Update);
2,294✔
2067
  return resp;
2,294✔
2068
}
2,294✔
2069

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

2075
  auto resp=pkt.replyPacket();  // generate an empty reply packet
×
2076
  resp->setRcode(RCode::NotImp);
×
2077
  return resp;
×
2078
}
×
2079

2080
bool PacketHandler::checkForCorrectTSIG(const DNSPacket& packet, DNSName* tsigkeyname, string* secret, TSIGRecordContent* tsigContent)
2081
{
254✔
2082
  uint16_t tsigPos{0};
254✔
2083

2084
  if (!packet.getTSIGDetails(tsigContent, tsigkeyname, &tsigPos)) {
254!
2085
    return false;
×
2086
  }
×
2087

2088
  TSIGTriplet tsigTriplet;
254✔
2089
  tsigTriplet.name = *tsigkeyname;
254✔
2090
  tsigTriplet.algo = tsigContent->d_algoName;
254✔
2091
  if (tsigTriplet.algo == g_hmacmd5dnsname_long) {
254✔
2092
    tsigTriplet.algo = g_hmacmd5dnsname;
182✔
2093
  }
182✔
2094

2095
  if (tsigTriplet.algo != g_gsstsigdnsname) {
254!
2096
    string secret64;
254✔
2097
    if (!B.getTSIGKey(*tsigkeyname, tsigTriplet.algo, secret64)) {
254!
2098
      g_log << Logger::Error << "Packet for domain '" << packet.qdomain << "' denied: can't find TSIG key with name '" << *tsigkeyname << "' and algorithm '" << tsigTriplet.algo << "'" << endl;
×
2099
      return false;
×
2100
    }
×
2101
    B64Decode(secret64, *secret);
254✔
2102
    tsigTriplet.secret = *secret;
254✔
2103
  }
254✔
2104

2105
  try {
254✔
2106
    return packet.validateTSIG(tsigTriplet, *tsigContent, "", tsigContent->d_mac, false);
254✔
2107
  }
254✔
2108
  catch(const std::runtime_error& err) {
254✔
2109
    g_log<<Logger::Error<<"Packet for '"<<packet.qdomain<<"' denied: "<<err.what()<<endl;
144✔
2110
    return false;
144✔
2111
  }
144✔
2112
}
254✔
2113

2114
bool PacketHandler::doLuaRecords()
2115
{
128,146✔
2116
#ifdef HAVE_LUA_RECORDS
128,146✔
2117
  if (g_doLuaRecord) {
128,146!
2118
    return true;
×
2119
  }
×
2120
  if (!d_doLua) {
128,146✔
2121
    d_doLua = d_dk.isMetadataOne(d_sd.zonename, "ENABLE-LUA-RECORDS", true);
608✔
2122
  }
608✔
2123
  return *d_doLua;
128,146✔
2124
#endif
×
2125
  return false;
×
2126
}
128,146✔
2127

2128
bool PacketHandler::isPresigned()
2129
{
116,633✔
2130
  if (!d_ispresigned) {
116,633✔
2131
    d_ispresigned = d_dk.isPresigned(d_sd.zonename);
87,840✔
2132
  }
87,840✔
2133
  return *d_ispresigned;
116,633✔
2134
}
116,633✔
2135

2136
bool PacketHandler::isSecuredZone()
2137
{
130,493✔
2138
  if (!d_issecuredzone) {
130,493✔
2139
    d_issecuredzone = d_dk.isSecuredZone(d_sd.zonename);
125,312✔
2140
  }
125,312✔
2141
  return *d_issecuredzone;
130,493✔
2142
}
130,493✔
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