}
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);
+}
* 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 */
dbversion_test \
dh_test \
dispatch_test \
+ dns64_test \
dst_test \
geoip_test \
keytable_test \
--- /dev/null
+/*
+ * 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, ®ion);
+ 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, ®ion);
+ region.base = ina6[1].s6_addr;
+ region.length = sizeof(ina6[1].s6_addr);
+ dns_rdata_fromregion(&rdata[1], dns_rdataclass_in,
+ dns_rdatatype_aaaa, ®ion);
+
+ 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
dns_dns64_append
dns_dns64_create
dns_dns64_destroy
+dns_dns64_findprefix
dns_dns64_next
dns_dns64_unlink
@IF NOTYET
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);
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. */
./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