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