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