]> granicus.if.org Git - pdns/commitdiff
Use a bitset for NSEC(3) records with lots of types
authorRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 17 Dec 2018 13:53:07 +0000 (14:53 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 17 Dec 2018 17:05:34 +0000 (18:05 +0100)
pdns/dnsrecords.hh
pdns/nsec3dig.cc
pdns/nsecrecords.cc
pdns/packethandler.cc
pdns/recursordist/test-syncres_cc.cc
pdns/slavecommunicator.cc
pdns/tcpreceiver.cc
pdns/validate.cc

index 353bbef31a4e7b310132ed52a6ab5c40c431ae26..1d46aa08ec2d12c336c2e52cb44d3a3b4ec3774f 100644 (file)
@@ -542,6 +542,62 @@ public:
   struct soatimes d_st;
 };
 
+class NSECBitmap
+{
+public:
+  bool isSet(uint16_t type) const
+  {
+    if (d_bitset) {
+      return d_bitset->test(type);
+    }
+    return d_set.count(type);
+  }
+  void set(uint16_t type)
+  {
+    if (!d_bitset) {
+      if (d_set.size() >= 200) {
+        migrateToBitSet();
+      }
+    }
+    if (d_bitset) {
+      d_bitset->set(type);
+    }
+    else {
+      d_set.insert(type);
+    }
+  }
+  size_t count() const
+  {
+    if (d_bitset) {
+      return d_bitset->count();
+    }
+    else {
+      return d_set.size();
+    }
+  }
+
+  void fromPacket(PacketReader& pr);
+  void toPacket(DNSPacketWriter& pw);
+  std::string getZoneRepresentation() const;
+
+private:
+  void migrateToBitSet()
+  {
+    d_bitset = std::unique_ptr<std::bitset<65536>>(new std::bitset<65536>());
+    for (const auto& type : d_set) {
+      d_bitset->set(type);
+    }
+    d_set.clear();
+  }
+  /* using a dynamic set is very efficient for a small number of
+     types covered (~200), but uses a lot of memory (up to 3MB)
+     when there are a lot of them.
+     So we start with the set, but allocate and switch to a bitset
+     if the number of covered types increases a lot */
+  std::unique_ptr<std::bitset<65536>> d_bitset;
+  std::set<uint16_t> d_set;
+};
+
 class NSECRecordContent : public DNSRecordContent
 {
 public:
@@ -558,9 +614,22 @@ public:
   {
     return QType::NSEC;
   }
+  bool isSet(uint16_t type) const
+  {
+    return d_bitmap.isSet(type);
+  }
+  void set(uint16_t type)
+  {
+    d_bitmap.set(type);
+  }
+  size_t numberOfTypesSet() const
+  {
+    return d_bitmap.count();
+  }
+
   DNSName d_next;
-  std::set<uint16_t> d_set;
 private:
+  NSECBitmap d_bitmap;
 };
 
 class NSEC3RecordContent : public DNSRecordContent
@@ -580,15 +649,26 @@ public:
   uint16_t d_iterations{0};
   string d_salt;
   string d_nexthash;
-  std::set<uint16_t> d_set;
 
   uint16_t getType() const override
   {
     return QType::NSEC3;
   }
-
+  bool isSet(uint16_t type) const
+  {
+    return d_bitmap.isSet(type);
+  }
+  void set(uint16_t type)
+  {
+    d_bitmap.set(type);
+  }
+  size_t numberOfTypesSet() const
+  {
+    return d_bitmap.count();
+  }
 
 private:
+  NSECBitmap d_bitmap;
 };
 
 
