1/* 2 * Copyright (C) 2004-2007, 2009 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.24 2009/09/02 23:48:01 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 48static isc_result_t start_find(ns_lwdclient_t *); 49static void restart_find(ns_lwdclient_t *); 50static 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 */ 56static void 57cleanup_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 70static void 71setup_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 107typedef struct { 108 isc_netaddr_t address; 109 int rank; 110} rankedaddress; 111 112static int 113addr_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 119static void 120sort_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 163static void 164generate_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 */ 276static isc_result_t 277add_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 311static isc_result_t 312store_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 340static void 341process_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 404static void 405restart_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 525static isc_result_t 526start_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 543static void 544init_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 */ 597void 598ns_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, 0, 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} 658