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