1/* 2 * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 2003 Internet Software Consortium. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 * PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18/* $Id: portlist.c,v 1.13 2007/06/19 23:47:16 tbox Exp $ */ 19 20/*! \file */ 21 22#include <config.h> 23 24#include <stdlib.h> 25 26#include <isc/magic.h> 27#include <isc/mem.h> 28#include <isc/mutex.h> 29#include <isc/net.h> 30#include <isc/refcount.h> 31#include <isc/result.h> 32#include <isc/string.h> 33#include <isc/types.h> 34#include <isc/util.h> 35 36#include <dns/types.h> 37#include <dns/portlist.h> 38 39#define DNS_PORTLIST_MAGIC ISC_MAGIC('P','L','S','T') 40#define DNS_VALID_PORTLIST(p) ISC_MAGIC_VALID(p, DNS_PORTLIST_MAGIC) 41 42typedef struct dns_element { 43 in_port_t port; 44 isc_uint16_t flags; 45} dns_element_t; 46 47struct dns_portlist { 48 unsigned int magic; 49 isc_mem_t *mctx; 50 isc_refcount_t refcount; 51 isc_mutex_t lock; 52 dns_element_t *list; 53 unsigned int allocated; 54 unsigned int active; 55}; 56 57#define DNS_PL_INET 0x0001 58#define DNS_PL_INET6 0x0002 59#define DNS_PL_ALLOCATE 16 60 61static int 62compare(const void *arg1, const void *arg2) { 63 const dns_element_t *e1 = (const dns_element_t *)arg1; 64 const dns_element_t *e2 = (const dns_element_t *)arg2; 65 66 if (e1->port < e2->port) 67 return (-1); 68 if (e1->port > e2->port) 69 return (1); 70 return (0); 71} 72 73isc_result_t 74dns_portlist_create(isc_mem_t *mctx, dns_portlist_t **portlistp) { 75 dns_portlist_t *portlist; 76 isc_result_t result; 77 78 REQUIRE(portlistp != NULL && *portlistp == NULL); 79 80 portlist = isc_mem_get(mctx, sizeof(*portlist)); 81 if (portlist == NULL) 82 return (ISC_R_NOMEMORY); 83 result = isc_mutex_init(&portlist->lock); 84 if (result != ISC_R_SUCCESS) { 85 isc_mem_put(mctx, portlist, sizeof(*portlist)); 86 return (result); 87 } 88 result = isc_refcount_init(&portlist->refcount, 1); 89 if (result != ISC_R_SUCCESS) { 90 DESTROYLOCK(&portlist->lock); 91 isc_mem_put(mctx, portlist, sizeof(*portlist)); 92 return (result); 93 } 94 portlist->list = NULL; 95 portlist->allocated = 0; 96 portlist->active = 0; 97 portlist->mctx = NULL; 98 isc_mem_attach(mctx, &portlist->mctx); 99 portlist->magic = DNS_PORTLIST_MAGIC; 100 *portlistp = portlist; 101 return (ISC_R_SUCCESS); 102} 103 104static dns_element_t * 105find_port(dns_element_t *list, unsigned int len, in_port_t port) { 106 unsigned int xtry = len / 2; 107 unsigned int min = 0; 108 unsigned int max = len - 1; 109 unsigned int last = len; 110 111 for (;;) { 112 if (list[xtry].port == port) 113 return (&list[xtry]); 114 if (port > list[xtry].port) { 115 if (xtry == max) 116 break; 117 min = xtry; 118 xtry = xtry + (max - xtry + 1) / 2; 119 INSIST(xtry <= max); 120 if (xtry == last) 121 break; 122 last = min; 123 } else { 124 if (xtry == min) 125 break; 126 max = xtry; 127 xtry = xtry - (xtry - min + 1) / 2; 128 INSIST(xtry >= min); 129 if (xtry == last) 130 break; 131 last = max; 132 } 133 } 134 return (NULL); 135} 136 137isc_result_t 138dns_portlist_add(dns_portlist_t *portlist, int af, in_port_t port) { 139 dns_element_t *el; 140 isc_result_t result; 141 142 REQUIRE(DNS_VALID_PORTLIST(portlist)); 143 REQUIRE(af == AF_INET || af == AF_INET6); 144 145 LOCK(&portlist->lock); 146 if (portlist->active != 0) { 147 el = find_port(portlist->list, portlist->active, port); 148 if (el != NULL) { 149 if (af == AF_INET) 150 el->flags |= DNS_PL_INET; 151 else 152 el->flags |= DNS_PL_INET6; 153 result = ISC_R_SUCCESS; 154 goto unlock; 155 } 156 } 157 158 if (portlist->allocated <= portlist->active) { 159 unsigned int allocated; 160 allocated = portlist->allocated + DNS_PL_ALLOCATE; 161 el = isc_mem_get(portlist->mctx, sizeof(*el) * allocated); 162 if (el == NULL) { 163 result = ISC_R_NOMEMORY; 164 goto unlock; 165 } 166 if (portlist->list != NULL) { 167 memcpy(el, portlist->list, 168 portlist->allocated * sizeof(*el)); 169 isc_mem_put(portlist->mctx, portlist->list, 170 portlist->allocated * sizeof(*el)); 171 } 172 portlist->list = el; 173 portlist->allocated = allocated; 174 } 175 portlist->list[portlist->active].port = port; 176 if (af == AF_INET) 177 portlist->list[portlist->active].flags = DNS_PL_INET; 178 else 179 portlist->list[portlist->active].flags = DNS_PL_INET6; 180 portlist->active++; 181 qsort(portlist->list, portlist->active, sizeof(*el), compare); 182 result = ISC_R_SUCCESS; 183 unlock: 184 UNLOCK(&portlist->lock); 185 return (result); 186} 187 188void 189dns_portlist_remove(dns_portlist_t *portlist, int af, in_port_t port) { 190 dns_element_t *el; 191 192 REQUIRE(DNS_VALID_PORTLIST(portlist)); 193 REQUIRE(af == AF_INET || af == AF_INET6); 194 195 LOCK(&portlist->lock); 196 if (portlist->active != 0) { 197 el = find_port(portlist->list, portlist->active, port); 198 if (el != NULL) { 199 if (af == AF_INET) 200 el->flags &= ~DNS_PL_INET; 201 else 202 el->flags &= ~DNS_PL_INET6; 203 if (el->flags == 0) { 204 *el = portlist->list[portlist->active]; 205 portlist->active--; 206 qsort(portlist->list, portlist->active, 207 sizeof(*el), compare); 208 } 209 } 210 } 211 UNLOCK(&portlist->lock); 212} 213 214isc_boolean_t 215dns_portlist_match(dns_portlist_t *portlist, int af, in_port_t port) { 216 dns_element_t *el; 217 isc_boolean_t result = ISC_FALSE; 218 219 REQUIRE(DNS_VALID_PORTLIST(portlist)); 220 REQUIRE(af == AF_INET || af == AF_INET6); 221 LOCK(&portlist->lock); 222 if (portlist->active != 0) { 223 el = find_port(portlist->list, portlist->active, port); 224 if (el != NULL) { 225 if (af == AF_INET && (el->flags & DNS_PL_INET) != 0) 226 result = ISC_TRUE; 227 if (af == AF_INET6 && (el->flags & DNS_PL_INET6) != 0) 228 result = ISC_TRUE; 229 } 230 } 231 UNLOCK(&portlist->lock); 232 return (result); 233} 234 235void 236dns_portlist_attach(dns_portlist_t *portlist, dns_portlist_t **portlistp) { 237 238 REQUIRE(DNS_VALID_PORTLIST(portlist)); 239 REQUIRE(portlistp != NULL && *portlistp == NULL); 240 241 isc_refcount_increment(&portlist->refcount, NULL); 242 *portlistp = portlist; 243} 244 245void 246dns_portlist_detach(dns_portlist_t **portlistp) { 247 dns_portlist_t *portlist; 248 unsigned int count; 249 250 REQUIRE(portlistp != NULL); 251 portlist = *portlistp; 252 REQUIRE(DNS_VALID_PORTLIST(portlist)); 253 *portlistp = NULL; 254 isc_refcount_decrement(&portlist->refcount, &count); 255 if (count == 0) { 256 portlist->magic = 0; 257 isc_refcount_destroy(&portlist->refcount); 258 if (portlist->list != NULL) 259 isc_mem_put(portlist->mctx, portlist->list, 260 portlist->allocated * 261 sizeof(*portlist->list)); 262 DESTROYLOCK(&portlist->lock); 263 isc_mem_putanddetach(&portlist->mctx, portlist, 264 sizeof(*portlist)); 265 } 266} 267