1 /*
2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 *
8 * See the COPYRIGHT file distributed with this work for additional
9 * information regarding copyright ownership.
10 */
11
12 /*! \file */
13
14 #include <inttypes.h>
15 #include <stdbool.h>
16 #include <stdlib.h>
17
18 #include <isc/magic.h>
19 #include <isc/mem.h>
20 #include <isc/mutex.h>
21 #include <isc/net.h>
22 #include <isc/refcount.h>
23 #include <isc/result.h>
24 #include <isc/string.h>
25 #include <isc/types.h>
26 #include <isc/util.h>
27
28 #include <dns/portlist.h>
29 #include <dns/types.h>
30
31 #define DNS_PORTLIST_MAGIC ISC_MAGIC('P', 'L', 'S', 'T')
32 #define DNS_VALID_PORTLIST(p) ISC_MAGIC_VALID(p, DNS_PORTLIST_MAGIC)
33
34 typedef struct dns_element {
35 in_port_t port;
36 uint16_t flags;
37 } dns_element_t;
38
39 struct dns_portlist {
40 unsigned int magic;
41 isc_mem_t *mctx;
42 isc_refcount_t refcount;
43 isc_mutex_t lock;
44 dns_element_t *list;
45 unsigned int allocated;
46 unsigned int active;
47 };
48
49 #define DNS_PL_INET 0x0001
50 #define DNS_PL_INET6 0x0002
51 #define DNS_PL_ALLOCATE 16
52
53 static int
compare(const void * arg1,const void * arg2)54 compare(const void *arg1, const void *arg2) {
55 const dns_element_t *e1 = (const dns_element_t *)arg1;
56 const dns_element_t *e2 = (const dns_element_t *)arg2;
57
58 if (e1->port < e2->port) {
59 return (-1);
60 }
61 if (e1->port > e2->port) {
62 return (1);
63 }
64 return (0);
65 }
66
67 isc_result_t
dns_portlist_create(isc_mem_t * mctx,dns_portlist_t ** portlistp)68 dns_portlist_create(isc_mem_t *mctx, dns_portlist_t **portlistp) {
69 dns_portlist_t *portlist;
70
71 REQUIRE(portlistp != NULL && *portlistp == NULL);
72
73 portlist = isc_mem_get(mctx, sizeof(*portlist));
74 isc_mutex_init(&portlist->lock);
75 isc_refcount_init(&portlist->refcount, 1);
76 portlist->list = NULL;
77 portlist->allocated = 0;
78 portlist->active = 0;
79 portlist->mctx = NULL;
80 isc_mem_attach(mctx, &portlist->mctx);
81 portlist->magic = DNS_PORTLIST_MAGIC;
82 *portlistp = portlist;
83 return (ISC_R_SUCCESS);
84 }
85
86 static dns_element_t *
find_port(dns_element_t * list,unsigned int len,in_port_t port)87 find_port(dns_element_t *list, unsigned int len, in_port_t port) {
88 unsigned int xtry = len / 2;
89 unsigned int min = 0;
90 unsigned int max = len - 1;
91 unsigned int last = len;
92
93 for (;;) {
94 if (list[xtry].port == port) {
95 return (&list[xtry]);
96 }
97 if (port > list[xtry].port) {
98 if (xtry == max) {
99 break;
100 }
101 min = xtry;
102 xtry = xtry + (max - xtry + 1) / 2;
103 INSIST(xtry <= max);
104 if (xtry == last) {
105 break;
106 }
107 last = min;
108 } else {
109 if (xtry == min) {
110 break;
111 }
112 max = xtry;
113 xtry = xtry - (xtry - min + 1) / 2;
114 INSIST(xtry >= min);
115 if (xtry == last) {
116 break;
117 }
118 last = max;
119 }
120 }
121 return (NULL);
122 }
123
124 isc_result_t
dns_portlist_add(dns_portlist_t * portlist,int af,in_port_t port)125 dns_portlist_add(dns_portlist_t *portlist, int af, in_port_t port) {
126 dns_element_t *el;
127 isc_result_t result;
128
129 REQUIRE(DNS_VALID_PORTLIST(portlist));
130 REQUIRE(af == AF_INET || af == AF_INET6);
131
132 LOCK(&portlist->lock);
133 if (portlist->active != 0) {
134 el = find_port(portlist->list, portlist->active, port);
135 if (el != NULL) {
136 if (af == AF_INET) {
137 el->flags |= DNS_PL_INET;
138 } else {
139 el->flags |= DNS_PL_INET6;
140 }
141 result = ISC_R_SUCCESS;
142 goto unlock;
143 }
144 }
145
146 if (portlist->allocated <= portlist->active) {
147 unsigned int allocated;
148 allocated = portlist->allocated + DNS_PL_ALLOCATE;
149 el = isc_mem_get(portlist->mctx, sizeof(*el) * allocated);
150 if (portlist->list != NULL) {
151 memmove(el, portlist->list,
152 portlist->allocated * sizeof(*el));
153 isc_mem_put(portlist->mctx, portlist->list,
154 portlist->allocated * sizeof(*el));
155 }
156 portlist->list = el;
157 portlist->allocated = allocated;
158 }
159 portlist->list[portlist->active].port = port;
160 if (af == AF_INET) {
161 portlist->list[portlist->active].flags = DNS_PL_INET;
162 } else {
163 portlist->list[portlist->active].flags = DNS_PL_INET6;
164 }
165 portlist->active++;
166 qsort(portlist->list, portlist->active, sizeof(*el), compare);
167 result = ISC_R_SUCCESS;
168 unlock:
169 UNLOCK(&portlist->lock);
170 return (result);
171 }
172
173 void
dns_portlist_remove(dns_portlist_t * portlist,int af,in_port_t port)174 dns_portlist_remove(dns_portlist_t *portlist, int af, in_port_t port) {
175 dns_element_t *el;
176
177 REQUIRE(DNS_VALID_PORTLIST(portlist));
178 REQUIRE(af == AF_INET || af == AF_INET6);
179
180 LOCK(&portlist->lock);
181 if (portlist->active != 0) {
182 el = find_port(portlist->list, portlist->active, port);
183 if (el != NULL) {
184 if (af == AF_INET) {
185 el->flags &= ~DNS_PL_INET;
186 } else {
187 el->flags &= ~DNS_PL_INET6;
188 }
189 if (el->flags == 0) {
190 *el = portlist->list[portlist->active];
191 portlist->active--;
192 qsort(portlist->list, portlist->active,
193 sizeof(*el), compare);
194 }
195 }
196 }
197 UNLOCK(&portlist->lock);
198 }
199
200 bool
dns_portlist_match(dns_portlist_t * portlist,int af,in_port_t port)201 dns_portlist_match(dns_portlist_t *portlist, int af, in_port_t port) {
202 dns_element_t *el;
203 bool result = false;
204
205 REQUIRE(DNS_VALID_PORTLIST(portlist));
206 REQUIRE(af == AF_INET || af == AF_INET6);
207 LOCK(&portlist->lock);
208 if (portlist->active != 0) {
209 el = find_port(portlist->list, portlist->active, port);
210 if (el != NULL) {
211 if (af == AF_INET && (el->flags & DNS_PL_INET) != 0) {
212 result = true;
213 }
214 if (af == AF_INET6 && (el->flags & DNS_PL_INET6) != 0) {
215 result = true;
216 }
217 }
218 }
219 UNLOCK(&portlist->lock);
220 return (result);
221 }
222
223 void
dns_portlist_attach(dns_portlist_t * portlist,dns_portlist_t ** portlistp)224 dns_portlist_attach(dns_portlist_t *portlist, dns_portlist_t **portlistp) {
225 REQUIRE(DNS_VALID_PORTLIST(portlist));
226 REQUIRE(portlistp != NULL && *portlistp == NULL);
227
228 isc_refcount_increment(&portlist->refcount);
229 *portlistp = portlist;
230 }
231
232 void
dns_portlist_detach(dns_portlist_t ** portlistp)233 dns_portlist_detach(dns_portlist_t **portlistp) {
234 REQUIRE(portlistp != NULL && DNS_VALID_PORTLIST(*portlistp));
235 dns_portlist_t *portlist = *portlistp;
236 *portlistp = NULL;
237
238 if (isc_refcount_decrement(&portlist->refcount) == 1) {
239 portlist->magic = 0;
240 isc_refcount_destroy(&portlist->refcount);
241 if (portlist->list != NULL) {
242 isc_mem_put(portlist->mctx, portlist->list,
243 portlist->allocated *
244 sizeof(*portlist->list));
245 }
246 isc_mutex_destroy(&portlist->lock);
247 isc_mem_putanddetach(&portlist->mctx, portlist,
248 sizeof(*portlist));
249 }
250 }
251