xref: /netbsd-src/external/mpl/dhcp/bind/dist/lib/dns/portlist.c (revision 4afad4b7fa6d4a0d3dedf41d1587a7250710ae54)
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