portlist.c revision 165071
1/* 2 * Copyright (C) 2004, 2006 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 2003 Internet Software Consortium. 4 * 5 * Permission to use, copy, modify, and 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.3.72.6 2006/08/25 05:25:50 marka Exp $ */ 19 20#include <config.h> 21 22#include <stdlib.h> 23 24#include <isc/magic.h> 25#include <isc/mem.h> 26#include <isc/mutex.h> 27#include <isc/net.h> 28#include <isc/refcount.h> 29#include <isc/result.h> 30#include <isc/string.h> 31#include <isc/types.h> 32#include <isc/util.h> 33 34#include <dns/types.h> 35#include <dns/portlist.h> 36 37#define DNS_PORTLIST_MAGIC ISC_MAGIC('P','L','S','T') 38#define DNS_VALID_PORTLIST(p) ISC_MAGIC_VALID(p, DNS_PORTLIST_MAGIC) 39 40typedef struct dns_element { 41 in_port_t port; 42 isc_uint16_t flags; 43} dns_element_t; 44 45struct dns_portlist { 46 unsigned int magic; 47 isc_mem_t *mctx; 48 isc_refcount_t refcount; 49 isc_mutex_t lock; 50 dns_element_t *list; 51 unsigned int allocated; 52 unsigned int active; 53}; 54 55#define DNS_PL_INET 0x0001 56#define DNS_PL_INET6 0x0002 57#define DNS_PL_ALLOCATE 16 58 59static int 60compare(const void *arg1, const void *arg2) { 61 const dns_element_t *e1 = (const dns_element_t *)arg1; 62 const dns_element_t *e2 = (const dns_element_t *)arg2; 63 64 if (e1->port < e2->port) 65 return (-1); 66 if (e1->port > e2->port) 67 return (1); 68 return (0); 69} 70 71isc_result_t 72dns_portlist_create(isc_mem_t *mctx, dns_portlist_t **portlistp) { 73 dns_portlist_t *portlist; 74 isc_result_t result; 75 76 REQUIRE(portlistp != NULL && *portlistp == NULL); 77 78 portlist = isc_mem_get(mctx, sizeof(*portlist)); 79 if (portlist == NULL) 80 return (ISC_R_NOMEMORY); 81 result = isc_mutex_init(&portlist->lock); 82 if (result != ISC_R_SUCCESS) { 83 isc_mem_put(mctx, portlist, sizeof(*portlist)); 84 UNEXPECTED_ERROR(__FILE__, __LINE__, 85 "isc_mutex_init() failed: %s", 86 isc_result_totext(result)); 87 return (ISC_R_UNEXPECTED); 88 } 89 isc_refcount_init(&portlist->refcount, 1); 90 portlist->list = NULL; 91 portlist->allocated = 0; 92 portlist->active = 0; 93 portlist->mctx = NULL; 94 isc_mem_attach(mctx, &portlist->mctx); 95 portlist->magic = DNS_PORTLIST_MAGIC; 96 *portlistp = portlist; 97 return (ISC_R_SUCCESS); 98} 99 100static dns_element_t * 101find_port(dns_element_t *list, unsigned int len, in_port_t port) { 102 unsigned int xtry = len / 2; 103 unsigned int min = 0; 104 unsigned int max = len - 1; 105 unsigned int last = len; 106 107 for (;;) { 108 if (list[xtry].port == port) 109 return (&list[xtry]); 110 if (port > list[xtry].port) { 111 if (xtry == max) 112 break; 113 min = xtry; 114 xtry = xtry + (max - xtry + 1) / 2; 115 INSIST(xtry <= max); 116 if (xtry == last) 117 break; 118 last = min; 119 } else { 120 if (xtry == min) 121 break; 122 max = xtry; 123 xtry = xtry - (xtry - min + 1) / 2; 124 INSIST(xtry >= min); 125 if (xtry == last) 126 break; 127 last = max; 128 } 129 } 130 return (NULL); 131} 132 133isc_result_t 134dns_portlist_add(dns_portlist_t *portlist, int af, in_port_t port) { 135 dns_element_t *el; 136 isc_result_t result; 137 138 REQUIRE(DNS_VALID_PORTLIST(portlist)); 139 REQUIRE(af == AF_INET || af == AF_INET6); 140 141 LOCK(&portlist->lock); 142 if (portlist->active != 0) { 143 el = find_port(portlist->list, portlist->active, port); 144 if (el != NULL) { 145 if (af == AF_INET) 146 el->flags |= DNS_PL_INET; 147 else 148 el->flags |= DNS_PL_INET6; 149 result = ISC_R_SUCCESS; 150 goto unlock; 151 } 152 } 153 154 if (portlist->allocated <= portlist->active) { 155 unsigned int allocated; 156 allocated = portlist->allocated + DNS_PL_ALLOCATE; 157 el = isc_mem_get(portlist->mctx, sizeof(*el) * allocated); 158 if (el == NULL) { 159 result = ISC_R_NOMEMORY; 160 goto unlock; 161 } 162 if (portlist->list != NULL) { 163 memcpy(el, portlist->list, 164 portlist->allocated * sizeof(*el)); 165 isc_mem_put(portlist->mctx, portlist->list, 166 portlist->allocated * sizeof(*el)); 167 } 168 portlist->list = el; 169 portlist->allocated = allocated; 170 } 171 portlist->list[portlist->active].port = port; 172 if (af == AF_INET) 173 portlist->list[portlist->active].flags = DNS_PL_INET; 174 else 175 portlist->list[portlist->active].flags = DNS_PL_INET6; 176 portlist->active++; 177 qsort(portlist->list, portlist->active, sizeof(*el), compare); 178 result = ISC_R_SUCCESS; 179 unlock: 180 UNLOCK(&portlist->lock); 181 return (result); 182} 183 184void 185dns_portlist_remove(dns_portlist_t *portlist, int af, in_port_t port) { 186 dns_element_t *el; 187 188 REQUIRE(DNS_VALID_PORTLIST(portlist)); 189 REQUIRE(af == AF_INET || af == AF_INET6); 190 191 LOCK(&portlist->lock); 192 if (portlist->active != 0) { 193 el = find_port(portlist->list, portlist->active, port); 194 if (el != NULL) { 195 if (af == AF_INET) 196 el->flags &= ~DNS_PL_INET; 197 else 198 el->flags &= ~DNS_PL_INET6; 199 if (el->flags == 0) { 200 *el = portlist->list[portlist->active]; 201 portlist->active--; 202 qsort(portlist->list, portlist->active, 203 sizeof(*el), compare); 204 } 205 } 206 } 207 UNLOCK(&portlist->lock); 208} 209 210isc_boolean_t 211dns_portlist_match(dns_portlist_t *portlist, int af, in_port_t port) { 212 dns_element_t *el; 213 isc_boolean_t result = ISC_FALSE; 214 215 REQUIRE(DNS_VALID_PORTLIST(portlist)); 216 REQUIRE(af == AF_INET || af == AF_INET6); 217 LOCK(&portlist->lock); 218 if (portlist->active != 0) { 219 el = find_port(portlist->list, portlist->active, port); 220 if (el != NULL) { 221 if (af == AF_INET && (el->flags & DNS_PL_INET) != 0) 222 result = ISC_TRUE; 223 if (af == AF_INET6 && (el->flags & DNS_PL_INET6) != 0) 224 result = ISC_TRUE; 225 } 226 } 227 UNLOCK(&portlist->lock); 228 return (result); 229} 230 231void 232dns_portlist_attach(dns_portlist_t *portlist, dns_portlist_t **portlistp) { 233 234 REQUIRE(DNS_VALID_PORTLIST(portlist)); 235 REQUIRE(portlistp != NULL && *portlistp == NULL); 236 237 isc_refcount_increment(&portlist->refcount, NULL); 238 *portlistp = portlist; 239} 240 241void 242dns_portlist_detach(dns_portlist_t **portlistp) { 243 dns_portlist_t *portlist; 244 unsigned int count; 245 246 REQUIRE(portlistp != NULL); 247 portlist = *portlistp; 248 REQUIRE(DNS_VALID_PORTLIST(portlist)); 249 *portlistp = NULL; 250 isc_refcount_decrement(&portlist->refcount, &count); 251 if (count == 0) { 252 portlist->magic = 0; 253 isc_refcount_destroy(&portlist->refcount); 254 if (portlist->list != NULL) 255 isc_mem_put(portlist->mctx, portlist->list, 256 portlist->allocated * 257 sizeof(*portlist->list)); 258 DESTROYLOCK(&portlist->lock); 259 isc_mem_putanddetach(&portlist->mctx, portlist, 260 sizeof(*portlist)); 261 } 262} 263