Merge from vendor branch LESS:
[dragonfly.git] / contrib / bind-9.3 / lib / dns / portlist.c
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
40 typedef struct dns_element {
41         in_port_t       port;
42         isc_uint16_t    flags;
43 } dns_element_t;
44
45 struct 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
59 static int
60 compare(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
71 isc_result_t
72 dns_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
100 static dns_element_t *
101 find_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
133 isc_result_t
134 dns_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
184 void
185 dns_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
210 isc_boolean_t
211 dns_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
231 void
232 dns_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
241 void
242 dns_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 }