etc/services - sync with NetBSD-8
[minix.git] / external / bsd / bind / dist / bin / named / lwdgabn.c
blob32bc8be3a19fa5041609cb2c2a3125018bc8f2ae
1 /* $NetBSD: lwdgabn.c,v 1.4 2014/12/10 04:37:51 christos Exp $ */
3 /*
4 * Copyright (C) 2004-2007, 2009 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 2000, 2001 Internet Software Consortium.
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
20 /* Id: lwdgabn.c,v 1.24 2009/09/02 23:48:01 tbox Exp */
22 /*! \file */
24 #include <config.h>
26 #include <stdlib.h>
28 #include <isc/netaddr.h>
29 #include <isc/sockaddr.h>
30 #include <isc/socket.h>
31 #include <isc/string.h> /* Required for HP/UX (and others?) */
32 #include <isc/util.h>
34 #include <dns/adb.h>
35 #include <dns/events.h>
36 #include <dns/result.h>
38 #include <named/types.h>
39 #include <named/lwaddr.h>
40 #include <named/lwdclient.h>
41 #include <named/lwresd.h>
42 #include <named/lwsearch.h>
43 #include <named/sortlist.h>
45 #define NEED_V4(c) ((((c)->find_wanted & LWRES_ADDRTYPE_V4) != 0) \
46 && ((c)->v4find == NULL))
47 #define NEED_V6(c) ((((c)->find_wanted & LWRES_ADDRTYPE_V6) != 0) \
48 && ((c)->v6find == NULL))
50 static isc_result_t start_find(ns_lwdclient_t *);
51 static void restart_find(ns_lwdclient_t *);
52 static void init_gabn(ns_lwdclient_t *);
54 /*%
55 * Destroy any finds. This can be used to "start over from scratch" and
56 * should only be called when events are _not_ being generated by the finds.
58 static void
59 cleanup_gabn(ns_lwdclient_t *client) {
60 ns_lwdclient_log(50, "cleaning up client %p", client);
62 if (client->v6find != NULL) {
63 if (client->v6find == client->v4find)
64 client->v6find = NULL;
65 else
66 dns_adb_destroyfind(&client->v6find);
68 if (client->v4find != NULL)
69 dns_adb_destroyfind(&client->v4find);
72 static void
73 setup_addresses(ns_lwdclient_t *client, dns_adbfind_t *find, unsigned int at) {
74 dns_adbaddrinfo_t *ai;
75 lwres_addr_t *addr;
76 int af;
77 const struct sockaddr *sa;
78 isc_result_t result;
80 if (at == DNS_ADBFIND_INET)
81 af = AF_INET;
82 else
83 af = AF_INET6;
85 ai = ISC_LIST_HEAD(find->list);
86 while (ai != NULL && client->gabn.naddrs < LWRES_MAX_ADDRS) {
87 sa = &ai->sockaddr.type.sa;
88 if (sa->sa_family != af)
89 goto next;
91 addr = &client->addrs[client->gabn.naddrs];
93 result = lwaddr_lwresaddr_fromsockaddr(addr, &ai->sockaddr);
94 if (result != ISC_R_SUCCESS)
95 goto next;
97 ns_lwdclient_log(50, "adding address %p, family %d, length %d",
98 addr->address, addr->family, addr->length);
100 client->gabn.naddrs++;
101 REQUIRE(!LWRES_LINK_LINKED(addr, link));
102 LWRES_LIST_APPEND(client->gabn.addrs, addr, link);
104 next:
105 ai = ISC_LIST_NEXT(ai, publink);
109 typedef struct {
110 isc_netaddr_t address;
111 int rank;
112 } rankedaddress;
114 static int
115 addr_compare(const void *av, const void *bv) {
116 const rankedaddress *a = (const rankedaddress *) av;
117 const rankedaddress *b = (const rankedaddress *) bv;
118 return (a->rank - b->rank);
121 static void
122 sort_addresses(ns_lwdclient_t *client) {
123 unsigned int naddrs;
124 rankedaddress *addrs;
125 isc_netaddr_t remote;
126 dns_addressorderfunc_t order;
127 const void *arg;
128 ns_lwresd_t *lwresd = client->clientmgr->listener->manager;
129 unsigned int i;
130 isc_result_t result;
132 naddrs = client->gabn.naddrs;
134 if (naddrs <= 1 || lwresd->view->sortlist == NULL)
135 return;
137 addrs = isc_mem_get(lwresd->mctx, sizeof(rankedaddress) * naddrs);
138 if (addrs == NULL)
139 return;
141 isc_netaddr_fromsockaddr(&remote, &client->address);
142 ns_sortlist_byaddrsetup(lwresd->view->sortlist,
143 &remote, &order, &arg);
144 if (order == NULL) {
145 isc_mem_put(lwresd->mctx, addrs,
146 sizeof(rankedaddress) * naddrs);
147 return;
149 for (i = 0; i < naddrs; i++) {
150 result = lwaddr_netaddr_fromlwresaddr(&addrs[i].address,
151 &client->addrs[i]);
152 INSIST(result == ISC_R_SUCCESS);
153 addrs[i].rank = (*order)(&addrs[i].address, arg);
155 qsort(addrs, naddrs, sizeof(rankedaddress), addr_compare);
156 for (i = 0; i < naddrs; i++) {
157 result = lwaddr_lwresaddr_fromnetaddr(&client->addrs[i],
158 &addrs[i].address);
159 INSIST(result == ISC_R_SUCCESS);
162 isc_mem_put(lwresd->mctx, addrs, sizeof(rankedaddress) * naddrs);
165 static void
166 generate_reply(ns_lwdclient_t *client) {
167 isc_result_t result;
168 int lwres;
169 isc_region_t r;
170 lwres_buffer_t lwb;
171 ns_lwdclientmgr_t *cm;
173 cm = client->clientmgr;
174 lwb.base = NULL;
176 ns_lwdclient_log(50, "generating gabn reply for client %p", client);
179 * We must make certain the client->find is not still active.
180 * If it is either the v4 or v6 answer, just set it to NULL and
181 * let the cleanup code destroy it. Otherwise, destroy it now.
183 if (client->find == client->v4find || client->find == client->v6find)
184 client->find = NULL;
185 else
186 if (client->find != NULL)
187 dns_adb_destroyfind(&client->find);
190 * perhaps there are some here?
192 if (NEED_V6(client) && client->v4find != NULL)
193 client->v6find = client->v4find;
196 * Run through the finds we have and wire them up to the gabn
197 * structure.
199 LWRES_LIST_INIT(client->gabn.addrs);
200 if (client->v4find != NULL)
201 setup_addresses(client, client->v4find, DNS_ADBFIND_INET);
202 if (client->v6find != NULL)
203 setup_addresses(client, client->v6find, DNS_ADBFIND_INET6);
206 * If there are no addresses, try the next element in the search
207 * path, if there are any more. Otherwise, fall through into
208 * the error handling code below.
210 if (client->gabn.naddrs == 0) {
211 do {
212 result = ns_lwsearchctx_next(&client->searchctx);
213 if (result == ISC_R_SUCCESS) {
214 cleanup_gabn(client);
215 result = start_find(client);
216 if (result == ISC_R_SUCCESS)
217 return;
219 } while (result == ISC_R_SUCCESS);
223 * Render the packet.
225 client->pkt.recvlength = LWRES_RECVLENGTH;
226 client->pkt.authtype = 0; /* XXXMLG */
227 client->pkt.authlength = 0;
230 * If there are no addresses, return failure.
232 if (client->gabn.naddrs != 0)
233 client->pkt.result = LWRES_R_SUCCESS;
234 else
235 client->pkt.result = LWRES_R_NOTFOUND;
237 sort_addresses(client);
239 lwres = lwres_gabnresponse_render(cm->lwctx, &client->gabn,
240 &client->pkt, &lwb);
241 if (lwres != LWRES_R_SUCCESS)
242 goto out;
244 r.base = lwb.base;
245 r.length = lwb.used;
246 client->sendbuf = r.base;
247 client->sendlength = r.length;
248 result = ns_lwdclient_sendreply(client, &r);
249 if (result != ISC_R_SUCCESS)
250 goto out;
252 NS_LWDCLIENT_SETSEND(client);
255 * All done!
257 cleanup_gabn(client);
259 return;
261 out:
262 cleanup_gabn(client);
264 if (lwb.base != NULL)
265 lwres_context_freemem(client->clientmgr->lwctx,
266 lwb.base, lwb.length);
268 ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
272 * Take the current real name, move it to an alias slot (if any are
273 * open) then put this new name in as the real name for the target.
275 * Return success if it can be rendered, otherwise failure. Note that
276 * not having enough alias slots open is NOT a failure.
278 static isc_result_t
279 add_alias(ns_lwdclient_t *client) {
280 isc_buffer_t b;
281 isc_result_t result;
282 isc_uint16_t naliases;
284 b = client->recv_buffer;
287 * Render the new name to the buffer.
289 result = dns_name_totext(dns_fixedname_name(&client->target_name),
290 ISC_TRUE, &client->recv_buffer);
291 if (result != ISC_R_SUCCESS)
292 return (result);
295 * Are there any open slots?
297 naliases = client->gabn.naliases;
298 if (naliases < LWRES_MAX_ALIASES) {
299 client->gabn.aliases[naliases] = client->gabn.realname;
300 client->gabn.aliaslen[naliases] = client->gabn.realnamelen;
301 client->gabn.naliases++;
305 * Save this name away as the current real name.
307 client->gabn.realname = (char *)(b.base) + b.used;
308 client->gabn.realnamelen = client->recv_buffer.used - b.used;
310 return (ISC_R_SUCCESS);
313 static isc_result_t
314 store_realname(ns_lwdclient_t *client) {
315 isc_buffer_t b;
316 isc_result_t result;
317 dns_name_t *tname;
319 b = client->recv_buffer;
321 tname = dns_fixedname_name(&client->target_name);
322 result = ns_lwsearchctx_current(&client->searchctx, tname);
323 if (result != ISC_R_SUCCESS)
324 return (result);
327 * Render the new name to the buffer.
329 result = dns_name_totext(tname, ISC_TRUE, &client->recv_buffer);
330 if (result != ISC_R_SUCCESS)
331 return (result);
334 * Save this name away as the current real name.
336 client->gabn.realname = (char *) b.base + b.used;
337 client->gabn.realnamelen = client->recv_buffer.used - b.used;
339 return (ISC_R_SUCCESS);
342 static void
343 process_gabn_finddone(isc_task_t *task, isc_event_t *ev) {
344 ns_lwdclient_t *client = ev->ev_arg;
345 isc_eventtype_t evtype;
346 isc_boolean_t claimed;
348 ns_lwdclient_log(50, "find done for task %p, client %p", task, client);
350 evtype = ev->ev_type;
351 isc_event_free(&ev);
354 * No more info to be had? If so, we have all the good stuff
355 * right now, so we can render things.
357 claimed = ISC_FALSE;
358 if (evtype == DNS_EVENT_ADBNOMOREADDRESSES) {
359 if (NEED_V4(client)) {
360 client->v4find = client->find;
361 claimed = ISC_TRUE;
363 if (NEED_V6(client)) {
364 client->v6find = client->find;
365 claimed = ISC_TRUE;
367 if (client->find != NULL) {
368 if (claimed)
369 client->find = NULL;
370 else
371 dns_adb_destroyfind(&client->find);
374 generate_reply(client);
375 return;
379 * We probably don't need this find anymore. We're either going to
380 * reissue it, or an error occurred. Either way, we're done with
381 * it.
383 if ((client->find != client->v4find)
384 && (client->find != client->v6find)) {
385 dns_adb_destroyfind(&client->find);
386 } else {
387 client->find = NULL;
391 * We have some new information we can gather. Run off and fetch
392 * it.
394 if (evtype == DNS_EVENT_ADBMOREADDRESSES) {
395 restart_find(client);
396 return;
400 * An error or other strangeness happened. Drop this query.
402 cleanup_gabn(client);
403 ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
406 static void
407 restart_find(ns_lwdclient_t *client) {
408 unsigned int options;
409 isc_result_t result;
410 isc_boolean_t claimed;
412 ns_lwdclient_log(50, "starting find for client %p", client);
415 * Issue a find for the name contained in the request. We won't
416 * set the bit that says "anything is good enough" -- we want it
417 * all.
419 options = 0;
420 options |= DNS_ADBFIND_WANTEVENT;
421 options |= DNS_ADBFIND_RETURNLAME;
424 * Set the bits up here to mark that we want this address family
425 * and that we do not currently have a find pending. We will
426 * set that bit again below if it turns out we will get an event.
428 if (NEED_V4(client))
429 options |= DNS_ADBFIND_INET;
430 if (NEED_V6(client))
431 options |= DNS_ADBFIND_INET6;
433 find_again:
434 INSIST(client->find == NULL);
435 result = dns_adb_createfind(client->clientmgr->view->adb,
436 client->clientmgr->task,
437 process_gabn_finddone, client,
438 dns_fixedname_name(&client->target_name),
439 dns_rootname, 0, options, 0,
440 dns_fixedname_name(&client->target_name),
441 client->clientmgr->view->dstport,
442 &client->find);
445 * Did we get an alias? If so, save it and re-issue the query.
447 if (result == DNS_R_ALIAS) {
448 ns_lwdclient_log(50, "found alias, restarting query");
449 dns_adb_destroyfind(&client->find);
450 cleanup_gabn(client);
451 result = add_alias(client);
452 if (result != ISC_R_SUCCESS) {
453 ns_lwdclient_log(50,
454 "out of buffer space adding alias");
455 ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
456 return;
458 goto find_again;
461 ns_lwdclient_log(50, "find returned %d (%s)", result,
462 isc_result_totext(result));
465 * Did we get an error?
467 if (result != ISC_R_SUCCESS) {
468 if (client->find != NULL)
469 dns_adb_destroyfind(&client->find);
470 cleanup_gabn(client);
471 ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
472 return;
475 claimed = ISC_FALSE;
478 * Did we get our answer to V4 addresses?
480 if (NEED_V4(client)
481 && ((client->find->query_pending & DNS_ADBFIND_INET) == 0)) {
482 ns_lwdclient_log(50, "client %p ipv4 satisfied by find %p",
483 client, client->find);
484 claimed = ISC_TRUE;
485 client->v4find = client->find;
489 * Did we get our answer to V6 addresses?
491 if (NEED_V6(client)
492 && ((client->find->query_pending & DNS_ADBFIND_INET6) == 0)) {
493 ns_lwdclient_log(50, "client %p ipv6 satisfied by find %p",
494 client, client->find);
495 claimed = ISC_TRUE;
496 client->v6find = client->find;
500 * If we're going to get an event, set our internal pending flag
501 * and return. When we get an event back we'll do the right
502 * thing, basically by calling this function again, perhaps with a
503 * new target name.
505 * If we have both v4 and v6, and we are still getting an event,
506 * we have a programming error, so die hard.
508 if ((client->find->options & DNS_ADBFIND_WANTEVENT) != 0) {
509 ns_lwdclient_log(50, "event will be sent");
510 INSIST(client->v4find == NULL || client->v6find == NULL);
511 return;
513 ns_lwdclient_log(50, "no event will be sent");
514 if (claimed)
515 client->find = NULL;
516 else
517 dns_adb_destroyfind(&client->find);
520 * We seem to have everything we asked for, or at least we are
521 * able to respond with things we've learned.
524 generate_reply(client);
527 static isc_result_t
528 start_find(ns_lwdclient_t *client) {
529 isc_result_t result;
532 * Initialize the real name and alias arrays in the reply we're
533 * going to build up.
535 init_gabn(client);
537 result = store_realname(client);
538 if (result != ISC_R_SUCCESS)
539 return (result);
540 restart_find(client);
541 return (ISC_R_SUCCESS);
545 static void
546 init_gabn(ns_lwdclient_t *client) {
547 int i;
550 * Initialize the real name and alias arrays in the reply we're
551 * going to build up.
553 for (i = 0; i < LWRES_MAX_ALIASES; i++) {
554 client->aliases[i] = NULL;
555 client->aliaslen[i] = 0;
557 for (i = 0; i < LWRES_MAX_ADDRS; i++) {
558 client->addrs[i].family = 0;
559 client->addrs[i].length = 0;
560 memset(client->addrs[i].address, 0, LWRES_ADDR_MAXLEN);
561 LWRES_LINK_INIT(&client->addrs[i], link);
564 client->gabn.naliases = 0;
565 client->gabn.naddrs = 0;
566 client->gabn.realname = NULL;
567 client->gabn.aliases = client->aliases;
568 client->gabn.realnamelen = 0;
569 client->gabn.aliaslen = client->aliaslen;
570 LWRES_LIST_INIT(client->gabn.addrs);
571 client->gabn.base = NULL;
572 client->gabn.baselen = 0;
575 * Set up the internal buffer to point to the receive region.
577 isc_buffer_init(&client->recv_buffer, client->buffer, LWRES_RECVLENGTH);
581 * When we are called, we can be assured that:
583 * client->sockaddr contains the address we need to reply to,
585 * client->pkt contains the packet header data,
587 * the packet "checks out" overall -- any MD5 hashes or crypto
588 * bits have been verified,
590 * "b" points to the remaining data after the packet header
591 * was parsed off.
593 * We are in a the RECVDONE state.
595 * From this state we will enter the SEND state if we happen to have
596 * everything we need or we need to return an error packet, or to the
597 * FINDWAIT state if we need to look things up.
599 void
600 ns_lwdclient_processgabn(ns_lwdclient_t *client, lwres_buffer_t *b) {
601 isc_result_t result;
602 lwres_gabnrequest_t *req;
603 ns_lwdclientmgr_t *cm;
604 isc_buffer_t namebuf;
606 REQUIRE(NS_LWDCLIENT_ISRECVDONE(client));
608 cm = client->clientmgr;
609 req = NULL;
611 result = lwres_gabnrequest_parse(client->clientmgr->lwctx,
612 b, &client->pkt, &req);
613 if (result != LWRES_R_SUCCESS)
614 goto out;
615 if (req->name == NULL)
616 goto out;
618 isc_buffer_init(&namebuf, req->name, req->namelen);
619 isc_buffer_add(&namebuf, req->namelen);
621 dns_fixedname_init(&client->target_name);
622 dns_fixedname_init(&client->query_name);
623 result = dns_name_fromtext(dns_fixedname_name(&client->query_name),
624 &namebuf, NULL, 0, NULL);
625 if (result != ISC_R_SUCCESS)
626 goto out;
627 ns_lwsearchctx_init(&client->searchctx,
628 cm->listener->manager->search,
629 dns_fixedname_name(&client->query_name),
630 cm->listener->manager->ndots);
631 ns_lwsearchctx_first(&client->searchctx);
633 client->find_wanted = req->addrtypes;
634 ns_lwdclient_log(50, "client %p looking for addrtypes %08x",
635 client, client->find_wanted);
638 * We no longer need to keep this around.
640 lwres_gabnrequest_free(client->clientmgr->lwctx, &req);
643 * Start the find.
645 result = start_find(client);
646 if (result != ISC_R_SUCCESS)
647 goto out;
649 return;
652 * We're screwed. Return an error packet to our caller.
654 out:
655 if (req != NULL)
656 lwres_gabnrequest_free(client->clientmgr->lwctx, &req);
658 ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);