030a77ae7864891e9601004d27c8c5f3f1d7c2e3
[dragonfly.git] / contrib / bind-9.3 / bin / named / lwdgabn.c
1 /*
2  * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2000, 2001  Internet Software Consortium.
4  *
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.
8  *
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.
16  */
17
18 /* $Id: lwdgabn.c,v 1.13.12.3 2004/03/08 04:04:19 marka Exp $ */
19
20 #include <config.h>
21
22 #include <stdlib.h>
23
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>
29
30 #include <dns/adb.h>
31 #include <dns/events.h>
32 #include <dns/result.h>
33
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>
40
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))
45
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 *);
49
50 /*
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.
53  */
54 static void
55 cleanup_gabn(ns_lwdclient_t *client) {
56         ns_lwdclient_log(50, "cleaning up client %p", client);
57
58         if (client->v6find != NULL) {
59                 if (client->v6find == client->v4find)
60                         client->v6find = NULL;
61                 else
62                         dns_adb_destroyfind(&client->v6find);
63         }
64         if (client->v4find != NULL)
65                 dns_adb_destroyfind(&client->v4find);
66 }
67
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;
75
76         if (at == DNS_ADBFIND_INET)
77                 af = AF_INET;
78         else
79                 af = AF_INET6;
80
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;
86
87                 addr = &client->addrs[client->gabn.naddrs];
88
89                 result = lwaddr_lwresaddr_fromsockaddr(addr, &ai->sockaddr);
90                 if (result != ISC_R_SUCCESS)
91                         goto next;
92
93                 ns_lwdclient_log(50, "adding address %p, family %d, length %d",
94                                  addr->address, addr->family, addr->length);
95
96                 client->gabn.naddrs++;
97                 REQUIRE(!LWRES_LINK_LINKED(addr, link));
98                 LWRES_LIST_APPEND(client->gabn.addrs, addr, link);
99
100         next:
101                 ai = ISC_LIST_NEXT(ai, publink);
102         }
103 }
104
105 typedef struct {
106         isc_netaddr_t address;
107         int rank;
108 } rankedaddress;
109
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);
115 }
116
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         void *arg;
124         ns_lwresd_t *lwresd = client->clientmgr->listener->manager;
125         unsigned int i;
126         isc_result_t result;
127
128         naddrs = client->gabn.naddrs;
129
130         if (naddrs <= 1 || lwresd->view->sortlist == NULL)
131                 return;
132
133         addrs = isc_mem_get(lwresd->mctx, sizeof(rankedaddress) * naddrs);
134         if (addrs == NULL)
135                 return;
136
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;
144         }
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);
150         }
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);
156         }
157
158         isc_mem_put(lwresd->mctx, addrs, sizeof(rankedaddress) * naddrs);
159 }
160
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;
168
169         cm = client->clientmgr;
170         lwb.base = NULL;
171
172         ns_lwdclient_log(50, "generating gabn reply for client %p", client);
173
174         /*
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.
178          */
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);
184
185         /*
186          * perhaps there are some here?
187          */
188         if (NEED_V6(client) && client->v4find != NULL)
189                 client->v6find = client->v4find;
190
191         /*
192          * Run through the finds we have and wire them up to the gabn
193          * structure.
194          */
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);
200
201         /*
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.
205          */
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;
214                         }
215                 } while (result == ISC_R_SUCCESS);
216         }
217
218         /*
219          * Render the packet.
220          */
221         client->pkt.recvlength = LWRES_RECVLENGTH;
222         client->pkt.authtype = 0; /* XXXMLG */
223         client->pkt.authlength = 0;
224
225         /*
226          * If there are no addresses, return failure.
227          */
228         if (client->gabn.naddrs != 0)
229                 client->pkt.result = LWRES_R_SUCCESS;
230         else
231                 client->pkt.result = LWRES_R_NOTFOUND;
232
233         sort_addresses(client);
234
235         lwres = lwres_gabnresponse_render(cm->lwctx, &client->gabn,
236                                           &client->pkt, &lwb);
237         if (lwres != LWRES_R_SUCCESS)
238                 goto out;
239
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;
247
248         NS_LWDCLIENT_SETSEND(client);
249
250         /*
251          * All done!
252          */
253         cleanup_gabn(client);
254
255         return;
256
257  out:
258         cleanup_gabn(client);
259
260         if (lwb.base != NULL)
261                 lwres_context_freemem(client->clientmgr->lwctx,
262                                       lwb.base, lwb.length);
263
264         ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
265 }
266
267 /*
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.
270  *
271  * Return success if it can be rendered, otherwise failure.  Note that
272  * not having enough alias slots open is NOT a failure.
273  */
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;
279
280         b = client->recv_buffer;
281
282         /*
283          * Render the new name to the buffer.
284          */
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);
289
290         /*
291          * Are there any open slots?
292          */
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++;
298         }
299
300         /*
301          * Save this name away as the current real name.
302          */
303         client->gabn.realname = (char *)(b.base) + b.used;
304         client->gabn.realnamelen = client->recv_buffer.used - b.used;
305
306         return (ISC_R_SUCCESS);
307 }
308
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;
314
315         b = client->recv_buffer;
316
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);
321
322         /*
323          * Render the new name to the buffer.
324          */
325         result = dns_name_totext(tname, ISC_TRUE, &client->recv_buffer);
326         if (result != ISC_R_SUCCESS)
327                 return (result);
328
329         /*
330          * Save this name away as the current real name.
331          */
332         client->gabn.realname = (char *) b.base + b.used;
333         client->gabn.realnamelen = client->recv_buffer.used - b.used;
334
335         return (ISC_R_SUCCESS);
336 }
337
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;
343
344         ns_lwdclient_log(50, "find done for task %p, client %p", task, client);
345
346         evtype = ev->ev_type;
347         isc_event_free(&ev);
348
349         /*
350          * No more info to be had?  If so, we have all the good stuff
351          * right now, so we can render things.
352          */
353         claimed = ISC_FALSE;
354         if (evtype == DNS_EVENT_ADBNOMOREADDRESSES) {
355                 if (NEED_V4(client)) {
356                         client->v4find = client->find;
357                         claimed = ISC_TRUE;
358                 }
359                 if (NEED_V6(client)) {
360                         client->v6find = client->find;
361                         claimed = ISC_TRUE;
362                 }
363                 if (client->find != NULL) {
364                         if (claimed)
365                                 client->find = NULL;
366                         else
367                                 dns_adb_destroyfind(&client->find);
368
369                 }
370                 generate_reply(client);
371                 return;
372         }
373
374         /*
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.
378          */
379         if ((client->find != client->v4find)
380             && (client->find != client->v6find)) {
381                 dns_adb_destroyfind(&client->find);
382         } else {
383                 client->find = NULL;
384         }
385
386         /*
387          * We have some new information we can gather.  Run off and fetch
388          * it.
389          */
390         if (evtype == DNS_EVENT_ADBMOREADDRESSES) {
391                 restart_find(client);
392                 return;
393         }
394
395         /*
396          * An error or other strangeness happened.  Drop this query.
397          */
398         cleanup_gabn(client);
399         ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
400 }
401
402 static void
403 restart_find(ns_lwdclient_t *client) {
404         unsigned int options;
405         isc_result_t result;
406         isc_boolean_t claimed;
407
408         ns_lwdclient_log(50, "starting find for client %p", client);
409
410         /*
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.
414          */
415         options = 0;
416         options |= DNS_ADBFIND_WANTEVENT;
417         options |= DNS_ADBFIND_RETURNLAME;
418
419         /*
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.
423          */
424         if (NEED_V4(client))
425                 options |= DNS_ADBFIND_INET;
426         if (NEED_V6(client))
427                 options |= DNS_ADBFIND_INET6;
428
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);
439
440         /*
441          * Did we get an alias?  If so, save it and re-issue the query.
442          */
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;
453                 }
454                 goto find_again;
455         }
456
457         ns_lwdclient_log(50, "find returned %d (%s)", result,
458                          isc_result_totext(result));
459
460         /*
461          * Did we get an error?
462          */
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;
469         }
470
471         claimed = ISC_FALSE;
472
473         /*
474          * Did we get our answer to V4 addresses?
475          */
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;
482         }
483
484         /*
485          * Did we get our answer to V6 addresses?
486          */
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;
493         }
494
495         /*
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.
500          *
501          * If we have both v4 and v6, and we are still getting an event,
502          * we have a programming error, so die hard.
503          */
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;
508         }
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);
514
515         /*
516          * We seem to have everything we asked for, or at least we are
517          * able to respond with things we've learned.
518          */
519
520         generate_reply(client);
521 }
522
523 static isc_result_t
524 start_find(ns_lwdclient_t *client) {
525         isc_result_t result;
526
527         /*
528          * Initialize the real name and alias arrays in the reply we're
529          * going to build up.
530          */
531         init_gabn(client);
532
533         result = store_realname(client);
534         if (result != ISC_R_SUCCESS)
535                 return (result);
536         restart_find(client);
537         return (ISC_R_SUCCESS);
538
539 }
540
541 static void
542 init_gabn(ns_lwdclient_t *client) {
543         int i;
544
545         /*
546          * Initialize the real name and alias arrays in the reply we're
547          * going to build up.
548          */
549         for (i = 0; i < LWRES_MAX_ALIASES; i++) {
550                 client->aliases[i] = NULL;
551                 client->aliaslen[i] = 0;
552         }
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);
558         }
559
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;
569
570         /*
571          * Set up the internal buffer to point to the receive region.
572          */
573         isc_buffer_init(&client->recv_buffer, client->buffer, LWRES_RECVLENGTH);
574 }
575
576 /*
577  * When we are called, we can be assured that:
578  *
579  *      client->sockaddr contains the address we need to reply to,
580  *
581  *      client->pkt contains the packet header data,
582  *
583  *      the packet "checks out" overall -- any MD5 hashes or crypto
584  *      bits have been verified,
585  *
586  *      "b" points to the remaining data after the packet header
587  *      was parsed off.
588  *
589  *      We are in a the RECVDONE state.
590  *
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.
594  */
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;
601
602         REQUIRE(NS_LWDCLIENT_ISRECVDONE(client));
603
604         cm = client->clientmgr;
605         req = NULL;
606
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;
613
614         isc_buffer_init(&namebuf, req->name, req->namelen);
615         isc_buffer_add(&namebuf, req->namelen);
616
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);
628
629         client->find_wanted = req->addrtypes;
630         ns_lwdclient_log(50, "client %p looking for addrtypes %08x",
631                          client, client->find_wanted);
632
633         /*
634          * We no longer need to keep this around.
635          */
636         lwres_gabnrequest_free(client->clientmgr->lwctx, &req);
637
638         /*
639          * Start the find.
640          */
641         result = start_find(client);
642         if (result != ISC_R_SUCCESS)
643                 goto out;
644
645         return;
646
647         /*
648          * We're screwed.  Return an error packet to our caller.
649          */
650  out:
651         if (req != NULL)
652                 lwres_gabnrequest_free(client->clientmgr->lwctx, &req);
653
654         ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
655 }