#ifndef BOOST_TEST_DYN_LINK #define BOOST_TEST_DYN_LINK #endif #define BOOST_TEST_NO_MAIN #include #include "dnsdist-svc.hh" #include "svc-records.hh" #include "dnsparser.hh" BOOST_AUTO_TEST_SUITE(dnsdistsvc_cc) BOOST_AUTO_TEST_CASE(test_Basic) { DNSName target("powerdns.com."); { // invalid priority of 0 + parameters std::vector payload; const uint16_t priority = 0; BOOST_CHECK(!generateSVCPayload(payload, priority, target, {SvcParam::SvcParamKey::port}, {"dot"}, false, 853, std::string(), {ComboAddress("192.0.2.1")}, {ComboAddress("2001:db8::1")}, {})); } { std::vector payload; const uint16_t priority = 1; BOOST_CHECK(generateSVCPayload(payload, priority, target, {SvcParam::SvcParamKey::port}, {"dot"}, false, 853, std::string(), {ComboAddress("192.0.2.1")}, {ComboAddress("2001:db8::1")}, {})); /* 2 octet field for SvcPriority as an integer in network byte order */ /* uncompressed, fully-qualified TargetName */ /* list of SvcParams as: - 2 octet field containing the SvcParamKey as an integer in network byte order - 2 octet field containing the length of the SvcParamValue as an integer between 0 and 65535 in network byte order (but constrained by the RDATA and DNS message sizes) - an octet string of this length whose contents are in a format determined by the SvcParamKey SvcParamKeys SHALL appear in increasing numeric order */ size_t expectedSize = (/* priority */ 2) + target.wirelength() + (/* mandatory */ 2 + 2 + 2) + (/* alpns with 1-byte length field for each value */ 2 + 2 + 4) + (/* no-alpn-default is false */ 0) + (/* port */ 2 + 2 + 2) + (/* ech */ 0) + (/* v4 hints */ 2 + 2 + 9) + (/* v6 hints */ 2 + 2 + 11); BOOST_CHECK_EQUAL(payload.size(), expectedSize); std::set params; PacketReader pr(std::string_view(reinterpret_cast(payload.data()), payload.size()), 0); BOOST_CHECK_EQUAL(pr.get16BitInt(), priority); /* we can't use getName() directly because it assumes that there has to be a dnsheader before the name */ DNSName parsedTarget(reinterpret_cast(payload.data()), payload.size(), pr.getPosition(), false /* uncompress */, nullptr /* qtype */, nullptr /* qclass */, nullptr /* consumed */, 0); pr.skip(parsedTarget.wirelength()); BOOST_CHECK_EQUAL(target.toString(), parsedTarget.toString()); pr.xfrSvcParamKeyVals(params); BOOST_REQUIRE_EQUAL(params.size(), 5U); auto param = params.begin(); BOOST_CHECK(param->getKey() == SvcParam::SvcParamKey::mandatory); ++param; BOOST_CHECK(param->getKey() == SvcParam::SvcParamKey::alpn); ++param; BOOST_CHECK(param->getKey() == SvcParam::SvcParamKey::port); ++param; BOOST_CHECK(param->getKey() == SvcParam::SvcParamKey::ipv4hint); ++param; BOOST_CHECK(param->getKey() == SvcParam::SvcParamKey::ipv6hint); } { std::vector payload; const uint16_t priority = 2; const std::string ech("whatever"); const std::string dohParam("/dns-query{?dns}"); BOOST_CHECK(generateSVCPayload(payload, priority, target, {SvcParam::SvcParamKey::port}, {"h2"}, true, 443, ech, {ComboAddress("192.0.2.2")}, {ComboAddress("2001:db8::2")}, {std::pair(42, dohParam)})); size_t expectedSize = (/* priority */ 2) + target.wirelength() + (/* mandatory */ 2 + 2 + 2) + (/* alpns */ 2 + 2 + 3) + (/* no-alpn-default is true */ 2 + 2) + (/* port */ 2 + 2 + 2) + (/* ech */ 2 + 2 + ech.size()) + (/* v4 hints */ 2 + 2 + 9) + (/* v6 hints */ 2 + 2 + 11) + (/* doh parameter */ 2 + 2 + dohParam.size()); BOOST_CHECK_EQUAL(payload.size(), expectedSize); std::set params; PacketReader pr(std::string_view(reinterpret_cast(payload.data()), payload.size()), 0); BOOST_CHECK_EQUAL(pr.get16BitInt(), priority); /* we can't use getName() directly because it assumes that there has to be a dnsheader before the name */ DNSName parsedTarget(reinterpret_cast(payload.data()), payload.size(), pr.getPosition(), false /* uncompress */, nullptr /* qtype */, nullptr /* qclass */, nullptr /* consumed */, 0); pr.skip(parsedTarget.wirelength()); BOOST_CHECK_EQUAL(target.toString(), parsedTarget.toString()); pr.xfrSvcParamKeyVals(params); BOOST_REQUIRE_EQUAL(params.size(), 8U); auto param = params.begin(); BOOST_CHECK(param->getKey() == SvcParam::SvcParamKey::mandatory); ++param; BOOST_CHECK(param->getKey() == SvcParam::SvcParamKey::alpn); ++param; BOOST_CHECK(param->getKey() == SvcParam::SvcParamKey::no_default_alpn); ++param; BOOST_CHECK(param->getKey() == SvcParam::SvcParamKey::port); ++param; BOOST_CHECK(param->getKey() == SvcParam::SvcParamKey::ipv4hint); ++param; BOOST_CHECK(param->getKey() == SvcParam::SvcParamKey::ech); ++param; BOOST_CHECK(param->getKey() == SvcParam::SvcParamKey::ipv6hint); ++param; BOOST_CHECK_EQUAL(static_cast(param->getKey()), 42U); } } BOOST_AUTO_TEST_CASE(test_Parsing) { svcParamsLua_t params; params["mandatory"] = std::vector>({ {1, "port"}, }); params["alpn"] = std::vector>({ {1, "h2"}, }); params["noDefaultAlpn"] = static_cast(true); params["port"] = static_cast(443); params["ipv4hint"] = std::vector>({ {1, "192.0.2.1"}, }); params["ipv6hint"] = std::vector>({ {1, "2001:db8::1"}, }); params["ech"] = std::string("test"); auto parsed = parseSVCParameters(params); BOOST_CHECK(parsed.mandatoryParams == std::set{SvcParam::SvcParamKey::port}); BOOST_CHECK(parsed.alpns == std::vector{"h2"}); BOOST_CHECK(parsed.ipv4hints == std::vector{ComboAddress("192.0.2.1")}); BOOST_CHECK(parsed.ipv6hints == std::vector{ComboAddress("2001:db8::1")}); BOOST_CHECK_EQUAL(parsed.ech, "test"); BOOST_CHECK_EQUAL(*parsed.port, 443); BOOST_CHECK_EQUAL(parsed.noDefaultAlpn, true); } BOOST_AUTO_TEST_SUITE_END()