1/*	$NetBSD$	*/
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
50static isc_result_t start_find(ns_lwdclient_t *);
51static void restart_find(ns_lwdclient_t *);
52static 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 */
58static void
59cleanup_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
72static void
73setup_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
109typedef struct {
110	isc_netaddr_t address;
111	int rank;
112} rankedaddress;
113
114static int
115addr_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
121static void
122sort_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
165static void
166generate_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 */
278static isc_result_t
279add_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
313static isc_result_t
314store_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
342static void
343process_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
406static void
407restart_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
527static isc_result_t
528start_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
545static void
546init_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 */
599void
600ns_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