lwdgabn.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2004-2007, 2009  Internet Systems Consortium, Inc. ("ISC")
00003  * Copyright (C) 2000, 2001  Internet Software Consortium.
00004  *
00005  * Permission to use, copy, modify, and/or distribute this software for any
00006  * purpose with or without fee is hereby granted, provided that the above
00007  * copyright notice and this permission notice appear in all copies.
00008  *
00009  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
00010  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00011  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
00012  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00013  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00014  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00015  * PERFORMANCE OF THIS SOFTWARE.
00016  */
00017 
00018 /* $Id: lwdgabn.c,v 1.24 2009/09/02 23:48:01 tbox Exp $ */
00019 
00020 /*! \file */
00021 
00022 #include <config.h>
00023 
00024 #include <stdlib.h>
00025 
00026 #include <isc/netaddr.h>
00027 #include <isc/sockaddr.h>
00028 #include <isc/socket.h>
00029 #include <isc/string.h>         /* Required for HP/UX (and others?) */
00030 #include <isc/util.h>
00031 
00032 #include <dns/adb.h>
00033 #include <dns/events.h>
00034 #include <dns/result.h>
00035 
00036 #include <named/types.h>
00037 #include <named/lwaddr.h>
00038 #include <named/lwdclient.h>
00039 #include <named/lwresd.h>
00040 #include <named/lwsearch.h>
00041 #include <named/sortlist.h>
00042 
00043 #define NEED_V4(c)      ((((c)->find_wanted & LWRES_ADDRTYPE_V4) != 0) \
00044                          && ((c)->v4find == NULL))
00045 #define NEED_V6(c)      ((((c)->find_wanted & LWRES_ADDRTYPE_V6) != 0) \
00046                          && ((c)->v6find == NULL))
00047 
00048 static isc_result_t start_find(ns_lwdclient_t *);
00049 static void restart_find(ns_lwdclient_t *);
00050 static void init_gabn(ns_lwdclient_t *);
00051 
00052 /*%
00053  * Destroy any finds.  This can be used to "start over from scratch" and
00054  * should only be called when events are _not_ being generated by the finds.
00055  */
00056 static void
00057 cleanup_gabn(ns_lwdclient_t *client) {
00058         ns_lwdclient_log(50, "cleaning up client %p", client);
00059 
00060         if (client->v6find != NULL) {
00061                 if (client->v6find == client->v4find)
00062                         client->v6find = NULL;
00063                 else
00064                         dns_adb_destroyfind(&client->v6find);
00065         }
00066         if (client->v4find != NULL)
00067                 dns_adb_destroyfind(&client->v4find);
00068 }
00069 
00070 static void
00071 setup_addresses(ns_lwdclient_t *client, dns_adbfind_t *find, unsigned int at) {
00072         dns_adbaddrinfo_t *ai;
00073         lwres_addr_t *addr;
00074         int af;
00075         const struct sockaddr *sa;
00076         isc_result_t result;
00077 
00078         if (at == DNS_ADBFIND_INET)
00079                 af = AF_INET;
00080         else
00081                 af = AF_INET6;
00082 
00083         ai = ISC_LIST_HEAD(find->list);
00084         while (ai != NULL && client->gabn.naddrs < LWRES_MAX_ADDRS) {
00085                 sa = &ai->sockaddr.type.sa;
00086                 if (sa->sa_family != af)
00087                         goto next;
00088 
00089                 addr = &client->addrs[client->gabn.naddrs];
00090 
00091                 result = lwaddr_lwresaddr_fromsockaddr(addr, &ai->sockaddr);
00092                 if (result != ISC_R_SUCCESS)
00093                         goto next;
00094 
00095                 ns_lwdclient_log(50, "adding address %p, family %d, length %d",
00096                                  addr->address, addr->family, addr->length);
00097 
00098                 client->gabn.naddrs++;
00099                 REQUIRE(!LWRES_LINK_LINKED(addr, link));
00100                 LWRES_LIST_APPEND(client->gabn.addrs, addr, link);
00101 
00102         next:
00103                 ai = ISC_LIST_NEXT(ai, publink);
00104         }
00105 }
00106 
00107 typedef struct {
00108         isc_netaddr_t address;
00109         int rank;
00110 } rankedaddress;
00111 
00112 static int
00113 addr_compare(const void *av, const void *bv) {
00114         const rankedaddress *a = (const rankedaddress *) av;
00115         const rankedaddress *b = (const rankedaddress *) bv;
00116         return (a->rank - b->rank);
00117 }
00118 
00119 static void
00120 sort_addresses(ns_lwdclient_t *client) {
00121         unsigned int naddrs;
00122         rankedaddress *addrs;
00123         isc_netaddr_t remote;
00124         dns_addressorderfunc_t order;
00125         const void *arg;
00126         ns_lwresd_t *lwresd = client->clientmgr->listener->manager;
00127         unsigned int i;
00128         isc_result_t result;
00129 
00130         naddrs = client->gabn.naddrs;
00131 
00132         if (naddrs <= 1 || lwresd->view->sortlist == NULL)
00133                 return;
00134 
00135         addrs = isc_mem_get(lwresd->mctx, sizeof(rankedaddress) * naddrs);
00136         if (addrs == NULL)
00137                 return;
00138 
00139         isc_netaddr_fromsockaddr(&remote, &client->address);
00140         ns_sortlist_byaddrsetup(lwresd->view->sortlist,
00141                                 &remote, &order, &arg);
00142         if (order == NULL) {
00143                 isc_mem_put(lwresd->mctx, addrs,
00144                             sizeof(rankedaddress) * naddrs);
00145                 return;
00146         }
00147         for (i = 0; i < naddrs; i++) {
00148                 result = lwaddr_netaddr_fromlwresaddr(&addrs[i].address,
00149                                                       &client->addrs[i]);
00150                 INSIST(result == ISC_R_SUCCESS);
00151                 addrs[i].rank = (*order)(&addrs[i].address, arg);
00152         }
00153         qsort(addrs, naddrs, sizeof(rankedaddress), addr_compare);
00154         for (i = 0; i < naddrs; i++) {
00155                 result = lwaddr_lwresaddr_fromnetaddr(&client->addrs[i],
00156                                                       &addrs[i].address);
00157                 INSIST(result == ISC_R_SUCCESS);
00158         }
00159 
00160         isc_mem_put(lwresd->mctx, addrs, sizeof(rankedaddress) * naddrs);
00161 }
00162 
00163 static void
00164 generate_reply(ns_lwdclient_t *client) {
00165         isc_result_t result;
00166         int lwres;
00167         isc_region_t r;
00168         lwres_buffer_t lwb;
00169         ns_lwdclientmgr_t *cm;
00170 
00171         cm = client->clientmgr;
00172         lwb.base = NULL;
00173 
00174         ns_lwdclient_log(50, "generating gabn reply for client %p", client);
00175 
00176         /*
00177          * We must make certain the client->find is not still active.
00178          * If it is either the v4 or v6 answer, just set it to NULL and
00179          * let the cleanup code destroy it.  Otherwise, destroy it now.
00180          */
00181         if (client->find == client->v4find || client->find == client->v6find)
00182                 client->find = NULL;
00183         else
00184                 if (client->find != NULL)
00185                         dns_adb_destroyfind(&client->find);
00186 
00187         /*
00188          * perhaps there are some here?
00189          */
00190         if (NEED_V6(client) && client->v4find != NULL)
00191                 client->v6find = client->v4find;
00192 
00193         /*
00194          * Run through the finds we have and wire them up to the gabn
00195          * structure.
00196          */
00197         LWRES_LIST_INIT(client->gabn.addrs);
00198         if (client->v4find != NULL)
00199                 setup_addresses(client, client->v4find, DNS_ADBFIND_INET);
00200         if (client->v6find != NULL)
00201                 setup_addresses(client, client->v6find, DNS_ADBFIND_INET6);
00202 
00203         /*
00204          * If there are no addresses, try the next element in the search
00205          * path, if there are any more.  Otherwise, fall through into
00206          * the error handling code below.
00207          */
00208         if (client->gabn.naddrs == 0) {
00209                 do {
00210                         result = ns_lwsearchctx_next(&client->searchctx);
00211                         if (result == ISC_R_SUCCESS) {
00212                                 cleanup_gabn(client);
00213                                 result = start_find(client);
00214                                 if (result == ISC_R_SUCCESS)
00215                                         return;
00216                         }
00217                 } while (result == ISC_R_SUCCESS);
00218         }
00219 
00220         /*
00221          * Render the packet.
00222          */
00223         client->pkt.recvlength = LWRES_RECVLENGTH;
00224         client->pkt.authtype = 0; /* XXXMLG */
00225         client->pkt.authlength = 0;
00226 
00227         /*
00228          * If there are no addresses, return failure.
00229          */
00230         if (client->gabn.naddrs != 0)
00231                 client->pkt.result = LWRES_R_SUCCESS;
00232         else
00233                 client->pkt.result = LWRES_R_NOTFOUND;
00234 
00235         sort_addresses(client);
00236 
00237         lwres = lwres_gabnresponse_render(cm->lwctx, &client->gabn,
00238                                           &client->pkt, &lwb);
00239         if (lwres != LWRES_R_SUCCESS)
00240                 goto out;
00241 
00242         r.base = lwb.base;
00243         r.length = lwb.used;
00244         client->sendbuf = r.base;
00245         client->sendlength = r.length;
00246         result = ns_lwdclient_sendreply(client, &r);
00247         if (result != ISC_R_SUCCESS)
00248                 goto out;
00249 
00250         NS_LWDCLIENT_SETSEND(client);
00251 
00252         /*
00253          * All done!
00254          */
00255         cleanup_gabn(client);
00256 
00257         return;
00258 
00259  out:
00260         cleanup_gabn(client);
00261 
00262         if (lwb.base != NULL)
00263                 lwres_context_freemem(client->clientmgr->lwctx,
00264                                       lwb.base, lwb.length);
00265 
00266         ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
00267 }
00268 
00269 /*
00270  * Take the current real name, move it to an alias slot (if any are
00271  * open) then put this new name in as the real name for the target.
00272  *
00273  * Return success if it can be rendered, otherwise failure.  Note that
00274  * not having enough alias slots open is NOT a failure.
00275  */
00276 static isc_result_t
00277 add_alias(ns_lwdclient_t *client) {
00278         isc_buffer_t b;
00279         isc_result_t result;
00280         isc_uint16_t naliases;
00281 
00282         b = client->recv_buffer;
00283 
00284         /*
00285          * Render the new name to the buffer.
00286          */
00287         result = dns_name_totext(dns_fixedname_name(&client->target_name),
00288                                  ISC_TRUE, &client->recv_buffer);
00289         if (result != ISC_R_SUCCESS)
00290                 return (result);
00291 
00292         /*
00293          * Are there any open slots?
00294          */
00295         naliases = client->gabn.naliases;
00296         if (naliases < LWRES_MAX_ALIASES) {
00297                 client->gabn.aliases[naliases] = client->gabn.realname;
00298                 client->gabn.aliaslen[naliases] = client->gabn.realnamelen;
00299                 client->gabn.naliases++;
00300         }
00301 
00302         /*
00303          * Save this name away as the current real name.
00304          */
00305         client->gabn.realname = (char *)(b.base) + b.used;
00306         client->gabn.realnamelen = client->recv_buffer.used - b.used;
00307 
00308         return (ISC_R_SUCCESS);
00309 }
00310 
00311 static isc_result_t
00312 store_realname(ns_lwdclient_t *client) {
00313         isc_buffer_t b;
00314         isc_result_t result;
00315         dns_name_t *tname;
00316 
00317         b = client->recv_buffer;
00318 
00319         tname = dns_fixedname_name(&client->target_name);
00320         result = ns_lwsearchctx_current(&client->searchctx, tname);
00321         if (result != ISC_R_SUCCESS)
00322                 return (result);
00323 
00324         /*
00325          * Render the new name to the buffer.
00326          */
00327         result = dns_name_totext(tname, ISC_TRUE, &client->recv_buffer);
00328         if (result != ISC_R_SUCCESS)
00329                 return (result);
00330 
00331         /*
00332          * Save this name away as the current real name.
00333          */
00334         client->gabn.realname = (char *) b.base + b.used;
00335         client->gabn.realnamelen = client->recv_buffer.used - b.used;
00336 
00337         return (ISC_R_SUCCESS);
00338 }
00339 
00340 static void
00341 process_gabn_finddone(isc_task_t *task, isc_event_t *ev) {
00342         ns_lwdclient_t *client = ev->ev_arg;
00343         isc_eventtype_t evtype;
00344         isc_boolean_t claimed;
00345 
00346         ns_lwdclient_log(50, "find done for task %p, client %p", task, client);
00347 
00348         evtype = ev->ev_type;
00349         isc_event_free(&ev);
00350 
00351         /*
00352          * No more info to be had?  If so, we have all the good stuff
00353          * right now, so we can render things.
00354          */
00355         claimed = ISC_FALSE;
00356         if (evtype == DNS_EVENT_ADBNOMOREADDRESSES) {
00357                 if (NEED_V4(client)) {
00358                         client->v4find = client->find;
00359                         claimed = ISC_TRUE;
00360                 }
00361                 if (NEED_V6(client)) {
00362                         client->v6find = client->find;
00363                         claimed = ISC_TRUE;
00364                 }
00365                 if (client->find != NULL) {
00366                         if (claimed)
00367                                 client->find = NULL;
00368                         else
00369                                 dns_adb_destroyfind(&client->find);
00370 
00371                 }
00372                 generate_reply(client);
00373                 return;
00374         }
00375 
00376         /*
00377          * We probably don't need this find anymore.  We're either going to
00378          * reissue it, or an error occurred.  Either way, we're done with
00379          * it.
00380          */
00381         if ((client->find != client->v4find)
00382             && (client->find != client->v6find)) {
00383                 dns_adb_destroyfind(&client->find);
00384         } else {
00385                 client->find = NULL;
00386         }
00387 
00388         /*
00389          * We have some new information we can gather.  Run off and fetch
00390          * it.
00391          */
00392         if (evtype == DNS_EVENT_ADBMOREADDRESSES) {
00393                 restart_find(client);
00394                 return;
00395         }
00396 
00397         /*
00398          * An error or other strangeness happened.  Drop this query.
00399          */
00400         cleanup_gabn(client);
00401         ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
00402 }
00403 
00404 static void
00405 restart_find(ns_lwdclient_t *client) {
00406         unsigned int options;
00407         isc_result_t result;
00408         isc_boolean_t claimed;
00409 
00410         ns_lwdclient_log(50, "starting find for client %p", client);
00411 
00412         /*
00413          * Issue a find for the name contained in the request.  We won't
00414          * set the bit that says "anything is good enough" -- we want it
00415          * all.
00416          */
00417         options = 0;
00418         options |= DNS_ADBFIND_WANTEVENT;
00419         options |= DNS_ADBFIND_RETURNLAME;
00420 
00421         /*
00422          * Set the bits up here to mark that we want this address family
00423          * and that we do not currently have a find pending.  We will
00424          * set that bit again below if it turns out we will get an event.
00425          */
00426         if (NEED_V4(client))
00427                 options |= DNS_ADBFIND_INET;
00428         if (NEED_V6(client))
00429                 options |= DNS_ADBFIND_INET6;
00430 
00431  find_again:
00432         INSIST(client->find == NULL);
00433         result = dns_adb_createfind(client->clientmgr->view->adb,
00434                                     client->clientmgr->task,
00435                                     process_gabn_finddone, client,
00436                                     dns_fixedname_name(&client->target_name),
00437                                     dns_rootname, 0, options, 0,
00438                                     dns_fixedname_name(&client->target_name),
00439                                     client->clientmgr->view->dstport,
00440                                     &client->find);
00441 
00442         /*
00443          * Did we get an alias?  If so, save it and re-issue the query.
00444          */
00445         if (result == DNS_R_ALIAS) {
00446                 ns_lwdclient_log(50, "found alias, restarting query");
00447                 dns_adb_destroyfind(&client->find);
00448                 cleanup_gabn(client);
00449                 result = add_alias(client);
00450                 if (result != ISC_R_SUCCESS) {
00451                         ns_lwdclient_log(50,
00452                                          "out of buffer space adding alias");
00453                         ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
00454                         return;
00455                 }
00456                 goto find_again;
00457         }
00458 
00459         ns_lwdclient_log(50, "find returned %d (%s)", result,
00460                          isc_result_totext(result));
00461 
00462         /*
00463          * Did we get an error?
00464          */
00465         if (result != ISC_R_SUCCESS) {
00466                 if (client->find != NULL)
00467                         dns_adb_destroyfind(&client->find);
00468                 cleanup_gabn(client);
00469                 ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
00470                 return;
00471         }
00472 
00473         claimed = ISC_FALSE;
00474 
00475         /*
00476          * Did we get our answer to V4 addresses?
00477          */
00478         if (NEED_V4(client)
00479             && ((client->find->query_pending & DNS_ADBFIND_INET) == 0)) {
00480                 ns_lwdclient_log(50, "client %p ipv4 satisfied by find %p",
00481                                  client, client->find);
00482                 claimed = ISC_TRUE;
00483                 client->v4find = client->find;
00484         }
00485 
00486         /*
00487          * Did we get our answer to V6 addresses?
00488          */
00489         if (NEED_V6(client)
00490             && ((client->find->query_pending & DNS_ADBFIND_INET6) == 0)) {
00491                 ns_lwdclient_log(50, "client %p ipv6 satisfied by find %p",
00492                                  client, client->find);
00493                 claimed = ISC_TRUE;
00494                 client->v6find = client->find;
00495         }
00496 
00497         /*
00498          * If we're going to get an event, set our internal pending flag
00499          * and return.  When we get an event back we'll do the right
00500          * thing, basically by calling this function again, perhaps with a
00501          * new target name.
00502          *
00503          * If we have both v4 and v6, and we are still getting an event,
00504          * we have a programming error, so die hard.
00505          */
00506         if ((client->find->options & DNS_ADBFIND_WANTEVENT) != 0) {
00507                 ns_lwdclient_log(50, "event will be sent");
00508                 INSIST(client->v4find == NULL || client->v6find == NULL);
00509                 return;
00510         }
00511         ns_lwdclient_log(50, "no event will be sent");
00512         if (claimed)
00513                 client->find = NULL;
00514         else
00515                 dns_adb_destroyfind(&client->find);
00516 
00517         /*
00518          * We seem to have everything we asked for, or at least we are
00519          * able to respond with things we've learned.
00520          */
00521 
00522         generate_reply(client);
00523 }
00524 
00525 static isc_result_t
00526 start_find(ns_lwdclient_t *client) {
00527         isc_result_t result;
00528 
00529         /*
00530          * Initialize the real name and alias arrays in the reply we're
00531          * going to build up.
00532          */
00533         init_gabn(client);
00534 
00535         result = store_realname(client);
00536         if (result != ISC_R_SUCCESS)
00537                 return (result);
00538         restart_find(client);
00539         return (ISC_R_SUCCESS);
00540 
00541 }
00542 
00543 static void
00544 init_gabn(ns_lwdclient_t *client) {
00545         int i;
00546 
00547         /*
00548          * Initialize the real name and alias arrays in the reply we're
00549          * going to build up.
00550          */
00551         for (i = 0; i < LWRES_MAX_ALIASES; i++) {
00552                 client->aliases[i] = NULL;
00553                 client->aliaslen[i] = 0;
00554         }
00555         for (i = 0; i < LWRES_MAX_ADDRS; i++) {
00556                 client->addrs[i].family = 0;
00557                 client->addrs[i].length = 0;
00558                 memset(client->addrs[i].address, 0, LWRES_ADDR_MAXLEN);
00559                 LWRES_LINK_INIT(&client->addrs[i], link);
00560         }
00561 
00562         client->gabn.naliases = 0;
00563         client->gabn.naddrs = 0;
00564         client->gabn.realname = NULL;
00565         client->gabn.aliases = client->aliases;
00566         client->gabn.realnamelen = 0;
00567         client->gabn.aliaslen = client->aliaslen;
00568         LWRES_LIST_INIT(client->gabn.addrs);
00569         client->gabn.base = NULL;
00570         client->gabn.baselen = 0;
00571 
00572         /*
00573          * Set up the internal buffer to point to the receive region.
00574          */
00575         isc_buffer_init(&client->recv_buffer, client->buffer, LWRES_RECVLENGTH);
00576 }
00577 
00578 /*
00579  * When we are called, we can be assured that:
00580  *
00581  *      client->sockaddr contains the address we need to reply to,
00582  *
00583  *      client->pkt contains the packet header data,
00584  *
00585  *      the packet "checks out" overall -- any MD5 hashes or crypto
00586  *      bits have been verified,
00587  *
00588  *      "b" points to the remaining data after the packet header
00589  *      was parsed off.
00590  *
00591  *      We are in a the RECVDONE state.
00592  *
00593  * From this state we will enter the SEND state if we happen to have
00594  * everything we need or we need to return an error packet, or to the
00595  * FINDWAIT state if we need to look things up.
00596  */
00597 void
00598 ns_lwdclient_processgabn(ns_lwdclient_t *client, lwres_buffer_t *b) {
00599         isc_result_t result;
00600         lwres_gabnrequest_t *req;
00601         ns_lwdclientmgr_t *cm;
00602         isc_buffer_t namebuf;
00603 
00604         REQUIRE(NS_LWDCLIENT_ISRECVDONE(client));
00605 
00606         cm = client->clientmgr;
00607         req = NULL;
00608 
00609         result = lwres_gabnrequest_parse(client->clientmgr->lwctx,
00610                                          b, &client->pkt, &req);
00611         if (result != LWRES_R_SUCCESS)
00612                 goto out;
00613         if (req->name == NULL)
00614                 goto out;
00615 
00616         isc_buffer_init(&namebuf, req->name, req->namelen);
00617         isc_buffer_add(&namebuf, req->namelen);
00618 
00619         dns_fixedname_init(&client->target_name);
00620         dns_fixedname_init(&client->query_name);
00621         result = dns_name_fromtext(dns_fixedname_name(&client->query_name),
00622                                    &namebuf, NULL, 0, NULL);
00623         if (result != ISC_R_SUCCESS)
00624                 goto out;
00625         ns_lwsearchctx_init(&client->searchctx,
00626                             cm->listener->manager->search,
00627                             dns_fixedname_name(&client->query_name),
00628                             cm->listener->manager->ndots);
00629         ns_lwsearchctx_first(&client->searchctx);
00630 
00631         client->find_wanted = req->addrtypes;
00632         ns_lwdclient_log(50, "client %p looking for addrtypes %08x",
00633                          client, client->find_wanted);
00634 
00635         /*
00636          * We no longer need to keep this around.
00637          */
00638         lwres_gabnrequest_free(client->clientmgr->lwctx, &req);
00639 
00640         /*
00641          * Start the find.
00642          */
00643         result = start_find(client);
00644         if (result != ISC_R_SUCCESS)
00645                 goto out;
00646 
00647         return;
00648 
00649         /*
00650          * We're screwed.  Return an error packet to our caller.
00651          */
00652  out:
00653         if (req != NULL)
00654                 lwres_gabnrequest_free(client->clientmgr->lwctx, &req);
00655 
00656         ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
00657 }

Generated on Tue Apr 28 17:40:54 2015 by Doxygen 1.5.4 for BIND9 Internals 9.11.0pre-alpha