Import bind-9.3.4
[dragonfly.git] / contrib / bind-9.3 / bin / named / lwdgabn.c
blob539c25bf3d15bb49c214314fa5171b1fbf2fb5c7
1 /*
2 * Copyright (C) 2004, 2006 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000, 2001 Internet Software Consortium.
5 * Permission to use, copy, modify, and 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.13.12.5 2006/03/02 00:37:20 marka Exp $ */
20 #include <config.h>
22 #include <stdlib.h>
24 #include <isc/netaddr.h>
25 #include <isc/sockaddr.h>
26 #include <isc/socket.h>
27 #include <isc/string.h> /* Required for HP/UX (and others?) */
28 #include <isc/util.h>
30 #include <dns/adb.h>
31 #include <dns/events.h>
32 #include <dns/result.h>
34 #include <named/types.h>
35 #include <named/lwaddr.h>
36 #include <named/lwdclient.h>
37 #include <named/lwresd.h>
38 #include <named/lwsearch.h>
39 #include <named/sortlist.h>
41 #define NEED_V4(c) ((((c)->find_wanted & LWRES_ADDRTYPE_V4) != 0) \
42 && ((c)->v4find == NULL))
43 #define NEED_V6(c) ((((c)->find_wanted & LWRES_ADDRTYPE_V6) != 0) \
44 && ((c)->v6find == NULL))
46 static isc_result_t start_find(ns_lwdclient_t *);
47 static void restart_find(ns_lwdclient_t *);
48 static void init_gabn(ns_lwdclient_t *);
51 * Destroy any finds. This can be used to "start over from scratch" and
52 * should only be called when events are _not_ being generated by the finds.
54 static void
55 cleanup_gabn(ns_lwdclient_t *client) {
56 ns_lwdclient_log(50, "cleaning up client %p", client);
58 if (client->v6find != NULL) {
59 if (client->v6find == client->v4find)
60 client->v6find = NULL;
61 else
62 dns_adb_destroyfind(&client->v6find);
64 if (client->v4find != NULL)
65 dns_adb_destroyfind(&client->v4find);
68 static void
69 setup_addresses(ns_lwdclient_t *client, dns_adbfind_t *find, unsigned int at) {
70 dns_adbaddrinfo_t *ai;
71 lwres_addr_t *addr;
72 int af;
73 const struct sockaddr *sa;
74 isc_result_t result;
76 if (at == DNS_ADBFIND_INET)
77 af = AF_INET;
78 else
79 af = AF_INET6;
81 ai = ISC_LIST_HEAD(find->list);
82 while (ai != NULL && client->gabn.naddrs < LWRES_MAX_ADDRS) {
83 sa = &ai->sockaddr.type.sa;
84 if (sa->sa_family != af)
85 goto next;
87 addr = &client->addrs[client->gabn.naddrs];
89 result = lwaddr_lwresaddr_fromsockaddr(addr, &ai->sockaddr);
90 if (result != ISC_R_SUCCESS)
91 goto next;
93 ns_lwdclient_log(50, "adding address %p, family %d, length %d",
94 addr->address, addr->family, addr->length);
96 client->gabn.naddrs++;
97 REQUIRE(!LWRES_LINK_LINKED(addr, link));
98 LWRES_LIST_APPEND(client->gabn.addrs, addr, link);
100 next:
101 ai = ISC_LIST_NEXT(ai, publink);
105 typedef struct {
106 isc_netaddr_t address;
107 int rank;
108 } rankedaddress;
110 static int
111 addr_compare(const void *av, const void *bv) {
112 const rankedaddress *a = (const rankedaddress *) av;
113 const rankedaddress *b = (const rankedaddress *) bv;
114 return (a->rank - b->rank);
117 static void
118 sort_addresses(ns_lwdclient_t *client) {
119 unsigned int naddrs;
120 rankedaddress *addrs;
121 isc_netaddr_t remote;
122 dns_addressorderfunc_t order;
123 const void *arg;
124 ns_lwresd_t *lwresd = client->clientmgr->listener->manager;
125 unsigned int i;
126 isc_result_t result;
128 naddrs = client->gabn.naddrs;
130 if (naddrs <= 1 || lwresd->view->sortlist == NULL)
131 return;
133 addrs = isc_mem_get(lwresd->mctx, sizeof(rankedaddress) * naddrs);
134 if (addrs == NULL)
135 return;
137 isc_netaddr_fromsockaddr(&remote, &client->address);
138 ns_sortlist_byaddrsetup(lwresd->view->sortlist,
139 &remote, &order, &arg);
140 if (order == NULL) {
141 isc_mem_put(lwresd->mctx, addrs,
142 sizeof(rankedaddress) * naddrs);
143 return;
145 for (i = 0; i < naddrs; i++) {
146 result = lwaddr_netaddr_fromlwresaddr(&addrs[i].address,
147 &client->addrs[i]);
148 INSIST(result == ISC_R_SUCCESS);
149 addrs[i].rank = (*order)(&addrs[i].address, arg);
151 qsort(addrs, naddrs, sizeof(rankedaddress), addr_compare);
152 for (i = 0; i < naddrs; i++) {
153 result = lwaddr_lwresaddr_fromnetaddr(&client->addrs[i],
154 &addrs[i].address);
155 INSIST(result == ISC_R_SUCCESS);
158 isc_mem_put(lwresd->mctx, addrs, sizeof(rankedaddress) * naddrs);
161 static void
162 generate_reply(ns_lwdclient_t *client) {
163 isc_result_t result;
164 int lwres;
165 isc_region_t r;
166 lwres_buffer_t lwb;
167 ns_lwdclientmgr_t *cm;
169 cm = client->clientmgr;
170 lwb.base = NULL;
172 ns_lwdclient_log(50, "generating gabn reply for client %p", client);
175 * We must make certain the client->find is not still active.
176 * If it is either the v4 or v6 answer, just set it to NULL and
177 * let the cleanup code destroy it. Otherwise, destroy it now.
179 if (client->find == client->v4find || client->find == client->v6find)
180 client->find = NULL;
181 else
182 if (client->find != NULL)
183 dns_adb_destroyfind(&client->find);
186 * perhaps there are some here?
188 if (NEED_V6(client) && client->v4find != NULL)
189 client->v6find = client->v4find;
192 * Run through the finds we have and wire them up to the gabn
193 * structure.
195 LWRES_LIST_INIT(client->gabn.addrs);
196 if (client->v4find != NULL)
197 setup_addresses(client, client->v4find, DNS_ADBFIND_INET);
198 if (client->v6find != NULL)
199 setup_addresses(client, client->v6find, DNS_ADBFIND_INET6);
202 * If there are no addresses, try the next element in the search
203 * path, if there are any more. Otherwise, fall through into
204 * the error handling code below.
206 if (client->gabn.naddrs == 0) {
207 do {
208 result = ns_lwsearchctx_next(&client->searchctx);
209 if (result == ISC_R_SUCCESS) {
210 cleanup_gabn(client);
211 result = start_find(client);
212 if (result == ISC_R_SUCCESS)
213 return;
215 } while (result == ISC_R_SUCCESS);
219 * Render the packet.
221 client->pkt.recvlength = LWRES_RECVLENGTH;
222 client->pkt.authtype = 0; /* XXXMLG */
223 client->pkt.authlength = 0;
226 * If there are no addresses, return failure.
228 if (client->gabn.naddrs != 0)
229 client->pkt.result = LWRES_R_SUCCESS;
230 else
231 client->pkt.result = LWRES_R_NOTFOUND;
233 sort_addresses(client);
235 lwres = lwres_gabnresponse_render(cm->lwctx, &client->gabn,
236 &client->pkt, &lwb);
237 if (lwres != LWRES_R_SUCCESS)
238 goto out;
240 r.base = lwb.base;
241 r.length = lwb.used;
242 client->sendbuf = r.base;
243 client->sendlength = r.length;
244 result = ns_lwdclient_sendreply(client, &r);
245 if (result != ISC_R_SUCCESS)
246 goto out;
248 NS_LWDCLIENT_SETSEND(client);
251 * All done!
253 cleanup_gabn(client);
255 return;
257 out:
258 cleanup_gabn(client);
260 if (lwb.base != NULL)
261 lwres_context_freemem(client->clientmgr->lwctx,
262 lwb.base, lwb.length);
264 ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
268 * Take the current real name, move it to an alias slot (if any are
269 * open) then put this new name in as the real name for the target.
271 * Return success if it can be rendered, otherwise failure. Note that
272 * not having enough alias slots open is NOT a failure.
274 static isc_result_t
275 add_alias(ns_lwdclient_t *client) {
276 isc_buffer_t b;
277 isc_result_t result;
278 isc_uint16_t naliases;
280 b = client->recv_buffer;
283 * Render the new name to the buffer.
285 result = dns_name_totext(dns_fixedname_name(&client->target_name),
286 ISC_TRUE, &client->recv_buffer);
287 if (result != ISC_R_SUCCESS)
288 return (result);
291 * Are there any open slots?
293 naliases = client->gabn.naliases;
294 if (naliases < LWRES_MAX_ALIASES) {
295 client->gabn.aliases[naliases] = client->gabn.realname;
296 client->gabn.aliaslen[naliases] = client->gabn.realnamelen;
297 client->gabn.naliases++;
301 * Save this name away as the current real name.
303 client->gabn.realname = (char *)(b.base) + b.used;
304 client->gabn.realnamelen = client->recv_buffer.used - b.used;
306 return (ISC_R_SUCCESS);
309 static isc_result_t
310 store_realname(ns_lwdclient_t *client) {
311 isc_buffer_t b;
312 isc_result_t result;
313 dns_name_t *tname;
315 b = client->recv_buffer;
317 tname = dns_fixedname_name(&client->target_name);
318 result = ns_lwsearchctx_current(&client->searchctx, tname);
319 if (result != ISC_R_SUCCESS)
320 return (result);
323 * Render the new name to the buffer.
325 result = dns_name_totext(tname, ISC_TRUE, &client->recv_buffer);
326 if (result != ISC_R_SUCCESS)
327 return (result);
330 * Save this name away as the current real name.
332 client->gabn.realname = (char *) b.base + b.used;
333 client->gabn.realnamelen = client->recv_buffer.used - b.used;
335 return (ISC_R_SUCCESS);
338 static void
339 process_gabn_finddone(isc_task_t *task, isc_event_t *ev) {
340 ns_lwdclient_t *client = ev->ev_arg;
341 isc_eventtype_t evtype;
342 isc_boolean_t claimed;
344 ns_lwdclient_log(50, "find done for task %p, client %p", task, client);
346 evtype = ev->ev_type;
347 isc_event_free(&ev);
350 * No more info to be had? If so, we have all the good stuff
351 * right now, so we can render things.
353 claimed = ISC_FALSE;
354 if (evtype == DNS_EVENT_ADBNOMOREADDRESSES) {
355 if (NEED_V4(client)) {
356 client->v4find = client->find;
357 claimed = ISC_TRUE;
359 if (NEED_V6(client)) {
360 client->v6find = client->find;
361 claimed = ISC_TRUE;
363 if (client->find != NULL) {
364 if (claimed)
365 client->find = NULL;
366 else
367 dns_adb_destroyfind(&client->find);
370 generate_reply(client);
371 return;
375 * We probably don't need this find anymore. We're either going to
376 * reissue it, or an error occurred. Either way, we're done with
377 * it.
379 if ((client->find != client->v4find)
380 && (client->find != client->v6find)) {
381 dns_adb_destroyfind(&client->find);
382 } else {
383 client->find = NULL;
387 * We have some new information we can gather. Run off and fetch
388 * it.
390 if (evtype == DNS_EVENT_ADBMOREADDRESSES) {
391 restart_find(client);
392 return;
396 * An error or other strangeness happened. Drop this query.
398 cleanup_gabn(client);
399 ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
402 static void
403 restart_find(ns_lwdclient_t *client) {
404 unsigned int options;
405 isc_result_t result;
406 isc_boolean_t claimed;
408 ns_lwdclient_log(50, "starting find for client %p", client);
411 * Issue a find for the name contained in the request. We won't
412 * set the bit that says "anything is good enough" -- we want it
413 * all.
415 options = 0;
416 options |= DNS_ADBFIND_WANTEVENT;
417 options |= DNS_ADBFIND_RETURNLAME;
420 * Set the bits up here to mark that we want this address family
421 * and that we do not currently have a find pending. We will
422 * set that bit again below if it turns out we will get an event.
424 if (NEED_V4(client))
425 options |= DNS_ADBFIND_INET;
426 if (NEED_V6(client))
427 options |= DNS_ADBFIND_INET6;
429 find_again:
430 INSIST(client->find == NULL);
431 result = dns_adb_createfind(client->clientmgr->view->adb,
432 client->clientmgr->task,
433 process_gabn_finddone, client,
434 dns_fixedname_name(&client->target_name),
435 dns_rootname, options, 0,
436 dns_fixedname_name(&client->target_name),
437 client->clientmgr->view->dstport,
438 &client->find);
441 * Did we get an alias? If so, save it and re-issue the query.
443 if (result == DNS_R_ALIAS) {
444 ns_lwdclient_log(50, "found alias, restarting query");
445 dns_adb_destroyfind(&client->find);
446 cleanup_gabn(client);
447 result = add_alias(client);
448 if (result != ISC_R_SUCCESS) {
449 ns_lwdclient_log(50,
450 "out of buffer space adding alias");
451 ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
452 return;
454 goto find_again;
457 ns_lwdclient_log(50, "find returned %d (%s)", result,
458 isc_result_totext(result));
461 * Did we get an error?
463 if (result != ISC_R_SUCCESS) {
464 if (client->find != NULL)
465 dns_adb_destroyfind(&client->find);
466 cleanup_gabn(client);
467 ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
468 return;
471 claimed = ISC_FALSE;
474 * Did we get our answer to V4 addresses?
476 if (NEED_V4(client)
477 && ((client->find->query_pending & DNS_ADBFIND_INET) == 0)) {
478 ns_lwdclient_log(50, "client %p ipv4 satisfied by find %p",
479 client, client->find);
480 claimed = ISC_TRUE;
481 client->v4find = client->find;
485 * Did we get our answer to V6 addresses?
487 if (NEED_V6(client)
488 && ((client->find->query_pending & DNS_ADBFIND_INET6) == 0)) {
489 ns_lwdclient_log(50, "client %p ipv6 satisfied by find %p",
490 client, client->find);
491 claimed = ISC_TRUE;
492 client->v6find = client->find;
496 * If we're going to get an event, set our internal pending flag
497 * and return. When we get an event back we'll do the right
498 * thing, basically by calling this function again, perhaps with a
499 * new target name.
501 * If we have both v4 and v6, and we are still getting an event,
502 * we have a programming error, so die hard.
504 if ((client->find->options & DNS_ADBFIND_WANTEVENT) != 0) {
505 ns_lwdclient_log(50, "event will be sent");
506 INSIST(client->v4find == NULL || client->v6find == NULL);
507 return;
509 ns_lwdclient_log(50, "no event will be sent");
510 if (claimed)
511 client->find = NULL;
512 else
513 dns_adb_destroyfind(&client->find);
516 * We seem to have everything we asked for, or at least we are
517 * able to respond with things we've learned.
520 generate_reply(client);
523 static isc_result_t
524 start_find(ns_lwdclient_t *client) {
525 isc_result_t result;
528 * Initialize the real name and alias arrays in the reply we're
529 * going to build up.
531 init_gabn(client);
533 result = store_realname(client);
534 if (result != ISC_R_SUCCESS)
535 return (result);
536 restart_find(client);
537 return (ISC_R_SUCCESS);
541 static void
542 init_gabn(ns_lwdclient_t *client) {
543 int i;
546 * Initialize the real name and alias arrays in the reply we're
547 * going to build up.
549 for (i = 0; i < LWRES_MAX_ALIASES; i++) {
550 client->aliases[i] = NULL;
551 client->aliaslen[i] = 0;
553 for (i = 0; i < LWRES_MAX_ADDRS; i++) {
554 client->addrs[i].family = 0;
555 client->addrs[i].length = 0;
556 memset(client->addrs[i].address, 0, LWRES_ADDR_MAXLEN);
557 LWRES_LINK_INIT(&client->addrs[i], link);
560 client->gabn.naliases = 0;
561 client->gabn.naddrs = 0;
562 client->gabn.realname = NULL;
563 client->gabn.aliases = client->aliases;
564 client->gabn.realnamelen = 0;
565 client->gabn.aliaslen = client->aliaslen;
566 LWRES_LIST_INIT(client->gabn.addrs);
567 client->gabn.base = NULL;
568 client->gabn.baselen = 0;
571 * Set up the internal buffer to point to the receive region.
573 isc_buffer_init(&client->recv_buffer, client->buffer, LWRES_RECVLENGTH);
577 * When we are called, we can be assured that:
579 * client->sockaddr contains the address we need to reply to,
581 * client->pkt contains the packet header data,
583 * the packet "checks out" overall -- any MD5 hashes or crypto
584 * bits have been verified,
586 * "b" points to the remaining data after the packet header
587 * was parsed off.
589 * We are in a the RECVDONE state.
591 * From this state we will enter the SEND state if we happen to have
592 * everything we need or we need to return an error packet, or to the
593 * FINDWAIT state if we need to look things up.
595 void
596 ns_lwdclient_processgabn(ns_lwdclient_t *client, lwres_buffer_t *b) {
597 isc_result_t result;
598 lwres_gabnrequest_t *req;
599 ns_lwdclientmgr_t *cm;
600 isc_buffer_t namebuf;
602 REQUIRE(NS_LWDCLIENT_ISRECVDONE(client));
604 cm = client->clientmgr;
605 req = NULL;
607 result = lwres_gabnrequest_parse(client->clientmgr->lwctx,
608 b, &client->pkt, &req);
609 if (result != LWRES_R_SUCCESS)
610 goto out;
611 if (req->name == NULL)
612 goto out;
614 isc_buffer_init(&namebuf, req->name, req->namelen);
615 isc_buffer_add(&namebuf, req->namelen);
617 dns_fixedname_init(&client->target_name);
618 dns_fixedname_init(&client->query_name);
619 result = dns_name_fromtext(dns_fixedname_name(&client->query_name),
620 &namebuf, NULL, ISC_FALSE, NULL);
621 if (result != ISC_R_SUCCESS)
622 goto out;
623 ns_lwsearchctx_init(&client->searchctx,
624 cm->listener->manager->search,
625 dns_fixedname_name(&client->query_name),
626 cm->listener->manager->ndots);
627 ns_lwsearchctx_first(&client->searchctx);
629 client->find_wanted = req->addrtypes;
630 ns_lwdclient_log(50, "client %p looking for addrtypes %08x",
631 client, client->find_wanted);
634 * We no longer need to keep this around.
636 lwres_gabnrequest_free(client->clientmgr->lwctx, &req);
639 * Start the find.
641 result = start_find(client);
642 if (result != ISC_R_SUCCESS)
643 goto out;
645 return;
648 * We're screwed. Return an error packet to our caller.
650 out:
651 if (req != NULL)
652 lwres_gabnrequest_free(client->clientmgr->lwctx, &req);
654 ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);