/* * 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. */ #ifndef BOOST_TEST_DYN_LINK #define BOOST_TEST_DYN_LINK #endif #define BOOST_TEST_NO_MAIN #include #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 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(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 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 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(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 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(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(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(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(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 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(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(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(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(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 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(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(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(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(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(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 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(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 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(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(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 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(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(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 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(response.data()), 11U)), std::runtime_error); // check corrupted packet BOOST_CHECK_THROW(dnsdist::DNSPacketOverlay(std::string_view(reinterpret_cast(response.data()), response.size() - 1)), std::runtime_error); } dnsdist::DNSPacketOverlay overlay(std::string_view(reinterpret_cast(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, 7200U); 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();