adb.c revision 193149
1135446Strhodes/* 2193149Sdougb * Copyright (C) 2004-2009 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 18193149Sdougb/* $Id: adb.c,v 1.243.42.4 2009/02/03 22:34:28 jinmei Exp $ */ 19135446Strhodes 20186462Sdougb/*! \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 29135446Strhodes#include <config.h> 30135446Strhodes 31135446Strhodes#include <limits.h> 32135446Strhodes 33135446Strhodes#include <isc/mutexblock.h> 34135446Strhodes#include <isc/netaddr.h> 35135446Strhodes#include <isc/random.h> 36193149Sdougb#include <isc/stats.h> 37193149Sdougb#include <isc/string.h> /* Required for HP/UX (and others?) */ 38135446Strhodes#include <isc/task.h> 39135446Strhodes#include <isc/util.h> 40135446Strhodes 41135446Strhodes#include <dns/adb.h> 42135446Strhodes#include <dns/db.h> 43135446Strhodes#include <dns/events.h> 44135446Strhodes#include <dns/log.h> 45135446Strhodes#include <dns/rdata.h> 46135446Strhodes#include <dns/rdataset.h> 47135446Strhodes#include <dns/rdatastruct.h> 48170222Sdougb#include <dns/rdatatype.h> 49135446Strhodes#include <dns/resolver.h> 50135446Strhodes#include <dns/result.h> 51193149Sdougb#include <dns/stats.h> 52135446Strhodes 53193149Sdougb#define DNS_ADB_MAGIC ISC_MAGIC('D', 'a', 'd', 'b') 54193149Sdougb#define DNS_ADB_VALID(x) ISC_MAGIC_VALID(x, DNS_ADB_MAGIC) 55193149Sdougb#define DNS_ADBNAME_MAGIC ISC_MAGIC('a', 'd', 'b', 'N') 56193149Sdougb#define DNS_ADBNAME_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBNAME_MAGIC) 57193149Sdougb#define DNS_ADBNAMEHOOK_MAGIC ISC_MAGIC('a', 'd', 'N', 'H') 58135446Strhodes#define DNS_ADBNAMEHOOK_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBNAMEHOOK_MAGIC) 59193149Sdougb#define DNS_ADBLAMEINFO_MAGIC ISC_MAGIC('a', 'd', 'b', 'Z') 60170222Sdougb#define DNS_ADBLAMEINFO_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBLAMEINFO_MAGIC) 61193149Sdougb#define DNS_ADBENTRY_MAGIC ISC_MAGIC('a', 'd', 'b', 'E') 62193149Sdougb#define DNS_ADBENTRY_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBENTRY_MAGIC) 63193149Sdougb#define DNS_ADBFETCH_MAGIC ISC_MAGIC('a', 'd', 'F', '4') 64193149Sdougb#define DNS_ADBFETCH_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBFETCH_MAGIC) 65193149Sdougb#define DNS_ADBFETCH6_MAGIC ISC_MAGIC('a', 'd', 'F', '6') 66193149Sdougb#define DNS_ADBFETCH6_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBFETCH6_MAGIC) 67135446Strhodes 68186462Sdougb/*! 69135446Strhodes * The number of buckets needs to be a prime (for good hashing). 70135446Strhodes * 71135446Strhodes * XXXRTH How many buckets do we need? 72135446Strhodes */ 73193149Sdougb#define NBUCKETS 1009 /*%< how many buckets for names/addrs */ 74135446Strhodes 75170222Sdougb/*! 76135446Strhodes * For type 3 negative cache entries, we will remember that the address is 77135446Strhodes * broken for this long. XXXMLG This is also used for actual addresses, too. 78135446Strhodes * The intent is to keep us from constantly asking about A/AAAA records 79135446Strhodes * if the zone has extremely low TTLs. 80135446Strhodes */ 81193149Sdougb#define ADB_CACHE_MINIMUM 10 /*%< seconds */ 82193149Sdougb#define ADB_CACHE_MAXIMUM 86400 /*%< seconds (86400 = 24 hours) */ 83193149Sdougb#define ADB_ENTRY_WINDOW 1800 /*%< seconds */ 84135446Strhodes 85170222Sdougb/*% 86193149Sdougb * The period in seconds after which an ADB name entry is regarded as stale 87193149Sdougb * and forced to be cleaned up. 88193149Sdougb * TODO: This should probably be configurable at run-time. 89135446Strhodes */ 90193149Sdougb#ifndef ADB_STALE_MARGIN 91193149Sdougb#define ADB_STALE_MARGIN 1800 92193149Sdougb#endif 93135446Strhodes 94193149Sdougb#define FREE_ITEMS 64 /*%< free count for memory pools */ 95193149Sdougb#define FILL_COUNT 16 /*%< fill count for memory pools */ 96135446Strhodes 97193149Sdougb#define DNS_ADB_INVALIDBUCKET (-1) /*%< invalid bucket address */ 98135446Strhodes 99193149Sdougb#define DNS_ADB_MINADBSIZE (1024*1024) /*%< 1 Megabyte */ 100135446Strhodes 101135446Strhodestypedef ISC_LIST(dns_adbname_t) dns_adbnamelist_t; 102135446Strhodestypedef struct dns_adbnamehook dns_adbnamehook_t; 103135446Strhodestypedef ISC_LIST(dns_adbnamehook_t) dns_adbnamehooklist_t; 104170222Sdougbtypedef struct dns_adblameinfo dns_adblameinfo_t; 105135446Strhodestypedef ISC_LIST(dns_adbentry_t) dns_adbentrylist_t; 106135446Strhodestypedef struct dns_adbfetch dns_adbfetch_t; 107135446Strhodestypedef struct dns_adbfetch6 dns_adbfetch6_t; 108135446Strhodes 109170222Sdougb/*% dns adb structure */ 110135446Strhodesstruct dns_adb { 111193149Sdougb unsigned int magic; 112135446Strhodes 113193149Sdougb isc_mutex_t lock; 114193149Sdougb isc_mutex_t reflock; /*%< Covers irefcnt, erefcnt */ 115186462Sdougb isc_mutex_t overmemlock; /*%< Covers overmem */ 116193149Sdougb isc_mem_t *mctx; 117193149Sdougb dns_view_t *view; 118135446Strhodes 119193149Sdougb isc_taskmgr_t *taskmgr; 120193149Sdougb isc_task_t *task; 121193149Sdougb isc_boolean_t overmem; 122135446Strhodes 123193149Sdougb isc_interval_t tick_interval; 124193149Sdougb int next_cleanbucket; 125135446Strhodes 126193149Sdougb unsigned int irefcnt; 127193149Sdougb unsigned int erefcnt; 128135446Strhodes 129193149Sdougb isc_mutex_t mplock; 130193149Sdougb isc_mempool_t *nmp; /*%< dns_adbname_t */ 131193149Sdougb isc_mempool_t *nhmp; /*%< dns_adbnamehook_t */ 132193149Sdougb isc_mempool_t *limp; /*%< dns_adblameinfo_t */ 133193149Sdougb isc_mempool_t *emp; /*%< dns_adbentry_t */ 134193149Sdougb isc_mempool_t *ahmp; /*%< dns_adbfind_t */ 135193149Sdougb isc_mempool_t *aimp; /*%< dns_adbaddrinfo_t */ 136193149Sdougb isc_mempool_t *afmp; /*%< dns_adbfetch_t */ 137193149Sdougb 138170222Sdougb /*! 139135446Strhodes * Bucketized locks and lists for names. 140135446Strhodes * 141135446Strhodes * XXXRTH Have a per-bucket structure that contains all of these? 142135446Strhodes */ 143193149Sdougb dns_adbnamelist_t names[NBUCKETS]; 144193149Sdougb dns_adbnamelist_t deadnames[NBUCKETS]; 145170222Sdougb /*% See dns_adbnamelist_t */ 146193149Sdougb isc_mutex_t namelocks[NBUCKETS]; 147170222Sdougb /*% See dns_adbnamelist_t */ 148193149Sdougb isc_boolean_t name_sd[NBUCKETS]; 149170222Sdougb /*% See dns_adbnamelist_t */ 150193149Sdougb unsigned int name_refcnt[NBUCKETS]; 151135446Strhodes 152170222Sdougb /*! 153135446Strhodes * Bucketized locks for entries. 154135446Strhodes * 155135446Strhodes * XXXRTH Have a per-bucket structure that contains all of these? 156135446Strhodes */ 157193149Sdougb dns_adbentrylist_t entries[NBUCKETS]; 158193149Sdougb dns_adbentrylist_t deadentries[NBUCKETS]; 159193149Sdougb isc_mutex_t entrylocks[NBUCKETS]; 160193149Sdougb isc_boolean_t entry_sd[NBUCKETS]; /*%< shutting down */ 161193149Sdougb unsigned int entry_refcnt[NBUCKETS]; 162135446Strhodes 163193149Sdougb isc_event_t cevent; 164193149Sdougb isc_boolean_t cevent_sent; 165193149Sdougb isc_boolean_t shutting_down; 166193149Sdougb isc_eventlist_t whenshutdown; 167135446Strhodes}; 168135446Strhodes 169135446Strhodes/* 170135446Strhodes * XXXMLG Document these structures. 171135446Strhodes */ 172135446Strhodes 173170222Sdougb/*% dns_adbname structure */ 174135446Strhodesstruct dns_adbname { 175193149Sdougb unsigned int magic; 176193149Sdougb dns_name_t name; 177193149Sdougb dns_adb_t *adb; 178193149Sdougb unsigned int partial_result; 179193149Sdougb unsigned int flags; 180193149Sdougb int lock_bucket; 181193149Sdougb dns_name_t target; 182193149Sdougb isc_stdtime_t expire_target; 183193149Sdougb isc_stdtime_t expire_v4; 184193149Sdougb isc_stdtime_t expire_v6; 185193149Sdougb unsigned int chains; 186193149Sdougb dns_adbnamehooklist_t v4; 187193149Sdougb dns_adbnamehooklist_t v6; 188193149Sdougb dns_adbfetch_t *fetch_a; 189193149Sdougb dns_adbfetch_t *fetch_aaaa; 190193149Sdougb unsigned int fetch_err; 191193149Sdougb unsigned int fetch6_err; 192193149Sdougb dns_adbfindlist_t finds; 193193149Sdougb /* for LRU-based management */ 194193149Sdougb isc_stdtime_t last_used; 195193149Sdougb 196193149Sdougb ISC_LINK(dns_adbname_t) plink; 197135446Strhodes}; 198135446Strhodes 199170222Sdougb/*% The adbfetch structure */ 200135446Strhodesstruct dns_adbfetch { 201193149Sdougb unsigned int magic; 202193149Sdougb dns_fetch_t *fetch; 203193149Sdougb dns_rdataset_t rdataset; 204135446Strhodes}; 205135446Strhodes 206170222Sdougb/*% 207135446Strhodes * This is a small widget that dangles off a dns_adbname_t. It contains a 208135446Strhodes * pointer to the address information about this host, and a link to the next 209135446Strhodes * namehook that will contain the next address this host has. 210135446Strhodes */ 211135446Strhodesstruct dns_adbnamehook { 212193149Sdougb unsigned int magic; 213193149Sdougb dns_adbentry_t *entry; 214193149Sdougb ISC_LINK(dns_adbnamehook_t) plink; 215135446Strhodes}; 216135446Strhodes 217170222Sdougb/*% 218170222Sdougb * This is a small widget that holds qname-specific information about an 219135446Strhodes * address. Currently limited to lameness, but could just as easily be 220135446Strhodes * extended to other types of information about zones. 221135446Strhodes */ 222170222Sdougbstruct dns_adblameinfo { 223193149Sdougb unsigned int magic; 224135446Strhodes 225193149Sdougb dns_name_t qname; 226193149Sdougb dns_rdatatype_t qtype; 227193149Sdougb isc_stdtime_t lame_timer; 228135446Strhodes 229193149Sdougb ISC_LINK(dns_adblameinfo_t) plink; 230135446Strhodes}; 231135446Strhodes 232170222Sdougb/*% 233135446Strhodes * An address entry. It holds quite a bit of information about addresses, 234135446Strhodes * including edns state (in "flags"), rtt, and of course the address of 235135446Strhodes * the host. 236135446Strhodes */ 237135446Strhodesstruct dns_adbentry { 238193149Sdougb unsigned int magic; 239135446Strhodes 240193149Sdougb int lock_bucket; 241193149Sdougb unsigned int refcnt; 242135446Strhodes 243193149Sdougb unsigned int flags; 244193149Sdougb unsigned int srtt; 245193149Sdougb isc_sockaddr_t sockaddr; 246135446Strhodes 247193149Sdougb isc_stdtime_t expires; 248170222Sdougb /*%< 249135446Strhodes * A nonzero 'expires' field indicates that the entry should 250135446Strhodes * persist until that time. This allows entries found 251135446Strhodes * using dns_adb_findaddrinfo() to persist for a limited time 252135446Strhodes * even though they are not necessarily associated with a 253135446Strhodes * name. 254135446Strhodes */ 255135446Strhodes 256193149Sdougb ISC_LIST(dns_adblameinfo_t) lameinfo; 257193149Sdougb ISC_LINK(dns_adbentry_t) plink; 258135446Strhodes}; 259135446Strhodes 260135446Strhodes/* 261135446Strhodes * Internal functions (and prototypes). 262135446Strhodes */ 263135446Strhodesstatic inline dns_adbname_t *new_adbname(dns_adb_t *, dns_name_t *); 264135446Strhodesstatic inline void free_adbname(dns_adb_t *, dns_adbname_t **); 265135446Strhodesstatic inline dns_adbnamehook_t *new_adbnamehook(dns_adb_t *, 266135446Strhodes dns_adbentry_t *); 267135446Strhodesstatic inline void free_adbnamehook(dns_adb_t *, dns_adbnamehook_t **); 268170222Sdougbstatic inline dns_adblameinfo_t *new_adblameinfo(dns_adb_t *, dns_name_t *, 269170222Sdougb dns_rdatatype_t); 270170222Sdougbstatic inline void free_adblameinfo(dns_adb_t *, dns_adblameinfo_t **); 271135446Strhodesstatic inline dns_adbentry_t *new_adbentry(dns_adb_t *); 272135446Strhodesstatic inline void free_adbentry(dns_adb_t *, dns_adbentry_t **); 273135446Strhodesstatic inline dns_adbfind_t *new_adbfind(dns_adb_t *); 274135446Strhodesstatic inline isc_boolean_t free_adbfind(dns_adb_t *, dns_adbfind_t **); 275135446Strhodesstatic inline dns_adbaddrinfo_t *new_adbaddrinfo(dns_adb_t *, dns_adbentry_t *, 276135446Strhodes in_port_t); 277135446Strhodesstatic inline dns_adbfetch_t *new_adbfetch(dns_adb_t *); 278135446Strhodesstatic inline void free_adbfetch(dns_adb_t *, dns_adbfetch_t **); 279135446Strhodesstatic inline dns_adbname_t *find_name_and_lock(dns_adb_t *, dns_name_t *, 280135446Strhodes unsigned int, int *); 281135446Strhodesstatic inline dns_adbentry_t *find_entry_and_lock(dns_adb_t *, 282193149Sdougb isc_sockaddr_t *, int *, 283193149Sdougb isc_stdtime_t); 284143731Sdougbstatic void dump_adb(dns_adb_t *, FILE *, isc_boolean_t debug, isc_stdtime_t); 285135446Strhodesstatic void print_dns_name(FILE *, dns_name_t *); 286135446Strhodesstatic void print_namehook_list(FILE *, const char *legend, 287135446Strhodes dns_adbnamehooklist_t *list, 288135446Strhodes isc_boolean_t debug, 289135446Strhodes isc_stdtime_t now); 290135446Strhodesstatic void print_find_list(FILE *, dns_adbname_t *); 291135446Strhodesstatic void print_fetch_list(FILE *, dns_adbname_t *); 292135446Strhodesstatic inline isc_boolean_t dec_adb_irefcnt(dns_adb_t *); 293135446Strhodesstatic inline void inc_adb_irefcnt(dns_adb_t *); 294135446Strhodesstatic inline void inc_adb_erefcnt(dns_adb_t *); 295135446Strhodesstatic inline void inc_entry_refcnt(dns_adb_t *, dns_adbentry_t *, 296135446Strhodes isc_boolean_t); 297135446Strhodesstatic inline isc_boolean_t dec_entry_refcnt(dns_adb_t *, dns_adbentry_t *, 298135446Strhodes isc_boolean_t); 299135446Strhodesstatic inline void violate_locking_hierarchy(isc_mutex_t *, isc_mutex_t *); 300135446Strhodesstatic isc_boolean_t clean_namehooks(dns_adb_t *, dns_adbnamehooklist_t *); 301135446Strhodesstatic void clean_target(dns_adb_t *, dns_name_t *); 302135446Strhodesstatic void clean_finds_at_name(dns_adbname_t *, isc_eventtype_t, 303135446Strhodes unsigned int); 304193149Sdougbstatic isc_boolean_t check_expire_namehooks(dns_adbname_t *, isc_stdtime_t); 305193149Sdougbstatic isc_boolean_t check_expire_entry(dns_adb_t *, dns_adbentry_t **, 306193149Sdougb isc_stdtime_t); 307135446Strhodesstatic void cancel_fetches_at_name(dns_adbname_t *); 308135446Strhodesstatic isc_result_t dbfind_name(dns_adbname_t *, isc_stdtime_t, 309135446Strhodes dns_rdatatype_t); 310135446Strhodesstatic isc_result_t fetch_name(dns_adbname_t *, isc_boolean_t, 311135446Strhodes dns_rdatatype_t); 312135446Strhodesstatic inline void check_exit(dns_adb_t *); 313135446Strhodesstatic void destroy(dns_adb_t *); 314135446Strhodesstatic isc_boolean_t shutdown_names(dns_adb_t *); 315135446Strhodesstatic isc_boolean_t shutdown_entries(dns_adb_t *); 316135446Strhodesstatic inline void link_name(dns_adb_t *, int, dns_adbname_t *); 317135446Strhodesstatic inline isc_boolean_t unlink_name(dns_adb_t *, dns_adbname_t *); 318135446Strhodesstatic inline void link_entry(dns_adb_t *, int, dns_adbentry_t *); 319135446Strhodesstatic inline isc_boolean_t unlink_entry(dns_adb_t *, dns_adbentry_t *); 320135446Strhodesstatic isc_boolean_t kill_name(dns_adbname_t **, isc_eventtype_t); 321143731Sdougbstatic void water(void *, int); 322143731Sdougbstatic void dump_entry(FILE *, dns_adbentry_t *, isc_boolean_t, isc_stdtime_t); 323135446Strhodes 324135446Strhodes/* 325135446Strhodes * MUST NOT overlap DNS_ADBFIND_* flags! 326135446Strhodes */ 327193149Sdougb#define FIND_EVENT_SENT 0x40000000 328193149Sdougb#define FIND_EVENT_FREED 0x80000000 329193149Sdougb#define FIND_EVENTSENT(h) (((h)->flags & FIND_EVENT_SENT) != 0) 330193149Sdougb#define FIND_EVENTFREED(h) (((h)->flags & FIND_EVENT_FREED) != 0) 331135446Strhodes 332193149Sdougb#define NAME_NEEDS_POKE 0x80000000 333193149Sdougb#define NAME_IS_DEAD 0x40000000 334193149Sdougb#define NAME_HINT_OK DNS_ADBFIND_HINTOK 335193149Sdougb#define NAME_GLUE_OK DNS_ADBFIND_GLUEOK 336193149Sdougb#define NAME_STARTATZONE DNS_ADBFIND_STARTATZONE 337193149Sdougb#define NAME_DEAD(n) (((n)->flags & NAME_IS_DEAD) != 0) 338193149Sdougb#define NAME_NEEDSPOKE(n) (((n)->flags & NAME_NEEDS_POKE) != 0) 339193149Sdougb#define NAME_GLUEOK(n) (((n)->flags & NAME_GLUE_OK) != 0) 340193149Sdougb#define NAME_HINTOK(n) (((n)->flags & NAME_HINT_OK) != 0) 341135446Strhodes 342135446Strhodes/* 343193149Sdougb * Private flag(s) for entries. 344193149Sdougb * MUST NOT overlap FCTX_ADDRINFO_xxx and DNS_FETCHOPT_NOEDNS0. 345193149Sdougb */ 346193149Sdougb#define ENTRY_IS_DEAD 0x80000000 347193149Sdougb 348193149Sdougb/* 349135446Strhodes * To the name, address classes are all that really exist. If it has a 350135446Strhodes * V6 address it doesn't care if it came from a AAAA query. 351135446Strhodes */ 352193149Sdougb#define NAME_HAS_V4(n) (!ISC_LIST_EMPTY((n)->v4)) 353193149Sdougb#define NAME_HAS_V6(n) (!ISC_LIST_EMPTY((n)->v6)) 354193149Sdougb#define NAME_HAS_ADDRS(n) (NAME_HAS_V4(n) || NAME_HAS_V6(n)) 355135446Strhodes 356135446Strhodes/* 357135446Strhodes * Fetches are broken out into A and AAAA types. In some cases, 358135446Strhodes * however, it makes more sense to test for a particular class of fetches, 359135446Strhodes * like V4 or V6 above. 360135446Strhodes * Note: since we have removed the support of A6 in adb, FETCH_A and FETCH_AAAA 361135446Strhodes * are now equal to FETCH_V4 and FETCH_V6, respectively. 362135446Strhodes */ 363193149Sdougb#define NAME_FETCH_A(n) ((n)->fetch_a != NULL) 364193149Sdougb#define NAME_FETCH_AAAA(n) ((n)->fetch_aaaa != NULL) 365193149Sdougb#define NAME_FETCH_V4(n) (NAME_FETCH_A(n)) 366193149Sdougb#define NAME_FETCH_V6(n) (NAME_FETCH_AAAA(n)) 367193149Sdougb#define NAME_FETCH(n) (NAME_FETCH_V4(n) || NAME_FETCH_V6(n)) 368135446Strhodes 369135446Strhodes/* 370135446Strhodes * Find options and tests to see if there are addresses on the list. 371135446Strhodes */ 372193149Sdougb#define FIND_WANTEVENT(fn) (((fn)->options & DNS_ADBFIND_WANTEVENT) != 0) 373193149Sdougb#define FIND_WANTEMPTYEVENT(fn) (((fn)->options & DNS_ADBFIND_EMPTYEVENT) != 0) 374193149Sdougb#define FIND_AVOIDFETCHES(fn) (((fn)->options & DNS_ADBFIND_AVOIDFETCHES) \ 375135446Strhodes != 0) 376193149Sdougb#define FIND_STARTATZONE(fn) (((fn)->options & DNS_ADBFIND_STARTATZONE) \ 377135446Strhodes != 0) 378193149Sdougb#define FIND_HINTOK(fn) (((fn)->options & DNS_ADBFIND_HINTOK) != 0) 379193149Sdougb#define FIND_GLUEOK(fn) (((fn)->options & DNS_ADBFIND_GLUEOK) != 0) 380193149Sdougb#define FIND_HAS_ADDRS(fn) (!ISC_LIST_EMPTY((fn)->list)) 381193149Sdougb#define FIND_RETURNLAME(fn) (((fn)->options & DNS_ADBFIND_RETURNLAME) != 0) 382135446Strhodes 383135446Strhodes/* 384135446Strhodes * These are currently used on simple unsigned ints, so they are 385135446Strhodes * not really associated with any particular type. 386135446Strhodes */ 387193149Sdougb#define WANT_INET(x) (((x) & DNS_ADBFIND_INET) != 0) 388193149Sdougb#define WANT_INET6(x) (((x) & DNS_ADBFIND_INET6) != 0) 389135446Strhodes 390193149Sdougb#define EXPIRE_OK(exp, now) ((exp == INT_MAX) || (exp < now)) 391135446Strhodes 392135446Strhodes/* 393135446Strhodes * Find out if the flags on a name (nf) indicate if it is a hint or 394135446Strhodes * glue, and compare this to the appropriate bits set in o, to see if 395135446Strhodes * this is ok. 396135446Strhodes */ 397135446Strhodes#define GLUE_OK(nf, o) (!NAME_GLUEOK(nf) || (((o) & DNS_ADBFIND_GLUEOK) != 0)) 398135446Strhodes#define HINT_OK(nf, o) (!NAME_HINTOK(nf) || (((o) & DNS_ADBFIND_HINTOK) != 0)) 399135446Strhodes#define GLUEHINT_OK(nf, o) (GLUE_OK(nf, o) || HINT_OK(nf, o)) 400135446Strhodes#define STARTATZONE_MATCHES(nf, o) (((nf)->flags & NAME_STARTATZONE) == \ 401135446Strhodes ((o) & DNS_ADBFIND_STARTATZONE)) 402135446Strhodes 403193149Sdougb#define ENTER_LEVEL ISC_LOG_DEBUG(50) 404193149Sdougb#define EXIT_LEVEL ENTER_LEVEL 405193149Sdougb#define CLEAN_LEVEL ISC_LOG_DEBUG(100) 406193149Sdougb#define DEF_LEVEL ISC_LOG_DEBUG(5) 407193149Sdougb#define NCACHE_LEVEL ISC_LOG_DEBUG(20) 408135446Strhodes 409193149Sdougb#define NCACHE_RESULT(r) ((r) == DNS_R_NCACHENXDOMAIN || \ 410135446Strhodes (r) == DNS_R_NCACHENXRRSET) 411193149Sdougb#define AUTH_NX(r) ((r) == DNS_R_NXDOMAIN || \ 412135446Strhodes (r) == DNS_R_NXRRSET) 413193149Sdougb#define NXDOMAIN_RESULT(r) ((r) == DNS_R_NXDOMAIN || \ 414135446Strhodes (r) == DNS_R_NCACHENXDOMAIN) 415193149Sdougb#define NXRRSET_RESULT(r) ((r) == DNS_R_NCACHENXRRSET || \ 416135446Strhodes (r) == DNS_R_NXRRSET || \ 417135446Strhodes (r) == DNS_R_HINTNXRRSET) 418135446Strhodes 419135446Strhodes/* 420135446Strhodes * Error state rankings. 421135446Strhodes */ 422135446Strhodes 423193149Sdougb#define FIND_ERR_SUCCESS 0 /* highest rank */ 424193149Sdougb#define FIND_ERR_CANCELED 1 425193149Sdougb#define FIND_ERR_FAILURE 2 426193149Sdougb#define FIND_ERR_NXDOMAIN 3 427193149Sdougb#define FIND_ERR_NXRRSET 4 428193149Sdougb#define FIND_ERR_UNEXPECTED 5 429193149Sdougb#define FIND_ERR_NOTFOUND 6 430193149Sdougb#define FIND_ERR_MAX 7 431135446Strhodes 432135446Strhodesstatic const char *errnames[] = { 433135446Strhodes "success", 434135446Strhodes "canceled", 435135446Strhodes "failure", 436135446Strhodes "nxdomain", 437135446Strhodes "nxrrset", 438135446Strhodes "unexpected", 439135446Strhodes "not_found" 440135446Strhodes}; 441135446Strhodes 442193149Sdougb#define NEWERR(old, new) (ISC_MIN((old), (new))) 443135446Strhodes 444135446Strhodesstatic isc_result_t find_err_map[FIND_ERR_MAX] = { 445135446Strhodes ISC_R_SUCCESS, 446135446Strhodes ISC_R_CANCELED, 447135446Strhodes ISC_R_FAILURE, 448135446Strhodes DNS_R_NXDOMAIN, 449135446Strhodes DNS_R_NXRRSET, 450135446Strhodes ISC_R_UNEXPECTED, 451193149Sdougb ISC_R_NOTFOUND /* not YET found */ 452135446Strhodes}; 453135446Strhodes 454135446Strhodesstatic void 455135446StrhodesDP(int level, const char *format, ...) ISC_FORMAT_PRINTF(2, 3); 456135446Strhodes 457135446Strhodesstatic void 458135446StrhodesDP(int level, const char *format, ...) { 459135446Strhodes va_list args; 460135446Strhodes 461135446Strhodes va_start(args, format); 462135446Strhodes isc_log_vwrite(dns_lctx, 463135446Strhodes DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ADB, 464135446Strhodes level, format, args); 465135446Strhodes va_end(args); 466135446Strhodes} 467135446Strhodes 468193149Sdougb/*% 469193149Sdougb * Increment resolver-related statistics counters. 470193149Sdougb */ 471193149Sdougbstatic inline void 472193149Sdougbinc_stats(dns_adb_t *adb, isc_statscounter_t counter) { 473193149Sdougb if (adb->view->resstats != NULL) 474193149Sdougb isc_stats_increment(adb->view->resstats, counter); 475193149Sdougb} 476193149Sdougb 477135446Strhodesstatic inline dns_ttl_t 478135446Strhodesttlclamp(dns_ttl_t ttl) { 479135446Strhodes if (ttl < ADB_CACHE_MINIMUM) 480135446Strhodes ttl = ADB_CACHE_MINIMUM; 481135446Strhodes if (ttl > ADB_CACHE_MAXIMUM) 482135446Strhodes ttl = ADB_CACHE_MAXIMUM; 483135446Strhodes 484135446Strhodes return (ttl); 485135446Strhodes} 486135446Strhodes 487135446Strhodes/* 488135446Strhodes * Requires the adbname bucket be locked and that no entry buckets be locked. 489135446Strhodes * 490135446Strhodes * This code handles A and AAAA rdatasets only. 491135446Strhodes */ 492135446Strhodesstatic isc_result_t 493135446Strhodesimport_rdataset(dns_adbname_t *adbname, dns_rdataset_t *rdataset, 494135446Strhodes isc_stdtime_t now) 495135446Strhodes{ 496135446Strhodes isc_result_t result; 497135446Strhodes dns_adb_t *adb; 498135446Strhodes dns_adbnamehook_t *nh; 499135446Strhodes dns_adbnamehook_t *anh; 500135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 501135446Strhodes struct in_addr ina; 502135446Strhodes struct in6_addr in6a; 503135446Strhodes isc_sockaddr_t sockaddr; 504135446Strhodes dns_adbentry_t *foundentry; /* NO CLEAN UP! */ 505135446Strhodes int addr_bucket; 506135446Strhodes isc_boolean_t new_addresses_added; 507135446Strhodes dns_rdatatype_t rdtype; 508135446Strhodes unsigned int findoptions; 509186462Sdougb dns_adbnamehooklist_t *hookhead; 510135446Strhodes 511135446Strhodes INSIST(DNS_ADBNAME_VALID(adbname)); 512135446Strhodes adb = adbname->adb; 513135446Strhodes INSIST(DNS_ADB_VALID(adb)); 514135446Strhodes 515135446Strhodes rdtype = rdataset->type; 516135446Strhodes INSIST((rdtype == dns_rdatatype_a) || (rdtype == dns_rdatatype_aaaa)); 517135446Strhodes if (rdtype == dns_rdatatype_a) 518135446Strhodes findoptions = DNS_ADBFIND_INET; 519135446Strhodes else 520135446Strhodes findoptions = DNS_ADBFIND_INET6; 521135446Strhodes 522135446Strhodes addr_bucket = DNS_ADB_INVALIDBUCKET; 523135446Strhodes new_addresses_added = ISC_FALSE; 524135446Strhodes 525135446Strhodes nh = NULL; 526135446Strhodes result = dns_rdataset_first(rdataset); 527135446Strhodes while (result == ISC_R_SUCCESS) { 528135446Strhodes dns_rdata_reset(&rdata); 529135446Strhodes dns_rdataset_current(rdataset, &rdata); 530135446Strhodes if (rdtype == dns_rdatatype_a) { 531135446Strhodes INSIST(rdata.length == 4); 532135446Strhodes memcpy(&ina.s_addr, rdata.data, 4); 533135446Strhodes isc_sockaddr_fromin(&sockaddr, &ina, 0); 534186462Sdougb hookhead = &adbname->v4; 535135446Strhodes } else { 536135446Strhodes INSIST(rdata.length == 16); 537135446Strhodes memcpy(in6a.s6_addr, rdata.data, 16); 538135446Strhodes isc_sockaddr_fromin6(&sockaddr, &in6a, 0); 539186462Sdougb hookhead = &adbname->v6; 540135446Strhodes } 541135446Strhodes 542135446Strhodes INSIST(nh == NULL); 543135446Strhodes nh = new_adbnamehook(adb, NULL); 544135446Strhodes if (nh == NULL) { 545135446Strhodes adbname->partial_result |= findoptions; 546135446Strhodes result = ISC_R_NOMEMORY; 547135446Strhodes goto fail; 548135446Strhodes } 549135446Strhodes 550193149Sdougb foundentry = find_entry_and_lock(adb, &sockaddr, &addr_bucket, 551193149Sdougb now); 552135446Strhodes if (foundentry == NULL) { 553135446Strhodes dns_adbentry_t *entry; 554135446Strhodes 555135446Strhodes entry = new_adbentry(adb); 556135446Strhodes if (entry == NULL) { 557135446Strhodes adbname->partial_result |= findoptions; 558135446Strhodes result = ISC_R_NOMEMORY; 559135446Strhodes goto fail; 560135446Strhodes } 561135446Strhodes 562135446Strhodes entry->sockaddr = sockaddr; 563135446Strhodes entry->refcnt = 1; 564135446Strhodes 565135446Strhodes nh->entry = entry; 566135446Strhodes 567135446Strhodes link_entry(adb, addr_bucket, entry); 568135446Strhodes } else { 569186462Sdougb for (anh = ISC_LIST_HEAD(*hookhead); 570135446Strhodes anh != NULL; 571135446Strhodes anh = ISC_LIST_NEXT(anh, plink)) 572135446Strhodes if (anh->entry == foundentry) 573135446Strhodes break; 574135446Strhodes if (anh == NULL) { 575135446Strhodes foundentry->refcnt++; 576135446Strhodes nh->entry = foundentry; 577135446Strhodes } else 578135446Strhodes free_adbnamehook(adb, &nh); 579135446Strhodes } 580135446Strhodes 581135446Strhodes new_addresses_added = ISC_TRUE; 582186462Sdougb if (nh != NULL) 583186462Sdougb ISC_LIST_APPEND(*hookhead, nh, plink); 584135446Strhodes nh = NULL; 585135446Strhodes result = dns_rdataset_next(rdataset); 586135446Strhodes } 587135446Strhodes 588135446Strhodes fail: 589135446Strhodes if (nh != NULL) 590135446Strhodes free_adbnamehook(adb, &nh); 591135446Strhodes 592135446Strhodes if (addr_bucket != DNS_ADB_INVALIDBUCKET) 593135446Strhodes UNLOCK(&adb->entrylocks[addr_bucket]); 594135446Strhodes 595135446Strhodes if (rdataset->trust == dns_trust_glue || 596135446Strhodes rdataset->trust == dns_trust_additional) 597135446Strhodes rdataset->ttl = ADB_CACHE_MINIMUM; 598135446Strhodes else 599135446Strhodes rdataset->ttl = ttlclamp(rdataset->ttl); 600135446Strhodes 601135446Strhodes if (rdtype == dns_rdatatype_a) { 602135446Strhodes DP(NCACHE_LEVEL, "expire_v4 set to MIN(%u,%u) import_rdataset", 603135446Strhodes adbname->expire_v4, now + rdataset->ttl); 604135446Strhodes adbname->expire_v4 = ISC_MIN(adbname->expire_v4, 605135446Strhodes now + rdataset->ttl); 606135446Strhodes } else { 607135446Strhodes DP(NCACHE_LEVEL, "expire_v6 set to MIN(%u,%u) import_rdataset", 608135446Strhodes adbname->expire_v6, now + rdataset->ttl); 609135446Strhodes adbname->expire_v6 = ISC_MIN(adbname->expire_v6, 610135446Strhodes now + rdataset->ttl); 611135446Strhodes } 612135446Strhodes 613135446Strhodes if (new_addresses_added) { 614135446Strhodes /* 615135446Strhodes * Lie a little here. This is more or less so code that cares 616135446Strhodes * can find out if any new information was added or not. 617135446Strhodes */ 618135446Strhodes return (ISC_R_SUCCESS); 619135446Strhodes } 620135446Strhodes 621135446Strhodes return (result); 622135446Strhodes} 623135446Strhodes 624135446Strhodes/* 625135446Strhodes * Requires the name's bucket be locked. 626135446Strhodes */ 627135446Strhodesstatic isc_boolean_t 628135446Strhodeskill_name(dns_adbname_t **n, isc_eventtype_t ev) { 629135446Strhodes dns_adbname_t *name; 630135446Strhodes isc_boolean_t result = ISC_FALSE; 631135446Strhodes isc_boolean_t result4, result6; 632193149Sdougb int bucket; 633135446Strhodes dns_adb_t *adb; 634135446Strhodes 635135446Strhodes INSIST(n != NULL); 636135446Strhodes name = *n; 637135446Strhodes *n = NULL; 638135446Strhodes INSIST(DNS_ADBNAME_VALID(name)); 639135446Strhodes adb = name->adb; 640135446Strhodes INSIST(DNS_ADB_VALID(adb)); 641135446Strhodes 642135446Strhodes DP(DEF_LEVEL, "killing name %p", name); 643135446Strhodes 644135446Strhodes /* 645135446Strhodes * If we're dead already, just check to see if we should go 646135446Strhodes * away now or not. 647135446Strhodes */ 648135446Strhodes if (NAME_DEAD(name) && !NAME_FETCH(name)) { 649135446Strhodes result = unlink_name(adb, name); 650135446Strhodes free_adbname(adb, &name); 651135446Strhodes if (result) 652135446Strhodes result = dec_adb_irefcnt(adb); 653135446Strhodes return (result); 654135446Strhodes } 655135446Strhodes 656135446Strhodes /* 657135446Strhodes * Clean up the name's various lists. These two are destructive 658135446Strhodes * in that they will always empty the list. 659135446Strhodes */ 660135446Strhodes clean_finds_at_name(name, ev, DNS_ADBFIND_ADDRESSMASK); 661135446Strhodes result4 = clean_namehooks(adb, &name->v4); 662135446Strhodes result6 = clean_namehooks(adb, &name->v6); 663135446Strhodes clean_target(adb, &name->target); 664135446Strhodes result = ISC_TF(result4 || result6); 665135446Strhodes 666135446Strhodes /* 667135446Strhodes * If fetches are running, cancel them. If none are running, we can 668135446Strhodes * just kill the name here. 669135446Strhodes */ 670135446Strhodes if (!NAME_FETCH(name)) { 671135446Strhodes INSIST(result == ISC_FALSE); 672135446Strhodes result = unlink_name(adb, name); 673135446Strhodes free_adbname(adb, &name); 674135446Strhodes if (result) 675135446Strhodes result = dec_adb_irefcnt(adb); 676135446Strhodes } else { 677135446Strhodes cancel_fetches_at_name(name); 678193149Sdougb if (!NAME_DEAD(name)) { 679193149Sdougb bucket = name->lock_bucket; 680193149Sdougb ISC_LIST_UNLINK(adb->names[bucket], name, plink); 681193149Sdougb ISC_LIST_APPEND(adb->deadnames[bucket], name, plink); 682193149Sdougb name->flags |= NAME_IS_DEAD; 683193149Sdougb } 684135446Strhodes } 685135446Strhodes return (result); 686135446Strhodes} 687135446Strhodes 688135446Strhodes/* 689135446Strhodes * Requires the name's bucket be locked and no entry buckets be locked. 690135446Strhodes */ 691135446Strhodesstatic isc_boolean_t 692193149Sdougbcheck_expire_namehooks(dns_adbname_t *name, isc_stdtime_t now) { 693135446Strhodes dns_adb_t *adb; 694135446Strhodes isc_boolean_t result4 = ISC_FALSE; 695135446Strhodes isc_boolean_t result6 = ISC_FALSE; 696135446Strhodes 697135446Strhodes INSIST(DNS_ADBNAME_VALID(name)); 698135446Strhodes adb = name->adb; 699135446Strhodes INSIST(DNS_ADB_VALID(adb)); 700135446Strhodes 701135446Strhodes /* 702135446Strhodes * Check to see if we need to remove the v4 addresses 703135446Strhodes */ 704193149Sdougb if (!NAME_FETCH_V4(name) && EXPIRE_OK(name->expire_v4, now)) { 705135446Strhodes if (NAME_HAS_V4(name)) { 706135446Strhodes DP(DEF_LEVEL, "expiring v4 for name %p", name); 707135446Strhodes result4 = clean_namehooks(adb, &name->v4); 708135446Strhodes name->partial_result &= ~DNS_ADBFIND_INET; 709135446Strhodes } 710135446Strhodes name->expire_v4 = INT_MAX; 711135446Strhodes name->fetch_err = FIND_ERR_UNEXPECTED; 712135446Strhodes } 713135446Strhodes 714135446Strhodes /* 715135446Strhodes * Check to see if we need to remove the v6 addresses 716135446Strhodes */ 717193149Sdougb if (!NAME_FETCH_V6(name) && EXPIRE_OK(name->expire_v6, now)) { 718135446Strhodes if (NAME_HAS_V6(name)) { 719135446Strhodes DP(DEF_LEVEL, "expiring v6 for name %p", name); 720135446Strhodes result6 = clean_namehooks(adb, &name->v6); 721135446Strhodes name->partial_result &= ~DNS_ADBFIND_INET6; 722135446Strhodes } 723135446Strhodes name->expire_v6 = INT_MAX; 724135446Strhodes name->fetch6_err = FIND_ERR_UNEXPECTED; 725135446Strhodes } 726135446Strhodes 727135446Strhodes /* 728135446Strhodes * Check to see if we need to remove the alias target. 729135446Strhodes */ 730193149Sdougb if (EXPIRE_OK(name->expire_target, now)) { 731135446Strhodes clean_target(adb, &name->target); 732135446Strhodes name->expire_target = INT_MAX; 733135446Strhodes } 734135446Strhodes return (ISC_TF(result4 || result6)); 735135446Strhodes} 736135446Strhodes 737135446Strhodes/* 738135446Strhodes * Requires the name's bucket be locked. 739135446Strhodes */ 740135446Strhodesstatic inline void 741135446Strhodeslink_name(dns_adb_t *adb, int bucket, dns_adbname_t *name) { 742135446Strhodes INSIST(name->lock_bucket == DNS_ADB_INVALIDBUCKET); 743135446Strhodes 744135446Strhodes ISC_LIST_PREPEND(adb->names[bucket], name, plink); 745135446Strhodes name->lock_bucket = bucket; 746135446Strhodes adb->name_refcnt[bucket]++; 747135446Strhodes} 748135446Strhodes 749135446Strhodes/* 750135446Strhodes * Requires the name's bucket be locked. 751135446Strhodes */ 752135446Strhodesstatic inline isc_boolean_t 753135446Strhodesunlink_name(dns_adb_t *adb, dns_adbname_t *name) { 754135446Strhodes int bucket; 755135446Strhodes isc_boolean_t result = ISC_FALSE; 756135446Strhodes 757135446Strhodes bucket = name->lock_bucket; 758135446Strhodes INSIST(bucket != DNS_ADB_INVALIDBUCKET); 759135446Strhodes 760193149Sdougb if (NAME_DEAD(name)) 761193149Sdougb ISC_LIST_UNLINK(adb->deadnames[bucket], name, plink); 762193149Sdougb else 763193149Sdougb ISC_LIST_UNLINK(adb->names[bucket], name, plink); 764135446Strhodes name->lock_bucket = DNS_ADB_INVALIDBUCKET; 765135446Strhodes INSIST(adb->name_refcnt[bucket] > 0); 766135446Strhodes adb->name_refcnt[bucket]--; 767135446Strhodes if (adb->name_sd[bucket] && adb->name_refcnt[bucket] == 0) 768135446Strhodes result = ISC_TRUE; 769135446Strhodes return (result); 770135446Strhodes} 771135446Strhodes 772135446Strhodes/* 773135446Strhodes * Requires the entry's bucket be locked. 774135446Strhodes */ 775135446Strhodesstatic inline void 776135446Strhodeslink_entry(dns_adb_t *adb, int bucket, dns_adbentry_t *entry) { 777193149Sdougb int i; 778193149Sdougb dns_adbentry_t *e; 779193149Sdougb 780193149Sdougb if (adb->overmem) { 781193149Sdougb for (i = 0; i < 2; i++) { 782193149Sdougb e = ISC_LIST_TAIL(adb->entries[bucket]); 783193149Sdougb if (e == NULL) 784193149Sdougb break; 785193149Sdougb if (e->refcnt == 0) { 786193149Sdougb unlink_entry(adb, e); 787193149Sdougb free_adbentry(adb, &e); 788193149Sdougb continue; 789193149Sdougb } 790193149Sdougb INSIST((e->flags & ENTRY_IS_DEAD) == 0); 791193149Sdougb e->flags |= ENTRY_IS_DEAD; 792193149Sdougb ISC_LIST_UNLINK(adb->entries[bucket], e, plink); 793193149Sdougb ISC_LIST_PREPEND(adb->deadentries[bucket], e, plink); 794193149Sdougb } 795193149Sdougb } 796193149Sdougb 797135446Strhodes ISC_LIST_PREPEND(adb->entries[bucket], entry, plink); 798135446Strhodes entry->lock_bucket = bucket; 799135446Strhodes adb->entry_refcnt[bucket]++; 800135446Strhodes} 801135446Strhodes 802135446Strhodes/* 803135446Strhodes * Requires the entry's bucket be locked. 804135446Strhodes */ 805135446Strhodesstatic inline isc_boolean_t 806135446Strhodesunlink_entry(dns_adb_t *adb, dns_adbentry_t *entry) { 807135446Strhodes int bucket; 808135446Strhodes isc_boolean_t result = ISC_FALSE; 809135446Strhodes 810135446Strhodes bucket = entry->lock_bucket; 811135446Strhodes INSIST(bucket != DNS_ADB_INVALIDBUCKET); 812135446Strhodes 813193149Sdougb if ((entry->flags & ENTRY_IS_DEAD) != 0) 814193149Sdougb ISC_LIST_UNLINK(adb->deadentries[bucket], entry, plink); 815193149Sdougb else 816193149Sdougb ISC_LIST_UNLINK(adb->entries[bucket], entry, plink); 817135446Strhodes entry->lock_bucket = DNS_ADB_INVALIDBUCKET; 818135446Strhodes INSIST(adb->entry_refcnt[bucket] > 0); 819135446Strhodes adb->entry_refcnt[bucket]--; 820135446Strhodes if (adb->entry_sd[bucket] && adb->entry_refcnt[bucket] == 0) 821135446Strhodes result = ISC_TRUE; 822135446Strhodes return (result); 823135446Strhodes} 824135446Strhodes 825135446Strhodesstatic inline void 826135446Strhodesviolate_locking_hierarchy(isc_mutex_t *have, isc_mutex_t *want) { 827135446Strhodes if (isc_mutex_trylock(want) != ISC_R_SUCCESS) { 828135446Strhodes UNLOCK(have); 829135446Strhodes LOCK(want); 830135446Strhodes LOCK(have); 831135446Strhodes } 832135446Strhodes} 833135446Strhodes 834135446Strhodes/* 835135446Strhodes * The ADB _MUST_ be locked before calling. Also, exit conditions must be 836135446Strhodes * checked after calling this function. 837135446Strhodes */ 838135446Strhodesstatic isc_boolean_t 839135446Strhodesshutdown_names(dns_adb_t *adb) { 840135446Strhodes int bucket; 841135446Strhodes isc_boolean_t result = ISC_FALSE; 842135446Strhodes dns_adbname_t *name; 843135446Strhodes dns_adbname_t *next_name; 844135446Strhodes 845135446Strhodes for (bucket = 0; bucket < NBUCKETS; bucket++) { 846135446Strhodes LOCK(&adb->namelocks[bucket]); 847135446Strhodes adb->name_sd[bucket] = ISC_TRUE; 848135446Strhodes 849135446Strhodes name = ISC_LIST_HEAD(adb->names[bucket]); 850135446Strhodes if (name == NULL) { 851135446Strhodes /* 852135446Strhodes * This bucket has no names. We must decrement the 853135446Strhodes * irefcnt ourselves, since it will not be 854135446Strhodes * automatically triggered by a name being unlinked. 855135446Strhodes */ 856135446Strhodes INSIST(result == ISC_FALSE); 857135446Strhodes result = dec_adb_irefcnt(adb); 858135446Strhodes } else { 859135446Strhodes /* 860135446Strhodes * Run through the list. For each name, clean up finds 861135446Strhodes * found there, and cancel any fetches running. When 862135446Strhodes * all the fetches are canceled, the name will destroy 863135446Strhodes * itself. 864135446Strhodes */ 865135446Strhodes while (name != NULL) { 866135446Strhodes next_name = ISC_LIST_NEXT(name, plink); 867135446Strhodes INSIST(result == ISC_FALSE); 868135446Strhodes result = kill_name(&name, 869135446Strhodes DNS_EVENT_ADBSHUTDOWN); 870135446Strhodes name = next_name; 871135446Strhodes } 872135446Strhodes } 873135446Strhodes 874135446Strhodes UNLOCK(&adb->namelocks[bucket]); 875135446Strhodes } 876135446Strhodes return (result); 877135446Strhodes} 878135446Strhodes 879135446Strhodes/* 880135446Strhodes * The ADB _MUST_ be locked before calling. Also, exit conditions must be 881135446Strhodes * checked after calling this function. 882135446Strhodes */ 883135446Strhodesstatic isc_boolean_t 884135446Strhodesshutdown_entries(dns_adb_t *adb) { 885135446Strhodes int bucket; 886135446Strhodes isc_boolean_t result = ISC_FALSE; 887135446Strhodes dns_adbentry_t *entry; 888135446Strhodes dns_adbentry_t *next_entry; 889135446Strhodes 890135446Strhodes for (bucket = 0; bucket < NBUCKETS; bucket++) { 891135446Strhodes LOCK(&adb->entrylocks[bucket]); 892135446Strhodes adb->entry_sd[bucket] = ISC_TRUE; 893135446Strhodes 894135446Strhodes entry = ISC_LIST_HEAD(adb->entries[bucket]); 895193149Sdougb if (adb->entry_refcnt[bucket] == 0) { 896135446Strhodes /* 897135446Strhodes * This bucket has no entries. We must decrement the 898135446Strhodes * irefcnt ourselves, since it will not be 899135446Strhodes * automatically triggered by an entry being unlinked. 900135446Strhodes */ 901135446Strhodes result = dec_adb_irefcnt(adb); 902135446Strhodes } else { 903135446Strhodes /* 904135446Strhodes * Run through the list. Cleanup any entries not 905135446Strhodes * associated with names, and which are not in use. 906135446Strhodes */ 907135446Strhodes while (entry != NULL) { 908135446Strhodes next_entry = ISC_LIST_NEXT(entry, plink); 909135446Strhodes if (entry->refcnt == 0 && 910135446Strhodes entry->expires != 0) { 911135446Strhodes result = unlink_entry(adb, entry); 912135446Strhodes free_adbentry(adb, &entry); 913135446Strhodes if (result) 914135446Strhodes result = dec_adb_irefcnt(adb); 915135446Strhodes } 916135446Strhodes entry = next_entry; 917135446Strhodes } 918135446Strhodes } 919135446Strhodes 920135446Strhodes UNLOCK(&adb->entrylocks[bucket]); 921135446Strhodes } 922135446Strhodes return (result); 923135446Strhodes} 924135446Strhodes 925135446Strhodes/* 926135446Strhodes * Name bucket must be locked 927135446Strhodes */ 928135446Strhodesstatic void 929135446Strhodescancel_fetches_at_name(dns_adbname_t *name) { 930135446Strhodes if (NAME_FETCH_A(name)) 931135446Strhodes dns_resolver_cancelfetch(name->fetch_a->fetch); 932135446Strhodes 933135446Strhodes if (NAME_FETCH_AAAA(name)) 934135446Strhodes dns_resolver_cancelfetch(name->fetch_aaaa->fetch); 935135446Strhodes} 936135446Strhodes 937135446Strhodes/* 938135446Strhodes * Assumes the name bucket is locked. 939135446Strhodes */ 940135446Strhodesstatic isc_boolean_t 941135446Strhodesclean_namehooks(dns_adb_t *adb, dns_adbnamehooklist_t *namehooks) { 942135446Strhodes dns_adbentry_t *entry; 943135446Strhodes dns_adbnamehook_t *namehook; 944135446Strhodes int addr_bucket; 945135446Strhodes isc_boolean_t result = ISC_FALSE; 946135446Strhodes 947135446Strhodes addr_bucket = DNS_ADB_INVALIDBUCKET; 948135446Strhodes namehook = ISC_LIST_HEAD(*namehooks); 949135446Strhodes while (namehook != NULL) { 950135446Strhodes INSIST(DNS_ADBNAMEHOOK_VALID(namehook)); 951135446Strhodes 952135446Strhodes /* 953135446Strhodes * Clean up the entry if needed. 954135446Strhodes */ 955135446Strhodes entry = namehook->entry; 956135446Strhodes if (entry != NULL) { 957135446Strhodes INSIST(DNS_ADBENTRY_VALID(entry)); 958135446Strhodes 959135446Strhodes if (addr_bucket != entry->lock_bucket) { 960135446Strhodes if (addr_bucket != DNS_ADB_INVALIDBUCKET) 961135446Strhodes UNLOCK(&adb->entrylocks[addr_bucket]); 962135446Strhodes addr_bucket = entry->lock_bucket; 963135446Strhodes LOCK(&adb->entrylocks[addr_bucket]); 964135446Strhodes } 965135446Strhodes 966135446Strhodes result = dec_entry_refcnt(adb, entry, ISC_FALSE); 967135446Strhodes } 968135446Strhodes 969135446Strhodes /* 970135446Strhodes * Free the namehook 971135446Strhodes */ 972135446Strhodes namehook->entry = NULL; 973135446Strhodes ISC_LIST_UNLINK(*namehooks, namehook, plink); 974135446Strhodes free_adbnamehook(adb, &namehook); 975135446Strhodes 976135446Strhodes namehook = ISC_LIST_HEAD(*namehooks); 977135446Strhodes } 978135446Strhodes 979135446Strhodes if (addr_bucket != DNS_ADB_INVALIDBUCKET) 980135446Strhodes UNLOCK(&adb->entrylocks[addr_bucket]); 981135446Strhodes return (result); 982135446Strhodes} 983135446Strhodes 984135446Strhodesstatic void 985135446Strhodesclean_target(dns_adb_t *adb, dns_name_t *target) { 986135446Strhodes if (dns_name_countlabels(target) > 0) { 987135446Strhodes dns_name_free(target, adb->mctx); 988135446Strhodes dns_name_init(target, NULL); 989135446Strhodes } 990135446Strhodes} 991135446Strhodes 992135446Strhodesstatic isc_result_t 993135446Strhodesset_target(dns_adb_t *adb, dns_name_t *name, dns_name_t *fname, 994135446Strhodes dns_rdataset_t *rdataset, dns_name_t *target) 995135446Strhodes{ 996135446Strhodes isc_result_t result; 997135446Strhodes dns_namereln_t namereln; 998135446Strhodes unsigned int nlabels; 999135446Strhodes int order; 1000135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 1001135446Strhodes dns_fixedname_t fixed1, fixed2; 1002135446Strhodes dns_name_t *prefix, *new_target; 1003135446Strhodes 1004135446Strhodes REQUIRE(dns_name_countlabels(target) == 0); 1005135446Strhodes 1006135446Strhodes if (rdataset->type == dns_rdatatype_cname) { 1007135446Strhodes dns_rdata_cname_t cname; 1008135446Strhodes 1009135446Strhodes /* 1010135446Strhodes * Copy the CNAME's target into the target name. 1011135446Strhodes */ 1012135446Strhodes result = dns_rdataset_first(rdataset); 1013135446Strhodes if (result != ISC_R_SUCCESS) 1014135446Strhodes return (result); 1015135446Strhodes dns_rdataset_current(rdataset, &rdata); 1016135446Strhodes result = dns_rdata_tostruct(&rdata, &cname, NULL); 1017135446Strhodes if (result != ISC_R_SUCCESS) 1018135446Strhodes return (result); 1019135446Strhodes result = dns_name_dup(&cname.cname, adb->mctx, target); 1020135446Strhodes dns_rdata_freestruct(&cname); 1021135446Strhodes if (result != ISC_R_SUCCESS) 1022135446Strhodes return (result); 1023135446Strhodes } else { 1024135446Strhodes dns_rdata_dname_t dname; 1025135446Strhodes 1026135446Strhodes INSIST(rdataset->type == dns_rdatatype_dname); 1027135446Strhodes namereln = dns_name_fullcompare(name, fname, &order, &nlabels); 1028135446Strhodes INSIST(namereln == dns_namereln_subdomain); 1029135446Strhodes /* 1030135446Strhodes * Get the target name of the DNAME. 1031135446Strhodes */ 1032135446Strhodes result = dns_rdataset_first(rdataset); 1033135446Strhodes if (result != ISC_R_SUCCESS) 1034135446Strhodes return (result); 1035135446Strhodes dns_rdataset_current(rdataset, &rdata); 1036135446Strhodes result = dns_rdata_tostruct(&rdata, &dname, NULL); 1037135446Strhodes if (result != ISC_R_SUCCESS) 1038135446Strhodes return (result); 1039135446Strhodes /* 1040135446Strhodes * Construct the new target name. 1041135446Strhodes */ 1042135446Strhodes dns_fixedname_init(&fixed1); 1043135446Strhodes prefix = dns_fixedname_name(&fixed1); 1044135446Strhodes dns_fixedname_init(&fixed2); 1045135446Strhodes new_target = dns_fixedname_name(&fixed2); 1046135446Strhodes dns_name_split(name, nlabels, prefix, NULL); 1047135446Strhodes result = dns_name_concatenate(prefix, &dname.dname, new_target, 1048135446Strhodes NULL); 1049135446Strhodes dns_rdata_freestruct(&dname); 1050135446Strhodes if (result != ISC_R_SUCCESS) 1051135446Strhodes return (result); 1052135446Strhodes result = dns_name_dup(new_target, adb->mctx, target); 1053135446Strhodes if (result != ISC_R_SUCCESS) 1054135446Strhodes return (result); 1055135446Strhodes } 1056135446Strhodes 1057135446Strhodes return (ISC_R_SUCCESS); 1058135446Strhodes} 1059135446Strhodes 1060135446Strhodes/* 1061135446Strhodes * Assumes nothing is locked, since this is called by the client. 1062135446Strhodes */ 1063135446Strhodesstatic void 1064135446Strhodesevent_free(isc_event_t *event) { 1065135446Strhodes dns_adbfind_t *find; 1066135446Strhodes 1067135446Strhodes INSIST(event != NULL); 1068135446Strhodes find = event->ev_destroy_arg; 1069135446Strhodes INSIST(DNS_ADBFIND_VALID(find)); 1070135446Strhodes 1071135446Strhodes LOCK(&find->lock); 1072135446Strhodes find->flags |= FIND_EVENT_FREED; 1073135446Strhodes event->ev_destroy_arg = NULL; 1074135446Strhodes UNLOCK(&find->lock); 1075135446Strhodes} 1076135446Strhodes 1077135446Strhodes/* 1078135446Strhodes * Assumes the name bucket is locked. 1079135446Strhodes */ 1080135446Strhodesstatic void 1081135446Strhodesclean_finds_at_name(dns_adbname_t *name, isc_eventtype_t evtype, 1082135446Strhodes unsigned int addrs) 1083135446Strhodes{ 1084135446Strhodes isc_event_t *ev; 1085135446Strhodes isc_task_t *task; 1086135446Strhodes dns_adbfind_t *find; 1087135446Strhodes dns_adbfind_t *next_find; 1088135446Strhodes isc_boolean_t process; 1089135446Strhodes unsigned int wanted, notify; 1090135446Strhodes 1091135446Strhodes DP(ENTER_LEVEL, 1092135446Strhodes "ENTER clean_finds_at_name, name %p, evtype %08x, addrs %08x", 1093135446Strhodes name, evtype, addrs); 1094135446Strhodes 1095135446Strhodes find = ISC_LIST_HEAD(name->finds); 1096135446Strhodes while (find != NULL) { 1097135446Strhodes LOCK(&find->lock); 1098135446Strhodes next_find = ISC_LIST_NEXT(find, plink); 1099135446Strhodes 1100135446Strhodes process = ISC_FALSE; 1101135446Strhodes wanted = find->flags & DNS_ADBFIND_ADDRESSMASK; 1102135446Strhodes notify = wanted & addrs; 1103135446Strhodes 1104135446Strhodes switch (evtype) { 1105135446Strhodes case DNS_EVENT_ADBMOREADDRESSES: 1106135446Strhodes DP(ISC_LOG_DEBUG(3), "DNS_EVENT_ADBMOREADDRESSES"); 1107135446Strhodes if ((notify) != 0) { 1108135446Strhodes find->flags &= ~addrs; 1109135446Strhodes process = ISC_TRUE; 1110135446Strhodes } 1111135446Strhodes break; 1112135446Strhodes case DNS_EVENT_ADBNOMOREADDRESSES: 1113135446Strhodes DP(ISC_LOG_DEBUG(3), "DNS_EVENT_ADBNOMOREADDRESSES"); 1114135446Strhodes find->flags &= ~addrs; 1115135446Strhodes wanted = find->flags & DNS_ADBFIND_ADDRESSMASK; 1116135446Strhodes if (wanted == 0) 1117135446Strhodes process = ISC_TRUE; 1118135446Strhodes break; 1119135446Strhodes default: 1120135446Strhodes find->flags &= ~addrs; 1121135446Strhodes process = ISC_TRUE; 1122135446Strhodes } 1123135446Strhodes 1124135446Strhodes if (process) { 1125135446Strhodes DP(DEF_LEVEL, "cfan: processing find %p", find); 1126135446Strhodes /* 1127135446Strhodes * Unlink the find from the name, letting the caller 1128135446Strhodes * call dns_adb_destroyfind() on it to clean it up 1129135446Strhodes * later. 1130135446Strhodes */ 1131135446Strhodes ISC_LIST_UNLINK(name->finds, find, plink); 1132135446Strhodes find->adbname = NULL; 1133135446Strhodes find->name_bucket = DNS_ADB_INVALIDBUCKET; 1134135446Strhodes 1135135446Strhodes INSIST(!FIND_EVENTSENT(find)); 1136135446Strhodes 1137135446Strhodes ev = &find->event; 1138135446Strhodes task = ev->ev_sender; 1139135446Strhodes ev->ev_sender = find; 1140135446Strhodes find->result_v4 = find_err_map[name->fetch_err]; 1141135446Strhodes find->result_v6 = find_err_map[name->fetch6_err]; 1142135446Strhodes ev->ev_type = evtype; 1143135446Strhodes ev->ev_destroy = event_free; 1144135446Strhodes ev->ev_destroy_arg = find; 1145135446Strhodes 1146135446Strhodes DP(DEF_LEVEL, 1147135446Strhodes "sending event %p to task %p for find %p", 1148135446Strhodes ev, task, find); 1149135446Strhodes 1150135446Strhodes isc_task_sendanddetach(&task, (isc_event_t **)&ev); 1151135446Strhodes } else { 1152135446Strhodes DP(DEF_LEVEL, "cfan: skipping find %p", find); 1153135446Strhodes } 1154135446Strhodes 1155135446Strhodes UNLOCK(&find->lock); 1156135446Strhodes find = next_find; 1157135446Strhodes } 1158135446Strhodes 1159135446Strhodes DP(ENTER_LEVEL, "EXIT clean_finds_at_name, name %p", name); 1160135446Strhodes} 1161135446Strhodes 1162135446Strhodesstatic inline void 1163135446Strhodescheck_exit(dns_adb_t *adb) { 1164135446Strhodes isc_event_t *event; 1165135446Strhodes /* 1166135446Strhodes * The caller must be holding the adb lock. 1167135446Strhodes */ 1168135446Strhodes if (adb->shutting_down) { 1169135446Strhodes /* 1170135446Strhodes * If there aren't any external references either, we're 1171135446Strhodes * done. Send the control event to initiate shutdown. 1172135446Strhodes */ 1173193149Sdougb INSIST(!adb->cevent_sent); /* Sanity check. */ 1174135446Strhodes event = &adb->cevent; 1175135446Strhodes isc_task_send(adb->task, &event); 1176135446Strhodes adb->cevent_sent = ISC_TRUE; 1177135446Strhodes } 1178135446Strhodes} 1179135446Strhodes 1180135446Strhodesstatic inline isc_boolean_t 1181135446Strhodesdec_adb_irefcnt(dns_adb_t *adb) { 1182135446Strhodes isc_event_t *event; 1183135446Strhodes isc_task_t *etask; 1184135446Strhodes isc_boolean_t result = ISC_FALSE; 1185135446Strhodes 1186135446Strhodes LOCK(&adb->reflock); 1187135446Strhodes 1188135446Strhodes INSIST(adb->irefcnt > 0); 1189135446Strhodes adb->irefcnt--; 1190135446Strhodes 1191135446Strhodes if (adb->irefcnt == 0) { 1192135446Strhodes event = ISC_LIST_HEAD(adb->whenshutdown); 1193135446Strhodes while (event != NULL) { 1194135446Strhodes ISC_LIST_UNLINK(adb->whenshutdown, event, ev_link); 1195135446Strhodes etask = event->ev_sender; 1196135446Strhodes event->ev_sender = adb; 1197135446Strhodes isc_task_sendanddetach(&etask, &event); 1198135446Strhodes event = ISC_LIST_HEAD(adb->whenshutdown); 1199135446Strhodes } 1200135446Strhodes } 1201135446Strhodes 1202135446Strhodes if (adb->irefcnt == 0 && adb->erefcnt == 0) 1203135446Strhodes result = ISC_TRUE; 1204135446Strhodes UNLOCK(&adb->reflock); 1205135446Strhodes return (result); 1206135446Strhodes} 1207135446Strhodes 1208135446Strhodesstatic inline void 1209135446Strhodesinc_adb_irefcnt(dns_adb_t *adb) { 1210135446Strhodes LOCK(&adb->reflock); 1211135446Strhodes adb->irefcnt++; 1212135446Strhodes UNLOCK(&adb->reflock); 1213135446Strhodes} 1214135446Strhodes 1215135446Strhodesstatic inline void 1216135446Strhodesinc_adb_erefcnt(dns_adb_t *adb) { 1217135446Strhodes LOCK(&adb->reflock); 1218135446Strhodes adb->erefcnt++; 1219135446Strhodes UNLOCK(&adb->reflock); 1220135446Strhodes} 1221135446Strhodes 1222135446Strhodesstatic inline void 1223135446Strhodesinc_entry_refcnt(dns_adb_t *adb, dns_adbentry_t *entry, isc_boolean_t lock) { 1224135446Strhodes int bucket; 1225135446Strhodes 1226135446Strhodes bucket = entry->lock_bucket; 1227135446Strhodes 1228135446Strhodes if (lock) 1229135446Strhodes LOCK(&adb->entrylocks[bucket]); 1230135446Strhodes 1231135446Strhodes entry->refcnt++; 1232135446Strhodes 1233135446Strhodes if (lock) 1234135446Strhodes UNLOCK(&adb->entrylocks[bucket]); 1235135446Strhodes} 1236135446Strhodes 1237135446Strhodesstatic inline isc_boolean_t 1238135446Strhodesdec_entry_refcnt(dns_adb_t *adb, dns_adbentry_t *entry, isc_boolean_t lock) { 1239135446Strhodes int bucket; 1240135446Strhodes isc_boolean_t destroy_entry; 1241135446Strhodes isc_boolean_t result = ISC_FALSE; 1242135446Strhodes 1243135446Strhodes bucket = entry->lock_bucket; 1244135446Strhodes 1245135446Strhodes if (lock) 1246135446Strhodes LOCK(&adb->entrylocks[bucket]); 1247135446Strhodes 1248135446Strhodes INSIST(entry->refcnt > 0); 1249135446Strhodes entry->refcnt--; 1250135446Strhodes 1251135446Strhodes destroy_entry = ISC_FALSE; 1252135446Strhodes if (entry->refcnt == 0 && 1253193149Sdougb (adb->entry_sd[bucket] || entry->expires == 0 || adb->overmem || 1254193149Sdougb (entry->flags & ENTRY_IS_DEAD) != 0)) { 1255135446Strhodes destroy_entry = ISC_TRUE; 1256135446Strhodes result = unlink_entry(adb, entry); 1257135446Strhodes } 1258135446Strhodes 1259135446Strhodes if (lock) 1260135446Strhodes UNLOCK(&adb->entrylocks[bucket]); 1261135446Strhodes 1262135446Strhodes if (!destroy_entry) 1263135446Strhodes return (result); 1264135446Strhodes 1265135446Strhodes entry->lock_bucket = DNS_ADB_INVALIDBUCKET; 1266135446Strhodes 1267135446Strhodes free_adbentry(adb, &entry); 1268135446Strhodes if (result) 1269193149Sdougb result = dec_adb_irefcnt(adb); 1270135446Strhodes 1271135446Strhodes return (result); 1272135446Strhodes} 1273135446Strhodes 1274135446Strhodesstatic inline dns_adbname_t * 1275135446Strhodesnew_adbname(dns_adb_t *adb, dns_name_t *dnsname) { 1276135446Strhodes dns_adbname_t *name; 1277135446Strhodes 1278135446Strhodes name = isc_mempool_get(adb->nmp); 1279135446Strhodes if (name == NULL) 1280135446Strhodes return (NULL); 1281135446Strhodes 1282135446Strhodes dns_name_init(&name->name, NULL); 1283135446Strhodes if (dns_name_dup(dnsname, adb->mctx, &name->name) != ISC_R_SUCCESS) { 1284135446Strhodes isc_mempool_put(adb->nmp, name); 1285135446Strhodes return (NULL); 1286135446Strhodes } 1287135446Strhodes dns_name_init(&name->target, NULL); 1288135446Strhodes name->magic = DNS_ADBNAME_MAGIC; 1289135446Strhodes name->adb = adb; 1290135446Strhodes name->partial_result = 0; 1291135446Strhodes name->flags = 0; 1292135446Strhodes name->expire_v4 = INT_MAX; 1293135446Strhodes name->expire_v6 = INT_MAX; 1294135446Strhodes name->expire_target = INT_MAX; 1295135446Strhodes name->chains = 0; 1296135446Strhodes name->lock_bucket = DNS_ADB_INVALIDBUCKET; 1297135446Strhodes ISC_LIST_INIT(name->v4); 1298135446Strhodes ISC_LIST_INIT(name->v6); 1299135446Strhodes name->fetch_a = NULL; 1300135446Strhodes name->fetch_aaaa = NULL; 1301135446Strhodes name->fetch_err = FIND_ERR_UNEXPECTED; 1302135446Strhodes name->fetch6_err = FIND_ERR_UNEXPECTED; 1303135446Strhodes ISC_LIST_INIT(name->finds); 1304135446Strhodes ISC_LINK_INIT(name, plink); 1305135446Strhodes 1306135446Strhodes return (name); 1307135446Strhodes} 1308135446Strhodes 1309135446Strhodesstatic inline void 1310135446Strhodesfree_adbname(dns_adb_t *adb, dns_adbname_t **name) { 1311135446Strhodes dns_adbname_t *n; 1312135446Strhodes 1313135446Strhodes INSIST(name != NULL && DNS_ADBNAME_VALID(*name)); 1314135446Strhodes n = *name; 1315135446Strhodes *name = NULL; 1316135446Strhodes 1317135446Strhodes INSIST(!NAME_HAS_V4(n)); 1318135446Strhodes INSIST(!NAME_HAS_V6(n)); 1319135446Strhodes INSIST(!NAME_FETCH(n)); 1320135446Strhodes INSIST(ISC_LIST_EMPTY(n->finds)); 1321135446Strhodes INSIST(!ISC_LINK_LINKED(n, plink)); 1322135446Strhodes INSIST(n->lock_bucket == DNS_ADB_INVALIDBUCKET); 1323135446Strhodes INSIST(n->adb == adb); 1324135446Strhodes 1325135446Strhodes n->magic = 0; 1326135446Strhodes dns_name_free(&n->name, adb->mctx); 1327135446Strhodes 1328135446Strhodes isc_mempool_put(adb->nmp, n); 1329135446Strhodes} 1330135446Strhodes 1331135446Strhodesstatic inline dns_adbnamehook_t * 1332135446Strhodesnew_adbnamehook(dns_adb_t *adb, dns_adbentry_t *entry) { 1333135446Strhodes dns_adbnamehook_t *nh; 1334135446Strhodes 1335135446Strhodes nh = isc_mempool_get(adb->nhmp); 1336135446Strhodes if (nh == NULL) 1337135446Strhodes return (NULL); 1338135446Strhodes 1339135446Strhodes nh->magic = DNS_ADBNAMEHOOK_MAGIC; 1340135446Strhodes nh->entry = entry; 1341135446Strhodes ISC_LINK_INIT(nh, plink); 1342135446Strhodes 1343135446Strhodes return (nh); 1344135446Strhodes} 1345135446Strhodes 1346135446Strhodesstatic inline void 1347135446Strhodesfree_adbnamehook(dns_adb_t *adb, dns_adbnamehook_t **namehook) { 1348135446Strhodes dns_adbnamehook_t *nh; 1349135446Strhodes 1350135446Strhodes INSIST(namehook != NULL && DNS_ADBNAMEHOOK_VALID(*namehook)); 1351135446Strhodes nh = *namehook; 1352135446Strhodes *namehook = NULL; 1353135446Strhodes 1354135446Strhodes INSIST(nh->entry == NULL); 1355135446Strhodes INSIST(!ISC_LINK_LINKED(nh, plink)); 1356135446Strhodes 1357135446Strhodes nh->magic = 0; 1358135446Strhodes isc_mempool_put(adb->nhmp, nh); 1359135446Strhodes} 1360135446Strhodes 1361170222Sdougbstatic inline dns_adblameinfo_t * 1362170222Sdougbnew_adblameinfo(dns_adb_t *adb, dns_name_t *qname, dns_rdatatype_t qtype) { 1363170222Sdougb dns_adblameinfo_t *li; 1364135446Strhodes 1365170222Sdougb li = isc_mempool_get(adb->limp); 1366170222Sdougb if (li == NULL) 1367135446Strhodes return (NULL); 1368135446Strhodes 1369170222Sdougb dns_name_init(&li->qname, NULL); 1370170222Sdougb if (dns_name_dup(qname, adb->mctx, &li->qname) != ISC_R_SUCCESS) { 1371170222Sdougb isc_mempool_put(adb->limp, li); 1372135446Strhodes return (NULL); 1373135446Strhodes } 1374170222Sdougb li->magic = DNS_ADBLAMEINFO_MAGIC; 1375170222Sdougb li->lame_timer = 0; 1376170222Sdougb li->qtype = qtype; 1377170222Sdougb ISC_LINK_INIT(li, plink); 1378135446Strhodes 1379170222Sdougb return (li); 1380135446Strhodes} 1381135446Strhodes 1382135446Strhodesstatic inline void 1383170222Sdougbfree_adblameinfo(dns_adb_t *adb, dns_adblameinfo_t **lameinfo) { 1384170222Sdougb dns_adblameinfo_t *li; 1385135446Strhodes 1386170222Sdougb INSIST(lameinfo != NULL && DNS_ADBLAMEINFO_VALID(*lameinfo)); 1387170222Sdougb li = *lameinfo; 1388170222Sdougb *lameinfo = NULL; 1389135446Strhodes 1390170222Sdougb INSIST(!ISC_LINK_LINKED(li, plink)); 1391135446Strhodes 1392170222Sdougb dns_name_free(&li->qname, adb->mctx); 1393135446Strhodes 1394170222Sdougb li->magic = 0; 1395135446Strhodes 1396170222Sdougb isc_mempool_put(adb->limp, li); 1397135446Strhodes} 1398135446Strhodes 1399135446Strhodesstatic inline dns_adbentry_t * 1400135446Strhodesnew_adbentry(dns_adb_t *adb) { 1401135446Strhodes dns_adbentry_t *e; 1402135446Strhodes isc_uint32_t r; 1403135446Strhodes 1404135446Strhodes e = isc_mempool_get(adb->emp); 1405135446Strhodes if (e == NULL) 1406135446Strhodes return (NULL); 1407135446Strhodes 1408135446Strhodes e->magic = DNS_ADBENTRY_MAGIC; 1409135446Strhodes e->lock_bucket = DNS_ADB_INVALIDBUCKET; 1410135446Strhodes e->refcnt = 0; 1411135446Strhodes e->flags = 0; 1412135446Strhodes isc_random_get(&r); 1413135446Strhodes e->srtt = (r & 0x1f) + 1; 1414135446Strhodes e->expires = 0; 1415170222Sdougb ISC_LIST_INIT(e->lameinfo); 1416135446Strhodes ISC_LINK_INIT(e, plink); 1417135446Strhodes 1418135446Strhodes return (e); 1419135446Strhodes} 1420135446Strhodes 1421135446Strhodesstatic inline void 1422135446Strhodesfree_adbentry(dns_adb_t *adb, dns_adbentry_t **entry) { 1423135446Strhodes dns_adbentry_t *e; 1424170222Sdougb dns_adblameinfo_t *li; 1425135446Strhodes 1426135446Strhodes INSIST(entry != NULL && DNS_ADBENTRY_VALID(*entry)); 1427135446Strhodes e = *entry; 1428135446Strhodes *entry = NULL; 1429135446Strhodes 1430135446Strhodes INSIST(e->lock_bucket == DNS_ADB_INVALIDBUCKET); 1431135446Strhodes INSIST(e->refcnt == 0); 1432135446Strhodes INSIST(!ISC_LINK_LINKED(e, plink)); 1433135446Strhodes 1434135446Strhodes e->magic = 0; 1435135446Strhodes 1436170222Sdougb li = ISC_LIST_HEAD(e->lameinfo); 1437170222Sdougb while (li != NULL) { 1438170222Sdougb ISC_LIST_UNLINK(e->lameinfo, li, plink); 1439170222Sdougb free_adblameinfo(adb, &li); 1440170222Sdougb li = ISC_LIST_HEAD(e->lameinfo); 1441135446Strhodes } 1442135446Strhodes 1443135446Strhodes isc_mempool_put(adb->emp, e); 1444135446Strhodes} 1445135446Strhodes 1446135446Strhodesstatic inline dns_adbfind_t * 1447135446Strhodesnew_adbfind(dns_adb_t *adb) { 1448135446Strhodes dns_adbfind_t *h; 1449135446Strhodes isc_result_t result; 1450135446Strhodes 1451135446Strhodes h = isc_mempool_get(adb->ahmp); 1452135446Strhodes if (h == NULL) 1453135446Strhodes return (NULL); 1454135446Strhodes 1455135446Strhodes /* 1456135446Strhodes * Public members. 1457135446Strhodes */ 1458135446Strhodes h->magic = 0; 1459135446Strhodes h->adb = adb; 1460135446Strhodes h->partial_result = 0; 1461135446Strhodes h->options = 0; 1462135446Strhodes h->flags = 0; 1463135446Strhodes h->result_v4 = ISC_R_UNEXPECTED; 1464135446Strhodes h->result_v6 = ISC_R_UNEXPECTED; 1465135446Strhodes ISC_LINK_INIT(h, publink); 1466135446Strhodes ISC_LINK_INIT(h, plink); 1467135446Strhodes ISC_LIST_INIT(h->list); 1468135446Strhodes h->adbname = NULL; 1469135446Strhodes h->name_bucket = DNS_ADB_INVALIDBUCKET; 1470135446Strhodes 1471135446Strhodes /* 1472135446Strhodes * private members 1473135446Strhodes */ 1474135446Strhodes result = isc_mutex_init(&h->lock); 1475135446Strhodes if (result != ISC_R_SUCCESS) { 1476135446Strhodes isc_mempool_put(adb->ahmp, h); 1477135446Strhodes return (NULL); 1478135446Strhodes } 1479135446Strhodes 1480135446Strhodes ISC_EVENT_INIT(&h->event, sizeof(isc_event_t), 0, 0, 0, NULL, NULL, 1481135446Strhodes NULL, NULL, h); 1482135446Strhodes 1483135446Strhodes inc_adb_irefcnt(adb); 1484135446Strhodes h->magic = DNS_ADBFIND_MAGIC; 1485135446Strhodes return (h); 1486135446Strhodes} 1487135446Strhodes 1488135446Strhodesstatic inline dns_adbfetch_t * 1489135446Strhodesnew_adbfetch(dns_adb_t *adb) { 1490135446Strhodes dns_adbfetch_t *f; 1491135446Strhodes 1492135446Strhodes f = isc_mempool_get(adb->afmp); 1493135446Strhodes if (f == NULL) 1494135446Strhodes return (NULL); 1495135446Strhodes 1496135446Strhodes f->magic = 0; 1497135446Strhodes f->fetch = NULL; 1498135446Strhodes 1499135446Strhodes dns_rdataset_init(&f->rdataset); 1500135446Strhodes 1501135446Strhodes f->magic = DNS_ADBFETCH_MAGIC; 1502135446Strhodes 1503135446Strhodes return (f); 1504135446Strhodes} 1505135446Strhodes 1506135446Strhodesstatic inline void 1507135446Strhodesfree_adbfetch(dns_adb_t *adb, dns_adbfetch_t **fetch) { 1508135446Strhodes dns_adbfetch_t *f; 1509135446Strhodes 1510135446Strhodes INSIST(fetch != NULL && DNS_ADBFETCH_VALID(*fetch)); 1511135446Strhodes f = *fetch; 1512135446Strhodes *fetch = NULL; 1513135446Strhodes 1514135446Strhodes f->magic = 0; 1515135446Strhodes 1516135446Strhodes if (dns_rdataset_isassociated(&f->rdataset)) 1517135446Strhodes dns_rdataset_disassociate(&f->rdataset); 1518135446Strhodes 1519135446Strhodes isc_mempool_put(adb->afmp, f); 1520135446Strhodes} 1521135446Strhodes 1522135446Strhodesstatic inline isc_boolean_t 1523135446Strhodesfree_adbfind(dns_adb_t *adb, dns_adbfind_t **findp) { 1524135446Strhodes dns_adbfind_t *find; 1525135446Strhodes 1526135446Strhodes INSIST(findp != NULL && DNS_ADBFIND_VALID(*findp)); 1527135446Strhodes find = *findp; 1528135446Strhodes *findp = NULL; 1529135446Strhodes 1530135446Strhodes INSIST(!FIND_HAS_ADDRS(find)); 1531135446Strhodes INSIST(!ISC_LINK_LINKED(find, publink)); 1532135446Strhodes INSIST(!ISC_LINK_LINKED(find, plink)); 1533135446Strhodes INSIST(find->name_bucket == DNS_ADB_INVALIDBUCKET); 1534135446Strhodes INSIST(find->adbname == NULL); 1535135446Strhodes 1536135446Strhodes find->magic = 0; 1537135446Strhodes 1538135446Strhodes DESTROYLOCK(&find->lock); 1539135446Strhodes isc_mempool_put(adb->ahmp, find); 1540135446Strhodes return (dec_adb_irefcnt(adb)); 1541135446Strhodes} 1542135446Strhodes 1543135446Strhodes/* 1544135446Strhodes * Copy bits from the entry into the newly allocated addrinfo. The entry 1545135446Strhodes * must be locked, and the reference count must be bumped up by one 1546135446Strhodes * if this function returns a valid pointer. 1547135446Strhodes */ 1548135446Strhodesstatic inline dns_adbaddrinfo_t * 1549135446Strhodesnew_adbaddrinfo(dns_adb_t *adb, dns_adbentry_t *entry, in_port_t port) { 1550135446Strhodes dns_adbaddrinfo_t *ai; 1551135446Strhodes 1552135446Strhodes ai = isc_mempool_get(adb->aimp); 1553135446Strhodes if (ai == NULL) 1554135446Strhodes return (NULL); 1555135446Strhodes 1556135446Strhodes ai->magic = DNS_ADBADDRINFO_MAGIC; 1557135446Strhodes ai->sockaddr = entry->sockaddr; 1558135446Strhodes isc_sockaddr_setport(&ai->sockaddr, port); 1559135446Strhodes ai->srtt = entry->srtt; 1560135446Strhodes ai->flags = entry->flags; 1561135446Strhodes ai->entry = entry; 1562135446Strhodes ISC_LINK_INIT(ai, publink); 1563135446Strhodes 1564135446Strhodes return (ai); 1565135446Strhodes} 1566135446Strhodes 1567135446Strhodesstatic inline void 1568135446Strhodesfree_adbaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **ainfo) { 1569135446Strhodes dns_adbaddrinfo_t *ai; 1570135446Strhodes 1571135446Strhodes INSIST(ainfo != NULL && DNS_ADBADDRINFO_VALID(*ainfo)); 1572135446Strhodes ai = *ainfo; 1573135446Strhodes *ainfo = NULL; 1574135446Strhodes 1575135446Strhodes INSIST(ai->entry == NULL); 1576135446Strhodes INSIST(!ISC_LINK_LINKED(ai, publink)); 1577135446Strhodes 1578135446Strhodes ai->magic = 0; 1579135446Strhodes 1580135446Strhodes isc_mempool_put(adb->aimp, ai); 1581135446Strhodes} 1582135446Strhodes 1583135446Strhodes/* 1584135446Strhodes * Search for the name. NOTE: The bucket is kept locked on both 1585135446Strhodes * success and failure, so it must always be unlocked by the caller! 1586135446Strhodes * 1587135446Strhodes * On the first call to this function, *bucketp must be set to 1588135446Strhodes * DNS_ADB_INVALIDBUCKET. 1589135446Strhodes */ 1590135446Strhodesstatic inline dns_adbname_t * 1591135446Strhodesfind_name_and_lock(dns_adb_t *adb, dns_name_t *name, 1592135446Strhodes unsigned int options, int *bucketp) 1593135446Strhodes{ 1594135446Strhodes dns_adbname_t *adbname; 1595135446Strhodes int bucket; 1596135446Strhodes 1597135446Strhodes bucket = dns_name_fullhash(name, ISC_FALSE) % NBUCKETS; 1598135446Strhodes 1599135446Strhodes if (*bucketp == DNS_ADB_INVALIDBUCKET) { 1600135446Strhodes LOCK(&adb->namelocks[bucket]); 1601135446Strhodes *bucketp = bucket; 1602135446Strhodes } else if (*bucketp != bucket) { 1603135446Strhodes UNLOCK(&adb->namelocks[*bucketp]); 1604135446Strhodes LOCK(&adb->namelocks[bucket]); 1605135446Strhodes *bucketp = bucket; 1606135446Strhodes } 1607135446Strhodes 1608135446Strhodes adbname = ISC_LIST_HEAD(adb->names[bucket]); 1609135446Strhodes while (adbname != NULL) { 1610135446Strhodes if (!NAME_DEAD(adbname)) { 1611135446Strhodes if (dns_name_equal(name, &adbname->name) 1612135446Strhodes && GLUEHINT_OK(adbname, options) 1613135446Strhodes && STARTATZONE_MATCHES(adbname, options)) 1614135446Strhodes return (adbname); 1615135446Strhodes } 1616135446Strhodes adbname = ISC_LIST_NEXT(adbname, plink); 1617135446Strhodes } 1618135446Strhodes 1619135446Strhodes return (NULL); 1620135446Strhodes} 1621135446Strhodes 1622135446Strhodes/* 1623135446Strhodes * Search for the address. NOTE: The bucket is kept locked on both 1624135446Strhodes * success and failure, so it must always be unlocked by the caller. 1625135446Strhodes * 1626135446Strhodes * On the first call to this function, *bucketp must be set to 1627135446Strhodes * DNS_ADB_INVALIDBUCKET. This will cause a lock to occur. On 1628135446Strhodes * later calls (within the same "lock path") it can be left alone, so 1629135446Strhodes * if this function is called multiple times locking is only done if 1630135446Strhodes * the bucket changes. 1631135446Strhodes */ 1632135446Strhodesstatic inline dns_adbentry_t * 1633193149Sdougbfind_entry_and_lock(dns_adb_t *adb, isc_sockaddr_t *addr, int *bucketp, 1634193149Sdougb isc_stdtime_t now) 1635193149Sdougb{ 1636193149Sdougb dns_adbentry_t *entry, *entry_next; 1637135446Strhodes int bucket; 1638135446Strhodes 1639135446Strhodes bucket = isc_sockaddr_hash(addr, ISC_TRUE) % NBUCKETS; 1640135446Strhodes 1641135446Strhodes if (*bucketp == DNS_ADB_INVALIDBUCKET) { 1642135446Strhodes LOCK(&adb->entrylocks[bucket]); 1643135446Strhodes *bucketp = bucket; 1644135446Strhodes } else if (*bucketp != bucket) { 1645135446Strhodes UNLOCK(&adb->entrylocks[*bucketp]); 1646135446Strhodes LOCK(&adb->entrylocks[bucket]); 1647135446Strhodes *bucketp = bucket; 1648135446Strhodes } 1649135446Strhodes 1650193149Sdougb /* Search the list, while cleaning up expired entries. */ 1651193149Sdougb for (entry = ISC_LIST_HEAD(adb->entries[bucket]); 1652193149Sdougb entry != NULL; 1653193149Sdougb entry = entry_next) { 1654193149Sdougb entry_next = ISC_LIST_NEXT(entry, plink); 1655193149Sdougb (void)check_expire_entry(adb, &entry, now); 1656193149Sdougb if (entry != NULL && 1657193149Sdougb isc_sockaddr_equal(addr, &entry->sockaddr)) { 1658193149Sdougb ISC_LIST_UNLINK(adb->entries[bucket], entry, plink); 1659193149Sdougb ISC_LIST_PREPEND(adb->entries[bucket], entry, plink); 1660135446Strhodes return (entry); 1661193149Sdougb } 1662135446Strhodes } 1663135446Strhodes 1664135446Strhodes return (NULL); 1665135446Strhodes} 1666135446Strhodes 1667135446Strhodes/* 1668135446Strhodes * Entry bucket MUST be locked! 1669135446Strhodes */ 1670135446Strhodesstatic isc_boolean_t 1671170222Sdougbentry_is_lame(dns_adb_t *adb, dns_adbentry_t *entry, dns_name_t *qname, 1672170222Sdougb dns_rdatatype_t qtype, isc_stdtime_t now) 1673135446Strhodes{ 1674170222Sdougb dns_adblameinfo_t *li, *next_li; 1675135446Strhodes isc_boolean_t is_bad; 1676135446Strhodes 1677135446Strhodes is_bad = ISC_FALSE; 1678135446Strhodes 1679170222Sdougb li = ISC_LIST_HEAD(entry->lameinfo); 1680170222Sdougb if (li == NULL) 1681135446Strhodes return (ISC_FALSE); 1682170222Sdougb while (li != NULL) { 1683170222Sdougb next_li = ISC_LIST_NEXT(li, plink); 1684135446Strhodes 1685135446Strhodes /* 1686135446Strhodes * Has the entry expired? 1687135446Strhodes */ 1688170222Sdougb if (li->lame_timer < now) { 1689170222Sdougb ISC_LIST_UNLINK(entry->lameinfo, li, plink); 1690170222Sdougb free_adblameinfo(adb, &li); 1691135446Strhodes } 1692135446Strhodes 1693135446Strhodes /* 1694135446Strhodes * Order tests from least to most expensive. 1695170222Sdougb * 1696170222Sdougb * We do not break out of the main loop here as 1697170222Sdougb * we use the loop for house keeping. 1698135446Strhodes */ 1699170222Sdougb if (li != NULL && !is_bad && li->qtype == qtype && 1700170222Sdougb dns_name_equal(qname, &li->qname)) 1701170222Sdougb is_bad = ISC_TRUE; 1702135446Strhodes 1703170222Sdougb li = next_li; 1704135446Strhodes } 1705135446Strhodes 1706135446Strhodes return (is_bad); 1707135446Strhodes} 1708135446Strhodes 1709135446Strhodesstatic void 1710170222Sdougbcopy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, dns_name_t *qname, 1711170222Sdougb dns_rdatatype_t qtype, dns_adbname_t *name, 1712170222Sdougb isc_stdtime_t now) 1713135446Strhodes{ 1714135446Strhodes dns_adbnamehook_t *namehook; 1715135446Strhodes dns_adbaddrinfo_t *addrinfo; 1716135446Strhodes dns_adbentry_t *entry; 1717135446Strhodes int bucket; 1718135446Strhodes 1719135446Strhodes bucket = DNS_ADB_INVALIDBUCKET; 1720135446Strhodes 1721135446Strhodes if (find->options & DNS_ADBFIND_INET) { 1722135446Strhodes namehook = ISC_LIST_HEAD(name->v4); 1723135446Strhodes while (namehook != NULL) { 1724135446Strhodes entry = namehook->entry; 1725135446Strhodes bucket = entry->lock_bucket; 1726135446Strhodes LOCK(&adb->entrylocks[bucket]); 1727135446Strhodes 1728135446Strhodes if (!FIND_RETURNLAME(find) 1729170222Sdougb && entry_is_lame(adb, entry, qname, qtype, now)) { 1730135446Strhodes find->options |= DNS_ADBFIND_LAMEPRUNED; 1731135446Strhodes goto nextv4; 1732135446Strhodes } 1733135446Strhodes addrinfo = new_adbaddrinfo(adb, entry, find->port); 1734135446Strhodes if (addrinfo == NULL) { 1735135446Strhodes find->partial_result |= DNS_ADBFIND_INET; 1736135446Strhodes goto out; 1737135446Strhodes } 1738135446Strhodes /* 1739135446Strhodes * Found a valid entry. Add it to the find's list. 1740135446Strhodes */ 1741135446Strhodes inc_entry_refcnt(adb, entry, ISC_FALSE); 1742135446Strhodes ISC_LIST_APPEND(find->list, addrinfo, publink); 1743135446Strhodes addrinfo = NULL; 1744135446Strhodes nextv4: 1745135446Strhodes UNLOCK(&adb->entrylocks[bucket]); 1746135446Strhodes bucket = DNS_ADB_INVALIDBUCKET; 1747135446Strhodes namehook = ISC_LIST_NEXT(namehook, plink); 1748135446Strhodes } 1749135446Strhodes } 1750135446Strhodes 1751135446Strhodes if (find->options & DNS_ADBFIND_INET6) { 1752135446Strhodes namehook = ISC_LIST_HEAD(name->v6); 1753135446Strhodes while (namehook != NULL) { 1754135446Strhodes entry = namehook->entry; 1755135446Strhodes bucket = entry->lock_bucket; 1756135446Strhodes LOCK(&adb->entrylocks[bucket]); 1757135446Strhodes 1758186462Sdougb if (!FIND_RETURNLAME(find) 1759186462Sdougb && entry_is_lame(adb, entry, qname, qtype, now)) { 1760186462Sdougb find->options |= DNS_ADBFIND_LAMEPRUNED; 1761135446Strhodes goto nextv6; 1762186462Sdougb } 1763135446Strhodes addrinfo = new_adbaddrinfo(adb, entry, find->port); 1764135446Strhodes if (addrinfo == NULL) { 1765135446Strhodes find->partial_result |= DNS_ADBFIND_INET6; 1766135446Strhodes goto out; 1767135446Strhodes } 1768135446Strhodes /* 1769135446Strhodes * Found a valid entry. Add it to the find's list. 1770135446Strhodes */ 1771135446Strhodes inc_entry_refcnt(adb, entry, ISC_FALSE); 1772135446Strhodes ISC_LIST_APPEND(find->list, addrinfo, publink); 1773135446Strhodes addrinfo = NULL; 1774135446Strhodes nextv6: 1775135446Strhodes UNLOCK(&adb->entrylocks[bucket]); 1776135446Strhodes bucket = DNS_ADB_INVALIDBUCKET; 1777135446Strhodes namehook = ISC_LIST_NEXT(namehook, plink); 1778135446Strhodes } 1779135446Strhodes } 1780135446Strhodes 1781135446Strhodes out: 1782135446Strhodes if (bucket != DNS_ADB_INVALIDBUCKET) 1783135446Strhodes UNLOCK(&adb->entrylocks[bucket]); 1784135446Strhodes} 1785135446Strhodes 1786135446Strhodesstatic void 1787135446Strhodesshutdown_task(isc_task_t *task, isc_event_t *ev) { 1788135446Strhodes dns_adb_t *adb; 1789135446Strhodes 1790135446Strhodes UNUSED(task); 1791135446Strhodes 1792135446Strhodes adb = ev->ev_arg; 1793135446Strhodes INSIST(DNS_ADB_VALID(adb)); 1794135446Strhodes 1795193149Sdougb isc_event_free(&ev); 1796135446Strhodes /* 1797186462Sdougb * Wait for lock around check_exit() call to be released. 1798186462Sdougb */ 1799186462Sdougb LOCK(&adb->lock); 1800135446Strhodes UNLOCK(&adb->lock); 1801135446Strhodes destroy(adb); 1802135446Strhodes} 1803135446Strhodes 1804135446Strhodes/* 1805135446Strhodes * Name bucket must be locked; adb may be locked; no other locks held. 1806135446Strhodes */ 1807135446Strhodesstatic isc_boolean_t 1808135446Strhodescheck_expire_name(dns_adbname_t **namep, isc_stdtime_t now) { 1809135446Strhodes dns_adbname_t *name; 1810153816Sdougb isc_boolean_t result = ISC_FALSE; 1811135446Strhodes 1812135446Strhodes INSIST(namep != NULL && DNS_ADBNAME_VALID(*namep)); 1813135446Strhodes name = *namep; 1814135446Strhodes 1815135446Strhodes if (NAME_HAS_V4(name) || NAME_HAS_V6(name)) 1816135446Strhodes return (result); 1817135446Strhodes if (NAME_FETCH(name)) 1818135446Strhodes return (result); 1819135446Strhodes if (!EXPIRE_OK(name->expire_v4, now)) 1820135446Strhodes return (result); 1821135446Strhodes if (!EXPIRE_OK(name->expire_v6, now)) 1822135446Strhodes return (result); 1823135446Strhodes if (!EXPIRE_OK(name->expire_target, now)) 1824135446Strhodes return (result); 1825135446Strhodes 1826135446Strhodes /* 1827135446Strhodes * The name is empty. Delete it. 1828135446Strhodes */ 1829135446Strhodes result = kill_name(&name, DNS_EVENT_ADBEXPIRED); 1830135446Strhodes *namep = NULL; 1831135446Strhodes 1832135446Strhodes /* 1833135446Strhodes * Our caller, or one of its callers, will be calling check_exit() at 1834135446Strhodes * some point, so we don't need to do it here. 1835135446Strhodes */ 1836135446Strhodes return (result); 1837135446Strhodes} 1838135446Strhodes 1839193149Sdougb/*% 1840193149Sdougb * Examine the tail entry of the LRU list to see if it expires or is stale 1841193149Sdougb * (unused for some period); if so, the name entry will be freed. If the ADB 1842193149Sdougb * is in the overmem condition, the tail and the next to tail entries 1843193149Sdougb * will be unconditionally removed (unless they have an outstanding fetch). 1844193149Sdougb * We don't care about a race on 'overmem' at the risk of causing some 1845193149Sdougb * collateral damage or a small delay in starting cleanup, so we don't bother 1846193149Sdougb * to lock ADB (if it's not locked). 1847193149Sdougb * 1848193149Sdougb * Name bucket must be locked; adb may be locked; no other locks held. 1849193149Sdougb */ 1850193149Sdougbstatic void 1851193149Sdougbcheck_stale_name(dns_adb_t *adb, int bucket, isc_stdtime_t now) { 1852193149Sdougb int victims, max_victims; 1853193149Sdougb isc_boolean_t result; 1854193149Sdougb dns_adbname_t *victim, *next_victim; 1855193149Sdougb isc_boolean_t overmem = adb->overmem; 1856193149Sdougb int scans = 0; 1857193149Sdougb 1858193149Sdougb INSIST(bucket != DNS_ADB_INVALIDBUCKET); 1859193149Sdougb 1860193149Sdougb max_victims = overmem ? 2 : 1; 1861193149Sdougb 1862193149Sdougb /* 1863193149Sdougb * We limit the number of scanned entries to 10 (arbitrary choice) 1864193149Sdougb * in order to avoid examining too many entries when there are many 1865193149Sdougb * tail entries that have fetches (this should be rare, but could 1866193149Sdougb * happen). 1867193149Sdougb */ 1868193149Sdougb victim = ISC_LIST_TAIL(adb->names[bucket]); 1869193149Sdougb for (victims = 0; 1870193149Sdougb victim != NULL && victims < max_victims && scans < 10; 1871193149Sdougb victim = next_victim) { 1872193149Sdougb INSIST(!NAME_DEAD(victim)); 1873193149Sdougb scans++; 1874193149Sdougb next_victim = ISC_LIST_PREV(victim, plink); 1875193149Sdougb result = check_expire_name(&victim, now); 1876193149Sdougb if (victim == NULL) { 1877193149Sdougb victims++; 1878193149Sdougb goto next; 1879193149Sdougb } 1880193149Sdougb 1881193149Sdougb if (!NAME_FETCH(victim) && 1882193149Sdougb (overmem || victim->last_used + ADB_STALE_MARGIN <= now)) { 1883193149Sdougb RUNTIME_CHECK(kill_name(&victim, 1884193149Sdougb DNS_EVENT_ADBCANCELED) == 1885193149Sdougb ISC_FALSE); 1886193149Sdougb victims++; 1887193149Sdougb } 1888193149Sdougb 1889193149Sdougb next: 1890193149Sdougb if (!overmem) 1891193149Sdougb break; 1892193149Sdougb } 1893193149Sdougb} 1894193149Sdougb 1895135446Strhodes/* 1896135446Strhodes * Entry bucket must be locked; adb may be locked; no other locks held. 1897135446Strhodes */ 1898135446Strhodesstatic isc_boolean_t 1899135446Strhodescheck_expire_entry(dns_adb_t *adb, dns_adbentry_t **entryp, isc_stdtime_t now) 1900135446Strhodes{ 1901135446Strhodes dns_adbentry_t *entry; 1902135446Strhodes isc_boolean_t result = ISC_FALSE; 1903135446Strhodes 1904135446Strhodes INSIST(entryp != NULL && DNS_ADBENTRY_VALID(*entryp)); 1905135446Strhodes entry = *entryp; 1906135446Strhodes 1907135446Strhodes if (entry->refcnt != 0) 1908135446Strhodes return (result); 1909135446Strhodes 1910193149Sdougb if (entry->expires == 0 || entry->expires > now) 1911135446Strhodes return (result); 1912135446Strhodes 1913135446Strhodes /* 1914135446Strhodes * The entry is not in use. Delete it. 1915135446Strhodes */ 1916135446Strhodes DP(DEF_LEVEL, "killing entry %p", entry); 1917135446Strhodes INSIST(ISC_LINK_LINKED(entry, plink)); 1918135446Strhodes result = unlink_entry(adb, entry); 1919135446Strhodes free_adbentry(adb, &entry); 1920135446Strhodes if (result) 1921135446Strhodes dec_adb_irefcnt(adb); 1922135446Strhodes *entryp = NULL; 1923135446Strhodes return (result); 1924135446Strhodes} 1925135446Strhodes 1926135446Strhodes/* 1927135446Strhodes * ADB must be locked, and no other locks held. 1928135446Strhodes */ 1929135446Strhodesstatic isc_boolean_t 1930135446Strhodescleanup_names(dns_adb_t *adb, int bucket, isc_stdtime_t now) { 1931135446Strhodes dns_adbname_t *name; 1932135446Strhodes dns_adbname_t *next_name; 1933153816Sdougb isc_boolean_t result = ISC_FALSE; 1934135446Strhodes 1935135446Strhodes DP(CLEAN_LEVEL, "cleaning name bucket %d", bucket); 1936135446Strhodes 1937135446Strhodes LOCK(&adb->namelocks[bucket]); 1938135446Strhodes if (adb->name_sd[bucket]) { 1939135446Strhodes UNLOCK(&adb->namelocks[bucket]); 1940135446Strhodes return (result); 1941135446Strhodes } 1942135446Strhodes 1943135446Strhodes name = ISC_LIST_HEAD(adb->names[bucket]); 1944135446Strhodes while (name != NULL) { 1945135446Strhodes next_name = ISC_LIST_NEXT(name, plink); 1946135446Strhodes INSIST(result == ISC_FALSE); 1947193149Sdougb result = check_expire_namehooks(name, now); 1948135446Strhodes if (!result) 1949135446Strhodes result = check_expire_name(&name, now); 1950135446Strhodes name = next_name; 1951135446Strhodes } 1952135446Strhodes UNLOCK(&adb->namelocks[bucket]); 1953135446Strhodes return (result); 1954135446Strhodes} 1955135446Strhodes 1956135446Strhodes/* 1957135446Strhodes * ADB must be locked, and no other locks held. 1958135446Strhodes */ 1959135446Strhodesstatic isc_boolean_t 1960135446Strhodescleanup_entries(dns_adb_t *adb, int bucket, isc_stdtime_t now) { 1961135446Strhodes dns_adbentry_t *entry, *next_entry; 1962135446Strhodes isc_boolean_t result = ISC_FALSE; 1963135446Strhodes 1964135446Strhodes DP(CLEAN_LEVEL, "cleaning entry bucket %d", bucket); 1965135446Strhodes 1966135446Strhodes LOCK(&adb->entrylocks[bucket]); 1967135446Strhodes entry = ISC_LIST_HEAD(adb->entries[bucket]); 1968135446Strhodes while (entry != NULL) { 1969135446Strhodes next_entry = ISC_LIST_NEXT(entry, plink); 1970135446Strhodes INSIST(result == ISC_FALSE); 1971135446Strhodes result = check_expire_entry(adb, &entry, now); 1972135446Strhodes entry = next_entry; 1973135446Strhodes } 1974135446Strhodes UNLOCK(&adb->entrylocks[bucket]); 1975135446Strhodes return (result); 1976135446Strhodes} 1977135446Strhodes 1978135446Strhodesstatic void 1979135446Strhodesdestroy(dns_adb_t *adb) { 1980135446Strhodes adb->magic = 0; 1981135446Strhodes 1982135446Strhodes isc_task_detach(&adb->task); 1983135446Strhodes 1984135446Strhodes isc_mempool_destroy(&adb->nmp); 1985135446Strhodes isc_mempool_destroy(&adb->nhmp); 1986170222Sdougb isc_mempool_destroy(&adb->limp); 1987135446Strhodes isc_mempool_destroy(&adb->emp); 1988135446Strhodes isc_mempool_destroy(&adb->ahmp); 1989135446Strhodes isc_mempool_destroy(&adb->aimp); 1990135446Strhodes isc_mempool_destroy(&adb->afmp); 1991135446Strhodes 1992135446Strhodes DESTROYMUTEXBLOCK(adb->entrylocks, NBUCKETS); 1993135446Strhodes DESTROYMUTEXBLOCK(adb->namelocks, NBUCKETS); 1994135446Strhodes 1995135446Strhodes DESTROYLOCK(&adb->reflock); 1996135446Strhodes DESTROYLOCK(&adb->lock); 1997135446Strhodes DESTROYLOCK(&adb->mplock); 1998186462Sdougb DESTROYLOCK(&adb->overmemlock); 1999135446Strhodes 2000135446Strhodes isc_mem_putanddetach(&adb->mctx, adb, sizeof(dns_adb_t)); 2001135446Strhodes} 2002135446Strhodes 2003135446Strhodes 2004135446Strhodes/* 2005135446Strhodes * Public functions. 2006135446Strhodes */ 2007135446Strhodes 2008135446Strhodesisc_result_t 2009135446Strhodesdns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_timermgr_t *timermgr, 2010135446Strhodes isc_taskmgr_t *taskmgr, dns_adb_t **newadb) 2011135446Strhodes{ 2012135446Strhodes dns_adb_t *adb; 2013135446Strhodes isc_result_t result; 2014135446Strhodes int i; 2015135446Strhodes 2016135446Strhodes REQUIRE(mem != NULL); 2017135446Strhodes REQUIRE(view != NULL); 2018193149Sdougb REQUIRE(timermgr != NULL); /* this is actually unused */ 2019135446Strhodes REQUIRE(taskmgr != NULL); 2020135446Strhodes REQUIRE(newadb != NULL && *newadb == NULL); 2021135446Strhodes 2022193149Sdougb UNUSED(timermgr); 2023193149Sdougb 2024135446Strhodes adb = isc_mem_get(mem, sizeof(dns_adb_t)); 2025135446Strhodes if (adb == NULL) 2026135446Strhodes return (ISC_R_NOMEMORY); 2027135446Strhodes 2028135446Strhodes /* 2029135446Strhodes * Initialize things here that cannot fail, and especially things 2030135446Strhodes * that must be NULL for the error return to work properly. 2031135446Strhodes */ 2032135446Strhodes adb->magic = 0; 2033135446Strhodes adb->erefcnt = 1; 2034135446Strhodes adb->irefcnt = 0; 2035135446Strhodes adb->nmp = NULL; 2036135446Strhodes adb->nhmp = NULL; 2037170222Sdougb adb->limp = NULL; 2038135446Strhodes adb->emp = NULL; 2039135446Strhodes adb->ahmp = NULL; 2040135446Strhodes adb->aimp = NULL; 2041135446Strhodes adb->afmp = NULL; 2042135446Strhodes adb->task = NULL; 2043135446Strhodes adb->mctx = NULL; 2044135446Strhodes adb->view = view; 2045135446Strhodes adb->taskmgr = taskmgr; 2046135446Strhodes adb->next_cleanbucket = 0; 2047135446Strhodes ISC_EVENT_INIT(&adb->cevent, sizeof(adb->cevent), 0, NULL, 2048135446Strhodes DNS_EVENT_ADBCONTROL, shutdown_task, adb, 2049135446Strhodes adb, NULL, NULL); 2050135446Strhodes adb->cevent_sent = ISC_FALSE; 2051135446Strhodes adb->shutting_down = ISC_FALSE; 2052135446Strhodes adb->overmem = ISC_FALSE; 2053135446Strhodes ISC_LIST_INIT(adb->whenshutdown); 2054135446Strhodes 2055135446Strhodes isc_mem_attach(mem, &adb->mctx); 2056135446Strhodes 2057135446Strhodes result = isc_mutex_init(&adb->lock); 2058135446Strhodes if (result != ISC_R_SUCCESS) 2059135446Strhodes goto fail0b; 2060135446Strhodes 2061135446Strhodes result = isc_mutex_init(&adb->mplock); 2062135446Strhodes if (result != ISC_R_SUCCESS) 2063135446Strhodes goto fail0c; 2064135446Strhodes 2065135446Strhodes result = isc_mutex_init(&adb->reflock); 2066135446Strhodes if (result != ISC_R_SUCCESS) 2067135446Strhodes goto fail0d; 2068135446Strhodes 2069186462Sdougb result = isc_mutex_init(&adb->overmemlock); 2070186462Sdougb if (result != ISC_R_SUCCESS) 2071186462Sdougb goto fail0e; 2072186462Sdougb 2073135446Strhodes /* 2074135446Strhodes * Initialize the bucket locks for names and elements. 2075135446Strhodes * May as well initialize the list heads, too. 2076135446Strhodes */ 2077135446Strhodes result = isc_mutexblock_init(adb->namelocks, NBUCKETS); 2078135446Strhodes if (result != ISC_R_SUCCESS) 2079135446Strhodes goto fail1; 2080135446Strhodes for (i = 0; i < NBUCKETS; i++) { 2081135446Strhodes ISC_LIST_INIT(adb->names[i]); 2082193149Sdougb ISC_LIST_INIT(adb->deadnames[i]); 2083135446Strhodes adb->name_sd[i] = ISC_FALSE; 2084135446Strhodes adb->name_refcnt[i] = 0; 2085135446Strhodes adb->irefcnt++; 2086135446Strhodes } 2087135446Strhodes for (i = 0; i < NBUCKETS; i++) { 2088135446Strhodes ISC_LIST_INIT(adb->entries[i]); 2089193149Sdougb ISC_LIST_INIT(adb->deadentries[i]); 2090135446Strhodes adb->entry_sd[i] = ISC_FALSE; 2091135446Strhodes adb->entry_refcnt[i] = 0; 2092135446Strhodes adb->irefcnt++; 2093135446Strhodes } 2094135446Strhodes result = isc_mutexblock_init(adb->entrylocks, NBUCKETS); 2095135446Strhodes if (result != ISC_R_SUCCESS) 2096135446Strhodes goto fail2; 2097135446Strhodes 2098135446Strhodes /* 2099135446Strhodes * Memory pools 2100135446Strhodes */ 2101135446Strhodes#define MPINIT(t, p, n) do { \ 2102135446Strhodes result = isc_mempool_create(mem, sizeof(t), &(p)); \ 2103135446Strhodes if (result != ISC_R_SUCCESS) \ 2104135446Strhodes goto fail3; \ 2105135446Strhodes isc_mempool_setfreemax((p), FREE_ITEMS); \ 2106135446Strhodes isc_mempool_setfillcount((p), FILL_COUNT); \ 2107135446Strhodes isc_mempool_setname((p), n); \ 2108135446Strhodes isc_mempool_associatelock((p), &adb->mplock); \ 2109135446Strhodes} while (0) 2110135446Strhodes 2111135446Strhodes MPINIT(dns_adbname_t, adb->nmp, "adbname"); 2112135446Strhodes MPINIT(dns_adbnamehook_t, adb->nhmp, "adbnamehook"); 2113170222Sdougb MPINIT(dns_adblameinfo_t, adb->limp, "adblameinfo"); 2114135446Strhodes MPINIT(dns_adbentry_t, adb->emp, "adbentry"); 2115135446Strhodes MPINIT(dns_adbfind_t, adb->ahmp, "adbfind"); 2116135446Strhodes MPINIT(dns_adbaddrinfo_t, adb->aimp, "adbaddrinfo"); 2117135446Strhodes MPINIT(dns_adbfetch_t, adb->afmp, "adbfetch"); 2118135446Strhodes 2119135446Strhodes#undef MPINIT 2120135446Strhodes 2121135446Strhodes /* 2122193149Sdougb * Allocate an internal task. 2123135446Strhodes */ 2124135446Strhodes result = isc_task_create(adb->taskmgr, 0, &adb->task); 2125135446Strhodes if (result != ISC_R_SUCCESS) 2126135446Strhodes goto fail3; 2127135446Strhodes isc_task_setname(adb->task, "ADB", adb); 2128135446Strhodes 2129135446Strhodes /* 2130135446Strhodes * Normal return. 2131135446Strhodes */ 2132135446Strhodes adb->magic = DNS_ADB_MAGIC; 2133135446Strhodes *newadb = adb; 2134135446Strhodes return (ISC_R_SUCCESS); 2135135446Strhodes 2136135446Strhodes fail3: 2137135446Strhodes if (adb->task != NULL) 2138135446Strhodes isc_task_detach(&adb->task); 2139135446Strhodes 2140135446Strhodes /* clean up entrylocks */ 2141135446Strhodes DESTROYMUTEXBLOCK(adb->entrylocks, NBUCKETS); 2142135446Strhodes 2143135446Strhodes fail2: /* clean up namelocks */ 2144135446Strhodes DESTROYMUTEXBLOCK(adb->namelocks, NBUCKETS); 2145135446Strhodes 2146135446Strhodes fail1: /* clean up only allocated memory */ 2147135446Strhodes if (adb->nmp != NULL) 2148135446Strhodes isc_mempool_destroy(&adb->nmp); 2149135446Strhodes if (adb->nhmp != NULL) 2150135446Strhodes isc_mempool_destroy(&adb->nhmp); 2151170222Sdougb if (adb->limp != NULL) 2152170222Sdougb isc_mempool_destroy(&adb->limp); 2153135446Strhodes if (adb->emp != NULL) 2154135446Strhodes isc_mempool_destroy(&adb->emp); 2155135446Strhodes if (adb->ahmp != NULL) 2156135446Strhodes isc_mempool_destroy(&adb->ahmp); 2157135446Strhodes if (adb->aimp != NULL) 2158135446Strhodes isc_mempool_destroy(&adb->aimp); 2159135446Strhodes if (adb->afmp != NULL) 2160135446Strhodes isc_mempool_destroy(&adb->afmp); 2161135446Strhodes 2162186462Sdougb DESTROYLOCK(&adb->overmemlock); 2163186462Sdougb fail0e: 2164135446Strhodes DESTROYLOCK(&adb->reflock); 2165135446Strhodes fail0d: 2166135446Strhodes DESTROYLOCK(&adb->mplock); 2167135446Strhodes fail0c: 2168135446Strhodes DESTROYLOCK(&adb->lock); 2169135446Strhodes fail0b: 2170135446Strhodes isc_mem_putanddetach(&adb->mctx, adb, sizeof(dns_adb_t)); 2171135446Strhodes 2172135446Strhodes return (result); 2173135446Strhodes} 2174135446Strhodes 2175135446Strhodesvoid 2176135446Strhodesdns_adb_attach(dns_adb_t *adb, dns_adb_t **adbx) { 2177135446Strhodes 2178135446Strhodes REQUIRE(DNS_ADB_VALID(adb)); 2179135446Strhodes REQUIRE(adbx != NULL && *adbx == NULL); 2180135446Strhodes 2181135446Strhodes inc_adb_erefcnt(adb); 2182135446Strhodes *adbx = adb; 2183135446Strhodes} 2184135446Strhodes 2185135446Strhodesvoid 2186135446Strhodesdns_adb_detach(dns_adb_t **adbx) { 2187135446Strhodes dns_adb_t *adb; 2188135446Strhodes isc_boolean_t need_exit_check; 2189135446Strhodes 2190135446Strhodes REQUIRE(adbx != NULL && DNS_ADB_VALID(*adbx)); 2191135446Strhodes 2192135446Strhodes adb = *adbx; 2193135446Strhodes *adbx = NULL; 2194135446Strhodes 2195135446Strhodes INSIST(adb->erefcnt > 0); 2196135446Strhodes 2197135446Strhodes LOCK(&adb->reflock); 2198135446Strhodes adb->erefcnt--; 2199135446Strhodes need_exit_check = ISC_TF(adb->erefcnt == 0 && adb->irefcnt == 0); 2200135446Strhodes UNLOCK(&adb->reflock); 2201135446Strhodes 2202135446Strhodes if (need_exit_check) { 2203135446Strhodes LOCK(&adb->lock); 2204135446Strhodes INSIST(adb->shutting_down); 2205135446Strhodes check_exit(adb); 2206135446Strhodes UNLOCK(&adb->lock); 2207135446Strhodes } 2208135446Strhodes} 2209135446Strhodes 2210135446Strhodesvoid 2211135446Strhodesdns_adb_whenshutdown(dns_adb_t *adb, isc_task_t *task, isc_event_t **eventp) { 2212135446Strhodes isc_task_t *clone; 2213135446Strhodes isc_event_t *event; 2214135446Strhodes isc_boolean_t zeroirefcnt = ISC_FALSE; 2215135446Strhodes 2216135446Strhodes /* 2217135446Strhodes * Send '*eventp' to 'task' when 'adb' has shutdown. 2218135446Strhodes */ 2219135446Strhodes 2220135446Strhodes REQUIRE(DNS_ADB_VALID(adb)); 2221135446Strhodes REQUIRE(eventp != NULL); 2222135446Strhodes 2223135446Strhodes event = *eventp; 2224135446Strhodes *eventp = NULL; 2225135446Strhodes 2226135446Strhodes LOCK(&adb->lock); 2227135446Strhodes 2228135446Strhodes LOCK(&adb->reflock); 2229135446Strhodes zeroirefcnt = ISC_TF(adb->irefcnt == 0); 2230135446Strhodes 2231135446Strhodes if (adb->shutting_down && zeroirefcnt && 2232135446Strhodes isc_mempool_getallocated(adb->ahmp) == 0) { 2233135446Strhodes /* 2234135446Strhodes * We're already shutdown. Send the event. 2235135446Strhodes */ 2236135446Strhodes event->ev_sender = adb; 2237135446Strhodes isc_task_send(task, &event); 2238135446Strhodes } else { 2239135446Strhodes clone = NULL; 2240135446Strhodes isc_task_attach(task, &clone); 2241135446Strhodes event->ev_sender = clone; 2242135446Strhodes ISC_LIST_APPEND(adb->whenshutdown, event, ev_link); 2243135446Strhodes } 2244135446Strhodes 2245135446Strhodes UNLOCK(&adb->reflock); 2246135446Strhodes UNLOCK(&adb->lock); 2247135446Strhodes} 2248135446Strhodes 2249135446Strhodesvoid 2250135446Strhodesdns_adb_shutdown(dns_adb_t *adb) { 2251135446Strhodes isc_boolean_t need_check_exit; 2252135446Strhodes 2253135446Strhodes /* 2254135446Strhodes * Shutdown 'adb'. 2255135446Strhodes */ 2256135446Strhodes 2257135446Strhodes LOCK(&adb->lock); 2258135446Strhodes 2259135446Strhodes if (!adb->shutting_down) { 2260135446Strhodes adb->shutting_down = ISC_TRUE; 2261135446Strhodes isc_mem_setwater(adb->mctx, water, adb, 0, 0); 2262135446Strhodes need_check_exit = shutdown_names(adb); 2263135446Strhodes if (!need_check_exit) 2264135446Strhodes need_check_exit = shutdown_entries(adb); 2265135446Strhodes if (need_check_exit) 2266135446Strhodes check_exit(adb); 2267135446Strhodes } 2268135446Strhodes 2269135446Strhodes UNLOCK(&adb->lock); 2270135446Strhodes} 2271135446Strhodes 2272135446Strhodesisc_result_t 2273135446Strhodesdns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, 2274170222Sdougb void *arg, dns_name_t *name, dns_name_t *qname, 2275170222Sdougb dns_rdatatype_t qtype, unsigned int options, 2276170222Sdougb isc_stdtime_t now, dns_name_t *target, 2277135446Strhodes in_port_t port, dns_adbfind_t **findp) 2278135446Strhodes{ 2279135446Strhodes dns_adbfind_t *find; 2280135446Strhodes dns_adbname_t *adbname; 2281135446Strhodes int bucket; 2282135446Strhodes isc_boolean_t want_event, start_at_zone, alias, have_address; 2283135446Strhodes isc_result_t result; 2284135446Strhodes unsigned int wanted_addresses; 2285135446Strhodes unsigned int wanted_fetches; 2286135446Strhodes unsigned int query_pending; 2287135446Strhodes 2288135446Strhodes REQUIRE(DNS_ADB_VALID(adb)); 2289135446Strhodes if (task != NULL) { 2290135446Strhodes REQUIRE(action != NULL); 2291135446Strhodes } 2292135446Strhodes REQUIRE(name != NULL); 2293170222Sdougb REQUIRE(qname != NULL); 2294135446Strhodes REQUIRE(findp != NULL && *findp == NULL); 2295135446Strhodes REQUIRE(target == NULL || dns_name_hasbuffer(target)); 2296135446Strhodes 2297135446Strhodes REQUIRE((options & DNS_ADBFIND_ADDRESSMASK) != 0); 2298135446Strhodes 2299135446Strhodes result = ISC_R_UNEXPECTED; 2300135446Strhodes wanted_addresses = (options & DNS_ADBFIND_ADDRESSMASK); 2301135446Strhodes wanted_fetches = 0; 2302135446Strhodes query_pending = 0; 2303135446Strhodes want_event = ISC_FALSE; 2304135446Strhodes start_at_zone = ISC_FALSE; 2305135446Strhodes alias = ISC_FALSE; 2306135446Strhodes 2307135446Strhodes if (now == 0) 2308135446Strhodes isc_stdtime_get(&now); 2309135446Strhodes 2310135446Strhodes /* 2311135446Strhodes * XXXMLG Move this comment somewhere else! 2312135446Strhodes * 2313135446Strhodes * Look up the name in our internal database. 2314135446Strhodes * 2315135446Strhodes * Possibilities: Note that these are not always exclusive. 2316135446Strhodes * 2317193149Sdougb * No name found. In this case, allocate a new name header and 2318193149Sdougb * an initial namehook or two. If any of these allocations 2319193149Sdougb * fail, clean up and return ISC_R_NOMEMORY. 2320135446Strhodes * 2321193149Sdougb * Name found, valid addresses present. Allocate one addrinfo 2322193149Sdougb * structure for each found and append it to the linked list 2323193149Sdougb * of addresses for this header. 2324135446Strhodes * 2325193149Sdougb * Name found, queries pending. In this case, if a task was 2326193149Sdougb * passed in, allocate a job id, attach it to the name's job 2327193149Sdougb * list and remember to tell the caller that there will be 2328193149Sdougb * more info coming later. 2329135446Strhodes */ 2330135446Strhodes 2331135446Strhodes find = new_adbfind(adb); 2332135446Strhodes if (find == NULL) 2333135446Strhodes return (ISC_R_NOMEMORY); 2334135446Strhodes 2335135446Strhodes find->port = port; 2336135446Strhodes 2337135446Strhodes /* 2338135446Strhodes * Remember what types of addresses we are interested in. 2339135446Strhodes */ 2340135446Strhodes find->options = options; 2341135446Strhodes find->flags |= wanted_addresses; 2342135446Strhodes if (FIND_WANTEVENT(find)) { 2343135446Strhodes REQUIRE(task != NULL); 2344135446Strhodes } 2345135446Strhodes 2346135446Strhodes /* 2347135446Strhodes * Try to see if we know anything about this name at all. 2348135446Strhodes */ 2349135446Strhodes bucket = DNS_ADB_INVALIDBUCKET; 2350135446Strhodes adbname = find_name_and_lock(adb, name, find->options, &bucket); 2351135446Strhodes if (adb->name_sd[bucket]) { 2352135446Strhodes DP(DEF_LEVEL, 2353135446Strhodes "dns_adb_createfind: returning ISC_R_SHUTTINGDOWN"); 2354135446Strhodes RUNTIME_CHECK(free_adbfind(adb, &find) == ISC_FALSE); 2355135446Strhodes result = ISC_R_SHUTTINGDOWN; 2356135446Strhodes goto out; 2357135446Strhodes } 2358135446Strhodes 2359135446Strhodes /* 2360135446Strhodes * Nothing found. Allocate a new adbname structure for this name. 2361135446Strhodes */ 2362135446Strhodes if (adbname == NULL) { 2363193149Sdougb /* 2364193149Sdougb * See if there is any stale name at the end of list, and purge 2365193149Sdougb * it if so. 2366193149Sdougb */ 2367193149Sdougb check_stale_name(adb, bucket, now); 2368193149Sdougb 2369135446Strhodes adbname = new_adbname(adb, name); 2370135446Strhodes if (adbname == NULL) { 2371135446Strhodes RUNTIME_CHECK(free_adbfind(adb, &find) == ISC_FALSE); 2372135446Strhodes result = ISC_R_NOMEMORY; 2373135446Strhodes goto out; 2374135446Strhodes } 2375135446Strhodes link_name(adb, bucket, adbname); 2376135446Strhodes if (FIND_HINTOK(find)) 2377135446Strhodes adbname->flags |= NAME_HINT_OK; 2378135446Strhodes if (FIND_GLUEOK(find)) 2379135446Strhodes adbname->flags |= NAME_GLUE_OK; 2380135446Strhodes if (FIND_STARTATZONE(find)) 2381135446Strhodes adbname->flags |= NAME_STARTATZONE; 2382193149Sdougb } else { 2383193149Sdougb /* Move this name forward in the LRU list */ 2384193149Sdougb ISC_LIST_UNLINK(adb->names[bucket], adbname, plink); 2385193149Sdougb ISC_LIST_PREPEND(adb->names[bucket], adbname, plink); 2386135446Strhodes } 2387193149Sdougb adbname->last_used = now; 2388135446Strhodes 2389135446Strhodes /* 2390135446Strhodes * Expire old entries, etc. 2391135446Strhodes */ 2392193149Sdougb RUNTIME_CHECK(check_expire_namehooks(adbname, now) == ISC_FALSE); 2393135446Strhodes 2394135446Strhodes /* 2395135446Strhodes * Do we know that the name is an alias? 2396135446Strhodes */ 2397135446Strhodes if (!EXPIRE_OK(adbname->expire_target, now)) { 2398135446Strhodes /* 2399135446Strhodes * Yes, it is. 2400135446Strhodes */ 2401135446Strhodes DP(DEF_LEVEL, 2402135446Strhodes "dns_adb_createfind: name %p is an alias (cached)", 2403135446Strhodes adbname); 2404135446Strhodes alias = ISC_TRUE; 2405135446Strhodes goto post_copy; 2406135446Strhodes } 2407135446Strhodes 2408135446Strhodes /* 2409135446Strhodes * Try to populate the name from the database and/or 2410135446Strhodes * start fetches. First try looking for an A record 2411135446Strhodes * in the database. 2412135446Strhodes */ 2413135446Strhodes if (!NAME_HAS_V4(adbname) && EXPIRE_OK(adbname->expire_v4, now) 2414135446Strhodes && WANT_INET(wanted_addresses)) { 2415135446Strhodes result = dbfind_name(adbname, now, dns_rdatatype_a); 2416135446Strhodes if (result == ISC_R_SUCCESS) { 2417135446Strhodes DP(DEF_LEVEL, 2418135446Strhodes "dns_adb_createfind: found A for name %p in db", 2419135446Strhodes adbname); 2420135446Strhodes goto v6; 2421135446Strhodes } 2422135446Strhodes 2423135446Strhodes /* 2424135446Strhodes * Did we get a CNAME or DNAME? 2425135446Strhodes */ 2426135446Strhodes if (result == DNS_R_ALIAS) { 2427135446Strhodes DP(DEF_LEVEL, 2428135446Strhodes "dns_adb_createfind: name %p is an alias", 2429135446Strhodes adbname); 2430135446Strhodes alias = ISC_TRUE; 2431135446Strhodes goto post_copy; 2432135446Strhodes } 2433135446Strhodes 2434135446Strhodes /* 2435135446Strhodes * If the name doesn't exist at all, don't bother with 2436135446Strhodes * v6 queries; they won't work. 2437135446Strhodes * 2438135446Strhodes * If the name does exist but we didn't get our data, go 2439135446Strhodes * ahead and try AAAA. 2440135446Strhodes * 2441135446Strhodes * If the result is neither of these, try a fetch for A. 2442135446Strhodes */ 2443135446Strhodes if (NXDOMAIN_RESULT(result)) 2444135446Strhodes goto fetch; 2445135446Strhodes else if (NXRRSET_RESULT(result)) 2446135446Strhodes goto v6; 2447135446Strhodes 2448135446Strhodes if (!NAME_FETCH_V4(adbname)) 2449135446Strhodes wanted_fetches |= DNS_ADBFIND_INET; 2450135446Strhodes } 2451135446Strhodes 2452135446Strhodes v6: 2453135446Strhodes if (!NAME_HAS_V6(adbname) && EXPIRE_OK(adbname->expire_v6, now) 2454135446Strhodes && WANT_INET6(wanted_addresses)) { 2455135446Strhodes result = dbfind_name(adbname, now, dns_rdatatype_aaaa); 2456135446Strhodes if (result == ISC_R_SUCCESS) { 2457135446Strhodes DP(DEF_LEVEL, 2458135446Strhodes "dns_adb_createfind: found AAAA for name %p", 2459135446Strhodes adbname); 2460135446Strhodes goto fetch; 2461135446Strhodes } 2462135446Strhodes 2463135446Strhodes /* 2464135446Strhodes * Did we get a CNAME or DNAME? 2465135446Strhodes */ 2466135446Strhodes if (result == DNS_R_ALIAS) { 2467135446Strhodes DP(DEF_LEVEL, 2468135446Strhodes "dns_adb_createfind: name %p is an alias", 2469135446Strhodes adbname); 2470135446Strhodes alias = ISC_TRUE; 2471135446Strhodes goto post_copy; 2472135446Strhodes } 2473135446Strhodes 2474135446Strhodes /* 2475135446Strhodes * Listen to negative cache hints, and don't start 2476135446Strhodes * another query. 2477135446Strhodes */ 2478135446Strhodes if (NCACHE_RESULT(result) || AUTH_NX(result)) 2479135446Strhodes goto fetch; 2480135446Strhodes 2481135446Strhodes if (!NAME_FETCH_V6(adbname)) 2482135446Strhodes wanted_fetches |= DNS_ADBFIND_INET6; 2483135446Strhodes } 2484135446Strhodes 2485135446Strhodes fetch: 2486135446Strhodes if ((WANT_INET(wanted_addresses) && NAME_HAS_V4(adbname)) || 2487135446Strhodes (WANT_INET6(wanted_addresses) && NAME_HAS_V6(adbname))) 2488135446Strhodes have_address = ISC_TRUE; 2489135446Strhodes else 2490135446Strhodes have_address = ISC_FALSE; 2491135446Strhodes if (wanted_fetches != 0 && 2492135446Strhodes ! (FIND_AVOIDFETCHES(find) && have_address)) { 2493135446Strhodes /* 2494135446Strhodes * We're missing at least one address family. Either the 2495135446Strhodes * caller hasn't instructed us to avoid fetches, or we don't 2496135446Strhodes * know anything about any of the address families that would 2497135446Strhodes * be acceptable so we have to launch fetches. 2498135446Strhodes */ 2499135446Strhodes 2500135446Strhodes if (FIND_STARTATZONE(find)) 2501135446Strhodes start_at_zone = ISC_TRUE; 2502135446Strhodes 2503135446Strhodes /* 2504135446Strhodes * Start V4. 2505135446Strhodes */ 2506135446Strhodes if (WANT_INET(wanted_fetches) && 2507135446Strhodes fetch_name(adbname, start_at_zone, 2508135446Strhodes dns_rdatatype_a) == ISC_R_SUCCESS) { 2509135446Strhodes DP(DEF_LEVEL, 2510135446Strhodes "dns_adb_createfind: started A fetch for name %p", 2511135446Strhodes adbname); 2512135446Strhodes } 2513135446Strhodes 2514135446Strhodes /* 2515135446Strhodes * Start V6. 2516135446Strhodes */ 2517135446Strhodes if (WANT_INET6(wanted_fetches) && 2518135446Strhodes fetch_name(adbname, start_at_zone, 2519135446Strhodes dns_rdatatype_aaaa) == ISC_R_SUCCESS) { 2520135446Strhodes DP(DEF_LEVEL, 2521135446Strhodes "dns_adb_createfind: " 2522135446Strhodes "started AAAA fetch for name %p", 2523135446Strhodes adbname); 2524135446Strhodes } 2525135446Strhodes } 2526135446Strhodes 2527135446Strhodes /* 2528135446Strhodes * Run through the name and copy out the bits we are 2529135446Strhodes * interested in. 2530135446Strhodes */ 2531170222Sdougb copy_namehook_lists(adb, find, qname, qtype, adbname, now); 2532135446Strhodes 2533135446Strhodes post_copy: 2534135446Strhodes if (NAME_FETCH_V4(adbname)) 2535135446Strhodes query_pending |= DNS_ADBFIND_INET; 2536135446Strhodes if (NAME_FETCH_V6(adbname)) 2537135446Strhodes query_pending |= DNS_ADBFIND_INET6; 2538135446Strhodes 2539135446Strhodes /* 2540135446Strhodes * Attach to the name's query list if there are queries 2541135446Strhodes * already running, and we have been asked to. 2542135446Strhodes */ 2543135446Strhodes want_event = ISC_TRUE; 2544135446Strhodes if (!FIND_WANTEVENT(find)) 2545135446Strhodes want_event = ISC_FALSE; 2546135446Strhodes if (FIND_WANTEMPTYEVENT(find) && FIND_HAS_ADDRS(find)) 2547135446Strhodes want_event = ISC_FALSE; 2548135446Strhodes if ((wanted_addresses & query_pending) == 0) 2549135446Strhodes want_event = ISC_FALSE; 2550135446Strhodes if (alias) 2551135446Strhodes want_event = ISC_FALSE; 2552135446Strhodes if (want_event) { 2553135446Strhodes find->adbname = adbname; 2554135446Strhodes find->name_bucket = bucket; 2555135446Strhodes ISC_LIST_APPEND(adbname->finds, find, plink); 2556135446Strhodes find->query_pending = (query_pending & wanted_addresses); 2557135446Strhodes find->flags &= ~DNS_ADBFIND_ADDRESSMASK; 2558135446Strhodes find->flags |= (find->query_pending & DNS_ADBFIND_ADDRESSMASK); 2559135446Strhodes DP(DEF_LEVEL, "createfind: attaching find %p to adbname %p", 2560135446Strhodes find, adbname); 2561135446Strhodes } else { 2562135446Strhodes /* 2563135446Strhodes * Remove the flag so the caller knows there will never 2564135446Strhodes * be an event, and set internal flags to fake that 2565135446Strhodes * the event was sent and freed, so dns_adb_destroyfind() will 2566135446Strhodes * do the right thing. 2567135446Strhodes */ 2568135446Strhodes find->query_pending = (query_pending & wanted_addresses); 2569135446Strhodes find->options &= ~DNS_ADBFIND_WANTEVENT; 2570135446Strhodes find->flags |= (FIND_EVENT_SENT | FIND_EVENT_FREED); 2571135446Strhodes find->flags &= ~DNS_ADBFIND_ADDRESSMASK; 2572135446Strhodes } 2573135446Strhodes 2574135446Strhodes find->partial_result |= (adbname->partial_result & wanted_addresses); 2575135446Strhodes if (alias) { 2576135446Strhodes if (target != NULL) { 2577135446Strhodes result = dns_name_copy(&adbname->target, target, NULL); 2578135446Strhodes if (result != ISC_R_SUCCESS) 2579135446Strhodes goto out; 2580135446Strhodes } 2581135446Strhodes result = DNS_R_ALIAS; 2582135446Strhodes } else 2583135446Strhodes result = ISC_R_SUCCESS; 2584135446Strhodes 2585135446Strhodes /* 2586135446Strhodes * Copy out error flags from the name structure into the find. 2587135446Strhodes */ 2588135446Strhodes find->result_v4 = find_err_map[adbname->fetch_err]; 2589135446Strhodes find->result_v6 = find_err_map[adbname->fetch6_err]; 2590135446Strhodes 2591135446Strhodes out: 2592135446Strhodes if (find != NULL) { 2593135446Strhodes *findp = find; 2594135446Strhodes 2595135446Strhodes if (want_event) { 2596135446Strhodes isc_task_t *taskp; 2597135446Strhodes 2598135446Strhodes INSIST((find->flags & DNS_ADBFIND_ADDRESSMASK) != 0); 2599135446Strhodes taskp = NULL; 2600135446Strhodes isc_task_attach(task, &taskp); 2601135446Strhodes find->event.ev_sender = taskp; 2602135446Strhodes find->event.ev_action = action; 2603135446Strhodes find->event.ev_arg = arg; 2604135446Strhodes } 2605135446Strhodes } 2606135446Strhodes 2607165071Sdougb UNLOCK(&adb->namelocks[bucket]); 2608135446Strhodes 2609135446Strhodes return (result); 2610135446Strhodes} 2611135446Strhodes 2612135446Strhodesvoid 2613135446Strhodesdns_adb_destroyfind(dns_adbfind_t **findp) { 2614135446Strhodes dns_adbfind_t *find; 2615135446Strhodes dns_adbentry_t *entry; 2616135446Strhodes dns_adbaddrinfo_t *ai; 2617135446Strhodes int bucket; 2618135446Strhodes dns_adb_t *adb; 2619135446Strhodes 2620135446Strhodes REQUIRE(findp != NULL && DNS_ADBFIND_VALID(*findp)); 2621135446Strhodes find = *findp; 2622135446Strhodes *findp = NULL; 2623135446Strhodes 2624135446Strhodes LOCK(&find->lock); 2625135446Strhodes 2626135446Strhodes DP(DEF_LEVEL, "dns_adb_destroyfind on find %p", find); 2627135446Strhodes 2628135446Strhodes adb = find->adb; 2629135446Strhodes REQUIRE(DNS_ADB_VALID(adb)); 2630135446Strhodes 2631135446Strhodes REQUIRE(FIND_EVENTFREED(find)); 2632135446Strhodes 2633135446Strhodes bucket = find->name_bucket; 2634135446Strhodes INSIST(bucket == DNS_ADB_INVALIDBUCKET); 2635135446Strhodes 2636135446Strhodes UNLOCK(&find->lock); 2637135446Strhodes 2638135446Strhodes /* 2639135446Strhodes * The find doesn't exist on any list, and nothing is locked. 2640135446Strhodes * Return the find to the memory pool, and decrement the adb's 2641135446Strhodes * reference count. 2642135446Strhodes */ 2643135446Strhodes ai = ISC_LIST_HEAD(find->list); 2644135446Strhodes while (ai != NULL) { 2645135446Strhodes ISC_LIST_UNLINK(find->list, ai, publink); 2646135446Strhodes entry = ai->entry; 2647135446Strhodes ai->entry = NULL; 2648135446Strhodes INSIST(DNS_ADBENTRY_VALID(entry)); 2649135446Strhodes RUNTIME_CHECK(dec_entry_refcnt(adb, entry, ISC_TRUE) == 2650135446Strhodes ISC_FALSE); 2651135446Strhodes free_adbaddrinfo(adb, &ai); 2652135446Strhodes ai = ISC_LIST_HEAD(find->list); 2653135446Strhodes } 2654135446Strhodes 2655135446Strhodes /* 2656135446Strhodes * WARNING: The find is freed with the adb locked. This is done 2657135446Strhodes * to avoid a race condition where we free the find, some other 2658135446Strhodes * thread tests to see if it should be destroyed, detects it should 2659135446Strhodes * be, destroys it, and then we try to lock it for our check, but the 2660135446Strhodes * lock is destroyed. 2661135446Strhodes */ 2662135446Strhodes LOCK(&adb->lock); 2663135446Strhodes if (free_adbfind(adb, &find)) 2664135446Strhodes check_exit(adb); 2665135446Strhodes UNLOCK(&adb->lock); 2666135446Strhodes} 2667135446Strhodes 2668135446Strhodesvoid 2669135446Strhodesdns_adb_cancelfind(dns_adbfind_t *find) { 2670135446Strhodes isc_event_t *ev; 2671135446Strhodes isc_task_t *task; 2672135446Strhodes dns_adb_t *adb; 2673135446Strhodes int bucket; 2674135446Strhodes int unlock_bucket; 2675135446Strhodes 2676135446Strhodes LOCK(&find->lock); 2677135446Strhodes 2678135446Strhodes DP(DEF_LEVEL, "dns_adb_cancelfind on find %p", find); 2679135446Strhodes 2680135446Strhodes adb = find->adb; 2681135446Strhodes REQUIRE(DNS_ADB_VALID(adb)); 2682135446Strhodes 2683135446Strhodes REQUIRE(!FIND_EVENTFREED(find)); 2684135446Strhodes REQUIRE(FIND_WANTEVENT(find)); 2685135446Strhodes 2686135446Strhodes bucket = find->name_bucket; 2687135446Strhodes if (bucket == DNS_ADB_INVALIDBUCKET) 2688135446Strhodes goto cleanup; 2689135446Strhodes 2690135446Strhodes /* 2691135446Strhodes * We need to get the adbname's lock to unlink the find. 2692135446Strhodes */ 2693135446Strhodes unlock_bucket = bucket; 2694135446Strhodes violate_locking_hierarchy(&find->lock, &adb->namelocks[unlock_bucket]); 2695135446Strhodes bucket = find->name_bucket; 2696135446Strhodes if (bucket != DNS_ADB_INVALIDBUCKET) { 2697135446Strhodes ISC_LIST_UNLINK(find->adbname->finds, find, plink); 2698135446Strhodes find->adbname = NULL; 2699135446Strhodes find->name_bucket = DNS_ADB_INVALIDBUCKET; 2700135446Strhodes } 2701135446Strhodes UNLOCK(&adb->namelocks[unlock_bucket]); 2702135446Strhodes bucket = DNS_ADB_INVALIDBUCKET; 2703135446Strhodes 2704135446Strhodes cleanup: 2705135446Strhodes 2706135446Strhodes if (!FIND_EVENTSENT(find)) { 2707135446Strhodes ev = &find->event; 2708135446Strhodes task = ev->ev_sender; 2709135446Strhodes ev->ev_sender = find; 2710135446Strhodes ev->ev_type = DNS_EVENT_ADBCANCELED; 2711135446Strhodes ev->ev_destroy = event_free; 2712135446Strhodes ev->ev_destroy_arg = find; 2713135446Strhodes find->result_v4 = ISC_R_CANCELED; 2714135446Strhodes find->result_v6 = ISC_R_CANCELED; 2715135446Strhodes 2716135446Strhodes DP(DEF_LEVEL, "sending event %p to task %p for find %p", 2717135446Strhodes ev, task, find); 2718135446Strhodes 2719135446Strhodes isc_task_sendanddetach(&task, (isc_event_t **)&ev); 2720135446Strhodes } 2721135446Strhodes 2722135446Strhodes UNLOCK(&find->lock); 2723135446Strhodes} 2724135446Strhodes 2725135446Strhodesvoid 2726135446Strhodesdns_adb_dump(dns_adb_t *adb, FILE *f) { 2727143731Sdougb int i; 2728143731Sdougb isc_stdtime_t now; 2729143731Sdougb 2730135446Strhodes REQUIRE(DNS_ADB_VALID(adb)); 2731135446Strhodes REQUIRE(f != NULL); 2732135446Strhodes 2733135446Strhodes /* 2734135446Strhodes * Lock the adb itself, lock all the name buckets, then lock all 2735135446Strhodes * the entry buckets. This should put the adb into a state where 2736135446Strhodes * nothing can change, so we can iterate through everything and 2737135446Strhodes * print at our leisure. 2738135446Strhodes */ 2739135446Strhodes 2740135446Strhodes LOCK(&adb->lock); 2741143731Sdougb isc_stdtime_get(&now); 2742143731Sdougb 2743143731Sdougb for (i = 0; i < NBUCKETS; i++) 2744143731Sdougb RUNTIME_CHECK(cleanup_names(adb, i, now) == ISC_FALSE); 2745143731Sdougb for (i = 0; i < NBUCKETS; i++) 2746143731Sdougb RUNTIME_CHECK(cleanup_entries(adb, i, now) == ISC_FALSE); 2747143731Sdougb 2748143731Sdougb dump_adb(adb, f, ISC_FALSE, now); 2749135446Strhodes UNLOCK(&adb->lock); 2750135446Strhodes} 2751135446Strhodes 2752135446Strhodesstatic void 2753135446Strhodesdump_ttl(FILE *f, const char *legend, isc_stdtime_t value, isc_stdtime_t now) { 2754135446Strhodes if (value == INT_MAX) 2755135446Strhodes return; 2756135446Strhodes fprintf(f, " [%s TTL %d]", legend, value - now); 2757135446Strhodes} 2758135446Strhodes 2759135446Strhodesstatic void 2760143731Sdougbdump_adb(dns_adb_t *adb, FILE *f, isc_boolean_t debug, isc_stdtime_t now) { 2761135446Strhodes int i; 2762135446Strhodes dns_adbname_t *name; 2763143731Sdougb dns_adbentry_t *entry; 2764135446Strhodes 2765135446Strhodes fprintf(f, ";\n; Address database dump\n;\n"); 2766135446Strhodes if (debug) 2767135446Strhodes fprintf(f, "; addr %p, erefcnt %u, irefcnt %u, finds out %u\n", 2768135446Strhodes adb, adb->erefcnt, adb->irefcnt, 2769135446Strhodes isc_mempool_getallocated(adb->nhmp)); 2770135446Strhodes 2771135446Strhodes for (i = 0; i < NBUCKETS; i++) 2772135446Strhodes LOCK(&adb->namelocks[i]); 2773135446Strhodes for (i = 0; i < NBUCKETS; i++) 2774135446Strhodes LOCK(&adb->entrylocks[i]); 2775135446Strhodes 2776135446Strhodes /* 2777135446Strhodes * Dump the names 2778135446Strhodes */ 2779135446Strhodes for (i = 0; i < NBUCKETS; i++) { 2780135446Strhodes name = ISC_LIST_HEAD(adb->names[i]); 2781135446Strhodes if (name == NULL) 2782135446Strhodes continue; 2783135446Strhodes if (debug) 2784135446Strhodes fprintf(f, "; bucket %d\n", i); 2785135446Strhodes for (; 2786135446Strhodes name != NULL; 2787135446Strhodes name = ISC_LIST_NEXT(name, plink)) 2788135446Strhodes { 2789135446Strhodes if (debug) 2790135446Strhodes fprintf(f, "; name %p (flags %08x)\n", 2791135446Strhodes name, name->flags); 2792135446Strhodes 2793135446Strhodes fprintf(f, "; "); 2794135446Strhodes print_dns_name(f, &name->name); 2795135446Strhodes if (dns_name_countlabels(&name->target) > 0) { 2796135446Strhodes fprintf(f, " alias "); 2797135446Strhodes print_dns_name(f, &name->target); 2798135446Strhodes } 2799135446Strhodes 2800135446Strhodes dump_ttl(f, "v4", name->expire_v4, now); 2801135446Strhodes dump_ttl(f, "v6", name->expire_v6, now); 2802135446Strhodes dump_ttl(f, "target", name->expire_target, now); 2803135446Strhodes 2804135446Strhodes fprintf(f, " [v4 %s] [v6 %s]", 2805135446Strhodes errnames[name->fetch_err], 2806135446Strhodes errnames[name->fetch6_err]); 2807135446Strhodes 2808135446Strhodes fprintf(f, "\n"); 2809135446Strhodes 2810135446Strhodes print_namehook_list(f, "v4", &name->v4, debug, now); 2811135446Strhodes print_namehook_list(f, "v6", &name->v6, debug, now); 2812135446Strhodes 2813135446Strhodes if (debug) 2814135446Strhodes print_fetch_list(f, name); 2815135446Strhodes if (debug) 2816135446Strhodes print_find_list(f, name); 2817135446Strhodes 2818135446Strhodes } 2819135446Strhodes } 2820135446Strhodes 2821143731Sdougb fprintf(f, ";\n; Unassociated entries\n;\n"); 2822143731Sdougb 2823143731Sdougb for (i = 0; i < NBUCKETS; i++) { 2824143731Sdougb entry = ISC_LIST_HEAD(adb->entries[i]); 2825143731Sdougb while (entry != NULL) { 2826143731Sdougb if (entry->refcnt == 0) 2827143731Sdougb dump_entry(f, entry, debug, now); 2828143731Sdougb entry = ISC_LIST_NEXT(entry, plink); 2829143731Sdougb } 2830143731Sdougb } 2831143731Sdougb 2832135446Strhodes /* 2833135446Strhodes * Unlock everything 2834135446Strhodes */ 2835135446Strhodes for (i = 0; i < NBUCKETS; i++) 2836135446Strhodes UNLOCK(&adb->entrylocks[i]); 2837135446Strhodes for (i = 0; i < NBUCKETS; i++) 2838135446Strhodes UNLOCK(&adb->namelocks[i]); 2839135446Strhodes} 2840135446Strhodes 2841135446Strhodesstatic void 2842135446Strhodesdump_entry(FILE *f, dns_adbentry_t *entry, isc_boolean_t debug, 2843135446Strhodes isc_stdtime_t now) 2844135446Strhodes{ 2845135446Strhodes char addrbuf[ISC_NETADDR_FORMATSIZE]; 2846170222Sdougb char typebuf[DNS_RDATATYPE_FORMATSIZE]; 2847135446Strhodes isc_netaddr_t netaddr; 2848170222Sdougb dns_adblameinfo_t *li; 2849135446Strhodes 2850135446Strhodes isc_netaddr_fromsockaddr(&netaddr, &entry->sockaddr); 2851135446Strhodes isc_netaddr_format(&netaddr, addrbuf, sizeof(addrbuf)); 2852135446Strhodes 2853135446Strhodes if (debug) 2854135446Strhodes fprintf(f, ";\t%p: refcnt %u\n", entry, entry->refcnt); 2855135446Strhodes 2856135446Strhodes fprintf(f, ";\t%s [srtt %u] [flags %08x]", 2857135446Strhodes addrbuf, entry->srtt, entry->flags); 2858143731Sdougb if (entry->expires != 0) 2859143731Sdougb fprintf(f, " [ttl %d]", entry->expires - now); 2860135446Strhodes fprintf(f, "\n"); 2861170222Sdougb for (li = ISC_LIST_HEAD(entry->lameinfo); 2862170222Sdougb li != NULL; 2863170222Sdougb li = ISC_LIST_NEXT(li, plink)) { 2864135446Strhodes fprintf(f, ";\t\t"); 2865170222Sdougb print_dns_name(f, &li->qname); 2866170222Sdougb dns_rdatatype_format(li->qtype, typebuf, sizeof(typebuf)); 2867170222Sdougb fprintf(f, " %s [lame TTL %d]\n", typebuf, 2868170222Sdougb li->lame_timer - now); 2869135446Strhodes } 2870135446Strhodes} 2871135446Strhodes 2872135446Strhodesvoid 2873135446Strhodesdns_adb_dumpfind(dns_adbfind_t *find, FILE *f) { 2874135446Strhodes char tmp[512]; 2875135446Strhodes const char *tmpp; 2876135446Strhodes dns_adbaddrinfo_t *ai; 2877135446Strhodes isc_sockaddr_t *sa; 2878135446Strhodes 2879135446Strhodes /* 2880135446Strhodes * Not used currently, in the API Just In Case we 2881135446Strhodes * want to dump out the name and/or entries too. 2882135446Strhodes */ 2883135446Strhodes 2884135446Strhodes LOCK(&find->lock); 2885135446Strhodes 2886135446Strhodes fprintf(f, ";Find %p\n", find); 2887135446Strhodes fprintf(f, ";\tqpending %08x partial %08x options %08x flags %08x\n", 2888135446Strhodes find->query_pending, find->partial_result, 2889135446Strhodes find->options, find->flags); 2890135446Strhodes fprintf(f, ";\tname_bucket %d, name %p, event sender %p\n", 2891135446Strhodes find->name_bucket, find->adbname, find->event.ev_sender); 2892135446Strhodes 2893135446Strhodes ai = ISC_LIST_HEAD(find->list); 2894135446Strhodes if (ai != NULL) 2895135446Strhodes fprintf(f, "\tAddresses:\n"); 2896135446Strhodes while (ai != NULL) { 2897135446Strhodes sa = &ai->sockaddr; 2898135446Strhodes switch (sa->type.sa.sa_family) { 2899135446Strhodes case AF_INET: 2900135446Strhodes tmpp = inet_ntop(AF_INET, &sa->type.sin.sin_addr, 2901135446Strhodes tmp, sizeof(tmp)); 2902135446Strhodes break; 2903135446Strhodes case AF_INET6: 2904135446Strhodes tmpp = inet_ntop(AF_INET6, &sa->type.sin6.sin6_addr, 2905135446Strhodes tmp, sizeof(tmp)); 2906135446Strhodes break; 2907135446Strhodes default: 2908135446Strhodes tmpp = "UnkFamily"; 2909135446Strhodes } 2910135446Strhodes 2911135446Strhodes if (tmpp == NULL) 2912135446Strhodes tmpp = "BadAddress"; 2913135446Strhodes 2914135446Strhodes fprintf(f, "\t\tentry %p, flags %08x" 2915135446Strhodes " srtt %u addr %s\n", 2916135446Strhodes ai->entry, ai->flags, ai->srtt, tmpp); 2917135446Strhodes 2918135446Strhodes ai = ISC_LIST_NEXT(ai, publink); 2919135446Strhodes } 2920135446Strhodes 2921135446Strhodes UNLOCK(&find->lock); 2922135446Strhodes} 2923135446Strhodes 2924135446Strhodesstatic void 2925135446Strhodesprint_dns_name(FILE *f, dns_name_t *name) { 2926135446Strhodes char buf[DNS_NAME_FORMATSIZE]; 2927135446Strhodes 2928135446Strhodes INSIST(f != NULL); 2929135446Strhodes 2930135446Strhodes dns_name_format(name, buf, sizeof(buf)); 2931135446Strhodes fprintf(f, "%s", buf); 2932135446Strhodes} 2933135446Strhodes 2934135446Strhodesstatic void 2935135446Strhodesprint_namehook_list(FILE *f, const char *legend, dns_adbnamehooklist_t *list, 2936135446Strhodes isc_boolean_t debug, isc_stdtime_t now) 2937135446Strhodes{ 2938135446Strhodes dns_adbnamehook_t *nh; 2939135446Strhodes 2940135446Strhodes for (nh = ISC_LIST_HEAD(*list); 2941135446Strhodes nh != NULL; 2942135446Strhodes nh = ISC_LIST_NEXT(nh, plink)) 2943135446Strhodes { 2944135446Strhodes if (debug) 2945135446Strhodes fprintf(f, ";\tHook(%s) %p\n", legend, nh); 2946135446Strhodes dump_entry(f, nh->entry, debug, now); 2947135446Strhodes } 2948135446Strhodes} 2949135446Strhodes 2950135446Strhodesstatic inline void 2951135446Strhodesprint_fetch(FILE *f, dns_adbfetch_t *ft, const char *type) { 2952193149Sdougb fprintf(f, "\t\tFetch(%s): %p -> { fetch %p }\n", 2953193149Sdougb type, ft, ft->fetch); 2954135446Strhodes} 2955135446Strhodes 2956135446Strhodesstatic void 2957135446Strhodesprint_fetch_list(FILE *f, dns_adbname_t *n) { 2958135446Strhodes if (NAME_FETCH_A(n)) 2959135446Strhodes print_fetch(f, n->fetch_a, "A"); 2960135446Strhodes if (NAME_FETCH_AAAA(n)) 2961135446Strhodes print_fetch(f, n->fetch_aaaa, "AAAA"); 2962135446Strhodes} 2963135446Strhodes 2964135446Strhodesstatic void 2965135446Strhodesprint_find_list(FILE *f, dns_adbname_t *name) { 2966135446Strhodes dns_adbfind_t *find; 2967135446Strhodes 2968135446Strhodes find = ISC_LIST_HEAD(name->finds); 2969135446Strhodes while (find != NULL) { 2970135446Strhodes dns_adb_dumpfind(find, f); 2971135446Strhodes find = ISC_LIST_NEXT(find, plink); 2972135446Strhodes } 2973135446Strhodes} 2974135446Strhodes 2975135446Strhodesstatic isc_result_t 2976135446Strhodesdbfind_name(dns_adbname_t *adbname, isc_stdtime_t now, dns_rdatatype_t rdtype) 2977135446Strhodes{ 2978135446Strhodes isc_result_t result; 2979135446Strhodes dns_rdataset_t rdataset; 2980135446Strhodes dns_adb_t *adb; 2981135446Strhodes dns_fixedname_t foundname; 2982135446Strhodes dns_name_t *fname; 2983135446Strhodes 2984135446Strhodes INSIST(DNS_ADBNAME_VALID(adbname)); 2985135446Strhodes adb = adbname->adb; 2986135446Strhodes INSIST(DNS_ADB_VALID(adb)); 2987135446Strhodes INSIST(rdtype == dns_rdatatype_a || rdtype == dns_rdatatype_aaaa); 2988135446Strhodes 2989135446Strhodes dns_fixedname_init(&foundname); 2990193149Sdougb fname = dns_fixedname_name(&foundname); 2991135446Strhodes dns_rdataset_init(&rdataset); 2992135446Strhodes 2993135446Strhodes if (rdtype == dns_rdatatype_a) 2994135446Strhodes adbname->fetch_err = FIND_ERR_UNEXPECTED; 2995135446Strhodes else 2996135446Strhodes adbname->fetch6_err = FIND_ERR_UNEXPECTED; 2997135446Strhodes 2998135446Strhodes result = dns_view_find(adb->view, &adbname->name, rdtype, now, 2999174187Sdougb NAME_GLUEOK(adbname) ? DNS_DBFIND_GLUEOK : 0, 3000135446Strhodes ISC_TF(NAME_HINTOK(adbname)), 3001135446Strhodes NULL, NULL, fname, &rdataset, NULL); 3002135446Strhodes 3003135446Strhodes /* XXXVIX this switch statement is too sparse to gen a jump table. */ 3004135446Strhodes switch (result) { 3005135446Strhodes case DNS_R_GLUE: 3006135446Strhodes case DNS_R_HINT: 3007135446Strhodes case ISC_R_SUCCESS: 3008135446Strhodes /* 3009135446Strhodes * Found in the database. Even if we can't copy out 3010135446Strhodes * any information, return success, or else a fetch 3011135446Strhodes * will be made, which will only make things worse. 3012135446Strhodes */ 3013135446Strhodes if (rdtype == dns_rdatatype_a) 3014135446Strhodes adbname->fetch_err = FIND_ERR_SUCCESS; 3015135446Strhodes else 3016135446Strhodes adbname->fetch6_err = FIND_ERR_SUCCESS; 3017135446Strhodes result = import_rdataset(adbname, &rdataset, now); 3018135446Strhodes break; 3019135446Strhodes case DNS_R_NXDOMAIN: 3020135446Strhodes case DNS_R_NXRRSET: 3021135446Strhodes /* 3022135446Strhodes * We're authoritative and the data doesn't exist. 3023135446Strhodes * Make up a negative cache entry so we don't ask again 3024135446Strhodes * for a while. 3025135446Strhodes * 3026135446Strhodes * XXXRTH What time should we use? I'm putting in 30 seconds 3027135446Strhodes * for now. 3028135446Strhodes */ 3029135446Strhodes if (rdtype == dns_rdatatype_a) { 3030135446Strhodes adbname->expire_v4 = now + 30; 3031135446Strhodes DP(NCACHE_LEVEL, 3032135446Strhodes "adb name %p: Caching auth negative entry for A", 3033135446Strhodes adbname); 3034135446Strhodes if (result == DNS_R_NXDOMAIN) 3035135446Strhodes adbname->fetch_err = FIND_ERR_NXDOMAIN; 3036135446Strhodes else 3037135446Strhodes adbname->fetch_err = FIND_ERR_NXRRSET; 3038135446Strhodes } else { 3039135446Strhodes DP(NCACHE_LEVEL, 3040135446Strhodes "adb name %p: Caching auth negative entry for AAAA", 3041135446Strhodes adbname); 3042135446Strhodes adbname->expire_v6 = now + 30; 3043135446Strhodes if (result == DNS_R_NXDOMAIN) 3044135446Strhodes adbname->fetch6_err = FIND_ERR_NXDOMAIN; 3045135446Strhodes else 3046135446Strhodes adbname->fetch6_err = FIND_ERR_NXRRSET; 3047135446Strhodes } 3048135446Strhodes break; 3049135446Strhodes case DNS_R_NCACHENXDOMAIN: 3050135446Strhodes case DNS_R_NCACHENXRRSET: 3051135446Strhodes /* 3052135446Strhodes * We found a negative cache entry. Pull the TTL from it 3053135446Strhodes * so we won't ask again for a while. 3054135446Strhodes */ 3055135446Strhodes rdataset.ttl = ttlclamp(rdataset.ttl); 3056135446Strhodes if (rdtype == dns_rdatatype_a) { 3057135446Strhodes adbname->expire_v4 = rdataset.ttl + now; 3058135446Strhodes if (result == DNS_R_NCACHENXDOMAIN) 3059135446Strhodes adbname->fetch_err = FIND_ERR_NXDOMAIN; 3060135446Strhodes else 3061135446Strhodes adbname->fetch_err = FIND_ERR_NXRRSET; 3062135446Strhodes DP(NCACHE_LEVEL, 3063135446Strhodes "adb name %p: Caching negative entry for A (ttl %u)", 3064135446Strhodes adbname, rdataset.ttl); 3065135446Strhodes } else { 3066135446Strhodes DP(NCACHE_LEVEL, 3067135446Strhodes "adb name %p: Caching negative entry for AAAA (ttl %u)", 3068135446Strhodes adbname, rdataset.ttl); 3069135446Strhodes adbname->expire_v6 = rdataset.ttl + now; 3070135446Strhodes if (result == DNS_R_NCACHENXDOMAIN) 3071135446Strhodes adbname->fetch6_err = FIND_ERR_NXDOMAIN; 3072135446Strhodes else 3073135446Strhodes adbname->fetch6_err = FIND_ERR_NXRRSET; 3074135446Strhodes } 3075135446Strhodes break; 3076135446Strhodes case DNS_R_CNAME: 3077135446Strhodes case DNS_R_DNAME: 3078135446Strhodes /* 3079135446Strhodes * Clear the hint and glue flags, so this will match 3080135446Strhodes * more often. 3081135446Strhodes */ 3082135446Strhodes adbname->flags &= ~(DNS_ADBFIND_GLUEOK | DNS_ADBFIND_HINTOK); 3083135446Strhodes 3084135446Strhodes rdataset.ttl = ttlclamp(rdataset.ttl); 3085135446Strhodes clean_target(adb, &adbname->target); 3086135446Strhodes adbname->expire_target = INT_MAX; 3087135446Strhodes result = set_target(adb, &adbname->name, fname, &rdataset, 3088135446Strhodes &adbname->target); 3089135446Strhodes if (result == ISC_R_SUCCESS) { 3090135446Strhodes result = DNS_R_ALIAS; 3091135446Strhodes DP(NCACHE_LEVEL, 3092135446Strhodes "adb name %p: caching alias target", 3093135446Strhodes adbname); 3094135446Strhodes adbname->expire_target = rdataset.ttl + now; 3095135446Strhodes } 3096135446Strhodes if (rdtype == dns_rdatatype_a) 3097135446Strhodes adbname->fetch_err = FIND_ERR_SUCCESS; 3098135446Strhodes else 3099135446Strhodes adbname->fetch6_err = FIND_ERR_SUCCESS; 3100135446Strhodes break; 3101135446Strhodes } 3102135446Strhodes 3103135446Strhodes if (dns_rdataset_isassociated(&rdataset)) 3104135446Strhodes dns_rdataset_disassociate(&rdataset); 3105135446Strhodes 3106135446Strhodes return (result); 3107135446Strhodes} 3108135446Strhodes 3109135446Strhodesstatic void 3110135446Strhodesfetch_callback(isc_task_t *task, isc_event_t *ev) { 3111135446Strhodes dns_fetchevent_t *dev; 3112135446Strhodes dns_adbname_t *name; 3113135446Strhodes dns_adb_t *adb; 3114135446Strhodes dns_adbfetch_t *fetch; 3115135446Strhodes int bucket; 3116135446Strhodes isc_eventtype_t ev_status; 3117135446Strhodes isc_stdtime_t now; 3118135446Strhodes isc_result_t result; 3119135446Strhodes unsigned int address_type; 3120135446Strhodes isc_boolean_t want_check_exit = ISC_FALSE; 3121135446Strhodes 3122135446Strhodes UNUSED(task); 3123135446Strhodes 3124135446Strhodes INSIST(ev->ev_type == DNS_EVENT_FETCHDONE); 3125135446Strhodes dev = (dns_fetchevent_t *)ev; 3126135446Strhodes name = ev->ev_arg; 3127135446Strhodes INSIST(DNS_ADBNAME_VALID(name)); 3128135446Strhodes adb = name->adb; 3129135446Strhodes INSIST(DNS_ADB_VALID(adb)); 3130135446Strhodes 3131135446Strhodes bucket = name->lock_bucket; 3132135446Strhodes LOCK(&adb->namelocks[bucket]); 3133135446Strhodes 3134135446Strhodes INSIST(NAME_FETCH_A(name) || NAME_FETCH_AAAA(name)); 3135135446Strhodes address_type = 0; 3136135446Strhodes if (NAME_FETCH_A(name) && (name->fetch_a->fetch == dev->fetch)) { 3137135446Strhodes address_type = DNS_ADBFIND_INET; 3138135446Strhodes fetch = name->fetch_a; 3139135446Strhodes name->fetch_a = NULL; 3140135446Strhodes } else if (NAME_FETCH_AAAA(name) 3141135446Strhodes && (name->fetch_aaaa->fetch == dev->fetch)) { 3142135446Strhodes address_type = DNS_ADBFIND_INET6; 3143135446Strhodes fetch = name->fetch_aaaa; 3144135446Strhodes name->fetch_aaaa = NULL; 3145186462Sdougb } else 3146186462Sdougb fetch = NULL; 3147135446Strhodes 3148186462Sdougb INSIST(address_type != 0 && fetch != NULL); 3149186462Sdougb 3150135446Strhodes dns_resolver_destroyfetch(&fetch->fetch); 3151135446Strhodes dev->fetch = NULL; 3152135446Strhodes 3153135446Strhodes ev_status = DNS_EVENT_ADBNOMOREADDRESSES; 3154135446Strhodes 3155135446Strhodes /* 3156135446Strhodes * Cleanup things we don't care about. 3157135446Strhodes */ 3158135446Strhodes if (dev->node != NULL) 3159135446Strhodes dns_db_detachnode(dev->db, &dev->node); 3160135446Strhodes if (dev->db != NULL) 3161135446Strhodes dns_db_detach(&dev->db); 3162135446Strhodes 3163135446Strhodes /* 3164135446Strhodes * If this name is marked as dead, clean up, throwing away 3165135446Strhodes * potentially good data. 3166135446Strhodes */ 3167135446Strhodes if (NAME_DEAD(name)) { 3168135446Strhodes free_adbfetch(adb, &fetch); 3169135446Strhodes isc_event_free(&ev); 3170135446Strhodes 3171135446Strhodes want_check_exit = kill_name(&name, DNS_EVENT_ADBCANCELED); 3172135446Strhodes 3173135446Strhodes UNLOCK(&adb->namelocks[bucket]); 3174135446Strhodes 3175135446Strhodes if (want_check_exit) { 3176135446Strhodes LOCK(&adb->lock); 3177135446Strhodes check_exit(adb); 3178135446Strhodes UNLOCK(&adb->lock); 3179135446Strhodes } 3180135446Strhodes 3181135446Strhodes return; 3182135446Strhodes } 3183135446Strhodes 3184135446Strhodes isc_stdtime_get(&now); 3185135446Strhodes 3186135446Strhodes /* 3187135446Strhodes * If we got a negative cache response, remember it. 3188135446Strhodes */ 3189135446Strhodes if (NCACHE_RESULT(dev->result)) { 3190135446Strhodes dev->rdataset->ttl = ttlclamp(dev->rdataset->ttl); 3191135446Strhodes if (address_type == DNS_ADBFIND_INET) { 3192135446Strhodes DP(NCACHE_LEVEL, "adb fetch name %p: " 3193135446Strhodes "caching negative entry for A (ttl %u)", 3194135446Strhodes name, dev->rdataset->ttl); 3195135446Strhodes name->expire_v4 = ISC_MIN(name->expire_v4, 3196135446Strhodes dev->rdataset->ttl + now); 3197135446Strhodes if (dev->result == DNS_R_NCACHENXDOMAIN) 3198135446Strhodes name->fetch_err = FIND_ERR_NXDOMAIN; 3199135446Strhodes else 3200135446Strhodes name->fetch_err = FIND_ERR_NXRRSET; 3201193149Sdougb inc_stats(adb, dns_resstatscounter_gluefetchv4fail); 3202135446Strhodes } else { 3203135446Strhodes DP(NCACHE_LEVEL, "adb fetch name %p: " 3204135446Strhodes "caching negative entry for AAAA (ttl %u)", 3205135446Strhodes name, dev->rdataset->ttl); 3206135446Strhodes name->expire_v6 = ISC_MIN(name->expire_v6, 3207135446Strhodes dev->rdataset->ttl + now); 3208135446Strhodes if (dev->result == DNS_R_NCACHENXDOMAIN) 3209135446Strhodes name->fetch6_err = FIND_ERR_NXDOMAIN; 3210135446Strhodes else 3211135446Strhodes name->fetch6_err = FIND_ERR_NXRRSET; 3212193149Sdougb inc_stats(adb, dns_resstatscounter_gluefetchv6fail); 3213135446Strhodes } 3214135446Strhodes goto out; 3215135446Strhodes } 3216135446Strhodes 3217135446Strhodes /* 3218135446Strhodes * Handle CNAME/DNAME. 3219135446Strhodes */ 3220135446Strhodes if (dev->result == DNS_R_CNAME || dev->result == DNS_R_DNAME) { 3221135446Strhodes dev->rdataset->ttl = ttlclamp(dev->rdataset->ttl); 3222135446Strhodes clean_target(adb, &name->target); 3223135446Strhodes name->expire_target = INT_MAX; 3224135446Strhodes result = set_target(adb, &name->name, 3225135446Strhodes dns_fixedname_name(&dev->foundname), 3226135446Strhodes dev->rdataset, 3227135446Strhodes &name->target); 3228135446Strhodes if (result == ISC_R_SUCCESS) { 3229135446Strhodes DP(NCACHE_LEVEL, 3230135446Strhodes "adb fetch name %p: caching alias target", 3231135446Strhodes name); 3232135446Strhodes name->expire_target = dev->rdataset->ttl + now; 3233135446Strhodes } 3234135446Strhodes goto check_result; 3235135446Strhodes } 3236135446Strhodes 3237135446Strhodes /* 3238135446Strhodes * Did we get back junk? If so, and there are no more fetches 3239135446Strhodes * sitting out there, tell all the finds about it. 3240135446Strhodes */ 3241135446Strhodes if (dev->result != ISC_R_SUCCESS) { 3242135446Strhodes char buf[DNS_NAME_FORMATSIZE]; 3243135446Strhodes 3244135446Strhodes dns_name_format(&name->name, buf, sizeof(buf)); 3245135446Strhodes DP(DEF_LEVEL, "adb: fetch of '%s' %s failed: %s", 3246135446Strhodes buf, address_type == DNS_ADBFIND_INET ? "A" : "AAAA", 3247135446Strhodes dns_result_totext(dev->result)); 3248135446Strhodes /* XXXMLG Don't pound on bad servers. */ 3249135446Strhodes if (address_type == DNS_ADBFIND_INET) { 3250135446Strhodes name->expire_v4 = ISC_MIN(name->expire_v4, now + 300); 3251135446Strhodes name->fetch_err = FIND_ERR_FAILURE; 3252193149Sdougb inc_stats(adb, dns_resstatscounter_gluefetchv4fail); 3253135446Strhodes } else { 3254135446Strhodes name->expire_v6 = ISC_MIN(name->expire_v6, now + 300); 3255135446Strhodes name->fetch6_err = FIND_ERR_FAILURE; 3256193149Sdougb inc_stats(adb, dns_resstatscounter_gluefetchv6fail); 3257135446Strhodes } 3258135446Strhodes goto out; 3259135446Strhodes } 3260135446Strhodes 3261135446Strhodes /* 3262135446Strhodes * We got something potentially useful. 3263135446Strhodes */ 3264135446Strhodes result = import_rdataset(name, &fetch->rdataset, now); 3265135446Strhodes 3266135446Strhodes check_result: 3267135446Strhodes if (result == ISC_R_SUCCESS) { 3268135446Strhodes ev_status = DNS_EVENT_ADBMOREADDRESSES; 3269135446Strhodes if (address_type == DNS_ADBFIND_INET) 3270135446Strhodes name->fetch_err = FIND_ERR_SUCCESS; 3271135446Strhodes else 3272135446Strhodes name->fetch6_err = FIND_ERR_SUCCESS; 3273135446Strhodes } 3274135446Strhodes 3275135446Strhodes out: 3276135446Strhodes free_adbfetch(adb, &fetch); 3277135446Strhodes isc_event_free(&ev); 3278135446Strhodes 3279135446Strhodes clean_finds_at_name(name, ev_status, address_type); 3280135446Strhodes 3281135446Strhodes UNLOCK(&adb->namelocks[bucket]); 3282135446Strhodes} 3283135446Strhodes 3284135446Strhodesstatic isc_result_t 3285135446Strhodesfetch_name(dns_adbname_t *adbname, 3286135446Strhodes isc_boolean_t start_at_zone, 3287135446Strhodes dns_rdatatype_t type) 3288135446Strhodes{ 3289135446Strhodes isc_result_t result; 3290135446Strhodes dns_adbfetch_t *fetch = NULL; 3291135446Strhodes dns_adb_t *adb; 3292135446Strhodes dns_fixedname_t fixed; 3293135446Strhodes dns_name_t *name; 3294135446Strhodes dns_rdataset_t rdataset; 3295135446Strhodes dns_rdataset_t *nameservers; 3296135446Strhodes unsigned int options; 3297135446Strhodes 3298135446Strhodes INSIST(DNS_ADBNAME_VALID(adbname)); 3299135446Strhodes adb = adbname->adb; 3300135446Strhodes INSIST(DNS_ADB_VALID(adb)); 3301135446Strhodes 3302135446Strhodes INSIST((type == dns_rdatatype_a && !NAME_FETCH_V4(adbname)) || 3303135446Strhodes (type == dns_rdatatype_aaaa && !NAME_FETCH_V6(adbname))); 3304135446Strhodes 3305135446Strhodes adbname->fetch_err = FIND_ERR_NOTFOUND; 3306135446Strhodes 3307135446Strhodes name = NULL; 3308135446Strhodes nameservers = NULL; 3309135446Strhodes dns_rdataset_init(&rdataset); 3310135446Strhodes 3311135446Strhodes options = DNS_FETCHOPT_NOVALIDATE; 3312135446Strhodes if (start_at_zone) { 3313135446Strhodes DP(ENTER_LEVEL, 3314135446Strhodes "fetch_name: starting at zone for name %p", 3315135446Strhodes adbname); 3316135446Strhodes dns_fixedname_init(&fixed); 3317135446Strhodes name = dns_fixedname_name(&fixed); 3318135446Strhodes result = dns_view_findzonecut2(adb->view, &adbname->name, name, 3319135446Strhodes 0, 0, ISC_TRUE, ISC_FALSE, 3320135446Strhodes &rdataset, NULL); 3321135446Strhodes if (result != ISC_R_SUCCESS && result != DNS_R_HINT) 3322135446Strhodes goto cleanup; 3323135446Strhodes nameservers = &rdataset; 3324135446Strhodes options |= DNS_FETCHOPT_UNSHARED; 3325135446Strhodes } 3326135446Strhodes 3327135446Strhodes fetch = new_adbfetch(adb); 3328135446Strhodes if (fetch == NULL) { 3329135446Strhodes result = ISC_R_NOMEMORY; 3330135446Strhodes goto cleanup; 3331135446Strhodes } 3332135446Strhodes 3333135446Strhodes result = dns_resolver_createfetch(adb->view->resolver, &adbname->name, 3334135446Strhodes type, name, nameservers, NULL, 3335135446Strhodes options, adb->task, fetch_callback, 3336135446Strhodes adbname, &fetch->rdataset, NULL, 3337135446Strhodes &fetch->fetch); 3338135446Strhodes if (result != ISC_R_SUCCESS) 3339135446Strhodes goto cleanup; 3340135446Strhodes 3341193149Sdougb if (type == dns_rdatatype_a) { 3342135446Strhodes adbname->fetch_a = fetch; 3343193149Sdougb inc_stats(adb, dns_resstatscounter_gluefetchv4); 3344193149Sdougb } else { 3345135446Strhodes adbname->fetch_aaaa = fetch; 3346193149Sdougb inc_stats(adb, dns_resstatscounter_gluefetchv6); 3347193149Sdougb } 3348135446Strhodes fetch = NULL; /* Keep us from cleaning this up below. */ 3349135446Strhodes 3350135446Strhodes cleanup: 3351135446Strhodes if (fetch != NULL) 3352135446Strhodes free_adbfetch(adb, &fetch); 3353135446Strhodes if (dns_rdataset_isassociated(&rdataset)) 3354135446Strhodes dns_rdataset_disassociate(&rdataset); 3355135446Strhodes 3356135446Strhodes return (result); 3357135446Strhodes} 3358135446Strhodes 3359135446Strhodes/* 3360135446Strhodes * XXXMLG Needs to take a find argument and an address info, no zone or adb, 3361135446Strhodes * since these can be extracted from the find itself. 3362135446Strhodes */ 3363135446Strhodesisc_result_t 3364170222Sdougbdns_adb_marklame(dns_adb_t *adb, dns_adbaddrinfo_t *addr, dns_name_t *qname, 3365170222Sdougb dns_rdatatype_t qtype, isc_stdtime_t expire_time) 3366135446Strhodes{ 3367170222Sdougb dns_adblameinfo_t *li; 3368135446Strhodes int bucket; 3369135446Strhodes isc_result_t result = ISC_R_SUCCESS; 3370135446Strhodes 3371135446Strhodes REQUIRE(DNS_ADB_VALID(adb)); 3372135446Strhodes REQUIRE(DNS_ADBADDRINFO_VALID(addr)); 3373170222Sdougb REQUIRE(qname != NULL); 3374135446Strhodes 3375135446Strhodes bucket = addr->entry->lock_bucket; 3376135446Strhodes LOCK(&adb->entrylocks[bucket]); 3377170222Sdougb li = ISC_LIST_HEAD(addr->entry->lameinfo); 3378170222Sdougb while (li != NULL && 3379170222Sdougb (li->qtype != qtype || !dns_name_equal(qname, &li->qname))) 3380170222Sdougb li = ISC_LIST_NEXT(li, plink); 3381170222Sdougb if (li != NULL) { 3382170222Sdougb if (expire_time > li->lame_timer) 3383170222Sdougb li->lame_timer = expire_time; 3384135446Strhodes goto unlock; 3385135446Strhodes } 3386170222Sdougb li = new_adblameinfo(adb, qname, qtype); 3387170222Sdougb if (li == NULL) { 3388135446Strhodes result = ISC_R_NOMEMORY; 3389135446Strhodes goto unlock; 3390135446Strhodes } 3391135446Strhodes 3392170222Sdougb li->lame_timer = expire_time; 3393135446Strhodes 3394170222Sdougb ISC_LIST_PREPEND(addr->entry->lameinfo, li, plink); 3395135446Strhodes unlock: 3396135446Strhodes UNLOCK(&adb->entrylocks[bucket]); 3397135446Strhodes 3398153816Sdougb return (result); 3399135446Strhodes} 3400135446Strhodes 3401135446Strhodesvoid 3402135446Strhodesdns_adb_adjustsrtt(dns_adb_t *adb, dns_adbaddrinfo_t *addr, 3403135446Strhodes unsigned int rtt, unsigned int factor) 3404135446Strhodes{ 3405135446Strhodes int bucket; 3406135446Strhodes unsigned int new_srtt; 3407135446Strhodes isc_stdtime_t now; 3408135446Strhodes 3409135446Strhodes REQUIRE(DNS_ADB_VALID(adb)); 3410135446Strhodes REQUIRE(DNS_ADBADDRINFO_VALID(addr)); 3411135446Strhodes REQUIRE(factor <= 10); 3412135446Strhodes 3413135446Strhodes bucket = addr->entry->lock_bucket; 3414135446Strhodes LOCK(&adb->entrylocks[bucket]); 3415135446Strhodes 3416135446Strhodes if (factor == DNS_ADB_RTTADJAGE) 3417135446Strhodes new_srtt = addr->entry->srtt * 98 / 100; 3418135446Strhodes else 3419135446Strhodes new_srtt = (addr->entry->srtt / 10 * factor) 3420135446Strhodes + (rtt / 10 * (10 - factor)); 3421135446Strhodes 3422135446Strhodes addr->entry->srtt = new_srtt; 3423135446Strhodes addr->srtt = new_srtt; 3424135446Strhodes 3425135446Strhodes isc_stdtime_get(&now); 3426135446Strhodes addr->entry->expires = now + ADB_ENTRY_WINDOW; 3427135446Strhodes 3428135446Strhodes UNLOCK(&adb->entrylocks[bucket]); 3429135446Strhodes} 3430135446Strhodes 3431135446Strhodesvoid 3432135446Strhodesdns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr, 3433135446Strhodes unsigned int bits, unsigned int mask) 3434135446Strhodes{ 3435135446Strhodes int bucket; 3436135446Strhodes 3437135446Strhodes REQUIRE(DNS_ADB_VALID(adb)); 3438135446Strhodes REQUIRE(DNS_ADBADDRINFO_VALID(addr)); 3439135446Strhodes 3440135446Strhodes bucket = addr->entry->lock_bucket; 3441135446Strhodes LOCK(&adb->entrylocks[bucket]); 3442135446Strhodes 3443135446Strhodes addr->entry->flags = (addr->entry->flags & ~mask) | (bits & mask); 3444135446Strhodes /* 3445135446Strhodes * Note that we do not update the other bits in addr->flags with 3446135446Strhodes * the most recent values from addr->entry->flags. 3447135446Strhodes */ 3448135446Strhodes addr->flags = (addr->flags & ~mask) | (bits & mask); 3449135446Strhodes 3450135446Strhodes UNLOCK(&adb->entrylocks[bucket]); 3451135446Strhodes} 3452135446Strhodes 3453135446Strhodesisc_result_t 3454135446Strhodesdns_adb_findaddrinfo(dns_adb_t *adb, isc_sockaddr_t *sa, 3455135446Strhodes dns_adbaddrinfo_t **addrp, isc_stdtime_t now) 3456135446Strhodes{ 3457135446Strhodes int bucket; 3458135446Strhodes dns_adbentry_t *entry; 3459135446Strhodes dns_adbaddrinfo_t *addr; 3460135446Strhodes isc_result_t result; 3461135446Strhodes in_port_t port; 3462135446Strhodes 3463135446Strhodes REQUIRE(DNS_ADB_VALID(adb)); 3464135446Strhodes REQUIRE(addrp != NULL && *addrp == NULL); 3465135446Strhodes 3466135446Strhodes UNUSED(now); 3467135446Strhodes 3468135446Strhodes result = ISC_R_SUCCESS; 3469135446Strhodes bucket = DNS_ADB_INVALIDBUCKET; 3470193149Sdougb entry = find_entry_and_lock(adb, sa, &bucket, now); 3471135446Strhodes if (adb->entry_sd[bucket]) { 3472135446Strhodes result = ISC_R_SHUTTINGDOWN; 3473135446Strhodes goto unlock; 3474135446Strhodes } 3475135446Strhodes if (entry == NULL) { 3476135446Strhodes /* 3477135446Strhodes * We don't know anything about this address. 3478135446Strhodes */ 3479135446Strhodes entry = new_adbentry(adb); 3480135446Strhodes if (entry == NULL) { 3481135446Strhodes result = ISC_R_NOMEMORY; 3482135446Strhodes goto unlock; 3483135446Strhodes } 3484135446Strhodes entry->sockaddr = *sa; 3485135446Strhodes link_entry(adb, bucket, entry); 3486135446Strhodes DP(ENTER_LEVEL, "findaddrinfo: new entry %p", entry); 3487135446Strhodes } else 3488135446Strhodes DP(ENTER_LEVEL, "findaddrinfo: found entry %p", entry); 3489135446Strhodes 3490135446Strhodes port = isc_sockaddr_getport(sa); 3491135446Strhodes addr = new_adbaddrinfo(adb, entry, port); 3492174187Sdougb if (addr == NULL) { 3493174187Sdougb result = ISC_R_NOMEMORY; 3494174187Sdougb } else { 3495135446Strhodes inc_entry_refcnt(adb, entry, ISC_FALSE); 3496135446Strhodes *addrp = addr; 3497135446Strhodes } 3498135446Strhodes 3499135446Strhodes unlock: 3500135446Strhodes UNLOCK(&adb->entrylocks[bucket]); 3501135446Strhodes 3502135446Strhodes return (result); 3503135446Strhodes} 3504135446Strhodes 3505135446Strhodesvoid 3506135446Strhodesdns_adb_freeaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **addrp) { 3507135446Strhodes dns_adbaddrinfo_t *addr; 3508135446Strhodes dns_adbentry_t *entry; 3509135446Strhodes int bucket; 3510135446Strhodes isc_stdtime_t now; 3511135446Strhodes isc_boolean_t want_check_exit = ISC_FALSE; 3512135446Strhodes 3513135446Strhodes REQUIRE(DNS_ADB_VALID(adb)); 3514135446Strhodes REQUIRE(addrp != NULL); 3515135446Strhodes addr = *addrp; 3516135446Strhodes REQUIRE(DNS_ADBADDRINFO_VALID(addr)); 3517135446Strhodes entry = addr->entry; 3518135446Strhodes REQUIRE(DNS_ADBENTRY_VALID(entry)); 3519135446Strhodes 3520135446Strhodes isc_stdtime_get(&now); 3521135446Strhodes 3522135446Strhodes *addrp = NULL; 3523135446Strhodes 3524135446Strhodes bucket = addr->entry->lock_bucket; 3525135446Strhodes LOCK(&adb->entrylocks[bucket]); 3526135446Strhodes 3527135446Strhodes entry->expires = now + ADB_ENTRY_WINDOW; 3528135446Strhodes 3529135446Strhodes want_check_exit = dec_entry_refcnt(adb, entry, ISC_FALSE); 3530135446Strhodes 3531135446Strhodes UNLOCK(&adb->entrylocks[bucket]); 3532135446Strhodes 3533135446Strhodes addr->entry = NULL; 3534135446Strhodes free_adbaddrinfo(adb, &addr); 3535135446Strhodes 3536135446Strhodes if (want_check_exit) { 3537135446Strhodes LOCK(&adb->lock); 3538135446Strhodes check_exit(adb); 3539135446Strhodes UNLOCK(&adb->lock); 3540135446Strhodes } 3541135446Strhodes} 3542135446Strhodes 3543135446Strhodesvoid 3544135446Strhodesdns_adb_flush(dns_adb_t *adb) { 3545135446Strhodes unsigned int i; 3546135446Strhodes 3547135446Strhodes INSIST(DNS_ADB_VALID(adb)); 3548135446Strhodes 3549135446Strhodes LOCK(&adb->lock); 3550135446Strhodes 3551143731Sdougb /* 3552143731Sdougb * Call our cleanup routines. 3553143731Sdougb */ 3554143731Sdougb for (i = 0; i < NBUCKETS; i++) 3555135446Strhodes RUNTIME_CHECK(cleanup_names(adb, i, INT_MAX) == ISC_FALSE); 3556143731Sdougb for (i = 0; i < NBUCKETS; i++) 3557135446Strhodes RUNTIME_CHECK(cleanup_entries(adb, i, INT_MAX) == ISC_FALSE); 3558135446Strhodes 3559135446Strhodes#ifdef DUMP_ADB_AFTER_CLEANING 3560143731Sdougb dump_adb(adb, stdout, ISC_TRUE, INT_MAX); 3561135446Strhodes#endif 3562135446Strhodes 3563135446Strhodes UNLOCK(&adb->lock); 3564135446Strhodes} 3565135446Strhodes 3566135446Strhodesvoid 3567135446Strhodesdns_adb_flushname(dns_adb_t *adb, dns_name_t *name) { 3568135446Strhodes dns_adbname_t *adbname; 3569135446Strhodes dns_adbname_t *nextname; 3570135446Strhodes int bucket; 3571135446Strhodes 3572135446Strhodes INSIST(DNS_ADB_VALID(adb)); 3573135446Strhodes 3574135446Strhodes LOCK(&adb->lock); 3575135446Strhodes bucket = dns_name_hash(name, ISC_FALSE) % NBUCKETS; 3576135446Strhodes LOCK(&adb->namelocks[bucket]); 3577135446Strhodes adbname = ISC_LIST_HEAD(adb->names[bucket]); 3578135446Strhodes while (adbname != NULL) { 3579135446Strhodes nextname = ISC_LIST_NEXT(adbname, plink); 3580135446Strhodes if (!NAME_DEAD(adbname) && 3581135446Strhodes dns_name_equal(name, &adbname->name)) { 3582135446Strhodes RUNTIME_CHECK(kill_name(&adbname, 3583135446Strhodes DNS_EVENT_ADBCANCELED) == 3584135446Strhodes ISC_FALSE); 3585135446Strhodes } 3586135446Strhodes adbname = nextname; 3587135446Strhodes } 3588135446Strhodes UNLOCK(&adb->namelocks[bucket]); 3589135446Strhodes UNLOCK(&adb->lock); 3590135446Strhodes} 3591135446Strhodes 3592135446Strhodesstatic void 3593135446Strhodeswater(void *arg, int mark) { 3594135446Strhodes dns_adb_t *adb = arg; 3595135446Strhodes isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER); 3596135446Strhodes 3597135446Strhodes REQUIRE(DNS_ADB_VALID(adb)); 3598135446Strhodes 3599135446Strhodes DP(ISC_LOG_DEBUG(1), 3600135446Strhodes "adb reached %s water mark", overmem ? "high" : "low"); 3601135446Strhodes 3602186462Sdougb /* 3603186462Sdougb * We can't use adb->lock as there is potential for water 3604186462Sdougb * to be called when adb->lock is held. 3605186462Sdougb */ 3606186462Sdougb LOCK(&adb->overmemlock); 3607186462Sdougb if (adb->overmem != overmem) { 3608186462Sdougb adb->overmem = overmem; 3609186462Sdougb isc_mem_waterack(adb->mctx, mark); 3610135446Strhodes } 3611186462Sdougb UNLOCK(&adb->overmemlock); 3612135446Strhodes} 3613135446Strhodes 3614135446Strhodesvoid 3615135446Strhodesdns_adb_setadbsize(dns_adb_t *adb, isc_uint32_t size) { 3616135446Strhodes isc_uint32_t hiwater; 3617135446Strhodes isc_uint32_t lowater; 3618135446Strhodes 3619135446Strhodes INSIST(DNS_ADB_VALID(adb)); 3620135446Strhodes 3621135446Strhodes if (size != 0 && size < DNS_ADB_MINADBSIZE) 3622135446Strhodes size = DNS_ADB_MINADBSIZE; 3623135446Strhodes 3624135446Strhodes hiwater = size - (size >> 3); /* Approximately 7/8ths. */ 3625135446Strhodes lowater = size - (size >> 2); /* Approximately 3/4ths. */ 3626135446Strhodes 3627135446Strhodes if (size == 0 || hiwater == 0 || lowater == 0) 3628135446Strhodes isc_mem_setwater(adb->mctx, water, adb, 0, 0); 3629135446Strhodes else 3630135446Strhodes isc_mem_setwater(adb->mctx, water, adb, hiwater, lowater); 3631135446Strhodes} 3632