Merge branch 'vendor/BIND' into bind_vendor2
[dragonfly.git] / contrib / bind-9.5.2 / lib / dns / lookup.c
1 /*
2  * Copyright (C) 2004, 2005, 2007  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2000, 2001, 2003  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and/or 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.
8  *
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.
16  */
17
18 /* $Id: lookup.c,v 1.21 2007/06/18 23:47:40 tbox Exp $ */
19
20 /*! \file */
21
22 #include <config.h>
23
24 #include <isc/mem.h>
25 #include <isc/netaddr.h>
26 #include <isc/string.h>         /* Required for HP/UX (and others?) */
27 #include <isc/task.h>
28 #include <isc/util.h>
29
30 #include <dns/db.h>
31 #include <dns/events.h>
32 #include <dns/lookup.h>
33 #include <dns/rdata.h>
34 #include <dns/rdataset.h>
35 #include <dns/rdatastruct.h>
36 #include <dns/resolver.h>
37 #include <dns/result.h>
38 #include <dns/view.h>
39
40 struct dns_lookup {
41         /* Unlocked. */
42         unsigned int            magic;
43         isc_mem_t *             mctx;
44         isc_mutex_t             lock;
45         dns_rdatatype_t         type;
46         dns_fixedname_t         name;
47         /* Locked by lock. */
48         unsigned int            options;
49         isc_task_t *            task;
50         dns_view_t *            view;
51         dns_lookupevent_t *     event;
52         dns_fetch_t *           fetch;
53         unsigned int            restarts;
54         isc_boolean_t           canceled;
55         dns_rdataset_t          rdataset;
56         dns_rdataset_t          sigrdataset;
57 };
58
59 #define LOOKUP_MAGIC                    ISC_MAGIC('l', 'o', 'o', 'k')
60 #define VALID_LOOKUP(l)                 ISC_MAGIC_VALID((l), LOOKUP_MAGIC)
61
62 #define MAX_RESTARTS 16
63
64 static void lookup_find(dns_lookup_t *lookup, dns_fetchevent_t *event);
65
66 static void
67 fetch_done(isc_task_t *task, isc_event_t *event) {
68         dns_lookup_t *lookup = event->ev_arg;
69         dns_fetchevent_t *fevent;
70
71         UNUSED(task);
72         REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE);
73         REQUIRE(VALID_LOOKUP(lookup));
74         REQUIRE(lookup->task == task);
75         fevent = (dns_fetchevent_t *)event;
76         REQUIRE(fevent->fetch == lookup->fetch);
77
78         lookup_find(lookup, fevent);
79 }
80
81 static inline isc_result_t
82 start_fetch(dns_lookup_t *lookup) {
83         isc_result_t result;
84
85         /*
86          * The caller must be holding the lookup's lock.
87          */
88
89         REQUIRE(lookup->fetch == NULL);
90
91         result = dns_resolver_createfetch(lookup->view->resolver,
92                                           dns_fixedname_name(&lookup->name),
93                                           lookup->type,
94                                           NULL, NULL, NULL, 0,
95                                           lookup->task, fetch_done, lookup,
96                                           &lookup->rdataset,
97                                           &lookup->sigrdataset,
98                                           &lookup->fetch);
99
100         return (result);
101 }
102
103 static isc_result_t
104 build_event(dns_lookup_t *lookup) {
105         dns_name_t *name = NULL;
106         dns_rdataset_t *rdataset = NULL;
107         dns_rdataset_t *sigrdataset = NULL;
108         isc_result_t result;
109
110         name = isc_mem_get(lookup->mctx, sizeof(dns_name_t));
111         if (name == NULL) {
112                 result = ISC_R_NOMEMORY;
113                 goto fail;
114         }
115         dns_name_init(name, NULL);
116         result = dns_name_dup(dns_fixedname_name(&lookup->name),
117                               lookup->mctx, name);
118         if (result != ISC_R_SUCCESS)
119                 goto fail;
120
121         if (dns_rdataset_isassociated(&lookup->rdataset)) {
122                 rdataset = isc_mem_get(lookup->mctx, sizeof(dns_rdataset_t));
123                 if (rdataset == NULL) {
124                         result = ISC_R_NOMEMORY;
125                         goto fail;
126                 }
127                 dns_rdataset_init(rdataset);
128                 dns_rdataset_clone(&lookup->rdataset, rdataset);
129         }
130
131         if (dns_rdataset_isassociated(&lookup->sigrdataset)) {
132                 sigrdataset = isc_mem_get(lookup->mctx,
133                                           sizeof(dns_rdataset_t));
134                 if (sigrdataset == NULL) {
135                         result = ISC_R_NOMEMORY;
136                         goto fail;
137                 }
138                 dns_rdataset_init(sigrdataset);
139                 dns_rdataset_clone(&lookup->sigrdataset, sigrdataset);
140         }
141
142         lookup->event->name = name;
143         lookup->event->rdataset = rdataset;
144         lookup->event->sigrdataset = sigrdataset;
145
146         return (ISC_R_SUCCESS);
147
148  fail:
149         if (name != NULL) {
150                 if (dns_name_dynamic(name))
151                         dns_name_free(name, lookup->mctx);
152                 isc_mem_put(lookup->mctx, name, sizeof(dns_name_t));
153         }
154         if (rdataset != NULL) {
155                 if (dns_rdataset_isassociated(rdataset))
156                         dns_rdataset_disassociate(rdataset);
157                 isc_mem_put(lookup->mctx, rdataset, sizeof(dns_rdataset_t));
158         }
159         return (result);
160 }
161
162 static isc_result_t
163 view_find(dns_lookup_t *lookup, dns_name_t *foundname) {
164         isc_result_t result;
165         dns_name_t *name = dns_fixedname_name(&lookup->name);
166         dns_rdatatype_t type;
167
168         if (lookup->type == dns_rdatatype_rrsig)
169                 type = dns_rdatatype_any;
170         else
171                 type = lookup->type;
172
173         result = dns_view_find(lookup->view, name, type, 0, 0, ISC_FALSE,
174                                &lookup->event->db, &lookup->event->node,
175                                foundname, &lookup->rdataset,
176                                &lookup->sigrdataset);
177         return (result);
178 }
179
180 static void
181 lookup_find(dns_lookup_t *lookup, dns_fetchevent_t *event) {
182         isc_result_t result;
183         isc_boolean_t want_restart;
184         isc_boolean_t send_event;
185         dns_name_t *name, *fname, *prefix;
186         dns_fixedname_t foundname, fixed;
187         dns_rdata_t rdata = DNS_RDATA_INIT;
188         unsigned int nlabels;
189         int order;
190         dns_namereln_t namereln;
191         dns_rdata_cname_t cname;
192         dns_rdata_dname_t dname;
193
194         REQUIRE(VALID_LOOKUP(lookup));
195
196         LOCK(&lookup->lock);
197
198         result = ISC_R_SUCCESS;
199         name = dns_fixedname_name(&lookup->name);
200
201         do {
202                 lookup->restarts++;
203                 want_restart = ISC_FALSE;
204                 send_event = ISC_TRUE;
205
206                 if (event == NULL && !lookup->canceled) {
207                         dns_fixedname_init(&foundname);
208                         fname = dns_fixedname_name(&foundname);
209                         INSIST(!dns_rdataset_isassociated(&lookup->rdataset));
210                         INSIST(!dns_rdataset_isassociated
211                                                 (&lookup->sigrdataset));
212                         /*
213                          * If we have restarted then clear the old node.                                 */
214                         if  (lookup->event->node != NULL) {
215                                 INSIST(lookup->event->db != NULL);
216                                 dns_db_detachnode(lookup->event->db,
217                                                  &lookup->event->node);
218                         }
219                         if (lookup->event->db != NULL)
220                                 dns_db_detach(&lookup->event->db);
221                         result = view_find(lookup, fname);
222                         if (result == ISC_R_NOTFOUND) {
223                                 /*
224                                  * We don't know anything about the name.
225                                  * Launch a fetch.
226                                  */
227                                 if  (lookup->event->node != NULL) {
228                                         INSIST(lookup->event->db != NULL);
229                                         dns_db_detachnode(lookup->event->db,
230                                                          &lookup->event->node);
231                                 }
232                                 if (lookup->event->db != NULL)
233                                         dns_db_detach(&lookup->event->db);
234                                 result = start_fetch(lookup);
235                                 if (result == ISC_R_SUCCESS)
236                                         send_event = ISC_FALSE;
237                                 goto done;
238                         }
239                 } else if (event != NULL) {
240                         result = event->result;
241                         fname = dns_fixedname_name(&event->foundname);
242                         dns_resolver_destroyfetch(&lookup->fetch);
243                         INSIST(event->rdataset == &lookup->rdataset);
244                         INSIST(event->sigrdataset == &lookup->sigrdataset);
245                 } else
246                         fname = NULL;   /* Silence compiler warning. */
247
248                 /*
249                  * If we've been canceled, forget about the result.
250                  */
251                 if (lookup->canceled)
252                         result = ISC_R_CANCELED;
253
254                 switch (result) {
255                 case ISC_R_SUCCESS:
256                         result = build_event(lookup);
257                         if (event == NULL)
258                                 break;
259                         if (event->db != NULL)
260                                 dns_db_attach(event->db, &lookup->event->db);
261                         if (event->node != NULL)
262                                 dns_db_attachnode(lookup->event->db,
263                                                   event->node,
264                                                   &lookup->event->node);
265                         break;
266                 case DNS_R_CNAME:
267                         /*
268                          * Copy the CNAME's target into the lookup's
269                          * query name and start over.
270                          */
271                         result = dns_rdataset_first(&lookup->rdataset);
272                         if (result != ISC_R_SUCCESS)
273                                 break;
274                         dns_rdataset_current(&lookup->rdataset, &rdata);
275                         result = dns_rdata_tostruct(&rdata, &cname, NULL);
276                         dns_rdata_reset(&rdata);
277                         if (result != ISC_R_SUCCESS)
278                                 break;
279                         result = dns_name_copy(&cname.cname, name, NULL);
280                         dns_rdata_freestruct(&cname);
281                         if (result == ISC_R_SUCCESS) {
282                                 want_restart = ISC_TRUE;
283                                 send_event = ISC_FALSE;
284                         }
285                         break;
286                 case DNS_R_DNAME:
287                         namereln = dns_name_fullcompare(name, fname, &order,
288                                                         &nlabels);
289                         INSIST(namereln == dns_namereln_subdomain);
290                         /*
291                          * Get the target name of the DNAME.
292                          */
293                         result = dns_rdataset_first(&lookup->rdataset);
294                         if (result != ISC_R_SUCCESS)
295                                 break;
296                         dns_rdataset_current(&lookup->rdataset, &rdata);
297                         result = dns_rdata_tostruct(&rdata, &dname, NULL);
298                         dns_rdata_reset(&rdata);
299                         if (result != ISC_R_SUCCESS)
300                                 break;
301                         /*
302                          * Construct the new query name and start over.
303                          */
304                         dns_fixedname_init(&fixed);
305                         prefix = dns_fixedname_name(&fixed);
306                         dns_name_split(name, nlabels, prefix, NULL);
307                         result = dns_name_concatenate(prefix, &dname.dname,
308                                                       name, NULL);
309                         dns_rdata_freestruct(&dname);
310                         if (result == ISC_R_SUCCESS) {
311                                 want_restart = ISC_TRUE;
312                                 send_event = ISC_FALSE;
313                         }
314                         break;
315                 default:
316                         send_event = ISC_TRUE;
317                 }
318
319                 if (dns_rdataset_isassociated(&lookup->rdataset))
320                         dns_rdataset_disassociate(&lookup->rdataset);
321                 if (dns_rdataset_isassociated(&lookup->sigrdataset))
322                         dns_rdataset_disassociate(&lookup->sigrdataset);
323
324         done:
325                 if (event != NULL) {
326                         if (event->node != NULL)
327                                 dns_db_detachnode(event->db, &event->node);
328                         if (event->db != NULL)
329                                 dns_db_detach(&event->db);
330                         isc_event_free(ISC_EVENT_PTR(&event));
331                 }
332
333                 /*
334                  * Limit the number of restarts.
335                  */
336                 if (want_restart && lookup->restarts == MAX_RESTARTS) {
337                         want_restart = ISC_FALSE;
338                         result = ISC_R_QUOTA;
339                         send_event = ISC_TRUE;
340                 }
341
342         } while (want_restart);
343
344         if (send_event) {
345                 lookup->event->result = result;
346                 lookup->event->ev_sender = lookup;
347                 isc_task_sendanddetach(&lookup->task,
348                                        (isc_event_t **)&lookup->event);
349                 dns_view_detach(&lookup->view);
350         }
351
352         UNLOCK(&lookup->lock);
353 }
354
355 static void
356 levent_destroy(isc_event_t *event) {
357         dns_lookupevent_t *levent;
358         isc_mem_t *mctx;
359  
360         REQUIRE(event->ev_type == DNS_EVENT_LOOKUPDONE);
361         mctx = event->ev_destroy_arg;
362         levent = (dns_lookupevent_t *)event;
363
364         if (levent->name != NULL) {
365                 if (dns_name_dynamic(levent->name))
366                         dns_name_free(levent->name, mctx);
367                 isc_mem_put(mctx, levent->name, sizeof(dns_name_t));
368         }
369         if (levent->rdataset != NULL) {
370                 dns_rdataset_disassociate(levent->rdataset);
371                 isc_mem_put(mctx, levent->rdataset, sizeof(dns_rdataset_t));
372         }
373         if (levent->sigrdataset != NULL) {
374                 dns_rdataset_disassociate(levent->sigrdataset);
375                 isc_mem_put(mctx, levent->sigrdataset, sizeof(dns_rdataset_t));
376         }
377         if (levent->node != NULL)
378                 dns_db_detachnode(levent->db, &levent->node);
379         if (levent->db != NULL)
380                 dns_db_detach(&levent->db);
381         isc_mem_put(mctx, event, event->ev_size);
382 }
383
384 isc_result_t
385 dns_lookup_create(isc_mem_t *mctx, dns_name_t *name, dns_rdatatype_t type,
386                   dns_view_t *view, unsigned int options, isc_task_t *task,
387                   isc_taskaction_t action, void *arg, dns_lookup_t **lookupp)
388 {
389         isc_result_t result;
390         dns_lookup_t *lookup;
391         isc_event_t *ievent;
392
393         lookup = isc_mem_get(mctx, sizeof(*lookup));
394         if (lookup == NULL)
395                 return (ISC_R_NOMEMORY);
396         lookup->mctx = mctx;
397         lookup->options = options;
398
399         ievent = isc_event_allocate(mctx, lookup, DNS_EVENT_LOOKUPDONE,
400                                     action, arg, sizeof(*lookup->event));
401         if (ievent == NULL) {
402                 result = ISC_R_NOMEMORY;
403                 goto cleanup_lookup;
404         }
405         lookup->event = (dns_lookupevent_t *)ievent;
406         lookup->event->ev_destroy = levent_destroy;
407         lookup->event->ev_destroy_arg = mctx;
408         lookup->event->result = ISC_R_FAILURE;
409         lookup->event->name = NULL;
410         lookup->event->rdataset = NULL;
411         lookup->event->sigrdataset = NULL;
412         lookup->event->db = NULL;
413         lookup->event->node = NULL;
414
415         lookup->task = NULL;
416         isc_task_attach(task, &lookup->task);
417
418         result = isc_mutex_init(&lookup->lock);
419         if (result != ISC_R_SUCCESS)
420                 goto cleanup_event;
421
422         dns_fixedname_init(&lookup->name);
423
424         result = dns_name_copy(name, dns_fixedname_name(&lookup->name), NULL);
425         if (result != ISC_R_SUCCESS)
426                 goto cleanup_lock;
427
428         lookup->type = type;
429         lookup->view = NULL;
430         dns_view_attach(view, &lookup->view);
431         lookup->fetch = NULL;
432         lookup->restarts = 0;
433         lookup->canceled = ISC_FALSE;
434         dns_rdataset_init(&lookup->rdataset);
435         dns_rdataset_init(&lookup->sigrdataset);
436         lookup->magic = LOOKUP_MAGIC;
437
438         *lookupp = lookup;
439
440         lookup_find(lookup, NULL);
441
442         return (ISC_R_SUCCESS);
443
444  cleanup_lock:
445         DESTROYLOCK(&lookup->lock);
446
447  cleanup_event:
448         ievent = (isc_event_t *)lookup->event;
449         isc_event_free(&ievent);
450         lookup->event = NULL;
451
452         isc_task_detach(&lookup->task);
453
454  cleanup_lookup:
455         isc_mem_put(mctx, lookup, sizeof(*lookup));
456
457         return (result);
458 }
459
460 void
461 dns_lookup_cancel(dns_lookup_t *lookup) {
462         REQUIRE(VALID_LOOKUP(lookup));
463
464         LOCK(&lookup->lock);
465
466         if (!lookup->canceled) {
467                 lookup->canceled = ISC_TRUE;
468                 if (lookup->fetch != NULL) {
469                         INSIST(lookup->view != NULL);
470                         dns_resolver_cancelfetch(lookup->fetch);
471                 }
472         }
473
474         UNLOCK(&lookup->lock);
475 }
476
477 void
478 dns_lookup_destroy(dns_lookup_t **lookupp) {
479         dns_lookup_t *lookup;
480
481         REQUIRE(lookupp != NULL);
482         lookup = *lookupp;
483         REQUIRE(VALID_LOOKUP(lookup));
484         REQUIRE(lookup->event == NULL);
485         REQUIRE(lookup->task == NULL);
486         REQUIRE(lookup->view == NULL);
487         if (dns_rdataset_isassociated(&lookup->rdataset))
488                 dns_rdataset_disassociate(&lookup->rdataset);
489         if (dns_rdataset_isassociated(&lookup->sigrdataset))
490                 dns_rdataset_disassociate(&lookup->sigrdataset);
491
492         DESTROYLOCK(&lookup->lock);
493         lookup->magic = 0;
494         isc_mem_put(lookup->mctx, lookup, sizeof(*lookup));
495
496         *lookupp = NULL;
497 }