1/* $NetBSD: dns64.c,v 1.3.4.1 2012/06/05 21:15:01 bouyer Exp $ */ 2 3/* 4 * Copyright (C) 2010, 2011 Internet Systems Consortium, Inc. ("ISC") 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 11 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 12 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 13 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 15 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 * PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19/* Id: dns64.c,v 1.8 2011/03/12 04:59:47 tbox Exp */ 20 21#include <config.h> 22 23#include <isc/list.h> 24#include <isc/mem.h> 25#include <isc/netaddr.h> 26#include <isc/string.h> 27#include <isc/util.h> 28 29#include <dns/acl.h> 30#include <dns/dns64.h> 31#include <dns/rdata.h> 32#include <dns/rdataset.h> 33#include <dns/result.h> 34 35struct dns_dns64 { 36 unsigned char bits[16]; /* 37 * Prefix + suffix bits. 38 */ 39 dns_acl_t * clients; /* 40 * Which clients get mapped 41 * addresses. 42 */ 43 dns_acl_t * mapped; /* 44 * IPv4 addresses to be mapped. 45 */ 46 dns_acl_t * excluded; /* 47 * IPv6 addresses that are 48 * treated as not existing. 49 */ 50 unsigned int prefixlen; /* 51 * Start of mapped address. 52 */ 53 unsigned int flags; 54 isc_mem_t * mctx; 55 ISC_LINK(dns_dns64_t) link; 56}; 57 58isc_result_t 59dns_dns64_create(isc_mem_t *mctx, isc_netaddr_t *prefix, 60 unsigned int prefixlen, isc_netaddr_t *suffix, 61 dns_acl_t *clients, dns_acl_t *mapped, dns_acl_t *excluded, 62 unsigned int flags, dns_dns64_t **dns64) 63{ 64 dns_dns64_t *new; 65 unsigned int nbytes = 16; 66 67 REQUIRE(prefix != NULL && prefix->family == AF_INET6); 68 /* Legal prefix lengths from draft-ietf-behave-address-format-04. */ 69 REQUIRE(prefixlen == 32 || prefixlen == 40 || prefixlen == 48 || 70 prefixlen == 56 || prefixlen == 64 || prefixlen == 96); 71 REQUIRE(isc_netaddr_prefixok(prefix, prefixlen) == ISC_R_SUCCESS); 72 REQUIRE(dns64 != NULL && *dns64 == NULL); 73 74 if (suffix != NULL) { 75 static const unsigned char zeros[16]; 76 REQUIRE(prefix->family == AF_INET6); 77 nbytes = prefixlen / 8 + 4; 78 /* Bits 64-71 are zeros. draft-ietf-behave-address-format-04 */ 79 if (prefixlen >= 32 && prefixlen <= 64) 80 nbytes++; 81 REQUIRE(memcmp(suffix->type.in6.s6_addr, zeros, nbytes) == 0); 82 } 83 84 new = isc_mem_get(mctx, sizeof(dns_dns64_t)); 85 if (new == NULL) 86 return (ISC_R_NOMEMORY); 87 memset(new->bits, 0, sizeof(new->bits)); 88 memcpy(new->bits, prefix->type.in6.s6_addr, prefixlen / 8); 89 if (suffix != NULL) 90 memcpy(new->bits + nbytes, suffix->type.in6.s6_addr + nbytes, 91 16 - nbytes); 92 new->clients = NULL; 93 if (clients != NULL) 94 dns_acl_attach(clients, &new->clients); 95 new->mapped = NULL; 96 if (mapped != NULL) 97 dns_acl_attach(mapped, &new->mapped); 98 new->excluded = NULL; 99 if (excluded != NULL) 100 dns_acl_attach(excluded, &new->excluded); 101 new->prefixlen = prefixlen; 102 new->flags = flags; 103 ISC_LINK_INIT(new, link); 104 new->mctx = NULL; 105 isc_mem_attach(mctx, &new->mctx); 106 *dns64 = new; 107 return (ISC_R_SUCCESS); 108} 109 110void 111dns_dns64_destroy(dns_dns64_t **dns64p) { 112 dns_dns64_t *dns64; 113 114 REQUIRE(dns64p != NULL && *dns64p != NULL); 115 116 dns64 = *dns64p; 117 *dns64p = NULL; 118 119 REQUIRE(!ISC_LINK_LINKED(dns64, link)); 120 121 if (dns64->clients != NULL) 122 dns_acl_detach(&dns64->clients); 123 if (dns64->mapped != NULL) 124 dns_acl_detach(&dns64->mapped); 125 if (dns64->excluded != NULL) 126 dns_acl_detach(&dns64->excluded); 127 isc_mem_putanddetach(&dns64->mctx, dns64, sizeof(*dns64)); 128} 129 130isc_result_t 131dns_dns64_aaaafroma(const dns_dns64_t *dns64, const isc_netaddr_t *reqaddr, 132 const dns_name_t *reqsigner, const dns_aclenv_t *env, 133 unsigned int flags, unsigned char *a, unsigned char *aaaa) 134{ 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 return (DNS_R_DISALLOWED); 142 143 if ((dns64->flags & DNS_DNS64_BREAK_DNSSEC) == 0 && 144 (flags & DNS_DNS64_DNSSEC) != 0) 145 return (DNS_R_DISALLOWED); 146 147 if (dns64->clients != NULL) { 148 result = dns_acl_match(reqaddr, reqsigner, dns64->clients, env, 149 &match, NULL); 150 if (result != ISC_R_SUCCESS) 151 return (result); 152 if (match <= 0) 153 return (DNS_R_DISALLOWED); 154 } 155 156 if (dns64->mapped != NULL) { 157 struct in_addr ina; 158 isc_netaddr_t netaddr; 159 160 memcpy(&ina.s_addr, a, 4); 161 isc_netaddr_fromin(&netaddr, &ina); 162 result = dns_acl_match(&netaddr, NULL, dns64->mapped, env, 163 &match, NULL); 164 if (result != ISC_R_SUCCESS) 165 return (result); 166 if (match <= 0) 167 return (DNS_R_DISALLOWED); 168 } 169 170 nbytes = dns64->prefixlen / 8; 171 INSIST(nbytes <= 12); 172 /* Copy prefix. */ 173 memcpy(aaaa, dns64->bits, nbytes); 174 /* Bits 64-71 are zeros. draft-ietf-behave-address-format-04 */ 175 if (nbytes == 8) 176 aaaa[nbytes++] = 0; 177 /* Copy mapped address. */ 178 for (i = 0; i < 4U; i++) { 179 aaaa[nbytes++] = a[i]; 180 /* Bits 64-71 are zeros. draft-ietf-behave-address-format-04 */ 181 if (nbytes == 8) 182 aaaa[nbytes++] = 0; 183 } 184 /* Copy suffix. */ 185 memcpy(aaaa + nbytes, dns64->bits + nbytes, 16 - nbytes); 186 return (ISC_R_SUCCESS); 187} 188 189dns_dns64_t * 190dns_dns64_next(dns_dns64_t *dns64) { 191 dns64 = ISC_LIST_NEXT(dns64, link); 192 return (dns64); 193} 194 195void 196dns_dns64_append(dns_dns64list_t *list, dns_dns64_t *dns64) { 197 ISC_LIST_APPEND(*list, dns64, link); 198} 199 200void 201dns_dns64_unlink(dns_dns64list_t *list, dns_dns64_t *dns64) { 202 ISC_LIST_UNLINK(*list, dns64, link); 203} 204 205isc_boolean_t 206dns_dns64_aaaaok(const dns_dns64_t *dns64, const isc_netaddr_t *reqaddr, 207 const dns_name_t *reqsigner, const dns_aclenv_t *env, 208 unsigned int flags, dns_rdataset_t *rdataset, 209 isc_boolean_t *aaaaok, size_t aaaaoklen) 210{ 211 struct in6_addr in6; 212 isc_netaddr_t netaddr; 213 isc_result_t result; 214 int match; 215 isc_boolean_t answer = ISC_FALSE; 216 isc_boolean_t found = ISC_FALSE; 217 unsigned int i, ok; 218 219 REQUIRE(rdataset != NULL); 220 REQUIRE(rdataset->type == dns_rdatatype_aaaa); 221 REQUIRE(rdataset->rdclass == dns_rdataclass_in); 222 if (aaaaok != NULL) 223 REQUIRE(aaaaoklen == dns_rdataset_count(rdataset)); 224 225 for (;dns64 != NULL; dns64 = ISC_LIST_NEXT(dns64, link)) { 226 if ((dns64->flags & DNS_DNS64_RECURSIVE_ONLY) != 0 && 227 (flags & DNS_DNS64_RECURSIVE) == 0) 228 continue; 229 230 if ((dns64->flags & DNS_DNS64_BREAK_DNSSEC) == 0 && 231 (flags & DNS_DNS64_DNSSEC) != 0) 232 continue; 233 /* 234 * Work out if this dns64 structure applies to this client. 235 */ 236 if (dns64->clients != NULL) { 237 result = dns_acl_match(reqaddr, reqsigner, 238 dns64->clients, env, 239 &match, NULL); 240 if (result != ISC_R_SUCCESS) 241 continue; 242 if (match <= 0) 243 continue; 244 } 245 246 if (!found && aaaaok != NULL) { 247 for (i = 0; i < aaaaoklen; i++) 248 aaaaok[i] = ISC_FALSE; 249 } 250 found = ISC_TRUE; 251 252 /* 253 * If we are not excluding any addresses then any AAAA 254 * will do. 255 */ 256 if (dns64->excluded == NULL) { 257 answer = ISC_TRUE; 258 if (aaaaok == NULL) 259 goto done; 260 for (i = 0; i < aaaaoklen; i++) 261 aaaaok[i] = ISC_TRUE; 262 goto done; 263 } 264 265 i = 0; ok = 0; 266 for (result = dns_rdataset_first(rdataset); 267 result == ISC_R_SUCCESS; 268 result = dns_rdataset_next(rdataset)) { 269 dns_rdata_t rdata = DNS_RDATA_INIT; 270 if (aaaaok == NULL || !aaaaok[i]) { 271 272 dns_rdataset_current(rdataset, &rdata); 273 memcpy(&in6.s6_addr, rdata.data, 16); 274 isc_netaddr_fromin6(&netaddr, &in6); 275 276 result = dns_acl_match(&netaddr, NULL, 277 dns64->excluded, 278 env, &match, NULL); 279 if (result == ISC_R_SUCCESS && match <= 0) { 280 answer = ISC_TRUE; 281 if (aaaaok == NULL) 282 goto done; 283 aaaaok[i] = ISC_TRUE; 284 ok++; 285 } 286 } else 287 ok++; 288 i++; 289 } 290 /* 291 * Are all addresses ok? 292 */ 293 if (aaaaok != NULL && ok == aaaaoklen) 294 goto done; 295 } 296 297 done: 298 if (!found && aaaaok != NULL) { 299 for (i = 0; i < aaaaoklen; i++) 300 aaaaok[i] = ISC_TRUE; 301 } 302 return (found ? answer : ISC_TRUE); 303} 304