adb.c revision 153816
1135446Strhodes/* 2153816Sdougb * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 1999-2003 Internet Software Consortium. 4135446Strhodes * 5135446Strhodes * Permission to use, copy, modify, and 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 18153816Sdougb/* $Id: adb.c,v 1.181.2.11.2.24 2005/10/14 05:19:00 marka Exp $ */ 19135446Strhodes 20135446Strhodes/* 21135446Strhodes * Implementation notes 22135446Strhodes * -------------------- 23135446Strhodes * 24135446Strhodes * In finds, if task == NULL, no events will be generated, and no events 25135446Strhodes * have been sent. If task != NULL but taskaction == NULL, an event has been 26135446Strhodes * posted but not yet freed. If neither are NULL, no event was posted. 27135446Strhodes * 28135446Strhodes */ 29135446Strhodes 30135446Strhodes/* 31135446Strhodes * After we have cleaned all buckets, dump the database contents. 32135446Strhodes */ 33135446Strhodes#if 0 34135446Strhodes#define DUMP_ADB_AFTER_CLEANING 35135446Strhodes#endif 36135446Strhodes 37135446Strhodes#include <config.h> 38135446Strhodes 39135446Strhodes#include <limits.h> 40135446Strhodes 41135446Strhodes#include <isc/mutexblock.h> 42135446Strhodes#include <isc/netaddr.h> 43135446Strhodes#include <isc/random.h> 44135446Strhodes#include <isc/string.h> /* Required for HP/UX (and others?) */ 45135446Strhodes#include <isc/task.h> 46135446Strhodes#include <isc/timer.h> 47135446Strhodes#include <isc/util.h> 48135446Strhodes 49135446Strhodes#include <dns/adb.h> 50135446Strhodes#include <dns/db.h> 51135446Strhodes#include <dns/events.h> 52135446Strhodes#include <dns/log.h> 53135446Strhodes#include <dns/rdata.h> 54135446Strhodes#include <dns/rdataset.h> 55135446Strhodes#include <dns/rdatastruct.h> 56135446Strhodes#include <dns/resolver.h> 57135446Strhodes#include <dns/result.h> 58135446Strhodes 59135446Strhodes#define DNS_ADB_MAGIC ISC_MAGIC('D', 'a', 'd', 'b') 60135446Strhodes#define DNS_ADB_VALID(x) ISC_MAGIC_VALID(x, DNS_ADB_MAGIC) 61135446Strhodes#define DNS_ADBNAME_MAGIC ISC_MAGIC('a', 'd', 'b', 'N') 62135446Strhodes#define DNS_ADBNAME_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBNAME_MAGIC) 63135446Strhodes#define DNS_ADBNAMEHOOK_MAGIC ISC_MAGIC('a', 'd', 'N', 'H') 64135446Strhodes#define DNS_ADBNAMEHOOK_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBNAMEHOOK_MAGIC) 65135446Strhodes#define DNS_ADBZONEINFO_MAGIC ISC_MAGIC('a', 'd', 'b', 'Z') 66135446Strhodes#define DNS_ADBZONEINFO_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBZONEINFO_MAGIC) 67135446Strhodes#define DNS_ADBENTRY_MAGIC ISC_MAGIC('a', 'd', 'b', 'E') 68135446Strhodes#define DNS_ADBENTRY_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBENTRY_MAGIC) 69135446Strhodes#define DNS_ADBFETCH_MAGIC ISC_MAGIC('a', 'd', 'F', '4') 70135446Strhodes#define DNS_ADBFETCH_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBFETCH_MAGIC) 71135446Strhodes#define DNS_ADBFETCH6_MAGIC ISC_MAGIC('a', 'd', 'F', '6') 72135446Strhodes#define DNS_ADBFETCH6_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBFETCH6_MAGIC) 73135446Strhodes 74135446Strhodes/* 75135446Strhodes * The number of buckets needs to be a prime (for good hashing). 76135446Strhodes * 77135446Strhodes * XXXRTH How many buckets do we need? 78135446Strhodes */ 79135446Strhodes#define NBUCKETS 1009 /* how many buckets for names/addrs */ 80135446Strhodes 81135446Strhodes/* 82135446Strhodes * For type 3 negative cache entries, we will remember that the address is 83135446Strhodes * broken for this long. XXXMLG This is also used for actual addresses, too. 84135446Strhodes * The intent is to keep us from constantly asking about A/AAAA records 85135446Strhodes * if the zone has extremely low TTLs. 86135446Strhodes */ 87135446Strhodes#define ADB_CACHE_MINIMUM 10 /* seconds */ 88135446Strhodes#define ADB_CACHE_MAXIMUM 86400 /* seconds (86400 = 24 hours) */ 89135446Strhodes#define ADB_ENTRY_WINDOW 1800 /* seconds */ 90135446Strhodes 91135446Strhodes/* 92135446Strhodes * Wake up every CLEAN_SECONDS and clean CLEAN_BUCKETS buckets, so that all 93135446Strhodes * buckets are cleaned in CLEAN_PERIOD seconds. 94135446Strhodes */ 95135446Strhodes#define CLEAN_PERIOD 3600 96135446Strhodes#define CLEAN_SECONDS 30 97135446Strhodes#define CLEAN_BUCKETS ((NBUCKETS * CLEAN_SECONDS) / CLEAN_PERIOD) 98135446Strhodes 99135446Strhodes#define FREE_ITEMS 64 /* free count for memory pools */ 100135446Strhodes#define FILL_COUNT 16 /* fill count for memory pools */ 101135446Strhodes 102135446Strhodes#define DNS_ADB_INVALIDBUCKET (-1) /* invalid bucket address */ 103135446Strhodes 104135446Strhodes#define DNS_ADB_MINADBSIZE (1024*1024) /* 1 Megabyte */ 105135446Strhodes 106135446Strhodestypedef ISC_LIST(dns_adbname_t) dns_adbnamelist_t; 107135446Strhodestypedef struct dns_adbnamehook dns_adbnamehook_t; 108135446Strhodestypedef ISC_LIST(dns_adbnamehook_t) dns_adbnamehooklist_t; 109135446Strhodestypedef struct dns_adbzoneinfo dns_adbzoneinfo_t; 110135446Strhodestypedef ISC_LIST(dns_adbentry_t) dns_adbentrylist_t; 111135446Strhodestypedef struct dns_adbfetch dns_adbfetch_t; 112135446Strhodestypedef struct dns_adbfetch6 dns_adbfetch6_t; 113135446Strhodes 114135446Strhodesstruct dns_adb { 115135446Strhodes unsigned int magic; 116135446Strhodes 117135446Strhodes isc_mutex_t lock; 118135446Strhodes isc_mutex_t reflock; /* Covers irefcnt, erefcnt */ 119135446Strhodes isc_mem_t *mctx; 120135446Strhodes dns_view_t *view; 121135446Strhodes isc_timermgr_t *timermgr; 122135446Strhodes isc_timer_t *timer; 123135446Strhodes isc_taskmgr_t *taskmgr; 124135446Strhodes isc_task_t *task; 125135446Strhodes isc_boolean_t overmem; 126135446Strhodes 127135446Strhodes isc_interval_t tick_interval; 128135446Strhodes int next_cleanbucket; 129135446Strhodes 130135446Strhodes unsigned int irefcnt; 131135446Strhodes unsigned int erefcnt; 132135446Strhodes 133135446Strhodes isc_mutex_t mplock; 134135446Strhodes isc_mempool_t *nmp; /* dns_adbname_t */ 135135446Strhodes isc_mempool_t *nhmp; /* dns_adbnamehook_t */ 136135446Strhodes isc_mempool_t *zimp; /* dns_adbzoneinfo_t */ 137135446Strhodes isc_mempool_t *emp; /* dns_adbentry_t */ 138135446Strhodes isc_mempool_t *ahmp; /* dns_adbfind_t */ 139135446Strhodes isc_mempool_t *aimp; /* dns_adbaddrinfo_t */ 140135446Strhodes isc_mempool_t *afmp; /* dns_adbfetch_t */ 141135446Strhodes 142135446Strhodes /* 143135446Strhodes * Bucketized locks and lists for names. 144135446Strhodes * 145135446Strhodes * XXXRTH Have a per-bucket structure that contains all of these? 146135446Strhodes */ 147135446Strhodes dns_adbnamelist_t names[NBUCKETS]; 148135446Strhodes isc_mutex_t namelocks[NBUCKETS]; 149135446Strhodes isc_boolean_t name_sd[NBUCKETS]; 150135446Strhodes unsigned int name_refcnt[NBUCKETS]; 151135446Strhodes 152135446Strhodes /* 153135446Strhodes * Bucketized locks for entries. 154135446Strhodes * 155135446Strhodes * XXXRTH Have a per-bucket structure that contains all of these? 156135446Strhodes */ 157135446Strhodes dns_adbentrylist_t entries[NBUCKETS]; 158135446Strhodes isc_mutex_t entrylocks[NBUCKETS]; 159135446Strhodes isc_boolean_t entry_sd[NBUCKETS]; /* shutting down */ 160135446Strhodes unsigned int entry_refcnt[NBUCKETS]; 161135446Strhodes 162135446Strhodes isc_event_t cevent; 163135446Strhodes isc_boolean_t cevent_sent; 164135446Strhodes isc_boolean_t shutting_down; 165135446Strhodes isc_eventlist_t whenshutdown; 166135446Strhodes}; 167135446Strhodes 168135446Strhodes/* 169135446Strhodes * XXXMLG Document these structures. 170135446Strhodes */ 171135446Strhodes 172135446Strhodesstruct dns_adbname { 173135446Strhodes unsigned int magic; 174135446Strhodes dns_name_t name; 175135446Strhodes dns_adb_t *adb; 176135446Strhodes unsigned int partial_result; 177135446Strhodes unsigned int flags; 178135446Strhodes int lock_bucket; 179135446Strhodes dns_name_t target; 180135446Strhodes isc_stdtime_t expire_target; 181135446Strhodes isc_stdtime_t expire_v4; 182135446Strhodes isc_stdtime_t expire_v6; 183135446Strhodes unsigned int chains; 184135446Strhodes dns_adbnamehooklist_t v4; 185135446Strhodes dns_adbnamehooklist_t v6; 186135446Strhodes dns_adbfetch_t *fetch_a; 187135446Strhodes dns_adbfetch_t *fetch_aaaa; 188135446Strhodes unsigned int fetch_err; 189135446Strhodes unsigned int fetch6_err; 190135446Strhodes dns_adbfindlist_t finds; 191135446Strhodes ISC_LINK(dns_adbname_t) plink; 192135446Strhodes}; 193135446Strhodes 194135446Strhodesstruct dns_adbfetch { 195135446Strhodes unsigned int magic; 196135446Strhodes dns_adbnamehook_t *namehook; 197135446Strhodes dns_adbentry_t *entry; 198135446Strhodes dns_fetch_t *fetch; 199135446Strhodes dns_rdataset_t rdataset; 200135446Strhodes}; 201135446Strhodes 202135446Strhodes/* 203135446Strhodes * dns_adbnamehook_t 204135446Strhodes * 205135446Strhodes * This is a small widget that dangles off a dns_adbname_t. It contains a 206135446Strhodes * pointer to the address information about this host, and a link to the next 207135446Strhodes * namehook that will contain the next address this host has. 208135446Strhodes */ 209135446Strhodesstruct dns_adbnamehook { 210135446Strhodes unsigned int magic; 211135446Strhodes dns_adbentry_t *entry; 212135446Strhodes ISC_LINK(dns_adbnamehook_t) plink; 213135446Strhodes}; 214135446Strhodes 215135446Strhodes/* 216135446Strhodes * dns_adbzoneinfo_t 217135446Strhodes * 218135446Strhodes * This is a small widget that holds zone-specific information about an 219135446Strhodes * address. Currently limited to lameness, but could just as easily be 220135446Strhodes * extended to other types of information about zones. 221135446Strhodes */ 222135446Strhodesstruct dns_adbzoneinfo { 223135446Strhodes unsigned int magic; 224135446Strhodes 225135446Strhodes dns_name_t zone; 226135446Strhodes isc_stdtime_t lame_timer; 227135446Strhodes 228135446Strhodes ISC_LINK(dns_adbzoneinfo_t) plink; 229135446Strhodes}; 230135446Strhodes 231135446Strhodes/* 232135446Strhodes * An address entry. It holds quite a bit of information about addresses, 233135446Strhodes * including edns state (in "flags"), rtt, and of course the address of 234135446Strhodes * the host. 235135446Strhodes */ 236135446Strhodesstruct dns_adbentry { 237135446Strhodes unsigned int magic; 238135446Strhodes 239135446Strhodes int lock_bucket; 240135446Strhodes unsigned int refcnt; 241135446Strhodes 242135446Strhodes unsigned int flags; 243135446Strhodes unsigned int srtt; 244135446Strhodes isc_sockaddr_t sockaddr; 245135446Strhodes 246135446Strhodes isc_stdtime_t expires; 247135446Strhodes /* 248135446Strhodes * A nonzero 'expires' field indicates that the entry should 249135446Strhodes * persist until that time. This allows entries found 250135446Strhodes * using dns_adb_findaddrinfo() to persist for a limited time 251135446Strhodes * even though they are not necessarily associated with a 252135446Strhodes * name. 253135446Strhodes */ 254135446Strhodes 255135446Strhodes ISC_LIST(dns_adbzoneinfo_t) zoneinfo; 256135446Strhodes ISC_LINK(dns_adbentry_t) plink; 257135446Strhodes}; 258135446Strhodes 259135446Strhodes/* 260135446Strhodes * Internal functions (and prototypes). 261135446Strhodes */ 262135446Strhodesstatic inline dns_adbname_t *new_adbname(dns_adb_t *, dns_name_t *); 263135446Strhodesstatic inline void free_adbname(dns_adb_t *, dns_adbname_t **); 264135446Strhodesstatic inline dns_adbnamehook_t *new_adbnamehook(dns_adb_t *, 265135446Strhodes dns_adbentry_t *); 266135446Strhodesstatic inline void free_adbnamehook(dns_adb_t *, dns_adbnamehook_t **); 267135446Strhodesstatic inline dns_adbzoneinfo_t *new_adbzoneinfo(dns_adb_t *, dns_name_t *); 268135446Strhodesstatic inline void free_adbzoneinfo(dns_adb_t *, dns_adbzoneinfo_t **); 269135446Strhodesstatic inline dns_adbentry_t *new_adbentry(dns_adb_t *); 270135446Strhodesstatic inline void free_adbentry(dns_adb_t *, dns_adbentry_t **); 271135446Strhodesstatic inline dns_adbfind_t *new_adbfind(dns_adb_t *); 272135446Strhodesstatic inline isc_boolean_t free_adbfind(dns_adb_t *, dns_adbfind_t **); 273135446Strhodesstatic inline dns_adbaddrinfo_t *new_adbaddrinfo(dns_adb_t *, dns_adbentry_t *, 274135446Strhodes in_port_t); 275135446Strhodesstatic inline dns_adbfetch_t *new_adbfetch(dns_adb_t *); 276135446Strhodesstatic inline void free_adbfetch(dns_adb_t *, dns_adbfetch_t **); 277135446Strhodesstatic inline dns_adbname_t *find_name_and_lock(dns_adb_t *, dns_name_t *, 278135446Strhodes unsigned int, int *); 279135446Strhodesstatic inline dns_adbentry_t *find_entry_and_lock(dns_adb_t *, 280135446Strhodes isc_sockaddr_t *, int *); 281143731Sdougbstatic void dump_adb(dns_adb_t *, FILE *, isc_boolean_t debug, isc_stdtime_t); 282135446Strhodesstatic void print_dns_name(FILE *, dns_name_t *); 283135446Strhodesstatic void print_namehook_list(FILE *, const char *legend, 284135446Strhodes dns_adbnamehooklist_t *list, 285135446Strhodes isc_boolean_t debug, 286135446Strhodes isc_stdtime_t now); 287135446Strhodesstatic void print_find_list(FILE *, dns_adbname_t *); 288135446Strhodesstatic void print_fetch_list(FILE *, dns_adbname_t *); 289135446Strhodesstatic inline isc_boolean_t dec_adb_irefcnt(dns_adb_t *); 290135446Strhodesstatic inline void inc_adb_irefcnt(dns_adb_t *); 291135446Strhodesstatic inline void inc_adb_erefcnt(dns_adb_t *); 292135446Strhodesstatic inline void inc_entry_refcnt(dns_adb_t *, dns_adbentry_t *, 293135446Strhodes isc_boolean_t); 294135446Strhodesstatic inline isc_boolean_t dec_entry_refcnt(dns_adb_t *, dns_adbentry_t *, 295135446Strhodes isc_boolean_t); 296135446Strhodesstatic inline void violate_locking_hierarchy(isc_mutex_t *, isc_mutex_t *); 297135446Strhodesstatic isc_boolean_t clean_namehooks(dns_adb_t *, dns_adbnamehooklist_t *); 298135446Strhodesstatic void clean_target(dns_adb_t *, dns_name_t *); 299135446Strhodesstatic void clean_finds_at_name(dns_adbname_t *, isc_eventtype_t, 300135446Strhodes unsigned int); 301135446Strhodesstatic isc_boolean_t check_expire_namehooks(dns_adbname_t *, isc_stdtime_t, 302135446Strhodes isc_boolean_t); 303135446Strhodesstatic void cancel_fetches_at_name(dns_adbname_t *); 304135446Strhodesstatic isc_result_t dbfind_name(dns_adbname_t *, isc_stdtime_t, 305135446Strhodes dns_rdatatype_t); 306135446Strhodesstatic isc_result_t fetch_name(dns_adbname_t *, isc_boolean_t, 307135446Strhodes dns_rdatatype_t); 308135446Strhodesstatic inline void check_exit(dns_adb_t *); 309135446Strhodesstatic void timer_cleanup(isc_task_t *, isc_event_t *); 310135446Strhodesstatic void destroy(dns_adb_t *); 311135446Strhodesstatic isc_boolean_t shutdown_names(dns_adb_t *); 312135446Strhodesstatic isc_boolean_t shutdown_entries(dns_adb_t *); 313135446Strhodesstatic inline void link_name(dns_adb_t *, int, dns_adbname_t *); 314135446Strhodesstatic inline isc_boolean_t unlink_name(dns_adb_t *, dns_adbname_t *); 315135446Strhodesstatic inline void link_entry(dns_adb_t *, int, dns_adbentry_t *); 316135446Strhodesstatic inline isc_boolean_t unlink_entry(dns_adb_t *, dns_adbentry_t *); 317135446Strhodesstatic isc_boolean_t kill_name(dns_adbname_t **, isc_eventtype_t); 318143731Sdougbstatic void water(void *, int); 319143731Sdougbstatic void dump_entry(FILE *, dns_adbentry_t *, isc_boolean_t, isc_stdtime_t); 320135446Strhodes 321135446Strhodes/* 322135446Strhodes * MUST NOT overlap DNS_ADBFIND_* flags! 323135446Strhodes */ 324135446Strhodes#define FIND_EVENT_SENT 0x40000000 325135446Strhodes#define FIND_EVENT_FREED 0x80000000 326135446Strhodes#define FIND_EVENTSENT(h) (((h)->flags & FIND_EVENT_SENT) != 0) 327135446Strhodes#define FIND_EVENTFREED(h) (((h)->flags & FIND_EVENT_FREED) != 0) 328135446Strhodes 329135446Strhodes#define NAME_NEEDS_POKE 0x80000000 330135446Strhodes#define NAME_IS_DEAD 0x40000000 331135446Strhodes#define NAME_HINT_OK DNS_ADBFIND_HINTOK 332135446Strhodes#define NAME_GLUE_OK DNS_ADBFIND_GLUEOK 333135446Strhodes#define NAME_STARTATZONE DNS_ADBFIND_STARTATZONE 334135446Strhodes#define NAME_DEAD(n) (((n)->flags & NAME_IS_DEAD) != 0) 335135446Strhodes#define NAME_NEEDSPOKE(n) (((n)->flags & NAME_NEEDS_POKE) != 0) 336135446Strhodes#define NAME_GLUEOK(n) (((n)->flags & NAME_GLUE_OK) != 0) 337135446Strhodes#define NAME_HINTOK(n) (((n)->flags & NAME_HINT_OK) != 0) 338135446Strhodes 339135446Strhodes/* 340135446Strhodes * To the name, address classes are all that really exist. If it has a 341135446Strhodes * V6 address it doesn't care if it came from a AAAA query. 342135446Strhodes */ 343135446Strhodes#define NAME_HAS_V4(n) (!ISC_LIST_EMPTY((n)->v4)) 344135446Strhodes#define NAME_HAS_V6(n) (!ISC_LIST_EMPTY((n)->v6)) 345135446Strhodes#define NAME_HAS_ADDRS(n) (NAME_HAS_V4(n) || NAME_HAS_V6(n)) 346135446Strhodes 347135446Strhodes/* 348135446Strhodes * Fetches are broken out into A and AAAA types. In some cases, 349135446Strhodes * however, it makes more sense to test for a particular class of fetches, 350135446Strhodes * like V4 or V6 above. 351135446Strhodes * Note: since we have removed the support of A6 in adb, FETCH_A and FETCH_AAAA 352135446Strhodes * are now equal to FETCH_V4 and FETCH_V6, respectively. 353135446Strhodes */ 354135446Strhodes#define NAME_FETCH_A(n) ((n)->fetch_a != NULL) 355135446Strhodes#define NAME_FETCH_AAAA(n) ((n)->fetch_aaaa != NULL) 356135446Strhodes#define NAME_FETCH_V4(n) (NAME_FETCH_A(n)) 357135446Strhodes#define NAME_FETCH_V6(n) (NAME_FETCH_AAAA(n)) 358135446Strhodes#define NAME_FETCH(n) (NAME_FETCH_V4(n) || NAME_FETCH_V6(n)) 359135446Strhodes 360135446Strhodes/* 361135446Strhodes * Find options and tests to see if there are addresses on the list. 362135446Strhodes */ 363135446Strhodes#define FIND_WANTEVENT(fn) (((fn)->options & DNS_ADBFIND_WANTEVENT) != 0) 364135446Strhodes#define FIND_WANTEMPTYEVENT(fn) (((fn)->options & DNS_ADBFIND_EMPTYEVENT) != 0) 365135446Strhodes#define FIND_AVOIDFETCHES(fn) (((fn)->options & DNS_ADBFIND_AVOIDFETCHES) \ 366135446Strhodes != 0) 367135446Strhodes#define FIND_STARTATZONE(fn) (((fn)->options & DNS_ADBFIND_STARTATZONE) \ 368135446Strhodes != 0) 369135446Strhodes#define FIND_HINTOK(fn) (((fn)->options & DNS_ADBFIND_HINTOK) != 0) 370135446Strhodes#define FIND_GLUEOK(fn) (((fn)->options & DNS_ADBFIND_GLUEOK) != 0) 371135446Strhodes#define FIND_HAS_ADDRS(fn) (!ISC_LIST_EMPTY((fn)->list)) 372135446Strhodes#define FIND_RETURNLAME(fn) (((fn)->options & DNS_ADBFIND_RETURNLAME) != 0) 373135446Strhodes 374135446Strhodes/* 375135446Strhodes * These are currently used on simple unsigned ints, so they are 376135446Strhodes * not really associated with any particular type. 377135446Strhodes */ 378135446Strhodes#define WANT_INET(x) (((x) & DNS_ADBFIND_INET) != 0) 379135446Strhodes#define WANT_INET6(x) (((x) & DNS_ADBFIND_INET6) != 0) 380135446Strhodes 381135446Strhodes#define EXPIRE_OK(exp, now) ((exp == INT_MAX) || (exp < now)) 382135446Strhodes 383135446Strhodes/* 384135446Strhodes * Find out if the flags on a name (nf) indicate if it is a hint or 385135446Strhodes * glue, and compare this to the appropriate bits set in o, to see if 386135446Strhodes * this is ok. 387135446Strhodes */ 388135446Strhodes#define GLUE_OK(nf, o) (!NAME_GLUEOK(nf) || (((o) & DNS_ADBFIND_GLUEOK) != 0)) 389135446Strhodes#define HINT_OK(nf, o) (!NAME_HINTOK(nf) || (((o) & DNS_ADBFIND_HINTOK) != 0)) 390135446Strhodes#define GLUEHINT_OK(nf, o) (GLUE_OK(nf, o) || HINT_OK(nf, o)) 391135446Strhodes#define STARTATZONE_MATCHES(nf, o) (((nf)->flags & NAME_STARTATZONE) == \ 392135446Strhodes ((o) & DNS_ADBFIND_STARTATZONE)) 393135446Strhodes 394135446Strhodes#define ENTER_LEVEL ISC_LOG_DEBUG(50) 395135446Strhodes#define EXIT_LEVEL ENTER_LEVEL 396135446Strhodes#define CLEAN_LEVEL ISC_LOG_DEBUG(100) 397135446Strhodes#define DEF_LEVEL ISC_LOG_DEBUG(5) 398135446Strhodes#define NCACHE_LEVEL ISC_LOG_DEBUG(20) 399135446Strhodes 400135446Strhodes#define NCACHE_RESULT(r) ((r) == DNS_R_NCACHENXDOMAIN || \ 401135446Strhodes (r) == DNS_R_NCACHENXRRSET) 402135446Strhodes#define AUTH_NX(r) ((r) == DNS_R_NXDOMAIN || \ 403135446Strhodes (r) == DNS_R_NXRRSET) 404135446Strhodes#define NXDOMAIN_RESULT(r) ((r) == DNS_R_NXDOMAIN || \ 405135446Strhodes (r) == DNS_R_NCACHENXDOMAIN) 406135446Strhodes#define NXRRSET_RESULT(r) ((r) == DNS_R_NCACHENXRRSET || \ 407135446Strhodes (r) == DNS_R_NXRRSET || \ 408135446Strhodes (r) == DNS_R_HINTNXRRSET) 409135446Strhodes 410135446Strhodes/* 411135446Strhodes * Error state rankings. 412135446Strhodes */ 413135446Strhodes 414135446Strhodes#define FIND_ERR_SUCCESS 0 /* highest rank */ 415135446Strhodes#define FIND_ERR_CANCELED 1 416135446Strhodes#define FIND_ERR_FAILURE 2 417135446Strhodes#define FIND_ERR_NXDOMAIN 3 418135446Strhodes#define FIND_ERR_NXRRSET 4 419135446Strhodes#define FIND_ERR_UNEXPECTED 5 420135446Strhodes#define FIND_ERR_NOTFOUND 6 421135446Strhodes#define FIND_ERR_MAX 7 422135446Strhodes 423135446Strhodesstatic const char *errnames[] = { 424135446Strhodes "success", 425135446Strhodes "canceled", 426135446Strhodes "failure", 427135446Strhodes "nxdomain", 428135446Strhodes "nxrrset", 429135446Strhodes "unexpected", 430135446Strhodes "not_found" 431135446Strhodes}; 432135446Strhodes 433135446Strhodes#define NEWERR(old, new) (ISC_MIN((old), (new))) 434135446Strhodes 435135446Strhodesstatic isc_result_t find_err_map[FIND_ERR_MAX] = { 436135446Strhodes ISC_R_SUCCESS, 437135446Strhodes ISC_R_CANCELED, 438135446Strhodes ISC_R_FAILURE, 439135446Strhodes DNS_R_NXDOMAIN, 440135446Strhodes DNS_R_NXRRSET, 441135446Strhodes ISC_R_UNEXPECTED, 442135446Strhodes ISC_R_NOTFOUND /* not YET found */ 443135446Strhodes}; 444135446Strhodes 445135446Strhodesstatic void 446135446StrhodesDP(int level, const char *format, ...) ISC_FORMAT_PRINTF(2, 3); 447135446Strhodes 448135446Strhodesstatic void 449135446StrhodesDP(int level, const char *format, ...) { 450135446Strhodes va_list args; 451135446Strhodes 452135446Strhodes va_start(args, format); 453135446Strhodes isc_log_vwrite(dns_lctx, 454135446Strhodes DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ADB, 455135446Strhodes level, format, args); 456135446Strhodes va_end(args); 457135446Strhodes} 458135446Strhodes 459135446Strhodesstatic inline dns_ttl_t 460135446Strhodesttlclamp(dns_ttl_t ttl) { 461135446Strhodes if (ttl < ADB_CACHE_MINIMUM) 462135446Strhodes ttl = ADB_CACHE_MINIMUM; 463135446Strhodes if (ttl > ADB_CACHE_MAXIMUM) 464135446Strhodes ttl = ADB_CACHE_MAXIMUM; 465135446Strhodes 466135446Strhodes return (ttl); 467135446Strhodes} 468135446Strhodes 469135446Strhodes/* 470135446Strhodes * Requires the adbname bucket be locked and that no entry buckets be locked. 471135446Strhodes * 472135446Strhodes * This code handles A and AAAA rdatasets only. 473135446Strhodes */ 474135446Strhodesstatic isc_result_t 475135446Strhodesimport_rdataset(dns_adbname_t *adbname, dns_rdataset_t *rdataset, 476135446Strhodes isc_stdtime_t now) 477135446Strhodes{ 478135446Strhodes isc_result_t result; 479135446Strhodes dns_adb_t *adb; 480135446Strhodes dns_adbnamehook_t *nh; 481135446Strhodes dns_adbnamehook_t *anh; 482135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 483135446Strhodes struct in_addr ina; 484135446Strhodes struct in6_addr in6a; 485135446Strhodes isc_sockaddr_t sockaddr; 486135446Strhodes dns_adbentry_t *foundentry; /* NO CLEAN UP! */ 487135446Strhodes int addr_bucket; 488135446Strhodes isc_boolean_t new_addresses_added; 489135446Strhodes dns_rdatatype_t rdtype; 490135446Strhodes unsigned int findoptions; 491135446Strhodes 492135446Strhodes INSIST(DNS_ADBNAME_VALID(adbname)); 493135446Strhodes adb = adbname->adb; 494135446Strhodes INSIST(DNS_ADB_VALID(adb)); 495135446Strhodes 496135446Strhodes rdtype = rdataset->type; 497135446Strhodes INSIST((rdtype == dns_rdatatype_a) || (rdtype == dns_rdatatype_aaaa)); 498135446Strhodes if (rdtype == dns_rdatatype_a) 499135446Strhodes findoptions = DNS_ADBFIND_INET; 500135446Strhodes else 501135446Strhodes findoptions = DNS_ADBFIND_INET6; 502135446Strhodes 503135446Strhodes addr_bucket = DNS_ADB_INVALIDBUCKET; 504135446Strhodes new_addresses_added = ISC_FALSE; 505135446Strhodes 506135446Strhodes nh = NULL; 507135446Strhodes result = dns_rdataset_first(rdataset); 508135446Strhodes while (result == ISC_R_SUCCESS) { 509135446Strhodes dns_rdata_reset(&rdata); 510135446Strhodes dns_rdataset_current(rdataset, &rdata); 511135446Strhodes if (rdtype == dns_rdatatype_a) { 512135446Strhodes INSIST(rdata.length == 4); 513135446Strhodes memcpy(&ina.s_addr, rdata.data, 4); 514135446Strhodes isc_sockaddr_fromin(&sockaddr, &ina, 0); 515135446Strhodes } else { 516135446Strhodes INSIST(rdata.length == 16); 517135446Strhodes memcpy(in6a.s6_addr, rdata.data, 16); 518135446Strhodes isc_sockaddr_fromin6(&sockaddr, &in6a, 0); 519135446Strhodes } 520135446Strhodes 521135446Strhodes INSIST(nh == NULL); 522135446Strhodes nh = new_adbnamehook(adb, NULL); 523135446Strhodes if (nh == NULL) { 524135446Strhodes adbname->partial_result |= findoptions; 525135446Strhodes result = ISC_R_NOMEMORY; 526135446Strhodes goto fail; 527135446Strhodes } 528135446Strhodes 529135446Strhodes foundentry = find_entry_and_lock(adb, &sockaddr, &addr_bucket); 530135446Strhodes if (foundentry == NULL) { 531135446Strhodes dns_adbentry_t *entry; 532135446Strhodes 533135446Strhodes entry = new_adbentry(adb); 534135446Strhodes if (entry == NULL) { 535135446Strhodes adbname->partial_result |= findoptions; 536135446Strhodes result = ISC_R_NOMEMORY; 537135446Strhodes goto fail; 538135446Strhodes } 539135446Strhodes 540135446Strhodes entry->sockaddr = sockaddr; 541135446Strhodes entry->refcnt = 1; 542135446Strhodes 543135446Strhodes nh->entry = entry; 544135446Strhodes 545135446Strhodes link_entry(adb, addr_bucket, entry); 546135446Strhodes } else { 547135446Strhodes for (anh = ISC_LIST_HEAD(adbname->v4); 548135446Strhodes anh != NULL; 549135446Strhodes anh = ISC_LIST_NEXT(anh, plink)) 550135446Strhodes if (anh->entry == foundentry) 551135446Strhodes break; 552135446Strhodes if (anh == NULL) { 553135446Strhodes foundentry->refcnt++; 554135446Strhodes nh->entry = foundentry; 555135446Strhodes } else 556135446Strhodes free_adbnamehook(adb, &nh); 557135446Strhodes } 558135446Strhodes 559135446Strhodes new_addresses_added = ISC_TRUE; 560135446Strhodes if (nh != NULL) { 561135446Strhodes if (rdtype == dns_rdatatype_a) 562135446Strhodes ISC_LIST_APPEND(adbname->v4, nh, plink); 563135446Strhodes else 564135446Strhodes ISC_LIST_APPEND(adbname->v6, nh, plink); 565135446Strhodes } 566135446Strhodes nh = NULL; 567135446Strhodes result = dns_rdataset_next(rdataset); 568135446Strhodes } 569135446Strhodes 570135446Strhodes fail: 571135446Strhodes if (nh != NULL) 572135446Strhodes free_adbnamehook(adb, &nh); 573135446Strhodes 574135446Strhodes if (addr_bucket != DNS_ADB_INVALIDBUCKET) 575135446Strhodes UNLOCK(&adb->entrylocks[addr_bucket]); 576135446Strhodes 577135446Strhodes if (rdataset->trust == dns_trust_glue || 578135446Strhodes rdataset->trust == dns_trust_additional) 579135446Strhodes rdataset->ttl = ADB_CACHE_MINIMUM; 580135446Strhodes else 581135446Strhodes rdataset->ttl = ttlclamp(rdataset->ttl); 582135446Strhodes 583135446Strhodes if (rdtype == dns_rdatatype_a) { 584135446Strhodes DP(NCACHE_LEVEL, "expire_v4 set to MIN(%u,%u) import_rdataset", 585135446Strhodes adbname->expire_v4, now + rdataset->ttl); 586135446Strhodes adbname->expire_v4 = ISC_MIN(adbname->expire_v4, 587135446Strhodes now + rdataset->ttl); 588135446Strhodes } else { 589135446Strhodes DP(NCACHE_LEVEL, "expire_v6 set to MIN(%u,%u) import_rdataset", 590135446Strhodes adbname->expire_v6, now + rdataset->ttl); 591135446Strhodes adbname->expire_v6 = ISC_MIN(adbname->expire_v6, 592135446Strhodes now + rdataset->ttl); 593135446Strhodes } 594135446Strhodes 595135446Strhodes if (new_addresses_added) { 596135446Strhodes /* 597135446Strhodes * Lie a little here. This is more or less so code that cares 598135446Strhodes * can find out if any new information was added or not. 599135446Strhodes */ 600135446Strhodes return (ISC_R_SUCCESS); 601135446Strhodes } 602135446Strhodes 603135446Strhodes return (result); 604135446Strhodes} 605135446Strhodes 606135446Strhodes/* 607135446Strhodes * Requires the name's bucket be locked. 608135446Strhodes */ 609135446Strhodesstatic isc_boolean_t 610135446Strhodeskill_name(dns_adbname_t **n, isc_eventtype_t ev) { 611135446Strhodes dns_adbname_t *name; 612135446Strhodes isc_boolean_t result = ISC_FALSE; 613135446Strhodes isc_boolean_t result4, result6; 614135446Strhodes dns_adb_t *adb; 615135446Strhodes 616135446Strhodes INSIST(n != NULL); 617135446Strhodes name = *n; 618135446Strhodes *n = NULL; 619135446Strhodes INSIST(DNS_ADBNAME_VALID(name)); 620135446Strhodes adb = name->adb; 621135446Strhodes INSIST(DNS_ADB_VALID(adb)); 622135446Strhodes 623135446Strhodes DP(DEF_LEVEL, "killing name %p", name); 624135446Strhodes 625135446Strhodes /* 626135446Strhodes * If we're dead already, just check to see if we should go 627135446Strhodes * away now or not. 628135446Strhodes */ 629135446Strhodes if (NAME_DEAD(name) && !NAME_FETCH(name)) { 630135446Strhodes result = unlink_name(adb, name); 631135446Strhodes free_adbname(adb, &name); 632135446Strhodes if (result) 633135446Strhodes result = dec_adb_irefcnt(adb); 634135446Strhodes return (result); 635135446Strhodes } 636135446Strhodes 637135446Strhodes /* 638135446Strhodes * Clean up the name's various lists. These two are destructive 639135446Strhodes * in that they will always empty the list. 640135446Strhodes */ 641135446Strhodes clean_finds_at_name(name, ev, DNS_ADBFIND_ADDRESSMASK); 642135446Strhodes result4 = clean_namehooks(adb, &name->v4); 643135446Strhodes result6 = clean_namehooks(adb, &name->v6); 644135446Strhodes clean_target(adb, &name->target); 645135446Strhodes result = ISC_TF(result4 || result6); 646135446Strhodes 647135446Strhodes /* 648135446Strhodes * If fetches are running, cancel them. If none are running, we can 649135446Strhodes * just kill the name here. 650135446Strhodes */ 651135446Strhodes if (!NAME_FETCH(name)) { 652135446Strhodes INSIST(result == ISC_FALSE); 653135446Strhodes result = unlink_name(adb, name); 654135446Strhodes free_adbname(adb, &name); 655135446Strhodes if (result) 656135446Strhodes result = dec_adb_irefcnt(adb); 657135446Strhodes } else { 658135446Strhodes name->flags |= NAME_IS_DEAD; 659135446Strhodes cancel_fetches_at_name(name); 660135446Strhodes } 661135446Strhodes return (result); 662135446Strhodes} 663135446Strhodes 664135446Strhodes/* 665135446Strhodes * Requires the name's bucket be locked and no entry buckets be locked. 666135446Strhodes */ 667135446Strhodesstatic isc_boolean_t 668135446Strhodescheck_expire_namehooks(dns_adbname_t *name, isc_stdtime_t now, 669135446Strhodes isc_boolean_t overmem) 670135446Strhodes{ 671135446Strhodes dns_adb_t *adb; 672135446Strhodes isc_boolean_t expire; 673135446Strhodes isc_boolean_t result4 = ISC_FALSE; 674135446Strhodes isc_boolean_t result6 = ISC_FALSE; 675135446Strhodes 676135446Strhodes INSIST(DNS_ADBNAME_VALID(name)); 677135446Strhodes adb = name->adb; 678135446Strhodes INSIST(DNS_ADB_VALID(adb)); 679135446Strhodes 680135446Strhodes if (overmem) { 681135446Strhodes isc_uint32_t val; 682135446Strhodes 683135446Strhodes isc_random_get(&val); 684135446Strhodes 685135446Strhodes expire = ISC_TF((val % 4) == 0); 686135446Strhodes } else 687135446Strhodes expire = ISC_FALSE; 688135446Strhodes 689135446Strhodes /* 690135446Strhodes * Check to see if we need to remove the v4 addresses 691135446Strhodes */ 692135446Strhodes if (!NAME_FETCH_V4(name) && 693135446Strhodes (expire || EXPIRE_OK(name->expire_v4, now))) { 694135446Strhodes if (NAME_HAS_V4(name)) { 695135446Strhodes DP(DEF_LEVEL, "expiring v4 for name %p", name); 696135446Strhodes result4 = clean_namehooks(adb, &name->v4); 697135446Strhodes name->partial_result &= ~DNS_ADBFIND_INET; 698135446Strhodes } 699135446Strhodes name->expire_v4 = INT_MAX; 700135446Strhodes name->fetch_err = FIND_ERR_UNEXPECTED; 701135446Strhodes } 702135446Strhodes 703135446Strhodes /* 704135446Strhodes * Check to see if we need to remove the v6 addresses 705135446Strhodes */ 706135446Strhodes if (!NAME_FETCH_V6(name) && 707135446Strhodes (expire || EXPIRE_OK(name->expire_v6, now))) { 708135446Strhodes if (NAME_HAS_V6(name)) { 709135446Strhodes DP(DEF_LEVEL, "expiring v6 for name %p", name); 710135446Strhodes result6 = clean_namehooks(adb, &name->v6); 711135446Strhodes name->partial_result &= ~DNS_ADBFIND_INET6; 712135446Strhodes } 713135446Strhodes name->expire_v6 = INT_MAX; 714135446Strhodes name->fetch6_err = FIND_ERR_UNEXPECTED; 715135446Strhodes } 716135446Strhodes 717135446Strhodes /* 718135446Strhodes * Check to see if we need to remove the alias target. 719135446Strhodes */ 720135446Strhodes if (expire || EXPIRE_OK(name->expire_target, now)) { 721135446Strhodes clean_target(adb, &name->target); 722135446Strhodes name->expire_target = INT_MAX; 723135446Strhodes } 724135446Strhodes return (ISC_TF(result4 || result6)); 725135446Strhodes} 726135446Strhodes 727135446Strhodes/* 728135446Strhodes * Requires the name's bucket be locked. 729135446Strhodes */ 730135446Strhodesstatic inline void 731135446Strhodeslink_name(dns_adb_t *adb, int bucket, dns_adbname_t *name) { 732135446Strhodes INSIST(name->lock_bucket == DNS_ADB_INVALIDBUCKET); 733135446Strhodes 734135446Strhodes ISC_LIST_PREPEND(adb->names[bucket], name, plink); 735135446Strhodes name->lock_bucket = bucket; 736135446Strhodes adb->name_refcnt[bucket]++; 737135446Strhodes} 738135446Strhodes 739135446Strhodes/* 740135446Strhodes * Requires the name's bucket be locked. 741135446Strhodes */ 742135446Strhodesstatic inline isc_boolean_t 743135446Strhodesunlink_name(dns_adb_t *adb, dns_adbname_t *name) { 744135446Strhodes int bucket; 745135446Strhodes isc_boolean_t result = ISC_FALSE; 746135446Strhodes 747135446Strhodes bucket = name->lock_bucket; 748135446Strhodes INSIST(bucket != DNS_ADB_INVALIDBUCKET); 749135446Strhodes 750135446Strhodes ISC_LIST_UNLINK(adb->names[bucket], name, plink); 751135446Strhodes name->lock_bucket = DNS_ADB_INVALIDBUCKET; 752135446Strhodes INSIST(adb->name_refcnt[bucket] > 0); 753135446Strhodes adb->name_refcnt[bucket]--; 754135446Strhodes if (adb->name_sd[bucket] && adb->name_refcnt[bucket] == 0) 755135446Strhodes result = ISC_TRUE; 756135446Strhodes return (result); 757135446Strhodes} 758135446Strhodes 759135446Strhodes/* 760135446Strhodes * Requires the entry's bucket be locked. 761135446Strhodes */ 762135446Strhodesstatic inline void 763135446Strhodeslink_entry(dns_adb_t *adb, int bucket, dns_adbentry_t *entry) { 764135446Strhodes ISC_LIST_PREPEND(adb->entries[bucket], entry, plink); 765135446Strhodes entry->lock_bucket = bucket; 766135446Strhodes adb->entry_refcnt[bucket]++; 767135446Strhodes} 768135446Strhodes 769135446Strhodes/* 770135446Strhodes * Requires the entry's bucket be locked. 771135446Strhodes */ 772135446Strhodesstatic inline isc_boolean_t 773135446Strhodesunlink_entry(dns_adb_t *adb, dns_adbentry_t *entry) { 774135446Strhodes int bucket; 775135446Strhodes isc_boolean_t result = ISC_FALSE; 776135446Strhodes 777135446Strhodes bucket = entry->lock_bucket; 778135446Strhodes INSIST(bucket != DNS_ADB_INVALIDBUCKET); 779135446Strhodes 780135446Strhodes ISC_LIST_UNLINK(adb->entries[bucket], entry, plink); 781135446Strhodes entry->lock_bucket = DNS_ADB_INVALIDBUCKET; 782135446Strhodes INSIST(adb->entry_refcnt[bucket] > 0); 783135446Strhodes adb->entry_refcnt[bucket]--; 784135446Strhodes if (adb->entry_sd[bucket] && adb->entry_refcnt[bucket] == 0) 785135446Strhodes result = ISC_TRUE; 786135446Strhodes return (result); 787135446Strhodes} 788135446Strhodes 789135446Strhodesstatic inline void 790135446Strhodesviolate_locking_hierarchy(isc_mutex_t *have, isc_mutex_t *want) { 791135446Strhodes if (isc_mutex_trylock(want) != ISC_R_SUCCESS) { 792135446Strhodes UNLOCK(have); 793135446Strhodes LOCK(want); 794135446Strhodes LOCK(have); 795135446Strhodes } 796135446Strhodes} 797135446Strhodes 798135446Strhodes/* 799135446Strhodes * The ADB _MUST_ be locked before calling. Also, exit conditions must be 800135446Strhodes * checked after calling this function. 801135446Strhodes */ 802135446Strhodesstatic isc_boolean_t 803135446Strhodesshutdown_names(dns_adb_t *adb) { 804135446Strhodes int bucket; 805135446Strhodes isc_boolean_t result = ISC_FALSE; 806135446Strhodes dns_adbname_t *name; 807135446Strhodes dns_adbname_t *next_name; 808135446Strhodes 809135446Strhodes for (bucket = 0; bucket < NBUCKETS; bucket++) { 810135446Strhodes LOCK(&adb->namelocks[bucket]); 811135446Strhodes adb->name_sd[bucket] = ISC_TRUE; 812135446Strhodes 813135446Strhodes name = ISC_LIST_HEAD(adb->names[bucket]); 814135446Strhodes if (name == NULL) { 815135446Strhodes /* 816135446Strhodes * This bucket has no names. We must decrement the 817135446Strhodes * irefcnt ourselves, since it will not be 818135446Strhodes * automatically triggered by a name being unlinked. 819135446Strhodes */ 820135446Strhodes INSIST(result == ISC_FALSE); 821135446Strhodes result = dec_adb_irefcnt(adb); 822135446Strhodes } else { 823135446Strhodes /* 824135446Strhodes * Run through the list. For each name, clean up finds 825135446Strhodes * found there, and cancel any fetches running. When 826135446Strhodes * all the fetches are canceled, the name will destroy 827135446Strhodes * itself. 828135446Strhodes */ 829135446Strhodes while (name != NULL) { 830135446Strhodes next_name = ISC_LIST_NEXT(name, plink); 831135446Strhodes INSIST(result == ISC_FALSE); 832135446Strhodes result = kill_name(&name, 833135446Strhodes DNS_EVENT_ADBSHUTDOWN); 834135446Strhodes name = next_name; 835135446Strhodes } 836135446Strhodes } 837135446Strhodes 838135446Strhodes UNLOCK(&adb->namelocks[bucket]); 839135446Strhodes } 840135446Strhodes return (result); 841135446Strhodes} 842135446Strhodes 843135446Strhodes/* 844135446Strhodes * The ADB _MUST_ be locked before calling. Also, exit conditions must be 845135446Strhodes * checked after calling this function. 846135446Strhodes */ 847135446Strhodesstatic isc_boolean_t 848135446Strhodesshutdown_entries(dns_adb_t *adb) { 849135446Strhodes int bucket; 850135446Strhodes isc_boolean_t result = ISC_FALSE; 851135446Strhodes dns_adbentry_t *entry; 852135446Strhodes dns_adbentry_t *next_entry; 853135446Strhodes 854135446Strhodes for (bucket = 0; bucket < NBUCKETS; bucket++) { 855135446Strhodes LOCK(&adb->entrylocks[bucket]); 856135446Strhodes adb->entry_sd[bucket] = ISC_TRUE; 857135446Strhodes 858135446Strhodes entry = ISC_LIST_HEAD(adb->entries[bucket]); 859135446Strhodes if (entry == NULL) { 860135446Strhodes /* 861135446Strhodes * This bucket has no entries. We must decrement the 862135446Strhodes * irefcnt ourselves, since it will not be 863135446Strhodes * automatically triggered by an entry being unlinked. 864135446Strhodes */ 865135446Strhodes result = dec_adb_irefcnt(adb); 866135446Strhodes } else { 867135446Strhodes /* 868135446Strhodes * Run through the list. Cleanup any entries not 869135446Strhodes * associated with names, and which are not in use. 870135446Strhodes */ 871135446Strhodes while (entry != NULL) { 872135446Strhodes next_entry = ISC_LIST_NEXT(entry, plink); 873135446Strhodes if (entry->refcnt == 0 && 874135446Strhodes entry->expires != 0) { 875135446Strhodes result = unlink_entry(adb, entry); 876135446Strhodes free_adbentry(adb, &entry); 877135446Strhodes if (result) 878135446Strhodes result = dec_adb_irefcnt(adb); 879135446Strhodes } 880135446Strhodes entry = next_entry; 881135446Strhodes } 882135446Strhodes } 883135446Strhodes 884135446Strhodes UNLOCK(&adb->entrylocks[bucket]); 885135446Strhodes } 886135446Strhodes return (result); 887135446Strhodes} 888135446Strhodes 889135446Strhodes/* 890135446Strhodes * Name bucket must be locked 891135446Strhodes */ 892135446Strhodesstatic void 893135446Strhodescancel_fetches_at_name(dns_adbname_t *name) { 894135446Strhodes if (NAME_FETCH_A(name)) 895135446Strhodes dns_resolver_cancelfetch(name->fetch_a->fetch); 896135446Strhodes 897135446Strhodes if (NAME_FETCH_AAAA(name)) 898135446Strhodes dns_resolver_cancelfetch(name->fetch_aaaa->fetch); 899135446Strhodes} 900135446Strhodes 901135446Strhodes/* 902135446Strhodes * Assumes the name bucket is locked. 903135446Strhodes */ 904135446Strhodesstatic isc_boolean_t 905135446Strhodesclean_namehooks(dns_adb_t *adb, dns_adbnamehooklist_t *namehooks) { 906135446Strhodes dns_adbentry_t *entry; 907135446Strhodes dns_adbnamehook_t *namehook; 908135446Strhodes int addr_bucket; 909135446Strhodes isc_boolean_t result = ISC_FALSE; 910135446Strhodes 911135446Strhodes addr_bucket = DNS_ADB_INVALIDBUCKET; 912135446Strhodes namehook = ISC_LIST_HEAD(*namehooks); 913135446Strhodes while (namehook != NULL) { 914135446Strhodes INSIST(DNS_ADBNAMEHOOK_VALID(namehook)); 915135446Strhodes 916135446Strhodes /* 917135446Strhodes * Clean up the entry if needed. 918135446Strhodes */ 919135446Strhodes entry = namehook->entry; 920135446Strhodes if (entry != NULL) { 921135446Strhodes INSIST(DNS_ADBENTRY_VALID(entry)); 922135446Strhodes 923135446Strhodes if (addr_bucket != entry->lock_bucket) { 924135446Strhodes if (addr_bucket != DNS_ADB_INVALIDBUCKET) 925135446Strhodes UNLOCK(&adb->entrylocks[addr_bucket]); 926135446Strhodes addr_bucket = entry->lock_bucket; 927135446Strhodes LOCK(&adb->entrylocks[addr_bucket]); 928135446Strhodes } 929135446Strhodes 930135446Strhodes result = dec_entry_refcnt(adb, entry, ISC_FALSE); 931135446Strhodes } 932135446Strhodes 933135446Strhodes /* 934135446Strhodes * Free the namehook 935135446Strhodes */ 936135446Strhodes namehook->entry = NULL; 937135446Strhodes ISC_LIST_UNLINK(*namehooks, namehook, plink); 938135446Strhodes free_adbnamehook(adb, &namehook); 939135446Strhodes 940135446Strhodes namehook = ISC_LIST_HEAD(*namehooks); 941135446Strhodes } 942135446Strhodes 943135446Strhodes if (addr_bucket != DNS_ADB_INVALIDBUCKET) 944135446Strhodes UNLOCK(&adb->entrylocks[addr_bucket]); 945135446Strhodes return (result); 946135446Strhodes} 947135446Strhodes 948135446Strhodesstatic void 949135446Strhodesclean_target(dns_adb_t *adb, dns_name_t *target) { 950135446Strhodes if (dns_name_countlabels(target) > 0) { 951135446Strhodes dns_name_free(target, adb->mctx); 952135446Strhodes dns_name_init(target, NULL); 953135446Strhodes } 954135446Strhodes} 955135446Strhodes 956135446Strhodesstatic isc_result_t 957135446Strhodesset_target(dns_adb_t *adb, dns_name_t *name, dns_name_t *fname, 958135446Strhodes dns_rdataset_t *rdataset, dns_name_t *target) 959135446Strhodes{ 960135446Strhodes isc_result_t result; 961135446Strhodes dns_namereln_t namereln; 962135446Strhodes unsigned int nlabels; 963135446Strhodes int order; 964135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 965135446Strhodes dns_fixedname_t fixed1, fixed2; 966135446Strhodes dns_name_t *prefix, *new_target; 967135446Strhodes 968135446Strhodes REQUIRE(dns_name_countlabels(target) == 0); 969135446Strhodes 970135446Strhodes if (rdataset->type == dns_rdatatype_cname) { 971135446Strhodes dns_rdata_cname_t cname; 972135446Strhodes 973135446Strhodes /* 974135446Strhodes * Copy the CNAME's target into the target name. 975135446Strhodes */ 976135446Strhodes result = dns_rdataset_first(rdataset); 977135446Strhodes if (result != ISC_R_SUCCESS) 978135446Strhodes return (result); 979135446Strhodes dns_rdataset_current(rdataset, &rdata); 980135446Strhodes result = dns_rdata_tostruct(&rdata, &cname, NULL); 981135446Strhodes if (result != ISC_R_SUCCESS) 982135446Strhodes return (result); 983135446Strhodes result = dns_name_dup(&cname.cname, adb->mctx, target); 984135446Strhodes dns_rdata_freestruct(&cname); 985135446Strhodes if (result != ISC_R_SUCCESS) 986135446Strhodes return (result); 987135446Strhodes } else { 988135446Strhodes dns_rdata_dname_t dname; 989135446Strhodes 990135446Strhodes INSIST(rdataset->type == dns_rdatatype_dname); 991135446Strhodes namereln = dns_name_fullcompare(name, fname, &order, &nlabels); 992135446Strhodes INSIST(namereln == dns_namereln_subdomain); 993135446Strhodes /* 994135446Strhodes * Get the target name of the DNAME. 995135446Strhodes */ 996135446Strhodes result = dns_rdataset_first(rdataset); 997135446Strhodes if (result != ISC_R_SUCCESS) 998135446Strhodes return (result); 999135446Strhodes dns_rdataset_current(rdataset, &rdata); 1000135446Strhodes result = dns_rdata_tostruct(&rdata, &dname, NULL); 1001135446Strhodes if (result != ISC_R_SUCCESS) 1002135446Strhodes return (result); 1003135446Strhodes /* 1004135446Strhodes * Construct the new target name. 1005135446Strhodes */ 1006135446Strhodes dns_fixedname_init(&fixed1); 1007135446Strhodes prefix = dns_fixedname_name(&fixed1); 1008135446Strhodes dns_fixedname_init(&fixed2); 1009135446Strhodes new_target = dns_fixedname_name(&fixed2); 1010135446Strhodes dns_name_split(name, nlabels, prefix, NULL); 1011135446Strhodes result = dns_name_concatenate(prefix, &dname.dname, new_target, 1012135446Strhodes NULL); 1013135446Strhodes dns_rdata_freestruct(&dname); 1014135446Strhodes if (result != ISC_R_SUCCESS) 1015135446Strhodes return (result); 1016135446Strhodes result = dns_name_dup(new_target, adb->mctx, target); 1017135446Strhodes if (result != ISC_R_SUCCESS) 1018135446Strhodes return (result); 1019135446Strhodes } 1020135446Strhodes 1021135446Strhodes return (ISC_R_SUCCESS); 1022135446Strhodes} 1023135446Strhodes 1024135446Strhodes/* 1025135446Strhodes * Assumes nothing is locked, since this is called by the client. 1026135446Strhodes */ 1027135446Strhodesstatic void 1028135446Strhodesevent_free(isc_event_t *event) { 1029135446Strhodes dns_adbfind_t *find; 1030135446Strhodes 1031135446Strhodes INSIST(event != NULL); 1032135446Strhodes find = event->ev_destroy_arg; 1033135446Strhodes INSIST(DNS_ADBFIND_VALID(find)); 1034135446Strhodes 1035135446Strhodes LOCK(&find->lock); 1036135446Strhodes find->flags |= FIND_EVENT_FREED; 1037135446Strhodes event->ev_destroy_arg = NULL; 1038135446Strhodes UNLOCK(&find->lock); 1039135446Strhodes} 1040135446Strhodes 1041135446Strhodes/* 1042135446Strhodes * Assumes the name bucket is locked. 1043135446Strhodes */ 1044135446Strhodesstatic void 1045135446Strhodesclean_finds_at_name(dns_adbname_t *name, isc_eventtype_t evtype, 1046135446Strhodes unsigned int addrs) 1047135446Strhodes{ 1048135446Strhodes isc_event_t *ev; 1049135446Strhodes isc_task_t *task; 1050135446Strhodes dns_adbfind_t *find; 1051135446Strhodes dns_adbfind_t *next_find; 1052135446Strhodes isc_boolean_t process; 1053135446Strhodes unsigned int wanted, notify; 1054135446Strhodes 1055135446Strhodes DP(ENTER_LEVEL, 1056135446Strhodes "ENTER clean_finds_at_name, name %p, evtype %08x, addrs %08x", 1057135446Strhodes name, evtype, addrs); 1058135446Strhodes 1059135446Strhodes find = ISC_LIST_HEAD(name->finds); 1060135446Strhodes while (find != NULL) { 1061135446Strhodes LOCK(&find->lock); 1062135446Strhodes next_find = ISC_LIST_NEXT(find, plink); 1063135446Strhodes 1064135446Strhodes process = ISC_FALSE; 1065135446Strhodes wanted = find->flags & DNS_ADBFIND_ADDRESSMASK; 1066135446Strhodes notify = wanted & addrs; 1067135446Strhodes 1068135446Strhodes switch (evtype) { 1069135446Strhodes case DNS_EVENT_ADBMOREADDRESSES: 1070135446Strhodes DP(ISC_LOG_DEBUG(3), "DNS_EVENT_ADBMOREADDRESSES"); 1071135446Strhodes if ((notify) != 0) { 1072135446Strhodes find->flags &= ~addrs; 1073135446Strhodes process = ISC_TRUE; 1074135446Strhodes } 1075135446Strhodes break; 1076135446Strhodes case DNS_EVENT_ADBNOMOREADDRESSES: 1077135446Strhodes DP(ISC_LOG_DEBUG(3), "DNS_EVENT_ADBNOMOREADDRESSES"); 1078135446Strhodes find->flags &= ~addrs; 1079135446Strhodes wanted = find->flags & DNS_ADBFIND_ADDRESSMASK; 1080135446Strhodes if (wanted == 0) 1081135446Strhodes process = ISC_TRUE; 1082135446Strhodes break; 1083135446Strhodes default: 1084135446Strhodes find->flags &= ~addrs; 1085135446Strhodes process = ISC_TRUE; 1086135446Strhodes } 1087135446Strhodes 1088135446Strhodes if (process) { 1089135446Strhodes DP(DEF_LEVEL, "cfan: processing find %p", find); 1090135446Strhodes /* 1091135446Strhodes * Unlink the find from the name, letting the caller 1092135446Strhodes * call dns_adb_destroyfind() on it to clean it up 1093135446Strhodes * later. 1094135446Strhodes */ 1095135446Strhodes ISC_LIST_UNLINK(name->finds, find, plink); 1096135446Strhodes find->adbname = NULL; 1097135446Strhodes find->name_bucket = DNS_ADB_INVALIDBUCKET; 1098135446Strhodes 1099135446Strhodes INSIST(!FIND_EVENTSENT(find)); 1100135446Strhodes 1101135446Strhodes ev = &find->event; 1102135446Strhodes task = ev->ev_sender; 1103135446Strhodes ev->ev_sender = find; 1104135446Strhodes find->result_v4 = find_err_map[name->fetch_err]; 1105135446Strhodes find->result_v6 = find_err_map[name->fetch6_err]; 1106135446Strhodes ev->ev_type = evtype; 1107135446Strhodes ev->ev_destroy = event_free; 1108135446Strhodes ev->ev_destroy_arg = find; 1109135446Strhodes 1110135446Strhodes DP(DEF_LEVEL, 1111135446Strhodes "sending event %p to task %p for find %p", 1112135446Strhodes ev, task, find); 1113135446Strhodes 1114135446Strhodes isc_task_sendanddetach(&task, (isc_event_t **)&ev); 1115135446Strhodes } else { 1116135446Strhodes DP(DEF_LEVEL, "cfan: skipping find %p", find); 1117135446Strhodes } 1118135446Strhodes 1119135446Strhodes UNLOCK(&find->lock); 1120135446Strhodes find = next_find; 1121135446Strhodes } 1122135446Strhodes 1123135446Strhodes DP(ENTER_LEVEL, "EXIT clean_finds_at_name, name %p", name); 1124135446Strhodes} 1125135446Strhodes 1126135446Strhodesstatic inline void 1127135446Strhodescheck_exit(dns_adb_t *adb) { 1128135446Strhodes isc_event_t *event; 1129135446Strhodes /* 1130135446Strhodes * The caller must be holding the adb lock. 1131135446Strhodes */ 1132135446Strhodes if (adb->shutting_down) { 1133135446Strhodes /* 1134135446Strhodes * If there aren't any external references either, we're 1135135446Strhodes * done. Send the control event to initiate shutdown. 1136135446Strhodes */ 1137135446Strhodes INSIST(!adb->cevent_sent); /* Sanity check. */ 1138135446Strhodes event = &adb->cevent; 1139135446Strhodes isc_task_send(adb->task, &event); 1140135446Strhodes adb->cevent_sent = ISC_TRUE; 1141135446Strhodes } 1142135446Strhodes} 1143135446Strhodes 1144135446Strhodesstatic inline isc_boolean_t 1145135446Strhodesdec_adb_irefcnt(dns_adb_t *adb) { 1146135446Strhodes isc_event_t *event; 1147135446Strhodes isc_task_t *etask; 1148135446Strhodes isc_boolean_t result = ISC_FALSE; 1149135446Strhodes 1150135446Strhodes LOCK(&adb->reflock); 1151135446Strhodes 1152135446Strhodes INSIST(adb->irefcnt > 0); 1153135446Strhodes adb->irefcnt--; 1154135446Strhodes 1155135446Strhodes if (adb->irefcnt == 0) { 1156135446Strhodes event = ISC_LIST_HEAD(adb->whenshutdown); 1157135446Strhodes while (event != NULL) { 1158135446Strhodes ISC_LIST_UNLINK(adb->whenshutdown, event, ev_link); 1159135446Strhodes etask = event->ev_sender; 1160135446Strhodes event->ev_sender = adb; 1161135446Strhodes isc_task_sendanddetach(&etask, &event); 1162135446Strhodes event = ISC_LIST_HEAD(adb->whenshutdown); 1163135446Strhodes } 1164135446Strhodes } 1165135446Strhodes 1166135446Strhodes if (adb->irefcnt == 0 && adb->erefcnt == 0) 1167135446Strhodes result = ISC_TRUE; 1168135446Strhodes UNLOCK(&adb->reflock); 1169135446Strhodes return (result); 1170135446Strhodes} 1171135446Strhodes 1172135446Strhodesstatic inline void 1173135446Strhodesinc_adb_irefcnt(dns_adb_t *adb) { 1174135446Strhodes LOCK(&adb->reflock); 1175135446Strhodes adb->irefcnt++; 1176135446Strhodes UNLOCK(&adb->reflock); 1177135446Strhodes} 1178135446Strhodes 1179135446Strhodesstatic inline void 1180135446Strhodesinc_adb_erefcnt(dns_adb_t *adb) { 1181135446Strhodes LOCK(&adb->reflock); 1182135446Strhodes adb->erefcnt++; 1183135446Strhodes UNLOCK(&adb->reflock); 1184135446Strhodes} 1185135446Strhodes 1186135446Strhodesstatic inline void 1187135446Strhodesinc_entry_refcnt(dns_adb_t *adb, dns_adbentry_t *entry, isc_boolean_t lock) { 1188135446Strhodes int bucket; 1189135446Strhodes 1190135446Strhodes bucket = entry->lock_bucket; 1191135446Strhodes 1192135446Strhodes if (lock) 1193135446Strhodes LOCK(&adb->entrylocks[bucket]); 1194135446Strhodes 1195135446Strhodes entry->refcnt++; 1196135446Strhodes 1197135446Strhodes if (lock) 1198135446Strhodes UNLOCK(&adb->entrylocks[bucket]); 1199135446Strhodes} 1200135446Strhodes 1201135446Strhodesstatic inline isc_boolean_t 1202135446Strhodesdec_entry_refcnt(dns_adb_t *adb, dns_adbentry_t *entry, isc_boolean_t lock) { 1203135446Strhodes int bucket; 1204135446Strhodes isc_boolean_t destroy_entry; 1205135446Strhodes isc_boolean_t result = ISC_FALSE; 1206135446Strhodes 1207135446Strhodes bucket = entry->lock_bucket; 1208135446Strhodes 1209135446Strhodes if (lock) 1210135446Strhodes LOCK(&adb->entrylocks[bucket]); 1211135446Strhodes 1212135446Strhodes INSIST(entry->refcnt > 0); 1213135446Strhodes entry->refcnt--; 1214135446Strhodes 1215135446Strhodes destroy_entry = ISC_FALSE; 1216135446Strhodes if (entry->refcnt == 0 && 1217135446Strhodes (adb->entry_sd[bucket] || entry->expires == 0)) { 1218135446Strhodes destroy_entry = ISC_TRUE; 1219135446Strhodes result = unlink_entry(adb, entry); 1220135446Strhodes } 1221135446Strhodes 1222135446Strhodes if (lock) 1223135446Strhodes UNLOCK(&adb->entrylocks[bucket]); 1224135446Strhodes 1225135446Strhodes if (!destroy_entry) 1226135446Strhodes return (result); 1227135446Strhodes 1228135446Strhodes entry->lock_bucket = DNS_ADB_INVALIDBUCKET; 1229135446Strhodes 1230135446Strhodes free_adbentry(adb, &entry); 1231135446Strhodes if (result) 1232135446Strhodes result =dec_adb_irefcnt(adb); 1233135446Strhodes 1234135446Strhodes return (result); 1235135446Strhodes} 1236135446Strhodes 1237135446Strhodesstatic inline dns_adbname_t * 1238135446Strhodesnew_adbname(dns_adb_t *adb, dns_name_t *dnsname) { 1239135446Strhodes dns_adbname_t *name; 1240135446Strhodes 1241135446Strhodes name = isc_mempool_get(adb->nmp); 1242135446Strhodes if (name == NULL) 1243135446Strhodes return (NULL); 1244135446Strhodes 1245135446Strhodes dns_name_init(&name->name, NULL); 1246135446Strhodes if (dns_name_dup(dnsname, adb->mctx, &name->name) != ISC_R_SUCCESS) { 1247135446Strhodes isc_mempool_put(adb->nmp, name); 1248135446Strhodes return (NULL); 1249135446Strhodes } 1250135446Strhodes dns_name_init(&name->target, NULL); 1251135446Strhodes name->magic = DNS_ADBNAME_MAGIC; 1252135446Strhodes name->adb = adb; 1253135446Strhodes name->partial_result = 0; 1254135446Strhodes name->flags = 0; 1255135446Strhodes name->expire_v4 = INT_MAX; 1256135446Strhodes name->expire_v6 = INT_MAX; 1257135446Strhodes name->expire_target = INT_MAX; 1258135446Strhodes name->chains = 0; 1259135446Strhodes name->lock_bucket = DNS_ADB_INVALIDBUCKET; 1260135446Strhodes ISC_LIST_INIT(name->v4); 1261135446Strhodes ISC_LIST_INIT(name->v6); 1262135446Strhodes name->fetch_a = NULL; 1263135446Strhodes name->fetch_aaaa = NULL; 1264135446Strhodes name->fetch_err = FIND_ERR_UNEXPECTED; 1265135446Strhodes name->fetch6_err = FIND_ERR_UNEXPECTED; 1266135446Strhodes ISC_LIST_INIT(name->finds); 1267135446Strhodes ISC_LINK_INIT(name, plink); 1268135446Strhodes 1269135446Strhodes return (name); 1270135446Strhodes} 1271135446Strhodes 1272135446Strhodesstatic inline void 1273135446Strhodesfree_adbname(dns_adb_t *adb, dns_adbname_t **name) { 1274135446Strhodes dns_adbname_t *n; 1275135446Strhodes 1276135446Strhodes INSIST(name != NULL && DNS_ADBNAME_VALID(*name)); 1277135446Strhodes n = *name; 1278135446Strhodes *name = NULL; 1279135446Strhodes 1280135446Strhodes INSIST(!NAME_HAS_V4(n)); 1281135446Strhodes INSIST(!NAME_HAS_V6(n)); 1282135446Strhodes INSIST(!NAME_FETCH(n)); 1283135446Strhodes INSIST(ISC_LIST_EMPTY(n->finds)); 1284135446Strhodes INSIST(!ISC_LINK_LINKED(n, plink)); 1285135446Strhodes INSIST(n->lock_bucket == DNS_ADB_INVALIDBUCKET); 1286135446Strhodes INSIST(n->adb == adb); 1287135446Strhodes 1288135446Strhodes n->magic = 0; 1289135446Strhodes dns_name_free(&n->name, adb->mctx); 1290135446Strhodes 1291135446Strhodes isc_mempool_put(adb->nmp, n); 1292135446Strhodes} 1293135446Strhodes 1294135446Strhodesstatic inline dns_adbnamehook_t * 1295135446Strhodesnew_adbnamehook(dns_adb_t *adb, dns_adbentry_t *entry) { 1296135446Strhodes dns_adbnamehook_t *nh; 1297135446Strhodes 1298135446Strhodes nh = isc_mempool_get(adb->nhmp); 1299135446Strhodes if (nh == NULL) 1300135446Strhodes return (NULL); 1301135446Strhodes 1302135446Strhodes nh->magic = DNS_ADBNAMEHOOK_MAGIC; 1303135446Strhodes nh->entry = entry; 1304135446Strhodes ISC_LINK_INIT(nh, plink); 1305135446Strhodes 1306135446Strhodes return (nh); 1307135446Strhodes} 1308135446Strhodes 1309135446Strhodesstatic inline void 1310135446Strhodesfree_adbnamehook(dns_adb_t *adb, dns_adbnamehook_t **namehook) { 1311135446Strhodes dns_adbnamehook_t *nh; 1312135446Strhodes 1313135446Strhodes INSIST(namehook != NULL && DNS_ADBNAMEHOOK_VALID(*namehook)); 1314135446Strhodes nh = *namehook; 1315135446Strhodes *namehook = NULL; 1316135446Strhodes 1317135446Strhodes INSIST(nh->entry == NULL); 1318135446Strhodes INSIST(!ISC_LINK_LINKED(nh, plink)); 1319135446Strhodes 1320135446Strhodes nh->magic = 0; 1321135446Strhodes isc_mempool_put(adb->nhmp, nh); 1322135446Strhodes} 1323135446Strhodes 1324135446Strhodesstatic inline dns_adbzoneinfo_t * 1325135446Strhodesnew_adbzoneinfo(dns_adb_t *adb, dns_name_t *zone) { 1326135446Strhodes dns_adbzoneinfo_t *zi; 1327135446Strhodes 1328135446Strhodes zi = isc_mempool_get(adb->zimp); 1329135446Strhodes if (zi == NULL) 1330135446Strhodes return (NULL); 1331135446Strhodes 1332135446Strhodes dns_name_init(&zi->zone, NULL); 1333135446Strhodes if (dns_name_dup(zone, adb->mctx, &zi->zone) != ISC_R_SUCCESS) { 1334135446Strhodes isc_mempool_put(adb->zimp, zi); 1335135446Strhodes return (NULL); 1336135446Strhodes } 1337135446Strhodes 1338135446Strhodes zi->magic = DNS_ADBZONEINFO_MAGIC; 1339135446Strhodes zi->lame_timer = 0; 1340135446Strhodes ISC_LINK_INIT(zi, plink); 1341135446Strhodes 1342135446Strhodes return (zi); 1343135446Strhodes} 1344135446Strhodes 1345135446Strhodesstatic inline void 1346135446Strhodesfree_adbzoneinfo(dns_adb_t *adb, dns_adbzoneinfo_t **zoneinfo) { 1347135446Strhodes dns_adbzoneinfo_t *zi; 1348135446Strhodes 1349135446Strhodes INSIST(zoneinfo != NULL && DNS_ADBZONEINFO_VALID(*zoneinfo)); 1350135446Strhodes zi = *zoneinfo; 1351135446Strhodes *zoneinfo = NULL; 1352135446Strhodes 1353135446Strhodes INSIST(!ISC_LINK_LINKED(zi, plink)); 1354135446Strhodes 1355135446Strhodes dns_name_free(&zi->zone, adb->mctx); 1356135446Strhodes 1357135446Strhodes zi->magic = 0; 1358135446Strhodes 1359135446Strhodes isc_mempool_put(adb->zimp, zi); 1360135446Strhodes} 1361135446Strhodes 1362135446Strhodesstatic inline dns_adbentry_t * 1363135446Strhodesnew_adbentry(dns_adb_t *adb) { 1364135446Strhodes dns_adbentry_t *e; 1365135446Strhodes isc_uint32_t r; 1366135446Strhodes 1367135446Strhodes e = isc_mempool_get(adb->emp); 1368135446Strhodes if (e == NULL) 1369135446Strhodes return (NULL); 1370135446Strhodes 1371135446Strhodes e->magic = DNS_ADBENTRY_MAGIC; 1372135446Strhodes e->lock_bucket = DNS_ADB_INVALIDBUCKET; 1373135446Strhodes e->refcnt = 0; 1374135446Strhodes e->flags = 0; 1375135446Strhodes isc_random_get(&r); 1376135446Strhodes e->srtt = (r & 0x1f) + 1; 1377135446Strhodes e->expires = 0; 1378135446Strhodes ISC_LIST_INIT(e->zoneinfo); 1379135446Strhodes ISC_LINK_INIT(e, plink); 1380135446Strhodes 1381135446Strhodes return (e); 1382135446Strhodes} 1383135446Strhodes 1384135446Strhodesstatic inline void 1385135446Strhodesfree_adbentry(dns_adb_t *adb, dns_adbentry_t **entry) { 1386135446Strhodes dns_adbentry_t *e; 1387135446Strhodes dns_adbzoneinfo_t *zi; 1388135446Strhodes 1389135446Strhodes INSIST(entry != NULL && DNS_ADBENTRY_VALID(*entry)); 1390135446Strhodes e = *entry; 1391135446Strhodes *entry = NULL; 1392135446Strhodes 1393135446Strhodes INSIST(e->lock_bucket == DNS_ADB_INVALIDBUCKET); 1394135446Strhodes INSIST(e->refcnt == 0); 1395135446Strhodes INSIST(!ISC_LINK_LINKED(e, plink)); 1396135446Strhodes 1397135446Strhodes e->magic = 0; 1398135446Strhodes 1399135446Strhodes zi = ISC_LIST_HEAD(e->zoneinfo); 1400135446Strhodes while (zi != NULL) { 1401135446Strhodes ISC_LIST_UNLINK(e->zoneinfo, zi, plink); 1402135446Strhodes free_adbzoneinfo(adb, &zi); 1403135446Strhodes zi = ISC_LIST_HEAD(e->zoneinfo); 1404135446Strhodes } 1405135446Strhodes 1406135446Strhodes isc_mempool_put(adb->emp, e); 1407135446Strhodes} 1408135446Strhodes 1409135446Strhodesstatic inline dns_adbfind_t * 1410135446Strhodesnew_adbfind(dns_adb_t *adb) { 1411135446Strhodes dns_adbfind_t *h; 1412135446Strhodes isc_result_t result; 1413135446Strhodes 1414135446Strhodes h = isc_mempool_get(adb->ahmp); 1415135446Strhodes if (h == NULL) 1416135446Strhodes return (NULL); 1417135446Strhodes 1418135446Strhodes /* 1419135446Strhodes * Public members. 1420135446Strhodes */ 1421135446Strhodes h->magic = 0; 1422135446Strhodes h->adb = adb; 1423135446Strhodes h->partial_result = 0; 1424135446Strhodes h->options = 0; 1425135446Strhodes h->flags = 0; 1426135446Strhodes h->result_v4 = ISC_R_UNEXPECTED; 1427135446Strhodes h->result_v6 = ISC_R_UNEXPECTED; 1428135446Strhodes ISC_LINK_INIT(h, publink); 1429135446Strhodes ISC_LINK_INIT(h, plink); 1430135446Strhodes ISC_LIST_INIT(h->list); 1431135446Strhodes h->adbname = NULL; 1432135446Strhodes h->name_bucket = DNS_ADB_INVALIDBUCKET; 1433135446Strhodes 1434135446Strhodes /* 1435135446Strhodes * private members 1436135446Strhodes */ 1437135446Strhodes result = isc_mutex_init(&h->lock); 1438135446Strhodes if (result != ISC_R_SUCCESS) { 1439135446Strhodes UNEXPECTED_ERROR(__FILE__, __LINE__, 1440135446Strhodes "isc_mutex_init failed in new_adbfind()"); 1441135446Strhodes isc_mempool_put(adb->ahmp, h); 1442135446Strhodes return (NULL); 1443135446Strhodes } 1444135446Strhodes 1445135446Strhodes ISC_EVENT_INIT(&h->event, sizeof(isc_event_t), 0, 0, 0, NULL, NULL, 1446135446Strhodes NULL, NULL, h); 1447135446Strhodes 1448135446Strhodes inc_adb_irefcnt(adb); 1449135446Strhodes h->magic = DNS_ADBFIND_MAGIC; 1450135446Strhodes return (h); 1451135446Strhodes} 1452135446Strhodes 1453135446Strhodesstatic inline dns_adbfetch_t * 1454135446Strhodesnew_adbfetch(dns_adb_t *adb) { 1455135446Strhodes dns_adbfetch_t *f; 1456135446Strhodes 1457135446Strhodes f = isc_mempool_get(adb->afmp); 1458135446Strhodes if (f == NULL) 1459135446Strhodes return (NULL); 1460135446Strhodes 1461135446Strhodes f->magic = 0; 1462135446Strhodes f->namehook = NULL; 1463135446Strhodes f->entry = NULL; 1464135446Strhodes f->fetch = NULL; 1465135446Strhodes 1466135446Strhodes f->namehook = new_adbnamehook(adb, NULL); 1467135446Strhodes if (f->namehook == NULL) 1468135446Strhodes goto err; 1469135446Strhodes 1470135446Strhodes f->entry = new_adbentry(adb); 1471135446Strhodes if (f->entry == NULL) 1472135446Strhodes goto err; 1473135446Strhodes 1474135446Strhodes dns_rdataset_init(&f->rdataset); 1475135446Strhodes 1476135446Strhodes f->magic = DNS_ADBFETCH_MAGIC; 1477135446Strhodes 1478135446Strhodes return (f); 1479135446Strhodes 1480135446Strhodes err: 1481135446Strhodes if (f->namehook != NULL) 1482135446Strhodes free_adbnamehook(adb, &f->namehook); 1483135446Strhodes if (f->entry != NULL) 1484135446Strhodes free_adbentry(adb, &f->entry); 1485135446Strhodes isc_mempool_put(adb->afmp, f); 1486135446Strhodes return (NULL); 1487135446Strhodes} 1488135446Strhodes 1489135446Strhodesstatic inline void 1490135446Strhodesfree_adbfetch(dns_adb_t *adb, dns_adbfetch_t **fetch) { 1491135446Strhodes dns_adbfetch_t *f; 1492135446Strhodes 1493135446Strhodes INSIST(fetch != NULL && DNS_ADBFETCH_VALID(*fetch)); 1494135446Strhodes f = *fetch; 1495135446Strhodes *fetch = NULL; 1496135446Strhodes 1497135446Strhodes f->magic = 0; 1498135446Strhodes 1499135446Strhodes if (f->namehook != NULL) 1500135446Strhodes free_adbnamehook(adb, &f->namehook); 1501135446Strhodes if (f->entry != NULL) 1502135446Strhodes free_adbentry(adb, &f->entry); 1503135446Strhodes 1504135446Strhodes if (dns_rdataset_isassociated(&f->rdataset)) 1505135446Strhodes dns_rdataset_disassociate(&f->rdataset); 1506135446Strhodes 1507135446Strhodes isc_mempool_put(adb->afmp, f); 1508135446Strhodes} 1509135446Strhodes 1510135446Strhodesstatic inline isc_boolean_t 1511135446Strhodesfree_adbfind(dns_adb_t *adb, dns_adbfind_t **findp) { 1512135446Strhodes dns_adbfind_t *find; 1513135446Strhodes 1514135446Strhodes INSIST(findp != NULL && DNS_ADBFIND_VALID(*findp)); 1515135446Strhodes find = *findp; 1516135446Strhodes *findp = NULL; 1517135446Strhodes 1518135446Strhodes INSIST(!FIND_HAS_ADDRS(find)); 1519135446Strhodes INSIST(!ISC_LINK_LINKED(find, publink)); 1520135446Strhodes INSIST(!ISC_LINK_LINKED(find, plink)); 1521135446Strhodes INSIST(find->name_bucket == DNS_ADB_INVALIDBUCKET); 1522135446Strhodes INSIST(find->adbname == NULL); 1523135446Strhodes 1524135446Strhodes find->magic = 0; 1525135446Strhodes 1526135446Strhodes DESTROYLOCK(&find->lock); 1527135446Strhodes isc_mempool_put(adb->ahmp, find); 1528135446Strhodes return (dec_adb_irefcnt(adb)); 1529135446Strhodes} 1530135446Strhodes 1531135446Strhodes/* 1532135446Strhodes * Copy bits from the entry into the newly allocated addrinfo. The entry 1533135446Strhodes * must be locked, and the reference count must be bumped up by one 1534135446Strhodes * if this function returns a valid pointer. 1535135446Strhodes */ 1536135446Strhodesstatic inline dns_adbaddrinfo_t * 1537135446Strhodesnew_adbaddrinfo(dns_adb_t *adb, dns_adbentry_t *entry, in_port_t port) { 1538135446Strhodes dns_adbaddrinfo_t *ai; 1539135446Strhodes 1540135446Strhodes ai = isc_mempool_get(adb->aimp); 1541135446Strhodes if (ai == NULL) 1542135446Strhodes return (NULL); 1543135446Strhodes 1544135446Strhodes ai->magic = DNS_ADBADDRINFO_MAGIC; 1545135446Strhodes ai->sockaddr = entry->sockaddr; 1546135446Strhodes isc_sockaddr_setport(&ai->sockaddr, port); 1547135446Strhodes ai->srtt = entry->srtt; 1548135446Strhodes ai->flags = entry->flags; 1549135446Strhodes ai->entry = entry; 1550135446Strhodes ISC_LINK_INIT(ai, publink); 1551135446Strhodes 1552135446Strhodes return (ai); 1553135446Strhodes} 1554135446Strhodes 1555135446Strhodesstatic inline void 1556135446Strhodesfree_adbaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **ainfo) { 1557135446Strhodes dns_adbaddrinfo_t *ai; 1558135446Strhodes 1559135446Strhodes INSIST(ainfo != NULL && DNS_ADBADDRINFO_VALID(*ainfo)); 1560135446Strhodes ai = *ainfo; 1561135446Strhodes *ainfo = NULL; 1562135446Strhodes 1563135446Strhodes INSIST(ai->entry == NULL); 1564135446Strhodes INSIST(!ISC_LINK_LINKED(ai, publink)); 1565135446Strhodes 1566135446Strhodes ai->magic = 0; 1567135446Strhodes 1568135446Strhodes isc_mempool_put(adb->aimp, ai); 1569135446Strhodes} 1570135446Strhodes 1571135446Strhodes/* 1572135446Strhodes * Search for the name. NOTE: The bucket is kept locked on both 1573135446Strhodes * success and failure, so it must always be unlocked by the caller! 1574135446Strhodes * 1575135446Strhodes * On the first call to this function, *bucketp must be set to 1576135446Strhodes * DNS_ADB_INVALIDBUCKET. 1577135446Strhodes */ 1578135446Strhodesstatic inline dns_adbname_t * 1579135446Strhodesfind_name_and_lock(dns_adb_t *adb, dns_name_t *name, 1580135446Strhodes unsigned int options, int *bucketp) 1581135446Strhodes{ 1582135446Strhodes dns_adbname_t *adbname; 1583135446Strhodes int bucket; 1584135446Strhodes 1585135446Strhodes bucket = dns_name_fullhash(name, ISC_FALSE) % NBUCKETS; 1586135446Strhodes 1587135446Strhodes if (*bucketp == DNS_ADB_INVALIDBUCKET) { 1588135446Strhodes LOCK(&adb->namelocks[bucket]); 1589135446Strhodes *bucketp = bucket; 1590135446Strhodes } else if (*bucketp != bucket) { 1591135446Strhodes UNLOCK(&adb->namelocks[*bucketp]); 1592135446Strhodes LOCK(&adb->namelocks[bucket]); 1593135446Strhodes *bucketp = bucket; 1594135446Strhodes } 1595135446Strhodes 1596135446Strhodes adbname = ISC_LIST_HEAD(adb->names[bucket]); 1597135446Strhodes while (adbname != NULL) { 1598135446Strhodes if (!NAME_DEAD(adbname)) { 1599135446Strhodes if (dns_name_equal(name, &adbname->name) 1600135446Strhodes && GLUEHINT_OK(adbname, options) 1601135446Strhodes && STARTATZONE_MATCHES(adbname, options)) 1602135446Strhodes return (adbname); 1603135446Strhodes } 1604135446Strhodes adbname = ISC_LIST_NEXT(adbname, plink); 1605135446Strhodes } 1606135446Strhodes 1607135446Strhodes return (NULL); 1608135446Strhodes} 1609135446Strhodes 1610135446Strhodes/* 1611135446Strhodes * Search for the address. NOTE: The bucket is kept locked on both 1612135446Strhodes * success and failure, so it must always be unlocked by the caller. 1613135446Strhodes * 1614135446Strhodes * On the first call to this function, *bucketp must be set to 1615135446Strhodes * DNS_ADB_INVALIDBUCKET. This will cause a lock to occur. On 1616135446Strhodes * later calls (within the same "lock path") it can be left alone, so 1617135446Strhodes * if this function is called multiple times locking is only done if 1618135446Strhodes * the bucket changes. 1619135446Strhodes */ 1620135446Strhodesstatic inline dns_adbentry_t * 1621135446Strhodesfind_entry_and_lock(dns_adb_t *adb, isc_sockaddr_t *addr, int *bucketp) { 1622135446Strhodes dns_adbentry_t *entry; 1623135446Strhodes int bucket; 1624135446Strhodes 1625135446Strhodes bucket = isc_sockaddr_hash(addr, ISC_TRUE) % NBUCKETS; 1626135446Strhodes 1627135446Strhodes if (*bucketp == DNS_ADB_INVALIDBUCKET) { 1628135446Strhodes LOCK(&adb->entrylocks[bucket]); 1629135446Strhodes *bucketp = bucket; 1630135446Strhodes } else if (*bucketp != bucket) { 1631135446Strhodes UNLOCK(&adb->entrylocks[*bucketp]); 1632135446Strhodes LOCK(&adb->entrylocks[bucket]); 1633135446Strhodes *bucketp = bucket; 1634135446Strhodes } 1635135446Strhodes 1636135446Strhodes entry = ISC_LIST_HEAD(adb->entries[bucket]); 1637135446Strhodes while (entry != NULL) { 1638135446Strhodes if (isc_sockaddr_equal(addr, &entry->sockaddr)) 1639135446Strhodes return (entry); 1640135446Strhodes entry = ISC_LIST_NEXT(entry, plink); 1641135446Strhodes } 1642135446Strhodes 1643135446Strhodes return (NULL); 1644135446Strhodes} 1645135446Strhodes 1646135446Strhodes/* 1647135446Strhodes * Entry bucket MUST be locked! 1648135446Strhodes */ 1649135446Strhodesstatic isc_boolean_t 1650135446Strhodesentry_is_bad_for_zone(dns_adb_t *adb, dns_adbentry_t *entry, dns_name_t *zone, 1651135446Strhodes isc_stdtime_t now) 1652135446Strhodes{ 1653135446Strhodes dns_adbzoneinfo_t *zi, *next_zi; 1654135446Strhodes isc_boolean_t is_bad; 1655135446Strhodes 1656135446Strhodes is_bad = ISC_FALSE; 1657135446Strhodes 1658135446Strhodes zi = ISC_LIST_HEAD(entry->zoneinfo); 1659135446Strhodes if (zi == NULL) 1660135446Strhodes return (ISC_FALSE); 1661135446Strhodes while (zi != NULL) { 1662135446Strhodes next_zi = ISC_LIST_NEXT(zi, plink); 1663135446Strhodes 1664135446Strhodes /* 1665135446Strhodes * Has the entry expired? 1666135446Strhodes */ 1667135446Strhodes if (zi->lame_timer < now) { 1668135446Strhodes ISC_LIST_UNLINK(entry->zoneinfo, zi, plink); 1669135446Strhodes free_adbzoneinfo(adb, &zi); 1670135446Strhodes } 1671135446Strhodes 1672135446Strhodes /* 1673135446Strhodes * Order tests from least to most expensive. 1674135446Strhodes */ 1675135446Strhodes if (zi != NULL && !is_bad) { 1676135446Strhodes if (dns_name_equal(zone, &zi->zone)) 1677135446Strhodes is_bad = ISC_TRUE; 1678135446Strhodes } 1679135446Strhodes 1680135446Strhodes zi = next_zi; 1681135446Strhodes } 1682135446Strhodes 1683135446Strhodes return (is_bad); 1684135446Strhodes} 1685135446Strhodes 1686135446Strhodesstatic void 1687135446Strhodescopy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, dns_name_t *zone, 1688135446Strhodes dns_adbname_t *name, isc_stdtime_t now) 1689135446Strhodes{ 1690135446Strhodes dns_adbnamehook_t *namehook; 1691135446Strhodes dns_adbaddrinfo_t *addrinfo; 1692135446Strhodes dns_adbentry_t *entry; 1693135446Strhodes int bucket; 1694135446Strhodes 1695135446Strhodes bucket = DNS_ADB_INVALIDBUCKET; 1696135446Strhodes 1697135446Strhodes if (find->options & DNS_ADBFIND_INET) { 1698135446Strhodes namehook = ISC_LIST_HEAD(name->v4); 1699135446Strhodes while (namehook != NULL) { 1700135446Strhodes entry = namehook->entry; 1701135446Strhodes bucket = entry->lock_bucket; 1702135446Strhodes LOCK(&adb->entrylocks[bucket]); 1703135446Strhodes 1704135446Strhodes if (!FIND_RETURNLAME(find) 1705135446Strhodes && entry_is_bad_for_zone(adb, entry, zone, now)) { 1706135446Strhodes find->options |= DNS_ADBFIND_LAMEPRUNED; 1707135446Strhodes goto nextv4; 1708135446Strhodes } 1709135446Strhodes addrinfo = new_adbaddrinfo(adb, entry, find->port); 1710135446Strhodes if (addrinfo == NULL) { 1711135446Strhodes find->partial_result |= DNS_ADBFIND_INET; 1712135446Strhodes goto out; 1713135446Strhodes } 1714135446Strhodes /* 1715135446Strhodes * Found a valid entry. Add it to the find's list. 1716135446Strhodes */ 1717135446Strhodes inc_entry_refcnt(adb, entry, ISC_FALSE); 1718135446Strhodes ISC_LIST_APPEND(find->list, addrinfo, publink); 1719135446Strhodes addrinfo = NULL; 1720135446Strhodes nextv4: 1721135446Strhodes UNLOCK(&adb->entrylocks[bucket]); 1722135446Strhodes bucket = DNS_ADB_INVALIDBUCKET; 1723135446Strhodes namehook = ISC_LIST_NEXT(namehook, plink); 1724135446Strhodes } 1725135446Strhodes } 1726135446Strhodes 1727135446Strhodes if (find->options & DNS_ADBFIND_INET6) { 1728135446Strhodes namehook = ISC_LIST_HEAD(name->v6); 1729135446Strhodes while (namehook != NULL) { 1730135446Strhodes entry = namehook->entry; 1731135446Strhodes bucket = entry->lock_bucket; 1732135446Strhodes LOCK(&adb->entrylocks[bucket]); 1733135446Strhodes 1734135446Strhodes if (entry_is_bad_for_zone(adb, entry, zone, now)) 1735135446Strhodes goto nextv6; 1736135446Strhodes addrinfo = new_adbaddrinfo(adb, entry, find->port); 1737135446Strhodes if (addrinfo == NULL) { 1738135446Strhodes find->partial_result |= DNS_ADBFIND_INET6; 1739135446Strhodes goto out; 1740135446Strhodes } 1741135446Strhodes /* 1742135446Strhodes * Found a valid entry. Add it to the find's list. 1743135446Strhodes */ 1744135446Strhodes inc_entry_refcnt(adb, entry, ISC_FALSE); 1745135446Strhodes ISC_LIST_APPEND(find->list, addrinfo, publink); 1746135446Strhodes addrinfo = NULL; 1747135446Strhodes nextv6: 1748135446Strhodes UNLOCK(&adb->entrylocks[bucket]); 1749135446Strhodes bucket = DNS_ADB_INVALIDBUCKET; 1750135446Strhodes namehook = ISC_LIST_NEXT(namehook, plink); 1751135446Strhodes } 1752135446Strhodes } 1753135446Strhodes 1754135446Strhodes out: 1755135446Strhodes if (bucket != DNS_ADB_INVALIDBUCKET) 1756135446Strhodes UNLOCK(&adb->entrylocks[bucket]); 1757135446Strhodes} 1758135446Strhodes 1759135446Strhodesstatic void 1760135446Strhodesshutdown_task(isc_task_t *task, isc_event_t *ev) { 1761135446Strhodes dns_adb_t *adb; 1762135446Strhodes 1763135446Strhodes UNUSED(task); 1764135446Strhodes 1765135446Strhodes adb = ev->ev_arg; 1766135446Strhodes INSIST(DNS_ADB_VALID(adb)); 1767135446Strhodes 1768135446Strhodes /* 1769135446Strhodes * Kill the timer, and then the ADB itself. Note that this implies 1770135446Strhodes * that this task was the one scheduled to get timer events. If 1771135446Strhodes * this is not true (and it is unfortunate there is no way to INSIST() 1772135446Strhodes * this) badness will occur. 1773135446Strhodes */ 1774135446Strhodes LOCK(&adb->lock); 1775135446Strhodes isc_timer_detach(&adb->timer); 1776135446Strhodes UNLOCK(&adb->lock); 1777135446Strhodes isc_event_free(&ev); 1778135446Strhodes destroy(adb); 1779135446Strhodes} 1780135446Strhodes 1781135446Strhodes/* 1782135446Strhodes * Name bucket must be locked; adb may be locked; no other locks held. 1783135446Strhodes */ 1784135446Strhodesstatic isc_boolean_t 1785135446Strhodescheck_expire_name(dns_adbname_t **namep, isc_stdtime_t now) { 1786135446Strhodes dns_adbname_t *name; 1787153816Sdougb isc_boolean_t result = ISC_FALSE; 1788135446Strhodes 1789135446Strhodes INSIST(namep != NULL && DNS_ADBNAME_VALID(*namep)); 1790135446Strhodes name = *namep; 1791135446Strhodes 1792135446Strhodes if (NAME_HAS_V4(name) || NAME_HAS_V6(name)) 1793135446Strhodes return (result); 1794135446Strhodes if (NAME_FETCH(name)) 1795135446Strhodes return (result); 1796135446Strhodes if (!EXPIRE_OK(name->expire_v4, now)) 1797135446Strhodes return (result); 1798135446Strhodes if (!EXPIRE_OK(name->expire_v6, now)) 1799135446Strhodes return (result); 1800135446Strhodes if (!EXPIRE_OK(name->expire_target, now)) 1801135446Strhodes return (result); 1802135446Strhodes 1803135446Strhodes /* 1804135446Strhodes * The name is empty. Delete it. 1805135446Strhodes */ 1806135446Strhodes result = kill_name(&name, DNS_EVENT_ADBEXPIRED); 1807135446Strhodes *namep = NULL; 1808135446Strhodes 1809135446Strhodes /* 1810135446Strhodes * Our caller, or one of its callers, will be calling check_exit() at 1811135446Strhodes * some point, so we don't need to do it here. 1812135446Strhodes */ 1813135446Strhodes return (result); 1814135446Strhodes} 1815135446Strhodes 1816135446Strhodes/* 1817135446Strhodes * Entry bucket must be locked; adb may be locked; no other locks held. 1818135446Strhodes */ 1819135446Strhodesstatic isc_boolean_t 1820135446Strhodescheck_expire_entry(dns_adb_t *adb, dns_adbentry_t **entryp, isc_stdtime_t now) 1821135446Strhodes{ 1822135446Strhodes dns_adbentry_t *entry; 1823135446Strhodes isc_boolean_t expire; 1824135446Strhodes isc_boolean_t result = ISC_FALSE; 1825135446Strhodes 1826135446Strhodes INSIST(entryp != NULL && DNS_ADBENTRY_VALID(*entryp)); 1827135446Strhodes entry = *entryp; 1828135446Strhodes 1829135446Strhodes if (entry->refcnt != 0) 1830135446Strhodes return (result); 1831135446Strhodes 1832135446Strhodes if (adb->overmem) { 1833135446Strhodes isc_uint32_t val; 1834135446Strhodes 1835135446Strhodes isc_random_get(&val); 1836135446Strhodes 1837135446Strhodes expire = ISC_TF((val % 4) == 0); 1838135446Strhodes } else 1839135446Strhodes expire = ISC_FALSE; 1840135446Strhodes 1841135446Strhodes if (entry->expires == 0 || (! expire && entry->expires > now)) 1842135446Strhodes return (result); 1843135446Strhodes 1844135446Strhodes /* 1845135446Strhodes * The entry is not in use. Delete it. 1846135446Strhodes */ 1847135446Strhodes DP(DEF_LEVEL, "killing entry %p", entry); 1848135446Strhodes INSIST(ISC_LINK_LINKED(entry, plink)); 1849135446Strhodes result = unlink_entry(adb, entry); 1850135446Strhodes free_adbentry(adb, &entry); 1851135446Strhodes if (result) 1852135446Strhodes dec_adb_irefcnt(adb); 1853135446Strhodes *entryp = NULL; 1854135446Strhodes return (result); 1855135446Strhodes} 1856135446Strhodes 1857135446Strhodes/* 1858135446Strhodes * ADB must be locked, and no other locks held. 1859135446Strhodes */ 1860135446Strhodesstatic isc_boolean_t 1861135446Strhodescleanup_names(dns_adb_t *adb, int bucket, isc_stdtime_t now) { 1862135446Strhodes dns_adbname_t *name; 1863135446Strhodes dns_adbname_t *next_name; 1864153816Sdougb isc_boolean_t result = ISC_FALSE; 1865135446Strhodes 1866135446Strhodes DP(CLEAN_LEVEL, "cleaning name bucket %d", bucket); 1867135446Strhodes 1868135446Strhodes LOCK(&adb->namelocks[bucket]); 1869135446Strhodes if (adb->name_sd[bucket]) { 1870135446Strhodes UNLOCK(&adb->namelocks[bucket]); 1871135446Strhodes return (result); 1872135446Strhodes } 1873135446Strhodes 1874135446Strhodes name = ISC_LIST_HEAD(adb->names[bucket]); 1875135446Strhodes while (name != NULL) { 1876135446Strhodes next_name = ISC_LIST_NEXT(name, plink); 1877135446Strhodes INSIST(result == ISC_FALSE); 1878135446Strhodes result = check_expire_namehooks(name, now, adb->overmem); 1879135446Strhodes if (!result) 1880135446Strhodes result = check_expire_name(&name, now); 1881135446Strhodes name = next_name; 1882135446Strhodes } 1883135446Strhodes UNLOCK(&adb->namelocks[bucket]); 1884135446Strhodes return (result); 1885135446Strhodes} 1886135446Strhodes 1887135446Strhodes/* 1888135446Strhodes * ADB must be locked, and no other locks held. 1889135446Strhodes */ 1890135446Strhodesstatic isc_boolean_t 1891135446Strhodescleanup_entries(dns_adb_t *adb, int bucket, isc_stdtime_t now) { 1892135446Strhodes dns_adbentry_t *entry, *next_entry; 1893135446Strhodes isc_boolean_t result = ISC_FALSE; 1894135446Strhodes 1895135446Strhodes DP(CLEAN_LEVEL, "cleaning entry bucket %d", bucket); 1896135446Strhodes 1897135446Strhodes LOCK(&adb->entrylocks[bucket]); 1898135446Strhodes entry = ISC_LIST_HEAD(adb->entries[bucket]); 1899135446Strhodes while (entry != NULL) { 1900135446Strhodes next_entry = ISC_LIST_NEXT(entry, plink); 1901135446Strhodes INSIST(result == ISC_FALSE); 1902135446Strhodes result = check_expire_entry(adb, &entry, now); 1903135446Strhodes entry = next_entry; 1904135446Strhodes } 1905135446Strhodes UNLOCK(&adb->entrylocks[bucket]); 1906135446Strhodes return (result); 1907135446Strhodes} 1908135446Strhodes 1909135446Strhodesstatic void 1910135446Strhodestimer_cleanup(isc_task_t *task, isc_event_t *ev) { 1911135446Strhodes dns_adb_t *adb; 1912135446Strhodes isc_stdtime_t now; 1913135446Strhodes unsigned int i; 1914135446Strhodes isc_interval_t interval; 1915135446Strhodes 1916135446Strhodes UNUSED(task); 1917135446Strhodes 1918135446Strhodes adb = ev->ev_arg; 1919135446Strhodes INSIST(DNS_ADB_VALID(adb)); 1920135446Strhodes 1921135446Strhodes LOCK(&adb->lock); 1922135446Strhodes 1923135446Strhodes isc_stdtime_get(&now); 1924135446Strhodes 1925135446Strhodes for (i = 0; i < CLEAN_BUCKETS; i++) { 1926135446Strhodes /* 1927135446Strhodes * Call our cleanup routines. 1928135446Strhodes */ 1929135446Strhodes RUNTIME_CHECK(cleanup_names(adb, adb->next_cleanbucket, now) == 1930135446Strhodes ISC_FALSE); 1931135446Strhodes RUNTIME_CHECK(cleanup_entries(adb, adb->next_cleanbucket, now) 1932135446Strhodes == ISC_FALSE); 1933135446Strhodes 1934135446Strhodes /* 1935135446Strhodes * Set the next bucket to be cleaned. 1936135446Strhodes */ 1937135446Strhodes adb->next_cleanbucket++; 1938135446Strhodes if (adb->next_cleanbucket >= NBUCKETS) { 1939135446Strhodes adb->next_cleanbucket = 0; 1940135446Strhodes#ifdef DUMP_ADB_AFTER_CLEANING 1941143731Sdougb dump_adb(adb, stdout, ISC_TRUE, now); 1942135446Strhodes#endif 1943135446Strhodes } 1944135446Strhodes } 1945135446Strhodes 1946135446Strhodes /* 1947135446Strhodes * Reset the timer. 1948135446Strhodes * XXXDCL isc_timer_reset might return ISC_R_UNEXPECTED or 1949135446Strhodes * ISC_R_NOMEMORY, but it isn't clear what could be done here 1950135446Strhodes * if either one of those things happened. 1951135446Strhodes */ 1952135446Strhodes interval = adb->tick_interval; 1953135446Strhodes if (adb->overmem) 1954135446Strhodes isc_interval_set(&interval, 0, 1); 1955135446Strhodes (void)isc_timer_reset(adb->timer, isc_timertype_once, NULL, 1956135446Strhodes &interval, ISC_FALSE); 1957135446Strhodes 1958135446Strhodes UNLOCK(&adb->lock); 1959135446Strhodes 1960135446Strhodes isc_event_free(&ev); 1961135446Strhodes} 1962135446Strhodes 1963135446Strhodesstatic void 1964135446Strhodesdestroy(dns_adb_t *adb) { 1965135446Strhodes adb->magic = 0; 1966135446Strhodes 1967135446Strhodes /* 1968135446Strhodes * The timer is already dead, from the task's shutdown callback. 1969135446Strhodes */ 1970135446Strhodes isc_task_detach(&adb->task); 1971135446Strhodes 1972135446Strhodes isc_mempool_destroy(&adb->nmp); 1973135446Strhodes isc_mempool_destroy(&adb->nhmp); 1974135446Strhodes isc_mempool_destroy(&adb->zimp); 1975135446Strhodes isc_mempool_destroy(&adb->emp); 1976135446Strhodes isc_mempool_destroy(&adb->ahmp); 1977135446Strhodes isc_mempool_destroy(&adb->aimp); 1978135446Strhodes isc_mempool_destroy(&adb->afmp); 1979135446Strhodes 1980135446Strhodes DESTROYMUTEXBLOCK(adb->entrylocks, NBUCKETS); 1981135446Strhodes DESTROYMUTEXBLOCK(adb->namelocks, NBUCKETS); 1982135446Strhodes 1983135446Strhodes DESTROYLOCK(&adb->reflock); 1984135446Strhodes DESTROYLOCK(&adb->lock); 1985135446Strhodes DESTROYLOCK(&adb->mplock); 1986135446Strhodes 1987135446Strhodes isc_mem_putanddetach(&adb->mctx, adb, sizeof(dns_adb_t)); 1988135446Strhodes} 1989135446Strhodes 1990135446Strhodes 1991135446Strhodes/* 1992135446Strhodes * Public functions. 1993135446Strhodes */ 1994135446Strhodes 1995135446Strhodesisc_result_t 1996135446Strhodesdns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_timermgr_t *timermgr, 1997135446Strhodes isc_taskmgr_t *taskmgr, dns_adb_t **newadb) 1998135446Strhodes{ 1999135446Strhodes dns_adb_t *adb; 2000135446Strhodes isc_result_t result; 2001135446Strhodes int i; 2002135446Strhodes 2003135446Strhodes REQUIRE(mem != NULL); 2004135446Strhodes REQUIRE(view != NULL); 2005135446Strhodes REQUIRE(timermgr != NULL); 2006135446Strhodes REQUIRE(taskmgr != NULL); 2007135446Strhodes REQUIRE(newadb != NULL && *newadb == NULL); 2008135446Strhodes 2009135446Strhodes adb = isc_mem_get(mem, sizeof(dns_adb_t)); 2010135446Strhodes if (adb == NULL) 2011135446Strhodes return (ISC_R_NOMEMORY); 2012135446Strhodes 2013135446Strhodes /* 2014135446Strhodes * Initialize things here that cannot fail, and especially things 2015135446Strhodes * that must be NULL for the error return to work properly. 2016135446Strhodes */ 2017135446Strhodes adb->magic = 0; 2018135446Strhodes adb->erefcnt = 1; 2019135446Strhodes adb->irefcnt = 0; 2020135446Strhodes adb->nmp = NULL; 2021135446Strhodes adb->nhmp = NULL; 2022135446Strhodes adb->zimp = NULL; 2023135446Strhodes adb->emp = NULL; 2024135446Strhodes adb->ahmp = NULL; 2025135446Strhodes adb->aimp = NULL; 2026135446Strhodes adb->afmp = NULL; 2027135446Strhodes adb->task = NULL; 2028135446Strhodes adb->timer = NULL; 2029135446Strhodes adb->mctx = NULL; 2030135446Strhodes adb->view = view; 2031135446Strhodes adb->timermgr = timermgr; 2032135446Strhodes adb->taskmgr = taskmgr; 2033135446Strhodes adb->next_cleanbucket = 0; 2034135446Strhodes ISC_EVENT_INIT(&adb->cevent, sizeof(adb->cevent), 0, NULL, 2035135446Strhodes DNS_EVENT_ADBCONTROL, shutdown_task, adb, 2036135446Strhodes adb, NULL, NULL); 2037135446Strhodes adb->cevent_sent = ISC_FALSE; 2038135446Strhodes adb->shutting_down = ISC_FALSE; 2039135446Strhodes adb->overmem = ISC_FALSE; 2040135446Strhodes ISC_LIST_INIT(adb->whenshutdown); 2041135446Strhodes 2042135446Strhodes isc_mem_attach(mem, &adb->mctx); 2043135446Strhodes 2044135446Strhodes result = isc_mutex_init(&adb->lock); 2045135446Strhodes if (result != ISC_R_SUCCESS) 2046135446Strhodes goto fail0b; 2047135446Strhodes 2048135446Strhodes result = isc_mutex_init(&adb->mplock); 2049135446Strhodes if (result != ISC_R_SUCCESS) 2050135446Strhodes goto fail0c; 2051135446Strhodes 2052135446Strhodes result = isc_mutex_init(&adb->reflock); 2053135446Strhodes if (result != ISC_R_SUCCESS) 2054135446Strhodes goto fail0d; 2055135446Strhodes 2056135446Strhodes /* 2057135446Strhodes * Initialize the bucket locks for names and elements. 2058135446Strhodes * May as well initialize the list heads, too. 2059135446Strhodes */ 2060135446Strhodes result = isc_mutexblock_init(adb->namelocks, NBUCKETS); 2061135446Strhodes if (result != ISC_R_SUCCESS) 2062135446Strhodes goto fail1; 2063135446Strhodes for (i = 0; i < NBUCKETS; i++) { 2064135446Strhodes ISC_LIST_INIT(adb->names[i]); 2065135446Strhodes adb->name_sd[i] = ISC_FALSE; 2066135446Strhodes adb->name_refcnt[i] = 0; 2067135446Strhodes adb->irefcnt++; 2068135446Strhodes } 2069135446Strhodes for (i = 0; i < NBUCKETS; i++) { 2070135446Strhodes ISC_LIST_INIT(adb->entries[i]); 2071135446Strhodes adb->entry_sd[i] = ISC_FALSE; 2072135446Strhodes adb->entry_refcnt[i] = 0; 2073135446Strhodes adb->irefcnt++; 2074135446Strhodes } 2075135446Strhodes result = isc_mutexblock_init(adb->entrylocks, NBUCKETS); 2076135446Strhodes if (result != ISC_R_SUCCESS) 2077135446Strhodes goto fail2; 2078135446Strhodes 2079135446Strhodes /* 2080135446Strhodes * Memory pools 2081135446Strhodes */ 2082135446Strhodes#define MPINIT(t, p, n) do { \ 2083135446Strhodes result = isc_mempool_create(mem, sizeof(t), &(p)); \ 2084135446Strhodes if (result != ISC_R_SUCCESS) \ 2085135446Strhodes goto fail3; \ 2086135446Strhodes isc_mempool_setfreemax((p), FREE_ITEMS); \ 2087135446Strhodes isc_mempool_setfillcount((p), FILL_COUNT); \ 2088135446Strhodes isc_mempool_setname((p), n); \ 2089135446Strhodes isc_mempool_associatelock((p), &adb->mplock); \ 2090135446Strhodes} while (0) 2091135446Strhodes 2092135446Strhodes MPINIT(dns_adbname_t, adb->nmp, "adbname"); 2093135446Strhodes MPINIT(dns_adbnamehook_t, adb->nhmp, "adbnamehook"); 2094135446Strhodes MPINIT(dns_adbzoneinfo_t, adb->zimp, "adbzoneinfo"); 2095135446Strhodes MPINIT(dns_adbentry_t, adb->emp, "adbentry"); 2096135446Strhodes MPINIT(dns_adbfind_t, adb->ahmp, "adbfind"); 2097135446Strhodes MPINIT(dns_adbaddrinfo_t, adb->aimp, "adbaddrinfo"); 2098135446Strhodes MPINIT(dns_adbfetch_t, adb->afmp, "adbfetch"); 2099135446Strhodes 2100135446Strhodes#undef MPINIT 2101135446Strhodes 2102135446Strhodes /* 2103135446Strhodes * Allocate a timer and a task for our periodic cleanup. 2104135446Strhodes */ 2105135446Strhodes result = isc_task_create(adb->taskmgr, 0, &adb->task); 2106135446Strhodes if (result != ISC_R_SUCCESS) 2107135446Strhodes goto fail3; 2108135446Strhodes isc_task_setname(adb->task, "ADB", adb); 2109135446Strhodes /* 2110135446Strhodes * XXXMLG When this is changed to be a config file option, 2111135446Strhodes */ 2112135446Strhodes isc_interval_set(&adb->tick_interval, CLEAN_SECONDS, 0); 2113135446Strhodes result = isc_timer_create(adb->timermgr, isc_timertype_once, 2114135446Strhodes NULL, &adb->tick_interval, adb->task, 2115135446Strhodes timer_cleanup, adb, &adb->timer); 2116135446Strhodes if (result != ISC_R_SUCCESS) 2117135446Strhodes goto fail3; 2118135446Strhodes 2119135446Strhodes DP(ISC_LOG_DEBUG(5), "cleaning interval for adb: " 2120135446Strhodes "%u buckets every %u seconds, %u buckets in system, %u cl.interval", 2121135446Strhodes CLEAN_BUCKETS, CLEAN_SECONDS, NBUCKETS, CLEAN_PERIOD); 2122135446Strhodes 2123135446Strhodes /* 2124135446Strhodes * Normal return. 2125135446Strhodes */ 2126135446Strhodes adb->magic = DNS_ADB_MAGIC; 2127135446Strhodes *newadb = adb; 2128135446Strhodes return (ISC_R_SUCCESS); 2129135446Strhodes 2130135446Strhodes fail3: 2131135446Strhodes if (adb->task != NULL) 2132135446Strhodes isc_task_detach(&adb->task); 2133135446Strhodes if (adb->timer != NULL) 2134135446Strhodes isc_timer_detach(&adb->timer); 2135135446Strhodes 2136135446Strhodes /* clean up entrylocks */ 2137135446Strhodes DESTROYMUTEXBLOCK(adb->entrylocks, NBUCKETS); 2138135446Strhodes 2139135446Strhodes fail2: /* clean up namelocks */ 2140135446Strhodes DESTROYMUTEXBLOCK(adb->namelocks, NBUCKETS); 2141135446Strhodes 2142135446Strhodes fail1: /* clean up only allocated memory */ 2143135446Strhodes if (adb->nmp != NULL) 2144135446Strhodes isc_mempool_destroy(&adb->nmp); 2145135446Strhodes if (adb->nhmp != NULL) 2146135446Strhodes isc_mempool_destroy(&adb->nhmp); 2147135446Strhodes if (adb->zimp != NULL) 2148135446Strhodes isc_mempool_destroy(&adb->zimp); 2149135446Strhodes if (adb->emp != NULL) 2150135446Strhodes isc_mempool_destroy(&adb->emp); 2151135446Strhodes if (adb->ahmp != NULL) 2152135446Strhodes isc_mempool_destroy(&adb->ahmp); 2153135446Strhodes if (adb->aimp != NULL) 2154135446Strhodes isc_mempool_destroy(&adb->aimp); 2155135446Strhodes if (adb->afmp != NULL) 2156135446Strhodes isc_mempool_destroy(&adb->afmp); 2157135446Strhodes 2158135446Strhodes DESTROYLOCK(&adb->reflock); 2159135446Strhodes fail0d: 2160135446Strhodes DESTROYLOCK(&adb->mplock); 2161135446Strhodes fail0c: 2162135446Strhodes DESTROYLOCK(&adb->lock); 2163135446Strhodes fail0b: 2164135446Strhodes isc_mem_putanddetach(&adb->mctx, adb, sizeof(dns_adb_t)); 2165135446Strhodes 2166135446Strhodes return (result); 2167135446Strhodes} 2168135446Strhodes 2169135446Strhodesvoid 2170135446Strhodesdns_adb_attach(dns_adb_t *adb, dns_adb_t **adbx) { 2171135446Strhodes 2172135446Strhodes REQUIRE(DNS_ADB_VALID(adb)); 2173135446Strhodes REQUIRE(adbx != NULL && *adbx == NULL); 2174135446Strhodes 2175135446Strhodes inc_adb_erefcnt(adb); 2176135446Strhodes *adbx = adb; 2177135446Strhodes} 2178135446Strhodes 2179135446Strhodesvoid 2180135446Strhodesdns_adb_detach(dns_adb_t **adbx) { 2181135446Strhodes dns_adb_t *adb; 2182135446Strhodes isc_boolean_t need_exit_check; 2183135446Strhodes 2184135446Strhodes REQUIRE(adbx != NULL && DNS_ADB_VALID(*adbx)); 2185135446Strhodes 2186135446Strhodes adb = *adbx; 2187135446Strhodes *adbx = NULL; 2188135446Strhodes 2189135446Strhodes INSIST(adb->erefcnt > 0); 2190135446Strhodes 2191135446Strhodes LOCK(&adb->reflock); 2192135446Strhodes adb->erefcnt--; 2193135446Strhodes need_exit_check = ISC_TF(adb->erefcnt == 0 && adb->irefcnt == 0); 2194135446Strhodes UNLOCK(&adb->reflock); 2195135446Strhodes 2196135446Strhodes if (need_exit_check) { 2197135446Strhodes LOCK(&adb->lock); 2198135446Strhodes INSIST(adb->shutting_down); 2199135446Strhodes check_exit(adb); 2200135446Strhodes UNLOCK(&adb->lock); 2201135446Strhodes } 2202135446Strhodes} 2203135446Strhodes 2204135446Strhodesvoid 2205135446Strhodesdns_adb_whenshutdown(dns_adb_t *adb, isc_task_t *task, isc_event_t **eventp) { 2206135446Strhodes isc_task_t *clone; 2207135446Strhodes isc_event_t *event; 2208135446Strhodes isc_boolean_t zeroirefcnt = ISC_FALSE; 2209135446Strhodes 2210135446Strhodes /* 2211135446Strhodes * Send '*eventp' to 'task' when 'adb' has shutdown. 2212135446Strhodes */ 2213135446Strhodes 2214135446Strhodes REQUIRE(DNS_ADB_VALID(adb)); 2215135446Strhodes REQUIRE(eventp != NULL); 2216135446Strhodes 2217135446Strhodes event = *eventp; 2218135446Strhodes *eventp = NULL; 2219135446Strhodes 2220135446Strhodes LOCK(&adb->lock); 2221135446Strhodes 2222135446Strhodes LOCK(&adb->reflock); 2223135446Strhodes zeroirefcnt = ISC_TF(adb->irefcnt == 0); 2224135446Strhodes 2225135446Strhodes if (adb->shutting_down && zeroirefcnt && 2226135446Strhodes isc_mempool_getallocated(adb->ahmp) == 0) { 2227135446Strhodes /* 2228135446Strhodes * We're already shutdown. Send the event. 2229135446Strhodes */ 2230135446Strhodes event->ev_sender = adb; 2231135446Strhodes isc_task_send(task, &event); 2232135446Strhodes } else { 2233135446Strhodes clone = NULL; 2234135446Strhodes isc_task_attach(task, &clone); 2235135446Strhodes event->ev_sender = clone; 2236135446Strhodes ISC_LIST_APPEND(adb->whenshutdown, event, ev_link); 2237135446Strhodes } 2238135446Strhodes 2239135446Strhodes UNLOCK(&adb->reflock); 2240135446Strhodes UNLOCK(&adb->lock); 2241135446Strhodes} 2242135446Strhodes 2243135446Strhodesvoid 2244135446Strhodesdns_adb_shutdown(dns_adb_t *adb) { 2245135446Strhodes isc_boolean_t need_check_exit; 2246135446Strhodes 2247135446Strhodes /* 2248135446Strhodes * Shutdown 'adb'. 2249135446Strhodes */ 2250135446Strhodes 2251135446Strhodes LOCK(&adb->lock); 2252135446Strhodes 2253135446Strhodes if (!adb->shutting_down) { 2254135446Strhodes adb->shutting_down = ISC_TRUE; 2255135446Strhodes isc_mem_setwater(adb->mctx, water, adb, 0, 0); 2256135446Strhodes need_check_exit = shutdown_names(adb); 2257135446Strhodes if (!need_check_exit) 2258135446Strhodes need_check_exit = shutdown_entries(adb); 2259135446Strhodes if (need_check_exit) 2260135446Strhodes check_exit(adb); 2261135446Strhodes } 2262135446Strhodes 2263135446Strhodes UNLOCK(&adb->lock); 2264135446Strhodes} 2265135446Strhodes 2266135446Strhodesisc_result_t 2267135446Strhodesdns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, 2268135446Strhodes void *arg, dns_name_t *name, dns_name_t *zone, 2269135446Strhodes unsigned int options, isc_stdtime_t now, dns_name_t *target, 2270135446Strhodes in_port_t port, dns_adbfind_t **findp) 2271135446Strhodes{ 2272135446Strhodes dns_adbfind_t *find; 2273135446Strhodes dns_adbname_t *adbname; 2274135446Strhodes int bucket; 2275135446Strhodes isc_boolean_t want_event, start_at_zone, alias, have_address; 2276135446Strhodes isc_result_t result; 2277135446Strhodes unsigned int wanted_addresses; 2278135446Strhodes unsigned int wanted_fetches; 2279135446Strhodes unsigned int query_pending; 2280135446Strhodes 2281135446Strhodes REQUIRE(DNS_ADB_VALID(adb)); 2282135446Strhodes if (task != NULL) { 2283135446Strhodes REQUIRE(action != NULL); 2284135446Strhodes } 2285135446Strhodes REQUIRE(name != NULL); 2286135446Strhodes REQUIRE(zone != NULL); 2287135446Strhodes REQUIRE(findp != NULL && *findp == NULL); 2288135446Strhodes REQUIRE(target == NULL || dns_name_hasbuffer(target)); 2289135446Strhodes 2290135446Strhodes REQUIRE((options & DNS_ADBFIND_ADDRESSMASK) != 0); 2291135446Strhodes 2292135446Strhodes result = ISC_R_UNEXPECTED; 2293135446Strhodes wanted_addresses = (options & DNS_ADBFIND_ADDRESSMASK); 2294135446Strhodes wanted_fetches = 0; 2295135446Strhodes query_pending = 0; 2296135446Strhodes want_event = ISC_FALSE; 2297135446Strhodes start_at_zone = ISC_FALSE; 2298135446Strhodes alias = ISC_FALSE; 2299135446Strhodes 2300135446Strhodes if (now == 0) 2301135446Strhodes isc_stdtime_get(&now); 2302135446Strhodes 2303135446Strhodes /* 2304135446Strhodes * XXXMLG Move this comment somewhere else! 2305135446Strhodes * 2306135446Strhodes * Look up the name in our internal database. 2307135446Strhodes * 2308135446Strhodes * Possibilities: Note that these are not always exclusive. 2309135446Strhodes * 2310135446Strhodes * No name found. In this case, allocate a new name header and 2311135446Strhodes * an initial namehook or two. If any of these allocations 2312135446Strhodes * fail, clean up and return ISC_R_NOMEMORY. 2313135446Strhodes * 2314135446Strhodes * Name found, valid addresses present. Allocate one addrinfo 2315135446Strhodes * structure for each found and append it to the linked list 2316135446Strhodes * of addresses for this header. 2317135446Strhodes * 2318135446Strhodes * Name found, queries pending. In this case, if a task was 2319135446Strhodes * passed in, allocate a job id, attach it to the name's job 2320135446Strhodes * list and remember to tell the caller that there will be 2321135446Strhodes * more info coming later. 2322135446Strhodes */ 2323135446Strhodes 2324135446Strhodes find = new_adbfind(adb); 2325135446Strhodes if (find == NULL) 2326135446Strhodes return (ISC_R_NOMEMORY); 2327135446Strhodes 2328135446Strhodes find->port = port; 2329135446Strhodes 2330135446Strhodes /* 2331135446Strhodes * Remember what types of addresses we are interested in. 2332135446Strhodes */ 2333135446Strhodes find->options = options; 2334135446Strhodes find->flags |= wanted_addresses; 2335135446Strhodes if (FIND_WANTEVENT(find)) { 2336135446Strhodes REQUIRE(task != NULL); 2337135446Strhodes } 2338135446Strhodes 2339135446Strhodes /* 2340135446Strhodes * Try to see if we know anything about this name at all. 2341135446Strhodes */ 2342135446Strhodes bucket = DNS_ADB_INVALIDBUCKET; 2343135446Strhodes adbname = find_name_and_lock(adb, name, find->options, &bucket); 2344135446Strhodes if (adb->name_sd[bucket]) { 2345135446Strhodes DP(DEF_LEVEL, 2346135446Strhodes "dns_adb_createfind: returning ISC_R_SHUTTINGDOWN"); 2347135446Strhodes RUNTIME_CHECK(free_adbfind(adb, &find) == ISC_FALSE); 2348135446Strhodes result = ISC_R_SHUTTINGDOWN; 2349135446Strhodes goto out; 2350135446Strhodes } 2351135446Strhodes 2352135446Strhodes /* 2353135446Strhodes * Nothing found. Allocate a new adbname structure for this name. 2354135446Strhodes */ 2355135446Strhodes if (adbname == NULL) { 2356135446Strhodes adbname = new_adbname(adb, name); 2357135446Strhodes if (adbname == NULL) { 2358135446Strhodes RUNTIME_CHECK(free_adbfind(adb, &find) == ISC_FALSE); 2359135446Strhodes result = ISC_R_NOMEMORY; 2360135446Strhodes goto out; 2361135446Strhodes } 2362135446Strhodes link_name(adb, bucket, adbname); 2363135446Strhodes if (FIND_HINTOK(find)) 2364135446Strhodes adbname->flags |= NAME_HINT_OK; 2365135446Strhodes if (FIND_GLUEOK(find)) 2366135446Strhodes adbname->flags |= NAME_GLUE_OK; 2367135446Strhodes if (FIND_STARTATZONE(find)) 2368135446Strhodes adbname->flags |= NAME_STARTATZONE; 2369135446Strhodes } 2370135446Strhodes 2371135446Strhodes /* 2372135446Strhodes * Expire old entries, etc. 2373135446Strhodes */ 2374135446Strhodes RUNTIME_CHECK(check_expire_namehooks(adbname, now, adb->overmem) == 2375135446Strhodes ISC_FALSE); 2376135446Strhodes 2377135446Strhodes /* 2378135446Strhodes * Do we know that the name is an alias? 2379135446Strhodes */ 2380135446Strhodes if (!EXPIRE_OK(adbname->expire_target, now)) { 2381135446Strhodes /* 2382135446Strhodes * Yes, it is. 2383135446Strhodes */ 2384135446Strhodes DP(DEF_LEVEL, 2385135446Strhodes "dns_adb_createfind: name %p is an alias (cached)", 2386135446Strhodes adbname); 2387135446Strhodes alias = ISC_TRUE; 2388135446Strhodes goto post_copy; 2389135446Strhodes } 2390135446Strhodes 2391135446Strhodes /* 2392135446Strhodes * Try to populate the name from the database and/or 2393135446Strhodes * start fetches. First try looking for an A record 2394135446Strhodes * in the database. 2395135446Strhodes */ 2396135446Strhodes if (!NAME_HAS_V4(adbname) && EXPIRE_OK(adbname->expire_v4, now) 2397135446Strhodes && WANT_INET(wanted_addresses)) { 2398135446Strhodes result = dbfind_name(adbname, now, dns_rdatatype_a); 2399135446Strhodes if (result == ISC_R_SUCCESS) { 2400135446Strhodes DP(DEF_LEVEL, 2401135446Strhodes "dns_adb_createfind: found A for name %p in db", 2402135446Strhodes adbname); 2403135446Strhodes goto v6; 2404135446Strhodes } 2405135446Strhodes 2406135446Strhodes /* 2407135446Strhodes * Did we get a CNAME or DNAME? 2408135446Strhodes */ 2409135446Strhodes if (result == DNS_R_ALIAS) { 2410135446Strhodes DP(DEF_LEVEL, 2411135446Strhodes "dns_adb_createfind: name %p is an alias", 2412135446Strhodes adbname); 2413135446Strhodes alias = ISC_TRUE; 2414135446Strhodes goto post_copy; 2415135446Strhodes } 2416135446Strhodes 2417135446Strhodes /* 2418135446Strhodes * If the name doesn't exist at all, don't bother with 2419135446Strhodes * v6 queries; they won't work. 2420135446Strhodes * 2421135446Strhodes * If the name does exist but we didn't get our data, go 2422135446Strhodes * ahead and try AAAA. 2423135446Strhodes * 2424135446Strhodes * If the result is neither of these, try a fetch for A. 2425135446Strhodes */ 2426135446Strhodes if (NXDOMAIN_RESULT(result)) 2427135446Strhodes goto fetch; 2428135446Strhodes else if (NXRRSET_RESULT(result)) 2429135446Strhodes goto v6; 2430135446Strhodes 2431135446Strhodes if (!NAME_FETCH_V4(adbname)) 2432135446Strhodes wanted_fetches |= DNS_ADBFIND_INET; 2433135446Strhodes } 2434135446Strhodes 2435135446Strhodes v6: 2436135446Strhodes if (!NAME_HAS_V6(adbname) && EXPIRE_OK(adbname->expire_v6, now) 2437135446Strhodes && WANT_INET6(wanted_addresses)) { 2438135446Strhodes result = dbfind_name(adbname, now, dns_rdatatype_aaaa); 2439135446Strhodes if (result == ISC_R_SUCCESS) { 2440135446Strhodes DP(DEF_LEVEL, 2441135446Strhodes "dns_adb_createfind: found AAAA for name %p", 2442135446Strhodes adbname); 2443135446Strhodes goto fetch; 2444135446Strhodes } 2445135446Strhodes 2446135446Strhodes /* 2447135446Strhodes * Did we get a CNAME or DNAME? 2448135446Strhodes */ 2449135446Strhodes if (result == DNS_R_ALIAS) { 2450135446Strhodes DP(DEF_LEVEL, 2451135446Strhodes "dns_adb_createfind: name %p is an alias", 2452135446Strhodes adbname); 2453135446Strhodes alias = ISC_TRUE; 2454135446Strhodes goto post_copy; 2455135446Strhodes } 2456135446Strhodes 2457135446Strhodes /* 2458135446Strhodes * Listen to negative cache hints, and don't start 2459135446Strhodes * another query. 2460135446Strhodes */ 2461135446Strhodes if (NCACHE_RESULT(result) || AUTH_NX(result)) 2462135446Strhodes goto fetch; 2463135446Strhodes 2464135446Strhodes if (!NAME_FETCH_V6(adbname)) 2465135446Strhodes wanted_fetches |= DNS_ADBFIND_INET6; 2466135446Strhodes } 2467135446Strhodes 2468135446Strhodes fetch: 2469135446Strhodes if ((WANT_INET(wanted_addresses) && NAME_HAS_V4(adbname)) || 2470135446Strhodes (WANT_INET6(wanted_addresses) && NAME_HAS_V6(adbname))) 2471135446Strhodes have_address = ISC_TRUE; 2472135446Strhodes else 2473135446Strhodes have_address = ISC_FALSE; 2474135446Strhodes if (wanted_fetches != 0 && 2475135446Strhodes ! (FIND_AVOIDFETCHES(find) && have_address)) { 2476135446Strhodes /* 2477135446Strhodes * We're missing at least one address family. Either the 2478135446Strhodes * caller hasn't instructed us to avoid fetches, or we don't 2479135446Strhodes * know anything about any of the address families that would 2480135446Strhodes * be acceptable so we have to launch fetches. 2481135446Strhodes */ 2482135446Strhodes 2483135446Strhodes if (FIND_STARTATZONE(find)) 2484135446Strhodes start_at_zone = ISC_TRUE; 2485135446Strhodes 2486135446Strhodes /* 2487135446Strhodes * Start V4. 2488135446Strhodes */ 2489135446Strhodes if (WANT_INET(wanted_fetches) && 2490135446Strhodes fetch_name(adbname, start_at_zone, 2491135446Strhodes dns_rdatatype_a) == ISC_R_SUCCESS) { 2492135446Strhodes DP(DEF_LEVEL, 2493135446Strhodes "dns_adb_createfind: started A fetch for name %p", 2494135446Strhodes adbname); 2495135446Strhodes } 2496135446Strhodes 2497135446Strhodes /* 2498135446Strhodes * Start V6. 2499135446Strhodes */ 2500135446Strhodes if (WANT_INET6(wanted_fetches) && 2501135446Strhodes fetch_name(adbname, start_at_zone, 2502135446Strhodes dns_rdatatype_aaaa) == ISC_R_SUCCESS) { 2503135446Strhodes DP(DEF_LEVEL, 2504135446Strhodes "dns_adb_createfind: " 2505135446Strhodes "started AAAA fetch for name %p", 2506135446Strhodes adbname); 2507135446Strhodes } 2508135446Strhodes } 2509135446Strhodes 2510135446Strhodes /* 2511135446Strhodes * Run through the name and copy out the bits we are 2512135446Strhodes * interested in. 2513135446Strhodes */ 2514135446Strhodes copy_namehook_lists(adb, find, zone, adbname, now); 2515135446Strhodes 2516135446Strhodes post_copy: 2517135446Strhodes if (NAME_FETCH_V4(adbname)) 2518135446Strhodes query_pending |= DNS_ADBFIND_INET; 2519135446Strhodes if (NAME_FETCH_V6(adbname)) 2520135446Strhodes query_pending |= DNS_ADBFIND_INET6; 2521135446Strhodes 2522135446Strhodes /* 2523135446Strhodes * Attach to the name's query list if there are queries 2524135446Strhodes * already running, and we have been asked to. 2525135446Strhodes */ 2526135446Strhodes want_event = ISC_TRUE; 2527135446Strhodes if (!FIND_WANTEVENT(find)) 2528135446Strhodes want_event = ISC_FALSE; 2529135446Strhodes if (FIND_WANTEMPTYEVENT(find) && FIND_HAS_ADDRS(find)) 2530135446Strhodes want_event = ISC_FALSE; 2531135446Strhodes if ((wanted_addresses & query_pending) == 0) 2532135446Strhodes want_event = ISC_FALSE; 2533135446Strhodes if (alias) 2534135446Strhodes want_event = ISC_FALSE; 2535135446Strhodes if (want_event) { 2536135446Strhodes find->adbname = adbname; 2537135446Strhodes find->name_bucket = bucket; 2538135446Strhodes ISC_LIST_APPEND(adbname->finds, find, plink); 2539135446Strhodes find->query_pending = (query_pending & wanted_addresses); 2540135446Strhodes find->flags &= ~DNS_ADBFIND_ADDRESSMASK; 2541135446Strhodes find->flags |= (find->query_pending & DNS_ADBFIND_ADDRESSMASK); 2542135446Strhodes DP(DEF_LEVEL, "createfind: attaching find %p to adbname %p", 2543135446Strhodes find, adbname); 2544135446Strhodes } else { 2545135446Strhodes /* 2546135446Strhodes * Remove the flag so the caller knows there will never 2547135446Strhodes * be an event, and set internal flags to fake that 2548135446Strhodes * the event was sent and freed, so dns_adb_destroyfind() will 2549135446Strhodes * do the right thing. 2550135446Strhodes */ 2551135446Strhodes find->query_pending = (query_pending & wanted_addresses); 2552135446Strhodes find->options &= ~DNS_ADBFIND_WANTEVENT; 2553135446Strhodes find->flags |= (FIND_EVENT_SENT | FIND_EVENT_FREED); 2554135446Strhodes find->flags &= ~DNS_ADBFIND_ADDRESSMASK; 2555135446Strhodes } 2556135446Strhodes 2557135446Strhodes find->partial_result |= (adbname->partial_result & wanted_addresses); 2558135446Strhodes if (alias) { 2559135446Strhodes if (target != NULL) { 2560135446Strhodes result = dns_name_copy(&adbname->target, target, NULL); 2561135446Strhodes if (result != ISC_R_SUCCESS) 2562135446Strhodes goto out; 2563135446Strhodes } 2564135446Strhodes result = DNS_R_ALIAS; 2565135446Strhodes } else 2566135446Strhodes result = ISC_R_SUCCESS; 2567135446Strhodes 2568135446Strhodes /* 2569135446Strhodes * Copy out error flags from the name structure into the find. 2570135446Strhodes */ 2571135446Strhodes find->result_v4 = find_err_map[adbname->fetch_err]; 2572135446Strhodes find->result_v6 = find_err_map[adbname->fetch6_err]; 2573135446Strhodes 2574135446Strhodes out: 2575135446Strhodes if (find != NULL) { 2576135446Strhodes *findp = find; 2577135446Strhodes 2578135446Strhodes if (want_event) { 2579135446Strhodes isc_task_t *taskp; 2580135446Strhodes 2581135446Strhodes INSIST((find->flags & DNS_ADBFIND_ADDRESSMASK) != 0); 2582135446Strhodes taskp = NULL; 2583135446Strhodes isc_task_attach(task, &taskp); 2584135446Strhodes find->event.ev_sender = taskp; 2585135446Strhodes find->event.ev_action = action; 2586135446Strhodes find->event.ev_arg = arg; 2587135446Strhodes } 2588135446Strhodes } 2589135446Strhodes 2590135446Strhodes if (bucket != DNS_ADB_INVALIDBUCKET) 2591135446Strhodes UNLOCK(&adb->namelocks[bucket]); 2592135446Strhodes 2593135446Strhodes return (result); 2594135446Strhodes} 2595135446Strhodes 2596135446Strhodesvoid 2597135446Strhodesdns_adb_destroyfind(dns_adbfind_t **findp) { 2598135446Strhodes dns_adbfind_t *find; 2599135446Strhodes dns_adbentry_t *entry; 2600135446Strhodes dns_adbaddrinfo_t *ai; 2601135446Strhodes int bucket; 2602135446Strhodes dns_adb_t *adb; 2603135446Strhodes 2604135446Strhodes REQUIRE(findp != NULL && DNS_ADBFIND_VALID(*findp)); 2605135446Strhodes find = *findp; 2606135446Strhodes *findp = NULL; 2607135446Strhodes 2608135446Strhodes LOCK(&find->lock); 2609135446Strhodes 2610135446Strhodes DP(DEF_LEVEL, "dns_adb_destroyfind on find %p", find); 2611135446Strhodes 2612135446Strhodes adb = find->adb; 2613135446Strhodes REQUIRE(DNS_ADB_VALID(adb)); 2614135446Strhodes 2615135446Strhodes REQUIRE(FIND_EVENTFREED(find)); 2616135446Strhodes 2617135446Strhodes bucket = find->name_bucket; 2618135446Strhodes INSIST(bucket == DNS_ADB_INVALIDBUCKET); 2619135446Strhodes 2620135446Strhodes UNLOCK(&find->lock); 2621135446Strhodes 2622135446Strhodes /* 2623135446Strhodes * The find doesn't exist on any list, and nothing is locked. 2624135446Strhodes * Return the find to the memory pool, and decrement the adb's 2625135446Strhodes * reference count. 2626135446Strhodes */ 2627135446Strhodes ai = ISC_LIST_HEAD(find->list); 2628135446Strhodes while (ai != NULL) { 2629135446Strhodes ISC_LIST_UNLINK(find->list, ai, publink); 2630135446Strhodes entry = ai->entry; 2631135446Strhodes ai->entry = NULL; 2632135446Strhodes INSIST(DNS_ADBENTRY_VALID(entry)); 2633135446Strhodes RUNTIME_CHECK(dec_entry_refcnt(adb, entry, ISC_TRUE) == 2634135446Strhodes ISC_FALSE); 2635135446Strhodes free_adbaddrinfo(adb, &ai); 2636135446Strhodes ai = ISC_LIST_HEAD(find->list); 2637135446Strhodes } 2638135446Strhodes 2639135446Strhodes /* 2640135446Strhodes * WARNING: The find is freed with the adb locked. This is done 2641135446Strhodes * to avoid a race condition where we free the find, some other 2642135446Strhodes * thread tests to see if it should be destroyed, detects it should 2643135446Strhodes * be, destroys it, and then we try to lock it for our check, but the 2644135446Strhodes * lock is destroyed. 2645135446Strhodes */ 2646135446Strhodes LOCK(&adb->lock); 2647135446Strhodes if (free_adbfind(adb, &find)) 2648135446Strhodes check_exit(adb); 2649135446Strhodes UNLOCK(&adb->lock); 2650135446Strhodes} 2651135446Strhodes 2652135446Strhodesvoid 2653135446Strhodesdns_adb_cancelfind(dns_adbfind_t *find) { 2654135446Strhodes isc_event_t *ev; 2655135446Strhodes isc_task_t *task; 2656135446Strhodes dns_adb_t *adb; 2657135446Strhodes int bucket; 2658135446Strhodes int unlock_bucket; 2659135446Strhodes 2660135446Strhodes LOCK(&find->lock); 2661135446Strhodes 2662135446Strhodes DP(DEF_LEVEL, "dns_adb_cancelfind on find %p", find); 2663135446Strhodes 2664135446Strhodes adb = find->adb; 2665135446Strhodes REQUIRE(DNS_ADB_VALID(adb)); 2666135446Strhodes 2667135446Strhodes REQUIRE(!FIND_EVENTFREED(find)); 2668135446Strhodes REQUIRE(FIND_WANTEVENT(find)); 2669135446Strhodes 2670135446Strhodes bucket = find->name_bucket; 2671135446Strhodes if (bucket == DNS_ADB_INVALIDBUCKET) 2672135446Strhodes goto cleanup; 2673135446Strhodes 2674135446Strhodes /* 2675135446Strhodes * We need to get the adbname's lock to unlink the find. 2676135446Strhodes */ 2677135446Strhodes unlock_bucket = bucket; 2678135446Strhodes violate_locking_hierarchy(&find->lock, &adb->namelocks[unlock_bucket]); 2679135446Strhodes bucket = find->name_bucket; 2680135446Strhodes if (bucket != DNS_ADB_INVALIDBUCKET) { 2681135446Strhodes ISC_LIST_UNLINK(find->adbname->finds, find, plink); 2682135446Strhodes find->adbname = NULL; 2683135446Strhodes find->name_bucket = DNS_ADB_INVALIDBUCKET; 2684135446Strhodes } 2685135446Strhodes UNLOCK(&adb->namelocks[unlock_bucket]); 2686135446Strhodes bucket = DNS_ADB_INVALIDBUCKET; 2687135446Strhodes 2688135446Strhodes cleanup: 2689135446Strhodes 2690135446Strhodes if (!FIND_EVENTSENT(find)) { 2691135446Strhodes ev = &find->event; 2692135446Strhodes task = ev->ev_sender; 2693135446Strhodes ev->ev_sender = find; 2694135446Strhodes ev->ev_type = DNS_EVENT_ADBCANCELED; 2695135446Strhodes ev->ev_destroy = event_free; 2696135446Strhodes ev->ev_destroy_arg = find; 2697135446Strhodes find->result_v4 = ISC_R_CANCELED; 2698135446Strhodes find->result_v6 = ISC_R_CANCELED; 2699135446Strhodes 2700135446Strhodes DP(DEF_LEVEL, "sending event %p to task %p for find %p", 2701135446Strhodes ev, task, find); 2702135446Strhodes 2703135446Strhodes isc_task_sendanddetach(&task, (isc_event_t **)&ev); 2704135446Strhodes } 2705135446Strhodes 2706135446Strhodes UNLOCK(&find->lock); 2707135446Strhodes} 2708135446Strhodes 2709135446Strhodesvoid 2710135446Strhodesdns_adb_dump(dns_adb_t *adb, FILE *f) { 2711143731Sdougb int i; 2712143731Sdougb isc_stdtime_t now; 2713143731Sdougb 2714135446Strhodes REQUIRE(DNS_ADB_VALID(adb)); 2715135446Strhodes REQUIRE(f != NULL); 2716135446Strhodes 2717135446Strhodes /* 2718135446Strhodes * Lock the adb itself, lock all the name buckets, then lock all 2719135446Strhodes * the entry buckets. This should put the adb into a state where 2720135446Strhodes * nothing can change, so we can iterate through everything and 2721135446Strhodes * print at our leisure. 2722135446Strhodes */ 2723135446Strhodes 2724135446Strhodes LOCK(&adb->lock); 2725143731Sdougb isc_stdtime_get(&now); 2726143731Sdougb 2727143731Sdougb for (i = 0; i < NBUCKETS; i++) 2728143731Sdougb RUNTIME_CHECK(cleanup_names(adb, i, now) == ISC_FALSE); 2729143731Sdougb for (i = 0; i < NBUCKETS; i++) 2730143731Sdougb RUNTIME_CHECK(cleanup_entries(adb, i, now) == ISC_FALSE); 2731143731Sdougb 2732143731Sdougb dump_adb(adb, f, ISC_FALSE, now); 2733135446Strhodes UNLOCK(&adb->lock); 2734135446Strhodes} 2735135446Strhodes 2736135446Strhodesstatic void 2737135446Strhodesdump_ttl(FILE *f, const char *legend, isc_stdtime_t value, isc_stdtime_t now) { 2738135446Strhodes if (value == INT_MAX) 2739135446Strhodes return; 2740135446Strhodes fprintf(f, " [%s TTL %d]", legend, value - now); 2741135446Strhodes} 2742135446Strhodes 2743135446Strhodesstatic void 2744143731Sdougbdump_adb(dns_adb_t *adb, FILE *f, isc_boolean_t debug, isc_stdtime_t now) { 2745135446Strhodes int i; 2746135446Strhodes dns_adbname_t *name; 2747143731Sdougb dns_adbentry_t *entry; 2748135446Strhodes 2749135446Strhodes fprintf(f, ";\n; Address database dump\n;\n"); 2750135446Strhodes if (debug) 2751135446Strhodes fprintf(f, "; addr %p, erefcnt %u, irefcnt %u, finds out %u\n", 2752135446Strhodes adb, adb->erefcnt, adb->irefcnt, 2753135446Strhodes isc_mempool_getallocated(adb->nhmp)); 2754135446Strhodes 2755135446Strhodes for (i = 0; i < NBUCKETS; i++) 2756135446Strhodes LOCK(&adb->namelocks[i]); 2757135446Strhodes for (i = 0; i < NBUCKETS; i++) 2758135446Strhodes LOCK(&adb->entrylocks[i]); 2759135446Strhodes 2760135446Strhodes /* 2761135446Strhodes * Dump the names 2762135446Strhodes */ 2763135446Strhodes for (i = 0; i < NBUCKETS; i++) { 2764135446Strhodes name = ISC_LIST_HEAD(adb->names[i]); 2765135446Strhodes if (name == NULL) 2766135446Strhodes continue; 2767135446Strhodes if (debug) 2768135446Strhodes fprintf(f, "; bucket %d\n", i); 2769135446Strhodes for (; 2770135446Strhodes name != NULL; 2771135446Strhodes name = ISC_LIST_NEXT(name, plink)) 2772135446Strhodes { 2773135446Strhodes if (debug) 2774135446Strhodes fprintf(f, "; name %p (flags %08x)\n", 2775135446Strhodes name, name->flags); 2776135446Strhodes 2777135446Strhodes fprintf(f, "; "); 2778135446Strhodes print_dns_name(f, &name->name); 2779135446Strhodes if (dns_name_countlabels(&name->target) > 0) { 2780135446Strhodes fprintf(f, " alias "); 2781135446Strhodes print_dns_name(f, &name->target); 2782135446Strhodes } 2783135446Strhodes 2784135446Strhodes dump_ttl(f, "v4", name->expire_v4, now); 2785135446Strhodes dump_ttl(f, "v6", name->expire_v6, now); 2786135446Strhodes dump_ttl(f, "target", name->expire_target, now); 2787135446Strhodes 2788135446Strhodes fprintf(f, " [v4 %s] [v6 %s]", 2789135446Strhodes errnames[name->fetch_err], 2790135446Strhodes errnames[name->fetch6_err]); 2791135446Strhodes 2792135446Strhodes fprintf(f, "\n"); 2793135446Strhodes 2794135446Strhodes print_namehook_list(f, "v4", &name->v4, debug, now); 2795135446Strhodes print_namehook_list(f, "v6", &name->v6, debug, now); 2796135446Strhodes 2797135446Strhodes if (debug) 2798135446Strhodes print_fetch_list(f, name); 2799135446Strhodes if (debug) 2800135446Strhodes print_find_list(f, name); 2801135446Strhodes 2802135446Strhodes } 2803135446Strhodes } 2804135446Strhodes 2805143731Sdougb fprintf(f, ";\n; Unassociated entries\n;\n"); 2806143731Sdougb 2807143731Sdougb for (i = 0; i < NBUCKETS; i++) { 2808143731Sdougb entry = ISC_LIST_HEAD(adb->entries[i]); 2809143731Sdougb while (entry != NULL) { 2810143731Sdougb if (entry->refcnt == 0) 2811143731Sdougb dump_entry(f, entry, debug, now); 2812143731Sdougb entry = ISC_LIST_NEXT(entry, plink); 2813143731Sdougb } 2814143731Sdougb } 2815143731Sdougb 2816135446Strhodes /* 2817135446Strhodes * Unlock everything 2818135446Strhodes */ 2819135446Strhodes for (i = 0; i < NBUCKETS; i++) 2820135446Strhodes UNLOCK(&adb->entrylocks[i]); 2821135446Strhodes for (i = 0; i < NBUCKETS; i++) 2822135446Strhodes UNLOCK(&adb->namelocks[i]); 2823135446Strhodes} 2824135446Strhodes 2825135446Strhodesstatic void 2826135446Strhodesdump_entry(FILE *f, dns_adbentry_t *entry, isc_boolean_t debug, 2827135446Strhodes isc_stdtime_t now) 2828135446Strhodes{ 2829135446Strhodes char addrbuf[ISC_NETADDR_FORMATSIZE]; 2830135446Strhodes isc_netaddr_t netaddr; 2831135446Strhodes dns_adbzoneinfo_t *zi; 2832135446Strhodes 2833135446Strhodes isc_netaddr_fromsockaddr(&netaddr, &entry->sockaddr); 2834135446Strhodes isc_netaddr_format(&netaddr, addrbuf, sizeof(addrbuf)); 2835135446Strhodes 2836135446Strhodes if (debug) 2837135446Strhodes fprintf(f, ";\t%p: refcnt %u\n", entry, entry->refcnt); 2838135446Strhodes 2839135446Strhodes fprintf(f, ";\t%s [srtt %u] [flags %08x]", 2840135446Strhodes addrbuf, entry->srtt, entry->flags); 2841143731Sdougb if (entry->expires != 0) 2842143731Sdougb fprintf(f, " [ttl %d]", entry->expires - now); 2843135446Strhodes fprintf(f, "\n"); 2844135446Strhodes for (zi = ISC_LIST_HEAD(entry->zoneinfo); 2845135446Strhodes zi != NULL; 2846135446Strhodes zi = ISC_LIST_NEXT(zi, plink)) { 2847135446Strhodes fprintf(f, ";\t\t"); 2848135446Strhodes print_dns_name(f, &zi->zone); 2849135446Strhodes fprintf(f, " [lame TTL %d]\n", zi->lame_timer - now); 2850135446Strhodes } 2851135446Strhodes} 2852135446Strhodes 2853135446Strhodesvoid 2854135446Strhodesdns_adb_dumpfind(dns_adbfind_t *find, FILE *f) { 2855135446Strhodes char tmp[512]; 2856135446Strhodes const char *tmpp; 2857135446Strhodes dns_adbaddrinfo_t *ai; 2858135446Strhodes isc_sockaddr_t *sa; 2859135446Strhodes 2860135446Strhodes /* 2861135446Strhodes * Not used currently, in the API Just In Case we 2862135446Strhodes * want to dump out the name and/or entries too. 2863135446Strhodes */ 2864135446Strhodes 2865135446Strhodes LOCK(&find->lock); 2866135446Strhodes 2867135446Strhodes fprintf(f, ";Find %p\n", find); 2868135446Strhodes fprintf(f, ";\tqpending %08x partial %08x options %08x flags %08x\n", 2869135446Strhodes find->query_pending, find->partial_result, 2870135446Strhodes find->options, find->flags); 2871135446Strhodes fprintf(f, ";\tname_bucket %d, name %p, event sender %p\n", 2872135446Strhodes find->name_bucket, find->adbname, find->event.ev_sender); 2873135446Strhodes 2874135446Strhodes ai = ISC_LIST_HEAD(find->list); 2875135446Strhodes if (ai != NULL) 2876135446Strhodes fprintf(f, "\tAddresses:\n"); 2877135446Strhodes while (ai != NULL) { 2878135446Strhodes sa = &ai->sockaddr; 2879135446Strhodes switch (sa->type.sa.sa_family) { 2880135446Strhodes case AF_INET: 2881135446Strhodes tmpp = inet_ntop(AF_INET, &sa->type.sin.sin_addr, 2882135446Strhodes tmp, sizeof(tmp)); 2883135446Strhodes break; 2884135446Strhodes case AF_INET6: 2885135446Strhodes tmpp = inet_ntop(AF_INET6, &sa->type.sin6.sin6_addr, 2886135446Strhodes tmp, sizeof(tmp)); 2887135446Strhodes break; 2888135446Strhodes default: 2889135446Strhodes tmpp = "UnkFamily"; 2890135446Strhodes } 2891135446Strhodes 2892135446Strhodes if (tmpp == NULL) 2893135446Strhodes tmpp = "BadAddress"; 2894135446Strhodes 2895135446Strhodes fprintf(f, "\t\tentry %p, flags %08x" 2896135446Strhodes " srtt %u addr %s\n", 2897135446Strhodes ai->entry, ai->flags, ai->srtt, tmpp); 2898135446Strhodes 2899135446Strhodes ai = ISC_LIST_NEXT(ai, publink); 2900135446Strhodes } 2901135446Strhodes 2902135446Strhodes UNLOCK(&find->lock); 2903135446Strhodes} 2904135446Strhodes 2905135446Strhodesstatic void 2906135446Strhodesprint_dns_name(FILE *f, dns_name_t *name) { 2907135446Strhodes char buf[DNS_NAME_FORMATSIZE]; 2908135446Strhodes 2909135446Strhodes INSIST(f != NULL); 2910135446Strhodes 2911135446Strhodes dns_name_format(name, buf, sizeof(buf)); 2912135446Strhodes fprintf(f, "%s", buf); 2913135446Strhodes} 2914135446Strhodes 2915135446Strhodesstatic void 2916135446Strhodesprint_namehook_list(FILE *f, const char *legend, dns_adbnamehooklist_t *list, 2917135446Strhodes isc_boolean_t debug, isc_stdtime_t now) 2918135446Strhodes{ 2919135446Strhodes dns_adbnamehook_t *nh; 2920135446Strhodes 2921135446Strhodes for (nh = ISC_LIST_HEAD(*list); 2922135446Strhodes nh != NULL; 2923135446Strhodes nh = ISC_LIST_NEXT(nh, plink)) 2924135446Strhodes { 2925135446Strhodes if (debug) 2926135446Strhodes fprintf(f, ";\tHook(%s) %p\n", legend, nh); 2927135446Strhodes dump_entry(f, nh->entry, debug, now); 2928135446Strhodes } 2929135446Strhodes} 2930135446Strhodes 2931135446Strhodesstatic inline void 2932135446Strhodesprint_fetch(FILE *f, dns_adbfetch_t *ft, const char *type) { 2933135446Strhodes fprintf(f, "\t\tFetch(%s): %p -> { nh %p, entry %p, fetch %p }\n", 2934135446Strhodes type, ft, ft->namehook, ft->entry, ft->fetch); 2935135446Strhodes} 2936135446Strhodes 2937135446Strhodesstatic void 2938135446Strhodesprint_fetch_list(FILE *f, dns_adbname_t *n) { 2939135446Strhodes if (NAME_FETCH_A(n)) 2940135446Strhodes print_fetch(f, n->fetch_a, "A"); 2941135446Strhodes if (NAME_FETCH_AAAA(n)) 2942135446Strhodes print_fetch(f, n->fetch_aaaa, "AAAA"); 2943135446Strhodes} 2944135446Strhodes 2945135446Strhodesstatic void 2946135446Strhodesprint_find_list(FILE *f, dns_adbname_t *name) { 2947135446Strhodes dns_adbfind_t *find; 2948135446Strhodes 2949135446Strhodes find = ISC_LIST_HEAD(name->finds); 2950135446Strhodes while (find != NULL) { 2951135446Strhodes dns_adb_dumpfind(find, f); 2952135446Strhodes find = ISC_LIST_NEXT(find, plink); 2953135446Strhodes } 2954135446Strhodes} 2955135446Strhodes 2956135446Strhodesstatic isc_result_t 2957135446Strhodesdbfind_name(dns_adbname_t *adbname, isc_stdtime_t now, dns_rdatatype_t rdtype) 2958135446Strhodes{ 2959135446Strhodes isc_result_t result; 2960135446Strhodes dns_rdataset_t rdataset; 2961135446Strhodes dns_adb_t *adb; 2962135446Strhodes dns_fixedname_t foundname; 2963135446Strhodes dns_name_t *fname; 2964135446Strhodes 2965135446Strhodes INSIST(DNS_ADBNAME_VALID(adbname)); 2966135446Strhodes adb = adbname->adb; 2967135446Strhodes INSIST(DNS_ADB_VALID(adb)); 2968135446Strhodes INSIST(rdtype == dns_rdatatype_a || rdtype == dns_rdatatype_aaaa); 2969135446Strhodes 2970135446Strhodes dns_fixedname_init(&foundname); 2971135446Strhodes fname = dns_fixedname_name(&foundname); 2972135446Strhodes dns_rdataset_init(&rdataset); 2973135446Strhodes 2974135446Strhodes if (rdtype == dns_rdatatype_a) 2975135446Strhodes adbname->fetch_err = FIND_ERR_UNEXPECTED; 2976135446Strhodes else 2977135446Strhodes adbname->fetch6_err = FIND_ERR_UNEXPECTED; 2978135446Strhodes 2979135446Strhodes result = dns_view_find(adb->view, &adbname->name, rdtype, now, 2980135446Strhodes NAME_GLUEOK(adbname), 2981135446Strhodes ISC_TF(NAME_HINTOK(adbname)), 2982135446Strhodes NULL, NULL, fname, &rdataset, NULL); 2983135446Strhodes 2984135446Strhodes /* XXXVIX this switch statement is too sparse to gen a jump table. */ 2985135446Strhodes switch (result) { 2986135446Strhodes case DNS_R_GLUE: 2987135446Strhodes case DNS_R_HINT: 2988135446Strhodes case ISC_R_SUCCESS: 2989135446Strhodes /* 2990135446Strhodes * Found in the database. Even if we can't copy out 2991135446Strhodes * any information, return success, or else a fetch 2992135446Strhodes * will be made, which will only make things worse. 2993135446Strhodes */ 2994135446Strhodes if (rdtype == dns_rdatatype_a) 2995135446Strhodes adbname->fetch_err = FIND_ERR_SUCCESS; 2996135446Strhodes else 2997135446Strhodes adbname->fetch6_err = FIND_ERR_SUCCESS; 2998135446Strhodes result = import_rdataset(adbname, &rdataset, now); 2999135446Strhodes break; 3000135446Strhodes case DNS_R_NXDOMAIN: 3001135446Strhodes case DNS_R_NXRRSET: 3002135446Strhodes /* 3003135446Strhodes * We're authoritative and the data doesn't exist. 3004135446Strhodes * Make up a negative cache entry so we don't ask again 3005135446Strhodes * for a while. 3006135446Strhodes * 3007135446Strhodes * XXXRTH What time should we use? I'm putting in 30 seconds 3008135446Strhodes * for now. 3009135446Strhodes */ 3010135446Strhodes if (rdtype == dns_rdatatype_a) { 3011135446Strhodes adbname->expire_v4 = now + 30; 3012135446Strhodes DP(NCACHE_LEVEL, 3013135446Strhodes "adb name %p: Caching auth negative entry for A", 3014135446Strhodes adbname); 3015135446Strhodes if (result == DNS_R_NXDOMAIN) 3016135446Strhodes adbname->fetch_err = FIND_ERR_NXDOMAIN; 3017135446Strhodes else 3018135446Strhodes adbname->fetch_err = FIND_ERR_NXRRSET; 3019135446Strhodes } else { 3020135446Strhodes DP(NCACHE_LEVEL, 3021135446Strhodes "adb name %p: Caching auth negative entry for AAAA", 3022135446Strhodes adbname); 3023135446Strhodes adbname->expire_v6 = now + 30; 3024135446Strhodes if (result == DNS_R_NXDOMAIN) 3025135446Strhodes adbname->fetch6_err = FIND_ERR_NXDOMAIN; 3026135446Strhodes else 3027135446Strhodes adbname->fetch6_err = FIND_ERR_NXRRSET; 3028135446Strhodes } 3029135446Strhodes break; 3030135446Strhodes case DNS_R_NCACHENXDOMAIN: 3031135446Strhodes case DNS_R_NCACHENXRRSET: 3032135446Strhodes /* 3033135446Strhodes * We found a negative cache entry. Pull the TTL from it 3034135446Strhodes * so we won't ask again for a while. 3035135446Strhodes */ 3036135446Strhodes rdataset.ttl = ttlclamp(rdataset.ttl); 3037135446Strhodes if (rdtype == dns_rdatatype_a) { 3038135446Strhodes adbname->expire_v4 = rdataset.ttl + now; 3039135446Strhodes if (result == DNS_R_NCACHENXDOMAIN) 3040135446Strhodes adbname->fetch_err = FIND_ERR_NXDOMAIN; 3041135446Strhodes else 3042135446Strhodes adbname->fetch_err = FIND_ERR_NXRRSET; 3043135446Strhodes DP(NCACHE_LEVEL, 3044135446Strhodes "adb name %p: Caching negative entry for A (ttl %u)", 3045135446Strhodes adbname, rdataset.ttl); 3046135446Strhodes } else { 3047135446Strhodes DP(NCACHE_LEVEL, 3048135446Strhodes "adb name %p: Caching negative entry for AAAA (ttl %u)", 3049135446Strhodes adbname, rdataset.ttl); 3050135446Strhodes adbname->expire_v6 = rdataset.ttl + now; 3051135446Strhodes if (result == DNS_R_NCACHENXDOMAIN) 3052135446Strhodes adbname->fetch6_err = FIND_ERR_NXDOMAIN; 3053135446Strhodes else 3054135446Strhodes adbname->fetch6_err = FIND_ERR_NXRRSET; 3055135446Strhodes } 3056135446Strhodes break; 3057135446Strhodes case DNS_R_CNAME: 3058135446Strhodes case DNS_R_DNAME: 3059135446Strhodes /* 3060135446Strhodes * Clear the hint and glue flags, so this will match 3061135446Strhodes * more often. 3062135446Strhodes */ 3063135446Strhodes adbname->flags &= ~(DNS_ADBFIND_GLUEOK | DNS_ADBFIND_HINTOK); 3064135446Strhodes 3065135446Strhodes rdataset.ttl = ttlclamp(rdataset.ttl); 3066135446Strhodes clean_target(adb, &adbname->target); 3067135446Strhodes adbname->expire_target = INT_MAX; 3068135446Strhodes result = set_target(adb, &adbname->name, fname, &rdataset, 3069135446Strhodes &adbname->target); 3070135446Strhodes if (result == ISC_R_SUCCESS) { 3071135446Strhodes result = DNS_R_ALIAS; 3072135446Strhodes DP(NCACHE_LEVEL, 3073135446Strhodes "adb name %p: caching alias target", 3074135446Strhodes adbname); 3075135446Strhodes adbname->expire_target = rdataset.ttl + now; 3076135446Strhodes } 3077135446Strhodes if (rdtype == dns_rdatatype_a) 3078135446Strhodes adbname->fetch_err = FIND_ERR_SUCCESS; 3079135446Strhodes else 3080135446Strhodes adbname->fetch6_err = FIND_ERR_SUCCESS; 3081135446Strhodes break; 3082135446Strhodes } 3083135446Strhodes 3084135446Strhodes if (dns_rdataset_isassociated(&rdataset)) 3085135446Strhodes dns_rdataset_disassociate(&rdataset); 3086135446Strhodes 3087135446Strhodes return (result); 3088135446Strhodes} 3089135446Strhodes 3090135446Strhodesstatic void 3091135446Strhodesfetch_callback(isc_task_t *task, isc_event_t *ev) { 3092135446Strhodes dns_fetchevent_t *dev; 3093135446Strhodes dns_adbname_t *name; 3094135446Strhodes dns_adb_t *adb; 3095135446Strhodes dns_adbfetch_t *fetch; 3096135446Strhodes int bucket; 3097135446Strhodes isc_eventtype_t ev_status; 3098135446Strhodes isc_stdtime_t now; 3099135446Strhodes isc_result_t result; 3100135446Strhodes unsigned int address_type; 3101135446Strhodes isc_boolean_t want_check_exit = ISC_FALSE; 3102135446Strhodes 3103135446Strhodes UNUSED(task); 3104135446Strhodes 3105135446Strhodes INSIST(ev->ev_type == DNS_EVENT_FETCHDONE); 3106135446Strhodes dev = (dns_fetchevent_t *)ev; 3107135446Strhodes name = ev->ev_arg; 3108135446Strhodes INSIST(DNS_ADBNAME_VALID(name)); 3109135446Strhodes adb = name->adb; 3110135446Strhodes INSIST(DNS_ADB_VALID(adb)); 3111135446Strhodes 3112135446Strhodes bucket = name->lock_bucket; 3113135446Strhodes LOCK(&adb->namelocks[bucket]); 3114135446Strhodes 3115135446Strhodes INSIST(NAME_FETCH_A(name) || NAME_FETCH_AAAA(name)); 3116135446Strhodes address_type = 0; 3117135446Strhodes if (NAME_FETCH_A(name) && (name->fetch_a->fetch == dev->fetch)) { 3118135446Strhodes address_type = DNS_ADBFIND_INET; 3119135446Strhodes fetch = name->fetch_a; 3120135446Strhodes name->fetch_a = NULL; 3121135446Strhodes } else if (NAME_FETCH_AAAA(name) 3122135446Strhodes && (name->fetch_aaaa->fetch == dev->fetch)) { 3123135446Strhodes address_type = DNS_ADBFIND_INET6; 3124135446Strhodes fetch = name->fetch_aaaa; 3125135446Strhodes name->fetch_aaaa = NULL; 3126135446Strhodes } 3127135446Strhodes INSIST(address_type != 0); 3128135446Strhodes 3129135446Strhodes dns_resolver_destroyfetch(&fetch->fetch); 3130135446Strhodes dev->fetch = NULL; 3131135446Strhodes 3132135446Strhodes ev_status = DNS_EVENT_ADBNOMOREADDRESSES; 3133135446Strhodes 3134135446Strhodes /* 3135135446Strhodes * Cleanup things we don't care about. 3136135446Strhodes */ 3137135446Strhodes if (dev->node != NULL) 3138135446Strhodes dns_db_detachnode(dev->db, &dev->node); 3139135446Strhodes if (dev->db != NULL) 3140135446Strhodes dns_db_detach(&dev->db); 3141135446Strhodes 3142135446Strhodes /* 3143135446Strhodes * If this name is marked as dead, clean up, throwing away 3144135446Strhodes * potentially good data. 3145135446Strhodes */ 3146135446Strhodes if (NAME_DEAD(name)) { 3147135446Strhodes free_adbfetch(adb, &fetch); 3148135446Strhodes isc_event_free(&ev); 3149135446Strhodes 3150135446Strhodes want_check_exit = kill_name(&name, DNS_EVENT_ADBCANCELED); 3151135446Strhodes 3152135446Strhodes UNLOCK(&adb->namelocks[bucket]); 3153135446Strhodes 3154135446Strhodes if (want_check_exit) { 3155135446Strhodes LOCK(&adb->lock); 3156135446Strhodes check_exit(adb); 3157135446Strhodes UNLOCK(&adb->lock); 3158135446Strhodes } 3159135446Strhodes 3160135446Strhodes return; 3161135446Strhodes } 3162135446Strhodes 3163135446Strhodes isc_stdtime_get(&now); 3164135446Strhodes 3165135446Strhodes /* 3166135446Strhodes * If we got a negative cache response, remember it. 3167135446Strhodes */ 3168135446Strhodes if (NCACHE_RESULT(dev->result)) { 3169135446Strhodes dev->rdataset->ttl = ttlclamp(dev->rdataset->ttl); 3170135446Strhodes if (address_type == DNS_ADBFIND_INET) { 3171135446Strhodes DP(NCACHE_LEVEL, "adb fetch name %p: " 3172135446Strhodes "caching negative entry for A (ttl %u)", 3173135446Strhodes name, dev->rdataset->ttl); 3174135446Strhodes name->expire_v4 = ISC_MIN(name->expire_v4, 3175135446Strhodes dev->rdataset->ttl + now); 3176135446Strhodes if (dev->result == DNS_R_NCACHENXDOMAIN) 3177135446Strhodes name->fetch_err = FIND_ERR_NXDOMAIN; 3178135446Strhodes else 3179135446Strhodes name->fetch_err = FIND_ERR_NXRRSET; 3180135446Strhodes } else { 3181135446Strhodes DP(NCACHE_LEVEL, "adb fetch name %p: " 3182135446Strhodes "caching negative entry for AAAA (ttl %u)", 3183135446Strhodes name, dev->rdataset->ttl); 3184135446Strhodes name->expire_v6 = ISC_MIN(name->expire_v6, 3185135446Strhodes dev->rdataset->ttl + now); 3186135446Strhodes if (dev->result == DNS_R_NCACHENXDOMAIN) 3187135446Strhodes name->fetch6_err = FIND_ERR_NXDOMAIN; 3188135446Strhodes else 3189135446Strhodes name->fetch6_err = FIND_ERR_NXRRSET; 3190135446Strhodes } 3191135446Strhodes goto out; 3192135446Strhodes } 3193135446Strhodes 3194135446Strhodes /* 3195135446Strhodes * Handle CNAME/DNAME. 3196135446Strhodes */ 3197135446Strhodes if (dev->result == DNS_R_CNAME || dev->result == DNS_R_DNAME) { 3198135446Strhodes dev->rdataset->ttl = ttlclamp(dev->rdataset->ttl); 3199135446Strhodes clean_target(adb, &name->target); 3200135446Strhodes name->expire_target = INT_MAX; 3201135446Strhodes result = set_target(adb, &name->name, 3202135446Strhodes dns_fixedname_name(&dev->foundname), 3203135446Strhodes dev->rdataset, 3204135446Strhodes &name->target); 3205135446Strhodes if (result == ISC_R_SUCCESS) { 3206135446Strhodes DP(NCACHE_LEVEL, 3207135446Strhodes "adb fetch name %p: caching alias target", 3208135446Strhodes name); 3209135446Strhodes name->expire_target = dev->rdataset->ttl + now; 3210135446Strhodes } 3211135446Strhodes goto check_result; 3212135446Strhodes } 3213135446Strhodes 3214135446Strhodes /* 3215135446Strhodes * Did we get back junk? If so, and there are no more fetches 3216135446Strhodes * sitting out there, tell all the finds about it. 3217135446Strhodes */ 3218135446Strhodes if (dev->result != ISC_R_SUCCESS) { 3219135446Strhodes char buf[DNS_NAME_FORMATSIZE]; 3220135446Strhodes 3221135446Strhodes dns_name_format(&name->name, buf, sizeof(buf)); 3222135446Strhodes DP(DEF_LEVEL, "adb: fetch of '%s' %s failed: %s", 3223135446Strhodes buf, address_type == DNS_ADBFIND_INET ? "A" : "AAAA", 3224135446Strhodes dns_result_totext(dev->result)); 3225135446Strhodes /* XXXMLG Don't pound on bad servers. */ 3226135446Strhodes if (address_type == DNS_ADBFIND_INET) { 3227135446Strhodes name->expire_v4 = ISC_MIN(name->expire_v4, now + 300); 3228135446Strhodes name->fetch_err = FIND_ERR_FAILURE; 3229135446Strhodes } else { 3230135446Strhodes name->expire_v6 = ISC_MIN(name->expire_v6, now + 300); 3231135446Strhodes name->fetch6_err = FIND_ERR_FAILURE; 3232135446Strhodes } 3233135446Strhodes goto out; 3234135446Strhodes } 3235135446Strhodes 3236135446Strhodes /* 3237135446Strhodes * We got something potentially useful. 3238135446Strhodes */ 3239135446Strhodes result = import_rdataset(name, &fetch->rdataset, now); 3240135446Strhodes 3241135446Strhodes check_result: 3242135446Strhodes if (result == ISC_R_SUCCESS) { 3243135446Strhodes ev_status = DNS_EVENT_ADBMOREADDRESSES; 3244135446Strhodes if (address_type == DNS_ADBFIND_INET) 3245135446Strhodes name->fetch_err = FIND_ERR_SUCCESS; 3246135446Strhodes else 3247135446Strhodes name->fetch6_err = FIND_ERR_SUCCESS; 3248135446Strhodes } 3249135446Strhodes 3250135446Strhodes out: 3251135446Strhodes free_adbfetch(adb, &fetch); 3252135446Strhodes isc_event_free(&ev); 3253135446Strhodes 3254135446Strhodes clean_finds_at_name(name, ev_status, address_type); 3255135446Strhodes 3256135446Strhodes UNLOCK(&adb->namelocks[bucket]); 3257135446Strhodes} 3258135446Strhodes 3259135446Strhodesstatic isc_result_t 3260135446Strhodesfetch_name(dns_adbname_t *adbname, 3261135446Strhodes isc_boolean_t start_at_zone, 3262135446Strhodes dns_rdatatype_t type) 3263135446Strhodes{ 3264135446Strhodes isc_result_t result; 3265135446Strhodes dns_adbfetch_t *fetch = NULL; 3266135446Strhodes dns_adb_t *adb; 3267135446Strhodes dns_fixedname_t fixed; 3268135446Strhodes dns_name_t *name; 3269135446Strhodes dns_rdataset_t rdataset; 3270135446Strhodes dns_rdataset_t *nameservers; 3271135446Strhodes unsigned int options; 3272135446Strhodes 3273135446Strhodes INSIST(DNS_ADBNAME_VALID(adbname)); 3274135446Strhodes adb = adbname->adb; 3275135446Strhodes INSIST(DNS_ADB_VALID(adb)); 3276135446Strhodes 3277135446Strhodes INSIST((type == dns_rdatatype_a && !NAME_FETCH_V4(adbname)) || 3278135446Strhodes (type == dns_rdatatype_aaaa && !NAME_FETCH_V6(adbname))); 3279135446Strhodes 3280135446Strhodes adbname->fetch_err = FIND_ERR_NOTFOUND; 3281135446Strhodes 3282135446Strhodes name = NULL; 3283135446Strhodes nameservers = NULL; 3284135446Strhodes dns_rdataset_init(&rdataset); 3285135446Strhodes 3286135446Strhodes options = DNS_FETCHOPT_NOVALIDATE; 3287135446Strhodes if (start_at_zone) { 3288135446Strhodes DP(ENTER_LEVEL, 3289135446Strhodes "fetch_name: starting at zone for name %p", 3290135446Strhodes adbname); 3291135446Strhodes dns_fixedname_init(&fixed); 3292135446Strhodes name = dns_fixedname_name(&fixed); 3293135446Strhodes result = dns_view_findzonecut2(adb->view, &adbname->name, name, 3294135446Strhodes 0, 0, ISC_TRUE, ISC_FALSE, 3295135446Strhodes &rdataset, NULL); 3296135446Strhodes if (result != ISC_R_SUCCESS && result != DNS_R_HINT) 3297135446Strhodes goto cleanup; 3298135446Strhodes nameservers = &rdataset; 3299135446Strhodes options |= DNS_FETCHOPT_UNSHARED; 3300135446Strhodes } 3301135446Strhodes 3302135446Strhodes fetch = new_adbfetch(adb); 3303135446Strhodes if (fetch == NULL) { 3304135446Strhodes result = ISC_R_NOMEMORY; 3305135446Strhodes goto cleanup; 3306135446Strhodes } 3307135446Strhodes 3308135446Strhodes result = dns_resolver_createfetch(adb->view->resolver, &adbname->name, 3309135446Strhodes type, name, nameservers, NULL, 3310135446Strhodes options, adb->task, fetch_callback, 3311135446Strhodes adbname, &fetch->rdataset, NULL, 3312135446Strhodes &fetch->fetch); 3313135446Strhodes if (result != ISC_R_SUCCESS) 3314135446Strhodes goto cleanup; 3315135446Strhodes 3316135446Strhodes if (type == dns_rdatatype_a) 3317135446Strhodes adbname->fetch_a = fetch; 3318135446Strhodes else 3319135446Strhodes adbname->fetch_aaaa = fetch; 3320135446Strhodes fetch = NULL; /* Keep us from cleaning this up below. */ 3321135446Strhodes 3322135446Strhodes cleanup: 3323135446Strhodes if (fetch != NULL) 3324135446Strhodes free_adbfetch(adb, &fetch); 3325135446Strhodes if (dns_rdataset_isassociated(&rdataset)) 3326135446Strhodes dns_rdataset_disassociate(&rdataset); 3327135446Strhodes 3328135446Strhodes return (result); 3329135446Strhodes} 3330135446Strhodes 3331135446Strhodes/* 3332135446Strhodes * XXXMLG Needs to take a find argument and an address info, no zone or adb, 3333135446Strhodes * since these can be extracted from the find itself. 3334135446Strhodes */ 3335135446Strhodesisc_result_t 3336135446Strhodesdns_adb_marklame(dns_adb_t *adb, dns_adbaddrinfo_t *addr, dns_name_t *zone, 3337135446Strhodes isc_stdtime_t expire_time) 3338135446Strhodes{ 3339135446Strhodes dns_adbzoneinfo_t *zi; 3340135446Strhodes int bucket; 3341135446Strhodes isc_result_t result = ISC_R_SUCCESS; 3342135446Strhodes 3343135446Strhodes REQUIRE(DNS_ADB_VALID(adb)); 3344135446Strhodes REQUIRE(DNS_ADBADDRINFO_VALID(addr)); 3345135446Strhodes REQUIRE(zone != NULL); 3346135446Strhodes 3347135446Strhodes bucket = addr->entry->lock_bucket; 3348135446Strhodes LOCK(&adb->entrylocks[bucket]); 3349135446Strhodes zi = ISC_LIST_HEAD(addr->entry->zoneinfo); 3350153816Sdougb while (zi != NULL && !dns_name_equal(zone, &zi->zone)) 3351135446Strhodes zi = ISC_LIST_NEXT(zi, plink); 3352135446Strhodes if (zi != NULL) { 3353135446Strhodes if (expire_time > zi->lame_timer) 3354135446Strhodes zi->lame_timer = expire_time; 3355135446Strhodes goto unlock; 3356135446Strhodes } 3357135446Strhodes zi = new_adbzoneinfo(adb, zone); 3358135446Strhodes if (zi == NULL) { 3359135446Strhodes result = ISC_R_NOMEMORY; 3360135446Strhodes goto unlock; 3361135446Strhodes } 3362135446Strhodes 3363135446Strhodes zi->lame_timer = expire_time; 3364135446Strhodes 3365135446Strhodes ISC_LIST_PREPEND(addr->entry->zoneinfo, zi, plink); 3366135446Strhodes unlock: 3367135446Strhodes UNLOCK(&adb->entrylocks[bucket]); 3368135446Strhodes 3369153816Sdougb return (result); 3370135446Strhodes} 3371135446Strhodes 3372135446Strhodesvoid 3373135446Strhodesdns_adb_adjustsrtt(dns_adb_t *adb, dns_adbaddrinfo_t *addr, 3374135446Strhodes unsigned int rtt, unsigned int factor) 3375135446Strhodes{ 3376135446Strhodes int bucket; 3377135446Strhodes unsigned int new_srtt; 3378135446Strhodes isc_stdtime_t now; 3379135446Strhodes 3380135446Strhodes REQUIRE(DNS_ADB_VALID(adb)); 3381135446Strhodes REQUIRE(DNS_ADBADDRINFO_VALID(addr)); 3382135446Strhodes REQUIRE(factor <= 10); 3383135446Strhodes 3384135446Strhodes bucket = addr->entry->lock_bucket; 3385135446Strhodes LOCK(&adb->entrylocks[bucket]); 3386135446Strhodes 3387135446Strhodes if (factor == DNS_ADB_RTTADJAGE) 3388135446Strhodes new_srtt = addr->entry->srtt * 98 / 100; 3389135446Strhodes else 3390135446Strhodes new_srtt = (addr->entry->srtt / 10 * factor) 3391135446Strhodes + (rtt / 10 * (10 - factor)); 3392135446Strhodes 3393135446Strhodes addr->entry->srtt = new_srtt; 3394135446Strhodes addr->srtt = new_srtt; 3395135446Strhodes 3396135446Strhodes isc_stdtime_get(&now); 3397135446Strhodes addr->entry->expires = now + ADB_ENTRY_WINDOW; 3398135446Strhodes 3399135446Strhodes UNLOCK(&adb->entrylocks[bucket]); 3400135446Strhodes} 3401135446Strhodes 3402135446Strhodesvoid 3403135446Strhodesdns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr, 3404135446Strhodes unsigned int bits, unsigned int mask) 3405135446Strhodes{ 3406135446Strhodes int bucket; 3407135446Strhodes 3408135446Strhodes REQUIRE(DNS_ADB_VALID(adb)); 3409135446Strhodes REQUIRE(DNS_ADBADDRINFO_VALID(addr)); 3410135446Strhodes 3411135446Strhodes bucket = addr->entry->lock_bucket; 3412135446Strhodes LOCK(&adb->entrylocks[bucket]); 3413135446Strhodes 3414135446Strhodes addr->entry->flags = (addr->entry->flags & ~mask) | (bits & mask); 3415135446Strhodes /* 3416135446Strhodes * Note that we do not update the other bits in addr->flags with 3417135446Strhodes * the most recent values from addr->entry->flags. 3418135446Strhodes */ 3419135446Strhodes addr->flags = (addr->flags & ~mask) | (bits & mask); 3420135446Strhodes 3421135446Strhodes UNLOCK(&adb->entrylocks[bucket]); 3422135446Strhodes} 3423135446Strhodes 3424135446Strhodesisc_result_t 3425135446Strhodesdns_adb_findaddrinfo(dns_adb_t *adb, isc_sockaddr_t *sa, 3426135446Strhodes dns_adbaddrinfo_t **addrp, isc_stdtime_t now) 3427135446Strhodes{ 3428135446Strhodes int bucket; 3429135446Strhodes dns_adbentry_t *entry; 3430135446Strhodes dns_adbaddrinfo_t *addr; 3431135446Strhodes isc_result_t result; 3432135446Strhodes in_port_t port; 3433135446Strhodes 3434135446Strhodes REQUIRE(DNS_ADB_VALID(adb)); 3435135446Strhodes REQUIRE(addrp != NULL && *addrp == NULL); 3436135446Strhodes 3437135446Strhodes UNUSED(now); 3438135446Strhodes 3439135446Strhodes result = ISC_R_SUCCESS; 3440135446Strhodes bucket = DNS_ADB_INVALIDBUCKET; 3441135446Strhodes entry = find_entry_and_lock(adb, sa, &bucket); 3442135446Strhodes if (adb->entry_sd[bucket]) { 3443135446Strhodes result = ISC_R_SHUTTINGDOWN; 3444135446Strhodes goto unlock; 3445135446Strhodes } 3446135446Strhodes if (entry == NULL) { 3447135446Strhodes /* 3448135446Strhodes * We don't know anything about this address. 3449135446Strhodes */ 3450135446Strhodes entry = new_adbentry(adb); 3451135446Strhodes if (entry == NULL) { 3452135446Strhodes result = ISC_R_NOMEMORY; 3453135446Strhodes goto unlock; 3454135446Strhodes } 3455135446Strhodes entry->sockaddr = *sa; 3456135446Strhodes link_entry(adb, bucket, entry); 3457135446Strhodes DP(ENTER_LEVEL, "findaddrinfo: new entry %p", entry); 3458135446Strhodes } else 3459135446Strhodes DP(ENTER_LEVEL, "findaddrinfo: found entry %p", entry); 3460135446Strhodes 3461135446Strhodes port = isc_sockaddr_getport(sa); 3462135446Strhodes addr = new_adbaddrinfo(adb, entry, port); 3463135446Strhodes if (addr != NULL) { 3464135446Strhodes inc_entry_refcnt(adb, entry, ISC_FALSE); 3465135446Strhodes *addrp = addr; 3466135446Strhodes } 3467135446Strhodes 3468135446Strhodes unlock: 3469135446Strhodes UNLOCK(&adb->entrylocks[bucket]); 3470135446Strhodes 3471135446Strhodes return (result); 3472135446Strhodes} 3473135446Strhodes 3474135446Strhodesvoid 3475135446Strhodesdns_adb_freeaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **addrp) { 3476135446Strhodes dns_adbaddrinfo_t *addr; 3477135446Strhodes dns_adbentry_t *entry; 3478135446Strhodes int bucket; 3479135446Strhodes isc_stdtime_t now; 3480135446Strhodes isc_boolean_t want_check_exit = ISC_FALSE; 3481135446Strhodes 3482135446Strhodes REQUIRE(DNS_ADB_VALID(adb)); 3483135446Strhodes REQUIRE(addrp != NULL); 3484135446Strhodes addr = *addrp; 3485135446Strhodes REQUIRE(DNS_ADBADDRINFO_VALID(addr)); 3486135446Strhodes entry = addr->entry; 3487135446Strhodes REQUIRE(DNS_ADBENTRY_VALID(entry)); 3488135446Strhodes 3489135446Strhodes isc_stdtime_get(&now); 3490135446Strhodes 3491135446Strhodes *addrp = NULL; 3492135446Strhodes 3493135446Strhodes bucket = addr->entry->lock_bucket; 3494135446Strhodes LOCK(&adb->entrylocks[bucket]); 3495135446Strhodes 3496135446Strhodes entry->expires = now + ADB_ENTRY_WINDOW; 3497135446Strhodes 3498135446Strhodes want_check_exit = dec_entry_refcnt(adb, entry, ISC_FALSE); 3499135446Strhodes 3500135446Strhodes UNLOCK(&adb->entrylocks[bucket]); 3501135446Strhodes 3502135446Strhodes addr->entry = NULL; 3503135446Strhodes free_adbaddrinfo(adb, &addr); 3504135446Strhodes 3505135446Strhodes if (want_check_exit) { 3506135446Strhodes LOCK(&adb->lock); 3507135446Strhodes check_exit(adb); 3508135446Strhodes UNLOCK(&adb->lock); 3509135446Strhodes } 3510135446Strhodes} 3511135446Strhodes 3512135446Strhodesvoid 3513135446Strhodesdns_adb_flush(dns_adb_t *adb) { 3514135446Strhodes unsigned int i; 3515135446Strhodes 3516135446Strhodes INSIST(DNS_ADB_VALID(adb)); 3517135446Strhodes 3518135446Strhodes LOCK(&adb->lock); 3519135446Strhodes 3520143731Sdougb /* 3521143731Sdougb * Call our cleanup routines. 3522143731Sdougb */ 3523143731Sdougb for (i = 0; i < NBUCKETS; i++) 3524135446Strhodes RUNTIME_CHECK(cleanup_names(adb, i, INT_MAX) == ISC_FALSE); 3525143731Sdougb for (i = 0; i < NBUCKETS; i++) 3526135446Strhodes RUNTIME_CHECK(cleanup_entries(adb, i, INT_MAX) == ISC_FALSE); 3527135446Strhodes 3528135446Strhodes#ifdef DUMP_ADB_AFTER_CLEANING 3529143731Sdougb dump_adb(adb, stdout, ISC_TRUE, INT_MAX); 3530135446Strhodes#endif 3531135446Strhodes 3532135446Strhodes UNLOCK(&adb->lock); 3533135446Strhodes} 3534135446Strhodes 3535135446Strhodesvoid 3536135446Strhodesdns_adb_flushname(dns_adb_t *adb, dns_name_t *name) { 3537135446Strhodes dns_adbname_t *adbname; 3538135446Strhodes dns_adbname_t *nextname; 3539135446Strhodes int bucket; 3540135446Strhodes 3541135446Strhodes INSIST(DNS_ADB_VALID(adb)); 3542135446Strhodes 3543135446Strhodes LOCK(&adb->lock); 3544135446Strhodes bucket = dns_name_hash(name, ISC_FALSE) % NBUCKETS; 3545135446Strhodes LOCK(&adb->namelocks[bucket]); 3546135446Strhodes adbname = ISC_LIST_HEAD(adb->names[bucket]); 3547135446Strhodes while (adbname != NULL) { 3548135446Strhodes nextname = ISC_LIST_NEXT(adbname, plink); 3549135446Strhodes if (!NAME_DEAD(adbname) && 3550135446Strhodes dns_name_equal(name, &adbname->name)) { 3551135446Strhodes RUNTIME_CHECK(kill_name(&adbname, 3552135446Strhodes DNS_EVENT_ADBCANCELED) == 3553135446Strhodes ISC_FALSE); 3554135446Strhodes } 3555135446Strhodes adbname = nextname; 3556135446Strhodes } 3557135446Strhodes UNLOCK(&adb->namelocks[bucket]); 3558135446Strhodes UNLOCK(&adb->lock); 3559135446Strhodes} 3560135446Strhodes 3561135446Strhodesstatic void 3562135446Strhodeswater(void *arg, int mark) { 3563135446Strhodes dns_adb_t *adb = arg; 3564135446Strhodes isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER); 3565135446Strhodes isc_interval_t interval; 3566135446Strhodes 3567135446Strhodes REQUIRE(DNS_ADB_VALID(adb)); 3568135446Strhodes 3569135446Strhodes DP(ISC_LOG_DEBUG(1), 3570135446Strhodes "adb reached %s water mark", overmem ? "high" : "low"); 3571135446Strhodes 3572135446Strhodes adb->overmem = overmem; 3573135446Strhodes if (overmem) { 3574135446Strhodes isc_interval_set(&interval, 0, 1); 3575135446Strhodes (void)isc_timer_reset(adb->timer, isc_timertype_once, NULL, 3576135446Strhodes &interval, ISC_TRUE); 3577135446Strhodes } 3578135446Strhodes} 3579135446Strhodes 3580135446Strhodesvoid 3581135446Strhodesdns_adb_setadbsize(dns_adb_t *adb, isc_uint32_t size) { 3582135446Strhodes isc_uint32_t hiwater; 3583135446Strhodes isc_uint32_t lowater; 3584135446Strhodes 3585135446Strhodes INSIST(DNS_ADB_VALID(adb)); 3586135446Strhodes 3587135446Strhodes if (size != 0 && size < DNS_ADB_MINADBSIZE) 3588135446Strhodes size = DNS_ADB_MINADBSIZE; 3589135446Strhodes 3590135446Strhodes hiwater = size - (size >> 3); /* Approximately 7/8ths. */ 3591135446Strhodes lowater = size - (size >> 2); /* Approximately 3/4ths. */ 3592135446Strhodes 3593135446Strhodes if (size == 0 || hiwater == 0 || lowater == 0) 3594135446Strhodes isc_mem_setwater(adb->mctx, water, adb, 0, 0); 3595135446Strhodes else 3596135446Strhodes isc_mem_setwater(adb->mctx, water, adb, hiwater, lowater); 3597135446Strhodes} 3598