1135446Strhodes/*
2224092Sdougb * Copyright (C) 2004-2007, 2009  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 2000, 2001  Internet Software Consortium.
4135446Strhodes *
5193149Sdougb * Permission to use, copy, modify, and/or distribute this software for any
6135446Strhodes * purpose with or without fee is hereby granted, provided that the above
7135446Strhodes * copyright notice and this permission notice appear in all copies.
8135446Strhodes *
9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11135446Strhodes * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15135446Strhodes * PERFORMANCE OF THIS SOFTWARE.
16135446Strhodes */
17135446Strhodes
18234010Sdougb/* $Id: lwdgabn.c,v 1.24 2009/09/02 23:48:01 tbox Exp $ */
19135446Strhodes
20170222Sdougb/*! \file */
21170222Sdougb
22135446Strhodes#include <config.h>
23135446Strhodes
24135446Strhodes#include <stdlib.h>
25135446Strhodes
26135446Strhodes#include <isc/netaddr.h>
27135446Strhodes#include <isc/sockaddr.h>
28135446Strhodes#include <isc/socket.h>
29135446Strhodes#include <isc/string.h>		/* Required for HP/UX (and others?) */
30135446Strhodes#include <isc/util.h>
31135446Strhodes
32135446Strhodes#include <dns/adb.h>
33135446Strhodes#include <dns/events.h>
34135446Strhodes#include <dns/result.h>
35135446Strhodes
36135446Strhodes#include <named/types.h>
37135446Strhodes#include <named/lwaddr.h>
38135446Strhodes#include <named/lwdclient.h>
39135446Strhodes#include <named/lwresd.h>
40135446Strhodes#include <named/lwsearch.h>
41135446Strhodes#include <named/sortlist.h>
42135446Strhodes
43135446Strhodes#define NEED_V4(c)	((((c)->find_wanted & LWRES_ADDRTYPE_V4) != 0) \
44135446Strhodes			 && ((c)->v4find == NULL))
45135446Strhodes#define NEED_V6(c)	((((c)->find_wanted & LWRES_ADDRTYPE_V6) != 0) \
46135446Strhodes			 && ((c)->v6find == NULL))
47135446Strhodes
48135446Strhodesstatic isc_result_t start_find(ns_lwdclient_t *);
49135446Strhodesstatic void restart_find(ns_lwdclient_t *);
50135446Strhodesstatic void init_gabn(ns_lwdclient_t *);
51135446Strhodes
52170222Sdougb/*%
53135446Strhodes * Destroy any finds.  This can be used to "start over from scratch" and
54135446Strhodes * should only be called when events are _not_ being generated by the finds.
55135446Strhodes */
56135446Strhodesstatic void
57135446Strhodescleanup_gabn(ns_lwdclient_t *client) {
58135446Strhodes	ns_lwdclient_log(50, "cleaning up client %p", client);
59135446Strhodes
60135446Strhodes	if (client->v6find != NULL) {
61135446Strhodes		if (client->v6find == client->v4find)
62135446Strhodes			client->v6find = NULL;
63135446Strhodes		else
64135446Strhodes			dns_adb_destroyfind(&client->v6find);
65135446Strhodes	}
66135446Strhodes	if (client->v4find != NULL)
67135446Strhodes		dns_adb_destroyfind(&client->v4find);
68135446Strhodes}
69135446Strhodes
70135446Strhodesstatic void
71135446Strhodessetup_addresses(ns_lwdclient_t *client, dns_adbfind_t *find, unsigned int at) {
72135446Strhodes	dns_adbaddrinfo_t *ai;
73135446Strhodes	lwres_addr_t *addr;
74135446Strhodes	int af;
75135446Strhodes	const struct sockaddr *sa;
76135446Strhodes	isc_result_t result;
77135446Strhodes
78135446Strhodes	if (at == DNS_ADBFIND_INET)
79135446Strhodes		af = AF_INET;
80135446Strhodes	else
81135446Strhodes		af = AF_INET6;
82135446Strhodes
83135446Strhodes	ai = ISC_LIST_HEAD(find->list);
84135446Strhodes	while (ai != NULL && client->gabn.naddrs < LWRES_MAX_ADDRS) {
85135446Strhodes		sa = &ai->sockaddr.type.sa;
86135446Strhodes		if (sa->sa_family != af)
87135446Strhodes			goto next;
88135446Strhodes
89135446Strhodes		addr = &client->addrs[client->gabn.naddrs];
90135446Strhodes
91135446Strhodes		result = lwaddr_lwresaddr_fromsockaddr(addr, &ai->sockaddr);
92135446Strhodes		if (result != ISC_R_SUCCESS)
93135446Strhodes			goto next;
94135446Strhodes
95135446Strhodes		ns_lwdclient_log(50, "adding address %p, family %d, length %d",
96135446Strhodes				 addr->address, addr->family, addr->length);
97135446Strhodes
98135446Strhodes		client->gabn.naddrs++;
99135446Strhodes		REQUIRE(!LWRES_LINK_LINKED(addr, link));
100135446Strhodes		LWRES_LIST_APPEND(client->gabn.addrs, addr, link);
101135446Strhodes
102135446Strhodes	next:
103135446Strhodes		ai = ISC_LIST_NEXT(ai, publink);
104135446Strhodes	}
105135446Strhodes}
106135446Strhodes
107135446Strhodestypedef struct {
108135446Strhodes	isc_netaddr_t address;
109135446Strhodes	int rank;
110135446Strhodes} rankedaddress;
111135446Strhodes
112135446Strhodesstatic int
113135446Strhodesaddr_compare(const void *av, const void *bv) {
114135446Strhodes	const rankedaddress *a = (const rankedaddress *) av;
115135446Strhodes	const rankedaddress *b = (const rankedaddress *) bv;
116135446Strhodes	return (a->rank - b->rank);
117135446Strhodes}
118135446Strhodes
119135446Strhodesstatic void
120135446Strhodessort_addresses(ns_lwdclient_t *client) {
121135446Strhodes	unsigned int naddrs;
122135446Strhodes	rankedaddress *addrs;
123135446Strhodes	isc_netaddr_t remote;
124135446Strhodes	dns_addressorderfunc_t order;
125165071Sdougb	const void *arg;
126135446Strhodes	ns_lwresd_t *lwresd = client->clientmgr->listener->manager;
127135446Strhodes	unsigned int i;
128135446Strhodes	isc_result_t result;
129135446Strhodes
130135446Strhodes	naddrs = client->gabn.naddrs;
131135446Strhodes
132135446Strhodes	if (naddrs <= 1 || lwresd->view->sortlist == NULL)
133135446Strhodes		return;
134135446Strhodes
135135446Strhodes	addrs = isc_mem_get(lwresd->mctx, sizeof(rankedaddress) * naddrs);
136135446Strhodes	if (addrs == NULL)
137135446Strhodes		return;
138135446Strhodes
139135446Strhodes	isc_netaddr_fromsockaddr(&remote, &client->address);
140135446Strhodes	ns_sortlist_byaddrsetup(lwresd->view->sortlist,
141135446Strhodes				&remote, &order, &arg);
142135446Strhodes	if (order == NULL) {
143135446Strhodes		isc_mem_put(lwresd->mctx, addrs,
144135446Strhodes			    sizeof(rankedaddress) * naddrs);
145135446Strhodes		return;
146135446Strhodes	}
147135446Strhodes	for (i = 0; i < naddrs; i++) {
148135446Strhodes		result = lwaddr_netaddr_fromlwresaddr(&addrs[i].address,
149135446Strhodes						      &client->addrs[i]);
150135446Strhodes		INSIST(result == ISC_R_SUCCESS);
151135446Strhodes		addrs[i].rank = (*order)(&addrs[i].address, arg);
152135446Strhodes	}
153135446Strhodes	qsort(addrs, naddrs, sizeof(rankedaddress), addr_compare);
154135446Strhodes	for (i = 0; i < naddrs; i++) {
155135446Strhodes		result = lwaddr_lwresaddr_fromnetaddr(&client->addrs[i],
156135446Strhodes						      &addrs[i].address);
157135446Strhodes		INSIST(result == ISC_R_SUCCESS);
158135446Strhodes	}
159135446Strhodes
160135446Strhodes	isc_mem_put(lwresd->mctx, addrs, sizeof(rankedaddress) * naddrs);
161135446Strhodes}
162135446Strhodes
163135446Strhodesstatic void
164135446Strhodesgenerate_reply(ns_lwdclient_t *client) {
165135446Strhodes	isc_result_t result;
166135446Strhodes	int lwres;
167135446Strhodes	isc_region_t r;
168135446Strhodes	lwres_buffer_t lwb;
169135446Strhodes	ns_lwdclientmgr_t *cm;
170135446Strhodes
171135446Strhodes	cm = client->clientmgr;
172135446Strhodes	lwb.base = NULL;
173135446Strhodes
174135446Strhodes	ns_lwdclient_log(50, "generating gabn reply for client %p", client);
175135446Strhodes
176135446Strhodes	/*
177135446Strhodes	 * We must make certain the client->find is not still active.
178135446Strhodes	 * If it is either the v4 or v6 answer, just set it to NULL and
179135446Strhodes	 * let the cleanup code destroy it.  Otherwise, destroy it now.
180135446Strhodes	 */
181135446Strhodes	if (client->find == client->v4find || client->find == client->v6find)
182135446Strhodes		client->find = NULL;
183135446Strhodes	else
184135446Strhodes		if (client->find != NULL)
185135446Strhodes			dns_adb_destroyfind(&client->find);
186135446Strhodes
187135446Strhodes	/*
188135446Strhodes	 * perhaps there are some here?
189135446Strhodes	 */
190135446Strhodes	if (NEED_V6(client) && client->v4find != NULL)
191135446Strhodes		client->v6find = client->v4find;
192135446Strhodes
193135446Strhodes	/*
194135446Strhodes	 * Run through the finds we have and wire them up to the gabn
195135446Strhodes	 * structure.
196135446Strhodes	 */
197135446Strhodes	LWRES_LIST_INIT(client->gabn.addrs);
198135446Strhodes	if (client->v4find != NULL)
199135446Strhodes		setup_addresses(client, client->v4find, DNS_ADBFIND_INET);
200135446Strhodes	if (client->v6find != NULL)
201135446Strhodes		setup_addresses(client, client->v6find, DNS_ADBFIND_INET6);
202135446Strhodes
203135446Strhodes	/*
204135446Strhodes	 * If there are no addresses, try the next element in the search
205135446Strhodes	 * path, if there are any more.  Otherwise, fall through into
206135446Strhodes	 * the error handling code below.
207135446Strhodes	 */
208135446Strhodes	if (client->gabn.naddrs == 0) {
209135446Strhodes		do {
210135446Strhodes			result = ns_lwsearchctx_next(&client->searchctx);
211135446Strhodes			if (result == ISC_R_SUCCESS) {
212135446Strhodes				cleanup_gabn(client);
213135446Strhodes				result = start_find(client);
214135446Strhodes				if (result == ISC_R_SUCCESS)
215135446Strhodes					return;
216135446Strhodes			}
217135446Strhodes		} while (result == ISC_R_SUCCESS);
218135446Strhodes	}
219135446Strhodes
220135446Strhodes	/*
221135446Strhodes	 * Render the packet.
222135446Strhodes	 */
223135446Strhodes	client->pkt.recvlength = LWRES_RECVLENGTH;
224135446Strhodes	client->pkt.authtype = 0; /* XXXMLG */
225135446Strhodes	client->pkt.authlength = 0;
226135446Strhodes
227135446Strhodes	/*
228135446Strhodes	 * If there are no addresses, return failure.
229135446Strhodes	 */
230135446Strhodes	if (client->gabn.naddrs != 0)
231135446Strhodes		client->pkt.result = LWRES_R_SUCCESS;
232135446Strhodes	else
233135446Strhodes		client->pkt.result = LWRES_R_NOTFOUND;
234135446Strhodes
235135446Strhodes	sort_addresses(client);
236135446Strhodes
237135446Strhodes	lwres = lwres_gabnresponse_render(cm->lwctx, &client->gabn,
238135446Strhodes					  &client->pkt, &lwb);
239135446Strhodes	if (lwres != LWRES_R_SUCCESS)
240135446Strhodes		goto out;
241135446Strhodes
242135446Strhodes	r.base = lwb.base;
243135446Strhodes	r.length = lwb.used;
244135446Strhodes	client->sendbuf = r.base;
245135446Strhodes	client->sendlength = r.length;
246135446Strhodes	result = ns_lwdclient_sendreply(client, &r);
247135446Strhodes	if (result != ISC_R_SUCCESS)
248135446Strhodes		goto out;
249135446Strhodes
250135446Strhodes	NS_LWDCLIENT_SETSEND(client);
251135446Strhodes
252135446Strhodes	/*
253135446Strhodes	 * All done!
254135446Strhodes	 */
255135446Strhodes	cleanup_gabn(client);
256135446Strhodes
257135446Strhodes	return;
258135446Strhodes
259135446Strhodes out:
260135446Strhodes	cleanup_gabn(client);
261135446Strhodes
262135446Strhodes	if (lwb.base != NULL)
263135446Strhodes		lwres_context_freemem(client->clientmgr->lwctx,
264135446Strhodes				      lwb.base, lwb.length);
265135446Strhodes
266135446Strhodes	ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
267135446Strhodes}
268135446Strhodes
269135446Strhodes/*
270135446Strhodes * Take the current real name, move it to an alias slot (if any are
271135446Strhodes * open) then put this new name in as the real name for the target.
272135446Strhodes *
273135446Strhodes * Return success if it can be rendered, otherwise failure.  Note that
274135446Strhodes * not having enough alias slots open is NOT a failure.
275135446Strhodes */
276135446Strhodesstatic isc_result_t
277135446Strhodesadd_alias(ns_lwdclient_t *client) {
278135446Strhodes	isc_buffer_t b;
279135446Strhodes	isc_result_t result;
280135446Strhodes	isc_uint16_t naliases;
281135446Strhodes
282135446Strhodes	b = client->recv_buffer;
283135446Strhodes
284135446Strhodes	/*
285135446Strhodes	 * Render the new name to the buffer.
286135446Strhodes	 */
287135446Strhodes	result = dns_name_totext(dns_fixedname_name(&client->target_name),
288135446Strhodes				 ISC_TRUE, &client->recv_buffer);
289135446Strhodes	if (result != ISC_R_SUCCESS)
290135446Strhodes		return (result);
291135446Strhodes
292135446Strhodes	/*
293135446Strhodes	 * Are there any open slots?
294135446Strhodes	 */
295135446Strhodes	naliases = client->gabn.naliases;
296135446Strhodes	if (naliases < LWRES_MAX_ALIASES) {
297135446Strhodes		client->gabn.aliases[naliases] = client->gabn.realname;
298135446Strhodes		client->gabn.aliaslen[naliases] = client->gabn.realnamelen;
299135446Strhodes		client->gabn.naliases++;
300135446Strhodes	}
301135446Strhodes
302135446Strhodes	/*
303135446Strhodes	 * Save this name away as the current real name.
304135446Strhodes	 */
305135446Strhodes	client->gabn.realname = (char *)(b.base) + b.used;
306135446Strhodes	client->gabn.realnamelen = client->recv_buffer.used - b.used;
307135446Strhodes
308135446Strhodes	return (ISC_R_SUCCESS);
309135446Strhodes}
310135446Strhodes
311135446Strhodesstatic isc_result_t
312135446Strhodesstore_realname(ns_lwdclient_t *client) {
313135446Strhodes	isc_buffer_t b;
314135446Strhodes	isc_result_t result;
315135446Strhodes	dns_name_t *tname;
316135446Strhodes
317135446Strhodes	b = client->recv_buffer;
318135446Strhodes
319135446Strhodes	tname = dns_fixedname_name(&client->target_name);
320135446Strhodes	result = ns_lwsearchctx_current(&client->searchctx, tname);
321135446Strhodes	if (result != ISC_R_SUCCESS)
322135446Strhodes		return (result);
323135446Strhodes
324135446Strhodes	/*
325135446Strhodes	 * Render the new name to the buffer.
326135446Strhodes	 */
327135446Strhodes	result = dns_name_totext(tname, ISC_TRUE, &client->recv_buffer);
328135446Strhodes	if (result != ISC_R_SUCCESS)
329135446Strhodes		return (result);
330135446Strhodes
331135446Strhodes	/*
332135446Strhodes	 * Save this name away as the current real name.
333135446Strhodes	 */
334135446Strhodes	client->gabn.realname = (char *) b.base + b.used;
335135446Strhodes	client->gabn.realnamelen = client->recv_buffer.used - b.used;
336135446Strhodes
337135446Strhodes	return (ISC_R_SUCCESS);
338135446Strhodes}
339135446Strhodes
340135446Strhodesstatic void
341135446Strhodesprocess_gabn_finddone(isc_task_t *task, isc_event_t *ev) {
342135446Strhodes	ns_lwdclient_t *client = ev->ev_arg;
343135446Strhodes	isc_eventtype_t evtype;
344135446Strhodes	isc_boolean_t claimed;
345135446Strhodes
346135446Strhodes	ns_lwdclient_log(50, "find done for task %p, client %p", task, client);
347135446Strhodes
348135446Strhodes	evtype = ev->ev_type;
349135446Strhodes	isc_event_free(&ev);
350135446Strhodes
351135446Strhodes	/*
352135446Strhodes	 * No more info to be had?  If so, we have all the good stuff
353135446Strhodes	 * right now, so we can render things.
354135446Strhodes	 */
355135446Strhodes	claimed = ISC_FALSE;
356135446Strhodes	if (evtype == DNS_EVENT_ADBNOMOREADDRESSES) {
357135446Strhodes		if (NEED_V4(client)) {
358135446Strhodes			client->v4find = client->find;
359135446Strhodes			claimed = ISC_TRUE;
360135446Strhodes		}
361135446Strhodes		if (NEED_V6(client)) {
362135446Strhodes			client->v6find = client->find;
363135446Strhodes			claimed = ISC_TRUE;
364135446Strhodes		}
365135446Strhodes		if (client->find != NULL) {
366135446Strhodes			if (claimed)
367135446Strhodes				client->find = NULL;
368135446Strhodes			else
369135446Strhodes				dns_adb_destroyfind(&client->find);
370135446Strhodes
371135446Strhodes		}
372135446Strhodes		generate_reply(client);
373135446Strhodes		return;
374135446Strhodes	}
375135446Strhodes
376135446Strhodes	/*
377135446Strhodes	 * We probably don't need this find anymore.  We're either going to
378135446Strhodes	 * reissue it, or an error occurred.  Either way, we're done with
379135446Strhodes	 * it.
380135446Strhodes	 */
381135446Strhodes	if ((client->find != client->v4find)
382135446Strhodes	    && (client->find != client->v6find)) {
383135446Strhodes		dns_adb_destroyfind(&client->find);
384135446Strhodes	} else {
385135446Strhodes		client->find = NULL;
386135446Strhodes	}
387135446Strhodes
388135446Strhodes	/*
389135446Strhodes	 * We have some new information we can gather.  Run off and fetch
390135446Strhodes	 * it.
391135446Strhodes	 */
392135446Strhodes	if (evtype == DNS_EVENT_ADBMOREADDRESSES) {
393135446Strhodes		restart_find(client);
394135446Strhodes		return;
395135446Strhodes	}
396135446Strhodes
397135446Strhodes	/*
398135446Strhodes	 * An error or other strangeness happened.  Drop this query.
399135446Strhodes	 */
400135446Strhodes	cleanup_gabn(client);
401135446Strhodes	ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
402135446Strhodes}
403135446Strhodes
404135446Strhodesstatic void
405135446Strhodesrestart_find(ns_lwdclient_t *client) {
406135446Strhodes	unsigned int options;
407135446Strhodes	isc_result_t result;
408135446Strhodes	isc_boolean_t claimed;
409135446Strhodes
410135446Strhodes	ns_lwdclient_log(50, "starting find for client %p", client);
411135446Strhodes
412135446Strhodes	/*
413135446Strhodes	 * Issue a find for the name contained in the request.  We won't
414135446Strhodes	 * set the bit that says "anything is good enough" -- we want it
415135446Strhodes	 * all.
416135446Strhodes	 */
417135446Strhodes	options = 0;
418135446Strhodes	options |= DNS_ADBFIND_WANTEVENT;
419135446Strhodes	options |= DNS_ADBFIND_RETURNLAME;
420135446Strhodes
421135446Strhodes	/*
422135446Strhodes	 * Set the bits up here to mark that we want this address family
423135446Strhodes	 * and that we do not currently have a find pending.  We will
424135446Strhodes	 * set that bit again below if it turns out we will get an event.
425135446Strhodes	 */
426135446Strhodes	if (NEED_V4(client))
427135446Strhodes		options |= DNS_ADBFIND_INET;
428135446Strhodes	if (NEED_V6(client))
429135446Strhodes		options |= DNS_ADBFIND_INET6;
430135446Strhodes
431135446Strhodes find_again:
432135446Strhodes	INSIST(client->find == NULL);
433135446Strhodes	result = dns_adb_createfind(client->clientmgr->view->adb,
434135446Strhodes				    client->clientmgr->task,
435135446Strhodes				    process_gabn_finddone, client,
436135446Strhodes				    dns_fixedname_name(&client->target_name),
437170222Sdougb				    dns_rootname, 0, options, 0,
438135446Strhodes				    dns_fixedname_name(&client->target_name),
439135446Strhodes				    client->clientmgr->view->dstport,
440135446Strhodes				    &client->find);
441135446Strhodes
442135446Strhodes	/*
443135446Strhodes	 * Did we get an alias?  If so, save it and re-issue the query.
444135446Strhodes	 */
445135446Strhodes	if (result == DNS_R_ALIAS) {
446135446Strhodes		ns_lwdclient_log(50, "found alias, restarting query");
447135446Strhodes		dns_adb_destroyfind(&client->find);
448135446Strhodes		cleanup_gabn(client);
449135446Strhodes		result = add_alias(client);
450135446Strhodes		if (result != ISC_R_SUCCESS) {
451135446Strhodes			ns_lwdclient_log(50,
452135446Strhodes					 "out of buffer space adding alias");
453135446Strhodes			ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
454135446Strhodes			return;
455135446Strhodes		}
456135446Strhodes		goto find_again;
457135446Strhodes	}
458135446Strhodes
459135446Strhodes	ns_lwdclient_log(50, "find returned %d (%s)", result,
460135446Strhodes			 isc_result_totext(result));
461135446Strhodes
462135446Strhodes	/*
463135446Strhodes	 * Did we get an error?
464135446Strhodes	 */
465135446Strhodes	if (result != ISC_R_SUCCESS) {
466135446Strhodes		if (client->find != NULL)
467135446Strhodes			dns_adb_destroyfind(&client->find);
468135446Strhodes		cleanup_gabn(client);
469135446Strhodes		ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
470135446Strhodes		return;
471135446Strhodes	}
472135446Strhodes
473135446Strhodes	claimed = ISC_FALSE;
474135446Strhodes
475135446Strhodes	/*
476135446Strhodes	 * Did we get our answer to V4 addresses?
477135446Strhodes	 */
478135446Strhodes	if (NEED_V4(client)
479135446Strhodes	    && ((client->find->query_pending & DNS_ADBFIND_INET) == 0)) {
480135446Strhodes		ns_lwdclient_log(50, "client %p ipv4 satisfied by find %p",
481135446Strhodes				 client, client->find);
482135446Strhodes		claimed = ISC_TRUE;
483135446Strhodes		client->v4find = client->find;
484135446Strhodes	}
485135446Strhodes
486135446Strhodes	/*
487135446Strhodes	 * Did we get our answer to V6 addresses?
488135446Strhodes	 */
489135446Strhodes	if (NEED_V6(client)
490135446Strhodes	    && ((client->find->query_pending & DNS_ADBFIND_INET6) == 0)) {
491135446Strhodes		ns_lwdclient_log(50, "client %p ipv6 satisfied by find %p",
492135446Strhodes				 client, client->find);
493135446Strhodes		claimed = ISC_TRUE;
494135446Strhodes		client->v6find = client->find;
495135446Strhodes	}
496135446Strhodes
497135446Strhodes	/*
498135446Strhodes	 * If we're going to get an event, set our internal pending flag
499135446Strhodes	 * and return.  When we get an event back we'll do the right
500135446Strhodes	 * thing, basically by calling this function again, perhaps with a
501135446Strhodes	 * new target name.
502135446Strhodes	 *
503135446Strhodes	 * If we have both v4 and v6, and we are still getting an event,
504135446Strhodes	 * we have a programming error, so die hard.
505135446Strhodes	 */
506135446Strhodes	if ((client->find->options & DNS_ADBFIND_WANTEVENT) != 0) {
507135446Strhodes		ns_lwdclient_log(50, "event will be sent");
508135446Strhodes		INSIST(client->v4find == NULL || client->v6find == NULL);
509135446Strhodes		return;
510135446Strhodes	}
511135446Strhodes	ns_lwdclient_log(50, "no event will be sent");
512135446Strhodes	if (claimed)
513135446Strhodes		client->find = NULL;
514135446Strhodes	else
515135446Strhodes		dns_adb_destroyfind(&client->find);
516135446Strhodes
517135446Strhodes	/*
518135446Strhodes	 * We seem to have everything we asked for, or at least we are
519135446Strhodes	 * able to respond with things we've learned.
520135446Strhodes	 */
521135446Strhodes
522135446Strhodes	generate_reply(client);
523135446Strhodes}
524135446Strhodes
525135446Strhodesstatic isc_result_t
526135446Strhodesstart_find(ns_lwdclient_t *client) {
527135446Strhodes	isc_result_t result;
528135446Strhodes
529135446Strhodes	/*
530135446Strhodes	 * Initialize the real name and alias arrays in the reply we're
531135446Strhodes	 * going to build up.
532135446Strhodes	 */
533135446Strhodes	init_gabn(client);
534135446Strhodes
535135446Strhodes	result = store_realname(client);
536135446Strhodes	if (result != ISC_R_SUCCESS)
537135446Strhodes		return (result);
538135446Strhodes	restart_find(client);
539135446Strhodes	return (ISC_R_SUCCESS);
540135446Strhodes
541135446Strhodes}
542135446Strhodes
543135446Strhodesstatic void
544135446Strhodesinit_gabn(ns_lwdclient_t *client) {
545135446Strhodes	int i;
546135446Strhodes
547135446Strhodes	/*
548135446Strhodes	 * Initialize the real name and alias arrays in the reply we're
549135446Strhodes	 * going to build up.
550135446Strhodes	 */
551135446Strhodes	for (i = 0; i < LWRES_MAX_ALIASES; i++) {
552135446Strhodes		client->aliases[i] = NULL;
553135446Strhodes		client->aliaslen[i] = 0;
554135446Strhodes	}
555135446Strhodes	for (i = 0; i < LWRES_MAX_ADDRS; i++) {
556135446Strhodes		client->addrs[i].family = 0;
557135446Strhodes		client->addrs[i].length = 0;
558135446Strhodes		memset(client->addrs[i].address, 0, LWRES_ADDR_MAXLEN);
559135446Strhodes		LWRES_LINK_INIT(&client->addrs[i], link);
560135446Strhodes	}
561135446Strhodes
562135446Strhodes	client->gabn.naliases = 0;
563135446Strhodes	client->gabn.naddrs = 0;
564135446Strhodes	client->gabn.realname = NULL;
565135446Strhodes	client->gabn.aliases = client->aliases;
566135446Strhodes	client->gabn.realnamelen = 0;
567135446Strhodes	client->gabn.aliaslen = client->aliaslen;
568135446Strhodes	LWRES_LIST_INIT(client->gabn.addrs);
569135446Strhodes	client->gabn.base = NULL;
570135446Strhodes	client->gabn.baselen = 0;
571135446Strhodes
572135446Strhodes	/*
573135446Strhodes	 * Set up the internal buffer to point to the receive region.
574135446Strhodes	 */
575135446Strhodes	isc_buffer_init(&client->recv_buffer, client->buffer, LWRES_RECVLENGTH);
576135446Strhodes}
577135446Strhodes
578135446Strhodes/*
579135446Strhodes * When we are called, we can be assured that:
580135446Strhodes *
581135446Strhodes *	client->sockaddr contains the address we need to reply to,
582135446Strhodes *
583135446Strhodes *	client->pkt contains the packet header data,
584135446Strhodes *
585135446Strhodes *	the packet "checks out" overall -- any MD5 hashes or crypto
586135446Strhodes *	bits have been verified,
587135446Strhodes *
588135446Strhodes *	"b" points to the remaining data after the packet header
589135446Strhodes *	was parsed off.
590135446Strhodes *
591135446Strhodes *	We are in a the RECVDONE state.
592135446Strhodes *
593135446Strhodes * From this state we will enter the SEND state if we happen to have
594135446Strhodes * everything we need or we need to return an error packet, or to the
595135446Strhodes * FINDWAIT state if we need to look things up.
596135446Strhodes */
597135446Strhodesvoid
598135446Strhodesns_lwdclient_processgabn(ns_lwdclient_t *client, lwres_buffer_t *b) {
599135446Strhodes	isc_result_t result;
600135446Strhodes	lwres_gabnrequest_t *req;
601135446Strhodes	ns_lwdclientmgr_t *cm;
602135446Strhodes	isc_buffer_t namebuf;
603135446Strhodes
604135446Strhodes	REQUIRE(NS_LWDCLIENT_ISRECVDONE(client));
605135446Strhodes
606135446Strhodes	cm = client->clientmgr;
607135446Strhodes	req = NULL;
608135446Strhodes
609135446Strhodes	result = lwres_gabnrequest_parse(client->clientmgr->lwctx,
610135446Strhodes					 b, &client->pkt, &req);
611135446Strhodes	if (result != LWRES_R_SUCCESS)
612135446Strhodes		goto out;
613135446Strhodes	if (req->name == NULL)
614135446Strhodes		goto out;
615135446Strhodes
616135446Strhodes	isc_buffer_init(&namebuf, req->name, req->namelen);
617135446Strhodes	isc_buffer_add(&namebuf, req->namelen);
618135446Strhodes
619135446Strhodes	dns_fixedname_init(&client->target_name);
620135446Strhodes	dns_fixedname_init(&client->query_name);
621135446Strhodes	result = dns_name_fromtext(dns_fixedname_name(&client->query_name),
622224092Sdougb				   &namebuf, NULL, 0, NULL);
623135446Strhodes	if (result != ISC_R_SUCCESS)
624135446Strhodes		goto out;
625135446Strhodes	ns_lwsearchctx_init(&client->searchctx,
626135446Strhodes			    cm->listener->manager->search,
627135446Strhodes			    dns_fixedname_name(&client->query_name),
628135446Strhodes			    cm->listener->manager->ndots);
629135446Strhodes	ns_lwsearchctx_first(&client->searchctx);
630135446Strhodes
631135446Strhodes	client->find_wanted = req->addrtypes;
632135446Strhodes	ns_lwdclient_log(50, "client %p looking for addrtypes %08x",
633135446Strhodes			 client, client->find_wanted);
634135446Strhodes
635135446Strhodes	/*
636135446Strhodes	 * We no longer need to keep this around.
637135446Strhodes	 */
638135446Strhodes	lwres_gabnrequest_free(client->clientmgr->lwctx, &req);
639135446Strhodes
640135446Strhodes	/*
641135446Strhodes	 * Start the find.
642135446Strhodes	 */
643135446Strhodes	result = start_find(client);
644135446Strhodes	if (result != ISC_R_SUCCESS)
645135446Strhodes		goto out;
646135446Strhodes
647135446Strhodes	return;
648135446Strhodes
649135446Strhodes	/*
650135446Strhodes	 * We're screwed.  Return an error packet to our caller.
651135446Strhodes	 */
652135446Strhodes out:
653135446Strhodes	if (req != NULL)
654135446Strhodes		lwres_gabnrequest_free(client->clientmgr->lwctx, &req);
655135446Strhodes
656135446Strhodes	ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
657135446Strhodes}
658