lookup.c revision 170222
1/* 2 * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 2000, 2001, 2003 Internet Software Consortium. 4 * 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. 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.14.18.4 2005/11/30 03:44:39 marka 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 = ISC_FALSE; 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 205 if (event == NULL && !lookup->canceled) { 206 dns_fixedname_init(&foundname); 207 fname = dns_fixedname_name(&foundname); 208 INSIST(!dns_rdataset_isassociated(&lookup->rdataset)); 209 INSIST(!dns_rdataset_isassociated 210 (&lookup->sigrdataset)); 211 result = view_find(lookup, fname); 212 if (result == ISC_R_NOTFOUND) { 213 /* 214 * We don't know anything about the name. 215 * Launch a fetch. 216 */ 217 if (lookup->event->node != NULL) { 218 INSIST(lookup->event->db != NULL); 219 dns_db_detachnode(lookup->event->db, 220 &lookup->event->node); 221 } 222 if (lookup->event->db != NULL) 223 dns_db_detach(&lookup->event->db); 224 result = start_fetch(lookup); 225 if (result != ISC_R_SUCCESS) 226 send_event = ISC_TRUE; 227 goto done; 228 } 229 } else if (event != NULL) { 230 result = event->result; 231 fname = dns_fixedname_name(&event->foundname); 232 dns_resolver_destroyfetch(&lookup->fetch); 233 INSIST(event->rdataset == &lookup->rdataset); 234 INSIST(event->sigrdataset == &lookup->sigrdataset); 235 } else 236 fname = NULL; /* Silence compiler warning. */ 237 238 /* 239 * If we've been canceled, forget about the result. 240 */ 241 if (lookup->canceled) 242 result = ISC_R_CANCELED; 243 244 switch (result) { 245 case ISC_R_SUCCESS: 246 result = build_event(lookup); 247 send_event = ISC_TRUE; 248 if (event == NULL) 249 break; 250 if (event->db != NULL) 251 dns_db_attach(event->db, &lookup->event->db); 252 if (event->node != NULL) 253 dns_db_attachnode(lookup->event->db, 254 event->node, 255 &lookup->event->node); 256 break; 257 case DNS_R_CNAME: 258 /* 259 * Copy the CNAME's target into the lookup's 260 * query name and start over. 261 */ 262 result = dns_rdataset_first(&lookup->rdataset); 263 if (result != ISC_R_SUCCESS) 264 break; 265 dns_rdataset_current(&lookup->rdataset, &rdata); 266 result = dns_rdata_tostruct(&rdata, &cname, NULL); 267 dns_rdata_reset(&rdata); 268 if (result != ISC_R_SUCCESS) 269 break; 270 result = dns_name_copy(&cname.cname, name, NULL); 271 dns_rdata_freestruct(&cname); 272 if (result == ISC_R_SUCCESS) 273 want_restart = ISC_TRUE; 274 break; 275 case DNS_R_DNAME: 276 namereln = dns_name_fullcompare(name, fname, &order, 277 &nlabels); 278 INSIST(namereln == dns_namereln_subdomain); 279 /* 280 * Get the target name of the DNAME. 281 */ 282 result = dns_rdataset_first(&lookup->rdataset); 283 if (result != ISC_R_SUCCESS) 284 break; 285 dns_rdataset_current(&lookup->rdataset, &rdata); 286 result = dns_rdata_tostruct(&rdata, &dname, NULL); 287 dns_rdata_reset(&rdata); 288 if (result != ISC_R_SUCCESS) 289 break; 290 /* 291 * Construct the new query name and start over. 292 */ 293 dns_fixedname_init(&fixed); 294 prefix = dns_fixedname_name(&fixed); 295 dns_name_split(name, nlabels, prefix, NULL); 296 result = dns_name_concatenate(prefix, &dname.dname, 297 name, NULL); 298 dns_rdata_freestruct(&dname); 299 if (result == ISC_R_SUCCESS) 300 want_restart = ISC_TRUE; 301 break; 302 default: 303 send_event = ISC_TRUE; 304 } 305 306 if (dns_rdataset_isassociated(&lookup->rdataset)) 307 dns_rdataset_disassociate(&lookup->rdataset); 308 if (dns_rdataset_isassociated(&lookup->sigrdataset)) 309 dns_rdataset_disassociate(&lookup->sigrdataset); 310 311 done: 312 if (event != NULL) { 313 if (event->node != NULL) 314 dns_db_detachnode(event->db, &event->node); 315 if (event->db != NULL) 316 dns_db_detach(&event->db); 317 isc_event_free(ISC_EVENT_PTR(&event)); 318 } 319 320 /* 321 * Limit the number of restarts. 322 */ 323 if (want_restart && lookup->restarts == MAX_RESTARTS) { 324 want_restart = ISC_FALSE; 325 result = ISC_R_QUOTA; 326 send_event = ISC_TRUE; 327 } 328 329 } while (want_restart); 330 331 if (send_event) { 332 lookup->event->result = result; 333 lookup->event->ev_sender = lookup; 334 isc_task_sendanddetach(&lookup->task, 335 (isc_event_t **)&lookup->event); 336 dns_view_detach(&lookup->view); 337 } 338 339 UNLOCK(&lookup->lock); 340} 341 342static void 343levent_destroy(isc_event_t *event) { 344 dns_lookupevent_t *levent; 345 isc_mem_t *mctx; 346 347 REQUIRE(event->ev_type == DNS_EVENT_LOOKUPDONE); 348 mctx = event->ev_destroy_arg; 349 levent = (dns_lookupevent_t *)event; 350 351 if (levent->name != NULL) { 352 if (dns_name_dynamic(levent->name)) 353 dns_name_free(levent->name, mctx); 354 isc_mem_put(mctx, levent->name, sizeof(dns_name_t)); 355 } 356 if (levent->rdataset != NULL) { 357 dns_rdataset_disassociate(levent->rdataset); 358 isc_mem_put(mctx, levent->rdataset, sizeof(dns_rdataset_t)); 359 } 360 if (levent->sigrdataset != NULL) { 361 dns_rdataset_disassociate(levent->sigrdataset); 362 isc_mem_put(mctx, levent->sigrdataset, sizeof(dns_rdataset_t)); 363 } 364 if (levent->node != NULL) 365 dns_db_detachnode(levent->db, &levent->node); 366 if (levent->db != NULL) 367 dns_db_detach(&levent->db); 368 isc_mem_put(mctx, event, event->ev_size); 369} 370 371 372isc_result_t 373dns_lookup_create(isc_mem_t *mctx, dns_name_t *name, dns_rdatatype_t type, 374 dns_view_t *view, unsigned int options, isc_task_t *task, 375 isc_taskaction_t action, void *arg, dns_lookup_t **lookupp) 376{ 377 isc_result_t result; 378 dns_lookup_t *lookup; 379 isc_event_t *ievent; 380 381 lookup = isc_mem_get(mctx, sizeof(*lookup)); 382 if (lookup == NULL) 383 return (ISC_R_NOMEMORY); 384 lookup->mctx = mctx; 385 lookup->options = options; 386 387 ievent = isc_event_allocate(mctx, lookup, DNS_EVENT_LOOKUPDONE, 388 action, arg, sizeof(*lookup->event)); 389 if (ievent == NULL) { 390 result = ISC_R_NOMEMORY; 391 goto cleanup_lookup; 392 } 393 lookup->event = (dns_lookupevent_t *)ievent; 394 lookup->event->ev_destroy = levent_destroy; 395 lookup->event->ev_destroy_arg = mctx; 396 lookup->event->result = ISC_R_FAILURE; 397 lookup->event->name = NULL; 398 lookup->event->rdataset = NULL; 399 lookup->event->sigrdataset = NULL; 400 lookup->event->db = NULL; 401 lookup->event->node = NULL; 402 403 lookup->task = NULL; 404 isc_task_attach(task, &lookup->task); 405 406 result = isc_mutex_init(&lookup->lock); 407 if (result != ISC_R_SUCCESS) 408 goto cleanup_event; 409 410 dns_fixedname_init(&lookup->name); 411 412 result = dns_name_copy(name, dns_fixedname_name(&lookup->name), NULL); 413 if (result != ISC_R_SUCCESS) 414 goto cleanup_lock; 415 416 lookup->type = type; 417 lookup->view = NULL; 418 dns_view_attach(view, &lookup->view); 419 lookup->fetch = NULL; 420 lookup->restarts = 0; 421 lookup->canceled = ISC_FALSE; 422 dns_rdataset_init(&lookup->rdataset); 423 dns_rdataset_init(&lookup->sigrdataset); 424 lookup->magic = LOOKUP_MAGIC; 425 426 *lookupp = lookup; 427 428 lookup_find(lookup, NULL); 429 430 return (ISC_R_SUCCESS); 431 432 cleanup_lock: 433 DESTROYLOCK(&lookup->lock); 434 435 cleanup_event: 436 ievent = (isc_event_t *)lookup->event; 437 isc_event_free(&ievent); 438 lookup->event = NULL; 439 440 isc_task_detach(&lookup->task); 441 442 cleanup_lookup: 443 isc_mem_put(mctx, lookup, sizeof(*lookup)); 444 445 return (result); 446} 447 448void 449dns_lookup_cancel(dns_lookup_t *lookup) { 450 REQUIRE(VALID_LOOKUP(lookup)); 451 452 LOCK(&lookup->lock); 453 454 if (!lookup->canceled) { 455 lookup->canceled = ISC_TRUE; 456 if (lookup->fetch != NULL) { 457 INSIST(lookup->view != NULL); 458 dns_resolver_cancelfetch(lookup->fetch); 459 } 460 } 461 462 UNLOCK(&lookup->lock); 463} 464 465void 466dns_lookup_destroy(dns_lookup_t **lookupp) { 467 dns_lookup_t *lookup; 468 469 REQUIRE(lookupp != NULL); 470 lookup = *lookupp; 471 REQUIRE(VALID_LOOKUP(lookup)); 472 REQUIRE(lookup->event == NULL); 473 REQUIRE(lookup->task == NULL); 474 REQUIRE(lookup->view == NULL); 475 if (dns_rdataset_isassociated(&lookup->rdataset)) 476 dns_rdataset_disassociate(&lookup->rdataset); 477 if (dns_rdataset_isassociated(&lookup->sigrdataset)) 478 dns_rdataset_disassociate(&lookup->sigrdataset); 479 480 DESTROYLOCK(&lookup->lock); 481 lookup->magic = 0; 482 isc_mem_put(lookup->mctx, lookup, sizeof(*lookup)); 483 484 *lookupp = NULL; 485} 486