diff options
Diffstat (limited to 'test-dnsdist-dnsparser.cc')
-rw-r--r-- | test-dnsdist-dnsparser.cc | 462 |
1 files changed, 462 insertions, 0 deletions
diff --git a/test-dnsdist-dnsparser.cc b/test-dnsdist-dnsparser.cc new file mode 100644 index 0000000..cc29a19 --- /dev/null +++ b/test-dnsdist-dnsparser.cc @@ -0,0 +1,462 @@ +/* + * This file is part of PowerDNS or dnsdist. + * Copyright -- PowerDNS.COM B.V. and its contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * In addition, for the avoidance of any doubt, permission is granted to + * link this program with OpenSSL and to (re)distribute the binaries + * produced as the result of such linking. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_NO_MAIN + +#include <boost/test/unit_test.hpp> + +#include "dnsdist-dnsparser.hh" +#include "dnswriter.hh" +#include "dnsparser.hh" + +BOOST_AUTO_TEST_SUITE(test_dnsdist_dnsparser) + +BOOST_AUTO_TEST_CASE(test_Query) +{ + const DNSName target("powerdns.com."); + const DNSName newTarget("dnsdist.org."); + const DNSName notTheTarget("not-powerdns.com."); + + { + /* query for the target */ + PacketBuffer query; + GenericDNSPacketWriter<PacketBuffer> pw(query, target, QType::A, QClass::IN, 0); + pw.getHeader()->rd = 1; + pw.getHeader()->id = htons(42); + pw.commit(); + + BOOST_CHECK(dnsdist::changeNameInDNSPacket(query, target, newTarget)); + + MOADNSParser mdp(false, reinterpret_cast<const char*>(query.data()), query.size()); + BOOST_CHECK_EQUAL(mdp.d_qname, newTarget); + BOOST_CHECK_EQUAL(mdp.d_header.qdcount, 1U); + } + + { + /* query smaller than a DNS header */ + PacketBuffer query; + GenericDNSPacketWriter<PacketBuffer> pw(query, target, QType::A, QClass::IN, 0); + pw.getHeader()->rd = 1; + pw.getHeader()->id = htons(42); + pw.commit(); + + query.resize(sizeof(dnsheader) - 1); + BOOST_CHECK(!dnsdist::changeNameInDNSPacket(query, target, newTarget)); + } + + { + /* query for a different name than the target */ + PacketBuffer query; + GenericDNSPacketWriter<PacketBuffer> pw(query, notTheTarget, QType::A, QClass::IN, 0); + pw.getHeader()->rd = 1; + pw.getHeader()->id = htons(42); + pw.commit(); + + BOOST_CHECK(dnsdist::changeNameInDNSPacket(query, target, newTarget)); + + MOADNSParser mdp(false, reinterpret_cast<const char*>(query.data()), query.size()); + BOOST_CHECK_EQUAL(mdp.d_qname, notTheTarget); + BOOST_CHECK_EQUAL(mdp.d_header.qdcount, 1U); + } +} + +BOOST_AUTO_TEST_CASE(test_Response) +{ + const DNSName target("powerdns.com."); + const DNSName newTarget("dnsdist.org."); + const DNSName notTheTarget("not-powerdns.com."); + + { + /* response for the target, A and AAAA */ + PacketBuffer response; + GenericDNSPacketWriter<PacketBuffer> pwR(response, target, QType::A, QClass::IN, 0); + pwR.getHeader()->qr = 1; + pwR.getHeader()->rd = 1; + pwR.getHeader()->ra = 1; + pwR.getHeader()->id = htons(42); + pwR.startRecord(target, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER); + ComboAddress v4("192.0.2.1"); + pwR.xfrCAWithoutPort(4, v4); + pwR.commit(); + pwR.startRecord(target, QType::AAAA, 7200, QClass::IN, DNSResourceRecord::ADDITIONAL); + ComboAddress v6("2001:db8::1"); + pwR.xfrCAWithoutPort(6, v6); + pwR.commit(); + pwR.addOpt(4096, 0, 0); + pwR.commit(); + + BOOST_CHECK(dnsdist::changeNameInDNSPacket(response, target, newTarget)); + + MOADNSParser mdp(false, reinterpret_cast<const char*>(response.data()), response.size()); + BOOST_CHECK_EQUAL(mdp.d_qname, newTarget); + BOOST_CHECK_EQUAL(mdp.d_header.qdcount, 1U); + BOOST_CHECK_EQUAL(mdp.d_header.ancount, 1U); + BOOST_CHECK_EQUAL(mdp.d_header.nscount, 0U); + BOOST_CHECK_EQUAL(mdp.d_header.arcount, 2U); + + BOOST_REQUIRE_EQUAL(mdp.d_answers.size(), 3U); + BOOST_CHECK_EQUAL(mdp.d_answers.at(0).first.d_type, static_cast<uint16_t>(QType::A)); + BOOST_CHECK_EQUAL(mdp.d_answers.at(0).first.d_class, QClass::IN); + BOOST_CHECK_EQUAL(mdp.d_answers.at(0).first.d_name, newTarget); + BOOST_CHECK_EQUAL(mdp.d_answers.at(1).first.d_type, static_cast<uint16_t>(QType::AAAA)); + BOOST_CHECK_EQUAL(mdp.d_answers.at(1).first.d_class, QClass::IN); + BOOST_CHECK_EQUAL(mdp.d_answers.at(1).first.d_name, newTarget); + BOOST_CHECK_EQUAL(mdp.d_answers.at(2).first.d_type, static_cast<uint16_t>(QType::OPT)); + BOOST_CHECK_EQUAL(mdp.d_answers.at(2).first.d_name, g_rootdnsname); + } + + { + /* response with A for the target, AAAA for another name */ + PacketBuffer response; + GenericDNSPacketWriter<PacketBuffer> pwR(response, target, QType::A, QClass::IN, 0); + pwR.getHeader()->qr = 1; + pwR.getHeader()->rd = 1; + pwR.getHeader()->ra = 1; + pwR.getHeader()->id = htons(42); + pwR.startRecord(target, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER); + ComboAddress v4("192.0.2.1"); + pwR.xfrCAWithoutPort(4, v4); + pwR.commit(); + pwR.startRecord(notTheTarget, QType::AAAA, 7200, QClass::IN, DNSResourceRecord::ADDITIONAL); + ComboAddress v6("2001:db8::1"); + pwR.xfrCAWithoutPort(6, v6); + pwR.commit(); + pwR.addOpt(4096, 0, 0); + pwR.commit(); + + BOOST_CHECK(dnsdist::changeNameInDNSPacket(response, target, newTarget)); + + MOADNSParser mdp(false, reinterpret_cast<const char*>(response.data()), response.size()); + BOOST_CHECK_EQUAL(mdp.d_qname, newTarget); + BOOST_CHECK_EQUAL(mdp.d_header.qdcount, 1U); + BOOST_CHECK_EQUAL(mdp.d_header.ancount, 1U); + BOOST_CHECK_EQUAL(mdp.d_header.nscount, 0U); + BOOST_CHECK_EQUAL(mdp.d_header.arcount, 2U); + + BOOST_REQUIRE_EQUAL(mdp.d_answers.size(), 3U); + BOOST_CHECK_EQUAL(mdp.d_answers.at(0).first.d_type, static_cast<uint16_t>(QType::A)); + BOOST_CHECK_EQUAL(mdp.d_answers.at(0).first.d_class, QClass::IN); + BOOST_CHECK_EQUAL(mdp.d_answers.at(0).first.d_name, newTarget); + BOOST_CHECK_EQUAL(mdp.d_answers.at(1).first.d_type, static_cast<uint16_t>(QType::AAAA)); + BOOST_CHECK_EQUAL(mdp.d_answers.at(1).first.d_class, QClass::IN); + BOOST_CHECK_EQUAL(mdp.d_answers.at(1).first.d_name, notTheTarget); + BOOST_CHECK_EQUAL(mdp.d_answers.at(2).first.d_type, static_cast<uint16_t>(QType::OPT)); + BOOST_CHECK_EQUAL(mdp.d_answers.at(2).first.d_name, g_rootdnsname); + } + + { + /* response with CNAME for the target, A for another name */ + PacketBuffer response; + GenericDNSPacketWriter<PacketBuffer> pwR(response, target, QType::A, QClass::IN, 0); + pwR.getHeader()->qr = 1; + pwR.getHeader()->rd = 1; + pwR.getHeader()->ra = 1; + pwR.getHeader()->id = htons(42); + pwR.startRecord(target, QType::CNAME, 7200, QClass::IN, DNSResourceRecord::ANSWER); + pwR.xfrName(notTheTarget); + pwR.commit(); + pwR.startRecord(notTheTarget, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER); + ComboAddress v4("192.0.2.1"); + pwR.xfrCAWithoutPort(4, v4); + pwR.commit(); + pwR.addOpt(4096, 0, 0); + pwR.commit(); + + BOOST_CHECK(dnsdist::changeNameInDNSPacket(response, target, newTarget)); + + MOADNSParser mdp(false, reinterpret_cast<const char*>(response.data()), response.size()); + BOOST_CHECK_EQUAL(mdp.d_qname, newTarget); + BOOST_CHECK_EQUAL(mdp.d_header.qdcount, 1U); + BOOST_CHECK_EQUAL(mdp.d_header.ancount, 2U); + BOOST_CHECK_EQUAL(mdp.d_header.nscount, 0U); + BOOST_CHECK_EQUAL(mdp.d_header.arcount, 1U); + + BOOST_REQUIRE_EQUAL(mdp.d_answers.size(), 3U); + BOOST_CHECK_EQUAL(mdp.d_answers.at(0).first.d_type, static_cast<uint16_t>(QType::CNAME)); + BOOST_CHECK_EQUAL(mdp.d_answers.at(0).first.d_class, QClass::IN); + BOOST_CHECK_EQUAL(mdp.d_answers.at(0).first.d_name, newTarget); + auto content = getRR<UnknownRecordContent>(mdp.d_answers.at(0).first); + BOOST_REQUIRE(content != nullptr); + BOOST_CHECK_EQUAL(content->getRawContent().size(), notTheTarget.getStorage().size()); + + BOOST_CHECK_EQUAL(mdp.d_answers.at(1).first.d_type, static_cast<uint16_t>(QType::A)); + BOOST_CHECK_EQUAL(mdp.d_answers.at(1).first.d_class, QClass::IN); + BOOST_CHECK_EQUAL(mdp.d_answers.at(1).first.d_name, notTheTarget); + BOOST_CHECK_EQUAL(mdp.d_answers.at(2).first.d_type, static_cast<uint16_t>(QType::OPT)); + BOOST_CHECK_EQUAL(mdp.d_answers.at(2).first.d_name, g_rootdnsname); + } + + { + /* response with a lot of records for the target, all supported */ + PacketBuffer response; + GenericDNSPacketWriter<PacketBuffer> pwR(response, target, QType::ANY, QClass::IN, 0); + pwR.getHeader()->qr = 1; + pwR.getHeader()->rd = 1; + pwR.getHeader()->ra = 1; + pwR.getHeader()->id = htons(42); + pwR.startRecord(target, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER); + ComboAddress v4("192.0.2.1"); + pwR.xfrCAWithoutPort(4, v4); + pwR.commit(); + pwR.startRecord(target, QType::AAAA, 7200, QClass::IN, DNSResourceRecord::ANSWER); + ComboAddress v6("2001:db8::1"); + pwR.xfrCAWithoutPort(6, v6); + pwR.commit(); + pwR.startRecord(target, QType::NS, 7200, QClass::IN, DNSResourceRecord::ANSWER); + pwR.xfrName(DNSName("pdns-public-ns1.powerdns.com.")); + pwR.commit(); + pwR.startRecord(target, QType::MX, 7200, QClass::IN, DNSResourceRecord::ANSWER); + pwR.xfr16BitInt(75); + pwR.xfrName(DNSName("download1.powerdns.com.")); + pwR.commit(); + pwR.startRecord(target, QType::TXT, 7200, QClass::IN, DNSResourceRecord::ANSWER); + pwR.xfrText("\"random text\""); + pwR.commit(); + pwR.startRecord(target, QType::RRSIG, 7200, QClass::IN, DNSResourceRecord::ANSWER); + pwR.xfrType(QType::TXT); + pwR.xfr8BitInt(13); + pwR.xfr8BitInt(2); + pwR.xfr32BitInt(42); + pwR.xfrTime(42); + pwR.xfrTime(42); + pwR.xfr16BitInt(42); + pwR.xfrName(DNSName("powerdns.com.")); + pwR.xfrBlob(std::string()); + pwR.commit(); + pwR.addOpt(4096, 0, 0); + pwR.commit(); + + BOOST_CHECK(dnsdist::changeNameInDNSPacket(response, target, newTarget)); + + MOADNSParser mdp(false, reinterpret_cast<const char*>(response.data()), response.size()); + BOOST_CHECK_EQUAL(mdp.d_qname, newTarget); + BOOST_CHECK_EQUAL(mdp.d_header.qdcount, 1U); + BOOST_CHECK_EQUAL(mdp.d_header.ancount, 6U); + BOOST_CHECK_EQUAL(mdp.d_header.nscount, 0U); + BOOST_CHECK_EQUAL(mdp.d_header.arcount, 1U); + + BOOST_REQUIRE_EQUAL(mdp.d_answers.size(), 7U); + for (const auto& answer : mdp.d_answers) { + if (answer.first.d_type == QType::OPT) { + continue; + } + BOOST_CHECK_EQUAL(answer.first.d_class, QClass::IN); + BOOST_CHECK_EQUAL(answer.first.d_name, newTarget); + } + } + + { + /* response with a lot of records for the target, all supported */ + PacketBuffer response; + GenericDNSPacketWriter<PacketBuffer> pwR(response, target, QType::ANY, QClass::IN, 0); + pwR.getHeader()->qr = 1; + pwR.getHeader()->rd = 1; + pwR.getHeader()->ra = 1; + pwR.getHeader()->id = htons(42); + pwR.startRecord(target, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER); + ComboAddress v4("192.0.2.1"); + pwR.xfrCAWithoutPort(4, v4); + pwR.commit(); + pwR.startRecord(target, QType::AAAA, 7200, QClass::IN, DNSResourceRecord::ANSWER); + ComboAddress v6("2001:db8::1"); + pwR.xfrCAWithoutPort(6, v6); + pwR.commit(); + pwR.startRecord(target, QType::NS, 7200, QClass::IN, DNSResourceRecord::ANSWER); + pwR.xfrName(DNSName("pdns-public-ns1.powerdns.com.")); + pwR.commit(); + pwR.startRecord(target, QType::SOA, 7200, QClass::IN, DNSResourceRecord::ANSWER); + pwR.xfrName(DNSName("pdns-public-ns1.powerdns.com.")); + pwR.xfrName(DNSName("admin.powerdns.com.")); + pwR.xfr32BitInt(1); + pwR.xfr32BitInt(2); + pwR.xfr32BitInt(3); + pwR.xfr32BitInt(4); + pwR.xfr32BitInt(5); + pwR.commit(); + pwR.startRecord(target, QType::MX, 7200, QClass::IN, DNSResourceRecord::ANSWER); + pwR.xfr16BitInt(75); + pwR.xfrName(DNSName("download1.powerdns.com.")); + pwR.commit(); + pwR.startRecord(target, QType::TXT, 7200, QClass::IN, DNSResourceRecord::ANSWER); + pwR.xfrText("\"random text\""); + pwR.commit(); + pwR.startRecord(target, QType::SRV, 7200, QClass::IN, DNSResourceRecord::ANSWER); + pwR.xfr16BitInt(1); + pwR.xfr16BitInt(2); + pwR.xfr16BitInt(65535); + pwR.xfrName(DNSName("target.powerdns.com.")); + pwR.commit(); + pwR.addOpt(4096, 0, 0); + pwR.commit(); + + { + // before + MOADNSParser mdp(false, reinterpret_cast<const char*>(response.data()), response.size()); + BOOST_CHECK_EQUAL(mdp.d_qname, target); + BOOST_CHECK_EQUAL(mdp.d_header.qdcount, 1U); + BOOST_CHECK_EQUAL(mdp.d_header.ancount, 7U); + BOOST_CHECK_EQUAL(mdp.d_header.nscount, 0U); + BOOST_CHECK_EQUAL(mdp.d_header.arcount, 1U); + + BOOST_REQUIRE_EQUAL(mdp.d_answers.size(), 8U); + for (const auto& answer : mdp.d_answers) { + if (answer.first.d_type == QType::OPT) { + continue; + } + BOOST_CHECK_EQUAL(answer.first.d_class, QClass::IN); + BOOST_CHECK_EQUAL(answer.first.d_name, target); + } + } + + // rebasing + BOOST_CHECK(dnsdist::changeNameInDNSPacket(response, target, newTarget)); + + { + // after + MOADNSParser mdp(false, reinterpret_cast<const char*>(response.data()), response.size()); + BOOST_CHECK_EQUAL(mdp.d_qname, newTarget); + BOOST_CHECK_EQUAL(mdp.d_header.qdcount, 1U); + BOOST_CHECK_EQUAL(mdp.d_header.ancount, 7U); + BOOST_CHECK_EQUAL(mdp.d_header.nscount, 0U); + BOOST_CHECK_EQUAL(mdp.d_header.arcount, 1U); + + BOOST_REQUIRE_EQUAL(mdp.d_answers.size(), 8U); + for (const auto& answer : mdp.d_answers) { + if (answer.first.d_type == QType::OPT) { + continue; + } + BOOST_CHECK_EQUAL(answer.first.d_class, QClass::IN); + BOOST_CHECK_EQUAL(answer.first.d_name, newTarget); + } + } + } + + { + /* response with an ALIAS record, which is not supported */ + PacketBuffer response; + GenericDNSPacketWriter<PacketBuffer> pwR(response, target, QType::A, QClass::IN, 0); + pwR.getHeader()->qr = 1; + pwR.getHeader()->rd = 1; + pwR.getHeader()->ra = 1; + pwR.getHeader()->id = htons(42); + pwR.startRecord(target, QType::ALIAS, 7200, QClass::IN, DNSResourceRecord::ANSWER); + pwR.xfrName(notTheTarget); + pwR.commit(); + pwR.addOpt(4096, 0, 0); + pwR.commit(); + + BOOST_CHECK(!dnsdist::changeNameInDNSPacket(response, target, newTarget)); + + MOADNSParser mdp(false, reinterpret_cast<const char*>(response.data()), response.size()); + BOOST_CHECK_EQUAL(mdp.d_qname, target); + BOOST_CHECK_EQUAL(mdp.d_header.qdcount, 1U); + BOOST_CHECK_EQUAL(mdp.d_header.ancount, 1U); + BOOST_CHECK_EQUAL(mdp.d_header.nscount, 0U); + BOOST_CHECK_EQUAL(mdp.d_header.arcount, 1U); + + BOOST_REQUIRE_EQUAL(mdp.d_answers.size(), 2U); + BOOST_CHECK_EQUAL(mdp.d_answers.at(0).first.d_type, static_cast<uint16_t>(QType::ALIAS)); + BOOST_CHECK_EQUAL(mdp.d_answers.at(0).first.d_class, QClass::IN); + BOOST_CHECK_EQUAL(mdp.d_answers.at(0).first.d_name, target); + } +} + +BOOST_AUTO_TEST_CASE(test_Overlay) +{ + const DNSName target("powerdns.com."); + + { + PacketBuffer response; + GenericDNSPacketWriter<PacketBuffer> pwR(response, target, QType::ANY, QClass::IN, 0); + pwR.getHeader()->qr = 1; + pwR.getHeader()->rd = 1; + pwR.getHeader()->ra = 1; + pwR.getHeader()->id = htons(42); + pwR.startRecord(target, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER); + ComboAddress v4("192.0.2.1"); + pwR.xfrCAWithoutPort(4, v4); + pwR.commit(); + pwR.startRecord(target, QType::AAAA, 7200, QClass::IN, DNSResourceRecord::ANSWER); + ComboAddress v6("2001:db8::1"); + pwR.xfrCAWithoutPort(6, v6); + pwR.commit(); + pwR.startRecord(target, QType::NS, 7200, QClass::IN, DNSResourceRecord::ANSWER); + pwR.xfrName(DNSName("pdns-public-ns1.powerdns.com.")); + pwR.commit(); + pwR.startRecord(target, QType::SOA, 7200, QClass::IN, DNSResourceRecord::ANSWER); + pwR.xfrName(DNSName("pdns-public-ns1.powerdns.com.")); + pwR.xfrName(DNSName("admin.powerdns.com.")); + pwR.xfr32BitInt(1); + pwR.xfr32BitInt(2); + pwR.xfr32BitInt(3); + pwR.xfr32BitInt(4); + pwR.xfr32BitInt(5); + pwR.commit(); + pwR.startRecord(target, QType::MX, 7200, QClass::IN, DNSResourceRecord::ANSWER); + pwR.xfr16BitInt(75); + pwR.xfrName(DNSName("download1.powerdns.com.")); + pwR.commit(); + pwR.startRecord(target, QType::TXT, 7200, QClass::IN, DNSResourceRecord::ANSWER); + pwR.xfrText("\"random text\""); + pwR.commit(); + pwR.addOpt(4096, 0, 0); + pwR.commit(); + + { + // check packet smaller than dnsheader + BOOST_CHECK_THROW(dnsdist::DNSPacketOverlay(std::string_view(reinterpret_cast<const char*>(response.data()), 11U)), std::runtime_error); + // check corrupted packet + BOOST_CHECK_THROW(dnsdist::DNSPacketOverlay(std::string_view(reinterpret_cast<const char*>(response.data()), response.size() - 1)), std::runtime_error); + } + + dnsdist::DNSPacketOverlay overlay(std::string_view(reinterpret_cast<const char*>(response.data()), response.size())); + BOOST_CHECK_EQUAL(overlay.d_qname, target); + BOOST_CHECK_EQUAL(overlay.d_qtype, QType::ANY); + BOOST_CHECK_EQUAL(overlay.d_qclass, QClass::IN); + BOOST_CHECK_EQUAL(overlay.d_header.qr, 1U); + BOOST_CHECK_EQUAL(overlay.d_header.rd, 1U); + BOOST_CHECK_EQUAL(overlay.d_header.ra, 1U); + BOOST_CHECK_EQUAL(overlay.d_header.id, htons(42)); + BOOST_CHECK_EQUAL(ntohs(overlay.d_header.qdcount), 1U); + BOOST_CHECK_EQUAL(ntohs(overlay.d_header.ancount), 6U); + BOOST_CHECK_EQUAL(ntohs(overlay.d_header.nscount), 0U); + BOOST_CHECK_EQUAL(ntohs(overlay.d_header.arcount), 1U); + BOOST_CHECK_EQUAL(overlay.d_records.size(), 7U); + + /* this is off, of course, but we are only doing a sanity check here */ + uint16_t lastOffset = sizeof(dnsheader) + target.wirelength() + sizeof(uint16_t) + sizeof(uint16_t); + for (const auto& record : overlay.d_records) { + if (record.d_type == QType::OPT) { + continue; + } + + BOOST_CHECK_EQUAL(record.d_name, target); + BOOST_CHECK_EQUAL(record.d_class, QClass::IN); + BOOST_CHECK_EQUAL(record.d_ttl, 7200); + BOOST_CHECK_EQUAL(record.d_place, 1U); + BOOST_CHECK_GE(record.d_contentOffset, lastOffset); + lastOffset = record.d_contentOffset + record.d_contentLength; + } + } +} + +BOOST_AUTO_TEST_SUITE_END(); |