]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
add dns_dns64_findprefix
authorMark Andrews <marka@isc.org>
Mon, 22 Jul 2019 17:44:30 +0000 (03:44 +1000)
committerMark Andrews <marka@isc.org>
Tue, 24 Nov 2020 21:25:29 +0000 (08:25 +1100)
lib/dns/dns64.c
lib/dns/include/dns/dns64.h
lib/dns/tests/Makefile.am
lib/dns/tests/dns64_test.c [new file with mode: 0644]
lib/dns/win32/libdns.def.in
lib/isc/include/isc/netaddr.h
lib/isc/include/isc/types.h
util/copyrights

index 67344a992f0330c991af1c2613ff04820a30fd6f..cc99a1639ed11ea6542dbd92851287cbe496d7a0 100644 (file)
@@ -321,3 +321,162 @@ done:
        }
        return (found ? answer : true);
 }
+
+/*
+ * Posible mapping of IPV4ONLY.ARPA A records into AAAA records
+ * for valid RFC6052 prefixes.
+ */
+static struct {
+       const unsigned char aa[16]; /* mapped version of 192.0.0.170 */
+       const unsigned char ab[16]; /* mapped version of 192.0.0.171 */
+       const unsigned char mask[16];
+       const unsigned int plen;
+} const prefixes[6] = {
+       { { 0, 0, 0, 0, 192, 0, 0, 170, 0, 0, 0, 0, 0, 0, 0, 0 },
+         { 0, 0, 0, 0, 192, 0, 0, 171, 0, 0, 0, 0, 0, 0, 0, 0 },
+         { 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0 },
+         32 },
+       { { 0, 0, 0, 0, 0, 192, 0, 0, 0, 170, 0, 0, 0, 0, 0, 0 },
+         { 0, 0, 0, 0, 0, 192, 0, 0, 0, 171, 0, 0, 0, 0, 0, 0 },
+         { 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0 },
+         40 },
+       { { 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 170, 0, 0, 0, 0, 0 },
+         { 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 171, 0, 0, 0, 0, 0 },
+         { 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0 },
+         48 },
+       { { 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 170, 0, 0, 0, 0 },
+         { 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 171, 0, 0, 0, 0 },
+         { 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0 },
+         56 },
+       { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 170, 0, 0, 0 },
+         { 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 171, 0, 0, 0 },
+         { 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0 },
+         64 },
+       { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 170 },
+         { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 171 },
+         { 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255 },
+         96 }
+};
+
+static unsigned int
+search(const dns_rdata_t *rd1, const dns_rdata_t *rd2, unsigned int plen) {
+       unsigned int i = 0, j;
+       const unsigned char *c, *m;
+
+       /*
+        * Resume looking for another aa match?
+        */
+       if (plen != 0U && rd2 == NULL) {
+               while (i < 6U) {
+                       /* Post increment as we resume on next entry. */
+                       if (prefixes[i++].plen == plen) {
+                               break;
+                       }
+               }
+       }
+
+       for (; i < 6U; i++) {
+               j = 0;
+               if (rd2 != NULL) {
+                       /* Find the right entry. */
+                       if (prefixes[i].plen != plen) {
+                               continue;
+                       }
+                       /* Does the prefix match? */
+                       while ((j * 8U) < plen) {
+                               if (rd1->data[j] != rd2->data[j]) {
+                                       return (0);
+                               }
+                               j++;
+                       }
+               }
+
+               /* Match well known mapped addresses. */
+               c = (rd2 == NULL) ? prefixes[i].aa : prefixes[i].ab;
+               m = prefixes[i].mask;
+               for (; j < 16U; j++) {
+                       if ((rd1->data[j] & m[j]) != (c[j] & m[j])) {
+                               break;
+                       }
+               }
+               if (j == 16U) {
+                       return (prefixes[i].plen);
+               }
+               if (rd2 != NULL) {
+                       return (0);
+               }
+       }
+       return (0);
+}
+
+isc_result_t
+dns_dns64_findprefix(dns_rdataset_t *rdataset, isc_netprefix_t *prefix,
+                    size_t *len) {
+       dns_rdataset_t outer, inner;
+       unsigned int oplen, iplen;
+       size_t count = 0;
+       struct in6_addr ina6;
+       isc_result_t result;
+
+       REQUIRE(prefix != NULL && len != NULL && *len != 0U);
+       REQUIRE(rdataset != NULL && rdataset->type == dns_rdatatype_aaaa);
+
+       dns_rdataset_init(&outer);
+       dns_rdataset_init(&inner);
+       dns_rdataset_clone(rdataset, &outer);
+       dns_rdataset_clone(rdataset, &inner);
+
+       for (result = dns_rdataset_first(&outer); result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(&outer))
+       {
+               dns_rdata_t rd1 = DNS_RDATA_INIT;
+               dns_rdataset_current(&outer, &rd1);
+               oplen = 0;
+       resume:
+               /* Look for a 192.0.0.170 match. */
+               oplen = search(&rd1, NULL, oplen);
+               if (oplen == 0) {
+                       continue;
+               }
+
+               /* Look for the 192.0.0.171 match. */
+               for (result = dns_rdataset_first(&inner);
+                    result == ISC_R_SUCCESS;
+                    result = dns_rdataset_next(&inner))
+               {
+                       dns_rdata_t rd2 = DNS_RDATA_INIT;
+
+                       dns_rdataset_current(&inner, &rd2);
+                       iplen = search(&rd2, &rd1, oplen);
+                       if (iplen == 0) {
+                               continue;
+                       }
+                       INSIST(iplen == oplen);
+                       if (count >= *len) {
+                               count++;
+                               break;
+                       }
+
+                       /* We have a prefix. */
+                       memset(ina6.s6_addr, 0, sizeof(ina6.s6_addr));
+                       memmove(ina6.s6_addr, rd1.data, oplen / 8);
+                       isc_netaddr_fromin6(&prefix[count].addr, &ina6);
+                       prefix[count].prefixlen = oplen;
+                       count++;
+                       break;
+               }
+               /* Didn't find a match look for a different prefix length. */
+               if (result == ISC_R_NOMORE) {
+                       goto resume;
+               }
+       }
+       if (count == 0U) {
+               return (ISC_R_NOTFOUND);
+       }
+       if (count > *len) {
+               *len = count;
+               return (ISC_R_NOSPACE);
+       }
+       *len = count;
+       return (ISC_R_SUCCESS);
+}
index b3f6f42c406aa6141f7564cfeb3c1566ff3226e7..c1960f0031c77a4213ee299df53d5abff722c069 100644 (file)
@@ -167,6 +167,27 @@ dns_dns64_aaaaok(const dns_dns64_t *dns64, const isc_netaddr_t *reqaddr,
  *                     if 'aaaaok' in non NULL.
  */
 
+isc_result_t
+dns_dns64_findprefix(dns_rdataset_t *rdataset, isc_netprefix_t *prefix,
+                    size_t *len);
+/*
+ * Look through 'rdataset' for AAAA pairs which define encoded DNS64 prefixes.
+ * 'len' should be set to the number of entries in 'prefix' and returns
+ * the number of prefixes discovered.  This may be bigger than those that
+ * can fit in 'prefix'.
+ *
+ * Requires
+ *     'rdataset'      to be valid and to be for type AAAA and class IN.
+ *     'prefix'        to be non NULL.
+ *     'len'           to be non NULL and non zero.
+ *
+ * Returns
+ *     ISC_R_SUCCESS
+ *     ISC_R_NOSPACE   if there are more prefixes discovered than can fit
+ *                     into 'prefix'.
+ *     ISC_R_NOTFOUND  no prefixes where found.
+ */
+
 ISC_LANG_ENDDECLS
 
 #endif /* DNS_DNS64_H */
index 04ef09c0f53e20e42162797c0e8f9ecf1956f0a1..3322b892288ef7f16afd9e68b06e805c8b1093fd 100644 (file)
@@ -24,6 +24,7 @@ check_PROGRAMS =              \
        dbversion_test          \
        dh_test                 \
        dispatch_test           \
+       dns64_test              \
        dst_test                \
        geoip_test              \
        keytable_test           \
diff --git a/lib/dns/tests/dns64_test.c b/lib/dns/tests/dns64_test.c
new file mode 100644 (file)
index 0000000..450b09a
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#if HAVE_CMOCKA
+
+#include <sched.h> /* IWYU pragma: keep */
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define UNIT_TESTING
+#include <cmocka.h>
+
+#include <isc/netaddr.h>
+#include <isc/result.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/dns64.h>
+#include <dns/rdata.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+
+static void
+multiple_prefixes(void) {
+       size_t i, count;
+       /*
+        * Two prefix, non consectutive.
+        */
+       unsigned char aaaa[4][16] = {
+               { 0, 0, 0, 0, 192, 0, 0, 170, 0, 0, 0, 0, 192, 0, 0, 171 },
+               { 0, 0, 0, 0, 192, 55, 0, 170, 0, 0, 0, 0, 192, 0, 0, 170 },
+               { 0, 0, 0, 0, 192, 0, 0, 170, 0, 0, 0, 0, 192, 0, 0, 170 },
+               { 0, 0, 0, 0, 192, 55, 0, 170, 0, 0, 0, 0, 192, 0, 0, 171 },
+       };
+       dns_rdataset_t rdataset;
+       dns_rdatalist_t rdatalist;
+       dns_rdata_t rdata[4] = { DNS_RDATA_INIT, DNS_RDATA_INIT, DNS_RDATA_INIT,
+                                DNS_RDATA_INIT };
+       isc_netprefix_t prefix[2];
+       unsigned char p1[] = { 0, 0, 0, 0, 192, 0, 0, 170, 0, 0, 0, 0 };
+       unsigned char p2[] = { 0, 0, 0, 0, 192, 55, 0, 170, 0, 0, 0, 0 };
+       isc_result_t result;
+       bool have_p1, have_p2;
+
+       /*
+        * Construct AAAA rdataset containing 2 prefixes.
+        */
+       dns_rdatalist_init(&rdatalist);
+       for (i = 0; i < 4; i++) {
+               isc_region_t region;
+               region.base = aaaa[i];
+               region.length = 16;
+               dns_rdata_fromregion(&rdata[i], dns_rdataclass_in,
+                                    dns_rdatatype_aaaa, &region);
+               ISC_LIST_APPEND(rdatalist.rdata, &rdata[i], link);
+       }
+       rdatalist.type = rdata[0].type;
+       rdatalist.rdclass = rdata[0].rdclass;
+       rdatalist.ttl = 0;
+       dns_rdataset_init(&rdataset);
+       result = dns_rdatalist_tordataset(&rdatalist, &rdataset);
+       assert_int_equal(result, ISC_R_SUCCESS);
+
+       count = ARRAY_SIZE(prefix);
+       memset(&prefix, 0, sizeof(prefix));
+       result = dns_dns64_findprefix(&rdataset, prefix, &count);
+       assert_int_equal(result, ISC_R_SUCCESS);
+       assert_int_equal(count, 2);
+       have_p1 = have_p2 = false;
+       for (i = 0; i < count; i++) {
+               assert_int_equal(prefix[i].prefixlen, 96);
+               assert_int_equal(prefix[i].addr.family, AF_INET6);
+               if (memcmp(prefix[i].addr.type.in6.s6_addr, p1, 12) == 0) {
+                       have_p1 = true;
+               }
+               if (memcmp(prefix[i].addr.type.in6.s6_addr, p2, 12) == 0) {
+                       have_p2 = true;
+               }
+       }
+       assert_true(have_p1);
+       assert_true(have_p2);
+
+       /*
+        * Check that insufficient prefix space returns ISC_R_NOSPACE
+        * and that the prefix is populated.
+        */
+       count = 1;
+       memset(&prefix, 0, sizeof(prefix));
+       result = dns_dns64_findprefix(&rdataset, prefix, &count);
+       assert_int_equal(result, ISC_R_NOSPACE);
+       assert_int_equal(count, 2);
+       have_p1 = have_p2 = false;
+       assert_int_equal(prefix[0].prefixlen, 96);
+       assert_int_equal(prefix[0].addr.family, AF_INET6);
+       if (memcmp(prefix[0].addr.type.in6.s6_addr, p1, 12) == 0) {
+               have_p1 = true;
+       }
+       if (memcmp(prefix[0].addr.type.in6.s6_addr, p2, 12) == 0) {
+               have_p2 = true;
+       }
+       if (!have_p2) {
+               assert_true(have_p1);
+       }
+       if (!have_p1) {
+               assert_true(have_p2);
+       }
+       assert_true(have_p1 != have_p2);
+}
+
+static void
+dns64_findprefix(void **state) {
+       unsigned int i, j, o;
+       isc_result_t result;
+       struct {
+               unsigned char prefix[12];
+               unsigned int prefixlen;
+               isc_result_t result;
+       } tests[] = {
+               /* The WKP with various lengths. */
+               { { 0, 0x64, 0xff, 0x9b, 0, 0, 0, 0, 0, 0, 0, 0 },
+                 32,
+                 ISC_R_SUCCESS },
+               { { 0, 0x64, 0xff, 0x9b, 0, 0, 0, 0, 0, 0, 0, 0 },
+                 40,
+                 ISC_R_SUCCESS },
+               { { 0, 0x64, 0xff, 0x9b, 0, 0, 0, 0, 0, 0, 0, 0 },
+                 48,
+                 ISC_R_SUCCESS },
+               { { 0, 0x64, 0xff, 0x9b, 0, 0, 0, 0, 0, 0, 0, 0 },
+                 56,
+                 ISC_R_SUCCESS },
+               { { 0, 0x64, 0xff, 0x9b, 0, 0, 0, 0, 0, 0, 0, 0 },
+                 64,
+                 ISC_R_SUCCESS },
+               { { 0, 0x64, 0xff, 0x9b, 0, 0, 0, 0, 0, 0, 0, 0 },
+                 96,
+                 ISC_R_SUCCESS },
+               /*
+                * Prefix with the mapped addresses also appearing in the
+                * prefix.
+                */
+               { { 0, 0, 0, 0, 192, 0, 0, 170, 0, 0, 0, 0 },
+                 96,
+                 ISC_R_SUCCESS },
+               { { 0, 0, 0, 0, 192, 0, 0, 171, 0, 0, 0, 0 },
+                 96,
+                 ISC_R_SUCCESS },
+               /* Bad prefix, MBZ != 0. */
+               { { 0, 0x64, 0xff, 0x9b, 0, 0, 0, 0, 1, 0, 0, 0 },
+                 96,
+                 ISC_R_NOTFOUND },
+       };
+
+       UNUSED(state);
+
+       for (i = 0; i < ARRAY_SIZE(tests); i++) {
+               size_t count = 2;
+               dns_rdataset_t rdataset;
+               dns_rdatalist_t rdatalist;
+               dns_rdata_t rdata[2] = { DNS_RDATA_INIT, DNS_RDATA_INIT };
+               struct in6_addr ina6[2];
+               isc_netprefix_t prefix[2];
+               unsigned char aa[] = { 192, 0, 0, 170 };
+               unsigned char ab[] = { 192, 0, 0, 171 };
+               isc_region_t region;
+
+               /*
+                * Construct rdata.
+                */
+               memset(ina6[0].s6_addr, 0, sizeof(ina6[0].s6_addr));
+               memset(ina6[1].s6_addr, 0, sizeof(ina6[1].s6_addr));
+               memmove(ina6[0].s6_addr, tests[i].prefix, 12);
+               memmove(ina6[1].s6_addr, tests[i].prefix, 12);
+               o = tests[i].prefixlen / 8;
+               for (j = 0; j < 4; j++) {
+                       if ((o + j) == 8U) {
+                               o++; /* skip mbz */
+                       }
+                       ina6[0].s6_addr[j + o] = aa[j];
+                       ina6[1].s6_addr[j + o] = ab[j];
+               }
+               region.base = ina6[0].s6_addr;
+               region.length = sizeof(ina6[0].s6_addr);
+               dns_rdata_fromregion(&rdata[0], dns_rdataclass_in,
+                                    dns_rdatatype_aaaa, &region);
+               region.base = ina6[1].s6_addr;
+               region.length = sizeof(ina6[1].s6_addr);
+               dns_rdata_fromregion(&rdata[1], dns_rdataclass_in,
+                                    dns_rdatatype_aaaa, &region);
+
+               dns_rdatalist_init(&rdatalist);
+               rdatalist.type = rdata[0].type;
+               rdatalist.rdclass = rdata[0].rdclass;
+               rdatalist.ttl = 0;
+               ISC_LIST_APPEND(rdatalist.rdata, &rdata[0], link);
+               ISC_LIST_APPEND(rdatalist.rdata, &rdata[1], link);
+               dns_rdataset_init(&rdataset);
+               result = dns_rdatalist_tordataset(&rdatalist, &rdataset);
+               assert_int_equal(result, ISC_R_SUCCESS);
+
+               result = dns_dns64_findprefix(&rdataset, prefix, &count);
+               assert_int_equal(result, tests[i].result);
+               if (tests[i].result == ISC_R_SUCCESS) {
+                       assert_int_equal(count, 1);
+                       assert_int_equal(prefix[0].prefixlen,
+                                        tests[i].prefixlen);
+                       assert_int_equal(prefix[0].addr.family, AF_INET6);
+                       assert_memory_equal(prefix[0].addr.type.in6.s6_addr,
+                                           tests[i].prefix,
+                                           tests[i].prefixlen / 8);
+               }
+       }
+
+       /*
+        * Test multiple prefixes.
+        */
+       multiple_prefixes();
+}
+
+int
+main(void) {
+       const struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown(
+               dns64_findprefix, NULL, NULL) };
+
+       return (cmocka_run_group_tests(tests, NULL, NULL));
+}
+
+#else /* HAVE_CMOCKA */
+
+#include <stdio.h>
+
+int
+main(void) {
+       printf("1..0 # Skipped: cmocka not available\n");
+       return (0);
+}
+
+#endif
index 63d9530f0500d7e8d9a612a3e0be5011bb2da8f5..b9de1fee66f7564d9ef2e38ed307afb6c4f3a4b3 100644 (file)
@@ -305,6 +305,7 @@ dns_dns64_aaaaok
 dns_dns64_append
 dns_dns64_create
 dns_dns64_destroy
