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