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