+dns_dns64_findprefix
 dns_dns64_next
 dns_dns64_unlink
 @IF NOTYET
index 83b1ef4fb4e58957e406c04905e0b0ee83d6c2c5..d4d417e852e79e9206a45442f7757d7f0b84fbdb 100644 (file)
@@ -40,6 +40,11 @@ struct isc_netaddr {
        uint32_t zone;
 };
 
+struct isc_netprefix {
+       isc_netaddr_t addr;
+       unsigned int  prefixlen;
+};
+
 bool
 isc_netaddr_equal(const isc_netaddr_t *a, const isc_netaddr_t *b);
 
index b82ff18d2687f4a4c3dd5e72a12dd176ce07619a..b3b66f358c5abcc5d6c39473ce52387c3c3940c6 100644 (file)
@@ -64,6 +64,7 @@ typedef struct isc_logmodule   isc_logmodule_t;     /*%< Log Module */
 typedef struct isc_mem          isc_mem_t;           /*%< Memory */
 typedef struct isc_mempool      isc_mempool_t;       /*%< Memory Pool */
 typedef struct isc_netaddr      isc_netaddr_t;       /*%< Net Address */
+typedef struct isc_netprefix    isc_netprefix_t;     /*%< Net Prefix */
 typedef struct isc_nm           isc_nm_t;            /*%< Network manager */
 typedef struct isc_nmsocket     isc_nmsocket_t; /*%< Network manager socket */
 typedef struct isc_nmiface  isc_nmiface_t;  /*%< Network manager interface.  */
index 161226d4de291cdac326e6ff140ed3b946055d2f..5a50c6523f0f3eabe83ecafacdc5e37182339e3a 100644 (file)
 ./lib/dns/tests/dbversion_test.c               C       2011,2012,2014,2015,2016,2018,2019,2020
 ./lib/dns/tests/dh_test.c                      C       2014,2016,2018,2019,2020
 ./lib/dns/tests/dispatch_test.c                        C       2012,2014,2016,2018,2019,2020
+./lib/dns/tests/dns64_test.c                   C       2019,2020
 ./lib/dns/tests/dnstap_test.c                  C       2015,2016,2017,2018,2019,2020
 ./lib/dns/tests/dnstest.c                      C       2011,2012,2013,2014,2015,2016,2017,2018,2019,2020
 ./lib/dns/tests/dnstest.h                      C       2011,2012,2014,2015,2016,2017,2018,2019,2020