diff options
Diffstat (limited to 'bin/named/lwdgrbn.c')
-rw-r--r-- | bin/named/lwdgrbn.c | 514 |
1 files changed, 514 insertions, 0 deletions
diff --git a/bin/named/lwdgrbn.c b/bin/named/lwdgrbn.c new file mode 100644 index 0000000..407267d --- /dev/null +++ b/bin/named/lwdgrbn.c @@ -0,0 +1,514 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* $Id: lwdgrbn.c,v 1.22 2009/09/02 23:48:01 tbox Exp $ */ + +/*! \file */ + +#include <config.h> + +#include <inttypes.h> + +#include <isc/mem.h> +#include <isc/socket.h> +#include <isc/string.h> /* Required for HP/UX (and others?) */ +#include <isc/util.h> + +#include <dns/db.h> +#include <dns/lookup.h> +#include <dns/rdata.h> +#include <dns/rdataset.h> +#include <dns/rdatasetiter.h> +#include <dns/result.h> +#include <dns/view.h> + +#include <named/types.h> +#include <named/lwdclient.h> +#include <named/lwresd.h> +#include <named/lwsearch.h> + +static void start_lookup(ns_lwdclient_t *); + +static isc_result_t +fill_array(int *pos, dns_rdataset_t *rdataset, + int size, unsigned char **rdatas, uint16_t *rdatalen) +{ + dns_rdata_t rdata; + isc_result_t result; + isc_region_t r; + + UNUSED(size); + + dns_rdata_init(&rdata); + for (result = dns_rdataset_first(rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(rdataset)) + { + INSIST(*pos < size); + dns_rdataset_current(rdataset, &rdata); + dns_rdata_toregion(&rdata, &r); + rdatas[*pos] = r.base; + rdatalen[*pos] = r.length; + dns_rdata_reset(&rdata); + (*pos)++; + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + return (result); +} + +static isc_result_t +iterate_node(lwres_grbnresponse_t *grbn, dns_db_t *db, dns_dbnode_t *node, + isc_mem_t *mctx) +{ + int used = 0, count; + int size = 8, oldsize = 0; + unsigned char **rdatas = NULL, **oldrdatas = NULL, **newrdatas = NULL; + uint16_t *lens = NULL, *oldlens = NULL, *newlens = NULL; + dns_rdatasetiter_t *iter = NULL; + dns_rdataset_t set; + dns_ttl_t ttl = INT32_MAX; + uint32_t flags = LWRDATA_VALIDATED; + isc_result_t result = ISC_R_NOMEMORY; + + result = dns_db_allrdatasets(db, node, NULL, 0, &iter); + if (result != ISC_R_SUCCESS) + goto out; + + rdatas = isc_mem_get(mctx, size * sizeof(*rdatas)); + if (rdatas == NULL) + goto out; + lens = isc_mem_get(mctx, size * sizeof(*lens)); + if (lens == NULL) + goto out; + + for (result = dns_rdatasetiter_first(iter); + result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(iter)) + { + result = ISC_R_NOMEMORY; + dns_rdataset_init(&set); + dns_rdatasetiter_current(iter, &set); + + if (set.type != dns_rdatatype_rrsig) { + dns_rdataset_disassociate(&set); + continue; + } + + count = dns_rdataset_count(&set); + if (used + count > size) { + /* copy & reallocate */ + oldsize = size; + oldrdatas = rdatas; + oldlens = lens; + rdatas = NULL; + lens = NULL; + + size *= 2; + + rdatas = isc_mem_get(mctx, size * sizeof(*rdatas)); + if (rdatas == NULL) + goto out; + lens = isc_mem_get(mctx, size * sizeof(*lens)); + if (lens == NULL) + goto out; + memmove(rdatas, oldrdatas, used * sizeof(*rdatas)); + memmove(lens, oldlens, used * sizeof(*lens)); + isc_mem_put(mctx, oldrdatas, + oldsize * sizeof(*oldrdatas)); + isc_mem_put(mctx, oldlens, oldsize * sizeof(*oldlens)); + oldrdatas = NULL; + oldlens = NULL; + } + if (set.ttl < ttl) + ttl = set.ttl; + if (set.trust != dns_trust_secure) + flags &= (~LWRDATA_VALIDATED); + result = fill_array(&used, &set, size, rdatas, lens); + dns_rdataset_disassociate(&set); + if (result != ISC_R_SUCCESS) + goto out; + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + if (result != ISC_R_SUCCESS) + goto out; + dns_rdatasetiter_destroy(&iter); + + /* + * If necessary, shrink and copy the arrays. + */ + if (size != used) { + result = ISC_R_NOMEMORY; + newrdatas = isc_mem_get(mctx, used * sizeof(*rdatas)); + if (newrdatas == NULL) + goto out; + newlens = isc_mem_get(mctx, used * sizeof(*lens)); + if (newlens == NULL) + goto out; + memmove(newrdatas, rdatas, used * sizeof(*rdatas)); + memmove(newlens, lens, used * sizeof(*lens)); + isc_mem_put(mctx, rdatas, size * sizeof(*rdatas)); + isc_mem_put(mctx, lens, size * sizeof(*lens)); + grbn->rdatas = newrdatas; + grbn->rdatalen = newlens; + } else { + grbn->rdatas = rdatas; + grbn->rdatalen = lens; + } + grbn->nrdatas = used; + grbn->ttl = ttl; + grbn->flags = flags; + return (ISC_R_SUCCESS); + + out: + dns_rdatasetiter_destroy(&iter); + if (rdatas != NULL) + isc_mem_put(mctx, rdatas, size * sizeof(*rdatas)); + if (lens != NULL) + isc_mem_put(mctx, lens, size * sizeof(*lens)); + if (oldrdatas != NULL) + isc_mem_put(mctx, oldrdatas, oldsize * sizeof(*oldrdatas)); + if (oldlens != NULL) + isc_mem_put(mctx, oldlens, oldsize * sizeof(*oldlens)); + if (newrdatas != NULL) + isc_mem_put(mctx, newrdatas, used * sizeof(*newrdatas)); + return (result); +} + +static void +lookup_done(isc_task_t *task, isc_event_t *event) { + ns_lwdclient_t *client; + ns_lwdclientmgr_t *cm; + dns_lookupevent_t *levent; + lwres_buffer_t lwb; + dns_name_t *name; + dns_rdataset_t *rdataset; + dns_rdataset_t *sigrdataset; + isc_result_t result; + lwres_result_t lwresult; + isc_region_t r; + isc_buffer_t b; + lwres_grbnresponse_t *grbn; + int i; + + REQUIRE(event != NULL); + + UNUSED(task); + + lwb.base = NULL; + client = event->ev_arg; + cm = client->clientmgr; + INSIST(client->lookup == (dns_lookup_t *)event->ev_sender); + + levent = (dns_lookupevent_t *)event; + grbn = &client->grbn; + + ns_lwdclient_log(50, "lookup event result = %s", + isc_result_totext(levent->result)); + + result = levent->result; + if (result != ISC_R_SUCCESS) { + dns_lookup_destroy(&client->lookup); + isc_event_free(&event); + levent = NULL; + + switch (result) { + case DNS_R_NXDOMAIN: + case DNS_R_NCACHENXDOMAIN: + result = ns_lwsearchctx_next(&client->searchctx); + if (result != ISC_R_SUCCESS) + lwresult = LWRES_R_NOTFOUND; + else { + start_lookup(client); + return; + } + break; + case DNS_R_NXRRSET: + case DNS_R_NCACHENXRRSET: + lwresult = LWRES_R_TYPENOTFOUND; + break; + default: + lwresult = LWRES_R_FAILURE; + } + ns_lwdclient_errorpktsend(client, lwresult); + return; + } + + name = levent->name; + b = client->recv_buffer; + + grbn->flags = 0; + + grbn->nrdatas = 0; + grbn->rdatas = NULL; + grbn->rdatalen = NULL; + + grbn->nsigs = 0; + grbn->sigs = NULL; + grbn->siglen = NULL; + + result = dns_name_totext(name, true, &client->recv_buffer); + if (result != ISC_R_SUCCESS) + goto out; + grbn->realname = (char *)isc_buffer_used(&b); + grbn->realnamelen = isc_buffer_usedlength(&client->recv_buffer) - + isc_buffer_usedlength(&b); + ns_lwdclient_log(50, "found name '%.*s'", grbn->realnamelen, + grbn->realname); + + grbn->rdclass = cm->view->rdclass; + grbn->rdtype = client->rdtype; + + rdataset = levent->rdataset; + if (rdataset != NULL) { + /* The normal case */ + grbn->nrdatas = dns_rdataset_count(rdataset); + grbn->rdatas = isc_mem_get(cm->mctx, grbn->nrdatas * + sizeof(unsigned char *)); + if (grbn->rdatas == NULL) + goto out; + grbn->rdatalen = isc_mem_get(cm->mctx, grbn->nrdatas * + sizeof(uint16_t)); + if (grbn->rdatalen == NULL) + goto out; + + i = 0; + result = fill_array(&i, rdataset, grbn->nrdatas, grbn->rdatas, + grbn->rdatalen); + if (result != ISC_R_SUCCESS) + goto out; + INSIST(i == grbn->nrdatas); + grbn->ttl = rdataset->ttl; + if (rdataset->trust == dns_trust_secure) + grbn->flags |= LWRDATA_VALIDATED; + } else { + /* The SIG query case */ + result = iterate_node(grbn, levent->db, levent->node, + cm->mctx); + if (result != ISC_R_SUCCESS) + goto out; + } + ns_lwdclient_log(50, "filled in %d rdata%s", grbn->nrdatas, + (grbn->nrdatas == 1) ? "" : "s"); + + sigrdataset = levent->sigrdataset; + if (sigrdataset != NULL) { + grbn->nsigs = dns_rdataset_count(sigrdataset); + grbn->sigs = isc_mem_get(cm->mctx, grbn->nsigs * + sizeof(unsigned char *)); + if (grbn->sigs == NULL) + goto out; + grbn->siglen = isc_mem_get(cm->mctx, grbn->nsigs * + sizeof(uint16_t)); + if (grbn->siglen == NULL) + goto out; + + i = 0; + result = fill_array(&i, sigrdataset, grbn->nsigs, grbn->sigs, + grbn->siglen); + if (result != ISC_R_SUCCESS) + goto out; + INSIST(i == grbn->nsigs); + ns_lwdclient_log(50, "filled in %d signature%s", grbn->nsigs, + (grbn->nsigs == 1) ? "" : "s"); + } + + /* + * Render the packet. + */ + client->pkt.recvlength = LWRES_RECVLENGTH; + client->pkt.authtype = 0; /* XXXMLG */ + client->pkt.authlength = 0; + client->pkt.result = LWRES_R_SUCCESS; + + lwresult = lwres_grbnresponse_render(cm->lwctx, + grbn, &client->pkt, &lwb); + if (lwresult != LWRES_R_SUCCESS) + goto out; + + isc_mem_put(cm->mctx, grbn->rdatas, + grbn->nrdatas * sizeof(unsigned char *)); + isc_mem_put(cm->mctx, grbn->rdatalen, + grbn->nrdatas * sizeof(uint16_t)); + + if (grbn->sigs != NULL) + isc_mem_put(cm->mctx, grbn->sigs, + grbn->nsigs * sizeof(unsigned char *)); + if (grbn->siglen != NULL) + isc_mem_put(cm->mctx, grbn->siglen, + grbn->nsigs * sizeof(uint16_t)); + + 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 out2; + + NS_LWDCLIENT_SETSEND(client); + + dns_lookup_destroy(&client->lookup); + isc_event_free(&event); + + return; + + out: + if (grbn->rdatas != NULL) + isc_mem_put(cm->mctx, grbn->rdatas, + grbn->nrdatas * sizeof(unsigned char *)); + if (grbn->rdatalen != NULL) + isc_mem_put(cm->mctx, grbn->rdatalen, + grbn->nrdatas * sizeof(uint16_t)); + + if (grbn->sigs != NULL) + isc_mem_put(cm->mctx, grbn->sigs, + grbn->nsigs * sizeof(unsigned char *)); + if (grbn->siglen != NULL) + isc_mem_put(cm->mctx, grbn->siglen, + grbn->nsigs * sizeof(uint16_t)); + out2: + if (client->lookup != NULL) + dns_lookup_destroy(&client->lookup); + if (lwb.base != NULL) + lwres_context_freemem(cm->lwctx, lwb.base, lwb.length); + + isc_event_free(&event); + + ns_lwdclient_log(50, "error constructing getrrsetbyname response"); + ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE); +} + +static void +start_lookup(ns_lwdclient_t *client) { + isc_result_t result; + ns_lwdclientmgr_t *cm; + dns_fixedname_t absname; + + cm = client->clientmgr; + + INSIST(client->lookup == NULL); + + dns_fixedname_init(&absname); + + /* + * Perform search across all search domains until success + * is returned. Return in case of failure. + */ + while (ns_lwsearchctx_current(&client->searchctx, + dns_fixedname_name(&absname)) != ISC_R_SUCCESS) { + if (ns_lwsearchctx_next(&client->searchctx) != ISC_R_SUCCESS) { + ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE); + return; + } + } + + result = dns_lookup_create(cm->mctx, + dns_fixedname_name(&absname), + client->rdtype, cm->view, + client->options, cm->task, lookup_done, + client, &client->lookup); + if (result != ISC_R_SUCCESS) { + ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE); + return; + } +} + +static void +init_grbn(ns_lwdclient_t *client) { + client->grbn.rdclass = 0; + client->grbn.rdtype = 0; + client->grbn.ttl = 0; + client->grbn.nrdatas = 0; + client->grbn.realname = NULL; + client->grbn.realnamelen = 0; + client->grbn.rdatas = 0; + client->grbn.rdatalen = 0; + client->grbn.base = NULL; + client->grbn.baselen = 0; + isc_buffer_init(&client->recv_buffer, client->buffer, LWRES_RECVLENGTH); +} + +void +ns_lwdclient_processgrbn(ns_lwdclient_t *client, lwres_buffer_t *b) { + lwres_grbnrequest_t *req; + isc_result_t result; + ns_lwdclientmgr_t *cm; + isc_buffer_t namebuf; + + REQUIRE(NS_LWDCLIENT_ISRECVDONE(client)); + INSIST(client->byaddr == NULL); + + cm = client->clientmgr; + req = NULL; + + result = lwres_grbnrequest_parse(cm->lwctx, + b, &client->pkt, &req); + if (result != LWRES_R_SUCCESS) + goto out; + if (req->name == NULL) + goto out; + + client->options = 0; + if (req->rdclass != cm->view->rdclass) + goto out; + + if (req->rdclass == dns_rdataclass_any || + req->rdtype == dns_rdatatype_any) + goto out; + + client->rdtype = req->rdtype; + + isc_buffer_init(&namebuf, req->name, req->namelen); + isc_buffer_add(&namebuf, req->namelen); + + dns_fixedname_init(&client->query_name); + result = dns_name_fromtext(dns_fixedname_name(&client->query_name), + &namebuf, NULL, 0, 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); + + ns_lwdclient_log(50, "client %p looking for type %d", + client, client->rdtype); + + /* + * We no longer need to keep this around. + */ + lwres_grbnrequest_free(cm->lwctx, &req); + + /* + * Initialize the real name and alias arrays in the reply we're + * going to build up. + */ + init_grbn(client); + + /* + * Start the find. + */ + start_lookup(client); + + return; + + /* + * We're screwed. Return an error packet to our caller. + */ + out: + if (req != NULL) + lwres_grbnrequest_free(cm->lwctx, &req); + + ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE); +} |