lookup.c revision 135446
1/*
2 * Copyright (C) 2004  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.9.12.5 2004/04/15 02:10:40 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
38struct 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
62static void lookup_find(dns_lookup_t *lookup, dns_fetchevent_t *event);
63
64static void
65fetch_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
79static inline isc_result_t
80start_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
101static isc_result_t
102build_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
165static isc_result_t
166view_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_rrsig)
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
183static void
184lookup_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;
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);
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			dns_name_split(name, nlabels, prefix, NULL);
298			result = dns_name_concatenate(prefix, &dname.dname,
299						      name, NULL);
300			dns_rdata_freestruct(&dname);
301			if (result == ISC_R_SUCCESS)
302				want_restart = ISC_TRUE;
303			break;
304		default:
305			send_event = ISC_TRUE;
306		}
307
308		if (dns_rdataset_isassociated(&lookup->rdataset))
309			dns_rdataset_disassociate(&lookup->rdataset);
310		if (dns_rdataset_isassociated(&lookup->sigrdataset))
311			dns_rdataset_disassociate(&lookup->sigrdataset);
312
313	done:
314		if (event != NULL) {
315			if (event->node != NULL)
316				dns_db_detachnode(event->db, &event->node);
317			if (event->db != NULL)
318				dns_db_detach(&event->db);
319			isc_event_free(ISC_EVENT_PTR(&event));
320		}
321
322		/*
323		 * Limit the number of restarts.
324		 */
325		if (want_restart && lookup->restarts == MAX_RESTARTS) {
326			want_restart = ISC_FALSE;
327			result = ISC_R_QUOTA;
328			send_event = ISC_TRUE;
329		}
330
331	} while (want_restart);
332
333	if (send_event) {
334		lookup->event->result = result;
335		lookup->event->ev_sender = lookup;
336		isc_task_sendanddetach(&lookup->task,
337				       (isc_event_t **)&lookup->event);
338		dns_view_detach(&lookup->view);
339	}
340
341	UNLOCK(&lookup->lock);
342}
343
344static void
345levent_destroy(isc_event_t *event) {
346	dns_lookupevent_t *levent;
347	isc_mem_t *mctx;
348
349	REQUIRE(event->ev_type == DNS_EVENT_LOOKUPDONE);
350	mctx = event->ev_destroy_arg;
351	levent = (dns_lookupevent_t *)event;
352
353	if (levent->name != NULL) {
354		if (dns_name_dynamic(levent->name))
355			dns_name_free(levent->name, mctx);
356		isc_mem_put(mctx, levent->name, sizeof(dns_name_t));
357	}
358	if (levent->rdataset != NULL) {
359		dns_rdataset_disassociate(levent->rdataset);
360		isc_mem_put(mctx, levent->rdataset, sizeof(dns_rdataset_t));
361	}
362	if (levent->sigrdataset != NULL) {
363		dns_rdataset_disassociate(levent->sigrdataset);
364		isc_mem_put(mctx, levent->sigrdataset, sizeof(dns_rdataset_t));
365	}
366	if (levent->node != NULL)
367		dns_db_detachnode(levent->db, &levent->node);
368	if (levent->db != NULL)
369		dns_db_detach(&levent->db);
370	isc_mem_put(mctx, event, event->ev_size);
371}
372
373
374isc_result_t
375dns_lookup_create(isc_mem_t *mctx, dns_name_t *name, dns_rdatatype_t type,
376		  dns_view_t *view, unsigned int options, isc_task_t *task,
377		  isc_taskaction_t action, void *arg, dns_lookup_t **lookupp)
378{
379	isc_result_t result;
380	dns_lookup_t *lookup;
381	isc_event_t *ievent;
382
383	lookup = isc_mem_get(mctx, sizeof(*lookup));
384	if (lookup == NULL)
385		return (ISC_R_NOMEMORY);
386	lookup->mctx = mctx;
387	lookup->options = options;
388
389	ievent = isc_event_allocate(mctx, lookup, DNS_EVENT_LOOKUPDONE,
390				    action, arg, sizeof(*lookup->event));
391	if (ievent == NULL) {
392		result = ISC_R_NOMEMORY;
393		goto cleanup_lookup;
394	}
395	lookup->event = (dns_lookupevent_t *)ievent;
396	lookup->event->ev_destroy = levent_destroy;
397	lookup->event->ev_destroy_arg = mctx;
398	lookup->event->result = ISC_R_FAILURE;
399	lookup->event->name = NULL;
400	lookup->event->rdataset = NULL;
401	lookup->event->sigrdataset = NULL;
402	lookup->event->db = NULL;
403	lookup->event->node = NULL;
404
405	lookup->task = NULL;
406	isc_task_attach(task, &lookup->task);
407
408	result = isc_mutex_init(&lookup->lock);
409	if (result != ISC_R_SUCCESS)
410		goto cleanup_event;
411
412	dns_fixedname_init(&lookup->name);
413
414	result = dns_name_copy(name, dns_fixedname_name(&lookup->name), NULL);
415	if (result != ISC_R_SUCCESS)
416		goto cleanup_lock;
417
418	lookup->type = type;
419	lookup->view = NULL;
420	dns_view_attach(view, &lookup->view);
421	lookup->fetch = NULL;
422	lookup->restarts = 0;
423	lookup->canceled = ISC_FALSE;
424	dns_rdataset_init(&lookup->rdataset);
425	dns_rdataset_init(&lookup->sigrdataset);
426	lookup->magic = LOOKUP_MAGIC;
427
428	*lookupp = lookup;
429
430	lookup_find(lookup, NULL);
431
432	return (ISC_R_SUCCESS);
433
434 cleanup_lock:
435	DESTROYLOCK(&lookup->lock);
436
437 cleanup_event:
438	ievent = (isc_event_t *)lookup->event;
439	isc_event_free(&ievent);
440	lookup->event = NULL;
441
442	isc_task_detach(&lookup->task);
443
444 cleanup_lookup:
445	isc_mem_put(mctx, lookup, sizeof(*lookup));
446
447	return (result);
448}
449
450void
451dns_lookup_cancel(dns_lookup_t *lookup) {
452	REQUIRE(VALID_LOOKUP(lookup));
453
454	LOCK(&lookup->lock);
455
456	if (!lookup->canceled) {
457		lookup->canceled = ISC_TRUE;
458		if (lookup->fetch != NULL) {
459			INSIST(lookup->view != NULL);
460			dns_resolver_cancelfetch(lookup->fetch);
461		}
462	}
463
464	UNLOCK(&lookup->lock);
465}
466
467void
468dns_lookup_destroy(dns_lookup_t **lookupp) {
469	dns_lookup_t *lookup;
470
471	REQUIRE(lookupp != NULL);
472	lookup = *lookupp;
473	REQUIRE(VALID_LOOKUP(lookup));
474	REQUIRE(lookup->event == NULL);
475	REQUIRE(lookup->task == NULL);
476	REQUIRE(lookup->view == NULL);
477	if (dns_rdataset_isassociated(&lookup->rdataset))
478		dns_rdataset_disassociate(&lookup->rdataset);
479	if (dns_rdataset_isassociated(&lookup->sigrdataset))
480		dns_rdataset_disassociate(&lookup->sigrdataset);
481
482	DESTROYLOCK(&lookup->lock);
483	lookup->magic = 0;
484	isc_mem_put(lookup->mctx, lookup, sizeof(*lookup));
485
486	*lookupp = NULL;
487}
488