portlist.c revision 165071
1/*
2 * Copyright (C) 2004, 2006  Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2003  Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/* $Id: portlist.c,v 1.3.72.6 2006/08/25 05:25:50 marka Exp $ */
19
20#include <config.h>
21
22#include <stdlib.h>
23
24#include <isc/magic.h>
25#include <isc/mem.h>
26#include <isc/mutex.h>
27#include <isc/net.h>
28#include <isc/refcount.h>
29#include <isc/result.h>
30#include <isc/string.h>
31#include <isc/types.h>
32#include <isc/util.h>
33
34#include <dns/types.h>
35#include <dns/portlist.h>
36
37#define DNS_PORTLIST_MAGIC	ISC_MAGIC('P','L','S','T')
38#define DNS_VALID_PORTLIST(p)	ISC_MAGIC_VALID(p, DNS_PORTLIST_MAGIC)
39
40typedef struct dns_element {
41	in_port_t	port;
42	isc_uint16_t	flags;
43} dns_element_t;
44
45struct dns_portlist {
46	unsigned int	magic;
47	isc_mem_t	*mctx;
48	isc_refcount_t	refcount;
49	isc_mutex_t	lock;
50	dns_element_t 	*list;
51	unsigned int	allocated;
52	unsigned int	active;
53};
54
55#define DNS_PL_INET	0x0001
56#define DNS_PL_INET6	0x0002
57#define DNS_PL_ALLOCATE	16
58
59static int
60compare(const void *arg1, const void *arg2) {
61	const dns_element_t *e1 = (const dns_element_t *)arg1;
62	const dns_element_t *e2 = (const dns_element_t *)arg2;
63
64	if (e1->port < e2->port)
65		return (-1);
66	if (e1->port > e2->port)
67		return (1);
68	return (0);
69}
70
71isc_result_t
72dns_portlist_create(isc_mem_t *mctx, dns_portlist_t **portlistp) {
73	dns_portlist_t *portlist;
74	isc_result_t result;
75
76	REQUIRE(portlistp != NULL && *portlistp == NULL);
77
78	portlist = isc_mem_get(mctx, sizeof(*portlist));
79	if (portlist == NULL)
80		return (ISC_R_NOMEMORY);
81        result = isc_mutex_init(&portlist->lock);
82	if (result != ISC_R_SUCCESS) {
83		isc_mem_put(mctx, portlist, sizeof(*portlist));
84		UNEXPECTED_ERROR(__FILE__, __LINE__,
85				 "isc_mutex_init() failed: %s",
86				 isc_result_totext(result));
87		return (ISC_R_UNEXPECTED);
88	}
89	isc_refcount_init(&portlist->refcount, 1);
90	portlist->list = NULL;
91	portlist->allocated = 0;
92	portlist->active = 0;
93	portlist->mctx = NULL;
94	isc_mem_attach(mctx, &portlist->mctx);
95	portlist->magic = DNS_PORTLIST_MAGIC;
96	*portlistp = portlist;
97	return (ISC_R_SUCCESS);
98}
99
100static dns_element_t *
101find_port(dns_element_t *list, unsigned int len, in_port_t port) {
102	unsigned int xtry = len / 2;
103	unsigned int min = 0;
104	unsigned int max = len - 1;
105	unsigned int last = len;
106
107	for (;;) {
108		if (list[xtry].port == port)
109			return (&list[xtry]);
110	        if (port > list[xtry].port) {
111			if (xtry == max)
112				break;
113			min = xtry;
114			xtry = xtry + (max - xtry + 1) / 2;
115			INSIST(xtry <= max);
116			if (xtry == last)
117				break;
118			last = min;
119		} else {
120			if (xtry == min)
121				break;
122			max = xtry;
123			xtry = xtry - (xtry - min + 1) / 2;
124			INSIST(xtry >= min);
125			if (xtry == last)
126				break;
127			last = max;
128		}
129	}
130	return (NULL);
131}
132
133isc_result_t
134dns_portlist_add(dns_portlist_t *portlist, int af, in_port_t port) {
135	dns_element_t *el;
136	isc_result_t result;
137
138	REQUIRE(DNS_VALID_PORTLIST(portlist));
139	REQUIRE(af == AF_INET || af == AF_INET6);
140
141	LOCK(&portlist->lock);
142	if (portlist->active != 0) {
143		el = find_port(portlist->list, portlist->active, port);
144		if (el != NULL) {
145			if (af == AF_INET)
146				el->flags |= DNS_PL_INET;
147			else
148				el->flags |= DNS_PL_INET6;
149			result = ISC_R_SUCCESS;
150			goto unlock;
151		}
152	}
153
154	if (portlist->allocated <= portlist->active) {
155		unsigned int allocated;
156		allocated = portlist->allocated + DNS_PL_ALLOCATE;
157		el = isc_mem_get(portlist->mctx, sizeof(*el) * allocated);
158		if (el == NULL) {
159			result = ISC_R_NOMEMORY;
160			goto unlock;
161		}
162		if (portlist->list != NULL) {
163			memcpy(el, portlist->list,
164			       portlist->allocated * sizeof(*el));
165			isc_mem_put(portlist->mctx, portlist->list,
166				    portlist->allocated * sizeof(*el));
167		}
168		portlist->list = el;
169		portlist->allocated = allocated;
170	}
171	portlist->list[portlist->active].port = port;
172	if (af == AF_INET)
173		portlist->list[portlist->active].flags = DNS_PL_INET;
174	else
175		portlist->list[portlist->active].flags = DNS_PL_INET6;
176	portlist->active++;
177	qsort(portlist->list, portlist->active, sizeof(*el), compare);
178	result = ISC_R_SUCCESS;
179 unlock:
180	UNLOCK(&portlist->lock);
181	return (result);
182}
183
184void
185dns_portlist_remove(dns_portlist_t *portlist, int af, in_port_t port) {
186	dns_element_t *el;
187
188	REQUIRE(DNS_VALID_PORTLIST(portlist));
189	REQUIRE(af == AF_INET || af == AF_INET6);
190
191	LOCK(&portlist->lock);
192	if (portlist->active != 0) {
193		el = find_port(portlist->list, portlist->active, port);
194		if (el != NULL) {
195			if (af == AF_INET)
196				el->flags &= ~DNS_PL_INET;
197			else
198				el->flags &= ~DNS_PL_INET6;
199			if (el->flags == 0) {
200				*el = portlist->list[portlist->active];
201				portlist->active--;
202				qsort(portlist->list, portlist->active,
203				      sizeof(*el), compare);
204			}
205		}
206	}
207	UNLOCK(&portlist->lock);
208}
209
210isc_boolean_t
211dns_portlist_match(dns_portlist_t *portlist, int af, in_port_t port) {
212	dns_element_t *el;
213	isc_boolean_t result = ISC_FALSE;
214
215	REQUIRE(DNS_VALID_PORTLIST(portlist));
216	REQUIRE(af == AF_INET || af == AF_INET6);
217	LOCK(&portlist->lock);
218	if (portlist->active != 0) {
219		el = find_port(portlist->list, portlist->active, port);
220		if (el != NULL) {
221			if (af == AF_INET && (el->flags & DNS_PL_INET) != 0)
222				result = ISC_TRUE;
223			if (af == AF_INET6 && (el->flags & DNS_PL_INET6) != 0)
224				result = ISC_TRUE;
225		}
226	}
227	UNLOCK(&portlist->lock);
228	return (result);
229}
230
231void
232dns_portlist_attach(dns_portlist_t *portlist, dns_portlist_t **portlistp) {
233
234	REQUIRE(DNS_VALID_PORTLIST(portlist));
235	REQUIRE(portlistp != NULL && *portlistp == NULL);
236
237	isc_refcount_increment(&portlist->refcount, NULL);
238	*portlistp = portlist;
239}
240
241void
242dns_portlist_detach(dns_portlist_t **portlistp) {
243	dns_portlist_t *portlist;
244	unsigned int count;
245
246	REQUIRE(portlistp != NULL);
247	portlist = *portlistp;
248	REQUIRE(DNS_VALID_PORTLIST(portlist));
249	*portlistp = NULL;
250	isc_refcount_decrement(&portlist->refcount, &count);
251	if (count == 0) {
252		portlist->magic = 0;
253		isc_refcount_destroy(&portlist->refcount);
254		if (portlist->list != NULL)
255			isc_mem_put(portlist->mctx, portlist->list,
256				    portlist->allocated *
257				    sizeof(*portlist->list));
258		DESTROYLOCK(&portlist->lock);
259		isc_mem_putanddetach(&portlist->mctx, portlist,
260				     sizeof(*portlist));
261	}
262}
263