660 lines
16 KiB
C
660 lines
16 KiB
C
/* $NetBSD: lwdgabn.c,v 1.1.1.5 2008/06/21 18:35:07 christos Exp $ */
|
|
|
|
/*
|
|
* Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
|
|
* Copyright (C) 2000, 2001 Internet Software Consortium.
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
/* Id: lwdgabn.c,v 1.22 2007/06/19 23:46:59 tbox Exp */
|
|
|
|
/*! \file */
|
|
|
|
#include <config.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <isc/netaddr.h>
|
|
#include <isc/sockaddr.h>
|
|
#include <isc/socket.h>
|
|
#include <isc/string.h> /* Required for HP/UX (and others?) */
|
|
#include <isc/util.h>
|
|
|
|
#include <dns/adb.h>
|
|
#include <dns/events.h>
|
|
#include <dns/result.h>
|
|
|
|
#include <named/types.h>
|
|
#include <named/lwaddr.h>
|
|
#include <named/lwdclient.h>
|
|
#include <named/lwresd.h>
|
|
#include <named/lwsearch.h>
|
|
#include <named/sortlist.h>
|
|
|
|
#define NEED_V4(c) ((((c)->find_wanted & LWRES_ADDRTYPE_V4) != 0) \
|
|
&& ((c)->v4find == NULL))
|
|
#define NEED_V6(c) ((((c)->find_wanted & LWRES_ADDRTYPE_V6) != 0) \
|
|
&& ((c)->v6find == NULL))
|
|
|
|
static isc_result_t start_find(ns_lwdclient_t *);
|
|
static void restart_find(ns_lwdclient_t *);
|
|
static void init_gabn(ns_lwdclient_t *);
|
|
|
|
/*%
|
|
* Destroy any finds. This can be used to "start over from scratch" and
|
|
* should only be called when events are _not_ being generated by the finds.
|
|
*/
|
|
static void
|
|
cleanup_gabn(ns_lwdclient_t *client) {
|
|
ns_lwdclient_log(50, "cleaning up client %p", client);
|
|
|
|
if (client->v6find != NULL) {
|
|
if (client->v6find == client->v4find)
|
|
client->v6find = NULL;
|
|
else
|
|
dns_adb_destroyfind(&client->v6find);
|
|
}
|
|
if (client->v4find != NULL)
|
|
dns_adb_destroyfind(&client->v4find);
|
|
}
|
|
|
|
static void
|
|
setup_addresses(ns_lwdclient_t *client, dns_adbfind_t *find, unsigned int at) {
|
|
dns_adbaddrinfo_t *ai;
|
|
lwres_addr_t *addr;
|
|
int af;
|
|
const struct sockaddr *sa;
|
|
isc_result_t result;
|
|
|
|
if (at == DNS_ADBFIND_INET)
|
|
af = AF_INET;
|
|
else
|
|
af = AF_INET6;
|
|
|
|
ai = ISC_LIST_HEAD(find->list);
|
|
while (ai != NULL && client->gabn.naddrs < LWRES_MAX_ADDRS) {
|
|
sa = &ai->sockaddr.type.sa;
|
|
if (sa->sa_family != af)
|
|
goto next;
|
|
|
|
addr = &client->addrs[client->gabn.naddrs];
|
|
|
|
result = lwaddr_lwresaddr_fromsockaddr(addr, &ai->sockaddr);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto next;
|
|
|
|
ns_lwdclient_log(50, "adding address %p, family %d, length %d",
|
|
addr->address, addr->family, addr->length);
|
|
|
|
client->gabn.naddrs++;
|
|
REQUIRE(!LWRES_LINK_LINKED(addr, link));
|
|
LWRES_LIST_APPEND(client->gabn.addrs, addr, link);
|
|
|
|
next:
|
|
ai = ISC_LIST_NEXT(ai, publink);
|
|
}
|
|
}
|
|
|
|
typedef struct {
|
|
isc_netaddr_t address;
|
|
int rank;
|
|
} rankedaddress;
|
|
|
|
static int
|
|
addr_compare(const void *av, const void *bv) {
|
|
const rankedaddress *a = (const rankedaddress *) av;
|
|
const rankedaddress *b = (const rankedaddress *) bv;
|
|
return (a->rank - b->rank);
|
|
}
|
|
|
|
static void
|
|
sort_addresses(ns_lwdclient_t *client) {
|
|
unsigned int naddrs;
|
|
rankedaddress *addrs;
|
|
isc_netaddr_t remote;
|
|
dns_addressorderfunc_t order;
|
|
const void *arg;
|
|
ns_lwresd_t *lwresd = client->clientmgr->listener->manager;
|
|
unsigned int i;
|
|
isc_result_t result;
|
|
|
|
naddrs = client->gabn.naddrs;
|
|
|
|
if (naddrs <= 1 || lwresd->view->sortlist == NULL)
|
|
return;
|
|
|
|
addrs = isc_mem_get(lwresd->mctx, sizeof(rankedaddress) * naddrs);
|
|
if (addrs == NULL)
|
|
return;
|
|
|
|
isc_netaddr_fromsockaddr(&remote, &client->address);
|
|
ns_sortlist_byaddrsetup(lwresd->view->sortlist,
|
|
&remote, &order, &arg);
|
|
if (order == NULL) {
|
|
isc_mem_put(lwresd->mctx, addrs,
|
|
sizeof(rankedaddress) * naddrs);
|
|
return;
|
|
}
|
|
for (i = 0; i < naddrs; i++) {
|
|
result = lwaddr_netaddr_fromlwresaddr(&addrs[i].address,
|
|
&client->addrs[i]);
|
|
INSIST(result == ISC_R_SUCCESS);
|
|
addrs[i].rank = (*order)(&addrs[i].address, arg);
|
|
}
|
|
qsort(addrs, naddrs, sizeof(rankedaddress), addr_compare);
|
|
for (i = 0; i < naddrs; i++) {
|
|
result = lwaddr_lwresaddr_fromnetaddr(&client->addrs[i],
|
|
&addrs[i].address);
|
|
INSIST(result == ISC_R_SUCCESS);
|
|
}
|
|
|
|
isc_mem_put(lwresd->mctx, addrs, sizeof(rankedaddress) * naddrs);
|
|
}
|
|
|
|
static void
|
|
generate_reply(ns_lwdclient_t *client) {
|
|
isc_result_t result;
|
|
int lwres;
|
|
isc_region_t r;
|
|
lwres_buffer_t lwb;
|
|
ns_lwdclientmgr_t *cm;
|
|
|
|
cm = client->clientmgr;
|
|
lwb.base = NULL;
|
|
|
|
ns_lwdclient_log(50, "generating gabn reply for client %p", client);
|
|
|
|
/*
|
|
* We must make certain the client->find is not still active.
|
|
* If it is either the v4 or v6 answer, just set it to NULL and
|
|
* let the cleanup code destroy it. Otherwise, destroy it now.
|
|
*/
|
|
if (client->find == client->v4find || client->find == client->v6find)
|
|
client->find = NULL;
|
|
else
|
|
if (client->find != NULL)
|
|
dns_adb_destroyfind(&client->find);
|
|
|
|
/*
|
|
* perhaps there are some here?
|
|
*/
|
|
if (NEED_V6(client) && client->v4find != NULL)
|
|
client->v6find = client->v4find;
|
|
|
|
/*
|
|
* Run through the finds we have and wire them up to the gabn
|
|
* structure.
|
|
*/
|
|
LWRES_LIST_INIT(client->gabn.addrs);
|
|
if (client->v4find != NULL)
|
|
setup_addresses(client, client->v4find, DNS_ADBFIND_INET);
|
|
if (client->v6find != NULL)
|
|
setup_addresses(client, client->v6find, DNS_ADBFIND_INET6);
|
|
|
|
/*
|
|
* If there are no addresses, try the next element in the search
|
|
* path, if there are any more. Otherwise, fall through into
|
|
* the error handling code below.
|
|
*/
|
|
if (client->gabn.naddrs == 0) {
|
|
do {
|
|
result = ns_lwsearchctx_next(&client->searchctx);
|
|
if (result == ISC_R_SUCCESS) {
|
|
cleanup_gabn(client);
|
|
result = start_find(client);
|
|
if (result == ISC_R_SUCCESS)
|
|
return;
|
|
}
|
|
} while (result == ISC_R_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* Render the packet.
|
|
*/
|
|
client->pkt.recvlength = LWRES_RECVLENGTH;
|
|
client->pkt.authtype = 0; /* XXXMLG */
|
|
client->pkt.authlength = 0;
|
|
|
|
/*
|
|
* If there are no addresses, return failure.
|
|
*/
|
|
if (client->gabn.naddrs != 0)
|
|
client->pkt.result = LWRES_R_SUCCESS;
|
|
else
|
|
client->pkt.result = LWRES_R_NOTFOUND;
|
|
|
|
sort_addresses(client);
|
|
|
|
lwres = lwres_gabnresponse_render(cm->lwctx, &client->gabn,
|
|
&client->pkt, &lwb);
|
|
if (lwres != LWRES_R_SUCCESS)
|
|
goto out;
|
|
|
|
r.base = lwb.base;
|
|
r.length = lwb.used;
|
|
client->sendbuf = r.base;
|
|
client->sendlength = r.length;
|
|
result = ns_lwdclient_sendreply(client, &r);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto out;
|
|
|
|
NS_LWDCLIENT_SETSEND(client);
|
|
|
|
/*
|
|
* All done!
|
|
*/
|
|
cleanup_gabn(client);
|
|
|
|
return;
|
|
|
|
out:
|
|
cleanup_gabn(client);
|
|
|
|
if (lwb.base != NULL)
|
|
lwres_context_freemem(client->clientmgr->lwctx,
|
|
lwb.base, lwb.length);
|
|
|
|
ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
|
|
}
|
|
|
|
/*
|
|
* Take the current real name, move it to an alias slot (if any are
|
|
* open) then put this new name in as the real name for the target.
|
|
*
|
|
* Return success if it can be rendered, otherwise failure. Note that
|
|
* not having enough alias slots open is NOT a failure.
|
|
*/
|
|
static isc_result_t
|
|
add_alias(ns_lwdclient_t *client) {
|
|
isc_buffer_t b;
|
|
isc_result_t result;
|
|
isc_uint16_t naliases;
|
|
|
|
b = client->recv_buffer;
|
|
|
|
/*
|
|
* Render the new name to the buffer.
|
|
*/
|
|
result = dns_name_totext(dns_fixedname_name(&client->target_name),
|
|
ISC_TRUE, &client->recv_buffer);
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
|
|
/*
|
|
* Are there any open slots?
|
|
*/
|
|
naliases = client->gabn.naliases;
|
|
if (naliases < LWRES_MAX_ALIASES) {
|
|
client->gabn.aliases[naliases] = client->gabn.realname;
|
|
client->gabn.aliaslen[naliases] = client->gabn.realnamelen;
|
|
client->gabn.naliases++;
|
|
}
|
|
|
|
/*
|
|
* Save this name away as the current real name.
|
|
*/
|
|
client->gabn.realname = (char *)(b.base) + b.used;
|
|
client->gabn.realnamelen = client->recv_buffer.used - b.used;
|
|
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
static isc_result_t
|
|
store_realname(ns_lwdclient_t *client) {
|
|
isc_buffer_t b;
|
|
isc_result_t result;
|
|
dns_name_t *tname;
|
|
|
|
b = client->recv_buffer;
|
|
|
|
tname = dns_fixedname_name(&client->target_name);
|
|
result = ns_lwsearchctx_current(&client->searchctx, tname);
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
|
|
/*
|
|
* Render the new name to the buffer.
|
|
*/
|
|
result = dns_name_totext(tname, ISC_TRUE, &client->recv_buffer);
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
|
|
/*
|
|
* Save this name away as the current real name.
|
|
*/
|
|
client->gabn.realname = (char *) b.base + b.used;
|
|
client->gabn.realnamelen = client->recv_buffer.used - b.used;
|
|
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
static void
|
|
process_gabn_finddone(isc_task_t *task, isc_event_t *ev) {
|
|
ns_lwdclient_t *client = ev->ev_arg;
|
|
isc_eventtype_t evtype;
|
|
isc_boolean_t claimed;
|
|
|
|
ns_lwdclient_log(50, "find done for task %p, client %p", task, client);
|
|
|
|
evtype = ev->ev_type;
|
|
isc_event_free(&ev);
|
|
|
|
/*
|
|
* No more info to be had? If so, we have all the good stuff
|
|
* right now, so we can render things.
|
|
*/
|
|
claimed = ISC_FALSE;
|
|
if (evtype == DNS_EVENT_ADBNOMOREADDRESSES) {
|
|
if (NEED_V4(client)) {
|
|
client->v4find = client->find;
|
|
claimed = ISC_TRUE;
|
|
}
|
|
if (NEED_V6(client)) {
|
|
client->v6find = client->find;
|
|
claimed = ISC_TRUE;
|
|
}
|
|
if (client->find != NULL) {
|
|
if (claimed)
|
|
client->find = NULL;
|
|
else
|
|
dns_adb_destroyfind(&client->find);
|
|
|
|
}
|
|
generate_reply(client);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* We probably don't need this find anymore. We're either going to
|
|
* reissue it, or an error occurred. Either way, we're done with
|
|
* it.
|
|
*/
|
|
if ((client->find != client->v4find)
|
|
&& (client->find != client->v6find)) {
|
|
dns_adb_destroyfind(&client->find);
|
|
} else {
|
|
client->find = NULL;
|
|
}
|
|
|
|
/*
|
|
* We have some new information we can gather. Run off and fetch
|
|
* it.
|
|
*/
|
|
if (evtype == DNS_EVENT_ADBMOREADDRESSES) {
|
|
restart_find(client);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* An error or other strangeness happened. Drop this query.
|
|
*/
|
|
cleanup_gabn(client);
|
|
ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
|
|
}
|
|
|
|
static void
|
|
restart_find(ns_lwdclient_t *client) {
|
|
unsigned int options;
|
|
isc_result_t result;
|
|
isc_boolean_t claimed;
|
|
|
|
ns_lwdclient_log(50, "starting find for client %p", client);
|
|
|
|
/*
|
|
* Issue a find for the name contained in the request. We won't
|
|
* set the bit that says "anything is good enough" -- we want it
|
|
* all.
|
|
*/
|
|
options = 0;
|
|
options |= DNS_ADBFIND_WANTEVENT;
|
|
options |= DNS_ADBFIND_RETURNLAME;
|
|
|
|
/*
|
|
* Set the bits up here to mark that we want this address family
|
|
* and that we do not currently have a find pending. We will
|
|
* set that bit again below if it turns out we will get an event.
|
|
*/
|
|
if (NEED_V4(client))
|
|
options |= DNS_ADBFIND_INET;
|
|
if (NEED_V6(client))
|
|
options |= DNS_ADBFIND_INET6;
|
|
|
|
find_again:
|
|
INSIST(client->find == NULL);
|
|
result = dns_adb_createfind(client->clientmgr->view->adb,
|
|
client->clientmgr->task,
|
|
process_gabn_finddone, client,
|
|
dns_fixedname_name(&client->target_name),
|
|
dns_rootname, 0, options, 0,
|
|
dns_fixedname_name(&client->target_name),
|
|
client->clientmgr->view->dstport,
|
|
&client->find);
|
|
|
|
/*
|
|
* Did we get an alias? If so, save it and re-issue the query.
|
|
*/
|
|
if (result == DNS_R_ALIAS) {
|
|
ns_lwdclient_log(50, "found alias, restarting query");
|
|
dns_adb_destroyfind(&client->find);
|
|
cleanup_gabn(client);
|
|
result = add_alias(client);
|
|
if (result != ISC_R_SUCCESS) {
|
|
ns_lwdclient_log(50,
|
|
"out of buffer space adding alias");
|
|
ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
|
|
return;
|
|
}
|
|
goto find_again;
|
|
}
|
|
|
|
ns_lwdclient_log(50, "find returned %d (%s)", result,
|
|
isc_result_totext(result));
|
|
|
|
/*
|
|
* Did we get an error?
|
|
*/
|
|
if (result != ISC_R_SUCCESS) {
|
|
if (client->find != NULL)
|
|
dns_adb_destroyfind(&client->find);
|
|
cleanup_gabn(client);
|
|
ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
|
|
return;
|
|
}
|
|
|
|
claimed = ISC_FALSE;
|
|
|
|
/*
|
|
* Did we get our answer to V4 addresses?
|
|
*/
|
|
if (NEED_V4(client)
|
|
&& ((client->find->query_pending & DNS_ADBFIND_INET) == 0)) {
|
|
ns_lwdclient_log(50, "client %p ipv4 satisfied by find %p",
|
|
client, client->find);
|
|
claimed = ISC_TRUE;
|
|
client->v4find = client->find;
|
|
}
|
|
|
|
/*
|
|
* Did we get our answer to V6 addresses?
|
|
*/
|
|
if (NEED_V6(client)
|
|
&& ((client->find->query_pending & DNS_ADBFIND_INET6) == 0)) {
|
|
ns_lwdclient_log(50, "client %p ipv6 satisfied by find %p",
|
|
client, client->find);
|
|
claimed = ISC_TRUE;
|
|
client->v6find = client->find;
|
|
}
|
|
|
|
/*
|
|
* If we're going to get an event, set our internal pending flag
|
|
* and return. When we get an event back we'll do the right
|
|
* thing, basically by calling this function again, perhaps with a
|
|
* new target name.
|
|
*
|
|
* If we have both v4 and v6, and we are still getting an event,
|
|
* we have a programming error, so die hard.
|
|
*/
|
|
if ((client->find->options & DNS_ADBFIND_WANTEVENT) != 0) {
|
|
ns_lwdclient_log(50, "event will be sent");
|
|
INSIST(client->v4find == NULL || client->v6find == NULL);
|
|
return;
|
|
}
|
|
ns_lwdclient_log(50, "no event will be sent");
|
|
if (claimed)
|
|
client->find = NULL;
|
|
else
|
|
dns_adb_destroyfind(&client->find);
|
|
|
|
/*
|
|
* We seem to have everything we asked for, or at least we are
|
|
* able to respond with things we've learned.
|
|
*/
|
|
|
|
generate_reply(client);
|
|
}
|
|
|
|
static isc_result_t
|
|
start_find(ns_lwdclient_t *client) {
|
|
isc_result_t result;
|
|
|
|
/*
|
|
* Initialize the real name and alias arrays in the reply we're
|
|
* going to build up.
|
|
*/
|
|
init_gabn(client);
|
|
|
|
result = store_realname(client);
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
restart_find(client);
|
|
return (ISC_R_SUCCESS);
|
|
|
|
}
|
|
|
|
static void
|
|
init_gabn(ns_lwdclient_t *client) {
|
|
int i;
|
|
|
|
/*
|
|
* Initialize the real name and alias arrays in the reply we're
|
|
* going to build up.
|
|
*/
|
|
for (i = 0; i < LWRES_MAX_ALIASES; i++) {
|
|
client->aliases[i] = NULL;
|
|
client->aliaslen[i] = 0;
|
|
}
|
|
for (i = 0; i < LWRES_MAX_ADDRS; i++) {
|
|
client->addrs[i].family = 0;
|
|
client->addrs[i].length = 0;
|
|
memset(client->addrs[i].address, 0, LWRES_ADDR_MAXLEN);
|
|
LWRES_LINK_INIT(&client->addrs[i], link);
|
|
}
|
|
|
|
client->gabn.naliases = 0;
|
|
client->gabn.naddrs = 0;
|
|
client->gabn.realname = NULL;
|
|
client->gabn.aliases = client->aliases;
|
|
client->gabn.realnamelen = 0;
|
|
client->gabn.aliaslen = client->aliaslen;
|
|
LWRES_LIST_INIT(client->gabn.addrs);
|
|
client->gabn.base = NULL;
|
|
client->gabn.baselen = 0;
|
|
|
|
/*
|
|
* Set up the internal buffer to point to the receive region.
|
|
*/
|
|
isc_buffer_init(&client->recv_buffer, client->buffer, LWRES_RECVLENGTH);
|
|
}
|
|
|
|
/*
|
|
* When we are called, we can be assured that:
|
|
*
|
|
* client->sockaddr contains the address we need to reply to,
|
|
*
|
|
* client->pkt contains the packet header data,
|
|
*
|
|
* the packet "checks out" overall -- any MD5 hashes or crypto
|
|
* bits have been verified,
|
|
*
|
|
* "b" points to the remaining data after the packet header
|
|
* was parsed off.
|
|
*
|
|
* We are in a the RECVDONE state.
|
|
*
|
|
* From this state we will enter the SEND state if we happen to have
|
|
* everything we need or we need to return an error packet, or to the
|
|
* FINDWAIT state if we need to look things up.
|
|
*/
|
|
void
|
|
ns_lwdclient_processgabn(ns_lwdclient_t *client, lwres_buffer_t *b) {
|
|
isc_result_t result;
|
|
lwres_gabnrequest_t *req;
|
|
ns_lwdclientmgr_t *cm;
|
|
isc_buffer_t namebuf;
|
|
|
|
REQUIRE(NS_LWDCLIENT_ISRECVDONE(client));
|
|
|
|
cm = client->clientmgr;
|
|
req = NULL;
|
|
|
|
result = lwres_gabnrequest_parse(client->clientmgr->lwctx,
|
|
b, &client->pkt, &req);
|
|
if (result != LWRES_R_SUCCESS)
|
|
goto out;
|
|
if (req->name == NULL)
|
|
goto out;
|
|
|
|
isc_buffer_init(&namebuf, req->name, req->namelen);
|
|
isc_buffer_add(&namebuf, req->namelen);
|
|
|
|
dns_fixedname_init(&client->target_name);
|
|
dns_fixedname_init(&client->query_name);
|
|
result = dns_name_fromtext(dns_fixedname_name(&client->query_name),
|
|
&namebuf, NULL, ISC_FALSE, NULL);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto out;
|
|
ns_lwsearchctx_init(&client->searchctx,
|
|
cm->listener->manager->search,
|
|
dns_fixedname_name(&client->query_name),
|
|
cm->listener->manager->ndots);
|
|
ns_lwsearchctx_first(&client->searchctx);
|
|
|
|
client->find_wanted = req->addrtypes;
|
|
ns_lwdclient_log(50, "client %p looking for addrtypes %08x",
|
|
client, client->find_wanted);
|
|
|
|
/*
|
|
* We no longer need to keep this around.
|
|
*/
|
|
lwres_gabnrequest_free(client->clientmgr->lwctx, &req);
|
|
|
|
/*
|
|
* Start the find.
|
|
*/
|
|
result = start_find(client);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto out;
|
|
|
|
return;
|
|
|
|
/*
|
|
* We're screwed. Return an error packet to our caller.
|
|
*/
|
|
out:
|
|
if (req != NULL)
|
|
lwres_gabnrequest_free(client->clientmgr->lwctx, &req);
|
|
|
|
ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
|
|
}
|