"Fossies" - the Fresh Open Source Software Archive

Member "pdns-auth-4.2.0/pdns/recursordist/test-syncres_cc9.cc" (27 Aug 2019, 47362 Bytes) of package /linux/misc/dns/pdns-auth-4.2.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "test-syncres_cc9.cc" see the Fossies "Dox" file reference documentation.

    1 #define BOOST_TEST_DYN_LINK
    2 #include <boost/test/unit_test.hpp>
    3 
    4 #include "test-syncres_cc.hh"
    5 
    6 BOOST_AUTO_TEST_SUITE(syncres_cc9)
    7 
    8 BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cname_cache_secure) {
    9   /*
   10     Validation is optional, and the first query does not ask for it,
   11     so the answer is cached as Indeterminate.
   12     The second query asks for validation, answer should be marked as
   13     Secure.
   14   */
   15   std::unique_ptr<SyncRes> sr;
   16   initSR(sr, true);
   17 
   18   setDNSSECValidation(sr, DNSSECMode::Process);
   19 
   20   primeHints();
   21   const DNSName target("com.");
   22   const DNSName cnameTarget("cname-com.");
   23   testkeysset_t keys;
   24 
   25   auto luaconfsCopy = g_luaconfs.getCopy();
   26   luaconfsCopy.dsAnchors.clear();
   27   generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors);
   28   g_luaconfs.setState(luaconfsCopy);
   29 
   30   size_t queriesCount = 0;
   31 
   32   sr->setAsyncCallback([target,cnameTarget,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
   33       queriesCount++;
   34 
   35       if (type == QType::DS || type == QType::DNSKEY) {
   36         return genericDSAndDNSKEYHandler(res, domain, domain, type, keys, false);
   37       }
   38       else {
   39         if (domain == target && type == QType::A) {
   40           setLWResult(res, 0, true, false, true);
   41           addRecordToLW(res, target, QType::CNAME, cnameTarget.toString());
   42           addRRSIG(keys, res->d_records, DNSName("."), 300);
   43           addRecordToLW(res, cnameTarget, QType::A, "192.0.2.1");
   44           addRRSIG(keys, res->d_records, DNSName("."), 300);
   45           return 1;
   46         } else if (domain == cnameTarget && type == QType::A) {
   47           setLWResult(res, 0, true, false, true);
   48           addRecordToLW(res, cnameTarget, QType::A, "192.0.2.1");
   49           addRRSIG(keys, res->d_records, DNSName("."), 300);
   50           return 1;
   51         }
   52       }
   53 
   54       return 0;
   55     });
   56 
   57   vector<DNSRecord> ret;
   58   /* first query does not require validation */
   59   sr->setDNSSECValidationRequested(false);
   60   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   61   BOOST_CHECK_EQUAL(res, RCode::NoError);
   62   BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
   63   BOOST_REQUIRE_EQUAL(ret.size(), 4);
   64   for (const auto& record : ret) {
   65     BOOST_CHECK(record.d_type == QType::CNAME || record.d_type == QType::A || record.d_type == QType::RRSIG);
   66   }
   67   BOOST_CHECK_EQUAL(queriesCount, 2);
   68 
   69 
   70   ret.clear();
   71   /* second one _does_ require validation */
   72   sr->setDNSSECValidationRequested(true);
   73   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   74   BOOST_CHECK_EQUAL(res, RCode::NoError);
   75   BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
   76   BOOST_REQUIRE_EQUAL(ret.size(), 4);
   77   for (const auto& record : ret) {
   78     BOOST_CHECK(record.d_type == QType::CNAME || record.d_type == QType::A || record.d_type == QType::RRSIG);
   79   }
   80   BOOST_CHECK_EQUAL(queriesCount, 5);
   81 }
   82 
   83 BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cname_cache_insecure) {
   84   /*
   85     Validation is optional, and the first query does not ask for it,
   86     so the answer is cached as Indeterminate.
   87     The second query asks for validation, answer should be marked as
   88     Insecure.
   89   */
   90   std::unique_ptr<SyncRes> sr;
   91   initSR(sr, true);
   92 
   93   setDNSSECValidation(sr, DNSSECMode::Process);
   94 
   95   primeHints();
   96   const DNSName target("com.");
   97   const DNSName cnameTarget("cname-com.");
   98   testkeysset_t keys;
   99 
  100   auto luaconfsCopy = g_luaconfs.getCopy();
  101   luaconfsCopy.dsAnchors.clear();
  102   g_luaconfs.setState(luaconfsCopy);
  103 
  104   size_t queriesCount = 0;
  105 
  106   sr->setAsyncCallback([target,cnameTarget,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
  107       queriesCount++;
  108 
  109       if (type == QType::DS || type == QType::DNSKEY) {
  110         return genericDSAndDNSKEYHandler(res, domain, domain, type, keys, false);
  111       }
  112       else {
  113         if (domain == target && type == QType::A) {
  114           setLWResult(res, 0, true, false, true);
  115           addRecordToLW(res, target, QType::CNAME, cnameTarget.toString());
  116           addRecordToLW(res, cnameTarget, QType::A, "192.0.2.1");
  117           return 1;
  118         } else if (domain == cnameTarget && type == QType::A) {
  119           setLWResult(res, 0, true, false, true);
  120           addRecordToLW(res, cnameTarget, QType::A, "192.0.2.1");
  121           return 1;
  122         }
  123       }
  124 
  125       return 0;
  126     });
  127 
  128   vector<DNSRecord> ret;
  129   /* first query does not require validation */
  130   sr->setDNSSECValidationRequested(false);
  131   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
  132   BOOST_CHECK_EQUAL(res, RCode::NoError);
  133   BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
  134   BOOST_REQUIRE_EQUAL(ret.size(), 2);
  135   for (const auto& record : ret) {
  136     BOOST_CHECK(record.d_type == QType::CNAME || record.d_type == QType::A);
  137   }
  138   BOOST_CHECK_EQUAL(queriesCount, 2);
  139 
  140 
  141   ret.clear();
  142   /* second one _does_ require validation */
  143   sr->setDNSSECValidationRequested(true);
  144   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
  145   BOOST_CHECK_EQUAL(res, RCode::NoError);
  146   BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
  147   BOOST_REQUIRE_EQUAL(ret.size(), 2);
  148   for (const auto& record : ret) {
  149     BOOST_CHECK(record.d_type == QType::CNAME || record.d_type == QType::A);
  150   }
  151   BOOST_CHECK_EQUAL(queriesCount, 2);
  152 }
  153 
  154 BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cname_cache_bogus) {
  155   /*
  156     Validation is optional, and the first query does not ask for it,
  157     so the answer is cached as Indeterminate.
  158     The second query asks for validation, answer should be marked as
  159     Bogus.
  160   */
  161   std::unique_ptr<SyncRes> sr;
  162   initSR(sr, true);
  163 
  164   setDNSSECValidation(sr, DNSSECMode::Process);
  165 
  166   primeHints();
  167   const DNSName target("com.");
  168   const DNSName cnameTarget("cname-com.");
  169   testkeysset_t keys;
  170 
  171   auto luaconfsCopy = g_luaconfs.getCopy();
  172   luaconfsCopy.dsAnchors.clear();
  173   generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors);
  174   g_luaconfs.setState(luaconfsCopy);
  175 
  176   size_t queriesCount = 0;
  177 
  178   sr->setAsyncCallback([target,cnameTarget,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
  179       queriesCount++;
  180 
  181       if (type == QType::DS || type == QType::DNSKEY) {
  182         return genericDSAndDNSKEYHandler(res, domain, domain, type, keys, false);
  183       }
  184       else {
  185         if (domain == target && type == QType::A) {
  186           setLWResult(res, 0, true, false, true);
  187           addRecordToLW(res, target, QType::CNAME, cnameTarget.toString(), DNSResourceRecord::ANSWER, 86400);
  188           addRecordToLW(res, cnameTarget, QType::A, "192.0.2.1", DNSResourceRecord::ANSWER, 86400);
  189           /* no RRSIG */
  190           return 1;
  191         } else if (domain == cnameTarget && type == QType::A) {
  192           setLWResult(res, 0, true, false, true);
  193           addRecordToLW(res, cnameTarget, QType::A, "192.0.2.1", DNSResourceRecord::ANSWER, 86400);
  194           /* no RRSIG */
  195           return 1;
  196         }
  197       }
  198 
  199       return 0;
  200     });
  201 
  202   SyncRes::s_maxbogusttl = 60;
  203   SyncRes::s_maxnegttl = 3600;
  204 
  205   vector<DNSRecord> ret;
  206   /* first query does not require validation */
  207   sr->setDNSSECValidationRequested(false);
  208   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
  209   BOOST_CHECK_EQUAL(res, RCode::NoError);
  210   BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
  211   BOOST_REQUIRE_EQUAL(ret.size(), 2);
  212   for (const auto& record : ret) {
  213     BOOST_CHECK(record.d_type == QType::CNAME || record.d_type == QType::A);
  214     BOOST_CHECK_EQUAL(record.d_ttl, 86400);
  215   }
  216   BOOST_CHECK_EQUAL(queriesCount, 2);
  217 
  218 
  219   ret.clear();
  220   /* second one _does_ require validation */
  221   sr->setDNSSECValidationRequested(true);
  222   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
  223   BOOST_CHECK_EQUAL(res, RCode::NoError);
  224   BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
  225   BOOST_REQUIRE_EQUAL(ret.size(), 2);
  226   /* check that we correctly capped the TTD for a Bogus record after
  227      just-in-time validation */
  228   for (const auto& record : ret) {
  229     BOOST_CHECK(record.d_type == QType::CNAME || record.d_type == QType::A);
  230     BOOST_CHECK_EQUAL(record.d_ttl, SyncRes::s_maxbogusttl);
  231   }
  232   BOOST_CHECK_EQUAL(queriesCount, 5);
  233 
  234   ret.clear();
  235   /* and a third time to make sure that the validation status (and TTL!)
  236      was properly updated in the cache */
  237   sr->setDNSSECValidationRequested(true);
  238   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
  239   BOOST_CHECK_EQUAL(res, RCode::NoError);
  240   BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
  241   BOOST_REQUIRE_EQUAL(ret.size(), 2);
  242   for (const auto& record : ret) {
  243     BOOST_CHECK(record.d_type == QType::CNAME || record.d_type == QType::A);
  244     BOOST_CHECK_EQUAL(record.d_ttl, SyncRes::s_maxbogusttl);
  245   }
  246   BOOST_CHECK_EQUAL(queriesCount, 5);
  247 }
  248 
  249 BOOST_AUTO_TEST_CASE(test_dnssec_validation_additional_without_rrsig) {
  250   /*
  251     We get a record from a secure zone in the additional section, without
  252     the corresponding RRSIG. The record should not be marked as authoritative
  253     and should be correctly validated.
  254   */
  255   std::unique_ptr<SyncRes> sr;
  256   initSR(sr, true);
  257 
  258   setDNSSECValidation(sr, DNSSECMode::Process);
  259 
  260   primeHints();
  261   const DNSName target("com.");
  262   const DNSName addTarget("nsX.com.");
  263   testkeysset_t keys;
  264 
  265   auto luaconfsCopy = g_luaconfs.getCopy();
  266   luaconfsCopy.dsAnchors.clear();
  267   generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors);
  268   g_luaconfs.setState(luaconfsCopy);
  269 
  270   size_t queriesCount = 0;
  271 
  272   sr->setAsyncCallback([target,addTarget,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
  273       queriesCount++;
  274 
  275       if (type == QType::DS || type == QType::DNSKEY) {
  276         if (domain == addTarget) {
  277           DNSName auth(domain);
  278           /* no DS for com, auth will be . */
  279           auth.chopOff();
  280           return genericDSAndDNSKEYHandler(res, domain, auth, type, keys, false);
  281         }
  282         return genericDSAndDNSKEYHandler(res, domain, domain, type, keys, false);
  283       }
  284       else {
  285         if (domain == target && type == QType::A) {
  286           setLWResult(res, 0, true, false, true);
  287           addRecordToLW(res, target, QType::A, "192.0.2.1");
  288           addRRSIG(keys, res->d_records, DNSName("."), 300);
  289           addRecordToLW(res, addTarget, QType::A, "192.0.2.42", DNSResourceRecord::ADDITIONAL);
  290           /* no RRSIG for the additional record */
  291           return 1;
  292         } else if (domain == addTarget && type == QType::A) {
  293           setLWResult(res, 0, true, false, true);
  294           addRecordToLW(res, addTarget, QType::A, "192.0.2.42");
  295           addRRSIG(keys, res->d_records, DNSName("."), 300);
  296           return 1;
  297         }
  298       }
  299 
  300       return 0;
  301     });
  302 
  303   vector<DNSRecord> ret;
  304   /* first query for target/A, will pick up the additional record as non-auth / unvalidated */
  305   sr->setDNSSECValidationRequested(false);
  306   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
  307   BOOST_CHECK_EQUAL(res, RCode::NoError);
  308   BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
  309   BOOST_CHECK_EQUAL(ret.size(), 2);
  310   for (const auto& record : ret) {
  311     BOOST_CHECK(record.d_type == QType::RRSIG || record.d_type == QType::A);
  312   }
  313   BOOST_CHECK_EQUAL(queriesCount, 1);
  314 
  315   ret.clear();
  316   /* ask for the additional record directly, we should not use
  317      the non-auth one and issue a new query, properly validated */
  318   sr->setDNSSECValidationRequested(true);
  319   res = sr->beginResolve(addTarget, QType(QType::A), QClass::IN, ret);
  320   BOOST_CHECK_EQUAL(res, RCode::NoError);
  321   BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
  322   BOOST_CHECK_EQUAL(ret.size(), 2);
  323   for (const auto& record : ret) {
  324     BOOST_CHECK(record.d_type == QType::RRSIG || record.d_type == QType::A);
  325   }
  326   BOOST_CHECK_EQUAL(queriesCount, 5);
  327 }
  328 
  329 BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_negcache_secure) {
  330   /*
  331     Validation is optional, and the first query does not ask for it,
  332     so the answer is negatively cached as Indeterminate.
  333     The second query asks for validation, answer should be marked as
  334     Secure.
  335   */
  336   std::unique_ptr<SyncRes> sr;
  337   initSR(sr, true);
  338 
  339   setDNSSECValidation(sr, DNSSECMode::Process);
  340 
  341   primeHints();
  342   const DNSName target("com.");
  343   testkeysset_t keys;
  344 
  345   auto luaconfsCopy = g_luaconfs.getCopy();
  346   luaconfsCopy.dsAnchors.clear();
  347   generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors);
  348   generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys);
  349   g_luaconfs.setState(luaconfsCopy);
  350 
  351   size_t queriesCount = 0;
  352 
  353   sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
  354       queriesCount++;
  355 
  356       DNSName auth = domain;
  357       auth.chopOff();
  358 
  359       if (type == QType::DS || type == QType::DNSKEY) {
  360         return genericDSAndDNSKEYHandler(res, domain, auth, type, keys);
  361       }
  362       else {
  363         setLWResult(res, RCode::NoError, true, false, true);
  364         addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
  365         addRRSIG(keys, res->d_records, domain, 300);
  366         addNSECRecordToLW(domain, DNSName("z."), { QType::NSEC, QType::RRSIG }, 600, res->d_records);
  367         addRRSIG(keys, res->d_records, domain, 1);
  368         return 1;
  369       }
  370 
  371       return 0;
  372     });
  373 
  374   vector<DNSRecord> ret;
  375   /* first query does not require validation */
  376   sr->setDNSSECValidationRequested(false);
  377   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
  378   BOOST_CHECK_EQUAL(res, RCode::NoError);
  379   BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
  380   BOOST_REQUIRE_EQUAL(ret.size(), 4);
  381   BOOST_CHECK_EQUAL(queriesCount, 1);
  382   /* check that the entry has not been negatively cached */
  383   const NegCache::NegCacheEntry* ne = nullptr;
  384   BOOST_CHECK_EQUAL(SyncRes::t_sstorage.negcache.size(), 1);
  385   BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), &ne), true);
  386   BOOST_CHECK_EQUAL(ne->d_validationState, Indeterminate);
  387   BOOST_CHECK_EQUAL(ne->authoritySOA.records.size(), 1);
  388   BOOST_CHECK_EQUAL(ne->authoritySOA.signatures.size(), 1);
  389   BOOST_CHECK_EQUAL(ne->DNSSECRecords.records.size(), 1);
  390   BOOST_CHECK_EQUAL(ne->DNSSECRecords.signatures.size(), 1);
  391 
  392   ret.clear();
  393   /* second one _does_ require validation */
  394   sr->setDNSSECValidationRequested(true);
  395   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
  396   BOOST_CHECK_EQUAL(res, RCode::NoError);
  397   BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
  398   BOOST_REQUIRE_EQUAL(ret.size(), 4);
  399   BOOST_CHECK_EQUAL(queriesCount, 4);
  400   BOOST_CHECK_EQUAL(SyncRes::t_sstorage.negcache.size(), 1);
  401   BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), &ne), true);
  402   BOOST_CHECK_EQUAL(ne->d_validationState, Secure);
  403   BOOST_CHECK_EQUAL(ne->authoritySOA.records.size(), 1);
  404   BOOST_CHECK_EQUAL(ne->authoritySOA.signatures.size(), 1);
  405   BOOST_CHECK_EQUAL(ne->DNSSECRecords.records.size(), 1);
  406   BOOST_CHECK_EQUAL(ne->DNSSECRecords.signatures.size(), 1);
  407 }
  408 
  409 BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_negcache_secure_ds) {
  410   /*
  411     Validation is optional, and the first query does not ask for it,
  412     so the answer is negatively cached as Indeterminate.
  413     The second query asks for validation, answer should be marked as
  414     Secure.
  415     The difference with test_dnssec_validation_from_negcache_secure is
  416     that have one more level here, so we are going to look for the proof
  417     that the DS does not exist for the last level. Since there is no cut,
  418     we should accept the fact that the NSEC denies DS and NS both.
  419   */
  420   std::unique_ptr<SyncRes> sr;
  421   initSR(sr, true);
  422 
  423   setDNSSECValidation(sr, DNSSECMode::Process);
  424 
  425   primeHints();
  426   const DNSName target("www.com.");
  427   testkeysset_t keys;
  428 
  429   auto luaconfsCopy = g_luaconfs.getCopy();
  430   luaconfsCopy.dsAnchors.clear();
  431   generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors);
  432   generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys);
  433   g_luaconfs.setState(luaconfsCopy);
  434 
  435   size_t queriesCount = 0;
  436 
  437   sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
  438       queriesCount++;
  439 
  440       if (type == QType::DS || type == QType::DNSKEY) {
  441         if (domain == target) {
  442           /* there is no cut */
  443           return genericDSAndDNSKEYHandler(res, domain, domain, type, keys, false);
  444         }
  445         return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
  446       }
  447 
  448       return 0;
  449     });
  450 
  451   vector<DNSRecord> ret;
  452   /* first query does not require validation */
  453   sr->setDNSSECValidationRequested(false);
  454   int res = sr->beginResolve(target, QType(QType::DS), QClass::IN, ret);
  455   BOOST_CHECK_EQUAL(res, RCode::NoError);
  456   BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
  457   BOOST_REQUIRE_EQUAL(ret.size(), 4);
  458   BOOST_CHECK_EQUAL(queriesCount, 1);
  459 
  460   ret.clear();
  461   /* second one _does_ require validation */
  462   sr->setDNSSECValidationRequested(true);
  463   res = sr->beginResolve(target, QType(QType::DS), QClass::IN, ret);
  464   BOOST_CHECK_EQUAL(res, RCode::NoError);
  465   BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
  466   BOOST_REQUIRE_EQUAL(ret.size(), 4);
  467   BOOST_CHECK_EQUAL(queriesCount, 4);
  468 }
  469 
  470 BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_negcache_insecure) {
  471   /*
  472     Validation is optional, and the first query does not ask for it,
  473     so the answer is negatively cached as Indeterminate.
  474     The second query asks for validation, answer should be marked as
  475     Insecure.
  476   */
  477   std::unique_ptr<SyncRes> sr;
  478   initSR(sr, true);
  479 
  480   setDNSSECValidation(sr, DNSSECMode::Process);
  481 
  482   primeHints();
  483   const DNSName target("com.");
  484   testkeysset_t keys;
  485 
  486   auto luaconfsCopy = g_luaconfs.getCopy();
  487   luaconfsCopy.dsAnchors.clear();
  488   g_luaconfs.setState(luaconfsCopy);
  489 
  490   size_t queriesCount = 0;
  491 
  492   sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
  493       queriesCount++;
  494 
  495       DNSName auth = domain;
  496       auth.chopOff();
  497 
  498       if (type == QType::DS || type == QType::DNSKEY) {
  499         return genericDSAndDNSKEYHandler(res, domain, auth, type, keys);
  500       }
  501       else {
  502         setLWResult(res, RCode::NoError, true, false, true);
  503         addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
  504         return 1;
  505       }
  506 
  507       return 0;
  508     });
  509 
  510   vector<DNSRecord> ret;
  511   /* first query does not require validation */
  512   sr->setDNSSECValidationRequested(false);
  513   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
  514   BOOST_CHECK_EQUAL(res, RCode::NoError);
  515   BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
  516   BOOST_REQUIRE_EQUAL(ret.size(), 1);
  517   BOOST_CHECK_EQUAL(queriesCount, 1);
  518   /* check that the entry has not been negatively cached */
  519   const NegCache::NegCacheEntry* ne = nullptr;
  520   BOOST_CHECK_EQUAL(SyncRes::t_sstorage.negcache.size(), 1);
  521   BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), &ne), true);
  522   BOOST_CHECK_EQUAL(ne->d_validationState, Indeterminate);
  523   BOOST_CHECK_EQUAL(ne->authoritySOA.records.size(), 1);
  524   BOOST_CHECK_EQUAL(ne->authoritySOA.signatures.size(), 0);
  525   BOOST_CHECK_EQUAL(ne->DNSSECRecords.records.size(), 0);
  526   BOOST_CHECK_EQUAL(ne->DNSSECRecords.signatures.size(), 0);
  527 
  528   ret.clear();
  529   /* second one _does_ require validation */
  530   sr->setDNSSECValidationRequested(true);
  531   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
  532   BOOST_CHECK_EQUAL(res, RCode::NoError);
  533   BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
  534   BOOST_REQUIRE_EQUAL(ret.size(), 1);
  535   BOOST_CHECK_EQUAL(queriesCount, 1);
  536   BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), &ne), true);
  537   BOOST_CHECK_EQUAL(ne->d_validationState, Insecure);
  538   BOOST_CHECK_EQUAL(ne->authoritySOA.records.size(), 1);
  539   BOOST_CHECK_EQUAL(ne->authoritySOA.signatures.size(), 0);
  540   BOOST_CHECK_EQUAL(ne->DNSSECRecords.records.size(), 0);
  541   BOOST_CHECK_EQUAL(ne->DNSSECRecords.signatures.size(), 0);
  542 }
  543 
  544 BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_negcache_bogus) {
  545   /*
  546     Validation is optional, and the first query does not ask for it,
  547     so the answer is negatively cached as Indeterminate.
  548     The second query asks for validation, answer should be marked as
  549     Bogus.
  550   */
  551   std::unique_ptr<SyncRes> sr;
  552   initSR(sr, true);
  553 
  554   setDNSSECValidation(sr, DNSSECMode::Process);
  555 
  556   primeHints();
  557   const DNSName target("com.");
  558   testkeysset_t keys;
  559 
  560   auto luaconfsCopy = g_luaconfs.getCopy();
  561   luaconfsCopy.dsAnchors.clear();
  562   generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors);
  563   generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys);
  564   g_luaconfs.setState(luaconfsCopy);
  565 
  566   size_t queriesCount = 0;
  567 
  568   sr->setAsyncCallback([target,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
  569       queriesCount++;
  570 
  571       DNSName auth = domain;
  572       auth.chopOff();
  573 
  574       if (type == QType::DS || type == QType::DNSKEY) {
  575         return genericDSAndDNSKEYHandler(res, domain, auth, type, keys);
  576       }
  577       else {
  578         setLWResult(res, RCode::NoError, true, false, true);
  579         addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 86400);
  580         addRRSIG(keys, res->d_records, domain, 86400);
  581         /* no denial */
  582         return 1;
  583       }
  584 
  585       return 0;
  586     });
  587 
  588   SyncRes::s_maxbogusttl = 60;
  589   SyncRes::s_maxnegttl = 3600;
  590   const auto now = sr->getNow().tv_sec;
  591 
  592   vector<DNSRecord> ret;
  593   /* first query does not require validation */
  594   sr->setDNSSECValidationRequested(false);
  595   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
  596   BOOST_CHECK_EQUAL(res, RCode::NoError);
  597   BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
  598   BOOST_REQUIRE_EQUAL(ret.size(), 2);
  599   for (const auto& record : ret) {
  600     if (record.d_type == QType::SOA) {
  601       BOOST_CHECK_EQUAL(record.d_ttl, SyncRes::s_maxnegttl);
  602     }
  603   }
  604   BOOST_CHECK_EQUAL(queriesCount, 1);
  605   const NegCache::NegCacheEntry* ne = nullptr;
  606   BOOST_CHECK_EQUAL(SyncRes::t_sstorage.negcache.size(), 1);
  607   BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), &ne), true);
  608   BOOST_CHECK_EQUAL(ne->d_validationState, Indeterminate);
  609   BOOST_CHECK_EQUAL(ne->authoritySOA.records.size(), 1);
  610   BOOST_CHECK_EQUAL(ne->authoritySOA.signatures.size(), 1);
  611   BOOST_CHECK_EQUAL(ne->d_ttd, now + SyncRes::s_maxnegttl);
  612   BOOST_CHECK_EQUAL(ne->DNSSECRecords.records.size(), 0);
  613   BOOST_CHECK_EQUAL(ne->DNSSECRecords.signatures.size(), 0);
  614 
  615   ret.clear();
  616   /* second one _does_ require validation */
  617   sr->setDNSSECValidationRequested(true);
  618   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
  619   BOOST_CHECK_EQUAL(res, RCode::NoError);
  620   BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
  621   BOOST_REQUIRE_EQUAL(ret.size(), 2);
  622   for (const auto& record : ret) {
  623     BOOST_CHECK_EQUAL(record.d_ttl, SyncRes::s_maxbogusttl);
  624   }
  625   BOOST_CHECK_EQUAL(queriesCount, 4);
  626   BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), &ne), true);
  627   BOOST_CHECK_EQUAL(ne->d_validationState, Bogus);
  628   BOOST_CHECK_EQUAL(ne->authoritySOA.records.size(), 1);
  629   BOOST_CHECK_EQUAL(ne->authoritySOA.signatures.size(), 1);
  630   BOOST_CHECK_EQUAL(ne->d_ttd, now + SyncRes::s_maxbogusttl);
  631   BOOST_CHECK_EQUAL(ne->DNSSECRecords.records.size(), 0);
  632   BOOST_CHECK_EQUAL(ne->DNSSECRecords.signatures.size(), 0);
  633 
  634   ret.clear();
  635   /* third one _does_ not require validation, we just check that
  636      the cache (status and TTL) has been correctly updated */
  637   sr->setDNSSECValidationRequested(false);
  638   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
  639   BOOST_CHECK_EQUAL(res, RCode::NoError);
  640   BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
  641   BOOST_REQUIRE_EQUAL(ret.size(), 2);
  642   for (const auto& record : ret) {
  643     BOOST_CHECK_EQUAL(record.d_ttl, SyncRes::s_maxbogusttl);
  644   }
  645   BOOST_CHECK_EQUAL(queriesCount, 4);
  646   BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), &ne), true);
  647   BOOST_CHECK_EQUAL(ne->d_validationState, Bogus);
  648   BOOST_CHECK_EQUAL(ne->authoritySOA.records.size(), 1);
  649   BOOST_CHECK_EQUAL(ne->authoritySOA.signatures.size(), 1);
  650   BOOST_CHECK_EQUAL(ne->d_ttd, now + SyncRes::s_maxbogusttl);
  651   BOOST_CHECK_EQUAL(ne->DNSSECRecords.records.size(), 0);
  652   BOOST_CHECK_EQUAL(ne->DNSSECRecords.signatures.size(), 0);
  653 }
  654 
  655 BOOST_AUTO_TEST_CASE(test_lowercase_outgoing) {
  656   g_lowercaseOutgoing = true;
  657   std::unique_ptr<SyncRes> sr;
  658   initSR(sr);
  659 
  660   primeHints();
  661 
  662   vector<DNSName> sentOutQnames;
  663 
  664   const DNSName target("WWW.POWERDNS.COM");
  665   const DNSName cname("WWW.PowerDNS.org");
  666 
  667   sr->setAsyncCallback([target, cname, &sentOutQnames](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
  668 
  669       sentOutQnames.push_back(domain);
  670 
  671       if (isRootServer(ip)) {
  672         if (domain == target) {
  673           setLWResult(res, 0, false, false, true);
  674           addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
  675           addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
  676           return 1;
  677         }
  678         if (domain == cname) {
  679           setLWResult(res, 0, false, false, true);
  680           addRecordToLW(res, "powerdns.org.", QType::NS, "pdns-public-ns1.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800);
  681           addRecordToLW(res, "pdns-public-ns1.powerdns.org.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
  682           return 1;
  683         }
  684       } else if (ip == ComboAddress("192.0.2.1:53")) {
  685         if (domain == target) {
  686           setLWResult(res, 0, true, false, false);
  687           addRecordToLW(res, domain, QType::CNAME, cname.toString());
  688           return 1;
  689         }
  690       } else if (ip == ComboAddress("192.0.2.2:53")) {
  691         if (domain == cname) {
  692           setLWResult(res, 0, true, false, false);
  693           addRecordToLW(res, domain, QType::A, "127.0.0.1");
  694           return 1;
  695         }
  696       }
  697       return 0;
  698   });
  699 
  700   vector<DNSRecord> ret;
  701   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
  702 
  703   BOOST_CHECK_EQUAL(res, RCode::NoError);
  704 
  705   BOOST_REQUIRE_EQUAL(ret.size(), 2);
  706   BOOST_CHECK_EQUAL(ret[0].d_content->getZoneRepresentation(), cname.toString());
  707 
  708   BOOST_REQUIRE_EQUAL(sentOutQnames.size(), 4);
  709   BOOST_CHECK_EQUAL(sentOutQnames[0].toString(), target.makeLowerCase().toString());
  710   BOOST_CHECK_EQUAL(sentOutQnames[1].toString(), target.makeLowerCase().toString());
  711   BOOST_CHECK_EQUAL(sentOutQnames[2].toString(), cname.makeLowerCase().toString());
  712   BOOST_CHECK_EQUAL(sentOutQnames[3].toString(), cname.makeLowerCase().toString());
  713 
  714   g_lowercaseOutgoing = false;
  715 }
  716 
  717 BOOST_AUTO_TEST_CASE(test_getDSRecords_multialgo) {
  718   std::unique_ptr<SyncRes> sr;
  719   initSR(sr, true);
  720 
  721   setDNSSECValidation(sr, DNSSECMode::ValidateAll);
  722 
  723   primeHints();
  724   const DNSName target("com.");
  725   testkeysset_t keys, keys2;
  726 
  727   auto luaconfsCopy = g_luaconfs.getCopy();
  728   luaconfsCopy.dsAnchors.clear();
  729   generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors);
  730   generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys);
  731   g_luaconfs.setState(luaconfsCopy);
  732 
  733   // As testkeysset_t only contains one DSRecordContent, create another one with a different hash algo
  734   generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA1, keys2);
  735   // But add the existing root key otherwise no RRSIG can be created
  736   auto rootkey = keys.find(g_rootdnsname);
  737   keys2.insert(*rootkey);
  738 
  739   sr->setAsyncCallback([target, keys, keys2](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
  740       DNSName auth = domain;
  741       auth.chopOff();
  742       if (type == QType::DS || type == QType::DNSKEY) {
  743         if (domain == target) {
  744           if (genericDSAndDNSKEYHandler(res, domain, auth, type, keys2) != 1) {
  745             return 0;
  746           }
  747         }
  748         return genericDSAndDNSKEYHandler(res, domain, auth, type, keys);
  749       }
  750       return 0;
  751     });
  752 
  753   dsmap_t ds;
  754   auto state = sr->getDSRecords(target, ds, false, 0, false);
  755   BOOST_CHECK_EQUAL(state, Secure);
  756   BOOST_REQUIRE_EQUAL(ds.size(), 1);
  757   for (const auto& i : ds) {
  758     BOOST_CHECK_EQUAL(i.d_digesttype, DNSSECKeeper::SHA256);
  759   }
  760 }
  761 
  762 BOOST_AUTO_TEST_CASE(test_getDSRecords_multialgo_all_sha) {
  763   std::unique_ptr<SyncRes> sr;
  764   initSR(sr, true);
  765 
  766   setDNSSECValidation(sr, DNSSECMode::ValidateAll);
  767 
  768   primeHints();
  769   const DNSName target("com.");
  770   testkeysset_t keys, keys2, keys3;
  771 
  772   auto luaconfsCopy = g_luaconfs.getCopy();
  773   luaconfsCopy.dsAnchors.clear();
  774   generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors);
  775   generateKeyMaterial(target, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys);
  776   g_luaconfs.setState(luaconfsCopy);
  777 
  778   // As testkeysset_t only contains one DSRecordContent, create another one with a different hash algo
  779   generateKeyMaterial(target, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA1, keys2);
  780   // But add the existing root key otherwise no RRSIG can be created
  781   auto rootkey = keys.find(g_rootdnsname);
  782   keys2.insert(*rootkey);
  783 
  784   generateKeyMaterial(target, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA384, keys3);
  785   // But add the existing root key otherwise no RRSIG can be created
  786   keys3.insert(*rootkey);
  787 
  788   sr->setAsyncCallback([target, keys, keys2, keys3](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
  789       DNSName auth = domain;
  790       auth.chopOff();
  791       if (type == QType::DS || type == QType::DNSKEY) {
  792         if (domain == target) {
  793           if (genericDSAndDNSKEYHandler(res, domain, auth, type, keys2) != 1) {
  794             return 0;
  795           }
  796           if (genericDSAndDNSKEYHandler(res, domain, auth, type, keys3) != 1) {
  797             return 0;
  798           }
  799         }
  800         return genericDSAndDNSKEYHandler(res, domain, auth, type, keys);
  801       }
  802       return 0;
  803     });
  804 
  805   dsmap_t ds;
  806   auto state = sr->getDSRecords(target, ds, false, 0, false);
  807   BOOST_CHECK_EQUAL(state, Secure);
  808   BOOST_REQUIRE_EQUAL(ds.size(), 1);
  809   for (const auto& i : ds) {
  810     BOOST_CHECK_EQUAL(i.d_digesttype, DNSSECKeeper::SHA384);
  811   }
  812 }
  813 
  814 BOOST_AUTO_TEST_CASE(test_getDSRecords_multialgo_two_highest) {
  815   std::unique_ptr<SyncRes> sr;
  816   initSR(sr, true);
  817 
  818   setDNSSECValidation(sr, DNSSECMode::ValidateAll);
  819 
  820   primeHints();
  821   const DNSName target("com.");
  822   testkeysset_t keys, keys2, keys3;
  823 
  824   auto luaconfsCopy = g_luaconfs.getCopy();
  825   luaconfsCopy.dsAnchors.clear();
  826   generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors);
  827   generateKeyMaterial(target, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys);
  828   g_luaconfs.setState(luaconfsCopy);
  829 
  830   // As testkeysset_t only contains one DSRecordContent, create another one with a different hash algo
  831   generateKeyMaterial(target, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys2);
  832   // But add the existing root key otherwise no RRSIG can be created
  833   auto rootkey = keys.find(g_rootdnsname);
  834   keys2.insert(*rootkey);
  835 
  836   generateKeyMaterial(target, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA1, keys3);
  837   // But add the existing root key otherwise no RRSIG can be created
  838   keys3.insert(*rootkey);
  839 
  840   sr->setAsyncCallback([target, keys, keys2, keys3](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
  841       DNSName auth = domain;
  842       auth.chopOff();
  843       if (type == QType::DS || type == QType::DNSKEY) {
  844         if (domain == target) {
  845           if (genericDSAndDNSKEYHandler(res, domain, auth, type, keys2) != 1) {
  846             return 0;
  847           }
  848           if (genericDSAndDNSKEYHandler(res, domain, auth, type, keys3) != 1) {
  849             return 0;
  850           }
  851         }
  852         return genericDSAndDNSKEYHandler(res, domain, auth, type, keys);
  853       }
  854       return 0;
  855     });
  856 
  857   dsmap_t ds;
  858   auto state = sr->getDSRecords(target, ds, false, 0, false);
  859   BOOST_CHECK_EQUAL(state, Secure);
  860   BOOST_REQUIRE_EQUAL(ds.size(), 2);
  861   for (const auto& i : ds) {
  862     BOOST_CHECK_EQUAL(i.d_digesttype, DNSSECKeeper::SHA256);
  863   }
  864 }
  865 
  866 BOOST_AUTO_TEST_CASE(test_cname_plus_authority_ns_ttl) {
  867   std::unique_ptr<SyncRes> sr;
  868   initSR(sr);
  869 
  870   primeHints();
  871 
  872   const DNSName target("cname.powerdns.com.");
  873   const DNSName cnameTarget("cname-target.powerdns.com");
  874   size_t queriesCount = 0;
  875 
  876   sr->setAsyncCallback([target, cnameTarget, &queriesCount](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
  877 
  878        queriesCount++;
  879 
  880        if (isRootServer(ip)) {
  881         setLWResult(res, 0, false, false, true);
  882         addRecordToLW(res, DNSName("powerdns.com"), QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 42);
  883         addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
  884         return 1;
  885       } else if (ip == ComboAddress("192.0.2.1:53")) {
  886          if (domain == target) {
  887           setLWResult(res, 0, true, false, false);
  888           addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString());
  889           addRecordToLW(res, cnameTarget, QType::A, "192.0.2.2");
  890           addRecordToLW(res, DNSName("powerdns.com."), QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
  891           addRecordToLW(res, DNSName("a.gtld-servers.net."), QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
  892           return 1;
  893         }
  894         else if (domain == cnameTarget) {
  895           setLWResult(res, 0, true, false, false);
  896           addRecordToLW(res, domain, QType::A, "192.0.2.2");
  897         }
  898 
  899          return 1;
  900       }
  901 
  902       return 0;
  903     });
  904 
  905   const time_t now = sr->getNow().tv_sec;
  906   vector<DNSRecord> ret;
  907   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
  908   BOOST_CHECK_EQUAL(res, RCode::NoError);
  909   BOOST_REQUIRE_EQUAL(ret.size(), 2);
  910   BOOST_CHECK(ret[0].d_type == QType::CNAME);
  911   BOOST_CHECK_EQUAL(ret[0].d_name, target);
  912   BOOST_CHECK(ret[1].d_type == QType::A);
  913   BOOST_CHECK_EQUAL(ret[1].d_name, cnameTarget);
  914 
  915   /* check that the NS in authority has not replaced the one in the cache
  916      with auth=0 (or at least has not raised the TTL since it could otherwise
  917      be used to create a never-ending ghost zone even after the NS have been
  918      changed in the parent.
  919   */
  920   const ComboAddress who;
  921   vector<DNSRecord> cached;
  922   bool wasAuth = false;
  923 
  924   auto ttl = t_RC->get(now, DNSName("powerdns.com."), QType(QType::NS), false, &cached, who, nullptr, nullptr, nullptr, nullptr, &wasAuth);
  925   BOOST_REQUIRE_GE(ttl, 1);
  926   BOOST_REQUIRE_LE(ttl, 42);
  927   BOOST_CHECK_EQUAL(cached.size(), 1);
  928   BOOST_CHECK_EQUAL(wasAuth, false);
  929 
  930   cached.clear();
  931 
  932   /* Also check that the the part in additional is still not auth */
  933   BOOST_REQUIRE_GE(t_RC->get(now, DNSName("a.gtld-servers.net."), QType(QType::A), false, &cached, who, nullptr, nullptr, nullptr, nullptr, &wasAuth), -1);
  934   BOOST_CHECK_EQUAL(cached.size(), 1);
  935   BOOST_CHECK_EQUAL(wasAuth, false);
  936 }
  937 
  938 BOOST_AUTO_TEST_CASE(test_records_sanitization_general) {
  939   std::unique_ptr<SyncRes> sr;
  940   initSR(sr);
  941 
  942   primeHints();
  943 
  944   const DNSName target("sanitization.powerdns.com.");
  945 
  946   sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
  947 
  948       setLWResult(res, 0, true, false, true);
  949       addRecordToLW(res, domain, QType::A, "192.0.2.1");
  950       /* should be scrubbed because it doesn't match the QType */
  951       addRecordToLW(res, domain, QType::AAAA, "2001:db8::1");
  952       /* should be scrubbed because the DNAME is not relevant to the qname */
  953       addRecordToLW(res, DNSName("not-sanitization.powerdns.com."), QType::DNAME, "not-sanitization.powerdns.net.");
  954       /* should be scrubbed because a MX has no reason to show up in AUTHORITY */
  955       addRecordToLW(res, domain, QType::MX, "10 mx.powerdns.com.", DNSResourceRecord::AUTHORITY);
  956       /* should be scrubbed because the SOA name is not relevant to the qname */
  957       addRecordToLW(res, DNSName("not-sanitization.powerdns.com."), QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY);
  958       /* should be scrubbed because types other than A or AAAA are not really supposed to show up in ADDITIONAL */
  959       addRecordToLW(res, domain, QType::TXT, "TXT", DNSResourceRecord::ADDITIONAL);
  960       /* should be scrubbed because it doesn't match any of the accepted names in this answer (mostly 'domain') */
  961       addRecordToLW(res, DNSName("powerdns.com."), QType::AAAA, "2001:db8::1", DNSResourceRecord::ADDITIONAL);
  962       return 1;
  963     });
  964 
  965   const time_t now = sr->getNow().tv_sec;
  966 
  967   vector<DNSRecord> ret;
  968   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
  969   BOOST_CHECK_EQUAL(res, RCode::NoError);
  970   BOOST_REQUIRE_EQUAL(ret.size(), 1);
  971 
  972   const ComboAddress who;
  973   vector<DNSRecord> cached;
  974   BOOST_CHECK_GT(t_RC->get(now, target, QType(QType::A), true, &cached, who), 0);
  975   cached.clear();
  976   BOOST_CHECK_LT(t_RC->get(now, target, QType(QType::AAAA), true, &cached, who), 0);
  977   BOOST_CHECK_EQUAL(t_RC->get(now, DNSName("not-sanitization.powerdns.com."), QType(QType::DNAME), true, &cached, who), -1);
  978   BOOST_CHECK_LT(t_RC->get(now, target, QType(QType::MX), true, &cached, who), 0);
  979   BOOST_CHECK_EQUAL(t_RC->get(now, DNSName("not-sanitization.powerdns.com."), QType(QType::SOA), true, &cached, who), -1);
  980   BOOST_CHECK_LT(t_RC->get(now, target, QType(QType::TXT), false, &cached, who), 0);
  981   BOOST_CHECK_EQUAL(t_RC->get(now, DNSName("powerdns.com."), QType(QType::AAAA), false, &cached, who), -1);
  982 }
  983 
  984 BOOST_AUTO_TEST_CASE(test_records_sanitization_keep_relevant_additional_aaaa) {
  985   std::unique_ptr<SyncRes> sr;
  986   initSR(sr);
  987 
  988   primeHints();
  989 
  990   const DNSName target("sanitization.powerdns.com.");
  991 
  992   sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
  993 
  994       setLWResult(res, 0, true, false, true);
  995       addRecordToLW(res, domain, QType::A, "192.0.2.1");
  996       addRecordToLW(res, domain, QType::AAAA, "2001:db8::1", DNSResourceRecord::ADDITIONAL);
  997       return 1;
  998     });
  999 
 1000   const time_t now = sr->getNow().tv_sec;
 1001 
 1002   vector<DNSRecord> ret;
 1003   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
 1004   BOOST_CHECK_EQUAL(res, RCode::NoError);
 1005   BOOST_REQUIRE_EQUAL(ret.size(), 1);
 1006 
 1007   const ComboAddress who;
 1008   vector<DNSRecord> cached;
 1009   BOOST_CHECK_GT(t_RC->get(now, target, QType(QType::A), true, &cached, who), 0);
 1010   cached.clear();
 1011   /* not auth since it was in the additional section */
 1012   BOOST_CHECK_LT(t_RC->get(now, target, QType(QType::AAAA), true, &cached, who), 0);
 1013   BOOST_CHECK_GT(t_RC->get(now, target, QType(QType::AAAA), false, &cached, who), 0);
 1014 }
 1015 
 1016 BOOST_AUTO_TEST_CASE(test_records_sanitization_keep_glue) {
 1017   std::unique_ptr<SyncRes> sr;
 1018   initSR(sr);
 1019 
 1020   primeHints();
 1021 
 1022   const DNSName target("sanitization-glue.powerdns.com.");
 1023 
 1024   size_t queriesCount = 0;
 1025 
 1026   sr->setAsyncCallback([target,&queriesCount](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
 1027 
 1028       queriesCount++;
 1029 
 1030       if (isRootServer(ip)) {
 1031         setLWResult(res, 0, false, false, true);
 1032         addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
 1033         addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
 1034         addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
 1035         return 1;
 1036       }
 1037       else if (ip == ComboAddress("192.0.2.1:53") || ip == ComboAddress("[2001:DB8::1]:53")) {
 1038         setLWResult(res, 0, false, false, true);
 1039         addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
 1040         addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
 1041         addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 172800);
 1042         addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:DB8::2", DNSResourceRecord::ADDITIONAL, 172800);
 1043         addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3", DNSResourceRecord::ADDITIONAL, 172800);
 1044         addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:DB8::3", DNSResourceRecord::ADDITIONAL, 172800);
 1045         return 1;
 1046       }
 1047       else if (ip == ComboAddress("192.0.2.2:53") || ip == ComboAddress("192.0.2.3:53") || ip == ComboAddress("[2001:DB8::2]:53") || ip == ComboAddress("[2001:DB8::3]:53")) {
 1048         setLWResult(res, 0, true, false, true);
 1049         addRecordToLW(res, target, QType::A, "192.0.2.4");
 1050         return 1;
 1051       }
 1052       else {
 1053         return 0;
 1054       }
 1055     });
 1056 
 1057   const time_t now = sr->getNow().tv_sec;
 1058 
 1059   vector<DNSRecord> ret;
 1060   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
 1061   BOOST_CHECK_EQUAL(res, RCode::NoError);
 1062   BOOST_CHECK_EQUAL(ret.size(), 1);
 1063   BOOST_CHECK_EQUAL(queriesCount, 3);
 1064 
 1065   const ComboAddress who;
 1066   vector<DNSRecord> cached;
 1067   BOOST_CHECK_GT(t_RC->get(now, target, QType(QType::A), true, &cached, who), 0);
 1068   cached.clear();
 1069 
 1070   BOOST_CHECK_GT(t_RC->get(now, DNSName("com."), QType(QType::NS), false, &cached, who), 0);
 1071   BOOST_CHECK_GT(t_RC->get(now, DNSName("a.gtld-servers.net."), QType(QType::A), false, &cached, who), 0);
 1072   BOOST_CHECK_GT(t_RC->get(now, DNSName("a.gtld-servers.net."), QType(QType::AAAA), false, &cached, who), 0);
 1073   BOOST_CHECK_GT(t_RC->get(now, DNSName("powerdns.com."), QType(QType::NS), false, &cached, who), 0);
 1074   BOOST_CHECK_GT(t_RC->get(now, DNSName("pdns-public-ns1.powerdns.com."), QType(QType::A), false, &cached, who), 0);
 1075   BOOST_CHECK_GT(t_RC->get(now, DNSName("pdns-public-ns1.powerdns.com."), QType(QType::AAAA), false, &cached, who), 0);
 1076   BOOST_CHECK_GT(t_RC->get(now, DNSName("pdns-public-ns2.powerdns.com."), QType(QType::A), false, &cached, who), 0);
 1077   BOOST_CHECK_GT(t_RC->get(now, DNSName("pdns-public-ns2.powerdns.com."), QType(QType::AAAA), false, &cached, who), 0);
 1078 }
 1079 
 1080 BOOST_AUTO_TEST_CASE(test_records_sanitization_scrubs_ns_nxd) {
 1081   std::unique_ptr<SyncRes> sr;
 1082   initSR(sr);
 1083 
 1084   primeHints();
 1085 
 1086   const DNSName target("sanitization-ns-nxd.powerdns.com.");
 1087 
 1088   sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
 1089 
 1090      setLWResult(res, RCode::NXDomain, true, false, true);
 1091      addRecordToLW(res, "powerdns.com.", QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY);
 1092      addRecordToLW(res, "powerdns.com.", QType::NS, "spoofed.ns.", DNSResourceRecord::AUTHORITY, 172800);
 1093      addRecordToLW(res, "spoofed.ns.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
 1094      addRecordToLW(res, "spoofed.ns.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
 1095      return 1;
 1096     });
 1097 
 1098   const time_t now = sr->getNow().tv_sec;
 1099 
 1100   vector<DNSRecord> ret;
 1101   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
 1102   BOOST_CHECK_EQUAL(res, RCode::NXDomain);
 1103   BOOST_CHECK_EQUAL(ret.size(), 1);
 1104 
 1105   const ComboAddress who;
 1106   vector<DNSRecord> cached;
 1107   BOOST_CHECK_GT(t_RC->get(now, DNSName("powerdns.com."), QType(QType::SOA), true, &cached, who), 0);
 1108   cached.clear();
 1109 
 1110   BOOST_CHECK_LT(t_RC->get(now, DNSName("powerdns.com."), QType(QType::NS), false, &cached, who), 0);
 1111   BOOST_CHECK_LT(t_RC->get(now, DNSName("spoofed.ns."), QType(QType::A), false, &cached, who), 0);
 1112   BOOST_CHECK_LT(t_RC->get(now, DNSName("spoofed.ns."), QType(QType::AAAA), false, &cached, who), 0);
 1113 }
 1114 
 1115 BOOST_AUTO_TEST_SUITE_END()