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