1/* 2 * Copyright (C) 2004, 2005, 2007, 2013 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 2000, 2001, 2003 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: lookup.c,v 1.21 2007/06/18 23:47:40 tbox Exp $ */ 19 20/*! \file */ 21 22#include <config.h> 23 24#include <isc/mem.h> 25#include <isc/netaddr.h> 26#include <isc/string.h> /* Required for HP/UX (and others?) */ 27#include <isc/task.h> 28#include <isc/util.h> 29 30#include <dns/db.h> 31#include <dns/events.h> 32#include <dns/lookup.h> 33#include <dns/rdata.h> 34#include <dns/rdataset.h> 35#include <dns/rdatastruct.h> 36#include <dns/resolver.h> 37#include <dns/result.h> 38#include <dns/view.h> 39 40struct dns_lookup { 41 /* Unlocked. */ 42 unsigned int magic; 43 isc_mem_t * mctx; 44 isc_mutex_t lock; 45 dns_rdatatype_t type; 46 dns_fixedname_t name; 47 /* Locked by lock. */ 48 unsigned int options; 49 isc_task_t * task; 50 dns_view_t * view; 51 dns_lookupevent_t * event; 52 dns_fetch_t * fetch; 53 unsigned int restarts; 54 isc_boolean_t canceled; 55 dns_rdataset_t rdataset; 56 dns_rdataset_t sigrdataset; 57}; 58 59#define LOOKUP_MAGIC ISC_MAGIC('l', 'o', 'o', 'k') 60#define VALID_LOOKUP(l) ISC_MAGIC_VALID((l), LOOKUP_MAGIC) 61 62#define MAX_RESTARTS 16 63 64static void lookup_find(dns_lookup_t *lookup, dns_fetchevent_t *event); 65 66static void 67fetch_done(isc_task_t *task, isc_event_t *event) { 68 dns_lookup_t *lookup = event->ev_arg; 69 dns_fetchevent_t *fevent; 70 71 UNUSED(task); 72 REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE); 73 REQUIRE(VALID_LOOKUP(lookup)); 74 REQUIRE(lookup->task == task); 75 fevent = (dns_fetchevent_t *)event; 76 REQUIRE(fevent->fetch == lookup->fetch); 77 78 lookup_find(lookup, fevent); 79} 80 81static inline isc_result_t 82start_fetch(dns_lookup_t *lookup) { 83 isc_result_t result; 84 85 /* 86 * The caller must be holding the lookup's lock. 87 */ 88 89 REQUIRE(lookup->fetch == NULL); 90 91 result = dns_resolver_createfetch(lookup->view->resolver, 92 dns_fixedname_name(&lookup->name), 93 lookup->type, 94 NULL, NULL, NULL, 0, 95 lookup->task, fetch_done, lookup, 96 &lookup->rdataset, 97 &lookup->sigrdataset, 98 &lookup->fetch); 99 100 return (result); 101} 102 103static isc_result_t 104build_event(dns_lookup_t *lookup) { 105 dns_name_t *name = NULL; 106 dns_rdataset_t *rdataset = NULL; 107 dns_rdataset_t *sigrdataset = NULL; 108 isc_result_t result; 109 110 name = isc_mem_get(lookup->mctx, sizeof(dns_name_t)); 111 if (name == NULL) { 112 result = ISC_R_NOMEMORY; 113 goto fail; 114 } 115 dns_name_init(name, NULL); 116 result = dns_name_dup(dns_fixedname_name(&lookup->name), 117 lookup->mctx, name); 118 if (result != ISC_R_SUCCESS) 119 goto fail; 120 121 if (dns_rdataset_isassociated(&lookup->rdataset)) { 122 rdataset = isc_mem_get(lookup->mctx, sizeof(dns_rdataset_t)); 123 if (rdataset == NULL) { 124 result = ISC_R_NOMEMORY; 125 goto fail; 126 } 127 dns_rdataset_init(rdataset); 128 dns_rdataset_clone(&lookup->rdataset, rdataset); 129 } 130 131 if (dns_rdataset_isassociated(&lookup->sigrdataset)) { 132 sigrdataset = isc_mem_get(lookup->mctx, 133 sizeof(dns_rdataset_t)); 134 if (sigrdataset == NULL) { 135 result = ISC_R_NOMEMORY; 136 goto fail; 137 } 138 dns_rdataset_init(sigrdataset); 139 dns_rdataset_clone(&lookup->sigrdataset, sigrdataset); 140 } 141 142 lookup->event->name = name; 143 lookup->event->rdataset = rdataset; 144 lookup->event->sigrdataset = sigrdataset; 145 146 return (ISC_R_SUCCESS); 147 148 fail: 149 if (name != NULL) { 150 if (dns_name_dynamic(name)) 151 dns_name_free(name, lookup->mctx); 152 isc_mem_put(lookup->mctx, name, sizeof(dns_name_t)); 153 } 154 if (rdataset != NULL) { 155 if (dns_rdataset_isassociated(rdataset)) 156 dns_rdataset_disassociate(rdataset); 157 isc_mem_put(lookup->mctx, rdataset, sizeof(dns_rdataset_t)); 158 } 159 return (result); 160} 161 162static isc_result_t 163view_find(dns_lookup_t *lookup, dns_name_t *foundname) { 164 isc_result_t result; 165 dns_name_t *name = dns_fixedname_name(&lookup->name); 166 dns_rdatatype_t type; 167 168 if (lookup->type == dns_rdatatype_rrsig) 169 type = dns_rdatatype_any; 170 else 171 type = lookup->type; 172 173 result = dns_view_find(lookup->view, name, type, 0, 0, ISC_FALSE, 174 &lookup->event->db, &lookup->event->node, 175 foundname, &lookup->rdataset, 176 &lookup->sigrdataset); 177 return (result); 178} 179 180static void 181lookup_find(dns_lookup_t *lookup, dns_fetchevent_t *event) { 182 isc_result_t result; 183 isc_boolean_t want_restart; 184 isc_boolean_t send_event; 185 dns_name_t *name, *fname, *prefix; 186 dns_fixedname_t foundname, fixed; 187 dns_rdata_t rdata = DNS_RDATA_INIT; 188 unsigned int nlabels; 189 int order; 190 dns_namereln_t namereln; 191 dns_rdata_cname_t cname; 192 dns_rdata_dname_t dname; 193 194 REQUIRE(VALID_LOOKUP(lookup)); 195 196 LOCK(&lookup->lock); 197 198 result = ISC_R_SUCCESS; 199 name = dns_fixedname_name(&lookup->name); 200 201 do { 202 lookup->restarts++; 203 want_restart = ISC_FALSE; 204 send_event = ISC_TRUE; 205 206 if (event == NULL && !lookup->canceled) { 207 dns_fixedname_init(&foundname); 208 fname = dns_fixedname_name(&foundname); 209 INSIST(!dns_rdataset_isassociated(&lookup->rdataset)); 210 INSIST(!dns_rdataset_isassociated 211 (&lookup->sigrdataset)); 212 /* 213 * If we have restarted then clear the old node. */ 214 if (lookup->event->node != NULL) { 215 INSIST(lookup->event->db != NULL); 216 dns_db_detachnode(lookup->event->db, 217 &lookup->event->node); 218 } 219 if (lookup->event->db != NULL) 220 dns_db_detach(&lookup->event->db); 221 result = view_find(lookup, fname); 222 if (result == ISC_R_NOTFOUND) { 223 /* 224 * We don't know anything about the name. 225 * Launch a fetch. 226 */ 227 if (lookup->event->node != NULL) { 228 INSIST(lookup->event->db != NULL); 229 dns_db_detachnode(lookup->event->db, 230 &lookup->event->node); 231 } 232 if (lookup->event->db != NULL) 233 dns_db_detach(&lookup->event->db); 234 result = start_fetch(lookup); 235 if (result == ISC_R_SUCCESS) 236 send_event = ISC_FALSE; 237 goto done; 238 } 239 } else if (event != NULL) { 240 result = event->result; 241 fname = dns_fixedname_name(&event->foundname); 242 dns_resolver_destroyfetch(&lookup->fetch); 243 INSIST(event->rdataset == &lookup->rdataset); 244 INSIST(event->sigrdataset == &lookup->sigrdataset); 245 } else 246 fname = NULL; /* Silence compiler warning. */ 247 248 /* 249 * If we've been canceled, forget about the result. 250 */ 251 if (lookup->canceled) 252 result = ISC_R_CANCELED; 253 254 switch (result) { 255 case ISC_R_SUCCESS: 256 result = build_event(lookup); 257 if (event == NULL) 258 break; 259 if (event->db != NULL) 260 dns_db_attach(event->db, &lookup->event->db); 261 if (event->node != NULL) 262 dns_db_attachnode(lookup->event->db, 263 event->node, 264 &lookup->event->node); 265 break; 266 case DNS_R_CNAME: 267 /* 268 * Copy the CNAME's target into the lookup's 269 * query name and start over. 270 */ 271 result = dns_rdataset_first(&lookup->rdataset); 272 if (result != ISC_R_SUCCESS) 273 break; 274 dns_rdataset_current(&lookup->rdataset, &rdata); 275 result = dns_rdata_tostruct(&rdata, &cname, NULL); 276 dns_rdata_reset(&rdata); 277 if (result != ISC_R_SUCCESS) 278 break; 279 result = dns_name_copy(&cname.cname, name, NULL); 280 dns_rdata_freestruct(&cname); 281 if (result == ISC_R_SUCCESS) { 282 want_restart = ISC_TRUE; 283 send_event = ISC_FALSE; 284 } 285 break; 286 case DNS_R_DNAME: 287 namereln = dns_name_fullcompare(name, fname, &order, 288 &nlabels); 289 INSIST(namereln == dns_namereln_subdomain); 290 /* 291 * Get the target name of the DNAME. 292 */ 293 result = dns_rdataset_first(&lookup->rdataset); 294 if (result != ISC_R_SUCCESS) 295 break; 296 dns_rdataset_current(&lookup->rdataset, &rdata); 297 result = dns_rdata_tostruct(&rdata, &dname, NULL); 298 dns_rdata_reset(&rdata); 299 if (result != ISC_R_SUCCESS) 300 break; 301 /* 302 * Construct the new query name and start over. 303 */ 304 dns_fixedname_init(&fixed); 305 prefix = dns_fixedname_name(&fixed); 306 dns_name_split(name, nlabels, prefix, NULL); 307 result = dns_name_concatenate(prefix, &dname.dname, 308 name, NULL); 309 dns_rdata_freestruct(&dname); 310 if (result == ISC_R_SUCCESS) { 311 want_restart = ISC_TRUE; 312 send_event = ISC_FALSE; 313 } 314 break; 315 default: 316 send_event = ISC_TRUE; 317 } 318 319 if (dns_rdataset_isassociated(&lookup->rdataset)) 320 dns_rdataset_disassociate(&lookup->rdataset); 321 if (dns_rdataset_isassociated(&lookup->sigrdataset)) 322 dns_rdataset_disassociate(&lookup->sigrdataset); 323 324 done: 325 if (event != NULL) { 326 if (event->node != NULL) 327 dns_db_detachnode(event->db, &event->node); 328 if (event->db != NULL) 329 dns_db_detach(&event->db); 330 isc_event_free(ISC_EVENT_PTR(&event)); 331 } 332 333 /* 334 * Limit the number of restarts. 335 */ 336 if (want_restart && lookup->restarts == MAX_RESTARTS) { 337 want_restart = ISC_FALSE; 338 result = ISC_R_QUOTA; 339 send_event = ISC_TRUE; 340 } 341 342 } while (want_restart); 343 344 if (send_event) { 345 lookup->event->result = result; 346 lookup->event->ev_sender = lookup; 347 isc_task_sendanddetach(&lookup->task, 348 (isc_event_t **)&lookup->event); 349 dns_view_detach(&lookup->view); 350 } 351 352 UNLOCK(&lookup->lock); 353} 354 355static void 356levent_destroy(isc_event_t *event) { 357 dns_lookupevent_t *levent; 358 isc_mem_t *mctx; 359 360 REQUIRE(event->ev_type == DNS_EVENT_LOOKUPDONE); 361 mctx = event->ev_destroy_arg; 362 levent = (dns_lookupevent_t *)event; 363 364 if (levent->name != NULL) { 365 if (dns_name_dynamic(levent->name)) 366 dns_name_free(levent->name, mctx); 367 isc_mem_put(mctx, levent->name, sizeof(dns_name_t)); 368 } 369 if (levent->rdataset != NULL) { 370 dns_rdataset_disassociate(levent->rdataset); 371 isc_mem_put(mctx, levent->rdataset, sizeof(dns_rdataset_t)); 372 } 373 if (levent->sigrdataset != NULL) { 374 dns_rdataset_disassociate(levent->sigrdataset); 375 isc_mem_put(mctx, levent->sigrdataset, sizeof(dns_rdataset_t)); 376 } 377 if (levent->node != NULL) 378 dns_db_detachnode(levent->db, &levent->node); 379 if (levent->db != NULL) 380 dns_db_detach(&levent->db); 381 isc_mem_put(mctx, event, event->ev_size); 382} 383 384isc_result_t 385dns_lookup_create(isc_mem_t *mctx, dns_name_t *name, dns_rdatatype_t type, 386 dns_view_t *view, unsigned int options, isc_task_t *task, 387 isc_taskaction_t action, void *arg, dns_lookup_t **lookupp) 388{ 389 isc_result_t result; 390 dns_lookup_t *lookup; 391 isc_event_t *ievent; 392 393 lookup = isc_mem_get(mctx, sizeof(*lookup)); 394 if (lookup == NULL) 395 return (ISC_R_NOMEMORY); 396 lookup->mctx = NULL; 397 isc_mem_attach(mctx, &lookup->mctx); 398 lookup->options = options; 399 400 ievent = isc_event_allocate(mctx, lookup, DNS_EVENT_LOOKUPDONE, 401 action, arg, sizeof(*lookup->event)); 402 if (ievent == NULL) { 403 result = ISC_R_NOMEMORY; 404 goto cleanup_lookup; 405 } 406 lookup->event = (dns_lookupevent_t *)ievent; 407 lookup->event->ev_destroy = levent_destroy; 408 lookup->event->ev_destroy_arg = mctx; 409 lookup->event->result = ISC_R_FAILURE; 410 lookup->event->name = NULL; 411 lookup->event->rdataset = NULL; 412 lookup->event->sigrdataset = NULL; 413 lookup->event->db = NULL; 414 lookup->event->node = NULL; 415 416 lookup->task = NULL; 417 isc_task_attach(task, &lookup->task); 418 419 result = isc_mutex_init(&lookup->lock); 420 if (result != ISC_R_SUCCESS) 421 goto cleanup_event; 422 423 dns_fixedname_init(&lookup->name); 424 425 result = dns_name_copy(name, dns_fixedname_name(&lookup->name), NULL); 426 if (result != ISC_R_SUCCESS) 427 goto cleanup_lock; 428 429 lookup->type = type; 430 lookup->view = NULL; 431 dns_view_attach(view, &lookup->view); 432 lookup->fetch = NULL; 433 lookup->restarts = 0; 434 lookup->canceled = ISC_FALSE; 435 dns_rdataset_init(&lookup->rdataset); 436 dns_rdataset_init(&lookup->sigrdataset); 437 lookup->magic = LOOKUP_MAGIC; 438 439 *lookupp = lookup; 440 441 lookup_find(lookup, NULL); 442 443 return (ISC_R_SUCCESS); 444 445 cleanup_lock: 446 DESTROYLOCK(&lookup->lock); 447 448 cleanup_event: 449 ievent = (isc_event_t *)lookup->event; 450 isc_event_free(&ievent); 451 lookup->event = NULL; 452 453 isc_task_detach(&lookup->task); 454 455 cleanup_lookup: 456 isc_mem_putanddetach(&mctx, lookup, sizeof(*lookup)); 457 458 return (result); 459} 460 461void 462dns_lookup_cancel(dns_lookup_t *lookup) { 463 REQUIRE(VALID_LOOKUP(lookup)); 464 465 LOCK(&lookup->lock); 466 467 if (!lookup->canceled) { 468 lookup->canceled = ISC_TRUE; 469 if (lookup->fetch != NULL) { 470 INSIST(lookup->view != NULL); 471 dns_resolver_cancelfetch(lookup->fetch); 472 } 473 } 474 475 UNLOCK(&lookup->lock); 476} 477 478void 479dns_lookup_destroy(dns_lookup_t **lookupp) { 480 dns_lookup_t *lookup; 481 482 REQUIRE(lookupp != NULL); 483 lookup = *lookupp; 484 REQUIRE(VALID_LOOKUP(lookup)); 485 REQUIRE(lookup->event == NULL); 486 REQUIRE(lookup->task == NULL); 487 REQUIRE(lookup->view == NULL); 488 if (dns_rdataset_isassociated(&lookup->rdataset)) 489 dns_rdataset_disassociate(&lookup->rdataset); 490 if (dns_rdataset_isassociated(&lookup->sigrdataset)) 491 dns_rdataset_disassociate(&lookup->sigrdataset); 492 493 DESTROYLOCK(&lookup->lock); 494 lookup->magic = 0; 495 isc_mem_putanddetach(&lookup->mctx, lookup, sizeof(*lookup)); 496 497 *lookupp = NULL; 498} 499