1135446Strhodes/* 2262706Serwin * Copyright (C) 2004-2014 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 1999-2003 Internet Software Consortium. 4135446Strhodes * 5174187Sdougb * Permission to use, copy, modify, and/or distribute this software for any 6135446Strhodes * purpose with or without fee is hereby granted, provided that the above 7135446Strhodes * copyright notice and this permission notice appear in all copies. 8135446Strhodes * 9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11135446Strhodes * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15135446Strhodes * PERFORMANCE OF THIS SOFTWARE. 16135446Strhodes */ 17135446Strhodes 18234010Sdougb/* $Id$ */ 19135446Strhodes 20170222Sdougb/*! \file */ 21170222Sdougb 22135446Strhodes#include <config.h> 23135446Strhodes 24254402Serwin#include <isc/log.h> 25193149Sdougb#include <isc/platform.h> 26135446Strhodes#include <isc/print.h> 27135446Strhodes#include <isc/string.h> 28193149Sdougb#include <isc/random.h> 29254897Serwin#include <isc/socket.h> 30254897Serwin#include <isc/stats.h> 31135446Strhodes#include <isc/task.h> 32135446Strhodes#include <isc/timer.h> 33135446Strhodes#include <isc/util.h> 34135446Strhodes 35135446Strhodes#include <dns/acl.h> 36135446Strhodes#include <dns/adb.h> 37165071Sdougb#include <dns/cache.h> 38135446Strhodes#include <dns/db.h> 39135446Strhodes#include <dns/dispatch.h> 40165071Sdougb#include <dns/ds.h> 41135446Strhodes#include <dns/events.h> 42135446Strhodes#include <dns/forward.h> 43135446Strhodes#include <dns/keytable.h> 44135446Strhodes#include <dns/log.h> 45135446Strhodes#include <dns/message.h> 46135446Strhodes#include <dns/ncache.h> 47254402Serwin#include <dns/nsec.h> 48254402Serwin#include <dns/nsec3.h> 49135446Strhodes#include <dns/opcode.h> 50135446Strhodes#include <dns/peer.h> 51135446Strhodes#include <dns/rbt.h> 52135446Strhodes#include <dns/rcode.h> 53135446Strhodes#include <dns/rdata.h> 54135446Strhodes#include <dns/rdataclass.h> 55135446Strhodes#include <dns/rdatalist.h> 56135446Strhodes#include <dns/rdataset.h> 57135446Strhodes#include <dns/rdatastruct.h> 58135446Strhodes#include <dns/rdatatype.h> 59135446Strhodes#include <dns/resolver.h> 60135446Strhodes#include <dns/result.h> 61165071Sdougb#include <dns/rootns.h> 62193149Sdougb#include <dns/stats.h> 63135446Strhodes#include <dns/tsig.h> 64135446Strhodes#include <dns/validator.h> 65135446Strhodes 66135446Strhodes#define DNS_RESOLVER_TRACE 67135446Strhodes#ifdef DNS_RESOLVER_TRACE 68193149Sdougb#define RTRACE(m) isc_log_write(dns_lctx, \ 69135446Strhodes DNS_LOGCATEGORY_RESOLVER, \ 70135446Strhodes DNS_LOGMODULE_RESOLVER, \ 71135446Strhodes ISC_LOG_DEBUG(3), \ 72135446Strhodes "res %p: %s", res, (m)) 73193149Sdougb#define RRTRACE(r, m) isc_log_write(dns_lctx, \ 74135446Strhodes DNS_LOGCATEGORY_RESOLVER, \ 75135446Strhodes DNS_LOGMODULE_RESOLVER, \ 76135446Strhodes ISC_LOG_DEBUG(3), \ 77135446Strhodes "res %p: %s", (r), (m)) 78193149Sdougb#define FCTXTRACE(m) isc_log_write(dns_lctx, \ 79135446Strhodes DNS_LOGCATEGORY_RESOLVER, \ 80135446Strhodes DNS_LOGMODULE_RESOLVER, \ 81135446Strhodes ISC_LOG_DEBUG(3), \ 82254402Serwin "fctx %p(%s): %s", fctx, fctx->info, (m)) 83135446Strhodes#define FCTXTRACE2(m1, m2) \ 84135446Strhodes isc_log_write(dns_lctx, \ 85135446Strhodes DNS_LOGCATEGORY_RESOLVER, \ 86135446Strhodes DNS_LOGMODULE_RESOLVER, \ 87135446Strhodes ISC_LOG_DEBUG(3), \ 88135446Strhodes "fctx %p(%s): %s %s", \ 89135446Strhodes fctx, fctx->info, (m1), (m2)) 90193149Sdougb#define FTRACE(m) isc_log_write(dns_lctx, \ 91135446Strhodes DNS_LOGCATEGORY_RESOLVER, \ 92135446Strhodes DNS_LOGMODULE_RESOLVER, \ 93135446Strhodes ISC_LOG_DEBUG(3), \ 94135446Strhodes "fetch %p (fctx %p(%s)): %s", \ 95135446Strhodes fetch, fetch->private, \ 96135446Strhodes fetch->private->info, (m)) 97193149Sdougb#define QTRACE(m) isc_log_write(dns_lctx, \ 98135446Strhodes DNS_LOGCATEGORY_RESOLVER, \ 99135446Strhodes DNS_LOGMODULE_RESOLVER, \ 100135446Strhodes ISC_LOG_DEBUG(3), \ 101135446Strhodes "resquery %p (fctx %p(%s)): %s", \ 102135446Strhodes query, query->fctx, \ 103135446Strhodes query->fctx->info, (m)) 104135446Strhodes#else 105135446Strhodes#define RTRACE(m) 106135446Strhodes#define RRTRACE(r, m) 107135446Strhodes#define FCTXTRACE(m) 108262706Serwin#define FCTXTRACE2(m1, m2) 109135446Strhodes#define FTRACE(m) 110135446Strhodes#define QTRACE(m) 111135446Strhodes#endif 112135446Strhodes 113245163Serwin#define US_PER_SEC 1000000U 114245163Serwin/* 115245163Serwin * The maximum time we will wait for a single query. 116245163Serwin */ 117245163Serwin#define MAX_SINGLE_QUERY_TIMEOUT 9U 118245163Serwin#define MAX_SINGLE_QUERY_TIMEOUT_US (MAX_SINGLE_QUERY_TIMEOUT*US_PER_SEC) 119245163Serwin 120245163Serwin/* 121245163Serwin * We need to allow a individual query time to complete / timeout. 122245163Serwin */ 123245163Serwin#define MINIMUM_QUERY_TIMEOUT (MAX_SINGLE_QUERY_TIMEOUT + 1U) 124245163Serwin 125245163Serwin/* The default time in seconds for the whole query to live. */ 126224092Sdougb#ifndef DEFAULT_QUERY_TIMEOUT 127245163Serwin#define DEFAULT_QUERY_TIMEOUT MINIMUM_QUERY_TIMEOUT 128224092Sdougb#endif 129224092Sdougb 130224092Sdougb#ifndef MAXIMUM_QUERY_TIMEOUT 131224092Sdougb#define MAXIMUM_QUERY_TIMEOUT 30 /* The maximum time in seconds for the whole query to live. */ 132224092Sdougb#endif 133224092Sdougb 134275672Sdelphij/* The default maximum number of recursions to follow before giving up. */ 135275672Sdelphij#ifndef DEFAULT_RECURSION_DEPTH 136275672Sdelphij#define DEFAULT_RECURSION_DEPTH 7 137275672Sdelphij#endif 138275672Sdelphij 139275672Sdelphij/* The default maximum number of iterative queries to allow before giving up. */ 140275672Sdelphij#ifndef DEFAULT_MAX_QUERIES 141275672Sdelphij#define DEFAULT_MAX_QUERIES 50 142275672Sdelphij#endif 143275672Sdelphij 144170222Sdougb/*% 145135446Strhodes * Maximum EDNS0 input packet size. 146135446Strhodes */ 147193149Sdougb#define RECV_BUFFER_SIZE 4096 /* XXXRTH Constant. */ 148254402Serwin#define EDNSOPTS 2 149135446Strhodes 150170222Sdougb/*% 151135446Strhodes * This defines the maximum number of timeouts we will permit before we 152135446Strhodes * disable EDNS0 on the query. 153135446Strhodes */ 154193149Sdougb#define MAX_EDNS0_TIMEOUTS 3 155135446Strhodes 156135446Strhodestypedef struct fetchctx fetchctx_t; 157135446Strhodes 158135446Strhodestypedef struct query { 159135446Strhodes /* Locked by task event serialization. */ 160135446Strhodes unsigned int magic; 161135446Strhodes fetchctx_t * fctx; 162135446Strhodes isc_mem_t * mctx; 163135446Strhodes dns_dispatchmgr_t * dispatchmgr; 164135446Strhodes dns_dispatch_t * dispatch; 165186462Sdougb isc_boolean_t exclusivesocket; 166135446Strhodes dns_adbaddrinfo_t * addrinfo; 167135446Strhodes isc_socket_t * tcpsocket; 168135446Strhodes isc_time_t start; 169135446Strhodes dns_messageid_t id; 170135446Strhodes dns_dispentry_t * dispentry; 171135446Strhodes ISC_LINK(struct query) link; 172135446Strhodes isc_buffer_t buffer; 173135446Strhodes isc_buffer_t *tsig; 174135446Strhodes dns_tsigkey_t *tsigkey; 175254897Serwin isc_socketevent_t sendevent; 176135446Strhodes unsigned int options; 177135446Strhodes unsigned int attributes; 178135446Strhodes unsigned int sends; 179135446Strhodes unsigned int connects; 180135446Strhodes unsigned char data[512]; 181135446Strhodes} resquery_t; 182135446Strhodes 183135446Strhodes#define QUERY_MAGIC ISC_MAGIC('Q', '!', '!', '!') 184135446Strhodes#define VALID_QUERY(query) ISC_MAGIC_VALID(query, QUERY_MAGIC) 185135446Strhodes 186193149Sdougb#define RESQUERY_ATTR_CANCELED 0x02 187135446Strhodes 188193149Sdougb#define RESQUERY_CONNECTING(q) ((q)->connects > 0) 189193149Sdougb#define RESQUERY_CANCELED(q) (((q)->attributes & \ 190135446Strhodes RESQUERY_ATTR_CANCELED) != 0) 191193149Sdougb#define RESQUERY_SENDING(q) ((q)->sends > 0) 192135446Strhodes 193135446Strhodestypedef enum { 194193149Sdougb fetchstate_init = 0, /*%< Start event has not run yet. */ 195135446Strhodes fetchstate_active, 196193149Sdougb fetchstate_done /*%< FETCHDONE events posted. */ 197135446Strhodes} fetchstate; 198135446Strhodes 199193149Sdougbtypedef enum { 200193149Sdougb badns_unreachable = 0, 201193149Sdougb badns_response, 202193149Sdougb badns_validation 203193149Sdougb} badnstype_t; 204193149Sdougb 205135446Strhodesstruct fetchctx { 206170222Sdougb /*% Not locked. */ 207135446Strhodes unsigned int magic; 208135446Strhodes dns_resolver_t * res; 209135446Strhodes dns_name_t name; 210135446Strhodes dns_rdatatype_t type; 211135446Strhodes unsigned int options; 212135446Strhodes unsigned int bucketnum; 213236374Sdougb char * info; 214236374Sdougb isc_mem_t * mctx; 215236374Sdougb 216170222Sdougb /*% Locked by appropriate bucket lock. */ 217135446Strhodes fetchstate state; 218135446Strhodes isc_boolean_t want_shutdown; 219135446Strhodes isc_boolean_t cloned; 220170222Sdougb isc_boolean_t spilled; 221135446Strhodes unsigned int references; 222135446Strhodes isc_event_t control_event; 223193149Sdougb ISC_LINK(struct fetchctx) link; 224193149Sdougb ISC_LIST(dns_fetchevent_t) events; 225170222Sdougb /*% Locked by task event serialization. */ 226135446Strhodes dns_name_t domain; 227135446Strhodes dns_rdataset_t nameservers; 228135446Strhodes unsigned int attributes; 229135446Strhodes isc_timer_t * timer; 230135446Strhodes isc_time_t expires; 231135446Strhodes isc_interval_t interval; 232135446Strhodes dns_message_t * qmessage; 233135446Strhodes dns_message_t * rmessage; 234135446Strhodes ISC_LIST(resquery_t) queries; 235135446Strhodes dns_adbfindlist_t finds; 236135446Strhodes dns_adbfind_t * find; 237135446Strhodes dns_adbfindlist_t altfinds; 238135446Strhodes dns_adbfind_t * altfind; 239135446Strhodes dns_adbaddrinfolist_t forwaddrs; 240135446Strhodes dns_adbaddrinfolist_t altaddrs; 241135446Strhodes isc_sockaddrlist_t forwarders; 242135446Strhodes dns_fwdpolicy_t fwdpolicy; 243135446Strhodes isc_sockaddrlist_t bad; 244170222Sdougb isc_sockaddrlist_t edns; 245170222Sdougb isc_sockaddrlist_t edns512; 246218384Sdougb isc_sockaddrlist_t bad_edns; 247174187Sdougb dns_validator_t *validator; 248193149Sdougb ISC_LIST(dns_validator_t) validators; 249135446Strhodes dns_db_t * cache; 250135446Strhodes dns_adb_t * adb; 251234010Sdougb isc_boolean_t ns_ttl_ok; 252234010Sdougb isc_uint32_t ns_ttl; 253135446Strhodes 254170222Sdougb /*% 255135446Strhodes * The number of events we're waiting for. 256135446Strhodes */ 257135446Strhodes unsigned int pending; 258135446Strhodes 259170222Sdougb /*% 260135446Strhodes * The number of times we've "restarted" the current 261135446Strhodes * nameserver set. This acts as a failsafe to prevent 262135446Strhodes * us from pounding constantly on a particular set of 263135446Strhodes * servers that, for whatever reason, are not giving 264135446Strhodes * us useful responses, but are responding in such a 265135446Strhodes * way that they are not marked "bad". 266135446Strhodes */ 267135446Strhodes unsigned int restarts; 268135446Strhodes 269170222Sdougb /*% 270186462Sdougb * The number of timeouts that have occurred since we 271135446Strhodes * last successfully received a response packet. This 272135446Strhodes * is used for EDNS0 black hole detection. 273135446Strhodes */ 274135446Strhodes unsigned int timeouts; 275193149Sdougb 276170222Sdougb /*% 277135446Strhodes * Look aside state for DS lookups. 278135446Strhodes */ 279186462Sdougb dns_name_t nsname; 280135446Strhodes dns_fetch_t * nsfetch; 281135446Strhodes dns_rdataset_t nsrrset; 282166332Sdougb 283166332Sdougb /*% 284166332Sdougb * Number of queries that reference this context. 285166332Sdougb */ 286166332Sdougb unsigned int nqueries; 287193149Sdougb 288193149Sdougb /*% 289193149Sdougb * The reason to print when logging a successful 290193149Sdougb * response to a query. 291193149Sdougb */ 292193149Sdougb const char * reason; 293193149Sdougb 294193149Sdougb /*% 295193149Sdougb * Random numbers to use for mixing up server addresses. 296193149Sdougb */ 297193149Sdougb isc_uint32_t rand_buf; 298193149Sdougb isc_uint32_t rand_bits; 299193149Sdougb 300193149Sdougb /*% 301193149Sdougb * Fetch-local statistics for detailed logging. 302193149Sdougb */ 303193149Sdougb isc_result_t result; /*%< fetch result */ 304193149Sdougb isc_result_t vresult; /*%< validation result */ 305193149Sdougb int exitline; 306193149Sdougb isc_time_t start; 307193149Sdougb isc_uint64_t duration; 308193149Sdougb isc_boolean_t logged; 309193149Sdougb unsigned int querysent; 310275672Sdelphij unsigned int totalqueries; 311193149Sdougb unsigned int referrals; 312193149Sdougb unsigned int lamecount; 313193149Sdougb unsigned int neterr; 314193149Sdougb unsigned int badresp; 315193149Sdougb unsigned int adberr; 316193149Sdougb unsigned int findfail; 317193149Sdougb unsigned int valfail; 318193149Sdougb isc_boolean_t timeout; 319224092Sdougb dns_adbaddrinfo_t *addrinfo; 320224092Sdougb isc_sockaddr_t *client; 321275672Sdelphij unsigned int depth; 322135446Strhodes}; 323135446Strhodes 324135446Strhodes#define FCTX_MAGIC ISC_MAGIC('F', '!', '!', '!') 325135446Strhodes#define VALID_FCTX(fctx) ISC_MAGIC_VALID(fctx, FCTX_MAGIC) 326135446Strhodes 327193149Sdougb#define FCTX_ATTR_HAVEANSWER 0x0001 328193149Sdougb#define FCTX_ATTR_GLUING 0x0002 329193149Sdougb#define FCTX_ATTR_ADDRWAIT 0x0004 330193149Sdougb#define FCTX_ATTR_SHUTTINGDOWN 0x0008 331193149Sdougb#define FCTX_ATTR_WANTCACHE 0x0010 332193149Sdougb#define FCTX_ATTR_WANTNCACHE 0x0020 333193149Sdougb#define FCTX_ATTR_NEEDEDNS0 0x0040 334193149Sdougb#define FCTX_ATTR_TRIEDFIND 0x0080 335193149Sdougb#define FCTX_ATTR_TRIEDALT 0x0100 336135446Strhodes 337193149Sdougb#define HAVE_ANSWER(f) (((f)->attributes & FCTX_ATTR_HAVEANSWER) != \ 338135446Strhodes 0) 339193149Sdougb#define GLUING(f) (((f)->attributes & FCTX_ATTR_GLUING) != \ 340135446Strhodes 0) 341193149Sdougb#define ADDRWAIT(f) (((f)->attributes & FCTX_ATTR_ADDRWAIT) != \ 342135446Strhodes 0) 343193149Sdougb#define SHUTTINGDOWN(f) (((f)->attributes & FCTX_ATTR_SHUTTINGDOWN) \ 344174187Sdougb != 0) 345193149Sdougb#define WANTCACHE(f) (((f)->attributes & FCTX_ATTR_WANTCACHE) != 0) 346193149Sdougb#define WANTNCACHE(f) (((f)->attributes & FCTX_ATTR_WANTNCACHE) != 0) 347193149Sdougb#define NEEDEDNS0(f) (((f)->attributes & FCTX_ATTR_NEEDEDNS0) != 0) 348193149Sdougb#define TRIEDFIND(f) (((f)->attributes & FCTX_ATTR_TRIEDFIND) != 0) 349193149Sdougb#define TRIEDALT(f) (((f)->attributes & FCTX_ATTR_TRIEDALT) != 0) 350135446Strhodes 351153816Sdougbtypedef struct { 352153816Sdougb dns_adbaddrinfo_t * addrinfo; 353153816Sdougb fetchctx_t * fctx; 354153816Sdougb} dns_valarg_t; 355153816Sdougb 356135446Strhodesstruct dns_fetch { 357135446Strhodes unsigned int magic; 358135446Strhodes fetchctx_t * private; 359135446Strhodes}; 360135446Strhodes 361135446Strhodes#define DNS_FETCH_MAGIC ISC_MAGIC('F', 't', 'c', 'h') 362135446Strhodes#define DNS_FETCH_VALID(fetch) ISC_MAGIC_VALID(fetch, DNS_FETCH_MAGIC) 363135446Strhodes 364135446Strhodestypedef struct fctxbucket { 365135446Strhodes isc_task_t * task; 366135446Strhodes isc_mutex_t lock; 367135446Strhodes ISC_LIST(fetchctx_t) fctxs; 368135446Strhodes isc_boolean_t exiting; 369170222Sdougb isc_mem_t * mctx; 370135446Strhodes} fctxbucket_t; 371135446Strhodes 372135446Strhodestypedef struct alternate { 373135446Strhodes isc_boolean_t isaddress; 374193149Sdougb union { 375135446Strhodes isc_sockaddr_t addr; 376135446Strhodes struct { 377193149Sdougb dns_name_t name; 378193149Sdougb in_port_t port; 379135446Strhodes } _n; 380135446Strhodes } _u; 381193149Sdougb ISC_LINK(struct alternate) link; 382135446Strhodes} alternate_t; 383135446Strhodes 384205292Sdougbtypedef struct dns_badcache dns_badcache_t; 385205292Sdougbstruct dns_badcache { 386205292Sdougb dns_badcache_t * next; 387205292Sdougb dns_rdatatype_t type; 388205292Sdougb isc_time_t expire; 389205292Sdougb unsigned int hashval; 390205292Sdougb dns_name_t name; 391205292Sdougb}; 392205292Sdougb#define DNS_BADCACHE_SIZE 1021 393205292Sdougb#define DNS_BADCACHE_TTL(fctx) \ 394205292Sdougb (((fctx)->res->lame_ttl > 30 ) ? (fctx)->res->lame_ttl : 30) 395205292Sdougb 396135446Strhodesstruct dns_resolver { 397135446Strhodes /* Unlocked. */ 398135446Strhodes unsigned int magic; 399135446Strhodes isc_mem_t * mctx; 400135446Strhodes isc_mutex_t lock; 401186462Sdougb isc_mutex_t nlock; 402186462Sdougb isc_mutex_t primelock; 403135446Strhodes dns_rdataclass_t rdclass; 404135446Strhodes isc_socketmgr_t * socketmgr; 405135446Strhodes isc_timermgr_t * timermgr; 406135446Strhodes isc_taskmgr_t * taskmgr; 407135446Strhodes dns_view_t * view; 408135446Strhodes isc_boolean_t frozen; 409135446Strhodes unsigned int options; 410135446Strhodes dns_dispatchmgr_t * dispatchmgr; 411254897Serwin dns_dispatchset_t * dispatches4; 412186462Sdougb isc_boolean_t exclusivev4; 413254897Serwin dns_dispatchset_t * dispatches6; 414186462Sdougb isc_boolean_t exclusivev6; 415135446Strhodes unsigned int nbuckets; 416135446Strhodes fctxbucket_t * buckets; 417135446Strhodes isc_uint32_t lame_ttl; 418135446Strhodes ISC_LIST(alternate_t) alternates; 419135446Strhodes isc_uint16_t udpsize; 420135446Strhodes#if USE_ALGLOCK 421135446Strhodes isc_rwlock_t alglock; 422135446Strhodes#endif 423135446Strhodes dns_rbt_t * algorithms; 424135446Strhodes#if USE_MBSLOCK 425135446Strhodes isc_rwlock_t mbslock; 426135446Strhodes#endif 427135446Strhodes dns_rbt_t * mustbesecure; 428170222Sdougb unsigned int spillatmax; 429170222Sdougb unsigned int spillatmin; 430170222Sdougb isc_timer_t * spillattimer; 431170222Sdougb isc_boolean_t zero_no_soa_ttl; 432224092Sdougb unsigned int query_timeout; 433275672Sdelphij unsigned int maxdepth; 434193149Sdougb 435135446Strhodes /* Locked by lock. */ 436135446Strhodes unsigned int references; 437135446Strhodes isc_boolean_t exiting; 438135446Strhodes isc_eventlist_t whenshutdown; 439135446Strhodes unsigned int activebuckets; 440135446Strhodes isc_boolean_t priming; 441186462Sdougb unsigned int spillat; /* clients-per-query */ 442205292Sdougb 443205292Sdougb /* Bad cache. */ 444205292Sdougb dns_badcache_t ** badcache; 445205292Sdougb unsigned int badcount; 446205292Sdougb unsigned int badhash; 447205292Sdougb unsigned int badsweep; 448205292Sdougb 449135446Strhodes /* Locked by primelock. */ 450135446Strhodes dns_fetch_t * primefetch; 451135446Strhodes /* Locked by nlock. */ 452135446Strhodes unsigned int nfctx; 453135446Strhodes}; 454135446Strhodes 455135446Strhodes#define RES_MAGIC ISC_MAGIC('R', 'e', 's', '!') 456135446Strhodes#define VALID_RESOLVER(res) ISC_MAGIC_VALID(res, RES_MAGIC) 457135446Strhodes 458170222Sdougb/*% 459135446Strhodes * Private addrinfo flags. These must not conflict with DNS_FETCHOPT_NOEDNS0, 460135446Strhodes * which we also use as an addrinfo flag. 461135446Strhodes */ 462193149Sdougb#define FCTX_ADDRINFO_MARK 0x0001 463193149Sdougb#define FCTX_ADDRINFO_FORWARDER 0x1000 464193149Sdougb#define FCTX_ADDRINFO_TRIED 0x2000 465193149Sdougb#define UNMARKED(a) (((a)->flags & FCTX_ADDRINFO_MARK) \ 466135446Strhodes == 0) 467193149Sdougb#define ISFORWARDER(a) (((a)->flags & \ 468135446Strhodes FCTX_ADDRINFO_FORWARDER) != 0) 469193149Sdougb#define TRIED(a) (((a)->flags & \ 470193149Sdougb FCTX_ADDRINFO_TRIED) != 0) 471135446Strhodes 472135446Strhodes#define NXDOMAIN(r) (((r)->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0) 473223812Sdougb#define NEGATIVE(r) (((r)->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) 474135446Strhodes 475135446Strhodesstatic void destroy(dns_resolver_t *res); 476135446Strhodesstatic void empty_bucket(dns_resolver_t *res); 477135446Strhodesstatic isc_result_t resquery_send(resquery_t *query); 478135446Strhodesstatic void resquery_response(isc_task_t *task, isc_event_t *event); 479135446Strhodesstatic void resquery_connected(isc_task_t *task, isc_event_t *event); 480205292Sdougbstatic void fctx_try(fetchctx_t *fctx, isc_boolean_t retrying, 481205292Sdougb isc_boolean_t badcache); 482236374Sdougbstatic void fctx_destroy(fetchctx_t *fctx); 483236374Sdougbstatic isc_boolean_t fctx_unlink(fetchctx_t *fctx); 484135446Strhodesstatic isc_result_t ncache_adderesult(dns_message_t *message, 485135446Strhodes dns_db_t *cache, dns_dbnode_t *node, 486135446Strhodes dns_rdatatype_t covers, 487135446Strhodes isc_stdtime_t now, dns_ttl_t maxttl, 488193149Sdougb isc_boolean_t optout, 489254402Serwin isc_boolean_t secure, 490135446Strhodes dns_rdataset_t *ardataset, 491135446Strhodes isc_result_t *eresultp); 492186462Sdougbstatic void validated(isc_task_t *task, isc_event_t *event); 493234010Sdougbstatic isc_boolean_t maybe_destroy(fetchctx_t *fctx, isc_boolean_t locked); 494186462Sdougbstatic void add_bad(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, 495193149Sdougb isc_result_t reason, badnstype_t badtype); 496254402Serwinstatic inline isc_result_t findnoqname(fetchctx_t *fctx, dns_name_t *name, 497254402Serwin dns_rdatatype_t type, 498254402Serwin dns_name_t **noqname); 499135446Strhodes 500193149Sdougb/*% 501193149Sdougb * Increment resolver-related statistics counters. 502193149Sdougb */ 503193149Sdougbstatic inline void 504193149Sdougbinc_stats(dns_resolver_t *res, isc_statscounter_t counter) { 505193149Sdougb if (res->view->resstats != NULL) 506193149Sdougb isc_stats_increment(res->view->resstats, counter); 507193149Sdougb} 508193149Sdougb 509153816Sdougbstatic isc_result_t 510153816Sdougbvalcreate(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, dns_name_t *name, 511153816Sdougb dns_rdatatype_t type, dns_rdataset_t *rdataset, 512153816Sdougb dns_rdataset_t *sigrdataset, unsigned int valoptions, 513153816Sdougb isc_task_t *task) 514153816Sdougb{ 515153816Sdougb dns_validator_t *validator = NULL; 516153816Sdougb dns_valarg_t *valarg; 517153816Sdougb isc_result_t result; 518153816Sdougb 519236374Sdougb valarg = isc_mem_get(fctx->mctx, sizeof(*valarg)); 520153816Sdougb if (valarg == NULL) 521153816Sdougb return (ISC_R_NOMEMORY); 522153816Sdougb 523153816Sdougb valarg->fctx = fctx; 524153816Sdougb valarg->addrinfo = addrinfo; 525153816Sdougb 526166332Sdougb if (!ISC_LIST_EMPTY(fctx->validators)) 527308205Sdelphij valoptions |= DNS_VALIDATOR_DEFER; 528308205Sdelphij else 529308205Sdelphij valoptions &= ~DNS_VALIDATOR_DEFER; 530166332Sdougb 531153816Sdougb result = dns_validator_create(fctx->res->view, name, type, rdataset, 532153816Sdougb sigrdataset, fctx->rmessage, 533153816Sdougb valoptions, task, validated, valarg, 534153816Sdougb &validator); 535174187Sdougb if (result == ISC_R_SUCCESS) { 536193149Sdougb inc_stats(fctx->res, dns_resstatscounter_val); 537174187Sdougb if ((valoptions & DNS_VALIDATOR_DEFER) == 0) { 538174187Sdougb INSIST(fctx->validator == NULL); 539218384Sdougb fctx->validator = validator; 540174187Sdougb } 541153816Sdougb ISC_LIST_APPEND(fctx->validators, validator, link); 542174187Sdougb } else 543236374Sdougb isc_mem_put(fctx->mctx, valarg, sizeof(*valarg)); 544153816Sdougb return (result); 545153816Sdougb} 546153816Sdougb 547135446Strhodesstatic isc_boolean_t 548194995Sdougbrrsig_fromchildzone(fetchctx_t *fctx, dns_rdataset_t *rdataset) { 549194995Sdougb dns_namereln_t namereln; 550194995Sdougb dns_rdata_rrsig_t rrsig; 551194995Sdougb dns_rdata_t rdata = DNS_RDATA_INIT; 552194995Sdougb int order; 553194995Sdougb isc_result_t result; 554194995Sdougb unsigned int labels; 555194995Sdougb 556194995Sdougb for (result = dns_rdataset_first(rdataset); 557194995Sdougb result == ISC_R_SUCCESS; 558194995Sdougb result = dns_rdataset_next(rdataset)) { 559194995Sdougb dns_rdataset_current(rdataset, &rdata); 560194995Sdougb result = dns_rdata_tostruct(&rdata, &rrsig, NULL); 561194995Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 562194995Sdougb namereln = dns_name_fullcompare(&rrsig.signer, &fctx->domain, 563194995Sdougb &order, &labels); 564194995Sdougb if (namereln == dns_namereln_subdomain) 565194995Sdougb return (ISC_TRUE); 566194995Sdougb dns_rdata_reset(&rdata); 567194995Sdougb } 568194995Sdougb return (ISC_FALSE); 569194995Sdougb} 570194995Sdougb 571194995Sdougbstatic isc_boolean_t 572135446Strhodesfix_mustbedelegationornxdomain(dns_message_t *message, fetchctx_t *fctx) { 573135446Strhodes dns_name_t *name; 574135446Strhodes dns_name_t *domain = &fctx->domain; 575135446Strhodes dns_rdataset_t *rdataset; 576135446Strhodes dns_rdatatype_t type; 577135446Strhodes isc_result_t result; 578135446Strhodes isc_boolean_t keep_auth = ISC_FALSE; 579135446Strhodes 580135446Strhodes if (message->rcode == dns_rcode_nxdomain) 581135446Strhodes return (ISC_FALSE); 582135446Strhodes 583135446Strhodes /* 584194995Sdougb * A DS RRset can appear anywhere in a zone, even for a delegation-only 585194995Sdougb * zone. So a response to an explicit query for this type should be 586194995Sdougb * excluded from delegation-only fixup. 587194995Sdougb * 588194995Sdougb * SOA, NS, and DNSKEY can only exist at a zone apex, so a postive 589194995Sdougb * response to a query for these types can never violate the 590194995Sdougb * delegation-only assumption: if the query name is below a 591194995Sdougb * zone cut, the response should normally be a referral, which should 592194995Sdougb * be accepted; if the query name is below a zone cut but the server 593194995Sdougb * happens to have authority for the zone of the query name, the 594194995Sdougb * response is a (non-referral) answer. But this does not violate 595194995Sdougb * delegation-only because the query name must be in a different zone 596194995Sdougb * due to the "apex-only" nature of these types. Note that if the 597194995Sdougb * remote server happens to have authority for a child zone of a 598194995Sdougb * delegation-only zone, we may still incorrectly "fix" the response 599194995Sdougb * with NXDOMAIN for queries for other types. Unfortunately it's 600194995Sdougb * generally impossible to differentiate this case from violation of 601194995Sdougb * the delegation-only assumption. Once the resolver learns the 602194995Sdougb * correct zone cut, possibly via a separate query for an "apex-only" 603194995Sdougb * type, queries for other types will be resolved correctly. 604194995Sdougb * 605194995Sdougb * A query for type ANY will be accepted if it hits an exceptional 606194995Sdougb * type above in the answer section as it should be from a child 607194995Sdougb * zone. 608194995Sdougb * 609194995Sdougb * Also accept answers with RRSIG records from the child zone. 610194995Sdougb * Direct queries for RRSIG records should not be answered from 611194995Sdougb * the parent zone. 612135446Strhodes */ 613194995Sdougb 614135446Strhodes if (message->counts[DNS_SECTION_ANSWER] != 0 && 615135446Strhodes (fctx->type == dns_rdatatype_ns || 616194995Sdougb fctx->type == dns_rdatatype_ds || 617194995Sdougb fctx->type == dns_rdatatype_soa || 618194995Sdougb fctx->type == dns_rdatatype_any || 619194995Sdougb fctx->type == dns_rdatatype_rrsig || 620194995Sdougb fctx->type == dns_rdatatype_dnskey)) { 621135446Strhodes result = dns_message_firstname(message, DNS_SECTION_ANSWER); 622135446Strhodes while (result == ISC_R_SUCCESS) { 623135446Strhodes name = NULL; 624135446Strhodes dns_message_currentname(message, DNS_SECTION_ANSWER, 625135446Strhodes &name); 626135446Strhodes for (rdataset = ISC_LIST_HEAD(name->list); 627135446Strhodes rdataset != NULL; 628135446Strhodes rdataset = ISC_LIST_NEXT(rdataset, link)) { 629194995Sdougb if (!dns_name_equal(name, &fctx->name)) 630194995Sdougb continue; 631135446Strhodes type = rdataset->type; 632194995Sdougb /* 633194995Sdougb * RRsig from child? 634194995Sdougb */ 635194995Sdougb if (type == dns_rdatatype_rrsig && 636194995Sdougb rrsig_fromchildzone(fctx, rdataset)) 637135446Strhodes return (ISC_FALSE); 638194995Sdougb /* 639194995Sdougb * Direct query for apex records or DS. 640194995Sdougb */ 641194995Sdougb if (fctx->type == type && 642194995Sdougb (type == dns_rdatatype_ds || 643194995Sdougb type == dns_rdatatype_ns || 644194995Sdougb type == dns_rdatatype_soa || 645194995Sdougb type == dns_rdatatype_dnskey)) 646194995Sdougb return (ISC_FALSE); 647194995Sdougb /* 648194995Sdougb * Indirect query for apex records or DS. 649194995Sdougb */ 650194995Sdougb if (fctx->type == dns_rdatatype_any && 651194995Sdougb (type == dns_rdatatype_ns || 652194995Sdougb type == dns_rdatatype_ds || 653194995Sdougb type == dns_rdatatype_soa || 654194995Sdougb type == dns_rdatatype_dnskey)) 655194995Sdougb return (ISC_FALSE); 656135446Strhodes } 657135446Strhodes result = dns_message_nextname(message, 658135446Strhodes DNS_SECTION_ANSWER); 659135446Strhodes } 660135446Strhodes } 661135446Strhodes 662194995Sdougb /* 663194995Sdougb * A NODATA response to a DS query? 664194995Sdougb */ 665194995Sdougb if (fctx->type == dns_rdatatype_ds && 666194995Sdougb message->counts[DNS_SECTION_ANSWER] == 0) 667194995Sdougb return (ISC_FALSE); 668194995Sdougb 669194995Sdougb /* Look for referral or indication of answer from child zone? */ 670135446Strhodes if (message->counts[DNS_SECTION_AUTHORITY] == 0) 671135446Strhodes goto munge; 672135446Strhodes 673135446Strhodes result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); 674135446Strhodes while (result == ISC_R_SUCCESS) { 675135446Strhodes name = NULL; 676135446Strhodes dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name); 677135446Strhodes for (rdataset = ISC_LIST_HEAD(name->list); 678135446Strhodes rdataset != NULL; 679135446Strhodes rdataset = ISC_LIST_NEXT(rdataset, link)) { 680135446Strhodes type = rdataset->type; 681135446Strhodes if (type == dns_rdatatype_soa && 682135446Strhodes dns_name_equal(name, domain)) 683135446Strhodes keep_auth = ISC_TRUE; 684194995Sdougb 685135446Strhodes if (type != dns_rdatatype_ns && 686194995Sdougb type != dns_rdatatype_soa && 687194995Sdougb type != dns_rdatatype_rrsig) 688135446Strhodes continue; 689194995Sdougb 690194995Sdougb if (type == dns_rdatatype_rrsig) { 691194995Sdougb if (rrsig_fromchildzone(fctx, rdataset)) 692194995Sdougb return (ISC_FALSE); 693194995Sdougb else 694194995Sdougb continue; 695194995Sdougb } 696194995Sdougb 697194995Sdougb /* NS or SOA records. */ 698194995Sdougb if (dns_name_equal(name, domain)) { 699194995Sdougb /* 700194995Sdougb * If a query for ANY causes a negative 701194995Sdougb * response, we can be sure that this is 702194995Sdougb * an empty node. For other type of queries 703194995Sdougb * we cannot differentiate an empty node 704194995Sdougb * from a node that just doesn't have that 705194995Sdougb * type of record. We only accept the former 706194995Sdougb * case. 707194995Sdougb */ 708194995Sdougb if (message->counts[DNS_SECTION_ANSWER] == 0 && 709194995Sdougb fctx->type == dns_rdatatype_any) 710194995Sdougb return (ISC_FALSE); 711194995Sdougb } else if (dns_name_issubdomain(name, domain)) { 712194995Sdougb /* Referral or answer from child zone. */ 713135446Strhodes return (ISC_FALSE); 714194995Sdougb } 715135446Strhodes } 716135446Strhodes result = dns_message_nextname(message, DNS_SECTION_AUTHORITY); 717135446Strhodes } 718135446Strhodes 719135446Strhodes munge: 720135446Strhodes message->rcode = dns_rcode_nxdomain; 721135446Strhodes message->counts[DNS_SECTION_ANSWER] = 0; 722135446Strhodes if (!keep_auth) 723135446Strhodes message->counts[DNS_SECTION_AUTHORITY] = 0; 724135446Strhodes message->counts[DNS_SECTION_ADDITIONAL] = 0; 725135446Strhodes return (ISC_TRUE); 726135446Strhodes} 727135446Strhodes 728135446Strhodesstatic inline isc_result_t 729135446Strhodesfctx_starttimer(fetchctx_t *fctx) { 730135446Strhodes /* 731135446Strhodes * Start the lifetime timer for fctx. 732135446Strhodes * 733135446Strhodes * This is also used for stopping the idle timer; in that 734135446Strhodes * case we must purge events already posted to ensure that 735135446Strhodes * no further idle events are delivered. 736135446Strhodes */ 737135446Strhodes return (isc_timer_reset(fctx->timer, isc_timertype_once, 738165071Sdougb &fctx->expires, NULL, ISC_TRUE)); 739135446Strhodes} 740135446Strhodes 741135446Strhodesstatic inline void 742135446Strhodesfctx_stoptimer(fetchctx_t *fctx) { 743135446Strhodes isc_result_t result; 744135446Strhodes 745135446Strhodes /* 746135446Strhodes * We don't return a result if resetting the timer to inactive fails 747135446Strhodes * since there's nothing to be done about it. Resetting to inactive 748135446Strhodes * should never fail anyway, since the code as currently written 749135446Strhodes * cannot fail in that case. 750135446Strhodes */ 751135446Strhodes result = isc_timer_reset(fctx->timer, isc_timertype_inactive, 752135446Strhodes NULL, NULL, ISC_TRUE); 753135446Strhodes if (result != ISC_R_SUCCESS) { 754135446Strhodes UNEXPECTED_ERROR(__FILE__, __LINE__, 755135446Strhodes "isc_timer_reset(): %s", 756135446Strhodes isc_result_totext(result)); 757135446Strhodes } 758135446Strhodes} 759135446Strhodes 760135446Strhodes 761135446Strhodesstatic inline isc_result_t 762193149Sdougbfctx_startidletimer(fetchctx_t *fctx, isc_interval_t *interval) { 763135446Strhodes /* 764135446Strhodes * Start the idle timer for fctx. The lifetime timer continues 765135446Strhodes * to be in effect. 766135446Strhodes */ 767135446Strhodes return (isc_timer_reset(fctx->timer, isc_timertype_once, 768193149Sdougb &fctx->expires, interval, ISC_FALSE)); 769135446Strhodes} 770135446Strhodes 771135446Strhodes/* 772135446Strhodes * Stopping the idle timer is equivalent to calling fctx_starttimer(), but 773135446Strhodes * we use fctx_stopidletimer for readability in the code below. 774135446Strhodes */ 775193149Sdougb#define fctx_stopidletimer fctx_starttimer 776135446Strhodes 777135446Strhodes 778135446Strhodesstatic inline void 779135446Strhodesresquery_destroy(resquery_t **queryp) { 780135446Strhodes resquery_t *query; 781135446Strhodes 782135446Strhodes REQUIRE(queryp != NULL); 783135446Strhodes query = *queryp; 784135446Strhodes REQUIRE(!ISC_LINK_LINKED(query, link)); 785135446Strhodes 786135446Strhodes INSIST(query->tcpsocket == NULL); 787135446Strhodes 788166332Sdougb query->fctx->nqueries--; 789234010Sdougb if (SHUTTINGDOWN(query->fctx)) { 790234010Sdougb dns_resolver_t *res = query->fctx->res; 791234010Sdougb if (maybe_destroy(query->fctx, ISC_FALSE)) 792234010Sdougb empty_bucket(res); 793234010Sdougb } 794135446Strhodes query->magic = 0; 795135446Strhodes isc_mem_put(query->mctx, query, sizeof(*query)); 796135446Strhodes *queryp = NULL; 797135446Strhodes} 798135446Strhodes 799135446Strhodesstatic void 800135446Strhodesfctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp, 801135446Strhodes isc_time_t *finish, isc_boolean_t no_response) 802135446Strhodes{ 803135446Strhodes fetchctx_t *fctx; 804135446Strhodes resquery_t *query; 805193149Sdougb unsigned int rtt, rttms; 806135446Strhodes unsigned int factor; 807135446Strhodes dns_adbfind_t *find; 808135446Strhodes dns_adbaddrinfo_t *addrinfo; 809186462Sdougb isc_socket_t *socket; 810135446Strhodes 811135446Strhodes query = *queryp; 812135446Strhodes fctx = query->fctx; 813135446Strhodes 814135446Strhodes FCTXTRACE("cancelquery"); 815135446Strhodes 816135446Strhodes REQUIRE(!RESQUERY_CANCELED(query)); 817135446Strhodes 818135446Strhodes query->attributes |= RESQUERY_ATTR_CANCELED; 819135446Strhodes 820135446Strhodes /* 821135446Strhodes * Should we update the RTT? 822135446Strhodes */ 823135446Strhodes if (finish != NULL || no_response) { 824135446Strhodes if (finish != NULL) { 825135446Strhodes /* 826135446Strhodes * We have both the start and finish times for this 827135446Strhodes * packet, so we can compute a real RTT. 828135446Strhodes */ 829135446Strhodes rtt = (unsigned int)isc_time_microdiff(finish, 830135446Strhodes &query->start); 831135446Strhodes factor = DNS_ADB_RTTADJDEFAULT; 832193149Sdougb 833193149Sdougb rttms = rtt / 1000; 834193149Sdougb if (rttms < DNS_RESOLVER_QRYRTTCLASS0) { 835193149Sdougb inc_stats(fctx->res, 836193149Sdougb dns_resstatscounter_queryrtt0); 837193149Sdougb } else if (rttms < DNS_RESOLVER_QRYRTTCLASS1) { 838193149Sdougb inc_stats(fctx->res, 839193149Sdougb dns_resstatscounter_queryrtt1); 840193149Sdougb } else if (rttms < DNS_RESOLVER_QRYRTTCLASS2) { 841193149Sdougb inc_stats(fctx->res, 842193149Sdougb dns_resstatscounter_queryrtt2); 843193149Sdougb } else if (rttms < DNS_RESOLVER_QRYRTTCLASS3) { 844193149Sdougb inc_stats(fctx->res, 845193149Sdougb dns_resstatscounter_queryrtt3); 846193149Sdougb } else if (rttms < DNS_RESOLVER_QRYRTTCLASS4) { 847193149Sdougb inc_stats(fctx->res, 848193149Sdougb dns_resstatscounter_queryrtt4); 849193149Sdougb } else { 850193149Sdougb inc_stats(fctx->res, 851193149Sdougb dns_resstatscounter_queryrtt5); 852193149Sdougb } 853135446Strhodes } else { 854135446Strhodes /* 855135446Strhodes * We don't have an RTT for this query. Maybe the 856135446Strhodes * packet was lost, or maybe this server is very 857135446Strhodes * slow. We don't know. Increase the RTT. 858135446Strhodes */ 859135446Strhodes INSIST(no_response); 860170222Sdougb rtt = query->addrinfo->srtt + 200000; 861245163Serwin if (rtt > MAX_SINGLE_QUERY_TIMEOUT_US) 862245163Serwin rtt = MAX_SINGLE_QUERY_TIMEOUT_US; 863135446Strhodes /* 864135446Strhodes * Replace the current RTT with our value. 865135446Strhodes */ 866135446Strhodes factor = DNS_ADB_RTTADJREPLACE; 867135446Strhodes } 868135446Strhodes dns_adb_adjustsrtt(fctx->adb, query->addrinfo, rtt, factor); 869135446Strhodes } 870135446Strhodes 871193149Sdougb /* Remember that the server has been tried. */ 872193149Sdougb if (!TRIED(query->addrinfo)) { 873193149Sdougb dns_adb_changeflags(fctx->adb, query->addrinfo, 874193149Sdougb FCTX_ADDRINFO_TRIED, FCTX_ADDRINFO_TRIED); 875193149Sdougb } 876193149Sdougb 877135446Strhodes /* 878135446Strhodes * Age RTTs of servers not tried. 879135446Strhodes */ 880135446Strhodes factor = DNS_ADB_RTTADJAGE; 881135446Strhodes if (finish != NULL) 882135446Strhodes for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs); 883135446Strhodes addrinfo != NULL; 884135446Strhodes addrinfo = ISC_LIST_NEXT(addrinfo, publink)) 885135446Strhodes if (UNMARKED(addrinfo)) 886135446Strhodes dns_adb_adjustsrtt(fctx->adb, addrinfo, 887135446Strhodes 0, factor); 888135446Strhodes 889135446Strhodes if (finish != NULL && TRIEDFIND(fctx)) 890135446Strhodes for (find = ISC_LIST_HEAD(fctx->finds); 891135446Strhodes find != NULL; 892135446Strhodes find = ISC_LIST_NEXT(find, publink)) 893135446Strhodes for (addrinfo = ISC_LIST_HEAD(find->list); 894135446Strhodes addrinfo != NULL; 895135446Strhodes addrinfo = ISC_LIST_NEXT(addrinfo, publink)) 896135446Strhodes if (UNMARKED(addrinfo)) 897135446Strhodes dns_adb_adjustsrtt(fctx->adb, addrinfo, 898135446Strhodes 0, factor); 899135446Strhodes 900135446Strhodes if (finish != NULL && TRIEDALT(fctx)) { 901135446Strhodes for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs); 902135446Strhodes addrinfo != NULL; 903135446Strhodes addrinfo = ISC_LIST_NEXT(addrinfo, publink)) 904135446Strhodes if (UNMARKED(addrinfo)) 905135446Strhodes dns_adb_adjustsrtt(fctx->adb, addrinfo, 906135446Strhodes 0, factor); 907135446Strhodes for (find = ISC_LIST_HEAD(fctx->altfinds); 908135446Strhodes find != NULL; 909135446Strhodes find = ISC_LIST_NEXT(find, publink)) 910135446Strhodes for (addrinfo = ISC_LIST_HEAD(find->list); 911135446Strhodes addrinfo != NULL; 912135446Strhodes addrinfo = ISC_LIST_NEXT(addrinfo, publink)) 913135446Strhodes if (UNMARKED(addrinfo)) 914135446Strhodes dns_adb_adjustsrtt(fctx->adb, addrinfo, 915135446Strhodes 0, factor); 916135446Strhodes } 917135446Strhodes 918135446Strhodes /* 919135446Strhodes * Check for any outstanding socket events. If they exist, cancel 920135446Strhodes * them and let the event handlers finish the cleanup. The resolver 921135446Strhodes * only needs to worry about managing the connect and send events; 922135446Strhodes * the dispatcher manages the recv events. 923135446Strhodes */ 924186462Sdougb if (RESQUERY_CONNECTING(query)) { 925135446Strhodes /* 926135446Strhodes * Cancel the connect. 927135446Strhodes */ 928186462Sdougb if (query->tcpsocket != NULL) { 929186462Sdougb isc_socket_cancel(query->tcpsocket, NULL, 930186462Sdougb ISC_SOCKCANCEL_CONNECT); 931186462Sdougb } else if (query->dispentry != NULL) { 932186462Sdougb INSIST(query->exclusivesocket); 933186462Sdougb socket = dns_dispatch_getentrysocket(query->dispentry); 934186462Sdougb if (socket != NULL) 935186462Sdougb isc_socket_cancel(socket, NULL, 936186462Sdougb ISC_SOCKCANCEL_CONNECT); 937186462Sdougb } 938186462Sdougb } else if (RESQUERY_SENDING(query)) { 939135446Strhodes /* 940135446Strhodes * Cancel the pending send. 941135446Strhodes */ 942186462Sdougb if (query->exclusivesocket && query->dispentry != NULL) 943186462Sdougb socket = dns_dispatch_getentrysocket(query->dispentry); 944186462Sdougb else 945186462Sdougb socket = dns_dispatch_getsocket(query->dispatch); 946186462Sdougb if (socket != NULL) 947186462Sdougb isc_socket_cancel(socket, NULL, ISC_SOCKCANCEL_SEND); 948186462Sdougb } 949135446Strhodes 950186462Sdougb if (query->dispentry != NULL) 951186462Sdougb dns_dispatch_removeresponse(&query->dispentry, deventp); 952186462Sdougb 953186462Sdougb ISC_LIST_UNLINK(fctx->queries, query, link); 954186462Sdougb 955186462Sdougb if (query->tsig != NULL) 956186462Sdougb isc_buffer_free(&query->tsig); 957186462Sdougb 958186462Sdougb if (query->tsigkey != NULL) 959186462Sdougb dns_tsigkey_detach(&query->tsigkey); 960186462Sdougb 961135446Strhodes if (query->dispatch != NULL) 962135446Strhodes dns_dispatch_detach(&query->dispatch); 963135446Strhodes 964135446Strhodes if (! (RESQUERY_CONNECTING(query) || RESQUERY_SENDING(query))) 965135446Strhodes /* 966135446Strhodes * It's safe to destroy the query now. 967135446Strhodes */ 968135446Strhodes resquery_destroy(&query); 969135446Strhodes} 970135446Strhodes 971135446Strhodesstatic void 972135446Strhodesfctx_cancelqueries(fetchctx_t *fctx, isc_boolean_t no_response) { 973135446Strhodes resquery_t *query, *next_query; 974135446Strhodes 975135446Strhodes FCTXTRACE("cancelqueries"); 976135446Strhodes 977135446Strhodes for (query = ISC_LIST_HEAD(fctx->queries); 978135446Strhodes query != NULL; 979135446Strhodes query = next_query) { 980135446Strhodes next_query = ISC_LIST_NEXT(query, link); 981135446Strhodes fctx_cancelquery(&query, NULL, NULL, no_response); 982135446Strhodes } 983135446Strhodes} 984135446Strhodes 985135446Strhodesstatic void 986135446Strhodesfctx_cleanupfinds(fetchctx_t *fctx) { 987135446Strhodes dns_adbfind_t *find, *next_find; 988135446Strhodes 989135446Strhodes REQUIRE(ISC_LIST_EMPTY(fctx->queries)); 990135446Strhodes 991135446Strhodes for (find = ISC_LIST_HEAD(fctx->finds); 992135446Strhodes find != NULL; 993135446Strhodes find = next_find) { 994135446Strhodes next_find = ISC_LIST_NEXT(find, publink); 995135446Strhodes ISC_LIST_UNLINK(fctx->finds, find, publink); 996135446Strhodes dns_adb_destroyfind(&find); 997135446Strhodes } 998135446Strhodes fctx->find = NULL; 999135446Strhodes} 1000135446Strhodes 1001135446Strhodesstatic void 1002135446Strhodesfctx_cleanupaltfinds(fetchctx_t *fctx) { 1003135446Strhodes dns_adbfind_t *find, *next_find; 1004135446Strhodes 1005135446Strhodes REQUIRE(ISC_LIST_EMPTY(fctx->queries)); 1006135446Strhodes 1007135446Strhodes for (find = ISC_LIST_HEAD(fctx->altfinds); 1008135446Strhodes find != NULL; 1009135446Strhodes find = next_find) { 1010135446Strhodes next_find = ISC_LIST_NEXT(find, publink); 1011135446Strhodes ISC_LIST_UNLINK(fctx->altfinds, find, publink); 1012135446Strhodes dns_adb_destroyfind(&find); 1013135446Strhodes } 1014135446Strhodes fctx->altfind = NULL; 1015135446Strhodes} 1016135446Strhodes 1017135446Strhodesstatic void 1018135446Strhodesfctx_cleanupforwaddrs(fetchctx_t *fctx) { 1019135446Strhodes dns_adbaddrinfo_t *addr, *next_addr; 1020135446Strhodes 1021135446Strhodes REQUIRE(ISC_LIST_EMPTY(fctx->queries)); 1022135446Strhodes 1023135446Strhodes for (addr = ISC_LIST_HEAD(fctx->forwaddrs); 1024135446Strhodes addr != NULL; 1025135446Strhodes addr = next_addr) { 1026135446Strhodes next_addr = ISC_LIST_NEXT(addr, publink); 1027135446Strhodes ISC_LIST_UNLINK(fctx->forwaddrs, addr, publink); 1028135446Strhodes dns_adb_freeaddrinfo(fctx->adb, &addr); 1029135446Strhodes } 1030135446Strhodes} 1031135446Strhodes 1032135446Strhodesstatic void 1033135446Strhodesfctx_cleanupaltaddrs(fetchctx_t *fctx) { 1034135446Strhodes dns_adbaddrinfo_t *addr, *next_addr; 1035135446Strhodes 1036135446Strhodes REQUIRE(ISC_LIST_EMPTY(fctx->queries)); 1037135446Strhodes 1038135446Strhodes for (addr = ISC_LIST_HEAD(fctx->altaddrs); 1039135446Strhodes addr != NULL; 1040135446Strhodes addr = next_addr) { 1041135446Strhodes next_addr = ISC_LIST_NEXT(addr, publink); 1042135446Strhodes ISC_LIST_UNLINK(fctx->altaddrs, addr, publink); 1043135446Strhodes dns_adb_freeaddrinfo(fctx->adb, &addr); 1044135446Strhodes } 1045135446Strhodes} 1046135446Strhodes 1047135446Strhodesstatic inline void 1048135446Strhodesfctx_stopeverything(fetchctx_t *fctx, isc_boolean_t no_response) { 1049135446Strhodes FCTXTRACE("stopeverything"); 1050135446Strhodes fctx_cancelqueries(fctx, no_response); 1051135446Strhodes fctx_cleanupfinds(fctx); 1052135446Strhodes fctx_cleanupaltfinds(fctx); 1053135446Strhodes fctx_cleanupforwaddrs(fctx); 1054135446Strhodes fctx_cleanupaltaddrs(fctx); 1055135446Strhodes fctx_stoptimer(fctx); 1056135446Strhodes} 1057135446Strhodes 1058135446Strhodesstatic inline void 1059193149Sdougbfctx_sendevents(fetchctx_t *fctx, isc_result_t result, int line) { 1060135446Strhodes dns_fetchevent_t *event, *next_event; 1061135446Strhodes isc_task_t *task; 1062170222Sdougb unsigned int count = 0; 1063170222Sdougb isc_interval_t i; 1064170222Sdougb isc_boolean_t logit = ISC_FALSE; 1065193149Sdougb isc_time_t now; 1066186462Sdougb unsigned int old_spillat; 1067193149Sdougb unsigned int new_spillat = 0; /* initialized to silence 1068193149Sdougb compiler warnings */ 1069135446Strhodes 1070135446Strhodes /* 1071135446Strhodes * Caller must be holding the appropriate bucket lock. 1072135446Strhodes */ 1073135446Strhodes REQUIRE(fctx->state == fetchstate_done); 1074135446Strhodes 1075135446Strhodes FCTXTRACE("sendevents"); 1076135446Strhodes 1077193149Sdougb /* 1078193149Sdougb * Keep some record of fetch result for logging later (if required). 1079193149Sdougb */ 1080193149Sdougb fctx->result = result; 1081193149Sdougb fctx->exitline = line; 1082193149Sdougb TIME_NOW(&now); 1083193149Sdougb fctx->duration = isc_time_microdiff(&now, &fctx->start); 1084193149Sdougb 1085135446Strhodes for (event = ISC_LIST_HEAD(fctx->events); 1086135446Strhodes event != NULL; 1087135446Strhodes event = next_event) { 1088135446Strhodes next_event = ISC_LIST_NEXT(event, ev_link); 1089135446Strhodes ISC_LIST_UNLINK(fctx->events, event, ev_link); 1090135446Strhodes task = event->ev_sender; 1091135446Strhodes event->ev_sender = fctx; 1092224092Sdougb event->vresult = fctx->vresult; 1093135446Strhodes if (!HAVE_ANSWER(fctx)) 1094135446Strhodes event->result = result; 1095135446Strhodes 1096135446Strhodes INSIST(result != ISC_R_SUCCESS || 1097135446Strhodes dns_rdataset_isassociated(event->rdataset) || 1098135446Strhodes fctx->type == dns_rdatatype_any || 1099162079Sdougb fctx->type == dns_rdatatype_rrsig || 1100162079Sdougb fctx->type == dns_rdatatype_sig); 1101186462Sdougb 1102174187Sdougb /* 1103174187Sdougb * Negative results must be indicated in event->result. 1104174187Sdougb */ 1105174187Sdougb if (dns_rdataset_isassociated(event->rdataset) && 1106223812Sdougb NEGATIVE(event->rdataset)) { 1107174187Sdougb INSIST(event->result == DNS_R_NCACHENXDOMAIN || 1108174187Sdougb event->result == DNS_R_NCACHENXRRSET); 1109174187Sdougb } 1110135446Strhodes 1111275672Sdelphij event->qtotal = fctx->totalqueries; 1112135446Strhodes isc_task_sendanddetach(&task, ISC_EVENT_PTR(&event)); 1113170222Sdougb count++; 1114135446Strhodes } 1115170222Sdougb 1116170222Sdougb if ((fctx->attributes & FCTX_ATTR_HAVEANSWER) != 0 && 1117170222Sdougb fctx->spilled && 1118170222Sdougb (count < fctx->res->spillatmax || fctx->res->spillatmax == 0)) { 1119170222Sdougb LOCK(&fctx->res->lock); 1120174187Sdougb if (count == fctx->res->spillat && !fctx->res->exiting) { 1121186462Sdougb old_spillat = fctx->res->spillat; 1122170222Sdougb fctx->res->spillat += 5; 1123170222Sdougb if (fctx->res->spillat > fctx->res->spillatmax && 1124170222Sdougb fctx->res->spillatmax != 0) 1125170222Sdougb fctx->res->spillat = fctx->res->spillatmax; 1126186462Sdougb new_spillat = fctx->res->spillat; 1127186462Sdougb if (new_spillat != old_spillat) { 1128186462Sdougb logit = ISC_TRUE; 1129186462Sdougb } 1130170222Sdougb isc_interval_set(&i, 20 * 60, 0); 1131170222Sdougb result = isc_timer_reset(fctx->res->spillattimer, 1132170222Sdougb isc_timertype_ticker, NULL, 1133170222Sdougb &i, ISC_TRUE); 1134170222Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 1135170222Sdougb } 1136170222Sdougb UNLOCK(&fctx->res->lock); 1137170222Sdougb if (logit) 1138170222Sdougb isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, 1139170222Sdougb DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE, 1140170222Sdougb "clients-per-query increased to %u", 1141186462Sdougb new_spillat); 1142170222Sdougb } 1143135446Strhodes} 1144135446Strhodes 1145193149Sdougbstatic inline void 1146193149Sdougblog_edns(fetchctx_t *fctx) { 1147193149Sdougb char domainbuf[DNS_NAME_FORMATSIZE]; 1148193149Sdougb 1149193149Sdougb if (fctx->reason == NULL) 1150193149Sdougb return; 1151193149Sdougb 1152262706Serwin /* 1153262706Serwin * We do not know if fctx->domain is the actual domain the record 1154262706Serwin * lives in or a parent domain so we have a '?' after it. 1155262706Serwin */ 1156193149Sdougb dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf)); 1157193149Sdougb isc_log_write(dns_lctx, DNS_LOGCATEGORY_EDNS_DISABLED, 1158193149Sdougb DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO, 1159193149Sdougb "success resolving '%s' (in '%s'?) after %s", 1160193149Sdougb fctx->info, domainbuf, fctx->reason); 1161193149Sdougb 1162193149Sdougb fctx->reason = NULL; 1163193149Sdougb} 1164193149Sdougb 1165135446Strhodesstatic void 1166193149Sdougbfctx_done(fetchctx_t *fctx, isc_result_t result, int line) { 1167135446Strhodes dns_resolver_t *res; 1168135446Strhodes isc_boolean_t no_response; 1169135446Strhodes 1170193149Sdougb REQUIRE(line >= 0); 1171193149Sdougb 1172135446Strhodes FCTXTRACE("done"); 1173135446Strhodes 1174135446Strhodes res = fctx->res; 1175135446Strhodes 1176193149Sdougb if (result == ISC_R_SUCCESS) { 1177193149Sdougb /*% 1178193149Sdougb * Log any deferred EDNS timeout messages. 1179193149Sdougb */ 1180193149Sdougb log_edns(fctx); 1181135446Strhodes no_response = ISC_TRUE; 1182193149Sdougb } else 1183135446Strhodes no_response = ISC_FALSE; 1184193149Sdougb 1185193149Sdougb fctx->reason = NULL; 1186135446Strhodes fctx_stopeverything(fctx, no_response); 1187135446Strhodes 1188135446Strhodes LOCK(&res->buckets[fctx->bucketnum].lock); 1189135446Strhodes 1190135446Strhodes fctx->state = fetchstate_done; 1191135446Strhodes fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; 1192193149Sdougb fctx_sendevents(fctx, result, line); 1193135446Strhodes 1194135446Strhodes UNLOCK(&res->buckets[fctx->bucketnum].lock); 1195135446Strhodes} 1196135446Strhodes 1197135446Strhodesstatic void 1198186462Sdougbprocess_sendevent(resquery_t *query, isc_event_t *event) { 1199135446Strhodes isc_socketevent_t *sevent = (isc_socketevent_t *)event; 1200143731Sdougb isc_boolean_t retry = ISC_FALSE; 1201143731Sdougb isc_result_t result; 1202143731Sdougb fetchctx_t *fctx; 1203135446Strhodes 1204143731Sdougb fctx = query->fctx; 1205135446Strhodes 1206135446Strhodes if (RESQUERY_CANCELED(query)) { 1207186462Sdougb if (query->sends == 0 && query->connects == 0) { 1208135446Strhodes /* 1209135446Strhodes * This query was canceled while the 1210186462Sdougb * isc_socket_sendto/connect() was in progress. 1211135446Strhodes */ 1212135446Strhodes if (query->tcpsocket != NULL) 1213135446Strhodes isc_socket_detach(&query->tcpsocket); 1214135446Strhodes resquery_destroy(&query); 1215135446Strhodes } 1216186462Sdougb } else { 1217143731Sdougb switch (sevent->result) { 1218143731Sdougb case ISC_R_SUCCESS: 1219143731Sdougb break; 1220135446Strhodes 1221143731Sdougb case ISC_R_HOSTUNREACH: 1222143731Sdougb case ISC_R_NETUNREACH: 1223143731Sdougb case ISC_R_NOPERM: 1224143731Sdougb case ISC_R_ADDRNOTAVAIL: 1225143731Sdougb case ISC_R_CONNREFUSED: 1226143731Sdougb 1227143731Sdougb /* 1228143731Sdougb * No route to remote. 1229143731Sdougb */ 1230193149Sdougb add_bad(fctx, query->addrinfo, sevent->result, 1231193149Sdougb badns_unreachable); 1232143731Sdougb fctx_cancelquery(&query, NULL, NULL, ISC_TRUE); 1233143731Sdougb retry = ISC_TRUE; 1234143731Sdougb break; 1235143731Sdougb 1236143731Sdougb default: 1237143731Sdougb fctx_cancelquery(&query, NULL, NULL, ISC_FALSE); 1238143731Sdougb break; 1239143731Sdougb } 1240186462Sdougb } 1241143731Sdougb 1242254897Serwin if (event->ev_type == ISC_SOCKEVENT_CONNECT) 1243254897Serwin isc_event_free(&event); 1244143731Sdougb 1245143731Sdougb if (retry) { 1246143731Sdougb /* 1247143731Sdougb * Behave as if the idle timer has expired. For TCP 1248143731Sdougb * this may not actually reflect the latest timer. 1249143731Sdougb */ 1250143731Sdougb fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; 1251143731Sdougb result = fctx_stopidletimer(fctx); 1252143731Sdougb if (result != ISC_R_SUCCESS) 1253193149Sdougb fctx_done(fctx, result, __LINE__); 1254143731Sdougb else 1255205292Sdougb fctx_try(fctx, ISC_TRUE, ISC_FALSE); 1256143731Sdougb } 1257135446Strhodes} 1258135446Strhodes 1259186462Sdougbstatic void 1260186462Sdougbresquery_udpconnected(isc_task_t *task, isc_event_t *event) { 1261186462Sdougb resquery_t *query = event->ev_arg; 1262186462Sdougb 1263186462Sdougb REQUIRE(event->ev_type == ISC_SOCKEVENT_CONNECT); 1264186462Sdougb 1265186462Sdougb QTRACE("udpconnected"); 1266186462Sdougb 1267186462Sdougb UNUSED(task); 1268186462Sdougb 1269186462Sdougb INSIST(RESQUERY_CONNECTING(query)); 1270186462Sdougb 1271186462Sdougb query->connects--; 1272186462Sdougb 1273186462Sdougb process_sendevent(query, event); 1274186462Sdougb} 1275186462Sdougb 1276186462Sdougbstatic void 1277186462Sdougbresquery_senddone(isc_task_t *task, isc_event_t *event) { 1278186462Sdougb resquery_t *query = event->ev_arg; 1279186462Sdougb 1280186462Sdougb REQUIRE(event->ev_type == ISC_SOCKEVENT_SENDDONE); 1281186462Sdougb 1282186462Sdougb QTRACE("senddone"); 1283186462Sdougb 1284186462Sdougb /* 1285186462Sdougb * XXXRTH 1286186462Sdougb * 1287186462Sdougb * Currently we don't wait for the senddone event before retrying 1288186462Sdougb * a query. This means that if we get really behind, we may end 1289186462Sdougb * up doing extra work! 1290186462Sdougb */ 1291186462Sdougb 1292186462Sdougb UNUSED(task); 1293186462Sdougb 1294186462Sdougb INSIST(RESQUERY_SENDING(query)); 1295186462Sdougb 1296186462Sdougb query->sends--; 1297186462Sdougb 1298186462Sdougb process_sendevent(query, event); 1299186462Sdougb} 1300186462Sdougb 1301135446Strhodesstatic inline isc_result_t 1302193149Sdougbfctx_addopt(dns_message_t *message, unsigned int version, 1303254402Serwin isc_uint16_t udpsize, dns_ednsopt_t *ednsopts, size_t count) 1304186462Sdougb{ 1305254402Serwin dns_rdataset_t *rdataset = NULL; 1306135446Strhodes isc_result_t result; 1307135446Strhodes 1308254402Serwin result = dns_message_buildopt(message, &rdataset, version, udpsize, 1309254402Serwin DNS_MESSAGEEXTFLAG_DO, ednsopts, count); 1310135446Strhodes if (result != ISC_R_SUCCESS) 1311135446Strhodes return (result); 1312135446Strhodes return (dns_message_setopt(message, rdataset)); 1313135446Strhodes} 1314135446Strhodes 1315135446Strhodesstatic inline void 1316135446Strhodesfctx_setretryinterval(fetchctx_t *fctx, unsigned int rtt) { 1317135446Strhodes unsigned int seconds; 1318170222Sdougb unsigned int us; 1319135446Strhodes 1320135446Strhodes /* 1321193149Sdougb * We retry every .8 seconds the first two times through the address 1322135446Strhodes * list, and then we do exponential back-off. 1323135446Strhodes */ 1324135446Strhodes if (fctx->restarts < 3) 1325186462Sdougb us = 800000; 1326135446Strhodes else 1327186462Sdougb us = (800000 << (fctx->restarts - 2)); 1328135446Strhodes 1329135446Strhodes /* 1330245163Serwin * Add a fudge factor to the expected rtt based on the current 1331245163Serwin * estimate. 1332135446Strhodes */ 1333245163Serwin if (rtt < 50000) 1334245163Serwin rtt += 50000; 1335245163Serwin else if (rtt < 100000) 1336245163Serwin rtt += 100000; 1337245163Serwin else 1338245163Serwin rtt += 200000; 1339135446Strhodes 1340135446Strhodes /* 1341245163Serwin * Always wait for at least the expected rtt. 1342135446Strhodes */ 1343170222Sdougb if (us < rtt) 1344170222Sdougb us = rtt; 1345135446Strhodes 1346135446Strhodes /* 1347170222Sdougb * But don't ever wait for more than 10 seconds. 1348135446Strhodes */ 1349245163Serwin if (us > MAX_SINGLE_QUERY_TIMEOUT_US) 1350245163Serwin us = MAX_SINGLE_QUERY_TIMEOUT_US; 1351135446Strhodes 1352245163Serwin seconds = us / US_PER_SEC; 1353245163Serwin us -= seconds * US_PER_SEC; 1354170222Sdougb isc_interval_set(&fctx->interval, seconds, us * 1000); 1355135446Strhodes} 1356135446Strhodes 1357135446Strhodesstatic isc_result_t 1358135446Strhodesfctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, 1359135446Strhodes unsigned int options) 1360135446Strhodes{ 1361135446Strhodes dns_resolver_t *res; 1362135446Strhodes isc_task_t *task; 1363135446Strhodes isc_result_t result; 1364135446Strhodes resquery_t *query; 1365170222Sdougb isc_sockaddr_t addr; 1366170222Sdougb isc_boolean_t have_addr = ISC_FALSE; 1367193149Sdougb unsigned int srtt; 1368135446Strhodes 1369135446Strhodes FCTXTRACE("query"); 1370135446Strhodes 1371135446Strhodes res = fctx->res; 1372135446Strhodes task = res->buckets[fctx->bucketnum].task; 1373135446Strhodes 1374193149Sdougb srtt = addrinfo->srtt; 1375245163Serwin 1376245163Serwin /* 1377245163Serwin * A forwarder needs to make multiple queries. Give it at least 1378245163Serwin * a second to do these in. 1379245163Serwin */ 1380193149Sdougb if (ISFORWARDER(addrinfo) && srtt < 1000000) 1381193149Sdougb srtt = 1000000; 1382193149Sdougb 1383193149Sdougb fctx_setretryinterval(fctx, srtt); 1384193149Sdougb result = fctx_startidletimer(fctx, &fctx->interval); 1385135446Strhodes if (result != ISC_R_SUCCESS) 1386135446Strhodes return (result); 1387135446Strhodes 1388166332Sdougb INSIST(ISC_LIST_EMPTY(fctx->validators)); 1389166332Sdougb 1390135446Strhodes dns_message_reset(fctx->rmessage, DNS_MESSAGE_INTENTPARSE); 1391135446Strhodes 1392236374Sdougb query = isc_mem_get(fctx->mctx, sizeof(*query)); 1393135446Strhodes if (query == NULL) { 1394135446Strhodes result = ISC_R_NOMEMORY; 1395135446Strhodes goto stop_idle_timer; 1396135446Strhodes } 1397236374Sdougb query->mctx = fctx->mctx; 1398135446Strhodes query->options = options; 1399135446Strhodes query->attributes = 0; 1400135446Strhodes query->sends = 0; 1401135446Strhodes query->connects = 0; 1402135446Strhodes /* 1403135446Strhodes * Note that the caller MUST guarantee that 'addrinfo' will remain 1404135446Strhodes * valid until this query is canceled. 1405135446Strhodes */ 1406135446Strhodes query->addrinfo = addrinfo; 1407135446Strhodes TIME_NOW(&query->start); 1408135446Strhodes 1409135446Strhodes /* 1410135446Strhodes * If this is a TCP query, then we need to make a socket and 1411135446Strhodes * a dispatch for it here. Otherwise we use the resolver's 1412135446Strhodes * shared dispatch. 1413135446Strhodes */ 1414135446Strhodes query->dispatchmgr = res->dispatchmgr; 1415135446Strhodes query->dispatch = NULL; 1416186462Sdougb query->exclusivesocket = ISC_FALSE; 1417135446Strhodes query->tcpsocket = NULL; 1418170222Sdougb if (res->view->peers != NULL) { 1419170222Sdougb dns_peer_t *peer = NULL; 1420170222Sdougb isc_netaddr_t dstip; 1421170222Sdougb isc_netaddr_fromsockaddr(&dstip, &addrinfo->sockaddr); 1422170222Sdougb result = dns_peerlist_peerbyaddr(res->view->peers, 1423174187Sdougb &dstip, &peer); 1424170222Sdougb if (result == ISC_R_SUCCESS) { 1425170222Sdougb result = dns_peer_getquerysource(peer, &addr); 1426170222Sdougb if (result == ISC_R_SUCCESS) 1427170222Sdougb have_addr = ISC_TRUE; 1428170222Sdougb } 1429170222Sdougb } 1430170222Sdougb 1431135446Strhodes if ((query->options & DNS_FETCHOPT_TCP) != 0) { 1432135446Strhodes int pf; 1433135446Strhodes 1434135446Strhodes pf = isc_sockaddr_pf(&addrinfo->sockaddr); 1435170222Sdougb if (!have_addr) { 1436170222Sdougb switch (pf) { 1437170222Sdougb case PF_INET: 1438254897Serwin result = dns_dispatch_getlocaladdress( 1439254897Serwin res->dispatches4->dispatches[0], 1440254897Serwin &addr); 1441170222Sdougb break; 1442170222Sdougb case PF_INET6: 1443254897Serwin result = dns_dispatch_getlocaladdress( 1444254897Serwin res->dispatches6->dispatches[0], 1445254897Serwin &addr); 1446170222Sdougb break; 1447170222Sdougb default: 1448170222Sdougb result = ISC_R_NOTIMPLEMENTED; 1449170222Sdougb break; 1450170222Sdougb } 1451170222Sdougb if (result != ISC_R_SUCCESS) 1452170222Sdougb goto cleanup_query; 1453135446Strhodes } 1454135446Strhodes isc_sockaddr_setport(&addr, 0); 1455135446Strhodes 1456135446Strhodes result = isc_socket_create(res->socketmgr, pf, 1457135446Strhodes isc_sockettype_tcp, 1458135446Strhodes &query->tcpsocket); 1459135446Strhodes if (result != ISC_R_SUCCESS) 1460135446Strhodes goto cleanup_query; 1461135446Strhodes 1462165071Sdougb#ifndef BROKEN_TCP_BIND_BEFORE_CONNECT 1463182645Sdougb result = isc_socket_bind(query->tcpsocket, &addr, 0); 1464135446Strhodes if (result != ISC_R_SUCCESS) 1465135446Strhodes goto cleanup_socket; 1466165071Sdougb#endif 1467135446Strhodes 1468135446Strhodes /* 1469135446Strhodes * A dispatch will be created once the connect succeeds. 1470135446Strhodes */ 1471135446Strhodes } else { 1472170222Sdougb if (have_addr) { 1473170222Sdougb unsigned int attrs, attrmask; 1474170222Sdougb attrs = DNS_DISPATCHATTR_UDP; 1475170222Sdougb switch (isc_sockaddr_pf(&addr)) { 1476170222Sdougb case AF_INET: 1477170222Sdougb attrs |= DNS_DISPATCHATTR_IPV4; 1478170222Sdougb break; 1479170222Sdougb case AF_INET6: 1480170222Sdougb attrs |= DNS_DISPATCHATTR_IPV6; 1481170222Sdougb break; 1482170222Sdougb default: 1483170222Sdougb result = ISC_R_NOTIMPLEMENTED; 1484170222Sdougb goto cleanup_query; 1485170222Sdougb } 1486170222Sdougb attrmask = DNS_DISPATCHATTR_UDP; 1487170222Sdougb attrmask |= DNS_DISPATCHATTR_TCP; 1488170222Sdougb attrmask |= DNS_DISPATCHATTR_IPV4; 1489170222Sdougb attrmask |= DNS_DISPATCHATTR_IPV6; 1490170222Sdougb result = dns_dispatch_getudp(res->dispatchmgr, 1491170222Sdougb res->socketmgr, 1492170222Sdougb res->taskmgr, &addr, 1493170222Sdougb 4096, 1000, 32768, 16411, 1494170222Sdougb 16433, attrs, attrmask, 1495170222Sdougb &query->dispatch); 1496170222Sdougb if (result != ISC_R_SUCCESS) 1497170222Sdougb goto cleanup_query; 1498170222Sdougb } else { 1499170222Sdougb switch (isc_sockaddr_pf(&addrinfo->sockaddr)) { 1500186462Sdougb case PF_INET: 1501254897Serwin dns_dispatch_attach( 1502254897Serwin dns_resolver_dispatchv4(res), 1503254897Serwin &query->dispatch); 1504186462Sdougb query->exclusivesocket = res->exclusivev4; 1505170222Sdougb break; 1506186462Sdougb case PF_INET6: 1507254897Serwin dns_dispatch_attach( 1508254897Serwin dns_resolver_dispatchv6(res), 1509254897Serwin &query->dispatch); 1510186462Sdougb query->exclusivesocket = res->exclusivev6; 1511170222Sdougb break; 1512170222Sdougb default: 1513170222Sdougb result = ISC_R_NOTIMPLEMENTED; 1514170222Sdougb goto cleanup_query; 1515170222Sdougb } 1516135446Strhodes } 1517135446Strhodes /* 1518135446Strhodes * We should always have a valid dispatcher here. If we 1519135446Strhodes * don't support a protocol family, then its dispatcher 1520135446Strhodes * will be NULL, but we shouldn't be finding addresses for 1521135446Strhodes * protocol types we don't support, so the dispatcher 1522135446Strhodes * we found should never be NULL. 1523135446Strhodes */ 1524135446Strhodes INSIST(query->dispatch != NULL); 1525135446Strhodes } 1526135446Strhodes 1527135446Strhodes query->dispentry = NULL; 1528135446Strhodes query->fctx = fctx; 1529135446Strhodes query->tsig = NULL; 1530135446Strhodes query->tsigkey = NULL; 1531135446Strhodes ISC_LINK_INIT(query, link); 1532135446Strhodes query->magic = QUERY_MAGIC; 1533135446Strhodes 1534135446Strhodes if ((query->options & DNS_FETCHOPT_TCP) != 0) { 1535135446Strhodes /* 1536135446Strhodes * Connect to the remote server. 1537135446Strhodes * 1538135446Strhodes * XXXRTH Should we attach to the socket? 1539135446Strhodes */ 1540135446Strhodes result = isc_socket_connect(query->tcpsocket, 1541135446Strhodes &addrinfo->sockaddr, task, 1542135446Strhodes resquery_connected, query); 1543135446Strhodes if (result != ISC_R_SUCCESS) 1544135446Strhodes goto cleanup_socket; 1545135446Strhodes query->connects++; 1546135446Strhodes QTRACE("connecting via TCP"); 1547135446Strhodes } else { 1548135446Strhodes result = resquery_send(query); 1549135446Strhodes if (result != ISC_R_SUCCESS) 1550135446Strhodes goto cleanup_dispatch; 1551135446Strhodes } 1552275672Sdelphij 1553193149Sdougb fctx->querysent++; 1554275672Sdelphij fctx->totalqueries++; 1555135446Strhodes 1556135446Strhodes ISC_LIST_APPEND(fctx->queries, query, link); 1557166332Sdougb query->fctx->nqueries++; 1558193149Sdougb if (isc_sockaddr_pf(&addrinfo->sockaddr) == PF_INET) 1559193149Sdougb inc_stats(res, dns_resstatscounter_queryv4); 1560193149Sdougb else 1561193149Sdougb inc_stats(res, dns_resstatscounter_queryv6); 1562193149Sdougb if (res->view->resquerystats != NULL) 1563193149Sdougb dns_rdatatypestats_increment(res->view->resquerystats, 1564193149Sdougb fctx->type); 1565135446Strhodes 1566135446Strhodes return (ISC_R_SUCCESS); 1567135446Strhodes 1568135446Strhodes cleanup_socket: 1569135446Strhodes isc_socket_detach(&query->tcpsocket); 1570135446Strhodes 1571135446Strhodes cleanup_dispatch: 1572135446Strhodes if (query->dispatch != NULL) 1573135446Strhodes dns_dispatch_detach(&query->dispatch); 1574135446Strhodes 1575135446Strhodes cleanup_query: 1576234010Sdougb if (query->connects == 0) { 1577234010Sdougb query->magic = 0; 1578236374Sdougb isc_mem_put(fctx->mctx, query, sizeof(*query)); 1579234010Sdougb } 1580135446Strhodes 1581135446Strhodes stop_idle_timer: 1582135446Strhodes RUNTIME_CHECK(fctx_stopidletimer(fctx) == ISC_R_SUCCESS); 1583135446Strhodes 1584135446Strhodes return (result); 1585135446Strhodes} 1586135446Strhodes 1587170222Sdougbstatic isc_boolean_t 1588218384Sdougbbad_edns(fetchctx_t *fctx, isc_sockaddr_t *address) { 1589218384Sdougb isc_sockaddr_t *sa; 1590218384Sdougb 1591218384Sdougb for (sa = ISC_LIST_HEAD(fctx->bad_edns); 1592218384Sdougb sa != NULL; 1593218384Sdougb sa = ISC_LIST_NEXT(sa, link)) { 1594218384Sdougb if (isc_sockaddr_equal(sa, address)) 1595218384Sdougb return (ISC_TRUE); 1596218384Sdougb } 1597218384Sdougb 1598218384Sdougb return (ISC_FALSE); 1599218384Sdougb} 1600218384Sdougb 1601218384Sdougbstatic void 1602218384Sdougbadd_bad_edns(fetchctx_t *fctx, isc_sockaddr_t *address) { 1603218384Sdougb isc_sockaddr_t *sa; 1604218384Sdougb 1605218384Sdougb if (bad_edns(fctx, address)) 1606218384Sdougb return; 1607218384Sdougb 1608236374Sdougb sa = isc_mem_get(fctx->mctx, sizeof(*sa)); 1609218384Sdougb if (sa == NULL) 1610218384Sdougb return; 1611218384Sdougb 1612218384Sdougb *sa = *address; 1613218384Sdougb ISC_LIST_INITANDAPPEND(fctx->bad_edns, sa, link); 1614218384Sdougb} 1615218384Sdougb 1616218384Sdougbstatic isc_boolean_t 1617170222Sdougbtriededns(fetchctx_t *fctx, isc_sockaddr_t *address) { 1618170222Sdougb isc_sockaddr_t *sa; 1619170222Sdougb 1620170222Sdougb for (sa = ISC_LIST_HEAD(fctx->edns); 1621170222Sdougb sa != NULL; 1622170222Sdougb sa = ISC_LIST_NEXT(sa, link)) { 1623170222Sdougb if (isc_sockaddr_equal(sa, address)) 1624170222Sdougb return (ISC_TRUE); 1625170222Sdougb } 1626170222Sdougb 1627170222Sdougb return (ISC_FALSE); 1628170222Sdougb} 1629170222Sdougb 1630170222Sdougbstatic void 1631170222Sdougbadd_triededns(fetchctx_t *fctx, isc_sockaddr_t *address) { 1632170222Sdougb isc_sockaddr_t *sa; 1633170222Sdougb 1634170222Sdougb if (triededns(fctx, address)) 1635170222Sdougb return; 1636170222Sdougb 1637236374Sdougb sa = isc_mem_get(fctx->mctx, sizeof(*sa)); 1638170222Sdougb if (sa == NULL) 1639170222Sdougb return; 1640170222Sdougb 1641170222Sdougb *sa = *address; 1642170222Sdougb ISC_LIST_INITANDAPPEND(fctx->edns, sa, link); 1643170222Sdougb} 1644170222Sdougb 1645170222Sdougbstatic isc_boolean_t 1646170222Sdougbtriededns512(fetchctx_t *fctx, isc_sockaddr_t *address) { 1647170222Sdougb isc_sockaddr_t *sa; 1648170222Sdougb 1649170222Sdougb for (sa = ISC_LIST_HEAD(fctx->edns512); 1650170222Sdougb sa != NULL; 1651170222Sdougb sa = ISC_LIST_NEXT(sa, link)) { 1652170222Sdougb if (isc_sockaddr_equal(sa, address)) 1653170222Sdougb return (ISC_TRUE); 1654170222Sdougb } 1655170222Sdougb 1656170222Sdougb return (ISC_FALSE); 1657170222Sdougb} 1658170222Sdougb 1659170222Sdougbstatic void 1660170222Sdougbadd_triededns512(fetchctx_t *fctx, isc_sockaddr_t *address) { 1661170222Sdougb isc_sockaddr_t *sa; 1662170222Sdougb 1663170222Sdougb if (triededns512(fctx, address)) 1664170222Sdougb return; 1665170222Sdougb 1666236374Sdougb sa = isc_mem_get(fctx->mctx, sizeof(*sa)); 1667170222Sdougb if (sa == NULL) 1668170222Sdougb return; 1669170222Sdougb 1670170222Sdougb *sa = *address; 1671170222Sdougb ISC_LIST_INITANDAPPEND(fctx->edns512, sa, link); 1672170222Sdougb} 1673170222Sdougb 1674135446Strhodesstatic isc_result_t 1675135446Strhodesresquery_send(resquery_t *query) { 1676135446Strhodes fetchctx_t *fctx; 1677135446Strhodes isc_result_t result; 1678135446Strhodes dns_name_t *qname = NULL; 1679135446Strhodes dns_rdataset_t *qrdataset = NULL; 1680135446Strhodes isc_region_t r; 1681135446Strhodes dns_resolver_t *res; 1682135446Strhodes isc_task_t *task; 1683135446Strhodes isc_socket_t *socket; 1684135446Strhodes isc_buffer_t tcpbuffer; 1685135446Strhodes isc_sockaddr_t *address; 1686135446Strhodes isc_buffer_t *buffer; 1687135446Strhodes isc_netaddr_t ipaddr; 1688135446Strhodes dns_tsigkey_t *tsigkey = NULL; 1689135446Strhodes dns_peer_t *peer = NULL; 1690135446Strhodes isc_boolean_t useedns; 1691135446Strhodes dns_compress_t cctx; 1692135446Strhodes isc_boolean_t cleanup_cctx = ISC_FALSE; 1693135446Strhodes isc_boolean_t secure_domain; 1694234010Sdougb isc_boolean_t connecting = ISC_FALSE; 1695254402Serwin dns_ednsopt_t ednsopts[EDNSOPTS]; 1696254402Serwin unsigned ednsopt = 0; 1697135446Strhodes 1698135446Strhodes fctx = query->fctx; 1699135446Strhodes QTRACE("send"); 1700135446Strhodes 1701135446Strhodes res = fctx->res; 1702135446Strhodes task = res->buckets[fctx->bucketnum].task; 1703135446Strhodes address = NULL; 1704135446Strhodes 1705135446Strhodes if ((query->options & DNS_FETCHOPT_TCP) != 0) { 1706135446Strhodes /* 1707135446Strhodes * Reserve space for the TCP message length. 1708135446Strhodes */ 1709135446Strhodes isc_buffer_init(&tcpbuffer, query->data, sizeof(query->data)); 1710135446Strhodes isc_buffer_init(&query->buffer, query->data + 2, 1711135446Strhodes sizeof(query->data) - 2); 1712135446Strhodes buffer = &tcpbuffer; 1713135446Strhodes } else { 1714135446Strhodes isc_buffer_init(&query->buffer, query->data, 1715135446Strhodes sizeof(query->data)); 1716135446Strhodes buffer = &query->buffer; 1717135446Strhodes } 1718135446Strhodes 1719135446Strhodes result = dns_message_gettempname(fctx->qmessage, &qname); 1720135446Strhodes if (result != ISC_R_SUCCESS) 1721135446Strhodes goto cleanup_temps; 1722135446Strhodes result = dns_message_gettemprdataset(fctx->qmessage, &qrdataset); 1723135446Strhodes if (result != ISC_R_SUCCESS) 1724135446Strhodes goto cleanup_temps; 1725135446Strhodes 1726135446Strhodes /* 1727135446Strhodes * Get a query id from the dispatch. 1728135446Strhodes */ 1729186462Sdougb result = dns_dispatch_addresponse2(query->dispatch, 1730186462Sdougb &query->addrinfo->sockaddr, 1731186462Sdougb task, 1732186462Sdougb resquery_response, 1733186462Sdougb query, 1734186462Sdougb &query->id, 1735186462Sdougb &query->dispentry, 1736186462Sdougb res->socketmgr); 1737135446Strhodes if (result != ISC_R_SUCCESS) 1738135446Strhodes goto cleanup_temps; 1739135446Strhodes 1740135446Strhodes fctx->qmessage->opcode = dns_opcode_query; 1741135446Strhodes 1742135446Strhodes /* 1743135446Strhodes * Set up question. 1744135446Strhodes */ 1745135446Strhodes dns_name_init(qname, NULL); 1746135446Strhodes dns_name_clone(&fctx->name, qname); 1747135446Strhodes dns_rdataset_init(qrdataset); 1748135446Strhodes dns_rdataset_makequestion(qrdataset, res->rdclass, fctx->type); 1749135446Strhodes ISC_LIST_APPEND(qname->list, qrdataset, link); 1750135446Strhodes dns_message_addname(fctx->qmessage, qname, DNS_SECTION_QUESTION); 1751135446Strhodes qname = NULL; 1752135446Strhodes qrdataset = NULL; 1753135446Strhodes 1754135446Strhodes /* 1755135446Strhodes * Set RD if the client has requested that we do a recursive query, 1756135446Strhodes * or if we're sending to a forwarder. 1757135446Strhodes */ 1758135446Strhodes if ((query->options & DNS_FETCHOPT_RECURSIVE) != 0 || 1759135446Strhodes ISFORWARDER(query->addrinfo)) 1760135446Strhodes fctx->qmessage->flags |= DNS_MESSAGEFLAG_RD; 1761135446Strhodes 1762135446Strhodes /* 1763135446Strhodes * Set CD if the client says don't validate or the question is 1764135446Strhodes * under a secure entry point. 1765135446Strhodes */ 1766170222Sdougb if ((query->options & DNS_FETCHOPT_NOVALIDATE) != 0) { 1767170222Sdougb fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD; 1768170222Sdougb } else if (res->view->enablevalidation) { 1769224092Sdougb result = dns_view_issecuredomain(res->view, &fctx->name, 1770224092Sdougb &secure_domain); 1771135446Strhodes if (result != ISC_R_SUCCESS) 1772135446Strhodes secure_domain = ISC_FALSE; 1773135446Strhodes if (res->view->dlv != NULL) 1774135446Strhodes secure_domain = ISC_TRUE; 1775135446Strhodes if (secure_domain) 1776135446Strhodes fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD; 1777170222Sdougb } 1778135446Strhodes 1779135446Strhodes /* 1780135446Strhodes * We don't have to set opcode because it defaults to query. 1781135446Strhodes */ 1782135446Strhodes fctx->qmessage->id = query->id; 1783135446Strhodes 1784135446Strhodes /* 1785135446Strhodes * Convert the question to wire format. 1786135446Strhodes */ 1787135446Strhodes result = dns_compress_init(&cctx, -1, fctx->res->mctx); 1788135446Strhodes if (result != ISC_R_SUCCESS) 1789135446Strhodes goto cleanup_message; 1790135446Strhodes cleanup_cctx = ISC_TRUE; 1791135446Strhodes 1792135446Strhodes result = dns_message_renderbegin(fctx->qmessage, &cctx, 1793135446Strhodes &query->buffer); 1794135446Strhodes if (result != ISC_R_SUCCESS) 1795135446Strhodes goto cleanup_message; 1796135446Strhodes 1797135446Strhodes result = dns_message_rendersection(fctx->qmessage, 1798135446Strhodes DNS_SECTION_QUESTION, 0); 1799135446Strhodes if (result != ISC_R_SUCCESS) 1800135446Strhodes goto cleanup_message; 1801135446Strhodes 1802135446Strhodes peer = NULL; 1803135446Strhodes isc_netaddr_fromsockaddr(&ipaddr, &query->addrinfo->sockaddr); 1804135446Strhodes (void) dns_peerlist_peerbyaddr(fctx->res->view->peers, &ipaddr, &peer); 1805135446Strhodes 1806135446Strhodes /* 1807135446Strhodes * The ADB does not know about servers with "edns no". Check this, 1808135446Strhodes * and then inform the ADB for future use. 1809135446Strhodes */ 1810135446Strhodes if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) == 0 && 1811135446Strhodes peer != NULL && 1812135446Strhodes dns_peer_getsupportedns(peer, &useedns) == ISC_R_SUCCESS && 1813135446Strhodes !useedns) 1814135446Strhodes { 1815135446Strhodes query->options |= DNS_FETCHOPT_NOEDNS0; 1816193149Sdougb dns_adb_changeflags(fctx->adb, query->addrinfo, 1817135446Strhodes DNS_FETCHOPT_NOEDNS0, 1818135446Strhodes DNS_FETCHOPT_NOEDNS0); 1819135446Strhodes } 1820135446Strhodes 1821193149Sdougb /* Sync NOEDNS0 flag in addrinfo->flags and options now. */ 1822193149Sdougb if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) != 0) 1823193149Sdougb query->options |= DNS_FETCHOPT_NOEDNS0; 1824193149Sdougb 1825135446Strhodes /* 1826193149Sdougb * Handle timeouts by reducing the UDP response size to 512 bytes 1827193149Sdougb * then if that doesn't work disabling EDNS (includes DO) and CD. 1828193149Sdougb * 1829193149Sdougb * These timeout can be due to: 1830193149Sdougb * * broken nameservers that don't respond to EDNS queries. 1831193149Sdougb * * broken/misconfigured firewalls and NAT implementations 1832193149Sdougb * that don't handle IP fragmentation. 1833193149Sdougb * * broken/misconfigured firewalls that don't handle responses 1834193149Sdougb * greater than 512 bytes. 1835193149Sdougb * * broken/misconfigured firewalls that don't handle EDNS, DO 1836193149Sdougb * or CD. 1837193149Sdougb * * packet loss / link outage. 1838193149Sdougb */ 1839193149Sdougb if (fctx->timeout) { 1840193149Sdougb if ((triededns512(fctx, &query->addrinfo->sockaddr) || 1841193149Sdougb fctx->timeouts >= (MAX_EDNS0_TIMEOUTS * 2)) && 1842193149Sdougb (query->options & DNS_FETCHOPT_NOEDNS0) == 0) { 1843193149Sdougb query->options |= DNS_FETCHOPT_NOEDNS0; 1844193149Sdougb fctx->reason = "disabling EDNS"; 1845193149Sdougb } else if ((triededns(fctx, &query->addrinfo->sockaddr) || 1846193149Sdougb fctx->timeouts >= MAX_EDNS0_TIMEOUTS) && 1847193149Sdougb (query->options & DNS_FETCHOPT_NOEDNS0) == 0) { 1848193149Sdougb query->options |= DNS_FETCHOPT_EDNS512; 1849193149Sdougb fctx->reason = "reducing the advertised EDNS UDP " 1850193149Sdougb "packet size to 512 octets"; 1851193149Sdougb } 1852193149Sdougb fctx->timeout = ISC_FALSE; 1853193149Sdougb } 1854193149Sdougb 1855193149Sdougb /* 1856135446Strhodes * Use EDNS0, unless the caller doesn't want it, or we know that 1857135446Strhodes * the remote server doesn't like it. 1858135446Strhodes */ 1859135446Strhodes if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) { 1860135446Strhodes if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) == 0) { 1861193149Sdougb unsigned int version = 0; /* Default version. */ 1862170222Sdougb unsigned int flags; 1863170222Sdougb isc_uint16_t udpsize = res->udpsize; 1864193149Sdougb isc_boolean_t reqnsid = res->view->requestnsid; 1865170222Sdougb 1866170222Sdougb flags = query->addrinfo->flags; 1867170222Sdougb if ((flags & DNS_FETCHOPT_EDNSVERSIONSET) != 0) { 1868170222Sdougb version = flags & DNS_FETCHOPT_EDNSVERSIONMASK; 1869170222Sdougb version >>= DNS_FETCHOPT_EDNSVERSIONSHIFT; 1870170222Sdougb } 1871170222Sdougb if ((query->options & DNS_FETCHOPT_EDNS512) != 0) 1872170222Sdougb udpsize = 512; 1873170222Sdougb else if (peer != NULL) 1874170222Sdougb (void)dns_peer_getudpsize(peer, &udpsize); 1875193149Sdougb 1876193149Sdougb /* request NSID for current view or peer? */ 1877193149Sdougb if (peer != NULL) 1878193149Sdougb (void) dns_peer_getrequestnsid(peer, &reqnsid); 1879254402Serwin if (reqnsid) { 1880254402Serwin INSIST(ednsopt < EDNSOPTS); 1881254402Serwin ednsopts[ednsopt].code = DNS_OPT_NSID; 1882254402Serwin ednsopts[ednsopt].length = 0; 1883254402Serwin ednsopts[ednsopt].value = NULL; 1884254402Serwin ednsopt++; 1885254402Serwin } 1886193149Sdougb result = fctx_addopt(fctx->qmessage, version, 1887254402Serwin udpsize, ednsopts, ednsopt); 1888193149Sdougb if (reqnsid && result == ISC_R_SUCCESS) { 1889193149Sdougb query->options |= DNS_FETCHOPT_WANTNSID; 1890193149Sdougb } else if (result != ISC_R_SUCCESS) { 1891135446Strhodes /* 1892135446Strhodes * We couldn't add the OPT, but we'll press on. 1893135446Strhodes * We're not using EDNS0, so set the NOEDNS0 1894135446Strhodes * bit. 1895135446Strhodes */ 1896135446Strhodes query->options |= DNS_FETCHOPT_NOEDNS0; 1897135446Strhodes } 1898135446Strhodes } else { 1899135446Strhodes /* 1900135446Strhodes * We know this server doesn't like EDNS0, so we 1901135446Strhodes * won't use it. Set the NOEDNS0 bit since we're 1902135446Strhodes * not using EDNS0. 1903135446Strhodes */ 1904135446Strhodes query->options |= DNS_FETCHOPT_NOEDNS0; 1905135446Strhodes } 1906135446Strhodes } 1907135446Strhodes 1908135446Strhodes /* 1909135446Strhodes * If we need EDNS0 to do this query and aren't using it, we lose. 1910135446Strhodes */ 1911135446Strhodes if (NEEDEDNS0(fctx) && (query->options & DNS_FETCHOPT_NOEDNS0) != 0) { 1912135446Strhodes result = DNS_R_SERVFAIL; 1913135446Strhodes goto cleanup_message; 1914135446Strhodes } 1915135446Strhodes 1916170222Sdougb if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) 1917170222Sdougb add_triededns(fctx, &query->addrinfo->sockaddr); 1918170222Sdougb 1919170222Sdougb if ((query->options & DNS_FETCHOPT_EDNS512) != 0) 1920170222Sdougb add_triededns512(fctx, &query->addrinfo->sockaddr); 1921170222Sdougb 1922135446Strhodes /* 1923165071Sdougb * Clear CD if EDNS is not in use. 1924165071Sdougb */ 1925165071Sdougb if ((query->options & DNS_FETCHOPT_NOEDNS0) != 0) 1926165071Sdougb fctx->qmessage->flags &= ~DNS_MESSAGEFLAG_CD; 1927165071Sdougb 1928165071Sdougb /* 1929135446Strhodes * Add TSIG record tailored to the current recipient. 1930135446Strhodes */ 1931135446Strhodes result = dns_view_getpeertsig(fctx->res->view, &ipaddr, &tsigkey); 1932135446Strhodes if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) 1933135446Strhodes goto cleanup_message; 1934135446Strhodes 1935135446Strhodes if (tsigkey != NULL) { 1936135446Strhodes result = dns_message_settsigkey(fctx->qmessage, tsigkey); 1937135446Strhodes dns_tsigkey_detach(&tsigkey); 1938135446Strhodes if (result != ISC_R_SUCCESS) 1939135446Strhodes goto cleanup_message; 1940135446Strhodes } 1941135446Strhodes 1942135446Strhodes result = dns_message_rendersection(fctx->qmessage, 1943135446Strhodes DNS_SECTION_ADDITIONAL, 0); 1944135446Strhodes if (result != ISC_R_SUCCESS) 1945135446Strhodes goto cleanup_message; 1946135446Strhodes 1947135446Strhodes result = dns_message_renderend(fctx->qmessage); 1948135446Strhodes if (result != ISC_R_SUCCESS) 1949135446Strhodes goto cleanup_message; 1950135446Strhodes 1951135446Strhodes dns_compress_invalidate(&cctx); 1952135446Strhodes cleanup_cctx = ISC_FALSE; 1953135446Strhodes 1954135446Strhodes if (dns_message_gettsigkey(fctx->qmessage) != NULL) { 1955135446Strhodes dns_tsigkey_attach(dns_message_gettsigkey(fctx->qmessage), 1956135446Strhodes &query->tsigkey); 1957135446Strhodes result = dns_message_getquerytsig(fctx->qmessage, 1958135446Strhodes fctx->res->mctx, 1959135446Strhodes &query->tsig); 1960135446Strhodes if (result != ISC_R_SUCCESS) 1961135446Strhodes goto cleanup_message; 1962135446Strhodes } 1963135446Strhodes 1964135446Strhodes /* 1965135446Strhodes * If using TCP, write the length of the message at the beginning 1966135446Strhodes * of the buffer. 1967135446Strhodes */ 1968135446Strhodes if ((query->options & DNS_FETCHOPT_TCP) != 0) { 1969135446Strhodes isc_buffer_usedregion(&query->buffer, &r); 1970135446Strhodes isc_buffer_putuint16(&tcpbuffer, (isc_uint16_t)r.length); 1971135446Strhodes isc_buffer_add(&tcpbuffer, r.length); 1972135446Strhodes } 1973135446Strhodes 1974135446Strhodes /* 1975135446Strhodes * We're now done with the query message. 1976135446Strhodes */ 1977135446Strhodes dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER); 1978135446Strhodes 1979186462Sdougb if (query->exclusivesocket) 1980186462Sdougb socket = dns_dispatch_getentrysocket(query->dispentry); 1981186462Sdougb else 1982186462Sdougb socket = dns_dispatch_getsocket(query->dispatch); 1983135446Strhodes /* 1984135446Strhodes * Send the query! 1985135446Strhodes */ 1986186462Sdougb if ((query->options & DNS_FETCHOPT_TCP) == 0) { 1987135446Strhodes address = &query->addrinfo->sockaddr; 1988186462Sdougb if (query->exclusivesocket) { 1989186462Sdougb result = isc_socket_connect(socket, address, task, 1990186462Sdougb resquery_udpconnected, 1991186462Sdougb query); 1992186462Sdougb if (result != ISC_R_SUCCESS) 1993186462Sdougb goto cleanup_message; 1994234010Sdougb connecting = ISC_TRUE; 1995186462Sdougb query->connects++; 1996186462Sdougb } 1997186462Sdougb } 1998135446Strhodes isc_buffer_usedregion(buffer, &r); 1999135446Strhodes 2000135446Strhodes /* 2001135446Strhodes * XXXRTH Make sure we don't send to ourselves! We should probably 2002193149Sdougb * prune out these addresses when we get them from the ADB. 2003135446Strhodes */ 2004254897Serwin ISC_EVENT_INIT(&query->sendevent, sizeof(query->sendevent), 0, NULL, 2005254897Serwin ISC_SOCKEVENT_SENDDONE, resquery_senddone, query, 2006254897Serwin NULL, NULL, NULL); 2007254897Serwin result = isc_socket_sendto2(socket, &r, task, address, NULL, 2008254897Serwin &query->sendevent, 0); 2009234010Sdougb if (result != ISC_R_SUCCESS) { 2010234010Sdougb if (connecting) { 2011234010Sdougb /* 2012234010Sdougb * This query is still connecting. 2013234010Sdougb * Mark it as canceled so that it will just be 2014234010Sdougb * cleaned up when the connected event is received. 2015234010Sdougb * Keep fctx around until the event is processed. 2016234010Sdougb */ 2017234010Sdougb query->fctx->nqueries++; 2018234010Sdougb query->attributes |= RESQUERY_ATTR_CANCELED; 2019234010Sdougb } 2020135446Strhodes goto cleanup_message; 2021234010Sdougb } 2022193149Sdougb 2023135446Strhodes query->sends++; 2024193149Sdougb 2025135446Strhodes QTRACE("sent"); 2026135446Strhodes 2027135446Strhodes return (ISC_R_SUCCESS); 2028135446Strhodes 2029135446Strhodes cleanup_message: 2030135446Strhodes if (cleanup_cctx) 2031135446Strhodes dns_compress_invalidate(&cctx); 2032135446Strhodes 2033135446Strhodes dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER); 2034135446Strhodes 2035135446Strhodes /* 2036135446Strhodes * Stop the dispatcher from listening. 2037135446Strhodes */ 2038135446Strhodes dns_dispatch_removeresponse(&query->dispentry, NULL); 2039135446Strhodes 2040135446Strhodes cleanup_temps: 2041135446Strhodes if (qname != NULL) 2042135446Strhodes dns_message_puttempname(fctx->qmessage, &qname); 2043135446Strhodes if (qrdataset != NULL) 2044135446Strhodes dns_message_puttemprdataset(fctx->qmessage, &qrdataset); 2045135446Strhodes 2046135446Strhodes return (result); 2047135446Strhodes} 2048135446Strhodes 2049135446Strhodesstatic void 2050135446Strhodesresquery_connected(isc_task_t *task, isc_event_t *event) { 2051135446Strhodes isc_socketevent_t *sevent = (isc_socketevent_t *)event; 2052135446Strhodes resquery_t *query = event->ev_arg; 2053143731Sdougb isc_boolean_t retry = ISC_FALSE; 2054193149Sdougb isc_interval_t interval; 2055135446Strhodes isc_result_t result; 2056143731Sdougb unsigned int attrs; 2057143731Sdougb fetchctx_t *fctx; 2058135446Strhodes 2059135446Strhodes REQUIRE(event->ev_type == ISC_SOCKEVENT_CONNECT); 2060135446Strhodes REQUIRE(VALID_QUERY(query)); 2061135446Strhodes 2062135446Strhodes QTRACE("connected"); 2063135446Strhodes 2064135446Strhodes UNUSED(task); 2065135446Strhodes 2066135446Strhodes /* 2067135446Strhodes * XXXRTH 2068135446Strhodes * 2069135446Strhodes * Currently we don't wait for the connect event before retrying 2070135446Strhodes * a query. This means that if we get really behind, we may end 2071135446Strhodes * up doing extra work! 2072135446Strhodes */ 2073135446Strhodes 2074135446Strhodes query->connects--; 2075143731Sdougb fctx = query->fctx; 2076135446Strhodes 2077135446Strhodes if (RESQUERY_CANCELED(query)) { 2078135446Strhodes /* 2079135446Strhodes * This query was canceled while the connect() was in 2080135446Strhodes * progress. 2081135446Strhodes */ 2082135446Strhodes isc_socket_detach(&query->tcpsocket); 2083135446Strhodes resquery_destroy(&query); 2084135446Strhodes } else { 2085143731Sdougb switch (sevent->result) { 2086143731Sdougb case ISC_R_SUCCESS: 2087193149Sdougb 2088135446Strhodes /* 2089193149Sdougb * Extend the idle timer for TCP. 20 seconds 2090193149Sdougb * should be long enough for a TCP connection to be 2091193149Sdougb * established, a single DNS request to be sent, 2092193149Sdougb * and the response received. 2093193149Sdougb */ 2094193149Sdougb isc_interval_set(&interval, 20, 0); 2095193149Sdougb result = fctx_startidletimer(query->fctx, &interval); 2096193149Sdougb if (result != ISC_R_SUCCESS) { 2097193149Sdougb fctx_cancelquery(&query, NULL, NULL, ISC_FALSE); 2098193149Sdougb fctx_done(fctx, result, __LINE__); 2099193149Sdougb break; 2100193149Sdougb } 2101193149Sdougb /* 2102135446Strhodes * We are connected. Create a dispatcher and 2103135446Strhodes * send the query. 2104135446Strhodes */ 2105135446Strhodes attrs = 0; 2106135446Strhodes attrs |= DNS_DISPATCHATTR_TCP; 2107135446Strhodes attrs |= DNS_DISPATCHATTR_PRIVATE; 2108135446Strhodes attrs |= DNS_DISPATCHATTR_CONNECTED; 2109135446Strhodes if (isc_sockaddr_pf(&query->addrinfo->sockaddr) == 2110135446Strhodes AF_INET) 2111135446Strhodes attrs |= DNS_DISPATCHATTR_IPV4; 2112135446Strhodes else 2113135446Strhodes attrs |= DNS_DISPATCHATTR_IPV6; 2114135446Strhodes attrs |= DNS_DISPATCHATTR_MAKEQUERY; 2115135446Strhodes 2116135446Strhodes result = dns_dispatch_createtcp(query->dispatchmgr, 2117135446Strhodes query->tcpsocket, 2118135446Strhodes query->fctx->res->taskmgr, 2119135446Strhodes 4096, 2, 1, 1, 3, attrs, 2120135446Strhodes &query->dispatch); 2121135446Strhodes 2122135446Strhodes /* 2123135446Strhodes * Regardless of whether dns_dispatch_create() 2124135446Strhodes * succeeded or not, we don't need our reference 2125135446Strhodes * to the socket anymore. 2126135446Strhodes */ 2127135446Strhodes isc_socket_detach(&query->tcpsocket); 2128135446Strhodes 2129135446Strhodes if (result == ISC_R_SUCCESS) 2130135446Strhodes result = resquery_send(query); 2131135446Strhodes 2132135446Strhodes if (result != ISC_R_SUCCESS) { 2133193149Sdougb fctx_cancelquery(&query, NULL, NULL, ISC_FALSE); 2134193149Sdougb fctx_done(fctx, result, __LINE__); 2135135446Strhodes } 2136143731Sdougb break; 2137143731Sdougb 2138143731Sdougb case ISC_R_NETUNREACH: 2139143731Sdougb case ISC_R_HOSTUNREACH: 2140143731Sdougb case ISC_R_CONNREFUSED: 2141143731Sdougb case ISC_R_NOPERM: 2142143731Sdougb case ISC_R_ADDRNOTAVAIL: 2143153816Sdougb case ISC_R_CONNECTIONRESET: 2144143731Sdougb /* 2145143731Sdougb * No route to remote. 2146143731Sdougb */ 2147135446Strhodes isc_socket_detach(&query->tcpsocket); 2148143731Sdougb fctx_cancelquery(&query, NULL, NULL, ISC_TRUE); 2149143731Sdougb retry = ISC_TRUE; 2150143731Sdougb break; 2151143731Sdougb 2152143731Sdougb default: 2153143731Sdougb isc_socket_detach(&query->tcpsocket); 2154135446Strhodes fctx_cancelquery(&query, NULL, NULL, ISC_FALSE); 2155143731Sdougb break; 2156135446Strhodes } 2157135446Strhodes } 2158135446Strhodes 2159135446Strhodes isc_event_free(&event); 2160186462Sdougb 2161143731Sdougb if (retry) { 2162143731Sdougb /* 2163143731Sdougb * Behave as if the idle timer has expired. For TCP 2164143731Sdougb * connections this may not actually reflect the latest timer. 2165143731Sdougb */ 2166143731Sdougb fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; 2167143731Sdougb result = fctx_stopidletimer(fctx); 2168143731Sdougb if (result != ISC_R_SUCCESS) 2169193149Sdougb fctx_done(fctx, result, __LINE__); 2170143731Sdougb else 2171205292Sdougb fctx_try(fctx, ISC_TRUE, ISC_FALSE); 2172143731Sdougb } 2173135446Strhodes} 2174135446Strhodes 2175135446Strhodesstatic void 2176135446Strhodesfctx_finddone(isc_task_t *task, isc_event_t *event) { 2177135446Strhodes fetchctx_t *fctx; 2178135446Strhodes dns_adbfind_t *find; 2179135446Strhodes dns_resolver_t *res; 2180135446Strhodes isc_boolean_t want_try = ISC_FALSE; 2181135446Strhodes isc_boolean_t want_done = ISC_FALSE; 2182135446Strhodes isc_boolean_t bucket_empty = ISC_FALSE; 2183236374Sdougb unsigned int bucketnum; 2184234010Sdougb isc_boolean_t destroy = ISC_FALSE; 2185135446Strhodes 2186135446Strhodes find = event->ev_sender; 2187135446Strhodes fctx = event->ev_arg; 2188135446Strhodes REQUIRE(VALID_FCTX(fctx)); 2189135446Strhodes res = fctx->res; 2190135446Strhodes 2191135446Strhodes UNUSED(task); 2192135446Strhodes 2193135446Strhodes FCTXTRACE("finddone"); 2194135446Strhodes 2195234010Sdougb bucketnum = fctx->bucketnum; 2196234010Sdougb LOCK(&res->buckets[bucketnum].lock); 2197234010Sdougb 2198135446Strhodes INSIST(fctx->pending > 0); 2199135446Strhodes fctx->pending--; 2200135446Strhodes 2201135446Strhodes if (ADDRWAIT(fctx)) { 2202135446Strhodes /* 2203135446Strhodes * The fetch is waiting for a name to be found. 2204135446Strhodes */ 2205135446Strhodes INSIST(!SHUTTINGDOWN(fctx)); 2206135446Strhodes fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; 2207275672Sdelphij if (event->ev_type == DNS_EVENT_ADBMOREADDRESSES) { 2208135446Strhodes want_try = ISC_TRUE; 2209275672Sdelphij fctx->totalqueries += find->qtotal; 2210275672Sdelphij } else { 2211193149Sdougb fctx->findfail++; 2212193149Sdougb if (fctx->pending == 0) { 2213193149Sdougb /* 2214193149Sdougb * We've got nothing else to wait for and don't 2215193149Sdougb * know the answer. There's nothing to do but 2216193149Sdougb * fail the fctx. 2217193149Sdougb */ 2218193149Sdougb want_done = ISC_TRUE; 2219193149Sdougb } 2220135446Strhodes } 2221135446Strhodes } else if (SHUTTINGDOWN(fctx) && fctx->pending == 0 && 2222166332Sdougb fctx->nqueries == 0 && ISC_LIST_EMPTY(fctx->validators)) { 2223236374Sdougb 2224236374Sdougb if (fctx->references == 0) { 2225236374Sdougb bucket_empty = fctx_unlink(fctx); 2226234010Sdougb destroy = ISC_TRUE; 2227236374Sdougb } 2228135446Strhodes } 2229234010Sdougb UNLOCK(&res->buckets[bucketnum].lock); 2230135446Strhodes 2231135446Strhodes isc_event_free(&event); 2232135446Strhodes dns_adb_destroyfind(&find); 2233135446Strhodes 2234135446Strhodes if (want_try) 2235205292Sdougb fctx_try(fctx, ISC_TRUE, ISC_FALSE); 2236135446Strhodes else if (want_done) 2237193149Sdougb fctx_done(fctx, ISC_R_FAILURE, __LINE__); 2238236374Sdougb else if (destroy) { 2239236374Sdougb fctx_destroy(fctx); 2240236374Sdougb if (bucket_empty) 2241236374Sdougb empty_bucket(res); 2242236374Sdougb } 2243135446Strhodes} 2244135446Strhodes 2245135446Strhodes 2246135446Strhodesstatic inline isc_boolean_t 2247135446Strhodesbad_server(fetchctx_t *fctx, isc_sockaddr_t *address) { 2248135446Strhodes isc_sockaddr_t *sa; 2249135446Strhodes 2250135446Strhodes for (sa = ISC_LIST_HEAD(fctx->bad); 2251135446Strhodes sa != NULL; 2252135446Strhodes sa = ISC_LIST_NEXT(sa, link)) { 2253135446Strhodes if (isc_sockaddr_equal(sa, address)) 2254135446Strhodes return (ISC_TRUE); 2255135446Strhodes } 2256135446Strhodes 2257135446Strhodes return (ISC_FALSE); 2258135446Strhodes} 2259135446Strhodes 2260135446Strhodesstatic inline isc_boolean_t 2261135446Strhodesmark_bad(fetchctx_t *fctx) { 2262135446Strhodes dns_adbfind_t *curr; 2263135446Strhodes dns_adbaddrinfo_t *addrinfo; 2264135446Strhodes isc_boolean_t all_bad = ISC_TRUE; 2265135446Strhodes 2266135446Strhodes /* 2267135446Strhodes * Mark all known bad servers, so we don't try to talk to them 2268135446Strhodes * again. 2269135446Strhodes */ 2270135446Strhodes 2271135446Strhodes /* 2272135446Strhodes * Mark any bad nameservers. 2273135446Strhodes */ 2274135446Strhodes for (curr = ISC_LIST_HEAD(fctx->finds); 2275135446Strhodes curr != NULL; 2276135446Strhodes curr = ISC_LIST_NEXT(curr, publink)) { 2277135446Strhodes for (addrinfo = ISC_LIST_HEAD(curr->list); 2278135446Strhodes addrinfo != NULL; 2279135446Strhodes addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { 2280135446Strhodes if (bad_server(fctx, &addrinfo->sockaddr)) 2281135446Strhodes addrinfo->flags |= FCTX_ADDRINFO_MARK; 2282135446Strhodes else 2283135446Strhodes all_bad = ISC_FALSE; 2284135446Strhodes } 2285135446Strhodes } 2286135446Strhodes 2287135446Strhodes /* 2288135446Strhodes * Mark any bad forwarders. 2289135446Strhodes */ 2290135446Strhodes for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs); 2291135446Strhodes addrinfo != NULL; 2292135446Strhodes addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { 2293135446Strhodes if (bad_server(fctx, &addrinfo->sockaddr)) 2294135446Strhodes addrinfo->flags |= FCTX_ADDRINFO_MARK; 2295135446Strhodes else 2296135446Strhodes all_bad = ISC_FALSE; 2297135446Strhodes } 2298135446Strhodes 2299135446Strhodes /* 2300135446Strhodes * Mark any bad alternates. 2301135446Strhodes */ 2302135446Strhodes for (curr = ISC_LIST_HEAD(fctx->altfinds); 2303135446Strhodes curr != NULL; 2304135446Strhodes curr = ISC_LIST_NEXT(curr, publink)) { 2305135446Strhodes for (addrinfo = ISC_LIST_HEAD(curr->list); 2306135446Strhodes addrinfo != NULL; 2307135446Strhodes addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { 2308135446Strhodes if (bad_server(fctx, &addrinfo->sockaddr)) 2309135446Strhodes addrinfo->flags |= FCTX_ADDRINFO_MARK; 2310135446Strhodes else 2311135446Strhodes all_bad = ISC_FALSE; 2312135446Strhodes } 2313135446Strhodes } 2314135446Strhodes 2315135446Strhodes for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs); 2316135446Strhodes addrinfo != NULL; 2317135446Strhodes addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { 2318135446Strhodes if (bad_server(fctx, &addrinfo->sockaddr)) 2319135446Strhodes addrinfo->flags |= FCTX_ADDRINFO_MARK; 2320135446Strhodes else 2321135446Strhodes all_bad = ISC_FALSE; 2322135446Strhodes } 2323135446Strhodes 2324135446Strhodes return (all_bad); 2325135446Strhodes} 2326135446Strhodes 2327135446Strhodesstatic void 2328193149Sdougbadd_bad(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_result_t reason, 2329193149Sdougb badnstype_t badtype) 2330193149Sdougb{ 2331135446Strhodes char namebuf[DNS_NAME_FORMATSIZE]; 2332135446Strhodes char addrbuf[ISC_SOCKADDR_FORMATSIZE]; 2333135446Strhodes char classbuf[64]; 2334135446Strhodes char typebuf[64]; 2335135446Strhodes char code[64]; 2336135446Strhodes isc_buffer_t b; 2337135446Strhodes isc_sockaddr_t *sa; 2338224092Sdougb const char *spc = ""; 2339174187Sdougb isc_sockaddr_t *address = &addrinfo->sockaddr; 2340135446Strhodes 2341193149Sdougb if (reason == DNS_R_LAME) 2342193149Sdougb fctx->lamecount++; 2343193149Sdougb else { 2344193149Sdougb switch (badtype) { 2345193149Sdougb case badns_unreachable: 2346193149Sdougb fctx->neterr++; 2347193149Sdougb break; 2348193149Sdougb case badns_response: 2349193149Sdougb fctx->badresp++; 2350193149Sdougb break; 2351193149Sdougb case badns_validation: 2352193149Sdougb break; /* counted as 'valfail' */ 2353193149Sdougb } 2354193149Sdougb } 2355193149Sdougb 2356135446Strhodes if (bad_server(fctx, address)) { 2357135446Strhodes /* 2358135446Strhodes * We already know this server is bad. 2359135446Strhodes */ 2360135446Strhodes return; 2361135446Strhodes } 2362135446Strhodes 2363135446Strhodes FCTXTRACE("add_bad"); 2364135446Strhodes 2365236374Sdougb sa = isc_mem_get(fctx->mctx, sizeof(*sa)); 2366135446Strhodes if (sa == NULL) 2367135446Strhodes return; 2368135446Strhodes *sa = *address; 2369135446Strhodes ISC_LIST_INITANDAPPEND(fctx->bad, sa, link); 2370135446Strhodes 2371193149Sdougb if (reason == DNS_R_LAME) /* already logged */ 2372135446Strhodes return; 2373135446Strhodes 2374186462Sdougb if (reason == DNS_R_UNEXPECTEDRCODE && 2375186462Sdougb fctx->rmessage->rcode == dns_rcode_servfail && 2376174187Sdougb ISFORWARDER(addrinfo)) 2377174187Sdougb return; 2378174187Sdougb 2379135446Strhodes if (reason == DNS_R_UNEXPECTEDRCODE) { 2380135446Strhodes isc_buffer_init(&b, code, sizeof(code) - 1); 2381135446Strhodes dns_rcode_totext(fctx->rmessage->rcode, &b); 2382135446Strhodes code[isc_buffer_usedlength(&b)] = '\0'; 2383224092Sdougb spc = " "; 2384135446Strhodes } else if (reason == DNS_R_UNEXPECTEDOPCODE) { 2385135446Strhodes isc_buffer_init(&b, code, sizeof(code) - 1); 2386135446Strhodes dns_opcode_totext((dns_opcode_t)fctx->rmessage->opcode, &b); 2387135446Strhodes code[isc_buffer_usedlength(&b)] = '\0'; 2388224092Sdougb spc = " "; 2389135446Strhodes } else { 2390135446Strhodes code[0] = '\0'; 2391135446Strhodes } 2392135446Strhodes dns_name_format(&fctx->name, namebuf, sizeof(namebuf)); 2393135446Strhodes dns_rdatatype_format(fctx->type, typebuf, sizeof(typebuf)); 2394135446Strhodes dns_rdataclass_format(fctx->res->rdclass, classbuf, sizeof(classbuf)); 2395135446Strhodes isc_sockaddr_format(address, addrbuf, sizeof(addrbuf)); 2396135446Strhodes isc_log_write(dns_lctx, DNS_LOGCATEGORY_LAME_SERVERS, 2397135446Strhodes DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO, 2398224092Sdougb "error (%s%s%s) resolving '%s/%s/%s': %s", 2399224092Sdougb dns_result_totext(reason), spc, code, 2400135446Strhodes namebuf, typebuf, classbuf, addrbuf); 2401135446Strhodes} 2402135446Strhodes 2403193149Sdougb/* 2404224092Sdougb * Sort addrinfo list by RTT. 2405193149Sdougb */ 2406135446Strhodesstatic void 2407224092Sdougbsort_adbfind(dns_adbfind_t *find) { 2408135446Strhodes dns_adbaddrinfo_t *best, *curr; 2409135446Strhodes dns_adbaddrinfolist_t sorted; 2410135446Strhodes 2411193149Sdougb /* Lame N^2 bubble sort. */ 2412135446Strhodes ISC_LIST_INIT(sorted); 2413135446Strhodes while (!ISC_LIST_EMPTY(find->list)) { 2414135446Strhodes best = ISC_LIST_HEAD(find->list); 2415135446Strhodes curr = ISC_LIST_NEXT(best, publink); 2416135446Strhodes while (curr != NULL) { 2417135446Strhodes if (curr->srtt < best->srtt) 2418135446Strhodes best = curr; 2419135446Strhodes curr = ISC_LIST_NEXT(curr, publink); 2420135446Strhodes } 2421135446Strhodes ISC_LIST_UNLINK(find->list, best, publink); 2422135446Strhodes ISC_LIST_APPEND(sorted, best, publink); 2423135446Strhodes } 2424135446Strhodes find->list = sorted; 2425135446Strhodes} 2426135446Strhodes 2427193149Sdougb/* 2428224092Sdougb * Sort a list of finds by server RTT. 2429193149Sdougb */ 2430135446Strhodesstatic void 2431224092Sdougbsort_finds(dns_adbfindlist_t *findlist) { 2432135446Strhodes dns_adbfind_t *best, *curr; 2433135446Strhodes dns_adbfindlist_t sorted; 2434135446Strhodes dns_adbaddrinfo_t *addrinfo, *bestaddrinfo; 2435135446Strhodes 2436224092Sdougb /* Sort each find's addrinfo list by SRTT. */ 2437193149Sdougb for (curr = ISC_LIST_HEAD(*findlist); 2438193149Sdougb curr != NULL; 2439193149Sdougb curr = ISC_LIST_NEXT(curr, publink)) 2440224092Sdougb sort_adbfind(curr); 2441135446Strhodes 2442193149Sdougb /* Lame N^2 bubble sort. */ 2443135446Strhodes ISC_LIST_INIT(sorted); 2444193149Sdougb while (!ISC_LIST_EMPTY(*findlist)) { 2445193149Sdougb best = ISC_LIST_HEAD(*findlist); 2446135446Strhodes bestaddrinfo = ISC_LIST_HEAD(best->list); 2447135446Strhodes INSIST(bestaddrinfo != NULL); 2448135446Strhodes curr = ISC_LIST_NEXT(best, publink); 2449135446Strhodes while (curr != NULL) { 2450135446Strhodes addrinfo = ISC_LIST_HEAD(curr->list); 2451135446Strhodes INSIST(addrinfo != NULL); 2452135446Strhodes if (addrinfo->srtt < bestaddrinfo->srtt) { 2453135446Strhodes best = curr; 2454135446Strhodes bestaddrinfo = addrinfo; 2455135446Strhodes } 2456135446Strhodes curr = ISC_LIST_NEXT(curr, publink); 2457135446Strhodes } 2458193149Sdougb ISC_LIST_UNLINK(*findlist, best, publink); 2459135446Strhodes ISC_LIST_APPEND(sorted, best, publink); 2460135446Strhodes } 2461193149Sdougb *findlist = sorted; 2462135446Strhodes} 2463135446Strhodes 2464135446Strhodesstatic void 2465135446Strhodesfindname(fetchctx_t *fctx, dns_name_t *name, in_port_t port, 2466135446Strhodes unsigned int options, unsigned int flags, isc_stdtime_t now, 2467170222Sdougb isc_boolean_t *need_alternate) 2468135446Strhodes{ 2469135446Strhodes dns_adbaddrinfo_t *ai; 2470135446Strhodes dns_adbfind_t *find; 2471135446Strhodes dns_resolver_t *res; 2472135446Strhodes isc_boolean_t unshared; 2473135446Strhodes isc_result_t result; 2474135446Strhodes 2475135446Strhodes res = fctx->res; 2476254402Serwin unshared = ISC_TF((fctx->options & DNS_FETCHOPT_UNSHARED) != 0); 2477135446Strhodes /* 2478135446Strhodes * If this name is a subdomain of the query domain, tell 2479135446Strhodes * the ADB to start looking using zone/hint data. This keeps us 2480135446Strhodes * from getting stuck if the nameserver is beneath the zone cut 2481135446Strhodes * and we don't know its address (e.g. because the A record has 2482135446Strhodes * expired). 2483135446Strhodes */ 2484135446Strhodes if (dns_name_issubdomain(name, &fctx->domain)) 2485135446Strhodes options |= DNS_ADBFIND_STARTATZONE; 2486135446Strhodes options |= DNS_ADBFIND_GLUEOK; 2487135446Strhodes options |= DNS_ADBFIND_HINTOK; 2488135446Strhodes 2489135446Strhodes /* 2490135446Strhodes * See what we know about this address. 2491135446Strhodes */ 2492135446Strhodes find = NULL; 2493275672Sdelphij result = dns_adb_createfind2(fctx->adb, 2494275672Sdelphij res->buckets[fctx->bucketnum].task, 2495275672Sdelphij fctx_finddone, fctx, name, 2496275672Sdelphij &fctx->name, fctx->type, 2497275672Sdelphij options, now, NULL, 2498275672Sdelphij res->view->dstport, 2499275672Sdelphij fctx->depth + 1, &find); 2500135446Strhodes if (result != ISC_R_SUCCESS) { 2501135446Strhodes if (result == DNS_R_ALIAS) { 2502135446Strhodes /* 2503135446Strhodes * XXXRTH Follow the CNAME/DNAME chain? 2504135446Strhodes */ 2505135446Strhodes dns_adb_destroyfind(&find); 2506193149Sdougb fctx->adberr++; 2507135446Strhodes } 2508135446Strhodes } else if (!ISC_LIST_EMPTY(find->list)) { 2509135446Strhodes /* 2510135446Strhodes * We have at least some of the addresses for the 2511135446Strhodes * name. 2512135446Strhodes */ 2513135446Strhodes INSIST((find->options & DNS_ADBFIND_WANTEVENT) == 0); 2514135446Strhodes if (flags != 0 || port != 0) { 2515135446Strhodes for (ai = ISC_LIST_HEAD(find->list); 2516135446Strhodes ai != NULL; 2517135446Strhodes ai = ISC_LIST_NEXT(ai, publink)) { 2518135446Strhodes ai->flags |= flags; 2519135446Strhodes if (port != 0) 2520135446Strhodes isc_sockaddr_setport(&ai->sockaddr, 2521135446Strhodes port); 2522135446Strhodes } 2523135446Strhodes } 2524135446Strhodes if ((flags & FCTX_ADDRINFO_FORWARDER) != 0) 2525135446Strhodes ISC_LIST_APPEND(fctx->altfinds, find, publink); 2526135446Strhodes else 2527135446Strhodes ISC_LIST_APPEND(fctx->finds, find, publink); 2528135446Strhodes } else { 2529135446Strhodes /* 2530135446Strhodes * We don't know any of the addresses for this 2531135446Strhodes * name. 2532135446Strhodes */ 2533135446Strhodes if ((find->options & DNS_ADBFIND_WANTEVENT) != 0) { 2534135446Strhodes /* 2535135446Strhodes * We're looking for them and will get an 2536135446Strhodes * event about it later. 2537135446Strhodes */ 2538135446Strhodes fctx->pending++; 2539135446Strhodes /* 2540135446Strhodes * Bootstrap. 2541135446Strhodes */ 2542135446Strhodes if (need_alternate != NULL && 2543135446Strhodes !*need_alternate && unshared && 2544254897Serwin ((res->dispatches4 == NULL && 2545135446Strhodes find->result_v6 != DNS_R_NXDOMAIN) || 2546254897Serwin (res->dispatches6 == NULL && 2547135446Strhodes find->result_v4 != DNS_R_NXDOMAIN))) 2548135446Strhodes *need_alternate = ISC_TRUE; 2549135446Strhodes } else { 2550193149Sdougb if ((find->options & DNS_ADBFIND_LAMEPRUNED) != 0) 2551193149Sdougb fctx->lamecount++; /* cached lame server */ 2552193149Sdougb else 2553193149Sdougb fctx->adberr++; /* unreachable server, etc. */ 2554193149Sdougb 2555135446Strhodes /* 2556135446Strhodes * If we know there are no addresses for 2557135446Strhodes * the family we are using then try to add 2558135446Strhodes * an alternative server. 2559135446Strhodes */ 2560135446Strhodes if (need_alternate != NULL && !*need_alternate && 2561254897Serwin ((res->dispatches4 == NULL && 2562135446Strhodes find->result_v6 == DNS_R_NXRRSET) || 2563254897Serwin (res->dispatches6 == NULL && 2564135446Strhodes find->result_v4 == DNS_R_NXRRSET))) 2565135446Strhodes *need_alternate = ISC_TRUE; 2566135446Strhodes dns_adb_destroyfind(&find); 2567135446Strhodes } 2568135446Strhodes } 2569135446Strhodes} 2570135446Strhodes 2571204619Sdougbstatic isc_boolean_t 2572204619Sdougbisstrictsubdomain(dns_name_t *name1, dns_name_t *name2) { 2573204619Sdougb int order; 2574204619Sdougb unsigned int nlabels; 2575204619Sdougb dns_namereln_t namereln; 2576204619Sdougb 2577204619Sdougb namereln = dns_name_fullcompare(name1, name2, &order, &nlabels); 2578204619Sdougb return (ISC_TF(namereln == dns_namereln_subdomain)); 2579204619Sdougb} 2580204619Sdougb 2581135446Strhodesstatic isc_result_t 2582205292Sdougbfctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) { 2583135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 2584135446Strhodes isc_result_t result; 2585135446Strhodes dns_resolver_t *res; 2586135446Strhodes isc_stdtime_t now; 2587225361Sdougb unsigned int stdoptions = 0; 2588135446Strhodes isc_sockaddr_t *sa; 2589135446Strhodes dns_adbaddrinfo_t *ai; 2590170222Sdougb isc_boolean_t all_bad; 2591135446Strhodes dns_rdata_ns_t ns; 2592135446Strhodes isc_boolean_t need_alternate = ISC_FALSE; 2593135446Strhodes 2594135446Strhodes FCTXTRACE("getaddresses"); 2595135446Strhodes 2596135446Strhodes /* 2597135446Strhodes * Don't pound on remote servers. (Failsafe!) 2598135446Strhodes */ 2599135446Strhodes fctx->restarts++; 2600135446Strhodes if (fctx->restarts > 10) { 2601135446Strhodes FCTXTRACE("too many restarts"); 2602135446Strhodes return (DNS_R_SERVFAIL); 2603135446Strhodes } 2604135446Strhodes 2605135446Strhodes res = fctx->res; 2606135446Strhodes 2607275672Sdelphij if (fctx->depth > res->maxdepth) { 2608275672Sdelphij FCTXTRACE("too much NS indirection"); 2609275672Sdelphij return (DNS_R_SERVFAIL); 2610275672Sdelphij } 2611275672Sdelphij 2612135446Strhodes /* 2613135446Strhodes * Forwarders. 2614135446Strhodes */ 2615135446Strhodes 2616135446Strhodes INSIST(ISC_LIST_EMPTY(fctx->forwaddrs)); 2617135446Strhodes INSIST(ISC_LIST_EMPTY(fctx->altaddrs)); 2618135446Strhodes 2619135446Strhodes /* 2620135446Strhodes * If this fctx has forwarders, use them; otherwise use any 2621135446Strhodes * selective forwarders specified in the view; otherwise use the 2622135446Strhodes * resolver's forwarders (if any). 2623135446Strhodes */ 2624135446Strhodes sa = ISC_LIST_HEAD(fctx->forwarders); 2625135446Strhodes if (sa == NULL) { 2626135446Strhodes dns_forwarders_t *forwarders = NULL; 2627135446Strhodes dns_name_t *name = &fctx->name; 2628135446Strhodes dns_name_t suffix; 2629135446Strhodes unsigned int labels; 2630204619Sdougb dns_fixedname_t fixed; 2631204619Sdougb dns_name_t *domain; 2632135446Strhodes 2633135446Strhodes /* 2634135446Strhodes * DS records are found in the parent server. 2635135446Strhodes * Strip label to get the correct forwarder (if any). 2636135446Strhodes */ 2637204619Sdougb if (dns_rdatatype_atparent(fctx->type) && 2638135446Strhodes dns_name_countlabels(name) > 1) { 2639135446Strhodes dns_name_init(&suffix, NULL); 2640135446Strhodes labels = dns_name_countlabels(name); 2641135446Strhodes dns_name_getlabelsequence(name, 1, labels - 1, &suffix); 2642135446Strhodes name = &suffix; 2643135446Strhodes } 2644204619Sdougb 2645204619Sdougb dns_fixedname_init(&fixed); 2646204619Sdougb domain = dns_fixedname_name(&fixed); 2647204619Sdougb result = dns_fwdtable_find2(fctx->res->view->fwdtable, name, 2648204619Sdougb domain, &forwarders); 2649135446Strhodes if (result == ISC_R_SUCCESS) { 2650135446Strhodes sa = ISC_LIST_HEAD(forwarders->addrs); 2651135446Strhodes fctx->fwdpolicy = forwarders->fwdpolicy; 2652204619Sdougb if (fctx->fwdpolicy == dns_fwdpolicy_only && 2653204619Sdougb isstrictsubdomain(domain, &fctx->domain)) { 2654236374Sdougb dns_name_free(&fctx->domain, fctx->mctx); 2655204619Sdougb dns_name_init(&fctx->domain, NULL); 2656236374Sdougb result = dns_name_dup(domain, fctx->mctx, 2657204619Sdougb &fctx->domain); 2658204619Sdougb if (result != ISC_R_SUCCESS) 2659204619Sdougb return (result); 2660204619Sdougb } 2661135446Strhodes } 2662135446Strhodes } 2663135446Strhodes 2664135446Strhodes while (sa != NULL) { 2665186462Sdougb if ((isc_sockaddr_pf(sa) == AF_INET && 2666254897Serwin fctx->res->dispatches4 == NULL) || 2667186462Sdougb (isc_sockaddr_pf(sa) == AF_INET6 && 2668254897Serwin fctx->res->dispatches6 == NULL)) { 2669186462Sdougb sa = ISC_LIST_NEXT(sa, link); 2670186462Sdougb continue; 2671186462Sdougb } 2672135446Strhodes ai = NULL; 2673135446Strhodes result = dns_adb_findaddrinfo(fctx->adb, 2674135446Strhodes sa, &ai, 0); /* XXXMLG */ 2675135446Strhodes if (result == ISC_R_SUCCESS) { 2676135446Strhodes dns_adbaddrinfo_t *cur; 2677135446Strhodes ai->flags |= FCTX_ADDRINFO_FORWARDER; 2678135446Strhodes cur = ISC_LIST_HEAD(fctx->forwaddrs); 2679135446Strhodes while (cur != NULL && cur->srtt < ai->srtt) 2680135446Strhodes cur = ISC_LIST_NEXT(cur, publink); 2681135446Strhodes if (cur != NULL) 2682135446Strhodes ISC_LIST_INSERTBEFORE(fctx->forwaddrs, cur, 2683135446Strhodes ai, publink); 2684135446Strhodes else 2685135446Strhodes ISC_LIST_APPEND(fctx->forwaddrs, ai, publink); 2686135446Strhodes } 2687135446Strhodes sa = ISC_LIST_NEXT(sa, link); 2688135446Strhodes } 2689135446Strhodes 2690135446Strhodes /* 2691135446Strhodes * If the forwarding policy is "only", we don't need the addresses 2692135446Strhodes * of the nameservers. 2693135446Strhodes */ 2694135446Strhodes if (fctx->fwdpolicy == dns_fwdpolicy_only) 2695135446Strhodes goto out; 2696135446Strhodes 2697135446Strhodes /* 2698135446Strhodes * Normal nameservers. 2699135446Strhodes */ 2700135446Strhodes 2701135446Strhodes stdoptions = DNS_ADBFIND_WANTEVENT | DNS_ADBFIND_EMPTYEVENT; 2702135446Strhodes if (fctx->restarts == 1) { 2703135446Strhodes /* 2704135446Strhodes * To avoid sending out a flood of queries likely to 2705135446Strhodes * result in NXRRSET, we suppress fetches for address 2706135446Strhodes * families we don't have the first time through, 2707135446Strhodes * provided that we have addresses in some family we 2708135446Strhodes * can use. 2709135446Strhodes * 2710135446Strhodes * We don't want to set this option all the time, since 2711135446Strhodes * if fctx->restarts > 1, we've clearly been having trouble 2712135446Strhodes * with the addresses we had, so getting more could help. 2713135446Strhodes */ 2714135446Strhodes stdoptions |= DNS_ADBFIND_AVOIDFETCHES; 2715135446Strhodes } 2716254897Serwin if (res->dispatches4 != NULL) 2717135446Strhodes stdoptions |= DNS_ADBFIND_INET; 2718254897Serwin if (res->dispatches6 != NULL) 2719135446Strhodes stdoptions |= DNS_ADBFIND_INET6; 2720135446Strhodes isc_stdtime_get(&now); 2721135446Strhodes 2722135446Strhodes INSIST(ISC_LIST_EMPTY(fctx->finds)); 2723135446Strhodes INSIST(ISC_LIST_EMPTY(fctx->altfinds)); 2724135446Strhodes 2725135446Strhodes for (result = dns_rdataset_first(&fctx->nameservers); 2726135446Strhodes result == ISC_R_SUCCESS; 2727135446Strhodes result = dns_rdataset_next(&fctx->nameservers)) 2728135446Strhodes { 2729135446Strhodes dns_rdataset_current(&fctx->nameservers, &rdata); 2730135446Strhodes /* 2731135446Strhodes * Extract the name from the NS record. 2732135446Strhodes */ 2733135446Strhodes result = dns_rdata_tostruct(&rdata, &ns, NULL); 2734135446Strhodes if (result != ISC_R_SUCCESS) 2735135446Strhodes continue; 2736135446Strhodes 2737135446Strhodes findname(fctx, &ns.name, 0, stdoptions, 0, now, 2738170222Sdougb &need_alternate); 2739135446Strhodes dns_rdata_reset(&rdata); 2740135446Strhodes dns_rdata_freestruct(&ns); 2741135446Strhodes } 2742135446Strhodes if (result != ISC_R_NOMORE) 2743135446Strhodes return (result); 2744135446Strhodes 2745135446Strhodes /* 2746135446Strhodes * Do we need to use 6 to 4? 2747135446Strhodes */ 2748135446Strhodes if (need_alternate) { 2749135446Strhodes int family; 2750135446Strhodes alternate_t *a; 2751254897Serwin family = (res->dispatches6 != NULL) ? AF_INET6 : AF_INET; 2752135446Strhodes for (a = ISC_LIST_HEAD(fctx->res->alternates); 2753135446Strhodes a != NULL; 2754135446Strhodes a = ISC_LIST_NEXT(a, link)) { 2755135446Strhodes if (!a->isaddress) { 2756135446Strhodes findname(fctx, &a->_u._n.name, a->_u._n.port, 2757135446Strhodes stdoptions, FCTX_ADDRINFO_FORWARDER, 2758170222Sdougb now, NULL); 2759135446Strhodes continue; 2760135446Strhodes } 2761135446Strhodes if (isc_sockaddr_pf(&a->_u.addr) != family) 2762135446Strhodes continue; 2763135446Strhodes ai = NULL; 2764135446Strhodes result = dns_adb_findaddrinfo(fctx->adb, &a->_u.addr, 2765135446Strhodes &ai, 0); 2766135446Strhodes if (result == ISC_R_SUCCESS) { 2767135446Strhodes dns_adbaddrinfo_t *cur; 2768135446Strhodes ai->flags |= FCTX_ADDRINFO_FORWARDER; 2769135446Strhodes cur = ISC_LIST_HEAD(fctx->altaddrs); 2770135446Strhodes while (cur != NULL && cur->srtt < ai->srtt) 2771135446Strhodes cur = ISC_LIST_NEXT(cur, publink); 2772135446Strhodes if (cur != NULL) 2773135446Strhodes ISC_LIST_INSERTBEFORE(fctx->altaddrs, 2774135446Strhodes cur, ai, publink); 2775135446Strhodes else 2776135446Strhodes ISC_LIST_APPEND(fctx->altaddrs, ai, 2777135446Strhodes publink); 2778135446Strhodes } 2779135446Strhodes } 2780135446Strhodes } 2781135446Strhodes 2782135446Strhodes out: 2783135446Strhodes /* 2784135446Strhodes * Mark all known bad servers. 2785135446Strhodes */ 2786135446Strhodes all_bad = mark_bad(fctx); 2787135446Strhodes 2788135446Strhodes /* 2789135446Strhodes * How are we doing? 2790135446Strhodes */ 2791135446Strhodes if (all_bad) { 2792135446Strhodes /* 2793135446Strhodes * We've got no addresses. 2794135446Strhodes */ 2795135446Strhodes if (fctx->pending > 0) { 2796135446Strhodes /* 2797135446Strhodes * We're fetching the addresses, but don't have any 2798135446Strhodes * yet. Tell the caller to wait for an answer. 2799135446Strhodes */ 2800135446Strhodes result = DNS_R_WAIT; 2801135446Strhodes } else { 2802205292Sdougb isc_time_t expire; 2803205292Sdougb isc_interval_t i; 2804135446Strhodes /* 2805135446Strhodes * We've lost completely. We don't know any 2806135446Strhodes * addresses, and the ADB has told us it can't get 2807135446Strhodes * them. 2808135446Strhodes */ 2809135446Strhodes FCTXTRACE("no addresses"); 2810205292Sdougb isc_interval_set(&i, DNS_BADCACHE_TTL(fctx), 0); 2811205292Sdougb result = isc_time_nowplusinterval(&expire, &i); 2812205292Sdougb if (badcache && 2813205292Sdougb (fctx->type == dns_rdatatype_dnskey || 2814205292Sdougb fctx->type == dns_rdatatype_dlv || 2815205292Sdougb fctx->type == dns_rdatatype_ds) && 2816205292Sdougb result == ISC_R_SUCCESS) 2817205292Sdougb dns_resolver_addbadcache(fctx->res, 2818205292Sdougb &fctx->name, 2819205292Sdougb fctx->type, &expire); 2820135446Strhodes result = ISC_R_FAILURE; 2821135446Strhodes } 2822135446Strhodes } else { 2823135446Strhodes /* 2824135446Strhodes * We've found some addresses. We might still be looking 2825135446Strhodes * for more addresses. 2826135446Strhodes */ 2827224092Sdougb sort_finds(&fctx->finds); 2828224092Sdougb sort_finds(&fctx->altfinds); 2829135446Strhodes result = ISC_R_SUCCESS; 2830135446Strhodes } 2831135446Strhodes 2832135446Strhodes return (result); 2833135446Strhodes} 2834135446Strhodes 2835135446Strhodesstatic inline void 2836135446Strhodespossibly_mark(fetchctx_t *fctx, dns_adbaddrinfo_t *addr) 2837135446Strhodes{ 2838135446Strhodes isc_netaddr_t na; 2839135446Strhodes char buf[ISC_NETADDR_FORMATSIZE]; 2840135446Strhodes isc_sockaddr_t *sa; 2841135446Strhodes isc_boolean_t aborted = ISC_FALSE; 2842135446Strhodes isc_boolean_t bogus; 2843135446Strhodes dns_acl_t *blackhole; 2844135446Strhodes isc_netaddr_t ipaddr; 2845135446Strhodes dns_peer_t *peer = NULL; 2846135446Strhodes dns_resolver_t *res; 2847135446Strhodes const char *msg = NULL; 2848135446Strhodes 2849135446Strhodes sa = &addr->sockaddr; 2850135446Strhodes 2851135446Strhodes res = fctx->res; 2852135446Strhodes isc_netaddr_fromsockaddr(&ipaddr, sa); 2853135446Strhodes blackhole = dns_dispatchmgr_getblackhole(res->dispatchmgr); 2854135446Strhodes (void) dns_peerlist_peerbyaddr(res->view->peers, &ipaddr, &peer); 2855186462Sdougb 2856135446Strhodes if (blackhole != NULL) { 2857135446Strhodes int match; 2858135446Strhodes 2859135446Strhodes if (dns_acl_match(&ipaddr, NULL, blackhole, 2860135446Strhodes &res->view->aclenv, 2861135446Strhodes &match, NULL) == ISC_R_SUCCESS && 2862135446Strhodes match > 0) 2863135446Strhodes aborted = ISC_TRUE; 2864135446Strhodes } 2865135446Strhodes 2866135446Strhodes if (peer != NULL && 2867135446Strhodes dns_peer_getbogus(peer, &bogus) == ISC_R_SUCCESS && 2868135446Strhodes bogus) 2869135446Strhodes aborted = ISC_TRUE; 2870135446Strhodes 2871135446Strhodes if (aborted) { 2872135446Strhodes addr->flags |= FCTX_ADDRINFO_MARK; 2873135446Strhodes msg = "ignoring blackholed / bogus server: "; 2874135446Strhodes } else if (isc_sockaddr_ismulticast(sa)) { 2875135446Strhodes addr->flags |= FCTX_ADDRINFO_MARK; 2876135446Strhodes msg = "ignoring multicast address: "; 2877135446Strhodes } else if (isc_sockaddr_isexperimental(sa)) { 2878135446Strhodes addr->flags |= FCTX_ADDRINFO_MARK; 2879135446Strhodes msg = "ignoring experimental address: "; 2880135446Strhodes } else if (sa->type.sa.sa_family != AF_INET6) { 2881135446Strhodes return; 2882135446Strhodes } else if (IN6_IS_ADDR_V4MAPPED(&sa->type.sin6.sin6_addr)) { 2883135446Strhodes addr->flags |= FCTX_ADDRINFO_MARK; 2884135446Strhodes msg = "ignoring IPv6 mapped IPV4 address: "; 2885135446Strhodes } else if (IN6_IS_ADDR_V4COMPAT(&sa->type.sin6.sin6_addr)) { 2886135446Strhodes addr->flags |= FCTX_ADDRINFO_MARK; 2887135446Strhodes msg = "ignoring IPv6 compatibility IPV4 address: "; 2888135446Strhodes } else 2889135446Strhodes return; 2890135446Strhodes 2891135446Strhodes if (!isc_log_wouldlog(dns_lctx, ISC_LOG_DEBUG(3))) 2892135446Strhodes return; 2893135446Strhodes 2894135446Strhodes isc_netaddr_fromsockaddr(&na, sa); 2895135446Strhodes isc_netaddr_format(&na, buf, sizeof(buf)); 2896135446Strhodes FCTXTRACE2(msg, buf); 2897135446Strhodes} 2898135446Strhodes 2899135446Strhodesstatic inline dns_adbaddrinfo_t * 2900135446Strhodesfctx_nextaddress(fetchctx_t *fctx) { 2901135446Strhodes dns_adbfind_t *find, *start; 2902135446Strhodes dns_adbaddrinfo_t *addrinfo; 2903135446Strhodes dns_adbaddrinfo_t *faddrinfo; 2904135446Strhodes 2905135446Strhodes /* 2906135446Strhodes * Return the next untried address, if any. 2907135446Strhodes */ 2908135446Strhodes 2909135446Strhodes /* 2910135446Strhodes * Find the first unmarked forwarder (if any). 2911135446Strhodes */ 2912135446Strhodes for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs); 2913135446Strhodes addrinfo != NULL; 2914135446Strhodes addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { 2915135446Strhodes if (!UNMARKED(addrinfo)) 2916135446Strhodes continue; 2917135446Strhodes possibly_mark(fctx, addrinfo); 2918135446Strhodes if (UNMARKED(addrinfo)) { 2919135446Strhodes addrinfo->flags |= FCTX_ADDRINFO_MARK; 2920135446Strhodes fctx->find = NULL; 2921135446Strhodes return (addrinfo); 2922135446Strhodes } 2923135446Strhodes } 2924135446Strhodes 2925135446Strhodes /* 2926135446Strhodes * No forwarders. Move to the next find. 2927135446Strhodes */ 2928135446Strhodes 2929135446Strhodes fctx->attributes |= FCTX_ATTR_TRIEDFIND; 2930135446Strhodes 2931135446Strhodes find = fctx->find; 2932135446Strhodes if (find == NULL) 2933135446Strhodes find = ISC_LIST_HEAD(fctx->finds); 2934135446Strhodes else { 2935135446Strhodes find = ISC_LIST_NEXT(find, publink); 2936135446Strhodes if (find == NULL) 2937135446Strhodes find = ISC_LIST_HEAD(fctx->finds); 2938135446Strhodes } 2939135446Strhodes 2940135446Strhodes /* 2941135446Strhodes * Find the first unmarked addrinfo. 2942135446Strhodes */ 2943135446Strhodes addrinfo = NULL; 2944135446Strhodes if (find != NULL) { 2945135446Strhodes start = find; 2946135446Strhodes do { 2947135446Strhodes for (addrinfo = ISC_LIST_HEAD(find->list); 2948135446Strhodes addrinfo != NULL; 2949135446Strhodes addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { 2950135446Strhodes if (!UNMARKED(addrinfo)) 2951135446Strhodes continue; 2952135446Strhodes possibly_mark(fctx, addrinfo); 2953135446Strhodes if (UNMARKED(addrinfo)) { 2954135446Strhodes addrinfo->flags |= FCTX_ADDRINFO_MARK; 2955135446Strhodes break; 2956135446Strhodes } 2957135446Strhodes } 2958135446Strhodes if (addrinfo != NULL) 2959135446Strhodes break; 2960135446Strhodes find = ISC_LIST_NEXT(find, publink); 2961135446Strhodes if (find == NULL) 2962135446Strhodes find = ISC_LIST_HEAD(fctx->finds); 2963135446Strhodes } while (find != start); 2964135446Strhodes } 2965135446Strhodes 2966135446Strhodes fctx->find = find; 2967135446Strhodes if (addrinfo != NULL) 2968135446Strhodes return (addrinfo); 2969135446Strhodes 2970135446Strhodes /* 2971135446Strhodes * No nameservers left. Try alternates. 2972135446Strhodes */ 2973135446Strhodes 2974135446Strhodes fctx->attributes |= FCTX_ATTR_TRIEDALT; 2975135446Strhodes 2976135446Strhodes find = fctx->altfind; 2977135446Strhodes if (find == NULL) 2978135446Strhodes find = ISC_LIST_HEAD(fctx->altfinds); 2979135446Strhodes else { 2980135446Strhodes find = ISC_LIST_NEXT(find, publink); 2981135446Strhodes if (find == NULL) 2982135446Strhodes find = ISC_LIST_HEAD(fctx->altfinds); 2983135446Strhodes } 2984135446Strhodes 2985135446Strhodes /* 2986135446Strhodes * Find the first unmarked addrinfo. 2987135446Strhodes */ 2988135446Strhodes addrinfo = NULL; 2989135446Strhodes if (find != NULL) { 2990135446Strhodes start = find; 2991135446Strhodes do { 2992135446Strhodes for (addrinfo = ISC_LIST_HEAD(find->list); 2993135446Strhodes addrinfo != NULL; 2994135446Strhodes addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { 2995135446Strhodes if (!UNMARKED(addrinfo)) 2996135446Strhodes continue; 2997135446Strhodes possibly_mark(fctx, addrinfo); 2998135446Strhodes if (UNMARKED(addrinfo)) { 2999135446Strhodes addrinfo->flags |= FCTX_ADDRINFO_MARK; 3000135446Strhodes break; 3001135446Strhodes } 3002135446Strhodes } 3003135446Strhodes if (addrinfo != NULL) 3004135446Strhodes break; 3005135446Strhodes find = ISC_LIST_NEXT(find, publink); 3006135446Strhodes if (find == NULL) 3007135446Strhodes find = ISC_LIST_HEAD(fctx->altfinds); 3008135446Strhodes } while (find != start); 3009135446Strhodes } 3010135446Strhodes 3011135446Strhodes faddrinfo = addrinfo; 3012135446Strhodes 3013135446Strhodes /* 3014135446Strhodes * See if we have a better alternate server by address. 3015135446Strhodes */ 3016135446Strhodes 3017135446Strhodes for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs); 3018135446Strhodes addrinfo != NULL; 3019135446Strhodes addrinfo = ISC_LIST_NEXT(addrinfo, publink)) { 3020135446Strhodes if (!UNMARKED(addrinfo)) 3021135446Strhodes continue; 3022135446Strhodes possibly_mark(fctx, addrinfo); 3023135446Strhodes if (UNMARKED(addrinfo) && 3024135446Strhodes (faddrinfo == NULL || 3025135446Strhodes addrinfo->srtt < faddrinfo->srtt)) { 3026135446Strhodes if (faddrinfo != NULL) 3027135446Strhodes faddrinfo->flags &= ~FCTX_ADDRINFO_MARK; 3028135446Strhodes addrinfo->flags |= FCTX_ADDRINFO_MARK; 3029135446Strhodes break; 3030135446Strhodes } 3031135446Strhodes } 3032135446Strhodes 3033135446Strhodes if (addrinfo == NULL) { 3034135446Strhodes addrinfo = faddrinfo; 3035135446Strhodes fctx->altfind = find; 3036135446Strhodes } 3037135446Strhodes 3038135446Strhodes return (addrinfo); 3039135446Strhodes} 3040135446Strhodes 3041135446Strhodesstatic void 3042205292Sdougbfctx_try(fetchctx_t *fctx, isc_boolean_t retrying, isc_boolean_t badcache) { 3043135446Strhodes isc_result_t result; 3044135446Strhodes dns_adbaddrinfo_t *addrinfo; 3045135446Strhodes 3046135446Strhodes FCTXTRACE("try"); 3047135446Strhodes 3048135446Strhodes REQUIRE(!ADDRWAIT(fctx)); 3049135446Strhodes 3050275672Sdelphij if (fctx->totalqueries > DEFAULT_MAX_QUERIES) 3051275672Sdelphij fctx_done(fctx, DNS_R_SERVFAIL, __LINE__); 3052275672Sdelphij 3053135446Strhodes addrinfo = fctx_nextaddress(fctx); 3054135446Strhodes if (addrinfo == NULL) { 3055135446Strhodes /* 3056135446Strhodes * We have no more addresses. Start over. 3057135446Strhodes */ 3058135446Strhodes fctx_cancelqueries(fctx, ISC_TRUE); 3059135446Strhodes fctx_cleanupfinds(fctx); 3060135446Strhodes fctx_cleanupaltfinds(fctx); 3061135446Strhodes fctx_cleanupforwaddrs(fctx); 3062135446Strhodes fctx_cleanupaltaddrs(fctx); 3063205292Sdougb result = fctx_getaddresses(fctx, badcache); 3064135446Strhodes if (result == DNS_R_WAIT) { 3065135446Strhodes /* 3066135446Strhodes * Sleep waiting for addresses. 3067135446Strhodes */ 3068135446Strhodes FCTXTRACE("addrwait"); 3069135446Strhodes fctx->attributes |= FCTX_ATTR_ADDRWAIT; 3070135446Strhodes return; 3071135446Strhodes } else if (result != ISC_R_SUCCESS) { 3072135446Strhodes /* 3073135446Strhodes * Something bad happened. 3074135446Strhodes */ 3075193149Sdougb fctx_done(fctx, result, __LINE__); 3076135446Strhodes return; 3077135446Strhodes } 3078135446Strhodes 3079135446Strhodes addrinfo = fctx_nextaddress(fctx); 3080135446Strhodes /* 3081135446Strhodes * While we may have addresses from the ADB, they 3082135446Strhodes * might be bad ones. In this case, return SERVFAIL. 3083135446Strhodes */ 3084135446Strhodes if (addrinfo == NULL) { 3085193149Sdougb fctx_done(fctx, DNS_R_SERVFAIL, __LINE__); 3086135446Strhodes return; 3087135446Strhodes } 3088135446Strhodes } 3089135446Strhodes 3090135446Strhodes result = fctx_query(fctx, addrinfo, fctx->options); 3091135446Strhodes if (result != ISC_R_SUCCESS) 3092193149Sdougb fctx_done(fctx, result, __LINE__); 3093193149Sdougb else if (retrying) 3094193149Sdougb inc_stats(fctx->res, dns_resstatscounter_retry); 3095135446Strhodes} 3096135446Strhodes 3097135446Strhodesstatic isc_boolean_t 3098236374Sdougbfctx_unlink(fetchctx_t *fctx) { 3099135446Strhodes dns_resolver_t *res; 3100135446Strhodes unsigned int bucketnum; 3101135446Strhodes 3102135446Strhodes /* 3103135446Strhodes * Caller must be holding the bucket lock. 3104135446Strhodes */ 3105135446Strhodes 3106135446Strhodes REQUIRE(VALID_FCTX(fctx)); 3107135446Strhodes REQUIRE(fctx->state == fetchstate_done || 3108135446Strhodes fctx->state == fetchstate_init); 3109135446Strhodes REQUIRE(ISC_LIST_EMPTY(fctx->events)); 3110135446Strhodes REQUIRE(ISC_LIST_EMPTY(fctx->queries)); 3111135446Strhodes REQUIRE(ISC_LIST_EMPTY(fctx->finds)); 3112135446Strhodes REQUIRE(ISC_LIST_EMPTY(fctx->altfinds)); 3113135446Strhodes REQUIRE(fctx->pending == 0); 3114166332Sdougb REQUIRE(fctx->references == 0); 3115135446Strhodes REQUIRE(ISC_LIST_EMPTY(fctx->validators)); 3116135446Strhodes 3117236374Sdougb FCTXTRACE("unlink"); 3118135446Strhodes 3119135446Strhodes res = fctx->res; 3120135446Strhodes bucketnum = fctx->bucketnum; 3121135446Strhodes 3122135446Strhodes ISC_LIST_UNLINK(res->buckets[bucketnum].fctxs, fctx, link); 3123135446Strhodes 3124236374Sdougb LOCK(&res->nlock); 3125236374Sdougb res->nfctx--; 3126236374Sdougb UNLOCK(&res->nlock); 3127236374Sdougb 3128236374Sdougb if (res->buckets[bucketnum].exiting && 3129236374Sdougb ISC_LIST_EMPTY(res->buckets[bucketnum].fctxs)) 3130236374Sdougb return (ISC_TRUE); 3131236374Sdougb 3132236374Sdougb return (ISC_FALSE); 3133236374Sdougb} 3134236374Sdougb 3135236374Sdougbstatic void 3136236374Sdougbfctx_destroy(fetchctx_t *fctx) { 3137236374Sdougb isc_sockaddr_t *sa, *next_sa; 3138236374Sdougb 3139236374Sdougb REQUIRE(VALID_FCTX(fctx)); 3140236374Sdougb REQUIRE(fctx->state == fetchstate_done || 3141236374Sdougb fctx->state == fetchstate_init); 3142236374Sdougb REQUIRE(ISC_LIST_EMPTY(fctx->events)); 3143236374Sdougb REQUIRE(ISC_LIST_EMPTY(fctx->queries)); 3144236374Sdougb REQUIRE(ISC_LIST_EMPTY(fctx->finds)); 3145236374Sdougb REQUIRE(ISC_LIST_EMPTY(fctx->altfinds)); 3146236374Sdougb REQUIRE(fctx->pending == 0); 3147236374Sdougb REQUIRE(fctx->references == 0); 3148236374Sdougb REQUIRE(ISC_LIST_EMPTY(fctx->validators)); 3149236374Sdougb REQUIRE(!ISC_LINK_LINKED(fctx, link)); 3150236374Sdougb 3151236374Sdougb FCTXTRACE("destroy"); 3152236374Sdougb 3153135446Strhodes /* 3154135446Strhodes * Free bad. 3155135446Strhodes */ 3156135446Strhodes for (sa = ISC_LIST_HEAD(fctx->bad); 3157135446Strhodes sa != NULL; 3158135446Strhodes sa = next_sa) { 3159135446Strhodes next_sa = ISC_LIST_NEXT(sa, link); 3160135446Strhodes ISC_LIST_UNLINK(fctx->bad, sa, link); 3161236374Sdougb isc_mem_put(fctx->mctx, sa, sizeof(*sa)); 3162135446Strhodes } 3163135446Strhodes 3164170222Sdougb for (sa = ISC_LIST_HEAD(fctx->edns); 3165170222Sdougb sa != NULL; 3166170222Sdougb sa = next_sa) { 3167170222Sdougb next_sa = ISC_LIST_NEXT(sa, link); 3168170222Sdougb ISC_LIST_UNLINK(fctx->edns, sa, link); 3169236374Sdougb isc_mem_put(fctx->mctx, sa, sizeof(*sa)); 3170170222Sdougb } 3171170222Sdougb 3172170222Sdougb for (sa = ISC_LIST_HEAD(fctx->edns512); 3173170222Sdougb sa != NULL; 3174170222Sdougb sa = next_sa) { 3175170222Sdougb next_sa = ISC_LIST_NEXT(sa, link); 3176170222Sdougb ISC_LIST_UNLINK(fctx->edns512, sa, link); 3177236374Sdougb isc_mem_put(fctx->mctx, sa, sizeof(*sa)); 3178170222Sdougb } 3179170222Sdougb 3180218384Sdougb for (sa = ISC_LIST_HEAD(fctx->bad_edns); 3181218384Sdougb sa != NULL; 3182218384Sdougb sa = next_sa) { 3183218384Sdougb next_sa = ISC_LIST_NEXT(sa, link); 3184218384Sdougb ISC_LIST_UNLINK(fctx->bad_edns, sa, link); 3185236374Sdougb isc_mem_put(fctx->mctx, sa, sizeof(*sa)); 3186218384Sdougb } 3187218384Sdougb 3188135446Strhodes isc_timer_detach(&fctx->timer); 3189135446Strhodes dns_message_destroy(&fctx->rmessage); 3190135446Strhodes dns_message_destroy(&fctx->qmessage); 3191135446Strhodes if (dns_name_countlabels(&fctx->domain) > 0) 3192236374Sdougb dns_name_free(&fctx->domain, fctx->mctx); 3193135446Strhodes if (dns_rdataset_isassociated(&fctx->nameservers)) 3194135446Strhodes dns_rdataset_disassociate(&fctx->nameservers); 3195236374Sdougb dns_name_free(&fctx->name, fctx->mctx); 3196135446Strhodes dns_db_detach(&fctx->cache); 3197135446Strhodes dns_adb_detach(&fctx->adb); 3198236374Sdougb isc_mem_free(fctx->mctx, fctx->info); 3199236374Sdougb isc_mem_putanddetach(&fctx->mctx, fctx, sizeof(*fctx)); 3200135446Strhodes} 3201135446Strhodes 3202135446Strhodes/* 3203135446Strhodes * Fetch event handlers. 3204135446Strhodes */ 3205135446Strhodes 3206135446Strhodesstatic void 3207135446Strhodesfctx_timeout(isc_task_t *task, isc_event_t *event) { 3208135446Strhodes fetchctx_t *fctx = event->ev_arg; 3209182645Sdougb isc_timerevent_t *tevent = (isc_timerevent_t *)event; 3210182645Sdougb resquery_t *query; 3211135446Strhodes 3212135446Strhodes REQUIRE(VALID_FCTX(fctx)); 3213135446Strhodes 3214135446Strhodes UNUSED(task); 3215135446Strhodes 3216135446Strhodes FCTXTRACE("timeout"); 3217135446Strhodes 3218193149Sdougb inc_stats(fctx->res, dns_resstatscounter_querytimeout); 3219193149Sdougb 3220135446Strhodes if (event->ev_type == ISC_TIMEREVENT_LIFE) { 3221193149Sdougb fctx->reason = NULL; 3222193149Sdougb fctx_done(fctx, ISC_R_TIMEDOUT, __LINE__); 3223135446Strhodes } else { 3224135446Strhodes isc_result_t result; 3225135446Strhodes 3226135446Strhodes fctx->timeouts++; 3227193149Sdougb fctx->timeout = ISC_TRUE; 3228135446Strhodes /* 3229135446Strhodes * We could cancel the running queries here, or we could let 3230182645Sdougb * them keep going. Since we normally use separate sockets for 3231182645Sdougb * different queries, we adopt the former approach to reduce 3232182645Sdougb * the number of open sockets: cancel the oldest query if it 3233182645Sdougb * expired after the query had started (this is usually the 3234182645Sdougb * case but is not always so, depending on the task schedule 3235182645Sdougb * timing). 3236135446Strhodes */ 3237182645Sdougb query = ISC_LIST_HEAD(fctx->queries); 3238182645Sdougb if (query != NULL && 3239182645Sdougb isc_time_compare(&tevent->due, &query->start) >= 0) { 3240182645Sdougb fctx_cancelquery(&query, NULL, NULL, ISC_TRUE); 3241182645Sdougb } 3242135446Strhodes fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; 3243135446Strhodes /* 3244135446Strhodes * Our timer has triggered. Reestablish the fctx lifetime 3245135446Strhodes * timer. 3246135446Strhodes */ 3247135446Strhodes result = fctx_starttimer(fctx); 3248135446Strhodes if (result != ISC_R_SUCCESS) 3249193149Sdougb fctx_done(fctx, result, __LINE__); 3250135446Strhodes else 3251135446Strhodes /* 3252135446Strhodes * Keep trying. 3253135446Strhodes */ 3254205292Sdougb fctx_try(fctx, ISC_TRUE, ISC_FALSE); 3255135446Strhodes } 3256135446Strhodes 3257135446Strhodes isc_event_free(&event); 3258135446Strhodes} 3259135446Strhodes 3260135446Strhodesstatic void 3261135446Strhodesfctx_shutdown(fetchctx_t *fctx) { 3262135446Strhodes isc_event_t *cevent; 3263135446Strhodes 3264135446Strhodes /* 3265135446Strhodes * Start the shutdown process for fctx, if it isn't already underway. 3266135446Strhodes */ 3267135446Strhodes 3268135446Strhodes FCTXTRACE("shutdown"); 3269135446Strhodes 3270135446Strhodes /* 3271135446Strhodes * The caller must be holding the appropriate bucket lock. 3272135446Strhodes */ 3273135446Strhodes 3274135446Strhodes if (fctx->want_shutdown) 3275135446Strhodes return; 3276135446Strhodes 3277135446Strhodes fctx->want_shutdown = ISC_TRUE; 3278135446Strhodes 3279135446Strhodes /* 3280135446Strhodes * Unless we're still initializing (in which case the 3281135446Strhodes * control event is still outstanding), we need to post 3282135446Strhodes * the control event to tell the fetch we want it to 3283135446Strhodes * exit. 3284135446Strhodes */ 3285135446Strhodes if (fctx->state != fetchstate_init) { 3286135446Strhodes cevent = &fctx->control_event; 3287135446Strhodes isc_task_send(fctx->res->buckets[fctx->bucketnum].task, 3288135446Strhodes &cevent); 3289135446Strhodes } 3290135446Strhodes} 3291135446Strhodes 3292135446Strhodesstatic void 3293135446Strhodesfctx_doshutdown(isc_task_t *task, isc_event_t *event) { 3294135446Strhodes fetchctx_t *fctx = event->ev_arg; 3295135446Strhodes isc_boolean_t bucket_empty = ISC_FALSE; 3296135446Strhodes dns_resolver_t *res; 3297135446Strhodes unsigned int bucketnum; 3298135446Strhodes dns_validator_t *validator; 3299236374Sdougb isc_boolean_t destroy = ISC_FALSE; 3300135446Strhodes 3301135446Strhodes REQUIRE(VALID_FCTX(fctx)); 3302135446Strhodes 3303135446Strhodes UNUSED(task); 3304135446Strhodes 3305135446Strhodes res = fctx->res; 3306135446Strhodes bucketnum = fctx->bucketnum; 3307135446Strhodes 3308135446Strhodes FCTXTRACE("doshutdown"); 3309135446Strhodes 3310135446Strhodes /* 3311135446Strhodes * An fctx that is shutting down is no longer in ADDRWAIT mode. 3312135446Strhodes */ 3313135446Strhodes fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; 3314135446Strhodes 3315135446Strhodes /* 3316135446Strhodes * Cancel all pending validators. Note that this must be done 3317135446Strhodes * without the bucket lock held, since that could cause deadlock. 3318135446Strhodes */ 3319135446Strhodes validator = ISC_LIST_HEAD(fctx->validators); 3320135446Strhodes while (validator != NULL) { 3321135446Strhodes dns_validator_cancel(validator); 3322135446Strhodes validator = ISC_LIST_NEXT(validator, link); 3323135446Strhodes } 3324186462Sdougb 3325135446Strhodes if (fctx->nsfetch != NULL) 3326135446Strhodes dns_resolver_cancelfetch(fctx->nsfetch); 3327135446Strhodes 3328135446Strhodes /* 3329135446Strhodes * Shut down anything that is still running on behalf of this 3330135446Strhodes * fetch. To avoid deadlock with the ADB, we must do this 3331135446Strhodes * before we lock the bucket lock. 3332135446Strhodes */ 3333135446Strhodes fctx_stopeverything(fctx, ISC_FALSE); 3334135446Strhodes 3335135446Strhodes LOCK(&res->buckets[bucketnum].lock); 3336135446Strhodes 3337135446Strhodes fctx->attributes |= FCTX_ATTR_SHUTTINGDOWN; 3338135446Strhodes 3339135446Strhodes INSIST(fctx->state == fetchstate_active || 3340135446Strhodes fctx->state == fetchstate_done); 3341135446Strhodes INSIST(fctx->want_shutdown); 3342135446Strhodes 3343135446Strhodes if (fctx->state != fetchstate_done) { 3344135446Strhodes fctx->state = fetchstate_done; 3345193149Sdougb fctx_sendevents(fctx, ISC_R_CANCELED, __LINE__); 3346135446Strhodes } 3347135446Strhodes 3348135446Strhodes if (fctx->references == 0 && fctx->pending == 0 && 3349236374Sdougb fctx->nqueries == 0 && ISC_LIST_EMPTY(fctx->validators)) { 3350236374Sdougb bucket_empty = fctx_unlink(fctx); 3351236374Sdougb destroy = ISC_TRUE; 3352236374Sdougb } 3353135446Strhodes 3354135446Strhodes UNLOCK(&res->buckets[bucketnum].lock); 3355135446Strhodes 3356236374Sdougb if (destroy) { 3357236374Sdougb fctx_destroy(fctx); 3358236374Sdougb if (bucket_empty) 3359236374Sdougb empty_bucket(res); 3360236374Sdougb } 3361135446Strhodes} 3362135446Strhodes 3363135446Strhodesstatic void 3364135446Strhodesfctx_start(isc_task_t *task, isc_event_t *event) { 3365135446Strhodes fetchctx_t *fctx = event->ev_arg; 3366135446Strhodes isc_boolean_t done = ISC_FALSE, bucket_empty = ISC_FALSE; 3367135446Strhodes dns_resolver_t *res; 3368135446Strhodes unsigned int bucketnum; 3369236374Sdougb isc_boolean_t destroy = ISC_FALSE; 3370135446Strhodes 3371135446Strhodes REQUIRE(VALID_FCTX(fctx)); 3372135446Strhodes 3373135446Strhodes UNUSED(task); 3374135446Strhodes 3375135446Strhodes res = fctx->res; 3376135446Strhodes bucketnum = fctx->bucketnum; 3377135446Strhodes 3378135446Strhodes FCTXTRACE("start"); 3379135446Strhodes 3380135446Strhodes LOCK(&res->buckets[bucketnum].lock); 3381135446Strhodes 3382135446Strhodes INSIST(fctx->state == fetchstate_init); 3383135446Strhodes if (fctx->want_shutdown) { 3384135446Strhodes /* 3385135446Strhodes * We haven't started this fctx yet, and we've been requested 3386135446Strhodes * to shut it down. 3387135446Strhodes */ 3388135446Strhodes fctx->attributes |= FCTX_ATTR_SHUTTINGDOWN; 3389135446Strhodes fctx->state = fetchstate_done; 3390193149Sdougb fctx_sendevents(fctx, ISC_R_CANCELED, __LINE__); 3391135446Strhodes /* 3392135446Strhodes * Since we haven't started, we INSIST that we have no 3393135446Strhodes * pending ADB finds and no pending validations. 3394135446Strhodes */ 3395135446Strhodes INSIST(fctx->pending == 0); 3396166332Sdougb INSIST(fctx->nqueries == 0); 3397135446Strhodes INSIST(ISC_LIST_EMPTY(fctx->validators)); 3398135446Strhodes if (fctx->references == 0) { 3399135446Strhodes /* 3400135446Strhodes * It's now safe to destroy this fctx. 3401135446Strhodes */ 3402236374Sdougb bucket_empty = fctx_unlink(fctx); 3403236374Sdougb destroy = ISC_TRUE; 3404135446Strhodes } 3405135446Strhodes done = ISC_TRUE; 3406135446Strhodes } else { 3407135446Strhodes /* 3408135446Strhodes * Normal fctx startup. 3409135446Strhodes */ 3410135446Strhodes fctx->state = fetchstate_active; 3411275672Sdelphij fctx->totalqueries = 0; 3412135446Strhodes /* 3413135446Strhodes * Reset the control event for later use in shutting down 3414135446Strhodes * the fctx. 3415135446Strhodes */ 3416135446Strhodes ISC_EVENT_INIT(event, sizeof(*event), 0, NULL, 3417135446Strhodes DNS_EVENT_FETCHCONTROL, fctx_doshutdown, fctx, 3418135446Strhodes NULL, NULL, NULL); 3419135446Strhodes } 3420135446Strhodes 3421135446Strhodes UNLOCK(&res->buckets[bucketnum].lock); 3422135446Strhodes 3423135446Strhodes if (!done) { 3424135446Strhodes isc_result_t result; 3425135446Strhodes 3426236374Sdougb INSIST(!destroy); 3427236374Sdougb 3428135446Strhodes /* 3429135446Strhodes * All is well. Start working on the fetch. 3430135446Strhodes */ 3431135446Strhodes result = fctx_starttimer(fctx); 3432135446Strhodes if (result != ISC_R_SUCCESS) 3433193149Sdougb fctx_done(fctx, result, __LINE__); 3434135446Strhodes else 3435205292Sdougb fctx_try(fctx, ISC_FALSE, ISC_FALSE); 3436236374Sdougb } else if (destroy) { 3437236374Sdougb fctx_destroy(fctx); 3438236374Sdougb if (bucket_empty) 3439236374Sdougb empty_bucket(res); 3440236374Sdougb } 3441135446Strhodes} 3442135446Strhodes 3443135446Strhodes/* 3444135446Strhodes * Fetch Creation, Joining, and Cancelation. 3445135446Strhodes */ 3446135446Strhodes 3447135446Strhodesstatic inline isc_result_t 3448170222Sdougbfctx_join(fetchctx_t *fctx, isc_task_t *task, isc_sockaddr_t *client, 3449170222Sdougb dns_messageid_t id, isc_taskaction_t action, void *arg, 3450170222Sdougb dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, 3451135446Strhodes dns_fetch_t *fetch) 3452135446Strhodes{ 3453135446Strhodes isc_task_t *clone; 3454135446Strhodes dns_fetchevent_t *event; 3455135446Strhodes 3456135446Strhodes FCTXTRACE("join"); 3457135446Strhodes 3458135446Strhodes /* 3459135446Strhodes * We store the task we're going to send this event to in the 3460135446Strhodes * sender field. We'll make the fetch the sender when we actually 3461135446Strhodes * send the event. 3462135446Strhodes */ 3463135446Strhodes clone = NULL; 3464135446Strhodes isc_task_attach(task, &clone); 3465135446Strhodes event = (dns_fetchevent_t *) 3466170222Sdougb isc_event_allocate(fctx->res->mctx, clone, DNS_EVENT_FETCHDONE, 3467135446Strhodes action, arg, sizeof(*event)); 3468135446Strhodes if (event == NULL) { 3469135446Strhodes isc_task_detach(&clone); 3470135446Strhodes return (ISC_R_NOMEMORY); 3471135446Strhodes } 3472135446Strhodes event->result = DNS_R_SERVFAIL; 3473135446Strhodes event->qtype = fctx->type; 3474135446Strhodes event->db = NULL; 3475135446Strhodes event->node = NULL; 3476135446Strhodes event->rdataset = rdataset; 3477135446Strhodes event->sigrdataset = sigrdataset; 3478135446Strhodes event->fetch = fetch; 3479170222Sdougb event->client = client; 3480170222Sdougb event->id = id; 3481275672Sdelphij event->qtotal = 0; 3482135446Strhodes dns_fixedname_init(&event->foundname); 3483135446Strhodes 3484135446Strhodes /* 3485135446Strhodes * Make sure that we can store the sigrdataset in the 3486135446Strhodes * first event if it is needed by any of the events. 3487135446Strhodes */ 3488135446Strhodes if (event->sigrdataset != NULL) 3489135446Strhodes ISC_LIST_PREPEND(fctx->events, event, ev_link); 3490135446Strhodes else 3491135446Strhodes ISC_LIST_APPEND(fctx->events, event, ev_link); 3492135446Strhodes fctx->references++; 3493224092Sdougb fctx->client = client; 3494135446Strhodes 3495135446Strhodes fetch->magic = DNS_FETCH_MAGIC; 3496135446Strhodes fetch->private = fctx; 3497135446Strhodes 3498135446Strhodes return (ISC_R_SUCCESS); 3499135446Strhodes} 3500135446Strhodes 3501234010Sdougbstatic inline void 3502234010Sdougblog_ns_ttl(fetchctx_t *fctx, const char *where) { 3503234010Sdougb char namebuf[DNS_NAME_FORMATSIZE]; 3504234010Sdougb char domainbuf[DNS_NAME_FORMATSIZE]; 3505234010Sdougb 3506234010Sdougb dns_name_format(&fctx->name, namebuf, sizeof(namebuf)); 3507234010Sdougb dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf)); 3508234010Sdougb isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, 3509234010Sdougb DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(10), 3510234010Sdougb "log_ns_ttl: fctx %p: %s: %s (in '%s'?): %u %u", 3511234010Sdougb fctx, where, namebuf, domainbuf, 3512234010Sdougb fctx->ns_ttl_ok, fctx->ns_ttl); 3513234010Sdougb} 3514234010Sdougb 3515135446Strhodesstatic isc_result_t 3516135446Strhodesfctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, 3517135446Strhodes dns_name_t *domain, dns_rdataset_t *nameservers, 3518275672Sdelphij unsigned int options, unsigned int bucketnum, unsigned int depth, 3519275672Sdelphij fetchctx_t **fctxp) 3520135446Strhodes{ 3521135446Strhodes fetchctx_t *fctx; 3522149245Sdougb isc_result_t result; 3523135446Strhodes isc_result_t iresult; 3524135446Strhodes isc_interval_t interval; 3525153816Sdougb dns_fixedname_t fixed; 3526135446Strhodes unsigned int findoptions = 0; 3527135446Strhodes char buf[DNS_NAME_FORMATSIZE + DNS_RDATATYPE_FORMATSIZE]; 3528135446Strhodes char typebuf[DNS_RDATATYPE_FORMATSIZE]; 3529135446Strhodes dns_name_t suffix; 3530236374Sdougb isc_mem_t *mctx; 3531135446Strhodes 3532135446Strhodes /* 3533135446Strhodes * Caller must be holding the lock for bucket number 'bucketnum'. 3534135446Strhodes */ 3535135446Strhodes REQUIRE(fctxp != NULL && *fctxp == NULL); 3536135446Strhodes 3537236374Sdougb mctx = res->buckets[bucketnum].mctx; 3538236374Sdougb fctx = isc_mem_get(mctx, sizeof(*fctx)); 3539135446Strhodes if (fctx == NULL) 3540135446Strhodes return (ISC_R_NOMEMORY); 3541135446Strhodes dns_name_format(name, buf, sizeof(buf)); 3542135446Strhodes dns_rdatatype_format(type, typebuf, sizeof(typebuf)); 3543193149Sdougb strcat(buf, "/"); /* checked */ 3544193149Sdougb strcat(buf, typebuf); /* checked */ 3545236374Sdougb fctx->info = isc_mem_strdup(mctx, buf); 3546149245Sdougb if (fctx->info == NULL) { 3547149245Sdougb result = ISC_R_NOMEMORY; 3548135446Strhodes goto cleanup_fetch; 3549149245Sdougb } 3550135446Strhodes FCTXTRACE("create"); 3551135446Strhodes dns_name_init(&fctx->name, NULL); 3552236374Sdougb result = dns_name_dup(name, mctx, &fctx->name); 3553135446Strhodes if (result != ISC_R_SUCCESS) 3554135446Strhodes goto cleanup_info; 3555135446Strhodes dns_name_init(&fctx->domain, NULL); 3556135446Strhodes dns_rdataset_init(&fctx->nameservers); 3557135446Strhodes 3558135446Strhodes fctx->type = type; 3559135446Strhodes fctx->options = options; 3560135446Strhodes /* 3561135446Strhodes * Note! We do not attach to the task. We are relying on the 3562135446Strhodes * resolver to ensure that this task doesn't go away while we are 3563135446Strhodes * using it. 3564135446Strhodes */ 3565135446Strhodes fctx->res = res; 3566135446Strhodes fctx->references = 0; 3567135446Strhodes fctx->bucketnum = bucketnum; 3568135446Strhodes fctx->state = fetchstate_init; 3569135446Strhodes fctx->want_shutdown = ISC_FALSE; 3570135446Strhodes fctx->cloned = ISC_FALSE; 3571275672Sdelphij fctx->depth = depth; 3572135446Strhodes ISC_LIST_INIT(fctx->queries); 3573135446Strhodes ISC_LIST_INIT(fctx->finds); 3574135446Strhodes ISC_LIST_INIT(fctx->altfinds); 3575135446Strhodes ISC_LIST_INIT(fctx->forwaddrs); 3576135446Strhodes ISC_LIST_INIT(fctx->altaddrs); 3577135446Strhodes ISC_LIST_INIT(fctx->forwarders); 3578135446Strhodes fctx->fwdpolicy = dns_fwdpolicy_none; 3579135446Strhodes ISC_LIST_INIT(fctx->bad); 3580170222Sdougb ISC_LIST_INIT(fctx->edns); 3581170222Sdougb ISC_LIST_INIT(fctx->edns512); 3582218384Sdougb ISC_LIST_INIT(fctx->bad_edns); 3583135446Strhodes ISC_LIST_INIT(fctx->validators); 3584174187Sdougb fctx->validator = NULL; 3585135446Strhodes fctx->find = NULL; 3586135446Strhodes fctx->altfind = NULL; 3587135446Strhodes fctx->pending = 0; 3588135446Strhodes fctx->restarts = 0; 3589193149Sdougb fctx->querysent = 0; 3590275672Sdelphij fctx->totalqueries = 0; 3591193149Sdougb fctx->referrals = 0; 3592193149Sdougb TIME_NOW(&fctx->start); 3593135446Strhodes fctx->timeouts = 0; 3594193149Sdougb fctx->lamecount = 0; 3595193149Sdougb fctx->adberr = 0; 3596193149Sdougb fctx->neterr = 0; 3597193149Sdougb fctx->badresp = 0; 3598193149Sdougb fctx->findfail = 0; 3599193149Sdougb fctx->valfail = 0; 3600193149Sdougb fctx->result = ISC_R_FAILURE; 3601193149Sdougb fctx->vresult = ISC_R_SUCCESS; 3602193149Sdougb fctx->exitline = -1; /* sentinel */ 3603193149Sdougb fctx->logged = ISC_FALSE; 3604135446Strhodes fctx->attributes = 0; 3605170222Sdougb fctx->spilled = ISC_FALSE; 3606166332Sdougb fctx->nqueries = 0; 3607193149Sdougb fctx->reason = NULL; 3608193149Sdougb fctx->rand_buf = 0; 3609193149Sdougb fctx->rand_bits = 0; 3610193149Sdougb fctx->timeout = ISC_FALSE; 3611224092Sdougb fctx->addrinfo = NULL; 3612224092Sdougb fctx->client = NULL; 3613234010Sdougb fctx->ns_ttl = 0; 3614234010Sdougb fctx->ns_ttl_ok = ISC_FALSE; 3615135446Strhodes 3616135446Strhodes dns_name_init(&fctx->nsname, NULL); 3617135446Strhodes fctx->nsfetch = NULL; 3618135446Strhodes dns_rdataset_init(&fctx->nsrrset); 3619135446Strhodes 3620135446Strhodes if (domain == NULL) { 3621135446Strhodes dns_forwarders_t *forwarders = NULL; 3622135446Strhodes unsigned int labels; 3623204619Sdougb dns_name_t *fwdname = name; 3624135446Strhodes 3625135446Strhodes /* 3626135446Strhodes * DS records are found in the parent server. 3627135446Strhodes * Strip label to get the correct forwarder (if any). 3628135446Strhodes */ 3629204619Sdougb if (dns_rdatatype_atparent(fctx->type) && 3630135446Strhodes dns_name_countlabels(name) > 1) { 3631135446Strhodes dns_name_init(&suffix, NULL); 3632135446Strhodes labels = dns_name_countlabels(name); 3633135446Strhodes dns_name_getlabelsequence(name, 1, labels - 1, &suffix); 3634204619Sdougb fwdname = &suffix; 3635135446Strhodes } 3636153816Sdougb dns_fixedname_init(&fixed); 3637153816Sdougb domain = dns_fixedname_name(&fixed); 3638204619Sdougb result = dns_fwdtable_find2(fctx->res->view->fwdtable, fwdname, 3639153816Sdougb domain, &forwarders); 3640135446Strhodes if (result == ISC_R_SUCCESS) 3641135446Strhodes fctx->fwdpolicy = forwarders->fwdpolicy; 3642135446Strhodes 3643135446Strhodes if (fctx->fwdpolicy != dns_fwdpolicy_only) { 3644135446Strhodes /* 3645135446Strhodes * The caller didn't supply a query domain and 3646135446Strhodes * nameservers, and we're not in forward-only mode, 3647135446Strhodes * so find the best nameservers to use. 3648135446Strhodes */ 3649204619Sdougb if (dns_rdatatype_atparent(fctx->type)) 3650135446Strhodes findoptions |= DNS_DBFIND_NOEXACT; 3651262706Serwin result = dns_view_findzonecut(res->view, fwdname, 3652262706Serwin domain, 0, findoptions, 3653262706Serwin ISC_TRUE, 3654135446Strhodes &fctx->nameservers, 3655135446Strhodes NULL); 3656135446Strhodes if (result != ISC_R_SUCCESS) 3657135446Strhodes goto cleanup_name; 3658262706Serwin 3659236374Sdougb result = dns_name_dup(domain, mctx, &fctx->domain); 3660135446Strhodes if (result != ISC_R_SUCCESS) { 3661135446Strhodes dns_rdataset_disassociate(&fctx->nameservers); 3662135446Strhodes goto cleanup_name; 3663135446Strhodes } 3664234010Sdougb fctx->ns_ttl = fctx->nameservers.ttl; 3665234010Sdougb fctx->ns_ttl_ok = ISC_TRUE; 3666135446Strhodes } else { 3667135446Strhodes /* 3668153816Sdougb * We're in forward-only mode. Set the query domain. 3669135446Strhodes */ 3670236374Sdougb result = dns_name_dup(domain, mctx, &fctx->domain); 3671135446Strhodes if (result != ISC_R_SUCCESS) 3672135446Strhodes goto cleanup_name; 3673135446Strhodes } 3674135446Strhodes } else { 3675236374Sdougb result = dns_name_dup(domain, mctx, &fctx->domain); 3676135446Strhodes if (result != ISC_R_SUCCESS) 3677135446Strhodes goto cleanup_name; 3678135446Strhodes dns_rdataset_clone(nameservers, &fctx->nameservers); 3679234010Sdougb fctx->ns_ttl = fctx->nameservers.ttl; 3680234010Sdougb fctx->ns_ttl_ok = ISC_TRUE; 3681135446Strhodes } 3682135446Strhodes 3683234010Sdougb log_ns_ttl(fctx, "fctx_create"); 3684234010Sdougb 3685135446Strhodes INSIST(dns_name_issubdomain(&fctx->name, &fctx->domain)); 3686135446Strhodes 3687135446Strhodes fctx->qmessage = NULL; 3688236374Sdougb result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, 3689135446Strhodes &fctx->qmessage); 3690135446Strhodes 3691135446Strhodes if (result != ISC_R_SUCCESS) 3692135446Strhodes goto cleanup_domain; 3693135446Strhodes 3694135446Strhodes fctx->rmessage = NULL; 3695236374Sdougb result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, 3696135446Strhodes &fctx->rmessage); 3697135446Strhodes 3698135446Strhodes if (result != ISC_R_SUCCESS) 3699135446Strhodes goto cleanup_qmessage; 3700135446Strhodes 3701135446Strhodes /* 3702135446Strhodes * Compute an expiration time for the entire fetch. 3703135446Strhodes */ 3704224092Sdougb isc_interval_set(&interval, res->query_timeout, 0); 3705135446Strhodes iresult = isc_time_nowplusinterval(&fctx->expires, &interval); 3706135446Strhodes if (iresult != ISC_R_SUCCESS) { 3707135446Strhodes UNEXPECTED_ERROR(__FILE__, __LINE__, 3708135446Strhodes "isc_time_nowplusinterval: %s", 3709135446Strhodes isc_result_totext(iresult)); 3710135446Strhodes result = ISC_R_UNEXPECTED; 3711135446Strhodes goto cleanup_rmessage; 3712135446Strhodes } 3713135446Strhodes 3714135446Strhodes /* 3715135446Strhodes * Default retry interval initialization. We set the interval now 3716135446Strhodes * mostly so it won't be uninitialized. It will be set to the 3717135446Strhodes * correct value before a query is issued. 3718135446Strhodes */ 3719135446Strhodes isc_interval_set(&fctx->interval, 2, 0); 3720135446Strhodes 3721135446Strhodes /* 3722135446Strhodes * Create an inactive timer. It will be made active when the fetch 3723135446Strhodes * is actually started. 3724135446Strhodes */ 3725135446Strhodes fctx->timer = NULL; 3726135446Strhodes iresult = isc_timer_create(res->timermgr, isc_timertype_inactive, 3727135446Strhodes NULL, NULL, 3728135446Strhodes res->buckets[bucketnum].task, fctx_timeout, 3729135446Strhodes fctx, &fctx->timer); 3730135446Strhodes if (iresult != ISC_R_SUCCESS) { 3731135446Strhodes UNEXPECTED_ERROR(__FILE__, __LINE__, 3732135446Strhodes "isc_timer_create: %s", 3733135446Strhodes isc_result_totext(iresult)); 3734135446Strhodes result = ISC_R_UNEXPECTED; 3735135446Strhodes goto cleanup_rmessage; 3736135446Strhodes } 3737135446Strhodes 3738135446Strhodes /* 3739135446Strhodes * Attach to the view's cache and adb. 3740135446Strhodes */ 3741135446Strhodes fctx->cache = NULL; 3742135446Strhodes dns_db_attach(res->view->cachedb, &fctx->cache); 3743135446Strhodes fctx->adb = NULL; 3744135446Strhodes dns_adb_attach(res->view->adb, &fctx->adb); 3745236374Sdougb fctx->mctx = NULL; 3746236374Sdougb isc_mem_attach(mctx, &fctx->mctx); 3747135446Strhodes 3748135446Strhodes ISC_LIST_INIT(fctx->events); 3749135446Strhodes ISC_LINK_INIT(fctx, link); 3750135446Strhodes fctx->magic = FCTX_MAGIC; 3751135446Strhodes 3752135446Strhodes ISC_LIST_APPEND(res->buckets[bucketnum].fctxs, fctx, link); 3753135446Strhodes 3754135446Strhodes LOCK(&res->nlock); 3755135446Strhodes res->nfctx++; 3756135446Strhodes UNLOCK(&res->nlock); 3757135446Strhodes 3758135446Strhodes *fctxp = fctx; 3759135446Strhodes 3760135446Strhodes return (ISC_R_SUCCESS); 3761135446Strhodes 3762135446Strhodes cleanup_rmessage: 3763135446Strhodes dns_message_destroy(&fctx->rmessage); 3764135446Strhodes 3765135446Strhodes cleanup_qmessage: 3766135446Strhodes dns_message_destroy(&fctx->qmessage); 3767135446Strhodes 3768135446Strhodes cleanup_domain: 3769135446Strhodes if (dns_name_countlabels(&fctx->domain) > 0) 3770236374Sdougb dns_name_free(&fctx->domain, mctx); 3771135446Strhodes if (dns_rdataset_isassociated(&fctx->nameservers)) 3772135446Strhodes dns_rdataset_disassociate(&fctx->nameservers); 3773135446Strhodes 3774135446Strhodes cleanup_name: 3775236374Sdougb dns_name_free(&fctx->name, mctx); 3776135446Strhodes 3777135446Strhodes cleanup_info: 3778236374Sdougb isc_mem_free(mctx, fctx->info); 3779135446Strhodes 3780135446Strhodes cleanup_fetch: 3781236374Sdougb isc_mem_put(mctx, fctx, sizeof(*fctx)); 3782135446Strhodes 3783135446Strhodes return (result); 3784135446Strhodes} 3785135446Strhodes 3786135446Strhodes/* 3787135446Strhodes * Handle Responses 3788135446Strhodes */ 3789135446Strhodesstatic inline isc_boolean_t 3790135446Strhodesis_lame(fetchctx_t *fctx) { 3791135446Strhodes dns_message_t *message = fctx->rmessage; 3792135446Strhodes dns_name_t *name; 3793135446Strhodes dns_rdataset_t *rdataset; 3794135446Strhodes isc_result_t result; 3795135446Strhodes 3796135446Strhodes if (message->rcode != dns_rcode_noerror && 3797135446Strhodes message->rcode != dns_rcode_nxdomain) 3798135446Strhodes return (ISC_FALSE); 3799135446Strhodes 3800135446Strhodes if (message->counts[DNS_SECTION_ANSWER] != 0) 3801135446Strhodes return (ISC_FALSE); 3802135446Strhodes 3803135446Strhodes if (message->counts[DNS_SECTION_AUTHORITY] == 0) 3804135446Strhodes return (ISC_FALSE); 3805135446Strhodes 3806135446Strhodes result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); 3807135446Strhodes while (result == ISC_R_SUCCESS) { 3808135446Strhodes name = NULL; 3809135446Strhodes dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name); 3810135446Strhodes for (rdataset = ISC_LIST_HEAD(name->list); 3811135446Strhodes rdataset != NULL; 3812135446Strhodes rdataset = ISC_LIST_NEXT(rdataset, link)) { 3813135446Strhodes dns_namereln_t namereln; 3814135446Strhodes int order; 3815135446Strhodes unsigned int labels; 3816135446Strhodes if (rdataset->type != dns_rdatatype_ns) 3817135446Strhodes continue; 3818135446Strhodes namereln = dns_name_fullcompare(name, &fctx->domain, 3819174187Sdougb &order, &labels); 3820135446Strhodes if (namereln == dns_namereln_equal && 3821135446Strhodes (message->flags & DNS_MESSAGEFLAG_AA) != 0) 3822135446Strhodes return (ISC_FALSE); 3823135446Strhodes if (namereln == dns_namereln_subdomain) 3824135446Strhodes return (ISC_FALSE); 3825135446Strhodes return (ISC_TRUE); 3826135446Strhodes } 3827135446Strhodes result = dns_message_nextname(message, DNS_SECTION_AUTHORITY); 3828135446Strhodes } 3829135446Strhodes 3830135446Strhodes return (ISC_FALSE); 3831135446Strhodes} 3832135446Strhodes 3833135446Strhodesstatic inline void 3834135446Strhodeslog_lame(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo) { 3835135446Strhodes char namebuf[DNS_NAME_FORMATSIZE]; 3836186462Sdougb char domainbuf[DNS_NAME_FORMATSIZE]; 3837135446Strhodes char addrbuf[ISC_SOCKADDR_FORMATSIZE]; 3838186462Sdougb 3839135446Strhodes dns_name_format(&fctx->name, namebuf, sizeof(namebuf)); 3840135446Strhodes dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf)); 3841135446Strhodes isc_sockaddr_format(&addrinfo->sockaddr, addrbuf, sizeof(addrbuf)); 3842135446Strhodes isc_log_write(dns_lctx, DNS_LOGCATEGORY_LAME_SERVERS, 3843135446Strhodes DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO, 3844135446Strhodes "lame server resolving '%s' (in '%s'?): %s", 3845135446Strhodes namebuf, domainbuf, addrbuf); 3846135446Strhodes} 3847135446Strhodes 3848224092Sdougbstatic inline void 3849224092Sdougblog_formerr(fetchctx_t *fctx, const char *format, ...) { 3850224092Sdougb char nsbuf[ISC_SOCKADDR_FORMATSIZE]; 3851224092Sdougb char clbuf[ISC_SOCKADDR_FORMATSIZE]; 3852224092Sdougb const char *clmsg = ""; 3853224092Sdougb char msgbuf[2048]; 3854224092Sdougb va_list args; 3855224092Sdougb 3856224092Sdougb va_start(args, format); 3857224092Sdougb vsnprintf(msgbuf, sizeof(msgbuf), format, args); 3858224092Sdougb va_end(args); 3859224092Sdougb 3860224092Sdougb isc_sockaddr_format(&fctx->addrinfo->sockaddr, nsbuf, sizeof(nsbuf)); 3861224092Sdougb 3862224092Sdougb if (fctx->client != NULL) { 3863224092Sdougb clmsg = " for client "; 3864224092Sdougb isc_sockaddr_format(fctx->client, clbuf, sizeof(clbuf)); 3865224092Sdougb } else { 3866224092Sdougb clbuf[0] = '\0'; 3867224092Sdougb } 3868224092Sdougb 3869224092Sdougb isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, 3870224092Sdougb DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE, 3871224092Sdougb "DNS format error from %s resolving %s%s%s: %s", 3872224092Sdougb nsbuf, fctx->info, clmsg, clbuf, msgbuf); 3873224092Sdougb} 3874224092Sdougb 3875135446Strhodesstatic inline isc_result_t 3876135446Strhodessame_question(fetchctx_t *fctx) { 3877135446Strhodes isc_result_t result; 3878135446Strhodes dns_message_t *message = fctx->rmessage; 3879135446Strhodes dns_name_t *name; 3880135446Strhodes dns_rdataset_t *rdataset; 3881135446Strhodes 3882135446Strhodes /* 3883135446Strhodes * Caller must be holding the fctx lock. 3884135446Strhodes */ 3885135446Strhodes 3886135446Strhodes /* 3887135446Strhodes * XXXRTH Currently we support only one question. 3888135446Strhodes */ 3889224092Sdougb if (message->counts[DNS_SECTION_QUESTION] != 1) { 3890224092Sdougb log_formerr(fctx, "too many questions"); 3891135446Strhodes return (DNS_R_FORMERR); 3892224092Sdougb } 3893135446Strhodes 3894135446Strhodes result = dns_message_firstname(message, DNS_SECTION_QUESTION); 3895135446Strhodes if (result != ISC_R_SUCCESS) 3896135446Strhodes return (result); 3897135446Strhodes name = NULL; 3898135446Strhodes dns_message_currentname(message, DNS_SECTION_QUESTION, &name); 3899135446Strhodes rdataset = ISC_LIST_HEAD(name->list); 3900135446Strhodes INSIST(rdataset != NULL); 3901135446Strhodes INSIST(ISC_LIST_NEXT(rdataset, link) == NULL); 3902224092Sdougb 3903135446Strhodes if (fctx->type != rdataset->type || 3904135446Strhodes fctx->res->rdclass != rdataset->rdclass || 3905224092Sdougb !dns_name_equal(&fctx->name, name)) { 3906224092Sdougb char namebuf[DNS_NAME_FORMATSIZE]; 3907224092Sdougb char class[DNS_RDATACLASS_FORMATSIZE]; 3908224092Sdougb char type[DNS_RDATATYPE_FORMATSIZE]; 3909224092Sdougb 3910224092Sdougb dns_name_format(name, namebuf, sizeof(namebuf)); 3911224092Sdougb dns_rdataclass_format(rdataset->rdclass, class, sizeof(class)); 3912224092Sdougb dns_rdatatype_format(rdataset->type, type, sizeof(type)); 3913224092Sdougb log_formerr(fctx, "question section mismatch: got %s/%s/%s", 3914224092Sdougb namebuf, class, type); 3915135446Strhodes return (DNS_R_FORMERR); 3916224092Sdougb } 3917135446Strhodes 3918135446Strhodes return (ISC_R_SUCCESS); 3919135446Strhodes} 3920135446Strhodes 3921135446Strhodesstatic void 3922135446Strhodesclone_results(fetchctx_t *fctx) { 3923135446Strhodes dns_fetchevent_t *event, *hevent; 3924135446Strhodes isc_result_t result; 3925135446Strhodes dns_name_t *name, *hname; 3926135446Strhodes 3927135446Strhodes FCTXTRACE("clone_results"); 3928135446Strhodes 3929135446Strhodes /* 3930135446Strhodes * Set up any other events to have the same data as the first 3931135446Strhodes * event. 3932135446Strhodes * 3933135446Strhodes * Caller must be holding the appropriate lock. 3934135446Strhodes */ 3935135446Strhodes 3936135446Strhodes fctx->cloned = ISC_TRUE; 3937135446Strhodes hevent = ISC_LIST_HEAD(fctx->events); 3938135446Strhodes if (hevent == NULL) 3939135446Strhodes return; 3940135446Strhodes hname = dns_fixedname_name(&hevent->foundname); 3941135446Strhodes for (event = ISC_LIST_NEXT(hevent, ev_link); 3942135446Strhodes event != NULL; 3943135446Strhodes event = ISC_LIST_NEXT(event, ev_link)) { 3944135446Strhodes name = dns_fixedname_name(&event->foundname); 3945135446Strhodes result = dns_name_copy(hname, name, NULL); 3946135446Strhodes if (result != ISC_R_SUCCESS) 3947135446Strhodes event->result = result; 3948135446Strhodes else 3949135446Strhodes event->result = hevent->result; 3950135446Strhodes dns_db_attach(hevent->db, &event->db); 3951135446Strhodes dns_db_attachnode(hevent->db, hevent->node, &event->node); 3952135446Strhodes INSIST(hevent->rdataset != NULL); 3953135446Strhodes INSIST(event->rdataset != NULL); 3954135446Strhodes if (dns_rdataset_isassociated(hevent->rdataset)) 3955135446Strhodes dns_rdataset_clone(hevent->rdataset, event->rdataset); 3956135446Strhodes INSIST(! (hevent->sigrdataset == NULL && 3957135446Strhodes event->sigrdataset != NULL)); 3958135446Strhodes if (hevent->sigrdataset != NULL && 3959135446Strhodes dns_rdataset_isassociated(hevent->sigrdataset) && 3960135446Strhodes event->sigrdataset != NULL) 3961135446Strhodes dns_rdataset_clone(hevent->sigrdataset, 3962135446Strhodes event->sigrdataset); 3963135446Strhodes } 3964135446Strhodes} 3965135446Strhodes 3966193149Sdougb#define CACHE(r) (((r)->attributes & DNS_RDATASETATTR_CACHE) != 0) 3967193149Sdougb#define ANSWER(r) (((r)->attributes & DNS_RDATASETATTR_ANSWER) != 0) 3968193149Sdougb#define ANSWERSIG(r) (((r)->attributes & DNS_RDATASETATTR_ANSWERSIG) != 0) 3969193149Sdougb#define EXTERNAL(r) (((r)->attributes & DNS_RDATASETATTR_EXTERNAL) != 0) 3970193149Sdougb#define CHAINING(r) (((r)->attributes & DNS_RDATASETATTR_CHAINING) != 0) 3971193149Sdougb#define CHASE(r) (((r)->attributes & DNS_RDATASETATTR_CHASE) != 0) 3972193149Sdougb#define CHECKNAMES(r) (((r)->attributes & DNS_RDATASETATTR_CHECKNAMES) != 0) 3973135446Strhodes 3974135446Strhodes 3975135446Strhodes/* 3976135446Strhodes * Destroy '*fctx' if it is ready to be destroyed (i.e., if it has 3977234010Sdougb * no references and is no longer waiting for any events). 3978135446Strhodes * 3979135446Strhodes * Requires: 3980193149Sdougb * '*fctx' is shutting down. 3981234010Sdougb * 3982234010Sdougb * Returns: 3983234010Sdougb * true if the resolver is exiting and this is the last fctx in the bucket. 3984135446Strhodes */ 3985234010Sdougbstatic isc_boolean_t 3986234010Sdougbmaybe_destroy(fetchctx_t *fctx, isc_boolean_t locked) { 3987135446Strhodes unsigned int bucketnum; 3988135446Strhodes isc_boolean_t bucket_empty = ISC_FALSE; 3989135446Strhodes dns_resolver_t *res = fctx->res; 3990174187Sdougb dns_validator_t *validator, *next_validator; 3991236374Sdougb isc_boolean_t destroy = ISC_FALSE; 3992135446Strhodes 3993135446Strhodes REQUIRE(SHUTTINGDOWN(fctx)); 3994135446Strhodes 3995234010Sdougb bucketnum = fctx->bucketnum; 3996234010Sdougb if (!locked) 3997234010Sdougb LOCK(&res->buckets[bucketnum].lock); 3998166332Sdougb if (fctx->pending != 0 || fctx->nqueries != 0) 3999234010Sdougb goto unlock; 4000135446Strhodes 4001166332Sdougb for (validator = ISC_LIST_HEAD(fctx->validators); 4002174187Sdougb validator != NULL; validator = next_validator) { 4003174187Sdougb next_validator = ISC_LIST_NEXT(validator, link); 4004174187Sdougb dns_validator_cancel(validator); 4005166332Sdougb } 4006166332Sdougb 4007236374Sdougb if (fctx->references == 0 && ISC_LIST_EMPTY(fctx->validators)) { 4008236374Sdougb bucket_empty = fctx_unlink(fctx); 4009236374Sdougb destroy = ISC_TRUE; 4010236374Sdougb } 4011234010Sdougb unlock: 4012234010Sdougb if (!locked) 4013234010Sdougb UNLOCK(&res->buckets[bucketnum].lock); 4014236374Sdougb if (destroy) 4015236374Sdougb fctx_destroy(fctx); 4016234010Sdougb return (bucket_empty); 4017135446Strhodes} 4018135446Strhodes 4019135446Strhodes/* 4020135446Strhodes * The validator has finished. 4021135446Strhodes */ 4022135446Strhodesstatic void 4023135446Strhodesvalidated(isc_task_t *task, isc_event_t *event) { 4024234010Sdougb dns_adbaddrinfo_t *addrinfo; 4025234010Sdougb dns_dbnode_t *node = NULL; 4026234010Sdougb dns_dbnode_t *nsnode = NULL; 4027135446Strhodes dns_fetchevent_t *hevent; 4028234010Sdougb dns_name_t *name; 4029135446Strhodes dns_rdataset_t *ardataset = NULL; 4030135446Strhodes dns_rdataset_t *asigrdataset = NULL; 4031234010Sdougb dns_rdataset_t *rdataset; 4032234010Sdougb dns_rdataset_t *sigrdataset; 4033234010Sdougb dns_resolver_t *res; 4034234010Sdougb dns_valarg_t *valarg; 4035234010Sdougb dns_validatorevent_t *vevent; 4036234010Sdougb fetchctx_t *fctx; 4037234010Sdougb isc_boolean_t chaining; 4038135446Strhodes isc_boolean_t negative; 4039135446Strhodes isc_boolean_t sentresponse; 4040234010Sdougb isc_result_t eresult = ISC_R_SUCCESS; 4041234010Sdougb isc_result_t result = ISC_R_SUCCESS; 4042234010Sdougb isc_stdtime_t now; 4043135446Strhodes isc_uint32_t ttl; 4044135446Strhodes 4045135446Strhodes UNUSED(task); /* for now */ 4046135446Strhodes 4047135446Strhodes REQUIRE(event->ev_type == DNS_EVENT_VALIDATORDONE); 4048153816Sdougb valarg = event->ev_arg; 4049153816Sdougb fctx = valarg->fctx; 4050234010Sdougb res = fctx->res; 4051153816Sdougb addrinfo = valarg->addrinfo; 4052135446Strhodes REQUIRE(VALID_FCTX(fctx)); 4053135446Strhodes REQUIRE(!ISC_LIST_EMPTY(fctx->validators)); 4054135446Strhodes 4055135446Strhodes vevent = (dns_validatorevent_t *)event; 4056224092Sdougb fctx->vresult = vevent->result; 4057135446Strhodes 4058135446Strhodes FCTXTRACE("received validation completion event"); 4059135446Strhodes 4060234010Sdougb LOCK(&res->buckets[fctx->bucketnum].lock); 4061234010Sdougb 4062135446Strhodes ISC_LIST_UNLINK(fctx->validators, vevent->validator, link); 4063174187Sdougb fctx->validator = NULL; 4064135446Strhodes 4065135446Strhodes /* 4066135446Strhodes * Destroy the validator early so that we can 4067135446Strhodes * destroy the fctx if necessary. 4068135446Strhodes */ 4069135446Strhodes dns_validator_destroy(&vevent->validator); 4070236374Sdougb isc_mem_put(fctx->mctx, valarg, sizeof(*valarg)); 4071135446Strhodes 4072135446Strhodes negative = ISC_TF(vevent->rdataset == NULL); 4073135446Strhodes 4074135446Strhodes sentresponse = ISC_TF((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0); 4075135446Strhodes 4076135446Strhodes /* 4077135446Strhodes * If shutting down, ignore the results. Check to see if we're 4078135446Strhodes * done waiting for validator completions and ADB pending events; if 4079135446Strhodes * so, destroy the fctx. 4080135446Strhodes */ 4081135446Strhodes if (SHUTTINGDOWN(fctx) && !sentresponse) { 4082234010Sdougb isc_uint32_t bucketnum = fctx->bucketnum; 4083234010Sdougb isc_boolean_t bucket_empty; 4084234010Sdougb bucket_empty = maybe_destroy(fctx, ISC_TRUE); 4085234010Sdougb UNLOCK(&res->buckets[bucketnum].lock); 4086234010Sdougb if (bucket_empty) 4087234010Sdougb empty_bucket(res); 4088135446Strhodes goto cleanup_event; 4089135446Strhodes } 4090135446Strhodes 4091205292Sdougb isc_stdtime_get(&now); 4092205292Sdougb 4093135446Strhodes /* 4094135446Strhodes * If chaining, we need to make sure that the right result code is 4095135446Strhodes * returned, and that the rdatasets are bound. 4096135446Strhodes */ 4097135446Strhodes if (vevent->result == ISC_R_SUCCESS && 4098135446Strhodes !negative && 4099135446Strhodes vevent->rdataset != NULL && 4100135446Strhodes CHAINING(vevent->rdataset)) 4101135446Strhodes { 4102135446Strhodes if (vevent->rdataset->type == dns_rdatatype_cname) 4103135446Strhodes eresult = DNS_R_CNAME; 4104135446Strhodes else { 4105135446Strhodes INSIST(vevent->rdataset->type == dns_rdatatype_dname); 4106135446Strhodes eresult = DNS_R_DNAME; 4107135446Strhodes } 4108135446Strhodes chaining = ISC_TRUE; 4109135446Strhodes } else 4110135446Strhodes chaining = ISC_FALSE; 4111135446Strhodes 4112135446Strhodes /* 4113135446Strhodes * Either we're not shutting down, or we are shutting down but want 4114135446Strhodes * to cache the result anyway (if this was a validation started by 4115135446Strhodes * a query with cd set) 4116135446Strhodes */ 4117135446Strhodes 4118135446Strhodes hevent = ISC_LIST_HEAD(fctx->events); 4119135446Strhodes if (hevent != NULL) { 4120135446Strhodes if (!negative && !chaining && 4121135446Strhodes (fctx->type == dns_rdatatype_any || 4122162079Sdougb fctx->type == dns_rdatatype_rrsig || 4123162079Sdougb fctx->type == dns_rdatatype_sig)) { 4124135446Strhodes /* 4125135446Strhodes * Don't bind rdatasets; the caller 4126135446Strhodes * will iterate the node. 4127135446Strhodes */ 4128135446Strhodes } else { 4129135446Strhodes ardataset = hevent->rdataset; 4130135446Strhodes asigrdataset = hevent->sigrdataset; 4131135446Strhodes } 4132135446Strhodes } 4133135446Strhodes 4134135446Strhodes if (vevent->result != ISC_R_SUCCESS) { 4135135446Strhodes FCTXTRACE("validation failed"); 4136234010Sdougb inc_stats(res, dns_resstatscounter_valfail); 4137193149Sdougb fctx->valfail++; 4138193149Sdougb fctx->vresult = vevent->result; 4139205292Sdougb if (fctx->vresult != DNS_R_BROKENCHAIN) { 4140205292Sdougb result = ISC_R_NOTFOUND; 4141205292Sdougb if (vevent->rdataset != NULL) 4142205292Sdougb result = dns_db_findnode(fctx->cache, 4143205292Sdougb vevent->name, 4144205292Sdougb ISC_TRUE, &node); 4145205292Sdougb if (result == ISC_R_SUCCESS) 4146205292Sdougb (void)dns_db_deleterdataset(fctx->cache, node, 4147205292Sdougb NULL, 4148205292Sdougb vevent->type, 0); 4149205292Sdougb if (result == ISC_R_SUCCESS && 4150205292Sdougb vevent->sigrdataset != NULL) 4151205292Sdougb (void)dns_db_deleterdataset(fctx->cache, node, 4152205292Sdougb NULL, 4153205292Sdougb dns_rdatatype_rrsig, 4154205292Sdougb vevent->type); 4155205292Sdougb if (result == ISC_R_SUCCESS) 4156205292Sdougb dns_db_detachnode(fctx->cache, &node); 4157205292Sdougb } 4158205292Sdougb if (fctx->vresult == DNS_R_BROKENCHAIN && !negative) { 4159205292Sdougb /* 4160205292Sdougb * Cache the data as pending for later validation. 4161205292Sdougb */ 4162205292Sdougb result = ISC_R_NOTFOUND; 4163205292Sdougb if (vevent->rdataset != NULL) 4164205292Sdougb result = dns_db_findnode(fctx->cache, 4165205292Sdougb vevent->name, 4166205292Sdougb ISC_TRUE, &node); 4167205292Sdougb if (result == ISC_R_SUCCESS) { 4168205292Sdougb (void)dns_db_addrdataset(fctx->cache, node, 4169205292Sdougb NULL, now, 4170205292Sdougb vevent->rdataset, 0, 4171205292Sdougb NULL); 4172205292Sdougb } 4173205292Sdougb if (result == ISC_R_SUCCESS && 4174205292Sdougb vevent->sigrdataset != NULL) 4175205292Sdougb (void)dns_db_addrdataset(fctx->cache, node, 4176205292Sdougb NULL, now, 4177205292Sdougb vevent->sigrdataset, 4178205292Sdougb 0, NULL); 4179205292Sdougb if (result == ISC_R_SUCCESS) 4180205292Sdougb dns_db_detachnode(fctx->cache, &node); 4181205292Sdougb } 4182205292Sdougb result = fctx->vresult; 4183193149Sdougb add_bad(fctx, addrinfo, result, badns_validation); 4184153816Sdougb isc_event_free(&event); 4185234010Sdougb UNLOCK(&res->buckets[fctx->bucketnum].lock); 4186174187Sdougb INSIST(fctx->validator == NULL); 4187174187Sdougb fctx->validator = ISC_LIST_HEAD(fctx->validators); 4188205292Sdougb if (fctx->validator != NULL) 4189174187Sdougb dns_validator_send(fctx->validator); 4190205292Sdougb else if (sentresponse) 4191193149Sdougb fctx_done(fctx, result, __LINE__); /* Locks bucket. */ 4192205292Sdougb else if (result == DNS_R_BROKENCHAIN) { 4193205292Sdougb isc_result_t tresult; 4194205292Sdougb isc_time_t expire; 4195205292Sdougb isc_interval_t i; 4196205292Sdougb 4197205292Sdougb isc_interval_set(&i, DNS_BADCACHE_TTL(fctx), 0); 4198205292Sdougb tresult = isc_time_nowplusinterval(&expire, &i); 4199205292Sdougb if (negative && 4200205292Sdougb (fctx->type == dns_rdatatype_dnskey || 4201205292Sdougb fctx->type == dns_rdatatype_dlv || 4202205292Sdougb fctx->type == dns_rdatatype_ds) && 4203205292Sdougb tresult == ISC_R_SUCCESS) 4204234010Sdougb dns_resolver_addbadcache(res, &fctx->name, 4205205292Sdougb fctx->type, &expire); 4206205292Sdougb fctx_done(fctx, result, __LINE__); /* Locks bucket. */ 4207205292Sdougb } else 4208205292Sdougb fctx_try(fctx, ISC_TRUE, ISC_TRUE); /* Locks bucket. */ 4209153816Sdougb return; 4210135446Strhodes } 4211135446Strhodes 4212135446Strhodes 4213135446Strhodes if (negative) { 4214135446Strhodes dns_rdatatype_t covers; 4215135446Strhodes FCTXTRACE("nonexistence validation OK"); 4216135446Strhodes 4217234010Sdougb inc_stats(res, dns_resstatscounter_valnegsuccess); 4218193149Sdougb 4219135446Strhodes if (fctx->rmessage->rcode == dns_rcode_nxdomain) 4220135446Strhodes covers = dns_rdatatype_any; 4221135446Strhodes else 4222135446Strhodes covers = fctx->type; 4223135446Strhodes 4224135446Strhodes result = dns_db_findnode(fctx->cache, vevent->name, ISC_TRUE, 4225135446Strhodes &node); 4226135446Strhodes if (result != ISC_R_SUCCESS) 4227135446Strhodes goto noanswer_response; 4228135446Strhodes 4229135446Strhodes /* 4230135446Strhodes * If we are asking for a SOA record set the cache time 4231135446Strhodes * to zero to facilitate locating the containing zone of 4232193149Sdougb * a arbitrary zone. 4233135446Strhodes */ 4234234010Sdougb ttl = res->view->maxncachettl; 4235135446Strhodes if (fctx->type == dns_rdatatype_soa && 4236234010Sdougb covers == dns_rdatatype_any && res->zero_no_soa_ttl) 4237135446Strhodes ttl = 0; 4238135446Strhodes 4239135446Strhodes result = ncache_adderesult(fctx->rmessage, fctx->cache, node, 4240193149Sdougb covers, now, ttl, vevent->optout, 4241254402Serwin vevent->secure, ardataset, &eresult); 4242135446Strhodes if (result != ISC_R_SUCCESS) 4243135446Strhodes goto noanswer_response; 4244135446Strhodes goto answer_response; 4245193149Sdougb } else 4246234010Sdougb inc_stats(res, dns_resstatscounter_valsuccess); 4247135446Strhodes 4248135446Strhodes FCTXTRACE("validation OK"); 4249135446Strhodes 4250135446Strhodes if (vevent->proofs[DNS_VALIDATOR_NOQNAMEPROOF] != NULL) { 4251135446Strhodes result = dns_rdataset_addnoqname(vevent->rdataset, 4252135446Strhodes vevent->proofs[DNS_VALIDATOR_NOQNAMEPROOF]); 4253135446Strhodes RUNTIME_CHECK(result == ISC_R_SUCCESS); 4254165071Sdougb INSIST(vevent->sigrdataset != NULL); 4255135446Strhodes vevent->sigrdataset->ttl = vevent->rdataset->ttl; 4256193149Sdougb if (vevent->proofs[DNS_VALIDATOR_CLOSESTENCLOSER] != NULL) { 4257193149Sdougb result = dns_rdataset_addclosest(vevent->rdataset, 4258193149Sdougb vevent->proofs[DNS_VALIDATOR_CLOSESTENCLOSER]); 4259193149Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 4260193149Sdougb } 4261254402Serwin } else if (vevent->rdataset->trust == dns_trust_answer && 4262254402Serwin vevent->rdataset->type != dns_rdatatype_rrsig) 4263254402Serwin { 4264254402Serwin isc_result_t tresult; 4265254402Serwin dns_name_t *noqname = NULL; 4266254402Serwin tresult = findnoqname(fctx, vevent->name, 4267254402Serwin vevent->rdataset->type, &noqname); 4268254402Serwin if (tresult == ISC_R_SUCCESS && noqname != NULL) { 4269254402Serwin tresult = dns_rdataset_addnoqname(vevent->rdataset, 4270254402Serwin noqname); 4271254402Serwin RUNTIME_CHECK(tresult == ISC_R_SUCCESS); 4272254402Serwin } 4273135446Strhodes } 4274135446Strhodes 4275135446Strhodes /* 4276135446Strhodes * The data was already cached as pending data. 4277135446Strhodes * Re-cache it as secure and bind the cached 4278135446Strhodes * rdatasets to the first event on the fetch 4279135446Strhodes * event list. 4280135446Strhodes */ 4281135446Strhodes result = dns_db_findnode(fctx->cache, vevent->name, ISC_TRUE, &node); 4282135446Strhodes if (result != ISC_R_SUCCESS) 4283135446Strhodes goto noanswer_response; 4284135446Strhodes 4285135446Strhodes result = dns_db_addrdataset(fctx->cache, node, NULL, now, 4286135446Strhodes vevent->rdataset, 0, ardataset); 4287135446Strhodes if (result != ISC_R_SUCCESS && 4288135446Strhodes result != DNS_R_UNCHANGED) 4289135446Strhodes goto noanswer_response; 4290223812Sdougb if (ardataset != NULL && NEGATIVE(ardataset)) { 4291174187Sdougb if (NXDOMAIN(ardataset)) 4292174187Sdougb eresult = DNS_R_NCACHENXDOMAIN; 4293174187Sdougb else 4294174187Sdougb eresult = DNS_R_NCACHENXRRSET; 4295174187Sdougb } else if (vevent->sigrdataset != NULL) { 4296135446Strhodes result = dns_db_addrdataset(fctx->cache, node, NULL, now, 4297135446Strhodes vevent->sigrdataset, 0, 4298135446Strhodes asigrdataset); 4299135446Strhodes if (result != ISC_R_SUCCESS && 4300135446Strhodes result != DNS_R_UNCHANGED) 4301135446Strhodes goto noanswer_response; 4302135446Strhodes } 4303135446Strhodes 4304135446Strhodes if (sentresponse) { 4305234010Sdougb isc_boolean_t bucket_empty = ISC_FALSE; 4306135446Strhodes /* 4307135446Strhodes * If we only deferred the destroy because we wanted to cache 4308135446Strhodes * the data, destroy now. 4309135446Strhodes */ 4310174187Sdougb dns_db_detachnode(fctx->cache, &node); 4311135446Strhodes if (SHUTTINGDOWN(fctx)) 4312234010Sdougb bucket_empty = maybe_destroy(fctx, ISC_TRUE); 4313234010Sdougb UNLOCK(&res->buckets[fctx->bucketnum].lock); 4314234010Sdougb if (bucket_empty) 4315234010Sdougb empty_bucket(res); 4316135446Strhodes goto cleanup_event; 4317135446Strhodes } 4318135446Strhodes 4319135446Strhodes if (!ISC_LIST_EMPTY(fctx->validators)) { 4320135446Strhodes INSIST(!negative); 4321135446Strhodes INSIST(fctx->type == dns_rdatatype_any || 4322162079Sdougb fctx->type == dns_rdatatype_rrsig || 4323162079Sdougb fctx->type == dns_rdatatype_sig); 4324135446Strhodes /* 4325135446Strhodes * Don't send a response yet - we have 4326135446Strhodes * more rdatasets that still need to 4327135446Strhodes * be validated. 4328135446Strhodes */ 4329174187Sdougb dns_db_detachnode(fctx->cache, &node); 4330234010Sdougb UNLOCK(&res->buckets[fctx->bucketnum].lock); 4331166332Sdougb dns_validator_send(ISC_LIST_HEAD(fctx->validators)); 4332135446Strhodes goto cleanup_event; 4333135446Strhodes } 4334135446Strhodes 4335135446Strhodes answer_response: 4336135446Strhodes /* 4337135446Strhodes * Cache any NS/NSEC records that happened to be validated. 4338135446Strhodes */ 4339135446Strhodes result = dns_message_firstname(fctx->rmessage, DNS_SECTION_AUTHORITY); 4340135446Strhodes while (result == ISC_R_SUCCESS) { 4341135446Strhodes name = NULL; 4342135446Strhodes dns_message_currentname(fctx->rmessage, DNS_SECTION_AUTHORITY, 4343135446Strhodes &name); 4344135446Strhodes for (rdataset = ISC_LIST_HEAD(name->list); 4345135446Strhodes rdataset != NULL; 4346135446Strhodes rdataset = ISC_LIST_NEXT(rdataset, link)) { 4347135446Strhodes if ((rdataset->type != dns_rdatatype_ns && 4348135446Strhodes rdataset->type != dns_rdatatype_nsec) || 4349135446Strhodes rdataset->trust != dns_trust_secure) 4350135446Strhodes continue; 4351135446Strhodes for (sigrdataset = ISC_LIST_HEAD(name->list); 4352135446Strhodes sigrdataset != NULL; 4353135446Strhodes sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) { 4354135446Strhodes if (sigrdataset->type != dns_rdatatype_rrsig || 4355135446Strhodes sigrdataset->covers != rdataset->type) 4356135446Strhodes continue; 4357135446Strhodes break; 4358135446Strhodes } 4359135446Strhodes if (sigrdataset == NULL || 4360135446Strhodes sigrdataset->trust != dns_trust_secure) 4361135446Strhodes continue; 4362135446Strhodes result = dns_db_findnode(fctx->cache, name, ISC_TRUE, 4363135446Strhodes &nsnode); 4364135446Strhodes if (result != ISC_R_SUCCESS) 4365135446Strhodes continue; 4366135446Strhodes 4367135446Strhodes result = dns_db_addrdataset(fctx->cache, nsnode, NULL, 4368135446Strhodes now, rdataset, 0, NULL); 4369135446Strhodes if (result == ISC_R_SUCCESS) 4370135446Strhodes result = dns_db_addrdataset(fctx->cache, nsnode, 4371135446Strhodes NULL, now, 4372135446Strhodes sigrdataset, 0, 4373135446Strhodes NULL); 4374135446Strhodes dns_db_detachnode(fctx->cache, &nsnode); 4375225361Sdougb if (result != ISC_R_SUCCESS) 4376225361Sdougb continue; 4377135446Strhodes } 4378135446Strhodes result = dns_message_nextname(fctx->rmessage, 4379135446Strhodes DNS_SECTION_AUTHORITY); 4380135446Strhodes } 4381135446Strhodes 4382135446Strhodes result = ISC_R_SUCCESS; 4383135446Strhodes 4384135446Strhodes /* 4385135446Strhodes * Respond with an answer, positive or negative, 4386135446Strhodes * as opposed to an error. 'node' must be non-NULL. 4387135446Strhodes */ 4388135446Strhodes 4389135446Strhodes fctx->attributes |= FCTX_ATTR_HAVEANSWER; 4390135446Strhodes 4391135446Strhodes if (hevent != NULL) { 4392254402Serwin /* 4393254402Serwin * Negative results must be indicated in event->result. 4394254402Serwin */ 4395254402Serwin if (dns_rdataset_isassociated(hevent->rdataset) && 4396254402Serwin NEGATIVE(hevent->rdataset)) { 4397254402Serwin INSIST(eresult == DNS_R_NCACHENXDOMAIN || 4398254402Serwin eresult == DNS_R_NCACHENXRRSET); 4399254402Serwin } 4400135446Strhodes hevent->result = eresult; 4401135446Strhodes RUNTIME_CHECK(dns_name_copy(vevent->name, 4402135446Strhodes dns_fixedname_name(&hevent->foundname), NULL) 4403135446Strhodes == ISC_R_SUCCESS); 4404135446Strhodes dns_db_attach(fctx->cache, &hevent->db); 4405174187Sdougb dns_db_transfernode(fctx->cache, &node, &hevent->node); 4406135446Strhodes clone_results(fctx); 4407135446Strhodes } 4408135446Strhodes 4409135446Strhodes noanswer_response: 4410135446Strhodes if (node != NULL) 4411135446Strhodes dns_db_detachnode(fctx->cache, &node); 4412135446Strhodes 4413234010Sdougb UNLOCK(&res->buckets[fctx->bucketnum].lock); 4414193149Sdougb fctx_done(fctx, result, __LINE__); /* Locks bucket. */ 4415165071Sdougb 4416135446Strhodes cleanup_event: 4417174187Sdougb INSIST(node == NULL); 4418135446Strhodes isc_event_free(&event); 4419135446Strhodes} 4420135446Strhodes 4421254402Serwinstatic void 4422254402Serwinfctx_log(void *arg, int level, const char *fmt, ...) { 4423254402Serwin char msgbuf[2048]; 4424254402Serwin va_list args; 4425254402Serwin fetchctx_t *fctx = arg; 4426254402Serwin 4427254402Serwin va_start(args, fmt); 4428254402Serwin vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); 4429254402Serwin va_end(args); 4430254402Serwin 4431254402Serwin isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, 4432254402Serwin DNS_LOGMODULE_RESOLVER, level, 4433254402Serwin "fctx %p(%s): %s", fctx, fctx->info, msgbuf); 4434254402Serwin} 4435254402Serwin 4436135446Strhodesstatic inline isc_result_t 4437254402Serwinfindnoqname(fetchctx_t *fctx, dns_name_t *name, dns_rdatatype_t type, 4438254402Serwin dns_name_t **noqnamep) 4439254402Serwin{ 4440254402Serwin dns_rdataset_t *nrdataset, *next, *sigrdataset; 4441254402Serwin dns_rdata_rrsig_t rrsig; 4442254402Serwin isc_result_t result; 4443254402Serwin unsigned int labels; 4444254402Serwin dns_section_t section; 4445254402Serwin dns_name_t *zonename; 4446254402Serwin dns_fixedname_t fzonename; 4447254402Serwin dns_name_t *closest; 4448254402Serwin dns_fixedname_t fclosest; 4449254402Serwin dns_name_t *nearest; 4450254402Serwin dns_fixedname_t fnearest; 4451254402Serwin dns_rdatatype_t found = dns_rdatatype_none; 4452254402Serwin dns_name_t *noqname = NULL; 4453254402Serwin 4454254402Serwin FCTXTRACE("findnoqname"); 4455254402Serwin 4456254402Serwin REQUIRE(noqnamep != NULL && *noqnamep == NULL); 4457254402Serwin 4458254402Serwin /* 4459254402Serwin * Find the SIG for this rdataset, if we have it. 4460254402Serwin */ 4461254402Serwin for (sigrdataset = ISC_LIST_HEAD(name->list); 4462254402Serwin sigrdataset != NULL; 4463254402Serwin sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) { 4464254402Serwin if (sigrdataset->type == dns_rdatatype_rrsig && 4465254402Serwin sigrdataset->covers == type) 4466254402Serwin break; 4467254402Serwin } 4468254402Serwin 4469254402Serwin if (sigrdataset == NULL) 4470254402Serwin return (ISC_R_NOTFOUND); 4471254402Serwin 4472254402Serwin labels = dns_name_countlabels(name); 4473254402Serwin 4474254402Serwin for (result = dns_rdataset_first(sigrdataset); 4475254402Serwin result == ISC_R_SUCCESS; 4476254402Serwin result = dns_rdataset_next(sigrdataset)) { 4477254402Serwin dns_rdata_t rdata = DNS_RDATA_INIT; 4478254402Serwin dns_rdataset_current(sigrdataset, &rdata); 4479254402Serwin result = dns_rdata_tostruct(&rdata, &rrsig, NULL); 4480254402Serwin RUNTIME_CHECK(result == ISC_R_SUCCESS); 4481254402Serwin /* Wildcard has rrsig.labels < labels - 1. */ 4482254402Serwin if (rrsig.labels + 1U >= labels) 4483254402Serwin continue; 4484254402Serwin break; 4485254402Serwin } 4486254402Serwin 4487254402Serwin if (result == ISC_R_NOMORE) 4488254402Serwin return (ISC_R_NOTFOUND); 4489254402Serwin if (result != ISC_R_SUCCESS) 4490254402Serwin return (result); 4491254402Serwin 4492254402Serwin dns_fixedname_init(&fzonename); 4493254402Serwin zonename = dns_fixedname_name(&fzonename); 4494254402Serwin dns_fixedname_init(&fclosest); 4495254402Serwin closest = dns_fixedname_name(&fclosest); 4496254402Serwin dns_fixedname_init(&fnearest); 4497254402Serwin nearest = dns_fixedname_name(&fnearest); 4498254402Serwin 4499254402Serwin#define NXND(x) ((x) == ISC_R_SUCCESS) 4500254402Serwin 4501254402Serwin section = DNS_SECTION_AUTHORITY; 4502254402Serwin for (result = dns_message_firstname(fctx->rmessage, section); 4503254402Serwin result == ISC_R_SUCCESS; 4504254402Serwin result = dns_message_nextname(fctx->rmessage, section)) { 4505254402Serwin dns_name_t *nsec = NULL; 4506254402Serwin dns_message_currentname(fctx->rmessage, section, &nsec); 4507254402Serwin for (nrdataset = ISC_LIST_HEAD(nsec->list); 4508254402Serwin nrdataset != NULL; nrdataset = next) { 4509254402Serwin isc_boolean_t data = ISC_FALSE, exists = ISC_FALSE; 4510254402Serwin isc_boolean_t optout = ISC_FALSE, unknown = ISC_FALSE; 4511254402Serwin isc_boolean_t setclosest = ISC_FALSE; 4512254402Serwin isc_boolean_t setnearest = ISC_FALSE; 4513254402Serwin 4514254402Serwin next = ISC_LIST_NEXT(nrdataset, link); 4515254402Serwin if (nrdataset->type != dns_rdatatype_nsec && 4516254402Serwin nrdataset->type != dns_rdatatype_nsec3) 4517254402Serwin continue; 4518254402Serwin 4519254402Serwin if (nrdataset->type == dns_rdatatype_nsec && 4520254402Serwin NXND(dns_nsec_noexistnodata(type, name, nsec, 4521254402Serwin nrdataset, &exists, 4522254402Serwin &data, NULL, fctx_log, 4523254402Serwin fctx))) 4524254402Serwin { 4525254402Serwin if (!exists) { 4526254402Serwin noqname = nsec; 4527254402Serwin found = dns_rdatatype_nsec; 4528254402Serwin } 4529254402Serwin } 4530254402Serwin 4531254402Serwin if (nrdataset->type == dns_rdatatype_nsec3 && 4532254402Serwin NXND(dns_nsec3_noexistnodata(type, name, nsec, 4533254402Serwin nrdataset, zonename, 4534254402Serwin &exists, &data, 4535254402Serwin &optout, &unknown, 4536254402Serwin &setclosest, 4537254402Serwin &setnearest, 4538254402Serwin closest, nearest, 4539254402Serwin fctx_log, fctx))) 4540254402Serwin { 4541254402Serwin if (!exists && setnearest) { 4542254402Serwin noqname = nsec; 4543254402Serwin found = dns_rdatatype_nsec3; 4544254402Serwin } 4545254402Serwin } 4546254402Serwin } 4547254402Serwin } 4548254402Serwin if (result == ISC_R_NOMORE) 4549254402Serwin result = ISC_R_SUCCESS; 4550254402Serwin if (noqname != NULL) { 4551254402Serwin for (sigrdataset = ISC_LIST_HEAD(noqname->list); 4552254402Serwin sigrdataset != NULL; 4553254402Serwin sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) { 4554254402Serwin if (sigrdataset->type == dns_rdatatype_rrsig && 4555254402Serwin sigrdataset->covers == found) 4556254402Serwin break; 4557254402Serwin } 4558254402Serwin if (sigrdataset != NULL) 4559254402Serwin *noqnamep = noqname; 4560254402Serwin } 4561254402Serwin return (result); 4562254402Serwin} 4563254402Serwin 4564254402Serwinstatic inline isc_result_t 4565153816Sdougbcache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo, 4566174187Sdougb isc_stdtime_t now) 4567174187Sdougb{ 4568135446Strhodes dns_rdataset_t *rdataset, *sigrdataset; 4569135446Strhodes dns_rdataset_t *addedrdataset, *ardataset, *asigrdataset; 4570135446Strhodes dns_rdataset_t *valrdataset = NULL, *valsigrdataset = NULL; 4571135446Strhodes dns_dbnode_t *node, **anodep; 4572135446Strhodes dns_db_t **adbp; 4573135446Strhodes dns_name_t *aname; 4574135446Strhodes dns_resolver_t *res; 4575135446Strhodes isc_boolean_t need_validation, secure_domain, have_answer; 4576135446Strhodes isc_result_t result, eresult; 4577135446Strhodes dns_fetchevent_t *event; 4578135446Strhodes unsigned int options; 4579135446Strhodes isc_task_t *task; 4580135446Strhodes isc_boolean_t fail; 4581153816Sdougb unsigned int valoptions = 0; 4582135446Strhodes 4583135446Strhodes /* 4584135446Strhodes * The appropriate bucket lock must be held. 4585135446Strhodes */ 4586135446Strhodes 4587135446Strhodes res = fctx->res; 4588135446Strhodes need_validation = ISC_FALSE; 4589225361Sdougb POST(need_validation); 4590135446Strhodes secure_domain = ISC_FALSE; 4591135446Strhodes have_answer = ISC_FALSE; 4592135446Strhodes eresult = ISC_R_SUCCESS; 4593135446Strhodes task = res->buckets[fctx->bucketnum].task; 4594135446Strhodes 4595135446Strhodes /* 4596135446Strhodes * Is DNSSEC validation required for this name? 4597135446Strhodes */ 4598170222Sdougb if (res->view->enablevalidation) { 4599224092Sdougb result = dns_view_issecuredomain(res->view, name, 4600224092Sdougb &secure_domain); 4601170222Sdougb if (result != ISC_R_SUCCESS) 4602170222Sdougb return (result); 4603135446Strhodes 4604170222Sdougb if (!secure_domain && res->view->dlv != NULL) { 4605170222Sdougb valoptions = DNS_VALIDATOR_DLV; 4606170222Sdougb secure_domain = ISC_TRUE; 4607170222Sdougb } 4608153816Sdougb } 4609135446Strhodes 4610135446Strhodes if ((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0) 4611135446Strhodes need_validation = ISC_FALSE; 4612135446Strhodes else 4613135446Strhodes need_validation = secure_domain; 4614135446Strhodes 4615135446Strhodes adbp = NULL; 4616135446Strhodes aname = NULL; 4617135446Strhodes anodep = NULL; 4618135446Strhodes ardataset = NULL; 4619135446Strhodes asigrdataset = NULL; 4620135446Strhodes event = NULL; 4621135446Strhodes if ((name->attributes & DNS_NAMEATTR_ANSWER) != 0 && 4622135446Strhodes !need_validation) { 4623135446Strhodes have_answer = ISC_TRUE; 4624135446Strhodes event = ISC_LIST_HEAD(fctx->events); 4625135446Strhodes if (event != NULL) { 4626135446Strhodes adbp = &event->db; 4627135446Strhodes aname = dns_fixedname_name(&event->foundname); 4628135446Strhodes result = dns_name_copy(name, aname, NULL); 4629135446Strhodes if (result != ISC_R_SUCCESS) 4630135446Strhodes return (result); 4631135446Strhodes anodep = &event->node; 4632135446Strhodes /* 4633162079Sdougb * If this is an ANY, SIG or RRSIG query, we're not 4634162079Sdougb * going to return any rdatasets, unless we encountered 4635135446Strhodes * a CNAME or DNAME as "the answer". In this case, 4636135446Strhodes * we're going to return DNS_R_CNAME or DNS_R_DNAME 4637135446Strhodes * and we must set up the rdatasets. 4638135446Strhodes */ 4639135446Strhodes if ((fctx->type != dns_rdatatype_any && 4640162079Sdougb fctx->type != dns_rdatatype_rrsig && 4641162079Sdougb fctx->type != dns_rdatatype_sig) || 4642135446Strhodes (name->attributes & DNS_NAMEATTR_CHAINING) != 0) { 4643135446Strhodes ardataset = event->rdataset; 4644135446Strhodes asigrdataset = event->sigrdataset; 4645135446Strhodes } 4646135446Strhodes } 4647135446Strhodes } 4648135446Strhodes 4649135446Strhodes /* 4650135446Strhodes * Find or create the cache node. 4651135446Strhodes */ 4652135446Strhodes node = NULL; 4653135446Strhodes result = dns_db_findnode(fctx->cache, name, ISC_TRUE, &node); 4654135446Strhodes if (result != ISC_R_SUCCESS) 4655135446Strhodes return (result); 4656135446Strhodes 4657135446Strhodes /* 4658135446Strhodes * Cache or validate each cacheable rdataset. 4659135446Strhodes */ 4660153816Sdougb fail = ISC_TF((fctx->res->options & DNS_RESOLVER_CHECKNAMESFAIL) != 0); 4661135446Strhodes for (rdataset = ISC_LIST_HEAD(name->list); 4662135446Strhodes rdataset != NULL; 4663135446Strhodes rdataset = ISC_LIST_NEXT(rdataset, link)) { 4664135446Strhodes if (!CACHE(rdataset)) 4665135446Strhodes continue; 4666135446Strhodes if (CHECKNAMES(rdataset)) { 4667135446Strhodes char namebuf[DNS_NAME_FORMATSIZE]; 4668135446Strhodes char typebuf[DNS_RDATATYPE_FORMATSIZE]; 4669135446Strhodes char classbuf[DNS_RDATATYPE_FORMATSIZE]; 4670135446Strhodes 4671135446Strhodes dns_name_format(name, namebuf, sizeof(namebuf)); 4672135446Strhodes dns_rdatatype_format(rdataset->type, typebuf, 4673135446Strhodes sizeof(typebuf)); 4674135446Strhodes dns_rdataclass_format(rdataset->rdclass, classbuf, 4675135446Strhodes sizeof(classbuf)); 4676186462Sdougb isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, 4677135446Strhodes DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE, 4678186462Sdougb "check-names %s %s/%s/%s", 4679135446Strhodes fail ? "failure" : "warning", 4680135446Strhodes namebuf, typebuf, classbuf); 4681135446Strhodes if (fail) { 4682174187Sdougb if (ANSWER(rdataset)) { 4683174187Sdougb dns_db_detachnode(fctx->cache, &node); 4684135446Strhodes return (DNS_R_BADNAME); 4685174187Sdougb } 4686135446Strhodes continue; 4687135446Strhodes } 4688135446Strhodes } 4689135446Strhodes 4690135446Strhodes /* 4691135446Strhodes * Enforce the configure maximum cache TTL. 4692135446Strhodes */ 4693135446Strhodes if (rdataset->ttl > res->view->maxcachettl) 4694135446Strhodes rdataset->ttl = res->view->maxcachettl; 4695135446Strhodes 4696135446Strhodes /* 4697254402Serwin * Find the SIG for this rdataset, if we have it. 4698254402Serwin */ 4699254402Serwin for (sigrdataset = ISC_LIST_HEAD(name->list); 4700254402Serwin sigrdataset != NULL; 4701254402Serwin sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) { 4702254402Serwin if (sigrdataset->type == dns_rdatatype_rrsig && 4703254402Serwin sigrdataset->covers == rdataset->type) 4704254402Serwin break; 4705254402Serwin } 4706254402Serwin 4707254402Serwin /* 4708202961Sdougb * If this RRset is in a secure domain, is in bailiwick, 4709202961Sdougb * and is not glue, attempt DNSSEC validation. (We do not 4710202961Sdougb * attempt to validate glue or out-of-bailiwick data--even 4711202961Sdougb * though there might be some performance benefit to doing 4712202961Sdougb * so--because it makes it simpler and safer to ensure that 4713202961Sdougb * records from a secure domain are only cached if validated 4714202961Sdougb * within the context of a query to the domain that owns 4715202961Sdougb * them.) 4716135446Strhodes */ 4717202961Sdougb if (secure_domain && rdataset->trust != dns_trust_glue && 4718202961Sdougb !EXTERNAL(rdataset)) { 4719199958Sdougb dns_trust_t trust; 4720202961Sdougb 4721135446Strhodes /* 4722162079Sdougb * RRSIGs are validated as part of validating the 4723135446Strhodes * type they cover. 4724135446Strhodes */ 4725135446Strhodes if (rdataset->type == dns_rdatatype_rrsig) 4726135446Strhodes continue; 4727254402Serwin 4728135446Strhodes if (sigrdataset == NULL) { 4729135446Strhodes if (!ANSWER(rdataset) && need_validation) { 4730135446Strhodes /* 4731135446Strhodes * Ignore non-answer rdatasets that 4732135446Strhodes * are missing signatures. 4733135446Strhodes */ 4734135446Strhodes continue; 4735135446Strhodes } 4736135446Strhodes } 4737135446Strhodes 4738135446Strhodes /* 4739135446Strhodes * Normalize the rdataset and sigrdataset TTLs. 4740135446Strhodes */ 4741135446Strhodes if (sigrdataset != NULL) { 4742135446Strhodes rdataset->ttl = ISC_MIN(rdataset->ttl, 4743135446Strhodes sigrdataset->ttl); 4744135446Strhodes sigrdataset->ttl = rdataset->ttl; 4745135446Strhodes } 4746135446Strhodes 4747135446Strhodes /* 4748135446Strhodes * Cache this rdataset/sigrdataset pair as 4749199958Sdougb * pending data. Track whether it was additional 4750199958Sdougb * or not. 4751135446Strhodes */ 4752199958Sdougb if (rdataset->trust == dns_trust_additional) 4753199958Sdougb trust = dns_trust_pending_additional; 4754199958Sdougb else 4755199958Sdougb trust = dns_trust_pending_answer; 4756199958Sdougb 4757199958Sdougb rdataset->trust = trust; 4758135446Strhodes if (sigrdataset != NULL) 4759199958Sdougb sigrdataset->trust = trust; 4760193149Sdougb if (!need_validation || !ANSWER(rdataset)) { 4761254402Serwin if (ANSWER(rdataset) && 4762254402Serwin rdataset->type != dns_rdatatype_rrsig) { 4763254402Serwin isc_result_t tresult; 4764254402Serwin dns_name_t *noqname = NULL; 4765254402Serwin tresult = findnoqname(fctx, name, 4766254402Serwin rdataset->type, 4767254402Serwin &noqname); 4768254402Serwin if (tresult == ISC_R_SUCCESS && 4769254402Serwin noqname != NULL) { 4770254402Serwin tresult = 4771254402Serwin dns_rdataset_addnoqname( 4772254402Serwin rdataset, noqname); 4773254402Serwin RUNTIME_CHECK(tresult == 4774254402Serwin ISC_R_SUCCESS); 4775254402Serwin } 4776254402Serwin } 4777135446Strhodes addedrdataset = ardataset; 4778193149Sdougb result = dns_db_addrdataset(fctx->cache, node, 4779193149Sdougb NULL, now, rdataset, 4780193149Sdougb 0, addedrdataset); 4781193149Sdougb if (result == DNS_R_UNCHANGED) { 4782193149Sdougb result = ISC_R_SUCCESS; 4783193149Sdougb if (!need_validation && 4784193149Sdougb ardataset != NULL && 4785223812Sdougb NEGATIVE(ardataset)) { 4786193149Sdougb /* 4787193149Sdougb * The answer in the cache is 4788193149Sdougb * better than the answer we 4789193149Sdougb * found, and is a negative 4790193149Sdougb * cache entry, so we must set 4791193149Sdougb * eresult appropriately. 4792193149Sdougb */ 4793193149Sdougb if (NXDOMAIN(ardataset)) 4794193149Sdougb eresult = 4795193149Sdougb DNS_R_NCACHENXDOMAIN; 4796193149Sdougb else 4797193149Sdougb eresult = 4798193149Sdougb DNS_R_NCACHENXRRSET; 4799193149Sdougb /* 4800193149Sdougb * We have a negative response 4801193149Sdougb * from the cache so don't 4802193149Sdougb * attempt to add the RRSIG 4803193149Sdougb * rrset. 4804193149Sdougb */ 4805193149Sdougb continue; 4806193149Sdougb } 4807174187Sdougb } 4808135446Strhodes if (result != ISC_R_SUCCESS) 4809135446Strhodes break; 4810193149Sdougb if (sigrdataset != NULL) { 4811193149Sdougb addedrdataset = asigrdataset; 4812193149Sdougb result = dns_db_addrdataset(fctx->cache, 4813193149Sdougb node, NULL, now, 4814193149Sdougb sigrdataset, 0, 4815193149Sdougb addedrdataset); 4816193149Sdougb if (result == DNS_R_UNCHANGED) 4817193149Sdougb result = ISC_R_SUCCESS; 4818193149Sdougb if (result != ISC_R_SUCCESS) 4819193149Sdougb break; 4820193149Sdougb } else if (!ANSWER(rdataset)) 4821193149Sdougb continue; 4822193149Sdougb } 4823135446Strhodes 4824135446Strhodes if (ANSWER(rdataset) && need_validation) { 4825135446Strhodes if (fctx->type != dns_rdatatype_any && 4826162079Sdougb fctx->type != dns_rdatatype_rrsig && 4827162079Sdougb fctx->type != dns_rdatatype_sig) { 4828135446Strhodes /* 4829135446Strhodes * This is The Answer. We will 4830135446Strhodes * validate it, but first we cache 4831135446Strhodes * the rest of the response - it may 4832135446Strhodes * contain useful keys. 4833135446Strhodes */ 4834135446Strhodes INSIST(valrdataset == NULL && 4835135446Strhodes valsigrdataset == NULL); 4836135446Strhodes valrdataset = rdataset; 4837135446Strhodes valsigrdataset = sigrdataset; 4838135446Strhodes } else { 4839135446Strhodes /* 4840135446Strhodes * This is one of (potentially) 4841135446Strhodes * multiple answers to an ANY 4842135446Strhodes * or SIG query. To keep things 4843135446Strhodes * simple, we just start the 4844135446Strhodes * validator right away rather 4845135446Strhodes * than caching first and 4846135446Strhodes * having to remember which 4847135446Strhodes * rdatasets needed validation. 4848135446Strhodes */ 4849153816Sdougb result = valcreate(fctx, addrinfo, 4850153816Sdougb name, rdataset->type, 4851153816Sdougb rdataset, 4852153816Sdougb sigrdataset, 4853153816Sdougb valoptions, task); 4854135446Strhodes } 4855143731Sdougb } else if (CHAINING(rdataset)) { 4856143731Sdougb if (rdataset->type == dns_rdatatype_cname) 4857143731Sdougb eresult = DNS_R_CNAME; 4858143731Sdougb else { 4859143731Sdougb INSIST(rdataset->type == 4860143731Sdougb dns_rdatatype_dname); 4861143731Sdougb eresult = DNS_R_DNAME; 4862143731Sdougb } 4863135446Strhodes } 4864135446Strhodes } else if (!EXTERNAL(rdataset)) { 4865135446Strhodes /* 4866135446Strhodes * It's OK to cache this rdataset now. 4867135446Strhodes */ 4868135446Strhodes if (ANSWER(rdataset)) 4869135446Strhodes addedrdataset = ardataset; 4870135446Strhodes else if (ANSWERSIG(rdataset)) 4871135446Strhodes addedrdataset = asigrdataset; 4872135446Strhodes else 4873135446Strhodes addedrdataset = NULL; 4874135446Strhodes if (CHAINING(rdataset)) { 4875135446Strhodes if (rdataset->type == dns_rdatatype_cname) 4876135446Strhodes eresult = DNS_R_CNAME; 4877135446Strhodes else { 4878135446Strhodes INSIST(rdataset->type == 4879135446Strhodes dns_rdatatype_dname); 4880135446Strhodes eresult = DNS_R_DNAME; 4881135446Strhodes } 4882135446Strhodes } 4883135446Strhodes if (rdataset->trust == dns_trust_glue && 4884135446Strhodes (rdataset->type == dns_rdatatype_ns || 4885135446Strhodes (rdataset->type == dns_rdatatype_rrsig && 4886135446Strhodes rdataset->covers == dns_rdatatype_ns))) { 4887135446Strhodes /* 4888135446Strhodes * If the trust level is 'dns_trust_glue' 4889135446Strhodes * then we are adding data from a referral 4890135446Strhodes * we got while executing the search algorithm. 4891135446Strhodes * New referral data always takes precedence 4892135446Strhodes * over the existing cache contents. 4893135446Strhodes */ 4894135446Strhodes options = DNS_DBADD_FORCE; 4895135446Strhodes } else 4896135446Strhodes options = 0; 4897254402Serwin 4898254402Serwin if (ANSWER(rdataset) && 4899254402Serwin rdataset->type != dns_rdatatype_rrsig) { 4900254402Serwin isc_result_t tresult; 4901254402Serwin dns_name_t *noqname = NULL; 4902254402Serwin tresult = findnoqname(fctx, name, 4903254402Serwin rdataset->type, &noqname); 4904254402Serwin if (tresult == ISC_R_SUCCESS && 4905254402Serwin noqname != NULL) { 4906254402Serwin tresult = dns_rdataset_addnoqname( 4907254402Serwin rdataset, noqname); 4908254402Serwin RUNTIME_CHECK(tresult == ISC_R_SUCCESS); 4909254402Serwin } 4910254402Serwin } 4911254402Serwin 4912135446Strhodes /* 4913135446Strhodes * Now we can add the rdataset. 4914135446Strhodes */ 4915135446Strhodes result = dns_db_addrdataset(fctx->cache, 4916135446Strhodes node, NULL, now, 4917135446Strhodes rdataset, 4918135446Strhodes options, 4919135446Strhodes addedrdataset); 4920254402Serwin 4921135446Strhodes if (result == DNS_R_UNCHANGED) { 4922135446Strhodes if (ANSWER(rdataset) && 4923135446Strhodes ardataset != NULL && 4924223812Sdougb NEGATIVE(ardataset)) { 4925135446Strhodes /* 4926135446Strhodes * The answer in the cache is better 4927135446Strhodes * than the answer we found, and is 4928135446Strhodes * a negative cache entry, so we 4929135446Strhodes * must set eresult appropriately. 4930135446Strhodes */ 4931174187Sdougb if (NXDOMAIN(ardataset)) 4932174187Sdougb eresult = DNS_R_NCACHENXDOMAIN; 4933174187Sdougb else 4934174187Sdougb eresult = DNS_R_NCACHENXRRSET; 4935135446Strhodes } 4936135446Strhodes result = ISC_R_SUCCESS; 4937135446Strhodes } else if (result != ISC_R_SUCCESS) 4938135446Strhodes break; 4939135446Strhodes } 4940135446Strhodes } 4941135446Strhodes 4942153816Sdougb if (valrdataset != NULL) 4943153816Sdougb result = valcreate(fctx, addrinfo, name, fctx->type, 4944153816Sdougb valrdataset, valsigrdataset, valoptions, 4945174187Sdougb task); 4946135446Strhodes 4947135446Strhodes if (result == ISC_R_SUCCESS && have_answer) { 4948135446Strhodes fctx->attributes |= FCTX_ATTR_HAVEANSWER; 4949135446Strhodes if (event != NULL) { 4950174187Sdougb /* 4951174187Sdougb * Negative results must be indicated in event->result. 4952174187Sdougb */ 4953174187Sdougb if (dns_rdataset_isassociated(event->rdataset) && 4954223812Sdougb NEGATIVE(event->rdataset)) { 4955174187Sdougb INSIST(eresult == DNS_R_NCACHENXDOMAIN || 4956174187Sdougb eresult == DNS_R_NCACHENXRRSET); 4957174187Sdougb } 4958135446Strhodes event->result = eresult; 4959308205Sdelphij if (adbp != NULL && *adbp != NULL) { 4960308205Sdelphij if (anodep != NULL && *anodep != NULL) 4961308205Sdelphij dns_db_detachnode(*adbp, anodep); 4962308205Sdelphij dns_db_detach(adbp); 4963308205Sdelphij } 4964135446Strhodes dns_db_attach(fctx->cache, adbp); 4965174187Sdougb dns_db_transfernode(fctx->cache, &node, anodep); 4966135446Strhodes clone_results(fctx); 4967135446Strhodes } 4968135446Strhodes } 4969135446Strhodes 4970135446Strhodes if (node != NULL) 4971135446Strhodes dns_db_detachnode(fctx->cache, &node); 4972135446Strhodes 4973135446Strhodes return (result); 4974135446Strhodes} 4975135446Strhodes 4976135446Strhodesstatic inline isc_result_t 4977153816Sdougbcache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_stdtime_t now) 4978153816Sdougb{ 4979135446Strhodes isc_result_t result; 4980135446Strhodes dns_section_t section; 4981135446Strhodes dns_name_t *name; 4982135446Strhodes 4983135446Strhodes FCTXTRACE("cache_message"); 4984135446Strhodes 4985135446Strhodes fctx->attributes &= ~FCTX_ATTR_WANTCACHE; 4986135446Strhodes 4987135446Strhodes LOCK(&fctx->res->buckets[fctx->bucketnum].lock); 4988135446Strhodes 4989135446Strhodes for (section = DNS_SECTION_ANSWER; 4990135446Strhodes section <= DNS_SECTION_ADDITIONAL; 4991135446Strhodes section++) { 4992135446Strhodes result = dns_message_firstname(fctx->rmessage, section); 4993135446Strhodes while (result == ISC_R_SUCCESS) { 4994135446Strhodes name = NULL; 4995135446Strhodes dns_message_currentname(fctx->rmessage, section, 4996135446Strhodes &name); 4997135446Strhodes if ((name->attributes & DNS_NAMEATTR_CACHE) != 0) { 4998153816Sdougb result = cache_name(fctx, name, addrinfo, now); 4999135446Strhodes if (result != ISC_R_SUCCESS) 5000135446Strhodes break; 5001135446Strhodes } 5002135446Strhodes result = dns_message_nextname(fctx->rmessage, section); 5003135446Strhodes } 5004135446Strhodes if (result != ISC_R_NOMORE) 5005135446Strhodes break; 5006135446Strhodes } 5007135446Strhodes if (result == ISC_R_NOMORE) 5008135446Strhodes result = ISC_R_SUCCESS; 5009135446Strhodes 5010135446Strhodes UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); 5011135446Strhodes 5012135446Strhodes return (result); 5013135446Strhodes} 5014135446Strhodes 5015135446Strhodes/* 5016193149Sdougb * Do what dns_ncache_addoptout() does, and then compute an appropriate eresult. 5017135446Strhodes */ 5018135446Strhodesstatic isc_result_t 5019135446Strhodesncache_adderesult(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node, 5020135446Strhodes dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t maxttl, 5021254402Serwin isc_boolean_t optout, isc_boolean_t secure, 5022254402Serwin dns_rdataset_t *ardataset, isc_result_t *eresultp) 5023135446Strhodes{ 5024135446Strhodes isc_result_t result; 5025162079Sdougb dns_rdataset_t rdataset; 5026162079Sdougb 5027162079Sdougb if (ardataset == NULL) { 5028162079Sdougb dns_rdataset_init(&rdataset); 5029162079Sdougb ardataset = &rdataset; 5030162079Sdougb } 5031254402Serwin if (secure) 5032254402Serwin result = dns_ncache_addoptout(message, cache, node, covers, 5033254402Serwin now, maxttl, optout, ardataset); 5034254402Serwin else 5035254402Serwin result = dns_ncache_add(message, cache, node, covers, now, 5036254402Serwin maxttl, ardataset); 5037162079Sdougb if (result == DNS_R_UNCHANGED || result == ISC_R_SUCCESS) { 5038135446Strhodes /* 5039162079Sdougb * If the cache now contains a negative entry and we 5040162079Sdougb * care about whether it is DNS_R_NCACHENXDOMAIN or 5041162079Sdougb * DNS_R_NCACHENXRRSET then extract it. 5042135446Strhodes */ 5043223812Sdougb if (NEGATIVE(ardataset)) { 5044135446Strhodes /* 5045162079Sdougb * The cache data is a negative cache entry. 5046135446Strhodes */ 5047135446Strhodes if (NXDOMAIN(ardataset)) 5048135446Strhodes *eresultp = DNS_R_NCACHENXDOMAIN; 5049135446Strhodes else 5050135446Strhodes *eresultp = DNS_R_NCACHENXRRSET; 5051135446Strhodes } else { 5052135446Strhodes /* 5053135446Strhodes * Either we don't care about the nature of the 5054135446Strhodes * cache rdataset (because no fetch is interested 5055135446Strhodes * in the outcome), or the cache rdataset is not 5056135446Strhodes * a negative cache entry. Whichever case it is, 5057135446Strhodes * we can return success. 5058135446Strhodes * 5059135446Strhodes * XXXRTH There's a CNAME/DNAME problem here. 5060135446Strhodes */ 5061135446Strhodes *eresultp = ISC_R_SUCCESS; 5062135446Strhodes } 5063162079Sdougb result = ISC_R_SUCCESS; 5064135446Strhodes } 5065162079Sdougb if (ardataset == &rdataset && dns_rdataset_isassociated(ardataset)) 5066162079Sdougb dns_rdataset_disassociate(ardataset); 5067135446Strhodes 5068135446Strhodes return (result); 5069135446Strhodes} 5070135446Strhodes 5071135446Strhodesstatic inline isc_result_t 5072153816Sdougbncache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, 5073153816Sdougb dns_rdatatype_t covers, isc_stdtime_t now) 5074153816Sdougb{ 5075135446Strhodes isc_result_t result, eresult; 5076135446Strhodes dns_name_t *name; 5077135446Strhodes dns_resolver_t *res; 5078135446Strhodes dns_db_t **adbp; 5079135446Strhodes dns_dbnode_t *node, **anodep; 5080135446Strhodes dns_rdataset_t *ardataset; 5081135446Strhodes isc_boolean_t need_validation, secure_domain; 5082135446Strhodes dns_name_t *aname; 5083135446Strhodes dns_fetchevent_t *event; 5084135446Strhodes isc_uint32_t ttl; 5085153816Sdougb unsigned int valoptions = 0; 5086135446Strhodes 5087135446Strhodes FCTXTRACE("ncache_message"); 5088135446Strhodes 5089135446Strhodes fctx->attributes &= ~FCTX_ATTR_WANTNCACHE; 5090135446Strhodes 5091135446Strhodes res = fctx->res; 5092135446Strhodes need_validation = ISC_FALSE; 5093225361Sdougb POST(need_validation); 5094135446Strhodes secure_domain = ISC_FALSE; 5095135446Strhodes eresult = ISC_R_SUCCESS; 5096135446Strhodes name = &fctx->name; 5097135446Strhodes node = NULL; 5098135446Strhodes 5099135446Strhodes /* 5100135446Strhodes * XXXMPA remove when we follow cnames and adjust the setting 5101135446Strhodes * of FCTX_ATTR_WANTNCACHE in noanswer_response(). 5102135446Strhodes */ 5103135446Strhodes INSIST(fctx->rmessage->counts[DNS_SECTION_ANSWER] == 0); 5104135446Strhodes 5105135446Strhodes /* 5106135446Strhodes * Is DNSSEC validation required for this name? 5107135446Strhodes */ 5108170222Sdougb if (fctx->res->view->enablevalidation) { 5109224092Sdougb result = dns_view_issecuredomain(res->view, name, 5110224092Sdougb &secure_domain); 5111170222Sdougb if (result != ISC_R_SUCCESS) 5112170222Sdougb return (result); 5113135446Strhodes 5114170222Sdougb if (!secure_domain && res->view->dlv != NULL) { 5115170222Sdougb valoptions = DNS_VALIDATOR_DLV; 5116170222Sdougb secure_domain = ISC_TRUE; 5117170222Sdougb } 5118153816Sdougb } 5119135446Strhodes 5120135446Strhodes if ((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0) 5121135446Strhodes need_validation = ISC_FALSE; 5122135446Strhodes else 5123135446Strhodes need_validation = secure_domain; 5124135446Strhodes 5125135446Strhodes if (secure_domain) { 5126135446Strhodes /* 5127135446Strhodes * Mark all rdatasets as pending. 5128135446Strhodes */ 5129135446Strhodes dns_rdataset_t *trdataset; 5130135446Strhodes dns_name_t *tname; 5131135446Strhodes 5132135446Strhodes result = dns_message_firstname(fctx->rmessage, 5133135446Strhodes DNS_SECTION_AUTHORITY); 5134135446Strhodes while (result == ISC_R_SUCCESS) { 5135135446Strhodes tname = NULL; 5136135446Strhodes dns_message_currentname(fctx->rmessage, 5137135446Strhodes DNS_SECTION_AUTHORITY, 5138135446Strhodes &tname); 5139135446Strhodes for (trdataset = ISC_LIST_HEAD(tname->list); 5140135446Strhodes trdataset != NULL; 5141135446Strhodes trdataset = ISC_LIST_NEXT(trdataset, link)) 5142199958Sdougb trdataset->trust = dns_trust_pending_answer; 5143135446Strhodes result = dns_message_nextname(fctx->rmessage, 5144135446Strhodes DNS_SECTION_AUTHORITY); 5145135446Strhodes } 5146135446Strhodes if (result != ISC_R_NOMORE) 5147135446Strhodes return (result); 5148135446Strhodes 5149135446Strhodes } 5150135446Strhodes 5151135446Strhodes if (need_validation) { 5152135446Strhodes /* 5153135446Strhodes * Do negative response validation. 5154135446Strhodes */ 5155153816Sdougb result = valcreate(fctx, addrinfo, name, fctx->type, 5156153816Sdougb NULL, NULL, valoptions, 5157153816Sdougb res->buckets[fctx->bucketnum].task); 5158135446Strhodes /* 5159135446Strhodes * If validation is necessary, return now. Otherwise continue 5160135446Strhodes * to process the message, letting the validation complete 5161135446Strhodes * in its own good time. 5162135446Strhodes */ 5163153816Sdougb return (result); 5164135446Strhodes } 5165135446Strhodes 5166135446Strhodes LOCK(&res->buckets[fctx->bucketnum].lock); 5167135446Strhodes 5168135446Strhodes adbp = NULL; 5169135446Strhodes aname = NULL; 5170135446Strhodes anodep = NULL; 5171135446Strhodes ardataset = NULL; 5172135446Strhodes if (!HAVE_ANSWER(fctx)) { 5173135446Strhodes event = ISC_LIST_HEAD(fctx->events); 5174135446Strhodes if (event != NULL) { 5175135446Strhodes adbp = &event->db; 5176135446Strhodes aname = dns_fixedname_name(&event->foundname); 5177135446Strhodes result = dns_name_copy(name, aname, NULL); 5178135446Strhodes if (result != ISC_R_SUCCESS) 5179135446Strhodes goto unlock; 5180135446Strhodes anodep = &event->node; 5181135446Strhodes ardataset = event->rdataset; 5182135446Strhodes } 5183135446Strhodes } else 5184135446Strhodes event = NULL; 5185135446Strhodes 5186135446Strhodes result = dns_db_findnode(fctx->cache, name, ISC_TRUE, &node); 5187135446Strhodes if (result != ISC_R_SUCCESS) 5188135446Strhodes goto unlock; 5189135446Strhodes 5190135446Strhodes /* 5191135446Strhodes * If we are asking for a SOA record set the cache time 5192135446Strhodes * to zero to facilitate locating the containing zone of 5193193149Sdougb * a arbitrary zone. 5194135446Strhodes */ 5195135446Strhodes ttl = fctx->res->view->maxncachettl; 5196135446Strhodes if (fctx->type == dns_rdatatype_soa && 5197193149Sdougb covers == dns_rdatatype_any && 5198193149Sdougb fctx->res->zero_no_soa_ttl) 5199135446Strhodes ttl = 0; 5200135446Strhodes 5201135446Strhodes result = ncache_adderesult(fctx->rmessage, fctx->cache, node, 5202193149Sdougb covers, now, ttl, ISC_FALSE, 5203254402Serwin ISC_FALSE, ardataset, &eresult); 5204135446Strhodes if (result != ISC_R_SUCCESS) 5205135446Strhodes goto unlock; 5206135446Strhodes 5207135446Strhodes if (!HAVE_ANSWER(fctx)) { 5208135446Strhodes fctx->attributes |= FCTX_ATTR_HAVEANSWER; 5209135446Strhodes if (event != NULL) { 5210135446Strhodes event->result = eresult; 5211308205Sdelphij if (adbp != NULL && *adbp != NULL) { 5212308205Sdelphij if (anodep != NULL && *anodep != NULL) 5213308205Sdelphij dns_db_detachnode(*adbp, anodep); 5214308205Sdelphij dns_db_detach(adbp); 5215308205Sdelphij } 5216135446Strhodes dns_db_attach(fctx->cache, adbp); 5217174187Sdougb dns_db_transfernode(fctx->cache, &node, anodep); 5218135446Strhodes clone_results(fctx); 5219135446Strhodes } 5220135446Strhodes } 5221135446Strhodes 5222135446Strhodes unlock: 5223135446Strhodes UNLOCK(&res->buckets[fctx->bucketnum].lock); 5224135446Strhodes 5225135446Strhodes if (node != NULL) 5226135446Strhodes dns_db_detachnode(fctx->cache, &node); 5227135446Strhodes 5228135446Strhodes return (result); 5229135446Strhodes} 5230135446Strhodes 5231135446Strhodesstatic inline void 5232135446Strhodesmark_related(dns_name_t *name, dns_rdataset_t *rdataset, 5233135446Strhodes isc_boolean_t external, isc_boolean_t gluing) 5234135446Strhodes{ 5235135446Strhodes name->attributes |= DNS_NAMEATTR_CACHE; 5236135446Strhodes if (gluing) { 5237135446Strhodes rdataset->trust = dns_trust_glue; 5238135446Strhodes /* 5239135446Strhodes * Glue with 0 TTL causes problems. We force the TTL to 5240135446Strhodes * 1 second to prevent this. 5241135446Strhodes */ 5242135446Strhodes if (rdataset->ttl == 0) 5243135446Strhodes rdataset->ttl = 1; 5244135446Strhodes } else 5245135446Strhodes rdataset->trust = dns_trust_additional; 5246135446Strhodes /* 5247135446Strhodes * Avoid infinite loops by only marking new rdatasets. 5248135446Strhodes */ 5249135446Strhodes if (!CACHE(rdataset)) { 5250135446Strhodes name->attributes |= DNS_NAMEATTR_CHASE; 5251135446Strhodes rdataset->attributes |= DNS_RDATASETATTR_CHASE; 5252135446Strhodes } 5253135446Strhodes rdataset->attributes |= DNS_RDATASETATTR_CACHE; 5254135446Strhodes if (external) 5255135446Strhodes rdataset->attributes |= DNS_RDATASETATTR_EXTERNAL; 5256135446Strhodes} 5257135446Strhodes 5258135446Strhodesstatic isc_result_t 5259224092Sdougbcheck_section(void *arg, dns_name_t *addname, dns_rdatatype_t type, 5260224092Sdougb dns_section_t section) 5261224092Sdougb{ 5262135446Strhodes fetchctx_t *fctx = arg; 5263135446Strhodes isc_result_t result; 5264135446Strhodes dns_name_t *name; 5265135446Strhodes dns_rdataset_t *rdataset; 5266135446Strhodes isc_boolean_t external; 5267135446Strhodes dns_rdatatype_t rtype; 5268135446Strhodes isc_boolean_t gluing; 5269135446Strhodes 5270135446Strhodes REQUIRE(VALID_FCTX(fctx)); 5271135446Strhodes 5272224092Sdougb#if CHECK_FOR_GLUE_IN_ANSWER 5273224092Sdougb if (section == DNS_SECTION_ANSWER && type != dns_rdatatype_a) 5274224092Sdougb return (ISC_R_SUCCESS); 5275224092Sdougb#endif 5276224092Sdougb 5277135446Strhodes if (GLUING(fctx)) 5278135446Strhodes gluing = ISC_TRUE; 5279135446Strhodes else 5280135446Strhodes gluing = ISC_FALSE; 5281135446Strhodes name = NULL; 5282135446Strhodes rdataset = NULL; 5283224092Sdougb result = dns_message_findname(fctx->rmessage, section, addname, 5284224092Sdougb dns_rdatatype_any, 0, &name, NULL); 5285135446Strhodes if (result == ISC_R_SUCCESS) { 5286135446Strhodes external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain)); 5287135446Strhodes if (type == dns_rdatatype_a) { 5288135446Strhodes for (rdataset = ISC_LIST_HEAD(name->list); 5289135446Strhodes rdataset != NULL; 5290135446Strhodes rdataset = ISC_LIST_NEXT(rdataset, link)) { 5291135446Strhodes if (rdataset->type == dns_rdatatype_rrsig) 5292135446Strhodes rtype = rdataset->covers; 5293135446Strhodes else 5294135446Strhodes rtype = rdataset->type; 5295135446Strhodes if (rtype == dns_rdatatype_a || 5296135446Strhodes rtype == dns_rdatatype_aaaa) 5297135446Strhodes mark_related(name, rdataset, external, 5298135446Strhodes gluing); 5299135446Strhodes } 5300135446Strhodes } else { 5301135446Strhodes result = dns_message_findtype(name, type, 0, 5302135446Strhodes &rdataset); 5303135446Strhodes if (result == ISC_R_SUCCESS) { 5304135446Strhodes mark_related(name, rdataset, external, gluing); 5305135446Strhodes /* 5306135446Strhodes * Do we have its SIG too? 5307135446Strhodes */ 5308135446Strhodes rdataset = NULL; 5309135446Strhodes result = dns_message_findtype(name, 5310135446Strhodes dns_rdatatype_rrsig, 5311135446Strhodes type, &rdataset); 5312135446Strhodes if (result == ISC_R_SUCCESS) 5313135446Strhodes mark_related(name, rdataset, external, 5314135446Strhodes gluing); 5315135446Strhodes } 5316135446Strhodes } 5317135446Strhodes } 5318135446Strhodes 5319135446Strhodes return (ISC_R_SUCCESS); 5320135446Strhodes} 5321135446Strhodes 5322224092Sdougbstatic isc_result_t 5323224092Sdougbcheck_related(void *arg, dns_name_t *addname, dns_rdatatype_t type) { 5324224092Sdougb return (check_section(arg, addname, type, DNS_SECTION_ADDITIONAL)); 5325224092Sdougb} 5326224092Sdougb 5327224092Sdougb#ifndef CHECK_FOR_GLUE_IN_ANSWER 5328224092Sdougb#define CHECK_FOR_GLUE_IN_ANSWER 0 5329224092Sdougb#endif 5330224092Sdougb#if CHECK_FOR_GLUE_IN_ANSWER 5331224092Sdougbstatic isc_result_t 5332224092Sdougbcheck_answer(void *arg, dns_name_t *addname, dns_rdatatype_t type) { 5333224092Sdougb return (check_section(arg, addname, type, DNS_SECTION_ANSWER)); 5334224092Sdougb} 5335224092Sdougb#endif 5336224092Sdougb 5337135446Strhodesstatic void 5338135446Strhodeschase_additional(fetchctx_t *fctx) { 5339135446Strhodes isc_boolean_t rescan; 5340135446Strhodes dns_section_t section = DNS_SECTION_ADDITIONAL; 5341135446Strhodes isc_result_t result; 5342135446Strhodes 5343135446Strhodes again: 5344135446Strhodes rescan = ISC_FALSE; 5345186462Sdougb 5346135446Strhodes for (result = dns_message_firstname(fctx->rmessage, section); 5347135446Strhodes result == ISC_R_SUCCESS; 5348135446Strhodes result = dns_message_nextname(fctx->rmessage, section)) { 5349135446Strhodes dns_name_t *name = NULL; 5350135446Strhodes dns_rdataset_t *rdataset; 5351135446Strhodes dns_message_currentname(fctx->rmessage, DNS_SECTION_ADDITIONAL, 5352135446Strhodes &name); 5353135446Strhodes if ((name->attributes & DNS_NAMEATTR_CHASE) == 0) 5354135446Strhodes continue; 5355135446Strhodes name->attributes &= ~DNS_NAMEATTR_CHASE; 5356135446Strhodes for (rdataset = ISC_LIST_HEAD(name->list); 5357135446Strhodes rdataset != NULL; 5358135446Strhodes rdataset = ISC_LIST_NEXT(rdataset, link)) { 5359135446Strhodes if (CHASE(rdataset)) { 5360135446Strhodes rdataset->attributes &= ~DNS_RDATASETATTR_CHASE; 5361135446Strhodes (void)dns_rdataset_additionaldata(rdataset, 5362135446Strhodes check_related, 5363135446Strhodes fctx); 5364135446Strhodes rescan = ISC_TRUE; 5365135446Strhodes } 5366135446Strhodes } 5367135446Strhodes } 5368135446Strhodes if (rescan) 5369135446Strhodes goto again; 5370135446Strhodes} 5371135446Strhodes 5372135446Strhodesstatic inline isc_result_t 5373135446Strhodescname_target(dns_rdataset_t *rdataset, dns_name_t *tname) { 5374135446Strhodes isc_result_t result; 5375135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 5376135446Strhodes dns_rdata_cname_t cname; 5377135446Strhodes 5378135446Strhodes result = dns_rdataset_first(rdataset); 5379135446Strhodes if (result != ISC_R_SUCCESS) 5380135446Strhodes return (result); 5381135446Strhodes dns_rdataset_current(rdataset, &rdata); 5382135446Strhodes result = dns_rdata_tostruct(&rdata, &cname, NULL); 5383135446Strhodes if (result != ISC_R_SUCCESS) 5384135446Strhodes return (result); 5385135446Strhodes dns_name_init(tname, NULL); 5386135446Strhodes dns_name_clone(&cname.cname, tname); 5387135446Strhodes dns_rdata_freestruct(&cname); 5388135446Strhodes 5389135446Strhodes return (ISC_R_SUCCESS); 5390135446Strhodes} 5391135446Strhodes 5392135446Strhodesstatic inline isc_result_t 5393296611Sdelphijdname_target(dns_rdataset_t *rdataset, dns_name_t *qname, 5394296611Sdelphij unsigned int nlabels, dns_fixedname_t *fixeddname) 5395135446Strhodes{ 5396135446Strhodes isc_result_t result; 5397135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 5398135446Strhodes dns_rdata_dname_t dname; 5399135446Strhodes dns_fixedname_t prefix; 5400135446Strhodes 5401135446Strhodes /* 5402135446Strhodes * Get the target name of the DNAME. 5403135446Strhodes */ 5404135446Strhodes result = dns_rdataset_first(rdataset); 5405135446Strhodes if (result != ISC_R_SUCCESS) 5406135446Strhodes return (result); 5407135446Strhodes dns_rdataset_current(rdataset, &rdata); 5408135446Strhodes result = dns_rdata_tostruct(&rdata, &dname, NULL); 5409135446Strhodes if (result != ISC_R_SUCCESS) 5410135446Strhodes return (result); 5411135446Strhodes 5412135446Strhodes dns_fixedname_init(&prefix); 5413186462Sdougb dns_name_split(qname, nlabels, dns_fixedname_name(&prefix), NULL); 5414135446Strhodes dns_fixedname_init(fixeddname); 5415135446Strhodes result = dns_name_concatenate(dns_fixedname_name(&prefix), 5416135446Strhodes &dname.dname, 5417135446Strhodes dns_fixedname_name(fixeddname), NULL); 5418135446Strhodes dns_rdata_freestruct(&dname); 5419135446Strhodes return (result); 5420135446Strhodes} 5421135446Strhodes 5422224092Sdougbstatic isc_boolean_t 5423224092Sdougbis_answeraddress_allowed(dns_view_t *view, dns_name_t *name, 5424224092Sdougb dns_rdataset_t *rdataset) 5425224092Sdougb{ 5426224092Sdougb isc_result_t result; 5427224092Sdougb dns_rdata_t rdata = DNS_RDATA_INIT; 5428224092Sdougb struct in_addr ina; 5429224092Sdougb struct in6_addr in6a; 5430224092Sdougb isc_netaddr_t netaddr; 5431224092Sdougb char addrbuf[ISC_NETADDR_FORMATSIZE]; 5432224092Sdougb char namebuf[DNS_NAME_FORMATSIZE]; 5433224092Sdougb char classbuf[64]; 5434224092Sdougb char typebuf[64]; 5435224092Sdougb int match; 5436224092Sdougb 5437224092Sdougb /* By default, we allow any addresses. */ 5438224092Sdougb if (view->denyansweracl == NULL) 5439224092Sdougb return (ISC_TRUE); 5440224092Sdougb 5441224092Sdougb /* 5442224092Sdougb * If the owner name matches one in the exclusion list, either exactly 5443224092Sdougb * or partially, allow it. 5444224092Sdougb */ 5445224092Sdougb if (view->answeracl_exclude != NULL) { 5446224092Sdougb dns_rbtnode_t *node = NULL; 5447224092Sdougb 5448224092Sdougb result = dns_rbt_findnode(view->answeracl_exclude, name, NULL, 5449224092Sdougb &node, NULL, 0, NULL, NULL); 5450224092Sdougb 5451224092Sdougb if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) 5452224092Sdougb return (ISC_TRUE); 5453224092Sdougb } 5454224092Sdougb 5455224092Sdougb /* 5456224092Sdougb * Otherwise, search the filter list for a match for each address 5457224092Sdougb * record. If a match is found, the address should be filtered, 5458224092Sdougb * so should the entire answer. 5459224092Sdougb */ 5460224092Sdougb for (result = dns_rdataset_first(rdataset); 5461224092Sdougb result == ISC_R_SUCCESS; 5462224092Sdougb result = dns_rdataset_next(rdataset)) { 5463224092Sdougb dns_rdata_reset(&rdata); 5464224092Sdougb dns_rdataset_current(rdataset, &rdata); 5465224092Sdougb if (rdataset->type == dns_rdatatype_a) { 5466224092Sdougb INSIST(rdata.length == sizeof(ina.s_addr)); 5467262706Serwin memmove(&ina.s_addr, rdata.data, sizeof(ina.s_addr)); 5468224092Sdougb isc_netaddr_fromin(&netaddr, &ina); 5469224092Sdougb } else { 5470224092Sdougb INSIST(rdata.length == sizeof(in6a.s6_addr)); 5471262706Serwin memmove(in6a.s6_addr, rdata.data, sizeof(in6a.s6_addr)); 5472224092Sdougb isc_netaddr_fromin6(&netaddr, &in6a); 5473224092Sdougb } 5474224092Sdougb 5475224092Sdougb result = dns_acl_match(&netaddr, NULL, view->denyansweracl, 5476224092Sdougb &view->aclenv, &match, NULL); 5477224092Sdougb 5478224092Sdougb if (result == ISC_R_SUCCESS && match > 0) { 5479224092Sdougb isc_netaddr_format(&netaddr, addrbuf, sizeof(addrbuf)); 5480224092Sdougb dns_name_format(name, namebuf, sizeof(namebuf)); 5481224092Sdougb dns_rdatatype_format(rdataset->type, typebuf, 5482224092Sdougb sizeof(typebuf)); 5483224092Sdougb dns_rdataclass_format(rdataset->rdclass, classbuf, 5484224092Sdougb sizeof(classbuf)); 5485224092Sdougb isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, 5486224092Sdougb DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE, 5487224092Sdougb "answer address %s denied for %s/%s/%s", 5488224092Sdougb addrbuf, namebuf, typebuf, classbuf); 5489224092Sdougb return (ISC_FALSE); 5490224092Sdougb } 5491224092Sdougb } 5492224092Sdougb 5493224092Sdougb return (ISC_TRUE); 5494224092Sdougb} 5495224092Sdougb 5496224092Sdougbstatic isc_boolean_t 5497224092Sdougbis_answertarget_allowed(dns_view_t *view, dns_name_t *name, 5498224092Sdougb dns_rdatatype_t type, dns_name_t *tname, 5499224092Sdougb dns_name_t *domain) 5500224092Sdougb{ 5501224092Sdougb isc_result_t result; 5502224092Sdougb dns_rbtnode_t *node = NULL; 5503224092Sdougb char qnamebuf[DNS_NAME_FORMATSIZE]; 5504224092Sdougb char tnamebuf[DNS_NAME_FORMATSIZE]; 5505224092Sdougb char classbuf[64]; 5506224092Sdougb char typebuf[64]; 5507224092Sdougb 5508224092Sdougb /* By default, we allow any target name. */ 5509224092Sdougb if (view->denyanswernames == NULL) 5510224092Sdougb return (ISC_TRUE); 5511224092Sdougb 5512224092Sdougb /* 5513224092Sdougb * If the owner name matches one in the exclusion list, either exactly 5514224092Sdougb * or partially, allow it. 5515224092Sdougb */ 5516224092Sdougb if (view->answernames_exclude != NULL) { 5517224092Sdougb result = dns_rbt_findnode(view->answernames_exclude, name, NULL, 5518224092Sdougb &node, NULL, 0, NULL, NULL); 5519224092Sdougb if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) 5520224092Sdougb return (ISC_TRUE); 5521224092Sdougb } 5522224092Sdougb 5523224092Sdougb /* 5524224092Sdougb * If the target name is a subdomain of the search domain, allow it. 5525224092Sdougb */ 5526224092Sdougb if (dns_name_issubdomain(tname, domain)) 5527224092Sdougb return (ISC_TRUE); 5528224092Sdougb 5529224092Sdougb /* 5530224092Sdougb * Otherwise, apply filters. 5531224092Sdougb */ 5532224092Sdougb result = dns_rbt_findnode(view->denyanswernames, tname, NULL, &node, 5533224092Sdougb NULL, 0, NULL, NULL); 5534224092Sdougb if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) { 5535224092Sdougb dns_name_format(name, qnamebuf, sizeof(qnamebuf)); 5536224092Sdougb dns_name_format(tname, tnamebuf, sizeof(tnamebuf)); 5537224092Sdougb dns_rdatatype_format(type, typebuf, sizeof(typebuf)); 5538224092Sdougb dns_rdataclass_format(view->rdclass, classbuf, 5539224092Sdougb sizeof(classbuf)); 5540224092Sdougb isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, 5541224092Sdougb DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE, 5542224092Sdougb "%s target %s denied for %s/%s", 5543224092Sdougb typebuf, tnamebuf, qnamebuf, classbuf); 5544224092Sdougb return (ISC_FALSE); 5545224092Sdougb } 5546224092Sdougb 5547224092Sdougb return (ISC_TRUE); 5548224092Sdougb} 5549224092Sdougb 5550234010Sdougbstatic void 5551234010Sdougbtrim_ns_ttl(fetchctx_t *fctx, dns_name_t *name, dns_rdataset_t *rdataset) { 5552234010Sdougb char ns_namebuf[DNS_NAME_FORMATSIZE]; 5553234010Sdougb char namebuf[DNS_NAME_FORMATSIZE]; 5554234010Sdougb char tbuf[DNS_RDATATYPE_FORMATSIZE]; 5555234010Sdougb 5556234010Sdougb if (fctx->ns_ttl_ok && rdataset->ttl > fctx->ns_ttl) { 5557234010Sdougb dns_name_format(name, ns_namebuf, sizeof(ns_namebuf)); 5558234010Sdougb dns_name_format(&fctx->name, namebuf, sizeof(namebuf)); 5559234010Sdougb dns_rdatatype_format(fctx->type, tbuf, sizeof(tbuf)); 5560234010Sdougb 5561234010Sdougb isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, 5562234010Sdougb DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(10), 5563234010Sdougb "fctx %p: trimming ttl of %s/NS for %s/%s: " 5564234010Sdougb "%u -> %u", fctx, ns_namebuf, namebuf, tbuf, 5565234010Sdougb rdataset->ttl, fctx->ns_ttl); 5566234010Sdougb rdataset->ttl = fctx->ns_ttl; 5567234010Sdougb } 5568234010Sdougb} 5569234010Sdougb 5570135446Strhodes/* 5571135446Strhodes * Handle a no-answer response (NXDOMAIN, NXRRSET, or referral). 5572224092Sdougb * If look_in_options has LOOK_FOR_NS_IN_ANSWER then we look in the answer 5573224092Sdougb * section for the NS RRset if the query type is NS; if it has 5574224092Sdougb * LOOK_FOR_GLUE_IN_ANSWER we look for glue incorrectly returned in the answer 5575224092Sdougb * section for A and AAAA queries. 5576135446Strhodes */ 5577224092Sdougb#define LOOK_FOR_NS_IN_ANSWER 0x1 5578224092Sdougb#define LOOK_FOR_GLUE_IN_ANSWER 0x2 5579224092Sdougb 5580135446Strhodesstatic isc_result_t 5581135446Strhodesnoanswer_response(fetchctx_t *fctx, dns_name_t *oqname, 5582224092Sdougb unsigned int look_in_options) 5583135446Strhodes{ 5584135446Strhodes isc_result_t result; 5585135446Strhodes dns_message_t *message; 5586254402Serwin dns_name_t *name, *qname, *ns_name, *soa_name, *ds_name, *save_name; 5587135446Strhodes dns_rdataset_t *rdataset, *ns_rdataset; 5588170222Sdougb isc_boolean_t aa, negative_response; 5589254402Serwin dns_rdatatype_t type, save_type; 5590224092Sdougb dns_section_t section; 5591135446Strhodes 5592135446Strhodes FCTXTRACE("noanswer_response"); 5593135446Strhodes 5594224092Sdougb if ((look_in_options & LOOK_FOR_NS_IN_ANSWER) != 0) { 5595224092Sdougb INSIST(fctx->type == dns_rdatatype_ns); 5596224092Sdougb section = DNS_SECTION_ANSWER; 5597224092Sdougb } else 5598224092Sdougb section = DNS_SECTION_AUTHORITY; 5599224092Sdougb 5600135446Strhodes message = fctx->rmessage; 5601135446Strhodes 5602135446Strhodes /* 5603135446Strhodes * Setup qname. 5604135446Strhodes */ 5605135446Strhodes if (oqname == NULL) { 5606135446Strhodes /* 5607135446Strhodes * We have a normal, non-chained negative response or 5608135446Strhodes * referral. 5609135446Strhodes */ 5610135446Strhodes if ((message->flags & DNS_MESSAGEFLAG_AA) != 0) 5611135446Strhodes aa = ISC_TRUE; 5612135446Strhodes else 5613135446Strhodes aa = ISC_FALSE; 5614135446Strhodes qname = &fctx->name; 5615135446Strhodes } else { 5616135446Strhodes /* 5617135446Strhodes * We're being invoked by answer_response() after it has 5618135446Strhodes * followed a CNAME/DNAME chain. 5619135446Strhodes */ 5620135446Strhodes qname = oqname; 5621135446Strhodes aa = ISC_FALSE; 5622135446Strhodes /* 5623135446Strhodes * If the current qname is not a subdomain of the query 5624135446Strhodes * domain, there's no point in looking at the authority 5625135446Strhodes * section without doing DNSSEC validation. 5626135446Strhodes * 5627135446Strhodes * Until we do that validation, we'll just return success 5628135446Strhodes * in this case. 5629135446Strhodes */ 5630135446Strhodes if (!dns_name_issubdomain(qname, &fctx->domain)) 5631135446Strhodes return (ISC_R_SUCCESS); 5632135446Strhodes } 5633135446Strhodes 5634135446Strhodes /* 5635135446Strhodes * We have to figure out if this is a negative response, or a 5636135446Strhodes * referral. 5637135446Strhodes */ 5638135446Strhodes 5639135446Strhodes /* 5640135446Strhodes * Sometimes we can tell if its a negative response by looking at 5641135446Strhodes * the message header. 5642135446Strhodes */ 5643135446Strhodes negative_response = ISC_FALSE; 5644135446Strhodes if (message->rcode == dns_rcode_nxdomain || 5645135446Strhodes (message->counts[DNS_SECTION_ANSWER] == 0 && 5646135446Strhodes message->counts[DNS_SECTION_AUTHORITY] == 0)) 5647135446Strhodes negative_response = ISC_TRUE; 5648135446Strhodes 5649135446Strhodes /* 5650135446Strhodes * Process the authority section. 5651135446Strhodes */ 5652135446Strhodes ns_name = NULL; 5653135446Strhodes ns_rdataset = NULL; 5654135446Strhodes soa_name = NULL; 5655135446Strhodes ds_name = NULL; 5656254402Serwin save_name = NULL; 5657254402Serwin save_type = dns_rdatatype_none; 5658135446Strhodes result = dns_message_firstname(message, section); 5659170222Sdougb while (result == ISC_R_SUCCESS) { 5660135446Strhodes name = NULL; 5661135446Strhodes dns_message_currentname(message, section, &name); 5662135446Strhodes if (dns_name_issubdomain(name, &fctx->domain)) { 5663135446Strhodes /* 5664143731Sdougb * Look for NS/SOA RRsets first. 5665135446Strhodes */ 5666135446Strhodes for (rdataset = ISC_LIST_HEAD(name->list); 5667135446Strhodes rdataset != NULL; 5668135446Strhodes rdataset = ISC_LIST_NEXT(rdataset, link)) { 5669135446Strhodes type = rdataset->type; 5670135446Strhodes if (type == dns_rdatatype_rrsig) 5671135446Strhodes type = rdataset->covers; 5672135446Strhodes if (((type == dns_rdatatype_ns || 5673135446Strhodes type == dns_rdatatype_soa) && 5674224092Sdougb !dns_name_issubdomain(qname, name))) { 5675224092Sdougb char qbuf[DNS_NAME_FORMATSIZE]; 5676224092Sdougb char nbuf[DNS_NAME_FORMATSIZE]; 5677224092Sdougb char tbuf[DNS_RDATATYPE_FORMATSIZE]; 5678224092Sdougb dns_rdatatype_format(fctx->type, tbuf, 5679224092Sdougb sizeof(tbuf)); 5680224092Sdougb dns_name_format(name, nbuf, 5681224092Sdougb sizeof(nbuf)); 5682224092Sdougb dns_name_format(qname, qbuf, 5683224092Sdougb sizeof(qbuf)); 5684224092Sdougb log_formerr(fctx, 5685224092Sdougb "unrelated %s %s in " 5686224092Sdougb "%s authority section", 5687224092Sdougb tbuf, qbuf, nbuf); 5688135446Strhodes return (DNS_R_FORMERR); 5689224092Sdougb } 5690135446Strhodes if (type == dns_rdatatype_ns) { 5691135446Strhodes /* 5692143731Sdougb * NS or RRSIG NS. 5693135446Strhodes * 5694135446Strhodes * Only one set of NS RRs is allowed. 5695135446Strhodes */ 5696135446Strhodes if (rdataset->type == 5697135446Strhodes dns_rdatatype_ns) { 5698135446Strhodes if (ns_name != NULL && 5699224092Sdougb name != ns_name) { 5700224092Sdougb log_formerr(fctx, 5701224092Sdougb "multiple NS " 5702224092Sdougb "RRsets in " 5703224092Sdougb "authority " 5704224092Sdougb "section"); 5705135446Strhodes return (DNS_R_FORMERR); 5706224092Sdougb } 5707135446Strhodes ns_name = name; 5708135446Strhodes ns_rdataset = rdataset; 5709135446Strhodes } 5710135446Strhodes name->attributes |= 5711135446Strhodes DNS_NAMEATTR_CACHE; 5712135446Strhodes rdataset->attributes |= 5713135446Strhodes DNS_RDATASETATTR_CACHE; 5714135446Strhodes rdataset->trust = dns_trust_glue; 5715135446Strhodes } 5716143731Sdougb if (type == dns_rdatatype_soa) { 5717135446Strhodes /* 5718143731Sdougb * SOA, or RRSIG SOA. 5719135446Strhodes * 5720135446Strhodes * Only one SOA is allowed. 5721135446Strhodes */ 5722135446Strhodes if (rdataset->type == 5723135446Strhodes dns_rdatatype_soa) { 5724135446Strhodes if (soa_name != NULL && 5725224092Sdougb name != soa_name) { 5726224092Sdougb log_formerr(fctx, 5727224092Sdougb "multiple SOA " 5728224092Sdougb "RRs in " 5729224092Sdougb "authority " 5730224092Sdougb "section"); 5731135446Strhodes return (DNS_R_FORMERR); 5732224092Sdougb } 5733135446Strhodes soa_name = name; 5734135446Strhodes } 5735143731Sdougb name->attributes |= 5736143731Sdougb DNS_NAMEATTR_NCACHE; 5737143731Sdougb rdataset->attributes |= 5738143731Sdougb DNS_RDATASETATTR_NCACHE; 5739143731Sdougb if (aa) 5740143731Sdougb rdataset->trust = 5741143731Sdougb dns_trust_authauthority; 5742234010Sdougb else if (ISFORWARDER(fctx->addrinfo)) 5743234010Sdougb rdataset->trust = 5744234010Sdougb dns_trust_answer; 5745143731Sdougb else 5746143731Sdougb rdataset->trust = 5747143731Sdougb dns_trust_additional; 5748143731Sdougb } 5749143731Sdougb } 5750170222Sdougb } 5751170222Sdougb result = dns_message_nextname(message, section); 5752170222Sdougb if (result == ISC_R_NOMORE) 5753170222Sdougb break; 5754170222Sdougb else if (result != ISC_R_SUCCESS) 5755170222Sdougb return (result); 5756170222Sdougb } 5757170222Sdougb 5758234010Sdougb log_ns_ttl(fctx, "noanswer_response"); 5759234010Sdougb 5760234010Sdougb if (ns_rdataset != NULL && dns_name_equal(&fctx->domain, ns_name) && 5761234010Sdougb !dns_name_equal(ns_name, dns_rootname)) 5762234010Sdougb trim_ns_ttl(fctx, ns_name, ns_rdataset); 5763234010Sdougb 5764170222Sdougb /* 5765186462Sdougb * A negative response has a SOA record (Type 2) 5766170222Sdougb * and a optional NS RRset (Type 1) or it has neither 5767170222Sdougb * a SOA or a NS RRset (Type 3, handled above) or 5768170222Sdougb * rcode is NXDOMAIN (handled above) in which case 5769170222Sdougb * the NS RRset is allowed (Type 4). 5770170222Sdougb */ 5771170222Sdougb if (soa_name != NULL) 5772170222Sdougb negative_response = ISC_TRUE; 5773170222Sdougb 5774170222Sdougb result = dns_message_firstname(message, section); 5775170222Sdougb while (result == ISC_R_SUCCESS) { 5776170222Sdougb name = NULL; 5777170222Sdougb dns_message_currentname(message, section, &name); 5778170222Sdougb if (dns_name_issubdomain(name, &fctx->domain)) { 5779143731Sdougb for (rdataset = ISC_LIST_HEAD(name->list); 5780143731Sdougb rdataset != NULL; 5781143731Sdougb rdataset = ISC_LIST_NEXT(rdataset, link)) { 5782143731Sdougb type = rdataset->type; 5783143731Sdougb if (type == dns_rdatatype_rrsig) 5784143731Sdougb type = rdataset->covers; 5785193149Sdougb if (type == dns_rdatatype_nsec || 5786193149Sdougb type == dns_rdatatype_nsec3) { 5787143731Sdougb /* 5788143731Sdougb * NSEC or RRSIG NSEC. 5789143731Sdougb */ 5790143731Sdougb if (negative_response) { 5791135446Strhodes name->attributes |= 5792135446Strhodes DNS_NAMEATTR_NCACHE; 5793135446Strhodes rdataset->attributes |= 5794135446Strhodes DNS_RDATASETATTR_NCACHE; 5795193149Sdougb } else if (type == dns_rdatatype_nsec) { 5796135446Strhodes name->attributes |= 5797135446Strhodes DNS_NAMEATTR_CACHE; 5798135446Strhodes rdataset->attributes |= 5799135446Strhodes DNS_RDATASETATTR_CACHE; 5800135446Strhodes } 5801135446Strhodes if (aa) 5802135446Strhodes rdataset->trust = 5803135446Strhodes dns_trust_authauthority; 5804234010Sdougb else if (ISFORWARDER(fctx->addrinfo)) 5805234010Sdougb rdataset->trust = 5806234010Sdougb dns_trust_answer; 5807135446Strhodes else 5808135446Strhodes rdataset->trust = 5809135446Strhodes dns_trust_additional; 5810135446Strhodes /* 5811135446Strhodes * No additional data needs to be 5812135446Strhodes * marked. 5813135446Strhodes */ 5814135446Strhodes } else if (type == dns_rdatatype_ds) { 5815135446Strhodes /* 5816135446Strhodes * DS or SIG DS. 5817135446Strhodes * 5818135446Strhodes * These should only be here if 5819135446Strhodes * this is a referral, and there 5820224092Sdougb * should only be one DS RRset. 5821135446Strhodes */ 5822224092Sdougb if (ns_name == NULL) { 5823224092Sdougb log_formerr(fctx, 5824224092Sdougb "DS with no " 5825224092Sdougb "referral"); 5826135446Strhodes return (DNS_R_FORMERR); 5827224092Sdougb } 5828135446Strhodes if (rdataset->type == 5829135446Strhodes dns_rdatatype_ds) { 5830135446Strhodes if (ds_name != NULL && 5831224092Sdougb name != ds_name) { 5832224092Sdougb log_formerr(fctx, 5833224092Sdougb "DS doesn't " 5834224092Sdougb "match " 5835224092Sdougb "referral " 5836224092Sdougb "(NS)"); 5837135446Strhodes return (DNS_R_FORMERR); 5838224092Sdougb } 5839135446Strhodes ds_name = name; 5840135446Strhodes } 5841135446Strhodes name->attributes |= 5842135446Strhodes DNS_NAMEATTR_CACHE; 5843135446Strhodes rdataset->attributes |= 5844135446Strhodes DNS_RDATASETATTR_CACHE; 5845135446Strhodes if (aa) 5846135446Strhodes rdataset->trust = 5847135446Strhodes dns_trust_authauthority; 5848234010Sdougb else if (ISFORWARDER(fctx->addrinfo)) 5849234010Sdougb rdataset->trust = 5850234010Sdougb dns_trust_answer; 5851135446Strhodes else 5852135446Strhodes rdataset->trust = 5853135446Strhodes dns_trust_additional; 5854135446Strhodes } 5855135446Strhodes } 5856254402Serwin } else { 5857254402Serwin save_name = name; 5858254402Serwin save_type = ISC_LIST_HEAD(name->list)->type; 5859135446Strhodes } 5860135446Strhodes result = dns_message_nextname(message, section); 5861135446Strhodes if (result == ISC_R_NOMORE) 5862135446Strhodes break; 5863135446Strhodes else if (result != ISC_R_SUCCESS) 5864135446Strhodes return (result); 5865135446Strhodes } 5866135446Strhodes 5867135446Strhodes /* 5868135446Strhodes * Trigger lookups for DNS nameservers. 5869135446Strhodes */ 5870135446Strhodes if (negative_response && message->rcode == dns_rcode_noerror && 5871135446Strhodes fctx->type == dns_rdatatype_ds && soa_name != NULL && 5872135446Strhodes dns_name_equal(soa_name, qname) && 5873135446Strhodes !dns_name_equal(qname, dns_rootname)) 5874135446Strhodes return (DNS_R_CHASEDSSERVERS); 5875135446Strhodes 5876135446Strhodes /* 5877135446Strhodes * Did we find anything? 5878135446Strhodes */ 5879135446Strhodes if (!negative_response && ns_name == NULL) { 5880135446Strhodes /* 5881135446Strhodes * Nope. 5882135446Strhodes */ 5883135446Strhodes if (oqname != NULL) { 5884135446Strhodes /* 5885135446Strhodes * We've already got a partial CNAME/DNAME chain, 5886135446Strhodes * and haven't found else anything useful here, but 5887135446Strhodes * no error has occurred since we have an answer. 5888135446Strhodes */ 5889135446Strhodes return (ISC_R_SUCCESS); 5890135446Strhodes } else { 5891135446Strhodes /* 5892135446Strhodes * The responder is insane. 5893135446Strhodes */ 5894254402Serwin if (save_name == NULL) { 5895254402Serwin log_formerr(fctx, "invalid response"); 5896254402Serwin return (DNS_R_FORMERR); 5897254402Serwin } 5898254402Serwin if (!dns_name_issubdomain(save_name, &fctx->domain)) { 5899254402Serwin char nbuf[DNS_NAME_FORMATSIZE]; 5900254402Serwin char dbuf[DNS_NAME_FORMATSIZE]; 5901254402Serwin char tbuf[DNS_RDATATYPE_FORMATSIZE]; 5902254402Serwin 5903254402Serwin dns_rdatatype_format(save_type, tbuf, 5904254402Serwin sizeof(tbuf)); 5905254402Serwin dns_name_format(save_name, nbuf, sizeof(nbuf)); 5906254402Serwin dns_name_format(&fctx->domain, dbuf, 5907254402Serwin sizeof(dbuf)); 5908254402Serwin 5909254402Serwin log_formerr(fctx, "Name %s (%s) not subdomain" 5910254402Serwin " of zone %s -- invalid response", 5911254402Serwin nbuf, tbuf, dbuf); 5912254402Serwin } else { 5913254402Serwin log_formerr(fctx, "invalid response"); 5914254402Serwin } 5915135446Strhodes return (DNS_R_FORMERR); 5916135446Strhodes } 5917135446Strhodes } 5918135446Strhodes 5919135446Strhodes /* 5920135446Strhodes * If we found both NS and SOA, they should be the same name. 5921135446Strhodes */ 5922224092Sdougb if (ns_name != NULL && soa_name != NULL && ns_name != soa_name) { 5923224092Sdougb log_formerr(fctx, "NS/SOA mismatch"); 5924135446Strhodes return (DNS_R_FORMERR); 5925224092Sdougb } 5926135446Strhodes 5927135446Strhodes /* 5928135446Strhodes * Do we have a referral? (We only want to follow a referral if 5929135446Strhodes * we're not following a chain.) 5930135446Strhodes */ 5931135446Strhodes if (!negative_response && ns_name != NULL && oqname == NULL) { 5932135446Strhodes /* 5933135446Strhodes * We already know ns_name is a subdomain of fctx->domain. 5934135446Strhodes * If ns_name is equal to fctx->domain, we're not making 5935135446Strhodes * progress. We return DNS_R_FORMERR so that we'll keep 5936135446Strhodes * trying other servers. 5937135446Strhodes */ 5938224092Sdougb if (dns_name_equal(ns_name, &fctx->domain)) { 5939224092Sdougb log_formerr(fctx, "non-improving referral"); 5940135446Strhodes return (DNS_R_FORMERR); 5941224092Sdougb } 5942135446Strhodes 5943135446Strhodes /* 5944135446Strhodes * If the referral name is not a parent of the query 5945135446Strhodes * name, consider the responder insane. 5946135446Strhodes */ 5947135446Strhodes if (! dns_name_issubdomain(&fctx->name, ns_name)) { 5948224092Sdougb /* Logged twice */ 5949224092Sdougb log_formerr(fctx, "referral to non-parent"); 5950135446Strhodes FCTXTRACE("referral to non-parent"); 5951135446Strhodes return (DNS_R_FORMERR); 5952135446Strhodes } 5953135446Strhodes 5954135446Strhodes /* 5955135446Strhodes * Mark any additional data related to this rdataset. 5956135446Strhodes * It's important that we do this before we change the 5957135446Strhodes * query domain. 5958135446Strhodes */ 5959135446Strhodes INSIST(ns_rdataset != NULL); 5960135446Strhodes fctx->attributes |= FCTX_ATTR_GLUING; 5961135446Strhodes (void)dns_rdataset_additionaldata(ns_rdataset, check_related, 5962135446Strhodes fctx); 5963224092Sdougb#if CHECK_FOR_GLUE_IN_ANSWER 5964224092Sdougb /* 5965224092Sdougb * Look in the answer section for "glue" that is incorrectly 5966224092Sdougb * returned as a answer. This is needed if the server also 5967224092Sdougb * minimizes the response size by not adding records to the 5968224092Sdougb * additional section that are in the answer section or if 5969224092Sdougb * the record gets dropped due to message size constraints. 5970224092Sdougb */ 5971224092Sdougb if ((look_in_options & LOOK_FOR_GLUE_IN_ANSWER) != 0 && 5972224092Sdougb (fctx->type == dns_rdatatype_aaaa || 5973224092Sdougb fctx->type == dns_rdatatype_a)) 5974224092Sdougb (void)dns_rdataset_additionaldata(ns_rdataset, 5975224092Sdougb check_answer, fctx); 5976224092Sdougb#endif 5977135446Strhodes fctx->attributes &= ~FCTX_ATTR_GLUING; 5978135446Strhodes /* 5979135446Strhodes * NS rdatasets with 0 TTL cause problems. 5980135446Strhodes * dns_view_findzonecut() will not find them when we 5981135446Strhodes * try to follow the referral, and we'll SERVFAIL 5982135446Strhodes * because the best nameservers are now above QDOMAIN. 5983135446Strhodes * We force the TTL to 1 second to prevent this. 5984135446Strhodes */ 5985135446Strhodes if (ns_rdataset->ttl == 0) 5986135446Strhodes ns_rdataset->ttl = 1; 5987135446Strhodes /* 5988135446Strhodes * Set the current query domain to the referral name. 5989135446Strhodes * 5990135446Strhodes * XXXRTH We should check if we're in forward-only mode, and 5991193149Sdougb * if so we should bail out. 5992135446Strhodes */ 5993135446Strhodes INSIST(dns_name_countlabels(&fctx->domain) > 0); 5994236374Sdougb dns_name_free(&fctx->domain, fctx->mctx); 5995135446Strhodes if (dns_rdataset_isassociated(&fctx->nameservers)) 5996135446Strhodes dns_rdataset_disassociate(&fctx->nameservers); 5997135446Strhodes dns_name_init(&fctx->domain, NULL); 5998236374Sdougb result = dns_name_dup(ns_name, fctx->mctx, &fctx->domain); 5999135446Strhodes if (result != ISC_R_SUCCESS) 6000135446Strhodes return (result); 6001135446Strhodes fctx->attributes |= FCTX_ATTR_WANTCACHE; 6002234010Sdougb fctx->ns_ttl_ok = ISC_FALSE; 6003234010Sdougb log_ns_ttl(fctx, "DELEGATION"); 6004135446Strhodes return (DNS_R_DELEGATION); 6005135446Strhodes } 6006135446Strhodes 6007135446Strhodes /* 6008135446Strhodes * Since we're not doing a referral, we don't want to cache any 6009135446Strhodes * NS RRs we may have found. 6010135446Strhodes */ 6011135446Strhodes if (ns_name != NULL) 6012135446Strhodes ns_name->attributes &= ~DNS_NAMEATTR_CACHE; 6013135446Strhodes 6014135446Strhodes if (negative_response && oqname == NULL) 6015135446Strhodes fctx->attributes |= FCTX_ATTR_WANTNCACHE; 6016135446Strhodes 6017135446Strhodes return (ISC_R_SUCCESS); 6018135446Strhodes} 6019135446Strhodes 6020135446Strhodesstatic isc_result_t 6021135446Strhodesanswer_response(fetchctx_t *fctx) { 6022135446Strhodes isc_result_t result; 6023135446Strhodes dns_message_t *message; 6024308205Sdelphij dns_name_t *name, *dname = NULL, *qname, *dqname, tname, *ns_name; 6025308205Sdelphij dns_name_t *cname = NULL; 6026234010Sdougb dns_rdataset_t *rdataset, *ns_rdataset; 6027135446Strhodes isc_boolean_t done, external, chaining, aa, found, want_chaining; 6028308205Sdelphij isc_boolean_t have_answer, found_cname, found_dname, found_type; 6029308205Sdelphij isc_boolean_t wanted_chaining; 6030135446Strhodes unsigned int aflag; 6031135446Strhodes dns_rdatatype_t type; 6032308205Sdelphij dns_fixedname_t fdname, fqname, fqdname; 6033224092Sdougb dns_view_t *view; 6034135446Strhodes 6035135446Strhodes FCTXTRACE("answer_response"); 6036135446Strhodes 6037135446Strhodes message = fctx->rmessage; 6038135446Strhodes 6039135446Strhodes /* 6040135446Strhodes * Examine the answer section, marking those rdatasets which are 6041135446Strhodes * part of the answer and should be cached. 6042135446Strhodes */ 6043135446Strhodes 6044135446Strhodes done = ISC_FALSE; 6045135446Strhodes found_cname = ISC_FALSE; 6046308205Sdelphij found_dname = ISC_FALSE; 6047135446Strhodes found_type = ISC_FALSE; 6048135446Strhodes chaining = ISC_FALSE; 6049135446Strhodes have_answer = ISC_FALSE; 6050135446Strhodes want_chaining = ISC_FALSE; 6051225361Sdougb POST(want_chaining); 6052135446Strhodes if ((message->flags & DNS_MESSAGEFLAG_AA) != 0) 6053135446Strhodes aa = ISC_TRUE; 6054135446Strhodes else 6055135446Strhodes aa = ISC_FALSE; 6056308205Sdelphij dqname = qname = &fctx->name; 6057135446Strhodes type = fctx->type; 6058224092Sdougb view = fctx->res->view; 6059308205Sdelphij dns_fixedname_init(&fqdname); 6060135446Strhodes result = dns_message_firstname(message, DNS_SECTION_ANSWER); 6061135446Strhodes while (!done && result == ISC_R_SUCCESS) { 6062308205Sdelphij dns_namereln_t namereln, dnamereln; 6063296611Sdelphij int order; 6064296611Sdelphij unsigned int nlabels; 6065296611Sdelphij 6066135446Strhodes name = NULL; 6067135446Strhodes dns_message_currentname(message, DNS_SECTION_ANSWER, &name); 6068135446Strhodes external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain)); 6069296611Sdelphij namereln = dns_name_fullcompare(qname, name, &order, &nlabels); 6070308205Sdelphij dnamereln = dns_name_fullcompare(dqname, name, &order, 6071308205Sdelphij &nlabels); 6072296611Sdelphij if (namereln == dns_namereln_equal) { 6073135446Strhodes wanted_chaining = ISC_FALSE; 6074135446Strhodes for (rdataset = ISC_LIST_HEAD(name->list); 6075135446Strhodes rdataset != NULL; 6076135446Strhodes rdataset = ISC_LIST_NEXT(rdataset, link)) { 6077135446Strhodes found = ISC_FALSE; 6078135446Strhodes want_chaining = ISC_FALSE; 6079135446Strhodes aflag = 0; 6080193149Sdougb if (rdataset->type == dns_rdatatype_nsec3) { 6081193149Sdougb /* 6082193149Sdougb * NSEC3 records are not allowed to 6083193149Sdougb * appear in the answer section. 6084193149Sdougb */ 6085224092Sdougb log_formerr(fctx, "NSEC3 in answer"); 6086193149Sdougb return (DNS_R_FORMERR); 6087193149Sdougb } 6088224092Sdougb 6089224092Sdougb /* 6090224092Sdougb * Apply filters, if given, on answers to reject 6091224092Sdougb * a malicious attempt of rebinding. 6092224092Sdougb */ 6093224092Sdougb if ((rdataset->type == dns_rdatatype_a || 6094224092Sdougb rdataset->type == dns_rdatatype_aaaa) && 6095224092Sdougb !is_answeraddress_allowed(view, name, 6096224092Sdougb rdataset)) { 6097224092Sdougb return (DNS_R_SERVFAIL); 6098224092Sdougb } 6099224092Sdougb 6100135446Strhodes if (rdataset->type == type && !found_cname) { 6101135446Strhodes /* 6102135446Strhodes * We've found an ordinary answer. 6103135446Strhodes */ 6104135446Strhodes found = ISC_TRUE; 6105135446Strhodes found_type = ISC_TRUE; 6106135446Strhodes done = ISC_TRUE; 6107135446Strhodes aflag = DNS_RDATASETATTR_ANSWER; 6108135446Strhodes } else if (type == dns_rdatatype_any) { 6109135446Strhodes /* 6110135446Strhodes * We've found an answer matching 6111135446Strhodes * an ANY query. There may be 6112135446Strhodes * more. 6113135446Strhodes */ 6114135446Strhodes found = ISC_TRUE; 6115135446Strhodes aflag = DNS_RDATASETATTR_ANSWER; 6116135446Strhodes } else if (rdataset->type == dns_rdatatype_rrsig 6117135446Strhodes && rdataset->covers == type 6118135446Strhodes && !found_cname) { 6119135446Strhodes /* 6120135446Strhodes * We've found a signature that 6121135446Strhodes * covers the type we're looking for. 6122135446Strhodes */ 6123135446Strhodes found = ISC_TRUE; 6124135446Strhodes found_type = ISC_TRUE; 6125135446Strhodes aflag = DNS_RDATASETATTR_ANSWERSIG; 6126135446Strhodes } else if (rdataset->type == 6127135446Strhodes dns_rdatatype_cname 6128135446Strhodes && !found_type) { 6129135446Strhodes /* 6130135446Strhodes * We're looking for something else, 6131135446Strhodes * but we found a CNAME. 6132135446Strhodes * 6133135446Strhodes * Getting a CNAME response for some 6134254402Serwin * query types is an error, see 6135254402Serwin * RFC 4035, Section 2.5. 6136135446Strhodes */ 6137135446Strhodes if (type == dns_rdatatype_rrsig || 6138254402Serwin type == dns_rdatatype_key || 6139254402Serwin type == dns_rdatatype_nsec) { 6140224092Sdougb char buf[DNS_RDATATYPE_FORMATSIZE]; 6141224092Sdougb dns_rdatatype_format(fctx->type, 6142224092Sdougb buf, sizeof(buf)); 6143224092Sdougb log_formerr(fctx, 6144224092Sdougb "CNAME response " 6145224092Sdougb "for %s RR", buf); 6146135446Strhodes return (DNS_R_FORMERR); 6147224092Sdougb } 6148135446Strhodes found = ISC_TRUE; 6149135446Strhodes found_cname = ISC_TRUE; 6150135446Strhodes want_chaining = ISC_TRUE; 6151135446Strhodes aflag = DNS_RDATASETATTR_ANSWER; 6152135446Strhodes result = cname_target(rdataset, 6153135446Strhodes &tname); 6154135446Strhodes if (result != ISC_R_SUCCESS) 6155135446Strhodes return (result); 6156224092Sdougb /* Apply filters on the target name. */ 6157224092Sdougb if (!is_answertarget_allowed(view, 6158224092Sdougb name, 6159224092Sdougb rdataset->type, 6160224092Sdougb &tname, 6161224092Sdougb &fctx->domain)) { 6162224092Sdougb return (DNS_R_SERVFAIL); 6163224092Sdougb } 6164135446Strhodes } else if (rdataset->type == dns_rdatatype_rrsig 6165135446Strhodes && rdataset->covers == 6166308205Sdelphij dns_rdatatype_cname 6167135446Strhodes && !found_type) { 6168135446Strhodes /* 6169135446Strhodes * We're looking for something else, 6170135446Strhodes * but we found a SIG CNAME. 6171135446Strhodes */ 6172135446Strhodes found = ISC_TRUE; 6173135446Strhodes found_cname = ISC_TRUE; 6174135446Strhodes aflag = DNS_RDATASETATTR_ANSWERSIG; 6175135446Strhodes } 6176135446Strhodes 6177135446Strhodes if (found) { 6178135446Strhodes /* 6179135446Strhodes * We've found an answer to our 6180135446Strhodes * question. 6181135446Strhodes */ 6182135446Strhodes name->attributes |= 6183135446Strhodes DNS_NAMEATTR_CACHE; 6184135446Strhodes rdataset->attributes |= 6185135446Strhodes DNS_RDATASETATTR_CACHE; 6186135446Strhodes rdataset->trust = dns_trust_answer; 6187135446Strhodes if (!chaining) { 6188135446Strhodes /* 6189135446Strhodes * This data is "the" answer 6190135446Strhodes * to our question only if 6191135446Strhodes * we're not chaining (i.e. 6192135446Strhodes * if we haven't followed 6193135446Strhodes * a CNAME or DNAME). 6194135446Strhodes */ 6195135446Strhodes INSIST(!external); 6196308205Sdelphij if ((rdataset->type != 6197308205Sdelphij dns_rdatatype_cname) || 6198308205Sdelphij !found_dname || 6199308205Sdelphij (aflag == 6200308205Sdelphij DNS_RDATASETATTR_ANSWER)) 6201308205Sdelphij { 6202135446Strhodes have_answer = ISC_TRUE; 6203308205Sdelphij if (rdataset->type == 6204308205Sdelphij dns_rdatatype_cname) 6205308205Sdelphij cname = name; 6206296611Sdelphij name->attributes |= 6207308205Sdelphij DNS_NAMEATTR_ANSWER; 6208296611Sdelphij } 6209135446Strhodes rdataset->attributes |= aflag; 6210135446Strhodes if (aa) 6211135446Strhodes rdataset->trust = 6212135446Strhodes dns_trust_authanswer; 6213135446Strhodes } else if (external) { 6214135446Strhodes /* 6215135446Strhodes * This data is outside of 6216135446Strhodes * our query domain, and 6217202961Sdougb * may not be cached. 6218135446Strhodes */ 6219135446Strhodes rdataset->attributes |= 6220135446Strhodes DNS_RDATASETATTR_EXTERNAL; 6221135446Strhodes } 6222135446Strhodes 6223135446Strhodes /* 6224135446Strhodes * Mark any additional data related 6225135446Strhodes * to this rdataset. 6226135446Strhodes */ 6227135446Strhodes (void)dns_rdataset_additionaldata( 6228135446Strhodes rdataset, 6229135446Strhodes check_related, 6230135446Strhodes fctx); 6231135446Strhodes 6232135446Strhodes /* 6233135446Strhodes * CNAME chaining. 6234135446Strhodes */ 6235135446Strhodes if (want_chaining) { 6236135446Strhodes wanted_chaining = ISC_TRUE; 6237135446Strhodes name->attributes |= 6238135446Strhodes DNS_NAMEATTR_CHAINING; 6239135446Strhodes rdataset->attributes |= 6240135446Strhodes DNS_RDATASETATTR_CHAINING; 6241135446Strhodes qname = &tname; 6242135446Strhodes } 6243135446Strhodes } 6244135446Strhodes /* 6245135446Strhodes * We could add an "else" clause here and 6246135446Strhodes * log that we're ignoring this rdataset. 6247135446Strhodes */ 6248135446Strhodes } 6249135446Strhodes /* 6250135446Strhodes * If wanted_chaining is true, we've done 6251135446Strhodes * some chaining as the result of processing 6252135446Strhodes * this node, and thus we need to set 6253135446Strhodes * chaining to true. 6254135446Strhodes * 6255135446Strhodes * We don't set chaining inside of the 6256135446Strhodes * rdataset loop because doing that would 6257135446Strhodes * cause us to ignore the signatures of 6258135446Strhodes * CNAMEs. 6259135446Strhodes */ 6260135446Strhodes if (wanted_chaining) 6261135446Strhodes chaining = ISC_TRUE; 6262135446Strhodes } else { 6263296611Sdelphij dns_rdataset_t *dnameset = NULL; 6264296611Sdelphij 6265135446Strhodes /* 6266135446Strhodes * Look for a DNAME (or its SIG). Anything else is 6267135446Strhodes * ignored. 6268135446Strhodes */ 6269135446Strhodes wanted_chaining = ISC_FALSE; 6270135446Strhodes for (rdataset = ISC_LIST_HEAD(name->list); 6271135446Strhodes rdataset != NULL; 6272296611Sdelphij rdataset = ISC_LIST_NEXT(rdataset, link)) 6273296611Sdelphij { 6274296611Sdelphij /* 6275296611Sdelphij * Only pass DNAME or RRSIG(DNAME). 6276296611Sdelphij */ 6277296611Sdelphij if (rdataset->type != dns_rdatatype_dname && 6278296611Sdelphij (rdataset->type != dns_rdatatype_rrsig || 6279296611Sdelphij rdataset->covers != dns_rdatatype_dname)) 6280296611Sdelphij continue; 6281224092Sdougb 6282296611Sdelphij /* 6283296611Sdelphij * If we're not chaining, then the DNAME and 6284296611Sdelphij * its signature should not be external. 6285296611Sdelphij */ 6286296611Sdelphij if (!chaining && external) { 6287296611Sdelphij char qbuf[DNS_NAME_FORMATSIZE]; 6288296611Sdelphij char obuf[DNS_NAME_FORMATSIZE]; 6289296611Sdelphij 6290296611Sdelphij dns_name_format(name, qbuf, 6291296611Sdelphij sizeof(qbuf)); 6292296611Sdelphij dns_name_format(&fctx->domain, obuf, 6293296611Sdelphij sizeof(obuf)); 6294296611Sdelphij log_formerr(fctx, "external DNAME or " 6295296611Sdelphij "RRSIG covering DNAME " 6296296611Sdelphij "in answer: %s is " 6297296611Sdelphij "not in %s", qbuf, obuf); 6298296611Sdelphij return (DNS_R_FORMERR); 6299296611Sdelphij } 6300296611Sdelphij 6301308205Sdelphij if (dnamereln != dns_namereln_subdomain) { 6302296611Sdelphij char qbuf[DNS_NAME_FORMATSIZE]; 6303296611Sdelphij char obuf[DNS_NAME_FORMATSIZE]; 6304296611Sdelphij 6305308205Sdelphij dns_name_format(dqname, qbuf, 6306296611Sdelphij sizeof(qbuf)); 6307296611Sdelphij dns_name_format(name, obuf, 6308296611Sdelphij sizeof(obuf)); 6309296611Sdelphij log_formerr(fctx, "unrelated DNAME " 6310296611Sdelphij "in answer: %s is " 6311296611Sdelphij "not in %s", qbuf, obuf); 6312296611Sdelphij return (DNS_R_FORMERR); 6313296611Sdelphij } 6314296611Sdelphij 6315135446Strhodes aflag = 0; 6316135446Strhodes if (rdataset->type == dns_rdatatype_dname) { 6317135446Strhodes want_chaining = ISC_TRUE; 6318225361Sdougb POST(want_chaining); 6319135446Strhodes aflag = DNS_RDATASETATTR_ANSWER; 6320308205Sdelphij result = dname_target(rdataset, dqname, 6321296611Sdelphij nlabels, &fdname); 6322135446Strhodes if (result == ISC_R_NOSPACE) { 6323135446Strhodes /* 6324135446Strhodes * We can't construct the 6325135446Strhodes * DNAME target. Do not 6326135446Strhodes * try to continue. 6327135446Strhodes */ 6328135446Strhodes want_chaining = ISC_FALSE; 6329225361Sdougb POST(want_chaining); 6330135446Strhodes } else if (result != ISC_R_SUCCESS) 6331135446Strhodes return (result); 6332135446Strhodes else 6333296611Sdelphij dnameset = rdataset; 6334224092Sdougb 6335296611Sdelphij dname = dns_fixedname_name(&fdname); 6336224092Sdougb if (!is_answertarget_allowed(view, 6337308205Sdelphij dqname, rdataset->type, 6338308205Sdelphij dname, &fctx->domain)) 6339308205Sdelphij { 6340224092Sdougb return (DNS_R_SERVFAIL); 6341224092Sdougb } 6342308205Sdelphij dqname = dns_fixedname_name(&fqdname); 6343308205Sdelphij dns_name_copy(dname, dqname, NULL); 6344296611Sdelphij } else { 6345135446Strhodes /* 6346135446Strhodes * We've found a signature that 6347135446Strhodes * covers the DNAME. 6348135446Strhodes */ 6349135446Strhodes aflag = DNS_RDATASETATTR_ANSWERSIG; 6350135446Strhodes } 6351135446Strhodes 6352296611Sdelphij /* 6353296611Sdelphij * We've found an answer to our 6354296611Sdelphij * question. 6355296611Sdelphij */ 6356296611Sdelphij name->attributes |= DNS_NAMEATTR_CACHE; 6357296611Sdelphij rdataset->attributes |= DNS_RDATASETATTR_CACHE; 6358296611Sdelphij rdataset->trust = dns_trust_answer; 6359296611Sdelphij if (!chaining) { 6360135446Strhodes /* 6361296611Sdelphij * This data is "the" answer to 6362296611Sdelphij * our question only if we're 6363296611Sdelphij * not chaining. 6364135446Strhodes */ 6365296611Sdelphij INSIST(!external); 6366296611Sdelphij if (aflag == DNS_RDATASETATTR_ANSWER) { 6367296611Sdelphij have_answer = ISC_TRUE; 6368308205Sdelphij found_dname = ISC_TRUE; 6369308205Sdelphij if (cname != NULL) 6370308205Sdelphij cname->attributes &= 6371308205Sdelphij ~DNS_NAMEATTR_ANSWER; 6372135446Strhodes name->attributes |= 6373135446Strhodes DNS_NAMEATTR_ANSWER; 6374135446Strhodes } 6375296611Sdelphij rdataset->attributes |= aflag; 6376296611Sdelphij if (aa) 6377296611Sdelphij rdataset->trust = 6378296611Sdelphij dns_trust_authanswer; 6379296611Sdelphij } else if (external) { 6380296611Sdelphij rdataset->attributes |= 6381296611Sdelphij DNS_RDATASETATTR_EXTERNAL; 6382135446Strhodes } 6383135446Strhodes } 6384296611Sdelphij 6385296611Sdelphij /* 6386296611Sdelphij * DNAME chaining. 6387296611Sdelphij */ 6388296611Sdelphij if (dnameset != NULL) { 6389296611Sdelphij /* 6390296611Sdelphij * Copy the dname into the qname fixed name. 6391296611Sdelphij * 6392296611Sdelphij * Although we check for failure of the copy 6393296611Sdelphij * operation, in practice it should never fail 6394296611Sdelphij * since we already know that the result fits 6395296611Sdelphij * in a fixedname. 6396296611Sdelphij */ 6397296611Sdelphij dns_fixedname_init(&fqname); 6398296611Sdelphij qname = dns_fixedname_name(&fqname); 6399296611Sdelphij result = dns_name_copy(dname, qname, NULL); 6400296611Sdelphij if (result != ISC_R_SUCCESS) 6401296611Sdelphij return (result); 6402296611Sdelphij wanted_chaining = ISC_TRUE; 6403296611Sdelphij name->attributes |= DNS_NAMEATTR_CHAINING; 6404296611Sdelphij dnameset->attributes |= 6405296611Sdelphij DNS_RDATASETATTR_CHAINING; 6406296611Sdelphij } 6407135446Strhodes if (wanted_chaining) 6408135446Strhodes chaining = ISC_TRUE; 6409135446Strhodes } 6410135446Strhodes result = dns_message_nextname(message, DNS_SECTION_ANSWER); 6411135446Strhodes } 6412135446Strhodes if (result == ISC_R_NOMORE) 6413135446Strhodes result = ISC_R_SUCCESS; 6414135446Strhodes if (result != ISC_R_SUCCESS) 6415135446Strhodes return (result); 6416135446Strhodes 6417135446Strhodes /* 6418135446Strhodes * We should have found an answer. 6419135446Strhodes */ 6420224092Sdougb if (!have_answer) { 6421224092Sdougb log_formerr(fctx, "reply has no answer"); 6422135446Strhodes return (DNS_R_FORMERR); 6423224092Sdougb } 6424135446Strhodes 6425135446Strhodes /* 6426135446Strhodes * This response is now potentially cacheable. 6427135446Strhodes */ 6428135446Strhodes fctx->attributes |= FCTX_ATTR_WANTCACHE; 6429135446Strhodes 6430135446Strhodes /* 6431135446Strhodes * Did chaining end before we got the final answer? 6432135446Strhodes */ 6433135446Strhodes if (chaining) { 6434135446Strhodes /* 6435135446Strhodes * Yes. This may be a negative reply, so hand off 6436135446Strhodes * authority section processing to the noanswer code. 6437135446Strhodes * If it isn't a noanswer response, no harm will be 6438135446Strhodes * done. 6439135446Strhodes */ 6440224092Sdougb return (noanswer_response(fctx, qname, 0)); 6441135446Strhodes } 6442135446Strhodes 6443135446Strhodes /* 6444135446Strhodes * We didn't end with an incomplete chain, so the rcode should be 6445135446Strhodes * "no error". 6446135446Strhodes */ 6447224092Sdougb if (message->rcode != dns_rcode_noerror) { 6448224092Sdougb log_formerr(fctx, "CNAME/DNAME chain complete, but RCODE " 6449224092Sdougb "indicates error"); 6450135446Strhodes return (DNS_R_FORMERR); 6451224092Sdougb } 6452135446Strhodes 6453135446Strhodes /* 6454135446Strhodes * Examine the authority section (if there is one). 6455135446Strhodes * 6456135446Strhodes * We expect there to be only one owner name for all the rdatasets 6457135446Strhodes * in this section, and we expect that it is not external. 6458135446Strhodes */ 6459135446Strhodes done = ISC_FALSE; 6460234010Sdougb ns_name = NULL; 6461234010Sdougb ns_rdataset = NULL; 6462135446Strhodes result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); 6463135446Strhodes while (!done && result == ISC_R_SUCCESS) { 6464135446Strhodes name = NULL; 6465135446Strhodes dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name); 6466135446Strhodes external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain)); 6467135446Strhodes if (!external) { 6468135446Strhodes /* 6469135446Strhodes * We expect to find NS or SIG NS rdatasets, and 6470135446Strhodes * nothing else. 6471135446Strhodes */ 6472135446Strhodes for (rdataset = ISC_LIST_HEAD(name->list); 6473135446Strhodes rdataset != NULL; 6474135446Strhodes rdataset = ISC_LIST_NEXT(rdataset, link)) { 6475135446Strhodes if (rdataset->type == dns_rdatatype_ns || 6476135446Strhodes (rdataset->type == dns_rdatatype_rrsig && 6477135446Strhodes rdataset->covers == dns_rdatatype_ns)) { 6478135446Strhodes name->attributes |= 6479135446Strhodes DNS_NAMEATTR_CACHE; 6480135446Strhodes rdataset->attributes |= 6481135446Strhodes DNS_RDATASETATTR_CACHE; 6482135446Strhodes if (aa && !chaining) 6483135446Strhodes rdataset->trust = 6484135446Strhodes dns_trust_authauthority; 6485135446Strhodes else 6486135446Strhodes rdataset->trust = 6487135446Strhodes dns_trust_additional; 6488135446Strhodes 6489234010Sdougb if (rdataset->type == dns_rdatatype_ns) { 6490234010Sdougb ns_name = name; 6491234010Sdougb ns_rdataset = rdataset; 6492234010Sdougb } 6493135446Strhodes /* 6494135446Strhodes * Mark any additional data related 6495135446Strhodes * to this rdataset. 6496135446Strhodes */ 6497135446Strhodes (void)dns_rdataset_additionaldata( 6498135446Strhodes rdataset, 6499135446Strhodes check_related, 6500135446Strhodes fctx); 6501135446Strhodes done = ISC_TRUE; 6502135446Strhodes } 6503135446Strhodes } 6504135446Strhodes } 6505135446Strhodes result = dns_message_nextname(message, DNS_SECTION_AUTHORITY); 6506135446Strhodes } 6507135446Strhodes if (result == ISC_R_NOMORE) 6508135446Strhodes result = ISC_R_SUCCESS; 6509135446Strhodes 6510234010Sdougb log_ns_ttl(fctx, "answer_response"); 6511234010Sdougb 6512234010Sdougb if (ns_rdataset != NULL && dns_name_equal(&fctx->domain, ns_name) && 6513234010Sdougb !dns_name_equal(ns_name, dns_rootname)) 6514234010Sdougb trim_ns_ttl(fctx, ns_name, ns_rdataset); 6515234010Sdougb 6516135446Strhodes return (result); 6517135446Strhodes} 6518135446Strhodes 6519214586Sdougbstatic isc_boolean_t 6520214586Sdougbfctx_decreference(fetchctx_t *fctx) { 6521214586Sdougb isc_boolean_t bucket_empty = ISC_FALSE; 6522214586Sdougb 6523214586Sdougb INSIST(fctx->references > 0); 6524214586Sdougb fctx->references--; 6525214586Sdougb if (fctx->references == 0) { 6526214586Sdougb /* 6527214586Sdougb * No one cares about the result of this fetch anymore. 6528214586Sdougb */ 6529214586Sdougb if (fctx->pending == 0 && fctx->nqueries == 0 && 6530214586Sdougb ISC_LIST_EMPTY(fctx->validators) && SHUTTINGDOWN(fctx)) { 6531214586Sdougb /* 6532214586Sdougb * This fctx is already shutdown; we were just 6533214586Sdougb * waiting for the last reference to go away. 6534214586Sdougb */ 6535236374Sdougb bucket_empty = fctx_unlink(fctx); 6536236374Sdougb fctx_destroy(fctx); 6537214586Sdougb } else { 6538214586Sdougb /* 6539214586Sdougb * Initiate shutdown. 6540214586Sdougb */ 6541214586Sdougb fctx_shutdown(fctx); 6542214586Sdougb } 6543214586Sdougb } 6544214586Sdougb return (bucket_empty); 6545214586Sdougb} 6546214586Sdougb 6547135446Strhodesstatic void 6548135446Strhodesresume_dslookup(isc_task_t *task, isc_event_t *event) { 6549135446Strhodes dns_fetchevent_t *fevent; 6550135446Strhodes dns_resolver_t *res; 6551135446Strhodes fetchctx_t *fctx; 6552135446Strhodes isc_result_t result; 6553214586Sdougb isc_boolean_t bucket_empty; 6554135446Strhodes isc_boolean_t locked = ISC_FALSE; 6555135446Strhodes unsigned int bucketnum; 6556135446Strhodes dns_rdataset_t nameservers; 6557135446Strhodes dns_fixedname_t fixed; 6558135446Strhodes dns_name_t *domain; 6559135446Strhodes 6560135446Strhodes REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE); 6561135446Strhodes fevent = (dns_fetchevent_t *)event; 6562135446Strhodes fctx = event->ev_arg; 6563135446Strhodes REQUIRE(VALID_FCTX(fctx)); 6564135446Strhodes res = fctx->res; 6565135446Strhodes 6566135446Strhodes UNUSED(task); 6567135446Strhodes FCTXTRACE("resume_dslookup"); 6568135446Strhodes 6569135446Strhodes if (fevent->node != NULL) 6570135446Strhodes dns_db_detachnode(fevent->db, &fevent->node); 6571135446Strhodes if (fevent->db != NULL) 6572135446Strhodes dns_db_detach(&fevent->db); 6573135446Strhodes 6574135446Strhodes dns_rdataset_init(&nameservers); 6575135446Strhodes 6576135446Strhodes bucketnum = fctx->bucketnum; 6577135446Strhodes if (fevent->result == ISC_R_CANCELED) { 6578135446Strhodes dns_resolver_destroyfetch(&fctx->nsfetch); 6579193149Sdougb fctx_done(fctx, ISC_R_CANCELED, __LINE__); 6580135446Strhodes } else if (fevent->result == ISC_R_SUCCESS) { 6581135446Strhodes 6582135446Strhodes FCTXTRACE("resuming DS lookup"); 6583135446Strhodes 6584135446Strhodes dns_resolver_destroyfetch(&fctx->nsfetch); 6585135446Strhodes if (dns_rdataset_isassociated(&fctx->nameservers)) 6586135446Strhodes dns_rdataset_disassociate(&fctx->nameservers); 6587135446Strhodes dns_rdataset_clone(fevent->rdataset, &fctx->nameservers); 6588234010Sdougb fctx->ns_ttl = fctx->nameservers.ttl; 6589234010Sdougb fctx->ns_ttl_ok = ISC_TRUE; 6590234010Sdougb log_ns_ttl(fctx, "resume_dslookup"); 6591236374Sdougb dns_name_free(&fctx->domain, fctx->mctx); 6592135446Strhodes dns_name_init(&fctx->domain, NULL); 6593236374Sdougb result = dns_name_dup(&fctx->nsname, fctx->mctx, &fctx->domain); 6594135446Strhodes if (result != ISC_R_SUCCESS) { 6595193149Sdougb fctx_done(fctx, DNS_R_SERVFAIL, __LINE__); 6596135446Strhodes goto cleanup; 6597135446Strhodes } 6598135446Strhodes /* 6599135446Strhodes * Try again. 6600135446Strhodes */ 6601205292Sdougb fctx_try(fctx, ISC_TRUE, ISC_FALSE); 6602135446Strhodes } else { 6603135446Strhodes unsigned int n; 6604165071Sdougb dns_rdataset_t *nsrdataset = NULL; 6605135446Strhodes 6606135446Strhodes /* 6607135446Strhodes * Retrieve state from fctx->nsfetch before we destroy it. 6608135446Strhodes */ 6609135446Strhodes dns_fixedname_init(&fixed); 6610135446Strhodes domain = dns_fixedname_name(&fixed); 6611135446Strhodes dns_name_copy(&fctx->nsfetch->private->domain, domain, NULL); 6612135446Strhodes if (dns_name_equal(&fctx->nsname, domain)) { 6613193149Sdougb fctx_done(fctx, DNS_R_SERVFAIL, __LINE__); 6614165071Sdougb dns_resolver_destroyfetch(&fctx->nsfetch); 6615135446Strhodes goto cleanup; 6616135446Strhodes } 6617165071Sdougb if (dns_rdataset_isassociated( 6618165071Sdougb &fctx->nsfetch->private->nameservers)) { 6619165071Sdougb dns_rdataset_clone( 6620165071Sdougb &fctx->nsfetch->private->nameservers, 6621165071Sdougb &nameservers); 6622165071Sdougb nsrdataset = &nameservers; 6623165071Sdougb } else 6624165071Sdougb domain = NULL; 6625165071Sdougb dns_resolver_destroyfetch(&fctx->nsfetch); 6626135446Strhodes n = dns_name_countlabels(&fctx->nsname); 6627135446Strhodes dns_name_getlabelsequence(&fctx->nsname, 1, n - 1, 6628135446Strhodes &fctx->nsname); 6629135446Strhodes 6630135446Strhodes if (dns_rdataset_isassociated(fevent->rdataset)) 6631135446Strhodes dns_rdataset_disassociate(fevent->rdataset); 6632135446Strhodes FCTXTRACE("continuing to look for parent's NS records"); 6633135446Strhodes result = dns_resolver_createfetch(fctx->res, &fctx->nsname, 6634135446Strhodes dns_rdatatype_ns, domain, 6635165071Sdougb nsrdataset, NULL, 0, task, 6636135446Strhodes resume_dslookup, fctx, 6637135446Strhodes &fctx->nsrrset, NULL, 6638135446Strhodes &fctx->nsfetch); 6639135446Strhodes if (result != ISC_R_SUCCESS) 6640193149Sdougb fctx_done(fctx, result, __LINE__); 6641135446Strhodes else { 6642135446Strhodes LOCK(&res->buckets[bucketnum].lock); 6643135446Strhodes locked = ISC_TRUE; 6644135446Strhodes fctx->references++; 6645135446Strhodes } 6646135446Strhodes } 6647135446Strhodes 6648135446Strhodes cleanup: 6649135446Strhodes if (dns_rdataset_isassociated(&nameservers)) 6650135446Strhodes dns_rdataset_disassociate(&nameservers); 6651135446Strhodes if (dns_rdataset_isassociated(fevent->rdataset)) 6652135446Strhodes dns_rdataset_disassociate(fevent->rdataset); 6653135446Strhodes INSIST(fevent->sigrdataset == NULL); 6654135446Strhodes isc_event_free(&event); 6655135446Strhodes if (!locked) 6656135446Strhodes LOCK(&res->buckets[bucketnum].lock); 6657214586Sdougb bucket_empty = fctx_decreference(fctx); 6658135446Strhodes UNLOCK(&res->buckets[bucketnum].lock); 6659135446Strhodes if (bucket_empty) 6660135446Strhodes empty_bucket(res); 6661135446Strhodes} 6662135446Strhodes 6663135446Strhodesstatic inline void 6664135446Strhodeschecknamessection(dns_message_t *message, dns_section_t section) { 6665135446Strhodes isc_result_t result; 6666135446Strhodes dns_name_t *name; 6667135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 6668135446Strhodes dns_rdataset_t *rdataset; 6669186462Sdougb 6670135446Strhodes for (result = dns_message_firstname(message, section); 6671135446Strhodes result == ISC_R_SUCCESS; 6672135446Strhodes result = dns_message_nextname(message, section)) 6673135446Strhodes { 6674135446Strhodes name = NULL; 6675135446Strhodes dns_message_currentname(message, section, &name); 6676135446Strhodes for (rdataset = ISC_LIST_HEAD(name->list); 6677135446Strhodes rdataset != NULL; 6678135446Strhodes rdataset = ISC_LIST_NEXT(rdataset, link)) { 6679135446Strhodes for (result = dns_rdataset_first(rdataset); 6680135446Strhodes result == ISC_R_SUCCESS; 6681135446Strhodes result = dns_rdataset_next(rdataset)) { 6682135446Strhodes dns_rdataset_current(rdataset, &rdata); 6683135446Strhodes if (!dns_rdata_checkowner(name, rdata.rdclass, 6684135446Strhodes rdata.type, 6685135446Strhodes ISC_FALSE) || 6686135446Strhodes !dns_rdata_checknames(&rdata, name, NULL)) 6687135446Strhodes { 6688186462Sdougb rdataset->attributes |= 6689135446Strhodes DNS_RDATASETATTR_CHECKNAMES; 6690135446Strhodes } 6691135446Strhodes dns_rdata_reset(&rdata); 6692135446Strhodes } 6693135446Strhodes } 6694135446Strhodes } 6695135446Strhodes} 6696135446Strhodes 6697135446Strhodesstatic void 6698135446Strhodeschecknames(dns_message_t *message) { 6699135446Strhodes 6700135446Strhodes checknamessection(message, DNS_SECTION_ANSWER); 6701135446Strhodes checknamessection(message, DNS_SECTION_AUTHORITY); 6702135446Strhodes checknamessection(message, DNS_SECTION_ADDITIONAL); 6703135446Strhodes} 6704135446Strhodes 6705193149Sdougb/* 6706193149Sdougb * Log server NSID at log level 'level' 6707193149Sdougb */ 6708254402Serwinstatic void 6709254402Serwinlog_nsid(isc_buffer_t *opt, size_t nsid_len, resquery_t *query, 6710254402Serwin int level, isc_mem_t *mctx) 6711193149Sdougb{ 6712193149Sdougb static const char hex[17] = "0123456789abcdef"; 6713193149Sdougb char addrbuf[ISC_SOCKADDR_FORMATSIZE]; 6714254402Serwin isc_uint16_t buflen, i; 6715193149Sdougb unsigned char *p, *buf, *nsid; 6716193149Sdougb 6717193149Sdougb /* Allocate buffer for storing hex version of the NSID */ 6718262706Serwin buflen = (isc_uint16_t)nsid_len * 2 + 1; 6719193149Sdougb buf = isc_mem_get(mctx, buflen); 6720193149Sdougb if (buf == NULL) 6721254402Serwin return; 6722193149Sdougb 6723193149Sdougb /* Convert to hex */ 6724193149Sdougb p = buf; 6725254402Serwin nsid = isc_buffer_current(opt); 6726193149Sdougb for (i = 0; i < nsid_len; i++) { 6727193149Sdougb *p++ = hex[(nsid[0] >> 4) & 0xf]; 6728193149Sdougb *p++ = hex[nsid[0] & 0xf]; 6729193149Sdougb nsid++; 6730193149Sdougb } 6731193149Sdougb *p = '\0'; 6732193149Sdougb 6733193149Sdougb isc_sockaddr_format(&query->addrinfo->sockaddr, addrbuf, 6734193149Sdougb sizeof(addrbuf)); 6735193149Sdougb isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, 6736193149Sdougb DNS_LOGMODULE_RESOLVER, level, 6737193149Sdougb "received NSID '%s' from %s", buf, addrbuf); 6738193149Sdougb 6739193149Sdougb /* Clean up */ 6740193149Sdougb isc_mem_put(mctx, buf, buflen); 6741254402Serwin return; 6742193149Sdougb} 6743193149Sdougb 6744135446Strhodesstatic void 6745153816Sdougblog_packet(dns_message_t *message, int level, isc_mem_t *mctx) { 6746153816Sdougb isc_buffer_t buffer; 6747153816Sdougb char *buf = NULL; 6748153816Sdougb int len = 1024; 6749153816Sdougb isc_result_t result; 6750153816Sdougb 6751153816Sdougb if (! isc_log_wouldlog(dns_lctx, level)) 6752153816Sdougb return; 6753153816Sdougb 6754153816Sdougb /* 6755153816Sdougb * Note that these are multiline debug messages. We want a newline 6756153816Sdougb * to appear in the log after each message. 6757153816Sdougb */ 6758153816Sdougb 6759153816Sdougb do { 6760153816Sdougb buf = isc_mem_get(mctx, len); 6761153816Sdougb if (buf == NULL) 6762153816Sdougb break; 6763153816Sdougb isc_buffer_init(&buffer, buf, len); 6764153816Sdougb result = dns_message_totext(message, &dns_master_style_debug, 6765153816Sdougb 0, &buffer); 6766153816Sdougb if (result == ISC_R_NOSPACE) { 6767153816Sdougb isc_mem_put(mctx, buf, len); 6768153816Sdougb len += 1024; 6769153816Sdougb } else if (result == ISC_R_SUCCESS) 6770153816Sdougb isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, 6771153816Sdougb DNS_LOGMODULE_RESOLVER, level, 6772153816Sdougb "received packet:\n%.*s", 6773153816Sdougb (int)isc_buffer_usedlength(&buffer), 6774153816Sdougb buf); 6775153816Sdougb } while (result == ISC_R_NOSPACE); 6776153816Sdougb 6777153816Sdougb if (buf != NULL) 6778153816Sdougb isc_mem_put(mctx, buf, len); 6779153816Sdougb} 6780153816Sdougb 6781224092Sdougbstatic isc_boolean_t 6782224092Sdougbiscname(fetchctx_t *fctx) { 6783224092Sdougb isc_result_t result; 6784224092Sdougb 6785224092Sdougb result = dns_message_findname(fctx->rmessage, DNS_SECTION_ANSWER, 6786224092Sdougb &fctx->name, dns_rdatatype_cname, 0, 6787224092Sdougb NULL, NULL); 6788224092Sdougb return (result == ISC_R_SUCCESS ? ISC_TRUE : ISC_FALSE); 6789224092Sdougb} 6790224092Sdougb 6791224092Sdougbstatic isc_boolean_t 6792224092Sdougbbetterreferral(fetchctx_t *fctx) { 6793224092Sdougb isc_result_t result; 6794224092Sdougb dns_name_t *name; 6795224092Sdougb dns_rdataset_t *rdataset; 6796224092Sdougb dns_message_t *message = fctx->rmessage; 6797224092Sdougb 6798224092Sdougb for (result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); 6799224092Sdougb result == ISC_R_SUCCESS; 6800224092Sdougb result = dns_message_nextname(message, DNS_SECTION_AUTHORITY)) { 6801224092Sdougb name = NULL; 6802224092Sdougb dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name); 6803224092Sdougb if (!isstrictsubdomain(name, &fctx->domain)) 6804224092Sdougb continue; 6805224092Sdougb for (rdataset = ISC_LIST_HEAD(name->list); 6806224092Sdougb rdataset != NULL; 6807224092Sdougb rdataset = ISC_LIST_NEXT(rdataset, link)) 6808224092Sdougb if (rdataset->type == dns_rdatatype_ns) 6809224092Sdougb return (ISC_TRUE); 6810224092Sdougb } 6811224092Sdougb return (ISC_FALSE); 6812224092Sdougb} 6813224092Sdougb 6814153816Sdougbstatic void 6815254402Serwinprocess_opt(resquery_t *query, dns_rdataset_t *opt) { 6816254402Serwin dns_rdata_t rdata; 6817254402Serwin isc_buffer_t optbuf; 6818254402Serwin isc_result_t result; 6819254402Serwin isc_uint16_t optcode; 6820254402Serwin isc_uint16_t optlen; 6821254402Serwin 6822254402Serwin result = dns_rdataset_first(opt); 6823254402Serwin if (result == ISC_R_SUCCESS) { 6824254402Serwin dns_rdata_init(&rdata); 6825254402Serwin dns_rdataset_current(opt, &rdata); 6826254402Serwin isc_buffer_init(&optbuf, rdata.data, rdata.length); 6827254402Serwin isc_buffer_add(&optbuf, rdata.length); 6828254402Serwin while (isc_buffer_remaininglength(&optbuf) >= 4) { 6829254402Serwin optcode = isc_buffer_getuint16(&optbuf); 6830254402Serwin optlen = isc_buffer_getuint16(&optbuf); 6831254402Serwin INSIST(optlen <= isc_buffer_remaininglength(&optbuf)); 6832254402Serwin switch (optcode) { 6833254402Serwin case DNS_OPT_NSID: 6834254402Serwin if (query->options & DNS_FETCHOPT_WANTNSID) 6835254402Serwin log_nsid(&optbuf, optlen, query, 6836254402Serwin ISC_LOG_INFO, 6837254402Serwin query->fctx->res->mctx); 6838254402Serwin isc_buffer_forward(&optbuf, optlen); 6839254402Serwin break; 6840254402Serwin default: 6841254402Serwin isc_buffer_forward(&optbuf, optlen); 6842254402Serwin break; 6843254402Serwin } 6844254402Serwin } 6845254402Serwin INSIST(isc_buffer_remaininglength(&optbuf) == 0U); 6846254402Serwin } 6847254402Serwin} 6848254402Serwin 6849254402Serwinstatic void 6850135446Strhodesresquery_response(isc_task_t *task, isc_event_t *event) { 6851135446Strhodes isc_result_t result = ISC_R_SUCCESS; 6852135446Strhodes resquery_t *query = event->ev_arg; 6853135446Strhodes dns_dispatchevent_t *devent = (dns_dispatchevent_t *)event; 6854135446Strhodes isc_boolean_t keep_trying, get_nameservers, resend; 6855135446Strhodes isc_boolean_t truncated; 6856135446Strhodes dns_message_t *message; 6857193149Sdougb dns_rdataset_t *opt; 6858135446Strhodes fetchctx_t *fctx; 6859135446Strhodes dns_name_t *fname; 6860135446Strhodes dns_fixedname_t foundname; 6861135446Strhodes isc_stdtime_t now; 6862135446Strhodes isc_time_t tnow, *finish; 6863135446Strhodes dns_adbaddrinfo_t *addrinfo; 6864135446Strhodes unsigned int options; 6865135446Strhodes unsigned int findoptions; 6866135446Strhodes isc_result_t broken_server; 6867193149Sdougb badnstype_t broken_type = badns_response; 6868218384Sdougb isc_boolean_t no_response; 6869135446Strhodes 6870135446Strhodes REQUIRE(VALID_QUERY(query)); 6871135446Strhodes fctx = query->fctx; 6872135446Strhodes options = query->options; 6873135446Strhodes REQUIRE(VALID_FCTX(fctx)); 6874135446Strhodes REQUIRE(event->ev_type == DNS_EVENT_DISPATCH); 6875135446Strhodes 6876135446Strhodes QTRACE("response"); 6877135446Strhodes 6878193149Sdougb if (isc_sockaddr_pf(&query->addrinfo->sockaddr) == PF_INET) 6879193149Sdougb inc_stats(fctx->res, dns_resstatscounter_responsev4); 6880193149Sdougb else 6881193149Sdougb inc_stats(fctx->res, dns_resstatscounter_responsev6); 6882193149Sdougb 6883135446Strhodes (void)isc_timer_touch(fctx->timer); 6884135446Strhodes 6885135446Strhodes keep_trying = ISC_FALSE; 6886135446Strhodes broken_server = ISC_R_SUCCESS; 6887135446Strhodes get_nameservers = ISC_FALSE; 6888135446Strhodes resend = ISC_FALSE; 6889135446Strhodes truncated = ISC_FALSE; 6890135446Strhodes finish = NULL; 6891218384Sdougb no_response = ISC_FALSE; 6892135446Strhodes 6893135446Strhodes if (fctx->res->exiting) { 6894135446Strhodes result = ISC_R_SHUTTINGDOWN; 6895135446Strhodes goto done; 6896135446Strhodes } 6897135446Strhodes 6898135446Strhodes fctx->timeouts = 0; 6899193149Sdougb fctx->timeout = ISC_FALSE; 6900224092Sdougb fctx->addrinfo = query->addrinfo; 6901135446Strhodes 6902135446Strhodes /* 6903135446Strhodes * XXXRTH We should really get the current time just once. We 6904193149Sdougb * need a routine to convert from an isc_time_t to an 6905193149Sdougb * isc_stdtime_t. 6906135446Strhodes */ 6907135446Strhodes TIME_NOW(&tnow); 6908135446Strhodes finish = &tnow; 6909135446Strhodes isc_stdtime_get(&now); 6910135446Strhodes 6911135446Strhodes /* 6912135446Strhodes * Did the dispatcher have a problem? 6913135446Strhodes */ 6914135446Strhodes if (devent->result != ISC_R_SUCCESS) { 6915135446Strhodes if (devent->result == ISC_R_EOF && 6916135446Strhodes (query->options & DNS_FETCHOPT_NOEDNS0) == 0) { 6917135446Strhodes /* 6918135446Strhodes * The problem might be that they 6919135446Strhodes * don't understand EDNS0. Turn it 6920135446Strhodes * off and try again. 6921135446Strhodes */ 6922135446Strhodes options |= DNS_FETCHOPT_NOEDNS0; 6923135446Strhodes resend = ISC_TRUE; 6924254402Serwin add_bad_edns(fctx, &query->addrinfo->sockaddr); 6925135446Strhodes } else { 6926135446Strhodes /* 6927135446Strhodes * There's no hope for this query. 6928135446Strhodes */ 6929135446Strhodes keep_trying = ISC_TRUE; 6930186462Sdougb 6931186462Sdougb /* 6932186462Sdougb * If this is a network error on an exclusive query 6933186462Sdougb * socket, mark the server as bad so that we won't try 6934218384Sdougb * it for this fetch again. Also adjust finish and 6935218384Sdougb * no_response so that we penalize this address in SRTT 6936218384Sdougb * adjustment later. 6937186462Sdougb */ 6938186462Sdougb if (query->exclusivesocket && 6939186462Sdougb (devent->result == ISC_R_HOSTUNREACH || 6940186462Sdougb devent->result == ISC_R_NETUNREACH || 6941186462Sdougb devent->result == ISC_R_CONNREFUSED || 6942186462Sdougb devent->result == ISC_R_CANCELED)) { 6943186462Sdougb broken_server = devent->result; 6944193149Sdougb broken_type = badns_unreachable; 6945218384Sdougb finish = NULL; 6946218384Sdougb no_response = ISC_TRUE; 6947186462Sdougb } 6948135446Strhodes } 6949135446Strhodes goto done; 6950135446Strhodes } 6951135446Strhodes 6952135446Strhodes message = fctx->rmessage; 6953135446Strhodes 6954135446Strhodes if (query->tsig != NULL) { 6955135446Strhodes result = dns_message_setquerytsig(message, query->tsig); 6956135446Strhodes if (result != ISC_R_SUCCESS) 6957135446Strhodes goto done; 6958135446Strhodes } 6959135446Strhodes 6960135446Strhodes if (query->tsigkey) { 6961135446Strhodes result = dns_message_settsigkey(message, query->tsigkey); 6962135446Strhodes if (result != ISC_R_SUCCESS) 6963135446Strhodes goto done; 6964135446Strhodes } 6965135446Strhodes 6966292321Sdelphij dns_message_setclass(message, fctx->res->rdclass); 6967292321Sdelphij 6968135446Strhodes result = dns_message_parse(message, &devent->buffer, 0); 6969135446Strhodes if (result != ISC_R_SUCCESS) { 6970135446Strhodes switch (result) { 6971135446Strhodes case ISC_R_UNEXPECTEDEND: 6972135446Strhodes if (!message->question_ok || 6973135446Strhodes (message->flags & DNS_MESSAGEFLAG_TC) == 0 || 6974135446Strhodes (options & DNS_FETCHOPT_TCP) != 0) { 6975135446Strhodes /* 6976135446Strhodes * Either the message ended prematurely, 6977135446Strhodes * and/or wasn't marked as being truncated, 6978135446Strhodes * and/or this is a response to a query we 6979135446Strhodes * sent over TCP. In all of these cases, 6980135446Strhodes * something is wrong with the remote 6981135446Strhodes * server and we don't want to retry using 6982135446Strhodes * TCP. 6983135446Strhodes */ 6984135446Strhodes if ((query->options & DNS_FETCHOPT_NOEDNS0) 6985135446Strhodes == 0) { 6986135446Strhodes /* 6987135446Strhodes * The problem might be that they 6988135446Strhodes * don't understand EDNS0. Turn it 6989135446Strhodes * off and try again. 6990135446Strhodes */ 6991135446Strhodes options |= DNS_FETCHOPT_NOEDNS0; 6992135446Strhodes resend = ISC_TRUE; 6993254402Serwin add_bad_edns(fctx, 6994254402Serwin &query->addrinfo->sockaddr); 6995193149Sdougb inc_stats(fctx->res, 6996193149Sdougb dns_resstatscounter_edns0fail); 6997135446Strhodes } else { 6998135446Strhodes broken_server = result; 6999135446Strhodes keep_trying = ISC_TRUE; 7000135446Strhodes } 7001135446Strhodes goto done; 7002135446Strhodes } 7003135446Strhodes /* 7004135446Strhodes * We defer retrying via TCP for a bit so we can 7005135446Strhodes * check out this message further. 7006135446Strhodes */ 7007135446Strhodes truncated = ISC_TRUE; 7008135446Strhodes break; 7009135446Strhodes case DNS_R_FORMERR: 7010135446Strhodes if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) { 7011135446Strhodes /* 7012135446Strhodes * The problem might be that they 7013135446Strhodes * don't understand EDNS0. Turn it 7014135446Strhodes * off and try again. 7015135446Strhodes */ 7016135446Strhodes options |= DNS_FETCHOPT_NOEDNS0; 7017135446Strhodes resend = ISC_TRUE; 7018254402Serwin add_bad_edns(fctx, &query->addrinfo->sockaddr); 7019193149Sdougb inc_stats(fctx->res, 7020193149Sdougb dns_resstatscounter_edns0fail); 7021135446Strhodes } else { 7022135446Strhodes broken_server = DNS_R_UNEXPECTEDRCODE; 7023135446Strhodes keep_trying = ISC_TRUE; 7024135446Strhodes } 7025135446Strhodes goto done; 7026135446Strhodes default: 7027135446Strhodes /* 7028135446Strhodes * Something bad has happened. 7029135446Strhodes */ 7030135446Strhodes goto done; 7031135446Strhodes } 7032135446Strhodes } 7033135446Strhodes 7034193149Sdougb 7035135446Strhodes /* 7036153816Sdougb * Log the incoming packet. 7037153816Sdougb */ 7038153816Sdougb log_packet(message, ISC_LOG_DEBUG(10), fctx->res->mctx); 7039153816Sdougb 7040292321Sdelphij if (message->rdclass != fctx->res->rdclass) { 7041292321Sdelphij resend = ISC_TRUE; 7042292321Sdelphij FCTXTRACE("bad class"); 7043292321Sdelphij goto done; 7044292321Sdelphij } 7045292321Sdelphij 7046153816Sdougb /* 7047254402Serwin * Process receive opt record. 7048193149Sdougb */ 7049193149Sdougb opt = dns_message_getopt(message); 7050254402Serwin if (opt != NULL) 7051254402Serwin process_opt(query, opt); 7052193149Sdougb 7053193149Sdougb /* 7054135446Strhodes * If the message is signed, check the signature. If not, this 7055135446Strhodes * returns success anyway. 7056135446Strhodes */ 7057135446Strhodes result = dns_message_checksig(message, fctx->res->view); 7058135446Strhodes if (result != ISC_R_SUCCESS) 7059135446Strhodes goto done; 7060135446Strhodes 7061135446Strhodes /* 7062135446Strhodes * The dispatcher should ensure we only get responses with QR set. 7063135446Strhodes */ 7064135446Strhodes INSIST((message->flags & DNS_MESSAGEFLAG_QR) != 0); 7065135446Strhodes /* 7066135446Strhodes * INSIST() that the message comes from the place we sent it to, 7067135446Strhodes * since the dispatch code should ensure this. 7068135446Strhodes * 7069135446Strhodes * INSIST() that the message id is correct (this should also be 7070135446Strhodes * ensured by the dispatch code). 7071135446Strhodes */ 7072135446Strhodes 7073218384Sdougb /* 7074218384Sdougb * We have an affirmative response to the query and we have 7075218384Sdougb * previously got a response from this server which indicated 7076218384Sdougb * EDNS may not be supported so we can now cache the lack of 7077218384Sdougb * EDNS support. 7078218384Sdougb */ 7079218384Sdougb if (opt == NULL && 7080218384Sdougb (message->rcode == dns_rcode_noerror || 7081218384Sdougb message->rcode == dns_rcode_nxdomain || 7082218384Sdougb message->rcode == dns_rcode_refused || 7083218384Sdougb message->rcode == dns_rcode_yxdomain) && 7084218384Sdougb bad_edns(fctx, &query->addrinfo->sockaddr)) { 7085218384Sdougb char addrbuf[ISC_SOCKADDR_FORMATSIZE]; 7086218384Sdougb isc_sockaddr_format(&query->addrinfo->sockaddr, addrbuf, 7087218384Sdougb sizeof(addrbuf)); 7088218384Sdougb dns_adb_changeflags(fctx->adb, query->addrinfo, 7089218384Sdougb DNS_FETCHOPT_NOEDNS0, 7090218384Sdougb DNS_FETCHOPT_NOEDNS0); 7091218384Sdougb } 7092135446Strhodes 7093135446Strhodes /* 7094135446Strhodes * Deal with truncated responses by retrying using TCP. 7095135446Strhodes */ 7096135446Strhodes if ((message->flags & DNS_MESSAGEFLAG_TC) != 0) 7097135446Strhodes truncated = ISC_TRUE; 7098135446Strhodes 7099135446Strhodes if (truncated) { 7100193149Sdougb inc_stats(fctx->res, dns_resstatscounter_truncated); 7101135446Strhodes if ((options & DNS_FETCHOPT_TCP) != 0) { 7102135446Strhodes broken_server = DNS_R_TRUNCATEDTCP; 7103135446Strhodes keep_trying = ISC_TRUE; 7104135446Strhodes } else { 7105135446Strhodes options |= DNS_FETCHOPT_TCP; 7106135446Strhodes resend = ISC_TRUE; 7107135446Strhodes } 7108135446Strhodes goto done; 7109135446Strhodes } 7110135446Strhodes 7111135446Strhodes /* 7112135446Strhodes * Is it a query response? 7113135446Strhodes */ 7114135446Strhodes if (message->opcode != dns_opcode_query) { 7115135446Strhodes /* XXXRTH Log */ 7116135446Strhodes broken_server = DNS_R_UNEXPECTEDOPCODE; 7117135446Strhodes keep_trying = ISC_TRUE; 7118135446Strhodes goto done; 7119135446Strhodes } 7120135446Strhodes 7121135446Strhodes /* 7122193149Sdougb * Update statistics about erroneous responses. 7123193149Sdougb */ 7124193149Sdougb if (message->rcode != dns_rcode_noerror) { 7125193149Sdougb switch (message->rcode) { 7126193149Sdougb case dns_rcode_nxdomain: 7127193149Sdougb inc_stats(fctx->res, dns_resstatscounter_nxdomain); 7128193149Sdougb break; 7129193149Sdougb case dns_rcode_servfail: 7130193149Sdougb inc_stats(fctx->res, dns_resstatscounter_servfail); 7131193149Sdougb break; 7132193149Sdougb case dns_rcode_formerr: 7133193149Sdougb inc_stats(fctx->res, dns_resstatscounter_formerr); 7134193149Sdougb break; 7135193149Sdougb default: 7136193149Sdougb inc_stats(fctx->res, dns_resstatscounter_othererror); 7137193149Sdougb break; 7138193149Sdougb } 7139193149Sdougb } 7140193149Sdougb 7141193149Sdougb /* 7142135446Strhodes * Is the remote server broken, or does it dislike us? 7143135446Strhodes */ 7144135446Strhodes if (message->rcode != dns_rcode_noerror && 7145135446Strhodes message->rcode != dns_rcode_nxdomain) { 7146186462Sdougb if (((message->rcode == dns_rcode_formerr || 7147218384Sdougb message->rcode == dns_rcode_notimp) || 7148218384Sdougb (message->rcode == dns_rcode_servfail && 7149218384Sdougb dns_message_getopt(message) == NULL)) && 7150135446Strhodes (query->options & DNS_FETCHOPT_NOEDNS0) == 0) { 7151135446Strhodes /* 7152135446Strhodes * It's very likely they don't like EDNS0. 7153186462Sdougb * If the response code is SERVFAIL, also check if the 7154186462Sdougb * response contains an OPT RR and don't cache the 7155186462Sdougb * failure since it can be returned for various other 7156186462Sdougb * reasons. 7157135446Strhodes * 7158135446Strhodes * XXXRTH We should check if the question 7159193149Sdougb * we're asking requires EDNS0, and 7160193149Sdougb * if so, we should bail out. 7161135446Strhodes */ 7162135446Strhodes options |= DNS_FETCHOPT_NOEDNS0; 7163135446Strhodes resend = ISC_TRUE; 7164135446Strhodes /* 7165218384Sdougb * Remember that they may not like EDNS0. 7166135446Strhodes */ 7167218384Sdougb add_bad_edns(fctx, &query->addrinfo->sockaddr); 7168193149Sdougb inc_stats(fctx->res, dns_resstatscounter_edns0fail); 7169135446Strhodes } else if (message->rcode == dns_rcode_formerr) { 7170135446Strhodes if (ISFORWARDER(query->addrinfo)) { 7171135446Strhodes /* 7172135446Strhodes * This forwarder doesn't understand us, 7173135446Strhodes * but other forwarders might. Keep trying. 7174135446Strhodes */ 7175135446Strhodes broken_server = DNS_R_REMOTEFORMERR; 7176135446Strhodes keep_trying = ISC_TRUE; 7177135446Strhodes } else { 7178135446Strhodes /* 7179135446Strhodes * The server doesn't understand us. Since 7180135446Strhodes * all servers for a zone need similar 7181135446Strhodes * capabilities, we assume that we will get 7182135446Strhodes * FORMERR from all servers, and thus we 7183135446Strhodes * cannot make any more progress with this 7184135446Strhodes * fetch. 7185135446Strhodes */ 7186224092Sdougb log_formerr(fctx, "server sent FORMERR"); 7187135446Strhodes result = DNS_R_FORMERR; 7188135446Strhodes } 7189135446Strhodes } else if (message->rcode == dns_rcode_yxdomain) { 7190135446Strhodes /* 7191135446Strhodes * DNAME mapping failed because the new name 7192135446Strhodes * was too long. There's no chance of success 7193135446Strhodes * for this fetch. 7194135446Strhodes */ 7195135446Strhodes result = DNS_R_YXDOMAIN; 7196170222Sdougb } else if (message->rcode == dns_rcode_badvers) { 7197170222Sdougb unsigned int flags, mask; 7198170222Sdougb unsigned int version; 7199170222Sdougb 7200170222Sdougb resend = ISC_TRUE; 7201225361Sdougb INSIST(opt != NULL); 7202170222Sdougb version = (opt->ttl >> 16) & 0xff; 7203170222Sdougb flags = (version << DNS_FETCHOPT_EDNSVERSIONSHIFT) | 7204170222Sdougb DNS_FETCHOPT_EDNSVERSIONSET; 7205170222Sdougb mask = DNS_FETCHOPT_EDNSVERSIONMASK | 7206170222Sdougb DNS_FETCHOPT_EDNSVERSIONSET; 7207170222Sdougb switch (version) { 7208170222Sdougb case 0: 7209170222Sdougb dns_adb_changeflags(fctx->adb, query->addrinfo, 7210170222Sdougb flags, mask); 7211170222Sdougb break; 7212170222Sdougb default: 7213170222Sdougb broken_server = DNS_R_BADVERS; 7214170222Sdougb keep_trying = ISC_TRUE; 7215170222Sdougb break; 7216170222Sdougb } 7217135446Strhodes } else { 7218135446Strhodes /* 7219135446Strhodes * XXXRTH log. 7220135446Strhodes */ 7221135446Strhodes broken_server = DNS_R_UNEXPECTEDRCODE; 7222135446Strhodes INSIST(broken_server != ISC_R_SUCCESS); 7223135446Strhodes keep_trying = ISC_TRUE; 7224135446Strhodes } 7225135446Strhodes goto done; 7226135446Strhodes } 7227135446Strhodes 7228135446Strhodes /* 7229135446Strhodes * Is the question the same as the one we asked? 7230135446Strhodes */ 7231135446Strhodes result = same_question(fctx); 7232135446Strhodes if (result != ISC_R_SUCCESS) { 7233135446Strhodes /* XXXRTH Log */ 7234135446Strhodes if (result == DNS_R_FORMERR) 7235135446Strhodes keep_trying = ISC_TRUE; 7236135446Strhodes goto done; 7237135446Strhodes } 7238135446Strhodes 7239135446Strhodes /* 7240135446Strhodes * Is the server lame? 7241135446Strhodes */ 7242135446Strhodes if (fctx->res->lame_ttl != 0 && !ISFORWARDER(query->addrinfo) && 7243135446Strhodes is_lame(fctx)) { 7244193149Sdougb inc_stats(fctx->res, dns_resstatscounter_lame); 7245135446Strhodes log_lame(fctx, query->addrinfo); 7246135446Strhodes result = dns_adb_marklame(fctx->adb, query->addrinfo, 7247170222Sdougb &fctx->name, fctx->type, 7248135446Strhodes now + fctx->res->lame_ttl); 7249135446Strhodes if (result != ISC_R_SUCCESS) 7250135446Strhodes isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, 7251135446Strhodes DNS_LOGMODULE_RESOLVER, ISC_LOG_ERROR, 7252135446Strhodes "could not mark server as lame: %s", 7253135446Strhodes isc_result_totext(result)); 7254135446Strhodes broken_server = DNS_R_LAME; 7255135446Strhodes keep_trying = ISC_TRUE; 7256135446Strhodes goto done; 7257135446Strhodes } 7258135446Strhodes 7259135446Strhodes /* 7260135446Strhodes * Enforce delegations only zones like NET and COM. 7261135446Strhodes */ 7262135446Strhodes if (!ISFORWARDER(query->addrinfo) && 7263135446Strhodes dns_view_isdelegationonly(fctx->res->view, &fctx->domain) && 7264135446Strhodes !dns_name_equal(&fctx->domain, &fctx->name) && 7265135446Strhodes fix_mustbedelegationornxdomain(message, fctx)) { 7266135446Strhodes char namebuf[DNS_NAME_FORMATSIZE]; 7267135446Strhodes char domainbuf[DNS_NAME_FORMATSIZE]; 7268135446Strhodes char addrbuf[ISC_SOCKADDR_FORMATSIZE]; 7269135446Strhodes char classbuf[64]; 7270135446Strhodes char typebuf[64]; 7271135446Strhodes 7272135446Strhodes dns_name_format(&fctx->name, namebuf, sizeof(namebuf)); 7273135446Strhodes dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf)); 7274135446Strhodes dns_rdatatype_format(fctx->type, typebuf, sizeof(typebuf)); 7275135446Strhodes dns_rdataclass_format(fctx->res->rdclass, classbuf, 7276135446Strhodes sizeof(classbuf)); 7277135446Strhodes isc_sockaddr_format(&query->addrinfo->sockaddr, addrbuf, 7278135446Strhodes sizeof(addrbuf)); 7279135446Strhodes 7280135446Strhodes isc_log_write(dns_lctx, DNS_LOGCATEGORY_DELEGATION_ONLY, 7281135446Strhodes DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE, 7282135446Strhodes "enforced delegation-only for '%s' (%s/%s/%s) " 7283135446Strhodes "from %s", 7284135446Strhodes domainbuf, namebuf, typebuf, classbuf, addrbuf); 7285135446Strhodes } 7286135446Strhodes 7287143731Sdougb if ((fctx->res->options & DNS_RESOLVER_CHECKNAMES) != 0) 7288135446Strhodes checknames(message); 7289135446Strhodes 7290135446Strhodes /* 7291143731Sdougb * Clear cache bits. 7292143731Sdougb */ 7293143731Sdougb fctx->attributes &= ~(FCTX_ATTR_WANTNCACHE | FCTX_ATTR_WANTCACHE); 7294143731Sdougb 7295143731Sdougb /* 7296135446Strhodes * Did we get any answers? 7297135446Strhodes */ 7298135446Strhodes if (message->counts[DNS_SECTION_ANSWER] > 0 && 7299135446Strhodes (message->rcode == dns_rcode_noerror || 7300135446Strhodes message->rcode == dns_rcode_nxdomain)) { 7301135446Strhodes /* 7302224092Sdougb * [normal case] 7303224092Sdougb * We've got answers. If it has an authoritative answer or an 7304224092Sdougb * answer from a forwarder, we're done. 7305135446Strhodes */ 7306224092Sdougb if ((message->flags & DNS_MESSAGEFLAG_AA) != 0 || 7307224092Sdougb ISFORWARDER(query->addrinfo)) 7308224092Sdougb result = answer_response(fctx); 7309224092Sdougb else if (iscname(fctx) && 7310224092Sdougb fctx->type != dns_rdatatype_any && 7311224092Sdougb fctx->type != dns_rdatatype_cname) { 7312224092Sdougb /* 7313224092Sdougb * A BIND8 server could return a non-authoritative 7314224092Sdougb * answer when a CNAME is followed. We should treat 7315224092Sdougb * it as a valid answer. 7316224092Sdougb */ 7317224092Sdougb result = answer_response(fctx); 7318224092Sdougb } else if (fctx->type != dns_rdatatype_ns && 7319224092Sdougb !betterreferral(fctx)) { 7320224092Sdougb /* 7321224092Sdougb * Lame response !!!. 7322224092Sdougb */ 7323224092Sdougb result = answer_response(fctx); 7324224092Sdougb } else { 7325224092Sdougb if (fctx->type == dns_rdatatype_ns) { 7326224092Sdougb /* 7327224092Sdougb * A BIND 8 server could incorrectly return a 7328224092Sdougb * non-authoritative answer to an NS query 7329224092Sdougb * instead of a referral. Since this answer 7330224092Sdougb * lacks the SIGs necessary to do DNSSEC 7331224092Sdougb * validation, we must invoke the following 7332224092Sdougb * special kludge to treat it as a referral. 7333224092Sdougb */ 7334224092Sdougb result = noanswer_response(fctx, NULL, 7335224092Sdougb LOOK_FOR_NS_IN_ANSWER); 7336224092Sdougb } else { 7337224092Sdougb /* 7338224092Sdougb * Some other servers may still somehow include 7339224092Sdougb * an answer when it should return a referral 7340224092Sdougb * with an empty answer. Check to see if we can 7341224092Sdougb * treat this as a referral by ignoring the 7342224092Sdougb * answer. Further more, there may be an 7343224092Sdougb * implementation that moves A/AAAA glue records 7344224092Sdougb * to the answer section for that type of 7345224092Sdougb * delegation when the query is for that glue 7346224092Sdougb * record. LOOK_FOR_GLUE_IN_ANSWER will handle 7347224092Sdougb * such a corner case. 7348224092Sdougb */ 7349224092Sdougb result = noanswer_response(fctx, NULL, 7350224092Sdougb LOOK_FOR_GLUE_IN_ANSWER); 7351224092Sdougb } 7352135446Strhodes if (result != DNS_R_DELEGATION) { 7353135446Strhodes /* 7354224092Sdougb * At this point, AA is not set, the response 7355224092Sdougb * is not a referral, and the server is not a 7356224092Sdougb * forwarder. It is technically lame and it's 7357224092Sdougb * easier to treat it as such than to figure out 7358135446Strhodes * some more elaborate course of action. 7359135446Strhodes */ 7360135446Strhodes broken_server = DNS_R_LAME; 7361135446Strhodes keep_trying = ISC_TRUE; 7362135446Strhodes goto done; 7363135446Strhodes } 7364135446Strhodes goto force_referral; 7365135446Strhodes } 7366135446Strhodes if (result != ISC_R_SUCCESS) { 7367135446Strhodes if (result == DNS_R_FORMERR) 7368135446Strhodes keep_trying = ISC_TRUE; 7369135446Strhodes goto done; 7370135446Strhodes } 7371135446Strhodes } else if (message->counts[DNS_SECTION_AUTHORITY] > 0 || 7372135446Strhodes message->rcode == dns_rcode_noerror || 7373135446Strhodes message->rcode == dns_rcode_nxdomain) { 7374135446Strhodes /* 7375135446Strhodes * NXDOMAIN, NXRDATASET, or referral. 7376135446Strhodes */ 7377224092Sdougb result = noanswer_response(fctx, NULL, 0); 7378262706Serwin switch (result) { 7379262706Serwin case ISC_R_SUCCESS: 7380262706Serwin case DNS_R_CHASEDSSERVERS: 7381262706Serwin break; 7382262706Serwin case DNS_R_DELEGATION: 7383262706Serwin force_referral: 7384135446Strhodes /* 7385135446Strhodes * We don't have the answer, but we know a better 7386135446Strhodes * place to look. 7387135446Strhodes */ 7388135446Strhodes get_nameservers = ISC_TRUE; 7389135446Strhodes keep_trying = ISC_TRUE; 7390135446Strhodes /* 7391135446Strhodes * We have a new set of name servers, and it 7392135446Strhodes * has not experienced any restarts yet. 7393135446Strhodes */ 7394135446Strhodes fctx->restarts = 0; 7395193149Sdougb 7396193149Sdougb /* 7397193149Sdougb * Update local statistics counters collected for each 7398193149Sdougb * new zone. 7399193149Sdougb */ 7400193149Sdougb fctx->referrals++; 7401193149Sdougb fctx->querysent = 0; 7402193149Sdougb fctx->lamecount = 0; 7403193149Sdougb fctx->neterr = 0; 7404193149Sdougb fctx->badresp = 0; 7405193149Sdougb fctx->adberr = 0; 7406193149Sdougb 7407135446Strhodes result = ISC_R_SUCCESS; 7408262706Serwin break; 7409262706Serwin default: 7410135446Strhodes /* 7411135446Strhodes * Something has gone wrong. 7412135446Strhodes */ 7413135446Strhodes if (result == DNS_R_FORMERR) 7414135446Strhodes keep_trying = ISC_TRUE; 7415135446Strhodes goto done; 7416135446Strhodes } 7417135446Strhodes } else { 7418135446Strhodes /* 7419135446Strhodes * The server is insane. 7420135446Strhodes */ 7421135446Strhodes /* XXXRTH Log */ 7422135446Strhodes broken_server = DNS_R_UNEXPECTEDRCODE; 7423135446Strhodes keep_trying = ISC_TRUE; 7424135446Strhodes goto done; 7425135446Strhodes } 7426135446Strhodes 7427135446Strhodes /* 7428135446Strhodes * Follow additional section data chains. 7429135446Strhodes */ 7430135446Strhodes chase_additional(fctx); 7431135446Strhodes 7432135446Strhodes /* 7433135446Strhodes * Cache the cacheable parts of the message. This may also cause 7434135446Strhodes * work to be queued to the DNSSEC validator. 7435135446Strhodes */ 7436135446Strhodes if (WANTCACHE(fctx)) { 7437153816Sdougb result = cache_message(fctx, query->addrinfo, now); 7438135446Strhodes if (result != ISC_R_SUCCESS) 7439135446Strhodes goto done; 7440135446Strhodes } 7441135446Strhodes 7442135446Strhodes /* 7443135446Strhodes * Ncache the negatively cacheable parts of the message. This may 7444135446Strhodes * also cause work to be queued to the DNSSEC validator. 7445135446Strhodes */ 7446135446Strhodes if (WANTNCACHE(fctx)) { 7447135446Strhodes dns_rdatatype_t covers; 7448135446Strhodes if (message->rcode == dns_rcode_nxdomain) 7449135446Strhodes covers = dns_rdatatype_any; 7450135446Strhodes else 7451135446Strhodes covers = fctx->type; 7452135446Strhodes 7453135446Strhodes /* 7454135446Strhodes * Cache any negative cache entries in the message. 7455135446Strhodes */ 7456153816Sdougb result = ncache_message(fctx, query->addrinfo, covers, now); 7457135446Strhodes } 7458135446Strhodes 7459135446Strhodes done: 7460135446Strhodes /* 7461135446Strhodes * Remember the query's addrinfo, in case we need to mark the 7462135446Strhodes * server as broken. 7463135446Strhodes */ 7464135446Strhodes addrinfo = query->addrinfo; 7465135446Strhodes 7466135446Strhodes /* 7467135446Strhodes * Cancel the query. 7468135446Strhodes * 7469135446Strhodes * XXXRTH Don't cancel the query if waiting for validation? 7470135446Strhodes */ 7471218384Sdougb fctx_cancelquery(&query, &devent, finish, no_response); 7472135446Strhodes 7473135446Strhodes if (keep_trying) { 7474135446Strhodes if (result == DNS_R_FORMERR) 7475135446Strhodes broken_server = DNS_R_FORMERR; 7476135446Strhodes if (broken_server != ISC_R_SUCCESS) { 7477135446Strhodes /* 7478135446Strhodes * Add this server to the list of bad servers for 7479135446Strhodes * this fctx. 7480135446Strhodes */ 7481193149Sdougb add_bad(fctx, addrinfo, broken_server, broken_type); 7482135446Strhodes } 7483135446Strhodes 7484135446Strhodes if (get_nameservers) { 7485135446Strhodes dns_name_t *name; 7486135446Strhodes dns_fixedname_init(&foundname); 7487135446Strhodes fname = dns_fixedname_name(&foundname); 7488135446Strhodes if (result != ISC_R_SUCCESS) { 7489193149Sdougb fctx_done(fctx, DNS_R_SERVFAIL, __LINE__); 7490135446Strhodes return; 7491135446Strhodes } 7492135446Strhodes findoptions = 0; 7493143731Sdougb if (dns_rdatatype_atparent(fctx->type)) 7494143731Sdougb findoptions |= DNS_DBFIND_NOEXACT; 7495135446Strhodes if ((options & DNS_FETCHOPT_UNSHARED) == 0) 7496135446Strhodes name = &fctx->name; 7497135446Strhodes else 7498135446Strhodes name = &fctx->domain; 7499135446Strhodes result = dns_view_findzonecut(fctx->res->view, 7500135446Strhodes name, fname, 7501143731Sdougb now, findoptions, 7502143731Sdougb ISC_TRUE, 7503135446Strhodes &fctx->nameservers, 7504135446Strhodes NULL); 7505135446Strhodes if (result != ISC_R_SUCCESS) { 7506135446Strhodes FCTXTRACE("couldn't find a zonecut"); 7507193149Sdougb fctx_done(fctx, DNS_R_SERVFAIL, __LINE__); 7508135446Strhodes return; 7509135446Strhodes } 7510135446Strhodes if (!dns_name_issubdomain(fname, &fctx->domain)) { 7511135446Strhodes /* 7512135446Strhodes * The best nameservers are now above our 7513135446Strhodes * QDOMAIN. 7514135446Strhodes */ 7515135446Strhodes FCTXTRACE("nameservers now above QDOMAIN"); 7516193149Sdougb fctx_done(fctx, DNS_R_SERVFAIL, __LINE__); 7517135446Strhodes return; 7518135446Strhodes } 7519236374Sdougb dns_name_free(&fctx->domain, fctx->mctx); 7520135446Strhodes dns_name_init(&fctx->domain, NULL); 7521236374Sdougb result = dns_name_dup(fname, fctx->mctx, &fctx->domain); 7522135446Strhodes if (result != ISC_R_SUCCESS) { 7523193149Sdougb fctx_done(fctx, DNS_R_SERVFAIL, __LINE__); 7524135446Strhodes return; 7525135446Strhodes } 7526234010Sdougb fctx->ns_ttl = fctx->nameservers.ttl; 7527234010Sdougb fctx->ns_ttl_ok = ISC_TRUE; 7528135446Strhodes fctx_cancelqueries(fctx, ISC_TRUE); 7529135446Strhodes fctx_cleanupfinds(fctx); 7530135446Strhodes fctx_cleanupaltfinds(fctx); 7531135446Strhodes fctx_cleanupforwaddrs(fctx); 7532135446Strhodes fctx_cleanupaltaddrs(fctx); 7533135446Strhodes } 7534135446Strhodes /* 7535135446Strhodes * Try again. 7536135446Strhodes */ 7537205292Sdougb fctx_try(fctx, !get_nameservers, ISC_FALSE); 7538135446Strhodes } else if (resend) { 7539135446Strhodes /* 7540135446Strhodes * Resend (probably with changed options). 7541135446Strhodes */ 7542135446Strhodes FCTXTRACE("resend"); 7543193149Sdougb inc_stats(fctx->res, dns_resstatscounter_retry); 7544135446Strhodes result = fctx_query(fctx, addrinfo, options); 7545135446Strhodes if (result != ISC_R_SUCCESS) 7546193149Sdougb fctx_done(fctx, result, __LINE__); 7547135446Strhodes } else if (result == ISC_R_SUCCESS && !HAVE_ANSWER(fctx)) { 7548135446Strhodes /* 7549135446Strhodes * All has gone well so far, but we are waiting for the 7550135446Strhodes * DNSSEC validator to validate the answer. 7551135446Strhodes */ 7552135446Strhodes FCTXTRACE("wait for validator"); 7553135446Strhodes fctx_cancelqueries(fctx, ISC_TRUE); 7554135446Strhodes /* 7555135446Strhodes * We must not retransmit while the validator is working; 7556135446Strhodes * it has references to the current rmessage. 7557135446Strhodes */ 7558135446Strhodes result = fctx_stopidletimer(fctx); 7559135446Strhodes if (result != ISC_R_SUCCESS) 7560193149Sdougb fctx_done(fctx, result, __LINE__); 7561135446Strhodes } else if (result == DNS_R_CHASEDSSERVERS) { 7562135446Strhodes unsigned int n; 7563193149Sdougb add_bad(fctx, addrinfo, result, broken_type); 7564135446Strhodes fctx_cancelqueries(fctx, ISC_TRUE); 7565135446Strhodes fctx_cleanupfinds(fctx); 7566135446Strhodes fctx_cleanupforwaddrs(fctx); 7567135446Strhodes 7568135446Strhodes n = dns_name_countlabels(&fctx->name); 7569135446Strhodes dns_name_getlabelsequence(&fctx->name, 1, n - 1, &fctx->nsname); 7570135446Strhodes 7571135446Strhodes FCTXTRACE("suspending DS lookup to find parent's NS records"); 7572135446Strhodes 7573135446Strhodes result = dns_resolver_createfetch(fctx->res, &fctx->nsname, 7574135446Strhodes dns_rdatatype_ns, 7575135446Strhodes NULL, NULL, NULL, 0, task, 7576135446Strhodes resume_dslookup, fctx, 7577135446Strhodes &fctx->nsrrset, NULL, 7578135446Strhodes &fctx->nsfetch); 7579135446Strhodes if (result != ISC_R_SUCCESS) 7580193149Sdougb fctx_done(fctx, result, __LINE__); 7581214586Sdougb else { 7582214586Sdougb LOCK(&fctx->res->buckets[fctx->bucketnum].lock); 7583214586Sdougb fctx->references++; 7584214586Sdougb UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); 7585214586Sdougb result = fctx_stopidletimer(fctx); 7586214586Sdougb if (result != ISC_R_SUCCESS) 7587214586Sdougb fctx_done(fctx, result, __LINE__); 7588214586Sdougb } 7589135446Strhodes } else { 7590135446Strhodes /* 7591135446Strhodes * We're done. 7592135446Strhodes */ 7593193149Sdougb fctx_done(fctx, result, __LINE__); 7594135446Strhodes } 7595135446Strhodes} 7596135446Strhodes 7597135446Strhodes 7598135446Strhodes/*** 7599135446Strhodes *** Resolver Methods 7600135446Strhodes ***/ 7601205292Sdougbstatic void 7602205292Sdougbdestroy_badcache(dns_resolver_t *res) { 7603205292Sdougb dns_badcache_t *bad, *next; 7604205292Sdougb unsigned int i; 7605135446Strhodes 7606205292Sdougb if (res->badcache != NULL) { 7607205292Sdougb for (i = 0; i < res->badhash; i++) 7608205292Sdougb for (bad = res->badcache[i]; bad != NULL; 7609205292Sdougb bad = next) { 7610205292Sdougb next = bad->next; 7611205292Sdougb isc_mem_put(res->mctx, bad, sizeof(*bad) + 7612205292Sdougb bad->name.length); 7613205292Sdougb res->badcount--; 7614205292Sdougb } 7615205292Sdougb isc_mem_put(res->mctx, res->badcache, 7616205292Sdougb sizeof(*res->badcache) * res->badhash); 7617205292Sdougb res->badcache = NULL; 7618205292Sdougb res->badhash = 0; 7619205292Sdougb INSIST(res->badcount == 0); 7620205292Sdougb } 7621205292Sdougb} 7622205292Sdougb 7623135446Strhodesstatic void 7624135446Strhodesdestroy(dns_resolver_t *res) { 7625135446Strhodes unsigned int i; 7626135446Strhodes alternate_t *a; 7627135446Strhodes 7628135446Strhodes REQUIRE(res->references == 0); 7629135446Strhodes REQUIRE(!res->priming); 7630135446Strhodes REQUIRE(res->primefetch == NULL); 7631135446Strhodes 7632135446Strhodes RTRACE("destroy"); 7633135446Strhodes 7634135446Strhodes INSIST(res->nfctx == 0); 7635135446Strhodes 7636135446Strhodes DESTROYLOCK(&res->primelock); 7637135446Strhodes DESTROYLOCK(&res->nlock); 7638135446Strhodes DESTROYLOCK(&res->lock); 7639135446Strhodes for (i = 0; i < res->nbuckets; i++) { 7640135446Strhodes INSIST(ISC_LIST_EMPTY(res->buckets[i].fctxs)); 7641135446Strhodes isc_task_shutdown(res->buckets[i].task); 7642135446Strhodes isc_task_detach(&res->buckets[i].task); 7643135446Strhodes DESTROYLOCK(&res->buckets[i].lock); 7644170222Sdougb isc_mem_detach(&res->buckets[i].mctx); 7645135446Strhodes } 7646135446Strhodes isc_mem_put(res->mctx, res->buckets, 7647135446Strhodes res->nbuckets * sizeof(fctxbucket_t)); 7648254897Serwin if (res->dispatches4 != NULL) 7649254897Serwin dns_dispatchset_destroy(&res->dispatches4); 7650254897Serwin if (res->dispatches6 != NULL) 7651254897Serwin dns_dispatchset_destroy(&res->dispatches6); 7652135446Strhodes while ((a = ISC_LIST_HEAD(res->alternates)) != NULL) { 7653135446Strhodes ISC_LIST_UNLINK(res->alternates, a, link); 7654135446Strhodes if (!a->isaddress) 7655135446Strhodes dns_name_free(&a->_u._n.name, res->mctx); 7656135446Strhodes isc_mem_put(res->mctx, a, sizeof(*a)); 7657135446Strhodes } 7658135446Strhodes dns_resolver_reset_algorithms(res); 7659205292Sdougb destroy_badcache(res); 7660135446Strhodes dns_resolver_resetmustbesecure(res); 7661135446Strhodes#if USE_ALGLOCK 7662135446Strhodes isc_rwlock_destroy(&res->alglock); 7663135446Strhodes#endif 7664135446Strhodes#if USE_MBSLOCK 7665135446Strhodes isc_rwlock_destroy(&res->mbslock); 7666135446Strhodes#endif 7667170222Sdougb isc_timer_detach(&res->spillattimer); 7668135446Strhodes res->magic = 0; 7669135446Strhodes isc_mem_put(res->mctx, res, sizeof(*res)); 7670135446Strhodes} 7671135446Strhodes 7672135446Strhodesstatic void 7673135446Strhodessend_shutdown_events(dns_resolver_t *res) { 7674135446Strhodes isc_event_t *event, *next_event; 7675135446Strhodes isc_task_t *etask; 7676135446Strhodes 7677135446Strhodes /* 7678135446Strhodes * Caller must be holding the resolver lock. 7679135446Strhodes */ 7680135446Strhodes 7681135446Strhodes for (event = ISC_LIST_HEAD(res->whenshutdown); 7682135446Strhodes event != NULL; 7683135446Strhodes event = next_event) { 7684135446Strhodes next_event = ISC_LIST_NEXT(event, ev_link); 7685135446Strhodes ISC_LIST_UNLINK(res->whenshutdown, event, ev_link); 7686135446Strhodes etask = event->ev_sender; 7687135446Strhodes event->ev_sender = res; 7688135446Strhodes isc_task_sendanddetach(&etask, &event); 7689135446Strhodes } 7690135446Strhodes} 7691135446Strhodes 7692135446Strhodesstatic void 7693135446Strhodesempty_bucket(dns_resolver_t *res) { 7694135446Strhodes RTRACE("empty_bucket"); 7695135446Strhodes 7696135446Strhodes LOCK(&res->lock); 7697135446Strhodes 7698135446Strhodes INSIST(res->activebuckets > 0); 7699135446Strhodes res->activebuckets--; 7700135446Strhodes if (res->activebuckets == 0) 7701135446Strhodes send_shutdown_events(res); 7702135446Strhodes 7703135446Strhodes UNLOCK(&res->lock); 7704135446Strhodes} 7705135446Strhodes 7706170222Sdougbstatic void 7707170222Sdougbspillattimer_countdown(isc_task_t *task, isc_event_t *event) { 7708170222Sdougb dns_resolver_t *res = event->ev_arg; 7709170222Sdougb isc_result_t result; 7710170222Sdougb unsigned int count; 7711170222Sdougb isc_boolean_t logit = ISC_FALSE; 7712170222Sdougb 7713170222Sdougb REQUIRE(VALID_RESOLVER(res)); 7714170222Sdougb 7715170222Sdougb UNUSED(task); 7716186462Sdougb 7717170222Sdougb LOCK(&res->lock); 7718170222Sdougb INSIST(!res->exiting); 7719170222Sdougb if (res->spillat > res->spillatmin) { 7720170222Sdougb res->spillat--; 7721170222Sdougb logit = ISC_TRUE; 7722170222Sdougb } 7723170222Sdougb if (res->spillat <= res->spillatmin) { 7724170222Sdougb result = isc_timer_reset(res->spillattimer, 7725170222Sdougb isc_timertype_inactive, NULL, 7726170222Sdougb NULL, ISC_TRUE); 7727170222Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 7728170222Sdougb } 7729170222Sdougb count = res->spillat; 7730170222Sdougb UNLOCK(&res->lock); 7731170222Sdougb if (logit) 7732170222Sdougb isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, 7733170222Sdougb DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE, 7734170222Sdougb "clients-per-query decreased to %u", count); 7735170222Sdougb 7736170222Sdougb isc_event_free(&event); 7737170222Sdougb} 7738170222Sdougb 7739135446Strhodesisc_result_t 7740135446Strhodesdns_resolver_create(dns_view_t *view, 7741254897Serwin isc_taskmgr_t *taskmgr, 7742254897Serwin unsigned int ntasks, unsigned int ndisp, 7743135446Strhodes isc_socketmgr_t *socketmgr, 7744186462Sdougb isc_timermgr_t *timermgr, 7745135446Strhodes unsigned int options, 7746135446Strhodes dns_dispatchmgr_t *dispatchmgr, 7747135446Strhodes dns_dispatch_t *dispatchv4, 7748135446Strhodes dns_dispatch_t *dispatchv6, 7749135446Strhodes dns_resolver_t **resp) 7750135446Strhodes{ 7751135446Strhodes dns_resolver_t *res; 7752135446Strhodes isc_result_t result = ISC_R_SUCCESS; 7753135446Strhodes unsigned int i, buckets_created = 0; 7754170222Sdougb isc_task_t *task = NULL; 7755135446Strhodes char name[16]; 7756186462Sdougb unsigned dispattr; 7757135446Strhodes 7758135446Strhodes /* 7759135446Strhodes * Create a resolver. 7760135446Strhodes */ 7761135446Strhodes 7762135446Strhodes REQUIRE(DNS_VIEW_VALID(view)); 7763135446Strhodes REQUIRE(ntasks > 0); 7764254897Serwin REQUIRE(ndisp > 0); 7765135446Strhodes REQUIRE(resp != NULL && *resp == NULL); 7766135446Strhodes REQUIRE(dispatchmgr != NULL); 7767135446Strhodes REQUIRE(dispatchv4 != NULL || dispatchv6 != NULL); 7768135446Strhodes 7769135446Strhodes res = isc_mem_get(view->mctx, sizeof(*res)); 7770135446Strhodes if (res == NULL) 7771135446Strhodes return (ISC_R_NOMEMORY); 7772135446Strhodes RTRACE("create"); 7773135446Strhodes res->mctx = view->mctx; 7774135446Strhodes res->rdclass = view->rdclass; 7775135446Strhodes res->socketmgr = socketmgr; 7776135446Strhodes res->timermgr = timermgr; 7777135446Strhodes res->taskmgr = taskmgr; 7778135446Strhodes res->dispatchmgr = dispatchmgr; 7779135446Strhodes res->view = view; 7780135446Strhodes res->options = options; 7781135446Strhodes res->lame_ttl = 0; 7782135446Strhodes ISC_LIST_INIT(res->alternates); 7783135446Strhodes res->udpsize = RECV_BUFFER_SIZE; 7784135446Strhodes res->algorithms = NULL; 7785205292Sdougb res->badcache = NULL; 7786205292Sdougb res->badcount = 0; 7787205292Sdougb res->badhash = 0; 7788205292Sdougb res->badsweep = 0; 7789135446Strhodes res->mustbesecure = NULL; 7790170222Sdougb res->spillatmin = res->spillat = 10; 7791170222Sdougb res->spillatmax = 100; 7792170222Sdougb res->spillattimer = NULL; 7793170222Sdougb res->zero_no_soa_ttl = ISC_FALSE; 7794224092Sdougb res->query_timeout = DEFAULT_QUERY_TIMEOUT; 7795275672Sdelphij res->maxdepth = DEFAULT_RECURSION_DEPTH; 7796135446Strhodes res->nbuckets = ntasks; 7797135446Strhodes res->activebuckets = ntasks; 7798135446Strhodes res->buckets = isc_mem_get(view->mctx, 7799135446Strhodes ntasks * sizeof(fctxbucket_t)); 7800135446Strhodes if (res->buckets == NULL) { 7801135446Strhodes result = ISC_R_NOMEMORY; 7802135446Strhodes goto cleanup_res; 7803135446Strhodes } 7804135446Strhodes for (i = 0; i < ntasks; i++) { 7805135446Strhodes result = isc_mutex_init(&res->buckets[i].lock); 7806135446Strhodes if (result != ISC_R_SUCCESS) 7807135446Strhodes goto cleanup_buckets; 7808135446Strhodes res->buckets[i].task = NULL; 7809135446Strhodes result = isc_task_create(taskmgr, 0, &res->buckets[i].task); 7810135446Strhodes if (result != ISC_R_SUCCESS) { 7811135446Strhodes DESTROYLOCK(&res->buckets[i].lock); 7812135446Strhodes goto cleanup_buckets; 7813135446Strhodes } 7814170222Sdougb res->buckets[i].mctx = NULL; 7815193149Sdougb snprintf(name, sizeof(name), "res%u", i); 7816193149Sdougb#ifdef ISC_PLATFORM_USETHREADS 7817193149Sdougb /* 7818193149Sdougb * Use a separate memory context for each bucket to reduce 7819193149Sdougb * contention among multiple threads. Do this only when 7820193149Sdougb * enabling threads because it will be require more memory. 7821193149Sdougb */ 7822170222Sdougb result = isc_mem_create(0, 0, &res->buckets[i].mctx); 7823170222Sdougb if (result != ISC_R_SUCCESS) { 7824170222Sdougb isc_task_detach(&res->buckets[i].task); 7825170222Sdougb DESTROYLOCK(&res->buckets[i].lock); 7826170222Sdougb goto cleanup_buckets; 7827170222Sdougb } 7828193149Sdougb isc_mem_setname(res->buckets[i].mctx, name, NULL); 7829193149Sdougb#else 7830193149Sdougb isc_mem_attach(view->mctx, &res->buckets[i].mctx); 7831193149Sdougb#endif 7832135446Strhodes isc_task_setname(res->buckets[i].task, name, res); 7833135446Strhodes ISC_LIST_INIT(res->buckets[i].fctxs); 7834135446Strhodes res->buckets[i].exiting = ISC_FALSE; 7835135446Strhodes buckets_created++; 7836135446Strhodes } 7837135446Strhodes 7838254897Serwin res->dispatches4 = NULL; 7839186462Sdougb if (dispatchv4 != NULL) { 7840254897Serwin dns_dispatchset_create(view->mctx, socketmgr, taskmgr, 7841254897Serwin dispatchv4, &res->dispatches4, ndisp); 7842186462Sdougb dispattr = dns_dispatch_getattributes(dispatchv4); 7843186462Sdougb res->exclusivev4 = 7844186462Sdougb ISC_TF((dispattr & DNS_DISPATCHATTR_EXCLUSIVE) != 0); 7845186462Sdougb } 7846186462Sdougb 7847254897Serwin res->dispatches6 = NULL; 7848186462Sdougb if (dispatchv6 != NULL) { 7849254897Serwin dns_dispatchset_create(view->mctx, socketmgr, taskmgr, 7850254897Serwin dispatchv6, &res->dispatches6, ndisp); 7851186462Sdougb dispattr = dns_dispatch_getattributes(dispatchv6); 7852186462Sdougb res->exclusivev6 = 7853186462Sdougb ISC_TF((dispattr & DNS_DISPATCHATTR_EXCLUSIVE) != 0); 7854186462Sdougb } 7855135446Strhodes 7856135446Strhodes res->references = 1; 7857135446Strhodes res->exiting = ISC_FALSE; 7858135446Strhodes res->frozen = ISC_FALSE; 7859135446Strhodes ISC_LIST_INIT(res->whenshutdown); 7860135446Strhodes res->priming = ISC_FALSE; 7861135446Strhodes res->primefetch = NULL; 7862135446Strhodes res->nfctx = 0; 7863135446Strhodes 7864135446Strhodes result = isc_mutex_init(&res->lock); 7865135446Strhodes if (result != ISC_R_SUCCESS) 7866135446Strhodes goto cleanup_dispatches; 7867135446Strhodes 7868135446Strhodes result = isc_mutex_init(&res->nlock); 7869135446Strhodes if (result != ISC_R_SUCCESS) 7870135446Strhodes goto cleanup_lock; 7871135446Strhodes 7872135446Strhodes result = isc_mutex_init(&res->primelock); 7873135446Strhodes if (result != ISC_R_SUCCESS) 7874135446Strhodes goto cleanup_nlock; 7875135446Strhodes 7876170222Sdougb task = NULL; 7877170222Sdougb result = isc_task_create(taskmgr, 0, &task); 7878170222Sdougb if (result != ISC_R_SUCCESS) 7879193149Sdougb goto cleanup_primelock; 7880170222Sdougb 7881170222Sdougb result = isc_timer_create(timermgr, isc_timertype_inactive, NULL, NULL, 7882170222Sdougb task, spillattimer_countdown, res, 7883170222Sdougb &res->spillattimer); 7884170222Sdougb isc_task_detach(&task); 7885170222Sdougb if (result != ISC_R_SUCCESS) 7886193149Sdougb goto cleanup_primelock; 7887170222Sdougb 7888135446Strhodes#if USE_ALGLOCK 7889135446Strhodes result = isc_rwlock_init(&res->alglock, 0, 0); 7890135446Strhodes if (result != ISC_R_SUCCESS) 7891170222Sdougb goto cleanup_spillattimer; 7892135446Strhodes#endif 7893135446Strhodes#if USE_MBSLOCK 7894135446Strhodes result = isc_rwlock_init(&res->mbslock, 0, 0); 7895135446Strhodes if (result != ISC_R_SUCCESS) 7896135446Strhodes goto cleanup_alglock; 7897135446Strhodes#endif 7898135446Strhodes 7899135446Strhodes res->magic = RES_MAGIC; 7900135446Strhodes 7901135446Strhodes *resp = res; 7902135446Strhodes 7903135446Strhodes return (ISC_R_SUCCESS); 7904135446Strhodes 7905135446Strhodes#if USE_MBSLOCK 7906135446Strhodes cleanup_alglock: 7907135446Strhodes#if USE_ALGLOCK 7908135446Strhodes isc_rwlock_destroy(&res->alglock); 7909135446Strhodes#endif 7910135446Strhodes#endif 7911135446Strhodes#if USE_ALGLOCK || USE_MBSLOCK 7912170222Sdougb cleanup_spillattimer: 7913170222Sdougb isc_timer_detach(&res->spillattimer); 7914170222Sdougb#endif 7915170222Sdougb 7916135446Strhodes cleanup_primelock: 7917135446Strhodes DESTROYLOCK(&res->primelock); 7918135446Strhodes 7919135446Strhodes cleanup_nlock: 7920135446Strhodes DESTROYLOCK(&res->nlock); 7921135446Strhodes 7922135446Strhodes cleanup_lock: 7923135446Strhodes DESTROYLOCK(&res->lock); 7924135446Strhodes 7925135446Strhodes cleanup_dispatches: 7926254897Serwin if (res->dispatches6 != NULL) 7927254897Serwin dns_dispatchset_destroy(&res->dispatches6); 7928254897Serwin if (res->dispatches4 != NULL) 7929254897Serwin dns_dispatchset_destroy(&res->dispatches4); 7930135446Strhodes 7931135446Strhodes cleanup_buckets: 7932135446Strhodes for (i = 0; i < buckets_created; i++) { 7933170222Sdougb isc_mem_detach(&res->buckets[i].mctx); 7934135446Strhodes DESTROYLOCK(&res->buckets[i].lock); 7935135446Strhodes isc_task_shutdown(res->buckets[i].task); 7936135446Strhodes isc_task_detach(&res->buckets[i].task); 7937135446Strhodes } 7938135446Strhodes isc_mem_put(view->mctx, res->buckets, 7939135446Strhodes res->nbuckets * sizeof(fctxbucket_t)); 7940135446Strhodes 7941135446Strhodes cleanup_res: 7942135446Strhodes isc_mem_put(view->mctx, res, sizeof(*res)); 7943135446Strhodes 7944135446Strhodes return (result); 7945135446Strhodes} 7946135446Strhodes 7947224092Sdougb#ifdef BIND9 7948135446Strhodesstatic void 7949135446Strhodesprime_done(isc_task_t *task, isc_event_t *event) { 7950135446Strhodes dns_resolver_t *res; 7951135446Strhodes dns_fetchevent_t *fevent; 7952135446Strhodes dns_fetch_t *fetch; 7953170222Sdougb dns_db_t *db = NULL; 7954135446Strhodes 7955135446Strhodes REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE); 7956135446Strhodes fevent = (dns_fetchevent_t *)event; 7957135446Strhodes res = event->ev_arg; 7958135446Strhodes REQUIRE(VALID_RESOLVER(res)); 7959135446Strhodes 7960135446Strhodes UNUSED(task); 7961135446Strhodes 7962135446Strhodes LOCK(&res->lock); 7963135446Strhodes 7964135446Strhodes INSIST(res->priming); 7965135446Strhodes res->priming = ISC_FALSE; 7966135446Strhodes LOCK(&res->primelock); 7967135446Strhodes fetch = res->primefetch; 7968135446Strhodes res->primefetch = NULL; 7969135446Strhodes UNLOCK(&res->primelock); 7970135446Strhodes 7971135446Strhodes UNLOCK(&res->lock); 7972186462Sdougb 7973170222Sdougb if (fevent->result == ISC_R_SUCCESS && 7974170222Sdougb res->view->cache != NULL && res->view->hints != NULL) { 7975170222Sdougb dns_cache_attachdb(res->view->cache, &db); 7976170222Sdougb dns_root_checkhints(res->view, res->view->hints, db); 7977170222Sdougb dns_db_detach(&db); 7978170222Sdougb } 7979135446Strhodes 7980135446Strhodes if (fevent->node != NULL) 7981135446Strhodes dns_db_detachnode(fevent->db, &fevent->node); 7982135446Strhodes if (fevent->db != NULL) 7983135446Strhodes dns_db_detach(&fevent->db); 7984135446Strhodes if (dns_rdataset_isassociated(fevent->rdataset)) 7985135446Strhodes dns_rdataset_disassociate(fevent->rdataset); 7986135446Strhodes INSIST(fevent->sigrdataset == NULL); 7987135446Strhodes 7988135446Strhodes isc_mem_put(res->mctx, fevent->rdataset, sizeof(*fevent->rdataset)); 7989135446Strhodes 7990135446Strhodes isc_event_free(&event); 7991135446Strhodes dns_resolver_destroyfetch(&fetch); 7992135446Strhodes} 7993135446Strhodes 7994135446Strhodesvoid 7995135446Strhodesdns_resolver_prime(dns_resolver_t *res) { 7996135446Strhodes isc_boolean_t want_priming = ISC_FALSE; 7997135446Strhodes dns_rdataset_t *rdataset; 7998135446Strhodes isc_result_t result; 7999135446Strhodes 8000135446Strhodes REQUIRE(VALID_RESOLVER(res)); 8001135446Strhodes REQUIRE(res->frozen); 8002135446Strhodes 8003135446Strhodes RTRACE("dns_resolver_prime"); 8004135446Strhodes 8005135446Strhodes LOCK(&res->lock); 8006135446Strhodes 8007135446Strhodes if (!res->exiting && !res->priming) { 8008135446Strhodes INSIST(res->primefetch == NULL); 8009135446Strhodes res->priming = ISC_TRUE; 8010135446Strhodes want_priming = ISC_TRUE; 8011135446Strhodes } 8012135446Strhodes 8013135446Strhodes UNLOCK(&res->lock); 8014135446Strhodes 8015135446Strhodes if (want_priming) { 8016135446Strhodes /* 8017135446Strhodes * To avoid any possible recursive locking problems, we 8018135446Strhodes * start the priming fetch like any other fetch, and holding 8019135446Strhodes * no resolver locks. No one else will try to start it 8020135446Strhodes * because we're the ones who set res->priming to true. 8021135446Strhodes * Any other callers of dns_resolver_prime() while we're 8022135446Strhodes * running will see that res->priming is already true and 8023135446Strhodes * do nothing. 8024135446Strhodes */ 8025135446Strhodes RTRACE("priming"); 8026135446Strhodes rdataset = isc_mem_get(res->mctx, sizeof(*rdataset)); 8027135446Strhodes if (rdataset == NULL) { 8028135446Strhodes LOCK(&res->lock); 8029135446Strhodes INSIST(res->priming); 8030135446Strhodes INSIST(res->primefetch == NULL); 8031135446Strhodes res->priming = ISC_FALSE; 8032135446Strhodes UNLOCK(&res->lock); 8033135446Strhodes return; 8034135446Strhodes } 8035135446Strhodes dns_rdataset_init(rdataset); 8036135446Strhodes LOCK(&res->primelock); 8037135446Strhodes result = dns_resolver_createfetch(res, dns_rootname, 8038135446Strhodes dns_rdatatype_ns, 8039135446Strhodes NULL, NULL, NULL, 0, 8040135446Strhodes res->buckets[0].task, 8041135446Strhodes prime_done, 8042135446Strhodes res, rdataset, NULL, 8043135446Strhodes &res->primefetch); 8044135446Strhodes UNLOCK(&res->primelock); 8045135446Strhodes if (result != ISC_R_SUCCESS) { 8046135446Strhodes LOCK(&res->lock); 8047135446Strhodes INSIST(res->priming); 8048135446Strhodes res->priming = ISC_FALSE; 8049135446Strhodes UNLOCK(&res->lock); 8050135446Strhodes } 8051135446Strhodes } 8052135446Strhodes} 8053224092Sdougb#endif /* BIND9 */ 8054135446Strhodes 8055135446Strhodesvoid 8056135446Strhodesdns_resolver_freeze(dns_resolver_t *res) { 8057135446Strhodes /* 8058135446Strhodes * Freeze resolver. 8059135446Strhodes */ 8060135446Strhodes 8061135446Strhodes REQUIRE(VALID_RESOLVER(res)); 8062135446Strhodes 8063135446Strhodes res->frozen = ISC_TRUE; 8064135446Strhodes} 8065135446Strhodes 8066135446Strhodesvoid 8067135446Strhodesdns_resolver_attach(dns_resolver_t *source, dns_resolver_t **targetp) { 8068135446Strhodes REQUIRE(VALID_RESOLVER(source)); 8069135446Strhodes REQUIRE(targetp != NULL && *targetp == NULL); 8070135446Strhodes 8071135446Strhodes RRTRACE(source, "attach"); 8072135446Strhodes LOCK(&source->lock); 8073135446Strhodes REQUIRE(!source->exiting); 8074135446Strhodes 8075135446Strhodes INSIST(source->references > 0); 8076135446Strhodes source->references++; 8077135446Strhodes INSIST(source->references != 0); 8078135446Strhodes UNLOCK(&source->lock); 8079135446Strhodes 8080135446Strhodes *targetp = source; 8081135446Strhodes} 8082135446Strhodes 8083135446Strhodesvoid 8084135446Strhodesdns_resolver_whenshutdown(dns_resolver_t *res, isc_task_t *task, 8085135446Strhodes isc_event_t **eventp) 8086135446Strhodes{ 8087135446Strhodes isc_task_t *clone; 8088135446Strhodes isc_event_t *event; 8089135446Strhodes 8090135446Strhodes REQUIRE(VALID_RESOLVER(res)); 8091135446Strhodes REQUIRE(eventp != NULL); 8092135446Strhodes 8093135446Strhodes event = *eventp; 8094135446Strhodes *eventp = NULL; 8095135446Strhodes 8096135446Strhodes LOCK(&res->lock); 8097135446Strhodes 8098135446Strhodes if (res->exiting && res->activebuckets == 0) { 8099135446Strhodes /* 8100135446Strhodes * We're already shutdown. Send the event. 8101135446Strhodes */ 8102135446Strhodes event->ev_sender = res; 8103135446Strhodes isc_task_send(task, &event); 8104135446Strhodes } else { 8105135446Strhodes clone = NULL; 8106135446Strhodes isc_task_attach(task, &clone); 8107135446Strhodes event->ev_sender = clone; 8108135446Strhodes ISC_LIST_APPEND(res->whenshutdown, event, ev_link); 8109135446Strhodes } 8110135446Strhodes 8111135446Strhodes UNLOCK(&res->lock); 8112135446Strhodes} 8113135446Strhodes 8114135446Strhodesvoid 8115135446Strhodesdns_resolver_shutdown(dns_resolver_t *res) { 8116135446Strhodes unsigned int i; 8117135446Strhodes fetchctx_t *fctx; 8118170222Sdougb isc_result_t result; 8119135446Strhodes 8120135446Strhodes REQUIRE(VALID_RESOLVER(res)); 8121135446Strhodes 8122135446Strhodes RTRACE("shutdown"); 8123135446Strhodes 8124135446Strhodes LOCK(&res->lock); 8125135446Strhodes 8126135446Strhodes if (!res->exiting) { 8127135446Strhodes RTRACE("exiting"); 8128135446Strhodes res->exiting = ISC_TRUE; 8129135446Strhodes 8130135446Strhodes for (i = 0; i < res->nbuckets; i++) { 8131135446Strhodes LOCK(&res->buckets[i].lock); 8132135446Strhodes for (fctx = ISC_LIST_HEAD(res->buckets[i].fctxs); 8133135446Strhodes fctx != NULL; 8134135446Strhodes fctx = ISC_LIST_NEXT(fctx, link)) 8135135446Strhodes fctx_shutdown(fctx); 8136254897Serwin if (res->dispatches4 != NULL && !res->exclusivev4) { 8137254897Serwin dns_dispatchset_cancelall(res->dispatches4, 8138254897Serwin res->buckets[i].task); 8139135446Strhodes } 8140254897Serwin if (res->dispatches6 != NULL && !res->exclusivev6) { 8141254897Serwin dns_dispatchset_cancelall(res->dispatches6, 8142254897Serwin res->buckets[i].task); 8143135446Strhodes } 8144135446Strhodes res->buckets[i].exiting = ISC_TRUE; 8145135446Strhodes if (ISC_LIST_EMPTY(res->buckets[i].fctxs)) { 8146135446Strhodes INSIST(res->activebuckets > 0); 8147135446Strhodes res->activebuckets--; 8148135446Strhodes } 8149135446Strhodes UNLOCK(&res->buckets[i].lock); 8150135446Strhodes } 8151135446Strhodes if (res->activebuckets == 0) 8152135446Strhodes send_shutdown_events(res); 8153170222Sdougb result = isc_timer_reset(res->spillattimer, 8154170222Sdougb isc_timertype_inactive, NULL, 8155170222Sdougb NULL, ISC_TRUE); 8156170222Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 8157135446Strhodes } 8158135446Strhodes 8159135446Strhodes UNLOCK(&res->lock); 8160135446Strhodes} 8161135446Strhodes 8162135446Strhodesvoid 8163135446Strhodesdns_resolver_detach(dns_resolver_t **resp) { 8164135446Strhodes dns_resolver_t *res; 8165135446Strhodes isc_boolean_t need_destroy = ISC_FALSE; 8166135446Strhodes 8167135446Strhodes REQUIRE(resp != NULL); 8168135446Strhodes res = *resp; 8169135446Strhodes REQUIRE(VALID_RESOLVER(res)); 8170135446Strhodes 8171135446Strhodes RTRACE("detach"); 8172135446Strhodes 8173135446Strhodes LOCK(&res->lock); 8174135446Strhodes 8175135446Strhodes INSIST(res->references > 0); 8176135446Strhodes res->references--; 8177135446Strhodes if (res->references == 0) { 8178135446Strhodes INSIST(res->exiting && res->activebuckets == 0); 8179135446Strhodes need_destroy = ISC_TRUE; 8180135446Strhodes } 8181135446Strhodes 8182135446Strhodes UNLOCK(&res->lock); 8183135446Strhodes 8184135446Strhodes if (need_destroy) 8185135446Strhodes destroy(res); 8186135446Strhodes 8187135446Strhodes *resp = NULL; 8188135446Strhodes} 8189135446Strhodes 8190135446Strhodesstatic inline isc_boolean_t 8191135446Strhodesfctx_match(fetchctx_t *fctx, dns_name_t *name, dns_rdatatype_t type, 8192135446Strhodes unsigned int options) 8193135446Strhodes{ 8194218384Sdougb /* 8195218384Sdougb * Don't match fetch contexts that are shutting down. 8196218384Sdougb */ 8197218384Sdougb if (fctx->cloned || fctx->state == fetchstate_done || 8198218384Sdougb ISC_LIST_EMPTY(fctx->events)) 8199218384Sdougb return (ISC_FALSE); 8200218384Sdougb 8201135446Strhodes if (fctx->type != type || fctx->options != options) 8202135446Strhodes return (ISC_FALSE); 8203135446Strhodes return (dns_name_equal(&fctx->name, name)); 8204135446Strhodes} 8205135446Strhodes 8206135446Strhodesstatic inline void 8207135446Strhodeslog_fetch(dns_name_t *name, dns_rdatatype_t type) { 8208135446Strhodes char namebuf[DNS_NAME_FORMATSIZE]; 8209135446Strhodes char typebuf[DNS_RDATATYPE_FORMATSIZE]; 8210135446Strhodes int level = ISC_LOG_DEBUG(1); 8211135446Strhodes 8212135446Strhodes if (! isc_log_wouldlog(dns_lctx, level)) 8213135446Strhodes return; 8214135446Strhodes 8215135446Strhodes dns_name_format(name, namebuf, sizeof(namebuf)); 8216135446Strhodes dns_rdatatype_format(type, typebuf, sizeof(typebuf)); 8217135446Strhodes 8218135446Strhodes isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, 8219135446Strhodes DNS_LOGMODULE_RESOLVER, level, 8220135446Strhodes "createfetch: %s %s", namebuf, typebuf); 8221135446Strhodes} 8222135446Strhodes 8223135446Strhodesisc_result_t 8224135446Strhodesdns_resolver_createfetch(dns_resolver_t *res, dns_name_t *name, 8225135446Strhodes dns_rdatatype_t type, 8226135446Strhodes dns_name_t *domain, dns_rdataset_t *nameservers, 8227135446Strhodes dns_forwarders_t *forwarders, 8228135446Strhodes unsigned int options, isc_task_t *task, 8229135446Strhodes isc_taskaction_t action, void *arg, 8230135446Strhodes dns_rdataset_t *rdataset, 8231135446Strhodes dns_rdataset_t *sigrdataset, 8232135446Strhodes dns_fetch_t **fetchp) 8233135446Strhodes{ 8234275672Sdelphij return (dns_resolver_createfetch3(res, name, type, domain, 8235170222Sdougb nameservers, forwarders, NULL, 0, 8236275672Sdelphij options, 0, task, action, arg, 8237170222Sdougb rdataset, sigrdataset, fetchp)); 8238170222Sdougb} 8239170222Sdougb 8240170222Sdougbisc_result_t 8241170222Sdougbdns_resolver_createfetch2(dns_resolver_t *res, dns_name_t *name, 8242170222Sdougb dns_rdatatype_t type, 8243170222Sdougb dns_name_t *domain, dns_rdataset_t *nameservers, 8244170222Sdougb dns_forwarders_t *forwarders, 8245170222Sdougb isc_sockaddr_t *client, dns_messageid_t id, 8246170222Sdougb unsigned int options, isc_task_t *task, 8247170222Sdougb isc_taskaction_t action, void *arg, 8248170222Sdougb dns_rdataset_t *rdataset, 8249170222Sdougb dns_rdataset_t *sigrdataset, 8250170222Sdougb dns_fetch_t **fetchp) 8251170222Sdougb{ 8252275672Sdelphij return (dns_resolver_createfetch3(res, name, type, domain, 8253275672Sdelphij nameservers, forwarders, client, id, 8254275672Sdelphij options, 0, task, action, arg, 8255275672Sdelphij rdataset, sigrdataset, fetchp)); 8256275672Sdelphij} 8257275672Sdelphij 8258275672Sdelphijisc_result_t 8259275672Sdelphijdns_resolver_createfetch3(dns_resolver_t *res, dns_name_t *name, 8260275672Sdelphij dns_rdatatype_t type, 8261275672Sdelphij dns_name_t *domain, dns_rdataset_t *nameservers, 8262275672Sdelphij dns_forwarders_t *forwarders, 8263275672Sdelphij isc_sockaddr_t *client, dns_messageid_t id, 8264275672Sdelphij unsigned int options, unsigned int depth, 8265275672Sdelphij isc_task_t *task, 8266275672Sdelphij isc_taskaction_t action, void *arg, 8267275672Sdelphij dns_rdataset_t *rdataset, 8268275672Sdelphij dns_rdataset_t *sigrdataset, 8269275672Sdelphij dns_fetch_t **fetchp) 8270275672Sdelphij{ 8271135446Strhodes dns_fetch_t *fetch; 8272135446Strhodes fetchctx_t *fctx = NULL; 8273170222Sdougb isc_result_t result = ISC_R_SUCCESS; 8274135446Strhodes unsigned int bucketnum; 8275135446Strhodes isc_boolean_t new_fctx = ISC_FALSE; 8276135446Strhodes isc_event_t *event; 8277170222Sdougb unsigned int count = 0; 8278170222Sdougb unsigned int spillat; 8279186462Sdougb unsigned int spillatmin; 8280236374Sdougb isc_boolean_t destroy = ISC_FALSE; 8281135446Strhodes 8282135446Strhodes UNUSED(forwarders); 8283135446Strhodes 8284135446Strhodes REQUIRE(VALID_RESOLVER(res)); 8285135446Strhodes REQUIRE(res->frozen); 8286135446Strhodes /* XXXRTH Check for meta type */ 8287135446Strhodes if (domain != NULL) { 8288135446Strhodes REQUIRE(DNS_RDATASET_VALID(nameservers)); 8289135446Strhodes REQUIRE(nameservers->type == dns_rdatatype_ns); 8290135446Strhodes } else 8291135446Strhodes REQUIRE(nameservers == NULL); 8292135446Strhodes REQUIRE(forwarders == NULL); 8293135446Strhodes REQUIRE(!dns_rdataset_isassociated(rdataset)); 8294135446Strhodes REQUIRE(sigrdataset == NULL || 8295135446Strhodes !dns_rdataset_isassociated(sigrdataset)); 8296135446Strhodes REQUIRE(fetchp != NULL && *fetchp == NULL); 8297135446Strhodes 8298135446Strhodes log_fetch(name, type); 8299135446Strhodes 8300135446Strhodes /* 8301135446Strhodes * XXXRTH use a mempool? 8302135446Strhodes */ 8303135446Strhodes fetch = isc_mem_get(res->mctx, sizeof(*fetch)); 8304135446Strhodes if (fetch == NULL) 8305135446Strhodes return (ISC_R_NOMEMORY); 8306135446Strhodes 8307170222Sdougb bucketnum = dns_name_fullhash(name, ISC_FALSE) % res->nbuckets; 8308135446Strhodes 8309170222Sdougb LOCK(&res->lock); 8310170222Sdougb spillat = res->spillat; 8311186462Sdougb spillatmin = res->spillatmin; 8312170222Sdougb UNLOCK(&res->lock); 8313135446Strhodes LOCK(&res->buckets[bucketnum].lock); 8314135446Strhodes 8315135446Strhodes if (res->buckets[bucketnum].exiting) { 8316135446Strhodes result = ISC_R_SHUTTINGDOWN; 8317135446Strhodes goto unlock; 8318135446Strhodes } 8319135446Strhodes 8320135446Strhodes if ((options & DNS_FETCHOPT_UNSHARED) == 0) { 8321135446Strhodes for (fctx = ISC_LIST_HEAD(res->buckets[bucketnum].fctxs); 8322135446Strhodes fctx != NULL; 8323135446Strhodes fctx = ISC_LIST_NEXT(fctx, link)) { 8324135446Strhodes if (fctx_match(fctx, name, type, options)) 8325135446Strhodes break; 8326135446Strhodes } 8327135446Strhodes } 8328186462Sdougb 8329170222Sdougb /* 8330170222Sdougb * Is this a duplicate? 8331170222Sdougb */ 8332170222Sdougb if (fctx != NULL && client != NULL) { 8333170222Sdougb dns_fetchevent_t *fevent; 8334170222Sdougb for (fevent = ISC_LIST_HEAD(fctx->events); 8335170222Sdougb fevent != NULL; 8336170222Sdougb fevent = ISC_LIST_NEXT(fevent, ev_link)) { 8337170222Sdougb if (fevent->client != NULL && fevent->id == id && 8338170222Sdougb isc_sockaddr_equal(fevent->client, client)) { 8339170222Sdougb result = DNS_R_DUPLICATE; 8340170222Sdougb goto unlock; 8341170222Sdougb } 8342170222Sdougb count++; 8343170222Sdougb } 8344170222Sdougb } 8345186462Sdougb if (count >= spillatmin && spillatmin != 0) { 8346186462Sdougb INSIST(fctx != NULL); 8347170222Sdougb if (count >= spillat) 8348170222Sdougb fctx->spilled = ISC_TRUE; 8349170222Sdougb if (fctx->spilled) { 8350170222Sdougb result = DNS_R_DROP; 8351170222Sdougb goto unlock; 8352170222Sdougb } 8353170222Sdougb } 8354135446Strhodes 8355218384Sdougb if (fctx == NULL) { 8356135446Strhodes result = fctx_create(res, name, type, domain, nameservers, 8357275672Sdelphij options, bucketnum, depth, &fctx); 8358135446Strhodes if (result != ISC_R_SUCCESS) 8359135446Strhodes goto unlock; 8360135446Strhodes new_fctx = ISC_TRUE; 8361275672Sdelphij } else if (fctx->depth > depth) 8362275672Sdelphij fctx->depth = depth; 8363135446Strhodes 8364170222Sdougb result = fctx_join(fctx, task, client, id, action, arg, 8365135446Strhodes rdataset, sigrdataset, fetch); 8366135446Strhodes if (new_fctx) { 8367135446Strhodes if (result == ISC_R_SUCCESS) { 8368135446Strhodes /* 8369135446Strhodes * Launch this fctx. 8370135446Strhodes */ 8371135446Strhodes event = &fctx->control_event; 8372135446Strhodes ISC_EVENT_INIT(event, sizeof(*event), 0, NULL, 8373135446Strhodes DNS_EVENT_FETCHCONTROL, 8374135446Strhodes fctx_start, fctx, NULL, 8375135446Strhodes NULL, NULL); 8376135446Strhodes isc_task_send(res->buckets[bucketnum].task, &event); 8377135446Strhodes } else { 8378135446Strhodes /* 8379236374Sdougb * We don't care about the result of fctx_unlink() 8380135446Strhodes * since we know we're not exiting. 8381135446Strhodes */ 8382236374Sdougb (void)fctx_unlink(fctx); 8383236374Sdougb destroy = ISC_TRUE; 8384135446Strhodes } 8385135446Strhodes } 8386135446Strhodes 8387135446Strhodes unlock: 8388135446Strhodes UNLOCK(&res->buckets[bucketnum].lock); 8389135446Strhodes 8390236374Sdougb if (destroy) 8391236374Sdougb fctx_destroy(fctx); 8392236374Sdougb 8393135446Strhodes if (result == ISC_R_SUCCESS) { 8394135446Strhodes FTRACE("created"); 8395135446Strhodes *fetchp = fetch; 8396135446Strhodes } else 8397135446Strhodes isc_mem_put(res->mctx, fetch, sizeof(*fetch)); 8398135446Strhodes 8399135446Strhodes return (result); 8400135446Strhodes} 8401135446Strhodes 8402135446Strhodesvoid 8403135446Strhodesdns_resolver_cancelfetch(dns_fetch_t *fetch) { 8404135446Strhodes fetchctx_t *fctx; 8405135446Strhodes dns_resolver_t *res; 8406135446Strhodes dns_fetchevent_t *event, *next_event; 8407135446Strhodes isc_task_t *etask; 8408135446Strhodes 8409135446Strhodes REQUIRE(DNS_FETCH_VALID(fetch)); 8410135446Strhodes fctx = fetch->private; 8411135446Strhodes REQUIRE(VALID_FCTX(fctx)); 8412135446Strhodes res = fctx->res; 8413135446Strhodes 8414135446Strhodes FTRACE("cancelfetch"); 8415135446Strhodes 8416135446Strhodes LOCK(&res->buckets[fctx->bucketnum].lock); 8417135446Strhodes 8418135446Strhodes /* 8419135446Strhodes * Find the completion event for this fetch (as opposed 8420135446Strhodes * to those for other fetches that have joined the same 8421135446Strhodes * fctx) and send it with result = ISC_R_CANCELED. 8422135446Strhodes */ 8423135446Strhodes event = NULL; 8424135446Strhodes if (fctx->state != fetchstate_done) { 8425135446Strhodes for (event = ISC_LIST_HEAD(fctx->events); 8426135446Strhodes event != NULL; 8427135446Strhodes event = next_event) { 8428135446Strhodes next_event = ISC_LIST_NEXT(event, ev_link); 8429135446Strhodes if (event->fetch == fetch) { 8430135446Strhodes ISC_LIST_UNLINK(fctx->events, event, ev_link); 8431135446Strhodes break; 8432135446Strhodes } 8433135446Strhodes } 8434135446Strhodes } 8435135446Strhodes if (event != NULL) { 8436135446Strhodes etask = event->ev_sender; 8437135446Strhodes event->ev_sender = fctx; 8438135446Strhodes event->result = ISC_R_CANCELED; 8439135446Strhodes isc_task_sendanddetach(&etask, ISC_EVENT_PTR(&event)); 8440135446Strhodes } 8441135446Strhodes /* 8442135446Strhodes * The fctx continues running even if no fetches remain; 8443135446Strhodes * the answer is still cached. 8444135446Strhodes */ 8445135446Strhodes 8446135446Strhodes UNLOCK(&res->buckets[fctx->bucketnum].lock); 8447135446Strhodes} 8448135446Strhodes 8449135446Strhodesvoid 8450135446Strhodesdns_resolver_destroyfetch(dns_fetch_t **fetchp) { 8451135446Strhodes dns_fetch_t *fetch; 8452135446Strhodes dns_resolver_t *res; 8453135446Strhodes dns_fetchevent_t *event, *next_event; 8454135446Strhodes fetchctx_t *fctx; 8455135446Strhodes unsigned int bucketnum; 8456214586Sdougb isc_boolean_t bucket_empty; 8457135446Strhodes 8458135446Strhodes REQUIRE(fetchp != NULL); 8459135446Strhodes fetch = *fetchp; 8460135446Strhodes REQUIRE(DNS_FETCH_VALID(fetch)); 8461135446Strhodes fctx = fetch->private; 8462135446Strhodes REQUIRE(VALID_FCTX(fctx)); 8463135446Strhodes res = fctx->res; 8464135446Strhodes 8465135446Strhodes FTRACE("destroyfetch"); 8466135446Strhodes 8467135446Strhodes bucketnum = fctx->bucketnum; 8468135446Strhodes LOCK(&res->buckets[bucketnum].lock); 8469135446Strhodes 8470135446Strhodes /* 8471135446Strhodes * Sanity check: the caller should have gotten its event before 8472135446Strhodes * trying to destroy the fetch. 8473135446Strhodes */ 8474135446Strhodes event = NULL; 8475135446Strhodes if (fctx->state != fetchstate_done) { 8476135446Strhodes for (event = ISC_LIST_HEAD(fctx->events); 8477135446Strhodes event != NULL; 8478135446Strhodes event = next_event) { 8479135446Strhodes next_event = ISC_LIST_NEXT(event, ev_link); 8480135446Strhodes RUNTIME_CHECK(event->fetch != fetch); 8481135446Strhodes } 8482135446Strhodes } 8483135446Strhodes 8484214586Sdougb bucket_empty = fctx_decreference(fctx); 8485135446Strhodes 8486135446Strhodes UNLOCK(&res->buckets[bucketnum].lock); 8487135446Strhodes 8488135446Strhodes isc_mem_put(res->mctx, fetch, sizeof(*fetch)); 8489135446Strhodes *fetchp = NULL; 8490135446Strhodes 8491135446Strhodes if (bucket_empty) 8492135446Strhodes empty_bucket(res); 8493135446Strhodes} 8494135446Strhodes 8495193149Sdougbvoid 8496193149Sdougbdns_resolver_logfetch(dns_fetch_t *fetch, isc_log_t *lctx, 8497193149Sdougb isc_logcategory_t *category, isc_logmodule_t *module, 8498193149Sdougb int level, isc_boolean_t duplicateok) 8499193149Sdougb{ 8500193149Sdougb fetchctx_t *fctx; 8501193149Sdougb dns_resolver_t *res; 8502193149Sdougb char domainbuf[DNS_NAME_FORMATSIZE]; 8503193149Sdougb 8504193149Sdougb REQUIRE(DNS_FETCH_VALID(fetch)); 8505193149Sdougb fctx = fetch->private; 8506193149Sdougb REQUIRE(VALID_FCTX(fctx)); 8507193149Sdougb res = fctx->res; 8508193149Sdougb 8509193149Sdougb LOCK(&res->buckets[fctx->bucketnum].lock); 8510193149Sdougb 8511193149Sdougb INSIST(fctx->exitline >= 0); 8512193149Sdougb if (!fctx->logged || duplicateok) { 8513193149Sdougb dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf)); 8514193149Sdougb isc_log_write(lctx, category, module, level, 8515193149Sdougb "fetch completed at %s:%d for %s in " 8516193149Sdougb "%" ISC_PRINT_QUADFORMAT "u." 8517193149Sdougb "%06" ISC_PRINT_QUADFORMAT "u: %s/%s " 8518193149Sdougb "[domain:%s,referral:%u,restart:%u,qrysent:%u," 8519193149Sdougb "timeout:%u,lame:%u,neterr:%u,badresp:%u," 8520193149Sdougb "adberr:%u,findfail:%u,valfail:%u]", 8521193149Sdougb __FILE__, fctx->exitline, fctx->info, 8522245163Serwin fctx->duration / US_PER_SEC, 8523245163Serwin fctx->duration % US_PER_SEC, 8524193149Sdougb isc_result_totext(fctx->result), 8525193149Sdougb isc_result_totext(fctx->vresult), domainbuf, 8526193149Sdougb fctx->referrals, fctx->restarts, 8527193149Sdougb fctx->querysent, fctx->timeouts, fctx->lamecount, 8528193149Sdougb fctx->neterr, fctx->badresp, fctx->adberr, 8529193149Sdougb fctx->findfail, fctx->valfail); 8530193149Sdougb fctx->logged = ISC_TRUE; 8531193149Sdougb } 8532193149Sdougb 8533193149Sdougb UNLOCK(&res->buckets[fctx->bucketnum].lock); 8534193149Sdougb} 8535193149Sdougb 8536135446Strhodesdns_dispatchmgr_t * 8537135446Strhodesdns_resolver_dispatchmgr(dns_resolver_t *resolver) { 8538135446Strhodes REQUIRE(VALID_RESOLVER(resolver)); 8539135446Strhodes return (resolver->dispatchmgr); 8540135446Strhodes} 8541135446Strhodes 8542135446Strhodesdns_dispatch_t * 8543135446Strhodesdns_resolver_dispatchv4(dns_resolver_t *resolver) { 8544135446Strhodes REQUIRE(VALID_RESOLVER(resolver)); 8545254897Serwin return (dns_dispatchset_get(resolver->dispatches4)); 8546135446Strhodes} 8547135446Strhodes 8548135446Strhodesdns_dispatch_t * 8549135446Strhodesdns_resolver_dispatchv6(dns_resolver_t *resolver) { 8550135446Strhodes REQUIRE(VALID_RESOLVER(resolver)); 8551254897Serwin return (dns_dispatchset_get(resolver->dispatches6)); 8552135446Strhodes} 8553135446Strhodes 8554135446Strhodesisc_socketmgr_t * 8555135446Strhodesdns_resolver_socketmgr(dns_resolver_t *resolver) { 8556135446Strhodes REQUIRE(VALID_RESOLVER(resolver)); 8557135446Strhodes return (resolver->socketmgr); 8558135446Strhodes} 8559135446Strhodes 8560135446Strhodesisc_taskmgr_t * 8561135446Strhodesdns_resolver_taskmgr(dns_resolver_t *resolver) { 8562135446Strhodes REQUIRE(VALID_RESOLVER(resolver)); 8563135446Strhodes return (resolver->taskmgr); 8564135446Strhodes} 8565135446Strhodes 8566135446Strhodesisc_uint32_t 8567135446Strhodesdns_resolver_getlamettl(dns_resolver_t *resolver) { 8568135446Strhodes REQUIRE(VALID_RESOLVER(resolver)); 8569135446Strhodes return (resolver->lame_ttl); 8570135446Strhodes} 8571135446Strhodes 8572135446Strhodesvoid 8573135446Strhodesdns_resolver_setlamettl(dns_resolver_t *resolver, isc_uint32_t lame_ttl) { 8574135446Strhodes REQUIRE(VALID_RESOLVER(resolver)); 8575135446Strhodes resolver->lame_ttl = lame_ttl; 8576135446Strhodes} 8577135446Strhodes 8578135446Strhodesunsigned int 8579135446Strhodesdns_resolver_nrunning(dns_resolver_t *resolver) { 8580135446Strhodes unsigned int n; 8581135446Strhodes LOCK(&resolver->nlock); 8582135446Strhodes n = resolver->nfctx; 8583135446Strhodes UNLOCK(&resolver->nlock); 8584135446Strhodes return (n); 8585135446Strhodes} 8586135446Strhodes 8587135446Strhodesisc_result_t 8588135446Strhodesdns_resolver_addalternate(dns_resolver_t *resolver, isc_sockaddr_t *alt, 8589135446Strhodes dns_name_t *name, in_port_t port) { 8590135446Strhodes alternate_t *a; 8591135446Strhodes isc_result_t result; 8592135446Strhodes 8593135446Strhodes REQUIRE(VALID_RESOLVER(resolver)); 8594135446Strhodes REQUIRE(!resolver->frozen); 8595135446Strhodes REQUIRE((alt == NULL) ^ (name == NULL)); 8596135446Strhodes 8597135446Strhodes a = isc_mem_get(resolver->mctx, sizeof(*a)); 8598135446Strhodes if (a == NULL) 8599135446Strhodes return (ISC_R_NOMEMORY); 8600135446Strhodes if (alt != NULL) { 8601135446Strhodes a->isaddress = ISC_TRUE; 8602135446Strhodes a->_u.addr = *alt; 8603135446Strhodes } else { 8604135446Strhodes a->isaddress = ISC_FALSE; 8605135446Strhodes a->_u._n.port = port; 8606135446Strhodes dns_name_init(&a->_u._n.name, NULL); 8607135446Strhodes result = dns_name_dup(name, resolver->mctx, &a->_u._n.name); 8608135446Strhodes if (result != ISC_R_SUCCESS) { 8609135446Strhodes isc_mem_put(resolver->mctx, a, sizeof(*a)); 8610135446Strhodes return (result); 8611135446Strhodes } 8612135446Strhodes } 8613135446Strhodes ISC_LINK_INIT(a, link); 8614135446Strhodes ISC_LIST_APPEND(resolver->alternates, a, link); 8615135446Strhodes 8616135446Strhodes return (ISC_R_SUCCESS); 8617135446Strhodes} 8618135446Strhodes 8619135446Strhodesvoid 8620135446Strhodesdns_resolver_setudpsize(dns_resolver_t *resolver, isc_uint16_t udpsize) { 8621135446Strhodes REQUIRE(VALID_RESOLVER(resolver)); 8622135446Strhodes resolver->udpsize = udpsize; 8623135446Strhodes} 8624135446Strhodes 8625135446Strhodesisc_uint16_t 8626135446Strhodesdns_resolver_getudpsize(dns_resolver_t *resolver) { 8627135446Strhodes REQUIRE(VALID_RESOLVER(resolver)); 8628135446Strhodes return (resolver->udpsize); 8629135446Strhodes} 8630135446Strhodes 8631205292Sdougbvoid 8632205292Sdougbdns_resolver_flushbadcache(dns_resolver_t *resolver, dns_name_t *name) { 8633205292Sdougb unsigned int i; 8634205292Sdougb dns_badcache_t *bad, *prev, *next; 8635205292Sdougb 8636205292Sdougb REQUIRE(VALID_RESOLVER(resolver)); 8637205292Sdougb 8638205292Sdougb LOCK(&resolver->lock); 8639205292Sdougb if (resolver->badcache == NULL) 8640205292Sdougb goto unlock; 8641205292Sdougb 8642205292Sdougb if (name != NULL) { 8643205292Sdougb isc_time_t now; 8644205292Sdougb isc_result_t result; 8645205292Sdougb result = isc_time_now(&now); 8646205292Sdougb if (result != ISC_R_SUCCESS) 8647205292Sdougb isc_time_settoepoch(&now); 8648205292Sdougb i = dns_name_hash(name, ISC_FALSE) % resolver->badhash; 8649205292Sdougb prev = NULL; 8650205292Sdougb for (bad = resolver->badcache[i]; bad != NULL; bad = next) { 8651205292Sdougb int n; 8652205292Sdougb next = bad->next; 8653205292Sdougb n = isc_time_compare(&bad->expire, &now); 8654205292Sdougb if (n < 0 || dns_name_equal(name, &bad->name)) { 8655205292Sdougb if (prev == NULL) 8656205292Sdougb resolver->badcache[i] = bad->next; 8657205292Sdougb else 8658205292Sdougb prev->next = bad->next; 8659205292Sdougb isc_mem_put(resolver->mctx, bad, sizeof(*bad) + 8660205292Sdougb bad->name.length); 8661205292Sdougb resolver->badcount--; 8662205292Sdougb } else 8663205292Sdougb prev = bad; 8664205292Sdougb } 8665205292Sdougb } else 8666205292Sdougb destroy_badcache(resolver); 8667205292Sdougb 8668205292Sdougb unlock: 8669205292Sdougb UNLOCK(&resolver->lock); 8670205292Sdougb 8671205292Sdougb} 8672205292Sdougb 8673135446Strhodesstatic void 8674205292Sdougbresizehash(dns_resolver_t *resolver, isc_time_t *now, isc_boolean_t grow) { 8675205292Sdougb unsigned int newsize; 8676205292Sdougb dns_badcache_t **new, *bad, *next; 8677205292Sdougb unsigned int i; 8678205292Sdougb 8679205292Sdougb if (grow) 8680205292Sdougb newsize = resolver->badhash * 2 + 1; 8681205292Sdougb else 8682205292Sdougb newsize = (resolver->badhash - 1) / 2; 8683205292Sdougb 8684205292Sdougb new = isc_mem_get(resolver->mctx, 8685205292Sdougb sizeof(*resolver->badcache) * newsize); 8686205292Sdougb if (new == NULL) 8687205292Sdougb return; 8688205292Sdougb memset(new, 0, sizeof(*resolver->badcache) * newsize); 8689205292Sdougb for (i = 0; i < resolver->badhash; i++) { 8690205292Sdougb for (bad = resolver->badcache[i]; bad != NULL; bad = next) { 8691205292Sdougb next = bad->next; 8692205292Sdougb if (isc_time_compare(&bad->expire, now) < 0) { 8693205292Sdougb isc_mem_put(resolver->mctx, bad, sizeof(*bad) + 8694205292Sdougb bad->name.length); 8695205292Sdougb resolver->badcount--; 8696205292Sdougb } else { 8697205292Sdougb bad->next = new[bad->hashval % newsize]; 8698205292Sdougb new[bad->hashval % newsize] = bad; 8699205292Sdougb } 8700205292Sdougb } 8701205292Sdougb } 8702205292Sdougb isc_mem_put(resolver->mctx, resolver->badcache, 8703205292Sdougb sizeof(*resolver->badcache) * resolver->badhash); 8704205292Sdougb resolver->badhash = newsize; 8705205292Sdougb resolver->badcache = new; 8706205292Sdougb} 8707205292Sdougb 8708205292Sdougbvoid 8709205292Sdougbdns_resolver_addbadcache(dns_resolver_t *resolver, dns_name_t *name, 8710205292Sdougb dns_rdatatype_t type, isc_time_t *expire) 8711205292Sdougb{ 8712205292Sdougb isc_time_t now; 8713205292Sdougb isc_result_t result = ISC_R_SUCCESS; 8714205292Sdougb unsigned int i, hashval; 8715205292Sdougb dns_badcache_t *bad, *prev, *next; 8716205292Sdougb 8717205292Sdougb REQUIRE(VALID_RESOLVER(resolver)); 8718205292Sdougb 8719205292Sdougb LOCK(&resolver->lock); 8720205292Sdougb if (resolver->badcache == NULL) { 8721205292Sdougb resolver->badcache = isc_mem_get(resolver->mctx, 8722205292Sdougb sizeof(*resolver->badcache) * 8723205292Sdougb DNS_BADCACHE_SIZE); 8724225361Sdougb if (resolver->badcache == NULL) 8725205292Sdougb goto cleanup; 8726205292Sdougb resolver->badhash = DNS_BADCACHE_SIZE; 8727205292Sdougb memset(resolver->badcache, 0, sizeof(*resolver->badcache) * 8728205292Sdougb resolver->badhash); 8729205292Sdougb } 8730205292Sdougb 8731205292Sdougb result = isc_time_now(&now); 8732205292Sdougb if (result != ISC_R_SUCCESS) 8733205292Sdougb isc_time_settoepoch(&now); 8734205292Sdougb hashval = dns_name_hash(name, ISC_FALSE); 8735205292Sdougb i = hashval % resolver->badhash; 8736205292Sdougb prev = NULL; 8737205292Sdougb for (bad = resolver->badcache[i]; bad != NULL; bad = next) { 8738205292Sdougb next = bad->next; 8739205292Sdougb if (bad->type == type && dns_name_equal(name, &bad->name)) 8740205292Sdougb break; 8741205292Sdougb if (isc_time_compare(&bad->expire, &now) < 0) { 8742205292Sdougb if (prev == NULL) 8743205292Sdougb resolver->badcache[i] = bad->next; 8744205292Sdougb else 8745205292Sdougb prev->next = bad->next; 8746205292Sdougb isc_mem_put(resolver->mctx, bad, sizeof(*bad) + 8747205292Sdougb bad->name.length); 8748205292Sdougb resolver->badcount--; 8749205292Sdougb } else 8750205292Sdougb prev = bad; 8751205292Sdougb } 8752205292Sdougb if (bad == NULL) { 8753205292Sdougb isc_buffer_t buffer; 8754205292Sdougb bad = isc_mem_get(resolver->mctx, sizeof(*bad) + name->length); 8755225361Sdougb if (bad == NULL) 8756205292Sdougb goto cleanup; 8757205292Sdougb bad->type = type; 8758205292Sdougb bad->hashval = hashval; 8759238756Sdougb bad->expire = *expire; 8760205292Sdougb isc_buffer_init(&buffer, bad + 1, name->length); 8761205292Sdougb dns_name_init(&bad->name, NULL); 8762205292Sdougb dns_name_copy(name, &bad->name, &buffer); 8763205292Sdougb bad->next = resolver->badcache[i]; 8764205292Sdougb resolver->badcache[i] = bad; 8765205292Sdougb resolver->badcount++; 8766205292Sdougb if (resolver->badcount > resolver->badhash * 8) 8767205292Sdougb resizehash(resolver, &now, ISC_TRUE); 8768205292Sdougb if (resolver->badcount < resolver->badhash * 2 && 8769205292Sdougb resolver->badhash > DNS_BADCACHE_SIZE) 8770205292Sdougb resizehash(resolver, &now, ISC_FALSE); 8771238756Sdougb } else 8772238756Sdougb bad->expire = *expire; 8773205292Sdougb cleanup: 8774205292Sdougb UNLOCK(&resolver->lock); 8775205292Sdougb} 8776205292Sdougb 8777205292Sdougbisc_boolean_t 8778205292Sdougbdns_resolver_getbadcache(dns_resolver_t *resolver, dns_name_t *name, 8779205292Sdougb dns_rdatatype_t type, isc_time_t *now) 8780205292Sdougb{ 8781205292Sdougb dns_badcache_t *bad, *prev, *next; 8782205292Sdougb isc_boolean_t answer = ISC_FALSE; 8783205292Sdougb unsigned int i; 8784205292Sdougb 8785205292Sdougb REQUIRE(VALID_RESOLVER(resolver)); 8786205292Sdougb 8787205292Sdougb LOCK(&resolver->lock); 8788205292Sdougb if (resolver->badcache == NULL) 8789205292Sdougb goto unlock; 8790205292Sdougb 8791205292Sdougb i = dns_name_hash(name, ISC_FALSE) % resolver->badhash; 8792205292Sdougb prev = NULL; 8793205292Sdougb for (bad = resolver->badcache[i]; bad != NULL; bad = next) { 8794205292Sdougb next = bad->next; 8795205292Sdougb /* 8796205292Sdougb * Search the hash list. Clean out expired records as we go. 8797205292Sdougb */ 8798205292Sdougb if (isc_time_compare(&bad->expire, now) < 0) { 8799205292Sdougb if (prev != NULL) 8800205292Sdougb prev->next = bad->next; 8801205292Sdougb else 8802205292Sdougb resolver->badcache[i] = bad->next; 8803205292Sdougb isc_mem_put(resolver->mctx, bad, sizeof(*bad) + 8804205292Sdougb bad->name.length); 8805205292Sdougb resolver->badcount--; 8806205292Sdougb continue; 8807205292Sdougb } 8808205292Sdougb if (bad->type == type && dns_name_equal(name, &bad->name)) { 8809205292Sdougb answer = ISC_TRUE; 8810205292Sdougb break; 8811205292Sdougb } 8812205292Sdougb prev = bad; 8813205292Sdougb } 8814205292Sdougb 8815205292Sdougb /* 8816205292Sdougb * Slow sweep to clean out stale records. 8817205292Sdougb */ 8818205292Sdougb i = resolver->badsweep++ % resolver->badhash; 8819205292Sdougb bad = resolver->badcache[i]; 8820205292Sdougb if (bad != NULL && isc_time_compare(&bad->expire, now) < 0) { 8821205292Sdougb resolver->badcache[i] = bad->next; 8822205292Sdougb isc_mem_put(resolver->mctx, bad, sizeof(*bad) + 8823205292Sdougb bad->name.length); 8824205292Sdougb resolver->badcount--; 8825205292Sdougb } 8826205292Sdougb 8827205292Sdougb unlock: 8828205292Sdougb UNLOCK(&resolver->lock); 8829205292Sdougb return (answer); 8830205292Sdougb} 8831205292Sdougb 8832205292Sdougbvoid 8833205292Sdougbdns_resolver_printbadcache(dns_resolver_t *resolver, FILE *fp) { 8834205292Sdougb char namebuf[DNS_NAME_FORMATSIZE]; 8835205292Sdougb char typebuf[DNS_RDATATYPE_FORMATSIZE]; 8836205292Sdougb dns_badcache_t *bad, *next, *prev; 8837205292Sdougb isc_time_t now; 8838205292Sdougb unsigned int i; 8839205292Sdougb isc_uint64_t t; 8840205292Sdougb 8841205292Sdougb LOCK(&resolver->lock); 8842205292Sdougb fprintf(fp, ";\n; Bad cache\n;\n"); 8843205292Sdougb 8844205292Sdougb if (resolver->badcache == NULL) 8845205292Sdougb goto unlock; 8846205292Sdougb 8847205292Sdougb TIME_NOW(&now); 8848205292Sdougb for (i = 0; i < resolver->badhash; i++) { 8849205292Sdougb prev = NULL; 8850205292Sdougb for (bad = resolver->badcache[i]; bad != NULL; bad = next) { 8851205292Sdougb next = bad->next; 8852205292Sdougb if (isc_time_compare(&bad->expire, &now) < 0) { 8853205292Sdougb if (prev != NULL) 8854205292Sdougb prev->next = bad->next; 8855205292Sdougb else 8856205292Sdougb resolver->badcache[i] = bad->next; 8857205292Sdougb isc_mem_put(resolver->mctx, bad, sizeof(*bad) + 8858205292Sdougb bad->name.length); 8859205292Sdougb resolver->badcount--; 8860205292Sdougb continue; 8861205292Sdougb } 8862205292Sdougb prev = bad; 8863205292Sdougb dns_name_format(&bad->name, namebuf, sizeof(namebuf)); 8864205292Sdougb dns_rdatatype_format(bad->type, typebuf, 8865205292Sdougb sizeof(typebuf)); 8866205292Sdougb t = isc_time_microdiff(&bad->expire, &now); 8867205292Sdougb t /= 1000; 8868205292Sdougb fprintf(fp, "; %s/%s [ttl " 8869205292Sdougb "%" ISC_PLATFORM_QUADFORMAT "u]\n", 8870205292Sdougb namebuf, typebuf, t); 8871205292Sdougb } 8872205292Sdougb } 8873205292Sdougb 8874205292Sdougb unlock: 8875205292Sdougb UNLOCK(&resolver->lock); 8876205292Sdougb} 8877205292Sdougb 8878205292Sdougbstatic void 8879135446Strhodesfree_algorithm(void *node, void *arg) { 8880135446Strhodes unsigned char *algorithms = node; 8881135446Strhodes isc_mem_t *mctx = arg; 8882135446Strhodes 8883135446Strhodes isc_mem_put(mctx, algorithms, *algorithms); 8884135446Strhodes} 8885186462Sdougb 8886135446Strhodesvoid 8887135446Strhodesdns_resolver_reset_algorithms(dns_resolver_t *resolver) { 8888135446Strhodes 8889135446Strhodes REQUIRE(VALID_RESOLVER(resolver)); 8890135446Strhodes 8891135446Strhodes#if USE_ALGLOCK 8892135446Strhodes RWLOCK(&resolver->alglock, isc_rwlocktype_write); 8893135446Strhodes#endif 8894135446Strhodes if (resolver->algorithms != NULL) 8895135446Strhodes dns_rbt_destroy(&resolver->algorithms); 8896135446Strhodes#if USE_ALGLOCK 8897135446Strhodes RWUNLOCK(&resolver->alglock, isc_rwlocktype_write); 8898135446Strhodes#endif 8899135446Strhodes} 8900135446Strhodes 8901135446Strhodesisc_result_t 8902135446Strhodesdns_resolver_disable_algorithm(dns_resolver_t *resolver, dns_name_t *name, 8903135446Strhodes unsigned int alg) 8904135446Strhodes{ 8905135446Strhodes unsigned int len, mask; 8906135446Strhodes unsigned char *new; 8907135446Strhodes unsigned char *algorithms; 8908135446Strhodes isc_result_t result; 8909135446Strhodes dns_rbtnode_t *node = NULL; 8910135446Strhodes 8911135446Strhodes REQUIRE(VALID_RESOLVER(resolver)); 8912135446Strhodes if (alg > 255) 8913135446Strhodes return (ISC_R_RANGE); 8914135446Strhodes 8915135446Strhodes#if USE_ALGLOCK 8916135446Strhodes RWLOCK(&resolver->alglock, isc_rwlocktype_write); 8917135446Strhodes#endif 8918135446Strhodes if (resolver->algorithms == NULL) { 8919135446Strhodes result = dns_rbt_create(resolver->mctx, free_algorithm, 8920135446Strhodes resolver->mctx, &resolver->algorithms); 8921135446Strhodes if (result != ISC_R_SUCCESS) 8922135446Strhodes goto cleanup; 8923135446Strhodes } 8924135446Strhodes 8925135446Strhodes len = alg/8 + 2; 8926135446Strhodes mask = 1 << (alg%8); 8927135446Strhodes 8928135446Strhodes result = dns_rbt_addnode(resolver->algorithms, name, &node); 8929186462Sdougb 8930135446Strhodes if (result == ISC_R_SUCCESS || result == ISC_R_EXISTS) { 8931135446Strhodes algorithms = node->data; 8932135446Strhodes if (algorithms == NULL || len > *algorithms) { 8933135446Strhodes new = isc_mem_get(resolver->mctx, len); 8934135446Strhodes if (new == NULL) { 8935135446Strhodes result = ISC_R_NOMEMORY; 8936135446Strhodes goto cleanup; 8937135446Strhodes } 8938135446Strhodes memset(new, 0, len); 8939135446Strhodes if (algorithms != NULL) 8940262706Serwin memmove(new, algorithms, *algorithms); 8941135446Strhodes new[len-1] |= mask; 8942135446Strhodes *new = len; 8943135446Strhodes node->data = new; 8944135446Strhodes if (algorithms != NULL) 8945186462Sdougb isc_mem_put(resolver->mctx, algorithms, 8946135446Strhodes *algorithms); 8947135446Strhodes } else 8948135446Strhodes algorithms[len-1] |= mask; 8949135446Strhodes } 8950135446Strhodes result = ISC_R_SUCCESS; 8951135446Strhodes cleanup: 8952135446Strhodes#if USE_ALGLOCK 8953135446Strhodes RWUNLOCK(&resolver->alglock, isc_rwlocktype_write); 8954135446Strhodes#endif 8955135446Strhodes return (result); 8956135446Strhodes} 8957135446Strhodes 8958135446Strhodesisc_boolean_t 8959135446Strhodesdns_resolver_algorithm_supported(dns_resolver_t *resolver, dns_name_t *name, 8960135446Strhodes unsigned int alg) 8961135446Strhodes{ 8962135446Strhodes unsigned int len, mask; 8963135446Strhodes unsigned char *algorithms; 8964135446Strhodes void *data = NULL; 8965135446Strhodes isc_result_t result; 8966135446Strhodes isc_boolean_t found = ISC_FALSE; 8967135446Strhodes 8968135446Strhodes REQUIRE(VALID_RESOLVER(resolver)); 8969135446Strhodes 8970287410Sdelphij /* 8971287410Sdelphij * DH is unsupported for DNSKEYs, see RFC 4034 sec. A.1. 8972287410Sdelphij */ 8973287410Sdelphij if ((alg == DST_ALG_DH) || (alg == DST_ALG_INDIRECT)) 8974287410Sdelphij return (ISC_FALSE); 8975287410Sdelphij 8976135446Strhodes#if USE_ALGLOCK 8977135446Strhodes RWLOCK(&resolver->alglock, isc_rwlocktype_read); 8978135446Strhodes#endif 8979135446Strhodes if (resolver->algorithms == NULL) 8980135446Strhodes goto unlock; 8981135446Strhodes result = dns_rbt_findname(resolver->algorithms, name, 0, NULL, &data); 8982135446Strhodes if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) { 8983135446Strhodes len = alg/8 + 2; 8984135446Strhodes mask = 1 << (alg%8); 8985135446Strhodes algorithms = data; 8986135446Strhodes if (len <= *algorithms && (algorithms[len-1] & mask) != 0) 8987135446Strhodes found = ISC_TRUE; 8988135446Strhodes } 8989135446Strhodes unlock: 8990135446Strhodes#if USE_ALGLOCK 8991135446Strhodes RWUNLOCK(&resolver->alglock, isc_rwlocktype_read); 8992135446Strhodes#endif 8993135446Strhodes if (found) 8994135446Strhodes return (ISC_FALSE); 8995287410Sdelphij 8996135446Strhodes return (dst_algorithm_supported(alg)); 8997135446Strhodes} 8998135446Strhodes 8999170222Sdougbisc_boolean_t 9000170222Sdougbdns_resolver_digest_supported(dns_resolver_t *resolver, unsigned int digest) { 9001170222Sdougb 9002170222Sdougb UNUSED(resolver); 9003170222Sdougb return (dns_ds_digest_supported(digest)); 9004170222Sdougb} 9005170222Sdougb 9006135446Strhodesvoid 9007135446Strhodesdns_resolver_resetmustbesecure(dns_resolver_t *resolver) { 9008135446Strhodes 9009135446Strhodes REQUIRE(VALID_RESOLVER(resolver)); 9010135446Strhodes 9011135446Strhodes#if USE_MBSLOCK 9012135446Strhodes RWLOCK(&resolver->mbslock, isc_rwlocktype_write); 9013135446Strhodes#endif 9014135446Strhodes if (resolver->mustbesecure != NULL) 9015135446Strhodes dns_rbt_destroy(&resolver->mustbesecure); 9016135446Strhodes#if USE_MBSLOCK 9017135446Strhodes RWUNLOCK(&resolver->mbslock, isc_rwlocktype_write); 9018135446Strhodes#endif 9019135446Strhodes} 9020186462Sdougb 9021135446Strhodesstatic isc_boolean_t yes = ISC_TRUE, no = ISC_FALSE; 9022135446Strhodes 9023135446Strhodesisc_result_t 9024135446Strhodesdns_resolver_setmustbesecure(dns_resolver_t *resolver, dns_name_t *name, 9025174187Sdougb isc_boolean_t value) 9026135446Strhodes{ 9027135446Strhodes isc_result_t result; 9028135446Strhodes 9029135446Strhodes REQUIRE(VALID_RESOLVER(resolver)); 9030135446Strhodes 9031135446Strhodes#if USE_MBSLOCK 9032135446Strhodes RWLOCK(&resolver->mbslock, isc_rwlocktype_write); 9033135446Strhodes#endif 9034135446Strhodes if (resolver->mustbesecure == NULL) { 9035135446Strhodes result = dns_rbt_create(resolver->mctx, NULL, NULL, 9036135446Strhodes &resolver->mustbesecure); 9037135446Strhodes if (result != ISC_R_SUCCESS) 9038135446Strhodes goto cleanup; 9039135446Strhodes } 9040186462Sdougb result = dns_rbt_addname(resolver->mustbesecure, name, 9041135446Strhodes value ? &yes : &no); 9042135446Strhodes cleanup: 9043135446Strhodes#if USE_MBSLOCK 9044135446Strhodes RWUNLOCK(&resolver->mbslock, isc_rwlocktype_write); 9045135446Strhodes#endif 9046135446Strhodes return (result); 9047135446Strhodes} 9048135446Strhodes 9049135446Strhodesisc_boolean_t 9050135446Strhodesdns_resolver_getmustbesecure(dns_resolver_t *resolver, dns_name_t *name) { 9051135446Strhodes void *data = NULL; 9052135446Strhodes isc_boolean_t value = ISC_FALSE; 9053135446Strhodes isc_result_t result; 9054135446Strhodes 9055135446Strhodes REQUIRE(VALID_RESOLVER(resolver)); 9056135446Strhodes 9057135446Strhodes#if USE_MBSLOCK 9058135446Strhodes RWLOCK(&resolver->mbslock, isc_rwlocktype_read); 9059135446Strhodes#endif 9060135446Strhodes if (resolver->mustbesecure == NULL) 9061135446Strhodes goto unlock; 9062135446Strhodes result = dns_rbt_findname(resolver->mustbesecure, name, 0, NULL, &data); 9063135446Strhodes if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) 9064135446Strhodes value = *(isc_boolean_t*)data; 9065135446Strhodes unlock: 9066135446Strhodes#if USE_MBSLOCK 9067135446Strhodes RWUNLOCK(&resolver->mbslock, isc_rwlocktype_read); 9068135446Strhodes#endif 9069135446Strhodes return (value); 9070135446Strhodes} 9071170222Sdougb 9072170222Sdougbvoid 9073170222Sdougbdns_resolver_getclientsperquery(dns_resolver_t *resolver, isc_uint32_t *cur, 9074170222Sdougb isc_uint32_t *min, isc_uint32_t *max) 9075170222Sdougb{ 9076170222Sdougb REQUIRE(VALID_RESOLVER(resolver)); 9077170222Sdougb 9078170222Sdougb LOCK(&resolver->lock); 9079170222Sdougb if (cur != NULL) 9080170222Sdougb *cur = resolver->spillat; 9081170222Sdougb if (min != NULL) 9082170222Sdougb *min = resolver->spillatmin; 9083170222Sdougb if (max != NULL) 9084170222Sdougb *max = resolver->spillatmax; 9085170222Sdougb UNLOCK(&resolver->lock); 9086170222Sdougb} 9087170222Sdougb 9088170222Sdougbvoid 9089170222Sdougbdns_resolver_setclientsperquery(dns_resolver_t *resolver, isc_uint32_t min, 9090170222Sdougb isc_uint32_t max) 9091170222Sdougb{ 9092170222Sdougb REQUIRE(VALID_RESOLVER(resolver)); 9093170222Sdougb 9094170222Sdougb LOCK(&resolver->lock); 9095170222Sdougb resolver->spillatmin = resolver->spillat = min; 9096170222Sdougb resolver->spillatmax = max; 9097170222Sdougb UNLOCK(&resolver->lock); 9098170222Sdougb} 9099170222Sdougb 9100170222Sdougbisc_boolean_t 9101170222Sdougbdns_resolver_getzeronosoattl(dns_resolver_t *resolver) { 9102170222Sdougb REQUIRE(VALID_RESOLVER(resolver)); 9103170222Sdougb 9104170222Sdougb return (resolver->zero_no_soa_ttl); 9105170222Sdougb} 9106170222Sdougb 9107170222Sdougbvoid 9108170222Sdougbdns_resolver_setzeronosoattl(dns_resolver_t *resolver, isc_boolean_t state) { 9109170222Sdougb REQUIRE(VALID_RESOLVER(resolver)); 9110170222Sdougb 9111170222Sdougb resolver->zero_no_soa_ttl = state; 9112170222Sdougb} 9113193149Sdougb 9114193149Sdougbunsigned int 9115193149Sdougbdns_resolver_getoptions(dns_resolver_t *resolver) { 9116193149Sdougb REQUIRE(VALID_RESOLVER(resolver)); 9117193149Sdougb 9118193149Sdougb return (resolver->options); 9119193149Sdougb} 9120224092Sdougb 9121224092Sdougbunsigned int 9122224092Sdougbdns_resolver_gettimeout(dns_resolver_t *resolver) { 9123224092Sdougb REQUIRE(VALID_RESOLVER(resolver)); 9124224092Sdougb 9125224092Sdougb return (resolver->query_timeout); 9126224092Sdougb} 9127224092Sdougb 9128224092Sdougbvoid 9129224092Sdougbdns_resolver_settimeout(dns_resolver_t *resolver, unsigned int seconds) { 9130224092Sdougb REQUIRE(VALID_RESOLVER(resolver)); 9131224092Sdougb 9132224092Sdougb if (seconds == 0) 9133224092Sdougb seconds = DEFAULT_QUERY_TIMEOUT; 9134224092Sdougb if (seconds > MAXIMUM_QUERY_TIMEOUT) 9135224092Sdougb seconds = MAXIMUM_QUERY_TIMEOUT; 9136245163Serwin if (seconds < MINIMUM_QUERY_TIMEOUT) 9137245163Serwin seconds = MINIMUM_QUERY_TIMEOUT; 9138224092Sdougb 9139224092Sdougb resolver->query_timeout = seconds; 9140224092Sdougb} 9141275672Sdelphij 9142275672Sdelphijvoid 9143275672Sdelphijdns_resolver_setmaxdepth(dns_resolver_t *resolver, unsigned int maxdepth) { 9144275672Sdelphij REQUIRE(VALID_RESOLVER(resolver)); 9145275672Sdelphij resolver->maxdepth = maxdepth; 9146275672Sdelphij} 9147275672Sdelphij 9148275672Sdelphijunsigned int 9149275672Sdelphijdns_resolver_getmaxdepth(dns_resolver_t *resolver) { 9150275672Sdelphij REQUIRE(VALID_RESOLVER(resolver)); 9151275672Sdelphij return (resolver->maxdepth); 9152275672Sdelphij} 9153