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