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