1/* $NetBSD$ */ 2 3/* 4 * Copyright (C) 2004-2007, 2009 Internet Systems Consortium, Inc. ("ISC") 5 * Copyright (C) 2000, 2001 Internet Software Consortium. 6 * 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. 10 * 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. 18 */ 19 20/* Id: lwdgabn.c,v 1.24 2009/09/02 23:48:01 tbox Exp */ 21 22/*! \file */ 23 24#include <config.h> 25 26#include <stdlib.h> 27 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?) */ 32#include <isc/util.h> 33 34#include <dns/adb.h> 35#include <dns/events.h> 36#include <dns/result.h> 37 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> 44 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)) 49 50static isc_result_t start_find(ns_lwdclient_t *); 51static void restart_find(ns_lwdclient_t *); 52static void init_gabn(ns_lwdclient_t *); 53 54/*% 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. 57 */ 58static void 59cleanup_gabn(ns_lwdclient_t *client) { 60 ns_lwdclient_log(50, "cleaning up client %p", client); 61 62 if (client->v6find != NULL) { 63 if (client->v6find == client->v4find) 64 client->v6find = NULL; 65 else 66 dns_adb_destroyfind(&client->v6find); 67 } 68 if (client->v4find != NULL) 69 dns_adb_destroyfind(&client->v4find); 70} 71 72static void 73setup_addresses(ns_lwdclient_t *client, dns_adbfind_t *find, unsigned int at) { 74 dns_adbaddrinfo_t *ai; 75 lwres_addr_t *addr; 76 int af; 77 const struct sockaddr *sa; 78 isc_result_t result; 79 80 if (at == DNS_ADBFIND_INET) 81 af = AF_INET; 82 else 83 af = AF_INET6; 84 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) 89 goto next; 90 91 addr = &client->addrs[client->gabn.naddrs]; 92 93 result = lwaddr_lwresaddr_fromsockaddr(addr, &ai->sockaddr); 94 if (result != ISC_R_SUCCESS) 95 goto next; 96 97 ns_lwdclient_log(50, "adding address %p, family %d, length %d", 98 addr->address, addr->family, addr->length); 99 100 client->gabn.naddrs++; 101 REQUIRE(!LWRES_LINK_LINKED(addr, link)); 102 LWRES_LIST_APPEND(client->gabn.addrs, addr, link); 103 104 next: 105 ai = ISC_LIST_NEXT(ai, publink); 106 } 107} 108 109typedef struct { 110 isc_netaddr_t address; 111 int rank; 112} rankedaddress; 113 114static int 115addr_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); 119} 120 121static void 122sort_addresses(ns_lwdclient_t *client) { 123 unsigned int naddrs; 124 rankedaddress *addrs; 125 isc_netaddr_t remote; 126 dns_addressorderfunc_t order; 127 const void *arg; 128 ns_lwresd_t *lwresd = client->clientmgr->listener->manager; 129 unsigned int i; 130 isc_result_t result; 131 132 naddrs = client->gabn.naddrs; 133 134 if (naddrs <= 1 || lwresd->view->sortlist == NULL) 135 return; 136 137 addrs = isc_mem_get(lwresd->mctx, sizeof(rankedaddress) * naddrs); 138 if (addrs == NULL) 139 return; 140 141 isc_netaddr_fromsockaddr(&remote, &client->address); 142 ns_sortlist_byaddrsetup(lwresd->view->sortlist, 143 &remote, &order, &arg); 144 if (order == NULL) { 145 isc_mem_put(lwresd->mctx, addrs, 146 sizeof(rankedaddress) * naddrs); 147 return; 148 } 149 for (i = 0; i < naddrs; i++) { 150 result = lwaddr_netaddr_fromlwresaddr(&addrs[i].address, 151 &client->addrs[i]); 152 INSIST(result == ISC_R_SUCCESS); 153 addrs[i].rank = (*order)(&addrs[i].address, arg); 154 } 155 qsort(addrs, naddrs, sizeof(rankedaddress), addr_compare); 156 for (i = 0; i < naddrs; i++) { 157 result = lwaddr_lwresaddr_fromnetaddr(&client->addrs[i], 158 &addrs[i].address); 159 INSIST(result == ISC_R_SUCCESS); 160 } 161 162 isc_mem_put(lwresd->mctx, addrs, sizeof(rankedaddress) * naddrs); 163} 164 165static void 166generate_reply(ns_lwdclient_t *client) { 167 isc_result_t result; 168 int lwres; 169 isc_region_t r; 170 lwres_buffer_t lwb; 171 ns_lwdclientmgr_t *cm; 172 173 cm = client->clientmgr; 174 lwb.base = NULL; 175 176 ns_lwdclient_log(50, "generating gabn reply for client %p", client); 177 178 /* 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. 182 */ 183 if (client->find == client->v4find || client->find == client->v6find) 184 client->find = NULL; 185 else 186 if (client->find != NULL) 187 dns_adb_destroyfind(&client->find); 188 189 /* 190 * perhaps there are some here? 191 */ 192 if (NEED_V6(client) && client->v4find != NULL) 193 client->v6find = client->v4find; 194 195 /* 196 * Run through the finds we have and wire them up to the gabn 197 * structure. 198 */ 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); 204 205 /* 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. 209 */ 210 if (client->gabn.naddrs == 0) { 211 do { 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) 217 return; 218 } 219 } while (result == ISC_R_SUCCESS); 220 } 221 222 /* 223 * Render the packet. 224 */ 225 client->pkt.recvlength = LWRES_RECVLENGTH; 226 client->pkt.authtype = 0; /* XXXMLG */ 227 client->pkt.authlength = 0; 228 229 /* 230 * If there are no addresses, return failure. 231 */ 232 if (client->gabn.naddrs != 0) 233 client->pkt.result = LWRES_R_SUCCESS; 234 else 235 client->pkt.result = LWRES_R_NOTFOUND; 236 237 sort_addresses(client); 238 239 lwres = lwres_gabnresponse_render(cm->lwctx, &client->gabn, 240 &client->pkt, &lwb); 241 if (lwres != LWRES_R_SUCCESS) 242 goto out; 243 244 r.base = lwb.base; 245 r.length = lwb.used; 246 client->sendbuf = r.base; 247 client->sendlength = r.length; 248 result = ns_lwdclient_sendreply(client, &r); 249 if (result != ISC_R_SUCCESS) 250 goto out; 251 252 NS_LWDCLIENT_SETSEND(client); 253 254 /* 255 * All done! 256 */ 257 cleanup_gabn(client); 258 259 return; 260 261 out: 262 cleanup_gabn(client); 263 264 if (lwb.base != NULL) 265 lwres_context_freemem(client->clientmgr->lwctx, 266 lwb.base, lwb.length); 267 268 ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE); 269} 270 271/* 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. 274 * 275 * Return success if it can be rendered, otherwise failure. Note that 276 * not having enough alias slots open is NOT a failure. 277 */ 278static isc_result_t 279add_alias(ns_lwdclient_t *client) { 280 isc_buffer_t b; 281 isc_result_t result; 282 isc_uint16_t naliases; 283 284 b = client->recv_buffer; 285 286 /* 287 * Render the new name to the buffer. 288 */ 289 result = dns_name_totext(dns_fixedname_name(&client->target_name), 290 ISC_TRUE, &client->recv_buffer); 291 if (result != ISC_R_SUCCESS) 292 return (result); 293 294 /* 295 * Are there any open slots? 296 */ 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++; 302 } 303 304 /* 305 * Save this name away as the current real name. 306 */ 307 client->gabn.realname = (char *)(b.base) + b.used; 308 client->gabn.realnamelen = client->recv_buffer.used - b.used; 309 310 return (ISC_R_SUCCESS); 311} 312 313static isc_result_t 314store_realname(ns_lwdclient_t *client) { 315 isc_buffer_t b; 316 isc_result_t result; 317 dns_name_t *tname; 318 319 b = client->recv_buffer; 320 321 tname = dns_fixedname_name(&client->target_name); 322 result = ns_lwsearchctx_current(&client->searchctx, tname); 323 if (result != ISC_R_SUCCESS) 324 return (result); 325 326 /* 327 * Render the new name to the buffer. 328 */ 329 result = dns_name_totext(tname, ISC_TRUE, &client->recv_buffer); 330 if (result != ISC_R_SUCCESS) 331 return (result); 332 333 /* 334 * Save this name away as the current real name. 335 */ 336 client->gabn.realname = (char *) b.base + b.used; 337 client->gabn.realnamelen = client->recv_buffer.used - b.used; 338 339 return (ISC_R_SUCCESS); 340} 341 342static void 343process_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; 347 348 ns_lwdclient_log(50, "find done for task %p, client %p", task, client); 349 350 evtype = ev->ev_type; 351 isc_event_free(&ev); 352 353 /* 354 * No more info to be had? If so, we have all the good stuff 355 * right now, so we can render things. 356 */ 357 claimed = ISC_FALSE; 358 if (evtype == DNS_EVENT_ADBNOMOREADDRESSES) { 359 if (NEED_V4(client)) { 360 client->v4find = client->find; 361 claimed = ISC_TRUE; 362 } 363 if (NEED_V6(client)) { 364 client->v6find = client->find; 365 claimed = ISC_TRUE; 366 } 367 if (client->find != NULL) { 368 if (claimed) 369 client->find = NULL; 370 else 371 dns_adb_destroyfind(&client->find); 372 373 } 374 generate_reply(client); 375 return; 376 } 377 378 /* 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 381 * it. 382 */ 383 if ((client->find != client->v4find) 384 && (client->find != client->v6find)) { 385 dns_adb_destroyfind(&client->find); 386 } else { 387 client->find = NULL; 388 } 389 390 /* 391 * We have some new information we can gather. Run off and fetch 392 * it. 393 */ 394 if (evtype == DNS_EVENT_ADBMOREADDRESSES) { 395 restart_find(client); 396 return; 397 } 398 399 /* 400 * An error or other strangeness happened. Drop this query. 401 */ 402 cleanup_gabn(client); 403 ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE); 404} 405 406static void 407restart_find(ns_lwdclient_t *client) { 408 unsigned int options; 409 isc_result_t result; 410 isc_boolean_t claimed; 411 412 ns_lwdclient_log(50, "starting find for client %p", client); 413 414 /* 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 417 * all. 418 */ 419 options = 0; 420 options |= DNS_ADBFIND_WANTEVENT; 421 options |= DNS_ADBFIND_RETURNLAME; 422 423 /* 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. 427 */ 428 if (NEED_V4(client)) 429 options |= DNS_ADBFIND_INET; 430 if (NEED_V6(client)) 431 options |= DNS_ADBFIND_INET6; 432 433 find_again: 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, 442 &client->find); 443 444 /* 445 * Did we get an alias? If so, save it and re-issue the query. 446 */ 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) { 453 ns_lwdclient_log(50, 454 "out of buffer space adding alias"); 455 ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE); 456 return; 457 } 458 goto find_again; 459 } 460 461 ns_lwdclient_log(50, "find returned %d (%s)", result, 462 isc_result_totext(result)); 463 464 /* 465 * Did we get an error? 466 */ 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); 472 return; 473 } 474 475 claimed = ISC_FALSE; 476 477 /* 478 * Did we get our answer to V4 addresses? 479 */ 480 if (NEED_V4(client) 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); 484 claimed = ISC_TRUE; 485 client->v4find = client->find; 486 } 487 488 /* 489 * Did we get our answer to V6 addresses? 490 */ 491 if (NEED_V6(client) 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); 495 claimed = ISC_TRUE; 496 client->v6find = client->find; 497 } 498 499 /* 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 503 * new target name. 504 * 505 * If we have both v4 and v6, and we are still getting an event, 506 * we have a programming error, so die hard. 507 */ 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); 511 return; 512 } 513 ns_lwdclient_log(50, "no event will be sent"); 514 if (claimed) 515 client->find = NULL; 516 else 517 dns_adb_destroyfind(&client->find); 518 519 /* 520 * We seem to have everything we asked for, or at least we are 521 * able to respond with things we've learned. 522 */ 523 524 generate_reply(client); 525} 526 527static isc_result_t 528start_find(ns_lwdclient_t *client) { 529 isc_result_t result; 530 531 /* 532 * Initialize the real name and alias arrays in the reply we're 533 * going to build up. 534 */ 535 init_gabn(client); 536 537 result = store_realname(client); 538 if (result != ISC_R_SUCCESS) 539 return (result); 540 restart_find(client); 541 return (ISC_R_SUCCESS); 542 543} 544 545static void 546init_gabn(ns_lwdclient_t *client) { 547 int i; 548 549 /* 550 * Initialize the real name and alias arrays in the reply we're 551 * going to build up. 552 */ 553 for (i = 0; i < LWRES_MAX_ALIASES; i++) { 554 client->aliases[i] = NULL; 555 client->aliaslen[i] = 0; 556 } 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); 562 } 563 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; 573 574 /* 575 * Set up the internal buffer to point to the receive region. 576 */ 577 isc_buffer_init(&client->recv_buffer, client->buffer, LWRES_RECVLENGTH); 578} 579 580/* 581 * When we are called, we can be assured that: 582 * 583 * client->sockaddr contains the address we need to reply to, 584 * 585 * client->pkt contains the packet header data, 586 * 587 * the packet "checks out" overall -- any MD5 hashes or crypto 588 * bits have been verified, 589 * 590 * "b" points to the remaining data after the packet header 591 * was parsed off. 592 * 593 * We are in a the RECVDONE state. 594 * 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. 598 */ 599void 600ns_lwdclient_processgabn(ns_lwdclient_t *client, lwres_buffer_t *b) { 601 isc_result_t result; 602 lwres_gabnrequest_t *req; 603 ns_lwdclientmgr_t *cm; 604 isc_buffer_t namebuf; 605 606 REQUIRE(NS_LWDCLIENT_ISRECVDONE(client)); 607 608 cm = client->clientmgr; 609 req = NULL; 610 611 result = lwres_gabnrequest_parse(client->clientmgr->lwctx, 612 b, &client->pkt, &req); 613 if (result != LWRES_R_SUCCESS) 614 goto out; 615 if (req->name == NULL) 616 goto out; 617 618 isc_buffer_init(&namebuf, req->name, req->namelen); 619 isc_buffer_add(&namebuf, req->namelen); 620 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) 626 goto out; 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); 632 633 client->find_wanted = req->addrtypes; 634 ns_lwdclient_log(50, "client %p looking for addrtypes %08x", 635 client, client->find_wanted); 636 637 /* 638 * We no longer need to keep this around. 639 */ 640 lwres_gabnrequest_free(client->clientmgr->lwctx, &req); 641 642 /* 643 * Start the find. 644 */ 645 result = start_find(client); 646 if (result != ISC_R_SUCCESS) 647 goto out; 648 649 return; 650 651 /* 652 * We're screwed. Return an error packet to our caller. 653 */ 654 out: 655 if (req != NULL) 656 lwres_gabnrequest_free(client->clientmgr->lwctx, &req); 657 658 ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE); 659} 660