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