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