Detect FPU by checking CPUID features.
[dragonfly.git] / contrib / bind-9.5.2 / bin / named / lwdgabn.c
1 /*
2  * Copyright (C) 2004-2007  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2000, 2001  Internet Software Consortium.
4  *
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.
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.22 2007/06/19 23:46:59 tbox Exp $ */
19
20 /*! \file */
21
22 #include <config.h>
23
24 #include <stdlib.h>
25
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>
31
32 #include <dns/adb.h>
33 #include <dns/events.h>
34 #include <dns/result.h>
35
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>
42
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))
47
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 *);
51
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.
55  */
56 static void
57 cleanup_gabn(ns_lwdclient_t *client) {
58         ns_lwdclient_log(50, "cleaning up client %p", client);
59
60         if (client->v6find != NULL) {
61                 if (client->v6find == client->v4find)
62                         client->v6find = NULL;
63                 else
64                         dns_adb_destroyfind(&client->v6find);
65         }
66         if (client->v4find != NULL)
67                 dns_adb_destroyfind(&client->v4find);
68 }
69
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;
77
78         if (at == DNS_ADBFIND_INET)
79                 af = AF_INET;
80         else
81                 af = AF_INET6;
82
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;
88
89                 addr = &client->addrs[client->gabn.naddrs];
90
91                 result = lwaddr_lwresaddr_fromsockaddr(addr, &ai->sockaddr);
92                 if (result != ISC_R_SUCCESS)
93                         goto next;
94
95                 ns_lwdclient_log(50, "adding address %p, family %d, length %d",
96                                  addr->address, addr->family, addr->length);
97
98                 client->gabn.naddrs++;
99                 REQUIRE(!LWRES_LINK_LINKED(addr, link));
100                 LWRES_LIST_APPEND(client->gabn.addrs, addr, link);
101
102         next:
103                 ai = ISC_LIST_NEXT(ai, publink);
104         }
105 }
106
107 typedef struct {
108         isc_netaddr_t address;
109         int rank;
110 } rankedaddress;
111
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);
117 }
118
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;
129
130         naddrs = client->gabn.naddrs;
131
132         if (naddrs <= 1 || lwresd->view->sortlist == NULL)
133                 return;
134
135         addrs = isc_mem_get(lwresd->mctx, sizeof(rankedaddress) * naddrs);
136         if (addrs == NULL)
137                 return;
138
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;
146         }
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);
152         }
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);
158         }
159
160         isc_mem_put(lwresd->mctx, addrs, sizeof(rankedaddress) * naddrs);
161 }
162
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;
170
171         cm = client->clientmgr;
172         lwb.base = NULL;
173
174         ns_lwdclient_log(50, "generating gabn reply for client %p", client);
175
176         /*
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.
180          */
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);
186
187         /*
188          * perhaps there are some here?
189          */
190         if (NEED_V6(client) && client->v4find != NULL)
191                 client->v6find = client->v4find;
192
193         /*
194          * Run through the finds we have and wire them up to the gabn
195          * structure.
196          */
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);
202
203         /*
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.
207          */
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;
216                         }
217                 } while (result == ISC_R_SUCCESS);
218         }
219
220         /*
221          * Render the packet.
222          */
223         client->pkt.recvlength = LWRES_RECVLENGTH;
224         client->pkt.authtype = 0; /* XXXMLG */
225         client->pkt.authlength = 0;
226
227         /*
228          * If there are no addresses, return failure.
229          */
230         if (client->gabn.naddrs != 0)
231                 client->pkt.result = LWRES_R_SUCCESS;
232         else
233                 client->pkt.result = LWRES_R_NOTFOUND;
234
235         sort_addresses(client);
236
237         lwres = lwres_gabnresponse_render(cm->lwctx, &client->gabn,
238                                           &client->pkt, &lwb);
239         if (lwres != LWRES_R_SUCCESS)
240                 goto out;
241
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;
249
250         NS_LWDCLIENT_SETSEND(client);
251
252         /*
253          * All done!
254          */
255         cleanup_gabn(client);
256
257         return;
258
259  out:
260         cleanup_gabn(client);
261
262         if (lwb.base != NULL)
263                 lwres_context_freemem(client->clientmgr->lwctx,
264                                       lwb.base, lwb.length);
265
266         ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
267 }
268
269 /*
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.
272  *
273  * Return success if it can be rendered, otherwise failure.  Note that
274  * not having enough alias slots open is NOT a failure.
275  */
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;
281
282         b = client->recv_buffer;
283
284         /*
285          * Render the new name to the buffer.
286          */
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);
291
292         /*
293          * Are there any open slots?
294          */
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++;
300         }
301
302         /*
303          * Save this name away as the current real name.
304          */
305         client->gabn.realname = (char *)(b.base) + b.used;
306         client->gabn.realnamelen = client->recv_buffer.used - b.used;
307
308         return (ISC_R_SUCCESS);
309 }
310
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;
316
317         b = client->recv_buffer;
318
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);
323
324         /*
325          * Render the new name to the buffer.
326          */
327         result = dns_name_totext(tname, ISC_TRUE, &client->recv_buffer);
328         if (result != ISC_R_SUCCESS)
329                 return (result);
330
331         /*
332          * Save this name away as the current real name.
333          */
334         client->gabn.realname = (char *) b.base + b.used;
335         client->gabn.realnamelen = client->recv_buffer.used - b.used;
336
337         return (ISC_R_SUCCESS);
338 }
339
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;
345
346         ns_lwdclient_log(50, "find done for task %p, client %p", task, client);
347
348         evtype = ev->ev_type;
349         isc_event_free(&ev);
350
351         /*
352          * No more info to be had?  If so, we have all the good stuff
353          * right now, so we can render things.
354          */
355         claimed = ISC_FALSE;
356         if (evtype == DNS_EVENT_ADBNOMOREADDRESSES) {
357                 if (NEED_V4(client)) {
358                         client->v4find = client->find;
359                         claimed = ISC_TRUE;
360                 }
361                 if (NEED_V6(client)) {
362                         client->v6find = client->find;
363                         claimed = ISC_TRUE;
364                 }
365                 if (client->find != NULL) {
366                         if (claimed)
367                                 client->find = NULL;
368                         else
369                                 dns_adb_destroyfind(&client->find);
370
371                 }
372                 generate_reply(client);
373                 return;
374         }
375
376         /*
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.
380          */
381         if ((client->find != client->v4find)
382             && (client->find != client->v6find)) {
383                 dns_adb_destroyfind(&client->find);
384         } else {
385                 client->find = NULL;
386         }
387
388         /*
389          * We have some new information we can gather.  Run off and fetch
390          * it.
391          */
392         if (evtype == DNS_EVENT_ADBMOREADDRESSES) {
393                 restart_find(client);
394                 return;
395         }
396
397         /*
398          * An error or other strangeness happened.  Drop this query.
399          */
400         cleanup_gabn(client);
401         ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
402 }
403
404 static void
405 restart_find(ns_lwdclient_t *client) {
406         unsigned int options;
407         isc_result_t result;
408         isc_boolean_t claimed;
409
410         ns_lwdclient_log(50, "starting find for client %p", client);
411
412         /*
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.
416          */
417         options = 0;
418         options |= DNS_ADBFIND_WANTEVENT;
419         options |= DNS_ADBFIND_RETURNLAME;
420
421         /*
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.
425          */
426         if (NEED_V4(client))
427                 options |= DNS_ADBFIND_INET;
428         if (NEED_V6(client))
429                 options |= DNS_ADBFIND_INET6;
430
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);
441
442         /*
443          * Did we get an alias?  If so, save it and re-issue the query.
444          */
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;
455                 }
456                 goto find_again;
457         }
458
459         ns_lwdclient_log(50, "find returned %d (%s)", result,
460                          isc_result_totext(result));
461
462         /*
463          * Did we get an error?
464          */
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;
471         }
472
473         claimed = ISC_FALSE;
474
475         /*
476          * Did we get our answer to V4 addresses?
477          */
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;
484         }
485
486         /*
487          * Did we get our answer to V6 addresses?
488          */
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;
495         }
496
497         /*
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.
502          *
503          * If we have both v4 and v6, and we are still getting an event,
504          * we have a programming error, so die hard.
505          */
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;
510         }
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);
516
517         /*
518          * We seem to have everything we asked for, or at least we are
519          * able to respond with things we've learned.
520          */
521
522         generate_reply(client);
523 }
524
525 static isc_result_t
526 start_find(ns_lwdclient_t *client) {
527         isc_result_t result;
528
529         /*
530          * Initialize the real name and alias arrays in the reply we're
531          * going to build up.
532          */
533         init_gabn(client);
534
535         result = store_realname(client);
536         if (result != ISC_R_SUCCESS)
537                 return (result);
538         restart_find(client);
539         return (ISC_R_SUCCESS);
540
541 }
542
543 static void
544 init_gabn(ns_lwdclient_t *client) {
545         int i;
546
547         /*
548          * Initialize the real name and alias arrays in the reply we're
549          * going to build up.
550          */
551         for (i = 0; i < LWRES_MAX_ALIASES; i++) {
552                 client->aliases[i] = NULL;
553                 client->aliaslen[i] = 0;
554         }
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);
560         }
561
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;
571
572         /*
573          * Set up the internal buffer to point to the receive region.
574          */
575         isc_buffer_init(&client->recv_buffer, client->buffer, LWRES_RECVLENGTH);
576 }
577
578 /*
579  * When we are called, we can be assured that:
580  *
581  *      client->sockaddr contains the address we need to reply to,
582  *
583  *      client->pkt contains the packet header data,
584  *
585  *      the packet "checks out" overall -- any MD5 hashes or crypto
586  *      bits have been verified,
587  *
588  *      "b" points to the remaining data after the packet header
589  *      was parsed off.
590  *
591  *      We are in a the RECVDONE state.
592  *
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.
596  */
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;
603
604         REQUIRE(NS_LWDCLIENT_ISRECVDONE(client));
605
606         cm = client->clientmgr;
607         req = NULL;
608
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;
615
616         isc_buffer_init(&namebuf, req->name, req->namelen);
617         isc_buffer_add(&namebuf, req->namelen);
618
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);
630
631         client->find_wanted = req->addrtypes;
632         ns_lwdclient_log(50, "client %p looking for addrtypes %08x",
633                          client, client->find_wanted);
634
635         /*
636          * We no longer need to keep this around.
637          */
638         lwres_gabnrequest_free(client->clientmgr->lwctx, &req);
639
640         /*
641          * Start the find.
642          */
643         result = start_find(client);
644         if (result != ISC_R_SUCCESS)
645                 goto out;
646
647         return;
648
649         /*
650          * We're screwed.  Return an error packet to our caller.
651          */
652  out:
653         if (req != NULL)
654                 lwres_gabnrequest_free(client->clientmgr->lwctx, &req);
655
656         ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
657 }