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 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, 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 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, 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 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