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