Import bind 9.5.2 vendor sources.
[dragonfly.git] / contrib / bind-9.5.2 / bin / named / lwdgabn.c
blobdec1e1a571de115a4761d819d7b6ee2f95511e96
1 /*
2 * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000, 2001 Internet Software Consortium.
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
18 /* $Id: lwdgabn.c,v 1.22 2007/06/19 23:46:59 tbox Exp $ */
20 /*! \file */
22 #include <config.h>
24 #include <stdlib.h>
26 #include <isc/netaddr.h>
27 #include <isc/sockaddr.h>
28 #include <isc/socket.h>
29 #include <isc/string.h> /* Required for HP/UX (and others?) */
30 #include <isc/util.h>
32 #include <dns/adb.h>
33 #include <dns/events.h>
34 #include <dns/result.h>
36 #include <named/types.h>
37 #include <named/lwaddr.h>
38 #include <named/lwdclient.h>
39 #include <named/lwresd.h>
40 #include <named/lwsearch.h>
41 #include <named/sortlist.h>
43 #define NEED_V4(c) ((((c)->find_wanted & LWRES_ADDRTYPE_V4) != 0) \
44 && ((c)->v4find == NULL))
45 #define NEED_V6(c) ((((c)->find_wanted & LWRES_ADDRTYPE_V6) != 0) \
46 && ((c)->v6find == NULL))
48 static isc_result_t start_find(ns_lwdclient_t *);
49 static void restart_find(ns_lwdclient_t *);
50 static void init_gabn(ns_lwdclient_t *);
52 /*%
53 * Destroy any finds. This can be used to "start over from scratch" and
54 * should only be called when events are _not_ being generated by the finds.
56 static void
57 cleanup_gabn(ns_lwdclient_t *client) {
58 ns_lwdclient_log(50, "cleaning up client %p", client);
60 if (client->v6find != NULL) {
61 if (client->v6find == client->v4find)
62 client->v6find = NULL;
63 else
64 dns_adb_destroyfind(&client->v6find);
66 if (client->v4find != NULL)
67 dns_adb_destroyfind(&client->v4find);
70 static void
71 setup_addresses(ns_lwdclient_t *client, dns_adbfind_t *find, unsigned int at) {
72 dns_adbaddrinfo_t *ai;
73 lwres_addr_t *addr;
74 int af;
75 const struct sockaddr *sa;
76 isc_result_t result;
78 if (at == DNS_ADBFIND_INET)
79 af = AF_INET;
80 else
81 af = AF_INET6;
83 ai = ISC_LIST_HEAD(find->list);
84 while (ai != NULL && client->gabn.naddrs < LWRES_MAX_ADDRS) {
85 sa = &ai->sockaddr.type.sa;
86 if (sa->sa_family != af)
87 goto next;
89 addr = &client->addrs[client->gabn.naddrs];
91 result = lwaddr_lwresaddr_fromsockaddr(addr, &ai->sockaddr);
92 if (result != ISC_R_SUCCESS)
93 goto next;
95 ns_lwdclient_log(50, "adding address %p, family %d, length %d",
96 addr->address, addr->family, addr->length);
98 client->gabn.naddrs++;
99 REQUIRE(!LWRES_LINK_LINKED(addr, link));
100 LWRES_LIST_APPEND(client->gabn.addrs, addr, link);
102 next:
103 ai = ISC_LIST_NEXT(ai, publink);
107 typedef struct {
108 isc_netaddr_t address;
109 int rank;
110 } rankedaddress;
112 static int
113 addr_compare(const void *av, const void *bv) {
114 const rankedaddress *a = (const rankedaddress *) av;
115 const rankedaddress *b = (const rankedaddress *) bv;
116 return (a->rank - b->rank);
119 static void
120 sort_addresses(ns_lwdclient_t *client) {
121 unsigned int naddrs;
122 rankedaddress *addrs;
123 isc_netaddr_t remote;
124 dns_addressorderfunc_t order;
125 const void *arg;
126 ns_lwresd_t *lwresd = client->clientmgr->listener->manager;
127 unsigned int i;
128 isc_result_t result;
130 naddrs = client->gabn.naddrs;
132 if (naddrs <= 1 || lwresd->view->sortlist == NULL)
133 return;
135 addrs = isc_mem_get(lwresd->mctx, sizeof(rankedaddress) * naddrs);
136 if (addrs == NULL)
137 return;
139 isc_netaddr_fromsockaddr(&remote, &client->address);
140 ns_sortlist_byaddrsetup(lwresd->view->sortlist,
141 &remote, &order, &arg);
142 if (order == NULL) {
143 isc_mem_put(lwresd->mctx, addrs,
144 sizeof(rankedaddress) * naddrs);
145 return;
147 for (i = 0; i < naddrs; i++) {
148 result = lwaddr_netaddr_fromlwresaddr(&addrs[i].address,
149 &client->addrs[i]);
150 INSIST(result == ISC_R_SUCCESS);
151 addrs[i].rank = (*order)(&addrs[i].address, arg);
153 qsort(addrs, naddrs, sizeof(rankedaddress), addr_compare);
154 for (i = 0; i < naddrs; i++) {
155 result = lwaddr_lwresaddr_fromnetaddr(&client->addrs[i],
156 &addrs[i].address);
157 INSIST(result == ISC_R_SUCCESS);
160 isc_mem_put(lwresd->mctx, addrs, sizeof(rankedaddress) * naddrs);
163 static void
164 generate_reply(ns_lwdclient_t *client) {
165 isc_result_t result;
166 int lwres;
167 isc_region_t r;
168 lwres_buffer_t lwb;
169 ns_lwdclientmgr_t *cm;
171 cm = client->clientmgr;
172 lwb.base = NULL;
174 ns_lwdclient_log(50, "generating gabn reply for client %p", client);
177 * We must make certain the client->find is not still active.
178 * If it is either the v4 or v6 answer, just set it to NULL and
179 * let the cleanup code destroy it. Otherwise, destroy it now.
181 if (client->find == client->v4find || client->find == client->v6find)
182 client->find = NULL;
183 else
184 if (client->find != NULL)
185 dns_adb_destroyfind(&client->find);
188 * perhaps there are some here?
190 if (NEED_V6(client) && client->v4find != NULL)
191 client->v6find = client->v4find;
194 * Run through the finds we have and wire them up to the gabn
195 * structure.
197 LWRES_LIST_INIT(client->gabn.addrs);
198 if (client->v4find != NULL)
199 setup_addresses(client, client->v4find, DNS_ADBFIND_INET);
200 if (client->v6find != NULL)
201 setup_addresses(client, client->v6find, DNS_ADBFIND_INET6);
204 * If there are no addresses, try the next element in the search
205 * path, if there are any more. Otherwise, fall through into
206 * the error handling code below.
208 if (client->gabn.naddrs == 0) {
209 do {
210 result = ns_lwsearchctx_next(&client->searchctx);
211 if (result == ISC_R_SUCCESS) {
212 cleanup_gabn(client);
213 result = start_find(client);
214 if (result == ISC_R_SUCCESS)
215 return;
217 } while (result == ISC_R_SUCCESS);
221 * Render the packet.
223 client->pkt.recvlength = LWRES_RECVLENGTH;
224 client->pkt.authtype = 0; /* XXXMLG */
225 client->pkt.authlength = 0;
228 * If there are no addresses, return failure.
230 if (client->gabn.naddrs != 0)
231 client->pkt.result = LWRES_R_SUCCESS;
232 else
233 client->pkt.result = LWRES_R_NOTFOUND;
235 sort_addresses(client);
237 lwres = lwres_gabnresponse_render(cm->lwctx, &client->gabn,
238 &client->pkt, &lwb);
239 if (lwres != LWRES_R_SUCCESS)
240 goto out;
242 r.base = lwb.base;
243 r.length = lwb.used;
244 client->sendbuf = r.base;
245 client->sendlength = r.length;
246 result = ns_lwdclient_sendreply(client, &r);
247 if (result != ISC_R_SUCCESS)
248 goto out;
250 NS_LWDCLIENT_SETSEND(client);
253 * All done!
255 cleanup_gabn(client);
257 return;
259 out:
260 cleanup_gabn(client);
262 if (lwb.base != NULL)
263 lwres_context_freemem(client->clientmgr->lwctx,
264 lwb.base, lwb.length);
266 ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
270 * Take the current real name, move it to an alias slot (if any are
271 * open) then put this new name in as the real name for the target.
273 * Return success if it can be rendered, otherwise failure. Note that
274 * not having enough alias slots open is NOT a failure.
276 static isc_result_t
277 add_alias(ns_lwdclient_t *client) {
278 isc_buffer_t b;
279 isc_result_t result;
280 isc_uint16_t naliases;
282 b = client->recv_buffer;
285 * Render the new name to the buffer.
287 result = dns_name_totext(dns_fixedname_name(&client->target_name),
288 ISC_TRUE, &client->recv_buffer);
289 if (result != ISC_R_SUCCESS)
290 return (result);
293 * Are there any open slots?
295 naliases = client->gabn.naliases;
296 if (naliases < LWRES_MAX_ALIASES) {
297 client->gabn.aliases[naliases] = client->gabn.realname;
298 client->gabn.aliaslen[naliases] = client->gabn.realnamelen;
299 client->gabn.naliases++;
303 * Save this name away as the current real name.
305 client->gabn.realname = (char *)(b.base) + b.used;
306 client->gabn.realnamelen = client->recv_buffer.used - b.used;
308 return (ISC_R_SUCCESS);
311 static isc_result_t
312 store_realname(ns_lwdclient_t *client) {
313 isc_buffer_t b;
314 isc_result_t result;
315 dns_name_t *tname;
317 b = client->recv_buffer;
319 tname = dns_fixedname_name(&client->target_name);
320 result = ns_lwsearchctx_current(&client->searchctx, tname);
321 if (result != ISC_R_SUCCESS)
322 return (result);
325 * Render the new name to the buffer.
327 result = dns_name_totext(tname, ISC_TRUE, &client->recv_buffer);
328 if (result != ISC_R_SUCCESS)
329 return (result);
332 * Save this name away as the current real name.
334 client->gabn.realname = (char *) b.base + b.used;
335 client->gabn.realnamelen = client->recv_buffer.used - b.used;
337 return (ISC_R_SUCCESS);
340 static void
341 process_gabn_finddone(isc_task_t *task, isc_event_t *ev) {
342 ns_lwdclient_t *client = ev->ev_arg;
343 isc_eventtype_t evtype;
344 isc_boolean_t claimed;
346 ns_lwdclient_log(50, "find done for task %p, client %p", task, client);
348 evtype = ev->ev_type;
349 isc_event_free(&ev);
352 * No more info to be had? If so, we have all the good stuff
353 * right now, so we can render things.
355 claimed = ISC_FALSE;
356 if (evtype == DNS_EVENT_ADBNOMOREADDRESSES) {
357 if (NEED_V4(client)) {
358 client->v4find = client->find;
359 claimed = ISC_TRUE;
361 if (NEED_V6(client)) {
362 client->v6find = client->find;
363 claimed = ISC_TRUE;
365 if (client->find != NULL) {
366 if (claimed)
367 client->find = NULL;
368 else
369 dns_adb_destroyfind(&client->find);
372 generate_reply(client);
373 return;
377 * We probably don't need this find anymore. We're either going to
378 * reissue it, or an error occurred. Either way, we're done with
379 * it.
381 if ((client->find != client->v4find)
382 && (client->find != client->v6find)) {
383 dns_adb_destroyfind(&client->find);
384 } else {
385 client->find = NULL;
389 * We have some new information we can gather. Run off and fetch
390 * it.
392 if (evtype == DNS_EVENT_ADBMOREADDRESSES) {
393 restart_find(client);
394 return;
398 * An error or other strangeness happened. Drop this query.
400 cleanup_gabn(client);
401 ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
404 static void
405 restart_find(ns_lwdclient_t *client) {
406 unsigned int options;
407 isc_result_t result;
408 isc_boolean_t claimed;
410 ns_lwdclient_log(50, "starting find for client %p", client);
413 * Issue a find for the name contained in the request. We won't
414 * set the bit that says "anything is good enough" -- we want it
415 * all.
417 options = 0;
418 options |= DNS_ADBFIND_WANTEVENT;
419 options |= DNS_ADBFIND_RETURNLAME;
422 * Set the bits up here to mark that we want this address family
423 * and that we do not currently have a find pending. We will
424 * set that bit again below if it turns out we will get an event.
426 if (NEED_V4(client))
427 options |= DNS_ADBFIND_INET;
428 if (NEED_V6(client))
429 options |= DNS_ADBFIND_INET6;
431 find_again:
432 INSIST(client->find == NULL);
433 result = dns_adb_createfind(client->clientmgr->view->adb,
434 client->clientmgr->task,
435 process_gabn_finddone, client,
436 dns_fixedname_name(&client->target_name),
437 dns_rootname, 0, options, 0,
438 dns_fixedname_name(&client->target_name),
439 client->clientmgr->view->dstport,
440 &client->find);
443 * Did we get an alias? If so, save it and re-issue the query.
445 if (result == DNS_R_ALIAS) {
446 ns_lwdclient_log(50, "found alias, restarting query");
447 dns_adb_destroyfind(&client->find);
448 cleanup_gabn(client);
449 result = add_alias(client);
450 if (result != ISC_R_SUCCESS) {
451 ns_lwdclient_log(50,
452 "out of buffer space adding alias");
453 ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
454 return;
456 goto find_again;
459 ns_lwdclient_log(50, "find returned %d (%s)", result,
460 isc_result_totext(result));
463 * Did we get an error?
465 if (result != ISC_R_SUCCESS) {
466 if (client->find != NULL)
467 dns_adb_destroyfind(&client->find);
468 cleanup_gabn(client);
469 ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
470 return;
473 claimed = ISC_FALSE;
476 * Did we get our answer to V4 addresses?
478 if (NEED_V4(client)
479 && ((client->find->query_pending & DNS_ADBFIND_INET) == 0)) {
480 ns_lwdclient_log(50, "client %p ipv4 satisfied by find %p",
481 client, client->find);
482 claimed = ISC_TRUE;
483 client->v4find = client->find;
487 * Did we get our answer to V6 addresses?
489 if (NEED_V6(client)
490 && ((client->find->query_pending & DNS_ADBFIND_INET6) == 0)) {
491 ns_lwdclient_log(50, "client %p ipv6 satisfied by find %p",
492 client, client->find);
493 claimed = ISC_TRUE;
494 client->v6find = client->find;
498 * If we're going to get an event, set our internal pending flag
499 * and return. When we get an event back we'll do the right
500 * thing, basically by calling this function again, perhaps with a
501 * new target name.
503 * If we have both v4 and v6, and we are still getting an event,
504 * we have a programming error, so die hard.
506 if ((client->find->options & DNS_ADBFIND_WANTEVENT) != 0) {
507 ns_lwdclient_log(50, "event will be sent");
508 INSIST(client->v4find == NULL || client->v6find == NULL);
509 return;
511 ns_lwdclient_log(50, "no event will be sent");
512 if (claimed)
513 client->find = NULL;
514 else
515 dns_adb_destroyfind(&client->find);
518 * We seem to have everything we asked for, or at least we are
519 * able to respond with things we've learned.
522 generate_reply(client);
525 static isc_result_t
526 start_find(ns_lwdclient_t *client) {
527 isc_result_t result;
530 * Initialize the real name and alias arrays in the reply we're
531 * going to build up.
533 init_gabn(client);
535 result = store_realname(client);
536 if (result != ISC_R_SUCCESS)
537 return (result);
538 restart_find(client);
539 return (ISC_R_SUCCESS);
543 static void
544 init_gabn(ns_lwdclient_t *client) {
545 int i;
548 * Initialize the real name and alias arrays in the reply we're
549 * going to build up.
551 for (i = 0; i < LWRES_MAX_ALIASES; i++) {
552 client->aliases[i] = NULL;
553 client->aliaslen[i] = 0;
555 for (i = 0; i < LWRES_MAX_ADDRS; i++) {
556 client->addrs[i].family = 0;
557 client->addrs[i].length = 0;
558 memset(client->addrs[i].address, 0, LWRES_ADDR_MAXLEN);
559 LWRES_LINK_INIT(&client->addrs[i], link);
562 client->gabn.naliases = 0;
563 client->gabn.naddrs = 0;
564 client->gabn.realname = NULL;
565 client->gabn.aliases = client->aliases;
566 client->gabn.realnamelen = 0;
567 client->gabn.aliaslen = client->aliaslen;
568 LWRES_LIST_INIT(client->gabn.addrs);
569 client->gabn.base = NULL;
570 client->gabn.baselen = 0;
573 * Set up the internal buffer to point to the receive region.
575 isc_buffer_init(&client->recv_buffer, client->buffer, LWRES_RECVLENGTH);
579 * When we are called, we can be assured that:
581 * client->sockaddr contains the address we need to reply to,
583 * client->pkt contains the packet header data,
585 * the packet "checks out" overall -- any MD5 hashes or crypto
586 * bits have been verified,
588 * "b" points to the remaining data after the packet header
589 * was parsed off.
591 * We are in a the RECVDONE state.
593 * From this state we will enter the SEND state if we happen to have
594 * everything we need or we need to return an error packet, or to the
595 * FINDWAIT state if we need to look things up.
597 void
598 ns_lwdclient_processgabn(ns_lwdclient_t *client, lwres_buffer_t *b) {
599 isc_result_t result;
600 lwres_gabnrequest_t *req;
601 ns_lwdclientmgr_t *cm;
602 isc_buffer_t namebuf;
604 REQUIRE(NS_LWDCLIENT_ISRECVDONE(client));
606 cm = client->clientmgr;
607 req = NULL;
609 result = lwres_gabnrequest_parse(client->clientmgr->lwctx,
610 b, &client->pkt, &req);
611 if (result != LWRES_R_SUCCESS)
612 goto out;
613 if (req->name == NULL)
614 goto out;
616 isc_buffer_init(&namebuf, req->name, req->namelen);
617 isc_buffer_add(&namebuf, req->namelen);
619 dns_fixedname_init(&client->target_name);
620 dns_fixedname_init(&client->query_name);
621 result = dns_name_fromtext(dns_fixedname_name(&client->query_name),
622 &namebuf, NULL, ISC_FALSE, NULL);
623 if (result != ISC_R_SUCCESS)
624 goto out;
625 ns_lwsearchctx_init(&client->searchctx,
626 cm->listener->manager->search,
627 dns_fixedname_name(&client->query_name),
628 cm->listener->manager->ndots);
629 ns_lwsearchctx_first(&client->searchctx);
631 client->find_wanted = req->addrtypes;
632 ns_lwdclient_log(50, "client %p looking for addrtypes %08x",
633 client, client->find_wanted);
636 * We no longer need to keep this around.
638 lwres_gabnrequest_free(client->clientmgr->lwctx, &req);
641 * Start the find.
643 result = start_find(client);
644 if (result != ISC_R_SUCCESS)
645 goto out;
647 return;
650 * We're screwed. Return an error packet to our caller.
652 out:
653 if (req != NULL)
654 lwres_gabnrequest_free(client->clientmgr->lwctx, &req);
656 ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);