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