dns64.c revision 1.6.2.1 1 /* $NetBSD: dns64.c,v 1.6.2.1 2024/02/25 15:46:48 martin 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) {
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