1 /* $NetBSD: lwdgabn.c,v 1.4 2014/12/10 04:37:51 christos Exp $ */
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 */
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?) */
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
*);
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.
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
;
66 dns_adb_destroyfind(&client
->v6find
);
68 if (client
->v4find
!= NULL
)
69 dns_adb_destroyfind(&client
->v4find
);
73 setup_addresses(ns_lwdclient_t
*client
, dns_adbfind_t
*find
, unsigned int at
) {
74 dns_adbaddrinfo_t
*ai
;
77 const struct sockaddr
*sa
;
80 if (at
== DNS_ADBFIND_INET
)
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
)
91 addr
= &client
->addrs
[client
->gabn
.naddrs
];
93 result
= lwaddr_lwresaddr_fromsockaddr(addr
, &ai
->sockaddr
);
94 if (result
!= ISC_R_SUCCESS
)
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
);
105 ai
= ISC_LIST_NEXT(ai
, publink
);
110 isc_netaddr_t address
;
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
);
122 sort_addresses(ns_lwdclient_t
*client
) {
124 rankedaddress
*addrs
;
125 isc_netaddr_t remote
;
126 dns_addressorderfunc_t order
;
128 ns_lwresd_t
*lwresd
= client
->clientmgr
->listener
->manager
;
132 naddrs
= client
->gabn
.naddrs
;
134 if (naddrs
<= 1 || lwresd
->view
->sortlist
== NULL
)
137 addrs
= isc_mem_get(lwresd
->mctx
, sizeof(rankedaddress
) * naddrs
);
141 isc_netaddr_fromsockaddr(&remote
, &client
->address
);
142 ns_sortlist_byaddrsetup(lwresd
->view
->sortlist
,
143 &remote
, &order
, &arg
);
145 isc_mem_put(lwresd
->mctx
, addrs
,
146 sizeof(rankedaddress
) * naddrs
);
149 for (i
= 0; i
< naddrs
; i
++) {
150 result
= lwaddr_netaddr_fromlwresaddr(&addrs
[i
].address
,
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
],
159 INSIST(result
== ISC_R_SUCCESS
);
162 isc_mem_put(lwresd
->mctx
, addrs
, sizeof(rankedaddress
) * naddrs
);
166 generate_reply(ns_lwdclient_t
*client
) {
171 ns_lwdclientmgr_t
*cm
;
173 cm
= client
->clientmgr
;
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
)
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
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) {
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
)
219 } while (result
== ISC_R_SUCCESS
);
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
;
235 client
->pkt
.result
= LWRES_R_NOTFOUND
;
237 sort_addresses(client
);
239 lwres
= lwres_gabnresponse_render(cm
->lwctx
, &client
->gabn
,
241 if (lwres
!= LWRES_R_SUCCESS
)
246 client
->sendbuf
= r
.base
;
247 client
->sendlength
= r
.length
;
248 result
= ns_lwdclient_sendreply(client
, &r
);
249 if (result
!= ISC_R_SUCCESS
)
252 NS_LWDCLIENT_SETSEND(client
);
257 cleanup_gabn(client
);
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.
279 add_alias(ns_lwdclient_t
*client
) {
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
)
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
);
314 store_realname(ns_lwdclient_t
*client
) {
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
)
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
)
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
);
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
;
354 * No more info to be had? If so, we have all the good stuff
355 * right now, so we can render things.
358 if (evtype
== DNS_EVENT_ADBNOMOREADDRESSES
) {
359 if (NEED_V4(client
)) {
360 client
->v4find
= client
->find
;
363 if (NEED_V6(client
)) {
364 client
->v6find
= client
->find
;
367 if (client
->find
!= NULL
) {
371 dns_adb_destroyfind(&client
->find
);
374 generate_reply(client
);
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
383 if ((client
->find
!= client
->v4find
)
384 && (client
->find
!= client
->v6find
)) {
385 dns_adb_destroyfind(&client
->find
);
391 * We have some new information we can gather. Run off and fetch
394 if (evtype
== DNS_EVENT_ADBMOREADDRESSES
) {
395 restart_find(client
);
400 * An error or other strangeness happened. Drop this query.
402 cleanup_gabn(client
);
403 ns_lwdclient_errorpktsend(client
, LWRES_R_FAILURE
);
407 restart_find(ns_lwdclient_t
*client
) {
408 unsigned int options
;
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
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.
429 options
|= DNS_ADBFIND_INET
;
431 options
|= DNS_ADBFIND_INET6
;
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
,
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
) {
454 "out of buffer space adding alias");
455 ns_lwdclient_errorpktsend(client
, LWRES_R_FAILURE
);
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
);
478 * Did we get our answer to V4 addresses?
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
);
485 client
->v4find
= client
->find
;
489 * Did we get our answer to V6 addresses?
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
);
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
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
);
513 ns_lwdclient_log(50, "no event will be sent");
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
);
528 start_find(ns_lwdclient_t
*client
) {
532 * Initialize the real name and alias arrays in the reply we're
537 result
= store_realname(client
);
538 if (result
!= ISC_R_SUCCESS
)
540 restart_find(client
);
541 return (ISC_R_SUCCESS
);
546 init_gabn(ns_lwdclient_t
*client
) {
550 * Initialize the real name and alias arrays in the reply we're
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
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.
600 ns_lwdclient_processgabn(ns_lwdclient_t
*client
, lwres_buffer_t
*b
) {
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
;
611 result
= lwres_gabnrequest_parse(client
->clientmgr
->lwctx
,
612 b
, &client
->pkt
, &req
);
613 if (result
!= LWRES_R_SUCCESS
)
615 if (req
->name
== NULL
)
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
)
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
);
645 result
= start_find(client
);
646 if (result
!= ISC_R_SUCCESS
)
652 * We're screwed. Return an error packet to our caller.
656 lwres_gabnrequest_free(client
->clientmgr
->lwctx
, &req
);
658 ns_lwdclient_errorpktsend(client
, LWRES_R_FAILURE
);