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 $ */
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?) */
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.
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
;
62 dns_adb_destroyfind(&client
->v6find
);
64 if (client
->v4find
!= NULL
)
65 dns_adb_destroyfind(&client
->v4find
);
69 setup_addresses(ns_lwdclient_t
*client
, dns_adbfind_t
*find
, unsigned int at
) {
70 dns_adbaddrinfo_t
*ai
;
73 const struct sockaddr
*sa
;
76 if (at
== DNS_ADBFIND_INET
)
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
)
87 addr
= &client
->addrs
[client
->gabn
.naddrs
];
89 result
= lwaddr_lwresaddr_fromsockaddr(addr
, &ai
->sockaddr
);
90 if (result
!= ISC_R_SUCCESS
)
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
);
101 ai
= ISC_LIST_NEXT(ai
, publink
);
106 isc_netaddr_t address
;
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
);
118 sort_addresses(ns_lwdclient_t
*client
) {
120 rankedaddress
*addrs
;
121 isc_netaddr_t remote
;
122 dns_addressorderfunc_t order
;
124 ns_lwresd_t
*lwresd
= client
->clientmgr
->listener
->manager
;
128 naddrs
= client
->gabn
.naddrs
;
130 if (naddrs
<= 1 || lwresd
->view
->sortlist
== NULL
)
133 addrs
= isc_mem_get(lwresd
->mctx
, sizeof(rankedaddress
) * naddrs
);
137 isc_netaddr_fromsockaddr(&remote
, &client
->address
);
138 ns_sortlist_byaddrsetup(lwresd
->view
->sortlist
,
139 &remote
, &order
, &arg
);
141 isc_mem_put(lwresd
->mctx
, addrs
,
142 sizeof(rankedaddress
) * naddrs
);
145 for (i
= 0; i
< naddrs
; i
++) {
146 result
= lwaddr_netaddr_fromlwresaddr(&addrs
[i
].address
,
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
],
155 INSIST(result
== ISC_R_SUCCESS
);
158 isc_mem_put(lwresd
->mctx
, addrs
, sizeof(rankedaddress
) * naddrs
);
162 generate_reply(ns_lwdclient_t
*client
) {
167 ns_lwdclientmgr_t
*cm
;
169 cm
= client
->clientmgr
;
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
)
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
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) {
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
)
215 } while (result
== ISC_R_SUCCESS
);
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
;
231 client
->pkt
.result
= LWRES_R_NOTFOUND
;
233 sort_addresses(client
);
235 lwres
= lwres_gabnresponse_render(cm
->lwctx
, &client
->gabn
,
237 if (lwres
!= LWRES_R_SUCCESS
)
242 client
->sendbuf
= r
.base
;
243 client
->sendlength
= r
.length
;
244 result
= ns_lwdclient_sendreply(client
, &r
);
245 if (result
!= ISC_R_SUCCESS
)
248 NS_LWDCLIENT_SETSEND(client
);
253 cleanup_gabn(client
);
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.
275 add_alias(ns_lwdclient_t
*client
) {
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
)
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
);
310 store_realname(ns_lwdclient_t
*client
) {
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
)
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
)
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
);
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
;
350 * No more info to be had? If so, we have all the good stuff
351 * right now, so we can render things.
354 if (evtype
== DNS_EVENT_ADBNOMOREADDRESSES
) {
355 if (NEED_V4(client
)) {
356 client
->v4find
= client
->find
;
359 if (NEED_V6(client
)) {
360 client
->v6find
= client
->find
;
363 if (client
->find
!= NULL
) {
367 dns_adb_destroyfind(&client
->find
);
370 generate_reply(client
);
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
379 if ((client
->find
!= client
->v4find
)
380 && (client
->find
!= client
->v6find
)) {
381 dns_adb_destroyfind(&client
->find
);
387 * We have some new information we can gather. Run off and fetch
390 if (evtype
== DNS_EVENT_ADBMOREADDRESSES
) {
391 restart_find(client
);
396 * An error or other strangeness happened. Drop this query.
398 cleanup_gabn(client
);
399 ns_lwdclient_errorpktsend(client
, LWRES_R_FAILURE
);
403 restart_find(ns_lwdclient_t
*client
) {
404 unsigned int options
;
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
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.
425 options
|= DNS_ADBFIND_INET
;
427 options
|= DNS_ADBFIND_INET6
;
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
,
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
) {
450 "out of buffer space adding alias");
451 ns_lwdclient_errorpktsend(client
, LWRES_R_FAILURE
);
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
);
474 * Did we get our answer to V4 addresses?
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
);
481 client
->v4find
= client
->find
;
485 * Did we get our answer to V6 addresses?
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
);
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
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
);
509 ns_lwdclient_log(50, "no event will be sent");
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
);
524 start_find(ns_lwdclient_t
*client
) {
528 * Initialize the real name and alias arrays in the reply we're
533 result
= store_realname(client
);
534 if (result
!= ISC_R_SUCCESS
)
536 restart_find(client
);
537 return (ISC_R_SUCCESS
);
542 init_gabn(ns_lwdclient_t
*client
) {
546 * Initialize the real name and alias arrays in the reply we're
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
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.
596 ns_lwdclient_processgabn(ns_lwdclient_t
*client
, lwres_buffer_t
*b
) {
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
;
607 result
= lwres_gabnrequest_parse(client
->clientmgr
->lwctx
,
608 b
, &client
->pkt
, &req
);
609 if (result
!= LWRES_R_SUCCESS
)
611 if (req
->name
== NULL
)
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
)
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
);
641 result
= start_find(client
);
642 if (result
!= ISC_R_SUCCESS
)
648 * We're screwed. Return an error packet to our caller.
652 lwres_gabnrequest_free(client
->clientmgr
->lwctx
, &req
);
654 ns_lwdclient_errorpktsend(client
, LWRES_R_FAILURE
);