]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
rec: Add IXFR unit tests 5476/head
authorRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 29 Jun 2017 13:29:40 +0000 (15:29 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 29 Jun 2017 14:41:46 +0000 (16:41 +0200)
pdns/Makefile.am
pdns/recursordist/Makefile.am
pdns/recursordist/test-common.hh [new symlink]
pdns/recursordist/test-ixfr_cc.cc [new symlink]
pdns/recursordist/test-syncres_cc.cc
pdns/test-common.hh [new file with mode: 0644]
pdns/test-ixfr_cc.cc [new file with mode: 0644]

index 43515c44107bd827bd392f46546774fcc03f5d6b..4a3a41e76659af81294919ff2745d3af388f0f0b 100644 (file)
@@ -1149,6 +1149,7 @@ testrunner_SOURCES = \
        gettime.cc gettime.hh \
        gss_context.cc gss_context.hh \
        iputils.cc \
+       ixfr.cc ixfr.hh \
        logger.cc \
        misc.cc \
        nameserver.cc \
@@ -1163,6 +1164,7 @@ testrunner_SOURCES = \
        test-base32_cc.cc \
        test-base64_cc.cc \
        test-bindparser_cc.cc \
+       test-common.hh \
        test-delaypipe_hh.cc \
        test-dnsrecordcontent.cc \
        test-distributor_hh.cc \
@@ -1171,6 +1173,7 @@ testrunner_SOURCES = \
        test-dnsparser_hh.cc \
        test-dnsrecords_cc.cc \
        test-iputils_hh.cc \
+       test-ixfr_cc.cc \
        test-lock_hh.cc \
        test-md5_hh.cc \
        test-misc_hh.cc \
@@ -1184,6 +1187,7 @@ testrunner_SOURCES = \
        test-tsig.cc \
        test-zoneparser_tng_cc.cc \
        testrunner.cc \
+       tsigverifier.cc tsigverifier.hh \
        ueberbackend.cc \
        unix_utility.cc \
        zoneparser-tng.cc zoneparser-tng.hh
index f2c11e9983bfaa1674f8cd52d79ce301f07c1587..8b9ec4e088b87f3c739c38e488c11839df84d105 100644 (file)
@@ -195,6 +195,7 @@ testrunner_SOURCES = \
        gettime.cc gettime.hh \
        gss_context.cc gss_context.hh \
        iputils.cc iputils.hh \
+       ixfr.cc ixfr.hh \
        logger.cc logger.hh \
        misc.cc misc.hh \
        negcache.hh negcache.cc \
@@ -218,6 +219,7 @@ testrunner_SOURCES = \
        test-arguments_cc.cc \
        test-base32_cc.cc \
        test-base64_cc.cc \
+       test-common.hh \
        test-dnsrecordcontent.cc \
        test-dns_random_hh.cc \
        test-dnsname_cc.cc \
@@ -225,6 +227,7 @@ testrunner_SOURCES = \
        test-dnsrecords_cc.cc \
        test-ednsoptions_cc.cc \
        test-iputils_hh.cc \
+       test-ixfr_cc.cc \
        test-misc_hh.cc \
        test-nmtree.cc \
        test-negcache_cc.cc \
@@ -234,6 +237,7 @@ testrunner_SOURCES = \
        test-syncres_cc.cc \
        test-tsig.cc \
        testrunner.cc \
+       tsigverifier.cc tsigverifier.hh \
        unix_utility.cc \
        validate.cc validate.hh \
        validate-recursor.cc validate-recursor.hh \
diff --git a/pdns/recursordist/test-common.hh b/pdns/recursordist/test-common.hh
new file mode 120000 (symlink)
index 0000000..c03711b
--- /dev/null
@@ -0,0 +1 @@
+../test-common.hh
\ No newline at end of file
diff --git a/pdns/recursordist/test-ixfr_cc.cc b/pdns/recursordist/test-ixfr_cc.cc
new file mode 120000 (symlink)
index 0000000..d033c39
--- /dev/null
@@ -0,0 +1 @@
+../test-ixfr_cc.cc
\ No newline at end of file
index 20553fd369d257ca9fa696bdb57f74ff2295f756..0e1e5051b04dcda74be5aa0ffab5b2352d6cc620 100644 (file)
@@ -10,6 +10,7 @@
 #include "rec-lua-conf.hh"
 #include "root-dnssec.hh"
 #include "syncres.hh"
+#include "test-common.hh"
 #include "utility.hh"
 #include "validate-recursor.hh"
 
@@ -185,45 +186,6 @@ static void setLWResult(LWResult* res, int rcode, bool aa=false, bool tc=false,
   res->d_haveEDNS = edns;
 }
 
-static std::shared_ptr<DNSRecordContent> getRecordContent(uint16_t type, const std::string& content)
-{
-  std::shared_ptr<DNSRecordContent> result = nullptr;
-
-  if (type == QType::NS) {
-    result = std::make_shared<NSRecordContent>(DNSName(content));
-  }
-  else if (type == QType::A) {
-    result = std::make_shared<ARecordContent>(ComboAddress(content));
-  }
-  else if (type == QType::AAAA) {
-    result = std::make_shared<AAAARecordContent>(ComboAddress(content));
-  }
-  else if (type == QType::CNAME) {
-    result = std::make_shared<CNAMERecordContent>(DNSName(content));
-  }
-  else if (type == QType::OPT) {
-    result = std::make_shared<OPTRecordContent>();
-  }
-  else {
-    result = DNSRecordContent::mastermake(type, QClass::IN, content);
-  }
-
-  return result;
-}
-
-static void addRecordToList(std::vector<DNSRecord>& records, const DNSName& name, uint16_t type, const std::string& content, DNSResourceRecord::Place place, uint32_t ttl)
-{
-  DNSRecord rec;
-  rec.d_place = place;
-  rec.d_name = name;
-  rec.d_type = type;
-  rec.d_ttl = ttl;
-
-  rec.d_content = getRecordContent(type, content);
-
-  records.push_back(rec);
-}
-
 static void addRecordToLW(LWResult* res, const DNSName& name, uint16_t type, const std::string& content, DNSResourceRecord::Place place=DNSResourceRecord::ANSWER, uint32_t ttl=60)
 {
   addRecordToList(res->d_records, name, type, content, place, ttl);
diff --git a/pdns/test-common.hh b/pdns/test-common.hh
new file mode 100644 (file)
index 0000000..50de3b7
--- /dev/null
@@ -0,0 +1,42 @@
+
+#include "dnsrecords.hh"
+#include "iputils.hh"
+
+static inline std::shared_ptr<DNSRecordContent> getRecordContent(uint16_t type, const std::string& content)
+{
+  std::shared_ptr<DNSRecordContent> result = nullptr;
+
+  if (type == QType::NS) {
+    result = std::make_shared<NSRecordContent>(DNSName(content));
+  }
+  else if (type == QType::A) {
+    result = std::make_shared<ARecordContent>(ComboAddress(content));
+  }
+  else if (type == QType::AAAA) {
+    result = std::make_shared<AAAARecordContent>(ComboAddress(content));
+  }
+  else if (type == QType::CNAME) {
+    result = std::make_shared<CNAMERecordContent>(DNSName(content));
+  }
+  else if (type == QType::OPT) {
+    result = std::make_shared<OPTRecordContent>();
+  }
+  else {
+    result = DNSRecordContent::mastermake(type, QClass::IN, content);
+  }
+
+  return result;
+}
+
+static inline void addRecordToList(std::vector<DNSRecord>& records, const DNSName& name, uint16_t type, const std::string& content, DNSResourceRecord::Place place=DNSResourceRecord::ANSWER, uint32_t ttl=3600)
+{
+  DNSRecord rec;
+  rec.d_place = place;
+  rec.d_name = name;
+  rec.d_type = type;
+  rec.d_ttl = ttl;
+
+  rec.d_content = getRecordContent(type, content);
+
+  records.push_back(rec);
+}
diff --git a/pdns/test-ixfr_cc.cc b/pdns/test-ixfr_cc.cc
new file mode 100644 (file)
index 0000000..63f4bdd
--- /dev/null
@@ -0,0 +1,303 @@
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_NO_MAIN
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <boost/test/unit_test.hpp>
+
+#include "test-common.hh"
+#include "ixfr.hh"
+
+BOOST_AUTO_TEST_SUITE(ixfr_cc)
+
+BOOST_AUTO_TEST_CASE(test_ixfr_rfc1995_axfr) {
+  const ComboAddress master("[2001:DB8::1]:53");
+  const DNSName zone("JAIN.AD.JP.");
+
+  auto masterSOA = DNSRecordContent::mastermake(QType::SOA, QClass::IN, "NS.JAIN.AD.JP. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  vector<DNSRecord> records;
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::NS, "NS.JAIN.AD.JP.");
+  addRecordToList(records, DNSName("NS.JAIN.AD.JP."), QType::A, "133.69.136.1");
+  addRecordToList(records, DNSName("JAIN-BB.JAIN.AD.JP."), QType::A, "133.69.136.3");
+  addRecordToList(records, DNSName("JAIN-BB.JAIN.AD.JP."), QType::A, "192.41.197.2");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+
+  auto ret = processIXFRRecords(master, zone, records, std::dynamic_pointer_cast<SOARecordContent>(masterSOA));
+  BOOST_CHECK_EQUAL(ret.size(), 1);
+  BOOST_CHECK_EQUAL(ret.at(0).first.size(), 0);
+  BOOST_REQUIRE_EQUAL(ret.at(0).second.size(), records.size());
+  for (size_t idx = 0; idx < records.size(); idx++) {
+    BOOST_CHECK(ret.at(0).second.at(idx) == records.at(idx));
+  }
+}
+
+BOOST_AUTO_TEST_CASE(test_ixfr_rfc1995_incremental) {
+  const ComboAddress master("[2001:DB8::1]:53");
+  const DNSName zone("JAIN.AD.JP.");
+
+  auto masterSOA = DNSRecordContent::mastermake(QType::SOA, QClass::IN, "NS.JAIN.AD.JP. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  vector<DNSRecord> records;
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 1 600 600 3600000 604800");
+  addRecordToList(records, DNSName("NEZU.JAIN.AD.JP."), QType::A, "133.69.136.5");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 2 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN-BB.JAIN.AD.JP."), QType::A, "133.69.136.4");
+  addRecordToList(records, DNSName("JAIN-BB.JAIN.AD.JP."), QType::A, "192.41.197.2");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 2 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN-BB.JAIN.AD.JP."), QType::A, "133.69.136.4");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN-BB.JAIN.AD.JP."), QType::A, "133.69.136.3");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+
+  auto ret = processIXFRRecords(master, zone, records, std::dynamic_pointer_cast<SOARecordContent>(masterSOA));
+  // two sequences
+  BOOST_CHECK_EQUAL(ret.size(), 2);
+  // the first one has one removal, two additions (plus the corresponding SOA removal/addition)
+  BOOST_CHECK_EQUAL(ret.at(0).first.size(), 1 + 1);
+  BOOST_CHECK_EQUAL(ret.at(0).second.size(), 2 + 1);
+
+  // check removals
+  BOOST_CHECK_EQUAL(ret.at(0).first.at(0).d_type, QType(QType::SOA).getCode());
+  BOOST_CHECK_EQUAL(ret.at(0).first.at(1).d_type, QType(QType::A).getCode());
+
+  // check additions
+  BOOST_CHECK_EQUAL(ret.at(0).second.at(0).d_type, QType(QType::SOA).getCode());
+  BOOST_CHECK_EQUAL(ret.at(0).second.at(1).d_type, QType(QType::A).getCode());
+  BOOST_CHECK_EQUAL(ret.at(0).second.at(2).d_type, QType(QType::A).getCode());
+
+  // the second one has one removal, one addition
+  BOOST_CHECK_EQUAL(ret.at(1).first.size(), 1 + 1);
+  BOOST_CHECK_EQUAL(ret.at(1).second.size(), 1 + 1);
+
+  // check removals
+  BOOST_CHECK_EQUAL(ret.at(1).first.at(0).d_type, QType(QType::SOA).getCode());
+  BOOST_CHECK_EQUAL(ret.at(1).first.at(1).d_type, QType(QType::A).getCode());
+
+  // check additions
+  BOOST_CHECK_EQUAL(ret.at(1).second.at(0).d_type, QType(QType::SOA).getCode());
+  BOOST_CHECK_EQUAL(ret.at(1).second.at(1).d_type, QType(QType::A).getCode());
+}
+
+BOOST_AUTO_TEST_CASE(test_ixfr_rfc1995_condensed_incremental) {
+  const ComboAddress master("[2001:DB8::1]:53");
+  const DNSName zone("JAIN.AD.JP.");
+
+  auto masterSOA = DNSRecordContent::mastermake(QType::SOA, QClass::IN, "NS.JAIN.AD.JP. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  vector<DNSRecord> records;
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 1 600 600 3600000 604800");
+  addRecordToList(records, DNSName("NEZU.JAIN.AD.JP."), QType::A, "133.69.136.5");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN-BB.JAIN.AD.JP."), QType::A, "133.69.136.3");
+  addRecordToList(records, DNSName("JAIN-BB.JAIN.AD.JP."), QType::A, "192.41.197.2");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+
+  auto ret = processIXFRRecords(master, zone, records, std::dynamic_pointer_cast<SOARecordContent>(masterSOA));
+  // one sequence
+  BOOST_CHECK_EQUAL(ret.size(), 1);
+  // it has one removal, two additions (plus the corresponding SOA removal/addition)
+  BOOST_CHECK_EQUAL(ret.at(0).first.size(), 1 + 1);
+  BOOST_CHECK_EQUAL(ret.at(0).second.size(), 2 + 1);
+
+  // check removals
+  BOOST_CHECK_EQUAL(ret.at(0).first.at(0).d_type, QType(QType::SOA).getCode());
+  BOOST_CHECK_EQUAL(ret.at(0).first.at(1).d_type, QType(QType::A).getCode());
+
+  // check additions
+  BOOST_CHECK_EQUAL(ret.at(0).second.at(0).d_type, QType(QType::SOA).getCode());
+  BOOST_CHECK_EQUAL(ret.at(0).second.at(1).d_type, QType(QType::A).getCode());
+  BOOST_CHECK_EQUAL(ret.at(0).second.at(2).d_type, QType(QType::A).getCode());
+}
+
+BOOST_AUTO_TEST_CASE(test_ixfr_no_additions_in_first_sequence) {
+  const ComboAddress master("[2001:DB8::1]:53");
+  const DNSName zone("JAIN.AD.JP.");
+
+  auto masterSOA = DNSRecordContent::mastermake(QType::SOA, QClass::IN, "NS.JAIN.AD.JP. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  vector<DNSRecord> records;
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 1 600 600 3600000 604800");
+  addRecordToList(records, DNSName("NEZU.JAIN.AD.JP."), QType::A, "133.69.136.5");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 2 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 2 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN-BB.JAIN.AD.JP."), QType::A, "133.69.136.5");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN-BB.JAIN.AD.JP."), QType::A, "133.69.136.3");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+
+  auto ret = processIXFRRecords(master, zone, records, std::dynamic_pointer_cast<SOARecordContent>(masterSOA));
+  // two sequences
+  BOOST_CHECK_EQUAL(ret.size(), 2);
+  // the first one has one removal, no additions (plus the corresponding SOA removal/addition)
+  BOOST_CHECK_EQUAL(ret.at(0).first.size(), 1 + 1);
+  BOOST_CHECK_EQUAL(ret.at(0).second.size(), 0 + 1);
+
+  // check removals
+  BOOST_CHECK_EQUAL(ret.at(0).first.at(0).d_type, QType(QType::SOA).getCode());
+  BOOST_CHECK_EQUAL(ret.at(0).first.at(1).d_type, QType(QType::A).getCode());
+
+  // check additions
+  BOOST_CHECK_EQUAL(ret.at(0).second.at(0).d_type, QType(QType::SOA).getCode());
+
+  // the second one has one removal, one addition
+  BOOST_CHECK_EQUAL(ret.at(1).first.size(), 1 + 1);
+  BOOST_CHECK_EQUAL(ret.at(1).second.size(), 1 + 1);
+
+  // check removals
+  BOOST_CHECK_EQUAL(ret.at(1).first.at(0).d_type, QType(QType::SOA).getCode());
+  BOOST_CHECK_EQUAL(ret.at(1).first.at(1).d_type, QType(QType::A).getCode());
+
+  // check additions
+  BOOST_CHECK_EQUAL(ret.at(1).second.at(0).d_type, QType(QType::SOA).getCode());
+  BOOST_CHECK_EQUAL(ret.at(1).second.at(1).d_type, QType(QType::A).getCode());
+}
+
+BOOST_AUTO_TEST_CASE(test_ixfr_no_removals_in_first_sequence) {
+  const ComboAddress master("[2001:DB8::1]:53");
+  const DNSName zone("JAIN.AD.JP.");
+
+  auto masterSOA = DNSRecordContent::mastermake(QType::SOA, QClass::IN, "NS.JAIN.AD.JP. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  vector<DNSRecord> records;
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 1 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 2 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN-BB.JAIN.AD.JP."), QType::A, "133.69.136.4");
+  addRecordToList(records, DNSName("JAIN-BB.JAIN.AD.JP."), QType::A, "192.41.197.2");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 2 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN-BB.JAIN.AD.JP."), QType::A, "133.69.136.4");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN-BB.JAIN.AD.JP."), QType::A, "133.69.136.3");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+
+  auto ret = processIXFRRecords(master, zone, records, std::dynamic_pointer_cast<SOARecordContent>(masterSOA));
+  // two sequences
+  BOOST_CHECK_EQUAL(ret.size(), 2);
+  // the first one has no removal, two additions (plus the corresponding SOA removal/addition)
+  BOOST_CHECK_EQUAL(ret.at(0).first.size(), 0 + 1);
+  BOOST_CHECK_EQUAL(ret.at(0).second.size(), 2 + 1);
+
+  // check removals
+  BOOST_CHECK_EQUAL(ret.at(0).first.at(0).d_type, QType(QType::SOA).getCode());
+
+  // check additions
+  BOOST_CHECK_EQUAL(ret.at(0).second.at(0).d_type, QType(QType::SOA).getCode());
+  BOOST_CHECK_EQUAL(ret.at(0).second.at(1).d_type, QType(QType::A).getCode());
+  BOOST_CHECK_EQUAL(ret.at(0).second.at(1).d_type, QType(QType::A).getCode());
+
+  // the second one has one removal, one addition
+  BOOST_CHECK_EQUAL(ret.at(1).first.size(), 1 + 1);
+  BOOST_CHECK_EQUAL(ret.at(1).second.size(), 1 + 1);
+
+  // check removals
+  BOOST_CHECK_EQUAL(ret.at(1).first.at(0).d_type, QType(QType::SOA).getCode());
+  BOOST_CHECK_EQUAL(ret.at(1).first.at(1).d_type, QType(QType::A).getCode());
+
+  // check additions
+  BOOST_CHECK_EQUAL(ret.at(1).second.at(0).d_type, QType(QType::SOA).getCode());
+  BOOST_CHECK_EQUAL(ret.at(1).second.at(1).d_type, QType(QType::A).getCode());
+}
+
+BOOST_AUTO_TEST_CASE(test_ixfr_same_serial) {
+  const ComboAddress master("[2001:DB8::1]:53");
+  const DNSName zone("JAIN.AD.JP.");
+
+  auto masterSOA = DNSRecordContent::mastermake(QType::SOA, QClass::IN, "NS.JAIN.AD.JP. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  vector<DNSRecord> records;
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+
+  auto ret = processIXFRRecords(master, zone, records, std::dynamic_pointer_cast<SOARecordContent>(masterSOA));
+
+  BOOST_CHECK_EQUAL(ret.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(test_ixfr_invalid_no_records) {
+  const ComboAddress master("[2001:DB8::1]:53");
+  const DNSName zone("JAIN.AD.JP.");
+
+  auto masterSOA = DNSRecordContent::mastermake(QType::SOA, QClass::IN, "NS.JAIN.AD.JP. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  vector<DNSRecord> records;
+
+  auto ret = processIXFRRecords(master, zone, records, std::dynamic_pointer_cast<SOARecordContent>(masterSOA));
+  BOOST_CHECK_EQUAL(ret.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(test_ixfr_invalid_no_master_soa) {
+  const ComboAddress master("[2001:DB8::1]:53");
+  const DNSName zone("JAIN.AD.JP.");
+;
+  vector<DNSRecord> records;
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+
+  auto ret = processIXFRRecords(master, zone, records, nullptr);
+  BOOST_CHECK_EQUAL(ret.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(test_ixfr_invalid_no_trailing_soa) {
+  const ComboAddress master("[2001:DB8::1]:53");
+  const DNSName zone("JAIN.AD.JP.");
+
+  auto masterSOA = DNSRecordContent::mastermake(QType::SOA, QClass::IN, "NS.JAIN.AD.JP. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  vector<DNSRecord> records;
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 1 600 600 3600000 604800");
+  addRecordToList(records, DNSName("NEZU.JAIN.AD.JP."), QType::A, "133.69.136.5");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN-BB.JAIN.AD.JP."), QType::A, "133.69.136.3");
+  addRecordToList(records, DNSName("JAIN-BB.JAIN.AD.JP."), QType::A, "192.41.197.2");
+
+  BOOST_CHECK_THROW(processIXFRRecords(master, zone, records, std::dynamic_pointer_cast<SOARecordContent>(masterSOA)), std::runtime_error);
+}
+
+BOOST_AUTO_TEST_CASE(test_ixfr_invalid_no_soa_after_removals) {
+  const ComboAddress master("[2001:DB8::1]:53");
+  const DNSName zone("JAIN.AD.JP.");
+
+  auto masterSOA = DNSRecordContent::mastermake(QType::SOA, QClass::IN, "NS.JAIN.AD.JP. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  vector<DNSRecord> records;
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 1 600 600 3600000 604800");
+  addRecordToList(records, DNSName("NEZU.JAIN.AD.JP."), QType::A, "133.69.136.5");
+
+  BOOST_CHECK_THROW(processIXFRRecords(master, zone, records, std::dynamic_pointer_cast<SOARecordContent>(masterSOA)), std::runtime_error);
+}
+
+BOOST_AUTO_TEST_CASE(test_ixfr_mistmatching_serial_before_and_after_additions) {
+  const ComboAddress master("[2001:DB8::1]:53");
+  const DNSName zone("JAIN.AD.JP.");
+
+  auto masterSOA = DNSRecordContent::mastermake(QType::SOA, QClass::IN, "NS.JAIN.AD.JP. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  vector<DNSRecord> records;
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 1 600 600 3600000 604800");
+  addRecordToList(records, DNSName("NEZU.JAIN.AD.JP."), QType::A, "133.69.136.5");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 2 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN-BB.JAIN.AD.JP."), QType::A, "133.69.136.3");
+  addRecordToList(records, DNSName("JAIN-BB.JAIN.AD.JP."), QType::A, "192.41.197.2");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+
+  BOOST_CHECK_THROW(processIXFRRecords(master, zone, records, std::dynamic_pointer_cast<SOARecordContent>(masterSOA)), std::runtime_error);
+}
+
+BOOST_AUTO_TEST_CASE(test_ixfr_trailing_record_after_end) {
+  const ComboAddress master("[2001:DB8::1]:53");
+  const DNSName zone("JAIN.AD.JP.");
+
+  auto masterSOA = DNSRecordContent::mastermake(QType::SOA, QClass::IN, "NS.JAIN.AD.JP. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  vector<DNSRecord> records;
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 1 600 600 3600000 604800");
+  addRecordToList(records, DNSName("NEZU.JAIN.AD.JP."), QType::A, "133.69.136.5");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN-BB.JAIN.AD.JP."), QType::A, "133.69.136.3");
+  addRecordToList(records, DNSName("JAIN-BB.JAIN.AD.JP."), QType::A, "192.41.197.2");
+  addRecordToList(records, DNSName("JAIN.AD.JP."), QType::SOA, "ns.jain.ad.jp. mohta.jain.ad.jp. 3 600 600 3600000 604800");
+  addRecordToList(records, DNSName("JAIN-BB.JAIN.AD.JP."), QType::A, "133.69.136.3");
+
+  BOOST_CHECK_THROW(processIXFRRecords(master, zone, records, std::dynamic_pointer_cast<SOARecordContent>(masterSOA)), std::runtime_error);
+}
+
+BOOST_AUTO_TEST_SUITE_END();