Merge from vendor branch GCC:
[dragonfly.git] / contrib / bind-9.3 / lib / dns / portlist.c
1 /*
2  * Copyright (C) 2004  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.4 2004/03/16 05:50:21 marka Exp $ */
19
20 #include <stdlib.h>
21
22 #include <isc/magic.h>
23 #include <isc/mem.h>
24 #include <isc/mutex.h>
25 #include <isc/net.h>
26 #include <isc/refcount.h>
27 #include <isc/result.h>
28 #include <isc/string.h>
29 #include <isc/types.h>
30 #include <isc/util.h>
31
32 #include <dns/types.h>
33 #include <dns/portlist.h>
34
35 #define DNS_PORTLIST_MAGIC      ISC_MAGIC('P','L','S','T')
36 #define DNS_VALID_PORTLIST(p)   ISC_MAGIC_VALID(p, DNS_PORTLIST_MAGIC)
37
38 typedef struct dns_element {
39         in_port_t       port;
40         isc_uint16_t    flags;
41 } dns_element_t;
42
43 struct dns_portlist {
44         unsigned int    magic;
45         isc_mem_t       *mctx;
46         isc_refcount_t  refcount;
47         isc_mutex_t     lock;
48         dns_element_t   *list;
49         unsigned int    allocated;
50         unsigned int    active;
51 };
52
53 #define DNS_PL_INET     0x0001
54 #define DNS_PL_INET6    0x0002
55 #define DNS_PL_ALLOCATE 16
56
57 static int
58 compare(const void *arg1, const void *arg2) {
59         const dns_element_t *e1 = (const dns_element_t *)arg1;
60         const dns_element_t *e2 = (const dns_element_t *)arg2;
61
62         if (e1->port < e2->port)
63                 return (-1);
64         if (e1->port > e2->port)
65                 return (1);
66         return (0);
67 }
68
69 isc_result_t
70 dns_portlist_create(isc_mem_t *mctx, dns_portlist_t **portlistp) {
71         dns_portlist_t *portlist;
72         isc_result_t result;
73
74         REQUIRE(portlistp != NULL && *portlistp == NULL);
75
76         portlist = isc_mem_get(mctx, sizeof(*portlist));
77         if (portlist == NULL)
78                 return (ISC_R_NOMEMORY);
79         result = isc_mutex_init(&portlist->lock);
80         if (result != ISC_R_SUCCESS) {
81                 isc_mem_put(mctx, portlist, sizeof(*portlist));
82                 UNEXPECTED_ERROR(__FILE__, __LINE__,
83                                  "isc_mutex_init() failed: %s",
84                                  isc_result_totext(result));
85                 return (ISC_R_UNEXPECTED);
86         }
87         isc_refcount_init(&portlist->refcount, 1);
88         portlist->list = NULL;
89         portlist->allocated = 0;
90         portlist->active = 0;
91         portlist->mctx = NULL;
92         isc_mem_attach(mctx, &portlist->mctx);
93         portlist->magic = DNS_PORTLIST_MAGIC;
94         *portlistp = portlist;
95         return (ISC_R_SUCCESS);
96 }
97
98 static dns_element_t *
99 find_port(dns_element_t *list, unsigned int len, in_port_t port) {
100         unsigned int xtry = len / 2;
101         unsigned int min = 0;
102         unsigned int max = len - 1;
103         unsigned int last = len;
104
105         for (;;) {
106                 if (list[xtry].port == port)
107                         return (&list[xtry]);
108                 if (port > list[xtry].port) {
109                         if (xtry == max)
110                                 break;
111                         min = xtry;
112                         xtry = xtry + (max - xtry + 1) / 2;
113                         INSIST(xtry <= max);
114                         if (xtry == last)
115                                 break;
116                         last = min;
117                 } else {
118                         if (xtry == min)
119                                 break;
120                         max = xtry;
121                         xtry = xtry - (xtry - min + 1) / 2;
122                         INSIST(xtry >= min);
123                         if (xtry == last)
124                                 break;
125                         last = max;
126                 }
127         }
128         return (NULL);
129 }
130
131 isc_result_t
132 dns_portlist_add(dns_portlist_t *portlist, int af, in_port_t port) {
133         dns_element_t *el;
134         isc_result_t result;
135
136         REQUIRE(DNS_VALID_PORTLIST(portlist));
137         REQUIRE(af == AF_INET || af == AF_INET6);
138
139         LOCK(&portlist->lock);
140         if (portlist->active != 0) {
141                 el = find_port(portlist->list, portlist->active, port);
142                 if (el != NULL) {
143                         if (af == AF_INET)
144                                 el->flags |= DNS_PL_INET;
145                         else
146                                 el->flags |= DNS_PL_INET6;
147                         result = ISC_R_SUCCESS;
148                         goto unlock;
149                 }
150         }
151
152         if (portlist->allocated <= portlist->active) {
153                 unsigned int allocated;
154                 allocated = portlist->allocated + DNS_PL_ALLOCATE;
155                 el = isc_mem_get(portlist->mctx, sizeof(*el) * allocated);
156                 if (el == NULL) {
157                         result = ISC_R_NOMEMORY;
158                         goto unlock;
159                 }
160                 if (portlist->list != NULL) {
161                         memcpy(el, portlist->list,
162                                portlist->allocated * sizeof(*el)); 
163                         isc_mem_put(portlist->mctx, portlist->list,
164                                     portlist->allocated * sizeof(*el));
165                 }
166                 portlist->list = el;
167                 portlist->allocated = allocated;
168         }
169         portlist->list[portlist->active].port = port;
170         if (af == AF_INET)
171                 portlist->list[portlist->active].flags = DNS_PL_INET;
172         else
173                 portlist->list[portlist->active].flags = DNS_PL_INET6;
174         portlist->active++;
175         qsort(portlist->list, portlist->active, sizeof(*el), compare);
176         result = ISC_R_SUCCESS;
177  unlock:
178         UNLOCK(&portlist->lock);
179         return (result);
180 }
181
182 void
183 dns_portlist_remove(dns_portlist_t *portlist, int af, in_port_t port) {
184         dns_element_t *el;
185
186         REQUIRE(DNS_VALID_PORTLIST(portlist));
187         REQUIRE(af == AF_INET || af == AF_INET6);
188
189         LOCK(&portlist->lock);
190         if (portlist->active != 0) {
191                 el = find_port(portlist->list, portlist->active, port);
192                 if (el != NULL) {
193                         if (af == AF_INET)
194                                 el->flags &= ~DNS_PL_INET;
195                         else
196                                 el->flags &= ~DNS_PL_INET6;
197                         if (el->flags == 0) {
198                                 *el = portlist->list[portlist->active];
199                                 portlist->active--;
200                                 qsort(portlist->list, portlist->active,
201                                       sizeof(*el), compare);
202                         }
203                 }
204         }
205         UNLOCK(&portlist->lock);
206 }
207
208 isc_boolean_t
209 dns_portlist_match(dns_portlist_t *portlist, int af, in_port_t port) {
210         dns_element_t *el;
211         isc_boolean_t result = ISC_FALSE;
212         
213         REQUIRE(DNS_VALID_PORTLIST(portlist));
214         REQUIRE(af == AF_INET || af == AF_INET6);
215         LOCK(&portlist->lock);
216         if (portlist->active != 0) {
217                 el = find_port(portlist->list, portlist->active, port);
218                 if (el != NULL) {
219                         if (af == AF_INET && (el->flags & DNS_PL_INET) != 0)
220                                 result = ISC_TRUE;
221                         if (af == AF_INET6 && (el->flags & DNS_PL_INET6) != 0)
222                                 result = ISC_TRUE;
223                 }
224         }       
225         UNLOCK(&portlist->lock);
226         return (result);
227 }
228
229 void
230 dns_portlist_attach(dns_portlist_t *portlist, dns_portlist_t **portlistp) {
231
232         REQUIRE(DNS_VALID_PORTLIST(portlist));
233         REQUIRE(portlistp != NULL && *portlistp == NULL);
234
235         isc_refcount_increment(&portlist->refcount, NULL);
236         *portlistp = portlist;
237 }
238
239 void
240 dns_portlist_detach(dns_portlist_t **portlistp) {
241         dns_portlist_t *portlist;
242         unsigned int count;
243
244         REQUIRE(portlistp != NULL);
245         portlist = *portlistp;
246         REQUIRE(DNS_VALID_PORTLIST(portlist));
247         *portlistp = NULL;
248         isc_refcount_decrement(&portlist->refcount, &count);
249         if (count == 0) {
250                 portlist->magic = 0;
251                 isc_refcount_destroy(&portlist->refcount);
252                 if (portlist->list != NULL)
253                         isc_mem_put(portlist->mctx, portlist->list,
254                                     portlist->allocated *
255                                     sizeof(*portlist->list));
256                 DESTROYLOCK(&portlist->lock);
257                 isc_mem_putanddetach(&portlist->mctx, portlist,
258                                      sizeof(*portlist));
259         }
260 }