]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bind9/lib/dns/lookup.c
This commit was generated by cvs2svn to compensate for changes in r171366,
[FreeBSD/FreeBSD.git] / contrib / bind9 / lib / dns / lookup.c
1 /*
2  * Copyright (C) 2004, 2005  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2000, 2001, 2003  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.14.18.4 2005/11/30 03:44:39 marka 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 = ISC_FALSE;
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
205                 if (event == NULL && !lookup->canceled) {
206                         dns_fixedname_init(&foundname);
207                         fname = dns_fixedname_name(&foundname);
208                         INSIST(!dns_rdataset_isassociated(&lookup->rdataset));
209                         INSIST(!dns_rdataset_isassociated
210                                                 (&lookup->sigrdataset));
211                         result = view_find(lookup, fname);
212                         if (result == ISC_R_NOTFOUND) {
213                                 /*
214                                  * We don't know anything about the name.
215                                  * Launch a fetch.
216                                  */
217                                 if  (lookup->event->node != NULL) {
218                                         INSIST(lookup->event->db != NULL);
219                                         dns_db_detachnode(lookup->event->db,
220                                                          &lookup->event->node);
221                                 }
222                                 if (lookup->event->db != NULL)
223                                         dns_db_detach(&lookup->event->db);
224                                 result = start_fetch(lookup);
225                                 if (result != ISC_R_SUCCESS)
226                                         send_event = ISC_TRUE;
227                                 goto done;
228                         }
229                 } else if (event != NULL) {
230                         result = event->result;
231                         fname = dns_fixedname_name(&event->foundname);
232                         dns_resolver_destroyfetch(&lookup->fetch);
233                         INSIST(event->rdataset == &lookup->rdataset);
234                         INSIST(event->sigrdataset == &lookup->sigrdataset);
235                 } else
236                         fname = NULL;   /* Silence compiler warning. */
237
238                 /*
239                  * If we've been canceled, forget about the result.
240                  */
241                 if (lookup->canceled)
242                         result = ISC_R_CANCELED;
243
244                 switch (result) {
245                 case ISC_R_SUCCESS:
246                         result = build_event(lookup);
247                         send_event = ISC_TRUE;
248                         if (event == NULL)
249                                 break;
250                         if (event->db != NULL)
251                                 dns_db_attach(event->db, &lookup->event->db);
252                         if (event->node != NULL)
253                                 dns_db_attachnode(lookup->event->db,
254                                                   event->node,
255                                                   &lookup->event->node);
256                         break;
257                 case DNS_R_CNAME:
258                         /*
259                          * Copy the CNAME's target into the lookup's
260                          * query name and start over.
261                          */
262                         result = dns_rdataset_first(&lookup->rdataset);
263                         if (result != ISC_R_SUCCESS)
264                                 break;
265                         dns_rdataset_current(&lookup->rdataset, &rdata);
266                         result = dns_rdata_tostruct(&rdata, &cname, NULL);
267                         dns_rdata_reset(&rdata);
268                         if (result != ISC_R_SUCCESS)
269                                 break;
270                         result = dns_name_copy(&cname.cname, name, NULL);
271                         dns_rdata_freestruct(&cname);
272                         if (result == ISC_R_SUCCESS)
273                                 want_restart = ISC_TRUE;
274                         break;
275                 case DNS_R_DNAME:
276                         namereln = dns_name_fullcompare(name, fname, &order,
277                                                         &nlabels);
278                         INSIST(namereln == dns_namereln_subdomain);
279                         /*
280                          * Get the target name of the DNAME.
281                          */
282                         result = dns_rdataset_first(&lookup->rdataset);
283                         if (result != ISC_R_SUCCESS)
284                                 break;
285                         dns_rdataset_current(&lookup->rdataset, &rdata);
286                         result = dns_rdata_tostruct(&rdata, &dname, NULL);
287                         dns_rdata_reset(&rdata);
288                         if (result != ISC_R_SUCCESS)
289                                 break;
290                         /*
291                          * Construct the new query name and start over.
292                          */
293                         dns_fixedname_init(&fixed);
294                         prefix = dns_fixedname_name(&fixed);
295                         dns_name_split(name, nlabels, prefix, NULL);
296                         result = dns_name_concatenate(prefix, &dname.dname,
297                                                       name, NULL);
298                         dns_rdata_freestruct(&dname);
299                         if (result == ISC_R_SUCCESS)
300                                 want_restart = ISC_TRUE;
301                         break;
302                 default:
303                         send_event = ISC_TRUE;
304                 }
305
306                 if (dns_rdataset_isassociated(&lookup->rdataset))
307                         dns_rdataset_disassociate(&lookup->rdataset);
308                 if (dns_rdataset_isassociated(&lookup->sigrdataset))
309                         dns_rdataset_disassociate(&lookup->sigrdataset);
310
311         done:
312                 if (event != NULL) {
313                         if (event->node != NULL)
314                                 dns_db_detachnode(event->db, &event->node);
315                         if (event->db != NULL)
316                                 dns_db_detach(&event->db);
317                         isc_event_free(ISC_EVENT_PTR(&event));
318                 }
319
320                 /*
321                  * Limit the number of restarts.
322                  */
323                 if (want_restart && lookup->restarts == MAX_RESTARTS) {
324                         want_restart = ISC_FALSE;
325                         result = ISC_R_QUOTA;
326                         send_event = ISC_TRUE;
327                 }
328
329         } while (want_restart);
330
331         if (send_event) {
332                 lookup->event->result = result;
333                 lookup->event->ev_sender = lookup;
334                 isc_task_sendanddetach(&lookup->task,
335                                        (isc_event_t **)&lookup->event);
336                 dns_view_detach(&lookup->view);
337         }
338
339         UNLOCK(&lookup->lock);
340 }
341
342 static void
343 levent_destroy(isc_event_t *event) {
344         dns_lookupevent_t *levent;
345         isc_mem_t *mctx;
346  
347         REQUIRE(event->ev_type == DNS_EVENT_LOOKUPDONE);
348         mctx = event->ev_destroy_arg;
349         levent = (dns_lookupevent_t *)event;
350
351         if (levent->name != NULL) {
352                 if (dns_name_dynamic(levent->name))
353                         dns_name_free(levent->name, mctx);
354                 isc_mem_put(mctx, levent->name, sizeof(dns_name_t));
355         }
356         if (levent->rdataset != NULL) {
357                 dns_rdataset_disassociate(levent->rdataset);
358                 isc_mem_put(mctx, levent->rdataset, sizeof(dns_rdataset_t));
359         }
360         if (levent->sigrdataset != NULL) {
361                 dns_rdataset_disassociate(levent->sigrdataset);
362                 isc_mem_put(mctx, levent->sigrdataset, sizeof(dns_rdataset_t));
363         }
364         if (levent->node != NULL)
365                 dns_db_detachnode(levent->db, &levent->node);
366         if (levent->db != NULL)
367                 dns_db_detach(&levent->db);
368         isc_mem_put(mctx, event, event->ev_size);
369 }
370
371
372 isc_result_t
373 dns_lookup_create(isc_mem_t *mctx, dns_name_t *name, dns_rdatatype_t type,
374                   dns_view_t *view, unsigned int options, isc_task_t *task,
375                   isc_taskaction_t action, void *arg, dns_lookup_t **lookupp)
376 {
377         isc_result_t result;
378         dns_lookup_t *lookup;
379         isc_event_t *ievent;
380
381         lookup = isc_mem_get(mctx, sizeof(*lookup));
382         if (lookup == NULL)
383                 return (ISC_R_NOMEMORY);
384         lookup->mctx = mctx;
385         lookup->options = options;
386
387         ievent = isc_event_allocate(mctx, lookup, DNS_EVENT_LOOKUPDONE,
388                                     action, arg, sizeof(*lookup->event));
389         if (ievent == NULL) {
390                 result = ISC_R_NOMEMORY;
391                 goto cleanup_lookup;
392         }
393         lookup->event = (dns_lookupevent_t *)ievent;
394         lookup->event->ev_destroy = levent_destroy;
395         lookup->event->ev_destroy_arg = mctx;
396         lookup->event->result = ISC_R_FAILURE;
397         lookup->event->name = NULL;
398         lookup->event->rdataset = NULL;
399         lookup->event->sigrdataset = NULL;
400         lookup->event->db = NULL;
401         lookup->event->node = NULL;
402
403         lookup->task = NULL;
404         isc_task_attach(task, &lookup->task);
405
406         result = isc_mutex_init(&lookup->lock);
407         if (result != ISC_R_SUCCESS)
408                 goto cleanup_event;
409
410         dns_fixedname_init(&lookup->name);
411
412         result = dns_name_copy(name, dns_fixedname_name(&lookup->name), NULL);
413         if (result != ISC_R_SUCCESS)
414                 goto cleanup_lock;
415
416         lookup->type = type;
417         lookup->view = NULL;
418         dns_view_attach(view, &lookup->view);
419         lookup->fetch = NULL;
420         lookup->restarts = 0;
421         lookup->canceled = ISC_FALSE;
422         dns_rdataset_init(&lookup->rdataset);
423         dns_rdataset_init(&lookup->sigrdataset);
424         lookup->magic = LOOKUP_MAGIC;
425
426         *lookupp = lookup;
427
428         lookup_find(lookup, NULL);
429
430         return (ISC_R_SUCCESS);
431
432  cleanup_lock:
433         DESTROYLOCK(&lookup->lock);
434
435  cleanup_event:
436         ievent = (isc_event_t *)lookup->event;
437         isc_event_free(&ievent);
438         lookup->event = NULL;
439
440         isc_task_detach(&lookup->task);
441
442  cleanup_lookup:
443         isc_mem_put(mctx, lookup, sizeof(*lookup));
444
445         return (result);
446 }
447
448 void
449 dns_lookup_cancel(dns_lookup_t *lookup) {
450         REQUIRE(VALID_LOOKUP(lookup));
451
452         LOCK(&lookup->lock);
453
454         if (!lookup->canceled) {
455                 lookup->canceled = ISC_TRUE;
456                 if (lookup->fetch != NULL) {
457                         INSIST(lookup->view != NULL);
458                         dns_resolver_cancelfetch(lookup->fetch);
459                 }
460         }
461
462         UNLOCK(&lookup->lock);
463 }
464
465 void
466 dns_lookup_destroy(dns_lookup_t **lookupp) {
467         dns_lookup_t *lookup;
468
469         REQUIRE(lookupp != NULL);
470         lookup = *lookupp;
471         REQUIRE(VALID_LOOKUP(lookup));
472         REQUIRE(lookup->event == NULL);
473         REQUIRE(lookup->task == NULL);
474         REQUIRE(lookup->view == NULL);
475         if (dns_rdataset_isassociated(&lookup->rdataset))
476                 dns_rdataset_disassociate(&lookup->rdataset);
477         if (dns_rdataset_isassociated(&lookup->sigrdataset))
478                 dns_rdataset_disassociate(&lookup->sigrdataset);
479
480         DESTROYLOCK(&lookup->lock);
481         lookup->magic = 0;
482         isc_mem_put(lookup->mctx, lookup, sizeof(*lookup));
483
484         *lookupp = NULL;
485 }