You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1220 lines
50 KiB

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