BOOST_CHECK_EQUAL(res, 0);
}
+BOOST_AUTO_TEST_CASE(test_tc_over_tcp) {
+ std::unique_ptr<SyncRes> sr;
+ init();
+ initSR(sr, true, false);
+
+ size_t tcpQueriesCount = 0;
+
+ sr->setAsyncCallback([&tcpQueriesCount](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, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+ if (!doTCP) {
+ setLWResult(res, 0, true, true, false);
+ return 1;
+ }
+
+ /* first TCP query is answered with a TC response */
+ tcpQueriesCount++;
+ if (tcpQueriesCount == 1) {
+ setLWResult(res, 0, true, true, false);
+ }
+ else {
+ setLWResult(res, 0, true, false, false);
+ }
+
+ addRecordToLW(res, domain, QType::A, "192.0.2.1");
+ return 1;
+ });
+
+ primeHints();
+
+ vector<DNSRecord> ret;
+ int res = sr->beginResolve(DNSName("powerdns.com."), QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, 0);
+ BOOST_CHECK_EQUAL(tcpQueriesCount, 2);
+}
+
BOOST_AUTO_TEST_CASE(test_all_nss_down) {
std::unique_ptr<SyncRes> sr;
init();
BOOST_CHECK_EQUAL(ret.size(), 1);
}
+BOOST_AUTO_TEST_CASE(test_completely_flawed_nsset) {
+ std::unique_ptr<SyncRes> sr;
+ init();
+ initSR(sr, true, false);
+
+ primeHints();
+
+ const DNSName target("powerdns.com.");
+ size_t queriesCount = 0;
+
+ sr->setAsyncCallback([&queriesCount,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, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+ queriesCount++;
+
+ if (isRootServer(ip) && domain == target) {
+ setLWResult(res, 0, true, false, true);
+ addRecordToLW(res, domain, QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
+ addRecordToLW(res, domain, QType::NS, "pdns-public-ns3.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
+ return 1;
+ } else if (domain == DNSName("pdns-public-ns2.powerdns.com.") || domain == DNSName("pdns-public-ns3.powerdns.com.")){
+ setLWResult(res, 0, true, false, true);
+ addRecordToLW(res, ".", QType::SOA, "a.root-servers.net. nstld.verisign-grs.com. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
+ return 1;
+ }
+
+ return 0;
+ });
+
+ vector<DNSRecord> ret;
+ int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::ServFail);
+ BOOST_CHECK_EQUAL(ret.size(), 0);
+ /* one query to get NSs, then A and AAAA for each NS */
+ BOOST_CHECK_EQUAL(queriesCount, 5);
+}
+
BOOST_AUTO_TEST_CASE(test_cache_hit) {
std::unique_ptr<SyncRes> sr;
init();
BOOST_CHECK(sr->wasOutOfBand());
}
+BOOST_AUTO_TEST_CASE(test_auth_zone) {
+ std::unique_ptr<SyncRes> sr;
+ init();
+ initSR(sr, true, false);
+
+ primeHints();
+
+ size_t queriesCount = 0;
+ const DNSName target("powerdns.com.");
+ const ComboAddress addr("192.0.2.5");
+
+ SyncRes::AuthDomain ad;
+ ad.d_name = target;
+ DNSRecord dr;
+ dr.d_place = DNSResourceRecord::ANSWER;
+ dr.d_name = target;
+ dr.d_type = QType::SOA;
+ dr.d_ttl = 3600;
+ dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
+ ad.d_records.insert(dr);
+
+ dr.d_place = DNSResourceRecord::ANSWER;
+ dr.d_name = target;
+ dr.d_type = QType::A;
+ dr.d_ttl = 3600;
+ dr.d_content = std::make_shared<ARecordContent>(addr);
+ ad.d_records.insert(dr);
+
+ auto map = std::make_shared<SyncRes::domainmap_t>();
+ (*map)[target] = ad;
+ SyncRes::setDomainMap(map);
+
+ sr->setAsyncCallback([&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, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+ queriesCount++;
+ setLWResult(res, 0, true, false, true);
+ addRecordToLW(res, domain, QType::A, "192.0.2.42");
+ return 1;
+ });
+
+ vector<DNSRecord> ret;
+ int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, 0);
+ BOOST_CHECK_EQUAL(ret.size(), 1);
+ BOOST_CHECK(ret[0].d_type == QType::A);
+ BOOST_CHECK_EQUAL(getRR<ARecordContent>(ret[0])->getCA().toString(), addr.toString());
+ BOOST_CHECK_EQUAL(queriesCount, 0);
+}
+
+BOOST_AUTO_TEST_CASE(test_auth_zone_cname_lead_to_oob) {
+ std::unique_ptr<SyncRes> sr;
+ init();
+ initSR(sr, true, false);
+
+ primeHints();
+
+ size_t queriesCount = 0;
+ const DNSName target("powerdns.com.");
+ const DNSName authZone("internal.powerdns.com.");
+ const ComboAddress addr("192.0.2.5");
+
+ SyncRes::AuthDomain ad;
+ ad.d_name = authZone;
+ DNSRecord dr;
+ dr.d_place = DNSResourceRecord::ANSWER;
+ dr.d_name = authZone;
+ dr.d_type = QType::SOA;
+ dr.d_ttl = 3600;
+ dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
+ ad.d_records.insert(dr);
+
+ dr.d_place = DNSResourceRecord::ANSWER;
+ dr.d_name = authZone;
+ dr.d_type = QType::A;
+ dr.d_ttl = 3600;
+ dr.d_content = std::make_shared<ARecordContent>(addr);
+ ad.d_records.insert(dr);
+
+ auto map = std::make_shared<SyncRes::domainmap_t>();
+ (*map)[authZone] = ad;
+ SyncRes::setDomainMap(map);
+
+ sr->setAsyncCallback([&queriesCount,target,authZone](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, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+ queriesCount++;
+
+ if (domain == target) {
+ setLWResult(res, 0, true, false, true);
+ addRecordToLW(res, target, QType::CNAME, authZone.toString(), DNSResourceRecord::ANSWER, 3600);
+ return 1;
+ }
+
+ return 0;
+ });
+
+ vector<DNSRecord> ret;
+ int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, 0);
+ BOOST_CHECK_EQUAL(ret.size(), 2);
+ BOOST_CHECK(ret[0].d_type == QType::CNAME);
+ BOOST_CHECK_EQUAL(getRR<CNAMERecordContent>(ret[0])->getTarget().toString(), authZone.toString());
+ BOOST_CHECK(ret[1].d_type == QType::A);
+ BOOST_CHECK_EQUAL(getRR<ARecordContent>(ret[1])->getCA().toString(), addr.toString());
+ BOOST_CHECK_EQUAL(queriesCount, 1);
+}
+
+BOOST_AUTO_TEST_CASE(test_auth_zone_oob_lead_to_outgoing_queryb) {
+ std::unique_ptr<SyncRes> sr;
+ init();
+ initSR(sr, true, false);
+
+ primeHints();
+
+ size_t queriesCount = 0;
+ const DNSName target("powerdns.com.");
+ const DNSName externalCNAME("www.open-xchange.com.");
+ const ComboAddress addr("192.0.2.5");
+
+ SyncRes::AuthDomain ad;
+ ad.d_name = target;
+ DNSRecord dr;
+ dr.d_place = DNSResourceRecord::ANSWER;
+ dr.d_name = target;
+ dr.d_type = QType::SOA;
+ dr.d_ttl = 3600;
+ dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
+ ad.d_records.insert(dr);
+
+ dr.d_place = DNSResourceRecord::ANSWER;
+ dr.d_name = target;
+ dr.d_type = QType::CNAME;
+ dr.d_ttl = 3600;
+ dr.d_content = std::make_shared<CNAMERecordContent>(externalCNAME);
+ ad.d_records.insert(dr);
+
+ auto map = std::make_shared<SyncRes::domainmap_t>();
+ (*map)[target] = ad;
+ SyncRes::setDomainMap(map);
+
+ sr->setAsyncCallback([&queriesCount,externalCNAME,addr](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, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+ queriesCount++;
+
+ if (domain == externalCNAME) {
+ setLWResult(res, 0, true, false, true);
+ addRecordToLW(res, externalCNAME, QType::A, addr.toString(), DNSResourceRecord::ANSWER, 3600);
+ return 1;
+ }
+
+ return 0;
+ });
+
+ vector<DNSRecord> ret;
+ int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, 0);
+ BOOST_CHECK_EQUAL(ret.size(), 2);
+ BOOST_CHECK(ret[0].d_type == QType::CNAME);
+ BOOST_CHECK_EQUAL(getRR<CNAMERecordContent>(ret[0])->getTarget().toString(), externalCNAME.toString());
+ BOOST_CHECK(ret[1].d_type == QType::A);
+ BOOST_CHECK_EQUAL(getRR<ARecordContent>(ret[1])->getCA().toString(), addr.toString());
+ BOOST_CHECK_EQUAL(queriesCount, 1);
+}
+
+BOOST_AUTO_TEST_CASE(test_auth_zone_nodata) {
+ std::unique_ptr<SyncRes> sr;
+ init();
+ initSR(sr, true, false);
+
+ primeHints();
+
+ size_t queriesCount = 0;
+ const DNSName target("nodata.powerdns.com.");
+ const DNSName authZone("powerdns.com");
+
+ SyncRes::AuthDomain ad;
+ ad.d_name = authZone;
+ DNSRecord dr;
+ dr.d_place = DNSResourceRecord::ANSWER;
+ dr.d_name = target;
+ dr.d_type = QType::A;
+ dr.d_ttl = 3600;
+ dr.d_content = std::make_shared<ARecordContent>(ComboAddress("192.0.2.1"));
+ ad.d_records.insert(dr);
+
+ dr.d_place = DNSResourceRecord::ANSWER;
+ dr.d_name = authZone;
+ dr.d_type = QType::SOA;
+ dr.d_ttl = 3600;
+ dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
+ ad.d_records.insert(dr);
+
+ auto map = std::make_shared<SyncRes::domainmap_t>();
+ (*map)[authZone] = ad;
+ SyncRes::setDomainMap(map);
+
+ sr->setAsyncCallback([&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, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+ queriesCount++;
+
+ return 0;
+ });
+
+ vector<DNSRecord> ret;
+ int res = sr->beginResolve(target, QType(QType::AAAA), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, 0);
+ BOOST_REQUIRE_EQUAL(ret.size(), 1);
+ BOOST_CHECK(ret[0].d_type == QType::SOA);
+ BOOST_CHECK_EQUAL(queriesCount, 0);
+}
+
+BOOST_AUTO_TEST_CASE(test_auth_zone_nx) {
+ std::unique_ptr<SyncRes> sr;
+ init();
+ initSR(sr, true, false);
+
+ primeHints();
+
+ size_t queriesCount = 0;
+ const DNSName target("nx.powerdns.com.");
+ const DNSName authZone("powerdns.com");
+
+ SyncRes::AuthDomain ad;
+ ad.d_name = authZone;
+ DNSRecord dr;
+ dr.d_place = DNSResourceRecord::ANSWER;
+ dr.d_name = DNSName("powerdns.com.");
+ dr.d_type = QType::SOA;
+ dr.d_ttl = 3600;
+ dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
+ ad.d_records.insert(dr);
+
+ auto map = std::make_shared<SyncRes::domainmap_t>();
+ (*map)[authZone] = ad;
+ SyncRes::setDomainMap(map);
+
+ sr->setAsyncCallback([&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, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+ queriesCount++;
+
+ return 0;
+ });
+
+ vector<DNSRecord> ret;
+ int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::NXDomain);
+ BOOST_REQUIRE_EQUAL(ret.size(), 1);
+ BOOST_CHECK(ret[0].d_type == QType::SOA);
+ BOOST_CHECK_EQUAL(queriesCount, 0);
+}
+
+BOOST_AUTO_TEST_CASE(test_auth_zone_delegation) {
+ std::unique_ptr<SyncRes> sr;
+ init();
+ initSR(sr, true, false);
+
+ primeHints();
+
+ size_t queriesCount = 0;
+ const DNSName target("www.test.powerdns.com.");
+ const ComboAddress targetAddr("192.0.2.2");
+ const DNSName ns("ns1.test.powerdns.com.");
+ const ComboAddress nsAddr("192.0.2.1");
+ const DNSName authZone("powerdns.com");
+
+ SyncRes::AuthDomain ad;
+ ad.d_name = authZone;
+ DNSRecord dr;
+ dr.d_place = DNSResourceRecord::ANSWER;
+ dr.d_name = authZone;
+ dr.d_type = QType::SOA;
+ dr.d_ttl = 3600;
+ dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
+ ad.d_records.insert(dr);
+
+ dr.d_place = DNSResourceRecord::ANSWER;
+ dr.d_name = DNSName("test.powerdns.com.");
+ dr.d_type = QType::NS;
+ dr.d_ttl = 3600;
+ dr.d_content = std::make_shared<NSRecordContent>(ns);
+ ad.d_records.insert(dr);
+
+ dr.d_place = DNSResourceRecord::ANSWER;
+ dr.d_name = ns;
+ dr.d_type = QType::A;
+ dr.d_ttl = 3600;
+ dr.d_content = std::make_shared<ARecordContent>(nsAddr);
+ ad.d_records.insert(dr);
+
+ auto map = std::make_shared<SyncRes::domainmap_t>();
+ (*map)[authZone] = ad;
+ SyncRes::setDomainMap(map);
+
+ sr->setAsyncCallback([&queriesCount,target,targetAddr,nsAddr](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, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+ queriesCount++;
+ if (ip == ComboAddress(nsAddr.toString(), 53) && domain == target) {
+ setLWResult(res, 0, true, false, true);
+ addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
+ return 1;
+ }
+
+ return 0;
+ });
+
+ vector<DNSRecord> ret;
+ int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, 0);
+ BOOST_REQUIRE_EQUAL(ret.size(), 1);
+ BOOST_CHECK(ret[0].d_type == QType::A);
+ BOOST_CHECK_EQUAL(queriesCount, 1);
+}
+
+BOOST_AUTO_TEST_CASE(test_auth_zone_delegation_point) {
+ std::unique_ptr<SyncRes> sr;
+ init();
+ initSR(sr, true, false);
+
+ primeHints();
+
+ size_t queriesCount = 0;
+ const DNSName target("test.powerdns.com.");
+ const ComboAddress targetAddr("192.0.2.2");
+ const DNSName ns("ns1.test.powerdns.com.");
+ const ComboAddress nsAddr("192.0.2.1");
+ const DNSName authZone("powerdns.com");
+
+ SyncRes::AuthDomain ad;
+ ad.d_name = authZone;
+ DNSRecord dr;
+ dr.d_place = DNSResourceRecord::ANSWER;
+ dr.d_name = authZone;
+ dr.d_type = QType::SOA;
+ dr.d_ttl = 3600;
+ dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
+ ad.d_records.insert(dr);
+
+ dr.d_place = DNSResourceRecord::ANSWER;
+ dr.d_name = DNSName("test.powerdns.com.");
+ dr.d_type = QType::NS;
+ dr.d_ttl = 3600;
+ dr.d_content = std::make_shared<NSRecordContent>(ns);
+ ad.d_records.insert(dr);
+
+ dr.d_place = DNSResourceRecord::ANSWER;
+ dr.d_name = ns;
+ dr.d_type = QType::A;
+ dr.d_ttl = 3600;
+ dr.d_content = std::make_shared<ARecordContent>(nsAddr);
+ ad.d_records.insert(dr);
+
+ auto map = std::make_shared<SyncRes::domainmap_t>();
+ (*map)[authZone] = ad;
+ SyncRes::setDomainMap(map);
+
+ sr->setAsyncCallback([&queriesCount,nsAddr,target,targetAddr](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, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+ queriesCount++;
+
+ if (ip == ComboAddress(nsAddr.toString(), 53) && domain == target) {
+ setLWResult(res, 0, true, false, true);
+ addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
+ return 1;
+ }
+
+ return 0;
+ });
+
+ vector<DNSRecord> ret;
+ int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, 0);
+ BOOST_REQUIRE_EQUAL(ret.size(), 1);
+ BOOST_CHECK(ret[0].d_type == QType::A);
+ BOOST_CHECK_EQUAL(queriesCount, 1);
+}
+
+BOOST_AUTO_TEST_CASE(test_auth_zone_wildcard) {
+ std::unique_ptr<SyncRes> sr;
+ init();
+ initSR(sr, true, false);
+
+ primeHints();
+
+ size_t queriesCount = 0;
+ const DNSName target("test.powerdns.com.");
+ const ComboAddress targetAddr("192.0.2.2");
+ const DNSName authZone("powerdns.com");
+
+ SyncRes::AuthDomain ad;
+ ad.d_name = authZone;
+ DNSRecord dr;
+ dr.d_place = DNSResourceRecord::ANSWER;
+ dr.d_name = authZone;
+ dr.d_type = QType::SOA;
+ dr.d_ttl = 3600;
+ dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
+ ad.d_records.insert(dr);
+
+ dr.d_place = DNSResourceRecord::ANSWER;
+ dr.d_name = DNSName("*.powerdns.com.");
+ dr.d_type = QType::A;
+ dr.d_ttl = 3600;
+ dr.d_content = std::make_shared<ARecordContent>(targetAddr);
+ ad.d_records.insert(dr);
+
+ auto map = std::make_shared<SyncRes::domainmap_t>();
+ (*map)[authZone] = ad;
+ SyncRes::setDomainMap(map);
+
+ sr->setAsyncCallback([&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, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+ queriesCount++;
+
+ return 0;
+ });
+
+ vector<DNSRecord> ret;
+ int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, 0);
+ BOOST_REQUIRE_EQUAL(ret.size(), 1);
+ BOOST_CHECK(ret[0].d_type == QType::A);
+ BOOST_CHECK_EQUAL(queriesCount, 0);
+}
+
+BOOST_AUTO_TEST_CASE(test_auth_zone_wildcard_nodata) {
+ std::unique_ptr<SyncRes> sr;
+ init();
+ initSR(sr, true, false);
+
+ primeHints();
+
+ size_t queriesCount = 0;
+ const DNSName target("test.powerdns.com.");
+ const ComboAddress targetAddr("192.0.2.2");
+ const DNSName authZone("powerdns.com");
+
+ SyncRes::AuthDomain ad;
+ ad.d_name = authZone;
+ DNSRecord dr;
+ dr.d_place = DNSResourceRecord::ANSWER;
+ dr.d_name = authZone;
+ dr.d_type = QType::SOA;
+ dr.d_ttl = 3600;
+ dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
+ ad.d_records.insert(dr);
+
+ dr.d_place = DNSResourceRecord::ANSWER;
+ dr.d_name = DNSName("*.powerdns.com.");
+ dr.d_type = QType::A;
+ dr.d_ttl = 3600;
+ dr.d_content = std::make_shared<ARecordContent>(targetAddr);
+ ad.d_records.insert(dr);
+
+ auto map = std::make_shared<SyncRes::domainmap_t>();
+ (*map)[authZone] = ad;
+ SyncRes::setDomainMap(map);
+
+ sr->setAsyncCallback([&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, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+ queriesCount++;
+
+ return 0;
+ });
+
+ vector<DNSRecord> ret;
+ int res = sr->beginResolve(target, QType(QType::AAAA), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, 0);
+ BOOST_REQUIRE_EQUAL(ret.size(), 1);
+ BOOST_CHECK(ret[0].d_type == QType::SOA);
+ BOOST_CHECK_EQUAL(queriesCount, 0);
+}
+
+BOOST_AUTO_TEST_CASE(test_auth_zone_cache_only) {
+ std::unique_ptr<SyncRes> sr;
+ init();
+ initSR(sr, true, false);
+
+ primeHints();
+
+ size_t queriesCount = 0;
+ const DNSName target("powerdns.com.");
+ const ComboAddress addr("192.0.2.5");
+
+ SyncRes::AuthDomain ad;
+ ad.d_name = target;
+ DNSRecord dr;
+ dr.d_place = DNSResourceRecord::ANSWER;
+ dr.d_name = target;
+ dr.d_type = QType::SOA;
+ dr.d_ttl = 3600;
+ dr.d_content = std::make_shared<SOARecordContent>("pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600");
+ ad.d_records.insert(dr);
+
+ dr.d_place = DNSResourceRecord::ANSWER;
+ dr.d_name = target;
+ dr.d_type = QType::A;
+ dr.d_ttl = 3600;
+ dr.d_content = std::make_shared<ARecordContent>(addr);
+ ad.d_records.insert(dr);
+
+ auto map = std::make_shared<SyncRes::domainmap_t>();
+ (*map)[target] = ad;
+ SyncRes::setDomainMap(map);
+
+ sr->setAsyncCallback([&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, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+ queriesCount++;
+ setLWResult(res, 0, true, false, true);
+ addRecordToLW(res, domain, QType::A, "192.0.2.42");
+ return 1;
+ });
+
+ /* simulate a no-RD query */
+ sr->setCacheOnly();
+
+ vector<DNSRecord> ret;
+ int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, 0);
+ BOOST_CHECK_EQUAL(ret.size(), 1);
+ BOOST_CHECK(ret[0].d_type == QType::A);
+ BOOST_CHECK_EQUAL(getRR<ARecordContent>(ret[0])->getCA().toString(), addr.toString());
+ BOOST_CHECK_EQUAL(queriesCount, 0);
+}
+
/*
// cerr<<"asyncresolve called to ask "<<ip.toStringWithPort()<<" about "<<domain.toString()<<" / "<<QType(type).getName()<<" over "<<(doTCP ? "TCP" : "UDP")<<" (rd: "<<sendRDQuery<<", EDNS0 level: "<<EDNS0Level<<")"<<endl;
thread_local SyncRes::ThreadLocalStorage SyncRes::t_sstorage;
+std::unordered_set<DNSName> SyncRes::s_delegationOnly;
+std::unique_ptr<NetmaskGroup> SyncRes::s_dontQuery{nullptr};
+NetmaskGroup SyncRes::s_ednssubnets;
+SuffixMatchNode SyncRes::s_ednsdomains;
+string SyncRes::s_serverID;
+SyncRes::LogMode SyncRes::s_lm;
+
unsigned int SyncRes::s_maxnegttl;
unsigned int SyncRes::s_maxcachettl;
+unsigned int SyncRes::s_maxqperq;
+unsigned int SyncRes::s_maxtotusec;
+unsigned int SyncRes::s_maxdepth;
+unsigned int SyncRes::s_minimumTTL;
unsigned int SyncRes::s_packetcachettl;
unsigned int SyncRes::s_packetcacheservfailttl;
unsigned int SyncRes::s_serverdownmaxfails;
std::atomic<uint64_t> SyncRes::s_unreachables;
uint8_t SyncRes::s_ecsipv4limit;
uint8_t SyncRes::s_ecsipv6limit;
-unsigned int SyncRes::s_minimumTTL;
bool SyncRes::s_doIPv6;
bool SyncRes::s_nopacketcache;
bool SyncRes::s_rootNXTrust;
-unsigned int SyncRes::s_maxqperq;
-unsigned int SyncRes::s_maxtotusec;
-unsigned int SyncRes::s_maxdepth;
-string SyncRes::s_serverID;
-SyncRes::LogMode SyncRes::s_lm;
-std::unordered_set<DNSName> SyncRes::s_delegationOnly;
-std::unique_ptr<NetmaskGroup> SyncRes::s_dontQuery{nullptr};
-NetmaskGroup SyncRes::s_ednssubnets;
-SuffixMatchNode SyncRes::s_ednsdomains;
+bool SyncRes::s_noEDNS;
#define LOG(x) if(d_lm == Log) { L <<Logger::Warning << x; } else if(d_lm == Store) { d_trace << x; }
-bool SyncRes::s_noEDNS;
-
static void accountAuthLatency(int usec, int family)
{
if(family == AF_INET) {
//! This is the 'out of band resolver', in other words, the authoritative server
-bool SyncRes::doOOBResolve(const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, unsigned int depth, int& res)
+void SyncRes::AuthDomain::addSOA(std::vector<DNSRecord>& records) const
{
- string prefix;
- if(doLog()) {
- prefix=d_prefix;
- prefix.append(depth, ' ');
+ SyncRes::AuthDomain::records_t::const_iterator ziter = d_records.find(boost::make_tuple(getName(), QType::SOA));
+ if (ziter != d_records.end()) {
+ DNSRecord dr = *ziter;
+ dr.d_place = DNSResourceRecord::AUTHORITY;
+ records.push_back(dr);
+ }
+ else {
+ // cerr<<qname<<": can't find SOA record '"<<getName()<<"' in our zone!"<<endl;
}
+}
- LOG(prefix<<qname<<": checking auth storage for '"<<qname<<"|"<<qtype.getName()<<"'"<<endl);
- DNSName authdomain(qname);
+int SyncRes::AuthDomain::getRecords(const DNSName& qname, uint16_t qtype, std::vector<DNSRecord>& records) const
+{
+ int result = RCode::NoError;
+ records.clear();
- domainmap_t::const_iterator iter=getBestAuthZone(&authdomain);
- if(iter==t_sstorage.domainmap->end()) {
- LOG(prefix<<qname<<": auth storage has no zone for this query!"<<endl);
- return false;
- }
- LOG(prefix<<qname<<": auth storage has data, zone='"<<authdomain<<"'"<<endl);
- pair<AuthDomain::records_t::const_iterator, AuthDomain::records_t::const_iterator> range;
-
- range=iter->second.d_records.equal_range(tie(qname)); // partial lookup
-
- ret.clear();
- AuthDomain::records_t::const_iterator ziter;
- bool somedata=false;
- for(ziter=range.first; ziter!=range.second; ++ziter) {
- somedata=true;
- if(qtype.getCode()==QType::ANY || ziter->d_type==qtype.getCode() || ziter->d_type==QType::CNAME) // let rest of nameserver do the legwork on this one
- ret.push_back(*ziter);
- else if(ziter->d_type == QType::NS && ziter->d_name.countLabels() > authdomain.countLabels()) { // we hit a delegation point!
- DNSRecord dr=*ziter;
+ // partial lookup
+ std::pair<records_t::const_iterator,records_t::const_iterator> range = d_records.equal_range(tie(qname));
+
+ SyncRes::AuthDomain::records_t::const_iterator ziter;
+ bool somedata = false;
+
+ for(ziter = range.first; ziter != range.second; ++ziter) {
+ somedata = true;
+
+ if(qtype == QType::ANY || ziter->d_type == qtype || ziter->d_type == QType::CNAME) {
+ // let rest of nameserver do the legwork on this one
+ records.push_back(*ziter);
+ }
+ else if (ziter->d_type == QType::NS && ziter->d_name.countLabels() > getName().countLabels()) {
+ // we hit a delegation point!
+ DNSRecord dr = *ziter;
dr.d_place=DNSResourceRecord::AUTHORITY;
- ret.push_back(dr);
+ records.push_back(dr);
}
}
- if(!ret.empty()) {
- LOG(prefix<<qname<<": exact match in zone '"<<authdomain<<"'"<<endl);
- res=0;
- return true;
+
+ if (!records.empty()) {
+ /* We have found an exact match, we're done */
+ // cerr<<qname<<": exact match in zone '"<<getName()<<"'"<<endl;
+ return result;
}
- if(somedata) {
- LOG(prefix<<qname<<": found record in '"<<authdomain<<"', but nothing of the right type, sending SOA"<<endl);
- ziter=iter->second.d_records.find(boost::make_tuple(authdomain, QType::SOA));
- if(ziter!=iter->second.d_records.end()) {
- DNSRecord dr=*ziter;
- dr.d_place=DNSResourceRecord::AUTHORITY;
- ret.push_back(dr);
- }
- else
- LOG(prefix<<qname<<": can't find SOA record '"<<authdomain<<"' in our zone!"<<endl);
- res=RCode::NoError;
- return true;
+
+ if (somedata) {
+ /* We have records for that name, but not of the wanted qtype */
+ // cerr<<qname<<": found record in '"<<getName()<<"', but nothing of the right type, sending SOA"<<endl;
+ addSOA(records);
+
+ return result;
}
- LOG(prefix<<qname<<": nothing found so far in '"<<authdomain<<"', trying wildcards"<<endl);
+ // cerr<<qname<<": nothing found so far in '"<<getName()<<"', trying wildcards"<<endl;
DNSName wcarddomain(qname);
- while(wcarddomain != iter->first && wcarddomain.chopOff()) {
- LOG(prefix<<qname<<": trying '*."<<wcarddomain<<"' in "<<authdomain<<endl);
- range=iter->second.d_records.equal_range(boost::make_tuple(g_wildcarddnsname+wcarddomain));
- if(range.first==range.second)
+ while(wcarddomain != getName() && wcarddomain.chopOff()) {
+ // cerr<<qname<<": trying '*."<<wcarddomain<<"' in "<<getName()<<endl;
+ range = d_records.equal_range(boost::make_tuple(g_wildcarddnsname + wcarddomain));
+ if (range.first==range.second)
continue;
- for(ziter=range.first; ziter!=range.second; ++ziter) {
- DNSRecord dr=*ziter;
+ for(ziter = range.first; ziter != range.second; ++ziter) {
+ DNSRecord dr = *ziter;
// if we hit a CNAME, just answer that - rest of recursor will do the needful & follow
- if(dr.d_type == qtype.getCode() || qtype.getCode() == QType::ANY || dr.d_type == QType::CNAME) {
+ if(dr.d_type == qtype || qtype == QType::ANY || dr.d_type == QType::CNAME) {
dr.d_name = qname;
- dr.d_place=DNSResourceRecord::ANSWER;
- ret.push_back(dr);
+ dr.d_place = DNSResourceRecord::ANSWER;
+ records.push_back(dr);
}
}
- LOG(prefix<<qname<<": in '"<<authdomain<<"', had wildcard match on '*."<<wcarddomain<<"'"<<endl);
- res=RCode::NoError;
- return true;
+
+ if (records.empty()) {
+ addSOA(records);
+ }
+
+ // cerr<<qname<<": in '"<<getName()<<"', had wildcard match on '*."<<wcarddomain<<"'"<<endl;
+ return result;
}
+ /* Nothing for this name, no wildcard, let's see if there is some NS */
DNSName nsdomain(qname);
-
- while(nsdomain.chopOff() && nsdomain != iter->first) {
- range=iter->second.d_records.equal_range(boost::make_tuple(nsdomain,QType::NS));
- if(range.first==range.second)
+ while (nsdomain.chopOff() && nsdomain != getName()) {
+ range = d_records.equal_range(boost::make_tuple(nsdomain,QType::NS));
+ if(range.first == range.second)
continue;
- for(ziter=range.first; ziter!=range.second; ++ziter) {
- DNSRecord dr=*ziter;
- dr.d_place=DNSResourceRecord::AUTHORITY;
- ret.push_back(dr);
+ for(ziter = range.first; ziter != range.second; ++ziter) {
+ DNSRecord dr = *ziter;
+ dr.d_place = DNSResourceRecord::AUTHORITY;
+ records.push_back(dr);
}
}
- if(ret.empty()) {
- LOG(prefix<<qname<<": no NS match in zone '"<<authdomain<<"' either, handing out SOA"<<endl);
- ziter=iter->second.d_records.find(boost::make_tuple(authdomain, QType::SOA));
- if(ziter!=iter->second.d_records.end()) {
- DNSRecord dr=*ziter;
- dr.d_place=DNSResourceRecord::AUTHORITY;
- ret.push_back(dr);
- }
- else {
- LOG(prefix<<qname<<": can't find SOA record '"<<authdomain<<"' in our zone!"<<endl);
- }
- res=RCode::NXDomain;
+
+ if(records.empty()) {
+ // cerr<<qname<<": no NS match in zone '"<<getName()<<"' either, handing out SOA"<<endl;
+ addSOA(records);
+ result = RCode::NXDomain;
}
- else
- res=0;
+ return result;
+}
+
+bool SyncRes::doOOBResolve(const AuthDomain& domain, const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, int& res) const
+{
+ res = domain.getRecords(qname, qtype.getCode(), ret);
return true;
}
+bool SyncRes::doOOBResolve(const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, unsigned int depth, int& res)
+{
+ string prefix;
+ if(doLog()) {
+ prefix=d_prefix;
+ prefix.append(depth, ' ');
+ }
+
+ DNSName authdomain(qname);
+ domainmap_t::const_iterator iter=getBestAuthZone(&authdomain);
+ if(iter==t_sstorage.domainmap->end() || !iter->second.isAuth()) {
+ LOG(prefix<<qname<<": auth storage has no zone for this query!"<<endl);
+ return false;
+ }
+
+ LOG(prefix<<qname<<": auth storage has data, zone='"<<authdomain<<"'"<<endl);
+ return doOOBResolve(iter->second, qname, qtype, ret, res);
+}
+
void SyncRes::doEDNSDumpAndClose(int fd)
{
FILE* fp=fdopen(fd, "w");
DNSName authname(qname);
domainmap_t::const_iterator iter=getBestAuthZone(&authname);
if(iter != t_sstorage.domainmap->end()) {
- const vector<ComboAddress>& servers = iter->second.d_servers;
- if(servers.empty()) {
+ if(iter->second.isAuth()) {
ret.clear();
d_wasOutOfBand = doOOBResolve(qname, qtype, ret, depth, res);
return res;
}
else {
+ const vector<ComboAddress>& servers = iter->second.d_servers;
const ComboAddress remoteIP = servers.front();
LOG(prefix<<qname<<": forwarding query to hardcoded nameserver '"<< remoteIP.toStringWithPort()<<"' for zone '"<<authname<<"'"<<endl);
domainmap_t::const_iterator iter=getBestAuthZone(&authdomain);
if(iter!=t_sstorage.domainmap->end()) {
- if( iter->second.d_servers.empty() )
+ if( iter->second.isAuth() )
// this gets picked up in doResolveAt, the empty DNSName, combined with the
// empty vector means 'we are auth for this zone'
nsset.insert({DNSName(), {{}, false}});
// Again, picked up in doResolveAt. An empty DNSName, combined with a
// non-empty vector of ComboAddresses means 'this is a forwarded domain'
// This is actually picked up in retrieveAddressesForNS called from doResolveAt.
- nsset.insert({DNSName(), {iter->second.d_servers, iter->second.d_rdForward}});
+ nsset.insert({DNSName(), {iter->second.d_servers, iter->second.shouldRecurse() }});
}
return authdomain;
}