extern int errno considered harmful.
[dragonfly.git] / contrib / bind-9.2.4rc7 / lib / dns / lookup.c
1 /*
2  * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2000, 2001  Internet Software Consortium.
4  *
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.
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.9.2.3 2004/04/15 02:16:27 marka Exp $ */
19
20 #include <config.h>
21
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>
27
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>
37
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;
55 };
56
57 #define LOOKUP_MAGIC                    ISC_MAGIC('l', 'o', 'o', 'k')
58 #define VALID_LOOKUP(l)                 ISC_MAGIC_VALID((l), LOOKUP_MAGIC)
59
60 #define MAX_RESTARTS 16
61
62 static void lookup_find(dns_lookup_t *lookup, dns_fetchevent_t *event);
63
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;
68
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);
75
76         lookup_find(lookup, fevent);
77 }
78
79 static inline isc_result_t
80 start_fetch(dns_lookup_t *lookup) {
81         isc_result_t result;
82
83         /*
84          * The caller must be holding the lookup's lock.
85          */
86
87         REQUIRE(lookup->fetch == NULL);
88
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);
97
98         return (result);
99 }
100
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;
107
108         name = isc_mem_get(lookup->mctx, sizeof(dns_name_t));
109         if (name == NULL) {
110                 result = ISC_R_NOMEMORY;
111                 goto fail;
112         }
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;
118
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;
124                 }
125                 dns_rdataset_init(rdataset);
126                 dns_rdataset_clone(&lookup->rdataset, rdataset);
127         }
128
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;
135                 }
136                 dns_rdataset_init(sigrdataset);
137                 dns_rdataset_clone(&lookup->sigrdataset, sigrdataset);
138         }
139
140         lookup->event->name = name;
141         lookup->event->rdataset = rdataset;
142         lookup->event->sigrdataset = sigrdataset;
143
144         return (ISC_R_SUCCESS);
145
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));
151         }
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));
156         }
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));
161         }
162         return (result);
163 }
164
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;
170
171         if (lookup->type == dns_rdatatype_sig)
172                 type = dns_rdatatype_any;
173         else
174                 type = lookup->type;
175
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);
181 }
182
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;
196
197         REQUIRE(VALID_LOOKUP(lookup));
198
199         LOCK(&lookup->lock);
200
201         result = ISC_R_SUCCESS;
202         name = dns_fixedname_name(&lookup->name);
203
204         do {
205                 lookup->restarts++;
206                 want_restart = ISC_FALSE;
207
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) {
216                                 /*
217                                  * We don't know anything about the name.
218                                  * Launch a fetch.
219                                  */
220                                 if  (lookup->event->node != NULL) {
221                                         INSIST(lookup->event->db != NULL);
222                                         dns_db_detachnode(lookup->event->db,
223                                                          &lookup->event->node);
224                                 }
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;
231                         }
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);
238                 }
239
240                 /*
241                  * If we've been canceled, forget about the result.
242                  */
243                 if (lookup->canceled)
244                         result = ISC_R_CANCELED;
245
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:
260                         /*
261                          * Copy the CNAME's target into the lookup's
262                          * query name and start over.
263                          */
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);
281                         /*
282                          * Get the target name of the DNAME.
283                          */
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;
292                         /*
293                          * Construct the new query name and start over.
294                          */
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;
302                         }
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;
311                 }
312
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);
317
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));
325                 }
326
327                 /*
328                  * Limit the number of restarts.
329                  */
330                 if (want_restart && lookup->restarts == MAX_RESTARTS) {
331                         want_restart = ISC_FALSE;
332                         result = ISC_R_QUOTA;
333                         send_event = ISC_TRUE;
334                 }
335
336         } while (want_restart);
337
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);
344         }
345
346         UNLOCK(&lookup->lock);
347 }
348
349 static void
350 levent_destroy(isc_event_t *event) {
351         dns_lookupevent_t *levent;
352         isc_mem_t *mctx;
353  
354         REQUIRE(event->ev_type == DNS_EVENT_LOOKUPDONE);
355         mctx = event->ev_destroy_arg;
356         levent = (dns_lookupevent_t *)event;
357
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));
362         }
363         if (levent->rdataset != NULL) {
364                 dns_rdataset_disassociate(levent->rdataset);
365                 isc_mem_put(mctx, levent->rdataset, sizeof(dns_rdataset_t));
366         }
367         if (levent->sigrdataset != NULL) {
368                 dns_rdataset_disassociate(levent->sigrdataset);
369                 isc_mem_put(mctx, levent->sigrdataset, sizeof(dns_rdataset_t));
370         }
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);
376 }
377
378
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)
383 {
384         isc_result_t result;
385         dns_lookup_t *lookup;
386         isc_event_t *ievent;
387
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;
393
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;
399         }
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;
409
410         lookup->task = NULL;
411         isc_task_attach(task, &lookup->task);
412
413         result = isc_mutex_init(&lookup->lock);
414         if (result != ISC_R_SUCCESS)
415                 goto cleanup_event;
416
417         dns_fixedname_init(&lookup->name);
418
419         result = dns_name_copy(name, dns_fixedname_name(&lookup->name), NULL);
420         if (result != ISC_R_SUCCESS)
421                 goto cleanup_lock;
422
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;
432
433         *lookupp = lookup;
434
435         lookup_find(lookup, NULL);
436
437         return (ISC_R_SUCCESS);
438
439  cleanup_lock:
440         DESTROYLOCK(&lookup->lock);
441
442  cleanup_event:
443         ievent = (isc_event_t *)lookup->event;
444         isc_event_free(&ievent);
445         lookup->event = NULL;
446
447         isc_task_detach(&lookup->task);
448
449  cleanup_lookup:
450         isc_mem_put(mctx, lookup, sizeof *lookup);
451
452         return (result);
453 }
454
455 void
456 dns_lookup_cancel(dns_lookup_t *lookup) {
457         REQUIRE(VALID_LOOKUP(lookup));
458
459         LOCK(&lookup->lock);
460
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);
466                 }
467         }
468
469         UNLOCK(&lookup->lock);
470 }
471
472 void
473 dns_lookup_destroy(dns_lookup_t **lookupp) {
474         dns_lookup_t *lookup;
475
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);
486
487         DESTROYLOCK(&lookup->lock);
488         lookup->magic = 0;
489         isc_mem_put(lookup->mctx, lookup, sizeof *lookup);
490
491         *lookupp = NULL;
492 }