xref: /netbsd-src/external/mpl/bind/dist/lib/dns/dns64.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: dns64.c,v 1.8 2025/01/26 16:25:22 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/result.h>
23 #include <isc/string.h>
24 #include <isc/util.h>
25 
26 #include <dns/acl.h>
27 #include <dns/dns64.h>
28 #include <dns/rdata.h>
29 #include <dns/rdataset.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, 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 && reqaddr != 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, 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 
329 /*
330  * Posible mapping of IPV4ONLY.ARPA A records into AAAA records
331  * for valid RFC6052 prefixes.
332  */
333 static struct {
334 	const unsigned char aa[16]; /* mapped version of 192.0.0.170 */
335 	const unsigned char ab[16]; /* mapped version of 192.0.0.171 */
336 	const unsigned char mask[16];
337 	const unsigned int plen;
338 } const prefixes[6] = {
339 	{ { 0, 0, 0, 0, 192, 0, 0, 170, 0, 0, 0, 0, 0, 0, 0, 0 },
340 	  { 0, 0, 0, 0, 192, 0, 0, 171, 0, 0, 0, 0, 0, 0, 0, 0 },
341 	  { 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0 },
342 	  32 },
343 	{ { 0, 0, 0, 0, 0, 192, 0, 0, 0, 170, 0, 0, 0, 0, 0, 0 },
344 	  { 0, 0, 0, 0, 0, 192, 0, 0, 0, 171, 0, 0, 0, 0, 0, 0 },
345 	  { 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0 },
346 	  40 },
347 	{ { 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 170, 0, 0, 0, 0, 0 },
348 	  { 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 171, 0, 0, 0, 0, 0 },
349 	  { 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0 },
350 	  48 },
351 	{ { 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 170, 0, 0, 0, 0 },
352 	  { 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 171, 0, 0, 0, 0 },
353 	  { 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0 },
354 	  56 },
355 	{ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 170, 0, 0, 0 },
356 	  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 171, 0, 0, 0 },
357 	  { 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0 },
358 	  64 },
359 	{ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 170 },
360 	  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 171 },
361 	  { 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255 },
362 	  96 }
363 };
364 
365 static unsigned int
366 search(const dns_rdata_t *rd1, const dns_rdata_t *rd2, unsigned int plen) {
367 	unsigned int i = 0, j;
368 	const unsigned char *c, *m;
369 
370 	/*
371 	 * Resume looking for another aa match?
372 	 */
373 	if (plen != 0U && rd2 == NULL) {
374 		while (i < 6U) {
375 			/* Post increment as we resume on next entry. */
376 			if (prefixes[i++].plen == plen) {
377 				break;
378 			}
379 		}
380 	}
381 
382 	for (; i < 6U; i++) {
383 		j = 0;
384 		if (rd2 != NULL) {
385 			/* Find the right entry. */
386 			if (prefixes[i].plen != plen) {
387 				continue;
388 			}
389 			/* Does the prefix match? */
390 			while ((j * 8U) < plen) {
391 				if (rd1->data[j] != rd2->data[j]) {
392 					return 0;
393 				}
394 				j++;
395 			}
396 		}
397 
398 		/* Match well known mapped addresses. */
399 		c = (rd2 == NULL) ? prefixes[i].aa : prefixes[i].ab;
400 		m = prefixes[i].mask;
401 		for (; j < 16U; j++) {
402 			if ((rd1->data[j] & m[j]) != (c[j] & m[j])) {
403 				break;
404 			}
405 		}
406 		if (j == 16U) {
407 			return prefixes[i].plen;
408 		}
409 		if (rd2 != NULL) {
410 			return 0;
411 		}
412 	}
413 	return 0;
414 }
415 
416 isc_result_t
417 dns_dns64_findprefix(dns_rdataset_t *rdataset, isc_netprefix_t *prefix,
418 		     size_t *len) {
419 	dns_rdataset_t outer, inner;
420 	unsigned int oplen, iplen;
421 	size_t count = 0;
422 	struct in6_addr ina6;
423 	isc_result_t result;
424 
425 	REQUIRE(prefix != NULL && len != NULL && *len != 0U);
426 	REQUIRE(rdataset != NULL && rdataset->type == dns_rdatatype_aaaa);
427 
428 	dns_rdataset_init(&outer);
429 	dns_rdataset_init(&inner);
430 	dns_rdataset_clone(rdataset, &outer);
431 	dns_rdataset_clone(rdataset, &inner);
432 
433 	for (result = dns_rdataset_first(&outer); result == ISC_R_SUCCESS;
434 	     result = dns_rdataset_next(&outer))
435 	{
436 		dns_rdata_t rd1 = DNS_RDATA_INIT;
437 		dns_rdataset_current(&outer, &rd1);
438 		oplen = 0;
439 	resume:
440 		/* Look for a 192.0.0.170 match. */
441 		oplen = search(&rd1, NULL, oplen);
442 		if (oplen == 0) {
443 			continue;
444 		}
445 
446 		/* Look for the 192.0.0.171 match. */
447 		for (result = dns_rdataset_first(&inner);
448 		     result == ISC_R_SUCCESS;
449 		     result = dns_rdataset_next(&inner))
450 		{
451 			dns_rdata_t rd2 = DNS_RDATA_INIT;
452 
453 			dns_rdataset_current(&inner, &rd2);
454 			iplen = search(&rd2, &rd1, oplen);
455 			if (iplen == 0) {
456 				continue;
457 			}
458 			INSIST(iplen == oplen);
459 			if (count >= *len) {
460 				count++;
461 				break;
462 			}
463 
464 			/* We have a prefix. */
465 			memset(ina6.s6_addr, 0, sizeof(ina6.s6_addr));
466 			memmove(ina6.s6_addr, rd1.data, oplen / 8);
467 			isc_netaddr_fromin6(&prefix[count].addr, &ina6);
468 			prefix[count].prefixlen = oplen;
469 			count++;
470 			break;
471 		}
472 		/* Didn't find a match look for a different prefix length. */
473 		if (result == ISC_R_NOMORE) {
474 			goto resume;
475 		}
476 	}
477 	if (count == 0U) {
478 		return ISC_R_NOTFOUND;
479 	}
480 	if (count > *len) {
481 		*len = count;
482 		return ISC_R_NOSPACE;
483 	}
484 	*len = count;
485 	return ISC_R_SUCCESS;
486 }
487