index 82345f53294774d56e5a28d127d2f0682d4829ee..ee7430471f9aa0b5f3c14e5b25e4b81d083278d8 100644 (file)
@@ -171,15 +171,18 @@ try
     {
       // cerr<<"got nsec3 ["<<i->first.d_name<<"]"<<endl;
       // cerr<<i->first.d_content->getZoneRepresentation()<<endl;
-      NSEC3RecordContent r = dynamic_cast<NSEC3RecordContent&> (*(i->first.d_content));
+      const auto r = std::dynamic_pointer_cast<NSEC3RecordContent>(i->first.d_content);
+      if (!r) {
+        continue;
+      }
       // nsec3.insert(new nsec3()
       // cerr<<toBase32Hex(r.d_nexthash)<<endl;
       vector<string> parts;
       string sname=i->first.d_name.toString();
       boost::split(parts, sname /* FIXME400 */, boost::is_any_of("."));
-      nsec3s.insert(make_pair(toLower(parts[0]), toBase32Hex(r.d_nexthash)));
-      nsec3salt = r.d_salt;
-      nsec3iters = r.d_iterations;
+      nsec3s.insert(make_pair(toLower(parts[0]), toBase32Hex(r->d_nexthash)));
+      nsec3salt = r->d_salt;
+      nsec3iters = r->d_iterations;
     }
     else
     {
index 20329cf1ea8fcfc6cde9cc90f16188485cc1fd06..8149a9346e34ee0665ebe4913a2ce2d254774428 100644 (file)
 #endif
 #include "dnsrecords.hh"
 
-void NSECRecordContent::report(void)
+class NSECBitmapGenerator
 {
-  regist(1, 47, &make, &make, "NSEC");
-}
-
-std::shared_ptr<DNSRecordContent> NSECRecordContent::make(const string& content)
-{
-  return std::make_shared<NSECRecordContent>(content);
-}
-
-NSECRecordContent::NSECRecordContent(const string& content, const string& zone) 
-{
-  RecordTextReader rtr(content, DNSName(zone));
-  rtr.xfrName(d_next);
-
-  while(!rtr.eof()) {
-    uint16_t type;
-    rtr.xfrType(type);
-    d_set.insert(type);
+public:
+  NSECBitmapGenerator(DNSPacketWriter& pw_): pw(pw_)
+  {
   }
-}
-
-void NSECRecordContent::toPacket(DNSPacketWriter& pw) 
-{
-  pw.xfrName(d_next);
-
-  uint8_t res[34];
-  set<uint16_t>::const_iterator i;
-  int oldWindow = -1;
-  int window = 0;
-  int len = 0;
-  string tmp;
-
-  for(i=d_set.begin(); i != d_set.end(); ++i){
-    uint16_t bit = (*i)%256;
-    window = static_cast<int>((*i) / 256); 
+  void set(uint16_t type)
+  {
+    uint16_t bit = type % 256;
+    int window = static_cast<int>(type / 256);
 
     if (window != oldWindow) {
       if (oldWindow > -1) {
-          res[0] = static_cast<unsigned char>(oldWindow);
-          res[1] = static_cast<unsigned char>(len);
-          tmp.assign(res, res+len+2);
-          pw.xfrBlob(tmp);
+        res[0] = static_cast<unsigned char>(oldWindow);
+        res[1] = static_cast<unsigned char>(len);
+        tmp.assign(res, res+len+2);
+        pw.xfrBlob(tmp);
       }
       memset(res, 0, 34);
       oldWindow = window;
@@ -75,43 +49,150 @@ void NSECRecordContent::toPacket(DNSPacketWriter& pw)
     len=1+bit/8;
   }
 
-  res[0] = static_cast<unsigned char>(window);
-  res[1] = static_cast<unsigned char>(len);
-  tmp.assign(res, res+len+2);
-  pw.xfrBlob(tmp);
+  void finish()
+  {
+    res[0] = static_cast<unsigned char>(oldWindow);
+    res[1] = static_cast<unsigned char>(len);
+    if (len) {
+      tmp.assign(res, res+len+2);
+      pw.xfrBlob(tmp);
+    }
+  }
+
+private:
+  DNSPacketWriter& pw;
+  /* one byte for the window,
+     one for the length,
+     then the maximum of 32 bytes */
+  uint8_t res[34];
+  int oldWindow{-1};
+  int len{0};
+  string tmp;
+};
+
+void NSECBitmap::toPacket(DNSPacketWriter& pw)
+{
+  NSECBitmapGenerator nbg(pw);
+  if (d_bitset) {
+    size_t count = d_bitset->count();
+    size_t found = 0;
+    for(size_t idx = 0; idx < 65535 && found < count; ++idx){
+      if (!d_bitset->test(idx)) {
+        continue;
+      }
+      found++;
+      nbg.set(idx);
+    }
+  }
+  else {
+    for (const auto& type : d_set) {
+      nbg.set(type);
+    }
+  }
+
+  nbg.finish();
 }
 
-std::shared_ptr<NSECRecordContent::DNSRecordContent> NSECRecordContent::make(const DNSRecord &dr, PacketReader& pr)
+void NSECBitmap::fromPacket(PacketReader& pr)
 {
-  auto ret=std::make_shared<NSECRecordContent>();
-  pr.xfrName(ret->d_next);
   string bitmap;
   pr.xfrBlob(bitmap);
+
   // 00 06 20 00 00 00 00 03  -> NS RRSIG NSEC  ( 2, 46, 47 ) counts from left
-  if(bitmap.empty())
-    return ret;
+  if(bitmap.empty()) {
+    return;
+  }
 
-  if(bitmap.size() < 2)
+  if(bitmap.size() < 2) {
     throw MOADNSException("NSEC record with impossibly small bitmap");
+  }
   
   for(unsigned int n = 0; n+1 < bitmap.size();) {
     unsigned int window=static_cast<unsigned char>(bitmap[n++]);
     unsigned int blen=static_cast<unsigned char>(bitmap[n++]);
 
     // end if zero padding and ensure packet length
-    if(window == 0 && blen == 0) break;
-    if(n + blen > bitmap.size())
+    if(window == 0 && blen == 0) {
+      break;
+    }
+
+    if(n + blen > bitmap.size()) {
       throw MOADNSException("NSEC record with bitmap length > packet length");
+    }
 
     for(unsigned int k=0; k < blen; k++) {
       uint8_t val=bitmap[n++];
-      for(int bit = 0; bit < 8 ; ++bit , val>>=1)
+      for(int bit = 0; bit < 8 ; ++bit , val>>=1) {
         if(val & 1) {
-          ret->d_set.insert((7-bit) + 8*(k) + 256*window);
+          set((7-bit) + 8*(k) + 256*window);
         }
       }
+    }
+  }
+}
+
+string NSECBitmap::getZoneRepresentation() const
+{
+  string ret;
+
+  if (d_bitset) {
+    size_t count = d_bitset->count();
+    size_t found = 0;
+    for(size_t idx = 0; idx < 65535 && found < count; ++idx) {
+      if (!d_bitset->test(idx)) {
+        continue;
+      }
+      found++;
+
+      ret+=" ";
+      ret+=DNSRecordContent::NumberToType(idx);
+    }
+  }
+  else {
+    for(const auto& type : d_set) {
+      ret+=" ";
+      ret+=DNSRecordContent::NumberToType(type);
+    }
+  }
+
+  return ret;
+}
+
+void NSECRecordContent::report(void)
+{
+  regist(1, 47, &make, &make, "NSEC");
+}
+
+std::shared_ptr<DNSRecordContent> NSECRecordContent::make(const string& content)
+{
+  return std::make_shared<NSECRecordContent>(content);
+}
+
+NSECRecordContent::NSECRecordContent(const string& content, const string& zone)
+{
+  RecordTextReader rtr(content, DNSName(zone));
+  rtr.xfrName(d_next);
+
+  while(!rtr.eof()) {
+    uint16_t type;
+    rtr.xfrType(type);
+    set(type);
   }
+}
+
+void NSECRecordContent::toPacket(DNSPacketWriter& pw)
+{
+  pw.xfrName(d_next);
+  d_bitmap.toPacket(pw);
+}
+
+std::shared_ptr<NSECRecordContent::DNSRecordContent> NSECRecordContent::make(const DNSRecord &dr, PacketReader& pr)
+{
+  auto ret=std::make_shared<NSECRecordContent>();
+  pr.xfrName(ret->d_next);
+
+  ret->d_bitmap.fromPacket(pr);
+
   return ret;
 }
 
@@ -120,13 +201,8 @@ string NSECRecordContent::getZoneRepresentation(bool noDot) const
   string ret;
   RecordTextWriter rtw(ret);
   rtw.xfrName(d_next);
-  
-  for(set<uint16_t>::const_iterator i=d_set.begin(); i!=d_set.end(); ++i) {
-    ret+=" ";
-    ret+=NumberToType(*i);
-  }
-  
-  return ret;
+
+  return ret + d_bitmap.getZoneRepresentation();
 }
 
 ////// begin of NSEC3
@@ -154,7 +230,7 @@ NSEC3RecordContent::NSEC3RecordContent(const string& content, const string& zone
   while(!rtr.eof()) {
     uint16_t type;
     rtr.xfrType(type);
-    d_set.insert(type);
+    set(type);
   }
 }
 
@@ -168,39 +244,8 @@ void NSEC3RecordContent::toPacket(DNSPacketWriter& pw)
 
   pw.xfr8BitInt(d_nexthash.length());
   pw.xfrBlob(d_nexthash);
-  
-  uint8_t res[34];
-  set<uint16_t>::const_iterator i;
-  int oldWindow = -1;
-  int window = 0;
-  int len = 0;
-  string tmp;
-
-  for(i=d_set.begin(); i != d_set.end(); ++i){
-    uint16_t bit = (*i)%256;
-    window = static_cast<int>((*i) / 256);
 
-    if (window != oldWindow) {
-      if (oldWindow > -1) {
-          res[0] = static_cast<unsigned char>(oldWindow);
-          res[1] = static_cast<unsigned char>(len);
-          tmp.assign(res, res+len+2);
-          pw.xfrBlob(tmp);
-      }
-      memset(res, 0, 34);
-      oldWindow = window;
-    }
-    res[2+bit/8] |= 1 << (7-(bit%8));
-    len=1+bit/8;
-  }
-
-  res[0] = static_cast<unsigned char>(window);
-  res[1] = static_cast<unsigned char>(len);
-
-  if (len) {
-    tmp.assign(res, res+len+2);
-    pw.xfrBlob(tmp);
-  }
+  d_bitmap.toPacket(pw);
 }
 
 std::shared_ptr<NSEC3RecordContent::DNSRecordContent> NSEC3RecordContent::make(const DNSRecord &dr, PacketReader& pr)
@@ -215,35 +260,8 @@ std::shared_ptr<NSEC3RecordContent::DNSRecordContent> NSEC3RecordContent::make(c
 
   pr.xfr8BitInt(len);
   pr.xfrBlob(ret->d_nexthash, len);
-  
-  string bitmap;
-  pr.xfrBlob(bitmap);
-  
-  // 00 06 20 00 00 00 00 03  -> NS RRSIG NSEC  ( 2, 46, 47 ) counts from left
-  
-  if(bitmap.empty())
-    return ret;
-
-  if(bitmap.size() < 2)
-    throw MOADNSException("NSEC3 record with impossibly small bitmap");
-
-  for(unsigned int n = 0; n+1 < bitmap.size();) {
-    unsigned int window=static_cast<unsigned char>(bitmap[n++]);
-    unsigned int innerlen=static_cast<unsigned char>(bitmap[n++]);
-    
-    // end if zero padding and ensure packet length
-    if(window == 0&&innerlen == 0) break;
-    if(n+innerlen>bitmap.size())
-      throw MOADNSException("NSEC record with bitmap length > packet length");
 
-    for(unsigned int k=0; k < innerlen; k++) {
-      uint8_t val=bitmap[n++];
-      for(int bit = 0; bit < 8 ; ++bit , val>>=1)
-        if(val & 1) {
-          ret->d_set.insert((7-bit) + 8*(k) + 256*window);
-        }
-      }
-  }
+  ret->d_bitmap.fromPacket(pr);
   return ret;
 }
 
@@ -257,12 +275,8 @@ string NSEC3RecordContent::getZoneRepresentation(bool noDot) const
 
   rtw.xfrHexBlob(d_salt);
   rtw.xfrBase32HexBlob(d_nexthash);
-  for(set<uint16_t>::const_iterator i=d_set.begin(); i!=d_set.end(); ++i) {
-    ret+=" ";
-    ret+=NumberToType(*i);
-  }
-  
-  return ret;
+
+  return ret + d_bitmap.getZoneRepresentation();
 }
 
 
index c6680906c61b3e4aaebe7dc5bf6b6258fbaae156..c319f3e17aede09bb326aa89260f795aa2197219 100644 (file)
@@ -484,19 +484,19 @@ void PacketHandler::emitNSEC(DNSPacket *r, const SOAData& sd, const DNSName& nam
   NSECRecordContent nrc;
   nrc.d_next = next;
 
-  nrc.d_set.insert(QType::NSEC);
-  nrc.d_set.insert(QType::RRSIG);
+  nrc.set(QType::NSEC);
+  nrc.set(QType::RRSIG);
   if(sd.qname == name) {
-    nrc.d_set.insert(QType::SOA); // 1dfd8ad SOA can live outside the records table
-    nrc.d_set.insert(QType::DNSKEY);
+    nrc.set(QType::SOA); // 1dfd8ad SOA can live outside the records table
+    nrc.set(QType::DNSKEY);
     string publishCDNSKEY;
     d_dk.getFromMeta(name, "PUBLISH-CDNSKEY", publishCDNSKEY);
     if (publishCDNSKEY == "1")
-      nrc.d_set.insert(QType::CDNSKEY);
+      nrc.set(QType::CDNSKEY);
     string publishCDS;
     d_dk.getFromMeta(name, "PUBLISH-CDS", publishCDS);
     if (! publishCDS.empty())
-      nrc.d_set.insert(QType::CDS);
+      nrc.set(QType::CDS);
   }
 
   DNSZoneRecord rr;
@@ -505,17 +505,17 @@ void PacketHandler::emitNSEC(DNSPacket *r, const SOAData& sd, const DNSName& nam
   while(B.get(rr)) {
 #ifdef HAVE_LUA_RECORDS   
     if(rr.dr.d_type == QType::LUA)
-      nrc.d_set.insert(getRR<LUARecordContent>(rr.dr)->d_type);
+      nrc.set(getRR<LUARecordContent>(rr.dr)->d_type);
     else
 #endif
       if(rr.dr.d_type == QType::NS || rr.auth)
-      nrc.d_set.insert(rr.dr.d_type);
+      nrc.set(rr.dr.d_type);
   }
 
   rr.dr.d_name = name;
   rr.dr.d_ttl = sd.default_ttl;
   rr.dr.d_type = QType::NSEC;
-  rr.dr.d_content = std::make_shared<NSECRecordContent>(nrc);
+  rr.dr.d_content = std::make_shared<NSECRecordContent>(std::move(nrc));
   rr.dr.d_place = (mode == 5 ) ? DNSResourceRecord::ANSWER: DNSResourceRecord::AUTHORITY;
   rr.auth = true;
 
@@ -535,38 +535,40 @@ void PacketHandler::emitNSEC3(DNSPacket *r, const SOAData& sd, const NSEC3PARAMR
 
   if(!name.empty()) {
     if (sd.qname == name) {
-      n3rc.d_set.insert(QType::SOA); // 1dfd8ad SOA can live outside the records table
-      n3rc.d_set.insert(QType::NSEC3PARAM);
-      n3rc.d_set.insert(QType::DNSKEY);
+      n3rc.set(QType::SOA); // 1dfd8ad SOA can live outside the records table
+      n3rc.set(QType::NSEC3PARAM);
+      n3rc.set(QType::DNSKEY);
       string publishCDNSKEY;
       d_dk.getFromMeta(name, "PUBLISH-CDNSKEY", publishCDNSKEY);
       if (publishCDNSKEY == "1")
-        n3rc.d_set.insert(QType::CDNSKEY);
+        n3rc.set(QType::CDNSKEY);
       string publishCDS;
       d_dk.getFromMeta(name, "PUBLISH-CDS", publishCDS);
       if (! publishCDS.empty())
-        n3rc.d_set.insert(QType::CDS);
+        n3rc.set(QType::CDS);
     }
 
     B.lookup(QType(QType::ANY), name, NULL, sd.domain_id);
     while(B.get(rr)) {
 #ifdef HAVE_LUA_RECORDS
       if(rr.dr.d_type == QType::LUA)
-        n3rc.d_set.insert(getRR<LUARecordContent>(rr.dr)->d_type);
+        n3rc.set(getRR<LUARecordContent>(rr.dr)->d_type);
       else
 #endif
         if(rr.dr.d_type && (rr.dr.d_type == QType::NS || rr.auth)) // skip empty non-terminals
-        n3rc.d_set.insert(rr.dr.d_type);
+        n3rc.set(rr.dr.d_type);
     }
   }
 
-  if (n3rc.d_set.size() && !(n3rc.d_set.size() == 1 && n3rc.d_set.count(QType::NS)))
-    n3rc.d_set.insert(QType::RRSIG);
+  const auto numberOfTypesSet = n3rc.numberOfTypesSet();
+  if (numberOfTypesSet != 0 && !(numberOfTypesSet == 1 && n3rc.isSet(QType::NS))) {
+    n3rc.set(QType::RRSIG);
+  }
 
   rr.dr.d_name = DNSName(toBase32Hex(namehash))+sd.qname;
   rr.dr.d_ttl = sd.default_ttl;
   rr.dr.d_type=QType::NSEC3;
-  rr.dr.d_content=std::make_shared<NSEC3RecordContent>(n3rc);
+  rr.dr.d_content=std::make_shared<NSEC3RecordContent>(std::move(n3rc));
   rr.dr.d_place = (mode == 5 ) ? DNSResourceRecord::ANSWER: DNSResourceRecord::AUTHORITY;
   rr.auth = true;
 
index 22655c1fbd2a66c8b0a695b023a8b1076a5d08d8..828a00b7605958e1aac822aaf76315fca1e44ff5 100644 (file)
@@ -343,13 +343,15 @@ static void addNSECRecordToLW(const DNSName& domain, const DNSName& next, const
 {
   NSECRecordContent nrc;
   nrc.d_next = next;
-  nrc.d_set = types;
+  for (const auto& type : types) {
+    nrc.set(type);
+  }
 
   DNSRecord rec;
   rec.d_name = domain;
   rec.d_ttl = ttl;
   rec.d_type = QType::NSEC;
-  rec.d_content = std::make_shared<NSECRecordContent>(nrc);
+  rec.d_content = std::make_shared<NSECRecordContent>(std::move(nrc));
   rec.d_place = DNSResourceRecord::AUTHORITY;
 
   records.push_back(rec);
@@ -363,13 +365,15 @@ static void addNSEC3RecordToLW(const DNSName& hashedName, const std::string& has
   nrc.d_iterations = iterations;
   nrc.d_salt = salt;
   nrc.d_nexthash = hashedNext;
-  nrc.d_set = types;
+  for (const auto& type : types) {
+    nrc.set(type);
+  }
 
   DNSRecord rec;
   rec.d_name = hashedName;
   rec.d_ttl = ttl;
   rec.d_type = QType::NSEC3;
-  rec.d_content = std::make_shared<NSEC3RecordContent>(nrc);
+  rec.d_content = std::make_shared<NSEC3RecordContent>(std::move(nrc));
   rec.d_place = DNSResourceRecord::AUTHORITY;
 
   records.push_back(rec);
index ef52ff14d6fe6c4fd2d5dbdefa41163bd24b3221..43c074362f017ef70db67ca8149eb0d6f9cc0730 100644 (file)
@@ -200,7 +200,7 @@ static bool processRecordForZS(const DNSName& domain, bool& firstNSEC3, DNSResou
     } else if (zs.optOutFlag != (ns3rc.d_flags & 1))
       throw PDNSException("Zones with a mixture of Opt-Out NSEC3 RRs and non-Opt-Out NSEC3 RRs are not supported.");
     zs.optOutFlag = ns3rc.d_flags & 1;
-    if (ns3rc.d_set.count(QType::NS) && !(rr.qname==domain)) {
+    if (ns3rc.isSet(QType::NS) && !(rr.qname==domain)) {
       DNSName hashPart = rr.qname.makeRelative(domain);
       zs.secured.insert(hashPart);
     }
index 9b45cdf39ea8179b7e25142df02f3c7b58bf021b..e04415c95331d294cae7109b570701cd726b8cec 100644 (file)
@@ -951,13 +951,17 @@ int TCPNameserver::doAXFR(const DNSName &target, shared_ptr<DNSPacket> q, int ou
       for(nsecxrepo_t::const_iterator iter = nsecxrepo.begin(); iter != nsecxrepo.end(); ++iter) {
         if(iter->second.d_auth) {
           NSEC3RecordContent n3rc;
-          n3rc.d_set = iter->second.d_set;
-          if (n3rc.d_set.size() && (n3rc.d_set.size() != 1 || !n3rc.d_set.count(QType::NS)))
-            n3rc.d_set.insert(QType::RRSIG);
-          n3rc.d_salt=ns3pr.d_salt;
+          for (const auto type : iter->second.d_set) {
+            n3rc.set(type);
+          }
+          const auto numberOfTypesSet = n3rc.numberOfTypesSet();
+          if (numberOfTypesSet != 0 && (numberOfTypesSet != 1 || !n3rc.isSet(QType::NS))) {
+            n3rc.set(QType::RRSIG);
+          }
+          n3rc.d_salt = ns3pr.d_salt;
           n3rc.d_flags = ns3pr.d_flags;
           n3rc.d_iterations = ns3pr.d_iterations;
-          n3rc.d_algorithm = 1; // SHA1, fixed in PowerDNS for now
+          n3rc.d_algorithm = DNSSECKeeper::SHA1; // SHA1, fixed in PowerDNS for now
           nsecxrepo_t::const_iterator inext = iter;
           inext++;
           if(inext == nsecxrepo.end())
@@ -972,7 +976,7 @@ int TCPNameserver::doAXFR(const DNSName &target, shared_ptr<DNSPacket> q, int ou
           zrr.dr.d_name = iter->first+sd.qname;
 
           zrr.dr.d_ttl = sd.default_ttl;
-          zrr.dr.d_content = std::make_shared<NSEC3RecordContent>(n3rc);
+          zrr.dr.d_content = std::make_shared<NSEC3RecordContent>(std::move(n3rc));
           zrr.dr.d_type = QType::NSEC3;
           zrr.dr.d_place = DNSResourceRecord::ANSWER;
           zrr.auth=true;
@@ -995,9 +999,11 @@ int TCPNameserver::doAXFR(const DNSName &target, shared_ptr<DNSPacket> q, int ou
     }
     else for(nsecxrepo_t::const_iterator iter = nsecxrepo.begin(); iter != nsecxrepo.end(); ++iter) {
       NSECRecordContent nrc;
-      nrc.d_set = iter->second.d_set;
-      nrc.d_set.insert(QType::RRSIG);
-      nrc.d_set.insert(QType::NSEC);
+      for (const auto type : iter->second.d_set) {
+        nrc.set(type);
+      }
+      nrc.set(QType::RRSIG);
+      nrc.set(QType::NSEC);
 
       if(boost::next(iter) != nsecxrepo.end())
         nrc.d_next = boost::next(iter)->first;
@@ -1006,7 +1012,7 @@ int TCPNameserver::doAXFR(const DNSName &target, shared_ptr<DNSPacket> q, int ou
       zrr.dr.d_name = iter->first;
 
       zrr.dr.d_ttl = sd.default_ttl;
-      zrr.dr.d_content = std::make_shared<NSECRecordContent>(nrc);
+      zrr.dr.d_content = std::make_shared<NSECRecordContent>(std::move(nrc));
       zrr.dr.d_type = QType::NSEC;
       zrr.dr.d_place = DNSResourceRecord::ANSWER;
       zrr.auth=true;
index c081fe1556d9a819e3d578be4ba097f471fdce3b..c9d17c86e204b4880e54c90700c8689a960a587d 100644 (file)
@@ -75,7 +75,7 @@ bool denialProvesNoDelegation(const DNSName& zone, const std::vector<DNSRecord>&
       }
 
       if (record.d_name == zone) {
-        return !nsec->d_set.count(QType::NS);
+        return !nsec->isSet(QType::NS);
       }
 
       if (isCoveredByNSEC(zone, record.d_name, nsec->d_next)) {
@@ -95,7 +95,7 @@ bool denialProvesNoDelegation(const DNSName& zone, const std::vector<DNSRecord>&
 
       const string beginHash = fromBase32Hex(record.d_name.getRawLabels()[0]);
       if (beginHash == h) {
-        return !nsec3->d_set.count(QType::NS);
+        return !nsec3->isSet(QType::NS);
       }
 
       if (isCoveredByNSEC3Hash(h, beginHash, nsec3->d_nexthash)) {
@@ -154,15 +154,15 @@ static DNSName getNSECOwnerName(const DNSName& initialOwner, const std::vector<s
 
 static bool isNSECAncestorDelegation(const DNSName& signer, const DNSName& owner, const std::shared_ptr<NSECRecordContent>& nsec)
 {
-  return nsec->d_set.count(QType::NS) &&
-    !nsec->d_set.count(QType::SOA) &&
+  return nsec->isSet(QType::NS) &&
+    !nsec->isSet(QType::SOA) &&
     signer.countLabels() < owner.countLabels();
 }
 
 static bool isNSEC3AncestorDelegation(const DNSName& signer, const DNSName& owner, const std::shared_ptr<NSEC3RecordContent>& nsec3)
 {
-  return nsec3->d_set.count(QType::NS) &&
-    !nsec3->d_set.count(QType::SOA) &&
+  return nsec3->isSet(QType::NS) &&
+    !nsec3->isSet(QType::SOA) &&
     signer.countLabels() < owner.countLabels();
 }
 
@@ -191,7 +191,7 @@ static bool provesNoDataWildCard(const DNSName& qname, const uint16_t qtype, con
 
         if (qname.isPartOf(wildcard)) {
           LOG("\tWildcard matches");
-          if (qtype == 0 || !nsec->d_set.count(qtype)) {
+          if (qtype == 0 || !nsec->isSet(qtype)) {
             LOG(" and proves that the type did not exist"<<endl);
             return true;
           }
@@ -287,7 +287,7 @@ static bool provesNSEC3NoWildCard(DNSName wildcard, uint16_t const qtype, const
           if (wildcardExists) {
             *wildcardExists = true;
           }
-          if (qtype == 0 || !nsec3->d_set.count(qtype)) {
+          if (qtype == 0 || !nsec3->isSet(qtype)) {
             LOG(" and proves that the type did not exist"<<endl);
             return true;
           }
@@ -346,7 +346,7 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16
            owner name regardless of type.
         */
         if (qtype != QType::DS && (qname == owner || qname.isPartOf(owner)) && isNSECAncestorDelegation(signer, owner, nsec)) {
-          LOG("type is "<<QType(qtype).getName()<<", NS is "<<std::to_string(nsec->d_set.count(QType::NS))<<", SOA is "<<std::to_string(nsec->d_set.count(QType::SOA))<<", signer is "<<signer<<", owner name is "<<owner<<endl);
+          LOG("type is "<<QType(qtype).getName()<<", NS is "<<std::to_string(nsec->isSet(QType::NS))<<", SOA is "<<std::to_string(nsec->isSet(QType::SOA))<<", signer is "<<signer<<", owner name is "<<owner<<endl);
           /* this is an "ancestor delegation" NSEC RR */
           LOG("An ancestor delegation NSEC RR can only deny the existence of a DS"<<endl);
           return NODATA;
@@ -354,7 +354,7 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16
 
         /* check if the type is denied */
         if(qname == owner) {
-          if (nsec->d_set.count(qtype)) {
+          if (nsec->isSet(qtype)) {
             LOG("Does _not_ deny existence of type "<<QType(qtype).getName()<<endl);
             return NODATA;
           }
@@ -362,7 +362,7 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16
           LOG("Denies existence of type "<<QType(qtype).getName()<<endl);
 
           /* RFC 6840 section 4.3 */
-          if (nsec->d_set.count(QType::CNAME)) {
+          if (nsec->isSet(QType::CNAME)) {
             LOG("However a CNAME exists"<<endl);
             return NODATA;
           }
@@ -373,7 +373,7 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16
            * attention.  Bits corresponding to the delegation NS RRset and any
            * RRsets for which the parent zone has authoritative data MUST be set
            */
-          if (referralToUnsigned && qtype == QType::DS && !nsec->d_set.count(QType::NS)) {
+          if (referralToUnsigned && qtype == QType::DS && !nsec->isSet(QType::NS)) {
             LOG("However, no NS record exists at this level!"<<endl);
             return NODATA;
           }
@@ -421,7 +421,7 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16
           return NODATA;
         }
 
-        LOG("Did not deny existence of "<<QType(qtype).getName()<<", "<<owner<<"?="<<qname<<", "<<nsec->d_set.count(qtype)<<", next: "<<nsec->d_next<<endl);
+        LOG("Did not deny existence of "<<QType(qtype).getName()<<", "<<owner<<"?="<<qname<<", "<<nsec->isSet(qtype)<<", next: "<<nsec->d_next<<endl);
       }
     } else if(v.first.second==QType::NSEC3) {
       for(const auto& r : v.second.records) {
@@ -455,7 +455,7 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16
            owner name regardless of type.
         */
         if (qtype != QType::DS && beginHash == h && isNSEC3AncestorDelegation(signer, v.first.first, nsec3)) {
-          LOG("type is "<<QType(qtype).getName()<<", NS is "<<std::to_string(nsec3->d_set.count(QType::NS))<<", SOA is "<<std::to_string(nsec3->d_set.count(QType::SOA))<<", signer is "<<signer<<", owner name is "<<v.first.first<<endl);
+          LOG("type is "<<QType(qtype).getName()<<", NS is "<<std::to_string(nsec3->isSet(QType::NS))<<", SOA is "<<std::to_string(nsec3->isSet(QType::SOA))<<", signer is "<<signer<<", owner name is "<<v.first.first<<endl);
           /* this is an "ancestor delegation" NSEC3 RR */
           LOG("An ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl);
           return NODATA;
@@ -463,7 +463,7 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16
 
         // If the name exists, check if the qtype is denied
         if(beginHash == h) {
-          if (nsec3->d_set.count(qtype)) {
+          if (nsec3->isSet(qtype)) {
             LOG("Does _not_ deny existence of type "<<QType(qtype).getName()<<" for name "<<qname<<" (not opt-out)."<<endl);
             return NODATA;
           }
@@ -471,7 +471,7 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16
           LOG("Denies existence of type "<<QType(qtype).getName()<<" for name "<<qname<<" (not opt-out)."<<endl);
 
           /* RFC 6840 section 4.3 */
-          if (nsec3->d_set.count(QType::CNAME)) {
+          if (nsec3->isSet(QType::CNAME)) {
             LOG("However a CNAME exists"<<endl);
             return NODATA;
           }
@@ -483,7 +483,7 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16
            * set and that the DS bit is not set in the Type Bit Maps field of the
            * NSEC3 RR.
            */
-          if (referralToUnsigned && qtype == QType::DS && !nsec3->d_set.count(QType::NS)) {
+          if (referralToUnsigned && qtype == QType::DS && !nsec3->isSet(QType::NS)) {
             LOG("However, no NS record exists at this level!"<<endl);
             return NODATA;
           }