1135446Strhodes/*
2262706Serwin * Copyright (C) 2004-2007, 2014  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 2003  Internet Software Consortium.
4135446Strhodes *
5193149Sdougb * Permission to use, copy, modify, and/or distribute this software for any
6135446Strhodes * purpose with or without fee is hereby granted, provided that the above
7135446Strhodes * copyright notice and this permission notice appear in all copies.
8135446Strhodes *
9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11135446Strhodes * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15135446Strhodes * PERFORMANCE OF THIS SOFTWARE.
16135446Strhodes */
17135446Strhodes
18234010Sdougb/* $Id: portlist.c,v 1.13 2007/06/19 23:47:16 tbox Exp $ */
19135446Strhodes
20170222Sdougb/*! \file */
21170222Sdougb
22165071Sdougb#include <config.h>
23165071Sdougb
24135446Strhodes#include <stdlib.h>
25135446Strhodes
26135446Strhodes#include <isc/magic.h>
27135446Strhodes#include <isc/mem.h>
28135446Strhodes#include <isc/mutex.h>
29135446Strhodes#include <isc/net.h>
30135446Strhodes#include <isc/refcount.h>
31135446Strhodes#include <isc/result.h>
32135446Strhodes#include <isc/string.h>
33135446Strhodes#include <isc/types.h>
34135446Strhodes#include <isc/util.h>
35135446Strhodes
36135446Strhodes#include <dns/types.h>
37135446Strhodes#include <dns/portlist.h>
38135446Strhodes
39135446Strhodes#define DNS_PORTLIST_MAGIC	ISC_MAGIC('P','L','S','T')
40135446Strhodes#define DNS_VALID_PORTLIST(p)	ISC_MAGIC_VALID(p, DNS_PORTLIST_MAGIC)
41135446Strhodes
42135446Strhodestypedef struct dns_element {
43135446Strhodes	in_port_t	port;
44135446Strhodes	isc_uint16_t	flags;
45135446Strhodes} dns_element_t;
46135446Strhodes
47135446Strhodesstruct dns_portlist {
48135446Strhodes	unsigned int	magic;
49135446Strhodes	isc_mem_t	*mctx;
50135446Strhodes	isc_refcount_t	refcount;
51135446Strhodes	isc_mutex_t	lock;
52135446Strhodes	dns_element_t 	*list;
53135446Strhodes	unsigned int	allocated;
54135446Strhodes	unsigned int	active;
55135446Strhodes};
56135446Strhodes
57135446Strhodes#define DNS_PL_INET	0x0001
58135446Strhodes#define DNS_PL_INET6	0x0002
59135446Strhodes#define DNS_PL_ALLOCATE	16
60135446Strhodes
61135446Strhodesstatic int
62135446Strhodescompare(const void *arg1, const void *arg2) {
63135446Strhodes	const dns_element_t *e1 = (const dns_element_t *)arg1;
64135446Strhodes	const dns_element_t *e2 = (const dns_element_t *)arg2;
65135446Strhodes
66135446Strhodes	if (e1->port < e2->port)
67135446Strhodes		return (-1);
68135446Strhodes	if (e1->port > e2->port)
69135446Strhodes		return (1);
70135446Strhodes	return (0);
71135446Strhodes}
72135446Strhodes
73135446Strhodesisc_result_t
74135446Strhodesdns_portlist_create(isc_mem_t *mctx, dns_portlist_t **portlistp) {
75135446Strhodes	dns_portlist_t *portlist;
76135446Strhodes	isc_result_t result;
77135446Strhodes
78135446Strhodes	REQUIRE(portlistp != NULL && *portlistp == NULL);
79135446Strhodes
80135446Strhodes	portlist = isc_mem_get(mctx, sizeof(*portlist));
81135446Strhodes	if (portlist == NULL)
82135446Strhodes		return (ISC_R_NOMEMORY);
83262706Serwin	result = isc_mutex_init(&portlist->lock);
84135446Strhodes	if (result != ISC_R_SUCCESS) {
85135446Strhodes		isc_mem_put(mctx, portlist, sizeof(*portlist));
86170222Sdougb		return (result);
87135446Strhodes	}
88170222Sdougb	result = isc_refcount_init(&portlist->refcount, 1);
89170222Sdougb	if (result != ISC_R_SUCCESS) {
90170222Sdougb		DESTROYLOCK(&portlist->lock);
91170222Sdougb		isc_mem_put(mctx, portlist, sizeof(*portlist));
92170222Sdougb		return (result);
93170222Sdougb	}
94135446Strhodes	portlist->list = NULL;
95135446Strhodes	portlist->allocated = 0;
96135446Strhodes	portlist->active = 0;
97135446Strhodes	portlist->mctx = NULL;
98135446Strhodes	isc_mem_attach(mctx, &portlist->mctx);
99135446Strhodes	portlist->magic = DNS_PORTLIST_MAGIC;
100135446Strhodes	*portlistp = portlist;
101135446Strhodes	return (ISC_R_SUCCESS);
102135446Strhodes}
103135446Strhodes
104135446Strhodesstatic dns_element_t *
105135446Strhodesfind_port(dns_element_t *list, unsigned int len, in_port_t port) {
106135446Strhodes	unsigned int xtry = len / 2;
107135446Strhodes	unsigned int min = 0;
108135446Strhodes	unsigned int max = len - 1;
109135446Strhodes	unsigned int last = len;
110135446Strhodes
111135446Strhodes	for (;;) {
112135446Strhodes		if (list[xtry].port == port)
113135446Strhodes			return (&list[xtry]);
114262706Serwin		if (port > list[xtry].port) {
115135446Strhodes			if (xtry == max)
116135446Strhodes				break;
117135446Strhodes			min = xtry;
118135446Strhodes			xtry = xtry + (max - xtry + 1) / 2;
119135446Strhodes			INSIST(xtry <= max);
120135446Strhodes			if (xtry == last)
121135446Strhodes				break;
122135446Strhodes			last = min;
123135446Strhodes		} else {
124135446Strhodes			if (xtry == min)
125135446Strhodes				break;
126135446Strhodes			max = xtry;
127135446Strhodes			xtry = xtry - (xtry - min + 1) / 2;
128135446Strhodes			INSIST(xtry >= min);
129135446Strhodes			if (xtry == last)
130135446Strhodes				break;
131135446Strhodes			last = max;
132135446Strhodes		}
133135446Strhodes	}
134135446Strhodes	return (NULL);
135135446Strhodes}
136135446Strhodes
137135446Strhodesisc_result_t
138135446Strhodesdns_portlist_add(dns_portlist_t *portlist, int af, in_port_t port) {
139135446Strhodes	dns_element_t *el;
140135446Strhodes	isc_result_t result;
141135446Strhodes
142135446Strhodes	REQUIRE(DNS_VALID_PORTLIST(portlist));
143135446Strhodes	REQUIRE(af == AF_INET || af == AF_INET6);
144135446Strhodes
145135446Strhodes	LOCK(&portlist->lock);
146135446Strhodes	if (portlist->active != 0) {
147135446Strhodes		el = find_port(portlist->list, portlist->active, port);
148135446Strhodes		if (el != NULL) {
149135446Strhodes			if (af == AF_INET)
150135446Strhodes				el->flags |= DNS_PL_INET;
151135446Strhodes			else
152135446Strhodes				el->flags |= DNS_PL_INET6;
153135446Strhodes			result = ISC_R_SUCCESS;
154135446Strhodes			goto unlock;
155135446Strhodes		}
156135446Strhodes	}
157135446Strhodes
158135446Strhodes	if (portlist->allocated <= portlist->active) {
159135446Strhodes		unsigned int allocated;
160135446Strhodes		allocated = portlist->allocated + DNS_PL_ALLOCATE;
161135446Strhodes		el = isc_mem_get(portlist->mctx, sizeof(*el) * allocated);
162135446Strhodes		if (el == NULL) {
163135446Strhodes			result = ISC_R_NOMEMORY;
164135446Strhodes			goto unlock;
165135446Strhodes		}
166135446Strhodes		if (portlist->list != NULL) {
167262706Serwin			memmove(el, portlist->list,
168262706Serwin				portlist->allocated * sizeof(*el));
169135446Strhodes			isc_mem_put(portlist->mctx, portlist->list,
170135446Strhodes				    portlist->allocated * sizeof(*el));
171135446Strhodes		}
172135446Strhodes		portlist->list = el;
173135446Strhodes		portlist->allocated = allocated;
174135446Strhodes	}
175135446Strhodes	portlist->list[portlist->active].port = port;
176135446Strhodes	if (af == AF_INET)
177135446Strhodes		portlist->list[portlist->active].flags = DNS_PL_INET;
178135446Strhodes	else
179135446Strhodes		portlist->list[portlist->active].flags = DNS_PL_INET6;
180135446Strhodes	portlist->active++;
181135446Strhodes	qsort(portlist->list, portlist->active, sizeof(*el), compare);
182135446Strhodes	result = ISC_R_SUCCESS;
183135446Strhodes unlock:
184135446Strhodes	UNLOCK(&portlist->lock);
185135446Strhodes	return (result);
186135446Strhodes}
187135446Strhodes
188135446Strhodesvoid
189135446Strhodesdns_portlist_remove(dns_portlist_t *portlist, int af, in_port_t port) {
190135446Strhodes	dns_element_t *el;
191135446Strhodes
192135446Strhodes	REQUIRE(DNS_VALID_PORTLIST(portlist));
193135446Strhodes	REQUIRE(af == AF_INET || af == AF_INET6);
194135446Strhodes
195135446Strhodes	LOCK(&portlist->lock);
196135446Strhodes	if (portlist->active != 0) {
197135446Strhodes		el = find_port(portlist->list, portlist->active, port);
198135446Strhodes		if (el != NULL) {
199135446Strhodes			if (af == AF_INET)
200135446Strhodes				el->flags &= ~DNS_PL_INET;
201135446Strhodes			else
202135446Strhodes				el->flags &= ~DNS_PL_INET6;
203135446Strhodes			if (el->flags == 0) {
204135446Strhodes				*el = portlist->list[portlist->active];
205135446Strhodes				portlist->active--;
206135446Strhodes				qsort(portlist->list, portlist->active,
207135446Strhodes				      sizeof(*el), compare);
208135446Strhodes			}
209135446Strhodes		}
210135446Strhodes	}
211135446Strhodes	UNLOCK(&portlist->lock);
212135446Strhodes}
213135446Strhodes
214135446Strhodesisc_boolean_t
215135446Strhodesdns_portlist_match(dns_portlist_t *portlist, int af, in_port_t port) {
216135446Strhodes	dns_element_t *el;
217135446Strhodes	isc_boolean_t result = ISC_FALSE;
218262706Serwin
219135446Strhodes	REQUIRE(DNS_VALID_PORTLIST(portlist));
220135446Strhodes	REQUIRE(af == AF_INET || af == AF_INET6);
221135446Strhodes	LOCK(&portlist->lock);
222135446Strhodes	if (portlist->active != 0) {
223135446Strhodes		el = find_port(portlist->list, portlist->active, port);
224135446Strhodes		if (el != NULL) {
225135446Strhodes			if (af == AF_INET && (el->flags & DNS_PL_INET) != 0)
226135446Strhodes				result = ISC_TRUE;
227135446Strhodes			if (af == AF_INET6 && (el->flags & DNS_PL_INET6) != 0)
228135446Strhodes				result = ISC_TRUE;
229135446Strhodes		}
230262706Serwin	}
231135446Strhodes	UNLOCK(&portlist->lock);
232135446Strhodes	return (result);
233135446Strhodes}
234135446Strhodes
235135446Strhodesvoid
236135446Strhodesdns_portlist_attach(dns_portlist_t *portlist, dns_portlist_t **portlistp) {
237135446Strhodes
238135446Strhodes	REQUIRE(DNS_VALID_PORTLIST(portlist));
239135446Strhodes	REQUIRE(portlistp != NULL && *portlistp == NULL);
240135446Strhodes
241135446Strhodes	isc_refcount_increment(&portlist->refcount, NULL);
242135446Strhodes	*portlistp = portlist;
243135446Strhodes}
244135446Strhodes
245135446Strhodesvoid
246135446Strhodesdns_portlist_detach(dns_portlist_t **portlistp) {
247135446Strhodes	dns_portlist_t *portlist;
248135446Strhodes	unsigned int count;
249135446Strhodes
250135446Strhodes	REQUIRE(portlistp != NULL);
251135446Strhodes	portlist = *portlistp;
252135446Strhodes	REQUIRE(DNS_VALID_PORTLIST(portlist));
253135446Strhodes	*portlistp = NULL;
254135446Strhodes	isc_refcount_decrement(&portlist->refcount, &count);
255135446Strhodes	if (count == 0) {
256135446Strhodes		portlist->magic = 0;
257135446Strhodes		isc_refcount_destroy(&portlist->refcount);
258135446Strhodes		if (portlist->list != NULL)
259135446Strhodes			isc_mem_put(portlist->mctx, portlist->list,
260135446Strhodes				    portlist->allocated *
261135446Strhodes				    sizeof(*portlist->list));
262135446Strhodes		DESTROYLOCK(&portlist->lock);
263135446Strhodes		isc_mem_putanddetach(&portlist->mctx, portlist,
264135446Strhodes				     sizeof(*portlist));
265135446Strhodes	}
266135446Strhodes}
267