dns64.c revision 1.1.1.4 1 /* $NetBSD: dns64.c,v 1.1.1.4 2021/02/19 16:37:12 christos Exp $ */
2
3 /*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public
7 * License, v. 2.0. If a copy of the MPL was not distributed with this
8 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9 *
10 * See the COPYRIGHT file distributed with this work for additional
11 * information regarding copyright ownership.
12 */
13
14 #include <stdbool.h>
15 #include <string.h>
16
17 #include <isc/list.h>
18 #include <isc/mem.h>
19 #include <isc/netaddr.h>
20 #include <isc/string.h>
21 #include <isc/util.h>
22
23 #include <dns/acl.h>
24 #include <dns/dns64.h>
25 #include <dns/rdata.h>
26 #include <dns/rdataset.h>
27 #include <dns/result.h>
28
29 struct dns_dns64 {
30 unsigned char bits[16]; /*
31 * Prefix + suffix bits.
32 */
33 dns_acl_t *clients; /*
34 * Which clients get mapped
35 * addresses.
36 */
37 dns_acl_t *mapped; /*
38 * IPv4 addresses to be mapped.
39 */
40 dns_acl_t *excluded; /*
41 * IPv6 addresses that are
42 * treated as not existing.
43 */
44 unsigned int prefixlen; /*
45 * Start of mapped address.
46 */
47 unsigned int flags;
48 isc_mem_t *mctx;
49 ISC_LINK(dns_dns64_t) link;
50 };
51
52 isc_result_t
53 dns_dns64_create(isc_mem_t *mctx, const isc_netaddr_t *prefix,
54 unsigned int prefixlen, const isc_netaddr_t *suffix,
55 dns_acl_t *clients, dns_acl_t *mapped, dns_acl_t *excluded,
56 unsigned int flags, dns_dns64_t **dns64p) {
57 dns_dns64_t *dns64;
58 unsigned int nbytes = 16;
59
60 REQUIRE(prefix != NULL && prefix->family == AF_INET6);
61 /* Legal prefix lengths from rfc6052.txt. */
62 REQUIRE(prefixlen == 32 || prefixlen == 40 || prefixlen == 48 ||
63 prefixlen == 56 || prefixlen == 64 || prefixlen == 96);
64 REQUIRE(isc_netaddr_prefixok(prefix, prefixlen) == ISC_R_SUCCESS);
65 REQUIRE(dns64p != NULL && *dns64p == NULL);
66
67 if (suffix != NULL) {
68 static const unsigned char zeros[16];
69 REQUIRE(prefix->family == AF_INET6);
70 nbytes = prefixlen / 8 + 4;
71 /* Bits 64-71 are zeros. rfc6052.txt */
72 if (prefixlen >= 32 && prefixlen <= 64) {
73 nbytes++;
74 }
75 REQUIRE(memcmp(suffix->type.in6.s6_addr, zeros, nbytes) == 0);
76 }
77
78 dns64 = isc_mem_get(mctx, sizeof(dns_dns64_t));
79 memset(dns64->bits, 0, sizeof(dns64->bits));
80 memmove(dns64->bits, prefix->type.in6.s6_addr, prefixlen / 8);
81 if (suffix != NULL) {
82 memmove(dns64->bits + nbytes, suffix->type.in6.s6_addr + nbytes,
83 16 - nbytes);
84 }
85 dns64->clients = NULL;
86 if (clients != NULL) {
87 dns_acl_attach(clients, &dns64->clients);
88 }
89 dns64->mapped = NULL;
90 if (mapped != NULL) {
91 dns_acl_attach(mapped, &dns64->mapped);
92 }
93 dns64->excluded = NULL;
94 if (excluded != NULL) {
95 dns_acl_attach(excluded, &dns64->excluded);
96 }
97 dns64->prefixlen = prefixlen;
98 dns64->flags = flags;
99 ISC_LINK_INIT(dns64, link);
100 dns64->mctx = NULL;
101 isc_mem_attach(mctx, &dns64->mctx);
102 *dns64p = dns64;
103 return (ISC_R_SUCCESS);
104 }
105
106 void
107 dns_dns64_destroy(dns_dns64_t **dns64p) {
108 dns_dns64_t *dns64;
109
110 REQUIRE(dns64p != NULL && *dns64p != NULL);
111
112 dns64 = *dns64p;
113 *dns64p = NULL;
114
115 REQUIRE(!ISC_LINK_LINKED(dns64, link));
116
117 if (dns64->clients != NULL) {
118 dns_acl_detach(&dns64->clients);
119 }
120 if (dns64->mapped != NULL) {
121 dns_acl_detach(&dns64->mapped);
122 }
123 if (dns64->excluded != NULL) {
124 dns_acl_detach(&dns64->excluded);
125 }
126 isc_mem_putanddetach(&dns64->mctx, dns64, sizeof(*dns64));
127 }
128
129 isc_result_t
130 dns_dns64_aaaafroma(const dns_dns64_t *dns64, const isc_netaddr_t *reqaddr,
131 const dns_name_t *reqsigner, const dns_aclenv_t *env,
132 unsigned int flags, unsigned char *a, unsigned char *aaaa) {
133 unsigned int nbytes, i;
134 isc_result_t result;
135 int match;
136
137 if ((dns64->flags & DNS_DNS64_RECURSIVE_ONLY) != 0 &&
138 (flags & DNS_DNS64_RECURSIVE) == 0)
139 {
140 return (DNS_R_DISALLOWED);
141 }
142
143 if ((dns64->flags & DNS_DNS64_BREAK_DNSSEC) == 0 &&
144 (flags & DNS_DNS64_DNSSEC) != 0)
145 {
146 return (DNS_R_DISALLOWED);
147 }
148
149 if (dns64->clients != NULL) {
150 result = dns_acl_match(reqaddr, reqsigner, dns64->clients, env,
151 &match, NULL);
152 if (result != ISC_R_SUCCESS) {
153 return (result);
154 }
155 if (match <= 0) {
156 return (DNS_R_DISALLOWED);
157 }
158 }
159
160 if (dns64->mapped != NULL) {
161 struct in_addr ina;
162 isc_netaddr_t netaddr;
163
164 memmove(&ina.s_addr, a, 4);
165 isc_netaddr_fromin(&netaddr, &ina);
166 result = dns_acl_match(&netaddr, NULL, dns64->mapped, env,
167 &match, NULL);
168 if (result != ISC_R_SUCCESS) {
169 return (result);
170 }
171 if (match <= 0) {
172 return (DNS_R_DISALLOWED);
173 }
174 }
175
176 nbytes = dns64->prefixlen / 8;
177 INSIST(nbytes <= 12);
178 /* Copy prefix. */
179 memmove(aaaa, dns64->bits, nbytes);
180 /* Bits 64-71 are zeros. rfc6052.txt */
181 if (nbytes == 8) {
182 aaaa[nbytes++] = 0;
183 }
184 /* Copy mapped address. */
185 for (i = 0; i < 4U; i++) {
186 aaaa[nbytes++] = a[i];
187 /* Bits 64-71 are zeros. rfc6052.txt */
188 if (nbytes == 8) {
189 aaaa[nbytes++] = 0;
190 }
191 }
192 /* Copy suffix. */
193 memmove(aaaa + nbytes, dns64->bits + nbytes, 16 - nbytes);
194 return (ISC_R_SUCCESS);
195 }
196
197 dns_dns64_t *
198 dns_dns64_next(dns_dns64_t *dns64) {
199 dns64 = ISC_LIST_NEXT(dns64, link);
200 return (dns64);
201 }
202
203 void
204 dns_dns64_append(dns_dns64list_t *list, dns_dns64_t *dns64) {
205 ISC_LIST_APPEND(*list, dns64, link);
206 }
207
208 void
209 dns_dns64_unlink(dns_dns64list_t *list, dns_dns64_t *dns64) {
210 ISC_LIST_UNLINK(*list, dns64, link);
211 }
212
213 bool
214 dns_dns64_aaaaok(const dns_dns64_t *dns64, const isc_netaddr_t *reqaddr,
215 const dns_name_t *reqsigner, const dns_aclenv_t *env,
216 unsigned int flags, dns_rdataset_t *rdataset, bool *aaaaok,
217 size_t aaaaoklen) {
218 struct in6_addr in6;
219 isc_netaddr_t netaddr;
220 isc_result_t result;
221 int match;
222 bool answer = false;
223 bool found = false;
224 unsigned int i, ok;
225
226 REQUIRE(rdataset != NULL);
227 REQUIRE(rdataset->type == dns_rdatatype_aaaa);
228 REQUIRE(rdataset->rdclass == dns_rdataclass_in);
229 if (aaaaok != NULL) {
230 REQUIRE(aaaaoklen == dns_rdataset_count(rdataset));
231 }
232
233 for (; dns64 != NULL; dns64 = ISC_LIST_NEXT(dns64, link)) {
234 if ((dns64->flags & DNS_DNS64_RECURSIVE_ONLY) != 0 &&
235 (flags & DNS_DNS64_RECURSIVE) == 0)
236 {
237 continue;
238 }
239
240 if ((dns64->flags & DNS_DNS64_BREAK_DNSSEC) == 0 &&
241 (flags & DNS_DNS64_DNSSEC) != 0)
242 {
243 continue;
244 }
245 /*
246 * Work out if this dns64 structure applies to this client.
247 */
248 if (dns64->clients != NULL) {
249 result = dns_acl_match(reqaddr, reqsigner,
250 dns64->clients, env, &match,
251 NULL);
252 if (result != ISC_R_SUCCESS) {
253 continue;
254 }
255 if (match <= 0) {
256 continue;
257 }
258 }
259
260 if (!found && aaaaok != NULL) {
261 for (i = 0; i < aaaaoklen; i++) {
262 aaaaok[i] = false;
263 }
264 }
265 found = true;
266
267 /*
268 * If we are not excluding any addresses then any AAAA
269 * will do.
270 */
271 if (dns64->excluded == NULL) {
272 answer = true;
273 if (aaaaok == NULL) {
274 goto done;
275 }
276 for (i = 0; i < aaaaoklen; i++) {
277 aaaaok[i] = true;
278 }
279 goto done;
280 }
281
282 i = 0;
283 ok = 0;
284 for (result = dns_rdataset_first(rdataset);
285 result == ISC_R_SUCCESS;
286 result = dns_rdataset_next(rdataset))
287 {
288 dns_rdata_t rdata = DNS_RDATA_INIT;
289 if (aaaaok == NULL || !aaaaok[i]) {
290 dns_rdataset_current(rdataset, &rdata);
291 memmove(&in6.s6_addr, rdata.data, 16);
292 isc_netaddr_fromin6(&netaddr, &in6);
293
294 result = dns_acl_match(&netaddr, NULL,
295 dns64->excluded, env,
296 &match, NULL);
297 if (result == ISC_R_SUCCESS && match <= 0) {
298 answer = true;
299 if (aaaaok == NULL) {
300 goto done;
301 }
302 aaaaok[i] = true;
303 ok++;
304 }
305 } else {
306 ok++;
307 }
308 i++;
309 }
310 /*
311 * Are all addresses ok?
312 */
313 if (aaaaok != NULL && ok == aaaaoklen) {
314 goto done;
315 }
316 }
317
318 done:
319 if (!found && aaaaok != NULL) {
320 for (i = 0; i < aaaaoklen; i++) {
321 aaaaok[i] = true;
322 }
323 }
324 return (found ? answer : true);
325 }
326