1135446Strhodes/*
2262706Serwin * Copyright (C) 2004-2014  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
18254897Serwin/* $Id: adb.c,v 1.264 2011/12/05 17:10:51 each 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 * For type 3 negative cache entries, we will remember that the address is
70135446Strhodes * broken for this long.  XXXMLG This is also used for actual addresses, too.
71135446Strhodes * The intent is to keep us from constantly asking about A/AAAA records
72135446Strhodes * if the zone has extremely low TTLs.
73135446Strhodes */
74193149Sdougb#define ADB_CACHE_MINIMUM       10      /*%< seconds */
75193149Sdougb#define ADB_CACHE_MAXIMUM       86400   /*%< seconds (86400 = 24 hours) */
76193149Sdougb#define ADB_ENTRY_WINDOW        1800    /*%< seconds */
77135446Strhodes
78170222Sdougb/*%
79193149Sdougb * The period in seconds after which an ADB name entry is regarded as stale
80193149Sdougb * and forced to be cleaned up.
81193149Sdougb * TODO: This should probably be configurable at run-time.
82135446Strhodes */
83193149Sdougb#ifndef ADB_STALE_MARGIN
84193149Sdougb#define ADB_STALE_MARGIN        1800
85193149Sdougb#endif
86135446Strhodes
87193149Sdougb#define FREE_ITEMS              64      /*%< free count for memory pools */
88193149Sdougb#define FILL_COUNT              16      /*%< fill count for memory pools */
89135446Strhodes
90193149Sdougb#define DNS_ADB_INVALIDBUCKET (-1)      /*%< invalid bucket address */
91135446Strhodes
92254402Serwin#define DNS_ADB_MINADBSIZE      (1024U*1024U)     /*%< 1 Megabyte */
93135446Strhodes
94135446Strhodestypedef ISC_LIST(dns_adbname_t) dns_adbnamelist_t;
95135446Strhodestypedef struct dns_adbnamehook dns_adbnamehook_t;
96135446Strhodestypedef ISC_LIST(dns_adbnamehook_t) dns_adbnamehooklist_t;
97170222Sdougbtypedef struct dns_adblameinfo dns_adblameinfo_t;
98135446Strhodestypedef ISC_LIST(dns_adbentry_t) dns_adbentrylist_t;
99135446Strhodestypedef struct dns_adbfetch dns_adbfetch_t;
100135446Strhodestypedef struct dns_adbfetch6 dns_adbfetch6_t;
101135446Strhodes
102170222Sdougb/*% dns adb structure */
103135446Strhodesstruct dns_adb {
104193149Sdougb	unsigned int                    magic;
105135446Strhodes
106193149Sdougb	isc_mutex_t                     lock;
107193149Sdougb	isc_mutex_t                     reflock; /*%< Covers irefcnt, erefcnt */
108186462Sdougb	isc_mutex_t                     overmemlock; /*%< Covers overmem */
109193149Sdougb	isc_mem_t                      *mctx;
110193149Sdougb	dns_view_t                     *view;
111135446Strhodes
112193149Sdougb	isc_taskmgr_t                  *taskmgr;
113193149Sdougb	isc_task_t                     *task;
114245163Serwin	isc_task_t                     *excl;
115135446Strhodes
116193149Sdougb	isc_interval_t                  tick_interval;
117193149Sdougb	int                             next_cleanbucket;
118135446Strhodes
119193149Sdougb	unsigned int                    irefcnt;
120193149Sdougb	unsigned int                    erefcnt;
121135446Strhodes
122193149Sdougb	isc_mutex_t                     mplock;
123193149Sdougb	isc_mempool_t                  *nmp;    /*%< dns_adbname_t */
124193149Sdougb	isc_mempool_t                  *nhmp;   /*%< dns_adbnamehook_t */
125193149Sdougb	isc_mempool_t                  *limp;   /*%< dns_adblameinfo_t */
126193149Sdougb	isc_mempool_t                  *emp;    /*%< dns_adbentry_t */
127193149Sdougb	isc_mempool_t                  *ahmp;   /*%< dns_adbfind_t */
128193149Sdougb	isc_mempool_t                  *aimp;   /*%< dns_adbaddrinfo_t */
129193149Sdougb	isc_mempool_t                  *afmp;   /*%< dns_adbfetch_t */
130193149Sdougb
131170222Sdougb	/*!
132135446Strhodes	 * Bucketized locks and lists for names.
133135446Strhodes	 *
134135446Strhodes	 * XXXRTH  Have a per-bucket structure that contains all of these?
135135446Strhodes	 */
136224092Sdougb	unsigned int			nnames;
137224092Sdougb	isc_mutex_t                     namescntlock;
138224092Sdougb	unsigned int			namescnt;
139224092Sdougb	dns_adbnamelist_t               *names;
140224092Sdougb	dns_adbnamelist_t               *deadnames;
141224092Sdougb	isc_mutex_t                     *namelocks;
142224092Sdougb	isc_boolean_t                   *name_sd;
143224092Sdougb	unsigned int                    *name_refcnt;
144135446Strhodes
145170222Sdougb	/*!
146224092Sdougb	 * Bucketized locks and lists for entries.
147135446Strhodes	 *
148135446Strhodes	 * XXXRTH  Have a per-bucket structure that contains all of these?
149135446Strhodes	 */
150224092Sdougb	unsigned int			nentries;
151224092Sdougb	isc_mutex_t                     entriescntlock;
152224092Sdougb	unsigned int			entriescnt;
153224092Sdougb	dns_adbentrylist_t              *entries;
154224092Sdougb	dns_adbentrylist_t              *deadentries;
155224092Sdougb	isc_mutex_t                     *entrylocks;
156224092Sdougb	isc_boolean_t                   *entry_sd; /*%< shutting down */
157224092Sdougb	unsigned int                    *entry_refcnt;
158135446Strhodes
159193149Sdougb	isc_event_t                     cevent;
160193149Sdougb	isc_boolean_t                   cevent_sent;
161193149Sdougb	isc_boolean_t                   shutting_down;
162193149Sdougb	isc_eventlist_t                 whenshutdown;
163224092Sdougb	isc_event_t			growentries;
164224092Sdougb	isc_boolean_t			growentries_sent;
165224092Sdougb	isc_event_t			grownames;
166224092Sdougb	isc_boolean_t			grownames_sent;
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;
204275672Sdelphij	unsigned int			depth;
205135446Strhodes};
206135446Strhodes
207170222Sdougb/*%
208135446Strhodes * This is a small widget that dangles off a dns_adbname_t.  It contains a
209135446Strhodes * pointer to the address information about this host, and a link to the next
210135446Strhodes * namehook that will contain the next address this host has.
211135446Strhodes */
212135446Strhodesstruct dns_adbnamehook {
213193149Sdougb	unsigned int                    magic;
214193149Sdougb	dns_adbentry_t                 *entry;
215193149Sdougb	ISC_LINK(dns_adbnamehook_t)     plink;
216135446Strhodes};
217135446Strhodes
218170222Sdougb/*%
219170222Sdougb * This is a small widget that holds qname-specific information about an
220135446Strhodes * address.  Currently limited to lameness, but could just as easily be
221135446Strhodes * extended to other types of information about zones.
222135446Strhodes */
223170222Sdougbstruct dns_adblameinfo {
224193149Sdougb	unsigned int                    magic;
225135446Strhodes
226193149Sdougb	dns_name_t                      qname;
227193149Sdougb	dns_rdatatype_t                 qtype;
228193149Sdougb	isc_stdtime_t                   lame_timer;
229135446Strhodes
230193149Sdougb	ISC_LINK(dns_adblameinfo_t)     plink;
231135446Strhodes};
232135446Strhodes
233170222Sdougb/*%
234135446Strhodes * An address entry.  It holds quite a bit of information about addresses,
235135446Strhodes * including edns state (in "flags"), rtt, and of course the address of
236135446Strhodes * the host.
237135446Strhodes */
238135446Strhodesstruct dns_adbentry {
239193149Sdougb	unsigned int                    magic;
240135446Strhodes
241193149Sdougb	int                             lock_bucket;
242193149Sdougb	unsigned int                    refcnt;
243135446Strhodes
244193149Sdougb	unsigned int                    flags;
245193149Sdougb	unsigned int                    srtt;
246193149Sdougb	isc_sockaddr_t                  sockaddr;
247135446Strhodes
248193149Sdougb	isc_stdtime_t                   expires;
249170222Sdougb	/*%<
250135446Strhodes	 * A nonzero 'expires' field indicates that the entry should
251135446Strhodes	 * persist until that time.  This allows entries found
252135446Strhodes	 * using dns_adb_findaddrinfo() to persist for a limited time
253135446Strhodes	 * even though they are not necessarily associated with a
254135446Strhodes	 * name.
255135446Strhodes	 */
256135446Strhodes
257193149Sdougb	ISC_LIST(dns_adblameinfo_t)     lameinfo;
258193149Sdougb	ISC_LINK(dns_adbentry_t)        plink;
259234010Sdougb
260135446Strhodes};
261135446Strhodes
262135446Strhodes/*
263135446Strhodes * Internal functions (and prototypes).
264135446Strhodes */
265135446Strhodesstatic inline dns_adbname_t *new_adbname(dns_adb_t *, dns_name_t *);
266135446Strhodesstatic inline void free_adbname(dns_adb_t *, dns_adbname_t **);
267135446Strhodesstatic inline dns_adbnamehook_t *new_adbnamehook(dns_adb_t *,
268135446Strhodes						 dns_adbentry_t *);
269135446Strhodesstatic inline void free_adbnamehook(dns_adb_t *, dns_adbnamehook_t **);
270170222Sdougbstatic inline dns_adblameinfo_t *new_adblameinfo(dns_adb_t *, dns_name_t *,
271170222Sdougb						 dns_rdatatype_t);
272170222Sdougbstatic inline void free_adblameinfo(dns_adb_t *, dns_adblameinfo_t **);
273135446Strhodesstatic inline dns_adbentry_t *new_adbentry(dns_adb_t *);
274135446Strhodesstatic inline void free_adbentry(dns_adb_t *, dns_adbentry_t **);
275135446Strhodesstatic inline dns_adbfind_t *new_adbfind(dns_adb_t *);
276135446Strhodesstatic inline isc_boolean_t free_adbfind(dns_adb_t *, dns_adbfind_t **);
277135446Strhodesstatic inline dns_adbaddrinfo_t *new_adbaddrinfo(dns_adb_t *, dns_adbentry_t *,
278135446Strhodes						 in_port_t);
279135446Strhodesstatic inline dns_adbfetch_t *new_adbfetch(dns_adb_t *);
280135446Strhodesstatic inline void free_adbfetch(dns_adb_t *, dns_adbfetch_t **);
281135446Strhodesstatic inline dns_adbname_t *find_name_and_lock(dns_adb_t *, dns_name_t *,
282135446Strhodes						unsigned int, int *);
283135446Strhodesstatic inline dns_adbentry_t *find_entry_and_lock(dns_adb_t *,
284193149Sdougb						  isc_sockaddr_t *, int *,
285193149Sdougb						  isc_stdtime_t);
286143731Sdougbstatic void dump_adb(dns_adb_t *, FILE *, isc_boolean_t debug, isc_stdtime_t);
287135446Strhodesstatic void print_dns_name(FILE *, dns_name_t *);
288135446Strhodesstatic void print_namehook_list(FILE *, const char *legend,
289135446Strhodes				dns_adbnamehooklist_t *list,
290135446Strhodes				isc_boolean_t debug,
291135446Strhodes				isc_stdtime_t now);
292135446Strhodesstatic void print_find_list(FILE *, dns_adbname_t *);
293135446Strhodesstatic void print_fetch_list(FILE *, dns_adbname_t *);
294135446Strhodesstatic inline isc_boolean_t dec_adb_irefcnt(dns_adb_t *);
295135446Strhodesstatic inline void inc_adb_irefcnt(dns_adb_t *);
296135446Strhodesstatic inline void inc_adb_erefcnt(dns_adb_t *);
297135446Strhodesstatic inline void inc_entry_refcnt(dns_adb_t *, dns_adbentry_t *,
298135446Strhodes				    isc_boolean_t);
299214586Sdougbstatic inline isc_boolean_t dec_entry_refcnt(dns_adb_t *, isc_boolean_t,
300214586Sdougb					     dns_adbentry_t *, isc_boolean_t);
301135446Strhodesstatic inline void violate_locking_hierarchy(isc_mutex_t *, isc_mutex_t *);
302135446Strhodesstatic isc_boolean_t clean_namehooks(dns_adb_t *, dns_adbnamehooklist_t *);
303135446Strhodesstatic void clean_target(dns_adb_t *, dns_name_t *);
304135446Strhodesstatic void clean_finds_at_name(dns_adbname_t *, isc_eventtype_t,
305275672Sdelphij				isc_uint32_t, unsigned int);
306193149Sdougbstatic isc_boolean_t check_expire_namehooks(dns_adbname_t *, isc_stdtime_t);
307193149Sdougbstatic isc_boolean_t check_expire_entry(dns_adb_t *, dns_adbentry_t **,
308193149Sdougb					isc_stdtime_t);
309135446Strhodesstatic void cancel_fetches_at_name(dns_adbname_t *);
310135446Strhodesstatic isc_result_t dbfind_name(dns_adbname_t *, isc_stdtime_t,
311135446Strhodes				dns_rdatatype_t);
312135446Strhodesstatic isc_result_t fetch_name(dns_adbname_t *, isc_boolean_t,
313275672Sdelphij			       unsigned int, dns_rdatatype_t);
314135446Strhodesstatic inline void check_exit(dns_adb_t *);
315135446Strhodesstatic void destroy(dns_adb_t *);
316135446Strhodesstatic isc_boolean_t shutdown_names(dns_adb_t *);
317135446Strhodesstatic isc_boolean_t shutdown_entries(dns_adb_t *);
318135446Strhodesstatic inline void link_name(dns_adb_t *, int, dns_adbname_t *);
319135446Strhodesstatic inline isc_boolean_t unlink_name(dns_adb_t *, dns_adbname_t *);
320135446Strhodesstatic inline void link_entry(dns_adb_t *, int, dns_adbentry_t *);
321135446Strhodesstatic inline isc_boolean_t unlink_entry(dns_adb_t *, dns_adbentry_t *);
322135446Strhodesstatic isc_boolean_t kill_name(dns_adbname_t **, isc_eventtype_t);
323143731Sdougbstatic void water(void *, int);
324143731Sdougbstatic void dump_entry(FILE *, dns_adbentry_t *, isc_boolean_t, isc_stdtime_t);
325135446Strhodes
326135446Strhodes/*
327135446Strhodes * MUST NOT overlap DNS_ADBFIND_* flags!
328135446Strhodes */
329193149Sdougb#define FIND_EVENT_SENT         0x40000000
330193149Sdougb#define FIND_EVENT_FREED        0x80000000
331193149Sdougb#define FIND_EVENTSENT(h)       (((h)->flags & FIND_EVENT_SENT) != 0)
332193149Sdougb#define FIND_EVENTFREED(h)      (((h)->flags & FIND_EVENT_FREED) != 0)
333135446Strhodes
334193149Sdougb#define NAME_NEEDS_POKE         0x80000000
335193149Sdougb#define NAME_IS_DEAD            0x40000000
336193149Sdougb#define NAME_HINT_OK            DNS_ADBFIND_HINTOK
337193149Sdougb#define NAME_GLUE_OK            DNS_ADBFIND_GLUEOK
338193149Sdougb#define NAME_STARTATZONE        DNS_ADBFIND_STARTATZONE
339193149Sdougb#define NAME_DEAD(n)            (((n)->flags & NAME_IS_DEAD) != 0)
340193149Sdougb#define NAME_NEEDSPOKE(n)       (((n)->flags & NAME_NEEDS_POKE) != 0)
341193149Sdougb#define NAME_GLUEOK(n)          (((n)->flags & NAME_GLUE_OK) != 0)
342193149Sdougb#define NAME_HINTOK(n)          (((n)->flags & NAME_HINT_OK) != 0)
343135446Strhodes
344135446Strhodes/*
345193149Sdougb * Private flag(s) for entries.
346193149Sdougb * MUST NOT overlap FCTX_ADDRINFO_xxx and DNS_FETCHOPT_NOEDNS0.
347193149Sdougb */
348193149Sdougb#define ENTRY_IS_DEAD		0x80000000
349193149Sdougb
350193149Sdougb/*
351135446Strhodes * To the name, address classes are all that really exist.  If it has a
352135446Strhodes * V6 address it doesn't care if it came from a AAAA query.
353135446Strhodes */
354193149Sdougb#define NAME_HAS_V4(n)          (!ISC_LIST_EMPTY((n)->v4))
355193149Sdougb#define NAME_HAS_V6(n)          (!ISC_LIST_EMPTY((n)->v6))
356193149Sdougb#define NAME_HAS_ADDRS(n)       (NAME_HAS_V4(n) || NAME_HAS_V6(n))
357135446Strhodes
358135446Strhodes/*
359135446Strhodes * Fetches are broken out into A and AAAA types.  In some cases,
360135446Strhodes * however, it makes more sense to test for a particular class of fetches,
361135446Strhodes * like V4 or V6 above.
362135446Strhodes * Note: since we have removed the support of A6 in adb, FETCH_A and FETCH_AAAA
363135446Strhodes * are now equal to FETCH_V4 and FETCH_V6, respectively.
364135446Strhodes */
365193149Sdougb#define NAME_FETCH_A(n)         ((n)->fetch_a != NULL)
366193149Sdougb#define NAME_FETCH_AAAA(n)      ((n)->fetch_aaaa != NULL)
367193149Sdougb#define NAME_FETCH_V4(n)        (NAME_FETCH_A(n))
368193149Sdougb#define NAME_FETCH_V6(n)        (NAME_FETCH_AAAA(n))
369193149Sdougb#define NAME_FETCH(n)           (NAME_FETCH_V4(n) || NAME_FETCH_V6(n))
370135446Strhodes
371135446Strhodes/*
372135446Strhodes * Find options and tests to see if there are addresses on the list.
373135446Strhodes */
374193149Sdougb#define FIND_WANTEVENT(fn)      (((fn)->options & DNS_ADBFIND_WANTEVENT) != 0)
375193149Sdougb#define FIND_WANTEMPTYEVENT(fn) (((fn)->options & DNS_ADBFIND_EMPTYEVENT) != 0)
376193149Sdougb#define FIND_AVOIDFETCHES(fn)   (((fn)->options & DNS_ADBFIND_AVOIDFETCHES) \
377135446Strhodes				 != 0)
378193149Sdougb#define FIND_STARTATZONE(fn)    (((fn)->options & DNS_ADBFIND_STARTATZONE) \
379135446Strhodes				 != 0)
380193149Sdougb#define FIND_HINTOK(fn)         (((fn)->options & DNS_ADBFIND_HINTOK) != 0)
381193149Sdougb#define FIND_GLUEOK(fn)         (((fn)->options & DNS_ADBFIND_GLUEOK) != 0)
382193149Sdougb#define FIND_HAS_ADDRS(fn)      (!ISC_LIST_EMPTY((fn)->list))
383193149Sdougb#define FIND_RETURNLAME(fn)     (((fn)->options & DNS_ADBFIND_RETURNLAME) != 0)
384135446Strhodes
385135446Strhodes/*
386135446Strhodes * These are currently used on simple unsigned ints, so they are
387135446Strhodes * not really associated with any particular type.
388135446Strhodes */
389193149Sdougb#define WANT_INET(x)            (((x) & DNS_ADBFIND_INET) != 0)
390193149Sdougb#define WANT_INET6(x)           (((x) & DNS_ADBFIND_INET6) != 0)
391135446Strhodes
392193149Sdougb#define EXPIRE_OK(exp, now)     ((exp == INT_MAX) || (exp < now))
393135446Strhodes
394135446Strhodes/*
395135446Strhodes * Find out if the flags on a name (nf) indicate if it is a hint or
396135446Strhodes * glue, and compare this to the appropriate bits set in o, to see if
397135446Strhodes * this is ok.
398135446Strhodes */
399135446Strhodes#define GLUE_OK(nf, o) (!NAME_GLUEOK(nf) || (((o) & DNS_ADBFIND_GLUEOK) != 0))
400135446Strhodes#define HINT_OK(nf, o) (!NAME_HINTOK(nf) || (((o) & DNS_ADBFIND_HINTOK) != 0))
401135446Strhodes#define GLUEHINT_OK(nf, o) (GLUE_OK(nf, o) || HINT_OK(nf, o))
402135446Strhodes#define STARTATZONE_MATCHES(nf, o) (((nf)->flags & NAME_STARTATZONE) == \
403135446Strhodes				    ((o) & DNS_ADBFIND_STARTATZONE))
404135446Strhodes
405193149Sdougb#define ENTER_LEVEL             ISC_LOG_DEBUG(50)
406193149Sdougb#define EXIT_LEVEL              ENTER_LEVEL
407193149Sdougb#define CLEAN_LEVEL             ISC_LOG_DEBUG(100)
408193149Sdougb#define DEF_LEVEL               ISC_LOG_DEBUG(5)
409193149Sdougb#define NCACHE_LEVEL            ISC_LOG_DEBUG(20)
410135446Strhodes
411193149Sdougb#define NCACHE_RESULT(r)        ((r) == DNS_R_NCACHENXDOMAIN || \
412135446Strhodes				 (r) == DNS_R_NCACHENXRRSET)
413193149Sdougb#define AUTH_NX(r)              ((r) == DNS_R_NXDOMAIN || \
414135446Strhodes				 (r) == DNS_R_NXRRSET)
415193149Sdougb#define NXDOMAIN_RESULT(r)      ((r) == DNS_R_NXDOMAIN || \
416135446Strhodes				 (r) == DNS_R_NCACHENXDOMAIN)
417193149Sdougb#define NXRRSET_RESULT(r)       ((r) == DNS_R_NCACHENXRRSET || \
418135446Strhodes				 (r) == DNS_R_NXRRSET || \
419135446Strhodes				 (r) == DNS_R_HINTNXRRSET)
420135446Strhodes
421135446Strhodes/*
422135446Strhodes * Error state rankings.
423135446Strhodes */
424135446Strhodes
425193149Sdougb#define FIND_ERR_SUCCESS                0  /* highest rank */
426193149Sdougb#define FIND_ERR_CANCELED               1
427193149Sdougb#define FIND_ERR_FAILURE                2
428193149Sdougb#define FIND_ERR_NXDOMAIN               3
429193149Sdougb#define FIND_ERR_NXRRSET                4
430193149Sdougb#define FIND_ERR_UNEXPECTED             5
431193149Sdougb#define FIND_ERR_NOTFOUND               6
432193149Sdougb#define FIND_ERR_MAX                    7
433135446Strhodes
434135446Strhodesstatic const char *errnames[] = {
435135446Strhodes	"success",
436135446Strhodes	"canceled",
437135446Strhodes	"failure",
438135446Strhodes	"nxdomain",
439135446Strhodes	"nxrrset",
440135446Strhodes	"unexpected",
441135446Strhodes	"not_found"
442135446Strhodes};
443135446Strhodes
444193149Sdougb#define NEWERR(old, new)        (ISC_MIN((old), (new)))
445135446Strhodes
446135446Strhodesstatic isc_result_t find_err_map[FIND_ERR_MAX] = {
447135446Strhodes	ISC_R_SUCCESS,
448135446Strhodes	ISC_R_CANCELED,
449135446Strhodes	ISC_R_FAILURE,
450135446Strhodes	DNS_R_NXDOMAIN,
451135446Strhodes	DNS_R_NXRRSET,
452135446Strhodes	ISC_R_UNEXPECTED,
453193149Sdougb	ISC_R_NOTFOUND          /* not YET found */
454135446Strhodes};
455135446Strhodes
456135446Strhodesstatic void
457135446StrhodesDP(int level, const char *format, ...) ISC_FORMAT_PRINTF(2, 3);
458135446Strhodes
459135446Strhodesstatic void
460135446StrhodesDP(int level, const char *format, ...) {
461135446Strhodes	va_list args;
462135446Strhodes
463135446Strhodes	va_start(args, format);
464135446Strhodes	isc_log_vwrite(dns_lctx,
465135446Strhodes		       DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ADB,
466135446Strhodes		       level, format, args);
467135446Strhodes	va_end(args);
468135446Strhodes}
469135446Strhodes
470193149Sdougb/*%
471193149Sdougb * Increment resolver-related statistics counters.
472193149Sdougb */
473193149Sdougbstatic inline void
474193149Sdougbinc_stats(dns_adb_t *adb, isc_statscounter_t counter) {
475193149Sdougb	if (adb->view->resstats != NULL)
476193149Sdougb		isc_stats_increment(adb->view->resstats, counter);
477193149Sdougb}
478193149Sdougb
479135446Strhodesstatic inline dns_ttl_t
480135446Strhodesttlclamp(dns_ttl_t ttl) {
481135446Strhodes	if (ttl < ADB_CACHE_MINIMUM)
482135446Strhodes		ttl = ADB_CACHE_MINIMUM;
483135446Strhodes	if (ttl > ADB_CACHE_MAXIMUM)
484135446Strhodes		ttl = ADB_CACHE_MAXIMUM;
485135446Strhodes
486135446Strhodes	return (ttl);
487135446Strhodes}
488135446Strhodes
489135446Strhodes/*
490224092Sdougb * Hashing is most efficient if the number of buckets is prime.
491224092Sdougb * The sequence below is the closest previous primes to 2^n and
492224092Sdougb * 1.5 * 2^n, for values of n from 10 to 28.  (The tables will
493224092Sdougb * no longer grow beyond 2^28 entries.)
494224092Sdougb */
495224092Sdougbstatic const unsigned nbuckets[] = { 1021, 1531, 2039, 3067, 4093, 6143,
496224092Sdougb				     8191, 12281, 16381, 24571, 32749,
497224092Sdougb				     49193, 65521, 98299, 131071, 199603,
498224092Sdougb				     262139, 393209, 524287, 768431, 1048573,
499224092Sdougb				     1572853, 2097143, 3145721, 4194301,
500224092Sdougb				     6291449, 8388593, 12582893, 16777213,
501224092Sdougb				     25165813, 33554393, 50331599, 67108859,
502224092Sdougb				     100663291, 134217689, 201326557,
503224092Sdougb				     268535431, 0 };
504224092Sdougb
505224092Sdougbstatic void
506224092Sdougbgrow_entries(isc_task_t *task, isc_event_t *ev) {
507224092Sdougb	dns_adb_t *adb;
508224092Sdougb	dns_adbentry_t *e;
509224092Sdougb	dns_adbentrylist_t *newdeadentries = NULL;
510224092Sdougb	dns_adbentrylist_t *newentries = NULL;
511224092Sdougb	isc_boolean_t *newentry_sd = NULL;
512224092Sdougb	isc_mutex_t *newentrylocks = NULL;
513224092Sdougb	isc_result_t result;
514224092Sdougb	unsigned int *newentry_refcnt = NULL;
515224092Sdougb	unsigned int i, n, bucket;
516224092Sdougb
517224092Sdougb	adb = ev->ev_arg;
518224092Sdougb	INSIST(DNS_ADB_VALID(adb));
519224092Sdougb
520224092Sdougb	isc_event_free(&ev);
521224092Sdougb
522254402Serwin	result = isc_task_beginexclusive(task);
523254402Serwin	if (result != ISC_R_SUCCESS)
524254402Serwin		goto check_exit;
525224092Sdougb
526224092Sdougb	i = 0;
527224092Sdougb	while (nbuckets[i] != 0 && adb->nentries >= nbuckets[i])
528224092Sdougb		i++;
529224092Sdougb	if (nbuckets[i] != 0)
530224092Sdougb		n = nbuckets[i];
531224092Sdougb	else
532224092Sdougb		goto done;
533224092Sdougb
534224092Sdougb	DP(ISC_LOG_INFO, "adb: grow_entries to %u starting", n);
535224092Sdougb
536224092Sdougb	/*
537224092Sdougb	 * Are we shutting down?
538224092Sdougb	 */
539224092Sdougb	for (i = 0; i < adb->nentries; i++)
540224092Sdougb		if (adb->entry_sd[i])
541224092Sdougb			goto cleanup;
542224092Sdougb
543224092Sdougb	/*
544224092Sdougb	 * Grab all the resources we need.
545224092Sdougb	 */
546224092Sdougb	newentries = isc_mem_get(adb->mctx, sizeof(*newentries) * n);
547224092Sdougb	newdeadentries = isc_mem_get(adb->mctx, sizeof(*newdeadentries) * n);
548224092Sdougb	newentrylocks = isc_mem_get(adb->mctx, sizeof(*newentrylocks) * n);
549224092Sdougb	newentry_sd = isc_mem_get(adb->mctx, sizeof(*newentry_sd) * n);
550224092Sdougb	newentry_refcnt = isc_mem_get(adb->mctx, sizeof(*newentry_refcnt) * n);
551224092Sdougb	if (newentries == NULL || newdeadentries == NULL ||
552224092Sdougb	    newentrylocks == NULL || newentry_sd == NULL ||
553224092Sdougb	    newentry_refcnt == NULL)
554224092Sdougb		goto cleanup;
555224092Sdougb
556224092Sdougb	/*
557224092Sdougb	 * Initialise the new resources.
558224092Sdougb	 */
559224092Sdougb	result = isc_mutexblock_init(newentrylocks, n);
560224092Sdougb	if (result != ISC_R_SUCCESS)
561224092Sdougb		goto cleanup;
562224092Sdougb
563224092Sdougb	for (i = 0; i < n; i++) {
564224092Sdougb		ISC_LIST_INIT(newentries[i]);
565224092Sdougb		ISC_LIST_INIT(newdeadentries[i]);
566224092Sdougb		newentry_sd[i] = ISC_FALSE;
567224092Sdougb		newentry_refcnt[i] = 0;
568224092Sdougb		adb->irefcnt++;
569224092Sdougb	}
570224092Sdougb
571224092Sdougb	/*
572224092Sdougb	 * Move entries to new arrays.
573224092Sdougb	 */
574224092Sdougb	for (i = 0; i < adb->nentries; i++) {
575224092Sdougb		e = ISC_LIST_HEAD(adb->entries[i]);
576224092Sdougb		while (e != NULL) {
577224092Sdougb			ISC_LIST_UNLINK(adb->entries[i], e, plink);
578224092Sdougb			bucket = isc_sockaddr_hash(&e->sockaddr, ISC_TRUE) % n;
579224092Sdougb			e->lock_bucket = bucket;
580224092Sdougb			ISC_LIST_APPEND(newentries[bucket], e, plink);
581224092Sdougb			INSIST(adb->entry_refcnt[i] > 0);
582224092Sdougb			adb->entry_refcnt[i]--;
583224092Sdougb			newentry_refcnt[bucket]++;
584224092Sdougb			e = ISC_LIST_HEAD(adb->entries[i]);
585224092Sdougb		}
586224092Sdougb		e = ISC_LIST_HEAD(adb->deadentries[i]);
587224092Sdougb		while (e != NULL) {
588224092Sdougb			ISC_LIST_UNLINK(adb->deadentries[i], e, plink);
589224092Sdougb			bucket = isc_sockaddr_hash(&e->sockaddr, ISC_TRUE) % n;
590224092Sdougb			e->lock_bucket = bucket;
591224092Sdougb			ISC_LIST_APPEND(newdeadentries[bucket], e, plink);
592224092Sdougb			INSIST(adb->entry_refcnt[i] > 0);
593224092Sdougb			adb->entry_refcnt[i]--;
594224092Sdougb			newentry_refcnt[bucket]++;
595224092Sdougb			e = ISC_LIST_HEAD(adb->deadentries[i]);
596224092Sdougb		}
597224092Sdougb		INSIST(adb->entry_refcnt[i] == 0);
598224092Sdougb		adb->irefcnt--;
599224092Sdougb	}
600224092Sdougb
601224092Sdougb	/*
602224092Sdougb	 * Cleanup old resources.
603224092Sdougb	 */
604224092Sdougb	DESTROYMUTEXBLOCK(adb->entrylocks, adb->nentries);
605224092Sdougb	isc_mem_put(adb->mctx, adb->entries,
606224092Sdougb		    sizeof(*adb->entries) * adb->nentries);
607224092Sdougb	isc_mem_put(adb->mctx, adb->deadentries,
608224092Sdougb		    sizeof(*adb->deadentries) * adb->nentries);
609224092Sdougb	isc_mem_put(adb->mctx, adb->entrylocks,
610224092Sdougb		    sizeof(*adb->entrylocks) * adb->nentries);
611224092Sdougb	isc_mem_put(adb->mctx, adb->entry_sd,
612224092Sdougb		    sizeof(*adb->entry_sd) * adb->nentries);
613224092Sdougb	isc_mem_put(adb->mctx, adb->entry_refcnt,
614224092Sdougb		    sizeof(*adb->entry_refcnt) * adb->nentries);
615224092Sdougb
616224092Sdougb	/*
617224092Sdougb	 * Install new resources.
618224092Sdougb	 */
619224092Sdougb	adb->entries = newentries;
620224092Sdougb	adb->deadentries = newdeadentries;
621224092Sdougb	adb->entrylocks = newentrylocks;
622224092Sdougb	adb->entry_sd = newentry_sd;
623224092Sdougb	adb->entry_refcnt = newentry_refcnt;
624224092Sdougb	adb->nentries = n;
625224092Sdougb
626224092Sdougb	/*
627224092Sdougb	 * Only on success do we set adb->growentries_sent to ISC_FALSE.
628224092Sdougb	 * This will prevent us being continuously being called on error.
629224092Sdougb	 */
630224092Sdougb	adb->growentries_sent = ISC_FALSE;
631224092Sdougb	goto done;
632224092Sdougb
633224092Sdougb cleanup:
634224092Sdougb	if (newentries != NULL)
635224092Sdougb		isc_mem_put(adb->mctx, newentries,
636224092Sdougb			    sizeof(*newentries) * n);
637224092Sdougb	if (newdeadentries != NULL)
638224092Sdougb		isc_mem_put(adb->mctx, newdeadentries,
639224092Sdougb			    sizeof(*newdeadentries) * n);
640224092Sdougb	if (newentrylocks != NULL)
641224092Sdougb		isc_mem_put(adb->mctx, newentrylocks,
642224092Sdougb			    sizeof(*newentrylocks) * n);
643224092Sdougb	if (newentry_sd != NULL)
644224092Sdougb		isc_mem_put(adb->mctx, newentry_sd,
645224092Sdougb			    sizeof(*newentry_sd) * n);
646224092Sdougb	if (newentry_refcnt != NULL)
647224092Sdougb		isc_mem_put(adb->mctx, newentry_refcnt,
648224092Sdougb			     sizeof(*newentry_refcnt) * n);
649224092Sdougb done:
650224092Sdougb	isc_task_endexclusive(task);
651224092Sdougb
652254402Serwin check_exit:
653224092Sdougb	LOCK(&adb->lock);
654224092Sdougb	if (dec_adb_irefcnt(adb))
655224092Sdougb		check_exit(adb);
656224092Sdougb	UNLOCK(&adb->lock);
657224092Sdougb	DP(ISC_LOG_INFO, "adb: grow_entries finished");
658224092Sdougb}
659224092Sdougb
660224092Sdougbstatic void
661224092Sdougbgrow_names(isc_task_t *task, isc_event_t *ev) {
662224092Sdougb	dns_adb_t *adb;
663224092Sdougb	dns_adbname_t *name;
664224092Sdougb	dns_adbnamelist_t *newdeadnames = NULL;
665224092Sdougb	dns_adbnamelist_t *newnames = NULL;
666224092Sdougb	isc_boolean_t *newname_sd = NULL;
667224092Sdougb	isc_mutex_t *newnamelocks = NULL;
668224092Sdougb	isc_result_t result;
669224092Sdougb	unsigned int *newname_refcnt = NULL;
670224092Sdougb	unsigned int i, n, bucket;
671224092Sdougb
672224092Sdougb	adb = ev->ev_arg;
673224092Sdougb	INSIST(DNS_ADB_VALID(adb));
674224092Sdougb
675224092Sdougb	isc_event_free(&ev);
676224092Sdougb
677254402Serwin	result = isc_task_beginexclusive(task);
678254402Serwin	if (result != ISC_R_SUCCESS)
679254402Serwin		goto check_exit;
680224092Sdougb
681224092Sdougb	i = 0;
682224092Sdougb	while (nbuckets[i] != 0 && adb->nnames >= nbuckets[i])
683224092Sdougb		i++;
684224092Sdougb	if (nbuckets[i] != 0)
685224092Sdougb		n = nbuckets[i];
686224092Sdougb	else
687224092Sdougb		goto done;
688224092Sdougb
689224092Sdougb	DP(ISC_LOG_INFO, "adb: grow_names to %u starting", n);
690224092Sdougb
691224092Sdougb	/*
692224092Sdougb	 * Are we shutting down?
693224092Sdougb	 */
694224092Sdougb	for (i = 0; i < adb->nnames; i++)
695224092Sdougb		if (adb->name_sd[i])
696224092Sdougb			goto cleanup;
697224092Sdougb
698224092Sdougb	/*
699224092Sdougb	 * Grab all the resources we need.
700224092Sdougb	 */
701224092Sdougb	newnames = isc_mem_get(adb->mctx, sizeof(*newnames) * n);
702224092Sdougb	newdeadnames = isc_mem_get(adb->mctx, sizeof(*newdeadnames) * n);
703224092Sdougb	newnamelocks = isc_mem_get(adb->mctx, sizeof(*newnamelocks) * n);
704224092Sdougb	newname_sd = isc_mem_get(adb->mctx, sizeof(*newname_sd) * n);
705224092Sdougb	newname_refcnt = isc_mem_get(adb->mctx, sizeof(*newname_refcnt) * n);
706224092Sdougb	if (newnames == NULL || newdeadnames == NULL ||
707224092Sdougb	    newnamelocks == NULL || newname_sd == NULL ||
708224092Sdougb	    newname_refcnt == NULL)
709224092Sdougb		goto cleanup;
710224092Sdougb
711224092Sdougb	/*
712224092Sdougb	 * Initialise the new resources.
713224092Sdougb	 */
714224092Sdougb	result = isc_mutexblock_init(newnamelocks, n);
715224092Sdougb	if (result != ISC_R_SUCCESS)
716224092Sdougb		goto cleanup;
717224092Sdougb
718224092Sdougb	for (i = 0; i < n; i++) {
719224092Sdougb		ISC_LIST_INIT(newnames[i]);
720224092Sdougb		ISC_LIST_INIT(newdeadnames[i]);
721224092Sdougb		newname_sd[i] = ISC_FALSE;
722224092Sdougb		newname_refcnt[i] = 0;
723224092Sdougb		adb->irefcnt++;
724224092Sdougb	}
725224092Sdougb
726224092Sdougb	/*
727224092Sdougb	 * Move names to new arrays.
728224092Sdougb	 */
729224092Sdougb	for (i = 0; i < adb->nnames; i++) {
730224092Sdougb		name = ISC_LIST_HEAD(adb->names[i]);
731224092Sdougb		while (name != NULL) {
732224092Sdougb			ISC_LIST_UNLINK(adb->names[i], name, plink);
733224092Sdougb			bucket = dns_name_fullhash(&name->name, ISC_TRUE) % n;
734224092Sdougb			name->lock_bucket = bucket;
735224092Sdougb			ISC_LIST_APPEND(newnames[bucket], name, plink);
736224092Sdougb			INSIST(adb->name_refcnt[i] > 0);
737224092Sdougb			adb->name_refcnt[i]--;
738224092Sdougb			newname_refcnt[bucket]++;
739224092Sdougb			name = ISC_LIST_HEAD(adb->names[i]);
740224092Sdougb		}
741224092Sdougb		name = ISC_LIST_HEAD(adb->deadnames[i]);
742224092Sdougb		while (name != NULL) {
743224092Sdougb			ISC_LIST_UNLINK(adb->deadnames[i], name, plink);
744224092Sdougb			bucket = dns_name_fullhash(&name->name, ISC_TRUE) % n;
745224092Sdougb			name->lock_bucket = bucket;
746224092Sdougb			ISC_LIST_APPEND(newdeadnames[bucket], name, plink);
747224092Sdougb			INSIST(adb->name_refcnt[i] > 0);
748224092Sdougb			adb->name_refcnt[i]--;
749224092Sdougb			newname_refcnt[bucket]++;
750224092Sdougb			name = ISC_LIST_HEAD(adb->deadnames[i]);
751224092Sdougb		}
752224092Sdougb		INSIST(adb->name_refcnt[i] == 0);
753224092Sdougb		adb->irefcnt--;
754224092Sdougb	}
755224092Sdougb
756224092Sdougb	/*
757224092Sdougb	 * Cleanup old resources.
758224092Sdougb	 */
759224092Sdougb	DESTROYMUTEXBLOCK(adb->namelocks, adb->nnames);
760224092Sdougb	isc_mem_put(adb->mctx, adb->names,
761224092Sdougb		    sizeof(*adb->names) * adb->nnames);
762224092Sdougb	isc_mem_put(adb->mctx, adb->deadnames,
763224092Sdougb		    sizeof(*adb->deadnames) * adb->nnames);
764224092Sdougb	isc_mem_put(adb->mctx, adb->namelocks,
765224092Sdougb		    sizeof(*adb->namelocks) * adb->nnames);
766224092Sdougb	isc_mem_put(adb->mctx, adb->name_sd,
767224092Sdougb		    sizeof(*adb->name_sd) * adb->nnames);
768224092Sdougb	isc_mem_put(adb->mctx, adb->name_refcnt,
769224092Sdougb		    sizeof(*adb->name_refcnt) * adb->nnames);
770224092Sdougb
771224092Sdougb	/*
772224092Sdougb	 * Install new resources.
773224092Sdougb	 */
774224092Sdougb	adb->names = newnames;
775224092Sdougb	adb->deadnames = newdeadnames;
776224092Sdougb	adb->namelocks = newnamelocks;
777224092Sdougb	adb->name_sd = newname_sd;
778224092Sdougb	adb->name_refcnt = newname_refcnt;
779224092Sdougb	adb->nnames = n;
780224092Sdougb
781224092Sdougb	/*
782224092Sdougb	 * Only on success do we set adb->grownames_sent to ISC_FALSE.
783224092Sdougb	 * This will prevent us being continuously being called on error.
784224092Sdougb	 */
785224092Sdougb	adb->grownames_sent = ISC_FALSE;
786224092Sdougb	goto done;
787224092Sdougb
788224092Sdougb cleanup:
789224092Sdougb	if (newnames != NULL)
790224092Sdougb		isc_mem_put(adb->mctx, newnames, sizeof(*newnames) * n);
791224092Sdougb	if (newdeadnames != NULL)
792224092Sdougb		isc_mem_put(adb->mctx, newdeadnames, sizeof(*newdeadnames) * n);
793224092Sdougb	if (newnamelocks != NULL)
794224092Sdougb		isc_mem_put(adb->mctx, newnamelocks, sizeof(*newnamelocks) * n);
795224092Sdougb	if (newname_sd != NULL)
796224092Sdougb		isc_mem_put(adb->mctx, newname_sd, sizeof(*newname_sd) * n);
797224092Sdougb	if (newname_refcnt != NULL)
798224092Sdougb		isc_mem_put(adb->mctx, newname_refcnt,
799224092Sdougb			     sizeof(*newname_refcnt) * n);
800224092Sdougb done:
801224092Sdougb	isc_task_endexclusive(task);
802224092Sdougb
803254402Serwin check_exit:
804224092Sdougb	LOCK(&adb->lock);
805224092Sdougb	if (dec_adb_irefcnt(adb))
806224092Sdougb		check_exit(adb);
807224092Sdougb	UNLOCK(&adb->lock);
808224092Sdougb	DP(ISC_LOG_INFO, "adb: grow_names finished");
809224092Sdougb}
810224092Sdougb
811224092Sdougb/*
812135446Strhodes * Requires the adbname bucket be locked and that no entry buckets be locked.
813135446Strhodes *
814135446Strhodes * This code handles A and AAAA rdatasets only.
815135446Strhodes */
816135446Strhodesstatic isc_result_t
817135446Strhodesimport_rdataset(dns_adbname_t *adbname, dns_rdataset_t *rdataset,
818135446Strhodes		isc_stdtime_t now)
819135446Strhodes{
820135446Strhodes	isc_result_t result;
821135446Strhodes	dns_adb_t *adb;
822135446Strhodes	dns_adbnamehook_t *nh;
823135446Strhodes	dns_adbnamehook_t *anh;
824135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
825135446Strhodes	struct in_addr ina;
826135446Strhodes	struct in6_addr in6a;
827135446Strhodes	isc_sockaddr_t sockaddr;
828135446Strhodes	dns_adbentry_t *foundentry;  /* NO CLEAN UP! */
829135446Strhodes	int addr_bucket;
830135446Strhodes	isc_boolean_t new_addresses_added;
831135446Strhodes	dns_rdatatype_t rdtype;
832135446Strhodes	unsigned int findoptions;
833186462Sdougb	dns_adbnamehooklist_t *hookhead;
834135446Strhodes
835135446Strhodes	INSIST(DNS_ADBNAME_VALID(adbname));
836135446Strhodes	adb = adbname->adb;
837135446Strhodes	INSIST(DNS_ADB_VALID(adb));
838135446Strhodes
839135446Strhodes	rdtype = rdataset->type;
840135446Strhodes	INSIST((rdtype == dns_rdatatype_a) || (rdtype == dns_rdatatype_aaaa));
841135446Strhodes	if (rdtype == dns_rdatatype_a)
842135446Strhodes		findoptions = DNS_ADBFIND_INET;
843135446Strhodes	else
844135446Strhodes		findoptions = DNS_ADBFIND_INET6;
845135446Strhodes
846135446Strhodes	addr_bucket = DNS_ADB_INVALIDBUCKET;
847135446Strhodes	new_addresses_added = ISC_FALSE;
848135446Strhodes
849135446Strhodes	nh = NULL;
850135446Strhodes	result = dns_rdataset_first(rdataset);
851135446Strhodes	while (result == ISC_R_SUCCESS) {
852135446Strhodes		dns_rdata_reset(&rdata);
853135446Strhodes		dns_rdataset_current(rdataset, &rdata);
854135446Strhodes		if (rdtype == dns_rdatatype_a) {
855135446Strhodes			INSIST(rdata.length == 4);
856262706Serwin			memmove(&ina.s_addr, rdata.data, 4);
857135446Strhodes			isc_sockaddr_fromin(&sockaddr, &ina, 0);
858186462Sdougb			hookhead = &adbname->v4;
859135446Strhodes		} else {
860135446Strhodes			INSIST(rdata.length == 16);
861262706Serwin			memmove(in6a.s6_addr, rdata.data, 16);
862135446Strhodes			isc_sockaddr_fromin6(&sockaddr, &in6a, 0);
863186462Sdougb			hookhead = &adbname->v6;
864135446Strhodes		}
865135446Strhodes
866135446Strhodes		INSIST(nh == NULL);
867135446Strhodes		nh = new_adbnamehook(adb, NULL);
868135446Strhodes		if (nh == NULL) {
869135446Strhodes			adbname->partial_result |= findoptions;
870135446Strhodes			result = ISC_R_NOMEMORY;
871135446Strhodes			goto fail;
872135446Strhodes		}
873135446Strhodes
874193149Sdougb		foundentry = find_entry_and_lock(adb, &sockaddr, &addr_bucket,
875193149Sdougb						 now);
876135446Strhodes		if (foundentry == NULL) {
877135446Strhodes			dns_adbentry_t *entry;
878135446Strhodes
879135446Strhodes			entry = new_adbentry(adb);
880135446Strhodes			if (entry == NULL) {
881135446Strhodes				adbname->partial_result |= findoptions;
882135446Strhodes				result = ISC_R_NOMEMORY;
883135446Strhodes				goto fail;
884135446Strhodes			}
885135446Strhodes
886135446Strhodes			entry->sockaddr = sockaddr;
887135446Strhodes			entry->refcnt = 1;
888135446Strhodes
889135446Strhodes			nh->entry = entry;
890135446Strhodes
891135446Strhodes			link_entry(adb, addr_bucket, entry);
892135446Strhodes		} else {
893186462Sdougb			for (anh = ISC_LIST_HEAD(*hookhead);
894135446Strhodes			     anh != NULL;
895135446Strhodes			     anh = ISC_LIST_NEXT(anh, plink))
896135446Strhodes				if (anh->entry == foundentry)
897135446Strhodes					break;
898135446Strhodes			if (anh == NULL) {
899135446Strhodes				foundentry->refcnt++;
900135446Strhodes				nh->entry = foundentry;
901135446Strhodes			} else
902135446Strhodes				free_adbnamehook(adb, &nh);
903135446Strhodes		}
904135446Strhodes
905135446Strhodes		new_addresses_added = ISC_TRUE;
906186462Sdougb		if (nh != NULL)
907186462Sdougb			ISC_LIST_APPEND(*hookhead, nh, plink);
908135446Strhodes		nh = NULL;
909135446Strhodes		result = dns_rdataset_next(rdataset);
910135446Strhodes	}
911135446Strhodes
912135446Strhodes fail:
913135446Strhodes	if (nh != NULL)
914135446Strhodes		free_adbnamehook(adb, &nh);
915135446Strhodes
916135446Strhodes	if (addr_bucket != DNS_ADB_INVALIDBUCKET)
917135446Strhodes		UNLOCK(&adb->entrylocks[addr_bucket]);
918135446Strhodes
919135446Strhodes	if (rdataset->trust == dns_trust_glue ||
920135446Strhodes	    rdataset->trust == dns_trust_additional)
921135446Strhodes		rdataset->ttl = ADB_CACHE_MINIMUM;
922225361Sdougb	else if (rdataset->trust == dns_trust_ultimate)
923225361Sdougb		rdataset->ttl = 0;
924135446Strhodes	else
925135446Strhodes		rdataset->ttl = ttlclamp(rdataset->ttl);
926135446Strhodes
927135446Strhodes	if (rdtype == dns_rdatatype_a) {
928135446Strhodes		DP(NCACHE_LEVEL, "expire_v4 set to MIN(%u,%u) import_rdataset",
929135446Strhodes		   adbname->expire_v4, now + rdataset->ttl);
930135446Strhodes		adbname->expire_v4 = ISC_MIN(adbname->expire_v4,
931135446Strhodes					     now + rdataset->ttl);
932135446Strhodes	} else {
933135446Strhodes		DP(NCACHE_LEVEL, "expire_v6 set to MIN(%u,%u) import_rdataset",
934135446Strhodes		   adbname->expire_v6, now + rdataset->ttl);
935135446Strhodes		adbname->expire_v6 = ISC_MIN(adbname->expire_v6,
936135446Strhodes					     now + rdataset->ttl);
937135446Strhodes	}
938135446Strhodes
939135446Strhodes	if (new_addresses_added) {
940135446Strhodes		/*
941135446Strhodes		 * Lie a little here.  This is more or less so code that cares
942135446Strhodes		 * can find out if any new information was added or not.
943135446Strhodes		 */
944135446Strhodes		return (ISC_R_SUCCESS);
945135446Strhodes	}
946135446Strhodes
947135446Strhodes	return (result);
948135446Strhodes}
949135446Strhodes
950135446Strhodes/*
951135446Strhodes * Requires the name's bucket be locked.
952135446Strhodes */
953135446Strhodesstatic isc_boolean_t
954135446Strhodeskill_name(dns_adbname_t **n, isc_eventtype_t ev) {
955135446Strhodes	dns_adbname_t *name;
956135446Strhodes	isc_boolean_t result = ISC_FALSE;
957135446Strhodes	isc_boolean_t result4, result6;
958193149Sdougb	int bucket;
959135446Strhodes	dns_adb_t *adb;
960135446Strhodes
961135446Strhodes	INSIST(n != NULL);
962135446Strhodes	name = *n;
963135446Strhodes	*n = NULL;
964135446Strhodes	INSIST(DNS_ADBNAME_VALID(name));
965135446Strhodes	adb = name->adb;
966135446Strhodes	INSIST(DNS_ADB_VALID(adb));
967135446Strhodes
968135446Strhodes	DP(DEF_LEVEL, "killing name %p", name);
969135446Strhodes
970135446Strhodes	/*
971135446Strhodes	 * If we're dead already, just check to see if we should go
972135446Strhodes	 * away now or not.
973135446Strhodes	 */
974135446Strhodes	if (NAME_DEAD(name) && !NAME_FETCH(name)) {
975135446Strhodes		result = unlink_name(adb, name);
976135446Strhodes		free_adbname(adb, &name);
977135446Strhodes		if (result)
978135446Strhodes			result = dec_adb_irefcnt(adb);
979135446Strhodes		return (result);
980135446Strhodes	}
981135446Strhodes
982135446Strhodes	/*
983135446Strhodes	 * Clean up the name's various lists.  These two are destructive
984135446Strhodes	 * in that they will always empty the list.
985135446Strhodes	 */
986275672Sdelphij	clean_finds_at_name(name, ev, 0, DNS_ADBFIND_ADDRESSMASK);
987135446Strhodes	result4 = clean_namehooks(adb, &name->v4);
988135446Strhodes	result6 = clean_namehooks(adb, &name->v6);
989135446Strhodes	clean_target(adb, &name->target);
990135446Strhodes	result = ISC_TF(result4 || result6);
991135446Strhodes
992135446Strhodes	/*
993135446Strhodes	 * If fetches are running, cancel them.  If none are running, we can
994135446Strhodes	 * just kill the name here.
995135446Strhodes	 */
996135446Strhodes	if (!NAME_FETCH(name)) {
997135446Strhodes		INSIST(result == ISC_FALSE);
998135446Strhodes		result = unlink_name(adb, name);
999135446Strhodes		free_adbname(adb, &name);
1000135446Strhodes		if (result)
1001135446Strhodes			result = dec_adb_irefcnt(adb);
1002135446Strhodes	} else {
1003135446Strhodes		cancel_fetches_at_name(name);
1004193149Sdougb		if (!NAME_DEAD(name)) {
1005193149Sdougb			bucket = name->lock_bucket;
1006193149Sdougb			ISC_LIST_UNLINK(adb->names[bucket], name, plink);
1007193149Sdougb			ISC_LIST_APPEND(adb->deadnames[bucket], name, plink);
1008193149Sdougb			name->flags |= NAME_IS_DEAD;
1009193149Sdougb		}
1010135446Strhodes	}
1011135446Strhodes	return (result);
1012135446Strhodes}
1013135446Strhodes
1014135446Strhodes/*
1015135446Strhodes * Requires the name's bucket be locked and no entry buckets be locked.
1016135446Strhodes */
1017135446Strhodesstatic isc_boolean_t
1018193149Sdougbcheck_expire_namehooks(dns_adbname_t *name, isc_stdtime_t now) {
1019135446Strhodes	dns_adb_t *adb;
1020135446Strhodes	isc_boolean_t result4 = ISC_FALSE;
1021135446Strhodes	isc_boolean_t result6 = ISC_FALSE;
1022135446Strhodes
1023135446Strhodes	INSIST(DNS_ADBNAME_VALID(name));
1024135446Strhodes	adb = name->adb;
1025135446Strhodes	INSIST(DNS_ADB_VALID(adb));
1026135446Strhodes
1027135446Strhodes	/*
1028135446Strhodes	 * Check to see if we need to remove the v4 addresses
1029135446Strhodes	 */
1030193149Sdougb	if (!NAME_FETCH_V4(name) && EXPIRE_OK(name->expire_v4, now)) {
1031135446Strhodes		if (NAME_HAS_V4(name)) {
1032135446Strhodes			DP(DEF_LEVEL, "expiring v4 for name %p", name);
1033135446Strhodes			result4 = clean_namehooks(adb, &name->v4);
1034135446Strhodes			name->partial_result &= ~DNS_ADBFIND_INET;
1035135446Strhodes		}
1036135446Strhodes		name->expire_v4 = INT_MAX;
1037135446Strhodes		name->fetch_err = FIND_ERR_UNEXPECTED;
1038135446Strhodes	}
1039135446Strhodes
1040135446Strhodes	/*
1041135446Strhodes	 * Check to see if we need to remove the v6 addresses
1042135446Strhodes	 */
1043193149Sdougb	if (!NAME_FETCH_V6(name) && EXPIRE_OK(name->expire_v6, now)) {
1044135446Strhodes		if (NAME_HAS_V6(name)) {
1045135446Strhodes			DP(DEF_LEVEL, "expiring v6 for name %p", name);
1046135446Strhodes			result6 = clean_namehooks(adb, &name->v6);
1047135446Strhodes			name->partial_result &= ~DNS_ADBFIND_INET6;
1048135446Strhodes		}
1049135446Strhodes		name->expire_v6 = INT_MAX;
1050135446Strhodes		name->fetch6_err = FIND_ERR_UNEXPECTED;
1051135446Strhodes	}
1052135446Strhodes
1053135446Strhodes	/*
1054135446Strhodes	 * Check to see if we need to remove the alias target.
1055135446Strhodes	 */
1056193149Sdougb	if (EXPIRE_OK(name->expire_target, now)) {
1057135446Strhodes		clean_target(adb, &name->target);
1058135446Strhodes		name->expire_target = INT_MAX;
1059135446Strhodes	}
1060135446Strhodes	return (ISC_TF(result4 || result6));
1061135446Strhodes}
1062135446Strhodes
1063135446Strhodes/*
1064135446Strhodes * Requires the name's bucket be locked.
1065135446Strhodes */
1066135446Strhodesstatic inline void
1067135446Strhodeslink_name(dns_adb_t *adb, int bucket, dns_adbname_t *name) {
1068135446Strhodes	INSIST(name->lock_bucket == DNS_ADB_INVALIDBUCKET);
1069135446Strhodes
1070135446Strhodes	ISC_LIST_PREPEND(adb->names[bucket], name, plink);
1071135446Strhodes	name->lock_bucket = bucket;
1072135446Strhodes	adb->name_refcnt[bucket]++;
1073135446Strhodes}
1074135446Strhodes
1075135446Strhodes/*
1076135446Strhodes * Requires the name's bucket be locked.
1077135446Strhodes */
1078135446Strhodesstatic inline isc_boolean_t
1079135446Strhodesunlink_name(dns_adb_t *adb, dns_adbname_t *name) {
1080135446Strhodes	int bucket;
1081135446Strhodes	isc_boolean_t result = ISC_FALSE;
1082135446Strhodes
1083135446Strhodes	bucket = name->lock_bucket;
1084135446Strhodes	INSIST(bucket != DNS_ADB_INVALIDBUCKET);
1085135446Strhodes
1086193149Sdougb	if (NAME_DEAD(name))
1087193149Sdougb		ISC_LIST_UNLINK(adb->deadnames[bucket], name, plink);
1088193149Sdougb	else
1089193149Sdougb		ISC_LIST_UNLINK(adb->names[bucket], name, plink);
1090135446Strhodes	name->lock_bucket = DNS_ADB_INVALIDBUCKET;
1091135446Strhodes	INSIST(adb->name_refcnt[bucket] > 0);
1092135446Strhodes	adb->name_refcnt[bucket]--;
1093135446Strhodes	if (adb->name_sd[bucket] && adb->name_refcnt[bucket] == 0)
1094135446Strhodes		result = ISC_TRUE;
1095135446Strhodes	return (result);
1096135446Strhodes}
1097135446Strhodes
1098135446Strhodes/*
1099135446Strhodes * Requires the entry's bucket be locked.
1100135446Strhodes */
1101135446Strhodesstatic inline void
1102135446Strhodeslink_entry(dns_adb_t *adb, int bucket, dns_adbentry_t *entry) {
1103193149Sdougb	int i;
1104193149Sdougb	dns_adbentry_t *e;
1105193149Sdougb
1106214586Sdougb	if (isc_mem_isovermem(adb->mctx)) {
1107193149Sdougb		for (i = 0; i < 2; i++) {
1108193149Sdougb			e = ISC_LIST_TAIL(adb->entries[bucket]);
1109193149Sdougb			if (e == NULL)
1110193149Sdougb				break;
1111193149Sdougb			if (e->refcnt == 0) {
1112193149Sdougb				unlink_entry(adb, e);
1113193149Sdougb				free_adbentry(adb, &e);
1114193149Sdougb				continue;
1115193149Sdougb			}
1116193149Sdougb			INSIST((e->flags & ENTRY_IS_DEAD) == 0);
1117193149Sdougb			e->flags |= ENTRY_IS_DEAD;
1118193149Sdougb			ISC_LIST_UNLINK(adb->entries[bucket], e, plink);
1119193149Sdougb			ISC_LIST_PREPEND(adb->deadentries[bucket], e, plink);
1120193149Sdougb		}
1121193149Sdougb	}
1122193149Sdougb
1123135446Strhodes	ISC_LIST_PREPEND(adb->entries[bucket], entry, plink);
1124135446Strhodes	entry->lock_bucket = bucket;
1125135446Strhodes	adb->entry_refcnt[bucket]++;
1126135446Strhodes}
1127135446Strhodes
1128135446Strhodes/*
1129135446Strhodes * Requires the entry's bucket be locked.
1130135446Strhodes */
1131135446Strhodesstatic inline isc_boolean_t
1132135446Strhodesunlink_entry(dns_adb_t *adb, dns_adbentry_t *entry) {
1133135446Strhodes	int bucket;
1134135446Strhodes	isc_boolean_t result = ISC_FALSE;
1135135446Strhodes
1136135446Strhodes	bucket = entry->lock_bucket;
1137135446Strhodes	INSIST(bucket != DNS_ADB_INVALIDBUCKET);
1138135446Strhodes
1139193149Sdougb	if ((entry->flags & ENTRY_IS_DEAD) != 0)
1140193149Sdougb		ISC_LIST_UNLINK(adb->deadentries[bucket], entry, plink);
1141193149Sdougb	else
1142193149Sdougb		ISC_LIST_UNLINK(adb->entries[bucket], entry, plink);
1143135446Strhodes	entry->lock_bucket = DNS_ADB_INVALIDBUCKET;
1144135446Strhodes	INSIST(adb->entry_refcnt[bucket] > 0);
1145135446Strhodes	adb->entry_refcnt[bucket]--;
1146135446Strhodes	if (adb->entry_sd[bucket] && adb->entry_refcnt[bucket] == 0)
1147135446Strhodes		result = ISC_TRUE;
1148135446Strhodes	return (result);
1149135446Strhodes}
1150135446Strhodes
1151135446Strhodesstatic inline void
1152135446Strhodesviolate_locking_hierarchy(isc_mutex_t *have, isc_mutex_t *want) {
1153135446Strhodes	if (isc_mutex_trylock(want) != ISC_R_SUCCESS) {
1154135446Strhodes		UNLOCK(have);
1155135446Strhodes		LOCK(want);
1156135446Strhodes		LOCK(have);
1157135446Strhodes	}
1158135446Strhodes}
1159135446Strhodes
1160135446Strhodes/*
1161135446Strhodes * The ADB _MUST_ be locked before calling.  Also, exit conditions must be
1162135446Strhodes * checked after calling this function.
1163135446Strhodes */
1164135446Strhodesstatic isc_boolean_t
1165135446Strhodesshutdown_names(dns_adb_t *adb) {
1166224092Sdougb	unsigned int bucket;
1167135446Strhodes	isc_boolean_t result = ISC_FALSE;
1168135446Strhodes	dns_adbname_t *name;
1169135446Strhodes	dns_adbname_t *next_name;
1170135446Strhodes
1171224092Sdougb	for (bucket = 0; bucket < adb->nnames; bucket++) {
1172135446Strhodes		LOCK(&adb->namelocks[bucket]);
1173135446Strhodes		adb->name_sd[bucket] = ISC_TRUE;
1174135446Strhodes
1175135446Strhodes		name = ISC_LIST_HEAD(adb->names[bucket]);
1176135446Strhodes		if (name == NULL) {
1177135446Strhodes			/*
1178135446Strhodes			 * This bucket has no names.  We must decrement the
1179135446Strhodes			 * irefcnt ourselves, since it will not be
1180135446Strhodes			 * automatically triggered by a name being unlinked.
1181135446Strhodes			 */
1182135446Strhodes			INSIST(result == ISC_FALSE);
1183135446Strhodes			result = dec_adb_irefcnt(adb);
1184135446Strhodes		} else {
1185135446Strhodes			/*
1186135446Strhodes			 * Run through the list.  For each name, clean up finds
1187135446Strhodes			 * found there, and cancel any fetches running.  When
1188135446Strhodes			 * all the fetches are canceled, the name will destroy
1189135446Strhodes			 * itself.
1190135446Strhodes			 */
1191135446Strhodes			while (name != NULL) {
1192135446Strhodes				next_name = ISC_LIST_NEXT(name, plink);
1193135446Strhodes				INSIST(result == ISC_FALSE);
1194135446Strhodes				result = kill_name(&name,
1195135446Strhodes						   DNS_EVENT_ADBSHUTDOWN);
1196135446Strhodes				name = next_name;
1197135446Strhodes			}
1198135446Strhodes		}
1199135446Strhodes
1200135446Strhodes		UNLOCK(&adb->namelocks[bucket]);
1201135446Strhodes	}
1202135446Strhodes	return (result);
1203135446Strhodes}
1204135446Strhodes
1205135446Strhodes/*
1206135446Strhodes * The ADB _MUST_ be locked before calling.  Also, exit conditions must be
1207135446Strhodes * checked after calling this function.
1208135446Strhodes */
1209135446Strhodesstatic isc_boolean_t
1210135446Strhodesshutdown_entries(dns_adb_t *adb) {
1211224092Sdougb	unsigned int bucket;
1212135446Strhodes	isc_boolean_t result = ISC_FALSE;
1213135446Strhodes	dns_adbentry_t *entry;
1214135446Strhodes	dns_adbentry_t *next_entry;
1215135446Strhodes
1216224092Sdougb	for (bucket = 0; bucket < adb->nentries; bucket++) {
1217135446Strhodes		LOCK(&adb->entrylocks[bucket]);
1218135446Strhodes		adb->entry_sd[bucket] = ISC_TRUE;
1219135446Strhodes
1220135446Strhodes		entry = ISC_LIST_HEAD(adb->entries[bucket]);
1221193149Sdougb		if (adb->entry_refcnt[bucket] == 0) {
1222135446Strhodes			/*
1223135446Strhodes			 * This bucket has no entries.  We must decrement the
1224135446Strhodes			 * irefcnt ourselves, since it will not be
1225135446Strhodes			 * automatically triggered by an entry being unlinked.
1226135446Strhodes			 */
1227135446Strhodes			result = dec_adb_irefcnt(adb);
1228135446Strhodes		} else {
1229135446Strhodes			/*
1230135446Strhodes			 * Run through the list.  Cleanup any entries not
1231135446Strhodes			 * associated with names, and which are not in use.
1232135446Strhodes			 */
1233135446Strhodes			while (entry != NULL) {
1234135446Strhodes				next_entry = ISC_LIST_NEXT(entry, plink);
1235135446Strhodes				if (entry->refcnt == 0 &&
1236135446Strhodes				    entry->expires != 0) {
1237135446Strhodes					result = unlink_entry(adb, entry);
1238135446Strhodes					free_adbentry(adb, &entry);
1239135446Strhodes					if (result)
1240135446Strhodes						result = dec_adb_irefcnt(adb);
1241135446Strhodes				}
1242135446Strhodes				entry = next_entry;
1243135446Strhodes			}
1244135446Strhodes		}
1245135446Strhodes
1246135446Strhodes		UNLOCK(&adb->entrylocks[bucket]);
1247135446Strhodes	}
1248135446Strhodes	return (result);
1249135446Strhodes}
1250135446Strhodes
1251135446Strhodes/*
1252135446Strhodes * Name bucket must be locked
1253135446Strhodes */
1254135446Strhodesstatic void
1255135446Strhodescancel_fetches_at_name(dns_adbname_t *name) {
1256135446Strhodes	if (NAME_FETCH_A(name))
1257135446Strhodes	    dns_resolver_cancelfetch(name->fetch_a->fetch);
1258135446Strhodes
1259135446Strhodes	if (NAME_FETCH_AAAA(name))
1260135446Strhodes	    dns_resolver_cancelfetch(name->fetch_aaaa->fetch);
1261135446Strhodes}
1262135446Strhodes
1263135446Strhodes/*
1264135446Strhodes * Assumes the name bucket is locked.
1265135446Strhodes */
1266135446Strhodesstatic isc_boolean_t
1267135446Strhodesclean_namehooks(dns_adb_t *adb, dns_adbnamehooklist_t *namehooks) {
1268135446Strhodes	dns_adbentry_t *entry;
1269135446Strhodes	dns_adbnamehook_t *namehook;
1270135446Strhodes	int addr_bucket;
1271135446Strhodes	isc_boolean_t result = ISC_FALSE;
1272214586Sdougb	isc_boolean_t overmem = isc_mem_isovermem(adb->mctx);
1273135446Strhodes
1274135446Strhodes	addr_bucket = DNS_ADB_INVALIDBUCKET;
1275135446Strhodes	namehook = ISC_LIST_HEAD(*namehooks);
1276135446Strhodes	while (namehook != NULL) {
1277135446Strhodes		INSIST(DNS_ADBNAMEHOOK_VALID(namehook));
1278135446Strhodes
1279135446Strhodes		/*
1280135446Strhodes		 * Clean up the entry if needed.
1281135446Strhodes		 */
1282135446Strhodes		entry = namehook->entry;
1283135446Strhodes		if (entry != NULL) {
1284135446Strhodes			INSIST(DNS_ADBENTRY_VALID(entry));
1285135446Strhodes
1286135446Strhodes			if (addr_bucket != entry->lock_bucket) {
1287135446Strhodes				if (addr_bucket != DNS_ADB_INVALIDBUCKET)
1288135446Strhodes					UNLOCK(&adb->entrylocks[addr_bucket]);
1289135446Strhodes				addr_bucket = entry->lock_bucket;
1290254402Serwin				INSIST(addr_bucket != DNS_ADB_INVALIDBUCKET);
1291135446Strhodes				LOCK(&adb->entrylocks[addr_bucket]);
1292135446Strhodes			}
1293135446Strhodes
1294214586Sdougb			result = dec_entry_refcnt(adb, overmem, entry,
1295214586Sdougb						  ISC_FALSE);
1296135446Strhodes		}
1297135446Strhodes
1298135446Strhodes		/*
1299135446Strhodes		 * Free the namehook
1300135446Strhodes		 */
1301135446Strhodes		namehook->entry = NULL;
1302135446Strhodes		ISC_LIST_UNLINK(*namehooks, namehook, plink);
1303135446Strhodes		free_adbnamehook(adb, &namehook);
1304135446Strhodes
1305135446Strhodes		namehook = ISC_LIST_HEAD(*namehooks);
1306135446Strhodes	}
1307135446Strhodes
1308135446Strhodes	if (addr_bucket != DNS_ADB_INVALIDBUCKET)
1309135446Strhodes		UNLOCK(&adb->entrylocks[addr_bucket]);
1310135446Strhodes	return (result);
1311135446Strhodes}
1312135446Strhodes
1313135446Strhodesstatic void
1314135446Strhodesclean_target(dns_adb_t *adb, dns_name_t *target) {
1315135446Strhodes	if (dns_name_countlabels(target) > 0) {
1316135446Strhodes		dns_name_free(target, adb->mctx);
1317135446Strhodes		dns_name_init(target, NULL);
1318135446Strhodes	}
1319135446Strhodes}
1320135446Strhodes
1321135446Strhodesstatic isc_result_t
1322135446Strhodesset_target(dns_adb_t *adb, dns_name_t *name, dns_name_t *fname,
1323135446Strhodes	   dns_rdataset_t *rdataset, dns_name_t *target)
1324135446Strhodes{
1325135446Strhodes	isc_result_t result;
1326135446Strhodes	dns_namereln_t namereln;
1327135446Strhodes	unsigned int nlabels;
1328135446Strhodes	int order;
1329135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
1330135446Strhodes	dns_fixedname_t fixed1, fixed2;
1331135446Strhodes	dns_name_t *prefix, *new_target;
1332135446Strhodes
1333135446Strhodes	REQUIRE(dns_name_countlabels(target) == 0);
1334135446Strhodes
1335135446Strhodes	if (rdataset->type == dns_rdatatype_cname) {
1336135446Strhodes		dns_rdata_cname_t cname;
1337135446Strhodes
1338135446Strhodes		/*
1339135446Strhodes		 * Copy the CNAME's target into the target name.
1340135446Strhodes		 */
1341135446Strhodes		result = dns_rdataset_first(rdataset);
1342135446Strhodes		if (result != ISC_R_SUCCESS)
1343135446Strhodes			return (result);
1344135446Strhodes		dns_rdataset_current(rdataset, &rdata);
1345135446Strhodes		result = dns_rdata_tostruct(&rdata, &cname, NULL);
1346135446Strhodes		if (result != ISC_R_SUCCESS)
1347135446Strhodes			return (result);
1348135446Strhodes		result = dns_name_dup(&cname.cname, adb->mctx, target);
1349135446Strhodes		dns_rdata_freestruct(&cname);
1350135446Strhodes		if (result != ISC_R_SUCCESS)
1351135446Strhodes			return (result);
1352135446Strhodes	} else {
1353135446Strhodes		dns_rdata_dname_t dname;
1354135446Strhodes
1355135446Strhodes		INSIST(rdataset->type == dns_rdatatype_dname);
1356135446Strhodes		namereln = dns_name_fullcompare(name, fname, &order, &nlabels);
1357135446Strhodes		INSIST(namereln == dns_namereln_subdomain);
1358135446Strhodes		/*
1359135446Strhodes		 * Get the target name of the DNAME.
1360135446Strhodes		 */
1361135446Strhodes		result = dns_rdataset_first(rdataset);
1362135446Strhodes		if (result != ISC_R_SUCCESS)
1363135446Strhodes			return (result);
1364135446Strhodes		dns_rdataset_current(rdataset, &rdata);
1365135446Strhodes		result = dns_rdata_tostruct(&rdata, &dname, NULL);
1366135446Strhodes		if (result != ISC_R_SUCCESS)
1367135446Strhodes			return (result);
1368135446Strhodes		/*
1369135446Strhodes		 * Construct the new target name.
1370135446Strhodes		 */
1371135446Strhodes		dns_fixedname_init(&fixed1);
1372135446Strhodes		prefix = dns_fixedname_name(&fixed1);
1373135446Strhodes		dns_fixedname_init(&fixed2);
1374135446Strhodes		new_target = dns_fixedname_name(&fixed2);
1375135446Strhodes		dns_name_split(name, nlabels, prefix, NULL);
1376135446Strhodes		result = dns_name_concatenate(prefix, &dname.dname, new_target,
1377135446Strhodes					      NULL);
1378135446Strhodes		dns_rdata_freestruct(&dname);
1379135446Strhodes		if (result != ISC_R_SUCCESS)
1380135446Strhodes			return (result);
1381135446Strhodes		result = dns_name_dup(new_target, adb->mctx, target);
1382135446Strhodes		if (result != ISC_R_SUCCESS)
1383135446Strhodes			return (result);
1384135446Strhodes	}
1385135446Strhodes
1386135446Strhodes	return (ISC_R_SUCCESS);
1387135446Strhodes}
1388135446Strhodes
1389135446Strhodes/*
1390135446Strhodes * Assumes nothing is locked, since this is called by the client.
1391135446Strhodes */
1392135446Strhodesstatic void
1393135446Strhodesevent_free(isc_event_t *event) {
1394135446Strhodes	dns_adbfind_t *find;
1395135446Strhodes
1396135446Strhodes	INSIST(event != NULL);
1397135446Strhodes	find = event->ev_destroy_arg;
1398135446Strhodes	INSIST(DNS_ADBFIND_VALID(find));
1399135446Strhodes
1400135446Strhodes	LOCK(&find->lock);
1401135446Strhodes	find->flags |= FIND_EVENT_FREED;
1402135446Strhodes	event->ev_destroy_arg = NULL;
1403135446Strhodes	UNLOCK(&find->lock);
1404135446Strhodes}
1405135446Strhodes
1406135446Strhodes/*
1407135446Strhodes * Assumes the name bucket is locked.
1408135446Strhodes */
1409135446Strhodesstatic void
1410135446Strhodesclean_finds_at_name(dns_adbname_t *name, isc_eventtype_t evtype,
1411275672Sdelphij		    isc_uint32_t qtotal, unsigned int addrs)
1412135446Strhodes{
1413135446Strhodes	isc_event_t *ev;
1414135446Strhodes	isc_task_t *task;
1415135446Strhodes	dns_adbfind_t *find;
1416135446Strhodes	dns_adbfind_t *next_find;
1417135446Strhodes	isc_boolean_t process;
1418135446Strhodes	unsigned int wanted, notify;
1419135446Strhodes
1420135446Strhodes	DP(ENTER_LEVEL,
1421135446Strhodes	   "ENTER clean_finds_at_name, name %p, evtype %08x, addrs %08x",
1422135446Strhodes	   name, evtype, addrs);
1423135446Strhodes
1424135446Strhodes	find = ISC_LIST_HEAD(name->finds);
1425135446Strhodes	while (find != NULL) {
1426135446Strhodes		LOCK(&find->lock);
1427135446Strhodes		next_find = ISC_LIST_NEXT(find, plink);
1428135446Strhodes
1429135446Strhodes		process = ISC_FALSE;
1430135446Strhodes		wanted = find->flags & DNS_ADBFIND_ADDRESSMASK;
1431135446Strhodes		notify = wanted & addrs;
1432135446Strhodes
1433135446Strhodes		switch (evtype) {
1434135446Strhodes		case DNS_EVENT_ADBMOREADDRESSES:
1435135446Strhodes			DP(ISC_LOG_DEBUG(3), "DNS_EVENT_ADBMOREADDRESSES");
1436135446Strhodes			if ((notify) != 0) {
1437135446Strhodes				find->flags &= ~addrs;
1438135446Strhodes				process = ISC_TRUE;
1439135446Strhodes			}
1440135446Strhodes			break;
1441135446Strhodes		case DNS_EVENT_ADBNOMOREADDRESSES:
1442135446Strhodes			DP(ISC_LOG_DEBUG(3), "DNS_EVENT_ADBNOMOREADDRESSES");
1443135446Strhodes			find->flags &= ~addrs;
1444135446Strhodes			wanted = find->flags & DNS_ADBFIND_ADDRESSMASK;
1445135446Strhodes			if (wanted == 0)
1446135446Strhodes				process = ISC_TRUE;
1447135446Strhodes			break;
1448135446Strhodes		default:
1449135446Strhodes			find->flags &= ~addrs;
1450135446Strhodes			process = ISC_TRUE;
1451135446Strhodes		}
1452135446Strhodes
1453135446Strhodes		if (process) {
1454135446Strhodes			DP(DEF_LEVEL, "cfan: processing find %p", find);
1455135446Strhodes			/*
1456135446Strhodes			 * Unlink the find from the name, letting the caller
1457135446Strhodes			 * call dns_adb_destroyfind() on it to clean it up
1458135446Strhodes			 * later.
1459135446Strhodes			 */
1460135446Strhodes			ISC_LIST_UNLINK(name->finds, find, plink);
1461135446Strhodes			find->adbname = NULL;
1462135446Strhodes			find->name_bucket = DNS_ADB_INVALIDBUCKET;
1463135446Strhodes
1464135446Strhodes			INSIST(!FIND_EVENTSENT(find));
1465135446Strhodes
1466135446Strhodes			ev = &find->event;
1467135446Strhodes			task = ev->ev_sender;
1468135446Strhodes			ev->ev_sender = find;
1469135446Strhodes			find->result_v4 = find_err_map[name->fetch_err];
1470135446Strhodes			find->result_v6 = find_err_map[name->fetch6_err];
1471275672Sdelphij			find->qtotal += qtotal;
1472135446Strhodes			ev->ev_type = evtype;
1473135446Strhodes			ev->ev_destroy = event_free;
1474135446Strhodes			ev->ev_destroy_arg = find;
1475135446Strhodes
1476135446Strhodes			DP(DEF_LEVEL,
1477135446Strhodes			   "sending event %p to task %p for find %p",
1478135446Strhodes			   ev, task, find);
1479135446Strhodes
1480135446Strhodes			isc_task_sendanddetach(&task, (isc_event_t **)&ev);
1481135446Strhodes		} else {
1482135446Strhodes			DP(DEF_LEVEL, "cfan: skipping find %p", find);
1483135446Strhodes		}
1484135446Strhodes
1485135446Strhodes		UNLOCK(&find->lock);
1486135446Strhodes		find = next_find;
1487135446Strhodes	}
1488135446Strhodes
1489135446Strhodes	DP(ENTER_LEVEL, "EXIT clean_finds_at_name, name %p", name);
1490135446Strhodes}
1491135446Strhodes
1492135446Strhodesstatic inline void
1493135446Strhodescheck_exit(dns_adb_t *adb) {
1494135446Strhodes	isc_event_t *event;
1495135446Strhodes	/*
1496135446Strhodes	 * The caller must be holding the adb lock.
1497135446Strhodes	 */
1498135446Strhodes	if (adb->shutting_down) {
1499135446Strhodes		/*
1500135446Strhodes		 * If there aren't any external references either, we're
1501135446Strhodes		 * done.  Send the control event to initiate shutdown.
1502135446Strhodes		 */
1503193149Sdougb		INSIST(!adb->cevent_sent);      /* Sanity check. */
1504135446Strhodes		event = &adb->cevent;
1505135446Strhodes		isc_task_send(adb->task, &event);
1506135446Strhodes		adb->cevent_sent = ISC_TRUE;
1507135446Strhodes	}
1508135446Strhodes}
1509135446Strhodes
1510135446Strhodesstatic inline isc_boolean_t
1511135446Strhodesdec_adb_irefcnt(dns_adb_t *adb) {
1512135446Strhodes	isc_event_t *event;
1513135446Strhodes	isc_task_t *etask;
1514135446Strhodes	isc_boolean_t result = ISC_FALSE;
1515135446Strhodes
1516135446Strhodes	LOCK(&adb->reflock);
1517135446Strhodes
1518135446Strhodes	INSIST(adb->irefcnt > 0);
1519135446Strhodes	adb->irefcnt--;
1520135446Strhodes
1521135446Strhodes	if (adb->irefcnt == 0) {
1522135446Strhodes		event = ISC_LIST_HEAD(adb->whenshutdown);
1523135446Strhodes		while (event != NULL) {
1524135446Strhodes			ISC_LIST_UNLINK(adb->whenshutdown, event, ev_link);
1525135446Strhodes			etask = event->ev_sender;
1526135446Strhodes			event->ev_sender = adb;
1527135446Strhodes			isc_task_sendanddetach(&etask, &event);
1528135446Strhodes			event = ISC_LIST_HEAD(adb->whenshutdown);
1529135446Strhodes		}
1530135446Strhodes	}
1531135446Strhodes
1532135446Strhodes	if (adb->irefcnt == 0 && adb->erefcnt == 0)
1533135446Strhodes		result = ISC_TRUE;
1534135446Strhodes	UNLOCK(&adb->reflock);
1535135446Strhodes	return (result);
1536135446Strhodes}
1537135446Strhodes
1538135446Strhodesstatic inline void
1539135446Strhodesinc_adb_irefcnt(dns_adb_t *adb) {
1540135446Strhodes	LOCK(&adb->reflock);
1541135446Strhodes	adb->irefcnt++;
1542135446Strhodes	UNLOCK(&adb->reflock);
1543135446Strhodes}
1544135446Strhodes
1545135446Strhodesstatic inline void
1546135446Strhodesinc_adb_erefcnt(dns_adb_t *adb) {
1547135446Strhodes	LOCK(&adb->reflock);
1548135446Strhodes	adb->erefcnt++;
1549135446Strhodes	UNLOCK(&adb->reflock);
1550135446Strhodes}
1551135446Strhodes
1552135446Strhodesstatic inline void
1553135446Strhodesinc_entry_refcnt(dns_adb_t *adb, dns_adbentry_t *entry, isc_boolean_t lock) {
1554135446Strhodes	int bucket;
1555135446Strhodes
1556135446Strhodes	bucket = entry->lock_bucket;
1557135446Strhodes
1558135446Strhodes	if (lock)
1559135446Strhodes		LOCK(&adb->entrylocks[bucket]);
1560135446Strhodes
1561135446Strhodes	entry->refcnt++;
1562135446Strhodes
1563135446Strhodes	if (lock)
1564135446Strhodes		UNLOCK(&adb->entrylocks[bucket]);
1565135446Strhodes}
1566135446Strhodes
1567135446Strhodesstatic inline isc_boolean_t
1568214586Sdougbdec_entry_refcnt(dns_adb_t *adb, isc_boolean_t overmem, dns_adbentry_t *entry,
1569214586Sdougb		 isc_boolean_t lock)
1570214586Sdougb{
1571135446Strhodes	int bucket;
1572135446Strhodes	isc_boolean_t destroy_entry;
1573135446Strhodes	isc_boolean_t result = ISC_FALSE;
1574135446Strhodes
1575135446Strhodes	bucket = entry->lock_bucket;
1576135446Strhodes
1577135446Strhodes	if (lock)
1578135446Strhodes		LOCK(&adb->entrylocks[bucket]);
1579135446Strhodes
1580135446Strhodes	INSIST(entry->refcnt > 0);
1581135446Strhodes	entry->refcnt--;
1582135446Strhodes
1583135446Strhodes	destroy_entry = ISC_FALSE;
1584135446Strhodes	if (entry->refcnt == 0 &&
1585214586Sdougb	    (adb->entry_sd[bucket] || entry->expires == 0 || overmem ||
1586193149Sdougb	     (entry->flags & ENTRY_IS_DEAD) != 0)) {
1587135446Strhodes		destroy_entry = ISC_TRUE;
1588135446Strhodes		result = unlink_entry(adb, entry);
1589135446Strhodes	}
1590135446Strhodes
1591135446Strhodes	if (lock)
1592135446Strhodes		UNLOCK(&adb->entrylocks[bucket]);
1593135446Strhodes
1594135446Strhodes	if (!destroy_entry)
1595135446Strhodes		return (result);
1596135446Strhodes
1597135446Strhodes	entry->lock_bucket = DNS_ADB_INVALIDBUCKET;
1598135446Strhodes
1599135446Strhodes	free_adbentry(adb, &entry);
1600135446Strhodes	if (result)
1601193149Sdougb		result = dec_adb_irefcnt(adb);
1602135446Strhodes
1603135446Strhodes	return (result);
1604135446Strhodes}
1605135446Strhodes
1606135446Strhodesstatic inline dns_adbname_t *
1607135446Strhodesnew_adbname(dns_adb_t *adb, dns_name_t *dnsname) {
1608135446Strhodes	dns_adbname_t *name;
1609135446Strhodes
1610135446Strhodes	name = isc_mempool_get(adb->nmp);
1611135446Strhodes	if (name == NULL)
1612135446Strhodes		return (NULL);
1613135446Strhodes
1614135446Strhodes	dns_name_init(&name->name, NULL);
1615135446Strhodes	if (dns_name_dup(dnsname, adb->mctx, &name->name) != ISC_R_SUCCESS) {
1616135446Strhodes		isc_mempool_put(adb->nmp, name);
1617135446Strhodes		return (NULL);
1618135446Strhodes	}
1619135446Strhodes	dns_name_init(&name->target, NULL);
1620135446Strhodes	name->magic = DNS_ADBNAME_MAGIC;
1621135446Strhodes	name->adb = adb;
1622135446Strhodes	name->partial_result = 0;
1623135446Strhodes	name->flags = 0;
1624135446Strhodes	name->expire_v4 = INT_MAX;
1625135446Strhodes	name->expire_v6 = INT_MAX;
1626135446Strhodes	name->expire_target = INT_MAX;
1627135446Strhodes	name->chains = 0;
1628135446Strhodes	name->lock_bucket = DNS_ADB_INVALIDBUCKET;
1629135446Strhodes	ISC_LIST_INIT(name->v4);
1630135446Strhodes	ISC_LIST_INIT(name->v6);
1631135446Strhodes	name->fetch_a = NULL;
1632135446Strhodes	name->fetch_aaaa = NULL;
1633135446Strhodes	name->fetch_err = FIND_ERR_UNEXPECTED;
1634135446Strhodes	name->fetch6_err = FIND_ERR_UNEXPECTED;
1635135446Strhodes	ISC_LIST_INIT(name->finds);
1636135446Strhodes	ISC_LINK_INIT(name, plink);
1637135446Strhodes
1638224092Sdougb	LOCK(&adb->namescntlock);
1639224092Sdougb	adb->namescnt++;
1640245163Serwin	if (!adb->grownames_sent && adb->excl != NULL &&
1641245163Serwin	    adb->namescnt > (adb->nnames * 8))
1642245163Serwin	{
1643224092Sdougb		isc_event_t *event = &adb->grownames;
1644224092Sdougb		inc_adb_irefcnt(adb);
1645245163Serwin		isc_task_send(adb->excl, &event);
1646224092Sdougb		adb->grownames_sent = ISC_TRUE;
1647224092Sdougb	}
1648224092Sdougb	UNLOCK(&adb->namescntlock);
1649224092Sdougb
1650135446Strhodes	return (name);
1651135446Strhodes}
1652135446Strhodes
1653135446Strhodesstatic inline void
1654135446Strhodesfree_adbname(dns_adb_t *adb, dns_adbname_t **name) {
1655135446Strhodes	dns_adbname_t *n;
1656135446Strhodes
1657135446Strhodes	INSIST(name != NULL && DNS_ADBNAME_VALID(*name));
1658135446Strhodes	n = *name;
1659135446Strhodes	*name = NULL;
1660135446Strhodes
1661135446Strhodes	INSIST(!NAME_HAS_V4(n));
1662135446Strhodes	INSIST(!NAME_HAS_V6(n));
1663135446Strhodes	INSIST(!NAME_FETCH(n));
1664135446Strhodes	INSIST(ISC_LIST_EMPTY(n->finds));
1665135446Strhodes	INSIST(!ISC_LINK_LINKED(n, plink));
1666135446Strhodes	INSIST(n->lock_bucket == DNS_ADB_INVALIDBUCKET);
1667135446Strhodes	INSIST(n->adb == adb);
1668135446Strhodes
1669135446Strhodes	n->magic = 0;
1670135446Strhodes	dns_name_free(&n->name, adb->mctx);
1671135446Strhodes
1672135446Strhodes	isc_mempool_put(adb->nmp, n);
1673224092Sdougb	LOCK(&adb->namescntlock);
1674224092Sdougb	adb->namescnt--;
1675224092Sdougb	UNLOCK(&adb->namescntlock);
1676135446Strhodes}
1677135446Strhodes
1678135446Strhodesstatic inline dns_adbnamehook_t *
1679135446Strhodesnew_adbnamehook(dns_adb_t *adb, dns_adbentry_t *entry) {
1680135446Strhodes	dns_adbnamehook_t *nh;
1681135446Strhodes
1682135446Strhodes	nh = isc_mempool_get(adb->nhmp);
1683135446Strhodes	if (nh == NULL)
1684135446Strhodes		return (NULL);
1685135446Strhodes
1686135446Strhodes	nh->magic = DNS_ADBNAMEHOOK_MAGIC;
1687135446Strhodes	nh->entry = entry;
1688135446Strhodes	ISC_LINK_INIT(nh, plink);
1689135446Strhodes
1690135446Strhodes	return (nh);
1691135446Strhodes}
1692135446Strhodes
1693135446Strhodesstatic inline void
1694135446Strhodesfree_adbnamehook(dns_adb_t *adb, dns_adbnamehook_t **namehook) {
1695135446Strhodes	dns_adbnamehook_t *nh;
1696135446Strhodes
1697135446Strhodes	INSIST(namehook != NULL && DNS_ADBNAMEHOOK_VALID(*namehook));
1698135446Strhodes	nh = *namehook;
1699135446Strhodes	*namehook = NULL;
1700135446Strhodes
1701135446Strhodes	INSIST(nh->entry == NULL);
1702135446Strhodes	INSIST(!ISC_LINK_LINKED(nh, plink));
1703135446Strhodes
1704135446Strhodes	nh->magic = 0;
1705135446Strhodes	isc_mempool_put(adb->nhmp, nh);
1706135446Strhodes}
1707135446Strhodes
1708170222Sdougbstatic inline dns_adblameinfo_t *
1709170222Sdougbnew_adblameinfo(dns_adb_t *adb, dns_name_t *qname, dns_rdatatype_t qtype) {
1710170222Sdougb	dns_adblameinfo_t *li;
1711135446Strhodes
1712170222Sdougb	li = isc_mempool_get(adb->limp);
1713170222Sdougb	if (li == NULL)
1714135446Strhodes		return (NULL);
1715135446Strhodes
1716170222Sdougb	dns_name_init(&li->qname, NULL);
1717170222Sdougb	if (dns_name_dup(qname, adb->mctx, &li->qname) != ISC_R_SUCCESS) {
1718170222Sdougb		isc_mempool_put(adb->limp, li);
1719135446Strhodes		return (NULL);
1720135446Strhodes	}
1721170222Sdougb	li->magic = DNS_ADBLAMEINFO_MAGIC;
1722170222Sdougb	li->lame_timer = 0;
1723170222Sdougb	li->qtype = qtype;
1724170222Sdougb	ISC_LINK_INIT(li, plink);
1725135446Strhodes
1726170222Sdougb	return (li);
1727135446Strhodes}
1728135446Strhodes
1729135446Strhodesstatic inline void
1730170222Sdougbfree_adblameinfo(dns_adb_t *adb, dns_adblameinfo_t **lameinfo) {
1731170222Sdougb	dns_adblameinfo_t *li;
1732135446Strhodes
1733170222Sdougb	INSIST(lameinfo != NULL && DNS_ADBLAMEINFO_VALID(*lameinfo));
1734170222Sdougb	li = *lameinfo;
1735170222Sdougb	*lameinfo = NULL;
1736135446Strhodes
1737170222Sdougb	INSIST(!ISC_LINK_LINKED(li, plink));
1738135446Strhodes
1739170222Sdougb	dns_name_free(&li->qname, adb->mctx);
1740135446Strhodes
1741170222Sdougb	li->magic = 0;
1742135446Strhodes
1743170222Sdougb	isc_mempool_put(adb->limp, li);
1744135446Strhodes}
1745135446Strhodes
1746135446Strhodesstatic inline dns_adbentry_t *
1747135446Strhodesnew_adbentry(dns_adb_t *adb) {
1748135446Strhodes	dns_adbentry_t *e;
1749135446Strhodes	isc_uint32_t r;
1750135446Strhodes
1751135446Strhodes	e = isc_mempool_get(adb->emp);
1752135446Strhodes	if (e == NULL)
1753135446Strhodes		return (NULL);
1754135446Strhodes
1755135446Strhodes	e->magic = DNS_ADBENTRY_MAGIC;
1756135446Strhodes	e->lock_bucket = DNS_ADB_INVALIDBUCKET;
1757135446Strhodes	e->refcnt = 0;
1758135446Strhodes	e->flags = 0;
1759135446Strhodes	isc_random_get(&r);
1760135446Strhodes	e->srtt = (r & 0x1f) + 1;
1761135446Strhodes	e->expires = 0;
1762170222Sdougb	ISC_LIST_INIT(e->lameinfo);
1763135446Strhodes	ISC_LINK_INIT(e, plink);
1764224092Sdougb	LOCK(&adb->entriescntlock);
1765224092Sdougb	adb->entriescnt++;
1766245163Serwin	if (!adb->growentries_sent && adb->growentries_sent &&
1767245163Serwin	    adb->entriescnt > (adb->nentries * 8))
1768245163Serwin	{
1769224092Sdougb		isc_event_t *event = &adb->growentries;
1770224092Sdougb		inc_adb_irefcnt(adb);
1771224092Sdougb		isc_task_send(adb->task, &event);
1772224092Sdougb		adb->growentries_sent = ISC_TRUE;
1773224092Sdougb	}
1774224092Sdougb	UNLOCK(&adb->entriescntlock);
1775135446Strhodes
1776135446Strhodes	return (e);
1777135446Strhodes}
1778135446Strhodes
1779135446Strhodesstatic inline void
1780135446Strhodesfree_adbentry(dns_adb_t *adb, dns_adbentry_t **entry) {
1781135446Strhodes	dns_adbentry_t *e;
1782170222Sdougb	dns_adblameinfo_t *li;
1783135446Strhodes
1784135446Strhodes	INSIST(entry != NULL && DNS_ADBENTRY_VALID(*entry));
1785135446Strhodes	e = *entry;
1786135446Strhodes	*entry = NULL;
1787135446Strhodes
1788135446Strhodes	INSIST(e->lock_bucket == DNS_ADB_INVALIDBUCKET);
1789135446Strhodes	INSIST(e->refcnt == 0);
1790135446Strhodes	INSIST(!ISC_LINK_LINKED(e, plink));
1791135446Strhodes
1792135446Strhodes	e->magic = 0;
1793135446Strhodes
1794170222Sdougb	li = ISC_LIST_HEAD(e->lameinfo);
1795170222Sdougb	while (li != NULL) {
1796170222Sdougb		ISC_LIST_UNLINK(e->lameinfo, li, plink);
1797170222Sdougb		free_adblameinfo(adb, &li);
1798170222Sdougb		li = ISC_LIST_HEAD(e->lameinfo);
1799135446Strhodes	}
1800135446Strhodes
1801135446Strhodes	isc_mempool_put(adb->emp, e);
1802224092Sdougb	LOCK(&adb->entriescntlock);
1803224092Sdougb	adb->entriescnt--;
1804224092Sdougb	UNLOCK(&adb->entriescntlock);
1805135446Strhodes}
1806135446Strhodes
1807135446Strhodesstatic inline dns_adbfind_t *
1808135446Strhodesnew_adbfind(dns_adb_t *adb) {
1809135446Strhodes	dns_adbfind_t *h;
1810135446Strhodes	isc_result_t result;
1811135446Strhodes
1812135446Strhodes	h = isc_mempool_get(adb->ahmp);
1813135446Strhodes	if (h == NULL)
1814135446Strhodes		return (NULL);
1815135446Strhodes
1816135446Strhodes	/*
1817135446Strhodes	 * Public members.
1818135446Strhodes	 */
1819135446Strhodes	h->magic = 0;
1820135446Strhodes	h->adb = adb;
1821135446Strhodes	h->partial_result = 0;
1822135446Strhodes	h->options = 0;
1823135446Strhodes	h->flags = 0;
1824135446Strhodes	h->result_v4 = ISC_R_UNEXPECTED;
1825135446Strhodes	h->result_v6 = ISC_R_UNEXPECTED;
1826275672Sdelphij	h->qtotal = 0;
1827135446Strhodes	ISC_LINK_INIT(h, publink);
1828135446Strhodes	ISC_LINK_INIT(h, plink);
1829135446Strhodes	ISC_LIST_INIT(h->list);
1830135446Strhodes	h->adbname = NULL;
1831135446Strhodes	h->name_bucket = DNS_ADB_INVALIDBUCKET;
1832135446Strhodes
1833135446Strhodes	/*
1834135446Strhodes	 * private members
1835135446Strhodes	 */
1836135446Strhodes	result = isc_mutex_init(&h->lock);
1837135446Strhodes	if (result != ISC_R_SUCCESS) {
1838135446Strhodes		isc_mempool_put(adb->ahmp, h);
1839135446Strhodes		return (NULL);
1840135446Strhodes	}
1841135446Strhodes
1842135446Strhodes	ISC_EVENT_INIT(&h->event, sizeof(isc_event_t), 0, 0, 0, NULL, NULL,
1843135446Strhodes		       NULL, NULL, h);
1844135446Strhodes
1845135446Strhodes	inc_adb_irefcnt(adb);
1846135446Strhodes	h->magic = DNS_ADBFIND_MAGIC;
1847135446Strhodes	return (h);
1848135446Strhodes}
1849135446Strhodes
1850135446Strhodesstatic inline dns_adbfetch_t *
1851135446Strhodesnew_adbfetch(dns_adb_t *adb) {
1852135446Strhodes	dns_adbfetch_t *f;
1853135446Strhodes
1854135446Strhodes	f = isc_mempool_get(adb->afmp);
1855135446Strhodes	if (f == NULL)
1856135446Strhodes		return (NULL);
1857135446Strhodes
1858135446Strhodes	f->magic = 0;
1859135446Strhodes	f->fetch = NULL;
1860135446Strhodes
1861135446Strhodes	dns_rdataset_init(&f->rdataset);
1862135446Strhodes
1863135446Strhodes	f->magic = DNS_ADBFETCH_MAGIC;
1864135446Strhodes
1865135446Strhodes	return (f);
1866135446Strhodes}
1867135446Strhodes
1868135446Strhodesstatic inline void
1869135446Strhodesfree_adbfetch(dns_adb_t *adb, dns_adbfetch_t **fetch) {
1870135446Strhodes	dns_adbfetch_t *f;
1871135446Strhodes
1872135446Strhodes	INSIST(fetch != NULL && DNS_ADBFETCH_VALID(*fetch));
1873135446Strhodes	f = *fetch;
1874135446Strhodes	*fetch = NULL;
1875135446Strhodes
1876135446Strhodes	f->magic = 0;
1877135446Strhodes
1878135446Strhodes	if (dns_rdataset_isassociated(&f->rdataset))
1879135446Strhodes		dns_rdataset_disassociate(&f->rdataset);
1880135446Strhodes
1881135446Strhodes	isc_mempool_put(adb->afmp, f);
1882135446Strhodes}
1883135446Strhodes
1884135446Strhodesstatic inline isc_boolean_t
1885135446Strhodesfree_adbfind(dns_adb_t *adb, dns_adbfind_t **findp) {
1886135446Strhodes	dns_adbfind_t *find;
1887135446Strhodes
1888135446Strhodes	INSIST(findp != NULL && DNS_ADBFIND_VALID(*findp));
1889135446Strhodes	find = *findp;
1890135446Strhodes	*findp = NULL;
1891135446Strhodes
1892135446Strhodes	INSIST(!FIND_HAS_ADDRS(find));
1893135446Strhodes	INSIST(!ISC_LINK_LINKED(find, publink));
1894135446Strhodes	INSIST(!ISC_LINK_LINKED(find, plink));
1895135446Strhodes	INSIST(find->name_bucket == DNS_ADB_INVALIDBUCKET);
1896135446Strhodes	INSIST(find->adbname == NULL);
1897135446Strhodes
1898135446Strhodes	find->magic = 0;
1899135446Strhodes
1900135446Strhodes	DESTROYLOCK(&find->lock);
1901135446Strhodes	isc_mempool_put(adb->ahmp, find);
1902135446Strhodes	return (dec_adb_irefcnt(adb));
1903135446Strhodes}
1904135446Strhodes
1905135446Strhodes/*
1906135446Strhodes * Copy bits from the entry into the newly allocated addrinfo.  The entry
1907135446Strhodes * must be locked, and the reference count must be bumped up by one
1908135446Strhodes * if this function returns a valid pointer.
1909135446Strhodes */
1910135446Strhodesstatic inline dns_adbaddrinfo_t *
1911135446Strhodesnew_adbaddrinfo(dns_adb_t *adb, dns_adbentry_t *entry, in_port_t port) {
1912135446Strhodes	dns_adbaddrinfo_t *ai;
1913135446Strhodes
1914135446Strhodes	ai = isc_mempool_get(adb->aimp);
1915135446Strhodes	if (ai == NULL)
1916135446Strhodes		return (NULL);
1917135446Strhodes
1918135446Strhodes	ai->magic = DNS_ADBADDRINFO_MAGIC;
1919135446Strhodes	ai->sockaddr = entry->sockaddr;
1920135446Strhodes	isc_sockaddr_setport(&ai->sockaddr, port);
1921135446Strhodes	ai->srtt = entry->srtt;
1922135446Strhodes	ai->flags = entry->flags;
1923135446Strhodes	ai->entry = entry;
1924135446Strhodes	ISC_LINK_INIT(ai, publink);
1925135446Strhodes
1926135446Strhodes	return (ai);
1927135446Strhodes}
1928135446Strhodes
1929135446Strhodesstatic inline void
1930135446Strhodesfree_adbaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **ainfo) {
1931135446Strhodes	dns_adbaddrinfo_t *ai;
1932135446Strhodes
1933135446Strhodes	INSIST(ainfo != NULL && DNS_ADBADDRINFO_VALID(*ainfo));
1934135446Strhodes	ai = *ainfo;
1935135446Strhodes	*ainfo = NULL;
1936135446Strhodes
1937135446Strhodes	INSIST(ai->entry == NULL);
1938135446Strhodes	INSIST(!ISC_LINK_LINKED(ai, publink));
1939135446Strhodes
1940135446Strhodes	ai->magic = 0;
1941135446Strhodes
1942135446Strhodes	isc_mempool_put(adb->aimp, ai);
1943135446Strhodes}
1944135446Strhodes
1945135446Strhodes/*
1946135446Strhodes * Search for the name.  NOTE:  The bucket is kept locked on both
1947135446Strhodes * success and failure, so it must always be unlocked by the caller!
1948135446Strhodes *
1949135446Strhodes * On the first call to this function, *bucketp must be set to
1950135446Strhodes * DNS_ADB_INVALIDBUCKET.
1951135446Strhodes */
1952135446Strhodesstatic inline dns_adbname_t *
1953135446Strhodesfind_name_and_lock(dns_adb_t *adb, dns_name_t *name,
1954135446Strhodes		   unsigned int options, int *bucketp)
1955135446Strhodes{
1956135446Strhodes	dns_adbname_t *adbname;
1957135446Strhodes	int bucket;
1958135446Strhodes
1959224092Sdougb	bucket = dns_name_fullhash(name, ISC_FALSE) % adb->nnames;
1960135446Strhodes
1961135446Strhodes	if (*bucketp == DNS_ADB_INVALIDBUCKET) {
1962135446Strhodes		LOCK(&adb->namelocks[bucket]);
1963135446Strhodes		*bucketp = bucket;
1964135446Strhodes	} else if (*bucketp != bucket) {
1965135446Strhodes		UNLOCK(&adb->namelocks[*bucketp]);
1966135446Strhodes		LOCK(&adb->namelocks[bucket]);
1967135446Strhodes		*bucketp = bucket;
1968135446Strhodes	}
1969135446Strhodes
1970135446Strhodes	adbname = ISC_LIST_HEAD(adb->names[bucket]);
1971135446Strhodes	while (adbname != NULL) {
1972135446Strhodes		if (!NAME_DEAD(adbname)) {
1973135446Strhodes			if (dns_name_equal(name, &adbname->name)
1974135446Strhodes			    && GLUEHINT_OK(adbname, options)
1975135446Strhodes			    && STARTATZONE_MATCHES(adbname, options))
1976135446Strhodes				return (adbname);
1977135446Strhodes		}
1978135446Strhodes		adbname = ISC_LIST_NEXT(adbname, plink);
1979135446Strhodes	}
1980135446Strhodes
1981135446Strhodes	return (NULL);
1982135446Strhodes}
1983135446Strhodes
1984135446Strhodes/*
1985135446Strhodes * Search for the address.  NOTE:  The bucket is kept locked on both
1986135446Strhodes * success and failure, so it must always be unlocked by the caller.
1987135446Strhodes *
1988135446Strhodes * On the first call to this function, *bucketp must be set to
1989135446Strhodes * DNS_ADB_INVALIDBUCKET.  This will cause a lock to occur.  On
1990135446Strhodes * later calls (within the same "lock path") it can be left alone, so
1991135446Strhodes * if this function is called multiple times locking is only done if
1992135446Strhodes * the bucket changes.
1993135446Strhodes */
1994135446Strhodesstatic inline dns_adbentry_t *
1995193149Sdougbfind_entry_and_lock(dns_adb_t *adb, isc_sockaddr_t *addr, int *bucketp,
1996193149Sdougb	isc_stdtime_t now)
1997193149Sdougb{
1998193149Sdougb	dns_adbentry_t *entry, *entry_next;
1999135446Strhodes	int bucket;
2000135446Strhodes
2001224092Sdougb	bucket = isc_sockaddr_hash(addr, ISC_TRUE) % adb->nentries;
2002135446Strhodes
2003135446Strhodes	if (*bucketp == DNS_ADB_INVALIDBUCKET) {
2004135446Strhodes		LOCK(&adb->entrylocks[bucket]);
2005135446Strhodes		*bucketp = bucket;
2006135446Strhodes	} else if (*bucketp != bucket) {
2007135446Strhodes		UNLOCK(&adb->entrylocks[*bucketp]);
2008135446Strhodes		LOCK(&adb->entrylocks[bucket]);
2009135446Strhodes		*bucketp = bucket;
2010135446Strhodes	}
2011135446Strhodes
2012193149Sdougb	/* Search the list, while cleaning up expired entries. */
2013193149Sdougb	for (entry = ISC_LIST_HEAD(adb->entries[bucket]);
2014193149Sdougb	     entry != NULL;
2015193149Sdougb	     entry = entry_next) {
2016193149Sdougb		entry_next = ISC_LIST_NEXT(entry, plink);
2017193149Sdougb		(void)check_expire_entry(adb, &entry, now);
2018193149Sdougb		if (entry != NULL &&
2019193149Sdougb		    isc_sockaddr_equal(addr, &entry->sockaddr)) {
2020193149Sdougb			ISC_LIST_UNLINK(adb->entries[bucket], entry, plink);
2021193149Sdougb			ISC_LIST_PREPEND(adb->entries[bucket], entry, plink);
2022135446Strhodes			return (entry);
2023193149Sdougb		}
2024135446Strhodes	}
2025135446Strhodes
2026135446Strhodes	return (NULL);
2027135446Strhodes}
2028135446Strhodes
2029135446Strhodes/*
2030135446Strhodes * Entry bucket MUST be locked!
2031135446Strhodes */
2032135446Strhodesstatic isc_boolean_t
2033170222Sdougbentry_is_lame(dns_adb_t *adb, dns_adbentry_t *entry, dns_name_t *qname,
2034170222Sdougb	      dns_rdatatype_t qtype, isc_stdtime_t now)
2035135446Strhodes{
2036170222Sdougb	dns_adblameinfo_t *li, *next_li;
2037135446Strhodes	isc_boolean_t is_bad;
2038135446Strhodes
2039135446Strhodes	is_bad = ISC_FALSE;
2040135446Strhodes
2041170222Sdougb	li = ISC_LIST_HEAD(entry->lameinfo);
2042170222Sdougb	if (li == NULL)
2043135446Strhodes		return (ISC_FALSE);
2044170222Sdougb	while (li != NULL) {
2045170222Sdougb		next_li = ISC_LIST_NEXT(li, plink);
2046135446Strhodes
2047135446Strhodes		/*
2048135446Strhodes		 * Has the entry expired?
2049135446Strhodes		 */
2050170222Sdougb		if (li->lame_timer < now) {
2051170222Sdougb			ISC_LIST_UNLINK(entry->lameinfo, li, plink);
2052170222Sdougb			free_adblameinfo(adb, &li);
2053135446Strhodes		}
2054135446Strhodes
2055135446Strhodes		/*
2056135446Strhodes		 * Order tests from least to most expensive.
2057170222Sdougb		 *
2058170222Sdougb		 * We do not break out of the main loop here as
2059170222Sdougb		 * we use the loop for house keeping.
2060135446Strhodes		 */
2061170222Sdougb		if (li != NULL && !is_bad && li->qtype == qtype &&
2062170222Sdougb		    dns_name_equal(qname, &li->qname))
2063170222Sdougb			is_bad = ISC_TRUE;
2064135446Strhodes
2065170222Sdougb		li = next_li;
2066135446Strhodes	}
2067135446Strhodes
2068135446Strhodes	return (is_bad);
2069135446Strhodes}
2070135446Strhodes
2071135446Strhodesstatic void
2072170222Sdougbcopy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, dns_name_t *qname,
2073170222Sdougb		    dns_rdatatype_t qtype, dns_adbname_t *name,
2074170222Sdougb		    isc_stdtime_t now)
2075135446Strhodes{
2076135446Strhodes	dns_adbnamehook_t *namehook;
2077135446Strhodes	dns_adbaddrinfo_t *addrinfo;
2078135446Strhodes	dns_adbentry_t *entry;
2079135446Strhodes	int bucket;
2080135446Strhodes
2081135446Strhodes	bucket = DNS_ADB_INVALIDBUCKET;
2082135446Strhodes
2083135446Strhodes	if (find->options & DNS_ADBFIND_INET) {
2084135446Strhodes		namehook = ISC_LIST_HEAD(name->v4);
2085135446Strhodes		while (namehook != NULL) {
2086135446Strhodes			entry = namehook->entry;
2087135446Strhodes			bucket = entry->lock_bucket;
2088254402Serwin			INSIST(bucket != DNS_ADB_INVALIDBUCKET);
2089135446Strhodes			LOCK(&adb->entrylocks[bucket]);
2090135446Strhodes
2091135446Strhodes			if (!FIND_RETURNLAME(find)
2092170222Sdougb			    && entry_is_lame(adb, entry, qname, qtype, now)) {
2093135446Strhodes				find->options |= DNS_ADBFIND_LAMEPRUNED;
2094135446Strhodes				goto nextv4;
2095135446Strhodes			}
2096135446Strhodes			addrinfo = new_adbaddrinfo(adb, entry, find->port);
2097135446Strhodes			if (addrinfo == NULL) {
2098135446Strhodes				find->partial_result |= DNS_ADBFIND_INET;
2099135446Strhodes				goto out;
2100135446Strhodes			}
2101135446Strhodes			/*
2102135446Strhodes			 * Found a valid entry.  Add it to the find's list.
2103135446Strhodes			 */
2104135446Strhodes			inc_entry_refcnt(adb, entry, ISC_FALSE);
2105135446Strhodes			ISC_LIST_APPEND(find->list, addrinfo, publink);
2106135446Strhodes			addrinfo = NULL;
2107135446Strhodes		nextv4:
2108135446Strhodes			UNLOCK(&adb->entrylocks[bucket]);
2109135446Strhodes			bucket = DNS_ADB_INVALIDBUCKET;
2110135446Strhodes			namehook = ISC_LIST_NEXT(namehook, plink);
2111135446Strhodes		}
2112135446Strhodes	}
2113135446Strhodes
2114135446Strhodes	if (find->options & DNS_ADBFIND_INET6) {
2115135446Strhodes		namehook = ISC_LIST_HEAD(name->v6);
2116135446Strhodes		while (namehook != NULL) {
2117135446Strhodes			entry = namehook->entry;
2118135446Strhodes			bucket = entry->lock_bucket;
2119254402Serwin			INSIST(bucket != DNS_ADB_INVALIDBUCKET);
2120135446Strhodes			LOCK(&adb->entrylocks[bucket]);
2121135446Strhodes
2122186462Sdougb			if (!FIND_RETURNLAME(find)
2123186462Sdougb			    && entry_is_lame(adb, entry, qname, qtype, now)) {
2124186462Sdougb				find->options |= DNS_ADBFIND_LAMEPRUNED;
2125135446Strhodes				goto nextv6;
2126186462Sdougb			}
2127135446Strhodes			addrinfo = new_adbaddrinfo(adb, entry, find->port);
2128135446Strhodes			if (addrinfo == NULL) {
2129135446Strhodes				find->partial_result |= DNS_ADBFIND_INET6;
2130135446Strhodes				goto out;
2131135446Strhodes			}
2132135446Strhodes			/*
2133135446Strhodes			 * Found a valid entry.  Add it to the find's list.
2134135446Strhodes			 */
2135135446Strhodes			inc_entry_refcnt(adb, entry, ISC_FALSE);
2136135446Strhodes			ISC_LIST_APPEND(find->list, addrinfo, publink);
2137135446Strhodes			addrinfo = NULL;
2138135446Strhodes		nextv6:
2139135446Strhodes			UNLOCK(&adb->entrylocks[bucket]);
2140135446Strhodes			bucket = DNS_ADB_INVALIDBUCKET;
2141135446Strhodes			namehook = ISC_LIST_NEXT(namehook, plink);
2142135446Strhodes		}
2143135446Strhodes	}
2144135446Strhodes
2145135446Strhodes out:
2146135446Strhodes	if (bucket != DNS_ADB_INVALIDBUCKET)
2147135446Strhodes		UNLOCK(&adb->entrylocks[bucket]);
2148135446Strhodes}
2149135446Strhodes
2150135446Strhodesstatic void
2151135446Strhodesshutdown_task(isc_task_t *task, isc_event_t *ev) {
2152135446Strhodes	dns_adb_t *adb;
2153135446Strhodes
2154135446Strhodes	UNUSED(task);
2155135446Strhodes
2156135446Strhodes	adb = ev->ev_arg;
2157135446Strhodes	INSIST(DNS_ADB_VALID(adb));
2158135446Strhodes
2159193149Sdougb	isc_event_free(&ev);
2160135446Strhodes	/*
2161186462Sdougb	 * Wait for lock around check_exit() call to be released.
2162186462Sdougb	 */
2163186462Sdougb	LOCK(&adb->lock);
2164135446Strhodes	UNLOCK(&adb->lock);
2165135446Strhodes	destroy(adb);
2166135446Strhodes}
2167135446Strhodes
2168135446Strhodes/*
2169135446Strhodes * Name bucket must be locked; adb may be locked; no other locks held.
2170135446Strhodes */
2171135446Strhodesstatic isc_boolean_t
2172135446Strhodescheck_expire_name(dns_adbname_t **namep, isc_stdtime_t now) {
2173135446Strhodes	dns_adbname_t *name;
2174153816Sdougb	isc_boolean_t result = ISC_FALSE;
2175135446Strhodes
2176135446Strhodes	INSIST(namep != NULL && DNS_ADBNAME_VALID(*namep));
2177135446Strhodes	name = *namep;
2178135446Strhodes
2179135446Strhodes	if (NAME_HAS_V4(name) || NAME_HAS_V6(name))
2180135446Strhodes		return (result);
2181135446Strhodes	if (NAME_FETCH(name))
2182135446Strhodes		return (result);
2183135446Strhodes	if (!EXPIRE_OK(name->expire_v4, now))
2184135446Strhodes		return (result);
2185135446Strhodes	if (!EXPIRE_OK(name->expire_v6, now))
2186135446Strhodes		return (result);
2187135446Strhodes	if (!EXPIRE_OK(name->expire_target, now))
2188135446Strhodes		return (result);
2189135446Strhodes
2190135446Strhodes	/*
2191135446Strhodes	 * The name is empty.  Delete it.
2192135446Strhodes	 */
2193135446Strhodes	result = kill_name(&name, DNS_EVENT_ADBEXPIRED);
2194135446Strhodes	*namep = NULL;
2195135446Strhodes
2196135446Strhodes	/*
2197135446Strhodes	 * Our caller, or one of its callers, will be calling check_exit() at
2198135446Strhodes	 * some point, so we don't need to do it here.
2199135446Strhodes	 */
2200135446Strhodes	return (result);
2201135446Strhodes}
2202135446Strhodes
2203193149Sdougb/*%
2204193149Sdougb * Examine the tail entry of the LRU list to see if it expires or is stale
2205193149Sdougb * (unused for some period); if so, the name entry will be freed.  If the ADB
2206193149Sdougb * is in the overmem condition, the tail and the next to tail entries
2207193149Sdougb * will be unconditionally removed (unless they have an outstanding fetch).
2208193149Sdougb * We don't care about a race on 'overmem' at the risk of causing some
2209193149Sdougb * collateral damage or a small delay in starting cleanup, so we don't bother
2210193149Sdougb * to lock ADB (if it's not locked).
2211193149Sdougb *
2212193149Sdougb * Name bucket must be locked; adb may be locked; no other locks held.
2213193149Sdougb */
2214193149Sdougbstatic void
2215193149Sdougbcheck_stale_name(dns_adb_t *adb, int bucket, isc_stdtime_t now) {
2216193149Sdougb	int victims, max_victims;
2217193149Sdougb	dns_adbname_t *victim, *next_victim;
2218214586Sdougb	isc_boolean_t overmem = isc_mem_isovermem(adb->mctx);
2219193149Sdougb	int scans = 0;
2220193149Sdougb
2221193149Sdougb	INSIST(bucket != DNS_ADB_INVALIDBUCKET);
2222193149Sdougb
2223193149Sdougb	max_victims = overmem ? 2 : 1;
2224193149Sdougb
2225193149Sdougb	/*
2226193149Sdougb	 * We limit the number of scanned entries to 10 (arbitrary choice)
2227193149Sdougb	 * in order to avoid examining too many entries when there are many
2228193149Sdougb	 * tail entries that have fetches (this should be rare, but could
2229193149Sdougb	 * happen).
2230193149Sdougb	 */
2231193149Sdougb	victim = ISC_LIST_TAIL(adb->names[bucket]);
2232193149Sdougb	for (victims = 0;
2233193149Sdougb	     victim != NULL && victims < max_victims && scans < 10;
2234193149Sdougb	     victim = next_victim) {
2235193149Sdougb		INSIST(!NAME_DEAD(victim));
2236193149Sdougb		scans++;
2237193149Sdougb		next_victim = ISC_LIST_PREV(victim, plink);
2238225361Sdougb		(void)check_expire_name(&victim, now);
2239193149Sdougb		if (victim == NULL) {
2240193149Sdougb			victims++;
2241193149Sdougb			goto next;
2242193149Sdougb		}
2243193149Sdougb
2244193149Sdougb		if (!NAME_FETCH(victim) &&
2245193149Sdougb		    (overmem || victim->last_used + ADB_STALE_MARGIN <= now)) {
2246193149Sdougb			RUNTIME_CHECK(kill_name(&victim,
2247193149Sdougb						DNS_EVENT_ADBCANCELED) ==
2248193149Sdougb				      ISC_FALSE);
2249193149Sdougb			victims++;
2250193149Sdougb		}
2251193149Sdougb
2252193149Sdougb	next:
2253193149Sdougb		if (!overmem)
2254193149Sdougb			break;
2255193149Sdougb	}
2256193149Sdougb}
2257193149Sdougb
2258135446Strhodes/*
2259135446Strhodes * Entry bucket must be locked; adb may be locked; no other locks held.
2260135446Strhodes */
2261135446Strhodesstatic isc_boolean_t
2262135446Strhodescheck_expire_entry(dns_adb_t *adb, dns_adbentry_t **entryp, isc_stdtime_t now)
2263135446Strhodes{
2264135446Strhodes	dns_adbentry_t *entry;
2265135446Strhodes	isc_boolean_t result = ISC_FALSE;
2266135446Strhodes
2267135446Strhodes	INSIST(entryp != NULL && DNS_ADBENTRY_VALID(*entryp));
2268135446Strhodes	entry = *entryp;
2269135446Strhodes
2270135446Strhodes	if (entry->refcnt != 0)
2271135446Strhodes		return (result);
2272135446Strhodes
2273193149Sdougb	if (entry->expires == 0 || entry->expires > now)
2274135446Strhodes		return (result);
2275135446Strhodes
2276135446Strhodes	/*
2277135446Strhodes	 * The entry is not in use.  Delete it.
2278135446Strhodes	 */
2279135446Strhodes	DP(DEF_LEVEL, "killing entry %p", entry);
2280135446Strhodes	INSIST(ISC_LINK_LINKED(entry, plink));
2281135446Strhodes	result = unlink_entry(adb, entry);
2282135446Strhodes	free_adbentry(adb, &entry);
2283135446Strhodes	if (result)
2284135446Strhodes		dec_adb_irefcnt(adb);
2285135446Strhodes	*entryp = NULL;
2286135446Strhodes	return (result);
2287135446Strhodes}
2288135446Strhodes
2289135446Strhodes/*
2290135446Strhodes * ADB must be locked, and no other locks held.
2291135446Strhodes */
2292135446Strhodesstatic isc_boolean_t
2293135446Strhodescleanup_names(dns_adb_t *adb, int bucket, isc_stdtime_t now) {
2294135446Strhodes	dns_adbname_t *name;
2295135446Strhodes	dns_adbname_t *next_name;
2296153816Sdougb	isc_boolean_t result = ISC_FALSE;
2297135446Strhodes
2298135446Strhodes	DP(CLEAN_LEVEL, "cleaning name bucket %d", bucket);
2299135446Strhodes
2300135446Strhodes	LOCK(&adb->namelocks[bucket]);
2301135446Strhodes	if (adb->name_sd[bucket]) {
2302135446Strhodes		UNLOCK(&adb->namelocks[bucket]);
2303135446Strhodes		return (result);
2304135446Strhodes	}
2305135446Strhodes
2306135446Strhodes	name = ISC_LIST_HEAD(adb->names[bucket]);
2307135446Strhodes	while (name != NULL) {
2308135446Strhodes		next_name = ISC_LIST_NEXT(name, plink);
2309135446Strhodes		INSIST(result == ISC_FALSE);
2310193149Sdougb		result = check_expire_namehooks(name, now);
2311135446Strhodes		if (!result)
2312135446Strhodes			result = check_expire_name(&name, now);
2313135446Strhodes		name = next_name;
2314135446Strhodes	}
2315135446Strhodes	UNLOCK(&adb->namelocks[bucket]);
2316135446Strhodes	return (result);
2317135446Strhodes}
2318135446Strhodes
2319135446Strhodes/*
2320135446Strhodes * ADB must be locked, and no other locks held.
2321135446Strhodes */
2322135446Strhodesstatic isc_boolean_t
2323135446Strhodescleanup_entries(dns_adb_t *adb, int bucket, isc_stdtime_t now) {
2324135446Strhodes	dns_adbentry_t *entry, *next_entry;
2325135446Strhodes	isc_boolean_t result = ISC_FALSE;
2326135446Strhodes
2327135446Strhodes	DP(CLEAN_LEVEL, "cleaning entry bucket %d", bucket);
2328135446Strhodes
2329135446Strhodes	LOCK(&adb->entrylocks[bucket]);
2330135446Strhodes	entry = ISC_LIST_HEAD(adb->entries[bucket]);
2331135446Strhodes	while (entry != NULL) {
2332135446Strhodes		next_entry = ISC_LIST_NEXT(entry, plink);
2333135446Strhodes		INSIST(result == ISC_FALSE);
2334135446Strhodes		result = check_expire_entry(adb, &entry, now);
2335135446Strhodes		entry = next_entry;
2336135446Strhodes	}
2337135446Strhodes	UNLOCK(&adb->entrylocks[bucket]);
2338135446Strhodes	return (result);
2339135446Strhodes}
2340135446Strhodes
2341135446Strhodesstatic void
2342135446Strhodesdestroy(dns_adb_t *adb) {
2343135446Strhodes	adb->magic = 0;
2344135446Strhodes
2345135446Strhodes	isc_task_detach(&adb->task);
2346254402Serwin	if (adb->excl != NULL)
2347254402Serwin		isc_task_detach(&adb->excl);
2348135446Strhodes
2349135446Strhodes	isc_mempool_destroy(&adb->nmp);
2350135446Strhodes	isc_mempool_destroy(&adb->nhmp);
2351170222Sdougb	isc_mempool_destroy(&adb->limp);
2352135446Strhodes	isc_mempool_destroy(&adb->emp);
2353135446Strhodes	isc_mempool_destroy(&adb->ahmp);
2354135446Strhodes	isc_mempool_destroy(&adb->aimp);
2355135446Strhodes	isc_mempool_destroy(&adb->afmp);
2356135446Strhodes
2357224092Sdougb	DESTROYMUTEXBLOCK(adb->entrylocks, adb->nentries);
2358224092Sdougb	isc_mem_put(adb->mctx, adb->entries,
2359224092Sdougb		    sizeof(*adb->entries) * adb->nentries);
2360224092Sdougb	isc_mem_put(adb->mctx, adb->deadentries,
2361224092Sdougb		    sizeof(*adb->deadentries) * adb->nentries);
2362224092Sdougb	isc_mem_put(adb->mctx, adb->entrylocks,
2363224092Sdougb		    sizeof(*adb->entrylocks) * adb->nentries);
2364224092Sdougb	isc_mem_put(adb->mctx, adb->entry_sd,
2365224092Sdougb		    sizeof(*adb->entry_sd) * adb->nentries);
2366224092Sdougb	isc_mem_put(adb->mctx, adb->entry_refcnt,
2367224092Sdougb		    sizeof(*adb->entry_refcnt) * adb->nentries);
2368135446Strhodes
2369224092Sdougb	DESTROYMUTEXBLOCK(adb->namelocks, adb->nnames);
2370224092Sdougb	isc_mem_put(adb->mctx, adb->names,
2371224092Sdougb		    sizeof(*adb->names) * adb->nnames);
2372224092Sdougb	isc_mem_put(adb->mctx, adb->deadnames,
2373224092Sdougb		    sizeof(*adb->deadnames) * adb->nnames);
2374224092Sdougb	isc_mem_put(adb->mctx, adb->namelocks,
2375224092Sdougb		    sizeof(*adb->namelocks) * adb->nnames);
2376224092Sdougb	isc_mem_put(adb->mctx, adb->name_sd,
2377224092Sdougb		    sizeof(*adb->name_sd) * adb->nnames);
2378224092Sdougb	isc_mem_put(adb->mctx, adb->name_refcnt,
2379224092Sdougb		    sizeof(*adb->name_refcnt) * adb->nnames);
2380224092Sdougb
2381135446Strhodes	DESTROYLOCK(&adb->reflock);
2382135446Strhodes	DESTROYLOCK(&adb->lock);
2383135446Strhodes	DESTROYLOCK(&adb->mplock);
2384186462Sdougb	DESTROYLOCK(&adb->overmemlock);
2385224092Sdougb	DESTROYLOCK(&adb->entriescntlock);
2386224092Sdougb	DESTROYLOCK(&adb->namescntlock);
2387135446Strhodes
2388135446Strhodes	isc_mem_putanddetach(&adb->mctx, adb, sizeof(dns_adb_t));
2389135446Strhodes}
2390135446Strhodes
2391135446Strhodes
2392135446Strhodes/*
2393135446Strhodes * Public functions.
2394135446Strhodes */
2395135446Strhodes
2396135446Strhodesisc_result_t
2397135446Strhodesdns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_timermgr_t *timermgr,
2398135446Strhodes	       isc_taskmgr_t *taskmgr, dns_adb_t **newadb)
2399135446Strhodes{
2400135446Strhodes	dns_adb_t *adb;
2401135446Strhodes	isc_result_t result;
2402224092Sdougb	unsigned int i;
2403135446Strhodes
2404135446Strhodes	REQUIRE(mem != NULL);
2405135446Strhodes	REQUIRE(view != NULL);
2406193149Sdougb	REQUIRE(timermgr != NULL); /* this is actually unused */
2407135446Strhodes	REQUIRE(taskmgr != NULL);
2408135446Strhodes	REQUIRE(newadb != NULL && *newadb == NULL);
2409135446Strhodes
2410193149Sdougb	UNUSED(timermgr);
2411193149Sdougb
2412135446Strhodes	adb = isc_mem_get(mem, sizeof(dns_adb_t));
2413135446Strhodes	if (adb == NULL)
2414135446Strhodes		return (ISC_R_NOMEMORY);
2415135446Strhodes
2416135446Strhodes	/*
2417135446Strhodes	 * Initialize things here that cannot fail, and especially things
2418135446Strhodes	 * that must be NULL for the error return to work properly.
2419135446Strhodes	 */
2420135446Strhodes	adb->magic = 0;
2421135446Strhodes	adb->erefcnt = 1;
2422135446Strhodes	adb->irefcnt = 0;
2423135446Strhodes	adb->nmp = NULL;
2424135446Strhodes	adb->nhmp = NULL;
2425170222Sdougb	adb->limp = NULL;
2426135446Strhodes	adb->emp = NULL;
2427135446Strhodes	adb->ahmp = NULL;
2428135446Strhodes	adb->aimp = NULL;
2429135446Strhodes	adb->afmp = NULL;
2430135446Strhodes	adb->task = NULL;
2431245163Serwin	adb->excl = NULL;
2432135446Strhodes	adb->mctx = NULL;
2433135446Strhodes	adb->view = view;
2434135446Strhodes	adb->taskmgr = taskmgr;
2435135446Strhodes	adb->next_cleanbucket = 0;
2436135446Strhodes	ISC_EVENT_INIT(&adb->cevent, sizeof(adb->cevent), 0, NULL,
2437135446Strhodes		       DNS_EVENT_ADBCONTROL, shutdown_task, adb,
2438135446Strhodes		       adb, NULL, NULL);
2439135446Strhodes	adb->cevent_sent = ISC_FALSE;
2440135446Strhodes	adb->shutting_down = ISC_FALSE;
2441135446Strhodes	ISC_LIST_INIT(adb->whenshutdown);
2442135446Strhodes
2443224092Sdougb	adb->nentries = nbuckets[0];
2444224092Sdougb	adb->entriescnt = 0;
2445224092Sdougb	adb->entries = NULL;
2446224092Sdougb	adb->deadentries = NULL;
2447224092Sdougb	adb->entry_sd = NULL;
2448224092Sdougb	adb->entry_refcnt = NULL;
2449224092Sdougb	adb->entrylocks = NULL;
2450224092Sdougb	ISC_EVENT_INIT(&adb->growentries, sizeof(adb->growentries), 0, NULL,
2451224092Sdougb		       DNS_EVENT_ADBGROWENTRIES, grow_entries, adb,
2452224092Sdougb		       adb, NULL, NULL);
2453224092Sdougb	adb->growentries_sent = ISC_FALSE;
2454224092Sdougb
2455224092Sdougb	adb->nnames = nbuckets[0];
2456224092Sdougb	adb->namescnt = 0;
2457224092Sdougb	adb->names = NULL;
2458224092Sdougb	adb->deadnames = NULL;
2459224092Sdougb	adb->name_sd = NULL;
2460224092Sdougb	adb->name_refcnt = NULL;
2461224092Sdougb	adb->namelocks = NULL;
2462224092Sdougb	ISC_EVENT_INIT(&adb->grownames, sizeof(adb->grownames), 0, NULL,
2463224092Sdougb		       DNS_EVENT_ADBGROWNAMES, grow_names, adb,
2464224092Sdougb		       adb, NULL, NULL);
2465224092Sdougb	adb->grownames_sent = ISC_FALSE;
2466224092Sdougb
2467245163Serwin	result = isc_taskmgr_excltask(adb->taskmgr, &adb->excl);
2468245163Serwin	if (result != ISC_R_SUCCESS) {
2469245163Serwin		DP(ISC_LOG_INFO, "adb: task-exclusive mode unavailable, "
2470245163Serwin				 "intializing table sizes to %u\n",
2471245163Serwin				 nbuckets[11]);
2472245163Serwin		adb->nentries = nbuckets[11];
2473245163Serwin		adb->nnames= nbuckets[11];
2474245163Serwin
2475245163Serwin	}
2476245163Serwin
2477135446Strhodes	isc_mem_attach(mem, &adb->mctx);
2478135446Strhodes
2479135446Strhodes	result = isc_mutex_init(&adb->lock);
2480135446Strhodes	if (result != ISC_R_SUCCESS)
2481135446Strhodes		goto fail0b;
2482135446Strhodes
2483135446Strhodes	result = isc_mutex_init(&adb->mplock);
2484135446Strhodes	if (result != ISC_R_SUCCESS)
2485135446Strhodes		goto fail0c;
2486135446Strhodes
2487135446Strhodes	result = isc_mutex_init(&adb->reflock);
2488135446Strhodes	if (result != ISC_R_SUCCESS)
2489135446Strhodes		goto fail0d;
2490135446Strhodes
2491186462Sdougb	result = isc_mutex_init(&adb->overmemlock);
2492186462Sdougb	if (result != ISC_R_SUCCESS)
2493186462Sdougb		goto fail0e;
2494186462Sdougb
2495224092Sdougb	result = isc_mutex_init(&adb->entriescntlock);
2496224092Sdougb	if (result != ISC_R_SUCCESS)
2497224092Sdougb		goto fail0f;
2498224092Sdougb
2499224092Sdougb	result = isc_mutex_init(&adb->namescntlock);
2500224092Sdougb	if (result != ISC_R_SUCCESS)
2501224092Sdougb		goto fail0g;
2502224092Sdougb
2503224092Sdougb#define ALLOCENTRY(adb, el) \
2504224092Sdougb	do { \
2505224092Sdougb		(adb)->el = isc_mem_get((adb)->mctx, \
2506224092Sdougb				     sizeof(*(adb)->el) * (adb)->nentries); \
2507224092Sdougb		if ((adb)->el == NULL) { \
2508224092Sdougb			result = ISC_R_NOMEMORY; \
2509224092Sdougb			goto fail1; \
2510224092Sdougb		}\
2511224092Sdougb	} while (0)
2512224092Sdougb	ALLOCENTRY(adb, entries);
2513224092Sdougb	ALLOCENTRY(adb, deadentries);
2514224092Sdougb	ALLOCENTRY(adb, entrylocks);
2515224092Sdougb	ALLOCENTRY(adb, entry_sd);
2516224092Sdougb	ALLOCENTRY(adb, entry_refcnt);
2517224092Sdougb#undef ALLOCENTRY
2518224092Sdougb
2519224092Sdougb#define ALLOCNAME(adb, el) \
2520224092Sdougb	do { \
2521224092Sdougb		(adb)->el = isc_mem_get((adb)->mctx, \
2522224092Sdougb				     sizeof(*(adb)->el) * (adb)->nnames); \
2523224092Sdougb		if ((adb)->el == NULL) { \
2524224092Sdougb			result = ISC_R_NOMEMORY; \
2525224092Sdougb			goto fail1; \
2526224092Sdougb		}\
2527224092Sdougb	} while (0)
2528224092Sdougb	ALLOCNAME(adb, names);
2529224092Sdougb	ALLOCNAME(adb, deadnames);
2530224092Sdougb	ALLOCNAME(adb, namelocks);
2531224092Sdougb	ALLOCNAME(adb, name_sd);
2532224092Sdougb	ALLOCNAME(adb, name_refcnt);
2533224092Sdougb#undef ALLOCNAME
2534224092Sdougb
2535135446Strhodes	/*
2536135446Strhodes	 * Initialize the bucket locks for names and elements.
2537135446Strhodes	 * May as well initialize the list heads, too.
2538135446Strhodes	 */
2539224092Sdougb	result = isc_mutexblock_init(adb->namelocks, adb->nnames);
2540135446Strhodes	if (result != ISC_R_SUCCESS)
2541135446Strhodes		goto fail1;
2542224092Sdougb	for (i = 0; i < adb->nnames; i++) {
2543135446Strhodes		ISC_LIST_INIT(adb->names[i]);
2544193149Sdougb		ISC_LIST_INIT(adb->deadnames[i]);
2545135446Strhodes		adb->name_sd[i] = ISC_FALSE;
2546135446Strhodes		adb->name_refcnt[i] = 0;
2547135446Strhodes		adb->irefcnt++;
2548135446Strhodes	}
2549224092Sdougb	for (i = 0; i < adb->nentries; i++) {
2550135446Strhodes		ISC_LIST_INIT(adb->entries[i]);
2551193149Sdougb		ISC_LIST_INIT(adb->deadentries[i]);
2552135446Strhodes		adb->entry_sd[i] = ISC_FALSE;
2553135446Strhodes		adb->entry_refcnt[i] = 0;
2554135446Strhodes		adb->irefcnt++;
2555135446Strhodes	}
2556224092Sdougb	result = isc_mutexblock_init(adb->entrylocks, adb->nentries);
2557135446Strhodes	if (result != ISC_R_SUCCESS)
2558135446Strhodes		goto fail2;
2559135446Strhodes
2560135446Strhodes	/*
2561135446Strhodes	 * Memory pools
2562135446Strhodes	 */
2563135446Strhodes#define MPINIT(t, p, n) do { \
2564135446Strhodes	result = isc_mempool_create(mem, sizeof(t), &(p)); \
2565135446Strhodes	if (result != ISC_R_SUCCESS) \
2566135446Strhodes		goto fail3; \
2567135446Strhodes	isc_mempool_setfreemax((p), FREE_ITEMS); \
2568135446Strhodes	isc_mempool_setfillcount((p), FILL_COUNT); \
2569135446Strhodes	isc_mempool_setname((p), n); \
2570135446Strhodes	isc_mempool_associatelock((p), &adb->mplock); \
2571135446Strhodes} while (0)
2572135446Strhodes
2573135446Strhodes	MPINIT(dns_adbname_t, adb->nmp, "adbname");
2574135446Strhodes	MPINIT(dns_adbnamehook_t, adb->nhmp, "adbnamehook");
2575170222Sdougb	MPINIT(dns_adblameinfo_t, adb->limp, "adblameinfo");
2576135446Strhodes	MPINIT(dns_adbentry_t, adb->emp, "adbentry");
2577135446Strhodes	MPINIT(dns_adbfind_t, adb->ahmp, "adbfind");
2578135446Strhodes	MPINIT(dns_adbaddrinfo_t, adb->aimp, "adbaddrinfo");
2579135446Strhodes	MPINIT(dns_adbfetch_t, adb->afmp, "adbfetch");
2580135446Strhodes
2581135446Strhodes#undef MPINIT
2582135446Strhodes
2583135446Strhodes	/*
2584193149Sdougb	 * Allocate an internal task.
2585135446Strhodes	 */
2586135446Strhodes	result = isc_task_create(adb->taskmgr, 0, &adb->task);
2587135446Strhodes	if (result != ISC_R_SUCCESS)
2588135446Strhodes		goto fail3;
2589245163Serwin
2590135446Strhodes	isc_task_setname(adb->task, "ADB", adb);
2591135446Strhodes
2592135446Strhodes	/*
2593135446Strhodes	 * Normal return.
2594135446Strhodes	 */
2595135446Strhodes	adb->magic = DNS_ADB_MAGIC;
2596135446Strhodes	*newadb = adb;
2597135446Strhodes	return (ISC_R_SUCCESS);
2598135446Strhodes
2599135446Strhodes fail3:
2600135446Strhodes	if (adb->task != NULL)
2601135446Strhodes		isc_task_detach(&adb->task);
2602135446Strhodes
2603135446Strhodes	/* clean up entrylocks */
2604224092Sdougb	DESTROYMUTEXBLOCK(adb->entrylocks, adb->nentries);
2605135446Strhodes
2606135446Strhodes fail2: /* clean up namelocks */
2607224092Sdougb	DESTROYMUTEXBLOCK(adb->namelocks, adb->nnames);
2608135446Strhodes
2609135446Strhodes fail1: /* clean up only allocated memory */
2610224092Sdougb	if (adb->entries != NULL)
2611224092Sdougb		isc_mem_put(adb->mctx, adb->entries,
2612224092Sdougb			    sizeof(*adb->entries) * adb->nentries);
2613224092Sdougb	if (adb->deadentries != NULL)
2614224092Sdougb		isc_mem_put(adb->mctx, adb->deadentries,
2615224092Sdougb			    sizeof(*adb->deadentries) * adb->nentries);
2616224092Sdougb	if (adb->entrylocks != NULL)
2617224092Sdougb		isc_mem_put(adb->mctx, adb->entrylocks,
2618224092Sdougb			    sizeof(*adb->entrylocks) * adb->nentries);
2619224092Sdougb	if (adb->entry_sd != NULL)
2620224092Sdougb		isc_mem_put(adb->mctx, adb->entry_sd,
2621224092Sdougb			    sizeof(*adb->entry_sd) * adb->nentries);
2622224092Sdougb	if (adb->entry_refcnt != NULL)
2623224092Sdougb		isc_mem_put(adb->mctx, adb->entry_refcnt,
2624224092Sdougb			    sizeof(*adb->entry_refcnt) * adb->nentries);
2625224092Sdougb	if (adb->names != NULL)
2626224092Sdougb		isc_mem_put(adb->mctx, adb->names,
2627224092Sdougb			    sizeof(*adb->names) * adb->nnames);
2628224092Sdougb	if (adb->deadnames != NULL)
2629224092Sdougb		isc_mem_put(adb->mctx, adb->deadnames,
2630224092Sdougb			    sizeof(*adb->deadnames) * adb->nnames);
2631224092Sdougb	if (adb->namelocks != NULL)
2632224092Sdougb		isc_mem_put(adb->mctx, adb->namelocks,
2633224092Sdougb			    sizeof(*adb->namelocks) * adb->nnames);
2634224092Sdougb	if (adb->name_sd != NULL)
2635224092Sdougb		isc_mem_put(adb->mctx, adb->name_sd,
2636224092Sdougb			    sizeof(*adb->name_sd) * adb->nnames);
2637224092Sdougb	if (adb->name_refcnt != NULL)
2638224092Sdougb		isc_mem_put(adb->mctx, adb->name_refcnt,
2639224092Sdougb			    sizeof(*adb->name_refcnt) * adb->nnames);
2640135446Strhodes	if (adb->nmp != NULL)
2641135446Strhodes		isc_mempool_destroy(&adb->nmp);
2642135446Strhodes	if (adb->nhmp != NULL)
2643135446Strhodes		isc_mempool_destroy(&adb->nhmp);
2644170222Sdougb	if (adb->limp != NULL)
2645170222Sdougb		isc_mempool_destroy(&adb->limp);
2646135446Strhodes	if (adb->emp != NULL)
2647135446Strhodes		isc_mempool_destroy(&adb->emp);
2648135446Strhodes	if (adb->ahmp != NULL)
2649135446Strhodes		isc_mempool_destroy(&adb->ahmp);
2650135446Strhodes	if (adb->aimp != NULL)
2651135446Strhodes		isc_mempool_destroy(&adb->aimp);
2652135446Strhodes	if (adb->afmp != NULL)
2653135446Strhodes		isc_mempool_destroy(&adb->afmp);
2654135446Strhodes
2655224092Sdougb	DESTROYLOCK(&adb->namescntlock);
2656224092Sdougb fail0g:
2657224092Sdougb	DESTROYLOCK(&adb->entriescntlock);
2658224092Sdougb fail0f:
2659186462Sdougb	DESTROYLOCK(&adb->overmemlock);
2660186462Sdougb fail0e:
2661135446Strhodes	DESTROYLOCK(&adb->reflock);
2662135446Strhodes fail0d:
2663135446Strhodes	DESTROYLOCK(&adb->mplock);
2664135446Strhodes fail0c:
2665135446Strhodes	DESTROYLOCK(&adb->lock);
2666135446Strhodes fail0b:
2667135446Strhodes	isc_mem_putanddetach(&adb->mctx, adb, sizeof(dns_adb_t));
2668135446Strhodes
2669135446Strhodes	return (result);
2670135446Strhodes}
2671135446Strhodes
2672135446Strhodesvoid
2673135446Strhodesdns_adb_attach(dns_adb_t *adb, dns_adb_t **adbx) {
2674135446Strhodes
2675135446Strhodes	REQUIRE(DNS_ADB_VALID(adb));
2676135446Strhodes	REQUIRE(adbx != NULL && *adbx == NULL);
2677135446Strhodes
2678135446Strhodes	inc_adb_erefcnt(adb);
2679135446Strhodes	*adbx = adb;
2680135446Strhodes}
2681135446Strhodes
2682135446Strhodesvoid
2683135446Strhodesdns_adb_detach(dns_adb_t **adbx) {
2684135446Strhodes	dns_adb_t *adb;
2685135446Strhodes	isc_boolean_t need_exit_check;
2686135446Strhodes
2687135446Strhodes	REQUIRE(adbx != NULL && DNS_ADB_VALID(*adbx));
2688135446Strhodes
2689135446Strhodes	adb = *adbx;
2690135446Strhodes	*adbx = NULL;
2691135446Strhodes
2692135446Strhodes	INSIST(adb->erefcnt > 0);
2693135446Strhodes
2694135446Strhodes	LOCK(&adb->reflock);
2695135446Strhodes	adb->erefcnt--;
2696135446Strhodes	need_exit_check = ISC_TF(adb->erefcnt == 0 && adb->irefcnt == 0);
2697135446Strhodes	UNLOCK(&adb->reflock);
2698135446Strhodes
2699135446Strhodes	if (need_exit_check) {
2700135446Strhodes		LOCK(&adb->lock);
2701135446Strhodes		INSIST(adb->shutting_down);
2702135446Strhodes		check_exit(adb);
2703135446Strhodes		UNLOCK(&adb->lock);
2704135446Strhodes	}
2705135446Strhodes}
2706135446Strhodes
2707135446Strhodesvoid
2708135446Strhodesdns_adb_whenshutdown(dns_adb_t *adb, isc_task_t *task, isc_event_t **eventp) {
2709135446Strhodes	isc_task_t *clone;
2710135446Strhodes	isc_event_t *event;
2711135446Strhodes	isc_boolean_t zeroirefcnt = ISC_FALSE;
2712135446Strhodes
2713135446Strhodes	/*
2714135446Strhodes	 * Send '*eventp' to 'task' when 'adb' has shutdown.
2715135446Strhodes	 */
2716135446Strhodes
2717135446Strhodes	REQUIRE(DNS_ADB_VALID(adb));
2718135446Strhodes	REQUIRE(eventp != NULL);
2719135446Strhodes
2720135446Strhodes	event = *eventp;
2721135446Strhodes	*eventp = NULL;
2722135446Strhodes
2723135446Strhodes	LOCK(&adb->lock);
2724135446Strhodes
2725135446Strhodes	LOCK(&adb->reflock);
2726135446Strhodes	zeroirefcnt = ISC_TF(adb->irefcnt == 0);
2727135446Strhodes
2728135446Strhodes	if (adb->shutting_down && zeroirefcnt &&
2729135446Strhodes	    isc_mempool_getallocated(adb->ahmp) == 0) {
2730135446Strhodes		/*
2731135446Strhodes		 * We're already shutdown.  Send the event.
2732135446Strhodes		 */
2733135446Strhodes		event->ev_sender = adb;
2734135446Strhodes		isc_task_send(task, &event);
2735135446Strhodes	} else {
2736135446Strhodes		clone = NULL;
2737135446Strhodes		isc_task_attach(task, &clone);
2738135446Strhodes		event->ev_sender = clone;
2739135446Strhodes		ISC_LIST_APPEND(adb->whenshutdown, event, ev_link);
2740135446Strhodes	}
2741135446Strhodes
2742135446Strhodes	UNLOCK(&adb->reflock);
2743135446Strhodes	UNLOCK(&adb->lock);
2744135446Strhodes}
2745135446Strhodes
2746135446Strhodesvoid
2747135446Strhodesdns_adb_shutdown(dns_adb_t *adb) {
2748135446Strhodes	isc_boolean_t need_check_exit;
2749135446Strhodes
2750135446Strhodes	/*
2751135446Strhodes	 * Shutdown 'adb'.
2752135446Strhodes	 */
2753135446Strhodes
2754135446Strhodes	LOCK(&adb->lock);
2755135446Strhodes
2756135446Strhodes	if (!adb->shutting_down) {
2757135446Strhodes		adb->shutting_down = ISC_TRUE;
2758135446Strhodes		isc_mem_setwater(adb->mctx, water, adb, 0, 0);
2759135446Strhodes		need_check_exit = shutdown_names(adb);
2760135446Strhodes		if (!need_check_exit)
2761135446Strhodes			need_check_exit = shutdown_entries(adb);
2762135446Strhodes		if (need_check_exit)
2763135446Strhodes			check_exit(adb);
2764135446Strhodes	}
2765135446Strhodes
2766135446Strhodes	UNLOCK(&adb->lock);
2767135446Strhodes}
2768135446Strhodes
2769135446Strhodesisc_result_t
2770135446Strhodesdns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action,
2771170222Sdougb		   void *arg, dns_name_t *name, dns_name_t *qname,
2772170222Sdougb		   dns_rdatatype_t qtype, unsigned int options,
2773170222Sdougb		   isc_stdtime_t now, dns_name_t *target,
2774135446Strhodes		   in_port_t port, dns_adbfind_t **findp)
2775135446Strhodes{
2776275672Sdelphij	return (dns_adb_createfind2(adb, task, action, arg, name,
2777275672Sdelphij				    qname, qtype, options, now,
2778275672Sdelphij				    target, port, 0, findp));
2779275672Sdelphij}
2780275672Sdelphij
2781275672Sdelphijisc_result_t
2782275672Sdelphijdns_adb_createfind2(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action,
2783275672Sdelphij		    void *arg, dns_name_t *name, dns_name_t *qname,
2784275672Sdelphij		    dns_rdatatype_t qtype, unsigned int options,
2785275672Sdelphij		    isc_stdtime_t now, dns_name_t *target,
2786275672Sdelphij		    in_port_t port, unsigned int depth,
2787275672Sdelphij		    dns_adbfind_t **findp)
2788275672Sdelphij{
2789135446Strhodes	dns_adbfind_t *find;
2790135446Strhodes	dns_adbname_t *adbname;
2791135446Strhodes	int bucket;
2792135446Strhodes	isc_boolean_t want_event, start_at_zone, alias, have_address;
2793135446Strhodes	isc_result_t result;
2794135446Strhodes	unsigned int wanted_addresses;
2795135446Strhodes	unsigned int wanted_fetches;
2796135446Strhodes	unsigned int query_pending;
2797135446Strhodes
2798135446Strhodes	REQUIRE(DNS_ADB_VALID(adb));
2799135446Strhodes	if (task != NULL) {
2800135446Strhodes		REQUIRE(action != NULL);
2801135446Strhodes	}
2802135446Strhodes	REQUIRE(name != NULL);
2803170222Sdougb	REQUIRE(qname != NULL);
2804135446Strhodes	REQUIRE(findp != NULL && *findp == NULL);
2805135446Strhodes	REQUIRE(target == NULL || dns_name_hasbuffer(target));
2806135446Strhodes
2807135446Strhodes	REQUIRE((options & DNS_ADBFIND_ADDRESSMASK) != 0);
2808135446Strhodes
2809135446Strhodes	result = ISC_R_UNEXPECTED;
2810225361Sdougb	POST(result);
2811135446Strhodes	wanted_addresses = (options & DNS_ADBFIND_ADDRESSMASK);
2812135446Strhodes	wanted_fetches = 0;
2813135446Strhodes	query_pending = 0;
2814135446Strhodes	want_event = ISC_FALSE;
2815135446Strhodes	start_at_zone = ISC_FALSE;
2816135446Strhodes	alias = ISC_FALSE;
2817135446Strhodes
2818135446Strhodes	if (now == 0)
2819135446Strhodes		isc_stdtime_get(&now);
2820135446Strhodes
2821135446Strhodes	/*
2822135446Strhodes	 * XXXMLG  Move this comment somewhere else!
2823135446Strhodes	 *
2824135446Strhodes	 * Look up the name in our internal database.
2825135446Strhodes	 *
2826135446Strhodes	 * Possibilities:  Note that these are not always exclusive.
2827135446Strhodes	 *
2828193149Sdougb	 *      No name found.  In this case, allocate a new name header and
2829193149Sdougb	 *      an initial namehook or two.  If any of these allocations
2830193149Sdougb	 *      fail, clean up and return ISC_R_NOMEMORY.
2831135446Strhodes	 *
2832193149Sdougb	 *      Name found, valid addresses present.  Allocate one addrinfo
2833193149Sdougb	 *      structure for each found and append it to the linked list
2834193149Sdougb	 *      of addresses for this header.
2835135446Strhodes	 *
2836193149Sdougb	 *      Name found, queries pending.  In this case, if a task was
2837193149Sdougb	 *      passed in, allocate a job id, attach it to the name's job
2838193149Sdougb	 *      list and remember to tell the caller that there will be
2839193149Sdougb	 *      more info coming later.
2840135446Strhodes	 */
2841135446Strhodes
2842135446Strhodes	find = new_adbfind(adb);
2843135446Strhodes	if (find == NULL)
2844135446Strhodes		return (ISC_R_NOMEMORY);
2845135446Strhodes
2846135446Strhodes	find->port = port;
2847135446Strhodes
2848135446Strhodes	/*
2849135446Strhodes	 * Remember what types of addresses we are interested in.
2850135446Strhodes	 */
2851135446Strhodes	find->options = options;
2852135446Strhodes	find->flags |= wanted_addresses;
2853135446Strhodes	if (FIND_WANTEVENT(find)) {
2854135446Strhodes		REQUIRE(task != NULL);
2855135446Strhodes	}
2856135446Strhodes
2857135446Strhodes	/*
2858135446Strhodes	 * Try to see if we know anything about this name at all.
2859135446Strhodes	 */
2860135446Strhodes	bucket = DNS_ADB_INVALIDBUCKET;
2861135446Strhodes	adbname = find_name_and_lock(adb, name, find->options, &bucket);
2862225361Sdougb	INSIST(bucket != DNS_ADB_INVALIDBUCKET);
2863135446Strhodes	if (adb->name_sd[bucket]) {
2864135446Strhodes		DP(DEF_LEVEL,
2865135446Strhodes		   "dns_adb_createfind: returning ISC_R_SHUTTINGDOWN");
2866135446Strhodes		RUNTIME_CHECK(free_adbfind(adb, &find) == ISC_FALSE);
2867135446Strhodes		result = ISC_R_SHUTTINGDOWN;
2868135446Strhodes		goto out;
2869135446Strhodes	}
2870135446Strhodes
2871135446Strhodes	/*
2872135446Strhodes	 * Nothing found.  Allocate a new adbname structure for this name.
2873135446Strhodes	 */
2874135446Strhodes	if (adbname == NULL) {
2875193149Sdougb		/*
2876193149Sdougb		 * See if there is any stale name at the end of list, and purge
2877193149Sdougb		 * it if so.
2878193149Sdougb		 */
2879193149Sdougb		check_stale_name(adb, bucket, now);
2880193149Sdougb
2881135446Strhodes		adbname = new_adbname(adb, name);
2882135446Strhodes		if (adbname == NULL) {
2883135446Strhodes			RUNTIME_CHECK(free_adbfind(adb, &find) == ISC_FALSE);
2884135446Strhodes			result = ISC_R_NOMEMORY;
2885135446Strhodes			goto out;
2886135446Strhodes		}
2887135446Strhodes		link_name(adb, bucket, adbname);
2888135446Strhodes		if (FIND_HINTOK(find))
2889135446Strhodes			adbname->flags |= NAME_HINT_OK;
2890135446Strhodes		if (FIND_GLUEOK(find))
2891135446Strhodes			adbname->flags |= NAME_GLUE_OK;
2892135446Strhodes		if (FIND_STARTATZONE(find))
2893135446Strhodes			adbname->flags |= NAME_STARTATZONE;
2894193149Sdougb	} else {
2895193149Sdougb		/* Move this name forward in the LRU list */
2896193149Sdougb		ISC_LIST_UNLINK(adb->names[bucket], adbname, plink);
2897193149Sdougb		ISC_LIST_PREPEND(adb->names[bucket], adbname, plink);
2898135446Strhodes	}
2899193149Sdougb	adbname->last_used = now;
2900135446Strhodes
2901135446Strhodes	/*
2902135446Strhodes	 * Expire old entries, etc.
2903135446Strhodes	 */
2904193149Sdougb	RUNTIME_CHECK(check_expire_namehooks(adbname, now) == ISC_FALSE);
2905135446Strhodes
2906135446Strhodes	/*
2907135446Strhodes	 * Do we know that the name is an alias?
2908135446Strhodes	 */
2909135446Strhodes	if (!EXPIRE_OK(adbname->expire_target, now)) {
2910135446Strhodes		/*
2911135446Strhodes		 * Yes, it is.
2912135446Strhodes		 */
2913135446Strhodes		DP(DEF_LEVEL,
2914135446Strhodes		   "dns_adb_createfind: name %p is an alias (cached)",
2915135446Strhodes		   adbname);
2916135446Strhodes		alias = ISC_TRUE;
2917135446Strhodes		goto post_copy;
2918135446Strhodes	}
2919135446Strhodes
2920135446Strhodes	/*
2921135446Strhodes	 * Try to populate the name from the database and/or
2922135446Strhodes	 * start fetches.  First try looking for an A record
2923135446Strhodes	 * in the database.
2924135446Strhodes	 */
2925135446Strhodes	if (!NAME_HAS_V4(adbname) && EXPIRE_OK(adbname->expire_v4, now)
2926135446Strhodes	    && WANT_INET(wanted_addresses)) {
2927135446Strhodes		result = dbfind_name(adbname, now, dns_rdatatype_a);
2928135446Strhodes		if (result == ISC_R_SUCCESS) {
2929135446Strhodes			DP(DEF_LEVEL,
2930135446Strhodes			   "dns_adb_createfind: found A for name %p in db",
2931135446Strhodes			   adbname);
2932135446Strhodes			goto v6;
2933135446Strhodes		}
2934135446Strhodes
2935135446Strhodes		/*
2936135446Strhodes		 * Did we get a CNAME or DNAME?
2937135446Strhodes		 */
2938135446Strhodes		if (result == DNS_R_ALIAS) {
2939135446Strhodes			DP(DEF_LEVEL,
2940135446Strhodes			   "dns_adb_createfind: name %p is an alias",
2941135446Strhodes			   adbname);
2942135446Strhodes			alias = ISC_TRUE;
2943135446Strhodes			goto post_copy;
2944135446Strhodes		}
2945135446Strhodes
2946135446Strhodes		/*
2947135446Strhodes		 * If the name doesn't exist at all, don't bother with
2948135446Strhodes		 * v6 queries; they won't work.
2949135446Strhodes		 *
2950135446Strhodes		 * If the name does exist but we didn't get our data, go
2951135446Strhodes		 * ahead and try AAAA.
2952135446Strhodes		 *
2953135446Strhodes		 * If the result is neither of these, try a fetch for A.
2954135446Strhodes		 */
2955135446Strhodes		if (NXDOMAIN_RESULT(result))
2956135446Strhodes			goto fetch;
2957135446Strhodes		else if (NXRRSET_RESULT(result))
2958135446Strhodes			goto v6;
2959135446Strhodes
2960135446Strhodes		if (!NAME_FETCH_V4(adbname))
2961135446Strhodes			wanted_fetches |= DNS_ADBFIND_INET;
2962135446Strhodes	}
2963135446Strhodes
2964135446Strhodes v6:
2965135446Strhodes	if (!NAME_HAS_V6(adbname) && EXPIRE_OK(adbname->expire_v6, now)
2966135446Strhodes	    && WANT_INET6(wanted_addresses)) {
2967135446Strhodes		result = dbfind_name(adbname, now, dns_rdatatype_aaaa);
2968135446Strhodes		if (result == ISC_R_SUCCESS) {
2969135446Strhodes			DP(DEF_LEVEL,
2970135446Strhodes			   "dns_adb_createfind: found AAAA for name %p",
2971135446Strhodes			   adbname);
2972135446Strhodes			goto fetch;
2973135446Strhodes		}
2974135446Strhodes
2975135446Strhodes		/*
2976135446Strhodes		 * Did we get a CNAME or DNAME?
2977135446Strhodes		 */
2978135446Strhodes		if (result == DNS_R_ALIAS) {
2979135446Strhodes			DP(DEF_LEVEL,
2980135446Strhodes			   "dns_adb_createfind: name %p is an alias",
2981135446Strhodes			   adbname);
2982135446Strhodes			alias = ISC_TRUE;
2983135446Strhodes			goto post_copy;
2984135446Strhodes		}
2985135446Strhodes
2986135446Strhodes		/*
2987135446Strhodes		 * Listen to negative cache hints, and don't start
2988135446Strhodes		 * another query.
2989135446Strhodes		 */
2990135446Strhodes		if (NCACHE_RESULT(result) || AUTH_NX(result))
2991135446Strhodes			goto fetch;
2992135446Strhodes
2993135446Strhodes		if (!NAME_FETCH_V6(adbname))
2994135446Strhodes			wanted_fetches |= DNS_ADBFIND_INET6;
2995135446Strhodes	}
2996135446Strhodes
2997135446Strhodes fetch:
2998135446Strhodes	if ((WANT_INET(wanted_addresses) && NAME_HAS_V4(adbname)) ||
2999135446Strhodes	    (WANT_INET6(wanted_addresses) && NAME_HAS_V6(adbname)))
3000135446Strhodes		have_address = ISC_TRUE;
3001135446Strhodes	else
3002135446Strhodes		have_address = ISC_FALSE;
3003135446Strhodes	if (wanted_fetches != 0 &&
3004135446Strhodes	    ! (FIND_AVOIDFETCHES(find) && have_address)) {
3005135446Strhodes		/*
3006135446Strhodes		 * We're missing at least one address family.  Either the
3007135446Strhodes		 * caller hasn't instructed us to avoid fetches, or we don't
3008135446Strhodes		 * know anything about any of the address families that would
3009135446Strhodes		 * be acceptable so we have to launch fetches.
3010135446Strhodes		 */
3011135446Strhodes
3012135446Strhodes		if (FIND_STARTATZONE(find))
3013135446Strhodes			start_at_zone = ISC_TRUE;
3014135446Strhodes
3015135446Strhodes		/*
3016135446Strhodes		 * Start V4.
3017135446Strhodes		 */
3018135446Strhodes		if (WANT_INET(wanted_fetches) &&
3019275672Sdelphij		    fetch_name(adbname, start_at_zone, depth,
3020135446Strhodes			       dns_rdatatype_a) == ISC_R_SUCCESS) {
3021135446Strhodes			DP(DEF_LEVEL,
3022135446Strhodes			   "dns_adb_createfind: started A fetch for name %p",
3023135446Strhodes			   adbname);
3024135446Strhodes		}
3025135446Strhodes
3026135446Strhodes		/*
3027135446Strhodes		 * Start V6.
3028135446Strhodes		 */
3029135446Strhodes		if (WANT_INET6(wanted_fetches) &&
3030275672Sdelphij		    fetch_name(adbname, start_at_zone, depth,
3031135446Strhodes			       dns_rdatatype_aaaa) == ISC_R_SUCCESS) {
3032135446Strhodes			DP(DEF_LEVEL,
3033135446Strhodes			   "dns_adb_createfind: "
3034135446Strhodes			   "started AAAA fetch for name %p",
3035135446Strhodes			   adbname);
3036135446Strhodes		}
3037135446Strhodes	}
3038135446Strhodes
3039135446Strhodes	/*
3040135446Strhodes	 * Run through the name and copy out the bits we are
3041135446Strhodes	 * interested in.
3042135446Strhodes	 */
3043170222Sdougb	copy_namehook_lists(adb, find, qname, qtype, adbname, now);
3044135446Strhodes
3045135446Strhodes post_copy:
3046135446Strhodes	if (NAME_FETCH_V4(adbname))
3047135446Strhodes		query_pending |= DNS_ADBFIND_INET;
3048135446Strhodes	if (NAME_FETCH_V6(adbname))
3049135446Strhodes		query_pending |= DNS_ADBFIND_INET6;
3050135446Strhodes
3051135446Strhodes	/*
3052135446Strhodes	 * Attach to the name's query list if there are queries
3053135446Strhodes	 * already running, and we have been asked to.
3054135446Strhodes	 */
3055135446Strhodes	want_event = ISC_TRUE;
3056135446Strhodes	if (!FIND_WANTEVENT(find))
3057135446Strhodes		want_event = ISC_FALSE;
3058135446Strhodes	if (FIND_WANTEMPTYEVENT(find) && FIND_HAS_ADDRS(find))
3059135446Strhodes		want_event = ISC_FALSE;
3060135446Strhodes	if ((wanted_addresses & query_pending) == 0)
3061135446Strhodes		want_event = ISC_FALSE;
3062135446Strhodes	if (alias)
3063135446Strhodes		want_event = ISC_FALSE;
3064135446Strhodes	if (want_event) {
3065135446Strhodes		find->adbname = adbname;
3066135446Strhodes		find->name_bucket = bucket;
3067135446Strhodes		ISC_LIST_APPEND(adbname->finds, find, plink);
3068135446Strhodes		find->query_pending = (query_pending & wanted_addresses);
3069135446Strhodes		find->flags &= ~DNS_ADBFIND_ADDRESSMASK;
3070135446Strhodes		find->flags |= (find->query_pending & DNS_ADBFIND_ADDRESSMASK);
3071135446Strhodes		DP(DEF_LEVEL, "createfind: attaching find %p to adbname %p",
3072135446Strhodes		   find, adbname);
3073135446Strhodes	} else {
3074135446Strhodes		/*
3075135446Strhodes		 * Remove the flag so the caller knows there will never
3076135446Strhodes		 * be an event, and set internal flags to fake that
3077135446Strhodes		 * the event was sent and freed, so dns_adb_destroyfind() will
3078135446Strhodes		 * do the right thing.
3079135446Strhodes		 */
3080135446Strhodes		find->query_pending = (query_pending & wanted_addresses);
3081135446Strhodes		find->options &= ~DNS_ADBFIND_WANTEVENT;
3082135446Strhodes		find->flags |= (FIND_EVENT_SENT | FIND_EVENT_FREED);
3083135446Strhodes		find->flags &= ~DNS_ADBFIND_ADDRESSMASK;
3084135446Strhodes	}
3085135446Strhodes
3086135446Strhodes	find->partial_result |= (adbname->partial_result & wanted_addresses);
3087135446Strhodes	if (alias) {
3088135446Strhodes		if (target != NULL) {
3089135446Strhodes			result = dns_name_copy(&adbname->target, target, NULL);
3090135446Strhodes			if (result != ISC_R_SUCCESS)
3091135446Strhodes				goto out;
3092135446Strhodes		}
3093135446Strhodes		result = DNS_R_ALIAS;
3094135446Strhodes	} else
3095135446Strhodes		result = ISC_R_SUCCESS;
3096135446Strhodes
3097135446Strhodes	/*
3098135446Strhodes	 * Copy out error flags from the name structure into the find.
3099135446Strhodes	 */
3100135446Strhodes	find->result_v4 = find_err_map[adbname->fetch_err];
3101135446Strhodes	find->result_v6 = find_err_map[adbname->fetch6_err];
3102135446Strhodes
3103135446Strhodes out:
3104135446Strhodes	if (find != NULL) {
3105135446Strhodes		*findp = find;
3106135446Strhodes
3107135446Strhodes		if (want_event) {
3108135446Strhodes			isc_task_t *taskp;
3109135446Strhodes
3110135446Strhodes			INSIST((find->flags & DNS_ADBFIND_ADDRESSMASK) != 0);
3111135446Strhodes			taskp = NULL;
3112135446Strhodes			isc_task_attach(task, &taskp);
3113135446Strhodes			find->event.ev_sender = taskp;
3114135446Strhodes			find->event.ev_action = action;
3115135446Strhodes			find->event.ev_arg = arg;
3116135446Strhodes		}
3117135446Strhodes	}
3118135446Strhodes
3119165071Sdougb	UNLOCK(&adb->namelocks[bucket]);
3120135446Strhodes
3121135446Strhodes	return (result);
3122135446Strhodes}
3123135446Strhodes
3124135446Strhodesvoid
3125135446Strhodesdns_adb_destroyfind(dns_adbfind_t **findp) {
3126135446Strhodes	dns_adbfind_t *find;
3127135446Strhodes	dns_adbentry_t *entry;
3128135446Strhodes	dns_adbaddrinfo_t *ai;
3129135446Strhodes	int bucket;
3130135446Strhodes	dns_adb_t *adb;
3131214586Sdougb	isc_boolean_t overmem;
3132135446Strhodes
3133135446Strhodes	REQUIRE(findp != NULL && DNS_ADBFIND_VALID(*findp));
3134135446Strhodes	find = *findp;
3135135446Strhodes	*findp = NULL;
3136135446Strhodes
3137135446Strhodes	LOCK(&find->lock);
3138135446Strhodes
3139135446Strhodes	DP(DEF_LEVEL, "dns_adb_destroyfind on find %p", find);
3140135446Strhodes
3141135446Strhodes	adb = find->adb;
3142135446Strhodes	REQUIRE(DNS_ADB_VALID(adb));
3143135446Strhodes
3144135446Strhodes	REQUIRE(FIND_EVENTFREED(find));
3145135446Strhodes
3146135446Strhodes	bucket = find->name_bucket;
3147135446Strhodes	INSIST(bucket == DNS_ADB_INVALIDBUCKET);
3148135446Strhodes
3149135446Strhodes	UNLOCK(&find->lock);
3150135446Strhodes
3151135446Strhodes	/*
3152135446Strhodes	 * The find doesn't exist on any list, and nothing is locked.
3153135446Strhodes	 * Return the find to the memory pool, and decrement the adb's
3154135446Strhodes	 * reference count.
3155135446Strhodes	 */
3156214586Sdougb	overmem = isc_mem_isovermem(adb->mctx);
3157135446Strhodes	ai = ISC_LIST_HEAD(find->list);
3158135446Strhodes	while (ai != NULL) {
3159135446Strhodes		ISC_LIST_UNLINK(find->list, ai, publink);
3160135446Strhodes		entry = ai->entry;
3161135446Strhodes		ai->entry = NULL;
3162135446Strhodes		INSIST(DNS_ADBENTRY_VALID(entry));
3163214586Sdougb		RUNTIME_CHECK(dec_entry_refcnt(adb, overmem, entry, ISC_TRUE) ==
3164135446Strhodes			      ISC_FALSE);
3165135446Strhodes		free_adbaddrinfo(adb, &ai);
3166135446Strhodes		ai = ISC_LIST_HEAD(find->list);
3167135446Strhodes	}
3168135446Strhodes
3169135446Strhodes	/*
3170135446Strhodes	 * WARNING:  The find is freed with the adb locked.  This is done
3171135446Strhodes	 * to avoid a race condition where we free the find, some other
3172135446Strhodes	 * thread tests to see if it should be destroyed, detects it should
3173135446Strhodes	 * be, destroys it, and then we try to lock it for our check, but the
3174135446Strhodes	 * lock is destroyed.
3175135446Strhodes	 */
3176135446Strhodes	LOCK(&adb->lock);
3177135446Strhodes	if (free_adbfind(adb, &find))
3178135446Strhodes		check_exit(adb);
3179135446Strhodes	UNLOCK(&adb->lock);
3180135446Strhodes}
3181135446Strhodes
3182135446Strhodesvoid
3183135446Strhodesdns_adb_cancelfind(dns_adbfind_t *find) {
3184135446Strhodes	isc_event_t *ev;
3185135446Strhodes	isc_task_t *task;
3186135446Strhodes	dns_adb_t *adb;
3187135446Strhodes	int bucket;
3188135446Strhodes	int unlock_bucket;
3189135446Strhodes
3190135446Strhodes	LOCK(&find->lock);
3191135446Strhodes
3192135446Strhodes	DP(DEF_LEVEL, "dns_adb_cancelfind on find %p", find);
3193135446Strhodes
3194135446Strhodes	adb = find->adb;
3195135446Strhodes	REQUIRE(DNS_ADB_VALID(adb));
3196135446Strhodes
3197135446Strhodes	REQUIRE(!FIND_EVENTFREED(find));
3198135446Strhodes	REQUIRE(FIND_WANTEVENT(find));
3199135446Strhodes
3200135446Strhodes	bucket = find->name_bucket;
3201135446Strhodes	if (bucket == DNS_ADB_INVALIDBUCKET)
3202135446Strhodes		goto cleanup;
3203135446Strhodes
3204135446Strhodes	/*
3205135446Strhodes	 * We need to get the adbname's lock to unlink the find.
3206135446Strhodes	 */
3207135446Strhodes	unlock_bucket = bucket;
3208135446Strhodes	violate_locking_hierarchy(&find->lock, &adb->namelocks[unlock_bucket]);
3209135446Strhodes	bucket = find->name_bucket;
3210135446Strhodes	if (bucket != DNS_ADB_INVALIDBUCKET) {
3211135446Strhodes		ISC_LIST_UNLINK(find->adbname->finds, find, plink);
3212135446Strhodes		find->adbname = NULL;
3213135446Strhodes		find->name_bucket = DNS_ADB_INVALIDBUCKET;
3214135446Strhodes	}
3215135446Strhodes	UNLOCK(&adb->namelocks[unlock_bucket]);
3216135446Strhodes	bucket = DNS_ADB_INVALIDBUCKET;
3217225361Sdougb	POST(bucket);
3218135446Strhodes
3219135446Strhodes cleanup:
3220135446Strhodes
3221135446Strhodes	if (!FIND_EVENTSENT(find)) {
3222135446Strhodes		ev = &find->event;
3223135446Strhodes		task = ev->ev_sender;
3224135446Strhodes		ev->ev_sender = find;
3225135446Strhodes		ev->ev_type = DNS_EVENT_ADBCANCELED;
3226135446Strhodes		ev->ev_destroy = event_free;
3227135446Strhodes		ev->ev_destroy_arg = find;
3228135446Strhodes		find->result_v4 = ISC_R_CANCELED;
3229135446Strhodes		find->result_v6 = ISC_R_CANCELED;
3230135446Strhodes
3231135446Strhodes		DP(DEF_LEVEL, "sending event %p to task %p for find %p",
3232135446Strhodes		   ev, task, find);
3233135446Strhodes
3234135446Strhodes		isc_task_sendanddetach(&task, (isc_event_t **)&ev);
3235135446Strhodes	}
3236135446Strhodes
3237135446Strhodes	UNLOCK(&find->lock);
3238135446Strhodes}
3239135446Strhodes
3240135446Strhodesvoid
3241135446Strhodesdns_adb_dump(dns_adb_t *adb, FILE *f) {
3242224092Sdougb	unsigned int i;
3243143731Sdougb	isc_stdtime_t now;
3244143731Sdougb
3245135446Strhodes	REQUIRE(DNS_ADB_VALID(adb));
3246135446Strhodes	REQUIRE(f != NULL);
3247135446Strhodes
3248135446Strhodes	/*
3249135446Strhodes	 * Lock the adb itself, lock all the name buckets, then lock all
3250135446Strhodes	 * the entry buckets.  This should put the adb into a state where
3251135446Strhodes	 * nothing can change, so we can iterate through everything and
3252135446Strhodes	 * print at our leisure.
3253135446Strhodes	 */
3254135446Strhodes
3255135446Strhodes	LOCK(&adb->lock);
3256143731Sdougb	isc_stdtime_get(&now);
3257143731Sdougb
3258224092Sdougb	for (i = 0; i < adb->nnames; i++)
3259143731Sdougb		RUNTIME_CHECK(cleanup_names(adb, i, now) == ISC_FALSE);
3260224092Sdougb	for (i = 0; i < adb->nentries; i++)
3261143731Sdougb		RUNTIME_CHECK(cleanup_entries(adb, i, now) == ISC_FALSE);
3262143731Sdougb
3263143731Sdougb	dump_adb(adb, f, ISC_FALSE, now);
3264135446Strhodes	UNLOCK(&adb->lock);
3265135446Strhodes}
3266135446Strhodes
3267135446Strhodesstatic void
3268135446Strhodesdump_ttl(FILE *f, const char *legend, isc_stdtime_t value, isc_stdtime_t now) {
3269135446Strhodes	if (value == INT_MAX)
3270135446Strhodes		return;
3271135446Strhodes	fprintf(f, " [%s TTL %d]", legend, value - now);
3272135446Strhodes}
3273135446Strhodes
3274135446Strhodesstatic void
3275143731Sdougbdump_adb(dns_adb_t *adb, FILE *f, isc_boolean_t debug, isc_stdtime_t now) {
3276224092Sdougb	unsigned int i;
3277135446Strhodes	dns_adbname_t *name;
3278143731Sdougb	dns_adbentry_t *entry;
3279135446Strhodes
3280135446Strhodes	fprintf(f, ";\n; Address database dump\n;\n");
3281135446Strhodes	if (debug)
3282135446Strhodes		fprintf(f, "; addr %p, erefcnt %u, irefcnt %u, finds out %u\n",
3283135446Strhodes			adb, adb->erefcnt, adb->irefcnt,
3284135446Strhodes			isc_mempool_getallocated(adb->nhmp));
3285135446Strhodes
3286224092Sdougb	for (i = 0; i < adb->nnames; i++)
3287135446Strhodes		LOCK(&adb->namelocks[i]);
3288224092Sdougb	for (i = 0; i < adb->nentries; i++)
3289135446Strhodes		LOCK(&adb->entrylocks[i]);
3290135446Strhodes
3291135446Strhodes	/*
3292135446Strhodes	 * Dump the names
3293135446Strhodes	 */
3294224092Sdougb	for (i = 0; i < adb->nnames; i++) {
3295135446Strhodes		name = ISC_LIST_HEAD(adb->names[i]);
3296135446Strhodes		if (name == NULL)
3297135446Strhodes			continue;
3298135446Strhodes		if (debug)
3299135446Strhodes			fprintf(f, "; bucket %d\n", i);
3300135446Strhodes		for (;
3301135446Strhodes		     name != NULL;
3302135446Strhodes		     name = ISC_LIST_NEXT(name, plink))
3303135446Strhodes		{
3304135446Strhodes			if (debug)
3305135446Strhodes				fprintf(f, "; name %p (flags %08x)\n",
3306135446Strhodes					name, name->flags);
3307135446Strhodes
3308135446Strhodes			fprintf(f, "; ");
3309135446Strhodes			print_dns_name(f, &name->name);
3310135446Strhodes			if (dns_name_countlabels(&name->target) > 0) {
3311135446Strhodes				fprintf(f, " alias ");
3312135446Strhodes				print_dns_name(f, &name->target);
3313135446Strhodes			}
3314135446Strhodes
3315135446Strhodes			dump_ttl(f, "v4", name->expire_v4, now);
3316135446Strhodes			dump_ttl(f, "v6", name->expire_v6, now);
3317135446Strhodes			dump_ttl(f, "target", name->expire_target, now);
3318135446Strhodes
3319135446Strhodes			fprintf(f, " [v4 %s] [v6 %s]",
3320135446Strhodes				errnames[name->fetch_err],
3321135446Strhodes				errnames[name->fetch6_err]);
3322135446Strhodes
3323135446Strhodes			fprintf(f, "\n");
3324135446Strhodes
3325135446Strhodes			print_namehook_list(f, "v4", &name->v4, debug, now);
3326135446Strhodes			print_namehook_list(f, "v6", &name->v6, debug, now);
3327135446Strhodes
3328135446Strhodes			if (debug)
3329135446Strhodes				print_fetch_list(f, name);
3330135446Strhodes			if (debug)
3331135446Strhodes				print_find_list(f, name);
3332135446Strhodes
3333135446Strhodes		}
3334135446Strhodes	}
3335135446Strhodes
3336143731Sdougb	fprintf(f, ";\n; Unassociated entries\n;\n");
3337143731Sdougb
3338224092Sdougb	for (i = 0; i < adb->nentries; i++) {
3339143731Sdougb		entry = ISC_LIST_HEAD(adb->entries[i]);
3340143731Sdougb		while (entry != NULL) {
3341143731Sdougb			if (entry->refcnt == 0)
3342143731Sdougb				dump_entry(f, entry, debug, now);
3343143731Sdougb			entry = ISC_LIST_NEXT(entry, plink);
3344143731Sdougb		}
3345143731Sdougb	}
3346143731Sdougb
3347135446Strhodes	/*
3348135446Strhodes	 * Unlock everything
3349135446Strhodes	 */
3350224092Sdougb	for (i = 0; i < adb->nentries; i++)
3351135446Strhodes		UNLOCK(&adb->entrylocks[i]);
3352224092Sdougb	for (i = 0; i < adb->nnames; i++)
3353135446Strhodes		UNLOCK(&adb->namelocks[i]);
3354135446Strhodes}
3355135446Strhodes
3356135446Strhodesstatic void
3357135446Strhodesdump_entry(FILE *f, dns_adbentry_t *entry, isc_boolean_t debug,
3358135446Strhodes	   isc_stdtime_t now)
3359135446Strhodes{
3360135446Strhodes	char addrbuf[ISC_NETADDR_FORMATSIZE];
3361170222Sdougb	char typebuf[DNS_RDATATYPE_FORMATSIZE];
3362135446Strhodes	isc_netaddr_t netaddr;
3363170222Sdougb	dns_adblameinfo_t *li;
3364135446Strhodes
3365135446Strhodes	isc_netaddr_fromsockaddr(&netaddr, &entry->sockaddr);
3366135446Strhodes	isc_netaddr_format(&netaddr, addrbuf, sizeof(addrbuf));
3367135446Strhodes
3368135446Strhodes	if (debug)
3369135446Strhodes		fprintf(f, ";\t%p: refcnt %u\n", entry, entry->refcnt);
3370135446Strhodes
3371135446Strhodes	fprintf(f, ";\t%s [srtt %u] [flags %08x]",
3372135446Strhodes		addrbuf, entry->srtt, entry->flags);
3373143731Sdougb	if (entry->expires != 0)
3374143731Sdougb		fprintf(f, " [ttl %d]", entry->expires - now);
3375135446Strhodes	fprintf(f, "\n");
3376170222Sdougb	for (li = ISC_LIST_HEAD(entry->lameinfo);
3377170222Sdougb	     li != NULL;
3378170222Sdougb	     li = ISC_LIST_NEXT(li, plink)) {
3379135446Strhodes		fprintf(f, ";\t\t");
3380170222Sdougb		print_dns_name(f, &li->qname);
3381170222Sdougb		dns_rdatatype_format(li->qtype, typebuf, sizeof(typebuf));
3382170222Sdougb		fprintf(f, " %s [lame TTL %d]\n", typebuf,
3383170222Sdougb			li->lame_timer - now);
3384135446Strhodes	}
3385135446Strhodes}
3386135446Strhodes
3387135446Strhodesvoid
3388135446Strhodesdns_adb_dumpfind(dns_adbfind_t *find, FILE *f) {
3389135446Strhodes	char tmp[512];
3390135446Strhodes	const char *tmpp;
3391135446Strhodes	dns_adbaddrinfo_t *ai;
3392135446Strhodes	isc_sockaddr_t *sa;
3393135446Strhodes
3394135446Strhodes	/*
3395135446Strhodes	 * Not used currently, in the API Just In Case we
3396135446Strhodes	 * want to dump out the name and/or entries too.
3397135446Strhodes	 */
3398135446Strhodes
3399135446Strhodes	LOCK(&find->lock);
3400135446Strhodes
3401135446Strhodes	fprintf(f, ";Find %p\n", find);
3402135446Strhodes	fprintf(f, ";\tqpending %08x partial %08x options %08x flags %08x\n",
3403135446Strhodes		find->query_pending, find->partial_result,
3404135446Strhodes		find->options, find->flags);
3405135446Strhodes	fprintf(f, ";\tname_bucket %d, name %p, event sender %p\n",
3406135446Strhodes		find->name_bucket, find->adbname, find->event.ev_sender);
3407135446Strhodes
3408135446Strhodes	ai = ISC_LIST_HEAD(find->list);
3409135446Strhodes	if (ai != NULL)
3410135446Strhodes		fprintf(f, "\tAddresses:\n");
3411135446Strhodes	while (ai != NULL) {
3412135446Strhodes		sa = &ai->sockaddr;
3413135446Strhodes		switch (sa->type.sa.sa_family) {
3414135446Strhodes		case AF_INET:
3415135446Strhodes			tmpp = inet_ntop(AF_INET, &sa->type.sin.sin_addr,
3416135446Strhodes					 tmp, sizeof(tmp));
3417135446Strhodes			break;
3418135446Strhodes		case AF_INET6:
3419135446Strhodes			tmpp = inet_ntop(AF_INET6, &sa->type.sin6.sin6_addr,
3420135446Strhodes					 tmp, sizeof(tmp));
3421135446Strhodes			break;
3422135446Strhodes		default:
3423135446Strhodes			tmpp = "UnkFamily";
3424135446Strhodes		}
3425135446Strhodes
3426135446Strhodes		if (tmpp == NULL)
3427135446Strhodes			tmpp = "BadAddress";
3428135446Strhodes
3429135446Strhodes		fprintf(f, "\t\tentry %p, flags %08x"
3430135446Strhodes			" srtt %u addr %s\n",
3431135446Strhodes			ai->entry, ai->flags, ai->srtt, tmpp);
3432135446Strhodes
3433135446Strhodes		ai = ISC_LIST_NEXT(ai, publink);
3434135446Strhodes	}
3435135446Strhodes
3436135446Strhodes	UNLOCK(&find->lock);
3437135446Strhodes}
3438135446Strhodes
3439135446Strhodesstatic void
3440135446Strhodesprint_dns_name(FILE *f, dns_name_t *name) {
3441135446Strhodes	char buf[DNS_NAME_FORMATSIZE];
3442135446Strhodes
3443135446Strhodes	INSIST(f != NULL);
3444135446Strhodes
3445135446Strhodes	dns_name_format(name, buf, sizeof(buf));
3446135446Strhodes	fprintf(f, "%s", buf);
3447135446Strhodes}
3448135446Strhodes
3449135446Strhodesstatic void
3450135446Strhodesprint_namehook_list(FILE *f, const char *legend, dns_adbnamehooklist_t *list,
3451135446Strhodes		    isc_boolean_t debug, isc_stdtime_t now)
3452135446Strhodes{
3453135446Strhodes	dns_adbnamehook_t *nh;
3454135446Strhodes
3455135446Strhodes	for (nh = ISC_LIST_HEAD(*list);
3456135446Strhodes	     nh != NULL;
3457135446Strhodes	     nh = ISC_LIST_NEXT(nh, plink))
3458135446Strhodes	{
3459135446Strhodes		if (debug)
3460135446Strhodes			fprintf(f, ";\tHook(%s) %p\n", legend, nh);
3461135446Strhodes		dump_entry(f, nh->entry, debug, now);
3462135446Strhodes	}
3463135446Strhodes}
3464135446Strhodes
3465135446Strhodesstatic inline void
3466135446Strhodesprint_fetch(FILE *f, dns_adbfetch_t *ft, const char *type) {
3467193149Sdougb	fprintf(f, "\t\tFetch(%s): %p -> { fetch %p }\n",
3468193149Sdougb		type, ft, ft->fetch);
3469135446Strhodes}
3470135446Strhodes
3471135446Strhodesstatic void
3472135446Strhodesprint_fetch_list(FILE *f, dns_adbname_t *n) {
3473135446Strhodes	if (NAME_FETCH_A(n))
3474135446Strhodes		print_fetch(f, n->fetch_a, "A");
3475135446Strhodes	if (NAME_FETCH_AAAA(n))
3476135446Strhodes		print_fetch(f, n->fetch_aaaa, "AAAA");
3477135446Strhodes}
3478135446Strhodes
3479135446Strhodesstatic void
3480135446Strhodesprint_find_list(FILE *f, dns_adbname_t *name) {
3481135446Strhodes	dns_adbfind_t *find;
3482135446Strhodes
3483135446Strhodes	find = ISC_LIST_HEAD(name->finds);
3484135446Strhodes	while (find != NULL) {
3485135446Strhodes		dns_adb_dumpfind(find, f);
3486135446Strhodes		find = ISC_LIST_NEXT(find, plink);
3487135446Strhodes	}
3488135446Strhodes}
3489135446Strhodes
3490135446Strhodesstatic isc_result_t
3491135446Strhodesdbfind_name(dns_adbname_t *adbname, isc_stdtime_t now, dns_rdatatype_t rdtype)
3492135446Strhodes{
3493135446Strhodes	isc_result_t result;
3494135446Strhodes	dns_rdataset_t rdataset;
3495135446Strhodes	dns_adb_t *adb;
3496135446Strhodes	dns_fixedname_t foundname;
3497135446Strhodes	dns_name_t *fname;
3498135446Strhodes
3499135446Strhodes	INSIST(DNS_ADBNAME_VALID(adbname));
3500135446Strhodes	adb = adbname->adb;
3501135446Strhodes	INSIST(DNS_ADB_VALID(adb));
3502135446Strhodes	INSIST(rdtype == dns_rdatatype_a || rdtype == dns_rdatatype_aaaa);
3503135446Strhodes
3504135446Strhodes	dns_fixedname_init(&foundname);
3505193149Sdougb	fname = dns_fixedname_name(&foundname);
3506135446Strhodes	dns_rdataset_init(&rdataset);
3507135446Strhodes
3508135446Strhodes	if (rdtype == dns_rdatatype_a)
3509135446Strhodes		adbname->fetch_err = FIND_ERR_UNEXPECTED;
3510135446Strhodes	else
3511135446Strhodes		adbname->fetch6_err = FIND_ERR_UNEXPECTED;
3512135446Strhodes
3513224092Sdougb	/*
3514224092Sdougb	 * We need to specify whether to search static-stub zones (if
3515224092Sdougb	 * configured) depending on whether this is a "start at zone" lookup,
3516224092Sdougb	 * i.e., whether it's a "bailiwick" glue.  If it's bailiwick (in which
3517224092Sdougb	 * case NAME_STARTATZONE is set) we need to stop the search at any
3518224092Sdougb	 * matching static-stub zone without looking into the cache to honor
3519224092Sdougb	 * the configuration on which server we should send queries to.
3520224092Sdougb	 */
3521224092Sdougb	result = dns_view_find2(adb->view, &adbname->name, rdtype, now,
3522224092Sdougb				NAME_GLUEOK(adbname) ? DNS_DBFIND_GLUEOK : 0,
3523224092Sdougb				ISC_TF(NAME_HINTOK(adbname)),
3524224092Sdougb				(adbname->flags & NAME_STARTATZONE) != 0 ?
3525224092Sdougb				ISC_TRUE : ISC_FALSE,
3526224092Sdougb				NULL, NULL, fname, &rdataset, NULL);
3527135446Strhodes
3528135446Strhodes	/* XXXVIX this switch statement is too sparse to gen a jump table. */
3529135446Strhodes	switch (result) {
3530135446Strhodes	case DNS_R_GLUE:
3531135446Strhodes	case DNS_R_HINT:
3532135446Strhodes	case ISC_R_SUCCESS:
3533135446Strhodes		/*
3534135446Strhodes		 * Found in the database.  Even if we can't copy out
3535135446Strhodes		 * any information, return success, or else a fetch
3536135446Strhodes		 * will be made, which will only make things worse.
3537135446Strhodes		 */
3538135446Strhodes		if (rdtype == dns_rdatatype_a)
3539135446Strhodes			adbname->fetch_err = FIND_ERR_SUCCESS;
3540135446Strhodes		else
3541135446Strhodes			adbname->fetch6_err = FIND_ERR_SUCCESS;
3542135446Strhodes		result = import_rdataset(adbname, &rdataset, now);
3543135446Strhodes		break;
3544135446Strhodes	case DNS_R_NXDOMAIN:
3545135446Strhodes	case DNS_R_NXRRSET:
3546135446Strhodes		/*
3547135446Strhodes		 * We're authoritative and the data doesn't exist.
3548135446Strhodes		 * Make up a negative cache entry so we don't ask again
3549135446Strhodes		 * for a while.
3550135446Strhodes		 *
3551135446Strhodes		 * XXXRTH  What time should we use?  I'm putting in 30 seconds
3552135446Strhodes		 * for now.
3553135446Strhodes		 */
3554135446Strhodes		if (rdtype == dns_rdatatype_a) {
3555135446Strhodes			adbname->expire_v4 = now + 30;
3556135446Strhodes			DP(NCACHE_LEVEL,
3557135446Strhodes			   "adb name %p: Caching auth negative entry for A",
3558135446Strhodes			   adbname);
3559135446Strhodes			if (result == DNS_R_NXDOMAIN)
3560135446Strhodes				adbname->fetch_err = FIND_ERR_NXDOMAIN;
3561135446Strhodes			else
3562135446Strhodes				adbname->fetch_err = FIND_ERR_NXRRSET;
3563135446Strhodes		} else {
3564135446Strhodes			DP(NCACHE_LEVEL,
3565135446Strhodes			   "adb name %p: Caching auth negative entry for AAAA",
3566135446Strhodes			   adbname);
3567135446Strhodes			adbname->expire_v6 = now + 30;
3568135446Strhodes			if (result == DNS_R_NXDOMAIN)
3569135446Strhodes				adbname->fetch6_err = FIND_ERR_NXDOMAIN;
3570135446Strhodes			else
3571135446Strhodes				adbname->fetch6_err = FIND_ERR_NXRRSET;
3572135446Strhodes		}
3573135446Strhodes		break;
3574135446Strhodes	case DNS_R_NCACHENXDOMAIN:
3575135446Strhodes	case DNS_R_NCACHENXRRSET:
3576135446Strhodes		/*
3577135446Strhodes		 * We found a negative cache entry.  Pull the TTL from it
3578135446Strhodes		 * so we won't ask again for a while.
3579135446Strhodes		 */
3580135446Strhodes		rdataset.ttl = ttlclamp(rdataset.ttl);
3581135446Strhodes		if (rdtype == dns_rdatatype_a) {
3582135446Strhodes			adbname->expire_v4 = rdataset.ttl + now;
3583135446Strhodes			if (result == DNS_R_NCACHENXDOMAIN)
3584135446Strhodes				adbname->fetch_err = FIND_ERR_NXDOMAIN;
3585135446Strhodes			else
3586135446Strhodes				adbname->fetch_err = FIND_ERR_NXRRSET;
3587135446Strhodes			DP(NCACHE_LEVEL,
3588135446Strhodes			  "adb name %p: Caching negative entry for A (ttl %u)",
3589135446Strhodes			   adbname, rdataset.ttl);
3590135446Strhodes		} else {
3591135446Strhodes			DP(NCACHE_LEVEL,
3592135446Strhodes		       "adb name %p: Caching negative entry for AAAA (ttl %u)",
3593135446Strhodes			   adbname, rdataset.ttl);
3594135446Strhodes			adbname->expire_v6 = rdataset.ttl + now;
3595135446Strhodes			if (result == DNS_R_NCACHENXDOMAIN)
3596135446Strhodes				adbname->fetch6_err = FIND_ERR_NXDOMAIN;
3597135446Strhodes			else
3598135446Strhodes				adbname->fetch6_err = FIND_ERR_NXRRSET;
3599135446Strhodes		}
3600135446Strhodes		break;
3601135446Strhodes	case DNS_R_CNAME:
3602135446Strhodes	case DNS_R_DNAME:
3603135446Strhodes		/*
3604135446Strhodes		 * Clear the hint and glue flags, so this will match
3605135446Strhodes		 * more often.
3606135446Strhodes		 */
3607135446Strhodes		adbname->flags &= ~(DNS_ADBFIND_GLUEOK | DNS_ADBFIND_HINTOK);
3608135446Strhodes
3609135446Strhodes		rdataset.ttl = ttlclamp(rdataset.ttl);
3610135446Strhodes		clean_target(adb, &adbname->target);
3611135446Strhodes		adbname->expire_target = INT_MAX;
3612135446Strhodes		result = set_target(adb, &adbname->name, fname, &rdataset,
3613135446Strhodes				    &adbname->target);
3614135446Strhodes		if (result == ISC_R_SUCCESS) {
3615135446Strhodes			result = DNS_R_ALIAS;
3616135446Strhodes			DP(NCACHE_LEVEL,
3617135446Strhodes			   "adb name %p: caching alias target",
3618135446Strhodes			   adbname);
3619135446Strhodes			adbname->expire_target = rdataset.ttl + now;
3620135446Strhodes		}
3621135446Strhodes		if (rdtype == dns_rdatatype_a)
3622135446Strhodes			adbname->fetch_err = FIND_ERR_SUCCESS;
3623135446Strhodes		else
3624135446Strhodes			adbname->fetch6_err = FIND_ERR_SUCCESS;
3625135446Strhodes		break;
3626135446Strhodes	}
3627135446Strhodes
3628135446Strhodes	if (dns_rdataset_isassociated(&rdataset))
3629135446Strhodes		dns_rdataset_disassociate(&rdataset);
3630135446Strhodes
3631135446Strhodes	return (result);
3632135446Strhodes}
3633135446Strhodes
3634135446Strhodesstatic void
3635135446Strhodesfetch_callback(isc_task_t *task, isc_event_t *ev) {
3636135446Strhodes	dns_fetchevent_t *dev;
3637135446Strhodes	dns_adbname_t *name;
3638135446Strhodes	dns_adb_t *adb;
3639135446Strhodes	dns_adbfetch_t *fetch;
3640135446Strhodes	int bucket;
3641135446Strhodes	isc_eventtype_t ev_status;
3642135446Strhodes	isc_stdtime_t now;
3643135446Strhodes	isc_result_t result;
3644135446Strhodes	unsigned int address_type;
3645135446Strhodes	isc_boolean_t want_check_exit = ISC_FALSE;
3646275672Sdelphij	isc_uint32_t qtotal = 0;
3647135446Strhodes
3648135446Strhodes	UNUSED(task);
3649135446Strhodes
3650135446Strhodes	INSIST(ev->ev_type == DNS_EVENT_FETCHDONE);
3651135446Strhodes	dev = (dns_fetchevent_t *)ev;
3652135446Strhodes	name = ev->ev_arg;
3653135446Strhodes	INSIST(DNS_ADBNAME_VALID(name));
3654135446Strhodes	adb = name->adb;
3655135446Strhodes	INSIST(DNS_ADB_VALID(adb));
3656135446Strhodes
3657275672Sdelphij	qtotal = dev->qtotal;
3658275672Sdelphij
3659135446Strhodes	bucket = name->lock_bucket;
3660135446Strhodes	LOCK(&adb->namelocks[bucket]);
3661135446Strhodes
3662135446Strhodes	INSIST(NAME_FETCH_A(name) || NAME_FETCH_AAAA(name));
3663135446Strhodes	address_type = 0;
3664135446Strhodes	if (NAME_FETCH_A(name) && (name->fetch_a->fetch == dev->fetch)) {
3665135446Strhodes		address_type = DNS_ADBFIND_INET;
3666135446Strhodes		fetch = name->fetch_a;
3667135446Strhodes		name->fetch_a = NULL;
3668135446Strhodes	} else if (NAME_FETCH_AAAA(name)
3669135446Strhodes		   && (name->fetch_aaaa->fetch == dev->fetch)) {
3670135446Strhodes		address_type = DNS_ADBFIND_INET6;
3671135446Strhodes		fetch = name->fetch_aaaa;
3672135446Strhodes		name->fetch_aaaa = NULL;
3673186462Sdougb	} else
3674186462Sdougb		fetch = NULL;
3675135446Strhodes
3676186462Sdougb	INSIST(address_type != 0 && fetch != NULL);
3677186462Sdougb
3678135446Strhodes	dns_resolver_destroyfetch(&fetch->fetch);
3679135446Strhodes	dev->fetch = NULL;
3680135446Strhodes
3681135446Strhodes	ev_status = DNS_EVENT_ADBNOMOREADDRESSES;
3682135446Strhodes
3683135446Strhodes	/*
3684135446Strhodes	 * Cleanup things we don't care about.
3685135446Strhodes	 */
3686135446Strhodes	if (dev->node != NULL)
3687135446Strhodes		dns_db_detachnode(dev->db, &dev->node);
3688135446Strhodes	if (dev->db != NULL)
3689135446Strhodes		dns_db_detach(&dev->db);
3690135446Strhodes
3691135446Strhodes	/*
3692135446Strhodes	 * If this name is marked as dead, clean up, throwing away
3693135446Strhodes	 * potentially good data.
3694135446Strhodes	 */
3695135446Strhodes	if (NAME_DEAD(name)) {
3696135446Strhodes		free_adbfetch(adb, &fetch);
3697135446Strhodes		isc_event_free(&ev);
3698135446Strhodes
3699135446Strhodes		want_check_exit = kill_name(&name, DNS_EVENT_ADBCANCELED);
3700135446Strhodes
3701135446Strhodes		UNLOCK(&adb->namelocks[bucket]);
3702135446Strhodes
3703135446Strhodes		if (want_check_exit) {
3704135446Strhodes			LOCK(&adb->lock);
3705135446Strhodes			check_exit(adb);
3706135446Strhodes			UNLOCK(&adb->lock);
3707135446Strhodes		}
3708135446Strhodes
3709135446Strhodes		return;
3710135446Strhodes	}
3711135446Strhodes
3712135446Strhodes	isc_stdtime_get(&now);
3713135446Strhodes
3714135446Strhodes	/*
3715135446Strhodes	 * If we got a negative cache response, remember it.
3716135446Strhodes	 */
3717135446Strhodes	if (NCACHE_RESULT(dev->result)) {
3718135446Strhodes		dev->rdataset->ttl = ttlclamp(dev->rdataset->ttl);
3719135446Strhodes		if (address_type == DNS_ADBFIND_INET) {
3720135446Strhodes			DP(NCACHE_LEVEL, "adb fetch name %p: "
3721135446Strhodes			   "caching negative entry for A (ttl %u)",
3722135446Strhodes			   name, dev->rdataset->ttl);
3723135446Strhodes			name->expire_v4 = ISC_MIN(name->expire_v4,
3724135446Strhodes						  dev->rdataset->ttl + now);
3725135446Strhodes			if (dev->result == DNS_R_NCACHENXDOMAIN)
3726135446Strhodes				name->fetch_err = FIND_ERR_NXDOMAIN;
3727135446Strhodes			else
3728135446Strhodes				name->fetch_err = FIND_ERR_NXRRSET;
3729193149Sdougb			inc_stats(adb, dns_resstatscounter_gluefetchv4fail);
3730135446Strhodes		} else {
3731135446Strhodes			DP(NCACHE_LEVEL, "adb fetch name %p: "
3732135446Strhodes			   "caching negative entry for AAAA (ttl %u)",
3733135446Strhodes			   name, dev->rdataset->ttl);
3734135446Strhodes			name->expire_v6 = ISC_MIN(name->expire_v6,
3735135446Strhodes						  dev->rdataset->ttl + now);
3736135446Strhodes			if (dev->result == DNS_R_NCACHENXDOMAIN)
3737135446Strhodes				name->fetch6_err = FIND_ERR_NXDOMAIN;
3738135446Strhodes			else
3739135446Strhodes				name->fetch6_err = FIND_ERR_NXRRSET;
3740193149Sdougb			inc_stats(adb, dns_resstatscounter_gluefetchv6fail);
3741135446Strhodes		}
3742135446Strhodes		goto out;
3743135446Strhodes	}
3744135446Strhodes
3745135446Strhodes	/*
3746135446Strhodes	 * Handle CNAME/DNAME.
3747135446Strhodes	 */
3748135446Strhodes	if (dev->result == DNS_R_CNAME || dev->result == DNS_R_DNAME) {
3749135446Strhodes		dev->rdataset->ttl = ttlclamp(dev->rdataset->ttl);
3750135446Strhodes		clean_target(adb, &name->target);
3751135446Strhodes		name->expire_target = INT_MAX;
3752135446Strhodes		result = set_target(adb, &name->name,
3753135446Strhodes				    dns_fixedname_name(&dev->foundname),
3754135446Strhodes				    dev->rdataset,
3755135446Strhodes				    &name->target);
3756135446Strhodes		if (result == ISC_R_SUCCESS) {
3757135446Strhodes			DP(NCACHE_LEVEL,
3758135446Strhodes			   "adb fetch name %p: caching alias target",
3759135446Strhodes			   name);
3760135446Strhodes			name->expire_target = dev->rdataset->ttl + now;
3761135446Strhodes		}
3762135446Strhodes		goto check_result;
3763135446Strhodes	}
3764135446Strhodes
3765135446Strhodes	/*
3766135446Strhodes	 * Did we get back junk?  If so, and there are no more fetches
3767135446Strhodes	 * sitting out there, tell all the finds about it.
3768135446Strhodes	 */
3769135446Strhodes	if (dev->result != ISC_R_SUCCESS) {
3770135446Strhodes		char buf[DNS_NAME_FORMATSIZE];
3771135446Strhodes
3772135446Strhodes		dns_name_format(&name->name, buf, sizeof(buf));
3773135446Strhodes		DP(DEF_LEVEL, "adb: fetch of '%s' %s failed: %s",
3774135446Strhodes		   buf, address_type == DNS_ADBFIND_INET ? "A" : "AAAA",
3775135446Strhodes		   dns_result_totext(dev->result));
3776275672Sdelphij		/*
3777275672Sdelphij		 * Don't record a failure unless this is the initial
3778275672Sdelphij		 * fetch of a chain.
3779275672Sdelphij		 */
3780275672Sdelphij		if (fetch->depth > 1)
3781275672Sdelphij			goto out;
3782135446Strhodes		/* XXXMLG Don't pound on bad servers. */
3783135446Strhodes		if (address_type == DNS_ADBFIND_INET) {
3784135446Strhodes			name->expire_v4 = ISC_MIN(name->expire_v4, now + 300);
3785135446Strhodes			name->fetch_err = FIND_ERR_FAILURE;
3786193149Sdougb			inc_stats(adb, dns_resstatscounter_gluefetchv4fail);
3787135446Strhodes		} else {
3788135446Strhodes			name->expire_v6 = ISC_MIN(name->expire_v6, now + 300);
3789135446Strhodes			name->fetch6_err = FIND_ERR_FAILURE;
3790193149Sdougb			inc_stats(adb, dns_resstatscounter_gluefetchv6fail);
3791135446Strhodes		}
3792135446Strhodes		goto out;
3793135446Strhodes	}
3794135446Strhodes
3795135446Strhodes	/*
3796135446Strhodes	 * We got something potentially useful.
3797135446Strhodes	 */
3798135446Strhodes	result = import_rdataset(name, &fetch->rdataset, now);
3799135446Strhodes
3800135446Strhodes check_result:
3801135446Strhodes	if (result == ISC_R_SUCCESS) {
3802135446Strhodes		ev_status = DNS_EVENT_ADBMOREADDRESSES;
3803135446Strhodes		if (address_type == DNS_ADBFIND_INET)
3804135446Strhodes			name->fetch_err = FIND_ERR_SUCCESS;
3805135446Strhodes		else
3806135446Strhodes			name->fetch6_err = FIND_ERR_SUCCESS;
3807135446Strhodes	}
3808135446Strhodes
3809135446Strhodes out:
3810135446Strhodes	free_adbfetch(adb, &fetch);
3811135446Strhodes	isc_event_free(&ev);
3812135446Strhodes
3813275672Sdelphij	clean_finds_at_name(name, ev_status, qtotal, address_type);
3814135446Strhodes
3815135446Strhodes	UNLOCK(&adb->namelocks[bucket]);
3816135446Strhodes}
3817135446Strhodes
3818135446Strhodesstatic isc_result_t
3819275672Sdelphijfetch_name(dns_adbname_t *adbname, isc_boolean_t start_at_zone,
3820275672Sdelphij	   unsigned int depth, dns_rdatatype_t type)
3821135446Strhodes{
3822135446Strhodes	isc_result_t result;
3823135446Strhodes	dns_adbfetch_t *fetch = NULL;
3824135446Strhodes	dns_adb_t *adb;
3825135446Strhodes	dns_fixedname_t fixed;
3826135446Strhodes	dns_name_t *name;
3827135446Strhodes	dns_rdataset_t rdataset;
3828135446Strhodes	dns_rdataset_t *nameservers;
3829135446Strhodes	unsigned int options;
3830135446Strhodes
3831135446Strhodes	INSIST(DNS_ADBNAME_VALID(adbname));
3832135446Strhodes	adb = adbname->adb;
3833135446Strhodes	INSIST(DNS_ADB_VALID(adb));
3834135446Strhodes
3835135446Strhodes	INSIST((type == dns_rdatatype_a && !NAME_FETCH_V4(adbname)) ||
3836135446Strhodes	       (type == dns_rdatatype_aaaa && !NAME_FETCH_V6(adbname)));
3837135446Strhodes
3838135446Strhodes	adbname->fetch_err = FIND_ERR_NOTFOUND;
3839135446Strhodes
3840135446Strhodes	name = NULL;
3841135446Strhodes	nameservers = NULL;
3842135446Strhodes	dns_rdataset_init(&rdataset);
3843135446Strhodes
3844135446Strhodes	options = DNS_FETCHOPT_NOVALIDATE;
3845135446Strhodes	if (start_at_zone) {
3846135446Strhodes		DP(ENTER_LEVEL,
3847135446Strhodes		   "fetch_name: starting at zone for name %p",
3848135446Strhodes		   adbname);
3849135446Strhodes		dns_fixedname_init(&fixed);
3850135446Strhodes		name = dns_fixedname_name(&fixed);
3851135446Strhodes		result = dns_view_findzonecut2(adb->view, &adbname->name, name,
3852135446Strhodes					       0, 0, ISC_TRUE, ISC_FALSE,
3853135446Strhodes					       &rdataset, NULL);
3854135446Strhodes		if (result != ISC_R_SUCCESS && result != DNS_R_HINT)
3855135446Strhodes			goto cleanup;
3856135446Strhodes		nameservers = &rdataset;
3857135446Strhodes		options |= DNS_FETCHOPT_UNSHARED;
3858135446Strhodes	}
3859135446Strhodes
3860135446Strhodes	fetch = new_adbfetch(adb);
3861135446Strhodes	if (fetch == NULL) {
3862135446Strhodes		result = ISC_R_NOMEMORY;
3863135446Strhodes		goto cleanup;
3864135446Strhodes	}
3865275672Sdelphij	fetch->depth = depth;
3866135446Strhodes
3867275672Sdelphij	result = dns_resolver_createfetch3(adb->view->resolver, &adbname->name,
3868275672Sdelphij					   type, name, nameservers, NULL,
3869275672Sdelphij					   NULL, 0, options, depth, adb->task,
3870275672Sdelphij					   fetch_callback, adbname,
3871275672Sdelphij					   &fetch->rdataset, NULL,
3872275672Sdelphij					   &fetch->fetch);
3873135446Strhodes	if (result != ISC_R_SUCCESS)
3874135446Strhodes		goto cleanup;
3875135446Strhodes
3876193149Sdougb	if (type == dns_rdatatype_a) {
3877135446Strhodes		adbname->fetch_a = fetch;
3878193149Sdougb		inc_stats(adb, dns_resstatscounter_gluefetchv4);
3879193149Sdougb	} else {
3880135446Strhodes		adbname->fetch_aaaa = fetch;
3881193149Sdougb		inc_stats(adb, dns_resstatscounter_gluefetchv6);
3882193149Sdougb	}
3883135446Strhodes	fetch = NULL;  /* Keep us from cleaning this up below. */
3884135446Strhodes
3885135446Strhodes cleanup:
3886135446Strhodes	if (fetch != NULL)
3887135446Strhodes		free_adbfetch(adb, &fetch);
3888135446Strhodes	if (dns_rdataset_isassociated(&rdataset))
3889135446Strhodes		dns_rdataset_disassociate(&rdataset);
3890135446Strhodes
3891135446Strhodes	return (result);
3892135446Strhodes}
3893135446Strhodes
3894135446Strhodes/*
3895135446Strhodes * XXXMLG Needs to take a find argument and an address info, no zone or adb,
3896135446Strhodes * since these can be extracted from the find itself.
3897135446Strhodes */
3898135446Strhodesisc_result_t
3899170222Sdougbdns_adb_marklame(dns_adb_t *adb, dns_adbaddrinfo_t *addr, dns_name_t *qname,
3900170222Sdougb		 dns_rdatatype_t qtype, isc_stdtime_t expire_time)
3901135446Strhodes{
3902170222Sdougb	dns_adblameinfo_t *li;
3903135446Strhodes	int bucket;
3904135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
3905135446Strhodes
3906135446Strhodes	REQUIRE(DNS_ADB_VALID(adb));
3907135446Strhodes	REQUIRE(DNS_ADBADDRINFO_VALID(addr));
3908170222Sdougb	REQUIRE(qname != NULL);
3909135446Strhodes
3910135446Strhodes	bucket = addr->entry->lock_bucket;
3911135446Strhodes	LOCK(&adb->entrylocks[bucket]);
3912170222Sdougb	li = ISC_LIST_HEAD(addr->entry->lameinfo);
3913170222Sdougb	while (li != NULL &&
3914170222Sdougb	       (li->qtype != qtype || !dns_name_equal(qname, &li->qname)))
3915170222Sdougb		li = ISC_LIST_NEXT(li, plink);
3916170222Sdougb	if (li != NULL) {
3917170222Sdougb		if (expire_time > li->lame_timer)
3918170222Sdougb			li->lame_timer = expire_time;
3919135446Strhodes		goto unlock;
3920135446Strhodes	}
3921170222Sdougb	li = new_adblameinfo(adb, qname, qtype);
3922170222Sdougb	if (li == NULL) {
3923135446Strhodes		result = ISC_R_NOMEMORY;
3924135446Strhodes		goto unlock;
3925135446Strhodes	}
3926135446Strhodes
3927170222Sdougb	li->lame_timer = expire_time;
3928135446Strhodes
3929170222Sdougb	ISC_LIST_PREPEND(addr->entry->lameinfo, li, plink);
3930135446Strhodes unlock:
3931135446Strhodes	UNLOCK(&adb->entrylocks[bucket]);
3932135446Strhodes
3933153816Sdougb	return (result);
3934135446Strhodes}
3935135446Strhodes
3936135446Strhodesvoid
3937135446Strhodesdns_adb_adjustsrtt(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
3938135446Strhodes		   unsigned int rtt, unsigned int factor)
3939135446Strhodes{
3940135446Strhodes	int bucket;
3941135446Strhodes	unsigned int new_srtt;
3942135446Strhodes	isc_stdtime_t now;
3943135446Strhodes
3944135446Strhodes	REQUIRE(DNS_ADB_VALID(adb));
3945135446Strhodes	REQUIRE(DNS_ADBADDRINFO_VALID(addr));
3946135446Strhodes	REQUIRE(factor <= 10);
3947135446Strhodes
3948135446Strhodes	bucket = addr->entry->lock_bucket;
3949135446Strhodes	LOCK(&adb->entrylocks[bucket]);
3950135446Strhodes
3951135446Strhodes	if (factor == DNS_ADB_RTTADJAGE)
3952135446Strhodes		new_srtt = addr->entry->srtt * 98 / 100;
3953135446Strhodes	else
3954135446Strhodes		new_srtt = (addr->entry->srtt / 10 * factor)
3955135446Strhodes			+ (rtt / 10 * (10 - factor));
3956135446Strhodes
3957135446Strhodes	addr->entry->srtt = new_srtt;
3958135446Strhodes	addr->srtt = new_srtt;
3959135446Strhodes
3960245163Serwin	if (addr->entry->expires == 0) {
3961245163Serwin		isc_stdtime_get(&now);
3962245163Serwin		addr->entry->expires = now + ADB_ENTRY_WINDOW;
3963245163Serwin	}
3964135446Strhodes
3965135446Strhodes	UNLOCK(&adb->entrylocks[bucket]);
3966135446Strhodes}
3967135446Strhodes
3968135446Strhodesvoid
3969135446Strhodesdns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
3970135446Strhodes		    unsigned int bits, unsigned int mask)
3971135446Strhodes{
3972135446Strhodes	int bucket;
3973245163Serwin	isc_stdtime_t now;
3974135446Strhodes
3975135446Strhodes	REQUIRE(DNS_ADB_VALID(adb));
3976135446Strhodes	REQUIRE(DNS_ADBADDRINFO_VALID(addr));
3977135446Strhodes
3978135446Strhodes	bucket = addr->entry->lock_bucket;
3979135446Strhodes	LOCK(&adb->entrylocks[bucket]);
3980135446Strhodes
3981135446Strhodes	addr->entry->flags = (addr->entry->flags & ~mask) | (bits & mask);
3982245163Serwin	if (addr->entry->expires == 0) {
3983245163Serwin		isc_stdtime_get(&now);
3984245163Serwin		addr->entry->expires = now + ADB_ENTRY_WINDOW;
3985245163Serwin	}
3986245163Serwin
3987135446Strhodes	/*
3988135446Strhodes	 * Note that we do not update the other bits in addr->flags with
3989135446Strhodes	 * the most recent values from addr->entry->flags.
3990135446Strhodes	 */
3991135446Strhodes	addr->flags = (addr->flags & ~mask) | (bits & mask);
3992135446Strhodes
3993135446Strhodes	UNLOCK(&adb->entrylocks[bucket]);
3994135446Strhodes}
3995135446Strhodes
3996135446Strhodesisc_result_t
3997135446Strhodesdns_adb_findaddrinfo(dns_adb_t *adb, isc_sockaddr_t *sa,
3998135446Strhodes		     dns_adbaddrinfo_t **addrp, isc_stdtime_t now)
3999135446Strhodes{
4000135446Strhodes	int bucket;
4001135446Strhodes	dns_adbentry_t *entry;
4002135446Strhodes	dns_adbaddrinfo_t *addr;
4003135446Strhodes	isc_result_t result;
4004135446Strhodes	in_port_t port;
4005135446Strhodes
4006135446Strhodes	REQUIRE(DNS_ADB_VALID(adb));
4007135446Strhodes	REQUIRE(addrp != NULL && *addrp == NULL);
4008135446Strhodes
4009135446Strhodes	UNUSED(now);
4010135446Strhodes
4011135446Strhodes	result = ISC_R_SUCCESS;
4012135446Strhodes	bucket = DNS_ADB_INVALIDBUCKET;
4013193149Sdougb	entry = find_entry_and_lock(adb, sa, &bucket, now);
4014225361Sdougb	INSIST(bucket != DNS_ADB_INVALIDBUCKET);
4015135446Strhodes	if (adb->entry_sd[bucket]) {
4016135446Strhodes		result = ISC_R_SHUTTINGDOWN;
4017135446Strhodes		goto unlock;
4018135446Strhodes	}
4019135446Strhodes	if (entry == NULL) {
4020135446Strhodes		/*
4021135446Strhodes		 * We don't know anything about this address.
4022135446Strhodes		 */
4023135446Strhodes		entry = new_adbentry(adb);
4024135446Strhodes		if (entry == NULL) {
4025135446Strhodes			result = ISC_R_NOMEMORY;
4026135446Strhodes			goto unlock;
4027135446Strhodes		}
4028135446Strhodes		entry->sockaddr = *sa;
4029135446Strhodes		link_entry(adb, bucket, entry);
4030135446Strhodes		DP(ENTER_LEVEL, "findaddrinfo: new entry %p", entry);
4031135446Strhodes	} else
4032135446Strhodes		DP(ENTER_LEVEL, "findaddrinfo: found entry %p", entry);
4033135446Strhodes
4034135446Strhodes	port = isc_sockaddr_getport(sa);
4035135446Strhodes	addr = new_adbaddrinfo(adb, entry, port);
4036174187Sdougb	if (addr == NULL) {
4037174187Sdougb		result = ISC_R_NOMEMORY;
4038174187Sdougb	} else {
4039135446Strhodes		inc_entry_refcnt(adb, entry, ISC_FALSE);
4040135446Strhodes		*addrp = addr;
4041135446Strhodes	}
4042135446Strhodes
4043135446Strhodes unlock:
4044135446Strhodes	UNLOCK(&adb->entrylocks[bucket]);
4045135446Strhodes
4046135446Strhodes	return (result);
4047135446Strhodes}
4048135446Strhodes
4049135446Strhodesvoid
4050135446Strhodesdns_adb_freeaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **addrp) {
4051135446Strhodes	dns_adbaddrinfo_t *addr;
4052135446Strhodes	dns_adbentry_t *entry;
4053135446Strhodes	int bucket;
4054135446Strhodes	isc_stdtime_t now;
4055135446Strhodes	isc_boolean_t want_check_exit = ISC_FALSE;
4056214586Sdougb	isc_boolean_t overmem;
4057135446Strhodes
4058135446Strhodes	REQUIRE(DNS_ADB_VALID(adb));
4059135446Strhodes	REQUIRE(addrp != NULL);
4060135446Strhodes	addr = *addrp;
4061135446Strhodes	REQUIRE(DNS_ADBADDRINFO_VALID(addr));
4062135446Strhodes	entry = addr->entry;
4063135446Strhodes	REQUIRE(DNS_ADBENTRY_VALID(entry));
4064135446Strhodes
4065135446Strhodes	*addrp = NULL;
4066214586Sdougb	overmem = isc_mem_isovermem(adb->mctx);
4067135446Strhodes
4068135446Strhodes	bucket = addr->entry->lock_bucket;
4069135446Strhodes	LOCK(&adb->entrylocks[bucket]);
4070135446Strhodes
4071245163Serwin	if (entry->expires == 0) {
4072245163Serwin		isc_stdtime_get(&now);
4073245163Serwin		entry->expires = now + ADB_ENTRY_WINDOW;
4074245163Serwin	}
4075135446Strhodes
4076214586Sdougb	want_check_exit = dec_entry_refcnt(adb, overmem, entry, ISC_FALSE);
4077135446Strhodes
4078135446Strhodes	UNLOCK(&adb->entrylocks[bucket]);
4079135446Strhodes
4080135446Strhodes	addr->entry = NULL;
4081135446Strhodes	free_adbaddrinfo(adb, &addr);
4082135446Strhodes
4083135446Strhodes	if (want_check_exit) {
4084135446Strhodes		LOCK(&adb->lock);
4085135446Strhodes		check_exit(adb);
4086135446Strhodes		UNLOCK(&adb->lock);
4087135446Strhodes	}
4088135446Strhodes}
4089135446Strhodes
4090135446Strhodesvoid
4091135446Strhodesdns_adb_flush(dns_adb_t *adb) {
4092135446Strhodes	unsigned int i;
4093135446Strhodes
4094135446Strhodes	INSIST(DNS_ADB_VALID(adb));
4095135446Strhodes
4096135446Strhodes	LOCK(&adb->lock);
4097135446Strhodes
4098143731Sdougb	/*
4099143731Sdougb	 * Call our cleanup routines.
4100143731Sdougb	 */
4101224092Sdougb	for (i = 0; i < adb->nnames; i++)
4102135446Strhodes		RUNTIME_CHECK(cleanup_names(adb, i, INT_MAX) == ISC_FALSE);
4103224092Sdougb	for (i = 0; i < adb->nentries; i++)
4104135446Strhodes		RUNTIME_CHECK(cleanup_entries(adb, i, INT_MAX) == ISC_FALSE);
4105135446Strhodes
4106135446Strhodes#ifdef DUMP_ADB_AFTER_CLEANING
4107143731Sdougb	dump_adb(adb, stdout, ISC_TRUE, INT_MAX);
4108135446Strhodes#endif
4109135446Strhodes
4110135446Strhodes	UNLOCK(&adb->lock);
4111135446Strhodes}
4112135446Strhodes
4113135446Strhodesvoid
4114135446Strhodesdns_adb_flushname(dns_adb_t *adb, dns_name_t *name) {
4115135446Strhodes	dns_adbname_t *adbname;
4116135446Strhodes	dns_adbname_t *nextname;
4117135446Strhodes	int bucket;
4118135446Strhodes
4119135446Strhodes	INSIST(DNS_ADB_VALID(adb));
4120135446Strhodes
4121135446Strhodes	LOCK(&adb->lock);
4122224092Sdougb	bucket = dns_name_hash(name, ISC_FALSE) % adb->nnames;
4123135446Strhodes	LOCK(&adb->namelocks[bucket]);
4124135446Strhodes	adbname = ISC_LIST_HEAD(adb->names[bucket]);
4125135446Strhodes	while (adbname != NULL) {
4126135446Strhodes		nextname = ISC_LIST_NEXT(adbname, plink);
4127135446Strhodes		if (!NAME_DEAD(adbname) &&
4128135446Strhodes		    dns_name_equal(name, &adbname->name)) {
4129135446Strhodes			RUNTIME_CHECK(kill_name(&adbname,
4130135446Strhodes						DNS_EVENT_ADBCANCELED) ==
4131135446Strhodes				      ISC_FALSE);
4132135446Strhodes		}
4133135446Strhodes		adbname = nextname;
4134135446Strhodes	}
4135135446Strhodes	UNLOCK(&adb->namelocks[bucket]);
4136135446Strhodes	UNLOCK(&adb->lock);
4137135446Strhodes}
4138135446Strhodes
4139135446Strhodesstatic void
4140135446Strhodeswater(void *arg, int mark) {
4141214586Sdougb	/*
4142214586Sdougb	 * We're going to change the way to handle overmem condition: use
4143214586Sdougb	 * isc_mem_isovermem() instead of storing the state via this callback,
4144214586Sdougb	 * since the latter way tends to cause race conditions.
4145214586Sdougb	 * To minimize the change, and in case we re-enable the callback
4146214586Sdougb	 * approach, however, keep this function at the moment.
4147214586Sdougb	 */
4148214586Sdougb
4149135446Strhodes	dns_adb_t *adb = arg;
4150135446Strhodes	isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER);
4151135446Strhodes
4152135446Strhodes	REQUIRE(DNS_ADB_VALID(adb));
4153135446Strhodes
4154135446Strhodes	DP(ISC_LOG_DEBUG(1),
4155135446Strhodes	   "adb reached %s water mark", overmem ? "high" : "low");
4156135446Strhodes}
4157135446Strhodes
4158135446Strhodesvoid
4159254897Serwindns_adb_setadbsize(dns_adb_t *adb, size_t size) {
4160254897Serwin	size_t hiwater, lowater;
4161135446Strhodes
4162135446Strhodes	INSIST(DNS_ADB_VALID(adb));
4163135446Strhodes
4164254402Serwin	if (size != 0U && size < DNS_ADB_MINADBSIZE)
4165135446Strhodes		size = DNS_ADB_MINADBSIZE;
4166135446Strhodes
4167135446Strhodes	hiwater = size - (size >> 3);   /* Approximately 7/8ths. */
4168135446Strhodes	lowater = size - (size >> 2);   /* Approximately 3/4ths. */
4169135446Strhodes
4170254402Serwin	if (size == 0U || hiwater == 0U || lowater == 0U)
4171135446Strhodes		isc_mem_setwater(adb->mctx, water, adb, 0, 0);
4172135446Strhodes	else
4173135446Strhodes		isc_mem_setwater(adb->mctx, water, adb, hiwater, lowater);
4174135446Strhodes}
4175