Home | History | Annotate | Line # | Download | only in dns
      1 /*	$NetBSD: dns64.c,v 1.1 2024/02/18 20:57:31 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 #include <stdbool.h>
     17 #include <string.h>
     18 
     19 #include <isc/list.h>
     20 #include <isc/mem.h>
     21 #include <isc/netaddr.h>
     22 #include <isc/string.h>
     23 #include <isc/util.h>
     24 
     25 #include <dns/acl.h>
     26 #include <dns/dns64.h>
     27 #include <dns/rdata.h>
     28 #include <dns/rdataset.h>
     29 #include <dns/result.h>
     30 
     31 struct dns_dns64 {
     32 	unsigned char bits[16]; /*
     33 				 * Prefix + suffix bits.
     34 				 */
     35 	dns_acl_t *clients;	/*
     36 				 * Which clients get mapped
     37 				 * addresses.
     38 				 */
     39 	dns_acl_t *mapped;	/*
     40 				 * IPv4 addresses to be mapped.
     41 				 */
     42 	dns_acl_t *excluded;	/*
     43 				 * IPv6 addresses that are
     44 				 * treated as not existing.
     45 				 */
     46 	unsigned int prefixlen; /*
     47 				 * Start of mapped address.
     48 				 */
     49 	unsigned int flags;
     50 	isc_mem_t *mctx;
     51 	ISC_LINK(dns_dns64_t) link;
     52 };
     53 
     54 isc_result_t
     55 dns_dns64_create(isc_mem_t *mctx, const isc_netaddr_t *prefix,
     56 		 unsigned int prefixlen, const isc_netaddr_t *suffix,
     57 		 dns_acl_t *clients, dns_acl_t *mapped, dns_acl_t *excluded,
     58 		 unsigned int flags, dns_dns64_t **dns64p) {
     59 	dns_dns64_t *dns64;
     60 	unsigned int nbytes = 16;
     61 
     62 	REQUIRE(prefix != NULL && prefix->family == AF_INET6);
     63 	/* Legal prefix lengths from rfc6052.txt. */
     64 	REQUIRE(prefixlen == 32 || prefixlen == 40 || prefixlen == 48 ||
     65 		prefixlen == 56 || prefixlen == 64 || prefixlen == 96);
     66 	REQUIRE(isc_netaddr_prefixok(prefix, prefixlen) == ISC_R_SUCCESS);
     67 	REQUIRE(dns64p != NULL && *dns64p == NULL);
     68 
     69 	if (suffix != NULL) {
     70 		static const unsigned char zeros[16];
     71 		REQUIRE(prefix->family == AF_INET6);
     72 		nbytes = prefixlen / 8 + 4;
     73 		/* Bits 64-71 are zeros. rfc6052.txt */
     74 		if (prefixlen >= 32 && prefixlen <= 64) {
     75 			nbytes++;
     76 		}
     77 		REQUIRE(memcmp(suffix->type.in6.s6_addr, zeros, nbytes) == 0);
     78 	}
     79 
     80 	dns64 = isc_mem_get(mctx, sizeof(dns_dns64_t));
     81 	memset(dns64->bits, 0, sizeof(dns64->bits));
     82 	memmove(dns64->bits, prefix->type.in6.s6_addr, prefixlen / 8);
     83 	if (suffix != NULL) {
     84 		memmove(dns64->bits + nbytes, suffix->type.in6.s6_addr + nbytes,
     85 			16 - nbytes);
     86 	}
     87 	dns64->clients = NULL;
     88 	if (clients != NULL) {
     89 		dns_acl_attach(clients, &dns64->clients);
     90 	}
     91 	dns64->mapped = NULL;
     92 	if (mapped != NULL) {
     93 		dns_acl_attach(mapped, &dns64->mapped);
     94 	}
     95 	dns64->excluded = NULL;
     96 	if (excluded != NULL) {
     97 		dns_acl_attach(excluded, &dns64->excluded);
     98 	}
     99 	dns64->prefixlen = prefixlen;
    100 	dns64->flags = flags;
    101 	ISC_LINK_INIT(dns64, link);
    102 	dns64->mctx = NULL;
    103 	isc_mem_attach(mctx, &dns64->mctx);
    104 	*dns64p = dns64;
    105 	return (ISC_R_SUCCESS);
    106 }
    107 
    108 void
    109 dns_dns64_destroy(dns_dns64_t **dns64p) {
    110 	dns_dns64_t *dns64;
    111 
    112 	REQUIRE(dns64p != NULL && *dns64p != NULL);
    113 
    114 	dns64 = *dns64p;
    115 	*dns64p = NULL;
    116 
    117 	REQUIRE(!ISC_LINK_LINKED(dns64, link));
    118 
    119 	if (dns64->clients != NULL) {
    120 		dns_acl_detach(&dns64->clients);
    121 	}
    122 	if (dns64->mapped != NULL) {
    123 		dns_acl_detach(&dns64->mapped);
    124 	}
    125 	if (dns64->excluded != NULL) {
    126 		dns_acl_detach(&dns64->excluded);
    127 	}
    128 	isc_mem_putanddetach(&dns64->mctx, dns64, sizeof(*dns64));
    129 }
    130 
    131 isc_result_t
    132 dns_dns64_aaaafroma(const dns_dns64_t *dns64, const isc_netaddr_t *reqaddr,
    133 		    const dns_name_t *reqsigner, const dns_aclenv_t *env,
    134 		    unsigned int flags, unsigned char *a, unsigned char *aaaa) {
    135 	unsigned int nbytes, i;
    136 	isc_result_t result;
    137 	int match;
    138 
    139 	if ((dns64->flags & DNS_DNS64_RECURSIVE_ONLY) != 0 &&
    140 	    (flags & DNS_DNS64_RECURSIVE) == 0)
    141 	{
    142 		return (DNS_R_DISALLOWED);
    143 	}
    144 
    145 	if ((dns64->flags & DNS_DNS64_BREAK_DNSSEC) == 0 &&
    146 	    (flags & DNS_DNS64_DNSSEC) != 0)
    147 	{
    148 		return (DNS_R_DISALLOWED);
    149 	}
    150 
    151 	if (dns64->clients != NULL) {
    152 		result = dns_acl_match(reqaddr, reqsigner, dns64->clients, env,
    153 				       &match, NULL);
    154 		if (result != ISC_R_SUCCESS) {
    155 			return (result);
    156 		}
    157 		if (match <= 0) {
    158 			return (DNS_R_DISALLOWED);
    159 		}
    160 	}
    161 
    162 	if (dns64->mapped != NULL) {
    163 		struct in_addr ina;
    164 		isc_netaddr_t netaddr;
    165 
    166 		memmove(&ina.s_addr, a, 4);
    167 		isc_netaddr_fromin(&netaddr, &ina);
    168 		result = dns_acl_match(&netaddr, NULL, dns64->mapped, env,
    169 				       &match, NULL);
    170 		if (result != ISC_R_SUCCESS) {
    171 			return (result);
    172 		}
    173 		if (match <= 0) {
    174 			return (DNS_R_DISALLOWED);
    175 		}
    176 	}
    177 
    178 	nbytes = dns64->prefixlen / 8;
    179 	INSIST(nbytes <= 12);
    180 	/* Copy prefix. */
    181 	memmove(aaaa, dns64->bits, nbytes);
    182 	/* Bits 64-71 are zeros. rfc6052.txt */
    183 	if (nbytes == 8) {
    184 		aaaa[nbytes++] = 0;
    185 	}
    186 	/* Copy mapped address. */
    187 	for (i = 0; i < 4U; i++) {
    188 		aaaa[nbytes++] = a[i];
    189 		/* Bits 64-71 are zeros. rfc6052.txt */
    190 		if (nbytes == 8) {
    191 			aaaa[nbytes++] = 0;
    192 		}
    193 	}
    194 	/* Copy suffix. */
    195 	memmove(aaaa + nbytes, dns64->bits + nbytes, 16 - nbytes);
    196 	return (ISC_R_SUCCESS);
    197 }
    198 
    199 dns_dns64_t *
    200 dns_dns64_next(dns_dns64_t *dns64) {
    201 	dns64 = ISC_LIST_NEXT(dns64, link);
    202 	return (dns64);
    203 }
    204 
    205 void
    206 dns_dns64_append(dns_dns64list_t *list, dns_dns64_t *dns64) {
    207 	ISC_LIST_APPEND(*list, dns64, link);
    208 }
    209 
    210 void
    211 dns_dns64_unlink(dns_dns64list_t *list, dns_dns64_t *dns64) {
    212 	ISC_LIST_UNLINK(*list, dns64, link);
    213 }
    214 
    215 bool
    216 dns_dns64_aaaaok(const dns_dns64_t *dns64, const isc_netaddr_t *reqaddr,
    217 		 const dns_name_t *reqsigner, const dns_aclenv_t *env,
    218 		 unsigned int flags, dns_rdataset_t *rdataset, bool *aaaaok,
    219 		 size_t aaaaoklen) {
    220 	struct in6_addr in6;
    221 	isc_netaddr_t netaddr;
    222 	isc_result_t result;
    223 	int match;
    224 	bool answer = false;
    225 	bool found = false;
    226 	unsigned int i, ok;
    227 
    228 	REQUIRE(rdataset != NULL);
    229 	REQUIRE(rdataset->type == dns_rdatatype_aaaa);
    230 	REQUIRE(rdataset->rdclass == dns_rdataclass_in);
    231 	if (aaaaok != NULL) {
    232 		REQUIRE(aaaaoklen == dns_rdataset_count(rdataset));
    233 	}
    234 
    235 	for (; dns64 != NULL; dns64 = ISC_LIST_NEXT(dns64, link)) {
    236 		if ((dns64->flags & DNS_DNS64_RECURSIVE_ONLY) != 0 &&
    237 		    (flags & DNS_DNS64_RECURSIVE) == 0)
    238 		{
    239 			continue;
    240 		}
    241 
    242 		if ((dns64->flags & DNS_DNS64_BREAK_DNSSEC) == 0 &&
    243 		    (flags & DNS_DNS64_DNSSEC) != 0)
    244 		{
    245 			continue;
    246 		}
    247 		/*
    248 		 * Work out if this dns64 structure applies to this client.
    249 		 */
    250 		if (dns64->clients != NULL) {
    251 			result = dns_acl_match(reqaddr, reqsigner,
    252 					       dns64->clients, env, &match,
    253 					       NULL);
    254 			if (result != ISC_R_SUCCESS) {
    255 				continue;
    256 			}
    257 			if (match <= 0) {
    258 				continue;
    259 			}
    260 		}
    261 
    262 		if (!found && aaaaok != NULL) {
    263 			for (i = 0; i < aaaaoklen; i++) {
    264 				aaaaok[i] = false;
    265 			}
    266 		}
    267 		found = true;
    268 
    269 		/*
    270 		 * If we are not excluding any addresses then any AAAA
    271 		 * will do.
    272 		 */
    273 		if (dns64->excluded == NULL) {
    274 			answer = true;
    275 			if (aaaaok == NULL) {
    276 				goto done;
    277 			}
    278 			for (i = 0; i < aaaaoklen; i++) {
    279 				aaaaok[i] = true;
    280 			}
    281 			goto done;
    282 		}
    283 
    284 		i = 0;
    285 		ok = 0;
    286 		for (result = dns_rdataset_first(rdataset);
    287 		     result == ISC_R_SUCCESS;
    288 		     result = dns_rdataset_next(rdataset))
    289 		{
    290 			dns_rdata_t rdata = DNS_RDATA_INIT;
    291 			if (aaaaok == NULL || !aaaaok[i]) {
    292 				dns_rdataset_current(rdataset, &rdata);
    293 				memmove(&in6.s6_addr, rdata.data, 16);
    294 				isc_netaddr_fromin6(&netaddr, &in6);
    295 
    296 				result = dns_acl_match(&netaddr, NULL,
    297 						       dns64->excluded, env,
    298 						       &match, NULL);
    299 				if (result == ISC_R_SUCCESS && match <= 0) {
    300 					answer = true;
    301 					if (aaaaok == NULL) {
    302 						goto done;
    303 					}
    304 					aaaaok[i] = true;
    305 					ok++;
    306 				}
    307 			} else {
    308 				ok++;
    309 			}
    310 			i++;
    311 		}
    312 		/*
    313 		 * Are all addresses ok?
    314 		 */
    315 		if (aaaaok != NULL && ok == aaaaoklen) {
    316 			goto done;
    317 		}
    318 	}
    319 
    320 done:
    321 	if (!found && aaaaok != NULL) {
    322 		for (i = 0; i < aaaaoklen; i++) {
    323 			aaaaok[i] = true;
    324 		}
    325 	}
    326 	return (found ? answer : true);
    327 }
    328