xref: /minix/external/bsd/bind/dist/lib/dns/portlist.c (revision bb9622b5)
1 /*	$NetBSD: portlist.c,v 1.5 2014/12/10 04:37:58 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2004-2007, 2014  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 
44 typedef struct dns_element {
45 	in_port_t	port;
46 	isc_uint16_t	flags;
47 } dns_element_t;
48 
49 struct 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 
63 static int
64 compare(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 
75 isc_result_t
76 dns_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 
106 static dns_element_t *
107 find_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 
139 isc_result_t
140 dns_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 			memmove(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 
190 void
191 dns_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 
216 isc_boolean_t
217 dns_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 
237 void
238 dns_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 
247 void
248 dns_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