adb.c revision 254402
1135446Strhodes/*
2254402Serwin * Copyright (C) 2004-2013  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
18234010Sdougb/* $Id$ */
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;
204135446Strhodes};
205135446Strhodes
206170222Sdougb/*%
207135446Strhodes * This is a small widget that dangles off a dns_adbname_t.  It contains a
208135446Strhodes * pointer to the address information about this host, and a link to the next
209135446Strhodes * namehook that will contain the next address this host has.
210135446Strhodes */
211135446Strhodesstruct dns_adbnamehook {
212193149Sdougb	unsigned int                    magic;
213193149Sdougb	dns_adbentry_t                 *entry;
214193149Sdougb	ISC_LINK(dns_adbnamehook_t)     plink;
215135446Strhodes};
216135446Strhodes
217170222Sdougb/*%
218170222Sdougb * This is a small widget that holds qname-specific information about an
219135446Strhodes * address.  Currently limited to lameness, but could just as easily be
220135446Strhodes * extended to other types of information about zones.
221135446Strhodes */
222170222Sdougbstruct dns_adblameinfo {
223193149Sdougb	unsigned int                    magic;
224135446Strhodes
225193149Sdougb	dns_name_t                      qname;
226193149Sdougb	dns_rdatatype_t                 qtype;
227193149Sdougb	isc_stdtime_t                   lame_timer;
228135446Strhodes
229193149Sdougb	ISC_LINK(dns_adblameinfo_t)     plink;
230135446Strhodes};
231135446Strhodes
232170222Sdougb/*%
233135446Strhodes * An address entry.  It holds quite a bit of information about addresses,
234135446Strhodes * including edns state (in "flags"), rtt, and of course the address of
235135446Strhodes * the host.
236135446Strhodes */
237135446Strhodesstruct dns_adbentry {
238193149Sdougb	unsigned int                    magic;
239135446Strhodes
240193149Sdougb	int                             lock_bucket;
241193149Sdougb	unsigned int                    refcnt;
242135446Strhodes
243193149Sdougb	unsigned int                    flags;
244193149Sdougb	unsigned int                    srtt;
245193149Sdougb	isc_sockaddr_t                  sockaddr;
246135446Strhodes
247193149Sdougb	isc_stdtime_t                   expires;
248170222Sdougb	/*%<
249135446Strhodes	 * A nonzero 'expires' field indicates that the entry should
250135446Strhodes	 * persist until that time.  This allows entries found
251135446Strhodes	 * using dns_adb_findaddrinfo() to persist for a limited time
252135446Strhodes	 * even though they are not necessarily associated with a
253135446Strhodes	 * name.
254135446Strhodes	 */
255135446Strhodes
256193149Sdougb	ISC_LIST(dns_adblameinfo_t)     lameinfo;
257193149Sdougb	ISC_LINK(dns_adbentry_t)        plink;
258234010Sdougb
259135446Strhodes};
260135446Strhodes
261135446Strhodes/*
262135446Strhodes * Internal functions (and prototypes).
263135446Strhodes */
264135446Strhodesstatic inline dns_adbname_t *new_adbname(dns_adb_t *, dns_name_t *);
265135446Strhodesstatic inline void free_adbname(dns_adb_t *, dns_adbname_t **);
266135446Strhodesstatic inline dns_adbnamehook_t *new_adbnamehook(dns_adb_t *,
267135446Strhodes						 dns_adbentry_t *);
268135446Strhodesstatic inline void free_adbnamehook(dns_adb_t *, dns_adbnamehook_t **);
269170222Sdougbstatic inline dns_adblameinfo_t *new_adblameinfo(dns_adb_t *, dns_name_t *,
270170222Sdougb						 dns_rdatatype_t);
271170222Sdougbstatic inline void free_adblameinfo(dns_adb_t *, dns_adblameinfo_t **);
272135446Strhodesstatic inline dns_adbentry_t *new_adbentry(dns_adb_t *);
273135446Strhodesstatic inline void free_adbentry(dns_adb_t *, dns_adbentry_t **);
274135446Strhodesstatic inline dns_adbfind_t *new_adbfind(dns_adb_t *);
275135446Strhodesstatic inline isc_boolean_t free_adbfind(dns_adb_t *, dns_adbfind_t **);
276135446Strhodesstatic inline dns_adbaddrinfo_t *new_adbaddrinfo(dns_adb_t *, dns_adbentry_t *,
277135446Strhodes						 in_port_t);
278135446Strhodesstatic inline dns_adbfetch_t *new_adbfetch(dns_adb_t *);
279135446Strhodesstatic inline void free_adbfetch(dns_adb_t *, dns_adbfetch_t **);
280135446Strhodesstatic inline dns_adbname_t *find_name_and_lock(dns_adb_t *, dns_name_t *,
281135446Strhodes						unsigned int, int *);
282135446Strhodesstatic inline dns_adbentry_t *find_entry_and_lock(dns_adb_t *,
283193149Sdougb						  isc_sockaddr_t *, int *,
284193149Sdougb						  isc_stdtime_t);
285143731Sdougbstatic void dump_adb(dns_adb_t *, FILE *, isc_boolean_t debug, isc_stdtime_t);
286135446Strhodesstatic void print_dns_name(FILE *, dns_name_t *);
287135446Strhodesstatic void print_namehook_list(FILE *, const char *legend,
288135446Strhodes				dns_adbnamehooklist_t *list,
289135446Strhodes				isc_boolean_t debug,
290135446Strhodes				isc_stdtime_t now);
291135446Strhodesstatic void print_find_list(FILE *, dns_adbname_t *);
292135446Strhodesstatic void print_fetch_list(FILE *, dns_adbname_t *);
293135446Strhodesstatic inline isc_boolean_t dec_adb_irefcnt(dns_adb_t *);
294135446Strhodesstatic inline void inc_adb_irefcnt(dns_adb_t *);
295135446Strhodesstatic inline void inc_adb_erefcnt(dns_adb_t *);
296135446Strhodesstatic inline void inc_entry_refcnt(dns_adb_t *, dns_adbentry_t *,
297135446Strhodes				    isc_boolean_t);
298214586Sdougbstatic inline isc_boolean_t dec_entry_refcnt(dns_adb_t *, isc_boolean_t,
299214586Sdougb					     dns_adbentry_t *, isc_boolean_t);
300135446Strhodesstatic inline void violate_locking_hierarchy(isc_mutex_t *, isc_mutex_t *);
301135446Strhodesstatic isc_boolean_t clean_namehooks(dns_adb_t *, dns_adbnamehooklist_t *);
302135446Strhodesstatic void clean_target(dns_adb_t *, dns_name_t *);
303135446Strhodesstatic void clean_finds_at_name(dns_adbname_t *, isc_eventtype_t,
304135446Strhodes				unsigned int);
305193149Sdougbstatic isc_boolean_t check_expire_namehooks(dns_adbname_t *, isc_stdtime_t);
306193149Sdougbstatic isc_boolean_t check_expire_entry(dns_adb_t *, dns_adbentry_t **,
307193149Sdougb					isc_stdtime_t);
308135446Strhodesstatic void cancel_fetches_at_name(dns_adbname_t *);
309135446Strhodesstatic isc_result_t dbfind_name(dns_adbname_t *, isc_stdtime_t,
310135446Strhodes				dns_rdatatype_t);
311135446Strhodesstatic isc_result_t fetch_name(dns_adbname_t *, isc_boolean_t,
312135446Strhodes			       dns_rdatatype_t);
313135446Strhodesstatic inline void check_exit(dns_adb_t *);
314135446Strhodesstatic void destroy(dns_adb_t *);
315135446Strhodesstatic isc_boolean_t shutdown_names(dns_adb_t *);
316135446Strhodesstatic isc_boolean_t shutdown_entries(dns_adb_t *);
317135446Strhodesstatic inline void link_name(dns_adb_t *, int, dns_adbname_t *);
318135446Strhodesstatic inline isc_boolean_t unlink_name(dns_adb_t *, dns_adbname_t *);
319135446Strhodesstatic inline void link_entry(dns_adb_t *, int, dns_adbentry_t *);
320135446Strhodesstatic inline isc_boolean_t unlink_entry(dns_adb_t *, dns_adbentry_t *);
321135446Strhodesstatic isc_boolean_t kill_name(dns_adbname_t **, isc_eventtype_t);
322143731Sdougbstatic void water(void *, int);
323143731Sdougbstatic void dump_entry(FILE *, dns_adbentry_t *, isc_boolean_t, isc_stdtime_t);
324135446Strhodes
325135446Strhodes/*
326135446Strhodes * MUST NOT overlap DNS_ADBFIND_* flags!
327135446Strhodes */
328193149Sdougb#define FIND_EVENT_SENT         0x40000000
329193149Sdougb#define FIND_EVENT_FREED        0x80000000
330193149Sdougb#define FIND_EVENTSENT(h)       (((h)->flags & FIND_EVENT_SENT) != 0)
331193149Sdougb#define FIND_EVENTFREED(h)      (((h)->flags & FIND_EVENT_FREED) != 0)
332135446Strhodes
333193149Sdougb#define NAME_NEEDS_POKE         0x80000000
334193149Sdougb#define NAME_IS_DEAD            0x40000000
335193149Sdougb#define NAME_HINT_OK            DNS_ADBFIND_HINTOK
336193149Sdougb#define NAME_GLUE_OK            DNS_ADBFIND_GLUEOK
337193149Sdougb#define NAME_STARTATZONE        DNS_ADBFIND_STARTATZONE
338193149Sdougb#define NAME_DEAD(n)            (((n)->flags & NAME_IS_DEAD) != 0)
339193149Sdougb#define NAME_NEEDSPOKE(n)       (((n)->flags & NAME_NEEDS_POKE) != 0)
340193149Sdougb#define NAME_GLUEOK(n)          (((n)->flags & NAME_GLUE_OK) != 0)
341193149Sdougb#define NAME_HINTOK(n)          (((n)->flags & NAME_HINT_OK) != 0)
342135446Strhodes
343135446Strhodes/*
344193149Sdougb * Private flag(s) for entries.
345193149Sdougb * MUST NOT overlap FCTX_ADDRINFO_xxx and DNS_FETCHOPT_NOEDNS0.
346193149Sdougb */
347193149Sdougb#define ENTRY_IS_DEAD		0x80000000
348193149Sdougb
349193149Sdougb/*
350135446Strhodes * To the name, address classes are all that really exist.  If it has a
351135446Strhodes * V6 address it doesn't care if it came from a AAAA query.
352135446Strhodes */
353193149Sdougb#define NAME_HAS_V4(n)          (!ISC_LIST_EMPTY((n)->v4))
354193149Sdougb#define NAME_HAS_V6(n)          (!ISC_LIST_EMPTY((n)->v6))
355193149Sdougb#define NAME_HAS_ADDRS(n)       (NAME_HAS_V4(n) || NAME_HAS_V6(n))
356135446Strhodes
357135446Strhodes/*
358135446Strhodes * Fetches are broken out into A and AAAA types.  In some cases,
359135446Strhodes * however, it makes more sense to test for a particular class of fetches,
360135446Strhodes * like V4 or V6 above.
361135446Strhodes * Note: since we have removed the support of A6 in adb, FETCH_A and FETCH_AAAA
362135446Strhodes * are now equal to FETCH_V4 and FETCH_V6, respectively.
363135446Strhodes */
364193149Sdougb#define NAME_FETCH_A(n)         ((n)->fetch_a != NULL)
365193149Sdougb#define NAME_FETCH_AAAA(n)      ((n)->fetch_aaaa != NULL)
366193149Sdougb#define NAME_FETCH_V4(n)        (NAME_FETCH_A(n))
367193149Sdougb#define NAME_FETCH_V6(n)        (NAME_FETCH_AAAA(n))
368193149Sdougb#define NAME_FETCH(n)           (NAME_FETCH_V4(n) || NAME_FETCH_V6(n))
369135446Strhodes
370135446Strhodes/*
371135446Strhodes * Find options and tests to see if there are addresses on the list.
372135446Strhodes */
373193149Sdougb#define FIND_WANTEVENT(fn)      (((fn)->options & DNS_ADBFIND_WANTEVENT) != 0)
374193149Sdougb#define FIND_WANTEMPTYEVENT(fn) (((fn)->options & DNS_ADBFIND_EMPTYEVENT) != 0)
375193149Sdougb#define FIND_AVOIDFETCHES(fn)   (((fn)->options & DNS_ADBFIND_AVOIDFETCHES) \
376135446Strhodes				 != 0)
377193149Sdougb#define FIND_STARTATZONE(fn)    (((fn)->options & DNS_ADBFIND_STARTATZONE) \
378135446Strhodes				 != 0)
379193149Sdougb#define FIND_HINTOK(fn)         (((fn)->options & DNS_ADBFIND_HINTOK) != 0)
380193149Sdougb#define FIND_GLUEOK(fn)         (((fn)->options & DNS_ADBFIND_GLUEOK) != 0)
381193149Sdougb#define FIND_HAS_ADDRS(fn)      (!ISC_LIST_EMPTY((fn)->list))
382193149Sdougb#define FIND_RETURNLAME(fn)     (((fn)->options & DNS_ADBFIND_RETURNLAME) != 0)
383135446Strhodes
384135446Strhodes/*
385135446Strhodes * These are currently used on simple unsigned ints, so they are
386135446Strhodes * not really associated with any particular type.
387135446Strhodes */
388193149Sdougb#define WANT_INET(x)            (((x) & DNS_ADBFIND_INET) != 0)
389193149Sdougb#define WANT_INET6(x)           (((x) & DNS_ADBFIND_INET6) != 0)
390135446Strhodes
391193149Sdougb#define EXPIRE_OK(exp, now)     ((exp == INT_MAX) || (exp < now))
392135446Strhodes
393135446Strhodes/*
394135446Strhodes * Find out if the flags on a name (nf) indicate if it is a hint or
395135446Strhodes * glue, and compare this to the appropriate bits set in o, to see if
396135446Strhodes * this is ok.
397135446Strhodes */
398135446Strhodes#define GLUE_OK(nf, o) (!NAME_GLUEOK(nf) || (((o) & DNS_ADBFIND_GLUEOK) != 0))
399135446Strhodes#define HINT_OK(nf, o) (!NAME_HINTOK(nf) || (((o) & DNS_ADBFIND_HINTOK) != 0))
400135446Strhodes#define GLUEHINT_OK(nf, o) (GLUE_OK(nf, o) || HINT_OK(nf, o))
401135446Strhodes#define STARTATZONE_MATCHES(nf, o) (((nf)->flags & NAME_STARTATZONE) == \
402135446Strhodes				    ((o) & DNS_ADBFIND_STARTATZONE))
403135446Strhodes
404193149Sdougb#define ENTER_LEVEL             ISC_LOG_DEBUG(50)
405193149Sdougb#define EXIT_LEVEL              ENTER_LEVEL
406193149Sdougb#define CLEAN_LEVEL             ISC_LOG_DEBUG(100)
407193149Sdougb#define DEF_LEVEL               ISC_LOG_DEBUG(5)
408193149Sdougb#define NCACHE_LEVEL            ISC_LOG_DEBUG(20)
409135446Strhodes
410193149Sdougb#define NCACHE_RESULT(r)        ((r) == DNS_R_NCACHENXDOMAIN || \
411135446Strhodes				 (r) == DNS_R_NCACHENXRRSET)
412193149Sdougb#define AUTH_NX(r)              ((r) == DNS_R_NXDOMAIN || \
413135446Strhodes				 (r) == DNS_R_NXRRSET)
414193149Sdougb#define NXDOMAIN_RESULT(r)      ((r) == DNS_R_NXDOMAIN || \
415135446Strhodes				 (r) == DNS_R_NCACHENXDOMAIN)
416193149Sdougb#define NXRRSET_RESULT(r)       ((r) == DNS_R_NCACHENXRRSET || \
417135446Strhodes				 (r) == DNS_R_NXRRSET || \
418135446Strhodes				 (r) == DNS_R_HINTNXRRSET)
419135446Strhodes
420135446Strhodes/*
421135446Strhodes * Error state rankings.
422135446Strhodes */
423135446Strhodes
424193149Sdougb#define FIND_ERR_SUCCESS                0  /* highest rank */
425193149Sdougb#define FIND_ERR_CANCELED               1
426193149Sdougb#define FIND_ERR_FAILURE                2
427193149Sdougb#define FIND_ERR_NXDOMAIN               3
428193149Sdougb#define FIND_ERR_NXRRSET                4
429193149Sdougb#define FIND_ERR_UNEXPECTED             5
430193149Sdougb#define FIND_ERR_NOTFOUND               6
431193149Sdougb#define FIND_ERR_MAX                    7
432135446Strhodes
433135446Strhodesstatic const char *errnames[] = {
434135446Strhodes	"success",
435135446Strhodes	"canceled",
436135446Strhodes	"failure",
437135446Strhodes	"nxdomain",
438135446Strhodes	"nxrrset",
439135446Strhodes	"unexpected",
440135446Strhodes	"not_found"
441135446Strhodes};
442135446Strhodes
443193149Sdougb#define NEWERR(old, new)        (ISC_MIN((old), (new)))
444135446Strhodes
445135446Strhodesstatic isc_result_t find_err_map[FIND_ERR_MAX] = {
446135446Strhodes	ISC_R_SUCCESS,
447135446Strhodes	ISC_R_CANCELED,
448135446Strhodes	ISC_R_FAILURE,
449135446Strhodes	DNS_R_NXDOMAIN,
450135446Strhodes	DNS_R_NXRRSET,
451135446Strhodes	ISC_R_UNEXPECTED,
452193149Sdougb	ISC_R_NOTFOUND          /* not YET found */
453135446Strhodes};
454135446Strhodes
455135446Strhodesstatic void
456135446StrhodesDP(int level, const char *format, ...) ISC_FORMAT_PRINTF(2, 3);
457135446Strhodes
458135446Strhodesstatic void
459135446StrhodesDP(int level, const char *format, ...) {
460135446Strhodes	va_list args;
461135446Strhodes
462135446Strhodes	va_start(args, format);
463135446Strhodes	isc_log_vwrite(dns_lctx,
464135446Strhodes		       DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ADB,
465135446Strhodes		       level, format, args);
466135446Strhodes	va_end(args);
467135446Strhodes}
468135446Strhodes
469193149Sdougb/*%
470193149Sdougb * Increment resolver-related statistics counters.
471193149Sdougb */
472193149Sdougbstatic inline void
473193149Sdougbinc_stats(dns_adb_t *adb, isc_statscounter_t counter) {
474193149Sdougb	if (adb->view->resstats != NULL)
475193149Sdougb		isc_stats_increment(adb->view->resstats, counter);
476193149Sdougb}
477193149Sdougb
478135446Strhodesstatic inline dns_ttl_t
479135446Strhodesttlclamp(dns_ttl_t ttl) {
480135446Strhodes	if (ttl < ADB_CACHE_MINIMUM)
481135446Strhodes		ttl = ADB_CACHE_MINIMUM;
482135446Strhodes	if (ttl > ADB_CACHE_MAXIMUM)
483135446Strhodes		ttl = ADB_CACHE_MAXIMUM;
484135446Strhodes
485135446Strhodes	return (ttl);
486135446Strhodes}
487135446Strhodes
488135446Strhodes/*
489224092Sdougb * Hashing is most efficient if the number of buckets is prime.
490224092Sdougb * The sequence below is the closest previous primes to 2^n and
491224092Sdougb * 1.5 * 2^n, for values of n from 10 to 28.  (The tables will
492224092Sdougb * no longer grow beyond 2^28 entries.)
493224092Sdougb */
494224092Sdougbstatic const unsigned nbuckets[] = { 1021, 1531, 2039, 3067, 4093, 6143,
495224092Sdougb				     8191, 12281, 16381, 24571, 32749,
496224092Sdougb				     49193, 65521, 98299, 131071, 199603,
497224092Sdougb				     262139, 393209, 524287, 768431, 1048573,
498224092Sdougb				     1572853, 2097143, 3145721, 4194301,
499224092Sdougb				     6291449, 8388593, 12582893, 16777213,
500224092Sdougb				     25165813, 33554393, 50331599, 67108859,
501224092Sdougb				     100663291, 134217689, 201326557,
502224092Sdougb				     268535431, 0 };
503224092Sdougb
504224092Sdougbstatic void
505224092Sdougbgrow_entries(isc_task_t *task, isc_event_t *ev) {
506224092Sdougb	dns_adb_t *adb;
507224092Sdougb	dns_adbentry_t *e;
508224092Sdougb	dns_adbentrylist_t *newdeadentries = NULL;
509224092Sdougb	dns_adbentrylist_t *newentries = NULL;
510224092Sdougb	isc_boolean_t *newentry_sd = NULL;
511224092Sdougb	isc_mutex_t *newentrylocks = NULL;
512224092Sdougb	isc_result_t result;
513224092Sdougb	unsigned int *newentry_refcnt = NULL;
514224092Sdougb	unsigned int i, n, bucket;
515224092Sdougb
516224092Sdougb	adb = ev->ev_arg;
517224092Sdougb	INSIST(DNS_ADB_VALID(adb));
518224092Sdougb
519224092Sdougb	isc_event_free(&ev);
520224092Sdougb
521254402Serwin	result = isc_task_beginexclusive(task);
522254402Serwin	if (result != ISC_R_SUCCESS)
523254402Serwin		goto check_exit;
524224092Sdougb
525224092Sdougb	i = 0;
526224092Sdougb	while (nbuckets[i] != 0 && adb->nentries >= nbuckets[i])
527224092Sdougb		i++;
528224092Sdougb	if (nbuckets[i] != 0)
529224092Sdougb		n = nbuckets[i];
530224092Sdougb	else
531224092Sdougb		goto done;
532224092Sdougb
533224092Sdougb	DP(ISC_LOG_INFO, "adb: grow_entries to %u starting", n);
534224092Sdougb
535224092Sdougb	/*
536224092Sdougb	 * Are we shutting down?
537224092Sdougb	 */
538224092Sdougb	for (i = 0; i < adb->nentries; i++)
539224092Sdougb		if (adb->entry_sd[i])
540224092Sdougb			goto cleanup;
541224092Sdougb
542224092Sdougb	/*
543224092Sdougb	 * Grab all the resources we need.
544224092Sdougb	 */
545224092Sdougb	newentries = isc_mem_get(adb->mctx, sizeof(*newentries) * n);
546224092Sdougb	newdeadentries = isc_mem_get(adb->mctx, sizeof(*newdeadentries) * n);
547224092Sdougb	newentrylocks = isc_mem_get(adb->mctx, sizeof(*newentrylocks) * n);
548224092Sdougb	newentry_sd = isc_mem_get(adb->mctx, sizeof(*newentry_sd) * n);
549224092Sdougb	newentry_refcnt = isc_mem_get(adb->mctx, sizeof(*newentry_refcnt) * n);
550224092Sdougb	if (newentries == NULL || newdeadentries == NULL ||
551224092Sdougb	    newentrylocks == NULL || newentry_sd == NULL ||
552224092Sdougb	    newentry_refcnt == NULL)
553224092Sdougb		goto cleanup;
554224092Sdougb
555224092Sdougb	/*
556224092Sdougb	 * Initialise the new resources.
557224092Sdougb	 */
558224092Sdougb	result = isc_mutexblock_init(newentrylocks, n);
559224092Sdougb	if (result != ISC_R_SUCCESS)
560224092Sdougb		goto cleanup;
561224092Sdougb
562224092Sdougb	for (i = 0; i < n; i++) {
563224092Sdougb		ISC_LIST_INIT(newentries[i]);
564224092Sdougb		ISC_LIST_INIT(newdeadentries[i]);
565224092Sdougb		newentry_sd[i] = ISC_FALSE;
566224092Sdougb		newentry_refcnt[i] = 0;
567224092Sdougb		adb->irefcnt++;
568224092Sdougb	}
569224092Sdougb
570224092Sdougb	/*
571224092Sdougb	 * Move entries to new arrays.
572224092Sdougb	 */
573224092Sdougb	for (i = 0; i < adb->nentries; i++) {
574224092Sdougb		e = ISC_LIST_HEAD(adb->entries[i]);
575224092Sdougb		while (e != NULL) {
576224092Sdougb			ISC_LIST_UNLINK(adb->entries[i], e, plink);
577224092Sdougb			bucket = isc_sockaddr_hash(&e->sockaddr, ISC_TRUE) % n;
578224092Sdougb			e->lock_bucket = bucket;
579224092Sdougb			ISC_LIST_APPEND(newentries[bucket], e, plink);
580224092Sdougb			INSIST(adb->entry_refcnt[i] > 0);
581224092Sdougb			adb->entry_refcnt[i]--;
582224092Sdougb			newentry_refcnt[bucket]++;
583224092Sdougb			e = ISC_LIST_HEAD(adb->entries[i]);
584224092Sdougb		}
585224092Sdougb		e = ISC_LIST_HEAD(adb->deadentries[i]);
586224092Sdougb		while (e != NULL) {
587224092Sdougb			ISC_LIST_UNLINK(adb->deadentries[i], e, plink);
588224092Sdougb			bucket = isc_sockaddr_hash(&e->sockaddr, ISC_TRUE) % n;
589224092Sdougb			e->lock_bucket = bucket;
590224092Sdougb			ISC_LIST_APPEND(newdeadentries[bucket], e, plink);
591224092Sdougb			INSIST(adb->entry_refcnt[i] > 0);
592224092Sdougb			adb->entry_refcnt[i]--;
593224092Sdougb			newentry_refcnt[bucket]++;
594224092Sdougb			e = ISC_LIST_HEAD(adb->deadentries[i]);
595224092Sdougb		}
596224092Sdougb		INSIST(adb->entry_refcnt[i] == 0);
597224092Sdougb		adb->irefcnt--;
598224092Sdougb	}
599224092Sdougb
600224092Sdougb	/*
601224092Sdougb	 * Cleanup old resources.
602224092Sdougb	 */
603224092Sdougb	DESTROYMUTEXBLOCK(adb->entrylocks, adb->nentries);
604224092Sdougb	isc_mem_put(adb->mctx, adb->entries,
605224092Sdougb		    sizeof(*adb->entries) * adb->nentries);
606224092Sdougb	isc_mem_put(adb->mctx, adb->deadentries,
607224092Sdougb		    sizeof(*adb->deadentries) * adb->nentries);
608224092Sdougb	isc_mem_put(adb->mctx, adb->entrylocks,
609224092Sdougb		    sizeof(*adb->entrylocks) * adb->nentries);
610224092Sdougb	isc_mem_put(adb->mctx, adb->entry_sd,
611224092Sdougb		    sizeof(*adb->entry_sd) * adb->nentries);
612224092Sdougb	isc_mem_put(adb->mctx, adb->entry_refcnt,
613224092Sdougb		    sizeof(*adb->entry_refcnt) * adb->nentries);
614224092Sdougb
615224092Sdougb	/*
616224092Sdougb	 * Install new resources.
617224092Sdougb	 */
618224092Sdougb	adb->entries = newentries;
619224092Sdougb	adb->deadentries = newdeadentries;
620224092Sdougb	adb->entrylocks = newentrylocks;
621224092Sdougb	adb->entry_sd = newentry_sd;
622224092Sdougb	adb->entry_refcnt = newentry_refcnt;
623224092Sdougb	adb->nentries = n;
624224092Sdougb
625224092Sdougb	/*
626224092Sdougb	 * Only on success do we set adb->growentries_sent to ISC_FALSE.
627224092Sdougb	 * This will prevent us being continuously being called on error.
628224092Sdougb	 */
629224092Sdougb	adb->growentries_sent = ISC_FALSE;
630224092Sdougb	goto done;
631224092Sdougb
632224092Sdougb cleanup:
633224092Sdougb	if (newentries != NULL)
634224092Sdougb		isc_mem_put(adb->mctx, newentries,
635224092Sdougb			    sizeof(*newentries) * n);
636224092Sdougb	if (newdeadentries != NULL)
637224092Sdougb		isc_mem_put(adb->mctx, newdeadentries,
638224092Sdougb			    sizeof(*newdeadentries) * n);
639224092Sdougb	if (newentrylocks != NULL)
640224092Sdougb		isc_mem_put(adb->mctx, newentrylocks,
641224092Sdougb			    sizeof(*newentrylocks) * n);
642224092Sdougb	if (newentry_sd != NULL)
643224092Sdougb		isc_mem_put(adb->mctx, newentry_sd,
644224092Sdougb			    sizeof(*newentry_sd) * n);
645224092Sdougb	if (newentry_refcnt != NULL)
646224092Sdougb		isc_mem_put(adb->mctx, newentry_refcnt,
647224092Sdougb			     sizeof(*newentry_refcnt) * n);
648224092Sdougb done:
649224092Sdougb	isc_task_endexclusive(task);
650224092Sdougb
651254402Serwin check_exit:
652224092Sdougb	LOCK(&adb->lock);
653224092Sdougb	if (dec_adb_irefcnt(adb))
654224092Sdougb		check_exit(adb);
655224092Sdougb	UNLOCK(&adb->lock);
656224092Sdougb	DP(ISC_LOG_INFO, "adb: grow_entries finished");
657224092Sdougb}
658224092Sdougb
659224092Sdougbstatic void
660224092Sdougbgrow_names(isc_task_t *task, isc_event_t *ev) {
661224092Sdougb	dns_adb_t *adb;
662224092Sdougb	dns_adbname_t *name;
663224092Sdougb	dns_adbnamelist_t *newdeadnames = NULL;
664224092Sdougb	dns_adbnamelist_t *newnames = NULL;
665224092Sdougb	isc_boolean_t *newname_sd = NULL;
666224092Sdougb	isc_mutex_t *newnamelocks = NULL;
667224092Sdougb	isc_result_t result;
668224092Sdougb	unsigned int *newname_refcnt = NULL;
669224092Sdougb	unsigned int i, n, bucket;
670224092Sdougb
671224092Sdougb	adb = ev->ev_arg;
672224092Sdougb	INSIST(DNS_ADB_VALID(adb));
673224092Sdougb
674224092Sdougb	isc_event_free(&ev);
675224092Sdougb
676254402Serwin	result = isc_task_beginexclusive(task);
677254402Serwin	if (result != ISC_R_SUCCESS)
678254402Serwin		goto check_exit;
679224092Sdougb
680224092Sdougb	i = 0;
681224092Sdougb	while (nbuckets[i] != 0 && adb->nnames >= nbuckets[i])
682224092Sdougb		i++;
683224092Sdougb	if (nbuckets[i] != 0)
684224092Sdougb		n = nbuckets[i];
685224092Sdougb	else
686224092Sdougb		goto done;
687224092Sdougb
688224092Sdougb	DP(ISC_LOG_INFO, "adb: grow_names to %u starting", n);
689224092Sdougb
690224092Sdougb	/*
691224092Sdougb	 * Are we shutting down?
692224092Sdougb	 */
693224092Sdougb	for (i = 0; i < adb->nnames; i++)
694224092Sdougb		if (adb->name_sd[i])
695224092Sdougb			goto cleanup;
696224092Sdougb
697224092Sdougb	/*
698224092Sdougb	 * Grab all the resources we need.
699224092Sdougb	 */
700224092Sdougb	newnames = isc_mem_get(adb->mctx, sizeof(*newnames) * n);
701224092Sdougb	newdeadnames = isc_mem_get(adb->mctx, sizeof(*newdeadnames) * n);
702224092Sdougb	newnamelocks = isc_mem_get(adb->mctx, sizeof(*newnamelocks) * n);
703224092Sdougb	newname_sd = isc_mem_get(adb->mctx, sizeof(*newname_sd) * n);
704224092Sdougb	newname_refcnt = isc_mem_get(adb->mctx, sizeof(*newname_refcnt) * n);
705224092Sdougb	if (newnames == NULL || newdeadnames == NULL ||
706224092Sdougb	    newnamelocks == NULL || newname_sd == NULL ||
707224092Sdougb	    newname_refcnt == NULL)
708224092Sdougb		goto cleanup;
709224092Sdougb
710224092Sdougb	/*
711224092Sdougb	 * Initialise the new resources.
712224092Sdougb	 */
713224092Sdougb	result = isc_mutexblock_init(newnamelocks, n);
714224092Sdougb	if (result != ISC_R_SUCCESS)
715224092Sdougb		goto cleanup;
716224092Sdougb
717224092Sdougb	for (i = 0; i < n; i++) {
718224092Sdougb		ISC_LIST_INIT(newnames[i]);
719224092Sdougb		ISC_LIST_INIT(newdeadnames[i]);
720224092Sdougb		newname_sd[i] = ISC_FALSE;
721224092Sdougb		newname_refcnt[i] = 0;
722224092Sdougb		adb->irefcnt++;
723224092Sdougb	}
724224092Sdougb
725224092Sdougb	/*
726224092Sdougb	 * Move names to new arrays.
727224092Sdougb	 */
728224092Sdougb	for (i = 0; i < adb->nnames; i++) {
729224092Sdougb		name = ISC_LIST_HEAD(adb->names[i]);
730224092Sdougb		while (name != NULL) {
731224092Sdougb			ISC_LIST_UNLINK(adb->names[i], name, plink);
732224092Sdougb			bucket = dns_name_fullhash(&name->name, ISC_TRUE) % n;
733224092Sdougb			name->lock_bucket = bucket;
734224092Sdougb			ISC_LIST_APPEND(newnames[bucket], name, plink);
735224092Sdougb			INSIST(adb->name_refcnt[i] > 0);
736224092Sdougb			adb->name_refcnt[i]--;
737224092Sdougb			newname_refcnt[bucket]++;
738224092Sdougb			name = ISC_LIST_HEAD(adb->names[i]);
739224092Sdougb		}
740224092Sdougb		name = ISC_LIST_HEAD(adb->deadnames[i]);
741224092Sdougb		while (name != NULL) {
742224092Sdougb			ISC_LIST_UNLINK(adb->deadnames[i], name, plink);
743224092Sdougb			bucket = dns_name_fullhash(&name->name, ISC_TRUE) % n;
744224092Sdougb			name->lock_bucket = bucket;
745224092Sdougb			ISC_LIST_APPEND(newdeadnames[bucket], name, plink);
746224092Sdougb			INSIST(adb->name_refcnt[i] > 0);
747224092Sdougb			adb->name_refcnt[i]--;
748224092Sdougb			newname_refcnt[bucket]++;
749224092Sdougb			name = ISC_LIST_HEAD(adb->deadnames[i]);
750224092Sdougb		}
751224092Sdougb		INSIST(adb->name_refcnt[i] == 0);
752224092Sdougb		adb->irefcnt--;
753224092Sdougb	}
754224092Sdougb
755224092Sdougb	/*
756224092Sdougb	 * Cleanup old resources.
757224092Sdougb	 */
758224092Sdougb	DESTROYMUTEXBLOCK(adb->namelocks, adb->nnames);
759224092Sdougb	isc_mem_put(adb->mctx, adb->names,
760224092Sdougb		    sizeof(*adb->names) * adb->nnames);
761224092Sdougb	isc_mem_put(adb->mctx, adb->deadnames,
762224092Sdougb		    sizeof(*adb->deadnames) * adb->nnames);
763224092Sdougb	isc_mem_put(adb->mctx, adb->namelocks,
764224092Sdougb		    sizeof(*adb->namelocks) * adb->nnames);
765224092Sdougb	isc_mem_put(adb->mctx, adb->name_sd,
766224092Sdougb		    sizeof(*adb->name_sd) * adb->nnames);
767224092Sdougb	isc_mem_put(adb->mctx, adb->name_refcnt,
768224092Sdougb		    sizeof(*adb->name_refcnt) * adb->nnames);
769224092Sdougb
770224092Sdougb	/*
771224092Sdougb	 * Install new resources.
772224092Sdougb	 */
773224092Sdougb	adb->names = newnames;
774224092Sdougb	adb->deadnames = newdeadnames;
775224092Sdougb	adb->namelocks = newnamelocks;
776224092Sdougb	adb->name_sd = newname_sd;
777224092Sdougb	adb->name_refcnt = newname_refcnt;
778224092Sdougb	adb->nnames = n;
779224092Sdougb
780224092Sdougb	/*
781224092Sdougb	 * Only on success do we set adb->grownames_sent to ISC_FALSE.
782224092Sdougb	 * This will prevent us being continuously being called on error.
783224092Sdougb	 */
784224092Sdougb	adb->grownames_sent = ISC_FALSE;
785224092Sdougb	goto done;
786224092Sdougb
787224092Sdougb cleanup:
788224092Sdougb	if (newnames != NULL)
789224092Sdougb		isc_mem_put(adb->mctx, newnames, sizeof(*newnames) * n);
790224092Sdougb	if (newdeadnames != NULL)
791224092Sdougb		isc_mem_put(adb->mctx, newdeadnames, sizeof(*newdeadnames) * n);
792224092Sdougb	if (newnamelocks != NULL)
793224092Sdougb		isc_mem_put(adb->mctx, newnamelocks, sizeof(*newnamelocks) * n);
794224092Sdougb	if (newname_sd != NULL)
795224092Sdougb		isc_mem_put(adb->mctx, newname_sd, sizeof(*newname_sd) * n);
796224092Sdougb	if (newname_refcnt != NULL)
797224092Sdougb		isc_mem_put(adb->mctx, newname_refcnt,
798224092Sdougb			     sizeof(*newname_refcnt) * n);
799224092Sdougb done:
800224092Sdougb	isc_task_endexclusive(task);
801224092Sdougb
802254402Serwin check_exit:
803224092Sdougb	LOCK(&adb->lock);
804224092Sdougb	if (dec_adb_irefcnt(adb))
805224092Sdougb		check_exit(adb);
806224092Sdougb	UNLOCK(&adb->lock);
807224092Sdougb	DP(ISC_LOG_INFO, "adb: grow_names finished");
808224092Sdougb}
809224092Sdougb
810224092Sdougb/*
811135446Strhodes * Requires the adbname bucket be locked and that no entry buckets be locked.
812135446Strhodes *
813135446Strhodes * This code handles A and AAAA rdatasets only.
814135446Strhodes */
815135446Strhodesstatic isc_result_t
816135446Strhodesimport_rdataset(dns_adbname_t *adbname, dns_rdataset_t *rdataset,
817135446Strhodes		isc_stdtime_t now)
818135446Strhodes{
819135446Strhodes	isc_result_t result;
820135446Strhodes	dns_adb_t *adb;
821135446Strhodes	dns_adbnamehook_t *nh;
822135446Strhodes	dns_adbnamehook_t *anh;
823135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
824135446Strhodes	struct in_addr ina;
825135446Strhodes	struct in6_addr in6a;
826135446Strhodes	isc_sockaddr_t sockaddr;
827135446Strhodes	dns_adbentry_t *foundentry;  /* NO CLEAN UP! */
828135446Strhodes	int addr_bucket;
829135446Strhodes	isc_boolean_t new_addresses_added;
830135446Strhodes	dns_rdatatype_t rdtype;
831135446Strhodes	unsigned int findoptions;
832186462Sdougb	dns_adbnamehooklist_t *hookhead;
833135446Strhodes
834135446Strhodes	INSIST(DNS_ADBNAME_VALID(adbname));
835135446Strhodes	adb = adbname->adb;
836135446Strhodes	INSIST(DNS_ADB_VALID(adb));
837135446Strhodes
838135446Strhodes	rdtype = rdataset->type;
839135446Strhodes	INSIST((rdtype == dns_rdatatype_a) || (rdtype == dns_rdatatype_aaaa));
840135446Strhodes	if (rdtype == dns_rdatatype_a)
841135446Strhodes		findoptions = DNS_ADBFIND_INET;
842135446Strhodes	else
843135446Strhodes		findoptions = DNS_ADBFIND_INET6;
844135446Strhodes
845135446Strhodes	addr_bucket = DNS_ADB_INVALIDBUCKET;
846135446Strhodes	new_addresses_added = ISC_FALSE;
847135446Strhodes
848135446Strhodes	nh = NULL;
849135446Strhodes	result = dns_rdataset_first(rdataset);
850135446Strhodes	while (result == ISC_R_SUCCESS) {
851135446Strhodes		dns_rdata_reset(&rdata);
852135446Strhodes		dns_rdataset_current(rdataset, &rdata);
853135446Strhodes		if (rdtype == dns_rdatatype_a) {
854135446Strhodes			INSIST(rdata.length == 4);
855135446Strhodes			memcpy(&ina.s_addr, rdata.data, 4);
856135446Strhodes			isc_sockaddr_fromin(&sockaddr, &ina, 0);
857186462Sdougb			hookhead = &adbname->v4;
858135446Strhodes		} else {
859135446Strhodes			INSIST(rdata.length == 16);
860135446Strhodes			memcpy(in6a.s6_addr, rdata.data, 16);
861135446Strhodes			isc_sockaddr_fromin6(&sockaddr, &in6a, 0);
862186462Sdougb			hookhead = &adbname->v6;
863135446Strhodes		}
864135446Strhodes
865135446Strhodes		INSIST(nh == NULL);
866135446Strhodes		nh = new_adbnamehook(adb, NULL);
867135446Strhodes		if (nh == NULL) {
868135446Strhodes			adbname->partial_result |= findoptions;
869135446Strhodes			result = ISC_R_NOMEMORY;
870135446Strhodes			goto fail;
871135446Strhodes		}
872135446Strhodes
873193149Sdougb		foundentry = find_entry_and_lock(adb, &sockaddr, &addr_bucket,
874193149Sdougb						 now);
875135446Strhodes		if (foundentry == NULL) {
876135446Strhodes			dns_adbentry_t *entry;
877135446Strhodes
878135446Strhodes			entry = new_adbentry(adb);
879135446Strhodes			if (entry == NULL) {
880135446Strhodes				adbname->partial_result |= findoptions;
881135446Strhodes				result = ISC_R_NOMEMORY;
882135446Strhodes				goto fail;
883135446Strhodes			}
884135446Strhodes
885135446Strhodes			entry->sockaddr = sockaddr;
886135446Strhodes			entry->refcnt = 1;
887135446Strhodes
888135446Strhodes			nh->entry = entry;
889135446Strhodes
890135446Strhodes			link_entry(adb, addr_bucket, entry);
891135446Strhodes		} else {
892186462Sdougb			for (anh = ISC_LIST_HEAD(*hookhead);
893135446Strhodes			     anh != NULL;
894135446Strhodes			     anh = ISC_LIST_NEXT(anh, plink))
895135446Strhodes				if (anh->entry == foundentry)
896135446Strhodes					break;
897135446Strhodes			if (anh == NULL) {
898135446Strhodes				foundentry->refcnt++;
899135446Strhodes				nh->entry = foundentry;
900135446Strhodes			} else
901135446Strhodes				free_adbnamehook(adb, &nh);
902135446Strhodes		}
903135446Strhodes
904135446Strhodes		new_addresses_added = ISC_TRUE;
905186462Sdougb		if (nh != NULL)
906186462Sdougb			ISC_LIST_APPEND(*hookhead, nh, plink);
907135446Strhodes		nh = NULL;
908135446Strhodes		result = dns_rdataset_next(rdataset);
909135446Strhodes	}
910135446Strhodes
911135446Strhodes fail:
912135446Strhodes	if (nh != NULL)
913135446Strhodes		free_adbnamehook(adb, &nh);
914135446Strhodes
915135446Strhodes	if (addr_bucket != DNS_ADB_INVALIDBUCKET)
916135446Strhodes		UNLOCK(&adb->entrylocks[addr_bucket]);
917135446Strhodes
918135446Strhodes	if (rdataset->trust == dns_trust_glue ||
919135446Strhodes	    rdataset->trust == dns_trust_additional)
920135446Strhodes		rdataset->ttl = ADB_CACHE_MINIMUM;
921225361Sdougb	else if (rdataset->trust == dns_trust_ultimate)
922225361Sdougb		rdataset->ttl = 0;
923135446Strhodes	else
924135446Strhodes		rdataset->ttl = ttlclamp(rdataset->ttl);
925135446Strhodes
926135446Strhodes	if (rdtype == dns_rdatatype_a) {
927135446Strhodes		DP(NCACHE_LEVEL, "expire_v4 set to MIN(%u,%u) import_rdataset",
928135446Strhodes		   adbname->expire_v4, now + rdataset->ttl);
929135446Strhodes		adbname->expire_v4 = ISC_MIN(adbname->expire_v4,
930135446Strhodes					     now + rdataset->ttl);
931135446Strhodes	} else {
932135446Strhodes		DP(NCACHE_LEVEL, "expire_v6 set to MIN(%u,%u) import_rdataset",
933135446Strhodes		   adbname->expire_v6, now + rdataset->ttl);
934135446Strhodes		adbname->expire_v6 = ISC_MIN(adbname->expire_v6,
935135446Strhodes					     now + rdataset->ttl);
936135446Strhodes	}
937135446Strhodes
938135446Strhodes	if (new_addresses_added) {
939135446Strhodes		/*
940135446Strhodes		 * Lie a little here.  This is more or less so code that cares
941135446Strhodes		 * can find out if any new information was added or not.
942135446Strhodes		 */
943135446Strhodes		return (ISC_R_SUCCESS);
944135446Strhodes	}
945135446Strhodes
946135446Strhodes	return (result);
947135446Strhodes}
948135446Strhodes
949135446Strhodes/*
950135446Strhodes * Requires the name's bucket be locked.
951135446Strhodes */
952135446Strhodesstatic isc_boolean_t
953135446Strhodeskill_name(dns_adbname_t **n, isc_eventtype_t ev) {
954135446Strhodes	dns_adbname_t *name;
955135446Strhodes	isc_boolean_t result = ISC_FALSE;
956135446Strhodes	isc_boolean_t result4, result6;
957193149Sdougb	int bucket;
958135446Strhodes	dns_adb_t *adb;
959135446Strhodes
960135446Strhodes	INSIST(n != NULL);
961135446Strhodes	name = *n;
962135446Strhodes	*n = NULL;
963135446Strhodes	INSIST(DNS_ADBNAME_VALID(name));
964135446Strhodes	adb = name->adb;
965135446Strhodes	INSIST(DNS_ADB_VALID(adb));
966135446Strhodes
967135446Strhodes	DP(DEF_LEVEL, "killing name %p", name);
968135446Strhodes
969135446Strhodes	/*
970135446Strhodes	 * If we're dead already, just check to see if we should go
971135446Strhodes	 * away now or not.
972135446Strhodes	 */
973135446Strhodes	if (NAME_DEAD(name) && !NAME_FETCH(name)) {
974135446Strhodes		result = unlink_name(adb, name);
975135446Strhodes		free_adbname(adb, &name);
976135446Strhodes		if (result)
977135446Strhodes			result = dec_adb_irefcnt(adb);
978135446Strhodes		return (result);
979135446Strhodes	}
980135446Strhodes
981135446Strhodes	/*
982135446Strhodes	 * Clean up the name's various lists.  These two are destructive
983135446Strhodes	 * in that they will always empty the list.
984135446Strhodes	 */
985135446Strhodes	clean_finds_at_name(name, ev, DNS_ADBFIND_ADDRESSMASK);
986135446Strhodes	result4 = clean_namehooks(adb, &name->v4);
987135446Strhodes	result6 = clean_namehooks(adb, &name->v6);
988135446Strhodes	clean_target(adb, &name->target);
989135446Strhodes	result = ISC_TF(result4 || result6);
990135446Strhodes
991135446Strhodes	/*
992135446Strhodes	 * If fetches are running, cancel them.  If none are running, we can
993135446Strhodes	 * just kill the name here.
994135446Strhodes	 */
995135446Strhodes	if (!NAME_FETCH(name)) {
996135446Strhodes		INSIST(result == ISC_FALSE);
997135446Strhodes		result = unlink_name(adb, name);
998135446Strhodes		free_adbname(adb, &name);
999135446Strhodes		if (result)
1000135446Strhodes			result = dec_adb_irefcnt(adb);
1001135446Strhodes	} else {
1002135446Strhodes		cancel_fetches_at_name(name);
1003193149Sdougb		if (!NAME_DEAD(name)) {
1004193149Sdougb			bucket = name->lock_bucket;
1005193149Sdougb			ISC_LIST_UNLINK(adb->names[bucket], name, plink);
1006193149Sdougb			ISC_LIST_APPEND(adb->deadnames[bucket], name, plink);
1007193149Sdougb			name->flags |= NAME_IS_DEAD;
1008193149Sdougb		}
1009135446Strhodes	}
1010135446Strhodes	return (result);
1011135446Strhodes}
1012135446Strhodes
1013135446Strhodes/*
1014135446Strhodes * Requires the name's bucket be locked and no entry buckets be locked.
1015135446Strhodes */
1016135446Strhodesstatic isc_boolean_t
1017193149Sdougbcheck_expire_namehooks(dns_adbname_t *name, isc_stdtime_t now) {
1018135446Strhodes	dns_adb_t *adb;
1019135446Strhodes	isc_boolean_t result4 = ISC_FALSE;
1020135446Strhodes	isc_boolean_t result6 = ISC_FALSE;
1021135446Strhodes
1022135446Strhodes	INSIST(DNS_ADBNAME_VALID(name));
1023135446Strhodes	adb = name->adb;
1024135446Strhodes	INSIST(DNS_ADB_VALID(adb));
1025135446Strhodes
1026135446Strhodes	/*
1027135446Strhodes	 * Check to see if we need to remove the v4 addresses
1028135446Strhodes	 */
1029193149Sdougb	if (!NAME_FETCH_V4(name) && EXPIRE_OK(name->expire_v4, now)) {
1030135446Strhodes		if (NAME_HAS_V4(name)) {
1031135446Strhodes			DP(DEF_LEVEL, "expiring v4 for name %p", name);
1032135446Strhodes			result4 = clean_namehooks(adb, &name->v4);
1033135446Strhodes			name->partial_result &= ~DNS_ADBFIND_INET;
1034135446Strhodes		}
1035135446Strhodes		name->expire_v4 = INT_MAX;
1036135446Strhodes		name->fetch_err = FIND_ERR_UNEXPECTED;
1037135446Strhodes	}
1038135446Strhodes
1039135446Strhodes	/*
1040135446Strhodes	 * Check to see if we need to remove the v6 addresses
1041135446Strhodes	 */
1042193149Sdougb	if (!NAME_FETCH_V6(name) && EXPIRE_OK(name->expire_v6, now)) {
1043135446Strhodes		if (NAME_HAS_V6(name)) {
1044135446Strhodes			DP(DEF_LEVEL, "expiring v6 for name %p", name);
1045135446Strhodes			result6 = clean_namehooks(adb, &name->v6);
1046135446Strhodes			name->partial_result &= ~DNS_ADBFIND_INET6;
1047135446Strhodes		}
1048135446Strhodes		name->expire_v6 = INT_MAX;
1049135446Strhodes		name->fetch6_err = FIND_ERR_UNEXPECTED;
1050135446Strhodes	}
1051135446Strhodes
1052135446Strhodes	/*
1053135446Strhodes	 * Check to see if we need to remove the alias target.
1054135446Strhodes	 */
1055193149Sdougb	if (EXPIRE_OK(name->expire_target, now)) {
1056135446Strhodes		clean_target(adb, &name->target);
1057135446Strhodes		name->expire_target = INT_MAX;
1058135446Strhodes	}
1059135446Strhodes	return (ISC_TF(result4 || result6));
1060135446Strhodes}
1061135446Strhodes
1062135446Strhodes/*
1063135446Strhodes * Requires the name's bucket be locked.
1064135446Strhodes */
1065135446Strhodesstatic inline void
1066135446Strhodeslink_name(dns_adb_t *adb, int bucket, dns_adbname_t *name) {
1067135446Strhodes	INSIST(name->lock_bucket == DNS_ADB_INVALIDBUCKET);
1068135446Strhodes
1069135446Strhodes	ISC_LIST_PREPEND(adb->names[bucket], name, plink);
1070135446Strhodes	name->lock_bucket = bucket;
1071135446Strhodes	adb->name_refcnt[bucket]++;
1072135446Strhodes}
1073135446Strhodes
1074135446Strhodes/*
1075135446Strhodes * Requires the name's bucket be locked.
1076135446Strhodes */
1077135446Strhodesstatic inline isc_boolean_t
1078135446Strhodesunlink_name(dns_adb_t *adb, dns_adbname_t *name) {
1079135446Strhodes	int bucket;
1080135446Strhodes	isc_boolean_t result = ISC_FALSE;
1081135446Strhodes
1082135446Strhodes	bucket = name->lock_bucket;
1083135446Strhodes	INSIST(bucket != DNS_ADB_INVALIDBUCKET);
1084135446Strhodes
1085193149Sdougb	if (NAME_DEAD(name))
1086193149Sdougb		ISC_LIST_UNLINK(adb->deadnames[bucket], name, plink);
1087193149Sdougb	else
1088193149Sdougb		ISC_LIST_UNLINK(adb->names[bucket], name, plink);
1089135446Strhodes	name->lock_bucket = DNS_ADB_INVALIDBUCKET;
1090135446Strhodes	INSIST(adb->name_refcnt[bucket] > 0);
1091135446Strhodes	adb->name_refcnt[bucket]--;
1092135446Strhodes	if (adb->name_sd[bucket] && adb->name_refcnt[bucket] == 0)
1093135446Strhodes		result = ISC_TRUE;
1094135446Strhodes	return (result);
1095135446Strhodes}
1096135446Strhodes
1097135446Strhodes/*
1098135446Strhodes * Requires the entry's bucket be locked.
1099135446Strhodes */
1100135446Strhodesstatic inline void
1101135446Strhodeslink_entry(dns_adb_t *adb, int bucket, dns_adbentry_t *entry) {
1102193149Sdougb	int i;
1103193149Sdougb	dns_adbentry_t *e;
1104193149Sdougb
1105214586Sdougb	if (isc_mem_isovermem(adb->mctx)) {
1106193149Sdougb		for (i = 0; i < 2; i++) {
1107193149Sdougb			e = ISC_LIST_TAIL(adb->entries[bucket]);
1108193149Sdougb			if (e == NULL)
1109193149Sdougb				break;
1110193149Sdougb			if (e->refcnt == 0) {
1111193149Sdougb				unlink_entry(adb, e);
1112193149Sdougb				free_adbentry(adb, &e);
1113193149Sdougb				continue;
1114193149Sdougb			}
1115193149Sdougb			INSIST((e->flags & ENTRY_IS_DEAD) == 0);
1116193149Sdougb			e->flags |= ENTRY_IS_DEAD;
1117193149Sdougb			ISC_LIST_UNLINK(adb->entries[bucket], e, plink);
1118193149Sdougb			ISC_LIST_PREPEND(adb->deadentries[bucket], e, plink);
1119193149Sdougb		}
1120193149Sdougb	}
1121193149Sdougb
1122135446Strhodes	ISC_LIST_PREPEND(adb->entries[bucket], entry, plink);
1123135446Strhodes	entry->lock_bucket = bucket;
1124135446Strhodes	adb->entry_refcnt[bucket]++;
1125135446Strhodes}
1126135446Strhodes
1127135446Strhodes/*
1128135446Strhodes * Requires the entry's bucket be locked.
1129135446Strhodes */
1130135446Strhodesstatic inline isc_boolean_t
1131135446Strhodesunlink_entry(dns_adb_t *adb, dns_adbentry_t *entry) {
1132135446Strhodes	int bucket;
1133135446Strhodes	isc_boolean_t result = ISC_FALSE;
1134135446Strhodes
1135135446Strhodes	bucket = entry->lock_bucket;
1136135446Strhodes	INSIST(bucket != DNS_ADB_INVALIDBUCKET);
1137135446Strhodes
1138193149Sdougb	if ((entry->flags & ENTRY_IS_DEAD) != 0)
1139193149Sdougb		ISC_LIST_UNLINK(adb->deadentries[bucket], entry, plink);
1140193149Sdougb	else
1141193149Sdougb		ISC_LIST_UNLINK(adb->entries[bucket], entry, plink);
1142135446Strhodes	entry->lock_bucket = DNS_ADB_INVALIDBUCKET;
1143135446Strhodes	INSIST(adb->entry_refcnt[bucket] > 0);
1144135446Strhodes	adb->entry_refcnt[bucket]--;
1145135446Strhodes	if (adb->entry_sd[bucket] && adb->entry_refcnt[bucket] == 0)
1146135446Strhodes		result = ISC_TRUE;
1147135446Strhodes	return (result);
1148135446Strhodes}
1149135446Strhodes
1150135446Strhodesstatic inline void
1151135446Strhodesviolate_locking_hierarchy(isc_mutex_t *have, isc_mutex_t *want) {
1152135446Strhodes	if (isc_mutex_trylock(want) != ISC_R_SUCCESS) {
1153135446Strhodes		UNLOCK(have);
1154135446Strhodes		LOCK(want);
1155135446Strhodes		LOCK(have);
1156135446Strhodes	}
1157135446Strhodes}
1158135446Strhodes
1159135446Strhodes/*
1160135446Strhodes * The ADB _MUST_ be locked before calling.  Also, exit conditions must be
1161135446Strhodes * checked after calling this function.
1162135446Strhodes */
1163135446Strhodesstatic isc_boolean_t
1164135446Strhodesshutdown_names(dns_adb_t *adb) {
1165224092Sdougb	unsigned int bucket;
1166135446Strhodes	isc_boolean_t result = ISC_FALSE;
1167135446Strhodes	dns_adbname_t *name;
1168135446Strhodes	dns_adbname_t *next_name;
1169135446Strhodes
1170224092Sdougb	for (bucket = 0; bucket < adb->nnames; bucket++) {
1171135446Strhodes		LOCK(&adb->namelocks[bucket]);
1172135446Strhodes		adb->name_sd[bucket] = ISC_TRUE;
1173135446Strhodes
1174135446Strhodes		name = ISC_LIST_HEAD(adb->names[bucket]);
1175135446Strhodes		if (name == NULL) {
1176135446Strhodes			/*
1177135446Strhodes			 * This bucket has no names.  We must decrement the
1178135446Strhodes			 * irefcnt ourselves, since it will not be
1179135446Strhodes			 * automatically triggered by a name being unlinked.
1180135446Strhodes			 */
1181135446Strhodes			INSIST(result == ISC_FALSE);
1182135446Strhodes			result = dec_adb_irefcnt(adb);
1183135446Strhodes		} else {
1184135446Strhodes			/*
1185135446Strhodes			 * Run through the list.  For each name, clean up finds
1186135446Strhodes			 * found there, and cancel any fetches running.  When
1187135446Strhodes			 * all the fetches are canceled, the name will destroy
1188135446Strhodes			 * itself.
1189135446Strhodes			 */
1190135446Strhodes			while (name != NULL) {
1191135446Strhodes				next_name = ISC_LIST_NEXT(name, plink);
1192135446Strhodes				INSIST(result == ISC_FALSE);
1193135446Strhodes				result = kill_name(&name,
1194135446Strhodes						   DNS_EVENT_ADBSHUTDOWN);
1195135446Strhodes				name = next_name;
1196135446Strhodes			}
1197135446Strhodes		}
1198135446Strhodes
1199135446Strhodes		UNLOCK(&adb->namelocks[bucket]);
1200135446Strhodes	}
1201135446Strhodes	return (result);
1202135446Strhodes}
1203135446Strhodes
1204135446Strhodes/*
1205135446Strhodes * The ADB _MUST_ be locked before calling.  Also, exit conditions must be
1206135446Strhodes * checked after calling this function.
1207135446Strhodes */
1208135446Strhodesstatic isc_boolean_t
1209135446Strhodesshutdown_entries(dns_adb_t *adb) {
1210224092Sdougb	unsigned int bucket;
1211135446Strhodes	isc_boolean_t result = ISC_FALSE;
1212135446Strhodes	dns_adbentry_t *entry;
1213135446Strhodes	dns_adbentry_t *next_entry;
1214135446Strhodes
1215224092Sdougb	for (bucket = 0; bucket < adb->nentries; bucket++) {
1216135446Strhodes		LOCK(&adb->entrylocks[bucket]);
1217135446Strhodes		adb->entry_sd[bucket] = ISC_TRUE;
1218135446Strhodes
1219135446Strhodes		entry = ISC_LIST_HEAD(adb->entries[bucket]);
1220193149Sdougb		if (adb->entry_refcnt[bucket] == 0) {
1221135446Strhodes			/*
1222135446Strhodes			 * This bucket has no entries.  We must decrement the
1223135446Strhodes			 * irefcnt ourselves, since it will not be
1224135446Strhodes			 * automatically triggered by an entry being unlinked.
1225135446Strhodes			 */
1226135446Strhodes			result = dec_adb_irefcnt(adb);
1227135446Strhodes		} else {
1228135446Strhodes			/*
1229135446Strhodes			 * Run through the list.  Cleanup any entries not
1230135446Strhodes			 * associated with names, and which are not in use.
1231135446Strhodes			 */
1232135446Strhodes			while (entry != NULL) {
1233135446Strhodes				next_entry = ISC_LIST_NEXT(entry, plink);
1234135446Strhodes				if (entry->refcnt == 0 &&
1235135446Strhodes				    entry->expires != 0) {
1236135446Strhodes					result = unlink_entry(adb, entry);
1237135446Strhodes					free_adbentry(adb, &entry);
1238135446Strhodes					if (result)
1239135446Strhodes						result = dec_adb_irefcnt(adb);
1240135446Strhodes				}
1241135446Strhodes				entry = next_entry;
1242135446Strhodes			}
1243135446Strhodes		}
1244135446Strhodes
1245135446Strhodes		UNLOCK(&adb->entrylocks[bucket]);
1246135446Strhodes	}
1247135446Strhodes	return (result);
1248135446Strhodes}
1249135446Strhodes
1250135446Strhodes/*
1251135446Strhodes * Name bucket must be locked
1252135446Strhodes */
1253135446Strhodesstatic void
1254135446Strhodescancel_fetches_at_name(dns_adbname_t *name) {
1255135446Strhodes	if (NAME_FETCH_A(name))
1256135446Strhodes	    dns_resolver_cancelfetch(name->fetch_a->fetch);
1257135446Strhodes
1258135446Strhodes	if (NAME_FETCH_AAAA(name))
1259135446Strhodes	    dns_resolver_cancelfetch(name->fetch_aaaa->fetch);
1260135446Strhodes}
1261135446Strhodes
1262135446Strhodes/*
1263135446Strhodes * Assumes the name bucket is locked.
1264135446Strhodes */
1265135446Strhodesstatic isc_boolean_t
1266135446Strhodesclean_namehooks(dns_adb_t *adb, dns_adbnamehooklist_t *namehooks) {
1267135446Strhodes	dns_adbentry_t *entry;
1268135446Strhodes	dns_adbnamehook_t *namehook;
1269135446Strhodes	int addr_bucket;
1270135446Strhodes	isc_boolean_t result = ISC_FALSE;
1271214586Sdougb	isc_boolean_t overmem = isc_mem_isovermem(adb->mctx);
1272135446Strhodes
1273135446Strhodes	addr_bucket = DNS_ADB_INVALIDBUCKET;
1274135446Strhodes	namehook = ISC_LIST_HEAD(*namehooks);
1275135446Strhodes	while (namehook != NULL) {
1276135446Strhodes		INSIST(DNS_ADBNAMEHOOK_VALID(namehook));
1277135446Strhodes
1278135446Strhodes		/*
1279135446Strhodes		 * Clean up the entry if needed.
1280135446Strhodes		 */
1281135446Strhodes		entry = namehook->entry;
1282135446Strhodes		if (entry != NULL) {
1283135446Strhodes			INSIST(DNS_ADBENTRY_VALID(entry));
1284135446Strhodes
1285135446Strhodes			if (addr_bucket != entry->lock_bucket) {
1286135446Strhodes				if (addr_bucket != DNS_ADB_INVALIDBUCKET)
1287135446Strhodes					UNLOCK(&adb->entrylocks[addr_bucket]);
1288135446Strhodes				addr_bucket = entry->lock_bucket;
1289254402Serwin				INSIST(addr_bucket != DNS_ADB_INVALIDBUCKET);
1290135446Strhodes				LOCK(&adb->entrylocks[addr_bucket]);
1291135446Strhodes			}
1292135446Strhodes
1293214586Sdougb			result = dec_entry_refcnt(adb, overmem, entry,
1294214586Sdougb						  ISC_FALSE);
1295135446Strhodes		}
1296135446Strhodes
1297135446Strhodes		/*
1298135446Strhodes		 * Free the namehook
1299135446Strhodes		 */
1300135446Strhodes		namehook->entry = NULL;
1301135446Strhodes		ISC_LIST_UNLINK(*namehooks, namehook, plink);
1302135446Strhodes		free_adbnamehook(adb, &namehook);
1303135446Strhodes
1304135446Strhodes		namehook = ISC_LIST_HEAD(*namehooks);
1305135446Strhodes	}
1306135446Strhodes
1307135446Strhodes	if (addr_bucket != DNS_ADB_INVALIDBUCKET)
1308135446Strhodes		UNLOCK(&adb->entrylocks[addr_bucket]);
1309135446Strhodes	return (result);
1310135446Strhodes}
1311135446Strhodes
1312135446Strhodesstatic void
1313135446Strhodesclean_target(dns_adb_t *adb, dns_name_t *target) {
1314135446Strhodes	if (dns_name_countlabels(target) > 0) {
1315135446Strhodes		dns_name_free(target, adb->mctx);
1316135446Strhodes		dns_name_init(target, NULL);
1317135446Strhodes	}
1318135446Strhodes}
1319135446Strhodes
1320135446Strhodesstatic isc_result_t
1321135446Strhodesset_target(dns_adb_t *adb, dns_name_t *name, dns_name_t *fname,
1322135446Strhodes	   dns_rdataset_t *rdataset, dns_name_t *target)
1323135446Strhodes{
1324135446Strhodes	isc_result_t result;
1325135446Strhodes	dns_namereln_t namereln;
1326135446Strhodes	unsigned int nlabels;
1327135446Strhodes	int order;
1328135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
1329135446Strhodes	dns_fixedname_t fixed1, fixed2;
1330135446Strhodes	dns_name_t *prefix, *new_target;
1331135446Strhodes
1332135446Strhodes	REQUIRE(dns_name_countlabels(target) == 0);
1333135446Strhodes
1334135446Strhodes	if (rdataset->type == dns_rdatatype_cname) {
1335135446Strhodes		dns_rdata_cname_t cname;
1336135446Strhodes
1337135446Strhodes		/*
1338135446Strhodes		 * Copy the CNAME's target into the target name.
1339135446Strhodes		 */
1340135446Strhodes		result = dns_rdataset_first(rdataset);
1341135446Strhodes		if (result != ISC_R_SUCCESS)
1342135446Strhodes			return (result);
1343135446Strhodes		dns_rdataset_current(rdataset, &rdata);
1344135446Strhodes		result = dns_rdata_tostruct(&rdata, &cname, NULL);
1345135446Strhodes		if (result != ISC_R_SUCCESS)
1346135446Strhodes			return (result);
1347135446Strhodes		result = dns_name_dup(&cname.cname, adb->mctx, target);
1348135446Strhodes		dns_rdata_freestruct(&cname);
1349135446Strhodes		if (result != ISC_R_SUCCESS)
1350135446Strhodes			return (result);
1351135446Strhodes	} else {
1352135446Strhodes		dns_rdata_dname_t dname;
1353135446Strhodes
1354135446Strhodes		INSIST(rdataset->type == dns_rdatatype_dname);
1355135446Strhodes		namereln = dns_name_fullcompare(name, fname, &order, &nlabels);
1356135446Strhodes		INSIST(namereln == dns_namereln_subdomain);
1357135446Strhodes		/*
1358135446Strhodes		 * Get the target name of the DNAME.
1359135446Strhodes		 */
1360135446Strhodes		result = dns_rdataset_first(rdataset);
1361135446Strhodes		if (result != ISC_R_SUCCESS)
1362135446Strhodes			return (result);
1363135446Strhodes		dns_rdataset_current(rdataset, &rdata);
1364135446Strhodes		result = dns_rdata_tostruct(&rdata, &dname, NULL);
1365135446Strhodes		if (result != ISC_R_SUCCESS)
1366135446Strhodes			return (result);
1367135446Strhodes		/*
1368135446Strhodes		 * Construct the new target name.
1369135446Strhodes		 */
1370135446Strhodes		dns_fixedname_init(&fixed1);
1371135446Strhodes		prefix = dns_fixedname_name(&fixed1);
1372135446Strhodes		dns_fixedname_init(&fixed2);
1373135446Strhodes		new_target = dns_fixedname_name(&fixed2);
1374135446Strhodes		dns_name_split(name, nlabels, prefix, NULL);
1375135446Strhodes		result = dns_name_concatenate(prefix, &dname.dname, new_target,
1376135446Strhodes					      NULL);
1377135446Strhodes		dns_rdata_freestruct(&dname);
1378135446Strhodes		if (result != ISC_R_SUCCESS)
1379135446Strhodes			return (result);
1380135446Strhodes		result = dns_name_dup(new_target, adb->mctx, target);
1381135446Strhodes		if (result != ISC_R_SUCCESS)
1382135446Strhodes			return (result);
1383135446Strhodes	}
1384135446Strhodes
1385135446Strhodes	return (ISC_R_SUCCESS);
1386135446Strhodes}
1387135446Strhodes
1388135446Strhodes/*
1389135446Strhodes * Assumes nothing is locked, since this is called by the client.
1390135446Strhodes */
1391135446Strhodesstatic void
1392135446Strhodesevent_free(isc_event_t *event) {
1393135446Strhodes	dns_adbfind_t *find;
1394135446Strhodes
1395135446Strhodes	INSIST(event != NULL);
1396135446Strhodes	find = event->ev_destroy_arg;
1397135446Strhodes	INSIST(DNS_ADBFIND_VALID(find));
1398135446Strhodes
1399135446Strhodes	LOCK(&find->lock);
1400135446Strhodes	find->flags |= FIND_EVENT_FREED;
1401135446Strhodes	event->ev_destroy_arg = NULL;
1402135446Strhodes	UNLOCK(&find->lock);
1403135446Strhodes}
1404135446Strhodes
1405135446Strhodes/*
1406135446Strhodes * Assumes the name bucket is locked.
1407135446Strhodes */
1408135446Strhodesstatic void
1409135446Strhodesclean_finds_at_name(dns_adbname_t *name, isc_eventtype_t evtype,
1410135446Strhodes		    unsigned int addrs)
1411135446Strhodes{
1412135446Strhodes	isc_event_t *ev;
1413135446Strhodes	isc_task_t *task;
1414135446Strhodes	dns_adbfind_t *find;
1415135446Strhodes	dns_adbfind_t *next_find;
1416135446Strhodes	isc_boolean_t process;
1417135446Strhodes	unsigned int wanted, notify;
1418135446Strhodes
1419135446Strhodes	DP(ENTER_LEVEL,
1420135446Strhodes	   "ENTER clean_finds_at_name, name %p, evtype %08x, addrs %08x",
1421135446Strhodes	   name, evtype, addrs);
1422135446Strhodes
1423135446Strhodes	find = ISC_LIST_HEAD(name->finds);
1424135446Strhodes	while (find != NULL) {
1425135446Strhodes		LOCK(&find->lock);
1426135446Strhodes		next_find = ISC_LIST_NEXT(find, plink);
1427135446Strhodes
1428135446Strhodes		process = ISC_FALSE;
1429135446Strhodes		wanted = find->flags & DNS_ADBFIND_ADDRESSMASK;
1430135446Strhodes		notify = wanted & addrs;
1431135446Strhodes
1432135446Strhodes		switch (evtype) {
1433135446Strhodes		case DNS_EVENT_ADBMOREADDRESSES:
1434135446Strhodes			DP(ISC_LOG_DEBUG(3), "DNS_EVENT_ADBMOREADDRESSES");
1435135446Strhodes			if ((notify) != 0) {
1436135446Strhodes				find->flags &= ~addrs;
1437135446Strhodes				process = ISC_TRUE;
1438135446Strhodes			}
1439135446Strhodes			break;
1440135446Strhodes		case DNS_EVENT_ADBNOMOREADDRESSES:
1441135446Strhodes			DP(ISC_LOG_DEBUG(3), "DNS_EVENT_ADBNOMOREADDRESSES");
1442135446Strhodes			find->flags &= ~addrs;
1443135446Strhodes			wanted = find->flags & DNS_ADBFIND_ADDRESSMASK;
1444135446Strhodes			if (wanted == 0)
1445135446Strhodes				process = ISC_TRUE;
1446135446Strhodes			break;
1447135446Strhodes		default:
1448135446Strhodes			find->flags &= ~addrs;
1449135446Strhodes			process = ISC_TRUE;
1450135446Strhodes		}
1451135446Strhodes
1452135446Strhodes		if (process) {
1453135446Strhodes			DP(DEF_LEVEL, "cfan: processing find %p", find);
1454135446Strhodes			/*
1455135446Strhodes			 * Unlink the find from the name, letting the caller
1456135446Strhodes			 * call dns_adb_destroyfind() on it to clean it up
1457135446Strhodes			 * later.
1458135446Strhodes			 */
1459135446Strhodes			ISC_LIST_UNLINK(name->finds, find, plink);
1460135446Strhodes			find->adbname = NULL;
1461135446Strhodes			find->name_bucket = DNS_ADB_INVALIDBUCKET;
1462135446Strhodes
1463135446Strhodes			INSIST(!FIND_EVENTSENT(find));
1464135446Strhodes
1465135446Strhodes			ev = &find->event;
1466135446Strhodes			task = ev->ev_sender;
1467135446Strhodes			ev->ev_sender = find;
1468135446Strhodes			find->result_v4 = find_err_map[name->fetch_err];
1469135446Strhodes			find->result_v6 = find_err_map[name->fetch6_err];
1470135446Strhodes			ev->ev_type = evtype;
1471135446Strhodes			ev->ev_destroy = event_free;
1472135446Strhodes			ev->ev_destroy_arg = find;
1473135446Strhodes
1474135446Strhodes			DP(DEF_LEVEL,
1475135446Strhodes			   "sending event %p to task %p for find %p",
1476135446Strhodes			   ev, task, find);
1477135446Strhodes
1478135446Strhodes			isc_task_sendanddetach(&task, (isc_event_t **)&ev);
1479135446Strhodes		} else {
1480135446Strhodes			DP(DEF_LEVEL, "cfan: skipping find %p", find);
1481135446Strhodes		}
1482135446Strhodes
1483135446Strhodes		UNLOCK(&find->lock);
1484135446Strhodes		find = next_find;
1485135446Strhodes	}
1486135446Strhodes
1487135446Strhodes	DP(ENTER_LEVEL, "EXIT clean_finds_at_name, name %p", name);
1488135446Strhodes}
1489135446Strhodes
1490135446Strhodesstatic inline void
1491135446Strhodescheck_exit(dns_adb_t *adb) {
1492135446Strhodes	isc_event_t *event;
1493135446Strhodes	/*
1494135446Strhodes	 * The caller must be holding the adb lock.
1495135446Strhodes	 */
1496135446Strhodes	if (adb->shutting_down) {
1497135446Strhodes		/*
1498135446Strhodes		 * If there aren't any external references either, we're
1499135446Strhodes		 * done.  Send the control event to initiate shutdown.
1500135446Strhodes		 */
1501193149Sdougb		INSIST(!adb->cevent_sent);      /* Sanity check. */
1502135446Strhodes		event = &adb->cevent;
1503135446Strhodes		isc_task_send(adb->task, &event);
1504135446Strhodes		adb->cevent_sent = ISC_TRUE;
1505135446Strhodes	}
1506135446Strhodes}
1507135446Strhodes
1508135446Strhodesstatic inline isc_boolean_t
1509135446Strhodesdec_adb_irefcnt(dns_adb_t *adb) {
1510135446Strhodes	isc_event_t *event;
1511135446Strhodes	isc_task_t *etask;
1512135446Strhodes	isc_boolean_t result = ISC_FALSE;
1513135446Strhodes
1514135446Strhodes	LOCK(&adb->reflock);
1515135446Strhodes
1516135446Strhodes	INSIST(adb->irefcnt > 0);
1517135446Strhodes	adb->irefcnt--;
1518135446Strhodes
1519135446Strhodes	if (adb->irefcnt == 0) {
1520135446Strhodes		event = ISC_LIST_HEAD(adb->whenshutdown);
1521135446Strhodes		while (event != NULL) {
1522135446Strhodes			ISC_LIST_UNLINK(adb->whenshutdown, event, ev_link);
1523135446Strhodes			etask = event->ev_sender;
1524135446Strhodes			event->ev_sender = adb;
1525135446Strhodes			isc_task_sendanddetach(&etask, &event);
1526135446Strhodes			event = ISC_LIST_HEAD(adb->whenshutdown);
1527135446Strhodes		}
1528135446Strhodes	}
1529135446Strhodes
1530135446Strhodes	if (adb->irefcnt == 0 && adb->erefcnt == 0)
1531135446Strhodes		result = ISC_TRUE;
1532135446Strhodes	UNLOCK(&adb->reflock);
1533135446Strhodes	return (result);
1534135446Strhodes}
1535135446Strhodes
1536135446Strhodesstatic inline void
1537135446Strhodesinc_adb_irefcnt(dns_adb_t *adb) {
1538135446Strhodes	LOCK(&adb->reflock);
1539135446Strhodes	adb->irefcnt++;
1540135446Strhodes	UNLOCK(&adb->reflock);
1541135446Strhodes}
1542135446Strhodes
1543135446Strhodesstatic inline void
1544135446Strhodesinc_adb_erefcnt(dns_adb_t *adb) {
1545135446Strhodes	LOCK(&adb->reflock);
1546135446Strhodes	adb->erefcnt++;
1547135446Strhodes	UNLOCK(&adb->reflock);
1548135446Strhodes}
1549135446Strhodes
1550135446Strhodesstatic inline void
1551135446Strhodesinc_entry_refcnt(dns_adb_t *adb, dns_adbentry_t *entry, isc_boolean_t lock) {
1552135446Strhodes	int bucket;
1553135446Strhodes
1554135446Strhodes	bucket = entry->lock_bucket;
1555135446Strhodes
1556135446Strhodes	if (lock)
1557135446Strhodes		LOCK(&adb->entrylocks[bucket]);
1558135446Strhodes
1559135446Strhodes	entry->refcnt++;
1560135446Strhodes
1561135446Strhodes	if (lock)
1562135446Strhodes		UNLOCK(&adb->entrylocks[bucket]);
1563135446Strhodes}
1564135446Strhodes
1565135446Strhodesstatic inline isc_boolean_t
1566214586Sdougbdec_entry_refcnt(dns_adb_t *adb, isc_boolean_t overmem, dns_adbentry_t *entry,
1567214586Sdougb		 isc_boolean_t lock)
1568214586Sdougb{
1569135446Strhodes	int bucket;
1570135446Strhodes	isc_boolean_t destroy_entry;
1571135446Strhodes	isc_boolean_t result = ISC_FALSE;
1572135446Strhodes
1573135446Strhodes	bucket = entry->lock_bucket;
1574135446Strhodes
1575135446Strhodes	if (lock)
1576135446Strhodes		LOCK(&adb->entrylocks[bucket]);
1577135446Strhodes
1578135446Strhodes	INSIST(entry->refcnt > 0);
1579135446Strhodes	entry->refcnt--;
1580135446Strhodes
1581135446Strhodes	destroy_entry = ISC_FALSE;
1582135446Strhodes	if (entry->refcnt == 0 &&
1583214586Sdougb	    (adb->entry_sd[bucket] || entry->expires == 0 || overmem ||
1584193149Sdougb	     (entry->flags & ENTRY_IS_DEAD) != 0)) {
1585135446Strhodes		destroy_entry = ISC_TRUE;
1586135446Strhodes		result = unlink_entry(adb, entry);
1587135446Strhodes	}
1588135446Strhodes
1589135446Strhodes	if (lock)
1590135446Strhodes		UNLOCK(&adb->entrylocks[bucket]);
1591135446Strhodes
1592135446Strhodes	if (!destroy_entry)
1593135446Strhodes		return (result);
1594135446Strhodes
1595135446Strhodes	entry->lock_bucket = DNS_ADB_INVALIDBUCKET;
1596135446Strhodes
1597135446Strhodes	free_adbentry(adb, &entry);
1598135446Strhodes	if (result)
1599193149Sdougb		result = dec_adb_irefcnt(adb);
1600135446Strhodes
1601135446Strhodes	return (result);
1602135446Strhodes}
1603135446Strhodes
1604135446Strhodesstatic inline dns_adbname_t *
1605135446Strhodesnew_adbname(dns_adb_t *adb, dns_name_t *dnsname) {
1606135446Strhodes	dns_adbname_t *name;
1607135446Strhodes
1608135446Strhodes	name = isc_mempool_get(adb->nmp);
1609135446Strhodes	if (name == NULL)
1610135446Strhodes		return (NULL);
1611135446Strhodes
1612135446Strhodes	dns_name_init(&name->name, NULL);
1613135446Strhodes	if (dns_name_dup(dnsname, adb->mctx, &name->name) != ISC_R_SUCCESS) {
1614135446Strhodes		isc_mempool_put(adb->nmp, name);
1615135446Strhodes		return (NULL);
1616135446Strhodes	}
1617135446Strhodes	dns_name_init(&name->target, NULL);
1618135446Strhodes	name->magic = DNS_ADBNAME_MAGIC;
1619135446Strhodes	name->adb = adb;
1620135446Strhodes	name->partial_result = 0;
1621135446Strhodes	name->flags = 0;
1622135446Strhodes	name->expire_v4 = INT_MAX;
1623135446Strhodes	name->expire_v6 = INT_MAX;
1624135446Strhodes	name->expire_target = INT_MAX;
1625135446Strhodes	name->chains = 0;
1626135446Strhodes	name->lock_bucket = DNS_ADB_INVALIDBUCKET;
1627135446Strhodes	ISC_LIST_INIT(name->v4);
1628135446Strhodes	ISC_LIST_INIT(name->v6);
1629135446Strhodes	name->fetch_a = NULL;
1630135446Strhodes	name->fetch_aaaa = NULL;
1631135446Strhodes	name->fetch_err = FIND_ERR_UNEXPECTED;
1632135446Strhodes	name->fetch6_err = FIND_ERR_UNEXPECTED;
1633135446Strhodes	ISC_LIST_INIT(name->finds);
1634135446Strhodes	ISC_LINK_INIT(name, plink);
1635135446Strhodes
1636224092Sdougb	LOCK(&adb->namescntlock);
1637224092Sdougb	adb->namescnt++;
1638245163Serwin	if (!adb->grownames_sent && adb->excl != NULL &&
1639245163Serwin	    adb->namescnt > (adb->nnames * 8))
1640245163Serwin	{
1641224092Sdougb		isc_event_t *event = &adb->grownames;
1642224092Sdougb		inc_adb_irefcnt(adb);
1643245163Serwin		isc_task_send(adb->excl, &event);
1644224092Sdougb		adb->grownames_sent = ISC_TRUE;
1645224092Sdougb	}
1646224092Sdougb	UNLOCK(&adb->namescntlock);
1647224092Sdougb
1648135446Strhodes	return (name);
1649135446Strhodes}
1650135446Strhodes
1651135446Strhodesstatic inline void
1652135446Strhodesfree_adbname(dns_adb_t *adb, dns_adbname_t **name) {
1653135446Strhodes	dns_adbname_t *n;
1654135446Strhodes
1655135446Strhodes	INSIST(name != NULL && DNS_ADBNAME_VALID(*name));
1656135446Strhodes	n = *name;
1657135446Strhodes	*name = NULL;
1658135446Strhodes
1659135446Strhodes	INSIST(!NAME_HAS_V4(n));
1660135446Strhodes	INSIST(!NAME_HAS_V6(n));
1661135446Strhodes	INSIST(!NAME_FETCH(n));
1662135446Strhodes	INSIST(ISC_LIST_EMPTY(n->finds));
1663135446Strhodes	INSIST(!ISC_LINK_LINKED(n, plink));
1664135446Strhodes	INSIST(n->lock_bucket == DNS_ADB_INVALIDBUCKET);
1665135446Strhodes	INSIST(n->adb == adb);
1666135446Strhodes
1667135446Strhodes	n->magic = 0;
1668135446Strhodes	dns_name_free(&n->name, adb->mctx);
1669135446Strhodes
1670135446Strhodes	isc_mempool_put(adb->nmp, n);
1671224092Sdougb	LOCK(&adb->namescntlock);
1672224092Sdougb	adb->namescnt--;
1673224092Sdougb	UNLOCK(&adb->namescntlock);
1674135446Strhodes}
1675135446Strhodes
1676135446Strhodesstatic inline dns_adbnamehook_t *
1677135446Strhodesnew_adbnamehook(dns_adb_t *adb, dns_adbentry_t *entry) {
1678135446Strhodes	dns_adbnamehook_t *nh;
1679135446Strhodes
1680135446Strhodes	nh = isc_mempool_get(adb->nhmp);
1681135446Strhodes	if (nh == NULL)
1682135446Strhodes		return (NULL);
1683135446Strhodes
1684135446Strhodes	nh->magic = DNS_ADBNAMEHOOK_MAGIC;
1685135446Strhodes	nh->entry = entry;
1686135446Strhodes	ISC_LINK_INIT(nh, plink);
1687135446Strhodes
1688135446Strhodes	return (nh);
1689135446Strhodes}
1690135446Strhodes
1691135446Strhodesstatic inline void
1692135446Strhodesfree_adbnamehook(dns_adb_t *adb, dns_adbnamehook_t **namehook) {
1693135446Strhodes	dns_adbnamehook_t *nh;
1694135446Strhodes
1695135446Strhodes	INSIST(namehook != NULL && DNS_ADBNAMEHOOK_VALID(*namehook));
1696135446Strhodes	nh = *namehook;
1697135446Strhodes	*namehook = NULL;
1698135446Strhodes
1699135446Strhodes	INSIST(nh->entry == NULL);
1700135446Strhodes	INSIST(!ISC_LINK_LINKED(nh, plink));
1701135446Strhodes
1702135446Strhodes	nh->magic = 0;
1703135446Strhodes	isc_mempool_put(adb->nhmp, nh);
1704135446Strhodes}
1705135446Strhodes
1706170222Sdougbstatic inline dns_adblameinfo_t *
1707170222Sdougbnew_adblameinfo(dns_adb_t *adb, dns_name_t *qname, dns_rdatatype_t qtype) {
1708170222Sdougb	dns_adblameinfo_t *li;
1709135446Strhodes
1710170222Sdougb	li = isc_mempool_get(adb->limp);
1711170222Sdougb	if (li == NULL)
1712135446Strhodes		return (NULL);
1713135446Strhodes
1714170222Sdougb	dns_name_init(&li->qname, NULL);
1715170222Sdougb	if (dns_name_dup(qname, adb->mctx, &li->qname) != ISC_R_SUCCESS) {
1716170222Sdougb		isc_mempool_put(adb->limp, li);
1717135446Strhodes		return (NULL);
1718135446Strhodes	}
1719170222Sdougb	li->magic = DNS_ADBLAMEINFO_MAGIC;
1720170222Sdougb	li->lame_timer = 0;
1721170222Sdougb	li->qtype = qtype;
1722170222Sdougb	ISC_LINK_INIT(li, plink);
1723135446Strhodes
1724170222Sdougb	return (li);
1725135446Strhodes}
1726135446Strhodes
1727135446Strhodesstatic inline void
1728170222Sdougbfree_adblameinfo(dns_adb_t *adb, dns_adblameinfo_t **lameinfo) {
1729170222Sdougb	dns_adblameinfo_t *li;
1730135446Strhodes
1731170222Sdougb	INSIST(lameinfo != NULL && DNS_ADBLAMEINFO_VALID(*lameinfo));
1732170222Sdougb	li = *lameinfo;
1733170222Sdougb	*lameinfo = NULL;
1734135446Strhodes
1735170222Sdougb	INSIST(!ISC_LINK_LINKED(li, plink));
1736135446Strhodes
1737170222Sdougb	dns_name_free(&li->qname, adb->mctx);
1738135446Strhodes
1739170222Sdougb	li->magic = 0;
1740135446Strhodes
1741170222Sdougb	isc_mempool_put(adb->limp, li);
1742135446Strhodes}
1743135446Strhodes
1744135446Strhodesstatic inline dns_adbentry_t *
1745135446Strhodesnew_adbentry(dns_adb_t *adb) {
1746135446Strhodes	dns_adbentry_t *e;
1747135446Strhodes	isc_uint32_t r;
1748135446Strhodes
1749135446Strhodes	e = isc_mempool_get(adb->emp);
1750135446Strhodes	if (e == NULL)
1751135446Strhodes		return (NULL);
1752135446Strhodes
1753135446Strhodes	e->magic = DNS_ADBENTRY_MAGIC;
1754135446Strhodes	e->lock_bucket = DNS_ADB_INVALIDBUCKET;
1755135446Strhodes	e->refcnt = 0;
1756135446Strhodes	e->flags = 0;
1757135446Strhodes	isc_random_get(&r);
1758135446Strhodes	e->srtt = (r & 0x1f) + 1;
1759135446Strhodes	e->expires = 0;
1760170222Sdougb	ISC_LIST_INIT(e->lameinfo);
1761135446Strhodes	ISC_LINK_INIT(e, plink);
1762224092Sdougb	LOCK(&adb->entriescntlock);
1763224092Sdougb	adb->entriescnt++;
1764245163Serwin	if (!adb->growentries_sent && adb->growentries_sent &&
1765245163Serwin	    adb->entriescnt > (adb->nentries * 8))
1766245163Serwin	{
1767224092Sdougb		isc_event_t *event = &adb->growentries;
1768224092Sdougb		inc_adb_irefcnt(adb);
1769224092Sdougb		isc_task_send(adb->task, &event);
1770224092Sdougb		adb->growentries_sent = ISC_TRUE;
1771224092Sdougb	}
1772224092Sdougb	UNLOCK(&adb->entriescntlock);
1773135446Strhodes
1774135446Strhodes	return (e);
1775135446Strhodes}
1776135446Strhodes
1777135446Strhodesstatic inline void
1778135446Strhodesfree_adbentry(dns_adb_t *adb, dns_adbentry_t **entry) {
1779135446Strhodes	dns_adbentry_t *e;
1780170222Sdougb	dns_adblameinfo_t *li;
1781135446Strhodes
1782135446Strhodes	INSIST(entry != NULL && DNS_ADBENTRY_VALID(*entry));
1783135446Strhodes	e = *entry;
1784135446Strhodes	*entry = NULL;
1785135446Strhodes
1786135446Strhodes	INSIST(e->lock_bucket == DNS_ADB_INVALIDBUCKET);
1787135446Strhodes	INSIST(e->refcnt == 0);
1788135446Strhodes	INSIST(!ISC_LINK_LINKED(e, plink));
1789135446Strhodes
1790135446Strhodes	e->magic = 0;
1791135446Strhodes
1792170222Sdougb	li = ISC_LIST_HEAD(e->lameinfo);
1793170222Sdougb	while (li != NULL) {
1794170222Sdougb		ISC_LIST_UNLINK(e->lameinfo, li, plink);
1795170222Sdougb		free_adblameinfo(adb, &li);
1796170222Sdougb		li = ISC_LIST_HEAD(e->lameinfo);
1797135446Strhodes	}
1798135446Strhodes
1799135446Strhodes	isc_mempool_put(adb->emp, e);
1800224092Sdougb	LOCK(&adb->entriescntlock);
1801224092Sdougb	adb->entriescnt--;
1802224092Sdougb	UNLOCK(&adb->entriescntlock);
1803135446Strhodes}
1804135446Strhodes
1805135446Strhodesstatic inline dns_adbfind_t *
1806135446Strhodesnew_adbfind(dns_adb_t *adb) {
1807135446Strhodes	dns_adbfind_t *h;
1808135446Strhodes	isc_result_t result;
1809135446Strhodes
1810135446Strhodes	h = isc_mempool_get(adb->ahmp);
1811135446Strhodes	if (h == NULL)
1812135446Strhodes		return (NULL);
1813135446Strhodes
1814135446Strhodes	/*
1815135446Strhodes	 * Public members.
1816135446Strhodes	 */
1817135446Strhodes	h->magic = 0;
1818135446Strhodes	h->adb = adb;
1819135446Strhodes	h->partial_result = 0;
1820135446Strhodes	h->options = 0;
1821135446Strhodes	h->flags = 0;
1822135446Strhodes	h->result_v4 = ISC_R_UNEXPECTED;
1823135446Strhodes	h->result_v6 = ISC_R_UNEXPECTED;
1824135446Strhodes	ISC_LINK_INIT(h, publink);
1825135446Strhodes	ISC_LINK_INIT(h, plink);
1826135446Strhodes	ISC_LIST_INIT(h->list);
1827135446Strhodes	h->adbname = NULL;
1828135446Strhodes	h->name_bucket = DNS_ADB_INVALIDBUCKET;
1829135446Strhodes
1830135446Strhodes	/*
1831135446Strhodes	 * private members
1832135446Strhodes	 */
1833135446Strhodes	result = isc_mutex_init(&h->lock);
1834135446Strhodes	if (result != ISC_R_SUCCESS) {
1835135446Strhodes		isc_mempool_put(adb->ahmp, h);
1836135446Strhodes		return (NULL);
1837135446Strhodes	}
1838135446Strhodes
1839135446Strhodes	ISC_EVENT_INIT(&h->event, sizeof(isc_event_t), 0, 0, 0, NULL, NULL,
1840135446Strhodes		       NULL, NULL, h);
1841135446Strhodes
1842135446Strhodes	inc_adb_irefcnt(adb);
1843135446Strhodes	h->magic = DNS_ADBFIND_MAGIC;
1844135446Strhodes	return (h);
1845135446Strhodes}
1846135446Strhodes
1847135446Strhodesstatic inline dns_adbfetch_t *
1848135446Strhodesnew_adbfetch(dns_adb_t *adb) {
1849135446Strhodes	dns_adbfetch_t *f;
1850135446Strhodes
1851135446Strhodes	f = isc_mempool_get(adb->afmp);
1852135446Strhodes	if (f == NULL)
1853135446Strhodes		return (NULL);
1854135446Strhodes
1855135446Strhodes	f->magic = 0;
1856135446Strhodes	f->fetch = NULL;
1857135446Strhodes
1858135446Strhodes	dns_rdataset_init(&f->rdataset);
1859135446Strhodes
1860135446Strhodes	f->magic = DNS_ADBFETCH_MAGIC;
1861135446Strhodes
1862135446Strhodes	return (f);
1863135446Strhodes}
1864135446Strhodes
1865135446Strhodesstatic inline void
1866135446Strhodesfree_adbfetch(dns_adb_t *adb, dns_adbfetch_t **fetch) {
1867135446Strhodes	dns_adbfetch_t *f;
1868135446Strhodes
1869135446Strhodes	INSIST(fetch != NULL && DNS_ADBFETCH_VALID(*fetch));
1870135446Strhodes	f = *fetch;
1871135446Strhodes	*fetch = NULL;
1872135446Strhodes
1873135446Strhodes	f->magic = 0;
1874135446Strhodes
1875135446Strhodes	if (dns_rdataset_isassociated(&f->rdataset))
1876135446Strhodes		dns_rdataset_disassociate(&f->rdataset);
1877135446Strhodes
1878135446Strhodes	isc_mempool_put(adb->afmp, f);
1879135446Strhodes}
1880135446Strhodes
1881135446Strhodesstatic inline isc_boolean_t
1882135446Strhodesfree_adbfind(dns_adb_t *adb, dns_adbfind_t **findp) {
1883135446Strhodes	dns_adbfind_t *find;
1884135446Strhodes
1885135446Strhodes	INSIST(findp != NULL && DNS_ADBFIND_VALID(*findp));
1886135446Strhodes	find = *findp;
1887135446Strhodes	*findp = NULL;
1888135446Strhodes
1889135446Strhodes	INSIST(!FIND_HAS_ADDRS(find));
1890135446Strhodes	INSIST(!ISC_LINK_LINKED(find, publink));
1891135446Strhodes	INSIST(!ISC_LINK_LINKED(find, plink));
1892135446Strhodes	INSIST(find->name_bucket == DNS_ADB_INVALIDBUCKET);
1893135446Strhodes	INSIST(find->adbname == NULL);
1894135446Strhodes
1895135446Strhodes	find->magic = 0;
1896135446Strhodes
1897135446Strhodes	DESTROYLOCK(&find->lock);
1898135446Strhodes	isc_mempool_put(adb->ahmp, find);
1899135446Strhodes	return (dec_adb_irefcnt(adb));
1900135446Strhodes}
1901135446Strhodes
1902135446Strhodes/*
1903135446Strhodes * Copy bits from the entry into the newly allocated addrinfo.  The entry
1904135446Strhodes * must be locked, and the reference count must be bumped up by one
1905135446Strhodes * if this function returns a valid pointer.
1906135446Strhodes */
1907135446Strhodesstatic inline dns_adbaddrinfo_t *
1908135446Strhodesnew_adbaddrinfo(dns_adb_t *adb, dns_adbentry_t *entry, in_port_t port) {
1909135446Strhodes	dns_adbaddrinfo_t *ai;
1910135446Strhodes
1911135446Strhodes	ai = isc_mempool_get(adb->aimp);
1912135446Strhodes	if (ai == NULL)
1913135446Strhodes		return (NULL);
1914135446Strhodes
1915135446Strhodes	ai->magic = DNS_ADBADDRINFO_MAGIC;
1916135446Strhodes	ai->sockaddr = entry->sockaddr;
1917135446Strhodes	isc_sockaddr_setport(&ai->sockaddr, port);
1918135446Strhodes	ai->srtt = entry->srtt;
1919135446Strhodes	ai->flags = entry->flags;
1920135446Strhodes	ai->entry = entry;
1921135446Strhodes	ISC_LINK_INIT(ai, publink);
1922135446Strhodes
1923135446Strhodes	return (ai);
1924135446Strhodes}
1925135446Strhodes
1926135446Strhodesstatic inline void
1927135446Strhodesfree_adbaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **ainfo) {
1928135446Strhodes	dns_adbaddrinfo_t *ai;
1929135446Strhodes
1930135446Strhodes	INSIST(ainfo != NULL && DNS_ADBADDRINFO_VALID(*ainfo));
1931135446Strhodes	ai = *ainfo;
1932135446Strhodes	*ainfo = NULL;
1933135446Strhodes
1934135446Strhodes	INSIST(ai->entry == NULL);
1935135446Strhodes	INSIST(!ISC_LINK_LINKED(ai, publink));
1936135446Strhodes
1937135446Strhodes	ai->magic = 0;
1938135446Strhodes
1939135446Strhodes	isc_mempool_put(adb->aimp, ai);
1940135446Strhodes}
1941135446Strhodes
1942135446Strhodes/*
1943135446Strhodes * Search for the name.  NOTE:  The bucket is kept locked on both
1944135446Strhodes * success and failure, so it must always be unlocked by the caller!
1945135446Strhodes *
1946135446Strhodes * On the first call to this function, *bucketp must be set to
1947135446Strhodes * DNS_ADB_INVALIDBUCKET.
1948135446Strhodes */
1949135446Strhodesstatic inline dns_adbname_t *
1950135446Strhodesfind_name_and_lock(dns_adb_t *adb, dns_name_t *name,
1951135446Strhodes		   unsigned int options, int *bucketp)
1952135446Strhodes{
1953135446Strhodes	dns_adbname_t *adbname;
1954135446Strhodes	int bucket;
1955135446Strhodes
1956224092Sdougb	bucket = dns_name_fullhash(name, ISC_FALSE) % adb->nnames;
1957135446Strhodes
1958135446Strhodes	if (*bucketp == DNS_ADB_INVALIDBUCKET) {
1959135446Strhodes		LOCK(&adb->namelocks[bucket]);
1960135446Strhodes		*bucketp = bucket;
1961135446Strhodes	} else if (*bucketp != bucket) {
1962135446Strhodes		UNLOCK(&adb->namelocks[*bucketp]);
1963135446Strhodes		LOCK(&adb->namelocks[bucket]);
1964135446Strhodes		*bucketp = bucket;
1965135446Strhodes	}
1966135446Strhodes
1967135446Strhodes	adbname = ISC_LIST_HEAD(adb->names[bucket]);
1968135446Strhodes	while (adbname != NULL) {
1969135446Strhodes		if (!NAME_DEAD(adbname)) {
1970135446Strhodes			if (dns_name_equal(name, &adbname->name)
1971135446Strhodes			    && GLUEHINT_OK(adbname, options)
1972135446Strhodes			    && STARTATZONE_MATCHES(adbname, options))
1973135446Strhodes				return (adbname);
1974135446Strhodes		}
1975135446Strhodes		adbname = ISC_LIST_NEXT(adbname, plink);
1976135446Strhodes	}
1977135446Strhodes
1978135446Strhodes	return (NULL);
1979135446Strhodes}
1980135446Strhodes
1981135446Strhodes/*
1982135446Strhodes * Search for the address.  NOTE:  The bucket is kept locked on both
1983135446Strhodes * success and failure, so it must always be unlocked by the caller.
1984135446Strhodes *
1985135446Strhodes * On the first call to this function, *bucketp must be set to
1986135446Strhodes * DNS_ADB_INVALIDBUCKET.  This will cause a lock to occur.  On
1987135446Strhodes * later calls (within the same "lock path") it can be left alone, so
1988135446Strhodes * if this function is called multiple times locking is only done if
1989135446Strhodes * the bucket changes.
1990135446Strhodes */
1991135446Strhodesstatic inline dns_adbentry_t *
1992193149Sdougbfind_entry_and_lock(dns_adb_t *adb, isc_sockaddr_t *addr, int *bucketp,
1993193149Sdougb	isc_stdtime_t now)
1994193149Sdougb{
1995193149Sdougb	dns_adbentry_t *entry, *entry_next;
1996135446Strhodes	int bucket;
1997135446Strhodes
1998224092Sdougb	bucket = isc_sockaddr_hash(addr, ISC_TRUE) % adb->nentries;
1999135446Strhodes
2000135446Strhodes	if (*bucketp == DNS_ADB_INVALIDBUCKET) {
2001135446Strhodes		LOCK(&adb->entrylocks[bucket]);
2002135446Strhodes		*bucketp = bucket;
2003135446Strhodes	} else if (*bucketp != bucket) {
2004135446Strhodes		UNLOCK(&adb->entrylocks[*bucketp]);
2005135446Strhodes		LOCK(&adb->entrylocks[bucket]);
2006135446Strhodes		*bucketp = bucket;
2007135446Strhodes	}
2008135446Strhodes
2009193149Sdougb	/* Search the list, while cleaning up expired entries. */
2010193149Sdougb	for (entry = ISC_LIST_HEAD(adb->entries[bucket]);
2011193149Sdougb	     entry != NULL;
2012193149Sdougb	     entry = entry_next) {
2013193149Sdougb		entry_next = ISC_LIST_NEXT(entry, plink);
2014193149Sdougb		(void)check_expire_entry(adb, &entry, now);
2015193149Sdougb		if (entry != NULL &&
2016193149Sdougb		    isc_sockaddr_equal(addr, &entry->sockaddr)) {
2017193149Sdougb			ISC_LIST_UNLINK(adb->entries[bucket], entry, plink);
2018193149Sdougb			ISC_LIST_PREPEND(adb->entries[bucket], entry, plink);
2019135446Strhodes			return (entry);
2020193149Sdougb		}
2021135446Strhodes	}
2022135446Strhodes
2023135446Strhodes	return (NULL);
2024135446Strhodes}
2025135446Strhodes
2026135446Strhodes/*
2027135446Strhodes * Entry bucket MUST be locked!
2028135446Strhodes */
2029135446Strhodesstatic isc_boolean_t
2030170222Sdougbentry_is_lame(dns_adb_t *adb, dns_adbentry_t *entry, dns_name_t *qname,
2031170222Sdougb	      dns_rdatatype_t qtype, isc_stdtime_t now)
2032135446Strhodes{
2033170222Sdougb	dns_adblameinfo_t *li, *next_li;
2034135446Strhodes	isc_boolean_t is_bad;
2035135446Strhodes
2036135446Strhodes	is_bad = ISC_FALSE;
2037135446Strhodes
2038170222Sdougb	li = ISC_LIST_HEAD(entry->lameinfo);
2039170222Sdougb	if (li == NULL)
2040135446Strhodes		return (ISC_FALSE);
2041170222Sdougb	while (li != NULL) {
2042170222Sdougb		next_li = ISC_LIST_NEXT(li, plink);
2043135446Strhodes
2044135446Strhodes		/*
2045135446Strhodes		 * Has the entry expired?
2046135446Strhodes		 */
2047170222Sdougb		if (li->lame_timer < now) {
2048170222Sdougb			ISC_LIST_UNLINK(entry->lameinfo, li, plink);
2049170222Sdougb			free_adblameinfo(adb, &li);
2050135446Strhodes		}
2051135446Strhodes
2052135446Strhodes		/*
2053135446Strhodes		 * Order tests from least to most expensive.
2054170222Sdougb		 *
2055170222Sdougb		 * We do not break out of the main loop here as
2056170222Sdougb		 * we use the loop for house keeping.
2057135446Strhodes		 */
2058170222Sdougb		if (li != NULL && !is_bad && li->qtype == qtype &&
2059170222Sdougb		    dns_name_equal(qname, &li->qname))
2060170222Sdougb			is_bad = ISC_TRUE;
2061135446Strhodes
2062170222Sdougb		li = next_li;
2063135446Strhodes	}
2064135446Strhodes
2065135446Strhodes	return (is_bad);
2066135446Strhodes}
2067135446Strhodes
2068135446Strhodesstatic void
2069170222Sdougbcopy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, dns_name_t *qname,
2070170222Sdougb		    dns_rdatatype_t qtype, dns_adbname_t *name,
2071170222Sdougb		    isc_stdtime_t now)
2072135446Strhodes{
2073135446Strhodes	dns_adbnamehook_t *namehook;
2074135446Strhodes	dns_adbaddrinfo_t *addrinfo;
2075135446Strhodes	dns_adbentry_t *entry;
2076135446Strhodes	int bucket;
2077135446Strhodes
2078135446Strhodes	bucket = DNS_ADB_INVALIDBUCKET;
2079135446Strhodes
2080135446Strhodes	if (find->options & DNS_ADBFIND_INET) {
2081135446Strhodes		namehook = ISC_LIST_HEAD(name->v4);
2082135446Strhodes		while (namehook != NULL) {
2083135446Strhodes			entry = namehook->entry;
2084135446Strhodes			bucket = entry->lock_bucket;
2085254402Serwin			INSIST(bucket != DNS_ADB_INVALIDBUCKET);
2086135446Strhodes			LOCK(&adb->entrylocks[bucket]);
2087135446Strhodes
2088135446Strhodes			if (!FIND_RETURNLAME(find)
2089170222Sdougb			    && entry_is_lame(adb, entry, qname, qtype, now)) {
2090135446Strhodes				find->options |= DNS_ADBFIND_LAMEPRUNED;
2091135446Strhodes				goto nextv4;
2092135446Strhodes			}
2093135446Strhodes			addrinfo = new_adbaddrinfo(adb, entry, find->port);
2094135446Strhodes			if (addrinfo == NULL) {
2095135446Strhodes				find->partial_result |= DNS_ADBFIND_INET;
2096135446Strhodes				goto out;
2097135446Strhodes			}
2098135446Strhodes			/*
2099135446Strhodes			 * Found a valid entry.  Add it to the find's list.
2100135446Strhodes			 */
2101135446Strhodes			inc_entry_refcnt(adb, entry, ISC_FALSE);
2102135446Strhodes			ISC_LIST_APPEND(find->list, addrinfo, publink);
2103135446Strhodes			addrinfo = NULL;
2104135446Strhodes		nextv4:
2105135446Strhodes			UNLOCK(&adb->entrylocks[bucket]);
2106135446Strhodes			bucket = DNS_ADB_INVALIDBUCKET;
2107135446Strhodes			namehook = ISC_LIST_NEXT(namehook, plink);
2108135446Strhodes		}
2109135446Strhodes	}
2110135446Strhodes
2111135446Strhodes	if (find->options & DNS_ADBFIND_INET6) {
2112135446Strhodes		namehook = ISC_LIST_HEAD(name->v6);
2113135446Strhodes		while (namehook != NULL) {
2114135446Strhodes			entry = namehook->entry;
2115135446Strhodes			bucket = entry->lock_bucket;
2116254402Serwin			INSIST(bucket != DNS_ADB_INVALIDBUCKET);
2117135446Strhodes			LOCK(&adb->entrylocks[bucket]);
2118135446Strhodes
2119186462Sdougb			if (!FIND_RETURNLAME(find)
2120186462Sdougb			    && entry_is_lame(adb, entry, qname, qtype, now)) {
2121186462Sdougb				find->options |= DNS_ADBFIND_LAMEPRUNED;
2122135446Strhodes				goto nextv6;
2123186462Sdougb			}
2124135446Strhodes			addrinfo = new_adbaddrinfo(adb, entry, find->port);
2125135446Strhodes			if (addrinfo == NULL) {
2126135446Strhodes				find->partial_result |= DNS_ADBFIND_INET6;
2127135446Strhodes				goto out;
2128135446Strhodes			}
2129135446Strhodes			/*
2130135446Strhodes			 * Found a valid entry.  Add it to the find's list.
2131135446Strhodes			 */
2132135446Strhodes			inc_entry_refcnt(adb, entry, ISC_FALSE);
2133135446Strhodes			ISC_LIST_APPEND(find->list, addrinfo, publink);
2134135446Strhodes			addrinfo = NULL;
2135135446Strhodes		nextv6:
2136135446Strhodes			UNLOCK(&adb->entrylocks[bucket]);
2137135446Strhodes			bucket = DNS_ADB_INVALIDBUCKET;
2138135446Strhodes			namehook = ISC_LIST_NEXT(namehook, plink);
2139135446Strhodes		}
2140135446Strhodes	}
2141135446Strhodes
2142135446Strhodes out:
2143135446Strhodes	if (bucket != DNS_ADB_INVALIDBUCKET)
2144135446Strhodes		UNLOCK(&adb->entrylocks[bucket]);
2145135446Strhodes}
2146135446Strhodes
2147135446Strhodesstatic void
2148135446Strhodesshutdown_task(isc_task_t *task, isc_event_t *ev) {
2149135446Strhodes	dns_adb_t *adb;
2150135446Strhodes
2151135446Strhodes	UNUSED(task);
2152135446Strhodes
2153135446Strhodes	adb = ev->ev_arg;
2154135446Strhodes	INSIST(DNS_ADB_VALID(adb));
2155135446Strhodes
2156193149Sdougb	isc_event_free(&ev);
2157135446Strhodes	/*
2158186462Sdougb	 * Wait for lock around check_exit() call to be released.
2159186462Sdougb	 */
2160186462Sdougb	LOCK(&adb->lock);
2161135446Strhodes	UNLOCK(&adb->lock);
2162135446Strhodes	destroy(adb);
2163135446Strhodes}
2164135446Strhodes
2165135446Strhodes/*
2166135446Strhodes * Name bucket must be locked; adb may be locked; no other locks held.
2167135446Strhodes */
2168135446Strhodesstatic isc_boolean_t
2169135446Strhodescheck_expire_name(dns_adbname_t **namep, isc_stdtime_t now) {
2170135446Strhodes	dns_adbname_t *name;
2171153816Sdougb	isc_boolean_t result = ISC_FALSE;
2172135446Strhodes
2173135446Strhodes	INSIST(namep != NULL && DNS_ADBNAME_VALID(*namep));
2174135446Strhodes	name = *namep;
2175135446Strhodes
2176135446Strhodes	if (NAME_HAS_V4(name) || NAME_HAS_V6(name))
2177135446Strhodes		return (result);
2178135446Strhodes	if (NAME_FETCH(name))
2179135446Strhodes		return (result);
2180135446Strhodes	if (!EXPIRE_OK(name->expire_v4, now))
2181135446Strhodes		return (result);
2182135446Strhodes	if (!EXPIRE_OK(name->expire_v6, now))
2183135446Strhodes		return (result);
2184135446Strhodes	if (!EXPIRE_OK(name->expire_target, now))
2185135446Strhodes		return (result);
2186135446Strhodes
2187135446Strhodes	/*
2188135446Strhodes	 * The name is empty.  Delete it.
2189135446Strhodes	 */
2190135446Strhodes	result = kill_name(&name, DNS_EVENT_ADBEXPIRED);
2191135446Strhodes	*namep = NULL;
2192135446Strhodes
2193135446Strhodes	/*
2194135446Strhodes	 * Our caller, or one of its callers, will be calling check_exit() at
2195135446Strhodes	 * some point, so we don't need to do it here.
2196135446Strhodes	 */
2197135446Strhodes	return (result);
2198135446Strhodes}
2199135446Strhodes
2200193149Sdougb/*%
2201193149Sdougb * Examine the tail entry of the LRU list to see if it expires or is stale
2202193149Sdougb * (unused for some period); if so, the name entry will be freed.  If the ADB
2203193149Sdougb * is in the overmem condition, the tail and the next to tail entries
2204193149Sdougb * will be unconditionally removed (unless they have an outstanding fetch).
2205193149Sdougb * We don't care about a race on 'overmem' at the risk of causing some
2206193149Sdougb * collateral damage or a small delay in starting cleanup, so we don't bother
2207193149Sdougb * to lock ADB (if it's not locked).
2208193149Sdougb *
2209193149Sdougb * Name bucket must be locked; adb may be locked; no other locks held.
2210193149Sdougb */
2211193149Sdougbstatic void
2212193149Sdougbcheck_stale_name(dns_adb_t *adb, int bucket, isc_stdtime_t now) {
2213193149Sdougb	int victims, max_victims;
2214193149Sdougb	dns_adbname_t *victim, *next_victim;
2215214586Sdougb	isc_boolean_t overmem = isc_mem_isovermem(adb->mctx);
2216193149Sdougb	int scans = 0;
2217193149Sdougb
2218193149Sdougb	INSIST(bucket != DNS_ADB_INVALIDBUCKET);
2219193149Sdougb
2220193149Sdougb	max_victims = overmem ? 2 : 1;
2221193149Sdougb
2222193149Sdougb	/*
2223193149Sdougb	 * We limit the number of scanned entries to 10 (arbitrary choice)
2224193149Sdougb	 * in order to avoid examining too many entries when there are many
2225193149Sdougb	 * tail entries that have fetches (this should be rare, but could
2226193149Sdougb	 * happen).
2227193149Sdougb	 */
2228193149Sdougb	victim = ISC_LIST_TAIL(adb->names[bucket]);
2229193149Sdougb	for (victims = 0;
2230193149Sdougb	     victim != NULL && victims < max_victims && scans < 10;
2231193149Sdougb	     victim = next_victim) {
2232193149Sdougb		INSIST(!NAME_DEAD(victim));
2233193149Sdougb		scans++;
2234193149Sdougb		next_victim = ISC_LIST_PREV(victim, plink);
2235225361Sdougb		(void)check_expire_name(&victim, now);
2236193149Sdougb		if (victim == NULL) {
2237193149Sdougb			victims++;
2238193149Sdougb			goto next;
2239193149Sdougb		}
2240193149Sdougb
2241193149Sdougb		if (!NAME_FETCH(victim) &&
2242193149Sdougb		    (overmem || victim->last_used + ADB_STALE_MARGIN <= now)) {
2243193149Sdougb			RUNTIME_CHECK(kill_name(&victim,
2244193149Sdougb						DNS_EVENT_ADBCANCELED) ==
2245193149Sdougb				      ISC_FALSE);
2246193149Sdougb			victims++;
2247193149Sdougb		}
2248193149Sdougb
2249193149Sdougb	next:
2250193149Sdougb		if (!overmem)
2251193149Sdougb			break;
2252193149Sdougb	}
2253193149Sdougb}
2254193149Sdougb
2255135446Strhodes/*
2256135446Strhodes * Entry bucket must be locked; adb may be locked; no other locks held.
2257135446Strhodes */
2258135446Strhodesstatic isc_boolean_t
2259135446Strhodescheck_expire_entry(dns_adb_t *adb, dns_adbentry_t **entryp, isc_stdtime_t now)
2260135446Strhodes{
2261135446Strhodes	dns_adbentry_t *entry;
2262135446Strhodes	isc_boolean_t result = ISC_FALSE;
2263135446Strhodes
2264135446Strhodes	INSIST(entryp != NULL && DNS_ADBENTRY_VALID(*entryp));
2265135446Strhodes	entry = *entryp;
2266135446Strhodes
2267135446Strhodes	if (entry->refcnt != 0)
2268135446Strhodes		return (result);
2269135446Strhodes
2270193149Sdougb	if (entry->expires == 0 || entry->expires > now)
2271135446Strhodes		return (result);
2272135446Strhodes
2273135446Strhodes	/*
2274135446Strhodes	 * The entry is not in use.  Delete it.
2275135446Strhodes	 */
2276135446Strhodes	DP(DEF_LEVEL, "killing entry %p", entry);
2277135446Strhodes	INSIST(ISC_LINK_LINKED(entry, plink));
2278135446Strhodes	result = unlink_entry(adb, entry);
2279135446Strhodes	free_adbentry(adb, &entry);
2280135446Strhodes	if (result)
2281135446Strhodes		dec_adb_irefcnt(adb);
2282135446Strhodes	*entryp = NULL;
2283135446Strhodes	return (result);
2284135446Strhodes}
2285135446Strhodes
2286135446Strhodes/*
2287135446Strhodes * ADB must be locked, and no other locks held.
2288135446Strhodes */
2289135446Strhodesstatic isc_boolean_t
2290135446Strhodescleanup_names(dns_adb_t *adb, int bucket, isc_stdtime_t now) {
2291135446Strhodes	dns_adbname_t *name;
2292135446Strhodes	dns_adbname_t *next_name;
2293153816Sdougb	isc_boolean_t result = ISC_FALSE;
2294135446Strhodes
2295135446Strhodes	DP(CLEAN_LEVEL, "cleaning name bucket %d", bucket);
2296135446Strhodes
2297135446Strhodes	LOCK(&adb->namelocks[bucket]);
2298135446Strhodes	if (adb->name_sd[bucket]) {
2299135446Strhodes		UNLOCK(&adb->namelocks[bucket]);
2300135446Strhodes		return (result);
2301135446Strhodes	}
2302135446Strhodes
2303135446Strhodes	name = ISC_LIST_HEAD(adb->names[bucket]);
2304135446Strhodes	while (name != NULL) {
2305135446Strhodes		next_name = ISC_LIST_NEXT(name, plink);
2306135446Strhodes		INSIST(result == ISC_FALSE);
2307193149Sdougb		result = check_expire_namehooks(name, now);
2308135446Strhodes		if (!result)
2309135446Strhodes			result = check_expire_name(&name, now);
2310135446Strhodes		name = next_name;
2311135446Strhodes	}
2312135446Strhodes	UNLOCK(&adb->namelocks[bucket]);
2313135446Strhodes	return (result);
2314135446Strhodes}
2315135446Strhodes
2316135446Strhodes/*
2317135446Strhodes * ADB must be locked, and no other locks held.
2318135446Strhodes */
2319135446Strhodesstatic isc_boolean_t
2320135446Strhodescleanup_entries(dns_adb_t *adb, int bucket, isc_stdtime_t now) {
2321135446Strhodes	dns_adbentry_t *entry, *next_entry;
2322135446Strhodes	isc_boolean_t result = ISC_FALSE;
2323135446Strhodes
2324135446Strhodes	DP(CLEAN_LEVEL, "cleaning entry bucket %d", bucket);
2325135446Strhodes
2326135446Strhodes	LOCK(&adb->entrylocks[bucket]);
2327135446Strhodes	entry = ISC_LIST_HEAD(adb->entries[bucket]);
2328135446Strhodes	while (entry != NULL) {
2329135446Strhodes		next_entry = ISC_LIST_NEXT(entry, plink);
2330135446Strhodes		INSIST(result == ISC_FALSE);
2331135446Strhodes		result = check_expire_entry(adb, &entry, now);
2332135446Strhodes		entry = next_entry;
2333135446Strhodes	}
2334135446Strhodes	UNLOCK(&adb->entrylocks[bucket]);
2335135446Strhodes	return (result);
2336135446Strhodes}
2337135446Strhodes
2338135446Strhodesstatic void
2339135446Strhodesdestroy(dns_adb_t *adb) {
2340135446Strhodes	adb->magic = 0;
2341135446Strhodes
2342135446Strhodes	isc_task_detach(&adb->task);
2343254402Serwin	if (adb->excl != NULL)
2344254402Serwin		isc_task_detach(&adb->excl);
2345135446Strhodes
2346135446Strhodes	isc_mempool_destroy(&adb->nmp);
2347135446Strhodes	isc_mempool_destroy(&adb->nhmp);
2348170222Sdougb	isc_mempool_destroy(&adb->limp);
2349135446Strhodes	isc_mempool_destroy(&adb->emp);
2350135446Strhodes	isc_mempool_destroy(&adb->ahmp);
2351135446Strhodes	isc_mempool_destroy(&adb->aimp);
2352135446Strhodes	isc_mempool_destroy(&adb->afmp);
2353135446Strhodes
2354224092Sdougb	DESTROYMUTEXBLOCK(adb->entrylocks, adb->nentries);
2355224092Sdougb	isc_mem_put(adb->mctx, adb->entries,
2356224092Sdougb		    sizeof(*adb->entries) * adb->nentries);
2357224092Sdougb	isc_mem_put(adb->mctx, adb->deadentries,
2358224092Sdougb		    sizeof(*adb->deadentries) * adb->nentries);
2359224092Sdougb	isc_mem_put(adb->mctx, adb->entrylocks,
2360224092Sdougb		    sizeof(*adb->entrylocks) * adb->nentries);
2361224092Sdougb	isc_mem_put(adb->mctx, adb->entry_sd,
2362224092Sdougb		    sizeof(*adb->entry_sd) * adb->nentries);
2363224092Sdougb	isc_mem_put(adb->mctx, adb->entry_refcnt,
2364224092Sdougb		    sizeof(*adb->entry_refcnt) * adb->nentries);
2365135446Strhodes
2366224092Sdougb	DESTROYMUTEXBLOCK(adb->namelocks, adb->nnames);
2367224092Sdougb	isc_mem_put(adb->mctx, adb->names,
2368224092Sdougb		    sizeof(*adb->names) * adb->nnames);
2369224092Sdougb	isc_mem_put(adb->mctx, adb->deadnames,
2370224092Sdougb		    sizeof(*adb->deadnames) * adb->nnames);
2371224092Sdougb	isc_mem_put(adb->mctx, adb->namelocks,
2372224092Sdougb		    sizeof(*adb->namelocks) * adb->nnames);
2373224092Sdougb	isc_mem_put(adb->mctx, adb->name_sd,
2374224092Sdougb		    sizeof(*adb->name_sd) * adb->nnames);
2375224092Sdougb	isc_mem_put(adb->mctx, adb->name_refcnt,
2376224092Sdougb		    sizeof(*adb->name_refcnt) * adb->nnames);
2377224092Sdougb
2378135446Strhodes	DESTROYLOCK(&adb->reflock);
2379135446Strhodes	DESTROYLOCK(&adb->lock);
2380135446Strhodes	DESTROYLOCK(&adb->mplock);
2381186462Sdougb	DESTROYLOCK(&adb->overmemlock);
2382224092Sdougb	DESTROYLOCK(&adb->entriescntlock);
2383224092Sdougb	DESTROYLOCK(&adb->namescntlock);
2384135446Strhodes
2385135446Strhodes	isc_mem_putanddetach(&adb->mctx, adb, sizeof(dns_adb_t));
2386135446Strhodes}
2387135446Strhodes
2388135446Strhodes
2389135446Strhodes/*
2390135446Strhodes * Public functions.
2391135446Strhodes */
2392135446Strhodes
2393135446Strhodesisc_result_t
2394135446Strhodesdns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_timermgr_t *timermgr,
2395135446Strhodes	       isc_taskmgr_t *taskmgr, dns_adb_t **newadb)
2396135446Strhodes{
2397135446Strhodes	dns_adb_t *adb;
2398135446Strhodes	isc_result_t result;
2399224092Sdougb	unsigned int i;
2400135446Strhodes
2401135446Strhodes	REQUIRE(mem != NULL);
2402135446Strhodes	REQUIRE(view != NULL);
2403193149Sdougb	REQUIRE(timermgr != NULL); /* this is actually unused */
2404135446Strhodes	REQUIRE(taskmgr != NULL);
2405135446Strhodes	REQUIRE(newadb != NULL && *newadb == NULL);
2406135446Strhodes
2407193149Sdougb	UNUSED(timermgr);
2408193149Sdougb
2409135446Strhodes	adb = isc_mem_get(mem, sizeof(dns_adb_t));
2410135446Strhodes	if (adb == NULL)
2411135446Strhodes		return (ISC_R_NOMEMORY);
2412135446Strhodes
2413135446Strhodes	/*
2414135446Strhodes	 * Initialize things here that cannot fail, and especially things
2415135446Strhodes	 * that must be NULL for the error return to work properly.
2416135446Strhodes	 */
2417135446Strhodes	adb->magic = 0;
2418135446Strhodes	adb->erefcnt = 1;
2419135446Strhodes	adb->irefcnt = 0;
2420135446Strhodes	adb->nmp = NULL;
2421135446Strhodes	adb->nhmp = NULL;
2422170222Sdougb	adb->limp = NULL;
2423135446Strhodes	adb->emp = NULL;
2424135446Strhodes	adb->ahmp = NULL;
2425135446Strhodes	adb->aimp = NULL;
2426135446Strhodes	adb->afmp = NULL;
2427135446Strhodes	adb->task = NULL;
2428245163Serwin	adb->excl = NULL;
2429135446Strhodes	adb->mctx = NULL;
2430135446Strhodes	adb->view = view;
2431135446Strhodes	adb->taskmgr = taskmgr;
2432135446Strhodes	adb->next_cleanbucket = 0;
2433135446Strhodes	ISC_EVENT_INIT(&adb->cevent, sizeof(adb->cevent), 0, NULL,
2434135446Strhodes		       DNS_EVENT_ADBCONTROL, shutdown_task, adb,
2435135446Strhodes		       adb, NULL, NULL);
2436135446Strhodes	adb->cevent_sent = ISC_FALSE;
2437135446Strhodes	adb->shutting_down = ISC_FALSE;
2438135446Strhodes	ISC_LIST_INIT(adb->whenshutdown);
2439135446Strhodes
2440224092Sdougb	adb->nentries = nbuckets[0];
2441224092Sdougb	adb->entriescnt = 0;
2442224092Sdougb	adb->entries = NULL;
2443224092Sdougb	adb->deadentries = NULL;
2444224092Sdougb	adb->entry_sd = NULL;
2445224092Sdougb	adb->entry_refcnt = NULL;
2446224092Sdougb	adb->entrylocks = NULL;
2447224092Sdougb	ISC_EVENT_INIT(&adb->growentries, sizeof(adb->growentries), 0, NULL,
2448224092Sdougb		       DNS_EVENT_ADBGROWENTRIES, grow_entries, adb,
2449224092Sdougb		       adb, NULL, NULL);
2450224092Sdougb	adb->growentries_sent = ISC_FALSE;
2451224092Sdougb
2452224092Sdougb	adb->nnames = nbuckets[0];
2453224092Sdougb	adb->namescnt = 0;
2454224092Sdougb	adb->names = NULL;
2455224092Sdougb	adb->deadnames = NULL;
2456224092Sdougb	adb->name_sd = NULL;
2457224092Sdougb	adb->name_refcnt = NULL;
2458224092Sdougb	adb->namelocks = NULL;
2459224092Sdougb	ISC_EVENT_INIT(&adb->grownames, sizeof(adb->grownames), 0, NULL,
2460224092Sdougb		       DNS_EVENT_ADBGROWNAMES, grow_names, adb,
2461224092Sdougb		       adb, NULL, NULL);
2462224092Sdougb	adb->grownames_sent = ISC_FALSE;
2463224092Sdougb
2464245163Serwin	result = isc_taskmgr_excltask(adb->taskmgr, &adb->excl);
2465245163Serwin	if (result != ISC_R_SUCCESS) {
2466245163Serwin		DP(ISC_LOG_INFO, "adb: task-exclusive mode unavailable, "
2467245163Serwin				 "intializing table sizes to %u\n",
2468245163Serwin				 nbuckets[11]);
2469245163Serwin		adb->nentries = nbuckets[11];
2470245163Serwin		adb->nnames= nbuckets[11];
2471245163Serwin
2472245163Serwin	}
2473245163Serwin
2474135446Strhodes	isc_mem_attach(mem, &adb->mctx);
2475135446Strhodes
2476135446Strhodes	result = isc_mutex_init(&adb->lock);
2477135446Strhodes	if (result != ISC_R_SUCCESS)
2478135446Strhodes		goto fail0b;
2479135446Strhodes
2480135446Strhodes	result = isc_mutex_init(&adb->mplock);
2481135446Strhodes	if (result != ISC_R_SUCCESS)
2482135446Strhodes		goto fail0c;
2483135446Strhodes
2484135446Strhodes	result = isc_mutex_init(&adb->reflock);
2485135446Strhodes	if (result != ISC_R_SUCCESS)
2486135446Strhodes		goto fail0d;
2487135446Strhodes
2488186462Sdougb	result = isc_mutex_init(&adb->overmemlock);
2489186462Sdougb	if (result != ISC_R_SUCCESS)
2490186462Sdougb		goto fail0e;
2491186462Sdougb
2492224092Sdougb	result = isc_mutex_init(&adb->entriescntlock);
2493224092Sdougb	if (result != ISC_R_SUCCESS)
2494224092Sdougb		goto fail0f;
2495224092Sdougb
2496224092Sdougb	result = isc_mutex_init(&adb->namescntlock);
2497224092Sdougb	if (result != ISC_R_SUCCESS)
2498224092Sdougb		goto fail0g;
2499224092Sdougb
2500224092Sdougb#define ALLOCENTRY(adb, el) \
2501224092Sdougb	do { \
2502224092Sdougb		(adb)->el = isc_mem_get((adb)->mctx, \
2503224092Sdougb				     sizeof(*(adb)->el) * (adb)->nentries); \
2504224092Sdougb		if ((adb)->el == NULL) { \
2505224092Sdougb			result = ISC_R_NOMEMORY; \
2506224092Sdougb			goto fail1; \
2507224092Sdougb		}\
2508224092Sdougb	} while (0)
2509224092Sdougb	ALLOCENTRY(adb, entries);
2510224092Sdougb	ALLOCENTRY(adb, deadentries);
2511224092Sdougb	ALLOCENTRY(adb, entrylocks);
2512224092Sdougb	ALLOCENTRY(adb, entry_sd);
2513224092Sdougb	ALLOCENTRY(adb, entry_refcnt);
2514224092Sdougb#undef ALLOCENTRY
2515224092Sdougb
2516224092Sdougb#define ALLOCNAME(adb, el) \
2517224092Sdougb	do { \
2518224092Sdougb		(adb)->el = isc_mem_get((adb)->mctx, \
2519224092Sdougb				     sizeof(*(adb)->el) * (adb)->nnames); \
2520224092Sdougb		if ((adb)->el == NULL) { \
2521224092Sdougb			result = ISC_R_NOMEMORY; \
2522224092Sdougb			goto fail1; \
2523224092Sdougb		}\
2524224092Sdougb	} while (0)
2525224092Sdougb	ALLOCNAME(adb, names);
2526224092Sdougb	ALLOCNAME(adb, deadnames);
2527224092Sdougb	ALLOCNAME(adb, namelocks);
2528224092Sdougb	ALLOCNAME(adb, name_sd);
2529224092Sdougb	ALLOCNAME(adb, name_refcnt);
2530224092Sdougb#undef ALLOCNAME
2531224092Sdougb
2532135446Strhodes	/*
2533135446Strhodes	 * Initialize the bucket locks for names and elements.
2534135446Strhodes	 * May as well initialize the list heads, too.
2535135446Strhodes	 */
2536224092Sdougb	result = isc_mutexblock_init(adb->namelocks, adb->nnames);
2537135446Strhodes	if (result != ISC_R_SUCCESS)
2538135446Strhodes		goto fail1;
2539224092Sdougb	for (i = 0; i < adb->nnames; i++) {
2540135446Strhodes		ISC_LIST_INIT(adb->names[i]);
2541193149Sdougb		ISC_LIST_INIT(adb->deadnames[i]);
2542135446Strhodes		adb->name_sd[i] = ISC_FALSE;
2543135446Strhodes		adb->name_refcnt[i] = 0;
2544135446Strhodes		adb->irefcnt++;
2545135446Strhodes	}
2546224092Sdougb	for (i = 0; i < adb->nentries; i++) {
2547135446Strhodes		ISC_LIST_INIT(adb->entries[i]);
2548193149Sdougb		ISC_LIST_INIT(adb->deadentries[i]);
2549135446Strhodes		adb->entry_sd[i] = ISC_FALSE;
2550135446Strhodes		adb->entry_refcnt[i] = 0;
2551135446Strhodes		adb->irefcnt++;
2552135446Strhodes	}
2553224092Sdougb	result = isc_mutexblock_init(adb->entrylocks, adb->nentries);
2554135446Strhodes	if (result != ISC_R_SUCCESS)
2555135446Strhodes		goto fail2;
2556135446Strhodes
2557135446Strhodes	/*
2558135446Strhodes	 * Memory pools
2559135446Strhodes	 */
2560135446Strhodes#define MPINIT(t, p, n) do { \
2561135446Strhodes	result = isc_mempool_create(mem, sizeof(t), &(p)); \
2562135446Strhodes	if (result != ISC_R_SUCCESS) \
2563135446Strhodes		goto fail3; \
2564135446Strhodes	isc_mempool_setfreemax((p), FREE_ITEMS); \
2565135446Strhodes	isc_mempool_setfillcount((p), FILL_COUNT); \
2566135446Strhodes	isc_mempool_setname((p), n); \
2567135446Strhodes	isc_mempool_associatelock((p), &adb->mplock); \
2568135446Strhodes} while (0)
2569135446Strhodes
2570135446Strhodes	MPINIT(dns_adbname_t, adb->nmp, "adbname");
2571135446Strhodes	MPINIT(dns_adbnamehook_t, adb->nhmp, "adbnamehook");
2572170222Sdougb	MPINIT(dns_adblameinfo_t, adb->limp, "adblameinfo");
2573135446Strhodes	MPINIT(dns_adbentry_t, adb->emp, "adbentry");
2574135446Strhodes	MPINIT(dns_adbfind_t, adb->ahmp, "adbfind");
2575135446Strhodes	MPINIT(dns_adbaddrinfo_t, adb->aimp, "adbaddrinfo");
2576135446Strhodes	MPINIT(dns_adbfetch_t, adb->afmp, "adbfetch");
2577135446Strhodes
2578135446Strhodes#undef MPINIT
2579135446Strhodes
2580135446Strhodes	/*
2581193149Sdougb	 * Allocate an internal task.
2582135446Strhodes	 */
2583135446Strhodes	result = isc_task_create(adb->taskmgr, 0, &adb->task);
2584135446Strhodes	if (result != ISC_R_SUCCESS)
2585135446Strhodes		goto fail3;
2586245163Serwin
2587135446Strhodes	isc_task_setname(adb->task, "ADB", adb);
2588135446Strhodes
2589135446Strhodes	/*
2590135446Strhodes	 * Normal return.
2591135446Strhodes	 */
2592135446Strhodes	adb->magic = DNS_ADB_MAGIC;
2593135446Strhodes	*newadb = adb;
2594135446Strhodes	return (ISC_R_SUCCESS);
2595135446Strhodes
2596135446Strhodes fail3:
2597135446Strhodes	if (adb->task != NULL)
2598135446Strhodes		isc_task_detach(&adb->task);
2599135446Strhodes
2600135446Strhodes	/* clean up entrylocks */
2601224092Sdougb	DESTROYMUTEXBLOCK(adb->entrylocks, adb->nentries);
2602135446Strhodes
2603135446Strhodes fail2: /* clean up namelocks */
2604224092Sdougb	DESTROYMUTEXBLOCK(adb->namelocks, adb->nnames);
2605135446Strhodes
2606135446Strhodes fail1: /* clean up only allocated memory */
2607224092Sdougb	if (adb->entries != NULL)
2608224092Sdougb		isc_mem_put(adb->mctx, adb->entries,
2609224092Sdougb			    sizeof(*adb->entries) * adb->nentries);
2610224092Sdougb	if (adb->deadentries != NULL)
2611224092Sdougb		isc_mem_put(adb->mctx, adb->deadentries,
2612224092Sdougb			    sizeof(*adb->deadentries) * adb->nentries);
2613224092Sdougb	if (adb->entrylocks != NULL)
2614224092Sdougb		isc_mem_put(adb->mctx, adb->entrylocks,
2615224092Sdougb			    sizeof(*adb->entrylocks) * adb->nentries);
2616224092Sdougb	if (adb->entry_sd != NULL)
2617224092Sdougb		isc_mem_put(adb->mctx, adb->entry_sd,
2618224092Sdougb			    sizeof(*adb->entry_sd) * adb->nentries);
2619224092Sdougb	if (adb->entry_refcnt != NULL)
2620224092Sdougb		isc_mem_put(adb->mctx, adb->entry_refcnt,
2621224092Sdougb			    sizeof(*adb->entry_refcnt) * adb->nentries);
2622224092Sdougb	if (adb->names != NULL)
2623224092Sdougb		isc_mem_put(adb->mctx, adb->names,
2624224092Sdougb			    sizeof(*adb->names) * adb->nnames);
2625224092Sdougb	if (adb->deadnames != NULL)
2626224092Sdougb		isc_mem_put(adb->mctx, adb->deadnames,
2627224092Sdougb			    sizeof(*adb->deadnames) * adb->nnames);
2628224092Sdougb	if (adb->namelocks != NULL)
2629224092Sdougb		isc_mem_put(adb->mctx, adb->namelocks,
2630224092Sdougb			    sizeof(*adb->namelocks) * adb->nnames);
2631224092Sdougb	if (adb->name_sd != NULL)
2632224092Sdougb		isc_mem_put(adb->mctx, adb->name_sd,
2633224092Sdougb			    sizeof(*adb->name_sd) * adb->nnames);
2634224092Sdougb	if (adb->name_refcnt != NULL)
2635224092Sdougb		isc_mem_put(adb->mctx, adb->name_refcnt,
2636224092Sdougb			    sizeof(*adb->name_refcnt) * adb->nnames);
2637135446Strhodes	if (adb->nmp != NULL)
2638135446Strhodes		isc_mempool_destroy(&adb->nmp);
2639135446Strhodes	if (adb->nhmp != NULL)
2640135446Strhodes		isc_mempool_destroy(&adb->nhmp);
2641170222Sdougb	if (adb->limp != NULL)
2642170222Sdougb		isc_mempool_destroy(&adb->limp);
2643135446Strhodes	if (adb->emp != NULL)
2644135446Strhodes		isc_mempool_destroy(&adb->emp);
2645135446Strhodes	if (adb->ahmp != NULL)
2646135446Strhodes		isc_mempool_destroy(&adb->ahmp);
2647135446Strhodes	if (adb->aimp != NULL)
2648135446Strhodes		isc_mempool_destroy(&adb->aimp);
2649135446Strhodes	if (adb->afmp != NULL)
2650135446Strhodes		isc_mempool_destroy(&adb->afmp);
2651135446Strhodes
2652224092Sdougb	DESTROYLOCK(&adb->namescntlock);
2653224092Sdougb fail0g:
2654224092Sdougb	DESTROYLOCK(&adb->entriescntlock);
2655224092Sdougb fail0f:
2656186462Sdougb	DESTROYLOCK(&adb->overmemlock);
2657186462Sdougb fail0e:
2658135446Strhodes	DESTROYLOCK(&adb->reflock);
2659135446Strhodes fail0d:
2660135446Strhodes	DESTROYLOCK(&adb->mplock);
2661135446Strhodes fail0c:
2662135446Strhodes	DESTROYLOCK(&adb->lock);
2663135446Strhodes fail0b:
2664135446Strhodes	isc_mem_putanddetach(&adb->mctx, adb, sizeof(dns_adb_t));
2665135446Strhodes
2666135446Strhodes	return (result);
2667135446Strhodes}
2668135446Strhodes
2669135446Strhodesvoid
2670135446Strhodesdns_adb_attach(dns_adb_t *adb, dns_adb_t **adbx) {
2671135446Strhodes
2672135446Strhodes	REQUIRE(DNS_ADB_VALID(adb));
2673135446Strhodes	REQUIRE(adbx != NULL && *adbx == NULL);
2674135446Strhodes
2675135446Strhodes	inc_adb_erefcnt(adb);
2676135446Strhodes	*adbx = adb;
2677135446Strhodes}
2678135446Strhodes
2679135446Strhodesvoid
2680135446Strhodesdns_adb_detach(dns_adb_t **adbx) {
2681135446Strhodes	dns_adb_t *adb;
2682135446Strhodes	isc_boolean_t need_exit_check;
2683135446Strhodes
2684135446Strhodes	REQUIRE(adbx != NULL && DNS_ADB_VALID(*adbx));
2685135446Strhodes
2686135446Strhodes	adb = *adbx;
2687135446Strhodes	*adbx = NULL;
2688135446Strhodes
2689135446Strhodes	INSIST(adb->erefcnt > 0);
2690135446Strhodes
2691135446Strhodes	LOCK(&adb->reflock);
2692135446Strhodes	adb->erefcnt--;
2693135446Strhodes	need_exit_check = ISC_TF(adb->erefcnt == 0 && adb->irefcnt == 0);
2694135446Strhodes	UNLOCK(&adb->reflock);
2695135446Strhodes
2696135446Strhodes	if (need_exit_check) {
2697135446Strhodes		LOCK(&adb->lock);
2698135446Strhodes		INSIST(adb->shutting_down);
2699135446Strhodes		check_exit(adb);
2700135446Strhodes		UNLOCK(&adb->lock);
2701135446Strhodes	}
2702135446Strhodes}
2703135446Strhodes
2704135446Strhodesvoid
2705135446Strhodesdns_adb_whenshutdown(dns_adb_t *adb, isc_task_t *task, isc_event_t **eventp) {
2706135446Strhodes	isc_task_t *clone;
2707135446Strhodes	isc_event_t *event;
2708135446Strhodes	isc_boolean_t zeroirefcnt = ISC_FALSE;
2709135446Strhodes
2710135446Strhodes	/*
2711135446Strhodes	 * Send '*eventp' to 'task' when 'adb' has shutdown.
2712135446Strhodes	 */
2713135446Strhodes
2714135446Strhodes	REQUIRE(DNS_ADB_VALID(adb));
2715135446Strhodes	REQUIRE(eventp != NULL);
2716135446Strhodes
2717135446Strhodes	event = *eventp;
2718135446Strhodes	*eventp = NULL;
2719135446Strhodes
2720135446Strhodes	LOCK(&adb->lock);
2721135446Strhodes
2722135446Strhodes	LOCK(&adb->reflock);
2723135446Strhodes	zeroirefcnt = ISC_TF(adb->irefcnt == 0);
2724135446Strhodes
2725135446Strhodes	if (adb->shutting_down && zeroirefcnt &&
2726135446Strhodes	    isc_mempool_getallocated(adb->ahmp) == 0) {
2727135446Strhodes		/*
2728135446Strhodes		 * We're already shutdown.  Send the event.
2729135446Strhodes		 */
2730135446Strhodes		event->ev_sender = adb;
2731135446Strhodes		isc_task_send(task, &event);
2732135446Strhodes	} else {
2733135446Strhodes		clone = NULL;
2734135446Strhodes		isc_task_attach(task, &clone);
2735135446Strhodes		event->ev_sender = clone;
2736135446Strhodes		ISC_LIST_APPEND(adb->whenshutdown, event, ev_link);
2737135446Strhodes	}
2738135446Strhodes
2739135446Strhodes	UNLOCK(&adb->reflock);
2740135446Strhodes	UNLOCK(&adb->lock);
2741135446Strhodes}
2742135446Strhodes
2743135446Strhodesvoid
2744135446Strhodesdns_adb_shutdown(dns_adb_t *adb) {
2745135446Strhodes	isc_boolean_t need_check_exit;
2746135446Strhodes
2747135446Strhodes	/*
2748135446Strhodes	 * Shutdown 'adb'.
2749135446Strhodes	 */
2750135446Strhodes
2751135446Strhodes	LOCK(&adb->lock);
2752135446Strhodes
2753135446Strhodes	if (!adb->shutting_down) {
2754135446Strhodes		adb->shutting_down = ISC_TRUE;
2755135446Strhodes		isc_mem_setwater(adb->mctx, water, adb, 0, 0);
2756135446Strhodes		need_check_exit = shutdown_names(adb);
2757135446Strhodes		if (!need_check_exit)
2758135446Strhodes			need_check_exit = shutdown_entries(adb);
2759135446Strhodes		if (need_check_exit)
2760135446Strhodes			check_exit(adb);
2761135446Strhodes	}
2762135446Strhodes
2763135446Strhodes	UNLOCK(&adb->lock);
2764135446Strhodes}
2765135446Strhodes
2766135446Strhodesisc_result_t
2767135446Strhodesdns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action,
2768170222Sdougb		   void *arg, dns_name_t *name, dns_name_t *qname,
2769170222Sdougb		   dns_rdatatype_t qtype, unsigned int options,
2770170222Sdougb		   isc_stdtime_t now, dns_name_t *target,
2771135446Strhodes		   in_port_t port, dns_adbfind_t **findp)
2772135446Strhodes{
2773135446Strhodes	dns_adbfind_t *find;
2774135446Strhodes	dns_adbname_t *adbname;
2775135446Strhodes	int bucket;
2776135446Strhodes	isc_boolean_t want_event, start_at_zone, alias, have_address;
2777135446Strhodes	isc_result_t result;
2778135446Strhodes	unsigned int wanted_addresses;
2779135446Strhodes	unsigned int wanted_fetches;
2780135446Strhodes	unsigned int query_pending;
2781135446Strhodes
2782135446Strhodes	REQUIRE(DNS_ADB_VALID(adb));
2783135446Strhodes	if (task != NULL) {
2784135446Strhodes		REQUIRE(action != NULL);
2785135446Strhodes	}
2786135446Strhodes	REQUIRE(name != NULL);
2787170222Sdougb	REQUIRE(qname != NULL);
2788135446Strhodes	REQUIRE(findp != NULL && *findp == NULL);
2789135446Strhodes	REQUIRE(target == NULL || dns_name_hasbuffer(target));
2790135446Strhodes
2791135446Strhodes	REQUIRE((options & DNS_ADBFIND_ADDRESSMASK) != 0);
2792135446Strhodes
2793135446Strhodes	result = ISC_R_UNEXPECTED;
2794225361Sdougb	POST(result);
2795135446Strhodes	wanted_addresses = (options & DNS_ADBFIND_ADDRESSMASK);
2796135446Strhodes	wanted_fetches = 0;
2797135446Strhodes	query_pending = 0;
2798135446Strhodes	want_event = ISC_FALSE;
2799135446Strhodes	start_at_zone = ISC_FALSE;
2800135446Strhodes	alias = ISC_FALSE;
2801135446Strhodes
2802135446Strhodes	if (now == 0)
2803135446Strhodes		isc_stdtime_get(&now);
2804135446Strhodes
2805135446Strhodes	/*
2806135446Strhodes	 * XXXMLG  Move this comment somewhere else!
2807135446Strhodes	 *
2808135446Strhodes	 * Look up the name in our internal database.
2809135446Strhodes	 *
2810135446Strhodes	 * Possibilities:  Note that these are not always exclusive.
2811135446Strhodes	 *
2812193149Sdougb	 *      No name found.  In this case, allocate a new name header and
2813193149Sdougb	 *      an initial namehook or two.  If any of these allocations
2814193149Sdougb	 *      fail, clean up and return ISC_R_NOMEMORY.
2815135446Strhodes	 *
2816193149Sdougb	 *      Name found, valid addresses present.  Allocate one addrinfo
2817193149Sdougb	 *      structure for each found and append it to the linked list
2818193149Sdougb	 *      of addresses for this header.
2819135446Strhodes	 *
2820193149Sdougb	 *      Name found, queries pending.  In this case, if a task was
2821193149Sdougb	 *      passed in, allocate a job id, attach it to the name's job
2822193149Sdougb	 *      list and remember to tell the caller that there will be
2823193149Sdougb	 *      more info coming later.
2824135446Strhodes	 */
2825135446Strhodes
2826135446Strhodes	find = new_adbfind(adb);
2827135446Strhodes	if (find == NULL)
2828135446Strhodes		return (ISC_R_NOMEMORY);
2829135446Strhodes
2830135446Strhodes	find->port = port;
2831135446Strhodes
2832135446Strhodes	/*
2833135446Strhodes	 * Remember what types of addresses we are interested in.
2834135446Strhodes	 */
2835135446Strhodes	find->options = options;
2836135446Strhodes	find->flags |= wanted_addresses;
2837135446Strhodes	if (FIND_WANTEVENT(find)) {
2838135446Strhodes		REQUIRE(task != NULL);
2839135446Strhodes	}
2840135446Strhodes
2841135446Strhodes	/*
2842135446Strhodes	 * Try to see if we know anything about this name at all.
2843135446Strhodes	 */
2844135446Strhodes	bucket = DNS_ADB_INVALIDBUCKET;
2845135446Strhodes	adbname = find_name_and_lock(adb, name, find->options, &bucket);
2846225361Sdougb	INSIST(bucket != DNS_ADB_INVALIDBUCKET);
2847135446Strhodes	if (adb->name_sd[bucket]) {
2848135446Strhodes		DP(DEF_LEVEL,
2849135446Strhodes		   "dns_adb_createfind: returning ISC_R_SHUTTINGDOWN");
2850135446Strhodes		RUNTIME_CHECK(free_adbfind(adb, &find) == ISC_FALSE);
2851135446Strhodes		result = ISC_R_SHUTTINGDOWN;
2852135446Strhodes		goto out;
2853135446Strhodes	}
2854135446Strhodes
2855135446Strhodes	/*
2856135446Strhodes	 * Nothing found.  Allocate a new adbname structure for this name.
2857135446Strhodes	 */
2858135446Strhodes	if (adbname == NULL) {
2859193149Sdougb		/*
2860193149Sdougb		 * See if there is any stale name at the end of list, and purge
2861193149Sdougb		 * it if so.
2862193149Sdougb		 */
2863193149Sdougb		check_stale_name(adb, bucket, now);
2864193149Sdougb
2865135446Strhodes		adbname = new_adbname(adb, name);
2866135446Strhodes		if (adbname == NULL) {
2867135446Strhodes			RUNTIME_CHECK(free_adbfind(adb, &find) == ISC_FALSE);
2868135446Strhodes			result = ISC_R_NOMEMORY;
2869135446Strhodes			goto out;
2870135446Strhodes		}
2871135446Strhodes		link_name(adb, bucket, adbname);
2872135446Strhodes		if (FIND_HINTOK(find))
2873135446Strhodes			adbname->flags |= NAME_HINT_OK;
2874135446Strhodes		if (FIND_GLUEOK(find))
2875135446Strhodes			adbname->flags |= NAME_GLUE_OK;
2876135446Strhodes		if (FIND_STARTATZONE(find))
2877135446Strhodes			adbname->flags |= NAME_STARTATZONE;
2878193149Sdougb	} else {
2879193149Sdougb		/* Move this name forward in the LRU list */
2880193149Sdougb		ISC_LIST_UNLINK(adb->names[bucket], adbname, plink);
2881193149Sdougb		ISC_LIST_PREPEND(adb->names[bucket], adbname, plink);
2882135446Strhodes	}
2883193149Sdougb	adbname->last_used = now;
2884135446Strhodes
2885135446Strhodes	/*
2886135446Strhodes	 * Expire old entries, etc.
2887135446Strhodes	 */
2888193149Sdougb	RUNTIME_CHECK(check_expire_namehooks(adbname, now) == ISC_FALSE);
2889135446Strhodes
2890135446Strhodes	/*
2891135446Strhodes	 * Do we know that the name is an alias?
2892135446Strhodes	 */
2893135446Strhodes	if (!EXPIRE_OK(adbname->expire_target, now)) {
2894135446Strhodes		/*
2895135446Strhodes		 * Yes, it is.
2896135446Strhodes		 */
2897135446Strhodes		DP(DEF_LEVEL,
2898135446Strhodes		   "dns_adb_createfind: name %p is an alias (cached)",
2899135446Strhodes		   adbname);
2900135446Strhodes		alias = ISC_TRUE;
2901135446Strhodes		goto post_copy;
2902135446Strhodes	}
2903135446Strhodes
2904135446Strhodes	/*
2905135446Strhodes	 * Try to populate the name from the database and/or
2906135446Strhodes	 * start fetches.  First try looking for an A record
2907135446Strhodes	 * in the database.
2908135446Strhodes	 */
2909135446Strhodes	if (!NAME_HAS_V4(adbname) && EXPIRE_OK(adbname->expire_v4, now)
2910135446Strhodes	    && WANT_INET(wanted_addresses)) {
2911135446Strhodes		result = dbfind_name(adbname, now, dns_rdatatype_a);
2912135446Strhodes		if (result == ISC_R_SUCCESS) {
2913135446Strhodes			DP(DEF_LEVEL,
2914135446Strhodes			   "dns_adb_createfind: found A for name %p in db",
2915135446Strhodes			   adbname);
2916135446Strhodes			goto v6;
2917135446Strhodes		}
2918135446Strhodes
2919135446Strhodes		/*
2920135446Strhodes		 * Did we get a CNAME or DNAME?
2921135446Strhodes		 */
2922135446Strhodes		if (result == DNS_R_ALIAS) {
2923135446Strhodes			DP(DEF_LEVEL,
2924135446Strhodes			   "dns_adb_createfind: name %p is an alias",
2925135446Strhodes			   adbname);
2926135446Strhodes			alias = ISC_TRUE;
2927135446Strhodes			goto post_copy;
2928135446Strhodes		}
2929135446Strhodes
2930135446Strhodes		/*
2931135446Strhodes		 * If the name doesn't exist at all, don't bother with
2932135446Strhodes		 * v6 queries; they won't work.
2933135446Strhodes		 *
2934135446Strhodes		 * If the name does exist but we didn't get our data, go
2935135446Strhodes		 * ahead and try AAAA.
2936135446Strhodes		 *
2937135446Strhodes		 * If the result is neither of these, try a fetch for A.
2938135446Strhodes		 */
2939135446Strhodes		if (NXDOMAIN_RESULT(result))
2940135446Strhodes			goto fetch;
2941135446Strhodes		else if (NXRRSET_RESULT(result))
2942135446Strhodes			goto v6;
2943135446Strhodes
2944135446Strhodes		if (!NAME_FETCH_V4(adbname))
2945135446Strhodes			wanted_fetches |= DNS_ADBFIND_INET;
2946135446Strhodes	}
2947135446Strhodes
2948135446Strhodes v6:
2949135446Strhodes	if (!NAME_HAS_V6(adbname) && EXPIRE_OK(adbname->expire_v6, now)
2950135446Strhodes	    && WANT_INET6(wanted_addresses)) {
2951135446Strhodes		result = dbfind_name(adbname, now, dns_rdatatype_aaaa);
2952135446Strhodes		if (result == ISC_R_SUCCESS) {
2953135446Strhodes			DP(DEF_LEVEL,
2954135446Strhodes			   "dns_adb_createfind: found AAAA for name %p",
2955135446Strhodes			   adbname);
2956135446Strhodes			goto fetch;
2957135446Strhodes		}
2958135446Strhodes
2959135446Strhodes		/*
2960135446Strhodes		 * Did we get a CNAME or DNAME?
2961135446Strhodes		 */
2962135446Strhodes		if (result == DNS_R_ALIAS) {
2963135446Strhodes			DP(DEF_LEVEL,
2964135446Strhodes			   "dns_adb_createfind: name %p is an alias",
2965135446Strhodes			   adbname);
2966135446Strhodes			alias = ISC_TRUE;
2967135446Strhodes			goto post_copy;
2968135446Strhodes		}
2969135446Strhodes
2970135446Strhodes		/*
2971135446Strhodes		 * Listen to negative cache hints, and don't start
2972135446Strhodes		 * another query.
2973135446Strhodes		 */
2974135446Strhodes		if (NCACHE_RESULT(result) || AUTH_NX(result))
2975135446Strhodes			goto fetch;
2976135446Strhodes
2977135446Strhodes		if (!NAME_FETCH_V6(adbname))
2978135446Strhodes			wanted_fetches |= DNS_ADBFIND_INET6;
2979135446Strhodes	}
2980135446Strhodes
2981135446Strhodes fetch:
2982135446Strhodes	if ((WANT_INET(wanted_addresses) && NAME_HAS_V4(adbname)) ||
2983135446Strhodes	    (WANT_INET6(wanted_addresses) && NAME_HAS_V6(adbname)))
2984135446Strhodes		have_address = ISC_TRUE;
2985135446Strhodes	else
2986135446Strhodes		have_address = ISC_FALSE;
2987135446Strhodes	if (wanted_fetches != 0 &&
2988135446Strhodes	    ! (FIND_AVOIDFETCHES(find) && have_address)) {
2989135446Strhodes		/*
2990135446Strhodes		 * We're missing at least one address family.  Either the
2991135446Strhodes		 * caller hasn't instructed us to avoid fetches, or we don't
2992135446Strhodes		 * know anything about any of the address families that would
2993135446Strhodes		 * be acceptable so we have to launch fetches.
2994135446Strhodes		 */
2995135446Strhodes
2996135446Strhodes		if (FIND_STARTATZONE(find))
2997135446Strhodes			start_at_zone = ISC_TRUE;
2998135446Strhodes
2999135446Strhodes		/*
3000135446Strhodes		 * Start V4.
3001135446Strhodes		 */
3002135446Strhodes		if (WANT_INET(wanted_fetches) &&
3003135446Strhodes		    fetch_name(adbname, start_at_zone,
3004135446Strhodes			       dns_rdatatype_a) == ISC_R_SUCCESS) {
3005135446Strhodes			DP(DEF_LEVEL,
3006135446Strhodes			   "dns_adb_createfind: started A fetch for name %p",
3007135446Strhodes			   adbname);
3008135446Strhodes		}
3009135446Strhodes
3010135446Strhodes		/*
3011135446Strhodes		 * Start V6.
3012135446Strhodes		 */
3013135446Strhodes		if (WANT_INET6(wanted_fetches) &&
3014135446Strhodes		    fetch_name(adbname, start_at_zone,
3015135446Strhodes			       dns_rdatatype_aaaa) == ISC_R_SUCCESS) {
3016135446Strhodes			DP(DEF_LEVEL,
3017135446Strhodes			   "dns_adb_createfind: "
3018135446Strhodes			   "started AAAA fetch for name %p",
3019135446Strhodes			   adbname);
3020135446Strhodes		}
3021135446Strhodes	}
3022135446Strhodes
3023135446Strhodes	/*
3024135446Strhodes	 * Run through the name and copy out the bits we are
3025135446Strhodes	 * interested in.
3026135446Strhodes	 */
3027170222Sdougb	copy_namehook_lists(adb, find, qname, qtype, adbname, now);
3028135446Strhodes
3029135446Strhodes post_copy:
3030135446Strhodes	if (NAME_FETCH_V4(adbname))
3031135446Strhodes		query_pending |= DNS_ADBFIND_INET;
3032135446Strhodes	if (NAME_FETCH_V6(adbname))
3033135446Strhodes		query_pending |= DNS_ADBFIND_INET6;
3034135446Strhodes
3035135446Strhodes	/*
3036135446Strhodes	 * Attach to the name's query list if there are queries
3037135446Strhodes	 * already running, and we have been asked to.
3038135446Strhodes	 */
3039135446Strhodes	want_event = ISC_TRUE;
3040135446Strhodes	if (!FIND_WANTEVENT(find))
3041135446Strhodes		want_event = ISC_FALSE;
3042135446Strhodes	if (FIND_WANTEMPTYEVENT(find) && FIND_HAS_ADDRS(find))
3043135446Strhodes		want_event = ISC_FALSE;
3044135446Strhodes	if ((wanted_addresses & query_pending) == 0)
3045135446Strhodes		want_event = ISC_FALSE;
3046135446Strhodes	if (alias)
3047135446Strhodes		want_event = ISC_FALSE;
3048135446Strhodes	if (want_event) {
3049135446Strhodes		find->adbname = adbname;
3050135446Strhodes		find->name_bucket = bucket;
3051135446Strhodes		ISC_LIST_APPEND(adbname->finds, find, plink);
3052135446Strhodes		find->query_pending = (query_pending & wanted_addresses);
3053135446Strhodes		find->flags &= ~DNS_ADBFIND_ADDRESSMASK;
3054135446Strhodes		find->flags |= (find->query_pending & DNS_ADBFIND_ADDRESSMASK);
3055135446Strhodes		DP(DEF_LEVEL, "createfind: attaching find %p to adbname %p",
3056135446Strhodes		   find, adbname);
3057135446Strhodes	} else {
3058135446Strhodes		/*
3059135446Strhodes		 * Remove the flag so the caller knows there will never
3060135446Strhodes		 * be an event, and set internal flags to fake that
3061135446Strhodes		 * the event was sent and freed, so dns_adb_destroyfind() will
3062135446Strhodes		 * do the right thing.
3063135446Strhodes		 */
3064135446Strhodes		find->query_pending = (query_pending & wanted_addresses);
3065135446Strhodes		find->options &= ~DNS_ADBFIND_WANTEVENT;
3066135446Strhodes		find->flags |= (FIND_EVENT_SENT | FIND_EVENT_FREED);
3067135446Strhodes		find->flags &= ~DNS_ADBFIND_ADDRESSMASK;
3068135446Strhodes	}
3069135446Strhodes
3070135446Strhodes	find->partial_result |= (adbname->partial_result & wanted_addresses);
3071135446Strhodes	if (alias) {
3072135446Strhodes		if (target != NULL) {
3073135446Strhodes			result = dns_name_copy(&adbname->target, target, NULL);
3074135446Strhodes			if (result != ISC_R_SUCCESS)
3075135446Strhodes				goto out;
3076135446Strhodes		}
3077135446Strhodes		result = DNS_R_ALIAS;
3078135446Strhodes	} else
3079135446Strhodes		result = ISC_R_SUCCESS;
3080135446Strhodes
3081135446Strhodes	/*
3082135446Strhodes	 * Copy out error flags from the name structure into the find.
3083135446Strhodes	 */
3084135446Strhodes	find->result_v4 = find_err_map[adbname->fetch_err];
3085135446Strhodes	find->result_v6 = find_err_map[adbname->fetch6_err];
3086135446Strhodes
3087135446Strhodes out:
3088135446Strhodes	if (find != NULL) {
3089135446Strhodes		*findp = find;
3090135446Strhodes
3091135446Strhodes		if (want_event) {
3092135446Strhodes			isc_task_t *taskp;
3093135446Strhodes
3094135446Strhodes			INSIST((find->flags & DNS_ADBFIND_ADDRESSMASK) != 0);
3095135446Strhodes			taskp = NULL;
3096135446Strhodes			isc_task_attach(task, &taskp);
3097135446Strhodes			find->event.ev_sender = taskp;
3098135446Strhodes			find->event.ev_action = action;
3099135446Strhodes			find->event.ev_arg = arg;
3100135446Strhodes		}
3101135446Strhodes	}
3102135446Strhodes
3103165071Sdougb	UNLOCK(&adb->namelocks[bucket]);
3104135446Strhodes
3105135446Strhodes	return (result);
3106135446Strhodes}
3107135446Strhodes
3108135446Strhodesvoid
3109135446Strhodesdns_adb_destroyfind(dns_adbfind_t **findp) {
3110135446Strhodes	dns_adbfind_t *find;
3111135446Strhodes	dns_adbentry_t *entry;
3112135446Strhodes	dns_adbaddrinfo_t *ai;
3113135446Strhodes	int bucket;
3114135446Strhodes	dns_adb_t *adb;
3115214586Sdougb	isc_boolean_t overmem;
3116135446Strhodes
3117135446Strhodes	REQUIRE(findp != NULL && DNS_ADBFIND_VALID(*findp));
3118135446Strhodes	find = *findp;
3119135446Strhodes	*findp = NULL;
3120135446Strhodes
3121135446Strhodes	LOCK(&find->lock);
3122135446Strhodes
3123135446Strhodes	DP(DEF_LEVEL, "dns_adb_destroyfind on find %p", find);
3124135446Strhodes
3125135446Strhodes	adb = find->adb;
3126135446Strhodes	REQUIRE(DNS_ADB_VALID(adb));
3127135446Strhodes
3128135446Strhodes	REQUIRE(FIND_EVENTFREED(find));
3129135446Strhodes
3130135446Strhodes	bucket = find->name_bucket;
3131135446Strhodes	INSIST(bucket == DNS_ADB_INVALIDBUCKET);
3132135446Strhodes
3133135446Strhodes	UNLOCK(&find->lock);
3134135446Strhodes
3135135446Strhodes	/*
3136135446Strhodes	 * The find doesn't exist on any list, and nothing is locked.
3137135446Strhodes	 * Return the find to the memory pool, and decrement the adb's
3138135446Strhodes	 * reference count.
3139135446Strhodes	 */
3140214586Sdougb	overmem = isc_mem_isovermem(adb->mctx);
3141135446Strhodes	ai = ISC_LIST_HEAD(find->list);
3142135446Strhodes	while (ai != NULL) {
3143135446Strhodes		ISC_LIST_UNLINK(find->list, ai, publink);
3144135446Strhodes		entry = ai->entry;
3145135446Strhodes		ai->entry = NULL;
3146135446Strhodes		INSIST(DNS_ADBENTRY_VALID(entry));
3147214586Sdougb		RUNTIME_CHECK(dec_entry_refcnt(adb, overmem, entry, ISC_TRUE) ==
3148135446Strhodes			      ISC_FALSE);
3149135446Strhodes		free_adbaddrinfo(adb, &ai);
3150135446Strhodes		ai = ISC_LIST_HEAD(find->list);
3151135446Strhodes	}
3152135446Strhodes
3153135446Strhodes	/*
3154135446Strhodes	 * WARNING:  The find is freed with the adb locked.  This is done
3155135446Strhodes	 * to avoid a race condition where we free the find, some other
3156135446Strhodes	 * thread tests to see if it should be destroyed, detects it should
3157135446Strhodes	 * be, destroys it, and then we try to lock it for our check, but the
3158135446Strhodes	 * lock is destroyed.
3159135446Strhodes	 */
3160135446Strhodes	LOCK(&adb->lock);
3161135446Strhodes	if (free_adbfind(adb, &find))
3162135446Strhodes		check_exit(adb);
3163135446Strhodes	UNLOCK(&adb->lock);
3164135446Strhodes}
3165135446Strhodes
3166135446Strhodesvoid
3167135446Strhodesdns_adb_cancelfind(dns_adbfind_t *find) {
3168135446Strhodes	isc_event_t *ev;
3169135446Strhodes	isc_task_t *task;
3170135446Strhodes	dns_adb_t *adb;
3171135446Strhodes	int bucket;
3172135446Strhodes	int unlock_bucket;
3173135446Strhodes
3174135446Strhodes	LOCK(&find->lock);
3175135446Strhodes
3176135446Strhodes	DP(DEF_LEVEL, "dns_adb_cancelfind on find %p", find);
3177135446Strhodes
3178135446Strhodes	adb = find->adb;
3179135446Strhodes	REQUIRE(DNS_ADB_VALID(adb));
3180135446Strhodes
3181135446Strhodes	REQUIRE(!FIND_EVENTFREED(find));
3182135446Strhodes	REQUIRE(FIND_WANTEVENT(find));
3183135446Strhodes
3184135446Strhodes	bucket = find->name_bucket;
3185135446Strhodes	if (bucket == DNS_ADB_INVALIDBUCKET)
3186135446Strhodes		goto cleanup;
3187135446Strhodes
3188135446Strhodes	/*
3189135446Strhodes	 * We need to get the adbname's lock to unlink the find.
3190135446Strhodes	 */
3191135446Strhodes	unlock_bucket = bucket;
3192135446Strhodes	violate_locking_hierarchy(&find->lock, &adb->namelocks[unlock_bucket]);
3193135446Strhodes	bucket = find->name_bucket;
3194135446Strhodes	if (bucket != DNS_ADB_INVALIDBUCKET) {
3195135446Strhodes		ISC_LIST_UNLINK(find->adbname->finds, find, plink);
3196135446Strhodes		find->adbname = NULL;
3197135446Strhodes		find->name_bucket = DNS_ADB_INVALIDBUCKET;
3198135446Strhodes	}
3199135446Strhodes	UNLOCK(&adb->namelocks[unlock_bucket]);
3200135446Strhodes	bucket = DNS_ADB_INVALIDBUCKET;
3201225361Sdougb	POST(bucket);
3202135446Strhodes
3203135446Strhodes cleanup:
3204135446Strhodes
3205135446Strhodes	if (!FIND_EVENTSENT(find)) {
3206135446Strhodes		ev = &find->event;
3207135446Strhodes		task = ev->ev_sender;
3208135446Strhodes		ev->ev_sender = find;
3209135446Strhodes		ev->ev_type = DNS_EVENT_ADBCANCELED;
3210135446Strhodes		ev->ev_destroy = event_free;
3211135446Strhodes		ev->ev_destroy_arg = find;
3212135446Strhodes		find->result_v4 = ISC_R_CANCELED;
3213135446Strhodes		find->result_v6 = ISC_R_CANCELED;
3214135446Strhodes
3215135446Strhodes		DP(DEF_LEVEL, "sending event %p to task %p for find %p",
3216135446Strhodes		   ev, task, find);
3217135446Strhodes
3218135446Strhodes		isc_task_sendanddetach(&task, (isc_event_t **)&ev);
3219135446Strhodes	}
3220135446Strhodes
3221135446Strhodes	UNLOCK(&find->lock);
3222135446Strhodes}
3223135446Strhodes
3224135446Strhodesvoid
3225135446Strhodesdns_adb_dump(dns_adb_t *adb, FILE *f) {
3226224092Sdougb	unsigned int i;
3227143731Sdougb	isc_stdtime_t now;
3228143731Sdougb
3229135446Strhodes	REQUIRE(DNS_ADB_VALID(adb));
3230135446Strhodes	REQUIRE(f != NULL);
3231135446Strhodes
3232135446Strhodes	/*
3233135446Strhodes	 * Lock the adb itself, lock all the name buckets, then lock all
3234135446Strhodes	 * the entry buckets.  This should put the adb into a state where
3235135446Strhodes	 * nothing can change, so we can iterate through everything and
3236135446Strhodes	 * print at our leisure.
3237135446Strhodes	 */
3238135446Strhodes
3239135446Strhodes	LOCK(&adb->lock);
3240143731Sdougb	isc_stdtime_get(&now);
3241143731Sdougb
3242224092Sdougb	for (i = 0; i < adb->nnames; i++)
3243143731Sdougb		RUNTIME_CHECK(cleanup_names(adb, i, now) == ISC_FALSE);
3244224092Sdougb	for (i = 0; i < adb->nentries; i++)
3245143731Sdougb		RUNTIME_CHECK(cleanup_entries(adb, i, now) == ISC_FALSE);
3246143731Sdougb
3247143731Sdougb	dump_adb(adb, f, ISC_FALSE, now);
3248135446Strhodes	UNLOCK(&adb->lock);
3249135446Strhodes}
3250135446Strhodes
3251135446Strhodesstatic void
3252135446Strhodesdump_ttl(FILE *f, const char *legend, isc_stdtime_t value, isc_stdtime_t now) {
3253135446Strhodes	if (value == INT_MAX)
3254135446Strhodes		return;
3255135446Strhodes	fprintf(f, " [%s TTL %d]", legend, value - now);
3256135446Strhodes}
3257135446Strhodes
3258135446Strhodesstatic void
3259143731Sdougbdump_adb(dns_adb_t *adb, FILE *f, isc_boolean_t debug, isc_stdtime_t now) {
3260224092Sdougb	unsigned int i;
3261135446Strhodes	dns_adbname_t *name;
3262143731Sdougb	dns_adbentry_t *entry;
3263135446Strhodes
3264135446Strhodes	fprintf(f, ";\n; Address database dump\n;\n");
3265135446Strhodes	if (debug)
3266135446Strhodes		fprintf(f, "; addr %p, erefcnt %u, irefcnt %u, finds out %u\n",
3267135446Strhodes			adb, adb->erefcnt, adb->irefcnt,
3268135446Strhodes			isc_mempool_getallocated(adb->nhmp));
3269135446Strhodes
3270224092Sdougb	for (i = 0; i < adb->nnames; i++)
3271135446Strhodes		LOCK(&adb->namelocks[i]);
3272224092Sdougb	for (i = 0; i < adb->nentries; i++)
3273135446Strhodes		LOCK(&adb->entrylocks[i]);
3274135446Strhodes
3275135446Strhodes	/*
3276135446Strhodes	 * Dump the names
3277135446Strhodes	 */
3278224092Sdougb	for (i = 0; i < adb->nnames; i++) {
3279135446Strhodes		name = ISC_LIST_HEAD(adb->names[i]);
3280135446Strhodes		if (name == NULL)
3281135446Strhodes			continue;
3282135446Strhodes		if (debug)
3283135446Strhodes			fprintf(f, "; bucket %d\n", i);
3284135446Strhodes		for (;
3285135446Strhodes		     name != NULL;
3286135446Strhodes		     name = ISC_LIST_NEXT(name, plink))
3287135446Strhodes		{
3288135446Strhodes			if (debug)
3289135446Strhodes				fprintf(f, "; name %p (flags %08x)\n",
3290135446Strhodes					name, name->flags);
3291135446Strhodes
3292135446Strhodes			fprintf(f, "; ");
3293135446Strhodes			print_dns_name(f, &name->name);
3294135446Strhodes			if (dns_name_countlabels(&name->target) > 0) {
3295135446Strhodes				fprintf(f, " alias ");
3296135446Strhodes				print_dns_name(f, &name->target);
3297135446Strhodes			}
3298135446Strhodes
3299135446Strhodes			dump_ttl(f, "v4", name->expire_v4, now);
3300135446Strhodes			dump_ttl(f, "v6", name->expire_v6, now);
3301135446Strhodes			dump_ttl(f, "target", name->expire_target, now);
3302135446Strhodes
3303135446Strhodes			fprintf(f, " [v4 %s] [v6 %s]",
3304135446Strhodes				errnames[name->fetch_err],
3305135446Strhodes				errnames[name->fetch6_err]);
3306135446Strhodes
3307135446Strhodes			fprintf(f, "\n");
3308135446Strhodes
3309135446Strhodes			print_namehook_list(f, "v4", &name->v4, debug, now);
3310135446Strhodes			print_namehook_list(f, "v6", &name->v6, debug, now);
3311135446Strhodes
3312135446Strhodes			if (debug)
3313135446Strhodes				print_fetch_list(f, name);
3314135446Strhodes			if (debug)
3315135446Strhodes				print_find_list(f, name);
3316135446Strhodes
3317135446Strhodes		}
3318135446Strhodes	}
3319135446Strhodes
3320143731Sdougb	fprintf(f, ";\n; Unassociated entries\n;\n");
3321143731Sdougb
3322224092Sdougb	for (i = 0; i < adb->nentries; i++) {
3323143731Sdougb		entry = ISC_LIST_HEAD(adb->entries[i]);
3324143731Sdougb		while (entry != NULL) {
3325143731Sdougb			if (entry->refcnt == 0)
3326143731Sdougb				dump_entry(f, entry, debug, now);
3327143731Sdougb			entry = ISC_LIST_NEXT(entry, plink);
3328143731Sdougb		}
3329143731Sdougb	}
3330143731Sdougb
3331135446Strhodes	/*
3332135446Strhodes	 * Unlock everything
3333135446Strhodes	 */
3334224092Sdougb	for (i = 0; i < adb->nentries; i++)
3335135446Strhodes		UNLOCK(&adb->entrylocks[i]);
3336224092Sdougb	for (i = 0; i < adb->nnames; i++)
3337135446Strhodes		UNLOCK(&adb->namelocks[i]);
3338135446Strhodes}
3339135446Strhodes
3340135446Strhodesstatic void
3341135446Strhodesdump_entry(FILE *f, dns_adbentry_t *entry, isc_boolean_t debug,
3342135446Strhodes	   isc_stdtime_t now)
3343135446Strhodes{
3344135446Strhodes	char addrbuf[ISC_NETADDR_FORMATSIZE];
3345170222Sdougb	char typebuf[DNS_RDATATYPE_FORMATSIZE];
3346135446Strhodes	isc_netaddr_t netaddr;
3347170222Sdougb	dns_adblameinfo_t *li;
3348135446Strhodes
3349135446Strhodes	isc_netaddr_fromsockaddr(&netaddr, &entry->sockaddr);
3350135446Strhodes	isc_netaddr_format(&netaddr, addrbuf, sizeof(addrbuf));
3351135446Strhodes
3352135446Strhodes	if (debug)
3353135446Strhodes		fprintf(f, ";\t%p: refcnt %u\n", entry, entry->refcnt);
3354135446Strhodes
3355135446Strhodes	fprintf(f, ";\t%s [srtt %u] [flags %08x]",
3356135446Strhodes		addrbuf, entry->srtt, entry->flags);
3357143731Sdougb	if (entry->expires != 0)
3358143731Sdougb		fprintf(f, " [ttl %d]", entry->expires - now);
3359135446Strhodes	fprintf(f, "\n");
3360170222Sdougb	for (li = ISC_LIST_HEAD(entry->lameinfo);
3361170222Sdougb	     li != NULL;
3362170222Sdougb	     li = ISC_LIST_NEXT(li, plink)) {
3363135446Strhodes		fprintf(f, ";\t\t");
3364170222Sdougb		print_dns_name(f, &li->qname);
3365170222Sdougb		dns_rdatatype_format(li->qtype, typebuf, sizeof(typebuf));
3366170222Sdougb		fprintf(f, " %s [lame TTL %d]\n", typebuf,
3367170222Sdougb			li->lame_timer - now);
3368135446Strhodes	}
3369135446Strhodes}
3370135446Strhodes
3371135446Strhodesvoid
3372135446Strhodesdns_adb_dumpfind(dns_adbfind_t *find, FILE *f) {
3373135446Strhodes	char tmp[512];
3374135446Strhodes	const char *tmpp;
3375135446Strhodes	dns_adbaddrinfo_t *ai;
3376135446Strhodes	isc_sockaddr_t *sa;
3377135446Strhodes
3378135446Strhodes	/*
3379135446Strhodes	 * Not used currently, in the API Just In Case we
3380135446Strhodes	 * want to dump out the name and/or entries too.
3381135446Strhodes	 */
3382135446Strhodes
3383135446Strhodes	LOCK(&find->lock);
3384135446Strhodes
3385135446Strhodes	fprintf(f, ";Find %p\n", find);
3386135446Strhodes	fprintf(f, ";\tqpending %08x partial %08x options %08x flags %08x\n",
3387135446Strhodes		find->query_pending, find->partial_result,
3388135446Strhodes		find->options, find->flags);
3389135446Strhodes	fprintf(f, ";\tname_bucket %d, name %p, event sender %p\n",
3390135446Strhodes		find->name_bucket, find->adbname, find->event.ev_sender);
3391135446Strhodes
3392135446Strhodes	ai = ISC_LIST_HEAD(find->list);
3393135446Strhodes	if (ai != NULL)
3394135446Strhodes		fprintf(f, "\tAddresses:\n");
3395135446Strhodes	while (ai != NULL) {
3396135446Strhodes		sa = &ai->sockaddr;
3397135446Strhodes		switch (sa->type.sa.sa_family) {
3398135446Strhodes		case AF_INET:
3399135446Strhodes			tmpp = inet_ntop(AF_INET, &sa->type.sin.sin_addr,
3400135446Strhodes					 tmp, sizeof(tmp));
3401135446Strhodes			break;
3402135446Strhodes		case AF_INET6:
3403135446Strhodes			tmpp = inet_ntop(AF_INET6, &sa->type.sin6.sin6_addr,
3404135446Strhodes					 tmp, sizeof(tmp));
3405135446Strhodes			break;
3406135446Strhodes		default:
3407135446Strhodes			tmpp = "UnkFamily";
3408135446Strhodes		}
3409135446Strhodes
3410135446Strhodes		if (tmpp == NULL)
3411135446Strhodes			tmpp = "BadAddress";
3412135446Strhodes
3413135446Strhodes		fprintf(f, "\t\tentry %p, flags %08x"
3414135446Strhodes			" srtt %u addr %s\n",
3415135446Strhodes			ai->entry, ai->flags, ai->srtt, tmpp);
3416135446Strhodes
3417135446Strhodes		ai = ISC_LIST_NEXT(ai, publink);
3418135446Strhodes	}
3419135446Strhodes
3420135446Strhodes	UNLOCK(&find->lock);
3421135446Strhodes}
3422135446Strhodes
3423135446Strhodesstatic void
3424135446Strhodesprint_dns_name(FILE *f, dns_name_t *name) {
3425135446Strhodes	char buf[DNS_NAME_FORMATSIZE];
3426135446Strhodes
3427135446Strhodes	INSIST(f != NULL);
3428135446Strhodes
3429135446Strhodes	dns_name_format(name, buf, sizeof(buf));
3430135446Strhodes	fprintf(f, "%s", buf);
3431135446Strhodes}
3432135446Strhodes
3433135446Strhodesstatic void
3434135446Strhodesprint_namehook_list(FILE *f, const char *legend, dns_adbnamehooklist_t *list,
3435135446Strhodes		    isc_boolean_t debug, isc_stdtime_t now)
3436135446Strhodes{
3437135446Strhodes	dns_adbnamehook_t *nh;
3438135446Strhodes
3439135446Strhodes	for (nh = ISC_LIST_HEAD(*list);
3440135446Strhodes	     nh != NULL;
3441135446Strhodes	     nh = ISC_LIST_NEXT(nh, plink))
3442135446Strhodes	{
3443135446Strhodes		if (debug)
3444135446Strhodes			fprintf(f, ";\tHook(%s) %p\n", legend, nh);
3445135446Strhodes		dump_entry(f, nh->entry, debug, now);
3446135446Strhodes	}
3447135446Strhodes}
3448135446Strhodes
3449135446Strhodesstatic inline void
3450135446Strhodesprint_fetch(FILE *f, dns_adbfetch_t *ft, const char *type) {
3451193149Sdougb	fprintf(f, "\t\tFetch(%s): %p -> { fetch %p }\n",
3452193149Sdougb		type, ft, ft->fetch);
3453135446Strhodes}
3454135446Strhodes
3455135446Strhodesstatic void
3456135446Strhodesprint_fetch_list(FILE *f, dns_adbname_t *n) {
3457135446Strhodes	if (NAME_FETCH_A(n))
3458135446Strhodes		print_fetch(f, n->fetch_a, "A");
3459135446Strhodes	if (NAME_FETCH_AAAA(n))
3460135446Strhodes		print_fetch(f, n->fetch_aaaa, "AAAA");
3461135446Strhodes}
3462135446Strhodes
3463135446Strhodesstatic void
3464135446Strhodesprint_find_list(FILE *f, dns_adbname_t *name) {
3465135446Strhodes	dns_adbfind_t *find;
3466135446Strhodes
3467135446Strhodes	find = ISC_LIST_HEAD(name->finds);
3468135446Strhodes	while (find != NULL) {
3469135446Strhodes		dns_adb_dumpfind(find, f);
3470135446Strhodes		find = ISC_LIST_NEXT(find, plink);
3471135446Strhodes	}
3472135446Strhodes}
3473135446Strhodes
3474135446Strhodesstatic isc_result_t
3475135446Strhodesdbfind_name(dns_adbname_t *adbname, isc_stdtime_t now, dns_rdatatype_t rdtype)
3476135446Strhodes{
3477135446Strhodes	isc_result_t result;
3478135446Strhodes	dns_rdataset_t rdataset;
3479135446Strhodes	dns_adb_t *adb;
3480135446Strhodes	dns_fixedname_t foundname;
3481135446Strhodes	dns_name_t *fname;
3482135446Strhodes
3483135446Strhodes	INSIST(DNS_ADBNAME_VALID(adbname));
3484135446Strhodes	adb = adbname->adb;
3485135446Strhodes	INSIST(DNS_ADB_VALID(adb));
3486135446Strhodes	INSIST(rdtype == dns_rdatatype_a || rdtype == dns_rdatatype_aaaa);
3487135446Strhodes
3488135446Strhodes	dns_fixedname_init(&foundname);
3489193149Sdougb	fname = dns_fixedname_name(&foundname);
3490135446Strhodes	dns_rdataset_init(&rdataset);
3491135446Strhodes
3492135446Strhodes	if (rdtype == dns_rdatatype_a)
3493135446Strhodes		adbname->fetch_err = FIND_ERR_UNEXPECTED;
3494135446Strhodes	else
3495135446Strhodes		adbname->fetch6_err = FIND_ERR_UNEXPECTED;
3496135446Strhodes
3497224092Sdougb	/*
3498224092Sdougb	 * We need to specify whether to search static-stub zones (if
3499224092Sdougb	 * configured) depending on whether this is a "start at zone" lookup,
3500224092Sdougb	 * i.e., whether it's a "bailiwick" glue.  If it's bailiwick (in which
3501224092Sdougb	 * case NAME_STARTATZONE is set) we need to stop the search at any
3502224092Sdougb	 * matching static-stub zone without looking into the cache to honor
3503224092Sdougb	 * the configuration on which server we should send queries to.
3504224092Sdougb	 */
3505224092Sdougb	result = dns_view_find2(adb->view, &adbname->name, rdtype, now,
3506224092Sdougb				NAME_GLUEOK(adbname) ? DNS_DBFIND_GLUEOK : 0,
3507224092Sdougb				ISC_TF(NAME_HINTOK(adbname)),
3508224092Sdougb				(adbname->flags & NAME_STARTATZONE) != 0 ?
3509224092Sdougb				ISC_TRUE : ISC_FALSE,
3510224092Sdougb				NULL, NULL, fname, &rdataset, NULL);
3511135446Strhodes
3512135446Strhodes	/* XXXVIX this switch statement is too sparse to gen a jump table. */
3513135446Strhodes	switch (result) {
3514135446Strhodes	case DNS_R_GLUE:
3515135446Strhodes	case DNS_R_HINT:
3516135446Strhodes	case ISC_R_SUCCESS:
3517135446Strhodes		/*
3518135446Strhodes		 * Found in the database.  Even if we can't copy out
3519135446Strhodes		 * any information, return success, or else a fetch
3520135446Strhodes		 * will be made, which will only make things worse.
3521135446Strhodes		 */
3522135446Strhodes		if (rdtype == dns_rdatatype_a)
3523135446Strhodes			adbname->fetch_err = FIND_ERR_SUCCESS;
3524135446Strhodes		else
3525135446Strhodes			adbname->fetch6_err = FIND_ERR_SUCCESS;
3526135446Strhodes		result = import_rdataset(adbname, &rdataset, now);
3527135446Strhodes		break;
3528135446Strhodes	case DNS_R_NXDOMAIN:
3529135446Strhodes	case DNS_R_NXRRSET:
3530135446Strhodes		/*
3531135446Strhodes		 * We're authoritative and the data doesn't exist.
3532135446Strhodes		 * Make up a negative cache entry so we don't ask again
3533135446Strhodes		 * for a while.
3534135446Strhodes		 *
3535135446Strhodes		 * XXXRTH  What time should we use?  I'm putting in 30 seconds
3536135446Strhodes		 * for now.
3537135446Strhodes		 */
3538135446Strhodes		if (rdtype == dns_rdatatype_a) {
3539135446Strhodes			adbname->expire_v4 = now + 30;
3540135446Strhodes			DP(NCACHE_LEVEL,
3541135446Strhodes			   "adb name %p: Caching auth negative entry for A",
3542135446Strhodes			   adbname);
3543135446Strhodes			if (result == DNS_R_NXDOMAIN)
3544135446Strhodes				adbname->fetch_err = FIND_ERR_NXDOMAIN;
3545135446Strhodes			else
3546135446Strhodes				adbname->fetch_err = FIND_ERR_NXRRSET;
3547135446Strhodes		} else {
3548135446Strhodes			DP(NCACHE_LEVEL,
3549135446Strhodes			   "adb name %p: Caching auth negative entry for AAAA",
3550135446Strhodes			   adbname);
3551135446Strhodes			adbname->expire_v6 = now + 30;
3552135446Strhodes			if (result == DNS_R_NXDOMAIN)
3553135446Strhodes				adbname->fetch6_err = FIND_ERR_NXDOMAIN;
3554135446Strhodes			else
3555135446Strhodes				adbname->fetch6_err = FIND_ERR_NXRRSET;
3556135446Strhodes		}
3557135446Strhodes		break;
3558135446Strhodes	case DNS_R_NCACHENXDOMAIN:
3559135446Strhodes	case DNS_R_NCACHENXRRSET:
3560135446Strhodes		/*
3561135446Strhodes		 * We found a negative cache entry.  Pull the TTL from it
3562135446Strhodes		 * so we won't ask again for a while.
3563135446Strhodes		 */
3564135446Strhodes		rdataset.ttl = ttlclamp(rdataset.ttl);
3565135446Strhodes		if (rdtype == dns_rdatatype_a) {
3566135446Strhodes			adbname->expire_v4 = rdataset.ttl + now;
3567135446Strhodes			if (result == DNS_R_NCACHENXDOMAIN)
3568135446Strhodes				adbname->fetch_err = FIND_ERR_NXDOMAIN;
3569135446Strhodes			else
3570135446Strhodes				adbname->fetch_err = FIND_ERR_NXRRSET;
3571135446Strhodes			DP(NCACHE_LEVEL,
3572135446Strhodes			  "adb name %p: Caching negative entry for A (ttl %u)",
3573135446Strhodes			   adbname, rdataset.ttl);
3574135446Strhodes		} else {
3575135446Strhodes			DP(NCACHE_LEVEL,
3576135446Strhodes		       "adb name %p: Caching negative entry for AAAA (ttl %u)",
3577135446Strhodes			   adbname, rdataset.ttl);
3578135446Strhodes			adbname->expire_v6 = rdataset.ttl + now;
3579135446Strhodes			if (result == DNS_R_NCACHENXDOMAIN)
3580135446Strhodes				adbname->fetch6_err = FIND_ERR_NXDOMAIN;
3581135446Strhodes			else
3582135446Strhodes				adbname->fetch6_err = FIND_ERR_NXRRSET;
3583135446Strhodes		}
3584135446Strhodes		break;
3585135446Strhodes	case DNS_R_CNAME:
3586135446Strhodes	case DNS_R_DNAME:
3587135446Strhodes		/*
3588135446Strhodes		 * Clear the hint and glue flags, so this will match
3589135446Strhodes		 * more often.
3590135446Strhodes		 */
3591135446Strhodes		adbname->flags &= ~(DNS_ADBFIND_GLUEOK | DNS_ADBFIND_HINTOK);
3592135446Strhodes
3593135446Strhodes		rdataset.ttl = ttlclamp(rdataset.ttl);
3594135446Strhodes		clean_target(adb, &adbname->target);
3595135446Strhodes		adbname->expire_target = INT_MAX;
3596135446Strhodes		result = set_target(adb, &adbname->name, fname, &rdataset,
3597135446Strhodes				    &adbname->target);
3598135446Strhodes		if (result == ISC_R_SUCCESS) {
3599135446Strhodes			result = DNS_R_ALIAS;
3600135446Strhodes			DP(NCACHE_LEVEL,
3601135446Strhodes			   "adb name %p: caching alias target",
3602135446Strhodes			   adbname);
3603135446Strhodes			adbname->expire_target = rdataset.ttl + now;
3604135446Strhodes		}
3605135446Strhodes		if (rdtype == dns_rdatatype_a)
3606135446Strhodes			adbname->fetch_err = FIND_ERR_SUCCESS;
3607135446Strhodes		else
3608135446Strhodes			adbname->fetch6_err = FIND_ERR_SUCCESS;
3609135446Strhodes		break;
3610135446Strhodes	}
3611135446Strhodes
3612135446Strhodes	if (dns_rdataset_isassociated(&rdataset))
3613135446Strhodes		dns_rdataset_disassociate(&rdataset);
3614135446Strhodes
3615135446Strhodes	return (result);
3616135446Strhodes}
3617135446Strhodes
3618135446Strhodesstatic void
3619135446Strhodesfetch_callback(isc_task_t *task, isc_event_t *ev) {
3620135446Strhodes	dns_fetchevent_t *dev;
3621135446Strhodes	dns_adbname_t *name;
3622135446Strhodes	dns_adb_t *adb;
3623135446Strhodes	dns_adbfetch_t *fetch;
3624135446Strhodes	int bucket;
3625135446Strhodes	isc_eventtype_t ev_status;
3626135446Strhodes	isc_stdtime_t now;
3627135446Strhodes	isc_result_t result;
3628135446Strhodes	unsigned int address_type;
3629135446Strhodes	isc_boolean_t want_check_exit = ISC_FALSE;
3630135446Strhodes
3631135446Strhodes	UNUSED(task);
3632135446Strhodes
3633135446Strhodes	INSIST(ev->ev_type == DNS_EVENT_FETCHDONE);
3634135446Strhodes	dev = (dns_fetchevent_t *)ev;
3635135446Strhodes	name = ev->ev_arg;
3636135446Strhodes	INSIST(DNS_ADBNAME_VALID(name));
3637135446Strhodes	adb = name->adb;
3638135446Strhodes	INSIST(DNS_ADB_VALID(adb));
3639135446Strhodes
3640135446Strhodes	bucket = name->lock_bucket;
3641135446Strhodes	LOCK(&adb->namelocks[bucket]);
3642135446Strhodes
3643135446Strhodes	INSIST(NAME_FETCH_A(name) || NAME_FETCH_AAAA(name));
3644135446Strhodes	address_type = 0;
3645135446Strhodes	if (NAME_FETCH_A(name) && (name->fetch_a->fetch == dev->fetch)) {
3646135446Strhodes		address_type = DNS_ADBFIND_INET;
3647135446Strhodes		fetch = name->fetch_a;
3648135446Strhodes		name->fetch_a = NULL;
3649135446Strhodes	} else if (NAME_FETCH_AAAA(name)
3650135446Strhodes		   && (name->fetch_aaaa->fetch == dev->fetch)) {
3651135446Strhodes		address_type = DNS_ADBFIND_INET6;
3652135446Strhodes		fetch = name->fetch_aaaa;
3653135446Strhodes		name->fetch_aaaa = NULL;
3654186462Sdougb	} else
3655186462Sdougb		fetch = NULL;
3656135446Strhodes
3657186462Sdougb	INSIST(address_type != 0 && fetch != NULL);
3658186462Sdougb
3659135446Strhodes	dns_resolver_destroyfetch(&fetch->fetch);
3660135446Strhodes	dev->fetch = NULL;
3661135446Strhodes
3662135446Strhodes	ev_status = DNS_EVENT_ADBNOMOREADDRESSES;
3663135446Strhodes
3664135446Strhodes	/*
3665135446Strhodes	 * Cleanup things we don't care about.
3666135446Strhodes	 */
3667135446Strhodes	if (dev->node != NULL)
3668135446Strhodes		dns_db_detachnode(dev->db, &dev->node);
3669135446Strhodes	if (dev->db != NULL)
3670135446Strhodes		dns_db_detach(&dev->db);
3671135446Strhodes
3672135446Strhodes	/*
3673135446Strhodes	 * If this name is marked as dead, clean up, throwing away
3674135446Strhodes	 * potentially good data.
3675135446Strhodes	 */
3676135446Strhodes	if (NAME_DEAD(name)) {
3677135446Strhodes		free_adbfetch(adb, &fetch);
3678135446Strhodes		isc_event_free(&ev);
3679135446Strhodes
3680135446Strhodes		want_check_exit = kill_name(&name, DNS_EVENT_ADBCANCELED);
3681135446Strhodes
3682135446Strhodes		UNLOCK(&adb->namelocks[bucket]);
3683135446Strhodes
3684135446Strhodes		if (want_check_exit) {
3685135446Strhodes			LOCK(&adb->lock);
3686135446Strhodes			check_exit(adb);
3687135446Strhodes			UNLOCK(&adb->lock);
3688135446Strhodes		}
3689135446Strhodes
3690135446Strhodes		return;
3691135446Strhodes	}
3692135446Strhodes
3693135446Strhodes	isc_stdtime_get(&now);
3694135446Strhodes
3695135446Strhodes	/*
3696135446Strhodes	 * If we got a negative cache response, remember it.
3697135446Strhodes	 */
3698135446Strhodes	if (NCACHE_RESULT(dev->result)) {
3699135446Strhodes		dev->rdataset->ttl = ttlclamp(dev->rdataset->ttl);
3700135446Strhodes		if (address_type == DNS_ADBFIND_INET) {
3701135446Strhodes			DP(NCACHE_LEVEL, "adb fetch name %p: "
3702135446Strhodes			   "caching negative entry for A (ttl %u)",
3703135446Strhodes			   name, dev->rdataset->ttl);
3704135446Strhodes			name->expire_v4 = ISC_MIN(name->expire_v4,
3705135446Strhodes						  dev->rdataset->ttl + now);
3706135446Strhodes			if (dev->result == DNS_R_NCACHENXDOMAIN)
3707135446Strhodes				name->fetch_err = FIND_ERR_NXDOMAIN;
3708135446Strhodes			else
3709135446Strhodes				name->fetch_err = FIND_ERR_NXRRSET;
3710193149Sdougb			inc_stats(adb, dns_resstatscounter_gluefetchv4fail);
3711135446Strhodes		} else {
3712135446Strhodes			DP(NCACHE_LEVEL, "adb fetch name %p: "
3713135446Strhodes			   "caching negative entry for AAAA (ttl %u)",
3714135446Strhodes			   name, dev->rdataset->ttl);
3715135446Strhodes			name->expire_v6 = ISC_MIN(name->expire_v6,
3716135446Strhodes						  dev->rdataset->ttl + now);
3717135446Strhodes			if (dev->result == DNS_R_NCACHENXDOMAIN)
3718135446Strhodes				name->fetch6_err = FIND_ERR_NXDOMAIN;
3719135446Strhodes			else
3720135446Strhodes				name->fetch6_err = FIND_ERR_NXRRSET;
3721193149Sdougb			inc_stats(adb, dns_resstatscounter_gluefetchv6fail);
3722135446Strhodes		}
3723135446Strhodes		goto out;
3724135446Strhodes	}
3725135446Strhodes
3726135446Strhodes	/*
3727135446Strhodes	 * Handle CNAME/DNAME.
3728135446Strhodes	 */
3729135446Strhodes	if (dev->result == DNS_R_CNAME || dev->result == DNS_R_DNAME) {
3730135446Strhodes		dev->rdataset->ttl = ttlclamp(dev->rdataset->ttl);
3731135446Strhodes		clean_target(adb, &name->target);
3732135446Strhodes		name->expire_target = INT_MAX;
3733135446Strhodes		result = set_target(adb, &name->name,
3734135446Strhodes				    dns_fixedname_name(&dev->foundname),
3735135446Strhodes				    dev->rdataset,
3736135446Strhodes				    &name->target);
3737135446Strhodes		if (result == ISC_R_SUCCESS) {
3738135446Strhodes			DP(NCACHE_LEVEL,
3739135446Strhodes			   "adb fetch name %p: caching alias target",
3740135446Strhodes			   name);
3741135446Strhodes			name->expire_target = dev->rdataset->ttl + now;
3742135446Strhodes		}
3743135446Strhodes		goto check_result;
3744135446Strhodes	}
3745135446Strhodes
3746135446Strhodes	/*
3747135446Strhodes	 * Did we get back junk?  If so, and there are no more fetches
3748135446Strhodes	 * sitting out there, tell all the finds about it.
3749135446Strhodes	 */
3750135446Strhodes	if (dev->result != ISC_R_SUCCESS) {
3751135446Strhodes		char buf[DNS_NAME_FORMATSIZE];
3752135446Strhodes
3753135446Strhodes		dns_name_format(&name->name, buf, sizeof(buf));
3754135446Strhodes		DP(DEF_LEVEL, "adb: fetch of '%s' %s failed: %s",
3755135446Strhodes		   buf, address_type == DNS_ADBFIND_INET ? "A" : "AAAA",
3756135446Strhodes		   dns_result_totext(dev->result));
3757135446Strhodes		/* XXXMLG Don't pound on bad servers. */
3758135446Strhodes		if (address_type == DNS_ADBFIND_INET) {
3759135446Strhodes			name->expire_v4 = ISC_MIN(name->expire_v4, now + 300);
3760135446Strhodes			name->fetch_err = FIND_ERR_FAILURE;
3761193149Sdougb			inc_stats(adb, dns_resstatscounter_gluefetchv4fail);
3762135446Strhodes		} else {
3763135446Strhodes			name->expire_v6 = ISC_MIN(name->expire_v6, now + 300);
3764135446Strhodes			name->fetch6_err = FIND_ERR_FAILURE;
3765193149Sdougb			inc_stats(adb, dns_resstatscounter_gluefetchv6fail);
3766135446Strhodes		}
3767135446Strhodes		goto out;
3768135446Strhodes	}
3769135446Strhodes
3770135446Strhodes	/*
3771135446Strhodes	 * We got something potentially useful.
3772135446Strhodes	 */
3773135446Strhodes	result = import_rdataset(name, &fetch->rdataset, now);
3774135446Strhodes
3775135446Strhodes check_result:
3776135446Strhodes	if (result == ISC_R_SUCCESS) {
3777135446Strhodes		ev_status = DNS_EVENT_ADBMOREADDRESSES;
3778135446Strhodes		if (address_type == DNS_ADBFIND_INET)
3779135446Strhodes			name->fetch_err = FIND_ERR_SUCCESS;
3780135446Strhodes		else
3781135446Strhodes			name->fetch6_err = FIND_ERR_SUCCESS;
3782135446Strhodes	}
3783135446Strhodes
3784135446Strhodes out:
3785135446Strhodes	free_adbfetch(adb, &fetch);
3786135446Strhodes	isc_event_free(&ev);
3787135446Strhodes
3788135446Strhodes	clean_finds_at_name(name, ev_status, address_type);
3789135446Strhodes
3790135446Strhodes	UNLOCK(&adb->namelocks[bucket]);
3791135446Strhodes}
3792135446Strhodes
3793135446Strhodesstatic isc_result_t
3794135446Strhodesfetch_name(dns_adbname_t *adbname,
3795135446Strhodes	   isc_boolean_t start_at_zone,
3796135446Strhodes	   dns_rdatatype_t type)
3797135446Strhodes{
3798135446Strhodes	isc_result_t result;
3799135446Strhodes	dns_adbfetch_t *fetch = NULL;
3800135446Strhodes	dns_adb_t *adb;
3801135446Strhodes	dns_fixedname_t fixed;
3802135446Strhodes	dns_name_t *name;
3803135446Strhodes	dns_rdataset_t rdataset;
3804135446Strhodes	dns_rdataset_t *nameservers;
3805135446Strhodes	unsigned int options;
3806135446Strhodes
3807135446Strhodes	INSIST(DNS_ADBNAME_VALID(adbname));
3808135446Strhodes	adb = adbname->adb;
3809135446Strhodes	INSIST(DNS_ADB_VALID(adb));
3810135446Strhodes
3811135446Strhodes	INSIST((type == dns_rdatatype_a && !NAME_FETCH_V4(adbname)) ||
3812135446Strhodes	       (type == dns_rdatatype_aaaa && !NAME_FETCH_V6(adbname)));
3813135446Strhodes
3814135446Strhodes	adbname->fetch_err = FIND_ERR_NOTFOUND;
3815135446Strhodes
3816135446Strhodes	name = NULL;
3817135446Strhodes	nameservers = NULL;
3818135446Strhodes	dns_rdataset_init(&rdataset);
3819135446Strhodes
3820135446Strhodes	options = DNS_FETCHOPT_NOVALIDATE;
3821135446Strhodes	if (start_at_zone) {
3822135446Strhodes		DP(ENTER_LEVEL,
3823135446Strhodes		   "fetch_name: starting at zone for name %p",
3824135446Strhodes		   adbname);
3825135446Strhodes		dns_fixedname_init(&fixed);
3826135446Strhodes		name = dns_fixedname_name(&fixed);
3827135446Strhodes		result = dns_view_findzonecut2(adb->view, &adbname->name, name,
3828135446Strhodes					       0, 0, ISC_TRUE, ISC_FALSE,
3829135446Strhodes					       &rdataset, NULL);
3830135446Strhodes		if (result != ISC_R_SUCCESS && result != DNS_R_HINT)
3831135446Strhodes			goto cleanup;
3832135446Strhodes		nameservers = &rdataset;
3833135446Strhodes		options |= DNS_FETCHOPT_UNSHARED;
3834135446Strhodes	}
3835135446Strhodes
3836135446Strhodes	fetch = new_adbfetch(adb);
3837135446Strhodes	if (fetch == NULL) {
3838135446Strhodes		result = ISC_R_NOMEMORY;
3839135446Strhodes		goto cleanup;
3840135446Strhodes	}
3841135446Strhodes
3842135446Strhodes	result = dns_resolver_createfetch(adb->view->resolver, &adbname->name,
3843135446Strhodes					  type, name, nameservers, NULL,
3844135446Strhodes					  options, adb->task, fetch_callback,
3845135446Strhodes					  adbname, &fetch->rdataset, NULL,
3846135446Strhodes					  &fetch->fetch);
3847135446Strhodes	if (result != ISC_R_SUCCESS)
3848135446Strhodes		goto cleanup;
3849135446Strhodes
3850193149Sdougb	if (type == dns_rdatatype_a) {
3851135446Strhodes		adbname->fetch_a = fetch;
3852193149Sdougb		inc_stats(adb, dns_resstatscounter_gluefetchv4);
3853193149Sdougb	} else {
3854135446Strhodes		adbname->fetch_aaaa = fetch;
3855193149Sdougb		inc_stats(adb, dns_resstatscounter_gluefetchv6);
3856193149Sdougb	}
3857135446Strhodes	fetch = NULL;  /* Keep us from cleaning this up below. */
3858135446Strhodes
3859135446Strhodes cleanup:
3860135446Strhodes	if (fetch != NULL)
3861135446Strhodes		free_adbfetch(adb, &fetch);
3862135446Strhodes	if (dns_rdataset_isassociated(&rdataset))
3863135446Strhodes		dns_rdataset_disassociate(&rdataset);
3864135446Strhodes
3865135446Strhodes	return (result);
3866135446Strhodes}
3867135446Strhodes
3868135446Strhodes/*
3869135446Strhodes * XXXMLG Needs to take a find argument and an address info, no zone or adb,
3870135446Strhodes * since these can be extracted from the find itself.
3871135446Strhodes */
3872135446Strhodesisc_result_t
3873170222Sdougbdns_adb_marklame(dns_adb_t *adb, dns_adbaddrinfo_t *addr, dns_name_t *qname,
3874170222Sdougb		 dns_rdatatype_t qtype, isc_stdtime_t expire_time)
3875135446Strhodes{
3876170222Sdougb	dns_adblameinfo_t *li;
3877135446Strhodes	int bucket;
3878135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
3879135446Strhodes
3880135446Strhodes	REQUIRE(DNS_ADB_VALID(adb));
3881135446Strhodes	REQUIRE(DNS_ADBADDRINFO_VALID(addr));
3882170222Sdougb	REQUIRE(qname != NULL);
3883135446Strhodes
3884135446Strhodes	bucket = addr->entry->lock_bucket;
3885135446Strhodes	LOCK(&adb->entrylocks[bucket]);
3886170222Sdougb	li = ISC_LIST_HEAD(addr->entry->lameinfo);
3887170222Sdougb	while (li != NULL &&
3888170222Sdougb	       (li->qtype != qtype || !dns_name_equal(qname, &li->qname)))
3889170222Sdougb		li = ISC_LIST_NEXT(li, plink);
3890170222Sdougb	if (li != NULL) {
3891170222Sdougb		if (expire_time > li->lame_timer)
3892170222Sdougb			li->lame_timer = expire_time;
3893135446Strhodes		goto unlock;
3894135446Strhodes	}
3895170222Sdougb	li = new_adblameinfo(adb, qname, qtype);
3896170222Sdougb	if (li == NULL) {
3897135446Strhodes		result = ISC_R_NOMEMORY;
3898135446Strhodes		goto unlock;
3899135446Strhodes	}
3900135446Strhodes
3901170222Sdougb	li->lame_timer = expire_time;
3902135446Strhodes
3903170222Sdougb	ISC_LIST_PREPEND(addr->entry->lameinfo, li, plink);
3904135446Strhodes unlock:
3905135446Strhodes	UNLOCK(&adb->entrylocks[bucket]);
3906135446Strhodes
3907153816Sdougb	return (result);
3908135446Strhodes}
3909135446Strhodes
3910135446Strhodesvoid
3911135446Strhodesdns_adb_adjustsrtt(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
3912135446Strhodes		   unsigned int rtt, unsigned int factor)
3913135446Strhodes{
3914135446Strhodes	int bucket;
3915135446Strhodes	unsigned int new_srtt;
3916135446Strhodes	isc_stdtime_t now;
3917135446Strhodes
3918135446Strhodes	REQUIRE(DNS_ADB_VALID(adb));
3919135446Strhodes	REQUIRE(DNS_ADBADDRINFO_VALID(addr));
3920135446Strhodes	REQUIRE(factor <= 10);
3921135446Strhodes
3922135446Strhodes	bucket = addr->entry->lock_bucket;
3923135446Strhodes	LOCK(&adb->entrylocks[bucket]);
3924135446Strhodes
3925135446Strhodes	if (factor == DNS_ADB_RTTADJAGE)
3926135446Strhodes		new_srtt = addr->entry->srtt * 98 / 100;
3927135446Strhodes	else
3928135446Strhodes		new_srtt = (addr->entry->srtt / 10 * factor)
3929135446Strhodes			+ (rtt / 10 * (10 - factor));
3930135446Strhodes
3931135446Strhodes	addr->entry->srtt = new_srtt;
3932135446Strhodes	addr->srtt = new_srtt;
3933135446Strhodes
3934245163Serwin	if (addr->entry->expires == 0) {
3935245163Serwin		isc_stdtime_get(&now);
3936245163Serwin		addr->entry->expires = now + ADB_ENTRY_WINDOW;
3937245163Serwin	}
3938135446Strhodes
3939135446Strhodes	UNLOCK(&adb->entrylocks[bucket]);
3940135446Strhodes}
3941135446Strhodes
3942135446Strhodesvoid
3943135446Strhodesdns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
3944135446Strhodes		    unsigned int bits, unsigned int mask)
3945135446Strhodes{
3946135446Strhodes	int bucket;
3947245163Serwin	isc_stdtime_t now;
3948135446Strhodes
3949135446Strhodes	REQUIRE(DNS_ADB_VALID(adb));
3950135446Strhodes	REQUIRE(DNS_ADBADDRINFO_VALID(addr));
3951135446Strhodes
3952135446Strhodes	bucket = addr->entry->lock_bucket;
3953135446Strhodes	LOCK(&adb->entrylocks[bucket]);
3954135446Strhodes
3955135446Strhodes	addr->entry->flags = (addr->entry->flags & ~mask) | (bits & mask);
3956245163Serwin	if (addr->entry->expires == 0) {
3957245163Serwin		isc_stdtime_get(&now);
3958245163Serwin		addr->entry->expires = now + ADB_ENTRY_WINDOW;
3959245163Serwin	}
3960245163Serwin
3961135446Strhodes	/*
3962135446Strhodes	 * Note that we do not update the other bits in addr->flags with
3963135446Strhodes	 * the most recent values from addr->entry->flags.
3964135446Strhodes	 */
3965135446Strhodes	addr->flags = (addr->flags & ~mask) | (bits & mask);
3966135446Strhodes
3967135446Strhodes	UNLOCK(&adb->entrylocks[bucket]);
3968135446Strhodes}
3969135446Strhodes
3970135446Strhodesisc_result_t
3971135446Strhodesdns_adb_findaddrinfo(dns_adb_t *adb, isc_sockaddr_t *sa,
3972135446Strhodes		     dns_adbaddrinfo_t **addrp, isc_stdtime_t now)
3973135446Strhodes{
3974135446Strhodes	int bucket;
3975135446Strhodes	dns_adbentry_t *entry;
3976135446Strhodes	dns_adbaddrinfo_t *addr;
3977135446Strhodes	isc_result_t result;
3978135446Strhodes	in_port_t port;
3979135446Strhodes
3980135446Strhodes	REQUIRE(DNS_ADB_VALID(adb));
3981135446Strhodes	REQUIRE(addrp != NULL && *addrp == NULL);
3982135446Strhodes
3983135446Strhodes	UNUSED(now);
3984135446Strhodes
3985135446Strhodes	result = ISC_R_SUCCESS;
3986135446Strhodes	bucket = DNS_ADB_INVALIDBUCKET;
3987193149Sdougb	entry = find_entry_and_lock(adb, sa, &bucket, now);
3988225361Sdougb	INSIST(bucket != DNS_ADB_INVALIDBUCKET);
3989135446Strhodes	if (adb->entry_sd[bucket]) {
3990135446Strhodes		result = ISC_R_SHUTTINGDOWN;
3991135446Strhodes		goto unlock;
3992135446Strhodes	}
3993135446Strhodes	if (entry == NULL) {
3994135446Strhodes		/*
3995135446Strhodes		 * We don't know anything about this address.
3996135446Strhodes		 */
3997135446Strhodes		entry = new_adbentry(adb);
3998135446Strhodes		if (entry == NULL) {
3999135446Strhodes			result = ISC_R_NOMEMORY;
4000135446Strhodes			goto unlock;
4001135446Strhodes		}
4002135446Strhodes		entry->sockaddr = *sa;
4003135446Strhodes		link_entry(adb, bucket, entry);
4004135446Strhodes		DP(ENTER_LEVEL, "findaddrinfo: new entry %p", entry);
4005135446Strhodes	} else
4006135446Strhodes		DP(ENTER_LEVEL, "findaddrinfo: found entry %p", entry);
4007135446Strhodes
4008135446Strhodes	port = isc_sockaddr_getport(sa);
4009135446Strhodes	addr = new_adbaddrinfo(adb, entry, port);
4010174187Sdougb	if (addr == NULL) {
4011174187Sdougb		result = ISC_R_NOMEMORY;
4012174187Sdougb	} else {
4013135446Strhodes		inc_entry_refcnt(adb, entry, ISC_FALSE);
4014135446Strhodes		*addrp = addr;
4015135446Strhodes	}
4016135446Strhodes
4017135446Strhodes unlock:
4018135446Strhodes	UNLOCK(&adb->entrylocks[bucket]);
4019135446Strhodes
4020135446Strhodes	return (result);
4021135446Strhodes}
4022135446Strhodes
4023135446Strhodesvoid
4024135446Strhodesdns_adb_freeaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **addrp) {
4025135446Strhodes	dns_adbaddrinfo_t *addr;
4026135446Strhodes	dns_adbentry_t *entry;
4027135446Strhodes	int bucket;
4028135446Strhodes	isc_stdtime_t now;
4029135446Strhodes	isc_boolean_t want_check_exit = ISC_FALSE;
4030214586Sdougb	isc_boolean_t overmem;
4031135446Strhodes
4032135446Strhodes	REQUIRE(DNS_ADB_VALID(adb));
4033135446Strhodes	REQUIRE(addrp != NULL);
4034135446Strhodes	addr = *addrp;
4035135446Strhodes	REQUIRE(DNS_ADBADDRINFO_VALID(addr));
4036135446Strhodes	entry = addr->entry;
4037135446Strhodes	REQUIRE(DNS_ADBENTRY_VALID(entry));
4038135446Strhodes
4039135446Strhodes	*addrp = NULL;
4040214586Sdougb	overmem = isc_mem_isovermem(adb->mctx);
4041135446Strhodes
4042135446Strhodes	bucket = addr->entry->lock_bucket;
4043135446Strhodes	LOCK(&adb->entrylocks[bucket]);
4044135446Strhodes
4045245163Serwin	if (entry->expires == 0) {
4046245163Serwin		isc_stdtime_get(&now);
4047245163Serwin		entry->expires = now + ADB_ENTRY_WINDOW;
4048245163Serwin	}
4049135446Strhodes
4050214586Sdougb	want_check_exit = dec_entry_refcnt(adb, overmem, entry, ISC_FALSE);
4051135446Strhodes
4052135446Strhodes	UNLOCK(&adb->entrylocks[bucket]);
4053135446Strhodes
4054135446Strhodes	addr->entry = NULL;
4055135446Strhodes	free_adbaddrinfo(adb, &addr);
4056135446Strhodes
4057135446Strhodes	if (want_check_exit) {
4058135446Strhodes		LOCK(&adb->lock);
4059135446Strhodes		check_exit(adb);
4060135446Strhodes		UNLOCK(&adb->lock);
4061135446Strhodes	}
4062135446Strhodes}
4063135446Strhodes
4064135446Strhodesvoid
4065135446Strhodesdns_adb_flush(dns_adb_t *adb) {
4066135446Strhodes	unsigned int i;
4067135446Strhodes
4068135446Strhodes	INSIST(DNS_ADB_VALID(adb));
4069135446Strhodes
4070135446Strhodes	LOCK(&adb->lock);
4071135446Strhodes
4072143731Sdougb	/*
4073143731Sdougb	 * Call our cleanup routines.
4074143731Sdougb	 */
4075224092Sdougb	for (i = 0; i < adb->nnames; i++)
4076135446Strhodes		RUNTIME_CHECK(cleanup_names(adb, i, INT_MAX) == ISC_FALSE);
4077224092Sdougb	for (i = 0; i < adb->nentries; i++)
4078135446Strhodes		RUNTIME_CHECK(cleanup_entries(adb, i, INT_MAX) == ISC_FALSE);
4079135446Strhodes
4080135446Strhodes#ifdef DUMP_ADB_AFTER_CLEANING
4081143731Sdougb	dump_adb(adb, stdout, ISC_TRUE, INT_MAX);
4082135446Strhodes#endif
4083135446Strhodes
4084135446Strhodes	UNLOCK(&adb->lock);
4085135446Strhodes}
4086135446Strhodes
4087135446Strhodesvoid
4088135446Strhodesdns_adb_flushname(dns_adb_t *adb, dns_name_t *name) {
4089135446Strhodes	dns_adbname_t *adbname;
4090135446Strhodes	dns_adbname_t *nextname;
4091135446Strhodes	int bucket;
4092135446Strhodes
4093135446Strhodes	INSIST(DNS_ADB_VALID(adb));
4094135446Strhodes
4095135446Strhodes	LOCK(&adb->lock);
4096224092Sdougb	bucket = dns_name_hash(name, ISC_FALSE) % adb->nnames;
4097135446Strhodes	LOCK(&adb->namelocks[bucket]);
4098135446Strhodes	adbname = ISC_LIST_HEAD(adb->names[bucket]);
4099135446Strhodes	while (adbname != NULL) {
4100135446Strhodes		nextname = ISC_LIST_NEXT(adbname, plink);
4101135446Strhodes		if (!NAME_DEAD(adbname) &&
4102135446Strhodes		    dns_name_equal(name, &adbname->name)) {
4103135446Strhodes			RUNTIME_CHECK(kill_name(&adbname,
4104135446Strhodes						DNS_EVENT_ADBCANCELED) ==
4105135446Strhodes				      ISC_FALSE);
4106135446Strhodes		}
4107135446Strhodes		adbname = nextname;
4108135446Strhodes	}
4109135446Strhodes	UNLOCK(&adb->namelocks[bucket]);
4110135446Strhodes	UNLOCK(&adb->lock);
4111135446Strhodes}
4112135446Strhodes
4113135446Strhodesstatic void
4114135446Strhodeswater(void *arg, int mark) {
4115214586Sdougb	/*
4116214586Sdougb	 * We're going to change the way to handle overmem condition: use
4117214586Sdougb	 * isc_mem_isovermem() instead of storing the state via this callback,
4118214586Sdougb	 * since the latter way tends to cause race conditions.
4119214586Sdougb	 * To minimize the change, and in case we re-enable the callback
4120214586Sdougb	 * approach, however, keep this function at the moment.
4121214586Sdougb	 */
4122214586Sdougb
4123135446Strhodes	dns_adb_t *adb = arg;
4124135446Strhodes	isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER);
4125135446Strhodes
4126135446Strhodes	REQUIRE(DNS_ADB_VALID(adb));
4127135446Strhodes
4128135446Strhodes	DP(ISC_LOG_DEBUG(1),
4129135446Strhodes	   "adb reached %s water mark", overmem ? "high" : "low");
4130135446Strhodes}
4131135446Strhodes
4132135446Strhodesvoid
4133135446Strhodesdns_adb_setadbsize(dns_adb_t *adb, isc_uint32_t size) {
4134135446Strhodes	isc_uint32_t hiwater;
4135135446Strhodes	isc_uint32_t lowater;
4136135446Strhodes
4137135446Strhodes	INSIST(DNS_ADB_VALID(adb));
4138135446Strhodes
4139254402Serwin	if (size != 0U && size < DNS_ADB_MINADBSIZE)
4140135446Strhodes		size = DNS_ADB_MINADBSIZE;
4141135446Strhodes
4142135446Strhodes	hiwater = size - (size >> 3);   /* Approximately 7/8ths. */
4143135446Strhodes	lowater = size - (size >> 2);   /* Approximately 3/4ths. */
4144135446Strhodes
4145254402Serwin	if (size == 0U || hiwater == 0U || lowater == 0U)
4146135446Strhodes		isc_mem_setwater(adb->mctx, water, adb, 0, 0);
4147135446Strhodes	else
4148135446Strhodes		isc_mem_setwater(adb->mctx, water, adb, hiwater, lowater);
4149135446Strhodes}
4150