Add BIND 9.2.4rc7.
[dragonfly.git] / contrib / bind-9.2.4rc7 / lib / dns / lookup.c
blob37cf1a54df3fa179407c41b6dd0baf919fc9abd8
1 /*
2 * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000, 2001 Internet Software Consortium.
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
18 /* $Id: lookup.c,v 1.9.2.3 2004/04/15 02:16:27 marka Exp $ */
20 #include <config.h>
22 #include <isc/mem.h>
23 #include <isc/netaddr.h>
24 #include <isc/string.h> /* Required for HP/UX (and others?) */
25 #include <isc/task.h>
26 #include <isc/util.h>
28 #include <dns/db.h>
29 #include <dns/events.h>
30 #include <dns/lookup.h>
31 #include <dns/rdata.h>
32 #include <dns/rdataset.h>
33 #include <dns/rdatastruct.h>
34 #include <dns/resolver.h>
35 #include <dns/result.h>
36 #include <dns/view.h>
38 struct dns_lookup {
39 /* Unlocked. */
40 unsigned int magic;
41 isc_mem_t * mctx;
42 isc_mutex_t lock;
43 dns_rdatatype_t type;
44 dns_fixedname_t name;
45 /* Locked by lock. */
46 unsigned int options;
47 isc_task_t * task;
48 dns_view_t * view;
49 dns_lookupevent_t * event;
50 dns_fetch_t * fetch;
51 unsigned int restarts;
52 isc_boolean_t canceled;
53 dns_rdataset_t rdataset;
54 dns_rdataset_t sigrdataset;
57 #define LOOKUP_MAGIC ISC_MAGIC('l', 'o', 'o', 'k')
58 #define VALID_LOOKUP(l) ISC_MAGIC_VALID((l), LOOKUP_MAGIC)
60 #define MAX_RESTARTS 16
62 static void lookup_find(dns_lookup_t *lookup, dns_fetchevent_t *event);
64 static void
65 fetch_done(isc_task_t *task, isc_event_t *event) {
66 dns_lookup_t *lookup = event->ev_arg;
67 dns_fetchevent_t *fevent;
69 UNUSED(task);
70 REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE);
71 REQUIRE(VALID_LOOKUP(lookup));
72 REQUIRE(lookup->task == task);
73 fevent = (dns_fetchevent_t *)event;
74 REQUIRE(fevent->fetch == lookup->fetch);
76 lookup_find(lookup, fevent);
79 static inline isc_result_t
80 start_fetch(dns_lookup_t *lookup) {
81 isc_result_t result;
84 * The caller must be holding the lookup's lock.
87 REQUIRE(lookup->fetch == NULL);
89 result = dns_resolver_createfetch(lookup->view->resolver,
90 dns_fixedname_name(&lookup->name),
91 lookup->type,
92 NULL, NULL, NULL, 0,
93 lookup->task, fetch_done, lookup,
94 &lookup->rdataset,
95 &lookup->sigrdataset,
96 &lookup->fetch);
98 return (result);
101 static isc_result_t
102 build_event(dns_lookup_t *lookup) {
103 dns_name_t *name = NULL;
104 dns_rdataset_t *rdataset = NULL;
105 dns_rdataset_t *sigrdataset = NULL;
106 isc_result_t result;
108 name = isc_mem_get(lookup->mctx, sizeof(dns_name_t));
109 if (name == NULL) {
110 result = ISC_R_NOMEMORY;
111 goto fail;
113 dns_name_init(name, NULL);
114 result = dns_name_dup(dns_fixedname_name(&lookup->name),
115 lookup->mctx, name);
116 if (result != ISC_R_SUCCESS)
117 goto fail;
119 if (dns_rdataset_isassociated(&lookup->rdataset)) {
120 rdataset = isc_mem_get(lookup->mctx, sizeof(dns_rdataset_t));
121 if (rdataset == NULL) {
122 result = ISC_R_NOMEMORY;
123 goto fail;
125 dns_rdataset_init(rdataset);
126 dns_rdataset_clone(&lookup->rdataset, rdataset);
129 if (dns_rdataset_isassociated(&lookup->sigrdataset)) {
130 sigrdataset = isc_mem_get(lookup->mctx,
131 sizeof(dns_rdataset_t));
132 if (sigrdataset == NULL) {
133 result = ISC_R_NOMEMORY;
134 goto fail;
136 dns_rdataset_init(sigrdataset);
137 dns_rdataset_clone(&lookup->sigrdataset, sigrdataset);
140 lookup->event->name = name;
141 lookup->event->rdataset = rdataset;
142 lookup->event->sigrdataset = sigrdataset;
144 return (ISC_R_SUCCESS);
146 fail:
147 if (name != NULL) {
148 if (dns_name_dynamic(name))
149 dns_name_free(name, lookup->mctx);
150 isc_mem_put(lookup->mctx, name, sizeof(dns_name_t));
152 if (rdataset != NULL) {
153 if (dns_rdataset_isassociated(rdataset))
154 dns_rdataset_disassociate(rdataset);
155 isc_mem_put(lookup->mctx, rdataset, sizeof(dns_rdataset_t));
157 if (sigrdataset != NULL) {
158 if (dns_rdataset_isassociated(sigrdataset))
159 dns_rdataset_disassociate(sigrdataset);
160 isc_mem_put(lookup->mctx, sigrdataset, sizeof(dns_rdataset_t));
162 return (result);
165 static isc_result_t
166 view_find(dns_lookup_t *lookup, dns_name_t *foundname) {
167 isc_result_t result;
168 dns_name_t *name = dns_fixedname_name(&lookup->name);
169 dns_rdatatype_t type;
171 if (lookup->type == dns_rdatatype_sig)
172 type = dns_rdatatype_any;
173 else
174 type = lookup->type;
176 result = dns_view_find(lookup->view, name, type, 0, 0, ISC_FALSE,
177 &lookup->event->db, &lookup->event->node,
178 foundname, &lookup->rdataset,
179 &lookup->sigrdataset);
180 return (result);
183 static void
184 lookup_find(dns_lookup_t *lookup, dns_fetchevent_t *event) {
185 isc_result_t result;
186 isc_boolean_t want_restart;
187 isc_boolean_t send_event = ISC_FALSE;
188 dns_name_t *name, *fname, *prefix;
189 dns_fixedname_t foundname, fixed;
190 dns_rdata_t rdata = DNS_RDATA_INIT;
191 unsigned int nlabels, nbits;
192 int order;
193 dns_namereln_t namereln;
194 dns_rdata_cname_t cname;
195 dns_rdata_dname_t dname;
197 REQUIRE(VALID_LOOKUP(lookup));
199 LOCK(&lookup->lock);
201 result = ISC_R_SUCCESS;
202 name = dns_fixedname_name(&lookup->name);
204 do {
205 lookup->restarts++;
206 want_restart = ISC_FALSE;
208 if (event == NULL && !lookup->canceled) {
209 dns_fixedname_init(&foundname);
210 fname = dns_fixedname_name(&foundname);
211 INSIST(!dns_rdataset_isassociated(&lookup->rdataset));
212 INSIST(!dns_rdataset_isassociated
213 (&lookup->sigrdataset));
214 result = view_find(lookup, fname);
215 if (result == ISC_R_NOTFOUND) {
217 * We don't know anything about the name.
218 * Launch a fetch.
220 if (lookup->event->node != NULL) {
221 INSIST(lookup->event->db != NULL);
222 dns_db_detachnode(lookup->event->db,
223 &lookup->event->node);
225 if (lookup->event->db != NULL)
226 dns_db_detach(&lookup->event->db);
227 result = start_fetch(lookup);
228 if (result != ISC_R_SUCCESS)
229 send_event = ISC_TRUE;
230 goto done;
232 } else {
233 result = event->result;
234 fname = dns_fixedname_name(&event->foundname);
235 dns_resolver_destroyfetch(&lookup->fetch);
236 INSIST(event->rdataset == &lookup->rdataset);
237 INSIST(event->sigrdataset == &lookup->sigrdataset);
241 * If we've been canceled, forget about the result.
243 if (lookup->canceled)
244 result = ISC_R_CANCELED;
246 switch (result) {
247 case ISC_R_SUCCESS:
248 result = build_event(lookup);
249 send_event = ISC_TRUE;
250 if (event == NULL)
251 break;
252 if (event->db != NULL)
253 dns_db_attach(event->db, &lookup->event->db);
254 if (event->node != NULL)
255 dns_db_attachnode(lookup->event->db,
256 event->node,
257 &lookup->event->node);
258 break;
259 case DNS_R_CNAME:
261 * Copy the CNAME's target into the lookup's
262 * query name and start over.
264 result = dns_rdataset_first(&lookup->rdataset);
265 if (result != ISC_R_SUCCESS)
266 break;
267 dns_rdataset_current(&lookup->rdataset, &rdata);
268 result = dns_rdata_tostruct(&rdata, &cname, NULL);
269 dns_rdata_reset(&rdata);
270 if (result != ISC_R_SUCCESS)
271 break;
272 result = dns_name_copy(&cname.cname, name, NULL);
273 dns_rdata_freestruct(&cname);
274 if (result == ISC_R_SUCCESS)
275 want_restart = ISC_TRUE;
276 break;
277 case DNS_R_DNAME:
278 namereln = dns_name_fullcompare(name, fname, &order,
279 &nlabels, &nbits);
280 INSIST(namereln == dns_namereln_subdomain);
282 * Get the target name of the DNAME.
284 result = dns_rdataset_first(&lookup->rdataset);
285 if (result != ISC_R_SUCCESS)
286 break;
287 dns_rdataset_current(&lookup->rdataset, &rdata);
288 result = dns_rdata_tostruct(&rdata, &dname, NULL);
289 dns_rdata_reset(&rdata);
290 if (result != ISC_R_SUCCESS)
291 break;
293 * Construct the new query name and start over.
295 dns_fixedname_init(&fixed);
296 prefix = dns_fixedname_name(&fixed);
297 result = dns_name_split(name, nlabels, nbits, prefix,
298 NULL);
299 if (result != ISC_R_SUCCESS) {
300 dns_rdata_freestruct(&dname);
301 break;
303 result = dns_name_concatenate(prefix, &dname.dname,
304 name, NULL);
305 dns_rdata_freestruct(&dname);
306 if (result == ISC_R_SUCCESS)
307 want_restart = ISC_TRUE;
308 break;
309 default:
310 send_event = ISC_TRUE;
313 if (dns_rdataset_isassociated(&lookup->rdataset))
314 dns_rdataset_disassociate(&lookup->rdataset);
315 if (dns_rdataset_isassociated(&lookup->sigrdataset))
316 dns_rdataset_disassociate(&lookup->sigrdataset);
318 done:
319 if (event != NULL) {
320 if (event->node != NULL)
321 dns_db_detachnode(event->db, &event->node);
322 if (event->db != NULL)
323 dns_db_detach(&event->db);
324 isc_event_free(ISC_EVENT_PTR(&event));
328 * Limit the number of restarts.
330 if (want_restart && lookup->restarts == MAX_RESTARTS) {
331 want_restart = ISC_FALSE;
332 result = ISC_R_QUOTA;
333 send_event = ISC_TRUE;
336 } while (want_restart);
338 if (send_event) {
339 lookup->event->result = result;
340 lookup->event->ev_sender = lookup;
341 isc_task_sendanddetach(&lookup->task,
342 (isc_event_t **)&lookup->event);
343 dns_view_detach(&lookup->view);
346 UNLOCK(&lookup->lock);
349 static void
350 levent_destroy(isc_event_t *event) {
351 dns_lookupevent_t *levent;
352 isc_mem_t *mctx;
354 REQUIRE(event->ev_type == DNS_EVENT_LOOKUPDONE);
355 mctx = event->ev_destroy_arg;
356 levent = (dns_lookupevent_t *)event;
358 if (levent->name != NULL) {
359 if (dns_name_dynamic(levent->name))
360 dns_name_free(levent->name, mctx);
361 isc_mem_put(mctx, levent->name, sizeof(dns_name_t));
363 if (levent->rdataset != NULL) {
364 dns_rdataset_disassociate(levent->rdataset);
365 isc_mem_put(mctx, levent->rdataset, sizeof(dns_rdataset_t));
367 if (levent->sigrdataset != NULL) {
368 dns_rdataset_disassociate(levent->sigrdataset);
369 isc_mem_put(mctx, levent->sigrdataset, sizeof(dns_rdataset_t));
371 if (levent->node != NULL)
372 dns_db_detachnode(levent->db, &levent->node);
373 if (levent->db != NULL)
374 dns_db_detach(&levent->db);
375 isc_mem_put(mctx, event, event->ev_size);
379 isc_result_t
380 dns_lookup_create(isc_mem_t *mctx, dns_name_t *name, dns_rdatatype_t type,
381 dns_view_t *view, unsigned int options, isc_task_t *task,
382 isc_taskaction_t action, void *arg, dns_lookup_t **lookupp)
384 isc_result_t result;
385 dns_lookup_t *lookup;
386 isc_event_t *ievent;
388 lookup = isc_mem_get(mctx, sizeof *lookup);
389 if (lookup == NULL)
390 return (ISC_R_NOMEMORY);
391 lookup->mctx = mctx;
392 lookup->options = options;
394 ievent = isc_event_allocate(mctx, lookup, DNS_EVENT_LOOKUPDONE,
395 action, arg, sizeof *lookup->event);
396 if (ievent == NULL) {
397 result = ISC_R_NOMEMORY;
398 goto cleanup_lookup;
400 lookup->event = (dns_lookupevent_t *)ievent;
401 lookup->event->ev_destroy = levent_destroy;
402 lookup->event->ev_destroy_arg = mctx;
403 lookup->event->result = ISC_R_FAILURE;
404 lookup->event->name = NULL;
405 lookup->event->rdataset = NULL;
406 lookup->event->sigrdataset = NULL;
407 lookup->event->db = NULL;
408 lookup->event->node = NULL;
410 lookup->task = NULL;
411 isc_task_attach(task, &lookup->task);
413 result = isc_mutex_init(&lookup->lock);
414 if (result != ISC_R_SUCCESS)
415 goto cleanup_event;
417 dns_fixedname_init(&lookup->name);
419 result = dns_name_copy(name, dns_fixedname_name(&lookup->name), NULL);
420 if (result != ISC_R_SUCCESS)
421 goto cleanup_lock;
423 lookup->type = type;
424 lookup->view = NULL;
425 dns_view_attach(view, &lookup->view);
426 lookup->fetch = NULL;
427 lookup->restarts = 0;
428 lookup->canceled = ISC_FALSE;
429 dns_rdataset_init(&lookup->rdataset);
430 dns_rdataset_init(&lookup->sigrdataset);
431 lookup->magic = LOOKUP_MAGIC;
433 *lookupp = lookup;
435 lookup_find(lookup, NULL);
437 return (ISC_R_SUCCESS);
439 cleanup_lock:
440 DESTROYLOCK(&lookup->lock);
442 cleanup_event:
443 ievent = (isc_event_t *)lookup->event;
444 isc_event_free(&ievent);
445 lookup->event = NULL;
447 isc_task_detach(&lookup->task);
449 cleanup_lookup:
450 isc_mem_put(mctx, lookup, sizeof *lookup);
452 return (result);
455 void
456 dns_lookup_cancel(dns_lookup_t *lookup) {
457 REQUIRE(VALID_LOOKUP(lookup));
459 LOCK(&lookup->lock);
461 if (!lookup->canceled) {
462 lookup->canceled = ISC_TRUE;
463 if (lookup->fetch != NULL) {
464 INSIST(lookup->view != NULL);
465 dns_resolver_cancelfetch(lookup->fetch);
469 UNLOCK(&lookup->lock);
472 void
473 dns_lookup_destroy(dns_lookup_t **lookupp) {
474 dns_lookup_t *lookup;
476 REQUIRE(lookupp != NULL);
477 lookup = *lookupp;
478 REQUIRE(VALID_LOOKUP(lookup));
479 REQUIRE(lookup->event == NULL);
480 REQUIRE(lookup->task == NULL);
481 REQUIRE(lookup->view == NULL);
482 if (dns_rdataset_isassociated(&lookup->rdataset))
483 dns_rdataset_disassociate(&lookup->rdataset);
484 if (dns_rdataset_isassociated(&lookup->sigrdataset))
485 dns_rdataset_disassociate(&lookup->sigrdataset);
487 DESTROYLOCK(&lookup->lock);
488 lookup->magic = 0;
489 isc_mem_put(lookup->mctx, lookup, sizeof *lookup);
491 *lookupp = NULL;