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.15.18.5 2006/03/02 00:37:21 marka Exp $ */
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?) */
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
*);
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.
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
;
64 dns_adb_destroyfind(&client
->v6find
);
66 if (client
->v4find
!= NULL
)
67 dns_adb_destroyfind(&client
->v4find
);
71 setup_addresses(ns_lwdclient_t
*client
, dns_adbfind_t
*find
, unsigned int at
) {
72 dns_adbaddrinfo_t
*ai
;
75 const struct sockaddr
*sa
;
78 if (at
== DNS_ADBFIND_INET
)
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
)
89 addr
= &client
->addrs
[client
->gabn
.naddrs
];
91 result
= lwaddr_lwresaddr_fromsockaddr(addr
, &ai
->sockaddr
);
92 if (result
!= ISC_R_SUCCESS
)
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
);
103 ai
= ISC_LIST_NEXT(ai
, publink
);
108 isc_netaddr_t address
;
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
);
120 sort_addresses(ns_lwdclient_t
*client
) {
122 rankedaddress
*addrs
;
123 isc_netaddr_t remote
;
124 dns_addressorderfunc_t order
;
126 ns_lwresd_t
*lwresd
= client
->clientmgr
->listener
->manager
;
130 naddrs
= client
->gabn
.naddrs
;
132 if (naddrs
<= 1 || lwresd
->view
->sortlist
== NULL
)
135 addrs
= isc_mem_get(lwresd
->mctx
, sizeof(rankedaddress
) * naddrs
);
139 isc_netaddr_fromsockaddr(&remote
, &client
->address
);
140 ns_sortlist_byaddrsetup(lwresd
->view
->sortlist
,
141 &remote
, &order
, &arg
);
143 isc_mem_put(lwresd
->mctx
, addrs
,
144 sizeof(rankedaddress
) * naddrs
);
147 for (i
= 0; i
< naddrs
; i
++) {
148 result
= lwaddr_netaddr_fromlwresaddr(&addrs
[i
].address
,
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
],
157 INSIST(result
== ISC_R_SUCCESS
);
160 isc_mem_put(lwresd
->mctx
, addrs
, sizeof(rankedaddress
) * naddrs
);
164 generate_reply(ns_lwdclient_t
*client
) {
169 ns_lwdclientmgr_t
*cm
;
171 cm
= client
->clientmgr
;
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
)
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
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) {
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
)
217 } while (result
== ISC_R_SUCCESS
);
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
;
233 client
->pkt
.result
= LWRES_R_NOTFOUND
;
235 sort_addresses(client
);
237 lwres
= lwres_gabnresponse_render(cm
->lwctx
, &client
->gabn
,
239 if (lwres
!= LWRES_R_SUCCESS
)
244 client
->sendbuf
= r
.base
;
245 client
->sendlength
= r
.length
;
246 result
= ns_lwdclient_sendreply(client
, &r
);
247 if (result
!= ISC_R_SUCCESS
)
250 NS_LWDCLIENT_SETSEND(client
);
255 cleanup_gabn(client
);
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.
277 add_alias(ns_lwdclient_t
*client
) {
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
)
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
);
312 store_realname(ns_lwdclient_t
*client
) {
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
)
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
)
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
);
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
;
352 * No more info to be had? If so, we have all the good stuff
353 * right now, so we can render things.
356 if (evtype
== DNS_EVENT_ADBNOMOREADDRESSES
) {
357 if (NEED_V4(client
)) {
358 client
->v4find
= client
->find
;
361 if (NEED_V6(client
)) {
362 client
->v6find
= client
->find
;
365 if (client
->find
!= NULL
) {
369 dns_adb_destroyfind(&client
->find
);
372 generate_reply(client
);
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
381 if ((client
->find
!= client
->v4find
)
382 && (client
->find
!= client
->v6find
)) {
383 dns_adb_destroyfind(&client
->find
);
389 * We have some new information we can gather. Run off and fetch
392 if (evtype
== DNS_EVENT_ADBMOREADDRESSES
) {
393 restart_find(client
);
398 * An error or other strangeness happened. Drop this query.
400 cleanup_gabn(client
);
401 ns_lwdclient_errorpktsend(client
, LWRES_R_FAILURE
);
405 restart_find(ns_lwdclient_t
*client
) {
406 unsigned int options
;
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
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.
427 options
|= DNS_ADBFIND_INET
;
429 options
|= DNS_ADBFIND_INET6
;
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
,
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
) {
452 "out of buffer space adding alias");
453 ns_lwdclient_errorpktsend(client
, LWRES_R_FAILURE
);
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
);
476 * Did we get our answer to V4 addresses?
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
);
483 client
->v4find
= client
->find
;
487 * Did we get our answer to V6 addresses?
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
);
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
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
);
511 ns_lwdclient_log(50, "no event will be sent");
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
);
526 start_find(ns_lwdclient_t
*client
) {
530 * Initialize the real name and alias arrays in the reply we're
535 result
= store_realname(client
);
536 if (result
!= ISC_R_SUCCESS
)
538 restart_find(client
);
539 return (ISC_R_SUCCESS
);
544 init_gabn(ns_lwdclient_t
*client
) {
548 * Initialize the real name and alias arrays in the reply we're
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
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.
598 ns_lwdclient_processgabn(ns_lwdclient_t
*client
, lwres_buffer_t
*b
) {
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
;
609 result
= lwres_gabnrequest_parse(client
->clientmgr
->lwctx
,
610 b
, &client
->pkt
, &req
);
611 if (result
!= LWRES_R_SUCCESS
)
613 if (req
->name
== NULL
)
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
)
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
);
643 result
= start_find(client
);
644 if (result
!= ISC_R_SUCCESS
)
650 * We're screwed. Return an error packet to our caller.
654 lwres_gabnrequest_free(client
->clientmgr
->lwctx
, &req
);
656 ns_lwdclient_errorpktsend(client
, LWRES_R_FAILURE
);