xref: /minix3/external/bsd/bind/dist/bin/named/lwdgabn.c (revision 00b67f09dd46474d133c95011a48590a8e8f94c7)
1 /*	$NetBSD: lwdgabn.c,v 1.4 2014/12/10 04:37:51 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2004-2007, 2009  Internet Systems Consortium, Inc. ("ISC")
5  * Copyright (C) 2000, 2001  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: lwdgabn.c,v 1.24 2009/09/02 23:48:01 tbox Exp  */
21 
22 /*! \file */
23 
24 #include <config.h>
25 
26 #include <stdlib.h>
27 
28 #include <isc/netaddr.h>
29 #include <isc/sockaddr.h>
30 #include <isc/socket.h>
31 #include <isc/string.h>		/* Required for HP/UX (and others?) */
32 #include <isc/util.h>
33 
34 #include <dns/adb.h>
35 #include <dns/events.h>
36 #include <dns/result.h>
37 
38 #include <named/types.h>
39 #include <named/lwaddr.h>
40 #include <named/lwdclient.h>
41 #include <named/lwresd.h>
42 #include <named/lwsearch.h>
43 #include <named/sortlist.h>
44 
45 #define NEED_V4(c)	((((c)->find_wanted & LWRES_ADDRTYPE_V4) != 0) \
46 			 && ((c)->v4find == NULL))
47 #define NEED_V6(c)	((((c)->find_wanted & LWRES_ADDRTYPE_V6) != 0) \
48 			 && ((c)->v6find == NULL))
49 
50 static isc_result_t start_find(ns_lwdclient_t *);
51 static void restart_find(ns_lwdclient_t *);
52 static void init_gabn(ns_lwdclient_t *);
53 
54 /*%
55  * Destroy any finds.  This can be used to "start over from scratch" and
56  * should only be called when events are _not_ being generated by the finds.
57  */
58 static void
cleanup_gabn(ns_lwdclient_t * client)59 cleanup_gabn(ns_lwdclient_t *client) {
60 	ns_lwdclient_log(50, "cleaning up client %p", client);
61 
62 	if (client->v6find != NULL) {
63 		if (client->v6find == client->v4find)
64 			client->v6find = NULL;
65 		else
66 			dns_adb_destroyfind(&client->v6find);
67 	}
68 	if (client->v4find != NULL)
69 		dns_adb_destroyfind(&client->v4find);
70 }
71 
72 static void
setup_addresses(ns_lwdclient_t * client,dns_adbfind_t * find,unsigned int at)73 setup_addresses(ns_lwdclient_t *client, dns_adbfind_t *find, unsigned int at) {
74 	dns_adbaddrinfo_t *ai;
75 	lwres_addr_t *addr;
76 	int af;
77 	const struct sockaddr *sa;
78 	isc_result_t result;
79 
80 	if (at == DNS_ADBFIND_INET)
81 		af = AF_INET;
82 	else
83 		af = AF_INET6;
84 
85 	ai = ISC_LIST_HEAD(find->list);
86 	while (ai != NULL && client->gabn.naddrs < LWRES_MAX_ADDRS) {
87 		sa = &ai->sockaddr.type.sa;
88 		if (sa->sa_family != af)
89 			goto next;
90 
91 		addr = &client->addrs[client->gabn.naddrs];
92 
93 		result = lwaddr_lwresaddr_fromsockaddr(addr, &ai->sockaddr);
94 		if (result != ISC_R_SUCCESS)
95 			goto next;
96 
97 		ns_lwdclient_log(50, "adding address %p, family %d, length %d",
98 				 addr->address, addr->family, addr->length);
99 
100 		client->gabn.naddrs++;
101 		REQUIRE(!LWRES_LINK_LINKED(addr, link));
102 		LWRES_LIST_APPEND(client->gabn.addrs, addr, link);
103 
104 	next:
105 		ai = ISC_LIST_NEXT(ai, publink);
106 	}
107 }
108 
109 typedef struct {
110 	isc_netaddr_t address;
111 	int rank;
112 } rankedaddress;
113 
114 static int
addr_compare(const void * av,const void * bv)115 addr_compare(const void *av, const void *bv) {
116 	const rankedaddress *a = (const rankedaddress *) av;
117 	const rankedaddress *b = (const rankedaddress *) bv;
118 	return (a->rank - b->rank);
119 }
120 
121 static void
sort_addresses(ns_lwdclient_t * client)122 sort_addresses(ns_lwdclient_t *client) {
123 	unsigned int naddrs;
124 	rankedaddress *addrs;
125 	isc_netaddr_t remote;
126 	dns_addressorderfunc_t order;
127 	const void *arg;
128 	ns_lwresd_t *lwresd = client->clientmgr->listener->manager;
129 	unsigned int i;
130 	isc_result_t result;
131 
132 	naddrs = client->gabn.naddrs;
133 
134 	if (naddrs <= 1 || lwresd->view->sortlist == NULL)
135 		return;
136 
137 	addrs = isc_mem_get(lwresd->mctx, sizeof(rankedaddress) * naddrs);
138 	if (addrs == NULL)
139 		return;
140 
141 	isc_netaddr_fromsockaddr(&remote, &client->address);
142 	ns_sortlist_byaddrsetup(lwresd->view->sortlist,
143 				&remote, &order, &arg);
144 	if (order == NULL) {
145 		isc_mem_put(lwresd->mctx, addrs,
146 			    sizeof(rankedaddress) * naddrs);
147 		return;
148 	}
149 	for (i = 0; i < naddrs; i++) {
150 		result = lwaddr_netaddr_fromlwresaddr(&addrs[i].address,
151 						      &client->addrs[i]);
152 		INSIST(result == ISC_R_SUCCESS);
153 		addrs[i].rank = (*order)(&addrs[i].address, arg);
154 	}
155 	qsort(addrs, naddrs, sizeof(rankedaddress), addr_compare);
156 	for (i = 0; i < naddrs; i++) {
157 		result = lwaddr_lwresaddr_fromnetaddr(&client->addrs[i],
158 						      &addrs[i].address);
159 		INSIST(result == ISC_R_SUCCESS);
160 	}
161 
162 	isc_mem_put(lwresd->mctx, addrs, sizeof(rankedaddress) * naddrs);
163 }
164 
165 static void
generate_reply(ns_lwdclient_t * client)166 generate_reply(ns_lwdclient_t *client) {
167 	isc_result_t result;
168 	int lwres;
169 	isc_region_t r;
170 	lwres_buffer_t lwb;
171 	ns_lwdclientmgr_t *cm;
172 
173 	cm = client->clientmgr;
174 	lwb.base = NULL;
175 
176 	ns_lwdclient_log(50, "generating gabn reply for client %p", client);
177 
178 	/*
179 	 * We must make certain the client->find is not still active.
180 	 * If it is either the v4 or v6 answer, just set it to NULL and
181 	 * let the cleanup code destroy it.  Otherwise, destroy it now.
182 	 */
183 	if (client->find == client->v4find || client->find == client->v6find)
184 		client->find = NULL;
185 	else
186 		if (client->find != NULL)
187 			dns_adb_destroyfind(&client->find);
188 
189 	/*
190 	 * perhaps there are some here?
191 	 */
192 	if (NEED_V6(client) && client->v4find != NULL)
193 		client->v6find = client->v4find;
194 
195 	/*
196 	 * Run through the finds we have and wire them up to the gabn
197 	 * structure.
198 	 */
199 	LWRES_LIST_INIT(client->gabn.addrs);
200 	if (client->v4find != NULL)
201 		setup_addresses(client, client->v4find, DNS_ADBFIND_INET);
202 	if (client->v6find != NULL)
203 		setup_addresses(client, client->v6find, DNS_ADBFIND_INET6);
204 
205 	/*
206 	 * If there are no addresses, try the next element in the search
207 	 * path, if there are any more.  Otherwise, fall through into
208 	 * the error handling code below.
209 	 */
210 	if (client->gabn.naddrs == 0) {
211 		do {
212 			result = ns_lwsearchctx_next(&client->searchctx);
213 			if (result == ISC_R_SUCCESS) {
214 				cleanup_gabn(client);
215 				result = start_find(client);
216 				if (result == ISC_R_SUCCESS)
217 					return;
218 			}
219 		} while (result == ISC_R_SUCCESS);
220 	}
221 
222 	/*
223 	 * Render the packet.
224 	 */
225 	client->pkt.recvlength = LWRES_RECVLENGTH;
226 	client->pkt.authtype = 0; /* XXXMLG */
227 	client->pkt.authlength = 0;
228 
229 	/*
230 	 * If there are no addresses, return failure.
231 	 */
232 	if (client->gabn.naddrs != 0)
233 		client->pkt.result = LWRES_R_SUCCESS;
234 	else
235 		client->pkt.result = LWRES_R_NOTFOUND;
236 
237 	sort_addresses(client);
238 
239 	lwres = lwres_gabnresponse_render(cm->lwctx, &client->gabn,
240 					  &client->pkt, &lwb);
241 	if (lwres != LWRES_R_SUCCESS)
242 		goto out;
243 
244 	r.base = lwb.base;
245 	r.length = lwb.used;
246 	client->sendbuf = r.base;
247 	client->sendlength = r.length;
248 	result = ns_lwdclient_sendreply(client, &r);
249 	if (result != ISC_R_SUCCESS)
250 		goto out;
251 
252 	NS_LWDCLIENT_SETSEND(client);
253 
254 	/*
255 	 * All done!
256 	 */
257 	cleanup_gabn(client);
258 
259 	return;
260 
261  out:
262 	cleanup_gabn(client);
263 
264 	if (lwb.base != NULL)
265 		lwres_context_freemem(client->clientmgr->lwctx,
266 				      lwb.base, lwb.length);
267 
268 	ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
269 }
270 
271 /*
272  * Take the current real name, move it to an alias slot (if any are
273  * open) then put this new name in as the real name for the target.
274  *
275  * Return success if it can be rendered, otherwise failure.  Note that
276  * not having enough alias slots open is NOT a failure.
277  */
278 static isc_result_t
add_alias(ns_lwdclient_t * client)279 add_alias(ns_lwdclient_t *client) {
280 	isc_buffer_t b;
281 	isc_result_t result;
282 	isc_uint16_t naliases;
283 
284 	b = client->recv_buffer;
285 
286 	/*
287 	 * Render the new name to the buffer.
288 	 */
289 	result = dns_name_totext(dns_fixedname_name(&client->target_name),
290 				 ISC_TRUE, &client->recv_buffer);
291 	if (result != ISC_R_SUCCESS)
292 		return (result);
293 
294 	/*
295 	 * Are there any open slots?
296 	 */
297 	naliases = client->gabn.naliases;
298 	if (naliases < LWRES_MAX_ALIASES) {
299 		client->gabn.aliases[naliases] = client->gabn.realname;
300 		client->gabn.aliaslen[naliases] = client->gabn.realnamelen;
301 		client->gabn.naliases++;
302 	}
303 
304 	/*
305 	 * Save this name away as the current real name.
306 	 */
307 	client->gabn.realname = (char *)(b.base) + b.used;
308 	client->gabn.realnamelen = client->recv_buffer.used - b.used;
309 
310 	return (ISC_R_SUCCESS);
311 }
312 
313 static isc_result_t
store_realname(ns_lwdclient_t * client)314 store_realname(ns_lwdclient_t *client) {
315 	isc_buffer_t b;
316 	isc_result_t result;
317 	dns_name_t *tname;
318 
319 	b = client->recv_buffer;
320 
321 	tname = dns_fixedname_name(&client->target_name);
322 	result = ns_lwsearchctx_current(&client->searchctx, tname);
323 	if (result != ISC_R_SUCCESS)
324 		return (result);
325 
326 	/*
327 	 * Render the new name to the buffer.
328 	 */
329 	result = dns_name_totext(tname, ISC_TRUE, &client->recv_buffer);
330 	if (result != ISC_R_SUCCESS)
331 		return (result);
332 
333 	/*
334 	 * Save this name away as the current real name.
335 	 */
336 	client->gabn.realname = (char *) b.base + b.used;
337 	client->gabn.realnamelen = client->recv_buffer.used - b.used;
338 
339 	return (ISC_R_SUCCESS);
340 }
341 
342 static void
process_gabn_finddone(isc_task_t * task,isc_event_t * ev)343 process_gabn_finddone(isc_task_t *task, isc_event_t *ev) {
344 	ns_lwdclient_t *client = ev->ev_arg;
345 	isc_eventtype_t evtype;
346 	isc_boolean_t claimed;
347 
348 	ns_lwdclient_log(50, "find done for task %p, client %p", task, client);
349 
350 	evtype = ev->ev_type;
351 	isc_event_free(&ev);
352 
353 	/*
354 	 * No more info to be had?  If so, we have all the good stuff
355 	 * right now, so we can render things.
356 	 */
357 	claimed = ISC_FALSE;
358 	if (evtype == DNS_EVENT_ADBNOMOREADDRESSES) {
359 		if (NEED_V4(client)) {
360 			client->v4find = client->find;
361 			claimed = ISC_TRUE;
362 		}
363 		if (NEED_V6(client)) {
364 			client->v6find = client->find;
365 			claimed = ISC_TRUE;
366 		}
367 		if (client->find != NULL) {
368 			if (claimed)
369 				client->find = NULL;
370 			else
371 				dns_adb_destroyfind(&client->find);
372 
373 		}
374 		generate_reply(client);
375 		return;
376 	}
377 
378 	/*
379 	 * We probably don't need this find anymore.  We're either going to
380 	 * reissue it, or an error occurred.  Either way, we're done with
381 	 * it.
382 	 */
383 	if ((client->find != client->v4find)
384 	    && (client->find != client->v6find)) {
385 		dns_adb_destroyfind(&client->find);
386 	} else {
387 		client->find = NULL;
388 	}
389 
390 	/*
391 	 * We have some new information we can gather.  Run off and fetch
392 	 * it.
393 	 */
394 	if (evtype == DNS_EVENT_ADBMOREADDRESSES) {
395 		restart_find(client);
396 		return;
397 	}
398 
399 	/*
400 	 * An error or other strangeness happened.  Drop this query.
401 	 */
402 	cleanup_gabn(client);
403 	ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
404 }
405 
406 static void
restart_find(ns_lwdclient_t * client)407 restart_find(ns_lwdclient_t *client) {
408 	unsigned int options;
409 	isc_result_t result;
410 	isc_boolean_t claimed;
411 
412 	ns_lwdclient_log(50, "starting find for client %p", client);
413 
414 	/*
415 	 * Issue a find for the name contained in the request.  We won't
416 	 * set the bit that says "anything is good enough" -- we want it
417 	 * all.
418 	 */
419 	options = 0;
420 	options |= DNS_ADBFIND_WANTEVENT;
421 	options |= DNS_ADBFIND_RETURNLAME;
422 
423 	/*
424 	 * Set the bits up here to mark that we want this address family
425 	 * and that we do not currently have a find pending.  We will
426 	 * set that bit again below if it turns out we will get an event.
427 	 */
428 	if (NEED_V4(client))
429 		options |= DNS_ADBFIND_INET;
430 	if (NEED_V6(client))
431 		options |= DNS_ADBFIND_INET6;
432 
433  find_again:
434 	INSIST(client->find == NULL);
435 	result = dns_adb_createfind(client->clientmgr->view->adb,
436 				    client->clientmgr->task,
437 				    process_gabn_finddone, client,
438 				    dns_fixedname_name(&client->target_name),
439 				    dns_rootname, 0, options, 0,
440 				    dns_fixedname_name(&client->target_name),
441 				    client->clientmgr->view->dstport,
442 				    &client->find);
443 
444 	/*
445 	 * Did we get an alias?  If so, save it and re-issue the query.
446 	 */
447 	if (result == DNS_R_ALIAS) {
448 		ns_lwdclient_log(50, "found alias, restarting query");
449 		dns_adb_destroyfind(&client->find);
450 		cleanup_gabn(client);
451 		result = add_alias(client);
452 		if (result != ISC_R_SUCCESS) {
453 			ns_lwdclient_log(50,
454 					 "out of buffer space adding alias");
455 			ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
456 			return;
457 		}
458 		goto find_again;
459 	}
460 
461 	ns_lwdclient_log(50, "find returned %d (%s)", result,
462 			 isc_result_totext(result));
463 
464 	/*
465 	 * Did we get an error?
466 	 */
467 	if (result != ISC_R_SUCCESS) {
468 		if (client->find != NULL)
469 			dns_adb_destroyfind(&client->find);
470 		cleanup_gabn(client);
471 		ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
472 		return;
473 	}
474 
475 	claimed = ISC_FALSE;
476 
477 	/*
478 	 * Did we get our answer to V4 addresses?
479 	 */
480 	if (NEED_V4(client)
481 	    && ((client->find->query_pending & DNS_ADBFIND_INET) == 0)) {
482 		ns_lwdclient_log(50, "client %p ipv4 satisfied by find %p",
483 				 client, client->find);
484 		claimed = ISC_TRUE;
485 		client->v4find = client->find;
486 	}
487 
488 	/*
489 	 * Did we get our answer to V6 addresses?
490 	 */
491 	if (NEED_V6(client)
492 	    && ((client->find->query_pending & DNS_ADBFIND_INET6) == 0)) {
493 		ns_lwdclient_log(50, "client %p ipv6 satisfied by find %p",
494 				 client, client->find);
495 		claimed = ISC_TRUE;
496 		client->v6find = client->find;
497 	}
498 
499 	/*
500 	 * If we're going to get an event, set our internal pending flag
501 	 * and return.  When we get an event back we'll do the right
502 	 * thing, basically by calling this function again, perhaps with a
503 	 * new target name.
504 	 *
505 	 * If we have both v4 and v6, and we are still getting an event,
506 	 * we have a programming error, so die hard.
507 	 */
508 	if ((client->find->options & DNS_ADBFIND_WANTEVENT) != 0) {
509 		ns_lwdclient_log(50, "event will be sent");
510 		INSIST(client->v4find == NULL || client->v6find == NULL);
511 		return;
512 	}
513 	ns_lwdclient_log(50, "no event will be sent");
514 	if (claimed)
515 		client->find = NULL;
516 	else
517 		dns_adb_destroyfind(&client->find);
518 
519 	/*
520 	 * We seem to have everything we asked for, or at least we are
521 	 * able to respond with things we've learned.
522 	 */
523 
524 	generate_reply(client);
525 }
526 
527 static isc_result_t
start_find(ns_lwdclient_t * client)528 start_find(ns_lwdclient_t *client) {
529 	isc_result_t result;
530 
531 	/*
532 	 * Initialize the real name and alias arrays in the reply we're
533 	 * going to build up.
534 	 */
535 	init_gabn(client);
536 
537 	result = store_realname(client);
538 	if (result != ISC_R_SUCCESS)
539 		return (result);
540 	restart_find(client);
541 	return (ISC_R_SUCCESS);
542 
543 }
544 
545 static void
init_gabn(ns_lwdclient_t * client)546 init_gabn(ns_lwdclient_t *client) {
547 	int i;
548 
549 	/*
550 	 * Initialize the real name and alias arrays in the reply we're
551 	 * going to build up.
552 	 */
553 	for (i = 0; i < LWRES_MAX_ALIASES; i++) {
554 		client->aliases[i] = NULL;
555 		client->aliaslen[i] = 0;
556 	}
557 	for (i = 0; i < LWRES_MAX_ADDRS; i++) {
558 		client->addrs[i].family = 0;
559 		client->addrs[i].length = 0;
560 		memset(client->addrs[i].address, 0, LWRES_ADDR_MAXLEN);
561 		LWRES_LINK_INIT(&client->addrs[i], link);
562 	}
563 
564 	client->gabn.naliases = 0;
565 	client->gabn.naddrs = 0;
566 	client->gabn.realname = NULL;
567 	client->gabn.aliases = client->aliases;
568 	client->gabn.realnamelen = 0;
569 	client->gabn.aliaslen = client->aliaslen;
570 	LWRES_LIST_INIT(client->gabn.addrs);
571 	client->gabn.base = NULL;
572 	client->gabn.baselen = 0;
573 
574 	/*
575 	 * Set up the internal buffer to point to the receive region.
576 	 */
577 	isc_buffer_init(&client->recv_buffer, client->buffer, LWRES_RECVLENGTH);
578 }
579 
580 /*
581  * When we are called, we can be assured that:
582  *
583  *	client->sockaddr contains the address we need to reply to,
584  *
585  *	client->pkt contains the packet header data,
586  *
587  *	the packet "checks out" overall -- any MD5 hashes or crypto
588  *	bits have been verified,
589  *
590  *	"b" points to the remaining data after the packet header
591  *	was parsed off.
592  *
593  *	We are in a the RECVDONE state.
594  *
595  * From this state we will enter the SEND state if we happen to have
596  * everything we need or we need to return an error packet, or to the
597  * FINDWAIT state if we need to look things up.
598  */
599 void
ns_lwdclient_processgabn(ns_lwdclient_t * client,lwres_buffer_t * b)600 ns_lwdclient_processgabn(ns_lwdclient_t *client, lwres_buffer_t *b) {
601 	isc_result_t result;
602 	lwres_gabnrequest_t *req;
603 	ns_lwdclientmgr_t *cm;
604 	isc_buffer_t namebuf;
605 
606 	REQUIRE(NS_LWDCLIENT_ISRECVDONE(client));
607 
608 	cm = client->clientmgr;
609 	req = NULL;
610 
611 	result = lwres_gabnrequest_parse(client->clientmgr->lwctx,
612 					 b, &client->pkt, &req);
613 	if (result != LWRES_R_SUCCESS)
614 		goto out;
615 	if (req->name == NULL)
616 		goto out;
617 
618 	isc_buffer_init(&namebuf, req->name, req->namelen);
619 	isc_buffer_add(&namebuf, req->namelen);
620 
621 	dns_fixedname_init(&client->target_name);
622 	dns_fixedname_init(&client->query_name);
623 	result = dns_name_fromtext(dns_fixedname_name(&client->query_name),
624 				   &namebuf, NULL, 0, NULL);
625 	if (result != ISC_R_SUCCESS)
626 		goto out;
627 	ns_lwsearchctx_init(&client->searchctx,
628 			    cm->listener->manager->search,
629 			    dns_fixedname_name(&client->query_name),
630 			    cm->listener->manager->ndots);
631 	ns_lwsearchctx_first(&client->searchctx);
632 
633 	client->find_wanted = req->addrtypes;
634 	ns_lwdclient_log(50, "client %p looking for addrtypes %08x",
635 			 client, client->find_wanted);
636 
637 	/*
638 	 * We no longer need to keep this around.
639 	 */
640 	lwres_gabnrequest_free(client->clientmgr->lwctx, &req);
641 
642 	/*
643 	 * Start the find.
644 	 */
645 	result = start_find(client);
646 	if (result != ISC_R_SUCCESS)
647 		goto out;
648 
649 	return;
650 
651 	/*
652 	 * We're screwed.  Return an error packet to our caller.
653 	 */
654  out:
655 	if (req != NULL)
656 		lwres_gabnrequest_free(client->clientmgr->lwctx, &req);
657 
658 	ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
659 }
660