1/*	$NetBSD: dns64.c,v 1.7 2024/02/21 22:52:06 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
31struct 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
54isc_result_t
55dns_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
108void
109dns_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
131isc_result_t
132dns_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) {
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
199dns_dns64_t *
200dns_dns64_next(dns_dns64_t *dns64) {
201	dns64 = ISC_LIST_NEXT(dns64, link);
202	return (dns64);
203}
204
205void
206dns_dns64_append(dns_dns64list_t *list, dns_dns64_t *dns64) {
207	ISC_LIST_APPEND(*list, dns64, link);
208}
209
210void
211dns_dns64_unlink(dns_dns64list_t *list, dns_dns64_t *dns64) {
212	ISC_LIST_UNLINK(*list, dns64, link);
213}
214
215bool
216dns_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
320done:
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 */
333static 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
365static unsigned int
366search(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
416isc_result_t
417dns_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