2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-30 14:07:59 +00:00

add dns_dns64_findprefix

This commit is contained in:
Mark Andrews 2019-07-23 03:44:30 +10:00
parent 9c8dd9b678
commit 38d6f68de4
8 changed files with 439 additions and 0 deletions

View 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);
}

View 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 */

View File

@ -24,6 +24,7 @@ check_PROGRAMS = \
dbversion_test \
dh_test \
dispatch_test \
dns64_test \
dst_test \
geoip_test \
keytable_test \

250
lib/dns/tests/dns64_test.c Normal file
View File

@ -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

View 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

View 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);

View 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. */

View File

@ -1637,6 +1637,7 @@
./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