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