1/*
2 * Copyright (C) 2007-2009  Internet Systems Consortium, Inc. ("ISC")
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
17/* $Id: iptable.c,v 1.15 2009/02/18 23:47:48 tbox Exp $ */
18
19#include <config.h>
20
21#include <isc/mem.h>
22#include <isc/radix.h>
23
24#include <dns/acl.h>
25
26static void destroy_iptable(dns_iptable_t *dtab);
27
28/*
29 * Create a new IP table and the underlying radix structure
30 */
31isc_result_t
32dns_iptable_create(isc_mem_t *mctx, dns_iptable_t **target) {
33	isc_result_t result;
34	dns_iptable_t *tab;
35
36	tab = isc_mem_get(mctx, sizeof(*tab));
37	if (tab == NULL)
38		return (ISC_R_NOMEMORY);
39	tab->mctx = mctx;
40	isc_refcount_init(&tab->refcount, 1);
41	tab->radix = NULL;
42	tab->magic = DNS_IPTABLE_MAGIC;
43
44	result = isc_radix_create(mctx, &tab->radix, RADIX_MAXBITS);
45	if (result != ISC_R_SUCCESS)
46		goto cleanup;
47
48	*target = tab;
49	return (ISC_R_SUCCESS);
50
51 cleanup:
52	dns_iptable_detach(&tab);
53	return (result);
54}
55
56isc_boolean_t dns_iptable_neg = ISC_FALSE;
57isc_boolean_t dns_iptable_pos = ISC_TRUE;
58
59/*
60 * Add an IP prefix to an existing IP table
61 */
62isc_result_t
63dns_iptable_addprefix(dns_iptable_t *tab, isc_netaddr_t *addr,
64		      isc_uint16_t bitlen, isc_boolean_t pos)
65{
66	isc_result_t result;
67	isc_prefix_t pfx;
68	isc_radix_node_t *node = NULL;
69	int family;
70
71	INSIST(DNS_IPTABLE_VALID(tab));
72	INSIST(tab->radix);
73
74	NETADDR_TO_PREFIX_T(addr, pfx, bitlen);
75
76	result = isc_radix_insert(tab->radix, &node, NULL, &pfx);
77	if (result != ISC_R_SUCCESS) {
78		isc_refcount_destroy(&pfx.refcount);
79		return(result);
80	}
81
82	/* If a node already contains data, don't overwrite it */
83	family = pfx.family;
84	if (family == AF_UNSPEC) {
85		/* "any" or "none" */
86		INSIST(pfx.bitlen == 0);
87		if (pos) {
88			if (node->data[0] == NULL)
89				node->data[0] = &dns_iptable_pos;
90			if (node->data[1] == NULL)
91				node->data[1] = &dns_iptable_pos;
92		} else {
93			if (node->data[0] == NULL)
94				node->data[0] = &dns_iptable_neg;
95			if (node->data[1] == NULL)
96				node->data[1] = &dns_iptable_neg;
97		}
98	} else {
99		/* any other prefix */
100		if (node->data[ISC_IS6(family)] == NULL) {
101			if (pos)
102				node->data[ISC_IS6(family)] = &dns_iptable_pos;
103			else
104				node->data[ISC_IS6(family)] = &dns_iptable_neg;
105		}
106	}
107
108	isc_refcount_destroy(&pfx.refcount);
109	return (ISC_R_SUCCESS);
110}
111
112/*
113 * Merge one IP table into another one.
114 */
115isc_result_t
116dns_iptable_merge(dns_iptable_t *tab, dns_iptable_t *source, isc_boolean_t pos)
117{
118	isc_result_t result;
119	isc_radix_node_t *node, *new_node;
120	int max_node = 0;
121
122	RADIX_WALK (source->radix->head, node) {
123		new_node = NULL;
124		result = isc_radix_insert (tab->radix, &new_node, node, NULL);
125
126		if (result != ISC_R_SUCCESS)
127			return(result);
128
129		/*
130		 * If we're negating a nested ACL, then we should
131		 * reverse the sense of every node.  However, this
132		 * could lead to a negative node in a nested ACL
133		 * becoming a positive match in the parent, which
134		 * could be a security risk.  To prevent this, we
135		 * just leave the negative nodes negative.
136		 */
137		if (!pos) {
138			if (node->data[0] &&
139			    *(isc_boolean_t *) node->data[0] == ISC_TRUE)
140				new_node->data[0] = &dns_iptable_neg;
141
142			if (node->data[1] &&
143			    *(isc_boolean_t *) node->data[1] == ISC_TRUE)
144				new_node->data[1] = &dns_iptable_neg;
145		}
146
147		if (node->node_num[0] > max_node)
148			max_node = node->node_num[0];
149		if (node->node_num[1] > max_node)
150			max_node = node->node_num[1];
151	} RADIX_WALK_END;
152
153	tab->radix->num_added_node += max_node;
154	return (ISC_R_SUCCESS);
155}
156
157void
158dns_iptable_attach(dns_iptable_t *source, dns_iptable_t **target) {
159	REQUIRE(DNS_IPTABLE_VALID(source));
160	isc_refcount_increment(&source->refcount, NULL);
161	*target = source;
162}
163
164void
165dns_iptable_detach(dns_iptable_t **tabp) {
166	dns_iptable_t *tab = *tabp;
167	unsigned int refs;
168	REQUIRE(DNS_IPTABLE_VALID(tab));
169	isc_refcount_decrement(&tab->refcount, &refs);
170	if (refs == 0)
171		destroy_iptable(tab);
172	*tabp = NULL;
173}
174
175static void
176destroy_iptable(dns_iptable_t *dtab) {
177
178	REQUIRE(DNS_IPTABLE_VALID(dtab));
179
180	if (dtab->radix != NULL) {
181		isc_radix_destroy(dtab->radix, NULL);
182		dtab->radix = NULL;
183	}
184
185	isc_refcount_destroy(&dtab->refcount);
186	dtab->magic = 0;
187	isc_mem_put(dtab->mctx, dtab, sizeof(*dtab));
188}
189