]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Add the ability to change the qname and owner names in DNS packets
authorRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 12 Jan 2023 15:21:59 +0000 (16:21 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 24 Jan 2023 16:32:06 +0000 (17:32 +0100)
pdns/dnsdistdist/dnsdist-dnsparser.cc
pdns/dnsdistdist/dnsdist-dnsparser.hh
pdns/dnsdistdist/test-dnsdist-dnsparser.cc

index 00800b3f7c06f7459cd406aec87407deaf0f5718..fce410c00ed62e501b7e0c3408708c2d20386894 100644 (file)
@@ -63,4 +63,131 @@ DNSPacketOverlay::DNSPacketOverlay(const std::string_view& packet)
     throw std::runtime_error("Unable to parse DNS packet");
   }
 }
+
+bool changeNameInDNSPacket(PacketBuffer& initialPacket, const DNSName& from, const DNSName& to)
+{
+  if (initialPacket.size() < sizeof(dnsheader)) {
+    return false;
+  }
+
+  PacketReader pr(pdns_string_view(reinterpret_cast<const char*>(initialPacket.data()), initialPacket.size()));
+
+  dnsheader dh;
+  memcpy(&dh, initialPacket.data(), sizeof(dh));
+  size_t idx = 0;
+  DNSName rrname;
+  uint16_t qdcount = ntohs(dh.qdcount);
+  uint16_t ancount = ntohs(dh.ancount);
+  uint16_t nscount = ntohs(dh.nscount);
+  uint16_t arcount = ntohs(dh.arcount);
+  uint16_t rrtype;
+  uint16_t rrclass;
+  string blob;
+
+  size_t recordsCount = ancount + nscount + arcount;
+  struct dnsrecordheader ah;
+
+  rrname = pr.getName();
+  if (rrname == from) {
+    rrname = to;
+  }
+
+  rrtype = pr.get16BitInt();
+  rrclass = pr.get16BitInt();
+
+  PacketBuffer newContent;
+  newContent.reserve(initialPacket.size());
+  GenericDNSPacketWriter<PacketBuffer> pw(newContent, rrname, rrtype, rrclass, dh.opcode);
+  /* we want to copy the flags and ID but not the counts since we recreate the records below */
+  pw.getHeader()->id = dh.id;
+  pw.getHeader()->qr = dh.qr;
+  pw.getHeader()->aa = dh.aa;
+  pw.getHeader()->tc = dh.tc;
+  pw.getHeader()->rd = dh.rd;
+  pw.getHeader()->ra = dh.ra;
+  pw.getHeader()->ad = dh.ad;
+  pw.getHeader()->cd = dh.cd;
+  pw.getHeader()->rcode = dh.rcode;
+
+  /* consume remaining qd if any, but do not copy it */
+  if (qdcount > 1) {
+    for (idx = 1; idx < qdcount; idx++) {
+      rrname = pr.getName();
+      rrtype = pr.get16BitInt();
+      rrclass = pr.get16BitInt();
+      (void)rrtype;
+      (void)rrclass;
+    }
+  }
+
+  const std::unordered_set<QType> nameOnlyTypes{QType::NS, QType::PTR, QType::CNAME, QType::DNAME};
+  const std::unordered_set<QType> noNameTypes{QType::A, QType::AAAA, QType::DHCID, QType::TXT, QType::OPT, QType::HINFO, QType::DNSKEY, QType::CDNSKEY, QType::DS, QType::CDS, QType::DLV, QType::SSHFP, QType::KEY, QType::CERT, QType::TLSA, QType::SMIMEA, QType::OPENPGPKEY, QType::NSEC, QType::NSEC3, QType::CSYNC, QType::NSEC3PARAM, QType::LOC, QType::NID, QType::L32, QType::L64, QType::EUI48, QType::EUI64, QType::URI, QType::CAA};
+
+  /* copy AN, NS and AR */
+  for (idx = 0; idx < recordsCount; idx++) {
+    rrname = pr.getName();
+    if (rrname == from) {
+      rrname = to;
+    }
+    pr.getDnsrecordheader(ah);
+
+    auto place = idx < ancount ? DNSResourceRecord::ANSWER : (idx < (ancount + nscount) ? DNSResourceRecord::AUTHORITY : DNSResourceRecord::ADDITIONAL);
+    pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, place, true);
+    if (nameOnlyTypes.count(ah.d_type)) {
+      rrname = pr.getName();
+      pw.xfrName(rrname);
+    }
+    else if (noNameTypes.count(ah.d_type)) {
+      pr.xfrBlob(blob);
+      pw.xfrBlob(blob);
+    }
+    else if (ah.d_type == QType::RRSIG) {
+      /* good luck */
+      pr.xfrBlob(blob);
+      pw.xfrBlob(blob);
+    }
+    else if (ah.d_type == QType::MX) {
+      auto prio = pr.get16BitInt();
+      rrname = pr.getName();
+      pw.xfr16BitInt(prio);
+      pw.xfrName(rrname);
+    }
+    else if (ah.d_type == QType::SOA) {
+      auto mname = pr.getName();
+      pw.xfrName(mname);
+      auto rname = pr.getName();
+      pw.xfrName(rname);
+      /* serial */
+      pw.xfr32BitInt(pr.get32BitInt());
+      /* refresh */
+      pw.xfr32BitInt(pr.get32BitInt());
+      /* retry */
+      pw.xfr32BitInt(pr.get32BitInt());
+      /* expire */
+      pw.xfr32BitInt(pr.get32BitInt());
+      /* minimal */
+      pw.xfr32BitInt(pr.get32BitInt());
+    }
+    else if (ah.d_type == QType::SRV) {
+      /* preference */
+      pw.xfr16BitInt(pr.get16BitInt());
+      /* weight */
+      pw.xfr16BitInt(pr.get16BitInt());
+      /* port */
+      pw.xfr16BitInt(pr.get16BitInt());
+      auto target = pr.getName();
+      pw.xfrName(target);
+    }
+    else {
+      /* sorry, unsafe type */
+      return false;
+    }
+  }
+
+  pw.commit();
+  initialPacket = std::move(newContent);
+
+  return true;
+}
+
 }
index e6a4c009de84a669edce524bc1eede3703fcd179..91de7acf782f642bf4a0702cfeaa8ab788a5a9c5 100644 (file)
@@ -47,4 +47,11 @@ public:
   uint16_t d_qclass;
   dnsheader d_header;
 };
+
+/* Rewrite, if they are exactly equal to 'from', the qname and owner name of any record
+ * to 'to'. Since that might break DNS name pointers, the whole payload is rewritten,
+ * and the operation may fail if there is at least one unsupported record in the payload,
+ * because it could contain pointers that would not be rewritten.
+ */
+bool changeNameInDNSPacket(PacketBuffer& initialPacket, const DNSName& from, const DNSName& to);
 }
index 6bca068f6768153543c92da35c9b00180a8002ae..ed7ec5a9995cdd9c310085876db318efebf8e2dc 100644 (file)
 
 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 = std::dynamic_pointer_cast<UnknownRecordContent>(mdp.d_answers.at(0).first.d_content);
+    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.");