1/* $NetBSD: lookup.c,v 1.1 2024/02/18 20:57:32 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/string.h> /* Required for HP/UX (and others?) */ 23#include <isc/task.h> 24#include <isc/util.h> 25 26#include <dns/db.h> 27#include <dns/events.h> 28#include <dns/lookup.h> 29#include <dns/rdata.h> 30#include <dns/rdataset.h> 31#include <dns/rdatastruct.h> 32#include <dns/resolver.h> 33#include <dns/result.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 isc_result_t 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 return (ISC_R_SUCCESS); 124} 125 126static isc_result_t 127view_find(dns_lookup_t *lookup, dns_name_t *foundname) { 128 isc_result_t result; 129 dns_name_t *name = dns_fixedname_name(&lookup->name); 130 dns_rdatatype_t type; 131 132 if (lookup->type == dns_rdatatype_rrsig) { 133 type = dns_rdatatype_any; 134 } else { 135 type = lookup->type; 136 } 137 138 result = dns_view_find(lookup->view, name, type, 0, 0, false, false, 139 &lookup->event->db, &lookup->event->node, 140 foundname, &lookup->rdataset, 141 &lookup->sigrdataset); 142 return (result); 143} 144 145static void 146lookup_find(dns_lookup_t *lookup, dns_fetchevent_t *event) { 147 isc_result_t result; 148 bool want_restart; 149 bool send_event; 150 dns_name_t *name, *fname, *prefix; 151 dns_fixedname_t foundname, fixed; 152 dns_rdata_t rdata = DNS_RDATA_INIT; 153 unsigned int nlabels; 154 int order; 155 dns_namereln_t namereln; 156 dns_rdata_cname_t cname; 157 dns_rdata_dname_t dname; 158 159 REQUIRE(VALID_LOOKUP(lookup)); 160 161 LOCK(&lookup->lock); 162 163 result = ISC_R_SUCCESS; 164 name = dns_fixedname_name(&lookup->name); 165 166 do { 167 lookup->restarts++; 168 want_restart = false; 169 send_event = true; 170 171 if (event == NULL && !lookup->canceled) { 172 fname = dns_fixedname_initname(&foundname); 173 INSIST(!dns_rdataset_isassociated(&lookup->rdataset)); 174 INSIST(!dns_rdataset_isassociated( 175 &lookup->sigrdataset)); 176 /* 177 * If we have restarted then clear the old node. 178 */ 179 if (lookup->event->node != NULL) { 180 INSIST(lookup->event->db != NULL); 181 dns_db_detachnode(lookup->event->db, 182 &lookup->event->node); 183 } 184 if (lookup->event->db != NULL) { 185 dns_db_detach(&lookup->event->db); 186 } 187 result = view_find(lookup, fname); 188 if (result == ISC_R_NOTFOUND) { 189 /* 190 * We don't know anything about the name. 191 * Launch a fetch. 192 */ 193 if (lookup->event->node != NULL) { 194 INSIST(lookup->event->db != NULL); 195 dns_db_detachnode(lookup->event->db, 196 &lookup->event->node); 197 } 198 if (lookup->event->db != NULL) { 199 dns_db_detach(&lookup->event->db); 200 } 201 result = start_fetch(lookup); 202 if (result == ISC_R_SUCCESS) { 203 send_event = false; 204 } 205 goto done; 206 } 207 } else if (event != NULL) { 208 result = event->result; 209 fname = dns_fixedname_name(&event->foundname); 210 dns_resolver_destroyfetch(&lookup->fetch); 211 INSIST(event->rdataset == &lookup->rdataset); 212 INSIST(event->sigrdataset == &lookup->sigrdataset); 213 } else { 214 fname = NULL; /* Silence compiler warning. */ 215 } 216 217 /* 218 * If we've been canceled, forget about the result. 219 */ 220 if (lookup->canceled) { 221 result = ISC_R_CANCELED; 222 } 223 224 switch (result) { 225 case ISC_R_SUCCESS: 226 result = build_event(lookup); 227 if (event == NULL) { 228 break; 229 } 230 if (event->db != NULL) { 231 dns_db_attach(event->db, &lookup->event->db); 232 } 233 if (event->node != NULL) { 234 dns_db_attachnode(lookup->event->db, 235 event->node, 236 &lookup->event->node); 237 } 238 break; 239 case DNS_R_CNAME: 240 /* 241 * Copy the CNAME's target into the lookup's 242 * query name and start over. 243 */ 244 result = dns_rdataset_first(&lookup->rdataset); 245 if (result != ISC_R_SUCCESS) { 246 break; 247 } 248 dns_rdataset_current(&lookup->rdataset, &rdata); 249 result = dns_rdata_tostruct(&rdata, &cname, NULL); 250 dns_rdata_reset(&rdata); 251 if (result != ISC_R_SUCCESS) { 252 break; 253 } 254 dns_name_copynf(&cname.cname, name); 255 dns_rdata_freestruct(&cname); 256 want_restart = true; 257 send_event = false; 258 break; 259 case DNS_R_DNAME: 260 namereln = dns_name_fullcompare(name, fname, &order, 261 &nlabels); 262 INSIST(namereln == dns_namereln_subdomain); 263 /* 264 * Get the target name of the DNAME. 265 */ 266 result = dns_rdataset_first(&lookup->rdataset); 267 if (result != ISC_R_SUCCESS) { 268 break; 269 } 270 dns_rdataset_current(&lookup->rdataset, &rdata); 271 result = dns_rdata_tostruct(&rdata, &dname, NULL); 272 dns_rdata_reset(&rdata); 273 if (result != ISC_R_SUCCESS) { 274 break; 275 } 276 /* 277 * Construct the new query name and start over. 278 */ 279 prefix = dns_fixedname_initname(&fixed); 280 dns_name_split(name, nlabels, prefix, NULL); 281 result = dns_name_concatenate(prefix, &dname.dname, 282 name, NULL); 283 dns_rdata_freestruct(&dname); 284 if (result == ISC_R_SUCCESS) { 285 want_restart = true; 286 send_event = false; 287 } 288 break; 289 default: 290 send_event = true; 291 } 292 293 if (dns_rdataset_isassociated(&lookup->rdataset)) { 294 dns_rdataset_disassociate(&lookup->rdataset); 295 } 296 if (dns_rdataset_isassociated(&lookup->sigrdataset)) { 297 dns_rdataset_disassociate(&lookup->sigrdataset); 298 } 299 300 done: 301 if (event != NULL) { 302 if (event->node != NULL) { 303 dns_db_detachnode(event->db, &event->node); 304 } 305 if (event->db != NULL) { 306 dns_db_detach(&event->db); 307 } 308 isc_event_free(ISC_EVENT_PTR(&event)); 309 } 310 311 /* 312 * Limit the number of restarts. 313 */ 314 if (want_restart && lookup->restarts == MAX_RESTARTS) { 315 want_restart = false; 316 result = ISC_R_QUOTA; 317 send_event = true; 318 } 319 } while (want_restart); 320 321 if (send_event) { 322 lookup->event->result = result; 323 lookup->event->ev_sender = lookup; 324 isc_task_sendanddetach(&lookup->task, 325 (isc_event_t **)(void *)&lookup->event); 326 dns_view_detach(&lookup->view); 327 } 328 329 UNLOCK(&lookup->lock); 330} 331 332static void 333levent_destroy(isc_event_t *event) { 334 dns_lookupevent_t *levent; 335 isc_mem_t *mctx; 336 337 REQUIRE(event->ev_type == DNS_EVENT_LOOKUPDONE); 338 mctx = event->ev_destroy_arg; 339 levent = (dns_lookupevent_t *)event; 340 341 if (levent->name != NULL) { 342 if (dns_name_dynamic(levent->name)) { 343 dns_name_free(levent->name, mctx); 344 } 345 isc_mem_put(mctx, levent->name, sizeof(dns_name_t)); 346 } 347 if (levent->rdataset != NULL) { 348 dns_rdataset_disassociate(levent->rdataset); 349 isc_mem_put(mctx, levent->rdataset, sizeof(dns_rdataset_t)); 350 } 351 if (levent->sigrdataset != NULL) { 352 dns_rdataset_disassociate(levent->sigrdataset); 353 isc_mem_put(mctx, levent->sigrdataset, sizeof(dns_rdataset_t)); 354 } 355 if (levent->node != NULL) { 356 dns_db_detachnode(levent->db, &levent->node); 357 } 358 if (levent->db != NULL) { 359 dns_db_detach(&levent->db); 360 } 361 isc_mem_put(mctx, event, event->ev_size); 362} 363 364isc_result_t 365dns_lookup_create(isc_mem_t *mctx, const dns_name_t *name, dns_rdatatype_t type, 366 dns_view_t *view, unsigned int options, isc_task_t *task, 367 isc_taskaction_t action, void *arg, dns_lookup_t **lookupp) { 368 dns_lookup_t *lookup; 369 isc_event_t *ievent; 370 371 lookup = isc_mem_get(mctx, sizeof(*lookup)); 372 lookup->mctx = NULL; 373 isc_mem_attach(mctx, &lookup->mctx); 374 lookup->options = options; 375 376 ievent = isc_event_allocate(mctx, lookup, DNS_EVENT_LOOKUPDONE, action, 377 arg, sizeof(*lookup->event)); 378 lookup->event = (dns_lookupevent_t *)ievent; 379 lookup->event->ev_destroy = levent_destroy; 380 lookup->event->ev_destroy_arg = mctx; 381 lookup->event->result = ISC_R_FAILURE; 382 lookup->event->name = NULL; 383 lookup->event->rdataset = NULL; 384 lookup->event->sigrdataset = NULL; 385 lookup->event->db = NULL; 386 lookup->event->node = NULL; 387 388 lookup->task = NULL; 389 isc_task_attach(task, &lookup->task); 390 391 isc_mutex_init(&lookup->lock); 392 393 dns_fixedname_init(&lookup->name); 394 395 dns_name_copynf(name, dns_fixedname_name(&lookup->name)); 396 397 lookup->type = type; 398 lookup->view = NULL; 399 dns_view_attach(view, &lookup->view); 400 lookup->fetch = NULL; 401 lookup->restarts = 0; 402 lookup->canceled = false; 403 dns_rdataset_init(&lookup->rdataset); 404 dns_rdataset_init(&lookup->sigrdataset); 405 lookup->magic = LOOKUP_MAGIC; 406 407 *lookupp = lookup; 408 409 lookup_find(lookup, NULL); 410 411 return (ISC_R_SUCCESS); 412} 413 414void 415dns_lookup_cancel(dns_lookup_t *lookup) { 416 REQUIRE(VALID_LOOKUP(lookup)); 417 418 LOCK(&lookup->lock); 419 420 if (!lookup->canceled) { 421 lookup->canceled = true; 422 if (lookup->fetch != NULL) { 423 INSIST(lookup->view != NULL); 424 dns_resolver_cancelfetch(lookup->fetch); 425 } 426 } 427 428 UNLOCK(&lookup->lock); 429} 430 431void 432dns_lookup_destroy(dns_lookup_t **lookupp) { 433 dns_lookup_t *lookup; 434 435 REQUIRE(lookupp != NULL); 436 lookup = *lookupp; 437 *lookupp = NULL; 438 REQUIRE(VALID_LOOKUP(lookup)); 439 REQUIRE(lookup->event == NULL); 440 REQUIRE(lookup->task == NULL); 441 REQUIRE(lookup->view == NULL); 442 if (dns_rdataset_isassociated(&lookup->rdataset)) { 443 dns_rdataset_disassociate(&lookup->rdataset); 444 } 445 if (dns_rdataset_isassociated(&lookup->sigrdataset)) { 446 dns_rdataset_disassociate(&lookup->sigrdataset); 447 } 448 449 isc_mutex_destroy(&lookup->lock); 450 lookup->magic = 0; 451 isc_mem_putanddetach(&lookup->mctx, lookup, sizeof(*lookup)); 452} 453